diff options
Diffstat (limited to 'src/ninep/pty.zig')
| -rw-r--r-- | src/ninep/pty.zig | 63 |
1 files changed, 15 insertions, 48 deletions
diff --git a/src/ninep/pty.zig b/src/ninep/pty.zig index ddb7bcd1..706163e5 100644 --- a/src/ninep/pty.zig +++ b/src/ninep/pty.zig @@ -109,15 +109,12 @@ pub fn readStatus(p: *Pardes, req: Req, id: usize, pane: *Pane) Reply { // command. The shell's own OSC 133 marks say when the command started (C) // and ended (D, with its status); pardes injects them into bash and fish. -pub const run_slots = 32; - /// The most of a command's output an answer carries: its last this-many /// bytes, since that is where a failure says what went wrong. pub const output_cap = 64 * 1024; +/// What an open of run holds (tree.zig's open records). pub const Run = struct { - /// 0 is a free slot. - node: u64 = 0, phase: enum { idle, sent, done } = .idle, /// The shell's command count its C must reach for the end to be ours. want: u32 = 0, @@ -139,29 +136,8 @@ pub const Run = struct { len: u8 = 0, /// Then what the command printed, gpa-owned. output: []u8 = &.{}, - - pub fn reset(slot: *Run, gpa: std.mem.Allocator, node: u64) void { - gpa.free(slot.output); - slot.* = .{ .node = node }; - } }; -fn runOf(p: *Pardes, req: Req) ?*Run { - if (req.handle == 0 or req.handle > run_slots) return null; - const slot = &p.fs.runs[req.handle - 1]; - return if (slot.node == req.node) slot else null; -} - -pub fn openRun(p: *Pardes, req: Req, serial: u32) Reply { - _ = serial; - for (&p.fs.runs, 0..) |*slot, i| { - if (slot.node != 0) continue; - slot.* = .{ .node = req.node }; - return .{ .tag = req.tag, .handle = @intCast(i + 1) }; - } - return Reply.fail(req.tag, E.NFILE); -} - fn answer(p: *Pardes, slot: *Run, comptime fmt: []const u8, args: anytype) void { slot.len = @intCast((std.fmt.bufPrint(&slot.answer, fmt ++ "\n", args) catch unreachable).len); slot.phase = .done; @@ -169,9 +145,11 @@ fn answer(p: *Pardes, slot: *Run, comptime fmt: []const u8, args: anytype) void } pub fn writeRun(p: *Pardes, req: Req, id: usize, pane: *Pane) Reply { - const slot = runOf(p, req) orelse return Reply.fail(req.tag, E.INVAL); + const slot = &(tree.openOf(p, req) orelse return Reply.fail(req.tag, E.INVAL)).what.run; if (slot.phase == .sent) return tree.failText(req.tag, E.BUSY, tree.e_in_use); - slot.reset(p.gpa, slot.node); // a new line on this open: a new answer + // A new line on this open: a new answer. + p.gpa.free(slot.output); + slot.* = .{}; const line = std.mem.trimEnd(u8, req.data, "\r\n"); if (std.mem.trim(u8, line, " \t").len == 0) return tree.failText(req.tag, E.INVAL, e_bad_line); if (line.len == 0) return tree.failText(req.tag, E.INVAL, e_bad_line); @@ -197,13 +175,13 @@ pub fn writeRun(p: *Pardes, req: Req, id: usize, pane: *Pane) Reply { p.emitWrite(id, line); p.emitWrite(id, "\r"); slot.phase = .sent; - pf.run = @intCast(slot - &p.fs.runs[0]); + pf.run = @intCast(req.handle - 1); } return .{ .tag = req.tag, .written = @intCast(req.data.len) }; } pub fn readRun(p: *Pardes, req: Req) Reply { - const slot = runOf(p, req) orelse return Reply.fail(req.tag, E.INVAL); + const slot = &(tree.openOf(p, req) orelse return Reply.fail(req.tag, E.INVAL)).what.run; switch (slot.phase) { .idle => return .{ .tag = req.tag, .payload = .{ .staged = 0 } }, .sent => return .{ .tag = req.tag, .status = .again }, @@ -226,17 +204,6 @@ pub fn readRun(p: *Pardes, req: Req) Reply { return .{ .tag = req.tag, .payload = .{ .staged = @intCast(out.items.len) } }; } -pub fn releaseRun(p: *Pardes, req: Req) Reply { - const slot = runOf(p, req) orelse return .{ .tag = req.tag }; - const idx: u8 = @intCast(slot - &p.fs.runs[0]); - // The command runs on in the shell; nobody is waiting for it any more. - for (p.panes) |live| if (live) |pane| if (pane.fs.run == idx) { - pane.fs.run = null; - }; - slot.reset(p.gpa, 0); - return .{ .tag = req.tag }; -} - /// After each chunk of a terminal's output: once the command the run sent /// has ended and the shell is back at a prompt, the run answers how. Waiting /// for the prompt means whoever reads the answer can send the next command @@ -245,7 +212,7 @@ pub fn noteMarks(p: *Pardes, id: usize, pane: *Pane) void { if (comptime !pardes.panes.Terminal.enabled) return; const state = pane.terminal orelse return; const pf = &pane.fs; - const slot = &p.fs.runs[pf.run orelse return]; + const slot = &p.fs.opens[pf.run orelse return].what.run; const marks = &state.stream.handler; if (marks.phase != .input) return; // ponytail: a prompt redrawn before the command starts (a resize in that @@ -325,7 +292,7 @@ pub fn noteMarks(p: *Pardes, id: usize, pane: *Pane) void { /// be reported, so the run says so instead of waiting forever. pub fn shellGone(p: *Pardes, pane: *Pane) void { const idx = pane.fs.run orelse return; - answer(p, &p.fs.runs[idx], "error shell gone", .{}); + answer(p, &p.fs.opens[idx].what.run, "error shell gone", .{}); pane.fs.run = null; } @@ -387,7 +354,7 @@ test "a pane that is not a terminal has no pty/ at all" { .op = .open, .node = Node.of(serial, .pty_data), }).errno()); - try testing.expectEqual(@as(u16, 0), p.panes[0].?.fs.pty_readers); + try testing.expect(!p.panes[0].?.fs.pty_reader); var buf: [32]th.Dirent = undefined; const files = th.dirents(rdir(p, dir, 0).bytes, &buf); @@ -578,11 +545,11 @@ test "pty/data writes at the shell and reads the raw stream" { // An open that only writes is not a reader: nothing queues for it. const writer = call(p, .{ .tag = 4, .op = .open, .node = data, .omode = 1 }); - try testing.expectEqual(@as(u16, 0), p.panes[0].?.fs.pty_readers); + try testing.expect(!p.panes[0].?.fs.pty_reader); _ = call(p, .{ .tag = 4, .op = .release, .node = data, .handle = writer.reply.handle }); const reader = call(p, .{ .tag = 5, .op = .open, .node = data }); - try testing.expectEqual(@as(u16, 1), p.panes[0].?.fs.pty_readers); + try testing.expect(p.panes[0].?.fs.pty_reader); try testing.expectEqual(@as(u16, 0), p.fs.listeners); try testing.expect(p.panes[0].?.fs.readers == 0); @@ -602,7 +569,7 @@ test "pty/data writes at the shell and reads the raw stream" { p.update(.{ .output = .{ .pane = 0, .bytes = "orphan" } }); while (p.nextEffect()) |_| {} _ = call(p, .{ .tag = 6, .op = .release, .node = data, .handle = reader.reply.handle }); - try testing.expectEqual(@as(u16, 0), p.panes[0].?.fs.pty_readers); + try testing.expect(!p.panes[0].?.fs.pty_reader); try testing.expectEqual(@as(usize, 0), p.panes[0].?.fs.pty_out.buf.capacity); try testing.expectEqual(Status.again, rd(p, data, 0, 64).reply.status); @@ -611,7 +578,7 @@ test "pty/data writes at the shell and reads the raw stream" { const second = call(p, .{ .tag = 8, .op = .open, .node = data, .omode = 2 }); try testing.expectEqual(Status.err, second.reply.status); try testing.expectEqualStrings(tree.e_in_use, second.reply.ename); - try testing.expectEqual(@as(u16, 1), p.panes[0].?.fs.pty_readers); + try testing.expect(p.panes[0].?.fs.pty_reader); p.update(.{ .output = .{ .pane = 0, .bytes = "still" } }); while (p.nextEffect()) |_| {} try testing.expectEqualStrings("still", rd(p, data, 0, 64).bytes); @@ -717,7 +684,7 @@ test "run answers how its command ended once the shell is back at a prompt" { try p.removePane(0, null); try testing.expectEqualStrings("error shell gone\n", call(p, .{ .tag = 19, .op = .read, .node = node, .handle = gone.reply.handle, .size = 64 }).bytes); _ = call(p, .{ .tag = 20, .op = .release, .node = node, .handle = gone.reply.handle }); - for (p.fs.runs) |slot| try testing.expect(slot.node == 0); + for (p.fs.opens) |o| try testing.expect(o.node == 0); } test "the pty queue drops the oldest at its cap" { |
