btmux-0.6-rc4/doc/
btmux-0.6-rc4/event/
btmux-0.6-rc4/game/
btmux-0.6-rc4/game/maps/
btmux-0.6-rc4/game/mechs/
btmux-0.6-rc4/game/text/help/
btmux-0.6-rc4/game/text/help/cat_faction/
btmux-0.6-rc4/game/text/help/cat_inform/
btmux-0.6-rc4/game/text/help/cat_misc/
btmux-0.6-rc4/game/text/help/cat_mux/
btmux-0.6-rc4/game/text/help/cat_mux/cat_commands/
btmux-0.6-rc4/game/text/help/cat_mux/cat_functions/
btmux-0.6-rc4/game/text/help/cat_templates/
btmux-0.6-rc4/game/text/wizhelp/
btmux-0.6-rc4/include/
btmux-0.6-rc4/misc/
btmux-0.6-rc4/python/
btmux-0.6-rc4/src/hcode/btech/
btmux-0.6-rc4/tree/
/*
 * functions.c - MUX function handlers 
 */

#include "copyright.h"
#include "config.h"

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

#include "mudconf.h"
#include "db.h"
#include "flags.h"
#include "powers.h"
#include "attrs.h"
#include "externs.h"
#include "match.h"
#include "command.h"
#include "functions.h"
#include "misc.h"
#include "alloc.h"
#include "ansi.h"
#include "comsys.h"
#include "interface.h"

UFUN *ufun_head;

extern NAMETAB indiv_attraccess_nametab[];

extern int game_lag(void);

extern void cf_log_notfound(dbref player, char *cmd,
							const char *thingname, char *thing);

/*
 * Function definitions from funceval.c 
 */

extern void fun_btdesignex();
extern void fun_btgetcharvalue();
extern void fun_btmakemechs();
extern void fun_btmapelev();
extern void fun_btmapterr();
extern void fun_btsetcharvalue();
extern void fun_btgetxcodevalue();
extern void fun_btmakepilotroll();
extern void fun_btsetxcodevalue();
extern void fun_btstores();
extern void fun_btunderrepair();
extern void fun_btdamages();
extern void fun_btcritstatus();
extern void fun_btarmorstatus();
extern void fun_btsetarmorstatus();
extern void fun_btweaponstatus();
extern void fun_btthreshold();
extern void fun_btdamagemech();
extern void fun_bttechstatus();
extern void fun_btpartmatch();
extern void fun_btpartname();
extern void fun_btloadmap();
extern void fun_btloadmech();
extern void fun_btmechfreqs();
extern void fun_btmapunits();
extern void fun_btgetxcodevalue_ref();
extern void fun_btarmorstatus_ref();
extern void fun_btcritstatus_ref();
extern void fun_btweaponstatus_ref();
extern void fun_btid2db();
extern void fun_bthexlos();
extern void fun_btlosm2m();
extern void fun_bthexemit();

extern void fun_btgetweight();
extern void fun_btpartweight();
extern void fun_btaddstores();
extern void fun_btremovestores();
extern void fun_bttechtime();
extern void fun_btcritslot();
extern void fun_btcritslot_ref();
extern void fun_btgetrange();
extern void fun_btsetmaxspeed();
extern void fun_btgetrealmaxspeed();
extern void fun_btgetbv();
extern void fun_btgetbv_ref();
extern void fun_bttechlist();
extern void fun_bttechlist_ref();
extern void fun_btpayload_ref();
extern void fun_btshowstatus_ref();
extern void fun_btshowwspecs_ref();
extern void fun_btshowcritstatus_ref();
extern void fun_btengrate();
extern void fun_btengrate_ref();
extern void fun_btweapstat();
extern void fun_btnumrepjobs();
extern void fun_btsetxy();
extern void fun_btmapemit();
extern void fun_btparttype();
extern void fun_btticweaps();
#ifdef BT_ADVANCED_ECON
extern void fun_btgetpartcost();
extern void fun_btsetpartcost();
extern void fun_btfasabasecost_ref();
#endif
extern void fun_btunitfixable();
extern void fun_btlistblz();
extern void fun_bthexinblz();
extern void fun_btcharlist();
extern void fun_cobj();
extern void fun_config();

extern void fun_cwho();
extern void fun_clist();
extern void fun_cemit();
extern void fun_beep();
extern void fun_ansi();
extern void fun_zone();
#ifdef SIDE_EFFECT_FUNCTIONS
extern void fun_link();
extern void fun_tel();
extern void fun_pemit();
extern void fun_create();
extern void fun_set();
extern void fun_setlock();
#endif
extern void fun_last();
extern void fun_matchall();
extern void fun_ports();
extern void fun_mix();
extern void fun_foreach();
extern void fun_munge();
extern void fun_visible();
extern void fun_elements();
extern void fun_grab();
extern void fun_graball();
extern void fun_scramble();
extern void fun_shuffle();
extern void fun_sortby();
extern void fun_default();
extern void fun_edefault();
extern void fun_udefault();
extern void fun_findable();
extern void fun_isword();
extern void fun_hasattr();
extern void fun_hasattrp();
extern void fun_zwho();
extern void fun_zplayers();
extern void fun_inzone();
extern void fun_children();
extern void fun_encrypt();
extern void fun_decrypt();
extern void fun_objeval();
extern void fun_squish();
extern void fun_stripansi();
extern void fun_zfun();
extern void fun_columns();
extern void fun_playmem();
#ifdef USE_PYTHON
extern void fun_python();
extern void fun_pythoncall();
#endif
extern void fun_objmem();
extern void fun_orflags();
extern void fun_andflags();
extern void fun_strtrunc();
extern void fun_ifelse();
extern void fun_inc();
extern void fun_dec();
extern void fun_mail();
extern void fun_mailfrom();
extern void fun_die();
extern void fun_lit();
extern void fun_shl();
extern void fun_shr();
extern void fun_vadd();
extern void fun_vsub();
extern void fun_vmul();
extern void fun_vmag();
extern void fun_vunit();
extern void fun_vdim();
extern void fun_strcat();
extern void fun_grep();
extern void fun_grepi();
extern void fun_art();
extern void fun_alphamax();
extern void fun_alphamin();
extern void fun_valid();
extern void fun_hastype();
extern void fun_lparent();
extern void fun_empty();
extern void fun_push();
extern void fun_peek();
extern void fun_pop();
extern void fun_items();
extern void fun_lstack();
extern void fun_regmatch();
extern void fun_translate();

/*
 * This is for functions that take an optional delimiter character 
 */

#define varargs_preamble(xname,xnargs)					\
	if (!fn_range_check(xname, nfargs, xnargs-1, xnargs, buff, bufc))	\
		return;							\
	if (!delim_check(fargs, nfargs, xnargs, &sep, buff, bufc, 0,	\
		player, cause, cargs, ncargs))				\
		return;

#define evarargs_preamble(xname,xnargs)					\
	if (!fn_range_check(xname, nfargs, xnargs-1, xnargs, buff, bufc))	\
		return;							\
	if (!delim_check(fargs, nfargs, xnargs, &sep, buff, bufc, 1,	\
	    player, cause, cargs, ncargs))				\
		return;

#define mvarargs_preamble(xname,xminargs,xnargs)			\
	if (!fn_range_check(xname, nfargs, xminargs, xnargs, buff, bufc))	\
		return;							\
	if (!delim_check(fargs, nfargs, xnargs, &sep, buff, bufc, 0,		\
	    player, cause, cargs, ncargs))				\
		return;

/**
 * Trim off leading and trailing spaces if the separator char is a space 
 */
char *trim_space_sep(char *str, char sep)
{
	char *p;

    if(*str == '\0') return str;
	if(sep != ' ')
		return str;
	while (*str && (*str == ' '))
		str++;
	for(p = str; *p; p++);
	for(p--; *p == ' ' && p > str; p--);
	p++;
	*p = '\0';
	return str;
}

/*
 * next_token: Point at start of next token in string 
 */

char *next_token(char *str, char sep)
{
	while (*str && (*str != sep))
		str++;
	if(!*str)
		return NULL;
	str++;
	if(sep == ' ') {
		while (*str == sep)
			str++;
	}
	return str;
}

/*
 * split_token: Get next token from string as null-term string.  String is
 * * destructively modified.
 */

char *split_token(char **sp, char sep)
{
	char *str, *save;

	save = str = *sp;
	if(!str) {
		*sp = NULL;
		return NULL;
	}
	while (*str && (*str != sep))
		str++;
	if(*str) {
		*str++ = '\0';
		if(sep == ' ') {
			while (*str == sep)
				str++;
		}
	} else {
		str = NULL;
	}
	*sp = str;
	return save;
}

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

/*
 * ---------------------------------------------------------------------------
 * * List management utilities.
 */

#define	ALPHANUM_LIST	1
#define	NUMERIC_LIST	2
#define	DBREF_LIST	3
#define	FLOAT_LIST	4

static int autodetect_list(char *ptrs[], int nitems)
{
	int sort_type, i;
	char *p;

	sort_type = NUMERIC_LIST;
	for(i = 0; i < nitems; i++) {
		switch (sort_type) {
		case NUMERIC_LIST:
			if(!is_number(ptrs[i])) {

				/*
				 * If non-numeric, switch to alphanum sort. * 
				 * 
				 * *  * *  * * Exception: if this is the
				 * first * element * * and * it is a good
				 * dbref, * switch to a * * dbref sort. *
				 * We're a * little looser than *  * the
				 * normal * 'good  * dbref' rules, any * *
				 * number following # * the #-sign is
				 * accepted.  
				 */

				if(i == 0) {
					p = ptrs[i];
					if(*p++ != NUMBER_TOKEN) {
						return ALPHANUM_LIST;
					} else if(is_integer(p)) {
						sort_type = DBREF_LIST;
					} else {
						return ALPHANUM_LIST;
					}
				} else {
					return ALPHANUM_LIST;
				}
			} else if(index(ptrs[i], '.')) {
				sort_type = FLOAT_LIST;
			}
			break;
		case FLOAT_LIST:
			if(!is_number(ptrs[i])) {
				sort_type = ALPHANUM_LIST;
				return ALPHANUM_LIST;
			}
			break;
		case DBREF_LIST:
			p = ptrs[i];
			if(*p++ != NUMBER_TOKEN)
				return ALPHANUM_LIST;
			if(!is_integer(p))
				return ALPHANUM_LIST;
			break;
		default:
			return ALPHANUM_LIST;
		}
	}
	return sort_type;
}

static int get_list_type(char *fargs[], int nfargs, int type_pos,
						 char *ptrs[], int nitems)
{
	if(nfargs >= type_pos) {
		switch (ToLower(*fargs[type_pos - 1])) {
		case 'd':
			return DBREF_LIST;
		case 'n':
			return NUMERIC_LIST;
		case 'f':
			return FLOAT_LIST;
		case '\0':
			return autodetect_list(ptrs, nitems);
		default:
			return ALPHANUM_LIST;
		}
	}
	return autodetect_list(ptrs, nitems);
}

int list2arr(char *arr[], int maxlen, char *list, char sep)
{
	char *p;
	int i;

	list = trim_space_sep(list, sep);
	p = split_token(&list, sep);
	for(i = 0; p && i < maxlen; i++, p = split_token(&list, sep)) {
		arr[i] = p;
	}
	return i;
}

void arr2list(char *arr[], int alen, char *list, char **bufc, char sep)
{
	int i;

	for(i = 0; i < alen; i++) {
		safe_str(arr[i], list, bufc);
		safe_chr(sep, list, bufc);
	}
	if(*bufc != list)
		(*bufc)--;
}

static int dbnum(char *dbr)
{
	if((strlen(dbr) < 2) && (*dbr != '#'))
		return 0;
	else
		return atoi(dbr + 1);
}

/**
 * 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;
	return 1;
}

/**
 * Copy the floating point value into a buffer and make it presentable
 */
static void fval(char *buff, char **bufc, double result)
{
	char *p, *buf1;

	buf1 = *bufc;
	safe_tprintf_str(buff, bufc, "%.6f", result);	/*
													 * get double val * * 
													 * into buffer 
													 */
	**bufc = '\0';
	p = (char *) rindex(buf1, '0');
	if(p == NULL) {				/*
								 * remove useless trailing 0's 
								 */
		return;
	} else if(*(p + 1) == '\0') {
		while (*p == '0') {
			*p-- = '\0';
		}
		*bufc = p + 1;
	}
	p = (char *) rindex(buf1, '.');	/*
									 * take care of dangling '.' 
									 */
	if((p != NULL) && (*(p + 1) == '\0')) {
		*p = '\0';
		*bufc = p;
	}
}

/**
 * Check # of args to a function with an optional argument
 * for validity.
 */
int fn_range_check(const char *fname, int nfargs, int minargs, int maxargs,
				   char *result, char **bufc)
{
	if((nfargs >= minargs) && (nfargs <= maxargs))
		return 1;

	if(maxargs == (minargs + 1))
		safe_tprintf_str(result, bufc,
						 "#-1 FUNCTION (%s) EXPECTS %d OR %d ARGUMENTS",
						 fname, minargs, maxargs);
	else
		safe_tprintf_str(result, bufc,
						 "#-1 FUNCTION (%s) EXPECTS BETWEEN %d AND %d ARGUMENTS",
						 fname, minargs, maxargs);
	return 0;
}

/**
 * * delim_check: obtain delimiter
 */
int delim_check(char *fargs[], int nfargs, int sep_arg, char *sep, char *buff,
				char **bufc, int eval, dbref player, dbref cause,
				char *cargs[], int ncargs)
{
	char *tstr, *bp, *str;
	int tlen;

	if(nfargs >= sep_arg) {
		tlen = strlen(fargs[sep_arg - 1]);
		if(tlen <= 1)
			eval = 0;
		if(eval) {
			tstr = bp = alloc_lbuf("delim_check");
			str = fargs[sep_arg - 1];
			exec(tstr, &bp, 0, player, cause, EV_EVAL | EV_FCHECK, &str,
				 cargs, ncargs);
			*bp = '\0';
			tlen = strlen(tstr);
			*sep = *tstr;
			free_lbuf(tstr);
		}
		if(tlen == 0) {
			*sep = ' ';
		} else if(tlen != 1) {
			safe_str("#-1 SEPARATOR MUST BE ONE CHARACTER", buff, bufc);
			return 0;
		} else if(!eval) {
			*sep = *fargs[sep_arg - 1];
		}
	} else {
		*sep = ' ';
	}
	return 1;
}

/**
 * Returns number of words in a string.
 * Added 1/28/91 Philip D. Wasson
 */
int countwords(char *str, char sep)
{
	int n;

	str = trim_space_sep(str, sep);
	if(!*str)
		return 0;
	for(n = 0; str; str = next_token(str, sep), n++);
	return n;
}

static void fun_words(char *buff, char **bufc, dbref player, dbref cause,
					  char *fargs[], int nfargs, char *cargs[], int ncargs)
{
	char sep;

	if(nfargs == 0) {
		safe_str("0", buff, bufc);
		return;
	}
	varargs_preamble("WORDS", 2);
	safe_tprintf_str(buff, bufc, "%d", countwords(fargs[0], sep));
}

/**
 * Returns the flags on an object.
 * Because @switch is case-insensitive, not quite as useful as it could be.
 */
static void fun_flags(char *buff, char **bufc, dbref player, dbref cause,
					  char *fargs[], int nfargs, char *cargs[], int ncargs)
{
	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);
		safe_str(buff2, buff, bufc);
		free_sbuf(buff2);
	} else
		safe_str("#-1", buff, bufc);
	return;
}

/**
 * Return a random number from 0 to arg1-1
 */
static void fun_rand(char *buff, char **bufc, dbref player, dbref cause,
					 char *fargs[], int nfargs, char *cargs[], int ncargs)
{
	int num;

	num = atoi(fargs[0]);
	if(num < 1)
		safe_str("0", buff, bufc);
	else
		safe_tprintf_str(buff, bufc, "%ld", (random() % num));
}

/**
 * Returns the absolute value of its argument.
 */
static void fun_abs(char *buff, char **bufc, dbref player, dbref cause,
					char *fargs[], int nfargs, char *cargs[], int ncargs)
{
	double num;

	num = atof(fargs[0]);
	if(num == 0.0) {
		safe_str("0", buff, bufc);
	} else if(num < 0.0) {
		fval(buff, bufc, -num);
	} else {
		fval(buff, bufc, num);
	}
}

/**
 * Returns -1, 0, or 1 based on the the sign of its argument.
 */
static void fun_sign(char *buff, char **bufc, dbref player, dbref cause,
					 char *fargs[], int nfargs, char *cargs[], int ncargs)
{
	double num;

	num = atof(fargs[0]);
	if(num < 0)
		safe_str("-1", buff, bufc);
	else if(num > 0)
		safe_str("1", buff, bufc);
	else
		safe_str("0", buff, bufc);
}

extern char *silly_atr_get(dbref, int);

time_t mytime(dbref player)
{
	struct timeval tv;
	struct timezone tz;
	char *tzstr;
	int hours, dst;

	tzstr = silly_atr_get(player, A_TZ);
	if(sscanf(tzstr, "%d:%d", &hours, &dst) < 2)
		return mudstate.now;
	tz.tz_minuteswest = (0 - hours) * 60;
	tz.tz_dsttime = dst;
	gettimeofday(&tv, &tz);
	return tv.tv_sec;
}

/* Returns secs converted to digits, just like WHO does for connect time */

static void fun_digittime(char *buff, char **bufc, dbref player, dbref cause,
						  char *fargs[], int nfargs, char *cargs[],
						  int ncargs)
{
	register struct tm *delta;
	static char buf[64];
	time_t dt;

	dt = atol(fargs[0]);

	if(dt < 0)
		dt = 0;

	delta = gmtime(&dt);
	if(delta->tm_yday > 0) {
		sprintf(buf, "%dd %02d:%02d", delta->tm_yday, delta->tm_hour,
				delta->tm_min);
	} else {
		sprintf(buf, "%02d:%02d", delta->tm_hour, delta->tm_min);
	}

	safe_tprintf_str(buff, bufc, buf);

}

/**
 * Returns nicely-formatted time.
 */
static void fun_time(char *buff, char **bufc, dbref player, dbref cause,
					 char *fargs[], int nfargs, char *cargs[], int ncargs)
{
	char *temp;

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

/**
 * Seconds since 0:00 1/1/70
 */
static void fun_secs(char *buff, char **bufc, dbref player, dbref cause,
					 char *fargs[], int nfargs, char *cargs[], int ncargs)
{
	safe_tprintf_str(buff, bufc, "%d", mudstate.now);
}

/**
 * Converts seconds to time string, based off 0:00 1/1/70
 */
static void fun_convsecs(char *buff, char **bufc, dbref player, dbref cause,
						 char *fargs[], int nfargs, char *cargs[], int ncargs)
{
	char *temp;
	time_t tt;

	tt = atol(fargs[0]);
	temp = (char *) ctime(&tt);
	temp[strlen(temp) - 1] = '\0';
	safe_str(temp, buff, bufc);
}

/**
 * converts time string to seconds, based off 0:00 1/1/70
 * additional auxiliary function and table used to parse time string,
 * since no ANSI standard function are available to do this.
 */
static const char *monthtab[] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun",
	"Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
};

static const char daystab[] = { 31, 29, 31, 30, 31, 30,
	31, 31, 30, 31, 30, 31
};

/**
 * Converts time string to a struct tm. Returns 1 on success, 0 on fail.
 * Time string format is always 24 characters long, in format
 * Ddd Mmm DD HH:MM:SS YYYY
 */

#define	get_substr(buf, p) { \
	p = (char *)index(buf, ' '); \
	if (p) { \
		*p++ = '\0'; \
		while (*p == ' ') p++; \
	} \
}

int do_convtime(char *str, struct tm *ttm)
{
	char *buf, *p, *q;
	int i;

	if(!str || !ttm)
		return 0;
	while (*str == ' ')
		str++;
	buf = p = alloc_sbuf("do_convtime");	/*
											 * make a temp copy of arg 
											 */
	safe_sb_str(str, buf, &p);
	*p = '\0';

	get_substr(buf, p);			/*
								 * day-of-week or month 
								 */
	if(!p || strlen(buf) != 3) {
		free_sbuf(buf);
		return 0;
	}
	for(i = 0; (i < 12) && string_compare(monthtab[i], p); i++);
	if(i == 12) {
		get_substr(p, q);		/*
								 * month 
								 */
		if(!q || strlen(p) != 3) {
			free_sbuf(buf);
			return 0;
		}
		for(i = 0; (i < 12) && string_compare(monthtab[i], p); i++);
		if(i == 12) {
			free_sbuf(buf);
			return 0;
		}
		p = q;
	}
	ttm->tm_mon = i;

	get_substr(p, q);			/*
								 * day of month 
								 */
	if(!q || (ttm->tm_mday = atoi(p)) < 1 || ttm->tm_mday > daystab[i]) {
		free_sbuf(buf);
		return 0;
	}
	p = (char *) index(q, ':');	/*
								 * hours 
								 */
	if(!p) {
		free_sbuf(buf);
		return 0;
	}
	*p++ = '\0';
	if((ttm->tm_hour = atoi(q)) > 23 || ttm->tm_hour < 0) {
		free_sbuf(buf);
		return 0;
	}
	if(ttm->tm_hour == 0) {
		while (isspace(*q))
			q++;
		if(*q != '0') {
			free_sbuf(buf);
			return 0;
		}
	}
	q = (char *) index(p, ':');	/*
								 * minutes 
								 */
	if(!q) {
		free_sbuf(buf);
		return 0;
	}
	*q++ = '\0';
	if((ttm->tm_min = atoi(p)) > 59 || ttm->tm_min < 0) {
		free_sbuf(buf);
		return 0;
	}
	if(ttm->tm_min == 0) {
		while (isspace(*p))
			p++;
		if(*p != '0') {
			free_sbuf(buf);
			return 0;
		}
	}
	get_substr(q, p);			/*
								 * seconds 
								 */
	if(!p || (ttm->tm_sec = atoi(q)) > 59 || ttm->tm_sec < 0) {
		free_sbuf(buf);
		return 0;
	}
	if(ttm->tm_sec == 0) {
		while (isspace(*q))
			q++;
		if(*q != '0') {
			free_sbuf(buf);
			return 0;
		}
	}
	get_substr(p, q);			/*
								 * year 
								 */
	if((ttm->tm_year = atoi(p)) == 0) {
		while (isspace(*p))
			p++;
		if(*p != '0') {
			free_sbuf(buf);
			return 0;
		}
	}
	free_sbuf(buf);
	if(ttm->tm_year > 100)
		ttm->tm_year -= 1900;
	if(ttm->tm_year < 0) {
		return 0;
	}
#define LEAPYEAR_1900(yr) ((yr)%400==100||((yr)%100!=0&&(yr)%4==0))
	return (ttm->tm_mday != 29 || i != 1 || LEAPYEAR_1900(ttm->tm_year));
#undef LEAPYEAR_1900
}

