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