640 lines
		
	
	
		
			18 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			640 lines
		
	
	
		
			18 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /*
 | |
|     Copyright (c) 2006-2020 Chung, Hyung-Hwan. All rights reserved.
 | |
| 
 | |
|     Redistribution and use in source and binary forms, with or without
 | |
|     modification, are permitted provided that the following conditions
 | |
|     are met:
 | |
|     1. Redistributions of source code must retain the above copyright
 | |
|        notice, this list of conditions and the following disclaimer.
 | |
|     2. Redistributions in binary form must reproduce the above copyright
 | |
|        notice, this list of conditions and the following disclaimer in the
 | |
|        documentation and/or other materials provided with the distribution.
 | |
| 
 | |
|     THIS SOFTWARE IS PROVIDED BY THE AUTHOR "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 THE AUTHOR 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.
 | |
|  */
 | |
| 
 | |
| /*
 | |
|   tre-match-backtrack.c - TRE backtracking regex matching engine
 | |
| 
 | |
| This is the license, copyright notice, and disclaimer for TRE, a regex
 | |
| matching package (library and tools) with support for approximate
 | |
| matching.
 | |
| 
 | |
| Copyright (c) 2001-2009 Ville Laurikari <vl@iki.fi>
 | |
| All rights reserved.
 | |
| 
 | |
| Redistribution and use in source and binary forms, with or without
 | |
| modification, are permitted provided that the following conditions
 | |
| are met:
 | |
| 
 | |
|   1. Redistributions of source code must retain the above copyright
 | |
|      notice, this list of conditions and the following disclaimer.
 | |
| 
 | |
|   2. Redistributions in binary form must reproduce the above copyright
 | |
|      notice, this list of conditions and the following disclaimer in the
 | |
|      documentation and/or other materials provided with the distribution.
 | |
| 
 | |
| THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER AND CONTRIBUTORS
 | |
| ``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 THE COPYRIGHT
 | |
| HOLDER OR CONTRIBUTORS 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.
 | |
| */
 | |
| 
 | |
| /*
 | |
|   This matcher is for regexps that use back referencing.  Regexp matching
 | |
|   with back referencing is an NP-complete problem on the number of back
 | |
|   references.  The easiest way to match them is to use a backtracking
 | |
|   routine which basically goes through all possible paths in the TNFA
 | |
|   and chooses the one which results in the best (leftmost and longest)
 | |
|   match.  This can be spectacularly expensive and may run out of stack
 | |
|   space, but there really is no better known generic algorithm.	 Quoting
 | |
|   Henry Spencer from comp.compilers:
 | |
|   <URL: http://compilers.iecc.com/comparch/article/93-03-102>
 | |
| 
 | |
|     POSIX.2 REs require longest match, which is really exciting to
 | |
|     implement since the obsolete ("basic") variant also includes
 | |
|     \<digit>.  I haven't found a better way of tackling this than doing
 | |
|     a preliminary match using a DFA (or simulation) on a modified RE
 | |
|     that just replicates subREs for \<digit>, and then doing a
 | |
|     backtracking match to determine whether the subRE matches were
 | |
|     right.  This can be rather slow, but I console myself with the
 | |
|     thought that people who use \<digit> deserve very slow execution.
 | |
|     (Pun unintentional but very appropriate.)
 | |
| 
 | |
| */
 | |
| 
 | |
| #include "tre-prv.h"
 | |
| #include "tre-match-ut.h"
 | |
| #include "tre-mem.h"
 | |
| 
 | |
| typedef struct
 | |
| {
 | |
| 	int pos;
 | |
| 	const char *str_byte;
 | |
| #ifdef TRE_WCHAR
 | |
| 	const hawk_uch_t *str_wide;
 | |
| #endif /* TRE_WCHAR */
 | |
| 	tre_tnfa_transition_t *state;
 | |
| 	int state_id;
 | |
| 	int next_c;
 | |
| 	int *tags;
 | |
| #ifdef TRE_MBSTATE
 | |
| 	hawk_mbstate_t mbstate;
 | |
| #endif /* TRE_MBSTATE */
 | |
| } tre_backtrack_item_t;
 | |
| 
 | |
| typedef struct tre_backtrack_struct
 | |
