/* lazy-DFA representation */ struct arcp
{ /* "pointer" to an outarc */ struct sset *ss;
color co;
};
struct sset
{ /* state set */ unsigned *states; /* pointer to bitvector */ unsigned hash; /* hash of bitvector */ #define HASH(bv, nw) (((nw) == 1) ? *(bv) : hash(bv, nw)) #define HIT(h,bv,ss,nw) ((ss)->hash == (h) && ((nw) == 1 || \
memcmp(VS(bv), VS((ss)->states), (nw)*sizeof(unsigned)) == 0)) int flags; #define STARTER 01/* the initial state set */ #define POSTSTATE 02/* includes the goal state */ #define LOCKED 04/* locked in cache */ #define NOPROGRESS 010/* zero-progress state set */ struct arcp ins; /* chain of inarcs pointing here */
chr *lastseen; /* last entered on arrival here */ struct sset **outs; /* outarc vector indexed by color */ struct arcp *inchain; /* chain-pointer vector for outarcs */
};
struct dfa
{ int nssets; /* size of cache */ int nssused; /* how many entries occupied yet */ int nstates; /* number of states */ int ncolors; /* length of outarc and inchain vectors */ int wordsper; /* length of state-set bitvectors */ struct sset *ssets; /* state-set cache */ unsigned *statesarea; /* bitvector storage */ unsigned *work; /* pointer to work area within statesarea */ struct sset **outsarea; /* outarc-vector storage */ struct arcp *incarea; /* inchain storage */ struct cnfa *cnfa; struct colormap *cm;
chr *lastpost; /* location of last cache-flushed success */
chr *lastnopr; /* location of last cache-flushed NOPROGRESS */ struct sset *search; /* replacement-search-pointer memory */ int backno; /* if DFA for a backref, subno it refers to */ short backmin; /* min repetitions for backref */ short backmax; /* max repetitions for backref */ bool ismalloced; /* should this struct dfa be freed? */ bool arraysmalloced; /* should its subsidiary arrays be freed? */
};
#define WORK 1/* number of work bitvectors needed */
/* setup for non-malloc allocation for small cases */ #define FEWSTATES 20/* must be less than UBITS */ #define FEWCOLORS 15 struct smalldfa
{ struct dfa dfa; /* must be first */ struct sset ssets[FEWSTATES * 2]; unsigned statesarea[FEWSTATES * 2 + WORK]; struct sset *outsarea[FEWSTATES * 2 * FEWCOLORS]; struct arcp incarea[FEWSTATES * 2 * FEWCOLORS];
};
#define DOMALLOC ((struct smalldfa *)NULL) /* force malloc */
/* internal variables, bundled for easy passing around */ struct vars
{
regex_t *re; struct guts *g; int eflags; /* copies of arguments */
size_t nmatch;
regmatch_t *pmatch;
rm_detail_t *details;
chr *start; /* start of string */
chr *search_start; /* search start of string */
chr *stop; /* just past end of string */ int err; /* error code if any (0 none) */ struct dfa **subdfas; /* per-tree-subre DFAs */ struct dfa **ladfas; /* per-lacon-subre DFAs */ struct sset **lblastcss; /* per-lacon-subre lookbehind restart data */
chr **lblastcp; /* per-lacon-subre lookbehind restart data */ struct smalldfa dfa1; struct smalldfa dfa2;
};
#define VISERR(vv) ((vv)->err != 0) /* have we seen an error yet? */ #define ISERR() VISERR(v) #define VERR(vv,e) ((vv)->err = ((vv)->err ? (vv)->err : (e))) #define ERR(e) VERR(v, e) /* record an error */ #define NOERR() {if (ISERR()) return v->err;} /* if error seen, return it */ #define OFF(p) ((p) - v->start) #define LOFF(p) ((long)OFF(p))
/* sanity checks */ if (re == NULL || string == NULL || re->re_magic != REMAGIC) return REG_INVARG; if (re->re_csize != sizeof(chr)) return REG_MIXED; if (search_start > len) return REG_NOMATCH;
/* Initialize locale-dependent support */
pg_set_regex_collation(re->re_collation);
/* setup */
v->re = re;
v->g = (struct guts *) re->re_guts; if ((v->g->cflags & REG_EXPECT) && details == NULL) return REG_INVARG; if (v->g->info & REG_UIMPOSSIBLE) return REG_NOMATCH;
backref = (v->g->info & REG_UBACKREF) ? 1 : 0;
v->eflags = flags; if (backref && nmatch <= v->g->nsub)
{ /* need larger work area */
v->nmatch = v->g->nsub + 1; if (v->nmatch <= LOCALMAT)
v->pmatch = mat; else
v->pmatch = MALLOC_ARRAY(regmatch_t, v->nmatch); if (v->pmatch == NULL) return REG_ESPACE;
zapallsubs(v->pmatch, v->nmatch);
} else
{ /* we can store results directly in caller's array */
v->pmatch = pmatch; /* ensure any extra entries in caller's array are filled with -1 */ if (nmatch > 0)
zapallsubs(pmatch, nmatch); /* then forget about extra entries, to avoid useless work in find() */ if (nmatch > v->g->nsub + 1)
nmatch = v->g->nsub + 1;
v->nmatch = nmatch;
}
v->details = details;
v->start = (chr *) string;
v->search_start = (chr *) string + search_start;
v->stop = (chr *) string + len;
v->err = 0;
v->subdfas = NULL;
v->ladfas = NULL;
v->lblastcss = NULL;
v->lblastcp = NULL; /* below this point, "goto cleanup" will behave sanely */
assert(v->g->ntree >= 0);
n = (size_t) v->g->ntree; if (n <= LOCALDFAS)
v->subdfas = subdfas; else
{ /* ntree is surely less than the number of states, so this is safe: */
v->subdfas = (struct dfa **) MALLOC(n * sizeof(struct dfa *)); if (v->subdfas == NULL)
{
st = REG_ESPACE; goto cleanup;
}
} for (i = 0; i < n; i++)
v->subdfas[i] = NULL;
assert(v->g->nlacons >= 0);
n = (size_t) v->g->nlacons; if (n > 0)
{ /* nlacons is surely less than the number of arcs, so this is safe: */
v->ladfas = (struct dfa **) MALLOC(n * sizeof(struct dfa *)); if (v->ladfas == NULL)
{
st = REG_ESPACE; goto cleanup;
} for (i = 0; i < n; i++)
v->ladfas[i] = NULL;
v->lblastcss = (struct sset **) MALLOC(n * sizeof(struct sset *));
v->lblastcp = (chr **) MALLOC(n * sizeof(chr *)); if (v->lblastcss == NULL || v->lblastcp == NULL)
{
st = REG_ESPACE; goto cleanup;
} for (i = 0; i < n; i++)
{
v->lblastcss[i] = NULL;
v->lblastcp[i] = NULL;
}
}
/* do it */
assert(v->g->tree != NULL); if (backref)
st = cfind(v, &v->g->tree->cnfa, &v->g->cmap); else
st = find(v, &v->g->tree->cnfa, &v->g->cmap);
/* on success, ensure caller's match vector is filled correctly */ if (st == REG_OKAY && nmatch > 0)
{ if (v->pmatch != pmatch)
{ /* copy portion of match vector over from (larger) work area */
assert(nmatch <= v->nmatch);
memcpy(VS(pmatch), VS(v->pmatch), nmatch * sizeof(regmatch_t));
} if (v->g->cflags & REG_NOSUB)
{ /* don't expose possibly-partial sub-match results to caller */
zapallsubs(pmatch, nmatch);
}
}
/* clean up */
cleanup: if (v->pmatch != pmatch && v->pmatch != mat)
FREE(v->pmatch); if (v->subdfas != NULL)
{
n = (size_t) v->g->ntree; for (i = 0; i < n; i++)
{ if (v->subdfas[i] != NULL)
freedfa(v->subdfas[i]);
} if (v->subdfas != subdfas)
FREE(v->subdfas);
} if (v->ladfas != NULL)
{
n = (size_t) v->g->nlacons; for (i = 0; i < n; i++)
{ if (v->ladfas[i] != NULL)
freedfa(v->ladfas[i]);
}
FREE(v->ladfas);
} if (v->lblastcss != NULL)
FREE(v->lblastcss); if (v->lblastcp != NULL)
FREE(v->lblastcp);
#ifdef REG_DEBUG if (v->eflags & (REG_FTRACE | REG_MTRACE))
fflush(stdout); #endif
if (d == NULL)
{
d = newdfa(v, &t->cnfa, &v->g->cmap, DOMALLOC); if (d == NULL) return NULL; /* set up additional info if this is a backref node */ if (t->op == 'b')
{
d->backno = t->backno;
d->backmin = t->min;
d->backmax = t->max;
}
v->subdfas[t->id] = d;
} return d;
}
/* *getladfa-createorre-fetchtheDFAforaLACONsubrenode * *Sameasabove,butforLACONs.
*/ staticstruct dfa *
getladfa(struct vars *v, int n)
{
assert(n > 0 && n < v->g->nlacons && v->g->lacons != NULL);
if (v->ladfas[n] == NULL)
{ struct subre *sub = &v->g->lacons[n];
v->ladfas[n] = newdfa(v, &sub->cnfa, &v->g->cmap, DOMALLOC); /* a LACON can't contain a backref, so nothing else to do */
} return v->ladfas[n];
}
/* *find-findamatchforthemainNFA(no-complicationscase)
*/ staticint
find(struct vars *v, struct cnfa *cnfa, struct colormap *cm)
{ struct dfa *s; struct dfa *d;
chr *begin;
chr *end = NULL;
chr *cold;
chr *open; /* open and close of range of possible starts */
chr *close; int hitend; int shorter = (v->g->tree->flags & SHORTER) ? 1 : 0;
/* first, a shot with the search RE */
s = newdfa(v, &v->g->search, cm, &v->dfa1); if (s == NULL) return v->err;
MDEBUG(("\nsearch at %ld\n", LOFF(v->start)));
cold = NULL;
close = shortest(v, s, v->search_start, v->search_start, v->stop,
&cold, (int *) NULL);
freedfa(s);
NOERR(); if (v->g->cflags & REG_EXPECT)
{
assert(v->details != NULL); if (cold != NULL)
v->details->rm_extend.rm_so = OFF(cold); else
v->details->rm_extend.rm_so = OFF(v->stop);
v->details->rm_extend.rm_eo = OFF(v->stop); /* unknown */
} if (close == NULL) /* not found */ return REG_NOMATCH; if (v->nmatch == 0) /* found, don't need exact location */ return REG_OKAY;
/* find starting point and match */
assert(cold != NULL);
open = cold;
cold = NULL;
MDEBUG(("between %ld and %ld\n", LOFF(open), LOFF(close)));
d = newdfa(v, cnfa, cm, &v->dfa1); if (d == NULL) return v->err; for (begin = open; begin <= close; begin++)
{
MDEBUG(("\nfind trying at %ld\n", LOFF(begin))); if (shorter)
end = shortest(v, d, begin, begin, v->stop,
(chr **) NULL, &hitend); else
end = longest(v, d, begin, v->stop, &hitend); if (ISERR())
{
freedfa(d); return v->err;
} if (hitend && cold == NULL)
cold = begin; if (end != NULL) break; /* NOTE BREAK OUT */
}
assert(end != NULL); /* search RE succeeded so loop should */
freedfa(d);
/* and pin down details */
assert(v->nmatch > 0);
v->pmatch[0].rm_so = OFF(begin);
v->pmatch[0].rm_eo = OFF(end); if (v->g->cflags & REG_EXPECT)
{ if (cold != NULL)
v->details->rm_extend.rm_so = OFF(cold); else
v->details->rm_extend.rm_so = OFF(v->stop);
v->details->rm_extend.rm_eo = OFF(v->stop); /* unknown */
} if (v->nmatch == 1) /* no need for submatches */ return REG_OKAY;
s = newdfa(v, &v->g->search, cm, &v->dfa1); if (s == NULL) return v->err;
d = newdfa(v, cnfa, cm, &v->dfa2); if (d == NULL)
{
freedfa(s); return v->err;
}
/* *cfindloop-theheartofcfind
*/ staticint
cfindloop(struct vars *v, struct cnfa *cnfa, struct colormap *cm, struct dfa *d, struct dfa *s,
chr **coldp) /* where to put coldstart pointer */
{
chr *begin;
chr *end;
chr *cold;
chr *open; /* open and close of range of possible starts */
chr *close;
chr *estart;
chr *estop; int er; int shorter = v->g->tree->flags & SHORTER; int hitend;
assert(d != NULL && s != NULL);
cold = NULL;
close = v->search_start; do
{ /* Search with the search RE for match range at/beyond "close" */
MDEBUG(("\ncsearch at %ld\n", LOFF(close)));
close = shortest(v, s, close, close, v->stop, &cold, (int *) NULL); if (ISERR())
{
*coldp = cold; return v->err;
} if (close == NULL) break; /* no more possible match anywhere */
assert(cold != NULL);
open = cold;
cold = NULL; /* Search for matches starting between "open" and "close" inclusive */
MDEBUG(("cbetween %ld and %ld\n", LOFF(open), LOFF(close))); for (begin = open; begin <= close; begin++)
{
MDEBUG(("\ncfind trying at %ld\n", LOFF(begin)));
estart = begin;
estop = v->stop; for (;;)
{ /* Here we use the top node's detailed RE */ if (shorter)
end = shortest(v, d, begin, estart,
estop, (chr **) NULL, &hitend); else
end = longest(v, d, begin, estop,
&hitend); if (ISERR())
{
*coldp = cold; return v->err;
} if (hitend && cold == NULL)
cold = begin; if (end == NULL) break; /* no match with this begin point, try next */
MDEBUG(("tentative end %ld\n", LOFF(end))); /* Dissect the potential match to see if it really matches */
er = cdissect(v, v->g->tree, begin, end); if (er == REG_OKAY)
{ if (v->nmatch > 0)
{
v->pmatch[0].rm_so = OFF(begin);
v->pmatch[0].rm_eo = OFF(end);
}
*coldp = cold; return REG_OKAY;
} if (er != REG_NOMATCH)
{
ERR(er);
*coldp = cold; return er;
} /* Try next longer/shorter match with same begin point */ if (shorter)
{ if (end == estop) break; /* no more, so try next begin point */
estart = end + 1;
} else
{ if (end == begin) break; /* no more, so try next begin point */
estop = end - 1;
}
} /* end loop over endpoint positions */
} /* end loop over beginning positions */
/* iterate until satisfaction or failure */ for (;;)
{ /* try this midpoint on for size */ if (longest(v, d2, mid, end, (int *) NULL) == end)
{
er = cdissect(v, left, begin, mid); if (er == REG_OKAY)
{
er = cdissect(v, right, mid, end); if (er == REG_OKAY)
{ /* satisfaction */
MDEBUG(("%d: successful\n", t->id)); return REG_OKAY;
} /* Reset left's matches (right should have done so itself) */
zaptreesubs(v, left);
} if (er != REG_NOMATCH) return er;
}
NOERR();
/* that midpoint didn't work, find a new one */ if (mid == end)
{ /* all possibilities exhausted */
MDEBUG(("%d: no midpoint\n", t->id)); return REG_NOMATCH;
}
mid = shortest(v, d, begin, mid + 1, end, (chr **) NULL, (int *) NULL);
NOERR(); if (mid == NULL)
{ /* failed to find a new one */
MDEBUG(("%d: failed midpoint\n", t->id)); return REG_NOMATCH;
}
MDEBUG(("%d: new midpoint %ld\n", t->id, LOFF(mid)));
}
/* can't get here */ return REG_ASSERT;
}
/* *cbrdissect-dissectmatchforbackrefnode * *Thebackrefmatchmightalreadyhavebeenverifiedbydfa_backref(), *butwedon'tknowthatforsuresomustcheckithere.
*/ staticint/* regexec return code */
cbrdissect(struct vars *v, struct subre *t,
chr *begin, /* beginning of relevant substring */
chr *end) /* end of same */
{ int n = t->backno;
size_t numreps;
size_t tlen;
size_t brlen;
chr *brstring;
chr *p; int min = t->min; int max = t->max;
MDEBUG(("%d: cbrdissect %d{%d-%d} %ld-%ld\n", t->id, n, min, max,
LOFF(begin), LOFF(end)));
/* get the backreferenced string */ if (v->pmatch[n].rm_so == -1) return REG_NOMATCH;
brstring = v->start + v->pmatch[n].rm_so;
brlen = v->pmatch[n].rm_eo - v->pmatch[n].rm_so;
/* special cases for zero-length strings */ if (brlen == 0)
{ /* *matchesonlyiftargetiszerolength,butanynumberof *repetitionscanbeconsideredtobepresent
*/ if (begin == end && min <= max)
{
MDEBUG(("%d: backref matched trivially\n", t->id)); return REG_OKAY;
} return REG_NOMATCH;
} if (begin == end)
{ /* matches only if zero repetitions are okay */ if (min == 0)
{
MDEBUG(("%d: backref matched trivially\n", t->id)); return REG_OKAY;
} return REG_NOMATCH;
}
/* *checktargetlengthtoseeifitcouldpossiblybeanallowednumberof *repetitionsofbrstring
*/
assert(end > begin);
tlen = end - begin; if (tlen % brlen != 0) return REG_NOMATCH;
numreps = tlen / brlen; if (numreps < min || (numreps > max && max != DUPINF)) return REG_NOMATCH;
/* okay, compare the actual string contents */
p = begin; while (numreps-- > 0)
{ if ((*v->g->compare) (brstring, p, brlen) != 0) return REG_NOMATCH;
p += brlen;
}
d = getsubdfa(v, t);
NOERR(); if (longest(v, d, begin, end, (int *) NULL) == end)
{
MDEBUG(("%d: caltdissect matched\n", t->id));
er = cdissect(v, t, begin, end); if (er != REG_NOMATCH) return er;
}
NOERR();
t = t->sibling;
}
return REG_NOMATCH;
}
/* *citerdissect-dissectmatchforiterationnode
*/ staticint/* regexec return code */
citerdissect(struct vars *v, struct subre *t,
chr *begin, /* beginning of relevant substring */
chr *end) /* end of same */
{ struct dfa *d;
chr **endpts;
chr *limit; int min_matches;
size_t max_matches; int nverified; int k; int i; int er;
/* initialize to consider first sub-match */
nverified = 0;
k = 1;
limit = end;
/* iterate until satisfaction or failure */ while (k > 0)
{ /* try to find an endpoint for the k'th sub-match */
endpts[k] = longest(v, d, endpts[k - 1], limit, (int *) NULL); if (ISERR())
{
FREE(endpts); return v->err;
} if (endpts[k] == NULL)
{ /* no match possible, so see if we can shorten previous one */
k--; goto backtrack;
}
MDEBUG(("%d: working endpoint %d: %ld\n",
t->id, k, LOFF(endpts[k])));
/* k'th sub-match can no longer be considered verified */ if (nverified >= k)
nverified = k - 1;
if (endpts[k] != end)
{ /* haven't reached end yet, try another iteration if allowed */ if (k >= max_matches)
{ /* must try to shorten some previous match */
k--; goto backtrack;
}
/* reject zero-length match unless necessary to achieve min */ if (endpts[k] == endpts[k - 1] &&
(k >= min_matches || min_matches - k < end - endpts[k])) goto backtrack;
for (i = nverified + 1; i <= k; i++)
{ /* zap any match data from a non-last iteration */
zaptreesubs(v, t->child);
er = cdissect(v, t->child, endpts[i - 1], endpts[i]); if (er == REG_OKAY)
{
nverified = i; continue;
} if (er == REG_NOMATCH) break; /* oops, something failed */
FREE(endpts); return er;
}
if (i > k)
{ /* satisfaction */
MDEBUG(("%d: successful\n", t->id));
FREE(endpts); return REG_OKAY;
}
/* i'th match failed to verify, so backtrack it */
k = i;
/* *creviterdissect-dissectmatchforiterationnode,shortest-first
*/ staticint/* regexec return code */
creviterdissect(struct vars *v, struct subre *t,
chr *begin, /* beginning of relevant substring */
chr *end) /* end of same */
{ struct dfa *d;
chr **endpts;
chr *limit; int min_matches;
size_t max_matches; int nverified; int k; int i; int er;
/* initialize to consider first sub-match */
nverified = 0;
k = 1;
limit = begin;
/* iterate until satisfaction or failure */ while (k > 0)
{ /* disallow zero-length match unless necessary to achieve min */ if (limit == endpts[k - 1] &&
limit != end &&
(k >= min_matches || min_matches - k < end - limit))
limit++;
/* if this is the last allowed sub-match, it must reach to the end */ if (k >= max_matches)
limit = end;
/* try to find an endpoint for the k'th sub-match */
endpts[k] = shortest(v, d, endpts[k - 1], limit, end,
(chr **) NULL, (int *) NULL); if (ISERR())
{
FREE(endpts); return v->err;
} if (endpts[k] == NULL)
{ /* no match possible, so see if we can lengthen previous one */
k--; goto backtrack;
}
MDEBUG(("%d: working endpoint %d: %ld\n",
t->id, k, LOFF(endpts[k])));
/* k'th sub-match can no longer be considered verified */ if (nverified >= k)
nverified = k - 1;
if (endpts[k] != end)
{ /* haven't reached end yet, try another iteration if allowed */ if (k >= max_matches)
{ /* must try to lengthen some previous match */
k--; goto backtrack;
}
for (i = nverified + 1; i <= k; i++)
{ /* zap any match data from a non-last iteration */
zaptreesubs(v, t->child);
er = cdissect(v, t->child, endpts[i - 1], endpts[i]); if (er == REG_OKAY)
{
nverified = i; continue;
} if (er == REG_NOMATCH) break; /* oops, something failed */
FREE(endpts); return er;
}
if (i > k)
{ /* satisfaction */
MDEBUG(("%d: successful\n", t->id));
FREE(endpts); return REG_OKAY;
}
/* i'th match failed to verify, so backtrack it */
k = i;
backtrack:
/* *Mustconsiderlongerversionsofthek'thsub-match.
*/ while (k > 0)
{ if (endpts[k] < end)
{
limit = endpts[k] + 1; /* break out of backtrack loop, continue the outer one */ break;
} /* can't lengthen k'th sub-match any more, consider previous one */
k--;
}
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.