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