authorgravatar for squeek502@hotmail.comRyan Liptak <squeek502@hotmail.com> 2025-10-09 02:01:06-07:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2025-10-09 02:01:06-07:00
log0bdd1b5274ceb6ca4145fc880a3d78276801c3fe
tree0df4edc19cd18e44d78bbd7c20fe2fdf8031dd78
parent328ae41468f3514251ac1b0726c41eca9fbc3fb5
parent7bf740ee718f4b6109cd9fe7014d1784d48ada48
signaturebadge-check Signed by PGP key B5690EEEBB952194

Merge pull request #23657 from mpfaff/realpathW-no-convert

Return WTF-16 from W-suffixed functions instead of converting to WTF-8

3 files changed, 79 insertions(+), 38 deletions(-)

lib/std/fs.zig+11-2
...@@ -31,6 +31,7 @@ pub const wasi = @import("fs/wasi.zig");...@@ -31,6 +31,7 @@ pub const wasi = @import("fs/wasi.zig");
31pub const realpath = posix.realpath;31pub const realpath = posix.realpath;
32pub const realpathZ = posix.realpathZ;32pub const realpathZ = posix.realpathZ;
33pub const realpathW = posix.realpathW;33pub const realpathW = posix.realpathW;
34pub const realpathW2 = posix.realpathW2;
3435
35pub const getAppDataDir = @import("fs/get_app_data_dir.zig").getAppDataDir;36pub const getAppDataDir = @import("fs/get_app_data_dir.zig").getAppDataDir;
36pub const GetAppDataDirError = @import("fs/get_app_data_dir.zig").GetAppDataDirError;37pub const GetAppDataDirError = @import("fs/get_app_data_dir.zig").GetAppDataDirError;
...@@ -642,11 +643,19 @@ pub fn selfExePath(out_buffer: []u8) SelfExePathError![]u8 {...@@ -642,11 +643,19 @@ pub fn selfExePath(out_buffer: []u8) SelfExePathError![]u8 {
642 // If ImagePathName is a symlink, then it will contain the path of the643 // If ImagePathName is a symlink, then it will contain the path of the
643 // symlink, not the path that the symlink points to. We want the path644 // symlink, not the path that the symlink points to. We want the path
644 // that the symlink points to, though, so we need to get the realpath.645 // that the symlink points to, though, so we need to get the realpath.
645 const pathname_w = try windows.wToPrefixedFileW(null, image_path_name);646 var pathname_w = try windows.wToPrefixedFileW(null, image_path_name);
646 return std.fs.cwd().realpathW(pathname_w.span(), out_buffer) catch |err| switch (err) {647
648 const wide_slice = std.fs.cwd().realpathW2(pathname_w.span(), &pathname_w.data) catch |err| switch (err) {
647 error.InvalidWtf8 => unreachable,649 error.InvalidWtf8 => unreachable,
648 else => |e| return e,650 else => |e| return e,
649 };651 };
652
653 const len = std.unicode.calcWtf8Len(wide_slice);
654 if (len > out_buffer.len)
655 return error.NameTooLong;
656
657 const end_index = std.unicode.wtf16LeToWtf8(out_buffer, wide_slice);
658 return out_buffer[0..end_index];
650 },659 },
651 else => @compileError("std.fs.selfExePath not supported for this target"),660 else => @compileError("std.fs.selfExePath not supported for this target"),
652 }661 }
lib/std/fs/Dir.zig+45-11
...@@ -1369,8 +1369,16 @@ pub fn realpath(self: Dir, pathname: []const u8, out_buffer: []u8) RealPathError...@@ -1369,8 +1369,16 @@ pub fn realpath(self: Dir, pathname: []const u8, out_buffer: []u8) RealPathError
1369 @compileError("realpath is not available on WASI");1369 @compileError("realpath is not available on WASI");
1370 }1370 }
1371 if (native_os == .windows) {1371 if (native_os == .windows) {
1372 const pathname_w = try windows.sliceToPrefixedFileW(self.fd, pathname);1372 var pathname_w = try windows.sliceToPrefixedFileW(self.fd, pathname);
1373 return self.realpathW(pathname_w.span(), out_buffer);1373
1374 const wide_slice = try self.realpathW2(pathname_w.span(), &pathname_w.data);
1375
1376 const len = std.unicode.calcWtf8Len(wide_slice);
1377 if (len > out_buffer.len)
1378 return error.NameTooLong;
1379
1380 const end_index = std.unicode.wtf16LeToWtf8(out_buffer, wide_slice);
1381 return out_buffer[0..end_index];
1374 }1382 }
1375 const pathname_c = try posix.toPosixPath(pathname);1383 const pathname_c = try posix.toPosixPath(pathname);
1376 return self.realpathZ(&pathname_c, out_buffer);1384 return self.realpathZ(&pathname_c, out_buffer);
...@@ -1380,8 +1388,16 @@ pub fn realpath(self: Dir, pathname: []const u8, out_buffer: []u8) RealPathError...@@ -1380,8 +1388,16 @@ pub fn realpath(self: Dir, pathname: []const u8, out_buffer: []u8) RealPathError
1380/// See also `Dir.realpath`, `realpathZ`.1388/// See also `Dir.realpath`, `realpathZ`.
1381pub fn realpathZ(self: Dir, pathname: [*:0]const u8, out_buffer: []u8) RealPathError![]u8 {1389pub fn realpathZ(self: Dir, pathname: [*:0]const u8, out_buffer: []u8) RealPathError![]u8 {
1382 if (native_os == .windows) {1390 if (native_os == .windows) {
1383 const pathname_w = try windows.cStrToPrefixedFileW(self.fd, pathname);1391 var pathname_w = try windows.cStrToPrefixedFileW(self.fd, pathname);
1384 return self.realpathW(pathname_w.span(), out_buffer);1392
1393 const wide_slice = try self.realpathW2(pathname_w.span(), &pathname_w.data);
1394
1395 const len = std.unicode.calcWtf8Len(wide_slice);
1396 if (len > out_buffer.len)
1397 return error.NameTooLong;
1398
1399 const end_index = std.unicode.wtf16LeToWtf8(out_buffer, wide_slice);
1400 return out_buffer[0..end_index];
1385 }1401 }
13861402
1387 var flags: posix.O = .{};1403 var flags: posix.O = .{};
...@@ -1410,10 +1426,34 @@ pub fn realpathZ(self: Dir, pathname: [*:0]const u8, out_buffer: []u8) RealPathE...@@ -1410,10 +1426,34 @@ pub fn realpathZ(self: Dir, pathname: [*:0]const u8, out_buffer: []u8) RealPathE
1410 return result;1426 return result;
1411}1427}
14121428
1429/// Deprecated: use `realpathW2`.
1430///
1413/// Windows-only. Same as `Dir.realpath` except `pathname` is WTF16 LE encoded.1431/// Windows-only. Same as `Dir.realpath` except `pathname` is WTF16 LE encoded.
1414/// The result is encoded as [WTF-8](https://simonsapin.github.io/wtf-8/).1432/// The result is encoded as [WTF-8](https://simonsapin.github.io/wtf-8/).
1415/// See also `Dir.realpath`, `realpathW`.1433/// See also `Dir.realpath`, `realpathW`.
1416pub fn realpathW(self: Dir, pathname: []const u16, out_buffer: []u8) RealPathError![]u8 {1434pub fn realpathW(self: Dir, pathname: []const u16, out_buffer: []u8) RealPathError![]u8 {
1435 var wide_buf: [std.os.windows.PATH_MAX_WIDE]u16 = undefined;
1436
1437 const wide_slice = try self.realpathW2(pathname, &wide_buf);
1438
1439 var big_out_buf: [fs.max_path_bytes]u8 = undefined;
1440 const end_index = std.unicode.wtf16LeToWtf8(&big_out_buf, wide_slice);
1441 if (end_index > out_buffer.len)
1442 return error.NameTooLong;
1443 const result = out_buffer[0..end_index];
1444 @memcpy(result, big_out_buf[0..end_index]);
1445 return result;
1446}
1447
1448/// Windows-only. Same as `Dir.realpath` except
1449/// * `pathname` and the result are WTF-16 LE encoded
1450/// * `pathname` is relative or has the NT namespace prefix. See `windows.wToPrefixedFileW` for details.
1451///
1452/// Additionally, `pathname` will never be accessed after `out_buffer` has been written to, so it
1453/// is safe to reuse a single buffer for both.
1454///
1455/// See also `Dir.realpath`, `realpathW`.
1456pub fn realpathW2(self: Dir, pathname: []const u16, out_buffer: []u16) RealPathError![]u16 {
1417 const w = windows;1457 const w = windows;
14181458
1419 const access_mask = w.GENERIC_READ | w.SYNCHRONIZE;1459 const access_mask = w.GENERIC_READ | w.SYNCHRONIZE;
...@@ -1434,13 +1474,7 @@ pub fn realpathW(self: Dir, pathname: []const u16, out_buffer: []u8) RealPathErr...@@ -1434,13 +1474,7 @@ pub fn realpathW(self: Dir, pathname: []const u16, out_buffer: []u8) RealPathErr
1434 };1474 };
1435 defer w.CloseHandle(h_file);1475 defer w.CloseHandle(h_file);
14361476
1437 var wide_buf: [w.PATH_MAX_WIDE]u16 = undefined;1477 return w.GetFinalPathNameByHandle(h_file, .{}, out_buffer);
1438 const wide_slice = try w.GetFinalPathNameByHandle(h_file, .{}, &wide_buf);
1439 const len = std.unicode.calcWtf8Len(wide_slice);
1440 if (len > out_buffer.len)
1441 return error.NameTooLong;
1442 const end_index = std.unicode.wtf16LeToWtf8(out_buffer, wide_slice);
1443 return out_buffer[0..end_index];
1444}1478}
14451479
1446pub const RealPathAllocError = RealPathError || Allocator.Error;1480pub const RealPathAllocError = RealPathError || Allocator.Error;
lib/std/posix.zig+23-25
...@@ -5675,8 +5675,12 @@ pub const RealPathError = error{...@@ -5675,8 +5675,12 @@ pub const RealPathError = error{
5675/// Calling this function is usually a bug.5675/// Calling this function is usually a bug.
5676pub fn realpath(pathname: []const u8, out_buffer: *[max_path_bytes]u8) RealPathError![]u8 {5676pub fn realpath(pathname: []const u8, out_buffer: *[max_path_bytes]u8) RealPathError![]u8 {
5677 if (native_os == .windows) {5677 if (native_os == .windows) {
5678 const pathname_w = try windows.sliceToPrefixedFileW(null, pathname);5678 var pathname_w = try windows.sliceToPrefixedFileW(null, pathname);
5679 return realpathW(pathname_w.span(), out_buffer);5679
5680 const wide_slice = try realpathW2(pathname_w.span(), &pathname_w.data);
5681
5682 const end_index = std.unicode.wtf16LeToWtf8(out_buffer, wide_slice);
5683 return out_buffer[0..end_index];
5680 } else if (native_os == .wasi and !builtin.link_libc) {5684 } else if (native_os == .wasi and !builtin.link_libc) {
5681 @compileError("WASI does not support os.realpath");5685 @compileError("WASI does not support os.realpath");
5682 }5686 }
...@@ -5689,8 +5693,12 @@ pub fn realpath(pathname: []const u8, out_buffer: *[max_path_bytes]u8) RealPathE...@@ -5689,8 +5693,12 @@ pub fn realpath(pathname: []const u8, out_buffer: *[max_path_bytes]u8) RealPathE
5689/// Calling this function is usually a bug.5693/// Calling this function is usually a bug.
5690pub fn realpathZ(pathname: [*:0]const u8, out_buffer: *[max_path_bytes]u8) RealPathError![]u8 {5694pub fn realpathZ(pathname: [*:0]const u8, out_buffer: *[max_path_bytes]u8) RealPathError![]u8 {
5691 if (native_os == .windows) {5695 if (native_os == .windows) {
5692 const pathname_w = try windows.cStrToPrefixedFileW(null, pathname);5696 var pathname_w = try windows.cStrToPrefixedFileW(null, pathname);
5693 return realpathW(pathname_w.span(), out_buffer);5697
5698 const wide_slice = try realpathW2(pathname_w.span(), &pathname_w.data);
5699
5700 const end_index = std.unicode.wtf16LeToWtf8(out_buffer, wide_slice);
5701 return out_buffer[0..end_index];
5694 } else if (native_os == .wasi and !builtin.link_libc) {5702 } else if (native_os == .wasi and !builtin.link_libc) {
5695 return realpath(mem.sliceTo(pathname, 0), out_buffer);5703 return realpath(mem.sliceTo(pathname, 0), out_buffer);
5696 }5704 }
...@@ -5734,34 +5742,24 @@ pub fn realpathZ(pathname: [*:0]const u8, out_buffer: *[max_path_bytes]u8) RealP...@@ -5734,34 +5742,24 @@ pub fn realpathZ(pathname: [*:0]const u8, out_buffer: *[max_path_bytes]u8) RealP
5734 return mem.sliceTo(result_path, 0);5742 return mem.sliceTo(result_path, 0);
5735}5743}
57365744
5745/// Deprecated: use `realpathW2`.
5746///
5737/// Same as `realpath` except `pathname` is WTF16LE-encoded.5747/// Same as `realpath` except `pathname` is WTF16LE-encoded.
5738///5748///
5739/// The result is encoded as [WTF-8](https://simonsapin.github.io/wtf-8/).5749/// The result is encoded as [WTF-8](https://simonsapin.github.io/wtf-8/).
5740///5750///
5741/// Calling this function is usually a bug.5751/// Calling this function is usually a bug.
5742pub fn realpathW(pathname: []const u16, out_buffer: *[max_path_bytes]u8) RealPathError![]u8 {5752pub fn realpathW(pathname: []const u16, out_buffer: *[max_path_bytes]u8) RealPathError![]u8 {
5743 const w = windows;5753 return fs.cwd().realpathW(pathname, out_buffer);
57445754}
5745 const dir = fs.cwd().fd;
5746 const access_mask = w.GENERIC_READ | w.SYNCHRONIZE;
5747 const share_access = w.FILE_SHARE_READ | w.FILE_SHARE_WRITE | w.FILE_SHARE_DELETE;
5748 const creation = w.FILE_OPEN;
5749 const h_file = blk: {
5750 const res = w.OpenFile(pathname, .{
5751 .dir = dir,
5752 .access_mask = access_mask,
5753 .share_access = share_access,
5754 .creation = creation,
5755 .filter = .any,
5756 }) catch |err| switch (err) {
5757 error.WouldBlock => unreachable,
5758 else => |e| return e,
5759 };
5760 break :blk res;
5761 };
5762 defer w.CloseHandle(h_file);
57635755
5764 return std.os.getFdPath(h_file, out_buffer);5756/// Same as `realpath` except `pathname` is WTF16LE-encoded.
5757///
5758/// The result is encoded as WTF16LE.
5759///
5760/// Calling this function is usually a bug.
5761pub fn realpathW2(pathname: []const u16, out_buffer: *[std.os.windows.PATH_MAX_WIDE]u16) RealPathError![]u16 {
5762 return fs.cwd().realpathW2(pathname, out_buffer);
5765}5763}
57665764
5767/// Spurious wakeups are possible and no precision of timing is guaranteed.5765/// Spurious wakeups are possible and no precision of timing is guaranteed.