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