| author | |
| committer | |
| log | 753e71e2f57d488c055bdd34e0f591b62fb8a7bd |
| tree | f875a5723cf9daad4205ba8918f0e2d22038ad26 |
| parent | e5454ff780ae4571cfa71a2edb6f4287eb8cf4de |
10 files changed, 841 insertions(+), 981 deletions(-)
lib/std/Build/Watch.zig+6-11| ... | @@ -366,15 +366,7 @@ const Os = switch (builtin.os.tag) { | ... | @@ -366,15 +366,7 @@ const Os = switch (builtin.os.tag) { |
| 366 | .MaximumLength = @intCast(path_len_bytes), | 366 | .MaximumLength = @intCast(path_len_bytes), |
| 367 | .Buffer = @constCast(sub_path_w.span().ptr), | 367 | .Buffer = @constCast(sub_path_w.span().ptr), |
| 368 | }; | 368 | }; |
| 369 | var attr = windows.OBJECT_ATTRIBUTES{ | 369 | var iosb: windows.IO_STATUS_BLOCK = undefined; |
| 370 | .Length = @sizeOf(windows.OBJECT_ATTRIBUTES), | ||
| 371 | .RootDirectory = if (std.fs.path.isAbsoluteWindowsW(sub_path_w.span())) null else root_fd, | ||
| 372 | .Attributes = .{}, | ||
| 373 | .ObjectName = &nt_name, | ||
| 374 | .SecurityDescriptor = null, | ||
| 375 | .SecurityQualityOfService = null, | ||
| 376 | }; | ||
| 377 | var io: windows.IO_STATUS_BLOCK = undefined; | ||
| 378 | 370 | ||
| 379 | switch (windows.ntdll.NtCreateFile( | 371 | switch (windows.ntdll.NtCreateFile( |
| 380 | &dir_handle, | 372 | &dir_handle, |
| ... | @@ -385,8 +377,11 @@ const Os = switch (builtin.os.tag) { | ... | @@ -385,8 +377,11 @@ const Os = switch (builtin.os.tag) { |
| 385 | .STANDARD = .{ .SYNCHRONIZE = true }, | 377 | .STANDARD = .{ .SYNCHRONIZE = true }, |
| 386 | .GENERIC = .{ .READ = true }, | 378 | .GENERIC = .{ .READ = true }, |
| 387 | }, | 379 | }, |
| 388 | &attr, | 380 | &.{ |
| 389 | &io, | 381 | .RootDirectory = if (std.fs.path.isAbsoluteWindowsW(sub_path_w.span())) null else root_fd, |
| 382 | .ObjectName = &nt_name, | ||
| 383 | }, | ||
| 384 | &iosb, | ||
| 390 | null, | 385 | null, |
| 391 | .{}, | 386 | .{}, |
| 392 | .VALID_FLAGS, | 387 | .VALID_FLAGS, |
lib/std/Io/Threaded.zig+356-338| ... | @@ -73,6 +73,7 @@ environ: Environ, | ... | @@ -73,6 +73,7 @@ environ: Environ, |
| 73 | 73 | ||
| 74 | null_file: NullFile = .{}, | 74 | null_file: NullFile = .{}, |
| 75 | random_file: RandomFile = .{}, | 75 | random_file: RandomFile = .{}, |
| 76 | pipe_file: PipeFile = .{}, | ||
| 76 | 77 | ||
| 77 | csprng: Csprng = .{}, | 78 | csprng: Csprng = .{}, |
| 78 | 79 | ||
| ... | @@ -118,7 +119,7 @@ pub const Argv0 = switch (native_os) { | ... | @@ -118,7 +119,7 @@ pub const Argv0 = switch (native_os) { |
| 118 | 119 | ||
| 119 | const Environ = struct { | 120 | const Environ = struct { |
| 120 | /// Unmodified data directly from the OS. | 121 | /// Unmodified data directly from the OS. |
| 121 | process_environ: process.Environ = .empty, | 122 | process_environ: process.Environ, |
| 122 | /// Protected by `mutex`. Determines whether the other fields have been | 123 | /// Protected by `mutex`. Determines whether the other fields have been |
| 123 | /// memoized based on `process_environ`. | 124 | /// memoized based on `process_environ`. |
| 124 | initialized: bool = false, | 125 | initialized: bool = false, |
| ... | @@ -190,6 +191,24 @@ pub const RandomFile = switch (native_os) { | ... | @@ -190,6 +191,24 @@ pub const RandomFile = switch (native_os) { |
| 190 | }, | 191 | }, |
| 191 | }; | 192 | }; |
| 192 | 193 | ||
| 194 | pub const PipeFile = switch (native_os) { | ||
| 195 | .windows => struct { | ||
| 196 | handle: ?windows.HANDLE = null, | ||
| 197 | |||
| 198 | fn deinit(this: *@This()) void { | ||
| 199 | if (this.handle) |handle| { | ||
| 200 | windows.CloseHandle(handle); | ||
| 201 | this.handle = null; | ||
| 202 | } | ||
| 203 | } | ||
| 204 | }, | ||
| 205 | else => struct { | ||
| 206 | fn deinit(this: @This()) void { | ||
| 207 | _ = this; | ||
| 208 | } | ||
| 209 | }, | ||
| 210 | }; | ||
| 211 | |||
| 193 | pub const Pid = if (native_os == .linux) enum(posix.pid_t) { | 212 | pub const Pid = if (native_os == .linux) enum(posix.pid_t) { |
| 194 | unknown = 0, | 213 | unknown = 0, |
| 195 | _, | 214 | _, |
| ... | @@ -1467,7 +1486,9 @@ pub const init_single_threaded: Threaded = .{ | ... | @@ -1467,7 +1486,9 @@ pub const init_single_threaded: Threaded = .{ |
| 1467 | .old_sig_pipe = undefined, | 1486 | .old_sig_pipe = undefined, |
| 1468 | .have_signal_handler = false, | 1487 | .have_signal_handler = false, |
| 1469 | .argv0 = .empty, | 1488 | .argv0 = .empty, |
| 1470 | .environ = .{}, | 1489 | .environ = .{ .process_environ = .{ |
| 1490 | .block = if (process.Environ.Block == process.Environ.GlobalBlock) .global else .empty, | ||
| 1491 | } }, | ||
| 1471 | .worker_threads = .init(null), | 1492 | .worker_threads = .init(null), |
| 1472 | .disable_memory_mapping = false, | 1493 | .disable_memory_mapping = false, |
| 1473 | }; | 1494 | }; |
| ... | @@ -1502,6 +1523,7 @@ pub fn deinit(t: *Threaded) void { | ... | @@ -1502,6 +1523,7 @@ pub fn deinit(t: *Threaded) void { |
| 1502 | } | 1523 | } |
| 1503 | t.null_file.deinit(); | 1524 | t.null_file.deinit(); |
| 1504 | t.random_file.deinit(); | 1525 | t.random_file.deinit(); |
| 1526 | t.pipe_file.deinit(); | ||
| 1505 | t.* = undefined; | 1527 | t.* = undefined; |
| 1506 | } | 1528 | } |
| 1507 | 1529 | ||
| ... | @@ -1544,14 +1566,7 @@ fn worker(t: *Threaded) void { | ... | @@ -1544,14 +1566,7 @@ fn worker(t: *Threaded) void { |
| 1544 | }, | 1566 | }, |
| 1545 | }, | 1567 | }, |
| 1546 | }, | 1568 | }, |
| 1547 | &.{ | 1569 | &.{ .ObjectName = null }, |
| 1548 | .Length = @sizeOf(windows.OBJECT_ATTRIBUTES), | ||
| 1549 | .RootDirectory = null, | ||
| 1550 | .ObjectName = null, | ||
| 1551 | .Attributes = .{}, | ||
| 1552 | .SecurityDescriptor = null, | ||
| 1553 | .SecurityQualityOfService = null, | ||
| 1554 | }, | ||
| 1555 | &windows.teb().ClientId, | 1570 | &windows.teb().ClientId, |
| 1556 | ) == .SUCCESS); | 1571 | ) == .SUCCESS); |
| 1557 | } | 1572 | } |
| ... | @@ -3288,12 +3303,8 @@ fn dirCreateDirPathOpenWindows( | ... | @@ -3288,12 +3303,8 @@ fn dirCreateDirPathOpenWindows( |
| 3288 | }, | 3303 | }, |
| 3289 | }, | 3304 | }, |
| 3290 | &.{ | 3305 | &.{ |
| 3291 | .Length = @sizeOf(w.OBJECT_ATTRIBUTES), | ||
| 3292 | .RootDirectory = if (Dir.path.isAbsoluteWindowsWtf16(sub_path_w)) null else dir.handle, | 3306 | .RootDirectory = if (Dir.path.isAbsoluteWindowsWtf16(sub_path_w)) null else dir.handle, |
| 3293 | .Attributes = .{}, | ||
| 3294 | .ObjectName = &nt_name, | 3307 | .ObjectName = &nt_name, |
| 3295 | .SecurityDescriptor = null, | ||
| 3296 | .SecurityQualityOfService = null, | ||
| 3297 | }, | 3308 | }, |
| 3298 | &io_status_block, | 3309 | &io_status_block, |
| 3299 | null, | 3310 | null, |
| ... | @@ -3975,13 +3986,9 @@ fn dirAccessWindows( | ... | @@ -3975,13 +3986,9 @@ fn dirAccessWindows( |
| 3975 | .MaximumLength = path_len_bytes, | 3986 | .MaximumLength = path_len_bytes, |
| 3976 | .Buffer = @constCast(sub_path_w.ptr), | 3987 | .Buffer = @constCast(sub_path_w.ptr), |
| 3977 | }; | 3988 | }; |
| 3978 | var attr: windows.OBJECT_ATTRIBUTES = .{ | 3989 | const attr: windows.OBJECT_ATTRIBUTES = .{ |
| 3979 | .Length = @sizeOf(windows.OBJECT_ATTRIBUTES), | ||
| 3980 | .RootDirectory = if (Dir.path.isAbsoluteWindowsWtf16(sub_path_w)) null else dir.handle, | 3990 | .RootDirectory = if (Dir.path.isAbsoluteWindowsWtf16(sub_path_w)) null else dir.handle, |
| 3981 | .Attributes = .{}, | ||
| 3982 | .ObjectName = &nt_name, | 3991 | .ObjectName = &nt_name, |
| 3983 | .SecurityDescriptor = null, | ||
| 3984 | .SecurityQualityOfService = null, | ||
| 3985 | }; | 3992 | }; |
| 3986 | var basic_info: windows.FILE.BASIC_INFORMATION = undefined; | 3993 | var basic_info: windows.FILE.BASIC_INFORMATION = undefined; |
| 3987 | const syscall: Syscall = try .start(); | 3994 | const syscall: Syscall = try .start(); |
| ... | @@ -4197,14 +4204,8 @@ fn dirCreateFileWindows( | ... | @@ -4197,14 +4204,8 @@ fn dirCreateFileWindows( |
| 4197 | .Buffer = @constCast(sub_path_w.ptr), | 4204 | .Buffer = @constCast(sub_path_w.ptr), |
| 4198 | }; | 4205 | }; |
| 4199 | const attr: windows.OBJECT_ATTRIBUTES = .{ | 4206 | const attr: windows.OBJECT_ATTRIBUTES = .{ |
| 4200 | .Length = @sizeOf(windows.OBJECT_ATTRIBUTES), | ||
| 4201 | .RootDirectory = if (Dir.path.isAbsoluteWindowsWtf16(sub_path_w)) null else dir.handle, | 4207 | .RootDirectory = if (Dir.path.isAbsoluteWindowsWtf16(sub_path_w)) null else dir.handle, |
| 4202 | .Attributes = .{ | ||
| 4203 | .INHERIT = false, | ||
| 4204 | }, | ||
| 4205 | .ObjectName = &nt_name, | 4208 | .ObjectName = &nt_name, |
| 4206 | .SecurityDescriptor = null, | ||
| 4207 | .SecurityQualityOfService = null, | ||
| 4208 | }; | 4209 | }; |
| 4209 | const create_disposition: windows.FILE.CREATE_DISPOSITION = if (flags.exclusive) | 4210 | const create_disposition: windows.FILE.CREATE_DISPOSITION = if (flags.exclusive) |
| 4210 | .CREATE | 4211 | .CREATE |
| ... | @@ -4811,17 +4812,6 @@ pub fn dirOpenFileWtf16( | ... | @@ -4811,17 +4812,6 @@ pub fn dirOpenFileWtf16( |
| 4811 | .MaximumLength = path_len_bytes, | 4812 | .MaximumLength = path_len_bytes, |
| 4812 | .Buffer = @constCast(sub_path_w.ptr), | 4813 | .Buffer = @constCast(sub_path_w.ptr), |
| 4813 | }; | 4814 | }; |
| 4814 | var attr: w.OBJECT_ATTRIBUTES = .{ | ||
| 4815 | .Length = @sizeOf(w.OBJECT_ATTRIBUTES), | ||
| 4816 | .RootDirectory = dir_handle, | ||
| 4817 | .Attributes = .{ | ||
| 4818 | // TODO should we set INHERIT=false? | ||
| 4819 | //.INHERIT = false, | ||
| 4820 | }, | ||
| 4821 | .ObjectName = &nt_name, | ||
| 4822 | .SecurityDescriptor = null, | ||
| 4823 | .SecurityQualityOfService = null, | ||
| 4824 | }; | ||
| 4825 | var io_status_block: w.IO_STATUS_BLOCK = undefined; | 4815 | var io_status_block: w.IO_STATUS_BLOCK = undefined; |
| 4826 | 4816 | ||
| 4827 | // There are multiple kernel bugs being worked around with retries. | 4817 | // There are multiple kernel bugs being worked around with retries. |
| ... | @@ -4840,7 +4830,10 @@ pub fn dirOpenFileWtf16( | ... | @@ -4840,7 +4830,10 @@ pub fn dirOpenFileWtf16( |
| 4840 | .WRITE = flags.isWrite(), | 4830 | .WRITE = flags.isWrite(), |
| 4841 | }, | 4831 | }, |
| 4842 | }, | 4832 | }, |
| 4843 | &attr, | 4833 | &.{ |
| 4834 | .RootDirectory = dir_handle, | ||
| 4835 | .ObjectName = &nt_name, | ||
| 4836 | }, | ||
| 4844 | &io_status_block, | 4837 | &io_status_block, |
| 4845 | null, | 4838 | null, |
| 4846 | .{ .NORMAL = true }, | 4839 | .{ .NORMAL = true }, |
| ... | @@ -5202,12 +5195,8 @@ pub fn dirOpenDirWindows( | ... | @@ -5202,12 +5195,8 @@ pub fn dirOpenDirWindows( |
| 5202 | }, | 5195 | }, |
| 5203 | }, | 5196 | }, |
| 5204 | &.{ | 5197 | &.{ |
| 5205 | .Length = @sizeOf(w.OBJECT_ATTRIBUTES), | ||
| 5206 | .RootDirectory = if (Dir.path.isAbsoluteWindowsWtf16(sub_path_w)) null else dir.handle, | 5198 | .RootDirectory = if (Dir.path.isAbsoluteWindowsWtf16(sub_path_w)) null else dir.handle, |
| 5207 | .Attributes = .{}, | ||
| 5208 | .ObjectName = &nt_name, | 5199 | .ObjectName = &nt_name, |
| 5209 | .SecurityDescriptor = null, | ||
| 5210 | .SecurityQualityOfService = null, | ||
| 5211 | }, | 5200 | }, |
| 5212 | &io_status_block, | 5201 | &io_status_block, |
| 5213 | null, | 5202 | null, |
| ... | @@ -6417,12 +6406,8 @@ fn dirDeleteWindows(userdata: ?*anyopaque, dir: Dir, sub_path: []const u8, remov | ... | @@ -6417,12 +6406,8 @@ fn dirDeleteWindows(userdata: ?*anyopaque, dir: Dir, sub_path: []const u8, remov |
| 6417 | .SYNCHRONIZE = true, | 6406 | .SYNCHRONIZE = true, |
| 6418 | } }, | 6407 | } }, |
| 6419 | &.{ | 6408 | &.{ |
| 6420 | .Length = @sizeOf(w.OBJECT_ATTRIBUTES), | ||
| 6421 | .RootDirectory = if (Dir.path.isAbsoluteWindowsWtf16(sub_path_w)) null else dir.handle, | 6409 | .RootDirectory = if (Dir.path.isAbsoluteWindowsWtf16(sub_path_w)) null else dir.handle, |
| 6422 | .Attributes = .{}, | ||
| 6423 | .ObjectName = &nt_name, | 6410 | .ObjectName = &nt_name, |
| 6424 | .SecurityDescriptor = null, | ||
| 6425 | .SecurityQualityOfService = null, | ||
| 6426 | }, | 6411 | }, |
| 6427 | &io_status_block, | 6412 | &io_status_block, |
| 6428 | null, | 6413 | null, |
| ... | @@ -7242,14 +7227,8 @@ fn dirReadLinkWindows(dir: Dir, sub_path: []const u8, buffer: []u8) Dir.ReadLink | ... | @@ -7242,14 +7227,8 @@ fn dirReadLinkWindows(dir: Dir, sub_path: []const u8, buffer: []u8) Dir.ReadLink |
| 7242 | .Buffer = @constCast(sub_path_w.ptr), | 7227 | .Buffer = @constCast(sub_path_w.ptr), |
| 7243 | }; | 7228 | }; |
| 7244 | const attr: windows.OBJECT_ATTRIBUTES = .{ | 7229 | const attr: windows.OBJECT_ATTRIBUTES = .{ |
| 7245 | .Length = @sizeOf(windows.OBJECT_ATTRIBUTES), | ||
| 7246 | .RootDirectory = if (Dir.path.isAbsoluteWindowsWtf16(sub_path_w)) null else dir.handle, | 7230 | .RootDirectory = if (Dir.path.isAbsoluteWindowsWtf16(sub_path_w)) null else dir.handle, |
| 7247 | .Attributes = .{ | ||
| 7248 | .INHERIT = false, | ||
| 7249 | }, | ||
| 7250 | .ObjectName = &nt_name, | 7231 | .ObjectName = &nt_name, |
| 7251 | .SecurityDescriptor = null, | ||
| 7252 | .SecurityQualityOfService = null, | ||
| 7253 | }; | 7232 | }; |
| 7254 | var io_status_block: windows.IO_STATUS_BLOCK = undefined; | 7233 | var io_status_block: windows.IO_STATUS_BLOCK = undefined; |
| 7255 | var result_handle: windows.HANDLE = undefined; | 7234 | var result_handle: windows.HANDLE = undefined; |
| ... | @@ -7800,24 +7779,19 @@ fn fileSyncWindows(userdata: ?*anyopaque, file: File) File.SyncError!void { | ... | @@ -7800,24 +7779,19 @@ fn fileSyncWindows(userdata: ?*anyopaque, file: File) File.SyncError!void { |
| 7800 | const t: *Threaded = @ptrCast(@alignCast(userdata)); | 7779 | const t: *Threaded = @ptrCast(@alignCast(userdata)); |
| 7801 | _ = t; | 7780 | _ = t; |
| 7802 | 7781 | ||
| 7782 | var io_status_block: windows.IO_STATUS_BLOCK = undefined; | ||
| 7803 | const syscall: Syscall = try .start(); | 7783 | const syscall: Syscall = try .start(); |
| 7804 | while (true) { | 7784 | while (true) { |
| 7805 | if (windows.kernel32.FlushFileBuffers(file.handle) != 0) { | 7785 | switch (windows.ntdll.NtFlushBuffersFile(file.handle, &io_status_block)) { |
| 7806 | return syscall.finish(); | 7786 | .SUCCESS => break syscall.finish(), |
| 7807 | } | 7787 | .CANCELLED => { |
| 7808 | switch (windows.GetLastError()) { | ||
| 7809 | .SUCCESS => unreachable, // `FlushFileBuffers` returned nonzero | ||
| 7810 | .INVALID_HANDLE => unreachable, | ||
| 7811 | .ACCESS_DENIED => return syscall.fail(error.AccessDenied), // a sync was performed but the system couldn't update the access time | ||
| 7812 | .UNEXP_NET_ERR => return syscall.fail(error.InputOutput), | ||
| 7813 | .OPERATION_ABORTED => { | ||
| 7814 | try syscall.checkCancel(); | 7788 | try syscall.checkCancel(); |
| 7815 | continue; | 7789 | continue; |
| 7816 | }, | 7790 | }, |
| 7817 | else => |err| { | 7791 | .INVALID_HANDLE => unreachable, |
| 7818 | syscall.finish(); | 7792 | .ACCESS_DENIED => return syscall.fail(error.AccessDenied), // a sync was performed but the system couldn't update the access time |
| 7819 | return windows.unexpectedError(err); | 7793 | .UNEXPECTED_NETWORK_ERROR => return syscall.fail(error.InputOutput), |
| 7820 | }, | 7794 | else => |status| return syscall.unexpectedNtstatus(status), |
| 7821 | } | 7795 | } |
| 7822 | } | 7796 | } |
| 7823 | } | 7797 | } |
| ... | @@ -14364,15 +14338,15 @@ fn scanEnviron(t: *Threaded) void { | ... | @@ -14364,15 +14338,15 @@ fn scanEnviron(t: *Threaded) void { |
| 14364 | comptime assert(@sizeOf(Environ.String) == 0); | 14338 | comptime assert(@sizeOf(Environ.String) == 0); |
| 14365 | } | 14339 | } |
| 14366 | } else { | 14340 | } else { |
| 14367 | for (t.environ.process_environ.block) |opt_line| { | 14341 | for (t.environ.process_environ.block.slice) |opt_entry| { |
| 14368 | const line = opt_line.?; | 14342 | const entry = opt_entry.?; |
| 14369 | var line_i: usize = 0; | 14343 | var entry_i: usize = 0; |
| 14370 | while (line[line_i] != 0 and line[line_i] != '=') : (line_i += 1) {} | 14344 | while (entry[entry_i] != 0 and entry[entry_i] != '=') : (entry_i += 1) {} |
| 14371 | const key = line[0..line_i]; | 14345 | const key = entry[0..entry_i]; |
| 14372 | 14346 | ||
| 14373 | var end_i: usize = line_i; | 14347 | var end_i: usize = entry_i; |
| 14374 | while (line[end_i] != 0) : (end_i += 1) {} | 14348 | while (entry[end_i] != 0) : (end_i += 1) {} |
| 14375 | const value = line[line_i + 1 .. end_i :0]; | 14349 | const value = entry[entry_i + 1 .. end_i :0]; |
| 14376 | 14350 | ||
| 14377 | if (std.mem.eql(u8, key, "NO_COLOR")) { | 14351 | if (std.mem.eql(u8, key, "NO_COLOR")) { |
| 14378 | t.environ.exist.NO_COLOR = true; | 14352 | t.environ.exist.NO_COLOR = true; |
| ... | @@ -14402,19 +14376,17 @@ fn processReplace(userdata: ?*anyopaque, options: process.ReplaceOptions) proces | ... | @@ -14402,19 +14376,17 @@ fn processReplace(userdata: ?*anyopaque, options: process.ReplaceOptions) proces |
| 14402 | const argv_buf = try arena.allocSentinel(?[*:0]const u8, options.argv.len, null); | 14376 | const argv_buf = try arena.allocSentinel(?[*:0]const u8, options.argv.len, null); |
| 14403 | for (options.argv, 0..) |arg, i| argv_buf[i] = (try arena.dupeZ(u8, arg)).ptr; | 14377 | for (options.argv, 0..) |arg, i| argv_buf[i] = (try arena.dupeZ(u8, arg)).ptr; |
| 14404 | 14378 | ||
| 14405 | const envp: [*:null]const ?[*:0]const u8 = m: { | 14379 | const env_block = env_block: { |
| 14406 | const prog_fd: i32 = -1; | 14380 | const prog_fd: i32 = -1; |
| 14407 | if (options.environ_map) |environ_map| { | 14381 | if (options.environ_map) |environ_map| break :env_block try environ_map.createPosixBlock(arena, .{ |
| 14408 | break :m (try environ_map.createBlockPosix(arena, .{ | 14382 | .zig_progress_fd = prog_fd, |
| 14409 | .zig_progress_fd = prog_fd, | 14383 | }); |
| 14410 | })).ptr; | 14384 | break :env_block try t.environ.process_environ.createPosixBlock(arena, .{ |
| 14411 | } | ||
| 14412 | break :m (try process.Environ.createBlockPosix(t.environ.process_environ, arena, .{ | ||
| 14413 | .zig_progress_fd = prog_fd, | 14385 | .zig_progress_fd = prog_fd, |
| 14414 | })).ptr; | 14386 | }); |
| 14415 | }; | 14387 | }; |
| 14416 | 14388 | ||
| 14417 | return posixExecv(options.expand_arg0, argv_buf.ptr[0].?, argv_buf.ptr, envp, PATH); | 14389 | return posixExecv(options.expand_arg0, argv_buf.ptr[0].?, argv_buf.ptr, env_block, PATH); |
| 14418 | } | 14390 | } |
| 14419 | 14391 | ||
| 14420 | fn processReplacePath(userdata: ?*anyopaque, dir: Dir, options: process.ReplaceOptions) process.ReplaceError { | 14392 | fn processReplacePath(userdata: ?*anyopaque, dir: Dir, options: process.ReplaceOptions) process.ReplaceError { |
| ... | @@ -14513,16 +14485,14 @@ fn spawnPosix(t: *Threaded, options: process.SpawnOptions) process.SpawnError!Sp | ... | @@ -14513,16 +14485,14 @@ fn spawnPosix(t: *Threaded, options: process.SpawnOptions) process.SpawnError!Sp |
| 14513 | const prog_fileno = 3; | 14485 | const prog_fileno = 3; |
| 14514 | comptime assert(@max(posix.STDIN_FILENO, posix.STDOUT_FILENO, posix.STDERR_FILENO) + 1 == prog_fileno); | 14486 | comptime assert(@max(posix.STDIN_FILENO, posix.STDOUT_FILENO, posix.STDERR_FILENO) + 1 == prog_fileno); |
| 14515 | 14487 | ||
| 14516 | const envp: [*:null]const ?[*:0]const u8 = m: { | 14488 | const env_block = env_block: { |
| 14517 | const prog_fd: i32 = if (prog_pipe[1] == -1) -1 else prog_fileno; | 14489 | const prog_fd: i32 = if (prog_pipe[1] == -1) -1 else prog_fileno; |
| 14518 | if (options.environ_map) |environ_map| { | 14490 | if (options.environ_map) |environ_map| break :env_block try environ_map.createPosixBlock(arena, .{ |
| 14519 | break :m (try environ_map.createBlockPosix(arena, .{ | 14491 | .zig_progress_fd = prog_fd, |
| 14520 | .zig_progress_fd = prog_fd, | 14492 | }); |
| 14521 | })).ptr; | 14493 | break :env_block try t.environ.process_environ.createPosixBlock(arena, .{ |
| 14522 | } | ||
| 14523 | break :m (try process.Environ.createBlockPosix(t.environ.process_environ, arena, .{ | ||
| 14524 | .zig_progress_fd = prog_fd, | 14494 | .zig_progress_fd = prog_fd, |
| 14525 | })).ptr; | 14495 | }); |
| 14526 | }; | 14496 | }; |
| 14527 | 14497 | ||
| 14528 | // This pipe communicates to the parent errors in the child between `fork` and `execvpe`. | 14498 | // This pipe communicates to the parent errors in the child between `fork` and `execvpe`. |
| ... | @@ -14601,7 +14571,7 @@ fn spawnPosix(t: *Threaded, options: process.SpawnOptions) process.SpawnError!Sp | ... | @@ -14601,7 +14571,7 @@ fn spawnPosix(t: *Threaded, options: process.SpawnOptions) process.SpawnError!Sp |
| 14601 | } | 14571 | } |
| 14602 | } | 14572 | } |
| 14603 | 14573 | ||
| 14604 | const err = posixExecv(options.expand_arg0, argv_buf.ptr[0].?, argv_buf.ptr, envp, PATH); | 14574 | const err = posixExecv(options.expand_arg0, argv_buf.ptr[0].?, argv_buf.ptr, env_block, PATH); |
| 14605 | forkBail(ep1, err); | 14575 | forkBail(ep1, err); |
| 14606 | } | 14576 | } |
| 14607 | 14577 | ||
| ... | @@ -14615,7 +14585,6 @@ fn spawnPosix(t: *Threaded, options: process.SpawnOptions) process.SpawnError!Sp | ... | @@ -14615,7 +14585,6 @@ fn spawnPosix(t: *Threaded, options: process.SpawnOptions) process.SpawnError!Sp |
| 14615 | if (options.stderr == .pipe) posix.close(stderr_pipe[1]); | 14585 | if (options.stderr == .pipe) posix.close(stderr_pipe[1]); |
| 14616 | 14586 | ||
| 14617 | if (prog_pipe[1] != -1) posix.close(prog_pipe[1]); | 14587 | if (prog_pipe[1] != -1) posix.close(prog_pipe[1]); |
| 14618 | |||
| 14619 | options.progress_node.setIpcFd(prog_pipe[0]); | 14588 | options.progress_node.setIpcFd(prog_pipe[0]); |
| 14620 | 14589 | ||
| 14621 | return .{ | 14590 | return .{ |
| ... | @@ -14739,42 +14708,44 @@ fn childKillWindows(t: *Threaded, child: *process.Child, exit_code: windows.UINT | ... | @@ -14739,42 +14708,44 @@ fn childKillWindows(t: *Threaded, child: *process.Child, exit_code: windows.UINT |
| 14739 | // some rare edge cases where our process handle no longer has the | 14708 | // some rare edge cases where our process handle no longer has the |
| 14740 | // PROCESS_TERMINATE access right, so let's do another check to make | 14709 | // PROCESS_TERMINATE access right, so let's do another check to make |
| 14741 | // sure the process is really no longer running: | 14710 | // sure the process is really no longer running: |
| 14742 | windows.WaitForSingleObjectEx(handle, 0, false) catch return error.AccessDenied; | 14711 | const minimal_timeout: windows.LARGE_INTEGER = -1; |
| 14743 | return error.AlreadyTerminated; | 14712 | switch (windows.ntdll.NtWaitForSingleObject(handle, windows.FALSE, &minimal_timeout)) { |
| 14713 | .SUCCESS => return error.AlreadyTerminated, | ||
| 14714 | else => return error.AccessDenied, | ||
| 14715 | } | ||
| 14744 | }, | 14716 | }, |
| 14745 | else => |err| return windows.unexpectedError(err), | 14717 | else => |err| return windows.unexpectedError(err), |
| 14746 | } | 14718 | } |
| 14747 | } | 14719 | } |
| 14748 | _ = windows.kernel32.WaitForSingleObjectEx(handle, windows.INFINITE, windows.FALSE); | 14720 | const infinite_timeout: windows.LARGE_INTEGER = std.math.minInt(windows.LARGE_INTEGER); |
| 14721 | _ = windows.ntdll.NtWaitForSingleObject(handle, windows.FALSE, &infinite_timeout); | ||
| 14749 | childCleanupWindows(child); | 14722 | childCleanupWindows(child); |
| 14750 | } | 14723 | } |
| 14751 | 14724 | ||
| 14752 | fn childWaitWindows(child: *process.Child) process.Child.WaitError!process.Child.Term { | 14725 | fn childWaitWindows(child: *process.Child) process.Child.WaitError!process.Child.Term { |
| 14753 | const handle = child.id.?; | 14726 | const handle = child.id.?; |
| 14754 | 14727 | ||
| 14755 | const syscall: Syscall = try .start(); | 14728 | const alertable_syscall: AlertableSyscall = try .start(); |
| 14756 | while (true) switch (windows.kernel32.WaitForSingleObjectEx(handle, windows.INFINITE, windows.FALSE)) { | 14729 | const infinite_timeout: windows.LARGE_INTEGER = std.math.minInt(windows.LARGE_INTEGER); |
| 14757 | windows.WAIT_OBJECT_0 => break syscall.finish(), | 14730 | while (true) switch (windows.ntdll.NtWaitForSingleObject(handle, windows.TRUE, &infinite_timeout)) { |
| 14758 | windows.WAIT_ABANDONED, windows.WAIT_TIMEOUT => { | 14731 | .WAIT_0 => break alertable_syscall.finish(), |
| 14759 | try syscall.checkCancel(); | 14732 | .ABANDONED_WAIT_0, .TIMEOUT => { |
| 14733 | try alertable_syscall.checkCancel(); | ||
| 14760 | continue; | 14734 | continue; |
| 14761 | }, | 14735 | }, |
| 14762 | windows.WAIT_FAILED => { | 14736 | else => |status| return alertable_syscall.unexpectedNtstatus(status), |
| 14763 | syscall.finish(); | ||
| 14764 | switch (windows.GetLastError()) { | ||
| 14765 | else => |err| return windows.unexpectedError(err), | ||
| 14766 | } | ||
| 14767 | }, | ||
| 14768 | else => return syscall.fail(error.Unexpected), | ||
| 14769 | }; | 14737 | }; |
| 14770 | 14738 | ||
| 14771 | const term: process.Child.Term = x: { | 14739 | var info: windows.PROCESS_BASIC_INFORMATION = undefined; |
| 14772 | var exit_code: windows.DWORD = undefined; | 14740 | const term: process.Child.Term = switch (windows.ntdll.NtQueryInformationProcess( |
| 14773 | if (windows.kernel32.GetExitCodeProcess(handle, &exit_code) == 0) { | 14741 | handle, |
| 14774 | break :x .{ .unknown = 0 }; | 14742 | .BasicInformation, |
| 14775 | } else { | 14743 | &info, |
| 14776 | break :x .{ .exited = @as(u8, @truncate(exit_code)) }; | 14744 | @sizeOf(windows.PROCESS_BASIC_INFORMATION), |
| 14777 | } | 14745 | null, |
| 14746 | )) { | ||
| 14747 | .SUCCESS => .{ .exited = @as(u8, @truncate(@intFromEnum(info.ExitStatus))) }, | ||
| 14748 | else => .{ .unknown = 0 }, | ||
| 14778 | }; | 14749 | }; |
| 14779 | 14750 | ||
| 14780 | childCleanupWindows(child); | 14751 | childCleanupWindows(child); |
| ... | @@ -15037,88 +15008,70 @@ fn setUpChildIo(stdio: process.SpawnOptions.StdIo, pipe_fd: i32, std_fileno: i32 | ... | @@ -15037,88 +15008,70 @@ fn setUpChildIo(stdio: process.SpawnOptions.StdIo, pipe_fd: i32, std_fileno: i32 |
| 15037 | fn processSpawnWindows(userdata: ?*anyopaque, options: process.SpawnOptions) process.SpawnError!process.Child { | 15008 | fn processSpawnWindows(userdata: ?*anyopaque, options: process.SpawnOptions) process.SpawnError!process.Child { |
| 15038 | const t: *Threaded = @ptrCast(@alignCast(userdata)); | 15009 | const t: *Threaded = @ptrCast(@alignCast(userdata)); |
| 15039 | 15010 | ||
| 15040 | var saAttr: windows.SECURITY_ATTRIBUTES = .{ | ||
| 15041 | .nLength = @sizeOf(windows.SECURITY_ATTRIBUTES), | ||
| 15042 | .bInheritHandle = windows.TRUE, | ||
| 15043 | .lpSecurityDescriptor = null, | ||
| 15044 | }; | ||
| 15045 | |||
| 15046 | const any_ignore = | 15011 | const any_ignore = |
| 15047 | options.stdin == .ignore or | 15012 | options.stdin == .ignore or |
| 15048 | options.stdout == .ignore or | 15013 | options.stdout == .ignore or |
| 15049 | options.stderr == .ignore; | 15014 | options.stderr == .ignore; |
| 15050 | 15015 | const nul_handle = if (any_ignore) try getNulDevice(t) else undefined; | |
| 15051 | const nul_handle = if (any_ignore) try getNulHandle(t) else undefined; | 15016 | |
| 15052 | 15017 | const any_inherit = | |
| 15053 | var g_hChildStd_IN_Rd: ?windows.HANDLE = null; | 15018 | options.stdin == .inherit or |
| 15054 | var g_hChildStd_IN_Wr: ?windows.HANDLE = null; | 15019 | options.stdout == .inherit or |
| 15055 | switch (options.stdin) { | 15020 | options.stderr == .inherit; |
| 15056 | .pipe => { | 15021 | const peb = if (any_inherit) windows.peb() else undefined; |
| 15057 | try windowsMakePipeIn(&g_hChildStd_IN_Rd, &g_hChildStd_IN_Wr, &saAttr); | 15022 | |
| 15058 | }, | 15023 | const stdin_pipe = if (options.stdin == .pipe) try t.windowsCreatePipe(.{ |
| 15059 | .ignore => { | 15024 | .server = .{ .attributes = .{ .INHERIT = false }, .mode = .{ .IO = .SYNCHRONOUS_NONALERT } }, |
| 15060 | g_hChildStd_IN_Rd = nul_handle; | 15025 | .client = .{ .attributes = .{ .INHERIT = true }, .mode = .{ .IO = .SYNCHRONOUS_NONALERT } }, |
| 15061 | }, | 15026 | .outbound = true, |
| 15062 | .inherit => { | 15027 | }) else undefined; |
| 15063 | g_hChildStd_IN_Rd = windows.GetStdHandle(windows.STD_INPUT_HANDLE) catch null; | 15028 | errdefer if (options.stdin == .pipe) for (stdin_pipe) |handle| windows.CloseHandle(handle); |
| 15064 | }, | 15029 | |
| 15065 | .close => { | 15030 | const stdout_pipe = if (options.stdout == .pipe) try t.windowsCreatePipe(.{ |
| 15066 | g_hChildStd_IN_Rd = null; | 15031 | .server = .{ .attributes = .{ .INHERIT = false }, .mode = .{ .IO = .ASYNCHRONOUS } }, |
| 15067 | }, | 15032 | .client = .{ .attributes = .{ .INHERIT = true }, .mode = .{ .IO = .SYNCHRONOUS_NONALERT } }, |
| 15068 | .file => @panic("TODO implement passing file stdio in processSpawnWindows"), | 15033 | .inbound = true, |
| 15069 | } | 15034 | }) else undefined; |
| 15070 | errdefer if (options.stdin == .pipe) { | 15035 | errdefer if (options.stdout == .pipe) for (stdout_pipe) |handle| windows.CloseHandle(handle); |
| 15071 | windowsDestroyPipe(g_hChildStd_IN_Rd, g_hChildStd_IN_Wr); | 15036 | |
| 15072 | }; | 15037 | const stderr_pipe = if (options.stderr == .pipe) try t.windowsCreatePipe(.{ |
| 15073 | 15038 | .server = .{ .attributes = .{ .INHERIT = false }, .mode = .{ .IO = .ASYNCHRONOUS } }, | |
| 15074 | var g_hChildStd_OUT_Rd: ?windows.HANDLE = null; | 15039 | .client = .{ .attributes = .{ .INHERIT = true }, .mode = .{ .IO = .SYNCHRONOUS_NONALERT } }, |
| 15075 | var g_hChildStd_OUT_Wr: ?windows.HANDLE = null; | 15040 | .inbound = true, |
| 15076 | switch (options.stdout) { | 15041 | }) else undefined; |
| 15077 | .pipe => { | 15042 | errdefer if (options.stderr == .pipe) for (stderr_pipe) |handle| windows.CloseHandle(handle); |
| 15078 | try windowsMakeAsyncPipe(&g_hChildStd_OUT_Rd, &g_hChildStd_OUT_Wr, &saAttr); | 15043 | |
| 15079 | }, | 15044 | const prog_pipe = if (options.progress_node.index != .none) try t.windowsCreatePipe(.{ |
| 15080 | .ignore => { | 15045 | .server = .{ .attributes = .{ .INHERIT = false }, .mode = .{ .IO = .ASYNCHRONOUS } }, |
| 15081 | g_hChildStd_OUT_Wr = nul_handle; | 15046 | .client = .{ .attributes = .{ .INHERIT = true }, .mode = .{ .IO = .SYNCHRONOUS_NONALERT } }, |
| 15082 | }, | 15047 | .inbound = true, |
| 15083 | .inherit => { | 15048 | }) else undefined; |
| 15084 | g_hChildStd_OUT_Wr = windows.GetStdHandle(windows.STD_OUTPUT_HANDLE) catch null; | 15049 | errdefer if (options.progress_node.index != .none) for (prog_pipe) |handle| windows.CloseHandle(handle); |
| 15085 | }, | ||
| 15086 | .close => { | ||
| 15087 | g_hChildStd_OUT_Wr = null; | ||
| 15088 | }, | ||
| 15089 | .file => @panic("TODO implement passing file stdio in processSpawnWindows"), | ||
| 15090 | } | ||
| 15091 | errdefer if (options.stdout == .pipe) { | ||
| 15092 | windowsDestroyPipe(g_hChildStd_OUT_Rd, g_hChildStd_OUT_Wr); | ||
| 15093 | }; | ||
| 15094 | |||
| 15095 | var g_hChildStd_ERR_Rd: ?windows.HANDLE = null; | ||
| 15096 | var g_hChildStd_ERR_Wr: ?windows.HANDLE = null; | ||
| 15097 | switch (options.stderr) { | ||
| 15098 | .pipe => { | ||
| 15099 | try windowsMakeAsyncPipe(&g_hChildStd_ERR_Rd, &g_hChildStd_ERR_Wr, &saAttr); | ||
| 15100 | }, | ||
| 15101 | .ignore => { | ||
| 15102 | g_hChildStd_ERR_Wr = nul_handle; | ||
| 15103 | }, | ||
| 15104 | .inherit => { | ||
| 15105 | g_hChildStd_ERR_Wr = windows.GetStdHandle(windows.STD_ERROR_HANDLE) catch null; | ||
| 15106 | }, | ||
| 15107 | .close => { | ||
| 15108 | g_hChildStd_ERR_Wr = null; | ||
| 15109 | }, | ||
| 15110 | .file => @panic("TODO implement passing file stdio in processSpawnWindows"), | ||
| 15111 | } | ||
| 15112 | errdefer if (options.stderr == .pipe) { | ||
| 15113 | windowsDestroyPipe(g_hChildStd_ERR_Rd, g_hChildStd_ERR_Wr); | ||
| 15114 | }; | ||
| 15115 | 15050 | ||
| 15116 | var siStartInfo: windows.STARTUPINFOW = .{ | 15051 | var siStartInfo: windows.STARTUPINFOW = .{ |
| 15117 | .cb = @sizeOf(windows.STARTUPINFOW), | 15052 | .cb = @sizeOf(windows.STARTUPINFOW), |
| 15118 | .hStdError = g_hChildStd_ERR_Wr, | ||
| 15119 | .hStdOutput = g_hChildStd_OUT_Wr, | ||
| 15120 | .hStdInput = g_hChildStd_IN_Rd, | ||
| 15121 | .dwFlags = windows.STARTF_USESTDHANDLES, | 15053 | .dwFlags = windows.STARTF_USESTDHANDLES, |
| 15054 | .hStdInput = switch (options.stdin) { | ||
| 15055 | .inherit => peb.ProcessParameters.hStdInput, | ||
| 15056 | .file => |file| file.handle, | ||
| 15057 | .ignore => nul_handle, | ||
| 15058 | .pipe => stdin_pipe[1], | ||
| 15059 | .close => null, | ||
| 15060 | }, | ||
| 15061 | .hStdOutput = switch (options.stdout) { | ||
| 15062 | .inherit => peb.ProcessParameters.hStdOutput, | ||
| 15063 | .file => |file| file.handle, | ||
| 15064 | .ignore => nul_handle, | ||
| 15065 | .pipe => stdout_pipe[1], | ||
| 15066 | .close => null, | ||
| 15067 | }, | ||
| 15068 | .hStdError = switch (options.stdin) { | ||
| 15069 | .inherit => peb.ProcessParameters.hStdError, | ||
| 15070 | .file => |file| file.handle, | ||
| 15071 | .ignore => nul_handle, | ||
| 15072 | .pipe => stderr_pipe[1], | ||
| 15073 | .close => null, | ||
| 15074 | }, | ||
| 15122 | 15075 | ||
| 15123 | .lpReserved = null, | 15076 | .lpReserved = null, |
| 15124 | .lpDesktop = null, | 15077 | .lpDesktop = null, |
| ... | @@ -15143,8 +15096,18 @@ fn processSpawnWindows(userdata: ?*anyopaque, options: process.SpawnOptions) pro | ... | @@ -15143,8 +15096,18 @@ fn processSpawnWindows(userdata: ?*anyopaque, options: process.SpawnOptions) pro |
| 15143 | const cwd_w = if (options.cwd) |cwd| try std.unicode.wtf8ToWtf16LeAllocZ(arena, cwd) else null; | 15096 | const cwd_w = if (options.cwd) |cwd| try std.unicode.wtf8ToWtf16LeAllocZ(arena, cwd) else null; |
| 15144 | const cwd_w_ptr = if (cwd_w) |cwd| cwd.ptr else null; | 15097 | const cwd_w_ptr = if (cwd_w) |cwd| cwd.ptr else null; |
| 15145 | 15098 | ||
| 15146 | const maybe_envp_buf = if (options.environ_map) |environ_map| try environ_map.createBlockWindows(arena) else null; | 15099 | const env_block = env_block: { |
| 15147 | const envp_ptr = if (maybe_envp_buf) |envp_buf| envp_buf.ptr else null; | 15100 | const prog_handle = if (options.progress_node.index != .none) |
| 15101 | prog_pipe[1] | ||
| 15102 | else | ||
| 15103 | windows.INVALID_HANDLE_VALUE; | ||
| 15104 | if (options.environ_map) |environ_map| break :env_block try environ_map.createWindowsBlock(arena, .{ | ||
| 15105 | .zig_progress_handle = prog_handle, | ||
| 15106 | }); | ||
| 15107 | break :env_block try t.environ.process_environ.createWindowsBlock(arena, .{ | ||
| 15108 | .zig_progress_handle = if (options.progress_node.index != .none) prog_pipe[1] else windows.INVALID_HANDLE_VALUE, | ||
| 15109 | }); | ||
| 15110 | }; | ||
| 15148 | 15111 | ||
| 15149 | const app_name_wtf8 = options.argv[0]; | 15112 | const app_name_wtf8 = options.argv[0]; |
| 15150 | const app_name_is_absolute = Dir.path.isAbsolute(app_name_wtf8); | 15113 | const app_name_is_absolute = Dir.path.isAbsolute(app_name_wtf8); |
| ... | @@ -15222,7 +15185,7 @@ fn processSpawnWindows(userdata: ?*anyopaque, options: process.SpawnOptions) pro | ... | @@ -15222,7 +15185,7 @@ fn processSpawnWindows(userdata: ?*anyopaque, options: process.SpawnOptions) pro |
| 15222 | &app_buf, | 15185 | &app_buf, |
| 15223 | PATHEXT, | 15186 | PATHEXT, |
| 15224 | &cmd_line_cache, | 15187 | &cmd_line_cache, |
| 15225 | envp_ptr, | 15188 | env_block, |
| 15226 | cwd_w_ptr, | 15189 | cwd_w_ptr, |
| 15227 | flags, | 15190 | flags, |
| 15228 | &siStartInfo, | 15191 | &siStartInfo, |
| ... | @@ -15257,7 +15220,7 @@ fn processSpawnWindows(userdata: ?*anyopaque, options: process.SpawnOptions) pro | ... | @@ -15257,7 +15220,7 @@ fn processSpawnWindows(userdata: ?*anyopaque, options: process.SpawnOptions) pro |
| 15257 | &app_buf, | 15220 | &app_buf, |
| 15258 | PATHEXT, | 15221 | PATHEXT, |
| 15259 | &cmd_line_cache, | 15222 | &cmd_line_cache, |
| 15260 | envp_ptr, | 15223 | env_block, |
| 15261 | cwd_w_ptr, | 15224 | cwd_w_ptr, |
| 15262 | flags, | 15225 | flags, |
| 15263 | &siStartInfo, | 15226 | &siStartInfo, |
| ... | @@ -15277,21 +15240,40 @@ fn processSpawnWindows(userdata: ?*anyopaque, options: process.SpawnOptions) pro | ... | @@ -15277,21 +15240,40 @@ fn processSpawnWindows(userdata: ?*anyopaque, options: process.SpawnOptions) pro |
| 15277 | }; | 15240 | }; |
| 15278 | } | 15241 | } |
| 15279 | 15242 | ||
| 15280 | if (options.stdin == .pipe) windows.CloseHandle(g_hChildStd_IN_Rd.?); | 15243 | if (options.progress_node.index != .none) { |
| 15281 | if (options.stderr == .pipe) windows.CloseHandle(g_hChildStd_ERR_Wr.?); | 15244 | windows.CloseHandle(prog_pipe[1]); |
| 15282 | if (options.stdout == .pipe) windows.CloseHandle(g_hChildStd_OUT_Wr.?); | 15245 | options.progress_node.setIpcFd(prog_pipe[0]); |
| 15246 | } | ||
| 15283 | 15247 | ||
| 15284 | return .{ | 15248 | return .{ |
| 15285 | .id = piProcInfo.hProcess, | 15249 | .id = piProcInfo.hProcess, |
| 15286 | .thread_handle = piProcInfo.hThread, | 15250 | .thread_handle = piProcInfo.hThread, |
| 15287 | .stdin = if (g_hChildStd_IN_Wr) |h| .{ .handle = h, .flags = .{ .nonblocking = false } } else null, | 15251 | .stdin = stdin: switch (options.stdin) { |
| 15288 | .stdout = if (g_hChildStd_OUT_Rd) |h| .{ .handle = h, .flags = .{ .nonblocking = true } } else null, | 15252 | .pipe => { |
| 15289 | .stderr = if (g_hChildStd_ERR_Rd) |h| .{ .handle = h, .flags = .{ .nonblocking = true } } else null, | 15253 | windows.CloseHandle(stdin_pipe[1]); |
| 15254 | break :stdin .{ .handle = stdin_pipe[0], .flags = .{ .nonblocking = false } }; | ||
| 15255 | }, | ||
| 15256 | else => null, | ||
| 15257 | }, | ||
| 15258 | .stdout = stdout: switch (options.stdout) { | ||
| 15259 | .pipe => { | ||
| 15260 | windows.CloseHandle(stdout_pipe[1]); | ||
| 15261 | break :stdout .{ .handle = stdout_pipe[0], .flags = .{ .nonblocking = true } }; | ||
| 15262 | }, | ||
| 15263 | else => null, | ||
| 15264 | }, | ||
| 15265 | .stderr = stderr: switch (options.stderr) { | ||
| 15266 | .pipe => { | ||
| 15267 | windows.CloseHandle(stderr_pipe[1]); | ||
| 15268 | break :stderr .{ .handle = stderr_pipe[0], .flags = .{ .nonblocking = true } }; | ||
| 15269 | }, | ||
| 15270 | else => null, | ||
| 15271 | }, | ||
| 15290 | .request_resource_usage_statistics = options.request_resource_usage_statistics, | 15272 | .request_resource_usage_statistics = options.request_resource_usage_statistics, |
| 15291 | }; | 15273 | }; |
| 15292 | } | 15274 | } |
| 15293 | 15275 | ||
| 15294 | fn getCngHandle(t: *Threaded) Io.RandomSecureError!windows.HANDLE { | 15276 | fn getCngDevice(t: *Threaded) Io.RandomSecureError!windows.HANDLE { |
| 15295 | { | 15277 | { |
| 15296 | t.mutex.lock(); | 15278 | t.mutex.lock(); |
| 15297 | defer t.mutex.unlock(); | 15279 | defer t.mutex.unlock(); |
| ... | @@ -15299,12 +15281,6 @@ fn getCngHandle(t: *Threaded) Io.RandomSecureError!windows.HANDLE { | ... | @@ -15299,12 +15281,6 @@ fn getCngHandle(t: *Threaded) Io.RandomSecureError!windows.HANDLE { |
| 15299 | } | 15281 | } |
| 15300 | 15282 | ||
| 15301 | const device_path = [_]u16{ '\\', 'D', 'e', 'v', 'i', 'c', 'e', '\\', 'C', 'N', 'G' }; | 15283 | const device_path = [_]u16{ '\\', 'D', 'e', 'v', 'i', 'c', 'e', '\\', 'C', 'N', 'G' }; |
| 15302 | |||
| 15303 | var nt_name: windows.UNICODE_STRING = .{ | ||
| 15304 | .Length = device_path.len * 2, | ||
| 15305 | .MaximumLength = 0, | ||
| 15306 | .Buffer = @constCast(&device_path), | ||
| 15307 | }; | ||
| 15308 | var fresh_handle: windows.HANDLE = undefined; | 15284 | var fresh_handle: windows.HANDLE = undefined; |
| 15309 | var io_status_block: windows.IO_STATUS_BLOCK = undefined; | 15285 | var io_status_block: windows.IO_STATUS_BLOCK = undefined; |
| 15310 | var syscall: Syscall = try .start(); | 15286 | var syscall: Syscall = try .start(); |
| ... | @@ -15315,12 +15291,11 @@ fn getCngHandle(t: *Threaded) Io.RandomSecureError!windows.HANDLE { | ... | @@ -15315,12 +15291,11 @@ fn getCngHandle(t: *Threaded) Io.RandomSecureError!windows.HANDLE { |
| 15315 | .SPECIFIC = .{ .FILE = .{ .READ_DATA = true } }, | 15291 | .SPECIFIC = .{ .FILE = .{ .READ_DATA = true } }, |
| 15316 | }, | 15292 | }, |
| 15317 | &.{ | 15293 | &.{ |
| 15318 | .Length = @sizeOf(windows.OBJECT_ATTRIBUTES), | 15294 | .ObjectName = @constCast(&windows.UNICODE_STRING{ |
| 15319 | .RootDirectory = null, | 15295 | .Length = @sizeOf(@TypeOf(device_path)), |
| 15320 | .ObjectName = &nt_name, | 15296 | .MaximumLength = 0, |
| 15321 | .Attributes = .{}, | 15297 | .Buffer = @constCast(&device_path), |
| 15322 | .SecurityDescriptor = null, | 15298 | }), |
| 15323 | .SecurityQualityOfService = null, | ||
| 15324 | }, | 15299 | }, |
| 15325 | &io_status_block, | 15300 | &io_status_block, |
| 15326 | .VALID_FLAGS, | 15301 | .VALID_FLAGS, |
| ... | @@ -15347,7 +15322,7 @@ fn getCngHandle(t: *Threaded) Io.RandomSecureError!windows.HANDLE { | ... | @@ -15347,7 +15322,7 @@ fn getCngHandle(t: *Threaded) Io.RandomSecureError!windows.HANDLE { |
| 15347 | }; | 15322 | }; |
| 15348 | } | 15323 | } |
| 15349 | 15324 | ||
| 15350 | fn getNulHandle(t: *Threaded) !windows.HANDLE { | 15325 | fn getNulDevice(t: *Threaded) !windows.HANDLE { |
| 15351 | { | 15326 | { |
| 15352 | t.mutex.lock(); | 15327 | t.mutex.lock(); |
| 15353 | defer t.mutex.unlock(); | 15328 | defer t.mutex.unlock(); |
| ... | @@ -15355,44 +15330,26 @@ fn getNulHandle(t: *Threaded) !windows.HANDLE { | ... | @@ -15355,44 +15330,26 @@ fn getNulHandle(t: *Threaded) !windows.HANDLE { |
| 15355 | } | 15330 | } |
| 15356 | 15331 | ||
| 15357 | const device_path = [_]u16{ '\\', 'D', 'e', 'v', 'i', 'c', 'e', '\\', 'N', 'u', 'l', 'l' }; | 15332 | const device_path = [_]u16{ '\\', 'D', 'e', 'v', 'i', 'c', 'e', '\\', 'N', 'u', 'l', 'l' }; |
| 15358 | var nt_name: windows.UNICODE_STRING = .{ | ||
| 15359 | .Length = device_path.len * 2, | ||
| 15360 | .MaximumLength = 0, | ||
| 15361 | .Buffer = @constCast(&device_path), | ||
| 15362 | }; | ||
| 15363 | const attr: windows.OBJECT_ATTRIBUTES = .{ | ||
| 15364 | .Length = @sizeOf(windows.OBJECT_ATTRIBUTES), | ||
| 15365 | .RootDirectory = null, | ||
| 15366 | .Attributes = .{ | ||
| 15367 | .INHERIT = true, | ||
| 15368 | }, | ||
| 15369 | .ObjectName = &nt_name, | ||
| 15370 | .SecurityDescriptor = null, | ||
| 15371 | .SecurityQualityOfService = null, | ||
| 15372 | }; | ||
| 15373 | var io_status_block: windows.IO_STATUS_BLOCK = undefined; | ||
| 15374 | var fresh_handle: windows.HANDLE = undefined; | 15333 | var fresh_handle: windows.HANDLE = undefined; |
| 15334 | var io_status_block: windows.IO_STATUS_BLOCK = undefined; | ||
| 15375 | var syscall: Syscall = try .start(); | 15335 | var syscall: Syscall = try .start(); |
| 15376 | while (true) switch (windows.ntdll.NtCreateFile( | 15336 | while (true) switch (windows.ntdll.NtOpenFile( |
| 15377 | &fresh_handle, | 15337 | &fresh_handle, |
| 15378 | .{ | 15338 | .{ |
| 15379 | .STANDARD = .{ .SYNCHRONIZE = true }, | 15339 | .STANDARD = .{ .SYNCHRONIZE = true }, |
| 15380 | .GENERIC = .{ .WRITE = true, .READ = true }, | 15340 | .SPECIFIC = .{ .FILE = .{ .READ_DATA = true, .WRITE_DATA = true } }, |
| 15341 | }, | ||
| 15342 | &.{ | ||
| 15343 | .Attributes = .{ .INHERIT = true }, | ||
| 15344 | .ObjectName = @constCast(&windows.UNICODE_STRING{ | ||
| 15345 | .Length = @sizeOf(@TypeOf(device_path)), | ||
| 15346 | .MaximumLength = 0, | ||
| 15347 | .Buffer = @constCast(&device_path), | ||
| 15348 | }), | ||
| 15381 | }, | 15349 | }, |
| 15382 | &attr, | ||
| 15383 | &io_status_block, | 15350 | &io_status_block, |
| 15384 | null, | ||
| 15385 | .{ .NORMAL = true }, | ||
| 15386 | .VALID_FLAGS, | 15351 | .VALID_FLAGS, |
| 15387 | .OPEN, | 15352 | .{ .IO = .SYNCHRONOUS_NONALERT }, |
| 15388 | .{ | ||
| 15389 | .DIRECTORY_FILE = false, | ||
| 15390 | .NON_DIRECTORY_FILE = true, | ||
| 15391 | .IO = .SYNCHRONOUS_NONALERT, | ||
| 15392 | .OPEN_REPARSE_POINT = false, | ||
| 15393 | }, | ||
| 15394 | null, | ||
| 15395 | 0, | ||
| 15396 | )) { | 15353 | )) { |
| 15397 | .SUCCESS => { | 15354 | .SUCCESS => { |
| 15398 | syscall.finish(); | 15355 | syscall.finish(); |
| ... | @@ -15406,18 +15363,64 @@ fn getNulHandle(t: *Threaded) !windows.HANDLE { | ... | @@ -15406,18 +15363,64 @@ fn getNulHandle(t: *Threaded) !windows.HANDLE { |
| 15406 | return fresh_handle; | 15363 | return fresh_handle; |
| 15407 | } | 15364 | } |
| 15408 | }, | 15365 | }, |
| 15409 | .DELETE_PENDING => { | 15366 | .CANCELLED => { |
| 15410 | // This error means that there *was* a file in this location on | 15367 | try syscall.checkCancel(); |
| 15411 | // the file system, but it was deleted. However, the OS is not | ||
| 15412 | // finished with the deletion operation, and so this CreateFile | ||
| 15413 | // call has failed. There is not really a sane way to handle | ||
| 15414 | // this other than retrying the creation after the OS finishes | ||
| 15415 | // the deletion. | ||
| 15416 | syscall.finish(); | ||
| 15417 | try parking_sleep.windowsRetrySleep(1); | ||
| 15418 | syscall = try .start(); | ||
| 15419 | continue; | 15368 | continue; |
| 15420 | }, | 15369 | }, |
| 15370 | .INVALID_PARAMETER => |status| return syscall.ntstatusBug(status), | ||
| 15371 | .OBJECT_PATH_SYNTAX_BAD => |status| return syscall.ntstatusBug(status), | ||
| 15372 | .INVALID_HANDLE => |status| return syscall.ntstatusBug(status), | ||
| 15373 | .OBJECT_NAME_INVALID => return syscall.fail(error.BadPathName), | ||
| 15374 | .OBJECT_NAME_NOT_FOUND => return syscall.fail(error.FileNotFound), | ||
| 15375 | .OBJECT_PATH_NOT_FOUND => return syscall.fail(error.FileNotFound), | ||
| 15376 | .NO_MEDIA_IN_DEVICE => return syscall.fail(error.NoDevice), | ||
| 15377 | .SHARING_VIOLATION => return syscall.fail(error.AccessDenied), | ||
| 15378 | .ACCESS_DENIED => return syscall.fail(error.AccessDenied), | ||
| 15379 | .PIPE_NOT_AVAILABLE => return syscall.fail(error.NoDevice), | ||
| 15380 | .FILE_IS_A_DIRECTORY => return syscall.fail(error.IsDir), | ||
| 15381 | .NOT_A_DIRECTORY => return syscall.fail(error.NotDir), | ||
| 15382 | .USER_MAPPED_FILE => return syscall.fail(error.AccessDenied), | ||
| 15383 | else => |status| return syscall.unexpectedNtstatus(status), | ||
| 15384 | }; | ||
| 15385 | } | ||
| 15386 | |||
| 15387 | fn getNamedPipeDevice(t: *Threaded) !windows.HANDLE { | ||
| 15388 | { | ||
| 15389 | t.mutex.lock(); | ||
| 15390 | defer t.mutex.unlock(); | ||
| 15391 | if (t.pipe_file.handle) |handle| return handle; | ||
| 15392 | } | ||
| 15393 | |||
| 15394 | const device_path = [_]u16{ '\\', 'D', 'e', 'v', 'i', 'c', 'e', '\\', 'N', 'a', 'm', 'e', 'd', 'P', 'i', 'p', 'e', '\\' }; | ||
| 15395 | var fresh_handle: windows.HANDLE = undefined; | ||
| 15396 | var io_status_block: windows.IO_STATUS_BLOCK = undefined; | ||
| 15397 | var syscall: Syscall = try .start(); | ||
| 15398 | while (true) switch (windows.ntdll.NtOpenFile( | ||
| 15399 | &fresh_handle, | ||
| 15400 | .{ .STANDARD = .{ .SYNCHRONIZE = true } }, | ||
| 15401 | &.{ | ||
| 15402 | .ObjectName = @constCast(&windows.UNICODE_STRING{ | ||
| 15403 | .Length = @sizeOf(@TypeOf(device_path)), | ||
| 15404 | .MaximumLength = 0, | ||
| 15405 | .Buffer = @constCast(&device_path), | ||
| 15406 | }), | ||
| 15407 | }, | ||
| 15408 | &io_status_block, | ||
| 15409 | .VALID_FLAGS, | ||
| 15410 | .{ .IO = .SYNCHRONOUS_NONALERT }, | ||
| 15411 | )) { | ||
| 15412 | .SUCCESS => { | ||
| 15413 | syscall.finish(); | ||
| 15414 | t.mutex.lock(); // Another thread might have won the race. | ||
| 15415 | defer t.mutex.unlock(); | ||
| 15416 | if (t.pipe_file.handle) |prev_handle| { | ||
| 15417 | windows.CloseHandle(fresh_handle); | ||
| 15418 | return prev_handle; | ||
| 15419 | } else { | ||
| 15420 | t.pipe_file.handle = fresh_handle; | ||
| 15421 | return fresh_handle; | ||
| 15422 | } | ||
| 15423 | }, | ||
| 15421 | .CANCELLED => { | 15424 | .CANCELLED => { |
| 15422 | try syscall.checkCancel(); | 15425 | try syscall.checkCancel(); |
| 15423 | continue; | 15426 | continue; |
| ... | @@ -15449,7 +15452,7 @@ fn windowsCreateProcessPathExt( | ... | @@ -15449,7 +15452,7 @@ fn windowsCreateProcessPathExt( |
| 15449 | app_buf: *std.ArrayList(u16), | 15452 | app_buf: *std.ArrayList(u16), |
| 15450 | pathext: [:0]const u16, | 15453 | pathext: [:0]const u16, |
| 15451 | cmd_line_cache: *WindowsCommandLineCache, | 15454 | cmd_line_cache: *WindowsCommandLineCache, |
| 15452 | envp_ptr: ?[*:0]const u16, | 15455 | env_block: ?process.Environ.WindowsBlock, |
| 15453 | cwd_ptr: ?[*:0]u16, | 15456 | cwd_ptr: ?[*:0]u16, |
| 15454 | flags: windows.CreateProcessFlags, | 15457 | flags: windows.CreateProcessFlags, |
| 15455 | lpStartupInfo: *windows.STARTUPINFOW, | 15458 | lpStartupInfo: *windows.STARTUPINFOW, |
| ... | @@ -15626,7 +15629,7 @@ fn windowsCreateProcessPathExt( | ... | @@ -15626,7 +15629,7 @@ fn windowsCreateProcessPathExt( |
| 15626 | if (windowsCreateProcess( | 15629 | if (windowsCreateProcess( |
| 15627 | app_name_w.ptr, | 15630 | app_name_w.ptr, |
| 15628 | cmd_line_w.ptr, | 15631 | cmd_line_w.ptr, |
| 15629 | envp_ptr, | 15632 | env_block, |
| 15630 | cwd_ptr, | 15633 | cwd_ptr, |
| 15631 | flags, | 15634 | flags, |
| 15632 | lpStartupInfo, | 15635 | lpStartupInfo, |
| ... | @@ -15686,7 +15689,7 @@ fn windowsCreateProcessPathExt( | ... | @@ -15686,7 +15689,7 @@ fn windowsCreateProcessPathExt( |
| 15686 | else | 15689 | else |
| 15687 | full_app_name; | 15690 | full_app_name; |
| 15688 | 15691 | ||
| 15689 | if (windowsCreateProcess(app_name_w.ptr, cmd_line_w.ptr, envp_ptr, cwd_ptr, flags, lpStartupInfo, lpProcessInformation)) |_| { | 15692 | if (windowsCreateProcess(app_name_w.ptr, cmd_line_w.ptr, env_block, cwd_ptr, flags, lpStartupInfo, lpProcessInformation)) |_| { |
| 15690 | return; | 15693 | return; |
| 15691 | } else |err| switch (err) { | 15694 | } else |err| switch (err) { |
| 15692 | error.FileNotFound => continue, | 15695 | error.FileNotFound => continue, |
| ... | @@ -15710,7 +15713,7 @@ fn windowsCreateProcessPathExt( | ... | @@ -15710,7 +15713,7 @@ fn windowsCreateProcessPathExt( |
| 15710 | fn windowsCreateProcess( | 15713 | fn windowsCreateProcess( |
| 15711 | app_name: [*:0]u16, | 15714 | app_name: [*:0]u16, |
| 15712 | cmd_line: [*:0]u16, | 15715 | cmd_line: [*:0]u16, |
| 15713 | env_ptr: ?[*:0]const u16, | 15716 | env_block: ?process.Environ.WindowsBlock, |
| 15714 | cwd_ptr: ?[*:0]u16, | 15717 | cwd_ptr: ?[*:0]u16, |
| 15715 | flags: windows.CreateProcessFlags, | 15718 | flags: windows.CreateProcessFlags, |
| 15716 | lpStartupInfo: *windows.STARTUPINFOW, | 15719 | lpStartupInfo: *windows.STARTUPINFOW, |
| ... | @@ -15725,7 +15728,7 @@ fn windowsCreateProcess( | ... | @@ -15725,7 +15728,7 @@ fn windowsCreateProcess( |
| 15725 | null, | 15728 | null, |
| 15726 | windows.TRUE, | 15729 | windows.TRUE, |
| 15727 | flags, | 15730 | flags, |
| 15728 | env_ptr, | 15731 | if (env_block) |block| block.slice.ptr else null, |
| 15729 | cwd_ptr, | 15732 | cwd_ptr, |
| 15730 | lpStartupInfo, | 15733 | lpStartupInfo, |
| 15731 | lpProcessInformation, | 15734 | lpProcessInformation, |
| ... | @@ -16246,11 +16249,11 @@ fn posixExecv( | ... | @@ -16246,11 +16249,11 @@ fn posixExecv( |
| 16246 | arg0_expand: process.ArgExpansion, | 16249 | arg0_expand: process.ArgExpansion, |
| 16247 | file: [*:0]const u8, | 16250 | file: [*:0]const u8, |
| 16248 | child_argv: [*:null]?[*:0]const u8, | 16251 | child_argv: [*:null]?[*:0]const u8, |
| 16249 | envp: [*:null]const ?[*:0]const u8, | 16252 | env_block: process.Environ.PosixBlock, |
| 16250 | PATH: []const u8, | 16253 | PATH: []const u8, |
| 16251 | ) process.ReplaceError { | 16254 | ) process.ReplaceError { |
| 16252 | const file_slice = std.mem.sliceTo(file, 0); | 16255 | const file_slice = std.mem.sliceTo(file, 0); |
| 16253 | if (std.mem.findScalar(u8, file_slice, '/') != null) return posixExecvPath(file, child_argv, envp); | 16256 | if (std.mem.findScalar(u8, file_slice, '/') != null) return posixExecvPath(file, child_argv, env_block); |
| 16254 | 16257 | ||
| 16255 | // Use of PATH_MAX here is valid as the path_buf will be passed | 16258 | // Use of PATH_MAX here is valid as the path_buf will be passed |
| 16256 | // directly to the operating system in posixExecvPath. | 16259 | // directly to the operating system in posixExecvPath. |
| ... | @@ -16278,7 +16281,7 @@ fn posixExecv( | ... | @@ -16278,7 +16281,7 @@ fn posixExecv( |
| 16278 | .expand => child_argv[0] = full_path, | 16281 | .expand => child_argv[0] = full_path, |
| 16279 | .no_expand => {}, | 16282 | .no_expand => {}, |
| 16280 | } | 16283 | } |
| 16281 | err = posixExecvPath(full_path, child_argv, envp); | 16284 | err = posixExecvPath(full_path, child_argv, env_block); |
| 16282 | switch (err) { | 16285 | switch (err) { |
| 16283 | error.AccessDenied => seen_eacces = true, | 16286 | error.AccessDenied => seen_eacces = true, |
| 16284 | error.FileNotFound, error.NotDir => {}, | 16287 | error.FileNotFound, error.NotDir => {}, |
| ... | @@ -16293,10 +16296,10 @@ fn posixExecv( | ... | @@ -16293,10 +16296,10 @@ fn posixExecv( |
| 16293 | pub fn posixExecvPath( | 16296 | pub fn posixExecvPath( |
| 16294 | path: [*:0]const u8, | 16297 | path: [*:0]const u8, |
| 16295 | child_argv: [*:null]const ?[*:0]const u8, | 16298 | child_argv: [*:null]const ?[*:0]const u8, |
| 16296 | envp: [*:null]const ?[*:0]const u8, | 16299 | env_block: process.Environ.PosixBlock, |
| 16297 | ) process.ReplaceError { | 16300 | ) process.ReplaceError { |
| 16298 | try Thread.checkCancel(); | 16301 | try Thread.checkCancel(); |
| 16299 | switch (posix.errno(posix.system.execve(path, child_argv, envp))) { | 16302 | switch (posix.errno(posix.system.execve(path, child_argv, env_block.slice.ptr))) { |
| 16300 | .FAULT => |err| return errnoBug(err), // Bad pointer parameter. | 16303 | .FAULT => |err| return errnoBug(err), // Bad pointer parameter. |
| 16301 | .@"2BIG" => return error.SystemResources, | 16304 | .@"2BIG" => return error.SystemResources, |
| 16302 | .MFILE => return error.ProcessFdQuotaExceeded, | 16305 | .MFILE => return error.ProcessFdQuotaExceeded, |
| ... | @@ -16328,85 +16331,100 @@ pub fn posixExecvPath( | ... | @@ -16328,85 +16331,100 @@ pub fn posixExecvPath( |
| 16328 | } | 16331 | } |
| 16329 | } | 16332 | } |
| 16330 | 16333 | ||
| 16331 | fn windowsMakePipeIn(rd: *?windows.HANDLE, wr: *?windows.HANDLE, sattr: *const windows.SECURITY_ATTRIBUTES) !void { | 16334 | const PipeOptions = struct { |
| 16332 | var rd_h: windows.HANDLE = undefined; | 16335 | attributes: windows.OBJECT_ATTRIBUTES.ATTRIBUTES, |
| 16333 | var wr_h: windows.HANDLE = undefined; | 16336 | mode: windows.FILE.MODE, |
| 16334 | try windows.CreatePipe(&rd_h, &wr_h, sattr); | 16337 | }; |
| 16335 | errdefer windowsDestroyPipe(rd_h, wr_h); | 16338 | pub const CreatePipeOptions = struct { |
| 16336 | try windows.SetHandleInformation(wr_h, windows.HANDLE_FLAG_INHERIT, 0); | 16339 | server: PipeOptions, |
| 16337 | rd.* = rd_h; | 16340 | client: PipeOptions, |
| 16338 | wr.* = wr_h; | 16341 | inbound: bool = false, |
| 16339 | } | 16342 | outbound: bool = false, |
| 16340 | 16343 | maximum_instances: u32 = 1, | |
| 16341 | fn windowsDestroyPipe(rd: ?windows.HANDLE, wr: ?windows.HANDLE) void { | 16344 | quota: u32 = 4096, |
| 16342 | if (rd) |h| posix.close(h); | 16345 | default_timeout: windows.LARGE_INTEGER = -120 * std.time.ns_per_s / 100, |
| 16343 | if (wr) |h| posix.close(h); | 16346 | }; |
| 16344 | } | 16347 | fn windowsCreatePipe(t: *Threaded, options: CreatePipeOptions) ![2]windows.HANDLE { |
| 16345 | 16348 | const named_pipe_device = try t.getNamedPipeDevice(); | |
| 16346 | fn windowsMakeAsyncPipe(rd: *?windows.HANDLE, wr: *?windows.HANDLE, sattr: *const windows.SECURITY_ATTRIBUTES) !void { | 16349 | const server_handle = server_handle: { |
| 16347 | var tmp_bufw: [128]u16 = undefined; | 16350 | var handle: windows.HANDLE = undefined; |
| 16348 | 16351 | var io_status_block: windows.IO_STATUS_BLOCK = undefined; | |
| 16349 | // Anonymous pipes are built upon Named pipes. | 16352 | const syscall: Syscall = try .start(); |
| 16350 | // https://docs.microsoft.com/en-us/windows/win32/api/namedpipeapi/nf-namedpipeapi-createpipe | 16353 | while (true) switch (windows.ntdll.NtCreateNamedPipeFile( |
| 16351 | // Asynchronous (overlapped) read and write operations are not supported by anonymous pipes. | 16354 | &handle, |
| 16352 | // https://docs.microsoft.com/en-us/windows/win32/ipc/anonymous-pipe-operations | 16355 | .{ |
| 16353 | const pipe_path = blk: { | 16356 | .SPECIFIC = .{ .FILE_PIPE = .{ |
| 16354 | var tmp_buf: [128]u8 = undefined; | 16357 | .READ_DATA = options.inbound, |
| 16355 | // Forge a random path for the pipe. | 16358 | .WRITE_DATA = options.outbound, |
| 16356 | const pipe_path = std.fmt.bufPrintSentinel( | 16359 | .WRITE_ATTRIBUTES = true, |
| 16357 | &tmp_buf, | 16360 | } }, |
| 16358 | "\\\\.\\pipe\\zig-childprocess-{d}-{d}", | 16361 | .STANDARD = .{ .SYNCHRONIZE = true }, |
| 16359 | .{ windows.GetCurrentProcessId(), pipe_name_counter.fetchAdd(1, .monotonic) }, | 16362 | }, |
| 16360 | 0, | 16363 | &.{ |
| 16361 | ) catch unreachable; | 16364 | .RootDirectory = named_pipe_device, |
| 16362 | const len = std.unicode.wtf8ToWtf16Le(&tmp_bufw, pipe_path) catch unreachable; | 16365 | .Attributes = options.server.attributes, |
| 16363 | tmp_bufw[len] = 0; | 16366 | }, |
| 16364 | break :blk tmp_bufw[0..len :0]; | 16367 | &io_status_block, |
| 16368 | .{ .READ = true, .WRITE = true }, | ||
| 16369 | .CREATE, | ||
| 16370 | options.server.mode, | ||
| 16371 | .{ .TYPE = .BYTE_STREAM }, | ||
| 16372 | .{ .MODE = .BYTE_STREAM }, | ||
| 16373 | .{ .OPERATION = .QUEUE }, | ||
| 16374 | options.maximum_instances, | ||
| 16375 | if (options.inbound) options.quota else 0, | ||
| 16376 | if (options.outbound) options.quota else 0, | ||
| 16377 | &options.default_timeout, | ||
| 16378 | )) { | ||
| 16379 | .SUCCESS => syscall.finish(), | ||
| 16380 | .CANCELLED => { | ||
| 16381 | try syscall.checkCancel(); | ||
| 16382 | continue; | ||
| 16383 | }, | ||
| 16384 | .INVALID_PARAMETER => |status| return syscall.ntstatusBug(status), | ||
| 16385 | .INSUFFICIENT_RESOURCES => return syscall.fail(error.SystemResources), | ||
| 16386 | else => |status| return syscall.unexpectedNtstatus(status), | ||
| 16387 | }; | ||
| 16388 | break :server_handle handle; | ||
| 16365 | }; | 16389 | }; |
| 16366 | 16390 | errdefer windows.CloseHandle(server_handle); | |
| 16367 | // Create the read handle that can be used with overlapped IO ops. | 16391 | const client_handle = client_handle: { |
| 16368 | const read_handle = windows.kernel32.CreateNamedPipeW( | 16392 | var handle: windows.HANDLE = undefined; |
| 16369 | pipe_path.ptr, | 16393 | var io_status_block: windows.IO_STATUS_BLOCK = undefined; |
| 16370 | windows.PIPE_ACCESS_INBOUND | windows.FILE_FLAG_OVERLAPPED, | 16394 | const syscall: Syscall = try .start(); |
| 16371 | windows.PIPE_TYPE_BYTE, | 16395 | while (true) switch (windows.ntdll.NtOpenFile( |
| 16372 | 1, | 16396 | &handle, |
| 16373 | 4096, | 16397 | .{ |
| 16374 | 4096, | 16398 | .SPECIFIC = .{ .FILE_PIPE = .{ |
| 16375 | 0, | 16399 | .READ_DATA = options.outbound, |
| 16376 | sattr, | 16400 | .WRITE_DATA = options.inbound, |
| 16377 | ); | 16401 | .WRITE_ATTRIBUTES = true, |
| 16378 | if (read_handle == windows.INVALID_HANDLE_VALUE) { | 16402 | } }, |
| 16379 | switch (windows.GetLastError()) { | 16403 | .STANDARD = .{ .SYNCHRONIZE = true }, |
| 16380 | else => |err| return windows.unexpectedError(err), | 16404 | }, |
| 16381 | } | 16405 | &.{ |
| 16382 | } | 16406 | .RootDirectory = server_handle, |
| 16383 | errdefer posix.close(read_handle); | 16407 | .Attributes = options.client.attributes, |
| 16384 | 16408 | }, | |
| 16385 | var sattr_copy = sattr.*; | 16409 | &io_status_block, |
| 16386 | const write_handle = windows.kernel32.CreateFileW( | 16410 | .{ .READ = true, .WRITE = true }, |
| 16387 | pipe_path.ptr, | 16411 | options.client.mode, |
| 16388 | .{ .GENERIC = .{ .WRITE = true } }, | 16412 | )) { |
| 16389 | 0, | 16413 | .SUCCESS => syscall.finish(), |
| 16390 | &sattr_copy, | 16414 | .CANCELED => { |
| 16391 | windows.OPEN_EXISTING, | 16415 | try syscall.checkCancel(); |
| 16392 | @bitCast(windows.FILE.ATTRIBUTE{ .NORMAL = true }), | 16416 | continue; |
| 16393 | null, | 16417 | }, |
| 16394 | ); | 16418 | .INVALID_PARAMETER => |status| return syscall.ntstatusBug(status), |
| 16395 | if (write_handle == windows.INVALID_HANDLE_VALUE) { | 16419 | .INSUFFICIENT_RESOURCES => return syscall.fail(error.SystemResources), |
| 16396 | switch (windows.GetLastError()) { | 16420 | else => |status| return syscall.unexpectedNtstatus(status), |
| 16397 | else => |err| return windows.unexpectedError(err), | 16421 | }; |
| 16398 | } | 16422 | break :client_handle handle; |
| 16399 | } | 16423 | }; |
| 16400 | errdefer posix.close(write_handle); | 16424 | errdefer windows.CloseHandle(client_handle); |
| 16401 | 16425 | return .{ server_handle, client_handle }; | |
| 16402 | try windows.SetHandleInformation(read_handle, windows.HANDLE_FLAG_INHERIT, 0); | ||
| 16403 | |||
| 16404 | rd.* = read_handle; | ||
| 16405 | wr.* = write_handle; | ||
| 16406 | } | 16426 | } |
| 16407 | 16427 | ||
| 16408 | var pipe_name_counter = std.atomic.Value(u32).init(1); | ||
| 16409 | |||
| 16410 | fn progressParentFile(userdata: ?*anyopaque) std.Progress.ParentFileError!File { | 16428 | fn progressParentFile(userdata: ?*anyopaque) std.Progress.ParentFileError!File { |
| 16411 | const t: *Threaded = @ptrCast(@alignCast(userdata)); | 16429 | const t: *Threaded = @ptrCast(@alignCast(userdata)); |
| 16412 | 16430 | ||
| ... | @@ -16517,7 +16535,7 @@ fn randomSecure(userdata: ?*anyopaque, buffer: []u8) Io.RandomSecureError!void { | ... | @@ -16517,7 +16535,7 @@ fn randomSecure(userdata: ?*anyopaque, buffer: []u8) Io.RandomSecureError!void { |
| 16517 | // despite the function being documented to always return TRUE | 16535 | // despite the function being documented to always return TRUE |
| 16518 | // * reads from "\\Device\\CNG" which then seeds a per-CPU AES CSPRNG | 16536 | // * reads from "\\Device\\CNG" which then seeds a per-CPU AES CSPRNG |
| 16519 | // Therefore, that function is avoided in favor of using the device directly. | 16537 | // Therefore, that function is avoided in favor of using the device directly. |
| 16520 | const cng_device = try getCngHandle(t); | 16538 | const cng_device = try getCngDevice(t); |
| 16521 | var io_status_block: windows.IO_STATUS_BLOCK = undefined; | 16539 | var io_status_block: windows.IO_STATUS_BLOCK = undefined; |
| 16522 | var i: usize = 0; | 16540 | var i: usize = 0; |
| 16523 | const syscall: Syscall = try .start(); | 16541 | const syscall: Syscall = try .start(); |
lib/std/Progress.zig+20-17| ... | @@ -139,7 +139,7 @@ pub const Node = struct { | ... | @@ -139,7 +139,7 @@ pub const Node = struct { |
| 139 | fn setIpcFd(s: *Storage, fd: Io.File.Handle) void { | 139 | fn setIpcFd(s: *Storage, fd: Io.File.Handle) void { |
| 140 | const integer: u32 = switch (@typeInfo(Io.File.Handle)) { | 140 | const integer: u32 = switch (@typeInfo(Io.File.Handle)) { |
| 141 | .int => @bitCast(fd), | 141 | .int => @bitCast(fd), |
| 142 | .pointer => @intFromPtr(fd), | 142 | .pointer => @intCast(@intFromPtr(fd)), |
| 143 | else => @compileError("unsupported fd_t of " ++ @typeName(Io.File.Handle)), | 143 | else => @compileError("unsupported fd_t of " ++ @typeName(Io.File.Handle)), |
| 144 | }; | 144 | }; |
| 145 | // `estimated_total_count` max int indicates the special state that | 145 | // `estimated_total_count` max int indicates the special state that |
| ... | @@ -342,10 +342,18 @@ pub const Node = struct { | ... | @@ -342,10 +342,18 @@ pub const Node = struct { |
| 342 | /// Posix-only. Used by `std.process.Child`. Thread-safe. | 342 | /// Posix-only. Used by `std.process.Child`. Thread-safe. |
| 343 | pub fn setIpcFd(node: Node, fd: Io.File.Handle) void { | 343 | pub fn setIpcFd(node: Node, fd: Io.File.Handle) void { |
| 344 | const index = node.index.unwrap() orelse return; | 344 | const index = node.index.unwrap() orelse return; |
| 345 | assert(fd >= 0); | 345 | switch (@typeInfo(Io.File.Handle)) { |
| 346 | assert(fd != posix.STDOUT_FILENO); | 346 | .int => { |
| 347 | assert(fd != posix.STDIN_FILENO); | 347 | assert(fd >= 0); |
| 348 | assert(fd != posix.STDERR_FILENO); | 348 | assert(fd != posix.STDOUT_FILENO); |
| 349 | assert(fd != posix.STDIN_FILENO); | ||
| 350 | assert(fd != posix.STDERR_FILENO); | ||
| 351 | }, | ||
| 352 | .pointer => { | ||
| 353 | assert(fd != windows.INVALID_HANDLE_VALUE); | ||
| 354 | }, | ||
| 355 | else => @compileError("unsupported fd_t of " ++ @typeName(Io.File.Handle)), | ||
| 356 | } | ||
| 349 | storageByIndex(index).setIpcFd(fd); | 357 | storageByIndex(index).setIpcFd(fd); |
| 350 | } | 358 | } |
| 351 | 359 | ||
| ... | @@ -477,21 +485,18 @@ pub fn start(io: Io, options: Options) Node { | ... | @@ -477,21 +485,18 @@ pub fn start(io: Io, options: Options) Node { |
| 477 | global_progress.refresh_rate_ns = @intCast(options.refresh_rate_ns.toNanoseconds()); | 485 | global_progress.refresh_rate_ns = @intCast(options.refresh_rate_ns.toNanoseconds()); |
| 478 | global_progress.initial_delay_ns = @intCast(options.initial_delay_ns.toNanoseconds()); | 486 | global_progress.initial_delay_ns = @intCast(options.initial_delay_ns.toNanoseconds()); |
| 479 | 487 | ||
| 480 | if (noop_impl) | 488 | if (noop_impl) return .none; |
| 481 | return Node.none; | ||
| 482 | 489 | ||
| 483 | global_progress.io = io; | 490 | global_progress.io = io; |
| 484 | 491 | ||
| 485 | if (io.vtable.progressParentFile(io.userdata)) |ipc_file| { | 492 | if (io.vtable.progressParentFile(io.userdata)) |ipc_file| { |
| 486 | global_progress.update_worker = io.concurrent(ipcThreadRun, .{ io, ipc_file }) catch |err| { | 493 | global_progress.update_worker = io.concurrent(ipcThreadRun, .{ io, ipc_file }) catch |err| { |
| 487 | global_progress.start_failure = .{ .spawn_ipc_worker = err }; | 494 | global_progress.start_failure = .{ .spawn_ipc_worker = err }; |
| 488 | return Node.none; | 495 | return .none; |
| 489 | }; | 496 | }; |
| 490 | } else |env_err| switch (env_err) { | 497 | } else |env_err| switch (env_err) { |
| 491 | error.EnvironmentVariableMissing => { | 498 | error.EnvironmentVariableMissing => { |
| 492 | if (options.disable_printing) { | 499 | if (options.disable_printing) return .none; |
| 493 | return Node.none; | ||
| 494 | } | ||
| 495 | const stderr: Io.File = .stderr(); | 500 | const stderr: Io.File = .stderr(); |
| 496 | global_progress.terminal = stderr; | 501 | global_progress.terminal = stderr; |
| 497 | if (stderr.enableAnsiEscapeCodes(io)) |_| { | 502 | if (stderr.enableAnsiEscapeCodes(io)) |_| { |
| ... | @@ -504,14 +509,12 @@ pub fn start(io: Io, options: Options) Node { | ... | @@ -504,14 +509,12 @@ pub fn start(io: Io, options: Options) Node { |
| 504 | } else |err| switch (err) { | 509 | } else |err| switch (err) { |
| 505 | error.Canceled => { | 510 | error.Canceled => { |
| 506 | io.recancel(); | 511 | io.recancel(); |
| 507 | return Node.none; | 512 | return .none; |
| 508 | }, | 513 | }, |
| 509 | } | 514 | } |
| 510 | } | 515 | } |
| 511 | 516 | ||
| 512 | if (global_progress.terminal_mode == .off) { | 517 | if (global_progress.terminal_mode == .off) return .none; |
| 513 | return Node.none; | ||
| 514 | } | ||
| 515 | 518 | ||
| 516 | if (have_sigwinch) { | 519 | if (have_sigwinch) { |
| 517 | const act: posix.Sigaction = .{ | 520 | const act: posix.Sigaction = .{ |
| ... | @@ -530,12 +533,12 @@ pub fn start(io: Io, options: Options) Node { | ... | @@ -530,12 +533,12 @@ pub fn start(io: Io, options: Options) Node { |
| 530 | global_progress.update_worker = future; | 533 | global_progress.update_worker = future; |
| 531 | } else |err| { | 534 | } else |err| { |
| 532 | global_progress.start_failure = .{ .spawn_update_worker = err }; | 535 | global_progress.start_failure = .{ .spawn_update_worker = err }; |
| 533 | return Node.none; | 536 | return .none; |
| 534 | } | 537 | } |
| 535 | }, | 538 | }, |
| 536 | else => |e| { | 539 | else => |e| { |
| 537 | global_progress.start_failure = .{ .parent_ipc = e }; | 540 | global_progress.start_failure = .{ .parent_ipc = e }; |
| 538 | return Node.none; | 541 | return .none; |
| 539 | }, | 542 | }, |
| 540 | } | 543 | } |
| 541 | 544 |
lib/std/Thread.zig+5-1| ... | @@ -726,7 +726,11 @@ const WindowsThreadImpl = struct { | ... | @@ -726,7 +726,11 @@ const WindowsThreadImpl = struct { |
| 726 | } | 726 | } |
| 727 | 727 | ||
| 728 | fn join(self: Impl) void { | 728 | fn join(self: Impl) void { |
| 729 | windows.WaitForSingleObjectEx(self.thread.thread_handle, windows.INFINITE, false) catch unreachable; | 729 | const infinite_timeout: windows.LARGE_INTEGER = std.math.minInt(windows.LARGE_INTEGER); |
| 730 | switch (windows.ntdll.NtWaitForSingleObject(self.thread.thread_handle, windows.FALSE, &infinite_timeout)) { | ||
| 731 | .WAIT_0 => {}, | ||
| 732 | else => |status| windows.unexpectedStatus(status) catch unreachable, | ||
| 733 | } | ||
| 730 | windows.CloseHandle(self.thread.thread_handle); | 734 | windows.CloseHandle(self.thread.thread_handle); |
| 731 | assert(self.thread.completion.load(.seq_cst) == .completed); | 735 | assert(self.thread.completion.load(.seq_cst) == .completed); |
| 732 | self.thread.free(); | 736 | self.thread.free(); |
lib/std/os/windows.zig+18-249| ... | @@ -2324,12 +2324,12 @@ pub fn GetProcessHeap() ?*HEAP { | ... | @@ -2324,12 +2324,12 @@ pub fn GetProcessHeap() ?*HEAP { |
| 2324 | // ref: um/winternl.h | 2324 | // ref: um/winternl.h |
| 2325 | 2325 | ||
| 2326 | pub const OBJECT_ATTRIBUTES = extern struct { | 2326 | pub const OBJECT_ATTRIBUTES = extern struct { |
| 2327 | Length: ULONG, | 2327 | Length: ULONG = @sizeOf(OBJECT_ATTRIBUTES), |
| 2328 | RootDirectory: ?HANDLE, | 2328 | RootDirectory: ?HANDLE = null, |
| 2329 | ObjectName: ?*UNICODE_STRING, | 2329 | ObjectName: ?*UNICODE_STRING = @constCast(&UNICODE_STRING.empty), |
| 2330 | Attributes: ATTRIBUTES, | 2330 | Attributes: ATTRIBUTES = .{}, |
| 2331 | SecurityDescriptor: ?*anyopaque, | 2331 | SecurityDescriptor: ?*anyopaque = null, |
| 2332 | SecurityQualityOfService: ?*anyopaque, | 2332 | SecurityQualityOfService: ?*anyopaque = null, |
| 2333 | 2333 | ||
| 2334 | // Valid values for the Attributes field | 2334 | // Valid values for the Attributes field |
| 2335 | pub const ATTRIBUTES = packed struct(ULONG) { | 2335 | pub const ATTRIBUTES = packed struct(ULONG) { |
| ... | @@ -2420,14 +2420,10 @@ pub fn OpenFile(sub_path_w: []const u16, options: OpenFileOptions) OpenError!HAN | ... | @@ -2420,14 +2420,10 @@ pub fn OpenFile(sub_path_w: []const u16, options: OpenFileOptions) OpenError!HAN |
| 2420 | .Buffer = @constCast(sub_path_w.ptr), | 2420 | .Buffer = @constCast(sub_path_w.ptr), |
| 2421 | }; | 2421 | }; |
| 2422 | const attr: OBJECT_ATTRIBUTES = .{ | 2422 | const attr: OBJECT_ATTRIBUTES = .{ |
| 2423 | .Length = @sizeOf(OBJECT_ATTRIBUTES), | ||
| 2424 | .RootDirectory = if (std.fs.path.isAbsoluteWindowsWtf16(sub_path_w)) null else options.dir, | 2423 | .RootDirectory = if (std.fs.path.isAbsoluteWindowsWtf16(sub_path_w)) null else options.dir, |
| 2425 | .Attributes = .{ | 2424 | .Attributes = .{ .INHERIT = if (options.sa) |sa| sa.bInheritHandle != FALSE else false }, |
| 2426 | .INHERIT = if (options.sa) |sa| sa.bInheritHandle != FALSE else false, | ||
| 2427 | }, | ||
| 2428 | .ObjectName = &nt_name, | 2425 | .ObjectName = &nt_name, |
| 2429 | .SecurityDescriptor = if (options.sa) |ptr| ptr.lpSecurityDescriptor else null, | 2426 | .SecurityDescriptor = if (options.sa) |ptr| ptr.lpSecurityDescriptor else null, |
| 2430 | .SecurityQualityOfService = null, | ||
| 2431 | }; | 2427 | }; |
| 2432 | var io: IO_STATUS_BLOCK = undefined; | 2428 | var io: IO_STATUS_BLOCK = undefined; |
| 2433 | while (true) { | 2429 | while (true) { |
| ... | @@ -2475,7 +2471,8 @@ pub fn OpenFile(sub_path_w: []const u16, options: OpenFileOptions) OpenError!HAN | ... | @@ -2475,7 +2471,8 @@ pub fn OpenFile(sub_path_w: []const u16, options: OpenFileOptions) OpenError!HAN |
| 2475 | // call has failed. There is not really a sane way to handle | 2471 | // call has failed. There is not really a sane way to handle |
| 2476 | // this other than retrying the creation after the OS finishes | 2472 | // this other than retrying the creation after the OS finishes |
| 2477 | // the deletion. | 2473 | // the deletion. |
| 2478 | _ = kernel32.SleepEx(1, TRUE); | 2474 | const delay_one_ms: LARGE_INTEGER = -(std.time.ns_per_ms / 100); |
| 2475 | _ = ntdll.NtDelayExecution(TRUE, &delay_one_ms); | ||
| 2479 | continue; | 2476 | continue; |
| 2480 | }, | 2477 | }, |
| 2481 | .VIRUS_INFECTED, .VIRUS_DELETED => return error.AntivirusInterference, | 2478 | .VIRUS_INFECTED, .VIRUS_DELETED => return error.AntivirusInterference, |
| ... | @@ -2506,151 +2503,6 @@ pub fn GetCurrentThreadId() DWORD { | ... | @@ -2506,151 +2503,6 @@ pub fn GetCurrentThreadId() DWORD { |
| 2506 | pub fn GetLastError() Win32Error { | 2503 | pub fn GetLastError() Win32Error { |
| 2507 | return @enumFromInt(teb().LastErrorValue); | 2504 | return @enumFromInt(teb().LastErrorValue); |
| 2508 | } | 2505 | } |
| 2509 | |||
| 2510 | pub const CreatePipeError = error{ Unexpected, SystemResources }; | ||
| 2511 | |||
| 2512 | var npfs: ?HANDLE = null; | ||
| 2513 | |||
| 2514 | /// A Zig wrapper around `NtCreateNamedPipeFile` and `NtCreateFile` syscalls. | ||
| 2515 | /// It implements similar behavior to `CreatePipe` and is meant to serve | ||
| 2516 | /// as a direct substitute for that call. | ||
| 2517 | pub fn CreatePipe(rd: *HANDLE, wr: *HANDLE, sattr: *const SECURITY_ATTRIBUTES) CreatePipeError!void { | ||
| 2518 | // Up to NT 5.2 (Windows XP/Server 2003), `CreatePipe` would generate a pipe similar to: | ||
| 2519 | // | ||
| 2520 | // \??\pipe\Win32Pipes.{pid}.{count} | ||
| 2521 | // | ||
| 2522 | // where `pid` is the process id and count is a incrementing counter. | ||
| 2523 | // The implementation was changed after NT 6.0 (Vista) to open a handle to the Named Pipe File System | ||
| 2524 | // and use that as the root directory for `NtCreateNamedPipeFile`. | ||
| 2525 | // This object is visible under the NPFS but has no filename attached to it. | ||
| 2526 | // | ||
| 2527 | // This implementation replicates how `CreatePipe` works in modern Windows versions. | ||
| 2528 | const opt_dev_handle = @atomicLoad(?HANDLE, &npfs, .seq_cst); | ||
| 2529 | const dev_handle = opt_dev_handle orelse blk: { | ||
| 2530 | const str = std.unicode.utf8ToUtf16LeStringLiteral("\\Device\\NamedPipe\\"); | ||
| 2531 | const len: u16 = @truncate(str.len * @sizeOf(u16)); | ||
| 2532 | const name: UNICODE_STRING = .{ | ||
| 2533 | .Length = len, | ||
| 2534 | .MaximumLength = len, | ||
| 2535 | .Buffer = @ptrCast(@constCast(str)), | ||
| 2536 | }; | ||
| 2537 | const attrs: OBJECT_ATTRIBUTES = .{ | ||
| 2538 | .ObjectName = @constCast(&name), | ||
| 2539 | .Length = @sizeOf(OBJECT_ATTRIBUTES), | ||
| 2540 | .RootDirectory = null, | ||
| 2541 | .Attributes = .{}, | ||
| 2542 | .SecurityDescriptor = null, | ||
| 2543 | .SecurityQualityOfService = null, | ||
| 2544 | }; | ||
| 2545 | |||
| 2546 | var iosb: IO_STATUS_BLOCK = undefined; | ||
| 2547 | var handle: HANDLE = undefined; | ||
| 2548 | switch (ntdll.NtCreateFile( | ||
| 2549 | &handle, | ||
| 2550 | .{ | ||
| 2551 | .STANDARD = .{ .SYNCHRONIZE = true }, | ||
| 2552 | .GENERIC = .{ .READ = true }, | ||
| 2553 | }, | ||
| 2554 | @constCast(&attrs), | ||
| 2555 | &iosb, | ||
| 2556 | null, | ||
| 2557 | .{}, | ||
| 2558 | .VALID_FLAGS, | ||
| 2559 | .OPEN, | ||
| 2560 | .{ .IO = .SYNCHRONOUS_NONALERT }, | ||
| 2561 | null, | ||
| 2562 | 0, | ||
| 2563 | )) { | ||
| 2564 | .SUCCESS => {}, | ||
| 2565 | // Judging from the ReactOS sources this is technically possible. | ||
| 2566 | .INSUFFICIENT_RESOURCES => return error.SystemResources, | ||
| 2567 | .INVALID_PARAMETER => unreachable, | ||
| 2568 | else => |e| return unexpectedStatus(e), | ||
| 2569 | } | ||
| 2570 | if (@cmpxchgStrong(?HANDLE, &npfs, null, handle, .seq_cst, .seq_cst)) |xchg| { | ||
| 2571 | CloseHandle(handle); | ||
| 2572 | break :blk xchg.?; | ||
| 2573 | } else break :blk handle; | ||
| 2574 | }; | ||
| 2575 | |||
| 2576 | const name: UNICODE_STRING = .{ .Buffer = null, .Length = 0, .MaximumLength = 0 }; | ||
| 2577 | var attrs: OBJECT_ATTRIBUTES = .{ | ||
| 2578 | .ObjectName = @constCast(&name), | ||
| 2579 | .Length = @sizeOf(OBJECT_ATTRIBUTES), | ||
| 2580 | .RootDirectory = dev_handle, | ||
| 2581 | .Attributes = .{ .INHERIT = sattr.bInheritHandle != FALSE }, | ||
| 2582 | .SecurityDescriptor = sattr.lpSecurityDescriptor, | ||
| 2583 | .SecurityQualityOfService = null, | ||
| 2584 | }; | ||
| 2585 | |||
| 2586 | // 120 second relative timeout in 100ns units. | ||
| 2587 | const default_timeout: LARGE_INTEGER = (-120 * std.time.ns_per_s) / 100; | ||
| 2588 | var iosb: IO_STATUS_BLOCK = undefined; | ||
| 2589 | var read: HANDLE = undefined; | ||
| 2590 | switch (ntdll.NtCreateNamedPipeFile( | ||
| 2591 | &read, | ||
| 2592 | .{ | ||
| 2593 | .SPECIFIC = .{ .FILE_PIPE = .{ | ||
| 2594 | .WRITE_ATTRIBUTES = true, | ||
| 2595 | } }, | ||
| 2596 | .STANDARD = .{ .SYNCHRONIZE = true }, | ||
| 2597 | .GENERIC = .{ .READ = true }, | ||
| 2598 | }, | ||
| 2599 | &attrs, | ||
| 2600 | &iosb, | ||
| 2601 | .{ .READ = true, .WRITE = true }, | ||
| 2602 | .CREATE, | ||
| 2603 | .{ .IO = .SYNCHRONOUS_NONALERT }, | ||
| 2604 | .{ .TYPE = .BYTE_STREAM }, | ||
| 2605 | .{ .MODE = .BYTE_STREAM }, | ||
| 2606 | .{ .OPERATION = .QUEUE }, | ||
| 2607 | 1, | ||
| 2608 | 4096, | ||
| 2609 | 4096, | ||
| 2610 | @constCast(&default_timeout), | ||
| 2611 | )) { | ||
| 2612 | .SUCCESS => {}, | ||
| 2613 | .INVALID_PARAMETER => unreachable, | ||
| 2614 | .INSUFFICIENT_RESOURCES => return error.SystemResources, | ||
| 2615 | else => |e| return unexpectedStatus(e), | ||
| 2616 | } | ||
| 2617 | errdefer CloseHandle(read); | ||
| 2618 | |||
| 2619 | attrs.RootDirectory = read; | ||
| 2620 | |||
| 2621 | var write: HANDLE = undefined; | ||
| 2622 | switch (ntdll.NtCreateFile( | ||
| 2623 | &write, | ||
| 2624 | .{ | ||
| 2625 | .SPECIFIC = .{ .FILE_PIPE = .{ | ||
| 2626 | .READ_ATTRIBUTES = true, | ||
| 2627 | } }, | ||
| 2628 | .STANDARD = .{ .SYNCHRONIZE = true }, | ||
| 2629 | .GENERIC = .{ .WRITE = true }, | ||
| 2630 | }, | ||
| 2631 | &attrs, | ||
| 2632 | &iosb, | ||
| 2633 | null, | ||
| 2634 | .{}, | ||
| 2635 | .VALID_FLAGS, | ||
| 2636 | .OPEN, | ||
| 2637 | .{ | ||
| 2638 | .IO = .SYNCHRONOUS_NONALERT, | ||
| 2639 | .NON_DIRECTORY_FILE = true, | ||
| 2640 | }, | ||
| 2641 | null, | ||
| 2642 | 0, | ||
| 2643 | )) { | ||
| 2644 | .SUCCESS => {}, | ||
| 2645 | .INVALID_PARAMETER => unreachable, | ||
| 2646 | .INSUFFICIENT_RESOURCES => return error.SystemResources, | ||
| 2647 | else => |e| return unexpectedStatus(e), | ||
| 2648 | } | ||
| 2649 | |||
| 2650 | rd.* = read; | ||
| 2651 | wr.* = write; | ||
| 2652 | } | ||
| 2653 | |||
| 2654 | /// A Zig wrapper around `NtDeviceIoControlFile` and `NtFsControlFile` syscalls. | 2506 | /// A Zig wrapper around `NtDeviceIoControlFile` and `NtFsControlFile` syscalls. |
| 2655 | /// It implements similar behavior to `DeviceIoControl` and is meant to serve | 2507 | /// It implements similar behavior to `DeviceIoControl` and is meant to serve |
| 2656 | /// as a direct substitute for that call. | 2508 | /// as a direct substitute for that call. |
| ... | @@ -2707,66 +2559,6 @@ pub fn GetOverlappedResult(h: HANDLE, overlapped: *OVERLAPPED, wait: bool) !DWOR | ... | @@ -2707,66 +2559,6 @@ pub fn GetOverlappedResult(h: HANDLE, overlapped: *OVERLAPPED, wait: bool) !DWOR |
| 2707 | return bytes; | 2559 | return bytes; |
| 2708 | } | 2560 | } |
| 2709 | 2561 | ||
| 2710 | pub const SetHandleInformationError = error{Unexpected}; | ||
| 2711 | |||
| 2712 | pub fn SetHandleInformation(h: HANDLE, mask: DWORD, flags: DWORD) SetHandleInformationError!void { | ||
| 2713 | if (kernel32.SetHandleInformation(h, mask, flags) == 0) { | ||
| 2714 | switch (GetLastError()) { | ||
| 2715 | else => |err| return unexpectedError(err), | ||
| 2716 | } | ||
| 2717 | } | ||
| 2718 | } | ||
| 2719 | |||
| 2720 | pub const WaitForSingleObjectError = error{ | ||
| 2721 | WaitAbandoned, | ||
| 2722 | WaitTimeOut, | ||
| 2723 | Unexpected, | ||
| 2724 | }; | ||
| 2725 | |||
| 2726 | pub fn WaitForSingleObject(handle: HANDLE, milliseconds: DWORD) WaitForSingleObjectError!void { | ||
| 2727 | return WaitForSingleObjectEx(handle, milliseconds, false); | ||
| 2728 | } | ||
| 2729 | |||
| 2730 | pub fn WaitForSingleObjectEx(handle: HANDLE, milliseconds: DWORD, alertable: bool) WaitForSingleObjectError!void { | ||
| 2731 | switch (kernel32.WaitForSingleObjectEx(handle, milliseconds, @intFromBool(alertable))) { | ||
| 2732 | WAIT_ABANDONED => return error.WaitAbandoned, | ||
| 2733 | WAIT_OBJECT_0 => return, | ||
| 2734 | WAIT_TIMEOUT => return error.WaitTimeOut, | ||
| 2735 | WAIT_FAILED => switch (GetLastError()) { | ||
| 2736 | else => |err| return unexpectedError(err), | ||
| 2737 | }, | ||
| 2738 | else => return error.Unexpected, | ||
| 2739 | } | ||
| 2740 | } | ||
| 2741 | |||
| 2742 | pub fn WaitForMultipleObjectsEx(handles: []const HANDLE, waitAll: bool, milliseconds: DWORD, alertable: bool) !u32 { | ||
| 2743 | assert(handles.len > 0 and handles.len <= MAXIMUM_WAIT_OBJECTS); | ||
| 2744 | const nCount: DWORD = @as(DWORD, @intCast(handles.len)); | ||
| 2745 | switch (kernel32.WaitForMultipleObjectsEx( | ||
| 2746 | nCount, | ||
| 2747 | handles.ptr, | ||
| 2748 | @intFromBool(waitAll), | ||
| 2749 | milliseconds, | ||
| 2750 | @intFromBool(alertable), | ||
| 2751 | )) { | ||
| 2752 | WAIT_OBJECT_0...WAIT_OBJECT_0 + MAXIMUM_WAIT_OBJECTS => |n| { | ||
| 2753 | const handle_index = n - WAIT_OBJECT_0; | ||
| 2754 | assert(handle_index < nCount); | ||
| 2755 | return handle_index; | ||
| 2756 | }, | ||
| 2757 | WAIT_ABANDONED_0...WAIT_ABANDONED_0 + MAXIMUM_WAIT_OBJECTS => |n| { | ||
| 2758 | const handle_index = n - WAIT_ABANDONED_0; | ||
| 2759 | assert(handle_index < nCount); | ||
| 2760 | return error.WaitAbandoned; | ||
| 2761 | }, | ||
| 2762 | WAIT_TIMEOUT => return error.WaitTimeOut, | ||
| 2763 | WAIT_FAILED => switch (GetLastError()) { | ||
| 2764 | else => |err| return unexpectedError(err), | ||
| 2765 | }, | ||
| 2766 | else => return error.Unexpected, | ||
| 2767 | } | ||
| 2768 | } | ||
| 2769 | |||
| 2770 | pub const CreateIoCompletionPortError = error{Unexpected}; | 2562 | pub const CreateIoCompletionPortError = error{Unexpected}; |
| 2771 | 2563 | ||
| 2772 | pub fn CreateIoCompletionPort( | 2564 | pub fn CreateIoCompletionPort( |
| ... | @@ -2878,21 +2670,6 @@ pub fn CloseHandle(hObject: HANDLE) void { | ... | @@ -2878,21 +2670,6 @@ pub fn CloseHandle(hObject: HANDLE) void { |
| 2878 | assert(ntdll.NtClose(hObject) == .SUCCESS); | 2670 | assert(ntdll.NtClose(hObject) == .SUCCESS); |
| 2879 | } | 2671 | } |
| 2880 | 2672 | ||
| 2881 | pub const GetStdHandleError = error{ | ||
| 2882 | NoStandardHandleAttached, | ||
| 2883 | Unexpected, | ||
| 2884 | }; | ||
| 2885 | |||
| 2886 | pub fn GetStdHandle(handle_id: DWORD) GetStdHandleError!HANDLE { | ||
| 2887 | const handle = kernel32.GetStdHandle(handle_id) orelse return error.NoStandardHandleAttached; | ||
| 2888 | if (handle == INVALID_HANDLE_VALUE) { | ||
| 2889 | switch (GetLastError()) { | ||
| 2890 | else => |err| return unexpectedError(err), | ||
| 2891 | } | ||
| 2892 | } | ||
| 2893 | return handle; | ||
| 2894 | } | ||
| 2895 | |||
| 2896 | pub const QueryObjectNameError = error{ | 2673 | pub const QueryObjectNameError = error{ |
| 2897 | AccessDenied, | 2674 | AccessDenied, |
| 2898 | InvalidHandle, | 2675 | InvalidHandle, |
| ... | @@ -3545,6 +3322,12 @@ pub fn nanoSecondsToFileTime(ns: Io.Timestamp) FILETIME { | ... | @@ -3545,6 +3322,12 @@ pub fn nanoSecondsToFileTime(ns: Io.Timestamp) FILETIME { |
| 3545 | }; | 3322 | }; |
| 3546 | } | 3323 | } |
| 3547 | 3324 | ||
| 3325 | /// Use RtlUpcaseUnicodeChar on Windows when not in comptime to avoid including a | ||
| 3326 | /// redundant copy of the uppercase data. | ||
| 3327 | pub inline fn toUpperWtf16(c: u16) u16 { | ||
| 3328 | return (if (builtin.os.tag != .windows or @inComptime()) nls.upcaseW else ntdll.RtlUpcaseUnicodeChar)(c); | ||
| 3329 | } | ||
| 3330 | |||
| 3548 | /// Compares two WTF16 strings using the equivalent functionality of | 3331 | /// Compares two WTF16 strings using the equivalent functionality of |
| 3549 | /// `RtlEqualUnicodeString` (with case insensitive comparison enabled). | 3332 | /// `RtlEqualUnicodeString` (with case insensitive comparison enabled). |
| 3550 | /// This function can be called on any target. | 3333 | /// This function can be called on any target. |
| ... | @@ -3598,19 +3381,12 @@ pub fn eqlIgnoreCaseWtf8(a: []const u8, b: []const u8) bool { | ... | @@ -3598,19 +3381,12 @@ pub fn eqlIgnoreCaseWtf8(a: []const u8, b: []const u8) bool { |
| 3598 | var a_wtf8_it = std.unicode.Wtf8View.initUnchecked(a).iterator(); | 3381 | var a_wtf8_it = std.unicode.Wtf8View.initUnchecked(a).iterator(); |
| 3599 | var b_wtf8_it = std.unicode.Wtf8View.initUnchecked(b).iterator(); | 3382 | var b_wtf8_it = std.unicode.Wtf8View.initUnchecked(b).iterator(); |
| 3600 | 3383 | ||
| 3601 | // Use RtlUpcaseUnicodeChar on Windows when not in comptime to avoid including a | ||
| 3602 | // redundant copy of the uppercase data. | ||
| 3603 | const upcaseImpl = switch (builtin.os.tag) { | ||
| 3604 | .windows => if (@inComptime()) nls.upcaseW else ntdll.RtlUpcaseUnicodeChar, | ||
| 3605 | else => nls.upcaseW, | ||
| 3606 | }; | ||
| 3607 | |||
| 3608 | while (true) { | 3384 | while (true) { |
| 3609 | const a_cp = a_wtf8_it.nextCodepoint() orelse break; | 3385 | const a_cp = a_wtf8_it.nextCodepoint() orelse break; |
| 3610 | const b_cp = b_wtf8_it.nextCodepoint() orelse return false; | 3386 | const b_cp = b_wtf8_it.nextCodepoint() orelse return false; |
| 3611 | 3387 | ||
| 3612 | if (a_cp <= maxInt(u16) and b_cp <= maxInt(u16)) { | 3388 | if (a_cp <= maxInt(u16) and b_cp <= maxInt(u16)) { |
| 3613 | if (a_cp != b_cp and upcaseImpl(@intCast(a_cp)) != upcaseImpl(@intCast(b_cp))) { | 3389 | if (a_cp != b_cp and toUpperWtf16(@intCast(a_cp)) != toUpperWtf16(@intCast(b_cp))) { |
| 3614 | return false; | 3390 | return false; |
| 3615 | } | 3391 | } |
| 3616 | } else if (a_cp != b_cp) { | 3392 | } else if (a_cp != b_cp) { |
| ... | @@ -4098,15 +3874,6 @@ pub const Win32Error = @import("windows/win32error.zig").Win32Error; | ... | @@ -4098,15 +3874,6 @@ pub const Win32Error = @import("windows/win32error.zig").Win32Error; |
| 4098 | pub const LANG = @import("windows/lang.zig"); | 3874 | pub const LANG = @import("windows/lang.zig"); |
| 4099 | pub const SUBLANG = @import("windows/sublang.zig"); | 3875 | pub const SUBLANG = @import("windows/sublang.zig"); |
| 4100 | 3876 | ||
| 4101 | /// The standard input device. Initially, this is the console input buffer, CONIN$. | ||
| 4102 | pub const STD_INPUT_HANDLE = maxInt(DWORD) - 10 + 1; | ||
| 4103 | |||
| 4104 | /// The standard output device. Initially, this is the active console screen buffer, CONOUT$. | ||
| 4105 | pub const STD_OUTPUT_HANDLE = maxInt(DWORD) - 11 + 1; | ||
| 4106 | |||
| 4107 | /// The standard error device. Initially, this is the active console screen buffer, CONOUT$. | ||
| 4108 | pub const STD_ERROR_HANDLE = maxInt(DWORD) - 12 + 1; | ||
| 4109 | |||
| 4110 | pub const BOOL = c_int; | 3877 | pub const BOOL = c_int; |
| 4111 | pub const BOOLEAN = BYTE; | 3878 | pub const BOOLEAN = BYTE; |
| 4112 | pub const BYTE = u8; | 3879 | pub const BYTE = u8; |
| ... | @@ -5244,6 +5011,8 @@ pub const UNICODE_STRING = extern struct { | ... | @@ -5244,6 +5011,8 @@ pub const UNICODE_STRING = extern struct { |
| 5244 | Length: c_ushort, | 5011 | Length: c_ushort, |
| 5245 | MaximumLength: c_ushort, | 5012 | MaximumLength: c_ushort, |
| 5246 | Buffer: ?[*]WCHAR, | 5013 | Buffer: ?[*]WCHAR, |
| 5014 | |||
| 5015 | pub const empty: UNICODE_STRING = .{ .Length = 0, .MaximumLength = 0, .Buffer = null }; | ||
| 5247 | }; | 5016 | }; |
| 5248 | 5017 | ||
| 5249 | pub const ACTIVATION_CONTEXT_DATA = opaque {}; | 5018 | pub const ACTIVATION_CONTEXT_DATA = opaque {}; |
lib/std/os/windows/kernel32.zig-108| ... | @@ -12,8 +12,6 @@ const FILETIME = windows.FILETIME; | ... | @@ -12,8 +12,6 @@ const FILETIME = windows.FILETIME; |
| 12 | const HANDLE = windows.HANDLE; | 12 | const HANDLE = windows.HANDLE; |
| 13 | const HANDLER_ROUTINE = windows.HANDLER_ROUTINE; | 13 | const HANDLER_ROUTINE = windows.HANDLER_ROUTINE; |
| 14 | const HMODULE = windows.HMODULE; | 14 | const HMODULE = windows.HMODULE; |
| 15 | const INIT_ONCE = windows.INIT_ONCE; | ||
| 16 | const INIT_ONCE_FN = windows.INIT_ONCE_FN; | ||
| 17 | const LARGE_INTEGER = windows.LARGE_INTEGER; | 15 | const LARGE_INTEGER = windows.LARGE_INTEGER; |
| 18 | const LPCSTR = windows.LPCSTR; | 16 | const LPCSTR = windows.LPCSTR; |
| 19 | const LPCVOID = windows.LPCVOID; | 17 | const LPCVOID = windows.LPCVOID; |
| ... | @@ -24,7 +22,6 @@ const LPWSTR = windows.LPWSTR; | ... | @@ -24,7 +22,6 @@ const LPWSTR = windows.LPWSTR; |
| 24 | const MODULEENTRY32 = windows.MODULEENTRY32; | 22 | const MODULEENTRY32 = windows.MODULEENTRY32; |
| 25 | const OVERLAPPED = windows.OVERLAPPED; | 23 | const OVERLAPPED = windows.OVERLAPPED; |
| 26 | const OVERLAPPED_ENTRY = windows.OVERLAPPED_ENTRY; | 24 | const OVERLAPPED_ENTRY = windows.OVERLAPPED_ENTRY; |
| 27 | const PMEMORY_BASIC_INFORMATION = windows.PMEMORY_BASIC_INFORMATION; | ||
| 28 | const PROCESS_INFORMATION = windows.PROCESS_INFORMATION; | 25 | const PROCESS_INFORMATION = windows.PROCESS_INFORMATION; |
| 29 | const SECURITY_ATTRIBUTES = windows.SECURITY_ATTRIBUTES; | 26 | const SECURITY_ATTRIBUTES = windows.SECURITY_ATTRIBUTES; |
| 30 | const SIZE_T = windows.SIZE_T; | 27 | const SIZE_T = windows.SIZE_T; |
| ... | @@ -37,7 +34,6 @@ const ULONG = windows.ULONG; | ... | @@ -37,7 +34,6 @@ const ULONG = windows.ULONG; |
| 37 | const ULONG_PTR = windows.ULONG_PTR; | 34 | const ULONG_PTR = windows.ULONG_PTR; |
| 38 | const va_list = windows.va_list; | 35 | const va_list = windows.va_list; |
| 39 | const WCHAR = windows.WCHAR; | 36 | const WCHAR = windows.WCHAR; |
| 40 | const WIN32_FIND_DATAW = windows.WIN32_FIND_DATAW; | ||
| 41 | const Win32Error = windows.Win32Error; | 37 | const Win32Error = windows.Win32Error; |
| 42 | const WORD = windows.WORD; | 38 | const WORD = windows.WORD; |
| 43 | 39 | ||
| ... | @@ -59,39 +55,6 @@ pub extern "kernel32" fn CancelIo( | ... | @@ -59,39 +55,6 @@ pub extern "kernel32" fn CancelIo( |
| 59 | hFile: HANDLE, | 55 | hFile: HANDLE, |
| 60 | ) callconv(.winapi) BOOL; | 56 | ) callconv(.winapi) BOOL; |
| 61 | 57 | ||
| 62 | // TODO: Wrapper around NtCancelIoFileEx. | ||
| 63 | pub extern "kernel32" fn CancelIoEx( | ||
| 64 | hFile: HANDLE, | ||
| 65 | lpOverlapped: ?*OVERLAPPED, | ||
| 66 | ) callconv(.winapi) BOOL; | ||
| 67 | |||
| 68 | pub extern "kernel32" fn CreateFileW( | ||
| 69 | lpFileName: LPCWSTR, | ||
| 70 | dwDesiredAccess: ACCESS_MASK, | ||
| 71 | dwShareMode: DWORD, | ||
| 72 | lpSecurityAttributes: ?*SECURITY_ATTRIBUTES, | ||
| 73 | dwCreationDisposition: DWORD, | ||
| 74 | dwFlagsAndAttributes: DWORD, | ||
| 75 | hTemplateFile: ?HANDLE, | ||
| 76 | ) callconv(.winapi) HANDLE; | ||
| 77 | |||
| 78 | // TODO A bunch of logic around NtCreateNamedPipe | ||
| 79 | pub extern "kernel32" fn CreateNamedPipeW( | ||
| 80 | lpName: LPCWSTR, | ||
| 81 | dwOpenMode: DWORD, | ||
| 82 | dwPipeMode: DWORD, | ||
| 83 | nMaxInstances: DWORD, | ||
| 84 | nOutBufferSize: DWORD, | ||
| 85 | nInBufferSize: DWORD, | ||
| 86 | nDefaultTimeOut: DWORD, | ||
| 87 | lpSecurityAttributes: ?*const SECURITY_ATTRIBUTES, | ||
| 88 | ) callconv(.winapi) HANDLE; | ||
| 89 | |||
| 90 | // TODO: Matches `STD_*_HANDLE` to peb().ProcessParameters.Standard* | ||
| 91 | pub extern "kernel32" fn GetStdHandle( | ||
| 92 | nStdHandle: DWORD, | ||
| 93 | ) callconv(.winapi) ?HANDLE; | ||
| 94 | |||
| 95 | // TODO: Wrapper around NtSetInformationFile + `FILE_POSITION_INFORMATION`. | 58 | // TODO: Wrapper around NtSetInformationFile + `FILE_POSITION_INFORMATION`. |
| 96 | // `FILE_STANDARD_INFORMATION` is also used if dwMoveMethod is `FILE_END` | 59 | // `FILE_STANDARD_INFORMATION` is also used if dwMoveMethod is `FILE_END` |
| 97 | pub extern "kernel32" fn SetFilePointerEx( | 60 | pub extern "kernel32" fn SetFilePointerEx( |
| ... | @@ -117,11 +80,6 @@ pub extern "kernel32" fn WriteFile( | ... | @@ -117,11 +80,6 @@ pub extern "kernel32" fn WriteFile( |
| 117 | in_out_lpOverlapped: ?*OVERLAPPED, | 80 | in_out_lpOverlapped: ?*OVERLAPPED, |
| 118 | ) callconv(.winapi) BOOL; | 81 | ) callconv(.winapi) BOOL; |
| 119 | 82 | ||
| 120 | // TODO: Wrapper around GetStdHandle + NtFlushBuffersFile. | ||
| 121 | pub extern "kernel32" fn FlushFileBuffers( | ||
| 122 | hFile: HANDLE, | ||
| 123 | ) callconv(.winapi) BOOL; | ||
| 124 | |||
| 125 | // TODO: Wrapper around NtSetInformationFile + `FILE_IO_COMPLETION_NOTIFICATION_INFORMATION`. | 83 | // TODO: Wrapper around NtSetInformationFile + `FILE_IO_COMPLETION_NOTIFICATION_INFORMATION`. |
| 126 | pub extern "kernel32" fn SetFileCompletionNotificationModes( | 84 | pub extern "kernel32" fn SetFileCompletionNotificationModes( |
| 127 | FileHandle: HANDLE, | 85 | FileHandle: HANDLE, |
| ... | @@ -143,24 +101,6 @@ pub extern "kernel32" fn GetSystemDirectoryW( | ... | @@ -143,24 +101,6 @@ pub extern "kernel32" fn GetSystemDirectoryW( |
| 143 | 101 | ||
| 144 | // I/O - Kernel Objects | 102 | // I/O - Kernel Objects |
| 145 | 103 | ||
| 146 | // TODO: Wrapper around GetStdHandle + NtDuplicateObject. | ||
| 147 | pub extern "kernel32" fn DuplicateHandle( | ||
| 148 | hSourceProcessHandle: HANDLE, | ||
| 149 | hSourceHandle: HANDLE, | ||
| 150 | hTargetProcessHandle: HANDLE, | ||
| 151 | lpTargetHandle: *HANDLE, | ||
| 152 | dwDesiredAccess: ACCESS_MASK, | ||
| 153 | bInheritHandle: BOOL, | ||
| 154 | dwOptions: DWORD, | ||
| 155 | ) callconv(.winapi) BOOL; | ||
| 156 | |||
| 157 | // TODO: Wrapper around GetStdHandle + NtQueryObject + NtSetInformationObject with .ObjectHandleFlagInformation. | ||
| 158 | pub extern "kernel32" fn SetHandleInformation( | ||
| 159 | hObject: HANDLE, | ||
| 160 | dwMask: DWORD, | ||
| 161 | dwFlags: DWORD, | ||
| 162 | ) callconv(.winapi) BOOL; | ||
| 163 | |||
| 164 | // TODO: Wrapper around NtRemoveIoCompletion. | 104 | // TODO: Wrapper around NtRemoveIoCompletion. |
| 165 | pub extern "kernel32" fn GetQueuedCompletionStatus( | 105 | pub extern "kernel32" fn GetQueuedCompletionStatus( |
| 166 | CompletionPort: HANDLE, | 106 | CompletionPort: HANDLE, |
| ... | @@ -210,37 +150,6 @@ pub extern "kernel32" fn TerminateProcess( | ... | @@ -210,37 +150,6 @@ pub extern "kernel32" fn TerminateProcess( |
| 210 | uExitCode: UINT, | 150 | uExitCode: UINT, |
| 211 | ) callconv(.winapi) BOOL; | 151 | ) callconv(.winapi) BOOL; |
| 212 | 152 | ||
| 213 | // TODO: WaitForSingleObjectEx with bAlertable=false. | ||
| 214 | pub extern "kernel32" fn WaitForSingleObject( | ||
| 215 | hHandle: HANDLE, | ||
| 216 | dwMilliseconds: DWORD, | ||
| 217 | ) callconv(.winapi) DWORD; | ||
| 218 | |||
| 219 | // TODO: Wrapper for GetStdHandle + NtWaitForSingleObject. | ||
| 220 | // Sets up an activation context before calling NtWaitForSingleObject. | ||
| 221 | pub extern "kernel32" fn WaitForSingleObjectEx( | ||
| 222 | hHandle: HANDLE, | ||
| 223 | dwMilliseconds: DWORD, | ||
| 224 | bAlertable: BOOL, | ||
| 225 | ) callconv(.winapi) DWORD; | ||
| 226 | |||
| 227 | // TODO: WaitForMultipleObjectsEx with alertable=false | ||
| 228 | pub extern "kernel32" fn WaitForMultipleObjects( | ||
| 229 | nCount: DWORD, | ||
| 230 | lpHandle: [*]const HANDLE, | ||
| 231 | bWaitAll: BOOL, | ||
| 232 | dwMilliseconds: DWORD, | ||
| 233 | ) callconv(.winapi) DWORD; | ||
| 234 | |||
| 235 | // TODO: Wrapper around NtWaitForMultipleObjects. | ||
| 236 | pub extern "kernel32" fn WaitForMultipleObjectsEx( | ||
| 237 | nCount: DWORD, | ||
| 238 | lpHandle: [*]const HANDLE, | ||
| 239 | bWaitAll: BOOL, | ||
| 240 | dwMilliseconds: DWORD, | ||
| 241 | bAlertable: BOOL, | ||
| 242 | ) callconv(.winapi) DWORD; | ||
| 243 | |||
| 244 | // Process Management | 153 | // Process Management |
| 245 | 154 | ||
| 246 | pub extern "kernel32" fn CreateProcessW( | 155 | pub extern "kernel32" fn CreateProcessW( |
| ... | @@ -256,12 +165,6 @@ pub extern "kernel32" fn CreateProcessW( | ... | @@ -256,12 +165,6 @@ pub extern "kernel32" fn CreateProcessW( |
| 256 | lpProcessInformation: *PROCESS_INFORMATION, | 165 | lpProcessInformation: *PROCESS_INFORMATION, |
| 257 | ) callconv(.winapi) BOOL; | 166 | ) callconv(.winapi) BOOL; |
| 258 | 167 | ||
| 259 | // TODO: implement via ntdll instead | ||
| 260 | pub extern "kernel32" fn SleepEx( | ||
| 261 | dwMilliseconds: DWORD, | ||
| 262 | bAlertable: BOOL, | ||
| 263 | ) callconv(.winapi) DWORD; | ||
| 264 | |||
| 265 | // TODO: Wrapper around NtQueryInformationProcess with `PROCESS_BASIC_INFORMATION`. | 168 | // TODO: Wrapper around NtQueryInformationProcess with `PROCESS_BASIC_INFORMATION`. |
| 266 | pub extern "kernel32" fn GetExitCodeProcess( | 169 | pub extern "kernel32" fn GetExitCodeProcess( |
| 267 | hProcess: HANDLE, | 170 | hProcess: HANDLE, |
| ... | @@ -436,14 +339,3 @@ pub extern "kernel32" fn FormatMessageW( | ... | @@ -436,14 +339,3 @@ pub extern "kernel32" fn FormatMessageW( |
| 436 | 339 | ||
| 437 | // TODO: Getter for teb().LastErrorValue. | 340 | // TODO: Getter for teb().LastErrorValue. |
| 438 | pub extern "kernel32" fn GetLastError() callconv(.winapi) Win32Error; | 341 | pub extern "kernel32" fn GetLastError() callconv(.winapi) Win32Error; |
| 439 | |||
| 440 | // TODO: Wrapper around RtlSetLastWin32Error. | ||
| 441 | pub extern "kernel32" fn SetLastError( | ||
| 442 | dwErrCode: Win32Error, | ||
| 443 | ) callconv(.winapi) void; | ||
| 444 | |||
| 445 | // Everything Else | ||
| 446 | |||
| 447 | pub extern "kernel32" fn GetSystemInfo( | ||
| 448 | lpSystemInfo: *SYSTEM_INFO, | ||
| 449 | ) callconv(.winapi) void; |
lib/std/os/windows/ntdll.zig+9-4| ... | @@ -407,6 +407,11 @@ pub extern "ntdll" fn NtCreateNamedPipeFile( | ... | @@ -407,6 +407,11 @@ pub extern "ntdll" fn NtCreateNamedPipeFile( |
| 407 | DefaultTimeout: ?*const LARGE_INTEGER, | 407 | DefaultTimeout: ?*const LARGE_INTEGER, |
| 408 | ) callconv(.winapi) NTSTATUS; | 408 | ) callconv(.winapi) NTSTATUS; |
| 409 | 409 | ||
| 410 | pub extern "ntdll" fn NtFlushBuffersFile( | ||
| 411 | FileHandle: HANDLE, | ||
| 412 | IoStatusBlock: *IO_STATUS_BLOCK, | ||
| 413 | ) callconv(.winapi) NTSTATUS; | ||
| 414 | |||
| 410 | pub extern "ntdll" fn NtMapViewOfSection( | 415 | pub extern "ntdll" fn NtMapViewOfSection( |
| 411 | SectionHandle: HANDLE, | 416 | SectionHandle: HANDLE, |
| 412 | ProcessHandle: HANDLE, | 417 | ProcessHandle: HANDLE, |
| ... | @@ -590,7 +595,7 @@ pub extern "ntdll" fn NtOpenThread( | ... | @@ -590,7 +595,7 @@ pub extern "ntdll" fn NtOpenThread( |
| 590 | 595 | ||
| 591 | pub extern "ntdll" fn NtCancelSynchronousIoFile( | 596 | pub extern "ntdll" fn NtCancelSynchronousIoFile( |
| 592 | ThreadHandle: HANDLE, | 597 | ThreadHandle: HANDLE, |
| 593 | RequestToCancel: ?*IO_STATUS_BLOCK, | 598 | IoRequestToCancel: ?*IO_STATUS_BLOCK, |
| 594 | IoStatusBlock: *IO_STATUS_BLOCK, | 599 | IoStatusBlock: *IO_STATUS_BLOCK, |
| 595 | ) callconv(.winapi) NTSTATUS; | 600 | ) callconv(.winapi) NTSTATUS; |
| 596 | 601 | ||
| ... | @@ -606,13 +611,13 @@ pub extern "ntdll" fn NtDelayExecution( | ... | @@ -606,13 +611,13 @@ pub extern "ntdll" fn NtDelayExecution( |
| 606 | DelayInterval: *const LARGE_INTEGER, | 611 | DelayInterval: *const LARGE_INTEGER, |
| 607 | ) callconv(.winapi) NTSTATUS; | 612 | ) callconv(.winapi) NTSTATUS; |
| 608 | 613 | ||
| 609 | pub extern "ntdll" fn NtCancelIoFileEx( | 614 | pub extern "ntdll" fn NtCancelIoFile( |
| 610 | FileHandle: HANDLE, | 615 | FileHandle: HANDLE, |
| 611 | IoRequestToCancel: *const IO_STATUS_BLOCK, | ||
| 612 | IoStatusBlock: *IO_STATUS_BLOCK, | 616 | IoStatusBlock: *IO_STATUS_BLOCK, |
| 613 | ) callconv(.winapi) NTSTATUS; | 617 | ) callconv(.winapi) NTSTATUS; |
| 614 | 618 | ||
| 615 | pub extern "ntdll" fn NtCancelIoFile( | 619 | pub extern "ntdll" fn NtCancelIoFileEx( |
| 616 | FileHandle: HANDLE, | 620 | FileHandle: HANDLE, |
| 621 | IoRequestToCancel: *const IO_STATUS_BLOCK, | ||
| 617 | IoStatusBlock: *IO_STATUS_BLOCK, | 622 | IoStatusBlock: *IO_STATUS_BLOCK, |
| 618 | ) callconv(.winapi) NTSTATUS; | 623 | ) callconv(.winapi) NTSTATUS; |
lib/std/process/Environ.zig+411-245| ... | @@ -4,7 +4,7 @@ const builtin = @import("builtin"); | ... | @@ -4,7 +4,7 @@ const builtin = @import("builtin"); |
| 4 | const native_os = builtin.os.tag; | 4 | const native_os = builtin.os.tag; |
| 5 | 5 | ||
| 6 | const std = @import("../std.zig"); | 6 | const std = @import("../std.zig"); |
| 7 | const Allocator = std.mem.Allocator; | 7 | const Allocator = mem.Allocator; |
| 8 | const assert = std.debug.assert; | 8 | const assert = std.debug.assert; |
| 9 | const testing = std.testing; | 9 | const testing = std.testing; |
| 10 | const unicode = std.unicode; | 10 | const unicode = std.unicode; |
| ... | @@ -14,13 +14,6 @@ const mem = std.mem; | ... | @@ -14,13 +14,6 @@ const mem = std.mem; |
| 14 | /// Unmodified, unprocessed data provided by the operating system. | 14 | /// Unmodified, unprocessed data provided by the operating system. |
| 15 | block: Block, | 15 | block: Block, |
| 16 | 16 | ||
| 17 | pub const empty: Environ = .{ | ||
| 18 | .block = switch (Block) { | ||
| 19 | void => {}, | ||
| 20 | else => &.{}, | ||
| 21 | }, | ||
| 22 | }; | ||
| 23 | |||
| 24 | /// On WASI without libc, this is `void` because the environment has to be | 17 | /// On WASI without libc, this is `void` because the environment has to be |
| 25 | /// queried and heap-allocated at runtime. | 18 | /// queried and heap-allocated at runtime. |
| 26 | /// | 19 | /// |
| ... | @@ -28,13 +21,62 @@ pub const empty: Environ = .{ | ... | @@ -28,13 +21,62 @@ pub const empty: Environ = .{ |
| 28 | /// is modified, so a long-lived pointer cannot be used. Therefore, on this | 21 | /// is modified, so a long-lived pointer cannot be used. Therefore, on this |
| 29 | /// operating system `void` is also used. | 22 | /// operating system `void` is also used. |
| 30 | pub const Block = switch (native_os) { | 23 | pub const Block = switch (native_os) { |
| 31 | .windows => void, | 24 | .windows => GlobalBlock, |
| 32 | .wasi => switch (builtin.link_libc) { | 25 | .wasi => switch (builtin.link_libc) { |
| 33 | false => void, | 26 | false => GlobalBlock, |
| 34 | true => [:null]const ?[*:0]const u8, | 27 | true => PosixBlock, |
| 35 | }, | 28 | }, |
| 36 | .freestanding, .other => void, | 29 | .freestanding, .other => GlobalBlock, |
| 37 | else => [:null]const ?[*:0]const u8, | 30 | else => PosixBlock, |
| 31 | }; | ||
| 32 | |||
| 33 | pub const GlobalBlock = struct { | ||
| 34 | pub const global: GlobalBlock = .{}; | ||
| 35 | |||
| 36 | pub fn deinit(_: GlobalBlock, _: Allocator) void {} | ||
| 37 | }; | ||
| 38 | |||
| 39 | pub const PosixBlock = struct { | ||
| 40 | slice: [:null]const ?[*:0]const u8, | ||
| 41 | |||
| 42 | pub const empty: PosixBlock = .{ .slice = &.{} }; | ||
| 43 | |||
| 44 | pub fn deinit(block: PosixBlock, gpa: Allocator) void { | ||
| 45 | for (block.slice) |entry| gpa.free(mem.span(entry.?)); | ||
| 46 | gpa.free(block.slice); | ||
| 47 | } | ||
| 48 | |||
| 49 | pub const View = struct { | ||
| 50 | slice: []const [*:0]const u8, | ||
| 51 | |||
| 52 | pub fn isEmpty(v: View) bool { | ||
| 53 | return v.slice.len == 0; | ||
| 54 | } | ||
| 55 | }; | ||
| 56 | pub fn view(block: PosixBlock) View { | ||
| 57 | return .{ .slice = @ptrCast(block.slice) }; | ||
| 58 | } | ||
| 59 | }; | ||
| 60 | |||
| 61 | pub const WindowsBlock = struct { | ||
| 62 | slice: [:0]const u16, | ||
| 63 | |||
| 64 | pub const empty: WindowsBlock = .{ .slice = &.{0} }; | ||
| 65 | |||
| 66 | pub fn deinit(block: WindowsBlock, gpa: Allocator) void { | ||
| 67 | gpa.free(block.slice); | ||
| 68 | } | ||
| 69 | |||
| 70 | pub const View = struct { | ||
| 71 | ptr: [*:0]const u16, | ||
| 72 | |||
| 73 | pub fn isEmpty(v: View) bool { | ||
| 74 | return v.ptr[0] == 0; | ||
| 75 | } | ||
| 76 | }; | ||
| 77 | pub fn view(block: WindowsBlock) View { | ||
| 78 | return .{ .ptr = block.slice.ptr }; | ||
| 79 | } | ||
| 38 | }; | 80 | }; |
| 39 | 81 | ||
| 40 | pub const Map = struct { | 82 | pub const Map = struct { |
| ... | @@ -46,47 +88,64 @@ pub const Map = struct { | ... | @@ -46,47 +88,64 @@ pub const Map = struct { |
| 46 | pub const Size = usize; | 88 | pub const Size = usize; |
| 47 | 89 | ||
| 48 | pub const EnvNameHashContext = struct { | 90 | pub const EnvNameHashContext = struct { |
| 49 | fn upcase(c: u21) u21 { | ||
| 50 | if (c <= std.math.maxInt(u16)) | ||
| 51 | return std.os.windows.ntdll.RtlUpcaseUnicodeChar(@as(u16, @intCast(c))); | ||
| 52 | return c; | ||
| 53 | } | ||
| 54 | |||
| 55 | pub fn hash(self: @This(), s: []const u8) u32 { | 91 | pub fn hash(self: @This(), s: []const u8) u32 { |
| 56 | _ = self; | 92 | _ = self; |
| 57 | if (native_os == .windows) { | 93 | switch (native_os) { |
| 58 | var h = std.hash.Wyhash.init(0); | 94 | else => return std.array_hash_map.hashString(s), |
| 59 | var it = unicode.Wtf8View.initUnchecked(s).iterator(); | 95 | .windows => { |
| 60 | while (it.nextCodepoint()) |cp| { | 96 | var h = std.hash.Wyhash.init(0); |
| 61 | const cp_upper = upcase(cp); | 97 | var it = unicode.Wtf8View.initUnchecked(s).iterator(); |
| 62 | h.update(&[_]u8{ | 98 | while (it.nextCodepoint()) |cp| { |
| 63 | @as(u8, @intCast((cp_upper >> 16) & 0xff)), | 99 | const cp_upper = if (std.math.cast(u16, cp)) |wtf16| |
| 64 | @as(u8, @intCast((cp_upper >> 8) & 0xff)), | 100 | std.os.windows.toUpperWtf16(wtf16) |
| 65 | @as(u8, @intCast((cp_upper >> 0) & 0xff)), | 101 | else |
| 66 | }); | 102 | cp; |
| 67 | } | 103 | h.update(&[_]u8{ |
| 68 | return @truncate(h.final()); | 104 | @truncate(cp_upper >> 0), |
| 105 | @truncate(cp_upper >> 8), | ||
| 106 | @truncate(cp_upper >> 16), | ||
| 107 | }); | ||
| 108 | } | ||
| 109 | return @truncate(h.final()); | ||
| 110 | }, | ||
| 69 | } | 111 | } |
| 70 | return std.array_hash_map.hashString(s); | ||
| 71 | } | 112 | } |
| 72 | 113 | ||
| 73 | pub fn eql(self: @This(), a: []const u8, b: []const u8, b_index: usize) bool { | 114 | pub fn eql(self: @This(), a: []const u8, b: []const u8, b_index: usize) bool { |
| 74 | _ = self; | 115 | _ = self; |
| 75 | _ = b_index; | 116 | _ = b_index; |
| 76 | if (native_os == .windows) { | 117 | return eqlKeys(a, b); |
| 77 | var it_a = unicode.Wtf8View.initUnchecked(a).iterator(); | ||
| 78 | var it_b = unicode.Wtf8View.initUnchecked(b).iterator(); | ||
| 79 | while (true) { | ||
| 80 | const c_a = it_a.nextCodepoint() orelse break; | ||
| 81 | const c_b = it_b.nextCodepoint() orelse return false; | ||
| 82 | if (upcase(c_a) != upcase(c_b)) | ||
| 83 | return false; | ||
| 84 | } | ||
| 85 | return if (it_b.nextCodepoint()) |_| false else true; | ||
| 86 | } | ||
| 87 | return std.array_hash_map.eqlString(a, b); | ||
| 88 | } | 118 | } |
| 89 | }; | 119 | }; |
| 120 | fn eqlKeys(a: []const u8, b: []const u8) bool { | ||
| 121 | return switch (native_os) { | ||
| 122 | else => std.array_hash_map.eqlString(a, b), | ||
| 123 | .windows => std.os.windows.eqlIgnoreCaseWtf8(a, b), | ||
| 124 | }; | ||
| 125 | } | ||
| 126 | |||
| 127 | fn validateKey(key: []const u8) bool { | ||
| 128 | switch (builtin.mode) { | ||
| 129 | .Debug, .ReleaseSafe => {}, | ||
| 130 | .ReleaseFast, .ReleaseSmall => return key.len > 0, | ||
| 131 | } | ||
| 132 | switch (native_os) { | ||
| 133 | else => return key.len > 0 and mem.findAny(u8, key, &.{ 0, '=' }) == null, | ||
| 134 | .windows => { | ||
| 135 | if (!unicode.wtf8ValidateSlice(key)) return false; | ||
| 136 | var it = unicode.Wtf8View.initUnchecked(key).iterator(); | ||
| 137 | switch (it.nextCodepoint() orelse return false) { | ||
| 138 | 0 => return false, | ||
| 139 | else => {}, | ||
| 140 | } | ||
| 141 | while (it.nextCodepoint()) |cp| switch (cp) { | ||
| 142 | 0, '=' => return false, | ||
| 143 | else => {}, | ||
| 144 | }; | ||
| 145 | return true; | ||
| 146 | }, | ||
| 147 | } | ||
| 148 | } | ||
| 90 | 149 | ||
| 91 | /// Create a Map backed by a specific allocator. | 150 | /// Create a Map backed by a specific allocator. |
| 92 | /// That allocator will be used for both backing allocations | 151 | /// That allocator will be used for both backing allocations |
| ... | @@ -108,21 +167,65 @@ pub const Map = struct { | ... | @@ -108,21 +167,65 @@ pub const Map = struct { |
| 108 | self.* = undefined; | 167 | self.* = undefined; |
| 109 | } | 168 | } |
| 110 | 169 | ||
| 111 | pub fn keys(m: *const Map) [][]const u8 { | 170 | pub fn keys(map: *const Map) [][]const u8 { |
| 112 | return m.array_hash_map.keys(); | 171 | return map.array_hash_map.keys(); |
| 113 | } | 172 | } |
| 114 | 173 | ||
| 115 | pub fn values(m: *const Map) [][]const u8 { | 174 | pub fn values(map: *const Map) [][]const u8 { |
| 116 | return m.array_hash_map.values(); | 175 | return map.array_hash_map.values(); |
| 176 | } | ||
| 177 | |||
| 178 | pub fn putPosixBlock(map: *Map, view: PosixBlock.View) Allocator.Error!void { | ||
| 179 | for (view.slice) |entry| { | ||
| 180 | var entry_i: usize = 0; | ||
| 181 | while (entry[entry_i] != 0 and entry[entry_i] != '=') : (entry_i += 1) {} | ||
| 182 | const key = entry[0..entry_i]; | ||
| 183 | |||
| 184 | var end_i: usize = entry_i; | ||
| 185 | while (entry[end_i] != 0) : (end_i += 1) {} | ||
| 186 | const value = entry[entry_i + 1 .. end_i]; | ||
| 187 | |||
| 188 | try map.put(key, value); | ||
| 189 | } | ||
| 190 | } | ||
| 191 | |||
| 192 | pub fn putWindowsBlock(map: *Map, view: WindowsBlock.View) Allocator.Error!void { | ||
| 193 | var i: usize = 0; | ||
| 194 | while (view.ptr[i] != 0) { | ||
| 195 | const key_start = i; | ||
| 196 | |||
| 197 | // There are some special environment variables that start with =, | ||
| 198 | // so we need a special case to not treat = as a key/value separator | ||
| 199 | // if it's the first character. | ||
| 200 | // https://devblogs.microsoft.com/oldnewthing/20100506-00/?p=14133 | ||
| 201 | if (view.ptr[key_start] == '=') i += 1; | ||
| 202 | |||
| 203 | while (view.ptr[i] != 0 and view.ptr[i] != '=') : (i += 1) {} | ||
| 204 | const key_w = view.ptr[key_start..i]; | ||
| 205 | const key = try unicode.wtf16LeToWtf8Alloc(map.allocator, key_w); | ||
| 206 | errdefer map.allocator.free(key); | ||
| 207 | |||
| 208 | if (view.ptr[i] == '=') i += 1; | ||
| 209 | |||
| 210 | const value_start = i; | ||
| 211 | while (view.ptr[i] != 0) : (i += 1) {} | ||
| 212 | const value_w = view.ptr[value_start..i]; | ||
| 213 | const value = try unicode.wtf16LeToWtf8Alloc(map.allocator, value_w); | ||
| 214 | errdefer map.allocator.free(value); | ||
| 215 | |||
| 216 | i += 1; // skip over null byte | ||
| 217 | |||
| 218 | try map.putMove(key, value); | ||
| 219 | } | ||
| 117 | } | 220 | } |
| 118 | 221 | ||
| 119 | /// Same as `put` but the key and value become owned by the Map rather | 222 | /// Same as `put` but the key and value become owned by the Map rather |
| 120 | /// than being copied. | 223 | /// than being copied. |
| 121 | /// If `putMove` fails, the ownership of key and value does not transfer. | 224 | /// If `putMove` fails, the ownership of key and value does not transfer. |
| 122 | /// On Windows `key` must be a valid [WTF-8](https://wtf-8.codeberg.page/) string. | 225 | /// On Windows `key` must be a valid [WTF-8](https://wtf-8.codeberg.page/) string. |
| 123 | pub fn putMove(self: *Map, key: []u8, value: []u8) !void { | 226 | pub fn putMove(self: *Map, key: []u8, value: []u8) Allocator.Error!void { |
| 227 | assert(validateKey(key)); | ||
| 124 | const gpa = self.allocator; | 228 | const gpa = self.allocator; |
| 125 | assert(unicode.wtf8ValidateSlice(key)); | ||
| 126 | const get_or_put = try self.array_hash_map.getOrPut(gpa, key); | 229 | const get_or_put = try self.array_hash_map.getOrPut(gpa, key); |
| 127 | if (get_or_put.found_existing) { | 230 | if (get_or_put.found_existing) { |
| 128 | gpa.free(get_or_put.key_ptr.*); | 231 | gpa.free(get_or_put.key_ptr.*); |
| ... | @@ -134,8 +237,8 @@ pub const Map = struct { | ... | @@ -134,8 +237,8 @@ pub const Map = struct { |
| 134 | 237 | ||
| 135 | /// `key` and `value` are copied into the Map. | 238 | /// `key` and `value` are copied into the Map. |
| 136 | /// On Windows `key` must be a valid [WTF-8](https://wtf-8.codeberg.page/) string. | 239 | /// On Windows `key` must be a valid [WTF-8](https://wtf-8.codeberg.page/) string. |
| 137 | pub fn put(self: *Map, key: []const u8, value: []const u8) !void { | 240 | pub fn put(self: *Map, key: []const u8, value: []const u8) Allocator.Error!void { |
| 138 | assert(unicode.wtf8ValidateSlice(key)); | 241 | assert(validateKey(key)); |
| 139 | const gpa = self.allocator; | 242 | const gpa = self.allocator; |
| 140 | const value_copy = try gpa.dupe(u8, value); | 243 | const value_copy = try gpa.dupe(u8, value); |
| 141 | errdefer gpa.free(value_copy); | 244 | errdefer gpa.free(value_copy); |
| ... | @@ -155,7 +258,7 @@ pub const Map = struct { | ... | @@ -155,7 +258,7 @@ pub const Map = struct { |
| 155 | /// The returned pointer is invalidated if the map resizes. | 258 | /// The returned pointer is invalidated if the map resizes. |
| 156 | /// On Windows `key` must be a valid [WTF-8](https://wtf-8.codeberg.page/) string. | 259 | /// On Windows `key` must be a valid [WTF-8](https://wtf-8.codeberg.page/) string. |
| 157 | pub fn getPtr(self: Map, key: []const u8) ?*[]const u8 { | 260 | pub fn getPtr(self: Map, key: []const u8) ?*[]const u8 { |
| 158 | assert(unicode.wtf8ValidateSlice(key)); | 261 | assert(validateKey(key)); |
| 159 | return self.array_hash_map.getPtr(key); | 262 | return self.array_hash_map.getPtr(key); |
| 160 | } | 263 | } |
| 161 | 264 | ||
| ... | @@ -164,11 +267,12 @@ pub const Map = struct { | ... | @@ -164,11 +267,12 @@ pub const Map = struct { |
| 164 | /// key is removed from the map. | 267 | /// key is removed from the map. |
| 165 | /// On Windows `key` must be a valid [WTF-8](https://wtf-8.codeberg.page/) string. | 268 | /// On Windows `key` must be a valid [WTF-8](https://wtf-8.codeberg.page/) string. |
| 166 | pub fn get(self: Map, key: []const u8) ?[]const u8 { | 269 | pub fn get(self: Map, key: []const u8) ?[]const u8 { |
| 167 | assert(unicode.wtf8ValidateSlice(key)); | 270 | assert(validateKey(key)); |
| 168 | return self.array_hash_map.get(key); | 271 | return self.array_hash_map.get(key); |
| 169 | } | 272 | } |
| 170 | 273 | ||
| 171 | pub fn contains(m: *const Map, key: []const u8) bool { | 274 | pub fn contains(m: *const Map, key: []const u8) bool { |
| 275 | assert(validateKey(key)); | ||
| 172 | return m.array_hash_map.contains(key); | 276 | return m.array_hash_map.contains(key); |
| 173 | } | 277 | } |
| 174 | 278 | ||
| ... | @@ -181,7 +285,7 @@ pub const Map = struct { | ... | @@ -181,7 +285,7 @@ pub const Map = struct { |
| 181 | /// This invalidates the value returned by get() for this key. | 285 | /// This invalidates the value returned by get() for this key. |
| 182 | /// On Windows `key` must be a valid [WTF-8](https://wtf-8.codeberg.page/) string. | 286 | /// On Windows `key` must be a valid [WTF-8](https://wtf-8.codeberg.page/) string. |
| 183 | pub fn swapRemove(self: *Map, key: []const u8) bool { | 287 | pub fn swapRemove(self: *Map, key: []const u8) bool { |
| 184 | assert(unicode.wtf8ValidateSlice(key)); | 288 | assert(validateKey(key)); |
| 185 | const kv = self.array_hash_map.fetchSwapRemove(key) orelse return false; | 289 | const kv = self.array_hash_map.fetchSwapRemove(key) orelse return false; |
| 186 | const gpa = self.allocator; | 290 | const gpa = self.allocator; |
| 187 | gpa.free(kv.key); | 291 | gpa.free(kv.key); |
| ... | @@ -198,7 +302,7 @@ pub const Map = struct { | ... | @@ -198,7 +302,7 @@ pub const Map = struct { |
| 198 | /// This invalidates the value returned by get() for this key. | 302 | /// This invalidates the value returned by get() for this key. |
| 199 | /// On Windows `key` must be a valid [WTF-8](https://wtf-8.codeberg.page/) string. | 303 | /// On Windows `key` must be a valid [WTF-8](https://wtf-8.codeberg.page/) string. |
| 200 | pub fn orderedRemove(self: *Map, key: []const u8) bool { | 304 | pub fn orderedRemove(self: *Map, key: []const u8) bool { |
| 201 | assert(unicode.wtf8ValidateSlice(key)); | 305 | assert(validateKey(key)); |
| 202 | const kv = self.array_hash_map.fetchOrderedRemove(key) orelse return false; | 306 | const kv = self.array_hash_map.fetchOrderedRemove(key) orelse return false; |
| 203 | const gpa = self.allocator; | 307 | const gpa = self.allocator; |
| 204 | gpa.free(kv.key); | 308 | gpa.free(kv.key); |
| ... | @@ -233,39 +337,41 @@ pub const Map = struct { | ... | @@ -233,39 +337,41 @@ pub const Map = struct { |
| 233 | 337 | ||
| 234 | /// Creates a null-delimited environment variable block in the format | 338 | /// Creates a null-delimited environment variable block in the format |
| 235 | /// expected by POSIX, from a hash map plus options. | 339 | /// expected by POSIX, from a hash map plus options. |
| 236 | pub fn createBlockPosix( | 340 | pub fn createPosixBlock( |
| 237 | map: *const Map, | 341 | map: *const Map, |
| 238 | arena: Allocator, | 342 | gpa: Allocator, |
| 239 | options: CreateBlockPosixOptions, | 343 | options: CreatePosixBlockOptions, |
| 240 | ) Allocator.Error![:null]?[*:0]u8 { | 344 | ) Allocator.Error!PosixBlock { |
| 241 | const ZigProgressAction = enum { nothing, edit, delete, add }; | 345 | const ZigProgressAction = enum { nothing, edit, delete, add }; |
| 242 | const zig_progress_action: ZigProgressAction = a: { | 346 | const zig_progress_action: ZigProgressAction = action: { |
| 243 | const fd = options.zig_progress_fd orelse break :a .nothing; | 347 | const fd = options.zig_progress_fd orelse break :action .nothing; |
| 244 | const exists = map.get("ZIG_PROGRESS") != null; | 348 | const exists = map.contains("ZIG_PROGRESS"); |
| 245 | if (fd >= 0) { | 349 | if (fd >= 0) { |
| 246 | break :a if (exists) .edit else .add; | 350 | break :action if (exists) .edit else .add; |
| 247 | } else { | 351 | } else { |
| 248 | if (exists) break :a .delete; | 352 | if (exists) break :action .delete; |
| 249 | } | 353 | } |
| 250 | break :a .nothing; | 354 | break :action .nothing; |
| 251 | }; | 355 | }; |
| 252 | 356 | ||
| 253 | const envp_count: usize = c: { | 357 | const envp = try gpa.allocSentinel(?[*:0]u8, len: { |
| 254 | var c: usize = map.count(); | 358 | var len: usize = map.count(); |
| 255 | switch (zig_progress_action) { | 359 | switch (zig_progress_action) { |
| 256 | .add => c += 1, | 360 | .add => len += 1, |
| 257 | .delete => c -= 1, | 361 | .delete => len -= 1, |
| 258 | .nothing, .edit => {}, | 362 | .nothing, .edit => {}, |
| 259 | } | 363 | } |
| 260 | break :c c; | 364 | break :len len; |
| 261 | }; | 365 | }, null); |
| 262 | 366 | var envp_len: usize = 0; | |
| 263 | const envp_buf = try arena.allocSentinel(?[*:0]u8, envp_count, null); | 367 | errdefer { |
| 264 | var i: usize = 0; | 368 | envp[envp_len] = null; |
| 369 | PosixBlock.deinit(.{ .slice = envp[0..envp_len :null] }, gpa); | ||
| 370 | } | ||
| 265 | 371 | ||
| 266 | if (zig_progress_action == .add) { | 372 | if (zig_progress_action == .add) { |
| 267 | envp_buf[i] = try std.fmt.allocPrintSentinel(arena, "ZIG_PROGRESS={d}", .{options.zig_progress_fd.?}, 0); | 373 | envp[envp_len] = try std.fmt.allocPrintSentinel(gpa, "ZIG_PROGRESS={d}", .{options.zig_progress_fd.?}, 0); |
| 268 | i += 1; | 374 | envp_len += 1; |
| 269 | } | 375 | } |
| 270 | 376 | ||
| 271 | { | 377 | { |
| ... | @@ -275,63 +381,81 @@ pub const Map = struct { | ... | @@ -275,63 +381,81 @@ pub const Map = struct { |
| 275 | .add => unreachable, | 381 | .add => unreachable, |
| 276 | .delete => continue, | 382 | .delete => continue, |
| 277 | .edit => { | 383 | .edit => { |
| 278 | envp_buf[i] = try std.fmt.allocPrintSentinel(arena, "{s}={d}", .{ | 384 | envp[envp_len] = try std.fmt.allocPrintSentinel(gpa, "{s}={d}", .{ |
| 279 | pair.key_ptr.*, options.zig_progress_fd.?, | 385 | pair.key_ptr.*, options.zig_progress_fd.?, |
| 280 | }, 0); | 386 | }, 0); |
| 281 | i += 1; | 387 | envp_len += 1; |
| 282 | continue; | 388 | continue; |
| 283 | }, | 389 | }, |
| 284 | .nothing => {}, | 390 | .nothing => {}, |
| 285 | }; | 391 | }; |
| 286 | 392 | ||
| 287 | envp_buf[i] = try std.fmt.allocPrintSentinel(arena, "{s}={s}", .{ pair.key_ptr.*, pair.value_ptr.* }, 0); | 393 | envp[envp_len] = try std.fmt.allocPrintSentinel(gpa, "{s}={s}", .{ pair.key_ptr.*, pair.value_ptr.* }, 0); |
| 288 | i += 1; | 394 | envp_len += 1; |
| 289 | } | 395 | } |
| 290 | } | 396 | } |
| 291 | 397 | ||
| 292 | assert(i == envp_count); | 398 | assert(envp_len == envp.len); |
| 293 | return envp_buf; | 399 | return .{ .slice = envp }; |
| 294 | } | 400 | } |
| 295 | 401 | ||
| 296 | /// Caller owns result. | 402 | /// Caller owns result. |
| 297 | pub fn createBlockWindows(map: *const Map, gpa: Allocator) error{ OutOfMemory, InvalidWtf8 }![:0]u16 { | 403 | pub fn createWindowsBlock( |
| 404 | map: *const Map, | ||
| 405 | gpa: Allocator, | ||
| 406 | options: CreateWindowsBlockOptions, | ||
| 407 | ) error{ OutOfMemory, InvalidWtf8 }!WindowsBlock { | ||
| 298 | // count bytes needed | 408 | // count bytes needed |
| 299 | const max_chars_needed = x: { | 409 | const max_chars_needed = max_chars_needed: { |
| 300 | // Only need 2 trailing NUL code units for an empty environment | 410 | var max_chars_needed: usize = "\x00".len; |
| 301 | var max_chars_needed: usize = if (map.count() == 0) 2 else 1; | ||
| 302 | var it = map.iterator(); | 411 | var it = map.iterator(); |
| 412 | if (options.zig_progress_handle) |handle| if (handle != std.os.windows.INVALID_HANDLE_VALUE) { | ||
| 413 | max_chars_needed += std.fmt.count("ZIG_PROGRESS={d}\x00", .{@intFromPtr(handle)}); | ||
| 414 | }; | ||
| 303 | while (it.next()) |pair| { | 415 | while (it.next()) |pair| { |
| 304 | // +1 for '=' | 416 | if (options.zig_progress_handle != null and |
| 305 | // +1 for null byte | 417 | eqlKeys(pair.key_ptr.*, "ZIG_PROGRESS")) continue; |
| 306 | max_chars_needed += pair.key_ptr.len + pair.value_ptr.len + 2; | 418 | max_chars_needed += pair.key_ptr.len + "=".len + pair.value_ptr.len + "\x00".len; |
| 307 | } | 419 | } |
| 308 | break :x max_chars_needed; | 420 | break :max_chars_needed @max("\x00\x00".len, max_chars_needed); |
| 309 | }; | 421 | }; |
| 310 | const result = try gpa.alloc(u16, max_chars_needed); | 422 | const block = try gpa.alloc(u16, max_chars_needed); |
| 311 | errdefer gpa.free(result); | 423 | errdefer gpa.free(block); |
| 312 | 424 | ||
| 313 | var it = map.iterator(); | ||
| 314 | var i: usize = 0; | 425 | var i: usize = 0; |
| 426 | if (options.zig_progress_handle) |handle| if (handle != std.os.windows.INVALID_HANDLE_VALUE) { | ||
| 427 | @memcpy( | ||
| 428 | block[i..][0.."ZIG_PROGRESS=".len], | ||
| 429 | &[_]u16{ 'Z', 'I', 'G', '_', 'P', 'R', 'O', 'G', 'R', 'E', 'S', 'S', '=' }, | ||
| 430 | ); | ||
| 431 | i += "ZIG_PROGRESS=".len; | ||
| 432 | var value_buf: [std.fmt.count("{d}", .{std.math.maxInt(usize)})]u8 = undefined; | ||
| 433 | const value = std.fmt.bufPrint(&value_buf, "{d}", .{@intFromPtr(handle)}) catch unreachable; | ||
| 434 | for (block[i..][0..value.len], value) |*r, v| r.* = v; | ||
| 435 | i += value.len; | ||
| 436 | block[i] = 0; | ||
| 437 | i += 1; | ||
| 438 | }; | ||
| 439 | var it = map.iterator(); | ||
| 315 | while (it.next()) |pair| { | 440 | while (it.next()) |pair| { |
| 316 | i += try unicode.wtf8ToWtf16Le(result[i..], pair.key_ptr.*); | 441 | i += try unicode.wtf8ToWtf16Le(block[i..], pair.key_ptr.*); |
| 317 | result[i] = '='; | 442 | block[i] = '='; |
| 318 | i += 1; | 443 | i += 1; |
| 319 | i += try unicode.wtf8ToWtf16Le(result[i..], pair.value_ptr.*); | 444 | i += try unicode.wtf8ToWtf16Le(block[i..], pair.value_ptr.*); |
| 320 | result[i] = 0; | 445 | block[i] = 0; |
| 321 | i += 1; | 446 | i += 1; |
| 322 | } | 447 | } |
| 323 | result[i] = 0; | ||
| 324 | i += 1; | ||
| 325 | // An empty environment is a special case that requires a redundant | 448 | // An empty environment is a special case that requires a redundant |
| 326 | // NUL terminator. CreateProcess will read the second code unit even | 449 | // NUL terminator. CreateProcess will read the second code unit even |
| 327 | // though theoretically the first should be enough to recognize that the | 450 | // though theoretically the first should be enough to recognize that the |
| 328 | // environment is empty (see https://nullprogram.com/blog/2023/08/23/) | 451 | // environment is empty (see https://nullprogram.com/blog/2023/08/23/) |
| 329 | if (map.count() == 0) { | 452 | for (0..2) |_| { |
| 330 | result[i] = 0; | 453 | block[i] = 0; |
| 331 | i += 1; | 454 | i += 1; |
| 332 | } | 455 | if (i >= 2) break; |
| 333 | const reallocated = try gpa.realloc(result, i); | 456 | } else unreachable; |
| 334 | return reallocated[0 .. i - 1 :0]; | 457 | const reallocated = try gpa.realloc(block, i); |
| 458 | return .{ .slice = reallocated[0 .. i - 1 :0] }; | ||
| 335 | } | 459 | } |
| 336 | }; | 460 | }; |
| 337 | 461 | ||
| ... | @@ -344,13 +468,14 @@ pub const CreateMapError = error{ | ... | @@ -344,13 +468,14 @@ pub const CreateMapError = error{ |
| 344 | 468 | ||
| 345 | /// Allocates a `Map` and copies environment block into it. | 469 | /// Allocates a `Map` and copies environment block into it. |
| 346 | pub fn createMap(env: Environ, allocator: Allocator) CreateMapError!Map { | 470 | pub fn createMap(env: Environ, allocator: Allocator) CreateMapError!Map { |
| 347 | if (native_os == .windows) | 471 | var map = Map.init(allocator); |
| 348 | return createMapWide(std.os.windows.peb().ProcessParameters.Environment, allocator); | 472 | errdefer map.deinit(); |
| 349 | 473 | if (native_os == .windows) { | |
| 350 | var result = Map.init(allocator); | 474 | const peb = std.os.windows.peb(); |
| 351 | errdefer result.deinit(); | 475 | assert(std.os.windows.ntdll.RtlEnterCriticalSection(peb.FastPebLock) == .SUCCESS); |
| 352 | 476 | defer assert(std.os.windows.ntdll.RtlLeaveCriticalSection(peb.FastPebLock) == .SUCCESS); | |
| 353 | if (native_os == .wasi and !builtin.link_libc) { | 477 | try map.putWindowsBlock(.{ .ptr = peb.ProcessParameters.Environment }); |
| 478 | } else if (native_os == .wasi and !builtin.link_libc) { | ||
| 354 | var environ_count: usize = undefined; | 479 | var environ_count: usize = undefined; |
| 355 | var environ_buf_size: usize = undefined; | 480 | var environ_buf_size: usize = undefined; |
| 356 | 481 | ||
| ... | @@ -360,7 +485,7 @@ pub fn createMap(env: Environ, allocator: Allocator) CreateMapError!Map { | ... | @@ -360,7 +485,7 @@ pub fn createMap(env: Environ, allocator: Allocator) CreateMapError!Map { |
| 360 | } | 485 | } |
| 361 | 486 | ||
| 362 | if (environ_count == 0) { | 487 | if (environ_count == 0) { |
| 363 | return result; | 488 | return map; |
| 364 | } | 489 | } |
| 365 | 490 | ||
| 366 | const environ = try allocator.alloc([*:0]u8, environ_count); | 491 | const environ = try allocator.alloc([*:0]u8, environ_count); |
| ... | @@ -373,63 +498,9 @@ pub fn createMap(env: Environ, allocator: Allocator) CreateMapError!Map { | ... | @@ -373,63 +498,9 @@ pub fn createMap(env: Environ, allocator: Allocator) CreateMapError!Map { |
| 373 | return posix.unexpectedErrno(environ_get_ret); | 498 | return posix.unexpectedErrno(environ_get_ret); |
| 374 | } | 499 | } |
| 375 | 500 | ||
| 376 | for (environ) |line| { | 501 | try map.putPosixBlock(.{ .slice = environ }); |
| 377 | const pair = mem.sliceTo(line, 0); | 502 | } else try map.putPosixBlock(env.block.view()); |
| 378 | var parts = mem.splitScalar(u8, pair, '='); | 503 | return map; |
| 379 | const key = parts.first(); | ||
| 380 | const value = parts.rest(); | ||
| 381 | try result.put(key, value); | ||
| 382 | } | ||
| 383 | return result; | ||
| 384 | } else { | ||
| 385 | for (env.block) |opt_line| { | ||
| 386 | const line = opt_line.?; | ||
| 387 | var line_i: usize = 0; | ||
| 388 | while (line[line_i] != 0 and line[line_i] != '=') : (line_i += 1) {} | ||
| 389 | const key = line[0..line_i]; | ||
| 390 | |||
| 391 | var end_i: usize = line_i; | ||
| 392 | while (line[end_i] != 0) : (end_i += 1) {} | ||
| 393 | const value = line[line_i + 1 .. end_i]; | ||
| 394 | |||
| 395 | try result.put(key, value); | ||
| 396 | } | ||
| 397 | return result; | ||
| 398 | } | ||
| 399 | } | ||
| 400 | |||
| 401 | pub fn createMapWide(ptr: [*:0]u16, gpa: Allocator) CreateMapError!Map { | ||
| 402 | var result = Map.init(gpa); | ||
| 403 | errdefer result.deinit(); | ||
| 404 | |||
| 405 | var i: usize = 0; | ||
| 406 | while (ptr[i] != 0) { | ||
| 407 | const key_start = i; | ||
| 408 | |||
| 409 | // There are some special environment variables that start with =, | ||
| 410 | // so we need a special case to not treat = as a key/value separator | ||
| 411 | // if it's the first character. | ||
| 412 | // https://devblogs.microsoft.com/oldnewthing/20100506-00/?p=14133 | ||
| 413 | if (ptr[key_start] == '=') i += 1; | ||
| 414 | |||
| 415 | while (ptr[i] != 0 and ptr[i] != '=') : (i += 1) {} | ||
| 416 | const key_w = ptr[key_start..i]; | ||
| 417 | const key = try unicode.wtf16LeToWtf8Alloc(gpa, key_w); | ||
| 418 | errdefer gpa.free(key); | ||
| 419 | |||
| 420 | if (ptr[i] == '=') i += 1; | ||
| 421 | |||
| 422 | const value_start = i; | ||
| 423 | while (ptr[i] != 0) : (i += 1) {} | ||
| 424 | const value_w = ptr[value_start..i]; | ||
| 425 | const value = try unicode.wtf16LeToWtf8Alloc(gpa, value_w); | ||
| 426 | errdefer gpa.free(value); | ||
| 427 | |||
| 428 | i += 1; // skip over null byte | ||
| 429 | |||
| 430 | try result.putMove(key, value); | ||
| 431 | } | ||
| 432 | return result; | ||
| 433 | } | 504 | } |
| 434 | 505 | ||
| 435 | pub const ContainsError = error{ | 506 | pub const ContainsError = error{ |
| ... | @@ -516,16 +587,15 @@ pub inline fn containsUnemptyConstant(environ: Environ, comptime key: []const u8 | ... | @@ -516,16 +587,15 @@ pub inline fn containsUnemptyConstant(environ: Environ, comptime key: []const u8 |
| 516 | /// * `createMap` | 587 | /// * `createMap` |
| 517 | pub fn getPosix(environ: Environ, key: []const u8) ?[:0]const u8 { | 588 | pub fn getPosix(environ: Environ, key: []const u8) ?[:0]const u8 { |
| 518 | if (mem.findScalar(u8, key, '=') != null) return null; | 589 | if (mem.findScalar(u8, key, '=') != null) return null; |
| 519 | for (environ.block) |opt_line| { | 590 | for (environ.block.view().slice) |entry| { |
| 520 | const line = opt_line.?; | 591 | var entry_i: usize = 0; |
| 521 | var line_i: usize = 0; | 592 | while (entry[entry_i] != 0) : (entry_i += 1) { |
| 522 | while (line[line_i] != 0) : (line_i += 1) { | 593 | if (entry_i == key.len) break; |
| 523 | if (line_i == key.len) break; | 594 | if (entry[entry_i] != key[entry_i]) break; |
| 524 | if (line[line_i] != key[line_i]) break; | ||
| 525 | } | 595 | } |
| 526 | if ((line_i != key.len) or (line[line_i] != '=')) continue; | 596 | if ((entry_i != key.len) or (entry[entry_i] != '=')) continue; |
| 527 | 597 | ||
| 528 | return mem.sliceTo(line + line_i + 1, 0); | 598 | return mem.sliceTo(entry + entry_i + 1, 0); |
| 529 | } | 599 | } |
| 530 | return null; | 600 | return null; |
| 531 | } | 601 | } |
| ... | @@ -548,7 +618,10 @@ pub fn getWindows(environ: Environ, key: [*:0]const u16) ?[:0]const u16 { | ... | @@ -548,7 +618,10 @@ pub fn getWindows(environ: Environ, key: [*:0]const u16) ?[:0]const u16 { |
| 548 | const key_slice = mem.sliceTo(key, 0); | 618 | const key_slice = mem.sliceTo(key, 0); |
| 549 | if (key_slice.len > 0 and mem.findScalar(u16, key_slice[1..], '=') != null) return null; | 619 | if (key_slice.len > 0 and mem.findScalar(u16, key_slice[1..], '=') != null) return null; |
| 550 | 620 | ||
| 551 | const ptr = std.os.windows.peb().ProcessParameters.Environment; | 621 | const peb = std.os.windows.peb(); |
| 622 | assert(std.os.windows.ntdll.RtlEnterCriticalSection(peb.FastPebLock) == .SUCCESS); | ||
| 623 | defer assert(std.os.windows.ntdll.RtlLeaveCriticalSection(peb.FastPebLock) == .SUCCESS); | ||
| 624 | const ptr = peb.ProcessParameters.Environment; | ||
| 552 | 625 | ||
| 553 | var i: usize = 0; | 626 | var i: usize = 0; |
| 554 | while (ptr[i] != 0) { | 627 | while (ptr[i] != 0) { |
| ... | @@ -604,7 +677,7 @@ pub fn getAlloc(environ: Environ, gpa: Allocator, key: []const u8) GetAllocError | ... | @@ -604,7 +677,7 @@ pub fn getAlloc(environ: Environ, gpa: Allocator, key: []const u8) GetAllocError |
| 604 | return gpa.dupe(u8, val); | 677 | return gpa.dupe(u8, val); |
| 605 | } | 678 | } |
| 606 | 679 | ||
| 607 | pub const CreateBlockPosixOptions = struct { | 680 | pub const CreatePosixBlockOptions = struct { |
| 608 | /// `null` means to leave the `ZIG_PROGRESS` environment variable unmodified. | 681 | /// `null` means to leave the `ZIG_PROGRESS` environment variable unmodified. |
| 609 | /// If non-null, negative means to remove the environment variable, and >= 0 | 682 | /// If non-null, negative means to remove the environment variable, and >= 0 |
| 610 | /// means to provide it with the given integer. | 683 | /// means to provide it with the given integer. |
| ... | @@ -613,67 +686,145 @@ pub const CreateBlockPosixOptions = struct { | ... | @@ -613,67 +686,145 @@ pub const CreateBlockPosixOptions = struct { |
| 613 | 686 | ||
| 614 | /// Creates a null-delimited environment variable block in the format expected | 687 | /// Creates a null-delimited environment variable block in the format expected |
| 615 | /// by POSIX, from a different one. | 688 | /// by POSIX, from a different one. |
| 616 | pub fn createBlockPosix( | 689 | pub fn createPosixBlock( |
| 617 | existing: Environ, | 690 | existing: Environ, |
| 618 | arena: Allocator, | 691 | gpa: Allocator, |
| 619 | options: CreateBlockPosixOptions, | 692 | options: CreatePosixBlockOptions, |
| 620 | ) Allocator.Error![:null]?[*:0]u8 { | 693 | ) Allocator.Error!PosixBlock { |
| 621 | const contains_zig_progress = for (existing.block) |opt_line| { | 694 | const contains_zig_progress = for (existing.block.view().slice) |entry| { |
| 622 | if (mem.eql(u8, mem.sliceTo(opt_line.?, '='), "ZIG_PROGRESS")) break true; | 695 | if (mem.eql(u8, mem.sliceTo(entry, '='), "ZIG_PROGRESS")) break true; |
| 623 | } else false; | 696 | } else false; |
| 624 | 697 | ||
| 625 | const ZigProgressAction = enum { nothing, edit, delete, add }; | 698 | const ZigProgressAction = enum { nothing, edit, delete, add }; |
| 626 | const zig_progress_action: ZigProgressAction = a: { | 699 | const zig_progress_action: ZigProgressAction = action: { |
| 627 | const fd = options.zig_progress_fd orelse break :a .nothing; | 700 | const fd = options.zig_progress_fd orelse break :action .nothing; |
| 628 | if (fd >= 0) { | 701 | if (fd >= 0) { |
| 629 | break :a if (contains_zig_progress) .edit else .add; | 702 | break :action if (contains_zig_progress) .edit else .add; |
| 630 | } else { | 703 | } else { |
| 631 | if (contains_zig_progress) break :a .delete; | 704 | if (contains_zig_progress) break :action .delete; |
| 632 | } | 705 | } |
| 633 | break :a .nothing; | 706 | break :action .nothing; |
| 634 | }; | 707 | }; |
| 635 | 708 | ||
| 636 | const envp_count: usize = c: { | 709 | const envp = try gpa.allocSentinel(?[*:0]u8, len: { |
| 637 | var count: usize = existing.block.len; | 710 | var len: usize = existing.block.slice.len; |
| 638 | switch (zig_progress_action) { | 711 | switch (zig_progress_action) { |
| 639 | .add => count += 1, | 712 | .add => len += 1, |
| 640 | .delete => count -= 1, | 713 | .delete => len -= 1, |
| 641 | .nothing, .edit => {}, | 714 | .nothing, .edit => {}, |
| 642 | } | 715 | } |
| 643 | break :c count; | 716 | break :len len; |
| 644 | }; | 717 | }, null); |
| 645 | 718 | var envp_len: usize = 0; | |
| 646 | const envp_buf = try arena.allocSentinel(?[*:0]u8, envp_count, null); | 719 | errdefer { |
| 647 | var i: usize = 0; | 720 | envp[envp_len] = null; |
| 648 | var existing_index: usize = 0; | 721 | PosixBlock.deinit(.{ .slice = envp[0..envp_len :null] }, gpa); |
| 649 | 722 | } | |
| 650 | if (zig_progress_action == .add) { | 723 | if (zig_progress_action == .add) { |
| 651 | envp_buf[i] = try std.fmt.allocPrintSentinel(arena, "ZIG_PROGRESS={d}", .{options.zig_progress_fd.?}, 0); | 724 | envp[envp_len] = try std.fmt.allocPrintSentinel(gpa, "ZIG_PROGRESS={d}", .{options.zig_progress_fd.?}, 0); |
| 652 | i += 1; | 725 | envp_len += 1; |
| 653 | } | 726 | } |
| 654 | 727 | ||
| 655 | while (existing.block[existing_index]) |line| : (existing_index += 1) { | 728 | var existing_index: usize = 0; |
| 656 | if (mem.eql(u8, mem.sliceTo(line, '='), "ZIG_PROGRESS")) switch (zig_progress_action) { | 729 | while (existing.block.slice[existing_index]) |entry| : (existing_index += 1) { |
| 730 | if (mem.eql(u8, mem.sliceTo(entry, '='), "ZIG_PROGRESS")) switch (zig_progress_action) { | ||
| 657 | .add => unreachable, | 731 | .add => unreachable, |
| 658 | .delete => continue, | 732 | .delete => continue, |
| 659 | .edit => { | 733 | .edit => { |
| 660 | envp_buf[i] = try std.fmt.allocPrintSentinel(arena, "ZIG_PROGRESS={d}", .{options.zig_progress_fd.?}, 0); | 734 | envp[envp_len] = try std.fmt.allocPrintSentinel(gpa, "ZIG_PROGRESS={d}", .{options.zig_progress_fd.?}, 0); |
| 661 | i += 1; | 735 | envp_len += 1; |
| 662 | continue; | 736 | continue; |
| 663 | }, | 737 | }, |
| 664 | .nothing => {}, | 738 | .nothing => {}, |
| 665 | }; | 739 | }; |
| 666 | envp_buf[i] = try arena.dupeZ(u8, mem.span(line)); | 740 | envp[envp_len] = try gpa.dupeZ(u8, mem.span(entry)); |
| 667 | i += 1; | 741 | envp_len += 1; |
| 668 | } | 742 | } |
| 669 | 743 | ||
| 670 | assert(i == envp_count); | 744 | assert(envp_len == envp.len); |
| 671 | return envp_buf; | 745 | return .{ .slice = envp }; |
| 746 | } | ||
| 747 | |||
| 748 | pub const CreateWindowsBlockOptions = struct { | ||
| 749 | /// `null` means to leave the `ZIG_PROGRESS` environment variable unmodified. | ||
| 750 | /// If non-null, `std.os.windows.INVALID_HANDLE_VALUE` means to remove the | ||
| 751 | /// environment variable, otherwise provide it with the given handle as an integer. | ||
| 752 | zig_progress_handle: ?std.os.windows.HANDLE = null, | ||
| 753 | }; | ||
| 754 | |||
| 755 | /// Creates a null-delimited environment variable block in the format expected | ||
| 756 | /// by POSIX, from a different one. | ||
| 757 | pub fn createWindowsBlock( | ||
| 758 | existing: Environ, | ||
| 759 | gpa: Allocator, | ||
| 760 | options: CreateWindowsBlockOptions, | ||
| 761 | ) Allocator.Error!WindowsBlock { | ||
| 762 | _ = existing; | ||
| 763 | const peb = std.os.windows.peb(); | ||
| 764 | assert(std.os.windows.ntdll.RtlEnterCriticalSection(peb.FastPebLock) == .SUCCESS); | ||
| 765 | defer assert(std.os.windows.ntdll.RtlLeaveCriticalSection(peb.FastPebLock) == .SUCCESS); | ||
| 766 | const existing_block = peb.ProcessParameters.Environment; | ||
| 767 | var ranges: [2]struct { start: usize, end: usize } = undefined; | ||
| 768 | var ranges_len: usize = 0; | ||
| 769 | ranges[ranges_len].start = 0; | ||
| 770 | const zig_progress_key = [_]u16{ 'Z', 'I', 'G', '_', 'P', 'R', 'O', 'G', 'R', 'E', 'S', 'S', '=' }; | ||
| 771 | const needed_len = needed_len: { | ||
| 772 | var needed_len: usize = "\x00".len; | ||
| 773 | if (options.zig_progress_handle) |handle| if (handle != std.os.windows.INVALID_HANDLE_VALUE) { | ||
| 774 | needed_len += std.fmt.count("ZIG_PROGRESS={d}\x00", .{@intFromPtr(handle)}); | ||
| 775 | }; | ||
| 776 | var i: usize = 0; | ||
| 777 | while (existing_block[i] != 0) { | ||
| 778 | const start = i; | ||
| 779 | const entry = mem.sliceTo(existing_block[start..], 0); | ||
| 780 | i += entry.len + "\x00".len; | ||
| 781 | if (options.zig_progress_handle != null and entry.len >= zig_progress_key.len and | ||
| 782 | std.os.windows.eqlIgnoreCaseWtf16(entry[0..zig_progress_key.len], &zig_progress_key)) | ||
| 783 | { | ||
| 784 | ranges[ranges_len].end = start; | ||
| 785 | ranges_len += 1; | ||
| 786 | ranges[ranges_len].start = i; | ||
| 787 | } else needed_len += entry.len + "\x00".len; | ||
| 788 | } | ||
| 789 | ranges[ranges_len].end = i; | ||
| 790 | ranges_len += 1; | ||
| 791 | break :needed_len @max("\x00\x00".len, needed_len); | ||
| 792 | }; | ||
| 793 | const block = try gpa.alloc(u16, needed_len); | ||
| 794 | errdefer gpa.free(block); | ||
| 795 | var i: usize = 0; | ||
| 796 | if (options.zig_progress_handle) |handle| if (handle != std.os.windows.INVALID_HANDLE_VALUE) { | ||
| 797 | @memcpy(block[i..][0..zig_progress_key.len], &zig_progress_key); | ||
| 798 | i += zig_progress_key.len; | ||
| 799 | var value_buf: [std.fmt.count("{d}", .{std.math.maxInt(usize)})]u8 = undefined; | ||
| 800 | const value = std.fmt.bufPrint(&value_buf, "{d}", .{@intFromPtr(handle)}) catch unreachable; | ||
| 801 | for (block[i..][0..value.len], value) |*r, v| r.* = v; | ||
| 802 | i += value.len; | ||
| 803 | block[i] = 0; | ||
| 804 | i += 1; | ||
| 805 | }; | ||
| 806 | for (ranges[0..ranges_len]) |range| { | ||
| 807 | const range_len = range.end - range.start; | ||
| 808 | @memcpy(block[i..][0..range_len], existing_block[range.start..range.end]); | ||
| 809 | i += range_len; | ||
| 810 | } | ||
| 811 | // An empty environment is a special case that requires a redundant | ||
| 812 | // NUL terminator. CreateProcess will read the second code unit even | ||
| 813 | // though theoretically the first should be enough to recognize that the | ||
| 814 | // environment is empty (see https://nullprogram.com/blog/2023/08/23/) | ||
| 815 | for (0..2) |_| { | ||
| 816 | block[i] = 0; | ||
| 817 | i += 1; | ||
| 818 | if (i >= 2) break; | ||
| 819 | } else unreachable; | ||
| 820 | assert(i == block.len); | ||
| 821 | return .{ .slice = block[0 .. i - 1 :0] }; | ||
| 672 | } | 822 | } |
| 673 | 823 | ||
| 674 | test "Map.createBlock" { | 824 | test "Map.createPosixBlock" { |
| 675 | const allocator = testing.allocator; | 825 | const gpa = testing.allocator; |
| 676 | var envmap = Map.init(allocator); | 826 | |
| 827 | var envmap = Map.init(gpa); | ||
| 677 | defer envmap.deinit(); | 828 | defer envmap.deinit(); |
| 678 | 829 | ||
| 679 | try envmap.put("HOME", "/home/ifreund"); | 830 | try envmap.put("HOME", "/home/ifreund"); |
| ... | @@ -682,29 +833,24 @@ test "Map.createBlock" { | ... | @@ -682,29 +833,24 @@ test "Map.createBlock" { |
| 682 | try envmap.put("DEBUGINFOD_URLS", " "); | 833 | try envmap.put("DEBUGINFOD_URLS", " "); |
| 683 | try envmap.put("XCURSOR_SIZE", "24"); | 834 | try envmap.put("XCURSOR_SIZE", "24"); |
| 684 | 835 | ||
| 685 | var arena = std.heap.ArenaAllocator.init(allocator); | 836 | const block = try envmap.createPosixBlock(gpa, .{}); |
| 686 | defer arena.deinit(); | 837 | defer block.deinit(gpa); |
| 687 | const environ = try envmap.createBlockPosix(arena.allocator(), .{}); | ||
| 688 | 838 | ||
| 689 | try testing.expectEqual(@as(usize, 5), environ.len); | 839 | try testing.expectEqual(@as(usize, 5), block.slice.len); |
| 690 | 840 | ||
| 691 | inline for (.{ | 841 | for (&[_][]const u8{ |
| 692 | "HOME=/home/ifreund", | 842 | "HOME=/home/ifreund", |
| 693 | "WAYLAND_DISPLAY=wayland-1", | 843 | "WAYLAND_DISPLAY=wayland-1", |
| 694 | "DISPLAY=:1", | 844 | "DISPLAY=:1", |
| 695 | "DEBUGINFOD_URLS= ", | 845 | "DEBUGINFOD_URLS= ", |
| 696 | "XCURSOR_SIZE=24", | 846 | "XCURSOR_SIZE=24", |
| 697 | }) |target| { | 847 | }, block.slice) |expected, actual| try testing.expectEqualStrings(expected, mem.span(actual.?)); |
| 698 | for (environ) |variable| { | ||
| 699 | if (mem.eql(u8, mem.span(variable orelse continue), target)) break; | ||
| 700 | } else { | ||
| 701 | try testing.expect(false); // Environment variable not found | ||
| 702 | } | ||
| 703 | } | ||
| 704 | } | 848 | } |
| 705 | 849 | ||
| 706 | test Map { | 850 | test Map { |
| 707 | var env = Map.init(testing.allocator); | 851 | const gpa = testing.allocator; |
| 852 | |||
| 853 | var env: Map = .init(gpa); | ||
| 708 | defer env.deinit(); | 854 | defer env.deinit(); |
| 709 | 855 | ||
| 710 | try env.put("SOMETHING_NEW", "hello"); | 856 | try env.put("SOMETHING_NEW", "hello"); |
| ... | @@ -740,6 +886,7 @@ test Map { | ... | @@ -740,6 +886,7 @@ test Map { |
| 740 | try testing.expect(env.swapRemove("SOMETHING_NEW")); | 886 | try testing.expect(env.swapRemove("SOMETHING_NEW")); |
| 741 | try testing.expect(!env.swapRemove("SOMETHING_NEW")); | 887 | try testing.expect(!env.swapRemove("SOMETHING_NEW")); |
| 742 | try testing.expect(env.get("SOMETHING_NEW") == null); | 888 | try testing.expect(env.get("SOMETHING_NEW") == null); |
| 889 | try testing.expect(!env.contains("SOMETHING_NEW")); | ||
| 743 | 890 | ||
| 744 | try testing.expectEqual(@as(Map.Size, 1), env.count()); | 891 | try testing.expectEqual(@as(Map.Size, 1), env.count()); |
| 745 | 892 | ||
| ... | @@ -749,10 +896,10 @@ test Map { | ... | @@ -749,10 +896,10 @@ test Map { |
| 749 | try testing.expectEqualStrings("something else", env.get("кириллица").?); | 896 | try testing.expectEqualStrings("something else", env.get("кириллица").?); |
| 750 | 897 | ||
| 751 | // and WTF-8 that's not valid UTF-8 | 898 | // and WTF-8 that's not valid UTF-8 |
| 752 | const wtf8_with_surrogate_pair = try unicode.wtf16LeToWtf8Alloc(testing.allocator, &[_]u16{ | 899 | const wtf8_with_surrogate_pair = try unicode.wtf16LeToWtf8Alloc(gpa, &[_]u16{ |
| 753 | mem.nativeToLittle(u16, 0xD83D), // unpaired high surrogate | 900 | mem.nativeToLittle(u16, 0xD83D), // unpaired high surrogate |
| 754 | }); | 901 | }); |
| 755 | defer testing.allocator.free(wtf8_with_surrogate_pair); | 902 | defer gpa.free(wtf8_with_surrogate_pair); |
| 756 | 903 | ||
| 757 | try env.put(wtf8_with_surrogate_pair, wtf8_with_surrogate_pair); | 904 | try env.put(wtf8_with_surrogate_pair, wtf8_with_surrogate_pair); |
| 758 | try testing.expectEqualSlices(u8, wtf8_with_surrogate_pair, env.get(wtf8_with_surrogate_pair).?); | 905 | try testing.expectEqualSlices(u8, wtf8_with_surrogate_pair, env.get(wtf8_with_surrogate_pair).?); |
| ... | @@ -769,13 +916,9 @@ test "convert from Environ to Map and back again" { | ... | @@ -769,13 +916,9 @@ test "convert from Environ to Map and back again" { |
| 769 | defer map.deinit(); | 916 | defer map.deinit(); |
| 770 | try map.put("FOO", "BAR"); | 917 | try map.put("FOO", "BAR"); |
| 771 | try map.put("A", ""); | 918 | try map.put("A", ""); |
| 772 | try map.put("", "B"); | ||
| 773 | |||
| 774 | var arena_allocator = std.heap.ArenaAllocator.init(gpa); | ||
| 775 | defer arena_allocator.deinit(); | ||
| 776 | const arena = arena_allocator.allocator(); | ||
| 777 | 919 | ||
| 778 | const environ: Environ = .{ .block = try map.createBlockPosix(arena, .{}) }; | 920 | const environ: Environ = .{ .block = try map.createPosixBlock(gpa, .{}) }; |
| 921 | defer environ.block.deinit(gpa); | ||
| 779 | 922 | ||
| 780 | try testing.expectEqual(true, environ.contains(gpa, "FOO")); | 923 | try testing.expectEqual(true, environ.contains(gpa, "FOO")); |
| 781 | try testing.expectEqual(false, environ.contains(gpa, "BAR")); | 924 | try testing.expectEqual(false, environ.contains(gpa, "BAR")); |
| ... | @@ -783,7 +926,6 @@ test "convert from Environ to Map and back again" { | ... | @@ -783,7 +926,6 @@ test "convert from Environ to Map and back again" { |
| 783 | try testing.expectEqual(true, environ.containsConstant("A")); | 926 | try testing.expectEqual(true, environ.containsConstant("A")); |
| 784 | try testing.expectEqual(false, environ.containsUnempty(gpa, "A")); | 927 | try testing.expectEqual(false, environ.containsUnempty(gpa, "A")); |
| 785 | try testing.expectEqual(false, environ.containsUnemptyConstant("A")); | 928 | try testing.expectEqual(false, environ.containsUnemptyConstant("A")); |
| 786 | try testing.expectEqual(true, environ.contains(gpa, "")); | ||
| 787 | try testing.expectEqual(false, environ.contains(gpa, "B")); | 929 | try testing.expectEqual(false, environ.contains(gpa, "B")); |
| 788 | 930 | ||
| 789 | try testing.expectError(error.EnvironmentVariableMissing, environ.getAlloc(gpa, "BOGUS")); | 931 | try testing.expectError(error.EnvironmentVariableMissing, environ.getAlloc(gpa, "BOGUS")); |
| ... | @@ -800,23 +942,47 @@ test "convert from Environ to Map and back again" { | ... | @@ -800,23 +942,47 @@ test "convert from Environ to Map and back again" { |
| 800 | try testing.expectEqualDeep(map.values(), map2.values()); | 942 | try testing.expectEqualDeep(map.values(), map2.values()); |
| 801 | } | 943 | } |
| 802 | 944 | ||
| 803 | test createMapWide { | 945 | test "Map.putPosixBlock" { |
| 804 | if (builtin.cpu.arch.endian() == .big) return error.SkipZigTest; // TODO | 946 | const gpa = testing.allocator; |
| 947 | |||
| 948 | var map: Map = .init(gpa); | ||
| 949 | defer map.deinit(); | ||
| 950 | |||
| 951 | try map.put("FOO", "BAR"); | ||
| 952 | try map.put("A", ""); | ||
| 953 | try map.put("ZIG_PROGRESS", "unchanged"); | ||
| 954 | |||
| 955 | const block = try map.createPosixBlock(gpa, .{}); | ||
| 956 | defer block.deinit(gpa); | ||
| 957 | |||
| 958 | var map2: Map = .init(gpa); | ||
| 959 | defer map2.deinit(); | ||
| 960 | try map2.putPosixBlock(block.view()); | ||
| 961 | |||
| 962 | try testing.expectEqualDeep(&[_][]const u8{ "FOO", "A", "ZIG_PROGRESS" }, map2.keys()); | ||
| 963 | try testing.expectEqualDeep(&[_][]const u8{ "BAR", "", "unchanged" }, map2.values()); | ||
| 964 | } | ||
| 965 | |||
| 966 | test "Map.putWindowsBlock" { | ||
| 967 | if (native_os != .windows) return; | ||
| 805 | 968 | ||
| 806 | const gpa = testing.allocator; | 969 | const gpa = testing.allocator; |
| 807 | 970 | ||
| 808 | var map: Map = .init(gpa); | 971 | var map: Map = .init(gpa); |
| 809 | defer map.deinit(); | 972 | defer map.deinit(); |
| 973 | |||
| 810 | try map.put("FOO", "BAR"); | 974 | try map.put("FOO", "BAR"); |
| 811 | try map.put("A", ""); | 975 | try map.put("A", ""); |
| 812 | try map.put("", "B"); | 976 | try map.put("=B", ""); |
| 977 | try map.put("ZIG_PROGRESS", "unchanged"); | ||
| 813 | 978 | ||
| 814 | const environ: [:0]u16 = try map.createBlockWindows(gpa); | 979 | const block = try map.createWindowsBlock(gpa, .{}); |
| 815 | defer gpa.free(environ); | 980 | defer block.deinit(gpa); |
| 816 | 981 | ||
| 817 | var map2 = try createMapWide(environ, gpa); | 982 | var map2: Map = .init(gpa); |
| 818 | defer map2.deinit(); | 983 | defer map2.deinit(); |
| 984 | try map2.putWindowsBlock(block.view()); | ||
| 819 | 985 | ||
| 820 | try testing.expectEqualDeep(&[_][]const u8{ "FOO", "A", "=B" }, map2.keys()); | 986 | try testing.expectEqualDeep(&[_][]const u8{ "FOO", "A", "=B", "ZIG_PROGRESS" }, map2.keys()); |
| 821 | try testing.expectEqualDeep(&[_][]const u8{ "BAR", "", "" }, map2.values()); | 987 | try testing.expectEqualDeep(&[_][]const u8{ "BAR", "", "", "unchanged" }, map2.values()); |
| 822 | } | 988 | } |
lib/std/start.zig+9-8| ... | @@ -90,15 +90,15 @@ fn _DllMainCRTStartup( | ... | @@ -90,15 +90,15 @@ fn _DllMainCRTStartup( |
| 90 | fn wasm_freestanding_start() callconv(.c) void { | 90 | fn wasm_freestanding_start() callconv(.c) void { |
| 91 | // This is marked inline because for some reason LLVM in | 91 | // This is marked inline because for some reason LLVM in |
| 92 | // release mode fails to inline it, and we want fewer call frames in stack traces. | 92 | // release mode fails to inline it, and we want fewer call frames in stack traces. |
| 93 | _ = @call(.always_inline, callMain, .{ {}, {} }); | 93 | _ = @call(.always_inline, callMain, .{ {}, std.process.Environ.Block.global }); |
| 94 | } | 94 | } |
| 95 | 95 | ||
| 96 | fn startWasi() callconv(.c) void { | 96 | fn startWasi() callconv(.c) void { |
| 97 | // The function call is marked inline because for some reason LLVM in | 97 | // The function call is marked inline because for some reason LLVM in |
| 98 | // release mode fails to inline it, and we want fewer call frames in stack traces. | 98 | // release mode fails to inline it, and we want fewer call frames in stack traces. |
| 99 | switch (builtin.wasi_exec_model) { | 99 | switch (builtin.wasi_exec_model) { |
| 100 | .reactor => _ = @call(.always_inline, callMain, .{ {}, {} }), | 100 | .reactor => _ = @call(.always_inline, callMain, .{ {}, std.process.Environ.Block.global }), |
| 101 | .command => std.os.wasi.proc_exit(@call(.always_inline, callMain, .{ {}, {} })), | 101 | .command => std.os.wasi.proc_exit(@call(.always_inline, callMain, .{ {}, std.process.Environ.Block.global })), |
| 102 | } | 102 | } |
| 103 | } | 103 | } |
| 104 | 104 | ||
| ... | @@ -476,7 +476,7 @@ fn WinStartup() callconv(.withStackAlign(.c, 1)) noreturn { | ... | @@ -476,7 +476,7 @@ fn WinStartup() callconv(.withStackAlign(.c, 1)) noreturn { |
| 476 | const cmd_line = std.os.windows.peb().ProcessParameters.CommandLine; | 476 | const cmd_line = std.os.windows.peb().ProcessParameters.CommandLine; |
| 477 | const cmd_line_w = cmd_line.Buffer.?[0..@divExact(cmd_line.Length, 2)]; | 477 | const cmd_line_w = cmd_line.Buffer.?[0..@divExact(cmd_line.Length, 2)]; |
| 478 | 478 | ||
| 479 | std.os.windows.ntdll.RtlExitUserProcess(callMain(cmd_line_w, {})); | 479 | std.os.windows.ntdll.RtlExitUserProcess(callMain(cmd_line_w, .global)); |
| 480 | } | 480 | } |
| 481 | 481 | ||
| 482 | fn wWinMainCRTStartup() callconv(.withStackAlign(.c, 1)) noreturn { | 482 | fn wWinMainCRTStartup() callconv(.withStackAlign(.c, 1)) noreturn { |
| ... | @@ -620,13 +620,14 @@ fn expandStackSize(phdrs: []elf.Phdr) void { | ... | @@ -620,13 +620,14 @@ fn expandStackSize(phdrs: []elf.Phdr) void { |
| 620 | } | 620 | } |
| 621 | 621 | ||
| 622 | inline fn callMainWithArgs(argc: usize, argv: [*][*:0]u8, envp: [:null]?[*:0]u8) u8 { | 622 | inline fn callMainWithArgs(argc: usize, argv: [*][*:0]u8, envp: [:null]?[*:0]u8) u8 { |
| 623 | const env_block: std.process.Environ.Block = .{ .slice = envp }; | ||
| 623 | if (std.Options.debug_threaded_io) |t| { | 624 | if (std.Options.debug_threaded_io) |t| { |
| 624 | if (@sizeOf(std.Io.Threaded.Argv0) != 0) t.argv0.value = argv[0]; | 625 | if (@sizeOf(std.Io.Threaded.Argv0) != 0) t.argv0.value = argv[0]; |
| 625 | t.environ = .{ .process_environ = .{ .block = envp } }; | 626 | t.environ = .{ .process_environ = .{ .block = env_block } }; |
| 626 | } | 627 | } |
| 627 | std.Thread.maybeAttachSignalStack(); | 628 | std.Thread.maybeAttachSignalStack(); |
| 628 | std.debug.maybeEnableSegfaultHandler(); | 629 | std.debug.maybeEnableSegfaultHandler(); |
| 629 | return callMain(argv[0..argc], envp); | 630 | return callMain(argv[0..argc], env_block); |
| 630 | } | 631 | } |
| 631 | 632 | ||
| 632 | fn main(c_argc: c_int, c_argv: [*][*:0]c_char, c_envp: [*:null]?[*:0]c_char) callconv(.c) c_int { | 633 | fn main(c_argc: c_int, c_argv: [*][*:0]c_char, c_envp: [*:null]?[*:0]c_char) callconv(.c) c_int { |
| ... | @@ -648,7 +649,7 @@ fn main(c_argc: c_int, c_argv: [*][*:0]c_char, c_envp: [*:null]?[*:0]c_char) cal | ... | @@ -648,7 +649,7 @@ fn main(c_argc: c_int, c_argv: [*][*:0]c_char, c_envp: [*:null]?[*:0]c_char) cal |
| 648 | std.debug.maybeEnableSegfaultHandler(); | 649 | std.debug.maybeEnableSegfaultHandler(); |
| 649 | const cmd_line = std.os.windows.peb().ProcessParameters.CommandLine; | 650 | const cmd_line = std.os.windows.peb().ProcessParameters.CommandLine; |
| 650 | const cmd_line_w = cmd_line.Buffer.?[0..@divExact(cmd_line.Length, 2)]; | 651 | const cmd_line_w = cmd_line.Buffer.?[0..@divExact(cmd_line.Length, 2)]; |
| 651 | return callMain(cmd_line_w, {}); | 652 | return callMain(cmd_line_w, .global); |
| 652 | }, | 653 | }, |
| 653 | else => {}, | 654 | else => {}, |
| 654 | } | 655 | } |
| ... | @@ -661,7 +662,7 @@ fn mainWithoutEnv(c_argc: c_int, c_argv: [*][*:0]c_char) callconv(.c) c_int { | ... | @@ -661,7 +662,7 @@ fn mainWithoutEnv(c_argc: c_int, c_argv: [*][*:0]c_char) callconv(.c) c_int { |
| 661 | if (@sizeOf(std.Io.Threaded.Argv0) != 0) { | 662 | if (@sizeOf(std.Io.Threaded.Argv0) != 0) { |
| 662 | if (std.Options.debug_threaded_io) |t| t.argv0.value = argv[0]; | 663 | if (std.Options.debug_threaded_io) |t| t.argv0.value = argv[0]; |
| 663 | } | 664 | } |
| 664 | return callMain(argv, &.{}); | 665 | return callMain(argv, .empty); |
| 665 | } | 666 | } |
| 666 | 667 | ||
| 667 | /// General error message for a malformed return type | 668 | /// General error message for a malformed return type |
test/standalone/windows_argv/fuzz.zig+7| ... | @@ -149,6 +149,13 @@ fn spawnVerify(verify_path: [:0]const u16, cmd_line: [:0]const u16) !windows.DWO | ... | @@ -149,6 +149,13 @@ fn spawnVerify(verify_path: [:0]const u16, cmd_line: [:0]const u16) !windows.DWO |
| 149 | break :spawn proc_info.hProcess; | 149 | break :spawn proc_info.hProcess; |
| 150 | }; | 150 | }; |
| 151 | defer windows.CloseHandle(child_proc); | 151 | defer windows.CloseHandle(child_proc); |
| 152 | const infinite_timeout: windows.LARGE_INTEGER = std.math.minInt(windows.LARGE_INTEGER); | ||
| 153 | switch (windows.ntdll.NtWaitForSingleObject(child_proc, windows.FALSE, &infinite_timeout)) { | ||
| 154 | .WAIT_0 => {}, | ||
| 155 | .ABANDONED_WAIT_0 => return error.WaitAbandoned, | ||
| 156 | .TIMEOUT => return error.WaitTimeOut, | ||
| 157 | else => |status| return windows.unexpectedStatus(status), | ||
| 158 | } | ||
| 152 | try windows.WaitForSingleObjectEx(child_proc, windows.INFINITE, false); | 159 | try windows.WaitForSingleObjectEx(child_proc, windows.INFINITE, false); |
| 153 | 160 | ||
| 154 | var exit_code: windows.DWORD = undefined; | 161 | var exit_code: windows.DWORD = undefined; |