commit 9db5898c0e901560f5a1d71e14d5319124369ef9
parent 5ba3db310014d4e788d51f984fe29b0bbb66e588
Author: Drew DeVault <sir@cmpwn.com>
Date: Sat, 8 Jan 2022 10:04:17 +0100
pathbuf: add join
Signed-off-by: Drew DeVault <sir@cmpwn.com>
Diffstat:
5 files changed, 60 insertions(+), 16 deletions(-)
diff --git a/pathbuf/README b/pathbuf/README
@@ -3,9 +3,10 @@ filesystem paths through the [[buffer]] type.
let buf = pathbuf::init();
pathbuf::join(&buf, "/", "foo", "bar", "baz.txt");
- io::println(pathbuf::string()); // "/foo/bar/baz.txt"
+ io::println(pathbuf::path(&buf)); // "/foo/bar/baz.txt"
+
pathbuf::join(&buf, "../.././hello.txt");
- io::println(pathbuf::string()); // "/foo/hello.txt"
+ io::println(pathbuf::path(&buf)); // "/foo/hello.txt"
The buffer object includes an array of length [[path::PATH_MAX]], which can be
somewhat large - on Linux it's 4096. You can allocate this on the stack in most
diff --git a/pathbuf/buffer.ha b/pathbuf/buffer.ha
@@ -23,6 +23,15 @@ export fn init_static(buf: *buffer) void = {
reset(buf);
};
+// Initializes a new path buffer and sets its initial value from a set of path
+// components.
+export fn initfrom(items: str...) (buffer | errors::overflow) = {
+ let buf = buffer { ... };
+ reset(&buf);
+ join(&buf, items...)?;
+ return buf;
+};
+
// Resets a path buffer to its initial state (an empty path).
export fn reset(buf: *buffer) void = {
buf.cur = buf.buf[..0];
@@ -53,17 +62,8 @@ export fn path(buf: *buffer) str = {
// Overwrites the contents of a [[buffer]] with an arbitrary path.
export fn setpath(buf: *buffer, path: str) (void | errors::overflow) = {
- const path = strings::toutf8(path);
- const tok = bytes::tokenize(path, [path::PATHSEP]);
- for (true) {
- const next = match (bytes::next_token(&tok)) {
- case let tok: []u8 =>
- yield tok;
- case void =>
- break;
- };
- appendnorm(buf, next)?;
- };
+ reset(buf);
+ join(buf, path)?;
};
// Normalizes and appends a path component to a buffer.
diff --git a/pathbuf/ops.ha b/pathbuf/ops.ha
@@ -0,0 +1,40 @@
+use bytes;
+use errors;
+use path;
+use strings;
+
+// Joins several path elements together and appends them to a path buffer.
+export fn join(buf: *buffer, items: str...) (void | errors::overflow) = {
+ for (let i = 0z; i < len(items); i += 1) {
+ const elem = strings::toutf8(items[i]);
+ const tok = bytes::tokenize(elem, [path::PATHSEP]);
+ for (let j = 0z; true; j += 1) {
+ const next = match (bytes::next_token(&tok)) {
+ case let tok: []u8 =>
+ yield tok;
+ case void =>
+ break;
+ };
+ if (len(next) == 0 && j == 0) {
+ // Handles the join("/foo") case as
+ // join("/", "foo");
+ appendnorm(buf, "/")?;
+ };
+ appendnorm(buf, next)?;
+ };
+ };
+};
+
+@test fn join() void = {
+ let buf = init();
+ join(&buf, "foo", "bar", "baz")!;
+ assert(path(&buf) == "foo/bar/baz");
+
+ reset(&buf);
+ join(&buf, "/foo/bar", "baz")!;
+ assert(path(&buf) == "/foo/bar/baz");
+
+ reset(&buf);
+ join(&buf, "/", "foo/bar", "baz")!;
+ assert(path(&buf) == "/foo/bar/baz");
+};
diff --git a/scripts/gen-stdlib b/scripts/gen-stdlib
@@ -922,7 +922,8 @@ path() {
pathbuf() {
gen_srcs pathbuf \
- buffer.ha
+ buffer.ha \
+ ops.ha
gen_ssa pathbuf path
}
diff --git a/stdlib.mk b/stdlib.mk
@@ -1495,7 +1495,8 @@ $(HARECACHE)/path/path-any.ssa: $(stdlib_path_any_srcs) $(stdlib_rt) $(stdlib_st
# pathbuf (+any)
stdlib_pathbuf_any_srcs= \
- $(STDLIB)/pathbuf/buffer.ha
+ $(STDLIB)/pathbuf/buffer.ha \
+ $(STDLIB)/pathbuf/ops.ha
$(HARECACHE)/pathbuf/pathbuf-any.ssa: $(stdlib_pathbuf_any_srcs) $(stdlib_rt) $(stdlib_path_$(PLATFORM))
@printf 'HAREC \t$@\n'
@@ -3298,7 +3299,8 @@ $(TESTCACHE)/path/path-any.ssa: $(testlib_path_any_srcs) $(testlib_rt) $(testlib
# pathbuf (+any)
testlib_pathbuf_any_srcs= \
- $(STDLIB)/pathbuf/buffer.ha
+ $(STDLIB)/pathbuf/buffer.ha \
+ $(STDLIB)/pathbuf/ops.ha
$(TESTCACHE)/pathbuf/pathbuf-any.ssa: $(testlib_pathbuf_any_srcs) $(testlib_rt) $(testlib_path_$(PLATFORM))
@printf 'HAREC \t$@\n'