Download raw body.
bin/ksh: fix emacs mode UTF-8 display logic
tech@,
Here the second diff which address discovered issue with UTF-8 display logic
when resize terminal, but only for emacs mode.
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.
Ok?
diff --git bin/ksh/emacs.c bin/ksh/emacs.c
index fa18ecd3a14..6ab4ef8df41 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 *);
@@ -642,16 +643,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++);
}
}
@@ -670,7 +672,7 @@ x_size_str(char *cp)
{
int size = 0;
while (*cp)
- size += x_size(*cp++);
+ size += x_size((unsigned char)*cp++);
return size;
}
@@ -698,7 +700,7 @@ x_zots(char *str)
}
x_lastcp();
while (*str && str < xlp && adj == x_adj_done)
- x_zotc(*str++);
+ x_zotcp(str++);
}
static void
@@ -714,6 +716,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)
{
@@ -1033,7 +1068,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;
@@ -1073,10 +1108,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(' ');
@@ -1133,8 +1170,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;
@@ -1143,8 +1180,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;
@@ -1805,12 +1842,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();
@@ -2164,6 +2211,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);
bin/ksh: fix emacs mode UTF-8 display logic