hare

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

commit edafe4a69721d48ca71312681bf8feb70880ec32
parent e4d137d5824ee92e7c1343170e494128f3be428d
Author: Drew DeVault <sir@cmpwn.com>
Date:   Fri,  1 Apr 2022 14:56:30 +0200

fs: add finish for iter

Fixes: https://todo.sr.ht/~sircmpwn/hare/576
Signed-off-by: Drew DeVault <sir@cmpwn.com>

Diffstat:
Mcmd/hare/plan.ha | 1+
Mfs/fs.ha | 10++++++++++
Mfs/types.ha | 9+++++++++
Mhare/module/scan.ha | 1+
Mos/+linux/dirfdfs.ha | 11++++++++---
Mos/fs.ha | 3+++
6 files changed, 32 insertions(+), 3 deletions(-)

diff --git a/cmd/hare/plan.ha b/cmd/hare/plan.ha @@ -79,6 +79,7 @@ fn mkplan(ctx: *module::context, libs: []str) plan = { let ntag = 0z; const buf = path::init(); const iter = os::iter(rtdir)!; + defer os::finish(iter); for (true) match (fs::next(iter)) { case let d: fs::dirent => const p = module::parse_name(d.name); diff --git a/fs/fs.ha b/fs/fs.ha @@ -140,6 +140,16 @@ export fn iter(fs: *fs, path: str) (*iterator | error) = { }; }; +// Frees state associated with an [[iterator]]. +export fn finish(iter: *iterator) void = { + match (iter.finish) { + case null => + yield; + case let f: *finishfunc => + return f(iter); + }; +}; + // Obtains information about a file or directory. If the target is a symlink, // information is returned about the link, not its target. export fn stat(fs: *fs, path: str) (filestat | error) = { diff --git a/fs/types.ha b/fs/types.ha @@ -296,10 +296,19 @@ export type fs = struct { symlink: nullable *symlinkfunc, }; +// A function which returns the next directory from an [[iterator]]. export type nextfunc = fn(iter: *iterator) (dirent | void); +// A function which frees state associated with an [[iterator]]. +export type finishfunc = fn(iter: *iterator) void; + +// A directory iterator. To implement a directory iterator for a filesystem, +// subtype this struct to store any necessary state and populate the pointers +// with your implementation. export type iterator = struct { // Returns the next member of the directory, or void if there are none // remaining. next: *nextfunc, + // Frees resources associated with the iterator. + finish: nullable *finishfunc, }; diff --git a/hare/module/scan.ha b/hare/module/scan.ha @@ -72,6 +72,7 @@ export fn scan(ctx: *context, path: str) (version | error) = { case let iter: *fs::iterator => yield iter; }; + defer fs::finish(iter); let ver = version { basedir = strings::dup(path), ... diff --git a/os/+linux/dirfdfs.ha b/os/+linux/dirfdfs.ha @@ -415,6 +415,7 @@ fn fs_iter(fs: *fs::fs, path: str) (*fs::iterator | fs::error) = { let iter = alloc(os_iterator { iter = fs::iterator { next = &iter_next, + finish = &iter_finish, }, fd = fd, buf = buf[..fs.getdents_bufsz], @@ -429,9 +430,6 @@ fn iter_next(iter: *fs::iterator) (fs::dirent | void) = { let n = rt::getdents64(iter.fd, iter.buf: *[*]u8, len(iter.buf)) as size; if (n == 0) { - rt::close(iter.fd)!; - free(iter.buf); - free(iter); return; }; iter.buf_end = n; @@ -466,3 +464,10 @@ fn iter_next(iter: *fs::iterator) (fs::dirent | void) = { ftype = ftype, }; }; + +fn iter_finish(iter: *fs::iterator) void = { + let iter = iter: *os_iterator; + rt::close(iter.fd)!; + free(iter.buf); + free(iter); +}; diff --git a/os/fs.ha b/os/fs.ha @@ -26,6 +26,9 @@ export fn move(oldpath: str, newpath: str) (void | fs::error) = // Creates an [[fs::iterator]] for a given directory to read its contents. export fn iter(path: str) (*fs::iterator | fs::error) = fs::iter(cwd, path); +// Frees state associated with a directory iterator. +export fn finish(iter: *fs::iterator) void = fs::finish(iter); + // Reads all entries from a directory. The caller must free the return value // with [[fs::dirents_free]]. export fn readdir(path: str) ([]fs::dirent | fs::error) = fs::readdir(cwd, path);