diff --git a/README.md b/README.md index a28da25e54..20c0f47d63 100644 --- a/README.md +++ b/README.md @@ -18,7 +18,7 @@ clarity. writing buggy code. * Debug mode optimizes for fast compilation time and crashing with a stack trace when undefined behavior *would* happen. - * Release mode produces heavily optimized code. What other projects call + * ReleaseFast mode produces heavily optimized code. What other projects call "Link Time Optimization" Zig does automatically. * Compatible with C libraries with no wrapper necessary. Directly include C .h files and get access to the functions and symbols therein. @@ -36,16 +36,13 @@ clarity. a preprocessor or macros. * The binaries produced by Zig have complete debugging information so you can, for example, use GDB to debug your software. - * Mark functions as tests and automatically run them with `zig test`. + * Built-in unit tests with `zig test`. * Friendly toward package maintainers. Reproducible build, bootstrapping process carefully documented. Issues filed by package maintainers are considered especially important. * Cross-compiling is a primary use case. * In addition to creating executables, creating a C library is a primary use case. You can export an auto-generated .h file. - * For OS development, Zig supports all architectures that LLVM does. All the - standard library that does not depend on an OS is available to you in - freestanding mode. ### Support Table diff --git a/std/buf_set.zig b/std/buf_set.zig index c5ff790c7a..43002a8756 100644 --- a/std/buf_set.zig +++ b/std/buf_set.zig @@ -7,9 +7,9 @@ pub const BufSet = struct { const BufSetHashMap = HashMap([]const u8, void, mem.hash_slice_u8, mem.eql_slice_u8); - pub fn init(allocator: &Allocator) -> BufSet { + pub fn init(a: &Allocator) -> BufSet { var self = BufSet { - .hash_map = BufSetHashMap.init(allocator), + .hash_map = BufSetHashMap.init(a), }; return self; } @@ -45,6 +45,10 @@ pub const BufSet = struct { return self.hash_map.iterator(); } + pub fn allocator(self: &const BufSet) -> &Allocator { + return self.hash_map.allocator; + } + fn free(self: &BufSet, value: []const u8) { // remove the const const mut_value = @ptrCast(&u8, value.ptr)[0..value.len]; diff --git a/std/buffer.zig b/std/buffer.zig index b7b81e6442..2686a5cf22 100644 --- a/std/buffer.zig +++ b/std/buffer.zig @@ -91,8 +91,17 @@ pub const Buffer = struct { } pub fn appendByte(self: &Buffer, byte: u8) -> %void { - %return self.resize(self.len() + 1); - self.list.items[self.len() - 1] = byte; + return self.appendByteNTimes(byte, 1); + } + + pub fn appendByteNTimes(self: &Buffer, byte: u8, count: usize) -> %void { + var prev_size: usize = self.len(); + %return self.resize(prev_size + count); + + var i: usize = 0; + while (i < count) : (i += 1) { + self.list.items[prev_size + i] = byte; + } } pub fn eql(self: &const Buffer, m: []const u8) -> bool { diff --git a/std/cstr.zig b/std/cstr.zig index 60b9b77d9d..7e61381b90 100644 --- a/std/cstr.zig +++ b/std/cstr.zig @@ -1,4 +1,5 @@ const debug = @import("debug.zig"); +const mem = @import("mem.zig"); const assert = debug.assert; pub fn len(ptr: &const u8) -> usize { @@ -36,3 +37,13 @@ fn testCStrFnsImpl() { assert(cmp(c"aoeu", c"aoez") == -1); assert(len(c"123456789") == 9); } + +/// Returns a mutable slice with exactly the same size which is guaranteed to +/// have a null byte after it. +/// Caller owns the returned memory. +pub fn addNullByte(allocator: &mem.Allocator, slice: []const u8) -> %[]u8 { + const result = %return allocator.alloc(u8, slice.len + 1); + mem.copy(u8, result, slice); + result[slice.len] = 0; + return result[0..slice.len]; +} diff --git a/std/mem.zig b/std/mem.zig index 7ce497762d..3975ad275e 100644 --- a/std/mem.zig +++ b/std/mem.zig @@ -254,6 +254,21 @@ pub fn indexOfScalarPos(comptime T: type, slice: []const T, start_index: usize, return null; } +pub fn indexOfAny(comptime T: type, slice: []const T, values: []const T) -> ?usize { + return indexOfAnyPos(T, slice, 0, values); +} + +pub fn indexOfAnyPos(comptime T: type, slice: []const T, start_index: usize, values: []const T) -> ?usize { + var i: usize = start_index; + while (i < slice.len) : (i += 1) { + for (values) |value| { + if (slice[i] == value) + return i; + } + } + return null; +} + pub fn indexOf(comptime T: type, haystack: []const T, needle: []const T) -> ?usize { return indexOfPos(T, haystack, 0, needle); } diff --git a/std/os/child_process.zig b/std/os/child_process.zig index 679b79c011..4efb50da9b 100644 --- a/std/os/child_process.zig +++ b/std/os/child_process.zig @@ -1,22 +1,30 @@ -const io = @import("../io.zig"); -const os = @import("index.zig"); +const std = @import("../index.zig"); +const cstr = std.cstr; +const io = std.io; +const os = std.os; const posix = os.posix; -const mem = @import("../mem.zig"); +const windows = os.windows; +const mem = std.mem; const Allocator = mem.Allocator; -const debug = @import("../debug.zig"); +const debug = std.debug; const assert = debug.assert; -const BufMap = @import("../buf_map.zig").BufMap; +const BufMap = std.BufMap; +const Buffer = std.Buffer; const builtin = @import("builtin"); const Os = builtin.Os; -const LinkedList = @import("../linked_list.zig").LinkedList; +const LinkedList = std.LinkedList; error PermissionDenied; error ProcessNotFound; var children_nodes = LinkedList(&ChildProcess).init(); +const is_windows = builtin.os == Os.windows; + pub const ChildProcess = struct { - pub pid: i32, + pub pid: if (is_windows) void else i32, + pub handle: if (is_windows) windows.HANDLE else void, + pub allocator: &mem.Allocator, pub stdin: ?&io.OutStream, @@ -38,16 +46,16 @@ pub const ChildProcess = struct { pub stderr_behavior: StdIo, /// Set to change the user id when spawning the child process. - pub uid: ?u32, + pub uid: if (is_windows) void else ?u32, /// Set to change the group id when spawning the child process. - pub gid: ?u32, + pub gid: if (is_windows) void else ?u32, /// Set to change the current working directory when spawning the child process. pub cwd: ?[]const u8, - err_pipe: [2]i32, - llnode: LinkedList(&ChildProcess).Node, + err_pipe: if (is_windows) void else [2]i32, + llnode: if (is_windows) void else LinkedList(&ChildProcess).Node, pub const Term = enum { Exited: i32, @@ -73,14 +81,15 @@ pub const ChildProcess = struct { .allocator = allocator, .argv = argv, .pid = undefined, + .handle = undefined, .err_pipe = undefined, .llnode = undefined, .term = null, .onTerm = null, .env_map = null, .cwd = null, - .uid = null, - .gid = null, + .uid = if (is_windows) {} else null, + .gid = if (is_windows) {} else null, .stdin = null, .stdout = null, .stderr = null, @@ -101,9 +110,10 @@ pub const ChildProcess = struct { /// onTerm can be called before `spawn` returns. /// On success must call `kill` or `wait`. pub fn spawn(self: &ChildProcess) -> %void { - return switch (builtin.os) { - Os.linux, Os.macosx, Os.ios, Os.darwin => self.spawnPosix(), - else => @compileError("Unsupported OS"), + if (is_windows) { + return self.spawnWindows(); + } else { + return self.spawnPosix(); }; } @@ -114,6 +124,30 @@ pub const ChildProcess = struct { /// Forcibly terminates child process and then cleans up all resources. pub fn kill(self: &ChildProcess) -> %Term { + if (is_windows) { + return self.killWindows(1); + } else { + return self.killPosix(); + } + } + + pub fn killWindows(self: &ChildProcess, exit_code: windows.UINT) -> %Term { + if (self.term) |term| { + self.cleanupStreams(); + return term; + } + + if (!windows.TerminateProcess(self.handle, exit_code)) { + const err = windows.GetLastError(); + return switch (err) { + else => error.Unexpected, + }; + } + self.waitUnwrappedWindows(); + return ??self.term; + } + + pub fn killPosix(self: &ChildProcess) -> %Term { block_SIGCHLD(); defer restore_SIGCHLD(); @@ -137,6 +171,24 @@ pub const ChildProcess = struct { /// Blocks until child process terminates and then cleans up all resources. pub fn wait(self: &ChildProcess) -> %Term { + if (is_windows) { + return self.waitWindows(); + } else { + return self.waitPosix(); + } + } + + fn waitWindows(self: &ChildProcess) -> %Term { + if (self.term) |term| { + self.cleanupStreams(); + return term; + } + + %return self.waitUnwrappedWindows(); + return ??self.term; + } + + fn waitPosix(self: &ChildProcess) -> %Term { block_SIGCHLD(); defer restore_SIGCHLD(); @@ -153,6 +205,23 @@ pub const ChildProcess = struct { self.allocator.destroy(self); } + fn waitUnwrappedWindows(self: &ChildProcess) -> %void { + const result = os.windowsWaitSingle(self.handle, windows.INFINITE); + + self.term = (%Term)({ + var exit_code: windows.DWORD = undefined; + if (!windows.GetExitCodeProcess(self.handle, &exit_code)) { + Term.Unknown{0} + } else { + Term.Exited {@bitCast(i32, exit_code)} + } + }); + + os.windowsClose(self.handle); + self.cleanupStreams(); + return result; + } + fn waitUnwrapped(self: &ChildProcess) { var status: i32 = undefined; while (true) { @@ -262,16 +331,21 @@ pub const ChildProcess = struct { } else { null }; + %defer if (stdin_ptr) |ptr| self.allocator.destroy(ptr); + const stdout_ptr = if (self.stdout_behavior == StdIo.Pipe) { %return self.allocator.create(io.InStream) } else { null }; + %defer if (stdout_ptr) |ptr| self.allocator.destroy(ptr); + const stderr_ptr = if (self.stderr_behavior == StdIo.Pipe) { %return self.allocator.create(io.InStream) } else { null }; + %defer if (stderr_ptr) |ptr| self.allocator.destroy(ptr); block_SIGCHLD(); const pid_result = posix.fork(); @@ -355,6 +429,195 @@ pub const ChildProcess = struct { if (self.stderr_behavior == StdIo.Pipe) { os.posixClose(stderr_pipe[1]); } } + fn spawnWindows(self: &ChildProcess) -> %void { + var saAttr: windows.SECURITY_ATTRIBUTES = undefined; + saAttr.nLength = @sizeOf(windows.SECURITY_ATTRIBUTES); + saAttr.bInheritHandle = true; + saAttr.lpSecurityDescriptor = null; + + const any_ignore = (self.stdin_behavior == StdIo.Ignore or + self.stdout_behavior == StdIo.Ignore or + self.stderr_behavior == StdIo.Ignore); + + const nul_handle = if (any_ignore) { + %return os.windowsOpen("NUL", windows.GENERIC_READ, windows.FILE_SHARE_READ, + windows.OPEN_EXISTING, windows.FILE_ATTRIBUTE_NORMAL, null) + } else { + undefined + }; + defer { if (any_ignore) os.windowsClose(nul_handle); }; + if (any_ignore) { + %return windowsSetHandleInfo(nul_handle, windows.HANDLE_FLAG_INHERIT, 0); + } + + + var g_hChildStd_IN_Rd: ?windows.HANDLE = null; + var g_hChildStd_IN_Wr: ?windows.HANDLE = null; + switch (self.stdin_behavior) { + StdIo.Pipe => { + %return windowsMakePipeIn(&g_hChildStd_IN_Rd, &g_hChildStd_IN_Wr, &saAttr); + }, + StdIo.Ignore => { + g_hChildStd_IN_Rd = nul_handle; + }, + StdIo.Inherit => { + g_hChildStd_IN_Rd = windows.GetStdHandle(windows.STD_INPUT_HANDLE); + }, + StdIo.Close => { + g_hChildStd_IN_Rd = null; + }, + } + %defer if (self.stdin_behavior == StdIo.Pipe) { windowsDestroyPipe(g_hChildStd_IN_Rd, g_hChildStd_IN_Wr); }; + + var g_hChildStd_OUT_Rd: ?windows.HANDLE = null; + var g_hChildStd_OUT_Wr: ?windows.HANDLE = null; + switch (self.stdout_behavior) { + StdIo.Pipe => { + %return windowsMakePipeOut(&g_hChildStd_OUT_Rd, &g_hChildStd_OUT_Wr, &saAttr); + }, + StdIo.Ignore => { + g_hChildStd_OUT_Wr = nul_handle; + }, + StdIo.Inherit => { + g_hChildStd_OUT_Wr = windows.GetStdHandle(windows.STD_OUTPUT_HANDLE); + }, + StdIo.Close => { + g_hChildStd_OUT_Wr = null; + }, + } + %defer if (self.stdin_behavior == StdIo.Pipe) { windowsDestroyPipe(g_hChildStd_OUT_Rd, g_hChildStd_OUT_Wr); }; + + var g_hChildStd_ERR_Rd: ?windows.HANDLE = null; + var g_hChildStd_ERR_Wr: ?windows.HANDLE = null; + switch (self.stderr_behavior) { + StdIo.Pipe => { + %return windowsMakePipeOut(&g_hChildStd_ERR_Rd, &g_hChildStd_ERR_Wr, &saAttr); + }, + StdIo.Ignore => { + g_hChildStd_ERR_Wr = nul_handle; + }, + StdIo.Inherit => { + g_hChildStd_ERR_Wr = windows.GetStdHandle(windows.STD_ERROR_HANDLE); + }, + StdIo.Close => { + g_hChildStd_ERR_Wr = null; + }, + } + %defer if (self.stdin_behavior == StdIo.Pipe) { windowsDestroyPipe(g_hChildStd_ERR_Rd, g_hChildStd_ERR_Wr); }; + + const stdin_ptr = if (self.stdin_behavior == StdIo.Pipe) { + %return self.allocator.create(io.OutStream) + } else { + null + }; + %defer if (stdin_ptr) |ptr| self.allocator.destroy(ptr); + + const stdout_ptr = if (self.stdout_behavior == StdIo.Pipe) { + %return self.allocator.create(io.InStream) + } else { + null + }; + %defer if (stdout_ptr) |ptr| self.allocator.destroy(ptr); + + const stderr_ptr = if (self.stderr_behavior == StdIo.Pipe) { + %return self.allocator.create(io.InStream) + } else { + null + }; + %defer if (stderr_ptr) |ptr| self.allocator.destroy(ptr); + + const cmd_line = %return windowsCreateCommandLine(self.allocator, self.argv); + defer self.allocator.free(cmd_line); + + var siStartInfo = windows.STARTUPINFOA { + .cb = @sizeOf(windows.STARTUPINFOA), + .hStdError = g_hChildStd_ERR_Wr, + .hStdOutput = g_hChildStd_OUT_Wr, + .hStdInput = g_hChildStd_IN_Rd, + .dwFlags = windows.STARTF_USESTDHANDLES, + + .lpReserved = null, + .lpDesktop = null, + .lpTitle = null, + .dwX = 0, + .dwY = 0, + .dwXSize = 0, + .dwYSize = 0, + .dwXCountChars = 0, + .dwYCountChars = 0, + .dwFillAttribute = 0, + .wShowWindow = 0, + .cbReserved2 = 0, + .lpReserved2 = null, + }; + var piProcInfo: windows.PROCESS_INFORMATION = undefined; + + const app_name = %return cstr.addNullByte(self.allocator, self.argv[0]); + defer self.allocator.free(app_name); + + const cwd_slice = if (self.cwd) |cwd| { + %return cstr.addNullByte(self.allocator, cwd) + } else { + null + }; + defer if (cwd_slice) |cwd| self.allocator.free(cwd); + const cwd_ptr = if (cwd_slice) |cwd| cwd.ptr else null; + + const maybe_envp_buf = if (self.env_map) |env_map| { + %return os.createNullDelimitedEnvMap(self.allocator, env_map) + } else { + null + }; + defer if (maybe_envp_buf) |envp_buf| self.allocator.free(envp_buf); + const envp_ptr = if (maybe_envp_buf) |envp_buf| envp_buf.ptr else null; + + if (!windows.CreateProcessA(app_name.ptr, cmd_line.ptr, null, null, true, 0, + @ptrCast(?&c_void, envp_ptr), + cwd_ptr, &siStartInfo, &piProcInfo)) + { + const err = windows.GetLastError(); + return switch (err) { + windows.ERROR.FILE_NOT_FOUND => error.FileNotFound, + else => error.Unexpected, + }; + } + os.windowsClose(piProcInfo.hThread); + + if (stdin_ptr) |outstream| { + *outstream = io.OutStream { + .fd = {}, + .handle = g_hChildStd_IN_Wr, + .handle_id = undefined, + .buffer = undefined, + .index = 0, + }; + } + if (stdout_ptr) |instream| { + *instream = io.InStream { + .fd = {}, + .handle = g_hChildStd_OUT_Rd, + .handle_id = undefined, + }; + } + if (stderr_ptr) |instream| { + *instream = io.InStream { + .fd = {}, + .handle = g_hChildStd_ERR_Rd, + .handle_id = undefined, + }; + } + + self.handle = piProcInfo.hProcess; + self.term = null; + self.stdin = stdin_ptr; + self.stdout = stdout_ptr; + self.stderr = stderr_ptr; + + if (self.stdin_behavior == StdIo.Pipe) { os.windowsClose(??g_hChildStd_IN_Rd); } + if (self.stderr_behavior == StdIo.Pipe) { os.windowsClose(??g_hChildStd_ERR_Wr); } + if (self.stdout_behavior == StdIo.Pipe) { os.windowsClose(??g_hChildStd_OUT_Wr); } + } + fn setUpChildIo(stdio: StdIo, pipe_fd: i32, std_fileno: i32, dev_null_fd: i32) -> %void { switch (stdio) { StdIo.Pipe => %return os.posixDup2(pipe_fd, std_fileno), @@ -365,6 +628,84 @@ pub const ChildProcess = struct { } }; +/// Caller must dealloc. +/// Guarantees a null byte at result[result.len]. +fn windowsCreateCommandLine(allocator: &Allocator, argv: []const []const u8) -> %[]u8 { + var buf = %return Buffer.initSize(allocator, 0); + defer buf.deinit(); + + for (argv) |arg, arg_i| { + if (arg_i != 0) + %return buf.appendByte(' '); + if (mem.indexOfAny(u8, arg, " \t\n\"") == null) { + %return buf.append(arg); + continue; + } + %return buf.appendByte('"'); + var backslash_count: usize = 0; + for (arg) |byte| { + switch (byte) { + '\\' => backslash_count += 1, + '"' => { + %return buf.appendByteNTimes('\\', backslash_count * 2 + 1); + %return buf.appendByte('"'); + backslash_count = 0; + }, + else => { + %return buf.appendByteNTimes('\\', backslash_count); + %return buf.appendByte(byte); + backslash_count = 0; + }, + } + } + %return buf.appendByteNTimes('\\', backslash_count * 2); + %return buf.appendByte('"'); + } + + return buf.toOwnedSlice(); +} + +fn windowsDestroyPipe(rd: ?windows.HANDLE, wr: ?windows.HANDLE) { + if (rd) |h| os.windowsClose(h); + if (wr) |h| os.windowsClose(h); +} + +fn windowsMakePipe(rd: &windows.HANDLE, wr: &windows.HANDLE, sattr: &windows.SECURITY_ATTRIBUTES) -> %void { + if (!windows.CreatePipe(rd, wr, sattr, 0)) { + const err = windows.GetLastError(); + return switch (err) { + else => error.Unexpected, + }; + } +} + +fn windowsSetHandleInfo(h: windows.HANDLE, mask: windows.DWORD, flags: windows.DWORD) -> %void { + if (!windows.SetHandleInformation(h, mask, flags)) { + const err = windows.GetLastError(); + return switch (err) { + else => error.Unexpected, + }; + } +} + +fn windowsMakePipeIn(rd: &?windows.HANDLE, wr: &?windows.HANDLE, sattr: &windows.SECURITY_ATTRIBUTES) -> %void { + var rd_h: windows.HANDLE = undefined; + var wr_h: windows.HANDLE = undefined; + %return windowsMakePipe(&rd_h, &wr_h, sattr); + %return windowsSetHandleInfo(wr_h, windows.HANDLE_FLAG_INHERIT, 0); + *rd = rd_h; + *wr = wr_h; +} + +fn windowsMakePipeOut(rd: &?windows.HANDLE, wr: &?windows.HANDLE, sattr: &windows.SECURITY_ATTRIBUTES) -> %void { + var rd_h: windows.HANDLE = undefined; + var wr_h: windows.HANDLE = undefined; + %return windowsMakePipe(&rd_h, &wr_h, sattr); + %return windowsSetHandleInfo(rd_h, windows.HANDLE_FLAG_INHERIT, 0); + *rd = rd_h; + *wr = wr_h; +} + fn makePipe() -> %[2]i32 { var fds: [2]i32 = undefined; const err = posix.getErrno(posix.pipe(&fds)); diff --git a/std/os/index.zig b/std/os/index.zig index 9ecfb5901c..fcb32cbeb7 100644 --- a/std/os/index.zig +++ b/std/os/index.zig @@ -382,6 +382,37 @@ pub fn posixDup2(old_fd: i32, new_fd: i32) -> %void { } } +pub fn createNullDelimitedEnvMap(allocator: &Allocator, env_map: &const BufMap) -> %[]?&u8 { + const envp_count = env_map.count(); + const envp_buf = %return allocator.alloc(?&u8, envp_count + 1); + mem.set(?&u8, envp_buf, null); + %defer freeNullDelimitedEnvMap(allocator, envp_buf); + { + var it = env_map.iterator(); + var i: usize = 0; + while (it.next()) |pair| : (i += 1) { + const env_buf = %return allocator.alloc(u8, pair.key.len + pair.value.len + 2); + @memcpy(&env_buf[0], pair.key.ptr, pair.key.len); + env_buf[pair.key.len] = '='; + @memcpy(&env_buf[pair.key.len + 1], pair.value.ptr, pair.value.len); + env_buf[env_buf.len - 1] = 0; + + envp_buf[i] = env_buf.ptr; + } + assert(i == envp_count); + } + assert(envp_buf[envp_count] == null); + return envp_buf; +} + +pub fn freeNullDelimitedEnvMap(allocator: &Allocator, envp_buf: []?&u8) { + for (envp_buf) |env| { + const env_buf = if (env) |ptr| cstr.toSlice(ptr) else break; + allocator.free(env_buf); + } + allocator.free(envp_buf); +} + /// This function must allocate memory to add a null terminating bytes on path and each arg. /// It must also convert to KEY=VALUE\0 format for environment variables, and include null /// pointers after the args and after the environment variables. @@ -408,31 +439,8 @@ pub fn posixExecve(argv: []const []const u8, env_map: &const BufMap, } argv_buf[argv.len] = null; - const envp_count = env_map.count(); - const envp_buf = %return allocator.alloc(?&u8, envp_count + 1); - mem.set(?&u8, envp_buf, null); - defer { - for (envp_buf) |env| { - const env_buf = if (env) |ptr| cstr.toSlice(ptr) else break; - allocator.free(env_buf); - } - allocator.free(envp_buf); - } - { - var it = env_map.iterator(); - var i: usize = 0; - while (it.next()) |pair| : (i += 1) { - const env_buf = %return allocator.alloc(u8, pair.key.len + pair.value.len + 2); - @memcpy(&env_buf[0], pair.key.ptr, pair.key.len); - env_buf[pair.key.len] = '='; - @memcpy(&env_buf[pair.key.len + 1], pair.value.ptr, pair.value.len); - env_buf[env_buf.len - 1] = 0; - - envp_buf[i] = env_buf.ptr; - } - assert(i == envp_count); - } - envp_buf[envp_count] = null; + const envp_buf = %return createNullDelimitedEnvMap(allocator, env_map); + defer freeNullDelimitedEnvMap(allocator, envp_buf); const exe_path = argv[0]; if (mem.indexOfScalar(u8, exe_path, '/') != null) { @@ -1367,6 +1375,22 @@ fn testWindowsCmdLine(input_cmd_line: &const u8, expected_args: []const []const assert(it.next(&debug.global_allocator) == null); } +error WaitAbandoned; +error WaitTimeOut; + +pub fn windowsWaitSingle(handle: windows.HANDLE, milliseconds: windows.DWORD) -> %void { + const result = windows.WaitForSingleObject(handle, milliseconds); + return switch (result) { + windows.WAIT_ABANDONED => error.WaitAbandoned, + windows.WAIT_OBJECT_0 => {}, + windows.WAIT_TIMEOUT => error.WaitTimeOut, + windows.WAIT_FAILED => switch (windows.GetLastError()) { + else => error.Unexpected, + }, + else => error.Unexpected, + }; +} + test "std.os" { _ = @import("child_process.zig"); _ = @import("darwin_errno.zig"); diff --git a/std/os/windows/index.zig b/std/os/windows/index.zig index 90a2a30caa..d6b214d65f 100644 --- a/std/os/windows/index.zig +++ b/std/os/windows/index.zig @@ -14,6 +14,14 @@ pub extern "kernel32" stdcallcc fn CreateFileA(lpFileName: LPCSTR, dwDesiredAcce dwShareMode: DWORD, lpSecurityAttributes: ?LPSECURITY_ATTRIBUTES, dwCreationDisposition: DWORD, dwFlagsAndAttributes: DWORD, hTemplateFile: ?HANDLE) -> HANDLE; +pub extern "kernel32" stdcallcc fn CreatePipe(hReadPipe: &HANDLE, hWritePipe: &HANDLE, + lpPipeAttributes: &SECURITY_ATTRIBUTES, nSize: DWORD) -> BOOL; + +pub extern "kernel32" stdcallcc fn CreateProcessA(lpApplicationName: ?LPCSTR, lpCommandLine: LPSTR, + lpProcessAttributes: ?&SECURITY_ATTRIBUTES, lpThreadAttributes: ?&SECURITY_ATTRIBUTES, bInheritHandles: BOOL, + dwCreationFlags: DWORD, lpEnvironment: ?LPVOID, lpCurrentDirectory: ?LPCSTR, lpStartupInfo: &STARTUPINFOA, + lpProcessInformation: &PROCESS_INFORMATION) -> BOOL; + pub extern "kernel32" stdcallcc fn DeleteFileA(lpFileName: LPCSTR) -> bool; pub extern "kernel32" stdcallcc fn ExitProcess(exit_code: UINT) -> noreturn; @@ -24,11 +32,10 @@ pub extern "kernel32" stdcallcc fn GetConsoleMode(in_hConsoleHandle: HANDLE, out pub extern "kernel32" stdcallcc fn GetCurrentDirectoryA(nBufferLength: WORD, lpBuffer: ?LPSTR) -> DWORD; -/// Retrieves the calling thread's last-error code value. The last-error code is maintained on a per-thread basis. -/// Multiple threads do not overwrite each other's last-error code. +pub extern "kernel32" stdcallcc fn GetExitCodeProcess(hProcess: HANDLE, lpExitCode: &DWORD) -> BOOL; + pub extern "kernel32" stdcallcc fn GetLastError() -> DWORD; -/// Retrieves file information for the specified file. pub extern "kernel32" stdcallcc fn GetFileInformationByHandleEx(in_hFile: HANDLE, in_FileInformationClass: FILE_INFO_BY_HANDLE_CLASS, out_lpFileInformation: &c_void, in_dwBufferSize: DWORD) -> bool; @@ -36,26 +43,29 @@ pub extern "kernel32" stdcallcc fn GetFileInformationByHandleEx(in_hFile: HANDLE pub extern "kernel32" stdcallcc fn GetFinalPathNameByHandleA(hFile: HANDLE, lpszFilePath: LPSTR, cchFilePath: DWORD, dwFlags: DWORD) -> DWORD; -/// Retrieves a handle to the specified standard device (standard input, standard output, or standard error). +pub extern "kernel32" stdcallcc fn GetProcessHeap() -> HANDLE; + pub extern "kernel32" stdcallcc fn GetStdHandle(in_nStdHandle: DWORD) -> ?HANDLE; -pub extern "kernel32" stdcallcc fn ReadFile(in_hFile: HANDLE, out_lpBuffer: LPVOID, - in_nNumberOfBytesToRead: DWORD, out_lpNumberOfBytesRead: &DWORD, - in_out_lpOverlapped: ?&OVERLAPPED) -> BOOL; - -/// Writes data to the specified file or input/output (I/O) device. -/// This function is designed for both synchronous and asynchronous operation. For a similar function designed solely for asynchronous operation, see WriteFileEx. -pub extern "kernel32" stdcallcc fn WriteFile(in_hFile: HANDLE, in_lpBuffer: &const c_void, - in_nNumberOfBytesToWrite: DWORD, out_lpNumberOfBytesWritten: ?&DWORD, - in_out_lpOverlapped: ?&OVERLAPPED) -> BOOL; - -pub extern "kernel32" stdcallcc fn Sleep(dwMilliseconds: DWORD); - pub extern "kernel32" stdcallcc fn HeapAlloc(hHeap: HANDLE, dwFlags: DWORD, dwBytes: SIZE_T) -> LPVOID; pub extern "kernel32" stdcallcc fn HeapFree(hHeap: HANDLE, dwFlags: DWORD, lpMem: LPVOID) -> BOOL; -pub extern "kernel32" stdcallcc fn GetProcessHeap() -> HANDLE; +pub extern "kernel32" stdcallcc fn ReadFile(in_hFile: HANDLE, out_lpBuffer: LPVOID, + in_nNumberOfBytesToRead: DWORD, out_lpNumberOfBytesRead: &DWORD, + in_out_lpOverlapped: ?&OVERLAPPED) -> BOOL; + +pub extern "kernel32" stdcallcc fn SetHandleInformation(hObject: HANDLE, dwMask: DWORD, dwFlags: DWORD) -> BOOL; + +pub extern "kernel32" stdcallcc fn Sleep(dwMilliseconds: DWORD); + +pub extern "kernel32" stdcallcc fn TerminateProcess(hProcess: HANDLE, uExitCode: UINT) -> BOOL; + +pub extern "kernel32" stdcallcc fn WaitForSingleObject(hHandle: HANDLE, dwMilliseconds: DWORD) -> DWORD; + +pub extern "kernel32" stdcallcc fn WriteFile(in_hFile: HANDLE, in_lpBuffer: &const c_void, + in_nNumberOfBytesToWrite: DWORD, out_lpNumberOfBytesWritten: ?&DWORD, + in_out_lpOverlapped: ?&OVERLAPPED) -> BOOL; pub extern "user32" stdcallcc fn MessageBoxA(hWnd: ?HANDLE, lpText: ?LPCTSTR, lpCaption: ?LPCTSTR, uType: UINT) -> c_int; @@ -88,7 +98,7 @@ pub const INT = c_int; pub const ULONG_PTR = usize; pub const WCHAR = u16; pub const LPCVOID = &const c_void; - +pub const LPBYTE = &BYTE; /// The standard input device. Initially, this is the console input buffer, CONIN$. pub const STD_INPUT_HANDLE = @maxValue(DWORD) - 10 + 1; @@ -158,7 +168,7 @@ pub const VOLUME_NAME_NT = 0x2; pub const SECURITY_ATTRIBUTES = extern struct { nLength: DWORD, - lpSecurityDescriptor: LPVOID, + lpSecurityDescriptor: ?LPVOID, bInheritHandle: BOOL, }; pub const PSECURITY_ATTRIBUTES = &SECURITY_ATTRIBUTES; @@ -189,3 +199,56 @@ pub const FILE_ATTRIBUTE_OFFLINE = 0x1000; pub const FILE_ATTRIBUTE_READONLY = 0x1; pub const FILE_ATTRIBUTE_SYSTEM = 0x4; pub const FILE_ATTRIBUTE_TEMPORARY = 0x100; + +pub const PROCESS_INFORMATION = extern struct { + hProcess: HANDLE, + hThread: HANDLE, + dwProcessId: DWORD, + dwThreadId: DWORD, +}; + +pub const STARTUPINFOA = extern struct { + cb: DWORD, + lpReserved: ?LPSTR, + lpDesktop: ?LPSTR, + lpTitle: ?LPSTR, + dwX: DWORD, + dwY: DWORD, + dwXSize: DWORD, + dwYSize: DWORD, + dwXCountChars: DWORD, + dwYCountChars: DWORD, + dwFillAttribute: DWORD, + dwFlags: DWORD, + wShowWindow: WORD, + cbReserved2: WORD, + lpReserved2: ?LPBYTE, + hStdInput: ?HANDLE, + hStdOutput: ?HANDLE, + hStdError: ?HANDLE, +}; + +pub const STARTF_FORCEONFEEDBACK = 0x00000040; +pub const STARTF_FORCEOFFFEEDBACK = 0x00000080; +pub const STARTF_PREVENTPINNING = 0x00002000; +pub const STARTF_RUNFULLSCREEN = 0x00000020; +pub const STARTF_TITLEISAPPID = 0x00001000; +pub const STARTF_TITLEISLINKNAME = 0x00000800; +pub const STARTF_UNTRUSTEDSOURCE = 0x00008000; +pub const STARTF_USECOUNTCHARS = 0x00000008; +pub const STARTF_USEFILLATTRIBUTE = 0x00000010; +pub const STARTF_USEHOTKEY = 0x00000200; +pub const STARTF_USEPOSITION = 0x00000004; +pub const STARTF_USESHOWWINDOW = 0x00000001; +pub const STARTF_USESIZE = 0x00000002; +pub const STARTF_USESTDHANDLES = 0x00000100; + +pub const INFINITE = 4294967295; + +pub const WAIT_ABANDONED = 0x00000080; +pub const WAIT_OBJECT_0 = 0x00000000; +pub const WAIT_TIMEOUT = 0x00000102; +pub const WAIT_FAILED = 0xFFFFFFFF; + +pub const HANDLE_FLAG_INHERIT = 0x00000001; +pub const HANDLE_FLAG_PROTECT_FROM_CLOSE = 0x00000002;