/*
Known bugs:
	Does not handle strings starting with \B
	Does not handle a(.*)b correctly
	Basically + and * don't work inside groups (), because that means
	for all lengths of matches of the group, try the rest of the string
	and we don't do that, we pick the shortest match, so
			a(.*)b only matches aXb
	Solution, avoid groups :-)


    [\x20-\x60]%a%false
\b = word boundary
\B = not word boundary

  \r\n\t  cr lf tab

This is a regular expression library, it's not 100% legit but it's
fast and reliable and I understand it.

regcomp takes a regular expression and compiles it into
a linked list of objects, each object is one of:
	a single character match
	a mask of characters (char set) to match
	a list of objects to match
	...
The regexec applies teh given compiled objects to a string
and returns true or false if it matches.

*/

#include <stdlib.h>
#include <stdio.h>
#include <stdarg.h>
#include <string.h>
#include <ctype.h>
#include <stdarg.h>
#ifdef TESTONLY
#include "dmalloc.h"
#endif
#include "regex.h"
#include "defines.h"
#ifndef TRUE
#define FALSE 0
#define TRUE (!FALSE)
#endif
#include "debug.h"
#ifndef TESTINGX
#define printf imsg
#endif
#define dbg if (debug)
/* Whoever wrote this should be SHOT!
#ifdef NOVSNPRINTF
#define vsnprintf(a,b,c,d) vsprintf(a,c,d)
#endif*/
static void (*copy_dst)(char *sstart,char *send)=0;
void reg_result_dst(void (*dst)(char *sstart,char *send))
{
	copy_dst = dst;
}

