"Fossies" - the Fresh Open Source Software Archive

Member "s-nail-14.9.11/termcap.c" (8 Aug 2018, 28879 Bytes) of package /linux/misc/s-nail-14.9.11.tar.xz:


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 "termcap.c" see the Fossies "Dox" file reference documentation and the latest Fossies "Diffs" side-by-side code changes report: 14.9.10_vs_14.9.11.

    1 /*@ S-nail - a mail user agent derived from Berkeley Mail.
    2  *@ Terminal capability interaction.
    3  *@ For encapsulation purposes provide a basic foundation even without
    4  *@ HAVE_TERMCAP, but with config.h:n_HAVE_TCAP.
    5  *@ HOWTO add a new non-dynamic command or query:
    6  *@ - add an entry to nail.h:enum n_termcap_{cmd,query}
    7  *@ - run make-tcap-map.pl
    8  *@ - update the *termcap* member documentation on changes!
    9  *@ Bug: in case of clashes of two-letter names terminfo(5) wins.
   10  *
   11  * Copyright (c) 2016 - 2018 Steffen (Daode) Nurpmeso <steffen@sdaoden.eu>.
   12  * SPDX-License-Identifier: ISC
   13  *
   14  * Permission to use, copy, modify, and/or distribute this software for any
   15  * purpose with or without fee is hereby granted, provided that the above
   16  * copyright notice and this permission notice appear in all copies.
   17  *
   18  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
   19  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
   20  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
   21  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
   22  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
   23  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
   24  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
   25  */
   26 #undef n_FILE
   27 #define n_FILE termcap
   28 
   29 #ifndef HAVE_AMALGAMATION
   30 # include "nail.h"
   31 #endif
   32 
   33 EMPTY_FILE()
   34 #ifdef n_HAVE_TCAP
   35 /* If available, curses.h must be included before term.h! */
   36 #ifdef HAVE_TERMCAP
   37 # ifdef HAVE_TERMCAP_CURSES
   38 #  include <curses.h>
   39 # endif
   40 # include <term.h>
   41 #endif
   42 
   43 /*
   44  * xxx We are not really compatible with very old and strange terminals since
   45  * we don't care at all for circumstances indicated by terminal flags: if we
   46  * find a capability we use it and assume it works.  E.g., if "Co" indicates
   47  * colours we simply use ISO 6429 also for font attributes etc.  That is,
   48  * we don't use the ncurses/terminfo interface with all its internal logic.
   49  */
   50 
   51 /* Unless HAVE_TERMINFO or HAVE_TGETENT_NULL_BUF are defined we will use this
   52  * to space the buffer we pass through to tgetent(3).
   53  * Since for (such) elder non-emulated terminals really weird things will
   54  * happen if an entry would require more than 1024 bytes, don't really mind.
   55  * Use a ui16_t for storage */
   56 #define a_TERMCAP_ENTRYSIZE_MAX ((2668 + 128) & ~127) /* As of ncurses 6.0 */
   57 
   58 n_CTA(a_TERMCAP_ENTRYSIZE_MAX < UI16_MAX,
   59    "Chosen buffer size exceeds datatype capability");
   60 
   61 /* For simplicity we store commands and queries in single continuous control
   62  * and entry structure arrays: to index queries one has to add
   63  * n__TERMCAP_CMD_MAX1 first!  And don't confound with ENTRYSIZE_MAX! */
   64 enum{
   65    a_TERMCAP_ENT_MAX1 = n__TERMCAP_CMD_MAX1 + n__TERMCAP_QUERY_MAX1
   66 };
   67 
   68 enum a_termcap_flags{
   69    a_TERMCAP_F_NONE,
   70    /* enum n_termcap_captype values stored here.
   71     * Note that presence of a type in an a_termcap_ent signals initialization */
   72    a_TERMCAP_F_TYPE_MASK = (1<<4) - 1,
   73 
   74    a_TERMCAP_F_QUERY = 1<<4,     /* A query rather than a command */
   75    a_TERMCAP_F_DISABLED = 1<<5,  /* User explicitly disabled command/query */
   76    a_TERMCAP_F_ALTERN = 1<<6,    /* Not available, but has alternative */
   77    a_TERMCAP_F_NOENT = 1<<7,     /* Not available */
   78 
   79    /* _cmd() argument interpretion (_T_STR) */
   80    a_TERMCAP_F_ARG_IDX1 = 1<<11, /* Argument 1 used, and is an index */
   81    a_TERMCAP_F_ARG_IDX2 = 1<<12,
   82    a_TERMCAP_F_ARG_CNT = 1<<13,  /* .., and is a count */
   83 
   84    a_TERMCAP_F__LAST = a_TERMCAP_F_ARG_CNT
   85 };
   86 n_CTA((ui32_t)n__TERMCAP_CAPTYPE_MAX1 <= (ui32_t)a_TERMCAP_F_TYPE_MASK,
   87    "enum n_termcap_captype exceeds bit range of a_termcap_flags");
   88 
   89 struct a_termcap_control{
   90    ui16_t tc_flags;
   91    /* Offset base into a_termcap_namedat[], which stores the two-letter
   92     * termcap(5) name directly followed by a NUL terminated terminfo(5) name.
   93     * A termcap(5) name may consist of two NULs meaning ERR_NOENT,
   94     * a terminfo(5) name may be empty for the same purpose */
   95    ui16_t tc_off;
   96 };
   97 n_CTA(a_TERMCAP_F__LAST <= UI16_MAX,
   98    "a_termcap_flags exceed storage datatype in a_termcap_control");
   99 
  100 struct a_termcap_ent{
  101    ui16_t te_flags;
  102    ui16_t te_off;    /* in a_termcap_g->tg_dat / value for T_BOOL and T_NUM */
  103 };
  104 n_CTA(a_TERMCAP_F__LAST <= UI16_MAX,
  105    "a_termcap_flags exceed storage datatype in a_termcap_ent");
  106 
  107 /* Structure for extended queries, which don't have an entry constant in
  108  * n_termcap_query (to allow free query/binding of keycodes) */
  109 struct a_termcap_ext_ent{
  110    struct a_termcap_ent tee_super;
  111    ui8_t tee__dummy[4];
  112    struct a_termcap_ext_ent *tee_next;
  113    /* Resolvable termcap(5)/terminfo(5) name as given by user; the actual data
  114     * is stored just like for normal queries */
  115    char tee_name[n_VFIELD_SIZE(0)];
  116 };
  117 
  118 struct a_termcap_g{
  119    struct a_termcap_ext_ent *tg_ext_ents; /* List of extended queries */
  120    struct a_termcap_ent tg_ents[a_TERMCAP_ENT_MAX1];
  121    struct n_string tg_dat;                /* Storage for resolved caps */
  122 # if !defined HAVE_TGETENT_NULL_BUF && !defined HAVE_TERMINFO
  123    char tg_lib_buf[a_TERMCAP_ENTRYSIZE_MAX];
  124 # endif
  125 };
  126 
  127 /* Include the constant make-tcap-map.pl output */
  128 #include <gen-tcaps.h>
  129 n_CTA(sizeof a_termcap_namedat <= UI16_MAX,
  130    "Termcap command and query name data exceed storage datatype");
  131 n_CTA(a_TERMCAP_ENT_MAX1 == n_NELEM(a_termcap_control),
  132    "Control array does not match command/query array to be controlled");
  133 
  134 static struct a_termcap_g *a_termcap_g;
  135 
  136 /* Query *termcap*, parse it and incorporate into a_termcap_g */
  137 static void a_termcap_init_var(struct str const *termvar);
  138 
  139 /* Expand ^CNTRL, \[Ee] and \OCT.  False for parse error and empty results */
  140 static bool_t a_termcap__strexp(struct n_string *store, char const *ibuf);
  141 
  142 /* Initialize any _ent for which we have _F_ALTERN and which isn't yet set */
  143 static void a_termcap_init_altern(void);
  144 
  145 #ifdef HAVE_TERMCAP
  146 /* Setup the library we use to work with term */
  147 static bool_t a_termcap_load(char const *term);
  148 
  149 /* Query the capability tcp and fill in tep (upon success) */
  150 static bool_t a_termcap_ent_query(struct a_termcap_ent *tep,
  151                char const *cname, ui16_t cflags);
  152 n_INLINE bool_t a_termcap_ent_query_tcp(struct a_termcap_ent *tep,
  153                   struct a_termcap_control const *tcp);
  154 
  155 /* Output PTF for both, termcap(5) and terminfo(5) */
  156 static int a_termcap_putc(int c);
  157 #endif
  158 
  159 /* Get n_termcap_cmd or n_termcap_query constant belonging to (nlen bytes of)
  160  * name, or -1 if not found.  min and max have to be used to cramp the result */
  161 static si32_t a_termcap_enum_for_name(char const *name, size_t nlen,
  162                si32_t min, si32_t max);
  163 #define a_termcap_cmd_for_name(NB,NL) \
  164    a_termcap_enum_for_name(NB, NL, 0, n__TERMCAP_CMD_MAX1)
  165 #define a_termcap_query_for_name(NB,NL) \
  166    a_termcap_enum_for_name(NB, NL, n__TERMCAP_CMD_MAX1, a_TERMCAP_ENT_MAX1)
  167 
  168 static void
  169 a_termcap_init_var(struct str const *termvar){
  170    char *cbp_base, *cbp;
  171    size_t i;
  172    char const *ccp;
  173    NYD2_ENTER;
  174 
  175    if(termvar->l >= UI16_MAX){
  176       n_err(_("*termcap*: length excesses internal limit, skipping\n"));
  177       goto j_leave;
  178    }
  179 
  180    assert(termvar->s[termvar->l] == '\0');
  181    i = termvar->l +1;
  182    cbp_base = n_autorec_alloc(i);
  183    memcpy(cbp = cbp_base, termvar->s, i);
  184 
  185    for(; (ccp = n_strsep(&cbp, ',', TRU1)) != NULL;){
  186       struct a_termcap_ent *tep;
  187       size_t kl;
  188       char const *v;
  189       ui16_t f;
  190 
  191       /* Separate key/value, if any */
  192       if(/* no empties ccp[0] == '\0' ||*/ ccp[1] == '\0'){
  193 jeinvent:
  194          n_err(_("*termcap*: invalid entry: %s\n"), ccp);
  195          continue;
  196       }
  197       for(kl = 2, v = &ccp[2];; ++kl, ++v){
  198          char c = *v;
  199 
  200          if(c == '\0'){
  201             f = n_TERMCAP_CAPTYPE_BOOL;
  202             break;
  203          }else if(c == '#'){
  204             f = n_TERMCAP_CAPTYPE_NUMERIC;
  205             ++v;
  206             break;
  207          }else if(c == '='){
  208             f = n_TERMCAP_CAPTYPE_STRING;
  209             ++v;
  210             break;
  211          }
  212       }
  213 
  214       /* Do we know about this one? */
  215       /* C99 */{
  216          struct a_termcap_control const *tcp;
  217          si32_t tci;
  218 
  219          tci = a_termcap_enum_for_name(ccp, kl, 0, a_TERMCAP_ENT_MAX1);
  220          if(tci < 0){
  221             /* For key binding purposes, save any given string */
  222 #ifdef HAVE_KEY_BINDINGS
  223             if((f & a_TERMCAP_F_TYPE_MASK) == n_TERMCAP_CAPTYPE_STRING){
  224                struct a_termcap_ext_ent *teep;
  225 
  226                teep = n_alloc(n_VSTRUCT_SIZEOF(struct a_termcap_ext_ent,
  227                      tee_name) + kl +1);
  228                teep->tee_next = a_termcap_g->tg_ext_ents;
  229                a_termcap_g->tg_ext_ents = teep;
  230                memcpy(teep->tee_name, ccp, kl);
  231                teep->tee_name[kl] = '\0';
  232 
  233                tep = &teep->tee_super;
  234                tep->te_flags = n_TERMCAP_CAPTYPE_STRING | a_TERMCAP_F_QUERY;
  235                tep->te_off = (ui16_t)a_termcap_g->tg_dat.s_len;
  236                if(!a_termcap__strexp(&a_termcap_g->tg_dat, v))
  237                   tep->te_flags |= a_TERMCAP_F_DISABLED;
  238                goto jlearned;
  239             }else
  240 #endif /* HAVE_KEY_BINDINGS */
  241                   if(n_poption & n_PO_D_V)
  242                n_err(_("*termcap*: unknown capability: %s\n"), ccp);
  243             continue;
  244          }
  245          i = (size_t)tci;
  246 
  247          tcp = &a_termcap_control[i];
  248          if((tcp->tc_flags & a_TERMCAP_F_TYPE_MASK) != f){
  249             n_err(_("*termcap*: entry type mismatch: %s\n"), ccp);
  250             break;
  251          }
  252          tep = &a_termcap_g->tg_ents[i];
  253          tep->te_flags = tcp->tc_flags;
  254          tep->te_off = (ui16_t)a_termcap_g->tg_dat.s_len;
  255       }
  256 
  257       if((f & a_TERMCAP_F_TYPE_MASK) == n_TERMCAP_CAPTYPE_BOOL)
  258          ;
  259       else if(*v == '\0')
  260          tep->te_flags |= a_TERMCAP_F_DISABLED;
  261       else if((f & a_TERMCAP_F_TYPE_MASK) == n_TERMCAP_CAPTYPE_NUMERIC){
  262          if((n_idec_ui16_cp(&tep->te_off, v, 0, NULL
  263                   ) & (n_IDEC_STATE_EMASK | n_IDEC_STATE_CONSUMED)
  264                ) != n_IDEC_STATE_CONSUMED)
  265             goto jeinvent;
  266       }else if(!a_termcap__strexp(&a_termcap_g->tg_dat, v))
  267          tep->te_flags |= a_TERMCAP_F_DISABLED;
  268 #ifdef HAVE_KEY_BINDINGS
  269 jlearned:
  270 #endif
  271       if(n_poption & n_PO_D_V)
  272          n_err(_("*termcap*: learned %.*s: %s\n"), (int)kl, ccp,
  273             (tep->te_flags & a_TERMCAP_F_DISABLED ? "<disabled>"
  274              : (f & a_TERMCAP_F_TYPE_MASK) == n_TERMCAP_CAPTYPE_BOOL ? "true"
  275                : v));
  276    }
  277    DBG( if(n_poption & n_PO_D_V) n_err("*termcap* parsed: buffer used=%lu\n",
  278       (ul_i)a_termcap_g->tg_dat.s_len) );
  279 
  280    /* Catch some inter-dependencies the user may have triggered */
  281 #ifdef HAVE_TERMCAP
  282    if(a_termcap_g->tg_ents[n_TERMCAP_CMD_te].te_flags & a_TERMCAP_F_DISABLED)
  283       a_termcap_g->tg_ents[n_TERMCAP_CMD_ti].te_flags = a_TERMCAP_F_DISABLED;
  284    else if(a_termcap_g->tg_ents[n_TERMCAP_CMD_ti].te_flags &
  285          a_TERMCAP_F_DISABLED)
  286       a_termcap_g->tg_ents[n_TERMCAP_CMD_te].te_flags = a_TERMCAP_F_DISABLED;
  287 #endif
  288 
  289 j_leave:
  290    NYD2_LEAVE;
  291 }
  292 
  293 static bool_t
  294 a_termcap__strexp(struct n_string *store, char const *ibuf){ /* XXX ASCII */
  295    char c;
  296    char const *oibuf;
  297    size_t olen;
  298    NYD2_ENTER;
  299 
  300    olen = store->s_len;
  301 
  302    for(oibuf = ibuf; (c = *ibuf) != '\0';){
  303       if(c == '\\'){
  304          if((c = ibuf[1]) == '\0')
  305             goto jebsseq;
  306 
  307          if(c == 'E'){
  308             c = '\033';
  309             ibuf += 2;
  310             goto jpush;
  311          }
  312 
  313          if(octalchar(c)){
  314             char c2, c3;
  315 
  316             if((c2 = ibuf[2]) == '\0' || !octalchar(c2) ||
  317                   (c3 = ibuf[3]) == '\0' || !octalchar(c3)){
  318                n_err(_("*termcap*: invalid octal sequence: %s\n"), oibuf);
  319                goto jerr;
  320             }
  321             c -= '0', c2 -= '0', c3 -= '0';
  322             c <<= 3, c |= c2;
  323             if((ui8_t)c > 0x1F){
  324                n_err(_("*termcap*: octal number too large: %s\n"), oibuf);
  325                goto jerr;
  326             }
  327             c <<= 3, c |= c3;
  328             ibuf += 4;
  329             goto jpush;
  330          }
  331 jebsseq:
  332          n_err(_("*termcap*: invalid reverse solidus \\ sequence: %s\n"),oibuf);
  333          goto jerr;
  334       }else if(c == '^'){
  335          if((c = ibuf[1]) == '\0'){
  336             n_err(_("*termcap*: incomplete ^CNTRL sequence: %s\n"), oibuf);
  337             goto jerr;
  338          }
  339          c = upperconv(c) ^ 0x40;
  340          if((ui8_t)c > 0x1F && c != 0x7F){ /* ASCII C0: 0..1F, 7F */
  341             n_err(_("*termcap*: invalid ^CNTRL sequence: %s\n"), oibuf);
  342             goto jerr;
  343          }
  344          ibuf += 2;
  345       }else
  346          ++ibuf;
  347 
  348 jpush:
  349       store = n_string_push_c(store, c);
  350    }
  351 
  352    c = (store->s_len != olen) ? '\1' : '\0';
  353 jleave:
  354    n_string_push_c(store, '\0');
  355    NYD2_LEAVE;
  356    return (c != '\0');
  357 jerr:
  358    store = n_string_trunc(store, olen);
  359    c = '\0';
  360    goto jleave;
  361 }
  362 
  363 static void
  364 a_termcap_init_altern(void){
  365    /* We silently ignore user _F_DISABLED requests for those entries for which
  366     * we have fallback entries, and which we need to ensure proper functioning.
  367     * I.e., this allows users to explicitly disable some termcap(5) capability
  368     * and enforce usage of the built-in fallback */
  369    /* xxx Use table-based approach for fallback strategies */
  370 #define a_OK(CMD) a_OOK(&a_termcap_g->tg_ents[CMD])
  371 #define a_OOK(TEP) \
  372    ((TEP)->te_flags != 0 && !((TEP)->te_flags & a_TERMCAP_F_NOENT))
  373 #define a_SET(TEP,CMD,ALT) \
  374    (TEP)->te_flags = a_termcap_control[CMD].tc_flags |\
  375       ((ALT) ? a_TERMCAP_F_ALTERN : 0)
  376 
  377    struct a_termcap_ent *tep;
  378    NYD2_ENTER;
  379    n_UNUSED(tep);
  380 
  381    /* For simplicity in the rest of this file null flags of disabled commands,
  382     * as we won't check and try to lazy query any command */
  383    /* C99 */{
  384       size_t i;
  385 
  386       for(i = n__TERMCAP_CMD_MAX1;;){
  387          if(i-- == 0)
  388             break;
  389          if((tep = &a_termcap_g->tg_ents[i])->te_flags & a_TERMCAP_F_DISABLED)
  390             tep->te_flags = 0;
  391       }
  392    }
  393 
  394 #ifdef HAVE_MLE
  395    /* ce == ch + [:SPACE:] (start column specified by argument) */
  396    tep = &a_termcap_g->tg_ents[n_TERMCAP_CMD_ce];
  397    if(!a_OOK(tep))
  398       a_SET(tep, n_TERMCAP_CMD_ce, TRU1);
  399 
  400    /* ch == cr[\r] + nd[:\033C:] */
  401    tep = &a_termcap_g->tg_ents[n_TERMCAP_CMD_ch];
  402    if(!a_OOK(tep))
  403       a_SET(tep, n_TERMCAP_CMD_ch, TRU1);
  404 
  405    /* cr == \r */
  406    tep = &a_termcap_g->tg_ents[n_TERMCAP_CMD_cr];
  407    if(!a_OOK(tep)){
  408       a_SET(tep, n_TERMCAP_CMD_cr, FAL0);
  409       tep->te_off = (ui16_t)a_termcap_g->tg_dat.s_len;
  410       n_string_push_c(n_string_push_c(&a_termcap_g->tg_dat, '\r'), '\0');
  411    }
  412 
  413    /* le == \b */
  414    tep = &a_termcap_g->tg_ents[n_TERMCAP_CMD_le];
  415    if(!a_OOK(tep)){
  416       a_SET(tep, n_TERMCAP_CMD_le, FAL0);
  417       tep->te_off = (ui16_t)a_termcap_g->tg_dat.s_len;
  418       n_string_push_c(n_string_push_c(&a_termcap_g->tg_dat, '\b'), '\0');
  419    }
  420 
  421    /* nd == \033[C (we may not fail, anyway, so use xterm sequence default) */
  422    tep = &a_termcap_g->tg_ents[n_TERMCAP_CMD_nd];
  423    if(!a_OOK(tep)){
  424       a_SET(tep, n_TERMCAP_CMD_nd, FAL0);
  425       tep->te_off = (ui16_t)a_termcap_g->tg_dat.s_len;
  426       n_string_push_buf(&a_termcap_g->tg_dat, "\033[C", sizeof("\033[C"));
  427    }
  428 
  429 # ifdef HAVE_TERMCAP
  430    /* cl == ho+cd */
  431    tep = &a_termcap_g->tg_ents[n_TERMCAP_CMD_cl];
  432    if(!a_OOK(tep)){
  433       if(a_OK(n_TERMCAP_CMD_cd) && a_OK(n_TERMCAP_CMD_ho))
  434          a_SET(tep, n_TERMCAP_CMD_cl, TRU1);
  435    }
  436 # endif
  437 #endif /* HAVE_MLE */
  438 
  439    NYD2_LEAVE;
  440 #undef a_OK
  441 #undef a_OOK
  442 #undef a_SET
  443 }
  444 
  445 #ifdef HAVE_TERMCAP
  446 # ifdef HAVE_TERMINFO
  447 static bool_t
  448 a_termcap_load(char const *term){
  449    bool_t rv;
  450    int err;
  451    NYD2_ENTER;
  452 
  453    if(!(rv = (setupterm(term, fileno(n_tty_fp), &err) == OK)))
  454       n_err(_("Unknown ${TERM}inal, using only *termcap*: %s\n"), term);
  455    NYD2_LEAVE;
  456    return rv;
  457 }
  458 
  459 static bool_t
  460 a_termcap_ent_query(struct a_termcap_ent *tep,
  461       char const *cname, ui16_t cflags){
  462    bool_t rv;
  463    NYD2_ENTER;
  464    assert(!(n_psonce & n_PSO_TERMCAP_DISABLE));
  465 
  466    if(n_UNLIKELY(*cname == '\0'))
  467       rv = FAL0;
  468    else switch((tep->te_flags = cflags) & a_TERMCAP_F_TYPE_MASK){
  469    case n_TERMCAP_CAPTYPE_BOOL:
  470       tep->te_off = (tigetflag(cname) > 0);
  471       rv = TRU1;
  472       break;
  473    case n_TERMCAP_CAPTYPE_NUMERIC:{
  474       int r = tigetnum(cname);
  475 
  476       if((rv = (r >= 0)))
  477          tep->te_off = (ui16_t)n_MIN(UI16_MAX, r);
  478       else
  479          tep->te_flags |= a_TERMCAP_F_NOENT;
  480       }break;
  481    default:
  482    case n_TERMCAP_CAPTYPE_STRING:{
  483       char *cp;
  484 
  485       cp = tigetstr(cname);
  486       if((rv = (cp != NULL && cp != (char*)-1))){
  487          tep->te_off = (ui16_t)a_termcap_g->tg_dat.s_len;
  488          n_string_push_buf(&a_termcap_g->tg_dat, cp, strlen(cp) +1);
  489       }else
  490          tep->te_flags |= a_TERMCAP_F_NOENT;
  491       }break;
  492    }
  493    NYD2_LEAVE;
  494    return rv;
  495 }
  496 
  497 n_INLINE bool_t
  498 a_termcap_ent_query_tcp(struct a_termcap_ent *tep,
  499       struct a_termcap_control const *tcp){
  500    assert(!(n_psonce & n_PSO_TERMCAP_DISABLE));
  501    return a_termcap_ent_query(tep, &a_termcap_namedat[tcp->tc_off] + 2,
  502       tcp->tc_flags);
  503 }
  504 
  505 # else /* HAVE_TERMINFO */
  506 static bool_t
  507 a_termcap_load(char const *term){
  508    bool_t rv;
  509    NYD2_ENTER;
  510 
  511    /* ncurses may return -1 */
  512 # ifndef HAVE_TGETENT_NULL_BUF
  513 #  define a_BUF &a_termcap_g->tg_lib_buf[0]
  514 # else
  515 #  define a_BUF NULL
  516 # endif
  517    if(!(rv = tgetent(a_BUF, term) > 0))
  518       n_err(_("Unknown ${TERM}inal, using only *termcap*: %s\n"), term);
  519 # undef a_BUF
  520    NYD2_LEAVE;
  521    return rv;
  522 }
  523 
  524 static bool_t
  525 a_termcap_ent_query(struct a_termcap_ent *tep,
  526       char const *cname, ui16_t cflags){
  527    bool_t rv;
  528    NYD2_ENTER;
  529    assert(!(n_psonce & n_PSO_TERMCAP_DISABLE));
  530 
  531    if(n_UNLIKELY(*cname == '\0'))
  532       rv = FAL0;
  533    else switch((tep->te_flags = cflags) & a_TERMCAP_F_TYPE_MASK){
  534    case n_TERMCAP_CAPTYPE_BOOL:
  535       tep->te_off = (tgetflag(cname) > 0);
  536       rv = TRU1;
  537       break;
  538    case n_TERMCAP_CAPTYPE_NUMERIC:{
  539       int r = tgetnum(cname);
  540 
  541       if((rv = (r >= 0)))
  542          tep->te_off = (ui16_t)n_MIN(UI16_MAX, r);
  543       else
  544          tep->te_flags |= a_TERMCAP_F_NOENT;
  545       }break;
  546    default:
  547    case n_TERMCAP_CAPTYPE_STRING:{
  548 # ifndef HAVE_TGETENT_NULL_BUF
  549       char buf_base[a_TERMCAP_ENTRYSIZE_MAX], *buf = &buf_base[0];
  550 #  define a_BUF &buf
  551 # else
  552 #  define a_BUF NULL
  553 # endif
  554       char *cp;
  555 
  556       if((rv = ((cp = tgetstr(cname, a_BUF)) != NULL))){
  557          tep->te_off = (ui16_t)a_termcap_g->tg_dat.s_len;
  558          n_string_push_buf(&a_termcap_g->tg_dat, cp, strlen(cp) +1);
  559 # undef a_BUF
  560       }else
  561          tep->te_flags |= a_TERMCAP_F_NOENT;
  562       }break;
  563    }
  564    NYD2_LEAVE;
  565    return rv;
  566 }
  567 
  568 n_INLINE bool_t
  569 a_termcap_ent_query_tcp(struct a_termcap_ent *tep,
  570       struct a_termcap_control const *tcp){
  571    assert(!(n_psonce & n_PSO_TERMCAP_DISABLE));
  572    return a_termcap_ent_query(tep, &a_termcap_namedat[tcp->tc_off],
  573       tcp->tc_flags);
  574 }
  575 # endif /* !HAVE_TERMINFO */
  576 
  577 static int
  578 a_termcap_putc(int c){
  579    return putc(c, n_tty_fp);
  580 }
  581 #endif /* HAVE_TERMCAP */
  582 
  583 static si32_t
  584 a_termcap_enum_for_name(char const *name, size_t nlen, si32_t min, si32_t max){
  585    struct a_termcap_control const *tcp;
  586    char const *cnam;
  587    si32_t rv;
  588    NYD2_ENTER;
  589 
  590    /* Prefer terminfo(5) names */
  591    for(rv = max;;){
  592       if(rv-- == min){
  593          rv = -1;
  594          break;
  595       }
  596 
  597       tcp = &a_termcap_control[(ui32_t)rv];
  598       cnam = &a_termcap_namedat[tcp->tc_off];
  599       if(cnam[2] != '\0'){
  600          char const *xcp = cnam + 2;
  601 
  602          if(nlen == strlen(xcp) && !memcmp(xcp, name, nlen))
  603             break;
  604       }
  605       if(nlen == 2 && cnam[0] == name[0] && cnam[1] == name[1])
  606          break;
  607    }
  608    NYD2_LEAVE;
  609    return rv;
  610 }
  611 
  612 FL void
  613 n_termcap_init(void){
  614    struct str termvar;
  615    char const *ccp;
  616    NYD_ENTER;
  617 
  618    assert((n_psonce & n_PSO_INTERACTIVE) && !(n_poption & n_PO_QUICKRUN_MASK));
  619 
  620    a_termcap_g = n_alloc(sizeof *a_termcap_g);
  621    a_termcap_g->tg_ext_ents = NULL;
  622    memset(&a_termcap_g->tg_ents[0], 0, sizeof(a_termcap_g->tg_ents));
  623    if((ccp = ok_vlook(termcap)) != NULL)
  624       termvar.l = strlen(termvar.s = n_UNCONST(ccp));
  625    else
  626       /*termvar.s = NULL,*/ termvar.l = 0;
  627    n_string_reserve(n_string_creat(&a_termcap_g->tg_dat),
  628       ((termvar.l + (256 - 64)) & ~127));
  629 
  630    if(termvar.l > 0)
  631       a_termcap_init_var(&termvar);
  632 
  633    if(ok_blook(termcap_disable))
  634       n_psonce |= n_PSO_TERMCAP_DISABLE;
  635 #ifdef HAVE_TERMCAP
  636    else if((ccp = ok_vlook(TERM)) == NULL){
  637       n_err(_("Environment variable $TERM is not set, using only *termcap*\n"));
  638       n_psonce |= n_PSO_TERMCAP_DISABLE;
  639    }else if(!a_termcap_load(ccp))
  640       n_psonce |= n_PSO_TERMCAP_DISABLE;
  641    else{
  642       /* Query termcap(5) for each command slot that is not yet set */
  643       struct a_termcap_ent *tep;
  644       size_t i;
  645 
  646       for(i = n__TERMCAP_CMD_MAX1;;){
  647          if(i-- == 0)
  648             break;
  649          if((tep = &a_termcap_g->tg_ents[i])->te_flags == 0)
  650             a_termcap_ent_query_tcp(tep, &a_termcap_control[i]);
  651       }
  652    }
  653 #endif /* HAVE_TERMCAP */
  654 
  655    a_termcap_init_altern();
  656 
  657 #ifdef HAVE_TERMCAP
  658    if(a_termcap_g->tg_ents[n_TERMCAP_CMD_te].te_flags != 0 &&
  659          ok_blook(termcap_ca_mode))
  660       n_psonce |= n_PSO_TERMCAP_CA_MODE;
  661 #endif
  662    n_TERMCAP_RESUME(TRU1);
  663    NYD_LEAVE;
  664 }
  665 
  666 FL void
  667 n_termcap_destroy(void){
  668    NYD_ENTER;
  669    assert((n_psonce & n_PSO_INTERACTIVE) && !(n_poption & n_PO_QUICKRUN_MASK));
  670    assert(a_termcap_g != NULL);
  671 
  672    n_TERMCAP_SUSPEND(TRU1);
  673 
  674 #ifdef HAVE_DEBUG
  675    /* C99 */{
  676       struct a_termcap_ext_ent *tmp;
  677 
  678       while((tmp = a_termcap_g->tg_ext_ents) != NULL){
  679          a_termcap_g->tg_ext_ents = tmp->tee_next;
  680          n_free(tmp);
  681       }
  682    }
  683    n_string_gut(&a_termcap_g->tg_dat);
  684    n_free(a_termcap_g);
  685    a_termcap_g = NULL;
  686 #endif
  687    NYD_LEAVE;
  688 }
  689 
  690 #ifdef HAVE_TERMCAP
  691 FL void
  692 n_termcap_resume(bool_t complete){
  693    NYD_ENTER;
  694    if(a_termcap_g != NULL && !(n_psonce & n_PSO_TERMCAP_DISABLE)){
  695       if(complete && (n_psonce & n_PSO_TERMCAP_CA_MODE))
  696          n_termcap_cmdx(n_TERMCAP_CMD_ti);
  697       n_termcap_cmdx(n_TERMCAP_CMD_ks);
  698       fflush(n_tty_fp);
  699    }
  700    NYD_LEAVE;
  701 }
  702 
  703 FL void
  704 n_termcap_suspend(bool_t complete){
  705    NYD_ENTER;
  706    if(a_termcap_g != NULL && !(n_psonce & n_PSO_TERMCAP_DISABLE)){
  707       n_termcap_cmdx(n_TERMCAP_CMD_ke);
  708       if(complete && (n_psonce & n_PSO_TERMCAP_CA_MODE))
  709          n_termcap_cmdx(n_TERMCAP_CMD_te);
  710       fflush(n_tty_fp);
  711    }
  712    NYD_LEAVE;
  713 }
  714 #endif /* HAVE_TERMCAP */
  715 
  716 FL ssize_t
  717 n_termcap_cmd(enum n_termcap_cmd cmd, ssize_t a1, ssize_t a2){
  718    /* Commands are not lazy queried */
  719    struct a_termcap_ent const *tep;
  720    enum a_termcap_flags flags;
  721    ssize_t rv;
  722    NYD2_ENTER;
  723    n_UNUSED(a1);
  724    n_UNUSED(a2);
  725 
  726    rv = FAL0;
  727    if(a_termcap_g == NULL)
  728       goto jleave;
  729 
  730    flags = cmd & ~n__TERMCAP_CMD_MASK;
  731    cmd &= n__TERMCAP_CMD_MASK;
  732    tep = a_termcap_g->tg_ents;
  733 
  734    if((flags & n_TERMCAP_CMD_FLAG_CA_MODE) &&
  735          !(n_psonce & n_PSO_TERMCAP_CA_MODE))
  736       rv = TRU1;
  737    else if((tep += cmd)->te_flags == 0 || (tep->te_flags & a_TERMCAP_F_NOENT))
  738       rv = TRUM1;
  739    else if(!(tep->te_flags & a_TERMCAP_F_ALTERN)){
  740       char const *cp;
  741 
  742       assert((tep->te_flags & a_TERMCAP_F_TYPE_MASK) ==
  743          n_TERMCAP_CAPTYPE_STRING);
  744 
  745       cp = &a_termcap_g->tg_dat.s_dat[tep->te_off];
  746 
  747 #ifdef HAVE_TERMCAP
  748       if(tep->te_flags & (a_TERMCAP_F_ARG_IDX1 | a_TERMCAP_F_ARG_IDX2)){
  749          if(n_psonce & n_PSO_TERMCAP_DISABLE){
  750             if(n_poption & n_PO_D_V){
  751                char const *cnam = &a_termcap_namedat[
  752                      a_termcap_control[cmd].tc_off];
  753 
  754                if(cnam[2] != '\0')
  755                   cnam += 2;
  756                n_err(_("*termcap-disable*d (/$TERM not set/unknown): "
  757                   "can't perform CAP: %s\n"), cnam);
  758             }
  759             goto jleave;
  760          }
  761 
  762          /* Follow Thomas Dickey's advise on pre-va_arg prototypes, add 0s */
  763 # ifdef HAVE_TERMINFO
  764          if((cp = tparm(cp, a1, a2, 0,0,0,0,0,0,0)) == NULL)
  765             goto jleave;
  766 # else
  767          /* curs_termcap.3:
  768           * The \fBtgoto\fP function swaps the order of parameters.
  769           * It does this also for calls requiring only a single parameter.
  770           * In that case, the first parameter is merely a placeholder. */
  771          if(!(tep->te_flags & a_TERMCAP_F_ARG_IDX2)){
  772             a2 = a1;
  773             a1 = (ui32_t)-1;
  774          }
  775          if((cp = tgoto(cp, (int)a1, (int)a2)) == NULL)
  776             goto jleave;
  777 # endif
  778       }
  779 #endif /* HAVE_TERMCAP */
  780 
  781       for(;;){
  782 #ifdef HAVE_TERMCAP
  783          if(!(n_psonce & n_PSO_TERMCAP_DISABLE)){
  784             if(tputs(cp, 1, &a_termcap_putc) != OK)
  785                break;
  786          }else
  787 #endif
  788                if(fputs(cp, n_tty_fp) == EOF)
  789             break;
  790          if(!(tep->te_flags & a_TERMCAP_F_ARG_CNT) || --a1 <= 0){
  791             rv = TRU1;
  792             break;
  793          }
  794       }
  795       goto jflush;
  796    }else{
  797       switch(cmd){
  798       default:
  799          rv = TRUM1;
  800          break;
  801 
  802 #ifdef HAVE_MLE
  803       case n_TERMCAP_CMD_ce: /* ce == ch + [:SPACE:] */
  804          if(a1 > 0)
  805             --a1;
  806          if((rv = n_termcap_cmd(n_TERMCAP_CMD_ch, a1, 0)) > 0){
  807             for(a2 = n_scrnwidth - a1 - 1; a2 > 0; --a2)
  808                if(putc(' ', n_tty_fp) == EOF){
  809                   rv = FAL0;
  810                   break;
  811                }
  812             if(rv && n_termcap_cmd(n_TERMCAP_CMD_ch, a1, -1) != TRU1)
  813                rv = FAL0;
  814          }
  815          break;
  816       case n_TERMCAP_CMD_ch: /* ch == cr + nd */
  817          rv = n_termcap_cmdx(n_TERMCAP_CMD_cr);
  818          if(rv > 0 && a1 > 0){
  819             rv = n_termcap_cmd(n_TERMCAP_CMD_nd, a1, -1);
  820          }
  821          break;
  822 # ifdef HAVE_TERMCAP
  823       case n_TERMCAP_CMD_cl: /* cl = ho + cd */
  824          rv = n_termcap_cmdx(n_TERMCAP_CMD_ho);
  825          if(rv > 0)
  826             rv = n_termcap_cmdx(n_TERMCAP_CMD_cd | flags);
  827          break;
  828 # endif
  829 #endif /* HAVE_MLE */
  830       }
  831 
  832 jflush:
  833       if(flags & n_TERMCAP_CMD_FLAG_FLUSH)
  834          fflush(n_tty_fp);
  835       if(ferror(n_tty_fp))
  836          rv = FAL0;
  837    }
  838 
  839 jleave:
  840    NYD2_LEAVE;
  841    return rv;
  842 }
  843 
  844 FL bool_t
  845 n_termcap_query(enum n_termcap_query query, struct n_termcap_value *tvp){
  846    /* Queries are lazy queried upon request */
  847    struct a_termcap_ent const *tep;
  848    bool_t rv;
  849    NYD2_ENTER;
  850 
  851    assert(tvp != NULL);
  852 
  853    rv = FAL0;
  854    if(a_termcap_g == NULL)
  855       goto jleave;
  856 
  857    /* Is it a built-in query? */
  858    if(query != n__TERMCAP_QUERY_MAX1){
  859       tep = &a_termcap_g->tg_ents[n__TERMCAP_CMD_MAX1 + query];
  860 
  861       if(tep->te_flags == 0
  862 #ifdef HAVE_TERMCAP
  863             && ((n_psonce & n_PSO_TERMCAP_DISABLE) ||
  864                !a_termcap_ent_query_tcp(n_UNCONST(tep),
  865                &a_termcap_control[n__TERMCAP_CMD_MAX1 + query]))
  866 #endif
  867       )
  868          goto jleave;
  869    }else{
  870 #ifdef HAVE_TERMCAP
  871       size_t nlen;
  872 #endif
  873       struct a_termcap_ext_ent *teep;
  874       char const *ndat = tvp->tv_data.tvd_string;
  875 
  876       for(teep = a_termcap_g->tg_ext_ents; teep != NULL; teep = teep->tee_next)
  877          if(!strcmp(teep->tee_name, ndat)){
  878             tep = &teep->tee_super;
  879             goto jextok;
  880          }
  881 
  882 #ifdef HAVE_TERMCAP
  883       if(n_psonce & n_PSO_TERMCAP_DISABLE)
  884 #endif
  885          goto jleave;
  886 #ifdef HAVE_TERMCAP
  887       nlen = strlen(ndat) +1;
  888       teep = n_alloc(n_VSTRUCT_SIZEOF(struct a_termcap_ext_ent, tee_name) +
  889             nlen);
  890       tep = &teep->tee_super;
  891       teep->tee_next = a_termcap_g->tg_ext_ents;
  892       a_termcap_g->tg_ext_ents = teep;
  893       memcpy(teep->tee_name, ndat, nlen);
  894 
  895       if(!a_termcap_ent_query(n_UNCONST(tep), ndat,
  896                n_TERMCAP_CAPTYPE_STRING | a_TERMCAP_F_QUERY))
  897          goto jleave;
  898 #endif
  899 jextok:;
  900    }
  901 
  902    if(tep->te_flags & a_TERMCAP_F_NOENT)
  903       goto jleave;
  904 
  905    rv = (tep->te_flags & a_TERMCAP_F_ALTERN) ? TRUM1 : TRU1;
  906 
  907    switch((tvp->tv_captype = tep->te_flags & a_TERMCAP_F_TYPE_MASK)){
  908    case n_TERMCAP_CAPTYPE_BOOL:
  909       tvp->tv_data.tvd_bool = (bool_t)tep->te_off;
  910       break;
  911    case n_TERMCAP_CAPTYPE_NUMERIC:
  912       tvp->tv_data.tvd_numeric = (ui32_t)tep->te_off;
  913       break;
  914    default:
  915    case n_TERMCAP_CAPTYPE_STRING:
  916       tvp->tv_data.tvd_string = a_termcap_g->tg_dat.s_dat + tep->te_off;
  917       break;
  918    }
  919 jleave:
  920    NYD2_LEAVE;
  921    return rv;
  922 }
  923 
  924 #ifdef HAVE_KEY_BINDINGS
  925 FL si32_t
  926 n_termcap_query_for_name(char const *name, enum n_termcap_captype type){
  927    si32_t rv;
  928    NYD2_ENTER;
  929 
  930    if((rv = a_termcap_query_for_name(name, strlen(name))) >= 0){
  931       struct a_termcap_control const *tcp = &a_termcap_control[(ui32_t)rv];
  932 
  933       if(type != n_TERMCAP_CAPTYPE_NONE &&
  934             (tcp->tc_flags & a_TERMCAP_F_TYPE_MASK) != type)
  935          rv = -2;
  936       else
  937          rv -= n__TERMCAP_CMD_MAX1;
  938    }
  939    NYD2_LEAVE;
  940    return rv;
  941 }
  942 
  943 FL char const *
  944 n_termcap_name_of_query(enum n_termcap_query query){
  945    char const *rv;
  946    NYD2_ENTER;
  947 
  948    rv = &a_termcap_namedat[
  949          a_termcap_control[n__TERMCAP_CMD_MAX1 + query].tc_off + 2];
  950    NYD2_LEAVE;
  951    return rv;
  952 }
  953 #endif /* HAVE_KEY_BINDINGS */
  954 #endif /* n_HAVE_TCAP */
  955 
  956 /* s-it-mode */