/*
 * Changes by Gunnar Ritter, Freiburg i. Br., Germany, November 2002.
 *
 * Sccsid @(#)regnfa.c	1.8 (gritter) 2/6/05
 */
/*  UNIX(R) Regular Expresssion Library
 *
 *  Note: Code is released under the GNU LGPL
 *
 *  Copyright (C) 2001 Caldera International, Inc.
 *
 *  This library is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU Lesser General Public
 *  License as published by the Free Software Foundation; either
 *  version 2 of the License, or (at your option) any later version.
 *
 *  This library is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *  Lesser General Public License for more details.
 *
 *  You should have received a copy of the GNU Lesser General Public
 *  License along with this library; if not, write to:
 *        Free Software Foundation, Inc.
 *        59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

/*	#include "synonyms.h"	*/
#include <string.h>
#include <stdlib.h>
#include "re.h"
#include <stddef.h>
#include <ctype.h>

typedef unsigned char	Uchar;
typedef unsigned short	Ushort;

/*
* Nondeterministic Finite Automata.
*/
typedef struct t_graph	Graph;
struct t_graph
{
	union
	{
		Graph	*ptr;
		Info	info;
	} alt;
	Graph		*next;
	w_type		op;
};

typedef struct t_stack	Stack;
struct t_stack
{
	Stack		*link;	/* simplifies cleanup */
	Stack		*prev;	/* covered states */
	Graph		*wasgp;	/* node associated with this state */
	const Uchar	*str;	/* saved position in the string */
	Ushort		cnt;	/* ROP_BRACE: traversal count */
};

	/*
	* A Context holds all the information needed for each
	* potential path through the NFA graph.
	*/
typedef struct t_ctxt	Context;
struct t_ctxt
{
	Context		*link;	/* simplifies cleanup */
	Context		*next;	/* singly linked */
	Stack		*sp;	/* nested counts */
	Graph		*gp;	/* starting node */
	Graph		*wasgp;	/* node associated with this state */
	const Uchar	*str;	/* saved position in the string */
	Ushort		cnt;	/* ROP_BRACE: traversal count */
	size_t		nset;	/* length of rm[] that is currently set */
	regmatch_t	rm[1];	/* enough to cover re_nsub+1 (np->rmlen) */
};

struct re_nfa_ /*Nfa*/
{
	Graph	*gp;	/* entire NFA */
	Stack	*sp;	/* unused Stacks */
	Stack	*allsp;	/* linked Stacks (for cleanup) */
	Context	*allcp;	/* linked Contexts (for cleanup) */
	Context	*cur;	/* Contexts to be continued now */
	Context	*step;	/* Contexts waiting for a step of the NFA */
	Context	*avail;	/* unused Contexts */
	Context	**ecur;	/* ends cur list of Contexts */
	Context	**estp;	/* ends step list of Contexts */
	size_t	rmlen;	/* length of rm[] in each Context */
	size_t	rmmin;	/* minimum length needed */
	size_t	used;	/* length used for this libuxre_regnfaexec() */
	w_type	beg;	/* nonzero for fixed char initial node NFAs */
};

#define ROP_MTOR	ROP_CAT	/* ROP_OR, except might be empty loop */

	/*
	* Depth first traversal.
	* Make a singly linked list (in alt.ptr) of the graph's nodes.
	* Must toss any ROP_BKTs, too, since "alt" is overwritten.
	*/
static void
deltolist(Graph *gp, Graph **list)
{
	Graph *ptr;

	if ((ptr = gp->next) != 0) /* first time */
	{
		gp->next = 0;
		if (gp->op == ROP_OR || gp->op == ROP_MTOR)
			deltolist(gp->alt.ptr, list);
		deltolist(ptr, list);
		if (gp->op == ROP_BKT)
		{
			libuxre_bktfree(gp->alt.info.bkt);
			free(gp->alt.info.bkt);
		}
	}
	else if (gp->op == ROP_END)
		gp->op = ROP_NOP;
	else
		return;
	gp->alt.ptr = *list;
	*list = gp;
}

	/*
	* After the list is turned into a linked list,
	* walk that list freeing the nodes.
	*/
static void
delgraph(Graph *gp)
{
	Graph *gp2, end;

	gp2 = &end;
	deltolist(gp, &gp2);
	while ((gp = gp2) != &end)
	{
		gp2 = gp->alt.ptr;
		free(gp);
	}
}

	/*
	* Depth first traversal.
	* Look for ROP_NOPs and prune them from the graph.
	* Chain them all together on *nop's list.
	*/
