Module Name:    src
Committed By:   rin
Date:           Fri Nov 10 14:44:13 UTC 2017

Modified Files:
        src/external/bsd/nvi/dist/vi: vs_line.c vs_refresh.c vs_relative.c

Log Message:
- Fix cursor position when a multiwidth char does not fit within a line.
- Put cursor on the leftmost column of a multiwidth char, instead of
  the rightmost one. Otherwise, some terminal emulators do not focus on
  the entire of the char.

Logic taken from nvi-m17n by itojun.


To generate a diff of this commit:
cvs rdiff -u -r1.3 -r1.4 src/external/bsd/nvi/dist/vi/vs_line.c \
    src/external/bsd/nvi/dist/vi/vs_relative.c
cvs rdiff -u -r1.6 -r1.7 src/external/bsd/nvi/dist/vi/vs_refresh.c

Please note that diffs are not public domain; they are subject to the
copyright notices on the relevant files.

Modified files:

Index: src/external/bsd/nvi/dist/vi/vs_line.c
diff -u src/external/bsd/nvi/dist/vi/vs_line.c:1.3 src/external/bsd/nvi/dist/vi/vs_line.c:1.4
--- src/external/bsd/nvi/dist/vi/vs_line.c:1.3	Sun Jan 26 21:43:45 2014
+++ src/external/bsd/nvi/dist/vi/vs_line.c	Fri Nov 10 14:44:13 2017
@@ -1,4 +1,4 @@
-/*	$NetBSD: vs_line.c,v 1.3 2014/01/26 21:43:45 christos Exp $ */
+/*	$NetBSD: vs_line.c,v 1.4 2017/11/10 14:44:13 rin Exp $ */
 /*-
  * Copyright (c) 1993, 1994
  *	The Regents of the University of California.  All rights reserved.
@@ -16,7 +16,7 @@
 static const char sccsid[] = "Id: vs_line.c,v 10.38 2002/01/19 21:59:07 skimo Exp  (Berkeley) Date: 2002/01/19 21:59:07 ";
 #endif /* not lint */
 #else
-__RCSID("$NetBSD: vs_line.c,v 1.3 2014/01/26 21:43:45 christos Exp $");
+__RCSID("$NetBSD: vs_line.c,v 1.4 2017/11/10 14:44:13 rin Exp $");
 #endif
 
 #include <sys/types.h>
