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