]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - contrib/libedit/refresh.c
Merge llvm-project release/15.x llvmorg-15.0.0-rc2-40-gfbd2950d8d0d
[FreeBSD/FreeBSD.git] / contrib / libedit / refresh.c
1 /*      $NetBSD: refresh.c,v 1.58 2021/09/09 20:24:07 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.58 2021/09/09 20:24:07 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
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                 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
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, (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
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*/
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*/
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
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
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
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
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
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
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
1153 re_fastaddc(EditLine *el)
1154 {
1155         wchar_t c;
1156         int rhdiff;
1157
1158         c = el->el_line.cursor[-1];
1159
1160         if (c == '\t' || el->el_line.cursor != el->el_line.lastchar) {
1161                 re_refresh(el); /* too hard to handle */
1162                 return;
1163         }
1164         rhdiff = el->el_terminal.t_size.h - el->el_cursor.h -
1165             el->el_rprompt.p_pos.h;
1166         if (el->el_rprompt.p_pos.h && rhdiff < 3) {
1167                 re_refresh(el); /* clear out rprompt if less than 1 char gap */
1168                 return;
1169         }                       /* else (only do at end of line, no TAB) */
1170         switch (ct_chr_class(c)) {
1171         case CHTYPE_TAB: /* already handled, should never happen here */
1172                 break;
1173         case CHTYPE_NL:
1174         case CHTYPE_PRINT:
1175                 re_fastputc(el, c);
1176                 break;
1177         case CHTYPE_ASCIICTL:
1178         case CHTYPE_NONPRINT: {
1179                 wchar_t visbuf[VISUAL_WIDTH_MAX];
1180                 ssize_t i, n =
1181                     ct_visual_char(visbuf, VISUAL_WIDTH_MAX, c);
1182                 for (i = 0; n-- > 0; ++i)
1183                         re_fastputc(el, visbuf[i]);
1184                 break;
1185         }
1186         }
1187         terminal__flush(el);
1188 }
1189
1190
1191 /* re_clear_display():
1192  *      clear the screen buffers so that new new prompt starts fresh.
1193  */
1194 libedit_private void
1195 re_clear_display(EditLine *el)
1196 {
1197         int i;
1198
1199         el->el_cursor.v = 0;
1200         el->el_cursor.h = 0;
1201         for (i = 0; i < el->el_terminal.t_size.v; i++)
1202                 el->el_display[i][0] = '\0';
1203         el->el_refresh.r_oldcv = 0;
1204 }
1205
1206
1207 /* re_clear_lines():
1208  *      Make sure all lines are *really* blank
1209  */
1210 libedit_private void
1211 re_clear_lines(EditLine *el)
1212 {
1213
1214         if (EL_CAN_CEOL) {
1215                 int i;
1216                 for (i = el->el_refresh.r_oldcv; i >= 0; i--) {
1217                         /* for each line on the screen */
1218                         terminal_move_to_line(el, i);
1219                         terminal_move_to_char(el, 0);
1220                         terminal_clear_EOL(el, el->el_terminal.t_size.h);
1221                 }
1222         } else {
1223                 terminal_move_to_line(el, el->el_refresh.r_oldcv);
1224                                         /* go to last line */
1225                 terminal__putc(el, '\r');       /* go to BOL */
1226                 terminal__putc(el, '\n');       /* go to new line */
1227         }
1228 }