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