/* functions.c - MUSH function handlers */

#include "copyright.h"

#ifdef WANT_ANSI
#ifdef __STDC__
#include <stdlib.h>
#include <stddef.h>
#endif /* __STDC__ */
#endif /* WANT_ANSI */

#include <ctype.h>
#include <limits.h>
#include <math.h>
#include <time.h>

#include "mudconf.h"
#include "config.h"
#include "db.h"
#include "flags.h"
#include "externs.h"
#include "match.h"
#include "command.h"
#include "functions.h"
#include "misc.h"

extern char *index();

/* This is the prototype for functions */

#define	FUNCTION(x)	static void x(char *buff, dbref player, dbref cause, \
				char *fargs[], int nfargs, \
				char *cargs[], int ncargs)

dbref match_thing(dbref player, char *name)
{
	init_match(player, name, NOTYPE);
	match_everything();
	return (noisy_match_result());
}

/* ---------------------------------------------------------------------------
 * nearby_or_control: Check if player is near or controls thing
 */

int nearby_or_control (dbref player, dbref thing)
{
	if (!Good_obj(player) || !Good_obj(thing))
		return 0;
	if (Controls(player, thing))
		return 1;
	if (!nearby(player, thing))
		return 0;
	if ((Typeof(thing) == TYPE_EXIT) && Dark(thing))
		return 0;
	return 1;
}

/* ---------------------------------------------------------------------------
 * fun_words: Returns number of words in a string.
 * Added 1/28/91 Philip D. Wasson
 */

FUNCTION(fun_words)
{
char	*i;
int	n;

	for (i=fargs[0],n=0; strtok(i, " "); i=NULL,n++) ;
	sprintf(buff, "%d", n);
}

/* ---------------------------------------------------------------------------
 * fun_flags: Returns the flags on an object.
 * Because @switch is case-insensitive, not quite as useful as it could be.
 */

FUNCTION(fun_flags)
{
dbref	it;
char	*buff2;

	it = match_thing(player, fargs[0]);
	if ((it != NOTHING) &&
	    (mudconf.pub_flags || Examinable(player, it) || (it == cause))) {
		buff2 = unparse_flags(player, it);
		strcpy(buff, buff2);
		free_sbuf(buff2);
	} else
		strcpy(buff, "#-1");
	return;
}

/* ---------------------------------------------------------------------------
 * fun_rand: Return a random number from 0 to arg1-1
 */

FUNCTION(fun_rand)
{
int	num;

	num = atoi(fargs[0]);
	if (num < 1)
		strcpy(buff, "0");
	else
		sprintf(buff, "%d", (int)((double)num *
			((double)random() / (double)INT_MAX)));
}

/* ---------------------------------------------------------------------------
 * fun_abs: Returns the absolute value of its argument.
 */

FUNCTION(fun_abs)
{
int     num;

	num = atoi(fargs[0]);
	if (num == 0)
		strcpy(buff, "0");
	else
		sprintf(buff, "%d", abs(num));
}

/* ---------------------------------------------------------------------------
 * fun_sign: Returns -1, 0, or 1 based on the the sign of its argument.
 */

FUNCTION(fun_sign)
{
int     num;

	num = atoi(fargs[0]);
	if (num < 0)	
		strcpy(buff, "-1");
	else if (num > 0)
		strcpy(buff, "1");
	else
		strcpy(buff, "0");
}

/* ---------------------------------------------------------------------------
 * fun_time: Returns nicely-formatted time.
 */

FUNCTION(fun_time)
{
time_t	tt;
char	*temp;

	time(&tt);
	temp = (char *)ctime(&tt);
	temp[strlen(temp) - 1] = '\0';
	strcpy(buff, temp);
}

/* ---------------------------------------------------------------------------
 * fun_time: Seconds since 0:00 1/1/70
 */

FUNCTION(fun_secs)
{
time_t	tt;

	time(&tt);
	sprintf(buff, "%d", tt);
}

/* ---------------------------------------------------------------------------
 * fun_starttime: What time did this system last reboot?
 */

FUNCTION(fun_starttime)
{
char	*temp;

	temp = (char *)ctime(&mudstate.start_time);
	temp[strlen(temp) - 1] = '\0';
	strcpy(buff, temp);
}

/* ---------------------------------------------------------------------------
 * fun_get, fun_get_eval: Get attribute from object.
 */

FUNCTION(fun_get)
{
dbref	thing, aowner;
int	attrib, free_buffer, aflags;
ATTR	*attr;
char    *atr_gotten;
struct boolexp *bool;

	if (!parse_attrib(player, fargs[0], &thing, &attrib)) {
		strcpy(buff, "#-1 NO MATCH");
		return;
	}
	if (attrib == NOTHING) {
		*buff = '\0';
		return;
	}
	free_buffer = 1;
	attr = atr_num(attrib);  /* We need the attr's flags for this: */
	if (attr->flags & AF_IS_LOCK) {
		atr_gotten = atr_get(thing, attrib, &aowner, &aflags);
		if (Read_attr(player, thing, attr, aowner)) {
			bool = parse_boolexp(player, atr_gotten);
			free_lbuf(atr_gotten);
			atr_gotten = unparse_boolexp(player, bool);
			free_boolexp(bool);
		} else {
			free_lbuf(atr_gotten);
			atr_gotten = (char *)"#-1 PERMISSION DENIED";
		}
		free_buffer = 0;
	} else {
		atr_gotten = atr_pget(thing, attrib, &aowner, &aflags);
	}
	if (Examinable(player, thing) &&
	    See_attr(player, thing, attr, aowner)) {
		;
	} else if ((Typeof(thing) == TYPE_PLAYER) &&
		   See_attr(player, thing, attr, aowner)) {
		if (!mudconf.read_rem_desc && (attr->number == A_DESC) &&
		    !nearby(player, thing)) {
			free_lbuf(atr_gotten);
			atr_gotten = (char *)"#-1 TOO FAR AWAY TO SEE";
			free_buffer = 0;
		}
	} else if (See_attr(player, thing, attr, aowner)) {
		if (!mudconf.read_rem_desc && !nearby(player, thing)) {
			free_lbuf(atr_gotten);
			atr_gotten = (char *)"#-1 TOO FAR AWAY TO SEE";
			free_buffer = 0;
		}
	} else {
		free_lbuf(atr_gotten);
		atr_gotten = (char *)"#-1 PERMISSION DENIED";
		free_buffer = 0;
	}
	strcpy(buff, atr_gotten);
	if (free_buffer)
		free_lbuf(atr_gotten);
	return;
}

