Library

system/posix

Raw POSIX syscalls.

1 file lib/system/posix/posix.vader 75 exports

Types

Pollfd :: struct

struct pollfd: one descriptor and what to watch it for.

Field names are the C ones because a mirror copies BY NAME.

Fields

fd: i32
No description.
events: i16
No description.
revents: i16
No description.
Winsize :: struct

struct winsize: the terminal size TIOCGWINSZ fills in.

Field names are the C ones because a mirror copies BY NAME; the snake_case rule does not reach across the boundary.

Fields

ws_row: u16
No description.
ws_col: u16
No description.
ws_xpixel: u16
No description.
ws_ypixel: u16
No description.

Functions

sys_write :: fn(fd: i32, buf: u8[], count: i64) -> i64

write(2). Byte count written, or -1 with errno set.

i64, not i32: POSIX returns ssize_t, 64-bit on every target here. @c_header hands cc the real prototype, so a width mismatch stops the build rather than truncating silently.

Parameters

fdi32 No description.
bufu8[] No description.
counti64 No description.

Returns

i64
sys_opendir :: fn(path: string) -> CPointer | null

opendir(3). NULL when path is not a directory, does not exist, or cannot be searched.

It answers "is this a directory" only for a directory the caller can READ: sys_path_kind is what std/io::is_dir asks, and says why.

Parameters

pathstring the directory to open.

Returns

CPointer | null a DIR*, or null.
sys_closedir :: fn(dir: CPointer) -> i32

closedir(3). Pairs with sys_opendir.

Parameters

dirCPointer the handle sys_opendir returned.

Returns

i32
sys_last_errno :: fn() -> i32

The calling thread's errno, as the LAST foreign call left it.

errno is a macro, not a symbol: *__error() on Darwin, *__errno_location() on glibc, so no @extern can name it directly and the runtime reads it for us.

⚠️ Read it IMMEDIATELY after the call that failed. Any foreign call in between may overwrite it, this one included once it has returned.

Returns

i32 the raw platform value ; 0 when nothing has failed.
sys_path_kind :: fn(path: string) -> i32

What KIND of thing path is, via stat(2).

struct stat is NOT mirrorable, not "ambiguous between arches", genuinely inexpressible. A mirror is pinned field by field to __builtin_offsetof, so it is only valid as a contiguous LEADING prefix ; st_mode leads on neither arch (offset 24 on linux-x86_64, 16 on linux-arm64), and the two arches also disagree on field ORDER, which no width or arch axis could express. So the runtime resolves the member and hands back an ordinal. See docs/adr/0013-what-the-ffi-boundary-cannot-express.md.

sys_opendir is NOT a substitute: it needs READ permission where stat needs only traverse rights on the parent, so a directory with mode 0111 answers "not a directory".

An enum would name the answer set better, but @extern cannot carry one (T3050), and a Vader wrapper here would give this module a BODY, which the seed refuses: system/posix would then emit a unit on POSIX targets and none on Windows, and --seed-targets requires one module set for every target. Hence four constants and a declaration-only module.

Parameters

pathstring the path to classify.

Returns

i32 PATH_MISSING when stat fails, for any reason.
sys_path_mtime :: fn(path: string) -> i64

When path was last modified, in nanoseconds since the Unix epoch, via stat(2) : the runtime reads the member for the reason sys_path_kind gives, and Darwin names it st_mtimespec where Linux says st_mtim.

Parameters

pathstring the path to probe.

Returns

i64 -1 when stat fails, errno saying why.
sys_stream_size :: fn(stream: CPointer) -> i64

The size of the regular file stream reads, via fstat(2), for the reason sys_path_kind gives.

Parameters

streamCPointer an open FILE*.

Returns

i64 -1 for anything but a regular file (a pipe, a device, whose size says nothing of what a read finds), or when fstat fails.
sys_readdir :: fn(dir: CPointer) -> CPointer | null

readdir(3). The next entry, or null at the end of the directory.