| {
 | |
| 	tre_backtrack_item_t item;
 | |
| 	struct tre_backtrack_struct *prev;
 | |
| 	struct tre_backtrack_struct *next;
 | |
| } *tre_backtrack_t;
 | |
| 
 | |
| #ifdef TRE_WCHAR
 | |
| #define BT_STACK_WIDE_IN(_str_wide)	stack->item.str_wide = (_str_wide)
 | |
| #define BT_STACK_WIDE_OUT		(str_wide) = stack->item.str_wide
 | |
| #else /* !TRE_WCHAR */
 | |
| #define BT_STACK_WIDE_IN(_str_wide)
 | |
| #define BT_STACK_WIDE_OUT
 | |
| #endif /* !TRE_WCHAR */
 | |
| 
 | |
| #ifdef TRE_MBSTATE
 | |
| #define BT_STACK_MBSTATE_IN  stack->item.mbstate = (mbstate)
 | |
| #define BT_STACK_MBSTATE_OUT (mbstate) = stack->item.mbstate
 | |
| #else /* !TRE_MBSTATE */
 | |
| #define BT_STACK_MBSTATE_IN
 | |
| #define BT_STACK_MBSTATE_OUT
 | |
| #endif /* !TRE_MBSTATE */
 | |
| 
 | |
| #define tre_bt_mem_new		  tre_mem_new
 | |
| #define tre_bt_mem_alloc	  tre_mem_alloc
 | |
| #define tre_bt_mem_destroy	  tre_mem_destroy
 | |
| 
 | |
| 
 | |
| #define BT_STACK_PUSH(_hawk, _pos, _str_byte, _str_wide, _state, _state_id, _next_c, _tags, _mbstate) \
 | |
|   do									      \
 | |
|     {									      \
 | |
|       int i;								      \
 | |
|       if (!stack->next)							      \
 | |
| 	{								      \
 | |
| 	  tre_backtrack_t s;						      \
 | |
| 	  s = tre_bt_mem_alloc(mem, sizeof(*s));			      \
 | |
| 	  if (!s)							      \
 | |
| 	    {								      \
 | |
| 	      tre_bt_mem_destroy(mem);				 \
 | |
| 	      if (tags) xfree(_hawk,tags);				 \
 | |
| 	      if (pmatch)  xfree(_hawk,pmatch);			 \
 | |
| 	      if (states_seen) xfree(_hawk,states_seen);	 \
 | |
| 	      return REG_ESPACE;					      \
 | |
| 	    }								      \
 | |
| 	  s->prev = stack;						      \
 | |
| 	  s->next = NULL;						      \
 | |
| 	  s->item.tags = tre_bt_mem_alloc(mem,		\
 | |
| 		  sizeof(*tags) * tnfa->num_tags);    \
 | |
| 	  if (!s->item.tags)						\
 | |
| 	    {								     \
 | |
| 	      tre_bt_mem_destroy(mem);				\
 | |
| 	      if (tags) xfree(_hawk,tags);				\
 | |
| 	      if (pmatch) xfree(_hawk,pmatch);			\
 | |
| 	      if (states_seen) xfree(_hawk,states_seen);	\
 | |
| 	      return REG_ESPACE;					      \
 | |
| 	    }								      \
 | |
| 	  stack->next = s;						      \
 | |
| 	  stack = s;							      \
 | |
| 	}								      \
 | |
|       else								      \
 | |
| 	stack = stack->next;						      \
 | |
|       stack->item.pos = (_pos);						      \
 | |
|       stack->item.str_byte = (_str_byte);				      \
 | |
|       BT_STACK_WIDE_IN(_str_wide);					      \
 | |
|       stack->item.state = (_state);					      \
 | |
|       stack->item.state_id = (_state_id);				      \
 | |
|       stack->item.next_c = (_next_c);					      \
 | |
|       for (i = 0; i < tnfa->num_tags; i++)				      \
 | |
| 	stack->item.tags[i] = (_tags)[i];				      \
 | |
|       BT_STACK_MBSTATE_IN;						      \
 | |
|     }									      \
 | |
|   while (/*CONSTCOND*/0)
 | |
| 
 | |
| #define BT_STACK_POP()							      \
 | |
