2012-12-22 15:35:39 +00:00
|
|
|
/****************************************************************
|
|
|
|
Copyright (C) Lucent Technologies 1997
|
|
|
|
All Rights Reserved
|
|
|
|
|
|
|
|
Permission to use, copy, modify, and distribute this software and
|
|
|
|
its documentation for any purpose and without fee is hereby
|
|
|
|
granted, provided that the above copyright notice appear in all
|
|
|
|
copies and that both that the copyright notice and this
|
|
|
|
permission notice and warranty disclaimer appear in supporting
|
|
|
|
documentation, and that the name Lucent Technologies or any of
|
|
|
|
its entities not be used in advertising or publicity pertaining
|
|
|
|
to distribution of the software without specific, written prior
|
|
|
|
permission.
|
|
|
|
|
|
|
|
LUCENT DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
|
|
|
|
INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS.
|
|
|
|
IN NO EVENT SHALL LUCENT OR ANY OF ITS ENTITIES BE LIABLE FOR ANY
|
|
|
|
SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
|
|
|
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER
|
|
|
|
IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
|
|
|
|
ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
|
|
|
|
THIS SOFTWARE.
|
|
|
|
****************************************************************/
|
|
|
|
|
|
|
|
/* lasciate ogne speranza, voi ch'intrate. */
|
|
|
|
|
|
|
|
#define DEBUG
|
|
|
|
|
|
|
|
#include <ctype.h>
|
2018-08-29 16:06:33 +00:00
|
|
|
#include <limits.h>
|
2012-12-22 15:35:39 +00:00
|
|
|
#include <stdio.h>
|
|
|
|
#include <string.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include "awk.h"
|
|
|
|
#include "ytab.h"
|
|
|
|
|
|
|
|
#define MAXLIN 22
|
|
|
|
|
|
|
|
#define type(v) (v)->nobj /* badly overloaded here */
|
|
|
|
#define info(v) (v)->ntype /* badly overloaded here */
|
|
|
|
#define left(v) (v)->narg[0]
|
|
|
|
#define right(v) (v)->narg[1]
|
|
|
|
#define parent(v) (v)->nnext
|
|
|
|
|
|
|
|
#define LEAF case CCL: case NCCL: case CHAR: case DOT: case FINAL: case ALL:
|
|
|
|
#define ELEAF case EMPTYRE: /* empty string in regexp */
|
|
|
|
#define UNARY case STAR: case PLUS: case QUEST:
|
|
|
|
|
|
|
|
/* encoding in tree Nodes:
|
|
|
|
leaf (CCL, NCCL, CHAR, DOT, FINAL, ALL, EMPTYRE):
|
|
|
|
left is index, right contains value or pointer to value
|
|
|
|
unary (STAR, PLUS, QUEST): left is child, right is null
|
|
|
|
binary (CAT, OR): left and right are children
|
|
|
|
parent contains pointer to parent
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
|
int *setvec;
|
|
|
|
int *tmpset;
|
|
|
|
int maxsetvec = 0;
|
|
|
|
|
|
|
|
int rtok; /* next token in current re */
|
|
|
|
int rlxval;
|
2019-10-24 13:40:15 +00:00
|
|
|
static const uschar *rlxstr;
|
|
|
|
static const uschar *prestr; /* current position in current re */
|
|
|
|
static const uschar *lastre; /* origin of last re */
|
|
|
|
static const uschar *lastatom; /* origin of last Atom */
|
|
|
|
static const uschar *starttok;
|
|
|
|
static const uschar *basestr; /* starts with original, replaced during
|
2019-01-23 09:12:27 +00:00
|
|
|
repetition processing */
|
2019-10-24 13:40:15 +00:00
|
|
|
static const uschar *firstbasestr;
|
2012-12-22 15:35:39 +00:00
|
|
|
|
|
|
|
static int setcnt;
|
|
|
|
static int poscnt;
|
|
|
|
|
2019-10-24 13:40:15 +00:00
|
|
|
const char *patbeg;
|
2012-12-22 15:35:39 +00:00
|
|
|
int patlen;
|
|
|
|
|
2019-10-17 17:04:46 +00:00
|
|
|
#define NFA 128 /* cache this many dynamic fa's */
|
2012-12-22 15:35:39 +00:00
|
|
|
fa *fatab[NFA];
|
|
|
|
int nfatab = 0; /* entries in fatab */
|
|
|
|
|
2019-10-17 17:04:46 +00:00
|
|
|
static int *
|
|
|
|
intalloc(size_t n, const char *f)
|
|
|
|
{
|
|
|
|
void *p = calloc(n, sizeof(int));
|
|
|
|
if (p == NULL)
|
|
|
|
overflo(f);
|
|
|
|
return p;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
resizesetvec(const char *f)
|
|
|
|
{
|
|
|
|
if (maxsetvec == 0)
|
|
|
|
maxsetvec = MAXLIN;
|
|
|
|
else
|
|
|
|
maxsetvec *= 4;
|
|
|
|
setvec = realloc(setvec, maxsetvec * sizeof(*setvec));
|
|
|
|
tmpset = realloc(tmpset, maxsetvec * sizeof(*tmpset));
|
|
|
|
if (setvec == NULL || tmpset == NULL)
|
|
|
|
overflo(f);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
resize_state(fa *f, int state)
|
|
|
|
{
|
|
|
|
void *p;
|
|
|
|
int i, new_count;
|
|
|
|
|
|
|
|
if (++state < f->state_count)
|
|
|
|
return;
|
|
|
|
|
|
|
|
new_count = state + 10; /* needs to be tuned */
|
|
|
|
|
|
|
|
p = realloc(f->gototab, new_count * sizeof(f->gototab[0]));
|
|
|
|
if (p == NULL)
|
|
|
|
goto out;
|
|
|
|
f->gototab = p;
|
|
|
|
|
|
|
|
p = realloc(f->out, new_count * sizeof(f->out[0]));
|
|
|
|
if (p == NULL)
|
|
|
|
goto out;
|
|
|
|
f->out = p;
|
|
|
|
|
|
|
|
p = realloc(f->posns, new_count * sizeof(f->posns[0]));
|
|
|
|
if (p == NULL)
|
|
|
|
goto out;
|
|
|
|
f->posns = p;
|
|
|
|
|
|
|
|
for (i = f->state_count; i < new_count; ++i) {
|
|
|
|
f->gototab[i] = calloc(NCHARS, sizeof(**f->gototab));
|
|
|
|
if (f->gototab[i] == NULL)
|
|
|
|
goto out;
|
|
|
|
f->out[i] = 0;
|
|
|
|
f->posns[i] = NULL;
|
|
|
|
}
|
|
|
|
f->state_count = new_count;
|
|
|
|
return;
|
|
|
|
out:
|
|
|
|
overflo(__func__);
|
|
|
|
}
|
|
|
|
|
2019-11-10 19:19:18 +00:00
|
|
|
fa *makedfa(const char *s, bool anchor) /* returns dfa for reg expr s */
|
2012-12-22 15:35:39 +00:00
|
|
|
{
|
|
|
|
int i, use, nuse;
|
|
|
|
fa *pfa;
|
|
|
|
static int now = 1;
|
|
|
|
|
2016-06-03 21:23:11 +00:00
|
|
|
if (setvec == NULL) { /* first time through any RE */
|
2019-10-17 17:04:46 +00:00
|
|
|
resizesetvec(__func__);
|
2012-12-22 15:35:39 +00:00
|
|
|
}
|
|
|
|
|
2019-11-10 19:19:18 +00:00
|
|
|
if (compile_time != RUNNING) /* a constant for sure */
|
2012-12-22 15:35:39 +00:00
|
|
|
return mkdfa(s, anchor);
|
|
|
|
for (i = 0; i < nfatab; i++) /* is it there already? */
|
|
|
|
if (fatab[i]->anchor == anchor
|
|
|
|
&& strcmp((const char *) fatab[i]->restr, s) == 0) {
|
|
|
|
fatab[i]->use = now++;
|
|
|
|
return fatab[i];
|
|
|
|
}
|
|
|
|
pfa = mkdfa(s, anchor);
|
|
|
|
if (nfatab < NFA) { /* room for another */
|
|
|
|
fatab[nfatab] = pfa;
|
|
|
|
fatab[nfatab]->use = now++;
|
|
|
|
nfatab++;
|
|
|
|
return pfa;
|
|
|
|
}
|
|
|
|
use = fatab[0]->use; /* replace least-recently used */
|
|
|
|
nuse = 0;
|
|
|
|
for (i = 1; i < nfatab; i++)
|
|
|
|
if (fatab[i]->use < use) {
|
|
|
|
use = fatab[i]->use;
|
|
|
|
nuse = i;
|
|
|
|
}
|
|
|
|
freefa(fatab[nuse]);
|
|
|
|
fatab[nuse] = pfa;
|
|
|
|
pfa->use = now++;
|
|
|
|
return pfa;
|
|
|
|
}
|
|
|
|
|
2019-11-10 19:19:18 +00:00
|
|
|
fa *mkdfa(const char *s, bool anchor) /* does the real work of making a dfa */
|
2020-01-01 20:42:50 +00:00
|
|
|
/* anchor = true for anchored matches, else false */
|
2012-12-22 15:35:39 +00:00
|
|
|
{
|
|
|
|
Node *p, *p1;
|
|
|
|
fa *f;
|
|
|
|
|
2019-10-24 13:40:15 +00:00
|
|
|
firstbasestr = (const uschar *) s;
|
2019-01-23 09:12:27 +00:00
|
|
|
basestr = firstbasestr;
|
2012-12-22 15:35:39 +00:00
|
|
|
p = reparse(s);
|
|
|
|
p1 = op2(CAT, op2(STAR, op2(ALL, NIL, NIL), NIL), p);
|
|
|
|
/* put ALL STAR in front of reg. exp. */
|
|
|
|
p1 = op2(CAT, p1, op2(FINAL, NIL, NIL));
|
|
|
|
/* put FINAL after reg. exp. */
|
|
|
|
|
|
|
|
poscnt = 0;
|
|
|
|
penter(p1); /* enter parent pointers and leaf indices */
|
2019-10-24 13:40:15 +00:00
|
|
|
if ((f = calloc(1, sizeof(fa) + poscnt * sizeof(rrow))) == NULL)
|
2019-10-17 17:04:46 +00:00
|
|
|
overflo(__func__);
|
2012-12-22 15:35:39 +00:00
|
|
|
f->accept = poscnt-1; /* penter has computed number of positions in re */
|
|
|
|
cfoll(f, p1); /* set up follow sets */
|
|
|
|
freetr(p1);
|
2019-10-17 17:04:46 +00:00
|
|
|
resize_state(f, 1);
|
|
|
|
f->posns[0] = intalloc(*(f->re[0].lfollow), __func__);
|
|
|
|
f->posns[1] = intalloc(1, __func__);
|
2012-12-22 15:35:39 +00:00
|
|
|
*f->posns[1] = 0;
|
|
|
|
f->initstat = makeinit(f, anchor);
|
|
|
|
f->anchor = anchor;
|
|
|
|
f->restr = (uschar *) tostring(s);
|
2019-01-23 09:12:27 +00:00
|
|
|
if (firstbasestr != basestr) {
|
|
|
|
if (basestr)
|
|
|
|
xfree(basestr);
|
|
|
|
}
|
2012-12-22 15:35:39 +00:00
|
|
|
return f;
|
|
|
|
}
|
|
|
|
|
2019-11-10 19:19:18 +00:00
|
|
|
int makeinit(fa *f, bool anchor)
|
2012-12-22 15:35:39 +00:00
|
|
|
{
|
|
|
|
int i, k;
|
|
|
|
|
|
|
|
f->curstat = 2;
|
|
|
|
f->out[2] = 0;
|
|
|
|
k = *(f->re[0].lfollow);
|
2019-07-28 11:51:52 +00:00
|
|
|
xfree(f->posns[2]);
|
2019-10-17 17:04:46 +00:00
|
|
|
f->posns[2] = intalloc(k + 1, __func__);
|
2020-01-01 20:42:50 +00:00
|
|
|
for (i = 0; i <= k; i++) {
|
2012-12-22 15:35:39 +00:00
|
|
|
(f->posns[2])[i] = (f->re[0].lfollow)[i];
|
|
|
|
}
|
|
|
|
if ((f->posns[2])[1] == f->accept)
|
|
|
|
f->out[2] = 1;
|
2020-01-01 20:42:50 +00:00
|
|
|
for (i = 0; i < NCHARS; i++)
|
2012-12-22 15:35:39 +00:00
|
|
|
f->gototab[2][i] = 0;
|
|
|
|
f->curstat = cgoto(f, 2, HAT);
|
|
|
|
if (anchor) {
|
|
|
|
*f->posns[2] = k-1; /* leave out position 0 */
|
2020-01-01 20:42:50 +00:00
|
|
|
for (i = 0; i < k; i++) {
|
2012-12-22 15:35:39 +00:00
|
|
|
(f->posns[0])[i] = (f->posns[2])[i];
|
|
|
|
}
|
|
|
|
|
|
|
|
f->out[0] = f->out[2];
|
|
|
|
if (f->curstat != 2)
|
|
|
|
--(*f->posns[f->curstat]);
|
|
|
|
}
|
|
|
|
return f->curstat;
|
|
|
|
}
|
|
|
|
|
|
|
|
void penter(Node *p) /* set up parent pointers and leaf indices */
|
|
|
|
{
|
|
|
|
switch (type(p)) {
|
|
|
|
ELEAF
|
|
|
|
LEAF
|
|
|
|
info(p) = poscnt;
|
|
|
|
poscnt++;
|
|
|
|
break;
|
|
|
|
UNARY
|
|
|
|
penter(left(p));
|
|
|
|
parent(left(p)) = p;
|
|
|
|
break;
|
|
|
|
case CAT:
|
|
|
|
case OR:
|
|
|
|
penter(left(p));
|
|
|
|
penter(right(p));
|
|
|
|
parent(left(p)) = p;
|
|
|
|
parent(right(p)) = p;
|
|
|
|
break;
|
2020-01-01 20:47:29 +00:00
|
|
|
case ZERO:
|
|
|
|
break;
|
2012-12-22 15:35:39 +00:00
|
|
|
default: /* can't happen */
|
|
|
|
FATAL("can't happen: unknown type %d in penter", type(p));
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void freetr(Node *p) /* free parse tree */
|
|
|
|
{
|
|
|
|
switch (type(p)) {
|
|
|
|
ELEAF
|
|
|
|
LEAF
|
|
|
|
xfree(p);
|
|
|
|
break;
|
|
|
|
UNARY
|
2020-01-01 20:47:29 +00:00
|
|
|
case ZERO:
|
2012-12-22 15:35:39 +00:00
|
|
|
freetr(left(p));
|
|
|
|
xfree(p);
|
|
|
|
break;
|
|
|
|
case CAT:
|
|
|
|
case OR:
|
|
|
|
freetr(left(p));
|
|
|
|
freetr(right(p));
|
|
|
|
xfree(p);
|
|
|
|
break;
|
|
|
|
default: /* can't happen */
|
|
|
|
FATAL("can't happen: unknown type %d in freetr", type(p));
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* in the parsing of regular expressions, metacharacters like . have */
|
|
|
|
/* to be seen literally; \056 is not a metacharacter. */
|
|
|
|
|
2019-10-24 13:40:15 +00:00
|
|
|
int hexstr(const uschar **pp) /* find and eval hex string at pp, return new p */
|
2012-12-22 15:35:39 +00:00
|
|
|
{ /* only pick up one 8-bit byte (2 chars) */
|
2019-10-24 13:40:15 +00:00
|
|
|
const uschar *p;
|
2012-12-22 15:35:39 +00:00
|
|
|
int n = 0;
|
|
|
|
int i;
|
|
|
|
|
2019-10-24 13:40:15 +00:00
|
|
|
for (i = 0, p = *pp; i < 2 && isxdigit(*p); i++, p++) {
|
2012-12-22 15:35:39 +00:00
|
|
|
if (isdigit(*p))
|
|
|
|
n = 16 * n + *p - '0';
|
|
|
|
else if (*p >= 'a' && *p <= 'f')
|
|
|
|
n = 16 * n + *p - 'a' + 10;
|
|
|
|
else if (*p >= 'A' && *p <= 'F')
|
|
|
|
n = 16 * n + *p - 'A' + 10;
|
|
|
|
}
|
2019-10-24 13:40:15 +00:00
|
|
|
*pp = p;
|
2012-12-22 15:35:39 +00:00
|
|
|
return n;
|
|
|
|
}
|
|
|
|
|
|
|
|
#define isoctdigit(c) ((c) >= '0' && (c) <= '7') /* multiple use of arg */
|
|
|
|
|
2019-10-24 13:40:15 +00:00
|
|
|
int quoted(const uschar **pp) /* pick up next thing after a \\ */
|
2012-12-22 15:35:39 +00:00
|
|
|
/* and increment *pp */
|
|
|
|
{
|
2019-10-24 13:40:15 +00:00
|
|
|
const uschar *p = *pp;
|
2012-12-22 15:35:39 +00:00
|
|
|
int c;
|
|
|
|
|
|
|
|
if ((c = *p++) == 't')
|
|
|
|
c = '\t';
|
|
|
|
else if (c == 'n')
|
|
|
|
c = '\n';
|
|
|
|
else if (c == 'f')
|
|
|
|
c = '\f';
|
|
|
|
else if (c == 'r')
|
|
|
|
c = '\r';
|
|
|
|
else if (c == 'b')
|
|
|
|
c = '\b';
|
2019-07-26 08:46:58 +00:00
|
|
|
else if (c == 'v')
|
|
|
|
c = '\v';
|
|
|
|
else if (c == 'a')
|
|
|
|
c = '\a';
|
2012-12-22 15:35:39 +00:00
|
|
|
else if (c == '\\')
|
|
|
|
c = '\\';
|
|
|
|
else if (c == 'x') { /* hexadecimal goo follows */
|
|
|
|
c = hexstr(&p); /* this adds a null if number is invalid */
|
|
|
|
} else if (isoctdigit(c)) { /* \d \dd \ddd */
|
|
|
|
int n = c - '0';
|
|
|
|
if (isoctdigit(*p)) {
|
|
|
|
n = 8 * n + *p++ - '0';
|
|
|
|
if (isoctdigit(*p))
|
|
|
|
n = 8 * n + *p++ - '0';
|
|
|
|
}
|
|
|
|
c = n;
|
|
|
|
} /* else */
|
|
|
|
/* c = c; */
|
|
|
|
*pp = p;
|
|
|
|
return c;
|
|
|
|
}
|
|
|
|
|
|
|
|
char *cclenter(const char *argp) /* add a character class */
|
|
|
|
{
|
|
|
|
int i, c, c2;
|
2019-10-24 13:40:15 +00:00
|
|
|
const uschar *op, *p = (const uschar *) argp;
|
|
|
|
uschar *bp;
|
2016-06-03 21:23:11 +00:00
|
|
|
static uschar *buf = NULL;
|
2012-12-22 15:35:39 +00:00
|
|
|
static int bufsz = 100;
|
|
|
|
|
|
|
|
op = p;
|
2019-10-17 17:04:46 +00:00
|
|
|
if (buf == NULL && (buf = malloc(bufsz)) == NULL)
|
2012-12-22 15:35:39 +00:00
|
|
|
FATAL("out of space for character class [%.10s...] 1", p);
|
|
|
|
bp = buf;
|
|
|
|
for (i = 0; (c = *p++) != 0; ) {
|
|
|
|
if (c == '\\') {
|
|
|
|
c = quoted(&p);
|
|
|
|
} else if (c == '-' && i > 0 && bp[-1] != 0) {
|
|
|
|
if (*p != 0) {
|
|
|
|
c = bp[-1];
|
|
|
|
c2 = *p++;
|
|
|
|
if (c2 == '\\')
|
|
|
|
c2 = quoted(&p);
|
|
|
|
if (c > c2) { /* empty; ignore */
|
|
|
|
bp--;
|
|
|
|
i--;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
while (c < c2) {
|
|
|
|
if (!adjbuf((char **) &buf, &bufsz, bp-buf+2, 100, (char **) &bp, "cclenter1"))
|
|
|
|
FATAL("out of space for character class [%.10s...] 2", p);
|
|
|
|
*bp++ = ++c;
|
|
|
|
i++;
|
|
|
|
}
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (!adjbuf((char **) &buf, &bufsz, bp-buf+2, 100, (char **) &bp, "cclenter2"))
|
|
|
|
FATAL("out of space for character class [%.10s...] 3", p);
|
|
|
|
*bp++ = c;
|
|
|
|
i++;
|
|
|
|
}
|
|
|
|
*bp = 0;
|
2020-06-25 18:32:34 +00:00
|
|
|
DPRINTF("cclenter: in = |%s|, out = |%s|\n", op, buf);
|
2012-12-22 15:35:39 +00:00
|
|
|
xfree(op);
|
|
|
|
return (char *) tostring((char *) buf);
|
|
|
|
}
|
|
|
|
|
|
|
|
void overflo(const char *s)
|
|
|
|
{
|
2019-10-17 17:04:46 +00:00
|
|
|
FATAL("regular expression too big: out of space in %.30s...", s);
|
2012-12-22 15:35:39 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void cfoll(fa *f, Node *v) /* enter follow set of each leaf of vertex v into lfollow[leaf] */
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
int *p;
|
|
|
|
|
|
|
|
switch (type(v)) {
|
|
|
|
ELEAF
|
|
|
|
LEAF
|
|
|
|
f->re[info(v)].ltype = type(v);
|
|
|
|
f->re[info(v)].lval.np = right(v);
|
|
|
|
while (f->accept >= maxsetvec) { /* guessing here! */
|
2019-10-17 17:04:46 +00:00
|
|
|
resizesetvec(__func__);
|
2012-12-22 15:35:39 +00:00
|
|
|
}
|
|
|
|
for (i = 0; i <= f->accept; i++)
|
|
|
|
setvec[i] = 0;
|
|
|
|
setcnt = 0;
|
|
|
|
follow(v); /* computes setvec and setcnt */
|
2019-10-17 17:04:46 +00:00
|
|
|
p = intalloc(setcnt + 1, __func__);
|
2012-12-22 15:35:39 +00:00
|
|
|
f->re[info(v)].lfollow = p;
|
|
|
|
*p = setcnt;
|
|
|
|
for (i = f->accept; i >= 0; i--)
|
|
|
|
if (setvec[i] == 1)
|
|
|
|
*++p = i;
|
|
|
|
break;
|
|
|
|
UNARY
|
|
|
|
cfoll(f,left(v));
|
|
|
|
break;
|
|
|
|
case CAT:
|
|
|
|
case OR:
|
|
|
|
cfoll(f,left(v));
|
|
|
|
cfoll(f,right(v));
|
|
|
|
break;
|
2020-01-01 20:47:29 +00:00
|
|
|
case ZERO:
|
|
|
|
break;
|
2012-12-22 15:35:39 +00:00
|
|
|
default: /* can't happen */
|
|
|
|
FATAL("can't happen: unknown type %d in cfoll", type(v));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
int first(Node *p) /* collects initially active leaves of p into setvec */
|
|
|
|
/* returns 0 if p matches empty string */
|
|
|
|
{
|
|
|
|
int b, lp;
|
|
|
|
|
|
|
|
switch (type(p)) {
|
|
|
|
ELEAF
|
|
|
|
LEAF
|
|
|
|
lp = info(p); /* look for high-water mark of subscripts */
|
|
|
|
while (setcnt >= maxsetvec || lp >= maxsetvec) { /* guessing here! */
|
2019-10-17 17:04:46 +00:00
|
|
|
resizesetvec(__func__);
|
2012-12-22 15:35:39 +00:00
|
|
|
}
|
|
|
|
if (type(p) == EMPTYRE) {
|
|
|
|
setvec[lp] = 0;
|
|
|
|
return(0);
|
|
|
|
}
|
|
|
|
if (setvec[lp] != 1) {
|
|
|
|
setvec[lp] = 1;
|
|
|
|
setcnt++;
|
|
|
|
}
|
|
|
|
if (type(p) == CCL && (*(char *) right(p)) == '\0')
|
|
|
|
return(0); /* empty CCL */
|
2020-01-01 20:42:50 +00:00
|
|
|
return(1);
|
2012-12-22 15:35:39 +00:00
|
|
|
case PLUS:
|
2020-01-01 20:42:50 +00:00
|
|
|
if (first(left(p)) == 0)
|
|
|
|
return(0);
|
2012-12-22 15:35:39 +00:00
|
|
|
return(1);
|
|
|
|
case STAR:
|
|
|
|
case QUEST:
|
|
|
|
first(left(p));
|
|
|
|
return(0);
|
|
|
|
case CAT:
|
|
|
|
if (first(left(p)) == 0 && first(right(p)) == 0) return(0);
|
|
|
|
return(1);
|
|
|
|
case OR:
|
|
|
|
b = first(right(p));
|
|
|
|
if (first(left(p)) == 0 || b == 0) return(0);
|
|
|
|
return(1);
|
2020-01-01 20:47:29 +00:00
|
|
|
case ZERO:
|
|
|
|
return 0;
|
2012-12-22 15:35:39 +00:00
|
|
|
}
|
|
|
|
FATAL("can't happen: unknown type %d in first", type(p)); /* can't happen */
|
|
|
|
return(-1);
|
|
|
|
}
|
|
|
|
|
|
|
|
void follow(Node *v) /* collects leaves that can follow v into setvec */
|
|
|
|
{
|
|
|
|
Node *p;
|
|
|
|
|
|
|
|
if (type(v) == FINAL)
|
|
|
|
return;
|
|
|
|
p = parent(v);
|
|
|
|
switch (type(p)) {
|
|
|
|
case STAR:
|
|
|
|
case PLUS:
|
|
|
|
first(v);
|
|
|
|
follow(p);
|
|
|
|
return;
|
|
|
|
|
|
|
|
case OR:
|
|
|
|
case QUEST:
|
|
|
|
follow(p);
|
|
|
|
return;
|
|
|
|
|
|
|
|
case CAT:
|
|
|
|
if (v == left(p)) { /* v is left child of p */
|
|
|
|
if (first(right(p)) == 0) {
|
|
|
|
follow(p);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
} else /* v is right child */
|
|
|
|
follow(p);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
int member(int c, const char *sarg) /* is c in s? */
|
|
|
|
{
|
2019-10-24 13:40:15 +00:00
|
|
|
const uschar *s = (const uschar *) sarg;
|
2012-12-22 15:35:39 +00:00
|
|
|
|
|
|
|
while (*s)
|
|
|
|
if (c == *s++)
|
|
|
|
return(1);
|
|
|
|
return(0);
|
|
|
|
}
|
|
|
|
|
|
|
|
int match(fa *f, const char *p0) /* shortest match ? */
|
|
|
|
{
|
|
|
|
int s, ns;
|
2019-10-24 13:40:15 +00:00
|
|
|
const uschar *p = (const uschar *) p0;
|
2012-12-22 15:35:39 +00:00
|
|
|
|
2019-10-17 17:04:46 +00:00
|
|
|
s = f->initstat;
|
|
|
|
assert (s < f->state_count);
|
|
|
|
|
2012-12-22 15:35:39 +00:00
|
|
|
if (f->out[s])
|
|
|
|
return(1);
|
|
|
|
do {
|
|
|
|
/* assert(*p < NCHARS); */
|
|
|
|
if ((ns = f->gototab[s][*p]) != 0)
|
|
|
|
s = ns;
|
|
|
|
else
|
|
|
|
s = cgoto(f, s, *p);
|
|
|
|
if (f->out[s])
|
|
|
|
return(1);
|
|
|
|
} while (*p++ != 0);
|
|
|
|
return(0);
|
|
|
|
}
|
|
|
|
|
|
|
|
int pmatch(fa *f, const char *p0) /* longest match, for sub */
|
|
|
|
{
|
|
|
|
int s, ns;
|
2019-10-24 13:40:15 +00:00
|
|
|
const uschar *p = (const uschar *) p0;
|
|
|
|
const uschar *q;
|
2012-12-22 15:35:39 +00:00
|
|
|
|
2019-10-17 17:04:46 +00:00
|
|
|
s = f->initstat;
|
|
|
|
assert(s < f->state_count);
|
|
|
|
|
2019-10-24 13:40:15 +00:00
|
|
|
patbeg = (const char *)p;
|
2012-12-22 15:35:39 +00:00
|
|
|
patlen = -1;
|
|
|
|
do {
|
|
|
|
q = p;
|
|
|
|
do {
|
|
|
|
if (f->out[s]) /* final state */
|
|
|
|
patlen = q-p;
|
|
|
|
/* assert(*q < NCHARS); */
|
|
|
|
if ((ns = f->gototab[s][*q]) != 0)
|
|
|
|
s = ns;
|
|
|
|
else
|
|
|
|
s = cgoto(f, s, *q);
|
2019-10-17 17:04:46 +00:00
|
|
|
|
|
|
|
assert(s < f->state_count);
|
|
|
|
|
2012-12-22 15:35:39 +00:00
|
|
|
if (s == 1) { /* no transition */
|
|
|
|
if (patlen >= 0) {
|
2019-10-24 13:40:15 +00:00
|
|
|
patbeg = (const char *) p;
|
2012-12-22 15:35:39 +00:00
|
|
|
return(1);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
goto nextin; /* no match */
|
|
|
|
}
|
|
|
|
} while (*q++ != 0);
|
|
|
|
if (f->out[s])
|
|
|
|
patlen = q-p-1; /* don't count $ */
|
|
|
|
if (patlen >= 0) {
|
2019-10-24 13:40:15 +00:00
|
|
|
patbeg = (const char *) p;
|
2012-12-22 15:35:39 +00:00
|
|
|
return(1);
|
|
|
|
}
|
|
|
|
nextin:
|
|
|
|
s = 2;
|
2019-10-17 17:04:46 +00:00
|
|
|
} while (*p++);
|
2012-12-22 15:35:39 +00:00
|
|
|
return (0);
|
|
|
|
}
|
|
|
|
|
|
|
|
int nematch(fa *f, const char *p0) /* non-empty match, for sub */
|
|
|
|
{
|
|
|
|
int s, ns;
|
2019-10-24 13:40:15 +00:00
|
|
|
const uschar *p = (const uschar *) p0;
|
|
|
|
const uschar *q;
|
2012-12-22 15:35:39 +00:00
|
|
|
|
2019-10-17 17:04:46 +00:00
|
|
|
s = f->initstat;
|
|
|
|
assert(s < f->state_count);
|
|
|
|
|
2019-10-24 13:40:15 +00:00
|
|
|
patbeg = (const char *)p;
|
2012-12-22 15:35:39 +00:00
|
|
|
patlen = -1;
|
|
|
|
while (*p) {
|
|
|
|
q = p;
|
|
|
|
do {
|
|
|
|
if (f->out[s]) /* final state */
|
|
|
|
patlen = q-p;
|
|
|
|
/* assert(*q < NCHARS); */
|
|
|
|
if ((ns = f->gototab[s][*q]) != 0)
|
|
|
|
s = ns;
|
|
|
|
else
|
|
|
|
s = cgoto(f, s, *q);
|
|
|
|
if (s == 1) { /* no transition */
|
|
|
|
if (patlen > 0) {
|
2019-10-24 13:40:15 +00:00
|
|
|
patbeg = (const char *) p;
|
2012-12-22 15:35:39 +00:00
|
|
|
return(1);
|
|
|
|
} else
|
|
|
|
goto nnextin; /* no nonempty match */
|
|
|
|
}
|
|
|
|
} while (*q++ != 0);
|
|
|
|
if (f->out[s])
|
|
|
|
patlen = q-p-1; /* don't count $ */
|
|
|
|
if (patlen > 0 ) {
|
2019-10-24 13:40:15 +00:00
|
|
|
patbeg = (const char *) p;
|
2012-12-22 15:35:39 +00:00
|
|
|
return(1);
|
|
|
|
}
|
|
|
|
nnextin:
|
|
|
|
s = 2;
|
|
|
|
p++;
|
|
|
|
}
|
|
|
|
return (0);
|
|
|
|
}
|
|
|
|
|
2019-07-28 17:12:05 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* NAME
|
|
|
|
* fnematch
|
|
|
|
*
|
|
|
|
* DESCRIPTION
|
|
|
|
* A stream-fed version of nematch which transfers characters to a
|
|
|
|
* null-terminated buffer. All characters up to and including the last
|
|
|
|
* character of the matching text or EOF are placed in the buffer. If
|
|
|
|
* a match is found, patbeg and patlen are set appropriately.
|
|
|
|
*
|
|
|
|
* RETURN VALUES
|
2019-11-10 19:19:18 +00:00
|
|
|
* false No match found.
|
|
|
|
* true Match found.
|
2019-07-28 17:12:05 +00:00
|
|
|
*/
|
|
|
|
|
2019-11-10 19:19:18 +00:00
|
|
|
bool fnematch(fa *pfa, FILE *f, char **pbuf, int *pbufsize, int quantum)
|
2019-07-28 17:12:05 +00:00
|
|
|
{
|
2019-10-07 12:50:53 +00:00
|
|
|
char *buf = *pbuf;
|
2019-07-28 17:12:05 +00:00
|
|
|
int bufsize = *pbufsize;
|
|
|
|
int c, i, j, k, ns, s;
|
|
|
|
|
|
|
|
s = pfa->initstat;
|
|
|
|
patlen = 0;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* All indices relative to buf.
|
|
|
|
* i <= j <= k <= bufsize
|
|
|
|
*
|
|
|
|
* i: origin of active substring
|
|
|
|
* j: current character
|
|
|
|
* k: destination of next getc()
|
|
|
|
*/
|
|
|
|
i = -1, k = 0;
|
|
|
|
do {
|
|
|
|
j = i++;
|
|
|
|
do {
|
|
|
|
if (++j == k) {
|
|
|
|
if (k == bufsize)
|
|
|
|
if (!adjbuf((char **) &buf, &bufsize, bufsize+1, quantum, 0, "fnematch"))
|
|
|
|
FATAL("stream '%.30s...' too long", buf);
|
|
|
|
buf[k++] = (c = getc(f)) != EOF ? c : 0;
|
|
|
|
}
|
|
|
|
c = buf[j];
|
|
|
|
/* assert(c < NCHARS); */
|
|
|
|
|
|
|
|
if ((ns = pfa->gototab[s][c]) != 0)
|
|
|
|
s = ns;
|
|
|
|
else
|
|
|
|
s = cgoto(pfa, s, c);
|
|
|
|
|
|
|
|
if (pfa->out[s]) { /* final state */
|
|
|
|
patlen = j - i + 1;
|
|
|
|
if (c == 0) /* don't count $ */
|
|
|
|
patlen--;
|
|
|
|
}
|
|
|
|
} while (buf[j] && s != 1);
|
|
|
|
s = 2;
|
|
|
|
} while (buf[i] && !patlen);
|
|
|
|
|
|
|
|
/* adjbuf() may have relocated a resized buffer. Inform the world. */
|
|
|
|
*pbuf = buf;
|
|
|
|
*pbufsize = bufsize;
|
|
|
|
|
|
|
|
if (patlen) {
|
|
|
|
patbeg = (char *) buf + i;
|
|
|
|
/*
|
|
|
|
* Under no circumstances is the last character fed to
|
|
|
|
* the automaton part of the match. It is EOF's nullbyte,
|
|
|
|
* or it sent the automaton into a state with no further
|
|
|
|
* transitions available (s==1), or both. Room for a
|
|
|
|
* terminating nullbyte is guaranteed.
|
|
|
|
*
|
|
|
|
* ungetc any chars after the end of matching text
|
|
|
|
* (except for EOF's nullbyte, if present) and null
|
|
|
|
* terminate the buffer.
|
|
|
|
*/
|
|
|
|
do
|
|
|
|
if (buf[--k] && ungetc(buf[k], f) == EOF)
|
|
|
|
FATAL("unable to ungetc '%c'", buf[k]);
|
|
|
|
while (k > i + patlen);
|
2020-01-01 20:42:50 +00:00
|
|
|
buf[k] = '\0';
|
2019-11-10 19:19:18 +00:00
|
|
|
return true;
|
2019-07-28 17:12:05 +00:00
|
|
|
}
|
|
|
|
else
|
2019-11-10 19:19:18 +00:00
|
|
|
return false;
|
2019-07-28 17:12:05 +00:00
|
|
|
}
|
|
|
|
|
2012-12-22 15:35:39 +00:00
|
|
|
Node *reparse(const char *p) /* parses regular expression pointed to by p */
|
|
|
|
{ /* uses relex() to scan regular expression */
|
|
|
|
Node *np;
|
|
|
|
|
2020-06-25 18:32:34 +00:00
|
|
|
DPRINTF("reparse <%s>\n", p);
|
2019-10-24 13:40:15 +00:00
|
|
|
lastre = prestr = (const uschar *) p; /* prestr points to string to be parsed */
|
2012-12-22 15:35:39 +00:00
|
|
|
rtok = relex();
|
|
|
|
/* GNU compatibility: an empty regexp matches anything */
|
|
|
|
if (rtok == '\0') {
|
|
|
|
/* FATAL("empty regular expression"); previous */
|
|
|
|
return(op2(EMPTYRE, NIL, NIL));
|
|
|
|
}
|
|
|
|
np = regexp();
|
|
|
|
if (rtok != '\0')
|
|
|
|
FATAL("syntax error in regular expression %s at %s", lastre, prestr);
|
|
|
|
return(np);
|
|
|
|
}
|
|
|
|
|
|
|
|
Node *regexp(void) /* top-level parse of reg expr */
|
|
|
|
{
|
|
|
|
return (alt(concat(primary())));
|
|
|
|
}
|
|
|
|
|
|
|
|
Node *primary(void)
|
|
|
|
{
|
|
|
|
Node *np;
|
2019-01-23 09:12:27 +00:00
|
|
|
int savelastatom;
|
2012-12-22 15:35:39 +00:00
|
|
|
|
|
|
|
switch (rtok) {
|
|
|
|
case CHAR:
|
2019-01-23 09:12:27 +00:00
|
|
|
lastatom = starttok;
|
2012-12-22 15:35:39 +00:00
|
|
|
np = op2(CHAR, NIL, itonp(rlxval));
|
|
|
|
rtok = relex();
|
|
|
|
return (unary(np));
|
|
|
|
case ALL:
|
|
|
|
rtok = relex();
|
|
|
|
return (unary(op2(ALL, NIL, NIL)));
|
|
|
|
case EMPTYRE:
|
|
|
|
rtok = relex();
|
2019-01-23 09:12:27 +00:00
|
|
|
return (unary(op2(EMPTYRE, NIL, NIL)));
|
2012-12-22 15:35:39 +00:00
|
|
|
case DOT:
|
2019-01-23 09:12:27 +00:00
|
|
|
lastatom = starttok;
|
2012-12-22 15:35:39 +00:00
|
|
|
rtok = relex();
|
|
|
|
return (unary(op2(DOT, NIL, NIL)));
|
|
|
|
case CCL:
|
2019-10-24 13:40:15 +00:00
|
|
|
np = op2(CCL, NIL, (Node*) cclenter((const char *) rlxstr));
|
2019-01-23 09:12:27 +00:00
|
|
|
lastatom = starttok;
|
2012-12-22 15:35:39 +00:00
|
|
|
rtok = relex();
|
|
|
|
return (unary(np));
|
|
|
|
case NCCL:
|
2019-10-24 13:40:15 +00:00
|
|
|
np = op2(NCCL, NIL, (Node *) cclenter((const char *) rlxstr));
|
2019-01-23 09:12:27 +00:00
|
|
|
lastatom = starttok;
|
2012-12-22 15:35:39 +00:00
|
|
|
rtok = relex();
|
|
|
|
return (unary(np));
|
|
|
|
case '^':
|
|
|
|
rtok = relex();
|
|
|
|
return (unary(op2(CHAR, NIL, itonp(HAT))));
|
|
|
|
case '$':
|
|
|
|
rtok = relex();
|
|
|
|
return (unary(op2(CHAR, NIL, NIL)));
|
|
|
|
case '(':
|
2019-01-23 09:12:27 +00:00
|
|
|
lastatom = starttok;
|
|
|
|
savelastatom = starttok - basestr; /* Retain over recursion */
|
2012-12-22 15:35:39 +00:00
|
|
|
rtok = relex();
|
|
|
|
if (rtok == ')') { /* special pleading for () */
|
|
|
|
rtok = relex();
|
|
|
|
return unary(op2(CCL, NIL, (Node *) tostring("")));
|
|
|
|
}
|
|
|
|
np = regexp();
|
|
|
|
if (rtok == ')') {
|
2019-01-23 09:12:27 +00:00
|
|
|
lastatom = basestr + savelastatom; /* Restore */
|
2012-12-22 15:35:39 +00:00
|
|
|
rtok = relex();
|
|
|
|
return (unary(np));
|
|
|
|
}
|
|
|
|
else
|
|
|
|
FATAL("syntax error in regular expression %s at %s", lastre, prestr);
|
|
|
|
default:
|
|
|
|
FATAL("illegal primary in regular expression %s at %s", lastre, prestr);
|
|
|
|
}
|
|
|
|
return 0; /*NOTREACHED*/
|
|
|
|
}
|
|
|
|
|
|
|
|
Node *concat(Node *np)
|
|
|
|
{
|
|
|
|
switch (rtok) {
|
2019-01-23 09:12:27 +00:00
|
|
|
case CHAR: case DOT: case ALL: case CCL: case NCCL: case '$': case '(':
|
2012-12-22 15:35:39 +00:00
|
|
|
return (concat(op2(CAT, np, primary())));
|
2019-01-23 09:12:27 +00:00
|
|
|
case EMPTYRE:
|
|
|
|
rtok = relex();
|
|
|
|
return (concat(op2(CAT, op2(CCL, NIL, (Node *) tostring("")),
|
|
|
|
primary())));
|
2012-12-22 15:35:39 +00:00
|
|
|
}
|
|
|
|
return (np);
|
|
|
|
}
|
|
|
|
|
|
|
|
Node *alt(Node *np)
|
|
|
|
{
|
|
|
|
if (rtok == OR) {
|
|
|
|
rtok = relex();
|
|
|
|
return (alt(op2(OR, np, concat(primary()))));
|
|
|
|
}
|
|
|
|
return (np);
|
|
|
|
}
|
|
|
|
|
|
|
|
Node *unary(Node *np)
|
|
|
|
{
|
|
|
|
switch (rtok) {
|
|
|
|
case STAR:
|
|
|
|
rtok = relex();
|
|
|
|
return (unary(op2(STAR, np, NIL)));
|
|
|
|
case PLUS:
|
|
|
|
rtok = relex();
|
|
|
|
return (unary(op2(PLUS, np, NIL)));
|
|
|
|
case QUEST:
|
|
|
|
rtok = relex();
|
|
|
|
return (unary(op2(QUEST, np, NIL)));
|
2020-01-01 20:47:29 +00:00
|
|
|
case ZERO:
|
|
|
|
rtok = relex();
|
|
|
|
return (unary(op2(ZERO, np, NIL)));
|
2012-12-22 15:35:39 +00:00
|
|
|
default:
|
|
|
|
return (np);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Character class definitions conformant to the POSIX locale as
|
|
|
|
* defined in IEEE P1003.1 draft 7 of June 2001, assuming the source
|
|
|
|
* and operating character sets are both ASCII (ISO646) or supersets
|
|
|
|
* thereof.
|
|
|
|
*
|
|
|
|
* Note that to avoid overflowing the temporary buffer used in
|
|
|
|
* relex(), the expanded character class (prior to range expansion)
|
|
|
|
* must be less than twice the size of their full name.
|
|
|
|
*/
|
|
|
|
|
|
|
|
/* Because isblank doesn't show up in any of the header files on any
|
|
|
|
* system i use, it's defined here. if some other locale has a richer
|
|
|
|
* definition of "blank", define HAS_ISBLANK and provide your own
|
|
|
|
* version.
|
|
|
|
* the parentheses here are an attempt to find a path through the maze
|
|
|
|
* of macro definition and/or function and/or version provided. thanks
|
|
|
|
* to nelson beebe for the suggestion; let's see if it works everywhere.
|
|
|
|
*/
|
|
|
|
|
|
|
|
/* #define HAS_ISBLANK */
|
|
|
|
#ifndef HAS_ISBLANK
|
|
|
|
|
|
|
|
int (xisblank)(int c)
|
|
|
|
{
|
|
|
|
return c==' ' || c=='\t';
|
|
|
|
}
|
|
|
|
|
|
|
|
#endif
|
|
|
|
|
2020-01-24 09:11:59 +00:00
|
|
|
static const struct charclass {
|
2012-12-22 15:35:39 +00:00
|
|
|
const char *cc_name;
|
|
|
|
int cc_namelen;
|
|
|
|
int (*cc_func)(int);
|
|
|
|
} charclasses[] = {
|
|
|
|
{ "alnum", 5, isalnum },
|
|
|
|
{ "alpha", 5, isalpha },
|
|
|
|
#ifndef HAS_ISBLANK
|
2018-08-22 17:40:26 +00:00
|
|
|
{ "blank", 5, xisblank },
|
2012-12-22 15:35:39 +00:00
|
|
|
#else
|
|
|
|
{ "blank", 5, isblank },
|
|
|
|
#endif
|
|
|
|
{ "cntrl", 5, iscntrl },
|
|
|
|
{ "digit", 5, isdigit },
|
|
|
|
{ "graph", 5, isgraph },
|
|
|
|
{ "lower", 5, islower },
|
|
|
|
{ "print", 5, isprint },
|
|
|
|
{ "punct", 5, ispunct },
|
|
|
|
{ "space", 5, isspace },
|
|
|
|
{ "upper", 5, isupper },
|
|
|
|
{ "xdigit", 6, isxdigit },
|
|
|
|
{ NULL, 0, NULL },
|
|
|
|
};
|
|
|
|
|
2019-01-23 09:12:27 +00:00
|
|
|
#define REPEAT_SIMPLE 0
|
|
|
|
#define REPEAT_PLUS_APPENDED 1
|
|
|
|
#define REPEAT_WITH_Q 2
|
|
|
|
#define REPEAT_ZERO 3
|
|
|
|
|
|
|
|
static int
|
|
|
|
replace_repeat(const uschar *reptok, int reptoklen, const uschar *atom,
|
|
|
|
int atomlen, int firstnum, int secondnum, int special_case)
|
|
|
|
|