this repo has no description

initial commit

+120 -26
+120 -26
src/main.zig
··· 1 1 const std = @import("std"); 2 2 const builtin = @import("builtin"); 3 - const io = @import("ourio"); 3 + const ourio = @import("ourio"); 4 4 5 5 const linux = std.os.linux; 6 + const posix = std.posix; 7 + 8 + const Options = struct { 9 + all: bool = false, 10 + }; 11 + 12 + fn eql(a: []const u8, b: []const u8) bool { 13 + return std.mem.eql(u8, a, b); 14 + } 15 + 16 + fn optKind(a: []const u8) enum { short, long, positional } { 17 + if (std.mem.startsWith(u8, a, "--")) return .long; 18 + if (std.mem.startsWith(u8, a, "-")) return .short; 19 + return .positional; 20 + } 6 21 7 22 pub fn main() !void { 8 23 var debug_allocator: std.heap.DebugAllocator(.{}) = .init; ··· 19 34 var arena = std.heap.ArenaAllocator.init(gpa); 20 35 defer arena.deinit(); 21 36 22 - var ring: io.Ring = try .init(arena.allocator(), 64); 23 - defer ring.deinit(); 37 + var cmd: Command = .{ .arena = arena.allocator() }; 38 + 39 + var args = std.process.args(); 40 + while (args.next()) |arg| { 41 + switch (optKind(arg)) { 42 + .short => { 43 + const str = arg[1..]; 44 + for (str) |b| { 45 + switch (b) { 46 + 'a' => cmd.opts.all = true, 47 + else => { 48 + const w = std.io.getStdErr().writer(); 49 + try w.print("Invalid opt: '{c}'", .{b}); 50 + std.process.exit(1); 51 + }, 52 + } 53 + } 54 + }, 55 + .long => { 56 + const opt = arg[2..]; 57 + if (eql(opt, "all")) 58 + cmd.opts.all = true 59 + else { 60 + const w = std.io.getStdErr().writer(); 61 + try w.print("Invalid opt: '{s}'", .{opt}); 62 + std.process.exit(1); 63 + } 64 + }, 65 + .positional => {}, 66 + } 67 + } 24 68 25 - // TODO: implement openat in ourio 26 - var cwd = try std.fs.cwd().openDir(".", .{ .iterate = true }); 27 - defer cwd.close(); 69 + var ring: ourio.Ring = try .init(arena.allocator(), 64); 70 + defer ring.deinit(); 28 71 29 - var results: std.ArrayListUnmanaged(*Entry) = .empty; 72 + _ = try ring.open(".", .{ .DIRECTORY = true, .CLOEXEC = true }, 0, .{ 73 + .ptr = &cmd, 74 + .cb = onCompletion, 75 + .msg = @intFromEnum(Msg.cwd), 76 + }); 30 77 31 - var iter = cwd.iterate(); 32 - while (try iter.next()) |dirent| { 33 - const nameZ = try arena.allocator().dupeZ(u8, dirent.name); 34 - const entry = try arena.allocator().create(Entry); 35 - entry.* = .{ .name = nameZ, .kind = dirent.kind, .statx = undefined }; 36 - try results.append(arena.allocator(), entry); 37 - _ = try ring.stat(nameZ, &entry.statx, .{ .cb = onCompletion }); 78 + if (cmd.opts.all) { 79 + // We need to also open /etc/localtime and /etc/passwd 80 + _ = try ring.open("/etc/localtime", .{ .CLOEXEC = true }, 0, .{ 81 + .ptr = &cmd, 82 + .cb = onCompletion, 83 + .msg = @intFromEnum(Msg.localtime), 84 + }); 85 + _ = try ring.open("/etc/passwd", .{ .CLOEXEC = true }, 0, .{ 86 + .ptr = &cmd, 87 + .cb = onCompletion, 88 + .msg = @intFromEnum(Msg.localtime), 89 + }); 38 90 } 39 91 40 92 try ring.run(.until_done); 41 93 42 - var output: std.ArrayListUnmanaged(u8) = .empty; 43 - var writer = output.writer(arena.allocator()); 44 - for (results.items) |entry| { 45 - try writer.print("{s}\r\n", .{entry.name}); 94 + const stdout = std.io.getStdOut(); 95 + var bw = std.io.bufferedWriter(stdout.writer()); 96 + for (cmd.entries) |entry| { 97 + try bw.writer().print("{s}\r\n", .{entry.name}); 46 98 } 47 99 48 - try std.io.getStdOut().writeAll(output.items); 100 + try bw.flush(); 49 101 } 50 102 103 + const Command = struct { 104 + arena: std.mem.Allocator, 105 + opts: Options = .{}, 106 + entries: []Entry = &.{}, 107 + }; 108 + 109 + const Msg = enum(u16) { 110 + cwd, 111 + localtime, 112 + passwd, 113 + stat, 114 + }; 115 + 51 116 const Entry = struct { 52 117 name: [:0]const u8, 53 118 kind: std.fs.File.Kind, 54 - statx: io.Statx, 119 + statx: ourio.Statx, 55 120 56 - fn lessThan(_: void, lhs: *Entry, rhs: *Entry) bool { 121 + fn lessThan(_: void, lhs: Entry, rhs: Entry) bool { 57 122 return std.ascii.orderIgnoreCase(lhs.name, rhs.name).compare(.lt); 58 123 } 59 124 }; 60 125 61 - fn onCompletion(_: *io.Ring, task: io.Task) anyerror!void { 126 + fn onCompletion(io: *ourio.Ring, task: ourio.Task) anyerror!void { 127 + const cmd = task.userdataCast(Command); 128 + const msg = task.msgToEnum(Msg); 62 129 const result = task.result.?; 63 130 64 - _ = result.statx catch |err| { 65 - std.log.err("stat error: {}", .{err}); 66 - return; 67 - }; 131 + switch (msg) { 132 + .cwd => { 133 + const fd = try result.open; 134 + const dir: std.fs.Dir = .{ .fd = fd }; 135 + 136 + var results: std.ArrayListUnmanaged(Entry) = .empty; 137 + 138 + var iter = dir.iterate(); 139 + while (try iter.next()) |dirent| { 140 + const nameZ = try cmd.arena.dupeZ(u8, dirent.name); 141 + try results.append(cmd.arena, .{ 142 + .name = nameZ, 143 + .kind = dirent.kind, 144 + .statx = undefined, 145 + }); 146 + } 147 + cmd.entries = results.items; 148 + // best effort close 149 + _ = try io.close(fd, .{}); 150 + 151 + for (cmd.entries) |*entry| { 152 + _ = try io.stat(entry.name, &entry.statx, .{ 153 + .cb = onCompletion, 154 + .ptr = cmd, 155 + .msg = @intFromEnum(Msg.stat), 156 + }); 157 + } 158 + }, 159 + 160 + else => {}, 161 + } 68 162 }