/* functions.c - MUSH function handlers */
#include "autoconf.h"
#include "copyright.h"
#ifndef lint
static char RCSid[] = "$Id: functions.c,v 1.12 1995/03/29 02:55:13 ambar Exp $";
USE(RCSid);
#endif
#include <limits.h>
#include <math.h>
#include "externs.h"
#include "flags.h"
#include "attrs.h"
#include "match.h"
#include "command.h"
#include "functions.h"
#include "misc.h"
#include "alloc.h"
#include "ansi.h"
#ifdef FLOATING_POINTS
#ifndef linux /* linux defines atof as a macro */
double atof();
#endif /* ! linux */
#define aton atof
typedef double NVAL;
#else
#define aton atoi
typedef int NVAL;
#endif /* FLOATING_POINTS */
UFUN *ufun_head;
extern void FDECL(cf_log_notfound, (dbref player, char *cmd,
const char *thingname, char *thing));
extern void FDECL(make_portlist,
(dbref player, dbref target, char *buff));
#ifdef TCL_INTERP_SUPPORT
extern void FDECL(fun_tclclear, (char *, dbref, dbref, char **, int,
char **, int));
extern void FDECL(fun_tcleval, (char *, dbref, dbref, char **, int,
char **, int));
extern void FDECL(fun_tclparams, (char *, dbref, dbref, char **, int,
char **, int));
extern void FDECL(fun_tclregs, (char *, dbref, dbref, char **, int,
char **, int));
#endif /* TCL_INTERP_SUPPORT */
/* This is the prototype for functions */
#define FUNCTION(x) \
static void x(buff, player, cause, fargs, nfargs, cargs, ncargs) \
char *buff; \
dbref player, cause; \
char *fargs[], *cargs[]; \
int nfargs, ncargs;
/* -------------------------------------------------------------------------
* Macros for functions that take variable arguments, with or without
* a delimiter.
*
* Call varargs_preamble("FUNCTION", max_args) for functions which
* take either max_args - 1 args, or, with a delimiter, max_args args.
*
* Call mvarargs_preamble("FUNCTION", min_args, max_args) if there can
* be more variable arguments than just the delimiter.
*
* Call evarargs_preamble("FUNCTION", min_args, max_args) if the delimiters
* need to be evaluated.
*
* Call svarargs_preamble("FUNCTION", max_args) if the second to last and
* last arguments are delimiters.
*
* Call xvarargs_preamble("FUNCTION", min_args, max_args) if this is varargs
* but does not involve a delimiter.
*/
#define xvarargs_preamble(xname,xminargs,xnargs) \
if (!fn_range_check(xname, nfargs, xminargs, xnargs, buff)) \
return;
#define varargs_preamble(xname,xnargs) \
if (!fn_range_check(xname, nfargs, xnargs-1, xnargs, buff)) \
return; \
if (!delim_check(fargs, nfargs, xnargs, &sep, buff, 0, \
player, cause, cargs, ncargs)) \
return;
#define mvarargs_preamble(xname,xminargs,xnargs) \
if (!fn_range_check(xname, nfargs, xminargs, xnargs, buff)) \
return; \
if (!delim_check(fargs, nfargs, xnargs, &sep, buff, 0, \
player, cause, cargs, ncargs)) \
return;
#define evarargs_preamble(xname, xminargs, xnargs) \
if (!fn_range_check(xname, nfargs, xminargs, xnargs, buff)) \
return; \
if (!delim_check(fargs, nfargs, xnargs - 1, &sep, buff, 1, \
player, cause, cargs, ncargs)) \
return; \
if (!delim_check(fargs, nfargs, xnargs, &osep, buff, 1, \
player, cause, cargs, ncargs)) \
return;
#define svarargs_preamble(xname,xnargs) \
if (!fn_range_check(xname, nfargs, xnargs-2, xnargs, buff)) \
return; \
if (!delim_check(fargs, nfargs, xnargs-1, &sep, buff, 0, \
player, cause, cargs, ncargs)) \
return; \
if (nfargs < xnargs) \
osep = sep; \
else if (!delim_check(fargs, nfargs, xnargs, &osep, buff, 0, \
player, cause, cargs, ncargs)) \
return;
/* --------------------------------------------------------------------------
* Auxiliary functions for stacks.
*/
typedef struct object_stack STACK;
struct object_stack {
char *data;
STACK *next;
};
#define stack_get(x) ((STACK *) nhashfind(x, &mudstate.objstack_htab))
#define stack_object(p,x) \
x = match_thing(p, fargs[0]); \
if (!Good_obj(x)) { \
return; \
} \
if (!Controls(p, x)) { \
notify_quiet(p, "Permission denied."); \
return; \
}
/* --------------------------------------------------------------------------
* Various utility functions.
*/
/* Trim off leading and trailing spaces if the separator char is a space */
static char *
trim_space_sep(str, sep)
char *str, sep;
{
char *p;
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 */
static char *
next_token(str, sep)
char *str, 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.
*/
static char *
split_token(sp, sep)
char **sp, 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(player, name)
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(ptrs, nitems)
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(fargs, nfargs, type_pos, ptrs, nitems)
char *fargs[], *ptrs[];
int nfargs, nitems, type_pos;
{
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);
}
static int
list2arr(arr, maxlen, list, sep)
char *arr[], *list, sep;
int maxlen;
{
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;
}
static void
arr2list(arr, alen, list, sep)
char *arr[], *list, sep;
int alen;
{
char *p;
int i;
p = list;
for (i = 0; i < alen; i++) {
safe_str(arr[i], list, &p);
safe_chr(sep, list, &p);
}
if (p != list)
p--;
*p = '\0';
}
static int
dbnum(dbr)
char *dbr;
{
if ((*dbr != '#') || (strlen(dbr) < 2))
return 0;
else
return atoi(dbr + 1);
}
/* ---------------------------------------------------------------------------
* nearby_or_control: Check if player is near or controls thing
*/
int
nearby_or_control(player, thing)
dbref player, thing;
{
if (!Good_obj(player) || !Good_obj(thing))
return 0;
if (Controls(player, thing))
return 1;
if (!nearby(player, thing))
return 0;
return 1;
}
/* ---------------------------------------------------------------------------
* fval: copy the floating point value into a buffer and make it presentable.
* alternatively, if we're not using floating points, then we just
* print the integer.
*/
#ifdef FLOATING_POINTS
static void fval(buff, result)
char *buff;
double result;
{
char *p;
sprintf(buff, "%.6f", result); /* get double val into buffer */
/* remove useless trailing 0's */
if ((p = (char *) rindex(buff, '0')) == NULL)
return;
else if (*(p + 1) == '\0') {
while (*p == '0')
*p-- = '\0';
}
p = (char *) rindex(buff, '.'); /* take care of dangling '.' */
if (*(p + 1) == '\0')
*p = '\0';
}
#else
#define fval(b,n) ltos(b, n)
#endif /* FLOATING_POINTS */
/* ---------------------------------------------------------------------------
* fn_range_check: Check # of args to a function with an optional argument
* for validity.
*/
static int
fn_range_check(fname, nfargs, minargs, maxargs, result)
const char *fname;
char *result;
int nfargs, minargs, maxargs;
{
if ((nfargs >= minargs) && (nfargs <= maxargs))
return 1;
if (maxargs == (minargs + 1))
sprintf(result, "#-1 FUNCTION (%s) EXPECTS %d OR %d ARGUMENTS",
fname, minargs, maxargs);
else
sprintf(result,
"#-1 FUNCTION (%s) EXPECTS BETWEEN %d AND %d ARGUMENTS",
fname, minargs, maxargs);
return 0;
}
/* ---------------------------------------------------------------------------
* delim_check: obtain delimiter
*/
static int
delim_check(fargs, nfargs, sep_arg, sep, buff, eval, player, cause,
cargs, ncargs)
char *fargs[], *cargs[], *sep, *buff;
int nfargs, ncargs, sep_arg, eval;
dbref player, cause;
{
char *tstr;
int tlen;
if (nfargs >= sep_arg) {
tlen = strlen(fargs[sep_arg - 1]);
if (tlen <= 1)
eval = 0;
if (eval) {
tstr = exec(player, cause, EV_EVAL | EV_FCHECK,
fargs[sep_arg - 1], cargs, ncargs);
tlen = strlen(tstr);
*sep = *tstr;
free_lbuf(tstr);
}
if (tlen == 0) {
*sep = ' ';
} else if (tlen != 1) {
strcpy(buff,
"#-1 SEPARATOR MUST BE ONE CHARACTER");
return 0;
} else if (!eval) {
*sep = *fargs[sep_arg - 1];
}
} else {
*sep = ' ';
}
return 1;
}
/* ---------------------------------------------------------------------------
* fun_words: Returns number of words in a string.
* Added 1/28/91 Philip D. Wasson
*/
static int
countwords(str, sep)
char *str, 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;
}
FUNCTION(fun_words)
{
char sep;
if (nfargs == 0) {
strcpy(buff, "0");
return;
}
varargs_preamble("WORDS", 2);
ltos(buff, countwords(fargs[0], sep));
}
/* ---------------------------------------------------------------------------
* fun_flags: Returns the flags on an object.
* Because @switch is case-insensitive, not quite as useful as it could be.
*/
FUNCTION(fun_flags)
{
dbref it;
char *buff2;
it = match_thing(player, fargs[0]);
if ((it != NOTHING) &&
(mudconf.pub_flags || Examinable(player, it) || (it == cause))) {
buff2 = unparse_flags(player, it);
strcpy(buff, buff2);
free_sbuf(buff2);
} else
strcpy(buff, "#-1");
return;
}
/* ---------------------------------------------------------------------------
* fun_rand: Return a random number from 0 to arg1-1
*/
FUNCTION(fun_rand)
{
int num;
num = atoi(fargs[0]);
if (num < 1)
strcpy(buff, "0");
else
sprintf(buff, "%ld", (random() % num));
}
/* ---------------------------------------------------------------------------
* fun_die: Roll an N-sided die a certain number of times.
*/
FUNCTION(fun_die)
{
int n = atoi(fargs[0]);
int die = atoi(fargs[1]);
int count;
int total = 0;
if ((n == 0) || (die <= 0)) {
strcpy(buff, "0");
return;
}
if ((n < 1) || (n > 100)) {
strcpy(buff, "#-1 NUMBER OUT OF RANGE");
return;
}
for (count = 0; count < n; count++)
total += random() % die;
total += n;
ltos(buff, total);
}
/* ---------------------------------------------------------------------------
* fun_abs: Returns the absolute value of its argument.
*/
FUNCTION(fun_abs)
{
#ifdef FLOATING_POINTS
double num;
num = atof(fargs[0]);
if (num == 0.0) {
strcpy(buff, "0");
} else if (num < 0.0) {
fval(buff, -num);
} else {
fval(buff, num);
}
#else
ltos(buff, abs(atoi(fargs[0])));
#endif
}
/* ---------------------------------------------------------------------------
* fun_sign: Returns -1, 0, or 1 based on the the sign of its argument.
*/
FUNCTION(fun_sign)
{
NVAL num;
num = aton(fargs[0]);
if (num < 0)
strcpy(buff, "-1");
else if (num > 0)
strcpy(buff, "1");
else
strcpy(buff, "0");
}
/* ---------------------------------------------------------------------------
* fun_time: Returns nicely-formatted time.
*/
FUNCTION(fun_time)
{
char *temp;
temp = (char *) ctime(&mudstate.now);
temp[strlen(temp) - 1] = '\0';
strcpy(buff, temp);
}
/* ---------------------------------------------------------------------------
* fun_time: Seconds since 0:00 1/1/70
*/
FUNCTION(fun_secs)
{
ltos(buff, mudstate.now);
}
/* ---------------------------------------------------------------------------
* fun_convsecs: converts seconds to time string, based off 0:00 1/1/70
*/
FUNCTION(fun_convsecs)
{
char *temp;
time_t tt;
tt = atol(fargs[0]);
temp = (char *) ctime(&tt);
temp[strlen(temp) - 1] = '\0';
strcpy(buff, temp);
}
/* ---------------------------------------------------------------------------
* fun_convtime: 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++; \
} \
}
static int
do_convtime(str, ttm)
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);
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); /* bad length */
return 0;
}
for (i = 0; (i < 12) && string_compare(monthtab[i], p); i++);
if (i == 12) {
free_sbuf(buf); /* not found */
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;
}
}
if (ttm->tm_year > 100)
ttm->tm_year -= 1900;
free_sbuf(buf);
if (ttm->tm_year < 0)
return 0;
/* We don't whether or not it's daylight savings time. */
ttm->tm_isdst = -1;
#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
}
FUNCTION(fun_convtime)
{
struct tm *ttm;
ttm = localtime(&mudstate.now);
if (do_convtime(fargs[0], ttm))
ltos(buff, timelocal(ttm));
else
strcpy(buff, "-1");
}
/* ---------------------------------------------------------------------------
* fun_starttime: What time did this system last reboot?
*/
FUNCTION(fun_starttime)
{
char *temp;
temp = (char *) ctime(&mudstate.start_time);
temp[strlen(temp) - 1] = '\0';
strcpy(buff, temp);
}
/* ---------------------------------------------------------------------------
* fun_restarts: How many times have we restarted?
*/
FUNCTION(fun_restarts)
{
ltos(buff, mudstate.reboot_nums);
}
/* ---------------------------------------------------------------------------
* fun_restarttime: When did we last restart?
*/
FUNCTION(fun_restarttime)
{
char *temp;
temp = (char *) ctime(&mudstate.restart_time);
temp[strlen(temp) - 1] = '\0';
strcpy(buff, temp);
}
/* ---------------------------------------------------------------------------
* fun_get, fun_get_eval: Get attribute from object.
*/
static int
check_read_perms(player, thing, attr, aowner, aflags, buff)
dbref player, thing;
ATTR *attr;
int aowner, aflags;
char *buff;
{
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_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)) {
strcpy(buff, (char *) "#-1 TOO FAR AWAY TO SEE");
return 0;
} else {
return 1;
}
}
strcpy(buff, (char *) "#-1 PERMISSION DENIED");
return 0;
}
FUNCTION(fun_get)
{
dbref thing, aowner;
int attrib, free_buffer, aflags;
ATTR *attr;
char *atr_gotten;
struct boolexp *bool;
if (!parse_attrib(player, fargs[0], &thing, &attrib)) {
strcpy(buff, "#-1 NO MATCH");
return;
}
if (attrib == NOTHING) {
*buff = '\0';
return;
}
free_buffer = 1;
attr = atr_num(attrib); /* We need the attr's flags for this: */
if (!attr) {
*buff = '\0';
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))
strcpy(buff, atr_gotten);
if (free_buffer)
free_lbuf(atr_gotten);
return;
}
FUNCTION(fun_get_eval)
{
dbref thing, aowner;
int attrib, free_buffer, aflags, eval_it;
ATTR *attr;
char *atr_gotten;
struct boolexp *bool;
if (!parse_attrib(player, fargs[0], &thing, &attrib)) {
strcpy(buff, "#-1 NO MATCH");
return;
}
if (attrib == NOTHING) {
*buff = '\0';
return;
}
free_buffer = 1;
eval_it = 1;
attr = atr_num(attrib); /* We need the attr's flags for this: */
if (!attr) {
*buff = '\0';
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)) {
if (free_buffer)
free_lbuf(atr_gotten);
return;
}
strcpy(buff, atr_gotten);
if (free_buffer)
free_lbuf(atr_gotten);
if (eval_it) {
atr_gotten = exec(thing, player, EV_FIGNORE | EV_EVAL, buff,
(char **) NULL, 0);
strcpy(buff, atr_gotten);
free_lbuf(atr_gotten);
}
return;
}
/* ---------------------------------------------------------------------------
* fun_u and fun_ulocal: Call a user-defined function.
*/
static void do_ufun(buff, player, cause,
fargs, nfargs,
cargs, ncargs,
is_local)
char *buff;
dbref player, cause;
char *fargs[], *cargs[];
int nfargs, ncargs, is_local;
{
dbref aowner, thing;
int aflags, anum;
ATTR *ap;
char *atext, *result, *preserve[MAX_GLOBAL_REGS];
/* We need at least one argument */
if (nfargs < 1) {
strcpy(buff, "#-1 TOO FEW ARGUMENTS");
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) {
*buff = '\0';
return;
}
/* Use it if we can access it, otherwise return an error. */
atext = atr_pget(thing, ap->number, &aowner, &aflags);
if (!atext) {
*buff = '\0';
return;
}
if (!*atext) {
free_lbuf(atext);
*buff = '\0';
return;
}
if (!check_read_perms(player, thing, ap, aowner, aflags, buff)) {
free_lbuf(atext);
*buff = '\0';
return;
}
/* If we're evaluating locally, preserve the global registers. */
if (is_local)
save_global_regs("fun_ulocal_save", preserve);
/* Evaluate it using the rest of the passed function args */
result = exec(thing, cause, EV_FCHECK | EV_EVAL, atext,
&(fargs[1]), nfargs - 1);
free_lbuf(atext);
strcpy(buff, result);
free_lbuf(result);
/* If we're evaluating locally, restore the preserved registers. */
if (is_local)
restore_global_regs("fun_ulocal_restore", preserve);
}
FUNCTION(fun_u)
{
do_ufun(buff, player, cause, fargs, nfargs, cargs, ncargs, 0);
}
FUNCTION(fun_ulocal)
{
do_ufun(buff, player, cause, fargs, nfargs, cargs, ncargs, 1);
}
/* ---------------------------------------------------------------------------
* fun_default, fun_edefault, and fun_udefault:
* These check for the presence of an attribute. If it exists, then it
* is gotten, via the equivalent of get(), get_eval(), or u(), respectively.
* Otherwise, the default message is used.
* In the case of udefault(), the remaining arguments to the function
* are used as arguments to the u().
*/
FUNCTION(fun_default)
{
dbref thing, aowner;
int attrib, aflags;
ATTR *attr;
char *objname, *atr_gotten, *defcase;
objname = exec(player, cause, EV_EVAL | EV_STRIP | EV_FCHECK, fargs[0],
cargs, ncargs);
/* First we check to see that the attribute exists on the object.
* If so, we grab it and use it.
*/
if (objname != NULL) {
if (parse_attrib(player, objname, &thing, &attrib) &&
(attrib != NOTHING)) {
attr = atr_num(attrib);
if (attr && !(attr->flags & AF_IS_LOCK)) {
atr_gotten = atr_pget(thing, attrib, &aowner, &aflags);
if (*atr_gotten &&
check_read_perms(player, thing, attr, aowner,
aflags, buff)) {
strcpy(buff, atr_gotten);
free_lbuf(atr_gotten);
free_lbuf(objname);
return;
}
free_lbuf(atr_gotten);
}
}
free_lbuf(objname);
}
/* If we've hit this point, we've not gotten anything useful, so
* we go and evaluate the default.
*/
defcase = exec(player, cause, EV_EVAL | EV_STRIP | EV_FCHECK, fargs[1],
cargs, ncargs);
strcpy(buff, defcase);
free_lbuf(defcase);
}
FUNCTION(fun_edefault)
{
dbref thing, aowner;
int attrib, aflags;
ATTR *attr;
char *objname, *atr_gotten, *defcase;
objname = exec(player, cause, EV_EVAL | EV_STRIP | EV_FCHECK, fargs[0],
cargs, ncargs);
/* First we check to see that the attribute exists on the object.
* If so, we grab it and use it.
*/
if (objname != NULL) {
if (parse_attrib(player, objname, &thing, &attrib) &&
(attrib != NOTHING)) {
attr = atr_num(attrib);
if (attr && !(attr->flags & AF_IS_LOCK)) {
atr_gotten = atr_pget(thing, attrib, &aowner, &aflags);
if (*atr_gotten &&
check_read_perms(player, thing, attr, aowner,
aflags, buff)) {
strcpy(buff, atr_gotten);
free_lbuf(atr_gotten);
atr_gotten = exec(thing, player, EV_FIGNORE | EV_EVAL,
buff, (char **) NULL, 0);
strcpy(buff, atr_gotten);
free_lbuf(atr_gotten);
free_lbuf(objname);
return;
}
free_lbuf(atr_gotten);
}
}
free_lbuf(objname);
}
/* If we've hit this point, we've not gotten anything useful, so
* we go and evaluate the default.
*/
defcase = exec(player, cause, EV_EVAL | EV_STRIP | EV_FCHECK, fargs[1],
cargs, ncargs);
strcpy(buff, defcase);
free_lbuf(defcase);
}
FUNCTION(fun_udefault)
{
dbref thing, aowner;
int aflags, anum, i, j;
ATTR *ap;
char *objname, *atext, *result, *defcase, *xargs[NUM_ENV_VARS];
*buff = '\0';
if (nfargs < 2) /* must have at least two arguments */
return;
objname = exec(player, cause, EV_EVAL | EV_STRIP | EV_FCHECK, fargs[0],
cargs, ncargs);
/* First we check to see that the attribute exists on the object.
* If so, we grab it and use it.
*/
if (objname != NULL) {
if (parse_attrib(player, objname, &thing, &anum)) {
if ((anum == NOTHING) || (!Good_obj(thing)))
ap = NULL;
else
ap = atr_num(anum);
} else {
thing = player;
ap = atr_str(objname);
}
if (ap) {
atext = atr_pget(thing, ap->number, &aowner, &aflags);
if (atext) {
if (*atext &&
check_read_perms(player, thing, ap, aowner, aflags,
buff)) {
/* Now we have a problem -- we've got to go eval
* all of those arguments to the function.
*/
for (i = 2, j = 0; j < NUM_ENV_VARS; i++, j++) {
if ((i < NUM_ENV_VARS) && fargs[i])
xargs[j] = exec(player, cause,
EV_STRIP | EV_FCHECK | EV_EVAL,
fargs[i], cargs, ncargs);
else
xargs[j] = NULL;
}
/* Now we should do the evaluation. */
result = exec(thing, cause, EV_FCHECK | EV_EVAL, atext,
xargs, nfargs - 2);
/* Then clean up after ourselves. */
for (j = 0; j < NUM_ENV_VARS; j++)
if (xargs[j])
free_lbuf(xargs[j]);
free_lbuf(atext);
strcpy(buff, result);
free_lbuf(result);
free_lbuf(objname);
return;
}
free_lbuf(atext);
}
}
free_lbuf(objname);
}
/* If we've hit this point, we've not gotten anything useful, so
* we go and evaluate the default.
*/
defcase = exec(player, cause, EV_EVAL | EV_STRIP | EV_FCHECK, fargs[1],
cargs, ncargs);
strcpy(buff, defcase);
free_lbuf(defcase);
}
/* ---------------------------------------------------------------------------
* fun_hasattr: does object X have attribute Y.
*/
FUNCTION(fun_hasattr)
{
dbref thing, aowner;
int aflags;
ATTR *attr;
char *tbuf;
thing = match_thing(player, fargs[0]);
if (thing == NOTHING) {
strcpy(buff, "#-1 NO MATCH");
return;
} else if (!Examinable(player, thing)) {
strcpy(buff, "#-1 PERMISSION DENIED");
return;
}
attr = atr_str(fargs[1]);
if (!attr) {
strcpy(buff, "0");
return;
}
atr_pget_info(thing, attr->number, &aowner, &aflags);
if (!See_attr(player, thing, attr, aowner, aflags))
strcpy(buff, "0");
else {
tbuf = atr_pget(thing, attr->number, &aowner, &aflags);
if (*tbuf)
strcpy(buff, "1");
else
strcpy(buff, "0");
free_lbuf(tbuf);
}
}
/* ---------------------------------------------------------------------------
* fun_objeval: Evaluate expression from perspective of another object.
* All args to this function are passed _unevaluated_.
*/
FUNCTION(fun_objeval)
{
char *s, *name;
dbref obj;
/* Evaluate the first argument to get the object name. */
name = exec(player, cause, EV_EVAL | EV_STRIP | EV_FCHECK, fargs[0],
cargs, ncargs);
if (name == NULL) {
*buff = '\0';
return;
}
/* In order to evaluate from something else's viewpoint, you must
* have the same owner as it, or be a wizard. Otherwise, we default
* to evaluating from our own viewpoint.
*/
if (((obj = match_thing(player, name)) == NOTHING) ||
((Owner(player) != Owner(obj)) && !Wizard(player)))
obj = player;
s = exec(obj, cause, EV_EVAL | EV_STRIP | EV_FCHECK, fargs[1],
cargs, ncargs);
if (s != NULL) {
strcpy(buff, s);
free_lbuf(s);
} else {
*buff = '\0';
}
free_lbuf(name);
}
/* ---------------------------------------------------------------------------
* fun_parent: Get parent of object.
*/
FUNCTION(fun_parent)
{
dbref it;
it = match_thing(player, fargs[0]);
if (Good_obj(it) && (Examinable(player, it) || (it == cause))) {
*buff = '#';
ltos(&buff[1], Parent(it));
} else {
strcpy(buff, "#-1");
}
return;
}
/* ---------------------------------------------------------------------------
* fun_mid: mid(foobar,2,3) returns oba
*/
FUNCTION(fun_mid)
{
int l, len;
char *bufc, *strp;
bufc = buff;
l = atoi(fargs[1]);
len = atoi(fargs[2]);
if ((l < 0) || (len < 0) || ((len + l) > LBUF_SIZE)) {
strcpy(buff, "#-1 OUT OF RANGE");
return;
}
/* Set a pointer to the beginning of buffer -- strip_ansi() returns
* a static.
*/
strp = (mudconf.ansi_colors ? strip_ansi(fargs[0]) : fargs[0]);
if (l < strlen(strp))
strcpy(buff, strp + l);
else
*buff = '\0';
buff[len] = '\0';
}
/* ---------------------------------------------------------------------------
* fun_first: Returns first word in a string
*/
FUNCTION(fun_first)
{
char *s, *first, sep;
/* If we are passed an empty arglist return a null string */
if (nfargs == 0) {
*buff = '\0';
return;
}
varargs_preamble("FIRST", 2);
s = trim_space_sep(fargs[0], sep); /* leading spaces ... */
first = split_token(&s, sep);
if (!first) {
*buff = '\0';
} else {
strcpy(buff, first);
}
}
/* ---------------------------------------------------------------------------
* fun_rest: Returns all but the first word in a string
*/
FUNCTION(fun_rest)
{
char *s, *first, sep;
/* If we are passed an empty arglist return a null string */
if (nfargs == 0) {
*buff = '\0';
return;
}
varargs_preamble("REST", 2);
s = trim_space_sep(fargs[0], sep); /* leading spaces ... */
first = split_token(&s, sep);
if (!s) {
*buff = '\0';
} else {
strcpy(buff, s);
}
}
/* ---------------------------------------------------------------------------
* fun_last: Returns last word in a string
*/
FUNCTION(fun_last)
{
char *s, *last, sep;
int len, i;
/* If we are passed an empty arglist return a null string */
if (nfargs == 0) {
*buff = '\0';
return;
}
varargs_preamble("LAST", 2);
s = trim_space_sep(fargs[0], sep); /* trim leading spaces */
/* If we're dealing with spaces, trim off the trailing stuff */
if (sep == ' ') {
len = strlen(s);
for (i = len - 1; s[i] == ' '; i--)
;
if (i + 1 <= len)
s[i + 1] = '\0';
}
last = (char *) rindex(s, sep);
if (last)
strcpy(buff, ++last);
else
strcpy(buff, s);
}
/* ---------------------------------------------------------------------------
* fun_left: Returns first n characters in a string
*/
FUNCTION(fun_left)
{
int len = atoi(fargs[1]);
if (len < 1) {
*buff = '\0';
} else {
strcpy(buff, mudconf.ansi_colors ? strip_ansi(fargs[0]) : fargs[0]);
if (len < LBUF_SIZE)
buff[len] = '\0';
}
}
/* ---------------------------------------------------------------------------
* fun_right: Returns last n characters in a string
*/
FUNCTION(fun_right)
{
char *strp;
int len = atoi(fargs[1]);
if (len < 1) {
*buff = '\0';
} else {
strp = (mudconf.ansi_colors ? strip_ansi(fargs[0]) : fargs[0]);
len = strlen(strp) - len;
if (len < 1)
strcpy(buff, strp);
else
strcpy(buff, strp + len);
}
}
/* ---------------------------------------------------------------------------
* fun_v: Function form of %-substitution
*/
FUNCTION(fun_v)
{
dbref aowner;
int aflags;
char *sbuf, *sbufc, *tbuf;
ATTR *ap;
tbuf = fargs[0];
if (isalpha(tbuf[0]) && tbuf[1]) {
/* Fetch an attribute from me. First see if it exists,
* returning a null string if it does not. */
ap = atr_str(fargs[0]);
if (!ap) {
*buff = '\0';
return;
}
/* If we can access it, return it, otherwise return a
* null string */
atr_pget_info(player, ap->number, &aowner, &aflags);
if (See_attr(player, player, ap, aowner, aflags)) {
tbuf = atr_pget(player, ap->number, &aowner, &aflags);
strcpy(buff, tbuf);
free_lbuf(tbuf);
} else {
*buff = '\0';
}
return;
}
/* Not an attribute, process as %<arg> */
sbuf = alloc_sbuf("fun_v");
sbufc = sbuf;
safe_sb_chr('%', sbuf, &sbufc);
safe_sb_str(fargs[0], sbuf, &sbufc);
tbuf = exec(player, cause, EV_FIGNORE, sbuf, cargs, ncargs);
strcpy(buff, tbuf);
free_lbuf(tbuf);
free_sbuf(sbuf);
}
/* ---------------------------------------------------------------------------
* fun_s: Force substitution to occur.
*/
FUNCTION(fun_s)
{
char *tbuf;
tbuf = exec(player, cause, EV_FIGNORE | EV_EVAL, fargs[0],
cargs, ncargs);
strcpy(buff, tbuf);
free_lbuf(tbuf);
}
/* ---------------------------------------------------------------------------
* fun_con: Returns first item in contents list of object/room
*/
FUNCTION(fun_con)
{
dbref it;
it = match_thing(player, fargs[0]);
if ((it != NOTHING) &&
(Has_contents(it)) &&
(Examinable(player, it) ||
(where_is(player) == it) ||
(it == cause))) {
*buff = '#';
ltos(&buff[1], Contents(it));
return;
}
strcpy(buff, "#-1");
return;
}
/* ---------------------------------------------------------------------------
* fun_exit: Returns first exit in exits list of room.
*/
FUNCTION(fun_exit)
{
dbref it, exam;
it = match_thing(player, fargs[0]);
if (Good_obj(it) && Has_exits(it) && Good_obj(Exits(it))) {
exam = Examinable(player, it);
if (exam || (where_is(player) == it) || (it == cause)) {
*buff = '#';
ltos(&buff[1], next_exit(player, Exits(it), exam));
return;
}
}
strcpy(buff, "#-1");
return;
}
/* ---------------------------------------------------------------------------
* fun_next: return next thing in contents or exits chain
*/
FUNCTION(fun_next)
{
dbref it, loc, exam_here;
it = match_thing(player, fargs[0]);
if (Good_obj(it)) {
loc = where_is(it);
if (loc != NOTHING) {
exam_here = Examinable(player, loc);
if (exam_here || (loc == player) ||
(loc == where_is(player))) {
*buff = '#';
ltos(&buff[1], next_exit(player, Next(it), exam_here));
return;
}
}
}
strcpy(buff, "#-1");
return;
}
/* ---------------------------------------------------------------------------
* fun_lastcreate: Return the last object of type Y that X created.
*/
FUNCTION(fun_lastcreate)
{
int i, aowner, aflags, obj_list[4], obj_type;
char *obj_str, *p;
dbref obj = match_thing(player, fargs[0]);
if (!controls(player, obj)) { /* Automatically checks for GoodObj */
strcpy(buff, "#-1");
return;
}
switch (*fargs[1]) {
case 'R':
case 'r':
obj_type = 0;
break;
case 'E':
case 'e':
obj_type = 1;;
break;
case 'T':
case 't':
obj_type = 2;
break;
case 'P':
case 'p':
obj_type = 3;
break;
default:
notify_quiet(player, "Invalid object type.");
strcpy(buff, "#-1");
return;
}
if ((obj_str = atr_get(obj, A_NEWOBJS, &aowner, &aflags)) == NULL) {
strcpy(buff, "#-1");
return;
}
if (!*obj_str) {
free_lbuf(obj_str);
strcpy(buff, "#-1");
return;
}
for (p = (char *) strtok(obj_str, " "), i = 0;
p && (i < 4);
p = (char *) strtok(NULL, " "), i++) {
obj_list[i] = atoi(p);
}
free_lbuf(obj_str);
*buff = '#';
ltos(&buff[1], obj_list[obj_type]);
}
/* ---------------------------------------------------------------------------
* fun_findable: can X locate Y
*/
FUNCTION(fun_findable)
{
dbref obj = match_thing(player, fargs[0]);
dbref victim = match_thing(player, fargs[1]);
if (obj == NOTHING)
strcpy(buff, "#-1 ARG1 NOT FOUND");
else if (victim == NOTHING)
strcpy(buff, "#-1 ARG2 NOT FOUND");
else
ltos(buff, locatable(obj, victim, obj));
}
/* ---------------------------------------------------------------------------
* fun_loc: Returns the location of something
*/
FUNCTION(fun_loc)
{
dbref it;
it = match_thing(player, fargs[0]);
if (locatable(player, it, cause)) {
*buff = '#';
ltos(&buff[1], Location(it));
} else
strcpy(buff, "#-1");
return;
}
/* ---------------------------------------------------------------------------
* fun_where: Returns the "true" location of something
*/
FUNCTION(fun_where)
{
dbref it;
it = match_thing(player, fargs[0]);
if (locatable(player, it, cause)) {
*buff = '#';
ltos(&buff[1], where_is(it));
} else
strcpy(buff, "#-1");
return;
}
/* ---------------------------------------------------------------------------
* fun_rloc: Returns the recursed location of something (specifying #levels)
*/
FUNCTION(fun_rloc)
{
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);
}
*buff = '#';
ltos(&buff[1], it);
return;
}
strcpy(buff, "#-1");
}
/* ---------------------------------------------------------------------------
* fun_room: Find the room an object is ultimately in.
*/
FUNCTION(fun_room)
{
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)) {
*buff = '#';
ltos(&buff[1], it);
return;
}
}
strcpy(buff, "#-1");
} else if (isRoom(it)) {
*buff = '#';
ltos(&buff[1], it);
} else {
strcpy(buff, "#-1");
}
return;
}
/* ---------------------------------------------------------------------------
* fun_owner: Return the owner of an object.
*/
FUNCTION(fun_owner)
{
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);
}
*buff = '#';
ltos(&buff[1], it);
}
/* ---------------------------------------------------------------------------
* fun_controls: Does x control y?
*/
FUNCTION(fun_controls)
{
dbref x, y;
x = match_thing(player, fargs[0]);
if (x == NOTHING) {
strcpy(buff, "#-1 ARG1 NOT FOUND");
return;
}
y = match_thing(player, fargs[1]);
if (y == NOTHING) {
strcpy(buff, "#-1 ARG2 NOT FOUND");
return;
}
ltos(buff, Controls(x, y));
}
/* ---------------------------------------------------------------------------
* fun_sees: Can X see Y in the normal Contents list of the room. If X
* or Y do not exist, 0 is returned.
*/
FUNCTION(fun_sees)
{
dbref it, thing;
int can_see_loc;
if ((it = match_thing(player, fargs[0])) == NOTHING) {
strcpy(buff, "0");
return;
}
thing = match_thing(player, fargs[1]);
if (!Good_obj(thing)) {
strcpy(buff, "0");
return;
}
can_see_loc = (!Dark(Location(thing)) ||
(mudconf.see_own_dark &&
Examinable(player, Location(thing))));
ltos(buff, can_see(it, thing, can_see_loc));
}
/* ---------------------------------------------------------------------------
* fun_visible: Can X examine Y. If X does not exist, 0 is returned.
* If Y, the object, does not exist, 0 is returned. If
* Y the object exists, but the optional attribute does
* not, X's ability to return Y the object is returned.
*/
FUNCTION(fun_visible)
{
dbref it, thing, aowner;
int aflags, atr;
ATTR *ap;
if ((it = match_thing(player, fargs[0])) == NOTHING) {
strcpy(buff, "0");
return;
}
if (parse_attrib(player, fargs[1], &thing, &atr)) {
if (atr == NOTHING) {
ltos(buff, Examinable(it, thing));
return;
}
ap = atr_num(atr);
atr_pget_info(thing, atr, &aowner, &aflags);
ltos(buff, See_attr(it, thing, ap, aowner, aflags));
return;
}
thing = match_thing(player, fargs[1]);
if (!Good_obj(thing)) {
strcpy(buff, "0");
return;
}
ltos(buff, Examinable(it, thing));
}
/* ---------------------------------------------------------------------------
* fun_fullname: Return the fullname of an object (good for exits)
*/
FUNCTION(fun_fullname)
{
dbref it;
it = match_thing(player, fargs[0]);
if (it == NOTHING) {
buff[0] = '\0';
return;
}
if (!mudconf.read_rem_name) {
if (!nearby_or_control(player, it) &&
(!isPlayer(it))) {
strcpy(buff, "#-1 TOO FAR AWAY TO SEE");
return;
}
}
strcpy(buff, Name(it));
}
/* ---------------------------------------------------------------------------
* fun_name: Return the name of an object
*/
FUNCTION(fun_name)
{
dbref it;
char *s;
it = match_thing(player, fargs[0]);
if (it == NOTHING) {
buff[0] = '\0';
return;
}
if (!mudconf.read_rem_name) {
if (!nearby_or_control(player, it) && !isPlayer(it)) {
strcpy(buff, "#-1 TOO FAR AWAY TO SEE");
return;
}
}
strcpy(buff, Name(it));
if (isExit(it)) {
for (s = buff; *s && (*s != ';'); s++);
*s = '\0';
}
}
/* ---------------------------------------------------------------------------
* fun_match, fun_matchall, fun_strmatch: Match arg2 against each word of
* arg1 returning index of first match (or index of all matches), or against
* the whole string.
*/
FUNCTION(fun_match)
{
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)) {
ltos(buff, wcount);
return;
}
wcount++;
} while (s);
strcpy(buff, "0");
}
FUNCTION(fun_matchall)
{
int wcount;
char *r, *s, *bp, sep, osep, tbuf[8];
svarargs_preamble("MATCHALL", 4);
/* SPECIAL CASE: If there's no output delimiter specified, we use
* a space, NOT the delimiter given for the list!
*/
if (nfargs < 4)
osep = ' ';
/* Check each word individually, returning the word number of all
* that match. If none match, return an empty string.
*/
wcount = 1;
bp = buff;
s = trim_space_sep(fargs[0], sep);
do {
r = split_token(&s, sep);
if (quick_wild(fargs[1], r)) {
ltos(tbuf, wcount);
if (bp != buff) {
safe_chr(osep, buff, &bp);
}
safe_str(tbuf, buff, &bp);
}
wcount++;
} while (s);
*bp = '\0';
}
FUNCTION(fun_strmatch)
{
/* Check if we match the whole string. If so, return 1 */
if (quick_wild(fargs[1], fargs[0]))
strcpy(buff, "1");
else
strcpy(buff, "0");
return;
}
/* ---------------------------------------------------------------------------
* fun_regmatch: Return 0 or 1 depending on whether or not a regular
* expression matches a string. If a third argument is specified, dump
* the results of a regexp pattern match into a set of arbitrary r()-registers.
*
* regmatch(string, pattern, list of registers)
* If the number of matches exceeds the registers, those bits are tossed
* out.
* If -1 is specified as a register number, the matching bit is tossed.
* Therefore, if the list is "-1 0 3 5", the regexp $0 is tossed, and
* the regexp $1, $2, and $3 become r(0), r(3), and r(5), respectively.
*
*/
FUNCTION(fun_regmatch)
{
int i, nqregs, curq, len;
char *qregs[NSUBEXP];
regexp *re;
int matched;
if (!fn_range_check("REGMATCH", nfargs, 2, 3, buff))
return;
if ((re = regcomp(fargs[1])) == NULL) {
/* Matching error. */
notify_quiet(player, (const char *) regexp_errbuf);
strcpy(buff, "0");
return;
}
matched = (int) regexec(re, fargs[0]);
ltos(buff, matched);
/* If we don't have a third argument, we're done. */
if (nfargs != 3) {
free(re);
return;
}
/* We need to parse the list of registers. Anything that we don't get is
* assumed to be -1. If we didn't match, or the match went wonky,
* then set the register to empty. Otherwise, fill the register
* with the subexpression.
*/
nqregs = list2arr(qregs, NSUBEXP, fargs[2], ' ');
for (i = 0; i < nqregs; i++) {
if (qregs[i] && *qregs[i] && is_integer(qregs[i]))
curq = atoi(qregs[i]);
else
curq = -1;
if ((curq < 0) || (curq > 9))
continue;
if (!mudstate.global_regs[curq]) {
mudstate.global_regs[curq] = alloc_lbuf("fun_regmatch");
}
if (!matched || !re->startp[i] || !re->endp[i]) {
mudstate.global_regs[curq][0] = '\0'; /* empty string */
} else {
len = re->endp[i] - re->startp[i];
if (len > LBUF_SIZE - 1)
len = LBUF_SIZE - 1;
else if (len < 0)
len = 0;
strncpy(mudstate.global_regs[curq], re->startp[i], len);
mudstate.global_regs[curq][len] = '\0'; /* must null-terminate */
}
}
free(re);
}
/* ---------------------------------------------------------------------------
* 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'
*/
FUNCTION(fun_extract)
{
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)) {
*buff = '\0';
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) {
*buff = '\0';
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);
strcpy(buff, r);
}
int
xlate(arg)
char *arg;
{
/* Deals with either dbrefs or integers. This is not, mind you,
* used by the boolean-determining functions such as or() and not();
* it's used by the search routines, though.
*/
int temp;
char *temp2;
if (arg[0] == '#') {
arg++;
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_elements: given a list of numbers, get corresponding elements from
* the list. elements(ack bar eep foof yay,2 4) ==> bar foof
* The function takes a separator, but the separator only applies to the
* first list.
*/
FUNCTION(fun_elements)
{
int nwords, cur;
char *ptrs[LBUF_SIZE / 2];
char *wordlist, *s, *r, *bp, sep, osep;
svarargs_preamble("ELEMENTS", 4);
/* Turn the first list into an array. */
wordlist = alloc_lbuf("fun_elements.wordlist");
strcpy(wordlist, fargs[0]);
nwords = list2arr(ptrs, LBUF_SIZE / 2, wordlist, sep);
bp = buff;
s = trim_space_sep(fargs[1], ' ');
/* Go through the second list, grabbing the numbers and finding the
* corresponding elements.
*/
do {
r = split_token(&s, ' ');
cur = atoi(r) - 1;
if ((cur >= 0) && (cur < nwords) && ptrs[cur]) {
if (bp != buff) {
safe_chr(osep, buff, &bp);
}
safe_str(ptrs[cur], buff, &bp);
}
} while (s);
*bp = '\0';
free_lbuf(wordlist);
}
/* ---------------------------------------------------------------------------
* fun_grab: a combination of extract() and match(), sortof. We grab the
* single element that we match.
*
* grab(Test:1 Ack:2 Foof:3,*:2) => Ack:2
* grab(Test-1+Ack-2+Foof-3,*o*,+) => Ack:2
*/
FUNCTION(fun_grab)
{
char *r, *s, sep;
varargs_preamble("GRAB", 3);
/* Walk the wordstring, until we find the word we want. */
s = trim_space_sep(fargs[0], sep);
do {
r = split_token(&s, sep);
if (quick_wild(fargs[1], r)) {
strcpy(buff, r);
return;
}
} while (s);
*buff = '\0';
}
/* ---------------------------------------------------------------------------
* 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
*/
FUNCTION(fun_index)
{
int start, end;
char c, *s, *p, *bp;
s = fargs[0];
c = *fargs[1];
start = atoi(fargs[2]);
end = atoi(fargs[3]);
*buff = '\0';
bp = buff;
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, not tabs or newlines, since people may
* MUSHcode strings like "%r%tAmberyl %r%tMoonchilde %r%tEvinar"
*/
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, &bp);
return;
} else {
p++;
}
}
}
/* if we've gotten this far, we've run off the end of the string */
safe_str(s, buff, &bp);
}
FUNCTION(fun_cat)
{
int i;
char *bp;
bp = buff;
safe_str(fargs[0], buff, &bp);
for (i = 1; i < nfargs; i++) {
safe_chr(' ', buff, &bp);
safe_str(fargs[i], buff, &bp);
}
}
FUNCTION(fun_version)
{
strcpy(buff, mudstate.version);
}
FUNCTION(fun_strlen)
{
ltos(buff, (int) strlen(mudconf.ansi_colors ?
strip_ansi(fargs[0]) : fargs[0]));
}
FUNCTION(fun_num)
{
*buff = '#';
ltos(&buff[1], match_thing(player, fargs[0]));
}
FUNCTION(fun_gt)
{
ltos(buff, (aton(fargs[0]) > aton(fargs[1])));
}
FUNCTION(fun_gte)
{
ltos(buff, (aton(fargs[0]) >= aton(fargs[1])));
}
FUNCTION(fun_lt)
{
ltos(buff, (aton(fargs[0]) < aton(fargs[1])));
}
FUNCTION(fun_lte)
{
ltos(buff, (aton(fargs[0]) <= aton(fargs[1])));
}
FUNCTION(fun_eq)
{
ltos(buff, (aton(fargs[0]) == aton(fargs[1])));
}
FUNCTION(fun_neq)
{
ltos(buff, (aton(fargs[0]) != aton(fargs[1])));
}
FUNCTION(fun_and)
{
int i;
if (nfargs < 2) {
strcpy(buff, "#-1 TOO FEW ARGUMENTS");
} else {
for (i = 0; (i < nfargs) && atoi(fargs[i]); i++)
;
ltos(buff, i == nfargs);
}
return;
}
FUNCTION(fun_or)
{
int i;
if (nfargs < 2) {
strcpy(buff, "#-1 TOO FEW ARGUMENTS");
} else {
for (i = 0; (i < nfargs) && !atoi(fargs[i]); i++)
;
ltos(buff, i != nfargs);
}
return;
}
FUNCTION(fun_xor)
{
int i, val;
if (nfargs < 2) {
strcpy(buff, "#-1 TOO FEW ARGUMENTS");
} else {
val = atoi(fargs[0]);
for (i = 1; i < nfargs; i++) {
if (val) {
val = !atoi(fargs[i]);
} else {
val = atoi(fargs[i]);
}
}
ltos(buff, val ? 1 : 0);
}
return;
}
FUNCTION(fun_not)
{
ltos(buff, !atoi(fargs[0]));
}
/*-------------------------------------------------------------------------
* True boolean functions.
*/
FUNCTION(fun_andbool)
{
int i;
if (nfargs < 2) {
strcpy(buff, "#-1 TOO FEW ARGUMENTS");
} else {
for (i = 0; (i < nfargs) && xlate(fargs[i]); i++)
;
ltos(buff, i == nfargs);
}
return;
}
FUNCTION(fun_orbool)
{
int i;
if (nfargs < 2) {
strcpy(buff, "#-1 TOO FEW ARGUMENTS");
} else {
for (i = 0; (i < nfargs) && !xlate(fargs[i]); i++)
;
ltos(buff, i != nfargs);
}
return;
}
FUNCTION(fun_xorbool)
{
int i, val;
if (nfargs < 2) {
strcpy(buff, "#-1 TOO FEW ARGUMENTS");
} else {
val = xlate(fargs[0]);
for (i = 1; i < nfargs; i++) {
if (val) {
val = !xlate(fargs[i]);
} else {
val = xlate(fargs[i]);
}
}
ltos(buff, val ? 1 : 0);
}
return;
}
FUNCTION(fun_notbool)
{
ltos(buff, !xlate(fargs[0]));
}
FUNCTION(fun_t)
{
ltos(buff, xlate(fargs[0]) ? 1 : 0);
}
/*-------------------------------------------------------------------------
* Bitmask functions.
*/
FUNCTION(fun_shl)
{
ltos(buff, atoi(fargs[0]) << atoi(fargs[1]));
}
FUNCTION(fun_shr)
{
ltos(buff, atoi(fargs[0]) >> atoi(fargs[1]));
}
FUNCTION(fun_band)
{
ltos(buff, atoi(fargs[0]) & atoi(fargs[1]));
}
FUNCTION(fun_bor)
{
ltos(buff, atoi(fargs[0]) | atoi(fargs[1]));
}
FUNCTION(fun_bnand)
{
ltos(buff, atoi(fargs[0]) & ~(atoi(fargs[1])));
}
/*-------------------------------------------------------------------------
* inc and dec: increments and decrements a number. Faster than add().
* Differs slightly from the MUX functions in that no check is made for
* something being a number -- if it's not, atoi() will deal with that
* well enough. (This is useful for MUSHcoding, particularly when passed
* an empty string.)
*/
FUNCTION(fun_inc)
{
ltos(buff, atoi(fargs[0]) + 1);
}
FUNCTION(fun_dec)
{
ltos(buff, atoi(fargs[0]) - 1);
}
/*-------------------------------------------------------------------------
* More math functions.
*/
FUNCTION(fun_sqrt)
{
double val;
val = atof(fargs[0]);
if (val < 0) {
strcpy(buff, "#-1 SQUARE ROOT OF NEGATIVE");
} else if (val == 0) {
strcpy(buff, "0");
} else {
fval(buff, sqrt(val));
}
}
FUNCTION(fun_add)
{
NVAL sum;
int i;
if (nfargs < 2) {
strcpy(buff, "#-1 TOO FEW ARGUMENTS");
} else {
sum = aton(fargs[0]);
for (i = 1; i < nfargs; i++) {
sum += aton(fargs[i]);
}
fval(buff, sum);
}
return;
}
FUNCTION(fun_sub)
{
fval(buff, aton(fargs[0]) - aton(fargs[1]));
}
FUNCTION(fun_mul)
{
NVAL prod;
int i;
if (nfargs < 2) {
strcpy(buff, "#-1 TOO FEW ARGUMENTS");
} else {
prod = aton(fargs[0]);
for (i = 1; i < nfargs; i++) {
prod *= aton(fargs[i]);
}
fval(buff, prod);
}
return;
}
FUNCTION(fun_floor)
{
sprintf(buff, "%.0f", floor(atof(fargs[0])));
}
FUNCTION(fun_ceil)
{
sprintf(buff, "%.0f", ceil(atof(fargs[0])));
}
FUNCTION(fun_round)
{
const char *fstr;
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;
}
sprintf(buff, fstr, atof(fargs[0]));
/* Handle bogus result of "-0" from sprintf. Yay, cclib. */
if (!strcmp(buff, "-0")) {
strcpy(buff, "0");
}
}
FUNCTION(fun_trunc)
{
int num;
num = atoi(fargs[0]);
ltos(buff, num);
}
FUNCTION(fun_div)
{
int bot;
bot = atoi(fargs[1]);
if (bot == 0) {
strcpy(buff, "#-1 DIVIDE BY ZERO");
} else {
ltos(buff, (atoi(fargs[0]) / bot));
}
}
FUNCTION(fun_fdiv)
{
double bot;
bot = atof(fargs[1]);
if (bot == 0) {
strcpy(buff, "#-1 DIVIDE BY ZERO");
} else {
fval(buff, (atof(fargs[0]) / bot));
}
}
FUNCTION(fun_mod)
{
int bot;
bot = atoi(fargs[1]);
if (bot == 0)
bot = 1;
ltos(buff, atoi(fargs[0]) % bot);
}
FUNCTION(fun_pi)
{
strcpy(buff, "3.141592654");
}
FUNCTION(fun_e)
{
strcpy(buff, "2.718281828");
}
FUNCTION(fun_sin)
{
fval(buff, sin(atof(fargs[0])));
}
FUNCTION(fun_cos)
{
fval(buff, cos(atof(fargs[0])));
}
FUNCTION(fun_tan)
{
fval(buff, tan(atof(fargs[0])));
}
FUNCTION(fun_exp)
{
fval(buff, exp(atof(fargs[0])));
}
FUNCTION(fun_power)
{
double val1, val2;
val1 = atof(fargs[0]);
val2 = atof(fargs[1]);
if (val1 < 0) {
strcpy(buff, "#-1 POWER OF NEGATIVE");
} else {
fval(buff, pow(val1, val2));
}
}
FUNCTION(fun_ln)
{
double val;
val = atof(fargs[0]);
if (val > 0)
fval(buff, log(val));
else
strcpy(buff, "#-1 LN OF NEGATIVE OR ZERO");
}
FUNCTION(fun_log)
{
double val;
val = atof(fargs[0]);
if (val > 0) {
fval(buff, log10(val));
} else {
strcpy(buff, "#-1 LOG OF NEGATIVE OR ZERO");
}
}
FUNCTION(fun_asin)
{
double val;
val = atof(fargs[0]);
if ((val < -1) || (val > 1)) {
strcpy(buff, "#-1 ASIN ARGUMENT OUT OF RANGE");
} else {
fval(buff, asin(val));
}
}
FUNCTION(fun_acos)
{
double val;
val = atof(fargs[0]);
if ((val < -1) || (val > 1)) {
strcpy(buff, "#-1 ACOS ARGUMENT OUT OF RANGE");
} else {
fval(buff, acos(val));
}
}
FUNCTION(fun_atan)
{
fval(buff, atan(atof(fargs[0])));
}
FUNCTION(fun_dist2d)
{
int d;
double r;
d = atoi(fargs[0]) - atoi(fargs[2]);
r = (double) (d * d);
d = atoi(fargs[1]) - atoi(fargs[3]);
r += (double) (d * d);
d = (int) (sqrt(r) + 0.5);
ltos(buff, d);
}
FUNCTION(fun_dist3d)
{
int d;
double r;
d = atoi(fargs[0]) - atoi(fargs[3]);
r = (double) (d * d);
d = atoi(fargs[1]) - atoi(fargs[4]);
r += (double) (d * d);
d = atoi(fargs[2]) - atoi(fargs[5]);
r += (double) (d * d);
d = (int) (sqrt(r) + 0.5);
ltos(buff, d);
}
/* ---------------------------------------------------------------------------
* Vector functions, where vectors are space-separated numbers.
*
* TinyMUX and PennMUSH-derived implementation.
*/
#define MAXDIM 20
#define VADD_F 0
#define VSUB_F 1
#define VMUL_F 2
#define VDOT_F 3
#define VCROSS_F 4
static void handle_vectors(vecarg1, vecarg2, buff, sep, osep, flag)
char *vecarg1;
char *vecarg2;
char *buff;
char sep;
char osep;
int flag;
{
char *v1[LBUF_SIZE], *v2[LBUF_SIZE];
char vres[MAXDIM][LBUF_SIZE];
double scalar;
int n, m, i;
/*
* split the list up, or return if the list is empty
*/
if (!vecarg1 || !*vecarg1 || !vecarg2 || !*vecarg2) {
return;
}
n = list2arr(v1, LBUF_SIZE, vecarg1, sep);
m = list2arr(v2, LBUF_SIZE, vecarg2, sep);
if (n != m) {
strcpy(buff, "#-1 VECTORS MUST BE SAME DIMENSIONS");
return;
}
if (n > MAXDIM) {
strcpy(buff, "#-1 TOO MANY DIMENSIONS ON VECTORS");
return;
}
switch (flag) {
case VADD_F:
for (i = 0; i < n; i++) {
fval(vres[i], atof(v1[i]) + atof(v2[i]));
v1[i] = (char *) vres[i];
}
arr2list(v1, n, buff, osep);
return;
/* NOTREACHED */
case VSUB_F:
for (i = 0; i < n; i++) {
fval(vres[i], atof(v1[i]) - atof(v2[i]));
v1[i] = (char *) vres[i];
}
arr2list(v1, n, buff, osep);
return;
/* NOTREACHED */
case VMUL_F:
/* if n or m is 1, this is scalar multiplication.
* otherwise, multiply elementwise.
*/
if (n == 1) {
scalar = atof(v1[0]);
for (i = 0; i < m; i++) {
fval(vres[i], atof(v2[i]) * scalar);
v1[i] = (char *) vres[i];
}
n = m;
} else if (m == 1) {
scalar = atof(v2[0]);
for (i = 0; i < n; i++) {
fval(vres[i], atof(v1[i]) * scalar);
v1[i] = (char *) vres[i];
}
} else {
/* vector elementwise product.
*
* Note this is a departure from TinyMUX, but an imitation
* of the PennMUSH behavior: the documentation in Penn
* claims it's a dot product, but the actual behavior
* isn't. We implement dot product separately!
*/
for (i = 0; i < n; i++) {
fval(vres[i], atof(v1[i]) * atof(v2[i]));
v1[i] = (char *) vres[i];
}
}
arr2list(v1, n, buff, osep);
return;
/* NOTREACHED */
case VDOT_F:
scalar = 0;
for (i = 0; i < n; i++) {
scalar += atof(v1[i]) * atof(v2[i]);
v1[i] = (char *) vres[i];
}
fval(buff, scalar);
return;
/* NOTREACHED */
default:
/* If we reached this, we're in trouble. */
strcpy(buff, "#-1 UNIMPLEMENTED");
}
}
FUNCTION(fun_vadd)
{
char sep, osep;
svarargs_preamble("VADD", 4);
handle_vectors(fargs[0], fargs[1], buff, sep, osep, VADD_F);
}
FUNCTION(fun_vsub)
{
char sep, osep;
svarargs_preamble("VSUB", 4);
handle_vectors(fargs[0], fargs[1], buff, sep, osep, VSUB_F);
}
FUNCTION(fun_vmul)
{
char sep, osep;
svarargs_preamble("VMUL", 4);
handle_vectors(fargs[0], fargs[1], buff, sep, osep, VMUL_F);
}
FUNCTION(fun_vdot)
{
/* dot product: (a,b,c) . (d,e,f) = ad + be + cf
*
* no cross product implementation yet: it would be
* (a,b,c) x (d,e,f) = (bf - ce, cd - af, ae - bd)
*/
char sep, osep;
svarargs_preamble("VDOT", 4);
handle_vectors(fargs[0], fargs[1], buff, sep, osep, VDOT_F);
}
FUNCTION(fun_vmag)
{
char *v1[LBUF_SIZE];
int n, i;
double tmp, res = 0;
char sep;
varargs_preamble("VMAG", 2);
/*
* split the list up, or return if the list is empty
*/
if (!fargs[0] || !*fargs[0]) {
return;
}
n = list2arr(v1, LBUF_SIZE, fargs[0], sep);
if (n > MAXDIM) {
strcpy(buff, "#-1 TOO MANY DIMENSIONS ON VECTORS");
return;
}
/*
* calculate the magnitude
*/
for (i = 0; i < n; i++) {
tmp = atof(v1[i]);
res += tmp * tmp;
}
if (res > 0)
fval(buff, sqrt(res));
else
strcpy(buff, "0");
}
FUNCTION(fun_vunit)
{
char *v1[LBUF_SIZE];
char vres[MAXDIM][LBUF_SIZE];
int n, i;
double tmp, res = 0;
char sep;
varargs_preamble("VUNIT", 2);
/*
* split the list up, or return if the list is empty
*/
if (!fargs[0] || !*fargs[0]) {
return;
}
n = list2arr(v1, LBUF_SIZE, fargs[0], sep);
if (n > MAXDIM) {
strcpy(buff, "#-1 TOO MANY DIMENSIONS ON VECTORS");
return;
}
/*
* calculate the magnitude
*/
for (i = 0; i < n; i++) {
tmp = atof(v1[i]);
res += tmp * tmp;
}
if (res <= 0) {
strcpy(buff, "#-1 CAN'T MAKE UNIT VECTOR FROM ZERO-LENGTH VECTOR");
return;
}
for (i = 0; i < n; i++) {
fval(vres[i], atof(v1[i]) / sqrt(res));
v1[i] = (char *) vres[i];
}
arr2list(v1, n, buff, sep);
}
FUNCTION(fun_vdim)
{
char sep;
if (fargs == 0)
strcpy(buff, "0");
else {
varargs_preamble("VDIM", 2);
ltos(buff, countwords(fargs[0], sep));
}
}
/* ---------------------------------------------------------------------------
* fun_comp: string compare.
*/
FUNCTION(fun_comp)
{
int x;
x = strcmp(fargs[0], fargs[1]);
if (x > 0)
strcpy(buff, "1");
else if (x < 0)
strcpy(buff, "-1");
else
strcpy(buff, "0");
}
/* ---------------------------------------------------------------------------
* fun_xcon: Return a partial list of contents of an object, starting from
* a specified element in the list and copying a specified number
* of elements.
*/
FUNCTION(fun_xcon)
{
dbref thing, it;
char *bufp, *tbuf;
int i, first, last;
it = match_thing(player, fargs[0]);
*buff = '\0';
bufp = buff;
if ((it != NOTHING) && (Has_contents(it)) &&
(Examinable(player, it) || (Location(player) == it) ||
(it == cause))) {
first = atoi(fargs[1]);
last = atoi(fargs[2]);
if ((first > 0) && (last > 0)) {
tbuf = alloc_sbuf("fun_xcon");
/* Move to the first object that we want */
for (thing = Contents(it), i = 1;
(i < first) && (thing != NOTHING) && (Next(thing) != thing);
thing = Next(thing), i++)
;
/* Grab objects until we reach the last one we want */
for (i = 0;
(i < last) && (thing != NOTHING) && (Next(thing) != thing);
thing = Next(thing), i++) {
if (*buff) {
tbuf[0] = ' ';
tbuf[1] = '#';
ltos(&tbuf[2], thing);
} else {
*tbuf = '#';
ltos(&tbuf[1], thing);
}
safe_str(tbuf, buff, &bufp);
}
free_sbuf(tbuf);
*bufp = '\0';
}
} else
strcpy(buff, "#-1");
}
/* ---------------------------------------------------------------------------
* fun_lcon: Return a list of contents.
*/
FUNCTION(fun_lcon)
{
dbref thing, it;
char *bufp, *tbuf;
it = match_thing(player, fargs[0]);
*buff = '\0';
bufp = buff;
if ((it != NOTHING) &&
(Has_contents(it)) &&
(Examinable(player, it) ||
(Location(player) == it) ||
(it == cause))) {
tbuf = alloc_sbuf("fun_lcon");
DOLIST(thing, Contents(it)) {
if (*buff) {
tbuf[0] = ' ';
tbuf[1] = '#';
ltos(&tbuf[2], thing);
} else {
*tbuf = '#';
ltos(&tbuf[1], thing);
}
safe_str(tbuf, buff, &bufp);
}
free_sbuf(tbuf);
*bufp = '\0';
} else
strcpy(buff, "#-1");
}
/* ---------------------------------------------------------------------------
* fun_lexits: Return a list of exits.
*/
FUNCTION(fun_lexits)
{
dbref thing, it, parent;
char *bufp, *tbuf;
int exam, lev;
*buff = '\0';
bufp = buff;
it = match_thing(player, fargs[0]);
if (!Good_obj(it) || !Has_exits(it)) {
strcpy(buff, "#-1");
return;
}
exam = Examinable(player, it);
if (!exam && (where_is(player) != it) && (it != cause)) {
strcpy(buff, "#-1");
return;
}
tbuf = alloc_sbuf("fun_lexits");
/* Return info for all parent levels */
ITER_PARENTS(it, parent, lev) {
/* Look for exits at each level */
if (!Has_exits(parent))
continue;
for (thing = next_exit(player, Exits(parent), exam);
thing != NOTHING;
thing = next_exit(player, Next(thing), exam)) {
if (*buff) {
tbuf[0] = ' ';
tbuf[1] = '#';
ltos(&tbuf[2], thing);
} else {
*tbuf = '#';
ltos(&tbuf[1], thing);
}
safe_str(tbuf, buff, &bufp);
}
}
free_sbuf(tbuf);
*bufp = '\0';
return;
}
/* --------------------------------------------------------------------------
* fun_home: Return an object's home
*/
FUNCTION(fun_home)
{
dbref it;
it = match_thing(player, fargs[0]);
if (!Good_obj(it) || !Examinable(player, it))
strcpy(buff, "#-1");
else if (Has_home(it)) {
*buff = '#';
ltos(&buff[1], Home(it));
} else if (Has_dropto(it)) {
*buff = '#';
ltos(&buff[1], Dropto(it));
} else if (isExit(it)) {
*buff = '#';
ltos(&buff[1], where_is(it));
} else
strcpy(buff, "#-1");
return;
}
/* ---------------------------------------------------------------------------
* fun_money: Return an object's value
*/
FUNCTION(fun_money)
{
dbref it;
it = match_thing(player, fargs[0]);
if ((it == NOTHING) || !Examinable(player, it))
strcpy(buff, "#-1");
else
ltos(buff, Pennies(it));
}
/* ---------------------------------------------------------------------------
* fun_pos: Find a word in a string */
FUNCTION(fun_pos)
{
int i = 1;
char *s, *t, *u;
s = fargs[1];
while (*s) {
u = s;
t = fargs[0];
while (*t && *t == *u)
++t, ++u;
if (*t == '\0') {
ltos(buff, i);
return;
}
++i, ++s;
}
strcpy(buff, "#-1");
return;
}
/* ---------------------------------------------------------------------------
* fun_lpos: Find all occurrences of a character in a string, and return
* a space-separated list of the positions, starting at 0. i.e.,
* lpos(a-bc-def-g,-) ==> 1 4 8
*/
FUNCTION(fun_lpos)
{
char *s, *bp;
char c, tbuf[8];
int i;
*buff = '\0';
if (!fargs[0] || !*fargs[0])
return;
c = (char) *(fargs[1]);
if (!c)
c = ' ';
for (i = 0, s = fargs[0], bp = buff; *s; i++, s++) {
if (*s == c) {
if (bp != buff) {
safe_chr(' ', buff, &bp);
}
ltos(tbuf, i);
safe_str(tbuf, buff, &bp);
}
}
*bp = '\0';
}
/* ---------------------------------------------------------------------------
* 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(buff, str, el, word, sep, flag)
char *buff, *str, *word, sep;
int el, flag;
{
int ct, overrun;
char *sptr, *iptr, *eptr, *bp;
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))) {
*buff = '\0';
return;
}
/* we can't fiddle with anything before the first position */
if (el < 1) {
strcpy(buff, str);
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))) {
strcpy(buff, str);
return;
}
/* Split the 'target' word from the 'after' portion. */
if (eptr)
iptr = split_token(&eptr, sep);
else
iptr = NULL;
}
bp = buff;
switch (flag) {
case IF_DELETE: /* deletion */
if (sptr) {
safe_str(sptr, buff, &bp);
if (eptr) {
safe_chr(sep, buff, &bp);
}
}
if (eptr) {
safe_str(eptr, buff, &bp);
}
break;
case IF_REPLACE: /* replacing */
if (sptr) {
safe_str(sptr, buff, &bp);
safe_chr(sep, buff, &bp);
}
safe_str(word, buff, &bp);
if (eptr) {
safe_chr(sep, buff, &bp);
safe_str(eptr, buff, &bp);
}
break;
case IF_INSERT: /* insertion */
if (sptr) {
safe_str(sptr, buff, &bp);
safe_chr(sep, buff, &bp);
}
safe_str(word, buff, &bp);
if (iptr) {
safe_chr(sep, buff, &bp);
safe_str(iptr, buff, &bp);
}
if (eptr) {
safe_chr(sep, buff, &bp);
safe_str(eptr, buff, &bp);
}
break;
}
*bp = '\0';
}
FUNCTION(fun_ldelete)
{ /* delete a word at position X of a list */
char sep;
varargs_preamble("LDELETE", 3);
do_itemfuns(buff, fargs[0], atoi(fargs[1]), NULL, sep, IF_DELETE);
}
FUNCTION(fun_replace)
{ /* replace a word at position X of a list */
char sep;
varargs_preamble("REPLACE", 4);
do_itemfuns(buff, fargs[0], atoi(fargs[1]), fargs[2], sep, IF_REPLACE);
}
FUNCTION(fun_insert)
{ /* insert a word at position X of a list */
char sep;
varargs_preamble("INSERT", 4);
do_itemfuns(buff, fargs[0], atoi(fargs[1]), fargs[2], sep, IF_INSERT);
}
/* ---------------------------------------------------------------------------
* fun_remove: Remove a word from a string
*/
FUNCTION(fun_remove)
{
char *s, *sp, *word, *bp;
char sep;
int found;
varargs_preamble("REMOVE", 3);
if (index(fargs[1], sep)) {
strcpy(buff, "#-1 CAN ONLY DELETE ONE ELEMENT");
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.
*/
bp = buff;
sp = s;
found = 0;
while (s) {
sp = split_token(&s, sep);
if (found || strcmp(sp, word)) {
if (bp != buff)
safe_chr(sep, buff, &bp);
safe_str(sp, buff, &bp);
} else {
found = 1;
}
}
*bp = '\0';
}
/* ---------------------------------------------------------------------------
* fun_member: Is a word in a string
*/
FUNCTION(fun_member)
{
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)) {
ltos(buff, wcount);
return;
}
wcount++;
} while (s);
strcpy(buff, "0");
}
/* ---------------------------------------------------------------------------
* Functions that tinker with clobbering formatting.
*
* fun_secure, fun_escape: escape [, ], %, \, and the beginning of the string.
* fun_lit: returns a literal string.
* fun_translate: strips or converts raw sequences.
*/
FUNCTION(fun_secure)
{
char *s, *d;
s = fargs[0];
d = buff;
while (*s) {
switch (*s) {
case '%':
case '$':
case '\\':
case '[':
case ']':
case '(':
case ')':
case '{':
case '}':
case ',':
case ';':
safe_chr(' ', buff, &d);
break;
default:
safe_chr(*s, buff, &d);
}
s++;
}
*d = '\0';
}
FUNCTION(fun_escape)
{
char *s, *d;
s = fargs[0];
d = buff;
while (*s) {
switch (*s) {
case '%':
case '\\':
case '[':
case ']':
case '{':
case '}':
case ';':
safe_chr('\\', buff, &d);
default:
if (d == buff) {
safe_chr('\\', buff, &d);
}
safe_chr(*s, buff, &d);
}
s++;
}
*d = '\0';
}
FUNCTION(fun_lit)
{
strcpy(buff, fargs[0]); /* just copy the unevaluated argument */
}
FUNCTION(fun_translate)
{
int type = 0;
if (fargs[0] && fargs[1]) {
if (*fargs[1] && ((*fargs[1] == 's') || (*fargs[1] == '0')))
type = 0;
else if (*fargs[1] && ((*fargs[1] == 'p') || (*fargs[1] == '1')))
type = 1;
strcpy(buff, translate_string(fargs[0], type));
}
}
/*---------------------------------------------------------------------------
* Pueblo HTML-related functions.
*/
#ifdef PUEBLO_SUPPORT
FUNCTION(fun_html_escape)
{
*buff = '\0';
html_escape(fargs[0], buff, 0);
}
FUNCTION(fun_html_unescape)
{
const char *msg_orig;
char *temp;
int ret = 0;
char **destp;
temp = buff;
destp = &temp;
*buff = '\0';
for (msg_orig = fargs[0]; msg_orig && *msg_orig && !ret; msg_orig++) {
switch (*msg_orig) {
case '&':
if (!strncmp(msg_orig, """, 6)) {
ret = safe_chr_fn('\"', buff, destp);
msg_orig += 5;
} else if (!strncmp(msg_orig, "<", 4)) {
ret = safe_chr_fn('<', buff, destp);
msg_orig += 3;
} else if (!strncmp(msg_orig, ">", 4)) {
ret = safe_chr_fn('>', buff, destp);
msg_orig += 3;
} else if (!strncmp(msg_orig, "&", 5)) {
ret = safe_chr_fn('&', buff, destp);
msg_orig += 4;
}
break;
default:
ret = safe_chr_fn(*msg_orig, buff, destp);
break;
}
}
}
FUNCTION(fun_url_escape)
{
/* These are the characters which are converted to %<hex> */
char *escaped_chars = "<>#%{}|\\^~[]';/?:@=&\"+";
const char *msg_orig;
char *temp;
int ret = 0;
char **destp;
char tbuf[10];
temp = buff;
destp = &temp;
*buff = '\0';
for (msg_orig = fargs[0]; msg_orig && *msg_orig && !ret; msg_orig++) {
if (index(escaped_chars, *msg_orig)) {
sprintf(tbuf, "%%%2x", *msg_orig);
ret = safe_str(tbuf, buff, destp);
} else if (*msg_orig == ' ') {
ret = safe_chr_fn('+', buff, destp);
} else{
ret = safe_chr_fn(*msg_orig, buff, destp);
}
}
}
FUNCTION(fun_url_unescape)
{
const char *msg_orig;
char *temp;
int ret = 0;
char **destp;
unsigned int tempchar;
char tempstr[10];
temp = buff;
destp = &temp;
*buff = '\0';
for (msg_orig = fargs[0]; msg_orig && *msg_orig && !ret;) {
switch (*msg_orig) {
case '+':
ret = safe_chr_fn(' ', buff, destp);
msg_orig++;
break;
case '%':
strncpy(tempstr, msg_orig+1, 2);
tempstr[2] = '\0';
if (sscanf(tempstr, "%x", &tempchar) == 1)
ret = safe_chr_fn(tempchar, buff, destp);
if (*msg_orig)
msg_orig++; /* Skip the '%' */
if (*msg_orig) /* Skip the 1st hex character. */
msg_orig++;
if (*msg_orig) /* Skip the 2nd hex character. */
msg_orig++;
break;
default:
ret = safe_chr_fn(*msg_orig, buff, destp);
msg_orig++;
break;
}
}
return;
}
#endif /* PUEBLO_SUPPORT */
/*---------------------------------------------------------------------------
* Take a character position and return which word that char is in.
* wordpos(<string>, <charpos>)
*/
FUNCTION(fun_wordpos)
{
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);
}
ltos(buff, i);
return;
}
strcpy(buff, "#-1");
return;
}
FUNCTION(fun_type)
{
dbref it;
it = match_thing(player, fargs[0]);
if (!Good_obj(it)) {
strcpy(buff, "#-1 NOT FOUND");
return;
}
switch (Typeof(it)) {
case TYPE_ROOM:
strcpy(buff, "ROOM");
break;
case TYPE_EXIT:
strcpy(buff, "EXIT");
break;
case TYPE_PLAYER:
strcpy(buff, "PLAYER");
break;
case TYPE_THING:
strcpy(buff, "THING");
break;
default:
strcpy(buff, "#-1 ILLEGAL TYPE");
}
return;
}
/*---------------------------------------------------------------------------
* fun_hasflag: plus auxiliary function atr_has_flag.
*/
static int
atr_has_flag(player, thing, attr, aowner, aflags, flagname)
dbref player, thing;
ATTR *attr;
int aowner, aflags;
char *flagname;
{
if (!See_attr(player, thing, attr, aowner, aflags))
return 0;
else {
if (string_prefix("dark", flagname))
return (aflags & AF_DARK);
else if (string_prefix("wizard", flagname))
return (aflags & AF_WIZARD);
else if (string_prefix("hidden", flagname))
return (aflags & AF_MDARK);
else if (string_prefix("html", flagname))
return (aflags & AF_HTML);
else if (string_prefix("locked", flagname))
return (aflags & AF_LOCK);
else if (string_prefix("no_command", flagname))
return (aflags & AF_NOPROG);
else if (string_prefix("no_parse", flagname))
return (aflags & AF_NOPARSE);
else if (string_prefix("regexp", flagname))
return (aflags & AF_REGEXP);
else if (string_prefix("god", flagname))
return (aflags & AF_GOD);
else if (string_prefix("visual", flagname))
return (aflags & AF_VISUAL);
else if (string_prefix("no_inherit", flagname))
return (aflags & AF_PRIVATE);
else
return 0;
}
}
FUNCTION(fun_hasflag)
{
dbref it, aowner;
int atr, aflags;
ATTR *ap;
if (parse_attrib(player, fargs[0], &it, &atr)) {
if (atr == NOTHING) {
strcpy(buff, "#-1 NOT FOUND");
} else {
ap = atr_num(atr);
atr_pget_info(it, atr, &aowner, &aflags);
if (atr_has_flag(player, it, ap, aowner, aflags,
fargs[1]))
strcpy(buff, "1");
else
strcpy(buff, "0");
}
} else {
it = match_thing(player, fargs[0]);
if (!Good_obj(it)) {
strcpy(buff, "#-1 NOT FOUND");
return;
}
if (mudconf.pub_flags || Examinable(player, it) || (it == cause)) {
if (has_flag(player, it, fargs[1]))
strcpy(buff, "1");
else
strcpy(buff, "0");
} else {
strcpy(buff, "#-1 PERMISSION DENIED");
}
}
}
/*---------------------------------------------------------------------------
* fun_orflags, fun_andflags: check multiple flags at the same time.
* Based off the PennMUSH 1.50 code (and the TinyMUX derivative of it).
*/
static int handle_flaglists(player, cause, name, fstr, type)
dbref player;
dbref cause;
char *name;
char *fstr;
int type; /* 0 for orflags, 1 for andflags */
{
char *s, flagletter[2];
FLAGSET fset;
FLAG p_type;
int negate, temp, ret;
dbref it = match_thing(player, name);
if (it == NOTHING)
return 0;
if (! (mudconf.pub_flags || Examinable(player, it) || (it == cause)))
return 0;
ret = type;
negate = temp = 0;
for (s = fstr; *s; s++) {
/* Check for a negation sign. If we find it, we note it and
* increment the pointer to the next character.
*/
if (*s == '!') {
negate = 1;
s++;
} else {
negate = 0;
}
if (!*s)
return 0;
flagletter[0] = *s;
flagletter[1] = '\0';
if (!convert_flags(player, flagletter, &fset, &p_type)) {
/* Either we got a '!' that wasn't followed by a letter, or
* we couldn't find that flag. For AND, since we've failed
* a check, we can return false. Otherwise we just go on.
*/
if (type == 1)
return 0;
else
continue;
} else {
/* Does the object have this flag? */
if ((Flags(it) & fset.word1) || (Flags2(it) & fset.word2) ||
(Typeof(it) == p_type)) {
/* don't show Connected on Dark wizards to mortals. */
if (isPlayer(it) &&
(fset.word2 == CONNECTED) &&
((Flags(it) & (WIZARD | DARK)) == (WIZARD | DARK)) &&
!Wizard(player))
temp = 0;
else
temp = 1;
} else {
temp = 0;
}
if ((type == 1) && ((negate && temp) || (!negate && !temp))) {
/* Too bad there's no NXOR function...
* At this point we've either got a flag and we don't want
* it, or we don't have a flag and nwe want it. Since it's
* AND, we return false.
*/
return 0;
} else if ((type == 0) &&
((!negate && temp) || (negate && !temp))) {
/* We've found something we want, in an OR. Since we only
* need one thing to be true in order for the entire
* expression to be true, we can return true now.
*/
return 1;
}
/* Otherwise we don't need to do anything. */
}
}
return (ret);
}
FUNCTION(fun_orflags)
{
ltos(buff, handle_flaglists(player, cause, fargs[0], fargs[1], 0));
}
FUNCTION(fun_andflags)
{
ltos(buff, handle_flaglists(player, cause, fargs[0], fargs[1], 1));
}
/*---------------------------------------------------------------------------
*/
FUNCTION(fun_delete)
{
char *s, *d;
unsigned int i, start, nchars, len;
s = fargs[0];
start = atoi(fargs[1]);
nchars = atoi(fargs[2]);
len = strlen(s);
if ((start >= len) || (nchars <= 0)) {
strcpy(buff, s);
return;
}
d = buff;
for (i = 0; i < start; i++)
*d++ = (*s++);
if ( ((i + nchars) < len) && ((i + nchars) > 0) ) {
s += nchars;
while ((*d++ = *s++));
} else {
*d = '\0';
}
}
FUNCTION(fun_lock)
{
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))
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)) {
*buff = '\0';
free_lbuf(tbuf);
} else {
bool = parse_boolexp(player, tbuf, 1);
free_lbuf(tbuf);
tbuf = (char *) unparse_boolexp_function(player, bool);
free_boolexp(bool);
strcpy(buff, tbuf);
}
}
FUNCTION(fun_elock)
{
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))
return;
/* Get the victim and ensure we can do it */
victim = match_thing(player, fargs[1]);
if (!Good_obj(victim)) {
strcpy(buff, "#-1 NOT FOUND");
} else if (!nearby_or_control(player, victim) &&
!nearby_or_control(player, it)) {
strcpy(buff, "#-1 TOO FAR AWAY");
} else {
tbuf = atr_get(it, attr->number, &aowner, &aflags);
if ((aflags & AF_IS_LOCK) ||
Read_attr(player, it, attr, aowner, aflags)) {
bool = parse_boolexp(player, tbuf, 1);
ltos(buff, eval_boolexp(victim, it, it,
bool));
free_boolexp(bool);
} else {
strcpy(buff, "0");
}
free_lbuf(tbuf);
}
}
/* ---------------------------------------------------------------------------
* fun_lwho: Return list of connected users.
*/
FUNCTION(fun_lwho)
{
make_ulist(player, buff);
}
/* ---------------------------------------------------------------------------
* fun_ports: Returns a list of ports for a user.
*/
FUNCTION(fun_ports)
{
dbref target;
if (!Wizard(player)) {
*buff = '\0';
return;
}
target = lookup_player(player, fargs[0], 1);
if (!Good_obj(target) || !Connected(target)) {
*buff = '\0';
return;
}
make_portlist(player, target, buff);
}
/* ---------------------------------------------------------------------------
* fun_programmer: Returns the dbref or #-1 of an object in a @program.
*/
FUNCTION(fun_programmer)
{
dbref target;
target = lookup_player(player, fargs[0], 1);
if (!Good_obj(target) || !Connected(target) ||
!Examinable(player, target)) {
strcpy(buff, "#-1");
return;
}
*buff = '#';
ltos(&buff[1], get_programmer(target));
}
/* ---------------------------------------------------------------------------
* fun_nearby: Return whether or not obj1 is near obj2.
*/
FUNCTION(fun_nearby)
{
dbref obj1, obj2;
obj1 = match_thing(player, fargs[0]);
obj2 = match_thing(player, fargs[1]);
if (!(nearby_or_control(player, obj1) ||
nearby_or_control(player, obj2)))
strcpy(buff, "0");
else if (nearby(obj1, obj2))
strcpy(buff, "1");
else
strcpy(buff, "0");
}
/* ---------------------------------------------------------------------------
* fun_obj, fun_poss, and fun_subj: perform pronoun sub for object.
*/
static void
process_sex(player, what, token, buff)
dbref player;
char *what, *buff;
const char *token;
{
dbref it;
char *tbuff;
it = match_thing(player, what);
if (!Good_obj(it) ||
(!isPlayer(it) && !nearby_or_control(player, it))) {
strcpy(buff, "#-1 NO MATCH");
} else {
tbuff = exec(it, it, 0, (char *) token, (char **) NULL, 0);
strcpy(buff, tbuff);
free_lbuf(tbuff);
}
}
FUNCTION(fun_obj)
{
process_sex(player, fargs[0], "%o", buff);
}
FUNCTION(fun_poss)
{
process_sex(player, fargs[0], "%p", buff);
}
FUNCTION(fun_subj)
{
process_sex(player, fargs[0], "%s", buff);
}
FUNCTION(fun_aposs)
{
process_sex(player, fargs[0], "%a", buff);
}
/* ---------------------------------------------------------------------------
* fun_mudname: Return the name of the mud.
*/
FUNCTION(fun_mudname)
{
char *bp;
bp = buff;
safe_str(mudconf.mud_name, buff, &bp);
return;
}
/* ---------------------------------------------------------------------------
* fun_lcstr, fun_ucstr, fun_capstr: Lowercase, uppercase, or capitalize str.
*/
FUNCTION(fun_lcstr)
{
char *ap, *bp;
ap = fargs[0];
bp = buff;
while (*ap) {
*bp = ToLower(*ap);
ap++;
bp++;
}
*bp = '\0';
}
FUNCTION(fun_ucstr)
{
char *ap, *bp;
ap = fargs[0];
bp = buff;
while (*ap) {
*bp = ToUpper(*ap);
ap++;
bp++;
}
*bp = '\0';
}
FUNCTION(fun_capstr)
{
strcpy(buff, fargs[0]);
*buff = ToUpper(*buff);
}
/* ---------------------------------------------------------------------------
* fun_lnum: Return a list of numbers.
*/
FUNCTION(fun_lnum)
{
char *bp, tbuf[10], sep;
int bot, top, over, i;
*buff = '\0';
if (nfargs == 0) {
return;
}
mvarargs_preamble("LNUM", 1, 3);
if (nfargs >= 2) {
bot = atoi(fargs[0]);
top = atoi(fargs[1]);
} else {
bot = 0;
top = atoi(fargs[0]);
if (top-- < 1) /* still want to generate if arg is 1 */
return;
}
bp = buff;
over = 0;
if (top == bot) {
ltos(buff, bot);
return;
} else if (top > bot) {
for (i = bot; (i <= top) && !over; i++) {
if (bp != buff) {
safe_chr(sep, buff, &bp);
}
ltos(tbuf, i);
over = safe_str(tbuf, buff, &bp);
}
} else {
for (i = bot; (i >= top) && !over; i--) {
if (bp != buff) {
safe_chr(sep, buff, &bp);
}
ltos(tbuf, i);
over = safe_str(tbuf, buff, &bp);
}
}
*bp = '\0';
}
/* ---------------------------------------------------------------------------
* fun_lattr: Return list of attributes I can see on the object.
*/
FUNCTION(fun_lattr)
{
dbref thing;
int ca;
char *bp;
ATTR *attr;
bp = buff;
/* 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.
*/
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 (bp != buff) {
safe_chr(' ', buff, &bp);
}
safe_str((char *) attr->name, buff, &bp);
}
}
*bp = '\0';
} else {
*buff = '\0';
}
return;
}
/* ---------------------------------------------------------------------------
* fun_objmem: return the following totals for an object or obj/attr:
* memory consumed by attribute text
* memory consumed by attribute overhead (i.e. names, etc.)
* memory consumed by other stuff
* total memory consumed
*
* Current implementation: only the memory consumed by attribute text
* is returned.
*/
FUNCTION(fun_objmem)
{
dbref thing, aowner;
int atr, aflags;
char *abuf;
ATTR *ap;
int bytes_atext = 0;
if (parse_attrib_wild(player, fargs[0], &thing, 0, 0, 1)) {
for (atr = olist_first(); atr != NOTHING; atr = olist_next()) {
ap = atr_num(atr);
if (!ap)
continue;
abuf = atr_get(thing, atr, &aowner, &aflags);
/* Player must be able to read attribute with 'examine' */
if (Examinable(player, thing) &&
Read_attr(player, thing, ap, aowner, aflags))
bytes_atext += strlen(abuf);
free_lbuf(abuf);
}
}
ltos(buff, bytes_atext);
}
/* ---------------------------------------------------------------------------
* do_reverse, fun_reverse, fun_revwords: Reverse things.
*/
static void
do_reverse(from, to)
char *from, *to;
{
char *tp;
tp = to + strlen(from);
*tp-- = '\0';
while (*from) {
*tp-- = *from++;
}
}
FUNCTION(fun_reverse)
{
do_reverse(fargs[0], buff);
}
FUNCTION(fun_revwords)
{
char *temp, *bp, *tp, *t1, sep;
/* If we are passed an empty arglist return a null string */
if (nfargs == 0) {
*buff = '\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;
bp = buff;
while (tp) {
if (bp != buff)
safe_chr(sep, buff, &bp);
t1 = split_token(&tp, sep);
do_reverse(t1, bp);
while (*bp)
bp++;
}
*bp = '\0';
free_lbuf(temp);
}
/* ---------------------------------------------------------------------------
* fun_after, fun_before: Return substring after or before a specified string.
*/
FUNCTION(fun_after)
{
char *bp, *cp, *mp;
int mlen;
if (nfargs == 0) {
*buff = '\0';
return;
}
if (!fn_range_check("AFTER", nfargs, 1, 2, buff))
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 */
*buff = '\0';
return;
}
/* See if what follows is what we are looking for */
if (!strncmp(cp, mp, mlen)) {
/* Yup, return what follows */
bp = cp + mlen;
strcpy(buff, bp);
return;
}
/* Continue search after found first character */
bp = cp + 1;
}
/* Ran off the end without finding it */
*buff = '\0';
return;
}
FUNCTION(fun_before)
{
char *bp, *cp, *mp, *ip;
int mlen;
if (nfargs == 0) {
*buff = '\0';
return;
}
if (!fn_range_check("BEFORE", nfargs, 1, 2, buff))
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 */
strcpy(buff, ip);
return;
}
/* See if what follows is what we are looking for */
if (!strncmp(cp, mp, mlen)) {
/* Yup, return what follows */
*cp = '\0';
strcpy(buff, ip);
return;
}
/* Continue search after found first character */
bp = cp + 1;
}
/* Ran off the end without finding it */
strcpy(buff, ip);
return;
}
/* ---------------------------------------------------------------------------
* fun_max, fun_min: Return maximum (minimum) value.
*/
FUNCTION(fun_max)
{
int i;
NVAL max, val;
if (nfargs < 1) {
strcpy(buff, "#-1 TOO FEW ARGUMENTS");
} else {
max = aton(fargs[0]);
for (i = 0; i < nfargs; i++) {
val = aton(fargs[i]);
if (max < val)
max = val;
}
fval(buff, max);
}
return;
}
FUNCTION(fun_min)
{
int i;
NVAL min, val;
if (nfargs < 1) {
strcpy(buff, "#-1 TOO FEW ARGUMENTS");
} else {
min = aton(fargs[0]);
for (i = 0; i < nfargs; i++) {
val = aton(fargs[i]);
if (min > val)
min = val;
}
fval(buff, min);
}
return;
}
/* ---------------------------------------------------------------------------
* fun_search: Search the db for things, returning a list of what matches
*/
FUNCTION(fun_search)
{
dbref thing;
char *bp, *nbuf;
SEARCH searchparm;
/* Set up for the search. If any errors, abort. */
if (!search_setup(player, fargs[0], &searchparm)) {
strcpy(buff, "#-1 ERROR DURING SEARCH");
return;
}
/* Do the search and report the results */
search_perform(player, cause, &searchparm);
bp = buff;
nbuf = alloc_sbuf("fun_search");
for (thing = olist_first(); thing != NOTHING; thing = olist_next()) {
if (bp == buff) {
*nbuf = '#';
ltos(&nbuf[1], thing);
} else {
nbuf[0] = ' ';
nbuf[1] = '#';
ltos(&nbuf[2], thing);
}
safe_str(nbuf, buff, &bp);
}
*bp = '\0';
free_sbuf(nbuf);
olist_init();
}
/* ---------------------------------------------------------------------------
* fun_stats: Get database size statistics.
*/
FUNCTION(fun_stats)
{
dbref who;
STATS statinfo;
if ((!fargs[0]) || !*fargs[0] || !string_compare(fargs[0], "all")) {
who = NOTHING;
} else {
who = lookup_player(player, fargs[0], 1);
if (who == NOTHING) {
strcpy(buff, "#-1 NOT FOUND");
return;
}
}
if (!get_stats(player, who, &statinfo)) {
strcpy(buff, "#-1 ERROR GETTING STATS");
return;
}
sprintf(buff, "%d %d %d %d %d %d", statinfo.s_total, statinfo.s_rooms,
statinfo.s_exits, statinfo.s_things, statinfo.s_players,
statinfo.s_garbage);
}
/* ---------------------------------------------------------------------------
* fun_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.
*/
FUNCTION(fun_merge)
{
char *str, *rep, *bp;
char c;
/* do length checks first */
if (strlen(fargs[0]) != strlen(fargs[1])) {
strcpy(buff, "#-1 STRING LENGTHS MUST BE EQUAL");
return;
}
if (strlen(fargs[2]) > 1) {
strcpy(buff, "#-1 TOO MANY CHARACTERS");
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], bp = buff;
*str && *rep;
str++, rep++, bp++) {
if (*str == c)
*bp = *rep;
else
*bp = *str;
}
*bp = '\0'; /* terminate */
/* 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(), replaces by word instead of by character.
*/
FUNCTION(fun_splice)
{
char *bp, *p1, *p2, *q1, *q2, sep, osep;
int words, i;
svarargs_preamble("SPLICE", 5);
/* length checks */
if (countwords(fargs[2], sep) > 1) {
strcpy(buff, "#-1 TOO MANY WORDS");
return;
}
words = countwords(fargs[0], sep);
if (words != countwords(fargs[1], sep)) {
strcpy(buff, "#-1 NUMBER OF WORDS MUST BE EQUAL");
return;
}
/* loop through the two lists */
bp = buff;
p1 = fargs[0];
q1 = fargs[1];
for (i = 0; i < words; i++) {
p2 = split_token(&p1, sep);
q2 = split_token(&q1, sep);
if (bp != buff)
safe_chr(osep, buff, &bp);
if (!strcmp(p2, fargs[2]))
safe_str(q2, buff, &bp); /* replace */
else
safe_str(p2, buff, &bp); /* copy */
}
*bp = '\0';
}
/* ---------------------------------------------------------------------------
* fun_munge: combines two lists in an arbitrary manner.
*/
FUNCTION(fun_munge)
{
dbref aowner, thing;
int aflags, anum, nptrs1, nptrs2, nresults, i, j;
ATTR *ap;
char *list1, *list2, *rlist;
char *ptrs1[LBUF_SIZE / 2], *ptrs2[LBUF_SIZE / 2], *results[LBUF_SIZE / 2];
char *atext, *bp, sep, osep;
if ((nfargs == 0) || !fargs[0] || !*fargs[0]) {
*buff = '\0';
return;
}
svarargs_preamble("MUNGE", 5);
/* Find our object and attribute */
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]);
}
if (!ap) {
*buff = '\0';
return;
}
atext = atr_pget(thing, ap->number, &aowner, &aflags);
if (!atext) {
*buff = '\0';
return;
} else if (!*atext || !See_attr(player, thing, ap, aowner, aflags)) {
free_lbuf(atext);
*buff = '\0';
return;
}
/* Copy our lists and chop them up. */
list1 = alloc_lbuf("fun_munge.list1");
list2 = alloc_lbuf("fun_munge.list2");
strcpy(list1, fargs[1]);
strcpy(list2, fargs[2]);
nptrs1 = list2arr(ptrs1, LBUF_SIZE / 2, list1, sep);
nptrs2 = list2arr(ptrs2, LBUF_SIZE / 2, list2, sep);
if (nptrs1 != nptrs2) {
strcpy(buff, "#-1 LISTS MUST BE OF EQUAL SIZE");
free_lbuf(atext);
free_lbuf(list1);
free_lbuf(list2);
return;
}
/* Call the u-function with the first list as %0. */
rlist = exec(player, cause, EV_STRIP | EV_FCHECK | EV_EVAL, atext,
&fargs[1], 1);
/* Now that we have our result, put it back into array form. Search
* through list1 until we find the element position, then copy the
* corresponding element from list2.
*/
nresults = list2arr(results, LBUF_SIZE / 2, rlist, sep);
bp = buff;
for (i = 0; i < nresults; i++) {
for (j = 0; j < nptrs1; j++) {
if (!strcmp(results[i], ptrs1[j])) {
if (bp != buff) {
safe_chr(osep, buff, &bp);
}
safe_str(ptrs2[j], buff, &bp);
ptrs1[j][0] = '\0';
break;
}
}
}
*bp = '\0';
free_lbuf(atext);
free_lbuf(list1);
free_lbuf(list2);
free_lbuf(rlist);
}
/* ---------------------------------------------------------------------------
* fun_repeat: repeats a string
*/
FUNCTION(fun_repeat)
{
int times, i, over;
char *bp;
times = atoi(fargs[1]);
if ((times < 1) || !*fargs[0]) {
*buff = '\0';
} else if (times == 1) {
strcpy(buff, fargs[0]);
} else if (strlen(fargs[0]) * times >= (LBUF_SIZE - 1)) {
strcpy(buff, "#-1 STRING TOO LONG");
} else {
bp = buff;
for (i = 0, over = 0; (i < times) && !over; i++)
over = safe_str(fargs[0], buff, &bp);
*bp = '\0';
}
}
/* ---------------------------------------------------------------------------
* fun_scramble: randomizes the letters in a string.
*/
FUNCTION(fun_scramble)
{
int n, i, j;
char c;
if (!fargs[0] || !*fargs[0]) {
*buff = '\0';
return;
}
strcpy(buff, fargs[0]);
n = strlen(buff);
for (i = 0; i < n; i++) {
j = (random() % (n - i)) + i;
c = buff[i];
buff[i] = buff[j];
buff[j] = c;
}
}
/* ---------------------------------------------------------------------------
* fun_shuffle: randomize order of words in a list.
*/
static void swap(p, q)
char **p;
char **q;
{
/* swaps two points to strings */
char *temp;
temp = *p;
*p = *q;
*q = temp;
}
FUNCTION(fun_shuffle)
{
char *words[LBUF_SIZE];
int n, i, j;
char sep, osep;
if (!nfargs || !fargs[0] || !*fargs[0]) {
*buff = '\0';
return;
}
svarargs_preamble("SHUFFLE", 3);
n = list2arr(words, LBUF_SIZE, fargs[0], sep);
for (i = 0; i < n; i++) {
j = (random() % (n - i)) + i;
swap(&words[i], &words[j]);
}
arr2list(words, n, buff, osep);
}
/* ---------------------------------------------------------------------------
* fun_iter: Make list from evaluating arg2 with each member of arg1.
* NOTE: This function expects that its arguments have not been evaluated.
*
* Also note: parse() is equivalent to this.
*/
static void do_iter(buff, player, cause, list, exprstr, cargs, ncargs,
sep, osep, flag)
char *buff;
dbref player, cause;
char *list, *exprstr;
char *cargs[];
int ncargs;
char sep, osep;
int flag; /* 0 is iter(), 1 is list() */
{
char *curr, *objstring, *buff2, *buff3, *result, *bp, *cp;
char tbuf[8];
int number = 0;
cp = curr = exec(player, cause, EV_STRIP | EV_FCHECK | EV_EVAL, list,
cargs, ncargs);
cp = trim_space_sep(cp, sep);
if (!*cp) {
free_lbuf(curr);
*buff = '\0';
return;
}
bp = buff;
while (cp && (mudstate.func_invk_ctr < mudconf.func_invk_lim)) {
number++;
objstring = split_token(&cp, sep);
buff2 = replace_string(BOUND_VAR, objstring, exprstr);
ltos(tbuf, number);
buff3 = replace_string(LISTPLACE_VAR, tbuf, buff2);
result = exec(player, cause,
EV_STRIP | EV_FCHECK | EV_EVAL, buff3, cargs, ncargs);
free_lbuf(buff2);
free_lbuf(buff3);
if (flag) {
notify(cause, result);
} else {
if (bp != buff)
safe_chr(osep, buff, &bp);
safe_str(result, buff, &bp);
}
free_lbuf(result);
}
free_lbuf(curr);
*bp = '\0';
}
FUNCTION(fun_iter)
{
char sep, osep;
evarargs_preamble("ITER", 2, 4);
do_iter(buff, player, cause, fargs[0], fargs[1], cargs, ncargs,
sep, osep, 0);
}
FUNCTION(fun_list)
{
char sep, osep;
evarargs_preamble("LIST", 2, 4);
do_iter(buff, player, cause, fargs[0], fargs[1], cargs, ncargs,
sep, osep, 1);
}
/* ---------------------------------------------------------------------------
* 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!
*/
FUNCTION(fun_fold)
{
dbref aowner, thing;
int aflags, anum;
ATTR *ap;
char *atext, *result, *curr, *bp, *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) {
*buff = '\0';
return;
}
/* Use it if we can access it, otherwise return an error. */
atext = atr_pget(thing, ap->number, &aowner, &aflags);
if (!atext) {
*buff = '\0';
return;
} else if (!*atext || !See_attr(player, thing, ap, aowner, aflags)) {
free_lbuf(atext);
*buff = '\0';
return;
}
/* Evaluate it using the rest of the passed function args */
cp = curr = fargs[1];
bp = buff;
atextbuf = alloc_lbuf("fun_fold");
strcpy(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 = exec(player, cause, EV_STRIP | EV_FCHECK | EV_EVAL,
atextbuf, clist, 2);
} else {
clist[0] = split_token(&cp, sep);
clist[1] = split_token(&cp, sep);
result = exec(player, cause, EV_STRIP | EV_FCHECK | EV_EVAL,
atextbuf, clist, 2);
}
rstore = result;
result = NULL;
while (cp && (mudstate.func_invk_ctr < mudconf.func_invk_lim)) {
clist[0] = rstore;
clist[1] = split_token(&cp, sep);
strcpy(atextbuf, atext);
result = exec(player, cause,
EV_STRIP | EV_FCHECK | EV_EVAL, atextbuf, clist, 2);
strcpy(rstore, result);
free_lbuf(result);
}
safe_str(rstore, buff, &bp);
free_lbuf(rstore);
free_lbuf(atext);
free_lbuf(atextbuf);
}
/* ---------------------------------------------------------------------------
* fun_filter and fun_fliterbool:
*
* Iteratively perform a function with a list of arguments and return the
* arg, if the function evaluates to 1 or 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 handle_filter(player, cause, arg_func, arg_list, buff,
sep, osep, flag)
dbref player, cause;
char *arg_func, *arg_list;
char *buff;
char sep, osep;
int flag; /* 0 is filter(), 1 is filterbool() */
{
dbref aowner, thing;
int aflags, anum;
ATTR *ap;
char *atext, *result, *curr, *objstring, *bp, *cp, *atextbuf;
/* Two possibilities for the first arg: <obj>/<attr> and <attr>. */
if (parse_attrib(player, arg_func, &thing, &anum)) {
if ((anum == NOTHING) || (!Good_obj(thing)))
ap = NULL;
else
ap = atr_num(anum);
} else {
thing = player;
ap = atr_str(arg_func);
}
/* Make sure we got a good attribute */
if (!ap) {
*buff = '\0';
return;
}
/* Use it if we can access it, otherwise return an error. */
atext = atr_pget(thing, ap->number, &aowner, &aflags);
if (!atext) {
*buff = '\0';
return;
} else if (!*atext || !See_attr(player, thing, ap, aowner, aflags)) {
free_lbuf(atext);
*buff = '\0';
return;
}
/* Now iteratively eval the attrib with the argument list */
cp = curr = trim_space_sep(arg_list, sep);
bp = buff;
atextbuf = alloc_lbuf("handle_filter");
while (cp && (mudstate.func_invk_ctr < mudconf.func_invk_lim)) {
objstring = split_token(&cp, sep);
strcpy(atextbuf, atext);
result = exec(player, cause,
EV_STRIP | EV_FCHECK | EV_EVAL, atextbuf, &objstring, 1);
if ((!flag && (*result == '1')) || (flag && xlate(result))) {
if (bp != buff)
safe_chr(osep, buff, &bp);
safe_str(objstring, buff, &bp);
}
free_lbuf(result);
}
free_lbuf(atext);
free_lbuf(atextbuf);
*bp = '\0';
}
FUNCTION(fun_filter)
{
char sep, osep;
svarargs_preamble("FILTER", 4);
handle_filter(player, cause, fargs[0], fargs[1], buff, sep, osep, 0);
}
FUNCTION(fun_filterbool)
{
char sep, osep;
svarargs_preamble("FILTERBOOL", 4);
handle_filter(player, cause, fargs[0], fargs[1], buff, sep, osep, 1);
}
/* ---------------------------------------------------------------------------
* 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"
*
*/
FUNCTION(fun_map)
{
dbref aowner, thing;
int aflags, anum;
ATTR *ap;
char *atext, *result, *objstring, *bp, *cp, *atextbuf, sep, osep;
svarargs_preamble("MAP", 4);
*buff = '\0';
/* If we don't have anything for a second arg, don't bother. */
if (!fargs[1] || !*fargs[1])
return;
/* 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);
bp = buff;
atextbuf = alloc_lbuf("fun_map");
while (cp && (mudstate.func_invk_ctr < mudconf.func_invk_lim)) {
objstring = split_token(&cp, sep);
strcpy(atextbuf, atext);
result = exec(player, cause,
EV_STRIP | EV_FCHECK | EV_EVAL, atextbuf, &objstring, 1);
if (bp != buff)
safe_chr(osep, buff, &bp);
safe_str(result, buff, &bp);
free_lbuf(result);
}
free_lbuf(atext);
free_lbuf(atextbuf);
*bp = '\0';
}
/* ---------------------------------------------------------------------------
* fun_mix: Like map, but operates on two lists or more lists simultaneously,
* passing the elements as %0, %1, %2, etc.
*/
FUNCTION(fun_mix)
{
dbref aowner, thing;
int aflags, anum, i, lastn, nwords, wc;
ATTR *ap;
char *atext, *result, *os[10], *bp, *atextbuf, sep;
char *cp[10];
/* Check to see if we have an appropriate number of arguments.
* If there are more than three arguments, the last argument is
* ALWAYS assumed to be a delimiter.
*/
if (!fn_range_check("MIX", nfargs, 3, 10, buff)) {
return;
}
if (nfargs < 4) {
sep = ' ';
lastn = nfargs - 1;
} else if (!delim_check(fargs, nfargs, nfargs, &sep, buff, 0,
player, cause, cargs, ncargs)) {
return;
} else {
lastn = nfargs - 2;
}
/* Get the attribute, check the permissions. */
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]);
}
if (!ap) {
*buff = '\0';
return;
}
atext = atr_pget(thing, ap->number, &aowner, &aflags);
if (!atext) {
*buff = '\0';
return;
} else if (!*atext || !See_attr(player, thing, ap, aowner, aflags)) {
free_lbuf(atext);
*buff = '\0';
return;
}
for (i = 0; i < 10; i++)
cp[i] = NULL;
/* process the lists, one element at a time. */
for (i = 1; i <= lastn; i++) {
cp[i] = trim_space_sep(fargs[i], sep);
}
nwords = countwords(cp[1], sep);
for (i = 2; i<= lastn; i++) {
if (countwords(cp[i], sep) != nwords) {
free_lbuf(atext);
strcpy(buff, "#-1 LISTS MUST BE OF EQUAL SIZE");
return;
}
}
atextbuf = alloc_lbuf("fun_mix");
bp = buff;
for (wc = 0;
(wc < nwords) && (mudstate.func_invk_ctr < mudconf.func_invk_lim);
wc++) {
for (i = 1; i <= lastn; i++) {
os[i - 1] = split_token(&cp[i], sep);
}
strcpy(atextbuf, atext);
result = exec(player, cause, EV_STRIP | EV_FCHECK | EV_EVAL,
atextbuf, &(os[0]), lastn);
if (bp != buff)
safe_chr(sep, buff, &bp);
safe_str(result, buff, &bp);
free_lbuf(result);
}
free_lbuf(atext);
free_lbuf(atextbuf);
*bp = '\0';
}
/* ---------------------------------------------------------------------------
* fun_foreach: like map(), but it operates on a string, rather than on a list,
* calling a user-defined function for each character in the string.
* No delimiter is inserted between the results.
*/
FUNCTION(fun_foreach)
{
dbref aowner, thing;
int aflags, anum;
ATTR *ap;
char *atext, *atextbuf, *result, *bp, *cp, *cbuf;
char start_token, end_token;
int in_string = 1;
if (!fn_range_check("FOREACH", nfargs, 2, 4, buff))
return;
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]);
}
*buff = '\0';
if (!ap) {
return;
}
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;
}
atextbuf = alloc_lbuf("fun_foreach");
cbuf = alloc_lbuf("fun_foreach.cbuf");
cp = trim_space_sep(fargs[1], ' ');
bp = buff;
start_token = '\0';
end_token = '\0';
if (nfargs > 2) {
in_string = 0;
start_token = *fargs[2];
}
if (nfargs > 3) {
end_token = *fargs[3];
}
while (cp && *cp && (mudstate.func_invk_ctr < mudconf.func_invk_lim)) {
if (!in_string) {
/* Look for a start token. */
while (*cp && (*cp != start_token)) {
safe_chr(*cp, buff, &bp);
cp++;
}
if (!*cp)
break;
/* Skip to the next character. Don't copy the start token. */
cp++;
if (!*cp)
break;
in_string = 1;
}
if (*cp == end_token) {
/* We've found an end token. Skip over it. Note that it's
* possible to have a start and end token next to one
* another.
*/
cp++;
in_string = 0;
continue;
}
cbuf[0] = *cp++;
cbuf[1] = '\0';
strcpy(atextbuf, atext);
result = exec(player, cause, EV_STRIP | EV_FCHECK | EV_EVAL,
atextbuf, &cbuf, 1);
safe_str(result, buff, &bp);
free_lbuf(result);
}
*bp = '\0';
free_lbuf(atextbuf);
free_lbuf(atext);
free_lbuf(cbuf);
}
/* ---------------------------------------------------------------------------
* fun_edit: Edit text.
*/
FUNCTION(fun_edit)
{
char *tstr;
edit_string(mudconf.ansi_colors ? strip_ansi(fargs[0]) : fargs[0],
&tstr, fargs[1], fargs[2]);
strcpy(buff, tstr);
free_lbuf(tstr);
}
/* ---------------------------------------------------------------------------
* fun_locate: Search for things with the perspective of another obj.
*/
FUNCTION(fun_locate)
{
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 (Wizard(player))
thing = match_thing(player, fargs[0]);
else
thing = match_controlled(player, fargs[0]);
if (!Good_obj(thing)) {
strcpy(buff, "#-1 PERMISSION DENIED");
return;
}
/* Get pre- and post-conditions and modifiers */
for (cp = fargs[2]; *cp; cp++) {
switch (*cp) {
case 'E':
pref_type = TYPE_EXIT;
break;
case 'L':
check_locks = 1;
break;
case 'P':
pref_type = TYPE_PLAYER;
break;
case 'R':
pref_type = TYPE_ROOM;
break;
case 'T':
pref_type = TYPE_THING;
break;
case 'V':
verbose = 1;
break;
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);
*buff = '#';
ltos(&buff[1], what);
}
/* ---------------------------------------------------------------------------
* fun_switch: Return value based on pattern matching (ala @switch)
* NOTE: This function expects that its arguments have not been evaluated.
*/
FUNCTION(fun_switch)
{
int i;
char *mbuff, *tbuf1, *tbuf2;
/* If we don't have at least 2 args, return nothing */
if (nfargs < 2) {
*buff = '\0';
return;
}
/* Evaluate the target in fargs[0] */
mbuff = exec(player, cause, EV_STRIP | EV_FCHECK | EV_EVAL,
fargs[0], cargs, ncargs);
/* Loop through the patterns looking for a match */
for (i = 1; (i < nfargs - 1) && fargs[i] && fargs[i + 1]; i += 2) {
tbuf1 = exec(player, cause, EV_STRIP | EV_FCHECK | EV_EVAL,
fargs[i], cargs, ncargs);
if (quick_wild(tbuf1, mbuff)) {
free_lbuf(tbuf1);
tbuf1 = replace_string(SWITCH_VAR, mbuff, fargs[i + 1]);
tbuf2 = exec(player, cause, EV_STRIP | EV_FCHECK | EV_EVAL,
tbuf1, cargs, ncargs);
strcpy(buff, tbuf2);
free_lbuf(mbuff);
free_lbuf(tbuf1);
free_lbuf(tbuf2);
return;
}
free_lbuf(tbuf1);
}
/* Nope, return the default if there is one */
if ((i < nfargs) && fargs[i]) {
tbuf1 = replace_string(SWITCH_VAR, mbuff, fargs[i]);
tbuf2 = exec(player, cause, EV_STRIP | EV_FCHECK | EV_EVAL,
tbuf1, cargs, ncargs);
strcpy(buff, tbuf2);
free_lbuf(tbuf1);
free_lbuf(tbuf2);
} else {
*buff = '\0';
}
free_lbuf(mbuff);
return;
}
/* ---------------------------------------------------------------------------
* fun_ifelse: Sort of like switch, but works on a true/false basis.
* NOTE: This function expects that its arguments have not been evaluated.
*
* Derived from TinyMUX.
*/
FUNCTION(fun_ifelse)
{
char *mbuff, *tbuff, *str;
/* Evaluate our target. */
mbuff = exec(player, cause, EV_STRIP | EV_FCHECK | EV_EVAL,
fargs[0], cargs, ncargs);
if (!mbuff || !*mbuff || !xlate(mbuff)) {
str = fargs[2];
} else {
str = fargs[1];
}
tbuff = exec(player, cause, EV_STRIP | EV_FCHECK | EV_EVAL,
str, cargs, ncargs);
strcpy(buff, tbuff);
free_lbuf(tbuff);
free_lbuf(mbuff);
}
/* ---------------------------------------------------------------------------
* fun_space: Make spaces.
*/
FUNCTION(fun_space)
{
int num;
char *cp;
if (!fargs[0] || !(*fargs[0])) {
num = 1;
} else {
num = atoi(fargs[0]);
}
if (num < 1) {
/* 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 = buff; num > 0; num--)
*cp++ = ' ';
*cp = '\0';
return;
}
/* ---------------------------------------------------------------------------
* fun_idle, fun_conn: return seconds idle or connected.
*/
FUNCTION(fun_idle)
{
dbref target;
target = lookup_player(player, fargs[0], 1);
if (Good_obj(target) && Dark(target) && !Wizard(player))
target = NOTHING;
ltos(buff, fetch_idle(target));
}
FUNCTION(fun_conn)
{
dbref target;
target = lookup_player(player, fargs[0], 1);
if (Good_obj(target) && Dark(target) && !Wizard(player))
target = NOTHING;
ltos(buff, 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(s1, s2)
const void *s1, *s2;
{
return strcmp(*(char **) s1, *(char **) s2);
}
static int
f_comp(s1, s2)
const void *s1, *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(s1, s2)
const void *s1, *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(s, n, sort_type)
char *s[];
int n, 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 *) XMALLOC(n * sizeof(i_rec), "do_asort");
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 *) XMALLOC(n * sizeof(i_rec), "do_asort.2");
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 *) XMALLOC(n * sizeof(f_rec), "do_asort.3");
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;
}
}
FUNCTION(fun_sort)
{
int nitems, sort_type;
char *list, sep, osep;
char *ptrs[LBUF_SIZE / 2];
/* If we are passed an empty arglist return a null string */
if (nfargs == 0) {
*buff = '\0';
return;
}
if (!fn_range_check("SORT", nfargs, 1, 4, buff))
return;
if (!delim_check(fargs, nfargs, 3, &sep, buff, 0,
player, cause, cargs, ncargs))
return;
if (nfargs < 4)
osep = sep;
else if (!delim_check(fargs, nfargs, 4, &osep, buff, 0,
player, cause, cargs, ncargs))
return;
/* Convert the list to an array */
list = alloc_lbuf("fun_sort");
strcpy(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, osep);
free_lbuf(list);
}
static char ucomp_buff[LBUF_SIZE];
static dbref ucomp_cause;
static dbref ucomp_player;
static int
u_comp(s1, s2)
const void *s1, *s2;
{
/* Note that this function is for use in conjunction with our own
* sane_qsort routine, NOT with the standard library qsort!
*/
char *result, tbuf[LBUF_SIZE], *elems[2];
int n;
/* Prevent hideously large recursive sorts. */
if ((mudstate.func_invk_ctr > mudconf.func_invk_lim) ||
(mudstate.func_nest_lev > mudconf.func_nest_lim))
return 0;
elems[0] = (char *) s1;
elems[1] = (char *) s2;
strcpy(tbuf, ucomp_buff);
result = exec(ucomp_player, ucomp_cause, EV_STRIP | EV_FCHECK | EV_EVAL,
tbuf, &(elems[0]), 2);
if (!result)
n = 0;
else {
n = atoi(result);
free_lbuf(result);
}
return n;
}
static void sane_qsort(array, left, right, compare)
void *array[];
int left, right;
int (*compare)();
{
/* Andrew Molitor's qsort, which doesn't require transitivity between
* comparisons (essential for preventing crashes due to boneheads
* who write comparison functions where a > b doesn't mean b < a).
*/
int i, last;
void *tmp;
loop:
if (left >= right)
return;
/* Pick something at random at swap it into the leftmost slot */
/* This is the pivot, we'll put it back in the right spot later */
i = random() % (1 + (right - left));
tmp = array[left + i];
array[left + i] = array[left];
array[left] = tmp;
last = left;
for(i = left+1; i <= right; i++) {
/* Walk the array, looking for stuff that's less than our */
/* pivot. If it is, swap it with the next thing along */
if ((*compare)(array[i], array[left]) < 0) {
last++;
if (last == i)
continue;
tmp = array[last];
array[last] = array[i];
array[i] = tmp;
}
}
/* Now we put the pivot back, it's now in the right spot, we never */
/* need to look at it again, trust me. */
tmp = array[last];
array[last] = array[left];
array[left] = tmp;
/* At this point everything underneath the 'last' index is < the */
/* entry at 'last' and everything above it is not < it. */
if ((last - left) < (right - last)) {
sane_qsort(array, left, last-1, compare);
left = last+1;
goto loop;
} else {
sane_qsort(array, last+1, right, compare);
right = last - 1;
goto loop;
}
}
FUNCTION(fun_sortby)
{
char *atext, *list, *ptrs[LBUF_SIZE / 2], sep, osep;
int nptrs, aflags, anum;
dbref thing, aowner;
ATTR *ap;
if ((nfargs == 0) || !fargs[0] || !*fargs[0]) {
*buff = '\0';
return;
}
svarargs_preamble("SORTBY", 4);
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]);
}
if (!ap) {
*buff = '\0';
return;
}
atext = atr_pget(thing, ap->number, &aowner, &aflags);
if (!atext) {
*buff = '\0';
return;
} else if (!*atext || !See_attr(player, thing, ap, aowner, aflags)) {
free_lbuf(atext);
*buff = '\0';
return;
}
strcpy(ucomp_buff, atext);
ucomp_player = thing;
ucomp_cause = cause;
list = alloc_lbuf("fun_sortby");
strcpy(list, fargs[1]);
nptrs = list2arr(ptrs, LBUF_SIZE / 2, list, sep);
if (nptrs > 1) /* pointless to sort less than 2 elements */
sane_qsort((void *) ptrs, 0, nptrs-1, u_comp);
arr2list(ptrs, nptrs, buff, osep);
free_lbuf(list);
free_lbuf(atext);
}
/*---------------------------------------------------------------------------
* fun_setunion, fun_setdiff, fun_setinter: Set management.
*/
#define SET_UNION 1
#define SET_INTERSECT 2
#define SET_DIFF 3
static void
handle_sets(fargs, buff, oper, sep, osep)
char *fargs[], *buff, sep, osep;
int oper;
{
char *list1, *list2, *p, *oldp;
char *ptrs1[LBUF_SIZE], *ptrs2[LBUF_SIZE];
int i1, i2, n1, n2, val;
list1 = alloc_lbuf("fun_setunion.1");
strcpy(list1, fargs[0]);
n1 = list2arr(ptrs1, LBUF_SIZE, list1, sep);
do_asort(ptrs1, n1, ALPHANUM_LIST);
list2 = alloc_lbuf("fun_setunion.2");
strcpy(list2, fargs[1]);
n2 = list2arr(ptrs2, LBUF_SIZE, list2, sep);
do_asort(ptrs2, n2, ALPHANUM_LIST);
i1 = i2 = 0;
oldp = p = buff;
*p = '\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, &p);
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 (p != buff) {
safe_chr(osep, buff, &p);
}
oldp = p;
if (strcmp(ptrs1[i1], ptrs2[i2]) < 0) {
safe_str(ptrs1[i1], buff, &p);
i1++;
} else {
safe_str(ptrs2[i2], buff, &p);
i2++;
}
*p = '\0';
}
}
/* Copy rest of remaining list, stripping duplicates
*/
for (; i1 < n1; i1++) {
if (strcmp(oldp, ptrs1[i1])) {
if (p != buff) {
safe_chr(osep, buff, &p);
}
oldp = p;
safe_str(ptrs1[i1], buff, &p);
*p = '\0';
}
}
for (; i2 < n2; i2++) {
if (strcmp(oldp, ptrs2[i2])) {
if (p != buff) {
safe_chr(osep, buff, &p);
}
oldp = p;
safe_str(ptrs2[i2], buff, &p);
*p = '\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 (p != buff) {
safe_chr(osep, buff, &p);
}
oldp = p;
safe_str(ptrs1[i1], buff, &p);
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 (p != buff) {
safe_chr(osep, buff, &p);
}
safe_str(ptrs1[i1], buff, &p);
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 (p != buff) {
safe_chr(osep, buff, &p);
}
safe_str(ptrs1[i1], buff, &p);
oldp = ptrs1[i1];
i1++;
while ((i1 < n1) && !strcmp(ptrs1[i1], oldp))
i1++;
}
}
*p = '\0';
free_lbuf(list1);
free_lbuf(list2);
return;
}
FUNCTION(fun_setunion)
{
char sep, osep;
svarargs_preamble("SETUNION", 4);
handle_sets(fargs, buff, SET_UNION, sep, osep);
return;
}
FUNCTION(fun_setdiff)
{
char sep, osep;
svarargs_preamble("SETDIFF", 4);
handle_sets(fargs, buff, SET_DIFF, sep, osep);
return;
}
FUNCTION(fun_setinter)
{
char sep, osep;
svarargs_preamble("SETINTER", 4);
handle_sets(fargs, buff, SET_INTERSECT, sep, osep);
return;
}
/* ---------------------------------------------------------------------------
* rjust, ljust, center: Justify or center text, specifying fill character
*/
FUNCTION(fun_ljust)
{
int spaces, i, over;
char *bp, sep;
varargs_preamble("LJUST", 3);
spaces = atoi(fargs[1]) -
strlen(mudconf.ansi_colors ? strip_ansi(fargs[0]) : fargs[0]);
/* Sanitize number of spaces */
if (spaces <= 0) {
/* no padding needed, just return string */
strcpy(buff, fargs[0]);
return;
} else if (spaces > LBUF_SIZE) {
spaces = LBUF_SIZE;
}
bp = buff;
over = safe_str(fargs[0], buff, &bp);
for (i = 0; (i < spaces) && !over; i++) {
over = safe_chr_fn(sep, buff, &bp);
}
*bp = '\0';
}
FUNCTION(fun_rjust)
{
int spaces, i, over;
char *bp, sep;
varargs_preamble("RJUST", 3);
spaces = atoi(fargs[1]) -
strlen(mudconf.ansi_colors ? strip_ansi(fargs[0]) : fargs[0]);
/* Sanitize number of spaces */
if (spaces <= 0) {
/* no padding needed, just return string */
strcpy(buff, fargs[0]);
return;
} else if (spaces > LBUF_SIZE) {
spaces = LBUF_SIZE;
}
bp = buff;
for (i = 0, over = 0; (i < spaces) && !over; i++) {
over = safe_chr_fn(sep, buff, &bp);
}
safe_str(fargs[0], buff, &bp);
*bp = '\0';
}
FUNCTION(fun_center)
{
char *p, sep;
int i, len, lead_chrs, trail_chrs, width, over;
varargs_preamble("CENTER", 3);
width = atoi(fargs[1]);
len = strlen(mudconf.ansi_colors ? strip_ansi(fargs[0]) : fargs[0]);
if (len >= width) {
strcpy(buff, fargs[0]);
return;
}
p = buff;
lead_chrs = (width / 2) - (len / 2) + .5;
for (i = 0, over = 0; (i < lead_chrs) && !over; i++) {
over = safe_chr_fn(sep, buff, &p);
}
over = safe_str(fargs[0], buff, &p);
trail_chrs = width - lead_chrs - len;
for (i = 0; (i < trail_chrs) && !over; i++) {
safe_chr(sep, buff, &p);
}
*p = '\0';
}
/* ---------------------------------------------------------------------------
* pemit and remit: Side-effect functions for sending messages.
*/
FUNCTION(fun_pemit)
{
pemit_list(player, cause, fargs[0], fargs[1], 0);
*buff = '\0';
}
FUNCTION(fun_remit)
{
pemit_list(player, cause, fargs[0], fargs[1], 1);
*buff = '\0';
}
/* ---------------------------------------------------------------------------
* tel: Side-effect function for teleportation.
*/
FUNCTION(fun_tel)
{
do_teleport(player, cause, 0, fargs[0], fargs[1]);
*buff = '\0';
}
/* ---------------------------------------------------------------------------
* Side-effect function for linking objects.
*/
FUNCTION(fun_link)
{
do_link(player, cause, 0, fargs[0], fargs[1]);
*buff = '\0';
}
/* ---------------------------------------------------------------------------
* wipe: Like @wipe, but in function form.
*/
FUNCTION(fun_wipe)
{
do_wipe(player, cause, 0, fargs[0]);
*buff = '\0';
}
/* ---------------------------------------------------------------------------
* set: Side-effect function for setting attributes.
*/
FUNCTION(fun_set)
{
do_set(player, cause, SET_QUIET, fargs[0], fargs[1]);
*buff = '\0';
}
/* ---------------------------------------------------------------------------
* setq, setr, r: set and read global registers.
*/
INLINE static void do_setq(numstr, qval, buff, flag)
char *numstr;
char *qval;
char *buff;
int flag; /* 0 if setq, 1 if setr */
{
int regnum;
regnum = atoi(numstr);
if ((regnum < 0) || (regnum >= MAX_GLOBAL_REGS)) {
strcpy(buff, "#-1 INVALID GLOBAL REGISTER");
} else {
if (!mudstate.global_regs[regnum])
mudstate.global_regs[regnum] =
alloc_lbuf("do_setq");
strcpy(mudstate.global_regs[regnum], qval);
if (flag) {
strcpy(buff, qval);
} else {
*buff = '\0';
}
}
}
FUNCTION(fun_setq)
{
do_setq(fargs[0], fargs[1], buff, 0);
}
FUNCTION(fun_setr)
{
do_setq(fargs[0], fargs[1], buff, 1);
}
FUNCTION(fun_r)
{
int regnum;
regnum = atoi(fargs[0]);
if ((regnum < 0) || (regnum >= MAX_GLOBAL_REGS)) {
strcpy(buff, "#-1 INVALID GLOBAL REGISTER");
} else if (mudstate.global_regs[regnum]) {
strcpy(buff, mudstate.global_regs[regnum]);
} else {
*buff = '\0';
}
}
/* ---------------------------------------------------------------------------
* isword: is every character in the argument a letter?
*/
FUNCTION(fun_isword)
{
char *p;
for (p = fargs[0]; *p; p++) {
if (!isalpha(*p)) {
strcpy(buff, "0");
return;
}
}
strcpy(buff, "1");
}
/* ---------------------------------------------------------------------------
* isnum: is the argument a number?
*/
FUNCTION(fun_isnum)
{
strcpy(buff, (is_number(fargs[0]) ? "1" : "0"));
}
/* ---------------------------------------------------------------------------
* isdbref: is the argument a valid dbref?
*/
FUNCTION(fun_isdbref)
{
char *p;
dbref dbitem;
p = fargs[0];
if (*p++ == NUMBER_TOKEN) {
if (*p) { /* just the string '#' won't do! */
dbitem = parse_dbref(p);
if (Good_obj(dbitem)) {
strcpy(buff, "1");
return;
}
}
}
strcpy(buff, "0");
}
/* ---------------------------------------------------------------------------
* trim: trim off unwanted white space.
*/
FUNCTION(fun_trim)
{
char *p, *lastchar, *q, sep;
int trim;
if (nfargs == 0) {
*buff = '\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;
}
}
strcpy(buff, q);
}
/* ---------------------------------------------------------------------------
* fun_squish: Squash occurrences of a given character down to 1.
* We do this both on leading and trailing chars, as well as
* internal ones; if the player wants to trim off the leading
* and trailing as well, they can always call trim().
*/
FUNCTION(fun_squish)
{
char *bp, *tp, sep;
if (nfargs == 0) {
*buff = '\0';
return;
}
varargs_preamble("SQUISH", 2);
bp = buff;
tp = fargs[0];
while (*tp) {
while (*tp && (*tp != sep)) /* copy non-seps */
*bp++ = *tp++;
if (!*tp) { /* end of string */
*bp = '\0';
return;
}
/* We've hit a sep char. Copy it, then skip to the next non-sep. */
*bp++ = *tp++;
while (*tp && (*tp == sep))
tp++;
}
*bp = '\0';
}
/* ---------------------------------------------------------------------------
* Object stack functions.
*/
void stack_clr(thing)
dbref thing;
{
STACK *sp, *tp, *xp;
sp = stack_get(thing);
if (sp) {
for (tp = sp; tp != NULL; ) {
XFREE(tp->data, "stack_clr_data");
xp = tp;
tp = tp->next;
XFREE(xp, "stack_clr");
}
nhashdelete(thing, &mudstate.objstack_htab);
}
}
static void stack_set(thing, sp)
dbref thing;
STACK *sp;
{
STACK *xsp;
int stat;
if (!sp) {
nhashdelete(thing, &mudstate.objstack_htab);
return;
}
xsp = stack_get(thing);
if (xsp) {
stat = nhashrepl(thing, (int *) sp, &mudstate.objstack_htab);
} else {
stat = nhashadd(thing, (int *) sp, &mudstate.objstack_htab);
}
if (stat < 0) { /* failure for some reason */
STARTLOG(LOG_BUGS, "STK", "SET")
log_name(thing);
ENDLOG
stack_clr(thing);
}
}
FUNCTION(fun_empty)
{
dbref it;
xvarargs_preamble("EMPTY", 0, 1);
*buff = '\0';
if (!fargs[0]) {
it = player;
} else {
stack_object(player, it);
}
stack_clr(it);
}
FUNCTION(fun_items)
{
dbref it;
int i;
STACK *sp;
*buff = '\0';
if (!fargs[0]) {
it = player;
} else {
stack_object(player, it);
}
for (i = 0, sp = stack_get(it); sp != NULL; sp = sp->next, i++)
;
ltos(buff, i);
}
FUNCTION(fun_push)
{
dbref it;
char *data;
STACK *sp;
xvarargs_preamble("PUSH", 1, 2);
*buff = '\0';
if (!fargs[1]) {
it = player;
data = fargs[0];
} else {
stack_object(player, it);
data = fargs[1];
}
sp = (STACK *) XMALLOC(sizeof(STACK), "stack_push");
if (!sp) /* out of memory, ouch */
return;
sp->next = stack_get(it);
sp->data = (char *) XMALLOC(sizeof(char *) * (strlen(data) + 1),
"stack_push_data");
if (! sp->data)
return;
strcpy(sp->data, data);
stack_set(it, sp);
}
FUNCTION(fun_dup)
{
dbref it;
STACK *hp; /* head of stack */
STACK *tp; /* temporary stack pointer */
STACK *sp; /* new stack element */
int pos, count = 0;
xvarargs_preamble("DUP", 0, 2);
*buff = '\0';
if (!fargs[0]) {
it = player;
} else {
stack_object(player, it);
}
if (!fargs[1] || !*fargs[1]) {
pos = 0;
} else {
pos = atoi(fargs[1]);
}
hp = stack_get(it);
for (tp = hp; (count != pos) && (tp != NULL); count++, tp = tp->next)
;
if (!tp) {
notify_quiet(player, "No such item on stack.");
return;
}
sp = (STACK *) XMALLOC(sizeof(STACK), "stack_dup");
if (!sp)
return;
sp->next = hp;
sp->data = (char *) XMALLOC(sizeof(char *) * (strlen(tp->data) + 1),
"stack_dup_data");
if (!sp->data)
return;
strcpy(sp->data, tp->data);
stack_set(it, sp);
}
FUNCTION(fun_swap)
{
dbref it;
STACK *sp, *tp;
xvarargs_preamble("SWAP", 0, 1);
*buff = '\0';
if (!fargs[0]) {
it = player;
} else {
stack_object(player, it);
}
sp = stack_get(it);
if (!sp || (sp->next == NULL)) {
notify_quiet(player, "Not enough items on stack.");
return;
}
tp = sp->next;
sp->next = tp->next;
tp->next = sp;
stack_set(it, tp);
}
static void handle_pop(player, cause, buff, fargs, flag)
dbref player, cause;
char *buff;
char *fargs[];
int flag; /* if flag, don't copy */
{
dbref it;
int pos, count = 0;
STACK *sp;
STACK *prev = NULL;
*buff = '\0';
if (!fargs[0]) {
it = player;
} else {
stack_object(player, it);
}
if (!fargs[1] || !*fargs[1]) {
pos = 0;
} else {
pos = atoi(fargs[1]);
}
sp = stack_get(it);
if (!sp)
return;
while (count != pos) {
if (!sp)
return;
prev = sp;
sp = sp->next;
count++;
}
if (!sp)
return;
if (!flag)
strcpy(buff, sp->data);
if (count == 0) {
stack_set(it, sp->next);
} else {
prev->next = sp->next;
}
XFREE(sp->data, "stack_pop_data");
XFREE(sp, "stack_pop");
}
FUNCTION(fun_pop)
{
xvarargs_preamble("POP", 0, 2);
handle_pop(player, cause, buff, fargs, 0);
}
FUNCTION(fun_toss)
{
xvarargs_preamble("TOSS", 0, 2);
handle_pop(player, cause, buff, fargs, 1);
}
FUNCTION(fun_popn)
{
dbref it;
int pos, nitems, i, count = 0, over = 0;
STACK *sp, *tp, *xp;
STACK *prev = NULL;
char sep, *bp;
varargs_preamble("POPN", 4);
*buff = '\0';
stack_object(player, it);
pos = atoi(fargs[1]);
nitems = atoi(fargs[2]);
sp = stack_get(it);
if (!sp)
return;
while (count != pos) {
if (!sp)
return;
prev = sp;
sp = sp->next;
count++;
}
if (!sp)
return;
/* We've now hit the start item, the first item. Copy 'em off. */
for (bp = buff, i = 0, tp = sp;
(i < nitems) && (tp != NULL);
i++) {
if (!over) {
/* We have to pop off the items regardless of whether
* or not there's an overflow, but we can save ourselves
* some copying if so.
*/
if (bp != buff)
safe_chr(sep, buff, &bp);
over = safe_str(tp->data, buff, &bp);
}
xp = tp;
tp = tp->next;
XFREE(xp->data, "stack_popn_data");
XFREE(xp, "stack_popn");
}
/* Relink the chain. */
*bp = '\0';
if (count == 0) {
stack_set(it, tp);
} else {
prev->next = tp;
}
}
FUNCTION(fun_peek)
{
dbref it;
int pos, count = 0;
STACK *sp;
xvarargs_preamble("POP", 0, 2);
*buff = '\0';
if (!fargs[0]) {
it = player;
} else {
stack_object(player, it);
}
if (!fargs[1] || !*fargs[1]) {
pos = 0;
} else {
pos = atoi(fargs[1]);
}
sp = stack_get(it);
if (!sp)
return;
while (count != pos) {
if (!sp)
return;
sp = sp->next;
count++;
}
if (!sp)
return;
strcpy(buff, sp->data);
}
FUNCTION(fun_lstack)
{
char sep;
dbref it;
STACK *sp;
char *bp;
int over = 0;
mvarargs_preamble("LSTACK", 0, 2);
*buff = '\0';
if (!fargs[0]) {
it = player;
} else {
stack_object(player, it);
}
bp = buff;
for (sp = stack_get(it); (sp != NULL) && !over; sp = sp->next) {
if (bp != buff)
safe_chr(sep, buff, &bp);
over = safe_str(sp->data, buff, &bp);
}
*bp = '\0';
}
/* ---------------------------------------------------------------------------
* ANSI functions. Derived from PennMUSH/TinyMUX.
*/
FUNCTION(fun_ansi)
{
char *bufc;
char *s;
if (!mudconf.ansi_colors) {
strcpy(buff, fargs[1]);
return;
}
s = fargs[0];
bufc = buff;
while (*s) {
switch (*s) {
case 'h':
safe_str(ANSI_HILITE, buff, &bufc);
break;
case 'i':
safe_str(ANSI_INVERSE, buff, &bufc);
break;
case 'f':
safe_str(ANSI_BLINK, buff, &bufc);
break;
case 'n':
safe_str(ANSI_NORMAL, buff, &bufc);
break;
case 'x':
safe_str(ANSI_BLACK, buff, &bufc);
break;
case 'r':
safe_str(ANSI_RED, buff, &bufc);
break;
case 'g':
safe_str(ANSI_GREEN, buff, &bufc);
break;
case 'y':
safe_str(ANSI_YELLOW, buff, &bufc);
break;
case 'b':
safe_str(ANSI_BLUE, buff, &bufc);
break;
case 'm':
safe_str(ANSI_MAGENTA, buff, &bufc);
break;
case 'c':
safe_str(ANSI_CYAN, buff, &bufc);
break;
case 'w':
safe_str(ANSI_WHITE, buff, &bufc);
break;
case 'X':
safe_str(ANSI_BBLACK, buff, &bufc);
break;
case 'R':
safe_str(ANSI_BRED, buff, &bufc);
break;
case 'G':
safe_str(ANSI_BGREEN, buff, &bufc);
break;
case 'Y':
safe_str(ANSI_BYELLOW, buff, &bufc);
break;
case 'B':
safe_str(ANSI_BBLUE, buff, &bufc);
break;
case 'M':
safe_str(ANSI_BMAGENTA, buff, &bufc);
break;
case 'C':
safe_str(ANSI_BCYAN, buff, &bufc);
break;
case 'W':
safe_str(ANSI_BWHITE, buff, &bufc);
break;
}
s++;
}
safe_str(fargs[1], buff, &bufc);
safe_str(ANSI_NORMAL, buff, &bufc);
*bufc = '\0';
}
FUNCTION(fun_stripansi)
{
/* strip ANSI codes regardless of whether or not ANSI is on */
strcpy(buff, strip_ansi(fargs[0]));
}
/* ---------------------------------------------------------------------------
* 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},
{"AND", fun_and, 0, FN_VARARGS, CA_PUBLIC},
{"ANDBOOL", fun_andbool, 0, FN_VARARGS, CA_PUBLIC},
{"ANDFLAGS", fun_andflags, 2, 0, CA_PUBLIC},
{"ANSI", fun_ansi, 2, 0, CA_PUBLIC},
{"APOSS", fun_aposs, 1, 0, CA_PUBLIC},
{"ASIN", fun_asin, 1, 0, CA_PUBLIC},
{"ATAN", fun_atan, 1, 0, CA_PUBLIC},
{"BAND", fun_band, 2, 0, CA_PUBLIC},
{"BEFORE", fun_before, 0, FN_VARARGS, CA_PUBLIC},
{"BNAND", fun_bnand, 2, 0, CA_PUBLIC},
{"BOR", fun_bor, 0, 2, CA_PUBLIC},
{"CAPSTR", fun_capstr, -1, 0, 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},
{"COMP", fun_comp, 2, 0, CA_PUBLIC},
{"CON", fun_con, 1, 0, CA_PUBLIC},
{"CONN", fun_conn, 1, 0, CA_PUBLIC},
{"CONTROLS", fun_controls, 2, 0, CA_PUBLIC},
{"CONVSECS", fun_convsecs, 1, 0, CA_PUBLIC},
{"CONVTIME", fun_convtime, 1, 0, CA_PUBLIC},
{"COS", fun_cos, 1, 0, CA_PUBLIC},
{"DEC", fun_dec, 1, 0, CA_PUBLIC},
{"DEFAULT", fun_default, 2, FN_NO_EVAL, CA_PUBLIC},
{"DELETE", fun_delete, 3, 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},
{"DUP", fun_dup, 0, FN_VARARGS, CA_PUBLIC},
{"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},
{"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},
{"FDIV", fun_fdiv, 2, 0, CA_PUBLIC},
{"FILTER", fun_filter, 0, FN_VARARGS, CA_PUBLIC},
{"FILTERBOOL", fun_filterbool, 0, FN_VARARGS, 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},
{"FINDABLE", fun_findable, 2, 0, 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},
{"GT", fun_gt, 2, 0, CA_PUBLIC},
{"GTE", fun_gte, 2, 0, CA_PUBLIC},
{"HASATTR", fun_hasattr, 2, 0, CA_PUBLIC},
{"HASFLAG", fun_hasflag, 2, 0, CA_PUBLIC},
{"HOME", fun_home, 1, 0, CA_PUBLIC},
#ifdef PUEBLO_SUPPORT
{"HTML_ESCAPE", fun_html_escape, -1, 0, CA_PUBLIC},
{"HTML_UNESCAPE", fun_html_unescape, -1, 0, CA_PUBLIC},
#endif /* PUEBLO_SUPPORT */
{"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},
{"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},
{"LASTCREATE", fun_lastcreate, 2, 0, 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},
{"LEFT", fun_left, 2, 0, CA_PUBLIC},
{"LEXITS", fun_lexits, 1, 0, CA_PUBLIC},
{"LINK", fun_link, 2, 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},
{"LN", fun_ln, 1, 0, CA_PUBLIC},
{"LNUM", fun_lnum, 0, FN_VARARGS, CA_PUBLIC},
{"LOC", fun_loc, 1, 0, CA_PUBLIC},
{"LOCATE", fun_locate, 3, 0, CA_PUBLIC},
{"LOCK", fun_lock, 1, 0, CA_PUBLIC},
{"LOG", fun_log, 1, 0, CA_PUBLIC},
{"LPOS", fun_lpos, 2, 0, CA_PUBLIC},
{"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_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},
{"NOTBOOL", fun_notbool, 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},
{"ORBOOL", fun_orbool, 0, FN_VARARGS, CA_PUBLIC},
{"ORFLAGS", fun_orflags, 2, 0, CA_PUBLIC},
{"OWNER", fun_owner, 1, 0, CA_PUBLIC},
{"PARENT", fun_parent, 1, 0, CA_PUBLIC},
{"PARSE", fun_iter, 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},
{"POP", fun_pop, 0, FN_VARARGS, CA_PUBLIC},
{"POPN", fun_popn, 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},
{"PROGRAMMER", fun_programmer, 1, 0, CA_PUBLIC},
{"PUSH", fun_push, 0, FN_VARARGS, CA_PUBLIC},
{"R", fun_r, 1, 0, CA_PUBLIC},
{"RAND", fun_rand, 1, 0, CA_PUBLIC},
{"REGMATCH", fun_regmatch, 0, FN_VARARGS, CA_PUBLIC},
{"REMIT", fun_remit, 2, 0, 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},
{"RESTARTS", fun_restarts, 0, 0, CA_PUBLIC},
{"RESTARTTIME", fun_restarttime, 0, 0, CA_PUBLIC},
{"REVERSE", fun_reverse, -1, 0, CA_PUBLIC},
{"REVWORDS", fun_revwords, 0, FN_VARARGS, CA_PUBLIC},
{"RIGHT", fun_right, 2, 0, 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},
{"SEES", fun_sees, 2, 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},
{"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, 0, FN_VARARGS, CA_PUBLIC},
{"STARTTIME", fun_starttime, 0, 0, CA_PUBLIC},
{"STATS", fun_stats, 1, 0, CA_PUBLIC},
{"STRIPANSI", fun_stripansi, 1, 0, CA_PUBLIC},
{"STRLEN", fun_strlen, -1, 0, CA_PUBLIC},
{"STRMATCH", fun_strmatch, 2, 0, CA_PUBLIC},
{"SUB", fun_sub, 2, 0, CA_PUBLIC},
{"SUBJ", fun_subj, 1, 0, CA_PUBLIC},
{"SWAP", fun_swap, 0, FN_VARARGS, CA_PUBLIC},
{"SWITCH", fun_switch, 0, FN_VARARGS | FN_NO_EVAL,
CA_PUBLIC},
{"T", fun_t, 1, 0, CA_PUBLIC},
{"TAN", fun_tan, 1, 0, CA_PUBLIC},
#ifdef TCL_INTERP_SUPPORT
{"TCLCLEAR", fun_tclclear, 0, 0, CA_TICKLER},
{"TCLEVAL", fun_tcleval, 0, FN_VARARGS, CA_TICKLER},
{"TCLPARAMS", fun_tclparams, 0, FN_VARARGS, CA_TICKLER},
{"TCLREGS", fun_tclregs, 0, 0, CA_TICKLER},
#endif /* TCL_INTERP_SUPPORT */
{"TEL", fun_tel, 2, 0, CA_PUBLIC},
{"TIME", fun_time, 0, 0, CA_PUBLIC},
{"TOSS", fun_toss, 0, FN_VARARGS, CA_PUBLIC},
{"TRANSLATE", fun_translate, 2, 0, CA_PUBLIC},
{"TRIM", fun_trim, 0, FN_VARARGS, 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},
#ifdef PUEBLO_SUPPORT
{"URL_ESCAPE", fun_url_escape, -1, 0, CA_PUBLIC},
{"URL_UNESCAPE", fun_url_unescape, -1, 0, CA_PUBLIC},
#endif /* PUEBLO_SUPPORT */
{"V", fun_v, 1, 0, CA_PUBLIC},
{"VADD", fun_vadd, 0, FN_VARARGS, CA_PUBLIC},
{"VDIM", fun_vdim, 0, FN_VARARGS, CA_PUBLIC},
{"VDOT", fun_vdot, 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},
{"WIPE", fun_wipe, 1, 0, CA_PUBLIC},
{"WORDPOS", fun_wordpos, 0, FN_VARARGS, CA_PUBLIC},
{"WORDS", fun_words, 0, FN_VARARGS, CA_PUBLIC},
{"XCON", fun_xcon, 3, 0, CA_PUBLIC},
{"XOR", fun_xor, 0, FN_VARARGS, CA_PUBLIC},
{"XORBOOL", fun_xorbool, 0, FN_VARARGS, CA_PUBLIC},
{NULL, NULL, 0, 0, 0}
};
void
NDECL(init_functab)
{
FUN *fp;
char *buff, *cp, *dp;
buff = alloc_sbuf("init_functab");
hashinit(&mudstate.func_htab, 217);
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(player, cause, key, fname, target)
dbref player, cause;
int key;
char *fname, *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);
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 *) XMALLOC(sizeof(UFUN), "do_function");
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;
}
if (hashadd(np, (int *) ufp, &mudstate.ufunc_htab)) {
notify_quiet(player, tprintf("Function %s not defined.", fname));
XFREE((char *) ufp->name, "do_function");
XFREE(ufp, "do_function.2");
free_sbuf(np);
return;
}
}
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(player)
dbref player;
{
FUN *fp;
UFUN *ufp;
char *buf, *bp, *cp;
buf = alloc_lbuf("list_functable");
bp = buf;
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;
}
}
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
*/
CF_HAND(cf_func_access)
{
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;
}