"Fossies" - the Fresh Open Source Software Archive

Member "s-nail-14.9.10/termcap.c" (25 Mar 2018, 28813 Bytes) of package /linux/misc/s-nail-14.9.10.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 last Fossies "Diffs" side-by-side code changes report: 14.9.6_vs_14.9.7.

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