Download raw body.
bin/ksh: fix emacs/vi line editor getting wrong terminal width
Hi Kirill,
As far as I tested this diff works for me. I've tested it on xterm only.
On Wed, Jan 21, 2026 at 03:17:50AM +0100, Kirill A. Korinsky wrote:
> On Mon, 12 Jan 2026 07:27:03 +0100,
> Helg <helg-openbsd@gmx.de> wrote:
> >
> > On Sun, Jan 11, 2026 at 12:58:46AM +0100, Kirill A. Korinsky wrote:
> > > 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?
> >
> > This patch is heading in the right direction but I've tested it in both
> > X11 and the MacOS Terminal application and it still needs some tweaks.
> >
> > The following is the output when I resize MacOS Terminal to 64
> > characters wide.
> >
> > apple$ /usr/src/bin/ksh/ksh
> > asd we rewr ewr qr qewrt
> > <<<<<<<<<<<<<<<
> >
> > You can see that it is adding multiple chevrons, when before it didn't
> > do that. Also, if the cursor is close to the right when you resize,
> > the chevron disappears or is replaced by an asterisk. It's likely there
> > is an off by one error somewhere.
>
> May I ask you to try this one?
>
>
> 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 21 Jan 2026 02:16:58 -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 21 Jan 2026 02:16:58 -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 21 Jan 2026 02:16:58 -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,39 @@ x_lastcp(void)
> }
> xlp_valid = true;
> return (xlp);
> +}
> +
> +static void
> +x_emacs_resize(void)
> +{
> + /* clear old content before resizing */
> + x_putc('\r');
> + x_putc('\033');
> + x_putc('[');
> + x_putc('J');
> +
> + 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;
> + if (x_displen < 1) {
> + x_col = 0;
> + x_displen = xx_cols - 2;
> + prompt_redraw = 0;
> + } else {
> + xbp = xbuf;
> + xlp_valid = false;
> + if (xcp <= x_lastcp()) {
> + x_redraw(0);
> + x_flush();
> + return;
> + }
> + }
> +
> + x_col = 0;
> + x_displen = xx_cols - 2;
> + 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 21 Jan 2026 02:16:58 -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,30 @@ static int
> isu8cont(unsigned char c)
> {
> return !Flag(FVISHOW8) && (c & (0x80 | 0x40)) == 0x80;
> +}
> +
> +static void
> +x_vi_resize(void)
> +{
> + int cur = 0, col = 0;
> +
> + /* clear old content before resizing */
> + x_putc('\r');
> + x_putc('\033');
> + x_putc('[');
> + x_putc('J');
> +
> + calc_winsize();
> +
> + while (cur < es->linelen)
> + col = newcol((unsigned char) es->cbuf[cur++], col);
> + if (col <= winwidth)
> + es->winleft = 0;
> + else if (outofwin())
> + rewindow();
> +
> + redraw_line(0, 0);
> + refresh_line(insert != 0);
> + x_flush();
> }
> #endif /* VI */
>
>
>
> --
> wbr, Kirill
>
>
--
Walter
bin/ksh: fix emacs/vi line editor getting wrong terminal width