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