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