static void fun_convtime(char *buff, char **bufc, dbref player, dbref cause,
						 char *fargs[], int nfargs, char *cargs[], int ncargs)
{
	struct tm *ttm;

	ttm = localtime(&mudstate.now);
	if(do_convtime(fargs[0], ttm))
		safe_tprintf_str(buff, bufc, "%d", timelocal(ttm));
	else
		safe_str("-1", buff, bufc);
}

/**
 * Converts number to minutes/secs/days
 */

#define UPTIME_UNITS 6

struct {
	int multip;
	char *name;
	char *sname;
} uptime_unit_table[UPTIME_UNITS] = {
	{
	60 *60 * 24 * 30 * 12, "year", "y"}, {
	60 *60 * 24 * 30, "month", "m"}, {
	60 *60 * 24, "day", "d"}, {
	60 *60, "hour", "h"}, {
	60, "minute", "m"}, {
	1, "second", "s"}
};

char *get_uptime_to_string(int uptime)
{
	char *buf = alloc_sbuf("get_uptime_to_string");
	int units[UPTIME_UNITS];
	int taim = uptime;
	int ut = 0, uc = 0, foofaa;

	if(uptime <= 0) {
		strcpy(buf, "#-1 INVALID VALUE");
		return buf;
	}
	for(ut = 0; ut < UPTIME_UNITS; ut++)
		units[ut] = 0;
	ut = 0;
	buf[0] = 0;
	while (taim > 0) {
		if((foofaa = (taim / uptime_unit_table[ut].multip)) > 0) {
			uc++;
			units[ut] = foofaa;
			taim -= uptime_unit_table[ut].multip * foofaa;
		}
		ut++;
	}
	/*
	 * Now, we got it..
	 */
	for(ut = 0; ut < UPTIME_UNITS; ut++) {
		if(units[ut]) {
			uc--;
			if(units[ut] > 1)
				sprintf(buf + strlen(buf), "%d %ss", units[ut],
						uptime_unit_table[ut].name);
			else
				sprintf(buf + strlen(buf), "%d %s", units[ut],
						uptime_unit_table[ut].name);
			if(uc > 1)
				strcat(buf, ", ");
			else if(uc > 0)
				strcat(buf, " and ");
		}
	}
	return buf;
}

char *get_uptime_to_short_string(int uptime)
{
	char *buf = alloc_sbuf("get_uptime_to_string");
	int units[UPTIME_UNITS];
	int taim = uptime;
	int ut = 0, uc = 0, foofaa;

	if(uptime <= 0) {
		strcpy(buf, "#-1 INVALID VALUE");
		return buf;
	}
	for(ut = 0; ut < UPTIME_UNITS; ut++)
		units[ut] = 0;
	ut = 0;
	buf[0] = 0;
	while (taim > 0) {
		if((foofaa = (taim / uptime_unit_table[ut].multip)) > 0) {
			uc++;
			units[ut] = foofaa;
			taim -= uptime_unit_table[ut].multip * foofaa;
		}
		ut++;
	}
	/*
	 * Now, we got it..
	 */
	for(ut = 0; ut < UPTIME_UNITS; ut++) {
		if(units[ut]) {
			uc--;
			if(units[ut] > 1)
				sprintf(buf + strlen(buf), "%d%s", units[ut],
						uptime_unit_table[ut].sname);
			else
				sprintf(buf + strlen(buf), "%d%s", units[ut],
						uptime_unit_table[ut].sname);
			if(uc > 0)
				strcat(buf, " ");
		}
	}
	return buf;
}

static void fun_convuptime(char *buff, char **bufc, dbref player, dbref cause,
						   char *fargs[], int nfargs, char *cargs[],
						   int ncargs)
{
	char *uptimestring = get_uptime_to_string(atoi(fargs[0]));

	safe_str(uptimestring, buff, bufc);
	free_sbuf(uptimestring);
}

static void fun_lag(char *buff, char **bufc, dbref player, dbref cause,
					char *fargs[], int nfargs, char *cargs[], int ncargs)
{
	char buf[256];

	sprintf(buf, "%d", game_lag());
	safe_str(buf, buff, bufc);
}

/**
 * What time did this system last reboot?
 */
