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