xref: /netbsd-src/lib/libedit/refresh.c (revision 001c68bd94f75ce9270b69227c4199fbf34ee396)
1 /*	$NetBSD: refresh.c,v 1.25 2003/06/19 15:55:06 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.25 2003/06/19 15:55:06 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 __attribute__((__unused__)),
342     char *d, int dat, int dlen, char *s, int num)
343 {
344 	char *a, *b;
345 
346 	if (num <= 0)
347 		return;
348 	if (num > dlen - dat)
349 		num = dlen - dat;
350 
351 	ELRE_DEBUG(1,
352 	    (__F, "re_insert() starting: %d at %d max %d, d == \"%s\"\n",
353 	    num, dat, dlen, d));
354 	ELRE_DEBUG(1, (__F, "s == \"%s\"n", s));
355 
356 	/* open up the space for num chars */
357 	if (num > 0) {
358 		b = d + dlen - 1;
359 		a = b - num;
360 		while (a >= &d[dat])
361 			*b-- = *a--;
362 		d[dlen] = '\0';	/* just in case */
363 	}
364 	ELRE_DEBUG(1, (__F,
365 		"re_insert() after insert: %d at %d max %d, d == \"%s\"\n",
366 		num, dat, dlen, d));
367 	ELRE_DEBUG(1, (__F, "s == \"%s\"n", s));
368 
369 	/* copy the characters */
370 	for (a = d + dat; (a < d + dlen) && (num > 0); num--)
371 		*a++ = *s++;
372 
373 	ELRE_DEBUG(1,
374 	    (__F, "re_insert() after copy: %d at %d max %d, %s == \"%s\"\n",
375 	    num, dat, dlen, d, s));
376 	ELRE_DEBUG(1, (__F, "s == \"%s\"n", s));
377 }
378 
379 
380 /* re_delete():
381  *	delete num characters d at dat, maximum length of d is dlen
382  */
383 private void
384 /*ARGSUSED*/
385 re_delete(EditLine *el __attribute__((__unused__)),
386     char *d, int dat, int dlen, int num)
387 {
388 	char *a, *b;
389 
390 	if (num <= 0)
391 		return;
392 	if (dat + num >= dlen) {
393 		d[dat] = '\0';
394 		return;
395 	}
396 	ELRE_DEBUG(1,
397 	    (__F, "re_delete() starting: %d at %d max %d, d == \"%s\"\n",
398 	    num, dat, dlen, d));
399 
400 	/* open up the space for num chars */
401 	if (num > 0) {
402 		b = d + dat;
403 		a = b + num;
404 		while (a < &d[dlen])
405 			*b++ = *a++;
406 		d[dlen] = '\0';	/* just in case */
407 	}
408 	ELRE_DEBUG(1,
409 	    (__F, "re_delete() after delete: %d at %d max %d, d == \"%s\"\n",
410 	    num, dat, dlen, d));
411 }
412 
413 
414 /* re__strncopy():
415  *	Like strncpy without padding.
416  */
417 private void
418 re__strncopy(char *a, char *b, size_t n)
419 {
420 
421 	while (n-- && *b)
422 		*a++ = *b++;
423 }
424 
425 
426 /*****************************************************************
427     re_update_line() is based on finding the middle difference of each line
428     on the screen; vis:
429 
430 			     /old first difference
431 	/beginning of line   |              /old last same       /old EOL
432 	v		     v              v                    v
433 old:	eddie> Oh, my little gruntle-buggy is to me, as lurgid as
434 new:	eddie> Oh, my little buggy says to me, as lurgid as
435 	^		     ^        ^			   ^
436 	\beginning of line   |        \new last same	   \new end of line
437 			     \new first difference
438 
439     all are character pointers for the sake of speed.  Special cases for
440     no differences, as well as for end of line additions must be handled.
441 **************************************************************** */
442 
443 /* Minimum at which doing an insert it "worth it".  This should be about
444  * half the "cost" of going into insert mode, inserting a character, and
445  * going back out.  This should really be calculated from the termcap
446  * data...  For the moment, a good number for ANSI terminals.
447  */
448 #define	MIN_END_KEEP	4
449 
450 private void
451 re_update_line(EditLine *el, char *old, char *new, int i)
452 {
453 	char *o, *n, *p, c;
454 	char *ofd, *ols, *oe, *nfd, *nls, *ne;
455 	char *osb, *ose, *nsb, *nse;
456 	int fx, sx;
457 
458 	/*
459          * find first diff
460          */
461 	for (o = old, n = new; *o && (*o == *n); o++, n++)
462 		continue;
463 	ofd = o;
464 	nfd = n;
465 
466 	/*
467          * Find the end of both old and new
468          */
469 	while (*o)
470 		o++;
471 	/*
472          * Remove any trailing blanks off of the end, being careful not to
473          * back up past the beginning.
474          */
475 	while (ofd < o) {
476 		if (o[-1] != ' ')
477 			break;
478 		o--;
479 	}
480 	oe = o;
481 	*oe = '\0';
482 
483 	while (*n)
484 		n++;
485 
486 	/* remove blanks from end of new */
487 	while (nfd < n) {
488 		if (n[-1] != ' ')
489 			break;
490 		n--;
491 	}
492 	ne = n;
493 	*ne = '\0';
494 
495 	/*
496          * if no diff, continue to next line of redraw
497          */
498 	if (*ofd == '\0' && *nfd == '\0') {
499 		ELRE_DEBUG(1, (__F, "no difference.\r\n"));
500 		return;
501 	}
502 	/*
503          * find last same pointer
504          */
505 	while ((o > ofd) && (n > nfd) && (*--o == *--n))
506 		continue;
507 	ols = ++o;
508 	nls = ++n;
509 
510 	/*
511          * find same begining and same end
512          */
513 	osb = ols;
514 	nsb = nls;
515 	ose = ols;
516 	nse = nls;
517 
518 	/*
519          * case 1: insert: scan from nfd to nls looking for *ofd
520          */
521 	if (*ofd) {
522 		for (c = *ofd, n = nfd; n < nls; n++) {
523 			if (c == *n) {
524 				for (o = ofd, p = n;
525 				    p < nls && o < ols && *o == *p;
526 				    o++, p++)
527 					continue;
528 				/*
529 				 * if the new match is longer and it's worth
530 				 * keeping, then we take it
531 				 */
532 				if (((nse - nsb) < (p - n)) &&
533 				    (2 * (p - n) > n - nfd)) {
534 					nsb = n;
535 					nse = p;
536 					osb = ofd;
537 					ose = o;
538 				}
539 			}
540 		}
541 	}
542 	/*
543          * case 2: delete: scan from ofd to ols looking for *nfd
544          */
545 	if (*nfd) {
546 		for (c = *nfd, o = ofd; o < ols; o++) {
547 			if (c == *o) {
548 				for (n = nfd, p = o;
549 				    p < ols && n < nls && *p == *n;
550 				    p++, n++)
551 					continue;
552 				/*
553 				 * if the new match is longer and it's worth
554 				 * keeping, then we take it
555 				 */
556 				if (((ose - osb) < (p - o)) &&
557 				    (2 * (p - o) > o - ofd)) {
558 					nsb = nfd;
559 					nse = n;
560 					osb = o;
561 					ose = p;
562 				}
563 			}
564 		}
565 	}
566 	/*
567          * Pragmatics I: If old trailing whitespace or not enough characters to
568          * save to be worth it, then don't save the last same info.
569          */
570 	if ((oe - ols) < MIN_END_KEEP) {
571 		ols = oe;
572 		nls = ne;
573 	}
574 	/*
575          * Pragmatics II: if the terminal isn't smart enough, make the data
576          * dumber so the smart update doesn't try anything fancy
577          */
578 
579 	/*
580          * fx is the number of characters we need to insert/delete: in the
581          * beginning to bring the two same begins together
582          */
583 	fx = (nsb - nfd) - (osb - ofd);
584 	/*
585          * sx is the number of characters we need to insert/delete: in the
586          * end to bring the two same last parts together
587          */
588 	sx = (nls - nse) - (ols - ose);
589 
590 	if (!EL_CAN_INSERT) {
591 		if (fx > 0) {
592 			osb = ols;
593 			ose = ols;
594 			nsb = nls;
595 			nse = nls;
596 		}
597 		if (sx > 0) {
598 			ols = oe;
599 			nls = ne;
600 		}
601 		if ((ols - ofd) < (nls - nfd)) {
602 			ols = oe;
603 			nls = ne;
604 		}
605 	}
606 	if (!EL_CAN_DELETE) {
607 		if (fx < 0) {
608 			osb = ols;
609 			ose = ols;
610 			nsb = nls;
611 			nse = nls;
612 		}
613 		if (sx < 0) {
614 			ols = oe;
615 			nls = ne;
616 		}
617 		if ((ols - ofd) > (nls - nfd)) {
618 			ols = oe;
619 			nls = ne;
620 		}
621 	}
622 	/*
623          * Pragmatics III: make sure the middle shifted pointers are correct if
624          * they don't point to anything (we may have moved ols or nls).
625          */
626 	/* if the change isn't worth it, don't bother */
627 	/* was: if (osb == ose) */
628 	if ((ose - osb) < MIN_END_KEEP) {
629 		osb = ols;
630 		ose = ols;
631 		nsb = nls;
632 		nse = nls;
633 	}
634 	/*
635          * Now that we are done with pragmatics we recompute fx, sx
636          */
637 	fx = (nsb - nfd) - (osb - ofd);
638 	sx = (nls - nse) - (ols - ose);
639 
640 	ELRE_DEBUG(1, (__F, "\n"));
641 	ELRE_DEBUG(1, (__F, "ofd %d, osb %d, ose %d, ols %d, oe %d\n",
642 		ofd - old, osb - old, ose - old, ols - old, oe - old));
643 	ELRE_DEBUG(1, (__F, "nfd %d, nsb %d, nse %d, nls %d, ne %d\n",
644 		nfd - new, nsb - new, nse - new, nls - new, ne - new));
645 	ELRE_DEBUG(1, (__F,
646 		"xxx-xxx:\"00000000001111111111222222222233333333334\"\r\n"));
647 	ELRE_DEBUG(1, (__F,
648 		"xxx-xxx:\"01234567890123456789012345678901234567890\"\r\n"));
649 #ifdef DEBUG_REFRESH
650 	re_printstr(el, "old- oe", old, oe);
651 	re_printstr(el, "new- ne", new, ne);
652 	re_printstr(el, "old-ofd", old, ofd);
653 	re_printstr(el, "new-nfd", new, nfd);
654 	re_printstr(el, "ofd-osb", ofd, osb);
655 	re_printstr(el, "nfd-nsb", nfd, nsb);
656 	re_printstr(el, "osb-ose", osb, ose);
657 	re_printstr(el, "nsb-nse", nsb, nse);
658 	re_printstr(el, "ose-ols", ose, ols);
659 	re_printstr(el, "nse-nls", nse, nls);
660 	re_printstr(el, "ols- oe", ols, oe);
661 	re_printstr(el, "nls- ne", nls, ne);
662 #endif /* DEBUG_REFRESH */
663 
664 	/*
665          * el_cursor.v to this line i MUST be in this routine so that if we
666          * don't have to change the line, we don't move to it. el_cursor.h to
667          * first diff char
668          */
669 	term_move_to_line(el, i);
670 
671 	/*
672          * at this point we have something like this:
673          *
674          * /old                  /ofd    /osb               /ose    /ols     /oe
675          * v.....................v       v..................v       v........v
676          * eddie> Oh, my fredded gruntle-buggy is to me, as foo var lurgid as
677          * eddie> Oh, my fredded quiux buggy is to me, as gruntle-lurgid as
678          * ^.....................^     ^..................^       ^........^
679          * \new                  \nfd  \nsb               \nse     \nls    \ne
680          *
681          * fx is the difference in length between the chars between nfd and
682          * nsb, and the chars between ofd and osb, and is thus the number of
683          * characters to delete if < 0 (new is shorter than old, as above),
684          * or insert (new is longer than short).
685          *
686          * sx is the same for the second differences.
687          */
688 
689 	/*
690          * if we have a net insert on the first difference, AND inserting the
691          * net amount ((nsb-nfd) - (osb-ofd)) won't push the last useful
692          * character (which is ne if nls != ne, otherwise is nse) off the edge
693 	 * of the screen (el->el_term.t_size.h) else we do the deletes first
694 	 * so that we keep everything we need to.
695          */
696 
697 	/*
698          * if the last same is the same like the end, there is no last same
699          * part, otherwise we want to keep the last same part set p to the
700          * last useful old character
701          */
702 	p = (ols != oe) ? oe : ose;
703 
704 	/*
705          * if (There is a diffence in the beginning) && (we need to insert
706          *   characters) && (the number of characters to insert is less than
707          *   the term width)
708 	 *	We need to do an insert!
709 	 * else if (we need to delete characters)
710 	 *	We need to delete characters!
711 	 * else
712 	 *	No insert or delete
713          */
714 	if ((nsb != nfd) && fx > 0 &&
715 	    ((p - old) + fx <= el->el_term.t_size.h)) {
716 		ELRE_DEBUG(1,
717 		    (__F, "first diff insert at %d...\r\n", nfd - new));
718 		/*
719 		 * Move to the first char to insert, where the first diff is.
720 		 */
721 		term_move_to_char(el, nfd - new);
722 		/*
723 		 * Check if we have stuff to keep at end
724 		 */
725 		if (nsb != ne) {
726 			ELRE_DEBUG(1, (__F, "with stuff to keep at end\r\n"));
727 			/*
728 		         * insert fx chars of new starting at nfd
729 		         */
730 			if (fx > 0) {
731 				ELRE_DEBUG(!EL_CAN_INSERT, (__F,
732 				"ERROR: cannot insert in early first diff\n"));
733 				term_insertwrite(el, nfd, fx);
734 				re_insert(el, old, ofd - old,
735 				    el->el_term.t_size.h, nfd, fx);
736 			}
737 			/*
738 		         * write (nsb-nfd) - fx chars of new starting at
739 		         * (nfd + fx)
740 			 */
741 			term_overwrite(el, nfd + fx, (nsb - nfd) - fx);
742 			re__strncopy(ofd + fx, nfd + fx,
743 			    (size_t) ((nsb - nfd) - fx));
744 		} else {
745 			ELRE_DEBUG(1, (__F, "without anything to save\r\n"));
746 			term_overwrite(el, nfd, (nsb - nfd));
747 			re__strncopy(ofd, nfd, (size_t) (nsb - nfd));
748 			/*
749 		         * Done
750 		         */
751 			return;
752 		}
753 	} else if (fx < 0) {
754 		ELRE_DEBUG(1,
755 		    (__F, "first diff delete at %d...\r\n", ofd - old));
756 		/*
757 		 * move to the first char to delete where the first diff is
758 		 */
759 		term_move_to_char(el, ofd - old);
760 		/*
761 		 * Check if we have stuff to save
762 		 */
763 		if (osb != oe) {
764 			ELRE_DEBUG(1, (__F, "with stuff to save at end\r\n"));
765 			/*
766 		         * fx is less than zero *always* here but we check
767 		         * for code symmetry
768 		         */
769 			if (fx < 0) {
770 				ELRE_DEBUG(!EL_CAN_DELETE, (__F,
771 				    "ERROR: cannot delete in first diff\n"));
772 				term_deletechars(el, -fx);
773 				re_delete(el, old, ofd - old,
774 				    el->el_term.t_size.h, -fx);
775 			}
776 			/*
777 		         * write (nsb-nfd) chars of new starting at nfd
778 		         */
779 			term_overwrite(el, nfd, (nsb - nfd));
780 			re__strncopy(ofd, nfd, (size_t) (nsb - nfd));
781 
782 		} else {
783 			ELRE_DEBUG(1, (__F,
784 			    "but with nothing left to save\r\n"));
785 			/*
786 		         * write (nsb-nfd) chars of new starting at nfd
787 		         */
788 			term_overwrite(el, nfd, (nsb - nfd));
789 			ELRE_DEBUG(1, (__F,
790 			    "cleareol %d\n", (oe - old) - (ne - new)));
791 			term_clear_EOL(el, (oe - old) - (ne - new));
792 			/*
793 		         * Done
794 		         */
795 			return;
796 		}
797 	} else
798 		fx = 0;
799 
800 	if (sx < 0 && (ose - old) + fx < el->el_term.t_size.h) {
801 		ELRE_DEBUG(1, (__F,
802 		    "second diff delete at %d...\r\n", (ose - old) + fx));
803 		/*
804 		 * Check if we have stuff to delete
805 		 */
806 		/*
807 		 * fx is the number of characters inserted (+) or deleted (-)
808 		 */
809 
810 		term_move_to_char(el, (ose - old) + fx);
811 		/*
812 		 * Check if we have stuff to save
813 		 */
814 		if (ols != oe) {
815 			ELRE_DEBUG(1, (__F, "with stuff to save at end\r\n"));
816 			/*
817 		         * Again a duplicate test.
818 		         */
819 			if (sx < 0) {
820 				ELRE_DEBUG(!EL_CAN_DELETE, (__F,
821 				    "ERROR: cannot delete in second diff\n"));
822 				term_deletechars(el, -sx);
823 			}
824 			/*
825 		         * write (nls-nse) chars of new starting at nse
826 		         */
827 			term_overwrite(el, nse, (nls - nse));
828 		} else {
829 			ELRE_DEBUG(1, (__F,
830 			    "but with nothing left to save\r\n"));
831 			term_overwrite(el, nse, (nls - nse));
832 			ELRE_DEBUG(1, (__F,
833 			    "cleareol %d\n", (oe - old) - (ne - new)));
834 			if ((oe - old) - (ne - new) != 0)
835 				term_clear_EOL(el, (oe - old) - (ne - new));
836 		}
837 	}
838 	/*
839          * if we have a first insert AND WE HAVEN'T ALREADY DONE IT...
840          */
841 	if ((nsb != nfd) && (osb - ofd) <= (nsb - nfd) && (fx == 0)) {
842 		ELRE_DEBUG(1, (__F, "late first diff insert at %d...\r\n",
843 		    nfd - new));
844 
845 		term_move_to_char(el, nfd - new);
846 		/*
847 		 * Check if we have stuff to keep at the end
848 		 */
849 		if (nsb != ne) {
850 			ELRE_DEBUG(1, (__F, "with stuff to keep at end\r\n"));
851 			/*
852 		         * We have to recalculate fx here because we set it
853 		         * to zero above as a flag saying that we hadn't done
854 		         * an early first insert.
855 		         */
856 			fx = (nsb - nfd) - (osb - ofd);
857 			if (fx > 0) {
858 				/*
859 				 * insert fx chars of new starting at nfd
860 				 */
861 				ELRE_DEBUG(!EL_CAN_INSERT, (__F,
862 				 "ERROR: cannot insert in late first diff\n"));
863 				term_insertwrite(el, nfd, fx);
864 				re_insert(el, old, ofd - old,
865 				    el->el_term.t_size.h, nfd, fx);
866 			}
867 			/*
868 		         * write (nsb-nfd) - fx chars of new starting at
869 		         * (nfd + fx)
870 			 */
871 			term_overwrite(el, nfd + fx, (nsb - nfd) - fx);
872 			re__strncopy(ofd + fx, nfd + fx,
873 			    (size_t) ((nsb - nfd) - fx));
874 		} else {
875 			ELRE_DEBUG(1, (__F, "without anything to save\r\n"));
876 			term_overwrite(el, nfd, (nsb - nfd));
877 			re__strncopy(ofd, nfd, (size_t) (nsb - nfd));
878 		}
879 	}
880 	/*
881          * line is now NEW up to nse
882          */
883 	if (sx >= 0) {
884 		ELRE_DEBUG(1, (__F,
885 		    "second diff insert at %d...\r\n", nse - new));
886 		term_move_to_char(el, nse - new);
887 		if (ols != oe) {
888 			ELRE_DEBUG(1, (__F, "with stuff to keep at end\r\n"));
889 			if (sx > 0) {
890 				/* insert sx chars of new starting at nse */
891 				ELRE_DEBUG(!EL_CAN_INSERT, (__F,
892 				    "ERROR: cannot insert in second diff\n"));
893 				term_insertwrite(el, nse, sx);
894 			}
895 			/*
896 		         * write (nls-nse) - sx chars of new starting at
897 			 * (nse + sx)
898 		         */
899 			term_overwrite(el, nse + sx, (nls - nse) - sx);
900 		} else {
901 			ELRE_DEBUG(1, (__F, "without anything to save\r\n"));
902 			term_overwrite(el, nse, (nls - nse));
903 
904 			/*
905 	                 * No need to do a clear-to-end here because we were
906 	                 * doing a second insert, so we will have over
907 	                 * written all of the old string.
908 		         */
909 		}
910 	}
911 	ELRE_DEBUG(1, (__F, "done.\r\n"));
912 }
913 
914 
915 /* re__copy_and_pad():
916  *	Copy string and pad with spaces
917  */
918 private void
919 re__copy_and_pad(char *dst, const char *src, size_t width)
920 {
921 	size_t i;
922 
923 	for (i = 0; i < width; i++) {
924 		if (*src == '\0')
925 			break;
926 		*dst++ = *src++;
927 	}
928 
929 	for (; i < width; i++)
930 		*dst++ = ' ';
931 
932 	*dst = '\0';
933 }
934 
935 
936 /* re_refresh_cursor():
937  *	Move to the new cursor position
938  */
939 protected void
940 re_refresh_cursor(EditLine *el)
941 {
942 	char *cp, c;
943 	int h, v, th;
944 
945 	if (el->el_line.cursor >= el->el_line.lastchar) {
946 		if (el->el_map.current == el->el_map.alt
947 		    && el->el_line.lastchar != el->el_line.buffer)
948 			el->el_line.cursor = el->el_line.lastchar - 1;
949 		else
950 			el->el_line.cursor = el->el_line.lastchar;
951 	}
952 
953 	/* first we must find where the cursor is... */
954 	h = el->el_prompt.p_pos.h;
955 	v = el->el_prompt.p_pos.v;
956 	th = el->el_term.t_size.h;	/* optimize for speed */
957 
958 	/* do input buffer to el->el_line.cursor */
959 	for (cp = el->el_line.buffer; cp < el->el_line.cursor; cp++) {
960 		c = *cp;
961 		h++;		/* all chars at least this long */
962 
963 		if (c == '\n') {/* handle newline in data part too */
964 			h = 0;
965 			v++;
966 		} else {
967 			if (c == '\t') {	/* if a tab, to next tab stop */
968 				while (h & 07) {
969 					h++;
970 				}
971 			} else if (iscntrl((unsigned char) c)) {
972 						/* if control char */
973 				h++;
974 				if (h > th) {	/* if overflow, compensate */
975 					h = 1;
976 					v++;
977 				}
978 			} else if (!isprint((unsigned char) c)) {
979 				h += 3;
980 				if (h > th) {	/* if overflow, compensate */
981 					h = h - th;
982 					v++;
983 				}
984 			}
985 		}
986 
987 		if (h >= th) {	/* check, extra long tabs picked up here also */
988 			h = 0;
989 			v++;
990 		}
991 	}
992 
993 	/* now go there */
994 	term_move_to_line(el, v);
995 	term_move_to_char(el, h);
996 	term__flush();
997 }
998 
999 
1000 /* re_fastputc():
1001  *	Add a character fast.
1002  */
1003 private void
1004 re_fastputc(EditLine *el, int c)
1005 {
1006 
1007 	term__putc(c);
1008 	el->el_display[el->el_cursor.v][el->el_cursor.h++] = c;
1009 	if (el->el_cursor.h >= el->el_term.t_size.h) {
1010 		/* if we must overflow */
1011 		el->el_cursor.h = 0;
1012 
1013 		/*
1014 		 * If we would overflow (input is longer than terminal size),
1015 		 * emulate scroll by dropping first line and shuffling the rest.
1016 		 * We do this via pointer shuffling - it's safe in this case
1017 		 * and we avoid memcpy().
1018 		 */
1019 		if (el->el_cursor.v + 1 >= el->el_term.t_size.v) {
1020 			int i, lins = el->el_term.t_size.v;
1021 			char *firstline = el->el_display[0];
1022 
1023 			for(i=1; i < lins; i++)
1024 				el->el_display[i-1] = el->el_display[i];
1025 
1026 			re__copy_and_pad(firstline, "", 0);
1027 			el->el_display[i-1] = firstline;
1028 		} else {
1029 			el->el_cursor.v++;
1030 			el->el_refresh.r_oldcv++;
1031 		}
1032 		if (EL_HAS_AUTO_MARGINS) {
1033 			if (EL_HAS_MAGIC_MARGINS) {
1034 				term__putc(' ');
1035 				term__putc('\b');
1036 			}
1037 		} else {
1038 			term__putc('\r');
1039 			term__putc('\n');
1040 		}
1041 	}
1042 }
1043 
1044 
1045 /* re_fastaddc():
1046  *	we added just one char, handle it fast.
1047  *	Assumes that screen cursor == real cursor
1048  */
1049 protected void
1050 re_fastaddc(EditLine *el)
1051 {
1052 	char c;
1053 	int rhdiff;
1054 
1055 	c = el->el_line.cursor[-1];
1056 
1057 	if (c == '\t' || el->el_line.cursor != el->el_line.lastchar) {
1058 		re_refresh(el);	/* too hard to handle */
1059 		return;
1060 	}
1061 	rhdiff = el->el_term.t_size.h - el->el_cursor.h -
1062 	    el->el_rprompt.p_pos.h;
1063 	if (el->el_rprompt.p_pos.h && rhdiff < 3) {
1064 		re_refresh(el);	/* clear out rprompt if less than 1 char gap */
1065 		return;
1066 	}			/* else (only do at end of line, no TAB) */
1067 	if (iscntrl((unsigned char) c)) {	/* if control char, do caret */
1068 		char mc = (c == '\177') ? '?' : (c | 0100);
1069 		re_fastputc(el, '^');
1070 		re_fastputc(el, mc);
1071 	} else if (isprint((unsigned char) c)) {	/* normal char */
1072 		re_fastputc(el, c);
1073 	} else {
1074 		re_fastputc(el, '\\');
1075 		re_fastputc(el, (int)(((((unsigned int)c) >> 6) & 3) + '0'));
1076 		re_fastputc(el, (int)(((((unsigned int)c) >> 3) & 7) + '0'));
1077 		re_fastputc(el, (c & 7) + '0');
1078 	}
1079 	term__flush();
1080 }
1081 
1082 
1083 /* re_clear_display():
1084  *	clear the screen buffers so that new new prompt starts fresh.
1085  */
1086 protected void
1087 re_clear_display(EditLine *el)
1088 {
1089 	int i;
1090 
1091 	el->el_cursor.v = 0;
1092 	el->el_cursor.h = 0;
1093 	for (i = 0; i < el->el_term.t_size.v; i++)
1094 		el->el_display[i][0] = '\0';
1095 	el->el_refresh.r_oldcv = 0;
1096 }
1097 
1098 
1099 /* re_clear_lines():
1100  *	Make sure all lines are *really* blank
1101  */
1102 protected void
1103 re_clear_lines(EditLine *el)
1104 {
1105 
1106 	if (EL_CAN_CEOL) {
1107 		int i;
1108 		term_move_to_char(el, 0);
1109 		for (i = 0; i <= el->el_refresh.r_oldcv; i++) {
1110 			/* for each line on the screen */
1111 			term_move_to_line(el, i);
1112 			term_clear_EOL(el, el->el_term.t_size.h);
1113 		}
1114 		term_move_to_line(el, 0);
1115 	} else {
1116 		term_move_to_line(el, el->el_refresh.r_oldcv);
1117 					/* go to last line */
1118 		term__putc('\r');	/* go to BOL */
1119 		term__putc('\n');	/* go to new line */
1120 	}
1121 }
1122