authorgravatar for ybham6@gmail.comfifty-six <ybham6@gmail.com> 2022-01-11 01:05:41-05:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2022-01-11 10:49:40-07:00
log41fd343508880ffdfbc83c7b053237da09199f02
tree2bbc476dbc114f7c762430d95d1470ad0dfcd8a9
parentf4b3f1d6022265992f87cea1d9591ffa8ec226d6

std: fix path joining on UEFI

UEFI uses `\` for paths exclusively. This changes std.fs.path to use `\` for UEFI path joining. Also adds a few tests regarding it, specifically in making sure double-separators do not result from path joining, as the UEFI spec says to convert any that result from joining into single separators (UEFI Spec Version 2.7, pg. 448).

1 files changed, 28 insertions(+), 12 deletions(-)

lib/std/fs/path.zig+28-12
......@@ -12,13 +12,13 @@ const fs = std.fs;
1212const process = std.process;
1313const native_os = builtin.target.os.tag;
1414
15pub const sep_windows = '\\';
15pub const sep_windows_uefi = '\\';
1616pub const sep_posix = '/';
17pub const sep = if (native_os == .windows) sep_windows else sep_posix;
17pub const sep = if (native_os == .windows or native_os == .uefi) sep_windows_uefi else sep_posix;
1818
19pub const sep_str_windows = "\\";
19pub const sep_str_windows_uefi = "\\";
2020pub const sep_str_posix = "/";
21pub const sep_str = if (native_os == .windows) sep_str_windows else sep_str_posix;
21pub const sep_str = if (native_os == .windows or native_os == .uefi) sep_str_windows_uefi else sep_str_posix;
2222
2323pub const delimiter_windows = ';';
2424pub const delimiter_posix = ':';
......@@ -26,11 +26,11 @@ pub const delimiter = if (native_os == .windows) delimiter_windows else delimite
2626
2727/// Returns if the given byte is a valid path separator
2828pub fn isSep(byte: u8) bool {
29 if (native_os == .windows) {
30 return byte == '/' or byte == '\\';
31 } else {
32 return byte == '/';
33 }
29 return switch (native_os) {
30 .windows => byte == '/' or byte == '\\',
31 .uefi => byte == '\\',
32 else => byte == '/',
33 };
3434}
3535
3636/// This is different from mem.join in that the separator will not be repeated if
......@@ -110,13 +110,24 @@ pub fn joinZ(allocator: Allocator, paths: []const []const u8) ![:0]u8 {
110110 return out[0 .. out.len - 1 :0];
111111}
112112
113fn testJoinMaybeZUefi(paths: []const []const u8, expected: []const u8, zero: bool) !void {
114 const uefiIsSep = struct {
115 fn isSep(byte: u8) bool {
116 return byte == '\\';
117 }
118 }.isSep;
119 const actual = try joinSepMaybeZ(testing.allocator, sep_windows_uefi, uefiIsSep, paths, zero);
120 defer testing.allocator.free(actual);
121 try testing.expectEqualSlices(u8, expected, if (zero) actual[0 .. actual.len - 1 :0] else actual);
122}
123
113124fn testJoinMaybeZWindows(paths: []const []const u8, expected: []const u8, zero: bool) !void {
114125 const windowsIsSep = struct {
115126 fn isSep(byte: u8) bool {
116127 return byte == '/' or byte == '\\';
117128 }
118129 }.isSep;
119 const actual = try joinSepMaybeZ(testing.allocator, sep_windows, windowsIsSep, paths, zero);
130 const actual = try joinSepMaybeZ(testing.allocator, sep_windows_uefi, windowsIsSep, paths, zero);
120131 defer testing.allocator.free(actual);
121132 try testing.expectEqualSlices(u8, expected, if (zero) actual[0 .. actual.len - 1 :0] else actual);
122133}
......@@ -158,6 +169,11 @@ test "join" {
158169 zero,
159170 );
160171
172 try testJoinMaybeZUefi(&[_][]const u8{ "EFI", "Boot", "bootx64.efi" }, "EFI\\Boot\\bootx64.efi", zero);
173 try testJoinMaybeZUefi(&[_][]const u8{ "EFI\\Boot", "bootx64.efi" }, "EFI\\Boot\\bootx64.efi", zero);
174 try testJoinMaybeZUefi(&[_][]const u8{ "EFI\\", "\\Boot", "bootx64.efi" }, "EFI\\Boot\\bootx64.efi", zero);
175 try testJoinMaybeZUefi(&[_][]const u8{ "EFI\\", "\\Boot\\", "\\bootx64.efi" }, "EFI\\Boot\\bootx64.efi", zero);
176
161177 try testJoinMaybeZWindows(&[_][]const u8{ "c:\\", "a", "b/", "c" }, "c:\\a\\b/c", zero);
162178 try testJoinMaybeZWindows(&[_][]const u8{ "c:\\a/", "b\\", "/c" }, "c:\\a/b\\c", zero);
163179
......@@ -588,7 +604,7 @@ pub fn resolveWindows(allocator: Allocator, paths: []const []const u8) ![]u8 {
588604 result[0] = asciiUpper(result[0]);
589605 // Remove the trailing slash if present, eg. if the cwd is a root
590606 // directory.
591 if (cwd.len > 0 and cwd[cwd.len - 1] == sep_windows) {
607 if (cwd.len > 0 and cwd[cwd.len - 1] == sep_windows_uefi) {
592608 result_index -= 1;
593609 }
594610 }
......@@ -625,7 +641,7 @@ pub fn resolveWindows(allocator: Allocator, paths: []const []const u8) ![]u8 {
625641 break;
626642 }
627643 } else {
628 result[result_index] = sep_windows;
644 result[result_index] = sep_windows_uefi;
629645 result_index += 1;
630646 mem.copy(u8, result[result_index..], component);
631647 result_index += component.len;