FUNCTION(fun_get_eval)
{
dbref	thing, aowner;
int	attrib, free_buffer, aflags, eval_it;
ATTR	*attr;
char    *atr_gotten;
struct boolexp *bool;

	if (!parse_attrib(player, fargs[0], &thing, &attrib)) {
		strcpy(buff, "#-1 NO MATCH");
		return;
	}
	if (attrib == NOTHING) {
		*buff = '\0';
		return;
	}
	free_buffer = 1;
	eval_it = 1;
	attr = atr_num(attrib);  /* We need the attr's flags for this: */
	if (attr->flags & AF_IS_LOCK) {
		atr_gotten = atr_get(thing, attrib, &aowner, &aflags);
		if (Read_attr(player, thing, attr, aowner)) {
			bool = parse_boolexp(player, atr_gotten);
			free_lbuf(atr_gotten);
			atr_gotten = unparse_boolexp(player, bool);
			free_boolexp(bool);
		} else {
			free_lbuf(atr_gotten);
			atr_gotten = (char *)"#-1 PERMISSION DENIED";
		}
		free_buffer = 0;
		eval_it = 0;
	} else {
		atr_gotten = atr_pget(thing, attrib, &aowner, &aflags);
	}
	if (Examinable(player, thing) &&
	    See_attr(player, thing, attr, aowner)) {
		;
	} else if ((Typeof(thing) == TYPE_PLAYER) &&
		   See_attr(player, thing, attr, aowner)) {
		if (!mudconf.read_rem_desc && (attr->number == A_DESC) &&
		    !nearby(player, thing)) {
			free_lbuf(atr_gotten);
			atr_gotten = (char *)"#-1 TOO FAR AWAY TO SEE";
			free_buffer = 0;
			eval_it = 0;
		}
	} else if (See_attr(player, thing, attr, aowner)) {
		if (!mudconf.read_rem_desc && !nearby(player, thing)) {
			free_lbuf(atr_gotten);
			atr_gotten = (char *)"#-1 TOO FAR AWAY TO SEE";
			free_buffer = 0;
			eval_it = 0;
		}
	} else {
		free_lbuf(atr_gotten);
		atr_gotten = (char *)"#-1 PERMISSION DENIED";
		free_buffer = 0;
		eval_it = 0;
	}
	strcpy(buff, atr_gotten);
	if (free_buffer)
		free_lbuf(atr_gotten);
	if (eval_it) {
		atr_gotten = exec(thing, player, EV_FIGNORE, buff,
			(char **)NULL, 0);
		strcpy(buff, atr_gotten);
		free_lbuf(atr_gotten);
	}
	return;
}

/* ---------------------------------------------------------------------------
 * fun_parent: Get parent of object.
 */

FUNCTION(fun_parent)
{
dbref	it;

	it = match_thing(player, fargs[0]);
	if (Good_obj(it) && (Examinable(player, it) || (it == cause))) {
		sprintf(buff, "#%d", Parent(it));
	} else {
		strcpy(buff, "#-1");
	}
	return;
}

/* ---------------------------------------------------------------------------
 * fun_mid: mid(foobar,2,3) returns oba
 */

FUNCTION(fun_mid)
{
int	l, len;

	l = atoi(fargs[1]);
	len = atoi(fargs[2]);
	if ((l < 0) || (len < 0) || ((len + l) > LBUF_SIZE)) {
		strcpy(buff, "#-1 OUT OF RANGE");
		return;
	}
	if (l < strlen(fargs[0]))
		strcpy(buff, fargs[0] + l);
	else
		*buff = 0;
	buff[len] = 0;
}

/* ---------------------------------------------------------------------------
 * fun_first: Returns first word in a string
 */

FUNCTION(fun_first)
{
char	*s, *b;

	/* get rid of leading space */
	b = skip_space(fargs[0]);
	s = seek_char(b, ' ');
	if (s) *s = 0;
	strcpy(buff, b);
}

/* ---------------------------------------------------------------------------
 * fun_rest: Returns all but the first word in a string 
 */


FUNCTION(fun_rest)
{
char	*s;

	/* skip leading space */
	s = skip_space(fargs[0]);

	/* skip firsts word */
	s = seek_char(s, ' ');

	/* skip leading space */
	s = skip_space(s);
	strcpy(buff, s);
}

/* ---------------------------------------------------------------------------
 * fun_v: Function form of %-substitution
 */

FUNCTION(fun_v)
{
dbref	aowner;
int	aflags;
char	*sbuf, *sbufc, *tbuf;
ATTR	*ap;

	tbuf = fargs[0];
	if (isalpha(tbuf[0]) && tbuf[1]) {

		/* Fetch an attribute from me.  First see if it exists,
		 * returning a null string if it does not. */

		ap = atr_str(fargs[0]);
		if (!ap) {
			*buff = '\0';
			return;
		}

		/* If we can access it, return it, otherwise return a
		 * null string */

		atr_pget_info(player, ap->number, &aowner, &aflags);
		if (See_attr(player, player, ap, aowner)) {
			tbuf = atr_pget(player, ap->number, &aowner, &aflags);
			strcpy(buff, tbuf);
			free_lbuf(tbuf);
		} else {
			*buff = '\0';
		}
		return;
	}

	/* Not an attribute, process as %<arg> */

	sbuf = alloc_sbuf("fun_v");
	sbufc = sbuf;
	safe_sb_chr('%', sbuf, &sbufc);
	safe_sb_str(fargs[0], sbuf, &sbufc);
	*sbufc = '\0';
	tbuf = exec(player, cause, EV_FIGNORE, sbuf, cargs, ncargs);
	strcpy(buff, tbuf);
	free_lbuf(tbuf);
	free_sbuf(sbuf);
}

/* ---------------------------------------------------------------------------
 * fun_s: Force substitution to occur.
 */

FUNCTION(fun_s)
{
char	*tbuf;

	tbuf = exec(player, cause, EV_FIGNORE, fargs[0], cargs, ncargs);
	strcpy(buff, tbuf);
	free_lbuf(tbuf);
}

/* ---------------------------------------------------------------------------
 * fun_con: Returns first item in contents list of object/room
 */

FUNCTION(fun_con)
{
dbref	it, loc_player;

	it = match_thing(player, fargs[0]);
	loc_player = where_is(player);

	if ((it != NOTHING) &&
	    (Has_contents(it)) &&
	    (Examinable(player, it) ||
	     (where_is(player) == it) ||
	     (it == cause))) {
		sprintf(buff, "#%d", Contents(it));
		return;
	}
	strcpy(buff, "#-1");
	return;
}

/* ---------------------------------------------------------------------------
 * fun_exit: Returns first exit in exits list of room.
 */

FUNCTION(fun_exit)
{
dbref	it, exam;

	it = match_thing(player, fargs[0]);
	if (Good_obj(it) && (Typeof(it != TYPE_EXIT))) {
		exam = Examinable(player, it);
		if (exam || (where_is(player) == it) || (it == cause)) {
			sprintf(buff, "#%d",
				next_exit(player, Exits(it), exam));
			return;
		}
	}
	strcpy(buff, "#-1");
	return;
}

/* ---------------------------------------------------------------------------
 * fun_next: return next thing in contents or exits chain
 */

FUNCTION(fun_next)
{
dbref	it, loc, exam_here;

	it = match_thing(player, fargs[0]);
	loc = where_is(it);
	if (loc != NOTHING) {
		exam_here = Examinable(player, loc);
		if (exam_here || (loc == player) ||
		    (loc == where_is(player))) {
			sprintf(buff, "#%d",
				next_exit(player, Next(it), exam_here));
			return;
		}
	}
	strcpy(buff, "#-1");
	return;
}

/* ---------------------------------------------------------------------------
 * fun_loc: Returns the location of something
 */