The struct dirent it returns is owned and REUSED by the C library: read the name out before the next call and never keep the pointer.

Parameters

dirCPointer the handle sys_opendir returned.

Returns

CPointer | null a struct dirent*, or null at the end.
dirent_name :: fn(ent: CPointer) -> string

The d_name of a struct dirent, as a Vader string.

The one step a Vader FFI call cannot take for itself: reading a NAMED member of a C struct. d_name sits at a different offset on every system, and only cc can resolve it: from the real header, at the accessor in the runtime.

It has to be a CALL, not a read of the struct: the interpreter has no C layout, but it resolves an @extern by name out of the running process the way it reaches libc. That is what keeps read_dir working on both backends, which anything in the stdlib has to.

The returned bytes belong to the C library and die at the next sys_readdir; the shim interns them before then.

Parameters

entCPointer a struct dirent* from sys_readdir.

Returns

string the entry name.
sys_ferror :: fn(stream: CPointer) -> i32

ferror(3). Non-zero once the stream's error indicator is set.

A short fread means end of file OR an error, and the count cannot tell them apart -- this is what separates them.

Parameters

streamCPointer the FILE*.

Returns

i32 non-zero if the stream is in error.
sys_read :: fn(fd: i32, @c_out buf: u8[], count: i64) -> i64

read(2). Returns the byte count, 0 at end of file, -1 on error, and a SHORT read is not an error: a pipe hands over what has arrived so far, so a caller wanting n bytes loops until it has them.

Parameters

fdi32 the descriptor; 0 is stdin.
bufu8[] receives the bytes.
counti64 how many to ask for, at most buf.len().

Returns

i64 bytes read, 0 at EOF, -1 on error.
sys_pipe :: fn(@c_out fds: i32[]) -> i32

pipe(2). Fills fds with the read end then the write end.

Parameters

fdsi32[] a two-element array the call writes into.

Returns

i32 0 on success, -1 on failure.
sys_close :: fn(fd: i32) -> i32

close(2).

Parameters

fdi32 the descriptor to close.

Returns

i32
sys_spawn_actions_init :: fn(@c_out actions: u8[]) -> i32

posix_spawn_file_actions_init(3).

Parameters

actionsu8[] SPAWN_ACTIONS_BYTES of scratch.

Returns

i32
sys_spawn_actions_destroy :: fn(@c_out actions: u8[]) -> i32

posix_spawn_file_actions_destroy(3). Pairs with the init.

Parameters

actionsu8[] the buffer the init filled.

Returns

i32
sys_spawn_actions_addclose :: fn(@c_out actions: u8[], fd: i32) -> i32

posix_spawn_file_actions_addclose(3): close fd in the child.

Parameters

actionsu8[] the actions buffer.
fdi32 the descriptor to close after the fork.

Returns

i32
sys_spawn_actions_adddup2 :: fn(@c_out actions: u8[], fd: i32, newfd: i32) -> i32

posix_spawn_file_actions_adddup2(3): dup2(fd, newfd) in the child.

Parameters

actionsu8[] the actions buffer.
fdi32 the source descriptor.
newfdi32 where it lands in the child.

Returns

i32
sys_posix_spawnp :: fn(@c_out pid: i32[], file: string, actions: u8[], attrp: CPointer | null, argv: u64[], envp: CPointer) -> i32

posix_spawnp(3): launch file, resolved through PATH.

Parameters

pidi32[] a one-element array receiving the child's pid.
filestring the program name.
actionsu8[] the file actions, or an empty buffer for none.
attrpCPointer | null null: no spawn attributes are used.
argvu64[] a NULL-terminated vector of char*, as i64 words.
envpCPointer sys_environ().

Returns

i32 0 on success, an errno value otherwise.
sys_waitpid :: fn(pid: i32, @c_out status: i32[], options: i32) -> i32

waitpid(2).

Parameters

