Initial import from FreeBSD RELENG_4:
[dragonfly.git] / lib / libedit / refresh.c
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
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
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
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
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
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*/
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*/
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
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
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
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
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
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
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
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
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 */