From 8dc84e6f9f1c3802f1d28de4d49907381cdf224b Mon Sep 17 00:00:00 2001 From: Gabriel Schneider Date: Thu, 1 Oct 2026 04:00:32 -0300 Subject: pardes - asks /pager for its +Pager and reads the serial on the same open: another client's exec meanwhile can never take the text into its pane pardes - wrote `pager ` to /ctl, then read /exec on a fresh open, which answers the session's last answer: a client that exec'd in between had its pane overwritten by the paged text. /pager takes the directory and answers the +Pager's serial on the open that wrote it, as /pane/new answers its own open; the ctl verb is gone. Client.ask writes and reads on one open. The README's look recipe reads its answer on its own open too. Co-Authored-By: Claude Opus 5.5 --- src/9p_io.zig | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) (limited to 'src/9p_io.zig') diff --git a/src/9p_io.zig b/src/9p_io.zig index b11a4997..9fc19df9 100644 --- a/src/9p_io.zig +++ b/src/9p_io.zig @@ -2319,6 +2319,32 @@ pub const Client = struct { gpa.destroy(s); } + /// Writes `request` to `path` and reads the answer on the same open, + /// as /pane/new answers its own open: a session's last answer, from + /// another client, can never be what comes back. `exec 3<>file`. + pub fn ask(gpa: std.mem.Allocator, dial: []const u8, path: []const u8, request: []const u8) ![]u8 { + if (comptime !supported) return error.Unsupported; + var names: [max_depth][]const u8 = undefined; + const n = try elements(path, &names); + var sock_buf: [sun_path_len]u8 = undefined; + const sock = try resolve(&sock_buf, dial); + var remote: RemoteError = .{}; + errdefer said = remote; + const s = try startSession(gpa, sock, path); + defer endSession(gpa, s); + const fid = (try walkTo(s, names[0..n], &remote)).fid; + _ = try s.ask(.{ .open = .{ .fid = fid, .mode = ninep.ordwr } }, &remote); + _ = try s.ask(.{ .write = .{ .fid = fid, .offset = 0, .data = request } }, &remote); + var out: std.ArrayList(u8) = .empty; + errdefer out.deinit(gpa); + while (true) { + const data = (try s.ask(.{ .read = .{ .fid = fid, .offset = out.items.len, .count = s.cl.maxRead() } }, &remote)).read; + if (data.len == 0) return out.toOwnedSlice(gpa); + if (out.items.len + data.len > 4096) return Error.FileTooLarge; + try out.appendSlice(gpa, data); + } + } + /// Opens `path` on one connection and writes `first` to it (/log's /// `follow new`), all with the usual deadline; then, once `ready(ctx)` /// has said it is not done already, reads it with no deadline, a read at -- cgit v1.3