authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2017-10-13 09:31:03-04:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2017-10-14 15:32:18-04:00
log8d3eaab8717381023ba8b6837fc5840ad8be1004
treea53bba1b992fe8c84abd2f108c56cf87dd852c4b
parent7f9dc4ebc10eb13e73466224f28ba62747693df9

implement std.os.ChildProcess for windows


8 files changed, 528 insertions(+), 64 deletions(-)

README.md+2-5
......@@ -18,7 +18,7 @@ clarity.
1818 writing buggy code.
1919 * Debug mode optimizes for fast compilation time and crashing with a stack trace
2020 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
2222 "Link Time Optimization" Zig does automatically.
2323 * Compatible with C libraries with no wrapper necessary. Directly include
2424 C .h files and get access to the functions and symbols therein.
......@@ -36,16 +36,13 @@ clarity.
3636 a preprocessor or macros.
3737 * The binaries produced by Zig have complete debugging information so you can,
3838 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`.
4040 * Friendly toward package maintainers. Reproducible build, bootstrapping
4141 process carefully documented. Issues filed by package maintainers are
4242 considered especially important.
4343 * Cross-compiling is a primary use case.
4444 * In addition to creating executables, creating a C library is a primary use
4545 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.
4946
5047### Support Table
5148
std/buf_set.zig+6-2
......@@ -7,9 +7,9 @@ pub const BufSet = struct {
77
88 const BufSetHashMap = HashMap([]const u8, void, mem.hash_slice_u8, mem.eql_slice_u8);
99
10 pub fn init(allocator: &Allocator) -> BufSet {
10 pub fn init(a: &Allocator) -> BufSet {
1111 var self = BufSet {
12 .hash_map = BufSetHashMap.init(allocator),
12 .hash_map = BufSetHashMap.init(a),
1313 };
1414 return self;
1515 }
......@@ -45,6 +45,10 @@ pub const BufSet = struct {
4545 return self.hash_map.iterator();
4646 }
4747
48 pub fn allocator(self: &const BufSet) -> &Allocator {
49 return self.hash_map.allocator;
50 }
51
4852 fn free(self: &BufSet, value: []const u8) {
4953 // remove the const
5054 const mut_value = @ptrCast(&u8, value.ptr)[0..value.len];
std/buffer.zig+11-2
......@@ -91,8 +91,17 @@ pub const Buffer = struct {
9191 }
9292
9393 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 }
96105 }
97106
98107 pub fn eql(self: &const Buffer, m: []const u8) -> bool {
std/cstr.zig+11
......@@ -1,4 +1,5 @@
11const debug = @import("debug.zig");
2const mem = @import("mem.zig");
23const assert = debug.assert;
34
45pub fn len(ptr: &const u8) -> usize {
......@@ -36,3 +37,13 @@ fn testCStrFnsImpl() {
3637 assert(cmp(c"aoeu", c"aoez") == -1);
3738 assert(len(c"123456789") == 9);
3839}
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.
44pub 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,
254254 return null;
255255}
256256
257pub fn indexOfAny(comptime T: type, slice: []const T, values: []const T) -> ?usize {
258 return indexOfAnyPos(T, slice, 0, values);
259}
260
261pub 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
257272pub fn indexOf(comptime T: type, haystack: []const T, needle: []const T) -> ?usize {
258273 return indexOfPos(T, haystack, 0, needle);
259274}
std/os/child_process.zig+357-16
......@@ -1,22 +1,30 @@
1const io = @import("../io.zig");
2const os = @import("index.zig");
1const std = @import("../index.zig");
2const cstr = std.cstr;
3const io = std.io;
4const os = std.os;
35const posix = os.posix;
4const mem = @import("../mem.zig");
6const windows = os.windows;
7const mem = std.mem;
58const Allocator = mem.Allocator;
6const debug = @import("../debug.zig");
9const debug = std.debug;
710const assert = debug.assert;
8const BufMap = @import("../buf_map.zig").BufMap;
11const BufMap = std.BufMap;
12const Buffer = std.Buffer;
913const builtin = @import("builtin");
1014const Os = builtin.Os;
11const LinkedList = @import("../linked_list.zig").LinkedList;
15const LinkedList = std.LinkedList;
1216
1317error PermissionDenied;
1418error ProcessNotFound;
1519
1620var children_nodes = LinkedList(&ChildProcess).init();
1721
22const is_windows = builtin.os == Os.windows;
23
1824pub 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
2028 pub allocator: &mem.Allocator,
2129
2230 pub stdin: ?&io.OutStream,
......@@ -38,16 +46,16 @@ pub const ChildProcess = struct {
3846 pub stderr_behavior: StdIo,
3947
4048 /// Set to change the user id when spawning the child process.
41 pub uid: ?u32,
49 pub uid: if (is_windows) void else ?u32,
4250
4351 /// Set to change the group id when spawning the child process.
44 pub gid: ?u32,
52 pub gid: if (is_windows) void else ?u32,
4553
4654 /// Set to change the current working directory when spawning the child process.
4755 pub cwd: ?[]const u8,
4856
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,
5159
5260 pub const Term = enum {
5361 Exited: i32,
......@@ -73,14 +81,15 @@ pub const ChildProcess = struct {
7381 .allocator = allocator,
7482 .argv = argv,
7583 .pid = undefined,
84 .handle = undefined,
7685 .err_pipe = undefined,
7786 .llnode = undefined,
7887 .term = null,
7988 .onTerm = null,
8089 .env_map = null,
8190 .cwd = null,
82 .uid = null,
83 .gid = null,
91 .uid = if (is_windows) {} else null,
92 .gid = if (is_windows) {} else null,
8493 .stdin = null,
8594 .stdout = null,
8695 .stderr = null,
......@@ -101,9 +110,10 @@ pub const ChildProcess = struct {
101110 /// onTerm can be called before `spawn` returns.
102111 /// On success must call `kill` or `wait`.
103112 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();
107117 };
108118 }
109119
......@@ -114,6 +124,30 @@ pub const ChildProcess = struct {
114124
115125 /// Forcibly terminates child process and then cleans up all resources.
116126 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 {
117151 block_SIGCHLD();
118152 defer restore_SIGCHLD();
119153
......@@ -137,6 +171,24 @@ pub const ChildProcess = struct {
137171
138172 /// Blocks until child process terminates and then cleans up all resources.
139173 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 {
140192 block_SIGCHLD();
141193 defer restore_SIGCHLD();
142194
......@@ -153,6 +205,23 @@ pub const ChildProcess = struct {
153205 self.allocator.destroy(self);
154206 }
155207
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
156225 fn waitUnwrapped(self: &ChildProcess) {
157226 var status: i32 = undefined;
158227 while (true) {
......@@ -262,16 +331,21 @@ pub const ChildProcess = struct {
262331 } else {
263332 null
264333 };
334 %defer if (stdin_ptr) |ptr| self.allocator.destroy(ptr);
335
265336 const stdout_ptr = if (self.stdout_behavior == StdIo.Pipe) {
266337 %return self.allocator.create(io.InStream)
267338 } else {
268339 null
269340 };
341 %defer if (stdout_ptr) |ptr| self.allocator.destroy(ptr);
342
270343 const stderr_ptr = if (self.stderr_behavior == StdIo.Pipe) {
271344 %return self.allocator.create(io.InStream)
272345 } else {
273346 null
274347 };
348 %defer if (stderr_ptr) |ptr| self.allocator.destroy(ptr);
275349
276350 block_SIGCHLD();
277351 const pid_result = posix.fork();
......@@ -355,6 +429,195 @@ pub const ChildProcess = struct {
355429 if (self.stderr_behavior == StdIo.Pipe) { os.posixClose(stderr_pipe[1]); }
356430 }
357431
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
358621 fn setUpChildIo(stdio: StdIo, pipe_fd: i32, std_fileno: i32, dev_null_fd: i32) -> %void {
359622 switch (stdio) {
360623 StdIo.Pipe => %return os.posixDup2(pipe_fd, std_fileno),
......@@ -365,6 +628,84 @@ pub const ChildProcess = struct {
365628 }
366629};
367630
631/// Caller must dealloc.
632/// Guarantees a null byte at result[result.len].
633fn 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
668fn windowsDestroyPipe(rd: ?windows.HANDLE, wr: ?windows.HANDLE) {
669 if (rd) |h| os.windowsClose(h);
670 if (wr) |h| os.windowsClose(h);
671}
672
673fn 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
682fn 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
691fn 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
700fn 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
368709fn makePipe() -> %[2]i32 {
369710 var fds: [2]i32 = undefined;
370711 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 {
382382 }
383383}
384384
385pub 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
408pub 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
385416/// This function must allocate memory to add a null terminating bytes on path and each arg.
386417/// It must also convert to KEY=VALUE\0 format for environment variables, and include null
387418/// pointers after the args and after the environment variables.
......@@ -408,31 +439,8 @@ pub fn posixExecve(argv: []const []const u8, env_map: &const BufMap,
408439 }
409440 argv_buf[argv.len] = null;
410441
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);
436444
437445 const exe_path = argv[0];
438446 if (mem.indexOfScalar(u8, exe_path, '/') != null) {
......@@ -1367,6 +1375,22 @@ fn testWindowsCmdLine(input_cmd_line: &const u8, expected_args: []const []const
13671375 assert(it.next(&debug.global_allocator) == null);
13681376}
13691377
1378error WaitAbandoned;
1379error WaitTimeOut;
1380
1381pub 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
13701394test "std.os" {
13711395 _ = @import("child_process.zig");
13721396 _ = @import("darwin_errno.zig");
std/os/windows/index.zig+77-14
......@@ -14,6 +14,14 @@ pub extern "kernel32" stdcallcc fn CreateFileA(lpFileName: LPCSTR, dwDesiredAcce
1414 dwShareMode: DWORD, lpSecurityAttributes: ?LPSECURITY_ATTRIBUTES, dwCreationDisposition: DWORD,
1515 dwFlagsAndAttributes: DWORD, hTemplateFile: ?HANDLE) -> HANDLE;
1616
17pub extern "kernel32" stdcallcc fn CreatePipe(hReadPipe: &HANDLE, hWritePipe: &HANDLE,
18 lpPipeAttributes: &SECURITY_ATTRIBUTES, nSize: DWORD) -> BOOL;
19
20pub 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
1725pub extern "kernel32" stdcallcc fn DeleteFileA(lpFileName: LPCSTR) -> bool;
1826
1927pub extern "kernel32" stdcallcc fn ExitProcess(exit_code: UINT) -> noreturn;
......@@ -24,11 +32,10 @@ pub extern "kernel32" stdcallcc fn GetConsoleMode(in_hConsoleHandle: HANDLE, out
2432
2533pub extern "kernel32" stdcallcc fn GetCurrentDirectoryA(nBufferLength: WORD, lpBuffer: ?LPSTR) -> DWORD;
2634
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.
35pub extern "kernel32" stdcallcc fn GetExitCodeProcess(hProcess: HANDLE, lpExitCode: &DWORD) -> BOOL;
36
2937pub extern "kernel32" stdcallcc fn GetLastError() -> DWORD;
3038
31/// Retrieves file information for the specified file.
3239pub extern "kernel32" stdcallcc fn GetFileInformationByHandleEx(in_hFile: HANDLE,
3340 in_FileInformationClass: FILE_INFO_BY_HANDLE_CLASS, out_lpFileInformation: &c_void,
3441 in_dwBufferSize: DWORD) -> bool;
......@@ -36,26 +43,29 @@ pub extern "kernel32" stdcallcc fn GetFileInformationByHandleEx(in_hFile: HANDLE
3643pub extern "kernel32" stdcallcc fn GetFinalPathNameByHandleA(hFile: HANDLE, lpszFilePath: LPSTR,
3744 cchFilePath: DWORD, dwFlags: DWORD) -> DWORD;
3845
39/// Retrieves a handle to the specified standard device (standard input, standard output, or standard error).
46pub extern "kernel32" stdcallcc fn GetProcessHeap() -> HANDLE;
47
4048pub extern "kernel32" stdcallcc fn GetStdHandle(in_nStdHandle: DWORD) -> ?HANDLE;
4149
50pub extern "kernel32" stdcallcc fn HeapAlloc(hHeap: HANDLE, dwFlags: DWORD, dwBytes: SIZE_T) -> LPVOID;
51
52pub extern "kernel32" stdcallcc fn HeapFree(hHeap: HANDLE, dwFlags: DWORD, lpMem: LPVOID) -> BOOL;
53
4254pub extern "kernel32" stdcallcc fn ReadFile(in_hFile: HANDLE, out_lpBuffer: LPVOID,
4355 in_nNumberOfBytesToRead: DWORD, out_lpNumberOfBytesRead: &DWORD,
4456 in_out_lpOverlapped: ?&OVERLAPPED) -> BOOL;
4557
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.
48pub 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;
58pub extern "kernel32" stdcallcc fn SetHandleInformation(hObject: HANDLE, dwMask: DWORD, dwFlags: DWORD) -> BOOL;
5159
5260pub extern "kernel32" stdcallcc fn Sleep(dwMilliseconds: DWORD);
5361
54pub extern "kernel32" stdcallcc fn HeapAlloc(hHeap: HANDLE, dwFlags: DWORD, dwBytes: SIZE_T) -> LPVOID;
62pub extern "kernel32" stdcallcc fn TerminateProcess(hProcess: HANDLE, uExitCode: UINT) -> BOOL;
5563
56pub extern "kernel32" stdcallcc fn HeapFree(hHeap: HANDLE, dwFlags: DWORD, lpMem: LPVOID) -> BOOL;
64pub extern "kernel32" stdcallcc fn WaitForSingleObject(hHandle: HANDLE, dwMilliseconds: DWORD) -> DWORD;
5765
58pub extern "kernel32" stdcallcc fn GetProcessHeap() -> HANDLE;
66pub 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;
5969
6070pub extern "user32" stdcallcc fn MessageBoxA(hWnd: ?HANDLE, lpText: ?LPCTSTR, lpCaption: ?LPCTSTR, uType: UINT) -> c_int;
6171
......@@ -88,7 +98,7 @@ pub const INT = c_int;
8898pub const ULONG_PTR = usize;
8999pub const WCHAR = u16;
90100pub const LPCVOID = &const c_void;
91
101pub const LPBYTE = &BYTE;
92102
93103/// The standard input device. Initially, this is the console input buffer, CONIN$.
94104pub const STD_INPUT_HANDLE = @maxValue(DWORD) - 10 + 1;
......@@ -158,7 +168,7 @@ pub const VOLUME_NAME_NT = 0x2;
158168
159169pub const SECURITY_ATTRIBUTES = extern struct {
160170 nLength: DWORD,
161 lpSecurityDescriptor: LPVOID,
171 lpSecurityDescriptor: ?LPVOID,
162172 bInheritHandle: BOOL,
163173};
164174pub const PSECURITY_ATTRIBUTES = &SECURITY_ATTRIBUTES;
......@@ -189,3 +199,56 @@ pub const FILE_ATTRIBUTE_OFFLINE = 0x1000;
189199pub const FILE_ATTRIBUTE_READONLY = 0x1;
190200pub const FILE_ATTRIBUTE_SYSTEM = 0x4;
191201pub const FILE_ATTRIBUTE_TEMPORARY = 0x100;
202
203pub const PROCESS_INFORMATION = extern struct {
204 hProcess: HANDLE,
205 hThread: HANDLE,
206 dwProcessId: DWORD,
207 dwThreadId: DWORD,
208};
209
210pub 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
231pub const STARTF_FORCEONFEEDBACK = 0x00000040;
232pub const STARTF_FORCEOFFFEEDBACK = 0x00000080;
233pub const STARTF_PREVENTPINNING = 0x00002000;
234pub const STARTF_RUNFULLSCREEN = 0x00000020;
235pub const STARTF_TITLEISAPPID = 0x00001000;
236pub const STARTF_TITLEISLINKNAME = 0x00000800;
237pub const STARTF_UNTRUSTEDSOURCE = 0x00008000;
238pub const STARTF_USECOUNTCHARS = 0x00000008;
239pub const STARTF_USEFILLATTRIBUTE = 0x00000010;
240pub const STARTF_USEHOTKEY = 0x00000200;
241pub const STARTF_USEPOSITION = 0x00000004;
242pub const STARTF_USESHOWWINDOW = 0x00000001;
243pub const STARTF_USESIZE = 0x00000002;
244pub const STARTF_USESTDHANDLES = 0x00000100;
245
246pub const INFINITE = 4294967295;
247
248pub const WAIT_ABANDONED = 0x00000080;
249pub const WAIT_OBJECT_0 = 0x00000000;
250pub const WAIT_TIMEOUT = 0x00000102;
251pub const WAIT_FAILED = 0xFFFFFFFF;
252
253pub const HANDLE_FLAG_INHERIT = 0x00000001;
254pub const HANDLE_FLAG_PROTECT_FROM_CLOSE = 0x00000002;