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