static void fun_starttime(char *buff, char **bufc, dbref player, dbref cause,
						  char *fargs[], int nfargs, char *cargs[],
						  int ncargs)
{
	char *temp;

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

/**
 * What time (in seconds) did this system last reboot?
 */
static void fun_startsecs(char *buff, char **bufc, dbref player, dbref cause,
						  char *fargs[], int nfargs, char *cargs[],
						  int ncargs)
{
	safe_tprintf_str(buff, bufc, "%d", mudstate.start_time);
}

/**
 * What is the record number of players connected?
 */
static void fun_connrecord(char *buff, char **bufc, dbref player, dbref cause,
						   char *fargs[], int nfargs, char *cargs[],
						   int ncargs)
{
	safe_tprintf_str(buff, bufc, "%d", mudstate.record_players);
}

/**
 * Get attribute from object.
 */
int check_read_perms(dbref player, dbref thing, ATTR * attr, int aowner,
					 int aflags, char *buff, char **bufc)
{
	int see_it;

	/*
	 * If we have explicit read permission to the attr, return it 
	 */

	if(See_attr_explicit(player, thing, attr, aowner, aflags))
		return 1;

	/*
	 * If we are nearby or have examine privs to the attr and it is * * * 
	 * 
	 * * visible to us, return it. 
	 */

	see_it = See_attr(player, thing, attr, aowner, aflags);
	if((Examinable(player, thing) || nearby(player, thing) ||
		See_All(player)) && see_it)
		return 1;

	/*
	 * For any object, we can read its visible attributes, EXCEPT * for * 
	 * 
	 * *  * * descs, which are only visible if read_rem_desc is on. 
	 */

	if(see_it) {
		if(!mudconf.read_rem_desc && (attr->number == A_DESC)) {
			safe_str("#-1 TOO FAR AWAY TO SEE", buff, bufc);
			return 0;
		} else {
			return 1;
		}
	}
	safe_str("#-1 PERMISSION DENIED", buff, bufc);
	return 0;
}

static void fun_get(char *buff, char **bufc, dbref player, dbref cause,
					char *fargs[], int nfargs, char *cargs[], int ncargs)
{
	dbref thing, aowner;
	int attrib, free_buffer, aflags;
	ATTR *attr;
	char *atr_gotten;
	struct boolexp *bool;

	if(!parse_attrib(player, fargs[0], &thing, &attrib)) {
		safe_str("#-1 NO MATCH", buff, bufc);
		return;
	}
	if(attrib == NOTHING) {
		return;
	}
	free_buffer = 1;
	attr = atr_num(attrib);		/*
								 * We need the attr's flags for this: 
								 */
	if(!attr) {
		return;
	}
	if(attr->flags & AF_IS_LOCK) {
		atr_gotten = atr_get(thing, attrib, &aowner, &aflags);
		if(Read_attr(player, thing, attr, aowner, aflags)) {
			bool = parse_boolexp(player, atr_gotten, 1);
			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);
	}

	/*
	 * Perform access checks.  c_r_p fills buff with an error message * * 
	 * 
	 * *  * * if needed. 
	 */

	if(check_read_perms(player, thing, attr, aowner, aflags, buff, bufc))
		safe_str(atr_gotten, buff, bufc);
	if(free_buffer)
		free_lbuf(atr_gotten);
	return;
}

static void fun_xget(char *buff, char **bufc, dbref player, dbref cause,
					 char *fargs[], int nfargs, char *cargs[], int ncargs)
{
	dbref thing, aowner;
	int attrib, free_buffer, aflags;
	ATTR *attr;
	char *atr_gotten;
	struct boolexp *bool;

	if(!*fargs[0] || !*fargs[1])
		return;

	if(!parse_attrib(player, tprintf("%s/%s", fargs[0], fargs[1]), &thing,
					 &attrib)) {
		safe_str("#-1 NO MATCH", buff, bufc);
		return;
	}
	if(attrib == NOTHING) {
		return;
	}
	free_buffer = 1;
	attr = atr_num(attrib);		/*
								 * We need the attr's flags for this: 
								 */
	if(!attr) {
		return;
	}
	if(attr->flags & AF_IS_LOCK) {
		atr_gotten = atr_get(thing, attrib, &aowner, &aflags);
		if(Read_attr(player, thing, attr, aowner, aflags)) {
			bool = parse_boolexp(player, atr_gotten, 1);
			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);
	}

	/*
	 * Perform access checks.  c_r_p fills buff with an error message * * 
	 * 
	 * *  * * if needed. 
	 */

	if(check_read_perms(player, thing, attr, aowner, aflags, buff, bufc))
		safe_str(atr_gotten, buff, bufc);
	if(free_buffer)
		free_lbuf(atr_gotten);
	return;
}

static void fun_get_eval(char *buff, char **bufc, dbref player, dbref cause,
						 char *fargs[], int nfargs, char *cargs[], int ncargs)
{
	dbref thing, aowner;
	int attrib, free_buffer, aflags, eval_it;
	ATTR *attr;
	char *atr_gotten, *str;
	struct boolexp *bool;

	if(!parse_attrib(player, fargs[0], &thing, &attrib)) {
		safe_str("#-1 NO MATCH", buff, bufc);
		return;
	}
	if(attrib == NOTHING) {
		return;
	}
	free_buffer = 1;
	eval_it = 1;
	attr = atr_num(attrib);		/*
								 * We need the attr's flags for this: 
								 */
	if(!attr) {
		return;
	}
	if(attr->flags & AF_IS_LOCK) {
		atr_gotten = atr_get(thing, attrib, &aowner, &aflags);
		if(Read_attr(player, thing, attr, aowner, aflags)) {
			bool = parse_boolexp(player, atr_gotten, 1);
			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(!check_read_perms(player, thing, attr, aowner, aflags, buff, bufc)) {
		if(free_buffer)
			free_lbuf(atr_gotten);
		return;
	}
	if(eval_it) {
		str = atr_gotten;
		exec(buff, bufc, 0, thing, player, EV_FIGNORE | EV_EVAL, &str,
			 (char **) NULL, 0);
	} else {
		safe_str(atr_gotten, buff, bufc);
	}
	if(free_buffer)
		free_lbuf(atr_gotten);
	return;
}

static void fun_subeval(char *buff, char **bufc, dbref player, dbref cause,
						char *fargs[], int nfargs, char *cargs[], int ncargs)
{
	char *str;

	if(nfargs != 1) {
		safe_str("#-1 FUNCTION (EVALNOCOMP) EXPECTS 1 OR 2 ARGUMENTS",
				 buff, bufc);
		return;
	}

	str = fargs[0];
	exec(buff, bufc, 0, player, cause,
		 EV_NO_LOCATION | EV_NOFCHECK | EV_FIGNORE | EV_NO_COMPRESS, &str,
		 (char **) NULL, 0);
}

static void fun_eval(char *buff, char **bufc, dbref player, dbref cause,
					 char *fargs[], int nfargs, char *cargs[], int ncargs)
{
	dbref thing, aowner;
	int attrib, free_buffer, aflags, eval_it;
	ATTR *attr;
	char *atr_gotten, *str;
	struct boolexp *bool;

	if((nfargs != 1) && (nfargs != 2)) {
		safe_str("#-1 FUNCTION (EVAL) EXPECTS 1 OR 2 ARGUMENTS", buff, bufc);
		return;
	}
	if(nfargs == 1) {
		str = fargs[0];
		exec(buff, bufc, 0, player, cause, EV_EVAL, &str, (char **) NULL, 0);
		return;
	}
	if(!*fargs[0] || !*fargs[1])
		return;

	if(!parse_attrib(player, tprintf("%s/%s", fargs[0], fargs[1]), &thing,
					 &attrib)) {
		safe_str("#-1 NO MATCH", buff, bufc);
		return;
	}
	if(attrib == NOTHING) {
		return;
	}
	free_buffer = 1;
	eval_it = 1;
	attr = atr_num(attrib);
	if(!attr) {
		return;
	}
	if(attr->flags & AF_IS_LOCK) {
		atr_gotten = atr_get(thing, attrib, &aowner, &aflags);
		if(Read_attr(player, thing, attr, aowner, aflags)) {
			bool = parse_boolexp(player, atr_gotten, 1);
			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(!check_read_perms(player, thing, attr, aowner, aflags, buff, bufc)) {
		if(free_buffer)
			free_lbuf(atr_gotten);
		return;
	}
	if(eval_it) {
		str = atr_gotten;
		exec(buff, bufc, 0, thing, player, EV_FIGNORE | EV_EVAL, &str,
			 (char **) NULL, 0);
	} else {
		safe_str(atr_gotten, buff, bufc);
	}
	if(free_buffer)
		free_lbuf(atr_gotten);
	return;
}

/**
 * Call a user-defined function.
 */
static void do_ufun(char *buff, char **bufc, dbref player, dbref cause,
					char *fargs[], int nfargs, char *cargs[], int ncargs,
					int is_local)
{
	dbref aowner, thing;
	int aflags, anum, i;
	ATTR *ap;
	char *atext, *preserve[MAX_GLOBAL_REGS], *str;

	/*
	 * We need at least one argument 
	 */

	if(nfargs < 1) {
		safe_str("#-1 TOO FEW ARGUMENTS", buff, bufc);
		return;
	}
	/*
	 * 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) {
		return;
	}
	/*
	 * Use it if we can access it, otherwise return an error. 
	 */

	atext = atr_pget(thing, ap->number, &aowner, &aflags);
	if(!atext) {
		free_lbuf(atext);
		return;
	}
	if(!*atext) {
		free_lbuf(atext);
		return;
	}
	if(!check_read_perms(player, thing, ap, aowner, aflags, buff, bufc)) {
		free_lbuf(atext);
		return;
	}
	/*
	 * If we're evaluating locally, preserve the global registers. 
	 */

	if(is_local) {
		for(i = 0; i < MAX_GLOBAL_REGS; i++) {
			if(!mudstate.global_regs[i])
				preserve[i] = NULL;
			else {
				preserve[i] = alloc_lbuf("u_regs");
				StringCopy(preserve[i], mudstate.global_regs[i]);
			}
		}
	}
	/*
	 * Evaluate it using the rest of the passed function args 
	 */

	str = atext;
	exec(buff, bufc, 0, thing, cause, EV_FCHECK | EV_EVAL, &str,
		 &(fargs[1]), nfargs - 1);
	free_lbuf(atext);

	/*
	 * If we're evaluating locally, restore the preserved registers. 
	 */

	if(is_local) {
		for(i = 0; i < MAX_GLOBAL_REGS; i++) {
			if(preserve[i]) {
				if(!mudstate.global_regs[i])
					mudstate.global_regs[i] = alloc_lbuf("u_reg");
				StringCopy(mudstate.global_regs[i], preserve[i]);
				free_lbuf(preserve[i]);
			} else {
				if(mudstate.global_regs[i])
					*(mudstate.global_regs[i]) = '\0';
			}
		}
	}
}

static void fun_u(char *buff, char **bufc, dbref player, dbref cause,
				  char *fargs[], int nfargs, char *cargs[], int ncargs)
{
	do_ufun(buff, bufc, player, cause, fargs, nfargs, cargs, ncargs, 0);
}

static void fun_ulocal(char *buff, char **bufc, dbref player, dbref cause,
					   char *fargs[], int nfargs, char *cargs[], int ncargs)
{
	do_ufun(buff, bufc, player, cause, fargs, nfargs, cargs, ncargs, 1);
}

/**
 * Get parent of object.
 */
static void fun_parent(char *buff, char **bufc, dbref player, dbref cause,
					   char *fargs[], int nfargs, char *cargs[], int ncargs)
{
	dbref it;

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

/**
 * Make list from evaluating arg3 with each member of arg2.
 * arg1 specifies a delimiter character to use in the parsing of arg2.
 * NOTE: This function expects that its arguments have not been evaluated.
 */
static void fun_parse(char *buff, char **bufc, dbref player, dbref cause,
					  char *fargs[], int nfargs, char *cargs[], int ncargs)
{
	char *curr, *objstring, *buff2, *buff3, *cp, sep;
	char *dp, *str;
	int first, number = 0;

	evarargs_preamble("PARSE", 3);
	cp = curr = dp = alloc_lbuf("fun_parse");
	str = fargs[0];
	exec(curr, &dp, 0, player, cause, EV_STRIP | EV_FCHECK | EV_EVAL, &str,
		 cargs, ncargs);
	*dp = '\0';
	cp = trim_space_sep(cp, sep);
	if(!*cp) {
		free_lbuf(curr);
		return;
	}
	first = 1;
	while (cp) {
		if(!first)
			safe_chr(' ', buff, bufc);
		first = 0;
		number++;
		objstring = split_token(&cp, sep);
		buff2 = replace_string(BOUND_VAR, objstring, fargs[1]);
		buff3 = replace_string(LISTPLACE_VAR, tprintf("%d", number), buff2);
		str = buff3;
		exec(buff, bufc, 0, player, cause, EV_STRIP | EV_FCHECK | EV_EVAL,
			 &str, cargs, ncargs);
		free_lbuf(buff2);
		free_lbuf(buff3);
	}
	free_lbuf(curr);
}

/**
 * mid(foobar,2,3) returns oba
 */
static void fun_mid(char *buff, char **bufc, dbref player, dbref cause,
					char *fargs[], int nfargs, char *cargs[], int ncargs)
{
	int l, len;
	char *oldp;

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

/**
 * Returns first word in a string
 */
static void fun_first(char *buff, char **bufc, dbref player, dbref cause,
					  char *fargs[], int nfargs, char *cargs[], int ncargs)
{
	char *s, *first, sep;

	/*
	 * If we are passed an empty arglist return a null string 
	 */

	if(nfargs == 0) {
		return;
	}
	varargs_preamble("FIRST", 2);
	s = trim_space_sep(fargs[0], sep);	/*
										 * leading spaces ... 
										 */
	first = split_token(&s, sep);
	if(first) {
		safe_str(first, buff, bufc);
	}
}

/**
 * Returns all but the first word in a string 
 */
static void fun_rest(char *buff, char **bufc, dbref player, dbref cause,
					 char *fargs[], int nfargs, char *cargs[], int ncargs)
{
	char *s, *first, sep;

	/*
	 * If we are passed an empty arglist return a null string 
	 */

	if(nfargs == 0) {
		return;
	}
	varargs_preamble("REST", 2);
	s = trim_space_sep(fargs[0], sep);	/*
										 * leading spaces ... 
										 */
	first = split_token(&s, sep);
	if(s) {
		safe_str(s, buff, bufc);
	}
}

/**
 * Function form of %-substitution
 */
static void fun_v(char *buff, char **bufc, dbref player, dbref cause,
				  char *fargs[], int nfargs, char *cargs[], int ncargs)
{
	dbref aowner;
	int aflags;
	char *sbuf, *sbufc, *tbuf, *str;
	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) {
			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, aflags)) {
			tbuf = atr_pget(player, ap->number, &aowner, &aflags);
			safe_str(tbuf, buff, bufc);
			free_lbuf(tbuf);
		}
		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';
	str = sbuf;
	exec(buff, bufc, 0, player, cause, EV_FIGNORE, &str, cargs, ncargs);
	free_sbuf(sbuf);
}

/**
 * Force substitution to occur.
 */
static void fun_s(char *buff, char **bufc, dbref player, dbref cause,
				  char *fargs[], int nfargs, char *cargs[], int ncargs)
{
	char *str;

	str = fargs[0];
	exec(buff, bufc, 0, player, cause, EV_FIGNORE | EV_EVAL, &str, cargs,
		 ncargs);
}

/**
 * Returns first item in contents list of object/room
 */
static void fun_con(char *buff, char **bufc, dbref player, dbref cause,
					char *fargs[], int nfargs, char *cargs[], int ncargs)
{
	dbref it;

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

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

/*
 * ---------------------------------------------------------------------------
 * * fun_exit: Returns first exit in exits list of room.
 */
static void fun_exit(char *buff, char **bufc, dbref player, dbref cause,
					 char *fargs[], int nfargs, char *cargs[], int ncargs)
{
	dbref it, exit;
	int key;

	it = match_thing(player, fargs[0]);
	if(Good_obj(it) && Has_exits(it) && Good_obj(Exits(it))) {
		key = 0;
		if(Examinable(player, it))
			key |= VE_LOC_XAM;
		if(Dark(it))
			key |= VE_LOC_DARK;
		DOLIST(exit, Exits(it)) {
			if(exit_visible(exit, player, key)) {
				safe_tprintf_str(buff, bufc, "#%d", exit);
				return;
			}
		}
	}
	safe_str("#-1", buff, bufc);
	return;
}

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

static void fun_next(char *buff, char **bufc, dbref player, dbref cause,
					 char *fargs[], int nfargs, char *cargs[], int ncargs)
{
	dbref it, loc, exit, ex_here;
	int key;

	it = match_thing(player, fargs[0]);
	if(Good_obj(it) && Has_siblings(it)) {
		loc = where_is(it);
		ex_here = Good_obj(loc) ? Examinable(player, loc) : 0;
		if(ex_here || (loc == player) || (loc == where_is(player))) {
			if(!isExit(it)) {
				safe_tprintf_str(buff, bufc, "#%d", Next(it));
				return;
			} else {
				key = 0;
				if(ex_here)
					key |= VE_LOC_XAM;
				if(Dark(loc))
					key |= VE_LOC_DARK;
				DOLIST(exit, it) {
					if((exit != it) && exit_visible(exit, player, key)) {
						safe_tprintf_str(buff, bufc, "#%d", exit);
						return;
					}
				}
			}
		}
	}
	safe_str("#-1", buff, bufc);
	return;
}

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

static void fun_loc(char *buff, char **bufc, dbref player, dbref cause,
					char *fargs[], int nfargs, char *cargs[], int ncargs)
{
	dbref it;

	it = match_thing(player, fargs[0]);
	if(locatable(player, it, cause))
		safe_tprintf_str(buff, bufc, "#%d", Location(it));
	else
		safe_str("#-1", buff, bufc);
	return;
}

/*
 * ---------------------------------------------------------------------------
 * * fun_where: Returns the "true" location of something
 */

static void fun_where(char *buff, char **bufc, dbref player, dbref cause,
					  char *fargs[], int nfargs, char *cargs[], int ncargs)
{
	dbref it;

	it = match_thing(player, fargs[0]);
	if(locatable(player, it, cause))
		safe_tprintf_str(buff, bufc, "#%d", where_is(it));
	else
		safe_str("#-1", buff, bufc);
	return;
}

/*
 * ---------------------------------------------------------------------------
 * * fun_rloc: Returns the recursed location of something (specifying #levels)
 */

static void fun_rloc(char *buff, char **bufc, dbref player, dbref cause,
					 char *fargs[], int nfargs, char *cargs[], int ncargs)
{
	int i, levels;
	dbref it;

	levels = atoi(fargs[1]);
	if(levels > mudconf.ntfy_nest_lim)
		levels = mudconf.ntfy_nest_lim;

	it = match_thing(player, fargs[0]);
	if(locatable(player, it, cause)) {
		for(i = 0; i < levels; i++) {
			if(!Good_obj(it) || !Has_location(it))
				break;
			it = Location(it);
		}
		safe_tprintf_str(buff, bufc, "#%d", it);
		return;
	}
	safe_str("#-1", buff, bufc);
}

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

static void fun_room(char *buff, char **bufc, dbref player, dbref cause,
					 char *fargs[], int nfargs, char *cargs[], int ncargs)
{
	dbref it;
	int count;

	it = match_thing(player, fargs[0]);
	if(locatable(player, it, cause)) {
		for(count = mudconf.ntfy_nest_lim; count > 0; count--) {
			it = Location(it);
			if(!Good_obj(it))
				break;
			if(isRoom(it)) {
				safe_tprintf_str(buff, bufc, "#%d", it);
				return;
			}
		}
		safe_str("#-1", buff, bufc);
	} else if(isRoom(it)) {
		safe_tprintf_str(buff, bufc, "#%d", it);
	} else {
		safe_str("#-1", buff, bufc);
	}
	return;
}

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

static void fun_owner(char *buff, char **bufc, dbref player, dbref cause,
					  char *fargs[], int nfargs, char *cargs[], int ncargs)
{
	dbref it, aowner;
	int atr, aflags;

	if(parse_attrib(player, fargs[0], &it, &atr)) {
		if(atr == NOTHING) {
			it = NOTHING;
		} else {
			atr_pget_info(it, atr, &aowner, &aflags);
			it = aowner;
		}
	} else {
		it = match_thing(player, fargs[0]);
		if(it != NOTHING)
			it = Owner(it);
	}
	safe_tprintf_str(buff, bufc, "#%d", it);
}

/*
 * ---------------------------------------------------------------------------
 * * fun_controls: Does x control y?
 */

static void fun_controls(char *buff, char **bufc, dbref player, dbref cause,
						 char *fargs[], int nfargs, char *cargs[], int ncargs)
{
	dbref x, y;

	x = match_thing(player, fargs[0]);
	if(x == NOTHING) {
		safe_tprintf_str(buff, bufc, "%s", "#-1 ARG1 NOT FOUND");
		return;
	}
	y = match_thing(player, fargs[1]);
	if(y == NOTHING) {
		safe_tprintf_str(buff, bufc, "%s", "#-1 ARG2 NOT FOUND");
		return;
	}
	safe_tprintf_str(buff, bufc, "%d", Controls(x, y));
}

/*
 * ---------------------------------------------------------------------------
 * * fun_fullname: Return the fullname of an object (good for exits)
 */

static void fun_fullname(char *buff, char **bufc, dbref player, dbref cause,
						 char *fargs[], int nfargs, char *cargs[], int ncargs)
{
	dbref it;

	it = match_thing(player, fargs[0]);
	if(it == NOTHING) {
		return;
	}
	if(!mudconf.read_rem_name) {
		if(!nearby_or_control(player, it) && (!isPlayer(it))) {
			safe_str("#-1 TOO FAR AWAY TO SEE", buff, bufc);
			return;
		}
	}
	safe_str(Name(it), buff, bufc);
}

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

static void fun_name(char *buff, char **bufc, dbref player, dbref cause,
					 char *fargs[], int nfargs, char *cargs[], int ncargs)
{
	dbref it;
	char *s, *temp;

	it = match_thing(player, fargs[0]);
	if(it == NOTHING) {
		return;
	}
	if(!mudconf.read_rem_name) {
		if(!nearby_or_control(player, it) && !isPlayer(it) &&
		   !Long_Fingers(player)) {
			safe_str("#-1 TOO FAR AWAY TO SEE", buff, bufc);
			return;
		}
	}
	temp = *bufc;
	safe_str(Name(it), buff, bufc);
	if(isExit(it)) {
		for(s = temp; (s != *bufc) && (*s != ';'); s++);
		if(*s == ';')
			*bufc = s;
	}
}

/*
 * ---------------------------------------------------------------------------
 * * fun_match, fun_strmatch: Match arg2 against each word of arg1 returning
 * * index of first match, or against the whole string.
 */

static void fun_match(char *buff, char **bufc, dbref player, dbref cause,
					  char *fargs[], int nfargs, char *cargs[], int ncargs)
{
	int wcount;
	char *r, *s, sep;

	varargs_preamble("MATCH", 3);

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

	wcount = 1;
	s = trim_space_sep(fargs[0], sep);
	do {
		r = split_token(&s, sep);
		if(quick_wild(fargs[1], r)) {
			safe_tprintf_str(buff, bufc, "%d", wcount);
			return;
		}
		wcount++;
	} while (s);
	safe_str("0", buff, bufc);
}

static void fun_strmatch(char *buff, char **bufc, dbref player, dbref cause,
						 char *fargs[], int nfargs, char *cargs[], int ncargs)
{
	/*
	 * Check if we match the whole string.  If so, return 1 
	 */

	if(quick_wild(fargs[1], fargs[0]))
		safe_str("1", buff, bufc);
	else
		safe_str("0", buff, bufc);
	return;
}

/*
 * ---------------------------------------------------------------------------
 * * 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'
 * * 
 * * Now takes optional separator extract(foo-bar-baz,1,2,-) returns 'foo-bar'
 */

static void fun_extract(char *buff, char **bufc, dbref player, dbref cause,
						char *fargs[], int nfargs, char *cargs[], int ncargs)
{
	int start, len;
	char *r, *s, *t, sep;

	varargs_preamble("EXTRACT", 4);

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

	if((start < 1) || (len < 1)) {
		return;
	}
	/*
	 * Skip to the start of the string to save 
	 */

	start--;
	s = trim_space_sep(s, sep);
	while (start && s) {
		s = next_token(s, sep);
		start--;
	}

	/*
	 * If we ran of the end of the string, return nothing 
	 */

	if(!s || !*s) {
		return;
	}
	/*
	 * Count off the words in the string to save 
	 */

	r = s;
	len--;
	while (len && s) {
		s = next_token(s, sep);
		len--;
	}

	/*
	 * Chop off the rest of the string, if needed 
	 */

	if(s && *s)
		t = split_token(&s, sep);
	safe_str(r, buff, bufc);
}

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

	if(arg[0] == '#') {
		arg++;
		/* #- anything is false */
		if(arg[1] == '-') {
			return 0;
		}
		if(is_integer(arg)) {
			temp = atoi(arg);
			if(temp == -1)
				temp = 0;
			return temp;
		}
		return 0;
	}
	temp2 = trim_space_sep(arg, ' ');
	if(!*temp2)
		return 0;
	if(is_integer(temp2))
		return atoi(temp2);
	return 1;
}

/*
 * ---------------------------------------------------------------------------
 * * fun_index:  like extract(), but it works with an arbitrary separator.
 * * index(a b | c d e | f gh | ij k, |, 2, 1) => c d e
 * * index(a b | c d e | f gh | ij k, |, 2, 2) => c d e | f g h
 */

static void fun_index(char *buff, char **bufc, dbref player, dbref cause,
					  char *fargs[], int nfargs, char *cargs[], int ncargs)
{
	int start, end;
	char c, *s, *p;

	s = fargs[0];
	c = *fargs[1];
	start = atoi(fargs[2]);
	end = atoi(fargs[3]);

	if((start < 1) || (end < 1) || (*s == '\0'))
		return;
	if(c == '\0')
		c = ' ';

	/*
	 * move s to point to the start of the item we want 
	 */

	start--;
	while (start && s && *s) {
		if((s = (char *) index(s, c)) != NULL)
			s++;
		start--;
	}

	/*
	 * skip over just spaces 
	 */

	while (s && (*s == ' '))
		s++;
	if(!s || !*s)
		return;

	/*
	 * figure out where to end the string 
	 */

	p = s;
	while (end && p && *p) {
		if((p = (char *) index(p, c)) != NULL) {
			if(--end == 0) {
				do {
					p--;
				} while ((*p == ' ') && (p > s));
				*(++p) = '\0';
				safe_str(s, buff, bufc);
				return;
			} else {
				p++;
			}
		}
	}

	/*
	 * if we've gotten this far, we've run off the end of the string 
	 */

	safe_str(s, buff, bufc);
}

static void fun_cat(char *buff, char **bufc, dbref player, dbref cause,
					char *fargs[], int nfargs, char *cargs[], int ncargs)
{
	int i;

	safe_str(fargs[0], buff, bufc);
	for(i = 1; i < nfargs; i++) {
		safe_chr(' ', buff, bufc);
		safe_str(fargs[i], buff, bufc);
	}
}

static void fun_version(char *buff, char **bufc, dbref player, dbref cause,
						char *fargs[], int nfargs, char *cargs[], int ncargs)
{
	safe_str(mudstate.version, buff, bufc);
}

static void fun_strlen(char *buff, char **bufc, dbref player, dbref cause,
					   char *fargs[], int nfargs, char *cargs[], int ncargs)
{
	safe_tprintf_str(buff, bufc, "%d",
					 (int) strlen((char *) strip_ansi(fargs[0])));
}

static void fun_num(char *buff, char **bufc, dbref player, dbref cause,
					char *fargs[], int nfargs, char *cargs[], int ncargs)
{
	safe_tprintf_str(buff, bufc, "#%d", match_thing(player, fargs[0]));
}

static void fun_pmatch(char *buff, char **bufc, dbref player, dbref cause,
					   char *fargs[], int nfargs, char *cargs[], int ncargs)
{
	dbref thing;

	if(*fargs[0] == '#') {
		safe_tprintf_str(buff, bufc, "#%d", match_thing(player, fargs[0]));
		return;
	}
	if(!((thing = lookup_player(player, fargs[0], 1)) == NOTHING)) {
		safe_tprintf_str(buff, bufc, "#%d", thing);
		return;
	} else
		safe_str("#-1 NO MATCH", buff, bufc);
}

static void fun_gt(char *buff, char **bufc, dbref player, dbref cause,
				   char *fargs[], int nfargs, char *cargs[], int ncargs)
{
	safe_tprintf_str(buff, bufc, "%d", (atof(fargs[0]) > atof(fargs[1])));
}

static void fun_gte(char *buff, char **bufc, dbref player, dbref cause,
					char *fargs[], int nfargs, char *cargs[], int ncargs)
{
	safe_tprintf_str(buff, bufc, "%d", (atof(fargs[0]) >= atof(fargs[1])));
}

static void fun_lt(char *buff, char **bufc, dbref player, dbref cause,
				   char *fargs[], int nfargs, char *cargs[], int ncargs)
{
	safe_tprintf_str(buff, bufc, "%d", (atof(fargs[0]) < atof(fargs[1])));
}

static void fun_lte(char *buff, char **bufc, dbref player, dbref cause,
					char *fargs[], int nfargs, char *cargs[], int ncargs)
{
	safe_tprintf_str(buff, bufc, "%d", (atof(fargs[0]) <= atof(fargs[1])));
}

static void fun_eq(char *buff, char **bufc, dbref player, dbref cause,
				   char *fargs[], int nfargs, char *cargs[], int ncargs)
{
	safe_tprintf_str(buff, bufc, "%d", (atof(fargs[0]) == atof(fargs[1])));
}

static void fun_neq(char *buff, char **bufc, dbref player, dbref cause,
					char *fargs[], int nfargs, char *cargs[], int ncargs)
{
	safe_tprintf_str(buff, bufc, "%d", (atof(fargs[0]) != atof(fargs[1])));
}

static void fun_and(char *buff, char **bufc, dbref player, dbref cause,
					char *fargs[], int nfargs, char *cargs[], int ncargs)
{
	int i, val;

	if(nfargs < 2) {
		safe_str("#-1 TOO FEW ARGUMENTS", buff, bufc);
		return;
	}

	val = atoi(fargs[0]);
	for(i = 1; val && i < nfargs; i++)
		val = val && atoi(fargs[i]);

	safe_tprintf_str(buff, bufc, "%d", val);
}

static void fun_or(char *buff, char **bufc, dbref player, dbref cause,
				   char *fargs[], int nfargs, char *cargs[], int ncargs)
{
	int i, val;

	if(nfargs < 2) {
		safe_str("#-1 TOO FEW ARGUMENTS", buff, bufc);
		return;
	}

	val = atoi(fargs[0]);
	for(i = 1; !val && i < nfargs; i++)
		val = val || atoi(fargs[i]);

	safe_tprintf_str(buff, bufc, "%d", val);
}

static void fun_xor(char *buff, char **bufc, dbref player, dbref cause,
					char *fargs[], int nfargs, char *cargs[], int ncargs)
{
	int i, val;

	if(nfargs < 2) {
		safe_str("#-1 TOO FEW ARGUMENTS", buff, bufc);
		return;
	}

	val = atoi(fargs[0]);
	for(i = 1; i < nfargs; i++) {
		int tval = atoi(fargs[i]);
		val = (val && !tval) || (!val && tval);
	}
	safe_tprintf_str(buff, bufc, "%d", val);
}

static void fun_not(char *buff, char **bufc, dbref player, dbref cause,
					char *fargs[], int nfargs, char *cargs[], int ncargs)
{
	safe_tprintf_str(buff, bufc, "%d", !xlate(fargs[0]));
}

static void fun_t(char *buff, char **bufc, dbref player, dbref cause,
				  char *fargs[], int nfargs, char *cargs[], int ncargs)
{
	safe_tprintf_str(buff, bufc, "%d", !(!xlate(fargs[0])));
}

static void fun_sqrt(char *buff, char **bufc, dbref player, dbref cause,
					 char *fargs[], int nfargs, char *cargs[], int ncargs)
{
	double val;

	val = atof(fargs[0]);
	if(val < 0) {
		safe_str("#-1 SQUARE ROOT OF NEGATIVE", buff, bufc);
	} else if(val == 0) {
		safe_str("0", buff, bufc);
	} else {
		fval(buff, bufc, sqrt(val));
	}
}

static void fun_add(char *buff, char **bufc, dbref player, dbref cause,
					char *fargs[], int nfargs, char *cargs[], int ncargs)
{
	double sum = 0;
	int i;

	if(!nfargs) {
		safe_str("#-1 TOO FEW ARGUMENTS", buff, bufc);
		return;
	}
	for(i = 0; i < nfargs; i++)
		sum += atof(fargs[i]);

	fval(buff, bufc, sum);
}

static void fun_sub(char *buff, char **bufc, dbref player, dbref cause,
					char *fargs[], int nfargs, char *cargs[], int ncargs)
{
	fval(buff, bufc, atof(fargs[0]) - atof(fargs[1]));
}

static void fun_mul(char *buff, char **bufc, dbref player, dbref cause,
					char *fargs[], int nfargs, char *cargs[], int ncargs)
{
	int i;
	double prod;

	if(!nfargs) {
		safe_str("#-1 TOO FEW ARGUMENTS", buff, bufc);
		return;
	}

	prod = atof(fargs[0]);
	for(i = 1; i < nfargs; i++)
		prod *= atof(fargs[i]);

	fval(buff, bufc, prod);
}

static void fun_floor(char *buff, char **bufc, dbref player, dbref cause,
					  char *fargs[], int nfargs, char *cargs[], int ncargs)
{
	safe_tprintf_str(buff, bufc, "%.0f", floor(atof(fargs[0])));
}

static void fun_ceil(char *buff, char **bufc, dbref player, dbref cause,
					 char *fargs[], int nfargs, char *cargs[], int ncargs)
{
	safe_tprintf_str(buff, bufc, "%.0f", ceil(atof(fargs[0])));
}

static void fun_round(char *buff, char **bufc, dbref player, dbref cause,
					  char *fargs[], int nfargs, char *cargs[], int ncargs)
{
	const char *fstr;
	char *oldp;

	oldp = *bufc;

	switch (atoi(fargs[1])) {
	case 1:
		fstr = "%.1f";
		break;
	case 2:
		fstr = "%.2f";
		break;
	case 3:
		fstr = "%.3f";
		break;
	case 4:
		fstr = "%.4f";
		break;
	case 5:
		fstr = "%.5f";
		break;
	case 6:
		fstr = "%.6f";
		break;
	default:
		fstr = "%.0f";
		break;
	}
	safe_tprintf_str(buff, bufc, (char *) fstr, atof(fargs[0]));

	/* Handle bogus result of "-0" from sprintf.  Yay, cclib. */

	if(!strcmp(oldp, "-0")) {
		*oldp = '0';
		*bufc = oldp + 1;
	}
}

static void fun_trunc(char *buff, char **bufc, dbref player, dbref cause,
					  char *fargs[], int nfargs, char *cargs[], int ncargs)
{
	safe_tprintf_str(buff, bufc, "%.0f", atof(fargs[0]));
}

static void fun_div(char *buff, char **bufc, dbref player, dbref cause,
					char *fargs[], int nfargs, char *cargs[], int ncargs)
{
	int bot;

	bot = atoi(fargs[1]);
	if(bot == 0) {
		safe_str("#-1 DIVIDE BY ZERO", buff, bufc);
	} else {
		safe_tprintf_str(buff, bufc, "%d", (atoi(fargs[0]) / bot));
	}
}

static void fun_fdiv(char *buff, char **bufc, dbref player, dbref cause,
					 char *fargs[], int nfargs, char *cargs[], int ncargs)
{
	double bot;

	bot = atof(fargs[1]);
	if(bot == 0) {
		safe_str("#-1 DIVIDE BY ZERO", buff, bufc);
	} else {
		fval(buff, bufc, (atof(fargs[0]) / bot));
	}
}

static void fun_mod(char *buff, char **bufc, dbref player, dbref cause,
					char *fargs[], int nfargs, char *cargs[], int ncargs)
{
	int bot;

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

static void fun_pi(char *buff, char **bufc, dbref player, dbref cause,
				   char *fargs[], int nfargs, char *cargs[], int ncargs)
{
	safe_str("3.141592654", buff, bufc);
}

static void fun_e(char *buff, char **bufc, dbref player, dbref cause,
				  char *fargs[], int nfargs, char *cargs[], int ncargs)
{
	safe_str("2.718281828", buff, bufc);
}

static void fun_sin(char *buff, char **bufc, dbref player, dbref cause,
					char *fargs[], int nfargs, char *cargs[], int ncargs)
{
	fval(buff, bufc, sin(atof(fargs[0])));
}

static void fun_cos(char *buff, char **bufc, dbref player, dbref cause,
					char *fargs[], int nfargs, char *cargs[], int ncargs)
{
	fval(buff, bufc, cos(atof(fargs[0])));
}

static void fun_tan(char *buff, char **bufc, dbref player, dbref cause,
					char *fargs[], int nfargs, char *cargs[], int ncargs)
{
	fval(buff, bufc, tan(atof(fargs[0])));
}

static void fun_exp(char *buff, char **bufc, dbref player, dbref cause,
					char *fargs[], int nfargs, char *cargs[], int ncargs)
{
	fval(buff, bufc, exp(atof(fargs[0])));
}

static void fun_power(char *buff, char **bufc, dbref player, dbref cause,
					  char *fargs[], int nfargs, char *cargs[], int ncargs)
{
	double val1, val2;

	val1 = atof(fargs[0]);
	val2 = atof(fargs[1]);
	if(val1 < 0) {
		safe_str("#-1 POWER OF NEGATIVE", buff, bufc);
	} else {
		fval(buff, bufc, pow(val1, val2));
	}
}

static void fun_ln(char *buff, char **bufc, dbref player, dbref cause,
				   char *fargs[], int nfargs, char *cargs[], int ncargs)
{
	double val;

	val = atof(fargs[0]);
	if(val > 0)
		fval(buff, bufc, log(val));
	else
		safe_str("#-1 LN OF NEGATIVE OR ZERO", buff, bufc);
}

static void fun_log(char *buff, char **bufc, dbref player, dbref cause,
					char *fargs[], int nfargs, char *cargs[], int ncargs)
{
	double val;

	val = atof(fargs[0]);
	if(val > 0) {
		fval(buff, bufc, log10(val));
	} else {
		safe_str("#-1 LOG OF NEGATIVE OR ZERO", buff, bufc);
	}
}

static void fun_asin(char *buff, char **bufc, dbref player, dbref cause,
					 char *fargs[], int nfargs, char *cargs[], int ncargs)
{
	double val;

	val = atof(fargs[0]);
	if((val < -1) || (val > 1)) {
		safe_str("#-1 ASIN ARGUMENT OUT OF RANGE", buff, bufc);
	} else {
		fval(buff, bufc, asin(val));
	}
}

static void fun_acos(char *buff, char **bufc, dbref player, dbref cause,
					 char *fargs[], int nfargs, char *cargs[], int ncargs)
{
	double val;

	val = atof(fargs[0]);
	if((val < -1) || (val > 1)) {
		safe_str("#-1 ACOS ARGUMENT OUT OF RANGE", buff, bufc);
	} else {
		fval(buff, bufc, acos(val));
	}
}

static void fun_atan(char *buff, char **bufc, dbref player, dbref cause,
					 char *fargs[], int nfargs, char *cargs[], int ncargs)
{
	fval(buff, bufc, atan(atof(fargs[0])));
}

static void fun_dist2d(char *buff, char **bufc, dbref player, dbref cause,
					   char *fargs[], int nfargs, char *cargs[], int ncargs)
{
	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);
	safe_tprintf_str(buff, bufc, "%d", d);
}

static void fun_dist3d(char *buff, char **bufc, dbref player, dbref cause,
					   char *fargs[], int nfargs, char *cargs[], int ncargs)
{
	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);
	safe_tprintf_str(buff, bufc, "%d", d);
}

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

static void fun_comp(char *buff, char **bufc, dbref player, dbref cause,
					 char *fargs[], int nfargs, char *cargs[], int ncargs)
{
	int x;

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

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

static void fun_lcon(char *buff, char **bufc, dbref player, dbref cause,
					 char *fargs[], int nfargs, char *cargs[], int ncargs)
{
	dbref thing, it;
	char *tbuf;
	int first = 1;

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

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

static void fun_lexits(char *buff, char **bufc, dbref player, dbref cause,
					   char *fargs[], int nfargs, char *cargs[], int ncargs)
{
	dbref thing, it, parent;
	char *tbuf;
	int exam, lev, key;
	int first = 1;

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

	if(!Good_obj(it) || !Has_exits(it)) {
		safe_str("#-1", buff, bufc);
		return;
	}
	exam = Examinable(player, it);
	if(!exam && (where_is(player) != it) && (it != cause)) {
		safe_str("#-1", buff, bufc);
		return;
	}
	tbuf = alloc_sbuf("fun_lexits");

	/*
	 * Return info for all parent levels 
	 */

	ITER_PARENTS(it, parent, lev) {

		/*
		 * Look for exits at each level 
		 */

		if(!Has_exits(parent))
			continue;
		key = 0;
		if(Examinable(player, parent))
			key |= VE_LOC_XAM;
		if(Dark(parent))
			key |= VE_LOC_DARK;
		if(Dark(it))
			key |= VE_BASE_DARK;
		DOLIST(thing, Exits(parent)) {
			if(exit_visible(thing, player, key)) {
				if(!first)
					sprintf(tbuf, " #%d", thing);
				else {
					sprintf(tbuf, "#%d", thing);
					first = 0;
				}
				safe_str(tbuf, buff, bufc);
			}
		}
	}
	free_sbuf(tbuf);
	return;
}

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

static void fun_home(char *buff, char **bufc, dbref player, dbref cause,
					 char *fargs[], int nfargs, char *cargs[], int ncargs)
{
	dbref it;

	it = match_thing(player, fargs[0]);
	if(!Good_obj(it) || !Examinable(player, it))
		safe_str("#-1", buff, bufc);
	else if(Has_home(it))
		safe_tprintf_str(buff, bufc, "#%d", Home(it));
	else if(Has_dropto(it))
		safe_tprintf_str(buff, bufc, "#%d", Dropto(it));
	else if(isExit(it))
		safe_tprintf_str(buff, bufc, "#%d", where_is(it));
	else
		safe_str("#-1", buff, bufc);
	return;
}

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

static void fun_money(char *buff, char **bufc, dbref player, dbref cause,
					  char *fargs[], int nfargs, char *cargs[], int ncargs)
{
	dbref it;

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

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

static void fun_pos(char *buff, char **bufc, dbref player, dbref cause,
					char *fargs[], int nfargs, char *cargs[], int ncargs)
{
	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') {
			safe_tprintf_str(buff, bufc, "%d", i);
			return;
		}
		++i, ++s;
	}
	safe_str("#-1", buff, bufc);
	return;
}

/*
 * ---------------------------------------------------------------------------
 * * ldelete: Remove a word from a string by place
 * *  ldelete(<list>,<position>[,<separator>])
 * *
 * * insert: insert a word into a string by place
 * *  insert(<list>,<position>,<new item> [,<separator>])
 * *
 * * replace: replace a word into a string by place
 * *  replace(<list>,<position>,<new item>[,<separator>])
 */

#define	IF_DELETE	0
#define	IF_REPLACE	1
#define	IF_INSERT	2

static void do_itemfuns(char *buff, char **bufc, char *str, int el,
						char *word, char sep, int flag)
{
	int ct, overrun;
	char *sptr, *iptr, *eptr;
	char nullb;

	/*
	 * If passed a null string return an empty string, except that we * * 
	 * 
	 * *  * * are allowed to append to a null string. 
	 */

	if((!str || !*str) && ((flag != IF_INSERT) || (el != 1))) {
		return;
	}
	/*
	 * we can't fiddle with anything before the first position 
	 */

	if(el < 1) {
		safe_str(str, buff, bufc);
		return;
	}
	/*
	 * Split the list up into 'before', 'target', and 'after' chunks * *
	 * * * pointed to by sptr, iptr, and eptr respectively. 
	 */

	nullb = '\0';
	if(el == 1) {
		/*
		 * No 'before' portion, just split off element 1 
		 */

		sptr = NULL;
		if(!str || !*str) {
			eptr = NULL;
			iptr = NULL;
		} else {
			eptr = trim_space_sep(str, sep);
			iptr = split_token(&eptr, sep);
		}
	} else {
		/*
		 * Break off 'before' portion 
		 */

		sptr = eptr = trim_space_sep(str, sep);
		overrun = 1;
		for(ct = el; ct > 2 && eptr; eptr = next_token(eptr, sep), ct--);
		if(eptr) {
			overrun = 0;
			iptr = split_token(&eptr, sep);
		}
		/*
		 * If we didn't make it to the target element, just return *
		 * * * * the string.  Insert is allowed to continue if we are 
		 * *  * *  * exactly at the end of the string, but replace
		 * and * delete *  *  * * are not. 
		 */

		if(!(eptr || ((flag == IF_INSERT) && !overrun))) {
			safe_str(str, buff, bufc);
			return;
		}
		/*
		 * Split the 'target' word from the 'after' portion. 
		 */

		if(eptr)
			iptr = split_token(&eptr, sep);
		else
			iptr = NULL;
	}

	switch (flag) {
	case IF_DELETE:			/*
								 * deletion 
								 */
		if(sptr) {
			safe_str(sptr, buff, bufc);
			if(eptr)
				safe_chr(sep, buff, bufc);
		}
		if(eptr) {
			safe_str(eptr, buff, bufc);
		}
		break;
	case IF_REPLACE:			/*
								 * replacing 
								 */
		if(sptr) {
			safe_str(sptr, buff, bufc);
			safe_chr(sep, buff, bufc);
		}
		safe_str(word, buff, bufc);
		if(eptr) {
			safe_chr(sep, buff, bufc);
			safe_str(eptr, buff, bufc);
		}
		break;
	case IF_INSERT:			/*
								 * insertion 
								 */
		if(sptr) {
			safe_str(sptr, buff, bufc);
			safe_chr(sep, buff, bufc);
		}
		safe_str(word, buff, bufc);
		if(iptr) {
			safe_chr(sep, buff, bufc);
			safe_str(iptr, buff, bufc);
		}
		if(eptr) {
			safe_chr(sep, buff, bufc);
			safe_str(eptr, buff, bufc);
		}
		break;
	}
}

static void fun_ldelete(char *buff, char **bufc, dbref player, dbref cause,
						char *fargs[], int nfargs, char *cargs[], int ncargs)
{								/*
								 * delete a word at position X of a list 
								 */
	char sep;

	varargs_preamble("LDELETE", 3);
	do_itemfuns(buff, bufc, fargs[0], atoi(fargs[1]), NULL, sep, IF_DELETE);
}

static void fun_replace(char *buff, char **bufc, dbref player, dbref cause,
						char *fargs[], int nfargs, char *cargs[], int ncargs)
{								/*
								 * replace a word at position X of a list 
								 */
	char sep;

	varargs_preamble("REPLACE", 4);
	do_itemfuns(buff, bufc, fargs[0], atoi(fargs[1]), fargs[2], sep,
				IF_REPLACE);
}

static void fun_insert(char *buff, char **bufc, dbref player, dbref cause,
					   char *fargs[], int nfargs, char *cargs[], int ncargs)
{								/*
								 * insert a word at position X of a list 
								 */
	char sep;

	varargs_preamble("INSERT", 4);
	do_itemfuns(buff, bufc, fargs[0], atoi(fargs[1]), fargs[2], sep,
				IF_INSERT);
}

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

static void fun_remove(char *buff, char **bufc, dbref player, dbref cause,
					   char *fargs[], int nfargs, char *cargs[], int ncargs)
{
	char *s, *sp, *word;
	char sep;
	int first, found;

	varargs_preamble("REMOVE", 3);
	if(index(fargs[1], sep)) {
		safe_str("#-1 CAN ONLY DELETE ONE ELEMENT", buff, bufc);
		return;
	}
	s = fargs[0];
	word = fargs[1];

	/*
	 * Walk through the string copying words until (if ever) we get to *
	 * * * * one that matches the target word. 
	 */

	sp = s;
	found = 0;
	first = 1;
	while (s) {
		sp = split_token(&s, sep);
		if(found || strcmp(sp, word)) {
			if(!first)
				safe_chr(sep, buff, bufc);
			safe_str(sp, buff, bufc);
			first = 0;
		} else {
			found = 1;
		}
	}
}

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

static void fun_member(char *buff, char **bufc, dbref player, dbref cause,
					   char *fargs[], int nfargs, char *cargs[], int ncargs)
{
	int wcount;
	char *r, *s, sep;

	varargs_preamble("MEMBER", 3);
	wcount = 1;
	s = trim_space_sep(fargs[0], sep);
	do {
		r = split_token(&s, sep);
		if(!strcmp(fargs[1], r)) {
			safe_tprintf_str(buff, bufc, "%d", wcount);
			return;
		}
		wcount++;
	} while (s);
	safe_str("0", buff, bufc);
}

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

static void fun_secure(char *buff, char **bufc, dbref player, dbref cause,
					   char *fargs[], int nfargs, char *cargs[], int ncargs)
{
	char *s;

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

static void fun_ansi_secure(char *buff, char **bufc, dbref player,
							dbref cause, char *fargs[], int nfargs,
							char *cargs[], int ncargs)
{
	char *s;

	s = fargs[0];
	while (*s) {
		switch (*s) {
		case '\033':
			safe_chr(*s++, buff, bufc);
			if(*s == '[') {
				safe_chr(*s, buff, bufc);
				break;
			}					/* FALLTHRU */
		case '%':
		case '[':
		case '$':
		case '\\':
		case ']':
		case '(':
		case ')':
		case '{':
		case '}':
		case ',':
		case ';':
			safe_chr(' ', buff, bufc);
			break;
		default:
			safe_chr(*s, buff, bufc);
		}
		s++;
	}
}

static void fun_escape(char *buff, char **bufc, dbref player, dbref cause,
					   char *fargs[], int nfargs, char *cargs[], int ncargs)
{
	char *s, *d;

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

/*
 * Take a character position and return which word that char is in. 
 * wordpos(<string>, <charpos>)
 */
static void fun_wordpos(char *buff, char **bufc, dbref player, dbref cause,
						char *fargs[], int nfargs, char *cargs[], int ncargs)
{
	int charpos, i;
	char *cp, *tp, *xp, sep;

	varargs_preamble("WORDPOS", 3);

	charpos = atoi(fargs[1]);
	cp = fargs[0];
	if((charpos > 0) && (charpos <= strlen(cp))) {
		tp = &(cp[charpos - 1]);
		cp = trim_space_sep(cp, sep);
		xp = split_token(&cp, sep);
		for(i = 1; xp; i++) {
			if(tp < (xp + strlen(xp)))
				break;
			xp = split_token(&cp, sep);
		}
		safe_tprintf_str(buff, bufc, "%d", i);
		return;
	}
	safe_str("#-1", buff, bufc);
	return;
}

static void fun_type(char *buff, char **bufc, dbref player, dbref cause,
					 char *fargs[], int nfargs, char *cargs[], int ncargs)
{
	dbref it;

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

static void fun_hasflag(char *buff, char **bufc, dbref player, dbref cause,
						char *fargs[], int nfargs, char *cargs[], int ncargs)
{
	dbref it;

	it = match_thing(player, fargs[0]);
	if(!Good_obj(it)) {
		safe_str("#-1 NOT FOUND", buff, bufc);
		return;
	}
	if(mudconf.pub_flags || Examinable(player, it) || (it == cause)) {
		if(has_flag(player, it, fargs[1]))
			safe_str("1", buff, bufc);
		else
			safe_str("0", buff, bufc);
	} else {
		safe_str("#-1 PERMISSION DENIED", buff, bufc);
	}
}

static void fun_haspower(char *buff, char **bufc, dbref player, dbref cause,
						 char *fargs[], int nfargs, char *cargs[], int ncargs)
{
	dbref it;

	it = match_thing(player, fargs[0]);
	if(!Good_obj(it)) {
		safe_str("#-1 NOT FOUND", buff, bufc);
		return;
	}
	if(mudconf.pub_flags || Examinable(player, it) || (it == cause)) {
		if(has_power(player, it, fargs[1]))
			safe_str("1", buff, bufc);
		else
			safe_str("0", buff, bufc);
	} else {
		safe_str("#-1 PERMISSION DENIED", buff, bufc);
	}
}

static void fun_delete(char *buff, char **bufc, dbref player, dbref cause,
					   char *fargs[], int nfargs, char *cargs[], int ncargs)
{
	char *s, *temp, *bp;
	int i, start, nchars, len;

	s = fargs[0];
	start = atoi(fargs[1]);
	nchars = atoi(fargs[2]);
	len = strlen(s);
	if((start >= len) || (nchars <= 0)) {
		safe_str(s, buff, bufc);
		return;
	}
	bp = temp = alloc_lbuf("fun_delete");
	for(i = 0; i < start; i++)
		*bp++ = (*s++);
	if((i + nchars) < len && (i + nchars) > 0) {
		s += nchars;
		while ((*bp++ = *s++));
	} else
		*bp = '\0';

	safe_str(temp, buff, bufc);
	free_lbuf(temp);
}

static void fun_lock(char *buff, char **bufc, dbref player, dbref cause,
					 char *fargs[], int nfargs, char *cargs[], int ncargs)
{
	dbref it, aowner;
	int aflags;
	char *tbuf;
	ATTR *attr;
	struct boolexp *bool;

	/*
	 * Parse the argument into obj + lock 
	 */

	if(!get_obj_and_lock(player, fargs[0], &it, &attr, buff, bufc))
		return;

	/*
	 * Get the attribute and decode it if we can read it 
	 */

	tbuf = atr_get(it, attr->number, &aowner, &aflags);
	if(Read_attr(player, it, attr, aowner, aflags)) {
		bool = parse_boolexp(player, tbuf, 1);
		free_lbuf(tbuf);
		tbuf = (char *) unparse_boolexp_function(player, bool);
		free_boolexp(bool);
		safe_str(tbuf, buff, bufc);
	} else
		free_lbuf(tbuf);
}

static void fun_elock(char *buff, char **bufc, dbref player, dbref cause,
					  char *fargs[], int nfargs, char *cargs[], int ncargs)
{
	dbref it, victim, aowner;
	int aflags;
	char *tbuf;
	ATTR *attr;
	struct boolexp *bool;

	/*
	 * Parse lock supplier into obj + lock 
	 */

	if(!get_obj_and_lock(player, fargs[0], &it, &attr, buff, bufc))
		return;

	/*
	 * Get the victim and ensure we can do it 
	 */

	victim = match_thing(player, fargs[1]);
	if(!Good_obj(victim)) {
		safe_str("#-1 NOT FOUND", buff, bufc);
	} else if(!nearby_or_control(player, victim) &&
			  !nearby_or_control(player, it)) {
		safe_str("#-1 TOO FAR AWAY", buff, bufc);
	} else {
		tbuf = atr_get(it, attr->number, &aowner, &aflags);
		if((attr->number == A_LOCK) ||
		   Read_attr(player, it, attr, aowner, aflags)) {
			bool = parse_boolexp(player, tbuf, 1);
			safe_tprintf_str(buff, bufc, "%d", eval_boolexp(victim, it, it,
															bool));
			free_boolexp(bool);
		} else {
			safe_str("0", buff, bufc);
		}
		free_lbuf(tbuf);
	}
}

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

static void fun_lwho(char *buff, char **bufc, dbref player, dbref cause,
					 char *fargs[], int nfargs, char *cargs[], int ncargs)
{
	make_ulist(player, buff, bufc);
}

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

static void fun_nearby(char *buff, char **bufc, dbref player, dbref cause,
					   char *fargs[], int nfargs, char *cargs[], int ncargs)
{
	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)))
		safe_str("0", buff, bufc);
	else if(nearby(obj1, obj2))
		safe_str("1", buff, bufc);
	else
		safe_str("0", buff, bufc);
}

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

static void process_sex(dbref player, char *what, const char *token,
						char *buff, char **bufc)
{
	dbref it;
	char *str;

	it = match_thing(player, what);
	if(!Good_obj(it) || (!isPlayer(it) && !nearby_or_control(player, it))) {
		safe_str("#-1 NO MATCH", buff, bufc);
	} else {
		str = (char *) token;
		exec(buff, bufc, 0, it, it, 0, &str, (char **) NULL, 0);
	}
}

static void fun_obj(char *buff, char **bufc, dbref player, dbref cause,
					char *fargs[], int nfargs, char *cargs[], int ncargs)
{
	process_sex(player, fargs[0], "%o", buff, bufc);
}

static void fun_poss(char *buff, char **bufc, dbref player, dbref cause,
					 char *fargs[], int nfargs, char *cargs[], int ncargs)
{
	process_sex(player, fargs[0], "%p", buff, bufc);
}

static void fun_subj(char *buff, char **bufc, dbref player, dbref cause,
					 char *fargs[], int nfargs, char *cargs[], int ncargs)
{
	process_sex(player, fargs[0], "%s", buff, bufc);
}

static void fun_aposs(char *buff, char **bufc, dbref player, dbref cause,
					  char *fargs[], int nfargs, char *cargs[], int ncargs)
{
	process_sex(player, fargs[0], "%a", buff, bufc);
}

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

static void fun_mudname(char *buff, char **bufc, dbref player, dbref cause,
						char *fargs[], int nfargs, char *cargs[], int ncargs)
{
	safe_str(mudconf.mud_name, buff, bufc);
}

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

static void fun_lcstr(char *buff, char **bufc, dbref player, dbref cause,
					  char *fargs[], int nfargs, char *cargs[], int ncargs)
{
	char *ap;

	ap = fargs[0];
	while (*ap) {
		**bufc = ToLower(*ap);
		ap++;
		(*bufc)++;
	}
}

static void fun_ucstr(char *buff, char **bufc, dbref player, dbref cause,
					  char *fargs[], int nfargs, char *cargs[], int ncargs)
{
	char *ap;

	ap = fargs[0];
	while (*ap) {
		**bufc = ToUpper(*ap);
		ap++;
		(*bufc)++;
	}
}

static void fun_capstr(char *buff, char **bufc, dbref player, dbref cause,
					   char *fargs[], int nfargs, char *cargs[], int ncargs)
{
	char *s;

	s = *bufc;

	safe_str(fargs[0], buff, bufc);
	*s = ToUpper(*s);
}

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

static void fun_lnum(char *buff, char **bufc, dbref player, dbref cause,
					 char *fargs[], int nfargs, char *cargs[], int ncargs)
{
	char tbuff[10];
	int ctr, limit, llimit = 0, over;

	if(nfargs > 2 || nfargs < 1) {
		safe_str("#-1 TOO FEW ARGUMENTS", buff, bufc);
		return;
	}
	over = 0;
	limit = atoi(fargs[0]);
	if(nfargs == 2) {
		llimit = atoi(fargs[0]);
		limit = atoi(fargs[1]) + 1;
	} else
		limit = atoi(fargs[0]);
	if(limit > 0 && llimit >= 0 && llimit < limit) {
		for(ctr = llimit; ctr < limit && !over; ctr++) {
			{
				sprintf(tbuff, "%s%d", ctr != llimit ? " " : "", ctr);
				over = safe_str(tbuff, buff, bufc);
			}
		}
	}
}

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

static void fun_lattr(char *buff, char **bufc, dbref player, dbref cause,
					  char *fargs[], int nfargs, char *cargs[], int ncargs)
{
	dbref thing;
	int ca, first;
	ATTR *attr;

	/*
	 * Check for wildcard matching.  parse_attrib_wild checks for read
	 * permission, so we don't have to.  Have p_a_w assume the slash-star if
	 * it is missing.
	 */

	first = 1;
	olist_push();
	if(parse_attrib_wild(player, fargs[0], &thing, 0, 0, 1)) {
		for(ca = olist_first(); ca != NOTHING; ca = olist_next()) {
			attr = atr_num(ca);
			if(attr) {
				if(!first)
					safe_chr(' ', buff, bufc);
				first = 0;
				safe_str((char *) attr->name, buff, bufc);
			}
		}
	} else {
		safe_str("#-1 NO MATCH", buff, bufc);
	}
	olist_pop();
	return;
}

/*
 * ---------------------------------------------------------------------------
 * * 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++;
	}
}

static void fun_reverse(char *buff, char **bufc, dbref player, dbref cause,
						char *fargs[], int nfargs, char *cargs[], int ncargs)
{
	do_reverse(fargs[0], *bufc);
	*bufc += strlen(fargs[0]);
}

static void fun_revwords(char *buff, char **bufc, dbref player, dbref cause,
						 char *fargs[], int nfargs, char *cargs[], int ncargs)
{
	char *temp, *tp, *t1, sep;
	int first;

	/*
	 * If we are passed an empty arglist return a null string 
	 */

	if(nfargs == 0) {
		return;
	}
	varargs_preamble("REVWORDS", 2);
	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;
	first = 1;
	while (tp) {
		if(!first)
			safe_chr(sep, buff, bufc);
		t1 = split_token(&tp, sep);
		do_reverse(t1, *bufc);
		*bufc += strlen(t1);
		first = 0;
	}
	free_lbuf(temp);
}

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

static void fun_after(char *buff, char **bufc, dbref player, dbref cause,
					  char *fargs[], int nfargs, char *cargs[], int ncargs)
{
	char *bp, *cp, *mp;
	int mlen;

	if(nfargs == 0) {
		return;
	}
	if(!fn_range_check("AFTER", nfargs, 1, 2, buff, bufc))
		return;
	bp = fargs[0];
	mp = fargs[1];

	/*
	 * Sanity-check arg1 and arg2 
	 */

	if(bp == NULL)
		bp = "";
	if(mp == NULL)
		mp = " ";
	if(!mp || !*mp)
		mp = (char *) " ";
	mlen = strlen(mp);
	if((mlen == 1) && (*mp == ' '))
		bp = trim_space_sep(bp, ' ');

	/*
	 * Look for the target string 
	 */

	while (*bp) {

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

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

			/*
			 * Not found, return empty string 
			 */

			return;
		}
		/*
		 * See if what follows is what we are looking for 
		 */

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

			/*
			 * Yup, return what follows 
			 */

			bp = cp + mlen;
			safe_str(bp, buff, bufc);
			return;
		}
		/*
		 * Continue search after found first character 
		 */

		bp = cp + 1;
	}

	/*
	 * Ran off the end without finding it 
	 */

	return;
}

static void fun_before(char *buff, char **bufc, dbref player, dbref cause,
					   char *fargs[], int nfargs, char *cargs[], int ncargs)
{
	char *bp, *cp, *mp, *ip;
	int mlen;

	if(nfargs == 0) {
		return;
	}
	if(!fn_range_check("BEFORE", nfargs, 1, 2, buff, bufc))
		return;

	bp = fargs[0];
	mp = fargs[1];

	/*
	 * Sanity-check arg1 and arg2 
	 */

	if(bp == NULL)
		bp = "";
	if(mp == NULL)
		mp = " ";
	if(!mp || !*mp)
		mp = (char *) " ";
	mlen = strlen(mp);
	if((mlen == 1) && (*mp == ' '))
		bp = trim_space_sep(bp, ' ');
	ip = bp;

	/*
	 * Look for the target string 
	 */

	while (*bp) {

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

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

			/*
			 * Not found, return entire string 
			 */

			safe_str(ip, buff, bufc);
			return;
		}
		/*
		 * See if what follows is what we are looking for 
		 */

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

			/*
			 * Yup, return what follows 
			 */

			*cp = '\0';
			safe_str(ip, buff, bufc);
			return;
		}
		/*
		 * Continue search after found first character 
		 */

		bp = cp + 1;
	}

	/*
	 * Ran off the end without finding it 
	 */

	safe_str(ip, buff, bufc);
	return;
}

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