static Graph *
nopskip(Graph *gp, Graph **nop)
{
	Graph *ptr;

	if ((ptr = gp->next) != 0) /* might have yet to do this subgraph */
	{
		if (gp->op == ROP_NOP)
		{
			if (gp->alt.ptr != 0) /* touched */
				return gp->next; /* already did it */
			gp->alt.ptr = *nop;
			*nop = gp;
		}
		gp->next = 0; /* this subgraph's pending */
		if (gp->op == ROP_OR || gp->op == ROP_MTOR)
			gp->alt.ptr = nopskip(gp->alt.ptr, nop);
		gp->next = nopskip(ptr, nop);
		if (gp->op == ROP_NOP)
			return gp->next;
	}
	return gp;
}

	/*
	* Postorder traversal of the parse tree.
	* Build a graph using "Thompson's" algorithm.
	* The only significant modification is the
	* ROP_BRACE->ROP_MTOR construction.
	* Returns 1 => graph might match empty
	*	  0 => graph cannot match empty
	*	 -1 => error (in allocation)
	*/
static int
mkgraph(Tree *tp, Graph **first, Graph **last)
{
	Graph *new = 0, *nop, *lf, *ll, *rf, *rl;
	int lmt, rmt = 0;

	if (tp->op != ROP_CAT)
	{
		if ((new = malloc(sizeof(Graph))) == 0)
			return 0;
		new->op = tp->op;	/* usually */
	}
	switch (tp->op)
	{
	case ROP_REF:
		new->alt.info.sub = tp->right.info.sub;
		*first = new;
		*last = new;
		return 1; /* safe--can't really tell */
	case ROP_BKT:
		tp->op = ROP_BKTCOPY;	/* now graph owns clean up */
		/*FALLTHROUGH*/
	case ROP_BKTCOPY:
		new->alt.info.bkt = tp->right.info.bkt;
		/*FALLTHROUGH*/
	default:
		*first = new;
		*last = new;
		return 0;
	case ROP_EMPTY:
		new->op = ROP_NOP;
		new->alt.ptr = 0;	/* untouched */
		*first = new;
		*last = new;
		return 1;
	case ROP_OR:
	case ROP_CAT:
		lf = 0;	/* in case of error */
		if ((rmt = mkgraph(tp->right.ptr, &rf, &rl)) < 0)
			goto err;
		/*FALLTHROUGH*/
	case ROP_STAR:
	case ROP_PLUS:
	case ROP_QUEST:
	case ROP_BRACE:
	case ROP_LP:
		if ((lmt = mkgraph(tp->left.ptr, &lf, &ll)) < 0)
			goto err;
		break;
	}
	/*
	* Note that ROP_NOP only serves as the node that reconnects
	* the two choices of an incoming ROP_OR or ROP_QUEST.  To
	* prevent rewalking portions of the graph in nopskip(),
	* this code marks all ROP_NOP nodes as currently untouched.
	*/
	switch (tp->op)
	{
	case ROP_OR:
		if ((nop = malloc(sizeof(Graph))) == 0)
			goto err;
		nop->op = ROP_NOP;
		nop->alt.ptr = 0;	/* untouched */
		ll->next = nop;
		rl->next = nop;
		new->next = lf;
		new->alt.ptr = rf;
		*first = new;
		*last = nop;
		return lmt | rmt;
	case ROP_CAT:	/* no "new" */
		ll->next = rf;
		*first = lf;
		*last = rl;
		return lmt & rmt;
	case ROP_QUEST:
		if ((nop = malloc(sizeof(Graph))) == 0)
			goto err;
		nop->op = ROP_NOP;
		nop->alt.ptr = 0;	/* untouched */
		new->op = ROP_OR;
		new->next = lf;
		new->alt.ptr = nop;
		ll->next = nop;
		*first = new;
		*last = nop;
		return 1;
	case ROP_STAR:
		*first = new;
		rmt = 1;
	star:;
		new->op = lmt ? ROP_MTOR : ROP_OR;
		new->alt.ptr = lf;
		ll->next = new;
		*last = new;
		return rmt;
	case ROP_PLUS:
		*first = lf;
		rmt = lmt;
		goto star;
	case ROP_BRACE:
		if ((nop = malloc(sizeof(Graph))) == 0)
			goto err;
		nop->op = ROP_MTOR; /* going to save state anyway... */
		nop->alt.ptr = lf;
		ll->next = new;
		new->next = nop;
		new->alt.info.num[1] = tp->right.info.num[1];
		if ((new->alt.info.num[0] = tp->right.info.num[0]) == 0)
		{
			lmt = 1;
			*first = new;
		}
		else
		{
			new->alt.info.num[0]--;	/* already done 1 */
			if (new->alt.info.num[1] != BRACE_INF)
				new->alt.info.num[1]--;	/* likewise */
			*first = lf;
		}
		*last = nop;
		return lmt;
	case ROP_LP:
		if ((nop = malloc(sizeof(Graph))) == 0)
			goto err;
		nop->op = ROP_RP;
		nop->alt.info.sub = tp->right.info.sub;
		new->alt.info.sub = tp->right.info.sub;
		new->next = lf;
		ll->next = nop;
		*first = new;
		*last = nop;
		return lmt;
	}
err:;
	if (KIND_ROP(tp->op) == BINARY_ROP && rf != 0)
		delgraph(rf);
	if (lf != 0)
		delgraph(lf);
	if (tp->op != ROP_CAT)
		free(new);
	return -1;
}

	/*
	* Semi-preorder traversal.
	* Return zero if there's no simple first character
	* (including the operation ROP_BOL) that must always
	* be at the start of a matching string.
	* This code doesn't attempt to get an answer if the
	* first of the tree many be empty.
	*/
