const std = @import("std"); const model = @import("model.zig"); const scan = @import("scan.zig"); var general_purpose_allocator = std.heap.GeneralPurposeAllocator(.{}){}; pub const allocator = &general_purpose_allocator.allocator; pub const Config = struct { same_fs: bool = true, extended: bool = false, exclude_caches: bool = false, follow_symlinks: bool = false, exclude_kernfs: bool = false, // TODO: exclude patterns update_delay: u32 = 100, si: bool = false, // TODO: color scheme read_only: bool = false, can_shell: bool = true, confirm_quit: bool = false, }; pub var config = Config{}; fn die(comptime fmt: []const u8, args: anytype) noreturn { _ = std.io.getStdErr().writer().print(fmt, args) catch {}; std.process.exit(1); } // Simple generic argument parser, supports getopt_long() style arguments. // T can be any type that has a 'fn next(T) ?[]const u8' method, e.g.: // var args = Args(std.process.ArgIteratorPosix).init(std.process.ArgIteratorPosix.init()); fn Args(T: anytype) type { return struct { it: T, short: ?[]const u8 = null, // Remainder after a short option, e.g. -x (which may be either more short options or an argument) last: ?[]const u8 = null, last_arg: ?[]const u8 = null, // In the case of --option= shortbuf: [2]u8 = undefined, argsep: bool = false, const Self = @This(); const Option = struct { opt: bool, val: []const u8, fn is(self: @This(), cmp: []const u8) bool { return self.opt and std.mem.eql(u8, self.val, cmp); } }; fn init(it: T) Self { return Self{ .it = it }; } pub fn shortopt(self: *Self, s: []const u8) Option { self.shortbuf[0] = '-'; self.shortbuf[1] = s[0]; self.short = if (s.len > 1) s[1..] else null; self.last = &self.shortbuf; return .{ .opt = true, .val = &self.shortbuf }; } /// Return the next option or positional argument. /// 'opt' indicates whether it's an option or positional argument, /// 'val' will be either -x, --something or the argument. pub fn next(self: *Self) ?Option { if (self.last_arg != null) die("Option '{s}' does not expect an argument.\n", .{ self.last.? }); if (self.short) |s| return self.shortopt(s); const val = self.it.next() orelse return null; if (self.argsep or val.len == 0 or val[0] != '-') return Option{ .opt = false, .val = val }; if (val.len == 1) die("Invalid option '-'.\n", .{}); if (val.len == 2 and val[1] == '-') { self.argsep = true; return self.next(); } if (val[1] == '-') { if (std.mem.indexOfScalar(u8, val, '=')) |sep| { if (sep == 2) die("Invalid option '{s}'.\n", .{val}); self.last_arg = val[sep+1.. :0]; self.last = val[0..sep]; return Option{ .opt = true, .val = self.last.? }; } self.last = val; return Option{ .opt = true, .val = val }; } return self.shortopt(val[1..]); } /// Returns the argument given to the last returned option. Dies with an error if no argument is provided. pub fn arg(self: *Self) []const u8 { if (self.short) |a| { defer self.short = null; return a; } if (self.last_arg) |a| { defer self.last_arg = null; return a; } if (self.it.next()) |o| return o; die("Option '{s}' requires an argument.\n", .{ self.last.? }); } }; } // For debugging fn writeTree(out: anytype, e: *model.Entry, indent: u32) @TypeOf(out).Error!void { var i: u32 = 0; while (i