config: make diagnostic if homedir expansion fails

This commit is contained in:
Mitchell Hashimoto
2025-01-02 12:43:50 -08:00
parent 7bd842a530
commit a94cf4b3a2
2 changed files with 46 additions and 22 deletions

View File

@ -4362,11 +4362,26 @@ pub const RepeatablePath = struct {
// to the base. // to the base.
var buf: [std.fs.max_path_bytes]u8 = undefined; var buf: [std.fs.max_path_bytes]u8 = undefined;
// Check if the path starts with a tilde and expand it to the home directory on linux/mac // Check if the path starts with a tilde and expand it to the
// home directory on Linux/macOS. We explicitly look for "~/"
// because we don't support alternate users such as "~alice/"
if (std.mem.startsWith(u8, path, "~/")) { if (std.mem.startsWith(u8, path, "~/")) {
const expanded: []u8 = try internal_os.expandHome(path, &buf) orelse { const expanded: []const u8 = internal_os.expandHome(
// Blank this path so that we don't attempt to resolve it again path,
&buf,
) catch |err| {
try diags.append(alloc, .{
.message = try std.fmt.allocPrintZ(
alloc,
"error expanding home directory for path {s}: {}",
.{ path, err },
),
});
// Blank this path so that we don't attempt to resolve it
// again
self.value.items[i] = .{ .required = "" }; self.value.items[i] = .{ .required = "" };
continue; continue;
}; };
@ -4378,6 +4393,7 @@ pub const RepeatablePath = struct {
switch (self.value.items[i]) { switch (self.value.items[i]) {
.optional, .required => |*p| p.* = try alloc.dupeZ(u8, expanded), .optional, .required => |*p| p.* = try alloc.dupeZ(u8, expanded),
} }
continue; continue;
} }

View File

@ -12,7 +12,7 @@ const Error = error{
/// Determine the home directory for the currently executing user. This /// Determine the home directory for the currently executing user. This
/// is generally an expensive process so the value should be cached. /// is generally an expensive process so the value should be cached.
pub inline fn home(buf: []u8) !?[]u8 { pub inline fn home(buf: []u8) !?[]const u8 {
return switch (builtin.os.tag) { return switch (builtin.os.tag) {
inline .linux, .macos => try homeUnix(buf), inline .linux, .macos => try homeUnix(buf),
.windows => try homeWindows(buf), .windows => try homeWindows(buf),
@ -24,7 +24,7 @@ pub inline fn home(buf: []u8) !?[]u8 {
}; };
} }
fn homeUnix(buf: []u8) !?[]u8 { fn homeUnix(buf: []u8) !?[]const u8 {
// First: if we have a HOME env var, then we use that. // First: if we have a HOME env var, then we use that.
if (posix.getenv("HOME")) |result| { if (posix.getenv("HOME")) |result| {
if (buf.len < result.len) return Error.BufferTooSmall; if (buf.len < result.len) return Error.BufferTooSmall;
@ -77,7 +77,7 @@ fn homeUnix(buf: []u8) !?[]u8 {
return null; return null;
} }
fn homeWindows(buf: []u8) !?[]u8 { fn homeWindows(buf: []u8) !?[]const u8 {
const drive_len = blk: { const drive_len = blk: {
var fba_instance = std.heap.FixedBufferAllocator.init(buf); var fba_instance = std.heap.FixedBufferAllocator.init(buf);
const fba = fba_instance.allocator(); const fba = fba_instance.allocator();
@ -110,22 +110,30 @@ fn trimSpace(input: []const u8) []const u8 {
return std.mem.trim(u8, input, " \n\t"); return std.mem.trim(u8, input, " \n\t");
} }
/// Expands a path that starts with a tilde (~) to the home directory of the current user. pub const ExpandError = error{
HomeDetectionFailed,
BufferTooSmall,
};
/// Expands a path that starts with a tilde (~) to the home directory of
/// the current user.
/// ///
/// Errors if `home` fails or if the size of the expanded path is larger than `buf.len`. /// Errors if `home` fails or if the size of the expanded path is larger
/// /// than `buf.len`.
/// Returns null if the value returned from `home` is null, otherwise returns a slice to the expanded path. pub fn expandHome(path: []const u8, buf: []u8) ExpandError![]const u8 {
pub inline fn expandHome(path: []const u8, buf: []u8) !?[]u8 {
return switch (builtin.os.tag) { return switch (builtin.os.tag) {
inline .linux, .macos => expandHomeUnix(path, buf), .linux, .macos => try expandHomeUnix(path, buf),
.ios => return null, .ios => return path,
else => @compileError("unimplemented"), else => @compileError("unimplemented"),
}; };
} }
fn expandHomeUnix(path: []const u8, buf: []u8) !?[]u8 { fn expandHomeUnix(path: []const u8, buf: []u8) ExpandError![]const u8 {
if (!std.mem.startsWith(u8, path, "~/")) return null; if (!std.mem.startsWith(u8, path, "~/")) return path;
const home_dir = try home(buf) orelse return null; const home_dir: []const u8 = if (home(buf)) |home_|
home_ orelse return error.HomeDetectionFailed
else |_|
return error.HomeDetectionFailed;
const rest = path[1..]; // Skip the ~ const rest = path[1..]; // Skip the ~
const expanded_len = home_dir.len + rest.len; const expanded_len = home_dir.len + rest.len;
@ -139,20 +147,20 @@ test "expandHomeUnix" {
const testing = std.testing; const testing = std.testing;
const allocator = testing.allocator; const allocator = testing.allocator;
var buf: [std.fs.max_path_bytes]u8 = undefined; var buf: [std.fs.max_path_bytes]u8 = undefined;
const home_dir = (try expandHomeUnix("~/", &buf)).?; const home_dir = try expandHomeUnix("~/", &buf);
// Joining the home directory `~` with the path `/` // Joining the home directory `~` with the path `/`
// the result should end with a separator here. (e.g. `/home/user/`) // the result should end with a separator here. (e.g. `/home/user/`)
try testing.expect(home_dir[home_dir.len - 1] == std.fs.path.sep); try testing.expect(home_dir[home_dir.len - 1] == std.fs.path.sep);
const downloads = (try expandHomeUnix("~/Downloads/shader.glsl", &buf)).?; const downloads = try expandHomeUnix("~/Downloads/shader.glsl", &buf);
const expected_downloads = try std.mem.concat(allocator, u8, &[_][]const u8{ home_dir, "Downloads/shader.glsl" }); const expected_downloads = try std.mem.concat(allocator, u8, &[_][]const u8{ home_dir, "Downloads/shader.glsl" });
defer allocator.free(expected_downloads); defer allocator.free(expected_downloads);
try testing.expectEqualStrings(expected_downloads, downloads); try testing.expectEqualStrings(expected_downloads, downloads);
try testing.expect(try expandHomeUnix("~", &buf) == null); try testing.expectEqualStrings("~", try expandHomeUnix("~", &buf));
try testing.expect(try expandHomeUnix("~abc/", &buf) == null); try testing.expectEqualStrings("~abc/", try expandHomeUnix("~abc/", &buf));
try testing.expect(try expandHomeUnix("/home/user", &buf) == null); try testing.expectEqualStrings("/home/user", try expandHomeUnix("/home/user", &buf));
try testing.expect(try expandHomeUnix("", &buf) == null); try testing.expectEqualStrings("", try expandHomeUnix("", &buf));
// Expect an error if the buffer is large enough to hold the home directory, // Expect an error if the buffer is large enough to hold the home directory,
// but not the expanded path // but not the expanded path