const std = #import("std"); const Allocator = std.Allocator; const assert = std.assert; const builtin = #import("builtin"); const current = builtin.os; const link_libc = builtin.link_libc; const linux = #import("os/linux.nat"); const macos = #import("os/macos.nat"); const windows = #import("os/windows.nat"); const c = std.c; const system = switch (link_libc) { true => c, false => switch (current) { .linux => linux, .macos => macos, .windows => windows, }, }; const unwrap_syscall = system.unwrap_syscall; const exit = fn(exit_code: s32) noreturn { switch (current) { .linux => _ = #syscall(#cast(linux.Syscall.exit_group), #cast(exit_code)), .macos => macos.exit(exit_code), .windows => windows.ExitProcess(#cast(exit_code)), } } const max_file_operation_byte_count = switch (current) { .linux => 0x7ffff000, .macos => 0x7fffffff, else => #error("OS not supported"), }; const FileDescriptor = struct{ handle: system.FileDescriptor, const ReadError = error{ }; const read = fn(file_descriptor: FileDescriptor, bytes: []u8) ReadError!usize { if (bytes.len > 0) { switch (current) { .linux, .macos => { const len: usize = #min(max_file_operation_byte_count, bytes.len); const syscall_result = system.read(file_descriptor, bytes); const byte_count = unwrap_syscall(syscall_result) catch |err| switch (err) { else => unreachable, }; return byte_count; }, else => #error("OS not supported"), } } else { return 0; } } const WriteError = error{ write_failed, }; const write = fn (file_descriptor: FileDescriptor, bytes: []const u8) WriteError!usize { switch (current) { .linux, .macos => { const len: usize = #min(max_file_operation_byte_count, bytes.len); const syscall_result = system.write(file_descriptor.handle, bytes[0..len]); const byte_count = unwrap_syscall(syscall_result) catch |err| switch (err) { else => return WriteError.write_failed, }; return byte_count; }, else => #error("OS not supported"), } } }; const StdFileDescriptor = enum { stdin = 0, stdout = 1, stderr = 2, const get = fn(descriptor: StdFileDescriptor) FileDescriptor{ switch (current) { .linux, .macos => { return FileDescriptor{ .handle = #cast(descriptor), }; }, else => #error("OS not supported"), } } }; const ProtectionFlags = bitfield(u32){ read: bool, write: bool, execute: bool, }; const MapFlags = bitfield(u32){ reserve: bool, commit: bool, }; const VirtualAllocateError = error{ allocation_failed, }; const allocate_virtual_memory = fn(address: ?[&]u8, length: usize, general_protection_flags: ProtectionFlags, general_map_flags: MapFlags) VirtualAllocateError![&]u8 { const protection_flags = system.get_protection_flags(flags = general_protection_flags); const map_flags = system.get_map_flags(flags = general_map_flags); const file_descriptor = -1; const offset = 0; switch (current) { .linux, .macos => { const syscall_result = system.mmap(address, length, protection_flags, map_flags, file_descriptor, offset); if (link_libc) { if (result != system.MAP_FAILED) { const result_address: [&]u8 = #cast(result); return result_address; } else { // TODO: unreachable; } } else { const result = unwrap_syscall(syscall_result) catch |err| switch (err) { else => unreachable, }; const pointer: [&]u8 = #cast(result); return pointer; } }, else => #error("OS not supported"), } } const FreeError = error{ free_failed, }; const free_virtual_memory = fn(bytes: []const u8) FreeError!void { switch (current) { .linux, .macos => { const syscall_result = system.munmap(bytes); _ = unwrap_syscall(syscall_result) catch |err| switch (err) { else => unreachable, }; }, else => #error("OS not supported"), } } const ReadLinkError = error{ failed, }; const readlink = fn(file_path: [&:0]const u8, buffer: []u8) ReadLinkError![]u8 { switch (current) { .linux, .macos => { const syscall_result = system.readlink(file_path, buffer); const byte_count = unwrap_syscall(syscall_result) catch |err| switch (err) { else => unreachable, }; const bytes = buffer[0..byte_count]; return bytes; }, else => #error("OS not supported"), } } const max_path_byte_count = switch (current) { .linux => 0x1000, .macos => 1024, else => #error("OS not supported"), }; const CurrentExecutablePath = error{ failed, }; const current_executable_path = fn(buffer: [:0]u8) CurrentExecutablePath![]u8 { switch (current) { .linux => { const bytes = readlink("/proc/self/exe", buffer) catch |err| switch (err) { else => unreachable, }; return bytes; }, //.macos => { // TODO: //var symlink_path_buffer: [max_path_byte_count:0]u8 = undefined; //var symlink_path_len: u32 = symlink_path_buffer.len + 1; //const ns_result = macos._NSGetExecutablePath(symlink_path_buffer.&, symlink_path_len.&); //if (ns_result == 0) { // const symlink_path = symlink_path_buffer[0..symlink_path_len]; // const result = macos.realpath(symlink_path.ptr, buffer.ptr); // if (result != null) { // var i: usize = 0; // while (i < buffer.len) { // if (result[i] == 0) { // break; // } // i += 1; // } // assert(i < buffer.len); // return result[0..i]; // } else { // return null; // } //} else { // return null; //} //}, else => #error("OS not supported"), } } const Process = struct{ const Id = system.ProcessId; }; const DuplicateProcessError = error{ system_resources, out_of_memory, }; const duplicate_process = fn () DuplicateProcessError!Process.Id { switch (current) { .linux, .macos => { const syscall_result = system.fork(); const result = unwrap_syscall(syscall_result) catch |err| return switch (err) { .AGAIN, .NOMEM => DuplicateProcessError.system_resources, else => unreachable, }; const truncated: u32 = #cast(result); const process_id: Process.Id = #cast(truncated); return process_id; }, else => #error("OS not supported"), } } const ExecveError = error{ execve_failed, }; const execute = fn(path: [&:0]const u8, argv: [&:null]const ?[&:0]const u8, env: [&:null]const ?[&:null]const u8) ExecveError!noreturn { switch (current) { .linux, .macos => { const syscall_result = linux.execve(path, argv, env); const signed_syscall_result: ssize = #cast(syscall_result); if (signed_syscall_result == -1) { return ExecveError.execve_failed; } else { unreachable; } }, else => #error("OS not supported"), } } const EventFileDescriptorError = error{ }; const event_file_descriptor = fn(initial_value: u32, flags: u32) EventFileDescriptorError!FileDescriptor { switch (current) { .linux => { const syscall_result = linux.event_file_descriptor(count = initial_value, flags); const result = unwrap_syscall(syscall_result) catch |err| switch (err) { else => unreachable, }; const file_descriptor: system.FileDescriptor = #cast(result); return file_descriptor; }, else => #error("OS not supported"), } } const Dup2Error = error{ }; const dup2 = fn(old_file_descriptor: system.FileDescriptor, new_file_descriptor: system.FileDescriptor) Dup2Error!void { switch (current) { .linux => { const syscall_result = linux.dup2(old = old_file_descriptor, new = new_file_descriptor); _ = unwrap_syscall(syscall_result) catch |err| switch (err) { else => unreachable, }; }, else => #error("OS not supported"), } } const OpenError = error{ }; const open = fn(path: [&:0]const u8, flags: u32, permissions: u32) OpenError!FileDescriptor{ switch (current) { .linux => { const syscall_result = linux.open(path, flags, permissions); const result = unwrap_syscall(syscall_result) catch |err| switch (err) { else => unreachable, }; const file_descriptor = FileDescriptor{ .handle = #cast(result), }; return file_descriptor; }, else => #error("OS not supported"), } } const CloseError = error{ }; const close = fn(file_descriptor: system.FileDescriptor) CloseError!void { switch (current) { .linux => { const syscall_result = system.close(file_descriptor); _ = unwrap_syscall(syscall_result) catch |err| switch (err) { else => unreachable, }; }, else => #error("OS not supported"), } } const Pipe2Error = error{ }; const pipe2 = fn(flags: u32) Pipe2Error![2]system.FileDescriptor{ switch (current) { .linux => { var pipe: [2]system.FileDescriptor = undefined; const syscall_result = linux.pipe2(pipe.&, flags); _ = unwrap_syscall(syscall_result) catch |err| switch (err) { else => unreachable, }; return pipe; }, else => #error("OS not supported"), } } const PollFileDescriptor = system.PollFileDescriptor; const poll = fn(file_descriptors: []PollFileDescriptor, timeout: s32) ?usize { switch (current) { .linux => { if (linux.unwrap_syscall(syscall_result = linux.poll(file_descriptors = file_descriptors.ptr, file_descriptor_count = file_descriptors.len, timeout = timeout))) |result| { return result; } else { return null; } }, else => #error("OS not supported"), } } const termsig = fn(status: u32) u32 { return status & 0x7f; } const ifexited = fn(status: u32) bool { return termsig(status) == 0; } const exitstatus = fn(status: u32) u8 { const result: u8 = #cast((status & 0xff00) >> 8); return result; } const stopsig = fn(status: u32) u32 { return exitstatus(status); } const ifstopped = fn(status: u32) bool { const result: u16 = #cast(((status & 0xffff) * 0x10001) >> 8); return result > 0x7f00; } const ifsignaled = fn(status: u32) bool { return (status & 0xffff) - 1 < 0xff; } const WaitPidError = error{ failed, }; const waitpid = fn(pid: Process.Id, flags: u32) WaitPidError!u32 { switch (current) { .linux => { var status: u32 = undefined; while (true) { const syscall_result = linux.waitpid(pid, status = status.&, flags, resource_usage = 0); const signed_syscall_result: ssize = #cast(syscall_result); if (signed_syscall_result != -4) { _ = unwrap_syscall(syscall_result) catch |err| switch (err) { else => unreachable, }; return status; } } }, .macos => { var status: s32 = undefined; if (macos.waitpid(pid, status.&, #cast(flags)) != -1) { const status_u: u32 = #cast(status); return status_u; } else { unreachable; } }, else => #error("OS not supported"), } } const MemFdCreateError = error{ }; const memfd_create = fn(name: [&:0]const u8, flags: u32) MemFdCreateError!FileDescriptor{ switch (current) { .linux => { const syscall_result = linux.memfd_create(path, flags); const result = unwrap_syscall(syscall_result) catch |err| switch (err) { else => unreachable, }; const file_descriptor = FileDescriptor{ .handle = #cast(result), }; return file_descriptor; }, else => #error("OS not supported"), } } const IoChannelBehavior = enum{ pipe, close, inherit, ignore, };