Tweak previous, it added a bad memory access
[s-mailx.git] / ignore.c
blobbaeaec15122dd2a4eeef8bc8f7767ee58c3bfe48
1 /*@ S-nail - a mail user agent derived from Berkeley Mail.
2 *@ `headerpick', `retain' and `ignore', and `un..' variants.
4 * Copyright (c) 2012 - 2017 Steffen (Daode) Nurpmeso <steffen@sdaoden.eu>.
5 */
6 /*
7 * Copyright (c) 1980, 1993
8 * The Regents of the University of California. All rights reserved.
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
18 * 3. Neither the name of the University nor the names of its contributors
19 * may be used to endorse or promote products derived from this software
20 * without specific prior written permission.
22 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
23 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
26 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
27 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
28 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
29 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
31 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32 * SUCH DAMAGE.
34 #undef n_FILE
35 #define n_FILE ignore
37 #ifndef HAVE_AMALGAMATION
38 # include "nail.h"
39 #endif
41 struct a_ignore_type{
42 ui32_t it_count; /* Entries in .it_ht (and .it_re) */
43 bool_t it_all; /* _All_ fields ought to be _type_ (ignore/retain) */
44 ui8_t it__dummy[3];
45 struct a_ignore_field{
46 struct a_ignore_field *if_next;
47 char if_field[n_VFIELD_SIZE(0)]; /* Header field */
48 } *it_ht[3]; /* TODO make hashmap dynamic */
49 #ifdef HAVE_REGEX
50 struct a_ignore_re{
51 struct a_ignore_re *ir_next;
52 regex_t ir_regex;
53 char ir_input[n_VFIELD_SIZE(0)]; /* Regex input text (for showing it) */
54 } *it_re, *it_re_tail;
55 #endif
58 struct n_ignore{
59 struct a_ignore_type i_retain;
60 struct a_ignore_type i_ignore;
61 bool_t i_auto; /* In auto-reclaimed, not heap memory */
62 bool_t i_bltin; /* Is a builtin n_IGNORE* type */
63 ui8_t i_ibm_idx; /* If .i_bltin: a_ignore_bltin_map[] idx */
64 ui8_t i__dummy[5];
67 struct a_ignore_bltin_map{
68 struct n_ignore *ibm_ip;
69 char const ibm_name[8];
72 static struct a_ignore_bltin_map const a_ignore_bltin_map[] = {
73 {n_IGNORE_TYPE, "type\0"},
74 {n_IGNORE_SAVE, "save\0"},
75 {n_IGNORE_FWD, "forward\0"},
76 {n_IGNORE_TOP, "top\0"},
78 {n_IGNORE_TYPE, "print\0"},
79 {n_IGNORE_FWD, "fwd\0"}
81 #ifdef HAVE_DEVEL /* Avoid gcc warn cascade since n_ignore is defined locally */
82 n_CTAV(-n__IGNORE_TYPE - n__IGNORE_ADJUST == 0);
83 n_CTAV(-n__IGNORE_SAVE - n__IGNORE_ADJUST == 1);
84 n_CTAV(-n__IGNORE_FWD - n__IGNORE_ADJUST == 2);
85 n_CTAV(-n__IGNORE_TOP - n__IGNORE_ADJUST == 3);
86 n_CTAV(n__IGNORE_MAX == 3);
87 #endif
89 static struct n_ignore *a_ignore_bltin[n__IGNORE_MAX + 1];
90 /* Almost everyone uses `ignore'/`retain', put _TYPE in BSS */
91 static struct n_ignore a_ignore_type;
93 /* Return real self, which is xself unless that is one of the builtin specials,
94 * in which case NULL is returned if nonexistent and docreate is false.
95 * The other statics assume self has been resolved (unless noted) */
96 static struct n_ignore *a_ignore_resolve_self(struct n_ignore *xself,
97 bool_t docreate);
99 /* Lookup whether a mapping is contained: TRU1=retained, TRUM1=ignored.
100 * If retain is _not_ TRUM1 then only the retained/ignored slot is inspected,
101 * and regular expressions are not executed but instead their .ir_input is
102 * text-compared against len bytes of dat.
103 * Note it doesn't handle the .it_all "all fields" condition */
104 static bool_t a_ignore_lookup(struct n_ignore const *self, bool_t retain,
105 char const *dat, size_t len);
107 /* Delete all retain( else ignor)ed members */
108 static void a_ignore_del_allof(struct n_ignore *ip, bool_t retain);
110 /* Try to map a string to one of the builtin types */
111 static struct a_ignore_bltin_map const *a_ignore_resolve_bltin(char const *cp);
113 /* Logic behind `headerpick T T' (a.k.a. `retain'+) */
114 static bool_t a_ignore_addcmd_mux(struct n_ignore *ip, char const **list,
115 bool_t retain);
117 static void a_ignore__show(struct n_ignore const *ip, bool_t retain);
118 static int a_ignore__cmp(void const *l, void const *r);
120 /* Logic behind `unheaderpick T T' (a.k.a. `unretain'+) */
121 static bool_t a_ignore_delcmd_mux(struct n_ignore *ip, char const **list,
122 bool_t retain);
124 static bool_t a_ignore__delone(struct n_ignore *ip, bool_t retain,
125 char const *field);
127 static struct n_ignore *
128 a_ignore_resolve_self(struct n_ignore *xself, bool_t docreate){
129 uintptr_t suip;
130 struct n_ignore *self;
131 NYD2_ENTER;
133 self = xself;
134 suip = -(uintptr_t)self - n__IGNORE_ADJUST;
136 if(suip <= n__IGNORE_MAX){
137 if((self = a_ignore_bltin[suip]) == NULL && docreate){
138 if(xself == n_IGNORE_TYPE){
139 self = &a_ignore_type;
140 /* LIB: memset(self, 0, sizeof *self);*/
141 }else
142 self = n_ignore_new(FAL0);
143 self->i_bltin = TRU1;
144 self->i_ibm_idx = (ui8_t)suip;
145 a_ignore_bltin[suip] = self;
148 NYD2_LEAVE;
149 return self;
152 static bool_t
153 a_ignore_lookup(struct n_ignore const *self, bool_t retain,
154 char const *dat, size_t len){
155 bool_t rv;
156 #ifdef HAVE_REGEX
157 struct a_ignore_re *irp;
158 #endif
159 struct a_ignore_field *ifp;
160 ui32_t hi;
161 NYD2_ENTER;
163 if(len == UIZ_MAX)
164 len = strlen(dat);
165 hi = n_torek_ihashn(dat, len) % n_NELEM(self->i_retain.it_ht);
167 /* Again: doesn't handle .it_all conditions! */
168 /* (Inner functions would be nice, again) */
169 if(retain && self->i_retain.it_count > 0){
170 rv = TRU1;
171 for(ifp = self->i_retain.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_retain.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) ? TRUM1 : FAL0;
184 }else if((retain == TRUM1 || !retain) && self->i_ignore.it_count > 0){
185 rv = TRUM1;
186 for(ifp = self->i_ignore.it_ht[hi]; ifp != NULL; ifp = ifp->if_next)
187 if(!ascncasecmp(ifp->if_field, dat, len))
188 goto jleave;
189 #ifdef HAVE_REGEX
190 if(dat[len - 1] != '\0')
191 dat = savestrbuf(dat, len);
192 for(irp = self->i_ignore.it_re; irp != NULL; irp = irp->ir_next)
193 if((retain == TRUM1
194 ? (regexec(&irp->ir_regex, dat, 0,NULL, 0) != REG_NOMATCH)
195 : !strncmp(irp->ir_input, dat, len)))
196 goto jleave;
197 #endif
198 rv = (retain == TRUM1) ? TRU1 : FAL0;
199 }else
200 rv = FAL0;
201 jleave:
202 NYD2_LEAVE;
203 return rv;
206 static void
207 a_ignore_del_allof(struct n_ignore *ip, bool_t retain){
208 #ifdef HAVE_REGEX
209 struct a_ignore_re *irp;
210 #endif
211 struct a_ignore_field *ifp;
212 struct a_ignore_type *itp;
213 NYD2_ENTER;
215 itp = retain ? &ip->i_retain : &ip->i_ignore;
217 if(!ip->i_auto){
218 size_t i;
220 for(i = 0; i < n_NELEM(itp->it_ht); ++i)
221 for(ifp = itp->it_ht[i]; ifp != NULL;){
222 struct a_ignore_field *x;
224 x = ifp;
225 ifp = ifp->if_next;
226 n_free(x);
230 #ifdef HAVE_REGEX
231 for(irp = itp->it_re; irp != NULL;){
232 struct a_ignore_re *x;
234 x = irp;
235 irp = irp->ir_next;
236 regfree(&x->ir_regex);
237 if(!ip->i_auto)
238 n_free(x);
240 #endif
242 memset(itp, 0, sizeof *itp);
243 NYD2_LEAVE;
246 static struct a_ignore_bltin_map const *
247 a_ignore_resolve_bltin(char const *cp){
248 struct a_ignore_bltin_map const *ibmp;
249 NYD2_ENTER;
251 for(ibmp = &a_ignore_bltin_map[0];;)
252 if(!asccasecmp(cp, ibmp->ibm_name))
253 break;
254 else if(++ibmp == &a_ignore_bltin_map[n_NELEM(a_ignore_bltin_map)]){
255 ibmp = NULL;
256 break;
258 NYD2_LEAVE;
259 return ibmp;
262 static bool_t
263 a_ignore_addcmd_mux(struct n_ignore *ip, char const **list, bool_t retain){
264 char const **ap;
265 bool_t rv;
266 NYD2_ENTER;
268 ip = a_ignore_resolve_self(ip, rv = (*list != NULL));
270 if(!rv){
271 if(ip != NULL && ip->i_bltin)
272 a_ignore__show(ip, retain);
273 rv = TRU1;
274 }else{
275 for(ap = list; *ap != 0; ++ap)
276 switch(n_ignore_insert_cp(ip, retain, *ap)){
277 case FAL0:
278 n_err(_("Invalid field name cannot be %s: %s\n"),
279 (retain ? _("retained") : _("ignored")), *ap);
280 rv = FAL0;
281 break;
282 case TRUM1:
283 if(n_poption & n_PO_D_V)
284 n_err(_("Field already %s: %s\n"),
285 (retain ? _("retained") : _("ignored")), *ap);
286 /* FALLTHRU */
287 case TRU1:
288 break;
291 NYD2_LEAVE;
292 return rv;
295 static void
296 a_ignore__show(struct n_ignore const *ip, bool_t retain){
297 #ifdef HAVE_REGEX
298 struct a_ignore_re *irp;
299 #endif
300 struct a_ignore_field *ifp;
301 size_t i, sw;
302 char const **ap, **ring;
303 struct a_ignore_type const *itp;
304 NYD2_ENTER;
306 itp = retain ? &ip->i_retain : &ip->i_ignore;
309 char const *pre, *attr;
311 if(itp->it_all)
312 pre = n_empty, attr = n_star;
313 else if(itp->it_count == 0)
314 pre = n_ns, attr = _("currently covers no fields");
315 else
316 break;
317 fprintf(n_stdout, _("%sheaderpick %s %s %s\n"),
318 pre, a_ignore_bltin_map[ip->i_ibm_idx].ibm_name,
319 (retain ? "retain" : "ignore"), attr);
320 goto jleave;
321 }while(0);
323 ring = salloc((itp->it_count +1) * sizeof *ring);
324 for(ap = ring, i = 0; i < n_NELEM(itp->it_ht); ++i)
325 for(ifp = itp->it_ht[i]; ifp != NULL; ifp = ifp->if_next)
326 *ap++ = ifp->if_field;
327 *ap = NULL;
329 qsort(ring, PTR2SIZE(ap - ring), sizeof *ring, &a_ignore__cmp);
331 i = fprintf(n_stdout, "headerpick %s %s",
332 a_ignore_bltin_map[ip->i_ibm_idx].ibm_name,
333 (retain ? "retain" : "ignore"));
334 sw = n_scrnwidth;
336 for(ap = ring; *ap != NULL; ++ap){
337 /* These fields are all ASCII, no visual width needed */
338 size_t len;
340 len = strlen(*ap) + 1;
341 if(UICMP(z, len, >=, sw - i)){
342 fputs(" \\\n ", n_stdout);
343 i = 1;
345 i += len;
346 putc(' ', n_stdout);
347 fputs(*ap, n_stdout);
350 /* Regular expression in FIFO order */
351 #ifdef HAVE_REGEX
352 for(irp = itp->it_re; irp != NULL; irp = irp->ir_next){
353 size_t len;
354 char const *cp;
356 cp = n_shexp_quote_cp(irp->ir_input, FAL0);
357 len = strlen(cp) + 1;
358 if(UICMP(z, len, >=, sw - i)){
359 fputs(" \\\n ", n_stdout);
360 i = 1;
362 i += len;
363 putc(' ', n_stdout);
364 fputs(cp, n_stdout);
366 #endif
368 putc('\n', n_stdout);
369 jleave:
370 fflush(n_stdout);
371 NYD2_LEAVE;
374 static int
375 a_ignore__cmp(void const *l, void const *r){
376 int rv;
378 rv = asccasecmp(*(char const * const *)l, *(char const * const *)r);
379 return rv;
382 static bool_t
383 a_ignore_delcmd_mux(struct n_ignore *ip, char const **list, bool_t retain){
384 char const *cp;
385 struct a_ignore_type *itp;
386 bool_t rv;
387 NYD2_ENTER;
389 rv = TRU1;
391 ip = a_ignore_resolve_self(ip, rv = (*list != NULL));
392 itp = retain ? &ip->i_retain : &ip->i_ignore;
394 if(itp->it_count == 0 && !itp->it_all)
395 n_err(_("No fields currently being %s\n"),
396 (retain ? _("retained") : _("ignored")));
397 else
398 while((cp = *list++) != NULL)
399 if(cp[0] == '*' && cp[1] == '\0')
400 a_ignore_del_allof(ip, retain);
401 else if(!a_ignore__delone(ip, retain, cp)){
402 n_err(_("Field not %s: %s\n"),
403 (retain ? _("retained") : _("ignored")), cp);
404 rv = FAL0;
406 NYD2_LEAVE;
407 return rv;
410 static bool_t
411 a_ignore__delone(struct n_ignore *ip, bool_t retain, char const *field){
412 struct a_ignore_type *itp;
413 NYD_ENTER;
415 itp = retain ? &ip->i_retain : &ip->i_ignore;
417 #ifdef HAVE_REGEX
418 if(n_is_maybe_regex(field)){
419 struct a_ignore_re **lirp, *irp;
421 for(irp = *(lirp = &itp->it_re); irp != NULL;
422 lirp = &irp->ir_next, irp = irp->ir_next)
423 if(!strcmp(field, irp->ir_input)){
424 *lirp = irp->ir_next;
425 if(irp == itp->it_re_tail)
426 itp->it_re_tail = irp->ir_next;
428 regfree(&irp->ir_regex);
429 if(!ip->i_auto)
430 n_free(irp);
431 --itp->it_count;
432 goto jleave;
434 }else
435 #endif /* HAVE_REGEX */
437 struct a_ignore_field **ifpp, *ifp;
438 ui32_t hi;
440 hi = n_torek_ihashn(field, UIZ_MAX) % n_NELEM(itp->it_ht);
442 for(ifp = *(ifpp = &itp->it_ht[hi]); ifp != NULL;
443 ifpp = &ifp->if_next, ifp = ifp->if_next)
444 if(!asccasecmp(ifp->if_field, field)){
445 *ifpp = ifp->if_next;
446 if(!ip->i_auto)
447 n_free(ifp);
448 --itp->it_count;
449 goto jleave;
453 ip = NULL;
454 jleave:
455 NYD_LEAVE;
456 return (ip != NULL);
459 FL int
460 c_headerpick(void *vp){
461 bool_t retain;
462 struct a_ignore_bltin_map const *ibmp;
463 char const **argv;
464 int rv;
465 NYD_ENTER;
467 rv = 1;
468 argv = vp;
470 /* Without arguments, show all settings of all contexts */
471 if(*argv == NULL){
472 rv = 0;
473 for(ibmp = &a_ignore_bltin_map[0];
474 ibmp <= &a_ignore_bltin_map[n__IGNORE_MAX]; ++ibmp){
475 rv |= !a_ignore_addcmd_mux(ibmp->ibm_ip, argv, TRU1);
476 rv |= !a_ignore_addcmd_mux(ibmp->ibm_ip, argv, FAL0);
478 goto jleave;
481 if((ibmp = a_ignore_resolve_bltin(*argv)) == NULL){
482 n_err(_("`headerpick': invalid context: %s\n"), *argv);
483 goto jleave;
485 ++argv;
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;
503 ++argv;
505 /* With only <context> and <type>, show its settings */
506 if(*argv == NULL){
507 rv = !a_ignore_addcmd_mux(ibmp->ibm_ip, argv, retain);
508 goto jleave;
511 rv = !a_ignore_addcmd_mux(ibmp->ibm_ip, argv, retain);
512 jleave:
513 NYD_LEAVE;
514 return rv;
517 FL int
518 c_unheaderpick(void *vp){
519 bool_t retain;
520 struct a_ignore_bltin_map const *ibmp;
521 char const **argv;
522 int rv;
523 NYD_ENTER;
525 rv = 1;
526 argv = vp;
528 if((ibmp = a_ignore_resolve_bltin(*argv)) == NULL){
529 n_err(_("`unheaderpick': invalid context: %s\n"), *argv);
530 goto jleave;
532 ++argv;
534 if(is_asccaseprefix(*argv, "retain"))
535 retain = TRU1;
536 else if(is_asccaseprefix(*argv, "ignore"))
537 retain = FAL0;
538 else{
539 n_err(_("`unheaderpick': invalid type (retain, ignore): %s\n"), *argv);
540 goto jleave;
542 ++argv;
544 rv = !a_ignore_delcmd_mux(ibmp->ibm_ip, argv, retain);
545 jleave:
546 NYD_LEAVE;
547 return rv;
550 FL int
551 c_retain(void *vp){
552 int rv;
553 NYD_ENTER;
555 rv = !a_ignore_addcmd_mux(n_IGNORE_TYPE, vp, TRU1);
556 NYD_LEAVE;
557 return rv;
560 FL int
561 c_ignore(void *vp){
562 int rv;
563 NYD_ENTER;
565 rv = !a_ignore_addcmd_mux(n_IGNORE_TYPE, vp, FAL0);
566 NYD_LEAVE;
567 return rv;
570 FL int
571 c_unretain(void *vp){
572 int rv;
573 NYD_ENTER;
575 rv = !a_ignore_delcmd_mux(n_IGNORE_TYPE, vp, TRU1);
576 NYD_LEAVE;
577 return rv;
580 FL int
581 c_unignore(void *vp){
582 int rv;
583 NYD_ENTER;
585 rv = !a_ignore_delcmd_mux(n_IGNORE_TYPE, vp, FAL0);
586 NYD_LEAVE;
587 return rv;
590 FL int
591 c_saveretain(void *v){ /* TODO v15 drop */
592 int rv;
593 NYD_ENTER;
595 rv = !a_ignore_addcmd_mux(n_IGNORE_SAVE, v, TRU1);
596 NYD_LEAVE;
597 return rv;
600 FL int
601 c_saveignore(void *v){ /* TODO v15 drop */
602 int rv;
603 NYD_ENTER;
605 rv = !a_ignore_addcmd_mux(n_IGNORE_SAVE, v, FAL0);
606 NYD_LEAVE;
607 return rv;
610 FL int
611 c_unsaveretain(void *v){ /* TODO v15 drop */
612 int rv;
613 NYD_ENTER;
615 rv = !a_ignore_delcmd_mux(n_IGNORE_SAVE, v, TRU1);
616 NYD_LEAVE;
617 return rv;
620 FL int
621 c_unsaveignore(void *v){ /* TODO v15 drop */
622 int rv;
623 NYD_ENTER;
625 rv = !a_ignore_delcmd_mux(n_IGNORE_SAVE, v, FAL0);
626 NYD_LEAVE;
627 return rv;
630 FL int
631 c_fwdretain(void *v){ /* TODO v15 drop */
632 int rv;
633 NYD_ENTER;
635 rv = !a_ignore_addcmd_mux(n_IGNORE_FWD, v, TRU1);
636 NYD_LEAVE;
637 return rv;
640 FL int
641 c_fwdignore(void *v){ /* TODO v15 drop */
642 int rv;
643 NYD_ENTER;
645 rv = !a_ignore_addcmd_mux(n_IGNORE_FWD, v, FAL0);
646 NYD_LEAVE;
647 return rv;
650 FL int
651 c_unfwdretain(void *v){ /* TODO v15 drop */
652 int rv;
653 NYD_ENTER;
655 rv = !a_ignore_delcmd_mux(n_IGNORE_FWD, v, TRU1);
656 NYD_LEAVE;
657 return rv;
660 FL int
661 c_unfwdignore(void *v){ /* TODO v15 drop */
662 int rv;
663 NYD_ENTER;
665 rv = !a_ignore_delcmd_mux(n_IGNORE_FWD, v, FAL0);
666 NYD_LEAVE;
667 return rv;
670 FL struct n_ignore *
671 n_ignore_new(bool_t isauto){
672 struct n_ignore *self;
673 NYD_ENTER;
675 self = isauto ? n_autorec_calloc(1, sizeof *self) : n_calloc(1,sizeof *self);
676 self->i_auto = isauto;
677 NYD_LEAVE;
678 return self;
681 FL void
682 n_ignore_del(struct n_ignore *self){
683 NYD_ENTER;
684 a_ignore_del_allof(self, TRU1);
685 a_ignore_del_allof(self, FAL0);
686 if(!self->i_auto)
687 n_free(self);
688 NYD_LEAVE;
691 FL bool_t
692 n_ignore_is_any(struct n_ignore const *self){
693 bool_t rv;
694 NYD_ENTER;
696 self = a_ignore_resolve_self(n_UNCONST(self), FAL0);
697 rv = (self != NULL &&
698 (self->i_retain.it_count != 0 || self->i_retain.it_all ||
699 self->i_ignore.it_count != 0 || self->i_ignore.it_all));
700 NYD_LEAVE;
701 return rv;
704 FL bool_t
705 n_ignore_insert(struct n_ignore *self, bool_t retain,
706 char const *dat, size_t len){
707 #ifdef HAVE_REGEX
708 struct a_ignore_re *irp;
709 bool_t isre;
710 #endif
711 struct a_ignore_field *ifp;
712 struct a_ignore_type *itp;
713 bool_t rv;
714 NYD_ENTER;
716 retain = !!retain; /* Make it true bool, TRUM1 has special _lookup meaning */
717 rv = FAL0;
718 self = a_ignore_resolve_self(self, TRU1);
720 if(len == UIZ_MAX)
721 len = strlen(dat);
723 /* Request to ignore or retain _anything_? That is special-treated */
724 if(len == 1 && dat[0] == '*'){
725 itp = retain ? &self->i_retain : &self->i_ignore;
726 if(itp->it_all)
727 rv = TRUM1;
728 else{
729 itp->it_all = TRU1;
730 a_ignore_del_allof(self, retain);
731 rv = TRU1;
733 goto jleave;
736 /* Check for regular expression or valid fieldname */
737 #ifdef HAVE_REGEX
738 if(!(isre = n_is_maybe_regex_buf(dat, len)))
739 #endif
741 char c;
742 size_t i;
744 for(i = 0; i < len; ++i){
745 c = dat[i];
746 if(!fieldnamechar(c))
747 goto jleave;
751 rv = TRUM1;
752 if(a_ignore_lookup(self, retain, dat, len) == (retain ? TRU1 : TRUM1))
753 goto jleave;
755 itp = retain ? &self->i_retain : &self->i_ignore;
757 if(itp->it_count == UI32_MAX){
758 n_err(_("Header selection size limit reached, cannot insert: %.*s\n"),
759 (int)n_MIN(len, SI32_MAX), dat);
760 rv = FAL0;
761 goto jleave;
764 rv = TRU1;
765 #ifdef HAVE_REGEX
766 if(isre){
767 struct a_ignore_re *x;
768 int s;
769 size_t i;
771 i = n_VSTRUCT_SIZEOF(struct a_ignore_re, ir_input) + ++len;
772 irp = self->i_auto ? n_autorec_alloc(i) : n_alloc(i);
773 memcpy(irp->ir_input, dat, --len);
774 irp->ir_input[len] = '\0';
776 if((s = regcomp(&irp->ir_regex, irp->ir_input,
777 REG_EXTENDED | REG_ICASE | REG_NOSUB)) != 0){
778 n_err(_("Invalid regular expression: %s: %s\n"),
779 n_shexp_quote_cp(irp->ir_input, FAL0),
780 n_regex_err_to_doc(&irp->ir_regex, s));
781 if(!self->i_auto)
782 n_free(irp);
783 rv = FAL0;
784 goto jleave;
787 irp->ir_next = NULL;
788 if((x = itp->it_re_tail) != NULL)
789 x->ir_next = irp;
790 else
791 itp->it_re = irp;
792 itp->it_re_tail = irp;
793 }else
794 #endif /* HAVE_REGEX */
796 ui32_t hi;
797 size_t i;
799 i = n_VSTRUCT_SIZEOF(struct a_ignore_field, if_field) + len + 1;
800 ifp = self->i_auto ? n_autorec_alloc(i) : n_alloc(i);
801 memcpy(ifp->if_field, dat, len);
802 ifp->if_field[len] = '\0';
803 hi = n_torek_ihashn(dat, len) % n_NELEM(itp->it_ht);
804 ifp->if_next = itp->it_ht[hi];
805 itp->it_ht[hi] = ifp;
807 ++itp->it_count;
808 jleave:
809 NYD_LEAVE;
810 return rv;
813 FL bool_t
814 n_ignore_lookup(struct n_ignore const *self, char const *dat, size_t len){
815 bool_t rv;
816 NYD_ENTER;
818 if(self == n_IGNORE_ALL)
819 rv = TRUM1;
820 else if(len == 0 ||
821 (self = a_ignore_resolve_self(n_UNCONST(self), FAL0)) == NULL)
822 rv = FAL0;
823 else if(self->i_retain.it_all)
824 rv = TRU1;
825 else if(self->i_retain.it_count == 0 && self->i_ignore.it_all)
826 rv = TRUM1;
827 else
828 rv = a_ignore_lookup(self, TRUM1, dat, len);
829 NYD_LEAVE;
830 return rv;
833 /* s-it-mode */