NetBSD-Bugs archive

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]

lib/51986: libedit: strange behavior caused by a combination of scrolling and line wrapping



>Number:         51986
>Category:       lib
>Synopsis:       libedit: strange behavior caused by a combination of scrolling and line wrapping
>Confidential:   no
>Severity:       serious
>Priority:       medium
>Responsible:    lib-bug-people
>State:          open
>Class:          sw-bug
>Submitter-Id:   net
>Arrival-Date:   Sun Feb 19 18:50:00 +0000 2017
>Originator:     Satoshi Kanemitsu
>Release:        libedit (NetBSD-current)
>Organization:
>Environment:
NetBSD localhost 7.0.2 NetBSD 7.0.2 (GENERIC.201610210724Z) amd64
>Description:
libedit(/bin/sh) crashes or exhibits strange behavior when using the virtical scrolling and line wrapping at the same time.
It seems that the problem is related to the following factors.

 1. Array index is out of bounds in some cases (It will cause a segmentation fault).
 2. The screen does not refresh if the cursor goes off-screen.
 3. libedit can not get the right cursor position when the prompt disappears.

>How-To-Repeat:
1. Typing a long line (more than terminal size).
2. Press enter.
3. Press arrow-up (the prompt disappears).
4. Press arrow-left repeatedly (the cursor moves to the wrong position).
5. Segmentation fault.

>Fix:
I propose a following patch.

--- libedit/refresh.c.orig	2016-05-10 12:01:03.000000000 +0900
+++ libedit/refresh.c	2017-02-12 12:48:25.000000000 +0900
@@ -61,2 +61,3 @@ static void	re__strncopy(wchar_t *, wcha
 static void	re__copy_and_pad(wchar_t *, const wchar_t *, size_t);
+static void	re_get_nextposition(EditLine *, wint_t, coord_t *);
 
@@ -113,2 +114,4 @@ re_nextline(EditLine *el)
 		el->el_vdisplay[i - 1] = firstline;
+
+		el->el_prompt.p_pos.v--;
 	} else
@@ -266,2 +269,18 @@ re_refresh(EditLine *el)
 		}
+		if (el->el_refresh.r_cursor.v + 1 >= el->el_terminal.t_size.v &&
+		    cur.h >= 0) {
+			coord_t pos;
+
+			pos.h = el->el_refresh.r_cursor.h;
+			pos.v = el->el_refresh.r_cursor.v;
+			re_get_nextposition(el, *cp, &pos);
+			if (pos.v > el->el_refresh.r_cursor.v) {
+				cur.v--;
+				/* keep the cursor on the screen */
+				if (cur.v < 0) {
+					cur.v = 0;
+					break;
+				}
+			}
+		}
 		re_addc(el, *cp);
@@ -998,3 +1017,4 @@ re_refresh_cursor(EditLine *el)
 	wchar_t *cp;
-	int h, v, th, w;
+	coord_t cur;
+	int w;
 
@@ -1009,32 +1029,9 @@ re_refresh_cursor(EditLine *el)
 	/* first we must find where the cursor is... */
-	h = el->el_prompt.p_pos.h;
-	v = el->el_prompt.p_pos.v;
-	th = el->el_terminal.t_size.h;	/* optimize for speed */
+	cur.h = el->el_prompt.p_pos.h;
+	cur.v = el->el_prompt.p_pos.v;	/* negative value means "off-screen" */
 
 	/* do input buffer to el->el_line.cursor */
-	for (cp = el->el_line.buffer; cp < el->el_line.cursor; cp++) {
-                switch (ct_chr_class(*cp)) {
-		case CHTYPE_NL:  /* handle newline in data part too */
-			h = 0;
-			v++;
-			break;
-		case CHTYPE_TAB: /* if a tab, to next tab stop */
-			while (++h & 07)
-				continue;
-			break;
-		default:
-			w = wcwidth(*cp);
-			if (w > 1 && h + w > th) { /* won't fit on line */
-				h = 0;
-				v++;
-			}
-			h += ct_visual_width(*cp);
-			break;
-                }
+	for (cp = el->el_line.buffer; cp < el->el_line.cursor; cp++)
+		re_get_nextposition(el, *cp, &cur);
 
-		if (h >= th) {	/* check, extra long tabs picked up here also */
-			h -= th;
-			v++;
-		}
-	}
         /* if we have a next character, and it's a doublewidth one, we need to
@@ -1042,10 +1039,15 @@ re_refresh_cursor(EditLine *el)
         if (cp < el->el_line.lastchar && (w = wcwidth(*cp)) > 1)
-                if (h + w > th) {
-                    h = 0;
-                    v++;
+                if (cur.h + w > el->el_terminal.t_size.h) {
+                    cur.h = 0;
+                    cur.v++;
                 }
 
+	if (cur.v < 0 || cur.v >= el->el_terminal.t_size.v) {
+		re_refresh(el);
+		return;
+	}
+
 	/* now go there */
-	terminal_move_to_line(el, v);
-	terminal_move_to_char(el, h);
+	terminal_move_to_line(el, cur.v);
+	terminal_move_to_char(el, cur.h);
 	terminal__flush(el);
@@ -1089,2 +1091,4 @@ re_fastputc(EditLine *el, wint_t c)
 			el->el_display[i - 1] = firstline;
+
+			el->el_prompt.p_pos.v--;
 		} else {
@@ -1187 +1191,34 @@ re_clear_lines(EditLine *el)
 }
+
+
+/* re_get_nextposition():
+ *	Get the next position
+ */
+static void re_get_nextposition(EditLine * el, wint_t c, coord_t *pos)
+{
+	int th, w;
+
+	th = el->el_terminal.t_size.h;
+	switch (ct_chr_class(c)) {
+	case CHTYPE_TAB:	/* if a tab, to next tab stop */
+		while (++pos->h & 07)
+			continue;
+		break;
+	case CHTYPE_NL:
+		pos->h = 0;
+		pos->v++;
+		break;
+	default:
+		w = wcwidth(c);
+		if (w > 1 && pos->h + w > th) { /* won't fit on line */
+			pos->h = 0;
+			pos->v++;
+		}
+		pos->h += ct_visual_width(c);
+		break;
+	}
+	if (pos->h >= th) {	/* check, extra long tabs picked up here also */
+		pos->h -= th;
+		pos->v++;
+	}
+}
--- libedit/terminal.c.orig	2016-05-10 12:01:03.000000000 +0900
+++ libedit/terminal.c	2017-02-08 15:20:41.000000000 +0900
@@ -506,3 +506,3 @@ terminal_move_to_line(EditLine *el, int 
 
-	if (where > el->el_terminal.t_size.v) {
+	if (where >= el->el_terminal.t_size.v) {
 #ifdef DEBUG_SCREEN
@@ -570,3 +570,3 @@ mc_again:
 
-	if (where > el->el_terminal.t_size.h) {
+	if (where >= el->el_terminal.t_size.h) {
 #ifdef DEBUG_SCREEN
@@ -679,3 +679,4 @@ terminal_overwrite(EditLine *el, const w
 			el->el_cursor.h = 0;
-			el->el_cursor.v++;
+			if (el->el_cursor.v + 1 < el->el_terminal.t_size.v)
+				el->el_cursor.v++;
 			if (EL_HAS_MAGIC_MARGINS) {



Home | Main Index | Thread Index | Old Index