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