Fix top: use FFI for terminal control instead of shelling out
ober
92001ceae28b5156f7cfe03c479f8b05ab966f1a
--- a/lib/jerboa-coreutils/top.sls +++ b/lib/jerboa-coreutils/top.sls @@ -43,17 +43,45 @@ (only (std sugar) with-catch) (only (std format) eprintf format) (std cli getopt) - (std misc terminal) - (only (std os tty) tty? tty-size) + (except (std misc terminal) + terminal-width terminal-height + with-raw-mode with-alternate-screen) (jerboa-coreutils common) (jerboa-coreutils common version) (jerboa-coreutils common security)) - ;; ========== FFI for kill/renice ========== + ;; ========== FFI for kill/renice/terminal ========== (define _load-ffi (begin (load-shared-object #f) (void))) (define ffi-kill (foreign-procedure "kill" (int int) int)) (define ffi-setpriority (foreign-procedure "setpriority" (int int int) int)) + (define ffi-terminal-width (foreign-procedure "coreutils_terminal_width" (int) int)) + (define ffi-terminal-height (foreign-procedure "coreutils_terminal_height" (int) int)) + (define ffi-raw-mode-enter (foreign-procedure "coreutils_raw_mode_enter" (int) int)) + (define ffi-raw-mode-exit (foreign-procedure "coreutils_raw_mode_exit" (int) int)) + + ;; ========== Terminal helpers (FFI-based, no fork/exec) ========== + + (define (terminal-width) (ffi-terminal-width 1)) + (define (terminal-height) (ffi-terminal-height 1)) + + (define esc "\x1b;") + + (define (with-raw-mode thunk) + (dynamic-wind + (lambda () (ffi-raw-mode-enter 0)) + thunk + (lambda () (ffi-raw-mode-exit 0)))) + + (define (with-alternate-screen thunk) + (dynamic-wind + (lambda () + (display (string-append esc "[?1049h")) + (flush-output-port (current-output-port))) + thunk + (lambda () + (display (string-append esc "[?1049l")) + (flush-output-port (current-output-port))))) ;; ========== /proc Readers ========== --- a/support/libcoreutils.c +++ b/support/libcoreutils.c @@ -215,6 +215,37 @@ int coreutils_terminal_width(int fd) { return (int)ws.ws_col; } +int coreutils_terminal_height(int fd) { + struct winsize ws; + if (ioctl(fd, TIOCGWINSZ, &ws) < 0 || ws.ws_row == 0) + return 24; + return (int)ws.ws_row; +} + +/* Raw mode via tcgetattr/tcsetattr — no fork/exec needed */ +#include <termios.h> +static struct termios coreutils_saved_termios; +static int coreutils_termios_saved = 0; + +int coreutils_raw_mode_enter(int fd) { + struct termios raw; + if (tcgetattr(fd, &coreutils_saved_termios) < 0) return -1; + coreutils_termios_saved = 1; + raw = coreutils_saved_termios; + raw.c_lflag &= ~(ECHO | ICANON | ISIG | IEXTEN); + raw.c_iflag &= ~(IXON | ICRNL | BRKINT | INPCK | ISTRIP); + raw.c_oflag &= ~(OPOST); + raw.c_cc[VMIN] = 0; + raw.c_cc[VTIME] = 0; + return tcsetattr(fd, TCSAFLUSH, &raw); +} + +int coreutils_raw_mode_exit(int fd) { + if (!coreutils_termios_saved) return -1; + coreutils_termios_saved = 0; + return tcsetattr(fd, TCSAFLUSH, &coreutils_saved_termios); +} + static char ls_time_buf[64]; const char* coreutils_time_format(long t) { time_t tt = (time_t)t; Binary files a/support/libcoreutils.so and b/support/libcoreutils.so differ