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