summaryrefslogtreecommitdiff
path: root/src/ninep
diff options
context:
space:
mode:
authorGabriel Schneider <[email protected]>2026-09-29 09:56:14 -0300
committerGabriel Schneider <[email protected]>2026-10-01 00:12:16 -0300
commit742d986c1b773baba11b897598e870131b9b874b (patch)
tree5ab65cf14e9bc4fbf4ec30da24c8e289c08f88e4 /src/ninep
parenta72d0c6650242a9b02858de7020e9a4ae387ebba (diff)
downloadpardes-742d986c1b773baba11b897598e870131b9b874b.tar.gz
pardes-742d986c1b773baba11b897598e870131b9b874b.zip
A ctl line that ran and failed is not quoted after its reason; only one refused before running is
Failures of a builtin through a ctl quoted the line after the builtin's own words, which already named it (`Mount dead /x: dial failed: no answer "Mount dead /x"`), while the same failure through exec did not. A line that ran says why in its own words everywhere now; only a line refused as no message at all quotes it, as Plan 9's cmderror does. Co-Authored-By: Claude Opus 5.5 <[email protected]>
Diffstat (limited to 'src/ninep')
-rw-r--r--src/ninep/ctl.zig24
1 files changed, 13 insertions, 11 deletions
diff --git a/src/ninep/ctl.zig b/src/ninep/ctl.zig
index 2b256b1e..cf2068bc 100644
--- a/src/ninep/ctl.zig
+++ b/src/ninep/ctl.zig
@@ -447,11 +447,13 @@ pub fn runBuiltin(p: *Pardes, req: Req, id: usize, line: []const u8) ?Reply {
if (!created) noteResult(p, context);
if (p.fs.refused) return refuse(p, req, e_prompt, line);
if (p.fs.failure_len == 0) return null;
- // An Edit's failure names what failed (the pattern that missed): its
- // line, a program, is not quoted after it.
- const edit = std.mem.startsWith(u8, line, "Edit ") or std.mem.eql(u8, line, "Edit");
- const refusal = if (edit) tree.failText(req.tag, E.IO, p.fs.failure[0..p.fs.failure_len]) else refuse(p, req, p.fs.failure[0..p.fs.failure_len], line);
- return .{ .tag = req.tag, .status = .err, .errno = failureErrno(p.fs.failure[0..p.fs.failure_len]), .ename = refusal.ename };
+ // A line that ran and failed says why in the builtin's own words, which
+ // name what failed (`Mount peer /tmp/s: dial failed: ...`): the line is
+ // not quoted after them, as an exec's is not. Only a line refused before
+ // it runs, as no message at all, quotes it (`unknown control message
+ // "X"`).
+ const failure = p.fs.failure[0..p.fs.failure_len];
+ return tree.failText(req.tag, failureErrno(failure), std.fmt.bufPrint(&p.fs.ename, "{s}", .{failure}) catch failure);
}
const e_prompt = "control message needs its argument";
@@ -774,8 +776,8 @@ pub fn writePane(p: *Pardes, req: Req, pane: *Pane) Reply {
var said_buf: [320]u8 = undefined;
const said = said_buf[0..said_in_ename.len];
@memcpy(said, said_in_ename);
- const r = refuse(p, req, said, line);
- return .{ .tag = req.tag, .status = .err, .errno = E.IO, .ename = r.ename };
+ // It ran and was refused: its words, not the line quoted.
+ return tree.failText(req.tag, E.IO, std.fmt.bufPrint(&p.fs.ename, "{s}", .{said}) catch said);
}
var failed: anyerror = error.Unexpected;
const errno = get(p, pane, &failed);
@@ -1095,7 +1097,7 @@ test "the root ctl reads the settings as a write takes them, and takes the sessi
// its line; the lines before it have taken effect, as in acme.
const failed = wr(p, root_ctl, "Verbose on\nMount x\nVerbose off");
try testing.expectEqual(E.IO, failed.errno());
- try testing.expectEqualStrings("Mount name dial: missing argument \"Mount x\"", failed.reply.ename);
+ try testing.expectEqualStrings("Mount name dial: missing argument", failed.reply.ename);
try testing.expect(p.settings.verbose);
try testing.expect(!p.fs.no_prompt);
try testing.expectEqual(Status.ok, wr(p, root_ctl, "Verbose off").reply.status);
@@ -1453,7 +1455,7 @@ test "get reloads the pane from disk and reports a missing file without losing d
// Unsaved text is asked about once, as acme's get asks winclean.
const asked = wr(p, ctl_node, "get\n");
try testing.expectEqual(E.IO, asked.errno());
- try testing.expect(std.mem.endsWith(u8, asked.reply.ename, ": Modified (get again to discard) \"get\""));
+ try testing.expect(std.mem.endsWith(u8, asked.reply.ename, ": Modified (get again to discard)"));
try testing.expectEqual(E.NOENT, wr(p, ctl_node, "get\n").errno());
try testing.expect(p.paneBySerial(serial) != null);
try testing.expectEqualStrings("from disk\nunsaved\n", pane.file.?.content);
@@ -1701,7 +1703,7 @@ test "a builtin that fails a ctl write logs only its err, so the same failure ag
const log = @intFromEnum(tree.TopFile.log);
const h = call(p, .{ .tag = 1, .op = .open, .node = log }).reply.handle;
const text = call(p, .{ .tag = 2, .op = .read, .node = log, .handle = h, .size = 1 << 16 }).bytes;
- try testing.expect(std.mem.endsWith(u8, text, "err - ctl: Kill: no running command has that first word \"Kill zzz\" (x3)\n"));
+ try testing.expect(std.mem.endsWith(u8, text, "err - ctl: Kill: no running command has that first word (x3)\n"));
try testing.expect(std.mem.indexOf(u8, text, "msg - Kill") == null);
_ = call(p, .{ .tag = 3, .op = .release, .node = log, .handle = h });
}
@@ -1760,7 +1762,7 @@ test "Mount dials its peer and fails, mounting nothing, when none answers; a loo
const dead = "/tmp/pardes-test-no-such-peer.sock";
const refused = wr(p, root_ctl, "Mount dead " ++ dead ++ "\n");
try testing.expectEqual(E.IO, refused.errno());
- try testing.expectEqualStrings("Mount dead " ++ dead ++ ": dial failed: no answer \"Mount dead " ++ dead ++ "\"", refused.reply.ename);
+ try testing.expectEqualStrings("Mount dead " ++ dead ++ ": dial failed: no answer", refused.reply.ename);
try testing.expectEqual(@as(usize, 0), p.fs.mounts.items.len);
// A pane of a peer gone since: get says so too.
try p.fs.mounts.append(p.gpa, .{ .name = try p.gpa.dupe(u8, "away"), .dial = try p.gpa.dupe(u8, dead) });