xref: /netbsd-src/lib/libedit/refresh.c (revision 93bf6008f8b7982c1d1a9486e4a4a0e687fe36eb)
1 /*	$NetBSD: refresh.c,v 1.30 2009/03/31 17:38:27 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.30 2009/03/31 17:38:27 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", (size_t)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 	size_t len;
481 
482 	/*
483          * find first diff
484          */
485 	for (o = old, n = new; *o && (*o == *n); o++, n++)
486 		continue;
487 	ofd = o;
488 	nfd = n;
489 
490 	/*
491          * Find the end of both old and new
492          */
493 	while (*o)
494 		o++;
495 	/*
496          * Remove any trailing blanks off of the end, being careful not to
497          * back up past the beginning.
498          */
499 	while (ofd < o) {
500 		if (o[-1] != ' ')
501 			break;
502 		o--;
503 	}
504 	oe = o;
505 	*oe = '\0';
506 
507 	while (*n)
508 		n++;
509 
510 	/* remove blanks from end of new */
511 	while (nfd < n) {
512 		if (n[-1] != ' ')
513 			break;
514 		n--;
515 	}
516 	ne = n;
517 	*ne = '\0';
518 
519 	/*
520          * if no diff, continue to next line of redraw
521          */
522 	if (*ofd == '\0' && *nfd == '\0') {
523 		ELRE_DEBUG(1, (__F, "no difference.\r\n"));
524 		return;
525 	}
526 	/*
527          * find last same pointer
528          */
529 	while ((o > ofd) && (n > nfd) && (*--o == *--n))
530 		continue;
531 	ols = ++o;
532 	nls = ++n;
533 
534 	/*
535          * find same begining and same end
536          */
537 	osb = ols;
538 	nsb = nls;
539 	ose = ols;
540 	nse = nls;
541 
542 	/*
543          * case 1: insert: scan from nfd to nls looking for *ofd
544          */
545 	if (*ofd) {
546 		for (c = *ofd, n = nfd; n < nls; n++) {
547 			if (c == *n) {
548 				for (o = ofd, p = n;
549 				    p < nls && o < ols && *o == *p;
550 				    o++, p++)
551 					continue;
552 				/*
553 				 * if the new match is longer and it's worth
554 				 * keeping, then we take it
555 				 */
556 				if (((nse - nsb) < (p - n)) &&
557 				    (2 * (p - n) > n - nfd)) {
558 					nsb = n;
559 					nse = p;
560 					osb = ofd;
561 					ose = o;
562 				}
563 			}
564 		}
565 	}
566 	/*
567          * case 2: delete: scan from ofd to ols looking for *nfd
568          */
569 	if (*nfd) {
570 		for (c = *nfd, o = ofd; o < ols; o++) {
571 			if (c == *o) {
572 				for (n = nfd, p = o;
573 				    p < ols && n < nls && *p == *n;
574 				    p++, n++)
575 					continue;
576 				/*
577 				 * if the new match is longer and it's worth
578 				 * keeping, then we take it
579 				 */
580 				if (((ose - osb) < (p - o)) &&
581 				    (2 * (p - o) > o - ofd)) {
582 					nsb = nfd;
583 					nse = n;
584 					osb = o;
585 					ose = p;
586 				}
587 			}
588 		}
589 	}
590 	/*
591          * Pragmatics I: If old trailing whitespace or not enough characters to
592          * save to be worth it, then don't save the last same info.
593          */
594 	if ((oe - ols) < MIN_END_KEEP) {
595 		ols = oe;
596 		nls = ne;
597 	}
598 	/*
599          * Pragmatics II: if the terminal isn't smart enough, make the data
600          * dumber so the smart update doesn't try anything fancy
601          */
602 
603 	/*
604          * fx is the number of characters we need to insert/delete: in the
605          * beginning to bring the two same begins together
606          */
607 	fx = (int)((nsb - nfd) - (osb - ofd));
608 	/*
609          * sx is the number of characters we need to insert/delete: in the
610          * end to bring the two same last parts together
611          */
612 	sx = (int)((nls - nse) - (ols - ose));
613 
614 	if (!EL_CAN_INSERT) {
615 		if (fx > 0) {
616 			osb = ols;
617 			ose = ols;
618 			nsb = nls;
619 			nse = nls;
620 		}
621 		if (sx > 0) {
622 			ols = oe;
623 			nls = ne;
624 		}
625 		if ((ols - ofd) < (nls - nfd)) {
626 			ols = oe;
627 			nls = ne;
628 		}
629 	}
630 	if (!EL_CAN_DELETE) {
631 		if (fx < 0) {
632 			osb = ols;
633 			ose = ols;
634 			nsb = nls;
635 			nse = nls;
636 		}
637 		if (sx < 0) {
638 			ols = oe;
639 			nls = ne;
640 		}
641 		if ((ols - ofd) > (nls - nfd)) {
642 			ols = oe;
643 			nls = ne;
644 		}
645 	}
646 	/*
647          * Pragmatics III: make sure the middle shifted pointers are correct if
648          * they don't point to anything (we may have moved ols or nls).
649          */
650 	/* if the change isn't worth it, don't bother */
651 	/* was: if (osb == ose) */
652 	if ((ose - osb) < MIN_END_KEEP) {
653 		osb = ols;
654 		ose = ols;
655 		nsb = nls;
656 		nse = nls;
657 	}
658 	/*
659          * Now that we are done with pragmatics we recompute fx, sx
660          */
661 	fx = (int)((nsb - nfd) - (osb - ofd));
662 	sx = (int)((nls - nse) - (ols - ose));
663 
664 	ELRE_DEBUG(1, (__F, "fx %d, sx %d\n", fx, sx));
665 	ELRE_DEBUG(1, (__F, "ofd %d, osb %d, ose %d, ols %d, oe %d\n",
666 		ofd - old, osb - old, ose - old, ols - old, oe - old));
667 	ELRE_DEBUG(1, (__F, "nfd %d, nsb %d, nse %d, nls %d, ne %d\n",
668 		nfd - new, nsb - new, nse - new, nls - new, ne - new));
669 	ELRE_DEBUG(1, (__F,
670 		"xxx-xxx:\"00000000001111111111222222222233333333334\"\r\n"));
671 	ELRE_DEBUG(1, (__F,
672 		"xxx-xxx:\"01234567890123456789012345678901234567890\"\r\n"));
673 #ifdef DEBUG_REFRESH
674 	re_printstr(el, "old- oe", old, oe);
675 	re_printstr(el, "new- ne", new, ne);
676 	re_printstr(el, "old-ofd", old, ofd);
677 	re_printstr(el, "new-nfd", new, nfd);
678 	re_printstr(el, "ofd-osb", ofd, osb);
679 	re_printstr(el, "nfd-nsb", nfd, nsb);
680 	re_printstr(el, "osb-ose", osb, ose);
681 	re_printstr(el, "nsb-nse", nsb, nse);
682 	re_printstr(el, "ose-ols", ose, ols);
683 	re_printstr(el, "nse-nls", nse, nls);
684 	re_printstr(el, "ols- oe", ols, oe);
685 	re_printstr(el, "nls- ne", nls, ne);
686 #endif /* DEBUG_REFRESH */
687 
688 	/*
689          * el_cursor.v to this line i MUST be in this routine so that if we
690          * don't have to change the line, we don't move to it. el_cursor.h to
691          * first diff char
692          */
693 	term_move_to_line(el, i);
694 
695 	/*
696          * at this point we have something like this:
697          *
698          * /old                  /ofd    /osb               /ose    /ols     /oe
699          * v.....................v       v..................v       v........v
700          * eddie> Oh, my fredded gruntle-buggy is to me, as foo var lurgid as
701          * eddie> Oh, my fredded quiux buggy is to me, as gruntle-lurgid as
702          * ^.....................^     ^..................^       ^........^
703          * \new                  \nfd  \nsb               \nse     \nls    \ne
704          *
705          * fx is the difference in length between the chars between nfd and
706          * nsb, and the chars between ofd and osb, and is thus the number of
707          * characters to delete if < 0 (new is shorter than old, as above),
708          * or insert (new is longer than short).
709          *
710          * sx is the same for the second differences.
711          */
712 
713 	/*
714          * if we have a net insert on the first difference, AND inserting the
715          * net amount ((nsb-nfd) - (osb-ofd)) won't push the last useful
716          * character (which is ne if nls != ne, otherwise is nse) off the edge
717 	 * of the screen (el->el_term.t_size.h) else we do the deletes first
718 	 * so that we keep everything we need to.
719          */
720 
721 	/*
722          * if the last same is the same like the end, there is no last same
723          * part, otherwise we want to keep the last same part set p to the
724          * last useful old character
725          */
726 	p = (ols != oe) ? oe : ose;
727 
728 	/*
729          * if (There is a diffence in the beginning) && (we need to insert
730          *   characters) && (the number of characters to insert is less than
731          *   the term width)
732 	 *	We need to do an insert!
733 	 * else if (we need to delete characters)
734 	 *	We need to delete characters!
735 	 * else
736 	 *	No insert or delete
737          */
738 	if ((nsb != nfd) && fx > 0 &&
739 	    ((p - old) + fx <= el->el_term.t_size.h)) {
740 		ELRE_DEBUG(1,
741 		    (__F, "first diff insert at %d...\r\n", nfd - new));
742 		/*
743 		 * Move to the first char to insert, where the first diff is.
744 		 */
745 		term_move_to_char(el, (int)(nfd - new));
746 		/*
747 		 * Check if we have stuff to keep at end
748 		 */
749 		if (nsb != ne) {
750 			ELRE_DEBUG(1, (__F, "with stuff to keep at end\r\n"));
751 			/*
752 		         * insert fx chars of new starting at nfd
753 		         */
754 			if (fx > 0) {
755 				ELRE_DEBUG(!EL_CAN_INSERT, (__F,
756 				"ERROR: cannot insert in early first diff\n"));
757 				term_insertwrite(el, nfd, fx);
758 				re_insert(el, old, (int)(ofd - old),
759 				    el->el_term.t_size.h, nfd, fx);
760 			}
761 			/*
762 		         * write (nsb-nfd) - fx chars of new starting at
763 		         * (nfd + fx)
764 			 */
765 			len = (size_t) ((nsb - nfd) - fx);
766 			term_overwrite(el, (nfd + fx), len);
767 			re__strncopy(ofd + fx, nfd + fx, len);
768 		} else {
769 			ELRE_DEBUG(1, (__F, "without anything to save\r\n"));
770 			len = (size_t)(nsb - nfd);
771 			term_overwrite(el, nfd, len);
772 			re__strncopy(ofd, nfd, len);
773 			/*
774 		         * Done
775 		         */
776 			return;
777 		}
778 	} else if (fx < 0) {
779 		ELRE_DEBUG(1,
780 		    (__F, "first diff delete at %d...\r\n", ofd - old));
781 		/*
782 		 * move to the first char to delete where the first diff is
783 		 */
784 		term_move_to_char(el, (int)(ofd - old));
785 		/*
786 		 * Check if we have stuff to save
787 		 */
788 		if (osb != oe) {
789 			ELRE_DEBUG(1, (__F, "with stuff to save at end\r\n"));
790 			/*
791 		         * fx is less than zero *always* here but we check
792 		         * for code symmetry
793 		         */
794 			if (fx < 0) {
795 				ELRE_DEBUG(!EL_CAN_DELETE, (__F,
796 				    "ERROR: cannot delete in first diff\n"));
797 				term_deletechars(el, -fx);
798 				re_delete(el, old, (int)(ofd - old),
799 				    el->el_term.t_size.h, -fx);
800 			}
801 			/*
802 		         * write (nsb-nfd) chars of new starting at nfd
803 		         */
804 			len = (size_t) (nsb - nfd);
805 			term_overwrite(el, nfd, len);
806 			re__strncopy(ofd, nfd, len);
807 
808 		} else {
809 			ELRE_DEBUG(1, (__F,
810 			    "but with nothing left to save\r\n"));
811 			/*
812 		         * write (nsb-nfd) chars of new starting at nfd
813 		         */
814 			term_overwrite(el, nfd, (size_t)(nsb - nfd));
815 			re_clear_eol(el, fx, sx,
816 			    (int)((oe - old) - (ne - new)));
817 			/*
818 		         * Done
819 		         */
820 			return;
821 		}
822 	} else
823 		fx = 0;
824 
825 	if (sx < 0 && (ose - old) + fx < el->el_term.t_size.h) {
826 		ELRE_DEBUG(1, (__F,
827 		    "second diff delete at %d...\r\n", (ose - old) + fx));
828 		/*
829 		 * Check if we have stuff to delete
830 		 */
831 		/*
832 		 * fx is the number of characters inserted (+) or deleted (-)
833 		 */
834 
835 		term_move_to_char(el, (int)((ose - old) + fx));
836 		/*
837 		 * Check if we have stuff to save
838 		 */
839 		if (ols != oe) {
840 			ELRE_DEBUG(1, (__F, "with stuff to save at end\r\n"));
841 			/*
842 		         * Again a duplicate test.
843 		         */
844 			if (sx < 0) {
845 				ELRE_DEBUG(!EL_CAN_DELETE, (__F,
846 				    "ERROR: cannot delete in second diff\n"));
847 				term_deletechars(el, -sx);
848 			}
849 			/*
850 		         * write (nls-nse) chars of new starting at nse
851 		         */
852 			term_overwrite(el, nse, (size_t)(nls - nse));
853 		} else {
854 			ELRE_DEBUG(1, (__F,
855 			    "but with nothing left to save\r\n"));
856 			term_overwrite(el, nse, (size_t)(nls - nse));
857 			re_clear_eol(el, fx, sx,
858 			    (int)((oe - old) - (ne - new)));
859 		}
860 	}
861 	/*
862          * if we have a first insert AND WE HAVEN'T ALREADY DONE IT...
863          */
864 	if ((nsb != nfd) && (osb - ofd) <= (nsb - nfd) && (fx == 0)) {
865 		ELRE_DEBUG(1, (__F, "late first diff insert at %d...\r\n",
866 		    nfd - new));
867 
868 		term_move_to_char(el, (int)(nfd - new));
869 		/*
870 		 * Check if we have stuff to keep at the end
871 		 */
872 		if (nsb != ne) {
873 			ELRE_DEBUG(1, (__F, "with stuff to keep at end\r\n"));
874 			/*
875 		         * We have to recalculate fx here because we set it
876 		         * to zero above as a flag saying that we hadn't done
877 		         * an early first insert.
878 		         */
879 			fx = (int)((nsb - nfd) - (osb - ofd));
880 			if (fx > 0) {
881 				/*
882 				 * insert fx chars of new starting at nfd
883 				 */
884 				ELRE_DEBUG(!EL_CAN_INSERT, (__F,
885 				 "ERROR: cannot insert in late first diff\n"));
886 				term_insertwrite(el, nfd, fx);
887 				re_insert(el, old, (int)(ofd - old),
888 				    el->el_term.t_size.h, nfd, fx);
889 			}
890 			/*
891 		         * write (nsb-nfd) - fx chars of new starting at
892 		         * (nfd + fx)
893 			 */
894 			len = (size_t) ((nsb - nfd) - fx);
895 			term_overwrite(el, (nfd + fx), len);
896 			re__strncopy(ofd + fx, nfd + fx, len);
897 		} else {
898 			ELRE_DEBUG(1, (__F, "without anything to save\r\n"));
899 			len = (size_t) (nsb - nfd);
900 			term_overwrite(el, nfd, len);
901 			re__strncopy(ofd, nfd, len);
902 		}
903 	}
904 	/*
905          * line is now NEW up to nse
906          */
907 	if (sx >= 0) {
908 		ELRE_DEBUG(1, (__F,
909 		    "second diff insert at %d...\r\n", (int)(nse - new)));
910 		term_move_to_char(el, (int)(nse - new));
911 		if (ols != oe) {
912 			ELRE_DEBUG(1, (__F, "with stuff to keep at end\r\n"));
913 			if (sx > 0) {
914 				/* insert sx chars of new starting at nse */
915 				ELRE_DEBUG(!EL_CAN_INSERT, (__F,
916 				    "ERROR: cannot insert in second diff\n"));
917 				term_insertwrite(el, nse, sx);
918 			}
919 			/*
920 		         * write (nls-nse) - sx chars of new starting at
921 			 * (nse + sx)
922 		         */
923 			term_overwrite(el, (nse + sx),
924 			    (size_t)((nls - nse) - sx));
925 		} else {
926 			ELRE_DEBUG(1, (__F, "without anything to save\r\n"));
927 			term_overwrite(el, nse, (size_t)(nls - nse));
928 
929 			/*
930 	                 * No need to do a clear-to-end here because we were
931 	                 * doing a second insert, so we will have over
932 	                 * written all of the old string.
933 		         */
934 		}
935 	}
936 	ELRE_DEBUG(1, (__F, "done.\r\n"));
937 }
938 
939 
940 /* re__copy_and_pad():
941  *	Copy string and pad with spaces
942  */
943 private void
944 re__copy_and_pad(char *dst, const char *src, size_t width)
945 {
946 	size_t i;
947 
948 	for (i = 0; i < width; i++) {
949 		if (*src == '\0')
950 			break;
951 		*dst++ = *src++;
952 	}
953 
954 	for (; i < width; i++)
955 		*dst++ = ' ';
956 
957 	*dst = '\0';
958 }
959 
960 
961 /* re_refresh_cursor():
962  *	Move to the new cursor position
963  */
964 protected void
965 re_refresh_cursor(EditLine *el)
966 {
967 	char *cp, c;
968 	int h, v, th;
969 
970 	if (el->el_line.cursor >= el->el_line.lastchar) {
971 		if (el->el_map.current == el->el_map.alt
972 		    && el->el_line.lastchar != el->el_line.buffer)
973 			el->el_line.cursor = el->el_line.lastchar - 1;
974 		else
975 			el->el_line.cursor = el->el_line.lastchar;
976 	}
977 
978 	/* first we must find where the cursor is... */
979 	h = el->el_prompt.p_pos.h;
980 	v = el->el_prompt.p_pos.v;
981 	th = el->el_term.t_size.h;	/* optimize for speed */
982 
983 	/* do input buffer to el->el_line.cursor */
984 	for (cp = el->el_line.buffer; cp < el->el_line.cursor; cp++) {
985 		c = *cp;
986 		h++;		/* all chars at least this long */
987 
988 		if (c == '\n') {/* handle newline in data part too */
989 			h = 0;
990 			v++;
991 		} else {
992 			if (c == '\t') {	/* if a tab, to next tab stop */
993 				while (h & 07) {
994 					h++;
995 				}
996 			} else if (iscntrl((unsigned char) c)) {
997 						/* if control char */
998 				h++;
999 				if (h > th) {	/* if overflow, compensate */
1000 					h = 1;
1001 					v++;
1002 				}
1003 			} else if (!isprint((unsigned char) c)) {
1004 				h += 3;
1005 				if (h > th) {	/* if overflow, compensate */
1006 					h = h - th;
1007 					v++;
1008 				}
1009 			}
1010 		}
1011 
1012 		if (h >= th) {	/* check, extra long tabs picked up here also */
1013 			h = 0;
1014 			v++;
1015 		}
1016 	}
1017 
1018 	/* now go there */
1019 	term_move_to_line(el, v);
1020 	term_move_to_char(el, h);
1021 	term__flush(el);
1022 }
1023 
1024 
1025 /* re_fastputc():
1026  *	Add a character fast.
1027  */
1028 private void
1029 re_fastputc(EditLine *el, int c)
1030 {
1031 
1032 	term__putc(el, c);
1033 	el->el_display[el->el_cursor.v][el->el_cursor.h++] = c;
1034 	if (el->el_cursor.h >= el->el_term.t_size.h) {
1035 		/* if we must overflow */
1036 		el->el_cursor.h = 0;
1037 
1038 		/*
1039 		 * If we would overflow (input is longer than terminal size),
1040 		 * emulate scroll by dropping first line and shuffling the rest.
1041 		 * We do this via pointer shuffling - it's safe in this case
1042 		 * and we avoid memcpy().
1043 		 */
1044 		if (el->el_cursor.v + 1 >= el->el_term.t_size.v) {
1045 			int i, lins = el->el_term.t_size.v;
1046 			char *firstline = el->el_display[0];
1047 
1048 			for(i=1; i < lins; i++)
1049 				el->el_display[i-1] = el->el_display[i];
1050 
1051 			re__copy_and_pad(firstline, "", 0);
1052 			el->el_display[i-1] = firstline;
1053 		} else {
1054 			el->el_cursor.v++;
1055 			el->el_refresh.r_oldcv++;
1056 		}
1057 		if (EL_HAS_AUTO_MARGINS) {
1058 			if (EL_HAS_MAGIC_MARGINS) {
1059 				term__putc(el, ' ');
1060 				term__putc(el, '\b');
1061 			}
1062 		} else {
1063 			term__putc(el, '\r');
1064 			term__putc(el, '\n');
1065 		}
1066 	}
1067 }
1068 
1069 
1070 /* re_fastaddc():
1071  *	we added just one char, handle it fast.
1072  *	Assumes that screen cursor == real cursor
1073  */
1074 protected void
1075 re_fastaddc(EditLine *el)
1076 {
1077 	char c;
1078 	int rhdiff;
1079 
1080 	c = el->el_line.cursor[-1];
1081 
1082 	if (c == '\t' || el->el_line.cursor != el->el_line.lastchar) {
1083 		re_refresh(el);	/* too hard to handle */
1084 		return;
1085 	}
1086 	rhdiff = el->el_term.t_size.h - el->el_cursor.h -
1087 	    el->el_rprompt.p_pos.h;
1088 	if (el->el_rprompt.p_pos.h && rhdiff < 3) {
1089 		re_refresh(el);	/* clear out rprompt if less than 1 char gap */
1090 		return;
1091 	}			/* else (only do at end of line, no TAB) */
1092 	if (iscntrl((unsigned char) c)) {	/* if control char, do caret */
1093 		char mc = (c == '\177') ? '?' : (c | 0100);
1094 		re_fastputc(el, '^');
1095 		re_fastputc(el, mc);
1096 	} else if (isprint((unsigned char) c)) {	/* normal char */
1097 		re_fastputc(el, c);
1098 	} else {
1099 		re_fastputc(el, '\\');
1100 		re_fastputc(el, (int)(((((unsigned int)c) >> 6) & 3) + '0'));
1101 		re_fastputc(el, (int)(((((unsigned int)c) >> 3) & 7) + '0'));
1102 		re_fastputc(el, (c & 7) + '0');
1103 	}
1104 	term__flush(el);
1105 }
1106 
1107 
1108 /* re_clear_display():
1109  *	clear the screen buffers so that new new prompt starts fresh.
1110  */
1111 protected void
1112 re_clear_display(EditLine *el)
1113 {
1114 	int i;
1115 
1116 	el->el_cursor.v = 0;
1117 	el->el_cursor.h = 0;
1118 	for (i = 0; i < el->el_term.t_size.v; i++)
1119 		el->el_display[i][0] = '\0';
1120 	el->el_refresh.r_oldcv = 0;
1121 }
1122 
1123 
1124 /* re_clear_lines():
1125  *	Make sure all lines are *really* blank
1126  */
1127 protected void
1128 re_clear_lines(EditLine *el)
1129 {
1130 
1131 	if (EL_CAN_CEOL) {
1132 		int i;
1133 		term_move_to_char(el, 0);
1134 		for (i = 0; i <= el->el_refresh.r_oldcv; i++) {
1135 			/* for each line on the screen */
1136 			term_move_to_line(el, i);
1137 			term_clear_EOL(el, el->el_term.t_size.h);
1138 		}
1139 		term_move_to_line(el, 0);
1140 	} else {
1141 		term_move_to_line(el, el->el_refresh.r_oldcv);
1142 					/* go to last line */
1143 		term__putc(el, '\r');	/* go to BOL */
1144 		term__putc(el, '\n');	/* go to new line */
1145 	}
1146 }
1147