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