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