FUNCTION(fun_loc)
{
dbref	it, loc_it;

	it = match_thing(player, fargs[0]);
	loc_it = where_is(it);
	if ((it != NOTHING) &&
	    (Examinable(player, it) ||
	     (loc_it == player) ||
	     ((loc_it != NOTHING) &&
	      (Examinable(player, loc_it) || loc_it == where_is(player))) ||
	     Wizard(cause) ||
	     Findable(it) ||
	     (it == cause))) {
		sprintf(buff, "#%d", Location(it));
		return;
	}
	strcpy(buff, "#-1");
	return;
}

/* ---------------------------------------------------------------------------
 * fun_room: Find the room an object is ultimately in.
 */

FUNCTION(fun_room)
{
dbref	it;
int	count;

	it = match_thing(player, fargs[0]);
	if ((Good_obj(it) && Has_location(it)) &&
	    (Examinable(player, it) ||
	     nearby(player, it) ||
	     Wizard(cause) ||
	     Findable(it) ||
	     (it == cause))) {
		for (count=20; count>0; count--) {
			it = Location(it);
			if (Typeof(it) == TYPE_ROOM) {
				sprintf(buff, "#%d", it);
				return;
			}
		}
		strcpy(buff, "#-1");
	} else if (Typeof(it) == TYPE_ROOM) {
		sprintf(buff, "#%d", it);
	} else {
		strcpy(buff, "#-1");
	}
	return;
}

/* ---------------------------------------------------------------------------
 * fun_owner: Return the owner of an object.
 */

FUNCTION(fun_owner)
{
dbref	it;

	it = match_thing(player, fargs[0]);
	if (it != NOTHING)
		it = Owner(it);
	sprintf(buff, "#%d", it);
}

/* ---------------------------------------------------------------------------
 * fun_name: Return the name of an object
 */

FUNCTION(fun_name)
{
dbref	it;
char	*s;

	it = match_thing(player, fargs[0]);
	if (it == NOTHING) {
		buff[0] = '\0';
		return;
	}

	if (!mudconf.read_rem_name) {
		if (!nearby_or_control(player, it) &&
		    (Typeof(it) != TYPE_PLAYER)) {
			strcpy(buff, "#-1 TOO FAR AWAY TO SEE");
			return;
		}
	}

	if (Typeof(it) == TYPE_EXIT) {
		if (!Controls(player, it) && Dark(it)) {
			strcpy(buff, "#-1 TOO FAR AWAY TO SEE");
		} else {
			strcpy(buff, Name(it));
			for (s = buff; *s && (*s != ';'); s++) ;
			*s = '\0';
		}
	} else
		strcpy(buff, Name(it));
}

/* ---------------------------------------------------------------------------
 * fun_match: match(bar foo bar,foo) returns '2' (foo is the second word in
 * the first string.)
*/

FUNCTION(fun_match)
{
int	wcount;
char	*r, *s;

	/* First check if we match the whole string.  If so, return 1 */

	if (wild_match(fargs[1], fargs[0], (char **)NULL, 0)) {
		strcpy(buff, "1");
		return;
	}

	/* Now check each word individually, returning the word number of the
	 * first one that matches.  If none match, return 0.
	 */

	wcount = 1;
	s = fargs[0];
	do {
		r = skip_space(s);
		s = seek_char(r, ' ');
		if (*s) *s++ = 0;
		if (wild_match(fargs[1], r, (char **)NULL, 0)) {
			sprintf(buff, "%d", wcount);
			return;
		}
		wcount++;
	} while (*s);
	strcpy(buff, "0");
}

/* ---------------------------------------------------------------------------
 * fun_extract: extract words from string:
 * extract(foo bar baz,1,2) returns 'foo bar'
 * extract(foo bar baz,2,1) returns 'bar'
 * extract(foo bar baz,2,2) returns 'bar baz'
 * get it?
 */

FUNCTION(fun_extract)
{
int	start, len;
char	*r, *s;

	s = fargs[0];
	start = atoi(fargs[1]);
	len = atoi(fargs[2]);

	if ((start < 1) || (len < 1)) {
		*buff = '\0';
		return;
	}
	start--;
	while (start && *s) {
		s = skip_space(s);
		s = seek_char(s, ' ');
		start--;
	}
	s = skip_space(s);
	r = s;
	while (len && *s) {
		s = skip_space(s);
		s = seek_char(s, ' ');
		len--;
	}
	if (s) *s = '\0';
	strcpy(buff, r);
}

int xlate(char *arg)
{
int	temp;
char	*temp2;

	if (arg[0] == '#')
		if ((temp = atoi(arg + 1)) == -1)
			return 0;
		else
			return temp;
	temp2 = skip_space(arg);
	if (!*temp2)
		return 0;
	if (is_number(temp2))
		return atoi(temp2);
	return 1;
}

FUNCTION(fun_cat)
{
char	*bp;

	bp = buff;
	safe_str(fargs[0], buff, &bp);
	safe_chr(' ', buff, &bp);
	safe_str(fargs[1], buff, &bp);
	*bp = '\0';
}

FUNCTION(fun_version){ strcpy(buff, mudstate.version); }
FUNCTION(fun_strlen){ sprintf(buff, "%d", (int) strlen(fargs[0])); }
FUNCTION(fun_num)   { sprintf(buff, "#%d", match_thing(player, fargs[0])); }
FUNCTION(fun_gt)    { sprintf(buff, "%d", (atoi(fargs[0])> atoi(fargs[1]))); }
FUNCTION(fun_gte)   { sprintf(buff, "%d", (atoi(fargs[0])>=atoi(fargs[1]))); }
FUNCTION(fun_lt)    { sprintf(buff, "%d", (atoi(fargs[0])< atoi(fargs[1]))); }
FUNCTION(fun_lte)   { sprintf(buff, "%d", (atoi(fargs[0])<=atoi(fargs[1]))); }
FUNCTION(fun_eq)    { sprintf(buff, "%d", (atoi(fargs[0])==atoi(fargs[1]))); }
FUNCTION(fun_neq)   { sprintf(buff, "%d", (atoi(fargs[0])!=atoi(fargs[1]))); }
FUNCTION(fun_and)   { sprintf(buff, "%d", xlate(fargs[0]) && xlate(fargs[1])); }
FUNCTION(fun_or)    { sprintf(buff, "%d", xlate(fargs[0]) || xlate(fargs[1])); }
FUNCTION(fun_not)   { sprintf(buff, "%d", !xlate(fargs[0])); }
FUNCTION(fun_xor)   { sprintf(buff, "%d",
	((xlate(fargs[0]) && !xlate(fargs[1])) ||
	 (!xlate(fargs[0]) && xlate(fargs[1])))); }
FUNCTION(fun_add)	{ sprintf(buff, "%d", atoi(fargs[0]) + atoi(fargs[1])); }
FUNCTION(fun_sub)	{ sprintf(buff, "%d", atoi(fargs[0]) - atoi(fargs[1])); }
FUNCTION(fun_mul)	{ sprintf(buff, "%d", atoi(fargs[0]) * atoi(fargs[1])); }
FUNCTION(fun_div)
{
int	bot;

	bot = atoi(fargs[1]);
	if (bot == 0) bot = 1;
	sprintf(buff, "%d", atoi(fargs[0]) / bot);
}

