Commit 41fd343508

fifty-six <ybham6@gmail.com>
2022-01-11 07:05:41
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 parent f4b3f1d
Changed files (1)
lib
std
lib/std/fs/path.zig
@@ -12,13 +12,13 @@ const fs = std.fs;
 const process = std.process;
 const native_os = builtin.target.os.tag;
 
-pub const sep_windows = '\\';
+pub const sep_windows_uefi = '\\';
 pub const sep_posix = '/';
-pub const sep = if (native_os == .windows) sep_windows else sep_posix;
+pub const sep = if (native_os == .windows or native_os == .uefi) sep_windows_uefi else sep_posix;
 
-pub const sep_str_windows = "\\";
+pub const sep_str_windows_uefi = "\\";
 pub const sep_str_posix = "/";
-pub const sep_str = if (native_os == .windows) sep_str_windows else sep_str_posix;
+pub const sep_str = if (native_os == .windows or native_os == .uefi) sep_str_windows_uefi else sep_str_posix;
 
 pub const delimiter_windows = ';';
 pub const delimiter_posix = ':';
@@ -26,11 +26,11 @@ pub const delimiter = if (native_os == .windows) delimiter_windows else delimite
 
 /// Returns if the given byte is a valid path separator
 pub fn isSep(byte: u8) bool {
-    if (native_os == .windows) {
-        return byte == '/' or byte == '\\';
-    } else {
-        return byte == '/';
-    }
+    return switch (native_os) {
+        .windows => byte == '/' or byte == '\\',
+        .uefi => byte == '\\',
+        else => byte == '/',
+    };
 }
 
 /// 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 {
     return out[0 .. out.len - 1 :0];
 }
 
+fn testJoinMaybeZUefi(paths: []const []const u8, expected: []const u8, zero: bool) !void {
+    const uefiIsSep = struct {
+        fn isSep(byte: u8) bool {
+            return byte == '\\';
+        }
+    }.isSep;
+    const actual = try joinSepMaybeZ(testing.allocator, sep_windows_uefi, uefiIsSep, paths, zero);
+    defer testing.allocator.free(actual);
+    try testing.expectEqualSlices(u8, expected, if (zero) actual[0 .. actual.len - 1 :0] else actual);
+}
+
 fn testJoinMaybeZWindows(paths: []const []const u8, expected: []const u8, zero: bool) !void {
     const windowsIsSep = struct {
         fn isSep(byte: u8) bool {
             return byte == '/' or byte == '\\';
         }
     }.isSep;
-    const actual = try joinSepMaybeZ(testing.allocator, sep_windows, windowsIsSep, paths, zero);
+    const actual = try joinSepMaybeZ(testing.allocator, sep_windows_uefi, windowsIsSep, paths, zero);
     defer testing.allocator.free(actual);
     try testing.expectEqualSlices(u8, expected, if (zero) actual[0 .. actual.len - 1 :0] else actual);
 }
@@ -158,6 +169,11 @@ test "join" {
             zero,
         );
 
+        try testJoinMaybeZUefi(&[_][]const u8{ "EFI", "Boot", "bootx64.efi" }, "EFI\\Boot\\bootx64.efi", zero);
+        try testJoinMaybeZUefi(&[_][]const u8{ "EFI\\Boot", "bootx64.efi" }, "EFI\\Boot\\bootx64.efi", zero);
+        try testJoinMaybeZUefi(&[_][]const u8{ "EFI\\", "\\Boot", "bootx64.efi" }, "EFI\\Boot\\bootx64.efi", zero);
+        try testJoinMaybeZUefi(&[_][]const u8{ "EFI\\", "\\Boot\\", "\\bootx64.efi" }, "EFI\\Boot\\bootx64.efi", zero);
+
         try testJoinMaybeZWindows(&[_][]const u8{ "c:\\", "a", "b/", "c" }, "c:\\a\\b/c", zero);
         try testJoinMaybeZWindows(&[_][]const u8{ "c:\\a/", "b\\", "/c" }, "c:\\a/b\\c", zero);
 
@@ -588,7 +604,7 @@ pub fn resolveWindows(allocator: Allocator, paths: []const []const u8) ![]u8 {
             result[0] = asciiUpper(result[0]);
             // Remove the trailing slash if present, eg. if the cwd is a root
             // directory.
-            if (cwd.len > 0 and cwd[cwd.len - 1] == sep_windows) {
+            if (cwd.len > 0 and cwd[cwd.len - 1] == sep_windows_uefi) {
                 result_index -= 1;
             }
         }
@@ -625,7 +641,7 @@ pub fn resolveWindows(allocator: Allocator, paths: []const []const u8) ![]u8 {
                         break;
                 }
             } else {
-                result[result_index] = sep_windows;
+                result[result_index] = sep_windows_uefi;
                 result_index += 1;
                 mem.copy(u8, result[result_index..], component);
                 result_index += component.len;