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