
/********************************************
rexp2.c

libmawk changes (C) 2009-2010, Tibor 'Igor2' Palinkas;
based on mawk code coming with the below copyright:

copyright 1991, Michael D. Brennan

This is a source file for mawk, an implementation of
the AWK programming language.

Mawk is distributed without warranty under the terms of
the GNU General Public License, version 2, 1991.
********************************************/
/*  test a string against a machine   */

#include "rexp.h"
#include <string.h>

#define	 STACKGROWTH	16

#ifdef	DEBUG
static mawk_RT_STATE *slow_push(mawk_state_t *MAWK, mawk_RT_STATE *, mawk_RESTATE *, char *, int);
#endif

int mawk_RE_run_stack_init(mawk_state_t *MAWK)
{
	if (!MAWK->RE_run_stack_base) {
		MAWK->RE_run_stack_base = (mawk_RT_STATE *)
			mawk_RE_malloc(MAWK, sizeof(mawk_RT_STATE) * STACKGROWTH);
		if (MAWK->RE_run_stack_base == NULL)
			return -MEMORY_FAILURE;
		MAWK->RE_run_stack_limit = MAWK->RE_run_stack_base + STACKGROWTH;
		MAWK->RE_run_stack_empty = MAWK->RE_run_stack_base - 1;
	}
	return 0;
}

/* sometimes during mawk_REmatch(), this stack can grow pretty large.
   In real life cases, the back tracking usually fails. Some
   work is needed here to improve the algorithm.
   I.e., figure out how not to stack useless paths.
*/

mawk_RT_STATE *mawk_RE_new_run_stack(mawk_state_t *MAWK)
{
	int oldsize = MAWK->RE_run_stack_limit - MAWK->RE_run_stack_base;
	int newsize = oldsize + STACKGROWTH;

#ifdef	LMDOS										/* large model DOS */
	/* have to worry about overflow on multiplication (ugh) */
	if (newsize >= 4096)
		MAWK->RE_run_stack_base = (mawk_RT_STATE *) 0;
	else
#endif

		MAWK->RE_run_stack_base = (mawk_RT_STATE *) mawk_RE_realloc(MAWK, MAWK->RE_run_stack_base, oldsize * sizeof(mawk_RT_STATE), newsize * sizeof(mawk_RT_STATE));

	if (!MAWK->RE_run_stack_base) {
		fprintf(stderr, "out of memory for RE run time stack\n");
		/* this is pretty unusual, I've only seen it happen on
		   weird input to mawk_REmatch() under 16bit DOS , the same
		   situation worked easily on 32bit machine.  */
		exit(100);
	}

	MAWK->RE_run_stack_limit = MAWK->RE_run_stack_base + newsize;
	MAWK->RE_run_stack_empty = MAWK->RE_run_stack_base - 1;

	/* return the new stackp */
	return MAWK->RE_run_stack_base + oldsize;
}

#ifdef DEBUG
static mawk_RT_STATE *slow_push(mawk_state_t *MAWK, mawk_RT_STATE *sp, mawk_RESTATE *m, char *s, int u)
{
	if (sp == MAWK->RE_run_stack_limit)
		sp = mawk_RE_new_run_stack(MAWK);
	sp->m = m;
	sp->s = s;
	sp->u = u;
	return sp;
}
#endif

#ifdef	 DEBUG
#define	 push(mx,sx,ux)	  stackp = slow_push(MAWK, ++stackp, mx, sx, ux)
#else
#define	 push(mx,sx,ux)	  if (++stackp == MAWK->RE_run_stack_limit)\
				stackp = mawk_RE_new_run_stack(MAWK) ;\
stackp->m=(mx);stackp->s=(sx);stackp->u=(ux)
#endif


#define	  CASE_UANY(x)	case  x + U_OFF :  case	 x + U_ON

/* test if str ~ /machine/
*/

