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