summaryrefslogtreecommitdiff
path: root/src/ninep
diff options
context:
space:
mode:
authorGabriel Schneider <[email protected]>2026-09-28 09:55:21 -0300
committerGabriel Schneider <[email protected]>2026-10-01 00:12:14 -0300
commitf5c48e98b80ad6517c756e8fd15c976d4a2d1894 (patch)
tree5816a604bd76d44186d3301508841fbb10a22ef0 /src/ninep
parent12d387e4a03204a2b6d0c51633e228063e883fad (diff)
downloadpardes-f5c48e98b80ad6517c756e8fd15c976d4a2d1894.tar.gz
pardes-f5c48e98b80ad6517c756e8fd15c976d4a2d1894.zip
Inline the one-line helpers in src/ninep at their call sites
reads, paneFileNamed, topFileNamed, serialNamed, attrReply, makePane, events.pending, mtimeOf, versionOf, dirOf, cellOf, shiftBy, flagOf, stageIndexLine, ctlLen, listenersLen, activeSerial, fontName and Serials.has each wrapped a line or two used once or twice; the logic now reads where it is used. Helpers that do several things or are shared widely (bodyOf, fileOf, dirtyOf, nameOf, kindOf, clip, readRange, readFlag, pty's answer and waitingRun, nextSerialAfter) stay. Co-Authored-By: Claude Opus 5.5 <[email protected]>
Diffstat (limited to 'src/ninep')
-rw-r--r--src/ninep/ctl.zig22
-rw-r--r--src/ninep/events.zig7
-rw-r--r--src/ninep/pane.zig67
-rw-r--r--src/ninep/pty.zig5
-rw-r--r--src/ninep/screen.zig9
-rw-r--r--src/ninep/tree.zig100
6 files changed, 72 insertions, 138 deletions
diff --git a/src/ninep/ctl.zig b/src/ninep/ctl.zig
index 14f1ae4f..945119ca 100644
--- a/src/ninep/ctl.zig
+++ b/src/ninep/ctl.zig
@@ -38,11 +38,6 @@ const Serials = struct {
};
return s;
}
-
- fn has(s: *const Serials, serial: u32) bool {
- for (s.list[0..s.len]) |known| if (known == serial) return true;
- return false;
- }
};
fn noteResult(p: *Pardes, serial: u32) void {
@@ -52,11 +47,6 @@ fn noteResult(p: *Pardes, serial: u32) void {
p.fs.results_len += 1;
}
-fn activeSerial(p: *Pardes) ?u32 {
- const pane = p.panes[p.active] orelse return null;
- return pane.serial;
-}
-
/// Runs one look or exec from the pane `id`, then records the panes it
/// created; failing that, a look records the pane it focused and an exec
/// the pane it acted on (even one it closed).
@@ -66,12 +56,12 @@ fn run(p: *Pardes, id: usize, exec: bool, text: []const u8) void {
if (exec) _ = pardes.exec.execute(p, id, text) else look.lookAt(p, id, text);
const after = Serials.snapshot(p);
var created = false;
- for (after.list[0..after.len]) |serial| if (!before.has(serial)) {
+ for (after.list[0..after.len]) |serial| if (std.mem.indexOfScalar(u32, before.list[0..before.len], serial) == null) {
noteResult(p, serial);
created = true;
};
if (created) return;
- noteResult(p, if (exec) context else activeSerial(p) orelse return);
+ noteResult(p, if (exec) context else (p.panes[p.active] orelse return).serial);
}
// ---- look and exec ----
@@ -140,11 +130,6 @@ pub fn readStatus(p: *Pardes, req: Req) Reply {
// ---- /pane/<n>/ctl ----
-fn fontName(p: *Pardes) []const u8 {
- const name = p.settings.font.effective_name.get();
- return if (name.len == 0) "default" else name;
-}
-
fn writeQuoted(w: *std.Io.Writer, s: []const u8) !void {
const plain = s.len > 0 and for (s) |c| {
if (c <= ' ' or c == '\'') break false;
@@ -175,7 +160,8 @@ pub fn paneText(p: *Pardes, pane: *Pane, buf: []u8) []const u8 {
@intFromBool(pane_files.dirtyOf(pane)),
pane.cols,
}) catch return w.buffered();
- writeQuoted(&w, fontName(p)) catch return w.buffered();
+ const font = p.settings.font.effective_name.get();
+ writeQuoted(&w, if (font.len == 0) "default" else font) catch return w.buffered();
w.print(" {d:>11} {s} ", .{ config.tab_width, if (p.panes[p.active] == pane) "current" else "notcurrent" }) catch {};
return w.buffered();
}
diff --git a/src/ninep/events.zig b/src/ninep/events.zig
index 49e852b3..a501d1e2 100644
--- a/src/ninep/events.zig
+++ b/src/ninep/events.zig
@@ -96,13 +96,6 @@ pub const Queue = struct {
}
};
-/// What the next read would answer, which is the size a stat reports: a
-/// client can see there is something waiting without parking on it.
-pub fn pending(q: *const Queue) u64 {
- const record = q.peek() orelse return 0;
- return record.len;
-}
-
/// One record per read; `.again` holds the read until a record arrives.
pub fn readQueue(p: *Pardes, req: Req, q: *Queue) Reply {
const record = q.peek() orelse return .{ .tag = req.tag, .status = .again };
diff --git a/src/ninep/pane.zig b/src/ninep/pane.zig
index 64355e0e..1e549107 100644
--- a/src/ninep/pane.zig
+++ b/src/ninep/pane.zig
@@ -83,12 +83,6 @@ pub fn tagOf(p: *Pardes, pane: *Pane) []const u8 {
return tagline.tagText(p, p.scratch.allocator(), pane) catch "";
}
-pub fn dirOf(pane: *Pane) []const u8 {
- if (pane.file) |*f| return std.fs.path.dirname(f.path) orelse "/";
- const cwd = pane.cwdSlice();
- return if (cwd.len > 0) cwd else "/";
-}
-
pub fn dirtyOf(pane: *const Pane) bool {
const f = if (pane.file) |*x| x else return false;
if (!panes.Output.fileTraits(f.output).saves) return false;
@@ -111,24 +105,15 @@ pub fn kindOf(pane: *const Pane) []const u8 {
return "term";
}
-pub fn mtimeOf(p: *Pardes, pane: *const Pane) u32 {
- if (pane.file) |*f| if (f.mtime != 0) return f.mtime;
- return p.fs.started;
-}
-
pub fn clip(n: usize) u32 {
return std.math.cast(u32, n) orelse std.math.maxInt(u32);
}
-fn cellOf(row: i32, col: i32) modal.Cursor {
- return .{ .row = @intCast(@max(0, row)), .col = @intCast(@max(0, col)) };
-}
-
pub fn dotOf(pane: *Pane) State.Range {
const text = bodyOf(pane);
- const head = modal.offsetAt(text, cellOf(pane.body.cur_row, pane.body.cur_col));
+ const head = modal.offsetAt(text, .{ .row = @intCast(@max(0, pane.body.cur_row)), .col = @intCast(@max(0, pane.body.cur_col)) });
if (!pane.body.vsel.active) return .{ .q0 = clip(head), .q1 = clip(head) };
- const anchor = modal.offsetAt(text, cellOf(pane.body.vsel.row, pane.body.vsel.col));
+ const anchor = modal.offsetAt(text, .{ .row = @intCast(@max(0, pane.body.vsel.row)), .col = @intCast(@max(0, pane.body.vsel.col)) });
var hi = @max(head, anchor);
if (hi < text.len) hi = modal.nextGrapheme(text, hi);
return .{ .q0 = clip(@min(head, anchor)), .q1 = clip(hi) };
@@ -169,10 +154,6 @@ pub fn clampAddr(pf: *State, len: usize) void {
}
}
-fn shiftBy(r: State.Range, at: u32, removed: u32, inserted: u32) State.Range {
- return .{ .q0 = shiftOne(r.q0, at, removed, inserted), .q1 = shiftOne(r.q1, at, removed, inserted) };
-}
-
fn shiftOne(v: u32, at: u32, removed: u32, inserted: u32) u32 {
if (v <= at) return v;
if (v <= at +| removed) return at +| inserted;
@@ -234,30 +215,21 @@ pub fn fileSize(p: *Pardes, id: usize, f: PaneFile) u64 {
.limit => if (pf.limit == null) 0 else range_len,
.dirty, .mark, .scroll => 2,
.look, .exec => ctl.resultsLen(p),
- .event => events.pending(&pf.events),
+ // What the next read would answer, which a client can see without
+ // parking on it.
+ .event => if (pf.events.peek()) |record| record.len else 0,
.pty_status => pty.status_len,
- .pty_ctl => pty.ctlLen(pane),
- .pty_data => events.pending(&pf.pty_out),
+ .pty_ctl => pty_ctl: {
+ var buf: [32]u8 = undefined;
+ break :pty_ctl (std.fmt.bufPrint(&buf, "winsize {d} {d}\n", .{ pane.cols, pane.rows }) catch unreachable).len;
+ },
+ .pty_data => if (pf.pty_out.peek()) |chunk| chunk.len else 0,
.dir, .errors, .pty, .pty_run => 0,
};
}
-/// The qid version, which a client polls to watch a file for change. Only
-/// the pane's text can promise one: its revision counts every edit, and
-/// nothing else here changes in step with it.
-pub fn versionOf(pane: *const Pane, f: PaneFile) u32 {
- return switch (f) {
- .body, .data, .xdata => if (pane.file) |*file| file.revision else 0,
- else => 0,
- };
-}
-
// ---- the index ----
-pub fn stageIndexLine(p: *Pardes, out: *std.ArrayList(u8), pane: *Pane) void {
- out.print(p.gpa, "{d} {s} {d} {s}\n", .{ pane.serial, kindOf(pane), @intFromBool(dirtyOf(pane)), nameOf(pane) }) catch {};
-}
-
pub fn indexLen(p: *Pardes) u64 {
var n: u64 = 0;
var last: u32 = 0;
@@ -440,7 +412,10 @@ fn writeData(p: *Pardes, req: Req, pane: *Pane) Reply {
const before = dotOf(pane);
const take = spliceBody(p, pane, q0, q1, req.data) orelse
return Reply.fail(req.tag, E.NOMEM);
- setDot(pane, shiftBy(before, clip(q0), clip(q1 - q0), clip(take)));
+ setDot(pane, .{
+ .q0 = shiftOne(before.q0, clip(q0), clip(q1 - q0), clip(take)),
+ .q1 = shiftOne(before.q1, clip(q0), clip(q1 - q0), clip(take)),
+ });
pf.addr = .{ .q0 = clip(q0 + take), .q1 = clip(q0 + take) };
if (!pf.noscroll) showOffset(pane, q0 + take);
return .{ .tag = req.tag, .written = @intCast(take) };
@@ -497,15 +472,10 @@ fn writeRange(req: Req, pane: *Pane, file: PaneFile) Reply {
return .{ .tag = req.tag, .written = @intCast(req.data.len) };
}
-fn flagOf(data: []const u8) ?bool {
- const word = std.mem.trim(u8, data, " \t\r\n");
- if (std.mem.eql(u8, word, "1")) return true;
- if (std.mem.eql(u8, word, "0")) return false;
- return null;
-}
-
fn writeFlag(p: *Pardes, req: Req, pane: *Pane, file: PaneFile) Reply {
- const on = flagOf(req.data) orelse return Reply.fail(req.tag, E.INVAL);
+ const word = std.mem.trim(u8, req.data, " \t\r\n");
+ if (!std.mem.eql(u8, word, "1") and !std.mem.eql(u8, word, "0")) return Reply.fail(req.tag, E.INVAL);
+ const on = word[0] == '1';
const pf = &pane.fs;
switch (file) {
.dirty => {
@@ -577,7 +547,8 @@ pub fn nameBuffer(p: *Pardes, id: usize, full: []const u8, preserve_tag: bool) !
pub fn appendErrors(p: *Pardes, id: usize, text: []const u8) ?usize {
if (text.len == 0) return 0;
const pane = p.panes[id] orelse return null;
- const dir = dirOf(pane);
+ const cwd = pane.cwdSlice();
+ const dir = if (pane.file) |*f| std.fs.path.dirname(f.path) orelse "/" else if (cwd.len > 0) cwd else "/";
for (p.panes) |slot| {
const q = slot orelse continue;
const qf = fileOf(q) orelse continue;
diff --git a/src/ninep/pty.zig b/src/ninep/pty.zig
index 49f52c82..eda6e9d0 100644
--- a/src/ninep/pty.zig
+++ b/src/ninep/pty.zig
@@ -84,11 +84,6 @@ pub fn readCtl(p: *Pardes, req: Req, pane: *Pane) Reply {
return tree.stagedReply(p, req);
}
-pub fn ctlLen(pane: *const Pane) u64 {
- var buf: [32]u8 = undefined;
- return (std.fmt.bufPrint(&buf, "winsize {d} {d}\n", .{ pane.cols, pane.rows }) catch unreachable).len;
-}
-
/// Three right-aligned fields: cols, rows and whether the host holds the tty.
pub const status_len: u64 = 36;
diff --git a/src/ninep/screen.zig b/src/ninep/screen.zig
index f72043d4..bc2788ec 100644
--- a/src/ninep/screen.zig
+++ b/src/ninep/screen.zig
@@ -57,9 +57,9 @@ pub fn readSnapshot(p: *Pardes, req: Req, pane: ?*Pane) Reply {
return .{ .tag = req.tag, .payload = .{ .staged = @intCast(len) } };
}
-const listeners_cap = 512;
+pub const listeners_cap = 512;
-fn listenersText(p: *Pardes, buf: []u8) []const u8 {
+pub fn listenersText(p: *Pardes, buf: []u8) []const u8 {
var text = std.Io.Writer.fixed(buf);
if (p.fs.socket_path.len != 0)
text.print("unix!{s}\n", .{p.fs.socket_path}) catch return text.buffered();
@@ -75,11 +75,6 @@ fn listenersText(p: *Pardes, buf: []u8) []const u8 {
return text.buffered();
}
-pub fn listenersLen(p: *Pardes) u64 {
- var buf: [listeners_cap]u8 = undefined;
- return listenersText(p, &buf).len;
-}
-
pub fn readListeners(p: *Pardes, req: Req) Reply {
var buf: [listeners_cap]u8 = undefined;
const out = p.fs.stage(p.gpa);
diff --git a/src/ninep/tree.zig b/src/ninep/tree.zig
index 53472a60..27ad02c8 100644
--- a/src/ninep/tree.zig
+++ b/src/ninep/tree.zig
@@ -66,10 +66,6 @@ pub const e_bad_event = "bad event syntax";
pub const e_in_use = "file in use";
pub const e_shut_down = "window shut down";
-fn reads(omode: u8) bool {
- return omode & 3 != 1; // OWRITE is the one access mode that never reads
-}
-
/// Tremove closes a pane; nothing else in the tree is created or destroyed by
/// the protocol, and wstat stays a truncation. Making a pane is an open of
/// /pane/new, which needs no feature of the engine's, so Tcreate is refused
@@ -281,18 +277,6 @@ pub const Target = union(enum) {
pane: struct { serial: u32, file: PaneFile },
};
-fn paneFileNamed(name: []const u8) ?PaneFile {
- const f = std.meta.stringToEnum(PaneFile, name) orelse return null;
- if (f == .dir) return null;
- return if (f.inPty() and f != .pty) null else f;
-}
-
-fn topFileNamed(name: []const u8) ?TopFile {
- const f = std.meta.stringToEnum(TopFile, name) orelse return null;
- // `new` is reached in /pane, where the panes it makes are.
- return if (f == .root or f == .new) null else f;
-}
-
/// Opening /pane/new makes a pane and reading the open fid answers its
/// serial, which is /net/tcp/clone's mechanism exactly (kernel/network/ip/
/// devip.c, `case Qclone` in ipopen, whose Qctl read prints the number it
@@ -305,13 +289,6 @@ fn topFileNamed(name: []const u8) ?TopFile {
/// can never collide with one.
pub const new_pane = "new";
-fn serialNamed(name: []const u8) ?u32 {
- if (name.len == 0 or name.len > 10) return null;
- for (name) |c| if (c < '0' or c > '9') return null;
- const serial = std.fmt.parseInt(u32, name, 10) catch return null;
- return if (serial == 0) null else serial;
-}
-
pub fn nextSerialAfter(p: *Pardes, after: u32) ?u32 {
var best: ?u32 = null;
for (p.panes) |slot| {
@@ -329,8 +306,9 @@ pub fn resolveSelf(p: *Pardes, path: []const u8) ?u64 {
var parts = std.mem.tokenizeScalar(u8, normalized, '/');
const first = parts.next() orelse return root;
if (!std.mem.eql(u8, first, "pane")) {
- const top = topFileNamed(first) orelse return sources.node(normalized);
- if (top == .pane or parts.next() != null) return null;
+ // `new` is reached in /pane, where the panes it makes are.
+ const top = std.meta.stringToEnum(TopFile, first) orelse return sources.node(normalized);
+ if (top == .root or top == .new or top == .pane or parts.next() != null) return null;
return @intFromEnum(top);
}
const next = parts.next() orelse return @intFromEnum(TopFile.pane);
@@ -338,9 +316,15 @@ pub fn resolveSelf(p: *Pardes, path: []const u8) ?u64 {
// path: this resolves Look targets and hover previews, and a preview that
// opened `new` to see what was there would make a pane per hover.
if (std.mem.eql(u8, next, new_pane)) return null;
- const serial = serialNamed(next) orelse return null;
+ // A pane directory is its serial, in decimal digits and never 0.
+ if (next.len > 10) return null;
+ for (next) |c| if (c < '0' or c > '9') return null;
+ const serial = std.fmt.parseInt(u32, next, 10) catch return null;
+ if (serial == 0) return null;
const id = p.paneBySerial(serial) orelse return null;
- const file = paneFileNamed(parts.next() orelse return Node.of(serial, .dir)) orelse return null;
+ // The pty files are reached inside pty/, not beside it.
+ const file = std.meta.stringToEnum(PaneFile, parts.next() orelse return Node.of(serial, .dir)) orelse return null;
+ if (file == .dir or (file.inPty() and file != .pty)) return null;
if (file.inPty() and !p.panes[id].?.isTerminal()) return null;
if (parts.next()) |name| {
if (file != .pty) return null;
@@ -390,7 +374,7 @@ pub fn handle(p: *Pardes, req: Req) Reply {
};
return switch (req.op) {
.lookup => lookup(p, req, target),
- .getattr => attrReply(p, req.tag, target),
+ .getattr => .{ .tag = req.tag, .attr = attrOf(p, target) orelse return Reply.fail(req.tag, E.NOENT) },
.setattr => setattr(p, req, target),
.open => open(p, req, target),
.release => release(p, req),
@@ -420,8 +404,14 @@ fn attrOf(p: *Pardes, target: Target) ?Reply.Attr {
.dir = t.file.isDir(),
.mode = t.file.mode(),
.size = pane.fileSize(p, id, t.file),
- .mtime = pane.mtimeOf(p, pn),
- .version = pane.versionOf(pn, t.file),
+ .mtime = if (pn.file) |*f| (if (f.mtime != 0) f.mtime else p.fs.started) else p.fs.started,
+ // The qid version, which a client polls to watch a file for
+ // change. Only the pane's text can promise one: its revision
+ // counts every edit, and nothing else here changes in step.
+ .version = switch (t.file) {
+ .body, .data, .xdata => if (pn.file) |*f| f.revision else 0,
+ else => 0,
+ },
// A write to body or tag appends whatever its offset says,
// which is why acme's dirtab marks both DMAPPEND
// (editors/acme/fsys.c:78).
@@ -431,11 +421,6 @@ fn attrOf(p: *Pardes, target: Target) ?Reply.Attr {
}
}
-fn attrReply(p: *Pardes, tag: u64, target: Target) Reply {
- const a = attrOf(p, target) orelse return Reply.fail(tag, E.NOENT);
- return .{ .tag = tag, .attr = a };
-}
-
fn topSize(p: *Pardes, f: TopFile) u64 {
return switch (f) {
// ponytail: /screen has no length until an open renders its frame,
@@ -446,7 +431,10 @@ fn topSize(p: *Pardes, f: TopFile) u64 {
.status => ctl.statusLen(p),
.look, .exec => ctl.resultsLen(p),
.log => events.logLen(&p.fs.log),
- .listeners => screen.listenersLen(p),
+ .listeners => listeners: {
+ var buf: [screen.listeners_cap]u8 = undefined;
+ break :listeners screen.listenersText(p, &buf).len;
+ },
.ctl => ctl.rootLen(p),
.commands => ctl.commandsLen(),
.focus => focus: {
@@ -475,13 +463,18 @@ fn lookup(p: *Pardes, req: Req, target: Target) Reply {
.top => |f| switch (f) {
.root => root: {
if (std.mem.eql(u8, name, "os")) return fs.osHandle(p, .{ .tag = req.tag, .op = .getattr, .node = fs.os_root });
- if (topFileNamed(name)) |t| break :root @intFromEnum(t);
+ // `new` is reached in /pane, where the panes it makes are.
+ if (std.meta.stringToEnum(TopFile, name)) |t| if (t != .root and t != .new) break :root @intFromEnum(t);
if (sources.node(name)) |n| return sources.handle(p, .{ .tag = req.tag, .op = .getattr, .node = n });
return Reply.fail(req.tag, E.NOENT);
},
.pane => pane: {
if (std.mem.eql(u8, name, new_pane)) break :pane @intFromEnum(TopFile.new);
- const serial = serialNamed(name) orelse return Reply.fail(req.tag, E.NOENT);
+ // A pane directory is its serial, in decimal digits and never 0.
+ if (name.len > 10) return Reply.fail(req.tag, E.NOENT);
+ for (name) |c| if (c < '0' or c > '9') return Reply.fail(req.tag, E.NOENT);
+ const serial = std.fmt.parseInt(u32, name, 10) catch return Reply.fail(req.tag, E.NOENT);
+ if (serial == 0) return Reply.fail(req.tag, E.NOENT);
break :pane Node.of(serial, .dir);
},
else => return Reply.fail(req.tag, E.NOTDIR),
@@ -489,14 +482,15 @@ fn lookup(p: *Pardes, req: Req, target: Target) Reply {
.pane => |t| pane: {
_ = p.paneBySerial(t.serial) orelse return Reply.fail(req.tag, E.NOENT);
const f = switch (t.file) {
- .dir => paneFileNamed(name),
+ .dir => if (std.meta.stringToEnum(PaneFile, name)) |f| (if (f == .dir or (f.inPty() and f != .pty)) null else f) else null,
.pty => pty.fileNamed(name),
else => return Reply.fail(req.tag, E.NOTDIR),
} orelse return Reply.fail(req.tag, E.NOENT);
break :pane Node.of(t.serial, f);
},
};
- return attrReply(p, req.tag, Node.target(node) orelse return Reply.fail(req.tag, E.NOENT));
+ const found = Node.target(node) orelse return Reply.fail(req.tag, E.NOENT);
+ return .{ .tag = req.tag, .attr = attrOf(p, found) orelse return Reply.fail(req.tag, E.NOENT) };
}
fn stagePaneFiles(p: *Pardes, out: *std.ArrayList(u8), serial: u32, terminal: bool, skip: *u64) void {
@@ -564,20 +558,19 @@ fn readdir(p: *Pardes, req: Req, target: Target) Reply {
return .{ .tag = req.tag, .payload = .{ .staged = @intCast(out.items.len) } };
}
-/// Opens a pane for an open of /pane/new, whose serial becomes the handle:
-/// the fid remembers which pane it made, so reading it twice answers the
-/// same one and closing it leaves the pane alone.
-fn makePane(p: *Pardes) ?u32 {
- const slot = p.freeSlot() orelse return null;
- p.newScratchBelow(p.active);
- return (p.panes[slot] orelse return null).serial;
-}
-
fn open(p: *Pardes, req: Req, target: Target) Reply {
var pane_of: ?*Pane = null;
const what: @FieldType(Open, "what") = switch (target) {
.top => |f| switch (f) {
- .new => return .{ .tag = req.tag, .handle = makePane(p) orelse return Reply.fail(req.tag, E.NFILE) },
+ // The made pane's serial is the handle: the fid remembers which
+ // pane it made, so reading it twice answers the same one and
+ // closing it leaves the pane alone.
+ .new => {
+ const slot = p.freeSlot() orelse return Reply.fail(req.tag, E.NFILE);
+ p.newScratchBelow(p.active);
+ const made = p.panes[slot] orelse return Reply.fail(req.tag, E.NFILE);
+ return .{ .tag = req.tag, .handle = made.serial };
+ },
.screen => .{ .snapshot = null },
.log => .{ .log = .{} },
else => return .{ .tag = req.tag },
@@ -586,7 +579,7 @@ fn open(p: *Pardes, req: Req, target: Target) Reply {
const pn = p.panes[p.paneBySerial(t.serial) orelse return Reply.fail(req.tag, E.NOENT)].?;
if (t.file.inPty() and !pn.isTerminal()) return Reply.fail(req.tag, E.NOENT);
pane_of = pn;
- const reader = reads(req.omode);
+ const reader = req.omode & 3 != 1; // OWRITE is the one mode that never reads
break :what switch (t.file) {
.body => if (pn.isTerminal()) .{ .snapshot = null } else return .{ .tag = req.tag },
// Reads of these consume, so two readers would each see half
@@ -693,7 +686,7 @@ fn setattr(p: *Pardes, req: Req, target: Target) Reply {
},
.top => {},
};
- return attrReply(p, req.tag, target);
+ return .{ .tag = req.tag, .attr = attrOf(p, target) orelse return Reply.fail(req.tag, E.NOENT) };
}
fn read(p: *Pardes, req: Req, target: Target) Reply {
@@ -710,7 +703,8 @@ fn read(p: *Pardes, req: Req, target: Target) Reply {
var last: u32 = 0;
while (nextSerialAfter(p, last)) |serial| {
last = serial;
- pane.stageIndexLine(p, out, p.panes[p.paneBySerial(serial).?].?);
+ const pn = p.panes[p.paneBySerial(serial).?].?;
+ out.print(p.gpa, "{d} {s} {d} {s}\n", .{ serial, pane.kindOf(pn), @intFromBool(pane.dirtyOf(pn)), pane.nameOf(pn) }) catch {};
}
break :index stagedReply(p, req);
},