From 6046b8c67bf6894824d2b7f72c476b9dfd5daa72 Mon Sep 17 00:00:00 2001 From: Gabriel Schneider Date: Tue, 29 Sep 2026 09:59:34 -0300 Subject: Errnos that fit: a Dump into a missing DumpDir and a shell that is not there are ENOENT, a GUI-only setting EINVAL A Dump into a DumpDir not there failed EIO `file not found`; `no shell "x"` read as EIO through a mount; and a GUI-only setting on a terminal, EINVAL in the reply, had no word 9ns reads as EINVAL. Each says so in the words that give its errno: `no such directory`, `not found`, `invalid here`. Co-Authored-By: Claude Opus 5.5 --- docs/fs.md | 5 ++++- src/host_io.zig | 3 ++- src/ninep/ctl.zig | 10 ++++++---- src/pardes.zig | 9 ++++++++- test/snapshots/shellset.golden | 2 +- 5 files changed, 21 insertions(+), 8 deletions(-) diff --git a/docs/fs.md b/docs/fs.md index 8f50a45b..c53f83d6 100644 --- a/docs/fs.md +++ b/docs/fs.md @@ -243,7 +243,10 @@ and reads every setting there is, one a line, in the words a write of it takes (`Verbose on`, `WindowOpacity 70`, `PanelSlide off`, `DumpDir `, `LocationsConfig ...`), so writing what it reads back changes nothing; a setting the frontend cannot show (`Lift`, -`GripWidth` on a terminal) is refused as `Lift is GUI-only`; platform and startup facts are `/status`'s and the +`GripWidth` on a terminal) is refused as `Lift is GUI-only, invalid here` +(EINVAL: a request this build cannot take); a `Shell` or `Tty` naming no +shell says `no shell "x", not found` (ENOENT); a Dump into a `DumpDir` +that is not there says `Dump : no such directory` (ENOENT); platform and startup facts are `/status`'s and the Config window's, not settings. A pane's `ctl` takes the builtins that act on a pane (`Del`, or `Del k`/`Del j` to give its rows to the pane above or below, `Save f`, `Collapse`, which folds that pane, `Undo` and diff --git a/src/host_io.zig b/src/host_io.zig index fbdfb703..913930ac 100644 --- a/src/host_io.zig +++ b/src/host_io.zig @@ -867,7 +867,8 @@ pub const Shell = struct { buf[bin.len] = 0; if (isDirectory(@ptrCast(&buf))) return std.fmt.bufPrint(said, "not a shell: {s} is a directory", .{bin}) catch "not a shell: a directory"; } - return std.fmt.bufPrint(said, "no shell \"{s}\" (a name on the usual paths, or a path to one)", .{bin}) catch "no such shell"; + // "not found": the words a mount reads as ENOENT. + return std.fmt.bufPrint(said, "no shell \"{s}\", not found (a name on the usual paths, or a path to one)", .{bin}) catch "no such shell"; } fn fallback(buf: *[std.fs.max_path_bytes]u8) [*:0]const u8 { diff --git a/src/ninep/ctl.zig b/src/ninep/ctl.zig index 5f26a181..a474c781 100644 --- a/src/ninep/ctl.zig +++ b/src/ninep/ctl.zig @@ -386,7 +386,9 @@ fn checkBuiltin(p: *Pardes, req: Req, line: []const u8, scope: builtins.Scope) ? // terminal) is known, and says so rather than sound misspelt. if (config.Runtime.find(word) != null) { var why: [64]u8 = undefined; - return refuse(p, req, std.fmt.bufPrint(&why, "{s} is GUI-only", .{word}) catch "GUI-only", line); + // EINVAL, a request this build cannot take, in words 9ns reads + // as that: `invalid`. + return refuse(p, req, std.fmt.bufPrint(&why, "{s} is GUI-only, invalid here", .{word}) catch "GUI-only, invalid here", line); } return refuse(p, req, "unknown control message", line); }; @@ -1653,7 +1655,7 @@ test "a Restore of a file that is no dump fails the write before any warning, an p.fs.late_failure_len = 0; p.dumpFailed("/nowhere/pardes.dump.zon", error.FileNotFound); - try testing.expectEqualStrings("Dump /nowhere/pardes.dump.zon: file not found", p.fs.late_failure[0..p.fs.late_failure_len]); + try testing.expectEqualStrings("Dump /nowhere/pardes.dump.zon: no such directory", p.fs.late_failure[0..p.fs.late_failure_len]); } test "a setting this frontend cannot show says GUI-only, and DumpDir reads back the directory in effect" { @@ -1661,8 +1663,8 @@ test "a setting this frontend cannot show says GUI-only, and DumpDir reads back defer p.deinit(); const root_ctl = @intFromEnum(tree.TopFile.ctl); if (!builtins.capabilities.lift) { - try testing.expectEqualStrings("Lift is GUI-only \"Lift on\"", wr(p, root_ctl, "Lift on\n").reply.ename); - try testing.expectEqualStrings("GripWidth is GUI-only \"GripWidth 120\"", wr(p, root_ctl, "GripWidth 120\n").reply.ename); + try testing.expectEqualStrings("Lift is GUI-only, invalid here \"Lift on\"", wr(p, root_ctl, "Lift on\n").reply.ename); + try testing.expectEqualStrings("GripWidth is GUI-only, invalid here \"GripWidth 120\"", wr(p, root_ctl, "GripWidth 120\n").reply.ename); } try testing.expectEqualStrings("unknown control message \"Liftt on\"", wr(p, root_ctl, "Liftt on\n").reply.ename); var buf: [900]u8 = undefined; diff --git a/src/pardes.zig b/src/pardes.zig index 35b44871..225dbf9e 100644 --- a/src/pardes.zig +++ b/src/pardes.zig @@ -5302,7 +5302,14 @@ pub const Pardes = struct { pub fn dumpFailed(p: *Pardes, path: []const u8, err: anyerror) void { var what: [limits.host_path_cap + 8]u8 = undefined; const operation = std.fmt.bufPrint(&what, "Dump {s}", .{path}) catch "Dump"; - p.reportError(p.active, operation, err); + // Into a DumpDir that is not there: said so, ENOENT, as a Save is. + const parent = std.fs.path.dirname(path) orelse "/"; + const no_dir = err == error.FileNotFound or err == error.NotDir or + (if (comptime hosted) (if (filesystem.localPath(parent)) |local| !exec.isDirectory(local) else false) else false); + if (no_dir) { + var said: [limits.host_path_cap + 48]u8 = undefined; + p.reportFailure(p.active, std.fmt.bufPrint(&said, "{s}: no such directory", .{operation}) catch "Dump: no such directory"); + } else p.reportError(p.active, operation, err); // In the words the message row has, so the write's err is the one // record of it (its msg goes: 9p_io, dropMessage). const said = if (p.panes[p.active]) |pane| pane.msg[0..pane.msg_len] else "Dump failed"; diff --git a/test/snapshots/shellset.golden b/test/snapshots/shellset.golden index e2830801..db72bfc8 100644 --- a/test/snapshots/shellset.golden +++ b/test/snapshots/shellset.golden @@ -4,7 +4,7 @@ | /tmp/pardes-snap/shellset/cwd/cmd.txt Save Tty Collapse Del | 1 Shell zznosuchshell == snap builtin-ran grid=90x21 cursor=25,3 -|3: 1 Shel Shell: no shell "zznosuchshell" (a name on the usual paths, or a path to one) +|3: hell: no shell "zznosuchshell", not found (a name on the usual paths, or a path to one) == snap fallback-shell grid=90x21 cursor=49,6 |1: New Tty Find Grep Joincol Delcol New Tty Find Grep Joincol Delcol |2: /tmp/pardes-snap/shellset/cwd/cmd.txt Save /tmp/pardes-snap/shellset/cwd Tty+bash Sav -- cgit v1.3