diff --git a/src/main.zig b/src/main.zig index 264c440..4bb6729 100644 --- a/src/main.zig +++ b/src/main.zig @@ -1,8 +1,23 @@ const std = @import("std"); const builtin = @import("builtin"); -const io = @import("ourio"); +const ourio = @import("ourio"); const linux = std.os.linux; +const posix = std.posix; + +const Options = struct { + all: bool = false, +}; + +fn eql(a: []const u8, b: []const u8) bool { + return std.mem.eql(u8, a, b); +} + +fn optKind(a: []const u8) enum { short, long, positional } { + if (std.mem.startsWith(u8, a, "--")) return .long; + if (std.mem.startsWith(u8, a, "-")) return .short; + return .positional; +} pub fn main() !void { var debug_allocator: std.heap.DebugAllocator(.{}) = .init; @@ -19,50 +34,129 @@ pub fn main() !void { var arena = std.heap.ArenaAllocator.init(gpa); defer arena.deinit(); - var ring: io.Ring = try .init(arena.allocator(), 64); - defer ring.deinit(); + var cmd: Command = .{ .arena = arena.allocator() }; - // TODO: implement openat in ourio - var cwd = try std.fs.cwd().openDir(".", .{ .iterate = true }); - defer cwd.close(); + var args = std.process.args(); + while (args.next()) |arg| { + switch (optKind(arg)) { + .short => { + const str = arg[1..]; + for (str) |b| { + switch (b) { + 'a' => cmd.opts.all = true, + else => { + const w = std.io.getStdErr().writer(); + try w.print("Invalid opt: '{c}'", .{b}); + std.process.exit(1); + }, + } + } + }, + .long => { + const opt = arg[2..]; + if (eql(opt, "all")) + cmd.opts.all = true + else { + const w = std.io.getStdErr().writer(); + try w.print("Invalid opt: '{s}'", .{opt}); + std.process.exit(1); + } + }, + .positional => {}, + } + } + + var ring: ourio.Ring = try .init(arena.allocator(), 64); + defer ring.deinit(); - var results: std.ArrayListUnmanaged(*Entry) = .empty; + _ = try ring.open(".", .{ .DIRECTORY = true, .CLOEXEC = true }, 0, .{ + .ptr = &cmd, + .cb = onCompletion, + .msg = @intFromEnum(Msg.cwd), + }); - var iter = cwd.iterate(); - while (try iter.next()) |dirent| { - const nameZ = try arena.allocator().dupeZ(u8, dirent.name); - const entry = try arena.allocator().create(Entry); - entry.* = .{ .name = nameZ, .kind = dirent.kind, .statx = undefined }; - try results.append(arena.allocator(), entry); - _ = try ring.stat(nameZ, &entry.statx, .{ .cb = onCompletion }); + if (cmd.opts.all) { + // We need to also open /etc/localtime and /etc/passwd + _ = try ring.open("/etc/localtime", .{ .CLOEXEC = true }, 0, .{ + .ptr = &cmd, + .cb = onCompletion, + .msg = @intFromEnum(Msg.localtime), + }); + _ = try ring.open("/etc/passwd", .{ .CLOEXEC = true }, 0, .{ + .ptr = &cmd, + .cb = onCompletion, + .msg = @intFromEnum(Msg.localtime), + }); } try ring.run(.until_done); - var output: std.ArrayListUnmanaged(u8) = .empty; - var writer = output.writer(arena.allocator()); - for (results.items) |entry| { - try writer.print("{s}\r\n", .{entry.name}); + const stdout = std.io.getStdOut(); + var bw = std.io.bufferedWriter(stdout.writer()); + for (cmd.entries) |entry| { + try bw.writer().print("{s}\r\n", .{entry.name}); } - try std.io.getStdOut().writeAll(output.items); + try bw.flush(); } +const Command = struct { + arena: std.mem.Allocator, + opts: Options = .{}, + entries: []Entry = &.{}, +}; + +const Msg = enum(u16) { + cwd, + localtime, + passwd, + stat, +}; + const Entry = struct { name: [:0]const u8, kind: std.fs.File.Kind, - statx: io.Statx, + statx: ourio.Statx, - fn lessThan(_: void, lhs: *Entry, rhs: *Entry) bool { + fn lessThan(_: void, lhs: Entry, rhs: Entry) bool { return std.ascii.orderIgnoreCase(lhs.name, rhs.name).compare(.lt); } }; -fn onCompletion(_: *io.Ring, task: io.Task) anyerror!void { +fn onCompletion(io: *ourio.Ring, task: ourio.Task) anyerror!void { + const cmd = task.userdataCast(Command); + const msg = task.msgToEnum(Msg); const result = task.result.?; - _ = result.statx catch |err| { - std.log.err("stat error: {}", .{err}); - return; - }; + switch (msg) { + .cwd => { + const fd = try result.open; + const dir: std.fs.Dir = .{ .fd = fd }; + + var results: std.ArrayListUnmanaged(Entry) = .empty; + + var iter = dir.iterate(); + while (try iter.next()) |dirent| { + const nameZ = try cmd.arena.dupeZ(u8, dirent.name); + try results.append(cmd.arena, .{ + .name = nameZ, + .kind = dirent.kind, + .statx = undefined, + }); + } + cmd.entries = results.items; + // best effort close + _ = try io.close(fd, .{}); + + for (cmd.entries) |*entry| { + _ = try io.stat(entry.name, &entry.statx, .{ + .cb = onCompletion, + .ptr = cmd, + .msg = @intFromEnum(Msg.stat), + }); + } + }, + + else => {}, + } }