| author | |
| committer | |
| log | ffc6da29e3fa53a9c81bcb8af7467cdd6345538f |
| tree | d884e55fa802c6e12682135f917b13cc6c11428e |
| parent | fce7878a9149caa80433e6d650e0bd7f60d345fd |
15 files changed, 923 insertions(+), 1048 deletions(-)
build.zig+1| ... | @@ -1498,6 +1498,7 @@ fn generateLangRef(b: *std.Build) std.Build.LazyPath { | ... | @@ -1498,6 +1498,7 @@ fn generateLangRef(b: *std.Build) std.Build.LazyPath { |
| 1498 | defer dir.close(io); | 1498 | defer dir.close(io); |
| 1499 | 1499 | ||
| 1500 | var wf = b.addWriteFiles(); | 1500 | var wf = b.addWriteFiles(); |
| 1501 | b.step("test-docs", "Test code snippets from the docs").dependOn(&wf.step); | ||
| 1501 | 1502 | ||
| 1502 | var it = dir.iterateAssumeFirstIteration(); | 1503 | var it = dir.iterateAssumeFirstIteration(); |
| 1503 | while (it.next(io) catch @panic("failed to read dir")) |entry| { | 1504 | while (it.next(io) catch @panic("failed to read dir")) |entry| { |
lib/std/Build/Watch.zig+7-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,14 +377,18 @@ const Os = switch (builtin.os.tag) { | ... | @@ -385,14 +377,18 @@ 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, |
| 393 | .OPEN, | 388 | .OPEN, |
| 394 | .{ | 389 | .{ |
| 395 | .DIRECTORY_FILE = true, | 390 | .DIRECTORY_FILE = true, |
| 391 | .IO = .ASYNCHRONOUS, | ||
| 396 | .OPEN_FOR_BACKUP_INTENT = true, | 392 | .OPEN_FOR_BACKUP_INTENT = true, |
| 397 | }, | 393 | }, |
| 398 | null, | 394 | null, |
lib/std/Io/Threaded.zig+381-341| ... | @@ -76,6 +76,7 @@ environ: Environ, | ... | @@ -76,6 +76,7 @@ environ: Environ, |
| 76 | 76 | ||
| 77 | null_file: NullFile = .{}, | 77 | null_file: NullFile = .{}, |
| 78 | random_file: RandomFile = .{}, | 78 | random_file: RandomFile = .{}, |
| 79 | pipe_file: PipeFile = .{}, | ||
| 79 | 80 | ||
| 80 | csprng: Csprng = .{}, | 81 | csprng: Csprng = .{}, |
| 81 | 82 | ||
| ... | @@ -121,7 +122,7 @@ pub const Argv0 = switch (native_os) { | ... | @@ -121,7 +122,7 @@ pub const Argv0 = switch (native_os) { |
| 121 | 122 | ||
| 122 | const Environ = struct { | 123 | const Environ = struct { |
| 123 | /// Unmodified data directly from the OS. | 124 | /// Unmodified data directly from the OS. |
| 124 | process_environ: process.Environ = .empty, | 125 | process_environ: process.Environ, |
| 125 | /// Protected by `mutex`. Determines whether the other fields have been | 126 | /// Protected by `mutex`. Determines whether the other fields have been |
| 126 | /// memoized based on `process_environ`. | 127 | /// memoized based on `process_environ`. |
| 127 | initialized: bool = false, | 128 | initialized: bool = false, |
| ... | @@ -131,13 +132,15 @@ const Environ = struct { | ... | @@ -131,13 +132,15 @@ const Environ = struct { |
| 131 | /// Protected by `mutex`. Memoized based on `process_environ`. | 132 | /// Protected by `mutex`. Memoized based on `process_environ`. |
| 132 | string: String = .{}, | 133 | string: String = .{}, |
| 133 | /// ZIG_PROGRESS | 134 | /// ZIG_PROGRESS |
| 134 | zig_progress_handle: std.Progress.ParentFileError!u31 = error.EnvironmentVariableMissing, | 135 | zig_progress_file: std.Progress.ParentFileError!File = error.EnvironmentVariableMissing, |
| 135 | /// Protected by `mutex`. Tracks the problem, if any, that occurred when | 136 | /// Protected by `mutex`. Tracks the problem, if any, that occurred when |
| 136 | /// trying to scan environment variables. | 137 | /// trying to scan environment variables. |
| 137 | /// | 138 | /// |
| 138 | /// Errors are only possible on WASI. | 139 | /// Errors are only possible on WASI. |
| 139 | err: ?Error = null, | 140 | err: ?Error = null, |
| 140 | 141 | ||
| 142 | pub const empty: Environ = .{ .process_environ = .empty }; | ||
| 143 | |||
| 141 | pub const Error = Allocator.Error || Io.UnexpectedError; | 144 | pub const Error = Allocator.Error || Io.UnexpectedError; |
| 142 | 145 | ||
| 143 | pub const Exist = struct { | 146 | pub const Exist = struct { |
| ... | @@ -193,6 +196,24 @@ pub const RandomFile = switch (native_os) { | ... | @@ -193,6 +196,24 @@ pub const RandomFile = switch (native_os) { |
| 193 | }, | 196 | }, |
| 194 | }; | 197 | }; |
| 195 | 198 | ||
| 199 | pub const PipeFile = switch (native_os) { | ||
| 200 | .windows => struct { | ||
| 201 | handle: ?windows.HANDLE = null, | ||
| 202 | |||
| 203 | fn deinit(this: *@This()) void { | ||
| 204 | if (this.handle) |handle| { | ||
| 205 | windows.CloseHandle(handle); | ||
| 206 | this.handle = null; | ||
| 207 | } | ||
| 208 | } | ||
| 209 | }, | ||
| 210 | else => struct { | ||
| 211 | fn deinit(this: @This()) void { | ||
| 212 | _ = this; | ||
| 213 | } | ||
| 214 | }, | ||
| 215 | }; | ||
| 216 | |||
| 196 | pub const Pid = if (native_os == .linux) enum(posix.pid_t) { | 217 | pub const Pid = if (native_os == .linux) enum(posix.pid_t) { |
| 197 | unknown = 0, | 218 | unknown = 0, |
| 198 | _, | 219 | _, |
| ... | @@ -1496,7 +1517,9 @@ pub const init_single_threaded: Threaded = .{ | ... | @@ -1496,7 +1517,9 @@ pub const init_single_threaded: Threaded = .{ |
| 1496 | .old_sig_pipe = undefined, | 1517 | .old_sig_pipe = undefined, |
| 1497 | .have_signal_handler = false, | 1518 | .have_signal_handler = false, |
| 1498 | .argv0 = .empty, | 1519 | .argv0 = .empty, |
| 1499 | .environ = .{}, | 1520 | .environ = .{ .process_environ = .{ |
| 1521 | .block = if (process.Environ.Block == process.Environ.GlobalBlock) .global else .empty, | ||
| 1522 | } }, | ||
| 1500 | .worker_threads = .init(null), | 1523 | .worker_threads = .init(null), |
| 1501 | .disable_memory_mapping = false, | 1524 | .disable_memory_mapping = false, |
| 1502 | }; | 1525 | }; |
| ... | @@ -1531,6 +1554,7 @@ pub fn deinit(t: *Threaded) void { | ... | @@ -1531,6 +1554,7 @@ pub fn deinit(t: *Threaded) void { |
| 1531 | } | 1554 | } |
| 1532 | t.null_file.deinit(); | 1555 | t.null_file.deinit(); |
| 1533 | t.random_file.deinit(); | 1556 | t.random_file.deinit(); |
| 1557 | t.pipe_file.deinit(); | ||
| 1534 | t.* = undefined; | 1558 | t.* = undefined; |
| 1535 | } | 1559 | } |
| 1536 | 1560 | ||
| ... | @@ -1573,14 +1597,7 @@ fn worker(t: *Threaded) void { | ... | @@ -1573,14 +1597,7 @@ fn worker(t: *Threaded) void { |
| 1573 | }, | 1597 | }, |
| 1574 | }, | 1598 | }, |
| 1575 | }, | 1599 | }, |
| 1576 | &.{ | 1600 | &.{ .ObjectName = null }, |
| 1577 | .Length = @sizeOf(windows.OBJECT_ATTRIBUTES), | ||
| 1578 | .RootDirectory = null, | ||
| 1579 | .ObjectName = null, | ||
| 1580 | .Attributes = .{}, | ||
| 1581 | .SecurityDescriptor = null, | ||
| 1582 | .SecurityQualityOfService = null, | ||
| 1583 | }, | ||
| 1584 | &windows.teb().ClientId, | 1601 | &windows.teb().ClientId, |
| 1585 | ) == .SUCCESS); | 1602 | ) == .SUCCESS); |
| 1586 | } | 1603 | } |
| ... | @@ -3376,12 +3393,8 @@ fn dirCreateDirPathOpenWindows( | ... | @@ -3376,12 +3393,8 @@ fn dirCreateDirPathOpenWindows( |
| 3376 | }, | 3393 | }, |
| 3377 | }, | 3394 | }, |
| 3378 | &.{ | 3395 | &.{ |
| 3379 | .Length = @sizeOf(w.OBJECT_ATTRIBUTES), | ||
| 3380 | .RootDirectory = if (Dir.path.isAbsoluteWindowsWtf16(sub_path_w)) null else dir.handle, | 3396 | .RootDirectory = if (Dir.path.isAbsoluteWindowsWtf16(sub_path_w)) null else dir.handle, |
| 3381 | .Attributes = .{}, | ||
| 3382 | .ObjectName = &nt_name, | 3397 | .ObjectName = &nt_name, |
| 3383 | .SecurityDescriptor = null, | ||
| 3384 | .SecurityQualityOfService = null, | ||
| 3385 | }, | 3398 | }, |
| 3386 | &io_status_block, | 3399 | &io_status_block, |
| 3387 | null, | 3400 | null, |
| ... | @@ -4063,13 +4076,9 @@ fn dirAccessWindows( | ... | @@ -4063,13 +4076,9 @@ fn dirAccessWindows( |
| 4063 | .MaximumLength = path_len_bytes, | 4076 | .MaximumLength = path_len_bytes, |
| 4064 | .Buffer = @constCast(sub_path_w.ptr), | 4077 | .Buffer = @constCast(sub_path_w.ptr), |
| 4065 | }; | 4078 | }; |
| 4066 | var attr: windows.OBJECT_ATTRIBUTES = .{ | 4079 | const attr: windows.OBJECT_ATTRIBUTES = .{ |
| 4067 | .Length = @sizeOf(windows.OBJECT_ATTRIBUTES), | ||
| 4068 | .RootDirectory = if (Dir.path.isAbsoluteWindowsWtf16(sub_path_w)) null else dir.handle, | 4080 | .RootDirectory = if (Dir.path.isAbsoluteWindowsWtf16(sub_path_w)) null else dir.handle, |
| 4069 | .Attributes = .{}, | ||
| 4070 | .ObjectName = &nt_name, | 4081 | .ObjectName = &nt_name, |
| 4071 | .SecurityDescriptor = null, | ||
| 4072 | .SecurityQualityOfService = null, | ||
| 4073 | }; | 4082 | }; |
| 4074 | var basic_info: windows.FILE.BASIC_INFORMATION = undefined; | 4083 | var basic_info: windows.FILE.BASIC_INFORMATION = undefined; |
| 4075 | const syscall: Syscall = try .start(); | 4084 | const syscall: Syscall = try .start(); |
| ... | @@ -4285,14 +4294,8 @@ fn dirCreateFileWindows( | ... | @@ -4285,14 +4294,8 @@ fn dirCreateFileWindows( |
| 4285 | .Buffer = @constCast(sub_path_w.ptr), | 4294 | .Buffer = @constCast(sub_path_w.ptr), |
| 4286 | }; | 4295 | }; |
| 4287 | const attr: windows.OBJECT_ATTRIBUTES = .{ | 4296 | const attr: windows.OBJECT_ATTRIBUTES = .{ |
| 4288 | .Length = @sizeOf(windows.OBJECT_ATTRIBUTES), | ||
| 4289 | .RootDirectory = if (Dir.path.isAbsoluteWindowsWtf16(sub_path_w)) null else dir.handle, | 4297 | .RootDirectory = if (Dir.path.isAbsoluteWindowsWtf16(sub_path_w)) null else dir.handle, |
| 4290 | .Attributes = .{ | ||
| 4291 | .INHERIT = false, | ||
| 4292 | }, | ||
| 4293 | .ObjectName = &nt_name, | 4298 | .ObjectName = &nt_name, |
| 4294 | .SecurityDescriptor = null, | ||
| 4295 | .SecurityQualityOfService = null, | ||
| 4296 | }; | 4299 | }; |
| 4297 | const create_disposition: windows.FILE.CREATE_DISPOSITION = if (flags.exclusive) | 4300 | const create_disposition: windows.FILE.CREATE_DISPOSITION = if (flags.exclusive) |
| 4298 | .CREATE | 4301 | .CREATE |
| ... | @@ -4905,17 +4908,6 @@ pub fn dirOpenFileWtf16( | ... | @@ -4905,17 +4908,6 @@ pub fn dirOpenFileWtf16( |
| 4905 | .MaximumLength = path_len_bytes, | 4908 | .MaximumLength = path_len_bytes, |
| 4906 | .Buffer = @constCast(sub_path_w.ptr), | 4909 | .Buffer = @constCast(sub_path_w.ptr), |
| 4907 | }; | 4910 | }; |
| 4908 | var attr: w.OBJECT_ATTRIBUTES = .{ | ||
| 4909 | .Length = @sizeOf(w.OBJECT_ATTRIBUTES), | ||
| 4910 | .RootDirectory = dir_handle, | ||
| 4911 | .Attributes = .{ | ||
| 4912 | // TODO should we set INHERIT=false? | ||
| 4913 | //.INHERIT = false, | ||
| 4914 | }, | ||
| 4915 | .ObjectName = &nt_name, | ||
| 4916 | .SecurityDescriptor = null, | ||
| 4917 | .SecurityQualityOfService = null, | ||
| 4918 | }; | ||
| 4919 | var io_status_block: w.IO_STATUS_BLOCK = undefined; | 4911 | var io_status_block: w.IO_STATUS_BLOCK = undefined; |
| 4920 | 4912 | ||
| 4921 | // There are multiple kernel bugs being worked around with retries. | 4913 | // There are multiple kernel bugs being worked around with retries. |
| ... | @@ -4934,7 +4926,10 @@ pub fn dirOpenFileWtf16( | ... | @@ -4934,7 +4926,10 @@ pub fn dirOpenFileWtf16( |
| 4934 | .WRITE = flags.isWrite(), | 4926 | .WRITE = flags.isWrite(), |
| 4935 | }, | 4927 | }, |
| 4936 | }, | 4928 | }, |
| 4937 | &attr, | 4929 | &.{ |
| 4930 | .RootDirectory = dir_handle, | ||
| 4931 | .ObjectName = &nt_name, | ||
| 4932 | }, | ||
| 4938 | &io_status_block, | 4933 | &io_status_block, |
| 4939 | null, | 4934 | null, |
| 4940 | .{ .NORMAL = true }, | 4935 | .{ .NORMAL = true }, |
| ... | @@ -5302,12 +5297,8 @@ pub fn dirOpenDirWindows( | ... | @@ -5302,12 +5297,8 @@ pub fn dirOpenDirWindows( |
| 5302 | }, | 5297 | }, |
| 5303 | }, | 5298 | }, |
| 5304 | &.{ | 5299 | &.{ |
| 5305 | .Length = @sizeOf(w.OBJECT_ATTRIBUTES), | ||
| 5306 | .RootDirectory = if (Dir.path.isAbsoluteWindowsWtf16(sub_path_w)) null else dir.handle, | 5300 | .RootDirectory = if (Dir.path.isAbsoluteWindowsWtf16(sub_path_w)) null else dir.handle, |
| 5307 | .Attributes = .{}, | ||
| 5308 | .ObjectName = &nt_name, | 5301 | .ObjectName = &nt_name, |
| 5309 | .SecurityDescriptor = null, | ||
| 5310 | .SecurityQualityOfService = null, | ||
| 5311 | }, | 5302 | }, |
| 5312 | &io_status_block, | 5303 | &io_status_block, |
| 5313 | null, | 5304 | null, |
| ... | @@ -6517,12 +6508,8 @@ fn dirDeleteWindows(userdata: ?*anyopaque, dir: Dir, sub_path: []const u8, remov | ... | @@ -6517,12 +6508,8 @@ fn dirDeleteWindows(userdata: ?*anyopaque, dir: Dir, sub_path: []const u8, remov |
| 6517 | .SYNCHRONIZE = true, | 6508 | .SYNCHRONIZE = true, |
| 6518 | } }, | 6509 | } }, |
| 6519 | &.{ | 6510 | &.{ |
| 6520 | .Length = @sizeOf(w.OBJECT_ATTRIBUTES), | ||
| 6521 | .RootDirectory = if (Dir.path.isAbsoluteWindowsWtf16(sub_path_w)) null else dir.handle, | 6511 | .RootDirectory = if (Dir.path.isAbsoluteWindowsWtf16(sub_path_w)) null else dir.handle, |
| 6522 | .Attributes = .{}, | ||
| 6523 | .ObjectName = &nt_name, | 6512 | .ObjectName = &nt_name, |
| 6524 | .SecurityDescriptor = null, | ||
| 6525 | .SecurityQualityOfService = null, | ||
| 6526 | }, | 6513 | }, |
| 6527 | &io_status_block, | 6514 | &io_status_block, |
| 6528 | null, | 6515 | null, |
| ... | @@ -6531,6 +6518,7 @@ fn dirDeleteWindows(userdata: ?*anyopaque, dir: Dir, sub_path: []const u8, remov | ... | @@ -6531,6 +6518,7 @@ fn dirDeleteWindows(userdata: ?*anyopaque, dir: Dir, sub_path: []const u8, remov |
| 6531 | .OPEN, | 6518 | .OPEN, |
| 6532 | .{ | 6519 | .{ |
| 6533 | .DIRECTORY_FILE = remove_dir, | 6520 | .DIRECTORY_FILE = remove_dir, |
| 6521 | .IO = .SYNCHRONOUS_NONALERT, | ||
| 6534 | .NON_DIRECTORY_FILE = !remove_dir, | 6522 | .NON_DIRECTORY_FILE = !remove_dir, |
| 6535 | .OPEN_REPARSE_POINT = true, // would we ever want to delete the target instead? | 6523 | .OPEN_REPARSE_POINT = true, // would we ever want to delete the target instead? |
| 6536 | }, | 6524 | }, |
| ... | @@ -7342,14 +7330,8 @@ fn dirReadLinkWindows(dir: Dir, sub_path: []const u8, buffer: []u8) Dir.ReadLink | ... | @@ -7342,14 +7330,8 @@ fn dirReadLinkWindows(dir: Dir, sub_path: []const u8, buffer: []u8) Dir.ReadLink |
| 7342 | .Buffer = @constCast(sub_path_w.ptr), | 7330 | .Buffer = @constCast(sub_path_w.ptr), |
| 7343 | }; | 7331 | }; |
| 7344 | const attr: windows.OBJECT_ATTRIBUTES = .{ | 7332 | const attr: windows.OBJECT_ATTRIBUTES = .{ |
| 7345 | .Length = @sizeOf(windows.OBJECT_ATTRIBUTES), | ||
| 7346 | .RootDirectory = if (Dir.path.isAbsoluteWindowsWtf16(sub_path_w)) null else dir.handle, | 7333 | .RootDirectory = if (Dir.path.isAbsoluteWindowsWtf16(sub_path_w)) null else dir.handle, |
| 7347 | .Attributes = .{ | ||
| 7348 | .INHERIT = false, | ||
| 7349 | }, | ||
| 7350 | .ObjectName = &nt_name, | 7334 | .ObjectName = &nt_name, |
| 7351 | .SecurityDescriptor = null, | ||
| 7352 | .SecurityQualityOfService = null, | ||
| 7353 | }; | 7335 | }; |
| 7354 | var io_status_block: windows.IO_STATUS_BLOCK = undefined; | 7336 | var io_status_block: windows.IO_STATUS_BLOCK = undefined; |
| 7355 | var result_handle: windows.HANDLE = undefined; | 7337 | var result_handle: windows.HANDLE = undefined; |
| ... | @@ -7906,24 +7888,19 @@ fn fileSyncWindows(userdata: ?*anyopaque, file: File) File.SyncError!void { | ... | @@ -7906,24 +7888,19 @@ fn fileSyncWindows(userdata: ?*anyopaque, file: File) File.SyncError!void { |
| 7906 | const t: *Threaded = @ptrCast(@alignCast(userdata)); | 7888 | const t: *Threaded = @ptrCast(@alignCast(userdata)); |
| 7907 | _ = t; | 7889 | _ = t; |
| 7908 | 7890 | ||
| 7891 | var io_status_block: windows.IO_STATUS_BLOCK = undefined; | ||
| 7909 | const syscall: Syscall = try .start(); | 7892 | const syscall: Syscall = try .start(); |
| 7910 | while (true) { | 7893 | while (true) { |
| 7911 | if (windows.kernel32.FlushFileBuffers(file.handle) != 0) { | 7894 | switch (windows.ntdll.NtFlushBuffersFile(file.handle, &io_status_block)) { |
| 7912 | return syscall.finish(); | 7895 | .SUCCESS => break syscall.finish(), |
| 7913 | } | 7896 | .CANCELLED => { |
| 7914 | switch (windows.GetLastError()) { | ||
| 7915 | .SUCCESS => unreachable, // `FlushFileBuffers` returned nonzero | ||
| 7916 | .INVALID_HANDLE => unreachable, | ||
| 7917 | .ACCESS_DENIED => return syscall.fail(error.AccessDenied), // a sync was performed but the system couldn't update the access time | ||
| 7918 | .UNEXP_NET_ERR => return syscall.fail(error.InputOutput), | ||
| 7919 | .OPERATION_ABORTED => { | ||
| 7920 | try syscall.checkCancel(); | 7897 | try syscall.checkCancel(); |
| 7921 | continue; | 7898 | continue; |
| 7922 | }, | 7899 | }, |
| 7923 | else => |err| { | 7900 | .INVALID_HANDLE => unreachable, |
| 7924 | syscall.finish(); | 7901 | .ACCESS_DENIED => return syscall.fail(error.AccessDenied), // a sync was performed but the system couldn't update the access time |
| 7925 | return windows.unexpectedError(err); | 7902 | .UNEXPECTED_NETWORK_ERROR => return syscall.fail(error.InputOutput), |
| 7926 | }, | 7903 | else => |status| return syscall.unexpectedNtstatus(status), |
| 7927 | } | 7904 | } |
| 7928 | } | 7905 | } |
| 7929 | } | 7906 | } |
| ... | @@ -14556,22 +14533,39 @@ fn scanEnviron(t: *Threaded) void { | ... | @@ -14556,22 +14533,39 @@ fn scanEnviron(t: *Threaded) void { |
| 14556 | comptime assert(@sizeOf(Environ.String) == 0); | 14533 | comptime assert(@sizeOf(Environ.String) == 0); |
| 14557 | } | 14534 | } |
| 14558 | } else { | 14535 | } else { |
| 14559 | for (t.environ.process_environ.block) |opt_line| { | 14536 | for (t.environ.process_environ.block.slice) |opt_entry| { |
| 14560 | const line = opt_line.?; | 14537 | const entry = opt_entry.?; |
| 14561 | var line_i: usize = 0; | 14538 | var entry_i: usize = 0; |
| 14562 | while (line[line_i] != 0 and line[line_i] != '=') : (line_i += 1) {} | 14539 | while (entry[entry_i] != 0 and entry[entry_i] != '=') : (entry_i += 1) {} |
| 14563 | const key = line[0..line_i]; | 14540 | const key = entry[0..entry_i]; |
| 14564 | 14541 | ||
| 14565 | var end_i: usize = line_i; | 14542 | var end_i: usize = entry_i; |
| 14566 | while (line[end_i] != 0) : (end_i += 1) {} | 14543 | while (entry[end_i] != 0) : (end_i += 1) {} |
| 14567 | const value = line[line_i + 1 .. end_i :0]; | 14544 | const value = entry[entry_i + 1 .. end_i :0]; |
| 14568 | 14545 | ||
| 14569 | if (std.mem.eql(u8, key, "NO_COLOR")) { | 14546 | if (std.mem.eql(u8, key, "NO_COLOR")) { |
| 14570 | t.environ.exist.NO_COLOR = true; | 14547 | t.environ.exist.NO_COLOR = true; |
| 14571 | } else if (std.mem.eql(u8, key, "CLICOLOR_FORCE")) { | 14548 | } else if (std.mem.eql(u8, key, "CLICOLOR_FORCE")) { |
| 14572 | t.environ.exist.CLICOLOR_FORCE = true; | 14549 | t.environ.exist.CLICOLOR_FORCE = true; |
| 14573 | } else if (std.mem.eql(u8, key, "ZIG_PROGRESS")) { | 14550 | } else if (std.mem.eql(u8, key, "ZIG_PROGRESS")) { |
| 14574 | t.environ.zig_progress_handle = std.fmt.parseInt(u31, value, 10) catch error.UnrecognizedFormat; | 14551 | t.environ.zig_progress_file = file: { |
| 14552 | const int = std.fmt.parseInt(switch (@typeInfo(File.Handle)) { | ||
| 14553 | .int => |int_info| @Int( | ||
| 14554 | .unsigned, | ||
| 14555 | int_info.bits - @intFromBool(int_info.signedness == .signed), | ||
| 14556 | ), | ||
| 14557 | .pointer => usize, | ||
| 14558 | else => break :file error.UnsupportedOperation, | ||
| 14559 | }, value, 10) catch break :file error.UnrecognizedFormat; | ||
| 14560 | break :file .{ | ||
| 14561 | .handle = switch (@typeInfo(File.Handle)) { | ||
| 14562 | .int => int, | ||
| 14563 | .pointer => @ptrFromInt(int), | ||
| 14564 | else => comptime unreachable, | ||
| 14565 | }, | ||
| 14566 | .flags = .{ .nonblocking = true }, | ||
| 14567 | }; | ||
| 14568 | }; | ||
| 14575 | } else inline for (@typeInfo(Environ.String).@"struct".fields) |field| { | 14569 | } else inline for (@typeInfo(Environ.String).@"struct".fields) |field| { |
| 14576 | if (std.mem.eql(u8, key, field.name)) @field(t.environ.string, field.name) = value; | 14570 | if (std.mem.eql(u8, key, field.name)) @field(t.environ.string, field.name) = value; |
| 14577 | } | 14571 | } |
| ... | @@ -14594,19 +14588,17 @@ fn processReplace(userdata: ?*anyopaque, options: process.ReplaceOptions) proces | ... | @@ -14594,19 +14588,17 @@ fn processReplace(userdata: ?*anyopaque, options: process.ReplaceOptions) proces |
| 14594 | const argv_buf = try arena.allocSentinel(?[*:0]const u8, options.argv.len, null); | 14588 | const argv_buf = try arena.allocSentinel(?[*:0]const u8, options.argv.len, null); |
| 14595 | for (options.argv, 0..) |arg, i| argv_buf[i] = (try arena.dupeZ(u8, arg)).ptr; | 14589 | for (options.argv, 0..) |arg, i| argv_buf[i] = (try arena.dupeZ(u8, arg)).ptr; |
| 14596 | 14590 | ||
| 14597 | const envp: [*:null]const ?[*:0]const u8 = m: { | 14591 | const env_block = env_block: { |
| 14598 | const prog_fd: i32 = -1; | 14592 | const prog_fd: i32 = -1; |
| 14599 | if (options.environ_map) |environ_map| { | 14593 | if (options.environ_map) |environ_map| break :env_block try environ_map.createPosixBlock(arena, .{ |
| 14600 | break :m (try environ_map.createBlockPosix(arena, .{ | ||
| 14601 | .zig_progress_fd = prog_fd, | ||
| 14602 | })).ptr; | ||
| 14603 | } | ||
| 14604 | break :m (try process.Environ.createBlockPosix(t.environ.process_environ, arena, .{ | ||
| 14605 | .zig_progress_fd = prog_fd, | 14594 | .zig_progress_fd = prog_fd, |
| 14606 | })).ptr; | 14595 | }); |
| 14596 | break :env_block try t.environ.process_environ.createPosixBlock(arena, .{ | ||
| 14597 | .zig_progress_fd = prog_fd, | ||
| 14598 | }); | ||
| 14607 | }; | 14599 | }; |
| 14608 | 14600 | ||
| 14609 | return posixExecv(options.expand_arg0, argv_buf.ptr[0].?, argv_buf.ptr, envp, PATH); | 14601 | return posixExecv(options.expand_arg0, argv_buf.ptr[0].?, argv_buf.ptr, env_block, PATH); |
| 14610 | } | 14602 | } |
| 14611 | 14603 | ||
| 14612 | fn processReplacePath(userdata: ?*anyopaque, dir: Dir, options: process.ReplaceOptions) process.ReplaceError { | 14604 | fn processReplacePath(userdata: ?*anyopaque, dir: Dir, options: process.ReplaceOptions) process.ReplaceError { |
| ... | @@ -14705,16 +14697,14 @@ fn spawnPosix(t: *Threaded, options: process.SpawnOptions) process.SpawnError!Sp | ... | @@ -14705,16 +14697,14 @@ fn spawnPosix(t: *Threaded, options: process.SpawnOptions) process.SpawnError!Sp |
| 14705 | const prog_fileno = 3; | 14697 | const prog_fileno = 3; |
| 14706 | comptime assert(@max(posix.STDIN_FILENO, posix.STDOUT_FILENO, posix.STDERR_FILENO) + 1 == prog_fileno); | 14698 | comptime assert(@max(posix.STDIN_FILENO, posix.STDOUT_FILENO, posix.STDERR_FILENO) + 1 == prog_fileno); |
| 14707 | 14699 | ||
| 14708 | const envp: [*:null]const ?[*:0]const u8 = m: { | 14700 | const env_block = env_block: { |
| 14709 | const prog_fd: i32 = if (prog_pipe[1] == -1) -1 else prog_fileno; | 14701 | const prog_fd: i32 = if (prog_pipe[1] == -1) -1 else prog_fileno; |
| 14710 | if (options.environ_map) |environ_map| { | 14702 | if (options.environ_map) |environ_map| break :env_block try environ_map.createPosixBlock(arena, .{ |
| 14711 | break :m (try environ_map.createBlockPosix(arena, .{ | ||
| 14712 | .zig_progress_fd = prog_fd, | ||
| 14713 | })).ptr; | ||
| 14714 | } | ||
| 14715 | break :m (try process.Environ.createBlockPosix(t.environ.process_environ, arena, .{ | ||
| 14716 | .zig_progress_fd = prog_fd, | 14703 | .zig_progress_fd = prog_fd, |
| 14717 | })).ptr; | 14704 | }); |
| 14705 | break :env_block try t.environ.process_environ.createPosixBlock(arena, .{ | ||
| 14706 | .zig_progress_fd = prog_fd, | ||
| 14707 | }); | ||
| 14718 | }; | 14708 | }; |
| 14719 | 14709 | ||
| 14720 | // This pipe communicates to the parent errors in the child between `fork` and `execvpe`. | 14710 | // This pipe communicates to the parent errors in the child between `fork` and `execvpe`. |
| ... | @@ -14797,7 +14787,7 @@ fn spawnPosix(t: *Threaded, options: process.SpawnOptions) process.SpawnError!Sp | ... | @@ -14797,7 +14787,7 @@ fn spawnPosix(t: *Threaded, options: process.SpawnOptions) process.SpawnError!Sp |
| 14797 | } | 14787 | } |
| 14798 | } | 14788 | } |
| 14799 | 14789 | ||
| 14800 | const err = posixExecv(options.expand_arg0, argv_buf.ptr[0].?, argv_buf.ptr, envp, PATH); | 14790 | const err = posixExecv(options.expand_arg0, argv_buf.ptr[0].?, argv_buf.ptr, env_block, PATH); |
| 14801 | forkBail(ep1, err); | 14791 | forkBail(ep1, err); |
| 14802 | } | 14792 | } |
| 14803 | 14793 | ||
| ... | @@ -14811,7 +14801,6 @@ fn spawnPosix(t: *Threaded, options: process.SpawnOptions) process.SpawnError!Sp | ... | @@ -14811,7 +14801,6 @@ fn spawnPosix(t: *Threaded, options: process.SpawnOptions) process.SpawnError!Sp |
| 14811 | if (options.stderr == .pipe) posix.close(stderr_pipe[1]); | 14801 | if (options.stderr == .pipe) posix.close(stderr_pipe[1]); |
| 14812 | 14802 | ||
| 14813 | if (prog_pipe[1] != -1) posix.close(prog_pipe[1]); | 14803 | if (prog_pipe[1] != -1) posix.close(prog_pipe[1]); |
| 14814 | |||
| 14815 | options.progress_node.setIpcFd(prog_pipe[0]); | 14804 | options.progress_node.setIpcFd(prog_pipe[0]); |
| 14816 | 14805 | ||
| 14817 | return .{ | 14806 | return .{ |
| ... | @@ -14935,42 +14924,44 @@ fn childKillWindows(t: *Threaded, child: *process.Child, exit_code: windows.UINT | ... | @@ -14935,42 +14924,44 @@ fn childKillWindows(t: *Threaded, child: *process.Child, exit_code: windows.UINT |
| 14935 | // some rare edge cases where our process handle no longer has the | 14924 | // some rare edge cases where our process handle no longer has the |
| 14936 | // PROCESS_TERMINATE access right, so let's do another check to make | 14925 | // PROCESS_TERMINATE access right, so let's do another check to make |
| 14937 | // sure the process is really no longer running: | 14926 | // sure the process is really no longer running: |
| 14938 | windows.WaitForSingleObjectEx(handle, 0, false) catch return error.AccessDenied; | 14927 | const minimal_timeout: windows.LARGE_INTEGER = -1; |
| 14939 | return error.AlreadyTerminated; | 14928 | switch (windows.ntdll.NtWaitForSingleObject(handle, windows.FALSE, &minimal_timeout)) { |
| 14929 | .SUCCESS => return error.AlreadyTerminated, | ||
| 14930 | else => return error.AccessDenied, | ||
| 14931 | } | ||
| 14940 | }, | 14932 | }, |
| 14941 | else => |err| return windows.unexpectedError(err), | 14933 | else => |err| return windows.unexpectedError(err), |
| 14942 | } | 14934 | } |
| 14943 | } | 14935 | } |
| 14944 | _ = windows.kernel32.WaitForSingleObjectEx(handle, windows.INFINITE, windows.FALSE); | 14936 | const infinite_timeout: windows.LARGE_INTEGER = std.math.minInt(windows.LARGE_INTEGER); |
| 14937 | _ = windows.ntdll.NtWaitForSingleObject(handle, windows.FALSE, &infinite_timeout); | ||
| 14945 | childCleanupWindows(child); | 14938 | childCleanupWindows(child); |
| 14946 | } | 14939 | } |
| 14947 | 14940 | ||
| 14948 | fn childWaitWindows(child: *process.Child) process.Child.WaitError!process.Child.Term { | 14941 | fn childWaitWindows(child: *process.Child) process.Child.WaitError!process.Child.Term { |
| 14949 | const handle = child.id.?; | 14942 | const handle = child.id.?; |
| 14950 | 14943 | ||
| 14951 | const syscall: Syscall = try .start(); | 14944 | const alertable_syscall: AlertableSyscall = try .start(); |
| 14952 | while (true) switch (windows.kernel32.WaitForSingleObjectEx(handle, windows.INFINITE, windows.FALSE)) { | 14945 | const infinite_timeout: windows.LARGE_INTEGER = std.math.minInt(windows.LARGE_INTEGER); |
| 14953 | windows.WAIT_OBJECT_0 => break syscall.finish(), | 14946 | while (true) switch (windows.ntdll.NtWaitForSingleObject(handle, windows.TRUE, &infinite_timeout)) { |
| 14954 | windows.WAIT_ABANDONED, windows.WAIT_TIMEOUT => { | 14947 | windows.NTSTATUS.WAIT_0 => break alertable_syscall.finish(), |
| 14955 | try syscall.checkCancel(); | 14948 | .USER_APC, .ALERTED, .TIMEOUT => { |
| 14949 | try alertable_syscall.checkCancel(); | ||
| 14956 | continue; | 14950 | continue; |
| 14957 | }, | 14951 | }, |
| 14958 | windows.WAIT_FAILED => { | 14952 | else => |status| return alertable_syscall.unexpectedNtstatus(status), |
| 14959 | syscall.finish(); | ||
| 14960 | switch (windows.GetLastError()) { | ||
| 14961 | else => |err| return windows.unexpectedError(err), | ||
| 14962 | } | ||
| 14963 | }, | ||
| 14964 | else => return syscall.fail(error.Unexpected), | ||
| 14965 | }; | 14953 | }; |
| 14966 | 14954 | ||
| 14967 | const term: process.Child.Term = x: { | 14955 | var info: windows.PROCESS_BASIC_INFORMATION = undefined; |
| 14968 | var exit_code: windows.DWORD = undefined; | 14956 | const term: process.Child.Term = switch (windows.ntdll.NtQueryInformationProcess( |
| 14969 | if (windows.kernel32.GetExitCodeProcess(handle, &exit_code) == 0) { | 14957 | handle, |
| 14970 | break :x .{ .unknown = 0 }; | 14958 | .BasicInformation, |
| 14971 | } else { | 14959 | &info, |
| 14972 | break :x .{ .exited = @as(u8, @truncate(exit_code)) }; | 14960 | @sizeOf(windows.PROCESS_BASIC_INFORMATION), |
| 14973 | } | 14961 | null, |
| 14962 | )) { | ||
| 14963 | .SUCCESS => .{ .exited = @as(u8, @truncate(@intFromEnum(info.ExitStatus))) }, | ||
| 14964 | else => .{ .unknown = 0 }, | ||
| 14974 | }; | 14965 | }; |
| 14975 | 14966 | ||
| 14976 | childCleanupWindows(child); | 14967 | childCleanupWindows(child); |
| ... | @@ -15233,88 +15224,70 @@ fn setUpChildIo(stdio: process.SpawnOptions.StdIo, pipe_fd: i32, std_fileno: i32 | ... | @@ -15233,88 +15224,70 @@ fn setUpChildIo(stdio: process.SpawnOptions.StdIo, pipe_fd: i32, std_fileno: i32 |
| 15233 | fn processSpawnWindows(userdata: ?*anyopaque, options: process.SpawnOptions) process.SpawnError!process.Child { | 15224 | fn processSpawnWindows(userdata: ?*anyopaque, options: process.SpawnOptions) process.SpawnError!process.Child { |
| 15234 | const t: *Threaded = @ptrCast(@alignCast(userdata)); | 15225 | const t: *Threaded = @ptrCast(@alignCast(userdata)); |
| 15235 | 15226 | ||
| 15236 | var saAttr: windows.SECURITY_ATTRIBUTES = .{ | ||
| 15237 | .nLength = @sizeOf(windows.SECURITY_ATTRIBUTES), | ||
| 15238 | .bInheritHandle = windows.TRUE, | ||
| 15239 | .lpSecurityDescriptor = null, | ||
| 15240 | }; | ||
| 15241 | |||
| 15242 | const any_ignore = | 15227 | const any_ignore = |
| 15243 | options.stdin == .ignore or | 15228 | options.stdin == .ignore or |
| 15244 | options.stdout == .ignore or | 15229 | options.stdout == .ignore or |
| 15245 | options.stderr == .ignore; | 15230 | options.stderr == .ignore; |
| 15246 | 15231 | const nul_handle = if (any_ignore) try getNulDevice(t) else undefined; | |
| 15247 | const nul_handle = if (any_ignore) try getNulHandle(t) else undefined; | 15232 | |
| 15248 | 15233 | const any_inherit = | |
| 15249 | var g_hChildStd_IN_Rd: ?windows.HANDLE = null; | 15234 | options.stdin == .inherit or |
| 15250 | var g_hChildStd_IN_Wr: ?windows.HANDLE = null; | 15235 | options.stdout == .inherit or |
| 15251 | switch (options.stdin) { | 15236 | options.stderr == .inherit; |
| 15252 | .pipe => { | 15237 | const peb = if (any_inherit) windows.peb() else undefined; |
| 15253 | try windowsMakePipeIn(&g_hChildStd_IN_Rd, &g_hChildStd_IN_Wr, &saAttr); | 15238 | |
| 15254 | }, | 15239 | const stdin_pipe = if (options.stdin == .pipe) try t.windowsCreatePipe(.{ |
| 15255 | .ignore => { | 15240 | .server = .{ .attributes = .{ .INHERIT = false }, .mode = .{ .IO = .SYNCHRONOUS_NONALERT } }, |
| 15256 | g_hChildStd_IN_Rd = nul_handle; | 15241 | .client = .{ .attributes = .{ .INHERIT = true }, .mode = .{ .IO = .SYNCHRONOUS_NONALERT } }, |
| 15257 | }, | 15242 | .outbound = true, |
| 15258 | .inherit => { | 15243 | }) else undefined; |
| 15259 | g_hChildStd_IN_Rd = windows.GetStdHandle(windows.STD_INPUT_HANDLE) catch null; | 15244 | errdefer if (options.stdin == .pipe) for (stdin_pipe) |handle| windows.CloseHandle(handle); |
| 15260 | }, | 15245 | |
| 15261 | .close => { | 15246 | const stdout_pipe = if (options.stdout == .pipe) try t.windowsCreatePipe(.{ |
| 15262 | g_hChildStd_IN_Rd = null; | 15247 | .server = .{ .attributes = .{ .INHERIT = false }, .mode = .{ .IO = .ASYNCHRONOUS } }, |
| 15263 | }, | 15248 | .client = .{ .attributes = .{ .INHERIT = true }, .mode = .{ .IO = .SYNCHRONOUS_NONALERT } }, |
| 15264 | .file => @panic("TODO implement passing file stdio in processSpawnWindows"), | 15249 | .inbound = true, |
| 15265 | } | 15250 | }) else undefined; |
| 15266 | errdefer if (options.stdin == .pipe) { | 15251 | errdefer if (options.stdout == .pipe) for (stdout_pipe) |handle| windows.CloseHandle(handle); |
| 15267 | windowsDestroyPipe(g_hChildStd_IN_Rd, g_hChildStd_IN_Wr); | 15252 | |
| 15268 | }; | 15253 | const stderr_pipe = if (options.stderr == .pipe) try t.windowsCreatePipe(.{ |
| 15269 | 15254 | .server = .{ .attributes = .{ .INHERIT = false }, .mode = .{ .IO = .ASYNCHRONOUS } }, | |
| 15270 | var g_hChildStd_OUT_Rd: ?windows.HANDLE = null; | 15255 | .client = .{ .attributes = .{ .INHERIT = true }, .mode = .{ .IO = .SYNCHRONOUS_NONALERT } }, |
| 15271 | var g_hChildStd_OUT_Wr: ?windows.HANDLE = null; | 15256 | .inbound = true, |
| 15272 | switch (options.stdout) { | 15257 | }) else undefined; |
| 15273 | .pipe => { | 15258 | errdefer if (options.stderr == .pipe) for (stderr_pipe) |handle| windows.CloseHandle(handle); |
| 15274 | try windowsMakeAsyncPipe(&g_hChildStd_OUT_Rd, &g_hChildStd_OUT_Wr, &saAttr); | 15259 | |
| 15275 | }, | 15260 | const prog_pipe = if (options.progress_node.index != .none) try t.windowsCreatePipe(.{ |
| 15276 | .ignore => { | 15261 | .server = .{ .attributes = .{ .INHERIT = false }, .mode = .{ .IO = .ASYNCHRONOUS } }, |
| 15277 | g_hChildStd_OUT_Wr = nul_handle; | 15262 | .client = .{ .attributes = .{ .INHERIT = true }, .mode = .{ .IO = .SYNCHRONOUS_NONALERT } }, |
| 15278 | }, | 15263 | .inbound = true, |
| 15279 | .inherit => { | 15264 | }) else undefined; |
| 15280 | g_hChildStd_OUT_Wr = windows.GetStdHandle(windows.STD_OUTPUT_HANDLE) catch null; | 15265 | errdefer if (options.progress_node.index != .none) for (prog_pipe) |handle| windows.CloseHandle(handle); |
| 15281 | }, | ||
| 15282 | .close => { | ||
| 15283 | g_hChildStd_OUT_Wr = null; | ||
| 15284 | }, | ||
| 15285 | .file => @panic("TODO implement passing file stdio in processSpawnWindows"), | ||
| 15286 | } | ||
| 15287 | errdefer if (options.stdout == .pipe) { | ||
| 15288 | windowsDestroyPipe(g_hChildStd_OUT_Rd, g_hChildStd_OUT_Wr); | ||
| 15289 | }; | ||
| 15290 | |||
| 15291 | var g_hChildStd_ERR_Rd: ?windows.HANDLE = null; | ||
| 15292 | var g_hChildStd_ERR_Wr: ?windows.HANDLE = null; | ||
| 15293 | switch (options.stderr) { | ||
| 15294 | .pipe => { | ||
| 15295 | try windowsMakeAsyncPipe(&g_hChildStd_ERR_Rd, &g_hChildStd_ERR_Wr, &saAttr); | ||
| 15296 | }, | ||
| 15297 | .ignore => { | ||
| 15298 | g_hChildStd_ERR_Wr = nul_handle; | ||
| 15299 | }, | ||
| 15300 | .inherit => { | ||
| 15301 | g_hChildStd_ERR_Wr = windows.GetStdHandle(windows.STD_ERROR_HANDLE) catch null; | ||
| 15302 | }, | ||
| 15303 | .close => { | ||
| 15304 | g_hChildStd_ERR_Wr = null; | ||
| 15305 | }, | ||
| 15306 | .file => @panic("TODO implement passing file stdio in processSpawnWindows"), | ||
| 15307 | } | ||
| 15308 | errdefer if (options.stderr == .pipe) { | ||
| 15309 | windowsDestroyPipe(g_hChildStd_ERR_Rd, g_hChildStd_ERR_Wr); | ||
| 15310 | }; | ||
| 15311 | 15266 | ||
| 15312 | var siStartInfo: windows.STARTUPINFOW = .{ | 15267 | var siStartInfo: windows.STARTUPINFOW = .{ |
| 15313 | .cb = @sizeOf(windows.STARTUPINFOW), | 15268 | .cb = @sizeOf(windows.STARTUPINFOW), |
| 15314 | .hStdError = g_hChildStd_ERR_Wr, | ||
| 15315 | .hStdOutput = g_hChildStd_OUT_Wr, | ||
| 15316 | .hStdInput = g_hChildStd_IN_Rd, | ||
| 15317 | .dwFlags = windows.STARTF_USESTDHANDLES, | 15269 | .dwFlags = windows.STARTF_USESTDHANDLES, |
| 15270 | .hStdInput = switch (options.stdin) { | ||
| 15271 | .inherit => peb.ProcessParameters.hStdInput, | ||
| 15272 | .file => |file| file.handle, | ||
| 15273 | .ignore => nul_handle, | ||
| 15274 | .pipe => stdin_pipe[1], | ||
| 15275 | .close => null, | ||
| 15276 | }, | ||
| 15277 | .hStdOutput = switch (options.stdout) { | ||
| 15278 | .inherit => peb.ProcessParameters.hStdOutput, | ||
| 15279 | .file => |file| file.handle, | ||
| 15280 | .ignore => nul_handle, | ||
| 15281 | .pipe => stdout_pipe[1], | ||
| 15282 | .close => null, | ||
| 15283 | }, | ||
| 15284 | .hStdError = switch (options.stderr) { | ||
| 15285 | .inherit => peb.ProcessParameters.hStdError, | ||
| 15286 | .file => |file| file.handle, | ||
| 15287 | .ignore => nul_handle, | ||
| 15288 | .pipe => stderr_pipe[1], | ||
| 15289 | .close => null, | ||
| 15290 | }, | ||
| 15318 | 15291 | ||
| 15319 | .lpReserved = null, | 15292 | .lpReserved = null, |
| 15320 | .lpDesktop = null, | 15293 | .lpDesktop = null, |
| ... | @@ -15360,8 +15333,18 @@ fn processSpawnWindows(userdata: ?*anyopaque, options: process.SpawnOptions) pro | ... | @@ -15360,8 +15333,18 @@ fn processSpawnWindows(userdata: ?*anyopaque, options: process.SpawnOptions) pro |
| 15360 | }; | 15333 | }; |
| 15361 | const cwd_w_ptr = if (cwd_w) |cwd| cwd.ptr else null; | 15334 | const cwd_w_ptr = if (cwd_w) |cwd| cwd.ptr else null; |
| 15362 | 15335 | ||
| 15363 | const maybe_envp_buf = if (options.environ_map) |environ_map| try environ_map.createBlockWindows(arena) else null; | 15336 | const env_block = env_block: { |
| 15364 | const envp_ptr = if (maybe_envp_buf) |envp_buf| envp_buf.ptr else null; | 15337 | const prog_handle = if (options.progress_node.index != .none) |
| 15338 | prog_pipe[1] | ||
| 15339 | else | ||
| 15340 | windows.INVALID_HANDLE_VALUE; | ||
| 15341 | if (options.environ_map) |environ_map| break :env_block try environ_map.createWindowsBlock(arena, .{ | ||
| 15342 | .zig_progress_handle = prog_handle, | ||
| 15343 | }); | ||
| 15344 | break :env_block try t.environ.process_environ.createWindowsBlock(arena, .{ | ||
| 15345 | .zig_progress_handle = if (options.progress_node.index != .none) prog_pipe[1] else windows.INVALID_HANDLE_VALUE, | ||
| 15346 | }); | ||
| 15347 | }; | ||
| 15365 | 15348 | ||
| 15366 | const app_name_wtf8 = options.argv[0]; | 15349 | const app_name_wtf8 = options.argv[0]; |
| 15367 | const app_name_is_absolute = Dir.path.isAbsolute(app_name_wtf8); | 15350 | const app_name_is_absolute = Dir.path.isAbsolute(app_name_wtf8); |
| ... | @@ -15436,7 +15419,7 @@ fn processSpawnWindows(userdata: ?*anyopaque, options: process.SpawnOptions) pro | ... | @@ -15436,7 +15419,7 @@ fn processSpawnWindows(userdata: ?*anyopaque, options: process.SpawnOptions) pro |
| 15436 | &app_buf, | 15419 | &app_buf, |
| 15437 | PATHEXT, | 15420 | PATHEXT, |
| 15438 | &cmd_line_cache, | 15421 | &cmd_line_cache, |
| 15439 | envp_ptr, | 15422 | env_block, |
| 15440 | cwd_w_ptr, | 15423 | cwd_w_ptr, |
| 15441 | flags, | 15424 | flags, |
| 15442 | &siStartInfo, | 15425 | &siStartInfo, |
| ... | @@ -15471,7 +15454,7 @@ fn processSpawnWindows(userdata: ?*anyopaque, options: process.SpawnOptions) pro | ... | @@ -15471,7 +15454,7 @@ fn processSpawnWindows(userdata: ?*anyopaque, options: process.SpawnOptions) pro |
| 15471 | &app_buf, | 15454 | &app_buf, |
| 15472 | PATHEXT, | 15455 | PATHEXT, |
| 15473 | &cmd_line_cache, | 15456 | &cmd_line_cache, |
| 15474 | envp_ptr, | 15457 | env_block, |
| 15475 | cwd_w_ptr, | 15458 | cwd_w_ptr, |
| 15476 | flags, | 15459 | flags, |
| 15477 | &siStartInfo, | 15460 | &siStartInfo, |
| ... | @@ -15491,21 +15474,40 @@ fn processSpawnWindows(userdata: ?*anyopaque, options: process.SpawnOptions) pro | ... | @@ -15491,21 +15474,40 @@ fn processSpawnWindows(userdata: ?*anyopaque, options: process.SpawnOptions) pro |
| 15491 | }; | 15474 | }; |
| 15492 | } | 15475 | } |
| 15493 | 15476 | ||
| 15494 | if (options.stdin == .pipe) windows.CloseHandle(g_hChildStd_IN_Rd.?); | 15477 | if (options.progress_node.index != .none) { |
| 15495 | if (options.stderr == .pipe) windows.CloseHandle(g_hChildStd_ERR_Wr.?); | 15478 | windows.CloseHandle(prog_pipe[1]); |
| 15496 | if (options.stdout == .pipe) windows.CloseHandle(g_hChildStd_OUT_Wr.?); | 15479 | options.progress_node.setIpcFd(prog_pipe[0]); |
| 15480 | } | ||
| 15497 | 15481 | ||
| 15498 | return .{ | 15482 | return .{ |
| 15499 | .id = piProcInfo.hProcess, | 15483 | .id = piProcInfo.hProcess, |
| 15500 | .thread_handle = piProcInfo.hThread, | 15484 | .thread_handle = piProcInfo.hThread, |
| 15501 | .stdin = if (g_hChildStd_IN_Wr) |h| .{ .handle = h, .flags = .{ .nonblocking = false } } else null, | 15485 | .stdin = stdin: switch (options.stdin) { |
| 15502 | .stdout = if (g_hChildStd_OUT_Rd) |h| .{ .handle = h, .flags = .{ .nonblocking = true } } else null, | 15486 | .pipe => { |
| 15503 | .stderr = if (g_hChildStd_ERR_Rd) |h| .{ .handle = h, .flags = .{ .nonblocking = true } } else null, | 15487 | windows.CloseHandle(stdin_pipe[1]); |
| 15488 | break :stdin .{ .handle = stdin_pipe[0], .flags = .{ .nonblocking = false } }; | ||
| 15489 | }, | ||
| 15490 | else => null, | ||
| 15491 | }, | ||
| 15492 | .stdout = stdout: switch (options.stdout) { | ||
| 15493 | .pipe => { | ||
| 15494 | windows.CloseHandle(stdout_pipe[1]); | ||
| 15495 | break :stdout .{ .handle = stdout_pipe[0], .flags = .{ .nonblocking = true } }; | ||
| 15496 | }, | ||
| 15497 | else => null, | ||
| 15498 | }, | ||
| 15499 | .stderr = stderr: switch (options.stderr) { | ||
| 15500 | .pipe => { | ||
| 15501 | windows.CloseHandle(stderr_pipe[1]); | ||
| 15502 | break :stderr .{ .handle = stderr_pipe[0], .flags = .{ .nonblocking = true } }; | ||
| 15503 | }, | ||
| 15504 | else => null, | ||
| 15505 | }, | ||
| 15504 | .request_resource_usage_statistics = options.request_resource_usage_statistics, | 15506 | .request_resource_usage_statistics = options.request_resource_usage_statistics, |
| 15505 | }; | 15507 | }; |
| 15506 | } | 15508 | } |
| 15507 | 15509 | ||
| 15508 | fn getCngHandle(t: *Threaded) Io.RandomSecureError!windows.HANDLE { | 15510 | fn getCngDevice(t: *Threaded) Io.RandomSecureError!windows.HANDLE { |
| 15509 | { | 15511 | { |
| 15510 | mutexLock(&t.mutex); | 15512 | mutexLock(&t.mutex); |
| 15511 | defer mutexUnlock(&t.mutex); | 15513 | defer mutexUnlock(&t.mutex); |
| ... | @@ -15513,12 +15515,6 @@ fn getCngHandle(t: *Threaded) Io.RandomSecureError!windows.HANDLE { | ... | @@ -15513,12 +15515,6 @@ fn getCngHandle(t: *Threaded) Io.RandomSecureError!windows.HANDLE { |
| 15513 | } | 15515 | } |
| 15514 | 15516 | ||
| 15515 | const device_path = [_]u16{ '\\', 'D', 'e', 'v', 'i', 'c', 'e', '\\', 'C', 'N', 'G' }; | 15517 | const device_path = [_]u16{ '\\', 'D', 'e', 'v', 'i', 'c', 'e', '\\', 'C', 'N', 'G' }; |
| 15516 | |||
| 15517 | var nt_name: windows.UNICODE_STRING = .{ | ||
| 15518 | .Length = device_path.len * 2, | ||
| 15519 | .MaximumLength = 0, | ||
| 15520 | .Buffer = @constCast(&device_path), | ||
| 15521 | }; | ||
| 15522 | var fresh_handle: windows.HANDLE = undefined; | 15518 | var fresh_handle: windows.HANDLE = undefined; |
| 15523 | var io_status_block: windows.IO_STATUS_BLOCK = undefined; | 15519 | var io_status_block: windows.IO_STATUS_BLOCK = undefined; |
| 15524 | var syscall: Syscall = try .start(); | 15520 | var syscall: Syscall = try .start(); |
| ... | @@ -15529,12 +15525,11 @@ fn getCngHandle(t: *Threaded) Io.RandomSecureError!windows.HANDLE { | ... | @@ -15529,12 +15525,11 @@ fn getCngHandle(t: *Threaded) Io.RandomSecureError!windows.HANDLE { |
| 15529 | .SPECIFIC = .{ .FILE = .{ .READ_DATA = true } }, | 15525 | .SPECIFIC = .{ .FILE = .{ .READ_DATA = true } }, |
| 15530 | }, | 15526 | }, |
| 15531 | &.{ | 15527 | &.{ |
| 15532 | .Length = @sizeOf(windows.OBJECT_ATTRIBUTES), | 15528 | .ObjectName = @constCast(&windows.UNICODE_STRING{ |
| 15533 | .RootDirectory = null, | 15529 | .Length = @sizeOf(@TypeOf(device_path)), |
| 15534 | .ObjectName = &nt_name, | 15530 | .MaximumLength = 0, |
| 15535 | .Attributes = .{}, | 15531 | .Buffer = @constCast(&device_path), |
| 15536 | .SecurityDescriptor = null, | 15532 | }), |
| 15537 | .SecurityQualityOfService = null, | ||
| 15538 | }, | 15533 | }, |
| 15539 | &io_status_block, | 15534 | &io_status_block, |
| 15540 | .VALID_FLAGS, | 15535 | .VALID_FLAGS, |
| ... | @@ -15561,7 +15556,7 @@ fn getCngHandle(t: *Threaded) Io.RandomSecureError!windows.HANDLE { | ... | @@ -15561,7 +15556,7 @@ fn getCngHandle(t: *Threaded) Io.RandomSecureError!windows.HANDLE { |
| 15561 | }; | 15556 | }; |
| 15562 | } | 15557 | } |
| 15563 | 15558 | ||
| 15564 | fn getNulHandle(t: *Threaded) !windows.HANDLE { | 15559 | fn getNulDevice(t: *Threaded) !windows.HANDLE { |
| 15565 | { | 15560 | { |
| 15566 | mutexLock(&t.mutex); | 15561 | mutexLock(&t.mutex); |
| 15567 | defer mutexUnlock(&t.mutex); | 15562 | defer mutexUnlock(&t.mutex); |
| ... | @@ -15569,44 +15564,26 @@ fn getNulHandle(t: *Threaded) !windows.HANDLE { | ... | @@ -15569,44 +15564,26 @@ fn getNulHandle(t: *Threaded) !windows.HANDLE { |
| 15569 | } | 15564 | } |
| 15570 | 15565 | ||
| 15571 | const device_path = [_]u16{ '\\', 'D', 'e', 'v', 'i', 'c', 'e', '\\', 'N', 'u', 'l', 'l' }; | 15566 | const device_path = [_]u16{ '\\', 'D', 'e', 'v', 'i', 'c', 'e', '\\', 'N', 'u', 'l', 'l' }; |
| 15572 | var nt_name: windows.UNICODE_STRING = .{ | ||
| 15573 | .Length = device_path.len * 2, | ||
| 15574 | .MaximumLength = 0, | ||
| 15575 | .Buffer = @constCast(&device_path), | ||
| 15576 | }; | ||
| 15577 | const attr: windows.OBJECT_ATTRIBUTES = .{ | ||
| 15578 | .Length = @sizeOf(windows.OBJECT_ATTRIBUTES), | ||
| 15579 | .RootDirectory = null, | ||
| 15580 | .Attributes = .{ | ||
| 15581 | .INHERIT = true, | ||
| 15582 | }, | ||
| 15583 | .ObjectName = &nt_name, | ||
| 15584 | .SecurityDescriptor = null, | ||
| 15585 | .SecurityQualityOfService = null, | ||
| 15586 | }; | ||
| 15587 | var io_status_block: windows.IO_STATUS_BLOCK = undefined; | ||
| 15588 | var fresh_handle: windows.HANDLE = undefined; | 15567 | var fresh_handle: windows.HANDLE = undefined; |
| 15568 | var io_status_block: windows.IO_STATUS_BLOCK = undefined; | ||
| 15589 | var syscall: Syscall = try .start(); | 15569 | var syscall: Syscall = try .start(); |
| 15590 | while (true) switch (windows.ntdll.NtCreateFile( | 15570 | while (true) switch (windows.ntdll.NtOpenFile( |
| 15591 | &fresh_handle, | 15571 | &fresh_handle, |
| 15592 | .{ | 15572 | .{ |
| 15593 | .STANDARD = .{ .SYNCHRONIZE = true }, | 15573 | .STANDARD = .{ .SYNCHRONIZE = true }, |
| 15594 | .GENERIC = .{ .WRITE = true, .READ = true }, | 15574 | .SPECIFIC = .{ .FILE = .{ .READ_DATA = true, .WRITE_DATA = true } }, |
| 15575 | }, | ||
| 15576 | &.{ | ||
| 15577 | .Attributes = .{ .INHERIT = true }, | ||
| 15578 | .ObjectName = @constCast(&windows.UNICODE_STRING{ | ||
| 15579 | .Length = @sizeOf(@TypeOf(device_path)), | ||
| 15580 | .MaximumLength = 0, | ||
| 15581 | .Buffer = @constCast(&device_path), | ||
| 15582 | }), | ||
| 15595 | }, | 15583 | }, |
| 15596 | &attr, | ||
| 15597 | &io_status_block, | 15584 | &io_status_block, |
| 15598 | null, | ||
| 15599 | .{ .NORMAL = true }, | ||
| 15600 | .VALID_FLAGS, | 15585 | .VALID_FLAGS, |
| 15601 | .OPEN, | 15586 | .{ .IO = .SYNCHRONOUS_NONALERT }, |
| 15602 | .{ | ||
| 15603 | .DIRECTORY_FILE = false, | ||
| 15604 | .NON_DIRECTORY_FILE = true, | ||
| 15605 | .IO = .SYNCHRONOUS_NONALERT, | ||
| 15606 | .OPEN_REPARSE_POINT = false, | ||
| 15607 | }, | ||
| 15608 | null, | ||
| 15609 | 0, | ||
| 15610 | )) { | 15587 | )) { |
| 15611 | .SUCCESS => { | 15588 | .SUCCESS => { |
| 15612 | syscall.finish(); | 15589 | syscall.finish(); |
| ... | @@ -15620,6 +15597,64 @@ fn getNulHandle(t: *Threaded) !windows.HANDLE { | ... | @@ -15620,6 +15597,64 @@ fn getNulHandle(t: *Threaded) !windows.HANDLE { |
| 15620 | return fresh_handle; | 15597 | return fresh_handle; |
| 15621 | } | 15598 | } |
| 15622 | }, | 15599 | }, |
| 15600 | .CANCELLED => { | ||
| 15601 | try syscall.checkCancel(); | ||
| 15602 | continue; | ||
| 15603 | }, | ||
| 15604 | .INVALID_PARAMETER => |status| return syscall.ntstatusBug(status), | ||
| 15605 | .OBJECT_PATH_SYNTAX_BAD => |status| return syscall.ntstatusBug(status), | ||
| 15606 | .INVALID_HANDLE => |status| return syscall.ntstatusBug(status), | ||
| 15607 | .OBJECT_NAME_INVALID => return syscall.fail(error.BadPathName), | ||
| 15608 | .OBJECT_NAME_NOT_FOUND => return syscall.fail(error.FileNotFound), | ||
| 15609 | .OBJECT_PATH_NOT_FOUND => return syscall.fail(error.FileNotFound), | ||
| 15610 | .NO_MEDIA_IN_DEVICE => return syscall.fail(error.NoDevice), | ||
| 15611 | .SHARING_VIOLATION => return syscall.fail(error.AccessDenied), | ||
| 15612 | .ACCESS_DENIED => return syscall.fail(error.AccessDenied), | ||
| 15613 | .PIPE_NOT_AVAILABLE => return syscall.fail(error.NoDevice), | ||
| 15614 | .FILE_IS_A_DIRECTORY => return syscall.fail(error.IsDir), | ||
| 15615 | .NOT_A_DIRECTORY => return syscall.fail(error.NotDir), | ||
| 15616 | .USER_MAPPED_FILE => return syscall.fail(error.AccessDenied), | ||
| 15617 | else => |status| return syscall.unexpectedNtstatus(status), | ||
| 15618 | }; | ||
| 15619 | } | ||
| 15620 | |||
| 15621 | fn getNamedPipeDevice(t: *Threaded) !windows.HANDLE { | ||
| 15622 | { | ||
| 15623 | mutexLock(&t.mutex); | ||
| 15624 | defer mutexUnlock(&t.mutex); | ||
| 15625 | if (t.pipe_file.handle) |handle| return handle; | ||
| 15626 | } | ||
| 15627 | |||
| 15628 | const device_path = [_]u16{ '\\', 'D', 'e', 'v', 'i', 'c', 'e', '\\', 'N', 'a', 'm', 'e', 'd', 'P', 'i', 'p', 'e', '\\' }; | ||
| 15629 | var fresh_handle: windows.HANDLE = undefined; | ||
| 15630 | var io_status_block: windows.IO_STATUS_BLOCK = undefined; | ||
| 15631 | var syscall: Syscall = try .start(); | ||
| 15632 | while (true) switch (windows.ntdll.NtOpenFile( | ||
| 15633 | &fresh_handle, | ||
| 15634 | .{ .STANDARD = .{ .SYNCHRONIZE = true } }, | ||
| 15635 | &.{ | ||
| 15636 | .ObjectName = @constCast(&windows.UNICODE_STRING{ | ||
| 15637 | .Length = @sizeOf(@TypeOf(device_path)), | ||
| 15638 | .MaximumLength = 0, | ||
| 15639 | .Buffer = @constCast(&device_path), | ||
| 15640 | }), | ||
| 15641 | }, | ||
| 15642 | &io_status_block, | ||
| 15643 | .VALID_FLAGS, | ||
| 15644 | .{ .IO = .SYNCHRONOUS_NONALERT }, | ||
| 15645 | )) { | ||
| 15646 | .SUCCESS => { | ||
| 15647 | syscall.finish(); | ||
| 15648 | mutexLock(&t.mutex); // Another thread might have won the race. | ||
| 15649 | defer mutexUnlock(&t.mutex); | ||
| 15650 | if (t.pipe_file.handle) |prev_handle| { | ||
| 15651 | windows.CloseHandle(fresh_handle); | ||
| 15652 | return prev_handle; | ||
| 15653 | } else { | ||
| 15654 | t.pipe_file.handle = fresh_handle; | ||
| 15655 | return fresh_handle; | ||
| 15656 | } | ||
| 15657 | }, | ||
| 15623 | .DELETE_PENDING => { | 15658 | .DELETE_PENDING => { |
| 15624 | // This error means that there *was* a file in this location on | 15659 | // This error means that there *was* a file in this location on |
| 15625 | // the file system, but it was deleted. However, the OS is not | 15660 | // the file system, but it was deleted. However, the OS is not |
| ... | @@ -15666,7 +15701,7 @@ fn windowsCreateProcessPathExt( | ... | @@ -15666,7 +15701,7 @@ fn windowsCreateProcessPathExt( |
| 15666 | app_buf: *std.ArrayList(u16), | 15701 | app_buf: *std.ArrayList(u16), |
| 15667 | pathext: [:0]const u16, | 15702 | pathext: [:0]const u16, |
| 15668 | cmd_line_cache: *WindowsCommandLineCache, | 15703 | cmd_line_cache: *WindowsCommandLineCache, |
| 15669 | envp_ptr: ?[*:0]const u16, | 15704 | env_block: ?process.Environ.WindowsBlock, |
| 15670 | cwd_ptr: ?[*:0]u16, | 15705 | cwd_ptr: ?[*:0]u16, |
| 15671 | flags: windows.CreateProcessFlags, | 15706 | flags: windows.CreateProcessFlags, |
| 15672 | lpStartupInfo: *windows.STARTUPINFOW, | 15707 | lpStartupInfo: *windows.STARTUPINFOW, |
| ... | @@ -15843,7 +15878,7 @@ fn windowsCreateProcessPathExt( | ... | @@ -15843,7 +15878,7 @@ fn windowsCreateProcessPathExt( |
| 15843 | if (windowsCreateProcess( | 15878 | if (windowsCreateProcess( |
| 15844 | app_name_w.ptr, | 15879 | app_name_w.ptr, |
| 15845 | cmd_line_w.ptr, | 15880 | cmd_line_w.ptr, |
| 15846 | envp_ptr, | 15881 | env_block, |
| 15847 | cwd_ptr, | 15882 | cwd_ptr, |
| 15848 | flags, | 15883 | flags, |
| 15849 | lpStartupInfo, | 15884 | lpStartupInfo, |
| ... | @@ -15903,7 +15938,7 @@ fn windowsCreateProcessPathExt( | ... | @@ -15903,7 +15938,7 @@ fn windowsCreateProcessPathExt( |
| 15903 | else | 15938 | else |
| 15904 | full_app_name; | 15939 | full_app_name; |
| 15905 | 15940 | ||
| 15906 | if (windowsCreateProcess(app_name_w.ptr, cmd_line_w.ptr, envp_ptr, cwd_ptr, flags, lpStartupInfo, lpProcessInformation)) |_| { | 15941 | if (windowsCreateProcess(app_name_w.ptr, cmd_line_w.ptr, env_block, cwd_ptr, flags, lpStartupInfo, lpProcessInformation)) |_| { |
| 15907 | return; | 15942 | return; |
| 15908 | } else |err| switch (err) { | 15943 | } else |err| switch (err) { |
| 15909 | error.FileNotFound => continue, | 15944 | error.FileNotFound => continue, |
| ... | @@ -15927,7 +15962,7 @@ fn windowsCreateProcessPathExt( | ... | @@ -15927,7 +15962,7 @@ fn windowsCreateProcessPathExt( |
| 15927 | fn windowsCreateProcess( | 15962 | fn windowsCreateProcess( |
| 15928 | app_name: [*:0]u16, | 15963 | app_name: [*:0]u16, |
| 15929 | cmd_line: [*:0]u16, | 15964 | cmd_line: [*:0]u16, |
| 15930 | env_ptr: ?[*:0]const u16, | 15965 | env_block: ?process.Environ.WindowsBlock, |
| 15931 | cwd_ptr: ?[*:0]u16, | 15966 | cwd_ptr: ?[*:0]u16, |
| 15932 | flags: windows.CreateProcessFlags, | 15967 | flags: windows.CreateProcessFlags, |
| 15933 | lpStartupInfo: *windows.STARTUPINFOW, | 15968 | lpStartupInfo: *windows.STARTUPINFOW, |
| ... | @@ -15942,7 +15977,7 @@ fn windowsCreateProcess( | ... | @@ -15942,7 +15977,7 @@ fn windowsCreateProcess( |
| 15942 | null, | 15977 | null, |
| 15943 | windows.TRUE, | 15978 | windows.TRUE, |
| 15944 | flags, | 15979 | flags, |
| 15945 | env_ptr, | 15980 | if (env_block) |block| block.slice.ptr else null, |
| 15946 | cwd_ptr, | 15981 | cwd_ptr, |
| 15947 | lpStartupInfo, | 15982 | lpStartupInfo, |
| 15948 | lpProcessInformation, | 15983 | lpProcessInformation, |
| ... | @@ -16463,11 +16498,11 @@ fn posixExecv( | ... | @@ -16463,11 +16498,11 @@ fn posixExecv( |
| 16463 | arg0_expand: process.ArgExpansion, | 16498 | arg0_expand: process.ArgExpansion, |
| 16464 | file: [*:0]const u8, | 16499 | file: [*:0]const u8, |
| 16465 | child_argv: [*:null]?[*:0]const u8, | 16500 | child_argv: [*:null]?[*:0]const u8, |
| 16466 | envp: [*:null]const ?[*:0]const u8, | 16501 | env_block: process.Environ.PosixBlock, |
| 16467 | PATH: []const u8, | 16502 | PATH: []const u8, |
| 16468 | ) process.ReplaceError { | 16503 | ) process.ReplaceError { |
| 16469 | const file_slice = std.mem.sliceTo(file, 0); | 16504 | const file_slice = std.mem.sliceTo(file, 0); |
| 16470 | if (std.mem.findScalar(u8, file_slice, '/') != null) return posixExecvPath(file, child_argv, envp); | 16505 | if (std.mem.findScalar(u8, file_slice, '/') != null) return posixExecvPath(file, child_argv, env_block); |
| 16471 | 16506 | ||
| 16472 | // Use of PATH_MAX here is valid as the path_buf will be passed | 16507 | // Use of PATH_MAX here is valid as the path_buf will be passed |
| 16473 | // directly to the operating system in posixExecvPath. | 16508 | // directly to the operating system in posixExecvPath. |
| ... | @@ -16495,7 +16530,7 @@ fn posixExecv( | ... | @@ -16495,7 +16530,7 @@ fn posixExecv( |
| 16495 | .expand => child_argv[0] = full_path, | 16530 | .expand => child_argv[0] = full_path, |
| 16496 | .no_expand => {}, | 16531 | .no_expand => {}, |
| 16497 | } | 16532 | } |
| 16498 | err = posixExecvPath(full_path, child_argv, envp); | 16533 | err = posixExecvPath(full_path, child_argv, env_block); |
| 16499 | switch (err) { | 16534 | switch (err) { |
| 16500 | error.AccessDenied => seen_eacces = true, | 16535 | error.AccessDenied => seen_eacces = true, |
| 16501 | error.FileNotFound, error.NotDir => {}, | 16536 | error.FileNotFound, error.NotDir => {}, |
| ... | @@ -16510,10 +16545,10 @@ fn posixExecv( | ... | @@ -16510,10 +16545,10 @@ fn posixExecv( |
| 16510 | pub fn posixExecvPath( | 16545 | pub fn posixExecvPath( |
| 16511 | path: [*:0]const u8, | 16546 | path: [*:0]const u8, |
| 16512 | child_argv: [*:null]const ?[*:0]const u8, | 16547 | child_argv: [*:null]const ?[*:0]const u8, |
| 16513 | envp: [*:null]const ?[*:0]const u8, | 16548 | env_block: process.Environ.PosixBlock, |
| 16514 | ) process.ReplaceError { | 16549 | ) process.ReplaceError { |
| 16515 | try Thread.checkCancel(); | 16550 | try Thread.checkCancel(); |
| 16516 | switch (posix.errno(posix.system.execve(path, child_argv, envp))) { | 16551 | switch (posix.errno(posix.system.execve(path, child_argv, env_block.slice.ptr))) { |
| 16517 | .FAULT => |err| return errnoBug(err), // Bad pointer parameter. | 16552 | .FAULT => |err| return errnoBug(err), // Bad pointer parameter. |
| 16518 | .@"2BIG" => return error.SystemResources, | 16553 | .@"2BIG" => return error.SystemResources, |
| 16519 | .MFILE => return error.ProcessFdQuotaExceeded, | 16554 | .MFILE => return error.ProcessFdQuotaExceeded, |
| ... | @@ -16545,100 +16580,105 @@ pub fn posixExecvPath( | ... | @@ -16545,100 +16580,105 @@ pub fn posixExecvPath( |
| 16545 | } | 16580 | } |
| 16546 | } | 16581 | } |
| 16547 | 16582 | ||
| 16548 | fn windowsMakePipeIn(rd: *?windows.HANDLE, wr: *?windows.HANDLE, sattr: *const windows.SECURITY_ATTRIBUTES) !void { | 16583 | pub const CreatePipeOptions = struct { |
| 16549 | var rd_h: windows.HANDLE = undefined; | 16584 | server: End, |
| 16550 | var wr_h: windows.HANDLE = undefined; | 16585 | client: End, |
| 16551 | try windows.CreatePipe(&rd_h, &wr_h, sattr); | 16586 | inbound: bool = false, |
| 16552 | errdefer windowsDestroyPipe(rd_h, wr_h); | 16587 | outbound: bool = false, |
| 16553 | try windows.SetHandleInformation(wr_h, windows.HANDLE_FLAG_INHERIT, 0); | 16588 | maximum_instances: u32 = 1, |
| 16554 | rd.* = rd_h; | 16589 | quota: u32 = 4096, |
| 16555 | wr.* = wr_h; | 16590 | default_timeout: windows.LARGE_INTEGER = -120 * std.time.ns_per_s / 100, |
| 16556 | } | ||
| 16557 | |||
| 16558 | fn windowsDestroyPipe(rd: ?windows.HANDLE, wr: ?windows.HANDLE) void { | ||
| 16559 | if (rd) |h| posix.close(h); | ||
| 16560 | if (wr) |h| posix.close(h); | ||
| 16561 | } | ||
| 16562 | |||
| 16563 | fn windowsMakeAsyncPipe(rd: *?windows.HANDLE, wr: *?windows.HANDLE, sattr: *const windows.SECURITY_ATTRIBUTES) !void { | ||
| 16564 | var tmp_bufw: [128]u16 = undefined; | ||
| 16565 | 16591 | ||
| 16566 | // Anonymous pipes are built upon Named pipes. | 16592 | pub const End = struct { |
| 16567 | // https://docs.microsoft.com/en-us/windows/win32/api/namedpipeapi/nf-namedpipeapi-createpipe | 16593 | attributes: windows.OBJECT_ATTRIBUTES.ATTRIBUTES = .{}, |
| 16568 | // Asynchronous (overlapped) read and write operations are not supported by anonymous pipes. | 16594 | mode: windows.FILE.MODE, |
| 16569 | // https://docs.microsoft.com/en-us/windows/win32/ipc/anonymous-pipe-operations | ||
| 16570 | const pipe_path = blk: { | ||
| 16571 | var tmp_buf: [128]u8 = undefined; | ||
| 16572 | // Forge a random path for the pipe. | ||
| 16573 | const pipe_path = std.fmt.bufPrintSentinel( | ||
| 16574 | &tmp_buf, | ||
| 16575 | "\\\\.\\pipe\\zig-childprocess-{d}-{d}", | ||
| 16576 | .{ windows.GetCurrentProcessId(), pipe_name_counter.fetchAdd(1, .monotonic) }, | ||
| 16577 | 0, | ||
| 16578 | ) catch unreachable; | ||
| 16579 | const len = std.unicode.wtf8ToWtf16Le(&tmp_bufw, pipe_path) catch unreachable; | ||
| 16580 | tmp_bufw[len] = 0; | ||
| 16581 | break :blk tmp_bufw[0..len :0]; | ||
| 16582 | }; | 16595 | }; |
| 16583 | 16596 | }; | |
| 16584 | // Create the read handle that can be used with overlapped IO ops. | 16597 | pub fn windowsCreatePipe(t: *Threaded, options: CreatePipeOptions) ![2]windows.HANDLE { |
| 16585 | const read_handle = windows.kernel32.CreateNamedPipeW( | 16598 | const named_pipe_device = try t.getNamedPipeDevice(); |
| 16586 | pipe_path.ptr, | 16599 | const server_handle = server_handle: { |
| 16587 | windows.PIPE_ACCESS_INBOUND | windows.FILE_FLAG_OVERLAPPED, | 16600 | var handle: windows.HANDLE = undefined; |
| 16588 | windows.PIPE_TYPE_BYTE, | 16601 | var io_status_block: windows.IO_STATUS_BLOCK = undefined; |
| 16589 | 1, | 16602 | const syscall: Syscall = try .start(); |
| 16590 | 4096, | 16603 | while (true) switch (windows.ntdll.NtCreateNamedPipeFile( |
| 16591 | 4096, | 16604 | &handle, |
| 16592 | 0, | 16605 | .{ |
| 16593 | sattr, | 16606 | .SPECIFIC = .{ .FILE_PIPE = .{ |
| 16594 | ); | 16607 | .READ_DATA = options.inbound, |
| 16595 | if (read_handle == windows.INVALID_HANDLE_VALUE) { | 16608 | .WRITE_DATA = options.outbound, |
| 16596 | switch (windows.GetLastError()) { | 16609 | .WRITE_ATTRIBUTES = true, |
| 16597 | else => |err| return windows.unexpectedError(err), | 16610 | } }, |
| 16598 | } | 16611 | .STANDARD = .{ .SYNCHRONIZE = true }, |
| 16599 | } | 16612 | }, |
| 16600 | errdefer posix.close(read_handle); | 16613 | &.{ |
| 16601 | 16614 | .RootDirectory = named_pipe_device, | |
| 16602 | var sattr_copy = sattr.*; | 16615 | .Attributes = options.server.attributes, |
| 16603 | const write_handle = windows.kernel32.CreateFileW( | 16616 | }, |
| 16604 | pipe_path.ptr, | 16617 | &io_status_block, |
| 16605 | .{ .GENERIC = .{ .WRITE = true } }, | 16618 | .{ .READ = true, .WRITE = true }, |
| 16606 | 0, | 16619 | .CREATE, |
| 16607 | &sattr_copy, | 16620 | options.server.mode, |
| 16608 | windows.OPEN_EXISTING, | 16621 | .{ .TYPE = .BYTE_STREAM }, |
| 16609 | @bitCast(windows.FILE.ATTRIBUTE{ .NORMAL = true }), | 16622 | .{ .MODE = .BYTE_STREAM }, |
| 16610 | null, | 16623 | .{ .OPERATION = .QUEUE }, |
| 16611 | ); | 16624 | options.maximum_instances, |
| 16612 | if (write_handle == windows.INVALID_HANDLE_VALUE) { | 16625 | if (options.inbound) options.quota else 0, |
| 16613 | switch (windows.GetLastError()) { | 16626 | if (options.outbound) options.quota else 0, |
| 16614 | else => |err| return windows.unexpectedError(err), | 16627 | &options.default_timeout, |
| 16615 | } | 16628 | )) { |
| 16616 | } | 16629 | .SUCCESS => break syscall.finish(), |
| 16617 | errdefer posix.close(write_handle); | 16630 | .CANCELLED => { |
| 16618 | 16631 | try syscall.checkCancel(); | |
| 16619 | try windows.SetHandleInformation(read_handle, windows.HANDLE_FLAG_INHERIT, 0); | 16632 | continue; |
| 16620 | 16633 | }, | |
| 16621 | rd.* = read_handle; | 16634 | .INVALID_PARAMETER => |status| return syscall.ntstatusBug(status), |
| 16622 | wr.* = write_handle; | 16635 | .INSUFFICIENT_RESOURCES => return syscall.fail(error.SystemResources), |
| 16636 | else => |status| return syscall.unexpectedNtstatus(status), | ||
| 16637 | }; | ||
| 16638 | break :server_handle handle; | ||
| 16639 | }; | ||
| 16640 | errdefer windows.CloseHandle(server_handle); | ||
| 16641 | const client_handle = client_handle: { | ||
| 16642 | var handle: windows.HANDLE = undefined; | ||
| 16643 | var io_status_block: windows.IO_STATUS_BLOCK = undefined; | ||
| 16644 | const syscall: Syscall = try .start(); | ||
| 16645 | while (true) switch (windows.ntdll.NtOpenFile( | ||
| 16646 | &handle, | ||
| 16647 | .{ | ||
| 16648 | .SPECIFIC = .{ .FILE_PIPE = .{ | ||
| 16649 | .READ_DATA = options.outbound, | ||
| 16650 | .WRITE_DATA = options.inbound, | ||
| 16651 | .WRITE_ATTRIBUTES = true, | ||
| 16652 | } }, | ||
| 16653 | .STANDARD = .{ .SYNCHRONIZE = true }, | ||
| 16654 | }, | ||
| 16655 | &.{ | ||
| 16656 | .RootDirectory = server_handle, | ||
| 16657 | .Attributes = options.client.attributes, | ||
| 16658 | }, | ||
| 16659 | &io_status_block, | ||
| 16660 | .{ .READ = true, .WRITE = true }, | ||
| 16661 | options.client.mode, | ||
| 16662 | )) { | ||
| 16663 | .SUCCESS => break syscall.finish(), | ||
| 16664 | .CANCELLED => { | ||
| 16665 | try syscall.checkCancel(); | ||
| 16666 | continue; | ||
| 16667 | }, | ||
| 16668 | .INVALID_PARAMETER => |status| return syscall.ntstatusBug(status), | ||
| 16669 | .INSUFFICIENT_RESOURCES => return syscall.fail(error.SystemResources), | ||
| 16670 | else => |status| return syscall.unexpectedNtstatus(status), | ||
| 16671 | }; | ||
| 16672 | break :client_handle handle; | ||
| 16673 | }; | ||
| 16674 | errdefer windows.CloseHandle(client_handle); | ||
| 16675 | return .{ server_handle, client_handle }; | ||
| 16623 | } | 16676 | } |
| 16624 | 16677 | ||
| 16625 | var pipe_name_counter = std.atomic.Value(u32).init(1); | ||
| 16626 | |||
| 16627 | fn progressParentFile(userdata: ?*anyopaque) std.Progress.ParentFileError!File { | 16678 | fn progressParentFile(userdata: ?*anyopaque) std.Progress.ParentFileError!File { |
| 16628 | const t: *Threaded = @ptrCast(@alignCast(userdata)); | 16679 | const t: *Threaded = @ptrCast(@alignCast(userdata)); |
| 16629 | |||
| 16630 | t.scanEnviron(); | 16680 | t.scanEnviron(); |
| 16631 | 16681 | return t.environ.zig_progress_file; | |
| 16632 | const int = try t.environ.zig_progress_handle; | ||
| 16633 | |||
| 16634 | return .{ | ||
| 16635 | .handle = switch (@typeInfo(Io.File.Handle)) { | ||
| 16636 | .int => int, | ||
| 16637 | .pointer => @ptrFromInt(int), | ||
| 16638 | else => return error.UnsupportedOperation, | ||
| 16639 | }, | ||
| 16640 | .flags = .{ .nonblocking = false }, | ||
| 16641 | }; | ||
| 16642 | } | 16682 | } |
| 16643 | 16683 | ||
| 16644 | pub fn environString(t: *Threaded, comptime name: []const u8) ?[:0]const u8 { | 16684 | pub fn environString(t: *Threaded, comptime name: []const u8) ?[:0]const u8 { |
| ... | @@ -16734,7 +16774,7 @@ fn randomSecure(userdata: ?*anyopaque, buffer: []u8) Io.RandomSecureError!void { | ... | @@ -16734,7 +16774,7 @@ fn randomSecure(userdata: ?*anyopaque, buffer: []u8) Io.RandomSecureError!void { |
| 16734 | // despite the function being documented to always return TRUE | 16774 | // despite the function being documented to always return TRUE |
| 16735 | // * reads from "\\Device\\CNG" which then seeds a per-CPU AES CSPRNG | 16775 | // * reads from "\\Device\\CNG" which then seeds a per-CPU AES CSPRNG |
| 16736 | // Therefore, that function is avoided in favor of using the device directly. | 16776 | // Therefore, that function is avoided in favor of using the device directly. |
| 16737 | const cng_device = try getCngHandle(t); | 16777 | const cng_device = try getCngDevice(t); |
| 16738 | var io_status_block: windows.IO_STATUS_BLOCK = undefined; | 16778 | var io_status_block: windows.IO_STATUS_BLOCK = undefined; |
| 16739 | var i: usize = 0; | 16779 | var i: usize = 0; |
| 16740 | const syscall: Syscall = try .start(); | 16780 | const syscall: Syscall = try .start(); |
lib/std/Io/Threaded/test.zig+10-6| ... | @@ -181,13 +181,17 @@ test "cancel blocked read from pipe" { | ... | @@ -181,13 +181,17 @@ test "cancel blocked read from pipe" { |
| 181 | var write_end: Io.File = undefined; | 181 | var write_end: Io.File = undefined; |
| 182 | switch (builtin.target.os.tag) { | 182 | switch (builtin.target.os.tag) { |
| 183 | .wasi => return error.SkipZigTest, | 183 | .wasi => return error.SkipZigTest, |
| 184 | .windows => try std.os.windows.CreatePipe(&read_end.handle, &write_end.handle, &.{ | 184 | .windows => { |
| 185 | .nLength = @sizeOf(std.os.windows.SECURITY_ATTRIBUTES), | 185 | const pipe = try threaded.windowsCreatePipe(.{ |
| 186 | .lpSecurityDescriptor = null, | 186 | .server = .{ .mode = .{ .IO = .SYNCHRONOUS_NONALERT } }, |
| 187 | .bInheritHandle = std.os.windows.FALSE, | 187 | .client = .{ .mode = .{ .IO = .SYNCHRONOUS_NONALERT } }, |
| 188 | }), | 188 | .inbound = true, |
| 189 | }); | ||
| 190 | read_end = .{ .handle = pipe[0], .flags = .{ .nonblocking = false } }; | ||
| 191 | write_end = .{ .handle = pipe[1], .flags = .{ .nonblocking = false } }; | ||
| 192 | }, | ||
| 189 | else => { | 193 | else => { |
| 190 | const pipe = try std.Io.Threaded.pipe2(.{}); | 194 | const pipe = try std.Io.Threaded.pipe2(.{ .CLOEXEC = true }); |
| 191 | read_end = .{ .handle = pipe[0], .flags = .{ .nonblocking = false } }; | 195 | read_end = .{ .handle = pipe[0], .flags = .{ .nonblocking = false } }; |
| 192 | write_end = .{ .handle = pipe[1], .flags = .{ .nonblocking = false } }; | 196 | write_end = .{ .handle = pipe[1], .flags = .{ .nonblocking = false } }; |
| 193 | }, | 197 | }, |
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| ... | @@ -598,7 +598,11 @@ const WindowsThreadImpl = struct { | ... | @@ -598,7 +598,11 @@ const WindowsThreadImpl = struct { |
| 598 | } | 598 | } |
| 599 | 599 | ||
| 600 | fn join(self: Impl) void { | 600 | fn join(self: Impl) void { |
| 601 | windows.WaitForSingleObjectEx(self.thread.thread_handle, windows.INFINITE, false) catch unreachable; | 601 | const infinite_timeout: windows.LARGE_INTEGER = std.math.minInt(windows.LARGE_INTEGER); |
| 602 | switch (windows.ntdll.NtWaitForSingleObject(self.thread.thread_handle, windows.FALSE, &infinite_timeout)) { | ||
| 603 | windows.NTSTATUS.WAIT_0 => {}, | ||
| 604 | else => |status| windows.unexpectedStatus(status) catch unreachable, | ||
| 605 | } | ||
| 602 | windows.CloseHandle(self.thread.thread_handle); | 606 | windows.CloseHandle(self.thread.thread_handle); |
| 603 | assert(self.thread.completion.load(.seq_cst) == .completed); | 607 | assert(self.thread.completion.load(.seq_cst) == .completed); |
| 604 | self.thread.free(); | 608 | self.thread.free(); |
lib/std/mem/Allocator.zig+13-2| ... | @@ -452,12 +452,23 @@ pub fn dupe(allocator: Allocator, comptime T: type, m: []const T) Error![]T { | ... | @@ -452,12 +452,23 @@ pub fn dupe(allocator: Allocator, comptime T: type, m: []const T) Error![]T { |
| 452 | return new_buf; | 452 | return new_buf; |
| 453 | } | 453 | } |
| 454 | 454 | ||
| 455 | /// Deprecated in favor of `dupeSentinel` | ||
| 455 | /// Copies `m` to newly allocated memory, with a null-terminated element. Caller owns the memory. | 456 | /// Copies `m` to newly allocated memory, with a null-terminated element. Caller owns the memory. |
| 456 | pub fn dupeZ(allocator: Allocator, comptime T: type, m: []const T) Error![:0]T { | 457 | pub fn dupeZ(allocator: Allocator, comptime T: type, m: []const T) Error![:0]T { |
| 458 | return allocator.dupeSentinel(T, m, 0); | ||
| 459 | } | ||
| 460 | |||
| 461 | /// Copies `m` to newly allocated memory, with a null-terminated element. Caller owns the memory. | ||
| 462 | pub fn dupeSentinel( | ||
| 463 | allocator: Allocator, | ||
| 464 | comptime T: type, | ||
| 465 | m: []const T, | ||
| 466 | comptime sentinel: T, | ||
| 467 | ) Error![:sentinel]T { | ||
| 457 | const new_buf = try allocator.alloc(T, m.len + 1); | 468 | const new_buf = try allocator.alloc(T, m.len + 1); |
| 458 | @memcpy(new_buf[0..m.len], m); | 469 | @memcpy(new_buf[0..m.len], m); |
| 459 | new_buf[m.len] = 0; | 470 | new_buf[m.len] = sentinel; |
| 460 | return new_buf[0..m.len :0]; | 471 | return new_buf[0..m.len :sentinel]; |
| 461 | } | 472 | } |
| 462 | 473 | ||
| 463 | /// An allocator that always fails to allocate. | 474 | /// An allocator that always fails to allocate. |
lib/std/os/windows.zig+19-250| ... | @@ -521,7 +521,7 @@ pub const FILE = struct { | ... | @@ -521,7 +521,7 @@ pub const FILE = struct { |
| 521 | _, | 521 | _, |
| 522 | 522 | ||
| 523 | pub const VALID_FLAGS: @This() = @enumFromInt(0b11); | 523 | pub const VALID_FLAGS: @This() = @enumFromInt(0b11); |
| 524 | } = .ASYNCHRONOUS, | 524 | }, |
| 525 | /// The file being opened must not be a directory file or this call | 525 | /// The file being opened must not be a directory file or this call |
| 526 | /// fails. The file object being opened can represent a data file, a | 526 | /// fails. The file object being opened can represent a data file, a |
| 527 | /// logical, virtual, or physical device, or a volume. | 527 | /// logical, virtual, or physical device, or a volume. |
| ... | @@ -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+438-272| ... | @@ -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,12 +14,7 @@ const mem = std.mem; | ... | @@ -14,12 +14,7 @@ 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 = .{ | 17 | pub const empty: Environ = .{ .block = .empty }; |
| 18 | .block = switch (Block) { | ||
| 19 | void => {}, | ||
| 20 | else => &.{}, | ||
| 21 | }, | ||
| 22 | }; | ||
| 23 | 18 | ||
| 24 | /// On WASI without libc, this is `void` because the environment has to be | 19 | /// On WASI without libc, this is `void` because the environment has to be |
| 25 | /// queried and heap-allocated at runtime. | 20 | /// queried and heap-allocated at runtime. |
| ... | @@ -28,13 +23,65 @@ pub const empty: Environ = .{ | ... | @@ -28,13 +23,65 @@ pub const empty: Environ = .{ |
| 28 | /// is modified, so a long-lived pointer cannot be used. Therefore, on this | 23 | /// is modified, so a long-lived pointer cannot be used. Therefore, on this |
| 29 | /// operating system `void` is also used. | 24 | /// operating system `void` is also used. |
| 30 | pub const Block = switch (native_os) { | 25 | pub const Block = switch (native_os) { |
| 31 | .windows => void, | 26 | .windows => GlobalBlock, |
| 32 | .wasi => switch (builtin.link_libc) { | 27 | .wasi => switch (builtin.link_libc) { |
| 33 | false => void, | 28 | false => GlobalBlock, |
| 34 | true => [:null]const ?[*:0]const u8, | 29 | true => PosixBlock, |
| 35 | }, | 30 | }, |
| 36 | .freestanding, .other => void, | 31 | .freestanding, .other => GlobalBlock, |
| 37 | else => [:null]const ?[*:0]const u8, | 32 | else => PosixBlock, |
| 33 | }; | ||
| 34 | |||
| 35 | pub const GlobalBlock = struct { | ||
| 36 | use_global: bool, | ||
| 37 | |||
| 38 | pub const empty: GlobalBlock = .{ .use_global = false }; | ||
| 39 | pub const global: GlobalBlock = .{ .use_global = true }; | ||
| 40 | |||
| 41 | pub fn deinit(_: GlobalBlock, _: Allocator) void {} | ||
| 42 | }; | ||
| 43 | |||
| 44 | pub const PosixBlock = struct { | ||
| 45 | slice: [:null]const ?[*:0]const u8, | ||
| 46 | |||
| 47 | pub const empty: PosixBlock = .{ .slice = &.{} }; | ||
| 48 | |||
| 49 | pub fn deinit(block: PosixBlock, gpa: Allocator) void { | ||
| 50 | for (block.slice) |entry| gpa.free(mem.span(entry.?)); | ||
| 51 | gpa.free(block.slice); | ||
| 52 | } | ||
| 53 | |||
| 54 | pub const View = struct { | ||
| 55 | slice: []const [*:0]const u8, | ||
| 56 | |||
| 57 | pub fn isEmpty(v: View) bool { | ||
| 58 | return v.slice.len == 0; | ||
| 59 | } | ||
| 60 | }; | ||
| 61 | pub fn view(block: PosixBlock) View { | ||
| 62 | return .{ .slice = @ptrCast(block.slice) }; | ||
| 63 | } | ||
| 64 | }; | ||
| 65 | |||
| 66 | pub const WindowsBlock = struct { | ||
| 67 | slice: [:0]const u16, | ||
| 68 | |||
| 69 | pub const empty: WindowsBlock = .{ .slice = &.{0} }; | ||
| 70 | |||
| 71 | pub fn deinit(block: WindowsBlock, gpa: Allocator) void { | ||
| 72 | gpa.free(block.slice); | ||
| 73 | } | ||
| 74 | |||
| 75 | pub const View = struct { | ||
| 76 | ptr: [*:0]const u16, | ||
| 77 | |||
| 78 | pub fn isEmpty(v: View) bool { | ||
| 79 | return v.ptr[0] == 0; | ||
| 80 | } | ||
| 81 | }; | ||
| 82 | pub fn view(block: WindowsBlock) View { | ||
| 83 | return .{ .ptr = block.slice.ptr }; | ||
| 84 | } | ||
| 38 | }; | 85 | }; |
| 39 | 86 | ||
| 40 | pub const Map = struct { | 87 | pub const Map = struct { |
| ... | @@ -46,47 +93,60 @@ pub const Map = struct { | ... | @@ -46,47 +93,60 @@ pub const Map = struct { |
| 46 | pub const Size = usize; | 93 | pub const Size = usize; |
| 47 | 94 | ||
| 48 | pub const EnvNameHashContext = struct { | 95 | 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 { | 96 | pub fn hash(self: @This(), s: []const u8) u32 { |
| 56 | _ = self; | 97 | _ = self; |
| 57 | if (native_os == .windows) { | 98 | switch (native_os) { |
| 58 | var h = std.hash.Wyhash.init(0); | 99 | else => return std.array_hash_map.hashString(s), |
| 59 | var it = unicode.Wtf8View.initUnchecked(s).iterator(); | 100 | .windows => { |
| 60 | while (it.nextCodepoint()) |cp| { | 101 | var h = std.hash.Wyhash.init(0); |
| 61 | const cp_upper = upcase(cp); | 102 | var it = unicode.Wtf8View.initUnchecked(s).iterator(); |
| 62 | h.update(&[_]u8{ | 103 | while (it.nextCodepoint()) |cp| { |
| 63 | @as(u8, @intCast((cp_upper >> 16) & 0xff)), | 104 | const cp_upper = if (std.math.cast(u16, cp)) |wtf16| |
| 64 | @as(u8, @intCast((cp_upper >> 8) & 0xff)), | 105 | std.os.windows.toUpperWtf16(wtf16) |
| 65 | @as(u8, @intCast((cp_upper >> 0) & 0xff)), | 106 | else |
| 66 | }); | 107 | cp; |
| 67 | } | 108 | h.update(&[_]u8{ |
| 68 | return @truncate(h.final()); | 109 | @truncate(cp_upper >> 0), |
| 110 | @truncate(cp_upper >> 8), | ||
| 111 | @truncate(cp_upper >> 16), | ||
| 112 | }); | ||
| 113 | } | ||
| 114 | return @truncate(h.final()); | ||
| 115 | }, | ||
| 69 | } | 116 | } |
| 70 | return std.array_hash_map.hashString(s); | ||
| 71 | } | 117 | } |
| 72 | 118 | ||
| 73 | pub fn eql(self: @This(), a: []const u8, b: []const u8, b_index: usize) bool { | 119 | pub fn eql(self: @This(), a: []const u8, b: []const u8, b_index: usize) bool { |
| 74 | _ = self; | 120 | _ = self; |
| 75 | _ = b_index; | 121 | _ = b_index; |
| 76 | if (native_os == .windows) { | 122 | 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 | } | 123 | } |
| 89 | }; | 124 | }; |
| 125 | fn eqlKeys(a: []const u8, b: []const u8) bool { | ||
| 126 | return switch (native_os) { | ||
| 127 | else => std.array_hash_map.eqlString(a, b), | ||
| 128 | .windows => std.os.windows.eqlIgnoreCaseWtf8(a, b), | ||
| 129 | }; | ||
| 130 | } | ||
| 131 | |||
| 132 | pub fn validateKey(key: []const u8) bool { | ||
| 133 | switch (native_os) { | ||
| 134 | else => return key.len > 0 and mem.findAny(u8, key, &.{ 0, '=' }) == null, | ||
| 135 | .windows => { | ||
| 136 | if (!unicode.wtf8ValidateSlice(key)) return false; | ||
| 137 | var it = unicode.Wtf8View.initUnchecked(key).iterator(); | ||
| 138 | switch (it.nextCodepoint() orelse return false) { | ||
| 139 | 0 => return false, | ||
| 140 | else => {}, | ||
| 141 | } | ||
| 142 | while (it.nextCodepoint()) |cp| switch (cp) { | ||
| 143 | 0, '=' => return false, | ||
| 144 | else => {}, | ||
| 145 | }; | ||
| 146 | return true; | ||
| 147 | }, | ||
| 148 | } | ||
| 149 | } | ||
| 90 | 150 | ||
| 91 | /// Create a Map backed by a specific allocator. | 151 | /// Create a Map backed by a specific allocator. |
| 92 | /// That allocator will be used for both backing allocations | 152 | /// That allocator will be used for both backing allocations |
| ... | @@ -99,30 +159,71 @@ pub const Map = struct { | ... | @@ -99,30 +159,71 @@ pub const Map = struct { |
| 99 | /// of the stored keys and values. | 159 | /// of the stored keys and values. |
| 100 | pub fn deinit(self: *Map) void { | 160 | pub fn deinit(self: *Map) void { |
| 101 | const gpa = self.allocator; | 161 | const gpa = self.allocator; |
| 102 | var it = self.array_hash_map.iterator(); | 162 | for (self.keys()) |key| gpa.free(key); |
| 103 | while (it.next()) |entry| { | 163 | for (self.values()) |value| gpa.free(value); |
| 104 | gpa.free(entry.key_ptr.*); | ||
| 105 | gpa.free(entry.value_ptr.*); | ||
| 106 | } | ||
| 107 | self.array_hash_map.deinit(gpa); | 164 | self.array_hash_map.deinit(gpa); |
| 108 | self.* = undefined; | 165 | self.* = undefined; |
| 109 | } | 166 | } |
| 110 | 167 | ||
| 111 | pub fn keys(m: *const Map) [][]const u8 { | 168 | pub fn keys(map: *const Map) [][]const u8 { |
| 112 | return m.array_hash_map.keys(); | 169 | return map.array_hash_map.keys(); |
| 170 | } | ||
| 171 | |||
| 172 | pub fn values(map: *const Map) [][]const u8 { | ||
| 173 | return map.array_hash_map.values(); | ||
| 174 | } | ||
| 175 | |||
| 176 | pub fn putPosixBlock(map: *Map, view: PosixBlock.View) Allocator.Error!void { | ||
| 177 | for (view.slice) |entry| { | ||
| 178 | var entry_i: usize = 0; | ||
| 179 | while (entry[entry_i] != 0 and entry[entry_i] != '=') : (entry_i += 1) {} | ||
| 180 | const key = entry[0..entry_i]; | ||
| 181 | |||
| 182 | var end_i: usize = entry_i; | ||
| 183 | while (entry[end_i] != 0) : (end_i += 1) {} | ||
| 184 | const value = entry[entry_i + 1 .. end_i]; | ||
| 185 | |||
| 186 | try map.put(key, value); | ||
| 187 | } | ||
| 113 | } | 188 | } |
| 114 | 189 | ||
| 115 | pub fn values(m: *const Map) [][]const u8 { | 190 | pub fn putWindowsBlock(map: *Map, view: WindowsBlock.View) Allocator.Error!void { |
| 116 | return m.array_hash_map.values(); | 191 | var i: usize = 0; |
| 192 | while (view.ptr[i] != 0) { | ||
| 193 | const key_start = i; | ||
| 194 | |||
| 195 | // There are some special environment variables that start with =, | ||
| 196 | // so we need a special case to not treat = as a key/value separator | ||
| 197 | // if it's the first character. | ||
| 198 | // https://devblogs.microsoft.com/oldnewthing/20100506-00/?p=14133 | ||
| 199 | if (view.ptr[key_start] == '=') i += 1; | ||
| 200 | |||
| 201 | while (view.ptr[i] != 0 and view.ptr[i] != '=') : (i += 1) {} | ||
| 202 | const key_w = view.ptr[key_start..i]; | ||
| 203 | const key = try unicode.wtf16LeToWtf8Alloc(map.allocator, key_w); | ||
| 204 | errdefer map.allocator.free(key); | ||
| 205 | |||
| 206 | if (view.ptr[i] == '=') i += 1; | ||
| 207 | |||
| 208 | const value_start = i; | ||
| 209 | while (view.ptr[i] != 0) : (i += 1) {} | ||
| 210 | const value_w = view.ptr[value_start..i]; | ||
| 211 | const value = try unicode.wtf16LeToWtf8Alloc(map.allocator, value_w); | ||
| 212 | errdefer map.allocator.free(value); | ||
| 213 | |||
| 214 | i += 1; // skip over null byte | ||
| 215 | |||
| 216 | try map.putMove(key, value); | ||
| 217 | } | ||
| 117 | } | 218 | } |
| 118 | 219 | ||
| 119 | /// Same as `put` but the key and value become owned by the Map rather | 220 | /// Same as `put` but the key and value become owned by the Map rather |
| 120 | /// than being copied. | 221 | /// than being copied. |
| 121 | /// If `putMove` fails, the ownership of key and value does not transfer. | 222 | /// 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. | 223 | /// 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 { | 224 | pub fn putMove(self: *Map, key: []u8, value: []u8) Allocator.Error!void { |
| 225 | assert(validateKey(key)); | ||
| 124 | const gpa = self.allocator; | 226 | const gpa = self.allocator; |
| 125 | assert(unicode.wtf8ValidateSlice(key)); | ||
| 126 | const get_or_put = try self.array_hash_map.getOrPut(gpa, key); | 227 | const get_or_put = try self.array_hash_map.getOrPut(gpa, key); |
| 127 | if (get_or_put.found_existing) { | 228 | if (get_or_put.found_existing) { |
| 128 | gpa.free(get_or_put.key_ptr.*); | 229 | gpa.free(get_or_put.key_ptr.*); |
| ... | @@ -134,8 +235,8 @@ pub const Map = struct { | ... | @@ -134,8 +235,8 @@ pub const Map = struct { |
| 134 | 235 | ||
| 135 | /// `key` and `value` are copied into the Map. | 236 | /// `key` and `value` are copied into the Map. |
| 136 | /// On Windows `key` must be a valid [WTF-8](https://wtf-8.codeberg.page/) string. | 237 | /// 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 { | 238 | pub fn put(self: *Map, key: []const u8, value: []const u8) Allocator.Error!void { |
| 138 | assert(unicode.wtf8ValidateSlice(key)); | 239 | assert(validateKey(key)); |
| 139 | const gpa = self.allocator; | 240 | const gpa = self.allocator; |
| 140 | const value_copy = try gpa.dupe(u8, value); | 241 | const value_copy = try gpa.dupe(u8, value); |
| 141 | errdefer gpa.free(value_copy); | 242 | errdefer gpa.free(value_copy); |
| ... | @@ -155,7 +256,7 @@ pub const Map = struct { | ... | @@ -155,7 +256,7 @@ pub const Map = struct { |
| 155 | /// The returned pointer is invalidated if the map resizes. | 256 | /// 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. | 257 | /// 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 { | 258 | pub fn getPtr(self: Map, key: []const u8) ?*[]const u8 { |
| 158 | assert(unicode.wtf8ValidateSlice(key)); | 259 | assert(validateKey(key)); |
| 159 | return self.array_hash_map.getPtr(key); | 260 | return self.array_hash_map.getPtr(key); |
| 160 | } | 261 | } |
| 161 | 262 | ||
| ... | @@ -164,11 +265,12 @@ pub const Map = struct { | ... | @@ -164,11 +265,12 @@ pub const Map = struct { |
| 164 | /// key is removed from the map. | 265 | /// key is removed from the map. |
| 165 | /// On Windows `key` must be a valid [WTF-8](https://wtf-8.codeberg.page/) string. | 266 | /// 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 { | 267 | pub fn get(self: Map, key: []const u8) ?[]const u8 { |
| 167 | assert(unicode.wtf8ValidateSlice(key)); | 268 | assert(validateKey(key)); |
| 168 | return self.array_hash_map.get(key); | 269 | return self.array_hash_map.get(key); |
| 169 | } | 270 | } |
| 170 | 271 | ||
| 171 | pub fn contains(m: *const Map, key: []const u8) bool { | 272 | pub fn contains(m: *const Map, key: []const u8) bool { |
| 273 | assert(validateKey(key)); | ||
| 172 | return m.array_hash_map.contains(key); | 274 | return m.array_hash_map.contains(key); |
| 173 | } | 275 | } |
| 174 | 276 | ||
| ... | @@ -181,7 +283,7 @@ pub const Map = struct { | ... | @@ -181,7 +283,7 @@ pub const Map = struct { |
| 181 | /// This invalidates the value returned by get() for this key. | 283 | /// 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. | 284 | /// 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 { | 285 | pub fn swapRemove(self: *Map, key: []const u8) bool { |
| 184 | assert(unicode.wtf8ValidateSlice(key)); | 286 | assert(validateKey(key)); |
| 185 | const kv = self.array_hash_map.fetchSwapRemove(key) orelse return false; | 287 | const kv = self.array_hash_map.fetchSwapRemove(key) orelse return false; |
| 186 | const gpa = self.allocator; | 288 | const gpa = self.allocator; |
| 187 | gpa.free(kv.key); | 289 | gpa.free(kv.key); |
| ... | @@ -198,7 +300,7 @@ pub const Map = struct { | ... | @@ -198,7 +300,7 @@ pub const Map = struct { |
| 198 | /// This invalidates the value returned by get() for this key. | 300 | /// 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. | 301 | /// 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 { | 302 | pub fn orderedRemove(self: *Map, key: []const u8) bool { |
| 201 | assert(unicode.wtf8ValidateSlice(key)); | 303 | assert(validateKey(key)); |
| 202 | const kv = self.array_hash_map.fetchOrderedRemove(key) orelse return false; | 304 | const kv = self.array_hash_map.fetchOrderedRemove(key) orelse return false; |
| 203 | const gpa = self.allocator; | 305 | const gpa = self.allocator; |
| 204 | gpa.free(kv.key); | 306 | gpa.free(kv.key); |
| ... | @@ -233,105 +335,120 @@ pub const Map = struct { | ... | @@ -233,105 +335,120 @@ pub const Map = struct { |
| 233 | 335 | ||
| 234 | /// Creates a null-delimited environment variable block in the format | 336 | /// Creates a null-delimited environment variable block in the format |
| 235 | /// expected by POSIX, from a hash map plus options. | 337 | /// expected by POSIX, from a hash map plus options. |
| 236 | pub fn createBlockPosix( | 338 | pub fn createPosixBlock( |
| 237 | map: *const Map, | 339 | map: *const Map, |
| 238 | arena: Allocator, | 340 | gpa: Allocator, |
| 239 | options: CreateBlockPosixOptions, | 341 | options: CreatePosixBlockOptions, |
| 240 | ) Allocator.Error![:null]?[*:0]u8 { | 342 | ) Allocator.Error!PosixBlock { |
| 241 | const ZigProgressAction = enum { nothing, edit, delete, add }; | 343 | const ZigProgressAction = enum { nothing, edit, delete, add }; |
| 242 | const zig_progress_action: ZigProgressAction = a: { | 344 | const zig_progress_action: ZigProgressAction = action: { |
| 243 | const fd = options.zig_progress_fd orelse break :a .nothing; | 345 | const fd = options.zig_progress_fd orelse break :action .nothing; |
| 244 | const exists = map.get("ZIG_PROGRESS") != null; | 346 | const exists = map.contains("ZIG_PROGRESS"); |
| 245 | if (fd >= 0) { | 347 | if (fd >= 0) { |
| 246 | break :a if (exists) .edit else .add; | 348 | break :action if (exists) .edit else .add; |
| 247 | } else { | 349 | } else { |
| 248 | if (exists) break :a .delete; | 350 | if (exists) break :action .delete; |
| 249 | } | 351 | } |
| 250 | break :a .nothing; | 352 | break :action .nothing; |
| 251 | }; | 353 | }; |
| 252 | 354 | ||
| 253 | const envp_count: usize = c: { | 355 | const envp = try gpa.allocSentinel(?[*:0]u8, len: { |
| 254 | var c: usize = map.count(); | 356 | var len: usize = map.count(); |
| 255 | switch (zig_progress_action) { | 357 | switch (zig_progress_action) { |
| 256 | .add => c += 1, | 358 | .add => len += 1, |
| 257 | .delete => c -= 1, | 359 | .delete => len -= 1, |
| 258 | .nothing, .edit => {}, | 360 | .nothing, .edit => {}, |
| 259 | } | 361 | } |
| 260 | break :c c; | 362 | break :len len; |
| 261 | }; | 363 | }, null); |
| 262 | 364 | var envp_len: usize = 0; | |
| 263 | const envp_buf = try arena.allocSentinel(?[*:0]u8, envp_count, null); | 365 | errdefer { |
| 264 | var i: usize = 0; | 366 | envp[envp_len] = null; |
| 367 | PosixBlock.deinit(.{ .slice = envp[0..envp_len :null] }, gpa); | ||
| 368 | } | ||
| 265 | 369 | ||
| 266 | if (zig_progress_action == .add) { | 370 | if (zig_progress_action == .add) { |
| 267 | envp_buf[i] = try std.fmt.allocPrintSentinel(arena, "ZIG_PROGRESS={d}", .{options.zig_progress_fd.?}, 0); | 371 | envp[envp_len] = try std.fmt.allocPrintSentinel(gpa, "ZIG_PROGRESS={d}", .{options.zig_progress_fd.?}, 0); |
| 268 | i += 1; | 372 | envp_len += 1; |
| 269 | } | 373 | } |
| 270 | 374 | ||
| 271 | { | 375 | for (map.keys(), map.values()) |key, value| { |
| 272 | var it = map.iterator(); | 376 | if (mem.eql(u8, key, "ZIG_PROGRESS")) switch (zig_progress_action) { |
| 273 | while (it.next()) |pair| { | 377 | .add => unreachable, |
| 274 | if (mem.eql(u8, pair.key_ptr.*, "ZIG_PROGRESS")) switch (zig_progress_action) { | 378 | .delete => continue, |
| 275 | .add => unreachable, | 379 | .edit => { |
| 276 | .delete => continue, | 380 | envp[envp_len] = try std.fmt.allocPrintSentinel(gpa, "{s}={d}", .{ |
| 277 | .edit => { | 381 | key, options.zig_progress_fd.?, |
| 278 | envp_buf[i] = try std.fmt.allocPrintSentinel(arena, "{s}={d}", .{ | 382 | }, 0); |
| 279 | pair.key_ptr.*, options.zig_progress_fd.?, | 383 | envp_len += 1; |
| 280 | }, 0); | 384 | continue; |
| 281 | i += 1; | 385 | }, |
| 282 | continue; | 386 | .nothing => {}, |
| 283 | }, | 387 | }; |
| 284 | .nothing => {}, | 388 | |
| 285 | }; | 389 | envp[envp_len] = try std.fmt.allocPrintSentinel(gpa, "{s}={s}", .{ key, value }, 0); |
| 286 | 390 | envp_len += 1; | |
| 287 | envp_buf[i] = try std.fmt.allocPrintSentinel(arena, "{s}={s}", .{ pair.key_ptr.*, pair.value_ptr.* }, 0); | ||
| 288 | i += 1; | ||
| 289 | } | ||
| 290 | } | 391 | } |
| 291 | 392 | ||
| 292 | assert(i == envp_count); | 393 | assert(envp_len == envp.len); |
| 293 | return envp_buf; | 394 | return .{ .slice = envp }; |
| 294 | } | 395 | } |
| 295 | 396 | ||
| 296 | /// Caller owns result. | 397 | /// Caller owns result. |
| 297 | pub fn createBlockWindows(map: *const Map, gpa: Allocator) error{ OutOfMemory, InvalidWtf8 }![:0]u16 { | 398 | pub fn createWindowsBlock( |
| 399 | map: *const Map, | ||
| 400 | gpa: Allocator, | ||
| 401 | options: CreateWindowsBlockOptions, | ||
| 402 | ) error{ OutOfMemory, InvalidWtf8 }!WindowsBlock { | ||
| 298 | // count bytes needed | 403 | // count bytes needed |
| 299 | const max_chars_needed = x: { | 404 | const max_chars_needed = max_chars_needed: { |
| 300 | // Only need 2 trailing NUL code units for an empty environment | 405 | var max_chars_needed: usize = "\x00".len; |
| 301 | var max_chars_needed: usize = if (map.count() == 0) 2 else 1; | 406 | if (options.zig_progress_handle) |handle| if (handle != std.os.windows.INVALID_HANDLE_VALUE) { |
| 302 | var it = map.iterator(); | 407 | max_chars_needed += std.fmt.count("ZIG_PROGRESS={d}\x00", .{@intFromPtr(handle)}); |
| 303 | while (it.next()) |pair| { | 408 | }; |
| 304 | // +1 for '=' | 409 | for (map.keys(), map.values()) |key, value| { |
| 305 | // +1 for null byte | 410 | if (options.zig_progress_handle != null and eqlKeys(key, "ZIG_PROGRESS")) continue; |
| 306 | max_chars_needed += pair.key_ptr.len + pair.value_ptr.len + 2; | 411 | max_chars_needed += key.len + "=".len + value.len + "\x00".len; |
| 307 | } | 412 | } |
| 308 | break :x max_chars_needed; | 413 | break :max_chars_needed @max("\x00\x00".len, max_chars_needed); |
| 309 | }; | 414 | }; |
| 310 | const result = try gpa.alloc(u16, max_chars_needed); | 415 | const block = try gpa.alloc(u16, max_chars_needed); |
| 311 | errdefer gpa.free(result); | 416 | errdefer gpa.free(block); |
| 312 | 417 | ||
| 313 | var it = map.iterator(); | ||
| 314 | var i: usize = 0; | 418 | var i: usize = 0; |
| 315 | while (it.next()) |pair| { | 419 | if (options.zig_progress_handle) |handle| if (handle != std.os.windows.INVALID_HANDLE_VALUE) { |
| 316 | i += try unicode.wtf8ToWtf16Le(result[i..], pair.key_ptr.*); | 420 | @memcpy( |
| 317 | result[i] = '='; | 421 | block[i..][0.."ZIG_PROGRESS=".len], |
| 422 | &[_]u16{ 'Z', 'I', 'G', '_', 'P', 'R', 'O', 'G', 'R', 'E', 'S', 'S', '=' }, | ||
| 423 | ); | ||
| 424 | i += "ZIG_PROGRESS=".len; | ||
| 425 | var value_buf: [std.fmt.count("{d}", .{std.math.maxInt(usize)})]u8 = undefined; | ||
| 426 | const value = std.fmt.bufPrint(&value_buf, "{d}", .{@intFromPtr(handle)}) catch unreachable; | ||
| 427 | for (block[i..][0..value.len], value) |*r, v| r.* = v; | ||
| 428 | i += value.len; | ||
| 429 | block[i] = 0; | ||
| 318 | i += 1; | 430 | i += 1; |
| 319 | i += try unicode.wtf8ToWtf16Le(result[i..], pair.value_ptr.*); | 431 | }; |
| 320 | result[i] = 0; | 432 | for (map.keys(), map.values()) |key, value| { |
| 433 | if (options.zig_progress_handle != null and eqlKeys(key, "ZIG_PROGRESS")) continue; | ||
| 434 | i += try unicode.wtf8ToWtf16Le(block[i..], key); | ||
| 435 | block[i] = '='; | ||
| 436 | i += 1; | ||
| 437 | i += try unicode.wtf8ToWtf16Le(block[i..], value); | ||
| 438 | block[i] = 0; | ||
| 321 | i += 1; | 439 | i += 1; |
| 322 | } | 440 | } |
| 323 | result[i] = 0; | ||
| 324 | i += 1; | ||
| 325 | // An empty environment is a special case that requires a redundant | 441 | // An empty environment is a special case that requires a redundant |
| 326 | // NUL terminator. CreateProcess will read the second code unit even | 442 | // NUL terminator. CreateProcess will read the second code unit even |
| 327 | // though theoretically the first should be enough to recognize that the | 443 | // though theoretically the first should be enough to recognize that the |
| 328 | // environment is empty (see https://nullprogram.com/blog/2023/08/23/) | 444 | // environment is empty (see https://nullprogram.com/blog/2023/08/23/) |
| 329 | if (map.count() == 0) { | 445 | for (0..2) |_| { |
| 330 | result[i] = 0; | 446 | block[i] = 0; |
| 331 | i += 1; | 447 | i += 1; |
| 332 | } | 448 | if (i >= 2) break; |
| 333 | const reallocated = try gpa.realloc(result, i); | 449 | } else unreachable; |
| 334 | return reallocated[0 .. i - 1 :0]; | 450 | const reallocated = try gpa.realloc(block, i); |
| 451 | return .{ .slice = reallocated[0 .. i - 1 :0] }; | ||
| 335 | } | 452 | } |
| 336 | }; | 453 | }; |
| 337 | 454 | ||
| ... | @@ -344,13 +461,18 @@ pub const CreateMapError = error{ | ... | @@ -344,13 +461,18 @@ pub const CreateMapError = error{ |
| 344 | 461 | ||
| 345 | /// Allocates a `Map` and copies environment block into it. | 462 | /// Allocates a `Map` and copies environment block into it. |
| 346 | pub fn createMap(env: Environ, allocator: Allocator) CreateMapError!Map { | 463 | pub fn createMap(env: Environ, allocator: Allocator) CreateMapError!Map { |
| 347 | if (native_os == .windows) | 464 | var map = Map.init(allocator); |
| 348 | return createMapWide(std.os.windows.peb().ProcessParameters.Environment, allocator); | 465 | errdefer map.deinit(); |
| 349 | 466 | if (native_os == .windows) empty: { | |
| 350 | var result = Map.init(allocator); | 467 | if (!env.block.use_global) break :empty; |
| 351 | errdefer result.deinit(); | 468 | |
| 469 | const peb = std.os.windows.peb(); | ||
| 470 | assert(std.os.windows.ntdll.RtlEnterCriticalSection(peb.FastPebLock) == .SUCCESS); | ||
| 471 | defer assert(std.os.windows.ntdll.RtlLeaveCriticalSection(peb.FastPebLock) == .SUCCESS); | ||
| 472 | try map.putWindowsBlock(.{ .ptr = peb.ProcessParameters.Environment }); | ||
| 473 | } else if (native_os == .wasi and !builtin.link_libc) empty: { | ||
| 474 | if (!env.block.use_global) break :empty; | ||
| 352 | 475 | ||
| 353 | if (native_os == .wasi and !builtin.link_libc) { | ||
| 354 | var environ_count: usize = undefined; | 476 | var environ_count: usize = undefined; |
| 355 | var environ_buf_size: usize = undefined; | 477 | var environ_buf_size: usize = undefined; |
| 356 | 478 | ||
| ... | @@ -360,7 +482,7 @@ pub fn createMap(env: Environ, allocator: Allocator) CreateMapError!Map { | ... | @@ -360,7 +482,7 @@ pub fn createMap(env: Environ, allocator: Allocator) CreateMapError!Map { |
| 360 | } | 482 | } |
| 361 | 483 | ||
| 362 | if (environ_count == 0) { | 484 | if (environ_count == 0) { |
| 363 | return result; | 485 | return map; |
| 364 | } | 486 | } |
| 365 | 487 | ||
| 366 | const environ = try allocator.alloc([*:0]u8, environ_count); | 488 | const environ = try allocator.alloc([*:0]u8, environ_count); |
| ... | @@ -373,63 +495,9 @@ pub fn createMap(env: Environ, allocator: Allocator) CreateMapError!Map { | ... | @@ -373,63 +495,9 @@ pub fn createMap(env: Environ, allocator: Allocator) CreateMapError!Map { |
| 373 | return posix.unexpectedErrno(environ_get_ret); | 495 | return posix.unexpectedErrno(environ_get_ret); |
| 374 | } | 496 | } |
| 375 | 497 | ||
| 376 | for (environ) |line| { | 498 | try map.putPosixBlock(.{ .slice = environ }); |
| 377 | const pair = mem.sliceTo(line, 0); | 499 | } else try map.putPosixBlock(env.block.view()); |
| 378 | var parts = mem.splitScalar(u8, pair, '='); | 500 | 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 | } | 501 | } |
| 434 | 502 | ||
| 435 | pub const ContainsError = error{ | 503 | pub const ContainsError = error{ |
| ... | @@ -451,6 +519,7 @@ pub const ContainsError = error{ | ... | @@ -451,6 +519,7 @@ pub const ContainsError = error{ |
| 451 | /// * `containsConstant` | 519 | /// * `containsConstant` |
| 452 | /// * `containsUnempty` | 520 | /// * `containsUnempty` |
| 453 | pub fn contains(environ: Environ, gpa: Allocator, key: []const u8) ContainsError!bool { | 521 | pub fn contains(environ: Environ, gpa: Allocator, key: []const u8) ContainsError!bool { |
| 522 | if (native_os == .windows and !unicode.wtf8ValidateSlice(key)) return error.InvalidWtf8; | ||
| 454 | var map = try createMap(environ, gpa); | 523 | var map = try createMap(environ, gpa); |
| 455 | defer map.deinit(); | 524 | defer map.deinit(); |
| 456 | return map.contains(key); | 525 | return map.contains(key); |
| ... | @@ -464,6 +533,7 @@ pub fn contains(environ: Environ, gpa: Allocator, key: []const u8) ContainsError | ... | @@ -464,6 +533,7 @@ pub fn contains(environ: Environ, gpa: Allocator, key: []const u8) ContainsError |
| 464 | /// * `containsUnemptyConstant` | 533 | /// * `containsUnemptyConstant` |
| 465 | /// * `contains` | 534 | /// * `contains` |
| 466 | pub fn containsUnempty(environ: Environ, gpa: Allocator, key: []const u8) ContainsError!bool { | 535 | pub fn containsUnempty(environ: Environ, gpa: Allocator, key: []const u8) ContainsError!bool { |
| 536 | if (native_os == .windows and !unicode.wtf8ValidateSlice(key)) return error.InvalidWtf8; | ||
| 467 | var map = try createMap(environ, gpa); | 537 | var map = try createMap(environ, gpa); |
| 468 | defer map.deinit(); | 538 | defer map.deinit(); |
| 469 | const value = map.get(key) orelse return false; | 539 | const value = map.get(key) orelse return false; |
| ... | @@ -516,16 +586,15 @@ pub inline fn containsUnemptyConstant(environ: Environ, comptime key: []const u8 | ... | @@ -516,16 +586,15 @@ pub inline fn containsUnemptyConstant(environ: Environ, comptime key: []const u8 |
| 516 | /// * `createMap` | 586 | /// * `createMap` |
| 517 | pub fn getPosix(environ: Environ, key: []const u8) ?[:0]const u8 { | 587 | pub fn getPosix(environ: Environ, key: []const u8) ?[:0]const u8 { |
| 518 | if (mem.findScalar(u8, key, '=') != null) return null; | 588 | if (mem.findScalar(u8, key, '=') != null) return null; |
| 519 | for (environ.block) |opt_line| { | 589 | for (environ.block.view().slice) |entry| { |
| 520 | const line = opt_line.?; | 590 | var entry_i: usize = 0; |
| 521 | var line_i: usize = 0; | 591 | while (entry[entry_i] != 0) : (entry_i += 1) { |
| 522 | while (line[line_i] != 0) : (line_i += 1) { | 592 | if (entry_i == key.len) break; |
| 523 | if (line_i == key.len) break; | 593 | if (entry[entry_i] != key[entry_i]) break; |
| 524 | if (line[line_i] != key[line_i]) break; | ||
| 525 | } | 594 | } |
| 526 | if ((line_i != key.len) or (line[line_i] != '=')) continue; | 595 | if ((entry_i != key.len) or (entry[entry_i] != '=')) continue; |
| 527 | 596 | ||
| 528 | return mem.sliceTo(line + line_i + 1, 0); | 597 | return mem.sliceTo(entry + entry_i + 1, 0); |
| 529 | } | 598 | } |
| 530 | return null; | 599 | return null; |
| 531 | } | 600 | } |
| ... | @@ -541,14 +610,16 @@ pub fn getPosix(environ: Environ, key: []const u8) ?[:0]const u8 { | ... | @@ -541,14 +610,16 @@ pub fn getPosix(environ: Environ, key: []const u8) ?[:0]const u8 { |
| 541 | /// * `containsConstant` | 610 | /// * `containsConstant` |
| 542 | /// * `contains` | 611 | /// * `contains` |
| 543 | pub fn getWindows(environ: Environ, key: [*:0]const u16) ?[:0]const u16 { | 612 | pub fn getWindows(environ: Environ, key: [*:0]const u16) ?[:0]const u16 { |
| 544 | comptime assert(native_os == .windows); | ||
| 545 | comptime assert(@TypeOf(environ.block) == void); | ||
| 546 | |||
| 547 | // '=' anywhere but the start makes this an invalid environment variable name. | 613 | // '=' anywhere but the start makes this an invalid environment variable name. |
| 548 | const key_slice = mem.sliceTo(key, 0); | 614 | const key_slice = mem.sliceTo(key, 0); |
| 549 | if (key_slice.len > 0 and mem.findScalar(u16, key_slice[1..], '=') != null) return null; | 615 | assert(key_slice.len > 0 and mem.findScalar(u16, key_slice[1..], '=') == null); |
| 550 | 616 | ||
| 551 | const ptr = std.os.windows.peb().ProcessParameters.Environment; | 617 | if (!environ.block.use_global) return null; |
| 618 | |||
| 619 | const peb = std.os.windows.peb(); | ||
| 620 | assert(std.os.windows.ntdll.RtlEnterCriticalSection(peb.FastPebLock) == .SUCCESS); | ||
| 621 | defer assert(std.os.windows.ntdll.RtlLeaveCriticalSection(peb.FastPebLock) == .SUCCESS); | ||
| 622 | const ptr = peb.ProcessParameters.Environment; | ||
| 552 | 623 | ||
| 553 | var i: usize = 0; | 624 | var i: usize = 0; |
| 554 | while (ptr[i] != 0) { | 625 | while (ptr[i] != 0) { |
| ... | @@ -558,8 +629,7 @@ pub fn getWindows(environ: Environ, key: [*:0]const u16) ?[:0]const u16 { | ... | @@ -558,8 +629,7 @@ pub fn getWindows(environ: Environ, key: [*:0]const u16) ?[:0]const u16 { |
| 558 | // so we need a special case to not treat = as a key/value separator | 629 | // so we need a special case to not treat = as a key/value separator |
| 559 | // if it's the first character. | 630 | // if it's the first character. |
| 560 | // https://devblogs.microsoft.com/oldnewthing/20100506-00/?p=14133 | 631 | // https://devblogs.microsoft.com/oldnewthing/20100506-00/?p=14133 |
| 561 | const equal_search_start: usize = if (key_value[0] == '=') 1 else 0; | 632 | const equal_index = mem.findScalarPos(u16, key_value, 1, '=') orelse { |
| 562 | const equal_index = mem.findScalarPos(u16, key_value, equal_search_start, '=') orelse { | ||
| 563 | // This is enforced by CreateProcess. | 633 | // This is enforced by CreateProcess. |
| 564 | // If violated, CreateProcess will fail with INVALID_PARAMETER. | 634 | // If violated, CreateProcess will fail with INVALID_PARAMETER. |
| 565 | unreachable; // must contain a = | 635 | unreachable; // must contain a = |
| ... | @@ -598,13 +668,14 @@ pub const GetAllocError = error{ | ... | @@ -598,13 +668,14 @@ pub const GetAllocError = error{ |
| 598 | /// See also: | 668 | /// See also: |
| 599 | /// * `createMap` | 669 | /// * `createMap` |
| 600 | pub fn getAlloc(environ: Environ, gpa: Allocator, key: []const u8) GetAllocError![]u8 { | 670 | pub fn getAlloc(environ: Environ, gpa: Allocator, key: []const u8) GetAllocError![]u8 { |
| 671 | if (native_os == .windows and !unicode.wtf8ValidateSlice(key)) return error.InvalidWtf8; | ||
| 601 | var map = createMap(environ, gpa) catch return error.OutOfMemory; | 672 | var map = createMap(environ, gpa) catch return error.OutOfMemory; |
| 602 | defer map.deinit(); | 673 | defer map.deinit(); |
| 603 | const val = map.get(key) orelse return error.EnvironmentVariableMissing; | 674 | const val = map.get(key) orelse return error.EnvironmentVariableMissing; |
| 604 | return gpa.dupe(u8, val); | 675 | return gpa.dupe(u8, val); |
| 605 | } | 676 | } |
| 606 | 677 | ||
| 607 | pub const CreateBlockPosixOptions = struct { | 678 | pub const CreatePosixBlockOptions = struct { |
| 608 | /// `null` means to leave the `ZIG_PROGRESS` environment variable unmodified. | 679 | /// `null` means to leave the `ZIG_PROGRESS` environment variable unmodified. |
| 609 | /// If non-null, negative means to remove the environment variable, and >= 0 | 680 | /// If non-null, negative means to remove the environment variable, and >= 0 |
| 610 | /// means to provide it with the given integer. | 681 | /// means to provide it with the given integer. |
| ... | @@ -613,67 +684,147 @@ pub const CreateBlockPosixOptions = struct { | ... | @@ -613,67 +684,147 @@ pub const CreateBlockPosixOptions = struct { |
| 613 | 684 | ||
| 614 | /// Creates a null-delimited environment variable block in the format expected | 685 | /// Creates a null-delimited environment variable block in the format expected |
| 615 | /// by POSIX, from a different one. | 686 | /// by POSIX, from a different one. |
| 616 | pub fn createBlockPosix( | 687 | pub fn createPosixBlock( |
| 617 | existing: Environ, | 688 | existing: Environ, |
| 618 | arena: Allocator, | 689 | gpa: Allocator, |
| 619 | options: CreateBlockPosixOptions, | 690 | options: CreatePosixBlockOptions, |
| 620 | ) Allocator.Error![:null]?[*:0]u8 { | 691 | ) Allocator.Error!PosixBlock { |
| 621 | const contains_zig_progress = for (existing.block) |opt_line| { | 692 | const contains_zig_progress = for (existing.block.view().slice) |entry| { |
| 622 | if (mem.eql(u8, mem.sliceTo(opt_line.?, '='), "ZIG_PROGRESS")) break true; | 693 | if (mem.eql(u8, mem.sliceTo(entry, '='), "ZIG_PROGRESS")) break true; |
| 623 | } else false; | 694 | } else false; |
| 624 | 695 | ||
| 625 | const ZigProgressAction = enum { nothing, edit, delete, add }; | 696 | const ZigProgressAction = enum { nothing, edit, delete, add }; |
| 626 | const zig_progress_action: ZigProgressAction = a: { | 697 | const zig_progress_action: ZigProgressAction = action: { |
| 627 | const fd = options.zig_progress_fd orelse break :a .nothing; | 698 | const fd = options.zig_progress_fd orelse break :action .nothing; |
| 628 | if (fd >= 0) { | 699 | if (fd >= 0) { |
| 629 | break :a if (contains_zig_progress) .edit else .add; | 700 | break :action if (contains_zig_progress) .edit else .add; |
| 630 | } else { | 701 | } else { |
| 631 | if (contains_zig_progress) break :a .delete; | 702 | if (contains_zig_progress) break :action .delete; |
| 632 | } | 703 | } |
| 633 | break :a .nothing; | 704 | break :action .nothing; |
| 634 | }; | 705 | }; |
| 635 | 706 | ||
| 636 | const envp_count: usize = c: { | 707 | const envp = try gpa.allocSentinel(?[*:0]u8, len: { |
| 637 | var count: usize = existing.block.len; | 708 | var len: usize = existing.block.slice.len; |
| 638 | switch (zig_progress_action) { | 709 | switch (zig_progress_action) { |
| 639 | .add => count += 1, | 710 | .add => len += 1, |
| 640 | .delete => count -= 1, | 711 | .delete => len -= 1, |
| 641 | .nothing, .edit => {}, | 712 | .nothing, .edit => {}, |
| 642 | } | 713 | } |
| 643 | break :c count; | 714 | break :len len; |
| 644 | }; | 715 | }, null); |
| 645 | 716 | var envp_len: usize = 0; | |
| 646 | const envp_buf = try arena.allocSentinel(?[*:0]u8, envp_count, null); | 717 | errdefer { |
| 647 | var i: usize = 0; | 718 | envp[envp_len] = null; |
| 648 | var existing_index: usize = 0; | 719 | PosixBlock.deinit(.{ .slice = envp[0..envp_len :null] }, gpa); |
| 649 | 720 | } | |
| 650 | if (zig_progress_action == .add) { | 721 | if (zig_progress_action == .add) { |
| 651 | envp_buf[i] = try std.fmt.allocPrintSentinel(arena, "ZIG_PROGRESS={d}", .{options.zig_progress_fd.?}, 0); | 722 | envp[envp_len] = try std.fmt.allocPrintSentinel(gpa, "ZIG_PROGRESS={d}", .{options.zig_progress_fd.?}, 0); |
| 652 | i += 1; | 723 | envp_len += 1; |
| 653 | } | 724 | } |
| 654 | 725 | ||
| 655 | while (existing.block[existing_index]) |line| : (existing_index += 1) { | 726 | var existing_index: usize = 0; |
| 656 | if (mem.eql(u8, mem.sliceTo(line, '='), "ZIG_PROGRESS")) switch (zig_progress_action) { | 727 | while (existing.block.slice[existing_index]) |entry| : (existing_index += 1) { |
| 728 | if (mem.eql(u8, mem.sliceTo(entry, '='), "ZIG_PROGRESS")) switch (zig_progress_action) { | ||
| 657 | .add => unreachable, | 729 | .add => unreachable, |
| 658 | .delete => continue, | 730 | .delete => continue, |
| 659 | .edit => { | 731 | .edit => { |
| 660 | envp_buf[i] = try std.fmt.allocPrintSentinel(arena, "ZIG_PROGRESS={d}", .{options.zig_progress_fd.?}, 0); | 732 | envp[envp_len] = try std.fmt.allocPrintSentinel(gpa, "ZIG_PROGRESS={d}", .{options.zig_progress_fd.?}, 0); |
| 661 | i += 1; | 733 | envp_len += 1; |
| 662 | continue; | 734 | continue; |
| 663 | }, | 735 | }, |
| 664 | .nothing => {}, | 736 | .nothing => {}, |
| 665 | }; | 737 | }; |
| 666 | envp_buf[i] = try arena.dupeZ(u8, mem.span(line)); | 738 | envp[envp_len] = try gpa.dupeZ(u8, mem.span(entry)); |
| 667 | i += 1; | 739 | envp_len += 1; |
| 668 | } | 740 | } |
| 669 | 741 | ||
| 670 | assert(i == envp_count); | 742 | assert(envp_len == envp.len); |
| 671 | return envp_buf; | 743 | return .{ .slice = envp }; |
| 672 | } | 744 | } |
| 673 | 745 | ||
| 674 | test "Map.createBlock" { | 746 | pub const CreateWindowsBlockOptions = struct { |
| 675 | const allocator = testing.allocator; | 747 | /// `null` means to leave the `ZIG_PROGRESS` environment variable unmodified. |
| 676 | var envmap = Map.init(allocator); | 748 | /// If non-null, `std.os.windows.INVALID_HANDLE_VALUE` means to remove the |
| 749 | /// environment variable, otherwise provide it with the given handle as an integer. | ||
| 750 | zig_progress_handle: ?std.os.windows.HANDLE = null, | ||
| 751 | }; | ||
| 752 | |||
| 753 | /// Creates a null-delimited environment variable block in the format expected | ||
| 754 | /// by POSIX, from a different one. | ||
| 755 | pub fn createWindowsBlock( | ||
| 756 | existing: Environ, | ||
| 757 | gpa: Allocator, | ||
| 758 | options: CreateWindowsBlockOptions, | ||
| 759 | ) Allocator.Error!WindowsBlock { | ||
| 760 | if (!existing.block.use_global) return .{ | ||
| 761 | .slice = try gpa.dupeSentinel(u16, WindowsBlock.empty.slice, 0), | ||
| 762 | }; | ||
| 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] }; | ||
| 822 | } | ||
| 823 | |||
| 824 | test "Map.createPosixBlock" { | ||
| 825 | const gpa = testing.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/env_vars/main.zig-23| ... | @@ -12,14 +12,10 @@ pub fn main(init: std.process.Init) !void { | ... | @@ -12,14 +12,10 @@ pub fn main(init: std.process.Init) !void { |
| 12 | // containsUnempty | 12 | // containsUnempty |
| 13 | { | 13 | { |
| 14 | try std.testing.expect(try environ.containsUnempty(allocator, "FOO")); | 14 | try std.testing.expect(try environ.containsUnempty(allocator, "FOO")); |
| 15 | try std.testing.expect(!(try environ.containsUnempty(allocator, "FOO="))); | ||
| 16 | try std.testing.expect(!(try environ.containsUnempty(allocator, "FO"))); | ||
| 17 | try std.testing.expect(!(try environ.containsUnempty(allocator, "FOOO"))); | ||
| 18 | if (builtin.os.tag == .windows) { | 15 | if (builtin.os.tag == .windows) { |
| 19 | try std.testing.expect(try environ.containsUnempty(allocator, "foo")); | 16 | try std.testing.expect(try environ.containsUnempty(allocator, "foo")); |
| 20 | } | 17 | } |
| 21 | try std.testing.expect(try environ.containsUnempty(allocator, "EQUALS")); | 18 | try std.testing.expect(try environ.containsUnempty(allocator, "EQUALS")); |
| 22 | try std.testing.expect(!(try environ.containsUnempty(allocator, "EQUALS=ABC"))); | ||
| 23 | try std.testing.expect(try environ.containsUnempty(allocator, "КИРиллИЦА")); | 19 | try std.testing.expect(try environ.containsUnempty(allocator, "КИРиллИЦА")); |
| 24 | if (builtin.os.tag == .windows) { | 20 | if (builtin.os.tag == .windows) { |
| 25 | try std.testing.expect(try environ.containsUnempty(allocator, "кирИЛЛица")); | 21 | try std.testing.expect(try environ.containsUnempty(allocator, "кирИЛЛица")); |
| ... | @@ -35,14 +31,10 @@ pub fn main(init: std.process.Init) !void { | ... | @@ -35,14 +31,10 @@ pub fn main(init: std.process.Init) !void { |
| 35 | // containsUnemptyConstant | 31 | // containsUnemptyConstant |
| 36 | { | 32 | { |
| 37 | try std.testing.expect(environ.containsUnemptyConstant("FOO")); | 33 | try std.testing.expect(environ.containsUnemptyConstant("FOO")); |
| 38 | try std.testing.expect(!environ.containsUnemptyConstant("FOO=")); | ||
| 39 | try std.testing.expect(!environ.containsUnemptyConstant("FO")); | ||
| 40 | try std.testing.expect(!environ.containsUnemptyConstant("FOOO")); | ||
| 41 | if (builtin.os.tag == .windows) { | 34 | if (builtin.os.tag == .windows) { |
| 42 | try std.testing.expect(environ.containsUnemptyConstant("foo")); | 35 | try std.testing.expect(environ.containsUnemptyConstant("foo")); |
| 43 | } | 36 | } |
| 44 | try std.testing.expect(environ.containsUnemptyConstant("EQUALS")); | 37 | try std.testing.expect(environ.containsUnemptyConstant("EQUALS")); |
| 45 | try std.testing.expect(!environ.containsUnemptyConstant("EQUALS=ABC")); | ||
| 46 | try std.testing.expect(environ.containsUnemptyConstant("КИРиллИЦА")); | 38 | try std.testing.expect(environ.containsUnemptyConstant("КИРиллИЦА")); |
| 47 | if (builtin.os.tag == .windows) { | 39 | if (builtin.os.tag == .windows) { |
| 48 | try std.testing.expect(environ.containsUnemptyConstant("кирИЛЛица")); | 40 | try std.testing.expect(environ.containsUnemptyConstant("кирИЛЛица")); |
| ... | @@ -58,14 +50,10 @@ pub fn main(init: std.process.Init) !void { | ... | @@ -58,14 +50,10 @@ pub fn main(init: std.process.Init) !void { |
| 58 | // contains | 50 | // contains |
| 59 | { | 51 | { |
| 60 | try std.testing.expect(try environ.contains(allocator, "FOO")); | 52 | try std.testing.expect(try environ.contains(allocator, "FOO")); |
| 61 | try std.testing.expect(!(try environ.contains(allocator, "FOO="))); | ||
| 62 | try std.testing.expect(!(try environ.contains(allocator, "FO"))); | ||
| 63 | try std.testing.expect(!(try environ.contains(allocator, "FOOO"))); | ||
| 64 | if (builtin.os.tag == .windows) { | 53 | if (builtin.os.tag == .windows) { |
| 65 | try std.testing.expect(try environ.contains(allocator, "foo")); | 54 | try std.testing.expect(try environ.contains(allocator, "foo")); |
| 66 | } | 55 | } |
| 67 | try std.testing.expect(try environ.contains(allocator, "EQUALS")); | 56 | try std.testing.expect(try environ.contains(allocator, "EQUALS")); |
| 68 | try std.testing.expect(!(try environ.contains(allocator, "EQUALS=ABC"))); | ||
| 69 | try std.testing.expect(try environ.contains(allocator, "КИРиллИЦА")); | 57 | try std.testing.expect(try environ.contains(allocator, "КИРиллИЦА")); |
| 70 | if (builtin.os.tag == .windows) { | 58 | if (builtin.os.tag == .windows) { |
| 71 | try std.testing.expect(try environ.contains(allocator, "кирИЛЛица")); | 59 | try std.testing.expect(try environ.contains(allocator, "кирИЛЛица")); |
| ... | @@ -81,14 +69,10 @@ pub fn main(init: std.process.Init) !void { | ... | @@ -81,14 +69,10 @@ pub fn main(init: std.process.Init) !void { |
| 81 | // containsConstant | 69 | // containsConstant |
| 82 | { | 70 | { |
| 83 | try std.testing.expect(environ.containsConstant("FOO")); | 71 | try std.testing.expect(environ.containsConstant("FOO")); |
| 84 | try std.testing.expect(!environ.containsConstant("FOO=")); | ||
| 85 | try std.testing.expect(!environ.containsConstant("FO")); | ||
| 86 | try std.testing.expect(!environ.containsConstant("FOOO")); | ||
| 87 | if (builtin.os.tag == .windows) { | 72 | if (builtin.os.tag == .windows) { |
| 88 | try std.testing.expect(environ.containsConstant("foo")); | 73 | try std.testing.expect(environ.containsConstant("foo")); |
| 89 | } | 74 | } |
| 90 | try std.testing.expect(environ.containsConstant("EQUALS")); | 75 | try std.testing.expect(environ.containsConstant("EQUALS")); |
| 91 | try std.testing.expect(!environ.containsConstant("EQUALS=ABC")); | ||
| 92 | try std.testing.expect(environ.containsConstant("КИРиллИЦА")); | 76 | try std.testing.expect(environ.containsConstant("КИРиллИЦА")); |
| 93 | if (builtin.os.tag == .windows) { | 77 | if (builtin.os.tag == .windows) { |
| 94 | try std.testing.expect(environ.containsConstant("кирИЛЛица")); | 78 | try std.testing.expect(environ.containsConstant("кирИЛЛица")); |
| ... | @@ -104,14 +88,10 @@ pub fn main(init: std.process.Init) !void { | ... | @@ -104,14 +88,10 @@ pub fn main(init: std.process.Init) !void { |
| 104 | // getAlloc | 88 | // getAlloc |
| 105 | { | 89 | { |
| 106 | try std.testing.expectEqualSlices(u8, "123", try environ.getAlloc(arena, "FOO")); | 90 | try std.testing.expectEqualSlices(u8, "123", try environ.getAlloc(arena, "FOO")); |
| 107 | try std.testing.expectError(error.EnvironmentVariableMissing, environ.getAlloc(arena, "FOO=")); | ||
| 108 | try std.testing.expectError(error.EnvironmentVariableMissing, environ.getAlloc(arena, "FO")); | ||
| 109 | try std.testing.expectError(error.EnvironmentVariableMissing, environ.getAlloc(arena, "FOOO")); | ||
| 110 | if (builtin.os.tag == .windows) { | 91 | if (builtin.os.tag == .windows) { |
| 111 | try std.testing.expectEqualSlices(u8, "123", try environ.getAlloc(arena, "foo")); | 92 | try std.testing.expectEqualSlices(u8, "123", try environ.getAlloc(arena, "foo")); |
| 112 | } | 93 | } |
| 113 | try std.testing.expectEqualSlices(u8, "ABC=123", try environ.getAlloc(arena, "EQUALS")); | 94 | try std.testing.expectEqualSlices(u8, "ABC=123", try environ.getAlloc(arena, "EQUALS")); |
| 114 | try std.testing.expectError(error.EnvironmentVariableMissing, environ.getAlloc(arena, "EQUALS=ABC")); | ||
| 115 | try std.testing.expectEqualSlices(u8, "non-ascii አማርኛ \u{10FFFF}", try environ.getAlloc(arena, "КИРиллИЦА")); | 95 | try std.testing.expectEqualSlices(u8, "non-ascii አማርኛ \u{10FFFF}", try environ.getAlloc(arena, "КИРиллИЦА")); |
| 116 | if (builtin.os.tag == .windows) { | 96 | if (builtin.os.tag == .windows) { |
| 117 | try std.testing.expectEqualSlices(u8, "non-ascii አማርኛ \u{10FFFF}", try environ.getAlloc(arena, "кирИЛЛица")); | 97 | try std.testing.expectEqualSlices(u8, "non-ascii አማርኛ \u{10FFFF}", try environ.getAlloc(arena, "кирИЛЛица")); |
| ... | @@ -130,13 +110,10 @@ pub fn main(init: std.process.Init) !void { | ... | @@ -130,13 +110,10 @@ pub fn main(init: std.process.Init) !void { |
| 130 | defer environ_map.deinit(); | 110 | defer environ_map.deinit(); |
| 131 | 111 | ||
| 132 | try std.testing.expectEqualSlices(u8, "123", environ_map.get("FOO").?); | 112 | try std.testing.expectEqualSlices(u8, "123", environ_map.get("FOO").?); |
| 133 | try std.testing.expectEqual(null, environ_map.get("FO")); | ||
| 134 | try std.testing.expectEqual(null, environ_map.get("FOOO")); | ||
| 135 | if (builtin.os.tag == .windows) { | 113 | if (builtin.os.tag == .windows) { |
| 136 | try std.testing.expectEqualSlices(u8, "123", environ_map.get("foo").?); | 114 | try std.testing.expectEqualSlices(u8, "123", environ_map.get("foo").?); |
| 137 | } | 115 | } |
| 138 | try std.testing.expectEqualSlices(u8, "ABC=123", environ_map.get("EQUALS").?); | 116 | try std.testing.expectEqualSlices(u8, "ABC=123", environ_map.get("EQUALS").?); |
| 139 | try std.testing.expectEqual(null, environ_map.get("EQUALS=ABC")); | ||
| 140 | try std.testing.expectEqualSlices(u8, "non-ascii አማርኛ \u{10FFFF}", environ_map.get("КИРиллИЦА").?); | 117 | try std.testing.expectEqualSlices(u8, "non-ascii አማርኛ \u{10FFFF}", environ_map.get("КИРиллИЦА").?); |
| 141 | if (builtin.os.tag == .windows) { | 118 | if (builtin.os.tag == .windows) { |
| 142 | try std.testing.expectEqualSlices(u8, "non-ascii አማርኛ \u{10FFFF}", environ_map.get("кирИЛЛица").?); | 119 | try std.testing.expectEqualSlices(u8, "non-ascii አማርኛ \u{10FFFF}", environ_map.get("кирИЛЛица").?); |
test/standalone/windows_argv/fuzz.zig+7-2| ... | @@ -125,7 +125,7 @@ fn spawnVerify(verify_path: [:0]const u16, cmd_line: [:0]const u16) !windows.DWO | ... | @@ -125,7 +125,7 @@ fn spawnVerify(verify_path: [:0]const u16, cmd_line: [:0]const u16) !windows.DWO |
| 125 | .lpReserved2 = null, | 125 | .lpReserved2 = null, |
| 126 | .hStdInput = null, | 126 | .hStdInput = null, |
| 127 | .hStdOutput = null, | 127 | .hStdOutput = null, |
| 128 | .hStdError = windows.GetStdHandle(windows.STD_ERROR_HANDLE) catch null, | 128 | .hStdError = windows.peb().ProcessParameters.hStdError, |
| 129 | }; | 129 | }; |
| 130 | var proc_info: windows.PROCESS_INFORMATION = undefined; | 130 | var proc_info: windows.PROCESS_INFORMATION = undefined; |
| 131 | 131 | ||
| ... | @@ -149,7 +149,12 @@ fn spawnVerify(verify_path: [:0]const u16, cmd_line: [:0]const u16) !windows.DWO | ... | @@ -149,7 +149,12 @@ 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 | try windows.WaitForSingleObjectEx(child_proc, windows.INFINITE, false); | 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 | windows.NTSTATUS.WAIT_0 => {}, | ||
| 155 | .TIMEOUT => return error.WaitTimeOut, | ||
| 156 | else => |status| return windows.unexpectedStatus(status), | ||
| 157 | } | ||
| 153 | 158 | ||
| 154 | var exit_code: windows.DWORD = undefined; | 159 | var exit_code: windows.DWORD = undefined; |
| 155 | if (windows.kernel32.GetExitCodeProcess(child_proc, &exit_code) == 0) { | 160 | if (windows.kernel32.GetExitCodeProcess(child_proc, &exit_code) == 0) { |
test/standalone/windows_spawn/main.zig+4-3| ... | @@ -233,12 +233,13 @@ fn testExecWithCwdInner(gpa: Allocator, io: Io, command: []const u8, cwd: std.pr | ... | @@ -233,12 +233,13 @@ fn testExecWithCwdInner(gpa: Allocator, io: Io, command: []const u8, cwd: std.pr |
| 233 | } | 233 | } |
| 234 | 234 | ||
| 235 | fn renameExe(dir: Io.Dir, io: Io, old_sub_path: []const u8, new_sub_path: []const u8) !void { | 235 | fn renameExe(dir: Io.Dir, io: Io, old_sub_path: []const u8, new_sub_path: []const u8) !void { |
| 236 | var attempt: u5 = 0; | 236 | var attempt: u5 = 10; |
| 237 | while (true) break dir.rename(old_sub_path, dir, new_sub_path, io) catch |err| switch (err) { | 237 | while (true) break dir.rename(old_sub_path, dir, new_sub_path, io) catch |err| switch (err) { |
| 238 | error.AccessDenied => { | 238 | error.AccessDenied => { |
| 239 | if (attempt == 13) return error.AccessDenied; | 239 | if (attempt == 26) return error.AccessDenied; |
| 240 | // give the kernel a chance to finish closing the executable handle | 240 | // give the kernel a chance to finish closing the executable handle |
| 241 | _ = std.os.windows.kernel32.SleepEx(@as(u32, 1) << attempt >> 1, std.os.windows.FALSE); | 241 | const interval = @as(std.os.windows.LARGE_INTEGER, -1) << attempt; |
| 242 | _ = std.os.windows.ntdll.NtDelayExecution(std.os.windows.FALSE, &interval); | ||
| 242 | attempt += 1; | 243 | attempt += 1; |
| 243 | continue; | 244 | continue; |
| 244 | }, | 245 | }, |