diff options
| author | Gabriel Schneider <[email protected]> | 2026-09-20 00:36:50 -0300 |
|---|---|---|
| committer | Gabriel Schneider <[email protected]> | 2026-10-01 00:12:14 -0300 |
| commit | c1990b3e6e196ad41379aa432bf3ccca8a65d9f0 (patch) | |
| tree | d7ad5afe559f7f56464b14c3122242fa3f16399b /test/ninep.py | |
| parent | 717afaf3177a2e0925b18ae3445119efe808b129 (diff) | |
| download | pardes-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.py | 39 |
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)) |
