hare

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

commit 858e98e6a733b11c7befea2690cbe5952886de10
parent f9afe6f169063ed90a1df8234cc9c6373aba45d3
Author: Sertonix <sertonix@posteo.net>
Date:   Wed, 13 Dec 2023 19:57:58 +0000

os: handle errors on file system iteration

Implements: https://todo.sr.ht/~sircmpwn/hare/914
Signed-off-by: Sertonix <sertonix@posteo.net>

Diffstat:
Mcmd/hare/build/gather.ha | 2+-
Mcmd/hare/cache.ha | 2+-
Mcmd/haredoc/doc/types.ha | 6+++++-
Mcmd/haredoc/doc/util.ha | 2+-
Mcmd/parsechk/main.ha | 2+-
Mfs/fs.ha | 9+++++----
Mfs/types.ha | 6+++++-
Mglob/glob.ha | 9++++++---
Mhare/module/srcs.ha | 2+-
Mhare/module/util.ha | 4++--
Mmakefiles/freebsd.aarch64.mk | 2+-
Mmakefiles/freebsd.riscv64.mk | 2+-
Mmakefiles/freebsd.x86_64.mk | 2+-
Mmakefiles/linux.aarch64.mk | 2+-
Mmakefiles/linux.riscv64.mk | 2+-
Mmakefiles/linux.x86_64.mk | 2+-
Mmakefiles/openbsd.aarch64.mk | 2+-
Mmakefiles/openbsd.riscv64.mk | 2+-
Mmakefiles/openbsd.x86_64.mk | 2+-
Mos/+freebsd/dirfdfs.ha | 13+++++++++----
Mos/+linux/dirfdfs.ha | 13+++++++++----
Mos/+openbsd/dirfdfs.ha | 13+++++++++----
22 files changed, 64 insertions(+), 37 deletions(-)

