Add ~I command escape (like ~i, but do not add newline)
[s-mailx.git] / ignore.c
blob8b1088744dd155ea8911f5520cbd1bd406bb0766
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 built-in 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 built-in 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 built-in 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 ip = a_ignore_resolve_self(ip, rv = (*list != NULL));
390 itp = retain ? &ip->i_retain : &ip->i_ignore;
392 if(itp->it_count == 0 && !itp->it_all)
393 n_err(_("No fields currently being %s\n"),
394 (retain ? _("retained") : _("ignored")));
395 else
396 while((cp = *list++) != NULL)
397 if(cp[0] == '*' && cp[1] == '\0')
398 a_ignore_del_allof(ip, retain);
399 else if(!a_ignore__delone(ip, retain, cp)){
400 n_err(_("Field not %s: %s\n"),
401 (retain ? _("retained") : _("ignored")), cp);
402 rv = FAL0;
404 NYD2_LEAVE;
405 return rv;
408 static bool_t
409 a_ignore__delone(struct n_ignore *ip, bool_t retain, char const *field){
410 struct a_ignore_type *itp;
411 NYD_ENTER;
413 itp = retain ? &ip->i_retain : &ip->i_ignore;
415 #ifdef HAVE_REGEX
416 if(n_is_maybe_regex(field)){
417 struct a_ignore_re **lirp, *irp;
419 for(irp = *(lirp = &itp->it_re); irp != NULL;
420 lirp = &irp->ir_next, irp = irp->ir_next)
421 if(!strcmp(field, irp->ir_input)){
422 *lirp = irp->ir_next;
423 if(irp == itp->it_re_tail)
424 itp->it_re_tail = irp->ir_next;
426 regfree(&irp->ir_regex);
427 if(!ip->i_auto)
428 n_free(irp);
429 --itp->it_count;
430 goto jleave;
432 }else
433 #endif /* HAVE_REGEX */
435 struct a_ignore_field **ifpp, *ifp;
436 ui32_t hi;
438 hi = n_torek_ihashn(field, UIZ_MAX) % n_NELEM(itp->it_ht);
440 for(ifp = *(ifpp = &itp->it_ht[hi]); ifp != NULL;
441 ifpp = &ifp->if_next, ifp = ifp->if_next)
442 if(!asccasecmp(ifp->if_field, field)){
443 *ifpp = ifp->if_next;
444 if(!ip->i_auto)
445 n_free(ifp);
446 --itp->it_count;
447 goto jleave;
451 ip = NULL;
452 jleave:
453 NYD_LEAVE;
454 return (ip != NULL);
457 FL int
458 c_headerpick(void *vp){
459 bool_t retain;
460 struct a_ignore_bltin_map const *ibmp;
461 char const **argv;
462 int rv;
463 NYD_ENTER;
465 rv = 1;
466 argv = vp;
468 /* Without arguments, show all settings of all contexts */
469 if(*argv == NULL){
470 rv = 0;
471 for(ibmp = &a_ignore_bltin_map[0];
472 ibmp <= &a_ignore_bltin_map[n__IGNORE_MAX]; ++ibmp){
473 rv |= !a_ignore_addcmd_mux(ibmp->ibm_ip, argv, TRU1);
474 rv |= !a_ignore_addcmd_mux(ibmp->ibm_ip, argv, FAL0);
476 goto jleave;
479 if((ibmp = a_ignore_resolve_bltin(*argv)) == NULL){
480 n_err(_("`headerpick': invalid context: %s\n"), *argv);
481 goto jleave;
483 ++argv;
485 /* With only <context>, show all settings of it */
486 if(*argv == NULL){
487 rv = 0;
488 rv |= !a_ignore_addcmd_mux(ibmp->ibm_ip, argv, TRU1);
489 rv |= !a_ignore_addcmd_mux(ibmp->ibm_ip, argv, FAL0);
490 goto jleave;
493 if(is_asccaseprefix(*argv, "retain"))
494 retain = TRU1;
495 else if(is_asccaseprefix(*argv, "ignore"))
496 retain = FAL0;
497 else{
498 n_err(_("`headerpick': invalid type (retain, ignore): %s\n"), *argv);
499 goto jleave;
501 ++argv;
503 /* With only <context> and <type>, show its settings */
504 if(*argv == NULL){
505 rv = !a_ignore_addcmd_mux(ibmp->ibm_ip, argv, retain);
506 goto jleave;
509 rv = !a_ignore_addcmd_mux(ibmp->ibm_ip, argv, retain);
510 jleave:
511 NYD_LEAVE;
512 return rv;
515 FL int
516 c_unheaderpick(void *vp){
517 bool_t retain;
518 struct a_ignore_bltin_map const *ibmp;
519 char const **argv;
520 int rv;
521 NYD_ENTER;
523 rv = 1;
524 argv = vp;
526 if((ibmp = a_ignore_resolve_bltin(*argv)) == NULL){
527 n_err(_("`unheaderpick': invalid context: %s\n"), *argv);
528 goto jleave;
530 ++argv;
532 if(is_asccaseprefix(*argv, "retain"))
533 retain = TRU1;
534 else if(is_asccaseprefix(*argv, "ignore"))
535 retain = FAL0;
536 else{
537 n_err(_("`unheaderpick': invalid type (retain, ignore): %s\n"), *argv);
538 goto jleave;
540 ++argv;
542 rv = !a_ignore_delcmd_mux(ibmp->ibm_ip, argv, retain);
543 jleave:
544 NYD_LEAVE;
545 return rv;
548 FL int
549 c_retain(void *vp){
550 int rv;
551 NYD_ENTER;
553 rv = !a_ignore_addcmd_mux(n_IGNORE_TYPE, vp, TRU1);
554 NYD_LEAVE;
555 return rv;
558 FL int
559 c_ignore(void *vp){
560 int rv;
561 NYD_ENTER;
563 rv = !a_ignore_addcmd_mux(n_IGNORE_TYPE, vp, FAL0);
564 NYD_LEAVE;
565 return rv;
568 FL int
569 c_unretain(void *vp){
570 int rv;
571 NYD_ENTER;
573 rv = !a_ignore_delcmd_mux(n_IGNORE_TYPE, vp, TRU1);
574 NYD_LEAVE;
575 return rv;
578 FL int
579 c_unignore(void *vp){
580 int rv;
581 NYD_ENTER;
583 rv = !a_ignore_delcmd_mux(n_IGNORE_TYPE, vp, FAL0);
584 NYD_LEAVE;
585 return rv;
588 FL int
589 c_saveretain(void *v){ /* TODO v15 drop */
590 int rv;
591 NYD_ENTER;
593 rv = !a_ignore_addcmd_mux(n_IGNORE_SAVE, v, TRU1);
594 NYD_LEAVE;
595 return rv;
598 FL int
599 c_saveignore(void *v){ /* TODO v15 drop */
600 int rv;
601 NYD_ENTER;
603 rv = !a_ignore_addcmd_mux(n_IGNORE_SAVE, v, FAL0);
604 NYD_LEAVE;
605 return rv;
608 FL int
609 c_unsaveretain(void *v){ /* TODO v15 drop */
610 int rv;
611 NYD_ENTER;
613 rv = !a_ignore_delcmd_mux(n_IGNORE_SAVE, v, TRU1);
614 NYD_LEAVE;
615 return rv;
618 FL int
619 c_unsaveignore(void *v){ /* TODO v15 drop */
620 int rv;
621 NYD_ENTER;
623 rv = !a_ignore_delcmd_mux(n_IGNORE_SAVE, v, FAL0);
624 NYD_LEAVE;
625 return rv;
628 FL int
629 c_fwdretain(void *v){ /* TODO v15 drop */
630 int rv;
631 NYD_ENTER;
633 rv = !a_ignore_addcmd_mux(n_IGNORE_FWD, v, TRU1);
634 NYD_LEAVE;
635 return rv;
638 FL int
639 c_fwdignore(void *v){ /* TODO v15 drop */
640 int rv;
641 NYD_ENTER;
643 rv = !a_ignore_addcmd_mux(n_IGNORE_FWD, v, FAL0);
644 NYD_LEAVE;
645 return rv;
648 FL int
649 c_unfwdretain(void *v){ /* TODO v15 drop */
650 int rv;
651 NYD_ENTER;
653 rv = !a_ignore_delcmd_mux(n_IGNORE_FWD, v, TRU1);
654 NYD_LEAVE;
655 return rv;
658 FL int
659 c_unfwdignore(void *v){ /* TODO v15 drop */
660 int rv;
661 NYD_ENTER;
663 rv = !a_ignore_delcmd_mux(n_IGNORE_FWD, v, FAL0);
664 NYD_LEAVE;
665 return rv;
668 FL struct n_ignore *
669 n_ignore_new(bool_t isauto){
670 struct n_ignore *self;
671 NYD_ENTER;
673 self = isauto ? n_autorec_calloc(1, sizeof *self) : n_calloc(1,sizeof *self);
674 self->i_auto = isauto;
675 NYD_LEAVE;
676 return self;
679 FL void
680 n_ignore_del(struct n_ignore *self){
681 NYD_ENTER;
682 a_ignore_del_allof(self, TRU1);
683 a_ignore_del_allof(self, FAL0);
684 if(!self->i_auto)
685 n_free(self);
686 NYD_LEAVE;
689 FL bool_t
690 n_ignore_is_any(struct n_ignore const *self){
691 bool_t rv;
692 NYD_ENTER;
694 self = a_ignore_resolve_self(n_UNCONST(self), FAL0);
695 rv = (self != NULL &&
696 (self->i_retain.it_count != 0 || self->i_retain.it_all ||
697 self->i_ignore.it_count != 0 || self->i_ignore.it_all));
698 NYD_LEAVE;
699 return rv;
702 FL bool_t
703 n_ignore_insert(struct n_ignore *self, bool_t retain,
704 char const *dat, size_t len){
705 #ifdef HAVE_REGEX
706 struct a_ignore_re *irp;
707 bool_t isre;
708 #endif
709 struct a_ignore_field *ifp;
710 struct a_ignore_type *itp;
711 bool_t rv;
712 NYD_ENTER;
714 retain = !!retain; /* Make it true bool, TRUM1 has special _lookup meaning */
715 rv = FAL0;
716 self = a_ignore_resolve_self(self, TRU1);
718 if(len == UIZ_MAX)
719 len = strlen(dat);
721 /* Request to ignore or retain _anything_? That is special-treated */
722 if(len == 1 && dat[0] == '*'){
723 itp = retain ? &self->i_retain : &self->i_ignore;
724 if(itp->it_all)
725 rv = TRUM1;
726 else{
727 itp->it_all = TRU1;
728 a_ignore_del_allof(self, retain);
729 rv = TRU1;
731 goto jleave;
734 /* Check for regular expression or valid fieldname */
735 #ifdef HAVE_REGEX
736 if(!(isre = n_is_maybe_regex_buf(dat, len)))
737 #endif
739 char c;
740 size_t i;
742 for(i = 0; i < len; ++i){
743 c = dat[i];
744 if(!fieldnamechar(c))
745 goto jleave;
749 rv = TRUM1;
750 if(a_ignore_lookup(self, retain, dat, len) == (retain ? TRU1 : TRUM1))
751 goto jleave;
753 itp = retain ? &self->i_retain : &self->i_ignore;
755 if(itp->it_count == UI32_MAX){
756 n_err(_("Header selection size limit reached, cannot insert: %.*s\n"),
757 (int)n_MIN(len, SI32_MAX), dat);
758 rv = FAL0;
759 goto jleave;
762 rv = TRU1;
763 #ifdef HAVE_REGEX
764 if(isre){
765 struct a_ignore_re *x;
766 int s;
767 size_t i;
769 i = n_VSTRUCT_SIZEOF(struct a_ignore_re, ir_input) + ++len;
770 irp = self->i_auto ? n_autorec_alloc(i) : n_alloc(i);
771 memcpy(irp->ir_input, dat, --len);
772 irp->ir_input[len] = '\0';
774 if((s = regcomp(&irp->ir_regex, irp->ir_input,
775 REG_EXTENDED | REG_ICASE | REG_NOSUB)) != 0){
776 n_err(_("Invalid regular expression: %s: %s\n"),
777 n_shexp_quote_cp(irp->ir_input, FAL0),
778 n_regex_err_to_doc(&irp->ir_regex, s));
779 if(!self->i_auto)
780 n_free(irp);
781 rv = FAL0;
782 goto jleave;
785 irp->ir_next = NULL;
786 if((x = itp->it_re_tail) != NULL)
787 x->ir_next = irp;
788 else
789 itp->it_re = irp;
790 itp->it_re_tail = irp;
791 }else
792 #endif /* HAVE_REGEX */
794 ui32_t hi;
795 size_t i;
797 i = n_VSTRUCT_SIZEOF(struct a_ignore_field, if_field) + len + 1;
798 ifp = self->i_auto ? n_autorec_alloc(i) : n_alloc(i);
799 memcpy(ifp->if_field, dat, len);
800 ifp->if_field[len] = '\0';
801 hi = n_torek_ihashn(dat, len) % n_NELEM(itp->it_ht);
802 ifp->if_next = itp->it_ht[hi];
803 itp->it_ht[hi] = ifp;
805 ++itp->it_count;
806 jleave:
807 NYD_LEAVE;
808 return rv;
811 FL bool_t
812 n_ignore_lookup(struct n_ignore const *self, char const *dat, size_t len){
813 bool_t rv;
814 NYD_ENTER;
816 if(self == n_IGNORE_ALL)
817 rv = TRUM1;
818 else if(len == 0 ||
819 (self = a_ignore_resolve_self(n_UNCONST(self), FAL0)) == NULL)
820 rv = FAL0;
821 else if(self->i_retain.it_all)
822 rv = TRU1;
823 else if(self->i_retain.it_count == 0 && self->i_ignore.it_all)
824 rv = TRUM1;
825 else
826 rv = a_ignore_lookup(self, TRUM1, dat, len);
827 NYD_LEAVE;
828 return rv;
831 /* s-it-mode */