executor: swap popen+sentinel for pty.spawn (Phase 1)
Replaces the Phase 0 io.popen + sentinel-echo exit-code recovery with
forkpty + waitpid via ffi/pty. The §7 amendment paragraph on PHASE0.md
is rewritten to point at PHASE1.md §5 — the workaround is gone, not
just renamed.
User-visible behavioral changes:
- Interactive commands (vim, less, htop, top) now work via $cmd /
:exec / known-command shell paths because the child has a real
PTY for line discipline.
- Exit codes are accurate: `false` -> 1, `exit 7` -> 7, signal kill
-> 128+N (bash convention), shell parse error -> sh's 2.
- Broken-shell-syntax cmd now shows the actual sh diagnostic
(e.g. "Syntax error: end of file unexpected") instead of Phase 0's
"(no output — possible shell parse error)" guess.
- Output normalization: PTY emits CR LF; executor collapses \r\n
-> \n to keep the Phase 0 contract ("output uses \n separators").
Code path:
pty.spawn(cmd) -> drain master_fd until EOF
-> wait() returns ("exit", N) | ("signal", N) | ...
-> exit_code mapped: exit -> N, signal -> 128+N, else -1
Phase 0 invariants intact: `cd` interception unchanged (still libc.chdir
per §3 + §7), `CMD: ` extraction unchanged.
PHASE0.md §7: the "LuaJIT 2.1 popen-close caveat" paragraph is rewritten
to "Superseded by Phase 1" — points at PHASE1.md §5 for the live model.
The illustrative sketch is left in place as historical context.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
+8
-7
@@ -171,13 +171,14 @@ local function exec(cmd)
|
||||
end
|
||||
```
|
||||
|
||||
**LuaJIT 2.1 popen-close caveat.** The sketch above assumes Lua 5.2's
|
||||
three-return `io.popen():close()` shape. LuaJIT 2.1 follows the Lua 5.1
|
||||
ABI and returns just `true` — no exit status. The Phase 0 implementation
|
||||
recovers the exit code by appending a sentinel echo to the wrapped
|
||||
command (`(cmd) 2>&1; echo __AISH_EXIT_<tag>__$?`) and parsing it back
|
||||
out. Phase 1's PTY work replaces this with `waitpid` via libc FFI; the
|
||||
sketch becomes accurate at that point.
|
||||
**Superseded by Phase 1.** The §7 sketch was never quite accurate on
|
||||
LuaJIT 2.1 (which follows the Lua 5.1 ABI for `io.popen():close()` and
|
||||
returns only `true` — no exit status). The Phase 0 implementation worked
|
||||
around this with a sentinel-echo wrapper (`(cmd) 2>&1; echo
|
||||
__AISH_EXIT_<tag>__$?`) and parsed the status back out of stdout. Phase 1
|
||||
retired the workaround entirely: `executor.lua` now spawns the child via
|
||||
`forkpty` and recovers exit status via `waitpid(WEXITSTATUS)`. See
|
||||
docs/PHASE1.md §5 for the current PTY model.
|
||||
|
||||
Output is captured and:
|
||||
1. Printed to the terminal
|
||||
|
||||
Reference in New Issue
Block a user