fix: use global vars for cat subprocess to avoid let* scope crash

Replaced (let* ((cat-proc ...) (tty-in ...)) ...) with global
special variables *cat-proc* and *tty-in* with defvar declarations.
The let* caused 'unbound variable' errors on Ctrl+Q because the
lexical scope didn't extend to terminate-process. Global vars have
indefinite scope and work reliably regardless of paren nesting.
This commit is contained in:
2026-05-14 15:57:37 -04:00
parent 74621cffd2
commit e453f9aad9

View File

@@ -886,6 +886,9 @@ Event handlers + daemon I/O + main loop.
(:down (lambda (e) (declare (ignore e)) (on-key :down)))
(:escape (lambda (e) (declare (ignore e)) (on-key :escape)))))
(defvar *cat-proc* nil "Cat subprocess for keyboard input")
(defvar *tty-in* nil "Stream from cat subprocess stdout")
(defun tui-main ()
(init-state)
(load-history)
@@ -912,12 +915,14 @@ Event handlers + daemon I/O + main loop.
;; bytes through a pipe that SBCL reads reliably.
;; stty -icanon -echo is set by the bash script before exec sbcl.
;; A cat subprocess reads keyboard input from the terminal.
(let* ((cat-proc (uiop:launch-program '("cat")
;; Global vars for cat subprocess — avoid lexical let* scope issue
(progn
(setq *cat-proc* (uiop:launch-program '("cat")
:output :stream
:input :interactive
:stderr nil))
(tty-in (uiop:process-info-output cat-proc)))
(add-msg :system (format nil "* cat pid=~a *" (uiop:process-info-pid cat-proc)))
:stderr nil)
*tty-in* (uiop:process-info-output *cat-proc*)))
(add-msg :system (format nil "* cat pid=~a *" (uiop:process-info-pid *cat-proc*)))
;; Guard against nil w/h from backend-size
(setq w (or (and (numberp w) (> w 0) w) 80)
h (or (and (numberp h) (> h 0) h) 24))
@@ -990,7 +995,7 @@ Event handlers + daemon I/O + main loop.
;; Keyboard reader: block on cat pipe with 0.1s timeout.
(handler-case
(sb-ext:with-timeout 0.1
(let ((raw-ch (read-char tty-in nil nil)))
(let ((raw-ch (read-char *tty-in* nil nil)))
(when raw-ch
(let ((code (char-code raw-ch)))
(queue-event
@@ -1064,12 +1069,12 @@ Event handlers + daemon I/O + main loop.
(cond (cat (theme-color :dim))
(sel-p (theme-color :accent))
(t (theme-color :agent-fg)))
nil :bold sel-p)
(incf y-off)))))))
nil :bold sel-p)
(incf y-off)))))))
(sleep 0.1))
(uiop:terminate-process cat-proc))
(add-msg :system (format nil "* cat ~a ended *" (uiop:process-info-pid cat-proc))))
(progn (disconnect-daemon))))
(uiop:terminate-process *cat-proc*))
(add-msg :system (format nil "* cat ~a ended *" (uiop:process-info-pid *cat-proc*))))
(progn (disconnect-daemon)))
#+END_SRC
* Test Suite