Skip to content

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

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 5 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 11 additions & 2 deletions lib/std/fs.zig
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ pub const wasi = @import("fs/wasi.zig");
pub const realpath = posix.realpath;
pub const realpathZ = posix.realpathZ;
pub const realpathW = posix.realpathW;
pub const realpathW2 = posix.realpathW2;

pub const getAppDataDir = @import("fs/get_app_data_dir.zig").getAppDataDir;
pub const GetAppDataDirError = @import("fs/get_app_data_dir.zig").GetAppDataDirError;
Expand Down Expand Up @@ -651,11 +652,19 @@ pub fn selfExePath(out_buffer: []u8) SelfExePathError![]u8 {
// If ImagePathName is a symlink, then it will contain the path of the
// 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 pathname_w = try windows.wToPrefixedFileW(null, image_path_name);

const wide_slice = std.fs.cwd().realpathW2(pathname_w.span(), &pathname_w.data) 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"),
}
Expand Down
56 changes: 45 additions & 11 deletions lib/std/fs/Dir.zig
Original file line number Diff line number Diff line change
Expand Up @@ -1282,8 +1282,16 @@ pub fn realpath(self: Dir, pathname: []const u8, out_buffer: []u8) RealPathError
@compileError("realpath is not available on WASI");
}
if (native_os == .windows) {
const pathname_w = try windows.sliceToPrefixedFileW(self.fd, pathname);
return self.realpathW(pathname_w.span(), out_buffer);
var pathname_w = try windows.sliceToPrefixedFileW(self.fd, pathname);

const wide_slice = try self.realpathW2(pathname_w.span(), &pathname_w.data);

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);
Expand All @@ -1293,8 +1301,16 @@ pub fn realpath(self: Dir, pathname: []const u8, out_buffer: []u8) RealPathError
/// See also `Dir.realpath`, `realpathZ`.
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 pathname_w = try windows.cStrToPrefixedFileW(self.fd, pathname);

const wide_slice = try self.realpathW2(pathname_w.span(), &pathname_w.data);

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 = .{};
Expand Down Expand Up @@ -1323,10 +1339,34 @@ pub fn realpathZ(self: Dir, pathname: [*:0]const u8, out_buffer: []u8) RealPathE
return result;
}

/// Deprecated: use `realpathW2`.
///
/// 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/).
/// See also `Dir.realpath`, `realpathW`.
pub fn realpathW(self: Dir, pathname: []const u16, out_buffer: []u8) RealPathError![]u8 {
var wide_buf: [std.os.windows.PATH_MAX_WIDE]u16 = undefined;

const wide_slice = try self.realpathW2(pathname, &wide_buf);

var big_out_buf: [fs.max_path_bytes]u8 = undefined;
const end_index = std.unicode.wtf16LeToWtf8(&big_out_buf, wide_slice);
if (end_index > out_buffer.len)
return error.NameTooLong;
const result = out_buffer[0..end_index];
@memcpy(result, big_out_buf[0..end_index]);
return result;
}

/// Windows-only. Same as `Dir.realpath` except
/// * `pathname` and the result are WTF-16 LE encoded
/// * `pathname` is relative or has the NT namespace prefix. See `windows.wToPrefixedFileW` for details.
///
/// Additionally, `pathname` will never be accessed after `out_buffer` has been written to, so it
/// is safe to reuse a single buffer for both.
///
/// See also `Dir.realpath`, `realpathW`.
pub fn realpathW2(self: Dir, pathname: []const u16, out_buffer: []u16) RealPathError![]u16 {
const w = windows;

const access_mask = w.GENERIC_READ | w.SYNCHRONIZE;
Expand All @@ -1347,13 +1387,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;
Expand Down
48 changes: 23 additions & 25 deletions lib/std/posix.zig
Original file line number Diff line number Diff line change
Expand Up @@ -5533,8 +5533,12 @@ pub const RealPathError = error{
/// Calling this function is usually a bug.
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 pathname_w = try windows.sliceToPrefixedFileW(null, pathname);

const wide_slice = try realpathW2(pathname_w.span(), &pathname_w.data);

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");
}
Expand All @@ -5547,8 +5551,12 @@ pub fn realpath(pathname: []const u8, out_buffer: *[max_path_bytes]u8) RealPathE
/// Calling this function is usually a bug.
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 pathname_w = try windows.cStrToPrefixedFileW(null, pathname);

const wide_slice = try realpathW2(pathname_w.span(), &pathname_w.data);

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);
}
Expand Down Expand Up @@ -5592,34 +5600,24 @@ pub fn realpathZ(pathname: [*:0]const u8, out_buffer: *[max_path_bytes]u8) RealP
return mem.sliceTo(result_path, 0);
}

/// Deprecated: use `realpathW2`.
///
/// Same as `realpath` except `pathname` is WTF16LE-encoded.
///
/// The result is encoded as [WTF-8](https://simonsapin.github.io/wtf-8/).
///
/// 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 fs.cwd().realpathW(pathname, out_buffer);
}

return std.os.getFdPath(h_file, out_buffer);
/// Same as `realpath` except `pathname` is WTF16LE-encoded.
///
/// The result is encoded as WTF16LE.
///
/// Calling this function is usually a bug.
pub fn realpathW2(pathname: []const u16, out_buffer: *[std.os.windows.PATH_MAX_WIDE]u16) RealPathError![]u16 {
return fs.cwd().realpathW2(pathname, out_buffer);
}

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