45 #ifdef HAS_POSIX_REGEXP
53 #ifdef STRCASECMP_IN_STRINGS_H
75 #define DICT_REGEXP_OP_MATCH 1
76 #define DICT_REGEXP_OP_IF 2
77 #define DICT_REGEXP_OP_ENDIF 3
86 } DICT_REGEXP_PATTERN;
91 typedef struct DICT_REGEXP_RULE {
94 struct DICT_REGEXP_RULE *next;
98 DICT_REGEXP_RULE rule;
105 } DICT_REGEXP_MATCH_RULE;
108 DICT_REGEXP_RULE rule;
111 struct DICT_REGEXP_RULE *endif_rule;
112 } DICT_REGEXP_IF_RULE;
120 DICT_REGEXP_RULE *head;
127 #define NULL_SUBSTITUTIONS (0)
128 #define NULL_MATCH_RESULT ((regmatch_t *) 0)
134 DICT_REGEXP *dict_regexp;
135 DICT_REGEXP_MATCH_RULE *match_rule;
136 const char *lookup_string;
137 } DICT_REGEXP_EXPAND_CONTEXT;
147 } DICT_REGEXP_PRESCAN_CONTEXT;
153 #define MAC_PARSE_OK 0
158 static int dict_regexp_expand(
int type,
VSTRING *buf,
void *ptr)
160 DICT_REGEXP_EXPAND_CONTEXT *ctxt = (DICT_REGEXP_EXPAND_CONTEXT *) ptr;
161 DICT_REGEXP_MATCH_RULE *match_rule = ctxt->match_rule;
162 DICT_REGEXP *dict_regexp = ctxt->dict_regexp;
173 if (n < 1 || n > match_rule->max_sub)
174 msg_panic(
"regexp map %s, line %d: out of range replacement index \"%s\"",
175 dict_regexp->dict.name, match_rule->rule.lineno,
177 pmatch = dict_regexp->pmatch + n;
178 if (pmatch->rm_so < 0 || pmatch->rm_so == pmatch->rm_eo)
181 ctxt->lookup_string + pmatch->rm_so,
182 pmatch->rm_eo - pmatch->rm_so);
197 static void dict_regexp_regerror(
const char *mapname,
int lineno,
int error,
202 (void) regerror(error, expr, errbuf,
sizeof(errbuf));
203 msg_warn(
"regexp map %s, line %d: %s", mapname, lineno, errbuf);
209 #define DICT_REGEXP_REGEXEC(err, map, line, expr, match, str, nsub, pmatch) \
210 ((err) = regexec((expr), (str), (nsub), (pmatch), 0), \
211 ((err) == REG_NOMATCH ? !(match) : \
212 (err) == 0 ? (match) : \
213 (dict_regexp_regerror((map), (line), (err), (expr)), 0)))
217 static const char *dict_regexp_lookup(
DICT *dict,
const char *lookup_string)
219 DICT_REGEXP *dict_regexp = (DICT_REGEXP *) dict;
220 DICT_REGEXP_RULE *rule;
221 DICT_REGEXP_IF_RULE *if_rule;
222 DICT_REGEXP_MATCH_RULE *match_rule;
223 DICT_REGEXP_EXPAND_CONTEXT expand_context;
229 msg_info(
"dict_regexp_lookup: %s: %s", dict->
name, lookup_string);
240 for (rule = dict_regexp->head; rule; rule = rule->next) {
248 case DICT_REGEXP_OP_MATCH:
249 match_rule = (DICT_REGEXP_MATCH_RULE *) rule;
250 if (!DICT_REGEXP_REGEXEC(error, dict->
name, rule->lineno,
251 match_rule->first_exp,
252 match_rule->first_match,
254 match_rule->max_sub > 0 ?
255 match_rule->max_sub + 1 : 0,
256 dict_regexp->pmatch))
258 if (match_rule->second_exp
259 && !DICT_REGEXP_REGEXEC(error, dict->
name, rule->lineno,
260 match_rule->second_exp,
261 match_rule->second_match,
272 if (match_rule->max_sub == 0)
273 return (match_rule->replacement);
281 if (!dict_regexp->expansion_buf)
284 expand_context.lookup_string = lookup_string;
285 expand_context.match_rule = match_rule;
286 expand_context.dict_regexp = dict_regexp;
288 if (
mac_parse(match_rule->replacement, dict_regexp_expand,
290 msg_panic(
"regexp map %s, line %d: bad replacement syntax",
291 dict->
name, rule->lineno);
298 case DICT_REGEXP_OP_IF:
299 if_rule = (DICT_REGEXP_IF_RULE *) rule;
300 if (DICT_REGEXP_REGEXEC(error, dict->
name, rule->lineno,
301 if_rule->expr, if_rule->match, lookup_string,
302 NULL_SUBSTITUTIONS, NULL_MATCH_RESULT))
305 if ((rule = if_rule->endif_rule) == 0)
312 case DICT_REGEXP_OP_ENDIF:
316 msg_panic(
"dict_regexp_lookup: impossible operation %d", rule->op);
324 static void dict_regexp_close(
DICT *dict)
326 DICT_REGEXP *dict_regexp = (DICT_REGEXP *) dict;
327 DICT_REGEXP_RULE *rule;
328 DICT_REGEXP_RULE *next;
329 DICT_REGEXP_MATCH_RULE *match_rule;
330 DICT_REGEXP_IF_RULE *if_rule;
332 for (rule = dict_regexp->head; rule; rule = next) {
335 case DICT_REGEXP_OP_MATCH:
336 match_rule = (DICT_REGEXP_MATCH_RULE *) rule;
337 if (match_rule->first_exp) {
338 regfree(match_rule->first_exp);
339 myfree((
void *) match_rule->first_exp);
341 if (match_rule->second_exp) {
342 regfree(match_rule->second_exp);
343 myfree((
void *) match_rule->second_exp);
345 if (match_rule->replacement)
346 myfree((
void *) match_rule->replacement);
348 case DICT_REGEXP_OP_IF:
349 if_rule = (DICT_REGEXP_IF_RULE *) rule;
351 regfree(if_rule->expr);
352 myfree((
void *) if_rule->expr);
355 case DICT_REGEXP_OP_ENDIF:
358 msg_panic(
"dict_regexp_close: unknown operation %d", rule->op);
362 if (dict_regexp->pmatch)
363 myfree((
void *) dict_regexp->pmatch);
364 if (dict_regexp->expansion_buf)
373 static int dict_regexp_get_pat(
const char *mapname,
int lineno,
char **bufp,
374 DICT_REGEXP_PATTERN *pat)
385 pat->match = !pat->match;
391 msg_warn(
"regexp map %s, line %d: no regexp: skipping this rule",
407 }
else if (*p == re_delim) {
413 msg_warn(
"regexp map %s, line %d: no closing regexp delimiter \"%c\": "
414 "skipping this rule", mapname, lineno, re_delim);
422 pat->options = REG_EXTENDED | REG_ICASE;
423 while (*p && !
ISSPACE(*p) && *p !=
'!') {
426 pat->options ^= REG_ICASE;
429 pat->options ^= REG_NEWLINE;
432 pat->options ^= REG_EXTENDED;
435 msg_warn(
"regexp map %s, line %d: unknown regexp option \"%c\": "
436 "skipping this rule", mapname, lineno, *p);
447 static int dict_regexp_get_pats(
const char *mapname,
int lineno,
char **p,
448 DICT_REGEXP_PATTERN *first_pat,
449 DICT_REGEXP_PATTERN *second_pat)
455 if (dict_regexp_get_pat(mapname, lineno, p, first_pat) == 0)
459 static int bitrot_warned = 0;
461 if (bitrot_warned == 0) {
462 msg_warn(
"regexp file %s, line %d: /pattern1/!/pattern2/ goes away,"
463 " use \"if !/pattern2/ ... /pattern1/ ... endif\" instead",
468 if (dict_regexp_get_pat(mapname, lineno, p, second_pat) == 0)
471 second_pat->regexp = 0;
478 static int dict_regexp_prescan(
int type,
VSTRING *buf,
void *context)
480 DICT_REGEXP_PRESCAN_CONTEXT *ctxt = (DICT_REGEXP_PRESCAN_CONTEXT *) context;
494 msg_warn(
"regexp map %s, line %d: non-numeric replacement index \"%s\"",
496 return (MAC_PARSE_ERROR);
500 msg_warn(
"regexp map %s, line %d: out-of-range replacement index \"%s\"",
502 return (MAC_PARSE_ERROR);
504 if (n > ctxt->max_sub)
508 msg_panic(
"regexp map %s, line %d: multiple literals but no $number",
509 ctxt->mapname, ctxt->lineno);
517 static regex_t *dict_regexp_compile_pat(
const char *mapname,
int lineno,
518 DICT_REGEXP_PATTERN *pat)
523 expr = (regex_t *)
mymalloc(
sizeof(*expr));
524 error = regcomp(expr, pat->regexp, pat->options);
526 dict_regexp_regerror(mapname, lineno, error, expr);
535 static DICT_REGEXP_RULE *dict_regexp_rule_alloc(
int op,
int lineno,
size_t size)
537 DICT_REGEXP_RULE *rule;
539 rule = (DICT_REGEXP_RULE *)
mymalloc(size);
541 rule->lineno = lineno;
549 static DICT_REGEXP_RULE *dict_regexp_parseline(
const char *mapname,
int lineno,
550 char *line,
int nesting,
561 DICT_REGEXP_PATTERN first_pat;
562 DICT_REGEXP_PATTERN second_pat;
563 DICT_REGEXP_PRESCAN_CONTEXT prescan_context;
564 regex_t *first_exp = 0;
566 DICT_REGEXP_MATCH_RULE *match_rule;
571 if (!dict_regexp_get_pats(mapname, lineno, &p, &first_pat, &second_pat))
580 msg_warn(
"regexp map %s, line %d: no replacement text: "
581 "using empty string", mapname, lineno);
592 prescan_context.mapname = mapname;
593 prescan_context.lineno = lineno;
594 prescan_context.max_sub = 0;
595 prescan_context.literal = 0;
600 #define CREATE_MATCHOP_ERROR_RETURN(rval) do { \
602 regfree(first_exp); \
603 myfree((void *) first_exp); \
605 if (prescan_context.literal) \
606 myfree(prescan_context.literal); \
610 if (
mac_parse(p, dict_regexp_prescan, (
void *) &prescan_context)
612 msg_warn(
"regexp map %s, line %d: bad replacement syntax: "
613 "skipping this rule", mapname, lineno);
614 CREATE_MATCHOP_ERROR_RETURN(0);
623 if (prescan_context.max_sub == 0)
624 first_pat.options |= REG_NOSUB;
625 if (prescan_context.max_sub > 0 && first_pat.match == 0) {
626 msg_warn(
"regexp map %s, line %d: $number found in negative match "
627 "replacement text: skipping this rule", mapname, lineno);
628 CREATE_MATCHOP_ERROR_RETURN(0);
632 "regular expression substitution is not allowed: "
633 "skipping this rule", mapname, lineno);
634 CREATE_MATCHOP_ERROR_RETURN(0);
636 if ((first_exp = dict_regexp_compile_pat(mapname, lineno,
638 CREATE_MATCHOP_ERROR_RETURN(0);
639 if (prescan_context.max_sub > first_exp->re_nsub) {
640 msg_warn(
"regexp map %s, line %d: out of range replacement index \"%d\": "
641 "skipping this rule", mapname, lineno,
642 (
int) prescan_context.max_sub);
643 CREATE_MATCHOP_ERROR_RETURN(0);
645 if (second_pat.regexp != 0) {
646 second_pat.options |= REG_NOSUB;
647 if ((second_exp = dict_regexp_compile_pat(mapname, lineno,
649 CREATE_MATCHOP_ERROR_RETURN(0);
653 match_rule = (DICT_REGEXP_MATCH_RULE *)
654 dict_regexp_rule_alloc(DICT_REGEXP_OP_MATCH, lineno,
655 sizeof(DICT_REGEXP_MATCH_RULE));
656 match_rule->first_exp = first_exp;
657 match_rule->first_match = first_pat.match;
658 match_rule->max_sub = prescan_context.max_sub;
659 match_rule->second_exp = second_exp;
660 match_rule->second_match = second_pat.match;
661 if (prescan_context.literal)
662 match_rule->replacement = prescan_context.literal;
664 match_rule->replacement =
mystrdup(p);
665 return ((DICT_REGEXP_RULE *) match_rule);
672 DICT_REGEXP_PATTERN pattern;
674 DICT_REGEXP_IF_RULE *if_rule;
679 if (!dict_regexp_get_pat(mapname, lineno, &p, &pattern))
684 msg_warn(
"regexp map %s, line %d: ignoring extra text after"
685 " IF statement: \"%s\"", mapname, lineno, p);
686 msg_warn(
"regexp map %s, line %d: do not prepend whitespace"
687 " to statements between IF and ENDIF", mapname, lineno);
689 if ((expr = dict_regexp_compile_pat(mapname, lineno, &pattern)) == 0)
691 if_rule = (DICT_REGEXP_IF_RULE *)
692 dict_regexp_rule_alloc(DICT_REGEXP_OP_IF, lineno,
693 sizeof(DICT_REGEXP_IF_RULE));
694 if_rule->expr = expr;
695 if_rule->match = pattern.match;
696 if_rule->endif_rule = 0;
697 return ((DICT_REGEXP_RULE *) if_rule);
704 DICT_REGEXP_RULE *rule;
708 msg_warn(
"regexp map %s, line %d: ignoring ENDIF without matching IF",
715 msg_warn(
"regexp map %s, line %d: ignoring extra text after ENDIF",
717 rule = dict_regexp_rule_alloc(DICT_REGEXP_OP_ENDIF, lineno,
718 sizeof(DICT_REGEXP_RULE));
726 msg_warn(
"regexp map %s, line %d: ignoring unrecognized request",
736 const char myname[] =
"dict_regexp_open";
737 DICT_REGEXP *dict_regexp;
741 DICT_REGEXP_RULE *rule;
742 DICT_REGEXP_RULE *last_rule = 0;
748 DICT_REGEXP_RULE **rule_stack = 0;
754 #define DICT_REGEXP_OPEN_RETURN(d) do { \
756 if (line_buffer != 0) \
757 vstring_free(line_buffer); \
759 vstream_fclose(map_fp); \
766 if (open_flags != O_RDONLY)
768 mapname, open_flags, dict_flags,
769 "%s:%s map requires O_RDONLY access mode",
777 open_flags, dict_flags,
778 "open %s: %m", mapname));
785 sizeof(*dict_regexp));
786 dict_regexp->dict.
lookup = dict_regexp_lookup;
787 dict_regexp->dict.close = dict_regexp_close;
791 dict_regexp->head = 0;
792 dict_regexp->pmatch = 0;
793 dict_regexp->expansion_buf = 0;
794 dict_regexp->dict.owner.uid = st.st_uid;
795 dict_regexp->dict.owner.status = (st.st_uid != 0);
800 while (
readllines(line_buffer, map_fp, &last_line, &lineno)) {
805 rule = dict_regexp_parseline(mapname, lineno, p, nesting, dict_flags);
808 if (rule->op == DICT_REGEXP_OP_MATCH) {
809 if (((DICT_REGEXP_MATCH_RULE *) rule)->max_sub > max_sub)
810 max_sub = ((DICT_REGEXP_MATCH_RULE *) rule)->max_sub;
811 }
else if (rule->op == DICT_REGEXP_OP_IF) {
813 rule_stack = (DICT_REGEXP_RULE **)
mvect_alloc(&mvect,
814 sizeof(*rule_stack), nesting + 1,
819 rule_stack[nesting] = rule;
821 }
else if (rule->op == DICT_REGEXP_OP_ENDIF) {
822 DICT_REGEXP_IF_RULE *if_rule;
826 msg_panic(
"%s: ENDIF without IF", myname);
827 if (rule_stack[nesting]->op != DICT_REGEXP_OP_IF)
828 msg_panic(
"%s: unexpected rule stack element type %d",
829 myname, rule_stack[nesting]->op);
830 if_rule = (DICT_REGEXP_IF_RULE *) rule_stack[nesting];
831 if_rule->endif_rule = rule;
834 dict_regexp->head = rule;
836 last_rule->next = rule;
840 while (nesting-- > 0)
841 msg_warn(
"regexp map %s, line %d: IF has no matching ENDIF",
842 mapname, rule_stack[nesting]->lineno);
852 dict_regexp->pmatch =
853 (regmatch_t *)
mymalloc(
sizeof(regmatch_t) * (max_sub + 1));
855 DICT_REGEXP_OPEN_RETURN(
DICT_DEBUG (&dict_regexp->dict));
char * mystrdup(const char *str)
NORETURN msg_panic(const char *fmt,...)
VSTRING * vstring_strncat(VSTRING *vp, const char *src, ssize_t len)
int alldig(const char *string)
int strncasecmp(const char *s1, const char *s2, size_t n)
VSTRING * vstring_strcpy(VSTRING *vp, const char *src)
VSTREAM * vstream_fopen(const char *path, int flags, mode_t mode)
#define VSTRING_TERMINATE(vp)
#define VSTRING_RESET(vp)
void msg_warn(const char *fmt,...)
VSTRING * vstring_alloc(ssize_t len)
char * lowercase(char *string)
const char *(* lookup)(struct DICT *, const char *)
char * mvect_free(MVECT *vect)
DICT * dict_regexp_open(const char *, int, int)
char * trimblanks(char *, ssize_t)
NORETURN msg_fatal(const char *fmt,...)
char * mvect_alloc(MVECT *vect, ssize_t elsize, ssize_t nelm, void(*init_fn)(char *, ssize_t), void(*wipe_fn)(char *, ssize_t))
VSTRING * readllines(VSTRING *buf, VSTREAM *fp, int *lineno, int *first_line)
#define DICT_FLAG_PATTERN
void(* MVECT_FN)(char *, ssize_t)
int mac_parse(const char *value, MAC_PARSE_FN action, void *context)
char * mvect_realloc(MVECT *vect, ssize_t nelm)
VSTRING * vstring_free(VSTRING *vp)
#define DICT_FLAG_NO_REGSUB
#define DICT_FLAG_FOLD_MUL
#define vstream_fileno(vp)
#define MAC_PARSE_VARNAME
DICT * dict_alloc(const char *, const char *, ssize_t)
#define MAC_PARSE_LITERAL
VSTRING * vstring_strcat(VSTRING *vp, const char *src)
DICT * dict_surrogate(const char *dict_type, const char *dict_name, int open_flags, int dict_flags, const char *fmt,...)
void * mymalloc(ssize_t len)
void msg_info(const char *fmt,...)