Index | Thread | Search

From:
Kirill A. Korinsky <kirill@korins.ky>
Subject:
bin/ksh: fix emacs/vi line editor getting wrong terminal width
To:
OpenBSD tech <tech@openbsd.org>
Date:
Sun, 11 Jan 2026 00:58:46 +0100

Download raw body.

Thread
tech@,

Xterm and other terminal emulators resize the pty after starting the
shell. x_init() queries terminal size at startup before the resize
occurs, so x_cols is kept at default 80 colums.

SIGWINCH arrives but check_sigwinch() wasn't called until after command
entry. This causes the line editor thinks that screen is 80 columns,
than leads to show '<' and truncate input before reaching the actual
terminal edge for large window, and wired behaiour for small one.

Here, I call check_sigwinch() when entering edit mode and on EINTR in
x_getc(). Callback lets emacs/vi recalculate display width and redraw.

Thoughts? Tests? Ok?

Index: edit.c
===================================================================
RCS file: /home/cvs/src/bin/ksh/edit.c,v
diff -u -p -r1.71 edit.c
--- edit.c	23 Apr 2024 13:34:50 -0000	1.71
+++ edit.c	10 Jan 2026 22:46:09 -0000
@@ -25,7 +25,8 @@ X_chars edchars;
 
 static void x_sigwinch(int);
 volatile sig_atomic_t got_sigwinch;
-static void check_sigwinch(void);
+void check_sigwinch(void);
+void (*x_resize_cb)(void);	/* callback for resize during edit */
 
 static int	x_file_glob(int, const char *, int, char ***);
 static int	x_command_glob(int, const char *, int, char ***);
@@ -58,7 +59,7 @@ x_sigwinch(int sig)
 	got_sigwinch = 1;
 }
 
