Faithful realignment of lib/path to Hare's buffer-centric API
(ref/hare/path/{stack,buffer}.ha). Replaces the old str-only path.ww
wholesale (zero consumers). Scope = stack-core: init() deferred (returns
~4KB (buffer|error), rides the #40 arc / #147); abs/dirname/basename land
in c3; extension/join dropped.
appendlit uses the #145 slice-copy-assign arm (buf.buf[lo:hi]=bs), no hand
loop. dot/dotdot are faithful module-global []u8 (D2 #148). MAX =
os.PATH_MAX-1. Divergences (size->i32 indices, frombytes, module-global
consts) cited inline.
Tests: lib/path/pathtest.ww (table-driven), wired at test/wcc/989_path_run.c;
push rows mirror stack.ha:107-111 verbatim incl the restored "/d"
intermediate. Slot 989 (overflow bucket) since 970 is taken.
C-first: cstage @test green; wwstage byte-id deferred to the #125 batch.
path classified M_WWREJECT in 989_lib_byteid (w6c_ww rejects the module-
global slice consts = #120/#29; + the #148 twin #151) — self-graduates back
to M_ID the day wwstage accepts. path moved off the 900_stdlib standalone
list (import-dependent: os.PATH_MAX def-dim + match over imported error
types; coverage at 989_path_run), per the bytes/fmt/os precedent.
Gate: all 324 passed, byte-id 990-997 green, w6c/w6c_ww unchanged
(lib-only, non-embedded).
140 lines
4.5 KiB
Plaintext
140 lines
4.5 KiB
Plaintext
// path — filesystem path manipulation. UTF-8 paths, '/' separator.
|
|
// Mirrors Hare's path:: surface (ref/hare/path/). This is the
|
|
// path::buffer stack-buffer port (c2-stack subset: error/posix defs +
|
|
// buffer + isroot + appendlit + appendnorm + push + string + set).
|
|
// init/abs/dirname/basename and the rest land in later folds.
|
|
|
|
package path;
|
|
|
|
import bytes;
|
|
import strings;
|
|
import os;
|
|
|
|
// ref/hare/path/error.ha:6-12. The 3 !void singletons are
|
|
// structurally identical; cstage discriminates them correctly
|
|
// (#142 is a wwstage-only flatvariantidxt ambiguity, rides #125).
|
|
export type cant_extend = !void;
|
|
export type too_long = !void;
|
|
export type not_prefix = !void;
|
|
export type error = !(cant_extend | too_long | not_prefix);
|
|
|
|
// ref/hare/path/error.ha:15-24.
|
|
export fn strerror(e: error) str = {
|
|
match (e) {
|
|
case cant_extend => return "Can't add extension (filename is root or all dots)";
|
|
case too_long => return "Path buffer overflow";
|
|
case not_prefix => return "Prefix not present";
|
|
};
|
|
};
|
|
|
|
// ref/hare/path/+linux.ha:7.
|
|
export def SEP: u8 = '/';
|
|
// ref/hare/path/+linux.ha:9. No c2 use; c3 abs/isroot-str consume it.
|
|
const sepstr: str = "/";
|
|
// ref/hare/path/+linux.ha:18 (sys::PATH_MAX-1). Div: route through
|
|
// os.PATH_MAX (lib/os/os.ww:84, i32=4096) → MAX=4095; const-folds as
|
|
// the buffer array dim (c1 #141). No hardcoded literal (rule-13).
|
|
export def MAX: i32 = os.PATH_MAX - 1;
|
|
|
|
// ref/hare/path/buffer.ha:6-9. Div: end size→i32 (lib/CLAUDE.md
|
|
// str.len:i32; bytes.index→(i32|void)).
|
|
export type buffer = struct {
|
|
buf: [MAX]u8,
|
|
end: i32,
|
|
};
|
|
|
|
// ref/hare/path/stack.ha:30-31. Faithful module-global []u8 (D2 #148
|
|
// fixes the by-value-arg garbage header) — no [N]u8, no copy-loop.
|
|
const dot: []u8 = ['.'];
|
|
const dotdot: []u8 = ['.', '.'];
|
|
|
|
// ref/hare/path/buffer.ha:44-51. c3 WIDENS to isroot(path:(*buffer|str))
|
|
// alongside abs; c2 needs only the *buffer arm (appendnorm/appendlit
|
|
// callers).
|
|
export fn isroot(buf: *buffer) bool = {
|
|
return buf.end == 1 && buf.buf[0] == SEP;
|
|
};
|
|
|
|
// ref/hare/path/stack.ha:63-74.
|
|
fn appendlit(buf: *buffer, bs: []u8) (i32 | error) = {
|
|
let newend: i32 = buf.end;
|
|
if (buf.end == 0 || isroot(buf)) {
|
|
if (MAX < buf.end + bs.len) {
|
|
// cstage rejects bare `return too_long` into the nested
|
|
// (i32|error); two-step widen (io precedent stream.ww:56-58).
|
|
let e: error = too_long; return e;
|
|
};
|
|
} else {
|
|
if (MAX < buf.end + bs.len + 1) {
|
|
let e: error = too_long; return e;
|
|
};
|
|
buf.buf[buf.end] = SEP;
|
|
newend += 1;
|
|
};
|
|
// Div: Hare `buf.buf[newend..newend+len(bs)] = bs` in colon
|
|
// spelling — the #145 slice-copy-assign LHS arm (stack.ha:72).
|
|
buf.buf[newend:newend+bs.len] = bs;
|
|
return newend + bs.len;
|
|
};
|
|
|
|
// ref/hare/path/stack.ha:43-59. Div: `..` reslice→colon; Hare if-expr
|
|
// → if-statement; `0z` yield → `0i32` (match arms must agree, bare 0
|
|
// is untyped_int).
|
|
fn appendnorm(buf: *buffer, seg: []u8) (i32 | error) = {
|
|
if (seg.len == 0 || bytes.equal(dot, seg)) { return buf.end; };
|
|
if (bytes.equal(dotdot, seg)) {
|
|
if (isroot(buf)) { return buf.end; };
|
|
let isep: i32 = match (bytes.rindex(buf.buf[0:buf.end], SEP)) {
|
|
case void => yield 0i32;
|
|
case let i: i32 => yield i + 1;
|
|
};
|
|
if (buf.end == 0 || bytes.equal(buf.buf[isep:buf.end], dotdot)) {
|
|
return appendlit(buf, dotdot)?;
|
|
} else {
|
|
if (isep <= 1) { return isep; };
|
|
return isep - 1;
|
|
};
|
|
} else {
|
|
return appendlit(buf, seg)?;
|
|
};
|
|
};
|
|
|
|
// ref/hare/path/buffer.ha:28-31. Div: fromutf8_unsafe→strings.frombytes
|
|
// (rule-9 carve-out); `..` reslice→colon.
|
|
export fn string(buf: *buffer) str = {
|
|
if (buf.end == 0) { return "."; };
|
|
return strings.frombytes(buf.buf[0:buf.end]);
|
|
};
|
|
|
|
// ref/hare/path/stack.ha:9-28. Div: `..` reslice→colon; len(x)→x.len.
|
|
export fn push(buf: *buffer, items: str...) (str | error) = {
|
|
for (let item .. items) {
|
|
let elem: []u8 = strings.toutf8(item);
|
|
// Hare's `for(true) match{...}` (stack.ha:13-25) breaks out of
|
|
// the void arm and re-loops the j-arm; ww spells the loop step
|
|
// as explicit `continue` (j-arm) + `break` (after match).
|
|
for (true) {
|
|
match (bytes.index(elem, SEP)) {
|
|
case void => { buf.end = appendnorm(buf, elem)?; };
|
|
case let j: i32 => {
|
|
if (j == 0 && buf.end == 0) {
|
|
buf.buf[0] = SEP; buf.end = 1;
|
|
} else {
|
|
buf.end = appendnorm(buf, elem[0:j])?;
|
|
};
|
|
elem = elem[j+1:];
|
|
continue;
|
|
};
|
|
};
|
|
break;
|
|
};
|
|
};
|
|
return string(buf);
|
|
};
|
|
|
|
// ref/hare/path/buffer.ha:20-23. (str|error) ≤32B return → in c2-stack.
|
|
export fn set(buf: *buffer, items: str...) (str | error) = {
|
|
buf.end = 0;
|
|
return push(buf, items...);
|
|
};
|