diff options
Diffstat (limited to 'src/ninep/ctl.zig')
| -rw-r--r-- | src/ninep/ctl.zig | 27 |
1 files changed, 21 insertions, 6 deletions
diff --git a/src/ninep/ctl.zig b/src/ninep/ctl.zig index 274caf88..8ece7ead 100644 --- a/src/ninep/ctl.zig +++ b/src/ninep/ctl.zig @@ -193,6 +193,7 @@ pub fn command(p: *Pardes, req: Req, serial: ?u32, exec: bool, in_tag: bool) Rep // The root's lines run at the keyboard's pane: a refusal before any runs // is that pane's too, in its err record. p.fs.ran_at = if (serial == null) (if (p.panes[p.active]) |pane| pane.serial else 0) else 0; + if (editNotAlone(p, req.data)) return tree.failText(req.tag, E.INVAL, e_after_edit); for ([2]bool{ false, true }) |apply| { if (apply) p.fs.results_len = 0; var it = Messages.init(p, req.data); @@ -203,7 +204,6 @@ pub fn command(p: *Pardes, req: Req, serial: ?u32, exec: bool, in_tag: bool) Rep for (text) |c| if ((c < ' ' and c != '\t' and c != '\n') or c == 0x7f) return tree.failText(req.tag, E.INVAL, e_control); if (exec) if (tooLong(req, text)) |refusal| return refusal; if (!apply) continue; - if (p.fs.edit_answer_at != null) return tree.failText(req.tag, E.INVAL, e_after_edit); var id = if (serial) |s| p.paneBySerial(s) orelse break else p.active; // No pane at all: the root's look and exec act in the session's // directory. An exec runs as the workspace tag's; a look from a @@ -260,10 +260,25 @@ pub fn tooLong(req: Req, line: []const u8) ?Reply { pub const e_control = "invalid command line: it holds a control character (or DEL) other than a tab"; -/// An Edit whose `<`, `|` or `>` commands are running answers its write -/// when they are done (edit_cmd.zig); a line after it in the same write -/// would run before them, on the text they are about to change. -pub const e_after_edit = "invalid command line: an Edit running commands ends its write; write what follows after it"; +/// An Edit whose `<`, `|` or `>` commands run answers its write when they +/// are done (edit_cmd.zig); a line after it in the same write would run +/// before them, on the text they are about to change. Refused up front, +/// before any line runs. +pub const e_after_edit = "invalid command line: an Edit that runs commands is a write of its own"; + +/// Whether `data` holds an Edit that runs commands and any other line. +fn editNotAlone(p: *Pardes, data: []const u8) bool { + const sam = @import("../sam_edit.zig"); + var lines: usize = 0; + var runs = false; + var it = Messages.init(p, data); + while (it.next()) |line| { + if (line.len == 0 or exec_line.isComment(line)) continue; + lines += 1; + if (std.mem.startsWith(u8, line, "Edit") and sam.runsCommands(p.scratch.allocator(), line["Edit".len..])) runs = true; + } + return runs and lines > 1; +} /// Runs one click (`run`) as a 9P write's: a builtin that fails there fails /// the write, with its words and an err record, and no msg, as a ctl line @@ -959,13 +974,13 @@ pub fn writePane(p: *Pardes, req: Req, pane: *Pane) Reply { const serial = pane.serial; // Checked whole before anything applies, so a write that must wait for // the lock has done nothing yet when it goes again. + if (editNotAlone(p, req.data)) return tree.failText(req.tag, E.INVAL, e_after_edit); for ([2]bool{ false, true }) |apply| { if (apply) p.fs.results_len = 0; var held = !other and pane.fs.lock != null; var it = Messages.init(p, req.data); while (it.next()) |line_in| { if (line_in.len == 0 or exec_line.isComment(line_in)) continue; - if (apply and p.fs.edit_answer_at != null) return tree.failText(req.tag, E.INVAL, e_after_edit); // A line before this one may have closed the pane (`Del`). if (apply and p.paneBySerial(serial) == null) return Reply.fail(req.tag, E.NOENT); // acme's ctl words: done by the file that replaces each, or the |
