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