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