commit 339fdf9693cc554798beb3326f103200199d45f6
parent ba988852467da3a3b4655f77110025322bec2296
Author: Sebastian <sebastian@sebsite.pw>
Date: Thu, 28 Apr 2022 17:28:45 -0400
path: use PATHSEP where applicable
Signed-off-by: Sebastian <sebastian@sebsite.pw>
Diffstat:
6 files changed, 144 insertions(+), 76 deletions(-)
diff --git a/path/+freebsd.ha b/path/+freebsd.ha
@@ -1,8 +1,10 @@
// License: MPL-2.0
// (c) 2021-2022 Drew DeVault <sir@cmpwn.com>
-// Platform-specific path separator.
+// Platform-specific path separator byte.
export def PATHSEP: u8 = '/';
+const pathsepstr: str = "/";
+
// Maximum length of a file path for this platform.
export def PATH_MAX: size = 4096;
diff --git a/path/+linux.ha b/path/+linux.ha
@@ -1,8 +1,10 @@
// License: MPL-2.0
// (c) 2021-2022 Drew DeVault <sir@cmpwn.com>
-// Platform-specific path separator.
+// Platform-specific path separator byte.
export def PATHSEP: u8 = '/';
+const pathsepstr: str = "/";
+
// Maximum length of a file path for this platform.
export def PATH_MAX: size = 4096;
diff --git a/path/buffer.ha b/path/buffer.ha
@@ -36,9 +36,9 @@ export fn set(buf: *buffer, items: str...) (str | errors::overflow) = {
// - Redundant path separators
// - Any "." components, except in the case of "."
//
-// "/usr//bin/../bin/./hare" becomes "/usr/bin/hare" and "../../foo/bar" is
-// unchanged. The path will only end in a slash if the last item which was
-// added ended in "/", like so:
+// Assuming that PATHSEP is '/', "/usr//bin/../bin/./hare" becomes
+// "/usr/bin/hare" and "../../foo/bar" is unchanged. The path will only end in a
+// slash if the last item which was added ended in PATHSEP, like so:
//
// let buf = path::init();
// path::set(&buf, "foo", "bar")!;
@@ -141,27 +141,33 @@ fn doappend(buf: *buffer, elem: []u8) (void | errors::overflow) = {
appendnorm(&buf, "foo")!;
appendnorm(&buf, "bar")!;
appendnorm(&buf, "baz")!;
- assert(string(&buf) == "foo/bar/baz");
+ let s = strings::join(pathsepstr, "foo", "bar", "baz");
+ assert(string(&buf) == s);
appendnorm(&buf, ".")!;
appendnorm(&buf, "bad")!;
appendnorm(&buf, ".")!;
- assert(string(&buf) == "foo/bar/baz/bad");
+ free(s);
+ s = strings::join(pathsepstr, "foo", "bar", "baz", "bad");
+ assert(string(&buf) == s);
+ free(s);
reset(&buf);
- appendnorm(&buf, "/")!;
+ appendnorm(&buf, pathsepstr)!;
appendnorm(&buf, "foo")!;
appendnorm(&buf, "bar")!;
appendnorm(&buf, "baz")!;
- assert(string(&buf) == "/foo/bar/baz");
- appendnorm(&buf, "/")!;
- appendnorm(&buf, "/")!;
- assert(string(&buf) == "/foo/bar/baz");
+ s = strings::join(pathsepstr, "", "foo", "bar", "baz");
+ assert(string(&buf) == s);
+ appendnorm(&buf, pathsepstr)!;
+ appendnorm(&buf, pathsepstr)!;
+ assert(string(&buf) == s);
+ free(s);
reset(&buf);
- appendnorm(&buf, "/")!;
- appendnorm(&buf, "/")!;
- appendnorm(&buf, "/")!;
- assert(string(&buf) == "/");
+ appendnorm(&buf, pathsepstr)!;
+ appendnorm(&buf, pathsepstr)!;
+ appendnorm(&buf, pathsepstr)!;
+ assert(string(&buf) == pathsepstr);
reset(&buf);
appendnorm(&buf, ".")!;
@@ -176,14 +182,20 @@ fn doappend(buf: *buffer, elem: []u8) (void | errors::overflow) = {
appendnorm(&buf, "..")!;
assert(string(&buf) == "..");
appendnorm(&buf, "..")!;
- assert(string(&buf) == "../..");
+ s = strings::join(pathsepstr, "..", "..");
+ assert(string(&buf) == s);
+ free(s);
appendnorm(&buf, "..")!;
- assert(string(&buf) == "../../..");
+ s = strings::join(pathsepstr, "..", "..", "..");
+ assert(string(&buf) == s);
+ free(s);
reset(&buf);
appendnorm(&buf, "foo")!;
appendnorm(&buf, "bar")!;
- assert(string(&buf) == "foo/bar");
+ s = strings::join(pathsepstr, "foo", "bar");
+ assert(string(&buf) == s);
+ free(s);
appendnorm(&buf, "..")!;
assert(string(&buf) == "foo");
appendnorm(&buf, "..")!;
@@ -191,19 +203,33 @@ fn doappend(buf: *buffer, elem: []u8) (void | errors::overflow) = {
appendnorm(&buf, "..")!;
assert(string(&buf) == "..");
appendnorm(&buf, "..")!;
- assert(string(&buf) == "../..");
+ s = strings::join(pathsepstr, "..", "..");
+ assert(string(&buf) == s);
+ free(s);
set(&buf, "foo", "bar")!;
- assert(string(&buf) == "foo/bar");
- set(&buf, "foo", "bar/")!;
- assert(string(&buf) == "foo/bar/");
- set(&buf, "foo", "bar", "/")!;
- assert(string(&buf) == "foo/bar/");
- add(&buf, "/baz")!;
- assert(string(&buf) == "foo/bar/baz");
+ s = strings::join(pathsepstr, "foo", "bar");
+ assert(string(&buf) == s);
+ free(s);
+ s = strings::concat("bar", pathsepstr);
+ set(&buf, "foo", s)!;
+ free(s);
+ s = strings::join(pathsepstr, "foo", "bar", "");
+ assert(string(&buf) == s);
+ set(&buf, "foo", "bar", pathsepstr)!;
+ assert(string(&buf) == s);
+ free(s);
+ s = strings::concat(pathsepstr, "baz");
+ add(&buf, s)!;
+ free(s);
+ s = strings::join(pathsepstr, "foo", "bar", "baz");
+ assert(string(&buf) == s);
+ free(s);
reset(&buf);
appendnorm(&buf, "a")!;
appendnorm(&buf, "b")!;
- assert(string(&buf) == "a/b");
+ s = strings::join(pathsepstr, "a", "b");
+ assert(string(&buf) == s);
+ free(s);
};
diff --git a/path/iter.ha b/path/iter.ha
@@ -51,14 +51,17 @@ export fn next(iter: *iterator) (str | void) = {
};
@test fn iter() void = {
- assert(PATHSEP == '/'); // meh
- let i = iter("/foo/bar/baz");
- assert(next(&i) as str == "/");
+ const s = strings::join(pathsepstr, "", "foo", "bar", "baz");
+ let i = iter(s);
+ assert(next(&i) as str == pathsepstr);
assert(next(&i) as str == "foo");
assert(next(&i) as str == "bar");
assert(next(&i) as str == "baz");
assert(next(&i) is void);
- let i = iter("foo/bar/baz/");
+ free(s);
+
+ const s = strings::join(pathsepstr, "foo", "bar", "baz", "");
+ let i = iter(s);
assert(next(&i) as str == "foo");
assert(next(&i) as str == "bar");
assert(next(&i) as str == "baz");
@@ -66,8 +69,9 @@ export fn next(iter: *iterator) (str | void) = {
let i = iter("foo");
assert(next(&i) as str == "foo");
assert(next(&i) is void);
+ free(s);
- let i = iter("/");
- assert(next(&i) as str == "/");
+ let i = iter(pathsepstr);
+ assert(next(&i) as str == pathsepstr);
assert(next(&i) is void);
};
diff --git a/path/join.ha b/path/join.ha
@@ -20,7 +20,7 @@ export fn add(buf: *buffer, items: str...) (str | errors::overflow) = {
if (len(next) == 0 && j == 0) {
// Handles the add("/foo") case as
// add("/", "foo");
- appendnorm(buf, "/")?;
+ appendnorm(buf, pathsepstr)?;
};
appendnorm(buf, next)?;
};
@@ -31,19 +31,33 @@ export fn add(buf: *buffer, items: str...) (str | errors::overflow) = {
@test fn add() void = {
let buf = init();
add(&buf, "foo", "bar", "baz")!;
- assert(string(&buf) == "foo/bar/baz");
+ let s = strings::join(pathsepstr, "foo", "bar", "baz");
+ assert(string(&buf) == s);
+ free(s);
reset(&buf);
- add(&buf, "/foo/bar", "baz")!;
- assert(string(&buf) == "/foo/bar/baz");
+ s = strings::join(pathsepstr, "", "foo", "bar");
+ add(&buf, s, "baz")!;
+ free(s);
+ s = strings::join(pathsepstr, "", "foo", "bar", "baz");
+ assert(string(&buf) == s);
+ free(s);
reset(&buf);
- add(&buf, "/", "foo/bar", "baz")!;
- assert(string(&buf) == "/foo/bar/baz");
+ s = strings::join(pathsepstr, "foo", "bar");
+ add(&buf, pathsepstr, s, "baz")!;
+ free(s);
+ s = strings::join(pathsepstr, "", "foo", "bar", "baz");
+ assert(string(&buf) == s);
+ free(s);
reset(&buf);
- add(&buf, "./foo/bar")!;
- assert(string(&buf) == "foo/bar");
+ s = strings::join(pathsepstr, ".", "foo", "bar");
+ add(&buf, s)!;
+ free(s);
+ s = strings::join(pathsepstr, "foo", "bar");
+ assert(string(&buf) == s);
+ free(s);
};
// Joins a list of path components together, normalizes it, and returns the
diff --git a/path/names.ha b/path/names.ha
@@ -19,7 +19,7 @@ export fn dirname(path: (str | *buffer)) const str = {
};
let trimmed = strings::rtrim(path, PATHSEP: u32: rune);
if (trimmed == "") {
- return "/";
+ return pathsepstr;
};
let b = strings::toutf8(trimmed);
let i = match (bytes::rindex(b, PATHSEP)) {
@@ -34,25 +34,29 @@ export fn dirname(path: (str | *buffer)) const str = {
path = strings::fromutf8_unsafe(b[..i]);
path = strings::rtrim(path, PATHSEP: u32: rune);
if (path == "") {
- return "/";
+ return pathsepstr;
};
return path;
};
@test fn dirname() void = {
- assert(dirname("/foo/bar") == "/foo");
- assert(dirname("/foo") == "/");
- assert(dirname("/") == "/");
- assert(dirname("//") == "/");
- assert(dirname("///") == "/");
- assert(dirname("foo/bar") == "foo");
- assert(dirname("") == ".");
- assert(dirname("foo") == ".");
- assert(dirname("foo/") == ".");
- assert(dirname("foo//") == ".");
- assert(dirname("///foo") == "/");
- assert(dirname("///foo//") == "/");
- assert(dirname("//foo///bar//") == "//foo");
+ assertpatheql(&dirname, pathsepstr, "", "foo");
+ assertpatheql(&dirname, pathsepstr, pathsepstr);
+ assertpatheql(&dirname, pathsepstr, "", "", "");
+ assertpatheql(&dirname, pathsepstr, "", "", "", "");
+ assertpatheql(&dirname, "foo", "foo", "bar");
+ assertpatheql(&dirname, ".", "");
+ assertpatheql(&dirname, ".", "foo");
+ assertpatheql(&dirname, ".", "foo", "");
+ assertpatheql(&dirname, ".", "foo", "", "");
+ assertpatheql(&dirname, pathsepstr, "", "", "", "foo");
+ assertpatheql(&dirname, pathsepstr, "", "", "", "foo", "", "");
+ let expected = strings::concat(pathsepstr, "foo");
+ assertpatheql(&dirname, expected, "", "foo", "bar");
+ free(expected);
+ expected = strings::concat(pathsepstr, pathsepstr, "foo");
+ assertpatheql(&dirname, expected, "", "", "foo", "", "", "bar", "", "");
+ free(expected);
};
// Returns the final component of a given path. For a path to a file name, this
@@ -68,7 +72,7 @@ export fn basename(path: (str | *buffer)) const str = {
};
let trimmed = strings::rtrim(path, PATHSEP: u32: rune);
if (trimmed == "") {
- return "/";
+ return pathsepstr;
};
let b = strings::toutf8(trimmed);
let i = match (bytes::rindex(b, PATHSEP)) {
@@ -81,14 +85,17 @@ export fn basename(path: (str | *buffer)) const str = {
};
@test fn basename() void = {
- assert(basename("/foo/bar") == "bar");
- assert(basename("/foo") == "foo");
- assert(basename("/") == "/");
- assert(basename("//") == "/");
- assert(basename("///") == "/");
- assert(basename("foo/bar") == "bar");
- assert(basename("foo/bar//") == "bar");
- assert(basename("foo") == "foo");
+ assertpatheql(&basename, "bar", "", "foo", "bar");
+ assertpatheql(&basename, "foo", "", "foo");
+ assertpatheql(&basename, pathsepstr, pathsepstr);
+ assertpatheql(&basename, pathsepstr, "", "", "");
+ assertpatheql(&basename, pathsepstr, "", "", "", "");
+ assertpatheql(&basename, "bar", "foo", "bar");
+ assertpatheql(&basename, "bar", "foo", "bar", "", "");
+ assertpatheql(&basename, "foo", "foo");
+ assertpatheql(&basename, "foo", "foo", "");
+ assertpatheql(&basename, "bar", "foo", "bar", "");
+ assertpatheql(&basename, ".", "");
};
// Returns the file name and extension for a path. The return value is borrowed
@@ -121,14 +128,27 @@ export fn extension(p: (str | *buffer)) (str, str) = {
};
@test fn extension() void = {
- assert(extension("").0 == "");
- assert(extension("").1 == "");
- assert(extension("foo/bar").0 == "bar");
- assert(extension("foo/bar").1 == "");
- assert(extension("foo/bar.txt").0 == "bar");
- assert(extension("foo/bar.txt").1 == ".txt");
- assert(extension("foo/bar.tar.gz").0 == "bar");
- assert(extension("foo/bar.tar.gz").1 == ".tar.gz");
- assert(extension("foo.bar/baz.ha").0 == "baz");
- assert(extension("foo.bar/baz.ha").1 == ".ha");
+ assertpatheql(&ext0, "", "");
+ assertpatheql(&ext1, "", "");
+ assertpatheql(&ext0, "bar", "foo", "bar");
+ assertpatheql(&ext1, "", "foo", "bar");
+ assertpatheql(&ext0, "bar", "foo", "bar.txt");
+ assertpatheql(&ext1, ".txt", "foo", "bar.txt");
+ assertpatheql(&ext0, "bar", "foo", "bar.tar.gz");
+ assertpatheql(&ext1, ".tar.gz", "foo", "bar.tar.gz");
+ assertpatheql(&ext0, "baz", "foo.bar", "baz.ha");
+ assertpatheql(&ext1, ".ha", "foo.bar", "baz.ha");
};
+
+fn assertpatheql(
+ func: *fn(path: (str | *buffer)) const str,
+ expected: str,
+ path: str...
+) void = {
+ const s = strings::join(pathsepstr, path...);
+ assert(func(s) == expected);
+ free(s);
+};
+
+fn ext0(p: (str | *buffer)) const str = extension(p).0;
+fn ext1(p: (str | *buffer)) const str = extension(p).1;