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 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#
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#
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#
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#
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#
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#
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#
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#
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_ALIASES | Aliases are extra names for an errno that already has a canonical name, so they take part in name -> number lookup only. |
| _LINUX_TABLE | — |
| _by_errno | Parsed 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. |