#include	<ctype.h>

#include	"ubermud.h"
#include	"externs.h"

/*
#define	MATCHDEBUG
*/


/*
Copyright(C) 1990, Marcus J. Ranum, All Rights Reserved.
This software may be freely used, modified, and redistributed,
as long as this copyright message is left intact, and this
software is not used to develop any commercial product, or used
in any product that is provided on a pay-for-use basis.
*/

struct	mtch	{
	Oper	op;
	int	typ;
	char	*fp;
};

static	struct	mtch	mlist[MAXMATCHLIST];
static	int		tomatch;



/*
return the # of bytes of matching prefix, or -1 to indicate an
UN-match - this assumes s2 is the user-provided string.
*/
static	int
prefixlen(s1,s2,flg)
register char	*s1;
register char	*s2;
int	*flg;
{
	register int	rv = 0;

#ifdef	MATCHDEBUG
	printf("prefixlen compare \"%s\" and \"%s\"\n",s1,s2);
#endif

	/* match while match can */
	while(*s1 != '\0' && *s2 != '\0') {
		if((isupper(*s1) ? tolower((int)*s1) : *s1) !=
			(isupper(*s2) ? tolower((int)*s2) : *s2))
			return(-1);
		rv++, s1++, s2++;
	}

	/*
	if there is more to the user-provided string, and not in
	the string we are matching against, abort the match.
	*/
	if(*s1 == '\0' && *s2 != '\0')
		return(-1);

	/* flag complete matches */
	if(*s1 == '\0' && *s2 == '\0')
		*flg = *flg + 1;

#ifdef	MATCHDEBUG
	printf("prefixlen returns %d (flg=%d)\n",rv,*flg);
#endif
	return(rv);
}




/*
vectorise and match.

REMEMBER - you CANNOT modify the data in memory here, as it is a
pointer directly into the copy in cache, and you will effectively
alter the cache or stack storage if you touch this!!  fun - but
don't do it!!
*/
static	int
matchvector(s1,s2,eflg)
char	*s1;
char	*s2;
int	*eflg;
{
	int	rv = 0;

	/* buffers to tokenize into */
	char	mbuf1[MUDBUFSIZ];
	char	mbuf2[MUDBUFSIZ];
	char	*mvec1[MUDMAXARG];
	char	*mvec2[MUDMAXARG];
	int	mac1;
	int	mac2;
	char	**vp1;
	char	**vp2;
	int	exak = 0;

	/* chopper-pointers */
	char	*p1 = s1;
	char	*p2 = s1;

	/* tokenize the first input string */
	if((mac2 = enargv(s2,mvec2,MUDMAXARG,mbuf2,MUDBUFSIZ)) == 0)
		return(0);

	/* s1 is allowed to have aliases... */
	while(*p1 != '\0') {
		register int	mv;
		register int	tmv;

		/* reset the exact match counter */
		*eflg = 0;

		while(*p2 != '\0' && *p2 != MATCH_CHAR)
			p2++;

		/* EVIL! */
		if(*p2 == MATCH_CHAR) {
			/*
			kludge - temporarily NULL-terminate the buffer
			before passing it to enargv, then repair it.
			remember - this is what I just warned you NOT
			to do ? we're mangling bits in the stack...
			*/
			*p2 = '\0';
			mac1 = enargv(s1,mvec1,MUDMAXARG,mbuf1,MUDBUFSIZ);
			*p2 = MATCH_CHAR;
			p2++;
		} else {
			mac1 = enargv(s1,mvec1,MUDMAXARG,mbuf1,MUDBUFSIZ);
		}

		/* if there are no tokens, or the user has given more */
		if(mac1 == 0 || mac2 > mac1) {
#ifdef	MATCHDEBUG
			printf("vectormatch: token count mismatch, skip.\n");
#endif
			goto failed;
		}

		/* loop/match across the match vectors */
		for(vp1 = mvec1; *vp1 != (char *)0; vp1++) {
			char	**tmpvp = vp1;
			for(mv = 0, vp2 = mvec2; *vp2 != (char *)0 && *tmpvp != (char *)0; vp2++, tmpvp++) {
				if((tmv = prefixlen(*tmpvp,*vp2,eflg)) == -1) {
#ifdef	MATCHDEBUG
					printf("bad prefix, scrub.\n");
#endif
					mv = -1;
					*eflg = 0;
					break;
				}
				mv += tmv;
			}
			if(*tmpvp == (char *)0 && *vp2 != (char *)0) {
#ifdef	MATCHDEBUG
				printf("token count mismatch, scrub.\n");
#endif
				mv = -1;
				exak = 0;
				*eflg = 0;
			}
			if(mv > rv) {
#ifdef	MATCHDEBUG
				printf("take %d as new best byte count.\n",mv);
#endif
				exak = *eflg;
				rv = mv;
			}
		}

/* failed just drops through */
failed:
		/* ready to match next hunk */
		p1 = p2;
	}

	*eflg = exak;

#ifdef	MATCHDEBUG
	printf("match value is %d, (flg=%d)\n",rv,*eflg);
#endif
	return(rv);
}



/*
reset the stored match pointers
*/
void
matchreset()
{
	tomatch = 0;
}



