summaryrefslogtreecommitdiff
path: root/test/ninep.py
diff options
context:
space:
mode:
authorGabriel Schneider <[email protected]>2026-09-20 00:36:50 -0300
committerGabriel Schneider <[email protected]>2026-10-01 00:12:14 -0300
commitc1990b3e6e196ad41379aa432bf3ccca8a65d9f0 (patch)
treed7ad5afe559f7f56464b14c3122242fa3f16399b /test/ninep.py
parent717afaf3177a2e0925b18ae3445119efe808b129 (diff)
downloadpardes-c1990b3e6e196ad41379aa432bf3ccca8a65d9f0.tar.gz
pardes-c1990b3e6e196ad41379aa432bf3ccca8a65d9f0.zip
Flatten the 9P control tree and move it out of fs.zig
The served tree loses the self/ level: /index /ctl /new /log /screen /listeners /pane/<n>/... /os, with /src only in -Dembed-sources=true builds (default off, on for esp32p4). ctl speaks the editor's own language with two lowercase verbs, look TEXT and exec TEXT, plus acme's addr verbs; the new/ factory directory becomes one clone file; cons is gone (exec Msg); name and sel are files; stats report real lengths, modes and mtimes; /log streams pane new/del/rename/save events. The tree code lives in src/ninep/ (tree, pane, ctl, addr, pty, events, screen, sources); fs.zig keeps host access, mounts, resolution and find/grep. Same engine and transports. README (fs-help.txt) and docs rewritten; tests updated and extended. Co-Authored-By: Claude Fable 5.1 <[email protected]>
Diffstat (limited to 'test/ninep.py')
-rw-r--r--test/ninep.py39
1 files changed, 38 insertions, 1 deletions
diff --git a/test/ninep.py b/test/ninep.py
index b128cc8a..6c625951 100644
--- a/test/ninep.py
+++ b/test/ninep.py
@@ -116,7 +116,44 @@ class Client:
self.close(fid)
def screen(self):
- return json.loads(self.read('/self/screen'))
+ return json.loads(self.read('/screen'))
+
+ def walk(self, path):
+ names = [part for part in os.fspath(path).split('/') if part]
+ self.fid += 1
+ fid = self.fid
+ oldfid = 1
+ try:
+ for start in range(0, max(1, len(names)), 16):
+ part = names[start:start + 16]
+ reply = self.rpc(110, struct.pack('<IIH', oldfid, fid, len(part)) +
+ b''.join(string(name) for name in part))
+ if struct.unpack_from('<H', reply)[0] != len(part):
+ raise FileNotFoundError(path)
+ oldfid = fid
+ return fid
+ except BaseException:
+ try:
+ self.close(fid)
+ except OSError:
+ pass
+ raise
+
+ def stat(self, path):
+ """Tstat without an open: {'name', 'mode', 'length', 'mtime', 'qid'}."""
+ fid = self.walk(path)
+ try:
+ reply = self.rpc(124, struct.pack('<I', fid))
+ finally:
+ self.close(fid)
+ size = struct.unpack_from('<H', reply, 2)[0]
+ record = reply[4:4 + size]
+ qid = record[6:19]
+ mode, _, mtime, length = struct.unpack_from('<IIIQ', record, 19)
+ name_len = struct.unpack_from('<H', record, 39)[0]
+ name = record[41:41 + name_len].decode()
+ return {'name': name, 'mode': mode & 0o777, 'dir': bool(mode & 0x80000000),
+ 'length': length, 'mtime': mtime, 'qid': qid}
def write(self, path, data, truncate=False):
fid = self.open(path, 1 | (16 if truncate else 0))