Codebase list fdm / 82be538
Move matches and decision into struct mail so they get passed around automaticaly. Abstract the regexp stuff more. Nicholas Marriott 17 years ago
24 changed file(s) with 192 addition(s) and 134 deletion(s). Raw diff Collapse all Expand all
1919 to account line
2020 - handle multiple header instances when looking for users (other places?)
2121 - fnmatch in find_header (don't forget ':')
22 - move pmatch etc into struct mail to avoid pointer nonsense?
2322 - add some of the other non-useless stuff reformail can do, if any
2423 - write/append/pipe would benefit from add-from per stdout
2524 - deliver to IMAP folder
241241 for (;;) {
242242 memset(&m, 0, sizeof m);
243243 m.body = -1;
244 m.decision = DECISION_DROP;
244245
245246 memset(&mctx, 0, sizeof mctx);
246247 mctx.io = io;
247248 mctx.account = a;
248249 mctx.mail = &m;
249 /* drop mail by default unless something else comes along */
250 mctx.decision = DECISION_DROP;
251250
252251 error = a->fetch->fetch(a, &m);
253252 switch (error) {
325324 case DECISION_NONE:
326325 log_warnx("%s: reached end of ruleset. no "
327326 "unmatched-mail option; keeping mail", a->name);
328 mctx.decision = DECISION_KEEP;
327 m.decision = DECISION_KEEP;
329328 break;
330329 case DECISION_KEEP:
331330 log_debug("%s: reached end of ruleset. keeping mail",
332331 a->name);
333 mctx.decision = DECISION_KEEP;
332 m.decision = DECISION_KEEP;
334333 break;
335334 case DECISION_DROP:
336335 log_debug("%s: reached end of ruleset. dropping mail",
337336 a->name);
338 mctx.decision = DECISION_DROP;
337 m.decision = DECISION_DROP;
339338 break;
340339 }
341340
342341 done:
343342 if (conf.keep_all || a->keep)
344 mctx.decision = DECISION_KEEP;
343 m.decision = DECISION_KEEP;
345344
346345 /* finished with the message */
347 switch (mctx.decision) {
346 log_debug2("%s: finishing with mail: decision=%d", a->name,
347 m.decision);
348 switch (m.decision) {
348349 case DECISION_DROP:
349350 log_debug("%s: deleting message", a->name);
350351 if (a->fetch->delete != NULL) {
462463
463464 /* tag mail if needed */
464465 if (r->key.str != NULL) {
465 tkey = replacestr(&r->key,
466 m->tags, m, mctx->pm_valid, mctx->pm);
467 tvalue = replacestr(&r->value,
468 m->tags, m, mctx->pm_valid, mctx->pm);
466 tkey = replacestr(&r->key, m->tags, m, &m->rml);
467 tvalue = replacestr(&r->value, m->tags, m, &m->rml);
469468
470469 if (tkey != NULL && *tkey != '\0' && tvalue != NULL) {
471470 log_debug2("%s: tagging message: %s (%s)",
561560
562561 for (i = 0; i < ARRAY_LENGTH(r->actions); i++) {
563562 rs = &ARRAY_ITEM(r->actions, i, struct replstr);
564 s = replacestr(rs, m->tags, m, mctx->pm_valid, mctx->pm);
563 s = replacestr(rs, m->tags, m, &m->rml);
565564
566565 log_debug2("%s: looking for actions matching: %s", a->name, s);
567566 ta = match_actions(s);
612611 memset(&dctx, 0, sizeof dctx);
613612 dctx.account = a;
614613 dctx.mail = m;
615 dctx.decision = &mctx->decision;
616 dctx.pm_valid = &mctx->pm_valid;
617 memcpy(&dctx.pm, mctx->pm, sizeof dctx.pm);
618614
619615 if (t->deliver->deliver(&dctx, t) != DELIVER_SUCCESS)
620616 return (1);
657653 msg.data.action = t;
658654 msg.data.uid = ARRAY_ITEM(users, i, uid_t);
659655
660 msg.data.pm_valid = mctx->pm_valid;
661 memcpy(&msg.data.pm, mctx->pm, sizeof msg.data.pm);
662
663656 mail_send(m, &msg);
664657
665658 if (privsep_send(mctx->io, &msg, m->tags,
700693 /* and recreate the wrapped array */
701694 l = fill_wrapped(m);
702695 log_debug2("%s: found %u wrapped lines", a->name, l);
703
704 /* invalidate the pmatch data since stuff may have moved */
705 mctx->pm_valid = 0;
706696 }
707697
708698 if (find)
3939 struct deliver_add_header_data *data = t->data;
4040 char *hdr, *value;
4141
42 hdr = replacestr(&data->hdr, m->tags, m, *dctx->pm_valid, dctx->pm);
42 hdr = replacestr(&data->hdr, m->tags, m, &m->rml);
4343 if (hdr == NULL || *hdr == '\0') {
4444 if (hdr != NULL)
4545 xfree(hdr);
4646 log_warnx("%s: empty header", a->name);
4747 return (DELIVER_FAILURE);
4848 }
49 value = replacestr(&data->value, m->tags, m, *dctx->pm_valid, dctx->pm);
49 value = replacestr(&data->value, m->tags, m, &m->rml);
5050 if (value == NULL) {
5151 log_warnx("%s: bad value for header %s", a->name, hdr);
5252 xfree(hdr);
6565 ARRAY_FREE(&m->wrapped);
6666 fill_wrapped(m);
6767
68 /* invalidate the pmatch data since stuff may have moved */
69 *dctx->pm_valid = 0;
68 /* invalidate the match data since stuff may have moved */
69 m->rml.valid = 0;
7070
7171 xfree(hdr);
7272 xfree(value);
3434 int
3535 deliver_drop_deliver(struct deliver_ctx *dctx, unused struct action *t)
3636 {
37 *dctx->decision = DECISION_DROP;
37 struct mail *m = dctx->mail;
38
39 m->decision = DECISION_DROP;
3840
3941 return (DELIVER_SUCCESS);
4042 }
3434 int
3535 deliver_keep_deliver(struct deliver_ctx *dctx, unused struct action *t)
3636 {
37 *dctx->decision = DECISION_KEEP;
37 struct mail *m = dctx->mail;
38
39 m->decision = DECISION_KEEP;
3840
3941 return (DELIVER_SUCCESS);
4042 }
5555 size_t first, last;
5656 gid_t gid;
5757
58 path = replacepath(&data->path, m->tags, m, *dctx->pm_valid, dctx->pm);
58 path = replacepath(&data->path, m->tags, m, &m->rml);
5959 if (path == NULL || *path == '\0') {
6060 log_warnx("%s: empty path", a->name);
6161 goto out;
7575 int res = DELIVER_FAILURE;
7676 gzFile gzf = NULL;
7777
78 path = replacepath(&data->path, m->tags, m, *dctx->pm_valid, dctx->pm);
78 path = replacepath(&data->path, m->tags, m, &m->rml);
7979 if (path == NULL || *path == '\0') {
8080 if (path != NULL)
8181 xfree(path);
6161 char *lbuf;
6262 size_t llen;
6363
64 s = replacepath(&data->cmd, m->tags, m, *dctx->pm_valid, dctx->pm);
64 s = replacepath(&data->cmd, m->tags, m, &m->rml);
6565 if (s == NULL || *s == '\0') {
6666 log_warnx("%s: empty command", a->name);
6767 if (s != NULL)
4141 size_t len, off, wrap;
4242 u_int i;
4343
44 hdr = replacestr(&data->hdr, m->tags, m, *dctx->pm_valid, dctx->pm);
44 hdr = replacestr(&data->hdr, m->tags, m, &m->rml);
4545 if (hdr == NULL || *hdr == '\0') {
4646 if (hdr != NULL)
4747 xfree(hdr);
8282 }
8383 }
8484
85 /* invalidate the pmatch data since stuff may have moved */
86 *dctx->pm_valid = 0;
85 /* invalidate the match data since stuff may have moved */
86 m->rml.valid = 0;
8787
8888 set_wrapped(m, '\n');
8989 return (DELIVER_SUCCESS);
5151 char *lbuf;
5252 size_t llen;
5353
54 s = replacepath(&data->cmd, m->tags, m, *dctx->pm_valid, dctx->pm);
54 s = replacepath(&data->cmd, m->tags, m, &m->rml);
5555 if (s == NULL || *s == '\0') {
5656 log_warnx("%s: empty command", a->name);
5757 if (s != NULL)
8686 if (data->to.str == NULL)
8787 to = xstrdup(from);
8888 else {
89 to = replacestr(&data->to, m->tags, m, *dctx->pm_valid,
90 dctx->pm);
89 to = replacestr(&data->to, m->tags, m, &m->rml);
9190 if (to == NULL || *to == '\0') {
9291 log_warnx("%s: empty to", a->name);
9392 goto error;
5858 char *path;
5959 FILE *f;
6060
61 path = replacepath(&data->path, m->tags, m, *dctx->pm_valid, dctx->pm);
61 path = replacepath(&data->path, m->tags, m, &m->rml);
6262 if (path == NULL || *path == '\0') {
6363 if (path != NULL)
6464 xfree(path);
2828 struct mail *mail;
2929
3030 struct mail wr_mail;
31
32 enum decision *decision;
33
34 int *pm_valid;
35 regmatch_t pm[NPMATCH];
3631 };
3732
3833 /* Delivery types. */
+35
-15
fdm.h less more
325325 #define STRB_ENTSIZE(sb) ((sb)->ent_max * (sizeof (struct strbent)))
326326 #define STRB_SIZE(sb) ((sizeof *(sb)) + (sb)->str_size + STRB_ENTSIZE((sb)))
327327
328 /* Regexp wrapper structs. */
329 struct re {
330 char *str;
331 regex_t re;
332 int flags;
333 };
334
335 struct rm {
336 int valid;
337
338 size_t so;
339 size_t eo;
340 };
341
342 struct rmlist {
343 int valid;
344
345 struct rm list[NPMATCH];
346 };
347
348 /* Regexp flags. */
349 #define RE_ICASE 0x1
350 #define RE_NOSUB 0x2
351
328352 /* A single mail. */
329353 struct mail {
330354 struct strb *tags;
345369 ARRAY_DECL(, size_t *) wrapped; /* list of wrapped lines */
346370
347371 ssize_t body; /* offset of body */
372
373 struct rmlist rml; /* regexp matches */
374 enum decision decision; /* final deliver decision */
348375 };
349376
350377 /* An attachment. */
362389 TAILQ_HEAD(, attach) children;
363390
364391 TAILQ_ENTRY(attach) entry;
365 };
366
367 /* Regexp wrapper struct. */
368 struct re {
369 char *str;
370 regex_t re;
371392 };
372393
373394 /* A single child. */
620641 struct msgdata {
621642 int error;
622643 struct mail mail;
623
624 int pm_valid;
625 regmatch_t pm[NPMATCH];
626644
627645 /* these only work so long as they aren't moved in either process */
628646 struct account *account;
710728
711729 /* re.c */
712730 int re_compile(struct re *, char *, int, char **);
713 int re_execute(struct re *, char *, int, regmatch_t *,
714 int, char **);
731 int re_string(struct re *, char *, struct rmlist *,
732 char **);
733 int re_block(struct re *, void *, size_t, struct rmlist *,
734 char **);
715735 int re_simple(struct re *, char *, char **);
716736 void re_free(struct re *);
717737
803823 const char *match_tag(struct strb *, const char *);
804824 void default_tags(struct strb **, char *, struct account *);
805825 void update_tags(struct strb **);
806 char *replace(char *, struct strb *, struct mail *, int,
807 regmatch_t [NPMATCH]);
826 char *replace(char *, struct strb *, struct mail *,
827 struct rmlist *);
808828 char *replacestr(struct replstr *, struct strb *,
809 struct mail *, int, regmatch_t [NPMATCH]);
829 struct mail *, struct rmlist *);
810830 char *replacepath(struct replpath *, struct strb *,
811 struct mail *, int, regmatch_t [NPMATCH]);
831 struct mail *, struct rmlist *);
812832
813833 /* io.c */
814834 struct io *io_create(int, SSL *, const char *, int);
106106 return (MATCH_FALSE);
107107
108108 /* for any type or name matches, construct the value */
109 if (data->op == ATTACHOP_ANYTYPE || data->op == ATTACHOP_ANYNAME) {
110 value = replacestr(&data->value.str,
111 m->tags, m, mctx->pm_valid, mctx->pm);
112 }
109 if (data->op == ATTACHOP_ANYTYPE || data->op == ATTACHOP_ANYNAME)
110 value = replacestr(&data->value.str, m->tags, m, &m->rml);
113111
114112 at = m->attach;
115113 while (at != NULL) {
5050 msg.data.cmddata = data;
5151 msg.data.uid = data->uid;
5252
53 msg.data.pm_valid = mctx->pm_valid;
54 memcpy(&msg.data.pm, mctx->pm, sizeof msg.data.pm);
55
5653 mail_send(m, &msg);
5754
5855 if (privsep_send(io, &msg, m->tags, STRB_SIZE(m->tags)) != 0)
3838 struct mail *m = mctx->mail;
3939 int res;
4040 char *cause;
41 size_t so, eo;
4142
42 if (data->area == AREA_BODY && m->body == -1)
43 return (MATCH_FALSE);
44
43 so = 0;
44 eo = m->size;
4545 switch (data->area) {
4646 case AREA_HEADERS:
47 mctx->pm[0].rm_so = 0;
48 if (m->body == -1)
49 mctx->pm[0].rm_eo = m->size;
50 else
51 mctx->pm[0].rm_eo = m->body;
47 if (m->body != -1)
48 eo = m->body;
5249 break;
5350 case AREA_BODY:
54 mctx->pm[0].rm_so = m->body;
55 mctx->pm[0].rm_eo = m->size;
51 if (m->body == -1)
52 return (MATCH_FALSE);
53 so = m->body;
5654 break;
5755 case AREA_ANY:
58 mctx->pm[0].rm_so = 0;
59 mctx->pm[0].rm_eo = m->size;
6056 break;
6157 }
6258
63 res = re_execute(&data->re, m->data, NPMATCH, mctx->pm, REG_STARTEND,
64 &cause);
59 res = re_block(&data->re, m->data + so, eo - so, &m->rml, &cause);
6560 if (res == -1) {
6661 log_warnx("%s: %s", a->name, cause);
6762 xfree(cause);
6863 return (MATCH_ERROR);
6964 }
70
71 mctx->pm_valid = 1;
7265 if (res == 0)
7366 return (MATCH_FALSE);
7467 return (MATCH_TRUE);
4040 int res;
4141 char *s, *cause;
4242
43 s = replacestr(&data->str, m->tags, m, mctx->pm_valid, mctx->pm);
43 s = replacestr(&data->str, m->tags, m, &m->rml);
4444 log_debug2("%s: matching \"%s\" to \"%s\"", a->name, s, data->re.str);
4545
4646 res = re_simple(&data->re, s, &cause);
3838 struct mail *m = mctx->mail;
3939 char *tag;
4040
41 tag = replacestr(&data->tag, m->tags, m, mctx->pm_valid, mctx->pm);
41 tag = replacestr(&data->tag, m->tags, m, &m->rml);
4242 if (match_tag(m->tags, tag) != NULL) {
4343 xfree(tag);
4444 return (MATCH_TRUE);
2929 struct account *account;
3030 struct mail *mail;
3131
32 enum decision decision;
33
3432 int matched;
3533 int stopped;
36
37 int pm_valid;
38 regmatch_t pm[NPMATCH];
3934 };
4035
4136 /* Match functions. */
6464 memset(dctx, 0, sizeof dctx);
6565 dctx->account = msg->data.account;
6666 dctx->mail = m;
67 dctx->decision = NULL; /* only altered in child */
68 dctx->pm_valid = &msg->data.pm_valid;
69 memcpy(&dctx->pm, &msg->data.pm, sizeof dctx->pm);
7067 }
7168
7269 if (mctx != NULL) {
7370 memset(mctx, 0, sizeof mctx);
7471 mctx->account = msg->data.account;
7572 mctx->mail = m;
76 mctx->decision = DECISION_NONE; /* only altered in child */
77 mctx->pm_valid = msg->data.pm_valid;
78 memcpy(&mctx->pm, &msg->data.pm, sizeof mctx->pm);
7973 }
8074 }
8175
282276 if (geteuid() == 0 &&
283277 fchown(md->shm.fd, conf.child_uid, conf.child_gid) != 0)
284278 fatal("fchown");
279 md->decision = m->decision;
285280 }
286281
287282 if (parent_child(a,
360355 char *s, *cause, *lbuf, *out, *err, tag[24];
361356 size_t llen;
362357 struct cmd *cmd = NULL;
363 regmatch_t pm[NPMATCH];
358 struct rmlist rml;
364359 u_int i;
365360
366361 /* if this is the parent, do nothing */
368363 return (0);
369364
370365 /* sort out the command */
371 s = replacepath(&data->cmd, m->tags, m, mctx->pm_valid, mctx->pm);
366 s = replacepath(&data->cmd, m->tags, m, &m->rml);
372367 if (s == NULL || *s == '\0') {
373368 log_warnx("%s: empty command", a->name);
374369 goto error;
406401 if (found) /* XXX stop early? */
407402 continue;
408403
409 found = re_execute(&data->re, out, NPMATCH, pm, 0, &cause);
404 found = re_string(&data->re, out, &rml, &cause);
410405 if (found == -1) {
411406 log_warnx("%s: %s", a->name, cause);
412407 goto error;
413408 }
414409 if (found != 1)
415410 continue;
416 /* save the pmatch */
411 /* save the matches */
412 if (!rml.valid)
413 continue;
417414 for (i = 0; i < NPMATCH; i++) {
418 if (pm[i].rm_so >= pm[i].rm_eo)
419 continue;
415 if (!rml.list[i].valid)
416 break;
420417 xsnprintf(tag, sizeof tag, "command%u", i);
421 add_tag(&m->tags, tag, "%.*s", (int) (pm[i].rm_eo -
422 pm[i].rm_so), out + pm[i].rm_so);
418 add_tag(&m->tags, tag, "%.*s", (int) (rml.list[i].eo -
419 rml.list[i].so), out + rml.list[i].so);
423420 }
424421 } while (status >= 0);
425422 status = -1 - status;
740740 }
741741
742742 rs.str = $1;
743 $$ = replacestr(&rs, parse_tags, NULL, 0, NULL);
743 $$ = replacestr(&rs, parse_tags, NULL, NULL);
744744 xfree($1);
745745 }
746746
754754 }
755755
756756 rp.str = $1;
757 $$ = replacepath(&rp, parse_tags, NULL, 0, NULL);
757 $$ = replacepath(&rp, parse_tags, NULL, NULL);
758758 xfree($1);
759759 }
760760
18971897
18981898 data->area = $4;
18991899
1900 flags = REG_EXTENDED|REG_NEWLINE;
1900 flags = 0;
19011901 if ($2)
1902 flags |= REG_ICASE;
1902 flags |= RE_ICASE;
19031903 if (re_compile(&data->re, $3, flags, &cause) != 0)
19041904 yyerror("%s", cause);
19051905 xfree($3);
19311931 data->ret = $7;
19321932
19331933 if ($9 != NULL) {
1934 flags = REG_EXTENDED|REG_NEWLINE;
1935 if (re_compile(&data->re, $9, flags, &cause) != 0)
1934 if (re_compile(&data->re, $9, 0, &cause) != 0)
19361935 yyerror("%s", cause);
19371936 xfree($9);
19381937 }
19811980 /** [$1: not (int)] [$3: strv (char *)] [$5: strv (char *)] */
19821981 {
19831982 struct match_string_data *data;
1984 int flags;
19851983 char *cause;
19861984
19871985 if (*$3 == '\0')
19971995
19981996 data->str.str = $3;
19991997
2000 flags = REG_EXTENDED|REG_NOSUB|REG_NEWLINE;
2001 if (re_compile(&data->re, $5, flags, &cause) != 0)
1998 if (re_compile(&data->re, $5, RE_NOSUB, &cause) != 0)
20021999 yyerror("%s", cause);
20032000 xfree($5);
20042001 }
+87
-12
re.c less more
1717
1818 #include <sys/types.h>
1919
20 #include <string.h>
21
2022 #include "fdm.h"
2123
2224 int
2628 size_t len;
2729 char *buf;
2830
29 if (s == NULL) {
30 *cause = xstrdup("invalid regexp");
31 return (1);
32 }
31 if (s == NULL)
32 fatalx("re_compile: null regexp");
3333 re->str = xstrdup(s);
3434 if (*s == '\0')
3535 return (0);
36 re->flags = flags;
37
38 flags = REG_EXTENDED|REG_NEWLINE;
39 if (re->flags & RE_NOSUB)
40 flags |= REG_NOSUB;
41 if (re->flags & RE_ICASE)
42 flags |= REG_ICASE;
3643
3744 if ((error = regcomp(&re->re, s, flags)) != 0) {
3845 len = regerror(error, &re->re, NULL, 0);
4653 }
4754
4855 int
49 re_execute(struct re *re, char *s, int npm, regmatch_t *pm, int flags,
50 char **cause)
56 re_string(struct re *re, char *s, struct rmlist *rml, char **cause)
5157 {
52 int res;
58 int res;
59 regmatch_t pm[NPMATCH];
60 u_int i;
61
62 if (re->flags & RE_NOSUB) {
63 if (rml != NULL)
64 fatalx("re_string: nosub re but rml != NULL");
65 } else {
66 if (rml == NULL)
67 fatalx("re_string: !nosub re but rml == NULL");
68 }
69
70 if (rml != NULL)
71 memset(rml, 0, NPMATCH * (sizeof *rml));
5372
5473 /*
5574 * If the source string is empty, there is no regexp, so just check
6180 return (0);
6281 }
6382
64 res = regexec(&re->re, s, npm, pm, flags);
83 res = regexec(&re->re, s, NPMATCH, pm, 0);
6584 if (res != 0 && res != REG_NOMATCH) {
6685 xasprintf(cause, "%s: regexec failed", re->str);
6786 return (-1);
87 } else
88
89 if (rml != NULL) {
90 for (i = 0; i < NPMATCH; i++) {
91 if (pm[i].rm_eo <= pm[i].rm_so)
92 break;
93 rml->list[i].valid = 1;
94 rml->list[i].so = pm[i].rm_so;
95 rml->list[i].eo = pm[i].rm_eo;
96 }
97 rml->valid = 1;
6898 }
6999
70 if (res == 0)
71 return (1);
72 return (0);
100 return (res == 0);
101 }
102
103 int
104 re_block(struct re *re, void *buf, size_t len, struct rmlist *rml, char **cause)
105 {
106 int res;
107 regmatch_t pm[NPMATCH];
108 u_int i;
109
110 if (re->flags & RE_NOSUB) {
111 if (rml != NULL)
112 fatalx("re_string: nosub re but rml != NULL");
113 } else {
114 if (rml == NULL)
115 fatalx("re_string: !nosub re but rml == NULL");
116 }
117
118 if (rml != NULL)
119 memset(rml, 0, sizeof *rml);
120
121 /* If the regexp is empty, just check whether the buffer is empty. */
122 if (*re->str == '\0') {
123 if (len == 0)
124 return (1);
125 return (0);
126 }
127
128 pm[0].rm_so = 0;
129 pm[0].rm_eo = len;
130 res = regexec(&re->re, buf, NPMATCH, pm, REG_STARTEND);
131 if (res != 0 && res != REG_NOMATCH) {
132 xasprintf(cause, "%s: regexec failed", re->str);
133 return (-1);
134 } else
135
136 if (rml != NULL) {
137 for (i = 0; i < NPMATCH; i++) {
138 if (pm[i].rm_eo <= pm[i].rm_so)
139 break;
140 rml->list[i].valid = 1;
141 rml->list[i].so = pm[i].rm_so;
142 rml->list[i].eo = pm[i].rm_eo;
143 }
144 rml->valid = 1;
145 }
146
147 return (res == 0);
73148 }
74149
75150 int
76151 re_simple(struct re *re, char *s, char **cause)
77152 {
78 return (re_execute(re, s, 0, NULL, 0, cause));
153 return (re_string(re, s, NULL, cause));
79154 }
80155
81156 void
158158
159159 char *
160160 replacestr(struct replstr *rs, struct strb *tags, struct mail *m,
161 int pm_valid, regmatch_t pm[NPMATCH])
162 {
163 return (replace(rs->str, tags, m, pm_valid, pm));
161 struct rmlist *rml)
162 {
163 return (replace(rs->str, tags, m, rml));
164164 }
165165
166166 char *
167167 replacepath(struct replpath *rp, struct strb *tags, struct mail *m,
168 int pm_valid, regmatch_t pm[NPMATCH])
168 struct rmlist *rml)
169169 {
170170 char *s, *ss;
171171
172 s = replace(rp->str, tags, m, pm_valid, pm);
172 s = replace(rp->str, tags, m, rml);
173173 ss = expand_path(s);
174174 if (ss == NULL)
175175 return (s);
178178 }
179179
180180 char *
181 replace(char *src, struct strb *tags, struct mail *m, int pm_valid,
182 regmatch_t pm[NPMATCH])
181 replace(char *src, struct strb *tags, struct mail *m, struct rmlist *rml)
183182 {
184183 char *ptr, *tend;
185184 const char *tptr, *alias;
236235 break;
237236 default:
238237 if (ch >= '0' && ch <= '9') {
239 if (!pm_valid || m == NULL || pm == NULL)
238 if (rml == NULL || !rml->valid || m == NULL)
240239 continue;
241240 idx = ((u_char) ch) - '0';
242 if (pm[idx].rm_so >= pm[idx].rm_eo)
241 if (!rml->list[idx].valid)
243242 continue;
244243
245 tptr = m->base + pm[idx].rm_so;
246 tlen = pm[idx].rm_eo - pm[idx].rm_so;
244 tptr = m->base + rml->list[idx].so;
245 tlen = rml->list[idx].eo - rml->list[idx].so;
247246 break;
248247 }
249248