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