FUNCTION(fun_mod)
{
int	bot;

	bot = atoi(fargs[1]);
	if (bot == 0) bot = 1;
	sprintf(buff, "%d", atoi(fargs[0]) % bot);
}

FUNCTION(fun_dist2d)
{
int	d;
double	r;

	d = atoi(fargs[0]) - atoi(fargs[2]);
	r = (double)(d * d);
	d = atoi(fargs[1]) - atoi(fargs[3]);
	r +=(double)(d * d);
	d = (int)(sqrt(r) + 0.5);
	sprintf(buff, "%d", d);
}

FUNCTION(fun_dist3d)
{
int	d;
double	r;

	d = atoi(fargs[0]) - atoi(fargs[3]);
	r = (double)(d * d);
	d = atoi(fargs[1]) - atoi(fargs[4]);
	r += (double)(d * d);
	d = atoi(fargs[2]) - atoi(fargs[5]);
	r += (double)(d * d);
	d = (int)(sqrt(r) + 0.5);
	sprintf(buff, "%d", d);
}

	

/* ---------------------------------------------------------------------------
 * fun_comp: string compare.
 */

FUNCTION(fun_comp)
{
int	x;

	x = strcmp(fargs[0], fargs[1]);
	if (x > 0)
		strcpy(buff, "1");
	else if (x < 0)
		strcpy(buff, "-1");
	else
		strcpy(buff, "0");
}

/* ---------------------------------------------------------------------------
 * fun_lcon: Return a list of contents.
 */

FUNCTION(fun_lcon)
{
dbref	thing, it;
char	*bufp, *tbuf;

	it = match_thing(player, fargs[0]);
	*buff = '\0';
	bufp = buff;
	if ((it != NOTHING) &&
            (Has_contents(it)) &&
	    (Examinable(player, it) ||
	     (Location(player) == it) ||
	     (it == cause))) {
		tbuf = alloc_sbuf("fun_lcon");
		DOLIST(thing, Contents(it)) {
			if (*buff)
				sprintf(tbuf, " #%d", thing);
			else
				sprintf(tbuf, "#%d", thing);
			safe_str(tbuf, buff, &bufp);
		}
		free_sbuf(tbuf);
		*bufp = '\0';
	} else
		strcpy(buff, "#-1");
}

/* ---------------------------------------------------------------------------
 * fun_lexits: Return a list of exits.
 */

FUNCTION(fun_lexits)
{
dbref	thing, it;
char	*bufp, *tbuf;
int	exam, lev;

	*buff = '\0';
	bufp = buff;
	it = match_thing(player, fargs[0]);

	if (!Good_obj(it) || (Typeof(it == TYPE_EXIT))) {
		strcpy(buff, "#-1");
		return;
	}

	exam = Examinable(player, it);
	if (!exam && (where_is(player) != it) && (it != cause)) {
		strcpy(buff, "#-1");
		return;
	}

	tbuf = alloc_sbuf("fun_lexits");

	/* Return info for all parent levels */

	for (lev=0;
	     (Good_obj(it) && (lev < mudconf.parent_nest_lim));
	     it=Parent(it), lev++) {

		/* Look for exits at each level */

		for (thing=next_exit(player, Exits(it), exam);
		     thing!=NOTHING;
		     thing=next_exit(player, Next(thing), exam)) {
			if (*buff)
				sprintf(tbuf, " #%d", thing);
			else
				sprintf(tbuf, "#%d", thing);
			safe_str(tbuf, buff, &bufp);
		}
	}
	free_sbuf(tbuf);
	*bufp = '\0';
	return;
}

/* --------------------------------------------------------------------------
 * fun_home: Return an object's home 
 */

FUNCTION(fun_home)
{
dbref	it;

	it = match_thing(player, fargs[0]);
	if (!Good_obj(it) || (Typeof(it) == TYPE_ROOM) ||
	    !Examinable(player, it))
		sprintf(buff, "#-1");
	else if (Typeof(it) == TYPE_EXIT)
		sprintf(buff, "#%d", Exits(it));
	else
		sprintf(buff, "#%d", Home(it));
}

/* ---------------------------------------------------------------------------
 * fun_money: Return an object's value
 */

FUNCTION(fun_money)
{
dbref	it;

	it = match_thing(player, fargs[0]);
	if ((it == NOTHING) || !Examinable(player, it))
		sprintf(buff, "#-1");
	else
		sprintf(buff, "%d", Pennies(it));
}

/* ---------------------------------------------------------------------------
 * fun_pos: Find a word in a string */

FUNCTION(fun_pos)
{
int	i = 1;
char	*s,*t, *u;

	i = 1;
	s = fargs[1];
	while (*s) {
		u = s;
		t = fargs[0];
		while (*t && *t == *u)
			++t, ++u;
		if (*t == '\0') {
			sprintf(buff, "%d", i);
			return;
		}
		++i, ++s;
	}
	strcpy(buff, "#-1");
	return;
}

/* ---------------------------------------------------------------------------
 * fun_remove: Remove a word from a string
 */

FUNCTION(fun_remove)
{
char	*s, *t, *p;
int	done;

	done = 0;
	if (index(fargs[1], ' ')) {
		strcpy(buff, "#-1 CAN ONLY DELETE ONE ELEMENT");
		return;
	}
	s = p = fargs[0];
	t = fargs[1];

	while (*s && !done) {
		if (*t)
			if (*s == *t)
				t++;
			else {
				t = fargs[1];
				while (*(s + 1) && *s != ' ')
					s++;
				p = s;
			}
		else
			done = 1;
		s++;
	}
	if (!*t && ((*(s - 1) == ' ') || !*s)) {
		if (p == fargs[0])
			strcpy(buff, s);
		else {
			*p = '\0';
			if (!*s)
				strcpy(buff, fargs[0]);
			else
				sprintf(buff, "%s %s", fargs[0], s);
		}
		return;
	}
	strcpy(buff, fargs[0]);
}

/* ---------------------------------------------------------------------------
 * fun_member: Is a word in a string
 */

FUNCTION(fun_member)
{
int	wcount;
char	*r, *s;

	if (index(fargs[1], ' ')) {
		strcpy(buff, "#-1 CAN ONLY TEST ONE ELEMENT");
		return;
	}

	wcount = 1;
	s = fargs[0];
	do {
		r = skip_space(s);
		s = seek_char(r, ' ');
		if (*s) *s++ = 0;
		if (!strcmp(fargs[1], r)) {
			sprintf(buff, "%d", wcount);
			return;
		}
		wcount++;
	} while (*s);
	strcpy(buff, "0");
}

/* ---------------------------------------------------------------------------
 * fun_secure, fun_escape: escape [, ], %, \, and the beginning of the string.
 */

FUNCTION(fun_secure)
{
char	*s, *d;

	s = fargs[0];
	d = buff;
	while (*s) {
		switch (*s) {
		case '%':
                case '$':
		case '\\':
		case '[':
		case ']':
		case '(':
		case ')':
		case '{':
		case '}':
		case ',':
		case ';':
			safe_chr(' ', buff, &d);
			break;
		default:
			safe_chr(*s, buff, &d);
		}
		s++;
	}
	*d = '\0';
}

