Home | History | Annotate | Line # | Download | only in libedit
refresh.c revision 1.52
      1 /*	$NetBSD: refresh.c,v 1.52 2017/06/27 23:23:48 christos Exp $	*/
      2 
      3 /*-
      4  * Copyright (c) 1992, 1993
      5  *	The Regents of the University of California.  All rights reserved.
      6  *
      7  * This code is derived from software contributed to Berkeley by
      8  * Christos Zoulas of Cornell University.
      9  *
     10  * Redistribution and use in source and binary forms, with or without
     11  * modification, are permitted provided that the following conditions
     12  * are met:
     13  * 1. Redistributions of source code must retain the above copyright
     14  *    notice, this list of conditions and the following disclaimer.
     15  * 2. Redistributions in binary form must reproduce the above copyright
     16  *    notice, this list of conditions and the following disclaimer in the
     17  *    documentation and/or other materials provided with the distribution.
     18  * 3. Neither the name of the University nor the names of its contributors
     19  *    may be used to endorse or promote products derived from this software
     20  *    without specific prior written permission.
     21  *
     22  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
     23  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
     24  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
     25  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
     26  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
     27  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
     28  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
     29  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
     30  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
     31  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
     32  * SUCH DAMAGE.
     33  */
     34 
     35 #include "config.h"
     36 #if !defined(lint) && !defined(SCCSID)
     37 #if 0
     38 static char sccsid[] = "@(#)refresh.c	8.1 (Berkeley) 6/4/93";
     39 #else
     40 __RCSID("$NetBSD: refresh.c,v 1.52 2017/06/27 23:23:48 christos Exp $");
     41 #endif
     42 #endif /* not lint && not SCCSID */
     43 
     44 /*
     45  * refresh.c: Lower level screen refreshing functions
     46  */
     47 #include <stdio.h>
     48 #include <string.h>
     49 #include <unistd.h>
     50 
     51 #include "el.h"
     52 
     53 static void	re_nextline(EditLine *);
     54 static void	re_addc(EditLine *, wint_t);
     55 static void	re_update_line(EditLine *, wchar_t *, wchar_t *, int);
     56 static void	re_insert (EditLine *, wchar_t *, int, int, wchar_t *, int);
     57 static void	re_delete(EditLine *, wchar_t *, int, int, int);
     58 static void	re_fastputc(EditLine *, wint_t);
     59 static void	re_clear_eol(EditLine *, int, int, int);
     60 static void	re__strncopy(wchar_t *, wchar_t *, size_t);
     61 static void	re__copy_and_pad(wchar_t *, const wchar_t *, size_t);
     62 
     63 #ifdef DEBUG_REFRESH
     64 static void	re_printstr(EditLine *, const char *, wchar_t *, wchar_t *);
     65 #define	__F el->el_errfile
     66 #define	ELRE_ASSERT(a, b, c)	do				\
     67 				    if (/*CONSTCOND*/ a) {	\
     68 					(void) fprintf b;	\
     69 					c;			\
     70 				    }				\
     71 				while (/*CONSTCOND*/0)
     72 #define	ELRE_DEBUG(a, b)	ELRE_ASSERT(a,b,;)
     73 
     74 /* re_printstr():
     75  *	Print a string on the debugging pty
     76  */
     77 static void
     78 re_printstr(EditLine *el, const char *str, wchar_t *f, wchar_t *t)
     79 {
     80 
     81 	ELRE_DEBUG(1, (__F, "%s:\"", str));
     82 	while (f < t)
     83 		ELRE_DEBUG(1, (__F, "%c", *f++ & 0177));
     84 	ELRE_DEBUG(1, (__F, "\"\r\n"));
     85 }
     86 #else
     87 #define	ELRE_ASSERT(a, b, c)
     88 #define	ELRE_DEBUG(a, b)
     89 #endif
     90 
     91 /* re_nextline():
     92  *	Move to the next line or scroll
     93  */
     94 static void
     95 re_nextline(EditLine *el)
     96 {
     97 	el->el_refresh.r_cursor.h = 0;	/* reset it. */
     98 
     99 	/*
    100 	 * If we would overflow (input is longer than terminal size),
    101 	 * emulate scroll by dropping first line and shuffling the rest.
    102 	 * We do this via pointer shuffling - it's safe in this case
    103 	 * and we avoid memcpy().
    104 	 */
    105 	if (el->el_refresh.r_cursor.v + 1 >= el->el_terminal.t_size.v) {
    106 		int i, lins = el->el_terminal.t_size.v;
    107 		wchar_t *firstline = el->el_vdisplay[0];
    108 
    109 		for(i = 1; i < lins; i++)
    110 			el->el_vdisplay[i - 1] = el->el_vdisplay[i];
    111 
    112 		firstline[0] = '\0';		/* empty the string */
    113 		el->el_vdisplay[i - 1] = firstline;
    114 	} else
    115 		el->el_refresh.r_cursor.v++;
    116 
    117 	ELRE_ASSERT(el->el_refresh.r_cursor.v >= el->el_terminal.t_size.v,
    118 	    (__F, "\r\nre_putc: overflow! r_cursor.v == %d > %d\r\n",
    119 	    el->el_refresh.r_cursor.v, el->el_terminal.t_size.v),
    120 	    abort());
    121 }
    122 
    123 /* re_addc():
    124  *	Draw c, expanding tabs, control chars etc.
    125  */
    126 static void
    127 re_addc(EditLine *el, wint_t c)
    128 {
    129 	switch (ct_chr_class(c)) {
    130 	case CHTYPE_TAB:        /* expand the tab */
    131 		for (;;) {
    132 			re_putc(el, ' ', 1);
    133 			if ((el->el_refresh.r_cursor.h & 07) == 0)
    134 				break;			/* go until tab stop */
    135 		}
    136 		break;
    137 	case CHTYPE_NL: {
    138 		int oldv = el->el_refresh.r_cursor.v;
    139 		re_putc(el, '\0', 0);			/* assure end of line */
    140 		if (oldv == el->el_refresh.r_cursor.v)	/* XXX */
    141 			re_nextline(el);
    142 		break;
    143 	}
    144 	case CHTYPE_PRINT:
    145 		re_putc(el, c, 1);
    146 		break;
    147 	default: {
    148 		wchar_t visbuf[VISUAL_WIDTH_MAX];
    149 		ssize_t i, n =
    150 		    ct_visual_char(visbuf, VISUAL_WIDTH_MAX, c);
    151 		for (i = 0; n-- > 0; ++i)
    152 		    re_putc(el, visbuf[i], 1);
    153 		break;
    154 	}
    155 	}
    156 }
    157 
    158 /* re_putc():
    159  *	Place the literal string given
    160  */
    161 libedit_private void
    162 re_putliteral(EditLine *el, const wchar_t *begin, const wchar_t *end)
    163 {
    164 	coord_t *cur = &el->el_refresh.r_cursor;
    165 	wint_t c;
    166 	int sizeh = el->el_terminal.t_size.h;
    167 
    168 	c = literal_add(el, begin, end);
    169 	if (c == 0)
    170 		return;
    171 	el->el_vdisplay[cur->v][cur->h] = c;
    172 	cur->h += 1;	/* XXX: only for narrow */
    173 	if (cur->h >= sizeh) {
    174 		/* assure end of line */
    175 		el->el_vdisplay[cur->v][sizeh] = '\0';
    176 		re_nextline(el);
    177 	}
    178 }
    179 
    180 /* re_putc():
    181  *	Draw the character given
    182  */
    183 libedit_private void
    184 re_putc(EditLine *el, wint_t c, int shift)
    185 {
    186 	coord_t *cur = &el->el_refresh.r_cursor;
    187 	int i, w = wcwidth(c);
    188 	int sizeh = el->el_terminal.t_size.h;
    189 
    190 	ELRE_DEBUG(1, (__F, "printing %5x '%lc'\r\n", c, c));
    191 	if (w == -1)
    192 		w = 0;
    193 
    194 	while (shift && (cur->h + w > sizeh))
    195 	    re_putc(el, ' ', 1);
    196 
    197 	el->el_vdisplay[cur->v][cur->h] = c;
    198 	/* assumes !shift is only used for single-column chars */
    199 	i = w;
    200 	while (--i > 0)
    201 		el->el_vdisplay[cur->v][cur->h + i] = MB_FILL_CHAR;
    202 
    203 	if (!shift)
    204 		return;
    205 
    206 	cur->h += w;	/* advance to next place */
    207 	if (cur->h >= sizeh) {
    208 		/* assure end of line */
    209 		el->el_vdisplay[cur->v][sizeh] = '\0';
    210 		re_nextline(el);
    211 	}
    212 }
    213 
    214 
    215 /* re_refresh():
    216  *	draws the new virtual screen image from the current input
    217  *	line, then goes line-by-line changing the real image to the new
    218  *	virtual image. The routine to re-draw a line can be replaced
    219  *	easily in hopes of a smarter one being placed there.
    220  */
    221 libedit_private void
    222 re_refresh(EditLine *el)
    223 {
    224 	int i, rhdiff;
    225 	wchar_t *cp, *st;
    226 	coord_t cur;
    227 #ifdef notyet
    228 	size_t termsz;
    229 #endif
    230 
    231 	ELRE_DEBUG(1, (__F, "el->el_line.buffer = :%ls:\r\n",
    232 	    el->el_line.buffer));
    233 
    234 	literal_clear(el);
    235 	/* reset the Drawing cursor */
    236 	el->el_refresh.r_cursor.h = 0;
    237 	el->el_refresh.r_cursor.v = 0;
    238 
    239 	terminal_move_to_char(el, 0);
    240 
    241 	/* temporarily draw rprompt to calculate its size */
    242 	prompt_print(el, EL_RPROMPT);
    243 
    244 	/* reset the Drawing cursor */
    245 	el->el_refresh.r_cursor.h = 0;
    246 	el->el_refresh.r_cursor.v = 0;
    247 
    248 	if (el->el_line.cursor >= el->el_line.lastchar) {
    249 		if (el->el_map.current == el->el_map.alt
    250 		    && el->el_line.lastchar != el->el_line.buffer)
    251 			el->el_line.cursor = el->el_line.lastchar - 1;
    252 		else
    253 			el->el_line.cursor = el->el_line.lastchar;
    254 	}
    255 
    256 	cur.h = -1;		/* set flag in case I'm not set */
    257 	cur.v = 0;
    258 
    259 	prompt_print(el, EL_PROMPT);
    260 
    261 	/* draw the current input buffer */
    262 #if notyet
    263 	termsz = el->el_terminal.t_size.h * el->el_terminal.t_size.v;
    264 	if (el->el_line.lastchar - el->el_line.buffer > termsz) {
    265 		/*
    266 		 * If line is longer than terminal, process only part
    267 		 * of line which would influence display.
    268 		 */
    269 		size_t rem = (el->el_line.lastchar-el->el_line.buffer)%termsz;
    270 
    271 		st = el->el_line.lastchar - rem
    272 			- (termsz - (((rem / el->el_terminal.t_size.v) - 1)
    273 					* el->el_terminal.t_size.v));
    274 	} else
    275 #endif
    276 		st = el->el_line.buffer;
    277 
    278 	for (cp = st; cp < el->el_line.lastchar; cp++) {
    279 		if (cp == el->el_line.cursor) {
    280                         int w = wcwidth(*cp);
    281 			/* save for later */
    282 			cur.h = el->el_refresh.r_cursor.h;
    283 			cur.v = el->el_refresh.r_cursor.v;
    284                         /* handle being at a linebroken doublewidth char */
    285                         if (w > 1 && el->el_refresh.r_cursor.h + w >
    286 			    el->el_terminal.t_size.h) {
    287 				cur.h = 0;
    288 				cur.v++;
    289                         }
    290 		}
    291 		re_addc(el, *cp);
    292 	}
    293 
    294 	if (cur.h == -1) {	/* if I haven't been set yet, I'm at the end */
    295 		cur.h = el->el_refresh.r_cursor.h;
    296 		cur.v = el->el_refresh.r_cursor.v;
    297 	}
    298 	rhdiff = el->el_terminal.t_size.h - el->el_refresh.r_cursor.h -
    299 	    el->el_rprompt.p_pos.h;
    300 	if (el->el_rprompt.p_pos.h && !el->el_rprompt.p_pos.v &&
    301 	    !el->el_refresh.r_cursor.v && rhdiff > 1) {
    302 		/*
    303 		 * have a right-hand side prompt that will fit
    304 		 * on the end of the first line with at least
    305 		 * one character gap to the input buffer.
    306 		 */
    307 		while (--rhdiff > 0)	/* pad out with spaces */
    308 			re_putc(el, ' ', 1);
    309 		prompt_print(el, EL_RPROMPT);
    310 	} else {
    311 		el->el_rprompt.p_pos.h = 0;	/* flag "not using rprompt" */
    312 		el->el_rprompt.p_pos.v = 0;
    313 	}
    314 
    315 	re_putc(el, '\0', 0);	/* make line ended with NUL, no cursor shift */
    316 
    317 	el->el_refresh.r_newcv = el->el_refresh.r_cursor.v;
    318 
    319 	ELRE_DEBUG(1, (__F,
    320 		"term.h=%d vcur.h=%d vcur.v=%d vdisplay[0]=\r\n:%80.80s:\r\n",
    321 		el->el_terminal.t_size.h, el->el_refresh.r_cursor.h,
    322 		el->el_refresh.r_cursor.v, ct_encode_string(el->el_vdisplay[0],
    323 		&el->el_scratch)));
    324 
    325 	ELRE_DEBUG(1, (__F, "updating %d lines.\r\n", el->el_refresh.r_newcv));
    326 	for (i = 0; i <= el->el_refresh.r_newcv; i++) {
    327 		/* NOTE THAT re_update_line MAY CHANGE el_display[i] */
    328 		re_update_line(el, el->el_display[i], el->el_vdisplay[i], i);
    329 
    330 		/*
    331 		 * Copy the new line to be the current one, and pad out with
    332 		 * spaces to the full width of the terminal so that if we try
    333 		 * moving the cursor by writing the character that is at the
    334 		 * end of the screen line, it won't be a NUL or some old
    335 		 * leftover stuff.
    336 		 */
    337 		re__copy_and_pad(el->el_display[i], el->el_vdisplay[i],
    338 		    (size_t) el->el_terminal.t_size.h);
    339 	}
    340 	ELRE_DEBUG(1, (__F,
    341 	"\r\nel->el_refresh.r_cursor.v=%d,el->el_refresh.r_oldcv=%d i=%d\r\n",
    342 	    el->el_refresh.r_cursor.v, el->el_refresh.r_oldcv, i));
    343 
    344 	if (el->el_refresh.r_oldcv > el->el_refresh.r_newcv)
    345 		for (; i <= el->el_refresh.r_oldcv; i++) {
    346 			terminal_move_to_line(el, i);
    347 			terminal_move_to_char(el, 0);
    348                         /* This wcslen should be safe even with MB_FILL_CHARs */
    349 			terminal_clear_EOL(el, (int) wcslen(el->el_display[i]));
    350 #ifdef DEBUG_REFRESH
    351 			terminal_overwrite(el, L"C\b", 2);
    352 #endif /* DEBUG_REFRESH */
    353 			el->el_display[i][0] = '\0';
    354 		}
    355 
    356 	el->el_refresh.r_oldcv = el->el_refresh.r_newcv; /* set for next time */
    357 	ELRE_DEBUG(1, (__F,
    358 	    "\r\ncursor.h = %d, cursor.v = %d, cur.h = %d, cur.v = %d\r\n",
    359 	    el->el_refresh.r_cursor.h, el->el_refresh.r_cursor.v,
    360 	    cur.h, cur.v));
    361 	terminal_move_to_line(el, cur.v);	/* go to where the cursor is */
    362 	terminal_move_to_char(el, cur.h);
    363 }
    364 
    365 
    366 /* re_goto_bottom():
    367  *	 used to go to last used screen line
    368  */
    369 libedit_private void
    370 re_goto_bottom(EditLine *el)
    371 {
    372 
    373 	terminal_move_to_line(el, el->el_refresh.r_oldcv);
    374 	terminal__putc(el, '\n');
    375 	re_clear_display(el);
    376 	terminal__flush(el);
    377 }
    378 
    379 
    380 /* re_insert():
    381  *	insert num characters of s into d (in front of the character)
    382  *	at dat, maximum length of d is dlen
    383  */
    384 static void
    385 /*ARGSUSED*/
    386 re_insert(EditLine *el __attribute__((__unused__)),
    387     wchar_t *d, int dat, int dlen, wchar_t *s, int num)
    388 {
    389 	wchar_t *a, *b;
    390 
    391 	if (num <= 0)
    392 		return;
    393 	if (num > dlen - dat)
    394 		num = dlen - dat;
    395 
    396 	ELRE_DEBUG(1,
    397 	    (__F, "re_insert() starting: %d at %d max %d, d == \"%s\"\n",
    398 	    num, dat, dlen, ct_encode_string(d, &el->el_scratch)));
    399 	ELRE_DEBUG(1, (__F, "s == \"%s\"\n", ct_encode_string(s,
    400 	    &el->el_scratch)));
    401 
    402 	/* open up the space for num chars */
    403 	if (num > 0) {
    404 		b = d + dlen - 1;
    405 		a = b - num;
    406 		while (a >= &d[dat])
    407 			*b-- = *a--;
    408 		d[dlen] = '\0';	/* just in case */
    409 	}
    410 
    411 	ELRE_DEBUG(1, (__F,
    412 		"re_insert() after insert: %d at %d max %d, d == \"%s\"\n",
    413 		num, dat, dlen, ct_encode_string(d, &el->el_scratch)));
    414 	ELRE_DEBUG(1, (__F, "s == \"%s\"\n", ct_encode_string(s,
    415 		&el->el_scratch)));
    416 
    417 	/* copy the characters */
    418 	for (a = d + dat; (a < d + dlen) && (num > 0); num--)
    419 		*a++ = *s++;
    420 
    421 #ifdef notyet
    422         /* ct_encode_string() uses a static buffer, so we can't conveniently
    423          * encode both d & s here */
    424 	ELRE_DEBUG(1,
    425 	    (__F, "re_insert() after copy: %d at %d max %d, %s == \"%s\"\n",
    426 	    num, dat, dlen, d, s));
    427 	ELRE_DEBUG(1, (__F, "s == \"%s\"\n", s));
    428 #endif
    429 }
    430 
    431 
    432 /* re_delete():
    433  *	delete num characters d at dat, maximum length of d is dlen
    434  */
    435 static void
    436 /*ARGSUSED*/
    437 re_delete(EditLine *el __attribute__((__unused__)),
    438     wchar_t *d, int dat, int dlen, int num)
    439 {
    440 	wchar_t *a, *b;
    441 
    442 	if (num <= 0)
    443 		return;
    444 	if (dat + num >= dlen) {
    445 		d[dat] = '\0';
    446 		return;
    447 	}
    448 	ELRE_DEBUG(1,
    449 	    (__F, "re_delete() starting: %d at %d max %d, d == \"%s\"\n",
    450 	    num, dat, dlen, ct_encode_string(d, &el->el_scratch)));
    451 
    452 	/* open up the space for num chars */
    453 	if (num > 0) {
    454 		b = d + dat;
    455 		a = b + num;
    456 		while (a < &d[dlen])
    457 			*b++ = *a++;
    458 		d[dlen] = '\0';	/* just in case */
    459 	}
    460 	ELRE_DEBUG(1,
    461 	    (__F, "re_delete() after delete: %d at %d max %d, d == \"%s\"\n",
    462 	    num, dat, dlen, ct_encode_string(d, &el->el_scratch)));
    463 }
    464 
    465 
    466 /* re__strncopy():
    467  *	Like strncpy without padding.
    468  */
    469 static void
    470 re__strncopy(wchar_t *a, wchar_t *b, size_t n)
    471 {
    472 
    473 	while (n-- && *b)
    474 		*a++ = *b++;
    475 }
    476 
    477 /* re_clear_eol():
    478  *	Find the number of characters we need to clear till the end of line
    479  *	in order to make sure that we have cleared the previous contents of
    480  *	the line. fx and sx is the number of characters inserted or deleted
    481  *	in the first or second diff, diff is the difference between the
    482  *	number of characters between the new and old line.
    483  */
    484 static void
    485 re_clear_eol(EditLine *el, int fx, int sx, int diff)
    486 {
    487 
    488 	ELRE_DEBUG(1, (__F, "re_clear_eol sx %d, fx %d, diff %d\n",
    489 	    sx, fx, diff));
    490 
    491 	if (fx < 0)
    492 		fx = -fx;
    493 	if (sx < 0)
    494 		sx = -sx;
    495 	if (fx > diff)
    496 		diff = fx;
    497 	if (sx > diff)
    498 		diff = sx;
    499 
    500 	ELRE_DEBUG(1, (__F, "re_clear_eol %d\n", diff));
    501 	terminal_clear_EOL(el, diff);
    502 }
    503 
    504 /*****************************************************************
    505     re_update_line() is based on finding the middle difference of each line
    506     on the screen; vis:
    507 
    508 			     /old first difference
    509 	/beginning of line   |              /old last same       /old EOL
    510 	v		     v              v                    v
    511 old:	eddie> Oh, my little gruntle-buggy is to me, as lurgid as
    512 new:	eddie> Oh, my little buggy says to me, as lurgid as
    513 	^		     ^        ^			   ^
    514 	\beginning of line   |        \new last same	   \new end of line
    515 			     \new first difference
    516 
    517     all are character pointers for the sake of speed.  Special cases for
    518     no differences, as well as for end of line additions must be handled.
    519 **************************************************************** */
    520 
    521 /* Minimum at which doing an insert it "worth it".  This should be about
    522  * half the "cost" of going into insert mode, inserting a character, and
    523  * going back out.  This should really be calculated from the termcap
    524  * data...  For the moment, a good number for ANSI terminals.
    525  */
    526 #define	MIN_END_KEEP	4
    527 
    528 static void
    529 re_update_line(EditLine *el, wchar_t *old, wchar_t *new, int i)
    530 {
    531 	wchar_t *o, *n, *p, c;
    532 	wchar_t *ofd, *ols, *oe, *nfd, *nls, *ne;
    533 	wchar_t *osb, *ose, *nsb, *nse;
    534 	int fx, sx;
    535 	size_t len;
    536 
    537 	/*
    538          * find first diff
    539          */
    540 	for (o = old, n = new; *o && (*o == *n); o++, n++)
    541 		continue;
    542 	ofd = o;
    543 	nfd = n;
    544 
    545 	/*
    546          * Find the end of both old and new
    547          */
    548 	while (*o)
    549 		o++;
    550 	/*
    551          * Remove any trailing blanks off of the end, being careful not to
    552          * back up past the beginning.
    553          */
    554 	while (ofd < o) {
    555 		if (o[-1] != ' ')
    556 			break;
    557 		o--;
    558 	}
    559 	oe = o;
    560 	*oe = '\0';
    561 
    562 	while (*n)
    563 		n++;
    564 
    565 	/* remove blanks from end of new */
    566 	while (nfd < n) {
    567 		if (n[-1] != ' ')
    568 			break;
    569 		n--;
    570 	}
    571 	ne = n;
    572 	*ne = '\0';
    573 
    574 	/*
    575          * if no diff, continue to next line of redraw
    576          */
    577 	if (*ofd == '\0' && *nfd == '\0') {
    578 		ELRE_DEBUG(1, (__F, "no difference.\r\n"));
    579 		return;
    580 	}
    581 	/*
    582          * find last same pointer
    583          */
    584 	while ((o > ofd) && (n > nfd) && (*--o == *--n))
    585 		continue;
    586 	ols = ++o;
    587 	nls = ++n;
    588 
    589 	/*
    590          * find same beginning and same end
    591          */
    592 	osb = ols;
    593 	nsb = nls;
    594 	ose = ols;
    595 	nse = nls;
    596 
    597 	/*
    598          * case 1: insert: scan from nfd to nls looking for *ofd
    599          */
    600 	if (*ofd) {
    601 		for (c = *ofd, n = nfd; n < nls; n++) {
    602 			if (c == *n) {
    603 				for (o = ofd, p = n;
    604 				    p < nls && o < ols && *o == *p;
    605 				    o++, p++)
    606 					continue;
    607 				/*
    608 				 * if the new match is longer and it's worth
    609 				 * keeping, then we take it
    610 				 */
    611 				if (((nse - nsb) < (p - n)) &&
    612 				    (2 * (p - n) > n - nfd)) {
    613 					nsb = n;
    614 					nse = p;
    615 					osb = ofd;
    616 					ose = o;
    617 				}
    618 			}
    619 		}
    620 	}
    621 	/*
    622          * case 2: delete: scan from ofd to ols looking for *nfd
    623          */
    624 	if (*nfd) {
    625 		for (c = *nfd, o = ofd; o < ols; o++) {
    626 			if (c == *o) {
    627 				for (n = nfd, p = o;
    628 				    p < ols && n < nls && *p == *n;
    629 				    p++, n++)
    630 					continue;
    631 				/*
    632 				 * if the new match is longer and it's worth
    633 				 * keeping, then we take it
    634 				 */
    635 				if (((ose - osb) < (p - o)) &&
    636 				    (2 * (p - o) > o - ofd)) {
    637 					nsb = nfd;
    638 					nse = n;
    639 					osb = o;
    640 					ose = p;
    641 				}
    642 			}
    643 		}
    644 	}
    645 	/*
    646          * Pragmatics I: If old trailing whitespace or not enough characters to
    647          * save to be worth it, then don't save the last same info.
    648          */
    649 	if ((oe - ols) < MIN_END_KEEP) {
    650 		ols = oe;
    651 		nls = ne;
    652 	}
    653 	/*
    654          * Pragmatics II: if the terminal isn't smart enough, make the data
    655          * dumber so the smart update doesn't try anything fancy
    656          */
    657 
    658 	/*
    659          * fx is the number of characters we need to insert/delete: in the
    660          * beginning to bring the two same begins together
    661          */
    662 	fx = (int)((nsb - nfd) - (osb - ofd));
    663 	/*
    664          * sx is the number of characters we need to insert/delete: in the
    665          * end to bring the two same last parts together
    666          */
    667 	sx = (int)((nls - nse) - (ols - ose));
    668 
    669 	if (!EL_CAN_INSERT) {
    670 		if (fx > 0) {
    671 			osb = ols;
    672 			ose = ols;
    673 			nsb = nls;
    674 			nse = nls;
    675 		}
    676 		if (sx > 0) {
    677 			ols = oe;
    678 			nls = ne;
    679 		}
    680 		if ((ols - ofd) < (nls - nfd)) {
    681 			ols = oe;
    682 			nls = ne;
    683 		}
    684 	}
    685 	if (!EL_CAN_DELETE) {
    686 		if (fx < 0) {
    687 			osb = ols;
    688 			ose = ols;
    689 			nsb = nls;
    690 			nse = nls;
    691 		}
    692 		if (sx < 0) {
    693 			ols = oe;
    694 			nls = ne;
    695 		}
    696 		if ((ols - ofd) > (nls - nfd)) {
    697 			ols = oe;
    698 			nls = ne;
    699 		}
    700 	}
    701 	/*
    702          * Pragmatics III: make sure the middle shifted pointers are correct if
    703          * they don't point to anything (we may have moved ols or nls).
    704          */
    705 	/* if the change isn't worth it, don't bother */
    706 	/* was: if (osb == ose) */
    707 	if ((ose - osb) < MIN_END_KEEP) {
    708 		osb = ols;
    709 		ose = ols;
    710 		nsb = nls;
    711 		nse = nls;
    712 	}
    713 	/*
    714          * Now that we are done with pragmatics we recompute fx, sx
    715          */
    716 	fx = (int)((nsb - nfd) - (osb - ofd));
    717 	sx = (int)((nls - nse) - (ols - ose));
    718 
    719 	ELRE_DEBUG(1, (__F, "fx %d, sx %d\n", fx, sx));
    720 	ELRE_DEBUG(1, (__F, "ofd %td, osb %td, ose %td, ols %td, oe %td\n",
    721 		ofd - old, osb - old, ose - old, ols - old, oe - old));
    722 	ELRE_DEBUG(1, (__F, "nfd %td, nsb %td, nse %td, nls %td, ne %td\n",
    723 		nfd - new, nsb - new, nse - new, nls - new, ne - new));
    724 	ELRE_DEBUG(1, (__F,
    725 		"xxx-xxx:\"00000000001111111111222222222233333333334\"\r\n"));
    726 	ELRE_DEBUG(1, (__F,
    727 		"xxx-xxx:\"01234567890123456789012345678901234567890\"\r\n"));
    728 #ifdef DEBUG_REFRESH
    729 	re_printstr(el, "old- oe", old, oe);
    730 	re_printstr(el, "new- ne", new, ne);
    731 	re_printstr(el, "old-ofd", old, ofd);
    732 	re_printstr(el, "new-nfd", new, nfd);
    733 	re_printstr(el, "ofd-osb", ofd, osb);
    734 	re_printstr(el, "nfd-nsb", nfd, nsb);
    735 	re_printstr(el, "osb-ose", osb, ose);
    736 	re_printstr(el, "nsb-nse", nsb, nse);
    737 	re_printstr(el, "ose-ols", ose, ols);
    738 	re_printstr(el, "nse-nls", nse, nls);
    739 	re_printstr(el, "ols- oe", ols, oe);
    740 	re_printstr(el, "nls- ne", nls, ne);
    741 #endif /* DEBUG_REFRESH */
    742 
    743 	/*
    744          * el_cursor.v to this line i MUST be in this routine so that if we
    745          * don't have to change the line, we don't move to it. el_cursor.h to
    746          * first diff char
    747          */
    748 	terminal_move_to_line(el, i);
    749 
    750 	/*
    751          * at this point we have something like this:
    752          *
    753          * /old                  /ofd    /osb               /ose    /ols     /oe
    754          * v.....................v       v..................v       v........v
    755          * eddie> Oh, my fredded gruntle-buggy is to me, as foo var lurgid as
    756          * eddie> Oh, my fredded quiux buggy is to me, as gruntle-lurgid as
    757          * ^.....................^     ^..................^       ^........^
    758          * \new                  \nfd  \nsb               \nse     \nls    \ne
    759          *
    760          * fx is the difference in length between the chars between nfd and
    761          * nsb, and the chars between ofd and osb, and is thus the number of
    762          * characters to delete if < 0 (new is shorter than old, as above),
    763          * or insert (new is longer than short).
    764          *
    765          * sx is the same for the second differences.
    766          */
    767 
    768 	/*
    769          * if we have a net insert on the first difference, AND inserting the
    770          * net amount ((nsb-nfd) - (osb-ofd)) won't push the last useful
    771          * character (which is ne if nls != ne, otherwise is nse) off the edge
    772 	 * of the screen (el->el_terminal.t_size.h) else we do the deletes first
    773 	 * so that we keep everything we need to.
    774          */
    775 
    776 	/*
    777          * if the last same is the same like the end, there is no last same
    778          * part, otherwise we want to keep the last same part set p to the
    779          * last useful old character
    780          */
    781 	p = (ols != oe) ? oe : ose;
    782 
    783 	/*
    784          * if (There is a diffence in the beginning) && (we need to insert
    785          *   characters) && (the number of characters to insert is less than
    786          *   the term width)
    787 	 *	We need to do an insert!
    788 	 * else if (we need to delete characters)
    789 	 *	We need to delete characters!
    790 	 * else
    791 	 *	No insert or delete
    792          */
    793 	if ((nsb != nfd) && fx > 0 &&
    794 	    ((p - old) + fx <= el->el_terminal.t_size.h)) {
    795 		ELRE_DEBUG(1,
    796 		    (__F, "first diff insert at %td...\r\n", nfd - new));
    797 		/*
    798 		 * Move to the first char to insert, where the first diff is.
    799 		 */
    800 		terminal_move_to_char(el, (int)(nfd - new));
    801 		/*
    802 		 * Check if we have stuff to keep at end
    803 		 */
    804 		if (nsb != ne) {
    805 			ELRE_DEBUG(1, (__F, "with stuff to keep at end\r\n"));
    806 			/*
    807 		         * insert fx chars of new starting at nfd
    808 		         */
    809 			if (fx > 0) {
    810 				ELRE_DEBUG(!EL_CAN_INSERT, (__F,
    811 				"ERROR: cannot insert in early first diff\n"));
    812 				terminal_insertwrite(el, nfd, fx);
    813 				re_insert(el, old, (int)(ofd - old),
    814 				    el->el_terminal.t_size.h, nfd, fx);
    815 			}
    816 			/*
    817 		         * write (nsb-nfd) - fx chars of new starting at
    818 		         * (nfd + fx)
    819 			 */
    820 			len = (size_t) ((nsb - nfd) - fx);
    821 			terminal_overwrite(el, (nfd + fx), len);
    822 			re__strncopy(ofd + fx, nfd + fx, len);
    823 		} else {
    824 			ELRE_DEBUG(1, (__F, "without anything to save\r\n"));
    825 			len = (size_t)(nsb - nfd);
    826 			terminal_overwrite(el, nfd, len);
    827 			re__strncopy(ofd, nfd, len);
    828 			/*
    829 		         * Done
    830 		         */
    831 			return;
    832 		}
    833 	} else if (fx < 0) {
    834 		ELRE_DEBUG(1,
    835 		    (__F, "first diff delete at %td...\r\n", ofd - old));
    836 		/*
    837 		 * move to the first char to delete where the first diff is
    838 		 */
    839 		terminal_move_to_char(el, (int)(ofd - old));
    840 		/*
    841 		 * Check if we have stuff to save
    842 		 */
    843 		if (osb != oe) {
    844 			ELRE_DEBUG(1, (__F, "with stuff to save at end\r\n"));
    845 			/*
    846 		         * fx is less than zero *always* here but we check
    847 		         * for code symmetry
    848 		         */
    849 			if (fx < 0) {
    850 				ELRE_DEBUG(!EL_CAN_DELETE, (__F,
    851 				    "ERROR: cannot delete in first diff\n"));
    852 				terminal_deletechars(el, -fx);
    853 				re_delete(el, old, (int)(ofd - old),
    854 				    el->el_terminal.t_size.h, -fx);
    855 			}
    856 			/*
    857 		         * write (nsb-nfd) chars of new starting at nfd
    858 		         */
    859 			len = (size_t) (nsb - nfd);
    860 			terminal_overwrite(el, nfd, len);
    861 			re__strncopy(ofd, nfd, len);
    862 
    863 		} else {
    864 			ELRE_DEBUG(1, (__F,
    865 			    "but with nothing left to save\r\n"));
    866 			/*
    867 		         * write (nsb-nfd) chars of new starting at nfd
    868 		         */
    869 			terminal_overwrite(el, nfd, (size_t)(nsb - nfd));
    870 			re_clear_eol(el, fx, sx,
    871 			    (int)((oe - old) - (ne - new)));
    872 			/*
    873 		         * Done
    874 		         */
    875 			return;
    876 		}
    877 	} else
    878 		fx = 0;
    879 
    880 	if (sx < 0 && (ose - old) + fx < el->el_terminal.t_size.h) {
    881 		ELRE_DEBUG(1, (__F,
    882 		    "second diff delete at %td...\r\n", (ose - old) + fx));
    883 		/*
    884 		 * Check if we have stuff to delete
    885 		 */
    886 		/*
    887 		 * fx is the number of characters inserted (+) or deleted (-)
    888 		 */
    889 
    890 		terminal_move_to_char(el, (int)((ose - old) + fx));
    891 		/*
    892 		 * Check if we have stuff to save
    893 		 */
    894 		if (ols != oe) {
    895 			ELRE_DEBUG(1, (__F, "with stuff to save at end\r\n"));
    896 			/*
    897 		         * Again a duplicate test.
    898 		         */
    899 			if (sx < 0) {
    900 				ELRE_DEBUG(!EL_CAN_DELETE, (__F,
    901 				    "ERROR: cannot delete in second diff\n"));
    902 				terminal_deletechars(el, -sx);
    903 			}
    904 			/*
    905 		         * write (nls-nse) chars of new starting at nse
    906 		         */
    907 			terminal_overwrite(el, nse, (size_t)(nls - nse));
    908 		} else {
    909 			ELRE_DEBUG(1, (__F,
    910 			    "but with nothing left to save\r\n"));
    911 			terminal_overwrite(el, nse, (size_t)(nls - nse));
    912 			re_clear_eol(el, fx, sx,
    913 			    (int)((oe - old) - (ne - new)));
    914 		}
    915 	}
    916 	/*
    917          * if we have a first insert AND WE HAVEN'T ALREADY DONE IT...
    918          */
    919 	if ((nsb != nfd) && (osb - ofd) <= (nsb - nfd) && (fx == 0)) {
    920 		ELRE_DEBUG(1, (__F, "late first diff insert at %td...\r\n",
    921 		    nfd - new));
    922 
    923 		terminal_move_to_char(el, (int)(nfd - new));
    924 		/*
    925 		 * Check if we have stuff to keep at the end
    926 		 */
    927 		if (nsb != ne) {
    928 			ELRE_DEBUG(1, (__F, "with stuff to keep at end\r\n"));
    929 			/*
    930 		         * We have to recalculate fx here because we set it
    931 		         * to zero above as a flag saying that we hadn't done
    932 		         * an early first insert.
    933 		         */
    934 			fx = (int)((nsb - nfd) - (osb - ofd));
    935 			if (fx > 0) {
    936 				/*
    937 				 * insert fx chars of new starting at nfd
    938 				 */
    939 				ELRE_DEBUG(!EL_CAN_INSERT, (__F,
    940 				 "ERROR: cannot insert in late first diff\n"));
    941 				terminal_insertwrite(el, nfd, fx);
    942 				re_insert(el, old, (int)(ofd - old),
    943 				    el->el_terminal.t_size.h, nfd, fx);
    944 			}
    945 			/*
    946 		         * write (nsb-nfd) - fx chars of new starting at
    947 		         * (nfd + fx)
    948 			 */
    949 			len = (size_t) ((nsb - nfd) - fx);
    950 			terminal_overwrite(el, (nfd + fx), len);
    951 			re__strncopy(ofd + fx, nfd + fx, len);
    952 		} else {
    953 			ELRE_DEBUG(1, (__F, "without anything to save\r\n"));
    954 			len = (size_t) (nsb - nfd);
    955 			terminal_overwrite(el, nfd, len);
    956 			re__strncopy(ofd, nfd, len);
    957 		}
    958 	}
    959 	/*
    960          * line is now NEW up to nse
    961          */
    962 	if (sx >= 0) {
    963 		ELRE_DEBUG(1, (__F,
    964 		    "second diff insert at %d...\r\n", (int)(nse - new)));
    965 		terminal_move_to_char(el, (int)(nse - new));
    966 		if (ols != oe) {
    967 			ELRE_DEBUG(1, (__F, "with stuff to keep at end\r\n"));
    968 			if (sx > 0) {
    969 				/* insert sx chars of new starting at nse */
    970 				ELRE_DEBUG(!EL_CAN_INSERT, (__F,
    971 				    "ERROR: cannot insert in second diff\n"));
    972 				terminal_insertwrite(el, nse, sx);
    973 			}
    974 			/*
    975 		         * write (nls-nse) - sx chars of new starting at
    976 			 * (nse + sx)
    977 		         */
    978 			terminal_overwrite(el, (nse + sx),
    979 			    (size_t)((nls - nse) - sx));
    980 		} else {
    981 			ELRE_DEBUG(1, (__F, "without anything to save\r\n"));
    982 			terminal_overwrite(el, nse, (size_t)(nls - nse));
    983 
    984 			/*
    985 	                 * No need to do a clear-to-end here because we were
    986 	                 * doing a second insert, so we will have over
    987 	                 * written all of the old string.
    988 		         */
    989 		}
    990 	}
    991 	ELRE_DEBUG(1, (__F, "done.\r\n"));
    992 }
    993 
    994 
    995 /* re__copy_and_pad():
    996  *	Copy string and pad with spaces
    997  */
    998 static void
    999 re__copy_and_pad(wchar_t *dst, const wchar_t *src, size_t width)
   1000 {
   1001 	size_t i;
   1002 
   1003 	for (i = 0; i < width; i++) {
   1004 		if (*src == '\0')
   1005 			break;
   1006 		*dst++ = *src++;
   1007 	}
   1008 
   1009 	for (; i < width; i++)
   1010 		*dst++ = ' ';
   1011 
   1012 	*dst = '\0';
   1013 }
   1014 
   1015 
   1016 /* re_refresh_cursor():
   1017  *	Move to the new cursor position
   1018  */
   1019 libedit_private void
   1020 re_refresh_cursor(EditLine *el)
   1021 {
   1022 	wchar_t *cp;
   1023 	int h, v, th, w;
   1024 
   1025 	if (el->el_line.cursor >= el->el_line.lastchar) {
   1026 		if (el->el_map.current == el->el_map.alt
   1027 		    && el->el_line.lastchar != el->el_line.buffer)
   1028 			el->el_line.cursor = el->el_line.lastchar - 1;
   1029 		else
   1030 			el->el_line.cursor = el->el_line.lastchar;
   1031 	}
   1032 
   1033 	/* first we must find where the cursor is... */
   1034 	h = el->el_prompt.p_pos.h;
   1035 	v = el->el_prompt.p_pos.v;
   1036 	th = el->el_terminal.t_size.h;	/* optimize for speed */
   1037 
   1038 	/* do input buffer to el->el_line.cursor */
   1039 	for (cp = el->el_line.buffer; cp < el->el_line.cursor; cp++) {
   1040                 switch (ct_chr_class(*cp)) {
   1041 		case CHTYPE_NL:  /* handle newline in data part too */
   1042 			h = 0;
   1043 			v++;
   1044 			break;
   1045 		case CHTYPE_TAB: /* if a tab, to next tab stop */
   1046 			while (++h & 07)
   1047 				continue;
   1048 			break;
   1049 		default:
   1050 			w = wcwidth(*cp);
   1051 			if (w > 1 && h + w > th) { /* won't fit on line */
   1052 				h = 0;
   1053 				v++;
   1054 			}
   1055 			h += ct_visual_width(*cp);
   1056 			break;
   1057                 }
   1058 
   1059 		if (h >= th) {	/* check, extra long tabs picked up here also */
   1060 			h -= th;
   1061 			v++;
   1062 		}
   1063 	}
   1064         /* if we have a next character, and it's a doublewidth one, we need to
   1065          * check whether we need to linebreak for it to fit */
   1066         if (cp < el->el_line.lastchar && (w = wcwidth(*cp)) > 1)
   1067                 if (h + w > th) {
   1068                     h = 0;
   1069                     v++;
   1070                 }
   1071 
   1072 	/* now go there */
   1073 	terminal_move_to_line(el, v);
   1074 	terminal_move_to_char(el, h);
   1075 	terminal__flush(el);
   1076 }
   1077 
   1078 
   1079 /* re_fastputc():
   1080  *	Add a character fast.
   1081  */
   1082 static void
   1083 re_fastputc(EditLine *el, wint_t c)
   1084 {
   1085 	int w = wcwidth(c);
   1086 	while (w > 1 && el->el_cursor.h + w > el->el_terminal.t_size.h)
   1087 	    re_fastputc(el, ' ');
   1088 
   1089 	terminal__putc(el, c);
   1090 	el->el_display[el->el_cursor.v][el->el_cursor.h++] = c;
   1091 	while (--w > 0)
   1092 		el->el_display[el->el_cursor.v][el->el_cursor.h++]
   1093 			= MB_FILL_CHAR;
   1094 
   1095 	if (el->el_cursor.h >= el->el_terminal.t_size.h) {
   1096 		/* if we must overflow */
   1097 		el->el_cursor.h = 0;
   1098 
   1099 		/*
   1100 		 * If we would overflow (input is longer than terminal size),
   1101 		 * emulate scroll by dropping first line and shuffling the rest.
   1102 		 * We do this via pointer shuffling - it's safe in this case
   1103 		 * and we avoid memcpy().
   1104 		 */
   1105 		if (el->el_cursor.v + 1 >= el->el_terminal.t_size.v) {
   1106 			int i, lins = el->el_terminal.t_size.v;
   1107 			wchar_t *firstline = el->el_display[0];
   1108 
   1109 			for(i = 1; i < lins; i++)
   1110 				el->el_display[i - 1] = el->el_display[i];
   1111 
   1112 			re__copy_and_pad(firstline, L"", (size_t)0);
   1113 			el->el_display[i - 1] = firstline;
   1114 		} else {
   1115 			el->el_cursor.v++;
   1116 			el->el_refresh.r_oldcv++;
   1117 		}
   1118 		if (EL_HAS_AUTO_MARGINS) {
   1119 			if (EL_HAS_MAGIC_MARGINS) {
   1120 				terminal__putc(el, ' ');
   1121 				terminal__putc(el, '\b');
   1122 			}
   1123 		} else {
   1124 			terminal__putc(el, '\r');
   1125 			terminal__putc(el, '\n');
   1126 		}
   1127 	}
   1128 }
   1129 
   1130 
   1131 /* re_fastaddc():
   1132  *	we added just one char, handle it fast.
   1133  *	Assumes that screen cursor == real cursor
   1134  */
   1135 libedit_private void
   1136 re_fastaddc(EditLine *el)
   1137 {
   1138 	wchar_t c;
   1139 	int rhdiff;
   1140 
   1141 	c = el->el_line.cursor[-1];
   1142 
   1143 	if (c == '\t' || el->el_line.cursor != el->el_line.lastchar) {
   1144 		re_refresh(el);	/* too hard to handle */
   1145 		return;
   1146 	}
   1147 	rhdiff = el->el_terminal.t_size.h - el->el_cursor.h -
   1148 	    el->el_rprompt.p_pos.h;
   1149 	if (el->el_rprompt.p_pos.h && rhdiff < 3) {
   1150 		re_refresh(el);	/* clear out rprompt if less than 1 char gap */
   1151 		return;
   1152 	}			/* else (only do at end of line, no TAB) */
   1153 	switch (ct_chr_class(c)) {
   1154 	case CHTYPE_TAB: /* already handled, should never happen here */
   1155 		break;
   1156 	case CHTYPE_NL:
   1157 	case CHTYPE_PRINT:
   1158 		re_fastputc(el, c);
   1159 		break;
   1160 	case CHTYPE_ASCIICTL:
   1161 	case CHTYPE_NONPRINT: {
   1162 		wchar_t visbuf[VISUAL_WIDTH_MAX];
   1163 		ssize_t i, n =
   1164 		    ct_visual_char(visbuf, VISUAL_WIDTH_MAX, c);
   1165 		for (i = 0; n-- > 0; ++i)
   1166 			re_fastputc(el, visbuf[i]);
   1167 		break;
   1168 	}
   1169 	}
   1170 	terminal__flush(el);
   1171 }
   1172 
   1173 
   1174 /* re_clear_display():
   1175  *	clear the screen buffers so that new new prompt starts fresh.
   1176  */
   1177 libedit_private void
   1178 re_clear_display(EditLine *el)
   1179 {
   1180 	int i;
   1181 
   1182 	el->el_cursor.v = 0;
   1183 	el->el_cursor.h = 0;
   1184 	for (i = 0; i < el->el_terminal.t_size.v; i++)
   1185 		el->el_display[i][0] = '\0';
   1186 	el->el_refresh.r_oldcv = 0;
   1187 }
   1188 
   1189 
   1190 /* re_clear_lines():
   1191  *	Make sure all lines are *really* blank
   1192  */
   1193 libedit_private void
   1194 re_clear_lines(EditLine *el)
   1195 {
   1196 
   1197 	if (EL_CAN_CEOL) {
   1198 		int i;
   1199 		for (i = el->el_refresh.r_oldcv; i >= 0; i--) {
   1200 			/* for each line on the screen */
   1201 			terminal_move_to_line(el, i);
   1202 			terminal_move_to_char(el, 0);
   1203 			terminal_clear_EOL(el, el->el_terminal.t_size.h);
   1204 		}
   1205 	} else {
   1206 		terminal_move_to_line(el, el->el_refresh.r_oldcv);
   1207 					/* go to last line */
   1208 		terminal__putc(el, '\r');	/* go to BOL */
   1209 		terminal__putc(el, '\n');	/* go to new line */
   1210 	}
   1211 }
   1212