Commit 8d3eaab871
Changed files (8)
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 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 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 SetHandleInformation(hObject: HANDLE, dwMask: DWORD, dwFlags: DWORD) -> 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 TerminateProcess(hProcess: HANDLE, uExitCode: UINT) -> BOOL;
-pub extern "kernel32" stdcallcc fn HeapFree(hHeap: HANDLE, dwFlags: DWORD, lpMem: LPVOID) -> BOOL;
+pub extern "kernel32" stdcallcc fn WaitForSingleObject(hHandle: HANDLE, dwMilliseconds: DWORD) -> DWORD;
-pub extern "kernel32" stdcallcc fn GetProcessHeap() -> HANDLE;
+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;
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));
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");
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];
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 {
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];
+}
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);
}
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