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