static void fun_max(char *buff, char **bufc, dbref player, dbref cause,
					char *fargs[], int nfargs, char *cargs[], int ncargs)
{
	int i, j, got_one;
	double max;

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

	if(!got_one)
		safe_str("#-1 TOO FEW ARGUMENTS", buff, bufc);
	else
		fval(buff, bufc, max);
	return;
}

static void fun_min(char *buff, char **bufc, dbref player, dbref cause,
					char *fargs[], int nfargs, char *cargs[], int ncargs)
{
	int i, j, got_one;
	double min;

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

	if(!got_one) {
		safe_str("#-1 TOO FEW ARGUMENTS", buff, bufc);
	} else {
		fval(buff, bufc, min);
	}
	return;
}

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

static void fun_search(char *buff, char **bufc, dbref player, dbref cause,
					   char *fargs[], int nfargs, char *cargs[], int ncargs)
{
	dbref thing;
	char *bp, *nbuf;
	SEARCH searchparm;

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

	if(!search_setup(player, fargs[0], &searchparm)) {
		safe_str("#-1 ERROR DURING SEARCH", buff, bufc);
		return;
	}
	/*
	 * Do the search and report the results 
	 */

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

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

static void fun_stats(char *buff, char **bufc, dbref player, dbref cause,
					  char *fargs[], int nfargs, char *cargs[], int ncargs)
{
	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) {
			safe_str("#-1 NOT FOUND", buff, bufc);
			return;
		}
	}
	if(!get_stats(player, who, &statinfo)) {
		safe_str("#-1 ERROR GETTING STATS", buff, bufc);
		return;
	}
	safe_tprintf_str(buff, bufc, "%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_merge:  given two strings and a character, merge the two strings
 * *   by replacing characters in string1 that are the same as the given 
 * *   character by the corresponding character in string2 (by position).
 * *   The strings must be of the same length.
 */

static void fun_merge(char *buff, char **bufc, dbref player, dbref cause,
					  char *fargs[], int nfargs, char *cargs[], int ncargs)
{
	char *str, *rep;
	char c;

	/*
	 * do length checks first 
	 */

	if(strlen(fargs[0]) != strlen(fargs[1])) {
		safe_str("#-1 STRING LENGTHS MUST BE EQUAL", buff, bufc);
		return;
	}
	if(strlen(fargs[2]) > 1) {
		safe_str("#-1 TOO MANY CHARACTERS", buff, bufc);
		return;
	}
	/*
	 * find the character to look for. null character is considered * a * 
	 * 
	 * *  * * space 
	 */

	if(!*fargs[2])
		c = ' ';
	else
		c = *fargs[2];

	/*
	 * walk strings, copy from the appropriate string 
	 */

	for(str = fargs[0], rep = fargs[1];
		*str && *rep && ((*bufc - buff) < LBUF_SIZE);
		str++, rep++, (*bufc)++) {
		if(*str == c)
			**bufc = *rep;
		else
			**bufc = *str;
	}

	/*
	 * There is no need to check for overflowing the buffer since * both
	 * * * * strings are LBUF_SIZE or less and the new string cannot be *
	 * * any * * longer. 
	 */

	return;
}

/*
 * ---------------------------------------------------------------------------
 * * fun_splice: similar to MERGE(), eplaces by word instead of by character.
 */

static void fun_splice(char *buff, char **bufc, dbref player, dbref cause,
					   char *fargs[], int nfargs, char *cargs[], int ncargs)
{
	char *p1, *p2, *q1, *q2, sep;
	int words, i, first;

	varargs_preamble("SPLICE", 4);

	/*
	 * length checks 
	 */

	if(countwords(fargs[2], sep) > 1) {
		safe_str("#-1 TOO MANY WORDS", buff, bufc);
		return;
	}
	words = countwords(fargs[0], sep);
	if(words != countwords(fargs[1], sep)) {
		safe_str("#-1 NUMBER OF WORDS MUST BE EQUAL", buff, bufc);
		return;
	}
	/*
	 * loop through the two lists 
	 */

	p1 = fargs[0];
	q1 = fargs[1];
	first = 1;
	for(i = 0; i < words; i++) {
		p2 = split_token(&p1, sep);
		q2 = split_token(&q1, sep);
		if(!first)
			safe_chr(sep, buff, bufc);
		if(!strcmp(p2, fargs[2]))
			safe_str(q2, buff, bufc);	/*
										 * replace 
										 */
		else
			safe_str(p2, buff, bufc);	/*
										 * copy 
										 */
		first = 0;
	}
}

/*
 * ---------------------------------------------------------------------------
 * * fun_repeat: repeats a string
 */

static void fun_repeat(char *buff, char **bufc, dbref player, dbref cause,
					   char *fargs[], int nfargs, char *cargs[], int ncargs)
{
	int times, i;

	times = atoi(fargs[1]);
	if((times < 1) || (fargs[0] == NULL) || (!*fargs[0])) {
		return;
	} else if(times == 1) {
		safe_str(fargs[0], buff, bufc);
	} else if(strlen(fargs[0]) * times >= (LBUF_SIZE - 1)) {
		safe_str("#-1 STRING TOO LONG", buff, bufc);
	} else {
		for(i = 0; i < times; i++)
			safe_str(fargs[0], buff, bufc);
	}
}

/*
 * ---------------------------------------------------------------------------
 * * fun_iter: Make list from evaluating arg2 with each member of arg1.
 * * NOTE: This function expects that its arguments have not been evaluated.
 */

static void fun_iter(char *buff, char **bufc, dbref player, dbref cause,
					 char *fargs[], int nfargs, char *cargs[], int ncargs)
{
	char *curr, *objstring, *buff2, *buff3, *cp, *dp, sep, *str;
	int first, number = 0;

	evarargs_preamble("ITER", 3);
	dp = cp = curr = alloc_lbuf("fun_iter");
	str = fargs[0];
	exec(curr, &dp, 0, player, cause, EV_STRIP | EV_FCHECK | EV_EVAL, &str,
		 cargs, ncargs);
	*dp = '\0';
	cp = trim_space_sep(cp, sep);
	if(!*cp) {
		free_lbuf(curr);
		return;
	}
	first = 1;
	while (cp) {
		if(!first)
			safe_chr(' ', buff, bufc);
		first = 0;
		number++;
		objstring = split_token(&cp, sep);
		buff2 = replace_string(BOUND_VAR, objstring, fargs[1]);
		buff3 = replace_string(LISTPLACE_VAR, tprintf("%d", number), buff2);
		str = buff3;
		exec(buff, bufc, 0, player, cause, EV_STRIP | EV_FCHECK | EV_EVAL,
			 &str, cargs, ncargs);
		free_lbuf(buff2);
		free_lbuf(buff3);
	}
	free_lbuf(curr);
}

static void fun_list(char *buff, char **bufc, dbref player, dbref cause,
					 char *fargs[], int nfargs, char *cargs[], int ncargs)
{
	char *curr, *objstring, *buff2, *buff3, *result, *cp, *dp, *str, sep;
	int number = 0;

	evarargs_preamble("LIST", 3);
	cp = curr = dp = alloc_lbuf("fun_list");
	str = fargs[0];
	exec(curr, &dp, 0, player, cause, EV_STRIP | EV_FCHECK | EV_EVAL, &str,
		 cargs, ncargs);
	cp = trim_space_sep(cp, sep);
	if(!*cp) {
		free_lbuf(curr);
		return;
	}
	while (cp) {
		number++;
		objstring = split_token(&cp, sep);
		buff2 = replace_string(BOUND_VAR, objstring, fargs[1]);
		buff3 = replace_string(LISTPLACE_VAR, tprintf("%d", number), buff2);
		dp = result = alloc_lbuf("fun_list.2");
		str = buff3;
		exec(result, &dp, 0, player, cause, EV_STRIP | EV_FCHECK | EV_EVAL,
			 &str, cargs, ncargs);
		*dp = '\0';
		free_lbuf(buff2);
		free_lbuf(buff3);
		notify(cause, result);
		free_lbuf(result);
	}
	free_lbuf(curr);
}

/*
 * ---------------------------------------------------------------------------
 * * fun_fold: iteratively eval an attrib with a list of arguments
 * *        and an optional base case.  With no base case, the first list element
 * *    is passed as %0 and the second is %1.  The attrib is then evaluated
 * *    with these args, the result is then used as %0 and the next arg is
 * *    %1 and so it goes as there are elements left in the list.  The
 * *    optinal base case gives the user a nice starting point.
 * *
 * *    > &REP_NUM object=[%0][repeat(%1,%1)]
 * *    > say fold(OBJECT/REP_NUM,1 2 3 4 5,->)
 * *    You say "->122333444455555"
 * *
 * *      NOTE: To use added list separator, you must use base case!
 */

static void fun_fold(char *buff, char **bufc, dbref player, dbref cause,
					 char *fargs[], int nfargs, char *cargs[], int ncargs)
{
	dbref aowner, thing;
	int aflags, anum;
	ATTR *ap;
	char *atext, *result, *curr, *bp, *str, *cp, *atextbuf, *clist[2],
		*rstore, sep;

	/*
	 * We need two to four arguements only 
	 */

	mvarargs_preamble("FOLD", 2, 4);

	/*
	 * 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) {
		return;
	}
	/*
	 * Use it if we can access it, otherwise return an error. 
	 */

	atext = atr_pget(thing, ap->number, &aowner, &aflags);
	if(!atext) {
		return;
	} else if(!*atext || !See_attr(player, thing, ap, aowner, aflags)) {
		free_lbuf(atext);
		return;
	}
	/*
	 * Evaluate it using the rest of the passed function args 
	 */

	cp = curr = fargs[1];
	atextbuf = alloc_lbuf("fun_fold");
	StringCopy(atextbuf, atext);

	/*
	 * may as well handle first case now 
	 */

	if((nfargs >= 3) && (fargs[2])) {
		clist[0] = fargs[2];
		clist[1] = split_token(&cp, sep);
		result = bp = alloc_lbuf("fun_fold");
		str = atextbuf;
		exec(result, &bp, 0, player, cause, EV_STRIP | EV_FCHECK | EV_EVAL,
			 &str, clist, 2);
		*bp = '\0';
	} else {
		clist[0] = split_token(&cp, sep);
		clist[1] = split_token(&cp, sep);
		result = bp = alloc_lbuf("fun_fold");
		str = atextbuf;
		exec(result, &bp, 0, player, cause, EV_STRIP | EV_FCHECK | EV_EVAL,
			 &str, clist, 2);
		*bp = '\0';
	}

	rstore = result;
	result = NULL;

	while (cp) {
		clist[0] = rstore;
		clist[1] = split_token(&cp, sep);
		StringCopy(atextbuf, atext);
		result = bp = alloc_lbuf("fun_fold");
		str = atextbuf;
		exec(result, &bp, 0, player, cause, EV_STRIP | EV_FCHECK | EV_EVAL,
			 &str, clist, 2);
		*bp = '\0';
		StringCopy(rstore, result);
		free_lbuf(result);
	}
	safe_str(rstore, buff, bufc);
	free_lbuf(rstore);
	free_lbuf(atext);
	free_lbuf(atextbuf);
}

/*
 * ---------------------------------------------------------------------------
 * * fun_filter: iteratively perform a function with a list of arguments
 * *              and return the arg, if the function evaluates to TRUE using the 
 * *      arg.
 * *
 * *      > &IS_ODD object=mod(%0,2)
 * *      > say filter(object/is_odd,1 2 3 4 5)
 * *      You say "1 3 5"
 * *      > say filter(object/is_odd,1-2-3-4-5,-)
 * *      You say "1-3-5"
 * *
 * *  NOTE:  If you specify a separator it is used to delimit returned list
 */

static void fun_filter(char *buff, char **bufc, dbref player, dbref cause,
					   char *fargs[], int nfargs, char *cargs[], int ncargs)
{
	dbref aowner, thing;
	int aflags, anum, first;
	ATTR *ap;
	char *atext, *result, *curr, *objstring, *bp, *str, *cp, *atextbuf, sep;

	varargs_preamble("FILTER", 3);

	/*
	 * 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) {
		return;
	}
	/*
	 * Use it if we can access it, otherwise return an error. 
	 */

	atext = atr_pget(thing, ap->number, &aowner, &aflags);
	if(!atext) {
		return;
	} else if(!*atext || !See_attr(player, thing, ap, aowner, aflags)) {
		free_lbuf(atext);
		return;
	}
	/*
	 * Now iteratively eval the attrib with the argument list 
	 */

	cp = curr = trim_space_sep(fargs[1], sep);
	atextbuf = alloc_lbuf("fun_filter");
	first = 1;
	while (cp) {
		objstring = split_token(&cp, sep);
		StringCopy(atextbuf, atext);
		result = bp = alloc_lbuf("fun_filter");
		str = atextbuf;
		exec(result, &bp, 0, player, cause, EV_STRIP | EV_FCHECK | EV_EVAL,
			 &str, &objstring, 1);
		*bp = '\0';
		if(!first && *result == '1')
			safe_chr(sep, buff, bufc);
		if(*result == '1') {
			safe_str(objstring, buff, bufc);
			first = 0;
		}
		free_lbuf(result);
	}
	free_lbuf(atext);
	free_lbuf(atextbuf);
}

/*
 * ---------------------------------------------------------------------------
 * * fun_map: iteratively evaluate an attribute with a list of arguments.
 * *
 * *  > &DIV_TWO object=fdiv(%0,2)
 * *  > say map(1 2 3 4 5,object/div_two)
 * *  You say "0.5 1 1.5 2 2.5"
 * *  > say map(object/div_two,1-2-3-4-5,-)
 * *  You say "0.5-1-1.5-2-2.5"
 * *
 */

static void fun_map(char *buff, char **bufc, dbref player, dbref cause,
					char *fargs[], int nfargs, char *cargs[], int ncargs)
{
	dbref aowner, thing;
	int aflags, anum, first;
	ATTR *ap;
	char *atext, *objstring, *str, *cp, *atextbuf, sep;

	varargs_preamble("MAP", 3);

	/*
	 * Two possibilities for the second 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) {
		return;
	}
	/*
	 * Use it if we can access it, otherwise return an error. 
	 */

	atext = atr_pget(thing, ap->number, &aowner, &aflags);
	if(!atext) {
		return;
	} else if(!*atext || !See_attr(player, thing, ap, aowner, aflags)) {
		free_lbuf(atext);
		return;
	}
	/*
	 * now process the list one element at a time 
	 */

	cp = trim_space_sep(fargs[1], sep);
	atextbuf = alloc_lbuf("fun_map");
	first = 1;
	while (cp) {
		if(!first)
			safe_chr(sep, buff, bufc);
		first = 0;
		objstring = split_token(&cp, sep);
		StringCopy(atextbuf, atext);
		str = atextbuf;
		exec(buff, bufc, 0, player, cause, EV_STRIP | EV_FCHECK | EV_EVAL,
			 &str, &objstring, 1);
	}
	free_lbuf(atext);
	free_lbuf(atextbuf);
}

/*
 * ---------------------------------------------------------------------------
 * * fun_edit: Edit text.
 */

static void fun_edit(char *buff, char **bufc, dbref player, dbref cause,
					 char *fargs[], int nfargs, char *cargs[], int ncargs)
{
	char *tstr;

	edit_string((char *) strip_ansi(fargs[0]), &tstr, fargs[1], fargs[2]);
	safe_str(tstr, buff, bufc);
	free_lbuf(tstr);
}

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

static void fun_locate(char *buff, char **bufc, dbref player, dbref cause,
					   char *fargs[], int nfargs, char *cargs[], int ncargs)
{
	int pref_type, check_locks, verbose, multiple;
	dbref thing, what;
	char *cp;

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

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

	if(See_All(player))
		thing = match_thing(player, fargs[0]);
	else
		thing = match_controlled(player, fargs[0]);
	if(!Good_obj(thing)) {
		safe_str("#-1 PERMISSION DENIED", buff, bufc);
		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;
		case 'X':
			multiple = 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(MAT_EXIT_PARENTS);
			break;
		}
	}

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

	if(multiple)
		what = last_match_result();
	else
		what = match_result();

	if(verbose)
		(void) match_status(player, what);

	safe_tprintf_str(buff, bufc, "#%d", what);
}

