lib/dirs+lib/os+test: add lib/dirs (XDG paths) + os.mkdirs
Port Hare's lib/dirs (ref/hare/dirs/xdg.ha) — XDG base-directory
lookup with mkdir-on-demand:
dirs.config(prog) — XDG_CONFIG_HOME/<prog>, fallback $HOME/.config/<prog>
dirs.cache(prog) — XDG_CACHE_HOME/<prog>, fallback $HOME/.cache/<prog>
dirs.data(prog) — XDG_DATA_HOME/<prog>, fallback $HOME/.local/share/<prog>
dirs.state(prog) — XDG_STATE_HOME/<prog>, fallback $HOME/.local/state/<prog>
Static-buffer return (256B pathbuf), overwritten on the next dirs.*
call — same contract as lib/temp. Fallback triggers on unset, empty,
or non-absolute XDG var (matches Hare's path::abs check). HOME-unset
rt_aborts (matches Hare's `as str` panic on missing HOME).
os.mkdirs(path, mode) — recursive mkdir, EEXIST-silenced. Walks the
path replacing each '/' with NUL, mkdir'ing each prefix, restoring
the slash. Path bytes must be writable (documented).
Surface skips with reason notes in dirs.ww header:
- runtime() — needs stat+getuid; defer until lib/os has them
- XDG_CONFIG_DIRS / XDG_DATA_DIRS — not in Hare's xdg.ha
- fmt::fatalf-on-mkdir — wired to rt_abort until {n}-placeholder
fmt lands
Cohort coverage in lib/dirs/dirstest.ww + test/wcc/975_dirs_run.c
(mkdtemp-rooted env state): XDG-absolute / XDG-non-absolute fallback /
XDG-unset×2.
This commit is contained in:
43
lib/os/os.ww
43
lib/os/os.ww
@@ -193,6 +193,49 @@ export fn rmdir(path: *u8) i32 = {
|
||||
return syscall1(nr.RMDIR, path: i64): i32;
|
||||
};
|
||||
|
||||
// mkdirs — recursive mkdir. Creates `path` and any non-existent
|
||||
// parent directories with the given mode. EEXIST is silently
|
||||
// accepted (matches Hare's `errors::exists` skip in os::mkdirs);
|
||||
// any other syscall failure surfaces as `oserror`.
|
||||
//
|
||||
// `path` must be NUL-terminated AND its bytes must be writable —
|
||||
// mkdirs temporarily replaces '/' separators with NUL while
|
||||
// invoking [[mkdir]] on each prefix, then restores them. Pointing
|
||||
// `path` at a string literal will segfault. Callers hold the bytes
|
||||
// in a writable buffer (rt_alloc'd, a static `[N]u8`, etc.) — same
|
||||
// precedent as [[temp.named]]'s pathbuf.
|
||||
//
|
||||
// Mirrors Hare's os::mkdirs (recursive variant of os::mkdir).
|
||||
export fn mkdirs(path: *u8, mode: i32) (void | oserror) = {
|
||||
// Find the path length (excluding trailing NUL).
|
||||
let n: i32 = 0;
|
||||
for (path[n] != 0u8) { n += 1; };
|
||||
if (n == 0) { return; };
|
||||
|
||||
// Walk forward; at each '/' boundary, NUL-terminate the prefix,
|
||||
// mkdir it, restore the slash, continue. Skip index 0 so a
|
||||
// leading '/' on absolute paths doesn't trigger an empty mkdir.
|
||||
let i: i32 = 1;
|
||||
for (i < n) {
|
||||
if (path[i] == 47u8) { // '/'
|
||||
path[i] = 0u8;
|
||||
let r: i32 = mkdir(path, mode);
|
||||
path[i] = 47u8;
|
||||
if (r < 0) {
|
||||
if (r != -17) { return r: i64: oserror; };
|
||||
};
|
||||
};
|
||||
i += 1;
|
||||
};
|
||||
|
||||
// mkdir the full path.
|
||||
let r: i32 = mkdir(path, mode);
|
||||
if (r < 0) {
|
||||
if (r != -17) { return r: i64: oserror; };
|
||||
};
|
||||
return;
|
||||
};
|
||||
|
||||
// getpid(2). Used by the driver to mint unique scratch paths.
|
||||
export fn getpid() i32 = {
|
||||
return syscall0(nr.GETPID): i32;
|
||||
|
||||
Reference in New Issue
Block a user