static int debug = FALSE;
int reg_test_file(char *fname);
static int (*imsg)(const char *format, ... );
void reg_init(int (*msgfn)( const char *format, ... ))
{
	imsg = msgfn;
}
#ifdef TESTINGX
#define imsg printf
#endif
#ifdef TESTINGX
int main(int argc, char *argv[])
{
	int r;
	int i;
	char bf[BFSZ];
	char *s=bf;
	regex_t reg;

	if (argc<2) {
		printf("Usage: regexp -file x.dat -debug\n");
		printf("Usage: regexp -speed x.dat\n");
		return 0;
	}
	for (i=1; i<argc; i++) {
		if (strcmp(argv[i],"-debug")==0) debug = TRUE;
	}
	if (strcmp(argv[1],"-file")==0) {
		reg_test_file(argv[2]);
		return 0;
	}
	printf("Enter test expression ? ");
	gets(bf);
	r = regcomp(&reg,bf,0);
	reg_show(reg.list);
	printf("Enter test string ? ");
	gets(bf);
	r = reg_find_start(s,&reg);
	if (r) {dbg printf("Matched ok\n");}
	else {dbg printf("Didn't match\n");}
	return 0;
}
#endif
#define SEP "%"
int reg_test_file(char *fname)
{
	FILE *f;
	regex_t reg;
	char exp[BFSZ];
	char data[BFSZ];
	char bf[BFSZ];
	char *s=bf;
	int expect;
	int r;

	f = fopen(fname,"r");
	if (f==NULL) {perror(fname); return 0;}
	for (;!feof(f);) {
		if (fgets(bf,BFSZ-1,f)==NULL) break;
		s = strtok(bf,SEP); if (s==NULL) continue;
		strcpy(exp,s);
		s = strtok(NULL,SEP); if (s==NULL) continue;
		strcpy(data,s);
		s = strtok(NULL,"\r\n"); if (s==NULL) continue;
		if (strcmp(s,"true")==0) expect=TRUE;
		else expect=FALSE;

		imsg("%s!%s!%s\n",exp,data,s);
		r = regcomp(&reg,exp,0);
		reg_show(reg.list);
		strcpy(bf,data); s = bf;

		r = reg_find_start(s,&reg);
		regfree(&reg);
		if (r) {dbg printf("Matched ok\n");}
		else {dbg printf("Didn't match\n");}
		if (r!=expect) {
			printf("----------------------------FAILED\n");
			printf("%s!%s!%d\n",exp,data,expect);
			exit(0);
		}
	}
	fclose(f);
#ifdef TESTONLY
	d_check();
#endif
	return 0;
}
Obj * reg_newobj(void)
{
	Obj *a;
	a = malloc(sizeof(Obj));
	memset(a,0,sizeof(Obj));
	a->c = '*';
	return a;
}
int reg_setdebug(int x)
{
	debug = x;
	return TRUE;
}
static int rcode;
static unsigned char *bol;
static int icase;
static unsigned char *best;
static unsigned char xisword[MAXCHAR];
static int done_init;
int regcomp(regex_t *reg, char *zin, int flags)
{
	int i;
	unsigned char *in= (unsigned char *) zin;
	unsigned char fake[2];
	unsigned char mask[MAXCHAR];
	Obj *ocopy;
	unsigned char *incopy;
	int x;

	if (!done_init) {
		done_init = TRUE;
		for (i=0; i<MAXCHAR; i++) {
			if ((i>='a') && (i<='z')) xisword[i] = TRUE;
			if ((i>='A') && (i<='Z')) xisword[i] = TRUE;
			if ((i>='0') && (i<='9')) xisword[i] = TRUE;
			if (i=='_') xisword[i] = TRUE;
		}
	}
	rcode = 0;
	reg->list = r_comp_all(&in);
	reg->list->icase = ((flags & REG_ICASE)>0);
	icase = reg->list->icase;
	x = debug;
	debug = FALSE;
	for (i=1; i<255; i++) {
		mask[i] = FALSE;
		fake[0] = (unsigned char) i;
		fake[1] = 0;
		bol = fake;
		ocopy = reg->list;
		incopy = fake; best = incopy;
		reg_match(&incopy,&ocopy);
		if (best!=fake) {
			mask[i] = TRUE;
			if (x) printf("Mask %c\n",i);
		}
	}
	debug = x;
	reg->list->mask = malloc(MAXCHAR);
	memcpy(reg->list->mask,mask,MAXCHAR);

	return rcode;
}
static int dep;
Obj *r_comp_all(unsigned char **in)
{
	unsigned char *s;
	Obj *o,*t,*list;
	o = list = reg_newobj();
	dbg printf("Root object %p\n",o);
	o->type = T_OR;
	dep++;
	if (dep>400) {
		dep--;
		imsg("r_comp_all stack overflow");
		return NULL;
	}
	for (s=*in;*s!=0;) {
		t = r_comp_any(&s);
		if (t!=NULL) reg_show_obj(t);

		if (t==NULL) break;
		if (t->type==T_OR_TOP) {
			dbg imsg("[%d] Pointing new %p to %p\n",dep,t,list);
			t->type = T_OR;
			t->or = list;
			list = t;
			o = t;
			dbg imsg("[%d] lastT=%p %s\n",dep,o,rtype[o->type]);
		} else {
			if (o->next!=NULL) {
				printf("Pointer not null %p next=%p new=%p\n",o,o->next,t);
				exit(0);
			}
			dbg imsg("[%d] Adding %p to %p, type %s\n",dep,t,o,rtype[o->type]);
			o->next = t;
			o = o->next;
			dbg imsg("[%d] last=%p %s\n",dep,o,rtype[o->type]);
		}
	}
	dep--;
	*in = s;
	return list;
}
static char failmsg[BFSZ];
char *reg_error(void)
{
	return failmsg;
}
int regerror(int e, regex_t *reg, char *bf, int bfsz)
{
	strncpy(bf,failmsg,bfsz-2);
	return 0;
}
void reg_freeobj(Obj *o)
{
	if (o->mask!=NULL) free(o->mask);
	dbg printf("Free %p %c\n",o,o->c);
	free(o);
}
int regfree_list(Obj *o);
int regfree(regex_t *reg)
{
	dbg imsg("regfree(%p)\n",reg->list);
	regfree_list(reg->list);
	reg->list = NULL;
	return TRUE;
}
int regfree_list(Obj *o)
{
	Obj *next;
	dbg imsg("regfree_list %p\n",o);
	for (;o!=NULL;o=next) {
		next = o->next;
		if ((o->type==T_OR) ||(o->type==T_GROUP) || (o->type==T_NOT)) {
			regfree_list(o->or);
		}
		reg_freeobj(o);
	}
	return TRUE;
}
int regexec(regex_t *reg, char *in, int xx, char *yy, int zz)
{
	char *s;
	icase = reg->list->icase;
	s = reg_find(in,reg);
	if (s==NULL) return TRUE;
	return FALSE;
}

