#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);
}