"Fossies" - the Fresh Open Source Software Archive

Member "tnftp-20200705/libedit/terminal.c" (4 Jul 2020, 41157 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 "terminal.c" see the Fossies "Dox" file reference documentation.

    1 /*  $NetBSD: terminal.c,v 1.3 2020/07/04 13:43:21 lukem Exp $   */
    2 /*  from    NetBSD: terminal.c,v 1.42 2020/05/31 23:24:23 christos 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[] = "@(#)term.c  8.2 (Berkeley) 4/30/95";
   42 #else
   43 __RCSID(" NetBSD: terminal.c,v 1.42 2020/05/31 23:24:23 christos Exp  ");
   44 #endif
   45 #endif /* not lint && not SCCSID */
   46 #endif /* tnftp */
   47 
   48 /*
   49  * terminal.c: Editor/termcap-curses interface
   50  *         We have to declare a static variable here, since the
   51  *         termcap putchar routine does not take an argument!
   52  */
   53 #if 0 /* tnftp */
   54 #include <sys/types.h>
   55 #include <sys/ioctl.h>
   56 #include <limits.h>
   57 #include <signal.h>
   58 #include <stdio.h>
   59 #include <stdlib.h>
   60 #include <string.h>
   61 #include <unistd.h>
   62 #ifdef HAVE_TERMCAP_H
   63 #include <termcap.h>
   64 #endif
   65 #ifdef HAVE_CURSES_H
   66 #include <curses.h>
   67 #elif HAVE_NCURSES_H
   68 #include <ncurses.h>
   69 #endif
   70 
   71 /* Solaris's term.h does horrid things. */
   72 #if defined(HAVE_TERM_H) && !defined(__sun) && !defined(HAVE_TERMCAP_H)
   73 #include <term.h>
   74 #endif
   75 
   76 #ifdef _REENTRANT
   77 #include <pthread.h>
   78 #endif
   79 #endif /* tnftp */
   80 
   81 #include "el.h"
   82 #include "fcns.h"
   83 
   84 /*
   85  * IMPORTANT NOTE: these routines are allowed to look at the current screen
   86  * and the current position assuming that it is correct.  If this is not
   87  * true, then the update will be WRONG!  This is (should be) a valid
   88  * assumption...
   89  */
   90 
   91 #define TC_BUFSIZE  ((size_t)2048)
   92 
   93 #define GoodStr(a)  (el->el_terminal.t_str[a] != NULL && \
   94                 el->el_terminal.t_str[a][0] != '\0')
   95 #define Str(a)      el->el_terminal.t_str[a]
   96 #define Val(a)      el->el_terminal.t_val[a]
   97 
   98 static const struct termcapstr {
   99     const char *name;
  100     const char *long_name;
  101 } tstr[] = {
  102 #define T_al    0
  103     { "al", "add new blank line" },
  104 #define T_bl    1
  105     { "bl", "audible bell" },
  106 #define T_cd    2
  107     { "cd", "clear to bottom" },
  108 #define T_ce    3
  109     { "ce", "clear to end of line" },
  110 #define T_ch    4
  111     { "ch", "cursor to horiz pos" },
  112 #define T_cl    5
  113     { "cl", "clear screen" },
  114 #define T_dc    6
  115     { "dc", "delete a character" },
  116 #define T_dl    7
  117     { "dl", "delete a line" },
  118 #define T_dm    8
  119     { "dm", "start delete mode" },
  120 #define T_ed    9
  121     { "ed", "end delete mode" },
  122 #define T_ei    10
  123     { "ei", "end insert mode" },
  124 #define T_fs    11
  125     { "fs", "cursor from status line" },
  126 #define T_ho    12
  127     { "ho", "home cursor" },
  128 #define T_ic    13
  129     { "ic", "insert character" },
  130 #define T_im    14
  131     { "im", "start insert mode" },
  132 #define T_ip    15
  133     { "ip", "insert padding" },
  134 #define T_kd    16
  135     { "kd", "sends cursor down" },
  136 #define T_kl    17
  137     { "kl", "sends cursor left" },
  138 #define T_kr    18
  139     { "kr", "sends cursor right" },
  140 #define T_ku    19
  141     { "ku", "sends cursor up" },
  142 #define T_md    20
  143     { "md", "begin bold" },
  144 #define T_me    21
  145     { "me", "end attributes" },
  146 #define T_nd    22
  147     { "nd", "non destructive space" },
  148 #define T_se    23
  149     { "se", "end standout" },
  150 #define T_so    24
  151     { "so", "begin standout" },
  152 #define T_ts    25
  153     { "ts", "cursor to status line" },
  154 #define T_up    26
  155     { "up", "cursor up one" },
  156 #define T_us    27
  157     { "us", "begin underline" },
  158 #define T_ue    28
  159     { "ue", "end underline" },
  160 #define T_vb    29
  161     { "vb", "visible bell" },
  162 #define T_DC    30
  163     { "DC", "delete multiple chars" },
  164 #define T_DO    31
  165     { "DO", "cursor down multiple" },
  166 #define T_IC    32
  167     { "IC", "insert multiple chars" },
  168 #define T_LE    33
  169     { "LE", "cursor left multiple" },
  170 #define T_RI    34
  171     { "RI", "cursor right multiple" },
  172 #define T_UP    35
  173     { "UP", "cursor up multiple" },
  174 #define T_kh    36
  175     { "kh", "send cursor home" },
  176 #define T_at7   37
  177     { "@7", "send cursor end" },
  178 #define T_kD    38
  179     { "kD", "send cursor delete" },
  180 #define T_str   39
  181     { NULL, NULL }
  182 };
  183 
  184 static const struct termcapval {
  185     const char *name;
  186     const char *long_name;
  187 } tval[] = {
  188 #define T_am    0
  189     { "am", "has automatic margins" },
  190 #define T_pt    1
  191     { "pt", "has physical tabs" },
  192 #define T_li    2
  193     { "li", "Number of lines" },
  194 #define T_co    3
  195     { "co", "Number of columns" },
  196 #define T_km    4
  197     { "km", "Has meta key" },
  198 #define T_xt    5
  199     { "xt", "Tab chars destructive" },
  200 #define T_xn    6
  201     { "xn", "newline ignored at right margin" },
  202 #define T_MT    7
  203     { "MT", "Has meta key" },           /* XXX? */
  204 #define T_val   8
  205     { NULL, NULL, }
  206 };
  207 /* do two or more of the attributes use me */
  208 
  209 static void terminal_setflags(EditLine *);
  210 static int  terminal_rebuffer_display(EditLine *);
  211 static void terminal_free_display(EditLine *);
  212 static int  terminal_alloc_display(EditLine *);
  213 static void terminal_alloc(EditLine *, const struct termcapstr *,
  214     const char *);
  215 static void terminal_init_arrow(EditLine *);
  216 static void terminal_reset_arrow(EditLine *);
  217 static int  terminal_putc(int);
  218 static void terminal_tputs(EditLine *, const char *, int);
  219 
  220 #ifdef _REENTRANT
  221 static pthread_mutex_t terminal_mutex = PTHREAD_MUTEX_INITIALIZER;
  222 #endif
  223 static FILE *terminal_outfile = NULL;
  224 
  225 
  226 /* terminal_setflags():
  227  *  Set the terminal capability flags
  228  */
  229 static void
  230 terminal_setflags(EditLine *el)
  231 {
  232     EL_FLAGS = 0;
  233     if (el->el_tty.t_tabs)
  234         EL_FLAGS |= (Val(T_pt) && !Val(T_xt)) ? TERM_CAN_TAB : 0;
  235 
  236     EL_FLAGS |= (Val(T_km) || Val(T_MT)) ? TERM_HAS_META : 0;
  237     EL_FLAGS |= GoodStr(T_ce) ? TERM_CAN_CEOL : 0;
  238     EL_FLAGS |= (GoodStr(T_dc) || GoodStr(T_DC)) ? TERM_CAN_DELETE : 0;
  239     EL_FLAGS |= (GoodStr(T_im) || GoodStr(T_ic) || GoodStr(T_IC)) ?
  240         TERM_CAN_INSERT : 0;
  241     EL_FLAGS |= (GoodStr(T_up) || GoodStr(T_UP)) ? TERM_CAN_UP : 0;
  242     EL_FLAGS |= Val(T_am) ? TERM_HAS_AUTO_MARGINS : 0;
  243     EL_FLAGS |= Val(T_xn) ? TERM_HAS_MAGIC_MARGINS : 0;
  244 
  245     if (GoodStr(T_me) && GoodStr(T_ue))
  246         EL_FLAGS |= (strcmp(Str(T_me), Str(T_ue)) == 0) ?
  247             TERM_CAN_ME : 0;
  248     else
  249         EL_FLAGS &= ~TERM_CAN_ME;
  250     if (GoodStr(T_me) && GoodStr(T_se))
  251         EL_FLAGS |= (strcmp(Str(T_me), Str(T_se)) == 0) ?
  252             TERM_CAN_ME : 0;
  253 
  254 
  255 #ifdef DEBUG_SCREEN
  256     if (!EL_CAN_UP) {
  257         (void) fprintf(el->el_errfile,
  258             "WARNING: Your terminal cannot move up.\n");
  259         (void) fprintf(el->el_errfile,
  260             "Editing may be odd for long lines.\n");
  261     }
  262     if (!EL_CAN_CEOL)
  263         (void) fprintf(el->el_errfile, "no clear EOL capability.\n");
  264     if (!EL_CAN_DELETE)
  265         (void) fprintf(el->el_errfile, "no delete char capability.\n");
  266     if (!EL_CAN_INSERT)
  267         (void) fprintf(el->el_errfile, "no insert char capability.\n");
  268 #endif /* DEBUG_SCREEN */
  269 }
  270 
  271 /* terminal_init():
  272  *  Initialize the terminal stuff
  273  */
  274 libedit_private int
  275 terminal_init(EditLine *el)
  276 {
  277 
  278     el->el_terminal.t_buf = el_calloc(TC_BUFSIZE,
  279         sizeof(*el->el_terminal.t_buf));
  280     if (el->el_terminal.t_buf == NULL)
  281         goto fail1;
  282     el->el_terminal.t_cap = el_calloc(TC_BUFSIZE,
  283         sizeof(*el->el_terminal.t_cap));
  284     if (el->el_terminal.t_cap == NULL)
  285         goto fail2;
  286     el->el_terminal.t_fkey = el_calloc(A_K_NKEYS,
  287         sizeof(*el->el_terminal.t_fkey));
  288     if (el->el_terminal.t_fkey == NULL)
  289         goto fail3;
  290     el->el_terminal.t_loc = 0;
  291     el->el_terminal.t_str = el_calloc(T_str,
  292         sizeof(*el->el_terminal.t_str));
  293     if (el->el_terminal.t_str == NULL)
  294         goto fail4;
  295     el->el_terminal.t_val = el_calloc(T_val,
  296         sizeof(*el->el_terminal.t_val));
  297     if (el->el_terminal.t_val == NULL)
  298         goto fail5;
  299     (void) terminal_set(el, NULL);
  300     terminal_init_arrow(el);
  301     return 0;
  302 fail5:
  303     free(el->el_terminal.t_str);
  304     el->el_terminal.t_str = NULL;
  305 fail4:
  306     free(el->el_terminal.t_fkey);
  307     el->el_terminal.t_fkey = NULL;
  308 fail3:
  309     free(el->el_terminal.t_cap);
  310     el->el_terminal.t_cap = NULL;
  311 fail2:
  312     free(el->el_terminal.t_buf);
  313     el->el_terminal.t_buf = NULL;
  314 fail1:
  315     return -1;
  316 }
  317 
  318 /* terminal_end():
  319  *  Clean up the terminal stuff
  320  */
  321 libedit_private void
  322 terminal_end(EditLine *el)
  323 {
  324 
  325     el_free(el->el_terminal.t_buf);
  326     el->el_terminal.t_buf = NULL;
  327     el_free(el->el_terminal.t_cap);
  328     el->el_terminal.t_cap = NULL;
  329     el->el_terminal.t_loc = 0;
  330     el_free(el->el_terminal.t_str);
  331     el->el_terminal.t_str = NULL;
  332     el_free(el->el_terminal.t_val);
  333     el->el_terminal.t_val = NULL;
  334     el_free(el->el_terminal.t_fkey);
  335     el->el_terminal.t_fkey = NULL;
  336     terminal_free_display(el);
  337 }
  338 
  339 
  340 /* terminal_alloc():
  341  *  Maintain a string pool for termcap strings
  342  */
  343 static void
  344 terminal_alloc(EditLine *el, const struct termcapstr *t, const char *cap)
  345 {
  346     char termbuf[TC_BUFSIZE];
  347     size_t tlen, clen;
  348     char **tlist = el->el_terminal.t_str;
  349     char **tmp, **str = &tlist[t - tstr];
  350 
  351     (void) memset(termbuf, 0, sizeof(termbuf));
  352     if (cap == NULL || *cap == '\0') {
  353         *str = NULL;
  354         return;
  355     } else
  356         clen = strlen(cap);
  357 
  358     tlen = *str == NULL ? 0 : strlen(*str);
  359 
  360     /*
  361          * New string is shorter; no need to allocate space
  362          */
  363     if (clen <= tlen) {
  364         if (*str)
  365             (void) strcpy(*str, cap);   /* XXX strcpy is safe */
  366         return;
  367     }
  368     /*
  369          * New string is longer; see if we have enough space to append
  370          */
  371     if (el->el_terminal.t_loc + 3 < TC_BUFSIZE) {
  372                         /* XXX strcpy is safe */
  373         (void) strcpy(*str = &el->el_terminal.t_buf[
  374             el->el_terminal.t_loc], cap);
  375         el->el_terminal.t_loc += clen + 1;  /* one for \0 */
  376         return;
  377     }
  378     /*
  379          * Compact our buffer; no need to check compaction, cause we know it
  380          * fits...
  381          */
  382     tlen = 0;
  383     for (tmp = tlist; tmp < &tlist[T_str]; tmp++)
  384         if (*tmp != NULL && **tmp != '\0' && *tmp != *str) {
  385             char *ptr;
  386 
  387             for (ptr = *tmp; *ptr != '\0'; termbuf[tlen++] = *ptr++)
  388                 continue;
  389             termbuf[tlen++] = '\0';
  390         }
  391     memcpy(el->el_terminal.t_buf, termbuf, TC_BUFSIZE);
  392     el->el_terminal.t_loc = tlen;
  393     if (el->el_terminal.t_loc + 3 >= TC_BUFSIZE) {
  394         (void) fprintf(el->el_errfile,
  395             "Out of termcap string space.\n");
  396         return;
  397     }
  398                     /* XXX strcpy is safe */
  399     (void) strcpy(*str = &el->el_terminal.t_buf[el->el_terminal.t_loc],
  400         cap);
  401     el->el_terminal.t_loc += (size_t)clen + 1;  /* one for \0 */
  402     return;
  403 }
  404 
  405 
  406 /* terminal_rebuffer_display():
  407  *  Rebuffer the display after the screen changed size
  408  */
  409 static int
  410 terminal_rebuffer_display(EditLine *el)
  411 {
  412     coord_t *c = &el->el_terminal.t_size;
  413 
  414     terminal_free_display(el);
  415 
  416     c->h = Val(T_co);
  417     c->v = Val(T_li);
  418 
  419     if (terminal_alloc_display(el) == -1)
  420         return -1;
  421     return 0;
  422 }
  423 
  424 static wint_t **
  425 terminal_alloc_buffer(EditLine *el)
  426 {
  427     wint_t **b;
  428     coord_t *c = &el->el_terminal.t_size;
  429     int i;
  430 
  431     b =  el_calloc((size_t)(c->v + 1), sizeof(*b));
  432     if (b == NULL)
  433         return NULL;
  434     for (i = 0; i < c->v; i++) {
  435         b[i] = el_calloc((size_t)(c->h + 1), sizeof(**b));
  436         if (b[i] == NULL) {
  437             while (--i >= 0)
  438                 el_free(b[i]);
  439             el_free(b);
  440             return NULL;
  441         }
  442     }
  443     b[c->v] = NULL;
  444     return b;
  445 }
  446 
  447 static void
  448 terminal_free_buffer(wint_t ***bp)
  449 {
  450     wint_t **b;
  451     wint_t **bufp;
  452 
  453     if (*bp == NULL)
  454         return;
  455 
  456     b = *bp;
  457     *bp = NULL;
  458 
  459     for (bufp = b; *bufp != NULL; bufp++)
  460         el_free(*bufp);
  461     el_free(b);
  462 }
  463 
  464 /* terminal_alloc_display():
  465  *  Allocate a new display.
  466  */
  467 static int
  468 terminal_alloc_display(EditLine *el)
  469 {
  470     el->el_display = terminal_alloc_buffer(el);
  471     if (el->el_display == NULL)
  472         goto done;
  473     el->el_vdisplay = terminal_alloc_buffer(el);
  474     if (el->el_vdisplay == NULL)
  475         goto done;
  476     return 0;
  477 done:
  478     terminal_free_display(el);
  479     return -1;
  480 }
  481 
  482 
  483 /* terminal_free_display():
  484  *  Free the display buffers
  485  */
  486 static void
  487 terminal_free_display(EditLine *el)
  488 {
  489     terminal_free_buffer(&el->el_display);
  490     terminal_free_buffer(&el->el_vdisplay);
  491 }
  492 
  493 
  494 /* terminal_move_to_line():
  495  *  move to line <where> (first line == 0)
  496  *  as efficiently as possible
  497  */
  498 libedit_private void
  499 terminal_move_to_line(EditLine *el, int where)
  500 {
  501     int del;
  502 
  503     if (where == el->el_cursor.v)
  504         return;
  505 
  506     if (where >= el->el_terminal.t_size.v) {
  507 #ifdef DEBUG_SCREEN
  508         (void) fprintf(el->el_errfile,
  509             "%s: where is ridiculous: %d\r\n", __func__, where);
  510 #endif /* DEBUG_SCREEN */
  511         return;
  512     }
  513     if ((del = where - el->el_cursor.v) > 0) {
  514         /*
  515          * We don't use DO here because some terminals are buggy
  516          * if the destination is beyond bottom of the screen.
  517          */
  518         for (; del > 0; del--)
  519             terminal__putc(el, '\n');
  520         /* because the \n will become \r\n */
  521         el->el_cursor.h = 0;
  522     } else {        /* del < 0 */
  523         if (GoodStr(T_UP) && (-del > 1 || !GoodStr(T_up)))
  524             terminal_tputs(el, tgoto(Str(T_UP), -del, -del), -del);
  525         else {
  526             if (GoodStr(T_up))
  527                 for (; del < 0; del++)
  528                     terminal_tputs(el, Str(T_up), 1);
  529         }
  530     }
  531     el->el_cursor.v = where;/* now where is here */
  532 }
  533 
  534 
  535 /* terminal_move_to_char():
  536  *  Move to the character position specified
  537  */
  538 libedit_private void
  539 terminal_move_to_char(EditLine *el, int where)
  540 {
  541     int del, i;
  542 
  543 mc_again:
  544     if (where == el->el_cursor.h)
  545         return;
  546 
  547     if (where > el->el_terminal.t_size.h) {
  548 #ifdef DEBUG_SCREEN
  549         (void) fprintf(el->el_errfile,
  550             "%s: where is ridiculous: %d\r\n", __func__, where);
  551 #endif /* DEBUG_SCREEN */
  552         return;
  553     }
  554     if (!where) {       /* if where is first column */
  555         terminal__putc(el, '\r');   /* do a CR */
  556         el->el_cursor.h = 0;
  557         return;
  558     }
  559     del = where - el->el_cursor.h;
  560 
  561     if ((del < -4 || del > 4) && GoodStr(T_ch))
  562         /* go there directly */
  563         terminal_tputs(el, tgoto(Str(T_ch), where, where), where);
  564     else {
  565         if (del > 0) {  /* moving forward */
  566             if ((del > 4) && GoodStr(T_RI))
  567                 terminal_tputs(el, tgoto(Str(T_RI), del, del),
  568                     del);
  569             else {
  570                     /* if I can do tabs, use them */
  571                 if (EL_CAN_TAB) {
  572                     if ((el->el_cursor.h & 0370) !=
  573                         (where & ~0x7)
  574                         && (el->el_display[
  575                         el->el_cursor.v][where & 0370] !=
  576                         MB_FILL_CHAR)
  577                         ) {
  578                         /* if not within tab stop */
  579                         for (i =
  580                             (el->el_cursor.h & 0370);
  581                             i < (where & ~0x7);
  582                             i += 8)
  583                             terminal__putc(el,
  584                                 '\t');
  585                             /* then tab over */
  586                         el->el_cursor.h = where & ~0x7;
  587                     }
  588                 }
  589                 /*
  590                  * it's usually cheaper to just write the
  591                  * chars, so we do.
  592                  */
  593                 /*
  594                  * NOTE THAT terminal_overwrite() WILL CHANGE
  595                  * el->el_cursor.h!!!
  596                  */
  597                 terminal_overwrite(el, &el->el_display[
  598                     el->el_cursor.v][el->el_cursor.h],
  599                     (size_t)(where - el->el_cursor.h));
  600 
  601             }
  602         } else {    /* del < 0 := moving backward */
  603             if ((-del > 4) && GoodStr(T_LE))
  604                 terminal_tputs(el, tgoto(Str(T_LE), -del, -del),
  605                     -del);
  606             else {  /* can't go directly there */
  607                 /*
  608                  * if the "cost" is greater than the "cost"
  609                  * from col 0
  610                  */
  611                 if (EL_CAN_TAB ?
  612                     ((unsigned int)-del >
  613                     (((unsigned int) where >> 3) +
  614                      (where & 07)))
  615                     : (-del > where)) {
  616                     terminal__putc(el, '\r');/* do a CR */
  617                     el->el_cursor.h = 0;
  618                     goto mc_again;  /* and try again */
  619                 }
  620                 for (i = 0; i < -del; i++)
  621                     terminal__putc(el, '\b');
  622             }
  623         }
  624     }
  625     el->el_cursor.h = where;        /* now where is here */
  626 }
  627 
  628 
  629 /* terminal_overwrite():
  630  *  Overstrike num characters
  631  *  Assumes MB_FILL_CHARs are present to keep the column count correct
  632  */
  633 libedit_private void
  634 terminal_overwrite(EditLine *el, const wchar_t *cp, size_t n)
  635 {
  636     if (n == 0)
  637         return;
  638 
  639     if (n > (size_t)el->el_terminal.t_size.h) {
  640 #ifdef DEBUG_SCREEN
  641         (void) fprintf(el->el_errfile,
  642             "%s: n is ridiculous: %zu\r\n", __func__, n);
  643 #endif /* DEBUG_SCREEN */
  644         return;
  645     }
  646 
  647         do {
  648                 /* terminal__putc() ignores any MB_FILL_CHARs */
  649                 terminal__putc(el, *cp++);
  650                 el->el_cursor.h++;
  651         } while (--n);
  652 
  653     if (el->el_cursor.h >= el->el_terminal.t_size.h) {  /* wrap? */
  654         if (EL_HAS_AUTO_MARGINS) {  /* yes */
  655             el->el_cursor.h = 0;
  656             if (el->el_cursor.v + 1 < el->el_terminal.t_size.v)
  657                 el->el_cursor.v++;
  658             if (EL_HAS_MAGIC_MARGINS) {
  659                 /* force the wrap to avoid the "magic"
  660                  * situation */
  661                 wchar_t c;
  662                 if ((c = el->el_display[el->el_cursor.v]
  663                     [el->el_cursor.h]) != '\0') {
  664                     terminal_overwrite(el, &c, (size_t)1);
  665                     while (el->el_display[el->el_cursor.v]
  666                         [el->el_cursor.h] == MB_FILL_CHAR)
  667                         el->el_cursor.h++;
  668                 } else {
  669                     terminal__putc(el, ' ');
  670                     el->el_cursor.h = 1;
  671                 }
  672             }
  673         } else      /* no wrap, but cursor stays on screen */
  674             el->el_cursor.h = el->el_terminal.t_size.h - 1;
  675     }
  676 }
  677 
  678 
  679 /* terminal_deletechars():
  680  *  Delete num characters
  681  */
  682 libedit_private void
  683 terminal_deletechars(EditLine *el, int num)
  684 {
  685     if (num <= 0)
  686         return;
  687 
  688     if (!EL_CAN_DELETE) {
  689 #ifdef DEBUG_EDIT
  690         (void) fprintf(el->el_errfile, "   ERROR: cannot delete   \n");
  691 #endif /* DEBUG_EDIT */
  692         return;
  693     }
  694     if (num > el->el_terminal.t_size.h) {
  695 #ifdef DEBUG_SCREEN
  696         (void) fprintf(el->el_errfile,
  697             "%s: num is ridiculous: %d\r\n", __func__, num);
  698 #endif /* DEBUG_SCREEN */
  699         return;
  700     }
  701     if (GoodStr(T_DC))  /* if I have multiple delete */
  702         if ((num > 1) || !GoodStr(T_dc)) {  /* if dc would be more
  703                              * expen. */
  704             terminal_tputs(el, tgoto(Str(T_DC), num, num), num);
  705             return;
  706         }
  707     if (GoodStr(T_dm))  /* if I have delete mode */
  708         terminal_tputs(el, Str(T_dm), 1);
  709 
  710     if (GoodStr(T_dc))  /* else do one at a time */
  711         while (num--)
  712             terminal_tputs(el, Str(T_dc), 1);
  713 
  714     if (GoodStr(T_ed))  /* if I have delete mode */
  715         terminal_tputs(el, Str(T_ed), 1);
  716 }
  717 
  718 
  719 /* terminal_insertwrite():
  720  *  Puts terminal in insert character mode or inserts num
  721  *  characters in the line
  722  *      Assumes MB_FILL_CHARs are present to keep column count correct
  723  */
  724 libedit_private void
  725 terminal_insertwrite(EditLine *el, wchar_t *cp, int num)
  726 {
  727     if (num <= 0)
  728         return;
  729     if (!EL_CAN_INSERT) {
  730 #ifdef DEBUG_EDIT
  731         (void) fprintf(el->el_errfile, "   ERROR: cannot insert   \n");
  732 #endif /* DEBUG_EDIT */
  733         return;
  734     }
  735     if (num > el->el_terminal.t_size.h) {
  736 #ifdef DEBUG_SCREEN
  737         (void) fprintf(el->el_errfile,
  738             "%s: num is ridiculous: %d\r\n", __func__, num);
  739 #endif /* DEBUG_SCREEN */
  740         return;
  741     }
  742     if (GoodStr(T_IC))  /* if I have multiple insert */
  743         if ((num > 1) || !GoodStr(T_ic)) {
  744                 /* if ic would be more expensive */
  745             terminal_tputs(el, tgoto(Str(T_IC), num, num), num);
  746             terminal_overwrite(el, cp, (size_t)num);
  747                 /* this updates el_cursor.h */
  748             return;
  749         }
  750     if (GoodStr(T_im) && GoodStr(T_ei)) {   /* if I have insert mode */
  751         terminal_tputs(el, Str(T_im), 1);
  752 
  753         el->el_cursor.h += num;
  754         do
  755             terminal__putc(el, *cp++);
  756         while (--num);
  757 
  758         if (GoodStr(T_ip))  /* have to make num chars insert */
  759             terminal_tputs(el, Str(T_ip), 1);
  760 
  761         terminal_tputs(el, Str(T_ei), 1);
  762         return;
  763     }
  764     do {
  765         if (GoodStr(T_ic))  /* have to make num chars insert */
  766             terminal_tputs(el, Str(T_ic), 1);
  767 
  768         terminal__putc(el, *cp++);
  769 
  770         el->el_cursor.h++;
  771 
  772         if (GoodStr(T_ip))  /* have to make num chars insert */
  773             terminal_tputs(el, Str(T_ip), 1);
  774                     /* pad the inserted char */
  775 
  776     } while (--num);
  777 }
  778 
  779 
  780 /* terminal_clear_EOL():
  781  *  clear to end of line.  There are num characters to clear
  782  */
  783 libedit_private void
  784 terminal_clear_EOL(EditLine *el, int num)
  785 {
  786     int i;
  787 
  788     if (EL_CAN_CEOL && GoodStr(T_ce))
  789         terminal_tputs(el, Str(T_ce), 1);
  790     else {
  791         for (i = 0; i < num; i++)
  792             terminal__putc(el, ' ');
  793         el->el_cursor.h += num; /* have written num spaces */
  794     }
  795 }
  796 
  797 
  798 /* terminal_clear_screen():
  799  *  Clear the screen
  800  */
  801 libedit_private void
  802 terminal_clear_screen(EditLine *el)
  803 {               /* clear the whole screen and home */
  804 
  805     if (GoodStr(T_cl))
  806         /* send the clear screen code */
  807         terminal_tputs(el, Str(T_cl), Val(T_li));
  808     else if (GoodStr(T_ho) && GoodStr(T_cd)) {
  809         terminal_tputs(el, Str(T_ho), Val(T_li));   /* home */
  810         /* clear to bottom of screen */
  811         terminal_tputs(el, Str(T_cd), Val(T_li));
  812     } else {
  813         terminal__putc(el, '\r');
  814         terminal__putc(el, '\n');
  815     }
  816 }
  817 
  818 
  819 /* terminal_beep():
  820  *  Beep the way the terminal wants us
  821  */
  822 libedit_private void
  823 terminal_beep(EditLine *el)
  824 {
  825     if (GoodStr(T_bl))
  826         /* what termcap says we should use */
  827         terminal_tputs(el, Str(T_bl), 1);
  828     else
  829         terminal__putc(el, '\007'); /* an ASCII bell; ^G */
  830 }
  831 
  832 
  833 libedit_private void
  834 terminal_get(EditLine *el, const char **term)
  835 {
  836     *term = el->el_terminal.t_name;
  837 }
  838 
  839 
  840 /* terminal_set():
  841  *  Read in the terminal capabilities from the requested terminal
  842  */
  843 libedit_private int
  844 terminal_set(EditLine *el, const char *term)
  845 {
  846     int i;
  847     char buf[TC_BUFSIZE];
  848     char *area;
  849     const struct termcapstr *t;
  850     sigset_t oset, nset;
  851     int lins, cols;
  852 
  853     (void) sigemptyset(&nset);
  854     (void) sigaddset(&nset, SIGWINCH);
  855     (void) sigprocmask(SIG_BLOCK, &nset, &oset);
  856 
  857     area = buf;
  858 
  859 
  860     if (term == NULL)
  861         term = getenv("TERM");
  862 
  863     if (!term || !term[0])
  864         term = "dumb";
  865 
  866     if (strcmp(term, "emacs") == 0)
  867         el->el_flags |= EDIT_DISABLED;
  868 
  869     (void) memset(el->el_terminal.t_cap, 0, TC_BUFSIZE);
  870 
  871     i = tgetent(el->el_terminal.t_cap, term);
  872 
  873     if (i <= 0) {
  874         if (i == -1)
  875             (void) fprintf(el->el_errfile,
  876                 "Cannot read termcap database;\n");
  877         else if (i == 0)
  878             (void) fprintf(el->el_errfile,
  879                 "No entry for terminal type \"%s\";\n", term);
  880         (void) fprintf(el->el_errfile,
  881             "using dumb terminal settings.\n");
  882         Val(T_co) = 80; /* do a dumb terminal */
  883         Val(T_pt) = Val(T_km) = Val(T_li) = 0;
  884         Val(T_xt) = Val(T_MT);
  885         for (t = tstr; t->name != NULL; t++)
  886             terminal_alloc(el, t, NULL);
  887     } else {
  888         /* auto/magic margins */
  889         Val(T_am) = tgetflag("am");
  890         Val(T_xn) = tgetflag("xn");
  891         /* Can we tab */
  892         Val(T_pt) = tgetflag("pt");
  893         Val(T_xt) = tgetflag("xt");
  894         /* do we have a meta? */
  895         Val(T_km) = tgetflag("km");
  896         Val(T_MT) = tgetflag("MT");
  897         /* Get the size */
  898         Val(T_co) = tgetnum("co");
  899         Val(T_li) = tgetnum("li");
  900         for (t = tstr; t->name != NULL; t++) {
  901             /* XXX: some systems' tgetstr needs non const */
  902             terminal_alloc(el, t, tgetstr(strchr(t->name, *t->name),
  903                 &area));
  904         }
  905     }
  906 
  907     if (Val(T_co) < 2)
  908         Val(T_co) = 80; /* just in case */
  909     if (Val(T_li) < 1)
  910         Val(T_li) = 24;
  911 
  912     el->el_terminal.t_size.v = Val(T_co);
  913     el->el_terminal.t_size.h = Val(T_li);
  914 
  915     terminal_setflags(el);
  916 
  917                 /* get the correct window size */
  918     (void) terminal_get_size(el, &lins, &cols);
  919     if (terminal_change_size(el, lins, cols) == -1)
  920         return -1;
  921     (void) sigprocmask(SIG_SETMASK, &oset, NULL);
  922     terminal_bind_arrow(el);
  923     el->el_terminal.t_name = term;
  924     return i <= 0 ? -1 : 0;
  925 }
  926 
  927 
  928 /* terminal_get_size():
  929  *  Return the new window size in lines and cols, and
  930  *  true if the size was changed.
  931  */
  932 libedit_private int
  933 terminal_get_size(EditLine *el, int *lins, int *cols)
  934 {
  935 
  936     *cols = Val(T_co);
  937     *lins = Val(T_li);
  938 
  939 #ifdef TIOCGWINSZ
  940     {
  941         struct winsize ws;
  942         if (ioctl(el->el_infd, TIOCGWINSZ, &ws) != -1) {
  943             if (ws.ws_col)
  944                 *cols = ws.ws_col;
  945             if (ws.ws_row)
  946                 *lins = ws.ws_row;
  947         }
  948     }
  949 #endif
  950 #ifdef TIOCGSIZE
  951     {
  952         struct ttysize ts;
  953         if (ioctl(el->el_infd, TIOCGSIZE, &ts) != -1) {
  954             if (ts.ts_cols)
  955                 *cols = ts.ts_cols;
  956             if (ts.ts_lines)
  957                 *lins = ts.ts_lines;
  958         }
  959     }
  960 #endif
  961     return Val(T_co) != *cols || Val(T_li) != *lins;
  962 }
  963 
  964 
  965 /* terminal_change_size():
  966  *  Change the size of the terminal
  967  */
  968 libedit_private int
  969 terminal_change_size(EditLine *el, int lins, int cols)
  970 {
  971     coord_t cur = el->el_cursor;
  972     /*
  973      * Just in case
  974      */
  975     Val(T_co) = (cols < 2) ? 80 : cols;
  976     Val(T_li) = (lins < 1) ? 24 : lins;
  977 
  978     /* re-make display buffers */
  979     if (terminal_rebuffer_display(el) == -1)
  980         return -1;
  981     re_clear_display(el);
  982     el->el_cursor = cur;
  983     return 0;
  984 }
  985 
  986 
  987 /* terminal_init_arrow():
  988  *  Initialize the arrow key bindings from termcap
  989  */
  990 static void
  991 terminal_init_arrow(EditLine *el)
  992 {
  993     funckey_t *arrow = el->el_terminal.t_fkey;
  994 
  995     arrow[A_K_DN].name = L"down";
  996     arrow[A_K_DN].key = T_kd;
  997     arrow[A_K_DN].fun.cmd = ED_NEXT_HISTORY;
  998     arrow[A_K_DN].type = XK_CMD;
  999 
 1000     arrow[A_K_UP].name = L"up";
 1001     arrow[A_K_UP].key = T_ku;
 1002     arrow[A_K_UP].fun.cmd = ED_PREV_HISTORY;
 1003     arrow[A_K_UP].type = XK_CMD;
 1004 
 1005     arrow[A_K_LT].name = L"left";
 1006     arrow[A_K_LT].key = T_kl;
 1007     arrow[A_K_LT].fun.cmd = ED_PREV_CHAR;
 1008     arrow[A_K_LT].type = XK_CMD;
 1009 
 1010     arrow[A_K_RT].name = L"right";
 1011     arrow[A_K_RT].key = T_kr;
 1012     arrow[A_K_RT].fun.cmd = ED_NEXT_CHAR;
 1013     arrow[A_K_RT].type = XK_CMD;
 1014 
 1015     arrow[A_K_HO].name = L"home";
 1016     arrow[A_K_HO].key = T_kh;
 1017     arrow[A_K_HO].fun.cmd = ED_MOVE_TO_BEG;
 1018     arrow[A_K_HO].type = XK_CMD;
 1019 
 1020     arrow[A_K_EN].name = L"end";
 1021     arrow[A_K_EN].key = T_at7;
 1022     arrow[A_K_EN].fun.cmd = ED_MOVE_TO_END;
 1023     arrow[A_K_EN].type = XK_CMD;
 1024 
 1025     arrow[A_K_DE].name = L"delete";
 1026     arrow[A_K_DE].key = T_kD;
 1027     arrow[A_K_DE].fun.cmd = ED_DELETE_NEXT_CHAR;
 1028     arrow[A_K_DE].type = XK_CMD;
 1029 }
 1030 
 1031 
 1032 /* terminal_reset_arrow():
 1033  *  Reset arrow key bindings
 1034  */
 1035 static void
 1036 terminal_reset_arrow(EditLine *el)
 1037 {
 1038     funckey_t *arrow = el->el_terminal.t_fkey;
 1039     static const wchar_t strA[] = L"\033[A";
 1040     static const wchar_t strB[] = L"\033[B";
 1041     static const wchar_t strC[] = L"\033[C";
 1042     static const wchar_t strD[] = L"\033[D";
 1043     static const wchar_t strH[] = L"\033[H";
 1044     static const wchar_t strF[] = L"\033[F";
 1045     static const wchar_t stOA[] = L"\033OA";
 1046     static const wchar_t stOB[] = L"\033OB";
 1047     static const wchar_t stOC[] = L"\033OC";
 1048     static const wchar_t stOD[] = L"\033OD";
 1049     static const wchar_t stOH[] = L"\033OH";
 1050     static const wchar_t stOF[] = L"\033OF";
 1051 
 1052     keymacro_add(el, strA, &arrow[A_K_UP].fun, arrow[A_K_UP].type);
 1053     keymacro_add(el, strB, &arrow[A_K_DN].fun, arrow[A_K_DN].type);
 1054     keymacro_add(el, strC, &arrow[A_K_RT].fun, arrow[A_K_RT].type);
 1055     keymacro_add(el, strD, &arrow[A_K_LT].fun, arrow[A_K_LT].type);
 1056     keymacro_add(el, strH, &arrow[A_K_HO].fun, arrow[A_K_HO].type);
 1057     keymacro_add(el, strF, &arrow[A_K_EN].fun, arrow[A_K_EN].type);
 1058     keymacro_add(el, stOA, &arrow[A_K_UP].fun, arrow[A_K_UP].type);
 1059     keymacro_add(el, stOB, &arrow[A_K_DN].fun, arrow[A_K_DN].type);
 1060     keymacro_add(el, stOC, &arrow[A_K_RT].fun, arrow[A_K_RT].type);
 1061     keymacro_add(el, stOD, &arrow[A_K_LT].fun, arrow[A_K_LT].type);
 1062     keymacro_add(el, stOH, &arrow[A_K_HO].fun, arrow[A_K_HO].type);
 1063     keymacro_add(el, stOF, &arrow[A_K_EN].fun, arrow[A_K_EN].type);
 1064 
 1065     if (el->el_map.type != MAP_VI)
 1066         return;
 1067     keymacro_add(el, &strA[1], &arrow[A_K_UP].fun, arrow[A_K_UP].type);
 1068     keymacro_add(el, &strB[1], &arrow[A_K_DN].fun, arrow[A_K_DN].type);
 1069     keymacro_add(el, &strC[1], &arrow[A_K_RT].fun, arrow[A_K_RT].type);
 1070     keymacro_add(el, &strD[1], &arrow[A_K_LT].fun, arrow[A_K_LT].type);
 1071     keymacro_add(el, &strH[1], &arrow[A_K_HO].fun, arrow[A_K_HO].type);
 1072     keymacro_add(el, &strF[1], &arrow[A_K_EN].fun, arrow[A_K_EN].type);
 1073     keymacro_add(el, &stOA[1], &arrow[A_K_UP].fun, arrow[A_K_UP].type);
 1074     keymacro_add(el, &stOB[1], &arrow[A_K_DN].fun, arrow[A_K_DN].type);
 1075     keymacro_add(el, &stOC[1], &arrow[A_K_RT].fun, arrow[A_K_RT].type);
 1076     keymacro_add(el, &stOD[1], &arrow[A_K_LT].fun, arrow[A_K_LT].type);
 1077     keymacro_add(el, &stOH[1], &arrow[A_K_HO].fun, arrow[A_K_HO].type);
 1078     keymacro_add(el, &stOF[1], &arrow[A_K_EN].fun, arrow[A_K_EN].type);
 1079 }
 1080 
 1081 
 1082 /* terminal_set_arrow():
 1083  *  Set an arrow key binding
 1084  */
 1085 libedit_private int
 1086 terminal_set_arrow(EditLine *el, const wchar_t *name, keymacro_value_t *fun,
 1087     int type)
 1088 {
 1089     funckey_t *arrow = el->el_terminal.t_fkey;
 1090     int i;
 1091 
 1092     for (i = 0; i < A_K_NKEYS; i++)
 1093         if (wcscmp(name, arrow[i].name) == 0) {
 1094             arrow[i].fun = *fun;
 1095             arrow[i].type = type;
 1096             return 0;
 1097         }
 1098     return -1;
 1099 }
 1100 
 1101 
 1102 /* terminal_clear_arrow():
 1103  *  Clear an arrow key binding
 1104  */
 1105 libedit_private int
 1106 terminal_clear_arrow(EditLine *el, const wchar_t *name)
 1107 {
 1108     funckey_t *arrow = el->el_terminal.t_fkey;
 1109     int i;
 1110 
 1111     for (i = 0; i < A_K_NKEYS; i++)
 1112         if (wcscmp(name, arrow[i].name) == 0) {
 1113             arrow[i].type = XK_NOD;
 1114             return 0;
 1115         }
 1116     return -1;
 1117 }
 1118 
 1119 
 1120 /* terminal_print_arrow():
 1121  *  Print the arrow key bindings
 1122  */
 1123 libedit_private void
 1124 terminal_print_arrow(EditLine *el, const wchar_t *name)
 1125 {
 1126     int i;
 1127     funckey_t *arrow = el->el_terminal.t_fkey;
 1128 
 1129     for (i = 0; i < A_K_NKEYS; i++)
 1130         if (*name == '\0' || wcscmp(name, arrow[i].name) == 0)
 1131             if (arrow[i].type != XK_NOD)
 1132                 keymacro_kprint(el, arrow[i].name,
 1133                     &arrow[i].fun, arrow[i].type);
 1134 }
 1135 
 1136 
 1137 /* terminal_bind_arrow():
 1138  *  Bind the arrow keys
 1139  */
 1140 libedit_private void
 1141 terminal_bind_arrow(EditLine *el)
 1142 {
 1143     el_action_t *map;
 1144     const el_action_t *dmap;
 1145     int i, j;
 1146     char *p;
 1147     funckey_t *arrow = el->el_terminal.t_fkey;
 1148 
 1149     /* Check if the components needed are initialized */
 1150     if (el->el_terminal.t_buf == NULL || el->el_map.key == NULL)
 1151         return;
 1152 
 1153     map = el->el_map.type == MAP_VI ? el->el_map.alt : el->el_map.key;
 1154     dmap = el->el_map.type == MAP_VI ? el->el_map.vic : el->el_map.emacs;
 1155 
 1156     terminal_reset_arrow(el);
 1157 
 1158     for (i = 0; i < A_K_NKEYS; i++) {
 1159         wchar_t wt_str[VISUAL_WIDTH_MAX];
 1160         wchar_t *px;
 1161         size_t n;
 1162 
 1163         p = el->el_terminal.t_str[arrow[i].key];
 1164         if (!p || !*p)
 1165             continue;
 1166         for (n = 0; n < VISUAL_WIDTH_MAX && p[n]; ++n)
 1167             wt_str[n] = p[n];
 1168         while (n < VISUAL_WIDTH_MAX)
 1169             wt_str[n++] = '\0';
 1170         px = wt_str;
 1171         j = (unsigned char) *p;
 1172         /*
 1173          * Assign the arrow keys only if:
 1174          *
 1175          * 1. They are multi-character arrow keys and the user
 1176          *    has not re-assigned the leading character, or
 1177          *    has re-assigned the leading character to be
 1178          *    ED_SEQUENCE_LEAD_IN
 1179          * 2. They are single arrow keys pointing to an
 1180          *    unassigned key.
 1181          */
 1182         if (arrow[i].type == XK_NOD)
 1183             keymacro_clear(el, map, px);
 1184         else {
 1185             if (p[1] && (dmap[j] == map[j] ||
 1186                 map[j] == ED_SEQUENCE_LEAD_IN)) {
 1187                 keymacro_add(el, px, &arrow[i].fun,
 1188                     arrow[i].type);
 1189                 map[j] = ED_SEQUENCE_LEAD_IN;
 1190             } else if (map[j] == ED_UNASSIGNED) {
 1191                 keymacro_clear(el, map, px);
 1192                 if (arrow[i].type == XK_CMD)
 1193                     map[j] = arrow[i].fun.cmd;
 1194                 else
 1195                     keymacro_add(el, px, &arrow[i].fun,
 1196                         arrow[i].type);
 1197             }
 1198         }
 1199     }
 1200 }
 1201 
 1202 /* terminal_putc():
 1203  *  Add a character
 1204  */
 1205 static int
 1206 terminal_putc(int c)
 1207 {
 1208     if (terminal_outfile == NULL)
 1209         return -1;
 1210     return fputc(c, terminal_outfile);
 1211 }
 1212 
 1213 static void
 1214 terminal_tputs(EditLine *el, const char *cap, int affcnt)
 1215 {
 1216 #ifdef _REENTRANT
 1217     pthread_mutex_lock(&terminal_mutex);
 1218 #endif
 1219     terminal_outfile = el->el_outfile;
 1220     (void)tputs(cap, affcnt, terminal_putc);
 1221 #ifdef _REENTRANT
 1222     pthread_mutex_unlock(&terminal_mutex);
 1223 #endif
 1224 }
 1225 
 1226 /* terminal__putc():
 1227  *  Add a character
 1228  */
 1229 libedit_private int
 1230 terminal__putc(EditLine *el, wint_t c)
 1231 {
 1232     char buf[MB_LEN_MAX +1];
 1233     ssize_t i;
 1234     if (c == MB_FILL_CHAR)
 1235         return 0;
 1236     if (c & EL_LITERAL)
 1237         return fputs(literal_get(el, c), el->el_outfile);
 1238     i = ct_encode_char(buf, (size_t)MB_LEN_MAX, c);
 1239     if (i <= 0)
 1240         return (int)i;
 1241     buf[i] = '\0';
 1242     return fputs(buf, el->el_outfile);
 1243 }
 1244 
 1245 /* terminal__flush():
 1246  *  Flush output
 1247  */
 1248 libedit_private void
 1249 terminal__flush(EditLine *el)
 1250 {
 1251 
 1252     (void) fflush(el->el_outfile);
 1253 }
 1254 
 1255 /* terminal_writec():
 1256  *  Write the given character out, in a human readable form
 1257  */
 1258 libedit_private void
 1259 terminal_writec(EditLine *el, wint_t c)
 1260 {
 1261     wchar_t visbuf[VISUAL_WIDTH_MAX +1];
 1262     ssize_t vcnt = ct_visual_char(visbuf, VISUAL_WIDTH_MAX, c);
 1263     if (vcnt < 0)
 1264         vcnt = 0;
 1265     visbuf[vcnt] = '\0';
 1266     terminal_overwrite(el, visbuf, (size_t)vcnt);
 1267     terminal__flush(el);
 1268 }
 1269 
 1270 
 1271 /* terminal_telltc():
 1272  *  Print the current termcap characteristics
 1273  */
 1274 libedit_private int
 1275 /*ARGSUSED*/
 1276 terminal_telltc(EditLine *el, int argc __attribute__((__unused__)),
 1277     const wchar_t **argv __attribute__((__unused__)))
 1278 {
 1279     const struct termcapstr *t;
 1280     char **ts;
 1281 
 1282     (void) fprintf(el->el_outfile, "\n\tYour terminal has the\n");
 1283     (void) fprintf(el->el_outfile, "\tfollowing characteristics:\n\n");
 1284     (void) fprintf(el->el_outfile, "\tIt has %d columns and %d lines\n",
 1285         Val(T_co), Val(T_li));
 1286     (void) fprintf(el->el_outfile,
 1287         "\tIt has %s meta key\n", EL_HAS_META ? "a" : "no");
 1288     (void) fprintf(el->el_outfile,
 1289         "\tIt can%suse tabs\n", EL_CAN_TAB ? " " : "not ");
 1290     (void) fprintf(el->el_outfile, "\tIt %s automatic margins\n",
 1291         EL_HAS_AUTO_MARGINS ? "has" : "does not have");
 1292     if (EL_HAS_AUTO_MARGINS)
 1293         (void) fprintf(el->el_outfile, "\tIt %s magic margins\n",
 1294             EL_HAS_MAGIC_MARGINS ? "has" : "does not have");
 1295 
 1296     for (t = tstr, ts = el->el_terminal.t_str; t->name != NULL; t++, ts++) {
 1297         const char *ub;
 1298         if (*ts && **ts) {
 1299             ub = ct_encode_string(ct_visual_string(
 1300                 ct_decode_string(*ts, &el->el_scratch),
 1301                 &el->el_visual), &el->el_scratch);
 1302         } else {
 1303             ub = "(empty)";
 1304         }
 1305         (void) fprintf(el->el_outfile, "\t%25s (%s) == %s\n",
 1306             t->long_name, t->name, ub);
 1307     }
 1308     (void) fputc('\n', el->el_outfile);
 1309     return 0;
 1310 }
 1311 
 1312 
 1313 /* terminal_settc():
 1314  *  Change the current terminal characteristics
 1315  */
 1316 libedit_private int
 1317 /*ARGSUSED*/
 1318 terminal_settc(EditLine *el, int argc __attribute__((__unused__)),
 1319     const wchar_t **argv)
 1320 {
 1321     const struct termcapstr *ts;
 1322     const struct termcapval *tv;
 1323     char what[8], how[8];
 1324 
 1325     if (argv == NULL || argv[1] == NULL || argv[2] == NULL)
 1326         return -1;
 1327 
 1328     strlcpy(what, ct_encode_string(argv[1], &el->el_scratch), sizeof(what));
 1329     strlcpy(how,  ct_encode_string(argv[2], &el->el_scratch), sizeof(how));
 1330 
 1331     /*
 1332          * Do the strings first
 1333          */
 1334     for (ts = tstr; ts->name != NULL; ts++)
 1335         if (strcmp(ts->name, what) == 0)
 1336             break;
 1337 
 1338     if (ts->name != NULL) {
 1339         terminal_alloc(el, ts, how);
 1340         terminal_setflags(el);
 1341         return 0;
 1342     }
 1343     /*
 1344          * Do the numeric ones second
 1345          */
 1346     for (tv = tval; tv->name != NULL; tv++)
 1347         if (strcmp(tv->name, what) == 0)
 1348             break;
 1349 
 1350     if (tv->name != NULL)
 1351         return -1;
 1352 
 1353     if (tv == &tval[T_pt] || tv == &tval[T_km] ||
 1354         tv == &tval[T_am] || tv == &tval[T_xn]) {
 1355         if (strcmp(how, "yes") == 0)
 1356             el->el_terminal.t_val[tv - tval] = 1;
 1357         else if (strcmp(how, "no") == 0)
 1358             el->el_terminal.t_val[tv - tval] = 0;
 1359         else {
 1360             (void) fprintf(el->el_errfile,
 1361                 "%ls: Bad value `%s'.\n", argv[0], how);
 1362             return -1;
 1363         }
 1364         terminal_setflags(el);
 1365         if (terminal_change_size(el, Val(T_li), Val(T_co)) == -1)
 1366             return -1;
 1367         return 0;
 1368     } else {
 1369         long i;
 1370         char *ep;
 1371 
 1372         i = strtol(how, &ep, 10);
 1373         if (*ep != '\0') {
 1374             (void) fprintf(el->el_errfile,
 1375                 "%ls: Bad value `%s'.\n", argv[0], how);
 1376             return -1;
 1377         }
 1378         el->el_terminal.t_val[tv - tval] = (int) i;
 1379         el->el_terminal.t_size.v = Val(T_co);
 1380         el->el_terminal.t_size.h = Val(T_li);
 1381         if (tv == &tval[T_co] || tv == &tval[T_li])
 1382             if (terminal_change_size(el, Val(T_li), Val(T_co))
 1383                 == -1)
 1384                 return -1;
 1385         return 0;
 1386     }
 1387 }
 1388 
 1389 
 1390 /* terminal_gettc():
 1391  *  Get the current terminal characteristics
 1392  */
 1393 libedit_private int
 1394 /*ARGSUSED*/
 1395 terminal_gettc(EditLine *el, int argc __attribute__((__unused__)), char **argv)
 1396 {
 1397     const struct termcapstr *ts;
 1398     const struct termcapval *tv;
 1399     char *what;
 1400     void *how;
 1401 
 1402     if (argv == NULL || argv[1] == NULL || argv[2] == NULL)
 1403         return -1;
 1404 
 1405     what = argv[1];
 1406     how = argv[2];
 1407 
 1408     /*
 1409          * Do the strings first
 1410          */
 1411     for (ts = tstr; ts->name != NULL; ts++)
 1412         if (strcmp(ts->name, what) == 0)
 1413             break;
 1414 
 1415     if (ts->name != NULL) {
 1416         *(char **)how = el->el_terminal.t_str[ts - tstr];
 1417         return 0;
 1418     }
 1419     /*
 1420          * Do the numeric ones second
 1421          */
 1422     for (tv = tval; tv->name != NULL; tv++)
 1423         if (strcmp(tv->name, what) == 0)
 1424             break;
 1425 
 1426     if (tv->name == NULL)
 1427         return -1;
 1428 
 1429     if (tv == &tval[T_pt] || tv == &tval[T_km] ||
 1430         tv == &tval[T_am] || tv == &tval[T_xn]) {
 1431         static char yes[] = "yes";
 1432         static char no[] = "no";
 1433         if (el->el_terminal.t_val[tv - tval])
 1434             *(char **)how = yes;
 1435         else
 1436             *(char **)how = no;
 1437         return 0;
 1438     } else {
 1439         *(int *)how = el->el_terminal.t_val[tv - tval];
 1440         return 0;
 1441     }
 1442 }
 1443 
 1444 /* terminal_echotc():
 1445  *  Print the termcap string out with variable substitution
 1446  */
 1447 libedit_private int
 1448 /*ARGSUSED*/
 1449 terminal_echotc(EditLine *el, int argc __attribute__((__unused__)),
 1450     const wchar_t **argv)
 1451 {
 1452     char *cap, *scap;
 1453     wchar_t *ep;
 1454     int arg_need, arg_cols, arg_rows;
 1455     int verbose = 0, silent = 0;
 1456     char *area;
 1457     static const char fmts[] = "%s\n", fmtd[] = "%d\n";
 1458     const struct termcapstr *t;
 1459     char buf[TC_BUFSIZE];
 1460     long i;
 1461 
 1462     area = buf;
 1463 
 1464     if (argv == NULL || argv[1] == NULL)
 1465         return -1;
 1466     argv++;
 1467 
 1468     if (argv[0][0] == '-') {
 1469         switch (argv[0][1]) {
 1470         case 'v':
 1471             verbose = 1;
 1472             break;
 1473         case 's':
 1474             silent = 1;
 1475             break;
 1476         default:
 1477             /* stderror(ERR_NAME | ERR_TCUSAGE); */
 1478             break;
 1479         }
 1480         argv++;
 1481     }
 1482     if (!*argv || *argv[0] == '\0')
 1483         return 0;
 1484     if (wcscmp(*argv, L"tabs") == 0) {
 1485         (void) fprintf(el->el_outfile, fmts, EL_CAN_TAB ? "yes" : "no");
 1486         return 0;
 1487     } else if (wcscmp(*argv, L"meta") == 0) {
 1488         (void) fprintf(el->el_outfile, fmts, Val(T_km) ? "yes" : "no");
 1489         return 0;
 1490     } else if (wcscmp(*argv, L"xn") == 0) {
 1491         (void) fprintf(el->el_outfile, fmts, EL_HAS_MAGIC_MARGINS ?
 1492             "yes" : "no");
 1493         return 0;
 1494     } else if (wcscmp(*argv, L"am") == 0) {
 1495         (void) fprintf(el->el_outfile, fmts, EL_HAS_AUTO_MARGINS ?
 1496             "yes" : "no");
 1497         return 0;
 1498     } else if (wcscmp(*argv, L"baud") == 0) {
 1499         (void) fprintf(el->el_outfile, fmtd, (int)el->el_tty.t_speed);
 1500         return 0;
 1501     } else if (wcscmp(*argv, L"rows") == 0 ||
 1502                    wcscmp(*argv, L"lines") == 0) {
 1503         (void) fprintf(el->el_outfile, fmtd, Val(T_li));
 1504         return 0;
 1505     } else if (wcscmp(*argv, L"cols") == 0) {
 1506         (void) fprintf(el->el_outfile, fmtd, Val(T_co));
 1507         return 0;
 1508     }
 1509     /*
 1510          * Try to use our local definition first
 1511          */
 1512     scap = NULL;
 1513     for (t = tstr; t->name != NULL; t++)
 1514         if (strcmp(t->name,
 1515             ct_encode_string(*argv, &el->el_scratch)) == 0) {
 1516             scap = el->el_terminal.t_str[t - tstr];
 1517             break;
 1518         }
 1519     if (t->name == NULL) {
 1520         /* XXX: some systems' tgetstr needs non const */
 1521                 scap = tgetstr(ct_encode_string(*argv, &el->el_scratch), &area);
 1522     }
 1523     if (!scap || scap[0] == '\0') {
 1524         if (!silent)
 1525             (void) fprintf(el->el_errfile,
 1526                 "echotc: Termcap parameter `%ls' not found.\n",
 1527                 *argv);
 1528         return -1;
 1529     }
 1530     /*
 1531          * Count home many values we need for this capability.
 1532          */
 1533     for (cap = scap, arg_need = 0; *cap; cap++)
 1534         if (*cap == '%')
 1535             switch (*++cap) {
 1536             case 'd':
 1537             case '2':
 1538             case '3':
 1539             case '.':
 1540             case '+':
 1541                 arg_need++;
 1542                 break;
 1543             case '%':
 1544             case '>':
 1545             case 'i':
 1546             case 'r':
 1547             case 'n':
 1548             case 'B':
 1549             case 'D':
 1550                 break;
 1551             default:
 1552                 /*
 1553                  * hpux has lot's of them...
 1554                  */
 1555                 if (verbose)
 1556                     (void) fprintf(el->el_errfile,
 1557                 "echotc: Warning: unknown termcap %% `%c'.\n",
 1558                         *cap);
 1559                 /* This is bad, but I won't complain */
 1560                 break;
 1561             }
 1562 
 1563     switch (arg_need) {
 1564     case 0:
 1565         argv++;
 1566         if (*argv && *argv[0]) {
 1567             if (!silent)
 1568                 (void) fprintf(el->el_errfile,
 1569                     "echotc: Warning: Extra argument `%ls'.\n",
 1570                     *argv);
 1571             return -1;
 1572         }
 1573         terminal_tputs(el, scap, 1);
 1574         break;
 1575     case 1:
 1576         argv++;
 1577         if (!*argv || *argv[0] == '\0') {
 1578             if (!silent)
 1579                 (void) fprintf(el->el_errfile,
 1580                     "echotc: Warning: Missing argument.\n");
 1581             return -1;
 1582         }
 1583         arg_cols = 0;
 1584         i = wcstol(*argv, &ep, 10);
 1585         if (*ep != '\0' || i < 0) {
 1586             if (!silent)
 1587                 (void) fprintf(el->el_errfile,
 1588                     "echotc: Bad value `%ls' for rows.\n",
 1589                     *argv);
 1590             return -1;
 1591         }
 1592         arg_rows = (int) i;
 1593         argv++;
 1594         if (*argv && *argv[0]) {
 1595             if (!silent)
 1596                 (void) fprintf(el->el_errfile,
 1597                     "echotc: Warning: Extra argument `%ls"
 1598                     "'.\n", *argv);
 1599             return -1;
 1600         }
 1601         terminal_tputs(el, tgoto(scap, arg_cols, arg_rows), 1);
 1602         break;
 1603     default:
 1604         /* This is wrong, but I will ignore it... */
 1605         if (verbose)
 1606             (void) fprintf(el->el_errfile,
 1607              "echotc: Warning: Too many required arguments (%d).\n",
 1608                 arg_need);
 1609         /* FALLTHROUGH */
 1610     case 2:
 1611         argv++;
 1612         if (!*argv || *argv[0] == '\0') {
 1613             if (!silent)
 1614                 (void) fprintf(el->el_errfile,
 1615                     "echotc: Warning: Missing argument.\n");
 1616             return -1;
 1617         }
 1618         i = wcstol(*argv, &ep, 10);
 1619         if (*ep != '\0' || i < 0) {
 1620             if (!silent)
 1621                 (void) fprintf(el->el_errfile,
 1622                     "echotc: Bad value `%ls' for cols.\n",
 1623                     *argv);
 1624             return -1;
 1625         }
 1626         arg_cols = (int) i;
 1627         argv++;
 1628         if (!*argv || *argv[0] == '\0') {
 1629             if (!silent)
 1630                 (void) fprintf(el->el_errfile,
 1631                     "echotc: Warning: Missing argument.\n");
 1632             return -1;
 1633         }
 1634         i = wcstol(*argv, &ep, 10);
 1635         if (*ep != '\0' || i < 0) {
 1636             if (!silent)
 1637                 (void) fprintf(el->el_errfile,
 1638                     "echotc: Bad value `%ls' for rows.\n",
 1639                     *argv);
 1640             return -1;
 1641         }
 1642         arg_rows = (int) i;
 1643         if (*ep != '\0') {
 1644             if (!silent)
 1645                 (void) fprintf(el->el_errfile,
 1646                     "echotc: Bad value `%ls'.\n", *argv);
 1647             return -1;
 1648         }
 1649         argv++;
 1650         if (*argv && *argv[0]) {
 1651             if (!silent)
 1652                 (void) fprintf(el->el_errfile,
 1653                     "echotc: Warning: Extra argument `%ls"
 1654                     "'.\n", *argv);
 1655             return -1;
 1656         }
 1657         terminal_tputs(el, tgoto(scap, arg_cols, arg_rows), arg_rows);
 1658         break;
 1659     }
 1660     return 0;
 1661 }