From 80b1e82b1b8706b7654560013751dd4a6fc8ae2a Mon Sep 17 00:00:00 2001 From: Lee Cannon Date: Fri, 6 Nov 2020 23:57:54 +0000 Subject: [PATCH] Implement chdir and chdirZ for Windows --- lib/std/os.zig | 11 +++++++---- lib/std/os/windows.zig | 27 +++++++++++++++++++++++++++ lib/std/os/windows/kernel32.zig | 1 + 3 files changed, 35 insertions(+), 4 deletions(-) diff --git a/lib/std/os.zig b/lib/std/os.zig index 91b88304f..edb53ec6a 100644 --- a/lib/std/os.zig +++ b/lib/std/os.zig @@ -2305,8 +2305,12 @@ pub fn chdir(dir_path: []const u8) ChangeCurDirError!void { if (builtin.os.tag == .wasi) { @compileError("chdir is not supported in WASI"); } else if (builtin.os.tag == .windows) { - const dir_path_w = try windows.sliceToPrefixedFileW(dir_path); - @compileError("TODO implement chdir for Windows"); + windows.SetCurrentDirectory(dir_path) catch |err| switch (err) { + error.PathNotFound, error.InvalidName, error.InvalidUtf8 => return error.FileNotFound, + error.NameTooLong => return error.NameTooLong, + error.NotDir => return error.NotDir, + error.Unexpected => return error.Unexpected, + }; } else { const dir_path_c = try toPosixPath(dir_path); return chdirZ(&dir_path_c); @@ -2318,8 +2322,7 @@ pub const chdirC = @compileError("deprecated: renamed to chdirZ"); /// Same as `chdir` except the parameter is null-terminated. pub fn chdirZ(dir_path: [*:0]const u8) ChangeCurDirError!void { if (builtin.os.tag == .windows) { - const dir_path_w = try windows.cStrToPrefixedFileW(dir_path); - @compileError("TODO implement chdir for Windows"); + return chdir(mem.spanZ(dir_path)); } switch (errno(system.chdir(dir_path))) { 0 => return, diff --git a/lib/std/os/windows.zig b/lib/std/os/windows.zig index e7038d0a2..6bdc858fc 100644 --- a/lib/std/os/windows.zig +++ b/lib/std/os/windows.zig @@ -555,6 +555,33 @@ pub fn WriteFile( } } +pub const SetCurrentDirectoryError = error{ + PathNotFound, + InvalidName, + InvalidUtf8, + NameTooLong, + NotDir, + Unexpected, +}; + +pub fn SetCurrentDirectory(path_name: []const u8) SetCurrentDirectoryError!void { + // PATH_MAX_WIDE - 1 as we need to ensure there is space for the null terminator + if (path_name.len >= PATH_MAX_WIDE - 1) return error.NameTooLong; + + var utf16le_buf: [PATH_MAX_WIDE]u16 = undefined; + const end = try std.unicode.utf8ToUtf16Le(utf16le_buf[0..], path_name); + utf16le_buf[end] = 0; + + if (kernel32.SetCurrentDirectoryW(@ptrCast([*:0]const u16, &utf16le_buf)) == 0) { + switch (kernel32.GetLastError()) { + .PATH_NOT_FOUND, .FILE_NOT_FOUND => return error.PathNotFound, + .DIRECTORY => return error.NotDir, + .INVALID_NAME => return error.InvalidName, + else => |err| return unexpectedError(err), + } + } +} + pub const GetCurrentDirectoryError = error{ NameTooLong, Unexpected, diff --git a/lib/std/os/windows/kernel32.zig b/lib/std/os/windows/kernel32.zig index 5d0b5bb22..9a79f0e72 100644 --- a/lib/std/os/windows/kernel32.zig +++ b/lib/std/os/windows/kernel32.zig @@ -93,6 +93,7 @@ pub extern "kernel32" fn FillConsoleOutputCharacterA(hConsoleOutput: HANDLE, cCh pub extern "kernel32" fn FillConsoleOutputAttribute(hConsoleOutput: HANDLE, wAttribute: WORD, nLength: DWORD, dwWriteCoord: COORD, lpNumberOfAttrsWritten: LPDWORD) callconv(.Stdcall) BOOL; pub extern "kernel32" fn SetConsoleCursorPosition(hConsoleOutput: HANDLE, dwCursorPosition: COORD) callconv(.Stdcall) BOOL; +pub extern "kernel32" fn SetCurrentDirectoryW(lpPathName: [*:0]const u16) callconv(.Stdcall) BOOL; pub extern "kernel32" fn GetCurrentDirectoryW(nBufferLength: DWORD, lpBuffer: ?[*]WCHAR) callconv(.Stdcall) DWORD; pub extern "kernel32" fn GetCurrentThread() callconv(.Stdcall) HANDLE;