Commit f2227ae677

Michael Pfaff <michael@pfaff.dev>
2025-04-26 15:05:26
Return WTF16LE encoded path from realpathW
1 parent 328ae41
Changed files (3)
lib/std/fs/Dir.zig
@@ -1370,7 +1370,16 @@ pub fn realpath(self: Dir, pathname: []const u8, out_buffer: []u8) RealPathError
     }
     if (native_os == .windows) {
         const pathname_w = try windows.sliceToPrefixedFileW(self.fd, pathname);
-        return self.realpathW(pathname_w.span(), out_buffer);
+
+        var wide_buf: [windows.PATH_MAX_WIDE]u16 = undefined;
+        const wide_slice = try self.realpathW(pathname_w.span(), &wide_buf);
+
+        const len = std.unicode.calcWtf8Len(wide_slice);
+        if (len > out_buffer.len)
+            return error.NameTooLong;
+
+        const end_index = std.unicode.wtf16LeToWtf8(out_buffer, wide_slice);
+        return out_buffer[0..end_index];
     }
     const pathname_c = try posix.toPosixPath(pathname);
     return self.realpathZ(&pathname_c, out_buffer);
@@ -1381,7 +1390,16 @@ pub fn realpath(self: Dir, pathname: []const u8, out_buffer: []u8) RealPathError
 pub fn realpathZ(self: Dir, pathname: [*:0]const u8, out_buffer: []u8) RealPathError![]u8 {
     if (native_os == .windows) {
         const pathname_w = try windows.cStrToPrefixedFileW(self.fd, pathname);
-        return self.realpathW(pathname_w.span(), out_buffer);
+
+        var wide_buf: [windows.PATH_MAX_WIDE]u16 = undefined;
+        const wide_slice = try self.realpathW(pathname_w.span(), &wide_buf);
+
+        const len = std.unicode.calcWtf8Len(wide_slice);
+        if (len > out_buffer.len)
+            return error.NameTooLong;
+
+        const end_index = std.unicode.wtf16LeToWtf8(out_buffer, wide_slice);
+        return out_buffer[0..end_index];
     }
 
     var flags: posix.O = .{};
@@ -1411,9 +1429,9 @@ pub fn realpathZ(self: Dir, pathname: [*:0]const u8, out_buffer: []u8) RealPathE
 }
 
 /// Windows-only. Same as `Dir.realpath` except `pathname` is WTF16 LE encoded.
-/// The result is encoded as [WTF-8](https://simonsapin.github.io/wtf-8/).
+/// The result is encoded as WTF16 LE.
 /// See also `Dir.realpath`, `realpathW`.
-pub fn realpathW(self: Dir, pathname: []const u16, out_buffer: []u8) RealPathError![]u8 {
+pub fn realpathW(self: Dir, pathname: []const u16, out_buffer: []u16) RealPathError![]u16 {
     const w = windows;
 
     const access_mask = w.GENERIC_READ | w.SYNCHRONIZE;
@@ -1434,13 +1452,7 @@ pub fn realpathW(self: Dir, pathname: []const u16, out_buffer: []u8) RealPathErr
     };
     defer w.CloseHandle(h_file);
 
-    var wide_buf: [w.PATH_MAX_WIDE]u16 = undefined;
-    const wide_slice = try w.GetFinalPathNameByHandle(h_file, .{}, &wide_buf);
-    const len = std.unicode.calcWtf8Len(wide_slice);
-    if (len > out_buffer.len)
-        return error.NameTooLong;
-    const end_index = std.unicode.wtf16LeToWtf8(out_buffer, wide_slice);
-    return out_buffer[0..end_index];
+    return w.GetFinalPathNameByHandle(h_file, .{}, out_buffer);
 }
 
 pub const RealPathAllocError = RealPathError || Allocator.Error;
