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