-static void
+void
 check_sigwinch(void)
 {
 	if (got_sigwinch) {
@@ -75,11 +76,16 @@ check_sigwinch(void)
 			 * see the change cause the environ doesn't change.
 			 */
 			if (ws.ws_col) {
+				int old_cols = x_cols;
+
 				x_cols = ws.ws_col < MIN_COLS ? MIN_COLS :
 				    ws.ws_col;
 
 				if ((vp = typeset("COLUMNS", 0, 0, 0, 0)))
 					setint(vp, (int64_t) ws.ws_col);
+
+				if (x_cols != old_cols && x_resize_cb)
+					(*x_resize_cb)();
 			}
 			if (ws.ws_row && (vp = typeset("LINES", 0, 0, 0, 0)))
 				setint(vp, (int64_t) ws.ws_row);
@@ -120,12 +126,14 @@ x_getc(void)
 	char c;
 	int n;
 
-	while ((n = blocking_read(STDIN_FILENO, &c, 1)) < 0 && errno == EINTR)
+	while ((n = blocking_read(STDIN_FILENO, &c, 1)) < 0 && errno == EINTR) {
+		check_sigwinch();
 		if (trap) {
 			x_mode(false);
 			runtraps(0);
 			x_mode(true);
 		}
+	}
 	if (n != 1)
 		return -1;
 	return (int) (unsigned char) c;
Index: edit.h
===================================================================
RCS file: /home/cvs/src/bin/ksh/edit.h,v
diff -u -p -r1.13 edit.h
--- edit.h	21 Jun 2023 22:22:08 -0000	1.13
+++ edit.h	10 Jan 2026 22:46:15 -0000
@@ -39,6 +39,8 @@ extern X_chars edchars;
 #define XCF_COMMAND_FILE (XCF_COMMAND|XCF_FILE)
 
 /* edit.c */
+void	check_sigwinch(void);
+extern void (*x_resize_cb)(void);
 int	x_getc(void);
 void	x_flush(void);
 int	x_putc(int);
Index: emacs.c
===================================================================
RCS file: /home/cvs/src/bin/ksh/emacs.c,v
diff -u -p -r1.90 emacs.c
--- emacs.c	21 Jun 2023 22:22:08 -0000	1.90
+++ emacs.c	10 Jan 2026 22:55:00 -0000
@@ -105,6 +105,7 @@ static int	xlp_valid;
 /* end from 4.9 edit.h } */
 static	int	x_tty;		/* are we on a tty? */
 static	int	x_bind_quiet;	/* be quiet when binding keys */
+static void	x_emacs_resize(void);
 static int	(*x_last_command)(int);
 
 static	char   **x_histp;	/* history position */
@@ -286,6 +287,8 @@ x_emacs(char *buf, size_t len)
 	xmp = NULL;
 	x_histp = histptr + 1;
 
+	x_resize_cb = x_emacs_resize;
+	check_sigwinch();
 	xx_cols = x_cols;
 	x_col = promptlen(prompt, &p);
 	prompt_skip = p - prompt;
@@ -316,8 +319,11 @@ x_emacs(char *buf, size_t len)
 	x_last_command = NULL;
 	while (1) {
 		x_flush();
-		if ((at = x_e_getu8(line, at)) < 0)
+		if ((at = x_e_getu8(line, at)) < 0) {
+			x_resize_cb = NULL;
 			return 0;
+		}
+
 		ntries++;
 
 		if (x_arg == -1) {
@@ -378,6 +384,7 @@ x_emacs(char *buf, size_t len)
 				x_last_command = NULL;
 			break;
 		case KEOL:
+			x_resize_cb = NULL;
 			ret = xep - xbuf;
 			return (ret);
 			break;
@@ -2160,6 +2167,17 @@ x_lastcp(void)
 	}
 	xlp_valid = true;
 	return (xlp);
+}
+
+static void
+x_emacs_resize(void)
+{
+	xx_cols = x_cols;
+	x_col = promptlen(prompt, NULL);
+	if (x_col > xx_cols)
+		x_col = x_col - (x_col / xx_cols) * xx_cols;
+	x_displen = xx_cols - 2 - x_col;
+	x_adjust();
 }
 
 #endif /* EMACS */
Index: vi.c
===================================================================
RCS file: /home/cvs/src/bin/ksh/vi.c,v
diff -u -p -r1.67 vi.c
--- vi.c	20 Jul 2025 21:24:07 -0000	1.67
+++ vi.c	10 Jan 2026 23:57:16 -0000
@@ -75,6 +75,8 @@ static void	vi_error(void);
 static void	vi_macro_reset(void);
 static int	x_vi_putbuf(const char *, size_t);
 static int	isu8cont(unsigned char);
+static void	x_vi_resize(void);
+static void	calc_winsize(void);
 
 #define C_	0x1		/* a valid command that isn't a M_, E_, U_ */
 #define M_	0x2		/* movement command (h, l, etc.) */
@@ -201,6 +203,8 @@ x_vi(char *buf, size_t len)
 	int	c;
 
 	vi_reset(buf, len > LINE ? LINE : len);
+	x_resize_cb = x_vi_resize;
+	check_sigwinch();
 	vi_pprompt(1);
 	x_flush();
 	while (1) {
@@ -242,6 +246,7 @@ x_vi(char *buf, size_t len)
 		x_flush();
 	}
 
+	x_resize_cb = NULL;
 	x_putc('\r'); x_putc('\n'); x_flush();
 
 	if (c == -1 || len <= (size_t)es->linelen)
@@ -1424,8 +1429,6 @@ free_edstate(struct edstate *old)
 static void
 edit_reset(char *buf, size_t len)
 {
-	const char *p;
-
 	es = &ebuf;
 	es->cbuf = buf;
 	es->cbufsize = len;
@@ -1436,6 +1439,17 @@ edit_reset(char *buf, size_t len)
 	es->cursor = undo->cursor = 0;
 	es->winleft = undo->winleft = 0;
 
+	calc_winsize();
+	win = 0;
+	morec = ' ';
+	holdlen = 0;
+}
+
+static void
+calc_winsize(void)
+{
+	const char *p;
+
 	cur_col = pwidth = promptlen(prompt, &p);
 	prompt_skip = p - prompt;
 	if (pwidth > x_cols - 3 - MIN_EDIT_SPACE) {
@@ -1452,9 +1466,6 @@ edit_reset(char *buf, size_t len)
 	(void) memset(wbuf[0], ' ', wbuf_len);
 	(void) memset(wbuf[1], ' ', wbuf_len);
 	winwidth = x_cols - pwidth - 3;
-	win = 0;
-	morec = ' ';
-	holdlen = 0;
 }
 
 /*
@@ -2299,5 +2310,13 @@ static int
 isu8cont(unsigned char c)
 {
 	return !Flag(FVISHOW8) && (c & (0x80 | 0x40)) == 0x80;
+}
+
+static void
+x_vi_resize(void)
+{
+	calc_winsize();
+	rewindow();
+	redraw_line(0, 0);
 }
 #endif	/* VI */



-- 
wbr, Kirill