static w_type
firstop(Tree *tp)
{
	w_type op;

	switch (tp->op)
	{
	case ROP_OR:
		if ((op = firstop(tp->left.ptr)) == 0
			|| op != firstop(tp->right.ptr))
		{
			return 0;
		}
		return op;
	case ROP_BRACE:
		if (tp->right.info.num[0] == 0)
			return 0;
		/*FALLTHROUGH*/
	case ROP_CAT:
	case ROP_PLUS:
	case ROP_LP:
		return firstop(tp->left.ptr);
	default:
		if (tp->op < 0)
			return 0;
		/*FALLTHROUGH*/
	case ROP_BOL:
		return tp->op;
	}
}

void
libuxre_regdelnfa(Nfa *np)
{
	Context *cp, *cpn;
	Stack *sp, *spn;

	if (np->gp != 0)
		delgraph(np->gp);
	for (cp = np->allcp; cp != 0; cp = cpn)
	{
		cpn = cp->link;
		free(cp);
	}
	for (sp = np->allsp; sp != 0; sp = spn)
	{
		spn = sp->link;
		free(sp);
	}
	free(np);
}

LIBUXRE_STATIC int
libuxre_regnfacomp(regex_t *ep, Tree *tp, Lex *lxp)
{
	Graph *gp, end;
	Nfa *np;

	if ((np = malloc(sizeof(Nfa))) == 0)
		goto err;
	np->gp = 0; /* in case of error */
	if (mkgraph(tp, &np->gp, &gp) < 0)
		goto err;
	gp->next = 0;	/* nothing follows ROP_END */
	np->rmlen = 0;
	if ((ep->re_flags & REG_NOSUB) == 0)
		np->rmlen = ep->re_nsub + 1;
	np->rmmin = 0;
	if (lxp->maxref != 0 && (np->rmmin = lxp->maxref + 1) > np->rmlen)
		np->rmlen = np->rmmin;
	/*
	* Delete all ROP_NOPs from the graph.
	* nopskip() disconnects them from the graph and
	* links them together through their alt.ptr's.
	*/
	gp = &end;
	np->gp = nopskip(np->gp, &gp);
	while (gp != &end)
	{
		Graph *gp2 = gp;

		gp = gp->alt.ptr;
		free(gp2);
	}
	np->sp = 0;
	np->allsp = 0;
	np->avail = 0;
	np->allcp = 0;
	ep->re_nfa = np;
	np->beg = firstop(tp); 
	return 0;
err:;
	if (np != 0)
	{
		if (np->gp != 0)
			delgraph(np->gp);
		free(np);
	}
	return REG_ESPACE;
}

