hare

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

commit 53150ab548748e824ffd119661f997979049a779
parent b2814cd779dd321ae75538f3403a0c47a381ee15
Author: Drew DeVault <sir@cmpwn.com>
Date:   Sat,  8 Jan 2022 17:40:37 +0100

fs, os: add realpath

Signed-off-by: Drew DeVault <sir@cmpwn.com>

Diffstat:
Mfs/fs.ha | 30------------------------------
Mfs/util.ha | 66++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mos/fs.ha | 8++++++++
3 files changed, 74 insertions(+), 30 deletions(-)

diff --git a/fs/fs.ha b/fs/fs.ha @@ -220,36 +220,6 @@ export fn rmdir(fs: *fs, path: str) (void | error) = { }; }; -// Removes a directory, and anything in it. -export fn rmdirall(fs: *fs, path: str) (void | error) = { - let it = iter(fs, path)?; - for (true) { - match (next(it)) { - case let ent: dirent => - if (ent.name == "." || ent.name == "..") { - continue; - }; - // XXX: This could probably be more efficient if we - // re-used the buffer further down the call stack. - let buf = alloc(path::init()); - defer free(buf); - const path = path::set(buf, path, ent.name)!; - - switch (ent.ftype & mode::DIR) { - case mode::DIR => - rmdirall(fs, path)?; - case => - remove(fs, path)?; - }; - case void => - break; - }; - }; - if (path != "") { - return rmdir(fs, path); - }; -}; - // Creates a directory and returns a subdir for it. Some filesystems support // doing this operation atomically, but if not, a fallback is used. export fn mksubdir(fs: *fs, path: str) (*fs | error) = { diff --git a/fs/util.ha b/fs/util.ha @@ -110,3 +110,69 @@ export fn dirents_free(d: []dirent) void = { dirent_free(&d[i]); }; }; + +// Removes a directory, and anything in it. +export fn rmdirall(fs: *fs, path: str) (void | error) = { + let it = iter(fs, path)?; + for (true) { + match (next(it)) { + case let ent: dirent => + if (ent.name == "." || ent.name == "..") { + continue; + }; + // XXX: This could probably be more efficient if we + // re-used the buffer further down the call stack. + let buf = alloc(path::init()); + defer free(buf); + const path = path::set(buf, path, ent.name)!; + + switch (ent.ftype & mode::DIR) { + case mode::DIR => + rmdirall(fs, path)?; + case => + remove(fs, path)?; + }; + case void => + break; + }; + }; + if (path != "") { + return rmdir(fs, path); + }; +}; + +// 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 buf = path::buffer { ... }; + path::reset(&buf); + + const iter = path::iter(path); + for (true) { + const item = match (path::next(&iter)) { + case let item: str => + yield item; + case void => + break; + }; + + const item = path::add(&buf, item)!; + const link = match (fs::readlink(fs, item)) { + case let link: str => + yield link; + case wrongtype => + continue; + case let err: error => + return err; + }; + + if (!path::abs(link)) { + path::add(&buf, "..", link)!; + } else { + path::set(&buf, link)!; + }; + }; + + return path::string(&buf); +}; diff --git a/os/fs.ha b/os/fs.ha @@ -87,3 +87,11 @@ export fn create( mode: fs::mode, flags: fs::flags... ) (io::file | fs::error) = fs::create_file(cwd, path, mode, flags...); + +// Canonicalizes a path in this filesystem by resolving all symlinks and +// collapsing any "." or ".." path components. +// +// This function is a thin shim over [[fs::resolve]], and the return value is +// statically allocated by [[fs::resolve]]. Thus, calls to this function or to +// [[fs::resolve]] will overwrite the return value of either function. +export fn realpath(path: str) (str | fs::error) = fs::realpath(cwd, path);