int mawk_REtest(mawk_state_t *MAWK, char *str, PTR machine)
{
	register mawk_RESTATE *m = (mawk_RESTATE *) machine;
	register char *s = str;
	register mawk_RT_STATE *stackp;
	int u_flag;
	char *str_end;
	int t;												/*convenient temps */
	mawk_RESTATE *tm;

	/* handle the easy case quickly */
	if ((m + 1)->type == M_ACCEPT && m->type == M_STR)
		return mawk_str_str(s, m->data.str, m->len) != (char *) 0;
	else {
		u_flag = U_ON;
		str_end = (char *) 0;
		stackp = MAWK->RE_run_stack_empty;
		goto reswitch;
	}

refill:
	if (stackp == MAWK->RE_run_stack_empty)
		return 0;
	m = stackp->m;
	s = stackp->s;
	u_flag = stackp--->u;


reswitch:

	switch (m->type + u_flag) {
	case M_STR + U_OFF + END_OFF:
		if (strncmp(s, m->data.str, m->len))
			goto refill;
		s += m->len;
		m++;
		goto reswitch;

	case M_STR + U_OFF + END_ON:
		if (strcmp(s, m->data.str))
			goto refill;
		s += m->len;
		m++;
		goto reswitch;

	case M_STR + U_ON + END_OFF:
		if (!(s = mawk_str_str(s, m->data.str, m->len)))
			goto refill;
		push(m, s + 1, U_ON);
		s += m->len;
		m++;
		u_flag = U_OFF;
		goto reswitch;

	case M_STR + U_ON + END_ON:
		if (!str_end)
			str_end = s + strlen(s);
		t = (str_end - s) - m->len;
		if (t < 0 || memcmp(s + t, m->data.str, m->len))
			goto refill;
		s = str_end;
		m++;
		u_flag = U_OFF;
		goto reswitch;

	case M_CLASS + U_OFF + END_OFF:
		if (!ison(*m->data.bvp, s[0]))
			goto refill;
		s++;
		m++;
		goto reswitch;

	case M_CLASS + U_OFF + END_ON:
		if (s[1] || !ison(*m->data.bvp, s[0]))
			goto refill;
		s++;
		m++;
		goto reswitch;

	case M_CLASS + U_ON + END_OFF:
		while (!ison(*m->data.bvp, s[0])) {
			if (s[0] == 0)
				goto refill;
			else
				s++;
		}
		s++;
		push(m, s, U_ON);
		m++;
		u_flag = U_OFF;
		goto reswitch;

	case M_CLASS + U_ON + END_ON:
		if (!str_end)
			str_end = s + strlen(s);
		if (s[0] == 0 || !ison(*m->data.bvp, str_end[-1]))
			goto refill;
		s = str_end;
		m++;
		u_flag = U_OFF;
		goto reswitch;

	case M_ANY + U_OFF + END_OFF:
		if (s[0] == 0)
			goto refill;
		s++;
		m++;
		goto reswitch;

	case M_ANY + U_OFF + END_ON:
		if (s[0] == 0 || s[1] != 0)
			goto refill;
		s++;
		m++;
		goto reswitch;

	case M_ANY + U_ON + END_OFF:
		if (s[0] == 0)
			goto refill;
		s++;
		push(m, s, U_ON);
		m++;
		u_flag = U_OFF;
		goto reswitch;

	case M_ANY + U_ON + END_ON:
		if (s[0] == 0)
			goto refill;
		if (!str_end)
			str_end = s + strlen(s);
		s = str_end;
		m++;
		u_flag = U_OFF;
		goto reswitch;

	case M_START + U_OFF + END_OFF:
	case M_START + U_ON + END_OFF:
		if (s != str)
			goto refill;
		m++;
		u_flag = U_OFF;
		goto reswitch;

	case M_START + U_OFF + END_ON:
	case M_START + U_ON + END_ON:
		if (s != str || s[0] != 0)
			goto refill;
		m++;
		u_flag = U_OFF;
		goto reswitch;

	case M_END + U_OFF:
		if (s[0] != 0)
			goto refill;
		m++;
		goto reswitch;

	case M_END + U_ON:
		s += strlen(s);
		m++;
		u_flag = U_OFF;
		goto reswitch;

	CASE_UANY(M_U):
		u_flag = U_ON;
		m++;
		goto reswitch;

	CASE_UANY(M_1J):
		m += m->data.jump;
		goto reswitch;

	CASE_UANY(M_2JA):						/* take the non jump branch */
		/* don't stack an ACCEPT */
		if ((tm = m + m->data.jump)->type == M_ACCEPT)
			return 1;
		push(tm, s, u_flag);
		m++;
		goto reswitch;

	CASE_UANY(M_2JB):						/* take the jump branch */
		/* don't stack an ACCEPT */
		if ((tm = m + 1)->type == M_ACCEPT)
			return 1;
		push(tm, s, u_flag);
		m += m->data.jump;
		goto reswitch;

	CASE_UANY(M_ACCEPT):
		return 1;

	default:
		mawk_RE_panic("unexpected case in mawk_REtest");
	}
	return -1;
}



#ifndef	NOT_FOR_MAWK

char *mawk_is_string_split(register mawk_RESTATE *p, unsigned *lenp)
{
	if (p[0].type == M_STR && p[1].type == M_ACCEPT) {
		*lenp = p->len;
		return p->data.str;
	}
	else
		return (char *) 0;
}
#else	/* mawk provides its own mawk_str_str */

char *mawk_str_str(register char *target, register char *key, unsigned klen)
{
	int c = key[0];

	switch (klen) {
	case 0:
		return (char *) 0;

	case 1:
		return strchr(target, c);

	case 2:
		{
			int c1 = key[1];

			while (target = strchr(target, c)) {
				if (target[1] == c1)
					return target;
				else
					target++;
			}
			break;
		}

	default:
		klen--;
		key++;
		while (target = strchr(target, c)) {
			if (memcmp(target + 1, key, klen) == 0)
				return target;
			else
				target++;
		}
		break;
	}
	return (char *) 0;
}


#endif /* FORMAWK */
