#include	<stdio.h>
#include	<sys/file.h>

/* henry spencer's regexp, not the system's. */
#include	"regexp.h"

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


/*
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.
*/

/*
built-in primitives - simple stuff like str(), and so on. more
built-ins reside in list.c - these are all called through the
dispatch table in blttab.c

note - in all these functions, you CANNOT assign a stack value
to the result argument, until AFTER you are done processing
all the stuff in the stack - because result is ALSO firstarg
in some cases, and assigning result will overwrite the argument
stack
*/


static	char	*liststr = "<list>";
static	char	*funcstr = "<function>";

/* error message table. in ubermud, an error message is a data type */
static	char	*errtbl[] = {
/* ERR_NONE	0 */	"no error",
/* ERR_USER	1 */	"error",
/* ERR_OOM	2 */	"out of memory",
/* ERR_NUM	3 */	"numeric operation on non-number",
/* ERR_ZDIV	4 */	"division by zero",
/* ERR_BADOBJ	5 */	"badly formed element specifier",
/* ERR_BADARG	6 */	"bad parameter type",
/* ERR_NOTHERE	7 */	"nonexistent object",
/* ERR_REF	8 */	"cannot reference object",
/* ERR_NOVAL	9 */	"function returned no value",
/* ERR_NOPAR	10 */	"no such parameter",
/* ERR_DBASE	11 */	"I/O error (this is bad!)",
/* ERR_PERM	12 */	"permission denied",
/* ERR_NOTOWN	13 */	"not owner",
/* ERR_STACK	14 */	"stack over/underflow",
0
};


char	*errmsg(x)
int	x;
{
	return(errtbl[x]);
}


/*
atoi. overload NUM and OBJ already.
*/
void
blt_atoi(m,argc,firstarg,result,uid,euid)
Machine	*m;
int	argc;
int	firstarg;
int	result;
long	*uid;
long	*euid;
{
	if(argc != 1) {
		m->m_mem[result].typ = TYP_NUM;
		m->m_mem[result].oper.i = 0;
		return;
	}

	switch(m->m_mem[firstarg].typ) {
		case	TYP_STR:
		m->m_mem[result].oper.i = atoi(m->m_mem[firstarg].oper.c);
		break;

		case	TYP_OBJ:
		m->m_mem[result].oper.i = (int)m->m_mem[firstarg].oper.l;
		break;

		case	TYP_NUM:
		m->m_mem[result].oper.i = m->m_mem[firstarg].oper.i;
		break;

		default:
		m->m_mem[result].oper.i = 0;
		break;
	}
	m->m_mem[result].typ = TYP_NUM;
}



/*
atoobj. overload NUM and OBJ already.
*/
void
blt_atoobj(m,argc,firstarg,result,uid,euid)
Machine	*m;
int	argc;
int	firstarg;
int	result;
long	*uid;
long	*euid;
{
	if(argc != 1) {
		m->m_mem[result].typ = TYP_NULL;
		m->m_mem[result].oper.i = ERR_BADARG;
		return;
	}

	switch(m->m_mem[firstarg].typ) {
		case	TYP_STR:
		if(*m->m_mem[firstarg].oper.c == '#') {
			m->m_mem[result].oper.l = atol(&m->m_mem[firstarg].oper.c[1]);
		} else {
			m->m_mem[result].oper.l = atol(m->m_mem[firstarg].oper.c);
		}
		m->m_mem[result].typ = TYP_OBJ;
		break;

		case	TYP_OBJ:
		m->m_mem[result].oper.l = m->m_mem[firstarg].oper.l;
		m->m_mem[result].typ = TYP_OBJ;
		break;

		case	TYP_NUM:
		m->m_mem[result].oper.l = (long)m->m_mem[firstarg].oper.i;
		m->m_mem[result].typ = TYP_OBJ;
		break;

		default:
		m->m_mem[result].oper.i = ERR_BADOBJ;
		m->m_mem[result].typ = TYP_NULL;
		break;
	}
	if(m->m_mem[result].typ != TYP_NULL && m->m_mem[result].oper.l == (long)0) {
		m->m_mem[result].oper.i = ERR_BADOBJ;
		m->m_mem[result].typ = TYP_NULL;
	}
}




