Home | History | Annotate | Line # | Download | only in libedit
refresh.c revision 1.24
      1 /*	$NetBSD: refresh.c,v 1.24 2003/03/10 21:18:49 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.24 2003/03/10 21:18:49 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, char *d, int dat, int dlen, char *s, int num)
    342 {
    343 	char *a, *b;
    344 
    345 	if (num <= 0)
    346 		return;
    347 	if (num > dlen - dat)
    348 		num = dlen - dat;
    349 
    350 	ELRE_DEBUG(1,
    351 	    (__F, "re_insert() starting: %d at %d max %d, d == \"%s\"\n",
    352 	    num, dat, dlen, d));
    353 	ELRE_DEBUG(1, (__F, "s == \"%s\"n", s));
    354 
    355 	/* open up the space for num chars */
    356 	if (num > 0) {
    357 		b = d + dlen - 1;
    358 		a = b - num;
    359 		while (a >= &d[dat])
    360 			*b-- = *a--;
    361 		d[dlen] = '\0';	/* just in case */
    362 	}
    363 	ELRE_DEBUG(1, (__F,
    364 		"re_insert() after insert: %d at %d max %d, d == \"%s\"\n",
    365 		num, dat, dlen, d));
    366 	ELRE_DEBUG(1, (__F, "s == \"%s\"n", s));
    367 
    368 	/* copy the characters */
    369 	for (a = d + dat; (a < d + dlen) && (num > 0); num--)
    370 		*a++ = *s++;
    371 
    372 	ELRE_DEBUG(1,
    373 	    (__F, "re_insert() after copy: %d at %d max %d, %s == \"%s\"\n",
    374 	    num, dat, dlen, d, s));
    375 	ELRE_DEBUG(1, (__F, "s == \"%s\"n", s));
    376 }
    377 
    378 
    379 /* re_delete():
    380  *	delete num characters d at dat, maximum length of d is dlen
    381  */
    382 private void
    383 /*ARGSUSED*/
    384 re_delete(EditLine *el, char *d, int dat, int dlen, int num)
    385 {
    386 	char *a, *b;
    387 
    388 	if (num <= 0)
    389 		return;
    390 	if (dat + num >= dlen) {
    391 		d[dat] = '\0';
    392 		return;
    393 	}
    394 	ELRE_DEBUG(1,
    395 	    (__F, "re_delete() starting: %d at %d max %d, d == \"%s\"\n",
    396 	    num, dat, dlen, d));
    397 
    398 	/* open up the space for num chars */
    399 	if (num > 0) {
    400 		b = d + dat;
    401 		a = b + num;
    402 		while (a < &d[dlen])
    403 			*b++ = *a++;
    404 		d[dlen] = '\0';	/* just in case */
    405 	}
    406 	ELRE_DEBUG(1,
    407 	    (__F, "re_delete() after delete: %d at %d max %d, d == \"%s\"\n",
    408 	    num, dat, dlen, d));
    409 }
    410 
    411 
    412 /* re__strncopy():
    413  *	Like strncpy without padding.
    414  */
    415 private void
    416 re__strncopy(char *a, char *b, size_t n)
    417 {
    418 
    419 	while (n-- && *b)
    420 		*a++ = *b++;
    421 }
    422 
    423 
    424 /*****************************************************************
    425     re_update_line() is based on finding the middle difference of each line
    426     on the screen; vis:
    427 
    428 			     /old first difference
    429 	/beginning of line   |              /old last same       /old EOL
    430 	v		     v              v                    v
    431 old:	eddie> Oh, my little gruntle-buggy is to me, as lurgid as
    432 new:	eddie> Oh, my little buggy says to me, as lurgid as
    433 	^		     ^        ^			   ^
    434 	\beginning of line   |        \new last same	   \new end of line
    435 			     \new first difference
    436 
    437     all are character pointers for the sake of speed.  Special cases for
    438     no differences, as well as for end of line additions must be handled.
    439 **************************************************************** */
    440 
    441 /* Minimum at which doing an insert it "worth it".  This should be about
    442  * half the "cost" of going into insert mode, inserting a character, and
    443  * going back out.  This should really be calculated from the termcap
    444  * data...  For the moment, a good number for ANSI terminals.
    445  */
    446 #define	MIN_END_KEEP	4
    447 
    448 private void
    449 re_update_line(EditLine *el, char *old, char *new, int i)
    450 {
    451 	char *o, *n, *p, c;
    452 	char *ofd, *ols, *oe, *nfd, *nls, *ne;
    453 	char *osb, *ose, *nsb, *nse;
    454 	int fx, sx;
    455 
    456 	/*
    457          * find first diff
    458          */
    459 	for (o = old, n = new; *o && (*o == *n); o++, n++)
    460 		continue;
    461 	ofd = o;
    462 	nfd = n;
    463 
    464 	/*
    465          * Find the end of both old and new
    466          */
    467 	while (*o)
    468 		o++;
    469 	/*
    470          * Remove any trailing blanks off of the end, being careful not to
    471          * back up past the beginning.
    472          */
    473 	while (ofd < o) {
    474 		if (o[-1] != ' ')
    475 			break;
    476 		o--;
    477 	}
    478 	oe = o;
    479 	*oe = '\0';
    480 
    481 	while (*n)
    482 		n++;
    483 
    484 	/* remove blanks from end of new */
    485 	while (nfd < n) {
    486 		if (n[-1] != ' ')
    487 			break;
    488 		n--;
    489 	}
    490 	ne = n;
    491 	*ne = '\0';
    492 
    493 	/*
    494          * if no diff, continue to next line of redraw
    495          */
    496 	if (*ofd == '\0' && *nfd == '\0') {
    497 		ELRE_DEBUG(1, (__F, "no difference.\r\n"));
    498 		return;
    499 	}
    500 	/*
    501          * find last same pointer
    502          */
    503 	while ((o > ofd) && (n > nfd) && (*--o == *--n))
    504 		continue;
    505 	ols = ++o;
    506 	nls = ++n;
    507 
    508 	/*
    509          * find same begining and same end
    510          */
    511 	osb = ols;
    512 	nsb = nls;
    513 	ose = ols;
    514 	nse = nls;
    515 
    516 	/*
    517          * case 1: insert: scan from nfd to nls looking for *ofd
    518          */
    519 	if (*ofd) {
    520 		for (c = *ofd, n = nfd; n < nls; n++) {
    521 			if (c == *n) {
    522 				for (o = ofd, p = n;
    523 				    p < nls && o < ols && *o == *p;
    524 				    o++, p++)
    525 					continue;
    526 				/*
    527 				 * if the new match is longer and it's worth
    528 				 * keeping, then we take it
    529 				 */
    530 				if (((nse - nsb) < (p - n)) &&
    531 				    (2 * (p - n) > n - nfd)) {
    532 					nsb = n;
    533 					nse = p;
    534 					osb = ofd;
    535 					ose = o;
    536 				}
    537 			}
    538 		}
    539 	}
    540 	/*
    541          * case 2: delete: scan from ofd to ols looking for *nfd
    542          */
    543 	if (*nfd) {
    544 		for (c = *nfd, o = ofd; o < ols; o++) {
    545 			if (c == *o) {
    546 				for (n = nfd, p = o;
    547 				    p < ols && n < nls && *p == *n;
    548 				    p++, n++)
    549 					continue;
    550 				/*
    551 				 * if the new match is longer and it's worth
    552 				 * keeping, then we take it
    553 				 */
    554 				if (((ose - osb) < (p - o)) &&
    555 				    (2 * (p - o) > o - ofd)) {
    556 					nsb = nfd;
    557 					nse = n;
    558 					osb = o;
    559 					ose = p;
    560 				}
    561 			}
    562 		}
    563 	}
    564 	/*
    565          * Pragmatics I: If old trailing whitespace or not enough characters to
    566          * save to be worth it, then don't save the last same info.
    567          */
    568 	if ((oe - ols) < MIN_END_KEEP) {
    569 		ols = oe;
    570 		nls = ne;
    571 	}
    572 	/*
    573          * Pragmatics II: if the terminal isn't smart enough, make the data
    574          * dumber so the smart update doesn't try anything fancy
    575          */
    576 
    577 	/*
    578          * fx is the number of characters we need to insert/delete: in the
    579          * beginning to bring the two same begins together
    580          */
    581 	fx = (nsb - nfd) - (osb - ofd);
    582 	/*
    583          * sx is the number of characters we need to insert/delete: in the
    584          * end to bring the two same last parts together
    585          */
    586 	sx = (nls - nse) - (ols - ose);
    587 
    588 	if (!EL_CAN_INSERT) {
    589 		if (fx > 0) {
    590 			osb = ols;
    591 			ose = ols;
    592 			nsb = nls;
    593 			nse = nls;
    594 		}
    595 		if (sx > 0) {
    596 			ols = oe;
    597 			nls = ne;
    598 		}
    599 		if ((ols - ofd) < (nls - nfd)) {
    600 			ols = oe;
    601 			nls = ne;
    602 		}
    603 	}
    604 	if (!EL_CAN_DELETE) {
    605 		if (fx < 0) {
    606 			osb = ols;
    607 			ose = ols;
    608 			nsb = nls;
    609 			nse = nls;
    610 		}
    611 		if (sx < 0) {
    612 			ols = oe;
    613 			nls = ne;
    614 		}
    615 		if ((ols - ofd) > (nls - nfd)) {
    616 			ols = oe;
    617 			nls = ne;
    618 		}
    619 	}
    620 	/*
    621          * Pragmatics III: make sure the middle shifted pointers are correct if
    622          * they don't point to anything (we may have moved ols or nls).
    623          */
    624 	/* if the change isn't worth it, don't bother */
    625 	/* was: if (osb == ose) */
    626 	if ((ose - osb) < MIN_END_KEEP) {
    627 		osb = ols;
    628 		ose = ols;
    629 		nsb = nls;
    630 		nse = nls;
    631 	}
    632 	/*
    633          * Now that we are done with pragmatics we recompute fx, sx
    634          */
    635 	fx = (nsb - nfd) - (osb - ofd);
    636 	sx = (nls - nse) - (ols - ose);
    637 
    638 	ELRE_DEBUG(1, (__F, "\n"));
    639 	ELRE_DEBUG(1, (__F, "ofd %d, osb %d, ose %d, ols %d, oe %d\n",
    640 		ofd - old, osb - old, ose - old, ols - old, oe - old));
    641 	ELRE_DEBUG(1, (__F, "nfd %d, nsb %d, nse %d, nls %d, ne %d\n",
    642 		nfd - new, nsb - new, nse - new, nls - new, ne - new));
    643 	ELRE_DEBUG(1, (__F,
    644 		"xxx-xxx:\"00000000001111111111222222222233333333334\"\r\n"));
    645 	ELRE_DEBUG(1, (__F,
    646 		"xxx-xxx:\"01234567890123456789012345678901234567890\"\r\n"));
    647 #ifdef DEBUG_REFRESH
    648 	re_printstr(el, "old- oe", old, oe);
    649 	re_printstr(el, "new- ne", new, ne);
    650 	re_printstr(el, "old-ofd", old, ofd);
    651 	re_printstr(el, "new-nfd", new, nfd);
    652 	re_printstr(el, "ofd-osb", ofd, osb);
    653 	re_printstr(el, "nfd-nsb", nfd, nsb);
    654 	re_printstr(el, "osb-ose", osb, ose);
    655 	re_printstr(el, "nsb-nse", nsb, nse);
    656 	re_printstr(el, "ose-ols", ose, ols);
    657 	re_printstr(el, "nse-nls", nse, nls);
    658 	re_printstr(el, "ols- oe", ols, oe);
    659 	re_printstr(el, "nls- ne", nls, ne);
    660 #endif /* DEBUG_REFRESH */
    661 
    662 	/*
    663          * el_cursor.v to this line i MUST be in this routine so that if we
    664          * don't have to change the line, we don't move to it. el_cursor.h to
    665          * first diff char
    666          */
    667 	term_move_to_line(el, i);
    668 
    669 	/*
    670          * at this point we have something like this:
    671          *
    672          * /old                  /ofd    /osb               /ose    /ols     /oe
    673          * v.....................v       v..................v       v........v
    674          * eddie> Oh, my fredded gruntle-buggy is to me, as foo var lurgid as
    675          * eddie> Oh, my fredded quiux buggy is to me, as gruntle-lurgid as
    676          * ^.....................^     ^..................^       ^........^
    677          * \new                  \nfd  \nsb               \nse     \nls    \ne
    678          *
    679          * fx is the difference in length between the chars between nfd and
    680          * nsb, and the chars between ofd and osb, and is thus the number of
    681          * characters to delete if < 0 (new is shorter than old, as above),
    682          * or insert (new is longer than short).
    683          *
    684          * sx is the same for the second differences.
    685          */
    686 
    687 	/*
    688          * if we have a net insert on the first difference, AND inserting the
    689          * net amount ((nsb-nfd) - (osb-ofd)) won't push the last useful
    690          * character (which is ne if nls != ne, otherwise is nse) off the edge
    691 	 * of the screen (el->el_term.t_size.h) else we do the deletes first
    692 	 * so that we keep everything we need to.
    693          */
    694 
    695 	/*
    696          * if the last same is the same like the end, there is no last same
    697          * part, otherwise we want to keep the last same part set p to the
    698          * last useful old character
    699          */
    700 	p = (ols != oe) ? oe : ose;
    701 
    702 	/*
    703          * if (There is a diffence in the beginning) && (we need to insert
    704          *   characters) && (the number of characters to insert is less than
    705          *   the term width)
    706 	 *	We need to do an insert!
    707 	 * else if (we need to delete characters)
    708 	 *	We need to delete characters!
    709 	 * else
    710 	 *	No insert or delete
    711          */
    712 	if ((nsb != nfd) && fx > 0 &&
    713 	    ((p - old) + fx <= el->el_term.t_size.h)) {
    714 		ELRE_DEBUG(1,
    715 		    (__F, "first diff insert at %d...\r\n", nfd - new));
    716 		/*
    717 		 * Move to the first char to insert, where the first diff is.
    718 		 */
    719 		term_move_to_char(el, nfd - new);
    720 		/*
    721 		 * Check if we have stuff to keep at end
    722 		 */
    723 		if (nsb != ne) {
    724 			ELRE_DEBUG(1, (__F, "with stuff to keep at end\r\n"));
    725 			/*
    726 		         * insert fx chars of new starting at nfd
    727 		         */
    728 			if (fx > 0) {
    729 				ELRE_DEBUG(!EL_CAN_INSERT, (__F,
    730 				"ERROR: cannot insert in early first diff\n"));
    731 				term_insertwrite(el, nfd, fx);
    732 				re_insert(el, old, ofd - old,
    733 				    el->el_term.t_size.h, nfd, fx);
    734 			}
    735 			/*
    736 		         * write (nsb-nfd) - fx chars of new starting at
    737 		         * (nfd + fx)
    738 			 */
    739 			term_overwrite(el, nfd + fx, (nsb - nfd) - fx);
    740 			re__strncopy(ofd + fx, nfd + fx,
    741 			    (size_t) ((nsb - nfd) - fx));
    742 		} else {
    743 			ELRE_DEBUG(1, (__F, "without anything to save\r\n"));
    744 			term_overwrite(el, nfd, (nsb - nfd));
    745 			re__strncopy(ofd, nfd, (size_t) (nsb - nfd));
    746 			/*
    747 		         * Done
    748 		         */
    749 			return;
    750 		}
    751 	} else if (fx < 0) {
    752 		ELRE_DEBUG(1,
    753 		    (__F, "first diff delete at %d...\r\n", ofd - old));
    754 		/*
    755 		 * move to the first char to delete where the first diff is
    756 		 */
    757 		term_move_to_char(el, ofd - old);
    758 		/*
    759 		 * Check if we have stuff to save
    760 		 */
    761 		if (osb != oe) {
    762 			ELRE_DEBUG(1, (__F, "with stuff to save at end\r\n"));
    763 			/*
    764 		         * fx is less than zero *always* here but we check
    765 		         * for code symmetry
    766 		         */
    767 			if (fx < 0) {
    768 				ELRE_DEBUG(!EL_CAN_DELETE, (__F,
    769 				    "ERROR: cannot delete in first diff\n"));
    770 				term_deletechars(el, -fx);
    771 				re_delete(el, old, ofd - old,
    772 				    el->el_term.t_size.h, -fx);
    773 			}
    774 			/*
    775 		         * write (nsb-nfd) chars of new starting at nfd
    776 		         */
    777 			term_overwrite(el, nfd, (nsb - nfd));
    778 			re__strncopy(ofd, nfd, (size_t) (nsb - nfd));
    779 
    780 		} else {
    781 			ELRE_DEBUG(1, (__F,
    782 			    "but with nothing left to save\r\n"));
    783 			/*
    784 		         * write (nsb-nfd) chars of new starting at nfd
    785 		         */
    786 			term_overwrite(el, nfd, (nsb - nfd));
    787 			ELRE_DEBUG(1, (__F,
    788 			    "cleareol %d\n", (oe - old) - (ne - new)));
    789 			term_clear_EOL(el, (oe - old) - (ne - new));
    790 			/*
    791 		         * Done
    792 		         */
    793 			return;
    794 		}
    795 	} else
    796 		fx = 0;
    797 
    798 	if (sx < 0 && (ose - old) + fx < el->el_term.t_size.h) {
    799 		ELRE_DEBUG(1, (__F,
    800 		    "second diff delete at %d...\r\n", (ose - old) + fx));
    801 		/*
    802 		 * Check if we have stuff to delete
    803 		 */
    804 		/*
    805 		 * fx is the number of characters inserted (+) or deleted (-)
    806 		 */
    807 
    808 		term_move_to_char(el, (ose - old) + fx);
    809 		/*
    810 		 * Check if we have stuff to save
    811 		 */
    812 		if (ols != oe) {
    813 			ELRE_DEBUG(1, (__F, "with stuff to save at end\r\n"));
    814 			/*
    815 		         * Again a duplicate test.
    816 		         */
    817 			if (sx < 0) {
    818 				ELRE_DEBUG(!EL_CAN_DELETE, (__F,
    819 				    "ERROR: cannot delete in second diff\n"));
    820 				term_deletechars(el, -sx);
    821 			}
    822 			/*
    823 		         * write (nls-nse) chars of new starting at nse
    824 		         */
    825 			term_overwrite(el, nse, (nls - nse));
    826 		} else {
    827 			ELRE_DEBUG(1, (__F,
    828 			    "but with nothing left to save\r\n"));
    829 			term_overwrite(el, nse, (nls - nse));
    830 			ELRE_DEBUG(1, (__F,
    831 			    "cleareol %d\n", (oe - old) - (ne - new)));
    832 			if ((oe - old) - (ne - new) != 0)
    833 				term_clear_EOL(el, (oe - old) - (ne - new));
    834 		}
    835 	}
    836 	/*
    837          * if we have a first insert AND WE HAVEN'T ALREADY DONE IT...
    838          */
    839 	if ((nsb != nfd) && (osb - ofd) <= (nsb - nfd) && (fx == 0)) {
    840 		ELRE_DEBUG(1, (__F, "late first diff insert at %d...\r\n",
    841 		    nfd - new));
    842 
    843 		term_move_to_char(el, nfd - new);
    844 		/*
    845 		 * Check if we have stuff to keep at the end
    846 		 */
    847 		if (nsb != ne) {
    848 			ELRE_DEBUG(1, (__F, "with stuff to keep at end\r\n"));
    849 			/*
    850 		         * We have to recalculate fx here because we set it
    851 		         * to zero above as a flag saying that we hadn't done
    852 		         * an early first insert.
    853 		         */
    854 			fx = (nsb - nfd) - (osb - ofd);
    855 			if (fx > 0) {
    856 				/*
    857 				 * insert fx chars of new starting at nfd
    858 				 */
    859 				ELRE_DEBUG(!EL_CAN_INSERT, (__F,
    860 				 "ERROR: cannot insert in late first diff\n"));
    861 				term_insertwrite(el, nfd, fx);
    862 				re_insert(el, old, ofd - old,
    863 				    el->el_term.t_size.h, nfd, fx);
    864 			}
    865 			/*
    866 		         * write (nsb-nfd) - fx chars of new starting at
    867 		         * (nfd + fx)
    868 			 */
    869 			term_overwrite(el, nfd + fx, (nsb - nfd) - fx);
    870 			re__strncopy(ofd + fx, nfd + fx,
    871 			    (size_t) ((nsb - nfd) - fx));
    872 		} else {
    873 			ELRE_DEBUG(1, (__F, "without anything to save\r\n"));
    874 			term_overwrite(el, nfd, (nsb - nfd));
    875 			re__strncopy(ofd, nfd, (size_t) (nsb - nfd));
    876 		}
    877 	}
    878 	/*
    879          * line is now NEW up to nse
    880          */
    881 	if (sx >= 0) {
    882 		ELRE_DEBUG(1, (__F,
    883 		    "second diff insert at %d...\r\n", nse - new));
    884 		term_move_to_char(el, nse - new);
    885 		if (ols != oe) {
    886 			ELRE_DEBUG(1, (__F, "with stuff to keep at end\r\n"));
    887 			if (sx > 0) {
    888 				/* insert sx chars of new starting at nse */
    889 				ELRE_DEBUG(!EL_CAN_INSERT, (__F,
    890 				    "ERROR: cannot insert in second diff\n"));
    891 				term_insertwrite(el, nse, sx);
    892 			}
    893 			/*
    894 		         * write (nls-nse) - sx chars of new starting at
    895 			 * (nse + sx)
    896 		         */
    897 			term_overwrite(el, nse + sx, (nls - nse) - sx);
    898 		} else {
    899 			ELRE_DEBUG(1, (__F, "without anything to save\r\n"));
    900 			term_overwrite(el, nse, (nls - nse));
    901 
    902 			/*
    903 	                 * No need to do a clear-to-end here because we were
    904 	                 * doing a second insert, so we will have over
    905 	                 * written all of the old string.
    906 		         */
    907 		}
    908 	}
    909 	ELRE_DEBUG(1, (__F, "done.\r\n"));
    910 }
    911 
    912 
    913 /* re__copy_and_pad():
    914  *	Copy string and pad with spaces
    915  */
    916 private void
    917 re__copy_and_pad(char *dst, const char *src, size_t width)
    918 {
    919 	size_t i;
    920 
    921 	for (i = 0; i < width; i++) {
    922 		if (*src == '\0')
    923 			break;
    924 		*dst++ = *src++;
    925 	}
    926 
    927 	for (; i < width; i++)
    928 		*dst++ = ' ';
    929 
    930 	*dst = '\0';
    931 }
    932 
    933 
    934 /* re_refresh_cursor():
    935  *	Move to the new cursor position
    936  */
    937 protected void
    938 re_refresh_cursor(EditLine *el)
    939 {
    940 	char *cp, c;
    941 	int h, v, th;
    942 
    943 	if (el->el_line.cursor >= el->el_line.lastchar) {
    944 		if (el->el_map.current == el->el_map.alt
    945 		    && el->el_line.lastchar != el->el_line.buffer)
    946 			el->el_line.cursor = el->el_line.lastchar - 1;
    947 		else
    948 			el->el_line.cursor = el->el_line.lastchar;
    949 	}
    950 
    951 	/* first we must find where the cursor is... */
    952 	h = el->el_prompt.p_pos.h;
    953 	v = el->el_prompt.p_pos.v;
    954 	th = el->el_term.t_size.h;	/* optimize for speed */
    955 
    956 	/* do input buffer to el->el_line.cursor */
    957 	for (cp = el->el_line.buffer; cp < el->el_line.cursor; cp++) {
    958 		c = *cp;
    959 		h++;		/* all chars at least this long */
    960 
    961 		if (c == '\n') {/* handle newline in data part too */
    962 			h = 0;
    963 			v++;
    964 		} else {
    965 			if (c == '\t') {	/* if a tab, to next tab stop */
    966 				while (h & 07) {
    967 					h++;
    968 				}
    969 			} else if (iscntrl((unsigned char) c)) {
    970 						/* if control char */
    971 				h++;
    972 				if (h > th) {	/* if overflow, compensate */
    973 					h = 1;
    974 					v++;
    975 				}
    976 			} else if (!isprint((unsigned char) c)) {
    977 				h += 3;
    978 				if (h > th) {	/* if overflow, compensate */
    979 					h = h - th;
    980 					v++;
    981 				}
    982 			}
    983 		}
    984 
    985 		if (h >= th) {	/* check, extra long tabs picked up here also */
    986 			h = 0;
    987 			v++;
    988 		}
    989 	}
    990 
    991 	/* now go there */
    992 	term_move_to_line(el, v);
    993 	term_move_to_char(el, h);
    994 	term__flush();
    995 }
    996 
    997 
    998 /* re_fastputc():
    999  *	Add a character fast.
   1000  */
   1001 private void
   1002 re_fastputc(EditLine *el, int c)
   1003 {
   1004 
   1005 	term__putc(c);
   1006 	el->el_display[el->el_cursor.v][el->el_cursor.h++] = c;
   1007 	if (el->el_cursor.h >= el->el_term.t_size.h) {
   1008 		/* if we must overflow */
   1009 		el->el_cursor.h = 0;
   1010 
   1011 		/*
   1012 		 * If we would overflow (input is longer than terminal size),
   1013 		 * emulate scroll by dropping first line and shuffling the rest.
   1014 		 * We do this via pointer shuffling - it's safe in this case
   1015 		 * and we avoid memcpy().
   1016 		 */
   1017 		if (el->el_cursor.v + 1 >= el->el_term.t_size.v) {
   1018 			int i, lins = el->el_term.t_size.v;
   1019 			char *firstline = el->el_display[0];
   1020 
   1021 			for(i=1; i < lins; i++)
   1022 				el->el_display[i-1] = el->el_display[i];
   1023 
   1024 			re__copy_and_pad(firstline, "", 0);
   1025 			el->el_display[i-1] = firstline;
   1026 		} else {
   1027 			el->el_cursor.v++;
   1028 			el->el_refresh.r_oldcv++;
   1029 		}
   1030 		if (EL_HAS_AUTO_MARGINS) {
   1031 			if (EL_HAS_MAGIC_MARGINS) {
   1032 				term__putc(' ');
   1033 				term__putc('\b');
   1034 			}
   1035 		} else {
   1036 			term__putc('\r');
   1037 			term__putc('\n');
   1038 		}
   1039 	}
   1040 }
   1041 
   1042 
   1043 /* re_fastaddc():
   1044  *	we added just one char, handle it fast.
   1045  *	Assumes that screen cursor == real cursor
   1046  */
   1047 protected void
   1048 re_fastaddc(EditLine *el)
   1049 {
   1050 	char c;
   1051 	int rhdiff;
   1052 
   1053 	c = el->el_line.cursor[-1];
   1054 
   1055 	if (c == '\t' || el->el_line.cursor != el->el_line.lastchar) {
   1056 		re_refresh(el);	/* too hard to handle */
   1057 		return;
   1058 	}
   1059 	rhdiff = el->el_term.t_size.h - el->el_cursor.h -
   1060 	    el->el_rprompt.p_pos.h;
   1061 	if (el->el_rprompt.p_pos.h && rhdiff < 3) {
   1062 		re_refresh(el);	/* clear out rprompt if less than 1 char gap */
   1063 		return;
   1064 	}			/* else (only do at end of line, no TAB) */
   1065 	if (iscntrl((unsigned char) c)) {	/* if control char, do caret */
   1066 		char mc = (c == '\177') ? '?' : (c | 0100);
   1067 		re_fastputc(el, '^');
   1068 		re_fastputc(el, mc);
   1069 	} else if (isprint((unsigned char) c)) {	/* normal char */
   1070 		re_fastputc(el, c);
   1071 	} else {
   1072 		re_fastputc(el, '\\');
   1073 		re_fastputc(el, (int)(((((unsigned int)c) >> 6) & 3) + '0'));
   1074 		re_fastputc(el, (int)(((((unsigned int)c) >> 3) & 7) + '0'));
   1075 		re_fastputc(el, (c & 7) + '0');
   1076 	}
   1077 	term__flush();
   1078 }
   1079 
   1080 
   1081 /* re_clear_display():
   1082  *	clear the screen buffers so that new new prompt starts fresh.
   1083  */
   1084 protected void
   1085 re_clear_display(EditLine *el)
   1086 {
   1087 	int i;
   1088 
   1089 	el->el_cursor.v = 0;
   1090 	el->el_cursor.h = 0;
   1091 	for (i = 0; i < el->el_term.t_size.v; i++)
   1092 		el->el_display[i][0] = '\0';
   1093 	el->el_refresh.r_oldcv = 0;
   1094 }
   1095 
   1096 
   1097 /* re_clear_lines():
   1098  *	Make sure all lines are *really* blank
   1099  */
   1100 protected void
   1101 re_clear_lines(EditLine *el)
   1102 {
   1103 
   1104 	if (EL_CAN_CEOL) {
   1105 		int i;
   1106 		term_move_to_char(el, 0);
   1107 		for (i = 0; i <= el->el_refresh.r_oldcv; i++) {
   1108 			/* for each line on the screen */
   1109 			term_move_to_line(el, i);
   1110 			term_clear_EOL(el, el->el_term.t_size.h);
   1111 		}
   1112 		term_move_to_line(el, 0);
   1113 	} else {
   1114 		term_move_to_line(el, el->el_refresh.r_oldcv);
   1115 					/* go to last line */
   1116 		term__putc('\r');	/* go to BOL */
   1117 		term__putc('\n');	/* go to new line */
   1118 	}
   1119 }
   1120