Skip to content

Commit 0bdd1b5

Browse files
authored
Merge pull request #23657 from mpfaff/realpathW-no-convert
Return WTF-16 from W-suffixed functions instead of converting to WTF-8
2 parents 328ae41 + 7bf740e commit 0bdd1b5

File tree

3 files changed

+79
-38
lines changed

3 files changed

+79
-38
lines changed

lib/std/fs.zig

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ pub const wasi = @import("fs/wasi.zig");
3131
pub const realpath = posix.realpath;
3232
pub const realpathZ = posix.realpathZ;
3333
pub const realpathW = posix.realpathW;
34+
pub const realpathW2 = posix.realpathW2;
3435

3536
pub const getAppDataDir = @import("fs/get_app_data_dir.zig").getAppDataDir;
3637
pub const GetAppDataDirError = @import("fs/get_app_data_dir.zig").GetAppDataDirError;
@@ -642,11 +643,19 @@ pub fn selfExePath(out_buffer: []u8) SelfExePathError![]u8 {
642643
// If ImagePathName is a symlink, then it will contain the path of the
643644
// symlink, not the path that the symlink points to. We want the path
644645
// 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-
return std.fs.cwd().realpathW(pathname_w.span(), out_buffer) catch |err| switch (err) {
646+
var pathname_w = try windows.wToPrefixedFileW(null, image_path_name);
647+
648+
const wide_slice = std.fs.cwd().realpathW2(pathname_w.span(), &pathname_w.data) catch |err| switch (err) {
647649
error.InvalidWtf8 => unreachable,
648650
else => |e| return e,
649651
};
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];
650659
},
651660
else => @compileError("std.fs.selfExePath not supported for this target"),
652661
}

lib/std/fs/Dir.zig

Lines changed: 45 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1369,8 +1369,16 @@ pub fn realpath(self: Dir, pathname: []const u8, out_buffer: []u8) RealPathError
13691369
@compileError("realpath is not available on WASI");
13701370
}
13711371
if (native_os == .windows) {
1372-
const pathname_w = try windows.sliceToPrefixedFileW(self.fd, pathname);
1373-
return self.realpathW(pathname_w.span(), out_buffer);
1372+
var pathname_w = try windows.sliceToPrefixedFileW(self.fd, pathname);
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];
13741382
}
13751383
const pathname_c = try posix.toPosixPath(pathname);
13761384
return self.realpathZ(&pathname_c, out_buffer);
@@ -1380,8 +1388,16 @@ pub fn realpath(self: Dir, pathname: []const u8, out_buffer: []u8) RealPathError
13801388
/// See also `Dir.realpath`, `realpathZ`.
13811389
pub fn realpathZ(self: Dir, pathname: [*:0]const u8, out_buffer: []u8) RealPathError![]u8 {
13821390
if (native_os == .windows) {
1383-
const pathname_w = try windows.cStrToPrefixedFileW(self.fd, pathname);
1384-
return self.realpathW(pathname_w.span(), out_buffer);
1391+
var pathname_w = try windows.cStrToPrefixedFileW(self.fd, pathname);
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];
13851401
}
13861402

13871403
var flags: posix.O = .{};
@@ -1410,10 +1426,34 @@ pub fn realpathZ(self: Dir, pathname: [*:0]const u8, out_buffer: []u8) RealPathE
14101426
return result;
14111427
}
14121428

1429+
/// Deprecated: use `realpathW2`.
1430+
///
14131431
/// Windows-only. Same as `Dir.realpath` except `pathname` is WTF16 LE encoded.
14141432
/// The result is encoded as [WTF-8](https://simonsapin.github.io/wtf-8/).
14151433
/// See also `Dir.realpath`, `realpathW`.
14161434
pub 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`.
1456+
pub fn realpathW2(self: Dir, pathname: []const u16, out_buffer: []u16) RealPathError![]u16 {
14171457
const w = windows;
14181458

14191459
const access_mask = w.GENERIC_READ | w.SYNCHRONIZE;
@@ -1434,13 +1474,7 @@ pub fn realpathW(self: Dir, pathname: []const u16, out_buffer: []u8) RealPathErr
14341474
};
14351475
defer w.CloseHandle(h_file);
14361476

1437-
var wide_buf: [w.PATH_MAX_WIDE]u16 = undefined;
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];
1477+
return w.GetFinalPathNameByHandle(h_file, .{}, out_buffer);
14441478
}
14451479

14461480
pub const RealPathAllocError = RealPathError || Allocator.Error;

lib/std/posix.zig

Lines changed: 23 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -5675,8 +5675,12 @@ pub const RealPathError = error{
56755675
/// Calling this function is usually a bug.
56765676
pub fn realpath(pathname: []const u8, out_buffer: *[max_path_bytes]u8) RealPathError![]u8 {
56775677
if (native_os == .windows) {
5678-
const pathname_w = try windows.sliceToPrefixedFileW(null, pathname);
5679-
return realpathW(pathname_w.span(), out_buffer);
5678+
var pathname_w = try windows.sliceToPrefixedFileW(null, pathname);
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];
56805684
} else if (native_os == .wasi and !builtin.link_libc) {
56815685
@compileError("WASI does not support os.realpath");
56825686
}
@@ -5689,8 +5693,12 @@ pub fn realpath(pathname: []const u8, out_buffer: *[max_path_bytes]u8) RealPathE
56895693
/// Calling this function is usually a bug.
56905694
pub fn realpathZ(pathname: [*:0]const u8, out_buffer: *[max_path_bytes]u8) RealPathError![]u8 {
56915695
if (native_os == .windows) {
5692-
const pathname_w = try windows.cStrToPrefixedFileW(null, pathname);
5693-
return realpathW(pathname_w.span(), out_buffer);
5696+
var pathname_w = try windows.cStrToPrefixedFileW(null, pathname);
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];
56945702
} else if (native_os == .wasi and !builtin.link_libc) {
56955703
return realpath(mem.sliceTo(pathname, 0), out_buffer);
56965704
}
@@ -5734,34 +5742,24 @@ pub fn realpathZ(pathname: [*:0]const u8, out_buffer: *[max_path_bytes]u8) RealP
57345742
return mem.sliceTo(result_path, 0);
57355743
}
57365744

5745+
/// Deprecated: use `realpathW2`.
5746+
///
57375747
/// Same as `realpath` except `pathname` is WTF16LE-encoded.
57385748
///
57395749
/// The result is encoded as [WTF-8](https://simonsapin.github.io/wtf-8/).
57405750
///
57415751
/// Calling this function is usually a bug.
57425752
pub fn realpathW(pathname: []const u16, out_buffer: *[max_path_bytes]u8) RealPathError![]u8 {
5743-
const w = windows;
5744-
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);
5753+
return fs.cwd().realpathW(pathname, out_buffer);
5754+
}
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.
5761+
pub fn realpathW2(pathname: []const u16, out_buffer: *[std.os.windows.PATH_MAX_WIDE]u16) RealPathError![]u16 {
5762+
return fs.cwd().realpathW2(pathname, out_buffer);
57655763
}
57665764

57675765
/// Spurious wakeups are possible and no precision of timing is guaranteed.

0 commit comments

Comments
 (0)