Obsolete -E and -B (-> STDOUT always LBF, STDIN with -#)
[s-mailx.git] / ignore.c
blob39a8a00035238d19a6bcda9c1e6a1677abe1013c
1 /*@ S-nail - a mail user agent derived from Berkeley Mail.
2 *@ `headerpick', `retain' and `ignore'.
3 *@ XXX Should these be in nam_a_grp.c?!
5 * Copyright (c) 2012 - 2016 Steffen (Daode) Nurpmeso <steffen@sdaoden.eu>.
6 */
7 /*
8 * Copyright (c) 1980, 1993
9 * The Regents of the University of California. All rights reserved.
11 * Redistribution and use in source and binary forms, with or without
12 * modification, are permitted provided that the following conditions
13 * are met:
14 * 1. Redistributions of source code must retain the above copyright
15 * notice, this list of conditions and the following disclaimer.
16 * 2. Redistributions in binary form must reproduce the above copyright
17 * notice, this list of conditions and the following disclaimer in the
18 * documentation and/or other materials provided with the distribution.
19 * 3. Neither the name of the University nor the names of its contributors
20 * may be used to endorse or promote products derived from this software
21 * without specific prior written permission.
23 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
24 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
27 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33 * SUCH DAMAGE.
35 #undef n_FILE
36 #define n_FILE ignore
38 #ifndef HAVE_AMALGAMATION
39 # include "nail.h"
40 #endif
42 struct a_ignore_type{
43 ui32_t it_count; /* Entries in .it_ht (and .it_re) */
44 bool_t it_all; /* _All_ fields ought to be _type_ (ignore/retain) */
45 ui8_t it__dummy[3];
46 struct a_ignore_field{
47 struct a_ignore_field *if_next;
48 char if_field[n_VFIELD_SIZE(0)]; /* Header field */
49 } *it_ht[3]; /* TODO make hashmap dynamic */
50 #ifdef HAVE_REGEX
51 struct a_ignore_re{
52 struct a_ignore_re *ir_next;
53 regex_t ir_regex;
54 char ir_input[n_VFIELD_SIZE(0)]; /* Regex input text (for showing it) */
55 } *it_re, *it_re_tail;
56 #endif
59 struct n_ignore{
60 struct a_ignore_type i_retain;
61 struct a_ignore_type i_ignore;
62 bool_t i_auto; /* In auto-reclaimed, not heap memory */
63 bool_t i_bltin; /* Is a builtin n_IGNORE* type */
64 ui8_t i_ibm_idx; /* If .i_bltin: a_ignore_bltin_map[] idx */
65 ui8_t i__dummy[5];
68 struct a_ignore_bltin_map{
69 struct n_ignore *ibm_ip;
70 char const ibm_name[8];
73 static struct a_ignore_bltin_map const a_ignore_bltin_map[] = {
74 {n_IGNORE_TYPE, "type\0"},
75 {n_IGNORE_SAVE, "save\0"},
76 {n_IGNORE_FWD, "forward\0"},
77 {n_IGNORE_TOP, "top\0"},
79 {n_IGNORE_TYPE, "print\0"},
80 {n_IGNORE_FWD, "fwd\0"}
82 #ifdef HAVE_DEVEL /* Avoid gcc warn cascade since n_ignore is defined locally */
83 n_CTAV(-(uintptr_t)n_IGNORE_TYPE - n__IGNORE_ADJUST == 0);
84 n_CTAV(-(uintptr_t)n_IGNORE_SAVE - n__IGNORE_ADJUST == 1);
85 n_CTAV(-(uintptr_t)n_IGNORE_FWD - n__IGNORE_ADJUST == 2);
86 n_CTAV(-(uintptr_t)n_IGNORE_TOP - n__IGNORE_ADJUST == 3);
87 n_CTAV(n__IGNORE_MAX == 3);
88 #endif
90 static struct n_ignore *a_ignore_bltin[n__IGNORE_MAX + 1];
91 /* Almost everyone uses `ignore'/`retain', put _TYPE in BSS */
92 static struct n_ignore a_ignore_type;
94 /* Return real self, which is xself unless that is one of the builtin specials,
95 * in which case NULL is returned if nonexistent and docreate is false.
96 * The other statics assume self has been resolved (unless noted) */
97 static struct n_ignore *a_ignore_resolve_self(struct n_ignore *xself,
98 bool_t docreate);
100 /* Lookup whether a mapping is contained: TRU1=retained, TRUM1=ignored.
101 * If retain is _not_ TRUM1 then only the retained/ignored slot is inspected,
102 * and regular expressions are not executed but instead their .ir_input is
103 * text-compared against len bytes of dat.
104 * Note it doesn't handle the .it_all "all fields" condition */
105 static bool_t a_ignore_lookup(struct n_ignore const *self, bool_t retain,
106 char const *dat, size_t len);
108 /* Delete all retain( else ignor)ed members */
109 static void a_ignore_del_allof(struct n_ignore *ip, bool_t retain);
111 /* Try to map a string to one of the builtin types */
112 static struct a_ignore_bltin_map const *a_ignore_resolve_bltin(char const *cp);
114 /* Logic behind `headerpick T T add' (a.k.a. `retain'+) */
115 static bool_t a_ignore_addcmd_mux(struct n_ignore *ip, char const **list,
116 bool_t retain);
118 static void a_ignore__show(struct n_ignore const *ip, bool_t retain);
119 static int a_ignore__cmp(void const *l, void const *r);
121 /* Logic behind `headerpick T T remove' (a.k.a. `unretain'+) */
122 static bool_t a_ignore_delcmd_mux(struct n_ignore *ip, char const **list,
123 bool_t retain);
125 static bool_t a_ignore__delone(struct n_ignore *ip, bool_t retain,
126 char const *field);
128 static struct n_ignore *
129 a_ignore_resolve_self(struct n_ignore *xself, bool_t docreate){
130 uintptr_t suip;
131 struct n_ignore *self;
132 NYD2_ENTER;
134 self = xself;
135 suip = -(uintptr_t)self - n__IGNORE_ADJUST;
137 if(suip <= n__IGNORE_MAX){
138 if((self = a_ignore_bltin[suip]) == NULL && docreate){
139 if(xself == n_IGNORE_TYPE){
140 self = &a_ignore_type;
141 /* LIB: memset(self, 0, sizeof *self);*/
142 }else
143 self = n_ignore_new(FAL0);
144 self->i_bltin = TRU1;
145 self->i_ibm_idx = (ui8_t)suip;
146 a_ignore_bltin[suip] = self;
149 NYD2_LEAVE;
150 return self;
153 static bool_t
154 a_ignore_lookup(struct n_ignore const *self, bool_t retain,
155 char const *dat, size_t len){
156 bool_t rv;
157 #ifdef HAVE_REGEX
158 struct a_ignore_re *irp;
159 #endif
160 struct a_ignore_field *ifp;
161 ui32_t hi;
162 NYD2_ENTER;
164 if(len == UIZ_MAX)
165 len = strlen(dat);
166 hi = torek_ihashn(dat, len) % n_NELEM(self->i_retain.it_ht);
168 /* Again: doesn't handle .it_all conditions! */
169 /* (Inner functions would be nice, again) */
170 if(retain && self->i_retain.it_count > 0){
171 rv = TRU1;
172 for(ifp = self->i_retain.it_ht[hi]; ifp != NULL; ifp = ifp->if_next)
173 if(!ascncasecmp(ifp->if_field, dat, len))
174 goto jleave;
175 #ifdef HAVE_REGEX
176 if(dat[len - 1] != '\0')
177 dat = savestrbuf(dat, len);
178 for(irp = self->i_retain.it_re; irp != NULL; irp = irp->ir_next)
179 if((retain == TRUM1
180 ? (regexec(&irp->ir_regex, dat, 0,NULL, 0) != REG_NOMATCH)
181 : !strncmp(irp->ir_input, dat, len)))
182 goto jleave;
183 #endif
184 rv = (retain == TRUM1) ? TRUM1 : FAL0;
185 }else if((retain == TRUM1 || !retain) && self->i_ignore.it_count > 0){
186 rv = TRUM1;
187 for(ifp = self->i_ignore.it_ht[hi]; ifp != NULL; ifp = ifp->if_next)
188 if(!ascncasecmp(ifp->if_field, dat, len))
189 goto jleave;
190 #ifdef HAVE_REGEX
191 if(dat[len - 1] != '\0')
192 dat = savestrbuf(dat, len);
193 for(irp = self->i_ignore.it_re; irp != NULL; irp = irp->ir_next)
194 if((retain == TRUM1
195 ? (regexec(&irp->ir_regex, dat, 0,NULL, 0) != REG_NOMATCH)
196 : !strncmp(irp->ir_input, dat, len)))
197 goto jleave;
198 #endif
199 rv = (retain == TRUM1) ? TRU1 : FAL0;
200 }else
201 rv = FAL0;
202 jleave:
203 NYD2_LEAVE;
204 return rv;
207 static void
208 a_ignore_del_allof(struct n_ignore *ip, bool_t retain){
209 #ifdef HAVE_REGEX
210 struct a_ignore_re *irp;
211 #endif
212 struct a_ignore_field *ifp;
213 struct a_ignore_type *itp;
214 NYD2_ENTER;
216 itp = retain ? &ip->i_retain : &ip->i_ignore;
218 if(!ip->i_auto){
219 size_t i;
221 for(i = 0; i < n_NELEM(itp->it_ht); ++i)
222 for(ifp = itp->it_ht[i]; ifp != NULL;){
223 struct a_ignore_field *x;
225 x = ifp;
226 ifp = ifp->if_next;
227 n_free(x);
231 #ifdef HAVE_REGEX
232 for(irp = itp->it_re; irp != NULL;){
233 struct a_ignore_re *x;
235 x = irp;
236 irp = irp->ir_next;
237 regfree(&x->ir_regex);
238 if(!ip->i_auto)
239 n_free(x);
241 #endif
243 memset(itp, 0, sizeof *itp);
244 NYD2_LEAVE;
247 static struct a_ignore_bltin_map const *
248 a_ignore_resolve_bltin(char const *cp){
249 struct a_ignore_bltin_map const *ibmp;
250 NYD2_ENTER;
252 for(ibmp = &a_ignore_bltin_map[0];;)
253 if(!asccasecmp(cp, ibmp->ibm_name))
254 break;
255 else if(++ibmp == &a_ignore_bltin_map[n_NELEM(a_ignore_bltin_map)]){
256 ibmp = NULL;
257 break;
259 NYD2_LEAVE;
260 return ibmp;
263 static bool_t
264 a_ignore_addcmd_mux(struct n_ignore *ip, char const **list, bool_t retain){
265 char const **ap;
266 bool_t rv;
267 NYD2_ENTER;
269 ip = a_ignore_resolve_self(ip, rv = (*list != NULL));
271 if(!rv){
272 if(ip != NULL && ip->i_bltin)
273 a_ignore__show(ip, retain);
274 rv = TRU1;
275 }else{
276 for(ap = list; *ap != 0; ++ap)
277 switch(n_ignore_insert_cp(ip, retain, *ap)){
278 case FAL0:
279 n_err(_("Invalid field name cannot be %s: %s\n"),
280 (retain ? _("retained") : _("ignored")), *ap);
281 rv = FAL0;
282 break;
283 case TRUM1:
284 if(options & OPT_D_V)
285 n_err(_("Field already %s: %s\n"),
286 (retain ? _("retained") : _("ignored")), *ap);
287 /* FALLTHRU */
288 case TRU1:
289 break;
292 NYD2_LEAVE;
293 return rv;
296 static void
297 a_ignore__show(struct n_ignore const *ip, bool_t retain){
298 #ifdef HAVE_REGEX
299 struct a_ignore_re *irp;
300 #endif
301 struct a_ignore_field *ifp;
302 size_t i, sw;
303 char const **ap, **ring;
304 struct a_ignore_type const *itp;
305 NYD2_ENTER;
307 itp = retain ? &ip->i_retain : &ip->i_ignore;
310 char const *pre, *attr;
312 if(itp->it_all)
313 pre = n_empty, attr = "*";
314 else if(itp->it_count == 0)
315 pre = "#", attr = _("currently covers no fields");
316 else
317 break;
318 printf(_("%sheaderpick %s %s %s\n"),
319 pre, a_ignore_bltin_map[ip->i_ibm_idx].ibm_name,
320 (retain ? "retain" : "ignore"), attr);
321 goto jleave;
322 }while(0);
324 ring = salloc((itp->it_count +1) * sizeof *ring);
325 for(ap = ring, i = 0; i < n_NELEM(itp->it_ht); ++i)
326 for(ifp = itp->it_ht[i]; ifp != NULL; ifp = ifp->if_next)
327 *ap++ = ifp->if_field;
328 *ap = NULL;
330 qsort(ring, PTR2SIZE(ap - ring), sizeof *ring, &a_ignore__cmp);
332 i = printf("headerpick %s %s add",
333 a_ignore_bltin_map[ip->i_ibm_idx].ibm_name,
334 (retain ? "retain" : "ignore"));
335 sw = scrnwidth;
337 for(ap = ring; *ap != NULL; ++ap){
338 /* These fields are all ASCII, no visual width needed */
339 size_t len;
341 len = strlen(*ap) + 1;
342 if(UICMP(z, len, >=, sw - i)){
343 fputs(" \\\n ", stdout);
344 i = 1;
346 i += len;
347 putc(' ', stdout);
348 fputs(*ap, stdout);
351 /* Regular expression in FIFO order */
352 #ifdef HAVE_REGEX
353 for(irp = itp->it_re; irp != NULL; irp = irp->ir_next){
354 size_t len;
355 char const *cp;
357 cp = n_shexp_quote_cp(irp->ir_input, FAL0);
358 len = strlen(cp) + 1;
359 if(UICMP(z, len, >=, sw - i)){
360 fputs(" \\\n ", stdout);
361 i = 1;
363 i += len;
364 putc(' ', stdout);
365 fputs(cp, stdout);
367 #endif
369 putchar('\n');
370 jleave:
371 fflush(stdout);
372 NYD2_LEAVE;
375 static int
376 a_ignore__cmp(void const *l, void const *r){
377 int rv;
379 rv = asccasecmp(*(char const * const *)l, *(char const * const *)r);
380 return rv;
383 static bool_t
384 a_ignore_delcmd_mux(struct n_ignore *ip, char const **list, bool_t retain){
385 char const *cp;
386 struct a_ignore_type *itp;
387 bool_t rv;
388 NYD2_ENTER;
390 rv = TRU1;
392 ip = a_ignore_resolve_self(ip, rv = (*list != NULL));
393 itp = retain ? &ip->i_retain : &ip->i_ignore;
395 if(itp->it_count == 0 && !itp->it_all)
396 n_err(_("No fields currently being %s\n"),
397 (retain ? _("retained") : _("ignored")));
398 else
399 while((cp = *list++) != NULL)
400 if(cp[0] == '*' && cp[1] == '\0')
401 a_ignore_del_allof(ip, retain);
402 else if(!a_ignore__delone(ip, retain, cp)){
403 n_err(_("Field not %s: %s\n"),
404 (retain ? _("retained") : _("ignored")), cp);
405 rv = FAL0;
407 NYD2_LEAVE;
408 return rv;
411 static bool_t
412 a_ignore__delone(struct n_ignore *ip, bool_t retain, char const *field){
413 struct a_ignore_type *itp;
414 NYD_ENTER;
416 itp = retain ? &ip->i_retain : &ip->i_ignore;
418 #ifdef HAVE_REGEX
419 if(n_is_maybe_regex(field)){
420 struct a_ignore_re **lirp, *irp;
422 for(irp = *(lirp = &itp->it_re); irp != NULL;
423 lirp = &irp->ir_next, irp = irp->ir_next)
424 if(!strcmp(field, irp->ir_input)){
425 *lirp = irp->ir_next;
426 if(irp == itp->it_re_tail)
427 itp->it_re_tail = irp->ir_next;
429 regfree(&irp->ir_regex);
430 if(!ip->i_auto)
431 n_free(irp);
432 --itp->it_count;
433 goto jleave;
435 }else
436 #endif /* HAVE_REGEX */
438 struct a_ignore_field **ifpp, *ifp;
439 ui32_t hi;
441 hi = torek_ihashn(field, UIZ_MAX) % n_NELEM(itp->it_ht);
443 for(ifp = *(ifpp = &itp->it_ht[hi]); ifp != NULL;
444 ifpp = &ifp->if_next, ifp = ifp->if_next)
445 if(!asccasecmp(ifp->if_field, field)){
446 *ifpp = ifp->if_next;
447 if(!ip->i_auto)
448 n_free(ifp);
449 --itp->it_count;
450 goto jleave;
454 ip = NULL;
455 jleave:
456 NYD_LEAVE;
457 return (ip != NULL);
460 FL int
461 c_headerpick(void *v){
462 bool_t retain;
463 struct a_ignore_bltin_map const *ibmp;
464 char const **argv;
465 int rv;
466 NYD_ENTER;
468 rv = 1;
469 argv = v;
471 /* Without arguments, show all settings of all contexts */
472 if(*argv == NULL){
473 rv = 0;
474 for(ibmp = &a_ignore_bltin_map[0];
475 ibmp <= &a_ignore_bltin_map[n__IGNORE_MAX]; ++ibmp){
476 rv |= !a_ignore_addcmd_mux(ibmp->ibm_ip, argv, TRU1);
477 rv |= !a_ignore_addcmd_mux(ibmp->ibm_ip, argv, FAL0);
479 goto jleave;
482 if((ibmp = a_ignore_resolve_bltin(*argv)) == NULL){
483 n_err(_("`headerpick': invalid context: %s\n"), *argv);
484 goto jleave;
487 /* With only <context>, show all settings of it */
488 if(*++argv == NULL){
489 rv = 0;
490 rv |= !a_ignore_addcmd_mux(ibmp->ibm_ip, argv, TRU1);
491 rv |= !a_ignore_addcmd_mux(ibmp->ibm_ip, argv, FAL0);
492 goto jleave;
495 if(is_asccaseprefix(*argv, "retain"))
496 retain = TRU1;
497 else if(is_asccaseprefix(*argv, "ignore"))
498 retain = FAL0;
499 else{
500 n_err(_("`headerpick': invalid type (retain, ignore): %s\n"), *argv);
501 goto jleave;
504 /* With only <context> and <type>, show its settings */
505 if(*++argv == NULL){
506 rv = !a_ignore_addcmd_mux(ibmp->ibm_ip, argv, retain);
507 goto jleave;
510 if(argv[1] == NULL){
511 n_err(_("Synopsis: headerpick: [<context> [<type> "
512 "[<action> <header-list>]]]\n"));
513 goto jleave;
514 }else if(is_asccaseprefix(*argv, "add") ||
515 (argv[0][0] == '+' && argv[0][1] == '\0'))
516 rv = !a_ignore_addcmd_mux(ibmp->ibm_ip, ++argv, retain);
517 else if(is_asccaseprefix(*argv, "remove") ||
518 (argv[0][0] == '-' && argv[0][1] == '\0'))
519 rv = !a_ignore_delcmd_mux(ibmp->ibm_ip, ++argv, retain);
520 else
521 n_err(_("`headerpick': invalid action (add, +, remove, -): %s\n"), *argv);
522 jleave:
523 NYD_LEAVE;
524 return rv;
527 FL int
528 c_retain(void *v){
529 int rv;
530 NYD_ENTER;
532 rv = !a_ignore_addcmd_mux(n_IGNORE_TYPE, v, TRU1);
533 NYD_LEAVE;
534 return rv;
537 FL int
538 c_ignore(void *v){
539 int rv;
540 NYD_ENTER;
542 rv = !a_ignore_addcmd_mux(n_IGNORE_TYPE, v, FAL0);
543 NYD_LEAVE;
544 return rv;
547 FL int
548 c_unretain(void *v){
549 int rv;
550 NYD_ENTER;
552 rv = !a_ignore_delcmd_mux(n_IGNORE_TYPE, v, TRU1);
553 NYD_LEAVE;
554 return rv;
557 FL int
558 c_unignore(void *v){
559 int rv;
560 NYD_ENTER;
562 rv = !a_ignore_delcmd_mux(n_IGNORE_TYPE, v, FAL0);
563 NYD_LEAVE;
564 return rv;
567 FL int
568 c_saveretain(void *v){ /* TODO v15 drop */
569 int rv;
570 NYD_ENTER;
572 rv = !a_ignore_addcmd_mux(n_IGNORE_SAVE, v, TRU1);
573 NYD_LEAVE;
574 return rv;
577 FL int
578 c_saveignore(void *v){ /* TODO v15 drop */
579 int rv;
580 NYD_ENTER;
582 rv = !a_ignore_addcmd_mux(n_IGNORE_SAVE, v, FAL0);
583 NYD_LEAVE;
584 return rv;
587 FL int
588 c_unsaveretain(void *v){ /* TODO v15 drop */
589 int rv;
590 NYD_ENTER;
592 rv = !a_ignore_delcmd_mux(n_IGNORE_SAVE, v, TRU1);
593 NYD_LEAVE;
594 return rv;
597 FL int
598 c_unsaveignore(void *v){ /* TODO v15 drop */
599 int rv;
600 NYD_ENTER;
602 rv = !a_ignore_delcmd_mux(n_IGNORE_SAVE, v, FAL0);
603 NYD_LEAVE;
604 return rv;
607 FL int
608 c_fwdretain(void *v){ /* TODO v15 drop */
609 int rv;
610 NYD_ENTER;
612 rv = !a_ignore_addcmd_mux(n_IGNORE_FWD, v, TRU1);
613 NYD_LEAVE;
614 return rv;
617 FL int
618 c_fwdignore(void *v){ /* TODO v15 drop */
619 int rv;
620 NYD_ENTER;
622 rv = !a_ignore_addcmd_mux(n_IGNORE_FWD, v, FAL0);
623 NYD_LEAVE;
624 return rv;
627 FL int
628 c_unfwdretain(void *v){ /* TODO v15 drop */
629 int rv;
630 NYD_ENTER;
632 rv = !a_ignore_delcmd_mux(n_IGNORE_FWD, v, TRU1);
633 NYD_LEAVE;
634 return rv;
637 FL int
638 c_unfwdignore(void *v){ /* TODO v15 drop */
639 int rv;
640 NYD_ENTER;
642 rv = !a_ignore_delcmd_mux(n_IGNORE_FWD, v, FAL0);
643 NYD_LEAVE;
644 return rv;
647 FL struct n_ignore *
648 n_ignore_new(bool_t isauto){
649 struct n_ignore *self;
650 NYD_ENTER;
652 self = isauto ? n_autorec_calloc(NULL, 1, sizeof *self)
653 : n_calloc(1, sizeof *self);
654 self->i_auto = isauto;
655 NYD_LEAVE;
656 return self;
659 FL void
660 n_ignore_del(struct n_ignore *self){
661 NYD_ENTER;
662 a_ignore_del_allof(self, TRU1);
663 a_ignore_del_allof(self, FAL0);
664 if(!self->i_auto)
665 n_free(self);
666 NYD_LEAVE;
669 FL bool_t
670 n_ignore_is_any(struct n_ignore const *self){
671 bool_t rv;
672 NYD_ENTER;
674 self = a_ignore_resolve_self(n_UNCONST(self), FAL0);
675 rv = (self != NULL &&
676 (self->i_retain.it_count != 0 || self->i_retain.it_all ||
677 self->i_ignore.it_count != 0 || self->i_ignore.it_all));
678 NYD_LEAVE;
679 return rv;
682 FL bool_t
683 n_ignore_insert(struct n_ignore *self, bool_t retain,
684 char const *dat, size_t len){
685 #ifdef HAVE_REGEX
686 struct a_ignore_re *irp;
687 bool_t isre;
688 #endif
689 struct a_ignore_field *ifp;
690 struct a_ignore_type *itp;
691 bool_t rv;
692 NYD_ENTER;
694 retain = !!retain; /* Make it true bool, TRUM1 has special _lookup meaning */
695 rv = FAL0;
696 self = a_ignore_resolve_self(self, TRU1);
698 if(len == UIZ_MAX)
699 len = strlen(dat);
701 /* Request to ignore or retain _anything_? That is special-treated */
702 if(len == 1 && dat[0] == '*'){
703 itp = retain ? &self->i_retain : &self->i_ignore;
704 if(itp->it_all)
705 rv = TRUM1;
706 else{
707 itp->it_all = TRU1;
708 a_ignore_del_allof(self, retain);
709 rv = TRU1;
711 goto jleave;
714 /* Check for regular expression or valid fieldname */
715 #ifdef HAVE_REGEX
716 if(!(isre = n_is_maybe_regex_buf(dat, len)))
717 #endif
719 char c;
720 size_t i;
722 for(i = 0; i < len; ++i){
723 c = dat[i];
724 if(!fieldnamechar(c))
725 goto jleave;
729 rv = TRUM1;
730 if(a_ignore_lookup(self, retain, dat, len) == TRU1)
731 goto jleave;
733 itp = retain ? &self->i_retain : &self->i_ignore;
735 if(itp->it_count == UI32_MAX){
736 n_err(_("Header selection size limit reached, cannot insert: %.*s\n"),
737 (int)n_MIN(len, SI32_MAX), dat);
738 rv = FAL0;
739 goto jleave;
742 rv = TRU1;
743 #ifdef HAVE_REGEX
744 if(isre){
745 struct a_ignore_re *x;
746 size_t i;
748 i = ++len + sizeof(*irp) - n_VFIELD_SIZEOF(struct a_ignore_re, ir_input);
749 irp = self->i_auto ? n_autorec_alloc(NULL, i) : n_alloc(i);
750 memcpy(irp->ir_input, dat, --len);
751 irp->ir_input[len] = '\0';
753 if(regcomp(&irp->ir_regex, irp->ir_input,
754 REG_EXTENDED | REG_ICASE | REG_NOSUB)){
755 n_err(_("Invalid regular expression: %s\n"), irp->ir_input);
756 if(!self->i_auto)
757 n_free(irp);
758 rv = FAL0;
759 goto jleave;
762 irp->ir_next = NULL;
763 if((x = itp->it_re_tail) != NULL)
764 x->ir_next = irp;
765 else
766 itp->it_re = irp;
767 itp->it_re_tail = irp;
768 }else
769 #endif /* HAVE_REGEX */
771 ui32_t hi;
772 size_t i;
774 i = sizeof(*ifp) - n_VFIELD_SIZEOF(struct a_ignore_field, if_field) +
775 len + 1;
776 ifp = self->i_auto ? n_autorec_alloc(NULL, i) : n_alloc(i);
777 memcpy(ifp->if_field, dat, len);
778 ifp->if_field[len] = '\0';
779 hi = torek_ihashn(dat, len) % n_NELEM(itp->it_ht);
780 ifp->if_next = itp->it_ht[hi];
781 itp->it_ht[hi] = ifp;
783 ++itp->it_count;
784 jleave:
785 NYD_LEAVE;
786 return rv;
789 FL bool_t
790 n_ignore_lookup(struct n_ignore const *self, char const *dat, size_t len){
791 bool_t rv;
792 NYD_ENTER;
794 if(self == n_IGNORE_ALL)
795 rv = TRUM1;
796 else if(len == 0 ||
797 (self = a_ignore_resolve_self(n_UNCONST(self), FAL0)) == NULL)
798 rv = FAL0;
799 else if(self->i_retain.it_all)
800 rv = TRU1;
801 else if(self->i_retain.it_count == 0 && self->i_ignore.it_all)
802 rv = TRUM1;
803 else
804 rv = a_ignore_lookup(self, TRUM1, dat, len);
805 NYD_LEAVE;
806 return rv;
809 /* s-it-mode */