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