authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2018-09-02 23:25:04-04:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2018-09-02 23:25:04-04:00
log92f747435930bc4d54114e414b372c7eafe7cc02
treef525a6d2c52e1bf4336ea359dd4f055a5c2bcfe4
parentd5968086fe357aa5cf678327295677dba5102fc8

switch most windows calls to use W versions instead of A

See #534

8 files changed, 223 insertions(+), 199 deletions(-)

CMakeLists.txt-2
...@@ -581,8 +581,6 @@ set(ZIG_STD_FILES...@@ -581,8 +581,6 @@ set(ZIG_STD_FILES
581 "os/windows/ntdll.zig"581 "os/windows/ntdll.zig"
582 "os/windows/ole32.zig"582 "os/windows/ole32.zig"
583 "os/windows/shell32.zig"583 "os/windows/shell32.zig"
584 "os/windows/shlwapi.zig"
585 "os/windows/user32.zig"
586 "os/windows/util.zig"584 "os/windows/util.zig"
587 "os/zen.zig"585 "os/zen.zig"
588 "pdb.zig"586 "pdb.zig"
std/os/child_process.zig+46-12
...@@ -1,5 +1,6 @@...@@ -1,5 +1,6 @@
1const std = @import("../index.zig");1const std = @import("../index.zig");
2const cstr = std.cstr;2const cstr = std.cstr;
3const unicode = std.unicode;
3const io = std.io;4const io = std.io;
4const os = std.os;5const os = std.os;
5const posix = os.posix;6const posix = os.posix;
...@@ -12,6 +13,7 @@ const Buffer = std.Buffer;...@@ -12,6 +13,7 @@ const Buffer = std.Buffer;
12const builtin = @import("builtin");13const builtin = @import("builtin");
13const Os = builtin.Os;14const Os = builtin.Os;
14const LinkedList = std.LinkedList;15const LinkedList = std.LinkedList;
16const windows_util = @import("windows/util.zig");
1517
16const is_windows = builtin.os == Os.windows;18const is_windows = builtin.os == Os.windows;
1719
...@@ -520,8 +522,8 @@ pub const ChildProcess = struct {...@@ -520,8 +522,8 @@ pub const ChildProcess = struct {
520 const cmd_line = try windowsCreateCommandLine(self.allocator, self.argv);522 const cmd_line = try windowsCreateCommandLine(self.allocator, self.argv);
521 defer self.allocator.free(cmd_line);523 defer self.allocator.free(cmd_line);
522524
523 var siStartInfo = windows.STARTUPINFOA{525 var siStartInfo = windows.STARTUPINFOW{
524 .cb = @sizeOf(windows.STARTUPINFOA),526 .cb = @sizeOf(windows.STARTUPINFOW),
525 .hStdError = g_hChildStd_ERR_Wr,527 .hStdError = g_hChildStd_ERR_Wr,
526 .hStdOutput = g_hChildStd_OUT_Wr,528 .hStdOutput = g_hChildStd_OUT_Wr,
527 .hStdInput = g_hChildStd_IN_Rd,529 .hStdInput = g_hChildStd_IN_Rd,
...@@ -545,7 +547,9 @@ pub const ChildProcess = struct {...@@ -545,7 +547,9 @@ pub const ChildProcess = struct {
545547
546 const cwd_slice = if (self.cwd) |cwd| try cstr.addNullByte(self.allocator, cwd) else null;548 const cwd_slice = if (self.cwd) |cwd| try cstr.addNullByte(self.allocator, cwd) else null;
547 defer if (cwd_slice) |cwd| self.allocator.free(cwd);549 defer if (cwd_slice) |cwd| self.allocator.free(cwd);
548 const cwd_ptr = if (cwd_slice) |cwd| cwd.ptr else null;550 const cwd_w = if (cwd_slice) |cwd| try unicode.utf8ToUtf16LeWithNull(self.allocator, cwd) else null;
551 defer if (cwd_w) |cwd| self.allocator.free(cwd);
552 const cwd_w_ptr = if (cwd_w) |cwd| cwd.ptr else null;
549553
550 const maybe_envp_buf = if (self.env_map) |env_map| try os.createWindowsEnvBlock(self.allocator, env_map) else null;554 const maybe_envp_buf = if (self.env_map) |env_map| try os.createWindowsEnvBlock(self.allocator, env_map) else null;
551 defer if (maybe_envp_buf) |envp_buf| self.allocator.free(envp_buf);555 defer if (maybe_envp_buf) |envp_buf| self.allocator.free(envp_buf);
...@@ -564,7 +568,13 @@ pub const ChildProcess = struct {...@@ -564,7 +568,13 @@ pub const ChildProcess = struct {
564 };568 };
565 defer self.allocator.free(app_name);569 defer self.allocator.free(app_name);
566570
567 windowsCreateProcess(app_name.ptr, cmd_line.ptr, envp_ptr, cwd_ptr, &siStartInfo, &piProcInfo) catch |no_path_err| {571 const app_name_w = try unicode.utf8ToUtf16LeWithNull(self.allocator, app_name);
572 defer self.allocator.free(app_name_w);
573
574 const cmd_line_w = try unicode.utf8ToUtf16LeWithNull(self.allocator, cmd_line);
575 defer self.allocator.free(cmd_line_w);
576
577 windowsCreateProcess(app_name_w.ptr, cmd_line_w.ptr, envp_ptr, cwd_w_ptr, &siStartInfo, &piProcInfo) catch |no_path_err| {
568 if (no_path_err != error.FileNotFound) return no_path_err;578 if (no_path_err != error.FileNotFound) return no_path_err;
569579
570 const PATH = try os.getEnvVarOwned(self.allocator, "PATH");580 const PATH = try os.getEnvVarOwned(self.allocator, "PATH");
...@@ -575,7 +585,10 @@ pub const ChildProcess = struct {...@@ -575,7 +585,10 @@ pub const ChildProcess = struct {
575 const joined_path = try os.path.join(self.allocator, search_path, app_name);585 const joined_path = try os.path.join(self.allocator, search_path, app_name);
576 defer self.allocator.free(joined_path);586 defer self.allocator.free(joined_path);
577587
578 if (windowsCreateProcess(joined_path.ptr, cmd_line.ptr, envp_ptr, cwd_ptr, &siStartInfo, &piProcInfo)) |_| {588 const joined_path_w = try unicode.utf8ToUtf16LeWithNull(self.allocator, app_name);
589 defer self.allocator.free(joined_path_w);
590
591 if (windowsCreateProcess(joined_path_w.ptr, cmd_line_w.ptr, envp_ptr, cwd_w_ptr, &siStartInfo, &piProcInfo)) |_| {
579 break;592 break;
580 } else |err| if (err == error.FileNotFound) {593 } else |err| if (err == error.FileNotFound) {
581 continue;594 continue;
...@@ -626,15 +639,36 @@ pub const ChildProcess = struct {...@@ -626,15 +639,36 @@ pub const ChildProcess = struct {
626 }639 }
627};640};
628641
629fn windowsCreateProcess(app_name: [*]u8, cmd_line: [*]u8, envp_ptr: ?[*]u8, cwd_ptr: ?[*]u8, lpStartupInfo: *windows.STARTUPINFOA, lpProcessInformation: *windows.PROCESS_INFORMATION) !void {642fn windowsCreateProcess(app_name: [*]u16, cmd_line: [*]u16, envp_ptr: ?[*]u16, cwd_ptr: ?[*]u16, lpStartupInfo: *windows.STARTUPINFOW, lpProcessInformation: *windows.PROCESS_INFORMATION) !void {
630 if (windows.CreateProcessA(app_name, cmd_line, null, null, windows.TRUE, 0, @ptrCast(?*c_void, envp_ptr), cwd_ptr, lpStartupInfo, lpProcessInformation) == 0) {643 // TODO the docs for environment pointer say:
644 // > A pointer to the environment block for the new process. If this parameter
645 // > is NULL, the new process uses the environment of the calling process.
646 // > ...
647 // > An environment block can contain either Unicode or ANSI characters. If
648 // > the environment block pointed to by lpEnvironment contains Unicode
649 // > characters, be sure that dwCreationFlags includes CREATE_UNICODE_ENVIRONMENT.
650 // > If this parameter is NULL and the environment block of the parent process
651 // > contains Unicode characters, you must also ensure that dwCreationFlags
652 // > includes CREATE_UNICODE_ENVIRONMENT.
653 // This seems to imply that we have to somehow know whether our process parent passed
654 // CREATE_UNICODE_ENVIRONMENT if we want to pass NULL for the environment parameter.
655 // Since we do not know this information that would imply that we must not pass NULL
656 // for the parameter.
657 // However this would imply that programs compiled with -DUNICODE could not pass
658 // environment variables to programs that were not, which seems unlikely.
659 // More investigation is needed.
660 if (windows.CreateProcessW(
661 app_name, cmd_line, null, null, windows.TRUE, windows.CREATE_UNICODE_ENVIRONMENT,
662 @ptrCast(?*c_void, envp_ptr), cwd_ptr, lpStartupInfo, lpProcessInformation,
663 ) == 0) {
631 const err = windows.GetLastError();664 const err = windows.GetLastError();
632 return switch (err) {665 switch (err) {
633 windows.ERROR.FILE_NOT_FOUND, windows.ERROR.PATH_NOT_FOUND => error.FileNotFound,666 windows.ERROR.FILE_NOT_FOUND => return error.FileNotFound,
667 windows.ERROR.PATH_NOT_FOUND => return error.FileNotFound,
634 windows.ERROR.INVALID_PARAMETER => unreachable,668 windows.ERROR.INVALID_PARAMETER => unreachable,
635 windows.ERROR.INVALID_NAME => error.InvalidName,669 windows.ERROR.INVALID_NAME => return error.InvalidName,
636 else => os.unexpectedErrorWindows(err),670 else => return os.unexpectedErrorWindows(err),
637 };671 }
638 }672 }
639}673}
640674
std/os/index.zig+112-87
...@@ -819,37 +819,40 @@ test "os.getCwd" {...@@ -819,37 +819,40 @@ test "os.getCwd" {
819819
820pub const SymLinkError = PosixSymLinkError || WindowsSymLinkError;820pub const SymLinkError = PosixSymLinkError || WindowsSymLinkError;
821821
822pub fn symLink(allocator: *Allocator, existing_path: []const u8, new_path: []const u8) SymLinkError!void {822/// TODO add a symLinkC variant
823pub fn symLink(existing_path: []const u8, new_path: []const u8) SymLinkError!void {
823 if (is_windows) {824 if (is_windows) {
824 return symLinkWindows(allocator, existing_path, new_path);825 return symLinkWindows(existing_path, new_path);
825 } else {826 } else {
826 return symLinkPosix(allocator, existing_path, new_path);827 return symLinkPosix(existing_path, new_path);
827 }828 }
828}829}
829830
830pub const WindowsSymLinkError = error{831pub const WindowsSymLinkError = error{
831 OutOfMemory,832 NameTooLong,
833 InvalidUtf8,
834 BadPathName,
832835
833 /// See https://github.com/ziglang/zig/issues/1396836 /// See https://github.com/ziglang/zig/issues/1396
834 Unexpected,837 Unexpected,
835};838};
836839
837pub fn symLinkWindows(allocator: *Allocator, existing_path: []const u8, new_path: []const u8) WindowsSymLinkError!void {840pub fn symLinkW(existing_path_w: [*]const u16, new_path_w: [*]const u16) WindowsSymLinkError!void {
838 const existing_with_null = try cstr.addNullByte(allocator, existing_path);841 if (windows.CreateSymbolicLinkW(existing_path_w, new_path_w, 0) == 0) {
839 defer allocator.free(existing_with_null);
840 const new_with_null = try cstr.addNullByte(allocator, new_path);
841 defer allocator.free(new_with_null);
842
843 if (windows.CreateSymbolicLinkA(existing_with_null.ptr, new_with_null.ptr, 0) == 0) {
844 const err = windows.GetLastError();842 const err = windows.GetLastError();
845 return switch (err) {843 switch (err) {
846 else => unexpectedErrorWindows(err),844 else => return unexpectedErrorWindows(err),
847 };845 }
848 }846 }
849}847}
850848
849pub fn symLinkWindows(existing_path: []const u8, new_path: []const u8) WindowsSymLinkError!void {
850 const existing_path_w = try windows_util.sliceToPrefixedFileW(existing_path);
851 const new_path_w = try windows_util.sliceToPrefixedFileW(new_path);
852 return symLinkW(&existing_path_w, &new_path_w);
853}
854
851pub const PosixSymLinkError = error{855pub const PosixSymLinkError = error{
852 OutOfMemory,
853 AccessDenied,856 AccessDenied,
854 DiskQuota,857 DiskQuota,
855 PathAlreadyExists,858 PathAlreadyExists,
...@@ -866,43 +869,40 @@ pub const PosixSymLinkError = error{...@@ -866,43 +869,40 @@ pub const PosixSymLinkError = error{
866 Unexpected,869 Unexpected,
867};870};
868871
869pub fn symLinkPosix(allocator: *Allocator, existing_path: []const u8, new_path: []const u8) PosixSymLinkError!void {872pub fn symLinkPosixC(existing_path: [*]const u8, new_path: [*]const u8) PosixSymLinkError!void {
870 const full_buf = try allocator.alloc(u8, existing_path.len + new_path.len + 2);873 const err = posix.getErrno(posix.symlink(existing_path, new_path));
871 defer allocator.free(full_buf);874 switch (err) {
872875 0 => return,
873 const existing_buf = full_buf;876 posix.EFAULT => unreachable,
874 mem.copy(u8, existing_buf, existing_path);877 posix.EINVAL => unreachable,
875 existing_buf[existing_path.len] = 0;878 posix.EACCES => return error.AccessDenied,
876879 posix.EPERM => return error.AccessDenied,
877 const new_buf = full_buf[existing_path.len + 1 ..];880 posix.EDQUOT => return error.DiskQuota,
878 mem.copy(u8, new_buf, new_path);881 posix.EEXIST => return error.PathAlreadyExists,
879 new_buf[new_path.len] = 0;882 posix.EIO => return error.FileSystem,
880883 posix.ELOOP => return error.SymLinkLoop,
881 const err = posix.getErrno(posix.symlink(existing_buf.ptr, new_buf.ptr));884 posix.ENAMETOOLONG => return error.NameTooLong,
882 if (err > 0) {885 posix.ENOENT => return error.FileNotFound,
883 return switch (err) {886 posix.ENOTDIR => return error.NotDir,
884 posix.EFAULT, posix.EINVAL => unreachable,887 posix.ENOMEM => return error.SystemResources,
885 posix.EACCES, posix.EPERM => error.AccessDenied,888 posix.ENOSPC => return error.NoSpaceLeft,
886 posix.EDQUOT => error.DiskQuota,889 posix.EROFS => return error.ReadOnlyFileSystem,
887 posix.EEXIST => error.PathAlreadyExists,890 else => return unexpectedErrorPosix(err),
888 posix.EIO => error.FileSystem,
889 posix.ELOOP => error.SymLinkLoop,
890 posix.ENAMETOOLONG => error.NameTooLong,
891 posix.ENOENT => error.FileNotFound,
892 posix.ENOTDIR => error.NotDir,
893 posix.ENOMEM => error.SystemResources,
894 posix.ENOSPC => error.NoSpaceLeft,
895 posix.EROFS => error.ReadOnlyFileSystem,
896 else => unexpectedErrorPosix(err),
897 };
898 }891 }
899}892}
900893
894pub fn symLinkPosix(existing_path: []const u8, new_path: []const u8) PosixSymLinkError!void {
895 const existing_path_c = try toPosixPath(existing_path);
896 const new_path_c = try toPosixPath(new_path);
897 return symLinkPosixC(&existing_path_c, &new_path_c);
898}
899
901// here we replace the standard +/ with -_ so that it can be used in a file name900// here we replace the standard +/ with -_ so that it can be used in a file name
902const b64_fs_encoder = base64.Base64Encoder.init("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_", base64.standard_pad_char);901const b64_fs_encoder = base64.Base64Encoder.init("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_", base64.standard_pad_char);
903902
903/// TODO remove the allocator requirement from this API
904pub fn atomicSymLink(allocator: *Allocator, existing_path: []const u8, new_path: []const u8) !void {904pub fn atomicSymLink(allocator: *Allocator, existing_path: []const u8, new_path: []const u8) !void {
905 if (symLink(allocator, existing_path, new_path)) {905 if (symLink(existing_path, new_path)) {
906 return;906 return;
907 } else |err| switch (err) {907 } else |err| switch (err) {
908 error.PathAlreadyExists => {},908 error.PathAlreadyExists => {},
...@@ -920,7 +920,7 @@ pub fn atomicSymLink(allocator: *Allocator, existing_path: []const u8, new_path:...@@ -920,7 +920,7 @@ pub fn atomicSymLink(allocator: *Allocator, existing_path: []const u8, new_path:
920 try getRandomBytes(rand_buf[0..]);920 try getRandomBytes(rand_buf[0..]);
921 b64_fs_encoder.encode(tmp_path[dirname.len + 1 ..], rand_buf);921 b64_fs_encoder.encode(tmp_path[dirname.len + 1 ..], rand_buf);
922922
923 if (symLink(allocator, existing_path, tmp_path)) {923 if (symLink(existing_path, tmp_path)) {
924 return rename(tmp_path, new_path);924 return rename(tmp_path, new_path);
925 } else |err| switch (err) {925 } else |err| switch (err) {
926 error.PathAlreadyExists => continue,926 error.PathAlreadyExists => continue,
...@@ -1252,49 +1252,65 @@ pub const DeleteDirError = error{...@@ -1252,49 +1252,65 @@ pub const DeleteDirError = error{
1252 NotDir,1252 NotDir,
1253 DirNotEmpty,1253 DirNotEmpty,
1254 ReadOnlyFileSystem,1254 ReadOnlyFileSystem,
1255 OutOfMemory,1255 InvalidUtf8,
1256 BadPathName,
12561257
1257 /// See https://github.com/ziglang/zig/issues/13961258 /// See https://github.com/ziglang/zig/issues/1396
1258 Unexpected,1259 Unexpected,
1259};1260};
12601261
1261/// Returns ::error.DirNotEmpty if the directory is not empty.1262pub fn deleteDirC(dir_path: [*]const u8) DeleteDirError!void {
1262/// To delete a directory recursively, see ::deleteTree1263 switch (builtin.os) {
1263pub fn deleteDir(allocator: *Allocator, dir_path: []const u8) DeleteDirError!void {1264 Os.windows => {
1264 const path_buf = try allocator.alloc(u8, dir_path.len + 1);1265 const dir_path_w = try windows_util.cStrToPrefixedFileW(dir_path);
1265 defer allocator.free(path_buf);1266 return deleteDirW(&dir_path_w);
1267 },
1268 Os.linux, Os.macosx, Os.ios => {
1269 const err = posix.getErrno(posix.rmdir(dir_path));
1270 switch (err) {
1271 0 => return,
1272 posix.EACCES => return error.AccessDenied,
1273 posix.EPERM => return error.AccessDenied,
1274 posix.EBUSY => return error.FileBusy,
1275 posix.EFAULT => unreachable,
1276 posix.EINVAL => unreachable,
1277 posix.ELOOP => return error.SymLinkLoop,
1278 posix.ENAMETOOLONG => return error.NameTooLong,
1279 posix.ENOENT => return error.FileNotFound,
1280 posix.ENOMEM => return error.SystemResources,
1281 posix.ENOTDIR => return error.NotDir,
1282 posix.EEXIST => return error.DirNotEmpty,
1283 posix.ENOTEMPTY => return error.DirNotEmpty,
1284 posix.EROFS => return error.ReadOnlyFileSystem,
1285 else => return unexpectedErrorPosix(err),
1286 }
1287 },
1288 else => @compileError("unimplemented"),
1289 }
1290}
12661291
1267 mem.copy(u8, path_buf, dir_path);1292pub fn deleteDirW(dir_path_w: [*]const u16) DeleteDirError!void {
1268 path_buf[dir_path.len] = 0;1293 if (windows.RemoveDirectoryW(dir_path_w) == 0) {
1294 const err = windows.GetLastError();
1295 switch (err) {
1296 windows.ERROR.PATH_NOT_FOUND => return error.FileNotFound,
1297 windows.ERROR.DIR_NOT_EMPTY => return error.DirNotEmpty,
1298 else => return unexpectedErrorWindows(err),
1299 }
1300 }
1301}
12691302
1303/// Returns ::error.DirNotEmpty if the directory is not empty.
1304/// To delete a directory recursively, see ::deleteTree
1305pub fn deleteDir(dir_path: []const u8) DeleteDirError!void {
1270 switch (builtin.os) {1306 switch (builtin.os) {
1271 Os.windows => {1307 Os.windows => {
1272 if (windows.RemoveDirectoryA(path_buf.ptr) == 0) {1308 const dir_path_w = try windows_util.sliceToPrefixedFileW(dir_path);
1273 const err = windows.GetLastError();1309 return deleteDirW(&dir_path_w);
1274 return switch (err) {
1275 windows.ERROR.PATH_NOT_FOUND => error.FileNotFound,
1276 windows.ERROR.DIR_NOT_EMPTY => error.DirNotEmpty,
1277 else => unexpectedErrorWindows(err),
1278 };
1279 }
1280 },1310 },
1281 Os.linux, Os.macosx, Os.ios => {1311 Os.linux, Os.macosx, Os.ios => {
1282 const err = posix.getErrno(posix.rmdir(path_buf.ptr));1312 const dir_path_c = try toPosixPath(dir_path);
1283 if (err > 0) {1313 return deleteDirC(&dir_path_c);
1284 return switch (err) {
1285 posix.EACCES, posix.EPERM => error.AccessDenied,
1286 posix.EBUSY => error.FileBusy,
1287 posix.EFAULT, posix.EINVAL => unreachable,
1288 posix.ELOOP => error.SymLinkLoop,
1289 posix.ENAMETOOLONG => error.NameTooLong,
1290 posix.ENOENT => error.FileNotFound,
1291 posix.ENOMEM => error.SystemResources,
1292 posix.ENOTDIR => error.NotDir,
1293 posix.EEXIST, posix.ENOTEMPTY => error.DirNotEmpty,
1294 posix.EROFS => error.ReadOnlyFileSystem,
1295 else => unexpectedErrorPosix(err),
1296 };
1297 }
1298 },1314 },
1299 else => @compileError("unimplemented"),1315 else => @compileError("unimplemented"),
1300 }1316 }
...@@ -1346,6 +1362,7 @@ pub fn deleteTree(allocator: *Allocator, full_path: []const u8) DeleteTreeError!...@@ -1346,6 +1362,7 @@ pub fn deleteTree(allocator: *Allocator, full_path: []const u8) DeleteTreeError!
1346 error.IsDir => {},1362 error.IsDir => {},
1347 error.AccessDenied => got_access_denied = true,1363 error.AccessDenied => got_access_denied = true,
13481364
1365 error.InvalidUtf8,
1349 error.SymLinkLoop,1366 error.SymLinkLoop,
1350 error.NameTooLong,1367 error.NameTooLong,
1351 error.SystemResources,1368 error.SystemResources,
...@@ -1353,7 +1370,6 @@ pub fn deleteTree(allocator: *Allocator, full_path: []const u8) DeleteTreeError!...@@ -1353,7 +1370,6 @@ pub fn deleteTree(allocator: *Allocator, full_path: []const u8) DeleteTreeError!
1353 error.NotDir,1370 error.NotDir,
1354 error.FileSystem,1371 error.FileSystem,
1355 error.FileBusy,1372 error.FileBusy,
1356 error.InvalidUtf8,
1357 error.BadPathName,1373 error.BadPathName,
1358 error.Unexpected,1374 error.Unexpected,
1359 => return err,1375 => return err,
...@@ -1381,6 +1397,8 @@ pub fn deleteTree(allocator: *Allocator, full_path: []const u8) DeleteTreeError!...@@ -1381,6 +1397,8 @@ pub fn deleteTree(allocator: *Allocator, full_path: []const u8) DeleteTreeError!
1381 error.NoSpaceLeft,1397 error.NoSpaceLeft,
1382 error.PathAlreadyExists,1398 error.PathAlreadyExists,
1383 error.Unexpected,1399 error.Unexpected,
1400 error.InvalidUtf8,
1401 error.BadPathName,
1384 => return err,1402 => return err,
1385 };1403 };
1386 defer dir.close();1404 defer dir.close();
...@@ -1398,7 +1416,7 @@ pub fn deleteTree(allocator: *Allocator, full_path: []const u8) DeleteTreeError!...@@ -1398,7 +1416,7 @@ pub fn deleteTree(allocator: *Allocator, full_path: []const u8) DeleteTreeError!
1398 try deleteTree(allocator, full_entry_path);1416 try deleteTree(allocator, full_entry_path);
1399 }1417 }
1400 }1418 }
1401 return deleteDir(allocator, full_path);1419 return deleteDir(full_path);
1402 }1420 }
1403}1421}
14041422
...@@ -1422,8 +1440,9 @@ pub const Dir = struct {...@@ -1422,8 +1440,9 @@ pub const Dir = struct {
1422 },1440 },
1423 Os.windows => struct {1441 Os.windows => struct {
1424 handle: windows.HANDLE,1442 handle: windows.HANDLE,
1425 find_file_data: windows.WIN32_FIND_DATAA,1443 find_file_data: windows.WIN32_FIND_DATAW,
1426 first: bool,1444 first: bool,
1445 name_data: [256]u8,
1427 },1446 },
1428 else => @compileError("unimplemented"),1447 else => @compileError("unimplemented"),
1429 };1448 };
...@@ -1460,6 +1479,8 @@ pub const Dir = struct {...@@ -1460,6 +1479,8 @@ pub const Dir = struct {
1460 NoSpaceLeft,1479 NoSpaceLeft,
1461 PathAlreadyExists,1480 PathAlreadyExists,
1462 OutOfMemory,1481 OutOfMemory,
1482 InvalidUtf8,
1483 BadPathName,
14631484
1464 /// See https://github.com/ziglang/zig/issues/13961485 /// See https://github.com/ziglang/zig/issues/1396
1465 Unexpected,1486 Unexpected,
...@@ -1471,12 +1492,13 @@ pub const Dir = struct {...@@ -1471,12 +1492,13 @@ pub const Dir = struct {
1471 .allocator = allocator,1492 .allocator = allocator,
1472 .handle = switch (builtin.os) {1493 .handle = switch (builtin.os) {
1473 Os.windows => blk: {1494 Os.windows => blk: {
1474 var find_file_data: windows.WIN32_FIND_DATAA = undefined;1495 var find_file_data: windows.WIN32_FIND_DATAW = undefined;
1475 const handle = try windows_util.windowsFindFirstFile(allocator, dir_path, &find_file_data);1496 const handle = try windows_util.windowsFindFirstFile(dir_path, &find_file_data);
1476 break :blk Handle{1497 break :blk Handle{
1477 .handle = handle,1498 .handle = handle,
1478 .find_file_data = find_file_data, // TODO guaranteed copy elision1499 .find_file_data = find_file_data, // TODO guaranteed copy elision
1479 .first = true,1500 .first = true,
1501 .name_data = undefined,
1480 };1502 };
1481 },1503 },
1482 Os.macosx, Os.ios => Handle{1504 Os.macosx, Os.ios => Handle{
...@@ -1591,9 +1613,12 @@ pub const Dir = struct {...@@ -1591,9 +1613,12 @@ pub const Dir = struct {
1591 if (!try windows_util.windowsFindNextFile(self.handle.handle, &self.handle.find_file_data))1613 if (!try windows_util.windowsFindNextFile(self.handle.handle, &self.handle.find_file_data))
1592 return null;1614 return null;
1593 }1615 }
1594 const name = std.cstr.toSlice(self.handle.find_file_data.cFileName[0..].ptr);1616 const name_utf16le = mem.toSlice(u16, self.handle.find_file_data.cFileName[0..].ptr);
1595 if (mem.eql(u8, name, ".") or mem.eql(u8, name, ".."))1617 if (mem.eql(u16, name_utf16le, []u16{'.'}) or mem.eql(u16, name_utf16le, []u16{'.', '.'}))
1596 continue;1618 continue;
1619 // Trust that Windows gives us valid UTF-16LE
1620 const name_utf8_len = std.unicode.utf16leToUtf8(self.handle.name_data[0..], name_utf16le) catch unreachable;
1621 const name_utf8 = self.handle.name_data[0..name_utf8_len];
1597 const kind = blk: {1622 const kind = blk: {
1598 const attrs = self.handle.find_file_data.dwFileAttributes;1623 const attrs = self.handle.find_file_data.dwFileAttributes;
1599 if (attrs & windows.FILE_ATTRIBUTE_DIRECTORY != 0) break :blk Entry.Kind.Directory;1624 if (attrs & windows.FILE_ATTRIBUTE_DIRECTORY != 0) break :blk Entry.Kind.Directory;
...@@ -1602,7 +1627,7 @@ pub const Dir = struct {...@@ -1602,7 +1627,7 @@ pub const Dir = struct {
1602 break :blk Entry.Kind.Unknown;1627 break :blk Entry.Kind.Unknown;
1603 };1628 };
1604 return Entry{1629 return Entry{
1605 .name = name,1630 .name = name_utf8,
1606 .kind = kind,1631 .kind = kind,
1607 };1632 };
1608 }1633 }
...@@ -2070,7 +2095,7 @@ fn testWindowsCmdLine(input_cmd_line: [*]const u8, expected_args: []const []cons...@@ -2070,7 +2095,7 @@ fn testWindowsCmdLine(input_cmd_line: [*]const u8, expected_args: []const []cons
2070}2095}
20712096
2072// TODO make this a build variable that you can set2097// TODO make this a build variable that you can set
2073const unexpected_error_tracing = true;2098const unexpected_error_tracing = false;
2074const UnexpectedError = error{2099const UnexpectedError = error{
2075 /// The Operating System returned an undocumented error code.2100 /// The Operating System returned an undocumented error code.
2076 Unexpected,2101 Unexpected,
std/os/windows/index.zig+9-9
...@@ -6,8 +6,6 @@ pub use @import("kernel32.zig");...@@ -6,8 +6,6 @@ pub use @import("kernel32.zig");
6pub use @import("ntdll.zig");6pub use @import("ntdll.zig");
7pub use @import("ole32.zig");7pub use @import("ole32.zig");
8pub use @import("shell32.zig");8pub use @import("shell32.zig");
9pub use @import("shlwapi.zig");
10pub use @import("user32.zig");
119
12test "import" {10test "import" {
13 _ = @import("util.zig");11 _ = @import("util.zig");
...@@ -174,11 +172,11 @@ pub const PROCESS_INFORMATION = extern struct {...@@ -174,11 +172,11 @@ pub const PROCESS_INFORMATION = extern struct {
174 dwThreadId: DWORD,172 dwThreadId: DWORD,
175};173};
176174
177pub const STARTUPINFOA = extern struct {175pub const STARTUPINFOW = extern struct {
178 cb: DWORD,176 cb: DWORD,
179 lpReserved: ?LPSTR,177 lpReserved: ?LPWSTR,
180 lpDesktop: ?LPSTR,178 lpDesktop: ?LPWSTR,
181 lpTitle: ?LPSTR,179 lpTitle: ?LPWSTR,
182 dwX: DWORD,180 dwX: DWORD,
183 dwY: DWORD,181 dwY: DWORD,
184 dwXSize: DWORD,182 dwXSize: DWORD,
...@@ -238,7 +236,7 @@ pub const HEAP_NO_SERIALIZE = 0x00000001;...@@ -238,7 +236,7 @@ pub const HEAP_NO_SERIALIZE = 0x00000001;
238pub const PTHREAD_START_ROUTINE = extern fn (LPVOID) DWORD;236pub const PTHREAD_START_ROUTINE = extern fn (LPVOID) DWORD;
239pub const LPTHREAD_START_ROUTINE = PTHREAD_START_ROUTINE;237pub const LPTHREAD_START_ROUTINE = PTHREAD_START_ROUTINE;
240238
241pub const WIN32_FIND_DATAA = extern struct {239pub const WIN32_FIND_DATAW = extern struct {
242 dwFileAttributes: DWORD,240 dwFileAttributes: DWORD,
243 ftCreationTime: FILETIME,241 ftCreationTime: FILETIME,
244 ftLastAccessTime: FILETIME,242 ftLastAccessTime: FILETIME,
...@@ -247,8 +245,8 @@ pub const WIN32_FIND_DATAA = extern struct {...@@ -247,8 +245,8 @@ pub const WIN32_FIND_DATAA = extern struct {
247 nFileSizeLow: DWORD,245 nFileSizeLow: DWORD,
248 dwReserved0: DWORD,246 dwReserved0: DWORD,
249 dwReserved1: DWORD,247 dwReserved1: DWORD,
250 cFileName: [260]CHAR,248 cFileName: [260]u16,
251 cAlternateFileName: [14]CHAR,249 cAlternateFileName: [14]u16,
252};250};
253251
254pub const FILETIME = extern struct {252pub const FILETIME = extern struct {
...@@ -377,3 +375,5 @@ pub const COORD = extern struct {...@@ -377,3 +375,5 @@ pub const COORD = extern struct {
377 X: SHORT,375 X: SHORT,
378 Y: SHORT,376 Y: SHORT,
379};377};
378
379pub const CREATE_UNICODE_ENVIRONMENT = 1024;
std/os/windows/kernel32.zig+10-43
...@@ -4,19 +4,8 @@ pub extern "kernel32" stdcallcc fn CancelIoEx(hFile: HANDLE, lpOverlapped: LPOVE...@@ -4,19 +4,8 @@ pub extern "kernel32" stdcallcc fn CancelIoEx(hFile: HANDLE, lpOverlapped: LPOVE
44
5pub extern "kernel32" stdcallcc fn CloseHandle(hObject: HANDLE) BOOL;5pub extern "kernel32" stdcallcc fn CloseHandle(hObject: HANDLE) BOOL;
66
7pub extern "kernel32" stdcallcc fn CreateDirectoryA(lpPathName: [*]const u8, lpSecurityAttributes: ?*SECURITY_ATTRIBUTES) BOOL;
8pub extern "kernel32" stdcallcc fn CreateDirectoryW(lpPathName: [*]const u16, lpSecurityAttributes: ?*SECURITY_ATTRIBUTES) BOOL;7pub extern "kernel32" stdcallcc fn CreateDirectoryW(lpPathName: [*]const u16, lpSecurityAttributes: ?*SECURITY_ATTRIBUTES) BOOL;
98
10pub extern "kernel32" stdcallcc fn CreateFileA(
11 lpFileName: [*]const u8, // TODO null terminated pointer type
12 dwDesiredAccess: DWORD,
13 dwShareMode: DWORD,
14 lpSecurityAttributes: ?LPSECURITY_ATTRIBUTES,
15 dwCreationDisposition: DWORD,
16 dwFlagsAndAttributes: DWORD,
17 hTemplateFile: ?HANDLE,
18) HANDLE;
19
20pub extern "kernel32" stdcallcc fn CreateFileW(9pub extern "kernel32" stdcallcc fn CreateFileW(
21 lpFileName: [*]const u16, // TODO null terminated pointer type10 lpFileName: [*]const u16, // TODO null terminated pointer type
22 dwDesiredAccess: DWORD,11 dwDesiredAccess: DWORD,
...@@ -34,37 +23,32 @@ pub extern "kernel32" stdcallcc fn CreatePipe(...@@ -34,37 +23,32 @@ pub extern "kernel32" stdcallcc fn CreatePipe(
34 nSize: DWORD,23 nSize: DWORD,
35) BOOL;24) BOOL;
3625
37pub extern "kernel32" stdcallcc fn CreateProcessA(26pub extern "kernel32" stdcallcc fn CreateProcessW(
38 lpApplicationName: ?LPCSTR,27 lpApplicationName: ?LPWSTR,
39 lpCommandLine: LPSTR,28 lpCommandLine: LPWSTR,
40 lpProcessAttributes: ?*SECURITY_ATTRIBUTES,29 lpProcessAttributes: ?*SECURITY_ATTRIBUTES,
41 lpThreadAttributes: ?*SECURITY_ATTRIBUTES,30 lpThreadAttributes: ?*SECURITY_ATTRIBUTES,
42 bInheritHandles: BOOL,31 bInheritHandles: BOOL,
43 dwCreationFlags: DWORD,32 dwCreationFlags: DWORD,
44 lpEnvironment: ?*c_void,33 lpEnvironment: ?*c_void,
45 lpCurrentDirectory: ?LPCSTR,34 lpCurrentDirectory: ?LPWSTR,
46 lpStartupInfo: *STARTUPINFOA,35 lpStartupInfo: *STARTUPINFOW,
47 lpProcessInformation: *PROCESS_INFORMATION,36 lpProcessInformation: *PROCESS_INFORMATION,
48) BOOL;37) BOOL;
4938
50pub extern "kernel32" stdcallcc fn CreateSymbolicLinkA(39pub extern "kernel32" stdcallcc fn CreateSymbolicLinkW(lpSymlinkFileName: [*]const u16, lpTargetFileName: [*]const u16, dwFlags: DWORD) BOOLEAN;
51 lpSymlinkFileName: LPCSTR,
52 lpTargetFileName: LPCSTR,
53 dwFlags: DWORD,
54) BOOLEAN;
5540
56pub extern "kernel32" stdcallcc fn CreateIoCompletionPort(FileHandle: HANDLE, ExistingCompletionPort: ?HANDLE, CompletionKey: ULONG_PTR, NumberOfConcurrentThreads: DWORD) ?HANDLE;41pub extern "kernel32" stdcallcc fn CreateIoCompletionPort(FileHandle: HANDLE, ExistingCompletionPort: ?HANDLE, CompletionKey: ULONG_PTR, NumberOfConcurrentThreads: DWORD) ?HANDLE;
5742
58pub extern "kernel32" stdcallcc fn CreateThread(lpThreadAttributes: ?LPSECURITY_ATTRIBUTES, dwStackSize: SIZE_T, lpStartAddress: LPTHREAD_START_ROUTINE, lpParameter: ?LPVOID, dwCreationFlags: DWORD, lpThreadId: ?LPDWORD) ?HANDLE;43pub extern "kernel32" stdcallcc fn CreateThread(lpThreadAttributes: ?LPSECURITY_ATTRIBUTES, dwStackSize: SIZE_T, lpStartAddress: LPTHREAD_START_ROUTINE, lpParameter: ?LPVOID, dwCreationFlags: DWORD, lpThreadId: ?LPDWORD) ?HANDLE;
5944
60pub extern "kernel32" stdcallcc fn DeleteFileA(lpFileName: [*]const u8) BOOL;
61pub extern "kernel32" stdcallcc fn DeleteFileW(lpFileName: [*]const u16) BOOL;45pub extern "kernel32" stdcallcc fn DeleteFileW(lpFileName: [*]const u16) BOOL;
6246
63pub extern "kernel32" stdcallcc fn ExitProcess(exit_code: UINT) noreturn;47pub extern "kernel32" stdcallcc fn ExitProcess(exit_code: UINT) noreturn;
6448
65pub extern "kernel32" stdcallcc fn FindFirstFileA(lpFileName: LPCSTR, lpFindFileData: *WIN32_FIND_DATAA) HANDLE;49pub extern "kernel32" stdcallcc fn FindFirstFileW(lpFileName: [*]const u16, lpFindFileData: *WIN32_FIND_DATAW) HANDLE;
66pub extern "kernel32" stdcallcc fn FindClose(hFindFile: HANDLE) BOOL;50pub extern "kernel32" stdcallcc fn FindClose(hFindFile: HANDLE) BOOL;
67pub extern "kernel32" stdcallcc fn FindNextFileA(hFindFile: HANDLE, lpFindFileData: *WIN32_FIND_DATAA) BOOL;51pub extern "kernel32" stdcallcc fn FindNextFileW(hFindFile: HANDLE, lpFindFileData: *WIN32_FIND_DATAW) BOOL;
6852
69pub extern "kernel32" stdcallcc fn FreeEnvironmentStringsA(penv: [*]u8) BOOL;53pub extern "kernel32" stdcallcc fn FreeEnvironmentStringsA(penv: [*]u8) BOOL;
7054
...@@ -74,7 +58,6 @@ pub extern "kernel32" stdcallcc fn GetConsoleMode(in_hConsoleHandle: HANDLE, out...@@ -74,7 +58,6 @@ pub extern "kernel32" stdcallcc fn GetConsoleMode(in_hConsoleHandle: HANDLE, out
7458
75pub extern "kernel32" stdcallcc fn GetConsoleScreenBufferInfo(hConsoleOutput: HANDLE, lpConsoleScreenBufferInfo: *CONSOLE_SCREEN_BUFFER_INFO) BOOL;59pub extern "kernel32" stdcallcc fn GetConsoleScreenBufferInfo(hConsoleOutput: HANDLE, lpConsoleScreenBufferInfo: *CONSOLE_SCREEN_BUFFER_INFO) BOOL;
7660
77pub extern "kernel32" stdcallcc fn GetCurrentDirectoryA(nBufferLength: DWORD, lpBuffer: ?[*]CHAR) DWORD;
78pub extern "kernel32" stdcallcc fn GetCurrentDirectoryW(nBufferLength: DWORD, lpBuffer: ?[*]WCHAR) DWORD;61pub extern "kernel32" stdcallcc fn GetCurrentDirectoryW(nBufferLength: DWORD, lpBuffer: ?[*]WCHAR) DWORD;
7962
80pub extern "kernel32" stdcallcc fn GetCurrentThread() HANDLE;63pub extern "kernel32" stdcallcc fn GetCurrentThread() HANDLE;
...@@ -88,10 +71,8 @@ pub extern "kernel32" stdcallcc fn GetExitCodeProcess(hProcess: HANDLE, lpExitCo...@@ -88,10 +71,8 @@ pub extern "kernel32" stdcallcc fn GetExitCodeProcess(hProcess: HANDLE, lpExitCo
8871
89pub extern "kernel32" stdcallcc fn GetFileSizeEx(hFile: HANDLE, lpFileSize: *LARGE_INTEGER) BOOL;72pub extern "kernel32" stdcallcc fn GetFileSizeEx(hFile: HANDLE, lpFileSize: *LARGE_INTEGER) BOOL;
9073
91pub extern "kernel32" stdcallcc fn GetFileAttributesA(lpFileName: [*]const CHAR) DWORD;
92pub extern "kernel32" stdcallcc fn GetFileAttributesW(lpFileName: [*]const WCHAR) DWORD;74pub extern "kernel32" stdcallcc fn GetFileAttributesW(lpFileName: [*]const WCHAR) DWORD;
9375
94pub extern "kernel32" stdcallcc fn GetModuleFileNameA(hModule: ?HMODULE, lpFilename: [*]u8, nSize: DWORD) DWORD;
95pub extern "kernel32" stdcallcc fn GetModuleFileNameW(hModule: ?HMODULE, lpFilename: [*]u16, nSize: DWORD) DWORD;76pub extern "kernel32" stdcallcc fn GetModuleFileNameW(hModule: ?HMODULE, lpFilename: [*]u16, nSize: DWORD) DWORD;
9677
97pub extern "kernel32" stdcallcc fn GetModuleHandleW(lpModuleName: ?[*]const WCHAR) HMODULE;78pub extern "kernel32" stdcallcc fn GetModuleHandleW(lpModuleName: ?[*]const WCHAR) HMODULE;
...@@ -105,13 +86,6 @@ pub extern "kernel32" stdcallcc fn GetFileInformationByHandleEx(...@@ -105,13 +86,6 @@ pub extern "kernel32" stdcallcc fn GetFileInformationByHandleEx(
105 in_dwBufferSize: DWORD,86 in_dwBufferSize: DWORD,
106) BOOL;87) BOOL;
10788
108pub extern "kernel32" stdcallcc fn GetFinalPathNameByHandleA(
109 hFile: HANDLE,
110 lpszFilePath: LPSTR,
111 cchFilePath: DWORD,
112 dwFlags: DWORD,
113) DWORD;
114
115pub extern "kernel32" stdcallcc fn GetFinalPathNameByHandleW(89pub extern "kernel32" stdcallcc fn GetFinalPathNameByHandleW(
116 hFile: HANDLE,90 hFile: HANDLE,
117 lpszFilePath: [*]u16,91 lpszFilePath: [*]u16,
...@@ -142,12 +116,6 @@ pub extern "kernel32" stdcallcc fn HeapFree(hHeap: HANDLE, dwFlags: DWORD, lpMem...@@ -142,12 +116,6 @@ pub extern "kernel32" stdcallcc fn HeapFree(hHeap: HANDLE, dwFlags: DWORD, lpMem
142116
143pub extern "kernel32" stdcallcc fn HeapValidate(hHeap: HANDLE, dwFlags: DWORD, lpMem: ?*const c_void) BOOL;117pub extern "kernel32" stdcallcc fn HeapValidate(hHeap: HANDLE, dwFlags: DWORD, lpMem: ?*const c_void) BOOL;
144118
145pub extern "kernel32" stdcallcc fn MoveFileExA(
146 lpExistingFileName: [*]const u8,
147 lpNewFileName: [*]const u8,
148 dwFlags: DWORD,
149) BOOL;
150
151pub extern "kernel32" stdcallcc fn MoveFileExW(119pub extern "kernel32" stdcallcc fn MoveFileExW(
152 lpExistingFileName: [*]const u16,120 lpExistingFileName: [*]const u16,
153 lpNewFileName: [*]const u16,121 lpNewFileName: [*]const u16,
...@@ -179,7 +147,7 @@ pub extern "kernel32" stdcallcc fn ReadFile(...@@ -179,7 +147,7 @@ pub extern "kernel32" stdcallcc fn ReadFile(
179 in_out_lpOverlapped: ?*OVERLAPPED,147 in_out_lpOverlapped: ?*OVERLAPPED,
180) BOOL;148) BOOL;
181149
182pub extern "kernel32" stdcallcc fn RemoveDirectoryA(lpPathName: LPCSTR) BOOL;150pub extern "kernel32" stdcallcc fn RemoveDirectoryW(lpPathName: [*]const u16) BOOL;
183151
184pub extern "kernel32" stdcallcc fn SetConsoleTextAttribute(hConsoleOutput: HANDLE, wAttributes: WORD) BOOL;152pub extern "kernel32" stdcallcc fn SetConsoleTextAttribute(hConsoleOutput: HANDLE, wAttributes: WORD) BOOL;
185153
...@@ -208,8 +176,7 @@ pub extern "kernel32" stdcallcc fn WriteFile(...@@ -208,8 +176,7 @@ pub extern "kernel32" stdcallcc fn WriteFile(
208176
209pub extern "kernel32" stdcallcc fn WriteFileEx(hFile: HANDLE, lpBuffer: [*]const u8, nNumberOfBytesToWrite: DWORD, lpOverlapped: LPOVERLAPPED, lpCompletionRoutine: LPOVERLAPPED_COMPLETION_ROUTINE) BOOL;177pub extern "kernel32" stdcallcc fn WriteFileEx(hFile: HANDLE, lpBuffer: [*]const u8, nNumberOfBytesToWrite: DWORD, lpOverlapped: LPOVERLAPPED, lpCompletionRoutine: LPOVERLAPPED_COMPLETION_ROUTINE) BOOL;
210178
211//TODO: call unicode versions instead of relying on ANSI code page179pub extern "kernel32" stdcallcc fn LoadLibraryW(lpLibFileName: [*]const u16) ?HMODULE;
212pub extern "kernel32" stdcallcc fn LoadLibraryA(lpLibFileName: LPCSTR) ?HMODULE;
213180
214pub extern "kernel32" stdcallcc fn FreeLibrary(hModule: HMODULE) BOOL;181pub extern "kernel32" stdcallcc fn FreeLibrary(hModule: HMODULE) BOOL;
215182
std/os/windows/shlwapi.zig deleted-4
...@@ -1,4 +0,0 @@
1use @import("index.zig");
2
3pub extern "shlwapi" stdcallcc fn PathFileExistsA(pszPath: ?LPCTSTR) BOOL;
4
std/os/windows/user32.zig deleted-4
...@@ -1,4 +0,0 @@
1use @import("index.zig");
2
3pub extern "user32" stdcallcc fn MessageBoxA(hWnd: ?HANDLE, lpText: ?LPCTSTR, lpCaption: ?LPCTSTR, uType: UINT) c_int;
4
std/os/windows/util.zig+46-38
...@@ -1,6 +1,7 @@...@@ -1,6 +1,7 @@
1const std = @import("../../index.zig");1const std = @import("../../index.zig");
2const builtin = @import("builtin");2const builtin = @import("builtin");
3const os = std.os;3const os = std.os;
4const unicode = std.unicode;
4const windows = std.os.windows;5const windows = std.os.windows;
5const assert = std.debug.assert;6const assert = std.debug.assert;
6const mem = std.mem;7const mem = std.mem;
...@@ -156,41 +157,51 @@ pub fn windowsOpen(...@@ -156,41 +157,51 @@ pub fn windowsOpen(
156}157}
157158
158/// Caller must free result.159/// Caller must free result.
159pub fn createWindowsEnvBlock(allocator: *mem.Allocator, env_map: *const BufMap) ![]u8 {160pub fn createWindowsEnvBlock(allocator: *mem.Allocator, env_map: *const BufMap) ![]u16 {
160 // count bytes needed161 // count bytes needed
161 const bytes_needed = x: {162 const max_chars_needed = x: {
162 var bytes_needed: usize = 1; // 1 for the final null byte163 var max_chars_needed: usize = 1; // 1 for the final null byte
163 var it = env_map.iterator();164 var it = env_map.iterator();
164 while (it.next()) |pair| {165 while (it.next()) |pair| {
165 // +1 for '='166 // +1 for '='
166 // +1 for null byte167 // +1 for null byte
167 bytes_needed += pair.key.len + pair.value.len + 2;168 max_chars_needed += pair.key.len + pair.value.len + 2;
168 }169 }
169 break :x bytes_needed;170 break :x max_chars_needed;
170 };171 };
171 const result = try allocator.alloc(u8, bytes_needed);172 const result = try allocator.alloc(u16, max_chars_needed);
172 errdefer allocator.free(result);173 errdefer allocator.free(result);
173174
174 var it = env_map.iterator();175 var it = env_map.iterator();
175 var i: usize = 0;176 var i: usize = 0;
176 while (it.next()) |pair| {177 while (it.next()) |pair| {
177 mem.copy(u8, result[i..], pair.key);178 i += try unicode.utf8ToUtf16Le(result[i..], pair.key);
178 i += pair.key.len;
179 result[i] = '=';179 result[i] = '=';
180 i += 1;180 i += 1;
181 mem.copy(u8, result[i..], pair.value);181 i += try unicode.utf8ToUtf16Le(result[i..], pair.value);
182 i += pair.value.len;
183 result[i] = 0;182 result[i] = 0;
184 i += 1;183 i += 1;
185 }184 }
186 result[i] = 0;185 result[i] = 0;
187 return result;186 i += 1;
187 return allocator.shrink(u16, result, i);
188}188}
189189
190pub fn windowsLoadDll(allocator: *mem.Allocator, dll_path: []const u8) !windows.HMODULE {190pub fn windowsLoadDllW(dll_path_w: [*]const u16) !windows.HMODULE {
191 const padded_buff = try cstr.addNullByte(allocator, dll_path);191 return windows.LoadLibraryW(dll_path_w) orelse {
192 defer allocator.free(padded_buff);192 const err = windows.GetLastError();
193 return windows.LoadLibraryA(padded_buff.ptr) orelse error.DllNotFound;193 switch (err) {
194 windows.ERROR.FILE_NOT_FOUND => return error.FileNotFound,
195 windows.ERROR.PATH_NOT_FOUND => return error.FileNotFound,
196 windows.ERROR.MOD_NOT_FOUND => return error.FileNotFound,
197 else => return os.unexpectedErrorWindows(err),
198 }
199 };
200}
201
202pub fn windowsLoadDll(dll_path: []const u8) !windows.HMODULE {
203 const dll_path_w = try sliceToPrefixedFileW(dll_path);
204 return windowsLoadDllW(&dll_path_w);
194}205}
195206
196pub fn windowsUnloadDll(hModule: windows.HMODULE) void {207pub fn windowsUnloadDll(hModule: windows.HMODULE) void {
...@@ -200,27 +211,19 @@ pub fn windowsUnloadDll(hModule: windows.HMODULE) void {...@@ -200,27 +211,19 @@ pub fn windowsUnloadDll(hModule: windows.HMODULE) void {
200test "InvalidDll" {211test "InvalidDll" {
201 if (builtin.os != builtin.Os.windows) return error.SkipZigTest;212 if (builtin.os != builtin.Os.windows) return error.SkipZigTest;
202213
203 const DllName = "asdf.dll";214 const handle = os.windowsLoadDll("asdf.dll") catch |err| {
204 const allocator = std.debug.global_allocator;215 assert(err == error.FileNotFound);
205 const handle = os.windowsLoadDll(allocator, DllName) catch |err| {
206 assert(err == error.DllNotFound);
207 return;216 return;
208 };217 };
218 @panic("Expected error from function");
209}219}
210220
211pub fn windowsFindFirstFile(221pub fn windowsFindFirstFile(
212 allocator: *mem.Allocator,
213 dir_path: []const u8,222 dir_path: []const u8,
214 find_file_data: *windows.WIN32_FIND_DATAA,223 find_file_data: *windows.WIN32_FIND_DATAW,
215) !windows.HANDLE {224) !windows.HANDLE {
216 const wild_and_null = []u8{ '\\', '*', 0 };225 const dir_path_w = try sliceToPrefixedSuffixedFileW(dir_path, []u16{'\\', '*', 0});
217 const path_with_wild_and_null = try allocator.alloc(u8, dir_path.len + wild_and_null.len);226 const handle = windows.FindFirstFileW(&dir_path_w, find_file_data);
218 defer allocator.free(path_with_wild_and_null);
219
220 mem.copy(u8, path_with_wild_and_null, dir_path);
221 mem.copy(u8, path_with_wild_and_null[dir_path.len..], wild_and_null);
222
223 const handle = windows.FindFirstFileA(path_with_wild_and_null.ptr, find_file_data);
224227
225 if (handle == windows.INVALID_HANDLE_VALUE) {228 if (handle == windows.INVALID_HANDLE_VALUE) {
226 const err = windows.GetLastError();229 const err = windows.GetLastError();
...@@ -235,8 +238,8 @@ pub fn windowsFindFirstFile(...@@ -235,8 +238,8 @@ pub fn windowsFindFirstFile(
235}238}
236239
237/// Returns `true` if there was another file, `false` otherwise.240/// Returns `true` if there was another file, `false` otherwise.
238pub fn windowsFindNextFile(handle: windows.HANDLE, find_file_data: *windows.WIN32_FIND_DATAA) !bool {241pub fn windowsFindNextFile(handle: windows.HANDLE, find_file_data: *windows.WIN32_FIND_DATAW) !bool {
239 if (windows.FindNextFileA(handle, find_file_data) == 0) {242 if (windows.FindNextFileW(handle, find_file_data) == 0) {
240 const err = windows.GetLastError();243 const err = windows.GetLastError();
241 return switch (err) {244 return switch (err) {
242 windows.ERROR.NO_MORE_FILES => false,245 windows.ERROR.NO_MORE_FILES => false,
...@@ -297,8 +300,12 @@ pub fn cStrToPrefixedFileW(s: [*]const u8) ![PATH_MAX_WIDE + 1]u16 {...@@ -297,8 +300,12 @@ pub fn cStrToPrefixedFileW(s: [*]const u8) ![PATH_MAX_WIDE + 1]u16 {
297}300}
298301
299pub fn sliceToPrefixedFileW(s: []const u8) ![PATH_MAX_WIDE + 1]u16 {302pub fn sliceToPrefixedFileW(s: []const u8) ![PATH_MAX_WIDE + 1]u16 {
303 return sliceToPrefixedSuffixedFileW(s, []u16{0});
304}
305
306pub fn sliceToPrefixedSuffixedFileW(s: []const u8, comptime suffix: []const u16) ![PATH_MAX_WIDE + suffix.len]u16 {
300 // TODO well defined copy elision307 // TODO well defined copy elision
301 var result: [PATH_MAX_WIDE + 1]u16 = undefined;308 var result: [PATH_MAX_WIDE + suffix.len]u16 = undefined;
302309
303 // > File I/O functions in the Windows API convert "/" to "\" as part of310 // > File I/O functions in the Windows API convert "/" to "\" as part of
304 // > converting the name to an NT-style name, except when using the "\\?\"311 // > 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 {...@@ -306,11 +313,12 @@ pub fn sliceToPrefixedFileW(s: []const u8) ![PATH_MAX_WIDE + 1]u16 {
306 // from https://docs.microsoft.com/en-us/windows/desktop/FileIO/naming-a-file#maximum-path-length-limitation313 // from https://docs.microsoft.com/en-us/windows/desktop/FileIO/naming-a-file#maximum-path-length-limitation
307 // Because we want the larger maximum path length for absolute paths, we314 // Because we want the larger maximum path length for absolute paths, we
308 // disallow forward slashes in zig std lib file functions on Windows.315 // disallow forward slashes in zig std lib file functions on Windows.
309 for (s) |byte|316 for (s) |byte| {
310 switch (byte) {317 switch (byte) {
311 '/', '*', '?', '"', '<', '>', '|' => return error.BadPathName,318 '/', '*', '?', '"', '<', '>', '|' => return error.BadPathName,
312 else => {},319 else => {},
313 };320 }
321 }
314 const start_index = if (mem.startsWith(u8, s, "\\\\") or !os.path.isAbsolute(s)) 0 else blk: {322 const start_index = if (mem.startsWith(u8, s, "\\\\") or !os.path.isAbsolute(s)) 0 else blk: {
315 const prefix = []u16{ '\\', '\\', '?', '\\' };323 const prefix = []u16{ '\\', '\\', '?', '\\' };
316 mem.copy(u16, result[0..], prefix);324 mem.copy(u16, result[0..], prefix);
...@@ -318,7 +326,7 @@ pub fn sliceToPrefixedFileW(s: []const u8) ![PATH_MAX_WIDE + 1]u16 {...@@ -318,7 +326,7 @@ pub fn sliceToPrefixedFileW(s: []const u8) ![PATH_MAX_WIDE + 1]u16 {
318 };326 };
319 const end_index = start_index + try std.unicode.utf8ToUtf16Le(result[start_index..], s);327 const end_index = start_index + try std.unicode.utf8ToUtf16Le(result[start_index..], s);
320 assert(end_index <= result.len);328 assert(end_index <= result.len);
321 if (end_index == result.len) return error.NameTooLong;329 if (end_index + suffix.len > result.len) return error.NameTooLong;
322 result[end_index] = 0;330 mem.copy(u16, result[end_index..], suffix);
323 return result;331 return result;
324}332}