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