| 	do { \
 | |
| 		int i;								      \
 | |
| 		assert(stack->prev);						      \
 | |
| 		pos = stack->item.pos;						      \
 | |
| 		str_byte = stack->item.str_byte;					      \
 | |
| 		BT_STACK_WIDE_OUT;						      \
 | |
| 		state = stack->item.state;					      \
 | |
| 		next_c = stack->item.next_c;					      \
 | |
| 		for (i = 0; i < tnfa->num_tags; i++) tags[i] = stack->item.tags[i]; \
 | |
| 		BT_STACK_MBSTATE_OUT;						      \
 | |
| 		stack = stack->prev;						      \
 | |
| 	} while (/*CONSTCOND*/0)
 | |
| 
 | |
| #undef MIN
 | |
| #define MIN(a, b) ((a) <= (b) ? (a) : (b))
 | |
| 
 | |
| reg_errcode_t
 | |
| tre_tnfa_run_backtrack(hawk_gem_t* gem, const tre_tnfa_t *tnfa, const void *string,
 | |
|                        int len, tre_str_type_t type, int *match_tags,
 | |
|                        int eflags, int *match_end_ofs)
 | |
| {
 | |
| 	/* State variables required by GET_NEXT_WCHAR. */
 | |
| 	tre_char_t prev_c = 0, next_c = 0;
 | |
| 	const char *str_byte = string;
 | |
| 	int pos = 0;
 | |
| 	unsigned int pos_add_next = 1;
 | |
| #ifdef TRE_WCHAR
 | |
| 	const hawk_uch_t *str_wide = string;
 | |
| #ifdef TRE_MBSTATE
 | |
| 	hawk_mbstate_t mbstate;
 | |
| #endif /* TRE_MBSTATE */
 | |
| #endif /* TRE_WCHAR */
 | |
| 	int reg_notbol = eflags & REG_NOTBOL;
 | |
| 	int reg_noteol = eflags & REG_NOTEOL;
 | |
| 	int reg_newline = tnfa->cflags & REG_NEWLINE;
 | |
| 
 | |
| 	/* These are used to remember the necessary values of the above
 | |
| 	   variables to return to the position where the current search
 | |
| 	   started from. */
 | |
| 	int next_c_start;
 | |
| 	const char *str_byte_start;
 | |
| 	int pos_start = -1;
 | |
| #ifdef TRE_WCHAR
 | |
| 	const hawk_uch_t *str_wide_start;
 | |
| #endif /* TRE_WCHAR */
 | |
| #ifdef TRE_MBSTATE
 | |
| 	hawk_mbstate_t mbstate_start;
 | |
| #endif /* TRE_MBSTATE */
 | |
| 
 | |
| 	/* End offset of best match so far, or -1 if no match found yet. */
 | |
| 	int match_eo = -1;
 | |
| 	/* Tag arrays. */
 | |
| 	int *next_tags, *tags = NULL;
 | |
| 	/* Current TNFA state. */
 | |
| 	tre_tnfa_transition_t *state;
 | |
| 	int *states_seen = NULL;
 | |
| 	/* Memory allocator to for allocating the backtracking stack. */
 | |
| 	tre_mem_t mem = tre_bt_mem_new(gem);
 | |
| 
 | |
| 	/* The backtracking stack. */
 | |
| 	tre_backtrack_t stack;
 | |
| 
 | |
| 	tre_tnfa_transition_t *trans_i;
 | |
| 	regmatch_t *pmatch = NULL;
 | |
| 	int ret;
 | |
| 
 | |
| #ifdef TRE_MBSTATE
 | |
| 	HAWK_MEMSET(&mbstate, '\0', sizeof(mbstate));
 | |
| #endif /* TRE_MBSTATE */
 | |
| 
 | |
| 	if (!mem)
 | |
| 		return REG_ESPACE;
 | |
| 	stack = tre_bt_mem_alloc(mem, sizeof(*stack));
 | |
| 	if (!stack)
 | |
| 	{
 | |
| 		ret = REG_ESPACE;
 | |
| 		goto error_exit;
 | |
| 	}
 | |
| 	stack->prev = NULL;
 | |
| 	stack->next = NULL;
 | |
| 
 | |
| 	DPRINT(("tnfa_execute_backtrack, input type %d\n", type));
 | |
| 	DPRINT(("len = %d\n", len));
 | |
| 
 | |
| 	if (tnfa->num_tags)
 | |
| 	{
 | |
| 		tags = xmalloc(gem, sizeof(*tags) * tnfa->num_tags);
 | |
| 		if (!tags)
 | |
| 		{
 | |
| 			ret = REG_ESPACE;
 | |
| 			goto error_exit;
 | |
| 		}
 | |
| 	}
 | |
| 	if (tnfa->num_submatches)
 | |
| 	{
 | |
| 		pmatch = xmalloc(gem, sizeof(*pmatch) * tnfa->num_submatches);
 | |
| 		if (!pmatch)
 | |
| 		{
 | |
| 			ret = REG_ESPACE;
 | |
| 			goto error_exit;
 | |
| 		}
 | |
| 	}
 | |
| 	if (tnfa->num_states)
 | |
| 	{
 | |
| 		states_seen = xmalloc(gem, sizeof(*states_seen) * tnfa->num_states);
 | |
| 		if (!states_seen)
 | |
| 		{
 | |
| 			ret = REG_ESPACE;
 | |
| 			goto error_exit;
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| retry:
 | |
| 	{
 | |
| 		int i;
 | |
| 		for (i = 0; i < tnfa->num_tags; i++)
 | |
| 		{
 | |
| 			tags[i] = -1;
 | |
| 			if (match_tags)
 | |
| 				match_tags[i] = -1;
 | |
| 		}
 | |
| 		for (i = 0; i < tnfa->num_states; i++)
 | |
| 			states_seen[i] = 0;
 | |
| 	}
 | |
| 
 | |
| 	state = NULL;
 | |
| 	pos = pos_start;
 | |
| 	GET_NEXT_WCHAR();
 | |
| 	pos_start = pos;
 | |
| 	next_c_start = next_c;
 | |
| 	str_byte_start = str_byte;
 | |
| #ifdef TRE_WCHAR
 | |
| 	str_wide_start = str_wide;
 | |
| #endif /* TRE_WCHAR */
 | |
| #ifdef TRE_MBSTATE
 | |
| 	mbstate_start = mbstate;
 | |
| #endif /* TRE_MBSTATE */
 | |
| 
 | |
| 	/* Handle initial states. */
 | |
| 	next_tags = NULL;
 | |
| 	for (trans_i = tnfa->initial; trans_i->state; trans_i++)
 | |
| 	{
 | |
| 		DPRINT(("> init %p, prev_c %lc\n", trans_i->state, (tre_cint_t)prev_c));
 | |
| 		if (trans_i->assertions && CHECK_ASSERTIONS(trans_i->assertions))
 | |
| 		{
 | |
| 			DPRINT(("assert failed\n"));
 | |
| 			continue;
 | |
| 		}
 | |
| 		if (state == NULL)
 | |
| 		{
 | |
| 			/* Start from this state. */
 | |
| 			state = trans_i->state;
 | |
| 			next_tags = trans_i->tags;
 | |
| 		}
 | |
| 		else
 | |
| 		{
 | |
| 			/* Backtrack to this state. */
 | |
| 			DPRINT(("saving state %d for backtracking\n", trans_i->state_id));
 | |
| 			BT_STACK_PUSH(gem, pos, str_byte, str_wide, trans_i->state,
 | |
| 			              trans_i->state_id, next_c, tags, mbstate);
 | |
| 			{
 | |
| 				int *tmp = trans_i->tags;
 | |
| 				if (tmp)
 | |
| 					while (*tmp >= 0)
 | |
| 						stack->item.tags[*tmp++] = pos;
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	if (next_tags)
 | |
| 		for (; *next_tags >= 0; next_tags++)
 | |
| 			tags[*next_tags] = pos;
 | |
| 
 | |
| 
 | |
| 	DPRINT(("entering match loop, pos %d, str_byte %p\n", pos, str_byte));
 | |
| 	DPRINT(("pos:chr/code | state and tags\n"));
 | |
| 	DPRINT(("-------------+------------------------------------------------\n"));
 | |
| 
 | |
| 	if (state == NULL)
 | |
| 		goto backtrack;
 | |
| 
 | |
| 	while (/*CONSTCOND*/1)
 | |
| 	{
 | |
| 		tre_tnfa_transition_t *next_state;
 | |
| 		int empty_br_match;
 | |
| 
 | |
| 		DPRINT(("start loop\n"));
 | |
| 		if (state == tnfa->final)
 | |
| 		{
 | |
| 			DPRINT(("  match found, %d %d\n", match_eo, pos));
 | |
| 			if (match_eo < pos
 | |
| 			        || (match_eo == pos
 | |
| 			            && match_tags
 | |
| 			            && tre_tag_order(tnfa->num_tags, tnfa->tag_directions,
 | |
| 			                             tags, match_tags)))
 | |
| 			{
 | |
| 				int i;
 | |
| 				/* This match wins the previous match. */
 | |
| 				DPRINT(("	 win previous\n"));
 | |
| 				match_eo = pos;
 | |
| 				if (match_tags)
 | |
| 					for (i = 0; i < tnfa->num_tags; i++)
 | |
| 						match_tags[i] = tags[i];
 | |
| 			}
 | |
| 			/* Our TNFAs never have transitions leaving from the final state,
 | |
| 			   so we jump right to backtracking. */
 | |
| 			goto backtrack;
 | |
| 		}
 | |
| 
 | |
| #ifdef TRE_DEBUG
 | |
| 		DPRINT(("%3d:%2lc/%05d | %p ", pos, (tre_cint_t)next_c, (int)next_c,
 | |
| 		        state));
 | |
| 		{
 | |
| 			int i;
 | |
| 			for (i = 0; i < tnfa->num_tags; i++)
 | |
| 				DPRINT(("%d%s", tags[i], i < tnfa->num_tags - 1 ? ", " : ""));
 | |
| 			DPRINT(("\n"));
 | |
| 		}
 | |
| #endif /* TRE_DEBUG */
 | |
| 
 | |
| 		/* Go to the next character in the input string. */
 | |
| 		empty_br_match = 0;
 | |
| 		trans_i = state;
 | |
| 		if (trans_i->state && (trans_i->assertions & ASSERT_BACKREF))
 | |
| 		{
 | |
| 			/* This is a back reference state.  All transitions leaving from
 | |
| 			   this state have the same back reference "assertion".  Instead
 | |
| 			   of reading the next character, we match the back reference. */
 | |
| 			int so, eo, bt = trans_i->u.backref;
 | |
| 			int bt_len;
 | |
| 			int result;
 | |
| 
 | |
| 			DPRINT(("  should match back reference %d\n", bt));
 | |
| 			/* Get the substring we need to match against.  Remember to
 | |
| 			   turn off REG_NOSUB temporarily. */
 | |
| 			tre_fill_pmatch(bt + 1, pmatch, tnfa->cflags & /*LINTED*/!REG_NOSUB,
 | |
| 			                tnfa, tags, pos);
 | |
| 			so = pmatch[bt].rm_so;
 | |
| 			eo = pmatch[bt].rm_eo;
 | |
| 			bt_len = eo - so;
 | |
| 
 | |
| #ifdef TRE_DEBUG
 | |
| 			{
 | |
| 				int slen;
 | |
| 				if (len < 0)
 | |
| 					slen = bt_len;
 | |
| 				else
 | |
| 					slen = MIN(bt_len, len - pos);
 | |
| 
 | |
| 				if (type == STR_BYTE)
 | |
| 				{
 | |
| 					DPRINT(("  substring (len %d) is [%d, %d[: '%.*s'\n",
 | |
| 					        bt_len, so, eo, bt_len, (char*)string + so));
 | |
| 					DPRINT(("  current string is '%.*s'\n", slen, str_byte - 1));
 | |
| 				}
 | |
| #ifdef TRE_WCHAR
 | |
| 				else if (type == STR_WIDE)
 | |
| 				{
 | |
| 					DPRINT(("  substring (len %d) is [%d, %d[: '%.*" STRF "'\n",
 | |
| 					        bt_len, so, eo, bt_len, (hawk_uch_t*)string + so));
 | |
| 					DPRINT(("  current string is '%.*" STRF "'\n",
 | |
| 					        slen, str_wide - 1));
 | |
| 				}
 | |
| #endif /* TRE_WCHAR */
 | |
| 			}
 | |
| #endif
 | |
| 
 | |
| 			if (len < 0)
 | |
| 			{
 | |
| #ifdef TRE_WCHAR
 | |
| 				if (type == STR_WIDE)
 | |
| 					result = hawk_comp_ucstr_limited((const hawk_uch_t*)string + so, str_wide - 1, (size_t)bt_len, 0);
 | |
| 				else
 | |
| #endif /* TRE_WCHAR */
 | |
| 					result = hawk_comp_bcstr_limited((const char*)string + so, str_byte - 1, (size_t)bt_len, 0);
 | |
| 			}
 | |
| 			else if (len - pos < bt_len)
 | |
| 				result = 1;
 | |
| #ifdef TRE_WCHAR
 | |
| 			else if (type == STR_WIDE)
 | |
| 			{
 | |
| 				/*result = wmemcmp((const hawk_uch_t*)string + so, str_wide - 1, (size_t)bt_len);*/
 | |
| 				result = hawk_comp_uchars((const hawk_uch_t*)string + so, (size_t)bt_len, str_wide - 1, (size_t)bt_len, 0);
 | |
| 			}
 | |
| #endif /* TRE_WCHAR */
 | |
| 			else
 | |
| 			{
 | |
| 				/*result = memcmp((const char*)string + so, str_byte - 1, (size_t)bt_len); */
 | |
| 				result = hawk_comp_bchars((const char*)string + so, (size_t)bt_len, str_byte - 1, (size_t)bt_len, 0);
 | |
| 			}
 | |
| 
 | |
| 
 | |
| 			if (result == 0)
 | |
| 			{
 | |
| 				/* Back reference matched.  Check for infinite loop. */
 | |
| 				if (bt_len == 0)
 | |
| 					empty_br_match = 1;
 | |
| 				if (empty_br_match && states_seen[trans_i->state_id])
 | |
| 				{
 | |
| 					DPRINT(("  avoid loop\n"));
 | |
| 					goto backtrack;
 | |
| 				}
 | |
| 
 | |
| 				states_seen[trans_i->state_id] = empty_br_match;
 | |
| 
 | |
| 				/* Advance in input string and resync `prev_c', `next_c'
 | |
| 						 and pos. */
 | |
| 				DPRINT(("	 back reference matched\n"));
 | |
| 				str_byte += bt_len - 1;
 | |
| #ifdef TRE_WCHAR
 | |
| 				str_wide += bt_len - 1;
 | |
| #endif /* TRE_WCHAR */
 | |
| 				pos += bt_len - 1;
 | |
| 				GET_NEXT_WCHAR();
 | |
| 				DPRINT(("	 pos now %d\n", pos));
 | |
| 			}
 | |
| 			else
 | |
| 			{
 | |
| 				DPRINT(("	 back reference did not match\n"));
 | |
| 				goto backtrack;
 | |
| 			}
 | |
| 		}
 | |
| 		else
 | |
| 		{
 | |
| 			/* Check for end of string. */
 | |
| 			if (len < 0)
 | |
| 			{
 | |
| 				if (next_c == HAWK_T('\0'))
 | |
| 					goto backtrack;
 | |
| 			}
 | |
| 			else
 | |
| 			{
 | |
| 				if (pos >= len)
 | |
| 					goto backtrack;
 | |
| 			}
 | |
| 
 | |
| 			/* Read the next character. */
 | |
| 			GET_NEXT_WCHAR();
 | |
| 		}
 | |
| 
 | |
| 		next_state = NULL;
 | |
| 		for (trans_i = state; trans_i->state; trans_i++)
 | |
| 		{
 | |
| 			DPRINT(("  transition %d-%d (%c-%c) %d to %d\n",
 | |
| 			        trans_i->code_min, trans_i->code_max,
 | |
| 			        trans_i->code_min, trans_i->code_max,
 | |
| 			        trans_i->assertions, trans_i->state_id));
 | |
| 
 | |
| 			if (trans_i->code_min <= (tre_cint_t)prev_c && trans_i->code_max >= (tre_cint_t)prev_c)
 | |
| 			{
 | |
| 				if (trans_i->assertions
 | |
| 				        && (CHECK_ASSERTIONS(trans_i->assertions)
 | |
| 				            || CHECK_CHAR_CLASSES(trans_i, tnfa, eflags)))
 | |
| 				{
 | |
| 					DPRINT(("  assertion failed\n"));
 | |
| 					continue;
 | |
| 				}
 | |
| 
 | |
| 				if (next_state == NULL)
 | |
| 				{
 | |
| 					/* First matching transition. */
 | |
| 					DPRINT(("  Next state is %d\n", trans_i->state_id));
 | |
| 					next_state = trans_i->state;
 | |
| 					next_tags = trans_i->tags;
 | |
| 				}
 | |
| 				else
 | |
| 				{
 | |
| 					/* Second matching transition.  We may need to backtrack here
 | |
| 					   to take this transition instead of the first one, so we
 | |
| 					   push this transition in the backtracking stack so we can
 | |
| 					   jump back here if needed. */
 | |
| 					DPRINT(("  saving state %d for backtracking\n",
 | |
| 					        trans_i->state_id));
 | |
| 					BT_STACK_PUSH(gem, pos, str_byte, str_wide, trans_i->state,
 | |
| 					              trans_i->state_id, next_c, tags, mbstate);
 | |
| 					{
 | |
| 						int *tmp;
 | |
| 						for (tmp = trans_i->tags; tmp && *tmp >= 0; tmp++)
 | |
| 							stack->item.tags[*tmp] = pos;
 | |
| 					}
 | |
| #if 0 /* XXX - it's important not to look at all transitions here to keep
 | |
| 					the stack small! */
 | |
| 					break;
 | |
| #endif
 | |
| 				}
 | |
| 			}
 | |
| 		}
 | |
| 
 | |
| 		if (next_state != NULL)
 | |
| 		{
 | |
| 			/* Matching transitions were found.  Take the first one. */
 | |
| 			state = next_state;
 | |
| 
 | |
| 			/* Update the tag values. */
 | |
| 			if (next_tags)
 | |
| 				while (*next_tags >= 0)
 | |
| 					tags[*next_tags++] = pos;
 | |
| 		}
 | |
| 		else
 | |
| 		{
 | |
| backtrack:
 | |
| 			/* A matching transition was not found.  Try to backtrack. */
 | |
| 			if (stack->prev)
 | |
| 			{
 | |
| 				DPRINT(("	 backtracking\n"));
 | |
| 				/*if (stack->item.state->assertions && ASSERT_BACKREF)*/
 | |
| 				if (stack->item.state->assertions & ASSERT_BACKREF)
 | |
| 				{
 | |
| 					DPRINT(("  states_seen[%d] = 0\n", stack->item.state_id));
 | |
| 					states_seen[stack->item.state_id] = 0;
 | |
| 				}
 | |
| 
 | |
| 				BT_STACK_POP();
 | |
| 			}
 | |
| 			else if (match_eo < 0)
 | |
| 			{
 | |
| 				/* Try starting from a later position in the input string. */
 | |
| 				/* Check for end of string. */
 | |
| 				if (len < 0)
 | |
| 				{
 | |
| 					if (next_c == HAWK_T('\0'))
 | |
| 					{
 | |
| 						DPRINT(("end of string.\n"));
 | |
| 						break;
 | |
| 					}
 | |
| 				}
 | |
| 				else
 | |
| 				{
 | |
| 					if (pos >= len)
 | |
| 					{
 | |
| 						DPRINT(("end of string.\n"));
 | |
| 						break;
 | |
| 					}
 | |
| 				}
 | |
| 				DPRINT(("restarting from next start position\n"));
 | |
| 				next_c = next_c_start;
 | |
| #ifdef TRE_MBSTATE
 | |
| 				mbstate = mbstate_start;
 | |
| #endif /* TRE_MBSTATE */
 | |
| 				str_byte = str_byte_start;
 | |
| #ifdef TRE_WCHAR
 | |
| 				str_wide = str_wide_start;
 | |
| #endif /* TRE_WCHAR */
 | |
| 				goto retry;
 | |
| 			}
 | |
| 			else
 | |
| 			{
 | |
| 				DPRINT(("finished\n"));
 | |
| 				break;
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	ret = match_eo >= 0 ? REG_OK : REG_NOMATCH;
 | |
| 	*match_end_ofs = match_eo;
 | |
| 
 | |
| error_exit:
 | |
| 	tre_bt_mem_destroy(mem);
 | |
| 	if (tags) xfree(gem, tags);
 | |
| 	if (pmatch) xfree(gem, pmatch);
 | |
| 	if (states_seen) xfree(gem, states_seen);
 | |
| 
 | |
| 	return ret;
 | |
| }
 |