hare

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

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:
Mfs/fs.ha | 5+----
Mfs/types.ha | 30+++++++++++++++++++-----------
Mfs/util.ha | 16++++++++++++----
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]); + }; +};