lib/std/fs.zig
@@ -643,10 +643,19 @@ pub fn selfExePath(out_buffer: []u8) SelfExePathError![]u8 {
             // symlink, not the path that the symlink points to. We want the path
             // that the symlink points to, though, so we need to get the realpath.
             const pathname_w = try windows.wToPrefixedFileW(null, image_path_name);
-            return std.fs.cwd().realpathW(pathname_w.span(), out_buffer) catch |err| switch (err) {
+
+            var wide_buf: [windows.PATH_MAX_WIDE]u16 = undefined;
+            const wide_slice = std.fs.cwd().realpathW(pathname_w.span(), &wide_buf) catch |err| switch (err) {
                 error.InvalidWtf8 => unreachable,
                 else => |e| return e,
             };
+
+            const len = std.unicode.calcWtf8Len(wide_slice);
+            if (len > out_buffer.len)
+                return error.NameTooLong;
+
+            const end_index = std.unicode.wtf16LeToWtf8(out_buffer, wide_slice);
+            return out_buffer[0..end_index];
         },
         else => @compileError("std.fs.selfExePath not supported for this target"),
     }
lib/std/posix.zig
@@ -5676,7 +5676,12 @@ pub const RealPathError = error{
 pub fn realpath(pathname: []const u8, out_buffer: *[max_path_bytes]u8) RealPathError![]u8 {
     if (native_os == .windows) {
         const pathname_w = try windows.sliceToPrefixedFileW(null, pathname);
-        return realpathW(pathname_w.span(), out_buffer);
+
+        var wide_buf: [windows.PATH_MAX_WIDE]u16 = undefined;
+        const wide_slice = try realpathW(pathname_w.span(), &wide_buf);
+
+        const end_index = std.unicode.wtf16LeToWtf8(out_buffer, wide_slice);
+        return out_buffer[0..end_index];
     } else if (native_os == .wasi and !builtin.link_libc) {
         @compileError("WASI does not support os.realpath");
     }
@@ -5690,7 +5695,12 @@ pub fn realpath(pathname: []const u8, out_buffer: *[max_path_bytes]u8) RealPathE
 pub fn realpathZ(pathname: [*:0]const u8, out_buffer: *[max_path_bytes]u8) RealPathError![]u8 {
     if (native_os == .windows) {
         const pathname_w = try windows.cStrToPrefixedFileW(null, pathname);
-        return realpathW(pathname_w.span(), out_buffer);
+
+        var wide_buf: [windows.PATH_MAX_WIDE]u16 = undefined;
+        const wide_slice = try realpathW(pathname_w.span(), &wide_buf);
+
+        const end_index = std.unicode.wtf16LeToWtf8(out_buffer, wide_slice);
+        return out_buffer[0..end_index];
     } else if (native_os == .wasi and !builtin.link_libc) {
         return realpath(mem.sliceTo(pathname, 0), out_buffer);
     }
@@ -5736,32 +5746,11 @@ pub fn realpathZ(pathname: [*:0]const u8, out_buffer: *[max_path_bytes]u8) RealP
 
 /// Same as `realpath` except `pathname` is WTF16LE-encoded.
 ///
-/// The result is encoded as [WTF-8](https://simonsapin.github.io/wtf-8/).
+/// The result is encoded as WTF16LE.
 ///
 /// Calling this function is usually a bug.
-pub fn realpathW(pathname: []const u16, out_buffer: *[max_path_bytes]u8) RealPathError![]u8 {
-    const w = windows;
-
-    const dir = fs.cwd().fd;
-    const access_mask = w.GENERIC_READ | w.SYNCHRONIZE;
-    const share_access = w.FILE_SHARE_READ | w.FILE_SHARE_WRITE | w.FILE_SHARE_DELETE;
-    const creation = w.FILE_OPEN;
-    const h_file = blk: {
-        const res = w.OpenFile(pathname, .{
-            .dir = dir,
-            .access_mask = access_mask,
-            .share_access = share_access,
-            .creation = creation,
-            .filter = .any,
-        }) catch |err| switch (err) {
-            error.WouldBlock => unreachable,
-            else => |e| return e,
-        };
-        break :blk res;
-    };
-    defer w.CloseHandle(h_file);
-
-    return std.os.getFdPath(h_file, out_buffer);
+pub fn realpathW(pathname: []const u16, out_buffer: *[max_path_bytes]u16) RealPathError![]u16 {
+    return fs.cwd().realpathW(pathname, out_buffer);
 }
 
 /// Spurious wakeups are possible and no precision of timing is guaranteed.