diff --git a/CMakeLists.txt b/CMakeLists.txt index 03fae22296cc..c6c017f23695 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -621,9 +621,9 @@ set(ZIG_STD_FILES "os/time.zig" "os/uefi.zig" "os/wasi.zig" - "os/wasi/core.zig" "os/windows.zig" "os/windows/advapi32.zig" + "os/windows/errno.zig" "os/windows/error.zig" "os/windows/kernel32.zig" "os/windows/ntdll.zig" diff --git a/doc/langref.html.in b/doc/langref.html.in index d578be4ba887..4564606f1e1d 100644 --- a/doc/langref.html.in +++ b/doc/langref.html.in @@ -195,7 +195,7 @@ const std = @import("std"); pub fn main() !void { // If this program is run without stdout attached, exit with an error. - const stdout_file = try std.io.getStdOut(); + const stdout_file = try std.os.File.stdout(); // If this program encounters pipe failure when printing to stdout, exit // with an error. try stdout_file.write("Hello, world!\n"); diff --git a/example/hello_world/hello.zig b/example/hello_world/hello.zig index cb7d5ef157e0..126faf62fb95 100644 --- a/example/hello_world/hello.zig +++ b/example/hello_world/hello.zig @@ -1,9 +1,5 @@ const std = @import("std"); -pub fn main() !void { - // If this program is run without stdout attached, exit with an error. - const stdout_file = try std.io.getStdOut(); - // If this program encounters pipe failure when printing to stdout, exit - // with an error. - try stdout_file.write("Hello, world!\n"); +pub fn main() void { + std.debug.warn("Hello, world!\n"); } diff --git a/example/hello_world/hello_libc.zig b/example/hello_world/hello_libc.zig index 60a1f7687142..3f077283dbce 100644 --- a/example/hello_world/hello_libc.zig +++ b/example/hello_world/hello_libc.zig @@ -2,13 +2,9 @@ const c = @cImport({ // See https://github.com/ziglang/zig/issues/515 @cDefine("_NO_CRT_STDIO_INLINE", "1"); @cInclude("stdio.h"); - @cInclude("string.h"); }); -const msg = c"Hello, world!\n"; - -export fn main(argc: c_int, argv: **u8) c_int { - if (c.printf(msg) != @intCast(c_int, c.strlen(msg))) return -1; - +export fn main(argc: c_int, argv: [*]?[*]u8) c_int { + c.fprintf(c.stderr, c"Hello, world!\n"); return 0; } diff --git a/std/c.zig b/std/c.zig index db28105eecef..f101186f341f 100644 --- a/std/c.zig +++ b/std/c.zig @@ -1,15 +1,24 @@ const builtin = @import("builtin"); -const Os = builtin.Os; + +pub const is_the_target = builtin.link_libc; pub use switch (builtin.os) { - Os.linux => @import("c/linux.zig"), - Os.windows => @import("c/windows.zig"), - Os.macosx, Os.ios => @import("c/darwin.zig"), - Os.freebsd => @import("c/freebsd.zig"), - Os.netbsd => @import("c/netbsd.zig"), + .linux => @import("c/linux.zig"), + .windows => @import("c/windows.zig"), + .macosx, .ios, .tvos, .watchos => @import("c/darwin.zig"), + .freebsd => @import("c/freebsd.zig"), + .netbsd => @import("c/netbsd.zig"), else => struct {}, }; +pub fn getErrno(rc: var) u12 { + if (rc == -1) { + return @intCast(u12, _errno().*); + } else { + return 0; + } +} + // TODO https://github.com/ziglang/zig/issues/265 on this whole file pub const FILE = @OpaqueType(); @@ -56,6 +65,7 @@ pub extern "c" fn nanosleep(rqtp: *const timespec, rmtp: ?*timespec) c_int; pub extern "c" fn setreuid(ruid: c_uint, euid: c_uint) c_int; pub extern "c" fn setregid(rgid: c_uint, egid: c_uint) c_int; pub extern "c" fn rmdir(path: [*]const u8) c_int; +pub extern "c" fn getenv(name: [*]const u8) ?[*]u8; pub extern "c" fn aligned_alloc(alignment: usize, size: usize) ?*c_void; pub extern "c" fn malloc(usize) ?*c_void; diff --git a/std/event/net.zig b/std/event/net.zig index 687c1199205c..07508b239ee7 100644 --- a/std/event/net.zig +++ b/std/event/net.zig @@ -89,14 +89,7 @@ pub const Server = struct { }, }; } else |err| switch (err) { - error.ProcessFdQuotaExceeded => { - errdefer os.emfile_promise_queue.remove(&self.waiting_for_emfile_node); - suspend { - self.waiting_for_emfile_node = PromiseNode.init(@handle()); - os.emfile_promise_queue.append(&self.waiting_for_emfile_node); - } - continue; - }, + error.ProcessFdQuotaExceeded => @panic("TODO handle this error"), error.ConnectionAborted => continue, error.FileDescriptorNotASocket => unreachable, diff --git a/std/io.zig b/std/io.zig index e47b2188c8cd..831156831953 100644 --- a/std/io.zig +++ b/std/io.zig @@ -18,23 +18,6 @@ const testing = std.testing; const is_posix = builtin.os != builtin.Os.windows; const is_windows = builtin.os == builtin.Os.windows; -const GetStdIoErrs = os.WindowsGetStdHandleErrs; - -pub fn getStdErr() GetStdIoErrs!File { - const handle = if (is_windows) try os.windowsGetStdHandle(os.windows.STD_ERROR_HANDLE) else if (is_posix) os.posix.STDERR_FILENO else unreachable; - return File.openHandle(handle); -} - -pub fn getStdOut() GetStdIoErrs!File { - const handle = if (is_windows) try os.windowsGetStdHandle(os.windows.STD_OUTPUT_HANDLE) else if (is_posix) os.posix.STDOUT_FILENO else unreachable; - return File.openHandle(handle); -} - -pub fn getStdIn() GetStdIoErrs!File { - const handle = if (is_windows) try os.windowsGetStdHandle(os.windows.STD_INPUT_HANDLE) else if (is_posix) os.posix.STDIN_FILENO else unreachable; - return File.openHandle(handle); -} - pub const SeekableStream = @import("io/seekable_stream.zig").SeekableStream; pub const SliceSeekableInStream = @import("io/seekable_stream.zig").SliceSeekableInStream; pub const COutStream = @import("io/c_out_stream.zig").COutStream; @@ -1116,10 +1099,12 @@ pub fn Deserializer(comptime endian: builtin.Endian, comptime packing: Packing, pub const Stream = InStream(Error); pub fn init(in_stream: *Stream) Self { - return Self{ .in_stream = switch (packing) { - .Bit => BitInStream(endian, Stream.Error).init(in_stream), - .Byte => in_stream, - } }; + return Self{ + .in_stream = switch (packing) { + .Bit => BitInStream(endian, Stream.Error).init(in_stream), + .Byte => in_stream, + }, + }; } pub fn alignToByte(self: *Self) void { @@ -1325,10 +1310,12 @@ pub fn Serializer(comptime endian: builtin.Endian, comptime packing: Packing, co pub const Stream = OutStream(Error); pub fn init(out_stream: *Stream) Self { - return Self{ .out_stream = switch (packing) { - .Bit => BitOutStream(endian, Stream.Error).init(out_stream), - .Byte => out_stream, - } }; + return Self{ + .out_stream = switch (packing) { + .Bit => BitOutStream(endian, Stream.Error).init(out_stream), + .Byte => out_stream, + }, + }; } /// Flushes any unwritten bits to the stream diff --git a/std/os.zig b/std/os.zig index 9b452e89ae5c..a8e4b9e5dc97 100644 --- a/std/os.zig +++ b/std/os.zig @@ -2,10 +2,6 @@ const std = @import("std.zig"); const builtin = @import("builtin"); const Os = builtin.Os; const is_windows = builtin.os == Os.windows; -const is_posix = switch (builtin.os) { - builtin.Os.linux, builtin.Os.macosx, builtin.Os.freebsd, builtin.Os.netbsd => true, - else => false, -}; const os = @This(); comptime { @@ -36,13 +32,13 @@ pub const zen = @import("os/zen.zig"); pub const uefi = @import("os/uefi.zig"); pub const wasi = @import("os/wasi.zig"); -pub const posix = switch (builtin.os) { - Os.linux => linux, - Os.macosx, Os.ios => darwin, - Os.freebsd => freebsd, - Os.netbsd => netbsd, - Os.zen => zen, - Os.wasi => wasi, +pub const system = if (builtin.link_libc) c else switch (builtin.os) { + .linux => linux, + .macosx, .ios, .watchos, .tvos => darwin, + .freebsd => freebsd, + .netbsd => netbsd, + .zen => zen, + .wasi => wasi, else => @compileError("Unsupported OS"), }; @@ -58,13 +54,17 @@ pub const page_size = switch (builtin.arch) { else => 4 * 1024, }; +/// 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) { - Os.linux, Os.macosx, Os.ios, Os.freebsd, Os.netbsd => posix.PATH_MAX, + .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. - Os.windows => windows_util.PATH_MAX_WIDE * 3 + 1, + .windows => posix.PATH_MAX_WIDE * 3 + 1, else => @compileError("Unsupported OS"), }; @@ -98,6 +98,22 @@ pub const FileHandle = if (is_windows) windows.HANDLE else i32; 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; @@ -116,610 +132,6 @@ const ArrayList = std.ArrayList; const Buffer = std.Buffer; const math = std.math; -/// Fills `buf` with random bytes. If linking against libc, this calls the -/// appropriate OS-specific library call. Otherwise it uses the zig standard -/// library implementation. -pub fn getRandomBytes(buf: []u8) !void { - switch (builtin.os) { - Os.linux => while (true) { - // TODO check libc version and potentially call c.getrandom. - // See #397 - const errno = posix.getErrno(posix.getrandom(buf.ptr, buf.len, 0)); - switch (errno) { - 0 => return, - posix.EINVAL => unreachable, - posix.EFAULT => unreachable, - posix.EINTR => continue, - posix.ENOSYS => return getRandomBytesDevURandom(buf), - else => return unexpectedErrorPosix(errno), - } - }, - Os.macosx, Os.ios, Os.freebsd, Os.netbsd => return getRandomBytesDevURandom(buf), - Os.windows => { - // 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.RtlGenRandom(buf.ptr, buf.len) == 0) { - const err = windows.GetLastError(); - return switch (err) { - else => unexpectedErrorWindows(err), - }; - } - }, - Os.wasi => { - const random_get_result = os.wasi.random_get(buf.ptr, buf.len); - if (random_get_result != os.wasi.ESUCCESS) { - return error.Unknown; - } - }, - Os.zen => { - const randomness = []u8{ 42, 1, 7, 12, 22, 17, 99, 16, 26, 87, 41, 45 }; - var i: usize = 0; - while (i < buf.len) : (i += 1) { - if (i > randomness.len) return error.Unknown; - buf[i] = randomness[i]; - } - }, - else => @compileError("Unsupported OS"), - } -} - -fn getRandomBytesDevURandom(buf: []u8) !void { - const fd = try posixOpenC(c"/dev/urandom", posix.O_RDONLY | posix.O_CLOEXEC, 0); - defer close(fd); - - const stream = &File.openHandle(fd).inStream().stream; - stream.readNoEof(buf) catch |err| switch (err) { - error.EndOfStream => unreachable, - error.OperationAborted => unreachable, - error.BrokenPipe => unreachable, - error.Unexpected => return error.Unexpected, - error.InputOutput => return error.Unexpected, - error.SystemResources => return error.Unexpected, - error.IsDir => unreachable, - }; -} - -test "os.getRandomBytes" { - var buf_a: [50]u8 = undefined; - var buf_b: [50]u8 = undefined; - // Call Twice - try getRandomBytes(buf_a[0..]); - try getRandomBytes(buf_b[0..]); - - // Check if random (not 100% conclusive) - testing.expect(!mem.eql(u8, buf_a, buf_b)); -} - -/// Raises a signal in the current kernel thread, ending its execution. -/// If linking against libc, this calls the abort() libc function. Otherwise -/// it uses the zig standard library implementation. -pub fn abort() noreturn { - @setCold(true); - if (builtin.link_libc) { - c.abort(); - } - switch (builtin.os) { - Os.linux, Os.macosx, Os.ios, Os.freebsd, Os.netbsd => { - _ = posix.raise(posix.SIGABRT); - _ = posix.raise(posix.SIGKILL); - while (true) {} - }, - Os.windows => { - if (builtin.mode == builtin.Mode.Debug) { - @breakpoint(); - } - windows.ExitProcess(3); - }, - Os.wasi => { - _ = wasi.proc_raise(wasi.SIGABRT); - // TODO: Is SIGKILL even necessary? - _ = wasi.proc_raise(wasi.SIGKILL); - while (true) {} - }, - Os.uefi => { - // TODO there's gotta be a better thing to do here than loop forever - while (true) {} - }, - else => @compileError("Unsupported OS"), - } -} - -/// Exits the program cleanly with the specified status code. -pub fn exit(status: u8) noreturn { - @setCold(true); - if (builtin.link_libc) { - c.exit(status); - } - switch (builtin.os) { - Os.linux => { - if (builtin.single_threaded) { - linux.exit(status); - } else { - linux.exit_group(status); - } - }, - Os.macosx, Os.ios, Os.freebsd, Os.netbsd => { - posix.exit(status); - }, - Os.windows => { - windows.ExitProcess(status); - }, - Os.wasi => { - wasi.proc_exit(status); - }, - else => @compileError("Unsupported OS"), - } -} - -/// When a file descriptor is closed on linux, it pops the first -/// node from this queue and resumes it. -/// Async functions which get the EMFILE error code can suspend, -/// putting their coroutine handle into this list. -/// TODO make this an atomic linked list -pub var emfile_promise_queue = std.LinkedList(promise).init(); - -/// Closes the file handle. Keeps trying if it gets interrupted by a signal. -pub fn close(handle: FileHandle) void { - if (is_windows) { - windows_util.windowsClose(handle); - } else { - while (true) { - const err = posix.getErrno(posix.close(handle)); - switch (err) { - posix.EINTR => continue, - else => { - if (emfile_promise_queue.popFirst()) |p| resume p.data; - return; - }, - } - } - } -} - -pub const PosixReadError = error{ - InputOutput, - SystemResources, - IsDir, - Unexpected, -}; - -/// Returns the number of bytes that were read, which can be less than -/// buf.len. If 0 bytes were read, that means EOF. -pub fn posixRead(fd: i32, buf: []u8) PosixReadError!usize { - // 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 = posix.read(fd, buf.ptr + index, want_to_read); - const err = posix.getErrno(rc); - switch (err) { - 0 => { - index += rc; - if (rc == want_to_read) continue; - // Read returned less than buf.len. - return index; - }, - posix.EINTR => continue, - posix.EINVAL => unreachable, - posix.EFAULT => unreachable, - posix.EAGAIN => unreachable, - posix.EBADF => unreachable, // always a race condition - posix.EIO => return error.InputOutput, - posix.EISDIR => return error.IsDir, - posix.ENOBUFS => return error.SystemResources, - posix.ENOMEM => return error.SystemResources, - else => return unexpectedErrorPosix(err), - } - } - return index; -} - -/// Number of bytes read is returned. Upon reading end-of-file, zero is returned. -pub fn posix_preadv(fd: i32, iov: [*]const posix.iovec, count: usize, offset: u64) PosixReadError!usize { - switch (builtin.os) { - builtin.Os.macosx => { - // 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; - }, - posix.EINTR => continue, - posix.EINVAL => unreachable, - posix.EFAULT => unreachable, - posix.ESPIPE => unreachable, // fd is not seekable - posix.EAGAIN => unreachable, // this function is not for non blocking - posix.EBADF => unreachable, // always a race condition - posix.EIO => return error.InputOutput, - posix.EISDIR => return error.IsDir, - posix.ENOBUFS => return error.SystemResources, - posix.ENOMEM => return error.SystemResources, - else => return unexpectedErrorPosix(err), - } - } - }, - builtin.Os.linux, builtin.Os.freebsd, Os.netbsd => while (true) { - const rc = posix.preadv(fd, iov, count, offset); - const err = posix.getErrno(rc); - switch (err) { - 0 => return rc, - posix.EINTR => continue, - posix.EINVAL => unreachable, - posix.EFAULT => unreachable, - posix.EAGAIN => unreachable, // don't call this function for non blocking - posix.EBADF => unreachable, // always a race condition - posix.EIO => return error.InputOutput, - posix.EISDIR => return error.IsDir, - posix.ENOBUFS => return error.SystemResources, - posix.ENOMEM => return error.SystemResources, - else => return unexpectedErrorPosix(err), - } - }, - else => @compileError("Unsupported OS"), - } -} - -pub const PosixWriteError = error{ - DiskQuota, - FileTooBig, - InputOutput, - NoSpaceLeft, - AccessDenied, - BrokenPipe, - - /// See https://github.com/ziglang/zig/issues/1396 - Unexpected, -}; - -/// Calls POSIX write, and keeps trying if it gets interrupted. -pub fn posixWrite(fd: i32, bytes: []const u8) PosixWriteError!void { - // 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 = posix.write(fd, bytes.ptr + index, amt_to_write); - const write_err = posix.getErrno(rc); - switch (write_err) { - 0 => { - index += rc; - continue; - }, - posix.EINTR => continue, - posix.EINVAL => unreachable, - posix.EFAULT => unreachable, - posix.EAGAIN => unreachable, // use posixAsyncWrite for non-blocking - posix.EBADF => unreachable, // always a race condition - posix.EDESTADDRREQ => unreachable, // connect was never called - posix.EDQUOT => return PosixWriteError.DiskQuota, - posix.EFBIG => return PosixWriteError.FileTooBig, - posix.EIO => return PosixWriteError.InputOutput, - posix.ENOSPC => return PosixWriteError.NoSpaceLeft, - posix.EPERM => return PosixWriteError.AccessDenied, - posix.EPIPE => return PosixWriteError.BrokenPipe, - else => return unexpectedErrorPosix(write_err), - } - } -} - -pub fn posix_pwritev(fd: i32, iov: [*]const posix.iovec_const, count: usize, offset: u64) PosixWriteError!void { - switch (builtin.os) { - builtin.Os.macosx => { - // 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; - }, - posix.EINTR => continue, - posix.ESPIPE => unreachable, // fd is not seekable - posix.EINVAL => unreachable, - posix.EFAULT => unreachable, - posix.EAGAIN => unreachable, // use posixAsyncPWriteV for non-blocking - posix.EBADF => unreachable, // always a race condition - posix.EDESTADDRREQ => unreachable, // connect was never called - posix.EDQUOT => return PosixWriteError.DiskQuota, - posix.EFBIG => return PosixWriteError.FileTooBig, - posix.EIO => return PosixWriteError.InputOutput, - posix.ENOSPC => return PosixWriteError.NoSpaceLeft, - posix.EPERM => return PosixWriteError.AccessDenied, - posix.EPIPE => return PosixWriteError.BrokenPipe, - else => return unexpectedErrorPosix(err), - } - } - }, - builtin.Os.linux, builtin.Os.freebsd, builtin.Os.netbsd => while (true) { - const rc = posix.pwritev(fd, iov, count, offset); - const err = posix.getErrno(rc); - switch (err) { - 0 => return, - posix.EINTR => continue, - posix.EINVAL => unreachable, - posix.EFAULT => unreachable, - posix.EAGAIN => unreachable, // use posixAsyncPWriteV for non-blocking - posix.EBADF => unreachable, // always a race condition - posix.EDESTADDRREQ => unreachable, // connect was never called - posix.EDQUOT => return PosixWriteError.DiskQuota, - posix.EFBIG => return PosixWriteError.FileTooBig, - posix.EIO => return PosixWriteError.InputOutput, - posix.ENOSPC => return PosixWriteError.NoSpaceLeft, - posix.EPERM => return PosixWriteError.AccessDenied, - posix.EPIPE => return PosixWriteError.BrokenPipe, - else => return unexpectedErrorPosix(err), - } - }, - else => @compileError("Unsupported OS"), - } -} - -pub const PosixOpenError = error{ - AccessDenied, - FileTooBig, - IsDir, - SymLinkLoop, - ProcessFdQuotaExceeded, - NameTooLong, - SystemFdQuotaExceeded, - NoDevice, - FileNotFound, - SystemResources, - NoSpaceLeft, - NotDir, - PathAlreadyExists, - DeviceBusy, - - /// See https://github.com/ziglang/zig/issues/1396 - Unexpected, -}; - -/// ::file_path needs to be copied in memory to add a null terminating byte. -/// Calls POSIX open, keeps trying if it gets interrupted, and translates -/// the return value into zig errors. -pub fn posixOpen(file_path: []const u8, flags: u32, perm: usize) PosixOpenError!i32 { - const file_path_c = try toPosixPath(file_path); - return posixOpenC(&file_path_c, flags, perm); -} - -// TODO https://github.com/ziglang/zig/issues/265 -pub fn posixOpenC(file_path: [*]const u8, flags: u32, perm: usize) !i32 { - while (true) { - const result = posix.open(file_path, flags, perm); - const err = posix.getErrno(result); - if (err > 0) { - switch (err) { - posix.EINTR => continue, - - posix.EFAULT => unreachable, - posix.EINVAL => unreachable, - posix.EACCES => return PosixOpenError.AccessDenied, - posix.EFBIG, posix.EOVERFLOW => return PosixOpenError.FileTooBig, - posix.EISDIR => return PosixOpenError.IsDir, - posix.ELOOP => return PosixOpenError.SymLinkLoop, - posix.EMFILE => return PosixOpenError.ProcessFdQuotaExceeded, - posix.ENAMETOOLONG => return PosixOpenError.NameTooLong, - posix.ENFILE => return PosixOpenError.SystemFdQuotaExceeded, - posix.ENODEV => return PosixOpenError.NoDevice, - posix.ENOENT => return PosixOpenError.FileNotFound, - posix.ENOMEM => return PosixOpenError.SystemResources, - posix.ENOSPC => return PosixOpenError.NoSpaceLeft, - posix.ENOTDIR => return PosixOpenError.NotDir, - posix.EPERM => return PosixOpenError.AccessDenied, - posix.EEXIST => return PosixOpenError.PathAlreadyExists, - posix.EBUSY => return PosixOpenError.DeviceBusy, - else => return unexpectedErrorPosix(err), - } - } - return @intCast(i32, result); - } -} - -/// Used to convert a slice to a null terminated slice on the stack. -/// TODO well defined copy elision -pub fn toPosixPath(file_path: []const u8) ![posix.PATH_MAX]u8 { - var path_with_null: [posix.PATH_MAX]u8 = undefined; - if (file_path.len >= posix.PATH_MAX) return error.NameTooLong; - mem.copy(u8, path_with_null[0..], file_path); - path_with_null[file_path.len] = 0; - return path_with_null; -} - -pub fn posixDup2(old_fd: i32, new_fd: i32) !void { - while (true) { - const err = posix.getErrno(posix.dup2(old_fd, new_fd)); - if (err > 0) { - return switch (err) { - posix.EBUSY, posix.EINTR => continue, - posix.EMFILE => error.ProcessFdQuotaExceeded, - posix.EINVAL => unreachable, - else => unexpectedErrorPosix(err), - }; - } - return; - } -} - -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); -} - -/// 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. -pub fn posixExecve(argv: []const []const u8, env_map: *const BufMap, allocator: *Allocator) !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 posixExecveErrnoToErr(posix.getErrno(posix.execve(argv_buf[0].?, argv_buf.ptr, envp_buf.ptr))); - } - - const PATH = getEnvPosix("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 = posix.getErrno(posix.execve(path_buf.ptr, argv_buf.ptr, envp_buf.ptr)); - assert(err > 0); - if (err == posix.EACCES) { - seen_eacces = true; - } else if (err != posix.ENOENT) { - return posixExecveErrnoToErr(err); - } - } - if (seen_eacces) { - err = posix.EACCES; - } - return posixExecveErrnoToErr(err); -} - -pub const PosixExecveError = error{ - SystemResources, - AccessDenied, - InvalidExe, - FileSystem, - IsDir, - FileNotFound, - NotDir, - FileBusy, - - /// See https://github.com/ziglang/zig/issues/1396 - Unexpected, -}; - -fn posixExecveErrnoToErr(err: usize) PosixExecveError { - assert(err > 0); - switch (err) { - posix.EFAULT => unreachable, - posix.E2BIG => return error.SystemResources, - posix.EMFILE => return error.SystemResources, - posix.ENAMETOOLONG => return error.SystemResources, - posix.ENFILE => return error.SystemResources, - posix.ENOMEM => return error.SystemResources, - posix.EACCES => return error.AccessDenied, - posix.EPERM => return error.AccessDenied, - posix.EINVAL => return error.InvalidExe, - posix.ENOEXEC => return error.InvalidExe, - posix.EIO => return error.FileSystem, - posix.ELOOP => return error.FileSystem, - posix.EISDIR => return error.IsDir, - posix.ENOENT => return error.FileNotFound, - posix.ENOTDIR => return error.NotDir, - posix.ETXTBSY => return error.FileBusy, - else => return unexpectedErrorPosix(err), - } -} - -pub var linux_elf_aux_maybe: ?[*]std.elf.Auxv = null; -pub var posix_environ_raw: [][*]u8 = undefined; - -/// See std.elf for the constants. -pub fn linuxGetAuxVal(index: usize) usize { - if (builtin.link_libc) { - return usize(std.c.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 fn getBaseAddress() usize { switch (builtin.os) { builtin.Os.linux => { @@ -803,7 +215,7 @@ pub fn getEnvMap(allocator: *Allocator) !BufMap { } return result; } else { - for (posix_environ_raw) |ptr| { + 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]; @@ -823,23 +235,6 @@ test "os.getEnvMap" { defer env.deinit(); } -/// TODO make this go through libc when we have it -pub fn getEnvPosix(key: []const u8) ?[]const u8 { - for (posix_environ_raw) |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 const GetEnvVarOwnedError = error{ OutOfMemory, EnvironmentVariableNotFound, @@ -896,130 +291,22 @@ test "os.getEnvVarOwned" { testing.expectError(error.EnvironmentVariableNotFound, getEnvVarOwned(ga, "BADENV")); } -/// 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 getCwd(&buf)); +/// 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); } -pub const GetCwdError = error{Unexpected}; - -/// The result is a slice of out_buffer. -pub fn getCwd(out_buffer: *[MAX_PATH_BYTES]u8) GetCwdError![]u8 { - switch (builtin.os) { - Os.windows => { - 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 casted_ptr = ([*]u16)(&utf16le_buf); // TODO shouldn't need this cast - const result = windows.GetCurrentDirectoryW(casted_len, casted_ptr); - if (result == 0) { - const err = windows.GetLastError(); - switch (err) { - else => return unexpectedErrorWindows(err), - } - } - assert(result <= utf16le_buf.len); - const utf16le_slice = utf16le_buf[0..result]; - // 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]; - }, - else => { - const err = posix.getErrno(posix.getcwd(out_buffer, out_buffer.len)); - switch (err) { - 0 => return cstr.toSlice(out_buffer), - posix.ERANGE => unreachable, - else => return unexpectedErrorPosix(err), - } - }, - } +/// 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)); } -test "os.getCwd" { +test "getCwdAlloc" { // at least call it so it gets compiled - _ = getCwdAlloc(debug.global_allocator) catch undefined; - var buf: [MAX_PATH_BYTES]u8 = undefined; - _ = getCwd(&buf) catch undefined; -} - -pub const SymLinkError = PosixSymLinkError || WindowsSymLinkError; - -/// TODO add a symLinkC variant -pub fn symLink(existing_path: []const u8, new_path: []const u8) SymLinkError!void { - if (is_windows) { - return symLinkWindows(existing_path, new_path); - } else { - return symLinkPosix(existing_path, new_path); - } -} - -pub const WindowsSymLinkError = error{ - NameTooLong, - InvalidUtf8, - BadPathName, - - /// See https://github.com/ziglang/zig/issues/1396 - Unexpected, -}; - -pub fn symLinkW(existing_path_w: [*]const u16, new_path_w: [*]const u16) WindowsSymLinkError!void { - if (windows.CreateSymbolicLinkW(existing_path_w, new_path_w, 0) == 0) { - const err = windows.GetLastError(); - switch (err) { - else => return unexpectedErrorWindows(err), - } - } -} - -pub fn symLinkWindows(existing_path: []const u8, new_path: []const u8) WindowsSymLinkError!void { - const existing_path_w = try windows_util.sliceToPrefixedFileW(existing_path); - const new_path_w = try windows_util.sliceToPrefixedFileW(new_path); - return symLinkW(&existing_path_w, &new_path_w); -} - -pub const PosixSymLinkError = error{ - AccessDenied, - DiskQuota, - PathAlreadyExists, - FileSystem, - SymLinkLoop, - NameTooLong, - FileNotFound, - SystemResources, - NoSpaceLeft, - ReadOnlyFileSystem, - NotDir, - - /// See https://github.com/ziglang/zig/issues/1396 - Unexpected, -}; - -pub fn symLinkPosixC(existing_path: [*]const u8, new_path: [*]const u8) PosixSymLinkError!void { - const err = posix.getErrno(posix.symlink(existing_path, new_path)); - switch (err) { - 0 => return, - posix.EFAULT => unreachable, - posix.EINVAL => unreachable, - posix.EACCES => return error.AccessDenied, - posix.EPERM => return error.AccessDenied, - posix.EDQUOT => return error.DiskQuota, - posix.EEXIST => return error.PathAlreadyExists, - posix.EIO => return error.FileSystem, - posix.ELOOP => return error.SymLinkLoop, - posix.ENAMETOOLONG => return error.NameTooLong, - posix.ENOENT => return error.FileNotFound, - posix.ENOTDIR => return error.NotDir, - posix.ENOMEM => return error.SystemResources, - posix.ENOSPC => return error.NoSpaceLeft, - posix.EROFS => return error.ReadOnlyFileSystem, - else => return unexpectedErrorPosix(err), - } -} - -pub fn symLinkPosix(existing_path: []const u8, new_path: []const u8) PosixSymLinkError!void { - const existing_path_c = try toPosixPath(existing_path); - const new_path_c = try toPosixPath(new_path); - return symLinkPosixC(&existing_path_c, &new_path_c); + 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 @@ -1054,78 +341,6 @@ pub fn atomicSymLink(allocator: *Allocator, existing_path: []const u8, new_path: } } -pub const DeleteFileError = error{ - FileNotFound, - AccessDenied, - FileBusy, - FileSystem, - IsDir, - SymLinkLoop, - NameTooLong, - NotDir, - SystemResources, - ReadOnlyFileSystem, - - /// On Windows, file paths must be valid Unicode. - InvalidUtf8, - - /// On Windows, file paths cannot contain these characters: - /// '/', '*', '?', '"', '<', '>', '|' - BadPathName, - - /// See https://github.com/ziglang/zig/issues/1396 - Unexpected, -}; - -pub fn deleteFile(file_path: []const u8) DeleteFileError!void { - if (builtin.os == Os.windows) { - const file_path_w = try windows_util.sliceToPrefixedFileW(file_path); - return deleteFileW(&file_path_w); - } else { - const file_path_c = try toPosixPath(file_path); - return deleteFileC(&file_path_c); - } -} - -pub fn deleteFileW(file_path: [*]const u16) DeleteFileError!void { - if (windows.DeleteFileW(file_path) == 0) { - 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, - windows.ERROR.INVALID_PARAMETER => return error.NameTooLong, - else => return unexpectedErrorWindows(err), - } - } -} - -pub fn deleteFileC(file_path: [*]const u8) DeleteFileError!void { - if (is_windows) { - const file_path_w = try windows_util.cStrToPrefixedFileW(file_path); - return deleteFileW(&file_path_w); - } else { - const err = posix.getErrno(posix.unlink(file_path)); - switch (err) { - 0 => return, - posix.EACCES => return error.AccessDenied, - posix.EPERM => return error.AccessDenied, - posix.EBUSY => return error.FileBusy, - posix.EFAULT => unreachable, - posix.EINVAL => unreachable, - posix.EIO => return error.FileSystem, - posix.EISDIR => return error.IsDir, - posix.ELOOP => return error.SymLinkLoop, - posix.ENAMETOOLONG => return error.NameTooLong, - posix.ENOENT => return error.FileNotFound, - posix.ENOTDIR => return error.NotDir, - posix.ENOMEM => return error.SystemResources, - posix.EROFS => return error.ReadOnlyFileSystem, - else => return unexpectedErrorPosix(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 @@ -1236,8 +451,8 @@ pub const AtomicFile = struct { 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 windows_util.sliceToPrefixedFileW(self.dest_path); - const tmp_path_w = try windows_util.cStrToPrefixedFileW(&self.tmp_path_buf); + 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"); @@ -1245,109 +460,27 @@ pub const AtomicFile = struct { } }; -pub fn renameC(old_path: [*]const u8, new_path: [*]const u8) !void { - if (is_windows) { - const old_path_w = try windows_util.cStrToPrefixedFileW(old_path); - const new_path_w = try windows_util.cStrToPrefixedFileW(new_path); - return renameW(&old_path_w, &new_path_w); - } else { - const err = posix.getErrno(posix.rename(old_path, new_path)); - switch (err) { - 0 => return, - posix.EACCES => return error.AccessDenied, - posix.EPERM => return error.AccessDenied, - posix.EBUSY => return error.FileBusy, - posix.EDQUOT => return error.DiskQuota, - posix.EFAULT => unreachable, - posix.EINVAL => unreachable, - posix.EISDIR => return error.IsDir, - posix.ELOOP => return error.SymLinkLoop, - posix.EMLINK => return error.LinkQuotaExceeded, - posix.ENAMETOOLONG => return error.NameTooLong, - posix.ENOENT => return error.FileNotFound, - posix.ENOTDIR => return error.NotDir, - posix.ENOMEM => return error.SystemResources, - posix.ENOSPC => return error.NoSpaceLeft, - posix.EEXIST => return error.PathAlreadyExists, - posix.ENOTEMPTY => return error.PathAlreadyExists, - posix.EROFS => return error.ReadOnlyFileSystem, - posix.EXDEV => return error.RenameAcrossMountPoints, - else => return unexpectedErrorPosix(err), - } - } -} - -pub fn renameW(old_path: [*]const u16, new_path: [*]const u16) !void { - const flags = windows.MOVEFILE_REPLACE_EXISTING | windows.MOVEFILE_WRITE_THROUGH; - if (windows.MoveFileExW(old_path, new_path, flags) == 0) { - const err = windows.GetLastError(); - switch (err) { - else => return unexpectedErrorWindows(err), - } - } -} - -pub fn rename(old_path: []const u8, new_path: []const u8) !void { - if (is_windows) { - const old_path_w = try windows_util.sliceToPrefixedFileW(old_path); - const new_path_w = try windows_util.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); - } -} +const default_new_dir_mode = 0o755; +/// Create a new directory. pub fn makeDir(dir_path: []const u8) !void { - if (is_windows) { - return makeDirWindows(dir_path); - } else { - return makeDirPosix(dir_path); - } + return posix.mkdir(dir_path, default_new_dir_mode); } -pub fn makeDirWindows(dir_path: []const u8) !void { - const dir_path_w = try windows_util.sliceToPrefixedFileW(dir_path); - - if (windows.CreateDirectoryW(&dir_path_w, null) == 0) { - const err = windows.GetLastError(); - return switch (err) { - windows.ERROR.ALREADY_EXISTS => error.PathAlreadyExists, - windows.ERROR.PATH_NOT_FOUND => error.FileNotFound, - else => unexpectedErrorWindows(err), - }; - } +/// 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 fn makeDirPosixC(dir_path: [*]const u8) !void { - const err = posix.getErrno(posix.mkdir(dir_path, 0o755)); - switch (err) { - 0 => return, - posix.EACCES => return error.AccessDenied, - posix.EPERM => return error.AccessDenied, - posix.EDQUOT => return error.DiskQuota, - posix.EEXIST => return error.PathAlreadyExists, - posix.EFAULT => unreachable, - posix.ELOOP => return error.SymLinkLoop, - posix.EMLINK => return error.LinkQuotaExceeded, - posix.ENAMETOOLONG => return error.NameTooLong, - posix.ENOENT => return error.FileNotFound, - posix.ENOMEM => return error.SystemResources, - posix.ENOSPC => return error.NoSpaceLeft, - posix.ENOTDIR => return error.NotDir, - posix.EROFS => return error.ReadOnlyFileSystem, - else => return unexpectedErrorPosix(err), - } -} - -pub fn makeDirPosix(dir_path: []const u8) !void { - const dir_path_c = try toPosixPath(dir_path); - return makeDirPosixC(&dir_path_c); +/// 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}); @@ -1381,78 +514,20 @@ pub fn makePath(allocator: *Allocator, full_path: []const u8) !void { } } -pub const DeleteDirError = error{ - AccessDenied, - FileBusy, - SymLinkLoop, - NameTooLong, - FileNotFound, - SystemResources, - NotDir, - DirNotEmpty, - ReadOnlyFileSystem, - InvalidUtf8, - BadPathName, - - /// See https://github.com/ziglang/zig/issues/1396 - Unexpected, -}; - -pub fn deleteDirC(dir_path: [*]const u8) DeleteDirError!void { - switch (builtin.os) { - Os.windows => { - const dir_path_w = try windows_util.cStrToPrefixedFileW(dir_path); - return deleteDirW(&dir_path_w); - }, - Os.linux, Os.macosx, Os.ios, Os.freebsd, Os.netbsd => { - const err = posix.getErrno(posix.rmdir(dir_path)); - switch (err) { - 0 => return, - posix.EACCES => return error.AccessDenied, - posix.EPERM => return error.AccessDenied, - posix.EBUSY => return error.FileBusy, - posix.EFAULT => unreachable, - posix.EINVAL => unreachable, - posix.ELOOP => return error.SymLinkLoop, - posix.ENAMETOOLONG => return error.NameTooLong, - posix.ENOENT => return error.FileNotFound, - posix.ENOMEM => return error.SystemResources, - posix.ENOTDIR => return error.NotDir, - posix.EEXIST => return error.DirNotEmpty, - posix.ENOTEMPTY => return error.DirNotEmpty, - posix.EROFS => return error.ReadOnlyFileSystem, - else => return unexpectedErrorPosix(err), - } - }, - else => @compileError("unimplemented"), - } +/// 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); } -pub fn deleteDirW(dir_path_w: [*]const u16) DeleteDirError!void { - if (windows.RemoveDirectoryW(dir_path_w) == 0) { - const err = windows.GetLastError(); - switch (err) { - windows.ERROR.PATH_NOT_FOUND => return error.FileNotFound, - windows.ERROR.DIR_NOT_EMPTY => return error.DirNotEmpty, - else => return unexpectedErrorWindows(err), - } - } +/// 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); } -/// 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 { - switch (builtin.os) { - Os.windows => { - const dir_path_w = try windows_util.sliceToPrefixedFileW(dir_path); - return deleteDirW(&dir_path_w); - }, - Os.linux, Os.macosx, Os.ios, Os.freebsd, Os.netbsd => { - const dir_path_c = try toPosixPath(dir_path); - return deleteDirC(&dir_path_c); - }, - else => @compileError("unimplemented"), - } +/// 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 @@ -1486,7 +561,6 @@ const DeleteTreeError = error{ /// '/', '*', '?', '"', '<', '>', '|' BadPathName, - /// See https://github.com/ziglang/zig/issues/1396 Unexpected, }; @@ -1624,7 +698,6 @@ pub const Dir = struct { BadPathName, DeviceBusy, - /// See https://github.com/ziglang/zig/issues/1396 Unexpected, }; @@ -1878,121 +951,23 @@ pub const Dir = struct { posix.DT_WHT => Entry.Kind.Whiteout, else => Entry.Kind.Unknown, }; - return Entry{ - .name = name, - .kind = entry_kind, - }; - } - } -}; - -pub fn changeCurDir(dir_path: []const u8) !void { - const dir_path_c = try toPosixPath(dir_path); - const err = posix.getErrno(posix.chdir(&dir_path_c)); - switch (err) { - 0 => return, - posix.EACCES => return error.AccessDenied, - posix.EFAULT => unreachable, - posix.EIO => return error.FileSystem, - posix.ELOOP => return error.SymLinkLoop, - posix.ENAMETOOLONG => return error.NameTooLong, - posix.ENOENT => return error.FileNotFound, - posix.ENOMEM => return error.SystemResources, - posix.ENOTDIR => return error.NotDir, - else => return unexpectedErrorPosix(err), - } -} - -/// Read value of a symbolic link. -/// The return value is a slice of out_buffer. -pub fn readLinkC(out_buffer: *[posix.PATH_MAX]u8, pathname: [*]const u8) ![]u8 { - const rc = posix.readlink(pathname, out_buffer, out_buffer.len); - const err = posix.getErrno(rc); - switch (err) { - 0 => return out_buffer[0..rc], - posix.EACCES => return error.AccessDenied, - posix.EFAULT => unreachable, - posix.EINVAL => unreachable, - posix.EIO => return error.FileSystem, - posix.ELOOP => return error.SymLinkLoop, - posix.ENAMETOOLONG => unreachable, // out_buffer is at least PATH_MAX - posix.ENOENT => return error.FileNotFound, - posix.ENOMEM => return error.SystemResources, - posix.ENOTDIR => return error.NotDir, - else => return unexpectedErrorPosix(err), - } -} - -/// Read value of a symbolic link. -/// The return value is a slice of out_buffer. -pub fn readLink(out_buffer: *[posix.PATH_MAX]u8, file_path: []const u8) ![]u8 { - const file_path_c = try toPosixPath(file_path); - return readLinkC(out_buffer, &file_path_c); -} - -pub fn posix_setuid(uid: u32) !void { - const err = posix.getErrno(posix.setuid(uid)); - if (err == 0) return; - return switch (err) { - posix.EAGAIN => error.ResourceLimitReached, - posix.EINVAL => error.InvalidUserId, - posix.EPERM => error.PermissionDenied, - else => unexpectedErrorPosix(err), - }; -} - -pub fn posix_setreuid(ruid: u32, euid: u32) !void { - const err = posix.getErrno(posix.setreuid(ruid, euid)); - if (err == 0) return; - return switch (err) { - posix.EAGAIN => error.ResourceLimitReached, - posix.EINVAL => error.InvalidUserId, - posix.EPERM => error.PermissionDenied, - else => unexpectedErrorPosix(err), - }; -} - -pub fn posix_setgid(gid: u32) !void { - const err = posix.getErrno(posix.setgid(gid)); - if (err == 0) return; - return switch (err) { - posix.EAGAIN => error.ResourceLimitReached, - posix.EINVAL => error.InvalidUserId, - posix.EPERM => error.PermissionDenied, - else => unexpectedErrorPosix(err), - }; -} - -pub fn posix_setregid(rgid: u32, egid: u32) !void { - const err = posix.getErrno(posix.setregid(rgid, egid)); - if (err == 0) return; - return switch (err) { - posix.EAGAIN => error.ResourceLimitReached, - posix.EINVAL => error.InvalidUserId, - posix.EPERM => error.PermissionDenied, - else => unexpectedErrorPosix(err), - }; -} - -pub const WindowsGetStdHandleErrs = error{ - NoStdHandles, - - /// See https://github.com/ziglang/zig/issues/1396 - Unexpected, -}; - -pub fn windowsGetStdHandle(handle_id: windows.DWORD) WindowsGetStdHandleErrs!windows.HANDLE { - if (windows.GetStdHandle(handle_id)) |handle| { - if (handle == windows.INVALID_HANDLE_VALUE) { - const err = windows.GetLastError(); - return switch (err) { - else => os.unexpectedErrorWindows(err), - }; + return Entry{ + .name = name, + .kind = entry_kind, + }; } - return handle; - } else { - return error.NoStdHandles; } +}; + +/// 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 const ArgIteratorPosix = struct { @@ -2328,34 +1303,6 @@ fn testWindowsCmdLine(input_cmd_line: [*]const u8, expected_args: []const []cons testing.expect(it.next(debug.global_allocator) == null); } -// TODO make this a build variable that you can set -const unexpected_error_tracing = false; -const UnexpectedError = error{ - /// The Operating System returned an undocumented error code. - Unexpected, -}; - -/// Call this when you made a syscall or something that sets errno -/// and you get an unexpected error. -pub fn unexpectedErrorPosix(errno: usize) UnexpectedError { - if (unexpected_error_tracing) { - debug.warn("unexpected errno: {}\n", errno); - debug.dumpCurrentStackTrace(null); - } - return error.Unexpected; -} - -/// Call this when you made a windows DLL call or something that does SetLastError -/// and you get an unexpected error. -pub fn unexpectedErrorWindows(err: windows.DWORD) UnexpectedError { - if (unexpected_error_tracing) { - debug.warn("unexpected GetLastError(): {}\n", err); - @breakpoint(); - debug.dumpCurrentStackTrace(null); - } - return error.Unexpected; -} - pub fn openSelfExe() !os.File { switch (builtin.os) { Os.linux => return os.File.openReadC(c"/proc/self/exe"), @@ -2366,7 +1313,7 @@ pub fn openSelfExe() !os.File { return os.File.openReadC(self_exe_path.ptr); }, Os.windows => { - var buf: [windows_util.PATH_MAX_WIDE]u16 = undefined; + var buf: [posix.PATH_MAX_WIDE]u16 = undefined; const wide_slice = try selfExePathW(&buf); return os.File.openReadW(wide_slice.ptr); }, @@ -2381,7 +1328,7 @@ test "openSelfExe" { } } -pub fn selfExePathW(out_buffer: *[windows_util.PATH_MAX_WIDE]u16) ![]u16 { +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); @@ -2434,7 +1381,7 @@ pub fn selfExePath(out_buffer: *[MAX_PATH_BYTES]u8) ![]u8 { }; }, Os.windows => { - var utf16le_buf: [windows_util.PATH_MAX_WIDE]u16 = undefined; + 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; @@ -2481,521 +1428,6 @@ pub fn selfExeDirPath(out_buffer: *[MAX_PATH_BYTES]u8) ![]const u8 { } } -pub fn isTty(handle: FileHandle) bool { - if (is_windows) { - return windows_util.windowsIsTty(handle); - } else { - if (builtin.link_libc) { - return c.isatty(handle) != 0; - } else { - return posix.isatty(handle); - } - } -} - -pub fn supportsAnsiEscapeCodes(handle: FileHandle) bool { - if (is_windows) { - return windows_util.windowsIsCygwinPty(handle); - } else { - if (builtin.link_libc) { - return c.isatty(handle) != 0; - } else { - return posix.isatty(handle); - } - } -} - -pub const PosixSocketError = 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, -}; - -pub fn posixSocket(domain: u32, socket_type: u32, protocol: u32) !i32 { - const rc = posix.socket(domain, socket_type, protocol); - const err = posix.getErrno(rc); - switch (err) { - 0 => return @intCast(i32, rc), - posix.EACCES => return PosixSocketError.PermissionDenied, - posix.EAFNOSUPPORT => return PosixSocketError.AddressFamilyNotSupported, - posix.EINVAL => return PosixSocketError.ProtocolFamilyNotAvailable, - posix.EMFILE => return PosixSocketError.ProcessFdQuotaExceeded, - posix.ENFILE => return PosixSocketError.SystemFdQuotaExceeded, - posix.ENOBUFS, posix.ENOMEM => return PosixSocketError.SystemResources, - posix.EPROTONOSUPPORT => return PosixSocketError.ProtocolNotSupported, - else => return unexpectedErrorPosix(err), - } -} - -pub const PosixBindError = 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, - - /// See https://github.com/ziglang/zig/issues/1396 - Unexpected, -}; - -/// addr is `&const T` where T is one of the sockaddr -pub fn posixBind(fd: i32, addr: *const posix.sockaddr) PosixBindError!void { - const rc = posix.bind(fd, addr, @sizeOf(posix.sockaddr)); - const err = posix.getErrno(rc); - switch (err) { - 0 => return, - posix.EACCES => return PosixBindError.AccessDenied, - posix.EADDRINUSE => return PosixBindError.AddressInUse, - posix.EBADF => unreachable, // always a race condition if this error is returned - posix.EINVAL => unreachable, - posix.ENOTSOCK => unreachable, - posix.EADDRNOTAVAIL => return PosixBindError.AddressNotAvailable, - posix.EFAULT => unreachable, - posix.ELOOP => return PosixBindError.SymLinkLoop, - posix.ENAMETOOLONG => return PosixBindError.NameTooLong, - posix.ENOENT => return PosixBindError.FileNotFound, - posix.ENOMEM => return PosixBindError.SystemResources, - posix.ENOTDIR => return PosixBindError.NotDir, - posix.EROFS => return PosixBindError.ReadOnlyFileSystem, - else => return unexpectedErrorPosix(err), - } -} - -const PosixListenError = 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, - - /// See https://github.com/ziglang/zig/issues/1396 - Unexpected, -}; - -pub fn posixListen(sockfd: i32, backlog: u32) PosixListenError!void { - const rc = posix.listen(sockfd, backlog); - const err = posix.getErrno(rc); - switch (err) { - 0 => return, - posix.EADDRINUSE => return PosixListenError.AddressInUse, - posix.EBADF => unreachable, - posix.ENOTSOCK => return PosixListenError.FileDescriptorNotASocket, - posix.EOPNOTSUPP => return PosixListenError.OperationNotSupported, - else => return unexpectedErrorPosix(err), - } -} - -pub const PosixAcceptError = 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, - - /// See https://github.com/ziglang/zig/issues/1396 - Unexpected, -}; - -pub fn posixAccept(fd: i32, addr: *posix.sockaddr, flags: u32) PosixAcceptError!i32 { - while (true) { - var sockaddr_size = u32(@sizeOf(posix.sockaddr)); - const rc = posix.accept4(fd, addr, &sockaddr_size, flags); - const err = posix.getErrno(rc); - switch (err) { - 0 => return @intCast(i32, rc), - posix.EINTR => continue, - else => return unexpectedErrorPosix(err), - - posix.EAGAIN => unreachable, // use posixAsyncAccept for non-blocking - posix.EBADF => unreachable, // always a race condition - posix.ECONNABORTED => return PosixAcceptError.ConnectionAborted, - posix.EFAULT => unreachable, - posix.EINVAL => unreachable, - posix.EMFILE => return PosixAcceptError.ProcessFdQuotaExceeded, - posix.ENFILE => return PosixAcceptError.SystemFdQuotaExceeded, - posix.ENOBUFS => return PosixAcceptError.SystemResources, - posix.ENOMEM => return PosixAcceptError.SystemResources, - posix.ENOTSOCK => return PosixAcceptError.FileDescriptorNotASocket, - posix.EOPNOTSUPP => return PosixAcceptError.OperationNotSupported, - posix.EPROTO => return PosixAcceptError.ProtocolFailure, - posix.EPERM => return PosixAcceptError.BlockedByFirewall, - } - } -} - -/// Returns -1 if would block. -pub fn posixAsyncAccept(fd: i32, addr: *posix.sockaddr, flags: u32) PosixAcceptError!i32 { - while (true) { - var sockaddr_size = u32(@sizeOf(posix.sockaddr)); - const rc = posix.accept4(fd, addr, &sockaddr_size, flags); - const err = posix.getErrno(rc); - switch (err) { - 0 => return @intCast(i32, rc), - posix.EINTR => continue, - else => return unexpectedErrorPosix(err), - - posix.EAGAIN => return -1, - posix.EBADF => unreachable, // always a race condition - posix.ECONNABORTED => return PosixAcceptError.ConnectionAborted, - posix.EFAULT => unreachable, - posix.EINVAL => unreachable, - posix.EMFILE => return PosixAcceptError.ProcessFdQuotaExceeded, - posix.ENFILE => return PosixAcceptError.SystemFdQuotaExceeded, - posix.ENOBUFS => return PosixAcceptError.SystemResources, - posix.ENOMEM => return PosixAcceptError.SystemResources, - posix.ENOTSOCK => return PosixAcceptError.FileDescriptorNotASocket, - posix.EOPNOTSUPP => return PosixAcceptError.OperationNotSupported, - posix.EPROTO => return PosixAcceptError.ProtocolFailure, - posix.EPERM => return PosixAcceptError.BlockedByFirewall, - } - } -} - -pub const LinuxEpollCreateError = 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, - - /// See https://github.com/ziglang/zig/issues/1396 - Unexpected, -}; - -pub fn linuxEpollCreate(flags: u32) LinuxEpollCreateError!i32 { - const rc = posix.epoll_create1(flags); - const err = posix.getErrno(rc); - switch (err) { - 0 => return @intCast(i32, rc), - else => return unexpectedErrorPosix(err), - - posix.EINVAL => unreachable, - posix.EMFILE => return LinuxEpollCreateError.ProcessFdQuotaExceeded, - posix.ENFILE => return LinuxEpollCreateError.SystemFdQuotaExceeded, - posix.ENOMEM => return LinuxEpollCreateError.SystemResources, - } -} - -pub const LinuxEpollCtlError = 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, - - /// See https://github.com/ziglang/zig/issues/1396 - Unexpected, -}; - -pub fn linuxEpollCtl(epfd: i32, op: u32, fd: i32, event: *linux.epoll_event) LinuxEpollCtlError!void { - const rc = posix.epoll_ctl(epfd, op, fd, event); - const err = posix.getErrno(rc); - switch (err) { - 0 => return, - else => return unexpectedErrorPosix(err), - - posix.EBADF => unreachable, // always a race condition if this happens - posix.EEXIST => return LinuxEpollCtlError.FileDescriptorAlreadyPresentInSet, - posix.EINVAL => unreachable, - posix.ELOOP => return LinuxEpollCtlError.OperationCausesCircularLoop, - posix.ENOENT => return LinuxEpollCtlError.FileDescriptorNotRegistered, - posix.ENOMEM => return LinuxEpollCtlError.SystemResources, - posix.ENOSPC => return LinuxEpollCtlError.UserResourceLimitReached, - posix.EPERM => return LinuxEpollCtlError.FileDescriptorIncompatibleWithEpoll, - } -} - -pub fn linuxEpollWait(epfd: i32, events: []linux.epoll_event, timeout: i32) usize { - while (true) { - const rc = posix.epoll_wait(epfd, events.ptr, @intCast(u32, events.len), timeout); - const err = posix.getErrno(rc); - switch (err) { - 0 => return rc, - posix.EINTR => continue, - posix.EBADF => unreachable, - posix.EFAULT => unreachable, - posix.EINVAL => unreachable, - else => unreachable, - } - } -} - -pub const LinuxEventFdError = error{ - InvalidFlagValue, - SystemResources, - ProcessFdQuotaExceeded, - SystemFdQuotaExceeded, - - /// See https://github.com/ziglang/zig/issues/1396 - Unexpected, -}; - -pub fn linuxEventFd(initval: u32, flags: u32) LinuxEventFdError!i32 { - const rc = posix.eventfd(initval, flags); - const err = posix.getErrno(rc); - switch (err) { - 0 => return @intCast(i32, rc), - else => return unexpectedErrorPosix(err), - - posix.EINVAL => return LinuxEventFdError.InvalidFlagValue, - posix.EMFILE => return LinuxEventFdError.ProcessFdQuotaExceeded, - posix.ENFILE => return LinuxEventFdError.SystemFdQuotaExceeded, - posix.ENODEV => return LinuxEventFdError.SystemResources, - posix.ENOMEM => return LinuxEventFdError.SystemResources, - } -} - -pub const PosixGetSockNameError = error{ - /// Insufficient resources were available in the system to perform the operation. - SystemResources, - - /// See https://github.com/ziglang/zig/issues/1396 - Unexpected, -}; - -pub fn posixGetSockName(sockfd: i32) PosixGetSockNameError!posix.sockaddr { - var addr: posix.sockaddr = undefined; - var addrlen: posix.socklen_t = @sizeOf(posix.sockaddr); - const rc = posix.getsockname(sockfd, &addr, &addrlen); - const err = posix.getErrno(rc); - switch (err) { - 0 => return addr, - else => return unexpectedErrorPosix(err), - - posix.EBADF => unreachable, - posix.EFAULT => unreachable, - posix.EINVAL => unreachable, - posix.ENOTSOCK => unreachable, - posix.ENOBUFS => return PosixGetSockNameError.SystemResources, - } -} - -pub const PosixConnectError = 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, - - /// See https://github.com/ziglang/zig/issues/1396 - Unexpected, -}; - -pub fn posixConnect(sockfd: i32, sockaddr: *const posix.sockaddr) PosixConnectError!void { - while (true) { - const rc = posix.connect(sockfd, sockaddr, @sizeOf(posix.sockaddr)); - const err = posix.getErrno(rc); - switch (err) { - 0 => return, - else => return unexpectedErrorPosix(err), - - posix.EACCES => return PosixConnectError.PermissionDenied, - posix.EPERM => return PosixConnectError.PermissionDenied, - posix.EADDRINUSE => return PosixConnectError.AddressInUse, - posix.EADDRNOTAVAIL => return PosixConnectError.AddressNotAvailable, - posix.EAFNOSUPPORT => return PosixConnectError.AddressFamilyNotSupported, - posix.EAGAIN => return PosixConnectError.SystemResources, - posix.EALREADY => unreachable, // The socket is nonblocking and a previous connection attempt has not yet been completed. - posix.EBADF => unreachable, // sockfd is not a valid open file descriptor. - posix.ECONNREFUSED => return PosixConnectError.ConnectionRefused, - posix.EFAULT => unreachable, // The socket structure address is outside the user's address space. - posix.EINPROGRESS => unreachable, // The socket is nonblocking and the connection cannot be completed immediately. - posix.EINTR => continue, - posix.EISCONN => unreachable, // The socket is already connected. - posix.ENETUNREACH => return PosixConnectError.NetworkUnreachable, - posix.ENOTSOCK => unreachable, // The file descriptor sockfd does not refer to a socket. - posix.EPROTOTYPE => unreachable, // The socket type does not support the requested communications protocol. - posix.ETIMEDOUT => return PosixConnectError.ConnectionTimedOut, - } - } -} - -/// Same as posixConnect except it is for blocking socket file descriptors. -/// It expects to receive EINPROGRESS. -pub fn posixConnectAsync(sockfd: i32, sockaddr: *const c_void, len: u32) PosixConnectError!void { - while (true) { - const rc = posix.connect(sockfd, sockaddr, len); - const err = posix.getErrno(rc); - switch (err) { - 0, posix.EINPROGRESS => return, - else => return unexpectedErrorPosix(err), - - posix.EACCES => return PosixConnectError.PermissionDenied, - posix.EPERM => return PosixConnectError.PermissionDenied, - posix.EADDRINUSE => return PosixConnectError.AddressInUse, - posix.EADDRNOTAVAIL => return PosixConnectError.AddressNotAvailable, - posix.EAFNOSUPPORT => return PosixConnectError.AddressFamilyNotSupported, - posix.EAGAIN => return PosixConnectError.SystemResources, - posix.EALREADY => unreachable, // The socket is nonblocking and a previous connection attempt has not yet been completed. - posix.EBADF => unreachable, // sockfd is not a valid open file descriptor. - posix.ECONNREFUSED => return PosixConnectError.ConnectionRefused, - posix.EFAULT => unreachable, // The socket structure address is outside the user's address space. - posix.EINTR => continue, - posix.EISCONN => unreachable, // The socket is already connected. - posix.ENETUNREACH => return PosixConnectError.NetworkUnreachable, - posix.ENOTSOCK => unreachable, // The file descriptor sockfd does not refer to a socket. - posix.EPROTOTYPE => unreachable, // The socket type does not support the requested communications protocol. - posix.ETIMEDOUT => return PosixConnectError.ConnectionTimedOut, - } - } -} - -pub fn posixGetSockOptConnectError(sockfd: i32) PosixConnectError!void { - var err_code: i32 = undefined; - var size: u32 = @sizeOf(i32); - const rc = posix.getsockopt(sockfd, posix.SOL_SOCKET, posix.SO_ERROR, @ptrCast([*]u8, &err_code), &size); - assert(size == 4); - const err = posix.getErrno(rc); - switch (err) { - 0 => switch (err_code) { - 0 => return, - else => return unexpectedErrorPosix(err), - - posix.EACCES => return PosixConnectError.PermissionDenied, - posix.EPERM => return PosixConnectError.PermissionDenied, - posix.EADDRINUSE => return PosixConnectError.AddressInUse, - posix.EADDRNOTAVAIL => return PosixConnectError.AddressNotAvailable, - posix.EAFNOSUPPORT => return PosixConnectError.AddressFamilyNotSupported, - posix.EAGAIN => return PosixConnectError.SystemResources, - posix.EALREADY => unreachable, // The socket is nonblocking and a previous connection attempt has not yet been completed. - posix.EBADF => unreachable, // sockfd is not a valid open file descriptor. - posix.ECONNREFUSED => return PosixConnectError.ConnectionRefused, - posix.EFAULT => unreachable, // The socket structure address is outside the user's address space. - posix.EISCONN => unreachable, // The socket is already connected. - posix.ENETUNREACH => return PosixConnectError.NetworkUnreachable, - posix.ENOTSOCK => unreachable, // The file descriptor sockfd does not refer to a socket. - posix.EPROTOTYPE => unreachable, // The socket type does not support the requested communications protocol. - posix.ETIMEDOUT => return PosixConnectError.ConnectionTimedOut, - }, - else => return unexpectedErrorPosix(err), - posix.EBADF => unreachable, // The argument sockfd is not a valid file descriptor. - posix.EFAULT => unreachable, // The address pointed to by optval or optlen is not in a valid part of the process address space. - posix.EINVAL => unreachable, - posix.ENOPROTOOPT => unreachable, // The option is unknown at the level indicated. - posix.ENOTSOCK => unreachable, // The file descriptor sockfd does not refer to a socket. - } -} - pub const Thread = struct { data: Data, @@ -3123,7 +1555,6 @@ pub const SpawnThreadError = error{ /// Not enough userland memory to spawn the thread. OutOfMemory, - /// See https://github.com/ziglang/zig/issues/1396 Unexpected, }; @@ -3301,47 +1732,16 @@ pub fn spawnThread(context: var, comptime startFn: var) SpawnThreadError!*Thread } } -pub fn posixWait(pid: i32) i32 { - var status: i32 = undefined; - while (true) { - const err = posix.getErrno(posix.waitpid(pid, &status, 0)); - switch (err) { - 0 => return status, - posix.EINTR => continue, - posix.ECHILD => unreachable, // The process specified does not exist. It would be a race condition to handle this error. - posix.EINVAL => unreachable, // The options argument was invalid - else => unreachable, - } - } -} - -pub fn posixFStat(fd: i32) !posix.Stat { - var stat: posix.Stat = undefined; - const err = posix.getErrno(posix.fstat(fd, &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), - }; - } - - return stat; -} - pub const CpuCountError = error{ OutOfMemory, PermissionDenied, - /// See https://github.com/ziglang/zig/issues/1396 Unexpected, }; pub fn cpuCount(fallback_allocator: *mem.Allocator) CpuCountError!usize { switch (builtin.os) { - builtin.Os.macosx, builtin.Os.freebsd, builtin.Os.netbsd => { + .macosx, .freebsd, .netbsd => { var count: c_int = undefined; var count_len: usize = @sizeOf(c_int); const rc = posix.sysctlbyname(switch (builtin.os) { @@ -3361,7 +1761,7 @@ pub fn cpuCount(fallback_allocator: *mem.Allocator) CpuCountError!usize { else => return os.unexpectedErrorPosix(err), } }, - builtin.Os.linux => { + .linux => { const usize_count = 16; const allocator = std.heap.stackFallback(usize_count * @sizeOf(usize), fallback_allocator).get(); @@ -3393,7 +1793,7 @@ pub fn cpuCount(fallback_allocator: *mem.Allocator) CpuCountError!usize { } } }, - builtin.Os.windows => { + .windows => { var system_info: windows.SYSTEM_INFO = undefined; windows.GetSystemInfo(&system_info); return @intCast(usize, system_info.dwNumberOfProcessors); @@ -3401,128 +1801,3 @@ pub fn cpuCount(fallback_allocator: *mem.Allocator) CpuCountError!usize { else => @compileError("unsupported OS"), } } - -pub const BsdKQueueError = 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, - - /// See https://github.com/ziglang/zig/issues/1396 - Unexpected, -}; - -pub fn bsdKQueue() BsdKQueueError!i32 { - const rc = posix.kqueue(); - const err = posix.getErrno(rc); - switch (err) { - 0 => return @intCast(i32, rc), - posix.EMFILE => return BsdKQueueError.ProcessFdQuotaExceeded, - posix.ENFILE => return BsdKQueueError.SystemFdQuotaExceeded, - else => return unexpectedErrorPosix(err), - } -} - -pub const BsdKEventError = 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 bsdKEvent( - kq: i32, - changelist: []const posix.Kevent, - eventlist: []posix.Kevent, - timeout: ?*const posix.timespec, -) BsdKEventError!usize { - while (true) { - const rc = posix.kevent(kq, changelist, eventlist, timeout); - const err = posix.getErrno(rc); - switch (err) { - 0 => return rc, - posix.EACCES => return BsdKEventError.AccessDenied, - posix.EFAULT => unreachable, - posix.EBADF => unreachable, - posix.EINTR => continue, - posix.EINVAL => unreachable, - posix.ENOENT => return BsdKEventError.EventNotFound, - posix.ENOMEM => return BsdKEventError.SystemResources, - posix.ESRCH => return BsdKEventError.ProcessNotFound, - else => unreachable, - } - } -} - -pub fn linuxINotifyInit1(flags: u32) !i32 { - const rc = linux.inotify_init1(flags); - const err = posix.getErrno(rc); - switch (err) { - 0 => return @intCast(i32, rc), - posix.EINVAL => unreachable, - posix.EMFILE => return error.ProcessFdQuotaExceeded, - posix.ENFILE => return error.SystemFdQuotaExceeded, - posix.ENOMEM => return error.SystemResources, - else => return unexpectedErrorPosix(err), - } -} - -pub fn linuxINotifyAddWatchC(inotify_fd: i32, pathname: [*]const u8, mask: u32) !i32 { - const rc = linux.inotify_add_watch(inotify_fd, pathname, mask); - const err = posix.getErrno(rc); - switch (err) { - 0 => return @intCast(i32, rc), - posix.EACCES => return error.AccessDenied, - posix.EBADF => unreachable, - posix.EFAULT => unreachable, - posix.EINVAL => unreachable, - posix.ENAMETOOLONG => return error.NameTooLong, - posix.ENOENT => return error.FileNotFound, - posix.ENOMEM => return error.SystemResources, - posix.ENOSPC => return error.UserResourceLimitReached, - else => return unexpectedErrorPosix(err), - } -} - -pub fn linuxINotifyRmWatch(inotify_fd: i32, wd: i32) !void { - const rc = linux.inotify_rm_watch(inotify_fd, wd); - const err = posix.getErrno(rc); - switch (err) { - 0 => return rc, - posix.EBADF => unreachable, - posix.EINVAL => unreachable, - else => unreachable, - } -} - -pub const MProtectError = error{ - AccessDenied, - OutOfMemory, - Unexpected, -}; - -/// address and length must be page-aligned -pub fn posixMProtect(address: usize, length: usize, protection: u32) MProtectError!void { - const negative_page_size = @bitCast(usize, -isize(page_size)); - const aligned_address = address & negative_page_size; - const aligned_end = (address + length + page_size - 1) & negative_page_size; - assert(address == aligned_address); - assert(length == aligned_end - aligned_address); - const rc = posix.mprotect(address, length, protection); - const err = posix.getErrno(rc); - switch (err) { - 0 => return, - posix.EINVAL => unreachable, - posix.EACCES => return error.AccessDenied, - posix.ENOMEM => return error.OutOfMemory, - else => return unexpectedErrorPosix(err), - } -} diff --git a/std/os/child_process.zig b/std/os/child_process.zig index 3269e39c7a15..1023216c5ff9 100644 --- a/std/os/child_process.zig +++ b/std/os/child_process.zig @@ -415,7 +415,7 @@ pub const ChildProcess = struct { os.posix_setreuid(uid, uid) catch |err| forkChildErrReport(err_pipe[1], err); } - os.posixExecve(self.argv, env_map, self.allocator) catch |err| forkChildErrReport(err_pipe[1], err); + os.posix.execve(self.allocator, self.argv, env_map) catch |err| forkChildErrReport(err_pipe[1], err); } // we are the parent diff --git a/std/os/darwin.zig b/std/os/darwin.zig index 04122100f40d..a6dcc034df3f 100644 --- a/std/os/darwin.zig +++ b/std/os/darwin.zig @@ -1,9 +1,16 @@ +const builtin = @import("builtin"); const std = @import("../std.zig"); const c = std.c; const assert = std.debug.assert; const maxInt = std.math.maxInt; -pub use @import("darwin/errno.zig"); +pub const is_the_target = switch (builtin.os) { + .ios, .macosx, .watchos, .tvos => true, + else => false, +}; + +pub const errno_codes = @import("darwin/errno.zig"); +pub use errno_codes; pub const PATH_MAX = 1024; diff --git a/std/os/file.zig b/std/os/file.zig index d223d55a46fd..b34e8c635360 100644 --- a/std/os/file.zig +++ b/std/os/file.zig @@ -223,9 +223,18 @@ pub const File = struct { os.close(self.handle); } - /// Calls `os.isTty` on `self.handle`. + /// Test whether the file refers to a terminal. + /// See also `supportsAnsiEscapeCodes`. pub fn isTty(self: File) bool { - return os.isTty(self.handle); + 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{ @@ -389,43 +398,16 @@ pub const File = struct { } } - pub const ReadError = os.WindowsReadError || os.PosixReadError; + pub const ReadError = posix.ReadError; pub fn read(self: File, buffer: []u8) ReadError!usize { - if (is_posix) { - return os.posixRead(self.handle, buffer); - } else if (is_windows) { - var index: usize = 0; - while (index < buffer.len) { - const want_read_count = @intCast(windows.DWORD, math.min(windows.DWORD(maxInt(windows.DWORD)), buffer.len - index)); - var amt_read: windows.DWORD = undefined; - if (windows.ReadFile(self.handle, buffer.ptr + index, want_read_count, &amt_read, null) == 0) { - const err = windows.GetLastError(); - return switch (err) { - windows.ERROR.OPERATION_ABORTED => continue, - windows.ERROR.BROKEN_PIPE => return index, - else => os.unexpectedErrorWindows(err), - }; - } - if (amt_read == 0) return index; - index += amt_read; - } - return index; - } else { - @compileError("Unsupported OS"); - } + return posix.read(self.handle, buffer); } - pub const WriteError = os.WindowsWriteError || os.PosixWriteError; + pub const WriteError = posix.WriteError; pub fn write(self: File, bytes: []const u8) WriteError!void { - if (is_posix) { - try os.posixWrite(self.handle, bytes); - } else if (is_windows) { - try os.windowsWrite(self.handle, bytes); - } else { - @compileError("Unsupported OS"); - } + return posix.write(self.handle, bytes); } pub fn inStream(file: File) InStream { @@ -509,4 +491,19 @@ pub const File = struct { 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); + } }; diff --git a/std/os/linux.zig b/std/os/linux.zig index ecbbf72d4276..db816cf1bed9 100644 --- a/std/os/linux.zig +++ b/std/os/linux.zig @@ -12,7 +12,12 @@ pub use switch (builtin.arch) { builtin.Arch.aarch64 => @import("linux/arm64.zig"), else => @compileError("unsupported arch"), }; -pub use @import("linux/errno.zig"); +pub const is_the_target = builtin.os == .linux; +pub const errno_codes = @import("linux/errno.zig"); +pub use errno_codes; + +/// See `std.os.posix.getauxval`. +pub var elf_aux_maybe: ?[*]std.elf.Auxv = null; pub const PATH_MAX = 4096; pub const IOV_MAX = 1024; @@ -697,9 +702,9 @@ pub const winsize = extern struct { }; /// Get the errno from a syscall return value, or 0 for no error. -pub fn getErrno(r: usize) usize { +pub fn getErrno(r: usize) u12 { const signed_r = @bitCast(isize, r); - return if (signed_r > -4096 and signed_r < 0) @intCast(usize, -signed_r) else 0; + return if (signed_r > -4096 and signed_r < 0) @intCast(u12, -signed_r) else 0; } pub fn dup2(old: i32, new: i32) usize { @@ -766,11 +771,6 @@ pub fn inotify_rm_watch(fd: i32, wd: i32) usize { return syscall2(SYS_inotify_rm_watch, @bitCast(usize, isize(fd)), @bitCast(usize, isize(wd))); } -pub fn isatty(fd: i32) bool { - var wsz: winsize = undefined; - return syscall3(SYS_ioctl, @bitCast(usize, isize(fd)), TIOCGWINSZ, @ptrToInt(&wsz)) == 0; -} - // TODO https://github.com/ziglang/zig/issues/265 pub fn readlink(noalias path: [*]const u8, noalias buf_ptr: [*]u8, buf_len: usize) usize { return readlinkat(AT_FDCWD, path, buf_ptr, buf_len); @@ -1137,15 +1137,6 @@ pub const SIG_DFL = @intToPtr(extern fn (i32) void, 0); pub const SIG_IGN = @intToPtr(extern fn (i32) void, 1); pub const empty_sigset = []usize{0} ** sigset_t.len; -pub fn raise(sig: i32) usize { - var set: sigset_t = undefined; - blockAppSignals(&set); - const tid = syscall0(SYS_gettid); - const ret = syscall2(SYS_tkill, tid, @bitCast(usize, isize(sig))); - restoreSignals(&set); - return ret; -} - fn blockAllSignals(set: *sigset_t) void { _ = syscall4(SYS_rt_sigprocmask, SIG_BLOCK, @ptrToInt(&all_mask), @ptrToInt(set), NSIG / 8); } @@ -1672,7 +1663,7 @@ pub fn dl_iterate_phdr(comptime T: type, callback: extern fn (info: *dl_phdr_inf } test "import" { - if (builtin.os == builtin.Os.linux) { + if (is_the_target) { _ = @import("linux/test.zig"); } } diff --git a/std/os/linux/tls.zig b/std/os/linux/tls.zig index 50225a235bb0..e040a45bc042 100644 --- a/std/os/linux/tls.zig +++ b/std/os/linux/tls.zig @@ -114,7 +114,7 @@ pub fn setThreadPointer(addr: usize) void { .aarch64 => { asm volatile ( \\ msr tpidr_el0, %[addr] - : + : : [addr] "r" (addr) ); }, @@ -126,7 +126,7 @@ pub fn initTLS() void { var tls_phdr: ?*elf.Phdr = null; var img_base: usize = 0; - const auxv = std.os.linux_elf_aux_maybe.?; + const auxv = std.os.linux.elf_aux_maybe.?; var at_phent: usize = undefined; var at_phnum: usize = undefined; var at_phdr: usize = undefined; diff --git a/std/os/posix.zig b/std/os/posix.zig new file mode 100644 index 000000000000..35fe408b4374 --- /dev/null +++ b/std/os/posix.zig @@ -0,0 +1,2159 @@ +// 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. For example, on Linux `rename` might call +// SYS_renameat or SYS_rename depending on the architecture. +// * 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. +// * Work around kernel bugs and limitations. For example, if a function accepts +// a `usize` number of bytes to write, but the kernel can only handle maxInt(u32) +// number of bytes, this API layer should introduce a loop to make multiple +// syscalls so that the full `usize` number of bytes are written. +// * Implement the OS-specific functions, types, and definitions that the Zig +// standard library needs, at the same API abstraction layer as outlined above. +// this includes, for example Windows functions. +// * When there exists a corresponding libc function and linking libc, call the +// libc function. +// 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 wasi = os.wasi; +const linux = os.linux; +const testing = std.testing; + +pub const FileHandle = if (windows.is_the_target) windows.HANDLE else if (wasi.is_the_target) wasi.fd_t else i32; +pub use system.errno_codes; + +pub const PATH_MAX = system.PATH_MAX; + +/// > The maximum path of 32,767 characters is approximate, because the "\\?\" +/// > prefix may be expanded to a longer string by the system at run time, and +/// > this expansion applies to the total length. +/// 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 iovec = system.iovec; +pub const iovec_const = system.iovec_const; + +/// 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 handle. +/// 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(handle: FileHandle) void { + if (windows.is_the_target and !builtin.link_libc) { + assert(windows.CloseHandle(handle) != 0); + return; + } + if (wasi.is_the_target) { + switch (wasi.fd_close(handle)) { + 0 => return, + else => |err| return unexpectedErrno(err), + } + } + switch (system.getErrno(system.close(handle))) { + 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.RtlGenRandom(buf.ptr, buf.len) == 0) { + const err = windows.GetLastError(); + return switch (err) { + else => unexpectedErrorWindows(err), + }; + } + return; + } + if (linux.is_the_target) { + while (true) { + switch (system.getErrno(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) { + c.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 (system.getErrno(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 (system.getErrno(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) { + std.c.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: FileHandle, 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) { + const err = windows.GetLastError(); + return switch (err) { + windows.ERROR.OPERATION_ABORTED => continue, + windows.ERROR.BROKEN_PIPE => return index, + else => unexpectedErrorWindows(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 (system.getErrno(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: FileHandle, 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); + const err = system.getErrno(rc); + switch (err) { + 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 => 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: FileHandle, 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 unexpectedErrorWindows(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); + const write_err = system.getErrno(rc); + switch (write_err) { + 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 => return unexpectedErrno(write_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: FileHandle, 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); + const err = system.getErrno(rc); + switch (err) { + 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 => 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!FileHandle { + 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!FileHandle { + while (true) { + const rc = system.open(file_path, flags, perm); + switch (system.getErrno(rc)) { + 0 => return @intCast(FileHandle, 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 const WindowsOpenError = error{ + SharingViolation, + PathAlreadyExists, + + /// When any of the path components can not be found or the file component can not + /// be found. Some operating systems distinguish between path components not found and + /// file components not found, but they are collapsed into FileNotFound to gain + /// consistency across operating systems. + FileNotFound, + + AccessDenied, + PipeBusy, + NameTooLong, + + /// On Windows, file paths must be valid Unicode. + InvalidUtf8, + + /// On Windows, file paths cannot contain these characters: + /// '/', '*', '?', '"', '<', '>', '|' + BadPathName, + + Unexpected, +}; + +pub fn openWindows( + file_path: []const u8, + desired_access: windows.DWORD, + share_mode: windows.DWORD, + creation_disposition: windows.DWORD, + flags_and_attrs: windows.DWORD, +) WindowsOpenError!FileHandle { + const file_path_w = try sliceToPrefixedFileW(file_path); + return openW(&file_path_w, desired_access, share_mode, creation_disposition, flags_and_attrs); +} + +pub fn openW( + file_path_w: [*]const u16, + desired_access: windows.DWORD, + share_mode: windows.DWORD, + creation_disposition: windows.DWORD, + flags_and_attrs: windows.DWORD, +) WindowsOpenError!windows.HANDLE { + const result = windows.CreateFileW(file_path_w, desired_access, share_mode, null, creation_disposition, flags_and_attrs, null); + + if (result == windows.INVALID_HANDLE_VALUE) { + const err = windows.GetLastError(); + switch (err) { + windows.ERROR.SHARING_VIOLATION => return error.SharingViolation, + windows.ERROR.ALREADY_EXISTS => return error.PathAlreadyExists, + windows.ERROR.FILE_EXISTS => return error.PathAlreadyExists, + windows.ERROR.FILE_NOT_FOUND => return error.FileNotFound, + windows.ERROR.PATH_NOT_FOUND => return error.FileNotFound, + windows.ERROR.ACCESS_DENIED => return error.AccessDenied, + windows.ERROR.PIPE_BUSY => return error.PipeBusy, + else => return unexpectedErrorWindows(err), + } + } + + return result; +} + +pub fn dup2(old_fd: FileHandle, new_fd: FileHandle) !void { + while (true) { + switch (system.getErrno(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. +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(system.getErrno(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 = system.getErrno(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 = std.c.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(std.c.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_util.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) { + const err = windows.GetLastError(); + switch (err) { + else => return unexpectedErrorWindows(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 (std.c.getcwd(out_buffer.ptr, out_buffer.len)) |_| 0 else std.c._errno().*; + } else blk: { + break :blk system.getErrno(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: FileHandle, 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: FileHandle, new_path: [*]const u8) SymLinkError!void { + const err = blk: { + if (builtin.link_libc) { + break :blk if (std.c.symlinkat(target_path, newdirfd, new_path) == -1) errno().* else 0; + } else { + break :blk system.getErrno(system.symlinkat(target_path, newdirfd, new_path)); + } + }; + switch (err) { + 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 => 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); + } + const err = if (builtin.link_libc) blk: { + break :blk if (std.c.symlink(target_path, new_path) == -1) errno().* else 0; + } else if (@hasDecl(system, "symlink")) blk: { + break :blk system.getErrno(system.symlink(target_path, new_path)); + } else blk: { + break :blk system.getErrno(system.symlinkat(target_path, AT_FDCWD, new_path)); + }; + switch (err) { + 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 => 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) { + const err = windows.GetLastError(); + switch (err) { + else => return unexpectedErrorWindows(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) { + 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, + windows.ERROR.INVALID_PARAMETER => return error.NameTooLong, + else => return unexpectedErrorWindows(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); + } + const err = if (builtin.link_libc) blk: { + break :blk if (std.c.unlink(file_path) == -1) errno().* else 0; + } else if (@hasDecl(system, "unlink")) blk: { + break :blk system.getErrno(system.unlink(file_path)); + } else blk: { + break :blk system.getErrno(system.unlinkat(AT_FDCWD, file_path, 0)); + }; + switch (err) { + 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 => 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); + } + const err = if (builtin.link_libc) blk: { + break :blk if (std.c.rename(old_path, new_path) == -1) errno().* else 0; + } else if (@hasDecl(system, "rename")) blk: { + break :blk system.getErrno(system.rename(old_path, new_path)); + } else if (@hasDecl(system, "renameat")) blk: { + break :blk system.getErrno(system.renameat(AT_FDCWD, old_path, AT_FDCWD, new_path)); + } else blk: { + break :blk system.getErrno(system.renameat2(AT_FDCWD, old_path, AT_FDCWD, new_path, 0)); + }; + switch (err) { + 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 => 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) { + const err = windows.GetLastError(); + switch (err) { + else => return unexpectedErrorWindows(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); + } + const err = if (builtin.link_libc) blk: { + break :blk if (std.c.mkdir(dir_path, mode) == -1) errno().* else 0; + } else if (@hasDecl(system, "mkdir")) blk: { + break :blk system.getErrno(system.mkdir(dir_path, mode)); + } else blk: { + break :blk system.getErrno(system.mkdirat(AT_FDCWD, dir_path, mode)); + }; + switch (err) { + 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 => 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) { + const err = windows.GetLastError(); + switch (err) { + windows.ERROR.ALREADY_EXISTS => return error.PathAlreadyExists, + windows.ERROR.PATH_NOT_FOUND => return error.FileNotFound, + else => return unexpectedErrorWindows(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); + } + const err = if (builtin.link_libc) blk: { + break :blk if (std.c.rmdir(dir_path) == -1) errno().* else 0; + } else if (@hasDecl(system, "rmdir")) blk: { + break :blk system.getErrno(system.rmdir(dir_path)); + } else blk: { + break :blk system.getErrno(system.unlinkat(AT_FDCWD, dir_path, AT_REMOVEDIR)); + }; + switch (err) { + 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 => 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) { + const err = windows.GetLastError(); + switch (err) { + windows.ERROR.PATH_NOT_FOUND => return error.FileNotFound, + windows.ERROR.DIR_NOT_EMPTY => return error.DirNotEmpty, + else => return unexpectedErrorWindows(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); + } + const err = if (builtin.link_libc) blk: { + break :blk if (std.c.chdir(dir_path) == -1) errno().* else 0; + } else blk: { + break :blk system.getErrno(system.chdir(dir_path)); + }; + switch (err) { + 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 => 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 err = if (builtin.link_libc) blk: { + break :blk if (std.c.readlink(file_path, out_buffer.ptr, out_buffer.len) == -1) errno().* else 0; + } else if (@hasDecl(system, "readlink")) blk: { + break :blk system.getErrno(system.readlink(file_path, out_buffer.ptr, out_buffer.len)); + } else blk: { + break :blk system.getErrno(system.readlinkat(AT_FDCWD, file_path, out_buffer.ptr, out_buffer.len)); + }; + const rc = system.readlink(file_path, out_buffer, out_buffer.len); + switch (system.getErrno(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 (system.getErrno(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 (system.getErrno(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 (system.getErrno(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 (system.getErrno(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!FileHandle { + 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| unexpectedErrorWindows(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: FileHandle) bool { + if (builtin.link_libc) { + return c.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: FileHandle) 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 (system.getErrno(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 (system.getErrno(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 (system.getErrno(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 (system.getErrno(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 (system.getErrno(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 (system.getErrno(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 (system.getErrno(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 (system.getErrno(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 (system.getErrno(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 (system.getErrno(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 (system.getErrno(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 (system.getErrno(system.connect(sockfd, sockaddr, len))) { + 0, EINPROGRESS => return, + EINTR => continue, + else => 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 (system.getErrno(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 wait(pid: i32) i32 { + var status: i32 = undefined; + while (true) { + switch (system.getErrno(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 fn fstat(fd: FileHandle) !Stat { + var stat: Stat = undefined; + switch (system.getErrno(system.fstat(fd, &stat))) { + 0 => return stat, + EBADF => unreachable, // Always a race condition. + ENOMEM => return error.SystemResources, + else => |err| return unexpectedErrno(err), + } + + return stat; +} + +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 (system.getErrno(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 (system.getErrno(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 (system.getErrno(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 (system.getErrno(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 (system.getErrno(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(page_size)); + const aligned_address = address & negative_page_size; + const aligned_end = (address + length + page_size - 1) & negative_page_size; + assert(address == aligned_address); + assert(length == aligned_end - aligned_address); + switch (system.getErrno(system.mprotect(address, length, protection))) { + 0 => return, + EINVAL => unreachable, + EACCES => return error.AccessDenied, + ENOMEM => return error.OutOfMemory, + else => return unexpectedErrno(err), + } +} + +/// 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; +} + +const unexpected_error_tracing = builtin.mode == .Debug; +const UnexpectedError = error{ + /// The Operating System returned an undocumented error code. + Unexpected, +}; + +/// Call this when you made a syscall or something that sets errno +/// and you get an unexpected error. +pub fn unexpectedErrno(errno: usize) UnexpectedError { + if (unexpected_error_tracing) { + std.debug.warn("unexpected errno: {}\n", errno); + std.debug.dumpCurrentStackTrace(null); + } + return error.Unexpected; +} + +/// Call this when you made a windows DLL call or something that does SetLastError +/// and you get an unexpected error. +pub fn unexpectedErrorWindows(err: windows.DWORD) UnexpectedError { + if (unexpected_error_tracing) { + std.debug.warn("unexpected GetLastError(): {}\n", err); + std.debug.dumpCurrentStackTrace(null); + } + return error.Unexpected; +} + +pub fn cStrToPrefixedFileW(s: [*]const u8) ![PATH_MAX_WIDE + 1]u16 { + return sliceToPrefixedFileW(mem.toSliceConst(u8, s)); +} + +pub fn sliceToPrefixedFileW(s: []const u8) ![PATH_MAX_WIDE + 1]u16 { + return sliceToPrefixedSuffixedFileW(s, []u16{0}); +} + +pub fn sliceToPrefixedSuffixedFileW(s: []const u8, comptime suffix: []const u16) ![PATH_MAX_WIDE + suffix.len]u16 { + // TODO well defined copy elision + var result: [PATH_MAX_WIDE + suffix.len]u16 = undefined; + + // > File I/O functions in the Windows API convert "/" to "\" as part of + // > converting the name to an NT-style name, except when using the "\\?\" + // > prefix as detailed in the following sections. + // from https://docs.microsoft.com/en-us/windows/desktop/FileIO/naming-a-file#maximum-path-length-limitation + // Because we want the larger maximum path length for absolute paths, we + // disallow forward slashes in zig std lib file functions on Windows. + for (s) |byte| { + switch (byte) { + '/', '*', '?', '"', '<', '>', '|' => return error.BadPathName, + else => {}, + } + } + const start_index = if (mem.startsWith(u8, s, "\\\\") or !os.path.isAbsolute(s)) 0 else blk: { + const prefix = []u16{ '\\', '\\', '?', '\\' }; + mem.copy(u16, result[0..], prefix); + break :blk prefix.len; + }; + const end_index = start_index + try std.unicode.utf8ToUtf16Le(result[start_index..], s); + assert(end_index <= result.len); + if (end_index + suffix.len > result.len) return error.NameTooLong; + mem.copy(u16, result[end_index..], suffix); + return result; +} diff --git a/std/os/wasi.zig b/std/os/wasi.zig index 2118db9a2a34..7da825c3948f 100644 --- a/std/os/wasi.zig +++ b/std/os/wasi.zig @@ -1,42 +1,393 @@ -pub use @import("wasi/core.zig"); +// Based on https://github.com/CraneStation/wasi-sysroot/blob/wasi/libc-bottom-half/headers/public/wasi/core.h +// and https://github.com/WebAssembly/WASI/blob/master/design/WASI-core.md +const std = @import("std"); +const assert = std.debug.assert; + +pub const is_the_target = @import("builtin").os == .wasi; pub const STDIN_FILENO = 0; pub const STDOUT_FILENO = 1; pub const STDERR_FILENO = 2; -pub fn getErrno(r: usize) usize { - const signed_r = @bitCast(isize, r); - return if (signed_r > -4096 and signed_r < 0) @intCast(usize, -signed_r) else 0; +comptime { + assert(@alignOf(i8) == 1); + assert(@alignOf(u8) == 1); + assert(@alignOf(i16) == 2); + assert(@alignOf(u16) == 2); + assert(@alignOf(i32) == 4); + assert(@alignOf(u32) == 4); + assert(@alignOf(i64) == 8); + assert(@alignOf(u64) == 8); } -pub fn write(fd: i32, buf: [*]const u8, count: usize) usize { - var nwritten: usize = undefined; +pub const advice_t = u8; +pub const ADVICE_NORMAL: advice_t = 0; +pub const ADVICE_SEQUENTIAL: advice_t = 1; +pub const ADVICE_RANDOM: advice_t = 2; +pub const ADVICE_WILLNEED: advice_t = 3; +pub const ADVICE_DONTNEED: advice_t = 4; +pub const ADVICE_NOREUSE: advice_t = 5; - const ciovs = ciovec_t{ - .buf = buf, - .buf_len = count, - }; +pub const ciovec_t = extern struct { + buf: [*]const u8, + buf_len: usize, +}; - const err = fd_write(@bitCast(fd_t, isize(fd)), &ciovs, 1, &nwritten); - if (err == ESUCCESS) { - return nwritten; - } else { - return @bitCast(usize, -isize(err)); - } -} +pub const clockid_t = u32; +pub const CLOCK_REALTIME: clockid_t = 0; +pub const CLOCK_MONOTONIC: clockid_t = 1; +pub const CLOCK_PROCESS_CPUTIME_ID: clockid_t = 2; +pub const CLOCK_THREAD_CPUTIME_ID: clockid_t = 3; -pub fn read(fd: i32, buf: [*]u8, nbyte: usize) usize { - var nread: usize = undefined; +pub const device_t = u64; - const iovs = iovec_t{ - .buf = buf, - .buf_len = nbyte, - }; +pub const dircookie_t = u64; +pub const DIRCOOKIE_START: dircookie_t = 0; - const err = fd_read(@bitCast(fd_t, isize(fd)), &iovs, 1, &nread); - if (err == ESUCCESS) { - return nread; - } else { - return @bitCast(usize, -isize(err)); - } -} +pub const dirent_t = extern struct { + d_next: dircookie_t, + d_ino: inode_t, + d_namlen: u32, + d_type: filetype_t, +}; + +pub const errno_t = u16; +pub const ESUCCESS: errno_t = 0; +pub const E2BIG: errno_t = 1; +pub const EACCES: errno_t = 2; +pub const EADDRINUSE: errno_t = 3; +pub const EADDRNOTAVAIL: errno_t = 4; +pub const EAFNOSUPPORT: errno_t = 5; +pub const EAGAIN: errno_t = 6; +pub const EALREADY: errno_t = 7; +pub const EBADF: errno_t = 8; +pub const EBADMSG: errno_t = 9; +pub const EBUSY: errno_t = 10; +pub const ECANCELED: errno_t = 11; +pub const ECHILD: errno_t = 12; +pub const ECONNABORTED: errno_t = 13; +pub const ECONNREFUSED: errno_t = 14; +pub const ECONNRESET: errno_t = 15; +pub const EDEADLK: errno_t = 16; +pub const EDESTADDRREQ: errno_t = 17; +pub const EDOM: errno_t = 18; +pub const EDQUOT: errno_t = 19; +pub const EEXIST: errno_t = 20; +pub const EFAULT: errno_t = 21; +pub const EFBIG: errno_t = 22; +pub const EHOSTUNREACH: errno_t = 23; +pub const EIDRM: errno_t = 24; +pub const EILSEQ: errno_t = 25; +pub const EINPROGRESS: errno_t = 26; +pub const EINTR: errno_t = 27; +pub const EINVAL: errno_t = 28; +pub const EIO: errno_t = 29; +pub const EISCONN: errno_t = 30; +pub const EISDIR: errno_t = 31; +pub const ELOOP: errno_t = 32; +pub const EMFILE: errno_t = 33; +pub const EMLINK: errno_t = 34; +pub const EMSGSIZE: errno_t = 35; +pub const EMULTIHOP: errno_t = 36; +pub const ENAMETOOLONG: errno_t = 37; +pub const ENETDOWN: errno_t = 38; +pub const ENETRESET: errno_t = 39; +pub const ENETUNREACH: errno_t = 40; +pub const ENFILE: errno_t = 41; +pub const ENOBUFS: errno_t = 42; +pub const ENODEV: errno_t = 43; +pub const ENOENT: errno_t = 44; +pub const ENOEXEC: errno_t = 45; +pub const ENOLCK: errno_t = 46; +pub const ENOLINK: errno_t = 47; +pub const ENOMEM: errno_t = 48; +pub const ENOMSG: errno_t = 49; +pub const ENOPROTOOPT: errno_t = 50; +pub const ENOSPC: errno_t = 51; +pub const ENOSYS: errno_t = 52; +pub const ENOTCONN: errno_t = 53; +pub const ENOTDIR: errno_t = 54; +pub const ENOTEMPTY: errno_t = 55; +pub const ENOTRECOVERABLE: errno_t = 56; +pub const ENOTSOCK: errno_t = 57; +pub const ENOTSUP: errno_t = 58; +pub const ENOTTY: errno_t = 59; +pub const ENXIO: errno_t = 60; +pub const EOVERFLOW: errno_t = 61; +pub const EOWNERDEAD: errno_t = 62; +pub const EPERM: errno_t = 63; +pub const EPIPE: errno_t = 64; +pub const EPROTO: errno_t = 65; +pub const EPROTONOSUPPORT: errno_t = 66; +pub const EPROTOTYPE: errno_t = 67; +pub const ERANGE: errno_t = 68; +pub const EROFS: errno_t = 69; +pub const ESPIPE: errno_t = 70; +pub const ESRCH: errno_t = 71; +pub const ESTALE: errno_t = 72; +pub const ETIMEDOUT: errno_t = 73; +pub const ETXTBSY: errno_t = 74; +pub const EXDEV: errno_t = 75; +pub const ENOTCAPABLE: errno_t = 76; + +pub const event_t = extern struct { + userdata: userdata_t, + @"error": errno_t, + @"type": eventtype_t, + u: extern union { + fd_readwrite: extern struct { + nbytes: filesize_t, + flags: eventrwflags_t, + }, + }, +}; + +pub const eventrwflags_t = u16; +pub const EVENT_FD_READWRITE_HANGUP: eventrwflags_t = 0x0001; + +pub const eventtype_t = u8; +pub const EVENTTYPE_CLOCK: eventtype_t = 0; +pub const EVENTTYPE_FD_READ: eventtype_t = 1; +pub const EVENTTYPE_FD_WRITE: eventtype_t = 2; + +pub const exitcode_t = u32; + +pub const fd_t = u32; + +pub const fdflags_t = u16; +pub const FDFLAG_APPEND: fdflags_t = 0x0001; +pub const FDFLAG_DSYNC: fdflags_t = 0x0002; +pub const FDFLAG_NONBLOCK: fdflags_t = 0x0004; +pub const FDFLAG_RSYNC: fdflags_t = 0x0008; +pub const FDFLAG_SYNC: fdflags_t = 0x0010; + +const fdstat_t = extern struct { + fs_filetype: filetype_t, + fs_flags: fdflags_t, + fs_rights_base: rights_t, + fs_rights_inheriting: rights_t, +}; + +pub const filedelta_t = i64; + +pub const filesize_t = u64; + +pub const filestat_t = extern struct { + st_dev: device_t, + st_ino: inode_t, + st_filetype: filetype_t, + st_nlink: linkcount_t, + st_size: filesize_t, + st_atim: timestamp_t, + st_mtim: timestamp_t, + st_ctim: timestamp_t, +}; + +pub const filetype_t = u8; +pub const FILETYPE_UNKNOWN: filetype_t = 0; +pub const FILETYPE_BLOCK_DEVICE: filetype_t = 1; +pub const FILETYPE_CHARACTER_DEVICE: filetype_t = 2; +pub const FILETYPE_DIRECTORY: filetype_t = 3; +pub const FILETYPE_REGULAR_FILE: filetype_t = 4; +pub const FILETYPE_SOCKET_DGRAM: filetype_t = 5; +pub const FILETYPE_SOCKET_STREAM: filetype_t = 6; +pub const FILETYPE_SYMBOLIC_LINK: filetype_t = 7; + +pub const fstflags_t = u16; +pub const FILESTAT_SET_ATIM: fstflags_t = 0x0001; +pub const FILESTAT_SET_ATIM_NOW: fstflags_t = 0x0002; +pub const FILESTAT_SET_MTIM: fstflags_t = 0x0004; +pub const FILESTAT_SET_MTIM_NOW: fstflags_t = 0x0008; + +pub const inode_t = u64; + +pub const iovec_t = extern struct { + buf: [*]u8, + buf_len: usize, +}; + +pub const linkcount_t = u32; + +pub const lookupflags_t = u32; +pub const LOOKUP_SYMLINK_FOLLOW: lookupflags_t = 0x00000001; + +pub const oflags_t = u16; +pub const O_CREAT: oflags_t = 0x0001; +pub const O_DIRECTORY: oflags_t = 0x0002; +pub const O_EXCL: oflags_t = 0x0004; +pub const O_TRUNC: oflags_t = 0x0008; + +pub const preopentype_t = u8; +pub const PREOPENTYPE_DIR: preopentype_t = 0; + +pub const prestat_t = extern struct { + pr_type: preopentype_t, + u: extern union { + dir: extern struct { + pr_name_len: usize, + }, + }, +}; + +pub const riflags_t = u16; +pub const SOCK_RECV_PEEK: riflags_t = 0x0001; +pub const SOCK_RECV_WAITALL: riflags_t = 0x0002; + +pub const rights_t = u64; +pub const RIGHT_FD_DATASYNC: rights_t = 0x0000000000000001; +pub const RIGHT_FD_READ: rights_t = 0x0000000000000002; +pub const RIGHT_FD_SEEK: rights_t = 0x0000000000000004; +pub const RIGHT_FD_FDSTAT_SET_FLAGS: rights_t = 0x0000000000000008; +pub const RIGHT_FD_SYNC: rights_t = 0x0000000000000010; +pub const RIGHT_FD_TELL: rights_t = 0x0000000000000020; +pub const RIGHT_FD_WRITE: rights_t = 0x0000000000000040; +pub const RIGHT_FD_ADVISE: rights_t = 0x0000000000000080; +pub const RIGHT_FD_ALLOCATE: rights_t = 0x0000000000000100; +pub const RIGHT_PATH_CREATE_DIRECTORY: rights_t = 0x0000000000000200; +pub const RIGHT_PATH_CREATE_FILE: rights_t = 0x0000000000000400; +pub const RIGHT_PATH_LINK_SOURCE: rights_t = 0x0000000000000800; +pub const RIGHT_PATH_LINK_TARGET: rights_t = 0x0000000000001000; +pub const RIGHT_PATH_OPEN: rights_t = 0x0000000000002000; +pub const RIGHT_FD_READDIR: rights_t = 0x0000000000004000; +pub const RIGHT_PATH_READLINK: rights_t = 0x0000000000008000; +pub const RIGHT_PATH_RENAME_SOURCE: rights_t = 0x0000000000010000; +pub const RIGHT_PATH_RENAME_TARGET: rights_t = 0x0000000000020000; +pub const RIGHT_PATH_FILESTAT_GET: rights_t = 0x0000000000040000; +pub const RIGHT_PATH_FILESTAT_SET_SIZE: rights_t = 0x0000000000080000; +pub const RIGHT_PATH_FILESTAT_SET_TIMES: rights_t = 0x0000000000100000; +pub const RIGHT_FD_FILESTAT_GET: rights_t = 0x0000000000200000; +pub const RIGHT_FD_FILESTAT_SET_SIZE: rights_t = 0x0000000000400000; +pub const RIGHT_FD_FILESTAT_SET_TIMES: rights_t = 0x0000000000800000; +pub const RIGHT_PATH_SYMLINK: rights_t = 0x0000000001000000; +pub const RIGHT_PATH_REMOVE_DIRECTORY: rights_t = 0x0000000002000000; +pub const RIGHT_PATH_UNLINK_FILE: rights_t = 0x0000000004000000; +pub const RIGHT_POLL_FD_READWRITE: rights_t = 0x0000000008000000; +pub const RIGHT_SOCK_SHUTDOWN: rights_t = 0x0000000010000000; + +pub const roflags_t = u16; +pub const SOCK_RECV_DATA_TRUNCATED: roflags_t = 0x0001; + +pub const sdflags_t = u8; +pub const SHUT_RD: sdflags_t = 0x01; +pub const SHUT_WR: sdflags_t = 0x02; + +pub const siflags_t = u16; + +pub const signal_t = u8; +pub const SIGHUP: signal_t = 1; +pub const SIGINT: signal_t = 2; +pub const SIGQUIT: signal_t = 3; +pub const SIGILL: signal_t = 4; +pub const SIGTRAP: signal_t = 5; +pub const SIGABRT: signal_t = 6; +pub const SIGBUS: signal_t = 7; +pub const SIGFPE: signal_t = 8; +pub const SIGKILL: signal_t = 9; +pub const SIGUSR1: signal_t = 10; +pub const SIGSEGV: signal_t = 11; +pub const SIGUSR2: signal_t = 12; +pub const SIGPIPE: signal_t = 13; +pub const SIGALRM: signal_t = 14; +pub const SIGTERM: signal_t = 15; +pub const SIGCHLD: signal_t = 16; +pub const SIGCONT: signal_t = 17; +pub const SIGSTOP: signal_t = 18; +pub const SIGTSTP: signal_t = 19; +pub const SIGTTIN: signal_t = 20; +pub const SIGTTOU: signal_t = 21; +pub const SIGURG: signal_t = 22; +pub const SIGXCPU: signal_t = 23; +pub const SIGXFSZ: signal_t = 24; +pub const SIGVTALRM: signal_t = 25; +pub const SIGPROF: signal_t = 26; +pub const SIGWINCH: signal_t = 27; +pub const SIGPOLL: signal_t = 28; +pub const SIGPWR: signal_t = 29; +pub const SIGSYS: signal_t = 30; + +pub const subclockflags_t = u16; +pub const SUBSCRIPTION_CLOCK_ABSTIME: subclockflags_t = 0x0001; + +pub const subscription_t = extern struct { + userdata: userdata_t, + @"type": eventtype_t, + u: extern union { + clock: extern struct { + identifier: userdata_t, + clock_id: clockid_t, + timeout: timestamp_t, + precision: timestamp_t, + flags: subclockflags_t, + }, + fd_readwrite: extern struct { + fd: fd_t, + }, + }, +}; + +pub const timestamp_t = u64; + +pub const userdata_t = u64; + +pub const whence_t = u8; +pub const WHENCE_CUR: whence_t = 0; +pub const WHENCE_END: whence_t = 1; +pub const WHENCE_SET: whence_t = 2; + +pub extern "wasi_unstable" fn args_get(argv: [*][*]u8, argv_buf: [*]u8) errno_t; +pub extern "wasi_unstable" fn args_sizes_get(argc: *usize, argv_buf_size: *usize) errno_t; + +pub extern "wasi_unstable" fn clock_res_get(clock_id: clockid_t, resolution: *timestamp_t) errno_t; +pub extern "wasi_unstable" fn clock_time_get(clock_id: clockid_t, precision: timestamp_t, timestamp: *timestamp_t) errno_t; + +pub extern "wasi_unstable" fn environ_get(environ: [*]?[*]u8, environ_buf: [*]u8) errno_t; +pub extern "wasi_unstable" fn environ_sizes_get(environ_count: *usize, environ_buf_size: *usize) errno_t; + +pub extern "wasi_unstable" fn fd_advise(fd: fd_t, offset: filesize_t, len: filesize_t, advice: advice_t) errno_t; +pub extern "wasi_unstable" fn fd_allocate(fd: fd_t, offset: filesize_t, len: filesize_t) errno_t; +pub extern "wasi_unstable" fn fd_close(fd: fd_t) errno_t; +pub extern "wasi_unstable" fn fd_datasync(fd: fd_t) errno_t; +pub extern "wasi_unstable" fn fd_pread(fd: fd_t, iovs: [*]const iovec_t, iovs_len: usize, offset: filesize_t, nread: *usize) errno_t; +pub extern "wasi_unstable" fn fd_pwrite(fd: fd_t, iovs: [*]const ciovec_t, iovs_len: usize, offset: filesize_t, nwritten: *usize) errno_t; +pub extern "wasi_unstable" fn fd_read(fd: fd_t, iovs: [*]const iovec_t, iovs_len: usize, nread: *usize) errno_t; +pub extern "wasi_unstable" fn fd_readdir(fd: fd_t, buf: [*]u8, buf_len: usize, cookie: dircookie_t, bufused: *usize) errno_t; +pub extern "wasi_unstable" fn fd_renumber(from: fd_t, to: fd_t) errno_t; +pub extern "wasi_unstable" fn fd_seek(fd: fd_t, offset: filedelta_t, whence: whence_t, newoffset: *filesize_t) errno_t; +pub extern "wasi_unstable" fn fd_sync(fd: fd_t) errno_t; +pub extern "wasi_unstable" fn fd_tell(fd: fd_t, newoffset: *filesize_t) errno_t; +pub extern "wasi_unstable" fn fd_write(fd: fd_t, iovs: [*]const ciovec_t, iovs_len: usize, nwritten: *usize) errno_t; + +pub extern "wasi_unstable" fn fd_fdstat_get(fd: fd_t, buf: *fdstat_t) errno_t; +pub extern "wasi_unstable" fn fd_fdstat_set_flags(fd: fd_t, flags: fdflags_t) errno_t; +pub extern "wasi_unstable" fn fd_fdstat_set_rights(fd: fd_t, fs_rights_base: rights_t, fs_rights_inheriting: rights_t) errno_t; + +pub extern "wasi_unstable" fn fd_filestat_get(fd: fd_t, buf: *filestat_t) errno_t; +pub extern "wasi_unstable" fn fd_filestat_set_size(fd: fd_t, st_size: filesize_t) errno_t; +pub extern "wasi_unstable" fn fd_filestat_set_times(fd: fd_t, st_atim: timestamp_t, st_mtim: timestamp_t, fstflags: fstflags_t) errno_t; + +pub extern "wasi_unstable" fn fd_prestat_get(fd: fd_t, buf: *prestat_t) errno_t; +pub extern "wasi_unstable" fn fd_prestat_dir_name(fd: fd_t, path: [*]u8, path_len: usize) errno_t; + +pub extern "wasi_unstable" fn path_create_directory(fd: fd_t, path: [*]const u8, path_len: usize) errno_t; +pub extern "wasi_unstable" fn path_filestat_get(fd: fd_t, flags: lookupflags_t, path: [*]const u8, path_len: usize, buf: *filestat_t) errno_t; +pub extern "wasi_unstable" fn path_filestat_set_times(fd: fd_t, flags: lookupflags_t, path: [*]const u8, path_len: usize, st_atim: timestamp_t, st_mtim: timestamp_t, fstflags: fstflags_t) errno_t; +pub extern "wasi_unstable" fn path_link(old_fd: fd_t, old_flags: lookupflags_t, old_path: [*]const u8, old_path_len: usize, new_fd: fd_t, new_path: [*]const u8, new_path_len: usize) errno_t; +pub extern "wasi_unstable" fn path_open(dirfd: fd_t, dirflags: lookupflags_t, path: [*]const u8, path_len: usize, oflags: oflags_t, fs_rights_base: rights_t, fs_rights_inheriting: rights_t, fs_flags: fdflags_t, fd: *fd_t) errno_t; +pub extern "wasi_unstable" fn path_readlink(fd: fd_t, path: [*]const u8, path_len: usize, buf: [*]u8, buf_len: usize, bufused: *usize) errno_t; +pub extern "wasi_unstable" fn path_remove_directory(fd: fd_t, path: [*]const u8, path_len: usize) errno_t; +pub extern "wasi_unstable" fn path_rename(old_fd: fd_t, old_path: [*]const u8, old_path_len: usize, new_fd: fd_t, new_path: [*]const u8, new_path_len: usize) errno_t; +pub extern "wasi_unstable" fn path_symlink(old_path: [*]const u8, old_path_len: usize, fd: fd_t, new_path: [*]const u8, new_path_len: usize) errno_t; +pub extern "wasi_unstable" fn path_unlink_file(fd: fd_t, path: [*]const u8, path_len: usize) errno_t; + +pub extern "wasi_unstable" fn poll_oneoff(in: *const subscription_t, out: *event_t, nsubscriptions: usize, nevents: *usize) errno_t; + +pub extern "wasi_unstable" fn proc_exit(rval: exitcode_t) noreturn; +pub extern "wasi_unstable" fn proc_raise(sig: signal_t) errno_t; + +pub extern "wasi_unstable" fn random_get(buf: [*]u8, buf_len: usize) errno_t; + +pub extern "wasi_unstable" fn sched_yield() errno_t; + +pub extern "wasi_unstable" fn sock_recv(sock: fd_t, ri_data: *const iovec_t, ri_data_len: usize, ri_flags: riflags_t, ro_datalen: *usize, ro_flags: *roflags_t) errno_t; +pub extern "wasi_unstable" fn sock_send(sock: fd_t, si_data: *const ciovec_t, si_data_len: usize, si_flags: siflags_t, so_datalen: *usize) errno_t; +pub extern "wasi_unstable" fn sock_shutdown(sock: fd_t, how: sdflags_t) errno_t; diff --git a/std/os/wasi/core.zig b/std/os/wasi/core.zig deleted file mode 100644 index f2bef73be9ba..000000000000 --- a/std/os/wasi/core.zig +++ /dev/null @@ -1,374 +0,0 @@ -// Based on https://github.com/CraneStation/wasi-sysroot/blob/wasi/libc-bottom-half/headers/public/wasi/core.h -// and https://github.com/WebAssembly/WASI/blob/master/design/WASI-core.md - -pub const advice_t = u8; -pub const ADVICE_NORMAL: advice_t = 0; -pub const ADVICE_SEQUENTIAL: advice_t = 1; -pub const ADVICE_RANDOM: advice_t = 2; -pub const ADVICE_WILLNEED: advice_t = 3; -pub const ADVICE_DONTNEED: advice_t = 4; -pub const ADVICE_NOREUSE: advice_t = 5; - -pub const ciovec_t = extern struct { - buf: [*]const u8, - buf_len: usize, -}; - -pub const clockid_t = u32; -pub const CLOCK_REALTIME: clockid_t = 0; -pub const CLOCK_MONOTONIC: clockid_t = 1; -pub const CLOCK_PROCESS_CPUTIME_ID: clockid_t = 2; -pub const CLOCK_THREAD_CPUTIME_ID: clockid_t = 3; - -pub const device_t = u64; - -pub const dircookie_t = u64; -pub const DIRCOOKIE_START: dircookie_t = 0; - -pub const dirent_t = extern struct { - d_next: dircookie_t, - d_ino: inode_t, - d_namlen: u32, - d_type: filetype_t, -}; - -pub const errno_t = u16; -pub const ESUCCESS: errno_t = 0; -pub const E2BIG: errno_t = 1; -pub const EACCES: errno_t = 2; -pub const EADDRINUSE: errno_t = 3; -pub const EADDRNOTAVAIL: errno_t = 4; -pub const EAFNOSUPPORT: errno_t = 5; -pub const EAGAIN: errno_t = 6; -pub const EALREADY: errno_t = 7; -pub const EBADF: errno_t = 8; -pub const EBADMSG: errno_t = 9; -pub const EBUSY: errno_t = 10; -pub const ECANCELED: errno_t = 11; -pub const ECHILD: errno_t = 12; -pub const ECONNABORTED: errno_t = 13; -pub const ECONNREFUSED: errno_t = 14; -pub const ECONNRESET: errno_t = 15; -pub const EDEADLK: errno_t = 16; -pub const EDESTADDRREQ: errno_t = 17; -pub const EDOM: errno_t = 18; -pub const EDQUOT: errno_t = 19; -pub const EEXIST: errno_t = 20; -pub const EFAULT: errno_t = 21; -pub const EFBIG: errno_t = 22; -pub const EHOSTUNREACH: errno_t = 23; -pub const EIDRM: errno_t = 24; -pub const EILSEQ: errno_t = 25; -pub const EINPROGRESS: errno_t = 26; -pub const EINTR: errno_t = 27; -pub const EINVAL: errno_t = 28; -pub const EIO: errno_t = 29; -pub const EISCONN: errno_t = 30; -pub const EISDIR: errno_t = 31; -pub const ELOOP: errno_t = 32; -pub const EMFILE: errno_t = 33; -pub const EMLINK: errno_t = 34; -pub const EMSGSIZE: errno_t = 35; -pub const EMULTIHOP: errno_t = 36; -pub const ENAMETOOLONG: errno_t = 37; -pub const ENETDOWN: errno_t = 38; -pub const ENETRESET: errno_t = 39; -pub const ENETUNREACH: errno_t = 40; -pub const ENFILE: errno_t = 41; -pub const ENOBUFS: errno_t = 42; -pub const ENODEV: errno_t = 43; -pub const ENOENT: errno_t = 44; -pub const ENOEXEC: errno_t = 45; -pub const ENOLCK: errno_t = 46; -pub const ENOLINK: errno_t = 47; -pub const ENOMEM: errno_t = 48; -pub const ENOMSG: errno_t = 49; -pub const ENOPROTOOPT: errno_t = 50; -pub const ENOSPC: errno_t = 51; -pub const ENOSYS: errno_t = 52; -pub const ENOTCONN: errno_t = 53; -pub const ENOTDIR: errno_t = 54; -pub const ENOTEMPTY: errno_t = 55; -pub const ENOTRECOVERABLE: errno_t = 56; -pub const ENOTSOCK: errno_t = 57; -pub const ENOTSUP: errno_t = 58; -pub const ENOTTY: errno_t = 59; -pub const ENXIO: errno_t = 60; -pub const EOVERFLOW: errno_t = 61; -pub const EOWNERDEAD: errno_t = 62; -pub const EPERM: errno_t = 63; -pub const EPIPE: errno_t = 64; -pub const EPROTO: errno_t = 65; -pub const EPROTONOSUPPORT: errno_t = 66; -pub const EPROTOTYPE: errno_t = 67; -pub const ERANGE: errno_t = 68; -pub const EROFS: errno_t = 69; -pub const ESPIPE: errno_t = 70; -pub const ESRCH: errno_t = 71; -pub const ESTALE: errno_t = 72; -pub const ETIMEDOUT: errno_t = 73; -pub const ETXTBSY: errno_t = 74; -pub const EXDEV: errno_t = 75; -pub const ENOTCAPABLE: errno_t = 76; - -pub const event_t = extern struct { - userdata: userdata_t, - @"error": errno_t, - @"type": eventtype_t, - u: extern union { - fd_readwrite: extern struct { - nbytes: filesize_t, - flags: eventrwflags_t, - }, - }, -}; - -pub const eventrwflags_t = u16; -pub const EVENT_FD_READWRITE_HANGUP: eventrwflags_t = 0x0001; - -pub const eventtype_t = u8; -pub const EVENTTYPE_CLOCK: eventtype_t = 0; -pub const EVENTTYPE_FD_READ: eventtype_t = 1; -pub const EVENTTYPE_FD_WRITE: eventtype_t = 2; - -pub const exitcode_t = u32; - -pub const fd_t = u32; - -pub const fdflags_t = u16; -pub const FDFLAG_APPEND: fdflags_t = 0x0001; -pub const FDFLAG_DSYNC: fdflags_t = 0x0002; -pub const FDFLAG_NONBLOCK: fdflags_t = 0x0004; -pub const FDFLAG_RSYNC: fdflags_t = 0x0008; -pub const FDFLAG_SYNC: fdflags_t = 0x0010; - -const fdstat_t = extern struct { - fs_filetype: filetype_t, - fs_flags: fdflags_t, - fs_rights_base: rights_t, - fs_rights_inheriting: rights_t, -}; - -pub const filedelta_t = i64; - -pub const filesize_t = u64; - -pub const filestat_t = extern struct { - st_dev: device_t, - st_ino: inode_t, - st_filetype: filetype_t, - st_nlink: linkcount_t, - st_size: filesize_t, - st_atim: timestamp_t, - st_mtim: timestamp_t, - st_ctim: timestamp_t, -}; - -pub const filetype_t = u8; -pub const FILETYPE_UNKNOWN: filetype_t = 0; -pub const FILETYPE_BLOCK_DEVICE: filetype_t = 1; -pub const FILETYPE_CHARACTER_DEVICE: filetype_t = 2; -pub const FILETYPE_DIRECTORY: filetype_t = 3; -pub const FILETYPE_REGULAR_FILE: filetype_t = 4; -pub const FILETYPE_SOCKET_DGRAM: filetype_t = 5; -pub const FILETYPE_SOCKET_STREAM: filetype_t = 6; -pub const FILETYPE_SYMBOLIC_LINK: filetype_t = 7; - -pub const fstflags_t = u16; -pub const FILESTAT_SET_ATIM: fstflags_t = 0x0001; -pub const FILESTAT_SET_ATIM_NOW: fstflags_t = 0x0002; -pub const FILESTAT_SET_MTIM: fstflags_t = 0x0004; -pub const FILESTAT_SET_MTIM_NOW: fstflags_t = 0x0008; - -pub const inode_t = u64; - -pub const iovec_t = extern struct { - buf: [*]u8, - buf_len: usize, -}; - -pub const linkcount_t = u32; - -pub const lookupflags_t = u32; -pub const LOOKUP_SYMLINK_FOLLOW: lookupflags_t = 0x00000001; - -pub const oflags_t = u16; -pub const O_CREAT: oflags_t = 0x0001; -pub const O_DIRECTORY: oflags_t = 0x0002; -pub const O_EXCL: oflags_t = 0x0004; -pub const O_TRUNC: oflags_t = 0x0008; - -pub const preopentype_t = u8; -pub const PREOPENTYPE_DIR: preopentype_t = 0; - -pub const prestat_t = extern struct { - pr_type: preopentype_t, - u: extern union { - dir: extern struct { - pr_name_len: usize, - }, - }, -}; - -pub const riflags_t = u16; -pub const SOCK_RECV_PEEK: riflags_t = 0x0001; -pub const SOCK_RECV_WAITALL: riflags_t = 0x0002; - -pub const rights_t = u64; -pub const RIGHT_FD_DATASYNC: rights_t = 0x0000000000000001; -pub const RIGHT_FD_READ: rights_t = 0x0000000000000002; -pub const RIGHT_FD_SEEK: rights_t = 0x0000000000000004; -pub const RIGHT_FD_FDSTAT_SET_FLAGS: rights_t = 0x0000000000000008; -pub const RIGHT_FD_SYNC: rights_t = 0x0000000000000010; -pub const RIGHT_FD_TELL: rights_t = 0x0000000000000020; -pub const RIGHT_FD_WRITE: rights_t = 0x0000000000000040; -pub const RIGHT_FD_ADVISE: rights_t = 0x0000000000000080; -pub const RIGHT_FD_ALLOCATE: rights_t = 0x0000000000000100; -pub const RIGHT_PATH_CREATE_DIRECTORY: rights_t = 0x0000000000000200; -pub const RIGHT_PATH_CREATE_FILE: rights_t = 0x0000000000000400; -pub const RIGHT_PATH_LINK_SOURCE: rights_t = 0x0000000000000800; -pub const RIGHT_PATH_LINK_TARGET: rights_t = 0x0000000000001000; -pub const RIGHT_PATH_OPEN: rights_t = 0x0000000000002000; -pub const RIGHT_FD_READDIR: rights_t = 0x0000000000004000; -pub const RIGHT_PATH_READLINK: rights_t = 0x0000000000008000; -pub const RIGHT_PATH_RENAME_SOURCE: rights_t = 0x0000000000010000; -pub const RIGHT_PATH_RENAME_TARGET: rights_t = 0x0000000000020000; -pub const RIGHT_PATH_FILESTAT_GET: rights_t = 0x0000000000040000; -pub const RIGHT_PATH_FILESTAT_SET_SIZE: rights_t = 0x0000000000080000; -pub const RIGHT_PATH_FILESTAT_SET_TIMES: rights_t = 0x0000000000100000; -pub const RIGHT_FD_FILESTAT_GET: rights_t = 0x0000000000200000; -pub const RIGHT_FD_FILESTAT_SET_SIZE: rights_t = 0x0000000000400000; -pub const RIGHT_FD_FILESTAT_SET_TIMES: rights_t = 0x0000000000800000; -pub const RIGHT_PATH_SYMLINK: rights_t = 0x0000000001000000; -pub const RIGHT_PATH_REMOVE_DIRECTORY: rights_t = 0x0000000002000000; -pub const RIGHT_PATH_UNLINK_FILE: rights_t = 0x0000000004000000; -pub const RIGHT_POLL_FD_READWRITE: rights_t = 0x0000000008000000; -pub const RIGHT_SOCK_SHUTDOWN: rights_t = 0x0000000010000000; - -pub const roflags_t = u16; -pub const SOCK_RECV_DATA_TRUNCATED: roflags_t = 0x0001; - -pub const sdflags_t = u8; -pub const SHUT_RD: sdflags_t = 0x01; -pub const SHUT_WR: sdflags_t = 0x02; - -pub const siflags_t = u16; - -pub const signal_t = u8; -pub const SIGHUP: signal_t = 1; -pub const SIGINT: signal_t = 2; -pub const SIGQUIT: signal_t = 3; -pub const SIGILL: signal_t = 4; -pub const SIGTRAP: signal_t = 5; -pub const SIGABRT: signal_t = 6; -pub const SIGBUS: signal_t = 7; -pub const SIGFPE: signal_t = 8; -pub const SIGKILL: signal_t = 9; -pub const SIGUSR1: signal_t = 10; -pub const SIGSEGV: signal_t = 11; -pub const SIGUSR2: signal_t = 12; -pub const SIGPIPE: signal_t = 13; -pub const SIGALRM: signal_t = 14; -pub const SIGTERM: signal_t = 15; -pub const SIGCHLD: signal_t = 16; -pub const SIGCONT: signal_t = 17; -pub const SIGSTOP: signal_t = 18; -pub const SIGTSTP: signal_t = 19; -pub const SIGTTIN: signal_t = 20; -pub const SIGTTOU: signal_t = 21; -pub const SIGURG: signal_t = 22; -pub const SIGXCPU: signal_t = 23; -pub const SIGXFSZ: signal_t = 24; -pub const SIGVTALRM: signal_t = 25; -pub const SIGPROF: signal_t = 26; -pub const SIGWINCH: signal_t = 27; -pub const SIGPOLL: signal_t = 28; -pub const SIGPWR: signal_t = 29; -pub const SIGSYS: signal_t = 30; - -pub const subclockflags_t = u16; -pub const SUBSCRIPTION_CLOCK_ABSTIME: subclockflags_t = 0x0001; - -pub const subscription_t = extern struct { - userdata: userdata_t, - @"type": eventtype_t, - u: extern union { - clock: extern struct { - identifier: userdata_t, - clock_id: clockid_t, - timeout: timestamp_t, - precision: timestamp_t, - flags: subclockflags_t, - }, - fd_readwrite: extern struct { - fd: fd_t, - }, - }, -}; - -pub const timestamp_t = u64; - -pub const userdata_t = u64; - -pub const whence_t = u8; -pub const WHENCE_CUR: whence_t = 0; -pub const WHENCE_END: whence_t = 1; -pub const WHENCE_SET: whence_t = 2; - -pub extern "wasi_unstable" fn args_get(argv: [*][*]u8, argv_buf: [*]u8) errno_t; -pub extern "wasi_unstable" fn args_sizes_get(argc: *usize, argv_buf_size: *usize) errno_t; - -pub extern "wasi_unstable" fn clock_res_get(clock_id: clockid_t, resolution: *timestamp_t) errno_t; -pub extern "wasi_unstable" fn clock_time_get(clock_id: clockid_t, precision: timestamp_t, timestamp: *timestamp_t) errno_t; - -pub extern "wasi_unstable" fn environ_get(environ: [*]?[*]u8, environ_buf: [*]u8) errno_t; -pub extern "wasi_unstable" fn environ_sizes_get(environ_count: *usize, environ_buf_size: *usize) errno_t; - -pub extern "wasi_unstable" fn fd_advise(fd: fd_t, offset: filesize_t, len: filesize_t, advice: advice_t) errno_t; -pub extern "wasi_unstable" fn fd_allocate(fd: fd_t, offset: filesize_t, len: filesize_t) errno_t; -pub extern "wasi_unstable" fn fd_close(fd: fd_t) errno_t; -pub extern "wasi_unstable" fn fd_datasync(fd: fd_t) errno_t; -pub extern "wasi_unstable" fn fd_pread(fd: fd_t, iovs: *const iovec_t, iovs_len: usize, offset: filesize_t, nread: *usize) errno_t; -pub extern "wasi_unstable" fn fd_pwrite(fd: fd_t, iovs: *const ciovec_t, iovs_len: usize, offset: filesize_t, nwritten: *usize) errno_t; -pub extern "wasi_unstable" fn fd_read(fd: fd_t, iovs: *const iovec_t, iovs_len: usize, nread: *usize) errno_t; -pub extern "wasi_unstable" fn fd_readdir(fd: fd_t, buf: [*]u8, buf_len: usize, cookie: dircookie_t, bufused: *usize) errno_t; -pub extern "wasi_unstable" fn fd_renumber(from: fd_t, to: fd_t) errno_t; -pub extern "wasi_unstable" fn fd_seek(fd: fd_t, offset: filedelta_t, whence: whence_t, newoffset: *filesize_t) errno_t; -pub extern "wasi_unstable" fn fd_sync(fd: fd_t) errno_t; -pub extern "wasi_unstable" fn fd_tell(fd: fd_t, newoffset: *filesize_t) errno_t; -pub extern "wasi_unstable" fn fd_write(fd: fd_t, iovs: *const ciovec_t, iovs_len: usize, nwritten: *usize) errno_t; - -pub extern "wasi_unstable" fn fd_fdstat_get(fd: fd_t, buf: *fdstat_t) errno_t; -pub extern "wasi_unstable" fn fd_fdstat_set_flags(fd: fd_t, flags: fdflags_t) errno_t; -pub extern "wasi_unstable" fn fd_fdstat_set_rights(fd: fd_t, fs_rights_base: rights_t, fs_rights_inheriting: rights_t) errno_t; - -pub extern "wasi_unstable" fn fd_filestat_get(fd: fd_t, buf: *filestat_t) errno_t; -pub extern "wasi_unstable" fn fd_filestat_set_size(fd: fd_t, st_size: filesize_t) errno_t; -pub extern "wasi_unstable" fn fd_filestat_set_times(fd: fd_t, st_atim: timestamp_t, st_mtim: timestamp_t, fstflags: fstflags_t) errno_t; - -pub extern "wasi_unstable" fn fd_prestat_get(fd: fd_t, buf: *prestat_t) errno_t; -pub extern "wasi_unstable" fn fd_prestat_dir_name(fd: fd_t, path: [*]u8, path_len: usize) errno_t; - -pub extern "wasi_unstable" fn path_create_directory(fd: fd_t, path: [*]const u8, path_len: usize) errno_t; -pub extern "wasi_unstable" fn path_filestat_get(fd: fd_t, flags: lookupflags_t, path: [*]const u8, path_len: usize, buf: *filestat_t) errno_t; -pub extern "wasi_unstable" fn path_filestat_set_times(fd: fd_t, flags: lookupflags_t, path: [*]const u8, path_len: usize, st_atim: timestamp_t, st_mtim: timestamp_t, fstflags: fstflags_t) errno_t; -pub extern "wasi_unstable" fn path_link(old_fd: fd_t, old_flags: lookupflags_t, old_path: [*]const u8, old_path_len: usize, new_fd: fd_t, new_path: [*]const u8, new_path_len: usize) errno_t; -pub extern "wasi_unstable" fn path_open(dirfd: fd_t, dirflags: lookupflags_t, path: [*]const u8, path_len: usize, oflags: oflags_t, fs_rights_base: rights_t, fs_rights_inheriting: rights_t, fs_flags: fdflags_t, fd: *fd_t) errno_t; -pub extern "wasi_unstable" fn path_readlink(fd: fd_t, path: [*]const u8, path_len: usize, buf: [*]u8, buf_len: usize, bufused: *usize) errno_t; -pub extern "wasi_unstable" fn path_remove_directory(fd: fd_t, path: [*]const u8, path_len: usize) errno_t; -pub extern "wasi_unstable" fn path_rename(old_fd: fd_t, old_path: [*]const u8, old_path_len: usize, new_fd: fd_t, new_path: [*]const u8, new_path_len: usize) errno_t; -pub extern "wasi_unstable" fn path_symlink(old_path: [*]const u8, old_path_len: usize, fd: fd_t, new_path: [*]const u8, new_path_len: usize) errno_t; -pub extern "wasi_unstable" fn path_unlink_file(fd: fd_t, path: [*]const u8, path_len: usize) errno_t; - -pub extern "wasi_unstable" fn poll_oneoff(in: *const subscription_t, out: *event_t, nsubscriptions: usize, nevents: *usize) errno_t; - -pub extern "wasi_unstable" fn proc_exit(rval: exitcode_t) noreturn; -pub extern "wasi_unstable" fn proc_raise(sig: signal_t) errno_t; - -pub extern "wasi_unstable" fn random_get(buf: [*]u8, buf_len: usize) errno_t; - -pub extern "wasi_unstable" fn sched_yield() errno_t; - -pub extern "wasi_unstable" fn sock_recv(sock: fd_t, ri_data: *const iovec_t, ri_data_len: usize, ri_flags: riflags_t, ro_datalen: *usize, ro_flags: *roflags_t) errno_t; -pub extern "wasi_unstable" fn sock_send(sock: fd_t, si_data: *const ciovec_t, si_data_len: usize, si_flags: siflags_t, so_datalen: *usize) errno_t; -pub extern "wasi_unstable" fn sock_shutdown(sock: fd_t, how: sdflags_t) errno_t; diff --git a/std/os/windows.zig b/std/os/windows.zig index e134d87eae38..3a31c497cc76 100644 --- a/std/os/windows.zig +++ b/std/os/windows.zig @@ -2,6 +2,11 @@ const std = @import("../std.zig"); const assert = std.debug.assert; const maxInt = std.math.maxInt; +pub const is_the_target = switch (builtin.os) { + .windows => true, + else => false, +}; + pub use @import("windows/advapi32.zig"); pub use @import("windows/kernel32.zig"); pub use @import("windows/ntdll.zig"); @@ -9,10 +14,13 @@ pub use @import("windows/ole32.zig"); pub use @import("windows/shell32.zig"); test "import" { - _ = @import("windows/util.zig"); + if (is_the_target) { + _ = @import("windows/util.zig"); + } } pub const ERROR = @import("windows/error.zig"); +pub const errno_codes = @import("windows/errno.zig"); pub const SHORT = c_short; pub const BOOL = c_int; diff --git a/std/os/windows/errno.zig b/std/os/windows/errno.zig new file mode 100644 index 000000000000..0ccdf14feb8c --- /dev/null +++ b/std/os/windows/errno.zig @@ -0,0 +1 @@ +// TODO get these values from msvcrt diff --git a/std/os/windows/util.zig b/std/os/windows/util.zig index 72c84502e369..4361b2aa9191 100644 --- a/std/os/windows/util.zig +++ b/std/os/windows/util.zig @@ -8,12 +8,6 @@ const mem = std.mem; const BufMap = std.BufMap; const cstr = std.cstr; -// > The maximum path of 32,767 characters is approximate, because the "\\?\" -// > prefix may be expanded to a longer string by the system at run time, and -// > this expansion applies to the total length. -// 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 WaitError = error{ WaitAbandoned, WaitTimeOut, @@ -38,131 +32,6 @@ pub fn windowsWaitSingle(handle: windows.HANDLE, milliseconds: windows.DWORD) Wa }; } -pub fn windowsClose(handle: windows.HANDLE) void { - assert(windows.CloseHandle(handle) != 0); -} - -pub const ReadError = error{ - OperationAborted, - BrokenPipe, - Unexpected, -}; - -pub const WriteError = error{ - SystemResources, - OperationAborted, - BrokenPipe, - - /// See https://github.com/ziglang/zig/issues/1396 - Unexpected, -}; - -pub fn windowsWrite(handle: windows.HANDLE, bytes: []const u8) WriteError!void { - var bytes_written: windows.DWORD = undefined; - if (windows.WriteFile(handle, bytes.ptr, @intCast(u32, bytes.len), &bytes_written, null) == 0) { - const err = windows.GetLastError(); - return switch (err) { - windows.ERROR.INVALID_USER_BUFFER => WriteError.SystemResources, - windows.ERROR.NOT_ENOUGH_MEMORY => WriteError.SystemResources, - windows.ERROR.OPERATION_ABORTED => WriteError.OperationAborted, - windows.ERROR.NOT_ENOUGH_QUOTA => WriteError.SystemResources, - windows.ERROR.IO_PENDING => unreachable, - windows.ERROR.BROKEN_PIPE => WriteError.BrokenPipe, - else => os.unexpectedErrorWindows(err), - }; - } -} - -pub fn windowsIsTty(handle: windows.HANDLE) bool { - if (windowsIsCygwinPty(handle)) - return true; - - var out: windows.DWORD = undefined; - return windows.GetConsoleMode(handle, &out) != 0; -} - -pub fn windowsIsCygwinPty(handle: windows.HANDLE) bool { - 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 OpenError = error{ - SharingViolation, - PathAlreadyExists, - - /// When any of the path components can not be found or the file component can not - /// be found. Some operating systems distinguish between path components not found and - /// file components not found, but they are collapsed into FileNotFound to gain - /// consistency across operating systems. - FileNotFound, - - AccessDenied, - PipeBusy, - NameTooLong, - - /// On Windows, file paths must be valid Unicode. - InvalidUtf8, - - /// On Windows, file paths cannot contain these characters: - /// '/', '*', '?', '"', '<', '>', '|' - BadPathName, - - /// See https://github.com/ziglang/zig/issues/1396 - Unexpected, -}; - -pub fn windowsOpenW( - file_path_w: [*]const u16, - desired_access: windows.DWORD, - share_mode: windows.DWORD, - creation_disposition: windows.DWORD, - flags_and_attrs: windows.DWORD, -) OpenError!windows.HANDLE { - const result = windows.CreateFileW(file_path_w, desired_access, share_mode, null, creation_disposition, flags_and_attrs, null); - - if (result == windows.INVALID_HANDLE_VALUE) { - const err = windows.GetLastError(); - switch (err) { - windows.ERROR.SHARING_VIOLATION => return OpenError.SharingViolation, - windows.ERROR.ALREADY_EXISTS => return OpenError.PathAlreadyExists, - windows.ERROR.FILE_EXISTS => return OpenError.PathAlreadyExists, - windows.ERROR.FILE_NOT_FOUND => return OpenError.FileNotFound, - windows.ERROR.PATH_NOT_FOUND => return OpenError.FileNotFound, - windows.ERROR.ACCESS_DENIED => return OpenError.AccessDenied, - windows.ERROR.PIPE_BUSY => return OpenError.PipeBusy, - else => return os.unexpectedErrorWindows(err), - } - } - - return result; -} - -pub fn windowsOpen( - file_path: []const u8, - desired_access: windows.DWORD, - share_mode: windows.DWORD, - creation_disposition: windows.DWORD, - flags_and_attrs: windows.DWORD, -) OpenError!windows.HANDLE { - const file_path_w = try sliceToPrefixedFileW(file_path); - return windowsOpenW(&file_path_w, desired_access, share_mode, creation_disposition, flags_and_attrs); -} - /// Caller must free result. pub fn createWindowsEnvBlock(allocator: *mem.Allocator, env_map: *const BufMap) ![]u16 { // count bytes needed @@ -278,39 +147,3 @@ pub fn windowsGetQueuedCompletionStatus(completion_port: windows.HANDLE, bytes_t } return WindowsWaitResult.Normal; } - -pub fn cStrToPrefixedFileW(s: [*]const u8) ![PATH_MAX_WIDE + 1]u16 { - return sliceToPrefixedFileW(mem.toSliceConst(u8, s)); -} - -pub fn sliceToPrefixedFileW(s: []const u8) ![PATH_MAX_WIDE + 1]u16 { - return sliceToPrefixedSuffixedFileW(s, []u16{0}); -} - -pub fn sliceToPrefixedSuffixedFileW(s: []const u8, comptime suffix: []const u16) ![PATH_MAX_WIDE + suffix.len]u16 { - // TODO well defined copy elision - var result: [PATH_MAX_WIDE + suffix.len]u16 = undefined; - - // > File I/O functions in the Windows API convert "/" to "\" as part of - // > converting the name to an NT-style name, except when using the "\\?\" - // > prefix as detailed in the following sections. - // from https://docs.microsoft.com/en-us/windows/desktop/FileIO/naming-a-file#maximum-path-length-limitation - // Because we want the larger maximum path length for absolute paths, we - // disallow forward slashes in zig std lib file functions on Windows. - for (s) |byte| { - switch (byte) { - '/', '*', '?', '"', '<', '>', '|' => return error.BadPathName, - else => {}, - } - } - const start_index = if (mem.startsWith(u8, s, "\\\\") or !os.path.isAbsolute(s)) 0 else blk: { - const prefix = []u16{ '\\', '\\', '?', '\\' }; - mem.copy(u16, result[0..], prefix); - break :blk prefix.len; - }; - const end_index = start_index + try std.unicode.utf8ToUtf16Le(result[start_index..], s); - assert(end_index <= result.len); - if (end_index + suffix.len > result.len) return error.NameTooLong; - mem.copy(u16, result[end_index..], suffix); - return result; -} diff --git a/std/special/bootstrap.zig b/std/special/bootstrap.zig index b389c03c8c66..c6630a594c35 100644 --- a/std/special/bootstrap.zig +++ b/std/special/bootstrap.zig @@ -8,7 +8,10 @@ const assert = std.debug.assert; var argc_ptr: [*]usize = undefined; -const is_wasm = switch (builtin.arch) { .wasm32, .wasm64 => true, else => false}; +const is_wasm = switch (builtin.arch) { + .wasm32, .wasm64 => true, + else => false, +}; comptime { if (builtin.link_libc) { @@ -78,7 +81,7 @@ fn posixCallMainAndExit() noreturn { if (builtin.os == builtin.Os.linux) { // Find the beginning of the auxiliary vector const auxv = @ptrCast([*]std.elf.Auxv, envp.ptr + envp_count + 1); - std.os.linux_elf_aux_maybe = auxv; + std.os.linux.elf_aux_maybe = auxv; // Initialize the TLS area std.os.linux.tls.initTLS(); @@ -96,7 +99,7 @@ fn posixCallMainAndExit() noreturn { // and we want fewer call frames in stack traces. inline fn callMainWithArgs(argc: usize, argv: [*][*]u8, envp: [][*]u8) u8 { std.os.ArgIteratorPosix.raw = argv[0..argc]; - std.os.posix_environ_raw = envp; + std.os.posix.environ = envp; return callMain(); }