| author | |
| committer | |
| log | 92f747435930bc4d54114e414b372c7eafe7cc02 |
| tree | f525a6d2c52e1bf4336ea359dd4f055a5c2bcfe4 |
| parent | d5968086fe357aa5cf678327295677dba5102fc8 |
See #5348 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 @@ |
| 1 | const std = @import("../index.zig"); | 1 | const std = @import("../index.zig"); |
| 2 | const cstr = std.cstr; | 2 | const cstr = std.cstr; |
| 3 | const unicode = std.unicode; | ||
| 3 | const io = std.io; | 4 | const io = std.io; |
| 4 | const os = std.os; | 5 | const os = std.os; |
| 5 | const posix = os.posix; | 6 | const posix = os.posix; |
| ... | @@ -12,6 +13,7 @@ const Buffer = std.Buffer; | ... | @@ -12,6 +13,7 @@ const Buffer = std.Buffer; |
| 12 | const builtin = @import("builtin"); | 13 | const builtin = @import("builtin"); |
| 13 | const Os = builtin.Os; | 14 | const Os = builtin.Os; |
| 14 | const LinkedList = std.LinkedList; | 15 | const LinkedList = std.LinkedList; |
| 16 | const windows_util = @import("windows/util.zig"); | ||
| 15 | 17 | ||
| 16 | const is_windows = builtin.os == Os.windows; | 18 | const is_windows = builtin.os == Os.windows; |
| 17 | 19 | ||
| ... | @@ -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); |
| 522 | 524 | ||
| 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 { |
| 545 | 547 | ||
| 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; | ||
| 549 | 553 | ||
| 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); |
| 566 | 570 | ||
| 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; |
| 569 | 579 | ||
| 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); |
| 577 | 587 | ||
| 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 | }; |
| 628 | 641 | ||
| 629 | fn windowsCreateProcess(app_name: [*]u8, cmd_line: [*]u8, envp_ptr: ?[*]u8, cwd_ptr: ?[*]u8, lpStartupInfo: *windows.STARTUPINFOA, lpProcessInformation: *windows.PROCESS_INFORMATION) !void { | 642 | fn 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 | } |
| 640 | 674 |
std/os/index.zig+112-87| ... | @@ -819,37 +819,40 @@ test "os.getCwd" { | ... | @@ -819,37 +819,40 @@ test "os.getCwd" { |
| 819 | 819 | ||
| 820 | pub const SymLinkError = PosixSymLinkError || WindowsSymLinkError; | 820 | pub const SymLinkError = PosixSymLinkError || WindowsSymLinkError; |
| 821 | 821 | ||
| 822 | pub fn symLink(allocator: *Allocator, existing_path: []const u8, new_path: []const u8) SymLinkError!void { | 822 | /// TODO add a symLinkC variant |
| 823 | pub 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 | } |
| 829 | 830 | ||
| 830 | pub const WindowsSymLinkError = error{ | 831 | pub const WindowsSymLinkError = error{ |
| 831 | OutOfMemory, | 832 | NameTooLong, |
| 833 | InvalidUtf8, | ||
| 834 | BadPathName, | ||
| 832 | 835 | ||
| 833 | /// See https://github.com/ziglang/zig/issues/1396 | 836 | /// See https://github.com/ziglang/zig/issues/1396 |
| 834 | Unexpected, | 837 | Unexpected, |
| 835 | }; | 838 | }; |
| 836 | 839 | ||
| 837 | pub fn symLinkWindows(allocator: *Allocator, existing_path: []const u8, new_path: []const u8) WindowsSymLinkError!void { | 840 | pub 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 | } |
| 850 | 848 | ||
| 849 | pub 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 | |||
| 851 | pub const PosixSymLinkError = error{ | 855 | pub 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 | }; |
| 868 | 871 | ||
| 869 | pub fn symLinkPosix(allocator: *Allocator, existing_path: []const u8, new_path: []const u8) PosixSymLinkError!void { | 872 | pub 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) { |
| 872 | 875 | 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, |
| 876 | 879 | 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, |
| 880 | 883 | 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 | } |
| 900 | 893 | ||
| 894 | pub 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 name | 900 | // here we replace the standard +/ with -_ so that it can be used in a file name |
| 902 | const b64_fs_encoder = base64.Base64Encoder.init("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_", base64.standard_pad_char); | 901 | const b64_fs_encoder = base64.Base64Encoder.init("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_", base64.standard_pad_char); |
| 903 | 902 | ||
| 903 | /// TODO remove the allocator requirement from this API | ||
| 904 | pub fn atomicSymLink(allocator: *Allocator, existing_path: []const u8, new_path: []const u8) !void { | 904 | pub 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); |
| 922 | 922 | ||
| 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, | ||
| 1256 | 1257 | ||
| 1257 | /// See https://github.com/ziglang/zig/issues/1396 | 1258 | /// See https://github.com/ziglang/zig/issues/1396 |
| 1258 | Unexpected, | 1259 | Unexpected, |
| 1259 | }; | 1260 | }; |
| 1260 | 1261 | ||
| 1261 | /// Returns ::error.DirNotEmpty if the directory is not empty. | 1262 | pub fn deleteDirC(dir_path: [*]const u8) DeleteDirError!void { |
| 1262 | /// To delete a directory recursively, see ::deleteTree | 1263 | switch (builtin.os) { |
| 1263 | pub 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 | } | ||
| 1266 | 1291 | ||
| 1267 | mem.copy(u8, path_buf, dir_path); | 1292 | pub 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 | } | ||
| 1269 | 1302 | ||
| 1303 | /// Returns ::error.DirNotEmpty if the directory is not empty. | ||
| 1304 | /// To delete a directory recursively, see ::deleteTree | ||
| 1305 | pub 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, |
| 1348 | 1364 | ||
| 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 | } |
| 1404 | 1422 | ||
| ... | @@ -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, | ||
| 1463 | 1484 | ||
| 1464 | /// See https://github.com/ziglang/zig/issues/1396 | 1485 | /// 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 elision | 1499 | .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 | } |
| 2071 | 2096 | ||
| 2072 | // TODO make this a build variable that you can set | 2097 | // TODO make this a build variable that you can set |
| 2073 | const unexpected_error_tracing = true; | 2098 | const unexpected_error_tracing = false; |
| 2074 | const UnexpectedError = error{ | 2099 | const 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"); |
| 6 | pub use @import("ntdll.zig"); | 6 | pub use @import("ntdll.zig"); |
| 7 | pub use @import("ole32.zig"); | 7 | pub use @import("ole32.zig"); |
| 8 | pub use @import("shell32.zig"); | 8 | pub use @import("shell32.zig"); |
| 9 | pub use @import("shlwapi.zig"); | ||
| 10 | pub use @import("user32.zig"); | ||
| 11 | 9 | ||
| 12 | test "import" { | 10 | test "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 | }; |
| 176 | 174 | ||
| 177 | pub const STARTUPINFOA = extern struct { | 175 | pub 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; |
| 238 | pub const PTHREAD_START_ROUTINE = extern fn (LPVOID) DWORD; | 236 | pub const PTHREAD_START_ROUTINE = extern fn (LPVOID) DWORD; |
| 239 | pub const LPTHREAD_START_ROUTINE = PTHREAD_START_ROUTINE; | 237 | pub const LPTHREAD_START_ROUTINE = PTHREAD_START_ROUTINE; |
| 240 | 238 | ||
| 241 | pub const WIN32_FIND_DATAA = extern struct { | 239 | pub 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 | }; |
| 253 | 251 | ||
| 254 | pub const FILETIME = extern struct { | 252 | pub 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 | |||
| 379 | pub 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 |
| 4 | 4 | ||
| 5 | pub extern "kernel32" stdcallcc fn CloseHandle(hObject: HANDLE) BOOL; | 5 | pub extern "kernel32" stdcallcc fn CloseHandle(hObject: HANDLE) BOOL; |
| 6 | 6 | ||
| 7 | pub extern "kernel32" stdcallcc fn CreateDirectoryA(lpPathName: [*]const u8, lpSecurityAttributes: ?*SECURITY_ATTRIBUTES) BOOL; | ||
| 8 | pub extern "kernel32" stdcallcc fn CreateDirectoryW(lpPathName: [*]const u16, lpSecurityAttributes: ?*SECURITY_ATTRIBUTES) BOOL; | 7 | pub extern "kernel32" stdcallcc fn CreateDirectoryW(lpPathName: [*]const u16, lpSecurityAttributes: ?*SECURITY_ATTRIBUTES) BOOL; |
| 9 | 8 | ||
| 10 | pub 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 | |||
| 20 | pub extern "kernel32" stdcallcc fn CreateFileW( | 9 | pub extern "kernel32" stdcallcc fn CreateFileW( |
| 21 | lpFileName: [*]const u16, // TODO null terminated pointer type | 10 | 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; |
| 36 | 25 | ||
| 37 | pub extern "kernel32" stdcallcc fn CreateProcessA( | 26 | pub 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; |
| 49 | 38 | ||
| 50 | pub extern "kernel32" stdcallcc fn CreateSymbolicLinkA( | 39 | pub 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; | ||
| 55 | 40 | ||
| 56 | pub extern "kernel32" stdcallcc fn CreateIoCompletionPort(FileHandle: HANDLE, ExistingCompletionPort: ?HANDLE, CompletionKey: ULONG_PTR, NumberOfConcurrentThreads: DWORD) ?HANDLE; | 41 | pub extern "kernel32" stdcallcc fn CreateIoCompletionPort(FileHandle: HANDLE, ExistingCompletionPort: ?HANDLE, CompletionKey: ULONG_PTR, NumberOfConcurrentThreads: DWORD) ?HANDLE; |
| 57 | 42 | ||
| 58 | pub extern "kernel32" stdcallcc fn CreateThread(lpThreadAttributes: ?LPSECURITY_ATTRIBUTES, dwStackSize: SIZE_T, lpStartAddress: LPTHREAD_START_ROUTINE, lpParameter: ?LPVOID, dwCreationFlags: DWORD, lpThreadId: ?LPDWORD) ?HANDLE; | 43 | pub extern "kernel32" stdcallcc fn CreateThread(lpThreadAttributes: ?LPSECURITY_ATTRIBUTES, dwStackSize: SIZE_T, lpStartAddress: LPTHREAD_START_ROUTINE, lpParameter: ?LPVOID, dwCreationFlags: DWORD, lpThreadId: ?LPDWORD) ?HANDLE; |
| 59 | 44 | ||
| 60 | pub extern "kernel32" stdcallcc fn DeleteFileA(lpFileName: [*]const u8) BOOL; | ||
| 61 | pub extern "kernel32" stdcallcc fn DeleteFileW(lpFileName: [*]const u16) BOOL; | 45 | pub extern "kernel32" stdcallcc fn DeleteFileW(lpFileName: [*]const u16) BOOL; |
| 62 | 46 | ||
| 63 | pub extern "kernel32" stdcallcc fn ExitProcess(exit_code: UINT) noreturn; | 47 | pub extern "kernel32" stdcallcc fn ExitProcess(exit_code: UINT) noreturn; |
| 64 | 48 | ||
| 65 | pub extern "kernel32" stdcallcc fn FindFirstFileA(lpFileName: LPCSTR, lpFindFileData: *WIN32_FIND_DATAA) HANDLE; | 49 | pub extern "kernel32" stdcallcc fn FindFirstFileW(lpFileName: [*]const u16, lpFindFileData: *WIN32_FIND_DATAW) HANDLE; |
| 66 | pub extern "kernel32" stdcallcc fn FindClose(hFindFile: HANDLE) BOOL; | 50 | pub extern "kernel32" stdcallcc fn FindClose(hFindFile: HANDLE) BOOL; |
| 67 | pub extern "kernel32" stdcallcc fn FindNextFileA(hFindFile: HANDLE, lpFindFileData: *WIN32_FIND_DATAA) BOOL; | 51 | pub extern "kernel32" stdcallcc fn FindNextFileW(hFindFile: HANDLE, lpFindFileData: *WIN32_FIND_DATAW) BOOL; |
| 68 | 52 | ||
| 69 | pub extern "kernel32" stdcallcc fn FreeEnvironmentStringsA(penv: [*]u8) BOOL; | 53 | pub extern "kernel32" stdcallcc fn FreeEnvironmentStringsA(penv: [*]u8) BOOL; |
| 70 | 54 | ||
| ... | @@ -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 |
| 74 | 58 | ||
| 75 | pub extern "kernel32" stdcallcc fn GetConsoleScreenBufferInfo(hConsoleOutput: HANDLE, lpConsoleScreenBufferInfo: *CONSOLE_SCREEN_BUFFER_INFO) BOOL; | 59 | pub extern "kernel32" stdcallcc fn GetConsoleScreenBufferInfo(hConsoleOutput: HANDLE, lpConsoleScreenBufferInfo: *CONSOLE_SCREEN_BUFFER_INFO) BOOL; |
| 76 | 60 | ||
| 77 | pub extern "kernel32" stdcallcc fn GetCurrentDirectoryA(nBufferLength: DWORD, lpBuffer: ?[*]CHAR) DWORD; | ||
| 78 | pub extern "kernel32" stdcallcc fn GetCurrentDirectoryW(nBufferLength: DWORD, lpBuffer: ?[*]WCHAR) DWORD; | 61 | pub extern "kernel32" stdcallcc fn GetCurrentDirectoryW(nBufferLength: DWORD, lpBuffer: ?[*]WCHAR) DWORD; |
| 79 | 62 | ||
| 80 | pub extern "kernel32" stdcallcc fn GetCurrentThread() HANDLE; | 63 | pub 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 |
| 88 | 71 | ||
| 89 | pub extern "kernel32" stdcallcc fn GetFileSizeEx(hFile: HANDLE, lpFileSize: *LARGE_INTEGER) BOOL; | 72 | pub extern "kernel32" stdcallcc fn GetFileSizeEx(hFile: HANDLE, lpFileSize: *LARGE_INTEGER) BOOL; |
| 90 | 73 | ||
| 91 | pub extern "kernel32" stdcallcc fn GetFileAttributesA(lpFileName: [*]const CHAR) DWORD; | ||
| 92 | pub extern "kernel32" stdcallcc fn GetFileAttributesW(lpFileName: [*]const WCHAR) DWORD; | 74 | pub extern "kernel32" stdcallcc fn GetFileAttributesW(lpFileName: [*]const WCHAR) DWORD; |
| 93 | 75 | ||
| 94 | pub extern "kernel32" stdcallcc fn GetModuleFileNameA(hModule: ?HMODULE, lpFilename: [*]u8, nSize: DWORD) DWORD; | ||
| 95 | pub extern "kernel32" stdcallcc fn GetModuleFileNameW(hModule: ?HMODULE, lpFilename: [*]u16, nSize: DWORD) DWORD; | 76 | pub extern "kernel32" stdcallcc fn GetModuleFileNameW(hModule: ?HMODULE, lpFilename: [*]u16, nSize: DWORD) DWORD; |
| 96 | 77 | ||
| 97 | pub extern "kernel32" stdcallcc fn GetModuleHandleW(lpModuleName: ?[*]const WCHAR) HMODULE; | 78 | pub 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; |
| 107 | 88 | ||
| 108 | pub extern "kernel32" stdcallcc fn GetFinalPathNameByHandleA( | ||
| 109 | hFile: HANDLE, | ||
| 110 | lpszFilePath: LPSTR, | ||
| 111 | cchFilePath: DWORD, | ||
| 112 | dwFlags: DWORD, | ||
| 113 | ) DWORD; | ||
| 114 | |||
| 115 | pub extern "kernel32" stdcallcc fn GetFinalPathNameByHandleW( | 89 | pub 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 |
| 142 | 116 | ||
| 143 | pub extern "kernel32" stdcallcc fn HeapValidate(hHeap: HANDLE, dwFlags: DWORD, lpMem: ?*const c_void) BOOL; | 117 | pub extern "kernel32" stdcallcc fn HeapValidate(hHeap: HANDLE, dwFlags: DWORD, lpMem: ?*const c_void) BOOL; |
| 144 | 118 | ||
| 145 | pub extern "kernel32" stdcallcc fn MoveFileExA( | ||
| 146 | lpExistingFileName: [*]const u8, | ||
| 147 | lpNewFileName: [*]const u8, | ||
| 148 | dwFlags: DWORD, | ||
| 149 | ) BOOL; | ||
| 150 | |||
| 151 | pub extern "kernel32" stdcallcc fn MoveFileExW( | 119 | pub 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; |
| 181 | 149 | ||
| 182 | pub extern "kernel32" stdcallcc fn RemoveDirectoryA(lpPathName: LPCSTR) BOOL; | 150 | pub extern "kernel32" stdcallcc fn RemoveDirectoryW(lpPathName: [*]const u16) BOOL; |
| 183 | 151 | ||
| 184 | pub extern "kernel32" stdcallcc fn SetConsoleTextAttribute(hConsoleOutput: HANDLE, wAttributes: WORD) BOOL; | 152 | pub extern "kernel32" stdcallcc fn SetConsoleTextAttribute(hConsoleOutput: HANDLE, wAttributes: WORD) BOOL; |
| 185 | 153 | ||
| ... | @@ -208,8 +176,7 @@ pub extern "kernel32" stdcallcc fn WriteFile( | ... | @@ -208,8 +176,7 @@ pub extern "kernel32" stdcallcc fn WriteFile( |
| 208 | 176 | ||
| 209 | pub extern "kernel32" stdcallcc fn WriteFileEx(hFile: HANDLE, lpBuffer: [*]const u8, nNumberOfBytesToWrite: DWORD, lpOverlapped: LPOVERLAPPED, lpCompletionRoutine: LPOVERLAPPED_COMPLETION_ROUTINE) BOOL; | 177 | pub extern "kernel32" stdcallcc fn WriteFileEx(hFile: HANDLE, lpBuffer: [*]const u8, nNumberOfBytesToWrite: DWORD, lpOverlapped: LPOVERLAPPED, lpCompletionRoutine: LPOVERLAPPED_COMPLETION_ROUTINE) BOOL; |
| 210 | 178 | ||
| 211 | //TODO: call unicode versions instead of relying on ANSI code page | 179 | pub extern "kernel32" stdcallcc fn LoadLibraryW(lpLibFileName: [*]const u16) ?HMODULE; |
| 212 | pub extern "kernel32" stdcallcc fn LoadLibraryA(lpLibFileName: LPCSTR) ?HMODULE; | ||
| 213 | 180 | ||
| 214 | pub extern "kernel32" stdcallcc fn FreeLibrary(hModule: HMODULE) BOOL; | 181 | pub extern "kernel32" stdcallcc fn FreeLibrary(hModule: HMODULE) BOOL; |
| 215 | 182 |
std/os/windows/shlwapi.zig deleted-4| ... | @@ -1,4 +0,0 @@ | ||
| 1 | use @import("index.zig"); | ||
| 2 | |||
| 3 | pub extern "shlwapi" stdcallcc fn PathFileExistsA(pszPath: ?LPCTSTR) BOOL; | ||
| 4 | |||
std/os/windows/user32.zig deleted-4| ... | @@ -1,4 +0,0 @@ | ||
| 1 | use @import("index.zig"); | ||
| 2 | |||
| 3 | pub 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 @@ |
| 1 | const std = @import("../../index.zig"); | 1 | const std = @import("../../index.zig"); |
| 2 | const builtin = @import("builtin"); | 2 | const builtin = @import("builtin"); |
| 3 | const os = std.os; | 3 | const os = std.os; |
| 4 | const unicode = std.unicode; | ||
| 4 | const windows = std.os.windows; | 5 | const windows = std.os.windows; |
| 5 | const assert = std.debug.assert; | 6 | const assert = std.debug.assert; |
| 6 | const mem = std.mem; | 7 | const mem = std.mem; |
| ... | @@ -156,41 +157,51 @@ pub fn windowsOpen( | ... | @@ -156,41 +157,51 @@ pub fn windowsOpen( |
| 156 | } | 157 | } |
| 157 | 158 | ||
| 158 | /// Caller must free result. | 159 | /// Caller must free result. |
| 159 | pub fn createWindowsEnvBlock(allocator: *mem.Allocator, env_map: *const BufMap) ![]u8 { | 160 | pub fn createWindowsEnvBlock(allocator: *mem.Allocator, env_map: *const BufMap) ![]u16 { |
| 160 | // count bytes needed | 161 | // 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 byte | 163 | 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 byte | 167 | // +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); |
| 173 | 174 | ||
| 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 | } |
| 189 | 189 | ||
| 190 | pub fn windowsLoadDll(allocator: *mem.Allocator, dll_path: []const u8) !windows.HMODULE { | 190 | pub 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 | |||
| 202 | pub 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 | } |
| 195 | 206 | ||
| 196 | pub fn windowsUnloadDll(hModule: windows.HMODULE) void { | 207 | pub 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 { |
| 200 | test "InvalidDll" { | 211 | test "InvalidDll" { |
| 201 | if (builtin.os != builtin.Os.windows) return error.SkipZigTest; | 212 | if (builtin.os != builtin.Os.windows) return error.SkipZigTest; |
| 202 | 213 | ||
| 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 | } |
| 210 | 220 | ||
| 211 | pub fn windowsFindFirstFile( | 221 | pub 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); | ||
| 224 | 227 | ||
| 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 | } |
| 236 | 239 | ||
| 237 | /// Returns `true` if there was another file, `false` otherwise. | 240 | /// Returns `true` if there was another file, `false` otherwise. |
| 238 | pub fn windowsFindNextFile(handle: windows.HANDLE, find_file_data: *windows.WIN32_FIND_DATAA) !bool { | 241 | pub 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 | } |
| 298 | 301 | ||
| 299 | pub fn sliceToPrefixedFileW(s: []const u8) ![PATH_MAX_WIDE + 1]u16 { | 302 | pub fn sliceToPrefixedFileW(s: []const u8) ![PATH_MAX_WIDE + 1]u16 { |
| 303 | return sliceToPrefixedSuffixedFileW(s, []u16{0}); | ||
| 304 | } | ||
| 305 | |||
| 306 | pub fn sliceToPrefixedSuffixedFileW(s: []const u8, comptime suffix: []const u16) ![PATH_MAX_WIDE + suffix.len]u16 { | ||
| 300 | // TODO well defined copy elision | 307 | // TODO well defined copy elision |
| 301 | var result: [PATH_MAX_WIDE + 1]u16 = undefined; | 308 | var result: [PATH_MAX_WIDE + suffix.len]u16 = undefined; |
| 302 | 309 | ||
| 303 | // > File I/O functions in the Windows API convert "/" to "\" as part of | 310 | // > 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-limitation | 313 | // 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, we | 314 | // 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 | } |