Commit 17b0166e00

Andrew Kelley <andrew@ziglang.org>
2019-05-25 00:27:18
do Jay's suggestion with posix/os API naming & layout
1 parent 2def230
std/event/fs.zig
@@ -688,7 +688,7 @@ pub async fn readFile(loop: *Loop, file_path: []const u8, max_size: usize) ![]u8
     defer list.deinit();
 
     while (true) {
-        try list.ensureCapacity(list.len + os.page_size);
+        try list.ensureCapacity(list.len + mem.page_size);
         const buf = list.items[list.len..];
         const buf_array = [][]u8{buf};
         const amt = try await (async preadv(loop, fd, buf_array, list.len) catch unreachable);
std/fs/file.zig
@@ -0,0 +1,334 @@
+const std = @import("../std.zig");
+const builtin = @import("builtin");
+const os = std.os;
+const io = std.io;
+const mem = std.mem;
+const math = std.math;
+const assert = std.debug.assert;
+const windows = os.windows;
+const Os = builtin.Os;
+const maxInt = std.math.maxInt;
+
+pub const File = struct {
+    /// The OS-specific file descriptor or file handle.
+    handle: os.fd_t,
+
+    pub const Mode = switch (builtin.os) {
+        Os.windows => void,
+        else => u32,
+    };
+
+    pub const default_mode = switch (builtin.os) {
+        Os.windows => {},
+        else => 0o666,
+    };
+
+    pub const OpenError = windows.CreateFileError || os.OpenError;
+
+    /// Call close to clean up.
+    pub fn openRead(path: []const u8) OpenError!File {
+        if (windows.is_the_target and !builtin.link_libc) {
+            const path_w = try windows.sliceToPrefixedFileW(path);
+            return openReadW(&path_w);
+        }
+        const path_c = try os.toPosixPath(path);
+        return openReadC(&path_c);
+    }
+
+    /// `openRead` except with a null terminated path
+    pub fn openReadC(path: [*]const u8) OpenError!File {
+        if (windows.is_the_target and !builtin.link_libc) {
+            const path_w = try windows.cStrToPrefixedFileW(path);
+            return openReadW(&path_w);
+        }
+        const flags = os.O_LARGEFILE | os.O_RDONLY;
+        const fd = try os.openC(path, flags, 0);
+        return openHandle(fd);
+    }
+
+    /// `openRead` except with a null terminated UTF16LE encoded path
+    pub fn openReadW(path_w: [*]const u16) OpenError!File {
+        const handle = try windows.CreateFileW(
+            path_w,
+            windows.GENERIC_READ,
+            windows.FILE_SHARE_READ,
+            windows.OPEN_EXISTING,
+            windows.FILE_ATTRIBUTE_NORMAL,
+        );
+        return openHandle(handle);
+    }
+
+    /// Calls `openWriteMode` with `default_mode` for the mode.
+    pub fn openWrite(path: []const u8) OpenError!File {
+        return openWriteMode(path, default_mode);
+    }
+
+    /// If the path does not exist it will be created.
+    /// If a file already exists in the destination it will be truncated.
+    /// Call close to clean up.
+    pub fn openWriteMode(path: []const u8, file_mode: Mode) OpenError!File {
+        if (windows.is_the_target and !builtin.link_libc) {
+            const path_w = try windows.sliceToPrefixedFileW(path);
+            return openWriteModeW(&path_w, file_mode);
+        }
+        const path_c = try os.toPosixPath(path);
+        return openWriteModeC(&path_c, file_mode);
+    }
+
+    /// Same as `openWriteMode` except `path` is null-terminated.
+    pub fn openWriteModeC(path: [*]const u8, file_mode: Mode) OpenError!File {
+        if (windows.is_the_target and !builtin.link_libc) {
+            const path_w = try windows.cStrToPrefixedFileW(path);
+            return openWriteModeW(&path_w, file_mode);
+        }
+        const flags = os.O_LARGEFILE | os.O_WRONLY | os.O_CREAT | os.O_CLOEXEC | os.O_TRUNC;
+        const fd = try os.openC(path, flags, file_mode);
+        return openHandle(fd);
+    }
+
+    /// Same as `openWriteMode` except `path` is null-terminated and UTF16LE encoded
+    pub fn openWriteModeW(path_w: [*]const u16, file_mode: Mode) OpenError!File {
+        const handle = try windows.CreateFileW(
+            path_w,
+            windows.GENERIC_WRITE,
+            windows.FILE_SHARE_WRITE | windows.FILE_SHARE_READ | windows.FILE_SHARE_DELETE,
+            windows.CREATE_ALWAYS,
+            windows.FILE_ATTRIBUTE_NORMAL,
+        );
+        return openHandle(handle);
+    }
+
+    /// If the path does not exist it will be created.
+    /// If a file already exists in the destination this returns OpenError.PathAlreadyExists
+    /// Call close to clean up.
+    pub fn openWriteNoClobber(path: []const u8, file_mode: Mode) OpenError!File {
+        if (windows.is_the_target and !builtin.link_libc) {
+            const path_w = try windows.sliceToPrefixedFileW(path);
+            return openWriteNoClobberW(&path_w, file_mode);
+        }
+        const path_c = try os.toPosixPath(path);
+        return openWriteNoClobberC(&path_c, file_mode);
+    }
+
+    pub fn openWriteNoClobberC(path: [*]const u8, file_mode: Mode) OpenError!File {
+        if (windows.is_the_target and !builtin.link_libc) {
+            const path_w = try windows.cStrToPrefixedFileW(path);
+            return openWriteNoClobberW(&path_w, file_mode);
+        }
+        const flags = os.O_LARGEFILE | os.O_WRONLY | os.O_CREAT | os.O_CLOEXEC | os.O_EXCL;
+        const fd = try os.openC(path, flags, file_mode);
+        return openHandle(fd);
+    }
+
+    pub fn openWriteNoClobberW(path_w: [*]const u16, file_mode: Mode) OpenError!File {
+        const handle = try windows.CreateFileW(
+            path_w,
+            windows.GENERIC_WRITE,
+            windows.FILE_SHARE_WRITE | windows.FILE_SHARE_READ | windows.FILE_SHARE_DELETE,
+            windows.CREATE_NEW,
+            windows.FILE_ATTRIBUTE_NORMAL,
+        );
+        return openHandle(handle);
+    }
+
+    pub fn openHandle(handle: os.fd_t) File {
+        return File{ .handle = handle };
+    }
+
+    /// Test for the existence of `path`.
+    /// `path` is UTF8-encoded.
+    pub fn exists(path: []const u8) AccessError!void {
+        return os.access(path, os.F_OK);
+    }
+
+    /// Same as `exists` except the parameter is null-terminated.
+    pub fn existsC(path: [*]const u8) AccessError!void {
+        return os.accessC(path, os.F_OK);
+    }
+
+    /// Same as `exists` except the parameter is null-terminated UTF16LE-encoded.
+    pub fn existsW(path: [*]const u16) AccessError!void {
+        return os.accessW(path, os.F_OK);
+    }
+
+    /// Upon success, the stream is in an uninitialized state. To continue using it,
+    /// you must use the open() function.
+    pub fn close(self: File) void {
+        os.close(self.handle);
+    }
+
+    /// Test whether the file refers to a terminal.
+    /// See also `supportsAnsiEscapeCodes`.
+    pub fn isTty(self: File) bool {
+        return os.isatty(self.handle);
+    }
+
+    /// Test whether ANSI escape codes will be treated as such.
+    pub fn supportsAnsiEscapeCodes(self: File) bool {
+        if (windows.is_the_target) {
+            return os.isCygwinPty(self.handle);
+        }
+        return self.isTty();
+    }
+
+    pub const SeekError = os.SeekError;
+
+    /// Repositions read/write file offset relative to the current offset.
+    pub fn seekBy(self: File, offset: i64) SeekError!void {
+        return os.lseek_CUR(self.handle, offset);
+    }
+
+    /// Repositions read/write file offset relative to the end.
+    pub fn seekFromEnd(self: File, offset: i64) SeekError!void {
+        return os.lseek_END(self.handle, offset);
+    }
+
+    /// Repositions read/write file offset relative to the beginning.
+    pub fn seekTo(self: File, offset: u64) SeekError!void {
+        return os.lseek_SET(self.handle, offset);
+    }
+
+    pub const GetPosError = os.SeekError || os.FStatError;
+
+    pub fn getPos(self: File) GetPosError!u64 {
+        return os.lseek_CUR_get(self.handle);
+    }
+
+    pub fn getEndPos(self: File) GetPosError!u64 {
+        if (windows.is_the_target and !builtin.link_libc) {
+            return windows.GetFileSizeEx(self.handle);
+        }
+        const stat = try os.fstat(self.handle);
+        return @bitCast(u64, stat.size);
+    }
+
+    pub const ModeError = os.FStatError;
+
+    pub fn mode(self: File) ModeError!Mode {
+        if (windows.is_the_target and !builtin.link_libc) {
+            return {};
+        }
+        const stat = try os.fstat(self.handle);
+        // TODO: we should be able to cast u16 to ModeError!u32, making this
+        // explicit cast not necessary
+        return Mode(stat.mode);
+    }
+
+    pub const ReadError = os.ReadError;
+
+    pub fn read(self: File, buffer: []u8) ReadError!usize {
+        return os.read(self.handle, buffer);
+    }
+
+    pub const WriteError = os.WriteError;
+
+    pub fn write(self: File, bytes: []const u8) WriteError!void {
+        return os.write(self.handle, bytes);
+    }
+
+    pub fn inStream(file: File) InStream {
+        return InStream{
+            .file = file,
+            .stream = InStream.Stream{ .readFn = InStream.readFn },
+        };
+    }
+
+    pub fn outStream(file: File) OutStream {
+        return OutStream{
+            .file = file,
+            .stream = OutStream.Stream{ .writeFn = OutStream.writeFn },
+        };
+    }
+
+    pub fn seekableStream(file: File) SeekableStream {
+        return SeekableStream{
+            .file = file,
+            .stream = SeekableStream.Stream{
+                .seekToFn = SeekableStream.seekToFn,
+                .seekByFn = SeekableStream.seekByFn,
+                .getPosFn = SeekableStream.getPosFn,
+                .getEndPosFn = SeekableStream.getEndPosFn,
+            },
+        };
+    }
+
+    /// Implementation of io.InStream trait for File
+    pub const InStream = struct {
+        file: File,
+        stream: Stream,
+
+        pub const Error = ReadError;
+        pub const Stream = io.InStream(Error);
+
+        fn readFn(in_stream: *Stream, buffer: []u8) Error!usize {
+            const self = @fieldParentPtr(InStream, "stream", in_stream);
+            return self.file.read(buffer);
+        }
+    };
+
+    /// Implementation of io.OutStream trait for File
+    pub const OutStream = struct {
+        file: File,
+        stream: Stream,
+
+        pub const Error = WriteError;
+        pub const Stream = io.OutStream(Error);
+
+        fn writeFn(out_stream: *Stream, bytes: []const u8) Error!void {
+            const self = @fieldParentPtr(OutStream, "stream", out_stream);
+            return self.file.write(bytes);
+        }
+    };
+
+    /// Implementation of io.SeekableStream trait for File
+    pub const SeekableStream = struct {
+        file: File,
+        stream: Stream,
+
+        pub const Stream = io.SeekableStream(SeekError, GetPosError);
+
+        pub fn seekToFn(seekable_stream: *Stream, pos: u64) SeekError!void {
+            const self = @fieldParentPtr(SeekableStream, "stream", seekable_stream);
+            return self.file.seekTo(pos);
+        }
+
+        pub fn seekByFn(seekable_stream: *Stream, amt: i64) SeekError!void {
+            const self = @fieldParentPtr(SeekableStream, "stream", seekable_stream);
+            return self.file.seekBy(amt);
+        }
+
+        pub fn getEndPosFn(seekable_stream: *Stream) GetPosError!u64 {
+            const self = @fieldParentPtr(SeekableStream, "stream", seekable_stream);
+            return self.file.getEndPos();
+        }
+
+        pub fn getPosFn(seekable_stream: *Stream) GetPosError!u64 {
+            const self = @fieldParentPtr(SeekableStream, "stream", seekable_stream);
+            return self.file.getPos();
+        }
+    };
+
+    pub fn stdout() !File {
+        if (windows.is_the_target) {
+            const handle = try windows.GetStdHandle(windows.STD_OUTPUT_HANDLE);
+            return openHandle(handle);
+        }
+        return openHandle(os.STDOUT_FILENO);
+    }
+
+    pub fn stderr() !File {
+        if (windows.is_the_target) {
+            const handle = try windows.GetStdHandle(windows.STD_ERROR_HANDLE);
+            return openHandle(handle);
+        }
+        return openHandle(os.STDERR_FILENO);
+    }
+
+    pub fn stdin() !File {
+        if (windows.is_the_target) {
+            const handle = try windows.GetStdHandle(windows.STD_INPUT_HANDLE);
+            return openHandle(handle);
+        }
+        return openHandle(os.STDIN_FILENO);
+    }
+};
std/os/get_app_data_dir.zig → std/fs/get_app_data_dir.zig
@@ -13,7 +13,7 @@ pub const GetAppDataDirError = error{
 /// TODO determine if we can remove the allocator requirement
 pub fn getAppDataDir(allocator: *mem.Allocator, appname: []const u8) GetAppDataDirError![]u8 {
     switch (builtin.os) {
-        builtin.Os.windows => {
+        .windows => {
             var dir_path_ptr: [*]u16 = undefined;
             switch (os.windows.SHGetKnownFolderPath(
                 &os.windows.FOLDERID_LocalAppData,
@@ -36,14 +36,14 @@ pub fn getAppDataDir(allocator: *mem.Allocator, appname: []const u8) GetAppDataD
                 else => return error.AppDataDirUnavailable,
             }
         },
-        builtin.Os.macosx => {
+        .macosx => {
             const home_dir = os.getEnvPosix("HOME") orelse {
                 // TODO look in /etc/passwd
                 return error.AppDataDirUnavailable;
             };
             return os.path.join(allocator, [][]const u8{ home_dir, "Library", "Application Support", appname });
         },
-        builtin.Os.linux, builtin.Os.freebsd, builtin.Os.netbsd => {
+        .linux, .freebsd, .netbsd => {
             const home_dir = os.getEnvPosix("HOME") orelse {
                 // TODO look in /etc/passwd
                 return error.AppDataDirUnavailable;
@@ -60,7 +60,7 @@ fn utf16lePtrSlice(ptr: [*]const u16) []const u16 {
     return ptr[0..index];
 }
 
-test "std.os.getAppDataDir" {
+test "getAppDataDir" {
     var buf: [512]u8 = undefined;
     const allocator = &std.heap.FixedBufferAllocator.init(buf[0..]).allocator;
 
std/os/path.zig → std/fs/path.zig
@@ -12,7 +12,6 @@ const math = std.math;
 const posix = os.posix;
 const windows = os.windows;
 const cstr = std.cstr;
-const windows_util = @import("windows/util.zig");
 
 pub const sep_windows = '\\';
 pub const sep_posix = '/';
@@ -105,7 +104,7 @@ fn testJoinPosix(paths: []const []const u8, expected: []const u8) void {
     testing.expectEqualSlices(u8, expected, actual);
 }
 
-test "os.path.join" {
+test "join" {
     testJoinWindows([][]const u8{ "c:\\a\\b", "c" }, "c:\\a\\b\\c");
     testJoinWindows([][]const u8{ "c:\\a\\b", "c" }, "c:\\a\\b\\c");
     testJoinWindows([][]const u8{ "c:\\a\\b\\", "c" }, "c:\\a\\b\\c");
@@ -164,7 +163,7 @@ pub fn isAbsolutePosix(path: []const u8) bool {
     return path[0] == sep_posix;
 }
 
-test "os.path.isAbsoluteWindows" {
+test "isAbsoluteWindows" {
     testIsAbsoluteWindows("/", true);
     testIsAbsoluteWindows("//", true);
     testIsAbsoluteWindows("//server", true);
@@ -186,7 +185,7 @@ test "os.path.isAbsoluteWindows" {
     testIsAbsoluteWindows("/usr/local", true);
 }
 
-test "os.path.isAbsolutePosix" {
+test "isAbsolutePosix" {
     testIsAbsolutePosix("/home/foo", true);
     testIsAbsolutePosix("/home/foo/..", true);
     testIsAbsolutePosix("bar/", false);
@@ -279,7 +278,7 @@ pub fn windowsParsePath(path: []const u8) WindowsPath {
     return relative_path;
 }
 
-test "os.path.windowsParsePath" {
+test "windowsParsePath" {
     {
         const parsed = windowsParsePath("//a/b");
         testing.expect(parsed.is_abs);
@@ -637,7 +636,7 @@ pub fn resolvePosix(allocator: *Allocator, paths: []const []const u8) ![]u8 {
     return allocator.shrink(result, result_index);
 }
 
-test "os.path.resolve" {
+test "resolve" {
     const cwd = try os.getCwdAlloc(debug.global_allocator);
     if (is_windows) {
         if (windowsParsePath(cwd).kind == WindowsPath.Kind.Drive) {
@@ -650,7 +649,7 @@ test "os.path.resolve" {
     }
 }
 
-test "os.path.resolveWindows" {
+test "resolveWindows" {
     if (is_windows) {
         const cwd = try os.getCwdAlloc(debug.global_allocator);
         const parsed_cwd = windowsParsePath(cwd);
@@ -693,7 +692,7 @@ test "os.path.resolveWindows" {
     testing.expect(mem.eql(u8, testResolveWindows([][]const u8{ "C:\\foo\\tmp.3\\", "..\\tmp.3\\cycles\\root.js" }), "C:\\foo\\tmp.3\\cycles\\root.js"));
 }
 
-test "os.path.resolvePosix" {
+test "resolvePosix" {
     testing.expect(mem.eql(u8, testResolvePosix([][]const u8{ "/a/b", "c" }), "/a/b/c"));
     testing.expect(mem.eql(u8, testResolvePosix([][]const u8{ "/a/b", "c", "//d", "e///" }), "/d/e"));
     testing.expect(mem.eql(u8, testResolvePosix([][]const u8{ "/a/b/c", "..", "../" }), "/a"));
@@ -784,7 +783,7 @@ pub fn dirnamePosix(path: []const u8) ?[]const u8 {
     return path[0..end_index];
 }
 
-test "os.path.dirnamePosix" {
+test "dirnamePosix" {
     testDirnamePosix("/a/b/c", "/a/b");
     testDirnamePosix("/a/b/c///", "/a/b");
     testDirnamePosix("/a", "/");
@@ -796,7 +795,7 @@ test "os.path.dirnamePosix" {
     testDirnamePosix("a//", null);
 }
 
-test "os.path.dirnameWindows" {
+test "dirnameWindows" {
     testDirnameWindows("c:\\", "c:\\");
     testDirnameWindows("c:\\foo", "c:\\");
     testDirnameWindows("c:\\foo\\", "c:\\");
@@ -909,7 +908,7 @@ pub fn basenameWindows(path: []const u8) []const u8 {
     return path[start_index + 1 .. end_index];
 }
 
-test "os.path.basename" {
+test "basename" {
     testBasename("", "");
     testBasename("/", "");
     testBasename("/dir/basename.ext", "basename.ext");
@@ -1090,7 +1089,7 @@ pub fn relativePosix(allocator: *Allocator, from: []const u8, to: []const u8) ![
     return []u8{};
 }
 
-test "os.path.relative" {
+test "relative" {
     testRelativeWindows("c:/blah\\blah", "d:/games", "D:\\games");
     testRelativeWindows("c:/aaaa/bbbb", "c:/aaaa", "..");
     testRelativeWindows("c:/aaaa/bbbb", "c:/cccc", "..\\..\\cccc");
@@ -1139,148 +1138,3 @@ fn testRelativeWindows(from: []const u8, to: []const u8, expected_output: []cons
     const result = relativeWindows(debug.global_allocator, from, to) catch unreachable;
     testing.expectEqualSlices(u8, expected_output, result);
 }
-
-pub const RealError = error{
-    FileNotFound,
-    AccessDenied,
-    NameTooLong,
-    NotSupported,
-    NotDir,
-    SymLinkLoop,
-    InputOutput,
-    FileTooBig,
-    IsDir,
-    ProcessFdQuotaExceeded,
-    SystemFdQuotaExceeded,
-    NoDevice,
-    SystemResources,
-    NoSpaceLeft,
-    FileSystem,
-    BadPathName,
-    DeviceBusy,
-
-    /// On Windows, file paths must be valid Unicode.
-    InvalidUtf8,
-
-    PathAlreadyExists,
-
-    Unexpected,
-};
-
-/// Call from Windows-specific code if you already have a UTF-16LE encoded, null terminated string.
-/// Otherwise use `real` or `realC`.
-pub fn realW(out_buffer: *[os.MAX_PATH_BYTES]u8, pathname: [*]const u16) RealError![]u8 {
-    const h_file = windows.CreateFileW(
-        pathname,
-        windows.GENERIC_READ,
-        windows.FILE_SHARE_READ,
-        null,
-        windows.OPEN_EXISTING,
-        windows.FILE_ATTRIBUTE_NORMAL,
-        null,
-    );
-    if (h_file == windows.INVALID_HANDLE_VALUE) {
-        const err = windows.GetLastError();
-        switch (err) {
-            windows.ERROR.FILE_NOT_FOUND => return error.FileNotFound,
-            windows.ERROR.ACCESS_DENIED => return error.AccessDenied,
-            windows.ERROR.FILENAME_EXCED_RANGE => return error.NameTooLong,
-            else => return os.unexpectedErrorWindows(err),
-        }
-    }
-    defer os.close(h_file);
-    var utf16le_buf: [windows_util.PATH_MAX_WIDE]u16 = undefined;
-    const casted_len = @intCast(windows.DWORD, utf16le_buf.len); // TODO shouldn't need this cast
-    const result = windows.GetFinalPathNameByHandleW(h_file, &utf16le_buf, casted_len, windows.VOLUME_NAME_DOS);
-    assert(result <= utf16le_buf.len);
-    if (result == 0) {
-        const err = windows.GetLastError();
-        switch (err) {
-            windows.ERROR.FILE_NOT_FOUND => return error.FileNotFound,
-            windows.ERROR.PATH_NOT_FOUND => return error.FileNotFound,
-            windows.ERROR.NOT_ENOUGH_MEMORY => return error.SystemResources,
-            windows.ERROR.FILENAME_EXCED_RANGE => return error.NameTooLong,
-            windows.ERROR.INVALID_PARAMETER => unreachable,
-            else => return os.unexpectedErrorWindows(err),
-        }
-    }
-    const utf16le_slice = utf16le_buf[0..result];
-
-    // windows returns \\?\ prepended to the path
-    // we strip it because nobody wants \\?\ prepended to their path
-    const prefix = []u16{ '\\', '\\', '?', '\\' };
-    const start_index = if (mem.startsWith(u16, utf16le_slice, prefix)) prefix.len else 0;
-
-    // Trust that Windows gives us valid UTF-16LE.
-    const end_index = std.unicode.utf16leToUtf8(out_buffer, utf16le_slice[start_index..]) catch unreachable;
-    return out_buffer[0..end_index];
-}
-
-/// See `real`
-/// Use this when you have a null terminated pointer path.
-pub fn realC(out_buffer: *[os.MAX_PATH_BYTES]u8, pathname: [*]const u8) RealError![]u8 {
-    switch (builtin.os) {
-        Os.windows => {
-            const pathname_w = try windows_util.cStrToPrefixedFileW(pathname);
-            return realW(out_buffer, pathname_w);
-        },
-        Os.freebsd, Os.netbsd, Os.macosx, Os.ios => {
-            // TODO instead of calling the libc function here, port the implementation to Zig
-            const err = posix.getErrno(posix.realpath(pathname, out_buffer));
-            switch (err) {
-                0 => return mem.toSlice(u8, out_buffer),
-                posix.EINVAL => unreachable,
-                posix.EBADF => unreachable,
-                posix.EFAULT => unreachable,
-                posix.EACCES => return error.AccessDenied,
-                posix.ENOENT => return error.FileNotFound,
-                posix.ENOTSUP => return error.NotSupported,
-                posix.ENOTDIR => return error.NotDir,
-                posix.ENAMETOOLONG => return error.NameTooLong,
-                posix.ELOOP => return error.SymLinkLoop,
-                posix.EIO => return error.InputOutput,
-                else => return os.unexpectedErrorPosix(err),
-            }
-        },
-        Os.linux => {
-            const fd = try os.posixOpenC(pathname, posix.O_PATH | posix.O_NONBLOCK | posix.O_CLOEXEC, 0);
-            defer os.close(fd);
-
-            var buf: ["/proc/self/fd/-2147483648\x00".len]u8 = undefined;
-            const proc_path = fmt.bufPrint(buf[0..], "/proc/self/fd/{}\x00", fd) catch unreachable;
-
-            return os.readLinkC(out_buffer, proc_path.ptr);
-        },
-        else => @compileError("TODO implement os.path.real for " ++ @tagName(builtin.os)),
-    }
-}
-
-/// Return the canonicalized absolute pathname.
-/// Expands all symbolic links and resolves references to `.`, `..`, and
-/// extra `/` characters in ::pathname.
-/// The return value is a slice of out_buffer, and not necessarily from the beginning.
-pub fn real(out_buffer: *[os.MAX_PATH_BYTES]u8, pathname: []const u8) RealError![]u8 {
-    switch (builtin.os) {
-        Os.windows => {
-            const pathname_w = try windows_util.sliceToPrefixedFileW(pathname);
-            return realW(out_buffer, &pathname_w);
-        },
-        Os.macosx, Os.ios, Os.linux, Os.freebsd, Os.netbsd => {
-            const pathname_c = try os.toPosixPath(pathname);
-            return realC(out_buffer, &pathname_c);
-        },
-        else => @compileError("Unsupported OS"),
-    }
-}
-
-/// `real`, except caller must free the returned memory.
-pub fn realAlloc(allocator: *Allocator, pathname: []const u8) ![]u8 {
-    var buf: [os.MAX_PATH_BYTES]u8 = undefined;
-    return mem.dupe(allocator, u8, try real(&buf, pathname));
-}
-
-test "os.path.real" {
-    // at least call it so it gets compiled
-    var buf: [os.MAX_PATH_BYTES]u8 = undefined;
-    testing.expectError(error.FileNotFound, real(&buf, "definitely_bogus_does_not_exist1234"));
-}
std/io/seekable_stream.zig
@@ -8,7 +8,7 @@ pub fn SeekableStream(comptime SeekErrorType: type, comptime GetSeekPosErrorType
         pub const GetSeekPosError = GetSeekPosErrorType;
 
         seekToFn: fn (self: *Self, pos: u64) SeekError!void,
-        seekForwardFn: fn (self: *Self, pos: i64) SeekError!void,
+        seekByFn: fn (self: *Self, pos: i64) SeekError!void,
 
         getPosFn: fn (self: *Self) GetSeekPosError!u64,
         getEndPosFn: fn (self: *Self) GetSeekPosError!u64,
@@ -17,8 +17,8 @@ pub fn SeekableStream(comptime SeekErrorType: type, comptime GetSeekPosErrorType
             return self.seekToFn(self, pos);
         }
 
-        pub fn seekForward(self: *Self, amt: i64) SeekError!void {
-            return self.seekForwardFn(self, amt);
+        pub fn seekBy(self: *Self, amt: i64) SeekError!void {
+            return self.seekByFn(self, amt);
         }
 
         pub fn getEndPos(self: *Self) GetSeekPosError!u64 {
@@ -52,7 +52,7 @@ pub const SliceSeekableInStream = struct {
             .stream = Stream{ .readFn = readFn },
             .seekable_stream = SeekableInStream{
                 .seekToFn = seekToFn,
-                .seekForwardFn = seekForwardFn,
+                .seekByFn = seekByFn,
                 .getEndPosFn = getEndPosFn,
                 .getPosFn = getPosFn,
             },
@@ -77,7 +77,7 @@ pub const SliceSeekableInStream = struct {
         self.pos = usize_pos;
     }
 
-    fn seekForwardFn(in_stream: *SeekableInStream, amt: i64) SeekError!void {
+    fn seekByFn(in_stream: *SeekableInStream, amt: i64) SeekError!void {
         const self = @fieldParentPtr(Self, "seekable_stream", in_stream);
 
         if (amt < 0) {
std/os/linux/sys.zig
@@ -320,8 +320,21 @@ pub fn close(fd: i32) usize {
     return syscall1(SYS_close, @bitCast(usize, isize(fd)));
 }
 
-pub fn lseek(fd: i32, offset: isize, ref_pos: usize) usize {
-    return syscall3(SYS_lseek, @bitCast(usize, isize(fd)), @bitCast(usize, offset), ref_pos);
+/// Can only be called on 32 bit systems. For 64 bit see `lseek`.
+pub fn llseek(fd: i32, offset: u64, result: ?*u64, whence: usize) usize {
+    return syscall5(
+        SYS__llseek,
+        @bitCast(usize, isize(fd)),
+        @truncate(usize, offset >> 32),
+        @truncate(usize, offset),
+        @ptrToInt(result),
+        whence,
+    );
+}
+
+/// Can only be called on 64 bit systems. For 32 bit see `llseek`.
+pub fn lseek(fd: i32, offset: i64, whence: usize) usize {
+    return syscall3(SYS_lseek, @bitCast(usize, isize(fd)), @bitCast(usize, offset), whence);
 }
 
 pub fn exit(status: i32) noreturn {
std/os/file.zig
@@ -1,450 +0,0 @@
-const std = @import("../std.zig");
-const builtin = @import("builtin");
-const os = std.os;
-const io = std.io;
-const mem = std.mem;
-const math = std.math;
-const assert = std.debug.assert;
-const posix = os.posix;
-const windows = os.windows;
-const Os = builtin.Os;
-const windows_util = @import("windows/util.zig");
-const maxInt = std.math.maxInt;
-
-const is_posix = builtin.os != builtin.Os.windows;
-const is_windows = builtin.os == builtin.Os.windows;
-
-pub const File = struct {
-    /// The OS-specific file descriptor or file handle.
-    handle: posix.fd_t,
-
-    pub const Mode = switch (builtin.os) {
-        Os.windows => void,
-        else => u32,
-    };
-
-    pub const default_mode = switch (builtin.os) {
-        Os.windows => {},
-        else => 0o666,
-    };
-
-    pub const OpenError = os.WindowsOpenError || os.PosixOpenError;
-
-    /// `openRead` except with a null terminated path
-    pub fn openReadC(path: [*]const u8) OpenError!File {
-        if (is_posix) {
-            const flags = posix.O_LARGEFILE | posix.O_RDONLY;
-            const fd = try os.posixOpenC(path, flags, 0);
-            return openHandle(fd);
-        }
-        if (is_windows) {
-            return openRead(mem.toSliceConst(u8, path));
-        }
-        @compileError("Unsupported OS");
-    }
-
-    /// Call close to clean up.
-    pub fn openRead(path: []const u8) OpenError!File {
-        if (is_posix) {
-            const path_c = try os.toPosixPath(path);
-            return openReadC(&path_c);
-        }
-        if (is_windows) {
-            const path_w = try windows_util.sliceToPrefixedFileW(path);
-            return openReadW(&path_w);
-        }
-        @compileError("Unsupported OS");
-    }
-
-    pub fn openReadW(path_w: [*]const u16) OpenError!File {
-        const handle = try os.windowsOpenW(
-            path_w,
-            windows.GENERIC_READ,
-            windows.FILE_SHARE_READ,
-            windows.OPEN_EXISTING,
-            windows.FILE_ATTRIBUTE_NORMAL,
-        );
-        return openHandle(handle);
-    }
-
-    /// Calls `openWriteMode` with os.File.default_mode for the mode.
-    pub fn openWrite(path: []const u8) OpenError!File {
-        return openWriteMode(path, os.File.default_mode);
-    }
-
-    /// If the path does not exist it will be created.
-    /// If a file already exists in the destination it will be truncated.
-    /// Call close to clean up.
-    pub fn openWriteMode(path: []const u8, file_mode: Mode) OpenError!File {
-        if (is_posix) {
-            const flags = posix.O_LARGEFILE | posix.O_WRONLY | posix.O_CREAT | posix.O_CLOEXEC | posix.O_TRUNC;
-            const fd = try os.posixOpen(path, flags, file_mode);
-            return openHandle(fd);
-        } else if (is_windows) {
-            const path_w = try windows_util.sliceToPrefixedFileW(path);
-            return openWriteModeW(&path_w, file_mode);
-        } else {
-            @compileError("TODO implement openWriteMode for this OS");
-        }
-    }
-
-    pub fn openWriteModeW(path_w: [*]const u16, file_mode: Mode) OpenError!File {
-        const handle = try os.windowsOpenW(
-            path_w,
-            windows.GENERIC_WRITE,
-            windows.FILE_SHARE_WRITE | windows.FILE_SHARE_READ | windows.FILE_SHARE_DELETE,
-            windows.CREATE_ALWAYS,
-            windows.FILE_ATTRIBUTE_NORMAL,
-        );
-        return openHandle(handle);
-    }
-
-    /// If the path does not exist it will be created.
-    /// If a file already exists in the destination this returns OpenError.PathAlreadyExists
-    /// Call close to clean up.
-    pub fn openWriteNoClobber(path: []const u8, file_mode: Mode) OpenError!File {
-        if (is_posix) {
-            const path_c = try os.toPosixPath(path);
-            return openWriteNoClobberC(&path_c, file_mode);
-        } else if (is_windows) {
-            const path_w = try windows_util.sliceToPrefixedFileW(path);
-            return openWriteNoClobberW(&path_w, file_mode);
-        } else {
-            @compileError("TODO implement openWriteMode for this OS");
-        }
-    }
-
-    pub fn openWriteNoClobberC(path: [*]const u8, file_mode: Mode) OpenError!File {
-        if (is_posix) {
-            const flags = posix.O_LARGEFILE | posix.O_WRONLY | posix.O_CREAT | posix.O_CLOEXEC | posix.O_EXCL;
-            const fd = try os.posixOpenC(path, flags, file_mode);
-            return openHandle(fd);
-        } else if (is_windows) {
-            const path_w = try windows_util.cStrToPrefixedFileW(path);
-            return openWriteNoClobberW(&path_w, file_mode);
-        } else {
-            @compileError("TODO implement openWriteMode for this OS");
-        }
-    }
-
-    pub fn openWriteNoClobberW(path_w: [*]const u16, file_mode: Mode) OpenError!File {
-        const handle = try os.windowsOpenW(
-            path_w,
-            windows.GENERIC_WRITE,
-            windows.FILE_SHARE_WRITE | windows.FILE_SHARE_READ | windows.FILE_SHARE_DELETE,
-            windows.CREATE_NEW,
-            windows.FILE_ATTRIBUTE_NORMAL,
-        );
-        return openHandle(handle);
-    }
-
-    pub fn openHandle(handle: posix.fd_t) File {
-        return File{ .handle = handle };
-    }
-
-    /// Test for the existence of `path`.
-    /// `path` is UTF8-encoded.
-    pub fn exists(path: []const u8) AccessError!void {
-        return posix.access(path, posix.F_OK);
-    }
-
-    /// Same as `exists` except the parameter is null-terminated UTF16LE-encoded.
-    pub fn existsW(path: [*]const u16) AccessError!void {
-        return posix.accessW(path, posix.F_OK);
-    }
-
-    /// Same as `exists` except the parameter is null-terminated.
-    pub fn existsC(path: [*]const u8) AccessError!void {
-        return posix.accessC(path, posix.F_OK);
-    }
-
-    /// Upon success, the stream is in an uninitialized state. To continue using it,
-    /// you must use the open() function.
-    pub fn close(self: File) void {
-        os.close(self.handle);
-    }
-
-    /// Test whether the file refers to a terminal.
-    /// See also `supportsAnsiEscapeCodes`.
-    pub fn isTty(self: File) bool {
-        return posix.isatty(self.handle);
-    }
-
-    /// Test whether ANSI escape codes will be treated as such.
-    pub fn supportsAnsiEscapeCodes(self: File) bool {
-        if (windows.is_the_target) {
-            return posix.isCygwinPty(self.handle);
-        }
-        return self.isTty();
-    }
-
-    pub const SeekError = error{
-        /// TODO make this error impossible to get
-        Overflow,
-        Unseekable,
-        Unexpected,
-    };
-
-    pub fn seekForward(self: File, amount: i64) SeekError!void {
-        switch (builtin.os) {
-            Os.linux, Os.macosx, Os.ios, Os.freebsd, Os.netbsd => {
-                const iamount = try math.cast(isize, amount);
-                const result = posix.lseek(self.handle, iamount, posix.SEEK_CUR);
-                const err = posix.getErrno(result);
-                if (err > 0) {
-                    return switch (err) {
-                        // We do not make this an error code because if you get EBADF it's always a bug,
-                        // since the fd could have been reused.
-                        posix.EBADF => unreachable,
-                        posix.EINVAL => error.Unseekable,
-                        posix.EOVERFLOW => error.Unseekable,
-                        posix.ESPIPE => error.Unseekable,
-                        posix.ENXIO => error.Unseekable,
-                        else => os.unexpectedErrorPosix(err),
-                    };
-                }
-            },
-            Os.windows => {
-                if (windows.SetFilePointerEx(self.handle, amount, null, windows.FILE_CURRENT) == 0) {
-                    const err = windows.GetLastError();
-                    return switch (err) {
-                        windows.ERROR.INVALID_PARAMETER => unreachable,
-                        else => os.unexpectedErrorWindows(err),
-                    };
-                }
-            },
-            else => @compileError("unsupported OS"),
-        }
-    }
-
-    pub fn seekTo(self: File, pos: u64) SeekError!void {
-        switch (builtin.os) {
-            Os.linux, Os.macosx, Os.ios, Os.freebsd, Os.netbsd => {
-                const ipos = try math.cast(isize, pos);
-                const result = posix.lseek(self.handle, ipos, posix.SEEK_SET);
-                const err = posix.getErrno(result);
-                if (err > 0) {
-                    return switch (err) {
-                        // We do not make this an error code because if you get EBADF it's always a bug,
-                        // since the fd could have been reused.
-                        posix.EBADF => unreachable,
-                        posix.EINVAL => error.Unseekable,
-                        posix.EOVERFLOW => error.Unseekable,
-                        posix.ESPIPE => error.Unseekable,
-                        posix.ENXIO => error.Unseekable,
-                        else => os.unexpectedErrorPosix(err),
-                    };
-                }
-            },
-            Os.windows => {
-                const ipos = try math.cast(isize, pos);
-                if (windows.SetFilePointerEx(self.handle, ipos, null, windows.FILE_BEGIN) == 0) {
-                    const err = windows.GetLastError();
-                    return switch (err) {
-                        windows.ERROR.INVALID_PARAMETER => unreachable,
-                        windows.ERROR.INVALID_HANDLE => unreachable,
-                        else => os.unexpectedErrorWindows(err),
-                    };
-                }
-            },
-            else => @compileError("unsupported OS: " ++ @tagName(builtin.os)),
-        }
-    }
-
-    pub const GetSeekPosError = error{
-        SystemResources,
-        Unseekable,
-        Unexpected,
-    };
-
-    pub fn getPos(self: File) GetSeekPosError!u64 {
-        switch (builtin.os) {
-            Os.linux, Os.macosx, Os.ios, Os.freebsd, Os.netbsd => {
-                const result = posix.lseek(self.handle, 0, posix.SEEK_CUR);
-                const err = posix.getErrno(result);
-                if (err > 0) {
-                    return switch (err) {
-                        // We do not make this an error code because if you get EBADF it's always a bug,
-                        // since the fd could have been reused.
-                        posix.EBADF => unreachable,
-                        posix.EINVAL => error.Unseekable,
-                        posix.EOVERFLOW => error.Unseekable,
-                        posix.ESPIPE => error.Unseekable,
-                        posix.ENXIO => error.Unseekable,
-                        else => os.unexpectedErrorPosix(err),
-                    };
-                }
-                return u64(result);
-            },
-            Os.windows => {
-                var pos: windows.LARGE_INTEGER = undefined;
-                if (windows.SetFilePointerEx(self.handle, 0, &pos, windows.FILE_CURRENT) == 0) {
-                    const err = windows.GetLastError();
-                    return switch (err) {
-                        windows.ERROR.INVALID_PARAMETER => unreachable,
-                        else => os.unexpectedErrorWindows(err),
-                    };
-                }
-
-                return @intCast(u64, pos);
-            },
-            else => @compileError("unsupported OS"),
-        }
-    }
-
-    pub fn getEndPos(self: File) GetSeekPosError!u64 {
-        if (is_posix) {
-            const stat = try os.posixFStat(self.handle);
-            return @intCast(u64, stat.size);
-        } else if (is_windows) {
-            var file_size: windows.LARGE_INTEGER = undefined;
-            if (windows.GetFileSizeEx(self.handle, &file_size) == 0) {
-                const err = windows.GetLastError();
-                return switch (err) {
-                    else => os.unexpectedErrorWindows(err),
-                };
-            }
-            return @intCast(u64, file_size);
-        } else {
-            @compileError("TODO support getEndPos on this OS");
-        }
-    }
-
-    pub const ModeError = error{
-        SystemResources,
-        Unexpected,
-    };
-
-    pub fn mode(self: File) ModeError!Mode {
-        if (is_posix) {
-            var stat: posix.Stat = undefined;
-            const err = posix.getErrno(posix.fstat(self.handle, &stat));
-            if (err > 0) {
-                return switch (err) {
-                    // We do not make this an error code because if you get EBADF it's always a bug,
-                    // since the fd could have been reused.
-                    posix.EBADF => unreachable,
-                    posix.ENOMEM => error.SystemResources,
-                    else => os.unexpectedErrorPosix(err),
-                };
-            }
-
-            // TODO: we should be able to cast u16 to ModeError!u32, making this
-            // explicit cast not necessary
-            return Mode(stat.mode);
-        } else if (is_windows) {
-            return {};
-        } else {
-            @compileError("TODO support file mode on this OS");
-        }
-    }
-
-    pub const ReadError = posix.ReadError;
-
-    pub fn read(self: File, buffer: []u8) ReadError!usize {
-        return posix.read(self.handle, buffer);
-    }
-
-    pub const WriteError = posix.WriteError;
-
-    pub fn write(self: File, bytes: []const u8) WriteError!void {
-        return posix.write(self.handle, bytes);
-    }
-
-    pub fn inStream(file: File) InStream {
-        return InStream{
-            .file = file,
-            .stream = InStream.Stream{ .readFn = InStream.readFn },
-        };
-    }
-
-    pub fn outStream(file: File) OutStream {
-        return OutStream{
-            .file = file,
-            .stream = OutStream.Stream{ .writeFn = OutStream.writeFn },
-        };
-    }
-
-    pub fn seekableStream(file: File) SeekableStream {
-        return SeekableStream{
-            .file = file,
-            .stream = SeekableStream.Stream{
-                .seekToFn = SeekableStream.seekToFn,
-                .seekForwardFn = SeekableStream.seekForwardFn,
-                .getPosFn = SeekableStream.getPosFn,
-                .getEndPosFn = SeekableStream.getEndPosFn,
-            },
-        };
-    }
-
-    /// Implementation of io.InStream trait for File
-    pub const InStream = struct {
-        file: File,
-        stream: Stream,
-
-        pub const Error = ReadError;
-        pub const Stream = io.InStream(Error);
-
-        fn readFn(in_stream: *Stream, buffer: []u8) Error!usize {
-            const self = @fieldParentPtr(InStream, "stream", in_stream);
-            return self.file.read(buffer);
-        }
-    };
-
-    /// Implementation of io.OutStream trait for File
-    pub const OutStream = struct {
-        file: File,
-        stream: Stream,
-
-        pub const Error = WriteError;
-        pub const Stream = io.OutStream(Error);
-
-        fn writeFn(out_stream: *Stream, bytes: []const u8) Error!void {
-            const self = @fieldParentPtr(OutStream, "stream", out_stream);
-            return self.file.write(bytes);
-        }
-    };
-
-    /// Implementation of io.SeekableStream trait for File
-    pub const SeekableStream = struct {
-        file: File,
-        stream: Stream,
-
-        pub const Stream = io.SeekableStream(SeekError, GetSeekPosError);
-
-        pub fn seekToFn(seekable_stream: *Stream, pos: u64) SeekError!void {
-            const self = @fieldParentPtr(SeekableStream, "stream", seekable_stream);
-            return self.file.seekTo(pos);
-        }
-
-        pub fn seekForwardFn(seekable_stream: *Stream, amt: i64) SeekError!void {
-            const self = @fieldParentPtr(SeekableStream, "stream", seekable_stream);
-            return self.file.seekForward(amt);
-        }
-
-        pub fn getEndPosFn(seekable_stream: *Stream) GetSeekPosError!u64 {
-            const self = @fieldParentPtr(SeekableStream, "stream", seekable_stream);
-            return self.file.getEndPos();
-        }
-
-        pub fn getPosFn(seekable_stream: *Stream) GetSeekPosError!u64 {
-            const self = @fieldParentPtr(SeekableStream, "stream", seekable_stream);
-            return self.file.getPos();
-        }
-    };
-
-    pub fn stdout() !File {
-        const handle = try posix.GetStdHandle(posix.STD_OUTPUT_HANDLE);
-        return openHandle(handle);
-    }
-
-    pub fn stderr() !File {
-        const handle = try posix.GetStdHandle(posix.STD_ERROR_HANDLE);
-        return openHandle(handle);
-    }
-
-    pub fn stdin() !File {
-        const handle = try posix.GetStdHandle(posix.STD_INPUT_HANDLE);
-        return openHandle(handle);
-    }
-};
std/os/get_user_id.zig
@@ -1,104 +0,0 @@
-const builtin = @import("builtin");
-const Os = builtin.Os;
-const os = @import("../os.zig");
-const io = @import("../io.zig");
-
-pub const UserInfo = struct {
-    uid: u32,
-    gid: u32,
-};
-
-/// POSIX function which gets a uid from username.
-pub fn getUserInfo(name: []const u8) !UserInfo {
-    return switch (builtin.os) {
-        Os.linux, Os.macosx, Os.ios, Os.freebsd, Os.netbsd => posixGetUserInfo(name),
-        else => @compileError("Unsupported OS"),
-    };
-}
-
-const State = enum {
-    Start,
-    WaitForNextLine,
-    SkipPassword,
-    ReadUserId,
-    ReadGroupId,
-};
-
-// TODO this reads /etc/passwd. But sometimes the user/id mapping is in something else
-// like NIS, AD, etc. See `man nss` or look at an strace for `id myuser`.
-
-pub fn posixGetUserInfo(name: []const u8) !UserInfo {
-    var in_stream = try io.InStream.open("/etc/passwd", null);
-    defer in_stream.close();
-
-    var buf: [os.page_size]u8 = undefined;
-    var name_index: usize = 0;
-    var state = State.Start;
-    var uid: u32 = 0;
-    var gid: u32 = 0;
-
-    while (true) {
-        const amt_read = try in_stream.read(buf[0..]);
-        for (buf[0..amt_read]) |byte| {
-            switch (state) {
-                State.Start => switch (byte) {
-                    ':' => {
-                        state = if (name_index == name.len) State.SkipPassword else State.WaitForNextLine;
-                    },
-                    '\n' => return error.CorruptPasswordFile,
-                    else => {
-                        if (name_index == name.len or name[name_index] != byte) {
-                            state = State.WaitForNextLine;
-                        }
-                        name_index += 1;
-                    },
-                },
-                State.WaitForNextLine => switch (byte) {
-                    '\n' => {
-                        name_index = 0;
-                        state = State.Start;
-                    },
-                    else => continue,
-                },
-                State.SkipPassword => switch (byte) {
-                    '\n' => return error.CorruptPasswordFile,
-                    ':' => {
-                        state = State.ReadUserId;
-                    },
-                    else => continue,
-                },
-                State.ReadUserId => switch (byte) {
-                    ':' => {
-                        state = State.ReadGroupId;
-                    },
-                    '\n' => return error.CorruptPasswordFile,
-                    else => {
-                        const digit = switch (byte) {
-                            '0'...'9' => byte - '0',
-                            else => return error.CorruptPasswordFile,
-                        };
-                        if (@mulWithOverflow(u32, uid, 10, *uid)) return error.CorruptPasswordFile;
-                        if (@addWithOverflow(u32, uid, digit, *uid)) return error.CorruptPasswordFile;
-                    },
-                },
-                State.ReadGroupId => switch (byte) {
-                    '\n', ':' => {
-                        return UserInfo{
-                            .uid = uid,
-                            .gid = gid,
-                        };
-                    },
-                    else => {
-                        const digit = switch (byte) {
-                            '0'...'9' => byte - '0',
-                            else => return error.CorruptPasswordFile,
-                        };
-                        if (@mulWithOverflow(u32, gid, 10, *gid)) return error.CorruptPasswordFile;
-                        if (@addWithOverflow(u32, gid, digit, *gid)) return error.CorruptPasswordFile;
-                    },
-                },
-            }
-        }
-        if (amt_read < buf.len) return error.UserNotFound;
-    }
-}
std/os/posix.zig
@@ -1,2307 +0,0 @@
-// This is the "Zig-flavored POSIX" API layer.
-// The purpose is not to match POSIX as closely as possible. Instead,
-// the goal is to provide a very specific layer of abstraction:
-// * Implement the POSIX functions, types, and definitions where possible,
-//   using lower-level target-specific API.
-// * When null-terminated byte buffers are required, provide APIs which accept
-//   slices as well as APIs which accept null-terminated byte buffers. Same goes
-//   for UTF-16LE encoding.
-// * Convert "errno"-style error codes into Zig errors.
-// * Implement the OS-specific functions, types, and definitions that the Zig
-//   standard library needs, at the same API abstraction layer as outlined above.
-//   For example kevent() and getrandom(). Windows-specific functions are separate,
-//   in `std.os.windows`.
-// * When there exists a corresponding libc function and linking libc, the libc
-//   implementation is used. Exceptions are made for known buggy areas of libc.
-//   On Linux libc can be side-stepped by using `std.os.linux.sys`.
-// Note: The Zig standard library does not support POSIX thread cancellation, and
-// in general EINTR is handled by trying again.
-
-const std = @import("../std.zig");
-const builtin = @import("builtin");
-const assert = std.debug.assert;
-const os = @import("../os.zig");
-const system = os.system;
-const mem = std.mem;
-const BufMap = std.BufMap;
-const Allocator = mem.Allocator;
-const windows = os.windows;
-const kernel32 = windows.kernel32;
-const wasi = os.wasi;
-const linux = os.linux;
-const testing = std.testing;
-
-pub use system.posix;
-
-/// See also `getenv`.
-pub var environ: [][*]u8 = undefined;
-
-/// To obtain errno, call this function with the return value of the
-/// system function call. For some systems this will obtain the value directly
-/// from the return code; for others it will use a thread-local errno variable.
-/// Therefore, this function only returns a well-defined value when it is called
-/// directly after the system function call which one wants to learn the errno
-/// value of.
-pub const errno = system.getErrno;
-
-/// Closes the file descriptor.
-/// This function is not capable of returning any indication of failure. An
-/// application which wants to ensure writes have succeeded before closing
-/// must call `fsync` before `close`.
-/// Note: The Zig standard library does not support POSIX thread cancellation.
-pub fn close(fd: fd_t) void {
-    if (windows.is_the_target and !builtin.link_libc) {
-        assert(kernel32.CloseHandle(fd) != 0);
-        return;
-    }
-    if (wasi.is_the_target) {
-        switch (wasi.fd_close(fd)) {
-            0 => return,
-            else => |err| return unexpectedErrno(err),
-        }
-    }
-    switch (errno(system.close(fd))) {
-        EBADF => unreachable, // Always a race condition.
-        EINTR => return, // This is still a success. See https://github.com/ziglang/zig/issues/2425
-        else => return,
-    }
-}
-
-pub const GetRandomError = error{};
-
-/// Obtain a series of random bytes. These bytes can be used to seed user-space
-/// random number generators or for cryptographic purposes.
-/// When linking against libc, this calls the
-/// appropriate OS-specific library call. Otherwise it uses the zig standard
-/// library implementation.
-pub fn getrandom(buf: []u8) GetRandomError!void {
-    if (windows.is_the_target) {
-        // Call RtlGenRandom() instead of CryptGetRandom() on Windows
-        // https://github.com/rust-lang-nursery/rand/issues/111
-        // https://bugzilla.mozilla.org/show_bug.cgi?id=504270
-        if (windows.advapi32.RtlGenRandom(buf.ptr, buf.len) == 0) {
-            switch (kernel32.GetLastError()) {
-                else => |err| return windows.unexpectedError(err),
-            }
-        }
-        return;
-    }
-    if (linux.is_the_target) {
-        while (true) {
-            switch (errno(system.getrandom(buf.ptr, buf.len, 0))) {
-                0 => return,
-                EINVAL => unreachable,
-                EFAULT => unreachable,
-                EINTR => continue,
-                ENOSYS => return getRandomBytesDevURandom(buf),
-                else => |err| return unexpectedErrno(err),
-            }
-        }
-    }
-    if (wasi.is_the_target) {
-        switch (os.wasi.random_get(buf.ptr, buf.len)) {
-            0 => return,
-            else => |err| return unexpectedErrno(err),
-        }
-    }
-    return getRandomBytesDevURandom(buf);
-}
-
-fn getRandomBytesDevURandom(buf: []u8) !void {
-    const fd = try openC(c"/dev/urandom", O_RDONLY | O_CLOEXEC, 0);
-    defer close(fd);
-
-    const stream = &os.File.openHandle(fd).inStream().stream;
-    stream.readNoEof(buf) catch return error.Unexpected;
-}
-
-test "os.getRandomBytes" {
-    var buf_a: [50]u8 = undefined;
-    var buf_b: [50]u8 = undefined;
-    try getRandomBytes(&buf_a);
-    try getRandomBytes(&buf_b);
-    // If this test fails the chance is significantly higher that there is a bug than
-    // that two sets of 50 bytes were equal.
-    testing.expect(!mem.eql(u8, buf_a, buf_b));
-}
-
-/// Causes abnormal process termination.
-/// If linking against libc, this calls the abort() libc function. Otherwise
-/// it raises SIGABRT followed by SIGKILL and finally lo
-pub fn abort() noreturn {
-    @setCold(true);
-    if (builtin.link_libc) {
-        system.abort();
-    }
-    if (windows.is_the_target) {
-        if (builtin.mode == .Debug) {
-            @breakpoint();
-        }
-        windows.ExitProcess(3);
-    }
-    if (builtin.os == .uefi) {
-        // TODO there must be a better thing to do here than loop forever
-        while (true) {}
-    }
-
-    raise(SIGABRT);
-
-    // TODO the rest of the implementation of abort() from musl libc here
-
-    raise(SIGKILL);
-    exit(127);
-}
-
-pub const RaiseError = error{};
-
-pub fn raise(sig: u8) RaiseError!void {
-    if (builtin.link_libc) {
-        switch (errno(system.raise(sig))) {
-            0 => return,
-            else => |err| return unexpectedErrno(err),
-        }
-    }
-
-    if (wasi.is_the_target) {
-        switch (wasi.proc_raise(SIGABRT)) {
-            0 => return,
-            else => |err| return unexpectedErrno(err),
-        }
-    }
-
-    if (windows.is_the_target) {
-        @compileError("TODO implement std.posix.raise for Windows");
-    }
-
-    var set: system.sigset_t = undefined;
-    system.blockAppSignals(&set);
-    const tid = system.syscall0(system.SYS_gettid);
-    const rc = system.syscall2(system.SYS_tkill, tid, sig);
-    system.restoreSignals(&set);
-    switch (errno(rc)) {
-        0 => return,
-        else => |err| return unexpectedErrno(err),
-    }
-}
-
-/// Exits the program cleanly with the specified status code.
-pub fn exit(status: u8) noreturn {
-    if (builtin.link_libc) {
-        system.exit(status);
-    }
-    if (windows.is_the_target) {
-        windows.ExitProcess(status);
-    }
-    if (wasi.is_the_target) {
-        wasi.proc_exit(status);
-    }
-    if (linux.is_the_target and !builtin.single_threaded) {
-        linux.exit_group(status);
-    }
-    system.exit(status);
-}
-
-pub const ReadError = error{
-    InputOutput,
-    SystemResources,
-    IsDir,
-    OperationAborted,
-    BrokenPipe,
-    Unexpected,
-};
-
-/// Returns the number of bytes that were read, which can be less than
-/// buf.len. If 0 bytes were read, that means EOF.
-/// This function is for blocking file descriptors only. For non-blocking, see
-/// `readAsync`.
-pub fn read(fd: fd_t, buf: []u8) ReadError!usize {
-    if (windows.is_the_target and !builtin.link_libc) {
-        var index: usize = 0;
-        while (index < buffer.len) {
-            const want_read_count = @intCast(windows.DWORD, math.min(windows.DWORD(math.maxInt(windows.DWORD)), buffer.len - index));
-            var amt_read: windows.DWORD = undefined;
-            if (windows.ReadFile(fd, buffer.ptr + index, want_read_count, &amt_read, null) == 0) {
-                switch (windows.GetLastError()) {
-                    windows.ERROR.OPERATION_ABORTED => continue,
-                    windows.ERROR.BROKEN_PIPE => return index,
-                    else => |err| return windows.unexpectedError(err),
-                }
-            }
-            if (amt_read == 0) return index;
-            index += amt_read;
-        }
-        return index;
-    }
-
-    if (wasi.is_the_target and !builtin.link_libc) {
-        const iovs = [1]was.iovec_t{wasi.iovec_t{
-            .buf = buf.ptr,
-            .buf_len = buf.len,
-        }};
-
-        var nread: usize = undefined;
-        switch (fd_read(fd, &iovs, iovs.len, &nread)) {
-            0 => return nread,
-            else => |err| return unexpectedErrno(err),
-        }
-    }
-
-    // Linux can return EINVAL when read amount is > 0x7ffff000
-    // See https://github.com/ziglang/zig/pull/743#issuecomment-363158274
-    const max_buf_len = 0x7ffff000;
-
-    var index: usize = 0;
-    while (index < buf.len) {
-        const want_to_read = math.min(buf.len - index, usize(max_buf_len));
-        const rc = system.read(fd, buf.ptr + index, want_to_read);
-        switch (errno(rc)) {
-            0 => {
-                index += rc;
-                if (rc == want_to_read) continue;
-                // Read returned less than buf.len.
-                return index;
-            },
-            EINTR => continue,
-            EINVAL => unreachable,
-            EFAULT => unreachable,
-            EAGAIN => unreachable, // This function is for blocking reads.
-            EBADF => unreachable, // Always a race condition.
-            EIO => return error.InputOutput,
-            EISDIR => return error.IsDir,
-            ENOBUFS => return error.SystemResources,
-            ENOMEM => return error.SystemResources,
-            else => |err| return unexpectedErrno(err),
-        }
-    }
-    return index;
-}
-
-/// Number of bytes read is returned. Upon reading end-of-file, zero is returned.
-/// This function is for blocking file descriptors only. For non-blocking, see
-/// `preadvAsync`.
-pub fn preadv(fd: fd_t, iov: [*]const iovec, count: usize, offset: u64) ReadError!usize {
-    if (os.darwin.is_the_target) {
-        // Darwin does not have preadv but it does have pread.
-        var off: usize = 0;
-        var iov_i: usize = 0;
-        var inner_off: usize = 0;
-        while (true) {
-            const v = iov[iov_i];
-            const rc = darwin.pread(fd, v.iov_base + inner_off, v.iov_len - inner_off, offset + off);
-            const err = darwin.getErrno(rc);
-            switch (err) {
-                0 => {
-                    off += rc;
-                    inner_off += rc;
-                    if (inner_off == v.iov_len) {
-                        iov_i += 1;
-                        inner_off = 0;
-                        if (iov_i == count) {
-                            return off;
-                        }
-                    }
-                    if (rc == 0) return off; // EOF
-                    continue;
-                },
-                EINTR => continue,
-                EINVAL => unreachable,
-                EFAULT => unreachable,
-                ESPIPE => unreachable, // fd is not seekable
-                EAGAIN => unreachable, // This function is for blocking reads.
-                EBADF => unreachable, // always a race condition
-                EIO => return error.InputOutput,
-                EISDIR => return error.IsDir,
-                ENOBUFS => return error.SystemResources,
-                ENOMEM => return error.SystemResources,
-                else => return unexpectedErrno(err),
-            }
-        }
-    }
-    while (true) {
-        const rc = system.preadv(fd, iov, count, offset);
-        switch (errno(rc)) {
-            0 => return rc,
-            EINTR => continue,
-            EINVAL => unreachable,
-            EFAULT => unreachable,
-            EAGAIN => unreachable, // This function is for blocking reads.
-            EBADF => unreachable, // always a race condition
-            EIO => return error.InputOutput,
-            EISDIR => return error.IsDir,
-            ENOBUFS => return error.SystemResources,
-            ENOMEM => return error.SystemResources,
-            else => |err| return unexpectedErrno(err),
-        }
-    }
-}
-
-pub const WriteError = error{
-    DiskQuota,
-    FileTooBig,
-    InputOutput,
-    NoSpaceLeft,
-    AccessDenied,
-    BrokenPipe,
-    SystemResources,
-    OperationAborted,
-    Unexpected,
-};
-
-/// Write to a file descriptor. Keeps trying if it gets interrupted.
-/// This function is for blocking file descriptors only. For non-blocking, see
-/// `writeAsync`.
-pub fn write(fd: fd_t, bytes: []const u8) WriteError!void {
-    if (windows.is_the_target and !builtin.link_libc) {
-        var bytes_written: windows.DWORD = undefined;
-        // TODO replace this @intCast with a loop that writes all the bytes
-        if (windows.WriteFile(handle, bytes.ptr, @intCast(u32, bytes.len), &bytes_written, null) == 0) {
-            switch (windows.GetLastError()) {
-                windows.ERROR.INVALID_USER_BUFFER => return error.SystemResources,
-                windows.ERROR.NOT_ENOUGH_MEMORY => return error.SystemResources,
-                windows.ERROR.OPERATION_ABORTED => return error.OperationAborted,
-                windows.ERROR.NOT_ENOUGH_QUOTA => return error.SystemResources,
-                windows.ERROR.IO_PENDING => unreachable,
-                windows.ERROR.BROKEN_PIPE => return error.BrokenPipe,
-                else => |err| return windows.unexpectedError(err),
-            }
-        }
-    }
-
-    if (wasi.is_the_target and !builtin.link_libc) {
-        const ciovs = [1]wasi.ciovec_t{wasi.ciovec_t{
-            .buf = bytes.ptr,
-            .buf_len = bytes.len,
-        }};
-        var nwritten: usize = undefined;
-        switch (fd_write(fd, &ciovs, ciovs.len, &nwritten)) {
-            0 => return,
-            else => |err| return unexpectedErrno(err),
-        }
-    }
-
-    // Linux can return EINVAL when write amount is > 0x7ffff000
-    // See https://github.com/ziglang/zig/pull/743#issuecomment-363165856
-    const max_bytes_len = 0x7ffff000;
-
-    var index: usize = 0;
-    while (index < bytes.len) {
-        const amt_to_write = math.min(bytes.len - index, usize(max_bytes_len));
-        const rc = system.write(fd, bytes.ptr + index, amt_to_write);
-        switch (errno(rc)) {
-            0 => {
-                index += rc;
-                continue;
-            },
-            EINTR => continue,
-            EINVAL => unreachable,
-            EFAULT => unreachable,
-            EAGAIN => unreachable, // This function is for blocking writes.
-            EBADF => unreachable, // Always a race condition.
-            EDESTADDRREQ => unreachable, // `connect` was never called.
-            EDQUOT => return error.DiskQuota,
-            EFBIG => return error.FileTooBig,
-            EIO => return error.InputOutput,
-            ENOSPC => return error.NoSpaceLeft,
-            EPERM => return error.AccessDenied,
-            EPIPE => return error.BrokenPipe,
-            else => |err| return unexpectedErrno(err),
-        }
-    }
-}
-
-/// Write multiple buffers to a file descriptor. Keeps trying if it gets interrupted.
-/// This function is for blocking file descriptors only. For non-blocking, see
-/// `pwritevAsync`.
-pub fn pwritev(fd: fd_t, iov: [*]const iovec_const, count: usize, offset: u64) WriteError!void {
-    if (darwin.is_the_target) {
-        // Darwin does not have pwritev but it does have pwrite.
-        var off: usize = 0;
-        var iov_i: usize = 0;
-        var inner_off: usize = 0;
-        while (true) {
-            const v = iov[iov_i];
-            const rc = darwin.pwrite(fd, v.iov_base + inner_off, v.iov_len - inner_off, offset + off);
-            const err = darwin.getErrno(rc);
-            switch (err) {
-                0 => {
-                    off += rc;
-                    inner_off += rc;
-                    if (inner_off == v.iov_len) {
-                        iov_i += 1;
-                        inner_off = 0;
-                        if (iov_i == count) {
-                            return;
-                        }
-                    }
-                    continue;
-                },
-                EINTR => continue,
-                ESPIPE => unreachable, // `fd` is not seekable.
-                EINVAL => unreachable,
-                EFAULT => unreachable,
-                EAGAIN => unreachable, // This function is for blocking writes.
-                EBADF => unreachable, // Always a race condition.
-                EDESTADDRREQ => unreachable, // `connect` was never called.
-                EDQUOT => return error.DiskQuota,
-                EFBIG => return error.FileTooBig,
-                EIO => return error.InputOutput,
-                ENOSPC => return error.NoSpaceLeft,
-                EPERM => return error.AccessDenied,
-                EPIPE => return error.BrokenPipe,
-                else => return unexpectedErrno(err),
-            }
-        }
-    }
-
-    while (true) {
-        const rc = system.pwritev(fd, iov, count, offset);
-        switch (errno(rc)) {
-            0 => return,
-            EINTR => continue,
-            EINVAL => unreachable,
-            EFAULT => unreachable,
-            EAGAIN => unreachable, // This function is for blocking writes.
-            EBADF => unreachable, // Always a race condition.
-            EDESTADDRREQ => unreachable, // `connect` was never called.
-            EDQUOT => return error.DiskQuota,
-            EFBIG => return error.FileTooBig,
-            EIO => return error.InputOutput,
-            ENOSPC => return error.NoSpaceLeft,
-            EPERM => return error.AccessDenied,
-            EPIPE => return error.BrokenPipe,
-            else => |err| return unexpectedErrno(err),
-        }
-    }
-}
-
-pub const OpenError = error{
-    AccessDenied,
-    FileTooBig,
-    IsDir,
-    SymLinkLoop,
-    ProcessFdQuotaExceeded,
-    NameTooLong,
-    SystemFdQuotaExceeded,
-    NoDevice,
-    FileNotFound,
-    SystemResources,
-    NoSpaceLeft,
-    NotDir,
-    PathAlreadyExists,
-    DeviceBusy,
-    Unexpected,
-};
-
-/// Open and possibly create a file. Keeps trying if it gets interrupted.
-/// `file_path` needs to be copied in memory to add a null terminating byte.
-/// See also `openC`.
-pub fn open(file_path: []const u8, flags: u32, perm: usize) OpenError!fd_t {
-    const file_path_c = try toPosixPath(file_path);
-    return openC(&file_path_c, flags, perm);
-}
-
-/// Open and possibly create a file. Keeps trying if it gets interrupted.
-/// See also `open`.
-/// TODO https://github.com/ziglang/zig/issues/265
-pub fn openC(file_path: [*]const u8, flags: u32, perm: usize) OpenError!fd_t {
-    while (true) {
-        const rc = system.open(file_path, flags, perm);
-        switch (errno(rc)) {
-            0 => return @intCast(fd_t, rc),
-            EINTR => continue,
-
-            EFAULT => unreachable,
-            EINVAL => unreachable,
-            EACCES => return error.AccessDenied,
-            EFBIG => return error.FileTooBig,
-            EOVERFLOW => return error.FileTooBig,
-            EISDIR => return error.IsDir,
-            ELOOP => return error.SymLinkLoop,
-            EMFILE => return error.ProcessFdQuotaExceeded,
-            ENAMETOOLONG => return error.NameTooLong,
-            ENFILE => return error.SystemFdQuotaExceeded,
-            ENODEV => return error.NoDevice,
-            ENOENT => return error.FileNotFound,
-            ENOMEM => return error.SystemResources,
-            ENOSPC => return error.NoSpaceLeft,
-            ENOTDIR => return error.NotDir,
-            EPERM => return error.AccessDenied,
-            EEXIST => return error.PathAlreadyExists,
-            EBUSY => return error.DeviceBusy,
-            else => |err| return unexpectedErrno(err),
-        }
-    }
-}
-
-pub fn dup2(old_fd: fd_t, new_fd: fd_t) !void {
-    while (true) {
-        switch (errno(system.dup2(old_fd, new_fd))) {
-            0 => return,
-            EBUSY, EINTR => continue,
-            EMFILE => return error.ProcessFdQuotaExceeded,
-            EINVAL => unreachable,
-            else => |err| return unexpectedErrno(err),
-        }
-    }
-}
-
-/// 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.
-/// `argv[0]` is the executable path.
-/// This function also uses the PATH environment variable to get the full path to the executable.
-/// TODO provide execveC which does not take an allocator
-pub fn execve(allocator: *Allocator, argv: []const []const u8, env_map: *const BufMap) !void {
-    const argv_buf = try allocator.alloc(?[*]u8, argv.len + 1);
-    mem.set(?[*]u8, argv_buf, null);
-    defer {
-        for (argv_buf) |arg| {
-            const arg_buf = if (arg) |ptr| cstr.toSlice(ptr) else break;
-            allocator.free(arg_buf);
-        }
-        allocator.free(argv_buf);
-    }
-    for (argv) |arg, i| {
-        const arg_buf = try allocator.alloc(u8, arg.len + 1);
-        @memcpy(arg_buf.ptr, arg.ptr, arg.len);
-        arg_buf[arg.len] = 0;
-
-        argv_buf[i] = arg_buf.ptr;
-    }
-    argv_buf[argv.len] = null;
-
-    const envp_buf = try createNullDelimitedEnvMap(allocator, env_map);
-    defer freeNullDelimitedEnvMap(allocator, envp_buf);
-
-    const exe_path = argv[0];
-    if (mem.indexOfScalar(u8, exe_path, '/') != null) {
-        return execveErrnoToErr(errno(system.execve(argv_buf[0].?, argv_buf.ptr, envp_buf.ptr)));
-    }
-
-    const PATH = getenv("PATH") orelse "/usr/local/bin:/bin/:/usr/bin";
-    // PATH.len because it is >= the largest search_path
-    // +1 for the / to join the search path and exe_path
-    // +1 for the null terminating byte
-    const path_buf = try allocator.alloc(u8, PATH.len + exe_path.len + 2);
-    defer allocator.free(path_buf);
-    var it = mem.tokenize(PATH, ":");
-    var seen_eacces = false;
-    var err: usize = undefined;
-    while (it.next()) |search_path| {
-        mem.copy(u8, path_buf, search_path);
-        path_buf[search_path.len] = '/';
-        mem.copy(u8, path_buf[search_path.len + 1 ..], exe_path);
-        path_buf[search_path.len + exe_path.len + 1] = 0;
-        err = errno(system.execve(path_buf.ptr, argv_buf.ptr, envp_buf.ptr));
-        assert(err > 0);
-        if (err == EACCES) {
-            seen_eacces = true;
-        } else if (err != ENOENT) {
-            return execveErrnoToErr(err);
-        }
-    }
-    if (seen_eacces) {
-        err = EACCES;
-    }
-    return execveErrnoToErr(err);
-}
-
-pub fn createNullDelimitedEnvMap(allocator: *Allocator, env_map: *const BufMap) ![]?[*]u8 {
-    const envp_count = env_map.count();
-    const envp_buf = try allocator.alloc(?[*]u8, envp_count + 1);
-    mem.set(?[*]u8, envp_buf, null);
-    errdefer freeNullDelimitedEnvMap(allocator, envp_buf);
-    {
-        var it = env_map.iterator();
-        var i: usize = 0;
-        while (it.next()) |pair| : (i += 1) {
-            const env_buf = try allocator.alloc(u8, pair.key.len + pair.value.len + 2);
-            @memcpy(env_buf.ptr, pair.key.ptr, pair.key.len);
-            env_buf[pair.key.len] = '=';
-            @memcpy(env_buf.ptr + 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) void {
-    for (envp_buf) |env| {
-        const env_buf = if (env) |ptr| ptr[0 .. cstr.len(ptr) + 1] else break;
-        allocator.free(env_buf);
-    }
-    allocator.free(envp_buf);
-}
-
-pub const ExecveError = error{
-    SystemResources,
-    AccessDenied,
-    InvalidExe,
-    FileSystem,
-    IsDir,
-    FileNotFound,
-    NotDir,
-    FileBusy,
-
-    Unexpected,
-};
-
-fn execveErrnoToErr(err: usize) ExecveError {
-    assert(err > 0);
-    switch (err) {
-        EFAULT => unreachable,
-        E2BIG => return error.SystemResources,
-        EMFILE => return error.ProcessFdQuotaExceeded,
-        ENAMETOOLONG => return error.NameTooLong,
-        ENFILE => return error.SystemFdQuotaExceeded,
-        ENOMEM => return error.SystemResources,
-        EACCES => return error.AccessDenied,
-        EPERM => return error.AccessDenied,
-        EINVAL => return error.InvalidExe,
-        ENOEXEC => return error.InvalidExe,
-        EIO => return error.FileSystem,
-        ELOOP => return error.FileSystem,
-        EISDIR => return error.IsDir,
-        ENOENT => return error.FileNotFound,
-        ENOTDIR => return error.NotDir,
-        ETXTBSY => return error.FileBusy,
-        else => return unexpectedErrno(err),
-    }
-}
-
-/// Get an environment variable.
-/// See also `getenvC`.
-/// TODO make this go through libc when we have it
-pub fn getenv(key: []const u8) ?[]const u8 {
-    for (environ) |ptr| {
-        var line_i: usize = 0;
-        while (ptr[line_i] != 0 and ptr[line_i] != '=') : (line_i += 1) {}
-        const this_key = ptr[0..line_i];
-        if (!mem.eql(u8, key, this_key)) continue;
-
-        var end_i: usize = line_i;
-        while (ptr[end_i] != 0) : (end_i += 1) {}
-        const this_value = ptr[line_i + 1 .. end_i];
-
-        return this_value;
-    }
-    return null;
-}
-
-/// Get an environment variable with a null-terminated name.
-/// See also `getenv`.
-/// TODO https://github.com/ziglang/zig/issues/265
-pub fn getenvC(key: [*]const u8) ?[]const u8 {
-    if (builtin.link_libc) {
-        const value = system.getenv(key) orelse return null;
-        return mem.toSliceConst(u8, value);
-    }
-    return getenv(mem.toSliceConst(u8, key));
-}
-
-/// See std.elf for the constants.
-pub fn getauxval(index: usize) usize {
-    if (builtin.link_libc) {
-        return usize(system.getauxval(index));
-    } else if (linux.elf_aux_maybe) |auxv| {
-        var i: usize = 0;
-        while (auxv[i].a_type != std.elf.AT_NULL) : (i += 1) {
-            if (auxv[i].a_type == index)
-                return auxv[i].a_un.a_val;
-        }
-    }
-    return 0;
-}
-
-pub const GetCwdError = error{
-    NameTooLong,
-    CurrentWorkingDirectoryUnlinked,
-    Unexpected,
-};
-
-/// The result is a slice of out_buffer, indexed from 0.
-pub fn getcwd(out_buffer: []u8) GetCwdError![]u8 {
-    if (windows.is_the_target and !builtin.link_libc) {
-        var utf16le_buf: [windows.PATH_MAX_WIDE]u16 = undefined;
-        const casted_len = @intCast(windows.DWORD, utf16le_buf.len); // TODO shouldn't need this cast
-        const casted_ptr = ([*]u16)(&utf16le_buf); // TODO shouldn't need this cast
-        const result = windows.GetCurrentDirectoryW(casted_len, casted_ptr);
-        if (result == 0) {
-            switch (windows.GetLastError()) {
-                else => |err| return windows.unexpectedError(err),
-            }
-        }
-        assert(result <= utf16le_buf.len);
-        const utf16le_slice = utf16le_buf[0..result];
-        // Trust that Windows gives us valid UTF-16LE.
-        var end_index: usize = 0;
-        var it = std.unicode.Utf16LeIterator.init(utf16le);
-        while (it.nextCodepoint() catch unreachable) |codepoint| {
-            if (end_index + std.unicode.utf8CodepointSequenceLength(codepoint) >= out_buffer.len)
-                return error.NameTooLong;
-            end_index += utf8Encode(codepoint, out_buffer[end_index..]) catch unreachable;
-        }
-        return out_buffer[0..end_index];
-    }
-
-    const err = if (builtin.link_libc) blk: {
-        break :blk if (system.getcwd(out_buffer.ptr, out_buffer.len)) |_| 0 else system._errno().*;
-    } else blk: {
-        break :blk errno(system.getcwd(out_buffer, out_buffer.len));
-    };
-    switch (err) {
-        0 => return mem.toSlice(u8, out_buffer),
-        EFAULT => unreachable,
-        EINVAL => unreachable,
-        ENOENT => return error.CurrentWorkingDirectoryUnlinked,
-        ERANGE => return error.NameTooLong,
-        else => |err| return unexpectedErrno(err),
-    }
-}
-
-test "getcwd" {
-    // at least call it so it gets compiled
-    var buf: [os.MAX_PATH_BYTES]u8 = undefined;
-    _ = getcwd(&buf) catch {};
-}
-
-pub const SymLinkError = error{
-    AccessDenied,
-    DiskQuota,
-    PathAlreadyExists,
-    FileSystem,
-    SymLinkLoop,
-    FileNotFound,
-    SystemResources,
-    NoSpaceLeft,
-    ReadOnlyFileSystem,
-    NotDir,
-    NameTooLong,
-    InvalidUtf8,
-    BadPathName,
-    Unexpected,
-};
-
-/// Creates a symbolic link named `new_path` which contains the string `target_path`.
-/// A symbolic link (also known as a soft link) may point to an existing file or to a nonexistent
-/// one; the latter case is known as a dangling link.
-/// If `new_path` exists, it will not be overwritten.
-/// See also `symlinkC` and `symlinkW`.
-pub fn symlink(target_path: []const u8, new_path: []const u8) SymLinkError!void {
-    if (windows.is_the_target and !builtin.link_libc) {
-        const target_path_w = try cStrToPrefixedFileW(target_path);
-        const new_path_w = try cStrToPrefixedFileW(new_path);
-        return symlinkW(&target_path_w, &new_path_w);
-    } else {
-        const target_path_c = try toPosixPath(target_path);
-        const new_path_c = try toPosixPath(new_path);
-        return symlinkC(&target_path_c, &new_path_c);
-    }
-}
-
-pub fn symlinkat(target_path: []const u8, newdirfd: fd_t, new_path: []const u8) SymLinkError!void {
-    const target_path_c = try toPosixPath(target_path);
-    const new_path_c = try toPosixPath(new_path);
-    return symlinkatC(target_path_c, newdirfd, new_path_c);
-}
-
-pub fn symlinkatC(target_path: [*]const u8, newdirfd: fd_t, new_path: [*]const u8) SymLinkError!void {
-    switch (errno(system.symlinkat(target_path, newdirfd, new_path))) {
-        0 => return,
-        EFAULT => unreachable,
-        EINVAL => unreachable,
-        EACCES => return error.AccessDenied,
-        EPERM => return error.AccessDenied,
-        EDQUOT => return error.DiskQuota,
-        EEXIST => return error.PathAlreadyExists,
-        EIO => return error.FileSystem,
-        ELOOP => return error.SymLinkLoop,
-        ENAMETOOLONG => return error.NameTooLong,
-        ENOENT => return error.FileNotFound,
-        ENOTDIR => return error.NotDir,
-        ENOMEM => return error.SystemResources,
-        ENOSPC => return error.NoSpaceLeft,
-        EROFS => return error.ReadOnlyFileSystem,
-        else => |err| return unexpectedErrno(err),
-    }
-}
-
-/// This is the same as `symlink` except the parameters are null-terminated pointers.
-/// See also `symlink` and `symlinkW`.
-pub fn symlinkC(target_path: [*]const u8, new_path: [*]const u8) SymLinkError!void {
-    if (windows.is_the_target and !builtin.link_libc) {
-        const target_path_w = try cStrToPrefixedFileW(target_path);
-        const new_path_w = try cStrToPrefixedFileW(new_path);
-        return symlinkW(&target_path_w, &new_path_w);
-    }
-    switch (errno(system.symlink(target_path, new_path))) {
-        0 => return,
-        EFAULT => unreachable,
-        EINVAL => unreachable,
-        EACCES => return error.AccessDenied,
-        EPERM => return error.AccessDenied,
-        EDQUOT => return error.DiskQuota,
-        EEXIST => return error.PathAlreadyExists,
-        EIO => return error.FileSystem,
-        ELOOP => return error.SymLinkLoop,
-        ENAMETOOLONG => return error.NameTooLong,
-        ENOENT => return error.FileNotFound,
-        ENOTDIR => return error.NotDir,
-        ENOMEM => return error.SystemResources,
-        ENOSPC => return error.NoSpaceLeft,
-        EROFS => return error.ReadOnlyFileSystem,
-        else => |err| return unexpectedErrno(err),
-    }
-}
-
-/// This is the same as `symlink` except the parameters are null-terminated pointers to
-/// UTF-16LE encoded strings.
-/// See also `symlink` and `symlinkC`.
-/// TODO handle when linking libc
-pub fn symlinkW(target_path_w: [*]const u16, new_path_w: [*]const u16) SymLinkError!void {
-    if (windows.CreateSymbolicLinkW(target_path_w, new_path_w, 0) == 0) {
-        switch (windows.GetLastError()) {
-            else => |err| return windows.unexpectedError(err),
-        }
-    }
-}
-
-pub const UnlinkError = error{
-    FileNotFound,
-    AccessDenied,
-    FileBusy,
-    FileSystem,
-    IsDir,
-    SymLinkLoop,
-    NameTooLong,
-    NotDir,
-    SystemResources,
-    ReadOnlyFileSystem,
-    Unexpected,
-
-    /// On Windows, file paths must be valid Unicode.
-    InvalidUtf8,
-
-    /// On Windows, file paths cannot contain these characters:
-    /// '/', '*', '?', '"', '<', '>', '|'
-    BadPathName,
-};
-
-/// Delete a name and possibly the file it refers to.
-pub fn unlink(file_path: []const u8) UnlinkError!void {
-    if (windows.is_the_target and !builtin.link_libc) {
-        const file_path_w = try sliceToPrefixedFileW(file_path);
-        return unlinkW(&file_path_w);
-    } else {
-        const file_path_c = try toPosixPath(file_path);
-        return unlinkC(&file_path_c);
-    }
-}
-
-/// Same as `unlink` except the parameter is a UTF16LE-encoded string.
-/// TODO handle when linking libc
-pub fn unlinkW(file_path: [*]const u16) UnlinkError!void {
-    if (windows.unlinkW(file_path) == 0) {
-        switch (windows.GetLastError()) {
-            windows.ERROR.FILE_NOT_FOUND => return error.FileNotFound,
-            windows.ERROR.ACCESS_DENIED => return error.AccessDenied,
-            windows.ERROR.FILENAME_EXCED_RANGE => return error.NameTooLong,
-            windows.ERROR.INVALID_PARAMETER => return error.NameTooLong,
-            else => |err| return windows.unexpectedError(err),
-        }
-    }
-}
-
-/// Same as `unlink` except the parameter is a null terminated UTF8-encoded string.
-pub fn unlinkC(file_path: [*]const u8) UnlinkError!void {
-    if (windows.is_the_target and !builtin.link_libc) {
-        const file_path_w = try cStrToPrefixedFileW(file_path);
-        return unlinkW(&file_path_w);
-    }
-    switch (errno(system.unlink(file_path))) {
-        0 => return,
-        EACCES => return error.AccessDenied,
-        EPERM => return error.AccessDenied,
-        EBUSY => return error.FileBusy,
-        EFAULT => unreachable,
-        EINVAL => unreachable,
-        EIO => return error.FileSystem,
-        EISDIR => return error.IsDir,
-        ELOOP => return error.SymLinkLoop,
-        ENAMETOOLONG => return error.NameTooLong,
-        ENOENT => return error.FileNotFound,
-        ENOTDIR => return error.NotDir,
-        ENOMEM => return error.SystemResources,
-        EROFS => return error.ReadOnlyFileSystem,
-        else => |err| return unexpectedErrno(err),
-    }
-}
-
-const RenameError = error{}; // TODO
-
-/// Change the name or location of a file.
-pub fn rename(old_path: []const u8, new_path: []const u8) RenameError!void {
-    if (windows.is_the_target and !builtin.link_libc) {
-        const old_path_w = try sliceToPrefixedFileW(old_path);
-        const new_path_w = try sliceToPrefixedFileW(new_path);
-        return renameW(&old_path_w, &new_path_w);
-    } else {
-        const old_path_c = try toPosixPath(old_path);
-        const new_path_c = try toPosixPath(new_path);
-        return renameC(&old_path_c, &new_path_c);
-    }
-}
-
-/// Same as `rename` except the parameters are null-terminated byte arrays.
-pub fn renameC(old_path: [*]const u8, new_path: [*]const u8) RenameError!void {
-    if (windows.is_the_target and !builtin.link_libc) {
-        const old_path_w = try cStrToPrefixedFileW(old_path);
-        const new_path_w = try cStrToPrefixedFileW(new_path);
-        return renameW(&old_path_w, &new_path_w);
-    }
-    switch (errno(system.rename(old_path, new_path))) {
-        0 => return,
-        EACCES => return error.AccessDenied,
-        EPERM => return error.AccessDenied,
-        EBUSY => return error.FileBusy,
-        EDQUOT => return error.DiskQuota,
-        EFAULT => unreachable,
-        EINVAL => unreachable,
-        EISDIR => return error.IsDir,
-        ELOOP => return error.SymLinkLoop,
-        EMLINK => return error.LinkQuotaExceeded,
-        ENAMETOOLONG => return error.NameTooLong,
-        ENOENT => return error.FileNotFound,
-        ENOTDIR => return error.NotDir,
-        ENOMEM => return error.SystemResources,
-        ENOSPC => return error.NoSpaceLeft,
-        EEXIST => return error.PathAlreadyExists,
-        ENOTEMPTY => return error.PathAlreadyExists,
-        EROFS => return error.ReadOnlyFileSystem,
-        EXDEV => return error.RenameAcrossMountPoints,
-        else => |err| return unexpectedErrno(err),
-    }
-}
-
-/// Same as `rename` except the parameters are null-terminated UTF16LE-encoded strings.
-/// TODO handle when linking libc
-pub fn renameW(old_path: [*]const u16, new_path: [*]const u16) RenameError!void {
-    const flags = windows.MOVEFILE_REPLACE_EXISTING | windows.MOVEFILE_WRITE_THROUGH;
-    if (windows.MoveFileExW(old_path, new_path, flags) == 0) {
-        switch (windows.GetLastError()) {
-            else => |err| return windows.unexpectedError(err),
-        }
-    }
-}
-
-pub const MakeDirError = error{};
-
-/// Create a directory.
-/// `mode` is ignored on Windows.
-pub fn mkdir(dir_path: []const u8, mode: u32) MakeDirError!void {
-    if (windows.is_the_target and !builtin.link_libc) {
-        const dir_path_w = try sliceToPrefixedFileW(dir_path);
-        return mkdirW(&dir_path_w, mode);
-    } else {
-        const dir_path_c = try toPosixPath(dir_path);
-        return mkdirC(&dir_path_c, mode);
-    }
-}
-
-/// Same as `mkdir` but the parameter is a null-terminated UTF8-encoded string.
-pub fn mkdirC(dir_path: [*]const u8, mode: u32) MakeDirError!void {
-    if (windows.is_the_target and !builtin.link_libc) {
-        const dir_path_w = try cStrToPrefixedFileW(dir_path);
-        return mkdirW(&dir_path_w, mode);
-    }
-    switch (errno(system.mkdir(dir_path, mode))) {
-        0 => return,
-        EACCES => return error.AccessDenied,
-        EPERM => return error.AccessDenied,
-        EDQUOT => return error.DiskQuota,
-        EEXIST => return error.PathAlreadyExists,
-        EFAULT => unreachable,
-        ELOOP => return error.SymLinkLoop,
-        EMLINK => return error.LinkQuotaExceeded,
-        ENAMETOOLONG => return error.NameTooLong,
-        ENOENT => return error.FileNotFound,
-        ENOMEM => return error.SystemResources,
-        ENOSPC => return error.NoSpaceLeft,
-        ENOTDIR => return error.NotDir,
-        EROFS => return error.ReadOnlyFileSystem,
-        else => |err| return unexpectedErrno(err),
-    }
-}
-
-/// Same as `mkdir` but the parameter is a null-terminated UTF16LE-encoded string.
-pub fn mkdirW(dir_path: []const u8, mode: u32) MakeDirError!void {
-    const dir_path_w = try sliceToPrefixedFileW(dir_path);
-
-    if (windows.CreateDirectoryW(&dir_path_w, null) == 0) {
-        switch (windows.GetLastError()) {
-            windows.ERROR.ALREADY_EXISTS => return error.PathAlreadyExists,
-            windows.ERROR.PATH_NOT_FOUND => return error.FileNotFound,
-            else => |err| return windows.unexpectedError(err),
-        }
-    }
-}
-
-pub const DeleteDirError = error{
-    AccessDenied,
-    FileBusy,
-    SymLinkLoop,
-    NameTooLong,
-    FileNotFound,
-    SystemResources,
-    NotDir,
-    DirNotEmpty,
-    ReadOnlyFileSystem,
-    InvalidUtf8,
-    BadPathName,
-    Unexpected,
-};
-
-/// Deletes an empty directory.
-pub fn rmdir(dir_path: []const u8) DeleteDirError!void {
-    if (windows.is_the_target and !builtin.link_libc) {
-        const dir_path_w = try sliceToPrefixedFileW(dir_path);
-        return rmdirW(&dir_path_w);
-    } else {
-        const dir_path_c = try toPosixPath(dir_path);
-        return rmdirC(&dir_path_c);
-    }
-}
-
-/// Same as `rmdir` except the parameter is a null-terminated UTF8-encoded string.
-pub fn rmdirC(dir_path: [*]const u8) DeleteDirError!void {
-    if (windows.is_the_target and !builtin.link_libc) {
-        const dir_path_w = try cStrToPrefixedFileW(dir_path);
-        return rmdirW(&dir_path_w);
-    }
-    switch (errno(system.rmdir(dir_path))) {
-        0 => return,
-        EACCES => return error.AccessDenied,
-        EPERM => return error.AccessDenied,
-        EBUSY => return error.FileBusy,
-        EFAULT => unreachable,
-        EINVAL => unreachable,
-        ELOOP => return error.SymLinkLoop,
-        ENAMETOOLONG => return error.NameTooLong,
-        ENOENT => return error.FileNotFound,
-        ENOMEM => return error.SystemResources,
-        ENOTDIR => return error.NotDir,
-        EEXIST => return error.DirNotEmpty,
-        ENOTEMPTY => return error.DirNotEmpty,
-        EROFS => return error.ReadOnlyFileSystem,
-        else => |err| return unexpectedErrno(err),
-    }
-}
-
-/// Same as `rmdir` except the parameter is a null-terminated UTF16LE-encoded string.
-/// TODO handle linking libc
-pub fn rmdirW(dir_path_w: [*]const u16) DeleteDirError!void {
-    if (windows.RemoveDirectoryW(dir_path_w) == 0) {
-        switch (windows.GetLastError()) {
-            windows.ERROR.PATH_NOT_FOUND => return error.FileNotFound,
-            windows.ERROR.DIR_NOT_EMPTY => return error.DirNotEmpty,
-            else => |err| return windows.unexpectedError(err),
-        }
-    }
-}
-
-pub const ChangeCurDirError = error{};
-
-/// Changes the current working directory of the calling process.
-/// `dir_path` is recommended to be a UTF-8 encoded string.
-pub fn chdir(dir_path: []const u8) ChangeCurDirError!void {
-    if (windows.is_the_target and !builtin.link_libc) {
-        const dir_path_w = try sliceToPrefixedFileW(dir_path);
-        return chdirW(&dir_path_w);
-    } else {
-        const dir_path_c = try toPosixPath(dir_path);
-        return chdirC(&dir_path_c);
-    }
-}
-
-/// Same as `chdir` except the parameter is null-terminated.
-pub fn chdirC(dir_path: [*]const u8) ChangeCurDirError!void {
-    if (windows.is_the_target and !builtin.link_libc) {
-        const dir_path_w = try cStrToPrefixedFileW(dir_path);
-        return chdirW(&dir_path_w);
-    }
-    switch (errno(system.chdir(dir_path))) {
-        0 => return,
-        EACCES => return error.AccessDenied,
-        EFAULT => unreachable,
-        EIO => return error.FileSystem,
-        ELOOP => return error.SymLinkLoop,
-        ENAMETOOLONG => return error.NameTooLong,
-        ENOENT => return error.FileNotFound,
-        ENOMEM => return error.SystemResources,
-        ENOTDIR => return error.NotDir,
-        else => |err| return unexpectedErrno(err),
-    }
-}
-
-/// Same as `chdir` except the parameter is a null-terminated, UTF16LE-encoded string.
-/// TODO handle linking libc
-pub fn chdirW(dir_path: [*]const u16) ChangeCurDirError!void {
-    @compileError("TODO implement chdir for Windows");
-}
-
-pub const ReadLinkError = error{};
-
-/// Read value of a symbolic link.
-/// The return value is a slice of `out_buffer` from index 0.
-pub fn readlink(file_path: []const u8, out_buffer: []u8) ReadLinkError![]u8 {
-    if (windows.is_the_target and !builtin.link_libc) {
-        const file_path_w = try sliceToPrefixedFileW(file_path);
-        return readlinkW(&file_path_w, out_buffer);
-    } else {
-        const file_path_c = try toPosixPath(file_path);
-        return readlinkC(&file_path_c, out_buffer);
-    }
-}
-
-/// Same as `readlink` except `file_path` is null-terminated.
-pub fn readlinkC(file_path: [*]const u8, out_buffer: []u8) ReadLinkError![]u8 {
-    if (windows.is_the_target and !builtin.link_libc) {
-        const file_path_w = try cStrToPrefixedFileW(file_path);
-        return readlinkW(&file_path_w, out_buffer);
-    }
-    const rc = system.readlink(file_path, out_buffer.ptr, out_buffer.len);
-    switch (errno(rc)) {
-        0 => return out_buffer[0..rc],
-        EACCES => return error.AccessDenied,
-        EFAULT => unreachable,
-        EINVAL => unreachable,
-        EIO => return error.FileSystem,
-        ELOOP => return error.SymLinkLoop,
-        ENAMETOOLONG => return error.NameTooLong,
-        ENOENT => return error.FileNotFound,
-        ENOMEM => return error.SystemResources,
-        ENOTDIR => return error.NotDir,
-        else => |err| return unexpectedErrno(err),
-    }
-}
-
-pub const SetIdError = error{
-    ResourceLimitReached,
-    InvalidUserId,
-    PermissionDenied,
-    Unexpected,
-};
-
-pub fn setuid(uid: u32) SetIdError!void {
-    switch (errno(system.setuid(uid))) {
-        0 => return,
-        EAGAIN => return error.ResourceLimitReached,
-        EINVAL => return error.InvalidUserId,
-        EPERM => return error.PermissionDenied,
-        else => |err| return unexpectedErrno(err),
-    }
-}
-
-pub fn setreuid(ruid: u32, euid: u32) SetIdError!void {
-    switch (errno(system.setreuid(ruid, euid))) {
-        0 => return,
-        EAGAIN => return error.ResourceLimitReached,
-        EINVAL => return error.InvalidUserId,
-        EPERM => return error.PermissionDenied,
-        else => |err| return unexpectedErrno(err),
-    }
-}
-
-pub fn setgid(gid: u32) SetIdError!void {
-    switch (errno(system.setgid(gid))) {
-        0 => return,
-        EAGAIN => return error.ResourceLimitReached,
-        EINVAL => return error.InvalidUserId,
-        EPERM => return error.PermissionDenied,
-        else => |err| return unexpectedErrno(err),
-    }
-}
-
-pub fn setregid(rgid: u32, egid: u32) SetIdError!void {
-    switch (errno(system.setregid(rgid, egid))) {
-        0 => return,
-        EAGAIN => return error.ResourceLimitReached,
-        EINVAL => return error.InvalidUserId,
-        EPERM => return error.PermissionDenied,
-        else => |err| return unexpectedErrno(err),
-    }
-}
-
-pub const GetStdHandleError = error{
-    NoStandardHandleAttached,
-    Unexpected,
-};
-
-pub fn GetStdHandle(handle_id: windows.DWORD) GetStdHandleError!fd_t {
-    if (windows.is_the_target) {
-        const handle = windows.GetStdHandle(handle_id) orelse return error.NoStandardHandleAttached;
-        if (handle == windows.INVALID_HANDLE_VALUE) {
-            switch (windows.GetLastError()) {
-                else => |err| windows.unexpectedError(err),
-            }
-        }
-        return handle;
-    }
-
-    switch (handle_id) {
-        windows.STD_ERROR_HANDLE => return STDERR_FILENO,
-        windows.STD_OUTPUT_HANDLE => return STDOUT_FILENO,
-        windows.STD_INPUT_HANDLE => return STDIN_FILENO,
-        else => unreachable,
-    }
-}
-
-/// Test whether a file descriptor refers to a terminal.
-pub fn isatty(handle: fd_t) bool {
-    if (builtin.link_libc) {
-        return system.isatty(handle) != 0;
-    }
-    if (windows.is_the_target) {
-        if (isCygwinPty(handle))
-            return true;
-
-        var out: windows.DWORD = undefined;
-        return windows.GetConsoleMode(handle, &out) != 0;
-    }
-    if (wasi.is_the_target) {
-        @compileError("TODO implement std.os.posix.isatty for WASI");
-    }
-
-    var wsz: system.winsize = undefined;
-    return system.syscall3(system.SYS_ioctl, @bitCast(usize, isize(handle)), TIOCGWINSZ, @ptrToInt(&wsz)) == 0;
-}
-
-pub fn isCygwinPty(handle: fd_t) bool {
-    if (!windows.is_the_target) return false;
-
-    const size = @sizeOf(windows.FILE_NAME_INFO);
-    var name_info_bytes align(@alignOf(windows.FILE_NAME_INFO)) = []u8{0} ** (size + windows.MAX_PATH);
-
-    if (windows.GetFileInformationByHandleEx(
-        handle,
-        windows.FileNameInfo,
-        @ptrCast(*c_void, &name_info_bytes[0]),
-        @intCast(u32, name_info_bytes.len),
-    ) == 0) {
-        return false;
-    }
-
-    const name_info = @ptrCast(*const windows.FILE_NAME_INFO, &name_info_bytes[0]);
-    const name_bytes = name_info_bytes[size .. size + usize(name_info.FileNameLength)];
-    const name_wide = @bytesToSlice(u16, name_bytes);
-    return mem.indexOf(u16, name_wide, []u16{ 'm', 's', 'y', 's', '-' }) != null or
-        mem.indexOf(u16, name_wide, []u16{ '-', 'p', 't', 'y' }) != null;
-}
-
-pub const SocketError = error{
-    /// Permission to create a socket of the specified type and/or
-    /// pro‐tocol is denied.
-    PermissionDenied,
-
-    /// The implementation does not support the specified address family.
-    AddressFamilyNotSupported,
-
-    /// Unknown protocol, or protocol family not available.
-    ProtocolFamilyNotAvailable,
-
-    /// The per-process limit on the number of open file descriptors has been reached.
-    ProcessFdQuotaExceeded,
-
-    /// The system-wide limit on the total number of open files has been reached.
-    SystemFdQuotaExceeded,
-
-    /// Insufficient memory is available. The socket cannot be created until sufficient
-    /// resources are freed.
-    SystemResources,
-
-    /// The protocol type or the specified protocol is not supported within this domain.
-    ProtocolNotSupported,
-
-    Unexpected,
-};
-
-pub fn socket(domain: u32, socket_type: u32, protocol: u32) SocketError!i32 {
-    const rc = system.socket(domain, socket_type, protocol);
-    switch (errno(rc)) {
-        0 => return @intCast(i32, rc),
-        EACCES => return error.PermissionDenied,
-        EAFNOSUPPORT => return error.AddressFamilyNotSupported,
-        EINVAL => return error.ProtocolFamilyNotAvailable,
-        EMFILE => return error.ProcessFdQuotaExceeded,
-        ENFILE => return error.SystemFdQuotaExceeded,
-        ENOBUFS, ENOMEM => return error.SystemResources,
-        EPROTONOSUPPORT => return error.ProtocolNotSupported,
-        else => |err| return unexpectedErrno(err),
-    }
-}
-
-pub const BindError = error{
-    /// The address is protected, and the user is not the superuser.
-    /// For UNIX domain sockets: Search permission is denied on  a  component
-    /// of  the  path  prefix.
-    AccessDenied,
-
-    /// The given address is already in use, or in the case of Internet domain sockets,
-    /// The  port number was specified as zero in the socket
-    /// address structure, but, upon attempting to bind to  an  ephemeral  port,  it  was
-    /// determined  that  all  port  numbers in the ephemeral port range are currently in
-    /// use.  See the discussion of /proc/sys/net/ipv4/ip_local_port_range ip(7).
-    AddressInUse,
-
-    /// A nonexistent interface was requested or the requested address was not local.
-    AddressNotAvailable,
-
-    /// Too many symbolic links were encountered in resolving addr.
-    SymLinkLoop,
-
-    /// addr is too long.
-    NameTooLong,
-
-    /// A component in the directory prefix of the socket pathname does not exist.
-    FileNotFound,
-
-    /// Insufficient kernel memory was available.
-    SystemResources,
-
-    /// A component of the path prefix is not a directory.
-    NotDir,
-
-    /// The socket inode would reside on a read-only filesystem.
-    ReadOnlyFileSystem,
-
-    Unexpected,
-};
-
-/// addr is `*const T` where T is one of the sockaddr
-pub fn bind(fd: i32, addr: *const sockaddr) BindError!void {
-    const rc = system.bind(fd, system, @sizeOf(sockaddr));
-    switch (errno(rc)) {
-        0 => return,
-        EACCES => return error.AccessDenied,
-        EADDRINUSE => return error.AddressInUse,
-        EBADF => unreachable, // always a race condition if this error is returned
-        EINVAL => unreachable,
-        ENOTSOCK => unreachable,
-        EADDRNOTAVAIL => return error.AddressNotAvailable,
-        EFAULT => unreachable,
-        ELOOP => return error.SymLinkLoop,
-        ENAMETOOLONG => return error.NameTooLong,
-        ENOENT => return error.FileNotFound,
-        ENOMEM => return error.SystemResources,
-        ENOTDIR => return error.NotDir,
-        EROFS => return error.ReadOnlyFileSystem,
-        else => |err| return unexpectedErrno(err),
-    }
-}
-
-const ListenError = error{
-    /// Another socket is already listening on the same port.
-    /// For Internet domain sockets, the  socket referred to by sockfd had not previously
-    /// been bound to an address and, upon attempting to bind it to an ephemeral port, it
-    /// was determined that all port numbers in the ephemeral port range are currently in
-    /// use.  See the discussion of /proc/sys/net/ipv4/ip_local_port_range in ip(7).
-    AddressInUse,
-
-    /// The file descriptor sockfd does not refer to a socket.
-    FileDescriptorNotASocket,
-
-    /// The socket is not of a type that supports the listen() operation.
-    OperationNotSupported,
-
-    Unexpected,
-};
-
-pub fn listen(sockfd: i32, backlog: u32) ListenError!void {
-    const rc = system.listen(sockfd, backlog);
-    switch (errno(rc)) {
-        0 => return,
-        EADDRINUSE => return error.AddressInUse,
-        EBADF => unreachable,
-        ENOTSOCK => return error.FileDescriptorNotASocket,
-        EOPNOTSUPP => return error.OperationNotSupported,
-        else => |err| return unexpectedErrno(err),
-    }
-}
-
-pub const AcceptError = error{
-    ConnectionAborted,
-
-    /// The per-process limit on the number of open file descriptors has been reached.
-    ProcessFdQuotaExceeded,
-
-    /// The system-wide limit on the total number of open files has been reached.
-    SystemFdQuotaExceeded,
-
-    /// Not enough free memory.  This often means that the memory allocation  is  limited
-    /// by the socket buffer limits, not by the system memory.
-    SystemResources,
-
-    /// The file descriptor sockfd does not refer to a socket.
-    FileDescriptorNotASocket,
-
-    /// The referenced socket is not of type SOCK_STREAM.
-    OperationNotSupported,
-
-    ProtocolFailure,
-
-    /// Firewall rules forbid connection.
-    BlockedByFirewall,
-
-    Unexpected,
-};
-
-/// Accept a connection on a socket. `fd` must be opened in blocking mode.
-/// See also `accept4_async`.
-pub fn accept4(fd: i32, addr: *sockaddr, flags: u32) AcceptError!i32 {
-    while (true) {
-        var sockaddr_size = u32(@sizeOf(sockaddr));
-        const rc = system.accept4(fd, addr, &sockaddr_size, flags);
-        switch (errno(rc)) {
-            0 => return @intCast(i32, rc),
-            EINTR => continue,
-            else => |err| return unexpectedErrno(err),
-
-            EAGAIN => unreachable, // This function is for blocking only.
-            EBADF => unreachable, // always a race condition
-            ECONNABORTED => return error.ConnectionAborted,
-            EFAULT => unreachable,
-            EINVAL => unreachable,
-            EMFILE => return error.ProcessFdQuotaExceeded,
-            ENFILE => return error.SystemFdQuotaExceeded,
-            ENOBUFS => return error.SystemResources,
-            ENOMEM => return error.SystemResources,
-            ENOTSOCK => return error.FileDescriptorNotASocket,
-            EOPNOTSUPP => return error.OperationNotSupported,
-            EPROTO => return error.ProtocolFailure,
-            EPERM => return error.BlockedByFirewall,
-        }
-    }
-}
-
-/// This is the same as `accept4` except `fd` is expected to be non-blocking.
-/// Returns -1 if would block.
-pub fn accept4_async(fd: i32, addr: *sockaddr, flags: u32) AcceptError!i32 {
-    while (true) {
-        var sockaddr_size = u32(@sizeOf(sockaddr));
-        const rc = system.accept4(fd, addr, &sockaddr_size, flags);
-        switch (errno(rc)) {
-            0 => return @intCast(i32, rc),
-            EINTR => continue,
-            else => |err| return unexpectedErrno(err),
-
-            EAGAIN => return -1,
-            EBADF => unreachable, // always a race condition
-            ECONNABORTED => return error.ConnectionAborted,
-            EFAULT => unreachable,
-            EINVAL => unreachable,
-            EMFILE => return error.ProcessFdQuotaExceeded,
-            ENFILE => return error.SystemFdQuotaExceeded,
-            ENOBUFS => return error.SystemResources,
-            ENOMEM => return error.SystemResources,
-            ENOTSOCK => return error.FileDescriptorNotASocket,
-            EOPNOTSUPP => return error.OperationNotSupported,
-            EPROTO => return error.ProtocolFailure,
-            EPERM => return error.BlockedByFirewall,
-        }
-    }
-}
-
-pub const EpollCreateError = error{
-    /// The  per-user   limit   on   the   number   of   epoll   instances   imposed   by
-    /// /proc/sys/fs/epoll/max_user_instances  was encountered.  See epoll(7) for further
-    /// details.
-    /// Or, The per-process limit on the number of open file descriptors has been reached.
-    ProcessFdQuotaExceeded,
-
-    /// The system-wide limit on the total number of open files has been reached.
-    SystemFdQuotaExceeded,
-
-    /// There was insufficient memory to create the kernel object.
-    SystemResources,
-
-    Unexpected,
-};
-
-pub fn epoll_create1(flags: u32) EpollCreateError!i32 {
-    const rc = system.epoll_create1(flags);
-    switch (errno(rc)) {
-        0 => return @intCast(i32, rc),
-        else => |err| return unexpectedErrno(err),
-
-        EINVAL => unreachable,
-        EMFILE => return error.ProcessFdQuotaExceeded,
-        ENFILE => return error.SystemFdQuotaExceeded,
-        ENOMEM => return error.SystemResources,
-    }
-}
-
-pub const EpollCtlError = error{
-    /// op was EPOLL_CTL_ADD, and the supplied file descriptor fd is  already  registered
-    /// with this epoll instance.
-    FileDescriptorAlreadyPresentInSet,
-
-    /// fd refers to an epoll instance and this EPOLL_CTL_ADD operation would result in a
-    /// circular loop of epoll instances monitoring one another.
-    OperationCausesCircularLoop,
-
-    /// op was EPOLL_CTL_MOD or EPOLL_CTL_DEL, and fd is not registered with  this  epoll
-    /// instance.
-    FileDescriptorNotRegistered,
-
-    /// There was insufficient memory to handle the requested op control operation.
-    SystemResources,
-
-    /// The  limit  imposed  by /proc/sys/fs/epoll/max_user_watches was encountered while
-    /// trying to register (EPOLL_CTL_ADD) a new file descriptor on  an  epoll  instance.
-    /// See epoll(7) for further details.
-    UserResourceLimitReached,
-
-    /// The target file fd does not support epoll.  This error can occur if fd refers to,
-    /// for example, a regular file or a directory.
-    FileDescriptorIncompatibleWithEpoll,
-
-    Unexpected,
-};
-
-pub fn epoll_ctl(epfd: i32, op: u32, fd: i32, event: *epoll_event) EpollCtlError!void {
-    const rc = system.epoll_ctl(epfd, op, fd, event);
-    switch (errno(rc)) {
-        0 => return,
-        else => |err| return unexpectedErrno(err),
-
-        EBADF => unreachable, // always a race condition if this happens
-        EEXIST => return error.FileDescriptorAlreadyPresentInSet,
-        EINVAL => unreachable,
-        ELOOP => return error.OperationCausesCircularLoop,
-        ENOENT => return error.FileDescriptorNotRegistered,
-        ENOMEM => return error.SystemResources,
-        ENOSPC => return error.UserResourceLimitReached,
-        EPERM => return error.FileDescriptorIncompatibleWithEpoll,
-    }
-}
-
-/// Waits for an I/O event on an epoll file descriptor.
-/// Returns the number of file descriptors ready for the requested I/O,
-/// or zero if no file descriptor became ready during the requested timeout milliseconds.
-pub fn epoll_wait(epfd: i32, events: []epoll_event, timeout: i32) usize {
-    while (true) {
-        // TODO get rid of the @intCast
-        const rc = system.epoll_wait(epfd, events.ptr, @intCast(u32, events.len), timeout);
-        switch (errno(rc)) {
-            0 => return rc,
-            EINTR => continue,
-            EBADF => unreachable,
-            EFAULT => unreachable,
-            EINVAL => unreachable,
-            else => unreachable,
-        }
-    }
-}
-
-pub const EventFdError = error{
-    SystemResources,
-    ProcessFdQuotaExceeded,
-    SystemFdQuotaExceeded,
-    Unexpected,
-};
-
-pub fn eventfd(initval: u32, flags: u32) EventFdError!i32 {
-    const rc = system.eventfd(initval, flags);
-    switch (errno(rc)) {
-        0 => return @intCast(i32, rc),
-        else => |err| return unexpectedErrno(err),
-
-        EINVAL => unreachable, // invalid parameters
-        EMFILE => return error.ProcessFdQuotaExceeded,
-        ENFILE => return error.SystemFdQuotaExceeded,
-        ENODEV => return error.SystemResources,
-        ENOMEM => return error.SystemResources,
-    }
-}
-
-pub const GetSockNameError = error{
-    /// Insufficient resources were available in the system to perform the operation.
-    SystemResources,
-
-    Unexpected,
-};
-
-pub fn getsockname(sockfd: i32) GetSockNameError!sockaddr {
-    var addr: sockaddr = undefined;
-    var addrlen: socklen_t = @sizeOf(sockaddr);
-    switch (errno(system.getsockname(sockfd, &addr, &addrlen))) {
-        0 => return addr,
-        else => |err| return unexpectedErrno(err),
-
-        EBADF => unreachable, // always a race condition
-        EFAULT => unreachable,
-        EINVAL => unreachable, // invalid parameters
-        ENOTSOCK => unreachable,
-        ENOBUFS => return error.SystemResources,
-    }
-}
-
-pub const ConnectError = error{
-    /// For UNIX domain sockets, which are identified by pathname: Write permission is denied on  the  socket
-    /// file,  or  search  permission  is  denied  for  one of the directories in the path prefix.
-    /// or
-    /// The user tried to connect to a broadcast address without having the socket broadcast flag enabled  or
-    /// the connection request failed because of a local firewall rule.
-    PermissionDenied,
-
-    /// Local address is already in use.
-    AddressInUse,
-
-    /// (Internet  domain  sockets)  The  socket  referred  to  by sockfd had not previously been bound to an
-    /// address and, upon attempting to bind it to an ephemeral port, it was determined that all port numbers
-    /// in    the    ephemeral    port    range    are   currently   in   use.    See   the   discussion   of
-    /// /proc/sys/net/ipv4/ip_local_port_range in ip(7).
-    AddressNotAvailable,
-
-    /// The passed address didn't have the correct address family in its sa_family field.
-    AddressFamilyNotSupported,
-
-    /// Insufficient entries in the routing cache.
-    SystemResources,
-
-    /// A connect() on a stream socket found no one listening on the remote address.
-    ConnectionRefused,
-
-    /// Network is unreachable.
-    NetworkUnreachable,
-
-    /// Timeout  while  attempting  connection.   The server may be too busy to accept new connections.  Note
-    /// that for IP sockets the timeout may be very long when syncookies are enabled on the server.
-    ConnectionTimedOut,
-
-    Unexpected,
-};
-
-/// Initiate a connection on a socket.
-/// This is for blocking file descriptors only.
-/// For non-blocking, see `connect_async`.
-pub fn connect(sockfd: i32, sockaddr: *const sockaddr) ConnectError!void {
-    while (true) {
-        switch (errno(system.connect(sockfd, sockaddr, @sizeOf(sockaddr)))) {
-            0 => return,
-            else => |err| return unexpectedErrno(err),
-
-            EACCES => return error.PermissionDenied,
-            EPERM => return error.PermissionDenied,
-            EADDRINUSE => return error.AddressInUse,
-            EADDRNOTAVAIL => return error.AddressNotAvailable,
-            EAFNOSUPPORT => return error.AddressFamilyNotSupported,
-            EAGAIN => return error.SystemResources,
-            EALREADY => unreachable, // The socket is nonblocking and a previous connection attempt has not yet been completed.
-            EBADF => unreachable, // sockfd is not a valid open file descriptor.
-            ECONNREFUSED => return error.ConnectionRefused,
-            EFAULT => unreachable, // The socket structure address is outside the user's address space.
-            EINPROGRESS => unreachable, // The socket is nonblocking and the connection cannot be completed immediately.
-            EINTR => continue,
-            EISCONN => unreachable, // The socket is already connected.
-            ENETUNREACH => return error.NetworkUnreachable,
-            ENOTSOCK => unreachable, // The file descriptor sockfd does not refer to a socket.
-            EPROTOTYPE => unreachable, // The socket type does not support the requested communications protocol.
-            ETIMEDOUT => return error.ConnectionTimedOut,
-        }
-    }
-}
-
-/// Same as `connect` except it is for blocking socket file descriptors.
-/// It expects to receive EINPROGRESS`.
-pub fn connect_async(sockfd: i32, sockaddr: *const c_void, len: u32) ConnectError!void {
-    while (true) {
-        switch (errno(system.connect(sockfd, sockaddr, len))) {
-            0, EINPROGRESS => return,
-            EINTR => continue,
-            else => |err| return unexpectedErrno(err),
-
-            EACCES => return error.PermissionDenied,
-            EPERM => return error.PermissionDenied,
-            EADDRINUSE => return error.AddressInUse,
-            EADDRNOTAVAIL => return error.AddressNotAvailable,
-            EAFNOSUPPORT => return error.AddressFamilyNotSupported,
-            EAGAIN => return error.SystemResources,
-            EALREADY => unreachable, // The socket is nonblocking and a previous connection attempt has not yet been completed.
-            EBADF => unreachable, // sockfd is not a valid open file descriptor.
-            ECONNREFUSED => return error.ConnectionRefused,
-            EFAULT => unreachable, // The socket structure address is outside the user's address space.
-            EISCONN => unreachable, // The socket is already connected.
-            ENETUNREACH => return error.NetworkUnreachable,
-            ENOTSOCK => unreachable, // The file descriptor sockfd does not refer to a socket.
-            EPROTOTYPE => unreachable, // The socket type does not support the requested communications protocol.
-            ETIMEDOUT => return error.ConnectionTimedOut,
-        }
-    }
-}
-
-pub fn getsockoptError(sockfd: i32) ConnectError!void {
-    var err_code: i32 = undefined;
-    var size: u32 = @sizeOf(i32);
-    const rc = system.getsockopt(sockfd, SOL_SOCKET, SO_ERROR, @ptrCast([*]u8, &err_code), &size);
-    assert(size == 4);
-    switch (errno(rc)) {
-        0 => switch (err_code) {
-            0 => return,
-            EACCES => return error.PermissionDenied,
-            EPERM => return error.PermissionDenied,
-            EADDRINUSE => return error.AddressInUse,
-            EADDRNOTAVAIL => return error.AddressNotAvailable,
-            EAFNOSUPPORT => return error.AddressFamilyNotSupported,
-            EAGAIN => return error.SystemResources,
-            EALREADY => unreachable, // The socket is nonblocking and a previous connection attempt has not yet been completed.
-            EBADF => unreachable, // sockfd is not a valid open file descriptor.
-            ECONNREFUSED => return error.ConnectionRefused,
-            EFAULT => unreachable, // The socket structure address is outside the user's address space.
-            EISCONN => unreachable, // The socket is already connected.
-            ENETUNREACH => return error.NetworkUnreachable,
-            ENOTSOCK => unreachable, // The file descriptor sockfd does not refer to a socket.
-            EPROTOTYPE => unreachable, // The socket type does not support the requested communications protocol.
-            ETIMEDOUT => return error.ConnectionTimedOut,
-            else => |err| return unexpectedErrno(err),
-        },
-        EBADF => unreachable, // The argument sockfd is not a valid file descriptor.
-        EFAULT => unreachable, // The address pointed to by optval or optlen is not in a valid part of the process address space.
-        EINVAL => unreachable,
-        ENOPROTOOPT => unreachable, // The option is unknown at the level indicated.
-        ENOTSOCK => unreachable, // The file descriptor sockfd does not refer to a socket.
-        else => |err| return unexpectedErrno(err),
-    }
-}
-
-pub fn waitpid(pid: i32) i32 {
-    var status: i32 = undefined;
-    while (true) {
-        switch (errno(system.waitpid(pid, &status, 0))) {
-            0 => return status,
-            EINTR => continue,
-            ECHILD => unreachable, // The process specified does not exist. It would be a race condition to handle this error.
-            EINVAL => unreachable, // The options argument was invalid
-            else => unreachable,
-        }
-    }
-}
-
-pub const FStatError = error{
-    SystemResources,
-    Unexpected,
-};
-
-pub fn fstat(fd: fd_t) FStatError!Stat {
-    var stat: Stat = undefined;
-    if (os.darwin.is_the_target) {
-        switch (errno(system.@"fstat$INODE64"(fd, buf))) {
-            0 => return stat,
-            EBADF => unreachable, // Always a race condition.
-            ENOMEM => return error.SystemResources,
-            else => |err| return unexpectedErrno(err),
-        }
-    }
-
-    switch (errno(system.fstat(fd, &stat))) {
-        0 => return stat,
-        EBADF => unreachable, // Always a race condition.
-        ENOMEM => return error.SystemResources,
-        else => |err| return unexpectedErrno(err),
-    }
-}
-
-pub const KQueueError = error{
-    /// The per-process limit on the number of open file descriptors has been reached.
-    ProcessFdQuotaExceeded,
-
-    /// The system-wide limit on the total number of open files has been reached.
-    SystemFdQuotaExceeded,
-
-    Unexpected,
-};
-
-pub fn kqueue() KQueueError!i32 {
-    const rc = system.kqueue();
-    switch (errno(rc)) {
-        0 => return @intCast(i32, rc),
-        EMFILE => return error.ProcessFdQuotaExceeded,
-        ENFILE => return error.SystemFdQuotaExceeded,
-        else => |err| return unexpectedErrno(err),
-    }
-}
-
-pub const KEventError = error{
-    /// The process does not have permission to register a filter.
-    AccessDenied,
-
-    /// The event could not be found to be modified or deleted.
-    EventNotFound,
-
-    /// No memory was available to register the event.
-    SystemResources,
-
-    /// The specified process to attach to does not exist.
-    ProcessNotFound,
-};
-
-pub fn kevent(
-    kq: i32,
-    changelist: []const Kevent,
-    eventlist: []Kevent,
-    timeout: ?*const timespec,
-) KEventError!usize {
-    while (true) {
-        const rc = system.kevent(kq, changelist, eventlist, timeout);
-        switch (errno(rc)) {
-            0 => return rc,
-            EACCES => return error.AccessDenied,
-            EFAULT => unreachable,
-            EBADF => unreachable, // Always a race condition.
-            EINTR => continue,
-            EINVAL => unreachable,
-            ENOENT => return error.EventNotFound,
-            ENOMEM => return error.SystemResources,
-            ESRCH => return error.ProcessNotFound,
-            else => unreachable,
-        }
-    }
-}
-
-pub const INotifyInitError = error{
-    ProcessFdQuotaExceeded,
-    SystemFdQuotaExceeded,
-    SystemResources,
-    Unexpected,
-};
-
-/// initialize an inotify instance
-pub fn inotify_init1(flags: u32) INotifyInitError!i32 {
-    const rc = system.inotify_init1(flags);
-    switch (errno(rc)) {
-        0 => return @intCast(i32, rc),
-        EINVAL => unreachable,
-        EMFILE => return error.ProcessFdQuotaExceeded,
-        ENFILE => return error.SystemFdQuotaExceeded,
-        ENOMEM => return error.SystemResources,
-        else => |err| return unexpectedErrno(err),
-    }
-}
-
-pub const INotifyAddWatchError = error{
-    AccessDenied,
-    NameTooLong,
-    FileNotFound,
-    SystemResources,
-    UserResourceLimitReached,
-    Unexpected,
-};
-
-/// add a watch to an initialized inotify instance
-pub fn inotify_add_watch(inotify_fd: i32, pathname: []const u8, mask: u32) INotifyAddWatchError!i32 {
-    const pathname_c = try toPosixPath(pathname);
-    return inotify_add_watchC(inotify_fd, &pathname_c, mask);
-}
-
-/// Same as `inotify_add_watch` except pathname is null-terminated.
-pub fn inotify_add_watchC(inotify_fd: i32, pathname: [*]const u8, mask: u32) INotifyAddWatchError!i32 {
-    const rc = system.inotify_add_watch(inotify_fd, pathname, mask);
-    switch (errno(rc)) {
-        0 => return @intCast(i32, rc),
-        EACCES => return error.AccessDenied,
-        EBADF => unreachable,
-        EFAULT => unreachable,
-        EINVAL => unreachable,
-        ENAMETOOLONG => return error.NameTooLong,
-        ENOENT => return error.FileNotFound,
-        ENOMEM => return error.SystemResources,
-        ENOSPC => return error.UserResourceLimitReached,
-        else => |err| return unexpectedErrno(err),
-    }
-}
-
-/// remove an existing watch from an inotify instance
-pub fn inotify_rm_watch(inotify_fd: i32, wd: i32) void {
-    switch (errno(system.inotify_rm_watch(inotify_fd, wd))) {
-        0 => return,
-        EBADF => unreachable,
-        EINVAL => unreachable,
-        else => unreachable,
-    }
-}
-
-pub const MProtectError = error{
-    AccessDenied,
-    OutOfMemory,
-    Unexpected,
-};
-
-/// address and length must be page-aligned
-pub fn mprotect(address: usize, length: usize, protection: u32) MProtectError!void {
-    const negative_page_size = @bitCast(usize, -isize(os.page_size));
-    const aligned_address = address & negative_page_size;
-    const aligned_end = (address + length + os.page_size - 1) & negative_page_size;
-    assert(address == aligned_address);
-    assert(length == aligned_end - aligned_address);
-    switch (errno(system.mprotect(address, length, protection))) {
-        0 => return,
-        EINVAL => unreachable,
-        EACCES => return error.AccessDenied,
-        ENOMEM => return error.OutOfMemory,
-        else => return unexpectedErrno(err),
-    }
-}
-
-pub const ForkError = error{
-    SystemResources,
-    Unexpected,
-};
-
-pub fn fork() ForkError!pid_t {
-    const rc = system.fork();
-    switch (errno(rc)) {
-        0 => return rc,
-        EAGAIN => return error.SystemResources,
-        ENOMEM => return error.SystemResources,
-        else => |err| return unexpectedErrno(err),
-    }
-}
-
-pub const MMapError = error{
-    AccessDenied,
-    PermissionDenied,
-    LockedMemoryLimitExceeded,
-    SystemFdQuotaExceeded,
-    MemoryMappingNotSupported,
-    OutOfMemory,
-};
-
-/// Map files or devices into memory.
-/// Use of a mapped region can result in these signals:
-/// * SIGSEGV - Attempted write into a region mapped as read-only.
-/// * SIGBUS - Attempted  access to a portion of the buffer that does not correspond to the file
-pub fn mmap(address: ?[*]u8, length: usize, prot: u32, flags: u32, fd: fd_t, offset: isize) MMapError!usize {
-    const err = if (builtin.link_libc) blk: {
-        const rc = system.mmap(address, length, prot, flags, fd, offset);
-        if (rc != system.MMAP_FAILED) return rc;
-        break :blk system._errno().*;
-    } else blk: {
-        const rc = system.mmap(address, length, prot, flags, fd, offset);
-        const err = errno(rc);
-        if (err == 0) return rc;
-        break :blk err;
-    };
-    switch (err) {
-        ETXTBSY => return error.AccessDenied,
-        EACCES => return error.AccessDenied,
-        EPERM => return error.PermissionDenied,
-        EAGAIN => return error.LockedMemoryLimitExceeded,
-        EBADF => unreachable, // Always a race condition.
-        EOVERFLOW => unreachable, // The number of pages used for length + offset would overflow.
-        ENFILE => return error.SystemFdQuotaExceeded,
-        ENODEV => return error.MemoryMappingNotSupported,
-        EINVAL => unreachable, // Invalid parameters to mmap()
-        ENOMEM => return error.OutOfMemory,
-        else => return unexpectedErrno(err),
-    }
-}
-
-/// Deletes the mappings for the specified address range, causing
-/// further references to addresses within the range to generate invalid memory references.
-/// Note that while POSIX allows unmapping a region in the middle of an existing mapping,
-/// Zig's munmap function does not, for two reasons:
-/// * It violates the Zig principle that resource deallocation must succeed.
-/// * The Windows function, VirtualFree, has this restriction.
-pub fn munmap(address: usize, length: usize) void {
-    switch (errno(system.munmap(address, length))) {
-        0 => return,
-        EINVAL => unreachable, // Invalid parameters.
-        ENOMEM => unreachable, // Attempted to unmap a region in the middle of an existing mapping.
-        else => unreachable,
-    }
-}
-
-pub const AccessError = error{
-    PermissionDenied,
-    FileNotFound,
-    NameTooLong,
-    InputOutput,
-    SystemResources,
-    BadPathName,
-
-    /// On Windows, file paths must be valid Unicode.
-    InvalidUtf8,
-
-    Unexpected,
-};
-
-/// check user's permissions for a file
-pub fn access(path: []const u8, mode: u32) AccessError!void {
-    if (windows.is_the_target and !builtin.link_libc) {
-        const path_w = try sliceToPrefixedFileW(path);
-        return accessW(&path_w, mode);
-    }
-    const path_c = try toPosixPath(path);
-    return accessC(&path_c, mode);
-}
-
-/// Call from Windows-specific code if you already have a UTF-16LE encoded, null terminated string.
-/// Otherwise use `access` or `accessC`.
-/// TODO currently this ignores `mode`.
-pub fn accessW(path: [*]const u16, mode: u32) AccessError!void {
-    if (windows.GetFileAttributesW(path) != windows.INVALID_FILE_ATTRIBUTES) {
-        return;
-    }
-    switch (windows.GetLastError()) {
-        windows.ERROR.FILE_NOT_FOUND => return error.FileNotFound,
-        windows.ERROR.PATH_NOT_FOUND => return error.FileNotFound,
-        windows.ERROR.ACCESS_DENIED => return error.PermissionDenied,
-        else => |err| return windows.unexpectedError(err),
-    }
-}
-
-/// Call if you have a UTF-8 encoded, null-terminated string.
-/// Otherwise use `access` or `accessW`.
-pub fn accessC(path: [*]const u8, mode: u32) AccessError!void {
-    if (windows.is_the_target) {
-        const path_w = try cStrToPrefixedFileW(path);
-        return accessW(&path_w, mode);
-    }
-    switch (errno(system.access(path, mode))) {
-        0 => return,
-        EACCES => return error.PermissionDenied,
-        EROFS => return error.PermissionDenied,
-        ELOOP => return error.PermissionDenied,
-        ETXTBSY => return error.PermissionDenied,
-        ENOTDIR => return error.FileNotFound,
-        ENOENT => return error.FileNotFound,
-
-        ENAMETOOLONG => return error.NameTooLong,
-        EINVAL => unreachable,
-        EFAULT => unreachable,
-        EIO => return error.InputOutput,
-        ENOMEM => return error.SystemResources,
-        else => |err| return unexpectedErrno(err),
-    }
-}
-
-pub const PipeError = error{
-    SystemFdQuotaExceeded,
-    ProcessFdQuotaExceeded,
-};
-
-/// Creates a unidirectional data channel that can be used for interprocess communication.
-pub fn pipe(fds: *[2]fd_t) PipeError!void {
-    switch (errno(system.pipe(fds))) {
-        0 => return,
-        EINVAL => unreachable, // Invalid parameters to pipe()
-        EFAULT => unreachable, // Invalid fds pointer
-        ENFILE => return error.SystemFdQuotaExceeded,
-        EMFILE => return error.ProcessFdQuotaExceeded,
-        else => |err| return unexpectedErrno(err),
-    }
-}
-
-pub fn pipe2(fds: *[2]fd_t, flags: u32) PipeError!void {
-    switch (errno(system.pipe2(fds, flags))) {
-        0 => return,
-        EINVAL => unreachable, // Invalid flags
-        EFAULT => unreachable, // Invalid fds pointer
-        ENFILE => return error.SystemFdQuotaExceeded,
-        EMFILE => return error.ProcessFdQuotaExceeded,
-        else => |err| return unexpectedErrno(err),
-    }
-}
-
-pub const SysCtlError = error{
-    PermissionDenied,
-    SystemResources,
-    Unexpected,
-};
-
-pub fn sysctl(
-    name: []const c_int,
-    oldp: ?*c_void,
-    oldlenp: ?*usize,
-    newp: ?*c_void,
-    newlen: usize,
-) SysCtlError!void {
-    switch (errno(system.sysctl(name.ptr, name.len, oldp, oldlenp, newp, newlen))) {
-        0 => return,
-        EFAULT => unreachable,
-        EPERM => return error.PermissionDenied,
-        ENOMEM => return error.SystemResources,
-        else => |err| return unexpectedErrno(err),
-    }
-}
-
-pub fn sysctlbynameC(
-    name: [*]const u8,
-    oldp: ?*c_void,
-    oldlenp: ?*usize,
-    newp: ?*c_void,
-    newlen: usize,
-) SysCtlError!void {
-    switch (errno(system.sysctlbyname(name, oldp, oldlenp, newp, newlen))) {
-        0 => return,
-        EFAULT => unreachable,
-        EPERM => return error.PermissionDenied,
-        ENOMEM => return error.SystemResources,
-        else => |err| return unexpectedErrno(err),
-    }
-}
-
-pub fn gettimeofday(tv: ?*timeval, tz: ?*timezone) void {
-    switch (errno(system.gettimeofday(tv, tz))) {
-        0 => return,
-        EINVAL => unreachable,
-        else => unreachable,
-    }
-}
-
-pub fn nanosleep(req: timespec) void {
-    var rem = req;
-    while (true) {
-        switch (errno(system.nanosleep(&rem, &rem))) {
-            0 => return,
-            EINVAL => unreachable, // Invalid parameters.
-            EFAULT => unreachable,
-            EINTR => continue,
-        }
-    }
-}
-
-pub const realpath = std.os.path.real;
-pub const realpathC = std.os.path.realC;
-pub const realpathW = std.os.path.realW;
-
-pub const WaitForSingleObjectError = error{
-    WaitAbandoned,
-    WaitTimeOut,
-    Unexpected,
-};
-
-pub fn WaitForSingleObject(handle: windows.HANDLE, milliseconds: windows.DWORD) WaitForSingleObjectError!void {
-    switch (windows.WaitForSingleObject(handle, milliseconds)) {
-        windows.WAIT_ABANDONED => return error.WaitAbandoned,
-        windows.WAIT_OBJECT_0 => return,
-        windows.WAIT_TIMEOUT => return error.WaitTimeOut,
-        windows.WAIT_FAILED => {
-            switch (windows.GetLastError()) {
-                else => |err| return windows.unexpectedError(err),
-            }
-        },
-        else => return error.Unexpected,
-    }
-}
-
-pub fn FindFirstFile(
-    dir_path: []const u8,
-    find_file_data: *windows.WIN32_FIND_DATAW,
-) !windows.HANDLE {
-    const dir_path_w = try sliceToPrefixedSuffixedFileW(dir_path, []u16{ '\\', '*', 0 });
-    const handle = windows.FindFirstFileW(&dir_path_w, find_file_data);
-
-    if (handle == windows.INVALID_HANDLE_VALUE) {
-        switch (windows.GetLastError()) {
-            windows.ERROR.FILE_NOT_FOUND => return error.FileNotFound,
-            windows.ERROR.PATH_NOT_FOUND => return error.FileNotFound,
-            else => |err| return windows.unexpectedError(err),
-        }
-    }
-
-    return handle;
-}
-
-/// Returns `true` if there was another file, `false` otherwise.
-pub fn FindNextFile(handle: windows.HANDLE, find_file_data: *windows.WIN32_FIND_DATAW) !bool {
-    if (windows.FindNextFileW(handle, find_file_data) == 0) {
-        switch (windows.GetLastError()) {
-            windows.ERROR.NO_MORE_FILES => return false,
-            else => |err| return windows.unexpectedError(err),
-        }
-    }
-    return true;
-}
-
-pub const CreateIoCompletionPortError = error{Unexpected};
-
-pub fn CreateIoCompletionPort(
-    file_handle: windows.HANDLE,
-    existing_completion_port: ?windows.HANDLE,
-    completion_key: usize,
-    concurrent_thread_count: windows.DWORD,
-) CreateIoCompletionPortError!windows.HANDLE {
-    const handle = windows.CreateIoCompletionPort(file_handle, existing_completion_port, completion_key, concurrent_thread_count) orelse {
-        switch (windows.GetLastError()) {
-            windows.ERROR.INVALID_PARAMETER => unreachable,
-            else => |err| return windows.unexpectedError(err),
-        }
-    };
-    return handle;
-}
-
-pub const WindowsPostQueuedCompletionStatusError = error{Unexpected};
-
-pub fn windowsPostQueuedCompletionStatus(completion_port: windows.HANDLE, bytes_transferred_count: windows.DWORD, completion_key: usize, lpOverlapped: ?*windows.OVERLAPPED) WindowsPostQueuedCompletionStatusError!void {
-    if (windows.PostQueuedCompletionStatus(completion_port, bytes_transferred_count, completion_key, lpOverlapped) == 0) {
-        const err = windows.GetLastError();
-        switch (err) {
-            else => return windows.unexpectedError(err),
-        }
-    }
-}
-
-pub const GetQueuedCompletionStatusResult = enum {
-    Normal,
-    Aborted,
-    Cancelled,
-    EOF,
-};
-
-pub fn GetQueuedCompletionStatus(
-    completion_port: windows.HANDLE,
-    bytes_transferred_count: *windows.DWORD,
-    lpCompletionKey: *usize,
-    lpOverlapped: *?*windows.OVERLAPPED,
-    dwMilliseconds: windows.DWORD,
-) GetQueuedCompletionStatusResult {
-    if (windows.GetQueuedCompletionStatus(completion_port, bytes_transferred_count, lpCompletionKey, lpOverlapped, dwMilliseconds) == windows.FALSE) {
-        switch (windows.GetLastError()) {
-            windows.ERROR.ABANDONED_WAIT_0 => return GetQueuedCompletionStatusResult.Aborted,
-            windows.ERROR.OPERATION_ABORTED => return GetQueuedCompletionStatusResult.Cancelled,
-            windows.ERROR.HANDLE_EOF => return GetQueuedCompletionStatusResult.EOF,
-            else => |err| {
-                if (std.debug.runtime_safety) {
-                    std.debug.panic("unexpected error: {}\n", err);
-                }
-            },
-        }
-    }
-    return GetQueuedCompletionStatusResult.Normal;
-}
-
-/// Used to convert a slice to a null terminated slice on the stack.
-/// TODO https://github.com/ziglang/zig/issues/287
-pub fn toPosixPath(file_path: []const u8) ![PATH_MAX]u8 {
-    var path_with_null: [PATH_MAX]u8 = undefined;
-    // >= rather than > to make room for the null byte
-    if (file_path.len >= PATH_MAX) return error.NameTooLong;
-    mem.copy(u8, &path_with_null, file_path);
-    path_with_null[file_path.len] = 0;
-    return path_with_null;
-}
-
-/// Call this when you made a syscall or something that sets errno
-/// and you get an unexpected error.
-pub fn unexpectedErrno(errno: usize) os.UnexpectedError {
-    if (os.unexpected_error_tracing) {
-        std.debug.warn("unexpected errno: {}\n", errno);
-        std.debug.dumpCurrentStackTrace(null);
-    }
-    return error.Unexpected;
-}
std/os/test.zig
@@ -1,5 +1,6 @@
 const std = @import("../std.zig");
 const os = std.os;
+const testing = std.testing;
 const expect = std.testing.expect;
 const io = std.io;
 const mem = std.mem;
@@ -127,3 +128,24 @@ fn testTls(context: void) void {
     x += 1;
     if (x != 1235) @panic("bad end value");
 }
+
+test "getrandom" {
+    var buf_a: [50]u8 = undefined;
+    var buf_b: [50]u8 = undefined;
+    try os.getrandom(&buf_a);
+    try os.getrandom(&buf_b);
+    // If this test fails the chance is significantly higher that there is a bug than
+    // that two sets of 50 bytes were equal.
+    expect(!mem.eql(u8, buf_a, buf_b));
+}
+
+test "getcwd" {
+    // at least call it so it gets compiled
+    var buf: [std.fs.MAX_PATH_BYTES]u8 = undefined;
+    _ = os.getcwd(&buf) catch {};
+}
+
+test "realpath" {
+    var buf: [std.fs.MAX_PATH_BYTES]u8 = undefined;
+    testing.expectError(error.FileNotFound, os.realpath("definitely_bogus_does_not_exist1234", &buf));
+}
std/os/windows.zig
@@ -535,7 +535,7 @@ pub const COINIT = extern enum {
 /// from https://docs.microsoft.com/en-us/windows/desktop/FileIO/naming-a-file#maximum-path-length-limitation
 pub const PATH_MAX_WIDE = 32767;
 
-pub const OpenError = error{
+pub const CreateFileError = error{
     SharingViolation,
     PathAlreadyExists,
 
@@ -565,7 +565,7 @@ pub fn CreateFile(
     share_mode: DWORD,
     creation_disposition: DWORD,
     flags_and_attrs: DWORD,
-) OpenError!fd_t {
+) CreateFileError!fd_t {
     const file_path_w = try sliceToPrefixedFileW(file_path);
     return CreateFileW(&file_path_w, desired_access, share_mode, creation_disposition, flags_and_attrs);
 }
@@ -576,7 +576,7 @@ pub fn CreateFileW(
     share_mode: DWORD,
     creation_disposition: DWORD,
     flags_and_attrs: DWORD,
-) OpenError!HANDLE {
+) CreateFileError!HANDLE {
     const result = kernel32.CreateFileW(file_path_w, desired_access, share_mode, null, creation_disposition, flags_and_attrs, null);
 
     if (result == INVALID_HANDLE_VALUE) {
@@ -588,6 +588,7 @@ pub fn CreateFileW(
             ERROR.PATH_NOT_FOUND => return error.FileNotFound,
             ERROR.ACCESS_DENIED => return error.AccessDenied,
             ERROR.PIPE_BUSY => return error.PipeBusy,
+            ERROR.FILENAME_EXCED_RANGE => return error.NameTooLong,
             else => |err| return unexpectedErrorWindows(err),
         }
     }
@@ -615,6 +616,451 @@ fn SetHandleInformation(h: HANDLE, mask: DWORD, flags: DWORD) SetHandleInformati
     }
 }
 
+pub const RtlGenRandomError = error{Unexpected};
+
+/// Call RtlGenRandom() instead of CryptGetRandom() on Windows
+/// https://github.com/rust-lang-nursery/rand/issues/111
+/// https://bugzilla.mozilla.org/show_bug.cgi?id=504270
+pub fn RtlGenRandom(output: []u8) RtlGenRandomError!void {
+    if (advapi32.RtlGenRandom(output.ptr, output.len) == 0) {
+        switch (kernel32.GetLastError()) {
+            else => |err| return unexpectedError(err),
+        }
+    }
+}
+
+pub const WaitForSingleObjectError = error{
+    WaitAbandoned,
+    WaitTimeOut,
+    Unexpected,
+};
+
+pub fn WaitForSingleObject(handle: HANDLE, milliseconds: DWORD) WaitForSingleObjectError!void {
+    switch (kernel32.WaitForSingleObject(handle, milliseconds)) {
+        WAIT_ABANDONED => return error.WaitAbandoned,
+        WAIT_OBJECT_0 => return,
+        WAIT_TIMEOUT => return error.WaitTimeOut,
+        WAIT_FAILED => switch (kernel32.GetLastError()) {
+            else => |err| return unexpectedError(err),
+        },
+        else => return error.Unexpected,
+    }
+}
+
+pub const FindFirstFileError = error{
+    FileNotFound,
+    Unexpected,
+};
+
+pub fn FindFirstFile(dir_path: []const u8, find_file_data: *WIN32_FIND_DATAW) FindFirstFileError!HANDLE {
+    const dir_path_w = try sliceToPrefixedSuffixedFileW(dir_path, []u16{ '\\', '*', 0 });
+    const handle = kernel32.FindFirstFileW(&dir_path_w, find_file_data);
+
+    if (handle == INVALID_HANDLE_VALUE) {
+        switch (kernel32.GetLastError()) {
+            ERROR.FILE_NOT_FOUND => return error.FileNotFound,
+            ERROR.PATH_NOT_FOUND => return error.FileNotFound,
+            else => |err| return unexpectedError(err),
+        }
+    }
+
+    return handle;
+}
+
+pub const FindNextFileError = error{Unexpected};
+
+/// Returns `true` if there was another file, `false` otherwise.
+pub fn FindNextFile(handle: HANDLE, find_file_data: *WIN32_FIND_DATAW) FindNextFileError!bool {
+    if (kernel32.FindNextFileW(handle, find_file_data) == 0) {
+        switch (kernel32.GetLastError()) {
+            ERROR.NO_MORE_FILES => return false,
+            else => |err| return unexpectedError(err),
+        }
+    }
+    return true;
+}
+
+pub const CreateIoCompletionPortError = error{Unexpected};
+
+pub fn CreateIoCompletionPort(
+    file_handle: HANDLE,
+    existing_completion_port: ?HANDLE,
+    completion_key: usize,
+    concurrent_thread_count: DWORD,
+) CreateIoCompletionPortError!HANDLE {
+    const handle = kernel32.CreateIoCompletionPort(file_handle, existing_completion_port, completion_key, concurrent_thread_count) orelse {
+        switch (kernel32.GetLastError()) {
+            ERROR.INVALID_PARAMETER => unreachable,
+            else => |err| return unexpectedError(err),
+        }
+    };
+    return handle;
+}
+
+pub const PostQueuedCompletionStatusError = error{Unexpected};
+
+pub fn PostQueuedCompletionStatus(
+    completion_port: HANDLE,
+    bytes_transferred_count: DWORD,
+    completion_key: usize,
+    lpOverlapped: ?*OVERLAPPED,
+) PostQueuedCompletionStatusError!void {
+    if (kernel32.PostQueuedCompletionStatus(completion_port, bytes_transferred_count, completion_key, lpOverlapped) == 0) {
+        switch (kernel32.GetLastError()) {
+            else => return unexpectedError(err),
+        }
+    }
+}
+
+pub const GetQueuedCompletionStatusResult = enum {
+    Normal,
+    Aborted,
+    Cancelled,
+    EOF,
+};
+
+pub fn GetQueuedCompletionStatus(
+    completion_port: HANDLE,
+    bytes_transferred_count: *DWORD,
+    lpCompletionKey: *usize,
+    lpOverlapped: *?*OVERLAPPED,
+    dwMilliseconds: DWORD,
+) GetQueuedCompletionStatusResult {
+    if (kernel32.GetQueuedCompletionStatus(
+        completion_port,
+        bytes_transferred_count,
+        lpCompletionKey,
+        lpOverlapped,
+        dwMilliseconds,
+    ) == FALSE) {
+        switch (kernel32.GetLastError()) {
+            ERROR.ABANDONED_WAIT_0 => return GetQueuedCompletionStatusResult.Aborted,
+            ERROR.OPERATION_ABORTED => return GetQueuedCompletionStatusResult.Cancelled,
+            ERROR.HANDLE_EOF => return GetQueuedCompletionStatusResult.EOF,
+            else => |err| {
+                if (std.debug.runtime_safety) {
+                    std.debug.panic("unexpected error: {}\n", err);
+                }
+            },
+        }
+    }
+    return GetQueuedCompletionStatusResult.Normal;
+}
+
+pub fn CloseHandle(hObject: HANDLE) void {
+    assert(kernel32.CloseHandle(hObject) != 0);
+}
+
+pub const ReadFileError = error{Unexpected};
+
+pub fn ReadFile(in_hFile: HANDLE, buffer: []u8) ReadFileError!usize {
+    var index: usize = 0;
+    while (index < buffer.len) {
+        const want_read_count = @intCast(DWORD, math.min(DWORD(maxInt(DWORD)), buffer.len - index));
+        var amt_read: DWORD = undefined;
+        if (kernel32.ReadFile(fd, buffer.ptr + index, want_read_count, &amt_read, null) == 0) {
+            switch (kernel32.GetLastError()) {
+                ERROR.OPERATION_ABORTED => continue,
+                ERROR.BROKEN_PIPE => return index,
+                else => |err| return unexpectedError(err),
+            }
+        }
+        if (amt_read == 0) return index;
+        index += amt_read;
+    }
+    return index;
+}
+
+pub const WriteFileError = error{
+    SystemResources,
+    OperationAborted,
+    BrokenPipe,
+    Unexpected,
+};
+
+/// This function is for blocking file descriptors only. For non-blocking, see
+/// `WriteFileAsync`.
+pub fn WriteFile(in_hFile: HANDLE, bytes: []const u8) WriteFileError!void {
+    var bytes_written: DWORD = undefined;
+    // TODO replace this @intCast with a loop that writes all the bytes
+    if (kernel32.WriteFile(handle, bytes.ptr, @intCast(u32, bytes.len), &bytes_written, null) == 0) {
+        switch (kernel32.GetLastError()) {
+            ERROR.INVALID_USER_BUFFER => return error.SystemResources,
+            ERROR.NOT_ENOUGH_MEMORY => return error.SystemResources,
+            ERROR.OPERATION_ABORTED => return error.OperationAborted,
+            ERROR.NOT_ENOUGH_QUOTA => return error.SystemResources,
+            ERROR.IO_PENDING => unreachable, // this function is for blocking files only
+            ERROR.BROKEN_PIPE => return error.BrokenPipe,
+            else => |err| return unexpectedError(err),
+        }
+    }
+}
+
+pub const GetCurrentDirectoryError = error{
+    NameTooLong,
+    Unexpected,
+};
+
+/// The result is a slice of out_buffer, indexed from 0.
+pub fn GetCurrentDirectory(buffer: []u8) GetCurrentDirectoryError![]u8 {
+    var utf16le_buf: [PATH_MAX_WIDE]u16 = undefined;
+    const result = kernel32.GetCurrentDirectoryW(utf16le_buf.len, &utf16le_buf);
+    if (result == 0) {
+        switch (kernel32.GetLastError()) {
+            else => |err| return unexpectedError(err),
+        }
+    }
+    assert(result <= utf16le_buf.len);
+    const utf16le_slice = utf16le_buf[0..result];
+    // Trust that Windows gives us valid UTF-16LE.
+    var end_index: usize = 0;
+    var it = std.unicode.Utf16LeIterator.init(utf16le);
+    while (it.nextCodepoint() catch unreachable) |codepoint| {
+        if (end_index + std.unicode.utf8CodepointSequenceLength(codepoint) >= out_buffer.len)
+            return error.NameTooLong;
+        end_index += utf8Encode(codepoint, out_buffer[end_index..]) catch unreachable;
+    }
+    return out_buffer[0..end_index];
+}
+
+pub const CreateSymbolicLinkError = error{Unexpected};
+
+pub fn CreateSymbolicLink(
+    sym_link_path: []const u8,
+    target_path: []const u8,
+    flags: DWORD,
+) CreateSymbolicLinkError!void {
+    const sym_link_path_w = try sliceToPrefixedFileW(sym_link_path);
+    const target_path_w = try sliceToPrefixedFileW(target_path);
+    return CreateSymbolicLinkW(&sym_link_path_w, &target_path_w, flags);
+}
+
+pub fn CreateSymbolicLinkW(
+    sym_link_path: [*]const u16,
+    target_path: [*]const u16,
+    flags: DWORD,
+) CreateSymbolicLinkError!void {
+    if (kernel32.CreateSymbolicLinkW(sym_link_path, target_path, flags) == 0) {
+        switch (kernel32.GetLastError()) {
+            else => |err| return kernel32.unexpectedError(err),
+        }
+    }
+}
+
+pub const DeleteFileError = error{
+    FileNotFound,
+    AccessDenied,
+    NameTooLong,
+    Unexpected,
+};
+
+pub fn DeleteFile(filename: []const u8) DeleteFileError!void {
+    const filename_w = try sliceToPrefixedFileW(filename);
+    return DeleteFileW(&filename_w);
+}
+
+pub fn DeleteFileW(filename: [*]const u16) DeleteFileError!void {
+    if (kernel32.DeleteFileW(file_path) == 0) {
+        switch (kernel32.GetLastError()) {
+            ERROR.FILE_NOT_FOUND => return error.FileNotFound,
+            ERROR.ACCESS_DENIED => return error.AccessDenied,
+            ERROR.FILENAME_EXCED_RANGE => return error.NameTooLong,
+            ERROR.INVALID_PARAMETER => return error.NameTooLong,
+            else => |err| return unexpectedError(err),
+        }
+    }
+}
+
+pub const MoveFileError = error{Unexpected};
+
+pub fn MoveFileEx(old_path: []const u8, new_path: []const u8, flags: DWORD) MoveFileError!void {
+    const old_path_w = try sliceToPrefixedFileW(old_path);
+    const new_path_w = try sliceToPrefixedFileW(new_path);
+    return MoveFileExW(&old_path_w, &new_path_w, flags);
+}
+
+pub fn MoveFileExW(old_path: [*]const u16, new_path: [*]const u16, flags: DWORD) MoveFileError!void {
+    if (kernel32.MoveFileExW(old_path, new_path, flags) == 0) {
+        switch (kernel32.GetLastError()) {
+            else => |err| return unexpectedError(err),
+        }
+    }
+}
+
+pub const CreateDirectoryError = error{
+    PathAlreadyExists,
+    FileNotFound,
+    Unexpected,
+};
+
+pub fn CreateDirectory(pathname: []const u8, attrs: ?*SECURITY_ATTRIBUTES) CreateDirectoryError!void {
+    const pathname_w = try sliceToPrefixedFileW(pathname);
+    return CreateDirectoryW(&pathname_w, attrs);
+}
+
+pub fn CreateDirectoryW(pathname: [*]const u16, attrs: ?*SECURITY_ATTRIBUTES) CreateDirectoryError!void {
+    if (kernel32.CreateDirectoryW(pathname, attrs) == 0) {
+        switch (kernel32.GetLastError()) {
+            ERROR.ALREADY_EXISTS => return error.PathAlreadyExists,
+            ERROR.PATH_NOT_FOUND => return error.FileNotFound,
+            else => |err| return unexpectedError(err),
+        }
+    }
+}
+
+pub const RemoveDirectoryError = error{
+    FileNotFound,
+    DirNotEmpty,
+    Unexpected,
+};
+
+pub fn RemoveDirectory(dir_path: []const u8) RemoveDirectoryError!void {
+    const dir_path_w = try sliceToPrefixedFileW(dir_path);
+    return RemoveDirectoryW(&dir_path_w);
+}
+
+pub fn RemoveDirectoryW(dir_path_w: [*]const u16) RemoveDirectoryError!void {
+    if (kernel32.RemoveDirectoryW(dir_path_w) == 0) {
+        switch (kernel32.GetLastError()) {
+            ERROR.PATH_NOT_FOUND => return error.FileNotFound,
+            ERROR.DIR_NOT_EMPTY => return error.DirNotEmpty,
+            else => |err| return unexpectedError(err),
+        }
+    }
+}
+
+pub const GetStdHandleError = error{
+    NoStandardHandleAttached,
+    Unexpected,
+};
+
+pub fn GetStdHandle(handle_id: DWORD) GetStdHandleError!fd_t {
+    const handle = kernel32.GetStdHandle(handle_id) orelse return error.NoStandardHandleAttached;
+    if (handle == INVALID_HANDLE_VALUE) {
+        switch (kernel32.GetLastError()) {
+            else => |err| return unexpectedError(err),
+        }
+    }
+    return handle;
+}
+
+pub const SetFilePointerError = error{Unexpected};
+
+/// The SetFilePointerEx function with the `dwMoveMethod` parameter set to `FILE_BEGIN`.
+pub fn SetFilePointerEx_BEGIN(handle: HANDLE, offset: u64) SetFilePointerError!void {
+    // "The starting point is zero or the beginning of the file. If [FILE_BEGIN]
+    // is specified, then the liDistanceToMove parameter is interpreted as an unsigned value."
+    // https://docs.microsoft.com/en-us/windows/desktop/api/fileapi/nf-fileapi-setfilepointerex
+    const ipos = @bitCast(LARGE_INTEGER, offset);
+    if (kernel32.SetFilePointerEx(handle, ipos, null, FILE_BEGIN) == 0) {
+        switch (kernel32.GetLastError()) {
+            ERROR.INVALID_PARAMETER => unreachable,
+            ERROR.INVALID_HANDLE => unreachable,
+            else => |err| return unexpectedError(err),
+        }
+    }
+}
+
+/// The SetFilePointerEx function with the `dwMoveMethod` parameter set to `FILE_CURRENT`.
+pub fn SetFilePointerEx_CURRENT(handle: HANDLE, offset: i64) SetFilePointerError!void {
+    if (kernel32.SetFilePointerEx(handle, offset, null, FILE_CURRENT) == 0) {
+        switch (kernel32.GetLastError()) {
+            ERROR.INVALID_PARAMETER => unreachable,
+            ERROR.INVALID_HANDLE => unreachable,
+            else => |err| return unexpectedError(err),
+        }
+    }
+}
+
+/// The SetFilePointerEx function with the `dwMoveMethod` parameter set to `FILE_END`.
+pub fn SetFilePointerEx_END(handle: HANDLE, offset: i64) SetFilePointerError!void {
+    if (kernel32.SetFilePointerEx(handle, offset, null, FILE_END) == 0) {
+        switch (kernel32.GetLastError()) {
+            ERROR.INVALID_PARAMETER => unreachable,
+            ERROR.INVALID_HANDLE => unreachable,
+            else => |err| return unexpectedError(err),
+        }
+    }
+}
+
+/// The SetFilePointerEx function with parameters to get the current offset.
+pub fn SetFilePointerEx_CURRENT_get(handle: HANDLE) SetFilePointerError!u64 {
+    var result: LARGE_INTEGER = undefined;
+    if (kernel32.SetFilePointerEx(handle, 0, &result, FILE_CURRENT) == 0) {
+        switch (kernel32.GetLastError()) {
+            ERROR.INVALID_PARAMETER => unreachable,
+            ERROR.INVALID_HANDLE => unreachable,
+            else => |err| return unexpectedError(err),
+        }
+    }
+    // Based on the docs for FILE_BEGIN, it seems that the returned signed integer
+    // should be interpreted as an unsigned integer.
+    return @bitCast(u64, result);
+}
+
+pub const GetFinalPathNameByHandleError = error{
+    FileNotFound,
+    SystemResources,
+    NameTooLong,
+    Unexpected,
+};
+
+pub fn GetFinalPathNameByHandleW(
+    hFile: HANDLE,
+    buf_ptr: [*]u16,
+    buf_len: DWORD,
+    flags: DWORD,
+) GetFinalPathNameByHandleError!DWORD {
+    const rc = kernel32.GetFinalPathNameByHandleW(h_file, buf_ptr, buf.len, flags);
+    if (rc == 0) {
+        switch (kernel32.GetLastError()) {
+            ERROR.FILE_NOT_FOUND => return error.FileNotFound,
+            ERROR.PATH_NOT_FOUND => return error.FileNotFound,
+            ERROR.NOT_ENOUGH_MEMORY => return error.SystemResources,
+            ERROR.FILENAME_EXCED_RANGE => return error.NameTooLong,
+            ERROR.INVALID_PARAMETER => unreachable,
+            else => |err| return unexpectedError(err),
+        }
+    }
+    return rc;
+}
+
+pub const GetFileSizeError = error{Unexpected};
+
+pub fn GetFileSizeEx(hFile: HANDLE) GetFileSizeError!u64 {
+    var file_size: LARGE_INTEGER = undefined;
+    if (kernel32.GetFileSizeEx(hFile, &file_size) == 0) {
+        switch (kernel32.GetLastError()) {
+            else => |err| return unexpectedError(err),
+        }
+    }
+    return @bitCast(u64, file_size);
+}
+
+pub const GetFileAttributesError = error{
+    FileNotFound,
+    PermissionDenied,
+    Unexpected,
+};
+
+pub fn GetFileAttributes(filename: []const u8) GetFileAttributesError!DWORD {
+    const filename_w = try sliceToPrefixedFileW(filename);
+    return GetFileAttributesW(&filename_w);
+}
+
+pub fn GetFileAttributesW(lpFileName: [*]const u16) GetFileAttributesError!DWORD {
+    const rc = kernel32.GetFileAttributesW(path);
+    if (rc == INVALID_FILE_ATTRIBUTES) {
+        switch (kernel32.GetLastError()) {
+            ERROR.FILE_NOT_FOUND => return error.FileNotFound,
+            ERROR.PATH_NOT_FOUND => return error.FileNotFound,
+            ERROR.ACCESS_DENIED => return error.PermissionDenied,
+            else => |err| return unexpectedError(err),
+        }
+    }
+    return rc;
+}
+
 pub fn cStrToPrefixedFileW(s: [*]const u8) ![PATH_MAX_WIDE + 1]u16 {
     return sliceToPrefixedFileW(mem.toSliceConst(u8, s));
 }
std/os/child_process.zig → std/child_process.zig
@@ -1,4 +1,4 @@
-const std = @import("../std.zig");
+const std = @import("std.zig");
 const cstr = std.cstr;
 const unicode = std.unicode;
 const io = std.io;
@@ -12,10 +12,9 @@ const Buffer = std.Buffer;
 const builtin = @import("builtin");
 const Os = builtin.Os;
 const LinkedList = std.LinkedList;
-const windows_util = @import("windows/util.zig");
 const maxInt = std.math.maxInt;
 
-const is_windows = builtin.os == Os.windows;
+const is_windows = builtin.os == .windows;
 
 pub const ChildProcess = struct {
     pub pid: if (is_windows) void else i32,
std/coff.zig
@@ -91,7 +91,7 @@ pub const Coff = struct {
         } else
             return error.InvalidPEMagic;
 
-        try self.in_file.seekForward(skip_size);
+        try self.in_file.seekBy(skip_size);
 
         const number_of_rva_and_sizes = try in.readIntLittle(u32);
         if (number_of_rva_and_sizes != IMAGE_NUMBEROF_DIRECTORY_ENTRIES)
std/crypto.zig
@@ -32,6 +32,9 @@ pub const chaCha20With64BitNonce = import_chaCha20.chaCha20With64BitNonce;
 pub const Poly1305 = @import("crypto/poly1305.zig").Poly1305;
 pub const X25519 = @import("crypto/x25519.zig").X25519;
 
+const std = @import("std.zig");
+pub const randomBytes = std.posix.getrandom;
+
 test "crypto" {
     _ = @import("crypto/blake2.zig");
     _ = @import("crypto/chacha20.zig");
std/debug.zig
@@ -893,7 +893,7 @@ fn openSelfDebugInfoWindows(allocator: *mem.Allocator) !DebugInfo {
         if (this_record_len % 4 != 0) {
             const round_to_next_4 = (this_record_len | 0x3) + 1;
             const march_forward_bytes = round_to_next_4 - this_record_len;
-            try dbi.seekForward(march_forward_bytes);
+            try dbi.seekBy(march_forward_bytes);
             this_record_len += march_forward_bytes;
         }
 
@@ -1116,7 +1116,7 @@ fn printLineFromFileAnyOs(out_stream: var, line_info: LineInfo) !void {
     defer f.close();
     // TODO fstat and make sure that the file has the correct size
 
-    var buf: [os.page_size]u8 = undefined;
+    var buf: [mem.page_size]u8 = undefined;
     var line: usize = 1;
     var column: usize = 1;
     var abs_index: usize = 0;
@@ -1938,7 +1938,7 @@ fn getLineNumberInfoDwarf(di: *DwarfInfo, compile_unit: CompileUnit, target_addr
                 },
                 else => {
                     const fwd_amt = math.cast(isize, op_size - 1) catch return error.InvalidDebugInfo;
-                    try di.dwarf_seekable_stream.seekForward(fwd_amt);
+                    try di.dwarf_seekable_stream.seekBy(fwd_amt);
                 },
             }
         } else if (opcode >= opcode_base) {
@@ -1990,7 +1990,7 @@ fn getLineNumberInfoDwarf(di: *DwarfInfo, compile_unit: CompileUnit, target_addr
                 else => {
                     if (opcode - 1 >= standard_opcode_lengths.len) return error.InvalidDebugInfo;
                     const len_bytes = standard_opcode_lengths[opcode - 1];
-                    try di.dwarf_seekable_stream.seekForward(len_bytes);
+                    try di.dwarf_seekable_stream.seekBy(len_bytes);
                 },
             }
         }
std/dynamic_library.zig
@@ -125,7 +125,7 @@ pub const LinuxDynLib = struct {
         );
         errdefer _ = linux.munmap(addr, size);
 
-        const bytes = @intToPtr([*]align(std.os.page_size) u8, addr)[0..size];
+        const bytes = @intToPtr([*]align(mem.page_size) u8, addr)[0..size];
 
         return DynLib{
             .elf_lib = try ElfLib.init(bytes),
std/elf.zig
@@ -410,7 +410,7 @@ pub const Elf = struct {
         if (version_byte != 1) return error.InvalidFormat;
 
         // skip over padding
-        try seekable_stream.seekForward(9);
+        try seekable_stream.seekBy(9);
 
         elf.file_type = switch (try in.readInt(u16, elf.endian)) {
             1 => FileType.Relocatable,
@@ -447,7 +447,7 @@ pub const Elf = struct {
         }
 
         // skip over flags
-        try seekable_stream.seekForward(4);
+        try seekable_stream.seekBy(4);
 
         const header_size = try in.readInt(u16, elf.endian);
         if ((elf.is_64 and header_size != 64) or (!elf.is_64 and header_size != 52)) {
std/fs.zig
@@ -0,0 +1,840 @@
+const std = @import("std.zig");
+const os = std.os;
+const mem = std.mem;
+
+pub const path = @import("fs/path.zig");
+pub const File = @import("fs/file.zig").File;
+
+pub const symLink = os.symlink;
+pub const symLinkC = os.symlinkC;
+pub const deleteFile = os.unlink;
+pub const deleteFileC = os.unlinkC;
+pub const rename = os.rename;
+pub const renameC = os.renameC;
+pub const changeCurDir = os.chdir;
+pub const changeCurDirC = os.chdirC;
+pub const realpath = os.realpath;
+pub const realpathC = os.realpathC;
+pub const realpathW = os.realpathW;
+
+pub const getAppDataDir = @import("fs/get_app_data_dir.zig").getAppDataDir;
+pub const GetAppDataDirError = @import("fs/get_app_data_dir.zig").GetAppDataDirError;
+
+/// This represents the maximum size of a UTF-8 encoded file path.
+/// All file system operations which return a path are guaranteed to
+/// fit into a UTF-8 encoded array of this length.
+/// path being too long if it is this 0long
+pub const MAX_PATH_BYTES = switch (builtin.os) {
+    .linux, .macosx, .ios, .freebsd, .netbsd => posix.PATH_MAX,
+    // Each UTF-16LE character may be expanded to 3 UTF-8 bytes.
+    // If it would require 4 UTF-8 bytes, then there would be a surrogate
+    // pair in the UTF-16LE, and we (over)account 3 bytes for it that way.
+    // +1 for the null byte at the end, which can be encoded in 1 byte.
+    .windows => posix.PATH_MAX_WIDE * 3 + 1,
+    else => @compileError("Unsupported OS"),
+};
+
+/// The result is a slice of `out_buffer`, from index `0`.
+pub fn getCwd(out_buffer: *[MAX_PATH_BYTES]u8) ![]u8 {
+    return posix.getcwd(out_buffer);
+}
+
+/// Caller must free the returned memory.
+pub fn getCwdAlloc(allocator: *Allocator) ![]u8 {
+    var buf: [MAX_PATH_BYTES]u8 = undefined;
+    return mem.dupe(allocator, u8, try posix.getcwd(&buf));
+}
+
+test "getCwdAlloc" {
+    // at least call it so it gets compiled
+    var buf: [1000]u8 = undefined;
+    const allocator = &std.heap.FixedBufferAllocator.init(&buf).allocator;
+    _ = getCwdAlloc(allocator) catch {};
+}
+
+// here we replace the standard +/ with -_ so that it can be used in a file name
+const b64_fs_encoder = base64.Base64Encoder.init("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_", base64.standard_pad_char);
+
+/// TODO remove the allocator requirement from this API
+pub fn atomicSymLink(allocator: *Allocator, existing_path: []const u8, new_path: []const u8) !void {
+    if (symLink(existing_path, new_path)) {
+        return;
+    } else |err| switch (err) {
+        error.PathAlreadyExists => {},
+        else => return err, // TODO zig should know this set does not include PathAlreadyExists
+    }
+
+    const dirname = os.path.dirname(new_path) orelse ".";
+
+    var rand_buf: [12]u8 = undefined;
+    const tmp_path = try allocator.alloc(u8, dirname.len + 1 + base64.Base64Encoder.calcSize(rand_buf.len));
+    defer allocator.free(tmp_path);
+    mem.copy(u8, tmp_path[0..], dirname);
+    tmp_path[dirname.len] = os.path.sep;
+    while (true) {
+        try getRandomBytes(rand_buf[0..]);
+        b64_fs_encoder.encode(tmp_path[dirname.len + 1 ..], rand_buf);
+
+        if (symLink(existing_path, tmp_path)) {
+            return rename(tmp_path, new_path);
+        } else |err| switch (err) {
+            error.PathAlreadyExists => continue,
+            else => return err, // TODO zig should know this set does not include PathAlreadyExists
+        }
+    }
+}
+
+/// Guaranteed to be atomic. However until https://patchwork.kernel.org/patch/9636735/ is
+/// merged and readily available,
+/// there is a possibility of power loss or application termination leaving temporary files present
+/// in the same directory as dest_path.
+/// Destination file will have the same mode as the source file.
+pub fn copyFile(source_path: []const u8, dest_path: []const u8) !void {
+    var in_file = try os.File.openRead(source_path);
+    defer in_file.close();
+
+    const mode = try in_file.mode();
+    const in_stream = &in_file.inStream().stream;
+
+    var atomic_file = try AtomicFile.init(dest_path, mode);
+    defer atomic_file.deinit();
+
+    var buf: [mem.page_size]u8 = undefined;
+    while (true) {
+        const amt = try in_stream.readFull(buf[0..]);
+        try atomic_file.file.write(buf[0..amt]);
+        if (amt != buf.len) {
+            return atomic_file.finish();
+        }
+    }
+}
+
+/// Guaranteed to be atomic. However until https://patchwork.kernel.org/patch/9636735/ is
+/// merged and readily available,
+/// there is a possibility of power loss or application termination leaving temporary files present
+pub fn copyFileMode(source_path: []const u8, dest_path: []const u8, mode: File.Mode) !void {
+    var in_file = try os.File.openRead(source_path);
+    defer in_file.close();
+
+    var atomic_file = try AtomicFile.init(dest_path, mode);
+    defer atomic_file.deinit();
+
+    var buf: [mem.page_size]u8 = undefined;
+    while (true) {
+        const amt = try in_file.read(buf[0..]);
+        try atomic_file.file.write(buf[0..amt]);
+        if (amt != buf.len) {
+            return atomic_file.finish();
+        }
+    }
+}
+
+pub const AtomicFile = struct {
+    file: os.File,
+    tmp_path_buf: [MAX_PATH_BYTES]u8,
+    dest_path: []const u8,
+    finished: bool,
+
+    const InitError = os.File.OpenError;
+
+    /// dest_path must remain valid for the lifetime of AtomicFile
+    /// call finish to atomically replace dest_path with contents
+    /// TODO once we have null terminated pointers, use the
+    /// openWriteNoClobberN function
+    pub fn init(dest_path: []const u8, mode: File.Mode) InitError!AtomicFile {
+        const dirname = os.path.dirname(dest_path);
+        var rand_buf: [12]u8 = undefined;
+        const dirname_component_len = if (dirname) |d| d.len + 1 else 0;
+        const encoded_rand_len = comptime base64.Base64Encoder.calcSize(rand_buf.len);
+        const tmp_path_len = dirname_component_len + encoded_rand_len;
+        var tmp_path_buf: [MAX_PATH_BYTES]u8 = undefined;
+        if (tmp_path_len >= tmp_path_buf.len) return error.NameTooLong;
+
+        if (dirname) |dir| {
+            mem.copy(u8, tmp_path_buf[0..], dir);
+            tmp_path_buf[dir.len] = os.path.sep;
+        }
+
+        tmp_path_buf[tmp_path_len] = 0;
+
+        while (true) {
+            try getRandomBytes(rand_buf[0..]);
+            b64_fs_encoder.encode(tmp_path_buf[dirname_component_len..tmp_path_len], rand_buf);
+
+            const file = os.File.openWriteNoClobberC(&tmp_path_buf, mode) catch |err| switch (err) {
+                error.PathAlreadyExists => continue,
+                // TODO zig should figure out that this error set does not include PathAlreadyExists since
+                // it is handled in the above switch
+                else => return err,
+            };
+
+            return AtomicFile{
+                .file = file,
+                .tmp_path_buf = tmp_path_buf,
+                .dest_path = dest_path,
+                .finished = false,
+            };
+        }
+    }
+
+    /// always call deinit, even after successful finish()
+    pub fn deinit(self: *AtomicFile) void {
+        if (!self.finished) {
+            self.file.close();
+            deleteFileC(&self.tmp_path_buf) catch {};
+            self.finished = true;
+        }
+    }
+
+    pub fn finish(self: *AtomicFile) !void {
+        assert(!self.finished);
+        self.file.close();
+        self.finished = true;
+        if (is_posix) {
+            const dest_path_c = try toPosixPath(self.dest_path);
+            return renameC(&self.tmp_path_buf, &dest_path_c);
+        } else if (is_windows) {
+            const dest_path_w = try posix.sliceToPrefixedFileW(self.dest_path);
+            const tmp_path_w = try posix.cStrToPrefixedFileW(&self.tmp_path_buf);
+            return renameW(&tmp_path_w, &dest_path_w);
+        } else {
+            @compileError("Unsupported OS");
+        }
+    }
+};
+
+const default_new_dir_mode = 0o755;
+
+/// Create a new directory.
+pub fn makeDir(dir_path: []const u8) !void {
+    return posix.mkdir(dir_path, default_new_dir_mode);
+}
+
+/// Same as `makeDir` except the parameter is a null-terminated UTF8-encoded string.
+pub fn makeDirC(dir_path: [*]const u8) !void {
+    return posix.mkdirC(dir_path, default_new_dir_mode);
+}
+
+/// Same as `makeDir` except the parameter is a null-terminated UTF16LE-encoded string.
+pub fn makeDirW(dir_path: [*]const u16) !void {
+    return posix.mkdirW(dir_path, default_new_dir_mode);
+}
+
+/// Calls makeDir recursively to make an entire path. Returns success if the path
+/// already exists and is a directory.
+/// This function is not atomic, and if it returns an error, the file system may
+/// have been modified regardless.
+/// TODO determine if we can remove the allocator requirement from this function
+pub fn makePath(allocator: *Allocator, full_path: []const u8) !void {
+    const resolved_path = try path.resolve(allocator, [][]const u8{full_path});
+    defer allocator.free(resolved_path);
+
+    var end_index: usize = resolved_path.len;
+    while (true) {
+        makeDir(resolved_path[0..end_index]) catch |err| switch (err) {
+            error.PathAlreadyExists => {
+                // TODO stat the file and return an error if it's not a directory
+                // this is important because otherwise a dangling symlink
+                // could cause an infinite loop
+                if (end_index == resolved_path.len) return;
+            },
+            error.FileNotFound => {
+                // march end_index backward until next path component
+                while (true) {
+                    end_index -= 1;
+                    if (os.path.isSep(resolved_path[end_index])) break;
+                }
+                continue;
+            },
+            else => return err,
+        };
+        if (end_index == resolved_path.len) return;
+        // march end_index forward until next path component
+        while (true) {
+            end_index += 1;
+            if (end_index == resolved_path.len or os.path.isSep(resolved_path[end_index])) break;
+        }
+    }
+}
+
+/// Returns `error.DirNotEmpty` if the directory is not empty.
+/// To delete a directory recursively, see `deleteTree`.
+pub fn deleteDir(dir_path: []const u8) DeleteDirError!void {
+    return posix.rmdir(dir_path);
+}
+
+/// Same as `deleteDir` except the parameter is a null-terminated UTF8-encoded string.
+pub fn deleteDirC(dir_path: [*]const u8) DeleteDirError!void {
+    return posix.rmdirC(dir_path);
+}
+
+/// Same as `deleteDir` except the parameter is a null-terminated UTF16LE-encoded string.
+pub fn deleteDirW(dir_path: [*]const u16) DeleteDirError!void {
+    return posix.rmdirW(dir_path);
+}
+
+/// Whether ::full_path describes a symlink, file, or directory, this function
+/// removes it. If it cannot be removed because it is a non-empty directory,
+/// this function recursively removes its entries and then tries again.
+const DeleteTreeError = error{
+    OutOfMemory,
+    AccessDenied,
+    FileTooBig,
+    IsDir,
+    SymLinkLoop,
+    ProcessFdQuotaExceeded,
+    NameTooLong,
+    SystemFdQuotaExceeded,
+    NoDevice,
+    SystemResources,
+    NoSpaceLeft,
+    PathAlreadyExists,
+    ReadOnlyFileSystem,
+    NotDir,
+    FileNotFound,
+    FileSystem,
+    FileBusy,
+    DirNotEmpty,
+    DeviceBusy,
+
+    /// On Windows, file paths must be valid Unicode.
+    InvalidUtf8,
+
+    /// On Windows, file paths cannot contain these characters:
+    /// '/', '*', '?', '"', '<', '>', '|'
+    BadPathName,
+
+    Unexpected,
+};
+
+/// TODO determine if we can remove the allocator requirement
+pub fn deleteTree(allocator: *Allocator, full_path: []const u8) DeleteTreeError!void {
+    start_over: while (true) {
+        var got_access_denied = false;
+        // First, try deleting the item as a file. This way we don't follow sym links.
+        if (deleteFile(full_path)) {
+            return;
+        } else |err| switch (err) {
+            error.FileNotFound => return,
+            error.IsDir => {},
+            error.AccessDenied => got_access_denied = true,
+
+            error.InvalidUtf8,
+            error.SymLinkLoop,
+            error.NameTooLong,
+            error.SystemResources,
+            error.ReadOnlyFileSystem,
+            error.NotDir,
+            error.FileSystem,
+            error.FileBusy,
+            error.BadPathName,
+            error.Unexpected,
+            => return err,
+        }
+        {
+            var dir = Dir.open(allocator, full_path) catch |err| switch (err) {
+                error.NotDir => {
+                    if (got_access_denied) {
+                        return error.AccessDenied;
+                    }
+                    continue :start_over;
+                },
+
+                error.OutOfMemory,
+                error.AccessDenied,
+                error.FileTooBig,
+                error.IsDir,
+                error.SymLinkLoop,
+                error.ProcessFdQuotaExceeded,
+                error.NameTooLong,
+                error.SystemFdQuotaExceeded,
+                error.NoDevice,
+                error.FileNotFound,
+                error.SystemResources,
+                error.NoSpaceLeft,
+                error.PathAlreadyExists,
+                error.Unexpected,
+                error.InvalidUtf8,
+                error.BadPathName,
+                error.DeviceBusy,
+                => return err,
+            };
+            defer dir.close();
+
+            var full_entry_buf = ArrayList(u8).init(allocator);
+            defer full_entry_buf.deinit();
+
+            while (try dir.next()) |entry| {
+                try full_entry_buf.resize(full_path.len + entry.name.len + 1);
+                const full_entry_path = full_entry_buf.toSlice();
+                mem.copy(u8, full_entry_path, full_path);
+                full_entry_path[full_path.len] = path.sep;
+                mem.copy(u8, full_entry_path[full_path.len + 1 ..], entry.name);
+
+                try deleteTree(allocator, full_entry_path);
+            }
+        }
+        return deleteDir(full_path);
+    }
+}
+
+pub const Dir = struct {
+    handle: Handle,
+    allocator: *Allocator,
+
+    pub const Handle = switch (builtin.os) {
+        Os.macosx, Os.ios, Os.freebsd, Os.netbsd => struct {
+            fd: i32,
+            seek: i64,
+            buf: []u8,
+            index: usize,
+            end_index: usize,
+        },
+        Os.linux => struct {
+            fd: i32,
+            buf: []u8,
+            index: usize,
+            end_index: usize,
+        },
+        Os.windows => struct {
+            handle: windows.HANDLE,
+            find_file_data: windows.WIN32_FIND_DATAW,
+            first: bool,
+            name_data: [256]u8,
+        },
+        else => @compileError("unimplemented"),
+    };
+
+    pub const Entry = struct {
+        name: []const u8,
+        kind: Kind,
+
+        pub const Kind = enum {
+            BlockDevice,
+            CharacterDevice,
+            Directory,
+            NamedPipe,
+            SymLink,
+            File,
+            UnixDomainSocket,
+            Whiteout,
+            Unknown,
+        };
+    };
+
+    pub const OpenError = error{
+        FileNotFound,
+        NotDir,
+        AccessDenied,
+        FileTooBig,
+        IsDir,
+        SymLinkLoop,
+        ProcessFdQuotaExceeded,
+        NameTooLong,
+        SystemFdQuotaExceeded,
+        NoDevice,
+        SystemResources,
+        NoSpaceLeft,
+        PathAlreadyExists,
+        OutOfMemory,
+        InvalidUtf8,
+        BadPathName,
+        DeviceBusy,
+
+        Unexpected,
+    };
+
+    /// TODO remove the allocator requirement from this API
+    pub fn open(allocator: *Allocator, dir_path: []const u8) OpenError!Dir {
+        return Dir{
+            .allocator = allocator,
+            .handle = switch (builtin.os) {
+                Os.windows => blk: {
+                    var find_file_data: windows.WIN32_FIND_DATAW = undefined;
+                    const handle = try windows_util.windowsFindFirstFile(dir_path, &find_file_data);
+                    break :blk Handle{
+                        .handle = handle,
+                        .find_file_data = find_file_data, // TODO guaranteed copy elision
+                        .first = true,
+                        .name_data = undefined,
+                    };
+                },
+                Os.macosx, Os.ios, Os.freebsd, Os.netbsd => Handle{
+                    .fd = try posixOpen(
+                        dir_path,
+                        posix.O_RDONLY | posix.O_NONBLOCK | posix.O_DIRECTORY | posix.O_CLOEXEC,
+                        0,
+                    ),
+                    .seek = 0,
+                    .index = 0,
+                    .end_index = 0,
+                    .buf = []u8{},
+                },
+                Os.linux => Handle{
+                    .fd = try posixOpen(
+                        dir_path,
+                        posix.O_RDONLY | posix.O_DIRECTORY | posix.O_CLOEXEC,
+                        0,
+                    ),
+                    .index = 0,
+                    .end_index = 0,
+                    .buf = []u8{},
+                },
+                else => @compileError("unimplemented"),
+            },
+        };
+    }
+
+    pub fn close(self: *Dir) void {
+        switch (builtin.os) {
+            Os.windows => {
+                _ = windows.FindClose(self.handle.handle);
+            },
+            Os.macosx, Os.ios, Os.linux, Os.freebsd, Os.netbsd => {
+                self.allocator.free(self.handle.buf);
+                os.close(self.handle.fd);
+            },
+            else => @compileError("unimplemented"),
+        }
+    }
+
+    /// Memory such as file names referenced in this returned entry becomes invalid
+    /// with subsequent calls to next, as well as when this `Dir` is deinitialized.
+    pub fn next(self: *Dir) !?Entry {
+        switch (builtin.os) {
+            Os.linux => return self.nextLinux(),
+            Os.macosx, Os.ios => return self.nextDarwin(),
+            Os.windows => return self.nextWindows(),
+            Os.freebsd => return self.nextFreebsd(),
+            Os.netbsd => return self.nextFreebsd(),
+            else => @compileError("unimplemented"),
+        }
+    }
+
+    fn nextDarwin(self: *Dir) !?Entry {
+        start_over: while (true) {
+            if (self.handle.index >= self.handle.end_index) {
+                if (self.handle.buf.len == 0) {
+                    self.handle.buf = try self.allocator.alloc(u8, mem.page_size);
+                }
+
+                while (true) {
+                    const result = system.__getdirentries64(self.handle.fd, self.handle.buf.ptr, self.handle.buf.len, &self.handle.seek);
+                    if (result == 0) return null;
+                    if (result < 0) {
+                        switch (system.getErrno(result)) {
+                            posix.EBADF => unreachable,
+                            posix.EFAULT => unreachable,
+                            posix.ENOTDIR => unreachable,
+                            posix.EINVAL => {
+                                self.handle.buf = try self.allocator.realloc(self.handle.buf, self.handle.buf.len * 2);
+                                continue;
+                            },
+                            else => return unexpectedErrorPosix(err),
+                        }
+                    }
+                    self.handle.index = 0;
+                    self.handle.end_index = @intCast(usize, result);
+                    break;
+                }
+            }
+            const darwin_entry = @ptrCast(*align(1) posix.dirent, &self.handle.buf[self.handle.index]);
+            const next_index = self.handle.index + darwin_entry.d_reclen;
+            self.handle.index = next_index;
+
+            const name = @ptrCast([*]u8, &darwin_entry.d_name)[0..darwin_entry.d_namlen];
+
+            if (mem.eql(u8, name, ".") or mem.eql(u8, name, "..")) {
+                continue :start_over;
+            }
+
+            const entry_kind = switch (darwin_entry.d_type) {
+                posix.DT_BLK => Entry.Kind.BlockDevice,
+                posix.DT_CHR => Entry.Kind.CharacterDevice,
+                posix.DT_DIR => Entry.Kind.Directory,
+                posix.DT_FIFO => Entry.Kind.NamedPipe,
+                posix.DT_LNK => Entry.Kind.SymLink,
+                posix.DT_REG => Entry.Kind.File,
+                posix.DT_SOCK => Entry.Kind.UnixDomainSocket,
+                posix.DT_WHT => Entry.Kind.Whiteout,
+                else => Entry.Kind.Unknown,
+            };
+            return Entry{
+                .name = name,
+                .kind = entry_kind,
+            };
+        }
+    }
+
+    fn nextWindows(self: *Dir) !?Entry {
+        while (true) {
+            if (self.handle.first) {
+                self.handle.first = false;
+            } else {
+                if (!try posix.FindNextFile(self.handle.handle, &self.handle.find_file_data))
+                    return null;
+            }
+            const name_utf16le = mem.toSlice(u16, self.handle.find_file_data.cFileName[0..].ptr);
+            if (mem.eql(u16, name_utf16le, []u16{'.'}) or mem.eql(u16, name_utf16le, []u16{ '.', '.' }))
+                continue;
+            // Trust that Windows gives us valid UTF-16LE
+            const name_utf8_len = std.unicode.utf16leToUtf8(self.handle.name_data[0..], name_utf16le) catch unreachable;
+            const name_utf8 = self.handle.name_data[0..name_utf8_len];
+            const kind = blk: {
+                const attrs = self.handle.find_file_data.dwFileAttributes;
+                if (attrs & windows.FILE_ATTRIBUTE_DIRECTORY != 0) break :blk Entry.Kind.Directory;
+                if (attrs & windows.FILE_ATTRIBUTE_REPARSE_POINT != 0) break :blk Entry.Kind.SymLink;
+                if (attrs & windows.FILE_ATTRIBUTE_NORMAL != 0) break :blk Entry.Kind.File;
+                break :blk Entry.Kind.Unknown;
+            };
+            return Entry{
+                .name = name_utf8,
+                .kind = kind,
+            };
+        }
+    }
+
+    fn nextLinux(self: *Dir) !?Entry {
+        start_over: while (true) {
+            if (self.handle.index >= self.handle.end_index) {
+                if (self.handle.buf.len == 0) {
+                    self.handle.buf = try self.allocator.alloc(u8, mem.page_size);
+                }
+
+                while (true) {
+                    const result = posix.getdents64(self.handle.fd, self.handle.buf.ptr, self.handle.buf.len);
+                    const err = posix.getErrno(result);
+                    if (err > 0) {
+                        switch (err) {
+                            posix.EBADF, posix.EFAULT, posix.ENOTDIR => unreachable,
+                            posix.EINVAL => {
+                                self.handle.buf = try self.allocator.realloc(self.handle.buf, self.handle.buf.len * 2);
+                                continue;
+                            },
+                            else => return unexpectedErrorPosix(err),
+                        }
+                    }
+                    if (result == 0) return null;
+                    self.handle.index = 0;
+                    self.handle.end_index = result;
+                    break;
+                }
+            }
+            const linux_entry = @ptrCast(*align(1) posix.dirent64, &self.handle.buf[self.handle.index]);
+            const next_index = self.handle.index + linux_entry.d_reclen;
+            self.handle.index = next_index;
+
+            const name = cstr.toSlice(@ptrCast([*]u8, &linux_entry.d_name));
+
+            // skip . and .. entries
+            if (mem.eql(u8, name, ".") or mem.eql(u8, name, "..")) {
+                continue :start_over;
+            }
+
+            const entry_kind = switch (linux_entry.d_type) {
+                posix.DT_BLK => Entry.Kind.BlockDevice,
+                posix.DT_CHR => Entry.Kind.CharacterDevice,
+                posix.DT_DIR => Entry.Kind.Directory,
+                posix.DT_FIFO => Entry.Kind.NamedPipe,
+                posix.DT_LNK => Entry.Kind.SymLink,
+                posix.DT_REG => Entry.Kind.File,
+                posix.DT_SOCK => Entry.Kind.UnixDomainSocket,
+                else => Entry.Kind.Unknown,
+            };
+            return Entry{
+                .name = name,
+                .kind = entry_kind,
+            };
+        }
+    }
+
+    fn nextFreebsd(self: *Dir) !?Entry {
+        start_over: while (true) {
+            if (self.handle.index >= self.handle.end_index) {
+                if (self.handle.buf.len == 0) {
+                    self.handle.buf = try self.allocator.alloc(u8, mem.page_size);
+                }
+
+                while (true) {
+                    const result = posix.getdirentries(self.handle.fd, self.handle.buf.ptr, self.handle.buf.len, &self.handle.seek);
+                    const err = posix.getErrno(result);
+                    if (err > 0) {
+                        switch (err) {
+                            posix.EBADF, posix.EFAULT, posix.ENOTDIR => unreachable,
+                            posix.EINVAL => {
+                                self.handle.buf = try self.allocator.realloc(self.handle.buf, self.handle.buf.len * 2);
+                                continue;
+                            },
+                            else => return unexpectedErrorPosix(err),
+                        }
+                    }
+                    if (result == 0) return null;
+                    self.handle.index = 0;
+                    self.handle.end_index = result;
+                    break;
+                }
+            }
+            const freebsd_entry = @ptrCast(*align(1) posix.dirent, &self.handle.buf[self.handle.index]);
+            const next_index = self.handle.index + freebsd_entry.d_reclen;
+            self.handle.index = next_index;
+
+            const name = @ptrCast([*]u8, &freebsd_entry.d_name)[0..freebsd_entry.d_namlen];
+
+            if (mem.eql(u8, name, ".") or mem.eql(u8, name, "..")) {
+                continue :start_over;
+            }
+
+            const entry_kind = switch (freebsd_entry.d_type) {
+                posix.DT_BLK => Entry.Kind.BlockDevice,
+                posix.DT_CHR => Entry.Kind.CharacterDevice,
+                posix.DT_DIR => Entry.Kind.Directory,
+                posix.DT_FIFO => Entry.Kind.NamedPipe,
+                posix.DT_LNK => Entry.Kind.SymLink,
+                posix.DT_REG => Entry.Kind.File,
+                posix.DT_SOCK => Entry.Kind.UnixDomainSocket,
+                posix.DT_WHT => Entry.Kind.Whiteout,
+                else => Entry.Kind.Unknown,
+            };
+            return Entry{
+                .name = name,
+                .kind = entry_kind,
+            };
+        }
+    }
+};
+
+/// Read value of a symbolic link.
+/// The return value is a slice of buffer, from index `0`.
+pub fn readLink(buffer: *[posix.PATH_MAX]u8, pathname: []const u8) ![]u8 {
+    return posix.readlink(pathname, buffer);
+}
+
+/// Same as `readLink`, except the `pathname` parameter is null-terminated.
+pub fn readLinkC(buffer: *[posix.PATH_MAX]u8, pathname: [*]const u8) ![]u8 {
+    return posix.readlinkC(pathname, buffer);
+}
+
+pub fn openSelfExe() !os.File {
+    switch (builtin.os) {
+        Os.linux => return os.File.openReadC(c"/proc/self/exe"),
+        Os.macosx, Os.ios, Os.freebsd, Os.netbsd => {
+            var buf: [MAX_PATH_BYTES]u8 = undefined;
+            const self_exe_path = try selfExePath(&buf);
+            buf[self_exe_path.len] = 0;
+            return os.File.openReadC(self_exe_path.ptr);
+        },
+        Os.windows => {
+            var buf: [posix.PATH_MAX_WIDE]u16 = undefined;
+            const wide_slice = try selfExePathW(&buf);
+            return os.File.openReadW(wide_slice.ptr);
+        },
+        else => @compileError("Unsupported OS"),
+    }
+}
+
+test "openSelfExe" {
+    switch (builtin.os) {
+        Os.linux, Os.macosx, Os.ios, Os.windows, Os.freebsd => (try openSelfExe()).close(),
+        else => return error.SkipZigTest, // Unsupported OS.
+    }
+}
+
+pub fn selfExePathW(out_buffer: *[posix.PATH_MAX_WIDE]u16) ![]u16 {
+    const casted_len = @intCast(windows.DWORD, out_buffer.len); // TODO shouldn't need this cast
+    const rc = windows.GetModuleFileNameW(null, out_buffer, casted_len);
+    assert(rc <= out_buffer.len);
+    if (rc == 0) {
+        const err = windows.GetLastError();
+        switch (err) {
+            else => return windows.unexpectedError(err),
+        }
+    }
+    return out_buffer[0..rc];
+}
+
+/// Get the path to the current executable.
+/// If you only need the directory, use selfExeDirPath.
+/// If you only want an open file handle, use openSelfExe.
+/// This function may return an error if the current executable
+/// was deleted after spawning.
+/// Returned value is a slice of out_buffer.
+///
+/// On Linux, depends on procfs being mounted. If the currently executing binary has
+/// been deleted, the file path looks something like `/a/b/c/exe (deleted)`.
+/// TODO make the return type of this a null terminated pointer
+pub fn selfExePath(out_buffer: *[MAX_PATH_BYTES]u8) ![]u8 {
+    switch (builtin.os) {
+        Os.linux => return readLink(out_buffer, "/proc/self/exe"),
+        Os.freebsd => {
+            var mib = [4]c_int{ posix.CTL_KERN, posix.KERN_PROC, posix.KERN_PROC_PATHNAME, -1 };
+            var out_len: usize = out_buffer.len;
+            try posix.sysctl(&mib, out_buffer, &out_len, null, 0);
+            // TODO could this slice from 0 to out_len instead?
+            return mem.toSlice(u8, out_buffer);
+        },
+        Os.netbsd => {
+            var mib = [4]c_int{ posix.CTL_KERN, posix.KERN_PROC_ARGS, -1, posix.KERN_PROC_PATHNAME };
+            var out_len: usize = out_buffer.len;
+            try posix.sysctl(&mib, out_buffer, &out_len, null, 0);
+            // TODO could this slice from 0 to out_len instead?
+            return mem.toSlice(u8, out_buffer);
+        },
+        Os.windows => {
+            var utf16le_buf: [posix.PATH_MAX_WIDE]u16 = undefined;
+            const utf16le_slice = try selfExePathW(&utf16le_buf);
+            // Trust that Windows gives us valid UTF-16LE.
+            const end_index = std.unicode.utf16leToUtf8(out_buffer, utf16le_slice) catch unreachable;
+            return out_buffer[0..end_index];
+        },
+        Os.macosx, Os.ios => {
+            var u32_len: u32 = @intCast(u32, out_buffer.len); // TODO shouldn't need this cast
+            const rc = c._NSGetExecutablePath(out_buffer, &u32_len);
+            if (rc != 0) return error.NameTooLong;
+            return mem.toSlice(u8, out_buffer);
+        },
+        else => @compileError("Unsupported OS"),
+    }
+}
+
+/// `selfExeDirPath` except allocates the result on the heap.
+/// Caller owns returned memory.
+pub fn selfExeDirPathAlloc(allocator: *Allocator) ![]u8 {
+    var buf: [MAX_PATH_BYTES]u8 = undefined;
+    return mem.dupe(allocator, u8, try selfExeDirPath(&buf));
+}
+
+/// Get the directory path that contains the current executable.
+/// Returned value is a slice of out_buffer.
+pub fn selfExeDirPath(out_buffer: *[MAX_PATH_BYTES]u8) ![]const u8 {
+    switch (builtin.os) {
+        Os.linux => {
+            // If the currently executing binary has been deleted,
+            // the file path looks something like `/a/b/c/exe (deleted)`
+            // This path cannot be opened, but it's valid for determining the directory
+            // the executable was in when it was run.
+            const full_exe_path = try readLinkC(out_buffer, c"/proc/self/exe");
+            // Assume that /proc/self/exe has an absolute path, and therefore dirname
+            // will not return null.
+            return path.dirname(full_exe_path).?;
+        },
+        Os.windows, Os.macosx, Os.ios, Os.freebsd, Os.netbsd => {
+            const self_exe_path = try selfExePath(out_buffer);
+            // Assume that the OS APIs return absolute paths, and therefore dirname
+            // will not return null.
+            return path.dirname(self_exe_path).?;
+        },
+        else => @compileError("Unsupported OS"),
+    }
+}
+
+/// `realpath`, except caller must free the returned memory.
+pub fn realAlloc(allocator: *Allocator, pathname: []const u8) ![]u8 {
+    var buf: [MAX_PATH_BYTES]u8 = undefined;
+    return mem.dupe(allocator, u8, try realpath(pathname, &buf));
+}
+
+test "" {
+    _ = @import("fs/path.zig");
+    _ = @import("fs/file.zig");
+    _ = @import("fs/get_app_data_dir.zig");
+}
std/io.zig
@@ -49,7 +49,7 @@ pub fn InStream(comptime ReadError: type) type {
                     return;
                 }
 
-                const new_buf_size = math.min(max_size, actual_buf_len + os.page_size);
+                const new_buf_size = math.min(max_size, actual_buf_len + mem.page_size);
                 if (new_buf_size == actual_buf_len) return error.StreamTooLong;
                 try buffer.resize(new_buf_size);
             }
@@ -284,7 +284,7 @@ pub fn readFileAllocAligned(allocator: *mem.Allocator, path: []const u8, comptim
 }
 
 pub fn BufferedInStream(comptime Error: type) type {
-    return BufferedInStreamCustom(os.page_size, Error);
+    return BufferedInStreamCustom(mem.page_size, Error);
 }
 
 pub fn BufferedInStreamCustom(comptime buffer_size: usize, comptime Error: type) type {
@@ -757,7 +757,7 @@ test "io.CountingOutStream" {
 }
 
 pub fn BufferedOutStream(comptime Error: type) type {
-    return BufferedOutStreamCustom(os.page_size, Error);
+    return BufferedOutStreamCustom(mem.page_size, Error);
 }
 
 pub fn BufferedOutStreamCustom(comptime buffer_size: usize, comptime OutStreamError: type) type {
std/mem.zig
@@ -8,6 +8,11 @@ const meta = std.meta;
 const trait = meta.trait;
 const testing = std.testing;
 
+pub const page_size = switch (builtin.arch) {
+    .wasm32, .wasm64 => 64 * 1024,
+    else => 4 * 1024,
+};
+
 pub const Allocator = struct {
     pub const Error = error{OutOfMemory};
 
@@ -1457,3 +1462,21 @@ test "std.mem.alignForward" {
     testing.expect(alignForward(16, 8) == 16);
     testing.expect(alignForward(17, 8) == 24);
 }
+
+pub fn getBaseAddress() usize {
+    switch (builtin.os) {
+        .linux => {
+            const base = std.os.posix.getauxval(std.elf.AT_BASE);
+            if (base != 0) {
+                return base;
+            }
+            const phdr = std.os.posix.getauxval(std.elf.AT_PHDR);
+            return phdr - @sizeOf(std.elf.Ehdr);
+        },
+        .macosx, .freebsd, .netbsd => {
+            return @ptrToInt(&std.c._mh_execute_header);
+        },
+        .windows => return @ptrToInt(windows.GetModuleHandleW(null)),
+        else => @compileError("Unsupported OS"),
+    }
+}
std/os.zig
@@ -1,544 +1,449 @@
+// This file contains thin wrappers around OS-specific APIs, with these
+// specific goals in mind:
+// * Convert "errno"-style error codes into Zig errors.
+// * When null-terminated byte buffers are required, provide APIs which accept
+//   slices as well as APIs which accept null-terminated byte buffers. Same goes
+//   for UTF-16LE encoding.
+// * Where operating systems share APIs, e.g. POSIX, these thin wrappers provide
+//   cross platform abstracting.
+// * When there exists a corresponding libc function and linking libc, the libc
+//   implementation is used. Exceptions are made for known buggy areas of libc.
+//   On Linux libc can be side-stepped by using `std.os.linux.sys`.
+// * For Windows, this file represents the API that libc would provide for
+//   Windows. For thin wrappers around Windows-specific APIs, see `std.os.windows`.
+// Note: The Zig standard library does not support POSIX thread cancellation, and
+// in general EINTR is handled by trying again.
+
 const std = @import("std.zig");
 const builtin = @import("builtin");
-const Os = builtin.Os;
-const is_windows = builtin.os == Os.windows;
-const os = @This();
+const MAX_PATH_BYTES = std.fs.MAX_PATH_BYTES;
 
 comptime {
     assert(@import("std") == std); // You have to run the std lib tests with --override-std-dir
 }
 
-test "std.os" {
-    _ = @import("os/child_process.zig");
-    _ = @import("os/darwin.zig");
-    _ = @import("os/get_user_id.zig");
-    _ = @import("os/linux.zig");
-    _ = @import("os/path.zig");
-    _ = @import("os/test.zig");
-    _ = @import("os/time.zig");
-    _ = @import("os/windows.zig");
-    _ = @import("os/uefi.zig");
-    _ = @import("os/wasi.zig");
-    _ = @import("os/get_app_data_dir.zig");
-}
-
-pub const windows = @import("os/windows.zig");
 pub const darwin = @import("os/darwin.zig");
-pub const linux = @import("os/linux.zig");
 pub const freebsd = @import("os/freebsd.zig");
+pub const linux = @import("os/linux.zig");
 pub const netbsd = @import("os/netbsd.zig");
-pub const zen = @import("os/zen.zig");
 pub const uefi = @import("os/uefi.zig");
 pub const wasi = @import("os/wasi.zig");
+pub const windows = @import("os/windows.zig");
+pub const zen = @import("os/zen.zig");
 
+/// When linking libc, this is the C API. Otherwise, it is the OS-specific system interface.
 pub const system = if (builtin.link_libc) std.c else switch (builtin.os) {
-    .linux => linux,
     .macosx, .ios, .watchos, .tvos => darwin,
     .freebsd => freebsd,
+    .linux => linux,
     .netbsd => netbsd,
-    .zen => zen,
     .wasi => wasi,
     .windows => windows,
+    .zen => zen,
     else => struct {},
 };
 
-pub const net = @import("net.zig");
-
-pub const ChildProcess = @import("os/child_process.zig").ChildProcess;
-pub const path = @import("os/path.zig");
-pub const File = @import("os/file.zig").File;
-pub const time = @import("os/time.zig");
-
-pub const page_size = switch (builtin.arch) {
-    .wasm32, .wasm64 => 64 * 1024,
-    else => 4 * 1024,
-};
-
-pub const unexpected_error_tracing = builtin.mode == .Debug;
-pub const UnexpectedError = error{
-    /// The Operating System returned an undocumented error code.
-    Unexpected,
-};
-
-/// This represents the maximum size of a UTF-8 encoded file path.
-/// All file system operations which return a path are guaranteed to
-/// fit into a UTF-8 encoded array of this length.
-/// path being too long if it is this 0long
-pub const MAX_PATH_BYTES = switch (builtin.os) {
-    .linux, .macosx, .ios, .freebsd, .netbsd => posix.PATH_MAX,
-    // Each UTF-16LE character may be expanded to 3 UTF-8 bytes.
-    // If it would require 4 UTF-8 bytes, then there would be a surrogate
-    // pair in the UTF-16LE, and we (over)account 3 bytes for it that way.
-    // +1 for the null byte at the end, which can be encoded in 1 byte.
-    .windows => posix.PATH_MAX_WIDE * 3 + 1,
-    else => @compileError("Unsupported OS"),
-};
-
-pub const UserInfo = @import("os/get_user_id.zig").UserInfo;
-pub const getUserInfo = @import("os/get_user_id.zig").getUserInfo;
-
-const windows_util = @import("os/windows/util.zig");
-pub const windowsWaitSingle = windows_util.windowsWaitSingle;
-pub const windowsWrite = windows_util.windowsWrite;
-pub const windowsIsCygwinPty = windows_util.windowsIsCygwinPty;
-pub const windowsOpen = windows_util.windowsOpen;
-pub const windowsOpenW = windows_util.windowsOpenW;
-pub const createWindowsEnvBlock = windows_util.createWindowsEnvBlock;
-
-pub const WindowsCreateIoCompletionPortError = windows_util.WindowsCreateIoCompletionPortError;
-pub const windowsCreateIoCompletionPort = windows_util.windowsCreateIoCompletionPort;
-
-pub const WindowsPostQueuedCompletionStatusError = windows_util.WindowsPostQueuedCompletionStatusError;
-pub const windowsPostQueuedCompletionStatus = windows_util.windowsPostQueuedCompletionStatus;
-
-pub const WindowsWaitResult = windows_util.WindowsWaitResult;
-pub const windowsGetQueuedCompletionStatus = windows_util.windowsGetQueuedCompletionStatus;
-
-pub const WindowsWaitError = windows_util.WaitError;
-pub const WindowsOpenError = windows_util.OpenError;
-pub const WindowsWriteError = windows_util.WriteError;
-pub const WindowsReadError = windows_util.ReadError;
-
-pub const getAppDataDir = @import("os/get_app_data_dir.zig").getAppDataDir;
-pub const GetAppDataDirError = @import("os/get_app_data_dir.zig").GetAppDataDirError;
-
-pub const getRandomBytes = posix.getrandom;
-pub const abort = posix.abort;
-pub const exit = posix.exit;
-pub const symLink = posix.symlink;
-pub const symLinkC = posix.symlinkC;
-pub const symLinkW = posix.symlinkW;
-pub const deleteFile = posix.unlink;
-pub const deleteFileC = posix.unlinkC;
-pub const deleteFileW = posix.unlinkW;
-pub const rename = posix.rename;
-pub const renameC = posix.renameC;
-pub const renameW = posix.renameW;
-pub const changeCurDir = posix.chdir;
-pub const changeCurDirC = posix.chdirC;
-pub const changeCurDirW = posix.chdirW;
-
-const debug = std.debug;
-const assert = debug.assert;
-const testing = std.testing;
-
-const c = std.c;
-
-const mem = std.mem;
-const Allocator = mem.Allocator;
-
-const BufMap = std.BufMap;
-const cstr = std.cstr;
-
-const io = std.io;
-const base64 = std.base64;
-const ArrayList = std.ArrayList;
-const Buffer = std.Buffer;
-const math = std.math;
-
-pub fn getBaseAddress() usize {
-    switch (builtin.os) {
-        builtin.Os.linux => {
-            const base = linuxGetAuxVal(std.elf.AT_BASE);
-            if (base != 0) {
-                return base;
-            }
-            const phdr = linuxGetAuxVal(std.elf.AT_PHDR);
-            return phdr - @sizeOf(std.elf.Ehdr);
-        },
-        builtin.Os.macosx, builtin.Os.freebsd, builtin.Os.netbsd => {
-            return @ptrToInt(&std.c._mh_execute_header);
-        },
-        builtin.Os.windows => return @ptrToInt(windows.GetModuleHandleW(null)),
-        else => @compileError("Unsupported OS"),
+/// See also `getenv`.
+pub var environ: [][*]u8 = undefined;
+
+/// To obtain errno, call this function with the return value of the
+/// system function call. For some systems this will obtain the value directly
+/// from the return code; for others it will use a thread-local errno variable.
+/// Therefore, this function only returns a well-defined value when it is called
+/// directly after the system function call which one wants to learn the errno
+/// value of.
+pub const errno = system.getErrno;
+
+/// Closes the file descriptor.
+/// This function is not capable of returning any indication of failure. An
+/// application which wants to ensure writes have succeeded before closing
+/// must call `fsync` before `close`.
+/// Note: The Zig standard library does not support POSIX thread cancellation.
+pub fn close(fd: fd_t) void {
+    if (windows.is_the_target and !builtin.link_libc) {
+        return windows.CloseHandle(fd);
+    }
+    if (wasi.is_the_target) {
+        switch (wasi.fd_close(fd)) {
+            0 => return,
+            else => |err| return unexpectedErrno(err),
+        }
+    }
+    switch (errno(system.close(fd))) {
+        EBADF => unreachable, // Always a race condition.
+        EINTR => return, // This is still a success. See https://github.com/ziglang/zig/issues/2425
+        else => return,
     }
 }
 
-/// Caller must free result when done.
-/// TODO make this go through libc when we have it
-pub fn getEnvMap(allocator: *Allocator) !BufMap {
-    var result = BufMap.init(allocator);
-    errdefer result.deinit();
-
-    if (is_windows) {
-        const ptr = windows.GetEnvironmentStringsW() orelse return error.OutOfMemory;
-        defer assert(windows.FreeEnvironmentStringsW(ptr) != 0);
+pub const GetRandomError = error{};
 
-        var i: usize = 0;
+/// Obtain a series of random bytes. These bytes can be used to seed user-space
+/// random number generators or for cryptographic purposes.
+/// When linking against libc, this calls the
+/// appropriate OS-specific library call. Otherwise it uses the zig standard
+/// library implementation.
+pub fn getrandom(buf: []u8) GetRandomError!void {
+    if (windows.is_the_target) {
+        return windows.RtlGenRandom(buf);
+    }
+    if (linux.is_the_target) {
         while (true) {
-            if (ptr[i] == 0) return result;
-
-            const key_start = i;
-
-            while (ptr[i] != 0 and ptr[i] != '=') : (i += 1) {}
-            const key_w = ptr[key_start..i];
-            const key = try std.unicode.utf16leToUtf8Alloc(allocator, key_w);
-            errdefer allocator.free(key);
-
-            if (ptr[i] == '=') i += 1;
-
-            const value_start = i;
-            while (ptr[i] != 0) : (i += 1) {}
-            const value_w = ptr[value_start..i];
-            const value = try std.unicode.utf16leToUtf8Alloc(allocator, value_w);
-            errdefer allocator.free(value);
-
-            i += 1; // skip over null byte
-
-            try result.setMove(key, value);
-        }
-    } else if (builtin.os == Os.wasi) {
-        var environ_count: usize = undefined;
-        var environ_buf_size: usize = undefined;
-
-        const environ_sizes_get_ret = std.os.wasi.environ_sizes_get(&environ_count, &environ_buf_size);
-        if (environ_sizes_get_ret != os.wasi.ESUCCESS) {
-            return unexpectedErrorPosix(environ_sizes_get_ret);
-        }
-
-        // TODO: Verify that the documentation is incorrect
-        // https://github.com/WebAssembly/WASI/issues/27
-        var environ = try allocator.alloc(?[*]u8, environ_count + 1);
-        defer allocator.free(environ);
-        var environ_buf = try std.heap.wasm_allocator.alloc(u8, environ_buf_size);
-        defer allocator.free(environ_buf);
-
-        const environ_get_ret = std.os.wasi.environ_get(environ.ptr, environ_buf.ptr);
-        if (environ_get_ret != os.wasi.ESUCCESS) {
-            return unexpectedErrorPosix(environ_get_ret);
-        }
-
-        for (environ) |env| {
-            if (env) |ptr| {
-                const pair = mem.toSlice(u8, ptr);
-                var parts = mem.separate(pair, "=");
-                const key = parts.next().?;
-                const value = parts.next().?;
-                try result.set(key, value);
+            switch (errno(system.getrandom(buf.ptr, buf.len, 0))) {
+                0 => return,
+                EINVAL => unreachable,
+                EFAULT => unreachable,
+                EINTR => continue,
+                ENOSYS => return getRandomBytesDevURandom(buf),
+                else => |err| return unexpectedErrno(err),
             }
         }
-        return result;
-    } else {
-        for (posix.environ) |ptr| {
-            var line_i: usize = 0;
-            while (ptr[line_i] != 0 and ptr[line_i] != '=') : (line_i += 1) {}
-            const key = ptr[0..line_i];
-
-            var end_i: usize = line_i;
-            while (ptr[end_i] != 0) : (end_i += 1) {}
-            const value = ptr[line_i + 1 .. end_i];
-
-            try result.set(key, value);
+    }
+    if (wasi.is_the_target) {
+        switch (os.wasi.random_get(buf.ptr, buf.len)) {
+            0 => return,
+            else => |err| return unexpectedErrno(err),
         }
-        return result;
     }
+    return getRandomBytesDevURandom(buf);
 }
 
-test "os.getEnvMap" {
-    var env = try getEnvMap(std.debug.global_allocator);
-    defer env.deinit();
-}
-
-pub const GetEnvVarOwnedError = error{
-    OutOfMemory,
-    EnvironmentVariableNotFound,
-
-    /// See https://github.com/ziglang/zig/issues/1774
-    InvalidUtf8,
-};
-
-/// Caller must free returned memory.
-/// TODO make this go through libc when we have it
-pub fn getEnvVarOwned(allocator: *mem.Allocator, key: []const u8) GetEnvVarOwnedError![]u8 {
-    if (is_windows) {
-        const key_with_null = try std.unicode.utf8ToUtf16LeWithNull(allocator, key);
-        defer allocator.free(key_with_null);
-
-        var buf = try allocator.alloc(u16, 256);
-        defer allocator.free(buf);
-
-        while (true) {
-            const windows_buf_len = math.cast(windows.DWORD, buf.len) catch return error.OutOfMemory;
-            const result = windows.GetEnvironmentVariableW(key_with_null.ptr, buf.ptr, windows_buf_len);
-
-            if (result == 0) {
-                const err = windows.GetLastError();
-                return switch (err) {
-                    windows.ERROR.ENVVAR_NOT_FOUND => error.EnvironmentVariableNotFound,
-                    else => {
-                        windows.unexpectedError(err) catch {};
-                        return error.EnvironmentVariableNotFound;
-                    },
-                };
-            }
+fn getRandomBytesDevURandom(buf: []u8) !void {
+    const fd = try openC(c"/dev/urandom", O_RDONLY | O_CLOEXEC, 0);
+    defer close(fd);
 
-            if (result > buf.len) {
-                buf = try allocator.realloc(buf, result);
-                continue;
-            }
+    const stream = &os.File.openHandle(fd).inStream().stream;
+    stream.readNoEof(buf) catch return error.Unexpected;
+}
 
-            return std.unicode.utf16leToUtf8Alloc(allocator, buf) catch |err| switch (err) {
-                error.DanglingSurrogateHalf => return error.InvalidUtf8,
-                error.ExpectedSecondSurrogateHalf => return error.InvalidUtf8,
-                error.UnexpectedSecondSurrogateHalf => return error.InvalidUtf8,
-                error.OutOfMemory => return error.OutOfMemory,
-            };
+/// Causes abnormal process termination.
+/// If linking against libc, this calls the abort() libc function. Otherwise
+/// it raises SIGABRT followed by SIGKILL and finally lo
+pub fn abort() noreturn {
+    @setCold(true);
+    if (builtin.link_libc) {
+        system.abort();
+    }
+    if (windows.is_the_target) {
+        if (builtin.mode == .Debug) {
+            @breakpoint();
         }
-    } else {
-        const result = getEnvPosix(key) orelse return error.EnvironmentVariableNotFound;
-        return mem.dupe(allocator, u8, result);
+        windows.kernel32.ExitProcess(3);
+    }
+    if (builtin.os == .uefi) {
+        // TODO there must be a better thing to do here than loop forever
+        while (true) {}
     }
-}
-
-test "os.getEnvVarOwned" {
-    var ga = debug.global_allocator;
-    testing.expectError(error.EnvironmentVariableNotFound, getEnvVarOwned(ga, "BADENV"));
-}
 
-/// The result is a slice of `out_buffer`, from index `0`.
-pub fn getCwd(out_buffer: *[MAX_PATH_BYTES]u8) ![]u8 {
-    return posix.getcwd(out_buffer);
-}
+    raise(SIGABRT);
 
-/// Caller must free the returned memory.
-pub fn getCwdAlloc(allocator: *Allocator) ![]u8 {
-    var buf: [os.MAX_PATH_BYTES]u8 = undefined;
-    return mem.dupe(allocator, u8, try posix.getcwd(&buf));
-}
+    // TODO the rest of the implementation of abort() from musl libc here
 
-test "getCwdAlloc" {
-    // at least call it so it gets compiled
-    var buf: [1000]u8 = undefined;
-    const allocator = &std.heap.FixedBufferAllocator.init(&buf).allocator;
-    _ = getCwdAlloc(allocator) catch {};
+    raise(SIGKILL);
+    exit(127);
 }
 
-// here we replace the standard +/ with -_ so that it can be used in a file name
-const b64_fs_encoder = base64.Base64Encoder.init("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_", base64.standard_pad_char);
+pub const RaiseError = error{};
 
-/// TODO remove the allocator requirement from this API
-pub fn atomicSymLink(allocator: *Allocator, existing_path: []const u8, new_path: []const u8) !void {
-    if (symLink(existing_path, new_path)) {
-        return;
-    } else |err| switch (err) {
-        error.PathAlreadyExists => {},
-        else => return err, // TODO zig should know this set does not include PathAlreadyExists
+pub fn raise(sig: u8) RaiseError!void {
+    if (builtin.link_libc) {
+        switch (errno(system.raise(sig))) {
+            0 => return,
+            else => |err| return unexpectedErrno(err),
+        }
     }
 
-    const dirname = os.path.dirname(new_path) orelse ".";
-
-    var rand_buf: [12]u8 = undefined;
-    const tmp_path = try allocator.alloc(u8, dirname.len + 1 + base64.Base64Encoder.calcSize(rand_buf.len));
-    defer allocator.free(tmp_path);
-    mem.copy(u8, tmp_path[0..], dirname);
-    tmp_path[dirname.len] = os.path.sep;
-    while (true) {
-        try getRandomBytes(rand_buf[0..]);
-        b64_fs_encoder.encode(tmp_path[dirname.len + 1 ..], rand_buf);
-
-        if (symLink(existing_path, tmp_path)) {
-            return rename(tmp_path, new_path);
-        } else |err| switch (err) {
-            error.PathAlreadyExists => continue,
-            else => return err, // TODO zig should know this set does not include PathAlreadyExists
+    if (wasi.is_the_target) {
+        switch (wasi.proc_raise(SIGABRT)) {
+            0 => return,
+            else => |err| return unexpectedErrno(err),
         }
     }
-}
-
-/// Guaranteed to be atomic. However until https://patchwork.kernel.org/patch/9636735/ is
-/// merged and readily available,
-/// there is a possibility of power loss or application termination leaving temporary files present
-/// in the same directory as dest_path.
-/// Destination file will have the same mode as the source file.
-pub fn copyFile(source_path: []const u8, dest_path: []const u8) !void {
-    var in_file = try os.File.openRead(source_path);
-    defer in_file.close();
-
-    const mode = try in_file.mode();
-    const in_stream = &in_file.inStream().stream;
 
-    var atomic_file = try AtomicFile.init(dest_path, mode);
-    defer atomic_file.deinit();
+    if (windows.is_the_target) {
+        @compileError("TODO implement std.posix.raise for Windows");
+    }
 
-    var buf: [page_size]u8 = undefined;
-    while (true) {
-        const amt = try in_stream.readFull(buf[0..]);
-        try atomic_file.file.write(buf[0..amt]);
-        if (amt != buf.len) {
-            return atomic_file.finish();
-        }
+    var set: system.sigset_t = undefined;
+    system.blockAppSignals(&set);
+    const tid = system.syscall0(system.SYS_gettid);
+    const rc = system.syscall2(system.SYS_tkill, tid, sig);
+    system.restoreSignals(&set);
+    switch (errno(rc)) {
+        0 => return,
+        else => |err| return unexpectedErrno(err),
     }
 }
 
-/// Guaranteed to be atomic. However until https://patchwork.kernel.org/patch/9636735/ is
-/// merged and readily available,
-/// there is a possibility of power loss or application termination leaving temporary files present
-pub fn copyFileMode(source_path: []const u8, dest_path: []const u8, mode: File.Mode) !void {
-    var in_file = try os.File.openRead(source_path);
-    defer in_file.close();
-
-    var atomic_file = try AtomicFile.init(dest_path, mode);
-    defer atomic_file.deinit();
-
-    var buf: [page_size]u8 = undefined;
-    while (true) {
-        const amt = try in_file.read(buf[0..]);
-        try atomic_file.file.write(buf[0..amt]);
-        if (amt != buf.len) {
-            return atomic_file.finish();
-        }
+/// Exits the program cleanly with the specified status code.
+pub fn exit(status: u8) noreturn {
+    if (builtin.link_libc) {
+        system.exit(status);
+    }
+    if (windows.is_the_target) {
+        windows.kernel32.ExitProcess(status);
     }
+    if (wasi.is_the_target) {
+        wasi.proc_exit(status);
+    }
+    if (linux.is_the_target and !builtin.single_threaded) {
+        linux.exit_group(status);
+    }
+    system.exit(status);
 }
 
-pub const AtomicFile = struct {
-    file: os.File,
-    tmp_path_buf: [MAX_PATH_BYTES]u8,
-    dest_path: []const u8,
-    finished: bool,
+pub const ReadError = error{
+    InputOutput,
+    SystemResources,
+    IsDir,
+    OperationAborted,
+    BrokenPipe,
+    Unexpected,
+};
 
-    const InitError = os.File.OpenError;
+/// Returns the number of bytes that were read, which can be less than
+/// buf.len. If 0 bytes were read, that means EOF.
+/// This function is for blocking file descriptors only. For non-blocking, see
+/// `readAsync`.
+pub fn read(fd: fd_t, buf: []u8) ReadError!usize {
+    if (windows.is_the_target and !builtin.link_libc) {
+        return windows.ReadFile(fd, buf);
+    }
 
-    /// dest_path must remain valid for the lifetime of AtomicFile
-    /// call finish to atomically replace dest_path with contents
-    /// TODO once we have null terminated pointers, use the
-    /// openWriteNoClobberN function
-    pub fn init(dest_path: []const u8, mode: File.Mode) InitError!AtomicFile {
-        const dirname = os.path.dirname(dest_path);
-        var rand_buf: [12]u8 = undefined;
-        const dirname_component_len = if (dirname) |d| d.len + 1 else 0;
-        const encoded_rand_len = comptime base64.Base64Encoder.calcSize(rand_buf.len);
-        const tmp_path_len = dirname_component_len + encoded_rand_len;
-        var tmp_path_buf: [MAX_PATH_BYTES]u8 = undefined;
-        if (tmp_path_len >= tmp_path_buf.len) return error.NameTooLong;
+    if (wasi.is_the_target and !builtin.link_libc) {
+        const iovs = [1]was.iovec_t{wasi.iovec_t{
+            .buf = buf.ptr,
+            .buf_len = buf.len,
+        }};
 
-        if (dirname) |dir| {
-            mem.copy(u8, tmp_path_buf[0..], dir);
-            tmp_path_buf[dir.len] = os.path.sep;
+        var nread: usize = undefined;
+        switch (fd_read(fd, &iovs, iovs.len, &nread)) {
+            0 => return nread,
+            else => |err| return unexpectedErrno(err),
         }
+    }
 
-        tmp_path_buf[tmp_path_len] = 0;
-
-        while (true) {
-            try getRandomBytes(rand_buf[0..]);
-            b64_fs_encoder.encode(tmp_path_buf[dirname_component_len..tmp_path_len], rand_buf);
-
-            const file = os.File.openWriteNoClobberC(&tmp_path_buf, mode) catch |err| switch (err) {
-                error.PathAlreadyExists => continue,
-                // TODO zig should figure out that this error set does not include PathAlreadyExists since
-                // it is handled in the above switch
-                else => return err,
-            };
-
-            return AtomicFile{
-                .file = file,
-                .tmp_path_buf = tmp_path_buf,
-                .dest_path = dest_path,
-                .finished = false,
-            };
+    // Linux can return EINVAL when read amount is > 0x7ffff000
+    // See https://github.com/ziglang/zig/pull/743#issuecomment-363158274
+    // TODO audit this. Shawn Landden says that this is not actually true.
+    // if this logic should stay, move it to std.os.linux.sys
+    const max_buf_len = 0x7ffff000;
+
+    var index: usize = 0;
+    while (index < buf.len) {
+        const want_to_read = math.min(buf.len - index, usize(max_buf_len));
+        const rc = system.read(fd, buf.ptr + index, want_to_read);
+        switch (errno(rc)) {
+            0 => {
+                index += rc;
+                if (rc == want_to_read) continue;
+                // Read returned less than buf.len.
+                return index;
+            },
+            EINTR => continue,
+            EINVAL => unreachable,
+            EFAULT => unreachable,
+            EAGAIN => unreachable, // This function is for blocking reads.
+            EBADF => unreachable, // Always a race condition.
+            EIO => return error.InputOutput,
+            EISDIR => return error.IsDir,
+            ENOBUFS => return error.SystemResources,
+            ENOMEM => return error.SystemResources,
+            else => |err| return unexpectedErrno(err),
         }
     }
+    return index;
+}
 
-    /// always call deinit, even after successful finish()
-    pub fn deinit(self: *AtomicFile) void {
-        if (!self.finished) {
-            self.file.close();
-            deleteFileC(&self.tmp_path_buf) catch {};
-            self.finished = true;
+/// Number of bytes read is returned. Upon reading end-of-file, zero is returned.
+/// This function is for blocking file descriptors only. For non-blocking, see
+/// `preadvAsync`.
+pub fn preadv(fd: fd_t, iov: [*]const iovec, count: usize, offset: u64) ReadError!usize {
+    if (os.darwin.is_the_target) {
+        // Darwin does not have preadv but it does have pread.
+        var off: usize = 0;
+        var iov_i: usize = 0;
+        var inner_off: usize = 0;
+        while (true) {
+            const v = iov[iov_i];
+            const rc = darwin.pread(fd, v.iov_base + inner_off, v.iov_len - inner_off, offset + off);
+            const err = darwin.getErrno(rc);
+            switch (err) {
+                0 => {
+                    off += rc;
+                    inner_off += rc;
+                    if (inner_off == v.iov_len) {
+                        iov_i += 1;
+                        inner_off = 0;
+                        if (iov_i == count) {
+                            return off;
+                        }
+                    }
+                    if (rc == 0) return off; // EOF
+                    continue;
+                },
+                EINTR => continue,
+                EINVAL => unreachable,
+                EFAULT => unreachable,
+                ESPIPE => unreachable, // fd is not seekable
+                EAGAIN => unreachable, // This function is for blocking reads.
+                EBADF => unreachable, // always a race condition
+                EIO => return error.InputOutput,
+                EISDIR => return error.IsDir,
+                ENOBUFS => return error.SystemResources,
+                ENOMEM => return error.SystemResources,
+                else => return unexpectedErrno(err),
+            }
         }
     }
-
-    pub fn finish(self: *AtomicFile) !void {
-        assert(!self.finished);
-        self.file.close();
-        self.finished = true;
-        if (is_posix) {
-            const dest_path_c = try toPosixPath(self.dest_path);
-            return renameC(&self.tmp_path_buf, &dest_path_c);
-        } else if (is_windows) {
-            const dest_path_w = try posix.sliceToPrefixedFileW(self.dest_path);
-            const tmp_path_w = try posix.cStrToPrefixedFileW(&self.tmp_path_buf);
-            return renameW(&tmp_path_w, &dest_path_w);
-        } else {
-            @compileError("Unsupported OS");
+    while (true) {
+        const rc = system.preadv(fd, iov, count, offset);
+        switch (errno(rc)) {
+            0 => return rc,
+            EINTR => continue,
+            EINVAL => unreachable,
+            EFAULT => unreachable,
+            EAGAIN => unreachable, // This function is for blocking reads.
+            EBADF => unreachable, // always a race condition
+            EIO => return error.InputOutput,
+            EISDIR => return error.IsDir,
+            ENOBUFS => return error.SystemResources,
+            ENOMEM => return error.SystemResources,
+            else => |err| return unexpectedErrno(err),
         }
     }
-};
-
-const default_new_dir_mode = 0o755;
-
-/// Create a new directory.
-pub fn makeDir(dir_path: []const u8) !void {
-    return posix.mkdir(dir_path, default_new_dir_mode);
 }
 
-/// Same as `makeDir` except the parameter is a null-terminated UTF8-encoded string.
-pub fn makeDirC(dir_path: [*]const u8) !void {
-    return posix.mkdirC(dir_path, default_new_dir_mode);
-}
+pub const WriteError = error{
+    DiskQuota,
+    FileTooBig,
+    InputOutput,
+    NoSpaceLeft,
+    AccessDenied,
+    BrokenPipe,
+    SystemResources,
+    OperationAborted,
+    Unexpected,
+};
 
-/// Same as `makeDir` except the parameter is a null-terminated UTF16LE-encoded string.
-pub fn makeDirW(dir_path: [*]const u16) !void {
-    return posix.mkdirW(dir_path, default_new_dir_mode);
-}
+/// Write to a file descriptor. Keeps trying if it gets interrupted.
+/// This function is for blocking file descriptors only. For non-blocking, see
+/// `writeAsync`.
+pub fn write(fd: fd_t, bytes: []const u8) WriteError!void {
+    if (windows.is_the_target and !builtin.link_libc) {
+        return windows.WriteFile(fd, bytes);
+    }
 
-/// Calls makeDir recursively to make an entire path. Returns success if the path
-/// already exists and is a directory.
-/// This function is not atomic, and if it returns an error, the file system may
-/// have been modified regardless.
-/// TODO determine if we can remove the allocator requirement from this function
-pub fn makePath(allocator: *Allocator, full_path: []const u8) !void {
-    const resolved_path = try path.resolve(allocator, [][]const u8{full_path});
-    defer allocator.free(resolved_path);
+    if (wasi.is_the_target and !builtin.link_libc) {
+        const ciovs = [1]wasi.ciovec_t{wasi.ciovec_t{
+            .buf = bytes.ptr,
+            .buf_len = bytes.len,
+        }};
+        var nwritten: usize = undefined;
+        switch (fd_write(fd, &ciovs, ciovs.len, &nwritten)) {
+            0 => return,
+            else => |err| return unexpectedErrno(err),
+        }
+    }
 
-    var end_index: usize = resolved_path.len;
-    while (true) {
-        makeDir(resolved_path[0..end_index]) catch |err| switch (err) {
-            error.PathAlreadyExists => {
-                // TODO stat the file and return an error if it's not a directory
-                // this is important because otherwise a dangling symlink
-                // could cause an infinite loop
-                if (end_index == resolved_path.len) return;
-            },
-            error.FileNotFound => {
-                // march end_index backward until next path component
-                while (true) {
-                    end_index -= 1;
-                    if (os.path.isSep(resolved_path[end_index])) break;
-                }
+    // Linux can return EINVAL when write amount is > 0x7ffff000
+    // See https://github.com/ziglang/zig/pull/743#issuecomment-363165856
+    // TODO audit this. Shawn Landden says that this is not actually true.
+    // if this logic should stay, move it to std.os.linux.sys
+    const max_bytes_len = 0x7ffff000;
+
+    var index: usize = 0;
+    while (index < bytes.len) {
+        const amt_to_write = math.min(bytes.len - index, usize(max_bytes_len));
+        const rc = system.write(fd, bytes.ptr + index, amt_to_write);
+        switch (errno(rc)) {
+            0 => {
+                index += rc;
                 continue;
             },
-            else => return err,
-        };
-        if (end_index == resolved_path.len) return;
-        // march end_index forward until next path component
-        while (true) {
-            end_index += 1;
-            if (end_index == resolved_path.len or os.path.isSep(resolved_path[end_index])) break;
+            EINTR => continue,
+            EINVAL => unreachable,
+            EFAULT => unreachable,
+            EAGAIN => unreachable, // This function is for blocking writes.
+            EBADF => unreachable, // Always a race condition.
+            EDESTADDRREQ => unreachable, // `connect` was never called.
+            EDQUOT => return error.DiskQuota,
+            EFBIG => return error.FileTooBig,
+            EIO => return error.InputOutput,
+            ENOSPC => return error.NoSpaceLeft,
+            EPERM => return error.AccessDenied,
+            EPIPE => return error.BrokenPipe,
+            else => |err| return unexpectedErrno(err),
         }
     }
 }
 
-/// Returns `error.DirNotEmpty` if the directory is not empty.
-/// To delete a directory recursively, see `deleteTree`.
-pub fn deleteDir(dir_path: []const u8) DeleteDirError!void {
-    return posix.rmdir(dir_path);
-}
-
-/// Same as `deleteDir` except the parameter is a null-terminated UTF8-encoded string.
-pub fn deleteDirC(dir_path: [*]const u8) DeleteDirError!void {
-    return posix.rmdirC(dir_path);
-}
+/// Write multiple buffers to a file descriptor. Keeps trying if it gets interrupted.
+/// This function is for blocking file descriptors only. For non-blocking, see
+/// `pwritevAsync`.
+pub fn pwritev(fd: fd_t, iov: [*]const iovec_const, count: usize, offset: u64) WriteError!void {
+    if (darwin.is_the_target) {
+        // Darwin does not have pwritev but it does have pwrite.
+        var off: usize = 0;
+        var iov_i: usize = 0;
+        var inner_off: usize = 0;
+        while (true) {
+            const v = iov[iov_i];
+            const rc = darwin.pwrite(fd, v.iov_base + inner_off, v.iov_len - inner_off, offset + off);
+            const err = darwin.getErrno(rc);
+            switch (err) {
+                0 => {
+                    off += rc;
+                    inner_off += rc;
+                    if (inner_off == v.iov_len) {
+                        iov_i += 1;
+                        inner_off = 0;
+                        if (iov_i == count) {
+                            return;
+                        }
+                    }
+                    continue;
+                },
+                EINTR => continue,
+                ESPIPE => unreachable, // `fd` is not seekable.
+                EINVAL => unreachable,
+                EFAULT => unreachable,
+                EAGAIN => unreachable, // This function is for blocking writes.
+                EBADF => unreachable, // Always a race condition.
+                EDESTADDRREQ => unreachable, // `connect` was never called.
+                EDQUOT => return error.DiskQuota,
+                EFBIG => return error.FileTooBig,
+                EIO => return error.InputOutput,
+                ENOSPC => return error.NoSpaceLeft,
+                EPERM => return error.AccessDenied,
+                EPIPE => return error.BrokenPipe,
+                else => return unexpectedErrno(err),
+            }
+        }
+    }
 
-/// Same as `deleteDir` except the parameter is a null-terminated UTF16LE-encoded string.
-pub fn deleteDirW(dir_path: [*]const u16) DeleteDirError!void {
-    return posix.rmdirW(dir_path);
+    while (true) {
+        const rc = system.pwritev(fd, iov, count, offset);
+        switch (errno(rc)) {
+            0 => return,
+            EINTR => continue,
+            EINVAL => unreachable,
+            EFAULT => unreachable,
+            EAGAIN => unreachable, // This function is for blocking writes.
+            EBADF => unreachable, // Always a race condition.
+            EDESTADDRREQ => unreachable, // `connect` was never called.
+            EDQUOT => return error.DiskQuota,
+            EFBIG => return error.FileTooBig,
+            EIO => return error.InputOutput,
+            ENOSPC => return error.NoSpaceLeft,
+            EPERM => return error.AccessDenied,
+            EPIPE => return error.BrokenPipe,
+            else => |err| return unexpectedErrno(err),
+        }
+    }
 }
 
-/// Whether ::full_path describes a symlink, file, or directory, this function
-/// removes it. If it cannot be removed because it is a non-empty directory,
-/// this function recursively removes its entries and then tries again.
-const DeleteTreeError = error{
-    OutOfMemory,
+pub const OpenError = error{
     AccessDenied,
     FileTooBig,
     IsDir,
@@ -547,1239 +452,1876 @@ const DeleteTreeError = error{
     NameTooLong,
     SystemFdQuotaExceeded,
     NoDevice,
+    FileNotFound,
     SystemResources,
     NoSpaceLeft,
-    PathAlreadyExists,
-    ReadOnlyFileSystem,
     NotDir,
-    FileNotFound,
-    FileSystem,
-    FileBusy,
-    DirNotEmpty,
+    PathAlreadyExists,
     DeviceBusy,
-
-    /// On Windows, file paths must be valid Unicode.
-    InvalidUtf8,
-
-    /// On Windows, file paths cannot contain these characters:
-    /// '/', '*', '?', '"', '<', '>', '|'
-    BadPathName,
-
     Unexpected,
 };
 
-/// TODO determine if we can remove the allocator requirement
-pub fn deleteTree(allocator: *Allocator, full_path: []const u8) DeleteTreeError!void {
-    start_over: while (true) {
-        var got_access_denied = false;
-        // First, try deleting the item as a file. This way we don't follow sym links.
-        if (deleteFile(full_path)) {
-            return;
-        } else |err| switch (err) {
-            error.FileNotFound => return,
-            error.IsDir => {},
-            error.AccessDenied => got_access_denied = true,
-
-            error.InvalidUtf8,
-            error.SymLinkLoop,
-            error.NameTooLong,
-            error.SystemResources,
-            error.ReadOnlyFileSystem,
-            error.NotDir,
-            error.FileSystem,
-            error.FileBusy,
-            error.BadPathName,
-            error.Unexpected,
-            => return err,
-        }
-        {
-            var dir = Dir.open(allocator, full_path) catch |err| switch (err) {
-                error.NotDir => {
-                    if (got_access_denied) {
-                        return error.AccessDenied;
-                    }
-                    continue :start_over;
-                },
+/// Open and possibly create a file. Keeps trying if it gets interrupted.
+/// `file_path` needs to be copied in memory to add a null terminating byte.
+/// See also `openC`.
+pub fn open(file_path: []const u8, flags: u32, perm: usize) OpenError!fd_t {
+    const file_path_c = try toPosixPath(file_path);
+    return openC(&file_path_c, flags, perm);
+}
 
-                error.OutOfMemory,
-                error.AccessDenied,
-                error.FileTooBig,
-                error.IsDir,
-                error.SymLinkLoop,
-                error.ProcessFdQuotaExceeded,
-                error.NameTooLong,
-                error.SystemFdQuotaExceeded,
-                error.NoDevice,
-                error.FileNotFound,
-                error.SystemResources,
-                error.NoSpaceLeft,
-                error.PathAlreadyExists,
-                error.Unexpected,
-                error.InvalidUtf8,
-                error.BadPathName,
-                error.DeviceBusy,
-                => return err,
-            };
-            defer dir.close();
-
-            var full_entry_buf = ArrayList(u8).init(allocator);
-            defer full_entry_buf.deinit();
-
-            while (try dir.next()) |entry| {
-                try full_entry_buf.resize(full_path.len + entry.name.len + 1);
-                const full_entry_path = full_entry_buf.toSlice();
-                mem.copy(u8, full_entry_path, full_path);
-                full_entry_path[full_path.len] = path.sep;
-                mem.copy(u8, full_entry_path[full_path.len + 1 ..], entry.name);
-
-                try deleteTree(allocator, full_entry_path);
-            }
+/// Open and possibly create a file. Keeps trying if it gets interrupted.
+/// See also `open`.
+/// TODO https://github.com/ziglang/zig/issues/265
+pub fn openC(file_path: [*]const u8, flags: u32, perm: usize) OpenError!fd_t {
+    while (true) {
+        const rc = system.open(file_path, flags, perm);
+        switch (errno(rc)) {
+            0 => return @intCast(fd_t, rc),
+            EINTR => continue,
+
+            EFAULT => unreachable,
+            EINVAL => unreachable,
+            EACCES => return error.AccessDenied,
+            EFBIG => return error.FileTooBig,
+            EOVERFLOW => return error.FileTooBig,
+            EISDIR => return error.IsDir,
+            ELOOP => return error.SymLinkLoop,
+            EMFILE => return error.ProcessFdQuotaExceeded,
+            ENAMETOOLONG => return error.NameTooLong,
+            ENFILE => return error.SystemFdQuotaExceeded,
+            ENODEV => return error.NoDevice,
+            ENOENT => return error.FileNotFound,
+            ENOMEM => return error.SystemResources,
+            ENOSPC => return error.NoSpaceLeft,
+            ENOTDIR => return error.NotDir,
+            EPERM => return error.AccessDenied,
+            EEXIST => return error.PathAlreadyExists,
+            EBUSY => return error.DeviceBusy,
+            else => |err| return unexpectedErrno(err),
         }
-        return deleteDir(full_path);
     }
 }
 
-pub const Dir = struct {
-    handle: Handle,
-    allocator: *Allocator,
-
-    pub const Handle = switch (builtin.os) {
-        Os.macosx, Os.ios, Os.freebsd, Os.netbsd => struct {
-            fd: i32,
-            seek: i64,
-            buf: []u8,
-            index: usize,
-            end_index: usize,
-        },
-        Os.linux => struct {
-            fd: i32,
-            buf: []u8,
-            index: usize,
-            end_index: usize,
-        },
-        Os.windows => struct {
-            handle: windows.HANDLE,
-            find_file_data: windows.WIN32_FIND_DATAW,
-            first: bool,
-            name_data: [256]u8,
-        },
-        else => @compileError("unimplemented"),
-    };
-
-    pub const Entry = struct {
-        name: []const u8,
-        kind: Kind,
-
-        pub const Kind = enum {
-            BlockDevice,
-            CharacterDevice,
-            Directory,
-            NamedPipe,
-            SymLink,
-            File,
-            UnixDomainSocket,
-            Whiteout,
-            Unknown,
-        };
-    };
-
-    pub const OpenError = error{
-        FileNotFound,
-        NotDir,
-        AccessDenied,
-        FileTooBig,
-        IsDir,
-        SymLinkLoop,
-        ProcessFdQuotaExceeded,
-        NameTooLong,
-        SystemFdQuotaExceeded,
-        NoDevice,
-        SystemResources,
-        NoSpaceLeft,
-        PathAlreadyExists,
-        OutOfMemory,
-        InvalidUtf8,
-        BadPathName,
-        DeviceBusy,
-
-        Unexpected,
-    };
-
-    /// TODO remove the allocator requirement from this API
-    pub fn open(allocator: *Allocator, dir_path: []const u8) OpenError!Dir {
-        return Dir{
-            .allocator = allocator,
-            .handle = switch (builtin.os) {
-                Os.windows => blk: {
-                    var find_file_data: windows.WIN32_FIND_DATAW = undefined;
-                    const handle = try windows_util.windowsFindFirstFile(dir_path, &find_file_data);
-                    break :blk Handle{
-                        .handle = handle,
-                        .find_file_data = find_file_data, // TODO guaranteed copy elision
-                        .first = true,
-                        .name_data = undefined,
-                    };
-                },
-                Os.macosx, Os.ios, Os.freebsd, Os.netbsd => Handle{
-                    .fd = try posixOpen(
-                        dir_path,
-                        posix.O_RDONLY | posix.O_NONBLOCK | posix.O_DIRECTORY | posix.O_CLOEXEC,
-                        0,
-                    ),
-                    .seek = 0,
-                    .index = 0,
-                    .end_index = 0,
-                    .buf = []u8{},
-                },
-                Os.linux => Handle{
-                    .fd = try posixOpen(
-                        dir_path,
-                        posix.O_RDONLY | posix.O_DIRECTORY | posix.O_CLOEXEC,
-                        0,
-                    ),
-                    .index = 0,
-                    .end_index = 0,
-                    .buf = []u8{},
-                },
-                else => @compileError("unimplemented"),
-            },
-        };
-    }
-
-    pub fn close(self: *Dir) void {
-        switch (builtin.os) {
-            Os.windows => {
-                _ = windows.FindClose(self.handle.handle);
-            },
-            Os.macosx, Os.ios, Os.linux, Os.freebsd, Os.netbsd => {
-                self.allocator.free(self.handle.buf);
-                os.close(self.handle.fd);
-            },
-            else => @compileError("unimplemented"),
+pub fn dup2(old_fd: fd_t, new_fd: fd_t) !void {
+    while (true) {
+        switch (errno(system.dup2(old_fd, new_fd))) {
+            0 => return,
+            EBUSY, EINTR => continue,
+            EMFILE => return error.ProcessFdQuotaExceeded,
+            EINVAL => unreachable,
+            else => |err| return unexpectedErrno(err),
         }
     }
+}
 
-    /// Memory such as file names referenced in this returned entry becomes invalid
-    /// with subsequent calls to next, as well as when this `Dir` is deinitialized.
-    pub fn next(self: *Dir) !?Entry {
-        switch (builtin.os) {
-            Os.linux => return self.nextLinux(),
-            Os.macosx, Os.ios => return self.nextDarwin(),
-            Os.windows => return self.nextWindows(),
-            Os.freebsd => return self.nextFreebsd(),
-            Os.netbsd => return self.nextFreebsd(),
-            else => @compileError("unimplemented"),
+/// 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.
+/// `argv[0]` is the executable path.
+/// This function also uses the PATH environment variable to get the full path to the executable.
+/// TODO provide execveC which does not take an allocator
+pub fn execve(allocator: *Allocator, argv: []const []const u8, env_map: *const BufMap) !void {
+    const argv_buf = try allocator.alloc(?[*]u8, argv.len + 1);
+    mem.set(?[*]u8, argv_buf, null);
+    defer {
+        for (argv_buf) |arg| {
+            const arg_buf = if (arg) |ptr| cstr.toSlice(ptr) else break;
+            allocator.free(arg_buf);
         }
+        allocator.free(argv_buf);
     }
+    for (argv) |arg, i| {
+        const arg_buf = try allocator.alloc(u8, arg.len + 1);
+        @memcpy(arg_buf.ptr, arg.ptr, arg.len);
+        arg_buf[arg.len] = 0;
 
-    fn nextDarwin(self: *Dir) !?Entry {
-        start_over: while (true) {
-            if (self.handle.index >= self.handle.end_index) {
-                if (self.handle.buf.len == 0) {
-                    self.handle.buf = try self.allocator.alloc(u8, page_size);
-                }
-
-                while (true) {
-                    const result = system.__getdirentries64(self.handle.fd, self.handle.buf.ptr, self.handle.buf.len, &self.handle.seek);
-                    if (result == 0) return null;
-                    if (result < 0) {
-                        switch (system.getErrno(result)) {
-                            posix.EBADF => unreachable,
-                            posix.EFAULT => unreachable,
-                            posix.ENOTDIR => unreachable,
-                            posix.EINVAL => {
-                                self.handle.buf = try self.allocator.realloc(self.handle.buf, self.handle.buf.len * 2);
-                                continue;
-                            },
-                            else => return unexpectedErrorPosix(err),
-                        }
-                    }
-                    self.handle.index = 0;
-                    self.handle.end_index = @intCast(usize, result);
-                    break;
-                }
-            }
-            const darwin_entry = @ptrCast(*align(1) posix.dirent, &self.handle.buf[self.handle.index]);
-            const next_index = self.handle.index + darwin_entry.d_reclen;
-            self.handle.index = next_index;
-
-            const name = @ptrCast([*]u8, &darwin_entry.d_name)[0..darwin_entry.d_namlen];
+        argv_buf[i] = arg_buf.ptr;
+    }
+    argv_buf[argv.len] = null;
 
-            if (mem.eql(u8, name, ".") or mem.eql(u8, name, "..")) {
-                continue :start_over;
-            }
+    const envp_buf = try createNullDelimitedEnvMap(allocator, env_map);
+    defer freeNullDelimitedEnvMap(allocator, envp_buf);
 
-            const entry_kind = switch (darwin_entry.d_type) {
-                posix.DT_BLK => Entry.Kind.BlockDevice,
-                posix.DT_CHR => Entry.Kind.CharacterDevice,
-                posix.DT_DIR => Entry.Kind.Directory,
-                posix.DT_FIFO => Entry.Kind.NamedPipe,
-                posix.DT_LNK => Entry.Kind.SymLink,
-                posix.DT_REG => Entry.Kind.File,
-                posix.DT_SOCK => Entry.Kind.UnixDomainSocket,
-                posix.DT_WHT => Entry.Kind.Whiteout,
-                else => Entry.Kind.Unknown,
-            };
-            return Entry{
-                .name = name,
-                .kind = entry_kind,
-            };
-        }
+    const exe_path = argv[0];
+    if (mem.indexOfScalar(u8, exe_path, '/') != null) {
+        return execveErrnoToErr(errno(system.execve(argv_buf[0].?, argv_buf.ptr, envp_buf.ptr)));
     }
 
-    fn nextWindows(self: *Dir) !?Entry {
-        while (true) {
-            if (self.handle.first) {
-                self.handle.first = false;
-            } else {
-                if (!try posix.FindNextFile(self.handle.handle, &self.handle.find_file_data))
-                    return null;
-            }
-            const name_utf16le = mem.toSlice(u16, self.handle.find_file_data.cFileName[0..].ptr);
-            if (mem.eql(u16, name_utf16le, []u16{'.'}) or mem.eql(u16, name_utf16le, []u16{ '.', '.' }))
-                continue;
-            // Trust that Windows gives us valid UTF-16LE
-            const name_utf8_len = std.unicode.utf16leToUtf8(self.handle.name_data[0..], name_utf16le) catch unreachable;
-            const name_utf8 = self.handle.name_data[0..name_utf8_len];
-            const kind = blk: {
-                const attrs = self.handle.find_file_data.dwFileAttributes;
-                if (attrs & windows.FILE_ATTRIBUTE_DIRECTORY != 0) break :blk Entry.Kind.Directory;
-                if (attrs & windows.FILE_ATTRIBUTE_REPARSE_POINT != 0) break :blk Entry.Kind.SymLink;
-                if (attrs & windows.FILE_ATTRIBUTE_NORMAL != 0) break :blk Entry.Kind.File;
-                break :blk Entry.Kind.Unknown;
-            };
-            return Entry{
-                .name = name_utf8,
-                .kind = kind,
-            };
+    const PATH = getenv("PATH") orelse "/usr/local/bin:/bin/:/usr/bin";
+    // PATH.len because it is >= the largest search_path
+    // +1 for the / to join the search path and exe_path
+    // +1 for the null terminating byte
+    const path_buf = try allocator.alloc(u8, PATH.len + exe_path.len + 2);
+    defer allocator.free(path_buf);
+    var it = mem.tokenize(PATH, ":");
+    var seen_eacces = false;
+    var err: usize = undefined;
+    while (it.next()) |search_path| {
+        mem.copy(u8, path_buf, search_path);
+        path_buf[search_path.len] = '/';
+        mem.copy(u8, path_buf[search_path.len + 1 ..], exe_path);
+        path_buf[search_path.len + exe_path.len + 1] = 0;
+        err = errno(system.execve(path_buf.ptr, argv_buf.ptr, envp_buf.ptr));
+        assert(err > 0);
+        if (err == EACCES) {
+            seen_eacces = true;
+        } else if (err != ENOENT) {
+            return execveErrnoToErr(err);
         }
     }
-
-    fn nextLinux(self: *Dir) !?Entry {
-        start_over: while (true) {
-            if (self.handle.index >= self.handle.end_index) {
-                if (self.handle.buf.len == 0) {
-                    self.handle.buf = try self.allocator.alloc(u8, page_size);
-                }
-
-                while (true) {
-                    const result = posix.getdents64(self.handle.fd, self.handle.buf.ptr, self.handle.buf.len);
-                    const err = posix.getErrno(result);
-                    if (err > 0) {
-                        switch (err) {
-                            posix.EBADF, posix.EFAULT, posix.ENOTDIR => unreachable,
-                            posix.EINVAL => {
-                                self.handle.buf = try self.allocator.realloc(self.handle.buf, self.handle.buf.len * 2);
-                                continue;
-                            },
-                            else => return unexpectedErrorPosix(err),
-                        }
-                    }
-                    if (result == 0) return null;
-                    self.handle.index = 0;
-                    self.handle.end_index = result;
-                    break;
-                }
-            }
-            const linux_entry = @ptrCast(*align(1) posix.dirent64, &self.handle.buf[self.handle.index]);
-            const next_index = self.handle.index + linux_entry.d_reclen;
-            self.handle.index = next_index;
-
-            const name = cstr.toSlice(@ptrCast([*]u8, &linux_entry.d_name));
-
-            // skip . and .. entries
-            if (mem.eql(u8, name, ".") or mem.eql(u8, name, "..")) {
-                continue :start_over;
-            }
-
-            const entry_kind = switch (linux_entry.d_type) {
-                posix.DT_BLK => Entry.Kind.BlockDevice,
-                posix.DT_CHR => Entry.Kind.CharacterDevice,
-                posix.DT_DIR => Entry.Kind.Directory,
-                posix.DT_FIFO => Entry.Kind.NamedPipe,
-                posix.DT_LNK => Entry.Kind.SymLink,
-                posix.DT_REG => Entry.Kind.File,
-                posix.DT_SOCK => Entry.Kind.UnixDomainSocket,
-                else => Entry.Kind.Unknown,
-            };
-            return Entry{
-                .name = name,
-                .kind = entry_kind,
-            };
-        }
+    if (seen_eacces) {
+        err = EACCES;
     }
+    return execveErrnoToErr(err);
+}
 
-    fn nextFreebsd(self: *Dir) !?Entry {
-        start_over: while (true) {
-            if (self.handle.index >= self.handle.end_index) {
-                if (self.handle.buf.len == 0) {
-                    self.handle.buf = try self.allocator.alloc(u8, page_size);
-                }
-
-                while (true) {
-                    const result = posix.getdirentries(self.handle.fd, self.handle.buf.ptr, self.handle.buf.len, &self.handle.seek);
-                    const err = posix.getErrno(result);
-                    if (err > 0) {
-                        switch (err) {
-                            posix.EBADF, posix.EFAULT, posix.ENOTDIR => unreachable,
-                            posix.EINVAL => {
-                                self.handle.buf = try self.allocator.realloc(self.handle.buf, self.handle.buf.len * 2);
-                                continue;
-                            },
-                            else => return unexpectedErrorPosix(err),
-                        }
-                    }
-                    if (result == 0) return null;
-                    self.handle.index = 0;
-                    self.handle.end_index = result;
-                    break;
-                }
-            }
-            const freebsd_entry = @ptrCast(*align(1) posix.dirent, &self.handle.buf[self.handle.index]);
-            const next_index = self.handle.index + freebsd_entry.d_reclen;
-            self.handle.index = next_index;
-
-            const name = @ptrCast([*]u8, &freebsd_entry.d_name)[0..freebsd_entry.d_namlen];
-
-            if (mem.eql(u8, name, ".") or mem.eql(u8, name, "..")) {
-                continue :start_over;
-            }
-
-            const entry_kind = switch (freebsd_entry.d_type) {
-                posix.DT_BLK => Entry.Kind.BlockDevice,
-                posix.DT_CHR => Entry.Kind.CharacterDevice,
-                posix.DT_DIR => Entry.Kind.Directory,
-                posix.DT_FIFO => Entry.Kind.NamedPipe,
-                posix.DT_LNK => Entry.Kind.SymLink,
-                posix.DT_REG => Entry.Kind.File,
-                posix.DT_SOCK => Entry.Kind.UnixDomainSocket,
-                posix.DT_WHT => Entry.Kind.Whiteout,
-                else => Entry.Kind.Unknown,
-            };
-            return Entry{
-                .name = name,
-                .kind = entry_kind,
-            };
+pub fn createNullDelimitedEnvMap(allocator: *Allocator, env_map: *const BufMap) ![]?[*]u8 {
+    const envp_count = env_map.count();
+    const envp_buf = try allocator.alloc(?[*]u8, envp_count + 1);
+    mem.set(?[*]u8, envp_buf, null);
+    errdefer freeNullDelimitedEnvMap(allocator, envp_buf);
+    {
+        var it = env_map.iterator();
+        var i: usize = 0;
+        while (it.next()) |pair| : (i += 1) {
+            const env_buf = try allocator.alloc(u8, pair.key.len + pair.value.len + 2);
+            @memcpy(env_buf.ptr, pair.key.ptr, pair.key.len);
+            env_buf[pair.key.len] = '=';
+            @memcpy(env_buf.ptr + 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);
     }
-};
-
-/// Read value of a symbolic link.
-/// The return value is a slice of buffer, from index `0`.
-pub fn readLink(buffer: *[posix.PATH_MAX]u8, pathname: []const u8) ![]u8 {
-    return posix.readlink(pathname, buffer);
+    assert(envp_buf[envp_count] == null);
+    return envp_buf;
 }
 
-/// Same as `readLink`, except the `pathname` parameter is null-terminated.
-pub fn readLinkC(buffer: *[posix.PATH_MAX]u8, pathname: [*]const u8) ![]u8 {
-    return posix.readlinkC(pathname, buffer);
+pub fn freeNullDelimitedEnvMap(allocator: *Allocator, envp_buf: []?[*]u8) void {
+    for (envp_buf) |env| {
+        const env_buf = if (env) |ptr| ptr[0 .. cstr.len(ptr) + 1] else break;
+        allocator.free(env_buf);
+    }
+    allocator.free(envp_buf);
 }
 
-pub const ArgIteratorPosix = struct {
-    index: usize,
-    count: usize,
+pub const ExecveError = error{
+    SystemResources,
+    AccessDenied,
+    InvalidExe,
+    FileSystem,
+    IsDir,
+    FileNotFound,
+    NotDir,
+    FileBusy,
 
-    pub fn init() ArgIteratorPosix {
-        return ArgIteratorPosix{
-            .index = 0,
-            .count = raw.len,
-        };
-    }
+    Unexpected,
+};
 
-    pub fn next(self: *ArgIteratorPosix) ?[]const u8 {
-        if (self.index == self.count) return null;
+fn execveErrnoToErr(err: usize) ExecveError {
+    assert(err > 0);
+    switch (err) {
+        EFAULT => unreachable,
+        E2BIG => return error.SystemResources,
+        EMFILE => return error.ProcessFdQuotaExceeded,
+        ENAMETOOLONG => return error.NameTooLong,
+        ENFILE => return error.SystemFdQuotaExceeded,
+        ENOMEM => return error.SystemResources,
+        EACCES => return error.AccessDenied,
+        EPERM => return error.AccessDenied,
+        EINVAL => return error.InvalidExe,
+        ENOEXEC => return error.InvalidExe,
+        EIO => return error.FileSystem,
+        ELOOP => return error.FileSystem,
+        EISDIR => return error.IsDir,
+        ENOENT => return error.FileNotFound,
+        ENOTDIR => return error.NotDir,
+        ETXTBSY => return error.FileBusy,
+        else => return unexpectedErrno(err),
+    }
+}
 
-        const s = raw[self.index];
-        self.index += 1;
-        return cstr.toSlice(s);
+/// Get an environment variable.
+/// See also `getenvC`.
+/// TODO make this go through libc when we have it
+pub fn getenv(key: []const u8) ?[]const u8 {
+    for (environ) |ptr| {
+        var line_i: usize = 0;
+        while (ptr[line_i] != 0 and ptr[line_i] != '=') : (line_i += 1) {}
+        const this_key = ptr[0..line_i];
+        if (!mem.eql(u8, key, this_key)) continue;
+
+        var end_i: usize = line_i;
+        while (ptr[end_i] != 0) : (end_i += 1) {}
+        const this_value = ptr[line_i + 1 .. end_i];
+
+        return this_value;
     }
+    return null;
+}
 
-    pub fn skip(self: *ArgIteratorPosix) bool {
-        if (self.index == self.count) return false;
+/// Get an environment variable with a null-terminated name.
+/// See also `getenv`.
+/// TODO https://github.com/ziglang/zig/issues/265
+pub fn getenvC(key: [*]const u8) ?[]const u8 {
+    if (builtin.link_libc) {
+        const value = system.getenv(key) orelse return null;
+        return mem.toSliceConst(u8, value);
+    }
+    return getenv(mem.toSliceConst(u8, key));
+}
 
-        self.index += 1;
-        return true;
+/// See std.elf for the constants.
+pub fn getauxval(index: usize) usize {
+    if (builtin.link_libc) {
+        return usize(system.getauxval(index));
+    } else if (linux.elf_aux_maybe) |auxv| {
+        var i: usize = 0;
+        while (auxv[i].a_type != std.elf.AT_NULL) : (i += 1) {
+            if (auxv[i].a_type == index)
+                return auxv[i].a_un.a_val;
+        }
     }
+    return 0;
+}
 
-    /// This is marked as public but actually it's only meant to be used
-    /// internally by zig's startup code.
-    pub var raw: [][*]u8 = undefined;
+pub const GetCwdError = error{
+    NameTooLong,
+    CurrentWorkingDirectoryUnlinked,
+    Unexpected,
 };
 
-pub const ArgIteratorWindows = struct {
-    index: usize,
-    cmd_line: [*]const u8,
-    in_quote: bool,
-    quote_count: usize,
-    seen_quote_count: usize,
-
-    pub const NextError = error{OutOfMemory};
-
-    pub fn init() ArgIteratorWindows {
-        return initWithCmdLine(windows.GetCommandLineA());
-    }
-
-    pub fn initWithCmdLine(cmd_line: [*]const u8) ArgIteratorWindows {
-        return ArgIteratorWindows{
-            .index = 0,
-            .cmd_line = cmd_line,
-            .in_quote = false,
-            .quote_count = countQuotes(cmd_line),
-            .seen_quote_count = 0,
-        };
-    }
-
-    /// You must free the returned memory when done.
-    pub fn next(self: *ArgIteratorWindows, allocator: *Allocator) ?(NextError![]u8) {
-        // march forward over whitespace
-        while (true) : (self.index += 1) {
-            const byte = self.cmd_line[self.index];
-            switch (byte) {
-                0 => return null,
-                ' ', '\t' => continue,
-                else => break,
-            }
-        }
-
-        return self.internalNext(allocator);
+/// The result is a slice of out_buffer, indexed from 0.
+pub fn getcwd(out_buffer: []u8) GetCwdError![]u8 {
+    if (windows.is_the_target and !builtin.link_libc) {
+        return windows.GetCurrentDirectory(out_buffer);
     }
 
-    pub fn skip(self: *ArgIteratorWindows) bool {
-        // march forward over whitespace
-        while (true) : (self.index += 1) {
-            const byte = self.cmd_line[self.index];
-            switch (byte) {
-                0 => return false,
-                ' ', '\t' => continue,
-                else => break,
-            }
-        }
+    const err = if (builtin.link_libc) blk: {
+        break :blk if (system.getcwd(out_buffer.ptr, out_buffer.len)) |_| 0 else system._errno().*;
+    } else blk: {
+        break :blk errno(system.getcwd(out_buffer, out_buffer.len));
+    };
+    switch (err) {
+        0 => return mem.toSlice(u8, out_buffer),
+        EFAULT => unreachable,
+        EINVAL => unreachable,
+        ENOENT => return error.CurrentWorkingDirectoryUnlinked,
+        ERANGE => return error.NameTooLong,
+        else => |err| return unexpectedErrno(err),
+    }
+}
 
-        var backslash_count: usize = 0;
-        while (true) : (self.index += 1) {
-            const byte = self.cmd_line[self.index];
-            switch (byte) {
-                0 => return true,
-                '"' => {
-                    const quote_is_real = backslash_count % 2 == 0;
-                    if (quote_is_real) {
-                        self.seen_quote_count += 1;
-                    }
-                },
-                '\\' => {
-                    backslash_count += 1;
-                },
-                ' ', '\t' => {
-                    if (self.seen_quote_count % 2 == 0 or self.seen_quote_count == self.quote_count) {
-                        return true;
-                    }
-                    backslash_count = 0;
-                },
-                else => {
-                    backslash_count = 0;
-                    continue;
-                },
-            }
+pub const SymLinkError = error{
+    AccessDenied,
+    DiskQuota,
+    PathAlreadyExists,
+    FileSystem,
+    SymLinkLoop,
+    FileNotFound,
+    SystemResources,
+    NoSpaceLeft,
+    ReadOnlyFileSystem,
+    NotDir,
+    NameTooLong,
+    InvalidUtf8,
+    BadPathName,
+    Unexpected,
+};
+
+/// Creates a symbolic link named `sym_link_path` which contains the string `target_path`.
+/// A symbolic link (also known as a soft link) may point to an existing file or to a nonexistent
+/// one; the latter case is known as a dangling link.
+/// If `sym_link_path` exists, it will not be overwritten.
+/// See also `symlinkC` and `symlinkW`.
+pub fn symlink(target_path: []const u8, sym_link_path: []const u8) SymLinkError!void {
+    if (windows.is_the_target and !builtin.link_libc) {
+        const target_path_w = try windows.sliceToPrefixedFileW(target_path);
+        const sym_link_path_w = try windows.sliceToPrefixedFileW(sym_link_path);
+        return windows.CreateSymbolicLinkW(&sym_link_path_w, &target_path_w, 0);
+    } else {
+        const target_path_c = try toPosixPath(target_path);
+        const sym_link_path_c = try toPosixPath(sym_link_path);
+        return symlinkC(&target_path_c, &sym_link_path_c);
+    }
+}
+
+/// This is the same as `symlink` except the parameters are null-terminated pointers.
+/// See also `symlink`.
+pub fn symlinkC(target_path: [*]const u8, sym_link_path: [*]const u8) SymLinkError!void {
+    if (windows.is_the_target and !builtin.link_libc) {
+        const target_path_w = try windows.cStrToPrefixedFileW(target_path);
+        const sym_link_path_w = try windows.cStrToPrefixedFileW(sym_link_path);
+        return windows.CreateSymbolicLinkW(&sym_link_path_w, &target_path_w, 0);
+    }
+    switch (errno(system.symlink(target_path, sym_link_path))) {
+        0 => return,
+        EFAULT => unreachable,
+        EINVAL => unreachable,
+        EACCES => return error.AccessDenied,
+        EPERM => return error.AccessDenied,
+        EDQUOT => return error.DiskQuota,
+        EEXIST => return error.PathAlreadyExists,
+        EIO => return error.FileSystem,
+        ELOOP => return error.SymLinkLoop,
+        ENAMETOOLONG => return error.NameTooLong,
+        ENOENT => return error.FileNotFound,
+        ENOTDIR => return error.NotDir,
+        ENOMEM => return error.SystemResources,
+        ENOSPC => return error.NoSpaceLeft,
+        EROFS => return error.ReadOnlyFileSystem,
+        else => |err| return unexpectedErrno(err),
+    }
+}
+
+pub fn symlinkat(target_path: []const u8, newdirfd: fd_t, sym_link_path: []const u8) SymLinkError!void {
+    const target_path_c = try toPosixPath(target_path);
+    const sym_link_path_c = try toPosixPath(sym_link_path);
+    return symlinkatC(target_path_c, newdirfd, sym_link_path_c);
+}
+
+pub fn symlinkatC(target_path: [*]const u8, newdirfd: fd_t, sym_link_path: [*]const u8) SymLinkError!void {
+    switch (errno(system.symlinkat(target_path, newdirfd, sym_link_path))) {
+        0 => return,
+        EFAULT => unreachable,
+        EINVAL => unreachable,
+        EACCES => return error.AccessDenied,
+        EPERM => return error.AccessDenied,
+        EDQUOT => return error.DiskQuota,
+        EEXIST => return error.PathAlreadyExists,
+        EIO => return error.FileSystem,
+        ELOOP => return error.SymLinkLoop,
+        ENAMETOOLONG => return error.NameTooLong,
+        ENOENT => return error.FileNotFound,
+        ENOTDIR => return error.NotDir,
+        ENOMEM => return error.SystemResources,
+        ENOSPC => return error.NoSpaceLeft,
+        EROFS => return error.ReadOnlyFileSystem,
+        else => |err| return unexpectedErrno(err),
+    }
+}
+
+pub const UnlinkError = error{
+    FileNotFound,
+    AccessDenied,
+    FileBusy,
+    FileSystem,
+    IsDir,
+    SymLinkLoop,
+    NameTooLong,
+    NotDir,
+    SystemResources,
+    ReadOnlyFileSystem,
+    Unexpected,
+
+    /// On Windows, file paths must be valid Unicode.
+    InvalidUtf8,
+
+    /// On Windows, file paths cannot contain these characters:
+    /// '/', '*', '?', '"', '<', '>', '|'
+    BadPathName,
+};
+
+/// Delete a name and possibly the file it refers to.
+/// See also `unlinkC`.
+pub fn unlink(file_path: []const u8) UnlinkError!void {
+    if (windows.is_the_target and !builtin.link_libc) {
+        const file_path_w = try windows.sliceToPrefixedFileW(file_path);
+        return windows.DeleteFileW(&file_path_w);
+    } else {
+        const file_path_c = try toPosixPath(file_path);
+        return unlinkC(&file_path_c);
+    }
+}
+
+/// Same as `unlink` except the parameter is a null terminated UTF8-encoded string.
+pub fn unlinkC(file_path: [*]const u8) UnlinkError!void {
+    if (windows.is_the_target and !builtin.link_libc) {
+        const file_path_w = try windows.cStrToPrefixedFileW(file_path);
+        return windows.DeleteFileW(&file_path_w);
+    }
+    switch (errno(system.unlink(file_path))) {
+        0 => return,
+        EACCES => return error.AccessDenied,
+        EPERM => return error.AccessDenied,
+        EBUSY => return error.FileBusy,
+        EFAULT => unreachable,
+        EINVAL => unreachable,
+        EIO => return error.FileSystem,
+        EISDIR => return error.IsDir,
+        ELOOP => return error.SymLinkLoop,
+        ENAMETOOLONG => return error.NameTooLong,
+        ENOENT => return error.FileNotFound,
+        ENOTDIR => return error.NotDir,
+        ENOMEM => return error.SystemResources,
+        EROFS => return error.ReadOnlyFileSystem,
+        else => |err| return unexpectedErrno(err),
+    }
+}
+
+const RenameError = error{
+    AccessDenied,
+    FileBusy,
+    DiskQuota,
+    IsDir,
+    SymLinkLoop,
+    LinkQuotaExceeded,
+    NameTooLong,
+    FileNotFound,
+    NotDir,
+    SystemResources,
+    NoSpaceLeft,
+    PathAlreadyExists,
+    ReadOnlyFileSystem,
+    RenameAcrossMountPoints,
+    Unexpected,
+};
+
+/// Change the name or location of a file.
+pub fn rename(old_path: []const u8, new_path: []const u8) RenameError!void {
+    if (windows.is_the_target and !builtin.link_libc) {
+        const old_path_w = try windows.sliceToPrefixedFileW(old_path);
+        const new_path_w = try windows.sliceToPrefixedFileW(new_path);
+        const flags = windows.MOVEFILE_REPLACE_EXISTING | windows.MOVEFILE_WRITE_THROUGH;
+        return windows.MoveFileExW(&old_path_w, &new_path_w, flags);
+    } else {
+        const old_path_c = try toPosixPath(old_path);
+        const new_path_c = try toPosixPath(new_path);
+        return renameC(&old_path_c, &new_path_c);
+    }
+}
+
+/// Same as `rename` except the parameters are null-terminated byte arrays.
+pub fn renameC(old_path: [*]const u8, new_path: [*]const u8) RenameError!void {
+    if (windows.is_the_target and !builtin.link_libc) {
+        const old_path_w = try windows.cStrToPrefixedFileW(old_path);
+        const new_path_w = try windows.cStrToPrefixedFileW(new_path);
+        const flags = windows.MOVEFILE_REPLACE_EXISTING | windows.MOVEFILE_WRITE_THROUGH;
+        return windows.MoveFileExW(&old_path_w, &new_path_w, flags);
+    }
+    switch (errno(system.rename(old_path, new_path))) {
+        0 => return,
+        EACCES => return error.AccessDenied,
+        EPERM => return error.AccessDenied,
+        EBUSY => return error.FileBusy,
+        EDQUOT => return error.DiskQuota,
+        EFAULT => unreachable,
+        EINVAL => unreachable,
+        EISDIR => return error.IsDir,
+        ELOOP => return error.SymLinkLoop,
+        EMLINK => return error.LinkQuotaExceeded,
+        ENAMETOOLONG => return error.NameTooLong,
+        ENOENT => return error.FileNotFound,
+        ENOTDIR => return error.NotDir,
+        ENOMEM => return error.SystemResources,
+        ENOSPC => return error.NoSpaceLeft,
+        EEXIST => return error.PathAlreadyExists,
+        ENOTEMPTY => return error.PathAlreadyExists,
+        EROFS => return error.ReadOnlyFileSystem,
+        EXDEV => return error.RenameAcrossMountPoints,
+        else => |err| return unexpectedErrno(err),
+    }
+}
+
+pub const MakeDirError = error{
+    AccessDenied,
+    DiskQuota,
+    PathAlreadyExists,
+    SymLinkLoop,
+    LinkQuotaExceeded,
+    NameTooLong,
+    FileNotFound,
+    SystemResources,
+    NoSpaceLeft,
+    NotDir,
+    ReadOnlyFileSystem,
+    Unexpected,
+};
+
+/// Create a directory.
+/// `mode` is ignored on Windows.
+pub fn mkdir(dir_path: []const u8, mode: u32) MakeDirError!void {
+    if (windows.is_the_target and !builtin.link_libc) {
+        const dir_path_w = try windows.sliceToPrefixedFileW(dir_path);
+        return windows.CreateDirectoryW(&dir_path_w, null);
+    } else {
+        const dir_path_c = try toPosixPath(dir_path);
+        return mkdirC(&dir_path_c, mode);
+    }
+}
+
+/// Same as `mkdir` but the parameter is a null-terminated UTF8-encoded string.
+pub fn mkdirC(dir_path: [*]const u8, mode: u32) MakeDirError!void {
+    if (windows.is_the_target and !builtin.link_libc) {
+        const dir_path_w = try windows.cStrToPrefixedFileW(dir_path);
+        return windows.CreateDirectoryW(&dir_path_w, null);
+    }
+    switch (errno(system.mkdir(dir_path, mode))) {
+        0 => return,
+        EACCES => return error.AccessDenied,
+        EPERM => return error.AccessDenied,
+        EDQUOT => return error.DiskQuota,
+        EEXIST => return error.PathAlreadyExists,
+        EFAULT => unreachable,
+        ELOOP => return error.SymLinkLoop,
+        EMLINK => return error.LinkQuotaExceeded,
+        ENAMETOOLONG => return error.NameTooLong,
+        ENOENT => return error.FileNotFound,
+        ENOMEM => return error.SystemResources,
+        ENOSPC => return error.NoSpaceLeft,
+        ENOTDIR => return error.NotDir,
+        EROFS => return error.ReadOnlyFileSystem,
+        else => |err| return unexpectedErrno(err),
+    }
+}
+
+pub const DeleteDirError = error{
+    AccessDenied,
+    FileBusy,
+    SymLinkLoop,
+    NameTooLong,
+    FileNotFound,
+    SystemResources,
+    NotDir,
+    DirNotEmpty,
+    ReadOnlyFileSystem,
+    InvalidUtf8,
+    BadPathName,
+    Unexpected,
+};
+
+/// Deletes an empty directory.
+pub fn rmdir(dir_path: []const u8) DeleteDirError!void {
+    if (windows.is_the_target and !builtin.link_libc) {
+        const dir_path_w = try windows.sliceToPrefixedFileW(dir_path);
+        return windows.RemoveDirectoryW(&dir_path_w);
+    } else {
+        const dir_path_c = try toPosixPath(dir_path);
+        return rmdirC(&dir_path_c);
+    }
+}
+
+/// Same as `rmdir` except the parameter is null-terminated.
+pub fn rmdirC(dir_path: [*]const u8) DeleteDirError!void {
+    if (windows.is_the_target and !builtin.link_libc) {
+        const dir_path_w = try windows.cStrToPrefixedFileW(dir_path);
+        return windows.RemoveDirectoryW(&dir_path_w);
+    }
+    switch (errno(system.rmdir(dir_path))) {
+        0 => return,
+        EACCES => return error.AccessDenied,
+        EPERM => return error.AccessDenied,
+        EBUSY => return error.FileBusy,
+        EFAULT => unreachable,
+        EINVAL => unreachable,
+        ELOOP => return error.SymLinkLoop,
+        ENAMETOOLONG => return error.NameTooLong,
+        ENOENT => return error.FileNotFound,
+        ENOMEM => return error.SystemResources,
+        ENOTDIR => return error.NotDir,
+        EEXIST => return error.DirNotEmpty,
+        ENOTEMPTY => return error.DirNotEmpty,
+        EROFS => return error.ReadOnlyFileSystem,
+        else => |err| return unexpectedErrno(err),
+    }
+}
+
+pub const ChangeCurDirError = error{
+    AccessDenied,
+    FileSystem,
+    SymLinkLoop,
+    NameTooLong,
+    FileNotFound,
+    SystemResources,
+    NotDir,
+    Unexpected,
+};
+
+/// Changes the current working directory of the calling process.
+/// `dir_path` is recommended to be a UTF-8 encoded string.
+pub fn chdir(dir_path: []const u8) ChangeCurDirError!void {
+    if (windows.is_the_target and !builtin.link_libc) {
+        const dir_path_w = try windows.sliceToPrefixedFileW(dir_path);
+        @compileError("TODO implement chdir for Windows");
+    } else {
+        const dir_path_c = try toPosixPath(dir_path);
+        return chdirC(&dir_path_c);
+    }
+}
+
+/// Same as `chdir` except the parameter is null-terminated.
+pub fn chdirC(dir_path: [*]const u8) ChangeCurDirError!void {
+    if (windows.is_the_target and !builtin.link_libc) {
+        const dir_path_w = try windows.cStrToPrefixedFileW(dir_path);
+        @compileError("TODO implement chdir for Windows");
+    }
+    switch (errno(system.chdir(dir_path))) {
+        0 => return,
+        EACCES => return error.AccessDenied,
+        EFAULT => unreachable,
+        EIO => return error.FileSystem,
+        ELOOP => return error.SymLinkLoop,
+        ENAMETOOLONG => return error.NameTooLong,
+        ENOENT => return error.FileNotFound,
+        ENOMEM => return error.SystemResources,
+        ENOTDIR => return error.NotDir,
+        else => |err| return unexpectedErrno(err),
+    }
+}
+
+pub const ReadLinkError = error{
+    AccessDenied,
+    FileSystem,
+    SymLinkLoop,
+    NameTooLong,
+    FileNotFound,
+    SystemResources,
+    NotDir,
+    Unexpected,
+};
+
+/// Read value of a symbolic link.
+/// The return value is a slice of `out_buffer` from index 0.
+pub fn readlink(file_path: []const u8, out_buffer: []u8) ReadLinkError![]u8 {
+    if (windows.is_the_target and !builtin.link_libc) {
+        const file_path_w = try windows.sliceToPrefixedFileW(file_path);
+        @compileError("TODO implement readlink for Windows");
+    } else {
+        const file_path_c = try toPosixPath(file_path);
+        return readlinkC(&file_path_c, out_buffer);
+    }
+}
+
+/// Same as `readlink` except `file_path` is null-terminated.
+pub fn readlinkC(file_path: [*]const u8, out_buffer: []u8) ReadLinkError![]u8 {
+    if (windows.is_the_target and !builtin.link_libc) {
+        const file_path_w = try windows.cStrToPrefixedFileW(file_path);
+        @compileError("TODO implement readlink for Windows");
+    }
+    const rc = system.readlink(file_path, out_buffer.ptr, out_buffer.len);
+    switch (errno(rc)) {
+        0 => return out_buffer[0..rc],
+        EACCES => return error.AccessDenied,
+        EFAULT => unreachable,
+        EINVAL => unreachable,
+        EIO => return error.FileSystem,
+        ELOOP => return error.SymLinkLoop,
+        ENAMETOOLONG => return error.NameTooLong,
+        ENOENT => return error.FileNotFound,
+        ENOMEM => return error.SystemResources,
+        ENOTDIR => return error.NotDir,
+        else => |err| return unexpectedErrno(err),
+    }
+}
+
+pub const SetIdError = error{
+    ResourceLimitReached,
+    InvalidUserId,
+    PermissionDenied,
+    Unexpected,
+};
+
+pub fn setuid(uid: u32) SetIdError!void {
+    switch (errno(system.setuid(uid))) {
+        0 => return,
+        EAGAIN => return error.ResourceLimitReached,
+        EINVAL => return error.InvalidUserId,
+        EPERM => return error.PermissionDenied,
+        else => |err| return unexpectedErrno(err),
+    }
+}
+
+pub fn setreuid(ruid: u32, euid: u32) SetIdError!void {
+    switch (errno(system.setreuid(ruid, euid))) {
+        0 => return,
+        EAGAIN => return error.ResourceLimitReached,
+        EINVAL => return error.InvalidUserId,
+        EPERM => return error.PermissionDenied,
+        else => |err| return unexpectedErrno(err),
+    }
+}
+
+pub fn setgid(gid: u32) SetIdError!void {
+    switch (errno(system.setgid(gid))) {
+        0 => return,
+        EAGAIN => return error.ResourceLimitReached,
+        EINVAL => return error.InvalidUserId,
+        EPERM => return error.PermissionDenied,
+        else => |err| return unexpectedErrno(err),
+    }
+}
+
+pub fn setregid(rgid: u32, egid: u32) SetIdError!void {
+    switch (errno(system.setregid(rgid, egid))) {
+        0 => return,
+        EAGAIN => return error.ResourceLimitReached,
+        EINVAL => return error.InvalidUserId,
+        EPERM => return error.PermissionDenied,
+        else => |err| return unexpectedErrno(err),
+    }
+}
+
+/// Test whether a file descriptor refers to a terminal.
+pub fn isatty(handle: fd_t) bool {
+    if (builtin.link_libc) {
+        return system.isatty(handle) != 0;
+    }
+    if (windows.is_the_target) {
+        if (isCygwinPty(handle))
+            return true;
+
+        var out: windows.DWORD = undefined;
+        return windows.kernel32.GetConsoleMode(handle, &out) != 0;
+    }
+    if (wasi.is_the_target) {
+        @compileError("TODO implement std.os.posix.isatty for WASI");
+    }
+    if (linux.is_the_target) {
+        var wsz: system.winsize = undefined;
+        return system.syscall3(system.SYS_ioctl, @bitCast(usize, isize(handle)), TIOCGWINSZ, @ptrToInt(&wsz)) == 0;
+    }
+    unreachable;
+}
+
+pub fn isCygwinPty(handle: fd_t) bool {
+    if (!windows.is_the_target) return false;
+
+    const size = @sizeOf(windows.FILE_NAME_INFO);
+    var name_info_bytes align(@alignOf(windows.FILE_NAME_INFO)) = []u8{0} ** (size + windows.MAX_PATH);
+
+    if (windows.kernel32.GetFileInformationByHandleEx(
+        handle,
+        windows.FileNameInfo,
+        @ptrCast(*c_void, &name_info_bytes),
+        name_info_bytes.len,
+    ) == 0) {
+        return false;
+    }
+
+    const name_info = @ptrCast(*const windows.FILE_NAME_INFO, &name_info_bytes[0]);
+    const name_bytes = name_info_bytes[size .. size + usize(name_info.FileNameLength)];
+    const name_wide = @bytesToSlice(u16, name_bytes);
+    return mem.indexOf(u16, name_wide, []u16{ 'm', 's', 'y', 's', '-' }) != null or
+        mem.indexOf(u16, name_wide, []u16{ '-', 'p', 't', 'y' }) != null;
+}
+
+pub const SocketError = error{
+    /// Permission to create a socket of the specified type and/or
+    /// pro‐tocol is denied.
+    PermissionDenied,
+
+    /// The implementation does not support the specified address family.
+    AddressFamilyNotSupported,
+
+    /// Unknown protocol, or protocol family not available.
+    ProtocolFamilyNotAvailable,
+
+    /// The per-process limit on the number of open file descriptors has been reached.
+    ProcessFdQuotaExceeded,
+
+    /// The system-wide limit on the total number of open files has been reached.
+    SystemFdQuotaExceeded,
+
+    /// Insufficient memory is available. The socket cannot be created until sufficient
+    /// resources are freed.
+    SystemResources,
+
+    /// The protocol type or the specified protocol is not supported within this domain.
+    ProtocolNotSupported,
+
+    Unexpected,
+};
+
+pub fn socket(domain: u32, socket_type: u32, protocol: u32) SocketError!i32 {
+    const rc = system.socket(domain, socket_type, protocol);
+    switch (errno(rc)) {
+        0 => return @intCast(i32, rc),
+        EACCES => return error.PermissionDenied,
+        EAFNOSUPPORT => return error.AddressFamilyNotSupported,
+        EINVAL => return error.ProtocolFamilyNotAvailable,
+        EMFILE => return error.ProcessFdQuotaExceeded,
+        ENFILE => return error.SystemFdQuotaExceeded,
+        ENOBUFS, ENOMEM => return error.SystemResources,
+        EPROTONOSUPPORT => return error.ProtocolNotSupported,
+        else => |err| return unexpectedErrno(err),
+    }
+}
+
+pub const BindError = error{
+    /// The address is protected, and the user is not the superuser.
+    /// For UNIX domain sockets: Search permission is denied on  a  component
+    /// of  the  path  prefix.
+    AccessDenied,
+
+    /// The given address is already in use, or in the case of Internet domain sockets,
+    /// The  port number was specified as zero in the socket
+    /// address structure, but, upon attempting to bind to  an  ephemeral  port,  it  was
+    /// determined  that  all  port  numbers in the ephemeral port range are currently in
+    /// use.  See the discussion of /proc/sys/net/ipv4/ip_local_port_range ip(7).
+    AddressInUse,
+
+    /// A nonexistent interface was requested or the requested address was not local.
+    AddressNotAvailable,
+
+    /// Too many symbolic links were encountered in resolving addr.
+    SymLinkLoop,
+
+    /// addr is too long.
+    NameTooLong,
+
+    /// A component in the directory prefix of the socket pathname does not exist.
+    FileNotFound,
+
+    /// Insufficient kernel memory was available.
+    SystemResources,
+
+    /// A component of the path prefix is not a directory.
+    NotDir,
+
+    /// The socket inode would reside on a read-only filesystem.
+    ReadOnlyFileSystem,
+
+    Unexpected,
+};
+
+/// addr is `*const T` where T is one of the sockaddr
+pub fn bind(fd: i32, addr: *const sockaddr) BindError!void {
+    const rc = system.bind(fd, system, @sizeOf(sockaddr));
+    switch (errno(rc)) {
+        0 => return,
+        EACCES => return error.AccessDenied,
+        EADDRINUSE => return error.AddressInUse,
+        EBADF => unreachable, // always a race condition if this error is returned
+        EINVAL => unreachable,
+        ENOTSOCK => unreachable,
+        EADDRNOTAVAIL => return error.AddressNotAvailable,
+        EFAULT => unreachable,
+        ELOOP => return error.SymLinkLoop,
+        ENAMETOOLONG => return error.NameTooLong,
+        ENOENT => return error.FileNotFound,
+        ENOMEM => return error.SystemResources,
+        ENOTDIR => return error.NotDir,
+        EROFS => return error.ReadOnlyFileSystem,
+        else => |err| return unexpectedErrno(err),
+    }
+}
+
+const ListenError = error{
+    /// Another socket is already listening on the same port.
+    /// For Internet domain sockets, the  socket referred to by sockfd had not previously
+    /// been bound to an address and, upon attempting to bind it to an ephemeral port, it
+    /// was determined that all port numbers in the ephemeral port range are currently in
+    /// use.  See the discussion of /proc/sys/net/ipv4/ip_local_port_range in ip(7).
+    AddressInUse,
+
+    /// The file descriptor sockfd does not refer to a socket.
+    FileDescriptorNotASocket,
+
+    /// The socket is not of a type that supports the listen() operation.
+    OperationNotSupported,
+
+    Unexpected,
+};
+
+pub fn listen(sockfd: i32, backlog: u32) ListenError!void {
+    const rc = system.listen(sockfd, backlog);
+    switch (errno(rc)) {
+        0 => return,
+        EADDRINUSE => return error.AddressInUse,
+        EBADF => unreachable,
+        ENOTSOCK => return error.FileDescriptorNotASocket,
+        EOPNOTSUPP => return error.OperationNotSupported,
+        else => |err| return unexpectedErrno(err),
+    }
+}
+
+pub const AcceptError = error{
+    ConnectionAborted,
+
+    /// The per-process limit on the number of open file descriptors has been reached.
+    ProcessFdQuotaExceeded,
+
+    /// The system-wide limit on the total number of open files has been reached.
+    SystemFdQuotaExceeded,
+
+    /// Not enough free memory.  This often means that the memory allocation  is  limited
+    /// by the socket buffer limits, not by the system memory.
+    SystemResources,
+
+    /// The file descriptor sockfd does not refer to a socket.
+    FileDescriptorNotASocket,
+
+    /// The referenced socket is not of type SOCK_STREAM.
+    OperationNotSupported,
+
+    ProtocolFailure,
+
+    /// Firewall rules forbid connection.
+    BlockedByFirewall,
+
+    Unexpected,
+};
+
+/// Accept a connection on a socket. `fd` must be opened in blocking mode.
+/// See also `accept4_async`.
+pub fn accept4(fd: i32, addr: *sockaddr, flags: u32) AcceptError!i32 {
+    while (true) {
+        var sockaddr_size = u32(@sizeOf(sockaddr));
+        const rc = system.accept4(fd, addr, &sockaddr_size, flags);
+        switch (errno(rc)) {
+            0 => return @intCast(i32, rc),
+            EINTR => continue,
+            else => |err| return unexpectedErrno(err),
+
+            EAGAIN => unreachable, // This function is for blocking only.
+            EBADF => unreachable, // always a race condition
+            ECONNABORTED => return error.ConnectionAborted,
+            EFAULT => unreachable,
+            EINVAL => unreachable,
+            EMFILE => return error.ProcessFdQuotaExceeded,
+            ENFILE => return error.SystemFdQuotaExceeded,
+            ENOBUFS => return error.SystemResources,
+            ENOMEM => return error.SystemResources,
+            ENOTSOCK => return error.FileDescriptorNotASocket,
+            EOPNOTSUPP => return error.OperationNotSupported,
+            EPROTO => return error.ProtocolFailure,
+            EPERM => return error.BlockedByFirewall,
+        }
+    }
+}
+
+/// This is the same as `accept4` except `fd` is expected to be non-blocking.
+/// Returns -1 if would block.
+pub fn accept4_async(fd: i32, addr: *sockaddr, flags: u32) AcceptError!i32 {
+    while (true) {
+        var sockaddr_size = u32(@sizeOf(sockaddr));
+        const rc = system.accept4(fd, addr, &sockaddr_size, flags);
+        switch (errno(rc)) {
+            0 => return @intCast(i32, rc),
+            EINTR => continue,
+            else => |err| return unexpectedErrno(err),
+
+            EAGAIN => return -1,
+            EBADF => unreachable, // always a race condition
+            ECONNABORTED => return error.ConnectionAborted,
+            EFAULT => unreachable,
+            EINVAL => unreachable,
+            EMFILE => return error.ProcessFdQuotaExceeded,
+            ENFILE => return error.SystemFdQuotaExceeded,
+            ENOBUFS => return error.SystemResources,
+            ENOMEM => return error.SystemResources,
+            ENOTSOCK => return error.FileDescriptorNotASocket,
+            EOPNOTSUPP => return error.OperationNotSupported,
+            EPROTO => return error.ProtocolFailure,
+            EPERM => return error.BlockedByFirewall,
         }
     }
+}
+
+pub const EpollCreateError = error{
+    /// The  per-user   limit   on   the   number   of   epoll   instances   imposed   by
+    /// /proc/sys/fs/epoll/max_user_instances  was encountered.  See epoll(7) for further
+    /// details.
+    /// Or, The per-process limit on the number of open file descriptors has been reached.
+    ProcessFdQuotaExceeded,
+
+    /// The system-wide limit on the total number of open files has been reached.
+    SystemFdQuotaExceeded,
+
+    /// There was insufficient memory to create the kernel object.
+    SystemResources,
+
+    Unexpected,
+};
+
+pub fn epoll_create1(flags: u32) EpollCreateError!i32 {
+    const rc = system.epoll_create1(flags);
+    switch (errno(rc)) {
+        0 => return @intCast(i32, rc),
+        else => |err| return unexpectedErrno(err),
+
+        EINVAL => unreachable,
+        EMFILE => return error.ProcessFdQuotaExceeded,
+        ENFILE => return error.SystemFdQuotaExceeded,
+        ENOMEM => return error.SystemResources,
+    }
+}
 
-    fn internalNext(self: *ArgIteratorWindows, allocator: *Allocator) NextError![]u8 {
-        var buf = try Buffer.initSize(allocator, 0);
-        defer buf.deinit();
-
-        var backslash_count: usize = 0;
-        while (true) : (self.index += 1) {
-            const byte = self.cmd_line[self.index];
-            switch (byte) {
-                0 => return buf.toOwnedSlice(),
-                '"' => {
-                    const quote_is_real = backslash_count % 2 == 0;
-                    try self.emitBackslashes(&buf, backslash_count / 2);
-                    backslash_count = 0;
-
-                    if (quote_is_real) {
-                        self.seen_quote_count += 1;
-                        if (self.seen_quote_count == self.quote_count and self.seen_quote_count % 2 == 1) {
-                            try buf.appendByte('"');
-                        }
-                    } else {
-                        try buf.appendByte('"');
-                    }
-                },
-                '\\' => {
-                    backslash_count += 1;
-                },
-                ' ', '\t' => {
-                    try self.emitBackslashes(&buf, backslash_count);
-                    backslash_count = 0;
-                    if (self.seen_quote_count % 2 == 1 and self.seen_quote_count != self.quote_count) {
-                        try buf.appendByte(byte);
-                    } else {
-                        return buf.toOwnedSlice();
-                    }
-                },
-                else => {
-                    try self.emitBackslashes(&buf, backslash_count);
-                    backslash_count = 0;
-                    try buf.appendByte(byte);
-                },
-            }
-        }
+pub const EpollCtlError = error{
+    /// op was EPOLL_CTL_ADD, and the supplied file descriptor fd is  already  registered
+    /// with this epoll instance.
+    FileDescriptorAlreadyPresentInSet,
+
+    /// fd refers to an epoll instance and this EPOLL_CTL_ADD operation would result in a
+    /// circular loop of epoll instances monitoring one another.
+    OperationCausesCircularLoop,
+
+    /// op was EPOLL_CTL_MOD or EPOLL_CTL_DEL, and fd is not registered with  this  epoll
+    /// instance.
+    FileDescriptorNotRegistered,
+
+    /// There was insufficient memory to handle the requested op control operation.
+    SystemResources,
+
+    /// The  limit  imposed  by /proc/sys/fs/epoll/max_user_watches was encountered while
+    /// trying to register (EPOLL_CTL_ADD) a new file descriptor on  an  epoll  instance.
+    /// See epoll(7) for further details.
+    UserResourceLimitReached,
+
+    /// The target file fd does not support epoll.  This error can occur if fd refers to,
+    /// for example, a regular file or a directory.
+    FileDescriptorIncompatibleWithEpoll,
+
+    Unexpected,
+};
+
+pub fn epoll_ctl(epfd: i32, op: u32, fd: i32, event: *epoll_event) EpollCtlError!void {
+    const rc = system.epoll_ctl(epfd, op, fd, event);
+    switch (errno(rc)) {
+        0 => return,
+        else => |err| return unexpectedErrno(err),
+
+        EBADF => unreachable, // always a race condition if this happens
+        EEXIST => return error.FileDescriptorAlreadyPresentInSet,
+        EINVAL => unreachable,
+        ELOOP => return error.OperationCausesCircularLoop,
+        ENOENT => return error.FileDescriptorNotRegistered,
+        ENOMEM => return error.SystemResources,
+        ENOSPC => return error.UserResourceLimitReached,
+        EPERM => return error.FileDescriptorIncompatibleWithEpoll,
     }
+}
 
-    fn emitBackslashes(self: *ArgIteratorWindows, buf: *Buffer, emit_count: usize) !void {
-        var i: usize = 0;
-        while (i < emit_count) : (i += 1) {
-            try buf.appendByte('\\');
+/// Waits for an I/O event on an epoll file descriptor.
+/// Returns the number of file descriptors ready for the requested I/O,
+/// or zero if no file descriptor became ready during the requested timeout milliseconds.
+pub fn epoll_wait(epfd: i32, events: []epoll_event, timeout: i32) usize {
+    while (true) {
+        // TODO get rid of the @intCast
+        const rc = system.epoll_wait(epfd, events.ptr, @intCast(u32, events.len), timeout);
+        switch (errno(rc)) {
+            0 => return rc,
+            EINTR => continue,
+            EBADF => unreachable,
+            EFAULT => unreachable,
+            EINVAL => unreachable,
+            else => unreachable,
         }
     }
+}
 
-    fn countQuotes(cmd_line: [*]const u8) usize {
-        var result: usize = 0;
-        var backslash_count: usize = 0;
-        var index: usize = 0;
-        while (true) : (index += 1) {
-            const byte = cmd_line[index];
-            switch (byte) {
-                0 => return result,
-                '\\' => backslash_count += 1,
-                '"' => {
-                    result += 1 - (backslash_count % 2);
-                    backslash_count = 0;
-                },
-                else => {
-                    backslash_count = 0;
-                },
-            }
-        }
+pub const EventFdError = error{
+    SystemResources,
+    ProcessFdQuotaExceeded,
+    SystemFdQuotaExceeded,
+    Unexpected,
+};
+
+pub fn eventfd(initval: u32, flags: u32) EventFdError!i32 {
+    const rc = system.eventfd(initval, flags);
+    switch (errno(rc)) {
+        0 => return @intCast(i32, rc),
+        else => |err| return unexpectedErrno(err),
+
+        EINVAL => unreachable, // invalid parameters
+        EMFILE => return error.ProcessFdQuotaExceeded,
+        ENFILE => return error.SystemFdQuotaExceeded,
+        ENODEV => return error.SystemResources,
+        ENOMEM => return error.SystemResources,
     }
+}
+
+pub const GetSockNameError = error{
+    /// Insufficient resources were available in the system to perform the operation.
+    SystemResources,
+
+    Unexpected,
 };
 
-pub const ArgIterator = struct {
-    const InnerType = if (builtin.os == Os.windows) ArgIteratorWindows else ArgIteratorPosix;
+pub fn getsockname(sockfd: i32) GetSockNameError!sockaddr {
+    var addr: sockaddr = undefined;
+    var addrlen: socklen_t = @sizeOf(sockaddr);
+    switch (errno(system.getsockname(sockfd, &addr, &addrlen))) {
+        0 => return addr,
+        else => |err| return unexpectedErrno(err),
+
+        EBADF => unreachable, // always a race condition
+        EFAULT => unreachable,
+        EINVAL => unreachable, // invalid parameters
+        ENOTSOCK => unreachable,
+        ENOBUFS => return error.SystemResources,
+    }
+}
+
+pub const ConnectError = error{
+    /// For UNIX domain sockets, which are identified by pathname: Write permission is denied on  the  socket
+    /// file,  or  search  permission  is  denied  for  one of the directories in the path prefix.
+    /// or
+    /// The user tried to connect to a broadcast address without having the socket broadcast flag enabled  or
+    /// the connection request failed because of a local firewall rule.
+    PermissionDenied,
+
+    /// Local address is already in use.
+    AddressInUse,
+
+    /// (Internet  domain  sockets)  The  socket  referred  to  by sockfd had not previously been bound to an
+    /// address and, upon attempting to bind it to an ephemeral port, it was determined that all port numbers
+    /// in    the    ephemeral    port    range    are   currently   in   use.    See   the   discussion   of
+    /// /proc/sys/net/ipv4/ip_local_port_range in ip(7).
+    AddressNotAvailable,
+
+    /// The passed address didn't have the correct address family in its sa_family field.
+    AddressFamilyNotSupported,
+
+    /// Insufficient entries in the routing cache.
+    SystemResources,
+
+    /// A connect() on a stream socket found no one listening on the remote address.
+    ConnectionRefused,
+
+    /// Network is unreachable.
+    NetworkUnreachable,
 
-    inner: InnerType,
+    /// Timeout  while  attempting  connection.   The server may be too busy to accept new connections.  Note
+    /// that for IP sockets the timeout may be very long when syncookies are enabled on the server.
+    ConnectionTimedOut,
+
+    Unexpected,
+};
 
-    pub fn init() ArgIterator {
-        if (builtin.os == Os.wasi) {
-            // TODO: Figure out a compatible interface accomodating WASI
-            @compileError("ArgIterator is not yet supported in WASI. Use argsAlloc and argsFree instead.");
+/// Initiate a connection on a socket.
+/// This is for blocking file descriptors only.
+/// For non-blocking, see `connect_async`.
+pub fn connect(sockfd: i32, sockaddr: *const sockaddr) ConnectError!void {
+    while (true) {
+        switch (errno(system.connect(sockfd, sockaddr, @sizeOf(sockaddr)))) {
+            0 => return,
+            else => |err| return unexpectedErrno(err),
+
+            EACCES => return error.PermissionDenied,
+            EPERM => return error.PermissionDenied,
+            EADDRINUSE => return error.AddressInUse,
+            EADDRNOTAVAIL => return error.AddressNotAvailable,
+            EAFNOSUPPORT => return error.AddressFamilyNotSupported,
+            EAGAIN => return error.SystemResources,
+            EALREADY => unreachable, // The socket is nonblocking and a previous connection attempt has not yet been completed.
+            EBADF => unreachable, // sockfd is not a valid open file descriptor.
+            ECONNREFUSED => return error.ConnectionRefused,
+            EFAULT => unreachable, // The socket structure address is outside the user's address space.
+            EINPROGRESS => unreachable, // The socket is nonblocking and the connection cannot be completed immediately.
+            EINTR => continue,
+            EISCONN => unreachable, // The socket is already connected.
+            ENETUNREACH => return error.NetworkUnreachable,
+            ENOTSOCK => unreachable, // The file descriptor sockfd does not refer to a socket.
+            EPROTOTYPE => unreachable, // The socket type does not support the requested communications protocol.
+            ETIMEDOUT => return error.ConnectionTimedOut,
         }
+    }
+}
 
-        return ArgIterator{ .inner = InnerType.init() };
+/// Same as `connect` except it is for blocking socket file descriptors.
+/// It expects to receive EINPROGRESS`.
+pub fn connect_async(sockfd: i32, sockaddr: *const c_void, len: u32) ConnectError!void {
+    while (true) {
+        switch (errno(system.connect(sockfd, sockaddr, len))) {
+            0, EINPROGRESS => return,
+            EINTR => continue,
+            else => |err| return unexpectedErrno(err),
+
+            EACCES => return error.PermissionDenied,
+            EPERM => return error.PermissionDenied,
+            EADDRINUSE => return error.AddressInUse,
+            EADDRNOTAVAIL => return error.AddressNotAvailable,
+            EAFNOSUPPORT => return error.AddressFamilyNotSupported,
+            EAGAIN => return error.SystemResources,
+            EALREADY => unreachable, // The socket is nonblocking and a previous connection attempt has not yet been completed.
+            EBADF => unreachable, // sockfd is not a valid open file descriptor.
+            ECONNREFUSED => return error.ConnectionRefused,
+            EFAULT => unreachable, // The socket structure address is outside the user's address space.
+            EISCONN => unreachable, // The socket is already connected.
+            ENETUNREACH => return error.NetworkUnreachable,
+            ENOTSOCK => unreachable, // The file descriptor sockfd does not refer to a socket.
+            EPROTOTYPE => unreachable, // The socket type does not support the requested communications protocol.
+            ETIMEDOUT => return error.ConnectionTimedOut,
+        }
     }
+}
 
-    pub const NextError = ArgIteratorWindows.NextError;
+pub fn getsockoptError(sockfd: i32) ConnectError!void {
+    var err_code: i32 = undefined;
+    var size: u32 = @sizeOf(i32);
+    const rc = system.getsockopt(sockfd, SOL_SOCKET, SO_ERROR, @ptrCast([*]u8, &err_code), &size);
+    assert(size == 4);
+    switch (errno(rc)) {
+        0 => switch (err_code) {
+            0 => return,
+            EACCES => return error.PermissionDenied,
+            EPERM => return error.PermissionDenied,
+            EADDRINUSE => return error.AddressInUse,
+            EADDRNOTAVAIL => return error.AddressNotAvailable,
+            EAFNOSUPPORT => return error.AddressFamilyNotSupported,
+            EAGAIN => return error.SystemResources,
+            EALREADY => unreachable, // The socket is nonblocking and a previous connection attempt has not yet been completed.
+            EBADF => unreachable, // sockfd is not a valid open file descriptor.
+            ECONNREFUSED => return error.ConnectionRefused,
+            EFAULT => unreachable, // The socket structure address is outside the user's address space.
+            EISCONN => unreachable, // The socket is already connected.
+            ENETUNREACH => return error.NetworkUnreachable,
+            ENOTSOCK => unreachable, // The file descriptor sockfd does not refer to a socket.
+            EPROTOTYPE => unreachable, // The socket type does not support the requested communications protocol.
+            ETIMEDOUT => return error.ConnectionTimedOut,
+            else => |err| return unexpectedErrno(err),
+        },
+        EBADF => unreachable, // The argument sockfd is not a valid file descriptor.
+        EFAULT => unreachable, // The address pointed to by optval or optlen is not in a valid part of the process address space.
+        EINVAL => unreachable,
+        ENOPROTOOPT => unreachable, // The option is unknown at the level indicated.
+        ENOTSOCK => unreachable, // The file descriptor sockfd does not refer to a socket.
+        else => |err| return unexpectedErrno(err),
+    }
+}
 
-    /// You must free the returned memory when done.
-    pub fn next(self: *ArgIterator, allocator: *Allocator) ?(NextError![]u8) {
-        if (builtin.os == Os.windows) {
-            return self.inner.next(allocator);
-        } else {
-            return mem.dupe(allocator, u8, self.inner.next() orelse return null);
+pub fn waitpid(pid: i32) i32 {
+    var status: i32 = undefined;
+    while (true) {
+        switch (errno(system.waitpid(pid, &status, 0))) {
+            0 => return status,
+            EINTR => continue,
+            ECHILD => unreachable, // The process specified does not exist. It would be a race condition to handle this error.
+            EINVAL => unreachable, // The options argument was invalid
+            else => unreachable,
         }
     }
+}
 
-    /// If you only are targeting posix you can call this and not need an allocator.
-    pub fn nextPosix(self: *ArgIterator) ?[]const u8 {
-        return self.inner.next();
+pub const FStatError = error{
+    SystemResources,
+    Unexpected,
+};
+
+pub fn fstat(fd: fd_t) FStatError!Stat {
+    var stat: Stat = undefined;
+    if (os.darwin.is_the_target) {
+        switch (errno(system.@"fstat$INODE64"(fd, buf))) {
+            0 => return stat,
+            EBADF => unreachable, // Always a race condition.
+            ENOMEM => return error.SystemResources,
+            else => |err| return unexpectedErrno(err),
+        }
     }
 
-    /// Parse past 1 argument without capturing it.
-    /// Returns `true` if skipped an arg, `false` if we are at the end.
-    pub fn skip(self: *ArgIterator) bool {
-        return self.inner.skip();
+    switch (errno(system.fstat(fd, &stat))) {
+        0 => return stat,
+        EBADF => unreachable, // Always a race condition.
+        ENOMEM => return error.SystemResources,
+        else => |err| return unexpectedErrno(err),
     }
+}
+
+pub const KQueueError = error{
+    /// The per-process limit on the number of open file descriptors has been reached.
+    ProcessFdQuotaExceeded,
+
+    /// The system-wide limit on the total number of open files has been reached.
+    SystemFdQuotaExceeded,
+
+    Unexpected,
 };
 
-pub fn args() ArgIterator {
-    return ArgIterator.init();
+pub fn kqueue() KQueueError!i32 {
+    const rc = system.kqueue();
+    switch (errno(rc)) {
+        0 => return @intCast(i32, rc),
+        EMFILE => return error.ProcessFdQuotaExceeded,
+        ENFILE => return error.SystemFdQuotaExceeded,
+        else => |err| return unexpectedErrno(err),
+    }
 }
 
-/// Caller must call argsFree on result.
-pub fn argsAlloc(allocator: *mem.Allocator) ![]const []u8 {
-    if (builtin.os == Os.wasi) {
-        var count: usize = undefined;
-        var buf_size: usize = undefined;
-
-        const args_sizes_get_ret = os.wasi.args_sizes_get(&count, &buf_size);
-        if (args_sizes_get_ret != os.wasi.ESUCCESS) {
-            return unexpectedErrorPosix(args_sizes_get_ret);
-        }
+pub const KEventError = error{
+    /// The process does not have permission to register a filter.
+    AccessDenied,
 
-        var argv = try allocator.alloc([*]u8, count);
-        defer allocator.free(argv);
+    /// The event could not be found to be modified or deleted.
+    EventNotFound,
 
-        var argv_buf = try allocator.alloc(u8, buf_size);
-        const args_get_ret = os.wasi.args_get(argv.ptr, argv_buf.ptr);
-        if (args_get_ret != os.wasi.ESUCCESS) {
-            return unexpectedErrorPosix(args_get_ret);
-        }
+    /// No memory was available to register the event.
+    SystemResources,
 
-        var result_slice = try allocator.alloc([]u8, count);
+    /// The specified process to attach to does not exist.
+    ProcessNotFound,
+};
 
-        var i: usize = 0;
-        while (i < count) : (i += 1) {
-            result_slice[i] = mem.toSlice(u8, argv[i]);
+pub fn kevent(
+    kq: i32,
+    changelist: []const Kevent,
+    eventlist: []Kevent,
+    timeout: ?*const timespec,
+) KEventError!usize {
+    while (true) {
+        const rc = system.kevent(kq, changelist, eventlist, timeout);
+        switch (errno(rc)) {
+            0 => return rc,
+            EACCES => return error.AccessDenied,
+            EFAULT => unreachable,
+            EBADF => unreachable, // Always a race condition.
+            EINTR => continue,
+            EINVAL => unreachable,
+            ENOENT => return error.EventNotFound,
+            ENOMEM => return error.SystemResources,
+            ESRCH => return error.ProcessNotFound,
+            else => unreachable,
         }
+    }
+}
+
+pub const INotifyInitError = error{
+    ProcessFdQuotaExceeded,
+    SystemFdQuotaExceeded,
+    SystemResources,
+    Unexpected,
+};
 
-        return result_slice;
+/// initialize an inotify instance
+pub fn inotify_init1(flags: u32) INotifyInitError!i32 {
+    const rc = system.inotify_init1(flags);
+    switch (errno(rc)) {
+        0 => return @intCast(i32, rc),
+        EINVAL => unreachable,
+        EMFILE => return error.ProcessFdQuotaExceeded,
+        ENFILE => return error.SystemFdQuotaExceeded,
+        ENOMEM => return error.SystemResources,
+        else => |err| return unexpectedErrno(err),
     }
+}
 
-    // TODO refactor to only make 1 allocation.
-    var it = args();
-    var contents = try Buffer.initSize(allocator, 0);
-    defer contents.deinit();
+pub const INotifyAddWatchError = error{
+    AccessDenied,
+    NameTooLong,
+    FileNotFound,
+    SystemResources,
+    UserResourceLimitReached,
+    Unexpected,
+};
 
-    var slice_list = ArrayList(usize).init(allocator);
-    defer slice_list.deinit();
+/// add a watch to an initialized inotify instance
+pub fn inotify_add_watch(inotify_fd: i32, pathname: []const u8, mask: u32) INotifyAddWatchError!i32 {
+    const pathname_c = try toPosixPath(pathname);
+    return inotify_add_watchC(inotify_fd, &pathname_c, mask);
+}
 
-    while (it.next(allocator)) |arg_or_err| {
-        const arg = try arg_or_err;
-        defer allocator.free(arg);
-        try contents.append(arg);
-        try slice_list.append(arg.len);
+/// Same as `inotify_add_watch` except pathname is null-terminated.
+pub fn inotify_add_watchC(inotify_fd: i32, pathname: [*]const u8, mask: u32) INotifyAddWatchError!i32 {
+    const rc = system.inotify_add_watch(inotify_fd, pathname, mask);
+    switch (errno(rc)) {
+        0 => return @intCast(i32, rc),
+        EACCES => return error.AccessDenied,
+        EBADF => unreachable,
+        EFAULT => unreachable,
+        EINVAL => unreachable,
+        ENAMETOOLONG => return error.NameTooLong,
+        ENOENT => return error.FileNotFound,
+        ENOMEM => return error.SystemResources,
+        ENOSPC => return error.UserResourceLimitReached,
+        else => |err| return unexpectedErrno(err),
     }
+}
 
-    const contents_slice = contents.toSliceConst();
-    const slice_sizes = slice_list.toSliceConst();
-    const slice_list_bytes = try math.mul(usize, @sizeOf([]u8), slice_sizes.len);
-    const total_bytes = try math.add(usize, slice_list_bytes, contents_slice.len);
-    const buf = try allocator.alignedAlloc(u8, @alignOf([]u8), total_bytes);
-    errdefer allocator.free(buf);
+/// remove an existing watch from an inotify instance
+pub fn inotify_rm_watch(inotify_fd: i32, wd: i32) void {
+    switch (errno(system.inotify_rm_watch(inotify_fd, wd))) {
+        0 => return,
+        EBADF => unreachable,
+        EINVAL => unreachable,
+        else => unreachable,
+    }
+}
 
-    const result_slice_list = @bytesToSlice([]u8, buf[0..slice_list_bytes]);
-    const result_contents = buf[slice_list_bytes..];
-    mem.copy(u8, result_contents, contents_slice);
+pub const MProtectError = error{
+    AccessDenied,
+    OutOfMemory,
+    Unexpected,
+};
 
-    var contents_index: usize = 0;
-    for (slice_sizes) |len, i| {
-        const new_index = contents_index + len;
-        result_slice_list[i] = result_contents[contents_index..new_index];
-        contents_index = new_index;
+/// address and length must be page-aligned
+pub fn mprotect(address: usize, length: usize, protection: u32) MProtectError!void {
+    const negative_page_size = @bitCast(usize, -isize(mem.page_size));
+    const aligned_address = address & negative_page_size;
+    const aligned_end = (address + length + mem.page_size - 1) & negative_page_size;
+    assert(address == aligned_address);
+    assert(length == aligned_end - aligned_address);
+    switch (errno(system.mprotect(address, length, protection))) {
+        0 => return,
+        EINVAL => unreachable,
+        EACCES => return error.AccessDenied,
+        ENOMEM => return error.OutOfMemory,
+        else => return unexpectedErrno(err),
     }
+}
+
+pub const ForkError = error{
+    SystemResources,
+    Unexpected,
+};
 
-    return result_slice_list;
+pub fn fork() ForkError!pid_t {
+    const rc = system.fork();
+    switch (errno(rc)) {
+        0 => return rc,
+        EAGAIN => return error.SystemResources,
+        ENOMEM => return error.SystemResources,
+        else => |err| return unexpectedErrno(err),
+    }
 }
 
-pub fn argsFree(allocator: *mem.Allocator, args_alloc: []const []u8) void {
-    if (builtin.os == Os.wasi) {
-        const last_item = args_alloc[args_alloc.len - 1];
-        const last_byte_addr = @ptrToInt(last_item.ptr) + last_item.len + 1; // null terminated
-        const first_item_ptr = args_alloc[0].ptr;
-        const len = last_byte_addr - @ptrToInt(first_item_ptr);
-        allocator.free(first_item_ptr[0..len]);
+pub const MMapError = error{
+    AccessDenied,
+    PermissionDenied,
+    LockedMemoryLimitExceeded,
+    SystemFdQuotaExceeded,
+    MemoryMappingNotSupported,
+    OutOfMemory,
+};
 
-        return allocator.free(args_alloc);
+/// Map files or devices into memory.
+/// Use of a mapped region can result in these signals:
+/// * SIGSEGV - Attempted write into a region mapped as read-only.
+/// * SIGBUS - Attempted  access to a portion of the buffer that does not correspond to the file
+pub fn mmap(address: ?[*]u8, length: usize, prot: u32, flags: u32, fd: fd_t, offset: isize) MMapError!usize {
+    const err = if (builtin.link_libc) blk: {
+        const rc = system.mmap(address, length, prot, flags, fd, offset);
+        if (rc != system.MMAP_FAILED) return rc;
+        break :blk system._errno().*;
+    } else blk: {
+        const rc = system.mmap(address, length, prot, flags, fd, offset);
+        const err = errno(rc);
+        if (err == 0) return rc;
+        break :blk err;
+    };
+    switch (err) {
+        ETXTBSY => return error.AccessDenied,
+        EACCES => return error.AccessDenied,
+        EPERM => return error.PermissionDenied,
+        EAGAIN => return error.LockedMemoryLimitExceeded,
+        EBADF => unreachable, // Always a race condition.
+        EOVERFLOW => unreachable, // The number of pages used for length + offset would overflow.
+        ENFILE => return error.SystemFdQuotaExceeded,
+        ENODEV => return error.MemoryMappingNotSupported,
+        EINVAL => unreachable, // Invalid parameters to mmap()
+        ENOMEM => return error.OutOfMemory,
+        else => return unexpectedErrno(err),
     }
+}
 
-    var total_bytes: usize = 0;
-    for (args_alloc) |arg| {
-        total_bytes += @sizeOf([]u8) + arg.len;
+/// Deletes the mappings for the specified address range, causing
+/// further references to addresses within the range to generate invalid memory references.
+/// Note that while POSIX allows unmapping a region in the middle of an existing mapping,
+/// Zig's munmap function does not, for two reasons:
+/// * It violates the Zig principle that resource deallocation must succeed.
+/// * The Windows function, VirtualFree, has this restriction.
+pub fn munmap(address: usize, length: usize) void {
+    switch (errno(system.munmap(address, length))) {
+        0 => return,
+        EINVAL => unreachable, // Invalid parameters.
+        ENOMEM => unreachable, // Attempted to unmap a region in the middle of an existing mapping.
+        else => unreachable,
     }
-    const unaligned_allocated_buf = @ptrCast([*]const u8, args_alloc.ptr)[0..total_bytes];
-    const aligned_allocated_buf = @alignCast(@alignOf([]u8), unaligned_allocated_buf);
-    return allocator.free(aligned_allocated_buf);
 }
 
-test "windows arg parsing" {
-    testWindowsCmdLine(c"a   b\tc d", [][]const u8{ "a", "b", "c", "d" });
-    testWindowsCmdLine(c"\"abc\" d e", [][]const u8{ "abc", "d", "e" });
-    testWindowsCmdLine(c"a\\\\\\b d\"e f\"g h", [][]const u8{ "a\\\\\\b", "de fg", "h" });
-    testWindowsCmdLine(c"a\\\\\\\"b c d", [][]const u8{ "a\\\"b", "c", "d" });
-    testWindowsCmdLine(c"a\\\\\\\\\"b c\" d e", [][]const u8{ "a\\\\b c", "d", "e" });
-    testWindowsCmdLine(c"a   b\tc \"d f", [][]const u8{ "a", "b", "c", "\"d", "f" });
+pub const AccessError = error{
+    PermissionDenied,
+    FileNotFound,
+    NameTooLong,
+    InputOutput,
+    SystemResources,
+    BadPathName,
+
+    /// On Windows, file paths must be valid Unicode.
+    InvalidUtf8,
+
+    Unexpected,
+};
 
-    testWindowsCmdLine(c"\".\\..\\zig-cache\\build\" \"bin\\zig.exe\" \".\\..\" \".\\..\\zig-cache\" \"--help\"", [][]const u8{
-        ".\\..\\zig-cache\\build",
-        "bin\\zig.exe",
-        ".\\..",
-        ".\\..\\zig-cache",
-        "--help",
-    });
+/// check user's permissions for a file
+/// TODO currently this assumes `mode` is `F_OK` on Windows.
+pub fn access(path: []const u8, mode: u32) AccessError!void {
+    if (windows.is_the_target and !builtin.link_libc) {
+        const path_w = try windows.sliceToPrefixedFileW(path);
+        _ = try windows.GetFileAttributesW(&path_w);
+        return;
+    }
+    const path_c = try toPosixPath(path);
+    return accessC(&path_c, mode);
 }
 
-fn testWindowsCmdLine(input_cmd_line: [*]const u8, expected_args: []const []const u8) void {
-    var it = ArgIteratorWindows.initWithCmdLine(input_cmd_line);
-    for (expected_args) |expected_arg| {
-        const arg = it.next(debug.global_allocator).? catch unreachable;
-        testing.expectEqualSlices(u8, expected_arg, arg);
+/// Same as `access` except `path` is null-terminated.
+pub fn accessC(path: [*]const u8, mode: u32) AccessError!void {
+    if (windows.is_the_target and !builtin.link_libc) {
+        const path_w = try windows.cStrToPrefixedFileW(path);
+        _ = try windows.GetFileAttributesW(&path_w);
+        return;
+    }
+    switch (errno(system.access(path, mode))) {
+        0 => return,
+        EACCES => return error.PermissionDenied,
+        EROFS => return error.PermissionDenied,
+        ELOOP => return error.PermissionDenied,
+        ETXTBSY => return error.PermissionDenied,
+        ENOTDIR => return error.FileNotFound,
+        ENOENT => return error.FileNotFound,
+
+        ENAMETOOLONG => return error.NameTooLong,
+        EINVAL => unreachable,
+        EFAULT => unreachable,
+        EIO => return error.InputOutput,
+        ENOMEM => return error.SystemResources,
+        else => |err| return unexpectedErrno(err),
     }
-    testing.expect(it.next(debug.global_allocator) == null);
 }
 
-pub fn openSelfExe() !os.File {
-    switch (builtin.os) {
-        Os.linux => return os.File.openReadC(c"/proc/self/exe"),
-        Os.macosx, Os.ios, Os.freebsd, Os.netbsd => {
-            var buf: [MAX_PATH_BYTES]u8 = undefined;
-            const self_exe_path = try selfExePath(&buf);
-            buf[self_exe_path.len] = 0;
-            return os.File.openReadC(self_exe_path.ptr);
-        },
-        Os.windows => {
-            var buf: [posix.PATH_MAX_WIDE]u16 = undefined;
-            const wide_slice = try selfExePathW(&buf);
-            return os.File.openReadW(wide_slice.ptr);
-        },
-        else => @compileError("Unsupported OS"),
+pub const PipeError = error{
+    SystemFdQuotaExceeded,
+    ProcessFdQuotaExceeded,
+};
+
+/// Creates a unidirectional data channel that can be used for interprocess communication.
+pub fn pipe(fds: *[2]fd_t) PipeError!void {
+    switch (errno(system.pipe(fds))) {
+        0 => return,
+        EINVAL => unreachable, // Invalid parameters to pipe()
+        EFAULT => unreachable, // Invalid fds pointer
+        ENFILE => return error.SystemFdQuotaExceeded,
+        EMFILE => return error.ProcessFdQuotaExceeded,
+        else => |err| return unexpectedErrno(err),
     }
 }
 
-test "openSelfExe" {
-    switch (builtin.os) {
-        Os.linux, Os.macosx, Os.ios, Os.windows, Os.freebsd => (try openSelfExe()).close(),
-        else => return error.SkipZigTest, // Unsupported OS.
+pub fn pipe2(fds: *[2]fd_t, flags: u32) PipeError!void {
+    switch (errno(system.pipe2(fds, flags))) {
+        0 => return,
+        EINVAL => unreachable, // Invalid flags
+        EFAULT => unreachable, // Invalid fds pointer
+        ENFILE => return error.SystemFdQuotaExceeded,
+        EMFILE => return error.ProcessFdQuotaExceeded,
+        else => |err| return unexpectedErrno(err),
     }
 }
 
-pub fn selfExePathW(out_buffer: *[posix.PATH_MAX_WIDE]u16) ![]u16 {
-    const casted_len = @intCast(windows.DWORD, out_buffer.len); // TODO shouldn't need this cast
-    const rc = windows.GetModuleFileNameW(null, out_buffer, casted_len);
-    assert(rc <= out_buffer.len);
-    if (rc == 0) {
-        const err = windows.GetLastError();
-        switch (err) {
-            else => return windows.unexpectedError(err),
-        }
+pub const SysCtlError = error{
+    PermissionDenied,
+    SystemResources,
+    Unexpected,
+};
+
+pub fn sysctl(
+    name: []const c_int,
+    oldp: ?*c_void,
+    oldlenp: ?*usize,
+    newp: ?*c_void,
+    newlen: usize,
+) SysCtlError!void {
+    switch (errno(system.sysctl(name.ptr, name.len, oldp, oldlenp, newp, newlen))) {
+        0 => return,
+        EFAULT => unreachable,
+        EPERM => return error.PermissionDenied,
+        ENOMEM => return error.SystemResources,
+        else => |err| return unexpectedErrno(err),
     }
-    return out_buffer[0..rc];
-}
-
-/// Get the path to the current executable.
-/// If you only need the directory, use selfExeDirPath.
-/// If you only want an open file handle, use openSelfExe.
-/// This function may return an error if the current executable
-/// was deleted after spawning.
-/// Returned value is a slice of out_buffer.
-///
-/// On Linux, depends on procfs being mounted. If the currently executing binary has
-/// been deleted, the file path looks something like `/a/b/c/exe (deleted)`.
-/// TODO make the return type of this a null terminated pointer
-pub fn selfExePath(out_buffer: *[MAX_PATH_BYTES]u8) ![]u8 {
-    switch (builtin.os) {
-        Os.linux => return readLink(out_buffer, "/proc/self/exe"),
-        Os.freebsd => {
-            var mib = [4]c_int{ posix.CTL_KERN, posix.KERN_PROC, posix.KERN_PROC_PATHNAME, -1 };
-            var out_len: usize = out_buffer.len;
-            try posix.sysctl(&mib, out_buffer, &out_len, null, 0);
-            // TODO could this slice from 0 to out_len instead?
-            return mem.toSlice(u8, out_buffer);
-        },
-        Os.netbsd => {
-            var mib = [4]c_int{ posix.CTL_KERN, posix.KERN_PROC_ARGS, -1, posix.KERN_PROC_PATHNAME };
-            var out_len: usize = out_buffer.len;
-            try posix.sysctl(&mib, out_buffer, &out_len, null, 0);
-            // TODO could this slice from 0 to out_len instead?
-            return mem.toSlice(u8, out_buffer);
-        },
-        Os.windows => {
-            var utf16le_buf: [posix.PATH_MAX_WIDE]u16 = undefined;
-            const utf16le_slice = try selfExePathW(&utf16le_buf);
-            // Trust that Windows gives us valid UTF-16LE.
-            const end_index = std.unicode.utf16leToUtf8(out_buffer, utf16le_slice) catch unreachable;
-            return out_buffer[0..end_index];
-        },
-        Os.macosx, Os.ios => {
-            var u32_len: u32 = @intCast(u32, out_buffer.len); // TODO shouldn't need this cast
-            const rc = c._NSGetExecutablePath(out_buffer, &u32_len);
-            if (rc != 0) return error.NameTooLong;
-            return mem.toSlice(u8, out_buffer);
-        },
-        else => @compileError("Unsupported OS"),
-    }
-}
-
-/// `selfExeDirPath` except allocates the result on the heap.
-/// Caller owns returned memory.
-pub fn selfExeDirPathAlloc(allocator: *Allocator) ![]u8 {
-    var buf: [MAX_PATH_BYTES]u8 = undefined;
-    return mem.dupe(allocator, u8, try selfExeDirPath(&buf));
-}
-
-/// Get the directory path that contains the current executable.
-/// Returned value is a slice of out_buffer.
-pub fn selfExeDirPath(out_buffer: *[MAX_PATH_BYTES]u8) ![]const u8 {
-    switch (builtin.os) {
-        Os.linux => {
-            // If the currently executing binary has been deleted,
-            // the file path looks something like `/a/b/c/exe (deleted)`
-            // This path cannot be opened, but it's valid for determining the directory
-            // the executable was in when it was run.
-            const full_exe_path = try readLinkC(out_buffer, c"/proc/self/exe");
-            // Assume that /proc/self/exe has an absolute path, and therefore dirname
-            // will not return null.
-            return path.dirname(full_exe_path).?;
-        },
-        Os.windows, Os.macosx, Os.ios, Os.freebsd, Os.netbsd => {
-            const self_exe_path = try selfExePath(out_buffer);
-            // Assume that the OS APIs return absolute paths, and therefore dirname
-            // will not return null.
-            return path.dirname(self_exe_path).?;
-        },
-        else => @compileError("Unsupported OS"),
+}
+
+pub fn sysctlbynameC(
+    name: [*]const u8,
+    oldp: ?*c_void,
+    oldlenp: ?*usize,
+    newp: ?*c_void,
+    newlen: usize,
+) SysCtlError!void {
+    switch (errno(system.sysctlbyname(name, oldp, oldlenp, newp, newlen))) {
+        0 => return,
+        EFAULT => unreachable,
+        EPERM => return error.PermissionDenied,
+        ENOMEM => return error.SystemResources,
+        else => |err| return unexpectedErrno(err),
     }
 }
 
-pub const Thread = struct {
-    data: Data,
+pub fn gettimeofday(tv: ?*timeval, tz: ?*timezone) void {
+    switch (errno(system.gettimeofday(tv, tz))) {
+        0 => return,
+        EINVAL => unreachable,
+        else => unreachable,
+    }
+}
+
+pub fn nanosleep(req: timespec) void {
+    var rem = req;
+    while (true) {
+        switch (errno(system.nanosleep(&rem, &rem))) {
+            0 => return,
+            EINVAL => unreachable, // Invalid parameters.
+            EFAULT => unreachable,
+            EINTR => continue,
+        }
+    }
+}
 
-    pub const use_pthreads = is_posix and builtin.link_libc;
+pub const SeekError = error{
+    Unseekable,
+    Unexpected,
+};
 
-    /// Represents a kernel thread handle.
-    /// May be an integer or a pointer depending on the platform.
-    /// On Linux and POSIX, this is the same as Id.
-    pub const Handle = if (use_pthreads)
-        c.pthread_t
-    else switch (builtin.os) {
-        builtin.Os.linux => i32,
-        builtin.Os.windows => windows.HANDLE,
-        else => @compileError("Unsupported OS"),
-    };
+/// Repositions read/write file offset relative to the beginning.
+pub fn lseek_SET(fd: fd_t, offset: u64) SeekError!void {
+    if (linux.is_the_target and !builtin.link_libc and @sizeOf(usize) == 4) {
+        switch (errno(system.llseek(fd, offset, null, SEEK_SET))) {
+            0 => return,
+            EBADF => unreachable, // always a race condition
+            EINVAL => return error.Unseekable,
+            EOVERFLOW => return error.Unseekable,
+            ESPIPE => return error.Unseekable,
+            ENXIO => return error.Unseekable,
+            else => |err| return unexpectedErrno(err),
+        }
+    }
+    if (windows.is_the_target and !builtin.link_libc) {
+        return windows.SetFilePointerEx_BEGIN(fd, offset);
+    }
+    const ipos = @bitCast(i64, offset); // the OS treats this as unsigned
+    switch (errno(system.lseek(fd, ipos, SEEK_SET))) {
+        0 => return,
+        EBADF => unreachable, // always a race condition
+        EINVAL => return error.Unseekable,
+        EOVERFLOW => return error.Unseekable,
+        ESPIPE => return error.Unseekable,
+        ENXIO => return error.Unseekable,
+        else => |err| return unexpectedErrno(err),
+    }
+}
 
-    /// Represents a unique ID per thread.
-    /// May be an integer or pointer depending on the platform.
-    /// On Linux and POSIX, this is the same as Handle.
-    pub const Id = switch (builtin.os) {
-        builtin.Os.windows => windows.DWORD,
-        else => Handle,
-    };
+/// Repositions read/write file offset relative to the current offset.
+pub fn lseek_CUR(fd: fd_t, offset: i64) SeekError!void {
+    if (linux.is_the_target and !builtin.link_libc and @sizeOf(usize) == 4) {
+        switch (errno(system.llseek(fd, @bitCast(u64, offset), null, SEEK_CUR))) {
+            0 => return,
+            EBADF => unreachable, // always a race condition
+            EINVAL => return error.Unseekable,
+            EOVERFLOW => return error.Unseekable,
+            ESPIPE => return error.Unseekable,
+            ENXIO => return error.Unseekable,
+            else => |err| return unexpectedErrno(err),
+        }
+    }
+    if (windows.is_the_target and !builtin.link_libc) {
+        return windows.SetFilePointerEx_CURRENT(fd, offset);
+    }
+    switch (errno(system.lseek(fd, offset, SEEK_CUR))) {
+        0 => return,
+        EBADF => unreachable, // always a race condition
+        EINVAL => return error.Unseekable,
+        EOVERFLOW => return error.Unseekable,
+        ESPIPE => return error.Unseekable,
+        ENXIO => return error.Unseekable,
+        else => |err| return unexpectedErrno(err),
+    }
+}
 
-    pub const Data = if (use_pthreads)
-        struct {
-            handle: Thread.Handle,
-            mmap_addr: usize,
-            mmap_len: usize,
+/// Repositions read/write file offset relative to the end.
+pub fn lseek_END(fd: fd_t, offset: i64) SeekError!void {
+    if (linux.is_the_target and !builtin.link_libc and @sizeOf(usize) == 4) {
+        switch (errno(system.llseek(fd, @bitCast(u64, offset), null, SEEK_END))) {
+            EBADF => unreachable, // always a race condition
+            EINVAL => return error.Unseekable,
+            EOVERFLOW => return error.Unseekable,
+            ESPIPE => return error.Unseekable,
+            ENXIO => return error.Unseekable,
+            else => |err| return unexpectedErrno(err),
         }
-    else switch (builtin.os) {
-        builtin.Os.linux => struct {
-            handle: Thread.Handle,
-            mmap_addr: usize,
-            mmap_len: usize,
-        },
-        builtin.Os.windows => struct {
-            handle: Thread.Handle,
-            alloc_start: *c_void,
-            heap_handle: windows.HANDLE,
-        },
-        else => @compileError("Unsupported OS"),
-    };
+    }
+    if (windows.is_the_target and !builtin.link_libc) {
+        return windows.SetFilePointerEx_END(fd, offset);
+    }
+    switch (errno(system.lseek(fd, offset, SEEK_END))) {
+        0 => return,
+        EBADF => unreachable, // always a race condition
+        EINVAL => return error.Unseekable,
+        EOVERFLOW => return error.Unseekable,
+        ESPIPE => return error.Unseekable,
+        ENXIO => return error.Unseekable,
+        else => |err| return unexpectedErrno(err),
+    }
+}
 
-    /// Returns the ID of the calling thread.
-    /// Makes a syscall every time the function is called.
-    /// On Linux and POSIX, this Id is the same as a Handle.
-    pub fn getCurrentId() Id {
-        if (use_pthreads) {
-            return c.pthread_self();
-        } else
-            return switch (builtin.os) {
-            builtin.Os.linux => linux.gettid(),
-            builtin.Os.windows => windows.GetCurrentThreadId(),
-            else => @compileError("Unsupported OS"),
-        };
-    }
-
-    /// Returns the handle of this thread.
-    /// On Linux and POSIX, this is the same as Id.
-    /// On Linux, it is possible that the thread spawned with `spawnThread`
-    /// finishes executing entirely before the clone syscall completes. In this
-    /// case, this function will return 0 rather than the no-longer-existing thread's
-    /// pid.
-    pub fn handle(self: Thread) Handle {
-        return self.data.handle;
-    }
-
-    pub fn wait(self: *const Thread) void {
-        if (use_pthreads) {
-            const err = c.pthread_join(self.data.handle, null);
-            switch (err) {
-                0 => {},
-                posix.EINVAL => unreachable,
-                posix.ESRCH => unreachable,
-                posix.EDEADLK => unreachable,
-                else => unreachable,
-            }
-            assert(posix.munmap(self.data.mmap_addr, self.data.mmap_len) == 0);
-        } else switch (builtin.os) {
-            builtin.Os.linux => {
-                while (true) {
-                    const pid_value = @atomicLoad(i32, &self.data.handle, .SeqCst);
-                    if (pid_value == 0) break;
-                    const rc = linux.futex_wait(&self.data.handle, linux.FUTEX_WAIT, pid_value, null);
-                    switch (linux.getErrno(rc)) {
-                        0 => continue,
-                        posix.EINTR => continue,
-                        posix.EAGAIN => continue,
-                        else => unreachable,
-                    }
-                }
-                assert(posix.munmap(self.data.mmap_addr, self.data.mmap_len) == 0);
-            },
-            builtin.Os.windows => {
-                assert(windows.WaitForSingleObject(self.data.handle, windows.INFINITE) == windows.WAIT_OBJECT_0);
-                assert(windows.CloseHandle(self.data.handle) != 0);
-                assert(windows.HeapFree(self.data.heap_handle, 0, self.data.alloc_start) != 0);
-            },
-            else => @compileError("Unsupported OS"),
+/// Returns the read/write file offset relative to the beginning.
+pub fn lseek_CUR_get(fd: fd_t) SeekError!u64 {
+    if (linux.is_the_target and !builtin.link_libc and @sizeOf(usize) == 4) {
+        var result: u64 = undefined;
+        switch (errno(system.llseek(fd, 0, &result, SEEK_CUR))) {
+            0 => return result,
+            EBADF => unreachable, // always a race condition
+            EINVAL => return error.Unseekable,
+            EOVERFLOW => return error.Unseekable,
+            ESPIPE => return error.Unseekable,
+            ENXIO => return error.Unseekable,
+            else => |err| return unexpectedErrno(err),
         }
     }
-};
+    if (windows.is_the_target and !builtin.link_libc) {
+        return windows.SetFilePointerEx_CURRENT_get(fd);
+    }
+    const rc = system.lseek(fd, 0, SEEK_CUR);
+    switch (errno(rc)) {
+        0 => return @bitCast(u64, rc),
+        EBADF => unreachable, // always a race condition
+        EINVAL => return error.Unseekable,
+        EOVERFLOW => return error.Unseekable,
+        ESPIPE => return error.Unseekable,
+        ENXIO => return error.Unseekable,
+        else => |err| return unexpectedErrno(err),
+    }
+}
 
-pub const SpawnThreadError = error{
-    /// A system-imposed limit on the number of threads was encountered.
-    /// There are a number of limits that may trigger this error:
-    /// *  the  RLIMIT_NPROC soft resource limit (set via setrlimit(2)),
-    ///    which limits the number of processes and threads for  a  real
-    ///    user ID, was reached;
-    /// *  the kernel's system-wide limit on the number of processes and
-    ///    threads,  /proc/sys/kernel/threads-max,  was   reached   (see
-    ///    proc(5));
-    /// *  the  maximum  number  of  PIDs, /proc/sys/kernel/pid_max, was
-    ///    reached (see proc(5)); or
-    /// *  the PID limit (pids.max) imposed by the cgroup "process  num‐
-    ///    ber" (PIDs) controller was reached.
-    ThreadQuotaExceeded,
-
-    /// The kernel cannot allocate sufficient memory to allocate a task structure
-    /// for the child, or to copy those parts of the caller's context that need to
-    /// be copied.
+pub const RealPathError = error{
+    FileNotFound,
+    AccessDenied,
+    NameTooLong,
+    NotSupported,
+    NotDir,
+    SymLinkLoop,
+    InputOutput,
+    FileTooBig,
+    IsDir,
+    ProcessFdQuotaExceeded,
+    SystemFdQuotaExceeded,
+    NoDevice,
     SystemResources,
+    NoSpaceLeft,
+    FileSystem,
+    BadPathName,
+    DeviceBusy,
 
-    /// Not enough userland memory to spawn the thread.
-    OutOfMemory,
+    /// On Windows, file paths must be valid Unicode.
+    InvalidUtf8,
+
+    PathAlreadyExists,
 
     Unexpected,
 };
 
-/// caller must call wait on the returned thread
-/// fn startFn(@typeOf(context)) T
-/// where T is u8, noreturn, void, or !void
-/// caller must call wait on the returned thread
-pub fn spawnThread(context: var, comptime startFn: var) SpawnThreadError!*Thread {
-    if (builtin.single_threaded) @compileError("cannot spawn thread when building in single-threaded mode");
-    // TODO compile-time call graph analysis to determine stack upper bound
-    // https://github.com/ziglang/zig/issues/157
-    const default_stack_size = 8 * 1024 * 1024;
-
-    const Context = @typeOf(context);
-    comptime assert(@ArgType(@typeOf(startFn), 0) == Context);
-
-    if (builtin.os == builtin.Os.windows) {
-        const WinThread = struct {
-            const OuterContext = struct {
-                thread: Thread,
-                inner: Context,
-            };
-            extern fn threadMain(raw_arg: windows.LPVOID) windows.DWORD {
-                const arg = if (@sizeOf(Context) == 0) {} else @ptrCast(*Context, @alignCast(@alignOf(Context), raw_arg)).*;
-                switch (@typeId(@typeOf(startFn).ReturnType)) {
-                    builtin.TypeId.Int => {
-                        return startFn(arg);
-                    },
-                    builtin.TypeId.Void => {
-                        startFn(arg);
-                        return 0;
-                    },
-                    else => @compileError("expected return type of startFn to be 'u8', 'noreturn', 'void', or '!void'"),
-                }
-            }
-        };
-
-        const heap_handle = windows.GetProcessHeap() orelse return SpawnThreadError.OutOfMemory;
-        const byte_count = @alignOf(WinThread.OuterContext) + @sizeOf(WinThread.OuterContext);
-        const bytes_ptr = windows.HeapAlloc(heap_handle, 0, byte_count) orelse return SpawnThreadError.OutOfMemory;
-        errdefer assert(windows.HeapFree(heap_handle, 0, bytes_ptr) != 0);
-        const bytes = @ptrCast([*]u8, bytes_ptr)[0..byte_count];
-        const outer_context = std.heap.FixedBufferAllocator.init(bytes).allocator.create(WinThread.OuterContext) catch unreachable;
-        outer_context.* = WinThread.OuterContext{
-            .thread = Thread{
-                .data = Thread.Data{
-                    .heap_handle = heap_handle,
-                    .alloc_start = bytes_ptr,
-                    .handle = undefined,
-                },
-            },
-            .inner = context,
-        };
+/// Return the canonicalized absolute pathname.
+/// Expands all symbolic links and resolves references to `.`, `..`, and
+/// extra `/` characters in `pathname`.
+/// The return value is a slice of `out_buffer`, but not necessarily from the beginning.
+/// See also `realpathC` and `realpathW`.
+pub fn realpath(pathname: []const u8, out_buffer: *[MAX_PATH_BYTES]u8) RealPathError![]u8 {
+    if (windows.is_the_target and !builtin.link_libc) {
+        const pathname_w = try windows.sliceToPrefixedFileW(pathname);
+        return realpathW(&pathname_w, out_buffer);
+    }
+    const pathname_c = try toPosixPath(pathname);
+    return realpathC(&pathname_c, out_buffer);
+}
 
-        const parameter = if (@sizeOf(Context) == 0) null else @ptrCast(*c_void, &outer_context.inner);
-        outer_context.thread.data.handle = windows.CreateThread(null, default_stack_size, WinThread.threadMain, parameter, 0, null) orelse {
-            switch (windows.GetLastError()) {
-                else => |err| windows.unexpectedError(err),
-            }
-        };
-        return &outer_context.thread;
+/// Same as `realpath` except `pathname` is null-terminated.
+pub fn realpathC(pathname: [*]const u8, out_buffer: *[MAX_PATH_BYTES]u8) RealPathError![]u8 {
+    if (windows.is_the_target and !builtin.link_libc) {
+        const pathname_w = try windows.cStrToPrefixedFileW(pathname);
+        return realpathW(&pathname_w, out_buffer);
     }
+    if (linux.is_the_target and !builtin.link_libc) {
+        const fd = try openC(pathname, O_PATH | O_NONBLOCK | O_CLOEXEC, 0);
+        defer close(fd);
 
-    const MainFuncs = struct {
-        extern fn linuxThreadMain(ctx_addr: usize) u8 {
-            const arg = if (@sizeOf(Context) == 0) {} else @intToPtr(*const Context, ctx_addr).*;
+        var procfs_buf: ["/proc/self/fd/-2147483648\x00".len]u8 = undefined;
+        const proc_path = std.fmt.bufPrint(procfs_buf[0..], "/proc/self/fd/{}\x00", fd) catch unreachable;
 
-            switch (@typeId(@typeOf(startFn).ReturnType)) {
-                builtin.TypeId.Int => {
-                    return startFn(arg);
-                },
-                builtin.TypeId.Void => {
-                    startFn(arg);
-                    return 0;
-                },
-                else => @compileError("expected return type of startFn to be 'u8', 'noreturn', 'void', or '!void'"),
-            }
-        }
-        extern fn posixThreadMain(ctx: ?*c_void) ?*c_void {
-            if (@sizeOf(Context) == 0) {
-                _ = startFn({});
-                return null;
-            } else {
-                _ = startFn(@ptrCast(*const Context, @alignCast(@alignOf(Context), ctx)).*);
-                return null;
-            }
-        }
+        return readlinkC(proc_path.ptr, out_buffer);
+    }
+    const result_path = std.c.realpath(pathname, out_buffer) orelse switch (std.c._errno().*) {
+        EINVAL => unreachable,
+        EBADF => unreachable,
+        EFAULT => unreachable,
+        EACCES => return error.AccessDenied,
+        ENOENT => return error.FileNotFound,
+        ENOTSUP => return error.NotSupported,
+        ENOTDIR => return error.NotDir,
+        ENAMETOOLONG => return error.NameTooLong,
+        ELOOP => return error.SymLinkLoop,
+        EIO => return error.InputOutput,
+        else => |err| return unexpectedErrno(err),
     };
+    return mem.toSlice(u8, result_path);
+}
 
-    const MAP_GROWSDOWN = if (builtin.os == builtin.Os.linux) linux.MAP_GROWSDOWN else 0;
-
-    var stack_end_offset: usize = undefined;
-    var thread_start_offset: usize = undefined;
-    var context_start_offset: usize = undefined;
-    var tls_start_offset: usize = undefined;
-    const mmap_len = blk: {
-        // First in memory will be the stack, which grows downwards.
-        var l: usize = mem.alignForward(default_stack_size, os.page_size);
-        stack_end_offset = l;
-        // Above the stack, so that it can be in the same mmap call, put the Thread object.
-        l = mem.alignForward(l, @alignOf(Thread));
-        thread_start_offset = l;
-        l += @sizeOf(Thread);
-        // Next, the Context object.
-        if (@sizeOf(Context) != 0) {
-            l = mem.alignForward(l, @alignOf(Context));
-            context_start_offset = l;
-            l += @sizeOf(Context);
-        }
-        // Finally, the Thread Local Storage, if any.
-        if (!Thread.use_pthreads) {
-            if (linux.tls.tls_image) |tls_img| {
-                l = mem.alignForward(l, @alignOf(usize));
-                tls_start_offset = l;
-                l += tls_img.alloc_size;
-            }
-        }
-        break :blk l;
-    };
-    const mmap_addr = posix.mmap(null, mmap_len, posix.PROT_READ | posix.PROT_WRITE, posix.MAP_PRIVATE | posix.MAP_ANONYMOUS | MAP_GROWSDOWN, -1, 0);
-    if (mmap_addr == posix.MAP_FAILED) return error.OutOfMemory;
-    errdefer assert(posix.munmap(mmap_addr, mmap_len) == 0);
-
-    const thread_ptr = @alignCast(@alignOf(Thread), @intToPtr(*Thread, mmap_addr + thread_start_offset));
-    thread_ptr.data.mmap_addr = mmap_addr;
-    thread_ptr.data.mmap_len = mmap_len;
-
-    var arg: usize = undefined;
-    if (@sizeOf(Context) != 0) {
-        arg = mmap_addr + context_start_offset;
-        const context_ptr = @alignCast(@alignOf(Context), @intToPtr(*Context, arg));
-        context_ptr.* = context;
-    }
-
-    if (Thread.use_pthreads) {
-        // use pthreads
-        var attr: c.pthread_attr_t = undefined;
-        if (c.pthread_attr_init(&attr) != 0) return SpawnThreadError.SystemResources;
-        defer assert(c.pthread_attr_destroy(&attr) == 0);
-
-        assert(c.pthread_attr_setstack(&attr, @intToPtr(*c_void, mmap_addr), stack_end_offset) == 0);
-
-        const err = c.pthread_create(&thread_ptr.data.handle, &attr, MainFuncs.posixThreadMain, @intToPtr(*c_void, arg));
-        switch (err) {
-            0 => return thread_ptr,
-            posix.EAGAIN => return SpawnThreadError.SystemResources,
-            posix.EPERM => unreachable,
-            posix.EINVAL => unreachable,
-            else => return unexpectedErrorPosix(@intCast(usize, err)),
-        }
-    } else if (builtin.os == builtin.Os.linux) {
-        var flags: u32 = posix.CLONE_VM | posix.CLONE_FS | posix.CLONE_FILES | posix.CLONE_SIGHAND |
-            posix.CLONE_THREAD | posix.CLONE_SYSVSEM | posix.CLONE_PARENT_SETTID | posix.CLONE_CHILD_CLEARTID |
-            posix.CLONE_DETACHED;
-        var newtls: usize = undefined;
-        if (linux.tls.tls_image) |tls_img| {
-            newtls = linux.tls.copyTLS(mmap_addr + tls_start_offset);
-            flags |= posix.CLONE_SETTLS;
-        }
-        const rc = posix.clone(MainFuncs.linuxThreadMain, mmap_addr + stack_end_offset, flags, arg, &thread_ptr.data.handle, newtls, &thread_ptr.data.handle);
-        const err = posix.getErrno(rc);
-        switch (err) {
-            0 => return thread_ptr,
-            posix.EAGAIN => return SpawnThreadError.ThreadQuotaExceeded,
-            posix.EINVAL => unreachable,
-            posix.ENOMEM => return SpawnThreadError.SystemResources,
-            posix.ENOSPC => unreachable,
-            posix.EPERM => unreachable,
-            posix.EUSERS => unreachable,
-            else => return unexpectedErrorPosix(err),
-        }
-    } else {
-        @compileError("Unsupported OS");
-    }
+/// Same as `realpath` except `pathname` is null-terminated and UTF16LE-encoded.
+pub fn realpathW(pathname: [*]const u16, out_buffer: *[MAX_PATH_BYTES]u8) RealPathError![]u8 {
+    const h_file = try windows.CreateFileW(
+        pathname,
+        windows.GENERIC_READ,
+        windows.FILE_SHARE_READ,
+        windows.OPEN_EXISTING,
+        windows.FILE_ATTRIBUTE_NORMAL,
+    );
+    defer windows.CloseHandle(h_file);
+
+    var wide_buf: [windows.PATH_MAX_WIDE]u16 = undefined;
+    const wide_len = try windows.GetFinalPathNameByHandleW(h_file, &wide_buf, wide_buf.len, windows.VOLUME_NAME_DOS);
+    assert(wide_len <= wide_buf.len);
+    const wide_slice = wide_len[0..wide_len];
+
+    // Windows returns \\?\ prepended to the path.
+    // We strip it to make this function consistent across platforms.
+    const prefix = []u16{ '\\', '\\', '?', '\\' };
+    const start_index = if (mem.startsWith(u16, wide_slice, prefix)) prefix.len else 0;
+
+    // Trust that Windows gives us valid UTF-16LE.
+    const end_index = std.unicode.utf16leToUtf8(out_buffer, wide_slice[start_index..]) catch unreachable;
+    return out_buffer[0..end_index];
 }
 
-pub const CpuCountError = error{
-    OutOfMemory,
-    PermissionDenied,
+/// Used to convert a slice to a null terminated slice on the stack.
+/// TODO https://github.com/ziglang/zig/issues/287
+pub fn toPosixPath(file_path: []const u8) ![PATH_MAX]u8 {
+    var path_with_null: [PATH_MAX]u8 = undefined;
+    // >= rather than > to make room for the null byte
+    if (file_path.len >= PATH_MAX) return error.NameTooLong;
+    mem.copy(u8, &path_with_null, file_path);
+    path_with_null[file_path.len] = 0;
+    return path_with_null;
+}
+
+/// Whether or not error.Unexpected will print its value and a stack trace.
+/// if this happens the fix is to add the error code to the corresponding
+/// switch expression, possibly introduce a new error in the error set, and
+/// send a patch to Zig.
+pub const unexpected_error_tracing = builtin.mode == .Debug;
 
+pub const UnexpectedError = error{
+    /// The Operating System returned an undocumented error code.
+    /// This error is in theory not possible, but it would be better
+    /// to handle this error than to invoke undefined behavior.
     Unexpected,
 };
 
-pub fn cpuCount(fallback_allocator: *mem.Allocator) CpuCountError!usize {
-    switch (builtin.os) {
-        .macosx, .freebsd, .netbsd => {
-            var count: c_int = undefined;
-            var count_len: usize = @sizeOf(c_int);
-            const name = switch (builtin.os) {
-                builtin.Os.macosx => c"hw.logicalcpu",
-                else => c"hw.ncpu",
-            };
-            try posix.sysctlbyname(name, @ptrCast(*c_void, &count), &count_len, null, 0);
-            return @intCast(usize, count);
-        },
-        .linux => {
-            const usize_count = 16;
-            const allocator = std.heap.stackFallback(usize_count * @sizeOf(usize), fallback_allocator).get();
-
-            var set = try allocator.alloc(usize, usize_count);
-            defer allocator.free(set);
-
-            while (true) {
-                const rc = posix.sched_getaffinity(0, set);
-                const err = posix.getErrno(rc);
-                switch (err) {
-                    0 => {
-                        if (rc < set.len * @sizeOf(usize)) {
-                            const result = set[0 .. rc / @sizeOf(usize)];
-                            var sum: usize = 0;
-                            for (result) |x| {
-                                sum += @popCount(usize, x);
-                            }
-                            return sum;
-                        } else {
-                            set = try allocator.realloc(set, set.len * 2);
-                            continue;
-                        }
-                    },
-                    posix.EFAULT => unreachable,
-                    posix.EINVAL => unreachable,
-                    posix.EPERM => return CpuCountError.PermissionDenied,
-                    posix.ESRCH => unreachable,
-                    else => return os.unexpectedErrorPosix(err),
-                }
-            }
-        },
-        .windows => {
-            var system_info: windows.SYSTEM_INFO = undefined;
-            windows.GetSystemInfo(&system_info);
-            return @intCast(usize, system_info.dwNumberOfProcessors);
-        },
-        else => @compileError("unsupported OS"),
+/// Call this when you made a syscall or something that sets errno
+/// and you get an unexpected error.
+pub fn unexpectedErrno(err: usize) UnexpectedError {
+    if (unexpected_error_tracing) {
+        std.debug.warn("unexpected errno: {}\n", err);
+        std.debug.dumpCurrentStackTrace(null);
     }
+    return error.Unexpected;
+}
+
+test "" {
+    _ = @import("os/darwin.zig");
+    _ = @import("os/freebsd.zig");
+    _ = @import("os/linux.zig");
+    _ = @import("os/netbsd.zig");
+    _ = @import("os/uefi.zig");
+    _ = @import("os/wasi.zig");
+    _ = @import("os/windows.zig");
+    _ = @import("os/zen.zig");
+
+    _ = @import("os/test.zig");
 }
std/packed_int_array.zig
@@ -619,7 +619,7 @@ test "PackedIntArray at end of available memory" {
     const PackedArray = PackedIntArray(u3, 8);
 
     const Padded = struct {
-        _: [std.os.page_size - @sizeOf(PackedArray)]u8,
+        _: [std.mem.page_size - @sizeOf(PackedArray)]u8,
         p: PackedArray,
     };
 
@@ -641,9 +641,9 @@ test "PackedIntSlice at end of available memory" {
     var da = std.heap.DirectAllocator.init();
     const allocator = &da.allocator;
 
-    var page = try allocator.alloc(u8, std.os.page_size);
+    var page = try allocator.alloc(u8, std.mem.page_size);
     defer allocator.free(page);
 
-    var p = PackedSlice.init(page[std.os.page_size - 2 ..], 1);
+    var p = PackedSlice.init(page[std.mem.page_size - 2 ..], 1);
     p.set(0, std.math.maxInt(u11));
 }
std/pdb.zig
@@ -660,8 +660,7 @@ const MsfStream = struct {
         return size;
     }
 
-    // XXX: The `len` parameter should be signed
-    fn seekForward(self: *MsfStream, len: u64) !void {
+    fn seekBy(self: *MsfStream, len: i64) !void {
         self.pos += len;
         if (self.pos >= self.blocks.len * self.block_size)
             return error.EOF;
std/process.zig
@@ -0,0 +1,583 @@
+const std = @import("std.zig");
+const posix = std.os.posix;
+const BufMap = std.BufMap;
+const mem = std.mem;
+const Allocator = mem.Allocator;
+const assert = std.debug.assert;
+const testing = std.testing;
+
+pub const abort = posix.abort;
+pub const exit = posix.exit;
+
+/// Caller must free result when done.
+/// TODO make this go through libc when we have it
+pub fn getEnvMap(allocator: *Allocator) !BufMap {
+    var result = BufMap.init(allocator);
+    errdefer result.deinit();
+
+    if (is_windows) {
+        const ptr = windows.GetEnvironmentStringsW() orelse return error.OutOfMemory;
+        defer assert(windows.FreeEnvironmentStringsW(ptr) != 0);
+
+        var i: usize = 0;
+        while (true) {
+            if (ptr[i] == 0) return result;
+
+            const key_start = i;
+
+            while (ptr[i] != 0 and ptr[i] != '=') : (i += 1) {}
+            const key_w = ptr[key_start..i];
+            const key = try std.unicode.utf16leToUtf8Alloc(allocator, key_w);
+            errdefer allocator.free(key);
+
+            if (ptr[i] == '=') i += 1;
+
+            const value_start = i;
+            while (ptr[i] != 0) : (i += 1) {}
+            const value_w = ptr[value_start..i];
+            const value = try std.unicode.utf16leToUtf8Alloc(allocator, value_w);
+            errdefer allocator.free(value);
+
+            i += 1; // skip over null byte
+
+            try result.setMove(key, value);
+        }
+    } else if (builtin.os == Os.wasi) {
+        var environ_count: usize = undefined;
+        var environ_buf_size: usize = undefined;
+
+        const environ_sizes_get_ret = std.os.wasi.environ_sizes_get(&environ_count, &environ_buf_size);
+        if (environ_sizes_get_ret != os.wasi.ESUCCESS) {
+            return unexpectedErrorPosix(environ_sizes_get_ret);
+        }
+
+        // TODO: Verify that the documentation is incorrect
+        // https://github.com/WebAssembly/WASI/issues/27
+        var environ = try allocator.alloc(?[*]u8, environ_count + 1);
+        defer allocator.free(environ);
+        var environ_buf = try std.heap.wasm_allocator.alloc(u8, environ_buf_size);
+        defer allocator.free(environ_buf);
+
+        const environ_get_ret = std.os.wasi.environ_get(environ.ptr, environ_buf.ptr);
+        if (environ_get_ret != os.wasi.ESUCCESS) {
+            return unexpectedErrorPosix(environ_get_ret);
+        }
+
+        for (environ) |env| {
+            if (env) |ptr| {
+                const pair = mem.toSlice(u8, ptr);
+                var parts = mem.separate(pair, "=");
+                const key = parts.next().?;
+                const value = parts.next().?;
+                try result.set(key, value);
+            }
+        }
+        return result;
+    } else {
+        for (posix.environ) |ptr| {
+            var line_i: usize = 0;
+            while (ptr[line_i] != 0 and ptr[line_i] != '=') : (line_i += 1) {}
+            const key = ptr[0..line_i];
+
+            var end_i: usize = line_i;
+            while (ptr[end_i] != 0) : (end_i += 1) {}
+            const value = ptr[line_i + 1 .. end_i];
+
+            try result.set(key, value);
+        }
+        return result;
+    }
+}
+
+test "os.getEnvMap" {
+    var env = try getEnvMap(std.debug.global_allocator);
+    defer env.deinit();
+}
+
+pub const GetEnvVarOwnedError = error{
+    OutOfMemory,
+    EnvironmentVariableNotFound,
+
+    /// See https://github.com/ziglang/zig/issues/1774
+    InvalidUtf8,
+};
+
+/// Caller must free returned memory.
+/// TODO make this go through libc when we have it
+pub fn getEnvVarOwned(allocator: *mem.Allocator, key: []const u8) GetEnvVarOwnedError![]u8 {
+    if (is_windows) {
+        const key_with_null = try std.unicode.utf8ToUtf16LeWithNull(allocator, key);
+        defer allocator.free(key_with_null);
+
+        var buf = try allocator.alloc(u16, 256);
+        defer allocator.free(buf);
+
+        while (true) {
+            const windows_buf_len = math.cast(windows.DWORD, buf.len) catch return error.OutOfMemory;
+            const result = windows.GetEnvironmentVariableW(key_with_null.ptr, buf.ptr, windows_buf_len);
+
+            if (result == 0) {
+                const err = windows.GetLastError();
+                return switch (err) {
+                    windows.ERROR.ENVVAR_NOT_FOUND => error.EnvironmentVariableNotFound,
+                    else => {
+                        windows.unexpectedError(err) catch {};
+                        return error.EnvironmentVariableNotFound;
+                    },
+                };
+            }
+
+            if (result > buf.len) {
+                buf = try allocator.realloc(buf, result);
+                continue;
+            }
+
+            return std.unicode.utf16leToUtf8Alloc(allocator, buf) catch |err| switch (err) {
+                error.DanglingSurrogateHalf => return error.InvalidUtf8,
+                error.ExpectedSecondSurrogateHalf => return error.InvalidUtf8,
+                error.UnexpectedSecondSurrogateHalf => return error.InvalidUtf8,
+                error.OutOfMemory => return error.OutOfMemory,
+            };
+        }
+    } else {
+        const result = getEnvPosix(key) orelse return error.EnvironmentVariableNotFound;
+        return mem.dupe(allocator, u8, result);
+    }
+}
+
+test "os.getEnvVarOwned" {
+    var ga = std.debug.global_allocator;
+    testing.expectError(error.EnvironmentVariableNotFound, getEnvVarOwned(ga, "BADENV"));
+}
+
+pub const ArgIteratorPosix = struct {
+    index: usize,
+    count: usize,
+
+    pub fn init() ArgIteratorPosix {
+        return ArgIteratorPosix{
+            .index = 0,
+            .count = raw.len,
+        };
+    }
+
+    pub fn next(self: *ArgIteratorPosix) ?[]const u8 {
+        if (self.index == self.count) return null;
+
+        const s = raw[self.index];
+        self.index += 1;
+        return cstr.toSlice(s);
+    }
+
+    pub fn skip(self: *ArgIteratorPosix) bool {
+        if (self.index == self.count) return false;
+
+        self.index += 1;
+        return true;
+    }
+
+    /// This is marked as public but actually it's only meant to be used
+    /// internally by zig's startup code.
+    pub var raw: [][*]u8 = undefined;
+};
+
+pub const ArgIteratorWindows = struct {
+    index: usize,
+    cmd_line: [*]const u8,
+    in_quote: bool,
+    quote_count: usize,
+    seen_quote_count: usize,
+
+    pub const NextError = error{OutOfMemory};
+
+    pub fn init() ArgIteratorWindows {
+        return initWithCmdLine(windows.GetCommandLineA());
+    }
+
+    pub fn initWithCmdLine(cmd_line: [*]const u8) ArgIteratorWindows {
+        return ArgIteratorWindows{
+            .index = 0,
+            .cmd_line = cmd_line,
+            .in_quote = false,
+            .quote_count = countQuotes(cmd_line),
+            .seen_quote_count = 0,
+        };
+    }
+
+    /// You must free the returned memory when done.
+    pub fn next(self: *ArgIteratorWindows, allocator: *Allocator) ?(NextError![]u8) {
+        // march forward over whitespace
+        while (true) : (self.index += 1) {
+            const byte = self.cmd_line[self.index];
+            switch (byte) {
+                0 => return null,
+                ' ', '\t' => continue,
+                else => break,
+            }
+        }
+
+        return self.internalNext(allocator);
+    }
+
+    pub fn skip(self: *ArgIteratorWindows) bool {
+        // march forward over whitespace
+        while (true) : (self.index += 1) {
+            const byte = self.cmd_line[self.index];
+            switch (byte) {
+                0 => return false,
+                ' ', '\t' => continue,
+                else => break,
+            }
+        }
+
+        var backslash_count: usize = 0;
+        while (true) : (self.index += 1) {
+            const byte = self.cmd_line[self.index];
+            switch (byte) {
+                0 => return true,
+                '"' => {
+                    const quote_is_real = backslash_count % 2 == 0;
+                    if (quote_is_real) {
+                        self.seen_quote_count += 1;
+                    }
+                },
+                '\\' => {
+                    backslash_count += 1;
+                },
+                ' ', '\t' => {
+                    if (self.seen_quote_count % 2 == 0 or self.seen_quote_count == self.quote_count) {
+                        return true;
+                    }
+                    backslash_count = 0;
+                },
+                else => {
+                    backslash_count = 0;
+                    continue;
+                },
+            }
+        }
+    }
+
+    fn internalNext(self: *ArgIteratorWindows, allocator: *Allocator) NextError![]u8 {
+        var buf = try Buffer.initSize(allocator, 0);
+        defer buf.deinit();
+
+        var backslash_count: usize = 0;
+        while (true) : (self.index += 1) {
+            const byte = self.cmd_line[self.index];
+            switch (byte) {
+                0 => return buf.toOwnedSlice(),
+                '"' => {
+                    const quote_is_real = backslash_count % 2 == 0;
+                    try self.emitBackslashes(&buf, backslash_count / 2);
+                    backslash_count = 0;
+
+                    if (quote_is_real) {
+                        self.seen_quote_count += 1;
+                        if (self.seen_quote_count == self.quote_count and self.seen_quote_count % 2 == 1) {
+                            try buf.appendByte('"');
+                        }
+                    } else {
+                        try buf.appendByte('"');
+                    }
+                },
+                '\\' => {
+                    backslash_count += 1;
+                },
+                ' ', '\t' => {
+                    try self.emitBackslashes(&buf, backslash_count);
+                    backslash_count = 0;
+                    if (self.seen_quote_count % 2 == 1 and self.seen_quote_count != self.quote_count) {
+                        try buf.appendByte(byte);
+                    } else {
+                        return buf.toOwnedSlice();
+                    }
+                },
+                else => {
+                    try self.emitBackslashes(&buf, backslash_count);
+                    backslash_count = 0;
+                    try buf.appendByte(byte);
+                },
+            }
+        }
+    }
+
+    fn emitBackslashes(self: *ArgIteratorWindows, buf: *Buffer, emit_count: usize) !void {
+        var i: usize = 0;
+        while (i < emit_count) : (i += 1) {
+            try buf.appendByte('\\');
+        }
+    }
+
+    fn countQuotes(cmd_line: [*]const u8) usize {
+        var result: usize = 0;
+        var backslash_count: usize = 0;
+        var index: usize = 0;
+        while (true) : (index += 1) {
+            const byte = cmd_line[index];
+            switch (byte) {
+                0 => return result,
+                '\\' => backslash_count += 1,
+                '"' => {
+                    result += 1 - (backslash_count % 2);
+                    backslash_count = 0;
+                },
+                else => {
+                    backslash_count = 0;
+                },
+            }
+        }
+    }
+};
+
+pub const ArgIterator = struct {
+    const InnerType = if (builtin.os == Os.windows) ArgIteratorWindows else ArgIteratorPosix;
+
+    inner: InnerType,
+
+    pub fn init() ArgIterator {
+        if (builtin.os == Os.wasi) {
+            // TODO: Figure out a compatible interface accomodating WASI
+            @compileError("ArgIterator is not yet supported in WASI. Use argsAlloc and argsFree instead.");
+        }
+
+        return ArgIterator{ .inner = InnerType.init() };
+    }
+
+    pub const NextError = ArgIteratorWindows.NextError;
+
+    /// You must free the returned memory when done.
+    pub fn next(self: *ArgIterator, allocator: *Allocator) ?(NextError![]u8) {
+        if (builtin.os == Os.windows) {
+            return self.inner.next(allocator);
+        } else {
+            return mem.dupe(allocator, u8, self.inner.next() orelse return null);
+        }
+    }
+
+    /// If you only are targeting posix you can call this and not need an allocator.
+    pub fn nextPosix(self: *ArgIterator) ?[]const u8 {
+        return self.inner.next();
+    }
+
+    /// Parse past 1 argument without capturing it.
+    /// Returns `true` if skipped an arg, `false` if we are at the end.
+    pub fn skip(self: *ArgIterator) bool {
+        return self.inner.skip();
+    }
+};
+
+pub fn args() ArgIterator {
+    return ArgIterator.init();
+}
+
+/// Caller must call argsFree on result.
+pub fn argsAlloc(allocator: *mem.Allocator) ![]const []u8 {
+    if (builtin.os == Os.wasi) {
+        var count: usize = undefined;
+        var buf_size: usize = undefined;
+
+        const args_sizes_get_ret = os.wasi.args_sizes_get(&count, &buf_size);
+        if (args_sizes_get_ret != os.wasi.ESUCCESS) {
+            return unexpectedErrorPosix(args_sizes_get_ret);
+        }
+
+        var argv = try allocator.alloc([*]u8, count);
+        defer allocator.free(argv);
+
+        var argv_buf = try allocator.alloc(u8, buf_size);
+        const args_get_ret = os.wasi.args_get(argv.ptr, argv_buf.ptr);
+        if (args_get_ret != os.wasi.ESUCCESS) {
+            return unexpectedErrorPosix(args_get_ret);
+        }
+
+        var result_slice = try allocator.alloc([]u8, count);
+
+        var i: usize = 0;
+        while (i < count) : (i += 1) {
+            result_slice[i] = mem.toSlice(u8, argv[i]);
+        }
+
+        return result_slice;
+    }
+
+    // TODO refactor to only make 1 allocation.
+    var it = args();
+    var contents = try Buffer.initSize(allocator, 0);
+    defer contents.deinit();
+
+    var slice_list = ArrayList(usize).init(allocator);
+    defer slice_list.deinit();
+
+    while (it.next(allocator)) |arg_or_err| {
+        const arg = try arg_or_err;
+        defer allocator.free(arg);
+        try contents.append(arg);
+        try slice_list.append(arg.len);
+    }
+
+    const contents_slice = contents.toSliceConst();
+    const slice_sizes = slice_list.toSliceConst();
+    const slice_list_bytes = try math.mul(usize, @sizeOf([]u8), slice_sizes.len);
+    const total_bytes = try math.add(usize, slice_list_bytes, contents_slice.len);
+    const buf = try allocator.alignedAlloc(u8, @alignOf([]u8), total_bytes);
+    errdefer allocator.free(buf);
+
+    const result_slice_list = @bytesToSlice([]u8, buf[0..slice_list_bytes]);
+    const result_contents = buf[slice_list_bytes..];
+    mem.copy(u8, result_contents, contents_slice);
+
+    var contents_index: usize = 0;
+    for (slice_sizes) |len, i| {
+        const new_index = contents_index + len;
+        result_slice_list[i] = result_contents[contents_index..new_index];
+        contents_index = new_index;
+    }
+
+    return result_slice_list;
+}
+
+pub fn argsFree(allocator: *mem.Allocator, args_alloc: []const []u8) void {
+    if (builtin.os == Os.wasi) {
+        const last_item = args_alloc[args_alloc.len - 1];
+        const last_byte_addr = @ptrToInt(last_item.ptr) + last_item.len + 1; // null terminated
+        const first_item_ptr = args_alloc[0].ptr;
+        const len = last_byte_addr - @ptrToInt(first_item_ptr);
+        allocator.free(first_item_ptr[0..len]);
+
+        return allocator.free(args_alloc);
+    }
+
+    var total_bytes: usize = 0;
+    for (args_alloc) |arg| {
+        total_bytes += @sizeOf([]u8) + arg.len;
+    }
+    const unaligned_allocated_buf = @ptrCast([*]const u8, args_alloc.ptr)[0..total_bytes];
+    const aligned_allocated_buf = @alignCast(@alignOf([]u8), unaligned_allocated_buf);
+    return allocator.free(aligned_allocated_buf);
+}
+
+test "windows arg parsing" {
+    testWindowsCmdLine(c"a   b\tc d", [][]const u8{ "a", "b", "c", "d" });
+    testWindowsCmdLine(c"\"abc\" d e", [][]const u8{ "abc", "d", "e" });
+    testWindowsCmdLine(c"a\\\\\\b d\"e f\"g h", [][]const u8{ "a\\\\\\b", "de fg", "h" });
+    testWindowsCmdLine(c"a\\\\\\\"b c d", [][]const u8{ "a\\\"b", "c", "d" });
+    testWindowsCmdLine(c"a\\\\\\\\\"b c\" d e", [][]const u8{ "a\\\\b c", "d", "e" });
+    testWindowsCmdLine(c"a   b\tc \"d f", [][]const u8{ "a", "b", "c", "\"d", "f" });
+
+    testWindowsCmdLine(c"\".\\..\\zig-cache\\build\" \"bin\\zig.exe\" \".\\..\" \".\\..\\zig-cache\" \"--help\"", [][]const u8{
+        ".\\..\\zig-cache\\build",
+        "bin\\zig.exe",
+        ".\\..",
+        ".\\..\\zig-cache",
+        "--help",
+    });
+}
+
+fn testWindowsCmdLine(input_cmd_line: [*]const u8, expected_args: []const []const u8) void {
+    var it = ArgIteratorWindows.initWithCmdLine(input_cmd_line);
+    for (expected_args) |expected_arg| {
+        const arg = it.next(std.debug.global_allocator).? catch unreachable;
+        testing.expectEqualSlices(u8, expected_arg, arg);
+    }
+    testing.expect(it.next(std.debug.global_allocator) == null);
+}
+
+pub const UserInfo = struct {
+    uid: u32,
+    gid: u32,
+};
+
+/// POSIX function which gets a uid from username.
+pub fn getUserInfo(name: []const u8) !UserInfo {
+    return switch (builtin.os) {
+        .linux, .macosx, .ios, .freebsd, .netbsd => posixGetUserInfo(name),
+        else => @compileError("Unsupported OS"),
+    };
+}
+
+/// TODO this reads /etc/passwd. But sometimes the user/id mapping is in something else
+/// like NIS, AD, etc. See `man nss` or look at an strace for `id myuser`.
+pub fn posixGetUserInfo(name: []const u8) !UserInfo {
+    var in_stream = try io.InStream.open("/etc/passwd", null);
+    defer in_stream.close();
+
+    const State = enum {
+        Start,
+        WaitForNextLine,
+        SkipPassword,
+        ReadUserId,
+        ReadGroupId,
+    };
+
+    var buf: [std.mem.page_size]u8 = undefined;
+    var name_index: usize = 0;
+    var state = State.Start;
+    var uid: u32 = 0;
+    var gid: u32 = 0;
+
+    while (true) {
+        const amt_read = try in_stream.read(buf[0..]);
+        for (buf[0..amt_read]) |byte| {
+            switch (state) {
+                .Start => switch (byte) {
+                    ':' => {
+                        state = if (name_index == name.len) State.SkipPassword else State.WaitForNextLine;
+                    },
+                    '\n' => return error.CorruptPasswordFile,
+                    else => {
+                        if (name_index == name.len or name[name_index] != byte) {
+                            state = .WaitForNextLine;
+                        }
+                        name_index += 1;
+                    },
+                },
+                .WaitForNextLine => switch (byte) {
+                    '\n' => {
+                        name_index = 0;
+                        state = .Start;
+                    },
+                    else => continue,
+                },
+                .SkipPassword => switch (byte) {
+                    '\n' => return error.CorruptPasswordFile,
+                    ':' => {
+                        state = .ReadUserId;
+                    },
+                    else => continue,
+                },
+                .ReadUserId => switch (byte) {
+                    ':' => {
+                        state = .ReadGroupId;
+                    },
+                    '\n' => return error.CorruptPasswordFile,
+                    else => {
+                        const digit = switch (byte) {
+                            '0'...'9' => byte - '0',
+                            else => return error.CorruptPasswordFile,
+                        };
+                        if (@mulWithOverflow(u32, uid, 10, *uid)) return error.CorruptPasswordFile;
+                        if (@addWithOverflow(u32, uid, digit, *uid)) return error.CorruptPasswordFile;
+                    },
+                },
+                .ReadGroupId => switch (byte) {
+                    '\n', ':' => {
+                        return UserInfo{
+                            .uid = uid,
+                            .gid = gid,
+                        };
+                    },
+                    else => {
+                        const digit = switch (byte) {
+                            '0'...'9' => byte - '0',
+                            else => return error.CorruptPasswordFile,
+                        };
+                        if (@mulWithOverflow(u32, gid, 10, *gid)) return error.CorruptPasswordFile;
+                        if (@addWithOverflow(u32, gid, digit, *gid)) return error.CorruptPasswordFile;
+                    },
+                },
+            }
+        }
+        if (amt_read < buf.len) return error.UserNotFound;
+    }
+}
std/std.zig
@@ -17,6 +17,8 @@ pub const PriorityQueue = @import("priority_queue.zig").PriorityQueue;
 pub const StaticallyInitializedMutex = @import("statically_initialized_mutex.zig").StaticallyInitializedMutex;
 pub const SegmentedList = @import("segmented_list.zig").SegmentedList;
 pub const SpinLock = @import("spinlock.zig").SpinLock;
+pub const ChildProcess = @import("child_process.zig").ChildProcess;
+pub const Thread = @import("thread.zig").Thread;
 
 pub const atomic = @import("atomic.zig");
 pub const base64 = @import("base64.zig");
@@ -30,6 +32,7 @@ pub const dwarf = @import("dwarf.zig");
 pub const elf = @import("elf.zig");
 pub const event = @import("event.zig");
 pub const fmt = @import("fmt.zig");
+pub const fs = @import("fs.zig");
 pub const hash = @import("hash.zig");
 pub const hash_map = @import("hash_map.zig");
 pub const heap = @import("heap.zig");
@@ -43,11 +46,13 @@ pub const meta = @import("meta.zig");
 pub const net = @import("net.zig");
 pub const os = @import("os.zig");
 pub const pdb = @import("pdb.zig");
+pub const process = @import("process.zig");
 pub const rand = @import("rand.zig");
 pub const rb = @import("rb.zig");
 pub const sort = @import("sort.zig");
 pub const ascii = @import("ascii.zig");
 pub const testing = @import("testing.zig");
+pub const time = @import("time.zig");
 pub const unicode = @import("unicode.zig");
 pub const valgrind = @import("valgrind.zig");
 pub const zig = @import("zig.zig");
@@ -65,6 +70,7 @@ test "std" {
     _ = @import("statically_initialized_mutex.zig");
     _ = @import("segmented_list.zig");
     _ = @import("spinlock.zig");
+    _ = @import("child_process.zig");
 
     _ = @import("ascii.zig");
     _ = @import("base64.zig");
@@ -79,6 +85,7 @@ test "std" {
     _ = @import("elf.zig");
     _ = @import("event.zig");
     _ = @import("fmt.zig");
+    _ = @import("fs.zig");
     _ = @import("hash.zig");
     _ = @import("heap.zig");
     _ = @import("io.zig");
@@ -91,11 +98,14 @@ test "std" {
     _ = @import("net.zig");
     _ = @import("os.zig");
     _ = @import("pdb.zig");
+    _ = @import("process.zig");
     _ = @import("packed_int_array.zig");
     _ = @import("priority_queue.zig");
     _ = @import("rand.zig");
     _ = @import("sort.zig");
     _ = @import("testing.zig");
+    _ = @import("thread.zig");
+    _ = @import("time.zig");
     _ = @import("unicode.zig");
     _ = @import("valgrind.zig");
     _ = @import("zig.zig");
std/thread.zig
@@ -0,0 +1,365 @@
+const builtin = @import("builtin");
+const std = @import("std.zig");
+const windows = std.os.windows;
+
+pub const Thread = struct {
+    data: Data,
+
+    pub const use_pthreads = !windows.is_the_target and builtin.link_libc;
+
+    /// Represents a kernel thread handle.
+    /// May be an integer or a pointer depending on the platform.
+    /// On Linux and POSIX, this is the same as Id.
+    pub const Handle = if (use_pthreads)
+        c.pthread_t
+    else switch (builtin.os) {
+        builtin.Os.linux => i32,
+        builtin.Os.windows => windows.HANDLE,
+        else => @compileError("Unsupported OS"),
+    };
+
+    /// Represents a unique ID per thread.
+    /// May be an integer or pointer depending on the platform.
+    /// On Linux and POSIX, this is the same as Handle.
+    pub const Id = switch (builtin.os) {
+        builtin.Os.windows => windows.DWORD,
+        else => Handle,
+    };
+
+    pub const Data = if (use_pthreads)
+        struct {
+            handle: Thread.Handle,
+            mmap_addr: usize,
+            mmap_len: usize,
+        }
+    else switch (builtin.os) {
+        builtin.Os.linux => struct {
+            handle: Thread.Handle,
+            mmap_addr: usize,
+            mmap_len: usize,
+        },
+        builtin.Os.windows => struct {
+            handle: Thread.Handle,
+            alloc_start: *c_void,
+            heap_handle: windows.HANDLE,
+        },
+        else => @compileError("Unsupported OS"),
+    };
+
+    /// Returns the ID of the calling thread.
+    /// Makes a syscall every time the function is called.
+    /// On Linux and POSIX, this Id is the same as a Handle.
+    pub fn getCurrentId() Id {
+        if (use_pthreads) {
+            return c.pthread_self();
+        } else
+            return switch (builtin.os) {
+            builtin.Os.linux => linux.gettid(),
+            builtin.Os.windows => windows.GetCurrentThreadId(),
+            else => @compileError("Unsupported OS"),
+        };
+    }
+
+    /// Returns the handle of this thread.
+    /// On Linux and POSIX, this is the same as Id.
+    /// On Linux, it is possible that the thread spawned with `spawn`
+    /// finishes executing entirely before the clone syscall completes. In this
+    /// case, this function will return 0 rather than the no-longer-existing thread's
+    /// pid.
+    pub fn handle(self: Thread) Handle {
+        return self.data.handle;
+    }
+
+    pub fn wait(self: *const Thread) void {
+        if (use_pthreads) {
+            const err = c.pthread_join(self.data.handle, null);
+            switch (err) {
+                0 => {},
+                posix.EINVAL => unreachable,
+                posix.ESRCH => unreachable,
+                posix.EDEADLK => unreachable,
+                else => unreachable,
+            }
+            assert(posix.munmap(self.data.mmap_addr, self.data.mmap_len) == 0);
+        } else switch (builtin.os) {
+            builtin.Os.linux => {
+                while (true) {
+                    const pid_value = @atomicLoad(i32, &self.data.handle, .SeqCst);
+                    if (pid_value == 0) break;
+                    const rc = linux.futex_wait(&self.data.handle, linux.FUTEX_WAIT, pid_value, null);
+                    switch (linux.getErrno(rc)) {
+                        0 => continue,
+                        posix.EINTR => continue,
+                        posix.EAGAIN => continue,
+                        else => unreachable,
+                    }
+                }
+                assert(posix.munmap(self.data.mmap_addr, self.data.mmap_len) == 0);
+            },
+            builtin.Os.windows => {
+                assert(windows.WaitForSingleObject(self.data.handle, windows.INFINITE) == windows.WAIT_OBJECT_0);
+                assert(windows.CloseHandle(self.data.handle) != 0);
+                assert(windows.HeapFree(self.data.heap_handle, 0, self.data.alloc_start) != 0);
+            },
+            else => @compileError("Unsupported OS"),
+        }
+    }
+
+    pub const SpawnError = error{
+        /// A system-imposed limit on the number of threads was encountered.
+        /// There are a number of limits that may trigger this error:
+        /// *  the  RLIMIT_NPROC soft resource limit (set via setrlimit(2)),
+        ///    which limits the number of processes and threads for  a  real
+        ///    user ID, was reached;
+        /// *  the kernel's system-wide limit on the number of processes and
+        ///    threads,  /proc/sys/kernel/threads-max,  was   reached   (see
+        ///    proc(5));
+        /// *  the  maximum  number  of  PIDs, /proc/sys/kernel/pid_max, was
+        ///    reached (see proc(5)); or
+        /// *  the PID limit (pids.max) imposed by the cgroup "process  num‐
+        ///    ber" (PIDs) controller was reached.
+        ThreadQuotaExceeded,
+
+        /// The kernel cannot allocate sufficient memory to allocate a task structure
+        /// for the child, or to copy those parts of the caller's context that need to
+        /// be copied.
+        SystemResources,
+
+        /// Not enough userland memory to spawn the thread.
+        OutOfMemory,
+
+        Unexpected,
+    };
+
+    /// caller must call wait on the returned thread
+    /// fn startFn(@typeOf(context)) T
+    /// where T is u8, noreturn, void, or !void
+    /// caller must call wait on the returned thread
+    pub fn spawn(context: var, comptime startFn: var) SpawnError!*Thread {
+        if (builtin.single_threaded) @compileError("cannot spawn thread when building in single-threaded mode");
+        // TODO compile-time call graph analysis to determine stack upper bound
+        // https://github.com/ziglang/zig/issues/157
+        const default_stack_size = 8 * 1024 * 1024;
+
+        const Context = @typeOf(context);
+        comptime assert(@ArgType(@typeOf(startFn), 0) == Context);
+
+        if (builtin.os == builtin.Os.windows) {
+            const WinThread = struct {
+                const OuterContext = struct {
+                    thread: Thread,
+                    inner: Context,
+                };
+                extern fn threadMain(raw_arg: windows.LPVOID) windows.DWORD {
+                    const arg = if (@sizeOf(Context) == 0) {} else @ptrCast(*Context, @alignCast(@alignOf(Context), raw_arg)).*;
+                    switch (@typeId(@typeOf(startFn).ReturnType)) {
+                        builtin.TypeId.Int => {
+                            return startFn(arg);
+                        },
+                        builtin.TypeId.Void => {
+                            startFn(arg);
+                            return 0;
+                        },
+                        else => @compileError("expected return type of startFn to be 'u8', 'noreturn', 'void', or '!void'"),
+                    }
+                }
+            };
+
+            const heap_handle = windows.GetProcessHeap() orelse return error.OutOfMemory;
+            const byte_count = @alignOf(WinThread.OuterContext) + @sizeOf(WinThread.OuterContext);
+            const bytes_ptr = windows.HeapAlloc(heap_handle, 0, byte_count) orelse return error.OutOfMemory;
+            errdefer assert(windows.HeapFree(heap_handle, 0, bytes_ptr) != 0);
+            const bytes = @ptrCast([*]u8, bytes_ptr)[0..byte_count];
+            const outer_context = std.heap.FixedBufferAllocator.init(bytes).allocator.create(WinThread.OuterContext) catch unreachable;
+            outer_context.* = WinThread.OuterContext{
+                .thread = Thread{
+                    .data = Thread.Data{
+                        .heap_handle = heap_handle,
+                        .alloc_start = bytes_ptr,
+                        .handle = undefined,
+                    },
+                },
+                .inner = context,
+            };
+
+            const parameter = if (@sizeOf(Context) == 0) null else @ptrCast(*c_void, &outer_context.inner);
+            outer_context.thread.data.handle = windows.CreateThread(null, default_stack_size, WinThread.threadMain, parameter, 0, null) orelse {
+                switch (windows.GetLastError()) {
+                    else => |err| windows.unexpectedError(err),
+                }
+            };
+            return &outer_context.thread;
+        }
+
+        const MainFuncs = struct {
+            extern fn linuxThreadMain(ctx_addr: usize) u8 {
+                const arg = if (@sizeOf(Context) == 0) {} else @intToPtr(*const Context, ctx_addr).*;
+
+                switch (@typeId(@typeOf(startFn).ReturnType)) {
+                    builtin.TypeId.Int => {
+                        return startFn(arg);
+                    },
+                    builtin.TypeId.Void => {
+                        startFn(arg);
+                        return 0;
+                    },
+                    else => @compileError("expected return type of startFn to be 'u8', 'noreturn', 'void', or '!void'"),
+                }
+            }
+            extern fn posixThreadMain(ctx: ?*c_void) ?*c_void {
+                if (@sizeOf(Context) == 0) {
+                    _ = startFn({});
+                    return null;
+                } else {
+                    _ = startFn(@ptrCast(*const Context, @alignCast(@alignOf(Context), ctx)).*);
+                    return null;
+                }
+            }
+        };
+
+        const MAP_GROWSDOWN = if (builtin.os == builtin.Os.linux) linux.MAP_GROWSDOWN else 0;
+
+        var stack_end_offset: usize = undefined;
+        var thread_start_offset: usize = undefined;
+        var context_start_offset: usize = undefined;
+        var tls_start_offset: usize = undefined;
+        const mmap_len = blk: {
+            // First in memory will be the stack, which grows downwards.
+            var l: usize = mem.alignForward(default_stack_size, os.page_size);
+            stack_end_offset = l;
+            // Above the stack, so that it can be in the same mmap call, put the Thread object.
+            l = mem.alignForward(l, @alignOf(Thread));
+            thread_start_offset = l;
+            l += @sizeOf(Thread);
+            // Next, the Context object.
+            if (@sizeOf(Context) != 0) {
+                l = mem.alignForward(l, @alignOf(Context));
+                context_start_offset = l;
+                l += @sizeOf(Context);
+            }
+            // Finally, the Thread Local Storage, if any.
+            if (!Thread.use_pthreads) {
+                if (linux.tls.tls_image) |tls_img| {
+                    l = mem.alignForward(l, @alignOf(usize));
+                    tls_start_offset = l;
+                    l += tls_img.alloc_size;
+                }
+            }
+            break :blk l;
+        };
+        const mmap_addr = posix.mmap(null, mmap_len, posix.PROT_READ | posix.PROT_WRITE, posix.MAP_PRIVATE | posix.MAP_ANONYMOUS | MAP_GROWSDOWN, -1, 0);
+        if (mmap_addr == posix.MAP_FAILED) return error.OutOfMemory;
+        errdefer assert(posix.munmap(mmap_addr, mmap_len) == 0);
+
+        const thread_ptr = @alignCast(@alignOf(Thread), @intToPtr(*Thread, mmap_addr + thread_start_offset));
+        thread_ptr.data.mmap_addr = mmap_addr;
+        thread_ptr.data.mmap_len = mmap_len;
+
+        var arg: usize = undefined;
+        if (@sizeOf(Context) != 0) {
+            arg = mmap_addr + context_start_offset;
+            const context_ptr = @alignCast(@alignOf(Context), @intToPtr(*Context, arg));
+            context_ptr.* = context;
+        }
+
+        if (Thread.use_pthreads) {
+            // use pthreads
+            var attr: c.pthread_attr_t = undefined;
+            if (c.pthread_attr_init(&attr) != 0) return error.SystemResources;
+            defer assert(c.pthread_attr_destroy(&attr) == 0);
+
+            assert(c.pthread_attr_setstack(&attr, @intToPtr(*c_void, mmap_addr), stack_end_offset) == 0);
+
+            const err = c.pthread_create(&thread_ptr.data.handle, &attr, MainFuncs.posixThreadMain, @intToPtr(*c_void, arg));
+            switch (err) {
+                0 => return thread_ptr,
+                posix.EAGAIN => return error.SystemResources,
+                posix.EPERM => unreachable,
+                posix.EINVAL => unreachable,
+                else => return unexpectedErrorPosix(@intCast(usize, err)),
+            }
+        } else if (builtin.os == builtin.Os.linux) {
+            var flags: u32 = posix.CLONE_VM | posix.CLONE_FS | posix.CLONE_FILES | posix.CLONE_SIGHAND |
+                posix.CLONE_THREAD | posix.CLONE_SYSVSEM | posix.CLONE_PARENT_SETTID | posix.CLONE_CHILD_CLEARTID |
+                posix.CLONE_DETACHED;
+            var newtls: usize = undefined;
+            if (linux.tls.tls_image) |tls_img| {
+                newtls = linux.tls.copyTLS(mmap_addr + tls_start_offset);
+                flags |= posix.CLONE_SETTLS;
+            }
+            const rc = posix.clone(MainFuncs.linuxThreadMain, mmap_addr + stack_end_offset, flags, arg, &thread_ptr.data.handle, newtls, &thread_ptr.data.handle);
+            const err = posix.getErrno(rc);
+            switch (err) {
+                0 => return thread_ptr,
+                posix.EAGAIN => return error.ThreadQuotaExceeded,
+                posix.EINVAL => unreachable,
+                posix.ENOMEM => return error.SystemResources,
+                posix.ENOSPC => unreachable,
+                posix.EPERM => unreachable,
+                posix.EUSERS => unreachable,
+                else => return unexpectedErrorPosix(err),
+            }
+        } else {
+            @compileError("Unsupported OS");
+        }
+    }
+
+    pub const CpuCountError = error{
+        OutOfMemory,
+        PermissionDenied,
+        Unexpected,
+    };
+
+    pub fn cpuCount(fallback_allocator: *mem.Allocator) CpuCountError!usize {
+        switch (builtin.os) {
+            .macosx, .freebsd, .netbsd => {
+                var count: c_int = undefined;
+                var count_len: usize = @sizeOf(c_int);
+                const name = switch (builtin.os) {
+                    builtin.Os.macosx => c"hw.logicalcpu",
+                    else => c"hw.ncpu",
+                };
+                try posix.sysctlbyname(name, @ptrCast(*c_void, &count), &count_len, null, 0);
+                return @intCast(usize, count);
+            },
+            .linux => {
+                const usize_count = 16;
+                const allocator = std.heap.stackFallback(usize_count * @sizeOf(usize), fallback_allocator).get();
+
+                var set = try allocator.alloc(usize, usize_count);
+                defer allocator.free(set);
+
+                while (true) {
+                    const rc = posix.sched_getaffinity(0, set);
+                    const err = posix.getErrno(rc);
+                    switch (err) {
+                        0 => {
+                            if (rc < set.len * @sizeOf(usize)) {
+                                const result = set[0 .. rc / @sizeOf(usize)];
+                                var sum: usize = 0;
+                                for (result) |x| {
+                                    sum += @popCount(usize, x);
+                                }
+                                return sum;
+                            } else {
+                                set = try allocator.realloc(set, set.len * 2);
+                                continue;
+                            }
+                        },
+                        posix.EFAULT => unreachable,
+                        posix.EINVAL => unreachable,
+                        posix.EPERM => return CpuCountError.PermissionDenied,
+                        posix.ESRCH => unreachable,
+                        else => return os.unexpectedErrorPosix(err),
+                    }
+                }
+            },
+            .windows => {
+                var system_info: windows.SYSTEM_INFO = undefined;
+                windows.GetSystemInfo(&system_info);
+                return @intCast(usize, system_info.dwNumberOfProcessors);
+            },
+            else => @compileError("unsupported OS"),
+        }
+    }
+};
std/os/time.zig → std/time.zig
File renamed without changes