diff options
| author | Gabriel Schneider <[email protected]> | 2026-09-28 13:00:06 -0300 |
|---|---|---|
| committer | Gabriel Schneider <[email protected]> | 2026-10-01 00:12:14 -0300 |
| commit | 047a2b50a7ac0162d7ef38f912d41c15a47479c7 (patch) | |
| tree | a2fb902819402ac11093a23cf5dc72f2bae68f0f /src/ninep/events.zig | |
| parent | 6fc9f416938b59b665b1fddc052402770a21cc3f (diff) | |
| download | pardes-047a2b50a7ac0162d7ef38f912d41c15a47479c7.tar.gz pardes-047a2b50a7ac0162d7ef38f912d41c15a47479c7.zip | |
A line written to a pane's own look or exec goes to the event reader holding the pane
A script writing to pane/N/exec bypassed the client holding that pane's event
file, which a middle click on it would not: acme sends a window's clicks to its
event reader (look.c:53, exec.c:173). Such a line is now an F record at 0 0
carrying its text; the root's look and exec still act at once. acme takes back
only origin, action and range, which a record with no place in the text cannot
use, so the whole record as read is taken too, and for an empty range acts on
its text.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Diffstat (limited to 'src/ninep/events.zig')
| -rw-r--r-- | src/ninep/events.zig | 21 |
1 files changed, 17 insertions, 4 deletions
diff --git a/src/ninep/events.zig b/src/ninep/events.zig index 1cab5605..3f36c65f 100644 --- a/src/ninep/events.zig +++ b/src/ninep/events.zig @@ -468,7 +468,7 @@ pub fn notePtyOutput(p: *Pardes, id: usize, bytes: []const u8) void { p.fs.news = true; } -const EventRecord = struct { action: Action, q0: u32, q1: u32 }; +const EventRecord = struct { action: Action, q0: u32, q1: u32, text: ?[]const u8 = null }; const EventReader = struct { data: []const u8, @@ -484,9 +484,20 @@ const EventReader = struct { const q0 = scanNumber(er.data, &i) orelse return null; const q1 = scanNumber(er.data, &i) orelse return null; while (i < er.data.len and er.data[i] == ' ') i += 1; + // acme takes only `origin action q0 q1`; the record whole, as read, + // is taken too, so a click with no place in the text (one written + // to a pane's own look or exec) can be written back and done. + var text: ?[]const u8 = null; + if (i < er.data.len and er.data[i] != '\n') { + _ = scanNumber(er.data, &i) orelse return null; + const n = scanNumber(er.data, &i) orelse return null; + if (i >= er.data.len or er.data[i] != ' ' or er.data.len - (i + 1) < n) return null; + text = er.data[i + 1 ..][0..n]; + i += 1 + n; + } if (i >= er.data.len or er.data[i] != '\n') return null; er.i = i + 1; - return .{ .action = action, .q0 = q0, .q1 = q1 }; + return .{ .action = action, .q0 = q0, .q1 = q1, .text = text }; } }; @@ -525,7 +536,8 @@ pub fn writeEvent(p: *Pardes, req: Req, id: usize) Reply { const whole = if (r.action.onTag()) pane_files.tagOf(p, pane) else pane_files.bodyOf(pane); const lo = @min(@as(usize, r.q0), whole.len); const hi = @max(lo, @min(@as(usize, r.q1), whole.len)); - const text = p.scratch.allocator().dupe(u8, whole[lo..hi]) catch continue; + const said = if (r.q0 == r.q1) r.text orelse whole[lo..hi] else whole[lo..hi]; + const text = p.scratch.allocator().dupe(u8, said) catch continue; switch (r.action) { .body_exec, .tag_exec => _ = exec.execute(p, live, text), .body_look, .tag_look => look.lookAt(p, live, text), @@ -672,7 +684,8 @@ test "a pane deleted while its event file is open leaves no suppression behind" const b = call(p, .{ .tag = 19, .op = .open, .node = event, .omode = 1 }); try testing.expectEqual(@as(u16, 2), p.fs.listeners); - _ = wr(p, Node.of(serial, .exec), "Del\n"); + // Through ctl: a line written to its exec would go to the reader. + _ = wr(p, Node.of(serial, .ctl), "Del\n"); try testing.expect(p.paneBySerial(serial) == null); try testing.expectEqual(@as(u16, 0), p.fs.listeners); // The reader's next read hears what acme says of a window gone under it. |
