| 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 | 581 | "os/windows/ntdll.zig" |
| 582 | 582 | "os/windows/ole32.zig" |
| 583 | 583 | "os/windows/shell32.zig" |
| 584 | "os/windows/shlwapi.zig" | |
| 585 | "os/windows/user32.zig" | |
| 586 | 584 | "os/windows/util.zig" |
| 587 | 585 | "os/zen.zig" |
| 588 | 586 | "pdb.zig" |
std/os/child_process.zig+46-12| ... | ... | @@ -1,5 +1,6 @@ |
| 1 | 1 | const std = @import("../index.zig"); |
| 2 | 2 | const cstr = std.cstr; |
| 3 | const unicode = std.unicode; | |
| 3 | 4 | const io = std.io; |
| 4 | 5 | const os = std.os; |
| 5 | 6 | const posix = os.posix; |
| ... | ... | @@ -12,6 +13,7 @@ const Buffer = std.Buffer; |
| 12 | 13 | const builtin = @import("builtin"); |
| 13 | 14 | const Os = builtin.Os; |
| 14 | 15 | const LinkedList = std.LinkedList; |
| 16 | const windows_util = @import("windows/util.zig"); | |
| 15 | 17 | |
| 16 | 18 | const is_windows = builtin.os == Os.windows; |
| 17 | 19 | |
| ... | ... | @@ -520,8 +522,8 @@ pub const ChildProcess = struct { |
| 520 | 522 | const cmd_line = try windowsCreateCommandLine(self.allocator, self.argv); |
| 521 | 523 | defer self.allocator.free(cmd_line); |
| 522 | 524 | |
| 523 | var siStartInfo = windows.STARTUPINFOA{ | |
| 524 | .cb = @sizeOf(windows.STARTUPINFOA), | |
| 525 | var siStartInfo = windows.STARTUPINFOW{ | |
| 526 | .cb = @sizeOf(windows.STARTUPINFOW), | |
| 525 | 527 | .hStdError = g_hChildStd_ERR_Wr, |
| 526 | 528 | .hStdOutput = g_hChildStd_OUT_Wr, |
| 527 | 529 | .hStdInput = g_hChildStd_IN_Rd, |
| ... | ... | @@ -545,7 +547,9 @@ pub const ChildProcess = struct { |
| 545 | 547 | |
| 546 | 548 | const cwd_slice = if (self.cwd) |cwd| try cstr.addNullByte(self.allocator, cwd) else null; |
| 547 | 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 | 554 | const maybe_envp_buf = if (self.env_map) |env_map| try os.createWindowsEnvBlock(self.allocator, env_map) else null; |
| 551 | 555 | defer if (maybe_envp_buf) |envp_buf| self.allocator.free(envp_buf); |
| ... | ... | @@ -564,7 +568,13 @@ pub const ChildProcess = struct { |
| 564 | 568 | }; |
| 565 | 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 | 578 | if (no_path_err != error.FileNotFound) return no_path_err; |
| 569 | 579 | |
| 570 | 580 | const PATH = try os.getEnvVarOwned(self.allocator, "PATH"); |
| ... | ... | @@ -575,7 +585,10 @@ pub const ChildProcess = struct { |
| 575 | 585 | const joined_path = try os.path.join(self.allocator, search_path, app_name); |
| 576 | 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 | 592 | break; |
| 580 | 593 | } else |err| if (err == error.FileNotFound) { |
| 581 | 594 | continue; |
| ... | ... | @@ -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 { | |
| 630 | if (windows.CreateProcessA(app_name, cmd_line, null, null, windows.TRUE, 0, @ptrCast(?*c_void, envp_ptr), cwd_ptr, lpStartupInfo, lpProcessInformation) == 0) { | |
| 642 | fn 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) { | |
| 631 | 664 | 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, | |
| 634 | 668 | 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 | } | |
| 638 | 672 | } |
| 639 | 673 | } |
| 640 | 674 |
std/os/index.zig+112-87| ... | ... | @@ -819,37 +819,40 @@ test "os.getCwd" { |
| 819 | 819 | |
| 820 | 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 | 824 | if (is_windows) { |
| 824 | return symLinkWindows(allocator, existing_path, new_path); | |
| 825 | return symLinkWindows(existing_path, new_path); | |
| 825 | 826 | } else { |
| 826 | return symLinkPosix(allocator, existing_path, new_path); | |
| 827 | return symLinkPosix(existing_path, new_path); | |
| 827 | 828 | } |
| 828 | 829 | } |
| 829 | 830 | |
| 830 | 831 | pub const WindowsSymLinkError = error{ |
| 831 | OutOfMemory, | |
| 832 | NameTooLong, | |
| 833 | InvalidUtf8, | |
| 834 | BadPathName, | |
| 832 | 835 | |
| 833 | 836 | /// See https://github.com/ziglang/zig/issues/1396 |
| 834 | 837 | Unexpected, |
| 835 | 838 | }; |
| 836 | 839 | |
| 837 | pub 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) { | |
| 840 | pub 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) { | |
| 844 | 842 | const err = windows.GetLastError(); |
| 845 | return switch (err) { | |
| 846 | else => unexpectedErrorWindows(err), | |
| 847 | }; | |
| 843 | switch (err) { | |
| 844 | else => return unexpectedErrorWindows(err), | |
| 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 | 855 | pub const PosixSymLinkError = error{ |
| 852 | OutOfMemory, | |
| 853 | 856 | AccessDenied, |
| 854 | 857 | DiskQuota, |
| 855 | 858 | PathAlreadyExists, |
| ... | ... | @@ -866,43 +869,40 @@ pub const PosixSymLinkError = error{ |
| 866 | 869 | Unexpected, |
| 867 | 870 | }; |
| 868 | 871 | |
| 869 | pub 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 | }; | |
| 872 | pub 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), | |
| 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 | 900 | // here we replace the standard +/ with -_ so that it can be used in a file name |
| 902 | 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 | 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 | 906 | return; |
| 907 | 907 | } else |err| switch (err) { |
| 908 | 908 | error.PathAlreadyExists => {}, |
| ... | ... | @@ -920,7 +920,7 @@ pub fn atomicSymLink(allocator: *Allocator, existing_path: []const u8, new_path: |
| 920 | 920 | try getRandomBytes(rand_buf[0..]); |
| 921 | 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 | 924 | return rename(tmp_path, new_path); |
| 925 | 925 | } else |err| switch (err) { |
| 926 | 926 | error.PathAlreadyExists => continue, |
| ... | ... | @@ -1252,49 +1252,65 @@ pub const DeleteDirError = error{ |
| 1252 | 1252 | NotDir, |
| 1253 | 1253 | DirNotEmpty, |
| 1254 | 1254 | ReadOnlyFileSystem, |
| 1255 | OutOfMemory, | |
| 1255 | InvalidUtf8, | |
| 1256 | BadPathName, | |
| 1256 | 1257 | |
| 1257 | 1258 | /// See https://github.com/ziglang/zig/issues/1396 |
| 1258 | 1259 | Unexpected, |
| 1259 | 1260 | }; |
| 1260 | 1261 | |
| 1261 | /// Returns ::error.DirNotEmpty if the directory is not empty. | |
| 1262 | /// To delete a directory recursively, see ::deleteTree | |
| 1263 | pub 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); | |
| 1262 | pub 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 | } | |
| 1266 | 1291 | |
| 1267 | mem.copy(u8, path_buf, dir_path); | |
| 1268 | path_buf[dir_path.len] = 0; | |
| 1292 | pub 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 | } | |
| 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 | 1306 | switch (builtin.os) { |
| 1271 | 1307 | 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); | |
| 1280 | 1310 | }, |
| 1281 | 1311 | 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); | |
| 1298 | 1314 | }, |
| 1299 | 1315 | else => @compileError("unimplemented"), |
| 1300 | 1316 | } |
| ... | ... | @@ -1346,6 +1362,7 @@ pub fn deleteTree(allocator: *Allocator, full_path: []const u8) DeleteTreeError! |
| 1346 | 1362 | error.IsDir => {}, |
| 1347 | 1363 | error.AccessDenied => got_access_denied = true, |
| 1348 | 1364 | |
| 1365 | error.InvalidUtf8, | |
| 1349 | 1366 | error.SymLinkLoop, |
| 1350 | 1367 | error.NameTooLong, |
| 1351 | 1368 | error.SystemResources, |
| ... | ... | @@ -1353,7 +1370,6 @@ pub fn deleteTree(allocator: *Allocator, full_path: []const u8) DeleteTreeError! |
| 1353 | 1370 | error.NotDir, |
| 1354 | 1371 | error.FileSystem, |
| 1355 | 1372 | error.FileBusy, |
| 1356 | error.InvalidUtf8, | |
| 1357 | 1373 | error.BadPathName, |
| 1358 | 1374 | error.Unexpected, |
| 1359 | 1375 | => return err, |
| ... | ... | @@ -1381,6 +1397,8 @@ pub fn deleteTree(allocator: *Allocator, full_path: []const u8) DeleteTreeError! |
| 1381 | 1397 | error.NoSpaceLeft, |
| 1382 | 1398 | error.PathAlreadyExists, |
| 1383 | 1399 | error.Unexpected, |
| 1400 | error.InvalidUtf8, | |
| 1401 | error.BadPathName, | |
| 1384 | 1402 | => return err, |
| 1385 | 1403 | }; |
| 1386 | 1404 | defer dir.close(); |
| ... | ... | @@ -1398,7 +1416,7 @@ pub fn deleteTree(allocator: *Allocator, full_path: []const u8) DeleteTreeError! |
| 1398 | 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 | 1440 | }, |
| 1423 | 1441 | Os.windows => struct { |
| 1424 | 1442 | handle: windows.HANDLE, |
| 1425 | find_file_data: windows.WIN32_FIND_DATAA, | |
| 1443 | find_file_data: windows.WIN32_FIND_DATAW, | |
| 1426 | 1444 | first: bool, |
| 1445 | name_data: [256]u8, | |
| 1427 | 1446 | }, |
| 1428 | 1447 | else => @compileError("unimplemented"), |
| 1429 | 1448 | }; |
| ... | ... | @@ -1460,6 +1479,8 @@ pub const Dir = struct { |
| 1460 | 1479 | NoSpaceLeft, |
| 1461 | 1480 | PathAlreadyExists, |
| 1462 | 1481 | OutOfMemory, |
| 1482 | InvalidUtf8, | |
| 1483 | BadPathName, | |
| 1463 | 1484 | |
| 1464 | 1485 | /// See https://github.com/ziglang/zig/issues/1396 |
| 1465 | 1486 | Unexpected, |
| ... | ... | @@ -1471,12 +1492,13 @@ pub const Dir = struct { |
| 1471 | 1492 | .allocator = allocator, |
| 1472 | 1493 | .handle = switch (builtin.os) { |
| 1473 | 1494 | 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); | |
| 1476 | 1497 | break :blk Handle{ |
| 1477 | 1498 | .handle = handle, |
| 1478 | 1499 | .find_file_data = find_file_data, // TODO guaranteed copy elision |
| 1479 | 1500 | .first = true, |
| 1501 | .name_data = undefined, | |
| 1480 | 1502 | }; |
| 1481 | 1503 | }, |
| 1482 | 1504 | Os.macosx, Os.ios => Handle{ |
| ... | ... | @@ -1591,9 +1613,12 @@ pub const Dir = struct { |
| 1591 | 1613 | if (!try windows_util.windowsFindNextFile(self.handle.handle, &self.handle.find_file_data)) |
| 1592 | 1614 | return null; |
| 1593 | 1615 | } |
| 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{'.', '.'})) | |
| 1596 | 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 | 1622 | const kind = blk: { |
| 1598 | 1623 | const attrs = self.handle.find_file_data.dwFileAttributes; |
| 1599 | 1624 | if (attrs & windows.FILE_ATTRIBUTE_DIRECTORY != 0) break :blk Entry.Kind.Directory; |
| ... | ... | @@ -1602,7 +1627,7 @@ pub const Dir = struct { |
| 1602 | 1627 | break :blk Entry.Kind.Unknown; |
| 1603 | 1628 | }; |
| 1604 | 1629 | return Entry{ |
| 1605 | .name = name, | |
| 1630 | .name = name_utf8, | |
| 1606 | 1631 | .kind = kind, |
| 1607 | 1632 | }; |
| 1608 | 1633 | } |
| ... | ... | @@ -2070,7 +2095,7 @@ fn testWindowsCmdLine(input_cmd_line: [*]const u8, expected_args: []const []cons |
| 2070 | 2095 | } |
| 2071 | 2096 | |
| 2072 | 2097 | // TODO make this a build variable that you can set |
| 2073 | const unexpected_error_tracing = true; | |
| 2098 | const unexpected_error_tracing = false; | |
| 2074 | 2099 | const UnexpectedError = error{ |
| 2075 | 2100 | /// The Operating System returned an undocumented error code. |
| 2076 | 2101 | Unexpected, |
std/os/windows/index.zig+9-9| ... | ... | @@ -6,8 +6,6 @@ pub use @import("kernel32.zig"); |
| 6 | 6 | pub use @import("ntdll.zig"); |
| 7 | 7 | pub use @import("ole32.zig"); |
| 8 | 8 | pub use @import("shell32.zig"); |
| 9 | pub use @import("shlwapi.zig"); | |
| 10 | pub use @import("user32.zig"); | |
| 11 | 9 | |
| 12 | 10 | test "import" { |
| 13 | 11 | _ = @import("util.zig"); |
| ... | ... | @@ -174,11 +172,11 @@ pub const PROCESS_INFORMATION = extern struct { |
| 174 | 172 | dwThreadId: DWORD, |
| 175 | 173 | }; |
| 176 | 174 | |
| 177 | pub const STARTUPINFOA = extern struct { | |
| 175 | pub const STARTUPINFOW = extern struct { | |
| 178 | 176 | cb: DWORD, |
| 179 | lpReserved: ?LPSTR, | |
| 180 | lpDesktop: ?LPSTR, | |
| 181 | lpTitle: ?LPSTR, | |
| 177 | lpReserved: ?LPWSTR, | |
| 178 | lpDesktop: ?LPWSTR, | |
| 179 | lpTitle: ?LPWSTR, | |
| 182 | 180 | dwX: DWORD, |
| 183 | 181 | dwY: DWORD, |
| 184 | 182 | dwXSize: DWORD, |
| ... | ... | @@ -238,7 +236,7 @@ pub const HEAP_NO_SERIALIZE = 0x00000001; |
| 238 | 236 | pub const PTHREAD_START_ROUTINE = extern fn (LPVOID) DWORD; |
| 239 | 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 | 240 | dwFileAttributes: DWORD, |
| 243 | 241 | ftCreationTime: FILETIME, |
| 244 | 242 | ftLastAccessTime: FILETIME, |
| ... | ... | @@ -247,8 +245,8 @@ pub const WIN32_FIND_DATAA = extern struct { |
| 247 | 245 | nFileSizeLow: DWORD, |
| 248 | 246 | dwReserved0: DWORD, |
| 249 | 247 | dwReserved1: DWORD, |
| 250 | cFileName: [260]CHAR, | |
| 251 | cAlternateFileName: [14]CHAR, | |
| 248 | cFileName: [260]u16, | |
| 249 | cAlternateFileName: [14]u16, | |
| 252 | 250 | }; |
| 253 | 251 | |
| 254 | 252 | pub const FILETIME = extern struct { |
| ... | ... | @@ -377,3 +375,5 @@ pub const COORD = extern struct { |
| 377 | 375 | X: SHORT, |
| 378 | 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 | 4 | |
| 5 | 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 | 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 | 9 | pub extern "kernel32" stdcallcc fn CreateFileW( |
| 21 | 10 | lpFileName: [*]const u16, // TODO null terminated pointer type |
| 22 | 11 | dwDesiredAccess: DWORD, |
| ... | ... | @@ -34,37 +23,32 @@ pub extern "kernel32" stdcallcc fn CreatePipe( |
| 34 | 23 | nSize: DWORD, |
| 35 | 24 | ) BOOL; |
| 36 | 25 | |
| 37 | pub extern "kernel32" stdcallcc fn CreateProcessA( | |
| 38 | lpApplicationName: ?LPCSTR, | |
| 39 | lpCommandLine: LPSTR, | |
| 26 | pub extern "kernel32" stdcallcc fn CreateProcessW( | |
| 27 | lpApplicationName: ?LPWSTR, | |
| 28 | lpCommandLine: LPWSTR, | |
| 40 | 29 | lpProcessAttributes: ?*SECURITY_ATTRIBUTES, |
| 41 | 30 | lpThreadAttributes: ?*SECURITY_ATTRIBUTES, |
| 42 | 31 | bInheritHandles: BOOL, |
| 43 | 32 | dwCreationFlags: DWORD, |
| 44 | 33 | lpEnvironment: ?*c_void, |
| 45 | lpCurrentDirectory: ?LPCSTR, | |
| 46 | lpStartupInfo: *STARTUPINFOA, | |
| 34 | lpCurrentDirectory: ?LPWSTR, | |
| 35 | lpStartupInfo: *STARTUPINFOW, | |
| 47 | 36 | lpProcessInformation: *PROCESS_INFORMATION, |
| 48 | 37 | ) BOOL; |
| 49 | 38 | |
| 50 | pub extern "kernel32" stdcallcc fn CreateSymbolicLinkA( | |
| 51 | lpSymlinkFileName: LPCSTR, | |
| 52 | lpTargetFileName: LPCSTR, | |
| 53 | dwFlags: DWORD, | |
| 54 | ) BOOLEAN; | |
| 39 | pub extern "kernel32" stdcallcc fn CreateSymbolicLinkW(lpSymlinkFileName: [*]const u16, lpTargetFileName: [*]const u16, dwFlags: DWORD) BOOLEAN; | |
| 55 | 40 | |
| 56 | 41 | pub extern "kernel32" stdcallcc fn CreateIoCompletionPort(FileHandle: HANDLE, ExistingCompletionPort: ?HANDLE, CompletionKey: ULONG_PTR, NumberOfConcurrentThreads: DWORD) ?HANDLE; |
| 57 | 42 | |
| 58 | 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 | 45 | pub extern "kernel32" stdcallcc fn DeleteFileW(lpFileName: [*]const u16) BOOL; |
| 62 | 46 | |
| 63 | 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 | 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 | 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 | 58 | |
| 75 | 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 | 61 | pub extern "kernel32" stdcallcc fn GetCurrentDirectoryW(nBufferLength: DWORD, lpBuffer: ?[*]WCHAR) DWORD; |
| 79 | 62 | |
| 80 | 63 | pub extern "kernel32" stdcallcc fn GetCurrentThread() HANDLE; |
| ... | ... | @@ -88,10 +71,8 @@ pub extern "kernel32" stdcallcc fn GetExitCodeProcess(hProcess: HANDLE, lpExitCo |
| 88 | 71 | |
| 89 | 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 | 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 | 76 | pub extern "kernel32" stdcallcc fn GetModuleFileNameW(hModule: ?HMODULE, lpFilename: [*]u16, nSize: DWORD) DWORD; |
| 96 | 77 | |
| 97 | 78 | pub extern "kernel32" stdcallcc fn GetModuleHandleW(lpModuleName: ?[*]const WCHAR) HMODULE; |
| ... | ... | @@ -105,13 +86,6 @@ pub extern "kernel32" stdcallcc fn GetFileInformationByHandleEx( |
| 105 | 86 | in_dwBufferSize: DWORD, |
| 106 | 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 | 89 | pub extern "kernel32" stdcallcc fn GetFinalPathNameByHandleW( |
| 116 | 90 | hFile: HANDLE, |
| 117 | 91 | lpszFilePath: [*]u16, |
| ... | ... | @@ -142,12 +116,6 @@ pub extern "kernel32" stdcallcc fn HeapFree(hHeap: HANDLE, dwFlags: DWORD, lpMem |
| 142 | 116 | |
| 143 | 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 | 119 | pub extern "kernel32" stdcallcc fn MoveFileExW( |
| 152 | 120 | lpExistingFileName: [*]const u16, |
| 153 | 121 | lpNewFileName: [*]const u16, |
| ... | ... | @@ -179,7 +147,7 @@ pub extern "kernel32" stdcallcc fn ReadFile( |
| 179 | 147 | in_out_lpOverlapped: ?*OVERLAPPED, |
| 180 | 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 | 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 | 176 | |
| 209 | 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 | |
| 212 | pub extern "kernel32" stdcallcc fn LoadLibraryA(lpLibFileName: LPCSTR) ?HMODULE; | |
| 179 | pub extern "kernel32" stdcallcc fn LoadLibraryW(lpLibFileName: [*]const u16) ?HMODULE; | |
| 213 | 180 | |
| 214 | 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 | 1 | const std = @import("../../index.zig"); |
| 2 | 2 | const builtin = @import("builtin"); |
| 3 | 3 | const os = std.os; |
| 4 | const unicode = std.unicode; | |
| 4 | 5 | const windows = std.os.windows; |
| 5 | 6 | const assert = std.debug.assert; |
| 6 | 7 | const mem = std.mem; |
| ... | ... | @@ -156,41 +157,51 @@ pub fn windowsOpen( |
| 156 | 157 | } |
| 157 | 158 | |
| 158 | 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 | 161 | // 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 | |
| 163 | 164 | var it = env_map.iterator(); |
| 164 | 165 | while (it.next()) |pair| { |
| 165 | 166 | // +1 for '=' |
| 166 | 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 | 173 | errdefer allocator.free(result); |
| 173 | 174 | |
| 174 | 175 | var it = env_map.iterator(); |
| 175 | 176 | var i: usize = 0; |
| 176 | 177 | 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); | |
| 179 | 179 | result[i] = '='; |
| 180 | 180 | i += 1; |
| 181 | mem.copy(u8, result[i..], pair.value); | |
| 182 | i += pair.value.len; | |
| 181 | i += try unicode.utf8ToUtf16Le(result[i..], pair.value); | |
| 183 | 182 | result[i] = 0; |
| 184 | 183 | i += 1; |
| 185 | 184 | } |
| 186 | 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 { | |
| 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; | |
| 190 | pub 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 | ||
| 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 | 207 | pub fn windowsUnloadDll(hModule: windows.HMODULE) void { |
| ... | ... | @@ -200,27 +211,19 @@ pub fn windowsUnloadDll(hModule: windows.HMODULE) void { |
| 200 | 211 | test "InvalidDll" { |
| 201 | 212 | if (builtin.os != builtin.Os.windows) return error.SkipZigTest; |
| 202 | 213 | |
| 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); | |
| 207 | 216 | return; |
| 208 | 217 | }; |
| 218 | @panic("Expected error from function"); | |
| 209 | 219 | } |
| 210 | 220 | |
| 211 | 221 | pub fn windowsFindFirstFile( |
| 212 | allocator: *mem.Allocator, | |
| 213 | 222 | dir_path: []const u8, |
| 214 | find_file_data: *windows.WIN32_FIND_DATAA, | |
| 223 | find_file_data: *windows.WIN32_FIND_DATAW, | |
| 215 | 224 | ) !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); | |
| 224 | 227 | |
| 225 | 228 | if (handle == windows.INVALID_HANDLE_VALUE) { |
| 226 | 229 | const err = windows.GetLastError(); |
| ... | ... | @@ -235,8 +238,8 @@ pub fn windowsFindFirstFile( |
| 235 | 238 | } |
| 236 | 239 | |
| 237 | 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 { | |
| 239 | if (windows.FindNextFileA(handle, find_file_data) == 0) { | |
| 241 | pub fn windowsFindNextFile(handle: windows.HANDLE, find_file_data: *windows.WIN32_FIND_DATAW) !bool { | |
| 242 | if (windows.FindNextFileW(handle, find_file_data) == 0) { | |
| 240 | 243 | const err = windows.GetLastError(); |
| 241 | 244 | return switch (err) { |
| 242 | 245 | windows.ERROR.NO_MORE_FILES => false, |
| ... | ... | @@ -297,8 +300,12 @@ pub fn cStrToPrefixedFileW(s: [*]const u8) ![PATH_MAX_WIDE + 1]u16 { |
| 297 | 300 | } |
| 298 | 301 | |
| 299 | 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 | 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 | 310 | // > File I/O functions in the Windows API convert "/" to "\" as part of |
| 304 | 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 | 313 | // from https://docs.microsoft.com/en-us/windows/desktop/FileIO/naming-a-file#maximum-path-length-limitation |
| 307 | 314 | // Because we want the larger maximum path length for absolute paths, we |
| 308 | 315 | // disallow forward slashes in zig std lib file functions on Windows. |
| 309 | for (s) |byte| | |
| 316 | for (s) |byte| { | |
| 310 | 317 | switch (byte) { |
| 311 | '/', '*', '?', '"', '<', '>', '|' => return error.BadPathName, | |
| 312 | else => {}, | |
| 313 | }; | |
| 318 | '/', '*', '?', '"', '<', '>', '|' => return error.BadPathName, | |
| 319 | else => {}, | |
| 320 | } | |
| 321 | } | |
| 314 | 322 | const start_index = if (mem.startsWith(u8, s, "\\\\") or !os.path.isAbsolute(s)) 0 else blk: { |
| 315 | 323 | const prefix = []u16{ '\\', '\\', '?', '\\' }; |
| 316 | 324 | mem.copy(u16, result[0..], prefix); |
| ... | ... | @@ -318,7 +326,7 @@ pub fn sliceToPrefixedFileW(s: []const u8) ![PATH_MAX_WIDE + 1]u16 { |
| 318 | 326 | }; |
| 319 | 327 | const end_index = start_index + try std.unicode.utf8ToUtf16Le(result[start_index..], s); |
| 320 | 328 | 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); | |
| 323 | 331 | return result; |
| 324 | 332 | } |