static Stack *
newstck(Nfa *np)
{
	Stack *sp, **spp;
	int i;

	if ((sp = np->sp) == 0)	/* get more */
	{
		spp = &np->sp;
		i = 4;
		while ((sp = malloc(sizeof(Stack))) != 0)
		{
			sp->link = np->allsp;
			np->allsp = sp;
			*spp = sp;
			spp = &sp->prev;
			if (--i == 0)
				break;
		}
		*spp = 0;
		if ((sp = np->sp) == 0)	/* first malloc failed */
			return 0;
	}
	np->sp = sp->prev;
	return sp;
}

static int
mkstck(Nfa *np, Context *cp, Graph *gp)
{
	Stack *new, *sp;

	if (gp == 0)	/* copy existing stack tail */
	{
		/*
		* Hoist up top of stack.
		*/
		new = cp->sp;
		cp->wasgp = new->wasgp;
		cp->str = new->str;
		cp->cnt = new->cnt;
		cp->sp = new->prev;
		if ((sp = new->prev) == 0) /* only one below */
		{
			new->prev = np->sp;
			np->sp = new;
			cp->sp = 0;
			return 0;
		}
		for (;;) /* copy the rest; reusing the old top */
		{
			new->wasgp = sp->wasgp;
			new->str = sp->str;
			new->cnt = sp->cnt;
			if ((new->prev = sp->prev) == 0)
				break;
			if ((new->prev = newstck(np)) == 0)
				return REG_ESPACE;
			new = new->prev;
			sp = sp->prev;
		}
		return 0;
	}
	if (cp->wasgp != 0)	/* push current down */
	{
		if ((new = newstck(np)) == 0)
			return REG_ESPACE;
		new->prev = cp->sp;
		cp->sp = new;
		new->wasgp = cp->wasgp;
		new->str = cp->str;
		new->cnt = cp->cnt;
	}
	cp->wasgp = gp;
	cp->str = 0;
	cp->cnt = 0;
	return 0;
}

	/*
	* Allocate a new Context (from np->avail)
	* and add it to the end of the current list.
	*/
static int
newctxt(Nfa *np, Context *cp, Graph *gp)
{
	Context *new;
	size_t n;

	if ((new = np->avail) == 0)	/* need more */
	{
		Context *ncp, **cpp;
		int i;

		/*
		* Can't easily allocate Contexts in one call because
		* the alignments (given the varying length of rm[])
		* are potentially nontrivial.
		*/
		n = offsetof(Context, rm) + np->rmlen * sizeof(regmatch_t);
		i = 4;
		cpp = &np->avail;
		while ((ncp = malloc(n)) != 0)
		{
			ncp->link = np->allcp;
			np->allcp = ncp;
			*cpp = ncp;
			cpp = &ncp->next;
			if (--i == 0)
				break;
		}
		*cpp = 0;
		if ((new = np->avail) == 0)	/* first malloc failed */
			return REG_ESPACE;
	}
	np->avail = new->next;
	new->next = 0;
	new->gp = gp;
	new->sp = 0;
	new->wasgp = 0;
	new->nset = 0;
	if (cp != 0) /* copy existing context information */
	{
		if (cp->sp != 0) /* copy tail of stack */
		{
			new->sp = cp->sp;
			if (mkstck(np, new, 0) != 0)
				return REG_ESPACE;
		}
		new->wasgp = cp->wasgp;
		new->str = cp->str;
		new->cnt = cp->cnt;
		/*
		* Copy any valid subexpression match information
		* from the existing context.
		*/
		if (np->used != 0 && (n = cp->nset) != 0)
		{
			regmatch_t *rmn = new->rm, *rmo = cp->rm;

			new->nset = n;
			for (;; ++rmn, ++rmo)
			{
				rmn->rm_so = rmo->rm_so;
				rmn->rm_eo = rmo->rm_eo;
				if (--n == 0)
					break;
			}
		}
	}
	/*
	* Append it to the end of the current Context list.
	*/
	*np->ecur = new;
	np->ecur = &new->next;
	return 0;
}

	/*
	* Compare two byte string sequences for equality.
	* If REG_ICASE, walk through the strings doing
	* caseless comparisons of the wide characters.
	*/
