"Fossies" - the Fresh Open Source Software Archive

Member "s-nail-14.9.11/colour.c" (8 Aug 2018, 29217 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 "colour.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  *@ `(un)?colour' commands, and anything working with it.
    3  *@ TODO n_colour_env should be objects, n_COLOUR_IS_ACTIVE() should take
    4  *@ TODO such an object!  We still should work together with n_go_data,
    5  *@ TODO but only for cleanup purposes.  No stack at all, that is to say!
    6  *@ TODO (Note we yet use autorec memory, so with JUMPS this needs care!)
    7  *
    8  * Copyright (c) 2014 - 2018 Steffen (Daode) Nurpmeso <steffen@sdaoden.eu>.
    9  * SPDX-License-Identifier: ISC
   10  *
   11  * Permission to use, copy, modify, and/or distribute this software for any
   12  * purpose with or without fee is hereby granted, provided that the above
   13  * copyright notice and this permission notice appear in all copies.
   14  *
   15  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
   16  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
   17  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
   18  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
   19  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
   20  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
   21  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
   22  */
   23 #undef n_FILE
   24 #define n_FILE colour
   25 
   26 #ifndef HAVE_AMALGAMATION
   27 # include "nail.h"
   28 #endif
   29 
   30 EMPTY_FILE()
   31 #ifdef HAVE_COLOUR
   32 
   33 /* Not needed publically, but extends a set from nail.h */
   34 #define n_COLOUR_TAG_ERR ((char*)-1)
   35 #define a_COLOUR_TAG_IS_SPECIAL(P) (PTR2SIZE(P) >= PTR2SIZE(-3))
   36 
   37 enum a_colour_type{
   38    a_COLOUR_T_256,
   39    a_COLOUR_T_8,
   40    a_COLOUR_T_1,
   41    a_COLOUR_T_NONE,     /* EQ largest real colour + 1! */
   42    a_COLOUR_T_UNKNOWN   /* Initial value: real one queried before 1st use */
   43 };
   44 
   45 enum a_colour_tag_type{
   46    a_COLOUR_TT_NONE,
   47    a_COLOUR_TT_DOT = 1<<0,       /* "dot" */
   48    a_COLOUR_TT_OLDER = 1<<1,     /* "older" */
   49    a_COLOUR_TT_HEADERS = 1<<2,   /* Comma-separated list of headers allowed */
   50 
   51    a_COLOUR_TT_SUM = a_COLOUR_TT_DOT | a_COLOUR_TT_OLDER,
   52    a_COLOUR_TT_VIEW = a_COLOUR_TT_HEADERS
   53 };
   54 
   55 struct a_colour_type_map{
   56    ui8_t ctm_type;   /* a_colour_type */
   57    char ctm_name[7];
   58 };
   59 
   60 struct a_colour_map_id{
   61    ui8_t cmi_ctx;    /* enum n_colour_ctx */
   62    ui8_t cmi_id;     /* enum n_colour_id */
   63    ui8_t cmi_tt;     /* enum a_colour_tag_type */
   64    char const cmi_name[13];
   65 };
   66 n_CTA(n__COLOUR_IDS <= UI8_MAX, "Enumeration exceeds storage datatype");
   67 
   68 struct n_colour_pen{
   69    struct str cp_dat;   /* Pre-prepared ISO 6429 escape sequence */
   70 };
   71 
   72 struct a_colour_map /* : public n_colour_pen */{
   73    struct n_colour_pen cm_pen;   /* Points into .cm_buf */
   74    struct a_colour_map *cm_next;
   75    char const *cm_tag;           /* Colour tag or NULL for default (last) */
   76    struct a_colour_map_id const *cm_cmi;
   77 #ifdef HAVE_REGEX
   78    regex_t *cm_regex;
   79 #endif
   80    ui32_t cm_refcnt;             /* Beware of reference drops in recursions */
   81    ui32_t cm_user_off;           /* User input offset in .cm_buf */
   82    char cm_buf[n_VFIELD_SIZE(0)];
   83 };
   84 
   85 struct a_colour_g{
   86    bool_t cg_is_init;
   87    ui8_t cg_type;                   /* a_colour_type */
   88    ui8_t __cg_pad[6];
   89    struct n_colour_pen cg_reset;    /* The reset sequence */
   90    struct a_colour_map
   91       *cg_maps[a_COLOUR_T_NONE][n__COLOUR_CTX_MAX1][n__COLOUR_IDS];
   92    char cg__reset_buf[n_ALIGN_SMALL(sizeof("\033[0m"))];
   93 };
   94 
   95 /* C99: use [INDEX]={} */
   96 /* */
   97 n_CTA(a_COLOUR_T_256 == 0, "Unexpected value of constant");
   98 n_CTA(a_COLOUR_T_8 == 1, "Unexpected value of constant");
   99 n_CTA(a_COLOUR_T_1 == 2, "Unexpected value of constant");
  100 static char const a_colour_types[][8] = {"256", "iso", "mono"};
  101 
  102 static struct a_colour_type_map const a_colour_type_maps[] = {
  103    {a_COLOUR_T_256, "256"},
  104    {a_COLOUR_T_8, "8"}, {a_COLOUR_T_8, "iso"}, {a_COLOUR_T_8, "ansi"},
  105    {a_COLOUR_T_1, "1"}, {a_COLOUR_T_1, "mono"}
  106 };
  107 
  108 n_CTA(n_COLOUR_CTX_SUM == 0, "Unexpected value of constant");
  109 n_CTA(n_COLOUR_CTX_VIEW == 1, "Unexpected value of constant");
  110 n_CTA(n_COLOUR_CTX_MLE == 2, "Unexpected value of constant");
  111 static char const a_colour_ctx_prefixes[n__COLOUR_CTX_MAX1][8] = {
  112    "sum-", "view-", "mle-"
  113 };
  114 
  115 static struct a_colour_map_id const
  116       a_colour_map_ids[n__COLOUR_CTX_MAX1][n__COLOUR_IDS] = {{
  117    {n_COLOUR_CTX_SUM, n_COLOUR_ID_SUM_DOTMARK, a_COLOUR_TT_SUM, "dotmark"},
  118    {n_COLOUR_CTX_SUM, n_COLOUR_ID_SUM_HEADER, a_COLOUR_TT_SUM, "header"},
  119    {n_COLOUR_CTX_SUM, n_COLOUR_ID_SUM_THREAD, a_COLOUR_TT_SUM, "thread"},
  120    }, {
  121    {n_COLOUR_CTX_VIEW, n_COLOUR_ID_VIEW_FROM_, a_COLOUR_TT_NONE, "from_"},
  122    {n_COLOUR_CTX_VIEW, n_COLOUR_ID_VIEW_HEADER, a_COLOUR_TT_VIEW, "header"},
  123    {n_COLOUR_CTX_VIEW, n_COLOUR_ID_VIEW_MSGINFO, a_COLOUR_TT_NONE, "msginfo"},
  124    {n_COLOUR_CTX_VIEW, n_COLOUR_ID_VIEW_PARTINFO, a_COLOUR_TT_NONE, "partinfo"},
  125    }, {
  126    {n_COLOUR_CTX_MLE, n_COLOUR_ID_MLE_POSITION, a_COLOUR_TT_NONE, "position"},
  127    {n_COLOUR_CTX_MLE, n_COLOUR_ID_MLE_PROMPT, a_COLOUR_TT_NONE, "prompt"},
  128 }};
  129 #define a_COLOUR_MAP_SHOW_FIELDWIDTH \
  130    (int)(sizeof("view-")-1 + sizeof("partinfo")-1)
  131 
  132 static struct a_colour_g a_colour_g;
  133 
  134 /* */
  135 static void a_colour_init(void);
  136 
  137 /* Find the type or -1 */
  138 static enum a_colour_type a_colour_type_find(char const *name);
  139 
  140 /* `(un)?colour' implementations */
  141 static bool_t a_colour_mux(char **argv);
  142 static bool_t a_colour_unmux(char **argv);
  143 
  144 static bool_t a_colour__show(enum a_colour_type ct);
  145 /* (regexpp may be NULL) */
  146 static char const *a_colour__tag_identify(struct a_colour_map_id const *cmip,
  147                      char const *ctag, void **regexpp);
  148 
  149 /* Try to find a mapping identity for user given slotname */
  150 static struct a_colour_map_id const *a_colour_map_id_find(char const *slotname);
  151 
  152 /* Find an existing mapping for the given combination */
  153 static struct a_colour_map *a_colour_map_find(enum n_colour_id cid,
  154                               enum n_colour_ctx cctx, char const *ctag);
  155 
  156 /* In-/Decrement reference counter, destroy if counts gets zero */
  157 #define a_colour_map_ref(SELF) do{ ++(SELF)->cm_refcnt; }while(0)
  158 static void a_colour_map_unref(struct a_colour_map *self);
  159 
  160 /* Create an ISO 6429 (ECMA-48/ANSI) terminal control escape sequence from user
  161  * input spec, store it or on error message in *store */
  162 static bool_t a_colour_iso6429(enum a_colour_type ct, char **store,
  163                char const *spec);
  164 
  165 static void
  166 a_colour_init(void){
  167    NYD2_ENTER;
  168    a_colour_g.cg_is_init = TRU1;
  169    memcpy(a_colour_g.cg_reset.cp_dat.s = a_colour_g.cg__reset_buf, "\033[0m",
  170       a_colour_g.cg_reset.cp_dat.l = sizeof("\033[0m") -1); /* (calloc) */
  171    a_colour_g.cg_type = a_COLOUR_T_UNKNOWN;
  172    NYD2_LEAVE;
  173 }
  174 
  175 static enum a_colour_type
  176 a_colour_type_find(char const *name){
  177    struct a_colour_type_map const *ctmp;
  178    enum a_colour_type rv;
  179    NYD2_ENTER;
  180 
  181    ctmp = a_colour_type_maps;
  182    do if(!asccasecmp(ctmp->ctm_name, name)){
  183       rv = ctmp->ctm_type;
  184       goto jleave;
  185    }while(PTRCMP(++ctmp, !=, a_colour_type_maps + n_NELEM(a_colour_type_maps)));
  186 
  187    rv = (enum a_colour_type)-1;
  188 jleave:
  189    NYD2_LEAVE;
  190    return rv;
  191 }
  192 
  193 static bool_t
  194 a_colour_mux(char **argv){
  195    void *regexp;
  196    char const *mapname, *ctag;
  197    struct a_colour_map **cmap, *blcmp, *lcmp, *cmp;
  198    struct a_colour_map_id const *cmip;
  199    bool_t rv;
  200    enum a_colour_type ct;
  201    NYD2_ENTER;
  202 
  203    if((ct = a_colour_type_find(*argv++)) == (enum a_colour_type)-1 &&
  204          (*argv != NULL || !n_is_all_or_aster(argv[-1]))){
  205       n_err(_("`colour': invalid colour type %s\n"),
  206          n_shexp_quote_cp(argv[-1], FAL0));
  207       rv = FAL0;
  208       goto jleave;
  209    }
  210 
  211    if(!a_colour_g.cg_is_init)
  212       a_colour_init();
  213 
  214    if(*argv == NULL){
  215       rv = a_colour__show(ct);
  216       goto jleave;
  217    }
  218 
  219    rv = FAL0;
  220    regexp = NULL;
  221 
  222    if((cmip = a_colour_map_id_find(mapname = argv[0])) == NULL){
  223       n_err(_("`colour': non-existing mapping: %s\n"),
  224          n_shexp_quote_cp(mapname, FAL0));
  225       goto jleave;
  226    }
  227 
  228    if(argv[1] == NULL){
  229       n_err(_("`colour': %s: missing attribute argument\n"),
  230          n_shexp_quote_cp(mapname, FAL0));
  231       goto jleave;
  232    }
  233 
  234    /* Check whether preconditions are at all allowed, verify them as far as
  235     * possible as necessary.  For shell_quote() simplicity let's just ignore an
  236     * empty precondition */
  237    if((ctag = argv[2]) != NULL && *ctag != '\0'){
  238       char const *xtag;
  239 
  240       if(cmip->cmi_tt == a_COLOUR_TT_NONE){
  241          n_err(_("`colour': %s does not support preconditions\n"),
  242             n_shexp_quote_cp(mapname, FAL0));
  243          goto jleave;
  244       }else if((xtag = a_colour__tag_identify(cmip, ctag, &regexp)) ==
  245             n_COLOUR_TAG_ERR){
  246          /* I18N: ..of colour mapping */
  247          n_err(_("`colour': %s: invalid precondition: %s\n"),
  248             n_shexp_quote_cp(mapname, FAL0), n_shexp_quote_cp(ctag, FAL0));
  249          goto jleave;
  250       }
  251       ctag = xtag;
  252    }
  253 
  254    /* At this time we have all the information to be able to query whether such
  255     * a mapping is yet established. If so, destroy it */
  256    for(blcmp = lcmp = NULL,
  257             cmp = *(cmap =
  258                   &a_colour_g.cg_maps[ct][cmip->cmi_ctx][cmip->cmi_id]);
  259          cmp != NULL; blcmp = lcmp, lcmp = cmp, cmp = cmp->cm_next){
  260       char const *xctag = cmp->cm_tag;
  261 
  262       if(xctag == ctag ||
  263             (ctag != NULL && !a_COLOUR_TAG_IS_SPECIAL(ctag) &&
  264              xctag != NULL && !a_COLOUR_TAG_IS_SPECIAL(xctag) &&
  265              !strcmp(xctag, ctag))){
  266          if(lcmp == NULL)
  267             *cmap = cmp->cm_next;
  268          else
  269             lcmp->cm_next = cmp->cm_next;
  270          a_colour_map_unref(cmp);
  271          break;
  272       }
  273    }
  274 
  275    /* Create mapping */
  276    /* C99 */{
  277       size_t tl, ul, cl;
  278       char *bp, *cp;
  279 
  280       if(!a_colour_iso6429(ct, &cp, argv[1])){
  281          /* I18N: colour command: mapping: error message: user argument */
  282          n_err(_("`colour': %s: %s: %s\n"), n_shexp_quote_cp(mapname, FAL0),
  283             cp, n_shexp_quote_cp(argv[1], FAL0));
  284          goto jleave;
  285       }
  286 
  287       tl = (ctag != NULL && !a_COLOUR_TAG_IS_SPECIAL(ctag)) ? strlen(ctag) : 0;
  288       cmp = n_alloc(n_VSTRUCT_SIZEOF(struct a_colour_map, cm_buf) +
  289             tl +1 + (ul = strlen(argv[1])) +1 + (cl = strlen(cp)) +1);
  290 
  291       /* .cm_buf stuff */
  292       cmp->cm_pen.cp_dat.s = bp = cmp->cm_buf;
  293       cmp->cm_pen.cp_dat.l = cl;
  294       memcpy(bp, cp, ++cl);
  295       bp += cl;
  296 
  297       cmp->cm_user_off = (ui32_t)PTR2SIZE(bp - cmp->cm_buf);
  298       memcpy(bp, argv[1], ++ul);
  299       bp += ul;
  300 
  301       if(tl > 0){
  302          cmp->cm_tag = bp;
  303          memcpy(bp, ctag, ++tl);
  304          /*bp += tl;*/
  305       }else
  306          cmp->cm_tag = ctag;
  307 
  308       /* Non-buf stuff; default mapping */
  309       if(lcmp != NULL){
  310          /* Default mappings must be last */
  311          if(ctag == NULL){
  312             while(lcmp->cm_next != NULL)
  313                lcmp = lcmp->cm_next;
  314          }else if(lcmp->cm_next == NULL && lcmp->cm_tag == NULL){
  315             if((lcmp = blcmp) == NULL)
  316                goto jlinkhead;
  317          }
  318          cmp->cm_next = lcmp->cm_next;
  319          lcmp->cm_next = cmp;
  320       }else{
  321 jlinkhead:
  322          cmp->cm_next = *cmap;
  323          *cmap = cmp;
  324       }
  325       cmp->cm_cmi = cmip;
  326 #ifdef HAVE_REGEX
  327       cmp->cm_regex = regexp;
  328 #endif
  329       cmp->cm_refcnt = 0;
  330       a_colour_map_ref(cmp);
  331    }
  332    rv = TRU1;
  333 jleave:
  334    NYD2_LEAVE;
  335    return rv;
  336 }
  337 
  338 static bool_t
  339 a_colour_unmux(char **argv){
  340    char const *mapname, *ctag, *xtag;
  341    struct a_colour_map **cmap, *lcmp, *cmp;
  342    struct a_colour_map_id const *cmip;
  343    enum a_colour_type ct;
  344    bool_t aster, rv;
  345    NYD2_ENTER;
  346 
  347    rv = TRU1;
  348    aster = FAL0;
  349 
  350    if((ct = a_colour_type_find(*argv++)) == (enum a_colour_type)-1){
  351       if(!n_is_all_or_aster(argv[-1])){
  352          n_err(_("`uncolour': invalid colour type %s\n"),
  353             n_shexp_quote_cp(argv[-1], FAL0));
  354          rv = FAL0;
  355          goto j_leave;
  356       }
  357       aster = TRU1;
  358       ct = 0;
  359    }
  360 
  361    mapname = argv[0];
  362    ctag = argv[1];
  363 
  364    if(!a_colour_g.cg_is_init)
  365       goto jemap;
  366 
  367    /* Delete anything? */
  368 jredo:
  369    if(ctag == NULL && mapname[0] == '*' && mapname[1] == '\0'){
  370       size_t i1, i2;
  371       struct a_colour_map *tmp;
  372 
  373       for(i1 = 0; i1 < n__COLOUR_CTX_MAX1; ++i1)
  374          for(i2 = 0; i2 < n__COLOUR_IDS; ++i2)
  375             for(cmp = *(cmap = &a_colour_g.cg_maps[ct][i1][i2]), *cmap = NULL;
  376                   cmp != NULL;){
  377                tmp = cmp;
  378                cmp = cmp->cm_next;
  379                a_colour_map_unref(tmp);
  380             }
  381    }else{
  382       if((cmip = a_colour_map_id_find(mapname)) == NULL){
  383          rv = FAL0;
  384 jemap:
  385          /* I18N: colour command, mapping and precondition (option in quotes) */
  386          n_err(_("`uncolour': non-existing mapping: %s%s%s\n"),
  387             n_shexp_quote_cp(mapname, FAL0), (ctag == NULL ? n_empty : " "),
  388             (ctag == NULL ? n_empty : n_shexp_quote_cp(ctag, FAL0)));
  389          goto jleave;
  390       }
  391 
  392       if((xtag = ctag) != NULL){
  393          if(cmip->cmi_tt == a_COLOUR_TT_NONE){
  394             n_err(_("`uncolour': %s does not support preconditions\n"),
  395                n_shexp_quote_cp(mapname, FAL0));
  396             rv = FAL0;
  397             goto jleave;
  398          }else if((xtag = a_colour__tag_identify(cmip, ctag, NULL)) ==
  399                n_COLOUR_TAG_ERR){
  400             n_err(_("`uncolour': %s: invalid precondition: %s\n"),
  401                n_shexp_quote_cp(mapname, FAL0), n_shexp_quote_cp(ctag, FAL0));
  402             rv = FAL0;
  403             goto jleave;
  404          }
  405          /* (Improve user experience) */
  406          if(xtag != NULL && !a_COLOUR_TAG_IS_SPECIAL(xtag))
  407             ctag = xtag;
  408       }
  409 
  410       lcmp = NULL;
  411       cmp = *(cmap = &a_colour_g.cg_maps[ct][cmip->cmi_ctx][cmip->cmi_id]);
  412       for(;;){
  413          char const *xctag;
  414 
  415          if(cmp == NULL){
  416             rv = FAL0;
  417             goto jemap;
  418          }
  419          if((xctag = cmp->cm_tag) == ctag)
  420             break;
  421          if(ctag != NULL && !a_COLOUR_TAG_IS_SPECIAL(ctag) &&
  422                xctag != NULL && !a_COLOUR_TAG_IS_SPECIAL(xctag) &&
  423                !strcmp(xctag, ctag))
  424             break;
  425          lcmp = cmp;
  426          cmp = cmp->cm_next;
  427       }
  428 
  429       if(lcmp == NULL)
  430          *cmap = cmp->cm_next;
  431       else
  432          lcmp->cm_next = cmp->cm_next;
  433       a_colour_map_unref(cmp);
  434    }
  435 
  436 jleave:
  437    if(aster && ++ct != a_COLOUR_T_NONE)
  438       goto jredo;
  439 j_leave:
  440    NYD2_LEAVE;
  441    return rv;
  442 }
  443 
  444 static bool_t
  445 a_colour__show(enum a_colour_type ct){
  446    struct a_colour_map *cmp;
  447    size_t i1, i2;
  448    bool_t rv;
  449    NYD2_ENTER;
  450 
  451    /* Show all possible types? */
  452    if((rv = (ct == (enum a_colour_type)-1 ? TRU1 : FAL0)))
  453       ct = 0;
  454 jredo:
  455    for(i1 = 0; i1 < n__COLOUR_CTX_MAX1; ++i1)
  456       for(i2 = 0; i2 < n__COLOUR_IDS; ++i2){
  457          if((cmp = a_colour_g.cg_maps[ct][i1][i2]) == NULL)
  458             continue;
  459 
  460          while(cmp != NULL){
  461             char const *tagann, *tag;
  462 
  463             tagann = n_empty;
  464             if((tag = cmp->cm_tag) == NULL)
  465                tag = n_empty;
  466             else if(tag == n_COLOUR_TAG_SUM_DOT)
  467                tag = "dot";
  468             else if(tag == n_COLOUR_TAG_SUM_OLDER)
  469                tag = "older";
  470 #ifdef HAVE_REGEX
  471             else if(cmp->cm_regex != NULL)
  472                tagann = "[rx] ";
  473 #endif
  474             fprintf(n_stdout, "colour %s %-*s %s %s%s\n",
  475                a_colour_types[ct], a_COLOUR_MAP_SHOW_FIELDWIDTH,
  476                savecat(a_colour_ctx_prefixes[i1],
  477                   a_colour_map_ids[i1][i2].cmi_name),
  478                (char const*)cmp->cm_buf + cmp->cm_user_off,
  479                tagann, n_shexp_quote_cp(tag, TRU1));
  480             cmp = cmp->cm_next;
  481          }
  482       }
  483 
  484    if(rv && ++ct != a_COLOUR_T_NONE)
  485       goto jredo;
  486    rv = TRU1;
  487    NYD2_LEAVE;
  488    return rv;
  489 }
  490 
  491 static char const *
  492 a_colour__tag_identify(struct a_colour_map_id const *cmip, char const *ctag,
  493       void **regexpp){
  494    NYD2_ENTER;
  495    n_UNUSED(regexpp);
  496 
  497    if((cmip->cmi_tt & a_COLOUR_TT_DOT) && !asccasecmp(ctag, "dot"))
  498       ctag = n_COLOUR_TAG_SUM_DOT;
  499    else if((cmip->cmi_tt & a_COLOUR_TT_OLDER) && !asccasecmp(ctag, "older"))
  500       ctag = n_COLOUR_TAG_SUM_OLDER;
  501    else if(cmip->cmi_tt & a_COLOUR_TT_HEADERS){
  502       char *cp, c;
  503       size_t i;
  504 
  505       /* Can this be a valid list of headers?  However, with regular expressions
  506        * simply use the input as such if it appears to be a regex */
  507 #ifdef HAVE_REGEX
  508       if(n_is_maybe_regex(ctag)){
  509          int s;
  510 
  511          if(regexpp != NULL &&
  512                (s = regcomp(*regexpp = n_alloc(sizeof(regex_t)), ctag,
  513                   REG_EXTENDED | REG_ICASE | REG_NOSUB)) != 0){
  514             n_err(_("`colour': invalid regular expression: %s: %s\n"),
  515                n_shexp_quote_cp(ctag, FAL0), n_regex_err_to_doc(NULL, s));
  516             n_free(*regexpp);
  517             goto jetag;
  518          }
  519       }else
  520 #endif
  521       {
  522          /* Normalize to lowercase and strip any whitespace before use */
  523          i = strlen(ctag);
  524          cp = n_autorec_alloc(i +1);
  525 
  526          for(i = 0; (c = *ctag++) != '\0';){
  527             bool_t isblspc = blankspacechar(c);
  528 
  529             if(!isblspc && !alnumchar(c) && c != '-' && c != ',')
  530                goto jetag;
  531             /* Since we compare header names as they come from the message this
  532              * lowercasing is however redundant: we need to asccasecmp() them */
  533             if(!isblspc)
  534                cp[i++] = lowerconv(c);
  535          }
  536          cp[i] = '\0';
  537          ctag = cp;
  538       }
  539    }else
  540 jetag:
  541       ctag = n_COLOUR_TAG_ERR;
  542    NYD2_LEAVE;
  543    return ctag;
  544 }
  545 
  546 static struct a_colour_map_id const *
  547 a_colour_map_id_find(char const *cp){
  548    size_t i;
  549    struct a_colour_map_id const (*cmip)[n__COLOUR_IDS], *rv;
  550    NYD2_ENTER;
  551 
  552    rv = NULL;
  553 
  554    for(i = 0;; ++i){
  555       if(i == n__COLOUR_IDS)
  556          goto jleave;
  557       else{
  558          size_t j = strlen(a_colour_ctx_prefixes[i]);
  559          if(!ascncasecmp(cp, a_colour_ctx_prefixes[i], j)){
  560             cp += j;
  561             break;
  562          }
  563       }
  564    }
  565    cmip = &a_colour_map_ids[i];
  566 
  567    for(i = 0;; ++i){
  568       if(i == n__COLOUR_IDS || (rv = &(*cmip)[i])->cmi_name[0] == '\0'){
  569          rv = NULL;
  570          break;
  571       }
  572       if(!asccasecmp(cp, rv->cmi_name))
  573          break;
  574    }
  575 jleave:
  576    NYD2_LEAVE;
  577    return rv;
  578 }
  579 
  580 static struct a_colour_map *
  581 a_colour_map_find(enum n_colour_id cid, enum n_colour_ctx cctx,
  582       char const *ctag){
  583    struct a_colour_map *cmp;
  584    NYD2_ENTER;
  585 
  586    cmp = a_colour_g.cg_maps[a_colour_g.cg_type][cctx][cid];
  587    for(; cmp != NULL; cmp = cmp->cm_next){
  588       char const *xtag = cmp->cm_tag;
  589 
  590       if(xtag == ctag)
  591          break;
  592       if(xtag == NULL)
  593          break;
  594       if(ctag == NULL || a_COLOUR_TAG_IS_SPECIAL(ctag))
  595          continue;
  596 #ifdef HAVE_REGEX
  597       if(cmp->cm_regex != NULL){
  598          if(regexec(cmp->cm_regex, ctag, 0,NULL, 0) != REG_NOMATCH)
  599             break;
  600       }else
  601 #endif
  602       if(cmp->cm_cmi->cmi_tt & a_COLOUR_TT_HEADERS){
  603          char *hlist = savestr(xtag), *cp;
  604 
  605          while((cp = n_strsep(&hlist, ',', TRU1)) != NULL){
  606             if(!asccasecmp(cp, ctag))
  607                break;
  608          }
  609          if(cp != NULL)
  610             break;
  611       }
  612    }
  613    NYD2_LEAVE;
  614    return cmp;
  615 }
  616 
  617 static void
  618 a_colour_map_unref(struct a_colour_map *self){
  619    NYD2_ENTER;
  620    if(--self->cm_refcnt == 0){
  621 #ifdef HAVE_REGEX
  622       if(self->cm_regex != NULL){
  623          regfree(self->cm_regex);
  624          n_free(self->cm_regex);
  625       }
  626 #endif
  627       n_free(self);
  628    }
  629    NYD2_LEAVE;
  630 }
  631 
  632 static bool_t
  633 a_colour_iso6429(enum a_colour_type ct, char **store, char const *spec){
  634    struct isodesc{
  635       char id_name[15];
  636       char id_modc;
  637    } const fta[] = {
  638       {"bold", '1'}, {"underline", '4'}, {"reverse", '7'}
  639    }, ca[] = {
  640       {"black", '0'}, {"red", '1'}, {"green", '2'}, {"brown", '3'},
  641       {"blue", '4'}, {"magenta", '5'}, {"cyan", '6'}, {"white", '7'}
  642    }, *idp;
  643    char *xspec, *cp, fg[3], cfg[2 + 2*sizeof("255")];
  644    ui8_t ftno_base, ftno;
  645    bool_t rv;
  646    NYD_ENTER;
  647 
  648    rv = FAL0;
  649    /* 0/1 indicate usage, thereafter possibly 256 color sequences */
  650    cfg[0] = cfg[1] = 0;
  651 
  652    /* Since we use autorec_alloc(), reuse the n_strsep() buffer also for the
  653     * return value, ensure we have enough room for that */
  654    /* C99 */{
  655       size_t i = strlen(spec) +1;
  656       xspec = n_autorec_alloc(n_MAX(i,
  657             sizeof("\033[1;4;7;38;5;255;48;5;255m")));
  658       memcpy(xspec, spec, i);
  659       spec = xspec;
  660    }
  661 
  662    /* Iterate over the colour spec */
  663    ftno = 0;
  664    while((cp = n_strsep(&xspec, ',', TRU1)) != NULL){
  665       char *y, *x = strchr(cp, '=');
  666       if(x == NULL){
  667 jbail:
  668          *store = n_UNCONST(_("invalid attribute list"));
  669          goto jleave;
  670       }
  671       *x++ = '\0';
  672 
  673       if(!asccasecmp(cp, "ft")){
  674          if(!asccasecmp(x, "inverse")){
  675             n_OBSOLETE(_("please use reverse for ft= fonts, not inverse"));
  676             x = n_UNCONST("reverse");
  677          }
  678          for(idp = fta;; ++idp)
  679             if(idp == fta + n_NELEM(fta)){
  680                *store = n_UNCONST(_("invalid font attribute"));
  681                goto jleave;
  682             }else if(!asccasecmp(x, idp->id_name)){
  683                if(ftno < n_NELEM(fg))
  684                   fg[ftno++] = idp->id_modc;
  685                else{
  686                   *store = n_UNCONST(_("too many font attributes"));
  687                   goto jleave;
  688                }
  689                break;
  690             }
  691       }else if(!asccasecmp(cp, "fg")){
  692          y = cfg + 0;
  693          goto jiter_colour;
  694       }else if(!asccasecmp(cp, "bg")){
  695          y = cfg + 1;
  696 jiter_colour:
  697          if(ct == a_COLOUR_T_1){
  698             *store = n_UNCONST(_("colours are not allowed"));
  699             goto jleave;
  700          }
  701          /* Maybe 256 color spec */
  702          if(digitchar(x[0])){
  703             ui8_t xv;
  704 
  705             if(ct == a_COLOUR_T_8){
  706                *store = n_UNCONST(_("invalid colour for 8-colour mode"));
  707                goto jleave;
  708             }
  709 
  710             if((n_idec_ui8_cp(&xv, x, 10, NULL
  711                      ) & (n_IDEC_STATE_EMASK | n_IDEC_STATE_CONSUMED)
  712                   ) != n_IDEC_STATE_CONSUMED){
  713                *store = n_UNCONST(_("invalid 256-colour specification"));
  714                goto jleave;
  715             }
  716             y[0] = 5;
  717             memcpy((y == &cfg[0] ? y + 2 : y + 1 + sizeof("255")), x,
  718                (x[1] == '\0' ? 2 : (x[2] == '\0' ? 3 : 4)));
  719          }else for(idp = ca;; ++idp)
  720             if(idp == ca + n_NELEM(ca)){
  721                *store = n_UNCONST(_("invalid colour attribute"));
  722                goto jleave;
  723             }else if(!asccasecmp(x, idp->id_name)){
  724                y[0] = 1;
  725                y[2] = idp->id_modc;
  726                break;
  727             }
  728       }else
  729          goto jbail;
  730    }
  731 
  732    /* Restore our autorec_alloc() buffer, create return value */
  733    xspec = n_UNCONST(spec);
  734    if(ftno > 0 || cfg[0] || cfg[1]){ /* TODO unite/share colour setters */
  735       xspec[0] = '\033';
  736       xspec[1] = '[';
  737       xspec += 2;
  738 
  739       for(ftno_base = ftno; ftno > 0;){
  740          if(ftno-- != ftno_base)
  741             *xspec++ = ';';
  742          *xspec++ = fg[ftno];
  743       }
  744 
  745       if(cfg[0]){
  746          if(ftno_base > 0)
  747             *xspec++ = ';';
  748          xspec[0] = '3';
  749          if(cfg[0] == 1){
  750             xspec[1] = cfg[2];
  751             xspec += 2;
  752          }else{
  753             memcpy(xspec + 1, "8;5;", 4);
  754             xspec += 5;
  755             for(ftno = 2; cfg[ftno] != '\0'; ++ftno)
  756                *xspec++ = cfg[ftno];
  757          }
  758       }
  759 
  760       if(cfg[1]){
  761          if(ftno_base > 0 || cfg[0])
  762             *xspec++ = ';';
  763          xspec[0] = '4';
  764          if(cfg[1] == 1){
  765             xspec[1] = cfg[3];
  766             xspec += 2;
  767          }else{
  768             memcpy(xspec + 1, "8;5;", 4);
  769             xspec += 5;
  770             for(ftno = 2 + sizeof("255"); cfg[ftno] != '\0'; ++ftno)
  771                *xspec++ = cfg[ftno];
  772          }
  773       }
  774 
  775       *xspec++ = 'm';
  776    }
  777    *xspec = '\0';
  778    *store = n_UNCONST(spec);
  779    rv = TRU1;
  780 jleave:
  781    NYD_LEAVE;
  782    return rv;
  783 }
  784 
  785 FL int
  786 c_colour(void *v){
  787    int rv;
  788    NYD_ENTER;
  789 
  790    rv = !a_colour_mux(v);
  791    NYD_LEAVE;
  792    return rv;
  793 }
  794 
  795 FL int
  796 c_uncolour(void *v){
  797    int rv;
  798    NYD_ENTER;
  799 
  800    rv = !a_colour_unmux(v);
  801    NYD_LEAVE;
  802    return rv;
  803 }
  804 
  805 FL void
  806 n_colour_stack_del(struct n_go_data_ctx *gdcp){
  807    struct n_colour_env *vp, *cep;
  808    NYD_ENTER;
  809 
  810    vp = gdcp->gdc_colour;
  811    gdcp->gdc_colour = NULL;
  812    gdcp->gdc_colour_active = FAL0;
  813 
  814    while((cep = vp) != NULL){
  815       vp = cep->ce_last;
  816 
  817       if(cep->ce_current != NULL && cep->ce_outfp == n_stdout){
  818          n_sighdl_t hdl;
  819 
  820          hdl = n_signal(SIGPIPE, SIG_IGN);
  821          fwrite(a_colour_g.cg_reset.cp_dat.s, a_colour_g.cg_reset.cp_dat.l, 1,
  822             cep->ce_outfp);
  823          fflush(cep->ce_outfp);
  824          n_signal(SIGPIPE, hdl);
  825       }
  826    }
  827    NYD_LEAVE;
  828 }
  829 
  830 FL void
  831 n_colour_env_create(enum n_colour_ctx cctx, FILE *fp, bool_t pager_used){
  832    struct n_colour_env *cep;
  833    NYD_ENTER;
  834 
  835    if(!(n_psonce & n_PSO_INTERACTIVE))
  836       goto jleave;
  837 
  838    if(!a_colour_g.cg_is_init)
  839       a_colour_init();
  840 
  841    /* TODO reset the outer level?  Iff ce_outfp==fp? */
  842    cep = n_autorec_alloc(sizeof *cep);
  843    cep->ce_last = n_go_data->gdc_colour;
  844    cep->ce_enabled = FAL0;
  845    cep->ce_ctx = cctx;
  846    cep->ce_ispipe = pager_used;
  847    cep->ce_outfp = fp;
  848    cep->ce_current = NULL;
  849    n_go_data->gdc_colour_active = FAL0;
  850    n_go_data->gdc_colour = cep;
  851 
  852    if(ok_blook(colour_disable) || (pager_used && !ok_blook(colour_pager)))
  853       goto jleave;
  854 
  855    if(n_UNLIKELY(a_colour_g.cg_type == a_COLOUR_T_UNKNOWN)){
  856       struct n_termcap_value tv;
  857 
  858       if(!n_termcap_query(n_TERMCAP_QUERY_colors, &tv)){
  859          a_colour_g.cg_type = a_COLOUR_T_NONE;
  860          goto jleave;
  861       }else
  862          switch(tv.tv_data.tvd_numeric){
  863          case 256: a_colour_g.cg_type = a_COLOUR_T_256; break;
  864          case 8: a_colour_g.cg_type = a_COLOUR_T_8; break;
  865          case 1: a_colour_g.cg_type = a_COLOUR_T_1; break;
  866          default:
  867             if(n_poption & n_PO_D_V)
  868                n_err(_("Ignoring unsupported termcap entry for Co(lors)\n"));
  869             /* FALLTHRU */
  870          case 0:
  871             a_colour_g.cg_type = a_COLOUR_T_NONE;
  872             goto jleave;
  873          }
  874    }
  875 
  876    if(a_colour_g.cg_type == a_COLOUR_T_NONE)
  877       goto jleave;
  878 
  879    n_go_data->gdc_colour_active = cep->ce_enabled = TRU1;
  880 jleave:
  881    NYD_LEAVE;
  882 }
  883 
  884 FL void
  885 n_colour_env_gut(void){
  886    struct n_colour_env *cep;
  887    NYD_ENTER;
  888 
  889    if(!(n_psonce & n_PSO_INTERACTIVE))
  890       goto jleave;
  891 
  892    /* TODO v15: Could happen because of jump, causing _stack_del().. */
  893    if((cep = n_go_data->gdc_colour) == NULL)
  894       goto jleave;
  895    n_go_data->gdc_colour_active = ((n_go_data->gdc_colour = cep->ce_last
  896          ) != NULL && cep->ce_last->ce_enabled);
  897 
  898    if(cep->ce_current != NULL){
  899       n_sighdl_t hdl;
  900 
  901       hdl = n_signal(SIGPIPE, SIG_IGN);
  902       fwrite(a_colour_g.cg_reset.cp_dat.s, a_colour_g.cg_reset.cp_dat.l, 1,
  903          cep->ce_outfp);
  904       n_signal(SIGPIPE, hdl);
  905    }
  906 jleave:
  907    NYD_LEAVE;
  908 }
  909 
  910 FL void
  911 n_colour_put(enum n_colour_id cid, char const *ctag){
  912    NYD_ENTER;
  913    if(n_COLOUR_IS_ACTIVE()){
  914       struct n_colour_env *cep;
  915 
  916       cep = n_go_data->gdc_colour;
  917 
  918       if(cep->ce_current != NULL)
  919          fwrite(a_colour_g.cg_reset.cp_dat.s, a_colour_g.cg_reset.cp_dat.l, 1,
  920             cep->ce_outfp);
  921 
  922       if((cep->ce_current = a_colour_map_find(cid, cep->ce_ctx, ctag)) != NULL)
  923          fwrite(cep->ce_current->cm_pen.cp_dat.s,
  924             cep->ce_current->cm_pen.cp_dat.l, 1, cep->ce_outfp);
  925    }
  926    NYD_LEAVE;
  927 }
  928 
  929 FL void
  930 n_colour_reset(void){
  931    NYD_ENTER;
  932    if(n_COLOUR_IS_ACTIVE()){
  933       struct n_colour_env *cep;
  934 
  935       cep = n_go_data->gdc_colour;
  936 
  937       if(cep->ce_current != NULL){
  938          cep->ce_current = NULL;
  939          fwrite(a_colour_g.cg_reset.cp_dat.s, a_colour_g.cg_reset.cp_dat.l, 1,
  940             cep->ce_outfp);
  941       }
  942    }
  943    NYD_LEAVE;
  944 }
  945 
  946 FL struct str const *
  947 n_colour_reset_to_str(void){
  948    struct str *rv;
  949    NYD_ENTER;
  950 
  951    if(n_COLOUR_IS_ACTIVE())
  952       rv = &a_colour_g.cg_reset.cp_dat;
  953    else
  954       rv = NULL;
  955    NYD_LEAVE;
  956    return rv;
  957 }
  958 
  959 FL struct n_colour_pen *
  960 n_colour_pen_create(enum n_colour_id cid, char const *ctag){
  961    struct a_colour_map *cmp;
  962    struct n_colour_pen *rv;
  963    NYD_ENTER;
  964 
  965    if(n_COLOUR_IS_ACTIVE() &&
  966          (cmp = a_colour_map_find(cid, n_go_data->gdc_colour->ce_ctx, ctag)
  967           ) != NULL){
  968       union {void *vp; char *cp; struct n_colour_pen *cpp;} u;
  969 
  970       u.vp = cmp;
  971       rv = u.cpp;
  972    }else
  973       rv = NULL;
  974    NYD_LEAVE;
  975    return rv;
  976 }
  977 
  978 FL void
  979 n_colour_pen_put(struct n_colour_pen *self){
  980    NYD_ENTER;
  981    if(n_COLOUR_IS_ACTIVE()){
  982       union {void *vp; char *cp; struct a_colour_map *cmp;} u;
  983       struct n_colour_env *cep;
  984 
  985       cep = n_go_data->gdc_colour;
  986       u.vp = self;
  987 
  988       if(u.cmp != cep->ce_current){
  989          if(cep->ce_current != NULL)
  990             fwrite(a_colour_g.cg_reset.cp_dat.s, a_colour_g.cg_reset.cp_dat.l,
  991                1, cep->ce_outfp);
  992 
  993          if(u.cmp != NULL)
  994             fwrite(self->cp_dat.s, self->cp_dat.l, 1, cep->ce_outfp);
  995          cep->ce_current = u.cmp;
  996       }
  997    }
  998    NYD_LEAVE;
  999 }
 1000 
 1001 FL struct str const *
 1002 n_colour_pen_to_str(struct n_colour_pen *self){
 1003    struct str *rv;
 1004    NYD_ENTER;
 1005 
 1006    if(n_COLOUR_IS_ACTIVE() && self != NULL)
 1007       rv = &self->cp_dat;
 1008    else
 1009       rv = NULL;
 1010    NYD_LEAVE;
 1011    return rv;
 1012 }
 1013 #endif /* HAVE_COLOUR */
 1014 
 1015 /* s-it-mode */