// funceval.cpp -- MUX function handlers.
//
// $Id: funceval.cpp,v 1.94 2005/10/16 20:48:14 sdennis Exp $
//
#include "copyright.h"
#include "autoconf.h"
#include "config.h"
#include "externs.h"
#include <limits.h>
#include <math.h>
#include "ansi.h"
#include "attrs.h"
#include "command.h"
#include "comsys.h"
#include "functions.h"
#include "misc.h"
#include "pcre.h"
#ifdef REALITY_LVLS
#include "levels.h"
#endif /* REALITY_LVLS */
/* Note: Many functions in this file have been taken, whole or in part, from
* PennMUSH 1.50, and TinyMUSH 2.2, for softcode compatibility. The
* maintainers of MUX would like to thank those responsible for PennMUSH 1.50
* and TinyMUSH 2.2, and hope we have adequately noted in the source where
* credit is due.
*/
extern NAMETAB indiv_attraccess_nametab[];
extern int countwords(char *str, SEP *psep);
extern void arr2list(char *arr[], int alen, char *list, char **bufc, SEP *psep);
extern void make_portlist(dbref, dbref, char *, char **);
bool parse_and_get_attrib(dbref executor, char *fargs[], char **atext, dbref *thing, char *buff, char **bufc)
{
ATTR *ap;
// Two possibilities for the first arg: <obj>/<attr> and <attr>.
//
if (!parse_attrib(executor, fargs[0], thing, &ap))
{
*thing = executor;
ap = atr_str(fargs[0]);
}
// Make sure we got a good attribute.
//
if (!ap)
{
return false;
}
// Use it if we can access it, otherwise return an error.
//
if (!See_attr(executor, *thing, ap))
{
safe_noperm(buff, bufc);
return false;
}
dbref aowner;
int aflags;
*atext = atr_pget(*thing, ap->number, &aowner, &aflags);
if (!*atext)
{
return false;
}
else if (!**atext)
{
free_lbuf(*atext);
return false;
}
return true;
}
#define CWHO_ON 0
#define CWHO_OFF 1
#define CWHO_ALL 2
FUNCTION(fun_cwho)
{
struct channel *ch = select_channel(fargs[0]);
if (!ch)
{
safe_str("#-1 CHANNEL NOT FOUND", buff, bufc);
return;
}
if ( !mudconf.have_comsys
|| ( !Comm_All(executor)
&& executor != ch->charge_who))
{
safe_noperm(buff, bufc);
return;
}
int match_type = CWHO_ON;
if (nfargs == 2)
{
if (mux_stricmp(fargs[1], "all") == 0)
{
match_type = CWHO_ALL;
}
else if (mux_stricmp(fargs[1], "off") == 0)
{
match_type = CWHO_OFF;
}
else if (mux_stricmp(fargs[1], "on") == 0)
{
match_type = CWHO_ON;
}
}
ITL pContext;
struct comuser *user;
ItemToList_Init(&pContext, buff, bufc, '#');
for (user = ch->on_users; user; user = user->on_next)
{
if ( ( match_type == CWHO_ALL
|| ( (Connected(user->who) || isThing(user->who))
&& ( (match_type == CWHO_ON && user->bUserIsOn)
|| (match_type == CWHO_OFF && !(user->bUserIsOn)))))
&& !ItemToList_AddInteger(&pContext, user->who))
{
break;
}
}
ItemToList_Final(&pContext);
}
FUNCTION(fun_beep)
{
safe_chr(BEEP_CHAR, buff, bufc);
}
#define ANSI_F 0x00000001
#define ANSI_H 0x00000002
#define ANSI_U 0x00000004
#define ANSI_I 0x00000008
#define ANSI_FC 0x00000010
#define ANSI_BC 0x00000020
static const unsigned char ansi_have_table[256] =
{
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x00-0x0F
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x10-0x1F
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x20-0x2F
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x30-0x3F
0, 0, ANSI_BC, ANSI_BC, // 0x40-0x43
0, 0, 0, ANSI_BC, // 0x44-0x47
0, 0, 0, 0, // 0x48-0x4B
0, ANSI_BC, 0, 0, // 0x4B-0x4F
0, 0, ANSI_BC, 0, // 0x50-0x53
0, 0, 0, ANSI_BC, // 0x54-0x57
ANSI_BC, ANSI_BC, 0, 0, // 0x58-0x5B
0, 0, 0, 0, // 0x5B-0x5F
0, 0, ANSI_FC, ANSI_FC, // 0x60-0x63
0, 0, ANSI_F, ANSI_FC, // 0x64-0x67
ANSI_H, ANSI_I, 0, 0, // 0x68-0x6B
0, ANSI_FC, 0, 0, // 0x6C-0x6F
0, 0, ANSI_FC, 0, // 0x70-0x73
0, ANSI_U, 0, ANSI_FC, // 0x74-0x77
ANSI_FC, ANSI_FC, 0, 0, // 0x78-0x7B
0, 0, 0, 0, // 0x7B-0x7F
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x80-0x8F
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x90-0x9F
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0xA0-0xAF
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0xB0-0xBF
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0xC0-0xCF
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0xD0-0xDF
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0xE0-0xEF
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 // 0xF0-0xFF
};
void SimplifyColorLetters(char *pOut, char *pIn)
{
if ( pIn[0] == 'n'
&& pIn[1] == '\0')
{
pOut[0] = 'n';
pOut[1] = '\0';
return;
}
char *p;
int have = 0;
size_t nIn = strlen(pIn);
for (p = pIn + nIn - 1; p >= pIn && *p != 'n'; p--)
{
int mask = ansi_have_table[(unsigned char)*p];
if ( mask
&& (have & mask) == 0)
{
*pOut++ = *p;
have |= mask;
}
}
*pOut = '\0';
}
// This function was originally taken from PennMUSH 1.50
//
FUNCTION(fun_ansi)
{
int iArg0;
for (iArg0 = 0; iArg0 + 1 < nfargs; iArg0 += 2)
{
char pOut[8];
SimplifyColorLetters(pOut, fargs[iArg0]);
char tmp[LBUF_SIZE];
char *bp = tmp;
char *s = pOut;
while (*s)
{
extern const char *ColorTable[256];
const char *pColor = ColorTable[(unsigned char)*s];
if (pColor)
{
safe_str(pColor, tmp, &bp);
}
s++;
}
safe_str(fargs[iArg0+1], tmp, &bp);
*bp = '\0';
int nVisualWidth;
size_t nBufferAvailable = LBUF_SIZE - (*bufc - buff) - 1;
size_t nLen = ANSI_TruncateToField(tmp, nBufferAvailable, *bufc,
LBUF_SIZE, &nVisualWidth, ANSI_ENDGOAL_NORMAL);
*bufc += nLen;
}
}
FUNCTION(fun_zone)
{
if (!mudconf.have_zones)
{
safe_str("#-1 ZONES DISABLED", buff, bufc);
return;
}
dbref it = match_thing_quiet(executor, fargs[0]);
if (!Good_obj(it))
{
safe_match_result(it, buff, bufc);
}
else if (Examinable(executor, it))
{
safe_tprintf_str(buff, bufc, "#%d", Zone(it));
}
else
{
safe_nothing(buff, bufc);
}
}
#ifdef SIDE_EFFECT_FUNCTIONS
static bool check_command(dbref player, char *name, char *buff, char **bufc)
{
CMDENT *cmdp = (CMDENT *)hashfindLEN(name, strlen(name), &mudstate.command_htab);
if (cmdp)
{
// Perform checks similiar to (but not exactly like) the
// ones in process_cmdent(): object type checks, permission
// checks, ands global flags.
//
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 true;
}
}
return false;
}
FUNCTION(fun_link)
{
if (check_command(executor, "@link", buff, bufc))
{
return;
}
do_link(executor, caller, enactor, 0, 2, fargs[0], fargs[1]);
}
FUNCTION(fun_tel)
{
if (check_command(executor, "@teleport", buff, bufc))
{
return;
}
do_teleport(executor, caller, enactor, 0, 2, fargs[0], fargs[1]);
}
FUNCTION(fun_pemit)
{
if (check_command(executor, "@pemit", buff, bufc))
{
return;
}
do_pemit_list(executor, PEMIT_PEMIT, false, 0, fargs[0], 0, fargs[1]);
}
FUNCTION(fun_oemit)
{
if (check_command(executor, "@oemit", buff, bufc))
{
return;
}
do_pemit_single(executor, PEMIT_OEMIT, false, 0, fargs[0], 0, fargs[1]);
}
FUNCTION(fun_emit)
{
if (check_command(executor, "@emit", buff, bufc))
{
return;
}
do_say(executor, caller, enactor, SAY_EMIT, fargs[0]);
}
FUNCTION(fun_remit)
{
if (check_command(executor, "@pemit", buff, bufc))
{
return;
}
do_pemit_single(executor, PEMIT_PEMIT, true, 0, fargs[0], 0, fargs[1]);
}
FUNCTION(fun_cemit)
{
if (check_command(executor, "@cemit", buff, bufc))
{
return;
}
do_cemit(executor, caller, enactor, 0, nfargs, fargs[0], fargs[1]);
}
// ------------------------------------------------------------------------
// fun_create: Creates a room, thing or exit.
//
FUNCTION(fun_create)
{
SEP sep;
if (!OPTIONAL_DELIM(3, sep, DELIM_DFLT))
{
return;
}
char *name = fargs[0];
if (!name || !*name)
{
safe_str("#-1 ILLEGAL NAME", buff, bufc);
return;
}
if (nfargs >= 3 && *fargs[2])
{
sep.str[0] = *fargs[2];
}
else
{
sep.str[0] = 't';
}
dbref thing;
int cost;
switch (sep.str[0])
{
case 'r':
if (check_command(executor, "@dig", buff, bufc))
{
return;
}
thing = create_obj(executor, TYPE_ROOM, name, 0);
if (thing != NOTHING)
{
local_data_create(thing);
}
break;
case 'e':
if (check_command(executor, "@open", buff, bufc))
{
return;
}
thing = create_obj(executor, TYPE_EXIT, name, 0);
if (thing != NOTHING)
{
s_Exits(thing, executor);
s_Next(thing, Exits(executor));
s_Exits(executor, thing);
local_data_create(thing);
}
break;
default:
if (check_command(executor, "@create", buff, bufc))
{
return;
}
if (*fargs[1])
{
cost = mux_atol(fargs[1]);
if ( cost < mudconf.createmin
|| mudconf.createmax < cost)
{
safe_range(buff, bufc);
return;
}
}
else
{
cost = mudconf.createmin;
}
thing = create_obj(executor, TYPE_THING, name, cost);
if (thing != NOTHING)
{
move_via_generic(thing, executor, NOTHING, 0);
s_Home(thing, new_home(executor));
local_data_create(thing);
}
break;
}
safe_tprintf_str(buff, bufc, "#%d", thing);
}
FUNCTION(fun_textfile)
{
mux_strlwr(fargs[0]);
CMDENT_ONE_ARG *cmdp = (CMDENT_ONE_ARG *)hashfindLEN(fargs[0],
strlen(fargs[0]), &mudstate.command_htab);
if ( !cmdp
|| cmdp->handler != do_help)
{
safe_str("#-1 NOT FOUND", buff, bufc);
return;
}
if (check_command(executor, fargs[0], buff, bufc))
{
return;
}
help_helper(executor, cmdp->extra, fargs[1], buff, bufc);
}
/* ---------------------------------------------------------------------------
* fun_set: sets an attribute on an object
*/
static void set_attr_internal(dbref player, dbref thing, int attrnum, char *attrtext, int key, char *buff, char **bufc)
{
if (!Good_obj(thing))
{
safe_noperm(buff, bufc);
notify_quiet(player, "You shouldn't be rummaging through the garbage.");
return;
}
dbref aowner;
int aflags;
ATTR *pattr = atr_num(attrnum);
atr_pget_info(thing, attrnum, &aowner, &aflags);
if ( pattr
&& bCanSetAttr(player, thing, pattr))
{
bool 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_noperm(buff, bufc);
}
}
FUNCTION(fun_set)
{
if (check_command(executor, "@set", buff, bufc))
{
return;
}
dbref thing, aowner;
int aflags;
ATTR *pattr;
// See if we have the <obj>/<attr> form, which is how you set
// attribute flags.
//
if (parse_attrib(executor, fargs[0], &thing, &pattr))
{
if ( pattr
&& See_attr(executor, thing, pattr))
{
char *flagname = fargs[1];
// You must specify a flag name.
//
if (flagname[0] == '\0')
{
safe_str("#-1 UNSPECIFIED PARAMETER", buff, bufc);
return;
}
// Check for clearing.
//
bool clear = false;
if (flagname[0] == NOT_TOKEN)
{
flagname++;
clear = true;
}
// Make sure player specified a valid attribute flag.
//
int flagvalue;
if (!search_nametab(executor, indiv_attraccess_nametab, flagname, &flagvalue))
{
safe_str("#-1 CANNOT SET", buff, bufc);
return;
}
// Make sure the object has the attribute present.
//
if (!atr_get_info(thing, pattr->number, &aowner, &aflags))
{
safe_str("#-1 ATTRIBUTE NOT PRESENT ON OBJECT", buff, bufc);
return;
}
// Make sure we can write to the attribute.
//
if (!bCanSetAttr(executor, thing, pattr))
{
safe_noperm(buff, bufc);
return;
}
// Go do it.
//
if (clear)
{
aflags &= ~flagvalue;
}
else
{
aflags |= flagvalue;
}
atr_set_flags(thing, pattr->number, aflags);
return;
}
}
// Find thing.
//
thing = match_controlled_quiet(executor, fargs[0]);
if (!Good_obj(thing))
{
safe_nothing(buff, bufc);
return;
}
// Check for attr set first.
//
char *p;
for (p = fargs[1]; *p && *p != ':'; p++)
{
; // Nothing
}
if (*p)
{
*p++ = 0;
int atr = mkattr(executor, fargs[1]);
if (atr <= 0)
{
safe_str("#-1 UNABLE TO CREATE ATTRIBUTE", buff, bufc);
return;
}
pattr = atr_num(atr);
if (!pattr)
{
safe_noperm(buff, bufc);
return;
}
if (!bCanSetAttr(executor, thing, pattr))
{
safe_noperm(buff, bufc);
return;
}
char *buff2 = alloc_lbuf("fun_set");
// Check for _
//
if (*p == '_')
{
ATTR *pattr2;
dbref thing2;
strcpy(buff2, p + 1);
if (!( parse_attrib(executor, p + 1, &thing2, &pattr2)
&& pattr2))
{
free_lbuf(buff2);
safe_nomatch(buff, bufc);
return;
}
p = buff2;
atr_pget_str(buff2, thing2, pattr2->number, &aowner, &aflags);
if (!See_attr(executor, thing2, pattr2))
{
free_lbuf(buff2);
safe_noperm(buff, bufc);
return;
}
}
// Go set it.
//
set_attr_internal(executor, thing, atr, p, 0, buff, bufc);
free_lbuf(buff2);
return;
}
// Set/clear a flag.
//
flag_set(thing, executor, fargs[1], 0);
}
#endif
// Generate a substitution array.
//
static unsigned int GenCode(char *pCode, const char *pCodeASCII)
{
// Strip out the ANSI.
//
size_t nIn;
char *pIn = strip_ansi(pCodeASCII, &nIn);
// Process the printable characters.
//
char *pOut = pCode;
while (*pIn)
{
unsigned char ch = *pIn;
if ( ' ' <= ch
&& ch <= '~')
{
*pOut++ = ch - ' ';
}
pIn++;
}
*pOut = '\0';
return pOut - pCode;
}
static char *crypt_code(char *code, char *text, bool type)
{
if ( !text
|| text[0] == '\0')
{
return "";
}
if ( !code
|| code[0] == '\0')
{
return text;
}
char codebuff[LBUF_SIZE];
unsigned int nCode = GenCode(codebuff, code);
if (nCode == 0)
{
return text;
}
static char textbuff[LBUF_SIZE];
char *p = strip_ansi(text);
char *q = codebuff;
unsigned int nq = nCode;
char *r = textbuff;
int iMod = '~' - ' ' + 1;
// Encryption loop:
//
while (*p)
{
unsigned char ch = *p;
if ( ' ' <= ch
&& ch <= '~')
{
int iCode = ch - ' ';
if (type)
{
iCode += *q;
if (iMod <= iCode)
{
iCode -= iMod;
}
}
else
{
iCode -= *q;
if (iCode < 0)
{
iCode += iMod;
}
}
*r++ = iCode + ' ';
q++;
nq--;
if (0 == nq)
{
q = codebuff;
nq = nCode;
}
}
p++;
}
*r = '\0';
return textbuff;
}
// Code for encrypt() and decrypt() was taken from the DarkZone
// server.
//
FUNCTION(fun_encrypt)
{
safe_str(crypt_code(fargs[1], fargs[0], true), buff, bufc);
}
FUNCTION(fun_decrypt)
{
safe_str(crypt_code(fargs[1], fargs[0], false), buff, bufc);
}
// Borrowed from DarkZone
//
void scan_zone
(
dbref executor,
char *szZone,
int ObjectType,
char *buff,
char **bufc
)
{
if (!mudconf.have_zones)
{
safe_str("#-1 ZONES DISABLED", buff, bufc);
return;
}
dbref it = match_thing_quiet(executor, szZone);
if (!Good_obj(it))
{
safe_match_result(it, buff, bufc);
return;
}
else if (!( WizRoy(executor)
|| Controls(executor, it)))
{
safe_noperm(buff, bufc);
return;
}
dbref i;
ITL pContext;
ItemToList_Init(&pContext, buff, bufc, '#');
DO_WHOLE_DB(i)
{
if ( Typeof(i) == ObjectType
&& Zone(i) == it
&& !ItemToList_AddInteger(&pContext, i))
{
break;
}
}
ItemToList_Final(&pContext);
}
FUNCTION(fun_zwho)
{
scan_zone(executor, fargs[0], TYPE_PLAYER, buff, bufc);
}
FUNCTION(fun_inzone)
{
scan_zone(executor, fargs[0], TYPE_ROOM, buff, bufc);
}
// Borrowed from DarkZone
//
FUNCTION(fun_children)
{
dbref it = match_thing_quiet(executor, fargs[0]);
if (!Good_obj(it))
{
safe_match_result(it, buff, bufc);
return;
}
else if (!( WizRoy(executor)
|| Controls(executor, it)))
{
safe_noperm(buff, bufc);
return;
}
dbref i;
ITL pContext;
ItemToList_Init(&pContext, buff, bufc, '#');
DO_WHOLE_DB(i)
{
if ( Parent(i) == it
&& !ItemToList_AddInteger(&pContext, i))
{
break;
}
}
ItemToList_Final(&pContext);
}
FUNCTION(fun_objeval)
{
if (!*fargs[0])
{
return;
}
char *name = alloc_lbuf("fun_objeval");
char *bp = name;
char *str = fargs[0];
mux_exec(name, &bp, executor, caller, enactor,
EV_FCHECK | EV_STRIP_CURLY | EV_EVAL, &str, cargs, ncargs);
*bp = '\0';
dbref obj = match_thing_quiet(executor, name);
free_lbuf(name);
if (!Good_obj(obj))
{
safe_match_result(obj, buff, bufc);
return;
}
if (!Controls(executor, obj))
{
// The right circumstances were not met, so we are evaluating
// as the executor who gave the command instead of the
// requested object.
//
obj = executor;
}
mudstate.nObjEvalNest++;
str = fargs[1];
mux_exec(buff, bufc, obj, executor, enactor,
EV_FCHECK | EV_STRIP_CURLY | EV_EVAL, &str, cargs, ncargs);
mudstate.nObjEvalNest--;
}
FUNCTION(fun_localize)
{
char **preserve = NULL;
int *preserve_len = NULL;
preserve = PushPointers(MAX_GLOBAL_REGS);
preserve_len = PushIntegers(MAX_GLOBAL_REGS);
save_global_regs("fun_localize", preserve, preserve_len);
char *str = fargs[0];
mux_exec(buff, bufc, executor, caller, enactor,
EV_FCHECK | EV_STRIP_CURLY | EV_EVAL, &str, cargs, ncargs);
restore_global_regs("fun_localize", preserve, preserve_len);
PopIntegers(preserve_len, MAX_GLOBAL_REGS);
PopPointers(preserve, MAX_GLOBAL_REGS);
}
FUNCTION(fun_null)
{
return;
}
FUNCTION(fun_squish)
{
if (nfargs == 0)
{
return;
}
SEP sep;
if (!OPTIONAL_DELIM(2, sep, DELIM_DFLT))
{
return;
}
char *p;
char *q = fargs[0];
while ((p = strchr(q, sep.str[0])) != NULL)
{
p = p + 1;
size_t nLen = p - q;
safe_copy_buf(q, nLen, buff, bufc);
q = p;
while (*q == sep.str[0])
{
q++;
}
}
safe_str(q, buff, bufc);
}
FUNCTION(fun_stripansi)
{
safe_str(strip_ansi(fargs[0]), buff, bufc);
}
// Borrowed from PennMUSH 1.50
//
FUNCTION(fun_zfun)
{
if (!mudconf.have_zones)
{
safe_str("#-1 ZONES DISABLED", buff, bufc);
return;
}
dbref zone = Zone(executor);
if (!Good_obj(zone))
{
safe_str("#-1 INVALID ZONE", buff, bufc);
return;
}
// Find the user function attribute.
//
int attrib = get_atr(fargs[0]);
if (!attrib)
{
safe_str("#-1 NO SUCH USER FUNCTION", buff, bufc);
return;
}
dbref aowner;
int aflags;
ATTR *pattr = atr_num(attrib);
char *tbuf1 = atr_pget(zone, attrib, &aowner, &aflags);
if ( !pattr
|| !See_attr(executor, zone, pattr))
{
safe_noperm(buff, bufc);
free_lbuf(tbuf1);
return;
}
char *str = tbuf1;
mux_exec(buff, bufc, zone, executor, enactor,
EV_EVAL | EV_STRIP_CURLY | EV_FCHECK, &str, &(fargs[1]), nfargs - 1);
free_lbuf(tbuf1);
}
FUNCTION(fun_columns)
{
SEP sep;
if (!OPTIONAL_DELIM(3, sep, DELIM_EVAL|DELIM_STRING))
{
return;
}
int nWidth = mux_atol(fargs[1]);
int nIndent = 0;
if (nfargs == 4)
{
nIndent = mux_atol(fargs[3]);
if (nIndent < 0 || 77 < nIndent)
{
nIndent = 1;
}
}
int nRight = nIndent + nWidth;
if ( nWidth < 1
|| 78 < nWidth
|| nRight < 1
|| 78 < nRight)
{
safe_range(buff, bufc);
return;
}
char *curr = alloc_lbuf("fun_columns");
char *bp = curr;
char *str = fargs[0];
mux_exec(curr, &bp, executor, caller, enactor,
EV_STRIP_CURLY | EV_FCHECK | EV_EVAL, &str, cargs, ncargs);
*bp = '\0';
int ncp;
char *cp = trim_space_sep_LEN(curr, bp-curr, &sep, &ncp);
if (!*cp)
{
free_lbuf(curr);
return;
}
int nColumns = (78-nIndent)/nWidth;
int iColumn = 0;
int nBufferAvailable = LBUF_SIZE - (*bufc-buff) - 1;
bool bNeedCRLF = false;
while ( cp
&& 0 < nBufferAvailable)
{
if (iColumn == 0)
{
nBufferAvailable -= safe_fill(buff, bufc, ' ', nIndent);
}
char *objstring = split_token(&cp, &sep);
int nVisualWidth;
int nLen = ANSI_TruncateToField(objstring, nBufferAvailable, *bufc,
nWidth, &nVisualWidth, ANSI_ENDGOAL_NORMAL);
*bufc += nLen;
nBufferAvailable -= nLen;
if (nColumns-1 <= iColumn)
{
iColumn = 0;
nBufferAvailable -= safe_copy_buf("\r\n", 2, buff, bufc);
bNeedCRLF = false;
}
else
{
iColumn++;
nBufferAvailable -= safe_fill(buff, bufc, ' ',
nWidth - nVisualWidth);
bNeedCRLF = true;
}
}
if (bNeedCRLF)
{
safe_copy_buf("\r\n", 2, buff, bufc);
}
free_lbuf(curr);
}
// table(<list>,<field width>,<line length>,<delimiter>,<output separator>, <padding>)
//
// Ported from PennMUSH 1.7.3 by Morgan.
//
// TODO: Support ANSI in output separator and padding.
//
FUNCTION(fun_table)
{
// Check argument numbers, assign values and defaults if necessary.
//
char *pPaddingStart = NULL;
char *pPaddingEnd = NULL;
if (nfargs == 6 && *fargs[5])
{
pPaddingStart = strip_ansi(fargs[5]);
pPaddingEnd = strchr(pPaddingStart, '\0');
}
// Get single-character separator.
//
char cSeparator = ' ';
if (nfargs >= 5 && fargs[4][0] != '\0')
{
if (fargs[4][1] == '\0')
{
cSeparator = *fargs[4];
}
else
{
safe_str("#-1 SEPARATOR MUST BE ONE CHARACTER", buff, bufc);
return;
}
}
// Get single-character delimiter.
//
char cDelimiter = ' ';
if (nfargs >= 4 && fargs[3][0] != '\0')
{
if (fargs[3][1] == '\0')
{
cDelimiter = *fargs[3];
}
else
{
safe_str("#-1 DELIMITER MUST BE ONE CHARACTER", buff, bufc);
return;
}
}
// Get line length.
//
int nLineLength = 78;
if (nfargs >= 3)
{
nLineLength = mux_atol(fargs[2]);
}
// Get field width.
//
int nFieldWidth = 10;
if (nfargs >= 2)
{
nFieldWidth = mux_atol(fargs[1]);
}
else
{
nFieldWidth = 10;
}
// Validate nFieldWidth and nLineLength.
//
if ( nLineLength < 1
|| LBUF_SIZE <= nLineLength
|| nFieldWidth < 1
|| nLineLength < nFieldWidth)
{
safe_range(buff, bufc);
return;
}
int nNumCols = nLineLength / nFieldWidth;
SEP sep;
sep.n = 1;
sep.str[0] = cDelimiter;
char *pNext = trim_space_sep(fargs[0], &sep);
if (!*pNext)
{
return;
}
char *pCurrent = split_token(&pNext, &sep);
if (!pCurrent)
{
return;
}
int nBufferAvailable = LBUF_SIZE - (*bufc - buff) - 1;
int nCurrentCol = nNumCols - 1;
for (;;)
{
int nVisibleLength, nPaddingLength;
int nStringLength =
ANSI_TruncateToField( pCurrent, nBufferAvailable, *bufc,
nFieldWidth, &nVisibleLength, ANSI_ENDGOAL_NORMAL);
*bufc += nStringLength;
nBufferAvailable -= nStringLength;
nPaddingLength = nFieldWidth - nVisibleLength;
if (nPaddingLength > nBufferAvailable)
{
nPaddingLength = nBufferAvailable;
}
if (nPaddingLength)
{
nBufferAvailable -= nPaddingLength;
if (pPaddingStart)
{
for ( char *pPaddingCurrent = pPaddingStart;
nPaddingLength > 0;
nPaddingLength--)
{
**bufc = *pPaddingCurrent;
(*bufc)++;
pPaddingCurrent++;
if (pPaddingCurrent == pPaddingEnd)
{
pPaddingCurrent = pPaddingStart;
}
}
}
else
{
memset(*bufc, ' ', nPaddingLength);
*bufc += nPaddingLength;
}
}
pCurrent = split_token(&pNext, &sep);
if (!pCurrent)
{
break;
}
if (!nCurrentCol)
{
nCurrentCol = nNumCols - 1;
if (nBufferAvailable >= 2)
{
char *p = *bufc;
p[0] = '\r';
p[1] = '\n';
nBufferAvailable -= 2;
*bufc += 2;
}
else
{
// nBufferAvailable has less than 2 characters left, if there's
// no room left just break out.
//
if (!nBufferAvailable)
{
break;
}
}
}
else
{
nCurrentCol--;
if (!nBufferAvailable)
{
break;
}
**bufc = cSeparator;
(*bufc)++;
nBufferAvailable--;
}
}
}
// Code for objmem and playmem borrowed from PennMUSH 1.50
//
static int mem_usage(dbref thing)
{
int ca;
char *as;
int k = sizeof(struct object) + strlen(Name(thing)) + 1;
for (ca = atr_head(thing, &as); ca; ca = atr_next(&as))
{
size_t nLen;
const char *str = atr_get_raw_LEN(thing, ca, &nLen);
k += nLen+1;
ATTR *pattr = atr_num(ca);
if (pattr)
{
str = pattr->name;
if ( str
&& *str)
{
k += strlen(str)+1;
}
}
}
return k;
}
FUNCTION(fun_objmem)
{
dbref thing = match_thing_quiet(executor, fargs[0]);
if (!Good_obj(thing))
{
safe_match_result(thing, buff, bufc);
}
else if (Examinable(executor, thing))
{
safe_ltoa(mem_usage(thing), buff, bufc);
}
else
{
safe_noperm(buff, bufc);
}
}
FUNCTION(fun_playmem)
{
dbref thing;
if (nfargs == 1)
{
thing = match_thing_quiet(executor, fargs[0]);
if (!Good_obj(thing))
{
safe_match_result(thing, buff, bufc);
return;
}
else if (!Examinable(executor, thing))
{
safe_noperm(buff, bufc);
return;
}
}
else
{
thing = executor;
}
int tot = 0;
dbref j;
DO_WHOLE_DB(j)
{
if (Owner(j) == thing)
{
tot += mem_usage(j);
}
}
safe_ltoa(tot, buff, bufc);
}
// Code for andflags() and orflags() borrowed from PennMUSH 1.50
// false for orflags, true for andflags
//
static bool handle_flaglists(dbref player, char *name, char *fstr, bool type)
{
dbref it = match_thing_quiet(player, name);
if (!Good_obj(it))
{
return false;
}
char *s;
char flagletter[2];
FLAGSET fset;
FLAG p_type;
bool negate = false;
bool temp = false;
bool ret = type;
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 = true;
s++;
}
else
{
negate = false;
}
if (!*s)
{
return false;
}
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)
{
return false;
}
else
{
continue;
}
}
else
{
// Does the object have this flag?
//
if ( (Flags(it) & fset.word[FLAG_WORD1])
|| (Flags2(it) & fset.word[FLAG_WORD2])
|| (Flags3(it) & fset.word[FLAG_WORD3])
|| Typeof(it) == p_type)
{
if ( isPlayer(it)
&& fset.word[FLAG_WORD2] == CONNECTED
&& Hidden(it)
&& !See_Hidden(player))
{
temp = false;
}
else
{
temp = true;
}
}
else
{
temp = false;
}
if ( type
&& ( (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 false;
}
else if ( !type
&& ( (!negate && temp)
|| (negate && !temp)))
{
// We've found something we want, in an OR. We OR a true with
// the current value.
//
ret |= true;
}
// Otherwise, we don't need to do anything.
//
}
}
return ret;
}
FUNCTION(fun_orflags)
{
safe_bool(handle_flaglists(executor, fargs[0], fargs[1], false), buff, bufc);
}
FUNCTION(fun_andflags)
{
safe_bool(handle_flaglists(executor, fargs[0], fargs[1], true), buff, bufc);
}
FUNCTION(fun_strtrunc)
{
int maxVisualWidth = mux_atol(fargs[1]);
if (maxVisualWidth < 0)
{
safe_range(buff, bufc);
return;
}
if (maxVisualWidth == 0)
{
return;
}
int nVisualWidth;
char buf[LBUF_SIZE+1];
ANSI_TruncateToField(fargs[0], LBUF_SIZE, buf, maxVisualWidth, &nVisualWidth, ANSI_ENDGOAL_NORMAL);
safe_str(buf, buff, bufc);
}
FUNCTION(fun_ifelse)
{
// This function assumes that its arguments have not been evaluated.
//
char *lbuff = alloc_lbuf("fun_ifelse");
char *bp = lbuff;
char *str = fargs[0];
mux_exec(lbuff, &bp, executor, caller, enactor,
EV_STRIP_CURLY | EV_FCHECK | EV_EVAL, &str, cargs, ncargs);
*bp = '\0';
if (!xlate(lbuff))
{
if (nfargs == 3)
{
str = fargs[2];
mux_exec(buff, bufc, executor, caller, enactor,
EV_STRIP_CURLY | EV_FCHECK | EV_EVAL, &str, cargs, ncargs);
}
}
else
{
str = fargs[1];
mux_exec(buff, bufc, executor, caller, enactor,
EV_STRIP_CURLY | EV_FCHECK | EV_EVAL, &str, cargs, ncargs);
}
free_lbuf(lbuff);
}
// Mail functions borrowed from DarkZone.
//
// This function can take one of three formats:
//
// 1. mail(num) --> returns message <num> for privs.
// 2. mail(executor) --> returns number of messages for <executor>.
// 3. mail(executor, num) --> returns message <num> for <executor>.
// 4. mail() --> returns number of messages for executor.
//
FUNCTION(fun_mail)
{
if (!mudconf.have_mailer)
{
safe_str("#-1 MAILER DISABLED.", buff, bufc);
return;
}
dbref playerask;
int num, rc, uc, cc;
// Make sure we have the right number of arguments.
//
if (nfargs == 0 || !fargs[0] || !fargs[0][0])
{
count_mail(executor, 0, &rc, &uc, &cc);
safe_ltoa(rc + uc, buff, bufc);
return;
}
else if (nfargs == 1)
{
if (!is_integer(fargs[0], NULL))
{
// Handle the case of wanting to count the number of
// messages.
//
playerask = lookup_player(executor, fargs[0], true);
if (playerask == NOTHING)
{
playerask = match_thing_quiet(executor, fargs[0]);
if (!isPlayer(playerask))
{
safe_str("#-1 NO SUCH PLAYER", buff, bufc);
return;
}
}
if (playerask == executor || Wizard(executor))
{
count_mail(playerask, 0, &rc, &uc, &cc);
safe_tprintf_str(buff, bufc, "%d %d %d", rc, uc, cc);
}
else
{
safe_noperm(buff, bufc);
}
return;
}
else
{
playerask = executor;
num = mux_atol(fargs[0]);
}
}
else // if (nfargs == 2)
{
playerask = lookup_player(executor, fargs[0], true);
if (playerask == NOTHING)
{
safe_str("#-1 NO SUCH PLAYER", buff, bufc);
return;
}
else if ( (playerask == executor && !mudstate.nObjEvalNest)
|| God(executor))
{
num = mux_atol(fargs[1]);
}
else
{
safe_noperm(buff, bufc);
return;
}
}
if ( num < 1
|| !isPlayer(playerask))
{
safe_str("#-1 NO SUCH MESSAGE", buff, bufc);
return;
}
const char *p = mail_fetch_message(playerask, num);
if (p)
{
safe_str(p, buff, bufc);
return;
}
// Ran off the end of the list without finding anything.
//
safe_str("#-1 NO SUCH MESSAGE", buff, bufc);
}
// This function can take these formats:
//
// 1) mailfrom(<num>)
// 2) mailfrom(<executor>,<num>)
//
// It returns the dbref of the executor the mail is from.
//
FUNCTION(fun_mailfrom)
{
if (!mudconf.have_mailer)
{
safe_str("#-1 MAILER DISABLED.", buff, bufc);
return;
}
// Make sure we have the right number of arguments.
//
int num;
dbref playerask;
if (nfargs == 1)
{
playerask = executor;
num = mux_atol(fargs[0]);
}
else // if (nfargs == 2)
{
playerask = lookup_player(executor, fargs[0], true);
if (playerask == NOTHING)
{
safe_str("#-1 NO SUCH PLAYER", buff, bufc);
return;
}
if ( playerask == executor
|| Wizard(executor))
{
num = mux_atol(fargs[1]);
}
else
{
safe_noperm(buff, bufc);
return;
}
}
if ( num < 1
|| !isPlayer(playerask))
{
safe_str("#-1 NO SUCH MESSAGE", buff, bufc);
return;
}
int from = mail_fetch_from(playerask, num);
if (NOTHING != from)
{
safe_tprintf_str(buff, bufc, "#%d", from);
return;
}
// Ran off the end of the list without finding anything.
//
safe_str("#-1 NO SUCH MESSAGE", buff, bufc);
}
// ---------------------------------------------------------------------------
// fun_hasattr: does object X have attribute Y.
// Hasattr (and hasattrp, which is derived from hasattr) borrowed from
// TinyMUSH 2.2.
void hasattr_handler(char *buff, char **bufc, dbref executor, char *fargs[],
bool bCheckParent)
{
dbref thing = match_thing_quiet(executor, fargs[0]);
if (!Good_obj(thing))
{
safe_match_result(thing, buff, bufc);
return;
}
ATTR *pattr = atr_str(fargs[1]);
bool result = false;
if (pattr)
{
if (!bCanReadAttr(executor, thing, pattr, bCheckParent))
{
safe_noperm(buff, bufc);
return;
}
else
{
if (bCheckParent)
{
dbref aowner;
int aflags;
char *tbuf = atr_pget(thing, pattr->number, &aowner, &aflags);
result = (tbuf[0] != '\0');
free_lbuf(tbuf);
}
else
{
const char *tbuf = atr_get_raw(thing, pattr->number);
result = (tbuf != NULL);
}
}
}
safe_bool(result, buff, bufc);
}
FUNCTION(fun_hasattr)
{
hasattr_handler(buff, bufc, executor, fargs, false);
}
FUNCTION(fun_hasattrp)
{
hasattr_handler(buff, bufc, executor, fargs, true);
}
/* ---------------------------------------------------------------------------
* 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().
*/
// default(), edefault(), and udefault() borrowed from TinyMUSH 2.2
//
#define DEFAULT_DEFAULT 1
#define DEFAULT_EDEFAULT 2
#define DEFAULT_UDEFAULT 4
void default_handler(char *buff, char **bufc, dbref executor, dbref caller, dbref enactor,
char *fargs[], int nfargs, char *cargs[], int ncargs, int key)
{
// Evaluating the first argument.
//
char *objattr = alloc_lbuf("default_handler");
char *bp = objattr;
char *str = fargs[0];
mux_exec(objattr, &bp, executor, caller, enactor,
EV_EVAL | EV_STRIP_CURLY | EV_FCHECK, &str, cargs, ncargs);
*bp = '\0';
// Parse the first argument as either <dbref>/<attrname> or <attrname>.
//
dbref thing;
ATTR *pattr;
if (!parse_attrib(executor, objattr, &thing, &pattr))
{
thing = executor;
pattr = atr_str(objattr);
}
free_lbuf(objattr);
if ( pattr
&& See_attr(executor, thing, pattr))
{
dbref aowner;
int aflags;
char *atr_gotten = atr_pget(thing, pattr->number, &aowner, &aflags);
if (atr_gotten[0] != '\0')
{
switch (key)
{
case DEFAULT_DEFAULT:
safe_str(atr_gotten, buff, bufc);
break;
case DEFAULT_EDEFAULT:
str = atr_gotten;
mux_exec(buff, bufc, thing, executor, executor,
EV_FIGNORE | EV_EVAL, &str, (char **)NULL, 0);
break;
case DEFAULT_UDEFAULT:
{
char *xargs[NUM_ENV_VARS];
int nxargs = nfargs-2;
int i;
for (i = 0; i < nxargs; i++)
{
xargs[i] = alloc_lbuf("fun_udefault_args");
char *bp2 = xargs[i];
str = fargs[i+2];
mux_exec(xargs[i], &bp2,
thing, caller, enactor,
EV_STRIP_CURLY | EV_FCHECK | EV_EVAL,
&str, cargs, ncargs);
}
str = atr_gotten;
mux_exec(buff, bufc, thing, caller, enactor,
EV_FCHECK | EV_EVAL, &str, xargs, nxargs);
for (i = 0; i < nxargs; i++)
{
free_lbuf(xargs[i]);
}
}
break;
}
free_lbuf(atr_gotten);
return;
}
free_lbuf(atr_gotten);
}
// If we've hit this point, we've not gotten anything useful, so
// we go and evaluate the default.
//
str = fargs[1];
mux_exec(buff, bufc, executor, caller, enactor,
EV_EVAL | EV_STRIP_CURLY | EV_FCHECK, &str, cargs, ncargs);
}
FUNCTION(fun_default)
{
default_handler(buff, bufc, executor, caller, enactor, fargs, nfargs, cargs,
ncargs, DEFAULT_DEFAULT);
}
FUNCTION(fun_edefault)
{
default_handler(buff, bufc, executor, caller, enactor, fargs, nfargs, cargs,
ncargs, DEFAULT_EDEFAULT);
}
FUNCTION(fun_udefault)
{
default_handler(buff, bufc, executor, caller, enactor, fargs, nfargs, cargs,
ncargs, DEFAULT_UDEFAULT);
}
/* ---------------------------------------------------------------------------
* fun_findable: can X locate Y
* Borrowed from PennMUSH 1.50
*/
FUNCTION(fun_findable)
{
dbref obj = match_thing_quiet(executor, fargs[0]);
if (!Good_obj(obj))
{
safe_match_result(obj, buff, bufc);
safe_str(" (ARG1)", buff, bufc);
return;
}
dbref victim = match_thing_quiet(executor, fargs[1]);
if (!Good_obj(victim))
{
safe_match_result(victim, buff, bufc);
safe_str(" (ARG2)", buff, bufc);
return;
}
#ifndef WOD_REALMS
#ifndef REALITY_LVLS
safe_bool(locatable(obj, victim, obj), buff, bufc);
#else
if (IsReal(obj, victim))
{
safe_bool(locatable(obj, victim, obj), buff, bufc);
}
else safe_chr('0', buff, bufc);
#endif
#else
#ifndef REALITY_LVLS
if (REALM_DO_HIDDEN_FROM_YOU != DoThingToThingVisibility(obj, victim, ACTION_IS_STATIONARY))
{
safe_bool(locatable(obj, victim, obj), buff, bufc);
}
else
{
safe_chr('0', buff, bufc);
}
#else
if (REALM_DO_HIDDEN_FROM_YOU != DoThingToThingVisibility(obj, victim, ACTION_IS_STATIONARY))
{
safe_bool(locatable(obj, victim, obj), buff, bufc);
}
else if (IsReal(obj, victim))
{
safe_bool(locatable(obj, victim, obj), buff, bufc);
}
else safe_chr('0', buff, bufc);
#endif
#endif
}
/* ---------------------------------------------------------------------------
* isword: is every character in the argument a letter?
* Borrowed from PennMUSH 1.50
*/
FUNCTION(fun_isword)
{
char *p;
bool result = true;
for (p = fargs[0]; *p; p++)
{
if (!mux_isalpha(*p))
{
result = false;
break;
}
}
safe_bool(result, buff, bufc);
}
/* ---------------------------------------------------------------------------
* fun_visible. Borrowed from PennMUSH 1.50
*/
FUNCTION(fun_visible)
{
dbref it = match_thing_quiet(executor, fargs[0]);
if (!Good_obj(it))
{
safe_match_result(it, buff, bufc);
safe_str(" (ARG1)", buff, bufc);
return;
}
else if (!Controls(executor, it))
{
safe_noperm(buff, bufc);
return;
}
bool result = false;
dbref thing;
ATTR *pattr;
if (!parse_attrib(executor, fargs[1], &thing, &pattr))
{
thing = match_thing_quiet(executor, fargs[1]);
if (!Good_obj(thing))
{
safe_match_result(thing, buff, bufc);
safe_str(" (ARG2)", buff, bufc);
return;
}
}
if (Good_obj(thing))
{
if (pattr)
{
result = (See_attr(it, thing, pattr));
}
else
{
result = (Examinable(it, thing));
}
}
safe_bool(result, buff, bufc);
}
/* ---------------------------------------------------------------------------
* 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)
{
SEP sep;
if (!OPTIONAL_DELIM(3, sep, DELIM_DFLT|DELIM_STRING))
{
return;
}
SEP osep = sep;
if (!OPTIONAL_DELIM(4, osep, DELIM_NULL|DELIM_CRLF|DELIM_STRING|DELIM_INIT))
{
return;
}
int nwords, cur;
char *ptrs[LBUF_SIZE / 2];
char *wordlist, *s, *r;
bool bFirst = true;
// 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], &sepSpace);
// Go through the second list, grabbing the numbers and finding the
// corresponding elements.
//
do {
r = split_token(&s, &sepSpace);
cur = mux_atol(r) - 1;
if ( cur >= 0
&& cur < nwords
&& ptrs[cur])
{
if (!bFirst)
{
print_sep(&osep, buff, bufc);
}
else
{
bFirst = false;
}
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*,+) => Foof-3
* Borrowed from PennMUSH 1.50
*/
FUNCTION(fun_grab)
{
SEP sep;
if (!OPTIONAL_DELIM(3, sep, DELIM_DFLT|DELIM_STRING))
{
return;
}
// Walk the wordstring, until we find the word we want.
//
char *s = trim_space_sep(fargs[0], &sep);
do
{
char *r = split_token(&s, &sep);
mudstate.wild_invk_ctr = 0;
if (quick_wild(fargs[1], r))
{
safe_str(r, buff, bufc);
return;
}
} while (s);
}
FUNCTION(fun_graball)
{
SEP sep;
if (!OPTIONAL_DELIM(3, sep, DELIM_DFLT|DELIM_STRING))
{
return;
}
SEP osep = sep;
if (!OPTIONAL_DELIM(4, osep, DELIM_NULL|DELIM_CRLF|DELIM_STRING|DELIM_INIT))
{
return;
}
bool bFirst = true;
char *s = trim_space_sep(fargs[0], &sep);
do
{
char *r = split_token(&s, &sep);
mudstate.wild_invk_ctr = 0;
if (quick_wild(fargs[1], r))
{
if (!bFirst)
{
print_sep(&osep, buff, bufc);
}
else
{
bFirst = false;
}
safe_str(r, buff, bufc);
}
} while (s);
}
/* ---------------------------------------------------------------------------
* fun_scramble: randomizes the letters in a string.
* Borrowed from PennMUSH 1.50
*/
FUNCTION(fun_scramble)
{
size_t n;
char *old = strip_ansi(fargs[0], &n);
if (2 <= n)
{
unsigned int i;
for (i = 0; i < n-1; i++)
{
int j = RandomINT32(i, n-1);
char c = old[i];
old[i] = old[j];
old[j] = c;
}
}
safe_str(old, buff, bufc);
}
/* ---------------------------------------------------------------------------
* fun_shuffle: randomize order of words in a list.
* Borrowed from PennMUSH 1.50
*/
FUNCTION(fun_shuffle)
{
SEP sep;
if (!OPTIONAL_DELIM(2, sep, DELIM_DFLT|DELIM_STRING))
{
return;
}
SEP osep = sep;
if (!OPTIONAL_DELIM(3, osep, DELIM_NULL|DELIM_CRLF|DELIM_STRING|DELIM_INIT))
{
return;
}
char *words[LBUF_SIZE];
int n, i, j;
n = list2arr(words, LBUF_SIZE, fargs[0], &sep);
for (i = 0; i < n-1; i++)
{
j = RandomINT32(i, n-1);
// Swap words[i] with words[j]
//
char *temp = words[i];
words[i] = words[j];
words[j] = temp;
}
arr2list(words, n, buff, bufc, &osep);
}
// pickrand -- choose a random item from a list.
//
FUNCTION(fun_pickrand)
{
if ( nfargs == 0
|| fargs[0][0] == '\0')
{
return;
}
SEP sep;
if (!OPTIONAL_DELIM(2, sep, DELIM_DFLT|DELIM_STRING))
{
return;
}
char *s = trim_space_sep(fargs[0], &sep);
char *t = s;
if (s[0] == '\0')
{
return;
}
INT32 n;
for (n = 0; t; t = next_token(t, &sep), n++)
{
; // Nothing
}
if (n >= 1)
{
INT32 w = RandomINT32(0, n-1);
for (n = 0; n < w; n++)
{
s = next_token(s, &sep);
}
t = split_token(&s, &sep);
safe_str(t, buff, bufc);
}
}
// sortby() code borrowed from TinyMUSH 2.2
//
static char ucomp_buff[LBUF_SIZE];
static dbref ucomp_executor;
static dbref ucomp_caller;
static dbref ucomp_enactor;
static int u_comp(const void *s1, const void *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
|| MuxAlarm.bAlarmed)
{
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;
mux_exec(result, &bp, ucomp_executor, ucomp_caller, ucomp_enactor,
EV_STRIP_CURLY | EV_FCHECK | EV_EVAL, &str, &(elems[0]), 2);
*bp = '\0';
if (!result)
{
n = 0;
}
else
{
n = mux_atol(result);
free_lbuf(result);
}
free_lbuf(tbuf);
return n;
}
typedef int PV(const void *, const void *);
static void sane_qsort(void *array[], int left, int right, PV 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 = RandomINT32(0, 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)
{
SEP sep;
if (!OPTIONAL_DELIM(3, sep, DELIM_DFLT|DELIM_STRING))
{
return;
}
SEP osep = sep;
if (!OPTIONAL_DELIM(4, osep, DELIM_NULL|DELIM_CRLF|DELIM_STRING|DELIM_INIT))
{
return;
}
char *atext;
dbref thing;
if (!parse_and_get_attrib(executor, fargs, &atext, &thing, buff, bufc))
{
return;
}
strcpy(ucomp_buff, atext);
ucomp_executor = thing;
ucomp_caller = executor;
ucomp_enactor = enactor;
char *list = alloc_lbuf("fun_sortby");
strcpy(list, fargs[1]);
char *ptrs[LBUF_SIZE / 2];
int nptrs = list2arr(ptrs, LBUF_SIZE / 2, list, &sep);
if (nptrs > 1)
{
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. Borrowed from TinyMUSH 2.2.
//
FUNCTION(fun_last)
{
// If we are passed an empty arglist return a null string.
//
if (nfargs == 0)
{
return;
}
SEP sep;
if (!OPTIONAL_DELIM(2, sep, DELIM_DFLT|DELIM_STRING))
{
return;
}
char *str;
char *lstr = trim_space_sep(fargs[0], &sep);
while (NULL != (str = next_token(lstr, &sep)))
{
lstr = str;
}
safe_str(lstr, buff, bufc);
}
// Borrowed from TinyMUSH 2.2
//
FUNCTION(fun_matchall)
{
SEP sep;
if (!OPTIONAL_DELIM(3, sep, DELIM_DFLT|DELIM_STRING))
{
return;
}
int wcount;
char *r, *s, *old, tbuf[8];
old = *bufc;
// Check each word individually, returning the word number of all that
// match. If none match, return 0.
//
wcount = 1;
s = trim_space_sep(fargs[0], &sep);
do
{
r = split_token(&s, &sep);
mudstate.wild_invk_ctr = 0;
if (quick_wild(fargs[1], r))
{
mux_ltoa(wcount, tbuf);
if (old != *bufc)
{
safe_chr(' ', buff, bufc);
}
safe_str(tbuf, buff, bufc);
}
wcount++;
} while (s);
if (*bufc == old)
{
safe_chr('0', buff, bufc);
}
}
// ---------------------------------------------------------------------------
// fun_ports: Returns a list of ports for a user.
// Borrowed from TinyMUSH 2.2
//
FUNCTION(fun_ports)
{
dbref target = lookup_player(executor, fargs[0], true);
if (Good_obj(target))
{
if (target == executor || Wizard(executor))
{
if (Connected(target))
{
make_portlist(executor, target, buff, bufc);
}
}
else
{
safe_noperm(buff, bufc);
}
}
else
{
safe_nomatch(buff, bufc);
}
}
/* ---------------------------------------------------------------------------
* fun_mix: Like map, but operates on up to ten lists simultaneously, passing
* the elements as %0 - %10.
* Borrowed from PennMUSH 1.50, upgraded by RhostMUSH.
*/
FUNCTION(fun_mix)
{
// 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.
//
SEP sep;
int lastn;
if (nfargs < 4)
{
sep.n = 1;
sep.str[0] = ' ';
sep.str[1] = '\0';
lastn = nfargs - 1;
}
else if (!OPTIONAL_DELIM(nfargs, sep, DELIM_DFLT|DELIM_STRING))
{
return;
}
else
{
lastn = nfargs - 2;
}
// Get the attribute, check the permissions.
//
dbref thing;
char *atext;
if (!parse_and_get_attrib(executor, fargs, &atext, &thing, buff, bufc))
{
return;
}
char *cp[10];
int i;
for (i = 0; i < lastn; i++)
{
cp[i] = NULL;
}
// Process the lists, one element at a time.
//
for (i = 0; i < lastn; i++)
{
cp[i] = trim_space_sep(fargs[i+1], &sep);
}
int twords;
int nwords = countwords(cp[0], &sep);
for (i = 1; i < lastn; i++)
{
twords = countwords(cp[i], &sep);
if (twords > nwords)
{
nwords = twords;
}
}
char *atextbuf = alloc_lbuf("fun_mix");
char *str, *os[10];
bool bFirst = true;
for (int wc = 0; wc < nwords && !MuxAlarm.bAlarmed; wc++)
{
if (!bFirst)
{
print_sep(&sep, buff, bufc);
}
else
{
bFirst = false;
}
for (i = 0; i < lastn; i++)
{
os[i] = split_token(&cp[i], &sep);
}
strcpy(atextbuf, atext);
str = atextbuf;
mux_exec(buff, bufc, thing, executor, enactor,
EV_STRIP_CURLY | EV_FCHECK | EV_EVAL, &str, &(os[0]), lastn);
}
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.
* Borrowed from TinyMUSH 2.2
*/
FUNCTION(fun_foreach)
{
if ( nfargs != 2
&& nfargs != 4)
{
safe_str("#-1 FUNCTION (FOREACH) EXPECTS 2 OR 4 ARGUMENTS", buff, bufc);
return;
}
char *atext;
dbref thing;
if (!parse_and_get_attrib(executor, fargs, &atext, &thing, buff, bufc))
{
return;
}
char *str;
char cbuf[2], prev = '\0';
char *atextbuf = alloc_lbuf("fun_foreach");
SEP sep;
sep.n = 1;
sep.str[0] = ' ';
sep.str[1] = '\0';
char *cp = trim_space_sep(fargs[1], &sep);
char *bp = cbuf;
cbuf[1] = '\0';
if (nfargs == 4)
{
bool flag = false;
while ( cp
&& *cp
&& mudstate.func_invk_ctr < mudconf.func_invk_lim
&& !MuxAlarm.bAlarmed)
{
cbuf[0] = *cp++;
if (flag)
{
if ( cbuf[0] == *fargs[3]
&& prev != '\\'
&& prev != '%')
{
flag = false;
continue;
}
}
else
{
if ( cbuf[0] == *fargs[2]
&& prev != '\\'
&& prev != '%')
{
flag = true;
continue;
}
else
{
safe_chr(cbuf[0], buff, bufc);
continue;
}
}
strcpy(atextbuf, atext);
str = atextbuf;
mux_exec(buff, bufc, thing, executor, enactor,
EV_STRIP_CURLY | EV_FCHECK | EV_EVAL, &str, &bp, 1);
prev = cbuf[0];
}
}
else
{
while ( cp
&& *cp
&& mudstate.func_invk_ctr < mudconf.func_invk_lim
&& !MuxAlarm.bAlarmed)
{
cbuf[0] = *cp++;
strcpy(atextbuf, atext);
str = atextbuf;
mux_exec(buff, bufc, thing, executor, enactor,
EV_STRIP_CURLY | EV_FCHECK | EV_EVAL, &str, &bp, 1);
}
}
free_lbuf(atextbuf);
free_lbuf(atext);
}
/* ---------------------------------------------------------------------------
* fun_munge: combines two lists in an arbitrary manner.
* Borrowed from TinyMUSH 2.2
*/
FUNCTION(fun_munge)
{
SEP sep;
if (!OPTIONAL_DELIM(4, sep, DELIM_DFLT|DELIM_STRING))
{
return;
}
// Find our object and attribute.
//
char *atext;
dbref thing;
if (!parse_and_get_attrib(executor, fargs, &atext, &thing, buff, bufc))
{
return;
}
int nptrs1, nptrs2, nresults, i, j;
char *list1, *list2, *rlist, *bp, *str, *oldp;
char *ptrs1[LBUF_SIZE / 2], *ptrs2[LBUF_SIZE / 2], *results[LBUF_SIZE / 2];
char *uargs[2];
oldp = *bufc;
// 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;
uargs[0] = fargs[1];
uargs[1] = sep.str;
mux_exec(rlist, &bp, executor, caller, enactor,
EV_STRIP_CURLY | EV_FCHECK | EV_EVAL, &str, uargs, 2);
*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);
bool bFirst = true;
for (i = 0; i < nresults; i++)
{
for (j = 0; j < nptrs1; j++)
{
if (!strcmp(results[i], ptrs1[j]))
{
if (!bFirst)
{
print_sep(&sep, buff, bufc);
}
else
{
bFirst = false;
}
safe_str(ptrs2[j], buff, bufc);
ptrs1[j][0] = '\0';
break;
}
}
}
free_lbuf(atext);
free_lbuf(list1);
free_lbuf(list2);
free_lbuf(rlist);
}
FUNCTION(fun_die)
{
int n = mux_atol(fargs[0]);
int die = mux_atol(fargs[1]);
if ( n == 0
|| die <= 0)
{
safe_chr('0', buff, bufc);
return;
}
if ( n < 1
|| LBUF_SIZE <= n)
{
safe_range(buff, bufc);
return;
}
if ( 3 <= nfargs
&& isTRUE(mux_atol(fargs[2])))
{
ITL pContext;
ItemToList_Init(&pContext, buff, bufc);
for (int count = 0; count < n; count++)
{
if (!ItemToList_AddInteger(&pContext, RandomINT32(1, die)))
{
break;
}
}
ItemToList_Final(&pContext);
return;
}
int total = 0;
for (int count = 0; count < n; count++)
{
total += RandomINT32(1, die);
}
safe_ltoa(total, buff, bufc);
}
FUNCTION(fun_lrand)
{
SEP sep;
if (!OPTIONAL_DELIM(4, sep, DELIM_NULL|DELIM_CRLF|DELIM_STRING))
{
return;
}
int n_times = mux_atol(fargs[2]);
if (n_times < 1)
{
return;
}
if (n_times > LBUF_SIZE)
{
n_times = LBUF_SIZE;
}
INT32 iLower = mux_atol(fargs[0]);
INT32 iUpper = mux_atol(fargs[1]);
if (iLower <= iUpper)
{
for (int i = 0; i < n_times-1; i++)
{
INT32 val = RandomINT32(iLower, iUpper);
safe_ltoa(val, buff, bufc);
print_sep(&sep, buff, bufc);
}
INT32 val = RandomINT32(iLower, iUpper);
safe_ltoa(val, buff, bufc);
}
}
// Borrowed from PennMUSH 1.50
//
FUNCTION(fun_lit)
{
// Just returns the argument, literally.
//
safe_str(fargs[0], buff, bufc);
}
FUNCTION(fun_dumping)
{
#ifdef WIN32
safe_chr('0', buff, bufc);
#else // WIN32
safe_bool(mudstate.dumping, buff, bufc);
#endif // WIN32
}
// The following table contains 64 symbols, so this supports -a-
// radix-64 encoding. It is not however 'unix-to-unix' encoding.
// All of the following characters are valid for an attribute
// name, but not for the first character of an attribute name.
//
static char aRadixTable[] =
"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz@$";
FUNCTION(fun_unpack)
{
// Validate radix if present.
//
INT64 iRadix = 64;
if (nfargs == 2)
{
if ( !is_integer(fargs[1], NULL)
|| (iRadix = mux_atoi64(fargs[1])) < 2
|| 64 < iRadix)
{
safe_str("#-1 RADIX MUST BE A NUMBER BETWEEN 2 and 64", buff, bufc);
return;
}
}
// Build Table of valid characters.
//
char MatchTable[256];
memset(MatchTable, 0, sizeof(MatchTable));
for (int i = 0; i < iRadix; i++)
{
MatchTable[aRadixTable[i]] = i+1;
}
// Validate that first argument contains only characters from the
// subset of permitted characters.
//
char *pString = fargs[0];
INT64 sum;
int c;
int LeadingCharacter;
// Leading whitespace
//
while (mux_isspace(*pString))
{
pString++;
}
// Possible sign
//
LeadingCharacter = c = *pString++;
if (c == '-' || c == '+')
{
c = *pString++;
}
sum = 0;
// Convert symbols
//
int iValue;
while ((iValue = MatchTable[(unsigned int)c]))
{
sum = iRadix * sum + iValue - 1;
c = *pString++;
}
// Interpret sign
//
if (LeadingCharacter == '-')
{
sum = -sum;
}
safe_i64toa(sum, buff, bufc);
}
size_t mux_Pack(INT64 val, int iRadix, char *buf)
{
char *p = buf;
// Handle sign.
//
if (val < 0)
{
*p++ = '-';
val = -val;
}
char *q = p;
while (val > iRadix-1)
{
INT64 iDiv = val / iRadix;
INT64 iTerm = val - iDiv * iRadix;
val = iDiv;
*p++ = aRadixTable[iTerm];
}
*p++ = aRadixTable[val];
int nLength = p - buf;
*p-- = '\0';
// The digits are in reverse order with a possible leading '-'
// if the value was negative. q points to the first digit,
// and p points to the last digit.
//
while (q < p)
{
// Swap characters are *p and *q
//
char temp = *p;
*p = *q;
*q = temp;
// Move p and first digit towards the middle.
//
--p;
++q;
// Stop when we reach or pass the middle.
//
}
return nLength;
}
FUNCTION(fun_pack)
{
// Validate the arguments are numeric.
//
if ( !is_integer(fargs[0], NULL)
|| (nfargs == 2 && !is_integer(fargs[1], NULL)))
{
safe_str("#-1 ARGUMENTS MUST BE NUMBERS", buff, bufc);
return;
}
INT64 val = mux_atoi64(fargs[0]);
// Validate the radix is between 2 and 64.
//
int iRadix = 64;
if (nfargs == 2)
{
iRadix = mux_atol(fargs[1]);
if (iRadix < 2 || 64 < iRadix)
{
safe_str("#-1 RADIX MUST BE A NUMBER BETWEEN 2 and 64", buff, bufc);
return;
}
}
char TempBuffer[76]; // 1 '-', 63 binary digits, 1 '\0', 11 for safety.
int nLength = mux_Pack(val, iRadix, TempBuffer);
safe_copy_buf(TempBuffer, nLength, buff, bufc);
}
FUNCTION(fun_strcat)
{
int i;
for (i = 0; i < nfargs; i++)
{
safe_str(fargs[i], buff, bufc);
}
}
// grep() and grepi() code borrowed from PennMUSH 1.50
//
char *grep_util(dbref player, dbref thing, char *pattern, char *lookfor, int len, bool insensitive)
{
// Returns a list of attributes which match <pattern> on <thing>
// whose contents have <lookfor>.
//
dbref aowner;
char *tbuf1, *buf;
char *bp, *bufc;
int ca, aflags;
tbuf1 = alloc_lbuf("grep_util");
bufc = buf = alloc_lbuf("grep_util.parse_attrib");
bp = tbuf1;
safe_tprintf_str(buf, &bufc, "#%d/%s", thing, pattern);
olist_push();
if (parse_attrib_wild(player, buf, &thing, false, false, true))
{
BMH_State bmhs;
if (insensitive)
{
BMH_PrepareI(&bmhs, len, lookfor);
}
else
{
BMH_Prepare(&bmhs, len, lookfor);
}
for (ca = olist_first(); ca != NOTHING && !MuxAlarm.bAlarmed; ca = olist_next())
{
size_t nText;
char *attrib = atr_get_LEN(thing, ca, &aowner, &aflags, &nText);
int i;
if (insensitive)
{
i = BMH_ExecuteI(&bmhs, len, lookfor, nText, attrib);
}
else
{
i = BMH_Execute(&bmhs, len, lookfor, nText, attrib);
}
if (i >= 0)
{
if (bp != tbuf1)
{
safe_chr(' ', tbuf1, &bp);
}
ATTR *ap = atr_num(ca);
const char *pName = "(WARNING: Bad Attribute Number)";
if (ap)
{
pName = ap->name;
}
safe_str(pName, tbuf1, &bp);
}
free_lbuf(attrib);
}
}
free_lbuf(buf);
*bp = '\0';
olist_pop();
return tbuf1;
}
void grep_handler(char *buff, char **bufc, dbref executor, char *fargs[],
bool bCaseInsens)
{
dbref it = match_thing_quiet(executor, fargs[0]);
if (!Good_obj(it))
{
safe_match_result(it, buff, bufc);
return;
}
if (!Examinable(executor, 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;
}
char *tp = grep_util(executor, it, fargs[1], fargs[2], strlen(fargs[2]), bCaseInsens);
safe_str(tp, buff, bufc);
free_lbuf(tp);
}
FUNCTION(fun_grep)
{
grep_handler(buff, bufc, executor, fargs, false);
}
FUNCTION(fun_grepi)
{
grep_handler(buff, bufc, executor, fargs, true);
}
// Borrowed from PennMUSH 1.50
//
FUNCTION(fun_alphamax)
{
char *amax = fargs[0];
for (int i = 1; i < nfargs; i++)
{
if (fargs[i] && strcmp(amax, fargs[i]) < 0)
{
amax = fargs[i];
}
}
safe_str(amax, buff, bufc);
}
// Borrowed from PennMUSH 1.50
//
FUNCTION(fun_alphamin)
{
char *amin = fargs[0];
for (int i = 1; i < nfargs; i++)
{
if (fargs[i] && strcmp(amin, fargs[i]) > 0)
{
amin = fargs[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)
//
int nValidName;
bool bValid;
if (!*fargs[0] || !*fargs[1])
{
bValid = false;
}
else if (!mux_stricmp(fargs[0], "name"))
{
MakeCanonicalObjectName(fargs[1], &nValidName, &bValid);
}
else if (!mux_stricmp(fargs[0], "attrname"))
{
MakeCanonicalAttributeName(fargs[1], &nValidName, &bValid);
}
else if (!mux_stricmp(fargs[0], "playername"))
{
bValid = ValidatePlayerName(fargs[1]);
}
else
{
safe_nothing(buff, bufc);
return;
}
safe_bool(bValid, buff, bufc);
}
// Borrowed from PennMUSH 1.50
//
FUNCTION(fun_hastype)
{
dbref it = match_thing_quiet(executor, fargs[0]);
if (!Good_obj(it))
{
safe_match_result(it, buff, bufc);
return;
}
bool bResult = false;
switch (mux_tolower(fargs[1][0]))
{
case 'r':
bResult = isRoom(it);
break;
case 'e':
bResult = isExit(it);
break;
case 'p':
bResult = isPlayer(it);
break;
case 't':
bResult = isThing(it);
break;
default:
safe_str("#-1 NO SUCH TYPE", buff, bufc);
break;
}
safe_bool(bResult, buff, bufc);
}
// Borrowed from PennMUSH 1.50
//
FUNCTION(fun_lparent)
{
dbref it = match_thing_quiet(executor, fargs[0]);
if (!Good_obj(it))
{
safe_match_result(it, buff, bufc);
return;
}
else if (!Examinable(executor, it))
{
safe_noperm(buff, bufc);
return;
}
ITL pContext;
ItemToList_Init(&pContext, buff, bufc, '#');
if (!ItemToList_AddInteger(&pContext, it))
{
ItemToList_Final(&pContext);
return;
}
dbref par = Parent(it);
int iNestLevel = 1;
while ( Good_obj(par)
&& Examinable(executor, it)
&& iNestLevel < mudconf.parent_nest_lim)
{
if (!ItemToList_AddInteger(&pContext, par))
{
break;
}
it = par;
par = Parent(par);
iNestLevel++;
}
ItemToList_Final(&pContext);
}
// stacksize - returns how many items are stuffed onto an object stack
//
int stacksize(dbref doer)
{
int i;
STACK *sp;
for (i = 0, sp = Stack(doer); sp != NULL; sp = sp->next, i++)
{
// Nothing
;
}
return i;
}
FUNCTION(fun_lstack)
{
STACK *sp;
dbref doer;
if (nfargs == 0 || !*fargs[0])
{
doer = executor;
}
else
{
doer = match_thing_quiet(executor, fargs[0]);
if (!Good_obj(doer))
{
safe_match_result(doer, buff, bufc);
return;
}
}
if (!Controls(executor, doer))
{
safe_noperm(buff, bufc);
return;
}
for (sp = Stack(doer); sp != NULL; sp = sp->next)
{
safe_str(sp->data, buff, bufc);
if (sp->next != NULL)
{
safe_chr(' ', buff, bufc);
}
}
}
// stack_clr - clear the stack.
//
void stack_clr(dbref obj)
{
// Clear the stack.
//
STACK *sp, *next;
for (sp = Stack(obj); sp != NULL; sp = next)
{
next = sp->next;
free_lbuf(sp->data);
MEMFREE(sp);
sp = NULL;
}
s_Stack(obj, NULL);
}
FUNCTION(fun_empty)
{
dbref doer;
if (nfargs == 0 || !*fargs[0])
{
doer = executor;
}
else
{
doer = match_thing_quiet(executor, fargs[0]);
if (!Good_obj(doer))
{
safe_match_result(doer, buff, bufc);
return;
}
}
if (!Controls(executor, doer))
{
safe_noperm(buff, bufc);
return;
}
stack_clr(doer);
}
FUNCTION(fun_items)
{
dbref doer;
if (nfargs == 0 || !*fargs[0])
{
doer = executor;
}
else
{
doer = match_thing_quiet(executor, fargs[0]);
if (!Good_obj(doer))
{
safe_match_result(doer, buff, bufc);
return;
}
}
if (!Controls(executor, doer))
{
safe_noperm(buff, bufc);
return;
}
safe_ltoa(stacksize(doer), buff, bufc);
}
FUNCTION(fun_peek)
{
STACK *sp;
dbref doer;
int count, pos;
if (nfargs <= 0 || !*fargs[0])
{
doer = executor;
}
else
{
doer = match_thing_quiet(executor, fargs[0]);
if (!Good_obj(doer))
{
safe_match_result(doer, buff, bufc);
return;
}
}
if (!Controls(executor, doer))
{
safe_noperm(buff, bufc);
return;
}
if (nfargs <= 1 || !*fargs[1])
{
pos = 0;
}
else
{
pos = mux_atol(fargs[1]);
}
if (stacksize(doer) == 0)
{
return;
}
if (pos > (stacksize(doer) - 1))
{
safe_str("#-1 POSITION TOO LARGE", buff, bufc);
return;
}
count = 0;
sp = Stack(doer);
while (count != pos)
{
if (sp == NULL)
{
return;
}
count++;
sp = sp->next;
}
safe_str(sp->data, buff, bufc);
}
FUNCTION(fun_pop)
{
dbref doer;
if (nfargs <= 0 || !*fargs[0])
{
doer = executor;
}
else
{
doer = match_thing_quiet(executor, fargs[0]);
if (!Good_obj(doer))
{
safe_match_result(doer, buff, bufc);
return;
}
}
if (!Controls(executor, doer))
{
safe_noperm(buff, bufc);
return;
}
int pos;
if (nfargs <= 1 || !*fargs[1])
{
pos = 0;
}
else
{
pos = mux_atol(fargs[1]);
}
if (stacksize(doer) == 0)
{
return;
}
if (pos > (stacksize(doer) - 1))
{
safe_str("#-1 POSITION TOO LARGE", buff, bufc);
return;
}
STACK *sp = Stack(doer);
STACK *prev = NULL;
int count = 0;
while (count != pos)
{
if (sp == NULL)
{
return;
}
prev = sp;
sp = sp->next;
count++;
}
safe_str(sp->data, buff, bufc);
if (count == 0)
{
s_Stack(doer, sp->next);
free_lbuf(sp->data);
MEMFREE(sp);
sp = NULL;
}
else
{
prev->next = sp->next;
free_lbuf(sp->data);
MEMFREE(sp);
sp = NULL;
}
}
FUNCTION(fun_push)
{
dbref doer;
char *data;
if (nfargs <= 1 || !*fargs[1])
{
doer = executor;
data = fargs[0];
}
else
{
doer = match_thing_quiet(executor, fargs[0]);
if (!Good_obj(doer))
{
safe_match_result(doer, buff, bufc);
return;
}
data = fargs[1];
}
if (!Controls(executor, doer))
{
safe_noperm(buff, bufc);
return;
}
if (stacksize(doer) >= mudconf.stack_limit)
{
safe_str("#-1 STACK SIZE EXCEEDED", buff, bufc);
return;
}
STACK *sp = (STACK *)MEMALLOC(sizeof(STACK));
ISOUTOFMEMORY(sp);
sp->next = Stack(doer);
sp->data = alloc_lbuf("push");
strcpy(sp->data, data);
s_Stack(doer, sp);
}
/* ---------------------------------------------------------------------------
* 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.
*/
void real_regmatch(const char *search, const char *pattern, char *registers,
int nfargs, char *buff, char **bufc, bool cis)
{
if (MuxAlarm.bAlarmed)
{
return;
}
const char *errptr;
int erroffset;
// To capture N substrings, you need space for 3(N+1) offsets in the
// offset vector. We'll allow 2N-1 substrings and possibly ignore some.
//
const int ovecsize = 6 * MAX_GLOBAL_REGS;
int ovec[ovecsize];
pcre *re = pcre_compile(pattern, cis ? PCRE_CASELESS : 0,
&errptr, &erroffset, NULL);
if (!re)
{
// Matching error.
//
safe_str("#-1 REGEXP ERROR ", buff, bufc);
safe_str(errptr, buff, bufc);
return;
}
int matches = pcre_exec(re, NULL, search, strlen(search), 0, 0,
ovec, ovecsize);
if (matches == 0)
{
// There were too many substring matches. See docs for
// pcre_copy_substring().
//
matches = ovecsize / 3;
}
safe_bool(matches > 0, buff, bufc);
if (matches < 0)
{
matches = 0;
}
// If we don't have a third argument, we're done.
//
if (nfargs != 3)
{
MEMFREE(re);
return;
}
// We need to parse the list of registers. If a register is
// mentioned in the list, then either fill the register with the
// subexpression, or if there wasn't a match, clear it.
//
const int NSUBEXP = 2 * MAX_GLOBAL_REGS;
char *qregs[NSUBEXP];
SEP sep;
sep.n = 1;
memcpy(sep.str, " ", 2);
int nqregs = list2arr(qregs, NSUBEXP, registers, &sep);
int i;
for (i = 0; i < nqregs; i++)
{
int curq;
if ( qregs[i]
&& *qregs[i]
&& (curq = mux_RegisterSet[(unsigned char)qregs[i][0]]) != -1
&& qregs[i][1] == '\0'
&& curq < MAX_GLOBAL_REGS)
{
if (!mudstate.global_regs[curq])
{
mudstate.global_regs[curq] = alloc_lbuf("fun_regmatch");
}
int len;
len = pcre_copy_substring(search, ovec, matches, i,
mudstate.global_regs[curq], LBUF_SIZE);
mudstate.glob_reg_len[curq] = (len > 0 ? len : 0);
}
}
MEMFREE(re);
}
FUNCTION(fun_regmatch)
{
real_regmatch(fargs[0], fargs[1], fargs[2], nfargs, buff, bufc, false);
}
FUNCTION(fun_regmatchi)
{
real_regmatch(fargs[0], fargs[1], fargs[2], nfargs, buff, bufc, true);
}
/* ---------------------------------------------------------------------------
* regrab(), regraball(). Like grab() and graball(), using a regular expression
* instead of a wildcard pattern. The versions ending in i are case-insensitive.
*/
void real_regrab(char *search, const char *pattern, SEP *psep, char *buff,
char **bufc, bool cis, bool all)
{
if (MuxAlarm.bAlarmed)
{
return;
}
pcre *re;
pcre_extra *study = NULL;
const char *errptr;
int erroffset;
// To capture N substrings, you need space for 3(N+1) offsets in the
// offset vector. We'll allow 2N-1 substrings and possibly ignore some.
//
const int ovecsize = 6 * MAX_GLOBAL_REGS;
int ovec[ovecsize];
re = pcre_compile(pattern, cis ? PCRE_CASELESS : 0,
&errptr, &erroffset, NULL);
if (!re)
{
// Matching error.
//
safe_str("#-1 REGEXP ERROR ", buff, bufc);
safe_str(errptr, buff, bufc);
return;
}
if (all)
{
study = pcre_study(re, 0, &errptr);
}
bool first = true;
char *s = trim_space_sep(search, psep);
do
{
char *r = split_token(&s, psep);
if ( !MuxAlarm.bAlarmed
&& pcre_exec(re, study, r, strlen(r), 0, 0, ovec, ovecsize) >= 0)
{
if (first)
{
first = false;
}
else
{
print_sep(psep, buff, bufc);
}
safe_str(r, buff, bufc);
if (!all)
{
break;
}
}
} while (s);
MEMFREE(re);
if (study)
{
MEMFREE(study);
}
}
FUNCTION(fun_regrab)
{
SEP sep;
if (!OPTIONAL_DELIM(3, sep, DELIM_DFLT|DELIM_STRING))
{
return;
}
real_regrab(fargs[0], fargs[1], &sep, buff, bufc, false, false);
}
FUNCTION(fun_regrabi)
{
SEP sep;
if (!OPTIONAL_DELIM(3, sep, DELIM_DFLT|DELIM_STRING))
{
return;
}
real_regrab(fargs[0], fargs[1], &sep, buff, bufc, true, false);
}
FUNCTION(fun_regraball)
{
SEP sep;
if (!OPTIONAL_DELIM(3, sep, DELIM_DFLT|DELIM_STRING))
{
return;
}
real_regrab(fargs[0], fargs[1], &sep, buff, bufc, false, true);
}
FUNCTION(fun_regraballi)
{
SEP sep;
if (!OPTIONAL_DELIM(3, sep, DELIM_DFLT|DELIM_STRING))
{
return;
}
real_regrab(fargs[0], fargs[1], &sep, buff, bufc, true, true);
}
/* ---------------------------------------------------------------------------
* 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)
{
int ch = fargs[1][0];
bool type = (ch == 'p' || ch == '1');
safe_str(translate_string(fargs[0], type), buff, bufc);
}
// Construct a CBitField to hold (nMaximum_arg+1) bits numbered 0 through
// nMaximum_arg.
//
CBitField::CBitField(unsigned int nMaximum_arg)
{
nMaximum = 0;
nInts = 0;
pInts = NULL;
pMasks = NULL;
nBitsPer = sizeof(UINT32)*8;
// Calculate Shift
//
nShift = 0;
unsigned int i = 1;
while (i < nBitsPer)
{
nShift++;
i <<= 1;
}
// Calculate Mask
//
nMask = nBitsPer - 1;
// Allocate array of UINT32s.
//
Resize(nMaximum_arg);
}
#define MINIMUM_RESIZE (4096*sizeof(UINT32))
void CBitField::Resize(unsigned int nMaximum_arg)
{
if ( 0 < nMaximum_arg
&& nMaximum < nMaximum_arg)
{
unsigned int nNewMaximum = nMaximum_arg;
// This provides some assurances that we are not resizing too often.
//
if ( pMasks
&& nNewMaximum < nMaximum + MINIMUM_RESIZE)
{
nNewMaximum = nMaximum + MINIMUM_RESIZE;
}
size_t nNewInts = (nNewMaximum+nBitsPer) >> nShift;
UINT32 *pNewMasks = (UINT32 *)MEMALLOC((nNewInts+nBitsPer)
* sizeof(UINT32));
ISOUTOFMEMORY(pNewMasks);
UINT32 *pNewInts = pNewMasks + nBitsPer;
// Is this the first sizing or a re-sizing?
//
if (pMasks)
{
// Copy existing masks and bits to the new location, and
// clear the new bits.
//
memcpy(pNewMasks, pMasks, (nInts+nBitsPer)*sizeof(UINT32));
memset(pNewInts + nInts, 0, (nNewInts - nInts)*sizeof(UINT32));
// Free the previous allocation.
//
MEMFREE(pMasks);
// A reallocation.
//
nMaximum = nNewMaximum;
nInts = nNewInts;
pMasks = pNewMasks;
pInts = pNewInts;
}
else
{
// First allocation.
//
nMaximum = nNewMaximum;
nInts = nNewInts;
pMasks = pNewMasks;
pInts = pNewInts;
// Initialize masks by calculating all possible single bits.
//
for (int i = 0; i < nBitsPer; i++)
{
pMasks[i] = ((UINT32)1) << i;
}
// Initialize bits by clearing them all.
//
ClearAll();
}
}
}
CBitField::~CBitField(void)
{
pInts = NULL;
if (pMasks)
{
MEMFREE(pMasks);
pMasks = NULL;
}
}
void CBitField::ClearAll(void)
{
memset(pInts, 0, nInts*sizeof(UINT32));
}
void CBitField::Set(unsigned int i)
{
if (i <= nMaximum)
{
pInts[i>>nShift] |= pMasks[i&nMask];
}
}
void CBitField::Clear(unsigned int i)
{
if (i <= nMaximum)
{
pInts[i>>nShift] &= ~pMasks[i&nMask];
}
}
bool CBitField::IsSet(unsigned int i)
{
if (i <= nMaximum)
{
if (pInts[i>>nShift] & pMasks[i&nMask])
{
return true;
}
}
return false;
}
// -------------------------------------------------------------------------
// fun_lrooms: Takes a dbref (room), an int (N), and an optional bool (B).
//
// MUX Syntax: lrooms(<room> [,<N>[,<B>]])
//
// Returns a list of rooms <N>-levels deep from <room>. If <B> == 1, it will
// return all room dbrefs between 0 and <N> levels, while <B> == 0 will
// return only the room dbrefs on the Nth level. The default is to show all
// rooms dbrefs between 0 and <N> levels.
//
// Written by Marlek. Idea from RhostMUSH.
//
static void room_list
(
dbref player,
dbref enactor,
dbref room,
int level,
int maxlevels,
bool showall
)
{
// Make sure the player can really see this room from their location.
//
if ( ( level == maxlevels
|| showall)
&& ( Examinable(player, room)
|| Location(player) == room
|| room == enactor))
{
mudstate.bfReport.Set(room);
}
// If the Nth level has been reach, stop this branch in the recursion
//
if (level >= maxlevels)
{
return;
}
// Return info for all parent levels.
//
int lev;
dbref parent;
ITER_PARENTS(room, parent, lev)
{
// Look for exits at each level.
//
if (!Has_exits(parent))
{
continue;
}
int key = 0;
if (Examinable(player, parent))
{
key |= VE_LOC_XAM;
}
if (Dark(parent))
{
key |= VE_LOC_DARK;
}
if (Dark(room))
{
key |= VE_BASE_DARK;
}
dbref thing;
DOLIST(thing, Exits(parent))
{
dbref loc = Location(thing);
if ( exit_visible(thing, player, key)
&& !mudstate.bfTraverse.IsSet(loc))
{
mudstate.bfTraverse.Set(loc);
room_list(player, enactor, loc, (level + 1), maxlevels, showall);
}
}
}
}
FUNCTION(fun_lrooms)
{
dbref room = match_thing_quiet(executor, fargs[0]);
if (!Good_obj(room))
{
safe_match_result(room, buff, bufc);
return;
}
else if (!isRoom(room))
{
safe_str("#-1 FIRST ARGUMENT MUST BE A ROOM", buff, bufc);
return;
}
int N = 1;
if (nfargs >= 2)
{
N = mux_atol(fargs[1]);
if (N < 0)
{
safe_str("#-1 SECOND ARGUMENT MUST BE A POSITIVE NUMBER",
buff, bufc);
return;
}
else if (N > 50)
{
// Maybe this can be turned into a config parameter to prevent
// misuse by putting in really large values.
//
safe_str("#-1 SECOND ARGUMENT IS TOO LARGE", buff, bufc);
return;
}
}
bool B = true;
if (nfargs == 3)
{
B = xlate(fargs[2]);
}
mudstate.bfReport.Resize(mudstate.db_top-1);
mudstate.bfTraverse.Resize(mudstate.db_top-1);
mudstate.bfReport.ClearAll();
mudstate.bfTraverse.ClearAll();
mudstate.bfTraverse.Set(room);
room_list(executor, enactor, room, 0, N, B);
mudstate.bfReport.Clear(room);
ITL pContext;
ItemToList_Init(&pContext, buff, bufc, '#');
dbref i;
DO_WHOLE_DB(i)
{
if ( mudstate.bfReport.IsSet(i)
&& !ItemToList_AddInteger(&pContext, i))
{
break;
}
}
ItemToList_Final(&pContext);
}