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