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