diff options
Diffstat (limited to 'generic/regexec.c')
| -rw-r--r-- | generic/regexec.c | 1218 | 
1 files changed, 1218 insertions, 0 deletions
| diff --git a/generic/regexec.c b/generic/regexec.c new file mode 100644 index 0000000..ad4b6e6 --- /dev/null +++ b/generic/regexec.c @@ -0,0 +1,1218 @@ +/* + * re_*exec and friends - match REs + * + * Copyright (c) 1998, 1999 Henry Spencer.  All rights reserved. + * + * Development of this software was funded, in part, by Cray Research Inc., + * UUNET Communications Services Inc., Sun Microsystems Inc., and Scriptics + * Corporation, none of whom are responsible for the results.  The author + * thanks all of them. + * + * Redistribution and use in source and binary forms -- with or without + * modification -- are permitted for any purpose, provided that + * redistributions in source form retain this entire copyright notice and + * indicate the origin and nature of any modifications. + * + * I'd appreciate being given credit for this package in the documentation of + * software which uses it, but that is not a requirement. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL + * HENRY SPENCER BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "regguts.h" + +/* + * 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 cptsmalloced;		/* were the areas individually malloced? */ +    char *mallocarea;		/* self, or master malloced area, or NULL */ +}; + +#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; +    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 *stop;			/* just past end of string */ +    int err;			/* error code if any (0 none) */ +    regoff_t *mem;		/* memory vector for backtracking */ +    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)) + +/* + * forward declarations + */ +/* =====^!^===== begin forwards =====^!^===== */ +/* automatically gathered by fwd; do not hand-edit */ +/* === regexec.c === */ +int exec(regex_t *, const chr *, size_t, rm_detail_t *, size_t, regmatch_t [], int); +static int simpleFind(struct vars *const, struct cnfa *const, struct colormap *const); +static int complicatedFind(struct vars *const, struct cnfa *const, struct colormap *const); +static int complicatedFindLoop(struct vars *const, struct cnfa *const, struct colormap *const, struct dfa *const, struct dfa *const, chr **const); +static void zapSubexpressions(regmatch_t *const, const size_t); +static void zapSubtree(struct vars *const, struct subre *const); +static void subset(struct vars *const, struct subre *const, chr *const, chr *const); +static int dissect(struct vars *const, struct subre *, chr *const, chr *const); +static int concatenationDissect(struct vars *const, struct subre *const, chr *const, chr *const); +static int alternationDissect(struct vars *const, struct subre *, chr *const, chr *const); +static inline int complicatedDissect(struct vars *const, struct subre *const, chr *const, chr *const); +static int complicatedCapturingDissect(struct vars *const, struct subre *const, chr *const, chr *const); +static int complicatedConcatenationDissect(struct vars *const, struct subre *const, chr *const, chr *const); +static int complicatedReversedDissect(struct vars *const, struct subre *const, chr *const, chr *const); +static int complicatedBackrefDissect(struct vars *const, struct subre *const, chr *const, chr *const); +static int complicatedAlternationDissect(struct vars *const, struct subre *, chr *const, chr *const); +/* === rege_dfa.c === */ +static chr *longest(struct vars *const, struct dfa *const, chr *const, chr *const, int *const); +static chr *shortest(struct vars *const, struct dfa *const, chr *const, chr *const, chr *const, chr **const, int *const); +static chr *lastCold(struct vars *const, struct dfa *const); +static struct dfa *newDFA(struct vars *const, struct cnfa *const, struct colormap *const, struct smalldfa *); +static void freeDFA(struct dfa *const); +static unsigned hash(unsigned *const, const int); +static struct sset *initialize(struct vars *const, struct dfa *const, chr *const); +static struct sset *miss(struct vars *const, struct dfa *const, struct sset *const, const pcolor, chr *const, chr *const); +static int checkLAConstraint(struct vars *const, struct cnfa *const, chr *const, const pcolor); +static struct sset *getVacantSS(struct vars *const, struct dfa *const, chr *const, chr *const); +static struct sset *pickNextSS(struct vars *const, struct dfa *const, chr *const, chr *const); +/* automatically gathered by fwd; do not hand-edit */ +/* =====^!^===== end forwards =====^!^===== */ + +/* + - exec - match regular expression + ^ int exec(regex_t *, const chr *, size_t, rm_detail_t *, + ^					size_t, regmatch_t [], int); + */ +int +exec( +    regex_t *re, +    const chr *string, +    size_t len, +    rm_detail_t *details, +    size_t nmatch, +    regmatch_t pmatch[], +    int flags) +{ +    AllocVars(v); +    int st, backref; +    size_t n; +#define	LOCALMAT	20 +    regmatch_t mat[LOCALMAT]; +#define	LOCALMEM	40 +    regoff_t mem[LOCALMEM]; + +    /* +     * Sanity checks. +     */ + +    if (re == NULL || string == NULL || re->re_magic != REMAGIC) { +	FreeVars(v); +	return REG_INVARG; +    } +    if (re->re_csize != sizeof(chr)) { +	FreeVars(v); +	return REG_MIXED; +    } + +    /* +     * Setup. +     */ + +    v->re = re; +    v->g = (struct guts *)re->re_guts; +    if ((v->g->cflags®_EXPECT) && details == NULL) { +	FreeVars(v); +	return REG_INVARG; +    } +    if (v->g->info®_UIMPOSSIBLE) { +	FreeVars(v); +	return REG_NOMATCH; +    } +    backref = (v->g->info®_UBACKREF) ? 1 : 0; +    v->eflags = flags; +    if (v->g->cflags®_NOSUB) { +	nmatch = 0;		/* override client */ +    } +    v->nmatch = nmatch; +    if (backref) { +	/* +	 * Need work area. +	 */ + +	if (v->g->nsub + 1 <= LOCALMAT) { +	    v->pmatch = mat; +	} else { +	    v->pmatch = (regmatch_t *) +		    MALLOC((v->g->nsub + 1) * sizeof(regmatch_t)); +	} +	if (v->pmatch == NULL) { +	    FreeVars(v); +	    return REG_ESPACE; +	} +	v->nmatch = v->g->nsub + 1; +    } else { +	v->pmatch = pmatch; +    } +    v->details = details; +    v->start = (chr *)string; +    v->stop = (chr *)string + len; +    v->err = 0; +    if (backref) { +	/* +	 * Need retry memory. +	 */ + +	assert(v->g->ntree >= 0); +	n = (size_t)v->g->ntree; +	if (n <= LOCALMEM) { +	    v->mem = mem; +	} else { +	    v->mem = (regoff_t *) MALLOC(n*sizeof(regoff_t)); +	} +	if (v->mem == NULL) { +	    if (v->pmatch != pmatch && v->pmatch != mat) { +		FREE(v->pmatch); +	    } +	    FreeVars(v); +	    return REG_ESPACE; +	} +    } else { +	v->mem = NULL; +    } + +    /* +     * Do it. +     */ + +    assert(v->g->tree != NULL); +    if (backref) { +	st = complicatedFind(v, &v->g->tree->cnfa, &v->g->cmap); +    } else { +	st = simpleFind(v, &v->g->tree->cnfa, &v->g->cmap); +    } + +    /* +     * Copy (portion of) match vector over if necessary. +     */ + +    if (st == REG_OKAY && v->pmatch != pmatch && nmatch > 0) { +	zapSubexpressions(pmatch, nmatch); +	n = (nmatch < v->nmatch) ? nmatch : v->nmatch; +	memcpy(VS(pmatch), VS(v->pmatch), n*sizeof(regmatch_t)); +    } + +    /* +     * Clean up. +     */ + +    if (v->pmatch != pmatch && v->pmatch != mat) { +	FREE(v->pmatch); +    } +    if (v->mem != NULL && v->mem != mem) { +	FREE(v->mem); +    } +    FreeVars(v); +    return st; +} + +/* + - simpleFind - find a match for the main NFA (no-complications case) + ^ static int simpleFind(struct vars *, struct cnfa *, struct colormap *); + */ +static int +simpleFind( +    struct vars *const v, +    struct cnfa *const cnfa, +    struct colormap *const cm) +{ +    struct dfa *s, *d; +    chr *begin, *end = NULL; +    chr *cold; +    chr *open, *close;		/* Open and close of range of possible +				 * starts */ +    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); +    assert(!(ISERR() && s != NULL)); +    NOERR(); +    MDEBUG(("\nsearch at %ld\n", LOFF(v->start))); +    cold = NULL; +    close = shortest(v, s, v->start, v->start, v->stop, &cold, NULL); +    freeDFA(s); +    NOERR(); +    if (v->g->cflags®_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); +    assert(!(ISERR() && d != NULL)); +    NOERR(); +    for (begin = open; begin <= close; begin++) { +	MDEBUG(("\nfind trying at %ld\n", LOFF(begin))); +	if (shorter) { +	    end = shortest(v, d, begin, begin, v->stop, NULL, &hitend); +	} else { +	    end = longest(v, d, begin, v->stop, &hitend); +	} +	NOERR(); +	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®_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; +    } + +    /* +     * Submatches. +     */ + +    zapSubexpressions(v->pmatch, v->nmatch); +    return dissect(v, v->g->tree, begin, end); +} + +/* + - complicatedFind - find a match for the main NFA (with complications) + ^ static int complicatedFind(struct vars *, struct cnfa *, struct colormap *); + */ +static int +complicatedFind( +    struct vars *const v, +    struct cnfa *const cnfa, +    struct colormap *const cm) +{ +    struct dfa *s, *d; +    chr *cold = NULL; /* silence gcc 4 warning */ +    int ret; + +    s = newDFA(v, &v->g->search, cm, &v->dfa1); +    NOERR(); +    d = newDFA(v, cnfa, cm, &v->dfa2); +    if (ISERR()) { +	assert(d == NULL); +	freeDFA(s); +	return v->err; +    } + +    ret = complicatedFindLoop(v, cnfa, cm, d, s, &cold); + +    freeDFA(d); +    freeDFA(s); +    NOERR(); +    if (v->g->cflags®_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 */ +    } +    return ret; +} + +/* + - complicatedFindLoop - the heart of complicatedFind + ^ static int complicatedFindLoop(struct vars *, struct cnfa *, struct colormap *, + ^	struct dfa *, struct dfa *, chr **); + */ +static int +complicatedFindLoop( +    struct vars *const v, +    struct cnfa *const cnfa, +    struct colormap *const cm, +    struct dfa *const d, +    struct dfa *const s, +    chr **const coldp)		/* where to put coldstart pointer */ +{ +    chr *begin, *end; +    chr *cold; +    chr *open, *close;		/* Open and close of range of possible +				 * starts */ +    chr *estart, *estop; +    int er, hitend; +    int shorter = v->g->tree->flags&SHORTER; + +    assert(d != NULL && s != NULL); +    cold = NULL; +    close = v->start; +    do { +	MDEBUG(("\ncsearch at %ld\n", LOFF(close))); +	close = shortest(v, s, close, close, v->stop, &cold, NULL); +	if (close == NULL) { +	    break;		/* NOTE BREAK */ +	} +	assert(cold != NULL); +	open = cold; +	cold = NULL; +	MDEBUG(("cbetween %ld and %ld\n", LOFF(open), LOFF(close))); +	for (begin = open; begin <= close; begin++) { +	    MDEBUG(("\ncomplicatedFind trying at %ld\n", LOFF(begin))); +	    estart = begin; +	    estop = v->stop; +	    for (;;) { +		if (shorter) { +		    end = shortest(v, d, begin, estart, estop, NULL, &hitend); +		} else { +		    end = longest(v, d, begin, estop, &hitend); +		} +		if (hitend && cold == NULL) { +		    cold = begin; +		} +		if (end == NULL) { +		    break;	/* NOTE BREAK OUT */ +		} + +		MDEBUG(("tentative end %ld\n", LOFF(end))); +		zapSubexpressions(v->pmatch, v->nmatch); +		zapSubtree(v, v->g->tree); +		er = complicatedDissect(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); +		    return er; +		} +		if ((shorter) ? end == estop : end == begin) { +		    break; +		} + +		/* +		 * Go around and try again +		 */ + +		if (shorter) { +		    estart = end + 1; +		} else { +		    estop = end - 1; +		} +	    } +	} +    } while (close < v->stop); + +    *coldp = cold; +    return REG_NOMATCH; +} + +/* + - zapSubexpressions - initialize the subexpression matches to "no match" + ^ static void zapSubexpressions(regmatch_t *, size_t); + */ +static void +zapSubexpressions( +    regmatch_t *const p, +    const size_t n) +{ +    size_t i; + +    for (i = n-1; i > 0; i--) { +	p[i].rm_so = -1; +	p[i].rm_eo = -1; +    } +} + +/* + - zapSubtree - initialize the retry memory of a subtree to zeros + ^ static void zapSubtree(struct vars *, struct subre *); + */ +static void +zapSubtree( +    struct vars *const v, +    struct subre *const t) +{ +    if (t == NULL) { +	return; +    } + +    assert(v->mem != NULL); +    v->mem[t->retry] = 0; +    if (t->op == '(') { +	assert(t->subno > 0); +	v->pmatch[t->subno].rm_so = -1; +	v->pmatch[t->subno].rm_eo = -1; +    } + +    if (t->left != NULL) { +	zapSubtree(v, t->left); +    } +    if (t->right != NULL) { +	zapSubtree(v, t->right); +    } +} + +/* + - subset - set any subexpression relevant to a successful subre + ^ static void subset(struct vars *, struct subre *, chr *, chr *); + */ +static void +subset( +    struct vars *const v, +    struct subre *const sub, +    chr *const begin, +    chr *const end) +{ +    int n = sub->subno; + +    assert(n > 0); +    if ((size_t)n >= v->nmatch) { +	return; +    } + +    MDEBUG(("setting %d\n", n)); +    v->pmatch[n].rm_so = OFF(begin); +    v->pmatch[n].rm_eo = OFF(end); +} + +/* + - dissect - determine subexpression matches (uncomplicated case) + ^ static int dissect(struct vars *, struct subre *, chr *, chr *); + */ +static int			/* regexec return code */ +dissect( +    struct vars *const v, +    struct subre *t, +    chr *const begin,		/* beginning of relevant substring */ +    chr *const end)		/* end of same */ +{ +#ifndef COMPILER_DOES_TAILCALL_OPTIMIZATION +  restart: +#endif +    assert(t != NULL); +    MDEBUG(("dissect %ld-%ld\n", LOFF(begin), LOFF(end))); + +    switch (t->op) { +    case '=':			/* terminal node */ +	assert(t->left == NULL && t->right == NULL); +	return REG_OKAY;	/* no action, parent did the work */ +    case '|':			/* alternation */ +	assert(t->left != NULL); +	return alternationDissect(v, t, begin, end); +    case 'b':			/* back ref -- shouldn't be calling us! */ +	return REG_ASSERT; +    case '.':			/* concatenation */ +	assert(t->left != NULL && t->right != NULL); +	return concatenationDissect(v, t, begin, end); +    case '(':			/* capturing */ +	assert(t->left != NULL && t->right == NULL); +	assert(t->subno > 0); +	subset(v, t, begin, end); +#ifndef COMPILER_DOES_TAILCALL_OPTIMIZATION +	t = t->left; +	goto restart; +#else +	return dissect(v, t->left, begin, end); +#endif +    default: +	return REG_ASSERT; +    } +} + +/* + - concatenationDissect - determine concatenation subexpression matches + - (uncomplicated) + ^ static int concatenationDissect(struct vars *, struct subre *, chr *, chr *); + */ +static int			/* regexec return code */ +concatenationDissect( +    struct vars *const v, +    struct subre *const t, +    chr *const begin,		/* beginning of relevant substring */ +    chr *const end)		/* end of same */ +{ +    struct dfa *d, *d2; +    chr *mid; +    int i; +    int shorter = (t->left->flags&SHORTER) ? 1 : 0; +    chr *stop = (shorter) ? end : begin; + +    assert(t->op == '.'); +    assert(t->left != NULL && t->left->cnfa.nstates > 0); +    assert(t->right != NULL && t->right->cnfa.nstates > 0); + +    d = newDFA(v, &t->left->cnfa, &v->g->cmap, &v->dfa1); +    NOERR(); +    d2 = newDFA(v, &t->right->cnfa, &v->g->cmap, &v->dfa2); +    if (ISERR()) { +	assert(d2 == NULL); +	freeDFA(d); +	return v->err; +    } + +    /* +     * Pick a tentative midpoint. +     */ + +    if (shorter) { +	mid = shortest(v, d, begin, begin, end, NULL, NULL); +    } else { +	mid = longest(v, d, begin, end, NULL); +    } +    if (mid == NULL) { +	freeDFA(d); +	freeDFA(d2); +	return REG_ASSERT; +    } +    MDEBUG(("tentative midpoint %ld\n", LOFF(mid))); + +    /* +     * Iterate until satisfaction or failure. +     */ + +    while (longest(v, d2, mid, end, NULL) != end) { +	/* +	 * That midpoint didn't work, find a new one. +	 */ + +	if (mid == stop) { +	    /* +	     * All possibilities exhausted! +	     */ + +	    MDEBUG(("no midpoint!\n")); +	    freeDFA(d); +	    freeDFA(d2); +	    return REG_ASSERT; +	} +	if (shorter) { +	    mid = shortest(v, d, begin, mid+1, end, NULL, NULL); +	} else { +	    mid = longest(v, d, begin, mid-1, NULL); +	} +	if (mid == NULL) { +	    /* +	     * Failed to find a new one! +	     */ + +	    MDEBUG(("failed midpoint!\n")); +	    freeDFA(d); +	    freeDFA(d2); +	    return REG_ASSERT; +	} +	MDEBUG(("new midpoint %ld\n", LOFF(mid))); +    } + +    /* +     * Satisfaction. +     */ + +    MDEBUG(("successful\n")); +    freeDFA(d); +    freeDFA(d2); +    i = dissect(v, t->left, begin, mid); +    if (i != REG_OKAY) { +	return i; +    } +    return dissect(v, t->right, mid, end); +} + +/* + - alternationDissect - determine alternative subexpression matches (uncomplicated) + ^ static int alternationDissect(struct vars *, struct subre *, chr *, chr *); + */ +static int			/* regexec return code */ +alternationDissect( +    struct vars *const v, +    struct subre *t, +    chr *const begin,		/* beginning of relevant substring */ +    chr *const end)		/* end of same */ +{ +    int i; + +    assert(t != NULL); +    assert(t->op == '|'); + +    for (i = 0; t != NULL; t = t->right, i++) { +	struct dfa *d; + +	MDEBUG(("trying %dth\n", i)); +	assert(t->left != NULL && t->left->cnfa.nstates > 0); +	d = newDFA(v, &t->left->cnfa, &v->g->cmap, &v->dfa1); +	if (ISERR()) { +	    return v->err; +	} +	if (longest(v, d, begin, end, NULL) == end) { +	    MDEBUG(("success\n")); +	    freeDFA(d); +	    return dissect(v, t->left, begin, end); +	} +	freeDFA(d); +    } +    return REG_ASSERT;		/* none of them matched?!? */ +} + +/* + - complicatedDissect - determine subexpression matches (with complications) + * The retry memory stores the offset of the trial midpoint from begin, plus 1 + * so that 0 uniquely means "clean slate". + ^ static int complicatedDissect(struct vars *, struct subre *, chr *, chr *); + */ +static inline int		/* regexec return code */ +complicatedDissect( +    struct vars *const v, +    struct subre *const t, +    chr *const begin,		/* beginning of relevant substring */ +    chr *const end)		/* end of same */ +{ +    assert(t != NULL); +    MDEBUG(("complicatedDissect %ld-%ld %c\n", LOFF(begin), LOFF(end), t->op)); + +    switch (t->op) { +    case '=':			/* terminal node */ +	assert(t->left == NULL && t->right == NULL); +	return REG_OKAY;	/* no action, parent did the work */ +    case '|':			/* alternation */ +	assert(t->left != NULL); +	return complicatedAlternationDissect(v, t, begin, end); +    case 'b':			/* back ref -- shouldn't be calling us! */ +	assert(t->left == NULL && t->right == NULL); +	return complicatedBackrefDissect(v, t, begin, end); +    case '.':			/* concatenation */ +	assert(t->left != NULL && t->right != NULL); +	return complicatedConcatenationDissect(v, t, begin, end); +    case '(':			/* capturing */ +	assert(t->left != NULL && t->right == NULL); +	assert(t->subno > 0); +	return complicatedCapturingDissect(v, t, begin, end); +    default: +	return REG_ASSERT; +    } +} + +static int			/* regexec return code */ +complicatedCapturingDissect( +    struct vars *const v, +    struct subre *const t, +    chr *const begin,		/* beginning of relevant substring */ +    chr *const end)		/* end of same */ +{ +    int er = complicatedDissect(v, t->left, begin, end); + +    if (er == REG_OKAY) { +	subset(v, t, begin, end); +    } +    return er; +} + +/* + - complicatedConcatenationDissect - concatenation subexpression matches (with complications) + * The retry memory stores the offset of the trial midpoint from begin, plus 1 + * so that 0 uniquely means "clean slate". + ^ static int complicatedConcatenationDissect(struct vars *, struct subre *, chr *, chr *); + */ +static int			/* regexec return code */ +complicatedConcatenationDissect( +    struct vars *const v, +    struct subre *const t, +    chr *const begin,		/* beginning of relevant substring */ +    chr *const end)		/* end of same */ +{ +    struct dfa *d, *d2; +    chr *mid; + +    assert(t->op == '.'); +    assert(t->left != NULL && t->left->cnfa.nstates > 0); +    assert(t->right != NULL && t->right->cnfa.nstates > 0); + +    if (t->left->flags&SHORTER) { /* reverse scan */ +	return complicatedReversedDissect(v, t, begin, end); +    } + +    d = newDFA(v, &t->left->cnfa, &v->g->cmap, DOMALLOC); +    if (ISERR()) { +	return v->err; +    } +    d2 = newDFA(v, &t->right->cnfa, &v->g->cmap, DOMALLOC); +    if (ISERR()) { +	freeDFA(d); +	return v->err; +    } +    MDEBUG(("cConcat %d\n", t->retry)); + +    /* +     * Pick a tentative midpoint. +     */ + +    if (v->mem[t->retry] == 0) { +	mid = longest(v, d, begin, end, NULL); +	if (mid == NULL) { +	    freeDFA(d); +	    freeDFA(d2); +	    return REG_NOMATCH; +	} +	MDEBUG(("tentative midpoint %ld\n", LOFF(mid))); +	v->mem[t->retry] = (mid - begin) + 1; +    } else { +	mid = begin + (v->mem[t->retry] - 1); +	MDEBUG(("working midpoint %ld\n", LOFF(mid))); +    } + +    /* +     * Iterate until satisfaction or failure. +     */ + +    for (;;) { +	/* +	 * Try this midpoint on for size. +	 */ + +	if (longest(v, d2, mid, end, NULL) == end) { +	    int er = complicatedDissect(v, t->left, begin, mid); + +	    if (er == REG_OKAY) { +		er = complicatedDissect(v, t->right, mid, end); +		if (er == REG_OKAY) { +		    /* +		     * Satisfaction. +		     */ + +		    MDEBUG(("successful\n")); +		    freeDFA(d); +		    freeDFA(d2); +		    return REG_OKAY; +		} +	    } +	    if ((er != REG_OKAY) && (er != REG_NOMATCH)) { +		freeDFA(d); +		freeDFA(d2); +		return er; +	    } +	} + +	/* +	 * That midpoint didn't work, find a new one. +	 */ + +	if (mid == begin) { +	    /* +	     * All possibilities exhausted. +	     */ + +	    MDEBUG(("%d no midpoint\n", t->retry)); +	    freeDFA(d); +	    freeDFA(d2); +	    return REG_NOMATCH; +	} +	mid = longest(v, d, begin, mid-1, NULL); +	if (mid == NULL) { +	    /* +	     * Failed to find a new one. +	     */ + +	    MDEBUG(("%d failed midpoint\n", t->retry)); +	    freeDFA(d); +	    freeDFA(d2); +	    return REG_NOMATCH; +	} +	MDEBUG(("%d: new midpoint %ld\n", t->retry, LOFF(mid))); +	v->mem[t->retry] = (mid - begin) + 1; +	zapSubtree(v, t->left); +	zapSubtree(v, t->right); +    } +} + +/* + - complicatedReversedDissect - determine backref shortest-first subexpression + - matches + * The retry memory stores the offset of the trial midpoint from begin, plus 1 + * so that 0 uniquely means "clean slate". + ^ static int complicatedReversedDissect(struct vars *, struct subre *, chr *, chr *); + */ +static int			/* regexec return code */ +complicatedReversedDissect( +    struct vars *const v, +    struct subre *const t, +    chr *const begin,		/* beginning of relevant substring */ +    chr *const end)		/* end of same */ +{ +    struct dfa *d, *d2; +    chr *mid; + +    assert(t->op == '.'); +    assert(t->left != NULL && t->left->cnfa.nstates > 0); +    assert(t->right != NULL && t->right->cnfa.nstates > 0); +    assert(t->left->flags&SHORTER); + +    /* +     * Concatenation -- need to split the substring between parts. +     */ + +    d = newDFA(v, &t->left->cnfa, &v->g->cmap, DOMALLOC); +    if (ISERR()) { +	return v->err; +    } +    d2 = newDFA(v, &t->right->cnfa, &v->g->cmap, DOMALLOC); +    if (ISERR()) { +	freeDFA(d); +	return v->err; +    } +    MDEBUG(("cRev %d\n", t->retry)); + +    /* +     * Pick a tentative midpoint. +     */ + +    if (v->mem[t->retry] == 0) { +	mid = shortest(v, d, begin, begin, end, NULL, NULL); +	if (mid == NULL) { +	    freeDFA(d); +	    freeDFA(d2); +	    return REG_NOMATCH; +	} +	MDEBUG(("tentative midpoint %ld\n", LOFF(mid))); +	v->mem[t->retry] = (mid - begin) + 1; +    } else { +	mid = begin + (v->mem[t->retry] - 1); +	MDEBUG(("working midpoint %ld\n", LOFF(mid))); +    } + +    /* +     * Iterate until satisfaction or failure. +     */ + +    for (;;) { +	/* +	 * Try this midpoint on for size. +	 */ + +	if (longest(v, d2, mid, end, NULL) == end) { +	    int er = complicatedDissect(v, t->left, begin, mid); + +	    if (er == REG_OKAY) { +		er = complicatedDissect(v, t->right, mid, end); +		if (er == REG_OKAY) { +		    /* +		     * Satisfaction. +		     */ + +		    MDEBUG(("successful\n")); +		    freeDFA(d); +		    freeDFA(d2); +		    return REG_OKAY; +		} +	    } +	    if (er != REG_OKAY && er != REG_NOMATCH) { +		freeDFA(d); +		freeDFA(d2); +		return er; +	    } +	} + +	/* +	 * That midpoint didn't work, find a new one. +	 */ + +	if (mid == end) { +	    /* +	     * All possibilities exhausted. +	     */ + +	    MDEBUG(("%d no midpoint\n", t->retry)); +	    freeDFA(d); +	    freeDFA(d2); +	    return REG_NOMATCH; +	} +	mid = shortest(v, d, begin, mid+1, end, NULL, NULL); +	if (mid == NULL) { +	    /* +	     * Failed to find a new one. +	     */ + +	    MDEBUG(("%d failed midpoint\n", t->retry)); +	    freeDFA(d); +	    freeDFA(d2); +	    return REG_NOMATCH; +	} +	MDEBUG(("%d: new midpoint %ld\n", t->retry, LOFF(mid))); +	v->mem[t->retry] = (mid - begin) + 1; +	zapSubtree(v, t->left); +	zapSubtree(v, t->right); +    } +} + +/* + - complicatedBackrefDissect - determine backref subexpression matches + ^ static int complicatedBackrefDissect(struct vars *, struct subre *, chr *, chr *); + */ +static int			/* regexec return code */ +complicatedBackrefDissect( +    struct vars *const v, +    struct subre *const t, +    chr *const begin,		/* beginning of relevant substring */ +    chr *const end)		/* end of same */ +{ +    int i, n = t->subno, min = t->min, max = t->max; +    chr *paren, *p, *stop; +    size_t len; + +    assert(t != NULL); +    assert(t->op == 'b'); +    assert(n >= 0); +    assert((size_t)n < v->nmatch); + +    MDEBUG(("cbackref n%d %d{%d-%d}\n", t->retry, n, min, max)); + +    if (v->pmatch[n].rm_so == -1) { +	return REG_NOMATCH; +    } +    paren = v->start + v->pmatch[n].rm_so; +    len = v->pmatch[n].rm_eo - v->pmatch[n].rm_so; + +    /* +     * No room to maneuver -- retries are pointless. +     */ + +    if (v->mem[t->retry]) { +	return REG_NOMATCH; +    } +    v->mem[t->retry] = 1; + +    /* +     * Special-case zero-length string. +     */ + +    if (len == 0) { +	if (begin == end) { +	    return REG_OKAY; +	} +	return REG_NOMATCH; +    } + +    /* +     * And too-short string. +     */ + +    assert(end >= begin); +    if ((size_t)(end - begin) < len) { +	return REG_NOMATCH; +    } +    stop = end - len; + +    /* +     * Count occurrences. +     */ + +    i = 0; +    for (p = begin; p <= stop && (i < max || max == INFINITY); p += len) { +	if (v->g->compare(paren, p, len) != 0) { +	    break; +	} +	i++; +    } +    MDEBUG(("cbackref found %d\n", i)); + +    /* +     * And sort it out. +     */ + +    if (p != end) {		/* didn't consume all of it */ +	return REG_NOMATCH; +    } +    if (min <= i && (i <= max || max == INFINITY)) { +	return REG_OKAY; +    } +    return REG_NOMATCH;		/* out of range */ +} + +/* + - complicatedAlternationDissect - determine alternative subexpression matches (w. + - complications) + ^ static int complicatedAlternationDissect(struct vars *, struct subre *, chr *, chr *); + */ +static int			/* regexec return code */ +complicatedAlternationDissect( +    struct vars *const v, +    struct subre *t, +    chr *const begin,		/* beginning of relevant substring */ +    chr *const end)		/* end of same */ +{ +    int er; +#define	UNTRIED	0		/* not yet tried at all */ +#define	TRYING	1		/* top matched, trying submatches */ +#define	TRIED	2		/* top didn't match or submatches exhausted */ + +#ifndef COMPILER_DOES_TAILCALL_OPTIMIZATION +    if (0) { +    doRight: +	t = t->right; +    } +#endif +    if (t == NULL) { +	return REG_NOMATCH; +    } +    assert(t->op == '|'); +    if (v->mem[t->retry] == TRIED) { +	goto doRight; +    } + +    MDEBUG(("cAlt n%d\n", t->retry)); +    assert(t->left != NULL); + +    if (v->mem[t->retry] == UNTRIED) { +	struct dfa *d = newDFA(v, &t->left->cnfa, &v->g->cmap, DOMALLOC); + +	if (ISERR()) { +	    return v->err; +	} +	if (longest(v, d, begin, end, NULL) != end) { +	    freeDFA(d); +	    v->mem[t->retry] = TRIED; +	    goto doRight; +	} +	freeDFA(d); +	MDEBUG(("cAlt matched\n")); +	v->mem[t->retry] = TRYING; +    } + +    er = complicatedDissect(v, t->left, begin, end); +    if (er != REG_NOMATCH) { +	return er; +    } + +    v->mem[t->retry] = TRIED; +#ifndef COMPILER_DOES_TAILCALL_OPTIMIZATION +    goto doRight; +#else +  doRight: +    return complicatedAlternationDissect(v, t->right, begin, end); +#endif +} + +#include "rege_dfa.c" + +/* + * Local Variables: + * mode: c + * c-basic-offset: 4 + * fill-column: 78 + * End: + */ | 
