Commit 92f7474359
Changed files (8)
std/os/windows/index.zig
@@ -6,8 +6,6 @@ pub use @import("kernel32.zig");
pub use @import("ntdll.zig");
pub use @import("ole32.zig");
pub use @import("shell32.zig");
-pub use @import("shlwapi.zig");
-pub use @import("user32.zig");
test "import" {
_ = @import("util.zig");
@@ -174,11 +172,11 @@ pub const PROCESS_INFORMATION = extern struct {
dwThreadId: DWORD,
};
-pub const STARTUPINFOA = extern struct {
+pub const STARTUPINFOW = extern struct {
cb: DWORD,
- lpReserved: ?LPSTR,
- lpDesktop: ?LPSTR,
- lpTitle: ?LPSTR,
+ lpReserved: ?LPWSTR,
+ lpDesktop: ?LPWSTR,
+ lpTitle: ?LPWSTR,
dwX: DWORD,
dwY: DWORD,
dwXSize: DWORD,
@@ -238,7 +236,7 @@ pub const HEAP_NO_SERIALIZE = 0x00000001;
pub const PTHREAD_START_ROUTINE = extern fn (LPVOID) DWORD;
pub const LPTHREAD_START_ROUTINE = PTHREAD_START_ROUTINE;
-pub const WIN32_FIND_DATAA = extern struct {
+pub const WIN32_FIND_DATAW = extern struct {
dwFileAttributes: DWORD,
ftCreationTime: FILETIME,
ftLastAccessTime: FILETIME,
@@ -247,8 +245,8 @@ pub const WIN32_FIND_DATAA = extern struct {
nFileSizeLow: DWORD,
dwReserved0: DWORD,
dwReserved1: DWORD,
- cFileName: [260]CHAR,
- cAlternateFileName: [14]CHAR,
+ cFileName: [260]u16,
+ cAlternateFileName: [14]u16,
};
pub const FILETIME = extern struct {
@@ -377,3 +375,5 @@ pub const COORD = extern struct {
X: SHORT,
Y: SHORT,
};
+
+pub const CREATE_UNICODE_ENVIRONMENT = 1024;
std/os/windows/kernel32.zig
@@ -4,19 +4,8 @@ pub extern "kernel32" stdcallcc fn CancelIoEx(hFile: HANDLE, lpOverlapped: LPOVE
pub extern "kernel32" stdcallcc fn CloseHandle(hObject: HANDLE) BOOL;
-pub extern "kernel32" stdcallcc fn CreateDirectoryA(lpPathName: [*]const u8, lpSecurityAttributes: ?*SECURITY_ATTRIBUTES) BOOL;
pub extern "kernel32" stdcallcc fn CreateDirectoryW(lpPathName: [*]const u16, lpSecurityAttributes: ?*SECURITY_ATTRIBUTES) BOOL;
-pub extern "kernel32" stdcallcc fn CreateFileA(
- lpFileName: [*]const u8, // TODO null terminated pointer type
- dwDesiredAccess: DWORD,
- dwShareMode: DWORD,
- lpSecurityAttributes: ?LPSECURITY_ATTRIBUTES,
- dwCreationDisposition: DWORD,
- dwFlagsAndAttributes: DWORD,
- hTemplateFile: ?HANDLE,
-) HANDLE;
-
pub extern "kernel32" stdcallcc fn CreateFileW(
lpFileName: [*]const u16, // TODO null terminated pointer type
dwDesiredAccess: DWORD,
@@ -34,37 +23,32 @@ pub extern "kernel32" stdcallcc fn CreatePipe(
nSize: DWORD,
) BOOL;
-pub extern "kernel32" stdcallcc fn CreateProcessA(
- lpApplicationName: ?LPCSTR,
- lpCommandLine: LPSTR,
+pub extern "kernel32" stdcallcc fn CreateProcessW(
+ lpApplicationName: ?LPWSTR,
+ lpCommandLine: LPWSTR,
lpProcessAttributes: ?*SECURITY_ATTRIBUTES,
lpThreadAttributes: ?*SECURITY_ATTRIBUTES,
bInheritHandles: BOOL,
dwCreationFlags: DWORD,
lpEnvironment: ?*c_void,
- lpCurrentDirectory: ?LPCSTR,
- lpStartupInfo: *STARTUPINFOA,
+ lpCurrentDirectory: ?LPWSTR,
+ lpStartupInfo: *STARTUPINFOW,
lpProcessInformation: *PROCESS_INFORMATION,
) BOOL;
-pub extern "kernel32" stdcallcc fn CreateSymbolicLinkA(
- lpSymlinkFileName: LPCSTR,
- lpTargetFileName: LPCSTR,
- dwFlags: DWORD,
-) BOOLEAN;
+pub extern "kernel32" stdcallcc fn CreateSymbolicLinkW(lpSymlinkFileName: [*]const u16, lpTargetFileName: [*]const u16, dwFlags: DWORD) BOOLEAN;
pub extern "kernel32" stdcallcc fn CreateIoCompletionPort(FileHandle: HANDLE, ExistingCompletionPort: ?HANDLE, CompletionKey: ULONG_PTR, NumberOfConcurrentThreads: DWORD) ?HANDLE;
pub extern "kernel32" stdcallcc fn CreateThread(lpThreadAttributes: ?LPSECURITY_ATTRIBUTES, dwStackSize: SIZE_T, lpStartAddress: LPTHREAD_START_ROUTINE, lpParameter: ?LPVOID, dwCreationFlags: DWORD, lpThreadId: ?LPDWORD) ?HANDLE;
-pub extern "kernel32" stdcallcc fn DeleteFileA(lpFileName: [*]const u8) BOOL;
pub extern "kernel32" stdcallcc fn DeleteFileW(lpFileName: [*]const u16) BOOL;
pub extern "kernel32" stdcallcc fn ExitProcess(exit_code: UINT) noreturn;
-pub extern "kernel32" stdcallcc fn FindFirstFileA(lpFileName: LPCSTR, lpFindFileData: *WIN32_FIND_DATAA) HANDLE;
+pub extern "kernel32" stdcallcc fn FindFirstFileW(lpFileName: [*]const u16, lpFindFileData: *WIN32_FIND_DATAW) HANDLE;
pub extern "kernel32" stdcallcc fn FindClose(hFindFile: HANDLE) BOOL;
-pub extern "kernel32" stdcallcc fn FindNextFileA(hFindFile: HANDLE, lpFindFileData: *WIN32_FIND_DATAA) BOOL;
+pub extern "kernel32" stdcallcc fn FindNextFileW(hFindFile: HANDLE, lpFindFileData: *WIN32_FIND_DATAW) BOOL;
pub extern "kernel32" stdcallcc fn FreeEnvironmentStringsA(penv: [*]u8) BOOL;
@@ -74,7 +58,6 @@ pub extern "kernel32" stdcallcc fn GetConsoleMode(in_hConsoleHandle: HANDLE, out
pub extern "kernel32" stdcallcc fn GetConsoleScreenBufferInfo(hConsoleOutput: HANDLE, lpConsoleScreenBufferInfo: *CONSOLE_SCREEN_BUFFER_INFO) BOOL;
-pub extern "kernel32" stdcallcc fn GetCurrentDirectoryA(nBufferLength: DWORD, lpBuffer: ?[*]CHAR) DWORD;
pub extern "kernel32" stdcallcc fn GetCurrentDirectoryW(nBufferLength: DWORD, lpBuffer: ?[*]WCHAR) DWORD;
pub extern "kernel32" stdcallcc fn GetCurrentThread() HANDLE;
@@ -88,10 +71,8 @@ pub extern "kernel32" stdcallcc fn GetExitCodeProcess(hProcess: HANDLE, lpExitCo
pub extern "kernel32" stdcallcc fn GetFileSizeEx(hFile: HANDLE, lpFileSize: *LARGE_INTEGER) BOOL;
-pub extern "kernel32" stdcallcc fn GetFileAttributesA(lpFileName: [*]const CHAR) DWORD;
pub extern "kernel32" stdcallcc fn GetFileAttributesW(lpFileName: [*]const WCHAR) DWORD;
-pub extern "kernel32" stdcallcc fn GetModuleFileNameA(hModule: ?HMODULE, lpFilename: [*]u8, nSize: DWORD) DWORD;
pub extern "kernel32" stdcallcc fn GetModuleFileNameW(hModule: ?HMODULE, lpFilename: [*]u16, nSize: DWORD) DWORD;
pub extern "kernel32" stdcallcc fn GetModuleHandleW(lpModuleName: ?[*]const WCHAR) HMODULE;
@@ -105,13 +86,6 @@ pub extern "kernel32" stdcallcc fn GetFileInformationByHandleEx(
in_dwBufferSize: DWORD,
) BOOL;
-pub extern "kernel32" stdcallcc fn GetFinalPathNameByHandleA(
- hFile: HANDLE,
- lpszFilePath: LPSTR,
- cchFilePath: DWORD,
- dwFlags: DWORD,
-) DWORD;
-
pub extern "kernel32" stdcallcc fn GetFinalPathNameByHandleW(
hFile: HANDLE,
lpszFilePath: [*]u16,
@@ -142,12 +116,6 @@ pub extern "kernel32" stdcallcc fn HeapFree(hHeap: HANDLE, dwFlags: DWORD, lpMem
pub extern "kernel32" stdcallcc fn HeapValidate(hHeap: HANDLE, dwFlags: DWORD, lpMem: ?*const c_void) BOOL;
-pub extern "kernel32" stdcallcc fn MoveFileExA(
- lpExistingFileName: [*]const u8,
- lpNewFileName: [*]const u8,
- dwFlags: DWORD,
-) BOOL;
-
pub extern "kernel32" stdcallcc fn MoveFileExW(
lpExistingFileName: [*]const u16,
lpNewFileName: [*]const u16,
@@ -179,7 +147,7 @@ pub extern "kernel32" stdcallcc fn ReadFile(
in_out_lpOverlapped: ?*OVERLAPPED,
) BOOL;
-pub extern "kernel32" stdcallcc fn RemoveDirectoryA(lpPathName: LPCSTR) BOOL;
+pub extern "kernel32" stdcallcc fn RemoveDirectoryW(lpPathName: [*]const u16) BOOL;
pub extern "kernel32" stdcallcc fn SetConsoleTextAttribute(hConsoleOutput: HANDLE, wAttributes: WORD) BOOL;
@@ -208,8 +176,7 @@ pub extern "kernel32" stdcallcc fn WriteFile(
pub extern "kernel32" stdcallcc fn WriteFileEx(hFile: HANDLE, lpBuffer: [*]const u8, nNumberOfBytesToWrite: DWORD, lpOverlapped: LPOVERLAPPED, lpCompletionRoutine: LPOVERLAPPED_COMPLETION_ROUTINE) BOOL;
-//TODO: call unicode versions instead of relying on ANSI code page
-pub extern "kernel32" stdcallcc fn LoadLibraryA(lpLibFileName: LPCSTR) ?HMODULE;
+pub extern "kernel32" stdcallcc fn LoadLibraryW(lpLibFileName: [*]const u16) ?HMODULE;
pub extern "kernel32" stdcallcc fn FreeLibrary(hModule: HMODULE) BOOL;
std/os/windows/shlwapi.zig
@@ -1,4 +0,0 @@
-use @import("index.zig");
-
-pub extern "shlwapi" stdcallcc fn PathFileExistsA(pszPath: ?LPCTSTR) BOOL;
-
std/os/windows/user32.zig
@@ -1,4 +0,0 @@
-use @import("index.zig");
-
-pub extern "user32" stdcallcc fn MessageBoxA(hWnd: ?HANDLE, lpText: ?LPCTSTR, lpCaption: ?LPCTSTR, uType: UINT) c_int;
-
std/os/windows/util.zig
@@ -1,6 +1,7 @@
const std = @import("../../index.zig");
const builtin = @import("builtin");
const os = std.os;
+const unicode = std.unicode;
const windows = std.os.windows;
const assert = std.debug.assert;
const mem = std.mem;
@@ -156,41 +157,51 @@ pub fn windowsOpen(
}
/// Caller must free result.
-pub fn createWindowsEnvBlock(allocator: *mem.Allocator, env_map: *const BufMap) ![]u8 {
+pub fn createWindowsEnvBlock(allocator: *mem.Allocator, env_map: *const BufMap) ![]u16 {
// count bytes needed
- const bytes_needed = x: {
- var bytes_needed: usize = 1; // 1 for the final null byte
+ const max_chars_needed = x: {
+ var max_chars_needed: usize = 1; // 1 for the final null byte
var it = env_map.iterator();
while (it.next()) |pair| {
// +1 for '='
// +1 for null byte
- bytes_needed += pair.key.len + pair.value.len + 2;
+ max_chars_needed += pair.key.len + pair.value.len + 2;
}
- break :x bytes_needed;
+ break :x max_chars_needed;
};
- const result = try allocator.alloc(u8, bytes_needed);
+ const result = try allocator.alloc(u16, max_chars_needed);
errdefer allocator.free(result);
var it = env_map.iterator();
var i: usize = 0;
while (it.next()) |pair| {
- mem.copy(u8, result[i..], pair.key);
- i += pair.key.len;
+ i += try unicode.utf8ToUtf16Le(result[i..], pair.key);
result[i] = '=';
i += 1;
- mem.copy(u8, result[i..], pair.value);
- i += pair.value.len;
+ i += try unicode.utf8ToUtf16Le(result[i..], pair.value);
result[i] = 0;
i += 1;
}
result[i] = 0;
- return result;
+ i += 1;
+ return allocator.shrink(u16, result, i);
}
-pub fn windowsLoadDll(allocator: *mem.Allocator, dll_path: []const u8) !windows.HMODULE {
- const padded_buff = try cstr.addNullByte(allocator, dll_path);
- defer allocator.free(padded_buff);
- return windows.LoadLibraryA(padded_buff.ptr) orelse error.DllNotFound;
+pub fn windowsLoadDllW(dll_path_w: [*]const u16) !windows.HMODULE {
+ return windows.LoadLibraryW(dll_path_w) orelse {
+ const err = windows.GetLastError();
+ switch (err) {
+ windows.ERROR.FILE_NOT_FOUND => return error.FileNotFound,
+ windows.ERROR.PATH_NOT_FOUND => return error.FileNotFound,
+ windows.ERROR.MOD_NOT_FOUND => return error.FileNotFound,
+ else => return os.unexpectedErrorWindows(err),
+ }
+ };
+}
+
+pub fn windowsLoadDll(dll_path: []const u8) !windows.HMODULE {
+ const dll_path_w = try sliceToPrefixedFileW(dll_path);
+ return windowsLoadDllW(&dll_path_w);
}
pub fn windowsUnloadDll(hModule: windows.HMODULE) void {
@@ -200,27 +211,19 @@ pub fn windowsUnloadDll(hModule: windows.HMODULE) void {
test "InvalidDll" {
if (builtin.os != builtin.Os.windows) return error.SkipZigTest;
- const DllName = "asdf.dll";
- const allocator = std.debug.global_allocator;
- const handle = os.windowsLoadDll(allocator, DllName) catch |err| {
- assert(err == error.DllNotFound);
+ const handle = os.windowsLoadDll("asdf.dll") catch |err| {
+ assert(err == error.FileNotFound);
return;
};
+ @panic("Expected error from function");
}
pub fn windowsFindFirstFile(
- allocator: *mem.Allocator,
dir_path: []const u8,
- find_file_data: *windows.WIN32_FIND_DATAA,
+ find_file_data: *windows.WIN32_FIND_DATAW,
) !windows.HANDLE {
- const wild_and_null = []u8{ '\\', '*', 0 };
- const path_with_wild_and_null = try allocator.alloc(u8, dir_path.len + wild_and_null.len);
- defer allocator.free(path_with_wild_and_null);
-
- mem.copy(u8, path_with_wild_and_null, dir_path);
- mem.copy(u8, path_with_wild_and_null[dir_path.len..], wild_and_null);
-
- const handle = windows.FindFirstFileA(path_with_wild_and_null.ptr, find_file_data);
+ const dir_path_w = try sliceToPrefixedSuffixedFileW(dir_path, []u16{'\\', '*', 0});
+ const handle = windows.FindFirstFileW(&dir_path_w, find_file_data);
if (handle == windows.INVALID_HANDLE_VALUE) {
const err = windows.GetLastError();
@@ -235,8 +238,8 @@ pub fn windowsFindFirstFile(
}
/// Returns `true` if there was another file, `false` otherwise.
-pub fn windowsFindNextFile(handle: windows.HANDLE, find_file_data: *windows.WIN32_FIND_DATAA) !bool {
- if (windows.FindNextFileA(handle, find_file_data) == 0) {
+pub fn windowsFindNextFile(handle: windows.HANDLE, find_file_data: *windows.WIN32_FIND_DATAW) !bool {
+ if (windows.FindNextFileW(handle, find_file_data) == 0) {
const err = windows.GetLastError();
return switch (err) {
windows.ERROR.NO_MORE_FILES => false,
@@ -297,8 +300,12 @@ pub fn cStrToPrefixedFileW(s: [*]const u8) ![PATH_MAX_WIDE + 1]u16 {
}
pub fn sliceToPrefixedFileW(s: []const u8) ![PATH_MAX_WIDE + 1]u16 {
+ return sliceToPrefixedSuffixedFileW(s, []u16{0});
+}
+
+pub fn sliceToPrefixedSuffixedFileW(s: []const u8, comptime suffix: []const u16) ![PATH_MAX_WIDE + suffix.len]u16 {
// TODO well defined copy elision
- var result: [PATH_MAX_WIDE + 1]u16 = undefined;
+ var result: [PATH_MAX_WIDE + suffix.len]u16 = undefined;
// > File I/O functions in the Windows API convert "/" to "\" as part of
// > converting the name to an NT-style name, except when using the "\\?\"
@@ -306,11 +313,12 @@ pub fn sliceToPrefixedFileW(s: []const u8) ![PATH_MAX_WIDE + 1]u16 {
// from https://docs.microsoft.com/en-us/windows/desktop/FileIO/naming-a-file#maximum-path-length-limitation
// Because we want the larger maximum path length for absolute paths, we
// disallow forward slashes in zig std lib file functions on Windows.
- for (s) |byte|
+ for (s) |byte| {
switch (byte) {
- '/', '*', '?', '"', '<', '>', '|' => return error.BadPathName,
- else => {},
- };
+ '/', '*', '?', '"', '<', '>', '|' => return error.BadPathName,
+ else => {},
+ }
+ }
const start_index = if (mem.startsWith(u8, s, "\\\\") or !os.path.isAbsolute(s)) 0 else blk: {
const prefix = []u16{ '\\', '\\', '?', '\\' };
mem.copy(u16, result[0..], prefix);
@@ -318,7 +326,7 @@ pub fn sliceToPrefixedFileW(s: []const u8) ![PATH_MAX_WIDE + 1]u16 {
};
const end_index = start_index + try std.unicode.utf8ToUtf16Le(result[start_index..], s);
assert(end_index <= result.len);
- if (end_index == result.len) return error.NameTooLong;
- result[end_index] = 0;
+ if (end_index + suffix.len > result.len) return error.NameTooLong;
+ mem.copy(u16, result[end_index..], suffix);
return result;
}
std/os/child_process.zig
@@ -1,5 +1,6 @@
const std = @import("../index.zig");
const cstr = std.cstr;
+const unicode = std.unicode;
const io = std.io;
const os = std.os;
const posix = os.posix;
@@ -12,6 +13,7 @@ const Buffer = std.Buffer;
const builtin = @import("builtin");
const Os = builtin.Os;
const LinkedList = std.LinkedList;
+const windows_util = @import("windows/util.zig");
const is_windows = builtin.os == Os.windows;
@@ -520,8 +522,8 @@ pub const ChildProcess = struct {
const cmd_line = try windowsCreateCommandLine(self.allocator, self.argv);
defer self.allocator.free(cmd_line);
- var siStartInfo = windows.STARTUPINFOA{
- .cb = @sizeOf(windows.STARTUPINFOA),
+ var siStartInfo = windows.STARTUPINFOW{
+ .cb = @sizeOf(windows.STARTUPINFOW),
.hStdError = g_hChildStd_ERR_Wr,
.hStdOutput = g_hChildStd_OUT_Wr,
.hStdInput = g_hChildStd_IN_Rd,
@@ -545,7 +547,9 @@ pub const ChildProcess = struct {
const cwd_slice = if (self.cwd) |cwd| try cstr.addNullByte(self.allocator, cwd) else null;
defer if (cwd_slice) |cwd| self.allocator.free(cwd);
- const cwd_ptr = if (cwd_slice) |cwd| cwd.ptr else null;
+ const cwd_w = if (cwd_slice) |cwd| try unicode.utf8ToUtf16LeWithNull(self.allocator, cwd) else null;
+ defer if (cwd_w) |cwd| self.allocator.free(cwd);
+ const cwd_w_ptr = if (cwd_w) |cwd| cwd.ptr else null;
const maybe_envp_buf = if (self.env_map) |env_map| try os.createWindowsEnvBlock(self.allocator, env_map) else null;
defer if (maybe_envp_buf) |envp_buf| self.allocator.free(envp_buf);
@@ -564,7 +568,13 @@ pub const ChildProcess = struct {
};
defer self.allocator.free(app_name);
- windowsCreateProcess(app_name.ptr, cmd_line.ptr, envp_ptr, cwd_ptr, &siStartInfo, &piProcInfo) catch |no_path_err| {
+ const app_name_w = try unicode.utf8ToUtf16LeWithNull(self.allocator, app_name);
+ defer self.allocator.free(app_name_w);
+
+ const cmd_line_w = try unicode.utf8ToUtf16LeWithNull(self.allocator, cmd_line);
+ defer self.allocator.free(cmd_line_w);
+
+ windowsCreateProcess(app_name_w.ptr, cmd_line_w.ptr, envp_ptr, cwd_w_ptr, &siStartInfo, &piProcInfo) catch |no_path_err| {
if (no_path_err != error.FileNotFound) return no_path_err;
const PATH = try os.getEnvVarOwned(self.allocator, "PATH");
@@ -575,7 +585,10 @@ pub const ChildProcess = struct {
const joined_path = try os.path.join(self.allocator, search_path, app_name);
defer self.allocator.free(joined_path);
- if (windowsCreateProcess(joined_path.ptr, cmd_line.ptr, envp_ptr, cwd_ptr, &siStartInfo, &piProcInfo)) |_| {
+ const joined_path_w = try unicode.utf8ToUtf16LeWithNull(self.allocator, app_name);
+ defer self.allocator.free(joined_path_w);
+
+ if (windowsCreateProcess(joined_path_w.ptr, cmd_line_w.ptr, envp_ptr, cwd_w_ptr, &siStartInfo, &piProcInfo)) |_| {
break;
} else |err| if (err == error.FileNotFound) {
continue;
@@ -626,15 +639,36 @@ pub const ChildProcess = struct {
}
};
-fn windowsCreateProcess(app_name: [*]u8, cmd_line: [*]u8, envp_ptr: ?[*]u8, cwd_ptr: ?[*]u8, lpStartupInfo: *windows.STARTUPINFOA, lpProcessInformation: *windows.PROCESS_INFORMATION) !void {
- if (windows.CreateProcessA(app_name, cmd_line, null, null, windows.TRUE, 0, @ptrCast(?*c_void, envp_ptr), cwd_ptr, lpStartupInfo, lpProcessInformation) == 0) {
+fn windowsCreateProcess(app_name: [*]u16, cmd_line: [*]u16, envp_ptr: ?[*]u16, cwd_ptr: ?[*]u16, lpStartupInfo: *windows.STARTUPINFOW, lpProcessInformation: *windows.PROCESS_INFORMATION) !void {
+ // TODO the docs for environment pointer say:
+ // > A pointer to the environment block for the new process. If this parameter
+ // > is NULL, the new process uses the environment of the calling process.
+ // > ...
+ // > An environment block can contain either Unicode or ANSI characters. If
+ // > the environment block pointed to by lpEnvironment contains Unicode
+ // > characters, be sure that dwCreationFlags includes CREATE_UNICODE_ENVIRONMENT.
+ // > If this parameter is NULL and the environment block of the parent process
+ // > contains Unicode characters, you must also ensure that dwCreationFlags
+ // > includes CREATE_UNICODE_ENVIRONMENT.
+ // This seems to imply that we have to somehow know whether our process parent passed
+ // CREATE_UNICODE_ENVIRONMENT if we want to pass NULL for the environment parameter.
+ // Since we do not know this information that would imply that we must not pass NULL
+ // for the parameter.
+ // However this would imply that programs compiled with -DUNICODE could not pass
+ // environment variables to programs that were not, which seems unlikely.
+ // More investigation is needed.
+ if (windows.CreateProcessW(
+ app_name, cmd_line, null, null, windows.TRUE, windows.CREATE_UNICODE_ENVIRONMENT,
+ @ptrCast(?*c_void, envp_ptr), cwd_ptr, lpStartupInfo, lpProcessInformation,
+ ) == 0) {
const err = windows.GetLastError();
- return switch (err) {
- windows.ERROR.FILE_NOT_FOUND, windows.ERROR.PATH_NOT_FOUND => error.FileNotFound,
+ switch (err) {
+ windows.ERROR.FILE_NOT_FOUND => return error.FileNotFound,
+ windows.ERROR.PATH_NOT_FOUND => return error.FileNotFound,
windows.ERROR.INVALID_PARAMETER => unreachable,
- windows.ERROR.INVALID_NAME => error.InvalidName,
- else => os.unexpectedErrorWindows(err),
- };
+ windows.ERROR.INVALID_NAME => return error.InvalidName,
+ else => return os.unexpectedErrorWindows(err),
+ }
}
}
std/os/index.zig
@@ -819,37 +819,40 @@ test "os.getCwd" {
pub const SymLinkError = PosixSymLinkError || WindowsSymLinkError;
-pub fn symLink(allocator: *Allocator, existing_path: []const u8, new_path: []const u8) SymLinkError!void {
+/// TODO add a symLinkC variant
+pub fn symLink(existing_path: []const u8, new_path: []const u8) SymLinkError!void {
if (is_windows) {
- return symLinkWindows(allocator, existing_path, new_path);
+ return symLinkWindows(existing_path, new_path);
} else {
- return symLinkPosix(allocator, existing_path, new_path);
+ return symLinkPosix(existing_path, new_path);
}
}
pub const WindowsSymLinkError = error{
- OutOfMemory,
+ NameTooLong,
+ InvalidUtf8,
+ BadPathName,
/// See https://github.com/ziglang/zig/issues/1396
Unexpected,
};
-pub fn symLinkWindows(allocator: *Allocator, existing_path: []const u8, new_path: []const u8) WindowsSymLinkError!void {
- const existing_with_null = try cstr.addNullByte(allocator, existing_path);
- defer allocator.free(existing_with_null);
- const new_with_null = try cstr.addNullByte(allocator, new_path);
- defer allocator.free(new_with_null);
-
- if (windows.CreateSymbolicLinkA(existing_with_null.ptr, new_with_null.ptr, 0) == 0) {
+pub fn symLinkW(existing_path_w: [*]const u16, new_path_w: [*]const u16) WindowsSymLinkError!void {
+ if (windows.CreateSymbolicLinkW(existing_path_w, new_path_w, 0) == 0) {
const err = windows.GetLastError();
- return switch (err) {
- else => unexpectedErrorWindows(err),
- };
+ switch (err) {
+ else => return unexpectedErrorWindows(err),
+ }
}
}
+pub fn symLinkWindows(existing_path: []const u8, new_path: []const u8) WindowsSymLinkError!void {
+ const existing_path_w = try windows_util.sliceToPrefixedFileW(existing_path);
+ const new_path_w = try windows_util.sliceToPrefixedFileW(new_path);
+ return symLinkW(&existing_path_w, &new_path_w);
+}
+
pub const PosixSymLinkError = error{
- OutOfMemory,
AccessDenied,
DiskQuota,
PathAlreadyExists,
@@ -866,43 +869,40 @@ pub const PosixSymLinkError = error{
Unexpected,
};
-pub fn symLinkPosix(allocator: *Allocator, existing_path: []const u8, new_path: []const u8) PosixSymLinkError!void {
- const full_buf = try allocator.alloc(u8, existing_path.len + new_path.len + 2);
- defer allocator.free(full_buf);
-
- const existing_buf = full_buf;
- mem.copy(u8, existing_buf, existing_path);
- existing_buf[existing_path.len] = 0;
-
- const new_buf = full_buf[existing_path.len + 1 ..];
- mem.copy(u8, new_buf, new_path);
- new_buf[new_path.len] = 0;
-
- const err = posix.getErrno(posix.symlink(existing_buf.ptr, new_buf.ptr));
- if (err > 0) {
- return switch (err) {
- posix.EFAULT, posix.EINVAL => unreachable,
- posix.EACCES, posix.EPERM => error.AccessDenied,
- posix.EDQUOT => error.DiskQuota,
- posix.EEXIST => error.PathAlreadyExists,
- posix.EIO => error.FileSystem,
- posix.ELOOP => error.SymLinkLoop,
- posix.ENAMETOOLONG => error.NameTooLong,
- posix.ENOENT => error.FileNotFound,
- posix.ENOTDIR => error.NotDir,
- posix.ENOMEM => error.SystemResources,
- posix.ENOSPC => error.NoSpaceLeft,
- posix.EROFS => error.ReadOnlyFileSystem,
- else => unexpectedErrorPosix(err),
- };
+pub fn symLinkPosixC(existing_path: [*]const u8, new_path: [*]const u8) PosixSymLinkError!void {
+ const err = posix.getErrno(posix.symlink(existing_path, new_path));
+ switch (err) {
+ 0 => return,
+ posix.EFAULT => unreachable,
+ posix.EINVAL => unreachable,
+ posix.EACCES => return error.AccessDenied,
+ posix.EPERM => return error.AccessDenied,
+ posix.EDQUOT => return error.DiskQuota,
+ posix.EEXIST => return error.PathAlreadyExists,
+ posix.EIO => return error.FileSystem,
+ posix.ELOOP => return error.SymLinkLoop,
+ posix.ENAMETOOLONG => return error.NameTooLong,
+ posix.ENOENT => return error.FileNotFound,
+ posix.ENOTDIR => return error.NotDir,
+ posix.ENOMEM => return error.SystemResources,
+ posix.ENOSPC => return error.NoSpaceLeft,
+ posix.EROFS => return error.ReadOnlyFileSystem,
+ else => return unexpectedErrorPosix(err),
}
}
+pub fn symLinkPosix(existing_path: []const u8, new_path: []const u8) PosixSymLinkError!void {
+ const existing_path_c = try toPosixPath(existing_path);
+ const new_path_c = try toPosixPath(new_path);
+ return symLinkPosixC(&existing_path_c, &new_path_c);
+}
+
// here we replace the standard +/ with -_ so that it can be used in a file name
const b64_fs_encoder = base64.Base64Encoder.init("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_", base64.standard_pad_char);
+/// TODO remove the allocator requirement from this API
pub fn atomicSymLink(allocator: *Allocator, existing_path: []const u8, new_path: []const u8) !void {
- if (symLink(allocator, existing_path, new_path)) {
+ if (symLink(existing_path, new_path)) {
return;
} else |err| switch (err) {
error.PathAlreadyExists => {},
@@ -920,7 +920,7 @@ pub fn atomicSymLink(allocator: *Allocator, existing_path: []const u8, new_path:
try getRandomBytes(rand_buf[0..]);
b64_fs_encoder.encode(tmp_path[dirname.len + 1 ..], rand_buf);
- if (symLink(allocator, existing_path, tmp_path)) {
+ if (symLink(existing_path, tmp_path)) {
return rename(tmp_path, new_path);
} else |err| switch (err) {
error.PathAlreadyExists => continue,
@@ -1252,49 +1252,65 @@ pub const DeleteDirError = error{
NotDir,
DirNotEmpty,
ReadOnlyFileSystem,
- OutOfMemory,
+ InvalidUtf8,
+ BadPathName,
/// See https://github.com/ziglang/zig/issues/1396
Unexpected,
};
-/// Returns ::error.DirNotEmpty if the directory is not empty.
-/// To delete a directory recursively, see ::deleteTree
-pub fn deleteDir(allocator: *Allocator, dir_path: []const u8) DeleteDirError!void {
- const path_buf = try allocator.alloc(u8, dir_path.len + 1);
- defer allocator.free(path_buf);
+pub fn deleteDirC(dir_path: [*]const u8) DeleteDirError!void {
+ switch (builtin.os) {
+ Os.windows => {
+ const dir_path_w = try windows_util.cStrToPrefixedFileW(dir_path);
+ return deleteDirW(&dir_path_w);
+ },
+ Os.linux, Os.macosx, Os.ios => {
+ const err = posix.getErrno(posix.rmdir(dir_path));
+ switch (err) {
+ 0 => return,
+ posix.EACCES => return error.AccessDenied,
+ posix.EPERM => return error.AccessDenied,
+ posix.EBUSY => return error.FileBusy,
+ posix.EFAULT => unreachable,
+ posix.EINVAL => unreachable,
+ posix.ELOOP => return error.SymLinkLoop,
+ posix.ENAMETOOLONG => return error.NameTooLong,
+ posix.ENOENT => return error.FileNotFound,
+ posix.ENOMEM => return error.SystemResources,
+ posix.ENOTDIR => return error.NotDir,
+ posix.EEXIST => return error.DirNotEmpty,
+ posix.ENOTEMPTY => return error.DirNotEmpty,
+ posix.EROFS => return error.ReadOnlyFileSystem,
+ else => return unexpectedErrorPosix(err),
+ }
+ },
+ else => @compileError("unimplemented"),
+ }
+}
- mem.copy(u8, path_buf, dir_path);
- path_buf[dir_path.len] = 0;
+pub fn deleteDirW(dir_path_w: [*]const u16) DeleteDirError!void {
+ if (windows.RemoveDirectoryW(dir_path_w) == 0) {
+ const err = windows.GetLastError();
+ switch (err) {
+ windows.ERROR.PATH_NOT_FOUND => return error.FileNotFound,
+ windows.ERROR.DIR_NOT_EMPTY => return error.DirNotEmpty,
+ else => return unexpectedErrorWindows(err),
+ }
+ }
+}
+/// Returns ::error.DirNotEmpty if the directory is not empty.
+/// To delete a directory recursively, see ::deleteTree
+pub fn deleteDir(dir_path: []const u8) DeleteDirError!void {
switch (builtin.os) {
Os.windows => {
- if (windows.RemoveDirectoryA(path_buf.ptr) == 0) {
- const err = windows.GetLastError();
- return switch (err) {
- windows.ERROR.PATH_NOT_FOUND => error.FileNotFound,
- windows.ERROR.DIR_NOT_EMPTY => error.DirNotEmpty,
- else => unexpectedErrorWindows(err),
- };
- }
+ const dir_path_w = try windows_util.sliceToPrefixedFileW(dir_path);
+ return deleteDirW(&dir_path_w);
},
Os.linux, Os.macosx, Os.ios => {
- const err = posix.getErrno(posix.rmdir(path_buf.ptr));
- if (err > 0) {
- return switch (err) {
- posix.EACCES, posix.EPERM => error.AccessDenied,
- posix.EBUSY => error.FileBusy,
- posix.EFAULT, posix.EINVAL => unreachable,
- posix.ELOOP => error.SymLinkLoop,
- posix.ENAMETOOLONG => error.NameTooLong,
- posix.ENOENT => error.FileNotFound,
- posix.ENOMEM => error.SystemResources,
- posix.ENOTDIR => error.NotDir,
- posix.EEXIST, posix.ENOTEMPTY => error.DirNotEmpty,
- posix.EROFS => error.ReadOnlyFileSystem,
- else => unexpectedErrorPosix(err),
- };
- }
+ const dir_path_c = try toPosixPath(dir_path);
+ return deleteDirC(&dir_path_c);
},
else => @compileError("unimplemented"),
}
@@ -1346,6 +1362,7 @@ pub fn deleteTree(allocator: *Allocator, full_path: []const u8) DeleteTreeError!
error.IsDir => {},
error.AccessDenied => got_access_denied = true,
+ error.InvalidUtf8,
error.SymLinkLoop,
error.NameTooLong,
error.SystemResources,
@@ -1353,7 +1370,6 @@ pub fn deleteTree(allocator: *Allocator, full_path: []const u8) DeleteTreeError!
error.NotDir,
error.FileSystem,
error.FileBusy,
- error.InvalidUtf8,
error.BadPathName,
error.Unexpected,
=> return err,
@@ -1381,6 +1397,8 @@ pub fn deleteTree(allocator: *Allocator, full_path: []const u8) DeleteTreeError!
error.NoSpaceLeft,
error.PathAlreadyExists,
error.Unexpected,
+ error.InvalidUtf8,
+ error.BadPathName,
=> return err,
};
defer dir.close();
@@ -1398,7 +1416,7 @@ pub fn deleteTree(allocator: *Allocator, full_path: []const u8) DeleteTreeError!
try deleteTree(allocator, full_entry_path);
}
}
- return deleteDir(allocator, full_path);
+ return deleteDir(full_path);
}
}
@@ -1422,8 +1440,9 @@ pub const Dir = struct {
},
Os.windows => struct {
handle: windows.HANDLE,
- find_file_data: windows.WIN32_FIND_DATAA,
+ find_file_data: windows.WIN32_FIND_DATAW,
first: bool,
+ name_data: [256]u8,
},
else => @compileError("unimplemented"),
};
@@ -1460,6 +1479,8 @@ pub const Dir = struct {
NoSpaceLeft,
PathAlreadyExists,
OutOfMemory,
+ InvalidUtf8,
+ BadPathName,
/// See https://github.com/ziglang/zig/issues/1396
Unexpected,
@@ -1471,12 +1492,13 @@ pub const Dir = struct {
.allocator = allocator,
.handle = switch (builtin.os) {
Os.windows => blk: {
- var find_file_data: windows.WIN32_FIND_DATAA = undefined;
- const handle = try windows_util.windowsFindFirstFile(allocator, dir_path, &find_file_data);
+ var find_file_data: windows.WIN32_FIND_DATAW = undefined;
+ const handle = try windows_util.windowsFindFirstFile(dir_path, &find_file_data);
break :blk Handle{
.handle = handle,
.find_file_data = find_file_data, // TODO guaranteed copy elision
.first = true,
+ .name_data = undefined,
};
},
Os.macosx, Os.ios => Handle{
@@ -1591,9 +1613,12 @@ pub const Dir = struct {
if (!try windows_util.windowsFindNextFile(self.handle.handle, &self.handle.find_file_data))
return null;
}
- const name = std.cstr.toSlice(self.handle.find_file_data.cFileName[0..].ptr);
- if (mem.eql(u8, name, ".") or mem.eql(u8, name, ".."))
+ const name_utf16le = mem.toSlice(u16, self.handle.find_file_data.cFileName[0..].ptr);
+ if (mem.eql(u16, name_utf16le, []u16{'.'}) or mem.eql(u16, name_utf16le, []u16{'.', '.'}))
continue;
+ // Trust that Windows gives us valid UTF-16LE
+ const name_utf8_len = std.unicode.utf16leToUtf8(self.handle.name_data[0..], name_utf16le) catch unreachable;
+ const name_utf8 = self.handle.name_data[0..name_utf8_len];
const kind = blk: {
const attrs = self.handle.find_file_data.dwFileAttributes;
if (attrs & windows.FILE_ATTRIBUTE_DIRECTORY != 0) break :blk Entry.Kind.Directory;
@@ -1602,7 +1627,7 @@ pub const Dir = struct {
break :blk Entry.Kind.Unknown;
};
return Entry{
- .name = name,
+ .name = name_utf8,
.kind = kind,
};
}
@@ -2070,7 +2095,7 @@ fn testWindowsCmdLine(input_cmd_line: [*]const u8, expected_args: []const []cons
}
// TODO make this a build variable that you can set
-const unexpected_error_tracing = true;
+const unexpected_error_tracing = false;
const UnexpectedError = error{
/// The Operating System returned an undocumented error code.
Unexpected,
CMakeLists.txt
@@ -581,8 +581,6 @@ set(ZIG_STD_FILES
"os/windows/ntdll.zig"
"os/windows/ole32.zig"
"os/windows/shell32.zig"
- "os/windows/shlwapi.zig"
- "os/windows/user32.zig"
"os/windows/util.zig"
"os/zen.zig"
"pdb.zig"