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