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