diff options
| author | Gabriel Schneider <[email protected]> | 2026-10-01 10:23:36 -0300 |
|---|---|---|
| committer | Gabriel Schneider <[email protected]> | 2026-10-01 10:36:28 -0300 |
| commit | 040620344ee8901582d944dbd04f8167f0a6cbc0 (patch) | |
| tree | 9a793361f6647f2b3d1ec2544838b79e31411ac0 /src | |
| parent | 3f76d4d88245e94a266c24dc23bbba78a00570a3 (diff) | |
| download | pardes-040620344ee8901582d944dbd04f8167f0a6cbc0.tar.gz pardes-040620344ee8901582d944dbd04f8167f0a6cbc0.zip | |
`answer z` quotes `z`, a bare `name` says it names nothing, and `Repl klingon` is invalid (EINVAL), its message citing no doc path
A refused answer quoted the word `answer`, not the choice refused. A
`name` with nothing after it was an unknown control message. A Repl of
no such language failed EIO with a pointer into the reference; it now
says `invalid language` (EINVAL, here and through a mount) and how many
more there are. A builtin's failure that says invalid anywhere is
EINVAL, as 9ns reads it.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Diffstat (limited to 'src')
| -rw-r--r-- | src/9p_io.zig | 2 | ||||
| -rw-r--r-- | src/builtins.zig | 4 | ||||
| -rw-r--r-- | src/ninep/ctl.zig | 27 | ||||
| -rw-r--r-- | src/pardes.zig | 4 |
4 files changed, 26 insertions, 11 deletions
diff --git a/src/9p_io.zig b/src/9p_io.zig index dd755f82..9a38b438 100644 --- a/src/9p_io.zig +++ b/src/9p_io.zig @@ -341,7 +341,7 @@ pub const Listener = struct { // builtin's failure saying so is (ctl.failureErrno). const errno = if (std.mem.indexOf(u8, late, "no such") != null or std.mem.indexOf(u8, late, "not found") != null) pardes.ctlfs.E.NOENT - else if (std.mem.indexOf(u8, late, "no space") != null) pardes.ctlfs.E.NOSPC else if (std.mem.indexOf(u8, late, "permission denied") != null) pardes.ctlfs.E.PERM else pardes.ctlfs.E.IO; + else if (std.mem.indexOf(u8, late, "no space") != null) pardes.ctlfs.E.NOSPC else if (std.mem.indexOf(u8, late, "permission denied") != null) pardes.ctlfs.E.PERM else if (std.mem.indexOf(u8, late, "invalid") != null) pardes.ctlfs.E.INVAL else pardes.ctlfs.E.IO; const failed = pardes.ctlfs.failText(req.tag, errno, late); // Its err record says it (the path in it), the one record: it // was said with no msg while this waited (fs.write_waits). diff --git a/src/builtins.zig b/src/builtins.zig index 907650bd..b9cf4840 100644 --- a/src/builtins.zig +++ b/src/builtins.zig @@ -1368,7 +1368,7 @@ pub const Repl = struct { // A few, whole, and where the rest are: a list cut to fit the // row would end mid-word. var w = std.Io.Writer.fixed(&buf); - w.print("Repl: no language \"{s}\"; - or one like", .{@import("Messages.zig").clip(arg, 24)}) catch {}; + w.print("Repl: invalid language \"{s}\"; - or one like", .{@import("Messages.zig").clip(arg, 24)}) catch {}; // Those that start as the word does first, a misspelling's // likeliest meaning (pithon: python). var shown: usize = 0; @@ -1382,7 +1382,7 @@ pub const Repl = struct { shown += 1; } }; - w.print(" ({d} more: docs/typ/reference.typ, REPLs)", .{known - shown}) catch {}; + w.print(" ({d} more)", .{known - shown}) catch {}; return c.p.reportFailure(c.id, w.buffered()); }; if (pane.repl) |r| if (r.lang == lang) diff --git a/src/ninep/ctl.zig b/src/ninep/ctl.zig index 06eb8cdd..697ef594 100644 --- a/src/ninep/ctl.zig +++ b/src/ninep/ctl.zig @@ -300,7 +300,7 @@ pub fn failureErrno(failure: []const u8) u16 { if (std.mem.indexOf(u8, failure, "no such") != null or std.mem.indexOf(u8, failure, "not found") != null) return E.NOENT; // A pattern refused (Edit's too) is malformed input, as 9ns reads it; // so is a word this build has not (`invalid: acme's Put ...`). - if (std.mem.indexOf(u8, failure, "bad regular expression") != null or std.mem.startsWith(u8, failure, "invalid") or + if (std.mem.indexOf(u8, failure, "bad regular expression") != null or std.mem.indexOf(u8, failure, "invalid") != null or std.mem.startsWith(u8, failure, "address out of range")) return E.INVAL; // A word's arguments wrong, or a dial that is no address: what 9ns // reads as EINVAL from the same words (`control message`, `bad `). @@ -997,7 +997,8 @@ pub fn writePane(p: *Pardes, req: Req, pane: *Pane) Reply { // else leaves it standing, and says what it takes. if (!std.mem.eql(u8, choice, "-") and (choice.len != 1 or std.mem.indexOfScalar(u8, choices, choice[0]) == null)) { var why: [96]u8 = undefined; - return refuse(p, req, std.fmt.bufPrint(&why, "answer takes {s} or -", .{choices}) catch "answer takes a choice or -", line); + // The choice refused is what is quoted: `answer takes a b or - "z"`. + return refuseQuoting(p, req, std.fmt.bufPrint(&why, "answer takes {s} or -", .{choices}) catch "answer takes a choice or -", choice); } if (apply) _ = p.answerQuestion(id, if (choice[0] == '-') null else choice[0]); } else if (!apply) { @@ -1032,6 +1033,9 @@ fn acmeCtl(p: *Pardes, req: Req, serial: u32, line: []const u8, apply: bool) Acm // name's first byte: a line's words are split at blanks. if (std.mem.eql(u8, word, "name") and rest.len > 0 and line[word.len + 1] == ' ') return .{ .refused = tree.failText(req.tag, E.INVAL, "bad character in file name: a blank at its start") }; + // `name` with nothing after it names nothing: said so, not an unknown word. + if (std.mem.eql(u8, word, "name") and rest.len == 0) + return .{ .refused = tree.failText(req.tag, E.INVAL, "invalid file name: name takes the name, and there is none") }; const into: ?Into = if (std.mem.eql(u8, word, "name") and rest.len > 0) .{ .file = .name, .data = line[word.len + 1 ..] } else if (std.mem.eql(u8, line, "clean")) @@ -2356,7 +2360,7 @@ test "a setting this frontend cannot show says GUI-only, and DumpDir reads back if (comptime !pardes.font_picker) { try testing.expectEqualStrings("Fonts is GUI-only, invalid here \"Fonts\"", wr(p, root_ctl, "Fonts\n").reply.ename); const ran = wr(p, root_exec, "Fonts\n"); - try testing.expectEqual(E.IO, ran.errno()); + try testing.expectEqual(E.INVAL, ran.errno()); try testing.expectEqualStrings("Fonts is GUI-only, invalid here", ran.reply.ename); } } @@ -3697,13 +3701,22 @@ test "size is monotonic: growing is never refused, and a size once taken is take try testing.expectStringStartsWith(bad.reply.ename, "invalid size"); } -test "Repl with no such language names a few whole, and where the rest are" { +test "Repl with no such language is invalid, EINVAL, and names a few whole and how many more" { const p = try th.withTerm(testing.allocator); defer p.deinit(); const refused = wr(p, Node.of(serialOf(p), .ctl), "Repl zzlang\n"); - try testing.expectEqual(Status.err, refused.reply.status); - try testing.expect(std.mem.indexOf(u8, refused.reply.ename, "Repl: no language \"zzlang\"; - or one like zig ada bash c c_sharp clojure (") != null); - try testing.expect(std.mem.indexOf(u8, refused.reply.ename, " more: docs/typ/reference.typ, REPLs)") != null); + try testing.expectEqual(E.INVAL, refused.errno()); + try testing.expect(std.mem.indexOf(u8, refused.reply.ename, "Repl: invalid language \"zzlang\"; - or one like zig ada bash c c_sharp clojure (") != null); + try testing.expect(std.mem.endsWith(u8, refused.reply.ename, " more)")); +} + +test "answer quotes the choice it refuses, and a bare name says it names nothing" { + const p = try withFile(testing.allocator, "x\n"); + defer p.deinit(); + const node = Node.of(serialOf(p), .ctl); + const empty = wr(p, node, "name \n"); + try testing.expectEqual(E.INVAL, empty.errno()); + try testing.expectEqualStrings("invalid file name: name takes the name, and there is none", empty.reply.ename); } test "a 10k-line Edit text block is taken in linear time and memory" { diff --git a/src/pardes.zig b/src/pardes.zig index 1659a94b..803686e0 100644 --- a/src/pardes.zig +++ b/src/pardes.zig @@ -1695,7 +1695,9 @@ test "a REPL bound for a language takes the exec made on a body in it; tags, Exe var want: [32]u8 = undefined; try std.testing.expect(th.logHas(p, try std.fmt.bufPrint(&want, "ask {d} repl a b\n", .{file.serial}))); // A choice not offered is refused, and the question stands. - try std.testing.expectEqual(ctlfs.E.INVAL, th.wr(p, ctlfs.Node.of(file.serial, .ctl), "answer z\n").errno()); + const refused = th.wr(p, ctlfs.Node.of(file.serial, .ctl), "answer z\n"); + try std.testing.expectEqual(ctlfs.E.INVAL, refused.errno()); + try std.testing.expect(std.mem.endsWith(u8, refused.reply.ename, " \"z\"")); try std.testing.expectEqual(Pane.Prompt.repl_choice, file.prompt); const answered = th.wr(p, ctlfs.Node.of(file.serial, .ctl), "answer a\n"); try std.testing.expectEqual(ctlfs.Status.ok, answered.reply.status); |
