Commit 80b1e82b1b

Lee Cannon <leecannon@leecannon.xyz>
2020-11-07 00:57:54
Implement chdir and chdirZ for Windows
1 parent a647008
Changed files (3)
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;
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,
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,