FUNCTION(fun_escape)
{
char	*s, *d;

	s = fargs[0];
	d = buff;
	while (*s) {
		switch (*s) {
		case '%':
		case '\\':
		case '[':
		case ']':
		case '{':
		case '}':
		case ';':
			safe_chr('\\', buff, &d);
		default:
			if (d == buff)
				safe_chr('\\', buff, &d);
			safe_chr(*s, buff, &d);
		}
		s++;
	}
	*d = '\0';
}

/* Take a character position and return which word that char is in.
   wordpos(<string>, <charpos>)
   */
FUNCTION(fun_wordpos)
{
  char *cp;
  int charpos, curword;
  char done, inspace;

  charpos = atoi(fargs[1]);
  curword = 1;
  for (inspace = 0, done = 0, cp = fargs[0]; cp && *cp && !done; cp++) {
    if ((*cp == ' ') && (!inspace)) {
      curword++;
      inspace = 1;
    }
    if ((*cp != ' ') && (inspace)) 
      inspace = 0;
    if ((cp - fargs[0] + 1) == charpos)
      done = 1;
  }
  if (!done)
    strcpy(buff, "#-1");
  else
    sprintf(buff, "%d", curword);
}

FUNCTION(fun_type)
{
dbref	it;

	it = match_thing(player, fargs[0]);
	if (!Good_obj(it)) {
		strcpy(buff, "#-1 NOT FOUND");
		return;
	}
	switch (Typeof(it)) {
	case TYPE_ROOM:
		strcpy(buff, "ROOM");
		break;
	case TYPE_EXIT:
		strcpy(buff, "EXIT");
		break;
	case TYPE_PLAYER:
		strcpy(buff, "PLAYER");
		break;
	case TYPE_THING:
		strcpy(buff, "THING");
		break;
	default:
		strcpy(buff, "#-1 ILLEGAL TYPE");
	}
	return;
}

FUNCTION(fun_hasflag)
{
dbref	it;
FLAGENT	*fp;

	it = match_thing(player, fargs[0]);
	if (!Good_obj(it)) {
		strcpy(buff, "#-1 NOT FOUND");
		return;
	}
	if (mudconf.pub_flags || Examinable(player, it) || (it == cause)) {
		fp = find_flag(it, fargs[1]);
		if (fp == NULL) {
			strcpy(buff, "0");
			return;
		}
		if (((fp->listperm & CA_WIZARD) && !Wizard(player)) ||
		    ((fp->listperm & CA_GOD) && !God(player))) {
			strcpy(buff, "0");
		} else if (IS(it, TYPE_PLAYER, WIZARD) && !Wizard(player) &&
			   (Flags(it) & DARK) &&
			   (fp->flagvalue == PLAYER_CONNECT)) {
			strcpy(buff, "0");
		} else if (Flags(it) & fp->flagvalue) {
			strcpy(buff, "1");
		} else {
			strcpy(buff, "0");
		}
	} else {
		strcpy(buff, "#-1 PERMISSION DENIED");
	}
}

FUNCTION(fun_delete)
{
char	*s, *d;
int	i, start, nchars, len;

	s = fargs[0];
	start = atoi(fargs[1]);
	nchars = atoi(fargs[2]);
	len = strlen(s);
	if ((start >= len) || (nchars <= 0)) {
		strcpy(buff, s);
		return;
	}

	d = buff;
	for (i=0; i<start; i++)
		*d++=(*s++);
	if ((i+nchars) < len) {
		s += nchars;
		while (*d++ = *s++) ;
	} else {
		*d = '\0';
	}
}

FUNCTION(fun_lock)
{
dbref	it, aowner;
int	aflags;
char	*tbuf;
struct boolexp *bool;

	it = match_thing(player, fargs[0]);
	if (Good_obj(it)) {
		if (!controls(player, it)) {
			strcpy(buff, "#-1 PERMISSION DENIED");
			return;
		}
		tbuf = atr_get(it, A_LOCK, &aowner, &aflags);
		bool = parse_boolexp(player, tbuf);
		free_lbuf(tbuf);
		tbuf = (char *)unparse_boolexp_function(player, bool);
		free_boolexp(bool);
		strcpy(buff, tbuf);
	} else {
		strcpy(buff, "#-1 NOT FOUND");
	}
}

FUNCTION(fun_elock)
{
dbref	it, victim, aowner;
int	aflags;
char	*tbuf;
struct boolexp *bool;

	it = match_thing(player, fargs[0]);
	if (!Good_obj(it)) {
		strcpy(buff, "#-1 NOT FOUND");
	} else {
		victim = match_thing(player, fargs[1]);
		if (!Good_obj(victim)) {
			strcpy(buff, "#-1 NOT FOUND");
		} else if ((!nearby(player, victim) && !nearby(it, victim)) &&
			   !Wizard(player)) {
			strcpy(buff, "#-1 TOO FAR AWAY");
		} else {
			tbuf = atr_get(it, A_LOCK, &aowner, &aflags);
			bool = parse_boolexp(player, tbuf);
			free_lbuf(tbuf);
			sprintf(buff, "%d", eval_boolexp(victim, it, bool));
			free_boolexp(bool);
		}
	}
}

/* ---------------------------------------------------------------------------
 * fun_lwho: Return list of connected users.
 */

FUNCTION(fun_lwho)
{
	make_ulist(player, buff);
}

/* ---------------------------------------------------------------------------
 * fun_nearby: Return whether or not obj1 is near obj2.
 */

FUNCTION(fun_nearby)
{
dbref	obj1, obj2;

	obj1 = match_thing(player, fargs[0]);
	obj2 = match_thing(player, fargs[1]);
	if (!(nearby_or_control(player, obj1) ||
	      nearby_or_control(player, obj2)))
		strcpy(buff, "0");
	else if (nearby(obj1, obj2))
		strcpy(buff, "1");
	else
		strcpy(buff, "0");
}

/* ---------------------------------------------------------------------------
 * fun_obj, fun_poss, and fun_subj: perform pronoun sub for object.
 */

FUNCTION(fun_obj)
{
dbref	it;
char	*tbuff;

	it = match_thing(player, fargs[0]);
	if (!nearby_or_control(player, it)) {
		strcpy(buff, "#-1 NO MATCH");
	} else {
		tbuff = exec(it, it, 0, (char *)"%o", (char **)NULL, 0);
		strcpy(buff, tbuff);
		free_lbuf(tbuff);
	}
}

FUNCTION(fun_poss)
{
dbref	it;
char	*tbuff;

	it = match_thing(player, fargs[0]);
	if (!nearby_or_control(player, it)) {
		strcpy(buff, "#-1 NO MATCH");
	} else {
		tbuff = exec(it, it, 0, (char *)"%p", (char **)NULL, 0);
		strcpy(buff, tbuff);
		free_lbuf(tbuff);
	}
}

FUNCTION(fun_subj)
{
dbref	it;
char	*tbuff;

	it = match_thing(player, fargs[0]);
	if (!nearby_or_control(player, it)) {
		strcpy(buff, "#-1 NO MATCH");
	} else {
		tbuff = exec(it, it, 0, (char *)"%s", (char **)NULL, 0);
		strcpy(buff, tbuff);
		free_lbuf(tbuff);
	}
}