pidi32 the child to wait for.
statusi32[] a one-element array receiving the raw wait status.
optionsi32 WNOHANG to poll rather than block.

Returns

i32 the pid when it changed state, 0 under WNOHANG while it still runs, -1 on error.
sys_kill :: fn(pid: i32, sig: i32) -> i32

kill(2).

Parameters

pidi32 the process to signal.
sigi32 the signal number.

Returns

i32
sys_strdup :: fn(s: string) -> CPointer

strdup(3): a heap copy of s, owned by the caller.

Used to build the char** a spawn wants: a i64[] of these lends as one.

Parameters

sstring the string to copy.

Returns

CPointer a char* the caller frees with sys_free.
sys_free :: fn(p: CPointer) -> void

free(3). Releases what sys_strdup returned.

Parameters

pCPointer the pointer to release.
sys_environ :: fn() -> CPointer

The process environment, for sys_posix_spawnp.

environ is a VARIABLE and the FFI calls functions, so the runtime names it. Passing null instead gives the child an EMPTY environment: no PATH, which every spawned tool needs.

Returns

CPointer the parent's char** environment.
sys_poll_one :: fn(@c_pointer @c_out fds: Pollfd, nfds: u64, timeout_ms: i32) -> i32

poll(2) on a SINGLE descriptor.

poll takes an array; one element crosses as its address, which is the same thing to C. A caller watching several would need the array form.

Parameters

fdsPollfd the descriptor to watch; revents is filled in.
nfdsu64 always 1 here. nfds_t is 4 bytes on Darwin and 8 on Linux, and neither is spellable per target: passing the wider one is ABI-correct on both, since an unsigned int argument travels in the low half of its register and 1 fits either way. It would not be, for a count that used the upper bits.
timeout_msi32 milliseconds, negative to block.

Returns

i32 how many descriptors are ready, 0 on timeout, -1 on error.
sys_tcgetattr :: fn(fd: i32, @c_out buf: u8[]) -> i32

tcgetattr(3). Reads the terminal state into an OPAQUE buffer.

The buffer is never interpreted on the Vader side: it travels from here to sys_tcsetattr and back, so nothing needs the layout of struct termios, whose shape differs between systems (c_line is Linux-only) and whose c_cc is an array. buf must hold at least TERMIOS_BYTES.

Parameters

fdi32 the descriptor: 0 for stdin.
bufu8[] receives the state.

Returns

i32 0 on success, -1 otherwise.
sys_tcsetattr :: fn(fd: i32, action: i32, buf: u8[]) -> i32

tcsetattr(3). Applies a state read by sys_tcgetattr.

Parameters

fdi32 the descriptor.
actioni32 when to apply it: TCSAFLUSH here.
bufu8[] the state to install.

Returns

i32 0 on success, -1 otherwise.
sys_cfmakeraw :: fn(@c_out buf: u8[]) -> void

cfmakeraw(3). Turns a state into the raw one, in place.

The whole reason Vader never has to name a termios member: the flag surgery happens in C. It is MORE aggressive than a hand-rolled raw mode: it also clears OPOST, so \n no longer returns the carriage and a caller must write \r\n itself.

Parameters

bufu8[] No description.
sys_ioctl_winsize :: fn(fd: i32, request: u64, @c_pointer @c_out ws: Winsize) -> i32

ioctl(2) for TIOCGWINSZ. 0 on success, -1 otherwise.

ioctl is variadic; @c_header brings the real prototype, so the shim's three-argument call is ordinary C rather than a redeclaration.

Parameters

fdi32 the descriptor to ask about: 1 for stdout.
requestu64 always TIOCGWINSZ here.
wsWinsize receives the size.

Returns

i32
sys_sysconf :: fn(name: i32) -> i64

sysconf(3). The value of the configuration variable name, or -1 with errno set: an unknown name is EINVAL.

Parameters

namei32 one of the SC_* selectors.

Returns

i64
sys_isatty :: fn(fd: i32) -> i32

