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