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