int r_comp_modifier(Obj *o, unsigned char **in)
{
	unsigned char c =  **in;
	unsigned char *s;
	o->min = o->max = 1;
	switch (c) {
	case '*': /* . zero or more of last char */
		o->min = 0;
		o->max = 9999;
		(*in)++;
		break;
	case '?': /* . zero or one of last char */
		o->min = 0;
		o->max = 1;
		(*in)++;
		break;
	case '+': /* . One or more of last char */
		o->min = 1;
		o->max = 9999;
		(*in)++;
		break;
	case '{': /* . Number or range of last char */
		s = *in;
		o->min = atoi((char * ) (s+1));
		o->max = o->min;
		for (;*s!=0;s++) {
			if (*s==',') o->max = atoi((char *)(s+1));
			if (*s=='}') break;
		}
		if (*s != '}') rfail("No right brace found %s",*in);
		else s++;
		*in = s;
		break;
	}
	return TRUE;
}
/*
\s [ \t]
\S [^ \t]
\d [0-9]
\D [^0-9]
*/
unsigned char get_hex(unsigned char *s)
{
	char bf[10];
	int x;
	strncpy(bf,s,2); bf[2] = 0;
	sscanf(bf,"%x",&x);
	return (unsigned char ) x;
}
Obj *r_comp_any(unsigned char **in)
{
	unsigned char c =  **in;
	unsigned char *s;
	Obj *o,*o2;
	o = reg_newobj();
	dbg printf("comp_any %c  %p  %s\n",c,o,*in);
	(*in)++;

	switch (c) {
	case '.': /* . (period) represents any one character.*/
		o->type = T_ANY;
		r_comp_modifier(o,in);
		return o;
	case '[': /* . start of a set or ^ not set */
		o->type = T_MASK;
		r_comp_set(o,in);
		r_comp_modifier(o,in);
		break;
	case '\\': /* . next character literal or special */
		if ((*in)[0]=='s') {
			o->type = T_MASK;
			s = (unsigned char *) " \t]";
			r_comp_set(o,&s);
			(*in)++;
			r_comp_modifier(o,in);
		} else if ((*in)[0]=='S') {
			o->type = T_MASK;
			s = (unsigned char *) "^ \t]";
			r_comp_set(o,&s);
			(*in)++;
			r_comp_modifier(o,in);
		} else if ((*in)[0]=='w') {
			o->type = T_MASK;
			s = (unsigned char *) "0-9a-zA-Z_]";
			r_comp_set(o,&s);
			(*in)++;
			r_comp_modifier(o,in);
		} else if ((*in)[0]=='W') {
			o->type = T_MASK;
			s = (unsigned char *) "^0-9a-zA-Z_";
			r_comp_set(o,&s);
			(*in)++;
			r_comp_modifier(o,in);
		} else if ((*in)[0]=='d') {
			o->type = T_MASK;
			s = (unsigned char *) "0-9]";
			r_comp_set(o,&s);
			(*in)++;
			r_comp_modifier(o,in);
		} else if ((*in)[0]=='D') {
			o->type = T_MASK;
			s = (unsigned char *) "^0-9]";
			r_comp_set(o,&s);
			(*in)++;
			r_comp_modifier(o,in);
		} else if ((*in)[0]=='b') {
			o->type = T_BOUNDARY;
			(*in)++;
		} else if ((*in)[0]=='B') {
			o->type = T_NBOUNDARY;
			(*in)++;
		} else if ((*in)[0]=='x') {
			o->type = T_CHAR;
			(*in)++; /* skip the x */
			o->c = get_hex(*in);
			(*in) += 2;
			r_comp_modifier(o,in);
			return o;
		} else if ((*in)[0]=='r') {
			o->type = T_CHAR;
			o->c = '\r';
			(*in)++;
			r_comp_modifier(o,in);
			return o;
		} else if ((*in)[0]=='n') {
			o->type = T_CHAR;
			o->c = '\n';
			(*in)++;
			r_comp_modifier(o,in);
			return o;
		} else if ((*in)[0]=='t') {
			o->type = T_CHAR;
			o->c = '\t';
			(*in)++;
			r_comp_modifier(o,in);
			return o;
		} else {
			o->type = T_CHAR;
			o->c = **in;
			(*in)++;
			r_comp_modifier(o,in);
			return o;
		}
		break;
	case '^': /* . start of line  */
		o->type = T_START;
		r_comp_modifier(o,in);
		break;
	case '$': /* . end of line  */
		o->type = T_END;
		r_comp_modifier(o,in);
		break;
	case '|': /* . Or   */
		/*if (**in=='(') (*in)++;*/
		o->type = T_OR_TOP;
		break;
	case '(': /* . Or list */
		  /* (?!=lookahead  (?:=normal*/
		if (((*in)[0]=='?') && ((*in)[1]=='!')) {
			(*in) += 2;
			o2 = r_comp_all(in);
			o->or = o2;
			o->type = T_NOT;
		} else {
			if (((*in)[0]=='?') && ((*in)[1]==':')) (*in) += 2;
			o2 = r_comp_all(in);
			o->or = o2;
			o->type = T_GROUP;
			r_comp_modifier(o,in);
		}
		break;
	case ')': /* . Or list "...)|(..." or "...)"   */
		reg_freeobj(o);
		return NULL;
		/*if (**in!='|') {
			return NULL;
		}
		o->type = T_OR_TOP;
		(*in)+=2;
		*/
		break;
	default:
		o->type = T_CHAR;
		o->c = c;
		dbg imsg("New Object %p c=%c\n",o,c);
		r_comp_modifier(o,in);
		break;
	}
	return o;
}