/* ---------------------------------------------------------------------------
 * fun_mudname: Return the name of the mud.
 */

FUNCTION(fun_mudname)
{
char	*bp;

	bp = buff;
	safe_str(mudconf.mud_name, buff, &bp);
	*bp = '\0';
	return;
}

/* ---------------------------------------------------------------------------
 * fun_lcstr, fun_ucstr, fun_capstr: Lowercase, uppercase, or capitalize str.
 */

FUNCTION(fun_lcstr)
{
char	*ap, *bp;

	ap = fargs[0];
	bp = buff;
	while (*ap) {
		if (isupper(*ap))
			*bp++ = tolower(*ap++);
		else
			*bp++ = *ap++;
	}
	*bp = '\0';
}

FUNCTION(fun_ucstr)
{
char	*ap, *bp;

	ap = fargs[0];
	bp = buff;
	while (*ap) {
		if (islower(*ap))
			*bp++ = toupper(*ap++);
		else
			*bp++ = *ap++;
	}
	*bp = '\0';
}

FUNCTION(fun_capstr)
{
	strcpy(buff, fargs[0]);
	if (islower(*buff))
		*buff = toupper(*buff);
}

/* ---------------------------------------------------------------------------
 * fun_lnum: Return a list of numbers.
 */

FUNCTION(fun_lnum)
{
char	*bp, tbuff[10];
int	ctr, limit, over;

	bp = buff;
	over = 0;
	limit = atol(fargs[0]);
	if (limit > 0) {
		safe_chr('0', buff, &bp);
		for (ctr=1; ctr<limit && !over; ctr++) {
			sprintf(tbuff, " %d", ctr);
			over = safe_str(tbuff, buff, &bp);
		}
	}
	*bp = '\0';
}

/* ---------------------------------------------------------------------------
 * fun_lattr: Return list of attributes I can see on the object.
 */

FUNCTION(fun_lattr)
{
dbref	thing, aowner;
int	ca, aflags;
char	*bp, *as;
ATTR	*attr;

	thing = match_thing(player, fargs[0]);
	if (thing == NOTHING) {
		strcpy(buff, "#-1 NO MATCH");
		return;
	}

	bp = buff;
	for (ca=atr_head(thing, &as); ca; ca=atr_next(&as)) {
		attr = atr_num(ca);
		if (attr) {
			atr_get_info(thing, ca, &aowner, &aflags);
			if (See_attr(player, thing, attr, aowner)) {
				if (bp != buff) {
					safe_chr(' ', buff, &bp);
				}
				safe_str((char *)attr->name, buff, &bp);
			}
		}
	}
	*bp = '\0';
}

/* ---------------------------------------------------------------------------
 * do_reverse, fun_reverse, fun_revwords: Reverse things.
 */

static void do_reverse (char *from, char *to)
{
char	*tp;

	tp = to + strlen(from);
	*tp-- = '\0';
	while (*from) {
		*tp-- = *from++;
	}
}

FUNCTION(fun_reverse)
{
	do_reverse(fargs[0], buff);
}

FUNCTION(fun_revwords)
{
char	*temp, *bp, *tp, *backp, savec;

	temp = alloc_lbuf("fun_revwords");

	/* Reverse the whole string */

	do_reverse(fargs[0], temp);

	/* Now individually reverse each word in the string.  This will
	 * undo the reversing of the words (so the words themselves are
	 * forwards again.
	 */

	tp = temp;
	bp = buff;
	while (*tp) {
		backp = tp;
		while (isprint(*tp) && !isspace(*tp)) {
			tp++;
		}
		if (tp != backp) {
			savec = *tp;
			*tp = '\0';
			do_reverse(backp, bp);
			bp += (tp - backp);
			*tp = savec;
		}
		if (!*tp) break;
		*bp++ = *tp++;
	}
	*bp = '\0';
	free_lbuf(temp);
}

/* ---------------------------------------------------------------------------
 * fun_after, fun_before: Return substring after or before a specified string.
 */

FUNCTION(fun_after)
{
char	*bp, *cp, *mp;
int	mlen;

	bp = fargs[0];
	mp = fargs[1];
	mlen = strlen(mp);

	/* If arg2 is empty, assume a space */

	if (!*mp) {
		cp = index(bp, ' ');
		if (cp == NULL) {
			*buff = '\0';
		} else {
			strcpy(buff, cp+1);
		}
		return;
	}

	/* Look for the target string */

	while (*bp) {

		/* Search for the first character in the target string */

		cp = index(bp, *mp);
		if (cp == NULL) {

			/* Not found, return empty string */

			*buff = '\0';
			return;
		}

		/* See if what follows is what we are looking for */

		if (!strncmp(cp, mp, mlen)) {

			/* Yup, return what follows */

			bp = cp + mlen;
			strcpy(buff, bp);
			return;
		}

		/* Continue search after found first character */

		bp = cp+1;
	}

	/* Ran off the end without finding it */

	*buff = '\0';
	return;
}

FUNCTION(fun_before)
{
char	*bp, *cp, *mp;
int	mlen;

	bp = fargs[0];
	mp = fargs[1];
	mlen = strlen(mp);

	/* If arg2 is empty, assume a space */

	if (!*mp) {
		cp = index(bp, ' ');
		if (cp != NULL) {
			*cp = '\0';
		}
		strcpy(buff, bp);
		return;
	}

	/* Look for the target string */

	while (*bp) {

		/* Search for the first character in the target string */

		cp = index(bp, *mp);
		if (cp == NULL) {

			/* Not found, return entire string */

			strcpy(buff, fargs[0]);
			return;
		}

		/* See if what follows is what we are looking for */

		if (!strncmp(cp, mp, mlen)) {

			/* Yup, return what follows */

			*cp = '\0';
			strcpy(buff, fargs[0]);
			return;
		}

		/* Continue search after found first character */

		bp = cp+1;
	}

	/* Ran off the end without finding it */

	strcpy(buff, fargs[0]);
	return;
}

/* ---------------------------------------------------------------------------
 * fun_max, fun_min: Return maximum (minimum) integer value.
 */

FUNCTION(fun_max)
{
int	i, j, max, got_one;

	max = 0;
	for (i=0,got_one=0; i<nfargs; i++) {
		if (fargs[i]) {
			j = atoi(fargs[i]);
			if (!got_one || (j > max)) {
				got_one = 1;
				max = j;
			}
		}
	}

	if (!got_one) {
		strcpy(buff, "#-1 TOO FEW ARGUMENTS");
	} else {
		sprintf(buff, "%d", max);
	}
	return;
}

FUNCTION(fun_min)
{
int	i, j, min, got_one;

	min = 0;
	for (i=0,got_one=0; i<nfargs; i++) {
		if (fargs[i]) {
			j = atoi(fargs[i]);
			if (!got_one || (j < min)) {
				got_one = 1;
				min = j;
			}
		}
	}

	if (!got_one) {
		strcpy(buff, "#-1 TOO FEW ARGUMENTS");
	} else {
		sprintf(buff, "%d", min);
	}
	return;
}

