commit 58ef0c237d0ae8b982e2485abebaa7fe289dd5d2
parent 6d9e6bedacd0af928d990eedc9271c52ca916138
Author: Drew DeVault <sir@cmpwn.com>
Date: Wed, 24 Feb 2021 13:08:42 -0500
fs: add dirent, fix iter & readdir
We don't typically have detailed stat information during readdir, so
this structure is more conservative.
Diffstat:
3 files changed, 32 insertions(+), 19 deletions(-)
diff --git a/fs/fs.ha b/fs/fs.ha
@@ -55,7 +55,4 @@ export fn subdir(fs: *fs, path: path) (*fs | error) = {
// It is a programming error to call this again after it has returned void. The
// file stat returned may only have the type bits set on the file mode; callers
// should call [fs::stat] to obtain the detailed file mode.
-//
-// 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);
+export fn next(iter: *iterator) (dirent | void) = iter.next(iter);
diff --git a/fs/types.ha b/fs/types.ha
@@ -89,12 +89,8 @@ export type stat_mask = enum uint {
// Information about a file or directory. The mask field defines what other
// fields are set; mode and path are always set.
-//
-// The path string may be 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: path,
mode: mode,
uid: uint,
gid: uint,
@@ -102,10 +98,22 @@ 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 = match (new.path) {
+// An entry in a directory. This may be borrowed from the filesystem's internal
+// state; if you want to keep this around beyond one call to [next], use
+// [dirent_dup].
+export type dirent = struct {
+ // The name of this entry. Not fully qualified: for example,
+ // "foo/bar/baz.txt" would store "baz.txt" here.
+ name: path,
+
+ // The type of this entry. The permission bits may be unset.
+ ftype: mode,
+};
+
+// Duplicates a [dirent] object. Call [dirent_free] to get rid of it later.
+export fn dirent_dup(e: dirent) dirent = {
+ let new = e;
+ new.name = match (new.name) {
s: str => strings::dup(s),
b: []u8 => {
let n: []u8 = [];
@@ -116,8 +124,8 @@ export fn stat_dup(f: filestat) filestat = {
return new;
};
-// Frees a [filestat] object which was duplicated with [stat_dup].
-export fn stat_free(f: filestat) void = match (f.path) {
+// Frees a [dirent] object which was duplicated with [dirent_dup].
+export fn dirent_free(e: dirent) void = match (e.name) {
s: str => free(s),
b: []u8 => free(b),
};
@@ -157,7 +165,7 @@ export type fs = struct {
subdir: nullable *subdirfunc,
};
-export type nextfunc = fn(iter: *iterator) (filestat | void);
+export type nextfunc = fn(iter: *iterator) (dirent | void);
export type iterator = struct {
// Returns the next member of the directory, or void if there are none
diff --git a/fs/util.ha b/fs/util.ha
@@ -78,15 +78,23 @@ 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) = {
+// Reads all entries from a directory. The caller must free the return value
+// with [dirents_free].
+export fn readdir(fs: *fs, path: path) ([]dirent | error) = {
let i = iter(fs, path)?;
- let ents: []filestat = [];
+ let ents: []dirent = [];
for (true) {
match (next(i)) {
- f: filestat => append(ents, stat_dup(f)),
+ d: dirent => append(ents, dirent_dup(d)),
void => break,
};
};
return ents;
};
+
+// Frees a slice of [dirent]s.
+export fn dirents_free(d: []dirent) void = {
+ for (let i = 0z; i < len(d); i += 1) {
+ dirent_free(d[i]);
+ };
+};