diff --git a/cmd/hare/build/gather.ha b/cmd/hare/build/gather.ha @@ -51,7 +51,7 @@ fn gather_submodules( let n = 0z; let it = os::iter(path::string(buf))?; defer fs::finish(it); - for (true) match (module::next(it)) { + for (true) match (module::next(it)?) { case void => break; case let dir: fs::dirent => diff --git a/cmd/hare/cache.ha b/cmd/hare/cache.ha @@ -51,7 +51,7 @@ fn dirsize(buf: *path::buffer) (size | error) = { let s = 0z; let it = os::iter(path::string(buf))?; defer fs::finish(it); - for (true) match (fs::next(it)) { + for (true) match (fs::next(it)?) { case void => break; case let d: fs::dirent => diff --git a/cmd/haredoc/doc/types.ha b/cmd/haredoc/doc/types.ha @@ -1,6 +1,7 @@ // SPDX-License-Identifier: GPL-3.0-only // (c) Hare authors <https://harelang.org> +use encoding::utf8; use fmt; use fs; use hare::ast; @@ -14,7 +15,8 @@ use os::exec; export type haredoc_colors_error = !str; export type error = !(lex::error | parse::error | doc::error | io::error | - module::error | exec::error | fs::error | haredoc_colors_error); + module::error | exec::error | fs::error | haredoc_colors_error | + utf8::invalid); export fn strerror(err: error) str = { match (err) { @@ -32,6 +34,8 @@ export fn strerror(err: error) str = { return exec::strerror(err); case let err: fs::error => return fs::strerror(err); + case let err: utf8::invalid => + return utf8::strerror(err); case let err: haredoc_colors_error => def ERRMSG = "Error parsing HAREDOC_COLORS: invalid key"; if (len(err) == 0) { diff --git a/cmd/haredoc/doc/util.ha b/cmd/haredoc/doc/util.ha @@ -34,7 +34,7 @@ export fn submodules(path: str, show_undocumented: bool) ([]str | error) = { let it = os::iter(path)?; defer fs::finish(it); let pathbuf = path::init(path)!; - for (true) match (module::next(it)) { + for (true) match (module::next(it)?) { case let d: fs::dirent => path::set(&pathbuf, path, d.name, "README")!; if (show_undocumented || os::exists(path::string(&pathbuf))) { diff --git a/cmd/parsechk/main.ha b/cmd/parsechk/main.ha @@ -22,7 +22,7 @@ export fn main() void = { fn iter(buf: *path::buffer, status: *int) void = { let it = os::iter(path::string(buf))!; defer fs::finish(it); - for (true) match (fs::next(it)) { + for (true) match (fs::next(it)!) { case void => break; case let ent: fs::dirent => diff --git a/fs/fs.ha b/fs/fs.ha @@ -302,7 +302,8 @@ export fn symlink(fs: *fs, target: str, path: str) (void | error) = { // Returns the next directory entry from an iterator, or void if none remain. // '.' and '..' are skipped. 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 [[stat]] to obtain the detailed file -// mode. -export fn next(iter: *iterator) (dirent | void) = iter.next(iter); +// it has returned void. Calling this again after an error is safe. The list is +// not guaranteed to be complete when an error has been returned. The file stat +// returned may only have the type bits set on the file mode; callers should +// call [[stat]] to obtain the detailed file mode. +export fn next(iter: *iterator) (dirent | void | error) = iter.next(iter); diff --git a/fs/types.ha b/fs/types.ha @@ -1,6 +1,7 @@ // SPDX-License-Identifier: MPL-2.0 // (c) Hare authors <https://harelang.org> +use encoding::utf8; use errors; use io; use path; @@ -25,6 +26,7 @@ export type error = !( errors::busy | errors::invalid | errors::unsupported | + utf8::invalid | wrongtype | cannotrename | io::error); @@ -47,6 +49,8 @@ case errors::busy => yield "Device is busy"; case errors::unsupported => yield "Operation not supported"; +case let err: utf8::invalid => + yield utf8::strerror(err); case let err: io::error => yield io::strerror(err); }; @@ -304,7 +308,7 @@ export type fs = struct { }; // A function which returns the next directory from an [[iterator]]. -export type nextfunc = fn(iter: *iterator) (dirent | void); +export type nextfunc = fn(iter: *iterator) (dirent | void | error); // A function which frees state associated with an [[iterator]]. export type finishfunc = fn(iter: *iterator) void; diff --git a/glob/glob.ha b/glob/glob.ha @@ -94,11 +94,9 @@ export fn next(gen: *generator) (str | void | failure) = { && len(memio::string(&gen.tmpp.dir)!) == 0 && len(memio::string(&gen.tmpp.pat)!) == 0 && len(memio::string(&gen.tmpp.rem)!) == 0; - match (next_match(gen)) { + match (next_match(gen)?) { case let s: str => return s; - case let f: failure => - return f; case void => void; }; if (init && gen.flgs & flag::NOCHECK != 0) { @@ -188,6 +186,11 @@ fn next_match(gen: *generator) (str | void | failure) = { }; }; gen.matc += 1; + case let e: fs::error => + return failure { + path = dir, + error = e, + }; }; if (gen.flgs & flag::NOSORT == 0) { strstack_sort(&gen.pats, l); diff --git a/hare/module/srcs.ha b/hare/module/srcs.ha @@ -277,7 +277,7 @@ fn _findsrcs( return attach(strings::dup(pathstr), e); }; defer fs::finish(iter); - for (true) match (fs::next(iter)) { + for (true) match (fs::next(iter)?) { case void => break; case let d: fs::dirent => diff --git a/hare/module/util.ha b/hare/module/util.ha @@ -44,8 +44,8 @@ export fn outdated(target: str, deps: []str, mtime: time::instant) bool = { }; // Wrapper for [[fs::next]] that only returns valid submodule directories. -export fn next(it: *fs::iterator) (fs::dirent | void) = { - for (true) match (fs::next(it)) { +export fn next(it: *fs::iterator) (fs::dirent | void | fs::error) = { + for (true) match (fs::next(it)?) { case void => return void; case let d: fs::dirent => diff --git a/makefiles/freebsd.aarch64.mk b/makefiles/freebsd.aarch64.mk @@ -118,7 +118,7 @@ $(HARECACHE)/time.ssa: $(time_ha) $(HARECACHE)/math.td $(HARECACHE)/rt.td $(HARE @$(TDENV) $(HAREC) $(HARECFLAGS) -o $(HARECACHE)/time.ssa -t $(HARECACHE)/time.td.tmp -N time $(time_ha) fs_ha = fs/fs.ha fs/types.ha fs/util.ha -$(HARECACHE)/fs.ssa: $(fs_ha) $(HARECACHE)/errors.td $(HARECACHE)/io.td $(HARECACHE)/path.td $(HARECACHE)/strings.td $(HARECACHE)/time.td +$(HARECACHE)/fs.ssa: $(fs_ha) $(HARECACHE)/encoding_utf8.td $(HARECACHE)/errors.td $(HARECACHE)/io.td $(HARECACHE)/path.td $(HARECACHE)/strings.td $(HARECACHE)/time.td @mkdir -p -- "$(HARECACHE)" @printf 'HAREC\t%s\n' "$@" @$(TDENV) $(HAREC) $(HARECFLAGS) -o $(HARECACHE)/fs.ssa -t $(HARECACHE)/fs.td.tmp -N fs $(fs_ha) diff --git a/makefiles/freebsd.riscv64.mk b/makefiles/freebsd.riscv64.mk @@ -118,7 +118,7 @@ $(HARECACHE)/time.ssa: $(time_ha) $(HARECACHE)/math.td $(HARECACHE)/rt.td $(HARE @$(TDENV) $(HAREC) $(HARECFLAGS) -o $(HARECACHE)/time.ssa -t $(HARECACHE)/time.td.tmp -N time $(time_ha) fs_ha = fs/fs.ha fs/types.ha fs/util.ha -$(HARECACHE)/fs.ssa: $(fs_ha) $(HARECACHE)/errors.td $(HARECACHE)/io.td $(HARECACHE)/path.td $(HARECACHE)/strings.td $(HARECACHE)/time.td +$(HARECACHE)/fs.ssa: $(fs_ha) $(HARECACHE)/encoding_utf8.td $(HARECACHE)/errors.td $(HARECACHE)/io.td $(HARECACHE)/path.td $(HARECACHE)/strings.td $(HARECACHE)/time.td @mkdir -p -- "$(HARECACHE)" @printf 'HAREC\t%s\n' "$@" @$(TDENV) $(HAREC) $(HARECFLAGS) -o $(HARECACHE)/fs.ssa -t $(HARECACHE)/fs.td.tmp -N fs $(fs_ha) diff --git a/makefiles/freebsd.x86_64.mk b/makefiles/freebsd.x86_64.mk @@ -118,7 +118,7 @@ $(HARECACHE)/time.ssa: $(time_ha) $(HARECACHE)/math.td $(HARECACHE)/rt.td $(HARE @$(TDENV) $(HAREC) $(HARECFLAGS) -o $(HARECACHE)/time.ssa -t $(HARECACHE)/time.td.tmp -N time $(time_ha) fs_ha = fs/fs.ha fs/types.ha fs/util.ha -$(HARECACHE)/fs.ssa: $(fs_ha) $(HARECACHE)/errors.td $(HARECACHE)/io.td $(HARECACHE)/path.td $(HARECACHE)/strings.td $(HARECACHE)/time.td +$(HARECACHE)/fs.ssa: $(fs_ha) $(HARECACHE)/encoding_utf8.td $(HARECACHE)/errors.td $(HARECACHE)/io.td $(HARECACHE)/path.td $(HARECACHE)/strings.td $(HARECACHE)/time.td @mkdir -p -- "$(HARECACHE)" @printf 'HAREC\t%s\n' "$@" @$(TDENV) $(HAREC) $(HARECFLAGS) -o $(HARECACHE)/fs.ssa -t $(HARECACHE)/fs.td.tmp -N fs $(fs_ha) diff --git a/makefiles/linux.aarch64.mk b/makefiles/linux.aarch64.mk @@ -142,7 +142,7 @@ $(HARECACHE)/time.ssa: $(time_ha) $(HARECACHE)/linux_vdso.td $(HARECACHE)/math.t @$(TDENV) $(HAREC) $(HARECFLAGS) -o $(HARECACHE)/time.ssa -t $(HARECACHE)/time.td.tmp -N time $(time_ha) fs_ha = fs/fs.ha fs/types.ha fs/util.ha -$(HARECACHE)/fs.ssa: $(fs_ha) $(HARECACHE)/errors.td $(HARECACHE)/io.td $(HARECACHE)/path.td $(HARECACHE)/strings.td $(HARECACHE)/time.td +$(HARECACHE)/fs.ssa: $(fs_ha) $(HARECACHE)/encoding_utf8.td $(HARECACHE)/errors.td $(HARECACHE)/io.td $(HARECACHE)/path.td $(HARECACHE)/strings.td $(HARECACHE)/time.td @mkdir -p -- "$(HARECACHE)" @printf 'HAREC\t%s\n' "$@" @$(TDENV) $(HAREC) $(HARECFLAGS) -o $(HARECACHE)/fs.ssa -t $(HARECACHE)/fs.td.tmp -N fs $(fs_ha) diff --git a/makefiles/linux.riscv64.mk b/makefiles/linux.riscv64.mk @@ -142,7 +142,7 @@ $(HARECACHE)/time.ssa: $(time_ha) $(HARECACHE)/linux_vdso.td $(HARECACHE)/math.t @$(TDENV) $(HAREC) $(HARECFLAGS) -o $(HARECACHE)/time.ssa -t $(HARECACHE)/time.td.tmp -N time $(time_ha) fs_ha = fs/fs.ha fs/types.ha fs/util.ha -$(HARECACHE)/fs.ssa: $(fs_ha) $(HARECACHE)/errors.td $(HARECACHE)/io.td $(HARECACHE)/path.td $(HARECACHE)/strings.td $(HARECACHE)/time.td +$(HARECACHE)/fs.ssa: $(fs_ha) $(HARECACHE)/encoding_utf8.td $(HARECACHE)/errors.td $(HARECACHE)/io.td $(HARECACHE)/path.td $(HARECACHE)/strings.td $(HARECACHE)/time.td @mkdir -p -- "$(HARECACHE)" @printf 'HAREC\t%s\n' "$@" @$(TDENV) $(HAREC) $(HARECFLAGS) -o $(HARECACHE)/fs.ssa -t $(HARECACHE)/fs.td.tmp -N fs $(fs_ha) diff --git a/makefiles/linux.x86_64.mk b/makefiles/linux.x86_64.mk @@ -142,7 +142,7 @@ $(HARECACHE)/time.ssa: $(time_ha) $(HARECACHE)/linux_vdso.td $(HARECACHE)/math.t @$(TDENV) $(HAREC) $(HARECFLAGS) -o $(HARECACHE)/time.ssa -t $(HARECACHE)/time.td.tmp -N time $(time_ha) fs_ha = fs/fs.ha fs/types.ha fs/util.ha -$(HARECACHE)/fs.ssa: $(fs_ha) $(HARECACHE)/errors.td $(HARECACHE)/io.td $(HARECACHE)/path.td $(HARECACHE)/strings.td $(HARECACHE)/time.td +$(HARECACHE)/fs.ssa: $(fs_ha) $(HARECACHE)/encoding_utf8.td $(HARECACHE)/errors.td $(HARECACHE)/io.td $(HARECACHE)/path.td $(HARECACHE)/strings.td $(HARECACHE)/time.td @mkdir -p -- "$(HARECACHE)" @printf 'HAREC\t%s\n' "$@" @$(TDENV) $(HAREC) $(HARECFLAGS) -o $(HARECACHE)/fs.ssa -t $(HARECACHE)/fs.td.tmp -N fs $(fs_ha) diff --git a/makefiles/openbsd.aarch64.mk b/makefiles/openbsd.aarch64.mk @@ -118,7 +118,7 @@ $(HARECACHE)/time.ssa: $(time_ha) $(HARECACHE)/math.td $(HARECACHE)/rt.td $(HARE @$(TDENV) $(HAREC) $(HARECFLAGS) -o $(HARECACHE)/time.ssa -t $(HARECACHE)/time.td.tmp -N time $(time_ha) fs_ha = fs/fs.ha fs/types.ha fs/util.ha -$(HARECACHE)/fs.ssa: $(fs_ha) $(HARECACHE)/errors.td $(HARECACHE)/io.td $(HARECACHE)/path.td $(HARECACHE)/strings.td $(HARECACHE)/time.td +$(HARECACHE)/fs.ssa: $(fs_ha) $(HARECACHE)/encoding_utf8.td $(HARECACHE)/errors.td $(HARECACHE)/io.td $(HARECACHE)/path.td $(HARECACHE)/strings.td $(HARECACHE)/time.td @mkdir -p -- "$(HARECACHE)" @printf 'HAREC\t%s\n' "$@" @$(TDENV) $(HAREC) $(HARECFLAGS) -o $(HARECACHE)/fs.ssa -t $(HARECACHE)/fs.td.tmp -N fs $(fs_ha) diff --git a/makefiles/openbsd.riscv64.mk b/makefiles/openbsd.riscv64.mk @@ -118,7 +118,7 @@ $(HARECACHE)/time.ssa: $(time_ha) $(HARECACHE)/math.td $(HARECACHE)/rt.td $(HARE @$(TDENV) $(HAREC) $(HARECFLAGS) -o $(HARECACHE)/time.ssa -t $(HARECACHE)/time.td.tmp -N time $(time_ha) fs_ha = fs/fs.ha fs/types.ha fs/util.ha -$(HARECACHE)/fs.ssa: $(fs_ha) $(HARECACHE)/errors.td $(HARECACHE)/io.td $(HARECACHE)/path.td $(HARECACHE)/strings.td $(HARECACHE)/time.td +$(HARECACHE)/fs.ssa: $(fs_ha) $(HARECACHE)/encoding_utf8.td $(HARECACHE)/errors.td $(HARECACHE)/io.td $(HARECACHE)/path.td $(HARECACHE)/strings.td $(HARECACHE)/time.td @mkdir -p -- "$(HARECACHE)" @printf 'HAREC\t%s\n' "$@" @$(TDENV) $(HAREC) $(HARECFLAGS) -o $(HARECACHE)/fs.ssa -t $(HARECACHE)/fs.td.tmp -N fs $(fs_ha) diff --git a/makefiles/openbsd.x86_64.mk b/makefiles/openbsd.x86_64.mk @@ -118,7 +118,7 @@ $(HARECACHE)/time.ssa: $(time_ha) $(HARECACHE)/math.td $(HARECACHE)/rt.td $(HARE @$(TDENV) $(HAREC) $(HARECFLAGS) -o $(HARECACHE)/time.ssa -t $(HARECACHE)/time.td.tmp -N time $(time_ha) fs_ha = fs/fs.ha fs/types.ha fs/util.ha -$(HARECACHE)/fs.ssa: $(fs_ha) $(HARECACHE)/errors.td $(HARECACHE)/io.td $(HARECACHE)/path.td $(HARECACHE)/strings.td $(HARECACHE)/time.td +$(HARECACHE)/fs.ssa: $(fs_ha) $(HARECACHE)/encoding_utf8.td $(HARECACHE)/errors.td $(HARECACHE)/io.td $(HARECACHE)/path.td $(HARECACHE)/strings.td $(HARECACHE)/time.td @mkdir -p -- "$(HARECACHE)" @printf 'HAREC\t%s\n' "$@" @$(TDENV) $(HAREC) $(HARECFLAGS) -o $(HARECACHE)/fs.ssa -t $(HARECACHE)/fs.td.tmp -N fs $(fs_ha) diff --git a/os/+freebsd/dirfdfs.ha b/os/+freebsd/dirfdfs.ha @@ -372,11 +372,16 @@ fn fs_iter(fs: *fs::fs, path: str) (*fs::iterator | fs::error) = { return &iter.iter; }; -fn iter_next(iter: *fs::iterator) (fs::dirent | void) = { +fn iter_next(iter: *fs::iterator) (fs::dirent | void | fs::error) = { let iter = iter: *os_iterator; if (iter.buf_pos >= iter.buf_end) { - let n = rt::getdents(iter.fd, - iter.buf: *[*]u8, len(iter.buf)) as size; + let n = match (rt::getdents(iter.fd, + iter.buf: *[*]u8, len(iter.buf))) { + case let err: rt::errno => + return errno_to_fs(err); + case let n: size => + yield n; + }; if (n == 0) { return; }; @@ -385,7 +390,7 @@ fn iter_next(iter: *fs::iterator) (fs::dirent | void) = { }; let de = &iter.buf[iter.buf_pos]: *rt::freebsd11_dirent; iter.buf_pos += de.d_reclen; - let name = c::tostr(&de.d_name: *const c::char)!; + let name = c::tostr(&de.d_name: *const c::char)?; if (name == "." || name == "..") { return iter_next(iter); }; diff --git a/os/+linux/dirfdfs.ha b/os/+linux/dirfdfs.ha @@ -415,11 +415,16 @@ fn fs_iter(fs: *fs::fs, path: str) (*fs::iterator | fs::error) = { return &iter.iter; }; -fn iter_next(iter: *fs::iterator) (fs::dirent | void) = { +fn iter_next(iter: *fs::iterator) (fs::dirent | void | fs::error) = { let iter = iter: *os_iterator; if (iter.buf_pos >= iter.buf_end) { - let n = rt::getdents64(iter.fd, - iter.buf: *[*]u8, len(iter.buf)) as size; + let n = match (rt::getdents64(iter.fd, + iter.buf: *[*]u8, len(iter.buf))) { + case let err: rt::errno => + return errno_to_fs(err); + case let n: size => + yield n; + }; if (n == 0) { return; }; @@ -428,7 +433,7 @@ fn iter_next(iter: *fs::iterator) (fs::dirent | void) = { }; let de = &iter.buf[iter.buf_pos]: *rt::dirent64; iter.buf_pos += de.d_reclen; - let name = c::tostr(&de.d_name: *const c::char)!; + let name = c::tostr(&de.d_name: *const c::char)?; if (name == "." || name == "..") { return iter_next(iter); }; diff --git a/os/+openbsd/dirfdfs.ha b/os/+openbsd/dirfdfs.ha @@ -175,13 +175,18 @@ type os_iterator = struct { buf: []u8, }; -fn iter_next(iter: *fs::iterator) (fs::dirent | void) = { +fn iter_next(iter: *fs::iterator) (fs::dirent | void | fs::error) = { let iter = iter: *os_iterator; for (true) { if (iter.buf_pos >= iter.buf_end) { - let n = rt::getdents(iter.fd, - iter.buf: *[*]u8, len(iter.buf)) as int; + let n = match (rt::getdents(iter.fd, + iter.buf: *[*]u8, len(iter.buf))) { + case let err: rt::errno => + return errno_to_fs(err); + case let n: int => + yield n; + }; if (n == 0) { return; }; @@ -197,7 +202,7 @@ fn iter_next(iter: *fs::iterator) (fs::dirent | void) = { continue; }; - let name = c::tostr(&de.d_name: *const c::char)!; + let name = c::tostr(&de.d_name: *const c::char)?; if (name == "." || name == "..") { continue; };