diff options
Diffstat (limited to 'src/ninep')
| -rw-r--r-- | src/ninep/ctl.zig | 42 |
1 files changed, 31 insertions, 11 deletions
diff --git a/src/ninep/ctl.zig b/src/ninep/ctl.zig index 945119ca..e47dec16 100644 --- a/src/ninep/ctl.zig +++ b/src/ninep/ctl.zig @@ -180,7 +180,8 @@ fn refuse(p: *Pardes, req: Req, why: []const u8, line: []const u8) Reply { /// Checks one line written to a ctl as a builtin of `scope` before any line /// of the write runs: a word the registry knows, of this ctl's scope, given -/// an argument only if it takes one, and for a setting a value it takes. +/// an argument if and only if it takes or requires one, and for a setting a +/// value it takes. /// Answers the refusal, or null. Words are the builtins' own, capitalised /// as on a tag; acme's lowercase verbs are the pane ctl's and alias none. fn checkBuiltin(p: *Pardes, req: Req, line: []const u8, scope: builtins.Scope) ?Reply { @@ -191,6 +192,7 @@ fn checkBuiltin(p: *Pardes, req: Req, line: []const u8, scope: builtins.Scope) ? if (builtins.registry.scope(b) != scope) return refuse(p, req, if (scope == .pane) "not a window control message" else "not a session control message", line); if (arg.len > 0 and !builtins.registry.takesArg(b)) return refuse(p, req, "wrong #args in control message", line); + if (arg.len == 0 and builtins.registry.requiresArg(b)) return refuse(p, req, "wrong #args in control message", line); const setting = config.Runtime.find(word) orelse return null; const takes = switch (setting.action) { .theme => for (pardes.themes) |t| { @@ -206,14 +208,19 @@ fn checkBuiltin(p: *Pardes, req: Req, line: []const u8, scope: builtins.Scope) ? } /// Runs a checked control message at pane `id`, as a click on its word -/// would; false when it would have opened a prompt for an argument, which -/// nobody writing to a file is there to type. -fn runBuiltin(p: *Pardes, id: usize, line: []const u8) bool { +/// would. Answers the refusal when it would have opened a prompt for an +/// argument, which nobody writing to a file is there to type, or when it +/// reported an error: `Mount: AlreadyMounted "Mount peer /tmp/s"`. +fn runBuiltin(p: *Pardes, req: Req, id: usize, line: []const u8) ?Reply { p.fs.no_prompt = true; p.fs.refused = false; + p.fs.failure_len = 0; defer p.fs.no_prompt = false; _ = exec_line.executeBuiltinLine(p, id, line); - return !p.fs.refused; + if (p.fs.refused) return refuse(p, req, e_prompt, line); + if (p.fs.failure_len == 0) return null; + const refusal = refuse(p, req, p.fs.failure[0..p.fs.failure_len], line); + return .{ .tag = req.tag, .status = .err, .errno = E.IO, .ename = refusal.ename }; } const e_prompt = "control message needs its argument"; @@ -288,7 +295,7 @@ pub fn writeRoot(p: *Pardes, req: Req) Reply { continue; } if (p.panes[p.active] == null) return Reply.fail(req.tag, E.NOENT); - if (!runBuiltin(p, p.active, line)) return tree.failText(req.tag, E.INVAL, e_prompt); + if (runBuiltin(p, req, p.active, line)) |refusal| return refusal; } } return .{ .tag = req.tag, .written = @intCast(req.data.len) }; @@ -404,8 +411,8 @@ pub fn writePane(p: *Pardes, req: Req, pane: *Pane) Reply { } } else if (!apply) { if (checkBuiltin(p, req, line, .pane)) |refusal| return refusal; - } else if (!runBuiltin(p, p.paneBySerial(serial).?, line)) { - return tree.failText(req.tag, E.INVAL, e_prompt); + } else if (runBuiltin(p, req, p.paneBySerial(serial).?, line)) |refusal| { + return refusal; } } } @@ -542,13 +549,15 @@ test "the pane ctl takes acme's verbs and the pane's builtins, and refuses the r try testing.expectEqual(Status.ok, wr(p, ctl_node, "Msg from ctl\n").reply.status); const pane = p.panes[p.paneBySerial(serial).?].?; try testing.expectEqualStrings("from ctl", pane.msg[0..pane.msg_len]); - // One that would ask at a prompt for its argument fails instead. - try testing.expectEqualStrings(e_prompt, wr(p, ctl_node, "Find").reply.ename); + // One that means nothing bare is refused before anything runs. + try testing.expectEqualStrings("wrong #args in control message \"Find\"", wr(p, ctl_node, "Find").reply.ename); + try testing.expectEqual(E.INVAL, wr(p, ctl_node, "Msg first\nMsg").errno()); + try testing.expect(!std.mem.eql(u8, pane.msg[0..pane.msg_len], "first")); try testing.expect(pane.prompt == .none); try testing.expect(!p.fs.no_prompt); // Save with no name on a scratch would ask for one: refused, not asked. const other = try th.newPane(p); - try testing.expectEqualStrings(e_prompt, wr(p, Node.of(other, .ctl), "Save").reply.ename); + try testing.expectEqualStrings(e_prompt ++ " \"Save\"", wr(p, Node.of(other, .ctl), "Save").reply.ename); try testing.expect(p.panes[p.paneBySerial(other).?].?.prompt == .none); // A line after the one that closed the pane has nowhere to run. try testing.expectEqual(E.NOENT, wr(p, Node.of(other, .ctl), "Del\nMsg after").errno()); @@ -587,7 +596,18 @@ test "the root ctl reads the settings as a write takes them, and takes the sessi try testing.expectEqualStrings("bad value in control message \"Verbose maybe\"", wr(p, root_ctl, "Verbose maybe").reply.ename); try testing.expectEqualStrings("bad value in control message \"Theme no-such-theme\"", wr(p, root_ctl, "Theme no-such-theme").reply.ename); try testing.expectEqualStrings("wrong #args in control message \"Newcol 2\"", wr(p, root_ctl, "Newcol 2").reply.ename); + try testing.expectEqualStrings("wrong #args in control message \"Theme\"", wr(p, root_ctl, "Theme").reply.ename); + // A bare required word fails the check, so the line before never runs. + try testing.expectEqual(E.INVAL, wr(p, root_ctl, "Verbose on\nMount").errno()); try testing.expect(!p.settings.verbose); + // A builtin that fails as it runs fails the write, quoting its error and + // 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: MissingArgument \"Mount x\"", 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); } |
