From 7813e2790748a813c4cc471409709ed9b5d0d6f4 Mon Sep 17 00:00:00 2001 From: Amr Gharbeia Date: Thu, 14 May 2026 13:05:53 -0400 Subject: [PATCH] fix: revert to simple ioctl-first with env var fallback The previous logic (check ioctl result, prefer env when 80x24) added complexity and crashes. Simple or with env vars after ioctl is safe: ioctl returns 80x24 on stdout fd mismatch, env vars (COLUMNS/LINES from shell) provide the correct initial size. --- src/backend/modern.lisp | 58 +++++++++++++++-------------------------- src/backend/simple.lisp | 52 +++++++++++++++--------------------- 2 files changed, 42 insertions(+), 68 deletions(-) diff --git a/src/backend/modern.lisp b/src/backend/modern.lisp index 6cf453a..e53a2d8 100644 --- a/src/backend/modern.lisp +++ b/src/backend/modern.lisp @@ -164,43 +164,27 @@ as a fallback when a keyword is not in *named-colors*.") (values)) (defmethod backend-size ((b modern-backend)) - (multiple-value-bind (w h) - (or (ignore-errors - (let* ((+tiocgwinsz+ 21523) ; 0x5413 on Linux - (winsize (sb-alien:make-alien sb-alien:unsigned-short 4))) - (unwind-protect - (progn - (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) ;; cols - (sb-alien:deref winsize 0))) ;; rows - (sb-alien:free-alien winsize)))) - (values 0 0)) - ;; If ioctl returned a plausible size (not the 80x24 default), - ;; trust it — it will be correct at runtime after SIGWINCH. - (if (and (> w 80) (> h 24)) - (values w h) - ;; ioctl returned 80x24 or less — suspicious. Try $COLUMNS/ - ;; $LINES from the shell, which reflect the true size at - ;; process start even when ioctl on stdout's fd disagrees. - (or (ignore-errors - (let* ((cstr (sb-ext:posix-getenv "COLUMNS")) - (rstr (sb-ext:posix-getenv "LINES")) - (cols (when cstr (parse-integer cstr :junk-allowed t))) - (rows (when rstr (parse-integer rstr :junk-allowed t)))) - ;; Some environments set only COLUMNS or only LINES. - ;; If one is missing, try the other from stty. - (when (and cols (null rows)) - (let* ((out (uiop:run-program '("stty" "size") - :output :string - :ignore-error-status t)) - (parts (when out (uiop:split-string (string-trim '(#\newline #\space) out))))) - (setf rows (when (and parts (= (length parts) 2)) - (parse-integer (second parts) :junk-allowed t))))) - (when (and cols rows (> cols 0) (> rows 0)) - (values cols rows)))) - (values w h))))) + (or (ignore-errors + (let* ((+tiocgwinsz+ 21523) ; 0x5413 on Linux + (winsize (sb-alien:make-alien sb-alien:unsigned-short 4))) + (unwind-protect + (progn + (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) ;; cols + (sb-alien:deref winsize 0))) ;; rows + (sb-alien:free-alien winsize)))) + ;; $COLUMNS/$LINES fallback — some systems don't update the + ;; TTY size via ioctl on stdout's fd. + (ignore-errors + (let* ((cstr (sb-ext:posix-getenv "COLUMNS")) + (rstr (sb-ext:posix-getenv "LINES")) + (cols (when cstr (parse-integer cstr :junk-allowed t))) + (rows (when rstr (parse-integer rstr :junk-allowed t)))) + (when (and cols rows (> cols 0) (> rows 0)) + (values cols rows)))) + (values 80 24))) (defmethod backend-write ((b modern-backend) string) (let ((stream (backend-output-stream b))) diff --git a/src/backend/simple.lisp b/src/backend/simple.lisp index dd0bcba..b0fb84a 100644 --- a/src/backend/simple.lisp +++ b/src/backend/simple.lisp @@ -27,37 +27,27 @@ (values)) (defmethod backend-size ((b simple-backend)) - (multiple-value-bind (w h) - (or (ignore-errors - (let* ((+tiocgwinsz+ 21523) - (winsize (sb-alien:make-alien sb-alien:unsigned-short 4))) - (unwind-protect - (progn - (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)))) - (values 0 0)) - (if (and (> w 80) (> h 24)) - (values w h) - (or (ignore-errors - (let* ((cstr (sb-ext:posix-getenv "COLUMNS")) - (rstr (sb-ext:posix-getenv "LINES")) - (cols (when cstr (parse-integer cstr :junk-allowed t))) - (rows (when rstr (parse-integer rstr :junk-allowed t)))) - (when (and cols (null rows)) - (let* ((out (uiop:run-program '("stty" "size") - :output :string - :ignore-error-status t)) - (parts (when out (uiop:split-string (string-trim '(#\newline #\space) out))))) - (setf rows (when (and parts (= (length parts) 2)) - (parse-integer (second parts) :junk-allowed t))))) - (when (and cols rows (> cols 0) (> rows 0)) - (values cols rows)))) - (values w h))))) + (or (ignore-errors + (let* ((+tiocgwinsz+ 21523) + (winsize (sb-alien:make-alien sb-alien:unsigned-short 4))) + (unwind-protect + (progn + (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)))) + ;; $COLUMNS/$LINES fallback + (ignore-errors + (let* ((cstr (sb-ext:posix-getenv "COLUMNS")) + (rstr (sb-ext:posix-getenv "LINES")) + (cols (when cstr (parse-integer cstr :junk-allowed t))) + (rows (when rstr (parse-integer rstr :junk-allowed t)))) + (when (and cols rows (> cols 0) (> rows 0)) + (values cols rows)))) + (values 80 24))) (defmethod backend-write ((b simple-backend) string) (let ((stream (backend-output-stream b)))