isatty(3). Whether fd refers to a terminal.

Parameters

fdi32 the descriptor: 1 for stdout, 2 for stderr.

Returns

i32 1 when it is a terminal, 0 otherwise.
sys_readlink :: fn(path: string, @c_out buf: u8[], size: i64) -> i64

readlink(2). Bytes placed in buf (NOT NUL-terminated) or -1.

Linux reads the running binary's path from /proc/self/exe.

Parameters

pathstring No description.
bufu8[] No description.
sizei64 No description.

Returns

i64
sys_realpath :: fn(path: string, @c_out resolved: u8[]) -> CPointer | null

realpath(3). Writes the canonical path into resolved and returns it, or null on failure. resolved must hold at least PATH_MAX bytes.

Parameters

pathstring No description.
resolvedu8[] No description.

Returns

CPointer | null
sys_fopen :: fn(path: string, mode: string) -> CPointer | null

fopen(3). A stream handle, or null when the file cannot be opened.

stdio rather than open(2): the mode is a STRING, so nothing here has to know the numeric value of O_CREAT, which is 512 on Darwin and 64 on Linux. Reading proceeds until end of file, so SEEK_END is not needed either.

Parameters

pathstring the file to open.
modestring "rb" or "wb".

Returns

CPointer | null
sys_fclose :: fn(stream: CPointer) -> i32

fclose(3). 0 on success.

Parameters

streamCPointer No description.

Returns

i32
sys_fread :: fn(@c_out buf: u8[], size: i64, count: i64, stream: CPointer) -> i64

fread(3). The number of ITEMS read: short at end of file.

Parameters

bufu8[] receives the bytes.
sizei64 item size in bytes; 1 to read bytes.
counti64 how many items to read.
streamCPointer an open stream.

Returns

i64
sys_fwrite :: fn(buf: u8[], size: i64, count: i64, stream: CPointer) -> i64

fwrite(3). The number of items written; short means failure.

Parameters

bufu8[] No description.
sizei64 No description.
counti64 No description.
streamCPointer No description.

Returns

i64
sys_getcwd :: fn(@c_out buf: u8[], size: i64) -> CPointer | null

getcwd(3). Fills buf with the working directory, NUL-terminated, and returns it, or null when buf is too small or the directory is unreadable. The length is not reported: read up to the NUL.

Parameters

bufu8[] receives the path.
sizei64 buf's capacity in bytes.

Returns

CPointer | null
sys_chdir :: fn(path: string) -> i32

chdir(2). 0 on success, -1 with errno set.

Parameters

pathstring the new working directory.

Returns

i32
sys_mkdir :: fn(path: string, mode: u32) -> i32

mkdir(2). 0 on success, -1 with errno set: EEXIST included, which is why the caller re-tests the path rather than reading errno.

Parameters

pathstring directory to create.
modeu32 permission bits, masked by the process umask.

Returns

i32
sys_access :: fn(path: string, mode: i32) -> i32

access(2). 0 when the check passes, -1 with errno set otherwise.

F_OK answers existence without a struct stat, which is why exists uses it: no C struct has to be mirrored to ask the only question being asked.

Parameters

pathstring No description.
modei32 No description.

Returns

i32
sys_unlink :: fn(path: string) -> i32

unlink(2). 0 on success, -1 with errno set.

NOT remove(3): on POSIX that falls back to rmdir for a directory, so an empty one would vanish through a call named remove_FILE.

Parameters

pathstring No description.

Returns

i32
sys_clock_gettime :: fn(clock_id: i32, @c_out ts: i64[]) -> i32

clock_gettime(2). Fills ts: two cells standing in for struct timespec, both members being 64-bit here: with seconds then nanoseconds. -1 on failure.

Parameters

clock_idi32 No description.
tsi64[] No description.

Returns

i32

Constants

STDOUT

const type inferred posix.vader:8
STDOUT: i32 :: 1

Standard output's file descriptor.

STDERR