/*
 * ---------------------------------------------------------------------------
 * * fun_switch: Return value based on pattern matching (ala @switch)
 * * NOTE: This function expects that its arguments have not been evaluated.
 */

static void fun_switch(char *buff, char **bufc, dbref player, dbref cause,
					   char *fargs[], int nfargs, char *cargs[], int ncargs)
{
	int i;
	char *mbuff, *tbuff, *buf, *bp, *str;

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

	if(nfargs < 2) {
		return;
	}
	/*
	 * Evaluate the target in fargs[0] 
	 */

	mbuff = bp = alloc_lbuf("fun_switch");
	str = fargs[0];
	exec(mbuff, &bp, 0, player, cause, EV_STRIP | EV_FCHECK | EV_EVAL,
		 &str, cargs, ncargs);
	*bp = '\0';

	/*
	 * Loop through the patterns looking for a match 
	 */

	for(i = 1; (i < nfargs - 1) && fargs[i] && fargs[i + 1]; i += 2) {
		tbuff = bp = alloc_lbuf("fun_switch.2");
		str = fargs[i];
		exec(tbuff, &bp, 0, player, cause, EV_STRIP | EV_FCHECK | EV_EVAL,
			 &str, cargs, ncargs);
		*bp = '\0';
		if(quick_wild(tbuff, mbuff)) {
			free_lbuf(tbuff);
			buf = alloc_lbuf("fun_switch");
			StringCopy(buf, fargs[i + 1]);
			str = buf;
			exec(buff, bufc, 0, player, cause,
				 EV_STRIP | EV_FCHECK | EV_EVAL, &str, cargs, ncargs);
			free_lbuf(buf);
			free_lbuf(mbuff);
			return;
		}
		free_lbuf(tbuff);
	}
	free_lbuf(mbuff);

	/*
	 * Nope, return the default if there is one 
	 */

	if((i < nfargs) && fargs[i]) {
		buf = alloc_lbuf("fun_switch");
		StringCopy(buf, fargs[i]);
		str = buf;
		exec(buff, bufc, 0, player, cause, EV_STRIP | EV_FCHECK | EV_EVAL,
			 &str, cargs, ncargs);
		free_lbuf(buf);
	}
	return;
}

