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