/* ---------------------------------------------------------------------------
 * fun_search: Search the db for things, returning a list of what matches
 */

FUNCTION(fun_search)
{
dbref	thing;
char	*bp, *nbuf;
SEARCH	searchparm;

	/* Set up for the search.  If any errors, abort. */

	if (!search_setup(player, fargs[0], &searchparm)) {
		strcpy(buff, "#-1 ERROR DURING SEARCH");
		return;
	}

	/* Do the search and report the results */

	search_perform(player, cause, &searchparm);
	bp = buff;
	nbuf = alloc_sbuf("fun_search");
	for (thing=olist_first(); thing!=NOTHING; thing=olist_next()) {
		if (bp == buff)
			sprintf(nbuf, "#%d", thing);
		else
			sprintf(nbuf, " #%d", thing);
		safe_str(nbuf, buff, &bp);
	}
	*bp = '\0';
	free_sbuf(nbuf);
	olist_init();
}

/* ---------------------------------------------------------------------------
 * fun_stats: Get database size statistics.
 */

FUNCTION(fun_stats)
{
dbref	who;
STATS	statinfo;

	if ((!fargs[0]) || !*fargs[0] || !string_compare(fargs[0], "all")) {
		who = NOTHING;
	} else {
		who = lookup_player(player, fargs[0], 1);
		if (who == NOTHING) {
			strcpy(buff, "#-1 NOT FOUND");
			return;
		}
	}
	if (!get_stats(player, who, &statinfo)) {
		strcpy(buff, "#-1 ERROR GETTING STATS");
		return;
	}

	sprintf(buff, "%d %d %d %d %d %d", statinfo.s_total, statinfo.s_rooms,
		statinfo.s_exits, statinfo.s_things, statinfo.s_players,
		statinfo.s_garbage);
}

/* ---------------------------------------------------------------------------
 * fun_iter: Make list from evaluating arg2 with each member of arg1.
 */

FUNCTION(fun_iter)
{
char	*curr, *objstring, *buff2, *result, *bp;

	curr = fargs[0];
	bp = buff;
	while (curr && *curr) {
		while (*curr==' ') curr++;
		if (*curr) {
			objstring = parse_to(&curr, ' ', EV_STRIP);
			buff2 = replace_string(BOUND_VAR, objstring, fargs[1]);
			result = exec(player, cause, EV_FCHECK, buff2,
				(char **)NULL, 0);
			free_lbuf(buff2);
			if (buff != bp)
				safe_chr(' ', buff, &bp);
			safe_str(result, buff, &bp);
			free_lbuf(result);
		}
	}
	*bp = '\0';
}

/* ---------------------------------------------------------------------------
 * do_edit: Edit text.
 */

FUNCTION(fun_edit)
{
char	*tstr;

	edit_string(fargs[0], &tstr, fargs[1], fargs[2]);
	strcpy(buff, tstr);
	free_lbuf(tstr);
}

/* ---------------------------------------------------------------------------
 * do_u: Return result of evaluating attribute.
 */

FUNCTION(fun_u)
{
dbref	aowner, thing;
int	aflags, anum;
ATTR	*ap;
char	*atext, *result;

	/* Two possibilities for the first arg: <obj>/<attr> and <attr>. */

	if (parse_attrib(player, fargs[0], &thing, &anum)) {
		if ((anum == NOTHING) || (!Good_obj(thing)))
			ap = NULL;
		else
			ap = atr_num(anum);
	} else {
		thing = player;
		ap = atr_str(fargs[0]);
	}

	/* Make sure we got a good attribute */

	if (!ap) {
		strcpy(buff, "#-1 NOT FOUND");
		return;
	}

	/* Use it if we can access it, otherwise return an error. */

	atext = atr_pget(thing, ap->number, &aowner, &aflags);
	if (!See_attr(player, thing, ap, aowner)) {
		free_lbuf(atext);
		strcpy(buff, "#-1 NOT FOUND");
		return;
	}

	/* Evaluate it using the rest of the passed function args */

	result = exec(player, cause, EV_FCHECK, atext, &(fargs[1]), nfargs-1);
	free_lbuf(atext);
	strcpy(buff, result);
	free_lbuf(result);
}

/* ---------------------------------------------------------------------------
 * fun_locate: Search for things with the perspective of another obj.
 */

FUNCTION(fun_locate)
{
int	pref_type, check_locks, verbose;
dbref	thing, what;
char	*cp;

	pref_type = NOTYPE;
	check_locks = 0;
	verbose = 0;

	/* Find the thing to do the looking, make sure we control it. */

	thing = match_controlled(player, fargs[0]);
	if (!Good_obj(thing)) {
		strcpy(buff, "#-1 PERMISSION DENIED");
		return;
	}

	/* Get pre- and post-conditions and modifiers */

	for (cp=fargs[2]; *cp; cp++) {
		switch (*cp) {
		case 'E':	pref_type = TYPE_EXIT;	break;
		case 'L':	check_locks = 1;	break;
		case 'P':	pref_type = TYPE_PLAYER;break;
		case 'R':	pref_type = TYPE_ROOM;	break;
		case 'T':	pref_type = TYPE_THING;	break;
		case 'V':	verbose = 1;		break;
		}
	}

	/* Set up for the search */

	if (check_locks)
		init_match_check_keys(thing, fargs[1], pref_type);
	else
		init_match(thing, fargs[1], pref_type);

	/* Search for each requested thing */

	for (cp=fargs[2]; *cp; cp++) {
		switch(*cp) {
		case 'a':	match_absolute();			break;
		case 'c':	match_carried_exit_with_parents();	break;
		case 'e':	match_exit_with_parents();		break;
		case 'h':	match_here();				break;
		case 'i':	match_possession();			break;
		case 'm':	match_me();				break;
		case 'n':	match_neighbor();			break;
		case 'p':	match_player();				break;
		case '*':	match_everything();			break;
		}
	}

	/* Get the result and return it to the caller */

	if (verbose)
		what = noisy_match_result();
	else
		what = match_result();

	sprintf(buff, "#%d", what);
}

/* ---------------------------------------------------------------------------
 * fun_switch: Return value based on pattern matching (ala @switch)
 */

FUNCTION(fun_switch)
{
int	i, got_any;
char	*tbuff;

	/* If we don't have at least 2 args, return nothing */

	if (nfargs < 2) {
		*buff = '\0';
		return;
	}

	got_any = 0;
	for (i=1; (i<nfargs-1) && fargs[i] && fargs[i+1]; i+=2) {
		tbuff = exec(player, cause, EV_STRIP+EV_FCHECK, fargs[i],
			(char **)NULL, 0);
		if (!got_any && quick_wild(tbuff, fargs[0])) {
			strcpy(buff, fargs[i+1]);
			got_any = 1;
		}
		free_lbuf(tbuff);
	}
	if (!got_any) {
		if ((i < nfargs) && fargs[i])
			strcpy(buff, fargs[i]);
		else
			*buff = '\0';
	}
	return;
}

/* ---------------------------------------------------------------------------
 * fun_space: Make spaces.
 */

