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