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