"Fossies" - the Fresh Open Source Software Archive

Member "s-nail-14.9.11/ignore.c" (8 Aug 2018, 20870 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 "ignore.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  *@ `headerpick', `retain' and `ignore', and `un..' variants.
    3  *
    4  * Copyright (c) 2012 - 2018 Steffen (Daode) Nurpmeso <steffen@sdaoden.eu>.
    5  * SPDX-License-Identifier: ISC
    6  *
    7  * Permission to use, copy, modify, and/or distribute this software for any
    8  * purpose with or without fee is hereby granted, provided that the above
    9  * copyright notice and this permission notice appear in all copies.
   10  *
   11  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
   12  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
   13  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
   14  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
   15  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
   16  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
   17  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
   18  */
   19 #undef n_FILE
   20 #define n_FILE ignore
   21 
   22 #ifndef HAVE_AMALGAMATION
   23 # include "nail.h"
   24 #endif
   25 
   26 struct a_ignore_type{
   27    ui32_t it_count;     /* Entries in .it_ht (and .it_re) */
   28    bool_t it_all;       /* _All_ fields ought to be _type_ (ignore/retain) */
   29    ui8_t it__dummy[3];
   30    struct a_ignore_field{
   31       struct a_ignore_field *if_next;
   32       char if_field[n_VFIELD_SIZE(0)]; /* Header field */
   33    } *it_ht[3]; /* TODO make hashmap dynamic */
   34 #ifdef HAVE_REGEX
   35    struct a_ignore_re{
   36       struct a_ignore_re *ir_next;
   37       regex_t ir_regex;
   38       char ir_input[n_VFIELD_SIZE(0)]; /* Regex input text (for showing it) */
   39    } *it_re, *it_re_tail;
   40 #endif
   41 };
   42 
   43 struct n_ignore{
   44    struct a_ignore_type i_retain;
   45    struct a_ignore_type i_ignore;
   46    bool_t i_auto;       /* In auto-reclaimed, not heap memory */
   47    bool_t i_bltin;      /* Is a built-in n_IGNORE* type */
   48    ui8_t i_ibm_idx;     /* If .i_bltin: a_ignore_bltin_map[] idx */
   49    ui8_t i__dummy[5];
   50 };
   51 
   52 struct a_ignore_bltin_map{
   53    struct n_ignore *ibm_ip;
   54    char const ibm_name[8];
   55 };
   56 
   57 static struct a_ignore_bltin_map const a_ignore_bltin_map[] = {
   58    {n_IGNORE_TYPE, "type\0"},
   59    {n_IGNORE_SAVE, "save\0"},
   60    {n_IGNORE_FWD, "forward\0"},
   61    {n_IGNORE_TOP, "top\0"},
   62 
   63    {n_IGNORE_TYPE, "print\0"},
   64    {n_IGNORE_FWD, "fwd\0"}
   65 };
   66 #ifdef HAVE_DEVEL /* Avoid gcc warn cascade since n_ignore is defined locally */
   67 n_CTAV(-n__IGNORE_TYPE - n__IGNORE_ADJUST == 0);
   68 n_CTAV(-n__IGNORE_SAVE - n__IGNORE_ADJUST == 1);
   69 n_CTAV(-n__IGNORE_FWD - n__IGNORE_ADJUST == 2);
   70 n_CTAV(-n__IGNORE_TOP - n__IGNORE_ADJUST == 3);
   71 n_CTAV(n__IGNORE_MAX == 3);
   72 #endif
   73 
   74 static struct n_ignore *a_ignore_bltin[n__IGNORE_MAX + 1];
   75 /* Almost everyone uses `ignore'/`retain', put _TYPE in BSS */
   76 static struct n_ignore a_ignore_type;
   77 
   78 /* Return real self, which is xself unless that is one of the built-in specials,
   79  * in which case NULL is returned if nonexistent and docreate is false.
   80  * The other statics assume self has been resolved (unless noted) */
   81 static struct n_ignore *a_ignore_resolve_self(struct n_ignore *xself,
   82                            bool_t docreate);
   83 
   84 /* Lookup whether a mapping is contained: TRU1=retained, TRUM1=ignored.
   85  * If retain is _not_ TRUM1 then only the retained/ignored slot is inspected,
   86  * and regular expressions are not executed but instead their .ir_input is
   87  * text-compared against len bytes of dat.
   88  * Note it doesn't handle the .it_all "all fields" condition */
   89 static bool_t a_ignore_lookup(struct n_ignore const *self, bool_t retain,
   90                char const *dat, size_t len);
   91 
   92 /* Delete all retain( else ignor)ed members */
   93 static void a_ignore_del_allof(struct n_ignore *ip, bool_t retain);
   94 
   95 /* Try to map a string to one of the built-in types */
   96 static struct a_ignore_bltin_map const *a_ignore_resolve_bltin(char const *cp);
   97 
   98 /* Logic behind `headerpick T T' (a.k.a. `retain'+) */
   99 static bool_t a_ignore_addcmd_mux(struct n_ignore *ip, char const **list,
  100                bool_t retain);
  101 
  102 static void a_ignore__show(struct n_ignore const *ip, bool_t retain);
  103 static int a_ignore__cmp(void const *l, void const *r);
  104 
  105 /* Logic behind `unheaderpick T T' (a.k.a. `unretain'+) */
  106 static bool_t a_ignore_delcmd_mux(struct n_ignore *ip, char const **list,
  107                bool_t retain);
  108 
  109 static bool_t a_ignore__delone(struct n_ignore *ip, bool_t retain,
  110                char const *field);
  111 
  112 static struct n_ignore *
  113 a_ignore_resolve_self(struct n_ignore *xself, bool_t docreate){
  114    uintptr_t suip;
  115    struct n_ignore *self;
  116    NYD2_ENTER;
  117 
  118    self = xself;
  119    suip = -(uintptr_t)self - n__IGNORE_ADJUST;
  120 
  121    if(suip <= n__IGNORE_MAX){
  122       if((self = a_ignore_bltin[suip]) == NULL && docreate){
  123          if(xself == n_IGNORE_TYPE){
  124             self = &a_ignore_type;
  125             /* LIB: memset(self, 0, sizeof *self);*/
  126          }else
  127             self = n_ignore_new(FAL0);
  128          self->i_bltin = TRU1;
  129          self->i_ibm_idx = (ui8_t)suip;
  130          a_ignore_bltin[suip] = self;
  131       }
  132    }
  133    NYD2_LEAVE;
  134    return self;
  135 }
  136 
  137 static bool_t
  138 a_ignore_lookup(struct n_ignore const *self, bool_t retain,
  139       char const *dat, size_t len){
  140    bool_t rv;
  141 #ifdef HAVE_REGEX
  142    struct a_ignore_re *irp;
  143 #endif
  144    struct a_ignore_field *ifp;
  145    ui32_t hi;
  146    NYD2_ENTER;
  147 
  148    if(len == UIZ_MAX)
  149       len = strlen(dat);
  150    hi = n_torek_ihashn(dat, len) % n_NELEM(self->i_retain.it_ht);
  151 
  152    /* Again: doesn't handle .it_all conditions! */
  153    /* (Inner functions would be nice, again) */
  154    if(retain && self->i_retain.it_count > 0){
  155       rv = TRU1;
  156       for(ifp = self->i_retain.it_ht[hi]; ifp != NULL; ifp = ifp->if_next)
  157          if(!ascncasecmp(ifp->if_field, dat, len))
  158             goto jleave;
  159 #ifdef HAVE_REGEX
  160       if(dat[len - 1] != '\0')
  161          dat = savestrbuf(dat, len);
  162       for(irp = self->i_retain.it_re; irp != NULL; irp = irp->ir_next)
  163          if((retain == TRUM1
  164                ? (regexec(&irp->ir_regex, dat, 0,NULL, 0) != REG_NOMATCH)
  165                : !strncmp(irp->ir_input, dat, len)))
  166             goto jleave;
  167 #endif
  168       rv = (retain == TRUM1) ? TRUM1 : FAL0;
  169    }else if((retain == TRUM1 || !retain) && self->i_ignore.it_count > 0){
  170       rv = TRUM1;
  171       for(ifp = self->i_ignore.it_ht[hi]; ifp != NULL; ifp = ifp->if_next)
  172          if(!ascncasecmp(ifp->if_field, dat, len))
  173             goto jleave;
  174 #ifdef HAVE_REGEX
  175       if(dat[len - 1] != '\0')
  176          dat = savestrbuf(dat, len);
  177       for(irp = self->i_ignore.it_re; irp != NULL; irp = irp->ir_next)
  178          if((retain == TRUM1
  179                ? (regexec(&irp->ir_regex, dat, 0,NULL, 0) != REG_NOMATCH)
  180                : !strncmp(irp->ir_input, dat, len)))
  181             goto jleave;
  182 #endif
  183       rv = (retain == TRUM1) ? TRU1 : FAL0;
  184    }else
  185       rv = FAL0;
  186 jleave:
  187    NYD2_LEAVE;
  188    return rv;
  189 }
  190 
  191 static void
  192 a_ignore_del_allof(struct n_ignore *ip, bool_t retain){
  193 #ifdef HAVE_REGEX
  194    struct a_ignore_re *irp;
  195 #endif
  196    struct a_ignore_field *ifp;
  197    struct a_ignore_type *itp;
  198    NYD2_ENTER;
  199 
  200    itp = retain ? &ip->i_retain : &ip->i_ignore;
  201 
  202    if(!ip->i_auto){
  203       size_t i;
  204 
  205       for(i = 0; i < n_NELEM(itp->it_ht); ++i)
  206          for(ifp = itp->it_ht[i]; ifp != NULL;){
  207             struct a_ignore_field *x;
  208 
  209             x = ifp;
  210             ifp = ifp->if_next;
  211             n_free(x);
  212          }
  213    }
  214 
  215 #ifdef HAVE_REGEX
  216    for(irp = itp->it_re; irp != NULL;){
  217       struct a_ignore_re *x;
  218 
  219       x = irp;
  220       irp = irp->ir_next;
  221       regfree(&x->ir_regex);
  222       if(!ip->i_auto)
  223          n_free(x);
  224    }
  225 #endif
  226 
  227    memset(itp, 0, sizeof *itp);
  228    NYD2_LEAVE;
  229 }
  230 
  231 static struct a_ignore_bltin_map const *
  232 a_ignore_resolve_bltin(char const *cp){
  233    struct a_ignore_bltin_map const *ibmp;
  234    NYD2_ENTER;
  235 
  236    for(ibmp = &a_ignore_bltin_map[0];;)
  237       if(!asccasecmp(cp, ibmp->ibm_name))
  238          break;
  239       else if(++ibmp == &a_ignore_bltin_map[n_NELEM(a_ignore_bltin_map)]){
  240          ibmp = NULL;
  241          break;
  242       }
  243    NYD2_LEAVE;
  244    return ibmp;
  245 }
  246 
  247 static bool_t
  248 a_ignore_addcmd_mux(struct n_ignore *ip, char const **list, bool_t retain){
  249    char const **ap;
  250    bool_t rv;
  251    NYD2_ENTER;
  252 
  253    ip = a_ignore_resolve_self(ip, rv = (*list != NULL));
  254 
  255    if(!rv){
  256       if(ip != NULL && ip->i_bltin)
  257          a_ignore__show(ip, retain);
  258       rv = TRU1;
  259    }else{
  260       for(ap = list; *ap != 0; ++ap)
  261          switch(n_ignore_insert_cp(ip, retain, *ap)){
  262          case FAL0:
  263             n_err(_("Invalid field name cannot be %s: %s\n"),
  264                (retain ? _("retained") : _("ignored")), *ap);
  265             rv = FAL0;
  266             break;
  267          case TRUM1:
  268             if(n_poption & n_PO_D_V)
  269                n_err(_("Field already %s: %s\n"),
  270                   (retain ? _("retained") : _("ignored")), *ap);
  271             /* FALLTHRU */
  272          case TRU1:
  273             break;
  274          }
  275    }
  276    NYD2_LEAVE;
  277    return rv;
  278 }
  279 
  280 static void
  281 a_ignore__show(struct n_ignore const *ip, bool_t retain){
  282 #ifdef HAVE_REGEX
  283    struct a_ignore_re *irp;
  284 #endif
  285    struct a_ignore_field *ifp;
  286    size_t i, sw;
  287    char const **ap, **ring;
  288    struct a_ignore_type const *itp;
  289    NYD2_ENTER;
  290 
  291    itp = retain ? &ip->i_retain : &ip->i_ignore;
  292 
  293    do{
  294       char const *pre, *attr;
  295 
  296       if(itp->it_all)
  297          pre = n_empty, attr = n_star;
  298       else if(itp->it_count == 0)
  299          pre = n_ns, attr = _("currently covers no fields");
  300       else
  301          break;
  302       fprintf(n_stdout, _("%sheaderpick %s %s %s\n"),
  303          pre, a_ignore_bltin_map[ip->i_ibm_idx].ibm_name,
  304          (retain ? "retain" : "ignore"), attr);
  305       goto jleave;
  306    }while(0);
  307 
  308    ring = n_autorec_alloc((itp->it_count +1) * sizeof *ring);
  309    for(ap = ring, i = 0; i < n_NELEM(itp->it_ht); ++i)
  310       for(ifp = itp->it_ht[i]; ifp != NULL; ifp = ifp->if_next)
  311          *ap++ = ifp->if_field;
  312    *ap = NULL;
  313 
  314    qsort(ring, PTR2SIZE(ap - ring), sizeof *ring, &a_ignore__cmp);
  315 
  316    i = fprintf(n_stdout, "headerpick %s %s",
  317       a_ignore_bltin_map[ip->i_ibm_idx].ibm_name,
  318       (retain ? "retain" : "ignore"));
  319    sw = n_scrnwidth;
  320 
  321    for(ap = ring; *ap != NULL; ++ap){
  322       /* These fields are all ASCII, no visual width needed */
  323       size_t len;
  324 
  325       len = strlen(*ap) + 1;
  326       if(UICMP(z, len, >=, sw - i)){
  327          fputs(" \\\n ", n_stdout);
  328          i = 1;
  329       }
  330       i += len;
  331       putc(' ', n_stdout);
  332       fputs(*ap, n_stdout);
  333    }
  334 
  335    /* Regular expression in FIFO order */
  336 #ifdef HAVE_REGEX
  337    for(irp = itp->it_re; irp != NULL; irp = irp->ir_next){
  338       size_t len;
  339       char const *cp;
  340 
  341       cp = n_shexp_quote_cp(irp->ir_input, FAL0);
  342       len = strlen(cp) + 1;
  343       if(UICMP(z, len, >=, sw - i)){
  344          fputs(" \\\n ", n_stdout);
  345          i = 1;
  346       }
  347       i += len;
  348       putc(' ', n_stdout);
  349       fputs(cp, n_stdout);
  350    }
  351 #endif
  352 
  353    putc('\n', n_stdout);
  354 jleave:
  355    fflush(n_stdout);
  356    NYD2_LEAVE;
  357 }
  358 
  359 static int
  360 a_ignore__cmp(void const *l, void const *r){
  361    int rv;
  362 
  363    rv = asccasecmp(*(char const * const *)l, *(char const * const *)r);
  364    return rv;
  365 }
  366 
  367 static bool_t
  368 a_ignore_delcmd_mux(struct n_ignore *ip, char const **list, bool_t retain){
  369    char const *cp;
  370    struct a_ignore_type *itp;
  371    bool_t rv;
  372    NYD2_ENTER;
  373 
  374    ip = a_ignore_resolve_self(ip, rv = (*list != NULL));
  375    itp = retain ? &ip->i_retain : &ip->i_ignore;
  376 
  377    if(itp->it_count == 0 && !itp->it_all)
  378       n_err(_("No fields currently being %s\n"),
  379          (retain ? _("retained") : _("ignored")));
  380    else
  381       while((cp = *list++) != NULL)
  382          if(cp[0] == '*' && cp[1] == '\0')
  383             a_ignore_del_allof(ip, retain);
  384          else if(!a_ignore__delone(ip, retain, cp)){
  385             n_err(_("Field not %s: %s\n"),
  386                (retain ? _("retained") : _("ignored")), cp);
  387             rv = FAL0;
  388          }
  389    NYD2_LEAVE;
  390    return rv;
  391 }
  392 
  393 static bool_t
  394 a_ignore__delone(struct n_ignore *ip, bool_t retain, char const *field){
  395    struct a_ignore_type *itp;
  396    NYD_ENTER;
  397 
  398    itp = retain ? &ip->i_retain : &ip->i_ignore;
  399 
  400 #ifdef HAVE_REGEX
  401    if(n_is_maybe_regex(field)){
  402       struct a_ignore_re **lirp, *irp;
  403 
  404       for(irp = *(lirp = &itp->it_re); irp != NULL;
  405             lirp = &irp->ir_next, irp = irp->ir_next)
  406          if(!strcmp(field, irp->ir_input)){
  407             *lirp = irp->ir_next;
  408             if(irp == itp->it_re_tail)
  409                itp->it_re_tail = irp->ir_next;
  410 
  411             regfree(&irp->ir_regex);
  412             if(!ip->i_auto)
  413                n_free(irp);
  414             --itp->it_count;
  415             goto jleave;
  416          }
  417    }else
  418 #endif /* HAVE_REGEX */
  419    {
  420       struct a_ignore_field **ifpp, *ifp;
  421       ui32_t hi;
  422 
  423       hi = n_torek_ihashn(field, UIZ_MAX) % n_NELEM(itp->it_ht);
  424 
  425       for(ifp = *(ifpp = &itp->it_ht[hi]); ifp != NULL;
  426             ifpp = &ifp->if_next, ifp = ifp->if_next)
  427          if(!asccasecmp(ifp->if_field, field)){
  428             *ifpp = ifp->if_next;
  429             if(!ip->i_auto)
  430                n_free(ifp);
  431             --itp->it_count;
  432            goto jleave;
  433          }
  434    }
  435 
  436    ip = NULL;
  437 jleave:
  438    NYD_LEAVE;
  439    return (ip != NULL);
  440 }
  441 
  442 FL int
  443 c_headerpick(void *vp){
  444    bool_t retain;
  445    struct a_ignore_bltin_map const *ibmp;
  446    char const **argv;
  447    int rv;
  448    NYD_ENTER;
  449 
  450    rv = 1;
  451    argv = vp;
  452 
  453    /* Without arguments, show all settings of all contexts */
  454    if(*argv == NULL){
  455       rv = 0;
  456       for(ibmp = &a_ignore_bltin_map[0];
  457             ibmp <= &a_ignore_bltin_map[n__IGNORE_MAX]; ++ibmp){
  458          rv |= !a_ignore_addcmd_mux(ibmp->ibm_ip, argv, TRU1);
  459          rv |= !a_ignore_addcmd_mux(ibmp->ibm_ip, argv, FAL0);
  460       }
  461       goto jleave;
  462    }
  463 
  464    if((ibmp = a_ignore_resolve_bltin(*argv)) == NULL){
  465       n_err(_("`headerpick': invalid context: %s\n"), *argv);
  466       goto jleave;
  467    }
  468    ++argv;
  469 
  470    /* With only <context>, show all settings of it */
  471    if(*argv == NULL){
  472       rv = 0;
  473       rv |= !a_ignore_addcmd_mux(ibmp->ibm_ip, argv, TRU1);
  474       rv |= !a_ignore_addcmd_mux(ibmp->ibm_ip, argv, FAL0);
  475       goto jleave;
  476    }
  477 
  478    if(is_asccaseprefix(*argv, "retain"))
  479       retain = TRU1;
  480    else if(is_asccaseprefix(*argv, "ignore"))
  481       retain = FAL0;
  482    else{
  483       n_err(_("`headerpick': invalid type (retain, ignore): %s\n"), *argv);
  484       goto jleave;
  485    }
  486    ++argv;
  487 
  488    /* With only <context> and <type>, show its settings */
  489    if(*argv == NULL){
  490       rv = !a_ignore_addcmd_mux(ibmp->ibm_ip, argv, retain);
  491       goto jleave;
  492    }
  493 
  494    rv = !a_ignore_addcmd_mux(ibmp->ibm_ip, argv, retain);
  495 jleave:
  496    NYD_LEAVE;
  497    return rv;
  498 }
  499 
  500 FL int
  501 c_unheaderpick(void *vp){
  502    bool_t retain;
  503    struct a_ignore_bltin_map const *ibmp;
  504    char const **argv;
  505    int rv;
  506    NYD_ENTER;
  507 
  508    rv = 1;
  509    argv = vp;
  510 
  511    if((ibmp = a_ignore_resolve_bltin(*argv)) == NULL){
  512       n_err(_("`unheaderpick': invalid context: %s\n"), *argv);
  513       goto jleave;
  514    }
  515    ++argv;
  516 
  517    if(is_asccaseprefix(*argv, "retain"))
  518       retain = TRU1;
  519    else if(is_asccaseprefix(*argv, "ignore"))
  520       retain = FAL0;
  521    else{
  522       n_err(_("`unheaderpick': invalid type (retain, ignore): %s\n"), *argv);
  523       goto jleave;
  524    }
  525    ++argv;
  526 
  527    rv = !a_ignore_delcmd_mux(ibmp->ibm_ip, argv, retain);
  528 jleave:
  529    NYD_LEAVE;
  530    return rv;
  531 }
  532 
  533 FL int
  534 c_retain(void *vp){
  535    int rv;
  536    NYD_ENTER;
  537 
  538    rv = !a_ignore_addcmd_mux(n_IGNORE_TYPE, vp, TRU1);
  539    NYD_LEAVE;
  540    return rv;
  541 }
  542 
  543 FL int
  544 c_ignore(void *vp){
  545    int rv;
  546    NYD_ENTER;
  547 
  548    rv = !a_ignore_addcmd_mux(n_IGNORE_TYPE, vp, FAL0);
  549    NYD_LEAVE;
  550    return rv;
  551 }
  552 
  553 FL int
  554 c_unretain(void *vp){
  555    int rv;
  556    NYD_ENTER;
  557 
  558    rv = !a_ignore_delcmd_mux(n_IGNORE_TYPE, vp, TRU1);
  559    NYD_LEAVE;
  560    return rv;
  561 }
  562 
  563 FL int
  564 c_unignore(void *vp){
  565    int rv;
  566    NYD_ENTER;
  567 
  568    rv = !a_ignore_delcmd_mux(n_IGNORE_TYPE, vp, FAL0);
  569    NYD_LEAVE;
  570    return rv;
  571 }
  572 
  573 FL int
  574 c_saveretain(void *v){ /* TODO v15 drop */
  575    int rv;
  576    NYD_ENTER;
  577 
  578    rv = !a_ignore_addcmd_mux(n_IGNORE_SAVE, v, TRU1);
  579    NYD_LEAVE;
  580    return rv;
  581 }
  582 
  583 FL int
  584 c_saveignore(void *v){ /* TODO v15 drop */
  585    int rv;
  586    NYD_ENTER;
  587 
  588    rv = !a_ignore_addcmd_mux(n_IGNORE_SAVE, v, FAL0);
  589    NYD_LEAVE;
  590    return rv;
  591 }
  592 
  593 FL int
  594 c_unsaveretain(void *v){ /* TODO v15 drop */
  595    int rv;
  596    NYD_ENTER;
  597 
  598    rv = !a_ignore_delcmd_mux(n_IGNORE_SAVE, v, TRU1);
  599    NYD_LEAVE;
  600    return rv;
  601 }
  602 
  603 FL int
  604 c_unsaveignore(void *v){ /* TODO v15 drop */
  605    int rv;
  606    NYD_ENTER;
  607 
  608    rv = !a_ignore_delcmd_mux(n_IGNORE_SAVE, v, FAL0);
  609    NYD_LEAVE;
  610    return rv;
  611 }
  612 
  613 FL int
  614 c_fwdretain(void *v){ /* TODO v15 drop */
  615    int rv;
  616    NYD_ENTER;
  617 
  618    rv = !a_ignore_addcmd_mux(n_IGNORE_FWD, v, TRU1);
  619    NYD_LEAVE;
  620    return rv;
  621 }
  622 
  623 FL int
  624 c_fwdignore(void *v){ /* TODO v15 drop */
  625    int rv;
  626    NYD_ENTER;
  627 
  628    rv = !a_ignore_addcmd_mux(n_IGNORE_FWD, v, FAL0);
  629    NYD_LEAVE;
  630    return rv;
  631 }
  632 
  633 FL int
  634 c_unfwdretain(void *v){ /* TODO v15 drop */
  635    int rv;
  636    NYD_ENTER;
  637 
  638    rv = !a_ignore_delcmd_mux(n_IGNORE_FWD, v, TRU1);
  639    NYD_LEAVE;
  640    return rv;
  641 }
  642 
  643 FL int
  644 c_unfwdignore(void *v){ /* TODO v15 drop */
  645    int rv;
  646    NYD_ENTER;
  647 
  648    rv = !a_ignore_delcmd_mux(n_IGNORE_FWD, v, FAL0);
  649    NYD_LEAVE;
  650    return rv;
  651 }
  652 
  653 FL struct n_ignore *
  654 n_ignore_new(bool_t isauto){
  655    struct n_ignore *self;
  656    NYD_ENTER;
  657 
  658    self = isauto ? n_autorec_calloc(1, sizeof *self) : n_calloc(1,sizeof *self);
  659    self->i_auto = isauto;
  660    NYD_LEAVE;
  661    return self;
  662 }
  663 
  664 FL void
  665 n_ignore_del(struct n_ignore *self){
  666    NYD_ENTER;
  667    a_ignore_del_allof(self, TRU1);
  668    a_ignore_del_allof(self, FAL0);
  669    if(!self->i_auto)
  670       n_free(self);
  671    NYD_LEAVE;
  672 }
  673 
  674 FL bool_t
  675 n_ignore_is_any(struct n_ignore const *self){
  676    bool_t rv;
  677    NYD_ENTER;
  678 
  679    self = a_ignore_resolve_self(n_UNCONST(self), FAL0);
  680    rv = (self != NULL &&
  681          (self->i_retain.it_count != 0 || self->i_retain.it_all ||
  682           self->i_ignore.it_count != 0 || self->i_ignore.it_all));
  683    NYD_LEAVE;
  684    return rv;
  685 }
  686 
  687 FL bool_t
  688 n_ignore_insert(struct n_ignore *self, bool_t retain,
  689       char const *dat, size_t len){
  690 #ifdef HAVE_REGEX
  691    struct a_ignore_re *irp;
  692    bool_t isre;
  693 #endif
  694    struct a_ignore_field *ifp;
  695    struct a_ignore_type *itp;
  696    bool_t rv;
  697    NYD_ENTER;
  698 
  699    retain = !!retain; /* Make it true bool, TRUM1 has special _lookup meaning */
  700    rv = FAL0;
  701    self = a_ignore_resolve_self(self, TRU1);
  702 
  703    if(len == UIZ_MAX)
  704       len = strlen(dat);
  705 
  706    /* Request to ignore or retain _anything_?  That is special-treated */
  707    if(len == 1 && dat[0] == '*'){
  708       itp = retain ? &self->i_retain : &self->i_ignore;
  709       if(itp->it_all)
  710          rv = TRUM1;
  711       else{
  712          itp->it_all = TRU1;
  713          a_ignore_del_allof(self, retain);
  714          rv = TRU1;
  715       }
  716       goto jleave;
  717    }
  718 
  719    /* Check for regular expression or valid fieldname */
  720 #ifdef HAVE_REGEX
  721    if(!(isre = n_is_maybe_regex_buf(dat, len)))
  722 #endif
  723    {
  724       char c;
  725       size_t i;
  726 
  727       for(i = 0; i < len; ++i){
  728          c = dat[i];
  729          if(!fieldnamechar(c))
  730             goto jleave;
  731       }
  732    }
  733 
  734    rv = TRUM1;
  735    if(a_ignore_lookup(self, retain, dat, len) == (retain ? TRU1 : TRUM1))
  736       goto jleave;
  737 
  738    itp = retain ? &self->i_retain : &self->i_ignore;
  739 
  740    if(itp->it_count == UI32_MAX){
  741       n_err(_("Header selection size limit reached, cannot insert: %.*s\n"),
  742          (int)n_MIN(len, SI32_MAX), dat);
  743       rv = FAL0;
  744       goto jleave;
  745    }
  746 
  747    rv = TRU1;
  748 #ifdef HAVE_REGEX
  749    if(isre){
  750       struct a_ignore_re *x;
  751       int s;
  752       size_t i;
  753 
  754       i = n_VSTRUCT_SIZEOF(struct a_ignore_re, ir_input) + ++len;
  755       irp = self->i_auto ? n_autorec_alloc(i) : n_alloc(i);
  756       memcpy(irp->ir_input, dat, --len);
  757       irp->ir_input[len] = '\0';
  758 
  759       if((s = regcomp(&irp->ir_regex, irp->ir_input,
  760             REG_EXTENDED | REG_ICASE | REG_NOSUB)) != 0){
  761          n_err(_("Invalid regular expression: %s: %s\n"),
  762             n_shexp_quote_cp(irp->ir_input, FAL0),
  763             n_regex_err_to_doc(NULL, s));
  764          if(!self->i_auto)
  765             n_free(irp);
  766          rv = FAL0;
  767          goto jleave;
  768       }
  769 
  770       irp->ir_next = NULL;
  771       if((x = itp->it_re_tail) != NULL)
  772          x->ir_next = irp;
  773       else
  774          itp->it_re = irp;
  775       itp->it_re_tail = irp;
  776    }else
  777 #endif /* HAVE_REGEX */
  778    {
  779       ui32_t hi;
  780       size_t i;
  781 
  782       i = n_VSTRUCT_SIZEOF(struct a_ignore_field, if_field) + len + 1;
  783       ifp = self->i_auto ? n_autorec_alloc(i) : n_alloc(i);
  784       memcpy(ifp->if_field, dat, len);
  785       ifp->if_field[len] = '\0';
  786       hi = n_torek_ihashn(dat, len) % n_NELEM(itp->it_ht);
  787       ifp->if_next = itp->it_ht[hi];
  788       itp->it_ht[hi] = ifp;
  789    }
  790    ++itp->it_count;
  791 jleave:
  792    NYD_LEAVE;
  793    return rv;
  794 }
  795 
  796 FL bool_t
  797 n_ignore_lookup(struct n_ignore const *self, char const *dat, size_t len){
  798    bool_t rv;
  799    NYD_ENTER;
  800 
  801    if(self == n_IGNORE_ALL)
  802       rv = TRUM1;
  803    else if(len == 0 ||
  804          (self = a_ignore_resolve_self(n_UNCONST(self), FAL0)) == NULL)
  805       rv = FAL0;
  806    else if(self->i_retain.it_all)
  807       rv = TRU1;
  808    else if(self->i_retain.it_count == 0 && self->i_ignore.it_all)
  809       rv = TRUM1;
  810    else
  811       rv = a_ignore_lookup(self, TRUM1, dat, len);
  812    NYD_LEAVE;
  813    return rv;
  814 }
  815 
  816 /* s-it-mode */