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:
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);