diff options
Diffstat (limited to 'src/ninep/pane.zig')
| -rw-r--r-- | src/ninep/pane.zig | 46 |
1 files changed, 30 insertions, 16 deletions
diff --git a/src/ninep/pane.zig b/src/ninep/pane.zig index 822e0704..0a511118 100644 --- a/src/ninep/pane.zig +++ b/src/ninep/pane.zig @@ -35,6 +35,10 @@ pub const State = struct { /// register, cleared by truncating the file, so that `cp addr dot` and /// `cat addr` answer what was written. addr: Range = .{}, + /// The last address written to `addr` failed, so there is none: `data` + /// and `xdata` refuse until one is written or `addr` is truncated, + /// rather than act at the address before it, which acme would do. + addr_failed: bool = false, limit: ?Range = null, /// Opens of `event`, which hold the pane scripted. readers: u16 = 0, @@ -262,6 +266,7 @@ pub fn read(p: *Pardes, req: Req, id: usize, pane: *Pane, file: PaneFile) Reply }, .ctl => ctl.readPane(p, req, pane), .addr => addr: { + if (pf.addr_failed) break :addr tree.failText(req.tag, E.INVAL, e_addr_failed); clampAddr(pf, bodyOf(pane).len); break :addr readRange(p, req, pf.addr); }, @@ -319,6 +324,7 @@ fn readBody(p: *Pardes, req: Req, id: usize, pane: *Pane) Reply { } fn readData(req: Req, id: usize, pane: *Pane, pf: *State, stop_at_end: bool) Reply { + if (pf.addr_failed) return tree.failText(req.tag, E.INVAL, e_addr_failed); const text = bodyOf(pane); clampAddr(pf, text.len); const q0: usize = pf.addr.q0; @@ -406,6 +412,7 @@ fn writeTag(req: Req, pane: *Pane) Reply { fn writeData(p: *Pardes, req: Req, pane: *Pane) Reply { if (fileOf(pane) == null) return Reply.fail(req.tag, E.INVAL); const pf = &pane.fs; + if (pf.addr_failed) return tree.failText(req.tag, E.INVAL, e_addr_failed); clampAddr(pf, bodyOf(pane).len); const q0: usize = pf.addr.q0; const q1: usize = @max(q0, @as(usize, pf.addr.q1)); @@ -442,30 +449,34 @@ fn pairOf(text: []const u8) ?State.Range { return .{ .q0 = q0, .q1 = @max(q0, q1) }; } -/// A range file takes an address expression, or that pair of offsets. -fn rangeOf(pf: *State, text: []const u8, data: []const u8) ?State.Range { - const expr = std.mem.trimEnd(u8, data, "\n"); - if (pairOf(expr)) |r| { - const n = clip(text.len); - return .{ .q0 = @min(r.q0, n), .q1 = @min(r.q1, n) }; - } - var a: addressing.Addr = .{ .text = text, .lim = pf.limit, .expr = expr }; - const r = a.address(pf.addr) orelse return null; - return if (a.i < expr.len) null else r; -} +pub const e_addr_failed = "no address: the last one written to addr failed"; +/// A range file takes an address expression, or that pair of offsets. One +/// that does not evaluate says why: `bad address syntax`, `no match for +/// regexp`, `address out of range`, `bad regular expression`. fn writeRange(req: Req, pane: *Pane, file: PaneFile) Reply { const pf = &pane.fs; const text = bodyOf(pane); clampAddr(pf, text.len); - const r = rangeOf(pf, text, req.data) orelse return tree.failText(req.tag, E.INVAL, tree.e_bad_addr); + const expr = std.mem.trimEnd(u8, req.data, "\n"); + var a: addressing.Addr = .{ .text = text, .lim = pf.limit, .expr = expr }; + const r = if (pairOf(expr)) |pair| + State.Range{ .q0 = @min(pair.q0, clip(text.len)), .q1 = @min(pair.q1, clip(text.len)) } + else if (a.address(pf.addr)) |found| (if (a.i < expr.len) null else found) else null; + const range = r orelse { + if (file == .addr) pf.addr_failed = true; + return tree.failText(req.tag, E.INVAL, a.err); + }; switch (file) { - .addr => pf.addr = r, - .limit => pf.limit = r, + .addr => { + pf.addr = range; + pf.addr_failed = false; + }, + .limit => pf.limit = range, // Setting dot scrolls to it, which is the whole of acme's `show`. .dot => { if (fileOf(pane) == null) return Reply.fail(req.tag, E.INVAL); - setDot(pane, r); + setDot(pane, range); }, else => unreachable, } @@ -601,7 +612,10 @@ pub fn truncate(p: *Pardes, pane: *Pane, file: PaneFile) tree.Status { pane.tag.vsel.active = false; pane.tag.nsel = 0; }, - .addr => pf.addr = .{}, + .addr => { + pf.addr = .{}; + pf.addr_failed = false; + }, .limit => pf.limit = null, .dot => if (fileOf(pane) != null) setDot(pane, .{}), else => {}, |
