mirror of
https://github.com/vdukhovni/postfix
synced 2025-08-30 13:48:06 +00:00
postfix-1.1.11-20020907
This commit is contained in:
committed by
Viktor Dukhovni
parent
87bb4754ed
commit
7a8151a014
3
postfix/.indent.pro
vendored
3
postfix/.indent.pro
vendored
@@ -40,6 +40,9 @@
|
||||
-TDICT_OPEN_INFO
|
||||
-TDICT_PCRE
|
||||
-TDICT_REGEXP
|
||||
-TDICT_REGEXP_EXPAND_CONTEXT
|
||||
-TDICT_REGEXP_PATTERN
|
||||
-TDICT_REGEXP_PRESCAN_CONTEXT
|
||||
-TDICT_REGEXP_RULE
|
||||
-TDICT_TCP
|
||||
-TDICT_UNIX
|
||||
|
@@ -297,8 +297,10 @@ static void qmqpd_copy_recipients(QMQPD_STATE *state)
|
||||
/*
|
||||
* Append the optional recipient who is copied on all mail.
|
||||
*/
|
||||
if (*var_always_bcc)
|
||||
rec_fputs(state->cleanup, REC_TYPE_RCPT, var_always_bcc);
|
||||
if (state->err == CLEANUP_STAT_OK
|
||||
&& *var_always_bcc
|
||||
&& rec_fputs(state->cleanup, REC_TYPE_RCPT, var_always_bcc) < 0)
|
||||
state->err = CLEANUP_STAT_WRITE;
|
||||
}
|
||||
|
||||
/* qmqpd_next_line - get line from buffer, return last char, newline, or -1 */
|
||||
|
@@ -12,7 +12,8 @@
|
||||
/* int dict_flags;
|
||||
/* DESCRIPTION
|
||||
/* dict_regexp_open() opens the named file and compiles the contained
|
||||
/* regular expressions.
|
||||
/* regular expressions. The result object can be used to match strings
|
||||
/* against the table.
|
||||
/* SEE ALSO
|
||||
/* dict(3) generic dictionary manager
|
||||
/* AUTHOR(S)
|
||||
@@ -25,7 +26,7 @@
|
||||
/* Level 3, 213 Miller St
|
||||
/* North Sydney, NSW, Australia
|
||||
/*
|
||||
/* Wietse Venema
|
||||
/* Heavily rewritten by Wietse Venema
|
||||
/* IBM T.J. Watson Research
|
||||
/* P.O. Box 704
|
||||
/* Yorktown Heights, NY 10598, USA
|
||||
@@ -56,70 +57,115 @@
|
||||
#include "dict_regexp.h"
|
||||
#include "mac_parse.h"
|
||||
|
||||
/*
|
||||
* Regular expression before compiling.
|
||||
*/
|
||||
typedef struct {
|
||||
char *regexp; /* regular expression */
|
||||
int options; /* regcomp() options */
|
||||
} DICT_REGEXP_PATTERN;
|
||||
|
||||
/*
|
||||
* Compiled regexp rule with replacement text.
|
||||
*/
|
||||
typedef struct dict_regexp_list {
|
||||
struct dict_regexp_list *next; /* Next regexp in dict */
|
||||
regex_t *expr[2]; /* The compiled pattern(s) */
|
||||
char *replace; /* Replacement string */
|
||||
int lineno; /* Source file line number */
|
||||
struct dict_regexp_list *next; /* next regexp in dict */
|
||||
regex_t *primary_exp; /* compiled primary pattern */
|
||||
regex_t *negated_exp; /* compiled negated pattern */
|
||||
char *replacement; /* replacement text */
|
||||
size_t max_nsub; /* largest replacement $number */
|
||||
int lineno; /* source file line number */
|
||||
} DICT_REGEXP_RULE;
|
||||
|
||||
/*
|
||||
* Regexp map.
|
||||
*/
|
||||
typedef struct {
|
||||
DICT dict; /* generic members */
|
||||
char *map; /* map name */
|
||||
int flags; /* unused at the moment */
|
||||
regmatch_t *pmatch; /* Cut substrings */
|
||||
int nmatch; /* number of elements in pmatch */
|
||||
regmatch_t *pmatch; /* replacement substring storage */
|
||||
DICT_REGEXP_RULE *head; /* first rule */
|
||||
} DICT_REGEXP;
|
||||
|
||||
/*
|
||||
* Context for macro expansion callback.
|
||||
*/
|
||||
struct dict_regexp_context {
|
||||
/*
|
||||
* Context for $number expansion callback.
|
||||
*/
|
||||
typedef struct {
|
||||
DICT_REGEXP *dict; /* the dictionary entry */
|
||||
DICT_REGEXP_RULE *rule; /* the rule we matched */
|
||||
VSTRING *buf; /* target string buffer */
|
||||
const char *subject; /* str against which we match */
|
||||
};
|
||||
const char *subject; /* matched text */
|
||||
} DICT_REGEXP_EXPAND_CONTEXT;
|
||||
|
||||
/*
|
||||
* Macro expansion callback - replace $0-${99} with strings cut from
|
||||
* matched string.
|
||||
*/
|
||||
static int dict_regexp_action(int type, VSTRING *buf, char *ptr)
|
||||
/*
|
||||
* Context for $number pre-scan callback.
|
||||
*/
|
||||
typedef struct {
|
||||
const char *map; /* name of regexp map */
|
||||
int lineno; /* where in file */
|
||||
size_t max_nsub; /* largest $number seen */
|
||||
} DICT_REGEXP_PRESCAN_CONTEXT;
|
||||
|
||||
/*
|
||||
* Compatibility.
|
||||
*/
|
||||
#ifndef MAC_PARSE_OK
|
||||
#define MAC_PARSE_OK 0
|
||||
#endif
|
||||
|
||||
/* dict_regexp_expand - replace $number with substring from matched text */
|
||||
|
||||
static int dict_regexp_expand(int type, VSTRING *buf, char *ptr)
|
||||
{
|
||||
struct dict_regexp_context *ctxt = (struct dict_regexp_context *) ptr;
|
||||
DICT_REGEXP_EXPAND_CONTEXT *ctxt = (DICT_REGEXP_EXPAND_CONTEXT *) ptr;
|
||||
DICT_REGEXP_RULE *rule = ctxt->rule;
|
||||
DICT_REGEXP *dict = ctxt->dict;
|
||||
int n;
|
||||
size_t n;
|
||||
|
||||
/*
|
||||
* Replace $number by the corresponding substring from the matched text.
|
||||
* We pre-scanned the replacement text at compile time, so any out of
|
||||
* range $number means that something impossible has happened.
|
||||
*/
|
||||
if (type == MAC_PARSE_VARNAME) {
|
||||
n = atoi(vstring_str(buf));
|
||||
if (n >= dict->nmatch)
|
||||
msg_fatal("regexp %s, line %d: replacement index out of range",
|
||||
dict->dict.name, rule->lineno);
|
||||
if (n < 1 || n > rule->max_nsub)
|
||||
msg_panic("regexp map %s, line %d: out of range replacement index \"%s\"",
|
||||
dict->dict.name, rule->lineno, vstring_str(buf));
|
||||
if (dict->pmatch[n].rm_so < 0 ||
|
||||
dict->pmatch[n].rm_so == dict->pmatch[n].rm_eo) {
|
||||
return (MAC_PARSE_UNDEF); /* empty string or not
|
||||
* matched */
|
||||
return (MAC_PARSE_UNDEF); /* empty or not matched */
|
||||
}
|
||||
vstring_strncat(ctxt->buf, ctxt->subject + dict->pmatch[n].rm_so,
|
||||
dict->pmatch[n].rm_eo - dict->pmatch[n].rm_so);
|
||||
} else
|
||||
/* Straight text - duplicate with no substitution */
|
||||
}
|
||||
|
||||
/*
|
||||
* Straight text - duplicate with no substitution.
|
||||
*/
|
||||
else
|
||||
vstring_strcat(ctxt->buf, vstring_str(buf));
|
||||
return (0);
|
||||
|
||||
return (MAC_PARSE_OK);
|
||||
}
|
||||
|
||||
/*
|
||||
* Look up regexp dict and perform string substitution on matched
|
||||
* strings.
|
||||
*/
|
||||
/* dict_regexp_regerror - report regexp compile/execute error */
|
||||
|
||||
static void dict_regexp_regerror(const char *map, int lineno, int error,
|
||||
const regex_t * expr)
|
||||
{
|
||||
char errbuf[256];
|
||||
|
||||
(void) regerror(error, expr, errbuf, sizeof(errbuf));
|
||||
msg_warn("regexp map %s, line %d: %s", map, lineno, errbuf);
|
||||
}
|
||||
|
||||
/* dict_regexp_lookup - match string and perform substitution */
|
||||
|
||||
static const char *dict_regexp_lookup(DICT *dict, const char *name)
|
||||
{
|
||||
DICT_REGEXP *dict_regexp = (DICT_REGEXP *) dict;
|
||||
DICT_REGEXP_RULE *rule;
|
||||
struct dict_regexp_context ctxt;
|
||||
DICT_REGEXP_EXPAND_CONTEXT ctxt;
|
||||
static VSTRING *buf;
|
||||
int error;
|
||||
|
||||
@@ -128,54 +174,72 @@ static const char *dict_regexp_lookup(DICT *dict, const char *name)
|
||||
if (msg_verbose)
|
||||
msg_info("dict_regexp_lookup: %s: %s", dict_regexp->dict.name, name);
|
||||
|
||||
/* Search for a matching expression */
|
||||
/*
|
||||
* Search for the first matching primary expression. Limit the overhead
|
||||
* for substring substitution to the bare minimum.
|
||||
*/
|
||||
for (rule = dict_regexp->head; rule; rule = rule->next) {
|
||||
error = regexec(rule->expr[0], name, rule->expr[0]->re_nsub + 1,
|
||||
dict_regexp->pmatch, 0);
|
||||
if (!error) {
|
||||
if (rule->expr[1]) {
|
||||
error = regexec(rule->expr[1], name, rule->expr[1]->re_nsub + 1,
|
||||
dict_regexp->pmatch + rule->expr[0]->re_nsub + 1, 0);
|
||||
if (!error) {
|
||||
continue;
|
||||
} else if (error != REG_NOMATCH) {
|
||||
char errbuf[256];
|
||||
|
||||
(void) regerror(error, rule->expr[1], errbuf, sizeof(errbuf));
|
||||
msg_fatal("regexp map %s, line %d: %s.",
|
||||
dict_regexp->dict.name, rule->lineno, errbuf);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* We found a match. Do some final initialization on the
|
||||
* subexpression fields, and perform substitution on replacement
|
||||
* string
|
||||
*/
|
||||
if (!buf)
|
||||
buf = vstring_alloc(10);
|
||||
VSTRING_RESET(buf);
|
||||
ctxt.buf = buf;
|
||||
ctxt.subject = name;
|
||||
ctxt.rule = rule;
|
||||
ctxt.dict = dict_regexp;
|
||||
|
||||
if (mac_parse(rule->replace, dict_regexp_action, (char *) &ctxt) & MAC_PARSE_ERROR)
|
||||
msg_fatal("regexp map %s, line %d: bad replacement syntax.",
|
||||
dict_regexp->dict.name, rule->lineno);
|
||||
|
||||
VSTRING_TERMINATE(buf);
|
||||
return (vstring_str(buf));
|
||||
} else if (error && error != REG_NOMATCH) {
|
||||
char errbuf[256];
|
||||
|
||||
(void) regerror(error, rule->expr[0], errbuf, sizeof(errbuf));
|
||||
msg_fatal("regexp map %s, line %d: %s.",
|
||||
dict_regexp->dict.name, rule->lineno, errbuf);
|
||||
return ((char *) 0);
|
||||
error = regexec(rule->primary_exp, name, rule->max_nsub + 1,
|
||||
rule->max_nsub ? dict_regexp->pmatch :
|
||||
(regmatch_t *) 0, 0);
|
||||
switch (error) {
|
||||
case REG_NOMATCH:
|
||||
continue;
|
||||
default:
|
||||
dict_regexp_regerror(dict_regexp->dict.name, rule->lineno,
|
||||
error, rule->primary_exp);
|
||||
continue;
|
||||
case 0:
|
||||
break;
|
||||
}
|
||||
|
||||
/*
|
||||
* Primary expression match found. Require a negative match on the
|
||||
* optional negated expression. In this case we're never going to do
|
||||
* any string substitution.
|
||||
*/
|
||||
if (rule->negated_exp) {
|
||||
error = regexec(rule->negated_exp, name, 0, (regmatch_t *) 0, 0);
|
||||
switch (error) {
|
||||
case 0:
|
||||
continue;
|
||||
default:
|
||||
dict_regexp_regerror(dict_regexp->dict.name, rule->lineno,
|
||||
error, rule->negated_exp);
|
||||
continue;
|
||||
case REG_NOMATCH:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Match found. Skip $number substitutions when the replacement text
|
||||
* contains no $number strings (as learned during the pre-scan).
|
||||
*/
|
||||
if (rule->max_nsub == 0)
|
||||
return (rule->replacement);
|
||||
|
||||
/*
|
||||
* Perform $number substitutions on the replacement text. We
|
||||
* pre-scanned the replacement text at compile time. Any macro
|
||||
* expansion errors at this point mean something impossible has
|
||||
* happened.
|
||||
*/
|
||||
if (!buf)
|
||||
buf = vstring_alloc(10);
|
||||
VSTRING_RESET(buf);
|
||||
ctxt.buf = buf;
|
||||
ctxt.subject = name;
|
||||
ctxt.rule = rule;
|
||||
ctxt.dict = dict_regexp;
|
||||
|
||||
if (mac_parse(rule->replacement, dict_regexp_expand, (char *) &ctxt) & MAC_PARSE_ERROR)
|
||||
msg_panic("regexp map %s, line %d: bad replacement syntax",
|
||||
dict_regexp->dict.name, rule->lineno);
|
||||
VSTRING_TERMINATE(buf);
|
||||
return (vstring_str(buf));
|
||||
}
|
||||
return ((char *) 0);
|
||||
return (0);
|
||||
}
|
||||
|
||||
/* dict_regexp_close - close regexp dictionary */
|
||||
@@ -183,19 +247,20 @@ static const char *dict_regexp_lookup(DICT *dict, const char *name)
|
||||
static void dict_regexp_close(DICT *dict)
|
||||
{
|
||||
DICT_REGEXP *dict_regexp = (DICT_REGEXP *) dict;
|
||||
DICT_REGEXP_RULE *rule,
|
||||
*next;
|
||||
int i;
|
||||
DICT_REGEXP_RULE *rule;
|
||||
DICT_REGEXP_RULE *next;
|
||||
|
||||
for (rule = dict_regexp->head; rule; rule = next) {
|
||||
next = rule->next;
|
||||
for (i = 0; i < 2; i++) {
|
||||
if (rule->expr[i]) {
|
||||
regfree(rule->expr[i]);
|
||||
myfree((char *) rule->expr[i]);
|
||||
}
|
||||
if (rule->primary_exp) {
|
||||
regfree(rule->primary_exp);
|
||||
myfree((char *) rule->primary_exp);
|
||||
}
|
||||
myfree((char *) rule->replace);
|
||||
if (rule->negated_exp) {
|
||||
regfree(rule->negated_exp);
|
||||
myfree((char *) rule->negated_exp);
|
||||
}
|
||||
myfree((char *) rule->replacement);
|
||||
myfree((char *) rule);
|
||||
}
|
||||
if (dict_regexp->pmatch)
|
||||
@@ -203,19 +268,20 @@ static void dict_regexp_close(DICT *dict)
|
||||
dict_free(dict);
|
||||
}
|
||||
|
||||
static regex_t *dict_regexp_get_expr(int lineno, char **bufp, VSTREAM *map_fp)
|
||||
/* dict_regexp_get_pattern - extract one pattern with options from rule */
|
||||
|
||||
static int dict_regexp_get_pattern(const char *map, int lineno, char **bufp,
|
||||
DICT_REGEXP_PATTERN *pat)
|
||||
{
|
||||
char *p = *bufp,
|
||||
*regexp,
|
||||
re_delim;
|
||||
int re_options,
|
||||
error;
|
||||
regex_t *expr;
|
||||
char *p = *bufp;
|
||||
char re_delim;
|
||||
|
||||
re_delim = *p++;
|
||||
regexp = p;
|
||||
pat->regexp = p;
|
||||
|
||||
/* Search for second delimiter, handling backslash escape */
|
||||
/*
|
||||
* Search for the closing delimiter, handling backslash escape.
|
||||
*/
|
||||
while (*p) {
|
||||
if (*p == '\\') {
|
||||
if (p[1])
|
||||
@@ -228,119 +294,186 @@ static regex_t *dict_regexp_get_expr(int lineno, char **bufp, VSTREAM *map_fp)
|
||||
++p;
|
||||
}
|
||||
if (!*p) {
|
||||
msg_warn("%s, line %d: no closing regexp delimiter: %c",
|
||||
VSTREAM_PATH(map_fp), lineno, re_delim);
|
||||
return NULL;
|
||||
msg_warn("regexp map %s, line %d: no closing regexp delimiter \"%c\": "
|
||||
"skipping this rule", map, lineno, re_delim);
|
||||
return (0);
|
||||
}
|
||||
*p++ = '\0'; /* Null term the regexp */
|
||||
*p++ = 0; /* null terminate */
|
||||
|
||||
re_options = REG_EXTENDED | REG_ICASE;
|
||||
while (*p) {
|
||||
if (!*p || ISSPACE(*p) || (*p == '!' && p[1] == re_delim)) {
|
||||
/* end of the regexp */
|
||||
expr = (regex_t *) mymalloc(sizeof(*expr));
|
||||
error = regcomp(expr, regexp, re_options);
|
||||
if (error != 0) {
|
||||
char errbuf[256];
|
||||
|
||||
(void) regerror(error, expr, errbuf, sizeof(errbuf));
|
||||
msg_warn("%s, line %d: error in regexp: %s.",
|
||||
VSTREAM_PATH(map_fp), lineno, errbuf);
|
||||
myfree((char *) expr);
|
||||
return NULL;
|
||||
}
|
||||
*bufp = p;
|
||||
return expr;
|
||||
} else {
|
||||
switch (*p) {
|
||||
case 'i':
|
||||
re_options ^= REG_ICASE;
|
||||
break;
|
||||
case 'm':
|
||||
re_options ^= REG_NEWLINE;
|
||||
break;
|
||||
case 'x':
|
||||
re_options ^= REG_EXTENDED;
|
||||
break;
|
||||
default:
|
||||
msg_warn("%s, line %d: unknown regexp option '%c'",
|
||||
VSTREAM_PATH(map_fp), lineno, *p);
|
||||
}
|
||||
++p;
|
||||
/*
|
||||
* Search for options.
|
||||
*/
|
||||
pat->options = REG_EXTENDED | REG_ICASE;
|
||||
while (*p && !ISSPACE(*p) && *p != '!') {
|
||||
switch (*p) {
|
||||
case 'i':
|
||||
pat->options ^= REG_ICASE;
|
||||
break;
|
||||
case 'm':
|
||||
pat->options ^= REG_NEWLINE;
|
||||
break;
|
||||
case 'x':
|
||||
pat->options ^= REG_EXTENDED;
|
||||
break;
|
||||
default:
|
||||
msg_warn("regexp map %s, line %d: unknown regexp option \"%c\": "
|
||||
"skipping this rule", map, lineno, *p);
|
||||
return (0);
|
||||
}
|
||||
++p;
|
||||
}
|
||||
return NULL;
|
||||
*bufp = p;
|
||||
return (1);
|
||||
}
|
||||
|
||||
static DICT_REGEXP_RULE *dict_regexp_parseline(int lineno, char *line, int *nsub, VSTREAM *map_fp)
|
||||
/* dict_regexp_compile - compile one pattern */
|
||||
|
||||
static regex_t *dict_regexp_compile(const char *map, int lineno,
|
||||
DICT_REGEXP_PATTERN *pat)
|
||||
{
|
||||
int error;
|
||||
regex_t *expr;
|
||||
|
||||
expr = (regex_t *) mymalloc(sizeof(*expr));
|
||||
error = regcomp(expr, pat->regexp, pat->options);
|
||||
if (error != 0) {
|
||||
dict_regexp_regerror(map, lineno, error, expr);
|
||||
myfree((char *) expr);
|
||||
return (0);
|
||||
}
|
||||
return (expr);
|
||||
}
|
||||
|
||||
/* dict_regexp_prescan - find largest $number in replacement text */
|
||||
|
||||
static int dict_regexp_prescan(int type, VSTRING *buf, char *context)
|
||||
{
|
||||
DICT_REGEXP_PRESCAN_CONTEXT *ctxt = (DICT_REGEXP_PRESCAN_CONTEXT *) context;
|
||||
size_t n;
|
||||
|
||||
if (type == MAC_PARSE_VARNAME) {
|
||||
if (!alldig(vstring_str(buf))) {
|
||||
msg_warn("regexp map %s, line %d: non-numeric replacement macro name \"%s\"",
|
||||
ctxt->map, ctxt->lineno, vstring_str(buf));
|
||||
return (MAC_PARSE_ERROR);
|
||||
}
|
||||
n = atoi(vstring_str(buf));
|
||||
if (n > ctxt->max_nsub)
|
||||
ctxt->max_nsub = n;
|
||||
}
|
||||
return (MAC_PARSE_OK);
|
||||
}
|
||||
|
||||
/* dict_regexp_parseline - parse one rule */
|
||||
|
||||
static DICT_REGEXP_RULE *dict_regexp_parseline(const char *map, int lineno,
|
||||
char *line)
|
||||
{
|
||||
DICT_REGEXP_RULE *rule;
|
||||
char *p,
|
||||
re_delim;
|
||||
regex_t *expr1,
|
||||
*expr2;
|
||||
int total_nsub;
|
||||
char *p;
|
||||
regex_t *primary_exp;
|
||||
regex_t *negated_exp;
|
||||
DICT_REGEXP_PATTERN primary_pat;
|
||||
DICT_REGEXP_PATTERN negated_pat;
|
||||
DICT_REGEXP_PRESCAN_CONTEXT ctxt;
|
||||
|
||||
p = line;
|
||||
re_delim = *p;
|
||||
|
||||
expr1 = dict_regexp_get_expr(lineno, &p, map_fp);
|
||||
if (!expr1) {
|
||||
return NULL;
|
||||
} else if (*p == '!' && p[1] == re_delim) {
|
||||
/*
|
||||
* Get the primary and optional negated patterns and their flags.
|
||||
*/
|
||||
if (dict_regexp_get_pattern(map, lineno, &p, &primary_pat) == 0)
|
||||
return (0);
|
||||
if (*p == '!' && p[1] && !ISSPACE(p[1])) {
|
||||
p++;
|
||||
expr2 = dict_regexp_get_expr(lineno, &p, map_fp);
|
||||
if (!expr2) {
|
||||
myfree((char *) expr1);
|
||||
return NULL;
|
||||
}
|
||||
total_nsub = expr1->re_nsub + expr2->re_nsub + 2;
|
||||
if (dict_regexp_get_pattern(map, lineno, &p, &negated_pat) == 0)
|
||||
return (0);
|
||||
} else {
|
||||
expr2 = NULL;
|
||||
total_nsub = expr1->re_nsub + 1;
|
||||
negated_pat.regexp = 0;
|
||||
}
|
||||
if (nsub)
|
||||
*nsub = total_nsub;
|
||||
|
||||
/*
|
||||
* Get the replacement text.
|
||||
*/
|
||||
if (!ISSPACE(*p)) {
|
||||
msg_warn("%s, line %d: Too many expressions.",
|
||||
VSTREAM_PATH(map_fp), lineno);
|
||||
myfree((char *) expr1);
|
||||
if (expr2)
|
||||
myfree((char *) expr2);
|
||||
return NULL;
|
||||
msg_warn("regexp map %s, line %d: invalid expression: "
|
||||
"skipping this rule", map, lineno);
|
||||
return (0);
|
||||
}
|
||||
rule = (DICT_REGEXP_RULE *) mymalloc(sizeof(DICT_REGEXP_RULE));
|
||||
|
||||
while (*p && ISSPACE(*p))
|
||||
++p;
|
||||
|
||||
if (!*p) {
|
||||
msg_warn("%s, line %d: no replacement text: using empty string",
|
||||
VSTREAM_PATH(map_fp), lineno);
|
||||
p = "";
|
||||
msg_warn("regexp map %s, line %d: using empty replacement string",
|
||||
map, lineno);
|
||||
}
|
||||
rule->expr[0] = expr1;
|
||||
rule->expr[1] = expr2;
|
||||
rule->replace = mystrdup(p);
|
||||
|
||||
/*
|
||||
* Find the highest-numbered $number substitution string. We can speed up
|
||||
* processing 1) by passing hints to the regexp compiler, setting the
|
||||
* REG_NOSUB flag when the replacement text contains no $number string;
|
||||
* 2) by passing hints to the regexp execution code, limiting the amount
|
||||
* of text that is made available for substitution.
|
||||
*/
|
||||
ctxt.map = map;
|
||||
ctxt.lineno = lineno;
|
||||
ctxt.max_nsub = 0;
|
||||
if (mac_parse(p, dict_regexp_prescan, (char *) &ctxt) & MAC_PARSE_ERROR) {
|
||||
msg_warn("regexp map %s, line %d: bad replacement syntax: "
|
||||
"skipping this rule", map, lineno);
|
||||
return (0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Compile the primary and the optional negated pattern. Speed up
|
||||
* execution when no matched text needs to be substituted into the result
|
||||
* string, or when the highest numbered substring is less than the total
|
||||
* number of () subpatterns.
|
||||
*/
|
||||
if (ctxt.max_nsub == 0)
|
||||
primary_pat.options |= REG_NOSUB;
|
||||
if ((primary_exp = dict_regexp_compile(map, lineno, &primary_pat)) == 0)
|
||||
return (0);
|
||||
if (ctxt.max_nsub > primary_exp->re_nsub) {
|
||||
msg_warn("regexp map %s, line %d: out of range replacement index \"%d\": "
|
||||
"skipping this rule", map, lineno, ctxt.max_nsub);
|
||||
regfree(primary_exp);
|
||||
myfree((char *) primary_exp);
|
||||
return (0);
|
||||
}
|
||||
if (negated_pat.regexp != 0) {
|
||||
negated_pat.options |= REG_NOSUB;
|
||||
if ((negated_exp = dict_regexp_compile(map, lineno, &negated_pat)) == 0) {
|
||||
regfree(primary_exp);
|
||||
myfree((char *) primary_exp);
|
||||
return (0);
|
||||
}
|
||||
} else
|
||||
negated_exp = 0;
|
||||
|
||||
/*
|
||||
* Package up the result.
|
||||
*/
|
||||
rule = (DICT_REGEXP_RULE *) mymalloc(sizeof(DICT_REGEXP_RULE));
|
||||
rule->primary_exp = primary_exp;
|
||||
rule->negated_exp = negated_exp;
|
||||
rule->replacement = mystrdup(p);
|
||||
rule->max_nsub = ctxt.max_nsub;
|
||||
rule->lineno = lineno;
|
||||
rule->next = NULL;
|
||||
return rule;
|
||||
rule->next = 0;
|
||||
return (rule);
|
||||
}
|
||||
|
||||
/*
|
||||
* dict_regexp_open - load and compile a file containing regular expressions
|
||||
*/
|
||||
/* dict_regexp_open - load and compile a file containing regular expressions */
|
||||
|
||||
DICT *dict_regexp_open(const char *map, int unused_flags, int dict_flags)
|
||||
{
|
||||
DICT_REGEXP *dict_regexp;
|
||||
VSTREAM *map_fp;
|
||||
VSTRING *line_buffer;
|
||||
DICT_REGEXP_RULE *rule,
|
||||
*last_rule = NULL;
|
||||
DICT_REGEXP_RULE *rule;
|
||||
DICT_REGEXP_RULE *last_rule = 0;
|
||||
int lineno = 0;
|
||||
int max_nsub = 0;
|
||||
int nsub;
|
||||
size_t max_nsub = 0;
|
||||
char *p;
|
||||
|
||||
line_buffer = vstring_alloc(100);
|
||||
@@ -353,20 +486,21 @@ DICT *dict_regexp_open(const char *map, int unused_flags, int dict_flags)
|
||||
dict_regexp->head = 0;
|
||||
dict_regexp->pmatch = 0;
|
||||
|
||||
if ((map_fp = vstream_fopen(map, O_RDONLY, 0)) == 0) {
|
||||
/*
|
||||
* Parse the regexp table.
|
||||
*/
|
||||
if ((map_fp = vstream_fopen(map, O_RDONLY, 0)) == 0)
|
||||
msg_fatal("open %s: %m", map);
|
||||
}
|
||||
|
||||
while (readlline(line_buffer, map_fp, &lineno)) {
|
||||
p = vstring_str(line_buffer);
|
||||
|
||||
trimblanks(p, 0)[0] = 0; /* Trim space at end */
|
||||
|
||||
rule = dict_regexp_parseline(lineno, p, &nsub, map_fp);
|
||||
trimblanks(p, 0)[0] = 0;
|
||||
rule = dict_regexp_parseline(map, lineno, p);
|
||||
if (rule) {
|
||||
if (nsub > max_nsub)
|
||||
max_nsub = nsub;
|
||||
if (rule->max_nsub > max_nsub)
|
||||
max_nsub = rule->max_nsub;
|
||||
|
||||
if (last_rule == NULL)
|
||||
if (last_rule == 0)
|
||||
dict_regexp->head = rule;
|
||||
else
|
||||
last_rule->next = rule;
|
||||
@@ -374,15 +508,21 @@ DICT *dict_regexp_open(const char *map, int unused_flags, int dict_flags)
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Allocate space for only as many matched substrings as used in the
|
||||
* replacement text.
|
||||
*/
|
||||
if (max_nsub > 0)
|
||||
dict_regexp->pmatch =
|
||||
(regmatch_t *) mymalloc(sizeof(regmatch_t) * max_nsub);
|
||||
dict_regexp->nmatch = max_nsub;
|
||||
(regmatch_t *) mymalloc(sizeof(regmatch_t) * (max_nsub + 1));
|
||||
|
||||
/*
|
||||
* Clean up.
|
||||
*/
|
||||
vstring_free(line_buffer);
|
||||
vstream_fclose(map_fp);
|
||||
|
||||
return (DICT_DEBUG(&dict_regexp->dict));
|
||||
return (DICT_DEBUG (&dict_regexp->dict));
|
||||
}
|
||||
|
||||
#endif
|
||||
|
@@ -33,6 +33,8 @@
|
||||
/* A parsing error was detected.
|
||||
/* .IP MAC_PARSE_UNDEF
|
||||
/* A macro was expanded but not defined.
|
||||
/* .PP
|
||||
/* Use the constant MAC_PARSE_OK when no error was detected.
|
||||
/* SEE ALSO
|
||||
/* dict(3) dictionary interface.
|
||||
/* DIAGNOSTICS
|
||||
|
@@ -22,6 +22,7 @@
|
||||
#define MAC_PARSE_LITERAL 1
|
||||
#define MAC_PARSE_VARNAME 2
|
||||
|
||||
#define MAC_PARSE_OK 0
|
||||
#define MAC_PARSE_ERROR (1<<0)
|
||||
#define MAC_PARSE_UNDEF (1<<1)
|
||||
#define MAC_PARSE_USER 2 /* start user definitions */
|
||||
|
Reference in New Issue
Block a user