xref: /netbsd-src/lib/libedit/refresh.c (revision 500db002748d9818288e46e10f026a2b09548086)
1 /*	$NetBSD: refresh.c,v 1.29 2009/02/15 21:55:23 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.29 2009/02/15 21:55:23 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 = (int)((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 = (int)((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 = (int)((nsb - nfd) - (osb - ofd));
661 	sx = (int)((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, (int)(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, (int)(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),
765 			    (int)((nsb - nfd) - fx));
766 			re__strncopy(ofd + fx, nfd + fx,
767 			    (size_t) ((nsb - nfd) - fx));
768 		} else {
769 			ELRE_DEBUG(1, (__F, "without anything to save\r\n"));
770 			term_overwrite(el, nfd, (int)(nsb - nfd));
771 			re__strncopy(ofd, nfd, (size_t) (nsb - nfd));
772 			/*
773 		         * Done
774 		         */
775 			return;
776 		}
777 	} else if (fx < 0) {
778 		ELRE_DEBUG(1,
779 		    (__F, "first diff delete at %d...\r\n", ofd - old));
780 		/*
781 		 * move to the first char to delete where the first diff is
782 		 */
783 		term_move_to_char(el, (int)(ofd - old));
784 		/*
785 		 * Check if we have stuff to save
786 		 */
787 		if (osb != oe) {
788 			ELRE_DEBUG(1, (__F, "with stuff to save at end\r\n"));
789 			/*
790 		         * fx is less than zero *always* here but we check
791 		         * for code symmetry
792 		         */
793 			if (fx < 0) {
794 				ELRE_DEBUG(!EL_CAN_DELETE, (__F,
795 				    "ERROR: cannot delete in first diff\n"));
796 				term_deletechars(el, -fx);
797 				re_delete(el, old, (int)(ofd - old),
798 				    el->el_term.t_size.h, -fx);
799 			}
800 			/*
801 		         * write (nsb-nfd) chars of new starting at nfd
802 		         */
803 			term_overwrite(el, nfd, (int)(nsb - nfd));
804 			re__strncopy(ofd, nfd, (size_t) (nsb - nfd));
805 
806 		} else {
807 			ELRE_DEBUG(1, (__F,
808 			    "but with nothing left to save\r\n"));
809 			/*
810 		         * write (nsb-nfd) chars of new starting at nfd
811 		         */
812 			term_overwrite(el, nfd, (int)(nsb - nfd));
813 			re_clear_eol(el, fx, sx,
814 			    (int)((oe - old) - (ne - new)));
815 			/*
816 		         * Done
817 		         */
818 			return;
819 		}
820 	} else
821 		fx = 0;
822 
823 	if (sx < 0 && (ose - old) + fx < el->el_term.t_size.h) {
824 		ELRE_DEBUG(1, (__F,
825 		    "second diff delete at %d...\r\n", (ose - old) + fx));
826 		/*
827 		 * Check if we have stuff to delete
828 		 */
829 		/*
830 		 * fx is the number of characters inserted (+) or deleted (-)
831 		 */
832 
833 		term_move_to_char(el, (int)((ose - old) + fx));
834 		/*
835 		 * Check if we have stuff to save
836 		 */
837 		if (ols != oe) {
838 			ELRE_DEBUG(1, (__F, "with stuff to save at end\r\n"));
839 			/*
840 		         * Again a duplicate test.
841 		         */
842 			if (sx < 0) {
843 				ELRE_DEBUG(!EL_CAN_DELETE, (__F,
844 				    "ERROR: cannot delete in second diff\n"));
845 				term_deletechars(el, -sx);
846 			}
847 			/*
848 		         * write (nls-nse) chars of new starting at nse
849 		         */
850 			term_overwrite(el, nse, (int)(nls - nse));
851 		} else {
852 			ELRE_DEBUG(1, (__F,
853 			    "but with nothing left to save\r\n"));
854 			term_overwrite(el, nse, (int)(nls - nse));
855 			re_clear_eol(el, fx, sx,
856 			    (int)((oe - old) - (ne - new)));
857 		}
858 	}
859 	/*
860          * if we have a first insert AND WE HAVEN'T ALREADY DONE IT...
861          */
862 	if ((nsb != nfd) && (osb - ofd) <= (nsb - nfd) && (fx == 0)) {
863 		ELRE_DEBUG(1, (__F, "late first diff insert at %d...\r\n",
864 		    nfd - new));
865 
866 		term_move_to_char(el, (int)(nfd - new));
867 		/*
868 		 * Check if we have stuff to keep at the end
869 		 */
870 		if (nsb != ne) {
871 			ELRE_DEBUG(1, (__F, "with stuff to keep at end\r\n"));
872 			/*
873 		         * We have to recalculate fx here because we set it
874 		         * to zero above as a flag saying that we hadn't done
875 		         * an early first insert.
876 		         */
877 			fx = (int)((nsb - nfd) - (osb - ofd));
878 			if (fx > 0) {
879 				/*
880 				 * insert fx chars of new starting at nfd
881 				 */
882 				ELRE_DEBUG(!EL_CAN_INSERT, (__F,
883 				 "ERROR: cannot insert in late first diff\n"));
884 				term_insertwrite(el, nfd, fx);
885 				re_insert(el, old, (int)(ofd - old),
886 				    el->el_term.t_size.h, nfd, fx);
887 			}
888 			/*
889 		         * write (nsb-nfd) - fx chars of new starting at
890 		         * (nfd + fx)
891 			 */
892 			term_overwrite(el, (nfd + fx),
893 			    (int)((nsb - nfd) - fx));
894 			re__strncopy(ofd + fx, nfd + fx,
895 			    (size_t) ((nsb - nfd) - fx));
896 		} else {
897 			ELRE_DEBUG(1, (__F, "without anything to save\r\n"));
898 			term_overwrite(el, nfd, (int)(nsb - nfd));
899 			re__strncopy(ofd, nfd, (size_t) (nsb - nfd));
900 		}
901 	}
902 	/*
903          * line is now NEW up to nse
904          */
905 	if (sx >= 0) {
906 		ELRE_DEBUG(1, (__F,
907 		    "second diff insert at %d...\r\n", (int)(nse - new)));
908 		term_move_to_char(el, (int)(nse - new));
909 		if (ols != oe) {
910 			ELRE_DEBUG(1, (__F, "with stuff to keep at end\r\n"));
911 			if (sx > 0) {
912 				/* insert sx chars of new starting at nse */
913 				ELRE_DEBUG(!EL_CAN_INSERT, (__F,
914 				    "ERROR: cannot insert in second diff\n"));
915 				term_insertwrite(el, nse, sx);
916 			}
917 			/*
918 		         * write (nls-nse) - sx chars of new starting at
919 			 * (nse + sx)
920 		         */
921 			term_overwrite(el, (nse + sx),
922 			    (int)((nls - nse) - sx));
923 		} else {
924 			ELRE_DEBUG(1, (__F, "without anything to save\r\n"));
925 			term_overwrite(el, nse, (int)(nls - nse));
926 
927 			/*
928 	                 * No need to do a clear-to-end here because we were
929 	                 * doing a second insert, so we will have over
930 	                 * written all of the old string.
931 		         */
932 		}
933 	}
934 	ELRE_DEBUG(1, (__F, "done.\r\n"));
935 }
936 
937 
938 /* re__copy_and_pad():
939  *	Copy string and pad with spaces
940  */
941 private void
942 re__copy_and_pad(char *dst, const char *src, size_t width)
943 {
944 	size_t i;
945 
946 	for (i = 0; i < width; i++) {
947 		if (*src == '\0')
948 			break;
949 		*dst++ = *src++;
950 	}
951 
952 	for (; i < width; i++)
953 		*dst++ = ' ';
954 
955 	*dst = '\0';
956 }
957 
958 
959 /* re_refresh_cursor():
960  *	Move to the new cursor position
961  */
962 protected void
963 re_refresh_cursor(EditLine *el)
964 {
965 	char *cp, c;
966 	int h, v, th;
967 
968 	if (el->el_line.cursor >= el->el_line.lastchar) {
969 		if (el->el_map.current == el->el_map.alt
970 		    && el->el_line.lastchar != el->el_line.buffer)
971 			el->el_line.cursor = el->el_line.lastchar - 1;
972 		else
973 			el->el_line.cursor = el->el_line.lastchar;
974 	}
975 
976 	/* first we must find where the cursor is... */
977 	h = el->el_prompt.p_pos.h;
978 	v = el->el_prompt.p_pos.v;
979 	th = el->el_term.t_size.h;	/* optimize for speed */
980 
981 	/* do input buffer to el->el_line.cursor */
982 	for (cp = el->el_line.buffer; cp < el->el_line.cursor; cp++) {
983 		c = *cp;
984 		h++;		/* all chars at least this long */
985 
986 		if (c == '\n') {/* handle newline in data part too */
987 			h = 0;
988 			v++;
989 		} else {
990 			if (c == '\t') {	/* if a tab, to next tab stop */
991 				while (h & 07) {
992 					h++;
993 				}
994 			} else if (iscntrl((unsigned char) c)) {
995 						/* if control char */
996 				h++;
997 				if (h > th) {	/* if overflow, compensate */
998 					h = 1;
999 					v++;
1000 				}
1001 			} else if (!isprint((unsigned char) c)) {
1002 				h += 3;
1003 				if (h > th) {	/* if overflow, compensate */
1004 					h = h - th;
1005 					v++;
1006 				}
1007 			}
1008 		}
1009 
1010 		if (h >= th) {	/* check, extra long tabs picked up here also */
1011 			h = 0;
1012 			v++;
1013 		}
1014 	}
1015 
1016 	/* now go there */
1017 	term_move_to_line(el, v);
1018 	term_move_to_char(el, h);
1019 	term__flush(el);
1020 }
1021 
1022 
1023 /* re_fastputc():
1024  *	Add a character fast.
1025  */
1026 private void
1027 re_fastputc(EditLine *el, int c)
1028 {
1029 
1030 	term__putc(el, c);
1031 	el->el_display[el->el_cursor.v][el->el_cursor.h++] = c;
1032 	if (el->el_cursor.h >= el->el_term.t_size.h) {
1033 		/* if we must overflow */
1034 		el->el_cursor.h = 0;
1035 
1036 		/*
1037 		 * If we would overflow (input is longer than terminal size),
1038 		 * emulate scroll by dropping first line and shuffling the rest.
1039 		 * We do this via pointer shuffling - it's safe in this case
1040 		 * and we avoid memcpy().
1041 		 */
1042 		if (el->el_cursor.v + 1 >= el->el_term.t_size.v) {
1043 			int i, lins = el->el_term.t_size.v;
1044 			char *firstline = el->el_display[0];
1045 
1046 			for(i=1; i < lins; i++)
1047 				el->el_display[i-1] = el->el_display[i];
1048 
1049 			re__copy_and_pad(firstline, "", 0);
1050 			el->el_display[i-1] = firstline;
1051 		} else {
1052 			el->el_cursor.v++;
1053 			el->el_refresh.r_oldcv++;
1054 		}
1055 		if (EL_HAS_AUTO_MARGINS) {
1056 			if (EL_HAS_MAGIC_MARGINS) {
1057 				term__putc(el, ' ');
1058 				term__putc(el, '\b');
1059 			}
1060 		} else {
1061 			term__putc(el, '\r');
1062 			term__putc(el, '\n');
1063 		}
1064 	}
1065 }
1066 
1067 
1068 /* re_fastaddc():
1069  *	we added just one char, handle it fast.
1070  *	Assumes that screen cursor == real cursor
1071  */
1072 protected void
1073 re_fastaddc(EditLine *el)
1074 {
1075 	char c;
1076 	int rhdiff;
1077 
1078 	c = el->el_line.cursor[-1];
1079 
1080 	if (c == '\t' || el->el_line.cursor != el->el_line.lastchar) {
1081 		re_refresh(el);	/* too hard to handle */
1082 		return;
1083 	}
1084 	rhdiff = el->el_term.t_size.h - el->el_cursor.h -
1085 	    el->el_rprompt.p_pos.h;
1086 	if (el->el_rprompt.p_pos.h && rhdiff < 3) {
1087 		re_refresh(el);	/* clear out rprompt if less than 1 char gap */
1088 		return;
1089 	}			/* else (only do at end of line, no TAB) */
1090 	if (iscntrl((unsigned char) c)) {	/* if control char, do caret */
1091 		char mc = (c == '\177') ? '?' : (c | 0100);
1092 		re_fastputc(el, '^');
1093 		re_fastputc(el, mc);
1094 	} else if (isprint((unsigned char) c)) {	/* normal char */
1095 		re_fastputc(el, c);
1096 	} else {
1097 		re_fastputc(el, '\\');
1098 		re_fastputc(el, (int)(((((unsigned int)c) >> 6) & 3) + '0'));
1099 		re_fastputc(el, (int)(((((unsigned int)c) >> 3) & 7) + '0'));
1100 		re_fastputc(el, (c & 7) + '0');
1101 	}
1102 	term__flush(el);
1103 }
1104 
1105 
1106 /* re_clear_display():
1107  *	clear the screen buffers so that new new prompt starts fresh.
1108  */
1109 protected void
1110 re_clear_display(EditLine *el)
1111 {
1112 	int i;
1113 
1114 	el->el_cursor.v = 0;
1115 	el->el_cursor.h = 0;
1116 	for (i = 0; i < el->el_term.t_size.v; i++)
1117 		el->el_display[i][0] = '\0';
1118 	el->el_refresh.r_oldcv = 0;
1119 }
1120 
1121 
1122 /* re_clear_lines():
1123  *	Make sure all lines are *really* blank
1124  */
1125 protected void
1126 re_clear_lines(EditLine *el)
1127 {
1128 
1129 	if (EL_CAN_CEOL) {
1130 		int i;
1131 		term_move_to_char(el, 0);
1132 		for (i = 0; i <= el->el_refresh.r_oldcv; i++) {
1133 			/* for each line on the screen */
1134 			term_move_to_line(el, i);
1135 			term_clear_EOL(el, el->el_term.t_size.h);
1136 		}
1137 		term_move_to_line(el, 0);
1138 	} else {
1139 		term_move_to_line(el, el->el_refresh.r_oldcv);
1140 					/* go to last line */
1141 		term__putc(el, '\r');	/* go to BOL */
1142 		term__putc(el, '\n');	/* go to new line */
1143 	}
1144 }
1145