Expand ~ and ~user in config file

Fixes #243
This commit is contained in:
Yorhel 2024-11-16 11:38:09 +01:00
parent 9d51df02c1
commit 5593fa2233
3 changed files with 41 additions and 6 deletions

View file

@ -9,6 +9,9 @@ pub const c = @cImport({
@cInclude("wchar.h"); // wcwidth() @cInclude("wchar.h"); // wcwidth()
@cInclude("locale.h"); // setlocale() and localeconv() @cInclude("locale.h"); // setlocale() and localeconv()
@cInclude("fnmatch.h"); // fnmatch() @cInclude("fnmatch.h"); // fnmatch()
@cInclude("unistd.h"); // getuid()
@cInclude("sys/types.h"); // struct passwd
@cInclude("pwd.h"); // getpwnam(), getpwuid()
if (@import("builtin").os.tag == .linux) { if (@import("builtin").os.tag == .linux) {
@cInclude("sys/vfs.h"); // statfs() @cInclude("sys/vfs.h"); // statfs()
} }

View file

@ -194,7 +194,7 @@ const Args = struct {
} }
}; };
fn argConfig(args: *Args, opt: Args.Option) bool { fn argConfig(args: *Args, opt: Args.Option, infile: bool) bool {
if (opt.is("-q") or opt.is("--slow-ui-updates")) config.update_delay = 2*std.time.ns_per_s if (opt.is("-q") or opt.is("--slow-ui-updates")) config.update_delay = 2*std.time.ns_per_s
else if (opt.is("--fast-ui-updates")) config.update_delay = 100*std.time.ns_per_ms else if (opt.is("--fast-ui-updates")) config.update_delay = 100*std.time.ns_per_ms
else if (opt.is("-x") or opt.is("--one-file-system")) config.same_fs = true else if (opt.is("-x") or opt.is("--one-file-system")) config.same_fs = true
@ -270,9 +270,13 @@ fn argConfig(args: *Args, opt: Args.Option) bool {
else if (opt.is("--no-si")) config.si = false else if (opt.is("--no-si")) config.si = false
else if (opt.is("-L") or opt.is("--follow-symlinks")) config.follow_symlinks = true else if (opt.is("-L") or opt.is("--follow-symlinks")) config.follow_symlinks = true
else if (opt.is("--no-follow-symlinks")) config.follow_symlinks = false else if (opt.is("--no-follow-symlinks")) config.follow_symlinks = false
else if (opt.is("--exclude")) exclude.addPattern(args.arg()) else if (opt.is("--exclude")) {
else if (opt.is("-X") or opt.is("--exclude-from")) { const arg = if (infile) (util.expanduser(args.arg(), allocator) catch unreachable) else args.arg();
const arg = args.arg(); defer if (infile) allocator.free(arg);
exclude.addPattern(arg);
} else if (opt.is("-X") or opt.is("--exclude-from")) {
const arg = if (infile) (util.expanduser(args.arg(), allocator) catch unreachable) else args.arg();
defer if (infile) allocator.free(arg);
readExcludeFile(arg) catch |e| ui.die("Error reading excludes from {s}: {s}.\n", .{ arg, ui.errorString(e) }); readExcludeFile(arg) catch |e| ui.die("Error reading excludes from {s}: {s}.\n", .{ arg, ui.errorString(e) });
} else if (opt.is("--exclude-caches")) config.exclude_caches = true } else if (opt.is("--exclude-caches")) config.exclude_caches = true
else if (opt.is("--include-caches")) config.exclude_caches = false else if (opt.is("--include-caches")) config.exclude_caches = false
@ -341,7 +345,7 @@ fn tryReadArgsFile(path: [:0]const u8) void {
var args = Args.init(arglist.items); var args = Args.init(arglist.items);
while (args.next()) |opt| { while (args.next()) |opt| {
if (!argConfig(&args, opt)) if (!argConfig(&args, opt, true))
ui.die("Unrecognized option in config file '{s}': {s}.\nRun with --ignore-config to skip reading config files.\n", .{path, opt.val}); ui.die("Unrecognized option in config file '{s}': {s}.\nRun with --ignore-config to skip reading config files.\n", .{path, opt.val});
} }
for (arglist.items) |i| allocator.free(i); for (arglist.items) |i| allocator.free(i);
@ -540,7 +544,7 @@ pub fn main() void {
else if (opt.is("-f")) import_file = allocator.dupeZ(u8, args.arg()) catch unreachable else if (opt.is("-f")) import_file = allocator.dupeZ(u8, args.arg()) catch unreachable
else if (opt.is("--ignore-config")) {} else if (opt.is("--ignore-config")) {}
else if (opt.is("--quit-after-scan")) quit_after_scan = true // undocumented feature to help with benchmarking scan/import else if (opt.is("--quit-after-scan")) quit_after_scan = true // undocumented feature to help with benchmarking scan/import
else if (argConfig(&args, opt)) {} else if (argConfig(&args, opt, false)) {}
else ui.die("Unrecognized option '{s}'.\n", .{opt.val}); else ui.die("Unrecognized option '{s}'.\n", .{opt.val});
} }
} }

View file

@ -2,6 +2,7 @@
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
const std = @import("std"); const std = @import("std");
const c = @import("c.zig").c;
// Cast any integer type to the target type, clamping the value to the supported maximum if necessary. // Cast any integer type to the target type, clamping the value to the supported maximum if necessary.
pub fn castClamp(comptime T: type, x: anytype) T { pub fn castClamp(comptime T: type, x: anytype) T {
@ -170,3 +171,30 @@ test "strnatcmp" {
for (i+1..w.len) |j| try eq(strnatcmp(w[i], w[j]), .lt); for (i+1..w.len) |j| try eq(strnatcmp(w[i], w[j]), .lt);
} }
} }
pub fn expanduser(path: []const u8, alloc: std.mem.Allocator) ![:0]u8 {
if (path.len == 0 or path[0] != '~') return alloc.dupeZ(u8, path);
const len = std.mem.indexOfScalar(u8, path, '/') orelse path.len;
const home_raw = blk: {
const pwd = pwd: {
if (len == 1) {
if (std.posix.getenvZ("HOME")) |p| break :blk p;
break :pwd c.getpwuid(c.getuid());
} else {
const name = try alloc.dupeZ(u8, path[1..len]);
defer alloc.free(name);
break :pwd c.getpwnam(name.ptr);
}
};
if (pwd != null)
if (@as(*c.struct_passwd, pwd).pw_dir) |p|
break :blk std.mem.span(p);
return alloc.dupeZ(u8, path);
};
const home = std.mem.trimRight(u8, home_raw, "/");
if (home.len == 0 and path.len == len) return alloc.dupeZ(u8, "/");
return try std.fmt.allocPrintZ(alloc, "{s}{s}", .{ home, path[len..] });
}