commit f1ea5921fca6696f8a3b22638a0839eb40540d66
parent 513dd8dbfbb955f330211c29616a9d59b3729c9f
Author: Byron Torres <b@torresjrjr.com>
Date: Fri, 15 Dec 2023 19:06:00 +0000
path: rename type 'buffer' to 'path'
Diffstat:
42 files changed, 449 insertions(+), 448 deletions(-)
diff --git a/cmd/hare/build.ha b/cmd/hare/build.ha
@@ -242,18 +242,18 @@ fn run(name: str, path: str, args: []str) error = {
};
fn get_output(goal: build::stage, input: str) (str | error) = {
- static let buf = path::buffer { ... };
+ static let p = path::path { ... };
let stat = os::stat(input)?;
- path::set(&buf, os::realpath(input)?)?;
+ path::set(&p, os::realpath(input)?)?;
if (!fs::isdir(stat.mode)) {
- path::pop_ext(&buf);
+ path::pop_ext(&p);
};
// don't add the .bin extension if the goal is to create a binary
if (goal != build::stage::BIN) {
- path::push_ext(&buf, build::stage_ext[goal])?;
+ path::push_ext(&p, build::stage_ext[goal])?;
};
- const output = match (path::peek(&buf)) {
+ const output = match (path::peek(&p)) {
case let s: str =>
yield s;
case void =>
diff --git a/cmd/hare/build/gather.ha b/cmd/hare/build/gather.ha
@@ -10,7 +10,7 @@ use strings;
export fn gather(ctx: *context, input: str) ([]module::module | error) = {
let mods: []module::module = [];
- path::set(&buf, input)!;
+ path::set(&pathbuf, input)!;
module::gather(&ctx.ctx, &mods, ["rt"])?;
if (ctx.test) {
module::gather(&ctx.ctx, &mods, ["test"])?;
@@ -19,10 +19,10 @@ export fn gather(ctx: *context, input: str) ([]module::module | error) = {
module::gather(&ctx.ctx, &mods, ["debug"])?;
};
const nsubmods = if (ctx.submods) {
- module::gather_submodules(&ctx.ctx, &mods, &buf)?;
+ module::gather_submodules(&ctx.ctx, &mods, &pathbuf)?;
} else 0z;
- ctx.top = match (module::gather(&ctx.ctx, &mods, &buf)) {
+ ctx.top = match (module::gather(&ctx.ctx, &mods, &pathbuf)) {
case let top: size =>
yield top;
case let e: module::error =>
diff --git a/cmd/hare/build/queue.ha b/cmd/hare/build/queue.ha
@@ -127,16 +127,16 @@ fn run_task(ctx: *context, jobs: *[]job, t: *task) (bool | error) = {
let out = get_cache(ctx, t.idx, t.kind)?;
defer free(out);
- path::set(&buf, out)?;
- let lock = path::push_ext(&buf, "lock")?;
+ path::set(&pathbuf, out)?;
+ let lock = path::push_ext(&pathbuf, "lock")?;
let lock = os::create(lock, 0o644, fs::flag::WRONLY)?;
if (!io::lock(lock, false, io::lockop::EXCLUSIVE)?) {
io::close(lock)?;
return false;
};
- path::set(&buf, out)?;
- let tmp = path::push_ext(&buf, "tmp")?;
+ path::set(&pathbuf, out)?;
+ let tmp = path::push_ext(&pathbuf, "tmp")?;
// TODO: use os::mkfile once that's supported on freebsd and openbsd
io::close(os::create(tmp, 0o644)?)?;
@@ -183,8 +183,8 @@ fn run_task(ctx: *context, jobs: *[]job, t: *task) (bool | error) = {
case let c: exec::command =>
yield c;
};
- path::set(&buf, out)?;
- let output = os::create(path::push_ext(&buf, "log")?, 0o644)?;
+ path::set(&pathbuf, out)?;
+ let output = os::create(path::push_ext(&pathbuf, "log")?, 0o644)?;
defer io::close(output)!;
exec::addfile(&cmd, os::stdout_file, output);
exec::addfile(&cmd, os::stderr_file, output);
@@ -216,9 +216,9 @@ fn await_task(ctx: *context, jobs: *[]job) (size | void | error) = {
let out = get_cache(ctx, t.idx, t.kind)?;
defer free(out);
- path::set(&buf, out)?;
+ path::set(&pathbuf, out)?;
- let output = os::open(path::push_ext(&buf, "log")?)?;
+ let output = os::open(path::push_ext(&pathbuf, "log")?)?;
defer io::close(output)!;
let output = io::drain(output)?;
defer free(output);
diff --git a/cmd/hare/build/util.ha b/cmd/hare/build/util.ha
@@ -17,7 +17,7 @@ use strings;
use time;
// for use as a scratch buffer
-let buf = path::buffer { ... };
+let pathbuf = path::path { ... };
fn get_deps(ctx: *context, t: *task) []str = {
let mod = ctx.mods[t.idx];
@@ -118,15 +118,15 @@ fn get_flags(ctx: *context, t: *task) ([]str | error) = {
append(flags, strings::dup("-m.main"))!;
};
append(flags, strings::dup("-M"))!;
- path::set(&buf, mod.path)?;
+ path::set(&pathbuf, mod.path)?;
for (let i = 0z; i < len(mod.ns); i += 1) {
- path::pop(&buf);
+ path::pop(&pathbuf);
};
- append(flags, strings::concat(path::string(&buf), "/"))!;
+ append(flags, strings::concat(path::string(&pathbuf), "/"))!;
- path::set(&buf, mod.path)?;
+ path::set(&pathbuf, mod.path)?;
let test = ctx.test && t.idx == ctx.top;
- test ||= path::trimprefix(&buf, os::getcwd()) is str && ctx.submods;
+ test ||= path::trimprefix(&pathbuf, os::getcwd()) is str && ctx.submods;
if (test) {
append(flags, strings::dup("-T"))!;
};
diff --git a/cmd/hare/cache.ha b/cmd/hare/cache.ha
@@ -36,8 +36,8 @@ fn cache(name: str, cmd: *getopt::command) (void | error) = {
};
os::mkdirs(cachedir, 0o755)!;
- let buf = path::init(cachedir)?;
- let sz = dirsize(&buf)?;
+ let p = path::init(cachedir)?;
+ let sz = dirsize(&p)?;
const suffix = ["B", "KiB", "MiB", "GiB", "TiB", "PiB", "EiB"];
let i = 0z;
for (i < len(suffix) - 1 && sz >= 1024; i += 1) {
@@ -46,19 +46,19 @@ fn cache(name: str, cmd: *getopt::command) (void | error) = {
fmt::printfln("{} ({} {})", cachedir, sz, suffix[i])?;
};
-fn dirsize(buf: *path::buffer) (size | error) = {
+fn dirsize(p: *path::path) (size | error) = {
let s = 0z;
- let it = os::iter(path::string(buf))?;
+ let it = os::iter(path::string(p))?;
defer fs::finish(it);
for (let d => fs::next(it)?) {
- path::push(buf, d.name)?;
- let stat = os::stat(path::string(buf))?;
+ path::push(p, d.name)?;
+ let stat = os::stat(path::string(p))?;
s += stat.sz;
if (fs::isdir(stat.mode)) {
- s += dirsize(buf)?;
+ s += dirsize(p)?;
};
- path::pop(buf);
+ path::pop(p);
};
return s;
};
diff --git a/cmd/hare/deps.ha b/cmd/hare/deps.ha
@@ -68,9 +68,9 @@ fn deps(name: str, cmd: *getopt::command) (void | error) = {
case let id: ast::ident =>
yield id;
case parse::error =>
- static let buf = path::buffer { ... };
- path::set(&buf, os::realpath(input)?)?;
- yield &buf;
+ static let p = path::path { ... };
+ path::set(&p, os::realpath(input)?)?;
+ yield &p;
};
if (submodules) {
diff --git a/cmd/haredoc/main.ha b/cmd/haredoc/main.ha
@@ -243,7 +243,7 @@ fn doc(name: str, cmd: *getopt::command) (void | error) = {
};
const rpath = match (path::init(modpath, "README")) {
- case let buf: path::buffer =>
+ case let buf: path::path =>
yield buf;
case let err: path::error =>
assert(err is path::too_long);
diff --git a/cmd/parsechk/main.ha b/cmd/parsechk/main.ha
@@ -12,29 +12,29 @@ use os;
use path;
export fn main() void = {
- let buf = path::init()!;
+ let p = path::init()!;
let status: int = os::status::SUCCESS;
- iter(&buf, &status);
+ iter(&p, &status);
os::exit(status);
};
-fn iter(buf: *path::buffer, status: *int) void = {
- let it = os::iter(path::string(buf))!;
+fn iter(p: *path::path, status: *int) void = {
+ let it = os::iter(path::string(p))!;
defer fs::finish(it);
for (let ent => fs::next(it)!) {
- path::push(buf, ent.name)!;
- defer path::pop(buf);
- const st = os::stat(path::string(buf))!;
+ path::push(p, ent.name)!;
+ defer path::pop(p);
+ const st = os::stat(path::string(p))!;
if (fs::isfile(st.mode)) {
- match (path::peek_ext(buf)) {
+ match (path::peek_ext(p)) {
case let s: str =>
if (s == "ha") {
- parse(path::string(buf), status);
+ parse(path::string(p), status);
};
case void => void;
};
} else if (fs::isdir(st.mode)) {
- iter(buf, status);
+ iter(p, status);
};
};
};
diff --git a/debug/dwarf/addr_to_line.ha b/debug/dwarf/addr_to_line.ha
@@ -82,7 +82,7 @@ export fn addr_to_line(
};
const file = &prog.head.files[state.file - 1];
- static let path = path::buffer { ... };
+ static let path = path::path { ... };
path::set(&path)!;
diff --git a/dirs/xdg.ha b/dirs/xdg.ha
@@ -9,11 +9,11 @@ use path;
use unix;
fn lookup(prog: str, var: str, default: str) str = {
- static let buf = path::buffer { ... };
- path::set(&buf)!;
+ static let p = path::path { ... };
+ path::set(&p)!;
match (os::getenv(var)) {
case let s: str =>
- const path = path::push(&buf, s, prog)!;
+ const path = path::push(&p, s, prog)!;
if (!path::abs(path)) {
yield;
};
@@ -30,7 +30,7 @@ fn lookup(prog: str, var: str, default: str) str = {
};
const home = os::getenv("HOME") as str;
- const path = path::set(&buf, home, default, prog)!;
+ const path = path::set(&p, home, default, prog)!;
match (os::mkdirs(path, 0o755)) {
case let err: fs::error =>
fmt::fatalf("Error creating {}: {}",
@@ -57,8 +57,8 @@ export fn cache(prog: str) str = lookup(prog, "XDG_CACHE_HOME", ".cache");
// statically allocated and will be overwritten on subsequent calls to any
// function in the dirs module.
export fn data(prog: str) str = {
- static let buf = path::buffer { ... };
- const fragment = path::set(&buf, ".local", "share")!;
+ static let p = path::path { ... };
+ const fragment = path::set(&p, ".local", "share")!;
return lookup(prog, "XDG_DATA_HOME", fragment);
};
@@ -67,8 +67,8 @@ export fn data(prog: str) str = {
// value is statically allocated and will be overwritten on subsequent calls to
// any function in the dirs module.
export fn state(prog: str) str = {
- static let buf = path::buffer { ... };
- const fragment = path::set(&buf, ".local", "state")!;
+ static let p = path::path { ... };
+ const fragment = path::set(&p, ".local", "state")!;
return lookup(prog, "XDG_STATE_HOME", fragment);
};
diff --git a/fs/fs.ha b/fs/fs.ha
@@ -338,9 +338,9 @@ export fn resolve(fs: *fs, path: str) str = {
case let f: *resolvefunc =>
return f(fs, path);
};
- static let buf = path::buffer { ... };
- path::set(&buf, path)!;
- return path::string(&buf);
+ static let p = path::path { ... };
+ path::set(&p, path)!;
+ return path::string(&p);
};
// Creates a new (hard) link at 'new' for the file at 'old'.
diff --git a/fs/util.ha b/fs/util.ha
@@ -105,7 +105,7 @@ export fn dirents_free(dirents: []dirent) void = {
// Removes a directory, and anything in it.
export fn rmdirall(fs: *fs, path: str) (void | error) = {
match (path::init(path)) {
- case let buf: path::buffer =>
+ case let buf: path::path =>
return rmdirall_path(fs, &buf);
case let err: path::error =>
assert(err is path::too_long);
@@ -113,32 +113,32 @@ export fn rmdirall(fs: *fs, path: str) (void | error) = {
};
};
-fn rmdirall_path(fs: *fs, buf: *path::buffer) (void | error) = {
- let it = iter(fs, path::string(buf))?;
+fn rmdirall_path(fs: *fs, p: *path::path) (void | error) = {
+ let it = iter(fs, path::string(p))?;
defer finish(it);
for (let ent => next(it)?) {
- path::push(buf, ent.name)!;
+ path::push(p, ent.name)!;
switch (ent.ftype & mode::DIR) {
case mode::DIR =>
- rmdirall_path(fs, buf)?;
+ rmdirall_path(fs, p)?;
case =>
- remove(fs, path::string(buf))?;
+ remove(fs, path::string(p))?;
};
- path::pop(buf);
+ path::pop(p);
};
- return rmdir(fs, path::string(buf));
+ return rmdir(fs, path::string(p));
};
// Canonicalizes a path in this filesystem by resolving all symlinks and
// collapsing any "." or ".." path components. The return value is statically
// allocated and will be overwritten on subsequent calls.
export fn realpath(fs: *fs, path: str) (str | error) = {
- static let res = path::buffer { ... };
+ static let res = path::path { ... };
path::set(&res)!;
- static let pathbuf = path::buffer { ... };
- path::set(&pathbuf, resolve(fs, path))!;
- const iter = path::iter(&pathbuf);
+ static let p = path::path { ... };
+ path::set(&p, resolve(fs, path))!;
+ const iter = path::iter(&p);
for (let item => path::nextiter(&iter)) {
const item = path::push(&res, item)!;
diff --git a/hare/lex/lex.ha b/hare/lex/lex.ha
@@ -863,8 +863,8 @@ export fn prevloc(lex: *lexer) location = {
};
export fn syntaxerr(loc: location, why: str) error = {
- static let buf = path::buffer{...};
- path::set(&buf, loc.path)!;
- loc.path = path::string(&buf);
+ static let p = path::path{...};
+ path::set(&p, loc.path)!;
+ loc.path = path::string(&p);
return (loc, why);
};
diff --git a/hare/module/cache.ha b/hare/module/cache.ha
@@ -8,6 +8,6 @@ use path;
// calls. An error is returned if the resulting path would be longer than
// [[path::MAX]].
export fn get_cache(harecache: str, modpath: str) (str | error) = {
- static let buf = path::buffer { ... };
- return path::set(&buf, harecache, modpath)?;
+ static let p = path::path { ... };
+ return path::set(&p, harecache, modpath)?;
};
diff --git a/hare/module/deps.ha b/hare/module/deps.ha
@@ -78,15 +78,15 @@ fn idcmp(a: const *opaque, b: const *opaque) int = {
// them if necessary. cachedir should be calculated with [[get_cache]],
// and srcset should be calculated with [[find]].
fn get_deps(cachedir: str, srcs: *srcset) ([]ast::ident | error) = {
- static let buf = path::buffer{...};
- path::set(&buf, cachedir, "deps")?;
- let rest = memio::fixed(buf.buf[buf.end..]);
- buf.end += format_tags(&rest, srcs.seentags)?;
- buf.end += memio::concat(&rest, ".txt")?;
+ static let p = path::path{...};
+ path::set(&p, cachedir, "deps")?;
+ let rest = memio::fixed(p.buf[p.end..]);
+ p.end += format_tags(&rest, srcs.seentags)?;
+ p.end += memio::concat(&rest, ".txt")?;
- let outofdate = outdated(path::string(&buf), srcs.ha, srcs.mtime);
+ let outofdate = outdated(path::string(&p), srcs.ha, srcs.mtime);
os::mkdirs(cachedir, 0o755)?;
- let depsfile = os::create(path::string(&buf), 0o644, fs::flag::RDWR)?;
+ let depsfile = os::create(path::string(&p), 0o644, fs::flag::RDWR)?;
defer io::close(depsfile)!;
io::lock(depsfile, true, io::lockop::EXCLUSIVE)?;
@@ -94,13 +94,13 @@ fn get_deps(cachedir: str, srcs: *srcset) ([]ast::ident | error) = {
if (outofdate) {
deps = parse_deps(srcs.ha...)?;
io::trunc(depsfile, 0)?;
- let out = bufio::init(depsfile, [], buf.buf);
+ let out = bufio::init(depsfile, [], p.buf);
for (let dep .. deps) {
unparse::ident(&out, dep)?;
fmt::fprintln(&out)?;
};
} else {
- let in = bufio::newscanner_static(depsfile, buf.buf);
+ let in = bufio::newscanner_static(depsfile, p.buf);
for (let s => bufio::scan_line(&in)?) {
append(deps, parse::identstr(s)?)!;
};
@@ -145,18 +145,18 @@ export fn gather_submodules(
defer ast::ident_free(id);
let buf = match (mod) {
- case let b: *path::buffer =>
+ case let b: *path::path =>
yield b;
case let m: ast::ident =>
append(id, m...)!;
- static let b = path::buffer { ... };
+ static let b = path::path { ... };
let res = find(ctx, id)?;
defer finish_srcset(&res.1);
path::set(&b, res.0)?;
yield &b;
};
- static let srcdir = path::buffer { ... };
+ static let srcdir = path::path { ... };
srcdir = *buf;
for (let i = 0z; i < len(id); i += 1) {
path::pop(&srcdir);
@@ -177,7 +177,7 @@ export fn gather_submodules(
export fn _gather_submodules(
ctx: *context,
out: *[]module,
- buf: *path::buffer,
+ buf: *path::path,
mod: *ast::ident,
recursive: bool = true,
) (size | error) = {
@@ -254,13 +254,13 @@ fn _gather(
append(out, module {
name = match (mod) {
- case let mod: *path::buffer =>
+ case let mod: *path::path =>
yield strings::dup(path::string(mod));
case let mod: ast::ident =>
yield unparse::identstr(mod);
},
ns = match (mod) {
- case let mod: *path::buffer =>
+ case let mod: *path::path =>
yield [];
case let mod: ast::ident =>
yield ast::ident_dup(mod);
diff --git a/hare/module/srcs.ha b/hare/module/srcs.ha
@@ -54,10 +54,10 @@ export fn finish_srcset(srcs: *srcset) void = {
// statically allocated and may be overwritten on subsequent calls.
export fn find(ctx: *context, loc: location) ((str, srcset) | error) = {
match (loc) {
- case let buf: *path::buffer =>
- match (path_find(ctx, buf)) {
+ case let p: *path::path =>
+ match (path_find(ctx, p)) {
case let s: srcset =>
- return (path::string(buf), s);
+ return (path::string(p), s);
case not_found =>
return attach(locstr(loc), not_found);
case let e: error =>
@@ -71,25 +71,25 @@ export fn find(ctx: *context, loc: location) ((str, srcset) | error) = {
continue;
};
- static let buf = path::buffer { ... };
- path::set(&buf, os::realpath(next as str)?)?;
+ static let p = path::path { ... };
+ path::set(&p, os::realpath(next as str)?)?;
for (let part .. ident) {
- path::push(&buf, part)?;
+ path::push(&p, part)?;
};
- match (path_find(ctx, &buf)) {
+ match (path_find(ctx, &p)) {
case let s: srcset =>
- return (path::string(&buf), s);
+ return (path::string(&p), s);
case not_found => void;
case let e: error =>
- return attach(strings::dup(path::string(&buf)), e);
+ return attach(strings::dup(path::string(&p)), e);
};
};
return attach(locstr(ident), not_found);
};
};
-fn path_find(ctx: *context, buf: *path::buffer) (srcset | error) = {
+fn path_find(ctx: *context, p: *path::path) (srcset | error) = {
// list of sources to return, with 3 extra fields prepended to allow
// quick lookup and comparison. each item is e.g.:
// ("basename", "ha", 2 (# of tags), ["path/-tag1/basename+tag2.ha"])
@@ -106,7 +106,7 @@ fn path_find(ctx: *context, buf: *path::buffer) (srcset | error) = {
let mtime = time::INSTANT_MIN;
let res = srcset { mtime = time::INSTANT_MIN, ... };
- _findsrcs(buf, ctx.tags, &srcs, &res, 0)?;
+ _findsrcs(p, ctx.tags, &srcs, &res, 0)?;
for (let i = 0z; i < len(srcs); i += 1) {
if (len(srcs[i].3) != 1) {
return alloc(srcs[i].3...)!: file_conflict;
@@ -129,9 +129,9 @@ fn path_find(ctx: *context, buf: *path::buffer) (srcset | error) = {
// valid. used to allow eg. shadowing foo::bar:: without accidentally
// shadowing foo::
if (len(res.ha) == 0) {
- path::push(buf, "README")?;
- defer path::pop(buf);
- if (!os::exists(path::string(buf))) {
+ path::push(p, "README")?;
+ defer path::pop(p);
+ if (!os::exists(path::string(p))) {
finish_srcset(&res);
return not_found;
};
@@ -182,13 +182,13 @@ fn path_find(ctx: *context, buf: *path::buffer) (srcset | error) = {
// one tag. in any case, the method used here is fast because it gets to stop
// searching as soon as it can
fn _findsrcs(
- buf: *path::buffer,
+ p: *path::path,
in_tags: []str,
srcs: *[](str, str, size, []str),
res: *srcset,
tagdepth: size,
) (void | error) = {
- const pathstr = path::string(buf);
+ const pathstr = path::string(p);
const stat = match (os::stat(pathstr)) {
case let stat: fs::filestat =>
yield stat;
@@ -206,7 +206,7 @@ fn _findsrcs(
};
if (fs::isfile(stat.mode)) {
- let ext = match (path::pop_ext(buf)) {
+ let ext = match (path::pop_ext(p)) {
case void =>
return;
case let ext: str =>
@@ -217,8 +217,8 @@ fn _findsrcs(
case =>
return;
};
- let filebytes = strings::toutf8(path::peek(buf) as str);
- path::push_ext(buf, ext)?;
+ let filebytes = strings::toutf8(path::peek(p) as str);
+ path::push_ext(p, ext)?;
let split = tagindex(filebytes);
let (base, tags) = (
@@ -230,7 +230,7 @@ fn _findsrcs(
case let tags: []tag =>
yield tags;
case let e: error =>
- return attach(strings::dup(path::string(buf)), e);
+ return attach(strings::dup(path::string(p)), e);
};
defer free(wanttags);
if (!seentags_compat(in_tags, wanttags, &res.seentags)) {
@@ -238,7 +238,7 @@ fn _findsrcs(
};
let ntags = tagdepth + len(wanttags);
- let bufstr = path::string(buf);
+ let pathstr = path::string(p);
for (let i = 0z; i < len(srcs); i += 1) {
if (srcs[i].0 == base && srcs[i].1 == ext) {
if (srcs[i].2 > ntags) {
@@ -249,7 +249,7 @@ fn _findsrcs(
strings::freeall(srcs[i].3);
srcs[i].3 = [];
};
- append(srcs[i].3, strings::dup(bufstr))!;
+ append(srcs[i].3, strings::dup(pathstr))!;
return;
};
};
@@ -258,7 +258,7 @@ fn _findsrcs(
strings::dup(base),
strings::dup(ext),
ntags,
- alloc([strings::dup(bufstr)])!,
+ alloc([strings::dup(pathstr)])!,
))!;
return;
};
@@ -279,8 +279,8 @@ fn _findsrcs(
defer fs::finish(iter);
for (let d => fs::next(iter)?) {
- path::push(buf, d.name)?;
- defer path::pop(buf);
+ path::push(p, d.name)?;
+ defer path::pop(p);
if (fs::isdir(d.ftype)) {
if (tagindex(strings::toutf8(d.name)) != 0) {
@@ -290,17 +290,17 @@ fn _findsrcs(
case let tags: []tag =>
yield tags;
case let e: error =>
- return attach(strings::dup(path::string(buf)), e);
+ return attach(strings::dup(path::string(p)), e);
};
defer free(wanttags);
if (!seentags_compat(in_tags, wanttags, &res.seentags)) {
continue;
};
- _findsrcs(buf, in_tags, srcs, res,
+ _findsrcs(p, in_tags, srcs, res,
tagdepth+len(wanttags))?;
} else if (fs::isfile(d.ftype)) {
- _findsrcs(buf, in_tags, srcs, res, tagdepth)?;
+ _findsrcs(p, in_tags, srcs, res, tagdepth)?;
};
};
};
diff --git a/hare/module/types.ha b/hare/module/types.ha
@@ -55,14 +55,14 @@ export type context = struct {
};
// The location of a module
-export type location = (*path::buffer | ast::ident);
+export type location = (*path::path | ast::ident);
// Returns a string representation of a [[location]]. The result must be freed
// by the caller.
export fn locstr(loc: location) str = {
match (loc) {
- case let buf: *path::buffer =>
- return strings::dup(path::string(buf));
+ case let p: *path::path =>
+ return strings::dup(path::string(p));
case let id: ast::ident =>
return unparse::identstr(id);
};
diff --git a/makefiles/freebsd.aarch64.mk b/makefiles/freebsd.aarch64.mk
@@ -105,7 +105,7 @@ $(HARECACHE)/memio.ssa: $(memio_ha) $(HARECACHE)/bytes.td $(HARECACHE)/encoding_
@printf 'HAREC\t%s\n' "$@"
@$(TDENV) $(HAREC) $(HARECFLAGS) -o $@ -t $(HARECACHE)/memio.td.tmp -N memio $(memio_ha)
-path_ha = path/+freebsd.ha path/buffer.ha path/error.ha path/ext_stack.ha path/iter.ha path/posix.ha path/prefix.ha path/stack.ha
+path_ha = path/+freebsd.ha path/error.ha path/ext_stack.ha path/iter.ha path/path.ha path/posix.ha path/prefix.ha path/stack.ha
$(HARECACHE)/path.ssa: $(path_ha) $(HARECACHE)/bytes.td $(HARECACHE)/strings.td
@mkdir -p -- "$(HARECACHE)"
@printf 'HAREC\t%s\n' "$@"
diff --git a/makefiles/freebsd.riscv64.mk b/makefiles/freebsd.riscv64.mk
@@ -105,7 +105,7 @@ $(HARECACHE)/memio.ssa: $(memio_ha) $(HARECACHE)/bytes.td $(HARECACHE)/encoding_
@printf 'HAREC\t%s\n' "$@"
@$(TDENV) $(HAREC) $(HARECFLAGS) -o $@ -t $(HARECACHE)/memio.td.tmp -N memio $(memio_ha)
-path_ha = path/+freebsd.ha path/buffer.ha path/error.ha path/ext_stack.ha path/iter.ha path/posix.ha path/prefix.ha path/stack.ha
+path_ha = path/+freebsd.ha path/error.ha path/ext_stack.ha path/iter.ha path/path.ha path/posix.ha path/prefix.ha path/stack.ha
$(HARECACHE)/path.ssa: $(path_ha) $(HARECACHE)/bytes.td $(HARECACHE)/strings.td
@mkdir -p -- "$(HARECACHE)"
@printf 'HAREC\t%s\n' "$@"
diff --git a/makefiles/freebsd.x86_64.mk b/makefiles/freebsd.x86_64.mk
@@ -105,7 +105,7 @@ $(HARECACHE)/memio.ssa: $(memio_ha) $(HARECACHE)/bytes.td $(HARECACHE)/encoding_
@printf 'HAREC\t%s\n' "$@"
@$(TDENV) $(HAREC) $(HARECFLAGS) -o $@ -t $(HARECACHE)/memio.td.tmp -N memio $(memio_ha)
-path_ha = path/+freebsd.ha path/buffer.ha path/error.ha path/ext_stack.ha path/iter.ha path/posix.ha path/prefix.ha path/stack.ha
+path_ha = path/+freebsd.ha path/error.ha path/ext_stack.ha path/iter.ha path/path.ha path/posix.ha path/prefix.ha path/stack.ha
$(HARECACHE)/path.ssa: $(path_ha) $(HARECACHE)/bytes.td $(HARECACHE)/strings.td
@mkdir -p -- "$(HARECACHE)"
@printf 'HAREC\t%s\n' "$@"
diff --git a/makefiles/linux.aarch64.mk b/makefiles/linux.aarch64.mk
@@ -105,7 +105,7 @@ $(HARECACHE)/memio.ssa: $(memio_ha) $(HARECACHE)/bytes.td $(HARECACHE)/encoding_
@printf 'HAREC\t%s\n' "$@"
@$(TDENV) $(HAREC) $(HARECFLAGS) -o $@ -t $(HARECACHE)/memio.td.tmp -N memio $(memio_ha)
-path_ha = path/+linux.ha path/buffer.ha path/error.ha path/ext_stack.ha path/iter.ha path/posix.ha path/prefix.ha path/stack.ha
+path_ha = path/+linux.ha path/error.ha path/ext_stack.ha path/iter.ha path/path.ha path/posix.ha path/prefix.ha path/stack.ha
$(HARECACHE)/path.ssa: $(path_ha) $(HARECACHE)/bytes.td $(HARECACHE)/strings.td
@mkdir -p -- "$(HARECACHE)"
@printf 'HAREC\t%s\n' "$@"
diff --git a/makefiles/linux.riscv64.mk b/makefiles/linux.riscv64.mk
@@ -105,7 +105,7 @@ $(HARECACHE)/memio.ssa: $(memio_ha) $(HARECACHE)/bytes.td $(HARECACHE)/encoding_
@printf 'HAREC\t%s\n' "$@"
@$(TDENV) $(HAREC) $(HARECFLAGS) -o $@ -t $(HARECACHE)/memio.td.tmp -N memio $(memio_ha)
-path_ha = path/+linux.ha path/buffer.ha path/error.ha path/ext_stack.ha path/iter.ha path/posix.ha path/prefix.ha path/stack.ha
+path_ha = path/+linux.ha path/error.ha path/ext_stack.ha path/iter.ha path/path.ha path/posix.ha path/prefix.ha path/stack.ha
$(HARECACHE)/path.ssa: $(path_ha) $(HARECACHE)/bytes.td $(HARECACHE)/strings.td
@mkdir -p -- "$(HARECACHE)"
@printf 'HAREC\t%s\n' "$@"
diff --git a/makefiles/linux.x86_64.mk b/makefiles/linux.x86_64.mk
@@ -105,7 +105,7 @@ $(HARECACHE)/memio.ssa: $(memio_ha) $(HARECACHE)/bytes.td $(HARECACHE)/encoding_
@printf 'HAREC\t%s\n' "$@"
@$(TDENV) $(HAREC) $(HARECFLAGS) -o $@ -t $(HARECACHE)/memio.td.tmp -N memio $(memio_ha)
-path_ha = path/+linux.ha path/buffer.ha path/error.ha path/ext_stack.ha path/iter.ha path/posix.ha path/prefix.ha path/stack.ha
+path_ha = path/+linux.ha path/error.ha path/ext_stack.ha path/iter.ha path/path.ha path/posix.ha path/prefix.ha path/stack.ha
$(HARECACHE)/path.ssa: $(path_ha) $(HARECACHE)/bytes.td $(HARECACHE)/strings.td
@mkdir -p -- "$(HARECACHE)"
@printf 'HAREC\t%s\n' "$@"
diff --git a/makefiles/openbsd.aarch64.mk b/makefiles/openbsd.aarch64.mk
@@ -105,7 +105,7 @@ $(HARECACHE)/memio.ssa: $(memio_ha) $(HARECACHE)/bytes.td $(HARECACHE)/encoding_
@printf 'HAREC\t%s\n' "$@"
@$(TDENV) $(HAREC) $(HARECFLAGS) -o $@ -t $(HARECACHE)/memio.td.tmp -N memio $(memio_ha)
-path_ha = path/+openbsd.ha path/buffer.ha path/error.ha path/ext_stack.ha path/iter.ha path/posix.ha path/prefix.ha path/stack.ha
+path_ha = path/+openbsd.ha path/error.ha path/ext_stack.ha path/iter.ha path/path.ha path/posix.ha path/prefix.ha path/stack.ha
$(HARECACHE)/path.ssa: $(path_ha) $(HARECACHE)/bytes.td $(HARECACHE)/rt.td $(HARECACHE)/strings.td
@mkdir -p -- "$(HARECACHE)"
@printf 'HAREC\t%s\n' "$@"
diff --git a/makefiles/openbsd.riscv64.mk b/makefiles/openbsd.riscv64.mk
@@ -105,7 +105,7 @@ $(HARECACHE)/memio.ssa: $(memio_ha) $(HARECACHE)/bytes.td $(HARECACHE)/encoding_
@printf 'HAREC\t%s\n' "$@"
@$(TDENV) $(HAREC) $(HARECFLAGS) -o $@ -t $(HARECACHE)/memio.td.tmp -N memio $(memio_ha)
-path_ha = path/+openbsd.ha path/buffer.ha path/error.ha path/ext_stack.ha path/iter.ha path/posix.ha path/prefix.ha path/stack.ha
+path_ha = path/+openbsd.ha path/error.ha path/ext_stack.ha path/iter.ha path/path.ha path/posix.ha path/prefix.ha path/stack.ha
$(HARECACHE)/path.ssa: $(path_ha) $(HARECACHE)/bytes.td $(HARECACHE)/rt.td $(HARECACHE)/strings.td
@mkdir -p -- "$(HARECACHE)"
@printf 'HAREC\t%s\n' "$@"
diff --git a/makefiles/openbsd.x86_64.mk b/makefiles/openbsd.x86_64.mk
@@ -105,7 +105,7 @@ $(HARECACHE)/memio.ssa: $(memio_ha) $(HARECACHE)/bytes.td $(HARECACHE)/encoding_
@printf 'HAREC\t%s\n' "$@"
@$(TDENV) $(HAREC) $(HARECFLAGS) -o $@ -t $(HARECACHE)/memio.td.tmp -N memio $(memio_ha)
-path_ha = path/+openbsd.ha path/buffer.ha path/error.ha path/ext_stack.ha path/iter.ha path/posix.ha path/prefix.ha path/stack.ha
+path_ha = path/+openbsd.ha path/error.ha path/ext_stack.ha path/iter.ha path/path.ha path/posix.ha path/prefix.ha path/stack.ha
$(HARECACHE)/path.ssa: $(path_ha) $(HARECACHE)/bytes.td $(HARECACHE)/rt.td $(HARECACHE)/strings.td
@mkdir -p -- "$(HARECACHE)"
@printf 'HAREC\t%s\n' "$@"
diff --git a/os/+freebsd/dirfdfs.ha b/os/+freebsd/dirfdfs.ha
@@ -410,9 +410,9 @@ fn fs_resolve(fs: *fs::fs, path: str) str = {
return path;
};
// XXX: This approach might not be right if this fs is based on a subdir
- static let buf = path::buffer { ... };
- path::set(&buf, getcwd(), path)!;
- return path::string(&buf);
+ static let p = path::path { ... };
+ path::set(&p, getcwd(), path)!;
+ return path::string(&p);
};
fn fs_close(fs: *fs::fs) void = {
diff --git a/os/+linux/dirfdfs.ha b/os/+linux/dirfdfs.ha
@@ -397,9 +397,9 @@ fn fs_resolve(fs: *fs::fs, path: str) str = {
return path;
};
// XXX: This approach might not be right if this fs is based on a subdir
- static let buf = path::buffer { ... };
- path::set(&buf, getcwd(), path)!;
- return path::string(&buf);
+ static let p = path::path { ... };
+ path::set(&p, getcwd(), path)!;
+ return path::string(&p);
};
fn fs_link(fs: *fs::fs, old: str, new: str) (void | fs::error) = {
diff --git a/os/+linux/shm.ha b/os/+linux/shm.ha
@@ -18,7 +18,7 @@ fn shm_path(name: str) (str | fs::error) = {
if (name == "." || name == "..") {
return errors::invalid;
};
- static let buf = path::buffer { ... };
+ static let buf = path::path { ... };
path::set(&buf, SHM_PATH, name)!;
return path::string(&buf);
};
diff --git a/os/+openbsd/dirfdfs.ha b/os/+openbsd/dirfdfs.ha
@@ -428,21 +428,21 @@ fn fs_fchtimes(fd: io::file, atime: (time::instant | void),
// TODO: cannot handle errors, i.e. path too long or cannot resolve.
fn fs_resolve(fs: *fs::fs, path: str) str = {
let fs = fs: *os_filesystem;
- static let buf = path::buffer { ... };
+ static let p = path::path { ... };
if (path::abs(path)) {
return path;
};
if (fs.dirfd == rt::AT_FDCWD) {
- path::set(&buf, getcwd(), path)!;
+ path::set(&p, getcwd(), path)!;
} else {
// XXX: this is the best we can for now. we should probably
// return an error
- path::set(&buf, "<unknown>", path)!;
+ path::set(&p, "<unknown>", path)!;
};
- return path::string(&buf);
+ return path::string(&p);
};
fn fs_link(fs: *fs::fs, old: str, new: str) (void | fs::error) = {
diff --git a/os/exec/+openbsd/platform_cmd.ha b/os/exec/+openbsd/platform_cmd.ha
@@ -3,4 +3,4 @@
use path;
-export type platform_cmd = path::buffer;
+export type platform_cmd = path::path;
diff --git a/os/exec/cmd.ha b/os/exec/cmd.ha
@@ -167,8 +167,8 @@ export fn chdir(cmd: *command, dir: str) void = {
// Similar to [[lookup]] but TOCTOU-proof
fn lookup_open(name: str) (platform_cmd | void | error) = {
- static let buf = path::buffer { ... };
- path::set(&buf)!;
+ static let pbuf = path::path { ... };
+ path::set(&pbuf)!;
// Try to open file directly
if (strings::contains(name, "/")) {
@@ -190,9 +190,9 @@ fn lookup_open(name: str) (platform_cmd | void | error) = {
let tok = strings::tokenize(path, ":");
for (let item => strings::next_token(&tok)) {
- path::set(&buf, item, name)!;
+ path::set(&pbuf, item, name)!;
- match (open(path::string(&buf))) {
+ match (open(path::string(&pbuf))) {
case (errors::noaccess | errors::noentry) =>
continue;
case let err: error =>
@@ -209,8 +209,8 @@ fn lookup_open(name: str) (platform_cmd | void | error) = {
// The use of this function is lightly discouraged if [[cmd]] is suitable;
// otherwise you may have a TOCTOU issue.
export fn lookup(name: str) (str | void) = {
- static let buf = path::buffer { ... };
- path::set(&buf)!;
+ static let pbuf = path::path { ... };
+ path::set(&pbuf)!;
// Try to open file directly
if (strings::contains(name, "/")) {
@@ -232,12 +232,12 @@ export fn lookup(name: str) (str | void) = {
let tok = strings::tokenize(path, ":");
for (let item => strings::next_token(&tok)) {
- path::set(&buf, item, name)!;
+ path::set(&pbuf, item, name)!;
- match (os::access(path::string(&buf), os::amode::X_OK)) {
+ match (os::access(path::string(&pbuf), os::amode::X_OK)) {
case let exec: bool =>
if (exec) {
- return path::string(&buf);
+ return path::string(&pbuf);
};
case => void; // Keep looking
};
diff --git a/path/README b/path/README
@@ -4,7 +4,7 @@ Note that Hare expects paths to be valid UTF-8 strings. If you require the use
of non-UTF-8 paths (ideally for only as long as it takes to delete or rename
those files), see the low-level functions available from [[rt::]].
-Use of the [[buffer]] type is recommended for efficient and consistent
+Use of the [[path]] type is recommended for efficient and consistent
manipulation of filesystem paths. The path will always be
normalized, which is to say that it will not include any of the following:
@@ -19,19 +19,19 @@ Different [[fs::fs]] implementations may have different rules for normalizing
paths. For use-cases in which this is relevant, [[fs::resolve]] should be used
instead.
-The buffer object includes an array of length [[MAX]], which can be somewhat
+The [[path]] object includes an array of length [[MAX]], which can be somewhat
large; on Linux it's 4095 bytes. You can allocate this on the stack in most
cases, but you may prefer to allocate it elsewhere depending on your needs.
-Functions in this module return [[too_long]] if the buffer's capacity would be
-exceeded.
+Functions in this module return [[too_long]] if the path's buffer capacity would
+be exceeded.
// Stack allocated
- let buf = path::init()!;
+ let p = path::init()!;
// Statically allocated
- static let buf = path::buffer { ... };
- path::set(&buf)!;
+ static let p = path::path { ... };
+ path::set(&p)!;
// Heap allocated
- let buf = alloc(path::init()!);
- defer free(buf);
+ let p = alloc(path::init()!);
+ defer free(p);
diff --git a/path/buffer.ha b/path/buffer.ha
@@ -1,64 +0,0 @@
-// SPDX-License-Identifier: MPL-2.0
-// (c) Hare authors <https://harelang.org>
-
-use strings;
-
-export type buffer = struct {
- buf: [MAX]u8,
- end: size,
-};
-
-// Initializes a new path buffer.
-export fn init(items: str...) (buffer | error) = {
- let buf = buffer { ... };
- push(&buf, items...)?;
- return buf;
-};
-
-// Sets the value of a path buffer to a list of components, overwriting any
-// previous value. Returns the new string value of the path.
-export fn set(buf: *buffer, items: str...) (str | error) = {
- buf.end = 0;
- return push(buf, items...);
-};
-
-// Returns the current path stored in this buffer.
-// The return value is borrowed from the buffer. Use [[strings::dup]] to
-// extend the lifetime of the string.
-export fn string(buf: *buffer) str = {
- if (buf.end == 0) return ".";
- return strings::fromutf8_unsafe(buf.buf[..buf.end]);
-};
-
-// Check if a path is an absolute path.
-export fn abs(path: (*buffer | str)) bool = match (path) {
-case let path: str => return strings::hasprefix(path, sepstr);
-case let buf: *buffer => return 0 < buf.end && buf.buf[0] == SEP;
-};
-
-// Check if a path is the root directory.
-export fn isroot(path: (*buffer | str)) bool = match (path) {
-case let path: str => return path == sepstr;
-case let buf: *buffer => return buf.end == 1 && buf.buf[0] == SEP;
-};
-
-// Replaces all instances of '/' in a string with [[SEP]]. The result is
-// statically-allocated.
-export fn local(path: str) str = {
- static let buf: [MAX]u8 = [0...];
- return _local(path, &buf);
-};
-
-fn _local(path: str, buf: *[MAX]u8) str = {
- let buf = buf[..0];
- const bytes = strings::toutf8(path);
-
- for (let byte .. bytes) {
- if (byte == '/') {
- static append(buf, SEP)!;
- } else {
- static append(buf, byte)!;
- };
- };
- return strings::fromutf8(buf)!;
-};
diff --git a/path/ext_stack.ha b/path/ext_stack.ha
@@ -7,130 +7,131 @@ use strings;
// Add extensions onto the end of the final path segment. The separating '.'
// will be inserted for you. If the final path segment consists entirely of dots
// or the path is root, this function will return [[cant_extend]].
-export fn push_ext(buf: *buffer, exts: str...) (str | error) = {
- match (peek(buf)) {
+export fn push_ext(p: *path, exts: str...) (str | error) = {
+ match (peek(p)) {
case void => return cant_extend;
case let s: str => if (strings::ltrim(s, '.') == "") return cant_extend;
};
for (let ext .. exts) {
- const newend = buf.end + 1 + len(ext);
+ const newend = p.end + 1 + len(ext);
if (MAX < newend) return too_long;
- buf.buf[buf.end] = '.';
- buf.buf[buf.end+1..newend] = strings::toutf8(ext);
- buf.end = newend;
+ p.buf[p.end] = '.';
+ p.buf[p.end+1..newend] = strings::toutf8(ext);
+ p.end = newend;
};
- return string(buf);
+ return string(p);
};
-// Remove and return the final extension in a path. The result will not
-// include the leading '.'. The result is borrowed from the buffer. Leading dots
-// will be ignored when looking for extensions, such that ".ssh" isn't
-// considered to have any extensions.
-export fn pop_ext(buf: *buffer) (str | void) = {
- const ext = split_ext(buf);
- buf.end = ext.0;
+// Remove and return the final extension in a [[path]]. The result will not
+// include the leading '.'. The result is borrowed from the path's buffer.
+// Leading dots will be ignored when looking for extensions, such that ".ssh"
+// isn't considered to have any extensions.
+export fn pop_ext(p: *path) (str | void) = {
+ const ext = split_ext(p);
+ p.end = ext.0;
return ext.1;
};
-// Examine the final extension in a path. The result will not
-// include the leading '.'. The result is borrowed from the buffer. Leading dots
-// will be ignored when looking for extensions, such that ".ssh" isn't
-// considered to have any extensions.
-export fn peek_ext(buf: *buffer) (str | void) = split_ext(buf).1;
+// Examine the final extension in a [[path]]. The result will not
+// include the leading '.'. The result is borrowed from the path's buffer.
+// Leading dots will be ignored when looking for extensions, such that ".ssh"
+// isn't considered to have any extensions.
+export fn peek_ext(p: *path) (str | void) = split_ext(p).1;
// helper function, returns (end of non-extension, extension string)
-fn split_ext(buf: *buffer) (size, (str | void)) = match (peek(buf)) {
-case void => return (buf.end, void);
+fn split_ext(p: *path) (size, (str | void)) = match (peek(p)) {
+case void => return (p.end, void);
case let s: str =>
const bs = strings::toutf8(s);
bs = bytes::ltrim(bs, '.');
match (bytes::rindex(bs, '.')) {
- case void => return (buf.end, void);
+ case void => return (p.end, void);
case let i: size =>
- return (buf.end - len(bs) + i, strings::fromutf8_unsafe(bs[i+1..]));
+ return (p.end - len(bs) + i, strings::fromutf8_unsafe(bs[i+1..]));
};
};
// Remove and return all the extensions in a path. The result will not
// include the leading '.', but will include separating dots. Leading dots
// will be ignored when looking for extensions, such that ".ssh" isn't
-// considered to have any extensions. The result is borrowed from the buffer.
-export fn pop_exts(buf: *buffer) (str | void) = {
- const ext = split_exts(buf);
- buf.end = ext.0;
+// considered to have any extensions. The result is borrowed from the path's
+// buffer.
+export fn pop_exts(p: *path) (str | void) = {
+ const ext = split_exts(p);
+ p.end = ext.0;
return ext.1;
};
// Examine all the extensions in a path. The result will not include the
// leading '.', but will include separating dots. Leading dots will
// be ignored when looking for extensions, such that ".ssh" isn't considered
-// to have any extensions. The result is borrowed from the buffer.
-export fn peek_exts(buf: *buffer) (str | void) = split_exts(buf).1;
+// to have any extensions. The result is borrowed from the path's buffer.
+export fn peek_exts(p: *path) (str | void) = split_exts(p).1;
// helper function, returns (end of non-extension, extension string)
-fn split_exts(buf: *buffer) (size, (str | void)) = match (peek(buf)) {
-case void => return (buf.end, void);
+fn split_exts(p: *path) (size, (str | void)) = match (peek(p)) {
+case void => return (p.end, void);
case let s: str =>
const bs = strings::toutf8(s);
bs = bytes::ltrim(bs, '.');
match (bytes::index(bs, '.')) {
- case void => return (buf.end, void);
+ case void => return (p.end, void);
case let i: size =>
- return (buf.end - len(bs) + i, strings::fromutf8_unsafe(bs[i+1..]));
+ return (p.end - len(bs) + i, strings::fromutf8_unsafe(bs[i+1..]));
};
};
@test fn ext() void = {
// push_ext
- let buf = init()!;
- assert(push_ext(&buf, "bash") is cant_extend);
- set(&buf, sepstr)!;
- assert(push_ext(&buf, "bash") is cant_extend);
- set(&buf, "....")!;
- assert(push_ext(&buf, "bash") is cant_extend);
- set(&buf, "bashrc")!;
- assert(push_ext(&buf, "bash") as str == "bashrc.bash");
- set(&buf, ".bashrc")!;
- assert(push_ext(&buf, "bash") as str == ".bashrc.bash");
+ let p = init()!;
+ assert(push_ext(&p, "bash") is cant_extend);
+ set(&p, sepstr)!;
+ assert(push_ext(&p, "bash") is cant_extend);
+ set(&p, "....")!;
+ assert(push_ext(&p, "bash") is cant_extend);
+ set(&p, "bashrc")!;
+ assert(push_ext(&p, "bash") as str == "bashrc.bash");
+ set(&p, ".bashrc")!;
+ assert(push_ext(&p, "bash") as str == ".bashrc.bash");
// pop_ext
- set(&buf)!;
- assert(pop_ext(&buf) is void);
- set(&buf, "..")!;
- assert(pop_ext(&buf) is void);
- set(&buf, sepstr)!;
- assert(pop_ext(&buf) is void);
+ set(&p)!;
+ assert(pop_ext(&p) is void);
+ set(&p, "..")!;
+ assert(pop_ext(&p) is void);
+ set(&p, sepstr)!;
+ assert(pop_ext(&p) is void);
- set(&buf, "index.html.tmpl")!;
- assert(pop_ext(&buf) as str == "tmpl");
- assert(string(&buf) == "index.html");
- assert(pop_ext(&buf) as str == "html");
- assert(string(&buf) == "index");
- assert(pop_ext(&buf) is void);
- assert(string(&buf) == "index");
+ set(&p, "index.html.tmpl")!;
+ assert(pop_ext(&p) as str == "tmpl");
+ assert(string(&p) == "index.html");
+ assert(pop_ext(&p) as str == "html");
+ assert(string(&p) == "index");
+ assert(pop_ext(&p) is void);
+ assert(string(&p) == "index");
- set(&buf, ".secret.tar.gz")!;
- assert(pop_ext(&buf) as str == "gz");
- assert(string(&buf) == ".secret.tar");
- assert(pop_ext(&buf) as str == "tar");
- assert(string(&buf) == ".secret");
- assert(pop_ext(&buf) is void);
- assert(string(&buf) == ".secret");
+ set(&p, ".secret.tar.gz")!;
+ assert(pop_ext(&p) as str == "gz");
+ assert(string(&p) == ".secret.tar");
+ assert(pop_ext(&p) as str == "tar");
+ assert(string(&p) == ".secret");
+ assert(pop_ext(&p) is void);
+ assert(string(&p) == ".secret");
- set(&buf, "..ext")!;
- assert(pop_ext(&buf) is void);
- assert(string(&buf) == "..ext");
+ set(&p, "..ext")!;
+ assert(pop_ext(&p) is void);
+ assert(string(&p) == "..ext");
// pop_exts
- set(&buf, "index.html.tmpl")!;
- assert(pop_exts(&buf) as str == "html.tmpl");
- assert(string(&buf) == "index");
- assert(pop_exts(&buf) is void);
- assert(string(&buf) == "index");
+ set(&p, "index.html.tmpl")!;
+ assert(pop_exts(&p) as str == "html.tmpl");
+ assert(string(&p) == "index");
+ assert(pop_exts(&p) is void);
+ assert(string(&p) == "index");
- set(&buf, ".secret.tar.gz")!;
- assert(pop_exts(&buf) as str == "tar.gz");
- assert(string(&buf) == ".secret");
- assert(pop_ext(&buf) is void);
- assert(string(&buf) == ".secret");
+ set(&p, ".secret.tar.gz")!;
+ assert(pop_exts(&p) as str == "tar.gz");
+ assert(string(&p) == ".secret");
+ assert(pop_ext(&p) is void);
+ assert(string(&p) == ".secret");
};
diff --git a/path/iter.ha b/path/iter.ha
@@ -12,16 +12,16 @@ export type iterator = struct {
// Returns an [[iterator]] which yields each component of a path, moving down
// through child dirs. If the path is absolute, the first component will be
// the root. The iterator can be copied to save its state.
-export fn iter(buf: *buffer) iterator = iterator {
- cur = buf.buf[..buf.end],
+export fn iter(p: *path) iterator = iterator {
+ cur = p.buf[..p.end],
reverse = false,
};
// Returns an [[iterator]] which yields each component of a path, moving up
// through parent dirs. If the path is absolute, the last component will be
// the root. The iterator can be copied to save its state.
-export fn riter(buf: *buffer) iterator = iterator {
- cur = buf.buf[..buf.end],
+export fn riter(p: *path) iterator = iterator {
+ cur = p.buf[..p.end],
reverse = true,
};
@@ -74,45 +74,45 @@ fn split_iter(it: *iterator) ([]u8, []u8) = {
export fn iterrem(it: *iterator) str = strings::fromutf8_unsafe(it.cur);
@test fn iter() void = {
- const buf = init(local("/foo/bar/baz"))!;
- let i = iter(&buf);
+ const p = init(local("/foo/bar/baz"))!;
+ let i = iter(&p);
assert(nextiter(&i) as str == local("/"));
assert(nextiter(&i) as str == "foo");
assert(nextiter(&i) as str == "bar");
assert(nextiter(&i) as str == "baz");
assert(nextiter(&i) is done);
- i = riter(&buf);
+ i = riter(&p);
assert(nextiter(&i) as str == "baz");
assert(nextiter(&i) as str == "bar");
assert(nextiter(&i) as str == "foo");
assert(nextiter(&i) as str == local("/"));
assert(nextiter(&i) is done);
- set(&buf, local("foo/bar/baz"))!;
- i = iter(&buf);
+ set(&p, local("foo/bar/baz"))!;
+ i = iter(&p);
assert(nextiter(&i) as str == "foo");
assert(nextiter(&i) as str == "bar");
assert(nextiter(&i) as str == "baz");
assert(nextiter(&i) is done);
- i = riter(&buf);
+ i = riter(&p);
assert(nextiter(&i) as str == "baz");
assert(nextiter(&i) as str == "bar");
assert(nextiter(&i) as str == "foo");
assert(nextiter(&i) is done);
- set(&buf, "foo")!;
- i = iter(&buf);
+ set(&p, "foo")!;
+ i = iter(&p);
assert(nextiter(&i) as str == "foo");
assert(nextiter(&i) is done);
- i = riter(&buf);
+ i = riter(&p);
assert(nextiter(&i) as str == "foo");
assert(nextiter(&i) is done);
- set(&buf, local("/"))!;
- i = iter(&buf);
+ set(&p, local("/"))!;
+ i = iter(&p);
assert(nextiter(&i) as str == local("/"));
assert(nextiter(&i) is done);
- i = riter(&buf);
+ i = riter(&p);
assert(nextiter(&i) as str == local("/"));
assert(nextiter(&i) is done);
};
diff --git a/path/path.ha b/path/path.ha
@@ -0,0 +1,64 @@
+// SPDX-License-Identifier: MPL-2.0
+// (c) Hare authors <https://harelang.org>
+
+use bytes;
+use strings;
+
+export type path = struct {
+ buf: [MAX]u8,
+ end: size,
+};
+
+// Initializes a new [[path]]
+export fn init(items: str...) (path | error) = {
+ let p = path { ... };
+ push(&p, items...)?;
+ return p;
+};
+
+// Sets the value of a [[path]] to a list of components, overwriting any
+// previous value. Returns the new string value of the path.
+export fn set(p: *path, items: str...) (str | error) = {
+ p.end = 0;
+ return push(p, items...);
+};
+
+// Returns the current path stored in this [[path]]'s buffer
+// The return value is borrowed from the [[path]].
+// Use [[strings::dup]] to extend the lifetime of the string.
+export fn string(p: *path) str = {
+ if (p.end == 0) return ".";
+ return strings::fromutf8_unsafe(p.buf[..p.end]);
+};
+
+// Checks if a [[path]] is an absolute path.
+export fn abs(p: (*path | str)) bool = match (p) {
+case let s: str => return strings::hasprefix(s, sepstr);
+case let p: *path => return 0 < p.end && p.buf[0] == SEP;
+};
+
+// Checks if a [[path]] is the root directory.
+export fn isroot(p: (*path | str)) bool = match (p) {
+case let s: str => return s == sepstr;
+case let p: *path => return p.end == 1 && p.buf[0] == SEP;
+};
+
+// Replaces all instances of '/' in a string with [[SEP]]. The result is
+// statically-allocated.
+export fn local(path: str) str = {
+ static let buf: [MAX]u8 = [0...];
+ return _local(path, &buf);
+};
+
+fn _local(path: str, buf: *[MAX]u8) str = {
+ let buf = buf[..0];
+ const path = strings::toutf8(path);
+ for (let i = 0z; i < len(path); i += 1) {
+ if (path[i] == '/') {
+ static append(buf, SEP)!;
+ } else {
+ static append(buf, path[i])!;
+ };
+ };
+ return strings::fromutf8(buf)!;
+};
diff --git a/path/prefix.ha b/path/prefix.ha
@@ -4,47 +4,47 @@
use bytes;
use strings;
-// Add a prefix to a buffer. The buffer will be modified, and it will
-// remain normalized, so any ".." components in the original buffer may be
+// Add a prefix to a [[path]]. The [[path]] will be modified, and it will
+// remain normalized, so any ".." components in the original [[path]] may be
// collapsed.
-export fn prepend(buf: *buffer, prefix: str...) (str | error) = {
- static let tmp = buffer { ... };
- tmp = *buf;
- set(buf, prefix...)?;
- return push(buf, string(&tmp));
+export fn prepend(p: *path, prefix: str...) (str | error) = {
+ static let tmp = path { ... };
+ tmp = *p;
+ set(p, prefix...)?;
+ return push(p, string(&tmp));
};
-// Returns a buffer without a prefix. The prefix is normalized before
+// Returns a [[path]] without a prefix. The prefix is normalized before
// processing, and this function will return [[too_long]] if the prefix is
// longer than [[MAX]]. If the prefix is not present, returns [[not_prefix]].
// The resulting path will always be relative.
//
-// This function does not modify the buffer. See [[popprefix]].
-export fn trimprefix(buf: *buffer, prefix: str) (str | error) = {
- const start = splitprefix(buf, prefix)?;
- if (start == buf.end) return ".";
- return strings::fromutf8_unsafe(buf.buf[start..buf.end]);
+// This function does not modify the [[path]]. See [[popprefix]].
+export fn trimprefix(p: *path, prefix: str) (str | error) = {
+ const start = splitprefix(p, prefix)?;
+ if (start == p.end) return ".";
+ return strings::fromutf8_unsafe(p.buf[start..p.end]);
};
-// Equivalent to [[trimprefix]], but modifies the buffer in the process.
-export fn popprefix(buf: *buffer, prefix: str) (str | error) = {
- const start = splitprefix(buf, prefix)?;
- static delete(buf.buf[..][..start]);
- buf.end -= start;
- return string(buf);
+// Equivalent to [[trimprefix]], but modifies the [[path]] in the process.
+export fn popprefix(p: *path, prefix: str) (str | error) = {
+ const start = splitprefix(p, prefix)?;
+ static delete(p.buf[..][..start]);
+ p.end -= start;
+ return string(p);
};
// helper function for trimprefix and popprefix, returns the new
-// start of the buffer, or an error.
-fn splitprefix(buf: *buffer, prefix: str) (size | error) = {
+// start of the [[path]], or an error.
+fn splitprefix(p: *path, prefix: str) (size | error) = {
let pref = init(prefix)?;
if (pref.end == 0) {
- if (abs(buf)) return not_prefix;
- } else if (pref.end < buf.end && pref.buf[pref.end-1] != SEP) {
+ if (abs(p)) return not_prefix;
+ } else if (pref.end < p.end && pref.buf[pref.end-1] != SEP) {
pref.buf[pref.end] = SEP;
pref.end += 1;
};
- if (bytes::hasprefix(buf.buf[..buf.end], pref.buf[..pref.end])) {
+ if (bytes::hasprefix(p.buf[..p.end], pref.buf[..pref.end])) {
return pref.end;
} else {
return not_prefix;
@@ -52,23 +52,23 @@ fn splitprefix(buf: *buffer, prefix: str) (size | error) = {
};
@test fn prepend() void = {
- const buf = init("a")!;
+ const p = init("a")!;
// relative
- assert(prepend(&buf, "apple")! == local("apple/a"));
- assert(popprefix(&buf, "b") is error);
- assert(popprefix(&buf, "appl") is error);
- assert(popprefix(&buf, local("/")) is error);
- assert(popprefix(&buf, ".")! == local("apple/a"));
- assert(popprefix(&buf, "apple")! == "a");
- assert(popprefix(&buf, "a")! == ".");
+ assert(prepend(&p, "apple")! == local("apple/a"));
+ assert(popprefix(&p, "b") is error);
+ assert(popprefix(&p, "appl") is error);
+ assert(popprefix(&p, local("/")) is error);
+ assert(popprefix(&p, ".")! == local("apple/a"));
+ assert(popprefix(&p, "apple")! == "a");
+ assert(popprefix(&p, "a")! == ".");
// absolute
- assert(prepend(&buf, local("/apple/a"))! == local("/apple/a"));
- assert(popprefix(&buf, local("/b")) is error);
- assert(popprefix(&buf, local("/appl")) is error);
- assert(popprefix(&buf, ".") is error);
- assert(popprefix(&buf, local("/"))! == local("apple/a"));
- assert(prepend(&buf, local("/"))! == local("/apple/a"));
- assert(popprefix(&buf, local("/apple/a"))! == ".");
+ assert(prepend(&p, local("/apple/a"))! == local("/apple/a"));
+ assert(popprefix(&p, local("/b")) is error);
+ assert(popprefix(&p, local("/appl")) is error);
+ assert(popprefix(&p, ".") is error);
+ assert(popprefix(&p, local("/"))! == local("apple/a"));
+ assert(prepend(&p, local("/"))! == local("/apple/a"));
+ assert(popprefix(&p, local("/apple/a"))! == ".");
};
diff --git a/path/stack.ha b/path/stack.ha
@@ -4,35 +4,35 @@
use bytes;
use strings;
-// Appends path elements onto the end of a path buffer.
+// Appends path elements onto the end of a [[path]] buffer.
// Returns the new string value of the path.
-export fn push(buf: *buffer, items: str...) (str | error) = {
+export fn push(p: *path, items: str...) (str | error) = {
for (let item .. items) {
let elem = strings::toutf8(item);
for (true) match (bytes::index(elem, SEP)) {
case void =>
- buf.end = appendnorm(buf, elem)?;
+ p.end = appendnorm(p, elem)?;
break;
case let j: size =>
- if (j == 0 && buf.end == 0) {
- buf.buf[0] = SEP;
- buf.end = 1;
+ if (j == 0 && p.end == 0) {
+ p.buf[0] = SEP;
+ p.end = 1;
} else {
- buf.end = appendnorm(buf, elem[..j])?;
+ p.end = appendnorm(p, elem[..j])?;
};
elem = elem[j+1..];
};
};
- return string(buf);
+ return string(p);
};
const dot: []u8 = ['.'];
const dotdot: []u8 = ['.', '.'];
-// append a path segment to a buffer, preserving normalization.
+// append a path segment to a [[path]], preserving normalization.
// seg must not contain any [[SEP]]s. if you need to make the path absolute, you
-// should do that manually. returns the new end of the buffer.
+// should do that manually. returns the new end of the [[path]].
// x + => x
// x + . => x
// / + .. => /
@@ -40,141 +40,141 @@ const dotdot: []u8 = ['.', '.'];
// x/.. + .. => x/../..
// x/y + .. => x
// x + y => x/y
-fn appendnorm(buf: *buffer, seg: []u8) (size | error) = {
- if (len(seg) == 0 || bytes::equal(dot, seg)) return buf.end;
+fn appendnorm(p: *path, seg: []u8) (size | error) = {
+ if (len(seg) == 0 || bytes::equal(dot, seg)) return p.end;
if (bytes::equal(dotdot, seg)) {
- if (isroot(buf)) return buf.end;
- const isep = match (bytes::rindex(buf.buf[..buf.end], SEP)) {
+ if (isroot(p)) return p.end;
+ const isep = match (bytes::rindex(p.buf[..p.end], SEP)) {
case void => yield 0z;
case let i: size => yield i + 1;
};
- if (buf.end == 0 || bytes::equal(buf.buf[isep..buf.end], dotdot)) {
- return appendlit(buf, dotdot)?;
+ if (p.end == 0 || bytes::equal(p.buf[isep..p.end], dotdot)) {
+ return appendlit(p, dotdot)?;
} else {
return if (isep <= 1) isep else isep - 1;
};
} else {
- return appendlit(buf, seg)?;
+ return appendlit(p, seg)?;
};
};
-// append a segment to a buffer, *without* preserving normalization.
-// returns the new end of the buffer
-fn appendlit(buf: *buffer, bs: []u8) (size | error) = {
- let newend = buf.end;
- if (buf.end == 0 || isroot(buf)) {
- if (MAX < buf.end + len(bs)) return too_long;
+// append a segment to a [[path]], *without* preserving normalization.
+// returns the new end of the [[path]]
+fn appendlit(p: *path, bs: []u8) (size | error) = {
+ let newend = p.end;
+ if (p.end == 0 || isroot(p)) {
+ if (MAX < p.end + len(bs)) return too_long;
} else {
- if (MAX < buf.end + len(bs) + 1) return too_long;
- buf.buf[buf.end] = SEP;
+ if (MAX < p.end + len(bs) + 1) return too_long;
+ p.buf[p.end] = SEP;
newend += 1;
};
- buf.buf[newend..newend+len(bs)] = bs;
+ p.buf[newend..newend+len(bs)] = bs;
return newend + len(bs);
};
@test fn push() void = {
- let buf = init()!;
- assert(string(&buf) == ".");
+ let p = init()!;
+ assert(string(&p) == ".");
// current dir invariants
- assert(push(&buf, "")! == ".");
- assert(push(&buf, ".")! == ".");
+ assert(push(&p, "")! == ".");
+ assert(push(&p, ".")! == ".");
// parent dir invariants
- assert(push(&buf, "..")! == "..");
- assert(push(&buf, "")! == "..");
- assert(push(&buf, ".")! == "..");
- assert(push(&buf, local("/"))! == "..");
+ assert(push(&p, "..")! == "..");
+ assert(push(&p, "")! == "..");
+ assert(push(&p, ".")! == "..");
+ assert(push(&p, local("/"))! == "..");
- assert(set(&buf)! == ".");
+ assert(set(&p)! == ".");
// root dir invariants
- assert(push(&buf, local("/"))! == local("/"));
- assert(push(&buf, "")! == local("/"));
- assert(push(&buf, ".")! == local("/"));
- assert(push(&buf, "..")! == local("/"));
- assert(push(&buf, local("/"))! == local("/"));
+ assert(push(&p, local("/"))! == local("/"));
+ assert(push(&p, "")! == local("/"));
+ assert(push(&p, ".")! == local("/"));
+ assert(push(&p, "..")! == local("/"));
+ assert(push(&p, local("/"))! == local("/"));
- assert(set(&buf)! == ".");
+ assert(set(&p)! == ".");
// regular path and parent
- assert(push(&buf, "foo")! == "foo");
- assert(push(&buf, ".")! == "foo");
- assert(push(&buf, local("/"))! == "foo");
- assert(push(&buf, "..")! == ".");
+ assert(push(&p, "foo")! == "foo");
+ assert(push(&p, ".")! == "foo");
+ assert(push(&p, local("/"))! == "foo");
+ assert(push(&p, "..")! == ".");
// multiple segments
- assert(push(&buf, "a", "b")! == local("a/b"));
- assert(push(&buf, "..", "c")! == local("a/c"));
- assert(push(&buf, "..")! == "a");
- assert(push(&buf, local("/d"))! == local("a/d"));
- assert(push(&buf, "..", "..")! == ".");
+ assert(push(&p, "a", "b")! == local("a/b"));
+ assert(push(&p, "..", "c")! == local("a/c"));
+ assert(push(&p, "..")! == "a");
+ assert(push(&p, local("/d"))! == local("a/d"));
+ assert(push(&p, "..", "..")! == ".");
// multiple segments, absolute
- assert(push(&buf, local("/"), "a", "b")! == local("/a/b"));
- assert(push(&buf, "..", "c")! == local("/a/c"));
- assert(push(&buf, "..")! == local("/a"));
- assert(push(&buf, local("/d"))! == local("/a/d"));
- assert(push(&buf, "..", "..", "..")! == local("/"));
+ assert(push(&p, local("/"), "a", "b")! == local("/a/b"));
+ assert(push(&p, "..", "c")! == local("/a/c"));
+ assert(push(&p, "..")! == local("/a"));
+ assert(push(&p, local("/d"))! == local("/a/d"));
+ assert(push(&p, "..", "..", "..")! == local("/"));
};
-// Examine the final path segment in a buffer.
+// Examine the final path segment in a [[path]].
// Returns void if the path is empty or is the root dir.
-export fn peek(buf: *const buffer) (str | void) = split(buf).1;
+export fn peek(p: *const path) (str | void) = split(p).1;
-// Remove and return the final path segment in a buffer.
+// Remove and return the final path segment in a [[path]].
// Returns void if the path is empty or is the root dir.
-export fn pop(buf: *buffer) (str | void) = {
- const (end, res) = split(buf);
- buf.end = end;
+export fn pop(p: *path) (str | void) = {
+ const (end, res) = split(p);
+ p.end = end;
return res;
};
-// helper function for pop/peek, returns (new end of buffer, result)
-fn split(buf: *buffer) (size, (str | void)) = {
- if (buf.end == 0 || isroot(buf)) return (buf.end, void);
- match (bytes::rindex(buf.buf[..buf.end], SEP)) {
+// helper function for pop/peek, returns (new end of [[path]], result)
+fn split(p: *path) (size, (str | void)) = {
+ if (p.end == 0 || isroot(p)) return (p.end, void);
+ match (bytes::rindex(p.buf[..p.end], SEP)) {
case void =>
- return (0z, strings::fromutf8_unsafe(buf.buf[..buf.end]));
+ return (0z, strings::fromutf8_unsafe(p.buf[..p.end]));
case let i: size =>
return (
if (i == 0) 1z else i,
- strings::fromutf8_unsafe(buf.buf[i+1..buf.end]),
+ strings::fromutf8_unsafe(p.buf[i+1..p.end]),
);
};
};
@test fn pop() void = {
// empty
- let buf = init()!;
- assert(pop(&buf) is void);
- assert(string(&buf) == ".");
+ let p = init()!;
+ assert(pop(&p) is void);
+ assert(string(&p) == ".");
// root dir
- buf.end = 0;
- push(&buf, local("/"))!;
- assert(pop(&buf) is void);
- assert(string(&buf) == local("/"));
+ p.end = 0;
+ push(&p, local("/"))!;
+ assert(pop(&p) is void);
+ assert(string(&p) == local("/"));
// relative file
- buf.end = 0;
- push(&buf, "foo")!;
- assert(pop(&buf) as str == "foo");
- assert(string(&buf) == ".");
+ p.end = 0;
+ push(&p, "foo")!;
+ assert(pop(&p) as str == "foo");
+ assert(string(&p) == ".");
// abs file
- buf.end = 0;
- push(&buf, local("/foo"))!;
- assert(pop(&buf) as str == "foo");
- assert(string(&buf) == local("/"));
+ p.end = 0;
+ push(&p, local("/foo"))!;
+ assert(pop(&p) as str == "foo");
+ assert(string(&p) == local("/"));
};
-// Returns the parent directory for a given path, without modifying the buffer.
+// Returns the parent directory for a given path, without modifying the [[path]].
// If the path is the root directory, the root directory is returned. The value
// is either borrowed from the input or statically allocated; use
// [[strings::dup]] to extend its lifetime or modify it.
-export fn parent(buf: *const buffer) (str | error) = {
- const newend = appendnorm(buf, dotdot)?;
+export fn parent(p: *const path) (str | error) = {
+ const newend = appendnorm(p, dotdot)?;
if (newend == 0) return ".";
- return strings::fromutf8_unsafe(buf.buf[..newend]);
+ return strings::fromutf8_unsafe(p.buf[..newend]);
};
diff --git a/temp/+freebsd.ha b/temp/+freebsd.ha
@@ -47,7 +47,7 @@ export fn named(
oflags |= fs::flag::WRONLY;
};
- static let pathbuf = path::buffer { ... };
+ static let pathbuf = path::path { ... };
static let namebuf: [32]u8 = [0...];
for (true) {
let id = 0u64;
@@ -83,9 +83,9 @@ export fn dir() str = {
io::write(&enc, buf)!;
let name = memio::string(&sink)!;
- static let buf = path::buffer { ... };
- path::set(&buf, get_tmpdir(), name)!;
- const path = path::string(&buf);
+ static let p = path::path { ... };
+ path::set(&p, get_tmpdir(), name)!;
+ const path = path::string(&p);
match (os::mkdir(path,0o755)) {
case let err: fs::error => abort("Could not create temp directory");
case void => void;
diff --git a/temp/+linux.ha b/temp/+linux.ha
@@ -57,7 +57,7 @@ export fn named(
oflags |= fs::flag::WRONLY;
};
- static let pathbuf = path::buffer { ... };
+ static let pathbuf = path::path { ... };
static let namebuf: [32]u8 = [0...];
for (true) {
let id = 0u64;
@@ -93,9 +93,9 @@ export fn dir() str = {
io::write(&enc, buf) as size;
const name = memio::string(&sink)!;
- static let buf = path::buffer { ... };
- path::set(&buf, get_tmpdir(), name)!;
- const path = path::string(&buf);
+ static let p = path::path { ... };
+ path::set(&p, get_tmpdir(), name)!;
+ const path = path::string(&p);
match (os::mkdir(path, 0o755)) {
case let err: fs::error => abort("Could not create temp directory");
case void => void;
diff --git a/time/chrono/tzdb.ha b/time/chrono/tzdb.ha
@@ -36,7 +36,7 @@ export fn tz(name: str) (locality | tzdberror) = {
yield path::init(TZDB_PATH, name);
};
const filepath = match (filepath) {
- case let buf: path::buffer =>
+ case let buf: path::path =>
yield buf;
case let err: path::error =>
assert(err is path::too_long);