/*
set a pointer into the match list for the next run. these pointers
cheerfully dangle when the objectlists are freed at the end of a 
run. make damn sure you do a matchreset between calls to match.
*/
matchset(op,typ,fld)
Oper	op;
int	typ;
char	*fld;
{
	if(tomatch >= MAXMATCHLIST)
		return(1);
	mlist[tomatch].op = op;
	mlist[tomatch].typ = typ;
	mlist[tomatch].fp = fld;
	tomatch++;
	return(0);
}



static	int
matchcompare(thing,nam,uid,euid,eflg)
char	*thing;
char	*nam;
long	uid;
long	euid;
int	*eflg;
{
	Oper	d1;
	int	d1t;
	int	tval;


	/* get the thing from disk */
	if(ELENUM(thing) == (long)0)
		tval = sys_thaw(thing,&d1t,&d1,uid,euid);
	else
		tval = disk_thaw(thing,&d1t,&d1,uid,euid);

	/* if it didn't thaw, or is ! a string skip it */
	if(tval != 0 || d1t != TYP_STR)
		return(0);

	return(matchvector(d1.c,nam,eflg));
}



/*
perform a match against the object lists by field in the match list
*/
long
match(nam,uid,euid)
char	*nam;
long	uid;
long	euid;
{
	static	char	*ibuf = (char *)0;
	int	x;
	int	best = 0;
	long	ret = (long)0;
	int	tval;

	/* flags to control exact matching - kinda kludgy */
	int	exact;
	int	exactcnt = 0;

	if(ibuf == (char *)0) {
		ibuf = malloc(MAXIDENTLEN * 2);
		if(ibuf == (char *)0)
			fatal("alloc ibuf in match failed: ",(char *)-1,"\n",0);
	}

	if(*nam == '\0')
		return((long)0);

	for(x = 0; x < tomatch; x++) {
		/* set field name */
		if(mlist[x].fp == (char *)0)
			continue;

		(void)strcpy(ELENAM(ibuf),mlist[x].fp);

		if(mlist[x].typ == TYP_OLIST) {
			int	lcnt = mlist[x].op.ol->l_cnt;
			long	*bobp = mlist[x].op.ol->l_data;

			while(lcnt--) {
				exact = 0;
				ELENUM(ibuf) = *bobp++;

				tval = matchcompare(ibuf,nam,uid,euid,&exact);

				if(tval == best && exact && exact > exactcnt) {
					best = tval;
					exactcnt = exact;
					ret = ELENUM(ibuf);
				}

				if(tval > best) {
					best = tval;
					ret = ELENUM(ibuf);
				}
			}
		} else
		if(mlist[x].typ == TYP_OBJ) {
			exact = 0;
			ELENUM(ibuf) = mlist[x].op.l;

			tval = matchcompare(ibuf,nam,uid,euid,&exact);
			if(tval == best && exact && exact > exactcnt) {
				best = tval;
				exactcnt = exact;
				ret = ELENUM(ibuf);
			}
			if(tval > best) {
				best = tval;
				ret = ELENUM(ibuf);
			}
		}
	}
	return(ret);
}



void
blt_match(m,argc,firstarg,result,uid,euid)
Machine	*m;
int	argc;
int	firstarg;
int	result;
long	*uid;
long	*euid;
{
	char	*np;
	int	x;
	long	matv;

	if(argc < 3 || m->m_mem[firstarg].typ != TYP_STR) {
		m->m_mem[result].oper.i = ERR_BADARG;
		m->m_mem[result].typ = TYP_NULL;
		return;
	}

	np = m->m_mem[firstarg].oper.c;

	matchreset();

	for(x = 1; x < argc; x += 2) {
		if(m->m_mem[firstarg + x + 1].typ != TYP_STR)
			continue;

		if(matchset(m->m_mem[firstarg + x].oper,
			m->m_mem[firstarg + x].typ,
			m->m_mem[firstarg + x + 1].oper.c)) {

			m->m_mem[result].oper.i = ERR_BADARG;
			m->m_mem[result].typ = TYP_NULL;
			return;
		}
	}

	matv = match(np,*uid,*euid);

#ifdef	MATCHDEBUG
	printf("blt_match:match returns %d\n",matv);
#endif

	if(matv == (long)0) {
		m->m_mem[result].oper.i = ERR_NOTHERE;
		m->m_mem[result].typ = TYP_NULL;
		return;
	}
	m->m_mem[result].oper.l = matv;
	m->m_mem[result].typ = TYP_OBJ;
}


/*
#ifdef	MATCHDEBUG
		printf("matchvector: matching across %s -> %s\n",p1,sp);
#endif

		while(*p1 != '\0' && *p1 != MATCH_CHAR && *sp != '\0') {
			c1 = isupper(*p1) ? tolower((int)*p1) : *p1;
			c2 = isupper(*sp) ? tolower((int)*sp) : *sp;

			if(c1 == c2) {
				mv++;
#ifdef	MATCHDEBUG
				printf("matchvector: match %d\n",mv);
#endif
			} else {
#ifdef	MATCHDEBUG
				printf("matchvector: stop matching\n");
#endif
				mv = 0;
				break;
			}
			sp++;
			p1++;
		}


		p1 = p2;

		if((*p1 == '\0' || *p1 == MATCH_CHAR) && *sp != '\0')
			continue;

		if(mv > rv) {
#ifdef	MATCHDEBUG
			printf("matchvector: new best match %d old, %d\n",mv,rv);
#endif
			rv = mv;
*/