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