static void fun_case(char *buff, char **bufc, dbref player, dbref cause,
					 char *fargs[], int nfargs, char *cargs[], int ncargs)
{
	int i;
	char *mbuff, *buf, *bp, *str;

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

	if(nfargs < 2) {
		return;
	}
	/*
	 * Evaluate the target in fargs[0] 
	 */

	mbuff = bp = alloc_lbuf("fun_switch");
	str = fargs[0];
	exec(mbuff, &bp, 0, player, cause, EV_STRIP | EV_FCHECK | EV_EVAL,
		 &str, cargs, ncargs);
	*bp = '\0';

	/*
	 * Loop through the patterns looking for a match 
	 */

	for(i = 1; (i < nfargs - 1) && fargs[i] && fargs[i + 1]; i += 2) {
		if(*fargs[i] == *mbuff) {
			buf = alloc_lbuf("fun_switch");
			StringCopy(buf, fargs[i + 1]);
			str = buf;
			exec(buff, bufc, 0, player, cause,
				 EV_STRIP | EV_FCHECK | EV_EVAL, &str, cargs, ncargs);
			free_lbuf(buf);
			free_lbuf(mbuff);
			return;
		}
	}
	free_lbuf(mbuff);

	/*
	 * Nope, return the default if there is one 
	 */

	if((i < nfargs) && fargs[i]) {
		buf = alloc_lbuf("fun_switch");
		StringCopy(buf, fargs[i]);
		str = buf;
		exec(buff, bufc, 0, player, cause, EV_STRIP | EV_FCHECK | EV_EVAL,
			 &str, cargs, ncargs);
		free_lbuf(buf);
	}
	return;
}

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

static void fun_space(char *buff, char **bufc, dbref player, dbref cause,
					  char *fargs[], int nfargs, char *cargs[], int ncargs)
{
	int num;
	char *cp;

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

	if(num < 1) {

		/*
		 * If negative or zero spaces return a single space,  * * * * 
		 * -except- allow 'space(0)' to return "" for calculated * *
		 * * * padding 
		 */

		if(!is_integer(fargs[0]) || (num != 0)) {
			num = 1;
		}
	} else if(num >= LBUF_SIZE) {
		num = LBUF_SIZE - 1;
	}
	for(cp = *bufc; num > 0; num--)
		*cp++ = ' ';
	*bufc = cp;
	return;
}

/*
 * ---------------------------------------------------------------------------
 * * fun_idle, fun_conn: return seconds idle or connected.
 */

static void fun_idle(char *buff, char **bufc, dbref player, dbref cause,
					 char *fargs[], int nfargs, char *cargs[], int ncargs)
{
	dbref target;

	target = lookup_player(player, fargs[0], 1);
	if(Good_obj(target) && Dark(target) && !Wizard(player))
		target = NOTHING;
	safe_tprintf_str(buff, bufc, "%d", fetch_idle(target));
}

static void fun_conn(char *buff, char **bufc, dbref player, dbref cause,
					 char *fargs[], int nfargs, char *cargs[], int ncargs)
{
	dbref target;

	target = lookup_player(player, fargs[0], 1);
	if(Good_obj(target) && Dark(target) && !Wizard(player))
		target = NOTHING;
	safe_tprintf_str(buff, bufc, "%d", fetch_connect(target));
}

/*
 * ---------------------------------------------------------------------------
 * * fun_sort: Sort lists.
 */

typedef struct f_record f_rec;
typedef struct i_record i_rec;

struct f_record {
	double data;
	char *str;
};

struct i_record {
	long data;
	char *str;
};

static int a_comp(const void *s1, const void *s2)
{
	return strcmp(*(char **) s1, *(char **) s2);
}

static int f_comp(const void *s1, const void *s2)
{
	if(((f_rec *) s1)->data > ((f_rec *) s2)->data)
		return 1;
	if(((f_rec *) s1)->data < ((f_rec *) s2)->data)
		return -1;
	return 0;
}

static int i_comp(const void *s1, const void *s2)
{
	if(((i_rec *) s1)->data > ((i_rec *) s2)->data)
		return 1;
	if(((i_rec *) s1)->data < ((i_rec *) s2)->data)
		return -1;
	return 0;
}

static void do_asort(char *s[], int n, int sort_type)
{
	int i;
	f_rec *fp;
	i_rec *ip;

	switch (sort_type) {
	case ALPHANUM_LIST:
		qsort((void *) s, n, sizeof(char *), a_comp);

		break;
	case NUMERIC_LIST:
		ip = (i_rec *) malloc(n * sizeof(i_rec));
		for(i = 0; i < n; i++) {
			ip[i].str = s[i];
			ip[i].data = atoi(s[i]);
		}
		qsort((void *) ip, n, sizeof(i_rec), i_comp);
		for(i = 0; i < n; i++) {
			s[i] = ip[i].str;
		}
		free(ip);
		break;
	case DBREF_LIST:
		ip = (i_rec *) malloc(n * sizeof(i_rec));
		for(i = 0; i < n; i++) {
			ip[i].str = s[i];
			ip[i].data = dbnum(s[i]);
		}
		qsort((void *) ip, n, sizeof(i_rec), i_comp);
		for(i = 0; i < n; i++) {
			s[i] = ip[i].str;
		}
		free(ip);
		break;
	case FLOAT_LIST:
		fp = (f_rec *) malloc(n * sizeof(f_rec));
		for(i = 0; i < n; i++) {
			fp[i].str = s[i];
			fp[i].data = atof(s[i]);
		}
		qsort((void *) fp, n, sizeof(f_rec), f_comp);
		for(i = 0; i < n; i++) {
			s[i] = fp[i].str;
		}
		free(fp);
		break;
	}
}

static void fun_sort(char *buff, char **bufc, dbref player, dbref cause,
					 char *fargs[], int nfargs, char *cargs[], int ncargs)
{
	int nitems, sort_type;
	char *list, sep;
	char *ptrs[LBUF_SIZE / 2];

	/*
	 * If we are passed an empty arglist return a null string 
	 */

	if(nfargs == 0) {
		return;
	}
	mvarargs_preamble("SORT", 1, 3);

	/*
	 * Convert the list to an array 
	 */

	list = alloc_lbuf("fun_sort");
	StringCopy(list, fargs[0]);
	nitems = list2arr(ptrs, LBUF_SIZE / 2, list, sep);
	sort_type = get_list_type(fargs, nfargs, 2, ptrs, nitems);
	do_asort(ptrs, nitems, sort_type);
	arr2list(ptrs, nitems, buff, bufc, sep);
	free_lbuf(list);
}

/*
 * ---------------------------------------------------------------------------
 * * fun_setunion, fun_setdiff, fun_setinter: Set management.
 */

#define	SET_UNION	1
#define	SET_INTERSECT	2
#define	SET_DIFF	3

static void handle_sets(char *fargs[], char *buff, char **bufc,
						int oper, char sep)
{
	char *list1, *list2, *oldp;
	char *ptrs1[LBUF_SIZE], *ptrs2[LBUF_SIZE];
	int i1, i2, n1, n2, val, first;

	list1 = alloc_lbuf("fun_setunion.1");
	StringCopy(list1, fargs[0]);
	n1 = list2arr(ptrs1, LBUF_SIZE, list1, sep);
	do_asort(ptrs1, n1, ALPHANUM_LIST);

	list2 = alloc_lbuf("fun_setunion.2");
	StringCopy(list2, fargs[1]);
	n2 = list2arr(ptrs2, LBUF_SIZE, list2, sep);
	do_asort(ptrs2, n2, ALPHANUM_LIST);

	i1 = i2 = 0;
	first = 1;
	oldp = *bufc;
	**bufc = '\0';

	switch (oper) {
	case SET_UNION:			/*
								 * Copy elements common to both lists 
								 */

		/*
		 * Handle case of two identical single-element lists 
		 */

		if((n1 == 1) && (n2 == 1) && (!strcmp(ptrs1[0], ptrs2[0]))) {
			safe_str(ptrs1[0], buff, bufc);
			break;
		}
		/*
		 * Process until one list is empty 
		 */

		while ((i1 < n1) && (i2 < n2)) {

			/*
			 * Skip over duplicates 
			 */

			if((i1 > 0) || (i2 > 0)) {
				while ((i1 < n1) && !strcmp(ptrs1[i1], oldp))
					i1++;
				while ((i2 < n2) && !strcmp(ptrs2[i2], oldp))
					i2++;
			}
			/*
			 * Compare and copy 
			 */

			if((i1 < n1) && (i2 < n2)) {
				if(!first)
					safe_chr(sep, buff, bufc);
				first = 0;
				oldp = *bufc;
				if(strcmp(ptrs1[i1], ptrs2[i2]) < 0) {
					safe_str(ptrs1[i1], buff, bufc);
					i1++;
				} else {
					safe_str(ptrs2[i2], buff, bufc);
					i2++;
				}
				**bufc = '\0';
			}
		}

		/*
		 * Copy rest of remaining list, stripping duplicates 
		 */

		for(; i1 < n1; i1++) {
			if(strcmp(oldp, ptrs1[i1])) {
				if(!first)
					safe_chr(sep, buff, bufc);
				first = 0;
				oldp = *bufc;
				safe_str(ptrs1[i1], buff, bufc);
				**bufc = '\0';
			}
		}
		for(; i2 < n2; i2++) {
			if(strcmp(oldp, ptrs2[i2])) {
				if(!first)
					safe_chr(sep, buff, bufc);
				first = 0;
				oldp = *bufc;
				safe_str(ptrs2[i2], buff, bufc);
				**bufc = '\0';
			}
		}
		break;
	case SET_INTERSECT:		/*
								 * Copy elements not in both lists 
								 */

		while ((i1 < n1) && (i2 < n2)) {
			val = strcmp(ptrs1[i1], ptrs2[i2]);
			if(!val) {

				/*
				 * Got a match, copy it 
				 */

				if(!first)
					safe_chr(sep, buff, bufc);
				first = 0;
				oldp = *bufc;
				safe_str(ptrs1[i1], buff, bufc);
				i1++;
				i2++;
				while ((i1 < n1) && !strcmp(ptrs1[i1], oldp))
					i1++;
				while ((i2 < n2) && !strcmp(ptrs2[i2], oldp))
					i2++;
			} else if(val < 0) {
				i1++;
			} else {
				i2++;
			}
		}
		break;
	case SET_DIFF:				/*
								 * Copy elements unique to list1 
								 */

		while ((i1 < n1) && (i2 < n2)) {
			val = strcmp(ptrs1[i1], ptrs2[i2]);
			if(!val) {

				/*
				 * Got a match, increment pointers 
				 */

				oldp = ptrs1[i1];
				while ((i1 < n1) && !strcmp(ptrs1[i1], oldp))
					i1++;
				while ((i2 < n2) && !strcmp(ptrs2[i2], oldp))
					i2++;
			} else if(val < 0) {

				/*
				 * Item in list1 not in list2, copy 
				 */

				if(!first)
					safe_chr(sep, buff, bufc);
				first = 0;
				safe_str(ptrs1[i1], buff, bufc);
				oldp = ptrs1[i1];
				i1++;
				while ((i1 < n1) && !strcmp(ptrs1[i1], oldp))
					i1++;
			} else {

				/*
				 * Item in list2 but not in list1, discard 
				 */

				oldp = ptrs2[i2];
				i2++;
				while ((i2 < n2) && !strcmp(ptrs2[i2], oldp))
					i2++;
			}
		}

		/*
		 * Copy remainder of list1 
		 */

		while (i1 < n1) {
			if(!first)
				safe_chr(sep, buff, bufc);
			first = 0;
			safe_str(ptrs1[i1], buff, bufc);
			oldp = ptrs1[i1];
			i1++;
			while ((i1 < n1) && !strcmp(ptrs1[i1], oldp))
				i1++;
		}
	}
	free_lbuf(list1);
	free_lbuf(list2);
	return;
}

static void fun_setunion(char *buff, char **bufc, dbref player, dbref cause,
						 char *fargs[], int nfargs, char *cargs[], int ncargs)
{
	char sep;

	varargs_preamble("SETUNION", 3);
	handle_sets(fargs, buff, bufc, SET_UNION, sep);
	return;
}

static void fun_setdiff(char *buff, char **bufc, dbref player, dbref cause,
						char *fargs[], int nfargs, char *cargs[], int ncargs)
{
	char sep;

	varargs_preamble("SETDIFF", 3);
	handle_sets(fargs, buff, bufc, SET_DIFF, sep);
	return;
}

static void fun_setinter(char *buff, char **bufc, dbref player, dbref cause,
						 char *fargs[], int nfargs, char *cargs[], int ncargs)
{
	char sep;

	varargs_preamble("SETINTER", 3);
	handle_sets(fargs, buff, bufc, SET_INTERSECT, sep);
	return;
}

/*
 * ---------------------------------------------------------------------------
 * * rjust, ljust, center: Justify or center text, specifying fill character
 */

static void fun_ljust(char *buff, char **bufc, dbref player, dbref cause,
					  char *fargs[], int nfargs, char *cargs[], int ncargs)
{
	int spaces, i;
	char sep;

	varargs_preamble("LJUST", 3);
	spaces = atoi(fargs[1]) - strlen((char *) strip_ansi(fargs[0]));

	/*
	 * Sanitize number of spaces 
	 */

	if(spaces <= 0) {
		/*
		 * no padding needed, just return string 
		 */
		safe_str(fargs[0], buff, bufc);
		return;
	} else if(spaces > LBUF_SIZE) {
		spaces = LBUF_SIZE;
	}
	safe_str(fargs[0], buff, bufc);
	for(i = 0; i < spaces; i++)
		safe_chr(sep, buff, bufc);
}

static void fun_rjust(char *buff, char **bufc, dbref player, dbref cause,
					  char *fargs[], int nfargs, char *cargs[], int ncargs)
{
	int spaces, i;
	char sep;

	varargs_preamble("RJUST", 3);
	spaces = atoi(fargs[1]) - strlen((char *) strip_ansi(fargs[0]));

	/*
	 * Sanitize number of spaces 
	 */

	if(spaces <= 0) {
		/*
		 * no padding needed, just return string 
		 */
		safe_str(fargs[0], buff, bufc);
		return;
	} else if(spaces > LBUF_SIZE) {
		spaces = LBUF_SIZE;
	}
	for(i = 0; i < spaces; i++)
		safe_chr(sep, buff, bufc);
	safe_str(fargs[0], buff, bufc);
}

static void fun_center(char *buff, char **bufc, dbref player, dbref cause,
					   char *fargs[], int nfargs, char *cargs[], int ncargs)
{
	char sep;
	int i, len, lead_chrs, trail_chrs, width;

	varargs_preamble("CENTER", 3);

	width = atoi(fargs[1]);
	len = strlen((char *) strip_ansi(fargs[0]));

	if(width > LBUF_SIZE) {
		safe_str("#-1 OUT OF RANGE", buff, bufc);
		return;
	}

	if(len >= width) {
		safe_str(fargs[0], buff, bufc);
		return;
	}

	lead_chrs = (width / 2) - (len / 2) + .5;
	for(i = 0; i < lead_chrs; i++)
		safe_chr(sep, buff, bufc);
	safe_str(fargs[0], buff, bufc);
	trail_chrs = width - lead_chrs - len;
	for(i = 0; i < trail_chrs; i++)
		safe_chr(sep, buff, bufc);
}

/*
 * ---------------------------------------------------------------------------
 * * setq, setr, r: set and read global registers.
 */

static void fun_setq(char *buff, char **bufc, dbref player, dbref cause,
					 char *fargs[], int nfargs, char *cargs[], int ncargs)
{
	int regnum;

	regnum = atoi(fargs[0]);
	if((regnum < 0) || (regnum >= MAX_GLOBAL_REGS)) {
		safe_str("#-1 INVALID GLOBAL REGISTER", buff, bufc);
	} else {
		if(!mudstate.global_regs[regnum])
			mudstate.global_regs[regnum] = alloc_lbuf("fun_setq");
		StringCopy(mudstate.global_regs[regnum], fargs[1]);
	}
}

static void fun_setr(char *buff, char **bufc, dbref player, dbref cause,
					 char *fargs[], int nfargs, char *cargs[], int ncargs)
{
	int regnum;

	regnum = atoi(fargs[0]);
	if((regnum < 0) || (regnum >= MAX_GLOBAL_REGS)) {
		safe_str("#-1 INVALID GLOBAL REGISTER", buff, bufc);
		return;
	} else {
		if(!mudstate.global_regs[regnum])
			mudstate.global_regs[regnum] = alloc_lbuf("fun_setq");
		StringCopy(mudstate.global_regs[regnum], fargs[1]);
	}
	safe_str(fargs[1], buff, bufc);
}

static void fun_r(char *buff, char **bufc, dbref player, dbref cause,
				  char *fargs[], int nfargs, char *cargs[], int ncargs)
{
	int regnum;

	regnum = atoi(fargs[0]);
	if((regnum < 0) || (regnum >= MAX_GLOBAL_REGS)) {
		safe_str("#-1 INVALID GLOBAL REGISTER", buff, bufc);
	} else if(mudstate.global_regs[regnum]) {
		safe_str(mudstate.global_regs[regnum], buff, bufc);
	}
}

/*
 * ---------------------------------------------------------------------------
 * * isnum: is the argument a number?
 */

static void fun_isnum(char *buff, char **bufc, dbref player, dbref cause,
					  char *fargs[], int nfargs, char *cargs[], int ncargs)
{
	safe_str((is_number(fargs[0]) ? "1" : "0"), buff, bufc);
}

/*
 * ---------------------------------------------------------------------------
 * * isdbref: is the argument a valid dbref?
 */

static void fun_isdbref(char *buff, char **bufc, dbref player, dbref cause,
						char *fargs[], int nfargs, char *cargs[], int ncargs)
{
	char *p;
	dbref dbitem;

	p = fargs[0];
	if(*p++ == NUMBER_TOKEN) {
		dbitem = parse_dbref(p);
		if(Good_obj(dbitem)) {
			safe_str("1", buff, bufc);
			return;
		}
	}
	safe_str("0", buff, bufc);
}

/*
 * ---------------------------------------------------------------------------
 * * trim: trim off unwanted white space.
 */

static void fun_trim(char *buff, char **bufc, dbref player, dbref cause,
					 char *fargs[], int nfargs, char *cargs[], int ncargs)
{
	char *p, *lastchar, *q, sep;
	int trim;

	if(nfargs == 0) {
		return;
	}
	mvarargs_preamble("TRIM", 1, 3);
	if(nfargs >= 2) {
		switch (ToLower(*fargs[1])) {
		case 'l':
			trim = 1;
			break;
		case 'r':
			trim = 2;
			break;
		default:
			trim = 3;
			break;
		}
	} else {
		trim = 3;
	}

	if(trim == 2 || trim == 3) {
		p = lastchar = fargs[0];
		while (*p != '\0') {
			if(*p != sep)
				lastchar = p;
			p++;
		}
		*(lastchar + 1) = '\0';
	}
	q = fargs[0];
	if(trim == 1 || trim == 3) {
		while (*q != '\0') {
			if(*q == sep)
				q++;
			else
				break;
		}
	}
	safe_str(q, buff, bufc);
}

