summaryrefslogtreecommitdiff
path: root/src/ninep/tree.zig
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/tree.zig
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/tree.zig')
-rw-r--r--src/ninep/tree.zig100
1 files changed, 47 insertions, 53 deletions
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);
},