static int
casecmp(const Uchar *s, Exec *xp, ssize_t i, ssize_t n, int mb_cur_max)
{
	const Uchar *p = &xp->str[i];
	const Uchar *end;
	w_type wc1, wc2;
	int k;

	if (strncmp((char *)s, (char *)p, n) == 0) /* try for exact match */
		return 1;
	if ((xp->flags & REG_ICASE) == 0)
		return 0;
	/*
	* Walk through each testing for a match, ignoring case,
	* of the resulting wide characters.
	* Note that only "s" can run out of characters.
	*/
	end = &p[n];
	do
	{
		if ((wc1 = *s++) == '\0')
			return 0;
		if (!ISONEBYTE(wc1) && (k = libuxre_mb2wc(&wc1, s)) > 0)
			s += k;
		if (!ISONEBYTE(wc2 = *p++) && (k = libuxre_mb2wc(&wc2, p)) > 0)
			p += k;
		if (wc1 != wc2)
		{
			wc1 = to_lower(wc1);
			wc2 = to_lower(wc2);
			if (wc1 != wc2)
				return 0;
		}
	} while (p < end);
	return 1;
}

LIBUXRE_STATIC int
libuxre_regnfaexec(Nfa *np, Exec *xp)
{
	const Uchar *s, *s1, *s2;
	Context *cp, *cpn;
	Graph *gp, *brace;
	Stack *sp, *spn;
	ssize_t rmso, len;
	int i, ret, mb_cur_max;
	w_type wc;
	size_t n;

	ret = 0;	/* assume it matches */
	rmso = -1;	/* but no match yet */
	np->cur = 0;
	np->step = 0;
	np->ecur = &np->cur;
	np->estp = &np->step;
	if ((np->used = xp->nmatch) < np->rmmin)
		np->used = np->rmmin;
	s1 = 0;		/* one char back */
	s = xp->str;	/* current high water in string */
	mb_cur_max = xp->mb_cur_max;
	for (;;)
	{
		/*
		* Get next character from string.
		* If the engine proper hasn't started and the engine
		* requires a particular character to start and this
		* character isn't it, try the next one.
		*/
		for (;;)
		{
			s2 = s1;
			s1 = s;
			if (!ISONEBYTE(wc = *s++) &&
					(i = libuxre_mb2wc(&wc, s)) > 0)
				s += i;
			if (np->cur != 0 || np->beg == wc || np->beg == 0)
				break;
			if (np->beg == ROP_BOL)
			{
				if (s2 == 0 && (xp->flags & REG_NOTBOL) == 0)
					break;
				if ((xp->flags & REG_NEWLINE) == 0)
					goto nomatch;
				if (s2 != 0 && *s2 == '\n')
					break;
			}
			if (wc == '\0')
				goto nomatch;
		}
		/*
		* Start the engine by inserting a fresh initial context
		* if there's no known match as yet.  (Once some match
		* has been found, the end is near.)
		*/
		if (rmso < 0 && newctxt(np, 0, np->gp) != 0)
			goto err;
		/*
		* Walk the current Contexts list, trying each.
		* "loop" is when a new Context is to be tried,
		* "again" is when the same Context continues,
		* but wc was not yet matched.
		*/
		cp = np->cur;
	loop:;
		gp = cp->gp;
	again:;
		switch (gp->op)
		{
		case ROP_BRACE: /* gp->next->op == ROP_MTOR */
			brace = gp;
			gp = gp->next;
			goto mtor;
		case ROP_MTOR:
			brace = 0;
		mtor:;
			if (cp->wasgp != gp) /* first time */
			{
				if (mkstck(np, cp, gp) != 0)
					goto err;
			}
			else if (cp->str == s) /* spinning */
				goto poptonext;
			cp->str = s;
			if (brace != 0)
			{
				if (cp->cnt >= brace->alt.info.num[1])
					goto poptonext;
				if (++cp->cnt <= brace->alt.info.num[0])
				{
					gp = gp->alt.ptr;
					goto again;
				}
				if (cp->cnt > BRACE_MAX)
					cp->cnt = BRACE_MAX;
			}
			if (newctxt(np, cp, gp->alt.ptr) != 0)
				goto err;
		poptonext:;
			cp->wasgp = 0;
			if ((sp = cp->sp) != 0) /* pop stack */
			{
				cp->sp = sp->prev;
				cp->wasgp = sp->wasgp;
				cp->str = sp->str;
				cp->cnt = sp->cnt;
				sp->prev = np->sp;
				np->sp = sp;
			}
			/*FALLTHROUGH*/
		case ROP_EMPTY:
		tonext:;
			gp = gp->next;
			goto again;
		case ROP_OR:
			if (newctxt(np, cp, gp->alt.ptr) != 0)
				goto err;
			goto tonext;
		case ROP_LP:
			if ((n = gp->alt.info.sub) < np->used)
			{
				size_t k;

				cp->rm[n].rm_so = s1 - xp->str;
				cp->rm[n].rm_eo = -1;
				/*
				* Mark any skipped subexpressions as
				* failing to participate in the match.
				*/
				if ((k = cp->nset) < n)
				{
					regmatch_t *rmp = &cp->rm[k];

					for (;; rmp++)
					{
						rmp->rm_so = -1;
						rmp->rm_eo = -1;
						if (++k >= n)
							break;
					}
				}
				cp->nset = n + 1;
			}
			goto tonext;
		case ROP_RP:
			if ((n = gp->alt.info.sub) < np->used)
				cp->rm[n].rm_eo = s1 - xp->str;
			goto tonext;
		case ROP_BOL:
			if (s2 == 0)
			{
				if (xp->flags & REG_NOTBOL)
					goto failed;
			}
			else if ((xp->flags & REG_NEWLINE) == 0 || *s2 != '\n')
				goto failed;
			goto tonext;
		case ROP_EOL:
			if (wc == '\0')
			{
				if (xp->flags & REG_NOTEOL)
					goto failed;
			}
			else if ((xp->flags & REG_NEWLINE) == 0 || wc != '\n')
				goto failed;
			goto tonext;
		default:	/* character match */
			if (gp->op != wc)
			{
				if ((xp->flags & REG_ICASE) == 0
					|| gp->op != to_lower(wc))
				{
					goto failed;
				}
			}
		nextwc:;
			cp->gp = gp->next;
		tostep:;
			cpn = cp->next;
			cp->next = 0;
			*np->estp = cp;
			np->estp = &cp->next;
			if ((cp = cpn) == 0)
				break;
			goto loop;
		case ROP_NOTNL:
			if (wc == '\n')
				goto failed;
			/*FALLTHROUGH*/
		case ROP_ANYCH:
			if (wc > '\0')
				goto nextwc;
			/*FALLTHROUGH*/
		case ROP_NONE:
		failed:;
			cpn = cp->next;
			cp->next = np->avail;
			np->avail = cp;
			if ((cp = cpn) == 0)
				break;
			goto loop;
		case ROP_LT:
			if (s2 == 0)
			{
				if (xp->flags & REG_NOTBOL)
					goto failed;
			}
			else
			{
				w_type pwc;

				if (wc != '_' &&
				    !iswalnum(mb_cur_max == 1 ? btowc(wc) : wc))
					goto failed;
				if (!ISONEBYTE(pwc = *s2))
					libuxre_mb2wc(&pwc, &s2[1]);
				if (pwc == '_' ||
				    iswalnum(mb_cur_max== 1 ? btowc(pwc) : pwc))
					goto failed;
			}
			goto tonext;
		case ROP_GT:
			if (wc == '_' ||
			    iswalnum(mb_cur_max == 1 ? btowc(wc) : wc))
				goto failed;
			goto tonext;
		case ROP_BKT:
		case ROP_BKTCOPY:
			if (cp->wasgp == gp) /* rest of MCCE */
			{
			checkspin:;
				if (s1 >= cp->str) /* got it all */
					goto poptonext;
				goto tostep;
			}
			if ((i = libuxre_bktmbexec(gp->alt.info.bkt, wc, s,
							mb_cur_max)) < 0)
				goto failed;
			if ((n = i) == 0)	/* only matched wc */
				goto nextwc;
		spin:;
			if (mkstck(np, cp, gp) != 0)
				goto err;
			cp->gp = gp;	/* stay here until reach past s+n */
			cp->str = s + n;
			goto tostep;
		case ROP_REF:
			if (cp->wasgp == gp)	/* rest of matched string */
				goto checkspin;
			if ((n = gp->alt.info.sub) >= cp->nset)
				goto failed;
			if ((len = cp->rm[n].rm_eo) < 0)
				goto failed;
			if ((len -= n = cp->rm[n].rm_so) == 0)
				goto tonext;
			if (casecmp(s1, xp, n, len, mb_cur_max) == 0)
				goto failed;
			if ((n = s - s1) >= len)
				goto nextwc;
			n = len - n;
			goto spin;
		case ROP_END:	/* success! */
			if (xp->flags & REG_NONEMPTY)
			{
				if (s2 == 0)
					goto failed;
			}
			if (xp->nmatch == 0)
				goto match;
			/*
			* Mark any skipped subexpressions as failing to match.
			*/
			if ((n = cp->nset) < xp->nmatch)
			{
				do
				{
					cp->rm[n].rm_so = -1;
					cp->rm[n].rm_eo = -1;
				} while (++n < xp->nmatch);
			}
			/*
			* Note the left-most match that's longest.
			*/
			n = cp->rm[0].rm_so;
			if (rmso < 0 || n < rmso)
			{
				rmso = n;
			record:;
				memcpy(xp->match, cp->rm,
					xp->nmatch * sizeof(regmatch_t));
				goto failed;
			}
			if (rmso < n || xp->match[0].rm_eo > cp->rm[0].rm_eo)
				goto failed;
			if (xp->match[0].rm_eo < cp->rm[0].rm_eo)
				goto record;
#if 0 /* maximize the lengths of earlier LP...RPs */
			/*
			* If both are of the same length and start
			* at the same point, choose the one with
			* a "longest submatch from left to right"
			* where an empty string wins over a nonmatch.
			*/
			for (n = 1; n < xp->nmatch; n++)
			{
				ssize_t nlen;

				/*
				* First, go with the choice that has any
				* match for subexpr n.
				*/
				len = xp->match[n].rm_eo;
				nlen = cp->rm[n].rm_eo;
				if (nlen < 0)
				{
					if (len >= 0)
						break;
				}
				else if (len < 0)
					goto record;
				/*
				* Both have a match; go with the longer.
				*/
				len -= xp->match[n].rm_so;
				nlen -= cp->rm[n].rm_so;
				if (nlen < len)
					break;
				if (nlen > len)
					goto record;
			}
#else /* take LP and RP as "fence posts" and maximize earlier gaps */
			/*
			* If both are of the same length and start
			* at the same point, choose the one with
			* the larger earlier subpatterns, in which
			* each rm_so and rm_eo serves as a separator.
			*/
			for (n = 1; n < xp->nmatch; n++)
			{
				ssize_t nlen;
				int	use;

				if (xp->flags & REG_AVOIDNULL) {
					/*
					* This is to to satisfy POSIX.1-2001
					* XBD pp. 172-173 ll. 6127-6129, whose
					* translation is "do not match null
					* expressions if there is a choice".
					* See also POSIX.2 interpretation #43
					* in which the question was raised.
					*
					* The first subexpression of "\(x*\)*"
					* must thus match the string "xxx".
					*/
					use = cp->rm[n].rm_eo -
							cp->rm[n].rm_so >=
						xp->match[n].rm_eo -
							xp->match[n].rm_so ||
						xp->match[n].rm_so < 0;
				} else
					use = 1;
				/*
				* Choose the rightmost ROP_LP as that
				* maximizes the gap from before.
				*/
				len = xp->match[n].rm_so;
				nlen = cp->rm[n].rm_so;
				if (len < nlen && use)
					goto record;
				if (len > nlen)
					break;
				/*
				* The ROP_LPs are at the same point:
				* Choose the rightmost ROP_RP.
				*/
				len = xp->match[n].rm_eo;
				nlen = cp->rm[n].rm_eo;
				if (len < nlen && use)
					goto record;
				if (len > nlen)
					break;
			}
#endif
			goto failed;
		}
		/*
		* Finished the current Context list.  If the input string
		* has been entirely scanned, we're done.  Otherwise, make
		* the next step list current for the next character.
		* If the next step list was empty and there's an existing
		* match, that's the left-most longest.
		*/
		if (wc == '\0')
		{
			if (rmso >= 0)
				goto match;
			goto nomatch;
		}
		np->ecur = np->estp;
		if ((np->cur = np->step) == 0)
		{
			if (rmso >= 0)
				goto match;
			np->ecur = &np->cur; /* was pointing at step */
		}
		np->step = 0;
		np->estp = &np->step;
	}
nomatch:;
	ret = REG_NOMATCH;
match:;
	np->avail = 0;
	for (cp = np->allcp; cp != 0; cp = cpn)
	{
		cpn = cp->link;
		cp->next = np->avail;
		np->avail = cp;
	}
	np->sp = 0;
	for (sp = np->allsp; sp != 0; sp = spn)
	{
		spn = sp->link;
		sp->prev = np->sp;
		np->sp = sp;
	}
	return ret;
err:;
	ret = REG_ESPACE;
	goto match;
}


syntax highlighted by Code2HTML, v. 0.9.1