summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorGabriel Schneider <[email protected]>2026-10-01 10:23:36 -0300
committerGabriel Schneider <[email protected]>2026-10-01 10:36:28 -0300
commit040620344ee8901582d944dbd04f8167f0a6cbc0 (patch)
tree9a793361f6647f2b3d1ec2544838b79e31411ac0 /src
parent3f76d4d88245e94a266c24dc23bbba78a00570a3 (diff)
downloadpardes-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.zig2
-rw-r--r--src/builtins.zig4
-rw-r--r--src/ninep/ctl.zig27
-rw-r--r--src/pardes.zig4
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);