| author | |
| committer | |
| log | 8d3eaab8717381023ba8b6837fc5840ad8be1004 |
| tree | a53bba1b992fe8c84abd2f108c56cf87dd852c4b |
| parent | 7f9dc4ebc10eb13e73466224f28ba62747693df9 |
8 files changed, 528 insertions(+), 64 deletions(-)
README.md+2-5| ... | ... | @@ -18,7 +18,7 @@ clarity. |
| 18 | 18 | writing buggy code. |
| 19 | 19 | * Debug mode optimizes for fast compilation time and crashing with a stack trace |
| 20 | 20 | when undefined behavior *would* happen. |
| 21 | * Release mode produces heavily optimized code. What other projects call | |
| 21 | * ReleaseFast mode produces heavily optimized code. What other projects call | |
| 22 | 22 | "Link Time Optimization" Zig does automatically. |
| 23 | 23 | * Compatible with C libraries with no wrapper necessary. Directly include |
| 24 | 24 | C .h files and get access to the functions and symbols therein. |
| ... | ... | @@ -36,16 +36,13 @@ clarity. |
| 36 | 36 | a preprocessor or macros. |
| 37 | 37 | * The binaries produced by Zig have complete debugging information so you can, |
| 38 | 38 | for example, use GDB to debug your software. |
| 39 | * Mark functions as tests and automatically run them with `zig test`. | |
| 39 | * Built-in unit tests with `zig test`. | |
| 40 | 40 | * Friendly toward package maintainers. Reproducible build, bootstrapping |
| 41 | 41 | process carefully documented. Issues filed by package maintainers are |
| 42 | 42 | considered especially important. |
| 43 | 43 | * Cross-compiling is a primary use case. |
| 44 | 44 | * In addition to creating executables, creating a C library is a primary use |
| 45 | 45 | case. You can export an auto-generated .h file. |
| 46 | * For OS development, Zig supports all architectures that LLVM does. All the | |
| 47 | standard library that does not depend on an OS is available to you in | |
| 48 | freestanding mode. | |
| 49 | 46 | |
| 50 | 47 | ### Support Table |
| 51 | 48 |
std/buf_set.zig+6-2| ... | ... | @@ -7,9 +7,9 @@ pub const BufSet = struct { |
| 7 | 7 | |
| 8 | 8 | const BufSetHashMap = HashMap([]const u8, void, mem.hash_slice_u8, mem.eql_slice_u8); |
| 9 | 9 | |
| 10 | pub fn init(allocator: &Allocator) -> BufSet { | |
| 10 | pub fn init(a: &Allocator) -> BufSet { | |
| 11 | 11 | var self = BufSet { |
| 12 | .hash_map = BufSetHashMap.init(allocator), | |
| 12 | .hash_map = BufSetHashMap.init(a), | |
| 13 | 13 | }; |
| 14 | 14 | return self; |
| 15 | 15 | } |
| ... | ... | @@ -45,6 +45,10 @@ pub const BufSet = struct { |
| 45 | 45 | return self.hash_map.iterator(); |
| 46 | 46 | } |
| 47 | 47 | |
| 48 | pub fn allocator(self: &const BufSet) -> &Allocator { | |
| 49 | return self.hash_map.allocator; | |
| 50 | } | |
| 51 | ||
| 48 | 52 | fn free(self: &BufSet, value: []const u8) { |
| 49 | 53 | // remove the const |
| 50 | 54 | const mut_value = @ptrCast(&u8, value.ptr)[0..value.len]; |
std/buffer.zig+11-2| ... | ... | @@ -91,8 +91,17 @@ pub const Buffer = struct { |
| 91 | 91 | } |
| 92 | 92 | |
| 93 | 93 | pub fn appendByte(self: &Buffer, byte: u8) -> %void { |
| 94 | %return self.resize(self.len() + 1); | |
| 95 | self.list.items[self.len() - 1] = byte; | |
| 94 | return self.appendByteNTimes(byte, 1); | |
| 95 | } | |
| 96 | ||
| 97 | pub fn appendByteNTimes(self: &Buffer, byte: u8, count: usize) -> %void { | |
| 98 | var prev_size: usize = self.len(); | |
| 99 | %return self.resize(prev_size + count); | |
| 100 | ||
| 101 | var i: usize = 0; | |
| 102 | while (i < count) : (i += 1) { | |
| 103 | self.list.items[prev_size + i] = byte; | |
| 104 | } | |
| 96 | 105 | } |
| 97 | 106 | |
| 98 | 107 | pub fn eql(self: &const Buffer, m: []const u8) -> bool { |
std/cstr.zig+11| ... | ... | @@ -1,4 +1,5 @@ |
| 1 | 1 | const debug = @import("debug.zig"); |
| 2 | const mem = @import("mem.zig"); | |
| 2 | 3 | const assert = debug.assert; |
| 3 | 4 | |
| 4 | 5 | pub fn len(ptr: &const u8) -> usize { |
| ... | ... | @@ -36,3 +37,13 @@ fn testCStrFnsImpl() { |
| 36 | 37 | assert(cmp(c"aoeu", c"aoez") == -1); |
| 37 | 38 | assert(len(c"123456789") == 9); |
| 38 | 39 | } |
| 40 | ||
| 41 | /// Returns a mutable slice with exactly the same size which is guaranteed to | |
| 42 | /// have a null byte after it. | |
| 43 | /// Caller owns the returned memory. | |
| 44 | pub fn addNullByte(allocator: &mem.Allocator, slice: []const u8) -> %[]u8 { | |
| 45 | const result = %return allocator.alloc(u8, slice.len + 1); | |
| 46 | mem.copy(u8, result, slice); | |
| 47 | result[slice.len] = 0; | |
| 48 | return result[0..slice.len]; | |
| 49 | } |
std/mem.zig+15| ... | ... | @@ -254,6 +254,21 @@ pub fn indexOfScalarPos(comptime T: type, slice: []const T, start_index: usize, |
| 254 | 254 | return null; |
| 255 | 255 | } |
| 256 | 256 | |
| 257 | pub fn indexOfAny(comptime T: type, slice: []const T, values: []const T) -> ?usize { | |
| 258 | return indexOfAnyPos(T, slice, 0, values); | |
| 259 | } | |
| 260 | ||
| 261 | pub fn indexOfAnyPos(comptime T: type, slice: []const T, start_index: usize, values: []const T) -> ?usize { | |
| 262 | var i: usize = start_index; | |
| 263 | while (i < slice.len) : (i += 1) { | |
| 264 | for (values) |value| { | |
| 265 | if (slice[i] == value) | |
| 266 | return i; | |
| 267 | } | |
| 268 | } | |
| 269 | return null; | |
| 270 | } | |
| 271 | ||
| 257 | 272 | pub fn indexOf(comptime T: type, haystack: []const T, needle: []const T) -> ?usize { |
| 258 | 273 | return indexOfPos(T, haystack, 0, needle); |
| 259 | 274 | } |
std/os/child_process.zig+357-16| ... | ... | @@ -1,22 +1,30 @@ |
| 1 | const io = @import("../io.zig"); | |
| 2 | const os = @import("index.zig"); | |
| 1 | const std = @import("../index.zig"); | |
| 2 | const cstr = std.cstr; | |
| 3 | const io = std.io; | |
| 4 | const os = std.os; | |
| 3 | 5 | const posix = os.posix; |
| 4 | const mem = @import("../mem.zig"); | |
| 6 | const windows = os.windows; | |
| 7 | const mem = std.mem; | |
| 5 | 8 | const Allocator = mem.Allocator; |
| 6 | const debug = @import("../debug.zig"); | |
| 9 | const debug = std.debug; | |
| 7 | 10 | const assert = debug.assert; |
| 8 | const BufMap = @import("../buf_map.zig").BufMap; | |
| 11 | const BufMap = std.BufMap; | |
| 12 | const Buffer = std.Buffer; | |
| 9 | 13 | const builtin = @import("builtin"); |
| 10 | 14 | const Os = builtin.Os; |
| 11 | const LinkedList = @import("../linked_list.zig").LinkedList; | |
| 15 | const LinkedList = std.LinkedList; | |
| 12 | 16 | |
| 13 | 17 | error PermissionDenied; |
| 14 | 18 | error ProcessNotFound; |
| 15 | 19 | |
| 16 | 20 | var children_nodes = LinkedList(&ChildProcess).init(); |
| 17 | 21 | |
| 22 | const is_windows = builtin.os == Os.windows; | |
| 23 | ||
| 18 | 24 | pub const ChildProcess = struct { |
| 19 | pub pid: i32, | |
| 25 | pub pid: if (is_windows) void else i32, | |
| 26 | pub handle: if (is_windows) windows.HANDLE else void, | |
| 27 | ||
| 20 | 28 | pub allocator: &mem.Allocator, |
| 21 | 29 | |
| 22 | 30 | pub stdin: ?&io.OutStream, |
| ... | ... | @@ -38,16 +46,16 @@ pub const ChildProcess = struct { |
| 38 | 46 | pub stderr_behavior: StdIo, |
| 39 | 47 | |
| 40 | 48 | /// Set to change the user id when spawning the child process. |
| 41 | pub uid: ?u32, | |
| 49 | pub uid: if (is_windows) void else ?u32, | |
| 42 | 50 | |
| 43 | 51 | /// Set to change the group id when spawning the child process. |
| 44 | pub gid: ?u32, | |
| 52 | pub gid: if (is_windows) void else ?u32, | |
| 45 | 53 | |
| 46 | 54 | /// Set to change the current working directory when spawning the child process. |
| 47 | 55 | pub cwd: ?[]const u8, |
| 48 | 56 | |
| 49 | err_pipe: [2]i32, | |
| 50 | llnode: LinkedList(&ChildProcess).Node, | |
| 57 | err_pipe: if (is_windows) void else [2]i32, | |
| 58 | llnode: if (is_windows) void else LinkedList(&ChildProcess).Node, | |
| 51 | 59 | |
| 52 | 60 | pub const Term = enum { |
| 53 | 61 | Exited: i32, |
| ... | ... | @@ -73,14 +81,15 @@ pub const ChildProcess = struct { |
| 73 | 81 | .allocator = allocator, |
| 74 | 82 | .argv = argv, |
| 75 | 83 | .pid = undefined, |
| 84 | .handle = undefined, | |
| 76 | 85 | .err_pipe = undefined, |
| 77 | 86 | .llnode = undefined, |
| 78 | 87 | .term = null, |
| 79 | 88 | .onTerm = null, |
| 80 | 89 | .env_map = null, |
| 81 | 90 | .cwd = null, |
| 82 | .uid = null, | |
| 83 | .gid = null, | |
| 91 | .uid = if (is_windows) {} else null, | |
| 92 | .gid = if (is_windows) {} else null, | |
| 84 | 93 | .stdin = null, |
| 85 | 94 | .stdout = null, |
| 86 | 95 | .stderr = null, |
| ... | ... | @@ -101,9 +110,10 @@ pub const ChildProcess = struct { |
| 101 | 110 | /// onTerm can be called before `spawn` returns. |
| 102 | 111 | /// On success must call `kill` or `wait`. |
| 103 | 112 | pub fn spawn(self: &ChildProcess) -> %void { |
| 104 | return switch (builtin.os) { | |
| 105 | Os.linux, Os.macosx, Os.ios, Os.darwin => self.spawnPosix(), | |
| 106 | else => @compileError("Unsupported OS"), | |
| 113 | if (is_windows) { | |
| 114 | return self.spawnWindows(); | |
| 115 | } else { | |
| 116 | return self.spawnPosix(); | |
| 107 | 117 | }; |
| 108 | 118 | } |
| 109 | 119 | |
| ... | ... | @@ -114,6 +124,30 @@ pub const ChildProcess = struct { |
| 114 | 124 | |
| 115 | 125 | /// Forcibly terminates child process and then cleans up all resources. |
| 116 | 126 | pub fn kill(self: &ChildProcess) -> %Term { |
| 127 | if (is_windows) { | |
| 128 | return self.killWindows(1); | |
| 129 | } else { | |
| 130 | return self.killPosix(); | |
| 131 | } | |
| 132 | } | |
| 133 | ||
| 134 | pub fn killWindows(self: &ChildProcess, exit_code: windows.UINT) -> %Term { | |
| 135 | if (self.term) |term| { | |
| 136 | self.cleanupStreams(); | |
| 137 | return term; | |
| 138 | } | |
| 139 | ||
| 140 | if (!windows.TerminateProcess(self.handle, exit_code)) { | |
| 141 | const err = windows.GetLastError(); | |
| 142 | return switch (err) { | |
| 143 | else => error.Unexpected, | |
| 144 | }; | |
| 145 | } | |
| 146 | self.waitUnwrappedWindows(); | |
| 147 | return ??self.term; | |
| 148 | } | |
| 149 | ||
| 150 | pub fn killPosix(self: &ChildProcess) -> %Term { | |
| 117 | 151 | block_SIGCHLD(); |
| 118 | 152 | defer restore_SIGCHLD(); |
| 119 | 153 | |
| ... | ... | @@ -137,6 +171,24 @@ pub const ChildProcess = struct { |
| 137 | 171 | |
| 138 | 172 | /// Blocks until child process terminates and then cleans up all resources. |
| 139 | 173 | pub fn wait(self: &ChildProcess) -> %Term { |
| 174 | if (is_windows) { | |
| 175 | return self.waitWindows(); | |
| 176 | } else { | |
| 177 | return self.waitPosix(); | |
| 178 | } | |
| 179 | } | |
| 180 | ||
| 181 | fn waitWindows(self: &ChildProcess) -> %Term { | |
| 182 | if (self.term) |term| { | |
| 183 | self.cleanupStreams(); | |
| 184 | return term; | |
| 185 | } | |
| 186 | ||
| 187 | %return self.waitUnwrappedWindows(); | |
| 188 | return ??self.term; | |
| 189 | } | |
| 190 | ||
| 191 | fn waitPosix(self: &ChildProcess) -> %Term { | |
| 140 | 192 | block_SIGCHLD(); |
| 141 | 193 | defer restore_SIGCHLD(); |
| 142 | 194 | |
| ... | ... | @@ -153,6 +205,23 @@ pub const ChildProcess = struct { |
| 153 | 205 | self.allocator.destroy(self); |
| 154 | 206 | } |
| 155 | 207 | |
| 208 | fn waitUnwrappedWindows(self: &ChildProcess) -> %void { | |
| 209 | const result = os.windowsWaitSingle(self.handle, windows.INFINITE); | |
| 210 | ||
| 211 | self.term = (%Term)({ | |
| 212 | var exit_code: windows.DWORD = undefined; | |
| 213 | if (!windows.GetExitCodeProcess(self.handle, &exit_code)) { | |
| 214 | Term.Unknown{0} | |
| 215 | } else { | |
| 216 | Term.Exited {@bitCast(i32, exit_code)} | |
| 217 | } | |
| 218 | }); | |
| 219 | ||
| 220 | os.windowsClose(self.handle); | |
| 221 | self.cleanupStreams(); | |
| 222 | return result; | |
| 223 | } | |
| 224 | ||
| 156 | 225 | fn waitUnwrapped(self: &ChildProcess) { |
| 157 | 226 | var status: i32 = undefined; |
| 158 | 227 | while (true) { |
| ... | ... | @@ -262,16 +331,21 @@ pub const ChildProcess = struct { |
| 262 | 331 | } else { |
| 263 | 332 | null |
| 264 | 333 | }; |
| 334 | %defer if (stdin_ptr) |ptr| self.allocator.destroy(ptr); | |
| 335 | ||
| 265 | 336 | const stdout_ptr = if (self.stdout_behavior == StdIo.Pipe) { |
| 266 | 337 | %return self.allocator.create(io.InStream) |
| 267 | 338 | } else { |
| 268 | 339 | null |
| 269 | 340 | }; |
| 341 | %defer if (stdout_ptr) |ptr| self.allocator.destroy(ptr); | |
| 342 | ||
| 270 | 343 | const stderr_ptr = if (self.stderr_behavior == StdIo.Pipe) { |
| 271 | 344 | %return self.allocator.create(io.InStream) |
| 272 | 345 | } else { |
| 273 | 346 | null |
| 274 | 347 | }; |
| 348 | %defer if (stderr_ptr) |ptr| self.allocator.destroy(ptr); | |
| 275 | 349 | |
| 276 | 350 | block_SIGCHLD(); |
| 277 | 351 | const pid_result = posix.fork(); |
| ... | ... | @@ -355,6 +429,195 @@ pub const ChildProcess = struct { |
| 355 | 429 | if (self.stderr_behavior == StdIo.Pipe) { os.posixClose(stderr_pipe[1]); } |
| 356 | 430 | } |
| 357 | 431 | |
| 432 | fn spawnWindows(self: &ChildProcess) -> %void { | |
| 433 | var saAttr: windows.SECURITY_ATTRIBUTES = undefined; | |
| 434 | saAttr.nLength = @sizeOf(windows.SECURITY_ATTRIBUTES); | |
| 435 | saAttr.bInheritHandle = true; | |
| 436 | saAttr.lpSecurityDescriptor = null; | |
| 437 | ||
| 438 | const any_ignore = (self.stdin_behavior == StdIo.Ignore or | |
| 439 | self.stdout_behavior == StdIo.Ignore or | |
| 440 | self.stderr_behavior == StdIo.Ignore); | |
| 441 | ||
| 442 | const nul_handle = if (any_ignore) { | |
| 443 | %return os.windowsOpen("NUL", windows.GENERIC_READ, windows.FILE_SHARE_READ, | |
| 444 | windows.OPEN_EXISTING, windows.FILE_ATTRIBUTE_NORMAL, null) | |
| 445 | } else { | |
| 446 | undefined | |
| 447 | }; | |
| 448 | defer { if (any_ignore) os.windowsClose(nul_handle); }; | |
| 449 | if (any_ignore) { | |
| 450 | %return windowsSetHandleInfo(nul_handle, windows.HANDLE_FLAG_INHERIT, 0); | |
| 451 | } | |
| 452 | ||
| 453 | ||
| 454 | var g_hChildStd_IN_Rd: ?windows.HANDLE = null; | |
| 455 | var g_hChildStd_IN_Wr: ?windows.HANDLE = null; | |
| 456 | switch (self.stdin_behavior) { | |
| 457 | StdIo.Pipe => { | |
| 458 | %return windowsMakePipeIn(&g_hChildStd_IN_Rd, &g_hChildStd_IN_Wr, &saAttr); | |
| 459 | }, | |
| 460 | StdIo.Ignore => { | |
| 461 | g_hChildStd_IN_Rd = nul_handle; | |
| 462 | }, | |
| 463 | StdIo.Inherit => { | |
| 464 | g_hChildStd_IN_Rd = windows.GetStdHandle(windows.STD_INPUT_HANDLE); | |
| 465 | }, | |
| 466 | StdIo.Close => { | |
| 467 | g_hChildStd_IN_Rd = null; | |
| 468 | }, | |
| 469 | } | |
| 470 | %defer if (self.stdin_behavior == StdIo.Pipe) { windowsDestroyPipe(g_hChildStd_IN_Rd, g_hChildStd_IN_Wr); }; | |
| 471 | ||
| 472 | var g_hChildStd_OUT_Rd: ?windows.HANDLE = null; | |
| 473 | var g_hChildStd_OUT_Wr: ?windows.HANDLE = null; | |
| 474 | switch (self.stdout_behavior) { | |
| 475 | StdIo.Pipe => { | |
| 476 | %return windowsMakePipeOut(&g_hChildStd_OUT_Rd, &g_hChildStd_OUT_Wr, &saAttr); | |
| 477 | }, | |
| 478 | StdIo.Ignore => { | |
| 479 | g_hChildStd_OUT_Wr = nul_handle; | |
| 480 | }, | |
| 481 | StdIo.Inherit => { | |
| 482 | g_hChildStd_OUT_Wr = windows.GetStdHandle(windows.STD_OUTPUT_HANDLE); | |
| 483 | }, | |
| 484 | StdIo.Close => { | |
| 485 | g_hChildStd_OUT_Wr = null; | |
| 486 | }, | |
| 487 | } | |
| 488 | %defer if (self.stdin_behavior == StdIo.Pipe) { windowsDestroyPipe(g_hChildStd_OUT_Rd, g_hChildStd_OUT_Wr); }; | |
| 489 | ||
| 490 | var g_hChildStd_ERR_Rd: ?windows.HANDLE = null; | |
| 491 | var g_hChildStd_ERR_Wr: ?windows.HANDLE = null; | |
| 492 | switch (self.stderr_behavior) { | |
| 493 | StdIo.Pipe => { | |
| 494 | %return windowsMakePipeOut(&g_hChildStd_ERR_Rd, &g_hChildStd_ERR_Wr, &saAttr); | |
| 495 | }, | |
| 496 | StdIo.Ignore => { | |
| 497 | g_hChildStd_ERR_Wr = nul_handle; | |
| 498 | }, | |
| 499 | StdIo.Inherit => { | |
| 500 | g_hChildStd_ERR_Wr = windows.GetStdHandle(windows.STD_ERROR_HANDLE); | |
| 501 | }, | |
| 502 | StdIo.Close => { | |
| 503 | g_hChildStd_ERR_Wr = null; | |
| 504 | }, | |
| 505 | } | |
| 506 | %defer if (self.stdin_behavior == StdIo.Pipe) { windowsDestroyPipe(g_hChildStd_ERR_Rd, g_hChildStd_ERR_Wr); }; | |
| 507 | ||
| 508 | const stdin_ptr = if (self.stdin_behavior == StdIo.Pipe) { | |
| 509 | %return self.allocator.create(io.OutStream) | |
| 510 | } else { | |
| 511 | null | |
| 512 | }; | |
| 513 | %defer if (stdin_ptr) |ptr| self.allocator.destroy(ptr); | |
| 514 | ||
| 515 | const stdout_ptr = if (self.stdout_behavior == StdIo.Pipe) { | |
| 516 | %return self.allocator.create(io.InStream) | |
| 517 | } else { | |
| 518 | null | |
| 519 | }; | |
| 520 | %defer if (stdout_ptr) |ptr| self.allocator.destroy(ptr); | |
| 521 | ||
| 522 | const stderr_ptr = if (self.stderr_behavior == StdIo.Pipe) { | |
| 523 | %return self.allocator.create(io.InStream) | |
| 524 | } else { | |
| 525 | null | |
| 526 | }; | |
| 527 | %defer if (stderr_ptr) |ptr| self.allocator.destroy(ptr); | |
| 528 | ||
| 529 | const cmd_line = %return windowsCreateCommandLine(self.allocator, self.argv); | |
| 530 | defer self.allocator.free(cmd_line); | |
| 531 | ||
| 532 | var siStartInfo = windows.STARTUPINFOA { | |
| 533 | .cb = @sizeOf(windows.STARTUPINFOA), | |
| 534 | .hStdError = g_hChildStd_ERR_Wr, | |
| 535 | .hStdOutput = g_hChildStd_OUT_Wr, | |
| 536 | .hStdInput = g_hChildStd_IN_Rd, | |
| 537 | .dwFlags = windows.STARTF_USESTDHANDLES, | |
| 538 | ||
| 539 | .lpReserved = null, | |
| 540 | .lpDesktop = null, | |
| 541 | .lpTitle = null, | |
| 542 | .dwX = 0, | |
| 543 | .dwY = 0, | |
| 544 | .dwXSize = 0, | |
| 545 | .dwYSize = 0, | |
| 546 | .dwXCountChars = 0, | |
| 547 | .dwYCountChars = 0, | |
| 548 | .dwFillAttribute = 0, | |
| 549 | .wShowWindow = 0, | |
| 550 | .cbReserved2 = 0, | |
| 551 | .lpReserved2 = null, | |
| 552 | }; | |
| 553 | var piProcInfo: windows.PROCESS_INFORMATION = undefined; | |
| 554 | ||
| 555 | const app_name = %return cstr.addNullByte(self.allocator, self.argv[0]); | |
| 556 | defer self.allocator.free(app_name); | |
| 557 | ||
| 558 | const cwd_slice = if (self.cwd) |cwd| { | |
| 559 | %return cstr.addNullByte(self.allocator, cwd) | |
| 560 | } else { | |
| 561 | null | |
| 562 | }; | |
| 563 | defer if (cwd_slice) |cwd| self.allocator.free(cwd); | |
| 564 | const cwd_ptr = if (cwd_slice) |cwd| cwd.ptr else null; | |
| 565 | ||
| 566 | const maybe_envp_buf = if (self.env_map) |env_map| { | |
| 567 | %return os.createNullDelimitedEnvMap(self.allocator, env_map) | |
| 568 | } else { | |
| 569 | null | |
| 570 | }; | |
| 571 | defer if (maybe_envp_buf) |envp_buf| self.allocator.free(envp_buf); | |
| 572 | const envp_ptr = if (maybe_envp_buf) |envp_buf| envp_buf.ptr else null; | |
| 573 | ||
| 574 | if (!windows.CreateProcessA(app_name.ptr, cmd_line.ptr, null, null, true, 0, | |
| 575 | @ptrCast(?&c_void, envp_ptr), | |
| 576 | cwd_ptr, &siStartInfo, &piProcInfo)) | |
| 577 | { | |
| 578 | const err = windows.GetLastError(); | |
| 579 | return switch (err) { | |
| 580 | windows.ERROR.FILE_NOT_FOUND => error.FileNotFound, | |
| 581 | else => error.Unexpected, | |
| 582 | }; | |
| 583 | } | |
| 584 | os.windowsClose(piProcInfo.hThread); | |
| 585 | ||
| 586 | if (stdin_ptr) |outstream| { | |
| 587 | *outstream = io.OutStream { | |
| 588 | .fd = {}, | |
| 589 | .handle = g_hChildStd_IN_Wr, | |
| 590 | .handle_id = undefined, | |
| 591 | .buffer = undefined, | |
| 592 | .index = 0, | |
| 593 | }; | |
| 594 | } | |
| 595 | if (stdout_ptr) |instream| { | |
| 596 | *instream = io.InStream { | |
| 597 | .fd = {}, | |
| 598 | .handle = g_hChildStd_OUT_Rd, | |
| 599 | .handle_id = undefined, | |
| 600 | }; | |
| 601 | } | |
| 602 | if (stderr_ptr) |instream| { | |
| 603 | *instream = io.InStream { | |
| 604 | .fd = {}, | |
| 605 | .handle = g_hChildStd_ERR_Rd, | |
| 606 | .handle_id = undefined, | |
| 607 | }; | |
| 608 | } | |
| 609 | ||
| 610 | self.handle = piProcInfo.hProcess; | |
| 611 | self.term = null; | |
| 612 | self.stdin = stdin_ptr; | |
| 613 | self.stdout = stdout_ptr; | |
| 614 | self.stderr = stderr_ptr; | |
| 615 | ||
| 616 | if (self.stdin_behavior == StdIo.Pipe) { os.windowsClose(??g_hChildStd_IN_Rd); } | |
| 617 | if (self.stderr_behavior == StdIo.Pipe) { os.windowsClose(??g_hChildStd_ERR_Wr); } | |
| 618 | if (self.stdout_behavior == StdIo.Pipe) { os.windowsClose(??g_hChildStd_OUT_Wr); } | |
| 619 | } | |
| 620 | ||
| 358 | 621 | fn setUpChildIo(stdio: StdIo, pipe_fd: i32, std_fileno: i32, dev_null_fd: i32) -> %void { |
| 359 | 622 | switch (stdio) { |
| 360 | 623 | StdIo.Pipe => %return os.posixDup2(pipe_fd, std_fileno), |
| ... | ... | @@ -365,6 +628,84 @@ pub const ChildProcess = struct { |
| 365 | 628 | } |
| 366 | 629 | }; |
| 367 | 630 | |
| 631 | /// Caller must dealloc. | |
| 632 | /// Guarantees a null byte at result[result.len]. | |
| 633 | fn windowsCreateCommandLine(allocator: &Allocator, argv: []const []const u8) -> %[]u8 { | |
| 634 | var buf = %return Buffer.initSize(allocator, 0); | |
| 635 | defer buf.deinit(); | |
| 636 | ||
| 637 | for (argv) |arg, arg_i| { | |
| 638 | if (arg_i != 0) | |
| 639 | %return buf.appendByte(' '); | |
| 640 | if (mem.indexOfAny(u8, arg, " \t\n\"") == null) { | |
| 641 | %return buf.append(arg); | |
| 642 | continue; | |
| 643 | } | |
| 644 | %return buf.appendByte('"'); | |
| 645 | var backslash_count: usize = 0; | |
| 646 | for (arg) |byte| { | |
| 647 | switch (byte) { | |
| 648 | '\\' => backslash_count += 1, | |
| 649 | '"' => { | |
| 650 | %return buf.appendByteNTimes('\\', backslash_count * 2 + 1); | |
| 651 | %return buf.appendByte('"'); | |
| 652 | backslash_count = 0; | |
| 653 | }, | |
| 654 | else => { | |
| 655 | %return buf.appendByteNTimes('\\', backslash_count); | |
| 656 | %return buf.appendByte(byte); | |
| 657 | backslash_count = 0; | |
| 658 | }, | |
| 659 | } | |
| 660 | } | |
| 661 | %return buf.appendByteNTimes('\\', backslash_count * 2); | |
| 662 | %return buf.appendByte('"'); | |
| 663 | } | |
| 664 | ||
| 665 | return buf.toOwnedSlice(); | |
| 666 | } | |
| 667 | ||
| 668 | fn windowsDestroyPipe(rd: ?windows.HANDLE, wr: ?windows.HANDLE) { | |
| 669 | if (rd) |h| os.windowsClose(h); | |
| 670 | if (wr) |h| os.windowsClose(h); | |
| 671 | } | |
| 672 | ||
| 673 | fn windowsMakePipe(rd: &windows.HANDLE, wr: &windows.HANDLE, sattr: &windows.SECURITY_ATTRIBUTES) -> %void { | |
| 674 | if (!windows.CreatePipe(rd, wr, sattr, 0)) { | |
| 675 | const err = windows.GetLastError(); | |
| 676 | return switch (err) { | |
| 677 | else => error.Unexpected, | |
| 678 | }; | |
| 679 | } | |
| 680 | } | |
| 681 | ||
| 682 | fn windowsSetHandleInfo(h: windows.HANDLE, mask: windows.DWORD, flags: windows.DWORD) -> %void { | |
| 683 | if (!windows.SetHandleInformation(h, mask, flags)) { | |
| 684 | const err = windows.GetLastError(); | |
| 685 | return switch (err) { | |
| 686 | else => error.Unexpected, | |
| 687 | }; | |
| 688 | } | |
| 689 | } | |
| 690 | ||
| 691 | fn windowsMakePipeIn(rd: &?windows.HANDLE, wr: &?windows.HANDLE, sattr: &windows.SECURITY_ATTRIBUTES) -> %void { | |
| 692 | var rd_h: windows.HANDLE = undefined; | |
| 693 | var wr_h: windows.HANDLE = undefined; | |
| 694 | %return windowsMakePipe(&rd_h, &wr_h, sattr); | |
| 695 | %return windowsSetHandleInfo(wr_h, windows.HANDLE_FLAG_INHERIT, 0); | |
| 696 | *rd = rd_h; | |
| 697 | *wr = wr_h; | |
| 698 | } | |
| 699 | ||
| 700 | fn windowsMakePipeOut(rd: &?windows.HANDLE, wr: &?windows.HANDLE, sattr: &windows.SECURITY_ATTRIBUTES) -> %void { | |
| 701 | var rd_h: windows.HANDLE = undefined; | |
| 702 | var wr_h: windows.HANDLE = undefined; | |
| 703 | %return windowsMakePipe(&rd_h, &wr_h, sattr); | |
| 704 | %return windowsSetHandleInfo(rd_h, windows.HANDLE_FLAG_INHERIT, 0); | |
| 705 | *rd = rd_h; | |
| 706 | *wr = wr_h; | |
| 707 | } | |
| 708 | ||
| 368 | 709 | fn makePipe() -> %[2]i32 { |
| 369 | 710 | var fds: [2]i32 = undefined; |
| 370 | 711 | const err = posix.getErrno(posix.pipe(&fds)); |
std/os/index.zig+49-25| ... | ... | @@ -382,6 +382,37 @@ pub fn posixDup2(old_fd: i32, new_fd: i32) -> %void { |
| 382 | 382 | } |
| 383 | 383 | } |
| 384 | 384 | |
| 385 | pub fn createNullDelimitedEnvMap(allocator: &Allocator, env_map: &const BufMap) -> %[]?&u8 { | |
| 386 | const envp_count = env_map.count(); | |
| 387 | const envp_buf = %return allocator.alloc(?&u8, envp_count + 1); | |
| 388 | mem.set(?&u8, envp_buf, null); | |
| 389 | %defer freeNullDelimitedEnvMap(allocator, envp_buf); | |
| 390 | { | |
| 391 | var it = env_map.iterator(); | |
| 392 | var i: usize = 0; | |
| 393 | while (it.next()) |pair| : (i += 1) { | |
| 394 | const env_buf = %return allocator.alloc(u8, pair.key.len + pair.value.len + 2); | |
| 395 | @memcpy(&env_buf[0], pair.key.ptr, pair.key.len); | |
| 396 | env_buf[pair.key.len] = '='; | |
| 397 | @memcpy(&env_buf[pair.key.len + 1], pair.value.ptr, pair.value.len); | |
| 398 | env_buf[env_buf.len - 1] = 0; | |
| 399 | ||
| 400 | envp_buf[i] = env_buf.ptr; | |
| 401 | } | |
| 402 | assert(i == envp_count); | |
| 403 | } | |
| 404 | assert(envp_buf[envp_count] == null); | |
| 405 | return envp_buf; | |
| 406 | } | |
| 407 | ||
| 408 | pub fn freeNullDelimitedEnvMap(allocator: &Allocator, envp_buf: []?&u8) { | |
| 409 | for (envp_buf) |env| { | |
| 410 | const env_buf = if (env) |ptr| cstr.toSlice(ptr) else break; | |
| 411 | allocator.free(env_buf); | |
| 412 | } | |
| 413 | allocator.free(envp_buf); | |
| 414 | } | |
| 415 | ||
| 385 | 416 | /// This function must allocate memory to add a null terminating bytes on path and each arg. |
| 386 | 417 | /// It must also convert to KEY=VALUE\0 format for environment variables, and include null |
| 387 | 418 | /// pointers after the args and after the environment variables. |
| ... | ... | @@ -408,31 +439,8 @@ pub fn posixExecve(argv: []const []const u8, env_map: &const BufMap, |
| 408 | 439 | } |
| 409 | 440 | argv_buf[argv.len] = null; |
| 410 | 441 | |
| 411 | const envp_count = env_map.count(); | |
| 412 | const envp_buf = %return allocator.alloc(?&u8, envp_count + 1); | |
| 413 | mem.set(?&u8, envp_buf, null); | |
| 414 | defer { | |
| 415 | for (envp_buf) |env| { | |
| 416 | const env_buf = if (env) |ptr| cstr.toSlice(ptr) else break; | |
| 417 | allocator.free(env_buf); | |
| 418 | } | |
| 419 | allocator.free(envp_buf); | |
| 420 | } | |
| 421 | { | |
| 422 | var it = env_map.iterator(); | |
| 423 | var i: usize = 0; | |
| 424 | while (it.next()) |pair| : (i += 1) { | |
| 425 | const env_buf = %return allocator.alloc(u8, pair.key.len + pair.value.len + 2); | |
| 426 | @memcpy(&env_buf[0], pair.key.ptr, pair.key.len); | |
| 427 | env_buf[pair.key.len] = '='; | |
| 428 | @memcpy(&env_buf[pair.key.len + 1], pair.value.ptr, pair.value.len); | |
| 429 | env_buf[env_buf.len - 1] = 0; | |
| 430 | ||
| 431 | envp_buf[i] = env_buf.ptr; | |
| 432 | } | |
| 433 | assert(i == envp_count); | |
| 434 | } | |
| 435 | envp_buf[envp_count] = null; | |
| 442 | const envp_buf = %return createNullDelimitedEnvMap(allocator, env_map); | |
| 443 | defer freeNullDelimitedEnvMap(allocator, envp_buf); | |
| 436 | 444 | |
| 437 | 445 | const exe_path = argv[0]; |
| 438 | 446 | if (mem.indexOfScalar(u8, exe_path, '/') != null) { |
| ... | ... | @@ -1367,6 +1375,22 @@ fn testWindowsCmdLine(input_cmd_line: &const u8, expected_args: []const []const |
| 1367 | 1375 | assert(it.next(&debug.global_allocator) == null); |
| 1368 | 1376 | } |
| 1369 | 1377 | |
| 1378 | error WaitAbandoned; | |
| 1379 | error WaitTimeOut; | |
| 1380 | ||
| 1381 | pub fn windowsWaitSingle(handle: windows.HANDLE, milliseconds: windows.DWORD) -> %void { | |
| 1382 | const result = windows.WaitForSingleObject(handle, milliseconds); | |
| 1383 | return switch (result) { | |
| 1384 | windows.WAIT_ABANDONED => error.WaitAbandoned, | |
| 1385 | windows.WAIT_OBJECT_0 => {}, | |
| 1386 | windows.WAIT_TIMEOUT => error.WaitTimeOut, | |
| 1387 | windows.WAIT_FAILED => switch (windows.GetLastError()) { | |
| 1388 | else => error.Unexpected, | |
| 1389 | }, | |
| 1390 | else => error.Unexpected, | |
| 1391 | }; | |
| 1392 | } | |
| 1393 | ||
| 1370 | 1394 | test "std.os" { |
| 1371 | 1395 | _ = @import("child_process.zig"); |
| 1372 | 1396 | _ = @import("darwin_errno.zig"); |
std/os/windows/index.zig+77-14| ... | ... | @@ -14,6 +14,14 @@ pub extern "kernel32" stdcallcc fn CreateFileA(lpFileName: LPCSTR, dwDesiredAcce |
| 14 | 14 | dwShareMode: DWORD, lpSecurityAttributes: ?LPSECURITY_ATTRIBUTES, dwCreationDisposition: DWORD, |
| 15 | 15 | dwFlagsAndAttributes: DWORD, hTemplateFile: ?HANDLE) -> HANDLE; |
| 16 | 16 | |
| 17 | pub extern "kernel32" stdcallcc fn CreatePipe(hReadPipe: &HANDLE, hWritePipe: &HANDLE, | |
| 18 | lpPipeAttributes: &SECURITY_ATTRIBUTES, nSize: DWORD) -> BOOL; | |
| 19 | ||
| 20 | pub extern "kernel32" stdcallcc fn CreateProcessA(lpApplicationName: ?LPCSTR, lpCommandLine: LPSTR, | |
| 21 | lpProcessAttributes: ?&SECURITY_ATTRIBUTES, lpThreadAttributes: ?&SECURITY_ATTRIBUTES, bInheritHandles: BOOL, | |
| 22 | dwCreationFlags: DWORD, lpEnvironment: ?LPVOID, lpCurrentDirectory: ?LPCSTR, lpStartupInfo: &STARTUPINFOA, | |
| 23 | lpProcessInformation: &PROCESS_INFORMATION) -> BOOL; | |
| 24 | ||
| 17 | 25 | pub extern "kernel32" stdcallcc fn DeleteFileA(lpFileName: LPCSTR) -> bool; |
| 18 | 26 | |
| 19 | 27 | pub extern "kernel32" stdcallcc fn ExitProcess(exit_code: UINT) -> noreturn; |
| ... | ... | @@ -24,11 +32,10 @@ pub extern "kernel32" stdcallcc fn GetConsoleMode(in_hConsoleHandle: HANDLE, out |
| 24 | 32 | |
| 25 | 33 | pub extern "kernel32" stdcallcc fn GetCurrentDirectoryA(nBufferLength: WORD, lpBuffer: ?LPSTR) -> DWORD; |
| 26 | 34 | |
| 27 | /// Retrieves the calling thread's last-error code value. The last-error code is maintained on a per-thread basis. | |
| 28 | /// Multiple threads do not overwrite each other's last-error code. | |
| 35 | pub extern "kernel32" stdcallcc fn GetExitCodeProcess(hProcess: HANDLE, lpExitCode: &DWORD) -> BOOL; | |
| 36 | ||
| 29 | 37 | pub extern "kernel32" stdcallcc fn GetLastError() -> DWORD; |
| 30 | 38 | |
| 31 | /// Retrieves file information for the specified file. | |
| 32 | 39 | pub extern "kernel32" stdcallcc fn GetFileInformationByHandleEx(in_hFile: HANDLE, |
| 33 | 40 | in_FileInformationClass: FILE_INFO_BY_HANDLE_CLASS, out_lpFileInformation: &c_void, |
| 34 | 41 | in_dwBufferSize: DWORD) -> bool; |
| ... | ... | @@ -36,26 +43,29 @@ pub extern "kernel32" stdcallcc fn GetFileInformationByHandleEx(in_hFile: HANDLE |
| 36 | 43 | pub extern "kernel32" stdcallcc fn GetFinalPathNameByHandleA(hFile: HANDLE, lpszFilePath: LPSTR, |
| 37 | 44 | cchFilePath: DWORD, dwFlags: DWORD) -> DWORD; |
| 38 | 45 | |
| 39 | /// Retrieves a handle to the specified standard device (standard input, standard output, or standard error). | |
| 46 | pub extern "kernel32" stdcallcc fn GetProcessHeap() -> HANDLE; | |
| 47 | ||
| 40 | 48 | pub extern "kernel32" stdcallcc fn GetStdHandle(in_nStdHandle: DWORD) -> ?HANDLE; |
| 41 | 49 | |
| 50 | pub extern "kernel32" stdcallcc fn HeapAlloc(hHeap: HANDLE, dwFlags: DWORD, dwBytes: SIZE_T) -> LPVOID; | |
| 51 | ||
| 52 | pub extern "kernel32" stdcallcc fn HeapFree(hHeap: HANDLE, dwFlags: DWORD, lpMem: LPVOID) -> BOOL; | |
| 53 | ||
| 42 | 54 | pub extern "kernel32" stdcallcc fn ReadFile(in_hFile: HANDLE, out_lpBuffer: LPVOID, |
| 43 | 55 | in_nNumberOfBytesToRead: DWORD, out_lpNumberOfBytesRead: &DWORD, |
| 44 | 56 | in_out_lpOverlapped: ?&OVERLAPPED) -> BOOL; |
| 45 | 57 | |
| 46 | /// Writes data to the specified file or input/output (I/O) device. | |
| 47 | /// This function is designed for both synchronous and asynchronous operation. For a similar function designed solely for asynchronous operation, see WriteFileEx. | |
| 48 | pub extern "kernel32" stdcallcc fn WriteFile(in_hFile: HANDLE, in_lpBuffer: &const c_void, | |
| 49 | in_nNumberOfBytesToWrite: DWORD, out_lpNumberOfBytesWritten: ?&DWORD, | |
| 50 | in_out_lpOverlapped: ?&OVERLAPPED) -> BOOL; | |
| 58 | pub extern "kernel32" stdcallcc fn SetHandleInformation(hObject: HANDLE, dwMask: DWORD, dwFlags: DWORD) -> BOOL; | |
| 51 | 59 | |
| 52 | 60 | pub extern "kernel32" stdcallcc fn Sleep(dwMilliseconds: DWORD); |
| 53 | 61 | |
| 54 | pub extern "kernel32" stdcallcc fn HeapAlloc(hHeap: HANDLE, dwFlags: DWORD, dwBytes: SIZE_T) -> LPVOID; | |
| 62 | pub extern "kernel32" stdcallcc fn TerminateProcess(hProcess: HANDLE, uExitCode: UINT) -> BOOL; | |
| 55 | 63 | |
| 56 | pub extern "kernel32" stdcallcc fn HeapFree(hHeap: HANDLE, dwFlags: DWORD, lpMem: LPVOID) -> BOOL; | |
| 64 | pub extern "kernel32" stdcallcc fn WaitForSingleObject(hHandle: HANDLE, dwMilliseconds: DWORD) -> DWORD; | |
| 57 | 65 | |
| 58 | pub extern "kernel32" stdcallcc fn GetProcessHeap() -> HANDLE; | |
| 66 | pub extern "kernel32" stdcallcc fn WriteFile(in_hFile: HANDLE, in_lpBuffer: &const c_void, | |
| 67 | in_nNumberOfBytesToWrite: DWORD, out_lpNumberOfBytesWritten: ?&DWORD, | |
| 68 | in_out_lpOverlapped: ?&OVERLAPPED) -> BOOL; | |
| 59 | 69 | |
| 60 | 70 | pub extern "user32" stdcallcc fn MessageBoxA(hWnd: ?HANDLE, lpText: ?LPCTSTR, lpCaption: ?LPCTSTR, uType: UINT) -> c_int; |
| 61 | 71 | |
| ... | ... | @@ -88,7 +98,7 @@ pub const INT = c_int; |
| 88 | 98 | pub const ULONG_PTR = usize; |
| 89 | 99 | pub const WCHAR = u16; |
| 90 | 100 | pub const LPCVOID = &const c_void; |
| 91 | ||
| 101 | pub const LPBYTE = &BYTE; | |
| 92 | 102 | |
| 93 | 103 | /// The standard input device. Initially, this is the console input buffer, CONIN$. |
| 94 | 104 | pub const STD_INPUT_HANDLE = @maxValue(DWORD) - 10 + 1; |
| ... | ... | @@ -158,7 +168,7 @@ pub const VOLUME_NAME_NT = 0x2; |
| 158 | 168 | |
| 159 | 169 | pub const SECURITY_ATTRIBUTES = extern struct { |
| 160 | 170 | nLength: DWORD, |
| 161 | lpSecurityDescriptor: LPVOID, | |
| 171 | lpSecurityDescriptor: ?LPVOID, | |
| 162 | 172 | bInheritHandle: BOOL, |
| 163 | 173 | }; |
| 164 | 174 | pub const PSECURITY_ATTRIBUTES = &SECURITY_ATTRIBUTES; |
| ... | ... | @@ -189,3 +199,56 @@ pub const FILE_ATTRIBUTE_OFFLINE = 0x1000; |
| 189 | 199 | pub const FILE_ATTRIBUTE_READONLY = 0x1; |
| 190 | 200 | pub const FILE_ATTRIBUTE_SYSTEM = 0x4; |
| 191 | 201 | pub const FILE_ATTRIBUTE_TEMPORARY = 0x100; |
| 202 | ||
| 203 | pub const PROCESS_INFORMATION = extern struct { | |
| 204 | hProcess: HANDLE, | |
| 205 | hThread: HANDLE, | |
| 206 | dwProcessId: DWORD, | |
| 207 | dwThreadId: DWORD, | |
| 208 | }; | |
| 209 | ||
| 210 | pub const STARTUPINFOA = extern struct { | |
| 211 | cb: DWORD, | |
| 212 | lpReserved: ?LPSTR, | |
| 213 | lpDesktop: ?LPSTR, | |
| 214 | lpTitle: ?LPSTR, | |
| 215 | dwX: DWORD, | |
| 216 | dwY: DWORD, | |
| 217 | dwXSize: DWORD, | |
| 218 | dwYSize: DWORD, | |
| 219 | dwXCountChars: DWORD, | |
| 220 | dwYCountChars: DWORD, | |
| 221 | dwFillAttribute: DWORD, | |
| 222 | dwFlags: DWORD, | |
| 223 | wShowWindow: WORD, | |
| 224 | cbReserved2: WORD, | |
| 225 | lpReserved2: ?LPBYTE, | |
| 226 | hStdInput: ?HANDLE, | |
| 227 | hStdOutput: ?HANDLE, | |
| 228 | hStdError: ?HANDLE, | |
| 229 | }; | |
| 230 | ||
| 231 | pub const STARTF_FORCEONFEEDBACK = 0x00000040; | |
| 232 | pub const STARTF_FORCEOFFFEEDBACK = 0x00000080; | |
| 233 | pub const STARTF_PREVENTPINNING = 0x00002000; | |
| 234 | pub const STARTF_RUNFULLSCREEN = 0x00000020; | |
| 235 | pub const STARTF_TITLEISAPPID = 0x00001000; | |
| 236 | pub const STARTF_TITLEISLINKNAME = 0x00000800; | |
| 237 | pub const STARTF_UNTRUSTEDSOURCE = 0x00008000; | |
| 238 | pub const STARTF_USECOUNTCHARS = 0x00000008; | |
| 239 | pub const STARTF_USEFILLATTRIBUTE = 0x00000010; | |
| 240 | pub const STARTF_USEHOTKEY = 0x00000200; | |
| 241 | pub const STARTF_USEPOSITION = 0x00000004; | |
| 242 | pub const STARTF_USESHOWWINDOW = 0x00000001; | |
| 243 | pub const STARTF_USESIZE = 0x00000002; | |
| 244 | pub const STARTF_USESTDHANDLES = 0x00000100; | |
| 245 | ||
| 246 | pub const INFINITE = 4294967295; | |
| 247 | ||
| 248 | pub const WAIT_ABANDONED = 0x00000080; | |
| 249 | pub const WAIT_OBJECT_0 = 0x00000000; | |
| 250 | pub const WAIT_TIMEOUT = 0x00000102; | |
| 251 | pub const WAIT_FAILED = 0xFFFFFFFF; | |
| 252 | ||
| 253 | pub const HANDLE_FLAG_INHERIT = 0x00000001; | |
| 254 | pub const HANDLE_FLAG_PROTECT_FROM_CLOSE = 0x00000002; |