"Fossies" - the Fresh Open Source Software Archive

Member "tnftp-20200705/libedit/refresh.c" (4 Jul 2020, 33357 Bytes) of package /linux/privat/tnftp-20200705.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "refresh.c" see the Fossies "Dox" file reference documentation and the latest Fossies "Diffs" side-by-side code changes report: 20151004_vs_20200705.

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