diff options
Diffstat (limited to 'src/fs.zig')
| -rw-r--r-- | src/fs.zig | 57 |
1 files changed, 56 insertions, 1 deletions
@@ -732,6 +732,59 @@ pub fn dotRelative(word: []const u8) bool { /// Where a path really is on the host, and whether it is a directory. The /// path may lie in a mount this editor serves, so the turn is given up for /// the syscalls: another thread answers them. +/// A path's leading `~` -- alone, or before `/` or `:` (`~/x:12`) -- is the +/// home directory: $HOME, else the passwd entry's. `~user/...` is that +/// user's. As in a shell, `~` is home even beside a file named `~` in the +/// current directory. Every entry point that takes a path the user typed +/// runs it through here: look (B3 and 9P), name, Save, ThemeFile, DumpDir, +/// Restore, a forwarded `pardes ~/x`. The path itself when there is no +/// `~` to expand, or no home for it. +pub fn expandHome(path: []const u8, out: *[4096]u8) []const u8 { + if (comptime !pardes.hosted) return path; + return expandHomeFrom(path, out, std.c.getenv("HOME")); +} + +/// expandHome with HOME given (null: unset), so a test need not change the +/// process's environment, which a child spawned later reads. +fn expandHomeFrom(path: []const u8, out: *[4096]u8, env_home: ?[*:0]const u8) []const u8 { + if (path.len == 0 or path[0] != '~') return path; + const end = std.mem.indexOfAny(u8, path, "/:") orelse path.len; + const home = (if (end == 1) homeDir(env_home) else userHome(path[1..end])) orelse return path; + const trimmed = std.mem.trimEnd(u8, home, "/"); + const rest = path[end..]; + if (trimmed.len == 0 and (rest.len == 0 or rest[0] != '/')) return std.fmt.bufPrint(out, "/{s}", .{rest}) catch path; + return std.fmt.bufPrint(out, "{s}{s}", .{ trimmed, rest }) catch path; +} + +fn homeDir(env_home: ?[*:0]const u8) ?[]const u8 { + if (env_home) |h| if (h[0] != 0) return std.mem.span(h); + const pw = std.c.getpwuid(std.c.getuid()) orelse return null; + return std.mem.span(pw.dir orelse return null); +} + +fn userHome(name: []const u8) ?[]const u8 { + var z: [256]u8 = undefined; + const name_z = std.fmt.bufPrintSentinel(&z, "{s}", .{name}, 0) catch return null; + const pw = std.c.getpwnam(name_z) orelse return null; + return std.mem.span(pw.dir orelse return null); +} + +test "a leading ~ is home, from HOME or else the passwd entry" { + var out: [4096]u8 = undefined; + const home = homeDir(std.c.getenv("HOME")) orelse return error.SkipZigTest; + var want: [4096]u8 = undefined; + try std.testing.expectEqualStrings(try std.fmt.bufPrint(&want, "{s}/x:12", .{std.mem.trimEnd(u8, home, "/")}), expandHome("~/x:12", &out)); + try std.testing.expectEqualStrings(std.mem.trimEnd(u8, home, "/"), expandHome("~", &out)); + try std.testing.expectEqualStrings("a/~/b", expandHome("a/~/b", &out)); + try std.testing.expectEqualStrings("~no-such-user-pardes/x", expandHome("~no-such-user-pardes/x", &out)); + try std.testing.expectEqualStrings("/", expandHome("~root", &out)[0..1]); + + // HOME unset: the passwd entry's home. + const pw = std.c.getpwuid(std.c.getuid()) orelse return error.SkipZigTest; + var from_passwd: [4096]u8 = undefined; + try std.testing.expectEqualStrings(try std.fmt.bufPrint(&from_passwd, "{s}/y", .{std.mem.trimEnd(u8, std.mem.span(pw.dir.?), "/")}), expandHomeFrom("~/y", &out, null)); +} + pub fn resolveOs(path: []const u8, out: *[4096]u8) ?Resolved { if (comptime !platform_has_fs) return null; var z: [4096]u8 = undefined; @@ -1171,7 +1224,9 @@ pub fn readFile(gpa: std.mem.Allocator, path: []const u8) ![]u8 { /// Restore first tries a relative argument under the default dump directory, /// then preserves the ordinary file lookup. Absolute paths remain verbatim. -pub fn readRestore(gpa: std.mem.Allocator, path: []const u8, dir_setting: []const u8) ![]u8 { +pub fn readRestore(gpa: std.mem.Allocator, typed: []const u8, dir_setting: []const u8) ![]u8 { + var home_buf: [4096]u8 = undefined; + const path = expandHome(typed, &home_buf); var dir_buf: [4096]u8 = undefined; const dir = @import("dump.zig").directory(&dir_buf, dir_setting); return readRestoreIn(gpa, path, dir); |
