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