diff options
| author | Gabriel Schneider <[email protected]> | 2026-09-29 02:15:43 -0300 |
|---|---|---|
| committer | Gabriel Schneider <[email protected]> | 2026-10-01 00:12:15 -0300 |
| commit | 9391d35d357ebf18759c10f6c756855e5a8f7b50 (patch) | |
| tree | 181645499d1728c12806dabb1ee65cb61e9f5a41 /src | |
| parent | 5492da0ba1d5f1c1cc0a880f82107961627c9ba6 (diff) | |
| download | pardes-9391d35d357ebf18759c10f6c756855e5a8f7b50.tar.gz pardes-9391d35d357ebf18759c10f6c756855e5a8f7b50.zip | |
A limit write that fails keeps the limit it had
A shell's > truncates limit, which lifted it, before the write; a write
that then failed left no limit at all. The truncation now keeps the old
limit until that open's write, and a failed write puts it back; a bare
truncation still lifts it once its open is released.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Diffstat (limited to 'src')
| -rw-r--r-- | src/ninep/pane.zig | 28 | ||||
| -rw-r--r-- | src/ninep/tree.zig | 4 |
2 files changed, 31 insertions, 1 deletions
diff --git a/src/ninep/pane.zig b/src/ninep/pane.zig index 902f4f2a..e7b72c88 100644 --- a/src/ninep/pane.zig +++ b/src/ninep/pane.zig @@ -41,6 +41,9 @@ pub const State = struct { /// rather than act at the address before it, which acme would do. addr_failed: bool = false, limit: ?Range = null, + /// The limit a truncation cleared, kept until the write that follows it + /// on that open (a shell's `>`): a write that fails puts it back. + limit_truncated: ?Range = null, /// Opens of `event`, which hold the pane scripted. readers: u16 = 0, /// One of `readers` reads `event`; a second reading open is refused. @@ -523,8 +526,13 @@ fn writeRange(req: Req, pane: *Pane, file: PaneFile) Reply { }; const range = r orelse { if (file == .addr) pf.addr_failed = true; + if (file == .limit) if (pf.limit_truncated) |kept| { + pf.limit = kept; + pf.limit_truncated = null; + }; return tree.failText(req.tag, E.INVAL, a.err); }; + if (file == .limit) pf.limit_truncated = null; switch (file) { .addr => { pf.addr = range; @@ -684,7 +692,10 @@ pub fn truncate(p: *Pardes, pane: *Pane, file: PaneFile) tree.Status { // is evaluated from where the last one left off, as with `>>`. // `0` (or `,`) is how to start over. See State.addr. .addr => {}, - .limit => pf.limit = null, + .limit => { + pf.limit_truncated = pf.limit; + pf.limit = null; + }, .dot => if (fileOf(pane) != null) setDot(pane, .{}), else => {}, } @@ -1236,3 +1247,18 @@ test "addresses are in runes: a combining mark and a lone \\r are places of thei _ = wr(p, addr_node, "#4,#5"); try testing.expectEqualStrings("\r", rd(p, Node.of(serial, .xdata), 0, 100).bytes); } + +test "a limit write that fails keeps the limit a truncation cleared before it" { + const gpa = testing.allocator; + const p = try withFile(gpa, "one two\n"); + defer p.deinit(); + const limit = Node.of(serialOf(p), .limit); + _ = wr(p, limit, "#0,#3"); + _ = call(p, .{ .tag = 1, .op = .setattr, .node = limit, .truncate = true }); + try testing.expectEqual(Status.err, wr(p, limit, "/nomatch/").reply.status); + try testing.expectEqualStrings(" 0 3 ", rd(p, limit, 0, 100).bytes); + // A truncation with no write after it clears it. + _ = call(p, .{ .tag = 2, .op = .setattr, .node = limit, .truncate = true }); + _ = call(p, .{ .tag = 3, .op = .release, .node = limit }); + try testing.expectEqualStrings("", rd(p, limit, 0, 100).bytes); +} diff --git a/src/ninep/tree.zig b/src/ninep/tree.zig index da9c37f0..278732b5 100644 --- a/src/ninep/tree.zig +++ b/src/ninep/tree.zig @@ -806,6 +806,10 @@ fn releaseHandle(p: *Pardes, req: Req) void { p.panes[id].?.fs.tag_rewrite = false; p.panes[id].?.fs.tag_held_newline = false; }; + // `: > limit` clears it for good once its open goes. + if (t == .pane and t.pane.file == .limit) if (p.paneBySerial(t.pane.serial)) |id| { + p.panes[id].?.fs.limit_truncated = null; + }; if (t == .col and t.col.file == .tag) cols.released(p, t.col.serial); if (t == .top and t.top == .tag) cols.released(p, null); const o = openOf(p, req) orelse return; |
