Index | Thread | Search

From:
Kirill A. Korinsky <kirill@korins.ky>
Subject:
bin/ksh: fix vi mode UTF-8 window display
To:
OpenBSD tech <tech@openbsd.org>
Date:
Thu, 22 Jan 2026 17:02:59 +0100

Download raw body.

Thread
tech@

here the last my patch for ksh which address issues with UTF-8 disply logic
which was discovered by testing of resize terminal.

It also included a fix for a crush reported by Walter Alejandro Iglesias.

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.

diff --git bin/ksh/vi.c bin/ksh/vi.c
index def7aeeae24..c16bef9a78f 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 */
@@ -1369,7 +1370,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 */
@@ -1449,6 +1450,7 @@ static void
 calc_winsize(void)
 {
 	const char *p;
+	int newlen;
 
 	cur_col = pwidth = promptlen(prompt, &p);
 	prompt_skip = p - prompt;
@@ -1458,8 +1460,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);
 	}
@@ -1880,6 +1883,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;
 
@@ -1891,8 +1895,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') {
@@ -1908,7 +1915,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) {
@@ -1916,6 +1924,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++;
@@ -2008,8 +2018,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;