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