Download raw body.
bin/ksh: series of patches which fixes wrong terminal width and UTF-8
tech@,
sorry to attached pathces, and not inlined, I think it makes it review
simpler.
I use that pathces for last half year in emacs mode and happy with this, but
I think we should fix that bugs and commit it.
It should be applied as sequence.
Anyone willing to test it? Ok it?
--
wbr, Kirill
From f2f236782d0f6ecfa15516512c31b7cd006e9091 Mon Sep 17 00:00:00 2001
From: "Kirill A. Korinsky" <kirill@korins.ky>
Date: Wed, 21 Jan 2026 01:16:04 +0100
Subject: [PATCH 1/3] bin/ksh: fix emacs/vi mode using wrong terminal width
xterm and other terminal emulators resize the pty after starting the
shell. x_init() queries the terminal size at startup before the resize
occurs, so x_cols stays at the default 80 columns.
SIGWINCH arrives, but check_sigwinch() isn't called until after command
entry. This makes the line editor think the screen is 80 columns, which
makes it show '<' and truncate input before the actual terminal edge on
large windows, and causes weird behavior on small ones.
Call check_sigwinch() when entering edit mode and on EINTR in x_getc().
The callback lets emacs/vi mode recalculate display width and redraw.
Regarding tests, the vi mode resize path is multi stage and can
interleave with pending input, so the byte stream varies even when the
final screen is correct; to make the test deterministic, edit.c waits
for two consecutive quiet poll timeouts to ensure the child is idle
before sending TIOCSWINSZ and SIGWINCH.
---
bin/ksh/edit.c | 14 ++++++--
bin/ksh/edit.h | 2 ++
bin/ksh/emacs.c | 57 ++++++++++++++++++++++++++++++++-
bin/ksh/vi.c | 60 ++++++++++++++++++++++++++++++++---
regress/bin/ksh/edit/edit.c | 54 +++++++++++++++++++++++++++++--
regress/bin/ksh/edit/emacs.sh | 24 ++++++++++++++
regress/bin/ksh/edit/vi.sh | 24 ++++++++++++++
7 files changed, 223 insertions(+), 12 deletions(-)
diff --git bin/ksh/edit.c bin/ksh/edit.c
index a510b5f9915..09254c1e8c4 100644
--- bin/ksh/edit.c
+++ bin/ksh/edit.c
@@ -26,7 +26,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 ***, int);
static char *x_tilde_expand(const char *);
@@ -64,7 +65,7 @@ x_sigwinch(int sig)
got_sigwinch = 1;
}
-static void
+void
check_sigwinch(void)
{
if (got_sigwinch) {
@@ -81,11 +82,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);
@@ -126,12 +132,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;
diff --git bin/ksh/edit.h bin/ksh/edit.h
index e1d200b9c93..e2978f3c7de 100644
--- bin/ksh/edit.h
+++ bin/ksh/edit.h
@@ -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);
diff --git bin/ksh/emacs.c bin/ksh/emacs.c
index 399ef59ce83..e3664df112f 100644
--- bin/ksh/emacs.c
+++ bin/ksh/emacs.c
@@ -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,9 @@ x_emacs(char *buf, size_t len)
xmp = NULL;
x_histp = histptr + 1;
+ x_resize_cb = NULL;
+ check_sigwinch();
+ x_resize_cb = x_emacs_resize;
xx_cols = x_cols;
x_col = promptlen(prompt, &p);
prompt_skip = p - prompt;
@@ -316,8 +320,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 +385,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;
@@ -2166,4 +2174,51 @@ x_lastcp(void)
return (xlp);
}
+static void
+x_emacs_resize(void)
+{
+ int cr_done = 0, cleared = 0;
+
+#ifndef SMALL
+ if (cur_term != NULL) {
+ if (carriage_return != NULL &&
+ tputs(carriage_return, 1, x_putc) != ERR)
+ cr_done = 1;
+ if (clr_eos != NULL &&
+ tputs(clr_eos, 1, x_putc) != ERR)
+ cleared = 1;
+ }
+#endif
+ if (!cr_done)
+ x_putc('\r');
+ if (!cleared) {
+ 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 */
diff --git bin/ksh/vi.c bin/ksh/vi.c
index e386fa1e65f..9ac55404ade 100644
--- bin/ksh/vi.c
+++ bin/ksh/vi.c
@@ -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.) */
@@ -200,6 +202,9 @@ x_vi(char *buf, size_t len)
{
int c;
+ x_resize_cb = NULL;
+ check_sigwinch();
+ x_resize_cb = x_vi_resize;
vi_reset(buf, len > LINE ? LINE : len);
vi_pprompt(1);
x_flush();
@@ -242,6 +247,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)
@@ -1425,8 +1431,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;
@@ -1437,6 +1441,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) {
@@ -1453,9 +1468,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;
}
/*
@@ -2305,4 +2317,42 @@ isu8cont(unsigned char c)
{
return !Flag(FVISHOW8) && (c & (0x80 | 0x40)) == 0x80;
}
+
+static void
+x_vi_resize(void)
+{
+ int cur = 0, col = 0;
+ int cr_done = 0, cleared = 0;
+
+#ifndef SMALL
+ if (cur_term != NULL) {
+ if (carriage_return != NULL &&
+ tputs(carriage_return, 1, x_putc) != ERR)
+ cr_done = 1;
+ if (clr_eos != NULL &&
+ tputs(clr_eos, 1, x_putc) != ERR)
+ cleared = 1;
+ }
+#endif
+ if (!cr_done)
+ x_putc('\r');
+ if (!cleared) {
+ 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 */
diff --git regress/bin/ksh/edit/edit.c regress/bin/ksh/edit/edit.c
index 7b49f371ab6..785dc5a251b 100644
--- regress/bin/ksh/edit/edit.c
+++ regress/bin/ksh/edit/edit.c
@@ -16,6 +16,7 @@
*/
#include <sys/wait.h>
+#include <sys/ioctl.h>
#include <err.h>
#include <errno.h>
@@ -49,6 +50,7 @@ main(int argc, char *argv[])
ssize_t n;
size_t nin, nprompt, nread, nwrite;
int c, nready, ptyfd, readprompt, ret, status, timeout;
+ int pending_resize, pending_cols, pending_quiet, pending_post;
while ((c = getopt(argc, argv, "p:")) != -1) {
switch (c) {
@@ -96,14 +98,20 @@ main(int argc, char *argv[])
}
nprompt = nread = nwrite = ret = 0;
+ pending_resize = pending_cols = pending_quiet = pending_post = 0;
readprompt = 1;
while (!gotsig) {
pfd.fd = ptyfd;
- if (!readprompt && nwrite < nin)
+ if (pending_resize || pending_post) {
+ pfd.events = POLLIN;
+ timeout = WRTIM;
+ } else if (!readprompt && nwrite < nin) {
pfd.events = POLLOUT;
- else
+ timeout = WRTIM;
+ } else {
pfd.events = POLLIN;
- timeout = readprompt ? PRTIM : WRTIM;
+ timeout = readprompt ? PRTIM : WRTIM;
+ }
nready = poll(&pfd, 1, timeout);
if (nready == -1) {
if (errno == EINTR)
@@ -111,6 +119,22 @@ main(int argc, char *argv[])
err(1, "poll");
}
if (nready == 0) {
+ if (pending_resize) {
+ if (++pending_quiet >= 2) {
+ ws.ws_col = pending_cols;
+ if (ioctl(ptyfd, TIOCSWINSZ, &ws) == -1)
+ err(1, "ioctl TIOCSWINSZ");
+ kill(pid, SIGWINCH);
+ pending_resize = pending_cols = 0;
+ pending_quiet = 0;
+ pending_post = 1;
+ }
+ continue;
+ }
+ if (pending_post) {
+ pending_post = 0;
+ continue;
+ }
if (timeout == PRTIM) {
warnx("timeout waiting from prompt");
ret = 1;
@@ -136,6 +160,28 @@ main(int argc, char *argv[])
readprompt = 0;
}
} else if (pfd.revents & POLLOUT) {
+ if (in[nwrite] == '\0' && nwrite + 3 < nin &&
+ in[nwrite + 1] == 'R' &&
+ in[nwrite + 2] == 'S' &&
+ in[nwrite + 3] >= '0' && in[nwrite + 3] <= '9') {
+ size_t i = nwrite + 3;
+ int cols = 0, nd = 0;
+
+ while (i < nin && nd < 3 &&
+ in[i] >= '0' && in[i] <= '9') {
+ cols = cols * 10 + (in[i] - '0');
+ i++;
+ nd++;
+ }
+ if (nd > 0 && (i == nin ||
+ in[i] < '0' || in[i] > '9') &&
+ cols > 0) {
+ pending_resize = 1;
+ pending_cols = cols;
+ nwrite = i;
+ continue;
+ }
+ }
if (strchr(linefeed, in[nwrite]) != NULL)
readprompt = 1;
@@ -143,6 +189,8 @@ main(int argc, char *argv[])
if (n == -1)
err(1, "write");
nwrite += n;
+ if (pending_resize)
+ pending_quiet = 0;
}
}
close(ptyfd);
diff --git regress/bin/ksh/edit/emacs.sh regress/bin/ksh/edit/emacs.sh
index abd664664bb..6ae4184e928 100644
--- regress/bin/ksh/edit/emacs.sh
+++ regress/bin/ksh/edit/emacs.sh
@@ -52,6 +52,30 @@ testseq "z\0002\0363\0277\0277\0277" " # z\b\0363\0277\0277\0277z\b"
testseq "z\0002\0364\0200\0200\0200" " # z\b\0364\0200\0200\0200z\b"
testseq "z\0002\0364\0217\0277\0277" " # z\b\0364\0217\0277\0277z\b"
+# ASCII resize: cols=1/11/12
+testseq "aaaaaaaaaabbbbbbbbbb\0001\000RS1" \
+ " # aaaaaaaaaabbbbbbbbbb\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\r\033[J\r # aaaaaaa>\b\b\b\b\b\b\b\b"
+testseq "aaaaaaaaaabbbbbbbbbb\003310\0002\000RS11" \
+ " # aaaaaaaaaabbbbbbbbbb\b\b\b\b\b\b\b\b\b\b\r\033[J\raaaaabbbbb*\b\b\b\b\b\b"
+testseq "aaaaaaaaaabbbbbbbbbb\000RS12" \
+ " # aaaaaaaaaabbbbbbbbbb\r\033[J\rbbbbb <\b\b\b\b\b\b"
+
+# ASCII resize: cols=13, cursor -1/0/+1
+testseq "aaaaaaaaaabbbbbbbbbb\003313\0002\000RS13" \
+ " # aaaaaaaaaabbbbbbbbbb\b\b\b\b\b\b\b\b\b\b\b\b\b\r\033[J\r # aaaaaaaa>\b\b"
+testseq "aaaaaaaaaabbbbbbbbbb\003312\0002\000RS13" \
+ " # aaaaaaaaaabbbbbbbbbb\b\b\b\b\b\b\b\b\b\b\b\b\r\033[J\r # aaaaaaaa>\b"
+testseq "aaaaaaaaaabbbbbbbbbb\003311\0002\000RS13" \
+ " # aaaaaaaaaabbbbbbbbbb\b\b\b\b\b\b\b\b\b\b\b\r\033[J\raaaaaabbbbb*\b\b\b\b\b\b\b"
+
+# ASCII resize: cols=79/80/81
+testseq "aaaaaaaaaaaaaaaaaaaaaaaaabbbbbbbbbbbbbbbbbbbbbbbbbccccccccccccccccccccccccc\0001\000RS79" \
+ " # aaaaaaaaaaaaaaaaaaaaaaaaabbbbbbbbbbbbbbbbbbbbbbbbbccccccccccccccccccccccccc\rbbbbbbbbbbbbccccccccccccccccccccccccc <\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\r # aaaaaaaaaaaaaaaaaaaaaaaaabbbbbbbbbbbbbbbbbbbbbbbbbccccccccccccccccccccccccc \b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\r\033[J\r # aaaaaaaaaaaaaaaaaaaaaaaaabbbbbbbbbbbbbbbbbbbbbbbbbcccccccccccccccccccccccc>\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b"
+testseq "aaaaaaaaaaaaaaaaaaaaaaaaabbbbbbbbbbbbbbbbbbbbbbbbbccccccccccccccccccccccccc\0001\000RS80" \
+ " # aaaaaaaaaaaaaaaaaaaaaaaaabbbbbbbbbbbbbbbbbbbbbbbbbccccccccccccccccccccccccc\rbbbbbbbbbbbbccccccccccccccccccccccccc <\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\r # aaaaaaaaaaaaaaaaaaaaaaaaabbbbbbbbbbbbbbbbbbbbbbbbbccccccccccccccccccccccccc \b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b"
+testseq "aaaaaaaaaaaaaaaaaaaaaaaaabbbbbbbbbbbbbbbbbbbbbbbbbccccccccccccccccccccccccc\0001\000RS81" \
+ " # aaaaaaaaaaaaaaaaaaaaaaaaabbbbbbbbbbbbbbbbbbbbbbbbbccccccccccccccccccccccccc\rbbbbbbbbbbbbccccccccccccccccccccccccc <\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\r # aaaaaaaaaaaaaaaaaaaaaaaaabbbbbbbbbbbbbbbbbbbbbbbbbccccccccccccccccccccccccc \b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\r\033[J\r # aaaaaaaaaaaaaaaaaaaaaaaaabbbbbbbbbbbbbbbbbbbbbbbbbccccccccccccccccccccccccc \b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b"
+
# insertion of incomplete UTF-8
testseq "z\0002\0302\0006" " # z\b\0302z\bz"
testseq "z\0002\0377\0006" " # z\b\0377z\bz"
diff --git regress/bin/ksh/edit/vi.sh regress/bin/ksh/edit/vi.sh
index d9ae7967908..eef5886f999 100644
--- regress/bin/ksh/edit/vi.sh
+++ regress/bin/ksh/edit/vi.sh
@@ -233,3 +233,27 @@ testseq "(x)\00330%hrc" " # (x)\b\b\b(x\bc\b"
# ^R: Redraw.
testseq "test\0033h\0022" " # test\b\b\r\r\n # test\b\b"
+
+# ASCII resize: cols=1/11/12
+testseq "aaaaaaaaaabbbbbbbbbb\0033hhhhhhhhhhhhhhhhhhh\000RS1" \
+ " # aaaaaaaaaabbbbbbbbbb\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\r\033[J# aaaaaaa >\r# "
+testseq "aaaaaaaaaabbbbbbbbbb\0033hhhhhhhhh\000RS11" \
+ " # aaaaaaaaaabbbbbbbbbb\b\b\b\b\b\b\b\b\b\b\r\033[J# aaabbbb +\b\b\b\b\b\b"
+testseq "aaaaaaaaaabbbbbbbbbb\0033\000RS12" \
+ " # aaaaaaaaaabbbbbbbbbb\b\r\033[J# bbbb <\b\b\b\b\b\b"
+
+# ASCII resize: cols=13, cursor -1/0/+1
+testseq "aaaaaaaaaabbbbbbbbbb\0033hhhhhhhhhhhhh\000RS13" \
+ " # aaaaaaaaaabbbbbbbbbb\b\b\b\b\b\b\b\b\b\b\b\b\b\b\r\033[J # aaaaaaa >\b\b\b"
+testseq "aaaaaaaaaabbbbbbbbbb\0033hhhhhhhhhhhh\000RS13" \
+ " # aaaaaaaaaabbbbbbbbbb\b\b\b\b\b\b\b\b\b\b\b\b\b\r\033[J # aaaaaab +\b\b\b\b\b\b"
+testseq "aaaaaaaaaabbbbbbbbbb\0033hhhhhhhhhhh\000RS13" \
+ " # aaaaaaaaaabbbbbbbbbb\b\b\b\b\b\b\b\b\b\b\b\b\r\033[J # aaaaabb +\b\b\b\b\b\b"
+
+# ASCII resize: cols=79/80/81
+testseq "aaaaaaaaaaaaaaaaaaaaaaaaabbbbbbbbbbbbbbbbbbbbbbbbbccccccccccccccccccccccccc\0033hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh\000RS79" \
+ " # aaaaaaaaaaaaaaaaaaaaaaaaabbbbbbbbbbbbbbbbbbbbbbbbbccccccccccccccccccccccc\r # bbbbbbbbbbbbbcccccccccccccccccccccccc <\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\bc\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\baaaaaaaaaaaaaaaaaaaaaaaaabbbbbbbbbbbbbbbbbbbbbbbbbcccccccccccccccccccccccc >\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\r\033[J # aaaaaaaaaaaaaaaaaaaaaaaaabbbbbbbbbbbbbbbbbbbbbbbbbccccccccccccccccccccccc >\r # "
+testseq "aaaaaaaaaaaaaaaaaaaaaaaaabbbbbbbbbbbbbbbbbbbbbbbbbccccccccccccccccccccccccc\0033hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh\000RS80" \
+ " # aaaaaaaaaaaaaaaaaaaaaaaaabbbbbbbbbbbbbbbbbbbbbbbbbccccccccccccccccccccccc\r # bbbbbbbbbbbbbcccccccccccccccccccccccc <\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\bc\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\baaaaaaaaaaaaaaaaaaaaaaaaabbbbbbbbbbbbbbbbbbbbbbbbbcccccccccccccccccccccccc >\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b"
+testseq "aaaaaaaaaaaaaaaaaaaaaaaaabbbbbbbbbbbbbbbbbbbbbbbbbccccccccccccccccccccccccc\0033hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh\000RS81" \
+ " # aaaaaaaaaaaaaaaaaaaaaaaaabbbbbbbbbbbbbbbbbbbbbbbbbccccccccccccccccccccccc\r # bbbbbbbbbbbbbcccccccccccccccccccccccc <\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\bc\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\baaaaaaaaaaaaaaaaaaaaaaaaabbbbbbbbbbbbbbbbbbbbbbbbbcccccccccccccccccccccccc >\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\r\033[J # aaaaaaaaaaaaaaaaaaaaaaaaabbbbbbbbbbbbbbbbbbbbbbbbbccccccccccccccccccccccccc >\r # "
--
2.54.0
From 6a24f1c027c1e3937ed3fa91662661f9637dafce Mon Sep 17 00:00:00 2001
From: "Kirill A. Korinsky" <kirill@korins.ky>
Date: Wed, 21 Jan 2026 17:05:09 +0100
Subject: [PATCH 2/3] bin/ksh: fix emacs mode UTF-8 display logic
The emacs line editor counts bytes when deciding what fits on screen and
can stop in the middle of a UTF-8 sequence. This leaves partial UTF-8
sequences on output, misplaces the "<" indicator, and can desync cursor
movement after a resize.
Track display columns instead of bytes by skipping UTF-8 continuation
bytes when positioning and redrawing. Add x_zotcp() to emit buffer bytes
without splitting UTF-8 sequences, and use it in cursor motion and
transpose paths.
---
bin/ksh/emacs.c | 81 ++++++++++++++++++++++++++++-------
regress/bin/ksh/edit/emacs.sh | 32 ++++++++++++++
2 files changed, 97 insertions(+), 16 deletions(-)
diff --git bin/ksh/emacs.c bin/ksh/emacs.c
index e3664df112f..d8b86c2851e 100644
--- bin/ksh/emacs.c
+++ bin/ksh/emacs.c
@@ -130,6 +130,7 @@ static int x_size_str(char *);
static int x_size(int);
static void x_zots(char *);
static void x_zotc(int);
+static void x_zotcp(char *);
static void x_load_hist(char **);
static int x_search(char *, int, int);
static int x_match(char *, char *);
@@ -643,16 +644,17 @@ x_fword(void)
static void
x_goto(char *cp)
{
- if (cp < xbp || cp >= (xbp + x_displen)) {
+ x_lastcp();
+ if (cp < xbp || cp > xlp) {
/* we are heading off screen */
xcp = cp;
x_adjust();
} else if (cp < xcp) { /* move back */
while (cp < xcp)
- x_bs((unsigned char)*--xcp);
+ x_bs(*--xcp);
} else if (cp > xcp) { /* move forward */
while (cp > xcp)
- x_zotc((unsigned char)*xcp++);
+ x_zotcp(xcp++);
}
}
@@ -671,7 +673,7 @@ x_size_str(char *cp)
{
int size = 0;
while (*cp)
- size += x_size(*cp++);
+ size += x_size((unsigned char)*cp++);
return size;
}
@@ -699,7 +701,7 @@ x_zots(char *str)
}
x_lastcp();
while (*str && str < xlp && adj == x_adj_done)
- x_zotc(*str++);
+ x_zotcp(str++);
}
static void
@@ -715,6 +717,39 @@ x_zotc(int c)
x_e_putc(c);
}
+static void
+x_zotcp(char *p)
+{
+ unsigned char uc = (unsigned char)*p;
+
+ if (uc == '\t') {
+ /* Kludge, tabs are always four spaces. */
+ x_e_puts(" ");
+ return;
+ }
+ if (isu8cont(uc)) {
+ if (x_col <= xx_cols) {
+ x_putc(uc);
+ }
+ if (x_adj_ok && !isu8cont((unsigned char)p[1]) &&
+ (x_col < 0 || x_col >= (xx_cols - 2)))
+ x_adjust();
+ return;
+ }
+ if (iscntrl(uc)) {
+ x_e_putc('^');
+ x_e_putc(UNCTRL(uc));
+ } else {
+ if (x_col < xx_cols) {
+ x_putc(uc);
+ x_col++;
+ }
+ if (x_adj_ok && !isu8cont((unsigned char)p[1]) &&
+ (x_col < 0 || x_col >= (xx_cols - 2)))
+ x_adjust();
+ }
+}
+
static int
x_mv_back(int c)
{
@@ -1034,7 +1069,7 @@ x_clear_screen(int c)
static void
x_redraw(int limit)
{
- int i, j, truncate = 0;
+ int i, j, truncate = 0, dcols;
char *cp;
x_adj_ok = 0;
@@ -1076,10 +1111,12 @@ x_redraw(int limit)
if (xbp != xbuf || xep > xlp)
limit = xx_cols;
if (limit >= 0) {
- if (xep > xlp)
- i = 0; /* we fill the line */
- else
- i = limit - (xlp - xbp);
+ dcols = 0;
+ for (cp = xbp; cp < xlp; cp++)
+ dcols += x_size((unsigned char)*cp);
+ i = limit - dcols;
+ if (i < 0)
+ i = 0;
for (j = 0; j < i && x_col < (xx_cols - 2); j++)
x_e_putc(' ');
@@ -1136,8 +1173,8 @@ x_transpose(int c)
*/
x_bs(xcp[-1]);
x_bs(xcp[-2]);
- x_zotc(xcp[-1]);
- x_zotc(xcp[-2]);
+ x_zotcp(&xcp[-1]);
+ x_zotcp(&xcp[-2]);
tmp = xcp[-1];
xcp[-1] = xcp[-2];
xcp[-2] = tmp;
@@ -1146,8 +1183,8 @@ x_transpose(int c)
* cursor, move cursor position along one.
*/
x_bs(xcp[-1]);
- x_zotc(xcp[0]);
- x_zotc(xcp[-1]);
+ x_zotcp(&xcp[0]);
+ x_zotcp(&xcp[-1]);
tmp = xcp[-1];
xcp[-1] = xcp[0];
xcp[0] = tmp;
@@ -1810,12 +1847,22 @@ do_complete(int flags, /* XCF_{COMMAND,FILE,COMMAND_FILE} */
static void
x_adjust(void)
{
+ char *cp;
+ int col;
+
x_adj_done++; /* flag the fact that we were called. */
/*
* we had a problem if the prompt length > xx_cols / 2
*/
- if ((xbp = xcp - (x_displen / 2)) < xbuf)
- xbp = xbuf;
+ col = x_displen / 2;
+ cp = xcp;
+ while (cp > xbuf && col > 0) {
+ cp--;
+ while (cp > xbuf && isu8cont(*cp))
+ cp--;
+ col -= x_size((unsigned char)*cp);
+ }
+ xbp = cp;
xlp_valid = false;
x_redraw(xx_cols);
x_flush();
@@ -2169,6 +2216,8 @@ x_lastcp(void)
for (i = 0, rcp = xbp; rcp < xep && i < x_displen; rcp++)
i += x_size((unsigned char)*rcp);
xlp = rcp;
+ while (xlp < xep && isu8cont(*xlp))
+ xlp++;
}
xlp_valid = true;
return (xlp);
diff --git regress/bin/ksh/edit/emacs.sh regress/bin/ksh/edit/emacs.sh
index 6ae4184e928..736d54f0616 100644
--- regress/bin/ksh/edit/emacs.sh
+++ regress/bin/ksh/edit/emacs.sh
@@ -76,6 +76,38 @@ testseq "aaaaaaaaaaaaaaaaaaaaaaaaabbbbbbbbbbbbbbbbbbbbbbbbbccccccccccccccccccccc
testseq "aaaaaaaaaaaaaaaaaaaaaaaaabbbbbbbbbbbbbbbbbbbbbbbbbccccccccccccccccccccccccc\0001\000RS81" \
" # aaaaaaaaaaaaaaaaaaaaaaaaabbbbbbbbbbbbbbbbbbbbbbbbbccccccccccccccccccccccccc\rbbbbbbbbbbbbccccccccccccccccccccccccc <\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\r # aaaaaaaaaaaaaaaaaaaaaaaaabbbbbbbbbbbbbbbbbbbbbbbbbccccccccccccccccccccccccc \b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\r\033[J\r # aaaaaaaaaaaaaaaaaaaaaaaaabbbbbbbbbbbbbbbbbbbbbbbbbccccccccccccccccccccccccc \b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b"
+# ASCII window indicator at left edge
+testseq "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb" \
+ " # aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\rbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb <\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b"
+
+# UTF-8 window indicator at left edge
+testseq "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\0303\0266bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb" \
+ " # aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\0303\0266bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\r\0303\0266bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb <\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b"
+
+# ASCII redraw at right edge
+PS1='012345678901234567890123456789012345678901234567890123456789'
+testseq "#aaaaaaaaaaaaaaaaab\0033#" "012345678901234567890123456789012345678901234567890123456789#aaaaaaaaaaaaaaaaa\raaaaaaaaa <\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\bb\r012345678901234567890123456789012345678901234567890123456789aaaaaaaaaaaaaaaaab \b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b"
+
+# UTF-8 redraw at right edge
+testseq "#aaaaaaaaaaaaaaaaa\0303\0266\0033#" "012345678901234567890123456789012345678901234567890123456789#aaaaaaaaaaaaaaaaa\raaaaaaaaa <\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\0303\0266\r012345678901234567890123456789012345678901234567890123456789aaaaaaaaaaaaaaaaa\0303\0266 \b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b"
+PS1=' # '
+
+# ASCII window indicator at right edge
+testseq "\003342ab\003397b\0001" \
+ " # aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\raaaabbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb <\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\rbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb <\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\bbbbbbbbbbbbbbbbbbbbbbbbb\r # aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb>\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b"
+
+# UTF-8 window indicator at right edge
+testseq "\003342a\0303\0266\003397b\0001" \
+ " # aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\0303\0266bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\raaaa\0303\0266bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb <\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\rbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb <\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\bbbbbbbbbbbbbbbbbbbbbbbbb\r # aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\0303\0266bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb>\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b"
+
+# ASCII window indicator on both sides
+testseq "\003342ab\003397b\0001\003379\0006" \
+ " # aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\raaaabbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb <\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\rbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb <\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\bbbbbbbbbbbbbbbbbbbbbbbbb\r # aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb>\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\rbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb*\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b"
+
+# UTF-8 window indicator on both sides
+testseq "\003342a\0303\0266\003397b\0001\003379\0006" \
+ " # aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\0303\0266bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\raaaa\0303\0266bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb <\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\rbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb <\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\bbbbbbbbbbbbbbbbbbbbbbbbb\r # aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\0303\0266bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb>\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\ra\0303\0266bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb*\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b"
+
# insertion of incomplete UTF-8
testseq "z\0002\0302\0006" " # z\b\0302z\bz"
testseq "z\0002\0377\0006" " # z\b\0377z\bz"
--
2.54.0
From 0832ae0c5c91a818cf4a7004cdde8b02ba7a8e9a Mon Sep 17 00:00:00 2001
From: "Kirill A. Korinsky" <kirill@korins.ky>
Date: Thu, 22 Jan 2026 16:54:45 +0100
Subject: [PATCH 3/3] bin/ksh: fix vi mode UTF-8 window display
The vi line editor sizes its window buffers by column count and stops
rendering at the right edge. With UTF-8 input, a continuation byte can
land at the edge, producing garbage like "\\xb9<" and misplacing the
indicator; small-window resizes can also overrun the window buffer.
Size the window buffers for UTF-8 and allow trailing continuation bytes
at the right edge. When a trailing UTF-8 byte is present, force the
indicator to print as a space plus the marker in the usual position.
---
bin/ksh/vi.c | 26 +++++++++++++++++++-------
regress/bin/ksh/edit/vi.sh | 32 ++++++++++++++++++++++++++++++++
2 files changed, 51 insertions(+), 7 deletions(-)
diff --git bin/ksh/vi.c bin/ksh/vi.c
index 9ac55404ade..edea59b40ff 100644
--- bin/ksh/vi.c
+++ bin/ksh/vi.c
@@ -24,6 +24,7 @@
#undef CTRL
#define CTRL(x) ((x) & 0x1F) /* ASCII */
+#define UTF8_MAXBYTES 4
struct edstate {
char *cbuf; /* main buffer to build the command line */
@@ -1371,7 +1372,7 @@ static int prompt_trunc; /* how much of prompt to truncate */
static int prompt_skip; /* how much of prompt to skip */
static int winwidth; /* available column positions */
static char *wbuf[2]; /* current & previous window buffer */
-static int wbuf_len; /* length of window buffers (x_cols-3)*/
+static int wbuf_len; /* length of window buffers */
static int win; /* number of window buffer in use */
static char morec; /* more character at right of window */
static char holdbuf[LINE]; /* place to hold last edit buffer */
@@ -1451,6 +1452,7 @@ static void
calc_winsize(void)
{
const char *p;
+ int newlen;
cur_col = pwidth = promptlen(prompt, &p);
prompt_skip = p - prompt;
@@ -1460,8 +1462,9 @@ calc_winsize(void)
pwidth -= prompt_trunc;
} else
prompt_trunc = 0;
- if (!wbuf_len || wbuf_len != x_cols - 3) {
- wbuf_len = x_cols - 3;
+ newlen = (x_cols - 3) * UTF8_MAXBYTES;
+ if (!wbuf_len || wbuf_len != newlen) {
+ wbuf_len = newlen;
wbuf[0] = aresize(wbuf[0], wbuf_len, APERM);
wbuf[1] = aresize(wbuf[1], wbuf_len, APERM);
}
@@ -1884,6 +1887,7 @@ display(char *wb1, char *wb2, int leftside)
int ncol; /* display column of the cursor */
int cnt; /* remaining display columns to fill */
int moreright;
+ int trailu8;
char mc; /* new "more character" at the right of window */
unsigned char ch;
@@ -1895,8 +1899,11 @@ display(char *wb1, char *wb2, int leftside)
ncol = col = 0;
cur = es->winleft;
moreright = 0;
+ trailu8 = 0;
twb1 = wb1;
- while (col < winwidth && cur < es->linelen) {
+ while (cur < es->linelen &&
+ (col < winwidth ||
+ (col == winwidth && isu8cont(es->cbuf[cur])))) {
if (cur == es->cursor && leftside)
ncol = col + pwidth;
if ((ch = es->cbuf[cur]) == '\t') {
@@ -1912,7 +1919,8 @@ display(char *wb1, char *wb2, int leftside)
}
ch &= 0x7f;
}
- if (col < winwidth) {
+ if (col < winwidth ||
+ (col == winwidth && isu8cont(ch))) {
if (ch < ' ' || ch == 0x7f) {
*twb1++ = '^';
if (++col < winwidth) {
@@ -1920,6 +1928,8 @@ display(char *wb1, char *wb2, int leftside)
col++;
}
} else {
+ if (col == winwidth && isu8cont(ch))
+ trailu8 = 1;
*twb1++ = ch;
if (col == 0 || !isu8cont(ch))
col++;
@@ -2012,8 +2022,10 @@ display(char *wb1, char *wb2, int leftside)
mc = '>';
else
mc = ' ';
- if (mc != morec) {
- ed_mov_opt(pwidth + winwidth + 1, wb1);
+ if (mc != morec || trailu8) {
+ ed_mov_opt(pwidth + winwidth, wb1);
+ x_putc(' ');
+ cur_col++;
x_putc(mc);
cur_col++;
morec = mc;
diff --git regress/bin/ksh/edit/vi.sh regress/bin/ksh/edit/vi.sh
index eef5886f999..dd4b9719ce7 100644
--- regress/bin/ksh/edit/vi.sh
+++ regress/bin/ksh/edit/vi.sh
@@ -257,3 +257,35 @@ testseq "aaaaaaaaaaaaaaaaaaaaaaaaabbbbbbbbbbbbbbbbbbbbbbbbbccccccccccccccccccccc
" # aaaaaaaaaaaaaaaaaaaaaaaaabbbbbbbbbbbbbbbbbbbbbbbbbccccccccccccccccccccccc\r # bbbbbbbbbbbbbcccccccccccccccccccccccc <\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\bc\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\baaaaaaaaaaaaaaaaaaaaaaaaabbbbbbbbbbbbbbbbbbbbbbbbbcccccccccccccccccccccccc >\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b"
testseq "aaaaaaaaaaaaaaaaaaaaaaaaabbbbbbbbbbbbbbbbbbbbbbbbbccccccccccccccccccccccccc\0033hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh\000RS81" \
" # aaaaaaaaaaaaaaaaaaaaaaaaabbbbbbbbbbbbbbbbbbbbbbbbbccccccccccccccccccccccc\r # bbbbbbbbbbbbbcccccccccccccccccccccccc <\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\bc\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\baaaaaaaaaaaaaaaaaaaaaaaaabbbbbbbbbbbbbbbbbbbbbbbbbcccccccccccccccccccccccc >\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\r\033[J # aaaaaaaaaaaaaaaaaaaaaaaaabbbbbbbbbbbbbbbbbbbbbbbbbccccccccccccccccccccccccc >\r # "
+
+# ASCII window indicator at left edge
+testseq "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabbbbbbbbbb" \
+ " # aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabbbbbbbbb\r # aaaaaaaaaaaaaaaaaaaaaaaaaaabbbbbbbbbb <\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b"
+
+# UTF-8 window indicator at left edge
+testseq "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\0303\0266bbbbbbbbb" \
+ " # aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\0303\0266bbbbbbbb\r # aaaaaaaaaaaaaaaaaaaaaaaaaaa\0303\0266bbbbbbbbb <\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b"
+
+# ASCII window indicator at right edge
+testseq "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabb\00330" \
+ " # aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b <\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\bbb\b\baaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa >\r # "
+
+# UTF-8 window indicator at right edge
+testseq "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\0303\0266b\00330" \
+ " # aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b <\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\0303\0266b\b\baaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa >\r # "
+
+# ASCII window indicator on both sides
+testseq "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\003340h" \
+ " # aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b <\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\baaaaaabbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\r # aaaaaabbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb \b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\r # aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabbbbbbbbbbbbbbbbbbbbbbbbbbb +\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b"
+
+# UTF-8 window indicator on both sides
+testseq "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\0303\0266bbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\003340h" \
+ " # aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b <\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\baaaaaa\0303\0266bbbbbbbbbbbbbbbbbbbbbbbbbbbbb\r # aaaaaa\0303\0266bbbbbbbbbbbbbbbbbbbbbbbbbbbbbb \b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\r # aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\0303\0266bbbbbbbbbbbbbbbbbbbbbbbbbb +\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b"
+
+# ASCII redraw at right edge
+testseq "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabb\00330\0022" \
+ " # aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b <\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\bbb\b\baaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa >\r # \r\r\n # aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa >\r # "
+
+# UTF-8 redraw at right edge
+testseq "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\0303\0266b\00330\0022" \
+ " # aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b <\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\0303\0266b\b\baaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa >\r # \r\r\n # aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa >\r # "
--
2.54.0
bin/ksh: series of patches which fixes wrong terminal width and UTF-8