"Fossies" - the Fresh Open Source Software Archive

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