const type inferred posix.vader:11
STDERR: i32 :: 2

Standard error's file descriptor.

CLOCK_REALTIME

const type inferred posix.vader:23
CLOCK_REALTIME: i32 :: 0

CLOCK_REALTIME: the wall clock.

CLOCK_MONOTONIC: i32: if VADER_OS == .Darwin { 6 } else { 1 }

CLOCK_MONOTONIC. The value is not portable: 6 on Darwin, 1 elsewhere.

EPERM: i32: 1

errno values std/io classifies.

The low numbers are fixed by history and identical on Darwin and Linux; the ones that are not say so on their own line. A raw errno is a PLATFORM value: compare against these, never against a literal.

ENOENT: i32: 2
EINTR: i32: 4
EIO: i32: 5
EBADF: i32: 9
EACCES: i32: 13
EEXIST: i32: 17
ENOTDIR: i32: 20
EISDIR: i32: 21
EINVAL: i32: 22
ENOSPC: i32: 28
EPIPE: i32: 32
EAGAIN: i32: if VADER_OS == .Darwin { 35 } else { 11 }

Diverges: 35 on Darwin, 11 on Linux, where EDEADLK sits on the other.

ENOTEMPTY: i32: if VADER_OS == .Darwin { 66 } else { 39 }

Diverges: 66 on Darwin, 39 on Linux.

PATH_MISSING: i32: 0

The four answers sys_path_kind gives. Values must match vader_path_kind.

PATH_REGULAR: i32: 1
PATH_DIRECTORY: i32: 2
PATH_OTHER: i32: 3

Device, FIFO, socket: anything with no size to speak of.

SPAWN_ACTIONS_BYTES: i64: 128

Bytes to reserve for a posix_spawn_file_actions_t.

An OPAQUE buffer: Vader never looks inside, the spawn_file_actions_* calls fill it. Nothing checks this number, so it is taken HIGH: MEASURED at 8 on Darwin (the type is a bare void*) and 80 on glibc: the same on x86_64, aarch64 and musl.

⚠️ Do NOT narrow this to what one system reports.

WNOHANG: i32: 1

WNOHANG: return immediately rather than waiting. 1 on both systems.

SIGKILL: i32: 9

SIGKILL. 9 on both systems.

POLLIN: i16: 1

POLLIN: data is available to read. 1 on both systems.

TERMIOS_BYTES: i64: 72

Bytes to reserve for a struct termios. The LARGER of the measured sizes, deliberately, and not a per-system value.

MEASURED: 72 on Darwin, 60 on Linux, x86_64 and arm64 alike, the type being built from fixed-width members. A SIZE, not an offset, which is why no architecture axis is needed where a struct stat mirror would have wanted one.

⚠️ Do NOT narrow this to the per-system figure. The risk is asymmetric: over-reserving wastes 12 bytes on Linux, under-reserving is an out-of-bounds write by tcgetattr, which nothing here checks. One constant, taken high, is the shape that cannot be got wrong.

TCSAFLUSH: i32: 2

TCSAFLUSH: apply after draining output, DISCARDING pending input. That last part is why raw mode uses it: keystrokes meant for the shell must not land in the prompt. 2 on both systems.

TIOCGWINSZ: u64: if VADER_OS == .Darwin { 0x4008_7468 } else { 0x5413 }

TIOCGWINSZ. A macro, and its value is not portable: it encodes the direction and the payload size, which differ between the two ioctl schemes.

SC_NPROCESSORS_ONLN: i32: if VADER_OS == .Darwin { 58 } else { 84 }

_SC_NPROCESSORS_ONLN: the processors currently online. The value is not portable: 58 on Darwin, 84 on Linux.

F_OK

const type inferred posix.vader:526
F_OK: i32 :: 0

access(2) mode: does the entry exist at all.

Written by vader doc. Icons: Codicons, CC BY 4.0. Fonts: Atkinson Hyperlegible, JetBrains Mono, OFL.