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