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