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