hare

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

commit 28361b77fe3ffcc4b1dc87c44f676fc5ece9e7b8
parent d0da286289e8d17e89ebeebbd5bdc1cd3df60076
Author: Eyal Sawady <ecs@d2evs.net>
Date:   Thu, 30 Sep 2021 21:09:50 +0000

path::{base,dir}name: match {base,dir}name(3p)

With path::basename("//") defined to return "/".

Signed-off-by: Eyal Sawady <ecs@d2evs.net>

Diffstat:
Mfs/mem/mem.ha | 4++--
Mpath/names.ha | 56+++++++++++++++++++++++++++++++++++++++++++++-----------
2 files changed, 47 insertions(+), 13 deletions(-)

diff --git a/fs/mem/mem.ha b/fs/mem/mem.ha @@ -104,7 +104,7 @@ fn create( case => return errors::exists; }; - if (path::dirname(path) != path) { + if (path::dirname(path) != ".") { parent = inode_find(parent, path::dirname(path))?; }; @@ -159,7 +159,7 @@ fn mksubdir(fs: *fs::fs, path: str) (*fs::fs | fs::error) = { case => return errors::exists; }; - if (path::dirname(path) != path) { + if (path::dirname(path) != ".") { parent = inode_find(parent, path::dirname(path))?; }; let name = strings::dup(path::basename(path)); diff --git a/path/names.ha b/path/names.ha @@ -4,36 +4,64 @@ use strings; // Returns the directory name for a given path. For a path to a file name, this // returns the directory in which that file resides. For a path to a directory, -// this returns the path to its parent directory. The return value is borrowed -// from the input, use [[dup]] to extend its lifetime. -export fn dirname(path: str) str = { - let b = strings::toutf8(path); +// this returns the path to its parent directory. The return value is eitherr +// borrowed from the input or statically allocated), use [[dup]] to extend its +// lifetime or modify it. +export fn dirname(path: str) const str = { + if (path == "") { + return "."; + }; + let trimmed = strings::rtrim(path, PATHSEP: u32: rune); + if (trimmed == "") { + return "/"; + }; + let b = strings::toutf8(trimmed); let i = match (bytes::rindex(b, PATHSEP)) { case void => - return path; + return "."; case z: size => yield z; }; if (i == 0) { i += 1; }; - return strings::fromutf8_unsafe(b[..i]); + path = strings::fromutf8_unsafe(b[..i]); + path = strings::rtrim(path, PATHSEP: u32: rune); + if (path == "") { + return "/"; + }; + 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("foo") == "foo"); + assert(dirname("") == "."); + assert(dirname("foo") == "."); + assert(dirname("foo/") == "."); + assert(dirname("foo//") == "."); + assert(dirname("///foo") == "/"); + assert(dirname("///foo//") == "/"); + assert(dirname("//foo///bar//") == "//foo"); }; // Returns the final component of a given path. For a path to a file name, this // returns the file name. For a path to a directory, this returns the directory -// name. The return value is borrowed from the input, use [[dup]] to extend its -// lifetime. -export fn basename(path: str) str = { - let b = strings::toutf8(path); +// name. The return value is either borrowed from the input or statically +// allocated, use [[dup]] to extend its lifetime or modify it. +export fn basename(path: str) const str = { + if (path == "") { + return "."; + }; + let trimmed = strings::rtrim(path, PATHSEP: u32: rune); + if (trimmed == "") { + return "/"; + }; + let b = strings::toutf8(trimmed); let i = match (bytes::rindex(b, PATHSEP)) { case void => return path; @@ -47,7 +75,10 @@ export fn basename(path: str) str = { 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"); }; @@ -60,6 +91,9 @@ export fn basename(path: str) str = { // extension("foo/example.txt") => ("example", ".txt") // extension("foo/example.tar.gz") => ("example", ".tar.gz") export fn extension(p: str) (str, str) = { + if (p == "") { + return ("", ""); + }; let p = basename(p); let b = strings::toutf8(p); if (len(b) == 0 || b[len(b) - 1] == PATHSEP) {