#ifdef ARBITRARY_LOGFILES
static void fun_logf(char *buff, char **bufc, dbref player, dbref cause,
					 char *fargs[], int nfargs, char *cargs[], int ncargs)
{
	if(!fargs[0] || !fargs[1]) {
		safe_str("#-1 INVALID ARGUMENTS", buff, bufc);
	}
	if(log_to_file(player, fargs[0], fargs[1]))
		return;

	safe_str("#-1 INVALID LOGFILE", buff, bufc);
	return;
}
#endif

/* ----------------------------------------------------------------------
** fun_doing: return @doing string for arg.
** arg is either socket or player (Code adopted from Mux2.3)
*/
static void fun_doing(char *buff, char **bufc, dbref player, dbref cause,
					  char *fargs[], int nfargs, char *cargs[], int ncargs)
{
	if(is_number(fargs[0])) {
		int s = atoi(fargs[0]);
		unsigned short bFound = 0;
		DESC *d;
		DESC_ITER_CONN(d) {
			if(d->descriptor == s) {
				bFound = 1;
				break;
			}
		}
		if(bFound && (d->player == player || Wizard_Who(player))) {
			safe_str(d->doing, buff, bufc);
		} else {
			/* [cad] Mux2.3 returns empty string here, not sure what
			   convention is used here. Just return #-1 for now */
			safe_str("#-1", buff, bufc);
		}
	} else {
		dbref victim = lookup_player(player, fargs[0], 1);
		if(victim == NOTHING) {
			safe_str("#-1 PLAYER DOES NOT EXIST", buff, bufc);
			return;
		}

		if(Wizard_Who(player)
		   || !Hidden(victim)) {
			DESC *d;
			DESC_ITER_CONN(d) {
				if(d->player == victim) {
					safe_str(d->doing, buff, bufc);
					return;
				}
			}
		}
		safe_str("#-1 NOT A CONNECTED PLAYER", buff, bufc);
	}
}

/* ----------------------------------------------------------------------
** fun_poll: return @doing header
** (Code adopted from Mux2.3)
*/
static void fun_poll(char *buff, char **bufc, dbref player, dbref cause,
					 char *fargs[], int nfargs, char *cargs[], int ncargs)
{
	safe_str(mudstate.doing_hdr, buff, bufc);
}

/* ----------------------------------------------------------------------
 ** fun_pairs: take an attr off an object and count the # of
 ** {[()]} in that attribute and return it as a list
 ** Modified from fun_get
 ** Dany - 06/2005
 */
static void fun_pairs(char *buff, char **bufc, dbref player, dbref cause,
					  char *fargs[], int nfargs, char *cargs[], int ncargs)
{
	dbref thing, aowner;
	int attrib, free_buffer, aflags;
	ATTR *attr;
	char *atr_gotten;
	struct boolexp *bool;

	char *tmp_char;
	int right_brace = 0, left_brace = 0, right_square_bracket = 0,
		left_square_bracket = 0, right_parenthesis = 0, left_parenthesis = 0;

	if(!parse_attrib(player, fargs[0], &thing, &attrib)) {
		safe_str("#-1 NO MATCH", buff, bufc);
		return;
	}
	if(attrib == NOTHING) {
		return;
	}
	free_buffer = 1;
	attr = atr_num(attrib);		/*
								 * We need the attr's flags for this: 
								 */
	if(!attr) {
		return;
	}
	if(attr->flags & AF_IS_LOCK) {
		atr_gotten = atr_get(thing, attrib, &aowner, &aflags);
		if(Read_attr(player, thing, attr, aowner, aflags)) {
			bool = parse_boolexp(player, atr_gotten, 1);
			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);
	}

	/*
	 * Perform access checks.  c_r_p fills buff with an error message * * 
	 * 
	 * *  * * if needed. 
	 */

	if(check_read_perms(player, thing, attr, aowner, aflags, buff, bufc)) {

		/* Scan through the attribute and count the various brackets */
		for(tmp_char = atr_gotten; *tmp_char; tmp_char++) {

			switch (*tmp_char) {
			case '{':
				left_brace++;
				break;
			case '[':
				left_square_bracket++;
				break;
			case '(':
				left_parenthesis++;
				break;
			case '}':
				right_brace++;
				break;
			case ']':
				right_square_bracket++;
				break;
			case ')':
				right_parenthesis++;
				break;
			default:
				break;
			}

		}

		snprintf(atr_gotten, LBUF_SIZE, "%d %d %d %d %d %d",
				 left_brace,
				 left_square_bracket,
				 left_parenthesis,
				 right_parenthesis, right_square_bracket, right_brace);
		safe_str(atr_gotten, buff, bufc);
	}

	if(free_buffer)
		free_lbuf(atr_gotten);
	return;
}

/* ----------------------------------------------------------------------
 ** fun_colorpairs: take an attr off an object and color the
 ** {[()]} in that attribute and return it.
 ** Modified from fun_get
 ** Dany - 09/2005
 */
static void fun_colorpairs(char *buff, char **bufc, dbref player, dbref cause,
						   char *fargs[], int nfargs, char *cargs[],
						   int ncargs)
{
	dbref thing, aowner;
	int attrib, free_buffer, aflags;
	ATTR *attr;
	char *atr_gotten;
	struct boolexp *bool;

	char *tmp_char;
	char tmp_string[LBUF_SIZE];
	char tmp_piece[2];

	if(!parse_attrib(player, fargs[0], &thing, &attrib)) {
		safe_str("#-1 NO MATCH", buff, bufc);
		return;
	}
	if(attrib == NOTHING) {
		return;
	}
	free_buffer = 1;
	attr = atr_num(attrib);		/*
								 * We need the attr's flags for this: 
								 */
	if(!attr) {
		return;
	}
	if(attr->flags & AF_IS_LOCK) {
		atr_gotten = atr_get(thing, attrib, &aowner, &aflags);
		if(Read_attr(player, thing, attr, aowner, aflags)) {
			bool = parse_boolexp(player, atr_gotten, 1);
			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);
	}

	/*
	 * Perform access checks.  c_r_p fills buff with an error message * * 
	 * 
	 * *  * * if needed. 
	 */

	if(check_read_perms(player, thing, attr, aowner, aflags, buff, bufc)) {

		/* zero temporary string */
		memset(tmp_string, 0, sizeof(tmp_string));

		/* Scan through the attribute, colorize what we want */
		for(tmp_char = atr_gotten; *tmp_char; tmp_char++) {

			switch (*tmp_char) {
			case '{':
				strncat(tmp_string, ANSI_RED, LBUF_SIZE);
				strncat(tmp_string, "{", LBUF_SIZE);
				strncat(tmp_string, ANSI_NORMAL, LBUF_SIZE);
				break;
			case '[':
				strncat(tmp_string, ANSI_YELLOW, LBUF_SIZE);
				strncat(tmp_string, "[", LBUF_SIZE);
				strncat(tmp_string, ANSI_NORMAL, LBUF_SIZE);
				break;
			case '(':
				strncat(tmp_string, ANSI_GREEN, LBUF_SIZE);
				strncat(tmp_string, "(", LBUF_SIZE);
				strncat(tmp_string, ANSI_NORMAL, LBUF_SIZE);
				break;
			case '}':
				strncat(tmp_string, ANSI_RED, LBUF_SIZE);
				strncat(tmp_string, "}", LBUF_SIZE);
				strncat(tmp_string, ANSI_NORMAL, LBUF_SIZE);
				break;
			case ']':
				strncat(tmp_string, ANSI_YELLOW, LBUF_SIZE);
				strncat(tmp_string, "]", LBUF_SIZE);
				strncat(tmp_string, ANSI_NORMAL, LBUF_SIZE);
				break;
			case ')':
				strncat(tmp_string, ANSI_GREEN, LBUF_SIZE);
				strncat(tmp_string, ")", LBUF_SIZE);
				strncat(tmp_string, ANSI_NORMAL, LBUF_SIZE);
				break;
			default:
				sprintf(tmp_piece, "%c", *tmp_char);
				strncat(tmp_string, tmp_piece, LBUF_SIZE);
				break;
			}

		}

		safe_str(tmp_string, buff, bufc);
	}

	if(free_buffer)
		free_lbuf(atr_gotten);
	return;
}

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