/*
Deal with [afasfdasda-z1-3]
Deal with and [^afasfdasda-z1-3]
*/
int r_comp_set(Obj *o,unsigned char **in)
{
	char mask[MAXCHAR];
	unsigned char *s;
	unsigned int k;
	unsigned char c,c2;
	int i;
	int isnot=FALSE;
	for (i=0; i<MAXCHAR; i++) mask[i] = FALSE;
	if (**in=='^') {isnot=TRUE; (*in)++;}
	for (s=*in; *s!=0; s++) {
		if (*s==']') {s++; break;}
		c = *s;

/*
	s  \t
	S ^ \t
	w 0-9a-zA-Z_
	W ^0-9a-zA-Z_
	d 0-9a-zA-Z_
	D ^0-9a-zA-Z_
	t tab
*/
		if (c=='\\') {
			s++;
			c = *s;
			if (c=='r') c = '\r';
			if (c=='n') c = '\n';
			if (c=='t') c = '\t';
			if (c=='x') {
				c = get_hex(s+1);
				s++;
				if (*s!=0) s++;
			}
			if (c=='s') {
				mask[' '] = TRUE;
				mask['\t'] = TRUE;
				continue;
			}
			if (c=='S') {
				for (i=0; i<255; i++) {
					if (i==' ') continue;
					if (i=='\t') continue;
					mask[i] = TRUE;
				}
				continue;
			}
			if (c=='w') {
				for (i=0; i<255; i++) {
					if (isalnum(i)) mask[i] = TRUE;
					if (i=='_') mask[i] = TRUE;
				}
				continue;
			}
			if (c=='W') {
				for (i=0; i<255; i++) {
					if (!isalnum(i)) if (i!='_') mask[i] = TRUE;
				}
				continue;
			}
			if (c=='d') {
				for (i=0; i<255; i++) {
					if (isdigit(i)) mask[i] = TRUE;
				}
				continue;
			}
			if (c=='D') {
				for (i=0; i<255; i++) {
					if (!isdigit(i)) mask[i] = TRUE;
				}
				continue;
			}
		}
		mask[c] = TRUE;
		if (s[1]=='-') {
			s += 2;
			c2 = *s;
			if (c2=='\\') {
				s++;
				c2 = *s;
				if (c2=='r') c2 = '\r';
				if (c2=='n') c2 = '\n';
				if (c2=='t') c2 = '\t';
				if (c2=='x') {
					c2 = get_hex(s+1);
					s++;
					if (*s!=0) s++;
				}
			}
			for (k=c;k<=c2;k++) {
				if (k>=MAXCHAR) break;
				mask[k] = TRUE;
			}
		}
		if (*s==0) break;
	}
	*in = s;
	if (isnot) {
		for (i=0; i<MAXCHAR; i++) mask[i] = (char) (!mask[i]);
	}

/*for (i=0; i<MAXCHAR; i++) if (mask[i]) printf("Masked [%d] %c\n",i,isprint(i) ? i : '.');*/

	o->mask = malloc(MAXCHAR);
	memcpy(o->mask,mask,MAXCHAR);
	return TRUE;
}
static int depth;
void reg_show(Obj *o)
{
	depth++;
	if (o==NULL) {depth--; return;}
	dbg printf("Show object List %p type=%s (%c)\n",o,rtype[o->type],o->c);
	for (;o!=NULL;o=o->next) {
		reg_show_obj(o);
		if ((o->type==T_GROUP) ||(o->type==T_OR) || (o->type==T_NOT)) {
			reg_show(o->or);
		}
	}
	depth--;
}
void reg_show_obj(Obj *o)
{
	dbg printf("%p %d Obj type=%s, c='%c' %d-%d mask %p, or=%p next=%p\n",
		o,depth,rtype[o->type],o->c,o->min,o->max,o->mask,o->or,o->next);
}
#ifdef WIN32
#define vsnprintf _vsnprintf
#endif
int rfail(const char * arg_list, ...)
{
        va_list arg_ptr;
        char *format;
        va_start(arg_ptr, arg_list);
        format = (char *) arg_list;
        vsnprintf(failmsg,400, format, arg_ptr);
	va_end(arg_ptr);
	rcode = 1;
	return 0;
}
char *reg_find(char *xin, regex_t *r)
{
	Obj *ocopy;
	unsigned char *incopy;
	unsigned char *mask;
	unsigned char *in= (unsigned char *) xin;
	bol = in;

	mask = 	r->list->mask;
	strcpy(failmsg,"None");
	for (;*in!=0;in++) {
		if (mask[(unsigned char) (*in)]) {
			ocopy = r->list;
			incopy = in;
			dbg imsg("=======reg_find_mask(%s)\n",in);
			if (reg_match(&incopy,&ocopy)) {
				strncpy(failmsg,(char *) in,30);
				failmsg[20] = 0;
				return (char *) in;
			}
		}
	}
	return NULL;
}
int reg_find_start(char *in, regex_t *r)
{
	Obj *ocopy;
	unsigned char *incopy;
	bol = (unsigned char *) in;
	ocopy = r->list;
	incopy = (unsigned char *) in;
	if (reg_match(&incopy,&ocopy)) return TRUE;
	return FALSE;
}
int reg_match(unsigned char **in,Obj **o)
{
	Obj *ocopy;
	int r;
	char *ini;
	if (*o==NULL) {
		printf("End of objects\n");
		if (best<(*in)) best = *in;
		return TRUE;
	}
	ini=*in;
	depth++;
	if (depth>500) {
		imsg("Stack overflow in reg_match()\n");
		depth--;
		return FALSE;
	}
	dbg printf("reg_match(%d) (%s)\n",depth,*in);
	for (;*o!=NULL;) {
		if ((*o)->type==T_OR) {
			ocopy = (*o)->or;
			if (ocopy!=NULL) {
				r = reg_match(in,&ocopy);
				if (r) {
					/* *o = ocopy;*/
					if (best<(*in)) best = *in;
/* Chris experiment, I think it should keep trying for all objects in list */
					depth--;
					return TRUE;
				}
			}
			*o = (*o)->next;
			continue;
		}
		if (reg_match_list(in,o)) {
			if (best<(*in)) best = *in;
			if(copy_dst) {
				copy_dst(ini,best);
			}
			depth--;
			return TRUE;
		}
		else break;
	}
	if (best<(*in)) best = *in;
	depth--;
	return FALSE;
}
int reg_match_list(unsigned char **in, Obj **ocur)
{
	unsigned char *s,*ss;
	int r;
	s = *in;
	dbg printf("Match_list (%s)\n",*in);
	for (;(*ocur)!=NULL;) {
		ss = s;
		r = reg_match_one(&s,ocur);
		dbg printf("reg_match_one %d (%s) (%s)\n",r,ss,s);
		if (!r) {
			if (best<(s)) best = s;
			return FALSE;
		}
		/* /if (r) if (*s==0) break; */
	}
	dbg printf("reg_match_list_succeded\n");
	*in = s;
	if (best<(*in)) best = *in;
	return TRUE;
}
int reg_match_next(unsigned char **in, Obj **o)
{
	unsigned char *xin;
	int r;
	Obj *xo;
	xo = (*o)->next;
	xin = *in; /* ++xin; */
	r = reg_match(&xin,&xo);
	if (r) {
		*in = xin;
		*o = xo;
	}
	if (best<(xin)) best = xin;
	return r;
}
static const unsigned char charmap[] = {
        0000, 0001, 0002, 0003, 0004, 0005, 0006, 0007,
        0010, 0011, 0012, 0013, 0014, 0015, 0016, 0017,
        0020, 0021, 0022, 0023, 0024, 0025, 0026, 0027,
        0030, 0031, 0032, 0033, 0034, 0035, 0036, 0037,
        0040, 0041, 0042, 0043, 0044, 0045, 0046, 0047,
        0050, 0051, 0052, 0053, 0054, 0055, 0056, 0057,
        0060, 0061, 0062, 0063, 0064, 0065, 0066, 0067,
        0070, 0071, 0072, 0073, 0074, 0075, 0076, 0077,
        0100, 0141, 0142, 0143, 0144, 0145, 0146, 0147,
        0150, 0151, 0152, 0153, 0154, 0155, 0156, 0157,
        0160, 0161, 0162, 0163, 0164, 0165, 0166, 0167,
        0170, 0171, 0172, 0133, 0134, 0135, 0136, 0137,
        0140, 0141, 0142, 0143, 0144, 0145, 0146, 0147,
        0150, 0151, 0152, 0153, 0154, 0155, 0156, 0157,
        0160, 0161, 0162, 0163, 0164, 0165, 0166, 0167,
        0170, 0171, 0172, 0173, 0174, 0175, 0176, 0177,
        0200, 0201, 0202, 0203, 0204, 0205, 0206, 0207,
        0210, 0211, 0212, 0213, 0214, 0215, 0216, 0217,
        0220, 0221, 0222, 0223, 0224, 0225, 0226, 0227,
        0230, 0231, 0232, 0233, 0234, 0235, 0236, 0237,
        0240, 0241, 0242, 0243, 0244, 0245, 0246, 0247,
        0250, 0251, 0252, 0253, 0254, 0255, 0256, 0257,
        0260, 0261, 0262, 0263, 0264, 0265, 0266, 0267,
        0270, 0271, 0272, 0273, 0274, 0275, 0276, 0277,
        0300, 0301, 0302, 0303, 0304, 0305, 0306, 0307,
        0310, 0311, 0312, 0313, 0314, 0315, 0316, 0317,
        0320, 0321, 0322, 0323, 0324, 0325, 0326, 0327,
        0330, 0331, 0332, 0333, 0334, 0335, 0336, 0337,
        0340, 0341, 0342, 0343, 0344, 0345, 0346, 0347,
        0350, 0351, 0352, 0353, 0354, 0355, 0356, 0357,
        0360, 0361, 0362, 0363, 0364, 0365, 0366, 0367,
        0370, 0371, 0372, 0373, 0374, 0375, 0376, 0377
};
int reg_match_one(unsigned char **in, Obj **ocur)
{
	unsigned char *s = (unsigned char *) *in;
	int i;
	Obj *ocopy;
	Obj *o = *ocur;
	unsigned char *ss;

	if(!o) return 0;

	dbg printf("From (%s)\n",*in);
	reg_show_obj(o);

	switch (o->type) {
	case T_CHAR:
		for (i=0; i<o->max; i++) {
			if (icase) {
				if (charmap[s[i]]!=charmap[o->c]) break;
			} else {
				if (s[i]!=o->c) break;
			}
			if (s[i]==0) break;
			dbg printf("%d t_char [%d]=%c == %c\n",depth,i,s[i],o->c);
			if (o->max>=(i+1)) if ((i)>=o->min) {
				dbg printf("%d Check rest of string %s\n",depth,*in);
				(*in) += i;
				if (reg_match_next(in,ocur)) return TRUE;
				(*in) -= i;
			}
			if (best<(*in)+i+1) best = *in+i+1;
		}
		dbg printf("%d t_char MATCHED SOME (%c) %d %p (%s)\n",depth,o->c,i,o,*in+i);
		if (i<o->min) return FALSE;
		*in += i;
		break;
	case T_MASK:
		for (i=0; i<o->max; i++) {
			if (s[i]==0) break;
			dbg printf("%d t_mask [%d]=%c =mask\n",depth,i,s[i]);
			if (!o->mask[s[i]]) break;
			if (o->max>=(i+1)) if ((i)>=o->min) {
				(*in) += i;
				if (reg_match_next(in,ocur)) return TRUE;
				(*in) -= i;
			}
			if (best<(*in)+i+1) best = *in+i+1;
		}
		dbg printf("%d t_mask MATCHED SOME  %d (%s)\n",depth,i,*in+i);
		if (i<o->min) return FALSE;
		*in += i;
		break;
	case T_ANY:
		for (i=0; i<o->max; i++) {
			if (s[i]==0) break;
			dbg printf("%d t_any [%d]=%c \n",depth,i,s[i]);
			if (o->max>=(i+1)) if ((i)>=o->min) {
				(*in)+= i;
				dbg printf("---> %d t_any [%d]=%c rest (%s)\n",depth,i,s[i],*in);
				if (reg_match_next(in,ocur)) return TRUE;
				(*in)-= i;
			}
			if (best<(*in)+i+1) best = *in+i+1;
		}
		dbg printf("%d t_anyMATCHED SOME  %d (%s)\n",depth,i,*in+i);
		if (i<o->min) return FALSE;
		*in += i;
		break;
	case T_BOUNDARY: /*  \bcat\b  .cat.*/
		*ocur = (*ocur)->next;
		if (*in==bol) return TRUE;
		if (xisword[**in]!=xisword[*((*in)-1)]) return TRUE;
		return FALSE;
	case T_NBOUNDARY: /*  \bcat\b  .cat.*/
		*ocur = (*ocur)->next;
		if (*in==bol) return FALSE;
		if (xisword[**in]!=xisword[*((*in)-1)]) return FALSE;
		return TRUE;
	case T_START:
		*ocur = (*ocur)->next;
		if (*in!=bol) return FALSE;
		return TRUE;
	case T_END:
		if (s[0]==0) break;
		if (s[0]=='\r') break;
		if (s[0]=='\n') break;
		*ocur = (*ocur)->next;
		return FALSE;
	case T_NOT:
		ocopy = o->or;
		if (reg_match(&s,&ocopy)) {
			return FALSE;
		}
		*ocur = (*ocur)->next;
		return TRUE;
	case T_OR:
		ocopy = o;
		if (reg_match(&s,&ocopy)) {
			*in = s;
			/* *ocur = ocopy; */
			*ocur = (*ocur)->next;
			return TRUE;
		}
		*ocur = (*ocur)->next;
		return FALSE;
	case T_GROUP:
		for (i=0; i<o->max; i++) {
			dbg printf("%d t_group [%d]=%c \n",depth,i,s[i]);
			if (s[i]==0) break;
			if (o->max>=(i+1)) if ((i)>=o->min) {
				(*in) += i;
				if (reg_match_next(in,ocur)) return TRUE;
				(*in) -= i;
			}
			ss = s;
			ocopy = o->or;
			if (reg_match(&ss,&ocopy)) {
				s = ss;
			} else break;
			if (best<(s)) best = s;
		}
		if (i<o->min) return FALSE;
		*in = s;
		*ocur = (*ocur)->next;
		return TRUE;
	}
	*ocur = (*ocur)->next;
	return TRUE;
}

#undef printf
#ifdef TESTONLY
int imsg(const char * arg_list, ...)
#else
int imsg_regex(const char * arg_list, ...)
#endif
{
        va_list arg_ptr;
        char *format;
	FILE *f;
	char output[BFSZ];
        va_start(arg_ptr, arg_list);
        format = (char *) arg_list;
        vsnprintf(output,400, format, arg_ptr);
	va_end(arg_ptr);

	printf("%s",output);
	f = fopen("r.log","a");
	if (f==NULL) return 0;
	fprintf(f,"%s",output);
	fclose(f);
	return 0;
}