/*
dump the contents of the file named to the object spec'd as first argument
*/
void
blt_catfile(m,argc,firstarg,result,uid,euid)
Machine	*m;
int	argc;
int	firstarg;
int	result;
long	*uid;
long	*euid;
{
	long	who;
	int	fd;
	int	red;
	char	buf[MUDBUFSIZ];
	char	*p;

	if(argc != 2 || m->m_mem[firstarg].typ != TYP_OBJ ||
		m->m_mem[firstarg + 1].typ != TYP_STR) {

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

	/* no files with '..' in them */
	for(p = m->m_mem[firstarg + 1].oper.c; *p != '\0'; p++)
		if(*p == '.' && *(p + 1) == '.') {
			m->m_mem[result].oper.i = ERR_BADARG;
			m->m_mem[result].typ = TYP_NULL;
			return;
		}

	/* no files starting with '/' */
	if(*m->m_mem[firstarg + 1].oper.c == '/') {
		m->m_mem[result].oper.i = ERR_BADARG;
		m->m_mem[result].typ = TYP_NULL;
		return;
	}

	/*
	heh. put them in a "files" directory - otherwise they
	can steal the password file.
	*/
	(void)sprintf(buf,"files/%s",m->m_mem[firstarg + 1].oper.c);
	who = m->m_mem[firstarg].oper.l;

	if((fd = open(buf,O_RDONLY)) < 0) {
		iobtell(who,"cannot open ",buf,": ",sys_errlist[errno],"\n",0);
		m->m_mem[result].oper.i = ERR_BADARG;
		m->m_mem[result].typ = TYP_NULL;
	}

	while((red = read(fd,buf,MUDBUFSIZ - 1)) > 0) {
		buf[red] = '\0';
		iobtell(who,buf,0);
	}

	(void)close(fd);
	m->m_mem[result].oper.i = 0;
	m->m_mem[result].typ = TYP_NUM;
}




/*
disconnect connections owned by XX
*/
void
blt_disconnect(m,argc,firstarg,result,uid,euid)
Machine	*m;
int	argc;
int	firstarg;
int	result;
long	*uid;
long	*euid;
{
	if(*uid != (long)0 && *euid != (long)0) {
		m->m_mem[result].oper.i = ERR_PERM;
		m->m_mem[result].typ = TYP_NULL;
		return;
	}

	while(argc--) {
		if(m->m_mem[firstarg].typ == TYP_OBJ)
			iobdisconnect(m->m_mem[firstarg].oper.l);
		firstarg++;
	}
	m->m_mem[result].oper.i = 0;
	m->m_mem[result].typ = TYP_NUM;
}



/*
echo arguments. some operator overloading is done to handle
ints/strings intelligently.
*/
void
blt_echo(m,argc,firstarg,result,uid,euid)
Machine	*m;
int	argc;
int	firstarg;
int	result;
long	*uid;
long	*euid;
{
	while(argc--) {
		switch(m->m_mem[firstarg].typ) {
			case	TYP_STR:
			iobtell(*uid,m->m_mem[firstarg].oper.c,0);
			break;

			case	TYP_OBJ:
			iobtell(*uid,"#",ltoa(m->m_mem[firstarg].oper.l),0);
			break;

			case	TYP_FUNC:
			iobtell(*uid,funcstr,0);
			break;

			case	TYP_OLIST:
			iobtell(*uid,liststr,0);
			break;

			case	TYP_NUM:
			iobtell(*uid,itoa(m->m_mem[firstarg].oper.i),0);
			break;
		}
		firstarg++;
	}

	/* echo does not return a value. put a NULL on the stack */
	m->m_mem[result].typ = TYP_NULL;
	m->m_mem[result].oper.i = ERR_NONE;
}



/*
echo arguments TO someone. some operator overloading is done to handle
ints/strings intelligently.
*/
void
blt_echoto(m,argc,firstarg,result,uid,euid)
Machine	*m;
int	argc;
int	firstarg;
int	result;
long	*uid;
long	*euid;
{
	long	who;

	if(m->m_mem[firstarg].typ != TYP_OBJ) {
		m->m_mem[result].typ = TYP_NULL;
		m->m_mem[result].oper.i = ERR_BADARG;
		return;
	}

	who = m->m_mem[firstarg].oper.l;

	firstarg++;
	argc--;

	while(argc--) {
		switch(m->m_mem[firstarg].typ) {
			case	TYP_STR:
			iobtell(who,m->m_mem[firstarg].oper.c,0);
			break;

			case	TYP_OBJ:
			iobtell(who,"#",ltoa(m->m_mem[firstarg].oper.l),0);
			break;

			case	TYP_FUNC:
			iobtell(who,funcstr,0);
			break;

			case	TYP_OLIST:
			iobtell(who,liststr,0);
			break;

			case	TYP_NUM:
			iobtell(who,itoa(m->m_mem[firstarg].oper.i),0);
			break;
		}
		firstarg++;
	}

	/* echo does not return a value. put a NULL on the stack */
	m->m_mem[result].typ = TYP_NULL;
	m->m_mem[result].oper.i = ERR_NONE;
}



/*
return numerical error
*/
void
blt_errno(m,argc,firstarg,result,uid,euid)
Machine	*m;
int	argc;
int	firstarg;
int	result;
long	*uid;
long	*euid;
{
	if(argc < 1 || m->m_mem[firstarg].typ != TYP_NULL)
		m->m_mem[result].oper.i = ERR_NONE;
	else
	m->m_mem[result].oper.i = m->m_mem[firstarg].oper.i;
	m->m_mem[result].typ = TYP_NUM;
}



/*
return string error
*/
void
blt_error(m,argc,firstarg,result,uid,euid)
Machine	*m;
int	argc;
int	firstarg;
int	result;
long	*uid;
long	*euid;
{
	if(argc < 1 || m->m_mem[firstarg].typ != TYP_NULL)
		m->m_mem[result].oper.c = "";
	else
	m->m_mem[result].oper.c = errtbl[m->m_mem[firstarg].oper.i];
	m->m_mem[result].typ = TYP_STR;
}




/*
return the person's effective user-id.
*/
void
blt_geteuid(m,argc,firstarg,result,uid,euid)
Machine	*m;
int	argc;
int	firstarg;
int	result;
long	*uid;
long	*euid;
{
	m->m_mem[result].oper.i = *euid;
	m->m_mem[result].typ = TYP_OBJ;
}




/*
return the person's real user-id.
*/
void
blt_getuid(m,argc,firstarg,result,uid,euid)
Machine	*m;
int	argc;
int	firstarg;
int	result;
long	*uid;
long	*euid;
{
	m->m_mem[result].oper.i = *uid;
	m->m_mem[result].typ = TYP_OBJ;
}




/*
return true if the argument is a list.
*/
void
blt_islist(m,argc,firstarg,result,uid,euid)
Machine	*m;
int	argc;
int	firstarg;
int	result;
long	*uid;
long	*euid;
{
	if(argc < 1 || m->m_mem[firstarg].typ != TYP_OLIST)
		m->m_mem[result].oper.i = 0;
	else
		m->m_mem[result].oper.i = 1;
	m->m_mem[result].typ = TYP_NUM;
}



/*
return true if the argument is a number.
*/
void
blt_isnum(m,argc,firstarg,result,uid,euid)
Machine	*m;
int	argc;
int	firstarg;
int	result;
long	*uid;
long	*euid;
{
	if(argc < 1 || m->m_mem[firstarg].typ != TYP_NUM)
		m->m_mem[result].oper.i = 0;
	else
		m->m_mem[result].oper.i = 1;
	m->m_mem[result].typ = TYP_NUM;
}



/*
return true if the argument is an object #.
*/
void
blt_isobj(m,argc,firstarg,result,uid,euid)
Machine	*m;
int	argc;
int	firstarg;
int	result;
long	*uid;
long	*euid;
{
	if(argc < 1 || m->m_mem[firstarg].typ != TYP_OBJ)
		m->m_mem[result].oper.i = 0;
	else
		m->m_mem[result].oper.i = 1;
	m->m_mem[result].typ = TYP_NUM;
}



/*
return true if the argument is a string.
*/
void
blt_isstr(m,argc,firstarg,result,uid,euid)
Machine	*m;
int	argc;
int	firstarg;
int	result;
long	*uid;
long	*euid;
{
	if(argc < 1 || m->m_mem[firstarg].typ != TYP_STR)
		m->m_mem[result].oper.i = 0;
	else
		m->m_mem[result].oper.i = 1;
	m->m_mem[result].typ = TYP_NUM;
}



/*
echo strings into system log
*/
void
blt_log(m,argc,firstarg,result,uid,euid)
Machine	*m;
int	argc;
int	firstarg;
int	result;
long	*uid;
long	*euid;
{
	while(argc--) {
		switch(m->m_mem[firstarg].typ) {
			case	TYP_STR:
			logf(m->m_mem[firstarg].oper.c,0);
			break;

			case	TYP_OBJ:
			logf("#",ltoa(m->m_mem[firstarg].oper.l),0);
			break;

			case	TYP_FUNC:
			logf(funcstr,0);
			break;

			case	TYP_OLIST:
			logf(liststr,0);
			break;

			case	TYP_NUM:
			logf(itoa(m->m_mem[firstarg].oper.i),0);
			break;
		}
		firstarg++;
	}

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




/*
return a random number between 0 and the arg given.
*/
void
blt_rand(m,argc,firstarg,result,uid,euid)
Machine	*m;
int	argc;
int	firstarg;
int	result;
long	*uid;
long	*euid;
{
	static	int	initd = 0;
	int	mod = 0;
	int	gotmod = 0;

	if(!initd) {
		(void)srandom(getpid() * getppid());
		initd++;
	}

	if(argc > 0) {
		if(m->m_mem[firstarg].typ == TYP_NUM) {
			mod = m->m_mem[firstarg].oper.i;
			gotmod++;
		} else {
			m->m_mem[result].oper.i = 0;
			return;
		}
	}

	m->m_mem[result].oper.i = (int)random();
	if(gotmod)
		m->m_mem[result].oper.i %= mod;
	m->m_mem[result].typ = TYP_NUM;
}




/*
perform a regular expression comparison.
*/
void
blt_regcmp(m,argc,firstarg,result,uid,euid)
Machine	*m;
int	argc;
int	firstarg;
int	result;
long	*uid;
long	*euid;
{
	regexp	*re;

	if(argc != 2 || m->m_mem[firstarg].typ != TYP_STR ||
		m->m_mem[firstarg + 1].typ != TYP_STR) {
		m->m_mem[result].oper.i = 0;
		m->m_mem[result].typ = TYP_NUM;
		return;
	}
	re = regcomp(m->m_mem[firstarg + 1].oper.c);
	if(re == (regexp *)0) {
		m->m_mem[result].oper.i = 0;
		m->m_mem[result].typ = TYP_NUM;
		return;
	}
	m->m_mem[result].oper.i = regexec(re,m->m_mem[firstarg].oper.c);
	m->m_mem[result].typ = TYP_NUM;
}




/*
return a section of a string matching the given expression.
expressions can be egrep-like. if there is no match, return
NULL.
*/
void
blt_regexp(m,argc,firstarg,result,uid,euid)
Machine	*m;
int	argc;
int	firstarg;
int	result;
long	*uid;
long	*euid;
{
	regexp	*re;
	int	rx;
	char	*nsp;
	char	*xp;
	char	*yp;
	char	*tp;
	int	l;

	if(argc != 2 || m->m_mem[firstarg].typ != TYP_STR ||
		m->m_mem[firstarg + 1].typ != TYP_STR) {
		m->m_mem[result].oper.i = ERR_BADARG;
		m->m_mem[result].typ = TYP_NULL;
		return;
	}
	re = regcomp(m->m_mem[firstarg + 1].oper.c);
	if(re == (regexp *)0) {
		m->m_mem[result].oper.i = ERR_BADARG;
		m->m_mem[result].typ = TYP_NULL;
		return;
	}
	rx = regexec(re,m->m_mem[firstarg].oper.c);
	if(rx == 0) {
		m->m_mem[result].oper.i = ERR_NOTHERE;
		m->m_mem[result].typ = TYP_NULL;
		return;
	}

	for(l = 0, tp = re->startp[0]; tp != re->endp[0]; tp++)
		l++;

	yp = nsp = tmpalloc((unsigned)l + 1);
	if(nsp == (char *)0) {
		m->m_mem[result].oper.i = ERR_OOM;
		m->m_mem[result].typ = TYP_NULL;
		return;
	}

	xp = re->startp[0];
	while(xp != re->endp[0])
		*yp++ = *xp++;
	*yp = '\0';

	m->m_mem[result].oper.c = nsp;
	m->m_mem[result].typ = TYP_STR;
}



/*
set uid, if permitted
*/
void
blt_setruid(m,argc,firstarg,result,uid,euid)
Machine	*m;
int	argc;
int	firstarg;
int	result;
long	*uid;
long	*euid;
{
	if(argc != 1 || m->m_mem[firstarg].typ != TYP_OBJ) {
		m->m_mem[result].typ = TYP_NUM;
		m->m_mem[result].oper.i = 1;
		return;
	}

	/* check perms */
	if(*uid != (long)0 && *euid != (long)0) {
		m->m_mem[result].typ = TYP_NUM;
		m->m_mem[result].oper.i = 1;
		return;
	}

	*uid = m->m_mem[firstarg].oper.l;
	m->m_mem[result].typ = TYP_NUM;
	m->m_mem[result].oper.i = 0;
	return;
}



/*
set user effective uid, if permitted
*/
void
blt_setuid(m,argc,firstarg,result,uid,euid)
Machine	*m;
int	argc;
int	firstarg;
int	result;
long	*uid;
long	*euid;
{
	if(argc != 1 || m->m_mem[firstarg].typ != TYP_OBJ) {
		m->m_mem[result].typ = TYP_NUM;
		m->m_mem[result].oper.i = 1;
		return;
	}

	/* check perms */
	if(*uid != m->m_mem[firstarg].oper.l && *uid != (long)0 && *euid != (long)0) {
		m->m_mem[result].typ = TYP_NUM;
		m->m_mem[result].oper.i = 1;
		return;
	}

	*euid = m->m_mem[firstarg].oper.l;
	m->m_mem[result].typ = TYP_NUM;
	m->m_mem[result].oper.i = 0;
	return;
}





/*
build a string from the arguments. numbers are converted into text
and concatenated together.
*/
void
blt_str(m,argc,firstarg,result,uid,euid)
Machine	*m;
int	argc;
int	firstarg;
int	result;
long	*uid;
long	*euid;
{
	int	totlen = 0;
	char	*ret;
	register char	*cp;
	register char	*ip;
	int	tac = argc;
	int	tag = firstarg;

	/*
	one problem with the way that tmpalloc() is written is that it
	does not elegantly handle realloc(). thus - we need to count
	the length of everything BEFORE we allocate the string.
	this is a trivial waste of CPU.
	*/
	if(tac == 0) {
		m->m_mem[result].typ = TYP_STR;
		m->m_mem[result].oper.c = "";
		return;
	}

	while(tac--) {
		switch(m->m_mem[tag].typ) {
			case	TYP_STR:
			totlen += strlen(m->m_mem[tag].oper.c);
			break;

			case	TYP_OBJ:
			totlen += strlen(itoa((int)m->m_mem[tag].oper.l)) + 1;
			break;

			case	TYP_FUNC:
			totlen += strlen(funcstr);
			break;

			case	TYP_OLIST:
			totlen += strlen(liststr);
			break;

			case	TYP_NUM:
			totlen += strlen(itoa(m->m_mem[tag].oper.i));
			break;
		}
		tag++;
	}

	ip = ret = tmpalloc(totlen + 1);
	if(ret == 0) {
		m->m_mem[result].typ = TYP_NULL;
		m->m_mem[result].oper.i = ERR_OOM;
		return;
	}

	/* now do the actual copy */
	tac = argc;
	tag = firstarg;
	while(tac--) {
		switch(m->m_mem[tag].typ) {
			case	TYP_STR:
			cp = m->m_mem[tag].oper.c;
			break;

			case	TYP_OBJ:
			*ip++ = '#';
			cp = itoa((int)m->m_mem[tag].oper.l);
			break;

			case	TYP_FUNC:
			cp = funcstr;
			break;

			case	TYP_OLIST:
			cp = liststr;
			break;

			case	TYP_NUM:
			cp = itoa(m->m_mem[tag].oper.i);
			break;

			default:
				cp = "";
		}
		while(*cp != '\0' && *cp != MATCH_CHAR)
			*ip++ = *cp++;
		tag++;
	}

	/* e finito! */
	*ip = '\0';
	m->m_mem[result].typ = TYP_STR;
	m->m_mem[result].oper.c = ret;
}



/*
return length of a string, or zero if non-string.
*/
void
blt_strlen(m,argc,firstarg,result,uid,euid)
Machine	*m;
int	argc;
int	firstarg;
int	result;
long	*uid;
long	*euid;
{

	if(argc < 1 || m->m_mem[firstarg].typ != TYP_STR)
		m->m_mem[result].oper.i = 0;
	else
		m->m_mem[result].oper.i = strlen(m->m_mem[firstarg].oper.c);
	m->m_mem[result].typ = TYP_NUM;
}



/*
stick the system's string notion of the time on the stack.
*/
void
blt_strtime(m,argc,firstarg,result,uid,euid)
Machine	*m;
int	argc;
int	firstarg;
int	result;
long	*uid;
long	*euid;
{
	char	*ip;
	char	*op;
	long	thistim;

	if(argc > 0 && m->m_mem[firstarg].typ == TYP_NUM)
		thistim = (long)m->m_mem[firstarg].oper.i;
	else
		(void)time(&thistim);

	/* if your ctime returns > 32 char, you will die! */
	m->m_mem[result].oper.c = tmpalloc(32);
	if(m->m_mem[result].oper.c == 0) {
		m->m_mem[result].typ = TYP_NULL;
		m->m_mem[result].oper.i = ERR_OOM;
		return;
	} 

	m->m_mem[result].typ = TYP_STR;

	ip = ctime(&thistim);
	op = m->m_mem[result].oper.c;
	while(*ip != '\0' && *ip != '\n')
		*op++ = *ip++;
	*op = '\0';
}



/*
stick the system's notion of the time on the stack. - the assumption is
made that a time_t is the same size as an int - not necessarily true.
to fix this, though, a whole new TYP_TIME datatype would need to be
added, and then it would be harder to manipulate with numeric ops. ick.
*/
void
blt_time(m,argc,firstarg,result,uid,euid)
Machine	*m;
int	argc;
int	firstarg;
int	result;
long	*uid;
long	*euid;
{
	m->m_mem[result].typ = TYP_NUM;
	m->m_mem[result].oper.i = (int)time((long *)0);
}