module errno

module lib/errno.mu

import "errno"

errno maps raw errno integers to symbolic names and human-friendly messages.

The tables are DATA, deliberately spelled as string constants rather than as map literals. A map literal is not data in mu -- it is code that builds a map every time the module loads, and 187 nested two-key maps cost 623KB in a native binary, ten times a whole hello-world. The same rows as text cost their own bytes and nothing else, and only the running platform's table is ever parsed.

Row format is "<errno> <NAME> <message>", one per line. The name never contains a space, so the parser splits on the first two spaces and takes the rest of the line as the message verbatim.

Sources of truth: <sys/errno.h> for darwin (Apple/BSD values, same numbering on amd64 and arm64), and Go's own syscall.Errno strings for linux (the asm-generic set, identical on amd64, arm64 and riscv64). Messages are capitalised as their source has them; describe() lowercases the first letter again when it renders one after a path prefix, which is how io.read_file produces "open /x: no such file or directory".

Imports

Values

SYSCALL_ERROR_PREFIX#

SYSCALL_ERROR_PREFIX

SYSCALL_ERROR_PREFIX is the message the syscall builtin uses to report a failure. Every backend emits exactly this text followed by the errno, and from_error below is the only thing that parses it -- keep the two together.

Source lib/errno.mu:343
SYSCALL_ERROR_PREFIX := "syscall failed: errno "

Functions

describe#

fn describe(err)

describe renders a syscall error the way a C program would report it, e.g. "no such file or directory". Returns nil when the errno is unknown or the error did not come from a syscall.

Source lib/errno.mu:385
fn describe(err) {
    code := from_error(err)
    if code == nil {
        return nil
    }
    text := message(code)
    if text == nil {
        return nil
    }
    return _lower_first(text)
}

from_error#

fn from_error(err)

from_error recovers the errno from an error value returned by syscall.

An ERROR carries only a message, so the number has to travel inside the text. Returns nil for anything that is not a syscall failure, so a caller can fall back rather than having to pre-classify the error.

Source lib/errno.mu:350
fn from_error(err) {
    if type(err) != "ERROR" {
        return nil
    }
    text := inspect(err)
    if len(text) <= len(SYSCALL_ERROR_PREFIX) {
        return nil
    }
    i := 0
    while i < len(SYSCALL_ERROR_PREFIX) {
        if text[i] != SYSCALL_ERROR_PREFIX[i] {
            return nil
        }
        i = i + 1
    }
    digits := 0
    seen := 0
    while i < len(text) {
        d := ord(text[i]) - 48
        if d < 0 || d > 9 {
            return nil
        }
        digits = digits * 10 + d
        seen = seen + 1
        i = i + 1
    }
    if seen == 0 {
        return nil
    }
    return digits
}

from_name#

fn from_name(errno_name)

from_name returns the errno integer for a symbolic name (e.g. "ENOENT") or ERROR.

Source lib/errno.mu:325
fn from_name(errno_name) {
    if !typing.is_str(errno_name) {
        return error("errno.from_name expects STRING name, got " + type(errno_name))
    }
    if !_load() {
        return error("errno: unsupported platform: " + platform())
    }

    code := _by_name[errno_name]
    if code == nil {
        return error("errno: unknown errno name: " + errno_name)
    }
    return code
}

info#

fn info(errno)

info returns a map {"errno": <int>, "name": <string>, "message": <string>}. Returns ERROR for unsupported platforms or unknown errno values.

Source lib/errno.mu:269
fn info(errno) {
    if !typing.is_int(errno) {
        return error("errno.info expects INTEGER errno, got " + type(errno))
    }
    if !_load() {
        return error("errno: unsupported platform: " + platform())
    }

    entry := _by_errno[errno]
    if entry == nil {
        return error("errno: unknown errno: " + str(errno))
    }

    return {"errno": errno, "name": entry["name"], "message": entry["message"]}
}

is_known#

fn is_known(errno)

is_known returns true if errno is known on the current platform.

Source lib/errno.mu:286
fn is_known(errno) {
    if !typing.is_int(errno) {
        return false
    }
    if !_load() {
        return false
    }
    return _by_errno[errno] != nil
}

message#

fn message(errno)

message returns the human message (e.g. "No such file or directory") or nil.

Source lib/errno.mu:306
fn message(errno) {
    res := info(errno)
    if is_error(res) {
        return nil
    }
    return res["message"]
}

name#

fn name(errno)

name returns the symbolic errno name (e.g. "ENOENT") or nil.

Source lib/errno.mu:297
fn name(errno) {
    res := info(errno)
    if is_error(res) {
        return nil
    }
    return res["name"]
}

to_string#

fn to_string(errno)

to_string returns a friendly string like "ENOENT: No such file or directory", or "" when the errno is unknown.

Source lib/errno.mu:316
fn to_string(errno) {
    res := info(errno)
    if is_error(res) {
        return ""
    }
    return res["name"] + ": " + res["message"]
}

Internal helpers

Underscore-prefixed names are implementation detail. They are listed so the module's source reads without surprises, not as API — they may change at any time.

_DARWIN_ALIASES
_DARWIN_TABLE
_LINUX_ALIASESAliases are extra names for an errno that already has a canonical name, so they take part in name -> number lookup only.
_LINUX_TABLE
_by_errnoParsed tables for the running platform, built on first use and kept.
_by_name
_each_row(table, visit)_each_row splits table into lines and hands each one to visit as a [errno, name, message] row.
_load()_load parses the running platform's table once.
_loaded_platform
_lower_first(text)_lower_first lowercases the leading character, so the errno table's "No such file or directory" reads correctly after a "path: " prefix.
_parse_row(table, start, end)_parse_row reads "<errno> <NAME> <message>" from table[start:end].
_slice(text, start, end)
_slice_from(text, start)
_tables(platform_key)_tables returns [table, aliases] for the platform, or nil if unsupported.