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