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