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