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