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
581581 "os/windows/ntdll.zig"
582582 "os/windows/ole32.zig"
583583 "os/windows/shell32.zig"
584 "os/windows/shlwapi.zig"
585 "os/windows/user32.zig"
586584 "os/windows/util.zig"
587585 "os/zen.zig"
588586 "pdb.zig"
std/os/child_process.zig+46-12
......@@ -1,5 +1,6 @@
11const std = @import("../index.zig");
22const cstr = std.cstr;
3const unicode = std.unicode;
34const io = std.io;
45const os = std.os;
56const posix = os.posix;
......@@ -12,6 +13,7 @@ const Buffer = std.Buffer;
1213const builtin = @import("builtin");
1314const Os = builtin.Os;
1415const LinkedList = std.LinkedList;
16const windows_util = @import("windows/util.zig");
1517
1618const is_windows = builtin.os == Os.windows;
1719
......@@ -520,8 +522,8 @@ pub const ChildProcess = struct {
520522 const cmd_line = try windowsCreateCommandLine(self.allocator, self.argv);
521523 defer self.allocator.free(cmd_line);
522524
523 var siStartInfo = windows.STARTUPINFOA{
524 .cb = @sizeOf(windows.STARTUPINFOA),
525 var siStartInfo = windows.STARTUPINFOW{
526 .cb = @sizeOf(windows.STARTUPINFOW),
525527 .hStdError = g_hChildStd_ERR_Wr,
526528 .hStdOutput = g_hChildStd_OUT_Wr,
527529 .hStdInput = g_hChildStd_IN_Rd,
......@@ -545,7 +547,9 @@ pub const ChildProcess = struct {
545547
546548 const cwd_slice = if (self.cwd) |cwd| try cstr.addNullByte(self.allocator, cwd) else null;
547549 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
550554 const maybe_envp_buf = if (self.env_map) |env_map| try os.createWindowsEnvBlock(self.allocator, env_map) else null;
551555 defer if (maybe_envp_buf) |envp_buf| self.allocator.free(envp_buf);
......@@ -564,7 +568,13 @@ pub const ChildProcess = struct {
564568 };
565569 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| {
568578 if (no_path_err != error.FileNotFound) return no_path_err;
569579
570580 const PATH = try os.getEnvVarOwned(self.allocator, "PATH");
......@@ -575,7 +585,10 @@ pub const ChildProcess = struct {
575585 const joined_path = try os.path.join(self.allocator, search_path, app_name);
576586 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)) |_| {
579592 break;
580593 } else |err| if (err == error.FileNotFound) {
581594 continue;
......@@ -626,15 +639,36 @@ pub const ChildProcess = struct {
626639 }
627640};
628641
629fn windowsCreateProcess(app_name: [*]u8, cmd_line: [*]u8, envp_ptr: ?[*]u8, cwd_ptr: ?[*]u8, lpStartupInfo: *windows.STARTUPINFOA, 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) {
642fn windowsCreateProcess(app_name: [*]u16, cmd_line: [*]u16, envp_ptr: ?[*]u16, cwd_ptr: ?[*]u16, lpStartupInfo: *windows.STARTUPINFOW, lpProcessInformation: *windows.PROCESS_INFORMATION) !void {
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) {
631664 const err = windows.GetLastError();
632 return switch (err) {
633 windows.ERROR.FILE_NOT_FOUND, windows.ERROR.PATH_NOT_FOUND => error.FileNotFound,
665 switch (err) {
666 windows.ERROR.FILE_NOT_FOUND => return error.FileNotFound,
667 windows.ERROR.PATH_NOT_FOUND => return error.FileNotFound,
634668 windows.ERROR.INVALID_PARAMETER => unreachable,
635 windows.ERROR.INVALID_NAME => error.InvalidName,
636 else => os.unexpectedErrorWindows(err),
637 };
669 windows.ERROR.INVALID_NAME => return error.InvalidName,
670 else => return os.unexpectedErrorWindows(err),
671 }
638672 }
639673}
640674
std/os/index.zig+112-87
......@@ -819,37 +819,40 @@ test "os.getCwd" {
819819
820820pub 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 {
823824 if (is_windows) {
824 return symLinkWindows(allocator, existing_path, new_path);
825 return symLinkWindows(existing_path, new_path);
825826 } else {
826 return symLinkPosix(allocator, existing_path, new_path);
827 return symLinkPosix(existing_path, new_path);
827828 }
828829}
829830
830831pub const WindowsSymLinkError = error{
831 OutOfMemory,
832 NameTooLong,
833 InvalidUtf8,
834 BadPathName,
832835
833836 /// See https://github.com/ziglang/zig/issues/1396
834837 Unexpected,
835838};
836839
837pub fn symLinkWindows(allocator: *Allocator, existing_path: []const u8, new_path: []const u8) WindowsSymLinkError!void {
838 const existing_with_null = try cstr.addNullByte(allocator, existing_path);
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) {
840pub fn symLinkW(existing_path_w: [*]const u16, new_path_w: [*]const u16) WindowsSymLinkError!void {
841 if (windows.CreateSymbolicLinkW(existing_path_w, new_path_w, 0) == 0) {
844842 const err = windows.GetLastError();
845 return switch (err) {
846 else => unexpectedErrorWindows(err),
847 };
843 switch (err) {
844 else => return unexpectedErrorWindows(err),
845 }
848846 }
849847}
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
851855pub const PosixSymLinkError = error{
852 OutOfMemory,
853856 AccessDenied,
854857 DiskQuota,
855858 PathAlreadyExists,
......@@ -866,43 +869,40 @@ pub const PosixSymLinkError = error{
866869 Unexpected,
867870};
868871
869pub fn symLinkPosix(allocator: *Allocator, 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);
871 defer allocator.free(full_buf);
872
873 const existing_buf = full_buf;
874 mem.copy(u8, existing_buf, existing_path);
875 existing_buf[existing_path.len] = 0;
876
877 const new_buf = full_buf[existing_path.len + 1 ..];
878 mem.copy(u8, new_buf, new_path);
879 new_buf[new_path.len] = 0;
880
881 const err = posix.getErrno(posix.symlink(existing_buf.ptr, new_buf.ptr));
882 if (err > 0) {
883 return switch (err) {
884 posix.EFAULT, posix.EINVAL => unreachable,
885 posix.EACCES, posix.EPERM => error.AccessDenied,
886 posix.EDQUOT => error.DiskQuota,
887 posix.EEXIST => error.PathAlreadyExists,
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 };
872pub fn symLinkPosixC(existing_path: [*]const u8, new_path: [*]const u8) PosixSymLinkError!void {
873 const err = posix.getErrno(posix.symlink(existing_path, new_path));
874 switch (err) {
875 0 => return,
876 posix.EFAULT => unreachable,
877 posix.EINVAL => unreachable,
878 posix.EACCES => return error.AccessDenied,
879 posix.EPERM => return error.AccessDenied,
880 posix.EDQUOT => return error.DiskQuota,
881 posix.EEXIST => return error.PathAlreadyExists,
882 posix.EIO => return error.FileSystem,
883 posix.ELOOP => return error.SymLinkLoop,
884 posix.ENAMETOOLONG => return error.NameTooLong,
885 posix.ENOENT => return error.FileNotFound,
886 posix.ENOTDIR => return error.NotDir,
887 posix.ENOMEM => return error.SystemResources,
888 posix.ENOSPC => return error.NoSpaceLeft,
889 posix.EROFS => return error.ReadOnlyFileSystem,
890 else => return unexpectedErrorPosix(err),
898891 }
899892}
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
901900// here we replace the standard +/ with -_ so that it can be used in a file name
902901const b64_fs_encoder = base64.Base64Encoder.init("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_", base64.standard_pad_char);
903902
903/// TODO remove the allocator requirement from this API
904904pub 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)) {
906906 return;
907907 } else |err| switch (err) {
908908 error.PathAlreadyExists => {},
......@@ -920,7 +920,7 @@ pub fn atomicSymLink(allocator: *Allocator, existing_path: []const u8, new_path:
920920 try getRandomBytes(rand_buf[0..]);
921921 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)) {
924924 return rename(tmp_path, new_path);
925925 } else |err| switch (err) {
926926 error.PathAlreadyExists => continue,
......@@ -1252,49 +1252,65 @@ pub const DeleteDirError = error{
12521252 NotDir,
12531253 DirNotEmpty,
12541254 ReadOnlyFileSystem,
1255 OutOfMemory,
1255 InvalidUtf8,
1256 BadPathName,
12561257
12571258 /// See https://github.com/ziglang/zig/issues/1396
12581259 Unexpected,
12591260};
12601261
1261/// Returns ::error.DirNotEmpty if the directory is not empty.
1262/// To delete a directory recursively, see ::deleteTree
1263pub fn deleteDir(allocator: *Allocator, dir_path: []const u8) DeleteDirError!void {
1264 const path_buf = try allocator.alloc(u8, dir_path.len + 1);
1265 defer allocator.free(path_buf);
1262pub fn deleteDirC(dir_path: [*]const u8) DeleteDirError!void {
1263 switch (builtin.os) {
1264 Os.windows => {
1265 const dir_path_w = try windows_util.cStrToPrefixedFileW(dir_path);
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);
1268 path_buf[dir_path.len] = 0;
1292pub fn deleteDirW(dir_path_w: [*]const u16) DeleteDirError!void {
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 {
12701306 switch (builtin.os) {
12711307 Os.windows => {
1272 if (windows.RemoveDirectoryA(path_buf.ptr) == 0) {
1273 const err = windows.GetLastError();
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 }
1308 const dir_path_w = try windows_util.sliceToPrefixedFileW(dir_path);
1309 return deleteDirW(&dir_path_w);
12801310 },
12811311 Os.linux, Os.macosx, Os.ios => {
1282 const err = posix.getErrno(posix.rmdir(path_buf.ptr));
1283 if (err > 0) {
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 }
1312 const dir_path_c = try toPosixPath(dir_path);
1313 return deleteDirC(&dir_path_c);
12981314 },
12991315 else => @compileError("unimplemented"),
13001316 }
......@@ -1346,6 +1362,7 @@ pub fn deleteTree(allocator: *Allocator, full_path: []const u8) DeleteTreeError!
13461362 error.IsDir => {},
13471363 error.AccessDenied => got_access_denied = true,
13481364
1365 error.InvalidUtf8,
13491366 error.SymLinkLoop,
13501367 error.NameTooLong,
13511368 error.SystemResources,
......@@ -1353,7 +1370,6 @@ pub fn deleteTree(allocator: *Allocator, full_path: []const u8) DeleteTreeError!
13531370 error.NotDir,
13541371 error.FileSystem,
13551372 error.FileBusy,
1356 error.InvalidUtf8,
13571373 error.BadPathName,
13581374 error.Unexpected,
13591375 => return err,
......@@ -1381,6 +1397,8 @@ pub fn deleteTree(allocator: *Allocator, full_path: []const u8) DeleteTreeError!
13811397 error.NoSpaceLeft,
13821398 error.PathAlreadyExists,
13831399 error.Unexpected,
1400 error.InvalidUtf8,
1401 error.BadPathName,
13841402 => return err,
13851403 };
13861404 defer dir.close();
......@@ -1398,7 +1416,7 @@ pub fn deleteTree(allocator: *Allocator, full_path: []const u8) DeleteTreeError!
13981416 try deleteTree(allocator, full_entry_path);
13991417 }
14001418 }
1401 return deleteDir(allocator, full_path);
1419 return deleteDir(full_path);
14021420 }
14031421}
14041422
......@@ -1422,8 +1440,9 @@ pub const Dir = struct {
14221440 },
14231441 Os.windows => struct {
14241442 handle: windows.HANDLE,
1425 find_file_data: windows.WIN32_FIND_DATAA,
1443 find_file_data: windows.WIN32_FIND_DATAW,
14261444 first: bool,
1445 name_data: [256]u8,
14271446 },
14281447 else => @compileError("unimplemented"),
14291448 };
......@@ -1460,6 +1479,8 @@ pub const Dir = struct {
14601479 NoSpaceLeft,
14611480 PathAlreadyExists,
14621481 OutOfMemory,
1482 InvalidUtf8,
1483 BadPathName,
14631484
14641485 /// See https://github.com/ziglang/zig/issues/1396
14651486 Unexpected,
......@@ -1471,12 +1492,13 @@ pub const Dir = struct {
14711492 .allocator = allocator,
14721493 .handle = switch (builtin.os) {
14731494 Os.windows => blk: {
1474 var find_file_data: windows.WIN32_FIND_DATAA = undefined;
1475 const handle = try windows_util.windowsFindFirstFile(allocator, dir_path, &find_file_data);
1495 var find_file_data: windows.WIN32_FIND_DATAW = undefined;
1496 const handle = try windows_util.windowsFindFirstFile(dir_path, &find_file_data);
14761497 break :blk Handle{
14771498 .handle = handle,
14781499 .find_file_data = find_file_data, // TODO guaranteed copy elision
14791500 .first = true,
1501 .name_data = undefined,
14801502 };
14811503 },
14821504 Os.macosx, Os.ios => Handle{
......@@ -1591,9 +1613,12 @@ pub const Dir = struct {
15911613 if (!try windows_util.windowsFindNextFile(self.handle.handle, &self.handle.find_file_data))
15921614 return null;
15931615 }
1594 const name = std.cstr.toSlice(self.handle.find_file_data.cFileName[0..].ptr);
1595 if (mem.eql(u8, name, ".") or mem.eql(u8, name, ".."))
1616 const name_utf16le = mem.toSlice(u16, self.handle.find_file_data.cFileName[0..].ptr);
1617 if (mem.eql(u16, name_utf16le, []u16{'.'}) or mem.eql(u16, name_utf16le, []u16{'.', '.'}))
15961618 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];
15971622 const kind = blk: {
15981623 const attrs = self.handle.find_file_data.dwFileAttributes;
15991624 if (attrs & windows.FILE_ATTRIBUTE_DIRECTORY != 0) break :blk Entry.Kind.Directory;
......@@ -1602,7 +1627,7 @@ pub const Dir = struct {
16021627 break :blk Entry.Kind.Unknown;
16031628 };
16041629 return Entry{
1605 .name = name,
1630 .name = name_utf8,
16061631 .kind = kind,
16071632 };
16081633 }
......@@ -2070,7 +2095,7 @@ fn testWindowsCmdLine(input_cmd_line: [*]const u8, expected_args: []const []cons
20702095}
20712096
20722097// TODO make this a build variable that you can set
2073const unexpected_error_tracing = true;
2098const unexpected_error_tracing = false;
20742099const UnexpectedError = error{
20752100 /// The Operating System returned an undocumented error code.
20762101 Unexpected,
std/os/windows/index.zig+9-9
......@@ -6,8 +6,6 @@ pub use @import("kernel32.zig");
66pub use @import("ntdll.zig");
77pub use @import("ole32.zig");
88pub use @import("shell32.zig");
9pub use @import("shlwapi.zig");
10pub use @import("user32.zig");
119
1210test "import" {
1311 _ = @import("util.zig");
......@@ -174,11 +172,11 @@ pub const PROCESS_INFORMATION = extern struct {
174172 dwThreadId: DWORD,
175173};
176174
177pub const STARTUPINFOA = extern struct {
175pub const STARTUPINFOW = extern struct {
178176 cb: DWORD,
179 lpReserved: ?LPSTR,
180 lpDesktop: ?LPSTR,
181 lpTitle: ?LPSTR,
177 lpReserved: ?LPWSTR,
178 lpDesktop: ?LPWSTR,
179 lpTitle: ?LPWSTR,
182180 dwX: DWORD,
183181 dwY: DWORD,
184182 dwXSize: DWORD,
......@@ -238,7 +236,7 @@ pub const HEAP_NO_SERIALIZE = 0x00000001;
238236pub const PTHREAD_START_ROUTINE = extern fn (LPVOID) DWORD;
239237pub const LPTHREAD_START_ROUTINE = PTHREAD_START_ROUTINE;
240238
241pub const WIN32_FIND_DATAA = extern struct {
239pub const WIN32_FIND_DATAW = extern struct {
242240 dwFileAttributes: DWORD,
243241 ftCreationTime: FILETIME,
244242 ftLastAccessTime: FILETIME,
......@@ -247,8 +245,8 @@ pub const WIN32_FIND_DATAA = extern struct {
247245 nFileSizeLow: DWORD,
248246 dwReserved0: DWORD,
249247 dwReserved1: DWORD,
250 cFileName: [260]CHAR,
251 cAlternateFileName: [14]CHAR,
248 cFileName: [260]u16,
249 cAlternateFileName: [14]u16,
252250};
253251
254252pub const FILETIME = extern struct {
......@@ -377,3 +375,5 @@ pub const COORD = extern struct {
377375 X: SHORT,
378376 Y: SHORT,
379377};
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
44
55pub extern "kernel32" stdcallcc fn CloseHandle(hObject: HANDLE) BOOL;
66
7pub extern "kernel32" stdcallcc fn CreateDirectoryA(lpPathName: [*]const u8, lpSecurityAttributes: ?*SECURITY_ATTRIBUTES) BOOL;
87pub 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
209pub extern "kernel32" stdcallcc fn CreateFileW(
2110 lpFileName: [*]const u16, // TODO null terminated pointer type
2211 dwDesiredAccess: DWORD,
......@@ -34,37 +23,32 @@ pub extern "kernel32" stdcallcc fn CreatePipe(
3423 nSize: DWORD,
3524) BOOL;
3625
37pub extern "kernel32" stdcallcc fn CreateProcessA(
38 lpApplicationName: ?LPCSTR,
39 lpCommandLine: LPSTR,
26pub extern "kernel32" stdcallcc fn CreateProcessW(
27 lpApplicationName: ?LPWSTR,
28 lpCommandLine: LPWSTR,
4029 lpProcessAttributes: ?*SECURITY_ATTRIBUTES,
4130 lpThreadAttributes: ?*SECURITY_ATTRIBUTES,
4231 bInheritHandles: BOOL,
4332 dwCreationFlags: DWORD,
4433 lpEnvironment: ?*c_void,
45 lpCurrentDirectory: ?LPCSTR,
46 lpStartupInfo: *STARTUPINFOA,
34 lpCurrentDirectory: ?LPWSTR,
35 lpStartupInfo: *STARTUPINFOW,
4736 lpProcessInformation: *PROCESS_INFORMATION,
4837) BOOL;
4938
50pub extern "kernel32" stdcallcc fn CreateSymbolicLinkA(
51 lpSymlinkFileName: LPCSTR,
52 lpTargetFileName: LPCSTR,
53 dwFlags: DWORD,
54) BOOLEAN;
39pub extern "kernel32" stdcallcc fn CreateSymbolicLinkW(lpSymlinkFileName: [*]const u16, lpTargetFileName: [*]const u16, dwFlags: DWORD) BOOLEAN;
5540
5641pub extern "kernel32" stdcallcc fn CreateIoCompletionPort(FileHandle: HANDLE, ExistingCompletionPort: ?HANDLE, CompletionKey: ULONG_PTR, NumberOfConcurrentThreads: DWORD) ?HANDLE;
5742
5843pub 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;
6145pub extern "kernel32" stdcallcc fn DeleteFileW(lpFileName: [*]const u16) BOOL;
6246
6347pub 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;
6650pub 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
6953pub extern "kernel32" stdcallcc fn FreeEnvironmentStringsA(penv: [*]u8) BOOL;
7054
......@@ -74,7 +58,6 @@ pub extern "kernel32" stdcallcc fn GetConsoleMode(in_hConsoleHandle: HANDLE, out
7458
7559pub 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;
7861pub extern "kernel32" stdcallcc fn GetCurrentDirectoryW(nBufferLength: DWORD, lpBuffer: ?[*]WCHAR) DWORD;
7962
8063pub extern "kernel32" stdcallcc fn GetCurrentThread() HANDLE;
......@@ -88,10 +71,8 @@ pub extern "kernel32" stdcallcc fn GetExitCodeProcess(hProcess: HANDLE, lpExitCo
8871
8972pub extern "kernel32" stdcallcc fn GetFileSizeEx(hFile: HANDLE, lpFileSize: *LARGE_INTEGER) BOOL;
9073
91pub extern "kernel32" stdcallcc fn GetFileAttributesA(lpFileName: [*]const CHAR) DWORD;
9274pub extern "kernel32" stdcallcc fn GetFileAttributesW(lpFileName: [*]const WCHAR) DWORD;
9375
94pub extern "kernel32" stdcallcc fn GetModuleFileNameA(hModule: ?HMODULE, lpFilename: [*]u8, nSize: DWORD) DWORD;
9576pub extern "kernel32" stdcallcc fn GetModuleFileNameW(hModule: ?HMODULE, lpFilename: [*]u16, nSize: DWORD) DWORD;
9677
9778pub extern "kernel32" stdcallcc fn GetModuleHandleW(lpModuleName: ?[*]const WCHAR) HMODULE;
......@@ -105,13 +86,6 @@ pub extern "kernel32" stdcallcc fn GetFileInformationByHandleEx(
10586 in_dwBufferSize: DWORD,
10687) BOOL;
10788
108pub extern "kernel32" stdcallcc fn GetFinalPathNameByHandleA(
109 hFile: HANDLE,
110 lpszFilePath: LPSTR,
111 cchFilePath: DWORD,
112 dwFlags: DWORD,
113) DWORD;
114
11589pub extern "kernel32" stdcallcc fn GetFinalPathNameByHandleW(
11690 hFile: HANDLE,
11791 lpszFilePath: [*]u16,
......@@ -142,12 +116,6 @@ pub extern "kernel32" stdcallcc fn HeapFree(hHeap: HANDLE, dwFlags: DWORD, lpMem
142116
143117pub 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
151119pub extern "kernel32" stdcallcc fn MoveFileExW(
152120 lpExistingFileName: [*]const u16,
153121 lpNewFileName: [*]const u16,
......@@ -179,7 +147,7 @@ pub extern "kernel32" stdcallcc fn ReadFile(
179147 in_out_lpOverlapped: ?*OVERLAPPED,
180148) BOOL;
181149
182pub extern "kernel32" stdcallcc fn RemoveDirectoryA(lpPathName: LPCSTR) BOOL;
150pub extern "kernel32" stdcallcc fn RemoveDirectoryW(lpPathName: [*]const u16) BOOL;
183151
184152pub extern "kernel32" stdcallcc fn SetConsoleTextAttribute(hConsoleOutput: HANDLE, wAttributes: WORD) BOOL;
185153
......@@ -208,8 +176,7 @@ pub extern "kernel32" stdcallcc fn WriteFile(
208176
209177pub 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 page
212pub extern "kernel32" stdcallcc fn LoadLibraryA(lpLibFileName: LPCSTR) ?HMODULE;
179pub extern "kernel32" stdcallcc fn LoadLibraryW(lpLibFileName: [*]const u16) ?HMODULE;
213180
214181pub 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 @@
11const std = @import("../../index.zig");
22const builtin = @import("builtin");
33const os = std.os;
4const unicode = std.unicode;
45const windows = std.os.windows;
56const assert = std.debug.assert;
67const mem = std.mem;
......@@ -156,41 +157,51 @@ pub fn windowsOpen(
156157}
157158
158159/// 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 {
160161 // count bytes needed
161 const bytes_needed = x: {
162 var bytes_needed: usize = 1; // 1 for the final null byte
162 const max_chars_needed = x: {
163 var max_chars_needed: usize = 1; // 1 for the final null byte
163164 var it = env_map.iterator();
164165 while (it.next()) |pair| {
165166 // +1 for '='
166167 // +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;
168169 }
169 break :x bytes_needed;
170 break :x max_chars_needed;
170171 };
171 const result = try allocator.alloc(u8, bytes_needed);
172 const result = try allocator.alloc(u16, max_chars_needed);
172173 errdefer allocator.free(result);
173174
174175 var it = env_map.iterator();
175176 var i: usize = 0;
176177 while (it.next()) |pair| {
177 mem.copy(u8, result[i..], pair.key);
178 i += pair.key.len;
178 i += try unicode.utf8ToUtf16Le(result[i..], pair.key);
179179 result[i] = '=';
180180 i += 1;
181 mem.copy(u8, result[i..], pair.value);
182 i += pair.value.len;
181 i += try unicode.utf8ToUtf16Le(result[i..], pair.value);
183182 result[i] = 0;
184183 i += 1;
185184 }
186185 result[i] = 0;
187 return result;
186 i += 1;
187 return allocator.shrink(u16, result, i);
188188}
189189
190pub fn windowsLoadDll(allocator: *mem.Allocator, dll_path: []const u8) !windows.HMODULE {
191 const padded_buff = try cstr.addNullByte(allocator, dll_path);
192 defer allocator.free(padded_buff);
193 return windows.LoadLibraryA(padded_buff.ptr) orelse error.DllNotFound;
190pub fn windowsLoadDllW(dll_path_w: [*]const u16) !windows.HMODULE {
191 return windows.LoadLibraryW(dll_path_w) orelse {
192 const err = windows.GetLastError();
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);
194205}
195206
196207pub fn windowsUnloadDll(hModule: windows.HMODULE) void {
......@@ -200,27 +211,19 @@ pub fn windowsUnloadDll(hModule: windows.HMODULE) void {
200211test "InvalidDll" {
201212 if (builtin.os != builtin.Os.windows) return error.SkipZigTest;
202213
203 const DllName = "asdf.dll";
204 const allocator = std.debug.global_allocator;
205 const handle = os.windowsLoadDll(allocator, DllName) catch |err| {
206 assert(err == error.DllNotFound);
214 const handle = os.windowsLoadDll("asdf.dll") catch |err| {
215 assert(err == error.FileNotFound);
207216 return;
208217 };
218 @panic("Expected error from function");
209219}
210220
211221pub fn windowsFindFirstFile(
212 allocator: *mem.Allocator,
213222 dir_path: []const u8,
214 find_file_data: *windows.WIN32_FIND_DATAA,
223 find_file_data: *windows.WIN32_FIND_DATAW,
215224) !windows.HANDLE {
216 const wild_and_null = []u8{ '\\', '*', 0 };
217 const path_with_wild_and_null = try allocator.alloc(u8, dir_path.len + wild_and_null.len);
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);
225 const dir_path_w = try sliceToPrefixedSuffixedFileW(dir_path, []u16{'\\', '*', 0});
226 const handle = windows.FindFirstFileW(&dir_path_w, find_file_data);
224227
225228 if (handle == windows.INVALID_HANDLE_VALUE) {
226229 const err = windows.GetLastError();
......@@ -235,8 +238,8 @@ pub fn windowsFindFirstFile(
235238}
236239
237240/// Returns `true` if there was another file, `false` otherwise.
238pub fn windowsFindNextFile(handle: windows.HANDLE, find_file_data: *windows.WIN32_FIND_DATAA) !bool {
239 if (windows.FindNextFileA(handle, find_file_data) == 0) {
241pub fn windowsFindNextFile(handle: windows.HANDLE, find_file_data: *windows.WIN32_FIND_DATAW) !bool {
242 if (windows.FindNextFileW(handle, find_file_data) == 0) {
240243 const err = windows.GetLastError();
241244 return switch (err) {
242245 windows.ERROR.NO_MORE_FILES => false,
......@@ -297,8 +300,12 @@ pub fn cStrToPrefixedFileW(s: [*]const u8) ![PATH_MAX_WIDE + 1]u16 {
297300}
298301
299302pub 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 {
300307 // 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
303310 // > File I/O functions in the Windows API convert "/" to "\" as part of
304311 // > 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 {
306313 // from https://docs.microsoft.com/en-us/windows/desktop/FileIO/naming-a-file#maximum-path-length-limitation
307314 // Because we want the larger maximum path length for absolute paths, we
308315 // disallow forward slashes in zig std lib file functions on Windows.
309 for (s) |byte|
316 for (s) |byte| {
310317 switch (byte) {
311 '/', '*', '?', '"', '<', '>', '|' => return error.BadPathName,
312 else => {},
313 };
318 '/', '*', '?', '"', '<', '>', '|' => return error.BadPathName,
319 else => {},
320 }
321 }
314322 const start_index = if (mem.startsWith(u8, s, "\\\\") or !os.path.isAbsolute(s)) 0 else blk: {
315323 const prefix = []u16{ '\\', '\\', '?', '\\' };
316324 mem.copy(u16, result[0..], prefix);
......@@ -318,7 +326,7 @@ pub fn sliceToPrefixedFileW(s: []const u8) ![PATH_MAX_WIDE + 1]u16 {
318326 };
319327 const end_index = start_index + try std.unicode.utf8ToUtf16Le(result[start_index..], s);
320328 assert(end_index <= result.len);
321 if (end_index == result.len) return error.NameTooLong;
322 result[end_index] = 0;
329 if (end_index + suffix.len > result.len) return error.NameTooLong;
330 mem.copy(u16, result[end_index..], suffix);
323331 return result;
324332}