mirror of
https://github.com/vdukhovni/postfix
synced 2025-09-01 06:35:27 +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_OPEN_INFO
|
||||||
-TDICT_PCRE
|
-TDICT_PCRE
|
||||||
-TDICT_REGEXP
|
-TDICT_REGEXP
|
||||||
|
-TDICT_REGEXP_EXPAND_CONTEXT
|
||||||
|
-TDICT_REGEXP_PATTERN
|
||||||
|
-TDICT_REGEXP_PRESCAN_CONTEXT
|
||||||
-TDICT_REGEXP_RULE
|
-TDICT_REGEXP_RULE
|
||||||
-TDICT_TCP
|
-TDICT_TCP
|
||||||
-TDICT_UNIX
|
-TDICT_UNIX
|
||||||
|
@@ -297,8 +297,10 @@ static void qmqpd_copy_recipients(QMQPD_STATE *state)
|
|||||||
/*
|
/*
|
||||||
* Append the optional recipient who is copied on all mail.
|
* Append the optional recipient who is copied on all mail.
|
||||||
*/
|
*/
|
||||||
if (*var_always_bcc)
|
if (state->err == CLEANUP_STAT_OK
|
||||||
rec_fputs(state->cleanup, REC_TYPE_RCPT, var_always_bcc);
|
&& *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 */
|
/* qmqpd_next_line - get line from buffer, return last char, newline, or -1 */
|
||||||
|
@@ -12,7 +12,8 @@
|
|||||||
/* int dict_flags;
|
/* int dict_flags;
|
||||||
/* DESCRIPTION
|
/* DESCRIPTION
|
||||||
/* dict_regexp_open() opens the named file and compiles the contained
|
/* 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
|
/* SEE ALSO
|
||||||
/* dict(3) generic dictionary manager
|
/* dict(3) generic dictionary manager
|
||||||
/* AUTHOR(S)
|
/* AUTHOR(S)
|
||||||
@@ -25,7 +26,7 @@
|
|||||||
/* Level 3, 213 Miller St
|
/* Level 3, 213 Miller St
|
||||||
/* North Sydney, NSW, Australia
|
/* North Sydney, NSW, Australia
|
||||||
/*
|
/*
|
||||||
/* Wietse Venema
|
/* Heavily rewritten by Wietse Venema
|
||||||
/* IBM T.J. Watson Research
|
/* IBM T.J. Watson Research
|
||||||
/* P.O. Box 704
|
/* P.O. Box 704
|
||||||
/* Yorktown Heights, NY 10598, USA
|
/* Yorktown Heights, NY 10598, USA
|
||||||
@@ -56,70 +57,115 @@
|
|||||||
#include "dict_regexp.h"
|
#include "dict_regexp.h"
|
||||||
#include "mac_parse.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 {
|
typedef struct dict_regexp_list {
|
||||||
struct dict_regexp_list *next; /* Next regexp in dict */
|
struct dict_regexp_list *next; /* next regexp in dict */
|
||||||
regex_t *expr[2]; /* The compiled pattern(s) */
|
regex_t *primary_exp; /* compiled primary pattern */
|
||||||
char *replace; /* Replacement string */
|
regex_t *negated_exp; /* compiled negated pattern */
|
||||||
int lineno; /* Source file line number */
|
char *replacement; /* replacement text */
|
||||||
|
size_t max_nsub; /* largest replacement $number */
|
||||||
|
int lineno; /* source file line number */
|
||||||
} DICT_REGEXP_RULE;
|
} DICT_REGEXP_RULE;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Regexp map.
|
||||||
|
*/
|
||||||
typedef struct {
|
typedef struct {
|
||||||
DICT dict; /* generic members */
|
DICT dict; /* generic members */
|
||||||
char *map; /* map name */
|
regmatch_t *pmatch; /* replacement substring storage */
|
||||||
int flags; /* unused at the moment */
|
|
||||||
regmatch_t *pmatch; /* Cut substrings */
|
|
||||||
int nmatch; /* number of elements in pmatch */
|
|
||||||
DICT_REGEXP_RULE *head; /* first rule */
|
DICT_REGEXP_RULE *head; /* first rule */
|
||||||
} DICT_REGEXP;
|
} DICT_REGEXP;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Context for macro expansion callback.
|
* Context for $number expansion callback.
|
||||||
*/
|
*/
|
||||||
struct dict_regexp_context {
|
typedef struct {
|
||||||
DICT_REGEXP *dict; /* the dictionary entry */
|
DICT_REGEXP *dict; /* the dictionary entry */
|
||||||
DICT_REGEXP_RULE *rule; /* the rule we matched */
|
DICT_REGEXP_RULE *rule; /* the rule we matched */
|
||||||
VSTRING *buf; /* target string buffer */
|
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
|
* Context for $number pre-scan callback.
|
||||||
* matched string.
|
*/
|
||||||
*/
|
typedef struct {
|
||||||
static int dict_regexp_action(int type, VSTRING *buf, char *ptr)
|
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_RULE *rule = ctxt->rule;
|
||||||
DICT_REGEXP *dict = ctxt->dict;
|
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) {
|
if (type == MAC_PARSE_VARNAME) {
|
||||||
n = atoi(vstring_str(buf));
|
n = atoi(vstring_str(buf));
|
||||||
if (n >= dict->nmatch)
|
if (n < 1 || n > rule->max_nsub)
|
||||||
msg_fatal("regexp %s, line %d: replacement index out of range",
|
msg_panic("regexp map %s, line %d: out of range replacement index \"%s\"",
|
||||||
dict->dict.name, rule->lineno);
|
dict->dict.name, rule->lineno, vstring_str(buf));
|
||||||
if (dict->pmatch[n].rm_so < 0 ||
|
if (dict->pmatch[n].rm_so < 0 ||
|
||||||
dict->pmatch[n].rm_so == dict->pmatch[n].rm_eo) {
|
dict->pmatch[n].rm_so == dict->pmatch[n].rm_eo) {
|
||||||
return (MAC_PARSE_UNDEF); /* empty string or not
|
return (MAC_PARSE_UNDEF); /* empty or not matched */
|
||||||
* matched */
|
|
||||||
}
|
}
|
||||||
vstring_strncat(ctxt->buf, ctxt->subject + dict->pmatch[n].rm_so,
|
vstring_strncat(ctxt->buf, ctxt->subject + dict->pmatch[n].rm_so,
|
||||||
dict->pmatch[n].rm_eo - 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));
|
vstring_strcat(ctxt->buf, vstring_str(buf));
|
||||||
return (0);
|
|
||||||
|
return (MAC_PARSE_OK);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/* dict_regexp_regerror - report regexp compile/execute error */
|
||||||
* Look up regexp dict and perform string substitution on matched
|
|
||||||
* strings.
|
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)
|
static const char *dict_regexp_lookup(DICT *dict, const char *name)
|
||||||
{
|
{
|
||||||
DICT_REGEXP *dict_regexp = (DICT_REGEXP *) dict;
|
DICT_REGEXP *dict_regexp = (DICT_REGEXP *) dict;
|
||||||
DICT_REGEXP_RULE *rule;
|
DICT_REGEXP_RULE *rule;
|
||||||
struct dict_regexp_context ctxt;
|
DICT_REGEXP_EXPAND_CONTEXT ctxt;
|
||||||
static VSTRING *buf;
|
static VSTRING *buf;
|
||||||
int error;
|
int error;
|
||||||
|
|
||||||
@@ -128,54 +174,72 @@ static const char *dict_regexp_lookup(DICT *dict, const char *name)
|
|||||||
if (msg_verbose)
|
if (msg_verbose)
|
||||||
msg_info("dict_regexp_lookup: %s: %s", dict_regexp->dict.name, name);
|
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) {
|
for (rule = dict_regexp->head; rule; rule = rule->next) {
|
||||||
error = regexec(rule->expr[0], name, rule->expr[0]->re_nsub + 1,
|
error = regexec(rule->primary_exp, name, rule->max_nsub + 1,
|
||||||
dict_regexp->pmatch, 0);
|
rule->max_nsub ? dict_regexp->pmatch :
|
||||||
if (!error) {
|
(regmatch_t *) 0, 0);
|
||||||
if (rule->expr[1]) {
|
switch (error) {
|
||||||
error = regexec(rule->expr[1], name, rule->expr[1]->re_nsub + 1,
|
case REG_NOMATCH:
|
||||||
dict_regexp->pmatch + rule->expr[0]->re_nsub + 1, 0);
|
continue;
|
||||||
if (!error) {
|
default:
|
||||||
continue;
|
dict_regexp_regerror(dict_regexp->dict.name, rule->lineno,
|
||||||
} else if (error != REG_NOMATCH) {
|
error, rule->primary_exp);
|
||||||
char errbuf[256];
|
continue;
|
||||||
|
case 0:
|
||||||
(void) regerror(error, rule->expr[1], errbuf, sizeof(errbuf));
|
break;
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 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 */
|
/* 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)
|
static void dict_regexp_close(DICT *dict)
|
||||||
{
|
{
|
||||||
DICT_REGEXP *dict_regexp = (DICT_REGEXP *) dict;
|
DICT_REGEXP *dict_regexp = (DICT_REGEXP *) dict;
|
||||||
DICT_REGEXP_RULE *rule,
|
DICT_REGEXP_RULE *rule;
|
||||||
*next;
|
DICT_REGEXP_RULE *next;
|
||||||
int i;
|
|
||||||
|
|
||||||
for (rule = dict_regexp->head; rule; rule = next) {
|
for (rule = dict_regexp->head; rule; rule = next) {
|
||||||
next = rule->next;
|
next = rule->next;
|
||||||
for (i = 0; i < 2; i++) {
|
if (rule->primary_exp) {
|
||||||
if (rule->expr[i]) {
|
regfree(rule->primary_exp);
|
||||||
regfree(rule->expr[i]);
|
myfree((char *) rule->primary_exp);
|
||||||
myfree((char *) rule->expr[i]);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
myfree((char *) rule->replace);
|
if (rule->negated_exp) {
|
||||||
|
regfree(rule->negated_exp);
|
||||||
|
myfree((char *) rule->negated_exp);
|
||||||
|
}
|
||||||
|
myfree((char *) rule->replacement);
|
||||||
myfree((char *) rule);
|
myfree((char *) rule);
|
||||||
}
|
}
|
||||||
if (dict_regexp->pmatch)
|
if (dict_regexp->pmatch)
|
||||||
@@ -203,19 +268,20 @@ static void dict_regexp_close(DICT *dict)
|
|||||||
dict_free(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,
|
char *p = *bufp;
|
||||||
*regexp,
|
char re_delim;
|
||||||
re_delim;
|
|
||||||
int re_options,
|
|
||||||
error;
|
|
||||||
regex_t *expr;
|
|
||||||
|
|
||||||
re_delim = *p++;
|
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) {
|
while (*p) {
|
||||||
if (*p == '\\') {
|
if (*p == '\\') {
|
||||||
if (p[1])
|
if (p[1])
|
||||||
@@ -228,119 +294,186 @@ static regex_t *dict_regexp_get_expr(int lineno, char **bufp, VSTREAM *map_fp)
|
|||||||
++p;
|
++p;
|
||||||
}
|
}
|
||||||
if (!*p) {
|
if (!*p) {
|
||||||
msg_warn("%s, line %d: no closing regexp delimiter: %c",
|
msg_warn("regexp map %s, line %d: no closing regexp delimiter \"%c\": "
|
||||||
VSTREAM_PATH(map_fp), lineno, re_delim);
|
"skipping this rule", map, lineno, re_delim);
|
||||||
return NULL;
|
return (0);
|
||||||
}
|
}
|
||||||
*p++ = '\0'; /* Null term the regexp */
|
*p++ = 0; /* null terminate */
|
||||||
|
|
||||||
re_options = REG_EXTENDED | REG_ICASE;
|
/*
|
||||||
while (*p) {
|
* Search for options.
|
||||||
if (!*p || ISSPACE(*p) || (*p == '!' && p[1] == re_delim)) {
|
*/
|
||||||
/* end of the regexp */
|
pat->options = REG_EXTENDED | REG_ICASE;
|
||||||
expr = (regex_t *) mymalloc(sizeof(*expr));
|
while (*p && !ISSPACE(*p) && *p != '!') {
|
||||||
error = regcomp(expr, regexp, re_options);
|
switch (*p) {
|
||||||
if (error != 0) {
|
case 'i':
|
||||||
char errbuf[256];
|
pat->options ^= REG_ICASE;
|
||||||
|
break;
|
||||||
(void) regerror(error, expr, errbuf, sizeof(errbuf));
|
case 'm':
|
||||||
msg_warn("%s, line %d: error in regexp: %s.",
|
pat->options ^= REG_NEWLINE;
|
||||||
VSTREAM_PATH(map_fp), lineno, errbuf);
|
break;
|
||||||
myfree((char *) expr);
|
case 'x':
|
||||||
return NULL;
|
pat->options ^= REG_EXTENDED;
|
||||||
}
|
break;
|
||||||
*bufp = p;
|
default:
|
||||||
return expr;
|
msg_warn("regexp map %s, line %d: unknown regexp option \"%c\": "
|
||||||
} else {
|
"skipping this rule", map, lineno, *p);
|
||||||
switch (*p) {
|
return (0);
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
++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;
|
DICT_REGEXP_RULE *rule;
|
||||||
char *p,
|
char *p;
|
||||||
re_delim;
|
regex_t *primary_exp;
|
||||||
regex_t *expr1,
|
regex_t *negated_exp;
|
||||||
*expr2;
|
DICT_REGEXP_PATTERN primary_pat;
|
||||||
int total_nsub;
|
DICT_REGEXP_PATTERN negated_pat;
|
||||||
|
DICT_REGEXP_PRESCAN_CONTEXT ctxt;
|
||||||
|
|
||||||
p = line;
|
p = line;
|
||||||
re_delim = *p;
|
|
||||||
|
|
||||||
expr1 = dict_regexp_get_expr(lineno, &p, map_fp);
|
/*
|
||||||
if (!expr1) {
|
* Get the primary and optional negated patterns and their flags.
|
||||||
return NULL;
|
*/
|
||||||
} else if (*p == '!' && p[1] == re_delim) {
|
if (dict_regexp_get_pattern(map, lineno, &p, &primary_pat) == 0)
|
||||||
|
return (0);
|
||||||
|
if (*p == '!' && p[1] && !ISSPACE(p[1])) {
|
||||||
p++;
|
p++;
|
||||||
expr2 = dict_regexp_get_expr(lineno, &p, map_fp);
|
if (dict_regexp_get_pattern(map, lineno, &p, &negated_pat) == 0)
|
||||||
if (!expr2) {
|
return (0);
|
||||||
myfree((char *) expr1);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
total_nsub = expr1->re_nsub + expr2->re_nsub + 2;
|
|
||||||
} else {
|
} else {
|
||||||
expr2 = NULL;
|
negated_pat.regexp = 0;
|
||||||
total_nsub = expr1->re_nsub + 1;
|
|
||||||
}
|
}
|
||||||
if (nsub)
|
|
||||||
*nsub = total_nsub;
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Get the replacement text.
|
||||||
|
*/
|
||||||
if (!ISSPACE(*p)) {
|
if (!ISSPACE(*p)) {
|
||||||
msg_warn("%s, line %d: Too many expressions.",
|
msg_warn("regexp map %s, line %d: invalid expression: "
|
||||||
VSTREAM_PATH(map_fp), lineno);
|
"skipping this rule", map, lineno);
|
||||||
myfree((char *) expr1);
|
return (0);
|
||||||
if (expr2)
|
|
||||||
myfree((char *) expr2);
|
|
||||||
return NULL;
|
|
||||||
}
|
}
|
||||||
rule = (DICT_REGEXP_RULE *) mymalloc(sizeof(DICT_REGEXP_RULE));
|
|
||||||
|
|
||||||
while (*p && ISSPACE(*p))
|
while (*p && ISSPACE(*p))
|
||||||
++p;
|
++p;
|
||||||
|
|
||||||
if (!*p) {
|
if (!*p) {
|
||||||
msg_warn("%s, line %d: no replacement text: using empty string",
|
msg_warn("regexp map %s, line %d: using empty replacement string",
|
||||||
VSTREAM_PATH(map_fp), lineno);
|
map, lineno);
|
||||||
p = "";
|
|
||||||
}
|
}
|
||||||
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->lineno = lineno;
|
||||||
rule->next = NULL;
|
rule->next = 0;
|
||||||
return rule;
|
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 *dict_regexp_open(const char *map, int unused_flags, int dict_flags)
|
||||||
{
|
{
|
||||||
DICT_REGEXP *dict_regexp;
|
DICT_REGEXP *dict_regexp;
|
||||||
VSTREAM *map_fp;
|
VSTREAM *map_fp;
|
||||||
VSTRING *line_buffer;
|
VSTRING *line_buffer;
|
||||||
DICT_REGEXP_RULE *rule,
|
DICT_REGEXP_RULE *rule;
|
||||||
*last_rule = NULL;
|
DICT_REGEXP_RULE *last_rule = 0;
|
||||||
int lineno = 0;
|
int lineno = 0;
|
||||||
int max_nsub = 0;
|
size_t max_nsub = 0;
|
||||||
int nsub;
|
|
||||||
char *p;
|
char *p;
|
||||||
|
|
||||||
line_buffer = vstring_alloc(100);
|
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->head = 0;
|
||||||
dict_regexp->pmatch = 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);
|
msg_fatal("open %s: %m", map);
|
||||||
}
|
|
||||||
while (readlline(line_buffer, map_fp, &lineno)) {
|
while (readlline(line_buffer, map_fp, &lineno)) {
|
||||||
p = vstring_str(line_buffer);
|
p = vstring_str(line_buffer);
|
||||||
|
trimblanks(p, 0)[0] = 0;
|
||||||
trimblanks(p, 0)[0] = 0; /* Trim space at end */
|
rule = dict_regexp_parseline(map, lineno, p);
|
||||||
|
|
||||||
rule = dict_regexp_parseline(lineno, p, &nsub, map_fp);
|
|
||||||
if (rule) {
|
if (rule) {
|
||||||
if (nsub > max_nsub)
|
if (rule->max_nsub > max_nsub)
|
||||||
max_nsub = nsub;
|
max_nsub = rule->max_nsub;
|
||||||
|
|
||||||
if (last_rule == NULL)
|
if (last_rule == 0)
|
||||||
dict_regexp->head = rule;
|
dict_regexp->head = rule;
|
||||||
else
|
else
|
||||||
last_rule->next = rule;
|
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)
|
if (max_nsub > 0)
|
||||||
dict_regexp->pmatch =
|
dict_regexp->pmatch =
|
||||||
(regmatch_t *) mymalloc(sizeof(regmatch_t) * max_nsub);
|
(regmatch_t *) mymalloc(sizeof(regmatch_t) * (max_nsub + 1));
|
||||||
dict_regexp->nmatch = max_nsub;
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Clean up.
|
||||||
|
*/
|
||||||
vstring_free(line_buffer);
|
vstring_free(line_buffer);
|
||||||
vstream_fclose(map_fp);
|
vstream_fclose(map_fp);
|
||||||
|
|
||||||
return (DICT_DEBUG(&dict_regexp->dict));
|
return (DICT_DEBUG (&dict_regexp->dict));
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@@ -33,6 +33,8 @@
|
|||||||
/* A parsing error was detected.
|
/* A parsing error was detected.
|
||||||
/* .IP MAC_PARSE_UNDEF
|
/* .IP MAC_PARSE_UNDEF
|
||||||
/* A macro was expanded but not defined.
|
/* A macro was expanded but not defined.
|
||||||
|
/* .PP
|
||||||
|
/* Use the constant MAC_PARSE_OK when no error was detected.
|
||||||
/* SEE ALSO
|
/* SEE ALSO
|
||||||
/* dict(3) dictionary interface.
|
/* dict(3) dictionary interface.
|
||||||
/* DIAGNOSTICS
|
/* DIAGNOSTICS
|
||||||
|
@@ -22,6 +22,7 @@
|
|||||||
#define MAC_PARSE_LITERAL 1
|
#define MAC_PARSE_LITERAL 1
|
||||||
#define MAC_PARSE_VARNAME 2
|
#define MAC_PARSE_VARNAME 2
|
||||||
|
|
||||||
|
#define MAC_PARSE_OK 0
|
||||||
#define MAC_PARSE_ERROR (1<<0)
|
#define MAC_PARSE_ERROR (1<<0)
|
||||||
#define MAC_PARSE_UNDEF (1<<1)
|
#define MAC_PARSE_UNDEF (1<<1)
|
||||||
#define MAC_PARSE_USER 2 /* start user definitions */
|
#define MAC_PARSE_USER 2 /* start user definitions */
|
||||||
|
Reference in New Issue
Block a user