commit c86cf0f40804bbbc02c1d2d8496b8b20535aa816
parent b2f00d7ab64d1058f3f5be593563d096b4e0d710
Author: Drew DeVault <sir@cmpwn.com>
Date: Wed, 24 Feb 2021 11:25:41 -0500
fs: add iterator details
Diffstat:
3 files changed, 46 insertions(+), 4 deletions(-)
diff --git a/fs/fs.ha b/fs/fs.ha
@@ -25,7 +25,8 @@ export fn create(fs: *fs, path: path, mode: io::mode) (*io::stream | error) = {
};
// Returns an iterator for a path, which yields the contents of a directory.
-// Pass empty string to yield from the root.
+// Pass empty string to yield from the root. The order in which entries are
+// returned is undefined.
export fn iter(fs: *fs, path: path) (*iterator | error) = {
return match (fs.iter) {
null => io::unsupported,
@@ -49,3 +50,9 @@ export fn subdir(fs: *fs, path: path) (*fs | error) = {
f: *subdirfunc => f(fs, path),
};
};
+
+// Returns the next directory entry from an interator, or void if none remain.
+//
+// Use [stat_dup] if you need to keep the filestat around for longer than the
+// next call to [next].
+export fn next(iter: *iterator) (filestat | void) = iter.next(iter);
diff --git a/fs/types.ha b/fs/types.ha
@@ -1,4 +1,5 @@
use io;
+use strings;
// A path or path component.
export type path = (str | []u8);
@@ -81,9 +82,14 @@ export type stat_mask = enum uint {
};
// Information about a file or directory. The mask field defines what other
-// fields are set; mode is always set.
+// fields are set; mode and path are always set.
+//
+// The path string is typically borrowed from the filesystem's internal state;
+// if you want to keep this around for any length of time you should use
+// [stat_dup].
export type filestat = struct {
mask: stat_mask,
+ path: str,
mode: mode,
uid: uint,
gid: uint,
@@ -91,6 +97,16 @@ export type filestat = struct {
// TODO: atime et al
};
+// Duplicates a [filestat] object. Call [stat_free] to get rid of it later.
+export fn stat_dup(f: filestat) filestat = {
+ let new = f;
+ new.path = strings::dup(new.path);
+ return new;
+};
+
+// Frees a [filestat] object which was duplicated with [stat_dup].
+export fn stat_free(f: filestat) void = free(f.path);
+
export type closefunc = fn(fs: *fs) void;
export type openfunc = fn(fs: *fs, path: path, mode: io::mode) (*io::stream | error);
export type createfunc = fn(fs: *fs, path: path, mode: io::mode) (*io::stream | error);
@@ -111,6 +127,9 @@ export type fs = struct {
// Returns an iterator for a path, which yields the contents of a
// directory. Pass empty string to yield from the root.
+ //
+ // The iterator must return all entries without error. If an error would
+ // occur, it should be identified here and returned upfront.
iter: nullable *iterfunc,
// Obtains information about a file or directory. If the target is a
@@ -121,7 +140,10 @@ export type fs = struct {
subdir: nullable *subdirfunc,
};
-// TODO
+export type nextfunc = fn(iter: *iterator) (filestat | void);
+
export type iterator = struct {
- placeholder: int,
+ // Returns the next member of the directory, or void if there are none
+ // remaining.
+ next: *nextfunc,
};
diff --git a/fs/util.ha b/fs/util.ha
@@ -33,3 +33,16 @@ export fn is_link(mode: mode) bool = mode & mode::LINK == mode::LINK;
// Returns true if this item is a Unix socket.
export fn is_socket(mode: mode) bool = mode & mode::SOCK == mode::SOCK;
+
+// Reads all entries from a directory.
+export fn readdir(fs: *fs, path: path) ([]filestat | error) = {
+ let i = iter(fs, path)?;
+ let ents: []filestat = [];
+ for (true) {
+ match (next(i)) {
+ f: filestat => append(ents, stat_dup(f)),
+ void => break,
+ };
+ };
+ return ents;
+};