FUNCTION(fun_space)
{
int	num;
char	*cp;

	if (!fargs[0] || !(*fargs[0])) {
		num = 1;
	} else {
		num = atoi(fargs[0]);
	}

	if (num < 1)
		num = 1;
	else if (num >= LBUF_SIZE)
		num = LBUF_SIZE - 1;

	for (cp=buff; num>0; num--)
		*cp++ = ' ';
	*cp = '\0';
	return;
}

/* ---------------------------------------------------------------------------
 * flist: List of existing functions in alphabetical order.
 */

FUN flist[] = {
{"ABS",		fun_abs,	1},	/* Absolute value */
{"ADD",		fun_add,	2},	/* Integer addition */
{"AFTER",	fun_after,	-2},	/* Return part of arg1 after arg2 */
{"AND",		fun_and,	2},	/* Logical AND */
{"BEFORE",	fun_before,	-2},	/* Return part of arg1 before arg2 */
{"CAPSTR",	fun_capstr,	-1},	/* Capitalize string */
{"CAT",		fun_cat,	-2},	/* Concatenate two strings */
{"COMP",	fun_comp,	2},	/* String compare */
{"CON",		fun_con,	1},	/* First item in contents list */
{"DELETE",	fun_delete,	3},	/* Remove characters from string */
{"DIST2D",	fun_dist2d,	4},	/* Distance in 2 dimensions */
{"DIST3D",	fun_dist3d,	6},	/* Distance in 3 dimensions */
{"DIV",		fun_div,	2},	/* Integer division */
{"EDIT",	fun_edit,	3},	/* Edit text */
{"ELOCK",	fun_elock,	2},	/* Evaluate lock with victim */
{"ESCAPE",	fun_escape,	-1},	/* Escape parser metacharacters */
{"EQ",		fun_eq,		2},	/* Numeric equals */
{"EXIT",	fun_exit,	1},	/* First item in exits list */
{"EXTRACT",	fun_extract,	3},	/* Remove a word from a list */
{"FIRST",	fun_first,	-1},	/* Return first word in string */
{"FLAGS",	fun_flags,	1},	/* Returns flags on an object */
{"GET",		fun_get,	1},	/* Get attribute of object */
{"GET_EVAL",	fun_get_eval,	1},	/* Get + eval the attr wrt its src */
{"GT",		fun_gt,		2},	/* Numeric greater than */
{"GTE",		fun_gte,	2},	/* Numeric greater than or equal */
{"HASFLAG",	fun_hasflag,	2},	/* Does object have named flag? */
{"HOME",	fun_home,	1},	/* Home of object */
{"ITER",	fun_iter,	2},	/* Iterative evaluation */
{"LATTR",	fun_lattr,	1},	/* List of visible attributes */
{"LCON",	fun_lcon,	1},	/* List of contents */
{"LCSTR",	fun_lcstr,	-1},	/* Lowercase string */
{"LEXITS",	fun_lexits,	1},	/* List of exits */
{"LNUM",	fun_lnum,	1},	/* Return list of numbers 0 to n-1 */
{"LOC",		fun_loc,	1},	/* Location of object */
{"LOCATE",	fun_locate,	3},	/* Locate an object */
{"LOCK",	fun_lock,	1},	/* Get lock of object */
{"LT",		fun_lt,		2},	/* Numeric less than */
{"LTE",		fun_lte,	2},	/* Numeric less than or equal */
{"LWHO",	fun_lwho,	0},	/* Return list of connected users */
{"MATCH",	fun_match,	2},	/* Find word in string */
{"MAX",		fun_max,	FN_VARARGS},	/* Return max integer value */
{"MEMBER",	fun_member,	2},	/* Test if words is in list */
{"MID",		fun_mid,	3},	/* Extract characters from string */
{"MIN",		fun_min,	FN_VARARGS},	/* Return min integer value */
{"MOD",		fun_mod,	2},	/* Integer modulus */
{"MONEY",	fun_money,	1},	/* Wealth of object */
{"MUDNAME",	fun_mudname,	0},	/* Name of the running mud */
{"MUL",		fun_mul,	2},	/* Integer multiplication */
{"NAME",	fun_name,	1},	/* Name of object */
{"NEARBY",	fun_nearby,	2},	/* Is obj1 near obj2? */
{"NEQ",		fun_neq,	2},	/* Numeric not equal */
{"NEXT",	fun_next,	1},	/* Next item/exit in chain */
{"NOT",		fun_not,	1},	/* Logical NOT */
{"NUM",		fun_num,	1},	/* Return DB number of object */
{"OBJ",		fun_obj,	1},	/* Objective pronoun sub */
{"OR",		fun_or,		2},	/* Logical OR */
{"OWNER",	fun_owner,	1},	/* Owner of object */
{"PARENT",	fun_parent,	1},	/* Parent of object */
{"POS",		fun_pos,	2},	/* Find substring in string */
{"POSS",	fun_poss,	1},	/* Possessive pronoun sub */
{"RAND",	fun_rand,	1},	/* Return a random number */
{"REMOVE",	fun_remove,	2},	/* Remove a word from a string */
{"REST",	fun_rest,	-1},	/* Return all but first word in str */
{"REVERSE",	fun_reverse,	-1},	/* Reverse a string */
{"REVWORDS",	fun_revwords,	-1},	/* Reverse order of words in string */
{"ROOM",	fun_room,	1},	/* What room contains obj? */
{"S",		fun_s,		-1},	/* Perform substitution on string */
{"SEARCH",	fun_search,	-1},	/* Search the db for things */
{"SECS",	fun_secs,	0},	/* seconds since 0:00 1/1/1970 */
{"SECURE",	fun_secure,	-1},	/* Remove '}' and ';' from string */
{"SIGN",	fun_sign,	1},	/* Return sign of arg (-1, 0, or 1) */
{"SPACE",	fun_space,	1},	/* Generate spaces */
{"STARTTIME",	fun_starttime,	0},	/* Char string time when MUSH booted */
{"STATS",	fun_stats,	1},	/* Get db size stats for player */
{"STRLEN",	fun_strlen,	-1},	/* Return length of string */
{"SUB",		fun_sub,	2},	/* Subtract arg2 from arg1 */
{"SUBJ",	fun_subj,	1},	/* Subjective pronoun sub */
{"SWITCH",	fun_switch,	FN_VARARGS},	/* Function from of @switch */
{"TIME",	fun_time,	0},	/* Return the current time */
{"TYPE",	fun_type,	1},	/* Return type of object */
{"U",		fun_u,		FN_VARARGS},	/* Return rslt of eval attr */
{"UCSTR",	fun_ucstr,	-1},	/* Uppercase string */
{"V",		fun_v,		1},	/* All kinds of neat stuff */
{"VERSION",	fun_version,	0},	/* Return MUSH version number */
{"WORDS",	fun_words,	-1},	/* Number of words in a string */
{"WORDPOS",     fun_wordpos,    2},     /* which word is this char pos in? */
{"XOR",		fun_xor,	2},	/* Logical XOR */
{NULL,		NULL,		0}
};

void init_functab()
{
FUN	*fp;

	hashinit(&mudstate.func_htab, 29);
	for (fp=flist; fp->name; fp++)
		hashadd((char *)fp->name, (int *)fp, &mudstate.func_htab);
}