hare

The Hare programming language
git clone https://git.torresjrjr.com/hare.git
Log | Files | Refs | README | LICENSE

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:
Mpath/+freebsd.ha | 4+++-
Mpath/+linux.ha | 4+++-
Mpath/buffer.ha | 78++++++++++++++++++++++++++++++++++++++++++++++++++++--------------------------
Mpath/iter.ha | 16++++++++++------
Mpath/join.ha | 30++++++++++++++++++++++--------
Mpath/names.ha | 88++++++++++++++++++++++++++++++++++++++++++++++++-------------------------------
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;