@@ -271,38 +271,64 @@ empty:					(void)gp->scr_addstr(sp,
 
 	/* Do it the hard way, for leftright scrolling screens. */
 	if (O_ISSET(sp, O_LEFTRIGHT)) {
-		for (; offset_in_line < len; ++offset_in_line) {
-			chlen = (ch = (UCHAR_T)*p++) == L('\t') && !list_tab ?
+		 while (offset_in_line < len) {
+			ch = (UCHAR_T)*p;
+			chlen = (ch == '\t' && !list_tab) ?
 			    TAB_OFF(scno) : KEY_COL(sp, ch);
-			if ((scno += chlen) >= skip_cols)
-				break;
+
+			/* easy cases first. */
+			if (scno + chlen < skip_cols) {
+				scno += chlen;
+				p++;
+				offset_in_line++;
+				continue;
+			}
+
+			if (scno + chlen == skip_cols) {
+				scno += chlen;
+				p++;
+				offset_in_line++;
+			}
+
+			break;
 		}
 
 		/* Set cols_per_screen to 2nd and later line length. */
 		cols_per_screen = sp->cols;
 
 		/* Put starting info for this line in the cache. */
-		if (offset_in_line >= len) {
-			smp->c_sboff = offset_in_line;
-			smp->c_scoff = 255;
-		} else if (scno != skip_cols) {
-			smp->c_sboff = offset_in_line;
-			smp->c_scoff =
-			    offset_in_char = chlen - (scno - skip_cols);
-			--p;
-		} else {
-			smp->c_sboff = ++offset_in_line;
-			smp->c_scoff = 0;
-		}
+		smp->c_sboff = offset_in_line;
+		smp->c_scoff = offset_in_char = scno + chlen - skip_cols;
 	}
 
 	/* Do it the hard way, for historic line-folding screens. */
 	else {
-		for (; offset_in_line < len; ++offset_in_line) {
-			chlen = (ch = (UCHAR_T)*p++) == L('\t') && !list_tab ?
+		 while (offset_in_line < len) {
+			ch = (UCHAR_T)*p;
+			chlen = (ch == '\t' && !list_tab) ?
 			    TAB_OFF(scno) : KEY_COL(sp, ch);
-			if ((scno += chlen) < cols_per_screen)
+
+			/* Easy case first. */
+			if (scno + chlen < cols_per_screen) {
+				scno += chlen;
+				p++;
+				offset_in_line++;
 				continue;
+			}
+
+			/*
+			 * Since we can't generally cross the rightmost column
+			 * by displaying multi-width char, we must check it.
+			 * In that case, we fake the scno so that you'll see
+			 * that the line was already filled up completely.
+			 */
+			if (!INTISWIDE(ch) || scno + chlen == cols_per_screen) {
+				scno += chlen;
+				p++;
+				offset_in_line++;
+			} else
+				scno = cols_per_screen;
+
 			scno -= cols_per_screen;
 
 			/* Set cols_per_screen to 2nd and later line length. */
@@ -320,9 +346,10 @@ empty:					(void)gp->scr_addstr(sp,
 		if (scno != 0) {
 			smp->c_sboff = offset_in_line;
 			smp->c_scoff = offset_in_char = chlen - scno;
-			--p;
+			offset_in_line--;
+			p--;
 		} else {
-			smp->c_sboff = ++offset_in_line;
+			smp->c_sboff = offset_in_line;
 			smp->c_scoff = 0;
 		}
 	}
@@ -334,10 +361,16 @@ display:
 	 * called repeatedly with a valid pointer to a cursor position.
 	 * Don't fill anything in unless it's the right line and the right
 	 * character, and the right part of the character...
+	 *
+	 * It is not true that every wide chars occupy at least single column.
+	 * - It is safe to compare sp->cno and offset_in_line since they are
+	 *   both offset in unit of CHAR_T.
+	 * - We can't simply compare offset_in_line + cols_per_screen against
+	 *   sp->cno, since cols_per_screen is screen column, not offset in
+	 *   CHAR_T.  Do it slowly.
 	 */
 	if (yp == NULL ||
-	    smp->lno != sp->lno || sp->cno < offset_in_line ||
-	    offset_in_line + cols_per_screen < sp->cno) {
+	    smp->lno != sp->lno || sp->cno < offset_in_line) {
 		cno_cnt = 0;
 		/* If the line is on the screen, quit. */
 		if (is_cached || no_draw)
@@ -358,6 +391,23 @@ display:
 		}
 
 		/*
+		 * Since we can't generally cross the rightmost column
+		 * by displaying multi-width char, we must check it.
+		 * In that case, we fake the scno so that you'll see
+		 * that the line was already filled up completely.
+		 */
+		if (INTISWIDE(ch) && scno > cols_per_screen) {
+			smp->c_ecsize = chlen;
+			smp->c_eclen = 0;
+
+			is_partial = 1;
+
+			smp->c_eboff = offset_in_line;
+
+			/* Terminate the loop. */
+			offset_in_line = len;
+		} else
+		/*
 		 * Only display up to the right-hand column.  Set a flag if
 		 * the entire character wasn't displayed for use in setting
 		 * the cursor.  If reached the end of the line, set the cache
@@ -400,6 +450,8 @@ display:
 					*xp = scno - smp->c_ecsize;
 				else
 					*xp = scno - chlen;
+			else if (INTISWIDE(ch))
+				*xp = scno - chlen;
 			else
 				*xp = scno - 1;
 			if (O_ISSET(sp, O_NUMBER) &&
@@ -437,8 +489,8 @@ display:
 			if (cbp + chlen >= ecbp)
 				FLUSH;
 
-			/* don't display half a wide character */
-			if (is_partial && CHAR_WIDTH(sp, ch) > 1) {
+			/* Don't display half a multi-width character */
+			if (is_partial && INTISWIDE(ch)) {
 				*cbp++ = ' ';
 				break;
 			}
@@ -458,7 +510,7 @@ display:
 
 	if (scno < cols_per_screen) {
 		/* If didn't paint the whole line, update the cache. */
-		smp->c_ecsize = smp->c_eclen = KEY_LEN(sp, ch);
+		smp->c_ecsize = smp->c_eclen = KEY_COL(sp, ch);
 		smp->c_eboff = len - 1;
 
 		/*
Index: src/external/bsd/nvi/dist/vi/vs_relative.c
diff -u src/external/bsd/nvi/dist/vi/vs_relative.c:1.3 src/external/bsd/nvi/dist/vi/vs_relative.c:1.4
--- src/external/bsd/nvi/dist/vi/vs_relative.c:1.3	Sun Jan 26 21:43:45 2014
+++ src/external/bsd/nvi/dist/vi/vs_relative.c	Fri Nov 10 14:44:13 2017
@@ -1,4 +1,4 @@
-/*	$NetBSD: vs_relative.c,v 1.3 2014/01/26 21:43:45 christos Exp $ */
+/*	$NetBSD: vs_relative.c,v 1.4 2017/11/10 14:44:13 rin Exp $ */
 /*-
  * Copyright (c) 1993, 1994
  *	The Regents of the University of California.  All rights reserved.
@@ -16,7 +16,7 @@
 static const char sccsid[] = "Id: vs_relative.c,v 10.18 2001/07/08 13:02:48 skimo Exp  (Berkeley) Date: 2001/07/08 13:02:48 ";
 #endif /* not lint */
 #else
-__RCSID("$NetBSD: vs_relative.c,v 1.3 2014/01/26 21:43:45 christos Exp $");
+__RCSID("$NetBSD: vs_relative.c,v 1.4 2017/11/10 14:44:13 rin Exp $");
 #endif
 
 #include <sys/types.h>
@@ -162,22 +162,84 @@ done:		if (diffp != NULL)		/* XXX */
 			curoff -= sp->cols;				\
 	}								\
 }
-	if (cnop == NULL)
-		while (len--) {
-			chlen = CHLEN(curoff);
+	if (cnop == NULL) {
+		while (len > 0) {
+			ch = (UCHAR_T)*p;
+
+			/* singlebyte case */
+			if (!INTISWIDE(ch)) {
+				chlen = CHLEN(curoff);
+				last = scno;
+				scno += chlen;
+				len--;
+				/* p will be modified in CHLEN() */
+				TAB_RESET;
+				continue;
+			}
+
+			/* multibyte case */
+			chlen = CHAR_WIDTH(sp, ch);
 			last = scno;
 			scno += chlen;
-			TAB_RESET;
+			len--;
+			p++;
+
+			/*
+			 * If multi-width char crosses the end-of-screen,
+			 * put it on the next line.
+			 */
+			curoff += chlen;
+			if (!leftright && curoff >= sp->cols) {
+				if (curoff == sp->cols)
+					curoff = 0;
+				else {
+					scno -= scno % sp->cols;
+					scno += chlen;
+					curoff = chlen;
+				}
+			}
 		}
-	else
+	} else {
 		for (cno = *cnop;; --cno) {
-			chlen = CHLEN(curoff);
+			ch = (UCHAR_T)*p;
+
+			/* singlebyte case */
+			if (!INTISWIDE(ch)) {
+				chlen = CHLEN(curoff);
+				last = scno;
+				scno += chlen;
+				/* p will be modified in CHLEN() */
+				TAB_RESET;
+				if (cno == 0)
+					break;
+				continue;
+			}
+
+			/* multibyte case */
+			chlen = CHAR_WIDTH(sp, ch);
 			last = scno;
 			scno += chlen;
-			TAB_RESET;
+			p++;
+
+			/*
+			 * If multi-width char crosses the end-of-screen,
+			 * put it on the next line.
+			 */
+			curoff += chlen;
+			if (!leftright && curoff >= sp->cols) {
+				if (curoff == sp->cols)
+					curoff = 0;
+				else {
+					scno -= scno % sp->cols;
+					scno += chlen;
+					curoff = chlen;
+				}
+			}
+
 			if (cno == 0)
 				break;
 		}
+	}
 
 	/* Add the trailing '$' if the O_LIST option set. */
 	if (listset && cnop == NULL)
@@ -249,8 +311,45 @@ vs_colpos(SCR *sp, db_recno_t lno, size_
 	off = cno / sp->cols;
 	cno %= sp->cols;
 	for (scno = 0, p = lp, len = llen; off--;) {
-		for (; len && scno < sp->cols; --len)
-			scno += CHLEN(scno);
+		while (len && scno < sp->cols) {
+			ch = (UCHAR_T)*p;
+			if (ch == '\t' && !listset) {
+				scno += TAB_OFF(scno);
+				len--;
+				p++;
+				continue;
+			}
+
+			chlen = KEY_COL(sp, ch);
+			if (!INTISWIDE(ch) || scno + chlen < sp->cols) {
+				/*
+				 * Singlebyte char can be displayed across
+				 * the end-of-screen.
+				 * If a multi-width char fits into this line,
+				 * put it here.
+				 */
+				scno += chlen;
+				len--;
+				p++;
+			} else if (leftright) {
+				/*
+				 * Side-scrolling screen is similar to
+				 * singlebyte case.
+				 */
+				scno += chlen;
+				len--;
+				p++;
+			} else {
+				/*
+				 * If multi-width char crosses the
+				 * end-of-screen, put it on the next line.
+				 *
+				 * We must adjust ch to the last char of the
+				 * line.
+				 */
+				scno = sp->cols;
+			}
+		}
 
 		/*
 		 * If reached the end of the physical line, return the last

Index: src/external/bsd/nvi/dist/vi/vs_refresh.c
diff -u src/external/bsd/nvi/dist/vi/vs_refresh.c:1.6 src/external/bsd/nvi/dist/vi/vs_refresh.c:1.7
--- src/external/bsd/nvi/dist/vi/vs_refresh.c:1.6	Sun Jan 26 21:43:45 2014
+++ src/external/bsd/nvi/dist/vi/vs_refresh.c	Fri Nov 10 14:44:13 2017
@@ -1,4 +1,4 @@
-/*	$NetBSD: vs_refresh.c,v 1.6 2014/01/26 21:43:45 christos Exp $ */
+/*	$NetBSD: vs_refresh.c,v 1.7 2017/11/10 14:44:13 rin Exp $ */
 /*-
  * Copyright (c) 1992, 1993, 1994
  *	The Regents of the University of California.  All rights reserved.
@@ -16,7 +16,7 @@
 static const char sccsid[] = "Id: vs_refresh.c,v 10.50 2001/06/25 15:19:37 skimo Exp  (Berkeley) Date: 2001/06/25 15:19:37 ";
 #endif /* not lint */
 #else
-__RCSID("$NetBSD: vs_refresh.c,v 1.6 2014/01/26 21:43:45 christos Exp $");
+__RCSID("$NetBSD: vs_refresh.c,v 1.7 2017/11/10 14:44:13 rin Exp $");
 #endif
 
 #include <sys/types.h>
@@ -148,7 +148,7 @@ vs_paint(SCR *sp, u_int flags)
 	SMAP *smp, tmp;
 	VI_PRIVATE *vip;
 	db_recno_t lastline, lcnt;
-	size_t cwtotal, cnt, len, notused, off, y;
+	size_t cwtotal, cnt, len, notused, off, y, chlen;
 	int ch = 0, didpaint, isempty, leftright_warp;
 	CHAR_T *p;
 
@@ -467,17 +467,33 @@ adjust:	if (!O_ISSET(sp, O_LEFTRIGHT) &&
 		/*
 		 * 7a: Cursor moved left.
 		 *
-		 * Point to the old character.  The old cursor position can
-		 * be past EOL if, for example, we just deleted the rest of
-		 * the line.  In this case, since we don't know the width of
-		 * the characters we traversed, we have to do it slowly.
+		 * The old cursor position can be past EOL if, for example,
+		 * we just deleted the rest of the line.  In this case, since
+		 * we don't know the width of the characters we traversed, we
+		 * have to do it slowly.
 		 */
-		p += OCNO;
-		cnt = (OCNO - CNO) + 1;
 		if (OCNO >= len)
 			goto slow;
 
 		/*
+		 * cwtotal acts as new value for SCNO.  Set cwtotal to the
+		 * first char for content on CNO byte, for ease handling of
+		 * wide characters.
+		 *
+		 * If the character we're stepping on lies across a screen
+		 * boundary, we have no hope to speed it up.  Do it slowly.
+		 */
+		p += OCNO;
+		if (INTISWIDE(ch = (UCHAR_T)*p))
+			cwtotal = SCNO;
+		else {
+			if (ch == '\t' || (chlen = KEY_LEN(sp, ch)) > SCNO + 1)
+				goto slow;
+			cwtotal = SCNO + 1 - chlen;
+		}
+		cnt = OCNO - CNO;
+
+		/*
 		 * Quick sanity check -- it's hard to figure out exactly when
 		 * we cross a screen boundary as we do in the cursor right
 		 * movement.  If cnt is so large that we're going to cross the
@@ -488,63 +504,87 @@ adjust:	if (!O_ISSET(sp, O_LEFTRIGHT) &&
 
 		/*
 		 * Count up the widths of the characters.  If it's a tab
-		 * character, go do it the the slow way.
+		 * character, go do it the slow way.
 		 */
-		for (cwtotal = 0; cnt--; cwtotal += KEY_COL(sp, ch))
-			if ((ch = *(UCHAR_T *)p--) == '\t')
+		while (cnt--) {
+			if ((ch = (UCHAR_T)*--p) == '\t'
+			    || (chlen = KEY_COL(sp, ch)) > cwtotal)
 				goto slow;
+			cwtotal -= chlen;
+		}
 
 		/*
-		 * Decrement the screen cursor by the total width of the
-		 * characters minus 1.
-		 */
-		cwtotal -= 1;
-
-		/*
-		 * If we're moving left, and there's a wide character in the
+		 * If we're moving left, and there's a multi-width char in the
 		 * current position, go to the end of the character.
 		 */
-		if (KEY_COL(sp, ch) > 1)
-			cwtotal -= KEY_COL(sp, ch) - 1;
+		if (!INTISWIDE(ch) && (chlen = KEY_LEN(sp, ch)) > 1)
+			cwtotal += chlen - 1;
 
 		/*
-		 * If the new column moved us off of the current logical line,
-		 * calculate a new one.  If doing leftright scrolling, we've
-		 * moved off of the current screen, as well.
+		 * At last, update the screen cursor.
 		 */
-		if (SCNO < cwtotal)
-			goto slow;
-		SCNO -= cwtotal;
+		SCNO = cwtotal;
 	} else {
 		/*
 		 * 7b: Cursor moved right.
-		 *
-		 * Point to the first character to the right.
 		 */
-		p += OCNO + 1;
+		if (OCNO >= len)
+			goto slow;
+
+		/*
+		 * cwtotal acts as new value for SCNO.  Set cwtotal to the
+		 * first char for content on CNO byte, for ease handling
+		 * of wide characters.
+		 */
+		p += OCNO;
+		if (INTISWIDE(ch = (UCHAR_T)*p))
+			cwtotal = SCNO;
+		else
+			cwtotal = SCNO + 1 - KEY_LEN(sp, ch);
 		cnt = CNO - OCNO;
 
 		/*
 		 * Count up the widths of the characters.  If it's a tab
-		 * character, go do it the the slow way.  If we cross a
-		 * screen boundary, we can quit.
+		 * character, go do it the the slow way.
+		 *
+		 * If a multi-width char seems to occupy the screen boundary,
+		 * that will be pushed to the next line.  Adjust the cursor
+		 * in that case.
+		 *
+		 * If we cross a screen boundary, we can quit.
 		 */
-		for (cwtotal = SCNO; cnt--;) {
-			if ((ch = *(UCHAR_T *)p++) == '\t')
+		while (cnt) {
+			if (ch == '\t')
 				goto slow;
-			if ((cwtotal += KEY_COL(sp, ch)) >= SCREEN_COLS(sp))
+			cwtotal += KEY_COL(sp, ch);
+			cnt--;
+			if (INTISWIDE(ch = (UCHAR_T)*++p)
+			    && (chlen = CHAR_WIDTH(sp, ch)) > 1
+			    && cwtotal + chlen >= SCREEN_COLS(sp))
+				cwtotal = SCREEN_COLS(sp);
+			if (cwtotal >= SCREEN_COLS(sp))
 				break;
 		}
 
 		/*
-		 * Increment the screen cursor by the total width of the
-		 * characters.
+		 * If we are on the tab character, we must do it slowly.
+		 *
+		 * If we're on a multi-width character in the current position,
+		 * go to the end of the character.
 		 */
-		SCNO = cwtotal;
+		if (ch == '\t')
+			goto slow;
+		if (!INTISWIDE(ch) && (chlen = KEY_LEN(sp, ch)) > 1)
+			cwtotal += chlen - 1;
 
 		/* See screen change comment in section 6a. */
-		if (SCNO >= SCREEN_COLS(sp))
+		if (cwtotal >= SCREEN_COLS(sp))
 			goto slow;
+
+		/*
+		 * At last, update the screen cursor.
+		 */
+		SCNO = cwtotal;
 	}
 
 	/*
@@ -678,6 +718,8 @@ done_cursor:
 	}
 #else
 	if (vip->sc_smap == NULL) {
+		if (F_ISSET(sp, SC_SCR_REFORMAT))
+			abort(); /* XXX */
 		F_SET(sp, SC_SCR_REFORMAT);
 		return (vs_paint(sp, flags));
 	}

Reply via email to