authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2026-01-30 06:38:08+01:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2026-01-30 06:38:08+01:00
log377bb8f23744001562cda4e15ab1f041bc3c6686
tree10fa53817ee2ea6d8a85adf57a35e79df176e8e3
parentccd82ae7cc5ce3f708a919a070bffc7572416c62
parentb1d1806fef56a72d0df14ad3a54c3074b1e55a69

Merge pull request 'std: migrate getcwd to Io' (#31047) from getcwd into master

Reviewed-on: https://codeberg.org/ziglang/zig/pulls/31047

24 files changed, 128 insertions(+), 156 deletions(-)

lib/compiler/build_runner.zig+1-1
...@@ -83,7 +83,7 @@ pub fn main(init: process.Init.Minimal) !void {...@@ -83,7 +83,7 @@ pub fn main(init: process.Init.Minimal) !void {
83 .io = io,83 .io = io,
84 .gpa = gpa,84 .gpa = gpa,
85 .manifest_dir = try local_cache_directory.handle.createDirPathOpen(io, "h", .{}),85 .manifest_dir = try local_cache_directory.handle.createDirPathOpen(io, "h", .{}),
86 .cwd = try process.getCwdAlloc(single_threaded_arena.allocator()),86 .cwd = try process.currentPathAlloc(io, single_threaded_arena.allocator()),
87 },87 },
88 .zig_exe = zig_exe,88 .zig_exe = zig_exe,
89 .environ_map = try init.environ.createMap(arena),89 .environ_map = try init.environ.createMap(arena),
lib/std/Build/Cache.zig+4-4
...@@ -1315,7 +1315,7 @@ test "cache file and then recall it" {...@@ -1315,7 +1315,7 @@ test "cache file and then recall it" {
1315 var tmp = testing.tmpDir(.{});1315 var tmp = testing.tmpDir(.{});
1316 defer tmp.cleanup();1316 defer tmp.cleanup();
13171317
1318 const cwd = try std.process.getCwdAlloc(testing.allocator);1318 const cwd = try std.process.currentPathAlloc(io, testing.allocator);
1319 defer testing.allocator.free(cwd);1319 defer testing.allocator.free(cwd);
13201320
1321 const temp_file = "test.txt";1321 const temp_file = "test.txt";
...@@ -1383,7 +1383,7 @@ test "check that changing a file makes cache fail" {...@@ -1383,7 +1383,7 @@ test "check that changing a file makes cache fail" {
1383 var tmp = testing.tmpDir(.{});1383 var tmp = testing.tmpDir(.{});
1384 defer tmp.cleanup();1384 defer tmp.cleanup();
13851385
1386 const cwd = try std.process.getCwdAlloc(testing.allocator);1386 const cwd = try std.process.currentPathAlloc(io, testing.allocator);
1387 defer testing.allocator.free(cwd);1387 defer testing.allocator.free(cwd);
13881388
1389 const temp_file = "cache_hash_change_file_test.txt";1389 const temp_file = "cache_hash_change_file_test.txt";
...@@ -1459,7 +1459,7 @@ test "no file inputs" {...@@ -1459,7 +1459,7 @@ test "no file inputs" {
1459 var tmp = testing.tmpDir(.{});1459 var tmp = testing.tmpDir(.{});
1460 defer tmp.cleanup();1460 defer tmp.cleanup();
14611461
1462 const cwd = try std.process.getCwdAlloc(testing.allocator);1462 const cwd = try std.process.currentPathAlloc(io, testing.allocator);
1463 defer testing.allocator.free(cwd);1463 defer testing.allocator.free(cwd);
14641464
1465 const temp_manifest_dir = "no_file_inputs_manifest_dir";1465 const temp_manifest_dir = "no_file_inputs_manifest_dir";
...@@ -1509,7 +1509,7 @@ test "Manifest with files added after initial hash work" {...@@ -1509,7 +1509,7 @@ test "Manifest with files added after initial hash work" {
1509 var tmp = testing.tmpDir(.{});1509 var tmp = testing.tmpDir(.{});
1510 defer tmp.cleanup();1510 defer tmp.cleanup();
15111511
1512 const cwd = try std.process.getCwdAlloc(testing.allocator);1512 const cwd = try std.process.currentPathAlloc(io, testing.allocator);
1513 defer testing.allocator.free(cwd);1513 defer testing.allocator.free(cwd);
15141514
1515 const temp_file1 = "cache_hash_post_file_test1.txt";1515 const temp_file1 = "cache_hash_post_file_test1.txt";
lib/std/Build/Step/Options.zig+1-1
...@@ -519,7 +519,7 @@ test Options {...@@ -519,7 +519,7 @@ test Options {
519 var arena = std.heap.ArenaAllocator.init(std.testing.allocator);519 var arena = std.heap.ArenaAllocator.init(std.testing.allocator);
520 defer arena.deinit();520 defer arena.deinit();
521521
522 const cwd = try std.process.getCwdAlloc(std.testing.allocator);522 const cwd = try std.process.currentPathAlloc(io, std.testing.allocator);
523 defer std.testing.allocator.free(cwd);523 defer std.testing.allocator.free(cwd);
524524
525 var graph: std.Build.Graph = .{525 var graph: std.Build.Graph = .{
lib/std/Io.zig+1
...@@ -665,6 +665,7 @@ pub const VTable = struct {...@@ -665,6 +665,7 @@ pub const VTable = struct {
665 lockStderr: *const fn (?*anyopaque, ?Terminal.Mode) Cancelable!LockedStderr,665 lockStderr: *const fn (?*anyopaque, ?Terminal.Mode) Cancelable!LockedStderr,
666 tryLockStderr: *const fn (?*anyopaque, ?Terminal.Mode) Cancelable!?LockedStderr,666 tryLockStderr: *const fn (?*anyopaque, ?Terminal.Mode) Cancelable!?LockedStderr,
667 unlockStderr: *const fn (?*anyopaque) void,667 unlockStderr: *const fn (?*anyopaque) void,
668 processCurrentPath: *const fn (?*anyopaque, buffer: []u8) std.process.CurrentPathError!usize,
668 processSetCurrentDir: *const fn (?*anyopaque, Dir) std.process.SetCurrentDirError!void,669 processSetCurrentDir: *const fn (?*anyopaque, Dir) std.process.SetCurrentDirError!void,
669 processReplace: *const fn (?*anyopaque, std.process.ReplaceOptions) std.process.ReplaceError,670 processReplace: *const fn (?*anyopaque, std.process.ReplaceOptions) std.process.ReplaceError,
670 processReplacePath: *const fn (?*anyopaque, Dir, std.process.ReplaceOptions) std.process.ReplaceError,671 processReplacePath: *const fn (?*anyopaque, Dir, std.process.ReplaceOptions) std.process.ReplaceError,
lib/std/Io/Threaded.zig+46
...@@ -1643,6 +1643,7 @@ pub fn io(t: *Threaded) Io {...@@ -1643,6 +1643,7 @@ pub fn io(t: *Threaded) Io {
1643 .lockStderr = lockStderr,1643 .lockStderr = lockStderr,
1644 .tryLockStderr = tryLockStderr,1644 .tryLockStderr = tryLockStderr,
1645 .unlockStderr = unlockStderr,1645 .unlockStderr = unlockStderr,
1646 .processCurrentPath = processCurrentPath,
1646 .processSetCurrentDir = processSetCurrentDir,1647 .processSetCurrentDir = processSetCurrentDir,
1647 .processReplace = processReplace,1648 .processReplace = processReplace,
1648 .processReplacePath = processReplacePath,1649 .processReplacePath = processReplacePath,
...@@ -1801,6 +1802,7 @@ pub fn ioBasic(t: *Threaded) Io {...@@ -1801,6 +1802,7 @@ pub fn ioBasic(t: *Threaded) Io {
1801 .lockStderr = lockStderr,1802 .lockStderr = lockStderr,
1802 .tryLockStderr = tryLockStderr,1803 .tryLockStderr = tryLockStderr,
1803 .unlockStderr = unlockStderr,1804 .unlockStderr = unlockStderr,
1805 .processCurrentPath = processCurrentPath,
1804 .processSetCurrentDir = processSetCurrentDir,1806 .processSetCurrentDir = processSetCurrentDir,
1805 .processReplace = processReplace,1807 .processReplace = processReplace,
1806 .processReplacePath = processReplacePath,1808 .processReplacePath = processReplacePath,
...@@ -2728,6 +2730,7 @@ fn dirCreateDirPathOpenWindows(...@@ -2728,6 +2730,7 @@ fn dirCreateDirPathOpenWindows(
2728 // This can happen if the directory has 'List folder contents' permission set to 'Deny'2730 // This can happen if the directory has 'List folder contents' permission set to 'Deny'
2729 // and the directory is trying to be opened for iteration.2731 // and the directory is trying to be opened for iteration.
2730 .ACCESS_DENIED => return syscall.fail(error.AccessDenied),2732 .ACCESS_DENIED => return syscall.fail(error.AccessDenied),
2733 .DISK_FULL => return syscall.fail(error.NoSpaceLeft),
2731 .INVALID_PARAMETER => |s| return syscall.ntstatusBug(s),2734 .INVALID_PARAMETER => |s| return syscall.ntstatusBug(s),
2732 else => |s| return syscall.unexpectedNtstatus(s),2735 else => |s| return syscall.unexpectedNtstatus(s),
2733 };2736 };
...@@ -3668,6 +3671,7 @@ fn dirCreateFileWindows(...@@ -3668,6 +3671,7 @@ fn dirCreateFileWindows(
3668 .NOT_A_DIRECTORY => return syscall.fail(error.NotDir),3671 .NOT_A_DIRECTORY => return syscall.fail(error.NotDir),
3669 .USER_MAPPED_FILE => return syscall.fail(error.AccessDenied),3672 .USER_MAPPED_FILE => return syscall.fail(error.AccessDenied),
3670 .VIRUS_INFECTED, .VIRUS_DELETED => return syscall.fail(error.AntivirusInterference),3673 .VIRUS_INFECTED, .VIRUS_DELETED => return syscall.fail(error.AntivirusInterference),
3674 .DISK_FULL => return syscall.fail(error.NoSpaceLeft),
3671 .INVALID_PARAMETER => |status| return syscall.ntstatusBug(status),3675 .INVALID_PARAMETER => |status| return syscall.ntstatusBug(status),
3672 .OBJECT_PATH_SYNTAX_BAD => |status| return syscall.ntstatusBug(status),3676 .OBJECT_PATH_SYNTAX_BAD => |status| return syscall.ntstatusBug(status),
3673 .INVALID_HANDLE => |status| return syscall.ntstatusBug(status),3677 .INVALID_HANDLE => |status| return syscall.ntstatusBug(status),
...@@ -9213,6 +9217,7 @@ fn writeFilePositionalWindows(...@@ -9213,6 +9217,7 @@ fn writeFilePositionalWindows(
9213 .LOCK_VIOLATION => return syscall.fail(error.LockViolation),9217 .LOCK_VIOLATION => return syscall.fail(error.LockViolation),
9214 .ACCESS_DENIED => return syscall.fail(error.AccessDenied),9218 .ACCESS_DENIED => return syscall.fail(error.AccessDenied),
9215 .WORKING_SET_QUOTA => return syscall.fail(error.SystemResources),9219 .WORKING_SET_QUOTA => return syscall.fail(error.SystemResources),
9220 .DISK_FULL => return syscall.fail(error.NoSpaceLeft),
9216 else => |err| {9221 else => |err| {
9217 syscall.finish();9222 syscall.finish();
9218 return windows.unexpectedError(err);9223 return windows.unexpectedError(err);
...@@ -9373,6 +9378,7 @@ fn writeFileStreamingWindows(...@@ -9373,6 +9378,7 @@ fn writeFileStreamingWindows(
9373 .LOCK_VIOLATION => return syscall.fail(error.LockViolation),9378 .LOCK_VIOLATION => return syscall.fail(error.LockViolation),
9374 .ACCESS_DENIED => return syscall.fail(error.AccessDenied),9379 .ACCESS_DENIED => return syscall.fail(error.AccessDenied),
9375 .WORKING_SET_QUOTA => return syscall.fail(error.SystemResources),9380 .WORKING_SET_QUOTA => return syscall.fail(error.SystemResources),
9381 .DISK_FULL => return syscall.fail(error.NoSpaceLeft),
9376 else => |err| {9382 else => |err| {
9377 syscall.finish();9383 syscall.finish();
9378 return windows.unexpectedError(err);9384 return windows.unexpectedError(err);
...@@ -12602,6 +12608,46 @@ fn unlockStderr(userdata: ?*anyopaque) void {...@@ -12602,6 +12608,46 @@ fn unlockStderr(userdata: ?*anyopaque) void {
12602 process.stderr_thread_mutex.unlock();12608 process.stderr_thread_mutex.unlock();
12603}12609}
1260412610
12611fn processCurrentPath(userdata: ?*anyopaque, buffer: []u8) process.CurrentPathError!usize {
12612 const t: *Threaded = @ptrCast(@alignCast(userdata));
12613 _ = t;
12614 if (is_windows) {
12615 var wtf16le_buf: [windows.PATH_MAX_WIDE:0]u16 = undefined;
12616 const n = windows.ntdll.RtlGetCurrentDirectory_U(wtf16le_buf.len * 2 + 2, &wtf16le_buf) / 2;
12617 if (n == 0) return error.Unexpected;
12618 assert(n <= wtf16le_buf.len);
12619 const wtf16le_slice = wtf16le_buf[0..n];
12620 var end_index: usize = 0;
12621 var it = std.unicode.Wtf16LeIterator.init(wtf16le_slice);
12622 while (it.nextCodepoint()) |codepoint| {
12623 const seq_len = std.unicode.utf8CodepointSequenceLength(codepoint) catch unreachable;
12624 if (end_index + seq_len >= buffer.len)
12625 return error.NameTooLong;
12626 end_index += std.unicode.wtf8Encode(codepoint, buffer[end_index..]) catch unreachable;
12627 }
12628 return end_index;
12629 } else if (native_os == .wasi and !builtin.link_libc) {
12630 if (buffer.len == 0) return error.NameTooLong;
12631 buffer[0] = '.';
12632 return 1;
12633 }
12634
12635 const err: posix.E = if (builtin.link_libc) err: {
12636 const c_err = if (std.c.getcwd(buffer.ptr, buffer.len)) |_| 0 else std.c._errno().*;
12637 break :err @enumFromInt(c_err);
12638 } else err: {
12639 break :err posix.errno(posix.system.getcwd(buffer.ptr, buffer.len));
12640 };
12641 switch (err) {
12642 .SUCCESS => return std.mem.findScalar(u8, buffer, 0).?,
12643 .NOENT => return error.CurrentDirUnlinked,
12644 .RANGE => return error.NameTooLong,
12645 .FAULT => |e| return errnoBug(e),
12646 .INVAL => |e| return errnoBug(e),
12647 else => return posix.unexpectedErrno(err),
12648 }
12649}
12650
12605fn processSetCurrentDir(userdata: ?*anyopaque, dir: Dir) process.SetCurrentDirError!void {12651fn processSetCurrentDir(userdata: ?*anyopaque, dir: Dir) process.SetCurrentDirError!void {
12606 if (native_os == .wasi) return error.OperationUnsupported;12652 if (native_os == .wasi) return error.OperationUnsupported;
12607 const t: *Threaded = @ptrCast(@alignCast(userdata));12653 const t: *Threaded = @ptrCast(@alignCast(userdata));
lib/std/fs/test.zig+1-1
...@@ -1787,7 +1787,7 @@ test "open file with exclusive nonblocking lock twice (absolute paths)" {...@@ -1787,7 +1787,7 @@ test "open file with exclusive nonblocking lock twice (absolute paths)" {
17871787
1788 const gpa = testing.allocator;1788 const gpa = testing.allocator;
17891789
1790 const cwd = try std.process.getCwdAlloc(gpa);1790 const cwd = try std.process.currentPathAlloc(io, gpa);
1791 defer gpa.free(cwd);1791 defer gpa.free(cwd);
17921792
1793 const filename = try Dir.path.resolve(gpa, &.{ cwd, sub_path });1793 const filename = try Dir.path.resolve(gpa, &.{ cwd, sub_path });
lib/std/os/windows.zig-28
...@@ -2878,34 +2878,6 @@ pub fn CloseHandle(hObject: HANDLE) void {...@@ -2878,34 +2878,6 @@ pub fn CloseHandle(hObject: HANDLE) void {
2878 assert(ntdll.NtClose(hObject) == .SUCCESS);2878 assert(ntdll.NtClose(hObject) == .SUCCESS);
2879}2879}
28802880
2881pub const GetCurrentDirectoryError = error{
2882 NameTooLong,
2883 Unexpected,
2884};
2885
2886/// The result is a slice of `buffer`, indexed from 0.
2887/// The result is encoded as [WTF-8](https://wtf-8.codeberg.page/).
2888pub fn GetCurrentDirectory(buffer: []u8) GetCurrentDirectoryError![]u8 {
2889 var wtf16le_buf: [PATH_MAX_WIDE:0]u16 = undefined;
2890 const result = kernel32.GetCurrentDirectoryW(wtf16le_buf.len + 1, &wtf16le_buf);
2891 if (result == 0) {
2892 switch (GetLastError()) {
2893 else => |err| return unexpectedError(err),
2894 }
2895 }
2896 assert(result <= wtf16le_buf.len);
2897 const wtf16le_slice = wtf16le_buf[0..result];
2898 var end_index: usize = 0;
2899 var it = std.unicode.Wtf16LeIterator.init(wtf16le_slice);
2900 while (it.nextCodepoint()) |codepoint| {
2901 const seq_len = std.unicode.utf8CodepointSequenceLength(codepoint) catch unreachable;
2902 if (end_index + seq_len >= buffer.len)
2903 return error.NameTooLong;
2904 end_index += std.unicode.wtf8Encode(codepoint, buffer[end_index..]) catch unreachable;
2905 }
2906 return buffer[0..end_index];
2907}
2908
2909pub const GetStdHandleError = error{2881pub const GetStdHandleError = error{
2910 NoStandardHandleAttached,2882 NoStandardHandleAttached,
2911 Unexpected,2883 Unexpected,
lib/std/os/windows/kernel32.zig-6
...@@ -128,12 +128,6 @@ pub extern "kernel32" fn SetFileCompletionNotificationModes(...@@ -128,12 +128,6 @@ pub extern "kernel32" fn SetFileCompletionNotificationModes(
128 Flags: UCHAR,128 Flags: UCHAR,
129) callconv(.winapi) BOOL;129) callconv(.winapi) BOOL;
130130
131// TODO: `RtlGetCurrentDirectory_U(nBufferLength * 2, lpBuffer)`
132pub extern "kernel32" fn GetCurrentDirectoryW(
133 nBufferLength: DWORD,
134 lpBuffer: ?[*]WCHAR,
135) callconv(.winapi) DWORD;
136
137pub extern "kernel32" fn ReadFile(131pub extern "kernel32" fn ReadFile(
138 hFile: HANDLE,132 hFile: HANDLE,
139 lpBuffer: LPVOID,133 lpBuffer: LPVOID,
lib/std/os/windows/ntdll.zig+5
...@@ -496,6 +496,11 @@ pub extern "ntdll" fn RtlGetFullPathName_U(...@@ -496,6 +496,11 @@ pub extern "ntdll" fn RtlGetFullPathName_U(
496 ShortName: ?*[*:0]const u16,496 ShortName: ?*[*:0]const u16,
497) callconv(.winapi) ULONG;497) callconv(.winapi) ULONG;
498498
499pub extern "ntdll" fn RtlGetCurrentDirectory_U(
500 BufferByteLength: ULONG,
501 Buffer: [*]u16,
502) callconv(.winapi) ULONG;
503
499pub extern "ntdll" fn RtlGetSystemTimePrecise() callconv(.winapi) LARGE_INTEGER;504pub extern "ntdll" fn RtlGetSystemTimePrecise() callconv(.winapi) LARGE_INTEGER;
500505
501pub extern "ntdll" fn RtlInitializeCriticalSection(506pub extern "ntdll" fn RtlInitializeCriticalSection(
lib/std/posix.zig-34
...@@ -519,40 +519,6 @@ pub fn getppid() pid_t {...@@ -519,40 +519,6 @@ pub fn getppid() pid_t {
519 return system.getppid();519 return system.getppid();
520}520}
521521
522pub const GetCwdError = error{
523 NameTooLong,
524 /// Not possible on Windows.
525 CurrentWorkingDirectoryUnlinked,
526} || UnexpectedError;
527
528/// The result is a slice of out_buffer, indexed from 0.
529pub fn getcwd(out_buffer: []u8) GetCwdError![]u8 {
530 if (native_os == .windows) {
531 return windows.GetCurrentDirectory(out_buffer);
532 } else if (native_os == .wasi and !builtin.link_libc) {
533 const path = ".";
534 if (out_buffer.len < path.len) return error.NameTooLong;
535 const result = out_buffer[0..path.len];
536 @memcpy(result, path);
537 return result;
538 }
539
540 const err: E = if (builtin.link_libc) err: {
541 const c_err = if (std.c.getcwd(out_buffer.ptr, out_buffer.len)) |_| 0 else std.c._errno().*;
542 break :err @enumFromInt(c_err);
543 } else err: {
544 break :err errno(system.getcwd(out_buffer.ptr, out_buffer.len));
545 };
546 switch (err) {
547 .SUCCESS => return mem.sliceTo(out_buffer, 0),
548 .FAULT => unreachable,
549 .INVAL => unreachable,
550 .NOENT => return error.CurrentWorkingDirectoryUnlinked,
551 .RANGE => return error.NameTooLong,
552 else => return unexpectedErrno(err),
553 }
554}
555
556pub const SocketError = error{522pub const SocketError = error{
557 /// Permission to create a socket of the specified type and/or523 /// Permission to create a socket of the specified type and/or
558 /// pro‐tocol is denied.524 /// pro‐tocol is denied.
lib/std/process.zig+27-40
...@@ -63,53 +63,40 @@ pub const Init = struct {...@@ -63,53 +63,40 @@ pub const Init = struct {
63 };63 };
64};64};
6565
66pub const GetCwdError = posix.GetCwdError;66pub const CurrentPathError = error{
67 NameTooLong,
68 /// Not possible on Windows. Always returned on WASI.
69 CurrentDirUnlinked,
70} || Io.UnexpectedError;
6771
68/// The result is a slice of `out_buffer`, from index `0`.
69/// On Windows, the result is encoded as [WTF-8](https://wtf-8.codeberg.page/).72/// On Windows, the result is encoded as [WTF-8](https://wtf-8.codeberg.page/).
70/// On other platforms, the result is an opaque sequence of bytes with no particular encoding.73/// On other platforms, the result is an opaque sequence of bytes with no
71pub fn getCwd(out_buffer: []u8) GetCwdError![]u8 {74/// particular encoding.
72 return posix.getcwd(out_buffer);75pub fn currentPath(io: Io, buffer: []u8) CurrentPathError!usize {
76 return io.vtable.processCurrentPath(io.userdata, buffer);
73}77}
7478
75// Same as GetCwdError, minus error.NameTooLong + Allocator.Error79pub const CurrentPathAllocError = Allocator.Error || error{
76pub const GetCwdAllocError = Allocator.Error || error{80 /// Not possible on Windows. Always returned on WASI.
77 /// Not possible on Windows.81 CurrentDirUnlinked,
78 CurrentWorkingDirectoryUnlinked,82} || Io.UnexpectedError;
79} || posix.UnexpectedError;
8083
81/// Caller must free the returned memory.
82/// On Windows, the result is encoded as [WTF-8](https://wtf-8.codeberg.page/).84/// On Windows, the result is encoded as [WTF-8](https://wtf-8.codeberg.page/).
83/// On other platforms, the result is an opaque sequence of bytes with no particular encoding.85/// On other platforms, the result is an opaque sequence of bytes with no
84pub fn getCwdAlloc(allocator: Allocator) GetCwdAllocError![]u8 {86/// particular encoding.
85 // The use of max_path_bytes here is just a heuristic: most paths will fit87///
86 // in stack_buf, avoiding an extra allocation in the common case.88/// Caller owns returned memory.
87 var stack_buf: [max_path_bytes]u8 = undefined;89pub fn currentPathAlloc(io: Io, allocator: Allocator) CurrentPathAllocError![:0]u8 {
88 var heap_buf: ?[]u8 = null;90 var buffer: [max_path_bytes]u8 = undefined;
89 defer if (heap_buf) |buf| allocator.free(buf);91 const n = currentPath(io, &buffer) catch |err| switch (err) {
9092 error.NameTooLong => unreachable,
91 var current_buf: []u8 = &stack_buf;93 else => |e| return e,
92 while (true) {94 };
93 if (posix.getcwd(current_buf)) |slice| {95 return allocator.dupeZ(u8, buffer[0..n]);
94 return allocator.dupe(u8, slice);
95 } else |err| switch (err) {
96 error.NameTooLong => {
97 // The path is too long to fit in stack_buf. Allocate geometrically
98 // increasing buffers until we find one that works
99 const new_capacity = current_buf.len * 2;
100 if (heap_buf) |buf| allocator.free(buf);
101 current_buf = try allocator.alloc(u8, new_capacity);
102 heap_buf = current_buf;
103 },
104 else => |e| return e,
105 }
106 }
107}96}
10897
109test getCwdAlloc {98test currentPathAlloc {
110 if (native_os == .wasi) return error.SkipZigTest;99 const cwd = try currentPathAlloc(testing.io, testing.allocator);
111
112 const cwd = try getCwdAlloc(testing.allocator);
113 testing.allocator.free(cwd);100 testing.allocator.free(cwd);
114}101}
115102
...@@ -466,7 +453,7 @@ pub fn spawnPath(io: Io, dir: Io.Dir, options: SpawnOptions) SpawnError!Child {...@@ -466,7 +453,7 @@ pub fn spawnPath(io: Io, dir: Io.Dir, options: SpawnOptions) SpawnError!Child {
466 return io.vtable.processSpawnPath(io.userdata, dir, options);453 return io.vtable.processSpawnPath(io.userdata, dir, options);
467}454}
468455
469pub const RunError = posix.GetCwdError || posix.ReadError || SpawnError || posix.PollError || error{456pub const RunError = CurrentPathError || posix.ReadError || SpawnError || posix.PollError || error{
470 StdoutStreamTooLong,457 StdoutStreamTooLong,
471 StderrStreamTooLong,458 StderrStreamTooLong,
472};459};
src/Compilation.zig+1-1
...@@ -767,7 +767,7 @@ pub const Directories = struct {...@@ -767,7 +767,7 @@ pub const Directories = struct {
767 ) Directories {767 ) Directories {
768 const wasi = builtin.target.os.tag == .wasi;768 const wasi = builtin.target.os.tag == .wasi;
769769
770 const cwd = introspect.getResolvedCwd(arena) catch |err| {770 const cwd = introspect.getResolvedCwd(io, arena) catch |err| {
771 fatal("unable to get cwd: {t}", .{err});771 fatal("unable to get cwd: {t}", .{err});
772 };772 };
773773
src/introspect.zig+6-6
...@@ -43,7 +43,7 @@ fn testZigInstallPrefix(io: Io, base_dir: Io.Dir) ?Cache.Directory {...@@ -43,7 +43,7 @@ fn testZigInstallPrefix(io: Io, base_dir: Io.Dir) ?Cache.Directory {
4343
44/// Both the directory handle and the path are newly allocated resources which the caller now owns.44/// Both the directory handle and the path are newly allocated resources which the caller now owns.
45pub fn findZigLibDir(gpa: Allocator, io: Io) !Cache.Directory {45pub fn findZigLibDir(gpa: Allocator, io: Io) !Cache.Directory {
46 const cwd_path = try getResolvedCwd(gpa);46 const cwd_path = try getResolvedCwd(io, gpa);
47 defer gpa.free(cwd_path);47 defer gpa.free(cwd_path);
48 const self_exe_path = try std.process.executablePathAlloc(io, gpa);48 const self_exe_path = try std.process.executablePathAlloc(io, gpa);
49 defer gpa.free(self_exe_path);49 defer gpa.free(self_exe_path);
...@@ -51,23 +51,23 @@ pub fn findZigLibDir(gpa: Allocator, io: Io) !Cache.Directory {...@@ -51,23 +51,23 @@ pub fn findZigLibDir(gpa: Allocator, io: Io) !Cache.Directory {
51 return findZigLibDirFromSelfExe(gpa, io, cwd_path, self_exe_path);51 return findZigLibDirFromSelfExe(gpa, io, cwd_path, self_exe_path);
52}52}
5353
54/// Like `std.process.getCwdAlloc`, but also resolves the path with `Dir.path.resolve`. This54/// Like `std.process.currentPathAlloc`, but also resolves the path with `Dir.path.resolve`. This
55/// means the path has no repeated separators, no "." or ".." components, and no trailing separator.55/// means the path has no repeated separators, no "." or ".." components, and no trailing separator.
56/// On WASI, "" is returned instead of ".".56/// On WASI, "" is returned instead of ".".
57pub fn getResolvedCwd(gpa: Allocator) error{57pub fn getResolvedCwd(io: Io, gpa: Allocator) error{
58 OutOfMemory,58 OutOfMemory,
59 CurrentWorkingDirectoryUnlinked,59 CurrentDirUnlinked,
60 Unexpected,60 Unexpected,
61}![]u8 {61}![]u8 {
62 if (builtin.target.os.tag == .wasi) {62 if (builtin.target.os.tag == .wasi) {
63 if (std.debug.runtime_safety) {63 if (std.debug.runtime_safety) {
64 const cwd = try std.process.getCwdAlloc(gpa);64 const cwd = try std.process.currentPathAlloc(io, gpa);
65 defer gpa.free(cwd);65 defer gpa.free(cwd);
66 assert(mem.eql(u8, cwd, "."));66 assert(mem.eql(u8, cwd, "."));
67 }67 }
68 return "";68 return "";
69 }69 }
70 const cwd = try std.process.getCwdAlloc(gpa);70 const cwd = try std.process.currentPathAlloc(io, gpa);
71 defer gpa.free(cwd);71 defer gpa.free(cwd);
72 const resolved = try Dir.path.resolve(gpa, &.{cwd});72 const resolved = try Dir.path.resolve(gpa, &.{cwd});
73 assert(Dir.path.isAbsolute(resolved));73 assert(Dir.path.isAbsolute(resolved));
src/main.zig+5-5
...@@ -4775,7 +4775,7 @@ fn cmdInit(gpa: Allocator, arena: Allocator, io: Io, args: []const []const u8) !...@@ -4775,7 +4775,7 @@ fn cmdInit(gpa: Allocator, arena: Allocator, io: Io, args: []const []const u8) !
4775 }4775 }
4776 }4776 }
47774777
4778 const cwd_path = try introspect.getResolvedCwd(arena);4778 const cwd_path = try introspect.getResolvedCwd(io, arena);
4779 const cwd_basename = fs.path.basename(cwd_path);4779 const cwd_basename = fs.path.basename(cwd_path);
4780 const sanitized_root_name = try sanitizeExampleName(arena, cwd_basename);4780 const sanitized_root_name = try sanitizeExampleName(arena, cwd_basename);
47814781
...@@ -5141,7 +5141,7 @@ fn cmdBuild(gpa: Allocator, arena: Allocator, io: Io, args: []const []const u8,...@@ -5141,7 +5141,7 @@ fn cmdBuild(gpa: Allocator, arena: Allocator, io: Io, args: []const []const u8,
51415141
5142 process.raiseFileDescriptorLimit();5142 process.raiseFileDescriptorLimit();
51435143
5144 const cwd_path = try introspect.getResolvedCwd(arena);5144 const cwd_path = try introspect.getResolvedCwd(io, arena);
5145 const build_root = try findBuildRoot(arena, io, .{5145 const build_root = try findBuildRoot(arena, io, .{
5146 .cwd_path = cwd_path,5146 .cwd_path = cwd_path,
5147 .build_file = build_file,5147 .build_file = build_file,
...@@ -7077,7 +7077,7 @@ fn cmdFetch(...@@ -7077,7 +7077,7 @@ fn cmdFetch(
7077 },7077 },
7078 };7078 };
70797079
7080 const cwd_path = try introspect.getResolvedCwd(arena);7080 const cwd_path = try introspect.getResolvedCwd(io, arena);
70817081
7082 var build_root = try findBuildRoot(arena, io, .{7082 var build_root = try findBuildRoot(arena, io, .{
7083 .cwd_path = cwd_path,7083 .cwd_path = cwd_path,
...@@ -7288,7 +7288,7 @@ const FindBuildRootOptions = struct {...@@ -7288,7 +7288,7 @@ const FindBuildRootOptions = struct {
7288};7288};
72897289
7290fn findBuildRoot(arena: Allocator, io: Io, options: FindBuildRootOptions) !BuildRoot {7290fn findBuildRoot(arena: Allocator, io: Io, options: FindBuildRootOptions) !BuildRoot {
7291 const cwd_path = options.cwd_path orelse try introspect.getResolvedCwd(arena);7291 const cwd_path = options.cwd_path orelse try introspect.getResolvedCwd(io, arena);
7292 const build_zig_basename = if (options.build_file) |bf|7292 const build_zig_basename = if (options.build_file) |bf|
7293 fs.path.basename(bf)7293 fs.path.basename(bf)
7294 else7294 else
...@@ -7490,7 +7490,7 @@ fn writeSimpleTemplateFile(io: Io, file_name: []const u8, comptime fmt: []const...@@ -7490,7 +7490,7 @@ fn writeSimpleTemplateFile(io: Io, file_name: []const u8, comptime fmt: []const
7490}7490}
74917491
7492fn findTemplates(gpa: Allocator, arena: Allocator, io: Io) Templates {7492fn findTemplates(gpa: Allocator, arena: Allocator, io: Io) Templates {
7493 const cwd_path = introspect.getResolvedCwd(arena) catch |err| {7493 const cwd_path = introspect.getResolvedCwd(io, arena) catch |err| {
7494 fatal("unable to get cwd: {t}", .{err});7494 fatal("unable to get cwd: {t}", .{err});
7495 };7495 };
7496 const self_exe_path = process.executablePathAlloc(io, arena) catch |err| {7496 const self_exe_path = process.executablePathAlloc(io, arena) catch |err| {
test/standalone/child_process/main.zig+8-8
...@@ -9,7 +9,14 @@ pub fn main(init: std.process.Init.Minimal) !void {...@@ -9,7 +9,14 @@ pub fn main(init: std.process.Init.Minimal) !void {
9 };9 };
10 const gpa = gpa_state.allocator();10 const gpa = gpa_state.allocator();
1111
12 const process_cwd_path = try std.process.getCwdAlloc(gpa);12 var threaded: Io.Threaded = .init(gpa, .{
13 .argv0 = .init(init.args),
14 .environ = init.environ,
15 });
16 defer threaded.deinit();
17 const io = threaded.io();
18
19 const process_cwd_path = try std.process.currentPathAlloc(io, gpa);
13 defer gpa.free(process_cwd_path);20 defer gpa.free(process_cwd_path);
1421
15 var environ_map = try init.environ.createMap(gpa);22 var environ_map = try init.environ.createMap(gpa);
...@@ -27,13 +34,6 @@ pub fn main(init: std.process.Init.Minimal) !void {...@@ -27,13 +34,6 @@ pub fn main(init: std.process.Init.Minimal) !void {
27 };34 };
28 defer if (needs_free) gpa.free(child_path);35 defer if (needs_free) gpa.free(child_path);
2936
30 var threaded: Io.Threaded = .init(gpa, .{
31 .argv0 = .init(init.args),
32 .environ = init.environ,
33 });
34 defer threaded.deinit();
35 const io = threaded.io();
36
37 var child = try std.process.spawn(io, .{37 var child = try std.process.spawn(io, .{
38 .argv = &.{ child_path, "hello arg" },38 .argv = &.{ child_path, "hello arg" },
39 .stdin = .pipe,39 .stdin = .pipe,
test/standalone/posix/cwd.zig+14-13
...@@ -13,43 +13,44 @@ pub fn main(init: std.process.Init) !void {...@@ -13,43 +13,44 @@ pub fn main(init: std.process.Init) !void {
13 .windows => return, // POSIX is not implemented by Windows13 .windows => return, // POSIX is not implemented by Windows
14 else => {},14 else => {},
15 }15 }
16 const io = init.io;
16 const args = try init.minimal.args.toSlice(init.arena.allocator());17 const args = try init.minimal.args.toSlice(init.arena.allocator());
17 const tmp_dir_path = args[1];18 const tmp_dir_path = args[1];
1819
19 var tmp_dir = try Io.Dir.cwd().openDir(init.io, tmp_dir_path, .{});20 var tmp_dir = try Io.Dir.cwd().openDir(init.io, tmp_dir_path, .{});
20 defer tmp_dir.close(init.io);21 defer tmp_dir.close(init.io);
2122
22 try test_chdir_self();23 try test_chdir_self(io);
23 try test_chdir_absolute();24 try test_chdir_absolute(io);
24 try test_chdir_relative(init.gpa, init.io, tmp_dir);25 try test_chdir_relative(init.gpa, io, tmp_dir);
25}26}
2627
27// get current working directory and expect it to match given path28// get current working directory and expect it to match given path
28fn expect_cwd(expected_cwd: []const u8) !void {29fn expect_cwd(io: Io, expected_cwd: []const u8) !void {
29 var cwd_buf: [path_max]u8 = undefined;30 var cwd_buf: [path_max]u8 = undefined;
30 const actual_cwd = try std.posix.getcwd(cwd_buf[0..]);31 const actual_cwd = cwd_buf[0..try std.process.currentPath(io, &cwd_buf)];
31 try std.testing.expectEqualStrings(actual_cwd, expected_cwd);32 try std.testing.expectEqualStrings(actual_cwd, expected_cwd);
32}33}
3334
34fn test_chdir_self() !void {35fn test_chdir_self(io: Io) !void {
35 var old_cwd_buf: [path_max]u8 = undefined;36 var old_cwd_buf: [path_max]u8 = undefined;
36 const old_cwd = try std.posix.getcwd(old_cwd_buf[0..]);37 const old_cwd = old_cwd_buf[0..try std.process.currentPath(io, &old_cwd_buf)];
3738
38 // Try changing to the current directory39 // Try changing to the current directory
39 try std.Io.Threaded.chdir(old_cwd);40 try std.Io.Threaded.chdir(old_cwd);
40 try expect_cwd(old_cwd);41 try expect_cwd(io, old_cwd);
41}42}
4243
43fn test_chdir_absolute() !void {44fn test_chdir_absolute(io: Io) !void {
44 var old_cwd_buf: [path_max]u8 = undefined;45 var old_cwd_buf: [path_max]u8 = undefined;
45 const old_cwd = try std.posix.getcwd(old_cwd_buf[0..]);46 const old_cwd = old_cwd_buf[0..try std.process.currentPath(io, &old_cwd_buf)];
4647
47 const parent = std.fs.path.dirname(old_cwd) orelse unreachable; // old_cwd should be absolute48 const parent = std.fs.path.dirname(old_cwd) orelse unreachable; // old_cwd should be absolute
4849
49 // Try changing to the parent via a full path50 // Try changing to the parent via a full path
50 try std.Io.Threaded.chdir(parent);51 try std.Io.Threaded.chdir(parent);
5152
52 try expect_cwd(parent);53 try expect_cwd(io, parent);
53}54}
5455
55fn test_chdir_relative(gpa: Allocator, io: Io, tmp_dir: Io.Dir) !void {56fn test_chdir_relative(gpa: Allocator, io: Io, tmp_dir: Io.Dir) !void {
...@@ -61,7 +62,7 @@ fn test_chdir_relative(gpa: Allocator, io: Io, tmp_dir: Io.Dir) !void {...@@ -61,7 +62,7 @@ fn test_chdir_relative(gpa: Allocator, io: Io, tmp_dir: Io.Dir) !void {
6162
62 // Capture base working directory path, to build expected full path63 // Capture base working directory path, to build expected full path
63 var base_cwd_buf: [path_max]u8 = undefined;64 var base_cwd_buf: [path_max]u8 = undefined;
64 const base_cwd = try std.posix.getcwd(base_cwd_buf[0..]);65 const base_cwd = base_cwd_buf[0..try std.process.currentPath(io, &base_cwd_buf)];
6566
66 const expected_path = try std.fs.path.resolve(gpa, &.{ base_cwd, subdir_path });67 const expected_path = try std.fs.path.resolve(gpa, &.{ base_cwd, subdir_path });
67 defer gpa.free(expected_path);68 defer gpa.free(expected_path);
...@@ -70,7 +71,7 @@ fn test_chdir_relative(gpa: Allocator, io: Io, tmp_dir: Io.Dir) !void {...@@ -70,7 +71,7 @@ fn test_chdir_relative(gpa: Allocator, io: Io, tmp_dir: Io.Dir) !void {
70 try std.Io.Threaded.chdir(subdir_path);71 try std.Io.Threaded.chdir(subdir_path);
7172
72 var new_cwd_buf: [path_max]u8 = undefined;73 var new_cwd_buf: [path_max]u8 = undefined;
73 const new_cwd = try std.posix.getcwd(new_cwd_buf[0..]);74 const new_cwd = new_cwd_buf[0..try std.process.currentPath(io, &new_cwd_buf)];
7475
75 // On Windows, fs.path.resolve returns an uppercase drive letter, but the drive letter returned by getcwd may be lowercase76 // On Windows, fs.path.resolve returns an uppercase drive letter, but the drive letter returned by getcwd may be lowercase
76 const resolved_cwd = try std.fs.path.resolve(gpa, &.{new_cwd});77 const resolved_cwd = try std.fs.path.resolve(gpa, &.{new_cwd});
test/standalone/self_exe_symlink/create-symlink.zig+1-1
...@@ -9,7 +9,7 @@ pub fn main(init: std.process.Init) !void {...@@ -9,7 +9,7 @@ pub fn main(init: std.process.Init) !void {
9 const exe_path = it.next() orelse unreachable;9 const exe_path = it.next() orelse unreachable;
10 const symlink_path = it.next() orelse unreachable;10 const symlink_path = it.next() orelse unreachable;
1111
12 const cwd = try std.process.getCwdAlloc(init.arena.allocator());12 const cwd = try std.process.currentPathAlloc(io, init.arena.allocator());
1313
14 // If `exe_path` is relative to our cwd, we need to convert it to be relative to the dirname of `symlink_path`.14 // If `exe_path` is relative to our cwd, we need to convert it to be relative to the dirname of `symlink_path`.
15 const exe_rel_path = try std.fs.path.relative(gpa, cwd, init.environ_map, std.fs.path.dirname(symlink_path) orelse ".", exe_path);15 const exe_rel_path = try std.fs.path.relative(gpa, cwd, init.environ_map, std.fs.path.dirname(symlink_path) orelse ".", exe_path);
test/standalone/windows_paths/relative.zig+1-1
...@@ -4,7 +4,7 @@ pub fn main(init: std.process.Init) !void {...@@ -4,7 +4,7 @@ pub fn main(init: std.process.Init) !void {
4 const arena = init.arena.allocator();4 const arena = init.arena.allocator();
5 const args = try init.minimal.args.toSlice(arena);5 const args = try init.minimal.args.toSlice(arena);
6 const io = init.io;6 const io = init.io;
7 const cwd_path = try std.process.getCwdAlloc(arena);7 const cwd_path = try std.process.currentPathAlloc(io, arena);
88
9 if (args.len < 3) return error.MissingArgs;9 if (args.len < 3) return error.MissingArgs;
1010
test/standalone/windows_paths/test.zig+1-1
...@@ -10,7 +10,7 @@ pub fn main(init: std.process.Init) !void {...@@ -10,7 +10,7 @@ pub fn main(init: std.process.Init) !void {
1010
11 const exe_path = args[1];11 const exe_path = args[1];
1212
13 const cwd_path = try std.process.getCwdAlloc(arena);13 const cwd_path = try std.process.currentPathAlloc(io, arena);
14 const parsed_cwd_path = std.fs.path.parsePathWindows(u8, cwd_path);14 const parsed_cwd_path = std.fs.path.parsePathWindows(u8, cwd_path);
1515
16 if (parsed_cwd_path.kind == .drive_absolute and !std.ascii.isAlphabetic(cwd_path[0])) {16 if (parsed_cwd_path.kind == .drive_absolute and !std.ascii.isAlphabetic(cwd_path[0])) {
test/standalone/windows_spawn/main.zig+1-1
...@@ -8,7 +8,7 @@ const utf16Literal = std.unicode.utf8ToUtf16LeStringLiteral;...@@ -8,7 +8,7 @@ const utf16Literal = std.unicode.utf8ToUtf16LeStringLiteral;
8pub fn main(init: std.process.Init) !void {8pub fn main(init: std.process.Init) !void {
9 const gpa = init.gpa;9 const gpa = init.gpa;
10 const io = init.io;10 const io = init.io;
11 const process_cwd_path = try std.process.getCwdAlloc(init.arena.allocator());11 const process_cwd_path = try std.process.currentPathAlloc(io, init.arena.allocator());
12 var initial_process_cwd = try Io.Dir.cwd().openDir(io, ".", .{});12 var initial_process_cwd = try Io.Dir.cwd().openDir(io, ".", .{});
13 defer initial_process_cwd.close(io);13 defer initial_process_cwd.close(io);
1414
tools/doctest.zig+1-1
...@@ -33,7 +33,7 @@ pub fn main(init: std.process.Init) !void {...@@ -33,7 +33,7 @@ pub fn main(init: std.process.Init) !void {
33 const arena = init.arena.allocator();33 const arena = init.arena.allocator();
34 const io = init.io;34 const io = init.io;
35 const environ_map = init.environ_map;35 const environ_map = init.environ_map;
36 const cwd_path = try std.process.getCwdAlloc(arena);36 const cwd_path = try std.process.currentPathAlloc(io, arena);
3737
38 try environ_map.put("CLICOLOR_FORCE", "1");38 try environ_map.put("CLICOLOR_FORCE", "1");
3939
tools/incr-check.zig+1-1
...@@ -32,7 +32,7 @@ pub fn main(init: std.process.Init) !void {...@@ -32,7 +32,7 @@ pub fn main(init: std.process.Init) !void {
32 const arena = init.arena.allocator();32 const arena = init.arena.allocator();
33 const io = init.io;33 const io = init.io;
34 const environ_map = init.environ_map;34 const environ_map = init.environ_map;
35 const cwd_path = try std.process.getCwdAlloc(arena);35 const cwd_path = try std.process.currentPathAlloc(io, arena);
3636
37 var opt_zig_exe: ?[]const u8 = null;37 var opt_zig_exe: ?[]const u8 = null;
38 var opt_input_file_name: ?[]const u8 = null;38 var opt_input_file_name: ?[]const u8 = null;
tools/process_headers.zig+1-1
...@@ -145,7 +145,7 @@ pub fn main(init: std.process.Init) !void {...@@ -145,7 +145,7 @@ pub fn main(init: std.process.Init) !void {
145 const arena = init.arena.allocator();145 const arena = init.arena.allocator();
146 const io = init.io;146 const io = init.io;
147 const args = try init.minimal.args.toSlice(arena);147 const args = try init.minimal.args.toSlice(arena);
148 const cwd_path = try std.process.getCwdAlloc(arena);148 const cwd_path = try std.process.currentPathAlloc(io, arena);
149 const environ_map = init.environ_map;149 const environ_map = init.environ_map;
150150
151 var search_paths = std.array_list.Managed([]const u8).init(arena);151 var search_paths = std.array_list.Managed([]const u8).init(arena);
tools/update-linux-headers.zig+1-1
...@@ -146,7 +146,7 @@ pub fn main(init: std.process.Init) !void {...@@ -146,7 +146,7 @@ pub fn main(init: std.process.Init) !void {
146 const io = init.io;146 const io = init.io;
147 const args = try init.minimal.args.toSlice(arena);147 const args = try init.minimal.args.toSlice(arena);
148 const environ_map = init.environ_map;148 const environ_map = init.environ_map;
149 const cwd = try std.process.getCwdAlloc(arena);149 const cwd = try std.process.currentPathAlloc(io, arena);
150150
151 var search_paths = std.array_list.Managed([]const u8).init(arena);151 var search_paths = std.array_list.Managed([]const u8).init(arena);
152 var opt_out_dir: ?[]const u8 = null;152 var opt_out_dir: ?[]const u8 = null;