FUN flist[] = {
	{"ABS", fun_abs, 1, 0, CA_PUBLIC}
	,
	{"ACOS", fun_acos, 1, 0, CA_PUBLIC}
	,
	{"ADD", fun_add, 0, FN_VARARGS, CA_PUBLIC}
	,
	{"AFTER", fun_after, 0, FN_VARARGS, CA_PUBLIC}
	,
	{"ALPHAMAX", fun_alphamax, 0, FN_VARARGS, CA_PUBLIC}
	,
	{"ALPHAMIN", fun_alphamin, 0, FN_VARARGS, CA_PUBLIC}
	,
	{"AND", fun_and, 0, FN_VARARGS, CA_PUBLIC}
	,
	{"ANDFLAGS", fun_andflags, 2, 0, CA_PUBLIC}
	,
	{"ANSI", fun_ansi, 2, 0, CA_PUBLIC}
	,
	{"ANSISECURE", fun_ansi_secure, -1, 0, CA_PUBLIC}
	,
	{"APOSS", fun_aposs, 1, 0, CA_PUBLIC}
	,
	{"ART", fun_art, 1, 0, CA_PUBLIC}
	,
	{"ASIN", fun_asin, 1, 0, CA_PUBLIC}
	,
	{"ATAN", fun_atan, 1, 0, CA_PUBLIC}
	,
	{"BEEP", fun_beep, 0, 0, CA_BUILDER}
	,
	{"BEFORE", fun_before, 0, FN_VARARGS, CA_PUBLIC}
	,
	{"BTADDSTORES", fun_btaddstores, 3, 0, CA_WIZARD}
	,
	{"BTARMORSTATUS", fun_btarmorstatus, 2, 0, CA_WIZARD}
	,
	{"BTARMORSTATUS_REF", fun_btarmorstatus_ref, 2, 0, CA_WIZARD}
	,
	{"BTCHARLIST", fun_btcharlist, 1, FN_VARARGS, CA_WIZARD}
	,
	{"BTCRITSLOT", fun_btcritslot, 0, FN_VARARGS, CA_WIZARD}
	,
	{"BTCRITSLOT_REF", fun_btcritslot_ref, 0, FN_VARARGS, CA_WIZARD}
	,
	{"BTCRITSTATUS", fun_btcritstatus, 2, 0, CA_WIZARD}
	,
	{"BTCRITSTATUS_REF", fun_btcritstatus_ref, 2, 0, CA_WIZARD}
	,
	{"BTDAMAGEMECH", fun_btdamagemech, 7, 0, CA_WIZARD}
	,
	{"BTDAMAGES", fun_btdamages, 1, 0, CA_WIZARD}
	,
	{"BTDESIGNEX", fun_btdesignex, 1, 0, CA_PUBLIC}
	,
	{"BTENGRATE", fun_btengrate, 1, 0, CA_WIZARD}
	,
	{"BTENGRATE_REF", fun_btengrate_ref, 1, 0, CA_WIZARD}
	,
#ifdef BT_ADVANCED_ECON
	{"BTFASABASECOST_REF", fun_btfasabasecost_ref, 1, 0, CA_WIZARD}
	,
#endif
	{"BTGETBV", fun_btgetbv, 1, 0, CA_WIZARD}
	,
	{"BTGETBV_REF", fun_btgetbv_ref, 1, 0, CA_WIZARD}
	,
	{"BTGETCHARVALUE", fun_btgetcharvalue, 3, 0, CA_WIZARD}
	,
#ifdef BT_ADVANCED_ECON
	{"BTGETPARTCOST", fun_btgetpartcost, 1, 0, CA_WIZARD}
	,
#endif
	{"BTGETRANGE", fun_btgetrange, 0, FN_VARARGS, CA_WIZARD}
	,
	{"BTGETREALMAXSPEED", fun_btgetrealmaxspeed, 1, 0, CA_WIZARD}
	,
	{"BTGETWEIGHT", fun_btgetweight, 1, 0, CA_WIZARD}
	,
	{"BTGETXCODEVALUE", fun_btgetxcodevalue, 2, 0, CA_WIZARD}
	,
	{"BTGETXCODEVALUE_REF", fun_btgetxcodevalue_ref, 2, 0, CA_WIZARD}
	,
	{"BTHEXEMIT", fun_bthexemit, 4, 0, CA_WIZARD}
	,
	{"BTHEXINBLZ", fun_bthexinblz, 3, 0, CA_WIZARD}
	,
	{"BTHEXLOS", fun_bthexlos, 3, 0, CA_WIZARD}
	,
	{"BTID2DB", fun_btid2db, 2, 0, CA_WIZARD}
	,
	{"BTLAG", fun_lag, 0, 0, CA_WIZARD}
	,
	{"BTLISTBLZ", fun_btlistblz, 1, 0, CA_WIZARD}
	,
	{"BTLOADMAP", fun_btloadmap, 2, FN_VARARGS, CA_WIZARD}
	,
	{"BTLOADMECH", fun_btloadmech, 2, 0, CA_WIZARD}
	,
	{"BTLOSM2M", fun_btlosm2m, 2, 0, CA_WIZARD}
	,
	{"BTMAKEMECHS", fun_btmakemechs, 0, FN_VARARGS, CA_WIZARD}
	,
	{"BTMAKEPILOTROLL", fun_btmakepilotroll, 3, 0, CA_WIZARD}
	,
	{"BTMAPELEV", fun_btmapelev, 3, 0, CA_WIZARD}
	,
	{"BTMAPEMIT", fun_btmapemit, 0, FN_VARARGS, CA_WIZARD}
	,
	{"BTMAPTERR", fun_btmapterr, 3, 0, CA_WIZARD}
	,
	{"BTMAPUNITS", fun_btmapunits, 0, FN_VARARGS, CA_WIZARD}
	,
	{"BTMECHFREQS", fun_btmechfreqs, 1, 0, CA_WIZARD}
	,
	{"BTNUMREPJOBS", fun_btnumrepjobs, 1, 0, CA_WIZARD}
	,
	{"BTPARTTYPE", fun_btparttype, 1, 0, CA_WIZARD}
	,
	{"BTPARTMATCH", fun_btpartmatch, 1, 0, CA_WIZARD}
	,
	{"BTPARTNAME", fun_btpartname, 2, 0, CA_WIZARD}
	,
	{"BTPARTWEIGHT", fun_btgetweight, 1, 0, CA_WIZARD}
	,
	{"BTPAYLOAD_REF", fun_btpayload_ref, 1, 0, CA_WIZARD}
	,
	{"BTREMOVESTORES", fun_btremovestores, 3, 0, CA_WIZARD}
	,
	{"BTSETARMORSTATUS", fun_btsetarmorstatus, 4, 0, CA_WIZARD}
	,
	{"BTSETCHARVALUE", fun_btsetcharvalue, 4, 0, CA_WIZARD}
	,
	{"BTSETMAXSPEED", fun_btsetmaxspeed, 2, 0, CA_WIZARD}
	,
#ifdef BT_ADVANCED_ECON
	{"BTSETPARTCOST", fun_btsetpartcost, 2, 0, CA_WIZARD}
	,
#endif
	{"BTSETXCODEVALUE", fun_btsetxcodevalue, 3, 0, CA_WIZARD}
	,
	{"BTSETXY", fun_btsetxy, 0, FN_VARARGS, CA_WIZARD}
	,
	{"BTSHOWCRITSTATUS_REF", fun_btshowcritstatus_ref, 3, 0, CA_WIZARD}
	,
	{"BTSHOWSTATUS_REF", fun_btshowstatus_ref, 2, 0, CA_WIZARD}
	,
	{"BTSHOWWSPECS_REF", fun_btshowwspecs_ref, 2, 0, CA_WIZARD}
	,
	{"BTSTORES", fun_btstores, 0, FN_VARARGS, CA_WIZARD}
	,
	{"BTTECHLIST", fun_bttechlist, 1, 0, CA_WIZARD}
	,
	{"BTTECHLIST_REF", fun_bttechlist_ref, 1, 0, CA_WIZARD}
	,
	{"BTTECHSTATUS", fun_bttechstatus, 1, 0, CA_WIZARD}
	,
	{"BTTECHTIME", fun_bttechtime, 0, 0, CA_WIZARD}
	,
	{"BTTHRESHOLD", fun_btthreshold, 1, 0, CA_WIZARD}
	,
	{"BTTICWEAPS", fun_btticweaps, 2, 0, CA_WIZARD}
	,
	{"BTUNDERREPAIR", fun_btunderrepair, 1, 0, CA_BUILDER}
	,
	{"BTUNITFIXABLE", fun_btunitfixable, 1, 0, CA_WIZARD}
	,
	{"BTWEAPONSTATUS", fun_btweaponstatus, 0, FN_VARARGS, CA_WIZARD}
	,
	{"BTWEAPONSTATUS_REF", fun_btweaponstatus_ref, 0, FN_VARARGS, CA_WIZARD}
	,
	{"BTWEAPSTAT", fun_btweapstat, 2, 0, CA_WIZARD}
	,

	{"CAPSTR", fun_capstr, -1, 0, CA_PUBLIC}
	,
	{"CASE", fun_case, 0, FN_VARARGS | FN_NO_EVAL,
	 CA_PUBLIC}
	,
	{"CAT", fun_cat, 0, FN_VARARGS, CA_PUBLIC}
	,
	{"CEIL", fun_ceil, 1, 0, CA_PUBLIC}
	,
	{"CENTER", fun_center, 0, FN_VARARGS, CA_PUBLIC}
	,
	{"CHILDREN", fun_children, 1, 0, CA_PUBLIC}
	,
	{"COBJ", fun_cobj, 1, 0, CA_PUBLIC}
	,
	{"COLORPAIRS", fun_colorpairs, 1, 0, CA_PUBLIC}
	,
	{"COLUMNS", fun_columns, 0, FN_VARARGS, CA_PUBLIC}
	,
	{"COMP", fun_comp, 2, 0, CA_PUBLIC}
	,
	{"CON", fun_con, 1, 0, CA_PUBLIC}
	,
	{"CONFIG", fun_config, 1, 0, CA_WIZARD}
	,
	{"CONN", fun_conn, 1, 0, CA_PUBLIC}
	,
	{"CONNRECORD", fun_connrecord, 0, 0, CA_PUBLIC}
	,
	{"CONTROLS", fun_controls, 2, 0, CA_PUBLIC}
	,
	{"CONVSECS", fun_convsecs, 1, 0, CA_PUBLIC}
	,
	{"CONVTIME", fun_convtime, 1, 0, CA_PUBLIC}
	,
	{"CONVUPTIME", fun_convuptime, 1, 0, CA_PUBLIC}
	,
	{"COS", fun_cos, 1, 0, CA_PUBLIC}
	,
	{"CREATE", fun_create, 0, FN_VARARGS, CA_PUBLIC}
	,
	{"CWHO", fun_cwho, 1, 0, CA_PUBLIC}
	,
	{"CLIST", fun_clist, 1, 0, CA_PUBLIC}
	,
	{"CEMIT", fun_cemit, 2, 0, CA_PUBLIC}
	,
	{"DEC", fun_dec, 1, 0, CA_PUBLIC}
	,
	{"DECRYPT", fun_decrypt, 2, 0, CA_PUBLIC}
	,
	{"DEFAULT", fun_default, 2, FN_NO_EVAL, CA_PUBLIC}
	,
	{"DELETE", fun_delete, 3, 0, CA_PUBLIC}
	,
	{"DIGITTIME", fun_digittime, 1, 0, CA_PUBLIC}
	,
	{"DIE", fun_die, 2, 0, CA_PUBLIC}
	,
	{"DIST2D", fun_dist2d, 4, 0, CA_PUBLIC}
	,
	{"DIST3D", fun_dist3d, 6, 0, CA_PUBLIC}
	,
	{"DIV", fun_div, 2, 0, CA_PUBLIC}
	,
	{"DOING", fun_doing, 1, 0, CA_WIZARD}
	,
	{"E", fun_e, 0, 0, CA_PUBLIC}
	,
	{"EDEFAULT", fun_edefault, 2, FN_NO_EVAL, CA_PUBLIC}
	,
	{"EDIT", fun_edit, 3, 0, CA_PUBLIC}
	,
	{"ELEMENTS", fun_elements, 0, FN_VARARGS, CA_PUBLIC}
	,
	{"ELOCK", fun_elock, 2, 0, CA_PUBLIC}
	,
	{"EMPTY", fun_empty, 0, FN_VARARGS, CA_PUBLIC}
	,
	{"ENCRYPT", fun_encrypt, 2, 0, CA_PUBLIC}
	,
	{"EQ", fun_eq, 2, 0, CA_PUBLIC}
	,
	{"ESCAPE", fun_escape, -1, 0, CA_PUBLIC}
	,
	{"EXIT", fun_exit, 1, 0, CA_PUBLIC}
	,
	{"EXP", fun_exp, 1, 0, CA_PUBLIC}
	,
	{"EXTRACT", fun_extract, 0, FN_VARARGS, CA_PUBLIC}
	,
	{"EVAL", fun_eval, 0, FN_VARARGS, CA_PUBLIC}
	,
	{"FDIV", fun_fdiv, 2, 0, CA_PUBLIC}
	,
	{"FILTER", fun_filter, 0, FN_VARARGS, CA_PUBLIC}
	,
	{"FINDABLE", fun_findable, 2, 0, CA_PUBLIC}
	,
	{"FIRST", fun_first, 0, FN_VARARGS, CA_PUBLIC}
	,
	{"FLAGS", fun_flags, 1, 0, CA_PUBLIC}
	,
	{"FLOOR", fun_floor, 1, 0, CA_PUBLIC}
	,
	{"FOLD", fun_fold, 0, FN_VARARGS, CA_PUBLIC}
	,
	{"FOREACH", fun_foreach, 0, FN_VARARGS, CA_PUBLIC}
	,
	{"FULLNAME", fun_fullname, 1, 0, CA_PUBLIC}
	,
	{"GET", fun_get, 1, 0, CA_PUBLIC}
	,
	{"GET_EVAL", fun_get_eval, 1, 0, CA_PUBLIC}
	,
	{"GRAB", fun_grab, 0, FN_VARARGS, CA_PUBLIC}
	,
	{"GRABALL", fun_graball, 0, FN_VARARGS, CA_PUBLIC}
	,
	{"GREP", fun_grep, 3, 0, CA_PUBLIC}
	,
	{"GREPI", fun_grepi, 3, 0, CA_PUBLIC}
	,
	{"GT", fun_gt, 2, 0, CA_PUBLIC}
	,
	{"GTE", fun_gte, 2, 0, CA_PUBLIC}
	,
	{"HASATTR", fun_hasattr, 2, 0, CA_PUBLIC}
	,
	{"HASATTRP", fun_hasattrp, 2, 0, CA_PUBLIC}
	,
	{"HASFLAG", fun_hasflag, 2, 0, CA_PUBLIC}
	,
	{"HASPOWER", fun_haspower, 2, 0, CA_PUBLIC}
	,
	{"HASTYPE", fun_hastype, 2, 0, CA_PUBLIC}
	,
	{"HOME", fun_home, 1, 0, CA_PUBLIC}
	,
	{"IDLE", fun_idle, 1, 0, CA_PUBLIC}
	,
	{"IFELSE", fun_ifelse, 3, FN_NO_EVAL, CA_PUBLIC}
	,
	{"INC", fun_inc, 1, 0, CA_PUBLIC}
	,
	{"INDEX", fun_index, 4, 0, CA_PUBLIC}
	,
	{"INSERT", fun_insert, 0, FN_VARARGS, CA_PUBLIC}
	,
	{"INZONE", fun_inzone, 1, 0, CA_PUBLIC}
	,
	{"ISDBREF", fun_isdbref, 1, 0, CA_PUBLIC}
	,
	{"ISNUM", fun_isnum, 1, 0, CA_PUBLIC}
	,
	{"ISWORD", fun_isword, 1, 0, CA_PUBLIC}
	,
	{"ITEMS", fun_items, 0, FN_VARARGS, CA_PUBLIC}
	,
	{"ITER", fun_iter, 0, FN_VARARGS | FN_NO_EVAL,
	 CA_PUBLIC}
	,
	{"LAST", fun_last, 0, FN_VARARGS, CA_PUBLIC}
	,
	{"LATTR", fun_lattr, 1, 0, CA_PUBLIC}
	,
	{"LCON", fun_lcon, 1, 0, CA_PUBLIC}
	,
	{"LCSTR", fun_lcstr, -1, 0, CA_PUBLIC}
	,
	{"LDELETE", fun_ldelete, 0, FN_VARARGS, CA_PUBLIC}
	,
	{"LEXITS", fun_lexits, 1, 0, CA_PUBLIC}
	,
	{"LPARENT", fun_lparent, 1, 0, CA_PUBLIC}
	,
	{"LIST", fun_list, 0, FN_VARARGS | FN_NO_EVAL,
	 CA_PUBLIC}
	,
	{"LIT", fun_lit, 1, FN_NO_EVAL, CA_PUBLIC}
	,
	{"LJUST", fun_ljust, 0, FN_VARARGS, CA_PUBLIC}
	,
	{"LINK", fun_link, 2, 0, CA_PUBLIC}
	,
	{"LN", fun_ln, 1, 0, CA_PUBLIC}
	,
	{"LNUM", fun_lnum, 0, FN_VARARGS, CA_PUBLIC}
	,
	{"LOC", fun_loc, 1, 0, CA_BUILDER}
	,
	{"LOCATE", fun_locate, 3, 0, CA_BUILDER}
	,
	{"LOCK", fun_lock, 1, 0, CA_PUBLIC}
	,
	{"LOG", fun_log, 1, 0, CA_PUBLIC}
	,
#ifdef ARBITRARY_LOGFILES
	{"LOGF", fun_logf, 2, 0, CA_WIZARD}
	,
#endif
	{"LSTACK", fun_lstack, 0, FN_VARARGS, CA_PUBLIC}
	,
	{"LT", fun_lt, 2, 0, CA_PUBLIC}
	,
	{"LTE", fun_lte, 2, 0, CA_PUBLIC}
	,
	{"LWHO", fun_lwho, 0, 0, CA_BUILDER}
	,
	{"MAIL", fun_mail, 0, FN_VARARGS, CA_PUBLIC}
	,
	{"MAILFROM", fun_mailfrom, 0, FN_VARARGS, CA_PUBLIC}
	,
	{"MAP", fun_map, 0, FN_VARARGS, CA_PUBLIC}
	,
	{"MATCH", fun_match, 0, FN_VARARGS, CA_PUBLIC}
	,
	{"MATCHALL", fun_matchall, 0, FN_VARARGS, CA_PUBLIC}
	,
	{"MAX", fun_max, 0, FN_VARARGS, CA_PUBLIC}
	,
	{"MEMBER", fun_member, 0, FN_VARARGS, CA_PUBLIC}
	,
	{"MERGE", fun_merge, 3, 0, CA_PUBLIC}
	,
	{"MID", fun_mid, 3, 0, CA_PUBLIC}
	,
	{"MIN", fun_min, 0, FN_VARARGS, CA_PUBLIC}
	,
	{"MIX", fun_mix, 0, FN_VARARGS, CA_PUBLIC}
	,
	{"MOD", fun_mod, 2, 0, CA_PUBLIC}
	,
	{"MONEY", fun_money, 1, 0, CA_PUBLIC}
	,
	{"MUDNAME", fun_mudname, 0, 0, CA_PUBLIC}
	,
	{"MUL", fun_mul, 0, FN_VARARGS, CA_PUBLIC}
	,
	{"MUNGE", fun_munge, 0, FN_VARARGS, CA_PUBLIC}
	,
	{"NAME", fun_name, 1, 0, CA_PUBLIC}
	,
	{"NEARBY", fun_nearby, 2, 0, CA_PUBLIC}
	,
	{"NEQ", fun_neq, 2, 0, CA_PUBLIC}
	,
	{"NEXT", fun_next, 1, 0, CA_PUBLIC}
	,
	{"NOT", fun_not, 1, 0, CA_PUBLIC}
	,
	{"NUM", fun_num, 1, 0, CA_PUBLIC}
	,
	{"OBJ", fun_obj, 1, 0, CA_PUBLIC}
	,
	{"OBJEVAL", fun_objeval, 2, FN_NO_EVAL, CA_PUBLIC}
	,
	{"OBJMEM", fun_objmem, 1, 0, CA_PUBLIC}
	,
	{"OR", fun_or, 0, FN_VARARGS, CA_PUBLIC}
	,
	{"ORFLAGS", fun_orflags, 2, 0, CA_PUBLIC}
	,
	{"OWNER", fun_owner, 1, 0, CA_PUBLIC}
	,
	{"PAIRS", fun_pairs, 1, 0, CA_PUBLIC}
	,
	{"PARENT", fun_parent, 1, 0, CA_PUBLIC}
	,
	{"PARSE", fun_parse, 0, FN_VARARGS | FN_NO_EVAL,
	 CA_PUBLIC}
	,
	{"PEEK", fun_peek, 0, FN_VARARGS, CA_PUBLIC}
	,
	{"PEMIT", fun_pemit, 2, 0, CA_PUBLIC}
	,
	{"PI", fun_pi, 0, 0, CA_PUBLIC}
	,
	{"PLAYMEM", fun_playmem, 1, 0, CA_PUBLIC}
	,
	{"PMATCH", fun_pmatch, 1, 0, CA_PUBLIC}
	,
	{"POLL", fun_poll, 0, 0, CA_WIZARD}
	,
	{"POP", fun_pop, 0, FN_VARARGS, CA_PUBLIC}
	,
	{"PORTS", fun_ports, 1, 0, CA_PUBLIC}
	,
	{"POS", fun_pos, 2, 0, CA_PUBLIC}
	,
	{"POSS", fun_poss, 1, 0, CA_PUBLIC}
	,
	{"POWER", fun_power, 2, 0, CA_PUBLIC}
	,
	{"PUSH", fun_push, 0, FN_VARARGS, CA_PUBLIC}
	,
#ifdef USE_PYTHON
	{"PYTHON", fun_python, 1, FN_NO_EVAL, CA_WIZARD}
	,
	{"PYTHONCALL", fun_pythoncall, 0, FN_VARARGS, CA_WIZARD}
	,
#endif
	{"R", fun_r, 1, 0, CA_PUBLIC}
	,
	{"RAND", fun_rand, 1, 0, CA_PUBLIC}
	,
	{"REGMATCH", fun_regmatch, 0, FN_VARARGS, CA_PUBLIC}
	,
	{"REMOVE", fun_remove, 0, FN_VARARGS, CA_PUBLIC}
	,
	{"REPEAT", fun_repeat, 2, 0, CA_PUBLIC}
	,
	{"REPLACE", fun_replace, 0, FN_VARARGS, CA_PUBLIC}
	,
	{"REST", fun_rest, 0, FN_VARARGS, CA_PUBLIC}
	,
	{"REVERSE", fun_reverse, -1, 0, CA_PUBLIC}
	,
	{"REVWORDS", fun_revwords, 0, FN_VARARGS, CA_PUBLIC}
	,
	{"RJUST", fun_rjust, 0, FN_VARARGS, CA_PUBLIC}
	,
	{"RLOC", fun_rloc, 2, 0, CA_PUBLIC}
	,
	{"ROOM", fun_room, 1, 0, CA_PUBLIC}
	,
	{"ROUND", fun_round, 2, 0, CA_PUBLIC}
	,
	{"S", fun_s, -1, 0, CA_PUBLIC}
	,
	{"SCRAMBLE", fun_scramble, 1, 0, CA_PUBLIC}
	,
	{"SEARCH", fun_search, -1, 0, CA_PUBLIC}
	,
	{"SECS", fun_secs, 0, 0, CA_PUBLIC}
	,
	{"SECURE", fun_secure, -1, 0, CA_PUBLIC}
	,
	{"SET", fun_set, 2, 0, CA_PUBLIC}
	,
	{"SETDIFF", fun_setdiff, 0, FN_VARARGS, CA_PUBLIC}
	,
	{"SETINTER", fun_setinter, 0, FN_VARARGS, CA_PUBLIC}
	,
	{"SETLOCK", fun_setlock, 3, 0, CA_WIZARD}
	,
	{"SETQ", fun_setq, 2, 0, CA_PUBLIC}
	,
	{"SETR", fun_setr, 2, 0, CA_PUBLIC}
	,
	{"SETUNION", fun_setunion, 0, FN_VARARGS, CA_PUBLIC}
	,
	{"SHL", fun_shl, 2, 0, CA_PUBLIC}
	,
	{"SHR", fun_shr, 2, 0, CA_PUBLIC}
	,
	{"SHUFFLE", fun_shuffle, 0, FN_VARARGS, CA_PUBLIC}
	,
	{"SIGN", fun_sign, 1, 0, CA_PUBLIC}
	,
	{"SIN", fun_sin, 1, 0, CA_PUBLIC}
	,
	{"SORT", fun_sort, 0, FN_VARARGS, CA_PUBLIC}
	,
	{"SORTBY", fun_sortby, 0, FN_VARARGS, CA_PUBLIC}
	,
	{"SPACE", fun_space, 1, 0, CA_PUBLIC}
	,
	{"SPLICE", fun_splice, 0, FN_VARARGS, CA_PUBLIC}
	,
	{"SQRT", fun_sqrt, 1, 0, CA_PUBLIC}
	,
	{"SQUISH", fun_squish, 1, 0, CA_PUBLIC}
	,
	{"STARTSECS", fun_startsecs, 0, 0, CA_PUBLIC}
	,
	{"STARTTIME", fun_starttime, 0, 0, CA_PUBLIC}
	,
	{"STATS", fun_stats, 1, 0, CA_PUBLIC}
	,
	{"STRCAT", fun_strcat, 0, FN_VARARGS, CA_PUBLIC}
	,
	{"STRIPANSI", fun_stripansi, 1, 0, CA_PUBLIC}
	,
	{"STRLEN", fun_strlen, -1, 0, CA_PUBLIC}
	,
	{"STRMATCH", fun_strmatch, 2, 0, CA_PUBLIC}
	,
	{"STRTRUNC", fun_strtrunc, 2, 0, CA_PUBLIC}
	,
	{"SUB", fun_sub, 2, 0, CA_PUBLIC}
	,
	{"SUBEVAL", fun_subeval, 1, 0, CA_PUBLIC}
	,
	{"SUBJ", fun_subj, 1, 0, CA_PUBLIC}
	,
	{"SWITCH", fun_switch, 0, FN_VARARGS | FN_NO_EVAL,
	 CA_PUBLIC}
	,
	{"TAN", fun_tan, 1, 0, CA_PUBLIC}
	,
	{"T", fun_t, 1, 0, CA_PUBLIC}
	,
	{"TEL", fun_tel, 2, 0, CA_PUBLIC}
	,
	{"TIME", fun_time, 0, 0, CA_PUBLIC}
	,
	{"TRANSLATE", fun_translate, 2, 0, CA_PUBLIC}
	,
	{"TRIM", fun_trim, 0, FN_VARARGS, CA_PUBLIC}
	,
	{"TRUE", fun_t, 1, 0, CA_PUBLIC}
	,
	{"TRUNC", fun_trunc, 1, 0, CA_PUBLIC}
	,
	{"TYPE", fun_type, 1, 0, CA_PUBLIC}
	,
	{"U", fun_u, 0, FN_VARARGS, CA_PUBLIC}
	,
	{"UCSTR", fun_ucstr, -1, 0, CA_PUBLIC}
	,
	{"UDEFAULT", fun_udefault, 0, FN_VARARGS | FN_NO_EVAL,
	 CA_PUBLIC}
	,
	{"ULOCAL", fun_ulocal, 0, FN_VARARGS, CA_PUBLIC}
	,
	{"V", fun_v, 1, 0, CA_PUBLIC}
	,
	{"VADD", fun_vadd, 0, FN_VARARGS, CA_PUBLIC}
	,
	{"VALID", fun_valid, 2, FN_VARARGS, CA_PUBLIC}
	,
	{"VDIM", fun_vdim, 0, FN_VARARGS, CA_PUBLIC}
	,
	{"VERSION", fun_version, 0, 0, CA_PUBLIC}
	,
	{"VISIBLE", fun_visible, 2, 0, CA_PUBLIC}
	,
	{"VMAG", fun_vmag, 0, FN_VARARGS, CA_PUBLIC}
	,
	{"VMUL", fun_vmul, 0, FN_VARARGS, CA_PUBLIC}
	,
	{"VSUB", fun_vsub, 0, FN_VARARGS, CA_PUBLIC}
	,
	{"VUNIT", fun_vunit, 0, FN_VARARGS, CA_PUBLIC}
	,
	{"WHERE", fun_where, 1, 0, CA_PUBLIC}
	,
	{"WORDPOS", fun_wordpos, 0, FN_VARARGS, CA_PUBLIC}
	,
	{"WORDS", fun_words, 0, FN_VARARGS, CA_PUBLIC}
	,
	{"XGET", fun_xget, 2, 0, CA_PUBLIC}
	,
	{"XOR", fun_xor, 0, FN_VARARGS, CA_PUBLIC}
	,
	{"ZFUN", fun_zfun, 0, FN_VARARGS, CA_PUBLIC}
	,
	{"ZONE", fun_zone, 1, 0, CA_PUBLIC}
	,
	{"ZPLAYERS", fun_zplayers, 1, 0, CA_PUBLIC}
	,
	{"ZWHO", fun_zwho, 1, 0, CA_PUBLIC}
	,
	{NULL, NULL, 0, 0, 0}
};

/* *INDENT-ON* */

void init_functab()
{
	FUN *fp;
	char *buff, *cp, *dp;

	buff = alloc_sbuf("init_functab");
	hashinit(&mudstate.func_htab, 100 * HASH_FACTOR);
	for(fp = flist; fp->name; fp++) {
		cp = (char *) fp->name;
		dp = buff;
		while (*cp) {
			*dp = ToLower(*cp);
			cp++;
			dp++;
		}
		*dp = '\0';
		hashadd(buff, (int *) fp, &mudstate.func_htab);
	}
	free_sbuf(buff);
	ufun_head = NULL;
	hashinit(&mudstate.ufunc_htab, 11);
}

void do_function(dbref player, dbref cause, int key, char *fname,
				 char *target)
{
	UFUN *ufp, *ufp2;
	ATTR *ap;
	char *np, *bp;
	int atr, aflags;
	dbref obj, aowner;

	/*
	 * Make a local uppercase copy of the function name 
	 */

	bp = np = alloc_sbuf("add_user_func");
	safe_sb_str(fname, np, &bp);
	*bp = '\0';
	for(bp = np; *bp; bp++)
		*bp = ToLower(*bp);

	/*
	 * Verify that the function doesn't exist in the builtin table 
	 */

	if(hashfind(np, &mudstate.func_htab) != NULL) {
		notify_quiet(player,
					 "Function already defined in builtin function table.");
		free_sbuf(np);
		return;
	}
	/*
	 * Make sure the target object exists 
	 */

	if(!parse_attrib(player, target, &obj, &atr)) {
		notify_quiet(player, "I don't see that here.");
		free_sbuf(np);
		return;
	}
	/*
	 * Make sure the attribute exists 
	 */

	if(atr == NOTHING) {
		notify_quiet(player, "No such attribute.");
		free_sbuf(np);
		return;
	}
	/*
	 * Make sure attribute is readably by me 
	 */

	ap = atr_num(atr);
	if(!ap) {
		notify_quiet(player, "No such attribute.");
		free_sbuf(np);
		return;
	}
	atr_get_info(obj, atr, &aowner, &aflags);
	if(!See_attr(player, obj, ap, aowner, aflags)) {
		notify_quiet(player, "Permission denied.");
		free_sbuf(np);
		return;
	}
	/*
	 * Privileged functions require you control the obj.  
	 */

	if((key & FN_PRIV) && !Controls(player, obj)) {
		notify_quiet(player, "Permission denied.");
		free_sbuf(np);
		return;
	}
	/*
	 * See if function already exists.  If so, redefine it 
	 */

	ufp = (UFUN *) hashfind(np, &mudstate.ufunc_htab);

	if(!ufp) {
		ufp = (UFUN *) malloc(sizeof(UFUN));
		ufp->name = strsave(np);
		for(bp = (char *) ufp->name; *bp; bp++)
			*bp = ToUpper(*bp);
		ufp->obj = obj;
		ufp->atr = atr;
		ufp->perms = CA_PUBLIC;
		ufp->next = NULL;
		if(!ufun_head) {
			ufun_head = ufp;
		} else {
			for(ufp2 = ufun_head; ufp2->next; ufp2 = ufp2->next);
			ufp2->next = ufp;
		}
		hashadd(np, (int *) ufp, &mudstate.ufunc_htab);
	}
	ufp->obj = obj;
	ufp->atr = atr;
	ufp->flags = key;
	free_sbuf(np);
	if(!Quiet(player))
		notify_quiet(player, tprintf("Function %s defined.", fname));
}

/*
 * ---------------------------------------------------------------------------
 * * list_functable: List available functions.
 */

void list_functable(dbref player)
{
	FUN *fp;
	UFUN *ufp;
	char *buf, *bp, *cp;

	buf = alloc_lbuf("list_functable");
	bp = buf;

	/* Hardcoded Functions */
	for(cp = (char *) "Functions:"; *cp; cp++)
		*bp++ = *cp;
	for(fp = flist; fp->name; fp++) {
		if(check_access(player, fp->perms)) {
			*bp++ = ' ';
			for(cp = (char *) (fp->name); *cp; cp++)
				*bp++ = *cp;
		}
	}
	*bp = '\0';
	notify(player, buf);

	/* User-Defined functions (via @function) */
	bp = buf;
	safe_str("User-Functions:", buf, &bp);

	for(ufp = ufun_head; ufp; ufp = ufp->next) {
		if(check_access(player, ufp->perms)) {
			*bp++ = ' ';
			for(cp = (char *) (ufp->name); *cp; cp++)
				*bp++ = *cp;
		}
	}
	*bp = '\0';
	notify(player, buf);
	free_lbuf(buf);
}

/*
 * ---------------------------------------------------------------------------
 * * cf_func_access: set access on functions
 */

int cf_func_access(int *vp, char *str, long extra, dbref player, char *cmd)
{
	FUN *fp;
	UFUN *ufp;
	char *ap;

	for(ap = str; *ap && !isspace(*ap); ap++);
	if(*ap)
		*ap++ = '\0';

	for(fp = flist; fp->name; fp++) {
		if(!string_compare(fp->name, str)) {
			return (cf_modify_bits(&fp->perms, ap, extra, player, cmd));
		}
	}
	for(ufp = ufun_head; ufp; ufp = ufp->next) {
		if(!string_compare(ufp->name, str)) {
			return (cf_modify_bits(&ufp->perms, ap, extra, player, cmd));
		}
	}
	cf_log_notfound(player, cmd, "Function", str);
	return -1;
}