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