/* funceval.c - More function handlers */
/* $Id: funceval.c,v 1.118 2000/01/15 14:53:16 cvs Exp $ */
#include "copyright.h"
#include "autoconf.h"
#include <limits.h>
#include <math.h>
#include "mudconf.h"
#include "config.h"
#include "db.h"
#include "flags.h"
#include "powers.h"
#include "attrs.h"
#include "externs.h"
#include "match.h"
#include "command.h"
#include "functions.h"
#include "misc.h"
#include "alloc.h"
#include "ansi.h"
#include "db_sql.h"
extern NAMETAB indiv_attraccess_nametab[];
extern char *FDECL(trim_space_sep, (char *, char));
extern char *FDECL(next_token, (char *, char));
extern char *FDECL(split_token, (char **, char));
extern dbref FDECL(match_thing, (dbref, char *));
extern int FDECL(countwords, (char *, char));
extern int FDECL(check_read_perms, (dbref, dbref, ATTR *, int, int, char *, char **));
extern void FDECL(arr2list, (char **, int, char *, char **, char));
extern int FDECL(list2arr, (char **, int, char *, char));
extern void FDECL(make_portlist, (dbref, dbref, char *, char **));
extern INLINE char *FDECL(get_mail_message, (int));
extern void FDECL(count_mail, (dbref, int, int *, int *, int *));
extern double NDECL(makerandom);
extern int FDECL(fn_range_check, (const char *, int, int, int, char *, char **));
extern int FDECL(delim_check, (char **, int, int, char *, char *, char **, int, dbref, dbref, char **, int, int));
extern INLINE int FDECL(safe_chr_real_fn, (char, char *, char **, int));
extern char *FDECL(upcasestr, (char *));
extern void FDECL(do_pemit_list, (dbref, char *, const char *, int));
#ifdef USE_COMSYS
extern void FDECL(make_cwho, (dbref, char *, char *, char **));
#endif
/* This is the prototype for functions */
#define FUNCTION(x) \
void x(buff, bufc, player, cause, fargs, nfargs, cargs, ncargs) \
char *buff, **bufc; \
dbref player, cause; \
char *fargs[], *cargs[]; \
int nfargs, ncargs;
#define Set_Max(x,y) (x) = ((y) > (x)) ? (y) : (x);
/* --------------------------------------------------------------------------
* 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, NOPERM_MESSAGE); \
return; \
}
/* --------------------------------------------------------------------------
* Auxiliary stuff for structures.
*/
typedef struct component_def COMPONENT;
struct component_def {
int (*typer_func)(); /* type-checking handler */
char *def_val; /* default value */
};
typedef struct structure_def STRUCTDEF;
struct structure_def {
char *s_name; /* name of the structure */
char **c_names; /* array of component names */
COMPONENT **c_array; /* array of pointers to components */
int c_count; /* number of components */
char delim; /* output delimiter when unloading */
int need_typecheck; /* any components without types of any? */
int n_instances; /* number of instances out there */
char *names_base; /* pointer for later freeing */
char *defs_base; /* pointer for later freeing */
};
typedef struct instance_def INSTANCE;
struct instance_def {
STRUCTDEF *datatype; /* pointer to structure data type def */
};
typedef struct data_def STRUCTDATA;
struct data_def {
char *text;
};
/* --------------------------------------------------------------------------
* Auxiliary stuff for listing out hashtables.
*/
static void print_htab_matches(obj, htab, buff, bufc)
dbref obj;
HASHTAB *htab;
char *buff;
char **bufc;
{
/* Things which use this are computationally expensive, and should
* be discouraged.
*/
char tbuf[SBUF_SIZE], *tp, *bb_p;
HASHENT *hptr;
int i, len;
tp = tbuf;
safe_ltos(tbuf, &tp, obj);
safe_sb_chr('.', tbuf, &tp);
*tp = '\0';
len = strlen(tbuf);
bb_p = *bufc;
for (i = 0; i < htab->hashsize; i++) {
for (hptr = htab->entry->element[i]; hptr != NULL; hptr = hptr->next) {
if (!strncmp(tbuf, hptr->target, len)) {
if (*bufc != bb_p)
safe_chr(' ', buff, bufc);
safe_str((char *) (index(hptr->target, '.') + 1), buff, bufc);
}
}
}
}
/* --------------------------------------------------------------------------
* Main body of functions starts here.
*/
#ifdef USE_COMSYS
FUNCTION(fun_cwho)
{
make_cwho(player, fargs[0], buff, bufc);
}
#endif
FUNCTION(fun_beep)
{
safe_chr(BEEP_CHAR, buff, bufc);
}
/* This function was originally taken from PennMUSH 1.50 */
char *ansi_nchartab[256] =
{
0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,
0, 0, N_ANSI_BBLUE, N_ANSI_BCYAN,
0, 0, 0, N_ANSI_BGREEN,
0, 0, 0, 0,
0, N_ANSI_BMAGENTA, 0, 0,
0, 0, N_ANSI_BRED, 0,
0, 0, 0, N_ANSI_BWHITE,
N_ANSI_BBLACK, N_ANSI_BYELLOW, 0, 0,
0, 0, 0, 0,
0, 0, N_ANSI_BLUE, N_ANSI_CYAN,
0, 0, N_ANSI_BLINK, N_ANSI_GREEN,
N_ANSI_HILITE, N_ANSI_INVERSE, 0, 0,
0, N_ANSI_MAGENTA, N_ANSI_NORMAL, 0,
0, 0, N_ANSI_RED, 0,
0, N_ANSI_UNDER, 0, N_ANSI_WHITE,
N_ANSI_BLACK, N_ANSI_YELLOW, 0, 0,
0, 0, 0, 0,
0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0
};
FUNCTION(fun_ansi)
{
char *s, *bb_p;
if (!mudconf.ansi_colors) {
safe_str(fargs[1], buff, bufc);
return;
}
if (!fargs[0] || !*fargs[0]) {
safe_str(fargs[1], buff, bufc);
return;
}
/* Favor truncating the string over truncating the ANSI codes,
* but make sure to leave room for ANSI_NORMAL (4 characters).
* That means that we need a minimum of 9 (maybe 10) characters
* left in the buffer (8 or 9 in ANSI codes, plus at least one
* character worth of string to hilite). We do 10 just to be safe.
*
* This means that in MOST cases, we are not going to drop the
* trailing ANSI code for lack of space in the buffer. However,
* because of the possibility of an extended buffer created by
* exec(), this is not a guarantee (because extending the buffer
* gives us a fresh new buff, rather than having us continue to
* copy through the new buffer). Sadly, the times when we extend
* are also to be the times we're most likely to run out of space
* in the buffer. There's nothing we can do about that, though.
*/
if (strlen(buff) > LBUF_SIZE - 11)
return;
s = fargs[0];
bb_p = *bufc;
while (*s) {
if (ansi_nchartab[(unsigned char) *s]) {
if (*bufc != bb_p) {
safe_copy_chr(';', buff, bufc, LBUF_SIZE - 5);
} else {
safe_copy_known_str(ANSI_BEGIN, 2, buff, bufc, LBUF_SIZE - 5);
}
safe_copy_str(ansi_nchartab[(unsigned char) *s],
buff, bufc, LBUF_SIZE - 5);
}
s++;
}
if (*bufc != bb_p) {
safe_copy_chr(ANSI_END, buff, bufc, LBUF_SIZE - 5);
}
safe_copy_str(fargs[1], buff, bufc, LBUF_SIZE - 5);
safe_ansi_normal(buff, bufc);
}
FUNCTION(fun_zone)
{
dbref it;
if (!mudconf.have_zones) {
return;
}
it = match_thing(player, fargs[0]);
if (it == NOTHING || !Examinable(player, it)) {
safe_nothing(buff, bufc);
return;
}
safe_dbref(buff, bufc, Zone(it));
}
/* ---------------------------------------------------------------------------
* Structures.
*/
static int istype_char(str)
char *str;
{
if (strlen(str) == 1)
return 1;
else
return 0;
}
static int istype_dbref(str)
char *str;
{
dbref it;
if (*str++ != NUMBER_TOKEN)
return 0;
if (*str) {
it = parse_dbref(str);
return (Good_obj(it));
}
return 0;
}
static int istype_int(str)
char *str;
{
return (is_integer(str));
}
static int istype_float(str)
char *str;
{
return (is_number(str));
}
static int istype_string(str)
char *str;
{
char *p;
for (p = str; *p; p++) {
if (isspace(*p))
return 0;
}
return 1;
}
FUNCTION(fun_structure)
{
char sep; /* delim for default values */
char osep; /* output delim for structure values */
char tbuf[SBUF_SIZE], *tp;
char cbuf[SBUF_SIZE], *cp;
char *p;
char *comp_names, *type_names, *default_vals;
char *comp_array[LBUF_SIZE / 2];
char *type_array[LBUF_SIZE / 2];
char *def_array[LBUF_SIZE / 2];
int n_comps, n_types, n_defs;
int i;
STRUCTDEF *this_struct;
COMPONENT *this_comp;
int check_type = 0;
svarargs_preamble("STRUCTURE", 6);
/* Prevent null delimiters and line delimiters. */
if (!osep) {
notify_quiet(player, "You cannot use a null output delimiter.");
safe_chr('0', buff, bufc);
return;
}
if (osep == '\r') {
notify_quiet(player, "You cannot use that output delimiter.");
safe_chr('0', buff, bufc);
return;
}
/* Enforce limits. */
if (StructCount(player) > mudconf.struct_lim) {
notify_quiet(player, "Too many structures.");
safe_chr('0', buff, bufc);
return;
}
/* If our structure name is too long, reject it. */
if (strlen(fargs[0]) > (SBUF_SIZE / 2) - 9) {
notify_quiet(player, "Structure name is too long.");
safe_chr('0', buff, bufc);
return;
}
/* No periods in structure names */
if (((char *) index(fargs[0], '.')) != NULL) {
notify_quiet(player, "Structure names cannot contain periods.");
safe_chr('0', buff, bufc);
return;
}
/* The hashtable is indexed by <dbref number>.<structure name> */
tp = tbuf;
safe_ltos(tbuf, &tp, player);
safe_sb_chr('.', tbuf, &tp);
for (p = fargs[0]; *p; p++)
*p = ToLower(*p);
safe_sb_str(fargs[0], tbuf, &tp);
*tp = '\0';
/* If we have this structure already, reject. */
if (hashfind(tbuf, &mudstate.structs_htab)) {
notify_quiet(player, "Structure is already defined.");
safe_chr('0', buff, bufc);
return;
}
/* Split things up. Make sure lists are the same size.
* If everything eventually goes well, comp_names and default_vals will
* REMAIN allocated.
*/
comp_names = (char *) strdup(fargs[1]);
n_comps = list2arr(comp_array, LBUF_SIZE / 2, comp_names, ' ');
if (n_comps < 1) {
notify_quiet(player, "There must be at least one component.");
safe_chr('0', buff, bufc);
free(comp_names);
return;
}
/* Make sure that we have a sane name for the components. They must
* be smaller than half an SBUF.
*/
for (i = 0; i < n_comps; i++) {
if (strlen(comp_array[i]) > (SBUF_SIZE / 2) - 9) {
notify_quiet(player, "Component name is too long.");
safe_chr('0', buff, bufc);
free(comp_names);
return;
}
}
type_names = alloc_lbuf("struct.types");
strcpy(type_names, fargs[2]);
n_types = list2arr(type_array, LBUF_SIZE / 2, type_names, ' ');
/* Make sure all types are valid. We look only at the first char, so
* typos will not be caught.
*/
for (i = 0; i < n_types; i++) {
switch (*(type_array[i])) {
case 'a': case 'A':
case 'c': case 'C':
case 'd': case 'D':
case 'i': case 'I':
case 'f': case 'F':
case 's': case 'S':
/* Valid types */
break;
default:
notify_quiet(player, "Invalid data type specified.");
safe_chr('0', buff, bufc);
free(comp_names);
free_lbuf(type_names);
return;
}
}
if (fargs[3] && *fargs[3]) {
default_vals = (char *) strdup(fargs[3]);
n_defs = list2arr(def_array, LBUF_SIZE / 2, default_vals, sep);
} else {
default_vals = NULL;
n_defs = 0;
}
if ((n_comps != n_types) || (n_defs && (n_comps != n_defs))) {
notify_quiet(player, "List sizes must be identical.");
safe_chr('0', buff, bufc);
free(comp_names);
free_lbuf(type_names);
if (default_vals)
free(default_vals);
return;
}
/* Allocate the structure and stuff it in the hashtable. Note that
* we must duplicate our name structure since the pointers to the
* strings have been allocated on the stack!
*/
this_struct = (STRUCTDEF *) XMALLOC(sizeof(STRUCTDEF), "struct_alloc");
this_struct->s_name = (char *) strdup(fargs[0]);
this_struct->c_names = (char **) calloc(n_comps, sizeof(char *));
for (i = 0; i < n_comps; i++)
this_struct->c_names[i] = comp_array[i];
this_struct->c_array = (COMPONENT **) calloc(n_comps, sizeof(COMPONENT *));
this_struct->c_count = n_comps;
this_struct->delim = osep;
this_struct->n_instances = 0;
this_struct->names_base = comp_names;
this_struct->defs_base = default_vals;
hashadd(tbuf, (int *) this_struct, &mudstate.structs_htab);
Set_Max(mudstate.max_structs, mudstate.structs_htab.entries);
/* Now that we're done with the base name, we can stick the
* joining period on the end.
*/
safe_sb_chr('.', tbuf, &tp);
*tp = '\0';
/* Allocate each individual component. */
for (i = 0; i < n_comps; i++) {
cp = cbuf;
safe_sb_str(tbuf, cbuf, &cp);
for (p = comp_array[i]; *p; p++)
*p = ToLower(*p);
safe_sb_str(comp_array[i], cbuf, &cp);
*cp = '\0';
this_comp = (COMPONENT *) XMALLOC(sizeof(COMPONENT), "comp_alloc");
this_comp->def_val = def_array[i];
switch (*(type_array[i])) {
case 'a': case 'A':
this_comp->typer_func = NULL;
break;
case 'c': case 'C':
this_comp->typer_func = istype_char;
check_type = 1;
break;
case 'd': case 'D':
this_comp->typer_func = istype_dbref;
check_type = 1;
break;
case 'i': case 'I':
this_comp->typer_func = istype_int;
check_type = 1;
break;
case 'f': case 'F':
this_comp->typer_func = istype_float;
check_type = 1;
break;
case 's': case 'S':
this_comp->typer_func = istype_string;
check_type = 1;
break;
default:
/* Should never happen */
this_comp->typer_func = NULL;
}
this_struct->need_typecheck = check_type;
this_struct->c_array[i] = this_comp;
hashadd(cbuf, (int *) this_comp, &mudstate.cdefs_htab);
Set_Max(mudstate.max_cdefs, mudstate.cdefs_htab.entries);
}
free_lbuf(type_names);
s_StructCount(player, StructCount(player) + 1);
safe_chr('1', buff, bufc);
}
FUNCTION(fun_construct)
{
char sep;
char tbuf[SBUF_SIZE], *tp;
char ibuf[SBUF_SIZE], *ip;
char cbuf[SBUF_SIZE], *cp;
char *p;
STRUCTDEF *this_struct;
char *comp_names, *init_vals;
char *comp_array[LBUF_SIZE / 2], *vals_array[LBUF_SIZE / 2];
int n_comps, n_vals;
int i;
COMPONENT *c_ptr;
INSTANCE *inst_ptr;
STRUCTDATA *d_ptr;
int retval;
/* This one is complicated: We need two, four, or five args. */
mvarargs_preamble("CONSTRUCT", 2, 5);
if (nfargs == 3) {
safe_str("#-1 FUNCTION (CONSTRUCT) EXPECTS 2 OR 4 OR 5 ARGUMENTS",
buff, bufc);
return;
}
/* Enforce limits. */
if (InstanceCount(player) > mudconf.instance_lim) {
notify_quiet(player, "Too many instances.");
safe_chr('0', buff, bufc);
return;
}
/* If our instance name is too long, reject it. */
if (strlen(fargs[0]) > (SBUF_SIZE / 2) - 9) {
notify_quiet(player, "Instance name is too long.");
safe_chr('0', buff, bufc);
return;
}
/* Make sure this instance doesn't exist. */
ip = ibuf;
safe_ltos(ibuf, &ip, player);
safe_sb_chr('.', ibuf, &ip);
for (p = fargs[0]; *p; p++)
*p = ToLower(*p);
safe_sb_str(fargs[0], ibuf, &ip);
*ip = '\0';
if (hashfind(ibuf, &mudstate.instance_htab)) {
notify_quiet(player, "That instance has already been defined.");
safe_chr('0', buff, bufc);
return;
}
/* Look up the structure. */
tp = tbuf;
safe_ltos(tbuf, &tp, player);
safe_sb_chr('.', tbuf, &tp);
for (p = fargs[1]; *p; p++)
*p = ToLower(*p);
safe_sb_str(fargs[1], tbuf, &tp);
*tp = '\0';
this_struct = (STRUCTDEF *) hashfind(tbuf, &mudstate.structs_htab);
if (!this_struct) {
notify_quiet(player, "No such structure.");
safe_chr('0', buff, bufc);
return;
}
/* Check to make sure that all the component names are valid, if we
* have been given defaults. Also, make sure that the defaults are
* of the appropriate type.
*/
safe_sb_chr('.', tbuf, &tp);
*tp = '\0';
if (fargs[2] && *fargs[2] && fargs[3] && *fargs[3]) {
comp_names = alloc_lbuf("construct.comps");
strcpy(comp_names, fargs[2]);
n_comps = list2arr(comp_array, LBUF_SIZE / 2, comp_names, ' ');
init_vals = alloc_lbuf("construct.vals");
strcpy(init_vals, fargs[3]);
n_vals = list2arr(vals_array, LBUF_SIZE / 2, init_vals, sep);
if (n_comps != n_vals) {
notify_quiet(player, "List sizes must be identical.");
safe_chr('0', buff, bufc);
free_lbuf(comp_names);
free_lbuf(init_vals);
return;
}
for (i = 0; i < n_comps; i++) {
cp = cbuf;
safe_sb_str(tbuf, cbuf, &cp);
for (p = comp_array[i]; *p; p++)
*p = ToLower(*p);
safe_sb_str(comp_array[i], cbuf, &cp);
c_ptr = (COMPONENT *) hashfind(cbuf, &mudstate.cdefs_htab);
if (!c_ptr) {
notify_quiet(player, "Invalid component name.");
safe_chr('0', buff, bufc);
free_lbuf(comp_names);
free_lbuf(init_vals);
return;
}
if (c_ptr->typer_func) {
retval = (*(c_ptr->typer_func)) (vals_array[i]);
if (!retval) {
notify_quiet(player, "Default value is of invalid type.");
safe_chr('0', buff, bufc);
free_lbuf(comp_names);
free_lbuf(init_vals);
return;
}
}
}
} else if ((!fargs[2] || !*fargs[2]) && (!fargs[3] || !*fargs[3])) {
/* Blank initializers. This is just fine. */
comp_names = init_vals = NULL;
n_comps = n_vals = 0;
} else {
notify_quiet(player, "List sizes must be identical.");
safe_chr('0', buff, bufc);
return;
}
/* Go go gadget constructor.
* Allocate the instance. We should have already made sure that the
* instance doesn't exist.
*/
inst_ptr = (INSTANCE *) XMALLOC(sizeof(INSTANCE), "constructor.inst");
inst_ptr->datatype = this_struct;
hashadd(ibuf, (int *) inst_ptr, &mudstate.instance_htab);
Set_Max(mudstate.max_instance, mudstate.instance_htab.entries);
/* Populate with default values. */
for (i = 0; i < this_struct->c_count; i++) {
d_ptr = (STRUCTDATA *) XMALLOC(sizeof(STRUCTDATA), "constructor.data");
if (this_struct->c_array[i]->def_val) {
d_ptr->text = (char *)
strdup(this_struct->c_array[i]->def_val);
} else {
d_ptr->text = NULL;
}
tp = tbuf;
safe_sb_str(ibuf, tbuf, &tp);
safe_sb_chr('.', tbuf, &tp);
safe_sb_str(this_struct->c_names[i], tbuf, &tp);
*tp = '\0';
hashadd(tbuf, (int *) d_ptr, &mudstate.instdata_htab);
Set_Max(mudstate.max_instdata, mudstate.instdata_htab.entries);
}
/* Overwrite with component values. */
for (i = 0; i < n_comps; i++) {
tp = tbuf;
safe_sb_str(ibuf, tbuf, &tp);
safe_sb_chr('.', tbuf, &tp);
safe_sb_str(comp_array[i], tbuf, &tp);
*tp = '\0';
d_ptr = (STRUCTDATA *) hashfind(tbuf, &mudstate.instdata_htab);
if (d_ptr) {
if (d_ptr->text)
free(d_ptr->text);
if (vals_array[i] && *(vals_array[i]))
d_ptr->text = (char *) strdup(vals_array[i]);
else
d_ptr->text = NULL;
}
}
if (comp_names)
free_lbuf(comp_names);
if (init_vals)
free_lbuf(init_vals);
this_struct->n_instances += 1;
s_InstanceCount(player, InstanceCount(player) + 1);
safe_chr('1', buff, bufc);
}
FUNCTION(fun_load)
{
char tbuf[SBUF_SIZE], *tp;
char ibuf[SBUF_SIZE], *ip;
char *p;
STRUCTDEF *this_struct;
char *val_list;
char *val_array[LBUF_SIZE / 2];
int n_vals;
INSTANCE *inst_ptr;
STRUCTDATA *d_ptr;
int i;
char sep;
varargs_preamble("LOAD", 4);
/* Enforce limits. */
if (InstanceCount(player) > mudconf.instance_lim) {
notify_quiet(player, "Too many instances.");
safe_chr('0', buff, bufc);
return;
}
/* If our instance name is too long, reject it. */
if (strlen(fargs[0]) > (SBUF_SIZE / 2) - 9) {
notify_quiet(player, "Instance name is too long.");
safe_chr('0', buff, bufc);
return;
}
/* Make sure this instance doesn't exist. */
ip = ibuf;
safe_ltos(ibuf, &ip, player);
safe_sb_chr('.', ibuf, &ip);
for (p = fargs[0]; *p; p++)
*p = ToLower(*p);
safe_sb_str(fargs[0], ibuf, &ip);
*ip = '\0';
if (hashfind(ibuf, &mudstate.instance_htab)) {
notify_quiet(player, "That instance has already been defined.");
safe_chr('0', buff, bufc);
return;
}
/* Look up the structure. */
tp = tbuf;
safe_ltos(tbuf, &tp, player);
safe_sb_chr('.', tbuf, &tp);
for (p = fargs[1]; *p; p++)
*p = ToLower(*p);
safe_sb_str(fargs[1], tbuf, &tp);
*tp = '\0';
this_struct = (STRUCTDEF *) hashfind(tbuf, &mudstate.structs_htab);
if (!this_struct) {
notify_quiet(player, "No such structure.");
safe_chr('0', buff, bufc);
return;
}
/* Chop up the raw stuff according to the delimiter. */
if (nfargs != 4)
sep = this_struct->delim;
val_list = alloc_lbuf("load.val_list");
strcpy(val_list, fargs[2]);
n_vals = list2arr(val_array, LBUF_SIZE / 2, val_list, sep);
if (n_vals != this_struct->c_count) {
notify_quiet(player, "Incorrect number of components.");
safe_chr('0', buff, bufc);
free_lbuf(val_list);
return;
}
/* Check the types of the data we've been passed. */
for (i = 0; i < n_vals; i++) {
if (this_struct->c_array[i]->typer_func &&
!((*(this_struct->c_array[i]->typer_func)) (val_array[i]))) {
notify_quiet(player, "Value is of invalid type.");
safe_chr('0', buff, bufc);
free_lbuf(val_list);
return;
}
}
/* Allocate the instance. We should have already made sure that the
* instance doesn't exist.
*/
inst_ptr = (INSTANCE *) XMALLOC(sizeof(INSTANCE), "constructor.inst");
inst_ptr->datatype = this_struct;
hashadd(ibuf, (int *) inst_ptr, &mudstate.instance_htab);
Set_Max(mudstate.max_instance, mudstate.instance_htab.entries);
/* Stuff data into memory. */
for (i = 0; i < this_struct->c_count; i++) {
d_ptr = (STRUCTDATA *) XMALLOC(sizeof(STRUCTDATA), "constructor.data");
if (val_array[i] && *(val_array[i]))
d_ptr->text = (char *) strdup(val_array[i]);
else
d_ptr->text = NULL;
tp = tbuf;
safe_sb_str(ibuf, tbuf, &tp);
safe_sb_chr('.', tbuf, &tp);
safe_sb_str(this_struct->c_names[i], tbuf, &tp);
*tp = '\0';
hashadd(tbuf, (int *) d_ptr, &mudstate.instdata_htab);
Set_Max(mudstate.max_instdata, mudstate.instdata_htab.entries);
}
free_lbuf(val_list);
this_struct->n_instances += 1;
s_InstanceCount(player, InstanceCount(player) + 1);
safe_chr('1', buff, bufc);
}
FUNCTION(fun_z)
{
char tbuf[SBUF_SIZE], *tp;
char *p;
STRUCTDATA *s_ptr;
tp = tbuf;
safe_ltos(tbuf, &tp, player);
safe_sb_chr('.', tbuf, &tp);
for (p = fargs[0]; *p; p++)
*p = ToLower(*p);
safe_sb_str(fargs[0], tbuf, &tp);
safe_sb_chr('.', tbuf, &tp);
for (p = fargs[1]; *p; p++)
*p = ToLower(*p);
safe_sb_str(fargs[1], tbuf, &tp);
*tp = '\0';
s_ptr = (STRUCTDATA *) hashfind(tbuf, &mudstate.instdata_htab);
if (!s_ptr || !s_ptr->text)
return;
safe_str(s_ptr->text, buff, bufc);
}
FUNCTION(fun_modify)
{
char tbuf[SBUF_SIZE], *tp;
char cbuf[SBUF_SIZE], *cp;
char *p;
INSTANCE *inst_ptr;
COMPONENT *c_ptr;
STRUCTDATA *s_ptr;
int retval;
/* Find the instance first, since this is how we get our typechecker. */
tp = tbuf;
safe_ltos(tbuf, &tp, player);
safe_sb_chr('.', tbuf, &tp);
for (p = fargs[0]; *p; p++)
*p = ToLower(*p);
safe_sb_str(fargs[0], tbuf, &tp);
*tp = '\0';
inst_ptr = (INSTANCE *) hashfind(tbuf, &mudstate.instance_htab);
if (!inst_ptr) {
notify_quiet(player, "No such instance.");
safe_chr('0', buff, bufc);
return;
}
/* Use that to find the component and check the type. */
if (inst_ptr->datatype->need_typecheck) {
cp = cbuf;
safe_ltos(cbuf, &cp, player);
safe_sb_chr('.', cbuf, &cp);
safe_sb_str(inst_ptr->datatype->s_name, cbuf, &cp);
safe_sb_chr('.', cbuf, &cp);
for (p = fargs[1]; *p; p++)
*p = ToLower(*p);
safe_sb_str(fargs[1], cbuf, &cp);
*cp = '\0';
c_ptr = (COMPONENT *) hashfind(cbuf, &mudstate.cdefs_htab);
if (!c_ptr) {
notify_quiet(player, "No such component.");
safe_chr('0', buff, bufc);
return;
}
if (c_ptr->typer_func) {
retval = (*(c_ptr->typer_func)) (fargs[2]);
if (!retval) {
notify_quiet(player, "Value is of invalid type.");
safe_chr('0', buff, bufc);
return;
}
}
}
/* Now go set it. */
safe_sb_chr('.', tbuf, &tp);
safe_sb_str(fargs[1], tbuf, &tp);
*tp = '\0';
s_ptr = (STRUCTDATA *) hashfind(tbuf, &mudstate.instdata_htab);
if (!s_ptr) {
notify_quiet(player, "No such data.");
safe_chr('0', buff, bufc);
return;
}
if (s_ptr->text)
free(s_ptr->text);
if (fargs[2] && *fargs[2]) {
s_ptr->text = (char *) strdup(fargs[2]);
} else {
s_ptr->text = NULL;
}
safe_chr('1', buff, bufc);
}
FUNCTION(fun_unload)
{
char tbuf[SBUF_SIZE], *tp;
char ibuf[SBUF_SIZE], *ip;
INSTANCE *inst_ptr;
char *p;
STRUCTDEF *this_struct;
STRUCTDATA *d_ptr;
int i;
char sep;
varargs_preamble("UNLOAD", 2);
/* Get the instance. */
ip = ibuf;
safe_ltos(ibuf, &ip, player);
safe_sb_chr('.', ibuf, &ip);
for (p = fargs[0]; *p; p++)
*p = ToLower(*p);
safe_sb_str(fargs[0], ibuf, &ip);
*ip = '\0';
inst_ptr = (INSTANCE *) hashfind(ibuf, &mudstate.instance_htab);
if (!inst_ptr)
return;
/* From the instance, we can get a pointer to the structure. We then
* have the information we need to figure out what components are
* associated with this, and print them appropriately.
*/
safe_sb_chr('.', ibuf, &ip);
*ip = '\0';
this_struct = inst_ptr->datatype;
/* Our delimiter is a special case. */
if (nfargs != 2)
sep = this_struct->delim;
for (i = 0; i < this_struct->c_count; i++) {
if (i != 0) {
safe_chr(sep, buff, bufc);
}
tp = tbuf;
safe_sb_str(ibuf, tbuf, &tp);
safe_sb_str(this_struct->c_names[i], tbuf, &tp);
*tp = '\0';
d_ptr = (STRUCTDATA *) hashfind(tbuf, &mudstate.instdata_htab);
if (d_ptr && d_ptr->text)
safe_str(d_ptr->text, buff, bufc);
}
}
FUNCTION(fun_destruct)
{
char tbuf[SBUF_SIZE], *tp;
char ibuf[SBUF_SIZE], *ip;
INSTANCE *inst_ptr;
char *p;
STRUCTDEF *this_struct;
STRUCTDATA *d_ptr;
int i;
/* Get the instance. */
ip = ibuf;
safe_ltos(ibuf, &ip, player);
safe_sb_chr('.', ibuf, &ip);
for (p = fargs[0]; *p; p++)
*p = ToLower(*p);
safe_sb_str(fargs[0], ibuf, &ip);
*ip = '\0';
inst_ptr = (INSTANCE *) hashfind(ibuf, &mudstate.instance_htab);
if (!inst_ptr) {
notify_quiet(player, "No such instance.");
safe_chr('0', buff, bufc);
return;
}
/* Now we can get a pointer to the structure and find the rest of the
* components.
*/
this_struct = inst_ptr->datatype;
XFREE(inst_ptr, "constructor.inst");
hashdelete(ibuf, &mudstate.instance_htab);
safe_sb_chr('.', ibuf, &ip);
*ip = '\0';
for (i = 0; i < this_struct->c_count; i++) {
tp = tbuf;
safe_sb_str(ibuf, tbuf, &tp);
safe_sb_str(this_struct->c_names[i], tbuf, &tp);
*tp = '\0';
d_ptr = (STRUCTDATA *) hashfind(tbuf, &mudstate.instdata_htab);
if (d_ptr) {
if (d_ptr->text)
free(d_ptr->text);
XFREE(d_ptr, "constructor.data");
hashdelete(tbuf, &mudstate.instdata_htab);
}
}
this_struct->n_instances -= 1;
s_InstanceCount(player, InstanceCount(player) - 1);
safe_chr('1', buff, bufc);
}
FUNCTION(fun_unstructure)
{
char tbuf[SBUF_SIZE], *tp;
char cbuf[SBUF_SIZE], *cp;
char *p;
STRUCTDEF *this_struct;
int i;
/* Find the structure */
tp = tbuf;
safe_ltos(tbuf, &tp, player);
safe_sb_chr('.', tbuf, &tp);
for (p = fargs[0]; *p; p++)
*p = ToLower(*p);
safe_sb_str(fargs[0], tbuf, &tp);
*tp = '\0';
this_struct = (STRUCTDEF *) hashfind(tbuf, &mudstate.structs_htab);
if (!this_struct) {
notify_quiet(player, "No such structure.");
safe_chr('0', buff, bufc);
return;
}
/* Can't delete what's in use. */
if (this_struct->n_instances > 0) {
notify_quiet(player, "This structure is in use.");
safe_chr('0', buff, bufc);
return;
}
/* Wipe the structure from the hashtable. */
hashdelete(tbuf, &mudstate.structs_htab);
/* Wipe out every component definition. */
safe_sb_chr('.', tbuf, &tp);
*tp = '\0';
for (i = 0; i < this_struct->c_count; i++) {
cp = cbuf;
safe_sb_str(tbuf, cbuf, &cp);
safe_sb_str(this_struct->c_names[i], cbuf, &cp);
*cp = '\0';
if (this_struct->c_array[i]) {
XFREE(this_struct->c_array[i], "comp_alloc");
}
hashdelete(cbuf, &mudstate.cdefs_htab);
}
/* Free up our bit of memory. */
free(this_struct->s_name);
if (this_struct->names_base)
free(this_struct->names_base);
if (this_struct->defs_base)
free(this_struct->defs_base);
free(this_struct->c_names);
XFREE(this_struct, "struct_alloc");
s_StructCount(player, StructCount(player) - 1);
safe_chr('1', buff, bufc);
}
FUNCTION(fun_lstructures)
{
print_htab_matches(player, &mudstate.structs_htab, buff, bufc);
}
FUNCTION(fun_linstances)
{
print_htab_matches(player, &mudstate.instance_htab, buff, bufc);
}
void structure_clr(thing)
dbref thing;
{
/* Wipe out all structure information associated with an object.
* Find all the object's instances. Destroy them.
* Then, find all the object's defined structures, and destroy those.
*/
HASHTAB *htab;
HASHENT *hptr;
char tbuf[SBUF_SIZE], ibuf[SBUF_SIZE], cbuf[SBUF_SIZE], *tp, *ip, *cp;
int i, j, len, count;
INSTANCE **inst_array;
char **name_array;
STRUCTDEF *this_struct;
STRUCTDATA *d_ptr;
STRUCTDEF **struct_array;
/* The instance table is indexed as <dbref number>.<instance name> */
tp = tbuf;
safe_ltos(tbuf, &tp, thing);
safe_sb_chr('.', tbuf, &tp);
*tp = '\0';
len = strlen(tbuf);
/* Because of the hashtable rechaining that's done, we cannot simply
* walk the hashtable and delete entries as we go. Instead, we've
* got to keep track of all of our pointers, and go back and do
* them one by one.
*/
inst_array = (INSTANCE **) calloc(mudconf.instance_lim + 1,
sizeof(INSTANCE *));
name_array = (char **) calloc(mudconf.instance_lim + 1, sizeof(char *));
htab = &mudstate.instance_htab;
count = 0;
for (i = 0; i < htab->hashsize; i++) {
for (hptr = htab->entry->element[i]; hptr != NULL; hptr = hptr->next) {
if (!strncmp(tbuf, hptr->target, len)) {
name_array[count] = (char *) hptr->target;
inst_array[count] = (INSTANCE *) hptr->data;
count++;
}
}
}
/* Now that we have the pointers to the instances, we can get the
* structure definitions, and use that to hunt down and wipe the
* components.
*/
if (count > 0) {
for (i = 0; i < count; i++) {
this_struct = inst_array[i]->datatype;
XFREE(inst_array[i], "constructor.inst");
hashdelete(name_array[i], &mudstate.instance_htab);
ip = ibuf;
safe_sb_str(name_array[i], ibuf, &ip);
safe_sb_chr('.', ibuf, &ip);
*ip = '\0';
for (j = 0; j < this_struct->c_count; j++) {
cp = cbuf;
safe_sb_str(ibuf, cbuf, &cp);
safe_sb_str(this_struct->c_names[j], cbuf, &cp);
*cp = '\0';
d_ptr = (STRUCTDATA *) hashfind(cbuf, &mudstate.instdata_htab);
if (d_ptr) {
if (d_ptr->text)
free(d_ptr->text);
XFREE(d_ptr, "constructor.data");
hashdelete(cbuf, &mudstate.instdata_htab);
}
}
this_struct->n_instances -= 1;
}
}
free(inst_array);
free(name_array);
/* The structure table is indexed as <dbref number>.<struct name> */
tp = tbuf;
safe_ltos(tbuf, &tp, thing);
safe_sb_chr('.', tbuf, &tp);
*tp = '\0';
len = strlen(tbuf);
/* Again, we have the hashtable rechaining problem. */
struct_array = (STRUCTDEF **) calloc(mudconf.struct_lim + 1,
sizeof(STRUCTDEF *));
name_array = (char **) calloc(mudconf.struct_lim + 1, sizeof(char *));
htab = &mudstate.structs_htab;
count = 0;
for (i = 0; i < htab->hashsize; i++) {
for (hptr = htab->entry->element[i]; hptr != NULL; hptr = hptr->next) {
if (!strncmp(tbuf, hptr->target, len)) {
name_array[count] = (char *) hptr->target;
struct_array[count] = (STRUCTDEF *) hptr->data;
count++;
}
}
}
/* We have the pointers to the structures. Flag a big error if they're
* still in use, wipe them from the hashtable, then wipe out every
* component definition. Free up the memory.
*/
if (count > 0) {
for (i = 0; i < count; i++) {
if (struct_array[i]->n_instances > 0) {
STARTLOG(LOG_ALWAYS, "BUG", "STRUCT")
log_name(thing);
log_text((char *) " structure ");
log_text((char *) name_array[i]);
log_text((char *) " has ");
log_number(struct_array[i]->n_instances);
log_text((char *) " allocated instances uncleared.");
ENDLOG
}
hashdelete(name_array[i], &mudstate.structs_htab);
ip = ibuf;
safe_sb_str(name_array[i], ibuf, &ip);
safe_sb_chr('.', ibuf, &ip);
*ip = '\0';
for (j = 0; j < struct_array[i]->c_count; j++) {
cp = cbuf;
safe_sb_str(ibuf, cbuf, &cp);
safe_sb_str(struct_array[i]->c_names[j], cbuf, &cp);
*cp = '\0';
if (struct_array[i]->c_array[j]) {
XFREE(struct_array[i]->c_array[j], "comp_alloc");
}
hashdelete(cbuf, &mudstate.cdefs_htab);
}
free(struct_array[i]->s_name);
if (struct_array[i]->names_base)
free(struct_array[i]->names_base);
if (struct_array[i]->defs_base)
free(struct_array[i]->defs_base);
free(struct_array[i]->c_names);
XFREE(struct_array[i], "struct_alloc");
}
}
free(struct_array);
free(name_array);
}
/*------------------------------------------------------------------------
* Side-effect functions.
*/
static int check_command(player, name, buff, bufc)
dbref player;
char *name, *buff, **bufc;
{
CMDENT *cmdp;
if ((cmdp = (CMDENT *) hashfind(name, &mudstate.command_htab))) {
/* Note that these permission checks are NOT identical to the
* ones in process_cmdent(). In particular, side-effects are NOT
* subject to the CA_GBL_INTERP flag. This is a design decision
* based on the concept that these are functions and not commands,
* even though they behave like commands in many respects. This
* is also the same reason why side-effects don't trigger hooks.
*/
if (Invalid_Objtype(player) || !check_access(player, cmdp->perms) ||
(!Builder(player) && Protect(CA_GBL_BUILD) &&
!(mudconf.control_flags & CF_BUILD))) {
safe_noperm(buff, bufc);
return 1;
}
}
return 0;
}
FUNCTION(fun_link)
{
if (check_command(player, "@link", buff, bufc))
return;
do_link(player, cause, 0, fargs[0], fargs[1]);
}
FUNCTION(fun_tel)
{
if (check_command(player, "@teleport", buff, bufc))
return;
do_teleport(player, cause, 0, fargs[0], fargs[1]);
}
FUNCTION(fun_wipe)
{
if (check_command(player, "@wipe", buff, bufc))
return;
do_wipe(player, cause, 0, fargs[0]);
}
FUNCTION(fun_pemit)
{
if (check_command(player, "@pemit", buff, bufc))
return;
do_pemit_list(player, fargs[0], fargs[1], 0);
}
FUNCTION(fun_remit)
{
if (check_command(player, "@pemit", buff, bufc))
return;
do_pemit_list(player, fargs[0], fargs[1], 1);
}
FUNCTION(fun_force)
{
if (check_command(player, "@force", buff, bufc))
return;
do_force(player, cause, 0, fargs[0], fargs[1], cargs, ncargs);
}
FUNCTION(fun_trigger)
{
if (nfargs < 1) {
safe_str("#-1 TOO FEW ARGUMENTS", buff, bufc);
return;
}
if (check_command(player, "@trigger", buff, bufc))
return;
do_trigger(player, cause, 0, fargs[0], &(fargs[1]), nfargs - 1);
}
FUNCTION(fun_wait)
{
do_wait(player, cause, 0, fargs[0], fargs[1], cargs, ncargs);
}
FUNCTION(fun_command)
{
CMDENT *cmdp;
char tbuf1[1], tbuf2[1];
int key;
if (!fargs[0] || !*fargs[0])
return;
cmdp = (CMDENT *) hashfind(fargs[0], &mudstate.command_htab);
if (!cmdp) {
notify(player, "Command not found.");
return;
}
if (Invalid_Objtype(player) || !check_access(player, cmdp->perms) ||
(!Builder(player) && Protect(CA_GBL_BUILD) &&
!(mudconf.control_flags & CF_BUILD))) {
notify(player, "Permission denied.");
return;
}
if (!(cmdp->callseq & CS_FUNCTION) || (cmdp->callseq & CS_ADDED)) {
notify(player, "Cannot call that command.");
return;
}
/* Strip command flags that are irrelevant. */
key = cmdp->extra;
key &= ~(SW_GOT_UNIQUE | SW_MULTIPLE | SW_NOEVAL);
/* Can't handle null args, so make sure there's something there. */
tbuf1[0] = '\0';
tbuf2[0] = '\0';
switch (cmdp->callseq & CS_NARG_MASK) {
case CS_NO_ARGS:
(*(cmdp->info.handler)) (player, cause, key);
break;
case CS_ONE_ARG:
(*(cmdp->info.handler)) (player, cause, key,
((fargs[1]) ? (fargs[1]) : tbuf1));
break;
case CS_TWO_ARG:
(*(cmdp->info.handler)) (player, cause, key,
((fargs[1]) ? (fargs[1]) : tbuf1),
((fargs[2]) ? (fargs[2]) : tbuf2));
break;
default:
notify(player, "Invalid command handler.");
return;
}
}
/*------------------------------------------------------------------------
* fun_create: Creates a room, thing or exit
*/
FUNCTION(fun_create)
{
dbref thing;
int cost;
char sep, *name;
varargs_preamble("CREATE", 3);
name = fargs[0];
if (!name || !*name) {
safe_str("#-1 ILLEGAL NAME", buff, bufc);
return;
}
if (fargs[2] && *fargs[2])
sep = *fargs[2];
else
sep = 't';
switch (sep) {
case 'r':
if (check_command(player, "@dig", buff, bufc)) {
return;
}
thing = create_obj(player, TYPE_ROOM, name, 0);
break;
case 'e':
if (check_command(player, "@open", buff, bufc)) {
return;
}
thing = create_obj(player, TYPE_EXIT, name, 0);
if (thing != NOTHING) {
s_Exits(thing, player);
s_Next(thing, Exits(player));
s_Exits(player, thing);
}
break;
default:
if (check_command(player, "@create", buff, bufc)) {
return;
}
if (fargs[1] && *fargs[1]) {
cost = atoi(fargs[1]);
if (cost < mudconf.createmin || cost > mudconf.createmax) {
safe_str("#-1 COST OUT OF RANGE", buff, bufc);
return;
}
} else {
cost = mudconf.createmin;
}
thing = create_obj(player, TYPE_THING, name, cost);
if (thing != NOTHING) {
move_via_generic(thing, player, NOTHING, 0);
s_Home(thing, new_home(player));
}
break;
}
safe_dbref(buff, bufc, thing);
}
/*---------------------------------------------------------------------------
* fun_set: sets an attribute on an object
*/
static void set_attr_internal(player, thing, attrnum, attrtext, key, buff, bufc)
dbref player, thing;
int attrnum, key;
char *attrtext, *buff;
char **bufc;
{
dbref aowner;
int aflags, could_hear;
ATTR *attr;
attr = atr_num(attrnum);
atr_pget_info(thing, attrnum, &aowner, &aflags);
if (attr && Set_attr(player, thing, attr, aflags)) {
if ((attr->check != NULL) &&
(!(*attr->check) (0, player, thing, attrnum, attrtext))) {
safe_noperm(buff, bufc);
return;
}
could_hear = Hearer(thing);
atr_add(thing, attrnum, attrtext, Owner(player), aflags);
handle_ears(thing, could_hear, Hearer(thing));
if (!(key & SET_QUIET) && !Quiet(player) && !Quiet(thing))
notify_quiet(player, "Set.");
} else {
safe_str("#-1 PERMISSION DENIED.", buff, bufc);
}
}
FUNCTION(fun_set)
{
dbref thing, thing2, aowner;
char *p, *buff2;
int atr, atr2, aflags, alen, clear, flagvalue, could_hear;
ATTR *attr, *attr2;
/* obj/attr form? */
if (check_command(player, "@set", buff, bufc))
return;
if (parse_attrib(player, fargs[0], &thing, &atr)) {
if (atr != NOTHING) {
/* must specify flag name */
if (!fargs[1] || !*fargs[1]) {
safe_str("#-1 UNSPECIFIED PARAMETER", buff, bufc);
}
/* are we clearing? */
clear = 0;
if (*fargs[0] == NOT_TOKEN) {
fargs[0]++;
clear = 1;
}
/* valid attribute flag? */
flagvalue = search_nametab(player,
indiv_attraccess_nametab, fargs[1]);
if (flagvalue < 0) {
safe_str("#-1 CAN NOT SET", buff, bufc);
return;
}
/* make sure attribute is present */
if (!atr_get_info(thing, atr, &aowner, &aflags)) {
safe_str("#-1 ATTRIBUTE NOT PRESENT ON OBJECT", buff, bufc);
return;
}
/* can we write to attribute? */
attr = atr_num(atr);
if (!attr || !Set_attr(player, thing, attr, aflags)) {
safe_noperm(buff, bufc);
return;
}
/* just do it! */
if (clear)
aflags &= ~flagvalue;
else
aflags |= flagvalue;
could_hear = Hearer(thing);
atr_set_flags(thing, atr, aflags);
return;
}
}
/* find thing */
if ((thing = match_controlled(player, fargs[0])) == NOTHING) {
safe_nothing(buff, bufc);
return;
}
/* check for attr set first */
for (p = fargs[1]; *p && (*p != ':'); p++) ;
if (*p) {
*p++ = 0;
atr = mkattr(fargs[1]);
if (atr <= 0) {
safe_str("#-1 UNABLE TO CREATE ATTRIBUTE", buff, bufc);
return;
}
attr = atr_num(atr);
if (!attr) {
safe_noperm(buff, bufc);
return;
}
atr_get_info(thing, atr, &aowner, &aflags);
if (!Set_attr(player, thing, attr, aflags)) {
safe_noperm(buff, bufc);
return;
}
buff2 = alloc_lbuf("fun_set");
/* check for _ */
if (*p == '_') {
strcpy(buff2, p + 1);
if (!parse_attrib(player, p + 1, &thing2, &atr2) ||
(atr == NOTHING)) {
free_lbuf(buff2);
safe_nomatch(buff, bufc);
return;
}
attr2 = atr_num(atr);
p = buff2;
atr_pget_str(buff2, thing2, atr2, &aowner,
&aflags, &alen);
if (!attr2 ||
!See_attr(player, thing2, attr2, aowner, aflags)) {
free_lbuf(buff2);
safe_noperm(buff, bufc);
return;
}
}
/* set it */
set_attr_internal(player, thing, atr, p, 0, buff, bufc);
free_lbuf(buff2);
return;
}
/* set/clear a flag */
flag_set(thing, player, fargs[1], 0);
}
/*---------------------------------------------------------------------------
*/
/* Borrowed from DarkZone */
void scan_zone(player, zone, type, buff, bufc)
dbref player;
char *zone, *buff, **bufc;
int type;
{
dbref i, it = match_thing(player, zone);
char *bb_p;
if (!mudconf.have_zones || (!Controls(player, it) && !WizRoy(player))) {
safe_str("#-1 NO PERMISSION TO USE", buff, bufc);
return;
}
bb_p = *bufc;
DO_WHOLE_DB(i) {
if (Typeof(i) == type) {
if (Zone(i) == it) {
if (*bufc != bb_p) {
safe_chr(' ', buff, bufc);
}
safe_dbref(buff, bufc, i);
}
}
}
}
FUNCTION(fun_zwho)
{
scan_zone(player, fargs[0], TYPE_PLAYER, buff, bufc);
}
FUNCTION(fun_inzone)
{
scan_zone(player, fargs[0], TYPE_ROOM, buff, bufc);
}
/* Borrowed from DarkZone */
FUNCTION(fun_children)
{
dbref i, it = match_thing(player, fargs[0]);
char *bb_p;
if (!(Controls(player, it)) || !(WizRoy(player))) {
safe_str("#-1 NO PERMISSION TO USE", buff, bufc);
return;
}
bb_p = *bufc;
DO_WHOLE_DB(i) {
if (Parent(i) == it) {
if (*bufc != bb_p) {
safe_chr(' ', buff, bufc);
}
safe_dbref(buff, bufc, i);
}
}
}
FUNCTION(fun_objeval)
{
dbref obj;
char *name, *bp, *str;
if (!*fargs[0]) {
return;
}
name = bp = alloc_lbuf("fun_objeval");
str = fargs[0];
exec(name, &bp, 0, player, cause, EV_FCHECK | EV_STRIP | EV_EVAL, &str,
cargs, ncargs);
*bp = '\0';
obj = match_thing(player, name);
/* 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. Also, you cannot evaluate
* things from the point of view of God.
*/
if ((obj == NOTHING) || (obj == GOD) ||
((Owner(obj) != Owner(player)) && !Wizard(player))) {
obj = player;
}
str = fargs[1];
exec(buff, bufc, 0, obj, cause, EV_FCHECK | EV_STRIP | EV_EVAL, &str,
cargs, ncargs);
free_lbuf(name);
}
/* ---------------------------------------------------------------------------
* fun_localize: Evaluate a function with local scope (i.e., preserve and
* restore the r-registers). Essentially like calling ulocal() but with the
* function string directly.
*/
FUNCTION(fun_localize)
{
char *str, *preserve[MAX_GLOBAL_REGS];
int preserve_len[MAX_GLOBAL_REGS];
save_global_regs("fun_localize_save", preserve, preserve_len);
str = fargs[0];
exec(buff, bufc, 0, player, cause, EV_FCHECK | EV_STRIP | EV_EVAL, &str,
cargs, ncargs);
restore_global_regs("fun_localize_restore", preserve, preserve_len);
}
/* ---------------------------------------------------------------------------
* fun_null: Just eat the contents of the string. Handy for those times
* when you've output a bunch of junk in a function call and
* just want to dispose of the output (like if you've done an
* iter() that just did a bunch of side-effects, and now you have
* bunches of spaces you need to get rid of.
*/
FUNCTION(fun_null)
{
return;
}
/* ---------------------------------------------------------------------------
* 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 *tp, *bp, sep;
if (nfargs == 0) {
return;
}
varargs_preamble("SQUISH", 2);
bp = tp = fargs[0];
while (*tp) {
/* Move over and copy the non-sep characters */
while (*tp && (*tp != sep)) {
*bp++ = *tp++;
}
/* If we've reached the end of the string, leave the loop. */
if (!*tp)
break;
/* Otherwise, we've hit a sep char. Move over it, and then move on to
* the next non-separator. Note that we're overwriting our own
* string as we do this. However, the other pointer will always
* be ahead of our current copy pointer.
*/
*bp++ = *tp++;
while (*tp && (*tp == sep))
tp++;
}
/* Must terminate the string */
*bp = '\0';
safe_str(fargs[0], buff, bufc);
}
FUNCTION(fun_stripansi)
{
safe_str((char *)strip_ansi(fargs[0]), buff, bufc);
}
/* Borrowed from PennMUSH 1.50 */
FUNCTION(fun_zfun)
{
dbref aowner;
int aflags, alen;
ATTR *ap;
char *tbuf1, *str;
dbref zone = Zone(player);
if (!mudconf.have_zones) {
safe_str("#-1 ZONES DISABLED", buff, bufc);
return;
}
if (zone == NOTHING) {
safe_str("#-1 INVALID ZONE", buff, bufc);
return;
}
if (!fargs[0] || !*fargs[0])
return;
/* find the user function attribute */
ap = atr_str(upcasestr(fargs[0]));
if (!ap) {
safe_str("#-1 NO SUCH USER FUNCTION", buff, bufc);
return;
}
tbuf1 = atr_pget(zone, ap->number, &aowner, &aflags, &alen);
if (!See_attr(player, zone, ap, aowner, aflags)) {
safe_str("#-1 NO PERMISSION TO GET ATTRIBUTE", buff, bufc);
free_lbuf(tbuf1);
return;
}
str = tbuf1;
exec(buff, bufc, 0, zone, player, EV_EVAL | EV_STRIP | EV_FCHECK, &str, &(fargs[1]),
nfargs - 1);
free_lbuf(tbuf1);
}
FUNCTION(fun_columns)
{
unsigned int spaces, number, ansinumber, striplen;
unsigned int count, i, indent = 0;
int isansi = 0, rturn = 1, cr = 0;
char *p, *q, *buf, *curr, *objstring, *bp, *cp, sep, *str;
if (!fn_range_check("COLUMNS", nfargs, 2, 4, buff, bufc))
return;
if (!delim_check(fargs, nfargs, 3, &sep, buff, bufc, 1,
player, cause, cargs, ncargs, 0))
return;
number = (unsigned int) safe_atoi(fargs[1]);
indent = (unsigned int) safe_atoi(fargs[3]);
if (indent > 77) { /* unsigned int, always a positive number */
indent = 1;
}
/* Must check number separately, since number + indent can
* result in an integer overflow.
*/
if ((number < 1) || (number > 77) ||
((unsigned int) (number + indent) > 78)) {
safe_str("#-1 OUT OF RANGE", buff, bufc);
return;
}
cp = curr = bp = alloc_lbuf("fun_columns");
str = fargs[0];
exec(curr, &bp, 0, player, cause, EV_STRIP | EV_FCHECK | EV_EVAL, &str,
cargs, ncargs);
*bp = '\0';
cp = trim_space_sep(cp, sep);
if (!*cp) {
free_lbuf(curr);
return;
}
for (i = 0; i < indent; i++)
safe_chr(' ', buff, bufc);
buf = alloc_lbuf("fun_columns");
while (cp) {
objstring = split_token(&cp, sep);
ansinumber = number;
striplen = strlen((char *) strip_ansi(objstring));
if (ansinumber > striplen)
ansinumber = striplen;
p = objstring;
q = buf;
count = 0;
while (p && *p) {
if (count == number) {
break;
}
if (*p == ESC_CHAR) {
/* Start of ANSI code. Skip to end. */
isansi = 1;
while (*p && !isalpha(*p))
*q++ = *p++;
if (*p)
*q++ = *p++;
} else {
*q++ = *p++;
count++;
}
}
if (isansi)
safe_ansi_normal(buf, &q);
*q = '\0';
isansi = 0;
safe_str(buf, buff, bufc);
if (striplen < number) {
/* We only need spaces if we need to pad out.
* Sanitize the number of spaces, too.
*/
spaces = number - striplen;
if (spaces > LBUF_SIZE) {
spaces = LBUF_SIZE;
}
for (i = 0; i < spaces; i++)
safe_chr(' ', buff, bufc);
}
if (!(rturn % (int)((78 - indent) / number))) {
safe_crlf(buff, bufc);
cr = 1;
for (i = 0; i < indent; i++)
safe_chr(' ', buff, bufc);
} else {
cr = 0;
}
rturn++;
}
if (!cr) {
safe_crlf(buff, bufc);
}
free_lbuf(buf);
free_lbuf(curr);
}
/* Code for objmem and playmem borrowed from PennMUSH 1.50 */
static int mem_usage(thing)
dbref thing;
{
int k;
int ca;
char *as, *str;
ATTR *attr;
k = sizeof(OBJ);
k += strlen(Name(thing)) + 1;
for (ca = atr_head(thing, &as); ca; ca = atr_next(&as)) {
str = atr_get_raw(thing, ca);
if (str && *str)
k += strlen(str);
attr = atr_num(ca);
if (attr) {
str = (char *)attr->name;
if (str && *str)
k += strlen(((ATTR *) atr_num(ca))->name);
}
}
return k;
}
static int mem_usage_attr(player, str)
dbref player;
char *str;
{
dbref thing, aowner;
int atr, aflags, alen;
char *abuf;
ATTR *ap;
int bytes_atext = 0;
olist_push();
if (parse_attrib_wild(player, str, &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, &alen);
/* 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);
}
}
olist_pop();
return bytes_atext;
}
FUNCTION(fun_objmem)
{
dbref thing;
if (index(fargs[0], '/') != NULL) {
safe_ltos(buff, bufc, mem_usage_attr(player, fargs[0]));
return;
}
thing = match_thing(player, fargs[0]);
if (thing == NOTHING || !Examinable(player, thing)) {
safe_noperm(buff, bufc);
return;
}
safe_ltos(buff, bufc, mem_usage(thing));
}
FUNCTION(fun_playmem)
{
int tot = 0;
dbref thing;
dbref j;
thing = match_thing(player, fargs[0]);
if (thing == NOTHING || !Examinable(player, thing)) {
safe_noperm(buff, bufc);
return;
}
DO_WHOLE_DB(j)
if (Owner(j) == thing)
tot += mem_usage(j);
safe_ltos(buff, bufc, tot);
}
/* Code for andflags() and orflags() borrowed from PennMUSH 1.50 */
static int handle_flaglists(player, cause, name, fstr, type)
dbref player, cause;
char *name;
char *fstr;
int type; /* 0 for orflags, 1 for andflags */
{
char *s;
char flagletter[2];
FLAGSET fset;
FLAG p_type;
int negate, temp;
int ret = type;
dbref it = match_thing(player, name);
negate = temp = 0;
if (it == NOTHING)
return 0;
if (! (mudconf.pub_flags || Examinable(player, it) || (it == cause)))
return 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) ||
(Flags3(it) & fset.word3) ||
(Typeof(it) == p_type)) {
if (isPlayer(it) && (fset.word2 == CONNECTED)
&& Hidden(it) && !See_Hidden(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 we 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. */
return 1;
}
/* Otherwise, we don't need to do anything. */
}
}
return (ret);
}
FUNCTION(fun_orflags)
{
safe_ltos(buff, bufc, handle_flaglists(player, cause, fargs[0], fargs[1], 0));
}
FUNCTION(fun_andflags)
{
safe_ltos(buff, bufc, handle_flaglists(player, cause, fargs[0], fargs[1], 1));
}
FUNCTION(fun_strtrunc)
{
int number, count = 0;
char *p = (char *)fargs[0];
char *q, *buf;
int isansi = 0;
q = buf = alloc_lbuf("fun_strtrunc");
number = atoi(fargs[1]);
if (number > strlen((char *)strip_ansi(fargs[0])))
number = strlen((char *)strip_ansi(fargs[0]));
if (number < 0) {
safe_str("#-1 OUT OF RANGE", buff, bufc);
free_lbuf(buf);
return;
}
while (p && *p) {
if (count == number) {
break;
}
if (*p == ESC_CHAR) {
/* Start of ANSI code. Skip to end. */
isansi = 1;
while (*p && !isalpha(*p))
*q++ = *p++;
if (*p)
*q++ = *p++;
} else {
*q++ = *p++;
count++;
}
}
if (isansi)
safe_ansi_normal(buf, &q);
*q = '\0';
safe_str(buf, buff, bufc);
free_lbuf(buf);
}
FUNCTION(fun_ifelse)
{
/* This function now assumes that its arguments have not been
evaluated. */
char *str, *mbuff, *bp;
mbuff = bp = alloc_lbuf("fun_ifelse");
str = fargs[0];
exec(mbuff, &bp, 0, player, cause, EV_STRIP | EV_FCHECK | EV_EVAL,
&str, cargs, ncargs);
*bp = '\0';
if (!mbuff || !*mbuff || !xlate(mbuff)) {
str = fargs[2];
exec(buff, bufc, 0, player, cause, EV_STRIP | EV_FCHECK | EV_EVAL,
&str, cargs, ncargs);
} else {
str = fargs[1];
exec(buff, bufc, 0, player, cause, EV_STRIP | EV_FCHECK | EV_EVAL,
&str, cargs, ncargs);
}
free_lbuf(mbuff);
}
FUNCTION(fun_nonzero)
{
/* MUX-style ifelse -- rather than bool check, check if the
* string is non-null/non-zero.
*/
char *str, *mbuff, *bp;
mbuff = bp = alloc_lbuf("fun_nonzero");
str = fargs[0];
exec(mbuff, &bp, 0, player, cause, EV_STRIP | EV_FCHECK | EV_EVAL,
&str, cargs, ncargs);
*bp = '\0';
if (!mbuff || !*mbuff || ((atoi(mbuff) == 0) && is_number(mbuff))) {
str = fargs[2];
exec(buff, bufc, 0, player, cause, EV_STRIP | EV_FCHECK | EV_EVAL,
&str, cargs, ncargs);
} else {
str = fargs[1];
exec(buff, bufc, 0, player, cause, EV_STRIP | EV_FCHECK | EV_EVAL,
&str, cargs, ncargs);
}
free_lbuf(mbuff);
}
FUNCTION(fun_inc)
{
safe_ltos(buff, bufc, atoi(fargs[0]) + 1);
}
FUNCTION(fun_dec)
{
safe_ltos(buff, bufc, atoi(fargs[0]) - 1);
}
#ifdef USE_MAIL
/* Mail functions borrowed from DarkZone */
FUNCTION(fun_mail)
{
/* This function can take one of three formats: 1. mail(num) -->
* returns message <num> for privs. 2. mail(player) -->
* returns number of messages for <player>. 3.
* mail(player, num) --> returns message <num> for
* <player>.
*
* It can now take one more format: 4. mail() --> returns number of
* messages for executor
*/
struct mail *mp;
dbref playerask;
int num, rc, uc, cc;
#ifdef RADIX_COMPRESSION
char *msgbuff;
#endif
if (!fn_range_check("MAIL", nfargs, 0, 2, buff, bufc))
return;
if ((nfargs == 0) || !fargs[0] || !fargs[0][0]) {
count_mail(player, 0, &rc, &uc, &cc);
safe_ltos(buff, bufc, rc + uc);
return;
}
if (nfargs == 1) {
if (!is_number(fargs[0])) {
/* handle the case of wanting to count the number of
* messages
*/
if ((playerask = lookup_player(player, fargs[0], 1)) == NOTHING) {
safe_str("#-1 NO SUCH PLAYER", buff, bufc);
return;
} else if ((player != playerask) && !Wizard(player)) {
safe_noperm(buff, bufc);
return;
} else {
count_mail(playerask, 0, &rc, &uc, &cc);
safe_tprintf_str(buff, bufc, "%d %d %d", rc, uc, cc);
return;
}
} else {
playerask = player;
num = atoi(fargs[0]);
}
} else {
if ((playerask = lookup_player(player, fargs[0], 1)) == NOTHING) {
safe_str("#-1 NO SUCH PLAYER", buff, bufc);
return;
} else if ((player != playerask) && !God(player)) {
safe_noperm(buff, bufc);
return;
}
num = atoi(fargs[1]);
}
if ((num < 1) || (Typeof(playerask) != TYPE_PLAYER)) {
safe_str("#-1 NO SUCH MESSAGE", buff, bufc);
return;
}
mp = mail_fetch(playerask, num);
if (mp != NULL) {
#ifdef RADIX_COMPRESSION
msgbuff = alloc_lbuf("fun_mail");
string_decompress(get_mail_message(mp->number), msgbuff);
safe_str(msgbuff, buff, bufc);
free_lbuf(msgbuff);
#else
safe_str(get_mail_message(mp->number), buff, bufc);
#endif
return;
}
/* ran off the end of the list without finding anything */
safe_str("#-1 NO SUCH MESSAGE", buff, bufc);
}
FUNCTION(fun_mailfrom)
{
/* This function can take these formats: 1) mailfrom(<num>) 2)
* mailfrom(<player>,<num>) It returns the dbref of the player the
* mail is from
*/
struct mail *mp;
dbref playerask;
int num;
if (!fn_range_check("MAILFROM", nfargs, 1, 2, buff, bufc))
return;
if (nfargs == 1) {
playerask = player;
num = atoi(fargs[0]);
} else {
if ((playerask = lookup_player(player, fargs[0], 1)) == NOTHING) {
safe_str("#-1 NO SUCH PLAYER", buff, bufc);
return;
} else if ((player != playerask) && !Wizard(player)) {
safe_noperm(buff, bufc);
return;
}
num = atoi(fargs[1]);
}
if ((num < 1) || (Typeof(playerask) != TYPE_PLAYER)) {
safe_str("#-1 NO SUCH MESSAGE", buff, bufc);
return;
}
mp = mail_fetch(playerask, num);
if (mp != NULL) {
safe_dbref(buff, bufc, mp->from);
return;
}
/* ran off the end of the list without finding anything */
safe_str("#-1 NO SUCH MESSAGE", buff, bufc);
}
#endif
/* ---------------------------------------------------------------------------
* fun_hasattr: does object X have attribute Y.
*/
FUNCTION(fun_hasattr)
{
dbref thing, aowner;
int aflags, alen;
ATTR *attr;
char *tbuf;
thing = match_thing(player, fargs[0]);
if (thing == NOTHING) {
safe_nomatch(buff, bufc);
return;
} else if (!Examinable(player, thing)) {
safe_noperm(buff, bufc);
return;
}
attr = atr_str(fargs[1]);
if (!attr) {
safe_chr('0', buff, bufc);
return;
}
atr_get_info(thing, attr->number, &aowner, &aflags);
if (!See_attr(player, thing, attr, aowner, aflags)) {
safe_chr('0', buff, bufc);
} else {
tbuf = atr_get(thing, attr->number, &aowner, &aflags, &alen);
if (*tbuf) {
safe_chr('1', buff, bufc);
} else {
safe_chr('0', buff, bufc);
}
free_lbuf(tbuf);
}
}
FUNCTION(fun_hasattrp)
{
dbref thing, aowner;
int aflags, alen;
ATTR *attr;
char *tbuf;
thing = match_thing(player, fargs[0]);
if (thing == NOTHING) {
safe_nomatch(buff, bufc);
return;
} else if (!Examinable(player, thing)) {
safe_noperm(buff, bufc);
return;
}
attr = atr_str(fargs[1]);
if (!attr) {
safe_chr('0', buff, bufc);
return;
}
atr_pget_info(thing, attr->number, &aowner, &aflags);
if (!See_attr(player, thing, attr, aowner, aflags)) {
safe_chr('0', buff, bufc);
} else {
tbuf = atr_pget(thing, attr->number, &aowner, &aflags, &alen);
if (*tbuf) {
safe_chr('1', buff, bufc);
} else {
safe_chr('0', buff, bufc);
}
free_lbuf(tbuf);
}
}
/* ---------------------------------------------------------------------------
* 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, alen;
ATTR *attr;
char *objname, *atr_gotten, *bp, *str;
objname = bp = alloc_lbuf("fun_default");
str = fargs[0];
exec(objname, &bp, 0, player, cause, EV_EVAL | EV_STRIP | EV_FCHECK, &str,
cargs, ncargs);
*bp = '\0';
/* 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, &alen);
if (*atr_gotten &&
check_read_perms(player, thing, attr, aowner,
aflags, buff, bufc)) {
safe_known_str(atr_gotten, alen,
buff, bufc);
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.
*/
str = fargs[1];
exec(buff, bufc, 0, player, cause, EV_EVAL | EV_STRIP | EV_FCHECK, &str,
cargs, ncargs);
}
FUNCTION(fun_edefault)
{
dbref thing, aowner;
int attrib, aflags, alen;
ATTR *attr;
char *objname, *atr_gotten, *bp, *str;
objname = bp = alloc_lbuf("fun_edefault");
str = fargs[0];
exec(objname, &bp, 0, player, cause, EV_EVAL | EV_STRIP | EV_FCHECK, &str,
cargs, ncargs);
*bp = '\0';
/* 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, &alen);
if (*atr_gotten &&
check_read_perms(player, thing, attr, aowner,
aflags, buff, bufc)) {
str = atr_gotten;
exec(buff, bufc, 0, thing, player, EV_FIGNORE | EV_EVAL,
&str, (char **)NULL, 0);
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.
*/
str = fargs[1];
exec(buff, bufc, 0, player, cause, EV_EVAL | EV_STRIP | EV_FCHECK, &str,
cargs, ncargs);
}
FUNCTION(fun_udefault)
{
dbref thing, aowner;
int aflags, alen, anum, i, j;
ATTR *ap;
char *objname, *atext, *str, *bp, *xargs[NUM_ENV_VARS];
if (nfargs < 2) /* must have at least two arguments */
return;
str = fargs[0];
objname = bp = alloc_lbuf("fun_udefault");
exec(objname, &bp, 0, player, cause, EV_EVAL | EV_STRIP | EV_FCHECK,
&str, cargs, ncargs);
*bp = '\0';
/* 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, &alen);
if (*atext &&
check_read_perms(player, thing, ap, aowner, aflags,
buff, bufc)) {
/* 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 < nfargs) && fargs[i]) {
bp = xargs[j] = alloc_lbuf("fun_udefault_args");
str = fargs[i];
exec(xargs[j], &bp, 0, player, cause,
EV_STRIP | EV_FCHECK | EV_EVAL,
&str, cargs, ncargs);
} else {
xargs[j] = NULL;
}
}
str = atext;
exec(buff, bufc, 0, thing, cause, EV_FCHECK | EV_EVAL,
&str, 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);
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.
*/
str = fargs[1];
exec(buff, bufc, 0, player, cause, EV_EVAL | EV_STRIP | EV_FCHECK, &str,
cargs, ncargs);
}
/* ---------------------------------------------------------------------------
* fun_findable: can X locate Y
*/
/* Borrowed from PennMUSH 1.50 */
FUNCTION(fun_findable)
{
dbref obj = match_thing(player, fargs[0]);
dbref victim = match_thing(player, fargs[1]);
if (obj == NOTHING)
safe_str("#-1 ARG1 NOT FOUND", buff, bufc);
else if (victim == NOTHING)
safe_str("#-1 ARG2 NOT FOUND", buff, bufc);
else
safe_ltos(buff, bufc, locatable(obj, victim, obj));
}
/* ---------------------------------------------------------------------------
* 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.
*/
/* Borrowed from PennMUSH 1.50 */
FUNCTION(fun_visible)
{
dbref it, thing, aowner;
int aflags, atr;
ATTR *ap;
if ((it = match_thing(player, fargs[0])) == NOTHING) {
safe_chr('0', buff, bufc);
return;
}
if (parse_attrib(player, fargs[1], &thing, &atr)) {
if (atr == NOTHING) {
safe_ltos(buff, bufc, Examinable(it, thing));
return;
}
ap = atr_num(atr);
atr_pget_info(thing, atr, &aowner, &aflags);
safe_ltos(buff, bufc, See_attr(it, thing, ap, aowner, aflags));
return;
}
thing = match_thing(player, fargs[1]);
if (!Good_obj(thing)) {
safe_chr('0', buff, bufc);
return;
}
safe_ltos(buff, bufc, Examinable(it, thing));
}
/* ---------------------------------------------------------------------------
* 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.
*/
/* Borrowed from PennMUSH 1.50 */
FUNCTION(fun_elements)
{
int nwords, cur;
char *ptrs[LBUF_SIZE / 2];
char *wordlist, *s, *r, sep, osep, *oldp;
svarargs_preamble("ELEMENTS", 4);
oldp = *bufc;
/* 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);
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 (oldp != *bufc) {
print_sep(osep, buff, bufc);
}
safe_str(ptrs[cur], buff, bufc);
}
} while (s);
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
*/
/* Borrowed from PennMUSH 1.50 */
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)) {
safe_str(r, buff, bufc);
return;
}
} while (s);
}
/* ---------------------------------------------------------------------------
* fun_scramble: randomizes the letters in a string.
*/
/* Borrowed from PennMUSH 1.50 */
FUNCTION(fun_scramble)
{
int n, i, j;
char c;
if (!fargs[0] || !*fargs[0]) {
return;
}
n = strlen(fargs[0]);
for (i = 0; i < n; i++) {
j = (random() % (n - i)) + i;
c = fargs[0][i];
fargs[0][i] = fargs[0][j];
fargs[0][j] = c;
}
safe_str(fargs[0], buff, bufc);
}
/* ---------------------------------------------------------------------------
* fun_shuffle: randomize order of words in a list.
*/
/* Borrowed from PennMUSH 1.50 */
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]) {
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, bufc, osep);
}
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, *elems[2], *bp, *str;
int n;
if ((mudstate.func_invk_ctr > mudconf.func_invk_lim) ||
(mudstate.func_nest_lev > mudconf.func_nest_lim))
return 0;
tbuf = alloc_lbuf("u_comp");
elems[0] = (char *)s1;
elems[1] = (char *)s2;
strcpy(tbuf, ucomp_buff);
result = bp = alloc_lbuf("u_comp");
str = tbuf;
exec(result, &bp, 0, ucomp_player, ucomp_cause, EV_STRIP | EV_FCHECK | EV_EVAL,
&str, &(elems[0]), 2);
*bp = '\0';
if (!result)
n = 0;
else {
n = atoi(result);
free_lbuf(result);
}
free_lbuf(tbuf);
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, alen, anum;
dbref thing, aowner;
ATTR *ap;
if ((nfargs == 0) || !fargs[0] || !*fargs[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) {
return;
}
atext = atr_pget(thing, ap->number, &aowner, &aflags, &alen);
if (!*atext || !See_attr(player, thing, ap, aowner, aflags)) {
free_lbuf(atext);
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, bufc, osep);
free_lbuf(list);
free_lbuf(atext);
}
/* ---------------------------------------------------------------------------
* 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) {
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)
safe_str(++last, buff, bufc);
else
safe_str(s, buff, bufc);
}
FUNCTION(fun_matchall)
{
int wcount;
char *r, *s, *old, 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 = ' ';
old = *bufc;
/* Check each word individually, returning the word number of all
* that match. If none match, return a null string.
*/
wcount = 1;
s = trim_space_sep(fargs[0], sep);
do {
r = split_token(&s, sep);
if (quick_wild(fargs[1], r)) {
ltos(tbuf, wcount);
if (old != *bufc) {
print_sep(osep, buff, bufc);
}
safe_str(tbuf, buff, bufc);
}
wcount++;
} while (s);
}
/* ---------------------------------------------------------------------------
* fun_ports: Returns a list of ports for a user.
*/
/* Borrowed from TinyMUSH 2.2 */
FUNCTION(fun_ports)
{
dbref target;
if (!Wizard(player)) {
return;
}
target = lookup_player(player, fargs[0], 1);
if (!Good_obj(target) || !Connected(target)) {
return;
}
make_portlist(player, target, buff, bufc);
}
/* ---------------------------------------------------------------------------
* 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, alen, anum, i, lastn, nwords, wc;
ATTR *ap;
char *str, *atext, *os[10], *atextbuf, *bb_p, sep;
char *cp[10];
int count[LBUF_SIZE / 2];
char tmpbuf[2];
/* 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, 12, buff, bufc)) {
return;
}
if (nfargs < 4) {
sep = ' ';
lastn = nfargs - 1;
} else if (!delim_check(fargs, nfargs, nfargs, &sep, buff, bufc, 0,
player, cause, cargs, ncargs, 0)) {
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) {
return;
}
atext = atr_pget(thing, ap->number, &aowner, &aflags, &alen);
if (!*atext || !See_attr(player, thing, ap, aowner, aflags)) {
free_lbuf(atext);
return;
}
for (i = 0; i < 10; i++)
cp[i] = NULL;
bb_p = *bufc;
/* process the lists, one element at a time. */
for (i = 1; i <= lastn; i++) {
cp[i] = trim_space_sep(fargs[i], sep);
}
nwords = count[1] = countwords(cp[1], sep);
for (i = 2; i<= lastn; i++) {
count[i] = countwords(cp[i], sep);
if (count[i] > nwords)
nwords = count[i];
}
atextbuf = alloc_lbuf("fun_mix");
for (wc = 0;
(wc < nwords) && (mudstate.func_invk_ctr < mudconf.func_invk_lim);
wc++) {
for (i = 1; i <= lastn; i++) {
if (count[i]) {
os[i - 1] = split_token(&cp[i], sep);
} else {
tmpbuf[0] = '\0';
os[i - 1] = tmpbuf;
}
}
strcpy(atextbuf, atext);
if (*bufc != bb_p)
safe_chr(sep, buff, bufc);
str = atextbuf;
exec(buff, bufc, 0, player, cause, EV_STRIP | EV_FCHECK | EV_EVAL,
&str, &(os[0]), lastn);
}
free_lbuf(atext);
free_lbuf(atextbuf);
}
/* ---------------------------------------------------------------------------
* fun_step: A little like a fusion of iter() and mix(), it takes elements
* of a list X at a time and passes them into a single function as %0, %1,
* etc. step(<attribute>,<list>,<step size>,<delim>,<outdelim>)
*/
FUNCTION(fun_step)
{
ATTR *ap;
dbref aowner, thing;
int aflags, alen, anum;
char *atext, *str, *cp, *atextbuf, *bb_p, *os[10];
char sep, osep;
int step_size, i;
svarargs_preamble("STEP", 5);
step_size = atoi(fargs[2]);
if ((step_size < 1) || (step_size > NUM_ENV_VARS)) {
notify(player, "Illegal step size.");
return;
}
/* Get attribute. Check 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)
return;
atext = atr_pget(thing, ap->number, &aowner, &aflags, &alen);
if (!*atext || !See_attr(player, thing, ap, aowner, aflags)) {
free_lbuf(atext);
return;
}
cp = trim_space_sep(fargs[1], sep);
atextbuf = alloc_lbuf("fun_step");
bb_p = *bufc;
while (cp && (mudstate.func_invk_ctr < mudconf.func_invk_lim)) {
if (*bufc != bb_p) {
print_sep(osep, buff, bufc);
}
for (i = 0; cp && (i < step_size); i++)
os[i] = split_token(&cp, sep);
strcpy(atextbuf, atext);
str = atextbuf;
exec(buff, bufc, 0, player, cause, EV_STRIP | EV_FCHECK | EV_EVAL,
&str, &(os[0]), i);
}
free_lbuf(atext);
free_lbuf(atextbuf);
}
/* ---------------------------------------------------------------------------
* 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, alen, anum;
ATTR *ap;
char *str, *atext, *atextbuf, *cp, *cbuf;
char start_token, end_token;
int in_string = 1;
if (!fn_range_check("FOREACH", nfargs, 2, 4, buff, bufc))
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]);
}
if (!ap) {
return;
}
atext = atr_pget(thing, ap->number, &aowner, &aflags, &alen);
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], ' ');
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, bufc);
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);
str = atextbuf;
exec(buff, bufc, 0, player, cause, EV_STRIP | EV_FCHECK | EV_EVAL,
&str, &cbuf, 1);
}
free_lbuf(atextbuf);
free_lbuf(atext);
free_lbuf(cbuf);
}
/* ---------------------------------------------------------------------------
* fun_munge: combines two lists in an arbitrary manner.
*/
/* Borrowed from TinyMUSH 2.2 */
FUNCTION(fun_munge)
{
dbref aowner, thing;
int aflags, alen, 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, *str, sep, osep, *oldp;
oldp = *bufc;
if ((nfargs == 0) || !fargs[0] || !*fargs[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) {
return;
}
atext = atr_pget(thing, ap->number, &aowner, &aflags, &alen);
if (!*atext || !See_attr(player, thing, ap, aowner, aflags)) {
free_lbuf(atext);
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) {
safe_str("#-1 LISTS MUST BE OF EQUAL SIZE", buff, bufc);
free_lbuf(atext);
free_lbuf(list1);
free_lbuf(list2);
return;
}
/* Call the u-function with the first list as %0. */
bp = rlist = alloc_lbuf("fun_munge");
str = atext;
exec(rlist, &bp, 0, player, cause, EV_STRIP | EV_FCHECK | EV_EVAL, &str,
&fargs[1], 1);
*bp = '\0';
/* 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);
for (i = 0; i < nresults; i++) {
for (j = 0; j < nptrs1; j++) {
if (!strcmp(results[i], ptrs1[j])) {
if (*bufc != oldp) {
print_sep(osep, buff, bufc);
}
safe_str(ptrs2[j], buff, bufc);
ptrs1[j][0] = '\0';
break;
}
}
}
free_lbuf(atext);
free_lbuf(list1);
free_lbuf(list2);
free_lbuf(rlist);
}
/* ---------------------------------------------------------------------------
* die(<number of dice>,<sides>): Roll XdY dice.
* lrand(<range bottom>,<range top>,<times>[,<delim>]): Generate random list.
*/
FUNCTION(fun_die)
{
int n, die, count;
int total = 0;
if (!fargs[0] || !fargs[1]) {
safe_chr('0', buff, bufc);
return;
}
n = atoi(fargs[0]);
die = atoi(fargs[1]);
if ((n == 0) || (die <= 0)) {
safe_chr('0', buff, bufc);
return;
}
if ((n < 1) || (n > 100)) {
safe_str("#-1 NUMBER OUT OF RANGE", buff, bufc);
return;
}
for (count = 0; count < n; count++)
total += (int) (makerandom() * die) + 1;
safe_ltos(buff, bufc, total);
}
FUNCTION(fun_lrand)
{
char sep;
int n_times, r_bot, r_top, i;
double n_range;
unsigned int tmp;
char *bb_p;
/* Special: the delim is really an output delim. */
if (!fn_range_check("LRAND", nfargs, 3, 4, buff, bufc))
return;
if (!delim_check(fargs, nfargs, 4, &sep, buff, bufc, 0,
player, cause, cargs, ncargs, 1))
return;
/* If we're generating no numbers, since this is a list function,
* we return empty, rather than returning 0.
*/
n_times = atoi(fargs[2]);
if (n_times < 1) {
return;
}
if (n_times > LBUF_SIZE) {
n_times = LBUF_SIZE;
}
r_bot = atoi(fargs[0]);
r_top = atoi(fargs[1]);
if (r_top < r_bot) {
/* This is an error condition. Just return an empty list. We
* obviously can't return a random number between X and Y if
* Y is less than X.
*/
return;
} else if (r_bot == r_top) {
/* Just generate a list of n repetitions. */
bb_p = *bufc;
for (i = 0; i < n_times; i++) {
if (*bufc != bb_p) {
print_sep(sep, buff, bufc);
}
safe_ltos(buff, bufc, r_bot);
}
return;
}
/* We've hit this point, we have a range. Generate a list. */
n_range = (double) r_top - r_bot + 1;
bb_p = *bufc;
for (i = 0; i < n_times; i++) {
if (*bufc != bb_p) {
print_sep(sep, buff, bufc);
}
tmp = (unsigned int) (makerandom() * n_range);
safe_ltos(buff, bufc, r_bot + tmp);
}
}
/* ---------------------------------------------------------------------------
* Miscellaneous binary operations.
*/
/* Borrowed from PennMUSH 1.50 */
FUNCTION(fun_lit)
{
/* Just returns the argument, literally */
safe_str(fargs[0], buff, bufc);
}
/* shl() and shr() borrowed from PennMUSH 1.50 */
FUNCTION(fun_shl)
{
safe_ltos(buff, bufc, atoi(fargs[0]) << atoi(fargs[1]));
}
FUNCTION(fun_shr)
{
safe_ltos(buff, bufc, atoi(fargs[0]) >> atoi(fargs[1]));
}
FUNCTION(fun_band)
{
safe_ltos(buff, bufc, atoi(fargs[0]) & atoi(fargs[1]));
}
FUNCTION(fun_bor)
{
safe_ltos(buff, bufc, atoi(fargs[0]) | atoi(fargs[1]));
}
FUNCTION(fun_bnand)
{
safe_ltos(buff, bufc, atoi(fargs[0]) & ~(atoi(fargs[1])));
}
/* ------------------------------------------------------------------------
*/
FUNCTION(fun_strcat)
{
int i;
safe_str(fargs[0], buff, bufc);
for (i = 1; i < nfargs; i++) {
safe_str(fargs[i], buff, bufc);
}
}
/* grep() and grepi() code borrowed from PennMUSH 1.50 */
char *grep_util(player, thing, pattern, lookfor, len, insensitive)
dbref player, thing;
char *pattern;
char *lookfor;
int len;
int insensitive;
{
/* returns a list of attributes which match <pattern> on <thing>
* whose contents have <lookfor>
*/
dbref aowner;
char *tbuf1, *buf, *text, *attrib;
char *bp, *bufc;
int found;
int ca, aflags, alen;
bp = tbuf1 = alloc_lbuf("grep_util");
bufc = buf = alloc_lbuf("grep_util.parse_attrib");
safe_tprintf_str(buf, &bufc, "#%d/%s", thing, pattern);
olist_push();
if (parse_attrib_wild(player, buf, &thing, 0, 0, 1)) {
for (ca = olist_first(); ca != NOTHING; ca = olist_next()) {
attrib = atr_get(thing, ca, &aowner, &aflags, &alen);
text = attrib;
found = 0;
while (*text && !found) {
if ((!insensitive && !strncmp(lookfor, text, len)) ||
(insensitive && !strncasecmp(lookfor, text, len)))
found = 1;
else
text++;
}
if (found) {
if (bp != tbuf1)
safe_chr(' ', tbuf1, &bp);
safe_str((char *)(atr_num(ca))->name, tbuf1, &bp);
}
free_lbuf(attrib);
}
}
free_lbuf(buf);
*bp = '\0';
olist_pop();
return tbuf1;
}
FUNCTION(fun_grep)
{
char *tp;
dbref it = match_thing(player, fargs[0]);
if (it == NOTHING) {
safe_nomatch(buff, bufc);
return;
} else if (!(Examinable(player, it))) {
safe_noperm(buff, bufc);
return;
}
/* make sure there's an attribute and a pattern */
if (!fargs[1] || !*fargs[1]) {
safe_str("#-1 NO SUCH ATTRIBUTE", buff, bufc);
return;
}
if (!fargs[2] || !*fargs[2]) {
safe_str("#-1 INVALID GREP PATTERN", buff, bufc);
return;
}
tp = grep_util(player, it, fargs[1], fargs[2], strlen(fargs[2]), 0);
safe_str(tp, buff, bufc);
free_lbuf(tp);
}
FUNCTION(fun_grepi)
{
char *tp;
dbref it = match_thing(player, fargs[0]);
if (it == NOTHING) {
safe_nomatch(buff, bufc);
return;
} else if (!(Examinable(player, it))) {
safe_noperm(buff, bufc);
return;
}
/* make sure there's an attribute and a pattern */
if (!fargs[1] || !*fargs[1]) {
safe_str("#-1 NO SUCH ATTRIBUTE", buff, bufc);
return;
}
if (!fargs[2] || !*fargs[2]) {
safe_str("#-1 INVALID GREP PATTERN", buff, bufc);
return;
}
tp = grep_util(player, it, fargs[1], fargs[2], strlen(fargs[2]), 1);
safe_str(tp, buff, bufc);
free_lbuf(tp);
}
/* Borrowed from PennMUSH 1.50 */
FUNCTION(fun_art)
{
/* checks a word and returns the appropriate article, "a" or "an" */
char c = tolower(*fargs[0]);
if (c == 'a' || c == 'e' || c == 'i' || c == 'o' || c == 'u')
safe_known_str("an", 2, buff, bufc);
else
safe_chr('a', buff, bufc);
}
/* Borrowed from PennMUSH 1.50 */
FUNCTION(fun_alphamax)
{
char *amax;
int i = 1;
if (!fargs[0]) {
safe_str("#-1 TOO FEW ARGUMENTS", buff, bufc);
return;
} else
amax = fargs[0];
while ((i < 10) && fargs[i]) {
amax = (strcmp(amax, fargs[i]) > 0) ? amax : fargs[i];
i++;
}
safe_str(amax, buff, bufc);
}
/* Borrowed from PennMUSH 1.50 */
FUNCTION(fun_alphamin)
{
char *amin;
int i = 1;
if (!fargs[0]) {
safe_str("#-1 TOO FEW ARGUMENTS", buff, bufc);
return;
} else
amin = fargs[0];
while ((i < 10) && fargs[i]) {
amin = (strcmp(amin, fargs[i]) < 0) ? amin : fargs[i];
i++;
}
safe_str(amin, buff, bufc);
}
/* Borrowed from PennMUSH 1.50 */
FUNCTION(fun_valid)
{
/* Checks to see if a given <something> is valid as a parameter of a
* given type (such as an object name).
*/
if (!fargs[0] || !*fargs[0] || !fargs[1] || !*fargs[1]) {
safe_chr('0', buff, bufc);
} else if (!strcasecmp(fargs[0], "name"))
safe_ltos(buff, bufc, ok_name(fargs[1]));
else
safe_nothing(buff, bufc);
}
/* Borrowed from PennMUSH 1.50 */
FUNCTION(fun_hastype)
{
dbref it = match_thing(player, fargs[0]);
if (it == NOTHING) {
safe_nomatch(buff, bufc);
return;
}
if (!fargs[1] || !*fargs[1]) {
safe_str("#-1 NO SUCH TYPE", buff, bufc);
return;
}
switch (*fargs[1]) {
case 'r':
case 'R':
safe_chr((Typeof(it) == TYPE_ROOM) ? '1' : '0', buff, bufc);
break;
case 'e':
case 'E':
safe_chr((Typeof(it) == TYPE_EXIT) ? '1' : '0', buff, bufc);
break;
case 'p':
case 'P':
safe_chr((Typeof(it) == TYPE_PLAYER) ? '1' : '0', buff, bufc);
break;
case 't':
case 'T':
safe_chr((Typeof(it) == TYPE_THING) ? '1' : '0', buff, bufc);
break;
default:
safe_str("#-1 NO SUCH TYPE", buff, bufc);
break;
};
}
/* Borrowed from PennMUSH 1.50 */
FUNCTION(fun_lparent)
{
dbref it;
dbref par;
char tbuf1[20];
int i;
it = match_thing(player, fargs[0]);
if (!Good_obj(it)) {
safe_nomatch(buff, bufc);
return;
} else if (!(Examinable(player, it))) {
safe_noperm(buff, bufc);
return;
}
sprintf(tbuf1, "#%d", it);
safe_str(tbuf1, buff, bufc);
par = Parent(it);
i = 1;
while (Good_obj(par) && Examinable(player, it) &&
(i < mudconf.parent_nest_lim)) {
sprintf(tbuf1, " #%d", par);
safe_str(tbuf1, buff, bufc);
it = par;
par = Parent(par);
i++;
}
}
/* ---------------------------------------------------------------------------
* 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);
s_StackCount(thing, 0);
}
}
static void stack_set(thing, sp)
dbref thing;
STACK *sp;
{
STACK *xsp;
int stat;
if (!sp) {
nhashdelete(thing, &mudstate.objstack_htab);
s_StackCount(thing, StackCount(thing) - 1);
return;
}
if (StackCount(thing) + 1 > mudconf.stack_lim) {
XFREE(sp->data, "stack_max_data");
XFREE(sp, "stack_max");
return;
}
xsp = stack_get(thing);
if (xsp) {
stat = nhashrepl(thing, (int *) sp, &mudstate.objstack_htab);
} else {
stat = nhashadd(thing, (int *) sp, &mudstate.objstack_htab);
s_StackCount(thing, StackCount(thing) + 1);
Set_Max(mudstate.max_stacks, mudstate.objstack_htab.entries);
}
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);
if (!fargs[0]) {
it = player;
} else {
stack_object(player, it);
}
stack_clr(it);
}
FUNCTION(fun_items)
{
dbref it;
int i;
STACK *sp;
if (!fargs[0]) {
it = player;
} else {
stack_object(player, it);
}
for (i = 0, sp = stack_get(it); sp != NULL; sp = sp->next, i++)
;
safe_ltos(buff, bufc, i);
}
FUNCTION(fun_push)
{
dbref it;
char *data;
STACK *sp;
xvarargs_preamble("PUSH", 1, 2);
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);
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);
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, bufc, fargs, flag)
dbref player, cause;
char *buff;
char **bufc;
char *fargs[];
int flag; /* if flag, don't copy */
{
dbref it;
int pos, count = 0;
STACK *sp;
STACK *prev = NULL;
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) {
safe_str(sp->data, buff, bufc);
}
if (count == 0) {
stack_set(it, sp->next);
} else {
prev->next = sp->next;
}
XFREE(sp->data, "stack_pop_data");
XFREE(sp, "stack_pop");
s_StackCount(it, StackCount(it) - 1);
}
FUNCTION(fun_pop)
{
xvarargs_preamble("POP", 0, 2);
handle_pop(player, cause, buff, bufc, fargs, 0);
}
FUNCTION(fun_toss)
{
xvarargs_preamble("TOSS", 0, 2);
handle_pop(player, cause, buff, bufc, fargs, 1);
}
FUNCTION(fun_popn)
{
dbref it;
int pos, nitems, i, count = 0, over = 0, first = 0;
STACK *sp, *tp, *xp;
STACK *prev = NULL;
char sep;
varargs_preamble("POPN", 4);
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 (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 (!first) {
safe_chr(sep, buff, bufc);
first = 1;
}
over = safe_str(tp->data, buff, bufc);
}
xp = tp;
tp = tp->next;
XFREE(xp->data, "stack_popn_data");
XFREE(xp, "stack_popn");
s_StackCount(it, StackCount(it) - 1);
}
/* Relink the chain. */
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);
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;
safe_str(sp->data, buff, bufc);
}
FUNCTION(fun_lstack)
{
char sep;
dbref it;
STACK *sp;
char *bp;
int over = 0, first = 1;
mvarargs_preamble("LSTACK", 0, 2);
if (!fargs[0]) {
it = player;
} else {
stack_object(player, it);
}
bp = buff;
for (sp = stack_get(it); (sp != NULL) && !over; sp = sp->next) {
if (!first) {
safe_chr(sep, buff, bufc);
} else {
first = 0;
}
over = safe_str(sp->data, buff, bufc);
}
}
/* ---------------------------------------------------------------------------
* fun_x: Returns a variable. x(<variable name>)
* fun_setx: Sets a variable. xset(<variable name>,<value>)
* fun_xvars: Takes a list, parses it, sets it into variables.
* xvars(<space-separated variable list>,<list>,<delimiter>)
* fun_let: Takes a list of variables and their values, sets them, executes
* a function, and clears out the variables. (Scheme/ML-like.)
* If <list> is empty, the values are reset to null.
* let(<space-separated var list>,<list>,<body>,<delimiter>)
* fun_lvars: Shows a list of variables associated with that object.
* fun_clearvars: Clears all variables associated with that object.
*/
static void set_xvar(obj, name, data)
dbref obj;
char *name;
char *data;
{
VARENT *xvar;
char tbuf[SBUF_SIZE], *tp, *p;
/* If we don't have at least one character in the name, toss it. */
if (!name || !*name)
return;
/* Variable string is '<dbref number minus #>.<variable name>'. We
* lowercase all names. Note that we're going to end up automatically
* truncating long names.
*/
tp = tbuf;
safe_ltos(tbuf, &tp, obj);
safe_sb_chr('.', tbuf, &tp);
for (p = name; *p; p++)
*p = ToLower(*p);
safe_sb_str(name, tbuf, &tp);
*tp = '\0';
/* Search for it. If it exists, replace it. If we get a blank string,
* delete the variable.
*/
if ((xvar = (VARENT *) hashfind(tbuf, &mudstate.vars_htab))) {
if (xvar->text) {
XFREE(xvar->text, "xvar_data");
}
if (data && *data) {
xvar->text = (char *) XMALLOC(sizeof(char) * (strlen(data) + 1),
"xvar_data");
if (!xvar->text)
return; /* out of memory */
strcpy(xvar->text, data);
} else {
xvar->text = NULL;
XFREE(xvar, "xvar_struct");
hashdelete(tbuf, &mudstate.vars_htab);
s_VarsCount(obj, VarsCount(obj) - 1);
}
} else {
/* We haven't found it. If it's non-empty, set it, provided we're
* not running into a limit on the number of vars per object.
*/
if (VarsCount(obj) + 1 > mudconf.numvars_lim)
return;
if (data && *data) {
xvar = (VARENT *) XMALLOC(sizeof(VARENT), "xvar_struct");
if (!xvar)
return; /* out of memory */
xvar->text = (char *) XMALLOC(sizeof(char) * (strlen(data) + 1),
"xvar_data");
if (!xvar->text)
return; /* out of memory */
strcpy(xvar->text, data);
hashadd(tbuf, (int *) xvar, &mudstate.vars_htab);
s_VarsCount(obj, VarsCount(obj) + 1);
Set_Max(mudstate.max_vars, mudstate.vars_htab.entries);
}
}
}
static void clear_xvars(obj, xvar_names, n_xvars)
dbref obj;
char **xvar_names;
int n_xvars;
{
/* Clear out an array of variable names. */
char pre[SBUF_SIZE], tbuf[SBUF_SIZE], *tp, *p;
VARENT *xvar;
int i;
/* Build our dbref bit first. */
tp = pre;
safe_ltos(pre, &tp, obj);
safe_sb_chr('.', pre, &tp);
*tp = '\0';
/* Go clear stuff. */
for (i = 0; i < n_xvars; i++) {
for (p = xvar_names[i]; *p; p++)
*p = ToLower(*p);
tp = tbuf;
safe_sb_str(pre, tbuf, &tp);
safe_sb_str(xvar_names[i], tbuf, &tp);
*tp = '\0';
if ((xvar = (VARENT *) hashfind(tbuf, &mudstate.vars_htab))) {
if (xvar->text) {
XFREE(xvar->text, "xvar_data");
xvar->text = NULL;
}
XFREE(xvar, "xvar_struct");
hashdelete(tbuf, &mudstate.vars_htab);
}
}
s_VarsCount(obj, VarsCount(obj) - n_xvars);
}
void xvars_clr(player)
dbref player;
{
char tbuf[SBUF_SIZE], *tp;
HASHTAB *htab;
HASHENT *hptr, *last, *next;
int i, len;
VARENT *xvar;
tp = tbuf;
safe_ltos(tbuf, &tp, player);
safe_sb_chr('.', tbuf, &tp);
*tp = '\0';
len = strlen(tbuf);
htab = &mudstate.vars_htab;
for (i = 0; i < htab->hashsize; i++) {
last = NULL;
for (hptr = htab->entry->element[i]; hptr != NULL; last = hptr) {
next = hptr->next;
if (!strncmp(tbuf, hptr->target, len)) {
if (last == NULL)
htab->entry->element[i] = next;
else
last->next = next;
xvar = (VARENT *) hptr->data;
XFREE(xvar->text, "xvar_data");
XFREE(xvar, "xvar_struct");
free(hptr->target);
free(hptr);
htab->deletes++;
htab->entries--;
if (htab->entry->element[i] == NULL)
htab->nulls++;
}
hptr = next;
}
}
s_VarsCount(player, 0);
}
FUNCTION(fun_x)
{
VARENT *xvar;
char tbuf[SBUF_SIZE], *tp, *p;
/* Variable string is '<dbref number minus #>.<variable name>' */
tp = tbuf;
safe_ltos(tbuf, &tp, player);
safe_sb_chr('.', tbuf, &tp);
for (p = fargs[0]; *p; p++)
*p = ToLower(*p);
safe_sb_str(fargs[0], tbuf, &tp);
*tp = '\0';
if ((xvar = (VARENT *) hashfind(tbuf, &mudstate.vars_htab)))
safe_str(xvar->text, buff, bufc);
}
FUNCTION(fun_setx)
{
set_xvar(player, fargs[0], fargs[1]);
}
FUNCTION(fun_xvars)
{
char *xvar_names[LBUF_SIZE / 2], *elems[LBUF_SIZE / 2];
int n_xvars, n_elems;
char *varlist, *elemlist;
int i;
char sep;
varargs_preamble("XVARS", 3);
varlist = alloc_lbuf("fun_xvars.vars");
strcpy(varlist, fargs[0]);
n_xvars = list2arr(xvar_names, LBUF_SIZE / 2, varlist, ' ');
if (n_xvars == 0) {
free_lbuf(varlist);
return;
}
if (!fargs[1] || !*fargs[1]) {
/* Empty list, clear out the data. */
clear_xvars(player, xvar_names, n_xvars);
free_lbuf(varlist);
return;
}
elemlist = alloc_lbuf("fun_xvars.elems");
strcpy(elemlist, fargs[1]);
n_elems = list2arr(elems, LBUF_SIZE / 2, elemlist, sep);
if (n_elems != n_xvars) {
safe_str("#-1 LIST MUST BE OF EQUAL SIZE", buff, bufc);
free_lbuf(varlist);
free_lbuf(elemlist);
return;
}
for (i = 0; i < n_elems; i++) {
set_xvar(player, xvar_names[i], elems[i]);
}
free_lbuf(varlist);
free_lbuf(elemlist);
}
FUNCTION(fun_let)
{
char *xvar_names[LBUF_SIZE / 2], *elems[LBUF_SIZE / 2];
char *old_xvars[LBUF_SIZE / 2];
int n_xvars, n_elems;
char *varlist, *elemlist;
char *str, *bp, *p;
char pre[SBUF_SIZE], tbuf[SBUF_SIZE], *tp;
VARENT *xvar;
int i;
char sep;
varargs_preamble("LET", 4);
if (!fargs[0] || !*fargs[0])
return;
varlist = bp = alloc_lbuf("fun_let.vars");
str = fargs[0];
exec(varlist, &bp, 0, player, cause, EV_FCHECK | EV_STRIP | EV_EVAL, &str,
cargs, ncargs);
*bp = '\0';
n_xvars = list2arr(xvar_names, LBUF_SIZE / 2, varlist, ' ');
if (n_xvars == 0) {
free_lbuf(varlist);
return;
}
/* Lowercase our variable names. */
for (i = 0, p = xvar_names[i]; *p; p++)
*p = ToLower(*p);
/* Save our original values. Copying this stuff into an array is
* unnecessarily expensive because we allocate and free memory
* that we could theoretically just trade pointers around for --
* but this way is cleaner.
*/
tp = pre;
safe_ltos(pre, &tp, player);
safe_sb_chr('.', pre, &tp);
*tp = '\0';
for (i = 0; i < n_xvars; i++) {
tp = tbuf;
safe_sb_str(pre, tbuf, &tp);
safe_sb_str(xvar_names[i], tbuf, &tp);
*tp = '\0';
if ((xvar = (VARENT *) hashfind(tbuf, &mudstate.vars_htab))) {
if (xvar->text)
old_xvars[i] = (char *) strdup(xvar->text);
else
old_xvars[i] = NULL;
} else {
old_xvars[i] = NULL;
}
}
if (fargs[1] && *fargs[1]) {
/* We have data, so we should initialize variables to their values,
* ala xvars(). However, unlike xvars(), if we don't get a list,
* we just leave the values alone (we don't clear them out).
*/
elemlist = bp = alloc_lbuf("fun_let.elems");
str = fargs[1];
exec(elemlist, &bp, 0, player, cause, EV_FCHECK | EV_STRIP | EV_EVAL,
&str, cargs, ncargs);
*bp = '\0';
n_elems = list2arr(elems, LBUF_SIZE / 2, elemlist, sep);
if (n_elems != n_xvars) {
safe_str("#-1 LIST MUST BE OF EQUAL SIZE", buff, bufc);
free_lbuf(varlist);
free_lbuf(elemlist);
return;
}
for (i = 0; i < n_elems; i++) {
set_xvar(player, xvar_names[i], elems[i]);
}
free_lbuf(elemlist);
}
/* Now we go to execute our function body. */
str = fargs[2];
exec(buff, bufc, 0, player, cause, EV_FCHECK | EV_STRIP | EV_EVAL, &str,
cargs, ncargs);
/* Restore the old values. */
for (i = 0; i < n_xvars; i++) {
set_xvar(player, xvar_names[i], old_xvars[i]);
if (old_xvars[i])
free(old_xvars[i]);
}
free_lbuf(varlist);
}
FUNCTION(fun_lvars)
{
print_htab_matches(player, &mudstate.vars_htab, buff, bufc);
}
FUNCTION(fun_clearvars)
{
/* This is computationally expensive. Necessary, but its use should
* be avoided if possible.
*/
xvars_clr(player);
}
/* ---------------------------------------------------------------------------
* fun_regparse: Slurp a string into up to ten named variables ($0 - $9).
* Unlike regmatch(), this returns no value.
* regparse(string, pattern, named vars)
*/
FUNCTION(fun_regparse)
{
int i, nqregs, len;
char *qregs[NSUBEXP];
char matchbuf[LBUF_SIZE];
regexp *re;
int matched;
if ((re = regcomp(fargs[1])) == NULL) {
/* Matching error. */
notify_quiet(player, (const char *) regexp_errbuf);
return;
}
matched = (int) regexec(re, fargs[0]);
nqregs = list2arr(qregs, NSUBEXP, fargs[2], ' ');
for (i = 0; i < nqregs; i++) {
if (qregs[i] && *qregs[i]) {
if (!matched || !re->startp[i] || !re->endp[i]) {
set_xvar(player, qregs[i], NULL);
} else {
len = re->endp[i] - re->startp[i];
if (len > LBUF_SIZE - 1)
len = LBUF_SIZE - 1;
else if (len < 0)
len = 0;
strncpy(matchbuf, re->startp[i], len);
matchbuf[len] = '\0';
set_xvar(player, qregs[i], matchbuf);
}
}
}
free(re);
}
/* ---------------------------------------------------------------------------
* 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, bufc))
return;
if ((re = regcomp(fargs[1])) == NULL) {
/* Matching error. */
notify_quiet(player, (const char *) regexp_errbuf);
safe_chr('0', buff, bufc);
return;
}
matched = (int) regexec(re, fargs[0]);
safe_ltos(buff, bufc, 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 */
mudstate.glob_reg_len[curq] = 0;
} 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-term */
mudstate.glob_reg_len[curq] = len;
}
}
free(re);
}
/* ---------------------------------------------------------------------------
* fun_translate: Takes a string and a second argument. If the second argument
* is 0 or s, control characters are converted to spaces. If it's 1 or p,
* they're converted to percent substitutions.
*/
FUNCTION(fun_translate)
{
if (*fargs[0] && *fargs[1]) {
/* Strictly speaking, we're just checking the first char */
if (fargs[1][0] == 's')
safe_str(translate_string(fargs[0], 0), buff, bufc);
else if (fargs[1][0] == 'p')
safe_str(translate_string(fargs[0], 1), buff, bufc);
else
safe_str(translate_string(fargs[0], atoi(fargs[1])), buff, bufc);
}
}
/* ---------------------------------------------------------------------------
* fun_lastcreate: Return the last object of type Y that X created.
*/
FUNCTION(fun_lastcreate)
{
int i, aowner, aflags, alen, obj_list[4], obj_type;
char *obj_str, *p, *tokst;
dbref obj = match_thing(player, fargs[0]);
if (!controls(player, obj)) { /* Automatically checks for GoodObj */
safe_nothing(buff, bufc);
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.");
safe_nothing(buff, bufc);
return;
}
obj_str = atr_get(obj, A_NEWOBJS, &aowner, &aflags, &alen);
if (!*obj_str) {
free_lbuf(obj_str);
safe_nothing(buff, bufc);
return;
}
for (p = strtok_r(obj_str, " ", &tokst), i = 0;
p && (i < 4);
p = strtok_r(NULL, " ", &tokst), i++) {
obj_list[i] = atoi(p);
}
free_lbuf(obj_str);
*(*bufc)++ = '#';
safe_ltos(buff, bufc, obj_list[obj_type]);
}
/*---------------------------------------------------------------------------
* encrypt() and decrypt(): From DarkZone.
*/
/*
* Copy over only alphanumeric chars
*/
static char *crunch_code(code)
char *code;
{
char *in;
char *out;
static char output[LBUF_SIZE];
out = output;
in = code;
while (*in) {
if ((*in >= 32) || (*in <= 126)) {
printf("%c", *in);
*out++ = *in;
}
in++;
}
*out = '\0';
return (output);
}
static char *crypt_code(code, text, type)
char *code;
char *text;
int type;
{
static char textbuff[LBUF_SIZE];
char codebuff[LBUF_SIZE];
int start = 32;
int end = 126;
int mod = end - start + 1;
char *p, *q, *r;
if (!text && !*text)
return ((char *)"");
StringCopy(codebuff, crunch_code(code));
if (!code || !*code || !codebuff || !*codebuff)
return (text);
StringCopy(textbuff, "");
p = text;
q = codebuff;
r = textbuff;
/*
* Encryption: Simply go through each character of the text, get its
* ascii value, subtract start, add the ascii value (less
* start) of the code, mod the result, add start. Continue
*/
while (*p) {
if ((*p < start) || (*p > end)) {
p++;
continue;
}
if (type)
*r++ = (((*p++ - start) + (*q++ - start)) % mod) + start;
else
*r++ = (((*p++ - *q++) + 2 * mod) % mod) + start;
if (!*q)
q = codebuff;
}
*r = '\0';
return (textbuff);
}
FUNCTION(fun_encrypt)
{
safe_str(crypt_code(fargs[1], fargs[0], 1), buff, bufc);
}
FUNCTION(fun_decrypt)
{
safe_str(crypt_code(fargs[1], fargs[0], 0), buff, bufc);
}
/*---------------------------------------------------------------------------
* SQL stuff.
*/
FUNCTION(fun_sql)
{
char row_delim, field_delim;
/* Special -- the last two arguments are output delimiters */
if (!fn_range_check("SQL", nfargs, 1, 3, buff, bufc))
return;
if (!delim_check(fargs, nfargs, 2, &row_delim, buff, bufc, 0,
player, cause, cargs, ncargs, 1))
return;
if (nfargs < 3)
field_delim = row_delim;
else if (!delim_check(fargs, nfargs, 3, &field_delim, buff, bufc, 0,
player, cause, cargs, ncargs, 1))
return;
sql_query(player, fargs[0], buff, bufc, row_delim, field_delim);
}
/*---------------------------------------------------------------------------
* Pueblo HTML-related functions.
*/
#ifdef PUEBLO_SUPPORT
FUNCTION(fun_html_escape)
{
html_escape(fargs[0], buff, bufc);
}
FUNCTION(fun_html_unescape)
{
const char *msg_orig;
int ret = 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, bufc);
msg_orig += 5;
} else if (!strncmp(msg_orig, "<", 4)) {
ret = safe_chr_fn('<', buff, bufc);
msg_orig += 3;
} else if (!strncmp(msg_orig, ">", 4)) {
ret = safe_chr_fn('>', buff, bufc);
msg_orig += 3;
} else if (!strncmp(msg_orig, "&", 5)) {
ret = safe_chr_fn('&', buff, bufc);
msg_orig += 4;
}
break;
default:
ret = safe_chr_fn(*msg_orig, buff, bufc);
break;
}
}
}
FUNCTION(fun_url_escape)
{
/* These are the characters which are converted to %<hex> */
char *escaped_chars = "<>#%{}|\\^~[]';/?:@=&\"+";
const char *msg_orig;
int ret = 0;
char tbuf[10];
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, bufc);
} else if (*msg_orig == ' ') {
ret = safe_chr_fn('+', buff, bufc);
} else{
ret = safe_chr_fn(*msg_orig, buff, bufc);
}
}
}
FUNCTION(fun_url_unescape)
{
const char *msg_orig;
int ret = 0;
unsigned int tempchar;
char tempstr[10];
for (msg_orig = fargs[0]; msg_orig && *msg_orig && !ret;) {
switch (*msg_orig) {
case '+':
ret = safe_chr_fn(' ', buff, bufc);
msg_orig++;
break;
case '%':
strncpy(tempstr, msg_orig+1, 2);
tempstr[2] = '\0';
if ((sscanf(tempstr, "%x", &tempchar) == 1) &&
(tempchar > 0x1F) && (tempchar < 0x7F)) {
ret = safe_chr_fn(tempchar, buff, bufc);
}
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, bufc);
msg_orig++;
break;
}
}
return;
}
#endif /* PUEBLO_SUPPORT */