fix: use :input :interactive for stty size subprocess
SBCL's stdin during --load is the load file, NOT the terminal. When uiop:run-program creates a subprocess, it inherits this stdin, so 'stty size' reads from the load file and fails. :input :interactive opens /dev/tty for the child's stdin, matching the behavior of 'stty size' from an interactive shell.
This commit is contained in:
@@ -163,20 +163,29 @@ as a fallback when a keyword is not in *named-colors*.")
|
|||||||
|
|
||||||
(defmethod backend-size ((b modern-backend))
|
(defmethod backend-size ((b modern-backend))
|
||||||
(or ;; stty size via subprocess — most reliable across all systems.
|
(or ;; stty size via subprocess — most reliable across all systems.
|
||||||
;; Returns "ROWS COLS". Uses explicit /dev/tty to bypass any
|
;; Returns "ROWS COLS". Uses :input :interactive so the subprocess
|
||||||
;; stdin redirection from uiop:run-program.
|
;; opens /dev/tty for its stdin (SBCL's stdin is the load file).
|
||||||
|
(multiple-value-bind (cols rows)
|
||||||
(ignore-errors
|
(ignore-errors
|
||||||
(let* ((out (uiop:run-program '("sh" "-c" "stty size < /dev/tty")
|
(let* ((out (uiop:run-program '("stty" "size")
|
||||||
:output :string
|
:output :string
|
||||||
|
:input :interactive
|
||||||
:ignore-error-status t))
|
:ignore-error-status t))
|
||||||
(parts (and out (uiop:split-string
|
(parts (and out (uiop:split-string
|
||||||
(string-trim '(#\newline #\space) out)))))
|
(string-trim '(#\newline #\space) out)))))
|
||||||
(when (and parts (= (length parts) 2))
|
(when parts
|
||||||
(let ((rows (parse-integer (first parts) :junk-allowed t))
|
(let ((a (parse-integer (first parts) :junk-allowed t))
|
||||||
(cols (parse-integer (second parts) :junk-allowed t)))
|
(b (when (cdr parts) (parse-integer (second parts) :junk-allowed t))))
|
||||||
(when (and rows cols (> rows 0) (> cols 0))
|
(if (and a b (> a 0) (> b 0))
|
||||||
(values cols rows))))))
|
;; stty returns "ROWS COLS"
|
||||||
|
(values b a)
|
||||||
|
;; stty returned only one value — treat as cols
|
||||||
|
(when (and a (> a 0))
|
||||||
|
(values a nil)))))))
|
||||||
|
(when (and cols rows (> cols 0) (> rows 0))
|
||||||
|
(values cols rows)))
|
||||||
;; ioctl on stdout fd — fast, correct after SIGWINCH at runtime.
|
;; ioctl on stdout fd — fast, correct after SIGWINCH at runtime.
|
||||||
|
(multiple-value-bind (cols rows)
|
||||||
(ignore-errors
|
(ignore-errors
|
||||||
(let* ((+tiocgwinsz+ 21523) ; 0x5413 on Linux
|
(let* ((+tiocgwinsz+ 21523) ; 0x5413 on Linux
|
||||||
(winsize (sb-alien:make-alien sb-alien:unsigned-short 4)))
|
(winsize (sb-alien:make-alien sb-alien:unsigned-short 4)))
|
||||||
@@ -188,6 +197,8 @@ as a fallback when a keyword is not in *named-colors*.")
|
|||||||
(values (sb-alien:deref winsize 1) ;; cols
|
(values (sb-alien:deref winsize 1) ;; cols
|
||||||
(sb-alien:deref winsize 0))) ;; rows
|
(sb-alien:deref winsize 0))) ;; rows
|
||||||
(sb-alien:free-alien winsize))))
|
(sb-alien:free-alien winsize))))
|
||||||
|
(when (and cols rows (> cols 0) (> rows 0))
|
||||||
|
(values cols rows)))
|
||||||
;; Direct ioctl on /dev/tty — opens the real controlling terminal.
|
;; Direct ioctl on /dev/tty — opens the real controlling terminal.
|
||||||
(ignore-errors
|
(ignore-errors
|
||||||
(let ((tty-fd (sb-unix:unix-open "/dev/tty" 0 0))) ; O_RDONLY
|
(let ((tty-fd (sb-unix:unix-open "/dev/tty" 0 0))) ; O_RDONLY
|
||||||
|
|||||||
@@ -23,39 +23,24 @@
|
|||||||
|
|
||||||
(defmethod backend-size ((b simple-backend))
|
(defmethod backend-size ((b simple-backend))
|
||||||
(or ;; stty size via subprocess — most reliable across all systems.
|
(or ;; stty size via subprocess — most reliable across all systems.
|
||||||
|
(multiple-value-bind (cols rows)
|
||||||
(ignore-errors
|
(ignore-errors
|
||||||
(let* ((out (uiop:run-program '("sh" "-c" "stty size < /dev/tty")
|
(let* ((out (uiop:run-program '("stty" "size")
|
||||||
:output :string
|
:output :string
|
||||||
|
:input :interactive
|
||||||
:ignore-error-status t))
|
:ignore-error-status t))
|
||||||
(parts (and out (uiop:split-string
|
(parts (and out (uiop:split-string
|
||||||
(string-trim '(#\newline #\space) out)))))
|
(string-trim '(#\newline #\space) out)))))
|
||||||
(when (and parts (= (length parts) 2))
|
(when parts
|
||||||
(let ((rows (parse-integer (first parts) :junk-allowed t))
|
(let ((a (parse-integer (first parts) :junk-allowed t))
|
||||||
(cols (parse-integer (second parts) :junk-allowed t)))
|
(b (when (cdr parts) (parse-integer (second parts) :junk-allowed t))))
|
||||||
(when (and rows cols (> rows 0) (> cols 0))
|
(if (and a b (> a 0) (> b 0))
|
||||||
(values cols rows))))))
|
(values b a) ; stty returns "ROWS COLS"
|
||||||
(ignore-errors
|
(when (and a (> a 0))
|
||||||
(let* ((+tiocgwinsz+ 21523)
|
(values a nil)))))))
|
||||||
(winsize (sb-alien:make-alien sb-alien:unsigned-short 4)))
|
(when (and cols rows (> cols 0) (> rows 0))
|
||||||
(unwind-protect
|
(values cols rows)))
|
||||||
(progn
|
(multiple-value-bind (cols rows)
|
||||||
(sb-unix:unix-ioctl (sb-sys:fd-stream-fd
|
|
||||||
(backend-output-stream b))
|
|
||||||
+tiocgwinsz+
|
|
||||||
(sb-alien:alien-sap winsize))
|
|
||||||
(values (sb-alien:deref winsize 1)
|
|
||||||
(sb-alien:deref winsize 0)))
|
|
||||||
(sb-alien:free-alien winsize))))
|
|
||||||
;; stty size via subprocess — reliable on every Unix.
|
|
||||||
(ignore-errors
|
|
||||||
(let* ((out (uiop:run-program '("sh" "-c" "stty size < /dev/tty")
|
|
||||||
:output :string
|
|
||||||
:ignore-error-status t))
|
|
||||||
(parts (and out (uiop:split-string
|
|
||||||
(string-trim '(#\newline #\space) out)))))
|
|
||||||
(when (and parts (= (length parts) 2))
|
|
||||||
(let ((rows (parse-integer (first parts) :junk-allowed t))
|
|
||||||
(cols (parse-integer (second parts) :junk-allowed t)))
|
|
||||||
(when (and rows cols (> rows 0) (> cols 0))
|
(when (and rows cols (> rows 0) (> cols 0))
|
||||||
(values cols rows))))))
|
(values cols rows))))))
|
||||||
(ignore-errors
|
(ignore-errors
|
||||||
|
|||||||
Reference in New Issue
Block a user