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