/* command.c - command parser and support routines */
/* $Id: command.c,v 1.205 2004/02/23 04:35:14 rmg Exp $ */
#include "copyright.h"
#include "autoconf.h"
#include "config.h"
#include "alloc.h" /* required by mudconf */
#include "flags.h" /* required by mudconf */
#include "htab.h" /* required by mudconf */
#include "mudconf.h" /* required by code */
#include "db.h" /* required by externs */
#include "externs.h" /* required by interface */
#include "interface.h" /* required by code */
#include "help.h" /* required by code */
#include "command.h" /* required by code */
#include "functions.h" /* required by code */
#include "match.h" /* required by code */
#include "attrs.h" /* required by code */
#include "powers.h" /* required by code */
#include "vattr.h" /* required by code */
#include "db_sql.h" /* required by code */
#include "pcre.h" /* required by code */
extern void FDECL(list_cf_access, (dbref));
extern void FDECL(list_cf_read_access, (dbref));
extern void FDECL(list_siteinfo, (dbref));
extern void FDECL(logged_out, (dbref, dbref, int, char *));
extern void FDECL(list_functable, (dbref));
extern void FDECL(list_funcaccess, (dbref));
extern void FDECL(list_bufstats, (dbref));
extern void FDECL(list_buftrace, (dbref));
extern void FDECL(list_cached_objs, (dbref));
extern void FDECL(list_cached_attrs, (dbref));
extern void FDECL(list_rawmemory, (dbref));
extern int FDECL(atr_match, (dbref, dbref, char, char *, char *, int));
extern int FDECL(list_check, (dbref, dbref, char, char *, char *, int, int *));
extern void FDECL(do_enter_internal, (dbref, dbref, int));
extern int FDECL(regexp_match, (char *, char *, int, char **, int));
extern void FDECL(register_prefix_cmds, (const char *));
#define CACHING "attribute"
#define NOGO_MESSAGE "You can't go that way."
/* Take care of all the assorted problems associated with getrusage(). */
#ifdef hpux
#define HAVE_GETRUSAGE 1
#include <sys/syscall.h>
#define getrusage(x,p) syscall(SYS_GETRUSAGE,x,p)
#endif
#ifdef _SEQUENT_
#define HAVE_GET_PROCESS_STATS 1
#include <sys/procstats.h>
#endif
/* This must be the LAST thing we include. */
#include "cmdtabs.h" /* required by code */
/* ---------------------------------------------------------------------------
* Hook macros.
*
* We never want to call hooks in the case of @addcommand'd commands
* (both for efficiency reasons and the fact that we might NOT match an
* @addcommand even if we've been told there is one), but we leave this
* to the hook-adder to prevent.
*/
#define CALL_PRE_HOOK(x,a,na) \
if (((x)->pre_hook != NULL) && !((x)->callseq & CS_ADDED)) { \
process_hook((x)->pre_hook, (x)->callseq & CS_PRESERVE, \
player, cause, (a), (na)); \
}
#define CALL_POST_HOOK(x,a,na) \
if (((x)->post_hook != NULL) && !((x)->callseq & CS_ADDED)) { \
process_hook((x)->post_hook, (x)->callseq & CS_PRESERVE, \
player, cause, (a), (na)); \
}
CMDENT *prefix_cmds[256];
CMDENT *goto_cmdp, *enter_cmdp, *leave_cmdp;
/* ---------------------------------------------------------------------------
* Main body of code.
*/
void NDECL(init_cmdtab)
{
CMDENT *cp;
ATTR *ap;
char *p, *q;
char *cbuff;
int i;
hashinit(&mudstate.command_htab, 250 * HASH_FACTOR, HT_STR);
/* Load attribute-setting commands */
cbuff = alloc_sbuf("init_cmdtab");
for (ap = attr; ap->name; ap++) {
if ((ap->flags & AF_NOCMD) == 0) {
p = cbuff;
*p++ = '@';
for (q = (char *)ap->name; *q; p++, q++)
*p = tolower(*q);
*p = '\0';
cp = (CMDENT *) XMALLOC(sizeof(CMDENT), "init_cmdtab");
cp->cmdname = XSTRDUP(cbuff, "init_cmdtab.cmdname");
cp->perms = CA_NO_GUEST | CA_NO_SLAVE;
cp->switches = NULL;
if (ap->flags & (AF_WIZARD | AF_MDARK)) {
cp->perms |= CA_WIZARD;
}
cp->extra = ap->number;
cp->callseq = CS_TWO_ARG;
cp->pre_hook = NULL;
cp->post_hook = NULL;
cp->userperms = NULL;
cp->info.handler = do_setattr;
if (hashadd(cp->cmdname, (int *)cp, &mudstate.command_htab, 0)) {
XFREE(cp->cmdname, "init_cmdtab.2");
XFREE(cp, "init_cmdtab.3");
} else {
/* also add the __ alias form */
hashadd(tprintf("__%s", cp->cmdname), (int *)cp, &mudstate.command_htab, HASH_ALIAS);
}
}
}
free_sbuf(cbuff);
/* Load the builtin commands, plus __ aliases */
for (cp = command_table; cp->cmdname; cp++) {
hashadd(cp->cmdname, (int *)cp, &mudstate.command_htab, 0);
hashadd(tprintf("__%s", cp->cmdname), (int *)cp, &mudstate.command_htab, HASH_ALIAS);
}
/* Set the builtin prefix commands */
for (i = 0; i < 256; i++)
prefix_cmds[i] = NULL;
register_prefix_cmds("\":;\\#&"); /* ":;\#& */
goto_cmdp = (CMDENT *) hashfind("goto", &mudstate.command_htab);
enter_cmdp = (CMDENT *) hashfind("enter", &mudstate.command_htab);
leave_cmdp = (CMDENT *) hashfind("leave", &mudstate.command_htab);
}
void reset_prefix_cmds()
{
int i;
char cn[2] = "x";
for (i = 0; i < 256; i++) {
if (prefix_cmds[i]) {
cn[0] = i;
prefix_cmds[i] = (CMDENT *) hashfind(cn, &mudstate.command_htab);
}
}
}
/* ---------------------------------------------------------------------------
* check_access: Check if player has access to function.
* Note that the calling function may also give permission
* denied messages on failure.
*/
int check_access(player, mask)
dbref player;
int mask;
{
int mval, nval;
/* Check if we have permission to execute */
if (mask & (CA_DISABLED | CA_STATIC))
return 0;
if (God(player) || mudstate.initializing)
return 1;
/* Check for bits that we have to have. Since we know that
* we're not God at this point, if it is God-only, it fails.
* (God in combination with other stuff is implicitly checked,
* since we return false if we don't find the other bits.)
*/
if ((mval = mask & (CA_ISPRIV_MASK | CA_MARKER_MASK)) == CA_GOD)
return 0;
if (mval) {
mval = mask & CA_ISPRIV_MASK;
nval = mask & CA_MARKER_MASK;
if (mval && !nval) {
if (!(((mask & CA_WIZARD) && Wizard(player)) ||
((mask & CA_ADMIN) && WizRoy(player)) ||
((mask & CA_BUILDER) && Builder(player)) ||
((mask & CA_STAFF) && Staff(player)) ||
((mask & CA_HEAD) && Head(player)) ||
((mask & CA_IMMORTAL) && Immortal(player)) ||
((mask & CA_SQL_OK) && Can_Use_SQL(player))))
return 0;
} else if (!mval && nval) {
if (!(((mask & CA_MARKER0) && H_Marker0(player)) ||
((mask & CA_MARKER1) && H_Marker1(player)) ||
((mask & CA_MARKER2) && H_Marker2(player)) ||
((mask & CA_MARKER3) && H_Marker3(player)) ||
((mask & CA_MARKER4) && H_Marker4(player)) ||
((mask & CA_MARKER5) && H_Marker5(player)) ||
((mask & CA_MARKER6) && H_Marker6(player)) ||
((mask & CA_MARKER7) && H_Marker7(player)) ||
((mask & CA_MARKER8) && H_Marker8(player)) ||
((mask & CA_MARKER9) && H_Marker9(player))))
return 0;
} else {
if (!(((mask & CA_WIZARD) && Wizard(player)) ||
((mask & CA_ADMIN) && WizRoy(player)) ||
((mask & CA_BUILDER) && Builder(player)) ||
((mask & CA_STAFF) && Staff(player)) ||
((mask & CA_HEAD) && Head(player)) ||
((mask & CA_IMMORTAL) && Immortal(player)) ||
((mask & CA_SQL_OK) && Can_Use_SQL(player)) ||
((mask & CA_MARKER0) && H_Marker0(player)) ||
((mask & CA_MARKER1) && H_Marker1(player)) ||
((mask & CA_MARKER2) && H_Marker2(player)) ||
((mask & CA_MARKER3) && H_Marker3(player)) ||
((mask & CA_MARKER4) && H_Marker4(player)) ||
((mask & CA_MARKER5) && H_Marker5(player)) ||
((mask & CA_MARKER6) && H_Marker6(player)) ||
((mask & CA_MARKER7) && H_Marker7(player)) ||
((mask & CA_MARKER8) && H_Marker8(player)) ||
((mask & CA_MARKER9) && H_Marker9(player))))
return 0;
}
}
/* Check the things that we can't be. */
if (((mask & CA_ISNOT_MASK) && !Wizard(player)) &&
(((mask & CA_NO_HAVEN) && Player_haven(player)) ||
((mask & CA_NO_ROBOT) && Robot(player)) ||
((mask & CA_NO_SLAVE) && Slave(player)) ||
((mask & CA_NO_SUSPECT) && Suspect(player)) ||
((mask & CA_NO_GUEST) && Guest(player)))) {
return 0;
}
return 1;
}
/* ---------------------------------------------------------------------------
* check_mod_access: Go through sequence of module call-outs, treating
* all of them like permission checks.
*/
int check_mod_access(player, xperms)
dbref player;
EXTFUNCS *xperms;
{
int i;
for (i = 0; i < xperms->num_funcs; i++) {
if (!xperms->ext_funcs[i])
continue;
if (!((xperms->ext_funcs[i]->handler)(player)))
return 0;
}
return 1;
}
/* ---------------------------------------------------------------------------
* check_userdef_access: Check if user has access to command with user-def'd
* permissions.
*/
int check_userdef_access(player, hookp, cargs, ncargs)
dbref player;
HOOKENT *hookp;
char *cargs[];
int ncargs;
{
char *buf;
char *bp, *tstr, *str;
dbref aowner;
int result, aflags, alen;
GDATA *preserve;
/* We have user-defined command permissions. Go evaluate the
* obj/attr pair that we've been given. If that result is
* nonexistent, we consider it a failure. We use boolean
* truth here.
*
* Note that unlike before and after hooks, we always preserve
* the registers. (When you get right down to it, this thing isn't
* really a hook. It's just convenient to re-use the same code
* that we use with hooks.)
*/
tstr = atr_get(hookp->thing, hookp->atr, &aowner, &aflags, &alen);
if (!tstr)
return 0;
if (!*tstr) {
free_lbuf(tstr);
return 0;
}
str = tstr;
preserve = save_global_regs("check_userdef_access");
bp = buf = alloc_lbuf("check_userdef_access");
exec(buf, &bp, hookp->thing, player, player,
EV_EVAL | EV_FCHECK | EV_TOP,
&str, cargs, ncargs);
restore_global_regs("check_userdef_access", preserve);
result = xlate(buf);
free_lbuf(buf);
free_lbuf(tstr);
return result;
}
/* ---------------------------------------------------------------------------
* process_hook: Evaluate a hook function.
*/
static void process_hook(hp, save_globs, player, cause, cargs, ncargs)
HOOKENT *hp;
int save_globs;
dbref player, cause;
char *cargs[];
int ncargs;
{
char *buf, *bp;
char *tstr, *str;
dbref aowner;
int aflags, alen;
GDATA *preserve;
/* We know we have a non-null hook. We want to evaluate the obj/attr
* pair of that hook. We consider the enactor to be the player who
* executed the command that caused this hook to be called.
*/
tstr = atr_get(hp->thing, hp->atr, &aowner, &aflags, &alen);
str = tstr;
if (save_globs) {
preserve = save_global_regs("process_hook");
}
buf = bp = alloc_lbuf("process_hook");
exec(buf, &bp, hp->thing, player, player, EV_EVAL | EV_FCHECK | EV_TOP,
&str, cargs, ncargs);
free_lbuf(buf);
if (save_globs) {
restore_global_regs("process_hook", preserve);
}
free_lbuf(tstr);
}
/* ---------------------------------------------------------------------------
* process_cmdent: Perform indicated command with passed args.
*/
void process_cmdent(cmdp, switchp, player, cause, interactive, arg,
unp_command, cargs, ncargs)
CMDENT *cmdp;
char *switchp, *arg, *unp_command, *cargs[];
dbref player, cause;
int interactive, ncargs;
{
char *buf1, *buf2, tchar, *bp, *str, *buff, *s, *j, *new;
char *args[MAX_ARG], *aargs[NUM_ENV_VARS];
int nargs, i, interp, key, xkey, aflags, alen;
int hasswitch = 0;
int cmd_matches = 0;
dbref aowner;
ADDENT *add;
GDATA *preserve;
/* Perform object type checks. */
if (Invalid_Objtype(player)) {
notify(player, "Command incompatible with invoker type.");
return;
}
/* Check if we have permission to execute the command */
if (!Check_Cmd_Access(player, cmdp, cargs, ncargs)) {
notify(player, NOPERM_MESSAGE);
return;
}
/* Check global flags */
if ((!Builder(player)) && Protect(CA_GBL_BUILD) &&
!(mudconf.control_flags & CF_BUILD)) {
notify(player, "Sorry, building is not allowed now.");
return;
}
if (Protect(CA_GBL_INTERP) && !(mudconf.control_flags & CF_INTERP)) {
notify(player,
"Sorry, queueing and triggering are not allowed now.");
return;
}
key = cmdp->extra & ~SW_MULTIPLE;
if (key & SW_GOT_UNIQUE) {
i = 1;
key = key & ~SW_GOT_UNIQUE;
} else {
i = 0;
}
/* Check command switches. Note that there may be more than one,
* and that we OR all of them together along with the extra value
* from the command table to produce the key value in the handler call.
*/
if (switchp && cmdp->switches) {
do {
buf1 = strchr(switchp, '/');
if (buf1)
*buf1++ = '\0';
xkey = search_nametab(player, cmdp->switches,
switchp);
if (xkey == -1) {
notify(player,
tprintf("Unrecognized switch '%s' for command '%s'.",
switchp, cmdp->cmdname));
return;
} else if (xkey == -2) {
notify(player, NOPERM_MESSAGE);
return;
} else if (!(xkey & SW_MULTIPLE)) {
if (i == 1) {
notify(player,
"Illegal combination of switches.");
return;
}
i = 1;
} else {
xkey &= ~SW_MULTIPLE;
}
key |= xkey;
switchp = buf1;
hasswitch = 1;
} while (buf1);
} else if (switchp && !(cmdp->callseq & CS_ADDED)) {
notify(player,
tprintf("Command %s does not take switches.",
cmdp->cmdname));
return;
}
/* At this point we're guaranteed we're going to execute something.
* Let's check to see if we have a pre-command hook.
*/
CALL_PRE_HOOK(cmdp, cargs, ncargs);
/* If the command normally has interpreted args, but the user
* specified, /noeval, just do EV_STRIP.
*
* If the command is interpreted, or we're interactive (and
* the command isn't specified CS_NOINTERP), eval the args.
*
* The others are obvious.
*/
if ((cmdp->callseq & CS_INTERP) && (key & SW_NOEVAL)) {
interp = EV_STRIP;
key &= ~SW_NOEVAL; /* Remove SW_NOEVAL from 'key' */
}
else if ((cmdp->callseq & CS_INTERP) ||
!(interactive || (cmdp->callseq & CS_NOINTERP)))
interp = EV_EVAL | EV_STRIP;
else if (cmdp->callseq & CS_STRIP)
interp = EV_STRIP;
else if (cmdp->callseq & CS_STRIP_AROUND)
interp = EV_STRIP_AROUND;
else
interp = 0;
switch (cmdp->callseq & CS_NARG_MASK) {
case CS_NO_ARGS: /* <cmd> (no args) */
(*(cmdp->info.handler)) (player, cause, key);
break;
case CS_ONE_ARG: /* <cmd> <arg> */
/* If an unparsed command, just give it to the handler */
if (cmdp->callseq & CS_UNPARSE) {
(*(cmdp->info.handler)) (player, unp_command);
break;
}
/* Interpret if necessary, but not twice for CS_ADDED */
if ((interp & EV_EVAL) && !(cmdp->callseq & CS_ADDED)) {
buf1 = bp = alloc_lbuf("process_cmdent");
str = arg;
exec(buf1, &bp, player, cause, cause,
interp | EV_FCHECK | EV_TOP,
&str, cargs, ncargs);
} else
buf1 = parse_to(&arg, '\0', interp | EV_TOP);
/* Call the correct handler */
if (cmdp->callseq & CS_CMDARG) {
(*(cmdp->info.handler)) (player, cause, key, buf1,
cargs, ncargs);
} else {
if (cmdp->callseq & CS_ADDED) {
preserve = save_global_regs("process_cmdent_added");
/* Construct the matching buffer. */
/* In the case of a single-letter prefix, we want
* to just skip past that first letter. Otherwise
* we want to go past the first word.
*/
if (!(cmdp->callseq & CS_LEADIN)) {
for (j = unp_command; *j && (*j != ' '); j++) ;
} else {
j = unp_command; j++;
}
new = alloc_lbuf("process_cmdent.soft");
bp = new;
if (!*j) {
/* No args */
if (!(cmdp->callseq & CS_LEADIN)) {
safe_str(cmdp->cmdname, new, &bp);
} else {
safe_str(unp_command, new, &bp);
}
if (switchp) {
safe_chr('/', new, &bp);
safe_str(switchp, new, &bp);
}
*bp = '\0';
} else {
if (!(cmdp->callseq & CS_LEADIN))
j++;
safe_str(cmdp->cmdname, new, &bp);
if (switchp) {
safe_chr('/', new, &bp);
safe_str(switchp, new, &bp);
}
if (!(cmdp->callseq & CS_LEADIN))
safe_chr(' ', new, &bp);
safe_str(j, new, &bp);
*bp = '\0';
}
/* Now search against the attributes, unless we
* can't pass the uselock.
*/
for (add = (ADDENT *)cmdp->info.added;
add != NULL; add = add->next) {
buff = atr_get(add->thing, add->atr,
&aowner, &aflags, &alen);
/* Skip the '$' character, and the next */
for (s = buff + 2;
*s && ((*s != ':') || (*(s - 1) == '\\'));
s++)
;
if (!*s) {
free_lbuf(buff);
break;
}
*s++ = '\0';
if (((!(aflags & AF_REGEXP) &&
wild(buff + 1, new, aargs,
NUM_ENV_VARS)) ||
((aflags & AF_REGEXP) &&
regexp_match(buff + 1, new,
((aflags & AF_CASE) ?
0 : PCRE_CASELESS),
aargs, NUM_ENV_VARS))) &&
(!mudconf.addcmd_obey_uselocks ||
could_doit(player, add->thing, A_LUSE))) {
process_cmdline(((!(cmdp->callseq & CS_ACTOR)
|| God(player)) ?
add->thing :
player),
player, s, aargs,
NUM_ENV_VARS, NULL);
for (i = 0; i < NUM_ENV_VARS; i++) {
if (aargs[i])
free_lbuf(aargs[i]);
}
cmd_matches++;
}
free_lbuf(buff);
if (cmd_matches && mudconf.addcmd_obey_stop &&
Stop_Match(add->thing)) {
break;
}
}
if (!cmd_matches && !mudconf.addcmd_match_blindly) {
/* The command the player typed didn't match
* any of the wildcard patterns we have for
* that addcommand. We should raise an error.
* We DO NOT go back into trying to match
* other stuff -- this is a 'Huh?' situation.
*/
notify(player, mudconf.huh_msg);
STARTLOG(LOG_BADCOMMANDS, "CMD", "BAD")
log_name_and_loc(player);
log_printf(" entered: %s", new);
ENDLOG
}
free_lbuf(new);
restore_global_regs("process_cmdent", preserve);
} else
(*(cmdp->info.handler)) (player, cause, key, buf1);
}
/* Free the buffer if one was allocated */
if ((interp & EV_EVAL) && !(cmdp->callseq & CS_ADDED))
free_lbuf(buf1);
break;
case CS_TWO_ARG: /* <cmd> <arg1> = <arg2> */
/* Interpret ARG1 */
buf2 = parse_to(&arg, '=', EV_STRIP_TS);
/* Handle when no '=' was specified */
if (!arg || (arg && !*arg)) {
arg = &tchar;
*arg = '\0';
}
buf1 = bp = alloc_lbuf("process_cmdent.2");
str = buf2;
exec(buf1, &bp, player, cause, cause,
EV_STRIP | EV_FCHECK | EV_EVAL | EV_TOP,
&str, cargs, ncargs);
if (cmdp->callseq & CS_ARGV) {
/* Arg2 is ARGV style. Go get the args */
parse_arglist(player, cause, cause, arg, '\0',
interp | EV_STRIP_LS | EV_STRIP_TS,
args, MAX_ARG, cargs, ncargs);
for (nargs = 0; (nargs < MAX_ARG) && args[nargs]; nargs++) ;
/* Call the correct command handler */
if (cmdp->callseq & CS_CMDARG) {
(*(cmdp->info.handler)) (player, cause, key,
buf1, args, nargs, cargs, ncargs);
} else {
(*(cmdp->info.handler)) (player, cause, key,
buf1, args, nargs);
}
/* Free the argument buffers */
for (i = 0; i <= nargs; i++)
if (args[i])
free_lbuf(args[i]);
} else {
/* Arg2 is normal style. Interpret if needed */
if (interp & EV_EVAL) {
buf2 = bp = alloc_lbuf("process_cmdent.3");
str = arg;
exec(buf2, &bp, player, cause, cause,
interp | EV_FCHECK | EV_TOP,
&str, cargs, ncargs);
} else if (cmdp->callseq & CS_UNPARSE) {
buf2 = parse_to(&arg, '\0',
interp | EV_TOP | EV_NO_COMPRESS);
} else {
buf2 = parse_to(&arg, '\0',
interp | EV_STRIP_LS | EV_STRIP_TS | EV_TOP);
}
/* Call the correct command handler */
if (cmdp->callseq & CS_CMDARG) {
(*(cmdp->info.handler)) (player, cause, key,
buf1, buf2, cargs, ncargs);
} else {
(*(cmdp->info.handler)) (player, cause, key,
buf1, buf2);
}
/* Free the buffer, if needed */
if (interp & EV_EVAL)
free_lbuf(buf2);
}
/* Free the buffer obtained by evaluating Arg1 */
free_lbuf(buf1);
break;
}
/* And now we go do the posthook, if we have one. */
CALL_POST_HOOK(cmdp, cargs, ncargs);
return;
}
/* ---------------------------------------------------------------------------
* process_command: Execute a command.
*/
char *process_command(player, cause, interactive, command, args, nargs)
dbref player, cause;
int interactive, nargs;
char *command, *args[];
{
static char preserve_cmd[LBUF_SIZE];
char *p, *q, *arg, *lcbuf, *slashp, *cmdsave, *bp, *str, *evcmd;
char *gbuf, *gc;
int succ, aflags, alen, i, got_stop, pcount, retval = 0;
dbref exit, aowner, parent;
CMDENT *cmdp;
NUMBERTAB *np;
if (mudstate.cmd_invk_ctr == mudconf.cmd_invk_lim)
return command;
mudstate.cmd_invk_ctr++;
/* Robustify player */
cmdsave = mudstate.debug_cmd;
mudstate.debug_cmd = (char *)"< process_command >";
if (!command) {
fprintf(mainlog_fp, "ABORT! command.c, null command in process_command().\n");
abort();
}
if (!Good_obj(player)) {
STARTLOG(LOG_BUGS, "CMD", "PLYR")
log_printf("Bad player in process_command: %d",
player);
ENDLOG
mudstate.debug_cmd = cmdsave;
return command;
}
/* Make sure player isn't going or halted */
if (Going(player) ||
(Halted(player) &&
!((Typeof(player) == TYPE_PLAYER) && interactive))) {
notify(Owner(player),
tprintf("Attempt to execute command by halted object #%d",
player));
mudstate.debug_cmd = cmdsave;
return command;
}
if (Suspect(player)) {
STARTLOG(LOG_SUSPECTCMDS, "CMD", "SUSP")
log_name_and_loc(player);
log_printf(" entered: %s", command);
ENDLOG
} else {
STARTLOG(LOG_ALLCOMMANDS, "CMD", "ALL")
log_name_and_loc(player);
log_printf(" entered: %s", command);
ENDLOG
}
s_Accessed(player);
/* Reset recursion limits. Baseline the CPU counter. */
mudstate.func_nest_lev = 0;
mudstate.func_invk_ctr = 0;
mudstate.ntfy_nest_lev = 0;
mudstate.lock_nest_lev = 0;
if (mudconf.func_cpu_lim > 0)
mudstate.cputime_base = clock();
if (Verbose(player)) {
if (H_Redirect(player)) {
np = (NUMBERTAB *) nhashfind(player, &mudstate.redir_htab);
if (np) {
notify(np->num,
tprintf("%s] %s", Name(player), command));
} else {
/* We have no pointer, we should have no flag. */
s_Flags3(player, Flags3(player) & ~HAS_REDIRECT);
}
} else {
notify(Owner(player), tprintf("%s] %s", Name(player),
command));
}
}
/*
* NOTE THAT THIS WILL BREAK IF "GOD" IS NOT A DBREF.
*/
if (mudconf.control_flags & CF_GODMONITOR) {
raw_notify(GOD, tprintf("%s(#%d)%c %s", Name(player), player,
(interactive) ? '|' : ':', command));
}
/* Eat leading whitespace, and space-compress if configured */
while (*command && isspace(*command))
command++;
strcpy(preserve_cmd, command);
mudstate.debug_cmd = command;
mudstate.curr_cmd = preserve_cmd;
if (mudconf.space_compress) {
p = q = command;
while (*p) {
while (*p && !isspace(*p))
*q++ = *p++;
while (*p && isspace(*p))
p++;
if (*p)
*q++ = ' ';
}
*q = '\0';
}
/* Allow modules to intercept command strings. */
CALL_SOME_MODULES(retval, process_command,
(player, cause, interactive, command, args, nargs));
if (retval > 0) {
mudstate.debug_cmd = cmdsave;
return preserve_cmd;
}
/* Now comes the fun stuff. First check for single-letter leadins.
* We check these before checking HOME because
* they are among the most frequently executed commands,
* and they can never be the HOME command.
*/
i = command[0] & 0xff;
if ((prefix_cmds[i] != NULL) && command[0]) {
process_cmdent(prefix_cmds[i], NULL, player, cause,
interactive, command, command, args, nargs);
mudstate.debug_cmd = cmdsave;
return preserve_cmd;
}
/* Check for the HOME command. You cannot do hooks on this because
* home is not part of the traditional command table.
*/
if (Has_location(player) && string_compare(command, "home") == 0) {
if (((Fixed(player)) || (Fixed(Owner(player)))) &&
!(WizRoy(player))) {
notify(player, mudconf.fixed_home_msg);
mudstate.debug_cmd = cmdsave;
return preserve_cmd;
}
do_move(player, cause, 0, "home");
mudstate.debug_cmd = cmdsave;
return preserve_cmd;
}
/* Only check for exits if we may use the goto command */
if (Check_Cmd_Access(player, goto_cmdp, args, nargs)) {
/* Check for an exit name */
init_match_check_keys(player, command, TYPE_EXIT);
match_exit_with_parents();
exit = last_match_result();
if (exit != NOTHING) {
if (mudconf.exit_calls_move) {
/* Exits literally call the 'move' command. Note
* that, later, when we go to matching master-room
* and other global-ish exits, that we also need
* to have move_match_more set to 'yes', or
* we'll match here only to encounter dead silence
* when we try to find the exit inside the move
* routine. We also need to directly find what
* the pointer for the move (goto) command is,
* since we could have @addcommand'd it (and
* probably did, if this conf option is on).
* Finally, we've got to make this look like
* we really did type 'goto <exit>', or the
* @addcommand will just skip over the string.
*/
cmdp = (CMDENT *) hashfind("goto",
&mudstate.command_htab);
if (cmdp) { /* just in case */
gbuf = alloc_lbuf("process_command.goto");
gc = gbuf;
safe_str(cmdp->cmdname, gbuf, &gc);
safe_chr(' ', gbuf, &gc);
safe_str(command, gbuf, &gc);
*gc = '\0';
process_cmdent(cmdp, NULL, player, cause,
interactive, command, gbuf,
args, nargs);
free_lbuf(gbuf);
}
} else {
/* Execute the pre-hook for the goto command */
CALL_PRE_HOOK(goto_cmdp, args, nargs);
move_exit(player, exit, 0, NOGO_MESSAGE, 0);
/* Execute the post-hook for the goto command */
CALL_POST_HOOK(goto_cmdp, args, nargs);
}
mudstate.debug_cmd = cmdsave;
return preserve_cmd;
}
/* Check for an exit in the master room */
init_match_check_keys(player, command, TYPE_EXIT);
match_master_exit();
exit = last_match_result();
if (exit != NOTHING) {
if (mudconf.exit_calls_move) {
cmdp = (CMDENT *) hashfind("goto",
&mudstate.command_htab);
if (cmdp) {
gbuf = alloc_lbuf("process_command.goto");
gc = gbuf;
safe_str(cmdp->cmdname, gbuf, &gc);
safe_chr(' ', gbuf, &gc);
safe_str(command, gbuf, &gc);
*gc = '\0';
process_cmdent(cmdp, NULL, player, cause,
interactive, command, gbuf,
args, nargs);
free_lbuf(gbuf);
}
} else {
CALL_PRE_HOOK(goto_cmdp, args, nargs);
move_exit(player, exit, 1, NOGO_MESSAGE, 0);
CALL_POST_HOOK(goto_cmdp, args, nargs);
}
mudstate.debug_cmd = cmdsave;
return preserve_cmd;
}
}
/* Set up a lowercase command and an arg pointer for the hashed
* command check. Since some types of argument
* processing destroy the arguments, make a copy so that
* we keep the original command line intact. Store the
* edible copy in lcbuf after the lowercased command.
*/
/* Removed copy of the rest of the command, since it's ok to allow
* it to be trashed. -dcm
*/
lcbuf = alloc_lbuf("process_commands.lcbuf");
for (p = command, q = lcbuf; *p && !isspace(*p); p++, q++)
*q = tolower(*p); /* Make lowercase command */
*q++ = '\0'; /* Terminate command */
while (*p && isspace(*p))
p++; /* Skip spaces before arg */
arg = p; /* Remember where arg starts */
/* Strip off any command switches and save them */
slashp = strchr(lcbuf, '/');
if (slashp)
*slashp++ = '\0';
/* Check for a builtin command (or an alias of a builtin command) */
cmdp = (CMDENT *) hashfind(lcbuf, &mudstate.command_htab);
if (cmdp != NULL) {
if (mudconf.space_compress && (cmdp->callseq & CS_NOSQUISH)) {
/* We handle this specially -- there is no space compression
* involved, so we must go back to the preserved command.
*/
strcpy(command, preserve_cmd);
arg = command;
while (*arg && !isspace(*arg))
arg++;
if (*arg) /* we stopped on the space, advance to next */
arg++;
}
process_cmdent(cmdp, slashp, player, cause, interactive, arg,
command, args, nargs);
free_lbuf(lcbuf);
mudstate.debug_cmd = cmdsave;
return preserve_cmd;
}
/* Check for enter and leave aliases, user-defined commands on the
* player, other objects where the player is, on objects in
* the player's inventory, and on the room that holds
* the player. We evaluate the command line here to allow
* chains of $-commands to work.
*/
str = evcmd = alloc_lbuf("process_command.evcmd");
StringCopy(evcmd, command);
bp = lcbuf;
exec(lcbuf, &bp, player, cause, cause,
EV_EVAL | EV_FCHECK | EV_STRIP | EV_TOP, &str, args, nargs);
free_lbuf(evcmd);
succ = 0;
/* Idea for enter/leave aliases from R'nice@TinyTIM */
if (Has_location(player) && Good_obj(Location(player))) {
/* Check for a leave alias, if we have permissions to
* use the 'leave' command.
*/
if (Check_Cmd_Access(player, leave_cmdp, args, nargs)) {
p = atr_pget(Location(player), A_LALIAS, &aowner, &aflags, &alen);
if (*p) {
if (matches_exit_from_list(lcbuf, p)) {
free_lbuf(lcbuf);
free_lbuf(p);
CALL_PRE_HOOK(leave_cmdp, args, nargs);
do_leave(player, player, 0);
CALL_POST_HOOK(leave_cmdp, args, nargs);
return preserve_cmd;
}
}
free_lbuf(p);
}
/* Check for enter aliases, if we have permissions to use the
* 'enter' command.
*/
if (Check_Cmd_Access(player, enter_cmdp, args, nargs)) {
DOLIST(exit, Contents(Location(player))) {
p = atr_pget(exit, A_EALIAS, &aowner, &aflags, &alen);
if (*p) {
if (matches_exit_from_list(lcbuf, p)) {
free_lbuf(lcbuf);
free_lbuf(p);
CALL_PRE_HOOK(enter_cmdp, args, nargs);
do_enter_internal(player, exit, 0);
CALL_POST_HOOK(enter_cmdp, args, nargs);
return preserve_cmd;
}
}
free_lbuf(p);
}
}
}
/* At each of the following stages, we check to make sure that we
* haven't hit a match on a STOP-set object.
*/
got_stop = 0;
/* Check for $-command matches on me */
if (mudconf.match_mine) {
if (((Typeof(player) != TYPE_PLAYER) ||
mudconf.match_mine_pl) &&
(atr_match(player, player, AMATCH_CMD, lcbuf, preserve_cmd, 1) > 0)) {
succ++;
got_stop = Stop_Match(player);
}
}
/* Check for $-command matches on nearby things and on my room */
if (!got_stop && Has_location(player)) {
succ += list_check(Contents(Location(player)), player,
AMATCH_CMD, lcbuf, preserve_cmd, 1, &got_stop);
if (!got_stop &&
atr_match(Location(player), player, AMATCH_CMD, lcbuf,
preserve_cmd, 1) > 0) {
succ++;
got_stop = Stop_Match(Location(player));
}
}
/* Check for $-command matches in my inventory */
if (!got_stop && Has_contents(player))
succ += list_check(Contents(player), player,
AMATCH_CMD, lcbuf, preserve_cmd, 1, &got_stop);
/* If we didn't find anything, and we're checking local masters,
* do those checks. Do it for the zone of the player's location first,
* and then, if nothing is found, on the player's personal zone.
* Walking back through the parent tree stops when a match is found.
* Also note that these matches are done in the style of the master room:
* parents of the contents of the rooms aren't checked for commands.
* We try to maintain 2.2/MUX compatibility here, putting both sets
* of checks together.
*/
if (Has_location(player) && Good_obj(Location(player))) {
/* 2.2 style location */
if (!succ && mudconf.local_masters) {
pcount = 0;
parent = Parent(Location(player));
while (!succ && !got_stop &&
Good_obj(parent) && ParentZone(parent) &&
(pcount < mudconf.parent_nest_lim)) {
if (Has_contents(parent)) {
succ += list_check(Contents(parent), player,
AMATCH_CMD, lcbuf, preserve_cmd,
0, &got_stop);
}
parent = Parent(parent);
pcount++;
}
}
/* MUX style location */
if ((!succ) && mudconf.have_zones &&
(Zone(Location(player)) != NOTHING)) {
if (Typeof(Zone(Location(player))) == TYPE_ROOM) {
/* zone of player's location is a parent room */
if (Location(player) != Zone(player)) {
/* check parent room exits */
init_match_check_keys(player, command, TYPE_EXIT);
match_zone_exit();
exit = last_match_result();
if (exit != NOTHING) {
if (mudconf.exit_calls_move) {
cmdp = (CMDENT *) hashfind("goto",
&mudstate.command_htab);
if (cmdp) {
gbuf = alloc_lbuf("process_command.goto");
gc = gbuf;
safe_str(cmdp->cmdname, gbuf, &gc);
safe_chr(' ', gbuf, &gc);
safe_str(command, gbuf, &gc);
*gc = '\0';
process_cmdent(cmdp, NULL, player, cause,
interactive, command, gbuf,
args, nargs);
free_lbuf(gbuf);
}
} else {
CALL_PRE_HOOK(goto_cmdp, args, nargs);
move_exit(player, exit, 1, NOGO_MESSAGE, 0);
CALL_POST_HOOK(goto_cmdp, args, nargs);
}
mudstate.debug_cmd = cmdsave;
return preserve_cmd;
}
if (!got_stop) {
succ += list_check(Contents(Zone(Location(player))),
player, AMATCH_CMD, lcbuf,
preserve_cmd, 1, &got_stop);
}
} /* end of parent room checks */
} else
/* try matching commands on area zone object */
if (!got_stop && !succ && mudconf.have_zones
&& (Zone(Location(player)) != NOTHING)) {
succ += atr_match(Zone(Location(player)), player,
AMATCH_CMD, lcbuf, preserve_cmd, 1);
}
} /* end of matching on zone of player's location */
}
/* 2.2 style player */
if (!succ && mudconf.local_masters) {
parent = Parent(player);
if (!Has_location(player) || !Good_obj(Location(player)) ||
((parent != Location(player)) &&
(parent != Parent(Location(player))))) {
pcount = 0;
while (!succ && !got_stop &&
Good_obj(parent) && ParentZone(parent) &&
(pcount < mudconf.parent_nest_lim)) {
if (Has_contents(parent)) {
succ += list_check(Contents(parent), player,
AMATCH_CMD, lcbuf, preserve_cmd, 0,
&got_stop);
}
parent = Parent(parent);
pcount++;
}
}
}
/* MUX style player */
/* if nothing matched with parent room/zone object, try matching
* zone commands on the player's personal zone
*/
if (!got_stop && !succ && mudconf.have_zones &&
(Zone(player) != NOTHING) &&
(!Has_location(player) || !Good_obj(Location(player)) ||
(Zone(Location(player)) != Zone(player)))) {
succ += atr_match(Zone(player), player, AMATCH_CMD, lcbuf,
preserve_cmd, 1);
}
/* If we didn't find anything, try in the master room */
if (!got_stop && !succ) {
if (Good_loc(mudconf.master_room)) {
succ += list_check(Contents(mudconf.master_room),
player, AMATCH_CMD, lcbuf,
preserve_cmd, 0, &got_stop);
if (!got_stop && atr_match(mudconf.master_room,
player, AMATCH_CMD, lcbuf, preserve_cmd, 0) > 0) {
succ++;
}
}
}
/* Allow modules to intercept, if still no match.
* This time we pass both the lower-cased evaluated buffer
* and the preserved command.
*/
if (!succ) {
CALL_SOME_MODULES(succ, process_no_match,
(player, cause, interactive, lcbuf, preserve_cmd,
args, nargs));
}
free_lbuf(lcbuf);
/* If we still didn't find anything, tell how to get help. */
if (!succ) {
notify(player, mudconf.huh_msg);
STARTLOG(LOG_BADCOMMANDS, "CMD", "BAD")
log_name_and_loc(player);
log_printf(" entered: %s", command);
ENDLOG
}
mudstate.debug_cmd = cmdsave;
return preserve_cmd;
}
/*
* ---------------------------------------------------------------------------
* * process_cmdline: Execute a semicolon/pipe-delimited series of commands.
*/
void process_cmdline(player, cause, cmdline, args, nargs, qent)
dbref player, cause;
char *cmdline, *args[];
int nargs;
BQUE *qent;
{
char *cp, *cmdsave, *save_poutnew, *save_poutbufc, *save_pout;
char *log_cmdbuf;
int save_inpipe, numpipes;
dbref save_poutobj, save_enactor, save_player;
#ifndef NO_LAG_CHECK
struct timeval begin_time, end_time;
int used_time;
#ifndef NO_TIMECHECKING
struct timeval obj_time;
#endif
#ifdef TRACK_USER_TIME
struct rusage usage;
struct timeval b_utime, e_utime;
#endif
#endif
if (mudstate.cmd_nest_lev == mudconf.cmd_nest_lim)
return;
mudstate.cmd_nest_lev++;
cmdsave = mudstate.debug_cmd;
save_enactor = mudstate.curr_enactor;
save_player = mudstate.curr_player;
mudstate.curr_enactor = cause;
mudstate.curr_player = player;
save_inpipe = mudstate.inpipe;
save_poutobj = mudstate.poutobj;
save_poutnew = mudstate.poutnew;
save_poutbufc = mudstate.poutbufc;
save_pout = mudstate.pout;
while (cmdline && (!qent || qent == mudstate.qfirst)) {
cp = parse_to(&cmdline, ';', 0);
if (cp && *cp) {
numpipes = 0;
while (cmdline && (*cmdline == '|') &&
(!qent || qent == mudstate.qfirst) &&
(numpipes < mudconf.ntfy_nest_lim)) {
cmdline++;
numpipes++;
mudstate.inpipe = 1;
mudstate.poutnew = alloc_lbuf("process_cmdline.pipe");
mudstate.poutbufc = mudstate.poutnew;
mudstate.poutobj = player;
mudstate.debug_cmd = cp;
/* No lag check on piped commands */
process_command(player, cause, 0, cp,
args, nargs);
if (mudstate.pout && mudstate.pout != save_pout) {
free_lbuf(mudstate.pout);
mudstate.pout = NULL;
}
*mudstate.poutbufc = '\0';
mudstate.pout = mudstate.poutnew;
cp = parse_to(&cmdline, ';', 0);
}
mudstate.inpipe = save_inpipe;
mudstate.poutnew = save_poutnew;
mudstate.poutbufc = save_poutbufc;
mudstate.poutobj = save_poutobj;
mudstate.debug_cmd = cp;
/* Is the queue still linked like we think it is? */
if (qent && qent != mudstate.qfirst) {
if (mudstate.pout && mudstate.pout != save_pout) {
free_lbuf(mudstate.pout);
mudstate.pout = NULL;
}
break;
}
#ifndef NO_LAG_CHECK
get_tod(&begin_time);
#ifdef TRACK_USER_TIME
getrusage(RUSAGE_SELF, &usage);
b_utime.tv_sec = usage.ru_utime.tv_sec;
b_utime.tv_usec = usage.ru_utime.tv_usec;
#endif
#endif /* ! NO_LAG_CHECK */
log_cmdbuf = process_command(player, cause,
0, cp, args, nargs);
if (mudstate.pout && mudstate.pout != save_pout) {
free_lbuf(mudstate.pout);
mudstate.pout = save_pout;
}
save_poutbufc = mudstate.poutbufc;
#ifndef NO_LAG_CHECK
get_tod(&end_time);
#ifdef TRACK_USER_TIME
getrusage(RUSAGE_SELF, &usage);
e_utime.tv_sec = usage.ru_utime.tv_sec;
e_utime.tv_usec = usage.ru_utime.tv_usec;
#endif
used_time = msec_diff(end_time, begin_time);
if ((used_time / 1000) >= mudconf.max_cmdsecs) {
STARTLOG(LOG_PROBLEMS, "CMD", "CPU")
log_name_and_loc(player);
log_printf(" queued command taking %.2f secs (enactor #%d): %s",
(double) (used_time / 1000),
mudstate.qfirst->cause,
log_cmdbuf);
ENDLOG
}
#ifndef NO_TIMECHECKING
/* Don't use msec_add(), this is more accurate */
obj_time = Time_Used(player);
#ifndef TRACK_USER_TIME
obj_time.tv_usec += end_time.tv_usec -
begin_time.tv_usec;
obj_time.tv_sec += end_time.tv_sec -
begin_time.tv_sec;
#else
obj_time.tv_usec += e_utime.tv_usec -
b_utime.tv_usec;
obj_time.tv_sec += e_utime.tv_sec -
b_utime.tv_sec;
#endif /* ! TRACK_USER_TIME */
if (obj_time.tv_usec < 0) {
obj_time.tv_usec += 1000000;
obj_time.tv_sec--;
} else if (obj_time.tv_usec >= 1000000) {
obj_time.tv_sec += obj_time.tv_usec / 1000000;
obj_time.tv_usec = obj_time.tv_usec % 1000000;
}
s_Time_Used(player, obj_time);
#endif /* ! NO_TIMECHECKING */
#endif /* ! NO_LAG_CHECK */
}
}
mudstate.debug_cmd = cmdsave;
mudstate.curr_enactor = save_enactor;
mudstate.curr_player = save_player;
mudstate.cmd_nest_lev--;
}
/* ---------------------------------------------------------------------------
* list_cmdtable: List internal commands. Note that user-defined command
* permissions are ignored in this context.
*/
static void list_cmdtable(player)
dbref player;
{
CMDENT *cmdp, *modcmds;
char *buf, *bp;
MODULE *mp;
buf = alloc_lbuf("list_cmdtable");
bp = buf;
safe_str((char *) "Built-in commands:", buf, &bp);
for (cmdp = command_table; cmdp->cmdname; cmdp++) {
if (check_access(player, cmdp->perms)) {
if (!(cmdp->perms & CF_DARK)) {
safe_chr(' ', buf, &bp);
safe_str((char *) cmdp->cmdname, buf, &bp);
}
}
}
/* Players get the list of logged-out cmds too */
if (isPlayer(player))
display_nametab(player, logout_cmdtable, buf, 1);
else
notify(player, buf);
WALK_ALL_MODULES(mp) {
if ((modcmds = DLSYM_VAR(mp->handle, mp->modname, "cmdtable",
CMDENT *)) != NULL) {
bp = buf;
safe_tprintf_str(buf, &bp, "Module %s commands:", mp->modname);
for (cmdp = modcmds; cmdp->cmdname; cmdp++) {
if (check_access(player, cmdp->perms)) {
if (!(cmdp->perms & CF_DARK)) {
safe_chr(' ', buf, &bp);
safe_str((char *) cmdp->cmdname, buf, &bp);
}
}
}
notify(player, buf);
}
}
free_lbuf(buf);
}
/* ---------------------------------------------------------------------------
* list_attrtable: List available attributes.
*/
static void list_attrtable(player)
dbref player;
{
ATTR *ap;
char *buf, *bp, *cp;
buf = alloc_lbuf("list_attrtable");
bp = buf;
for (cp = (char *)"Attributes:"; *cp; cp++)
*bp++ = *cp;
for (ap = attr; ap->name; ap++) {
if (See_attr(player, player, ap, player, 0)) {
*bp++ = ' ';
for (cp = (char *)(ap->name); *cp; cp++)
*bp++ = *cp;
}
}
*bp = '\0';
raw_notify(player, buf);
free_lbuf(buf);
}
/* ---------------------------------------------------------------------------
* list_cmdaccess: List access commands.
*/
static void helper_list_cmdaccess(player, ctab, buff)
dbref player;
CMDENT *ctab;
char *buff;
{
CMDENT *cmdp;
ATTR *ap;
for (cmdp = ctab; cmdp->cmdname; cmdp++) {
if (check_access(player, cmdp->perms)) {
if (!(cmdp->perms & CF_DARK)) {
if (cmdp->userperms) {
ap = atr_num(cmdp->userperms->atr);
if (!ap) {
sprintf(buff, "%s: user(#%d/?BAD?)",
cmdp->cmdname,
cmdp->userperms->thing);
} else {
sprintf(buff, "%s: user(#%d/%s)",
cmdp->cmdname,
cmdp->userperms->thing,
ap->name);
}
} else {
sprintf(buff, "%s:", cmdp->cmdname);
}
listset_nametab(player, access_nametab,
cmdp->perms, buff, 1);
}
}
}
}
static void list_cmdaccess(player)
dbref player;
{
char *buff, *p, *q;
CMDENT *cmdp, *ctab;
ATTR *ap;
MODULE *mp;
buff = alloc_sbuf("list_cmdaccess");
helper_list_cmdaccess(player, command_table, buff);
WALK_ALL_MODULES(mp) {
if ((ctab = DLSYM_VAR(mp->handle, mp->modname, "cmdtable",
CMDENT *)) != NULL) {
helper_list_cmdaccess(player, ctab, buff);
}
}
for (ap = attr; ap->name; ap++) {
p = buff;
*p++ = '@';
for (q = (char *)ap->name; *q; p++, q++)
*p = tolower(*q);
if (ap->flags & AF_NOCMD)
continue;
*p = '\0';
cmdp = (CMDENT *) hashfind(buff, &mudstate.command_htab);
if (cmdp == NULL)
continue;
if (!check_access(player, cmdp->perms))
continue;
if (!(cmdp->perms & CF_DARK)) {
sprintf(buff, "%s:", cmdp->cmdname);
listset_nametab(player, access_nametab,
cmdp->perms, buff, 1);
}
}
free_sbuf(buff);
}
/* ---------------------------------------------------------------------------
* list_cmdswitches: List switches for commands.
*/
static void list_cmdswitches(player)
dbref player;
{
char *buff;
CMDENT *cmdp, *ctab;
MODULE *mp;
buff = alloc_sbuf("list_cmdswitches");
for (cmdp = command_table; cmdp->cmdname; cmdp++) {
if (cmdp->switches) {
if (check_access(player, cmdp->perms)) {
if (!(cmdp->perms & CF_DARK)) {
sprintf(buff, "%s:", cmdp->cmdname);
display_nametab(player, cmdp->switches,
buff, 0);
}
}
}
}
WALK_ALL_MODULES(mp) {
if ((ctab = DLSYM_VAR(mp->handle, mp->modname, "cmdtable",
CMDENT *)) != NULL) {
for (cmdp = ctab; cmdp->cmdname; cmdp++) {
if (cmdp->switches) {
if (check_access(player, cmdp->perms)) {
if (!(cmdp->perms & CF_DARK)) {
sprintf(buff, "%s:", cmdp->cmdname);
display_nametab(player, cmdp->switches,
buff, 0);
}
}
}
}
}
}
free_sbuf(buff);
}
/* ---------------------------------------------------------------------------
* list_attraccess: List access to attributes.
*/
static void list_attraccess(player)
dbref player;
{
char *buff;
ATTR *ap;
buff = alloc_sbuf("list_attraccess");
for (ap = attr; ap->name; ap++) {
if (Read_attr(player, player, ap, player, 0)) {
sprintf(buff, "%s:", ap->name);
listset_nametab(player, attraccess_nametab,
ap->flags, buff, 1);
}
}
free_sbuf(buff);
}
/* ---------------------------------------------------------------------------
* list_attrtypes: List attribute "types" (wildcards and permissions)
*/
static void list_attrtypes(player)
dbref player;
{
char *buff;
KEYLIST *kp;
if (!mudconf.vattr_flag_list) {
notify(player, "No attribute type patterns defined.");
return;
}
buff = alloc_sbuf("list_attrtypes");
for (kp = mudconf.vattr_flag_list; kp != NULL; kp = kp->next) {
sprintf(buff, "%s:", kp->name);
listset_nametab(player, attraccess_nametab, kp->data, buff, 1);
}
free_sbuf(buff);
}
/* ---------------------------------------------------------------------------
* cf_access: Change command or switch permissions.
*/
CF_HAND(cf_access)
{
CMDENT *cmdp;
char *ap;
int set_switch;
for (ap = str; *ap && !isspace(*ap) && (*ap != '/'); ap++) ;
if (*ap == '/') {
set_switch = 1;
*ap++ = '\0';
} else {
set_switch = 0;
if (*ap)
*ap++ = '\0';
while (*ap && isspace(*ap))
ap++;
}
cmdp = (CMDENT *) hashfind(str, &mudstate.command_htab);
if (cmdp != NULL) {
if (set_switch)
return cf_ntab_access((int *)cmdp->switches, ap,
extra, player, cmd);
else
return cf_modify_bits(&(cmdp->perms), ap,
extra, player, cmd);
} else {
cf_log_notfound(player, cmd, "Command", str);
return -1;
}
}
/* ---------------------------------------------------------------------------
* cf_acmd_access: Change command permissions for all attr-setting cmds.
*/
CF_HAND(cf_acmd_access)
{
CMDENT *cmdp;
ATTR *ap;
char *buff, *p, *q;
int failure, save;
buff = alloc_sbuf("cf_acmd_access");
for (ap = attr; ap->name; ap++) {
p = buff;
*p++ = '@';
for (q = (char *)ap->name; *q; p++, q++)
*p = tolower(*q);
*p = '\0';
cmdp = (CMDENT *) hashfind(buff, &mudstate.command_htab);
if (cmdp != NULL) {
save = cmdp->perms;
failure = cf_modify_bits(&(cmdp->perms), str,
extra, player, cmd);
if (failure != 0) {
cmdp->perms = save;
free_sbuf(buff);
return -1;
}
}
}
free_sbuf(buff);
return 0;
}
/* ---------------------------------------------------------------------------
* cf_attr_access: Change access on an attribute.
*/
CF_HAND(cf_attr_access)
{
ATTR *ap;
char *sp;
for (sp = str; *sp && !isspace(*sp); sp++) ;
if (*sp)
*sp++ = '\0';
while (*sp && isspace(*sp))
sp++;
ap = atr_str(str);
if (ap != NULL)
return cf_modify_bits(&(ap->flags), sp, extra, player, cmd);
else {
cf_log_notfound(player, cmd, "Attribute", str);
return -1;
}
}
/* ---------------------------------------------------------------------------
* cf_attr_type: Define attribute flags for new user-named attributes
* whose names match a certain pattern.
*/
CF_HAND(cf_attr_type)
{
char *privs;
KEYLIST *kp;
int succ;
/* Split our string into the attribute pattern and privileges. Also
* uppercase it, while we're at it. Make sure it's not longer than
* an attribute name can be.
*/
for (privs = str; *privs && !isspace(*privs); privs++) {
*privs = toupper(*privs);
}
if (*privs)
*privs++ = '\0';
while (*privs && isspace(*privs))
privs++;
if (strlen(str) >= VNAME_SIZE)
str[VNAME_SIZE - 1] = '\0';
/* Create our new data blob. Make sure that we're setting the privs
* to something reasonable before trying to link it in. (If we're
* not, an error will have been logged; we don't need to do it.)
*/
kp = (KEYLIST *) XMALLOC(sizeof(KEYLIST), "cf_attr_type.kp");
kp->data = 0;
succ = cf_modify_bits(&(kp->data), privs, extra, player, cmd);
if (succ < 0) {
XFREE(kp, "cf_attr_type.kp");
return -1;
}
kp->name = XSTRDUP(str, "cf_attr_type.name");
kp->next = mudconf.vattr_flag_list;
mudconf.vattr_flag_list = kp;
return (succ);
}
/* ---------------------------------------------------------------------------
* cf_cmd_alias: Add a command alias.
*/
CF_HAND(cf_cmd_alias)
{
char *alias, *orig, *ap, *tokst;
CMDENT *cmdp, *cmd2;
NAMETAB *nt;
int *hp;
alias = strtok_r(str, " \t=,", &tokst);
orig = strtok_r(NULL, " \t=,", &tokst);
if (!orig) { /* we only got one argument to @alias. Bad. */
cf_log_syntax(player, cmd, "Invalid original for alias %s",
alias);
return -1;
}
if (alias[0] == '_' && alias[1] == '_') {
cf_log_syntax(player, cmd, "Alias %s would cause @addcommand conflict",
alias);
return -1;
}
for (ap = orig; *ap && (*ap != '/'); ap++) ;
if (*ap == '/') {
/* Switch form of command aliasing: create an alias for a
* command + a switch
*/
*ap++ = '\0';
/* Look up the command */
cmdp = (CMDENT *) hashfind(orig, (HASHTAB *) vp);
if (cmdp == NULL) {
cf_log_notfound(player, cmd, "Command", orig);
return -1;
}
/* Look up the switch */
nt = find_nametab_ent(player, (NAMETAB *) cmdp->switches, ap);
if (!nt) {
cf_log_notfound(player, cmd, "Switch", ap);
return -1;
}
/*
* Got it, create the new command table entry
*/
cmd2 = (CMDENT *) XMALLOC(sizeof(CMDENT), "cf_cmd_alias");
cmd2->cmdname = XSTRDUP(alias, "cf_cmd_alias.cmdname");
cmd2->switches = cmdp->switches;
cmd2->perms = cmdp->perms | nt->perm;
cmd2->extra = (cmdp->extra | nt->flag) & ~SW_MULTIPLE;
if (!(nt->flag & SW_MULTIPLE))
cmd2->extra |= SW_GOT_UNIQUE;
cmd2->callseq = cmdp->callseq;
/*
* KNOWN PROBLEM:
* We are not inheriting the hook that the 'original' command
* had -- we will have to add it manually (whereas an alias
* of a non-switched command is just another hashtable entry
* for the same command pointer and therefore gets the hook).
* This is preferable to having to search the hashtable for
* hooks when a hook is deleted, though.
*/
cmd2->pre_hook = NULL;
cmd2->post_hook = NULL;
cmd2->userperms = NULL;
cmd2->info.handler = cmdp->info.handler;
if (hashadd(cmd2->cmdname, (int *)cmd2, (HASHTAB *) vp, 0)) {
XFREE(cmd2->cmdname, "cf_cmd_alias.cmdname");
XFREE(cmd2, "cf_cmd_alias");
}
} else {
/* A normal (non-switch) alias */
hp = hashfind(orig, (HASHTAB *) vp);
if (hp == NULL) {
cf_log_notfound(player, cmd, "Entry", orig);
return -1;
}
hashadd(alias, hp, (HASHTAB *) vp, HASH_ALIAS);
}
return 0;
}
/* ---------------------------------------------------------------------------
* list_df_flags: List default flags at create time.
*/
static void list_df_flags(player)
dbref player;
{
char *playerb, *roomb, *thingb, *exitb, *robotb, *stripb, *buff;
playerb = decode_flags(player, mudconf.player_flags);
roomb = decode_flags(player, mudconf.room_flags);
exitb = decode_flags(player, mudconf.exit_flags);
thingb = decode_flags(player, mudconf.thing_flags);
robotb = decode_flags(player, mudconf.robot_flags);
stripb = decode_flags(player, mudconf.stripped_flags);
buff = alloc_lbuf("list_df_flags");
sprintf(buff,
"Default flags: Players...P%s Rooms...R%s Exits...E%s Things...%s Robots...P%s Stripped...%s",
playerb, roomb, exitb, thingb, robotb, stripb);
raw_notify(player, buff);
free_lbuf(buff);
free_sbuf(playerb);
free_sbuf(roomb);
free_sbuf(exitb);
free_sbuf(thingb);
free_sbuf(robotb);
free_sbuf(stripb);
}
/* ---------------------------------------------------------------------------
* list_costs: List the costs of things.
*/
#define coin_name(s) (((s)==1) ? mudconf.one_coin : mudconf.many_coins)
static void list_costs(player)
dbref player;
{
char *buff;
buff = alloc_mbuf("list_costs");
*buff = '\0';
if (mudconf.quotas)
sprintf(buff, " and %d quota", mudconf.room_quota);
notify(player,
tprintf("Digging a room costs %d %s%s.",
mudconf.digcost, coin_name(mudconf.digcost), buff));
if (mudconf.quotas)
sprintf(buff, " and %d quota", mudconf.exit_quota);
notify(player,
tprintf("Opening a new exit costs %d %s%s.",
mudconf.opencost, coin_name(mudconf.opencost), buff));
notify(player,
tprintf("Linking an exit, home, or dropto costs %d %s.",
mudconf.linkcost, coin_name(mudconf.linkcost)));
if (mudconf.quotas)
sprintf(buff, " and %d quota", mudconf.thing_quota);
if (mudconf.createmin == mudconf.createmax)
raw_notify(player,
tprintf("Creating a new thing costs %d %s%s.",
mudconf.createmin,
coin_name(mudconf.createmin), buff));
else
raw_notify(player,
tprintf("Creating a new thing costs between %d and %d %s%s.",
mudconf.createmin, mudconf.createmax,
mudconf.many_coins, buff));
if (mudconf.quotas)
sprintf(buff, " and %d quota", mudconf.player_quota);
notify(player,
tprintf("Creating a robot costs %d %s%s.",
mudconf.robotcost, coin_name(mudconf.robotcost),
buff));
if (mudconf.killmin == mudconf.killmax) {
raw_notify(player,
tprintf("Killing costs %d %s, with a %d%% chance of success.",
mudconf.killmin, coin_name(mudconf.digcost),
(mudconf.killmin * 100) /
mudconf.killguarantee));
} else {
raw_notify(player,
tprintf("Killing costs between %d and %d %s.",
mudconf.killmin, mudconf.killmax,
mudconf.many_coins));
raw_notify(player,
tprintf("You must spend %d %s to guarantee success.",
mudconf.killguarantee,
coin_name(mudconf.killguarantee)));
}
raw_notify(player,
tprintf("Computationally expensive commands and functions (ie: @entrances, @find, @search, @stats (with an argument or switch), search(), and stats()) cost %d %s.",
mudconf.searchcost, coin_name(mudconf.searchcost)));
if (mudconf.machinecost > 0)
raw_notify(player,
tprintf("Each command run from the queue costs 1/%d %s.",
mudconf.machinecost, mudconf.one_coin));
if (mudconf.waitcost > 0) {
raw_notify(player,
tprintf("A %d %s deposit is charged for putting a command on the queue.",
mudconf.waitcost, mudconf.one_coin));
raw_notify(player, "The deposit is refunded when the command is run or canceled.");
}
if (mudconf.sacfactor == 0)
sprintf(buff, "%d", mudconf.sacadjust);
else if (mudconf.sacfactor == 1) {
if (mudconf.sacadjust < 0)
sprintf(buff, "<create cost> - %d", -mudconf.sacadjust);
else if (mudconf.sacadjust > 0)
sprintf(buff, "<create cost> + %d", mudconf.sacadjust);
else
sprintf(buff, "<create cost>");
} else {
if (mudconf.sacadjust < 0)
sprintf(buff, "(<create cost> / %d) - %d",
mudconf.sacfactor, -mudconf.sacadjust);
else if (mudconf.sacadjust > 0)
sprintf(buff, "(<create cost> / %d) + %d",
mudconf.sacfactor, mudconf.sacadjust);
else
sprintf(buff, "<create cost> / %d", mudconf.sacfactor);
}
raw_notify(player, tprintf("The value of an object is %s.", buff));
if (mudconf.clone_copy_cost)
raw_notify(player, "The default value of cloned objects is the value of the original object.");
else
raw_notify(player,
tprintf("The default value of cloned objects is %d %s.",
mudconf.createmin,
coin_name(mudconf.createmin)));
free_mbuf(buff);
}
/* ---------------------------------------------------------------------------
* list_options: List boolean game options from mudconf.
* list_config: List non-boolean game options.
*/
extern void FDECL(list_options, (dbref));
static void list_params(player)
dbref player;
{
time_t now;
now = time(NULL);
raw_notify(player,
tprintf("Prototypes: Room...#%d Exit...#%d Thing...#%d Player...#%d",
mudconf.room_proto, mudconf.exit_proto,
mudconf.thing_proto, mudconf.player_proto));
raw_notify(player,
tprintf("Attr Defaults: Room...#%d Exit...#%d Thing...#%d Player...#%d",
mudconf.room_defobj, mudconf.exit_defobj,
mudconf.thing_defobj, mudconf.player_defobj));
raw_notify(player,
tprintf("Default Parents: Room...#%d Exit...#%d Thing...#%d Player...#%d",
mudconf.room_parent, mudconf.exit_parent,
mudconf.thing_parent, mudconf.player_parent));
raw_notify(player, "Limits:");
raw_notify(player,
tprintf(" Function recursion...%d Function invocation...%d",
mudconf.func_nest_lim, mudconf.func_invk_lim));
raw_notify(player,
tprintf(" Command recursion...%d Command invocation...%d",
mudconf.cmd_nest_lim, mudconf.cmd_invk_lim));
raw_notify(player,
tprintf(" Output...%d Queue...%d CPU...%d Forwardlist...%d Wild...%d",
mudconf.output_limit, mudconf.queuemax,
mudconf.func_cpu_lim_secs, mudconf.fwdlist_lim,
mudconf.wild_times_lim));
raw_notify(player,
tprintf(" Aliases...%d Registers...%d Stacks...%d",
mudconf.max_player_aliases, mudconf.register_limit,
mudconf.stack_lim));
raw_notify(player,
tprintf(" Variables...%d Structures...%d Instances...%d",
mudconf.numvars_lim, mudconf.struct_lim, mudconf.instance_lim));
raw_notify(player,
tprintf(" Objects...%d Allowance...%d Trace levels...%d Connect tries...%d",
mudconf.building_limit, mudconf.paylimit,
mudconf.trace_limit, mudconf.retry_limit));
if (mudconf.max_players >= 0)
raw_notify(player, tprintf(" Logins...%d", mudconf.max_players));
raw_notify(player,
tprintf("Nesting: Locks...%d Parents...%d Messages...%d Zones...%d",
mudconf.lock_nest_lim, mudconf.parent_nest_lim,
mudconf.ntfy_nest_lim, mudconf.zone_nest_lim));
raw_notify(player,
tprintf("Timeouts: Idle...%d Connect...%d Tries...%d Lag...%d",
mudconf.idle_timeout, mudconf.conn_timeout,
mudconf.retry_limit, mudconf.max_cmdsecs));
raw_notify(player,
tprintf("Money: Start...%d Daily...%d Singular: %s Plural: %s",
mudconf.paystart, mudconf.paycheck,
mudconf.one_coin, mudconf.many_coins));
if (mudconf.payfind > 0)
raw_notify(player, tprintf("Chance of finding money: 1 in %d",
mudconf.payfind));
raw_notify(player,
tprintf("Start Quotas: Total...%d Rooms...%d Exits...%d Things...%d Players...%d",
mudconf.start_quota,
mudconf.start_room_quota, mudconf.start_exit_quota,
mudconf.start_thing_quota, mudconf.start_player_quota));
raw_notify(player, "Dbrefs:");
raw_notify(player,
tprintf(" MasterRoom...#%d StartRoom...#%d StartHome...#%d DefaultHome...#%d",
mudconf.master_room, mudconf.start_room, mudconf.start_home,
mudconf.default_home));
if (Wizard(player)) {
raw_notify(player,
tprintf(" GuestChar...#%d GuestStart...#%d Freelist...#%d",
mudconf.guest_char, mudconf.guest_start_room,
mudstate.freelist));
raw_notify(player,
tprintf("Queue run sizes: No net activity... %d Activity... %d",
mudconf.queue_chunk, mudconf.active_q_chunk));
raw_notify(player,
tprintf("Intervals: Dump...%d Clean...%d Idlecheck...%d Optimize...%d",
mudconf.dump_interval, mudconf.check_interval,
mudconf.idle_interval, mudconf.dbopt_interval));
raw_notify(player,
tprintf("Timers: Dump...%d Clean...%d Idlecheck...%d",
(int) (mudstate.dump_counter - now),
(int) (mudstate.check_counter - now),
(int) (mudstate.idle_counter - now)));
raw_notify(player,
tprintf("Scheduling: Timeslice...%d Max_Quota...%d Increment...%d",
mudconf.timeslice, mudconf.cmd_quota_max,
mudconf.cmd_quota_incr));
raw_notify(player,
tprintf("Size of %s cache: Width...%d Size...%d",
CACHING, mudconf.cache_width, mudconf.cache_size));
}
}
/* ---------------------------------------------------------------------------
* list_vattrs: List user-defined attributes
*/
static void list_vattrs(player)
dbref player;
{
VATTR *va;
int na;
char *buff;
buff = alloc_lbuf("list_vattrs");
raw_notify(player, "--- User-Defined Attributes ---");
for (va = vattr_first(), na = 0; va; va = vattr_next(va), na++) {
if (!(va->flags & AF_DELETED)) {
sprintf(buff, "%s(%d):", va->name, va->number);
listset_nametab(player, attraccess_nametab, va->flags,
buff, 1);
}
}
raw_notify(player, tprintf("%d attributes, next=%d",
na, mudstate.attr_next));
free_lbuf(buff);
}
/* ---------------------------------------------------------------------------
* list_hashstats: List information from hash tables
*/
static void list_hashstat(player, tab_name, htab)
dbref player;
HASHTAB *htab;
const char *tab_name;
{
char *buff;
buff = hashinfo(tab_name, htab);
raw_notify(player, buff);
free_mbuf(buff);
}
static void list_nhashstat(player, tab_name, htab)
dbref player;
NHSHTAB *htab;
const char *tab_name;
{
char *buff;
buff = nhashinfo(tab_name, htab);
raw_notify(player, buff);
free_mbuf(buff);
}
static void list_hashstats(player)
dbref player;
{
MODULE *mp;
MODHASHES *m_htab, *hp;
MODNHASHES *m_ntab, *np;
raw_notify(player, "Hash Stats Size Entries Deleted Empty Lookups Hits Checks Longest");
list_hashstat(player, "Commands", &mudstate.command_htab);
list_hashstat(player, "Logged-out Cmds", &mudstate.logout_cmd_htab);
list_hashstat(player, "Functions", &mudstate.func_htab);
list_hashstat(player, "User Functions", &mudstate.ufunc_htab);
list_hashstat(player, "Flags", &mudstate.flags_htab);
list_hashstat(player, "Powers", &mudstate.powers_htab);
list_hashstat(player, "Attr names", &mudstate.attr_name_htab);
list_hashstat(player, "Vattr names", &mudstate.vattr_name_htab);
list_hashstat(player, "Player Names", &mudstate.player_htab);
list_hashstat(player, "References", &mudstate.nref_htab);
list_nhashstat(player, "Net Descriptors", &mudstate.desc_htab);
list_nhashstat(player, "Forwardlists", &mudstate.fwdlist_htab);
list_nhashstat(player, "Redirections", &mudstate.redir_htab);
list_nhashstat(player, "Overlaid $-cmds", &mudstate.parent_htab);
list_nhashstat(player, "Object Stacks", &mudstate.objstack_htab);
list_hashstat(player, "Variables", &mudstate.vars_htab);
list_hashstat(player, "Structure Defs", &mudstate.structs_htab);
list_hashstat(player, "Component Defs", &mudstate.cdefs_htab);
list_hashstat(player, "Instances", &mudstate.instance_htab);
list_hashstat(player, "Instance Data", &mudstate.instdata_htab);
list_hashstat(player, "Module APIs", &mudstate.api_func_htab);
WALK_ALL_MODULES(mp) {
m_htab = DLSYM_VAR(mp->handle, mp->modname,
"hashtable", MODHASHES *);
if (m_htab) {
for (hp = m_htab; hp->htab != NULL; hp++) {
list_hashstat(player, hp->tabname, hp->htab);
}
}
m_ntab = DLSYM_VAR(mp->handle, mp->modname,
"nhashtable", MODNHASHES *);
if (m_ntab) {
for (np = m_ntab; np->tabname != NULL; np++) {
list_nhashstat(player, np->tabname, np->htab);
}
}
}
}
static void list_textfiles(player)
dbref player;
{
int i;
raw_notify(player, "Help File Size Entries Deleted Empty Lookups Hits Checks Longest");
for (i = 0; i < mudstate.helpfiles; i++)
list_hashstat(player, mudstate.hfiletab[i], &mudstate.hfile_hashes[i]);
}
/* These are from 'udb_cache.c'. */
extern time_t cs_ltime;
extern int cs_writes; /* total writes */
extern int cs_reads; /* total reads */
extern int cs_dbreads; /* total read-throughs */
extern int cs_dbwrites; /* total write-throughs */
extern int cs_dels; /* total deletes */
extern int cs_checks; /* total checks */
extern int cs_rhits; /* total reads filled from cache */
extern int cs_ahits; /* total reads filled active cache */
extern int cs_whits; /* total writes to dirty cache */
extern int cs_fails; /* attempts to grab nonexistent */
extern int cs_resets; /* total cache resets */
extern int cs_syncs; /* total cache syncs */
extern int cs_size; /* total cache size */
/* ---------------------------------------------------------------------------
* list_db_stats: Get useful info from the DB layer about hash stats, etc.
*/
static void list_db_stats(player)
dbref player;
{
raw_notify(player,
tprintf("DB Cache Stats Writes Reads (over %d seconds)",
(int) (time(NULL) - cs_ltime)));
raw_notify(player, tprintf("Calls %12d%12d", cs_writes, cs_reads));
raw_notify(player, tprintf("Cache Hits %12d%12d",
cs_whits, cs_rhits));
raw_notify(player, tprintf("I/O %12d%12d",
cs_dbwrites, cs_dbreads));
raw_notify(player, tprintf("Failed %12d",
cs_fails));
raw_notify(player, tprintf("Hit ratio %2.0f%% %2.0f%%",
(cs_writes ? (float) cs_whits / cs_writes * 100 : 0.0),
(cs_reads ? (float) cs_rhits / cs_reads * 100 : 0.0)));
raw_notify(player, tprintf("\nDeletes %12d", cs_dels));
raw_notify(player, tprintf("Checks %12d", cs_checks));
raw_notify(player, tprintf("Syncs %12d", cs_syncs));
raw_notify(player, tprintf("Cache Size %12d bytes", cs_size));
}
/* ---------------------------------------------------------------------------
* list_process: List local resource usage stats of the MUSH process.
* Adapted from code by Claudius@PythonMUCK,
* posted to the net by Howard/Dark_Lord.
*/
static void list_process(player)
dbref player;
{
int pid, psize, maxfds;
#if defined(HAVE_GETRUSAGE) && defined(STRUCT_RUSAGE_COMPLETE)
struct rusage usage;
int ixrss, idrss, isrss, curr, last, dur;
getrusage(RUSAGE_SELF, &usage);
/*
* Calculate memory use from the aggregate totals
*/
curr = mudstate.mstat_curr;
last = 1 - curr;
dur = mudstate.mstat_secs[curr] - mudstate.mstat_secs[last];
if (dur > 0) {
ixrss = (mudstate.mstat_ixrss[curr] -
mudstate.mstat_ixrss[last]) / dur;
idrss = (mudstate.mstat_idrss[curr] -
mudstate.mstat_idrss[last]) / dur;
isrss = (mudstate.mstat_isrss[curr] -
mudstate.mstat_isrss[last]) / dur;
} else {
ixrss = 0;
idrss = 0;
isrss = 0;
}
#endif
#ifdef HAVE_GETDTABLESIZE
maxfds = getdtablesize();
#else
maxfds = sysconf(_SC_OPEN_MAX);
#endif
pid = getpid();
psize = getpagesize();
/*
* Go display everything
*/
raw_notify(player,
tprintf("Process ID: %10d %10d bytes per page",
pid, psize));
#if defined(HAVE_GETRUSAGE) && defined(STRUCT_RUSAGE_COMPLETE)
raw_notify(player,
tprintf("Time used: %10d user %10d sys",
usage.ru_utime.tv_sec, usage.ru_stime.tv_sec));
/*
* raw_notify(player,
* * tprintf("Resident mem:%10d shared %10d private%10d stack",
* * ixrss, idrss, isrss));
*/
raw_notify(player,
tprintf("Integral mem:%10d shared %10d private%10d stack",
usage.ru_ixrss, usage.ru_idrss, usage.ru_isrss));
raw_notify(player,
tprintf("Max res mem: %10d pages %10d bytes",
usage.ru_maxrss, (usage.ru_maxrss * psize)));
raw_notify(player,
tprintf("Page faults: %10d hard %10d soft %10d swapouts",
usage.ru_majflt, usage.ru_minflt, usage.ru_nswap));
raw_notify(player,
tprintf("Disk I/O: %10d reads %10d writes",
usage.ru_inblock, usage.ru_oublock));
raw_notify(player,
tprintf("Network I/O: %10d in %10d out",
usage.ru_msgrcv, usage.ru_msgsnd));
raw_notify(player,
tprintf("Context swi: %10d vol %10d forced %10d sigs",
usage.ru_nvcsw, usage.ru_nivcsw, usage.ru_nsignals));
raw_notify(player,
tprintf("Descs avail: %10d", maxfds));
#endif
}
/* ---------------------------------------------------------------------------
* list_memory: Breaks down memory usage of the process
*/
extern Chain *sys_c;
extern NAME *names, *purenames;
extern POOL pools[NUM_POOLS];
extern int anum_alc_top;
void list_memory(player)
{
double total = 0, each = 0, each2 = 0;
int i, j;
CMDENT *cmd;
ADDENT *add;
NAMETAB *name;
VATTR *vattr;
ATTR *attr;
FUN *func;
UFUN *ufunc;
Cache *cp;
Chain *sp;
HASHENT *htab;
struct help_entry *hlp;
FLAGENT *flag;
POWERENT *power;
OBJSTACK *stack;
VARENT *xvar;
STRUCTDEF *this_struct;
INSTANCE *inst_ptr;
STRUCTDATA *data_ptr;
/* Calculate size of object structures */
each = mudstate.db_top * sizeof(OBJ);
raw_notify(player,
tprintf("Object structures: %12.2fk", each / 1024));
total += each;
#ifdef MEMORY_BASED
each = 0;
/* Calculate size of stored attribute text */
DO_WHOLE_DB(i) {
each += obj_siz(&(db[i].attrtext));
each -= sizeof(Obj);
}
raw_notify(player,
tprintf("Stored attrtext : %12.2fk", each / 1024));
total += each;
#endif
/* Calculate size of mudstate and mudconf structures */
each = sizeof(CONFDATA) + sizeof(STATEDATA);
raw_notify(player,
tprintf("mudconf/mudstate : %12.2fk", each / 1024));
total += each;
/* Calculate size of cache */
each = cs_size;
raw_notify(player,
tprintf("Cache data : %12.2fk", each / 1024));
total += each;
each = sizeof(Chain) * mudconf.cache_width;
for (i = 0; i < mudconf.cache_width; i++) {
sp = &sys_c[i];
for(cp = sp->head; cp != NULL; cp = cp->nxt) {
each += sizeof(Cache);
each2 += cp->keylen;
}
}
raw_notify(player,
tprintf("Cache keys : %12.2fk", each2 / 1024));
raw_notify(player,
tprintf("Cache overhead : %12.2fk", each / 1024));
total += each + each2;
/* Calculate size of object pipelines */
each = 0;
for (i = 0; i < NUM_OBJPIPES; i++) {
if (mudstate.objpipes[i])
each += obj_siz(mudstate.objpipes[i]);
}
raw_notify(player,
tprintf("Object pipelines : %12.2fk", each / 1024));
total += each;
/* Calculate size of name caches */
each = sizeof(NAME *) * mudstate.db_top * 2;
for (i = 0; i < mudstate.db_top; i++) {
if (purenames[i])
each += strlen(purenames[i]) + 1;
if (names[i])
each += strlen(names[i]) + 1;
}
raw_notify(player,
tprintf("Name caches : %12.2fk", each / 1024));
total += each;
/* Calculate size of buffers */
each = sizeof(POOL) * NUM_POOLS;
for (i = 0; i < NUM_POOLS; i++) {
each += pools[i].max_alloc * (pools[i].pool_size +
sizeof(POOLHDR) + sizeof(POOLFTR));
}
raw_notify(player,
tprintf("Buffers : %12.2fk", each / 1024));
total += each;
/* Calculate size of command hashtable */
each = 0;
each += sizeof(HASHENT *) * mudstate.command_htab.hashsize;
for(i = 0; i < mudstate.command_htab.hashsize; i++) {
htab = mudstate.command_htab.entry[i];
while (htab != NULL) {
each += sizeof(HASHENT);
each += strlen(mudstate.command_htab.entry[i]->target.s) + 1;
/* Add up all the little bits in the CMDENT. */
if (!(htab->flags & HASH_ALIAS)) {
each += sizeof(CMDENT);
cmd = (CMDENT *)htab->data;
each += strlen(cmd->cmdname) + 1;
if ((name = cmd->switches) != NULL) {
for(j = 0; name[j].name != NULL; j++) {
each += sizeof(NAMETAB);
each += strlen(name[j].name) + 1;
}
}
if (cmd->callseq & CS_ADDED) {
add = cmd->info.added;
while (add != NULL) {
each += sizeof(ADDENT);
each += strlen(add->name) + 1;
add = add->next;
}
}
}
htab = htab->next;
}
}
raw_notify(player,
tprintf("Command table : %12.2fk", each / 1024));
total += each;
/* Calculate size of logged-out commands hashtable */
each = 0;
each += sizeof(HASHENT *) * mudstate.logout_cmd_htab.hashsize;
for(i = 0; i < mudstate.logout_cmd_htab.hashsize; i++) {
htab = mudstate.logout_cmd_htab.entry[i];
while (htab != NULL) {
each += sizeof(HASHENT);
each += strlen(htab->target.s) + 1;
if (!(htab->flags & HASH_ALIAS)) {
name = (NAMETAB *)htab->data;
each += sizeof(NAMETAB);
each += strlen(name->name) + 1;
}
htab = htab->next;
}
}
raw_notify(player,
tprintf("Logout cmd htab : %12.2fk", each / 1024));
total += each;
/* Calculate size of functions hashtable */
each = 0;
each += sizeof(HASHENT *) * mudstate.func_htab.hashsize;
for(i = 0; i < mudstate.func_htab.hashsize; i++) {
htab = mudstate.func_htab.entry[i];
while (htab != NULL) {
each += sizeof(HASHENT);
each += strlen(htab->target.s) + 1;
if (!(htab->flags & HASH_ALIAS)) {
func = (FUN *)htab->data;
each += sizeof(FUN);
}
/* We don't count func->name because we already got
* it with htab->target.s */
htab = htab->next;
}
}
raw_notify(player,
tprintf("Functions htab : %12.2fk", each / 1024));
total += each;
/* Calculate size of user-defined functions hashtable */
each = 0;
each += sizeof(HASHENT *) * mudstate.ufunc_htab.hashsize;
for(i = 0; i < mudstate.ufunc_htab.hashsize; i++) {
htab = mudstate.ufunc_htab.entry[i];
while (htab != NULL) {
each += sizeof(HASHENT);
each += strlen(htab->target.s) + 1;
if (!(htab->flags & HASH_ALIAS)) {
ufunc = (UFUN *)htab->data;
while (ufunc != NULL) {
each += sizeof(UFUN);
each += strlen(ufunc->name) + 1;
ufunc = ufunc->next;
}
}
htab = htab->next;
}
}
raw_notify(player,
tprintf("U-functions htab : %12.2fk", each / 1024));
total += each;
/* Calculate size of flags hashtable */
each = 0;
each += sizeof(HASHENT *) * mudstate.flags_htab.hashsize;
for(i = 0; i < mudstate.flags_htab.hashsize; i++) {
htab = mudstate.flags_htab.entry[i];
while (htab != NULL) {
each += sizeof(HASHENT);
each += strlen(htab->target.s) + 1;
if (!(htab->flags & HASH_ALIAS)) {
flag = (FLAGENT *)htab->data;
each += sizeof(FLAGENT);
}
/* We don't count flag->flagname because we already got
* it with htab->target.s */
htab = htab->next;
}
}
raw_notify(player,
tprintf("Flags htab : %12.2fk", each / 1024));
total += each;
/* Calculate size of powers hashtable */
each = 0;
each += sizeof(HASHENT *) * mudstate.powers_htab.hashsize;
for(i = 0; i < mudstate.powers_htab.hashsize; i++) {
htab = mudstate.powers_htab.entry[i];
while (htab != NULL) {
each += sizeof(HASHENT);
each += strlen(htab->target.s) + 1;
if (!(htab->flags & HASH_ALIAS)) {
power = (POWERENT *)htab->data;
each += sizeof(POWERENT);
}
/* We don't count power->powername because we already got
* it with htab->target.s */
htab = htab->next;
}
}
raw_notify(player,
tprintf("Powers htab : %12.2fk", each / 1024));
total += each;
/* Calculate size of helpfile hashtables */
each = 0;
for(j = 0; j < mudstate.helpfiles; j++) {
each += sizeof(HASHENT *) * mudstate.hfile_hashes[j].hashsize;
for(i = 0; i < mudstate.hfile_hashes[j].hashsize; i++) {
htab = mudstate.hfile_hashes[j].entry[i];
while (htab != NULL) {
each += sizeof(HASHENT);
each += strlen(htab->target.s) + 1;
if (!(htab->flags & HASH_ALIAS)) {
each += sizeof(struct help_entry);
hlp = (struct help_entry *)htab->data;
}
htab = htab->next;
}
}
}
raw_notify(player,
tprintf("Helpfiles htabs : %12.2fk", each / 1024));
total += each;
/* Calculate size of vattr name hashtable */
each = 0;
each += sizeof(HASHENT *) * mudstate.vattr_name_htab.hashsize;
for(i = 0; i < mudstate.vattr_name_htab.hashsize; i++) {
htab = mudstate.vattr_name_htab.entry[i];
while (htab != NULL) {
each += sizeof(HASHENT);
each += strlen(htab->target.s) + 1;
each += sizeof(VATTR);
htab = htab->next;
}
}
raw_notify(player,
tprintf("Vattr name htab : %12.2fk", each / 1024));
total += each;
/* Calculate size of attr name hashtable */
each = 0;
each += sizeof(HASHENT *) * mudstate.attr_name_htab.hashsize;
for(i = 0; i < mudstate.attr_name_htab.hashsize; i++) {
htab = mudstate.attr_name_htab.entry[i];
while (htab != NULL) {
each += sizeof(HASHENT);
each += strlen(htab->target.s) + 1;
if (!(htab->flags & HASH_ALIAS)) {
attr = (ATTR *)htab->data;
each += sizeof(ATTR);
each += strlen(attr->name) + 1;
}
htab = htab->next;
}
}
raw_notify(player,
tprintf("Attr name htab : %12.2fk", each / 1024));
total += each;
/* Calculate the size of anum_table */
each = sizeof(ATTR *) * anum_alc_top;
raw_notify(player,
tprintf("Attr num table : %12.2fk", each / 1024));
total += each;
/* --- After this point, we only report if it's non-zero. */
/* Calculate size of object stacks */
each = 0;
for (stack = (OBJSTACK *)hash_firstentry((HASHTAB *)&mudstate.objstack_htab);
stack != NULL;
stack = (OBJSTACK *)hash_nextentry((HASHTAB *)&mudstate.objstack_htab)) {
each += sizeof(OBJSTACK);
each += strlen(stack->data) + 1;
}
if (each) {
raw_notify(player,
tprintf("Object stacks : %12.2fk", each / 1024));
}
total += each;
/* Calculate the size of xvars. */
each = 0;
for (xvar = (VARENT *) hash_firstentry(&mudstate.vars_htab);
xvar != NULL;
xvar = (VARENT *) hash_nextentry(&mudstate.vars_htab)) {
each += sizeof(VARENT);
each += strlen(xvar->text) + 1;
}
if (each) {
raw_notify(player,
tprintf("X-Variables : %12.2fk", each / 1024));
}
total += each;
/* Calculate the size of overhead associated with structures. */
each = 0;
for (this_struct = (STRUCTDEF *) hash_firstentry(&mudstate.structs_htab);
this_struct != NULL;
this_struct = (STRUCTDEF *) hash_nextentry(&mudstate.structs_htab)) {
each += sizeof(STRUCTDEF);
each += strlen(this_struct->s_name) + 1;
for (i = 0; i < this_struct->c_count; i++) {
each += strlen(this_struct->c_names[i]) + 1;
each += sizeof(COMPONENT);
each += strlen(this_struct->c_array[i]->def_val) + 1;
}
}
for (inst_ptr = (INSTANCE *) hash_firstentry(&mudstate.instance_htab);
inst_ptr != NULL;
inst_ptr = (INSTANCE *) hash_nextentry(&mudstate.instance_htab)) {
each += sizeof(INSTANCE);
}
if (each) {
raw_notify(player,
tprintf("Struct var defs : %12.2fk", each / 1024));
}
total += each;
/* Calculate the size of data associated with structures. */
each = 0;
for (data_ptr = (STRUCTDATA *) hash_firstentry(&mudstate.instdata_htab);
data_ptr != NULL;
data_ptr = (STRUCTDATA *) hash_nextentry(&mudstate.instdata_htab)) {
each += sizeof(STRUCTDATA);
if (data_ptr->text) {
each += strlen(data_ptr->text) + 1;
}
}
if (each) {
raw_notify(player,
tprintf("Struct var data : %12.2fk", each / 1024));
}
total += each;
/* Report end total. */
raw_notify(player,
tprintf("\r\nTotal : %12.2fk", total / 1024));
}
/* ---------------------------------------------------------------------------
* do_list: List information stored in internal structures.
*/
#define LIST_ATTRIBUTES 1
#define LIST_COMMANDS 2
#define LIST_COSTS 3
#define LIST_FLAGS 4
#define LIST_FUNCTIONS 5
#define LIST_GLOBALS 6
#define LIST_ALLOCATOR 7
#define LIST_LOGGING 8
#define LIST_DF_FLAGS 9
#define LIST_PERMS 10
#define LIST_ATTRPERMS 11
#define LIST_OPTIONS 12
#define LIST_HASHSTATS 13
#define LIST_BUFTRACE 14
#define LIST_CONF_PERMS 15
#define LIST_SITEINFO 16
#define LIST_POWERS 17
#define LIST_SWITCHES 18
#define LIST_VATTRS 19
#define LIST_DB_STATS 20 /* GAC 4/6/92 */
#define LIST_PROCESS 21
#define LIST_BADNAMES 22
#define LIST_CACHEOBJS 23
#define LIST_TEXTFILES 24
#define LIST_PARAMS 25
#define LIST_CF_RPERMS 26
#define LIST_ATTRTYPES 27
#define LIST_FUNCPERMS 28
#define LIST_MEMORY 29
#define LIST_CACHEATTRS 30
#define LIST_RAWMEM 31
/* *INDENT-OFF* */
NAMETAB list_names[] = {
{(char *)"allocations", 2, CA_WIZARD, LIST_ALLOCATOR},
{(char *)"attr_permissions", 6, CA_WIZARD, LIST_ATTRPERMS},
{(char *)"attr_types", 6, CA_PUBLIC, LIST_ATTRTYPES},
{(char *)"attributes", 2, CA_PUBLIC, LIST_ATTRIBUTES},
{(char *)"bad_names", 2, CA_WIZARD, LIST_BADNAMES},
{(char *)"buffers", 2, CA_WIZARD, LIST_BUFTRACE},
{(char *)"cache", 2, CA_WIZARD, LIST_CACHEOBJS},
{(char *)"cache_attrs", 6, CA_WIZARD, LIST_CACHEATTRS},
{(char *)"commands", 3, CA_PUBLIC, LIST_COMMANDS},
{(char *)"config_permissions", 8, CA_GOD, LIST_CONF_PERMS},
{(char *)"config_read_perms", 4, CA_PUBLIC, LIST_CF_RPERMS},
{(char *)"costs", 3, CA_PUBLIC, LIST_COSTS},
{(char *)"db_stats", 2, CA_WIZARD, LIST_DB_STATS},
{(char *)"default_flags", 1, CA_PUBLIC, LIST_DF_FLAGS},
{(char *)"flags", 2, CA_PUBLIC, LIST_FLAGS},
{(char *)"func_permissions", 5, CA_WIZARD, LIST_FUNCPERMS},
{(char *)"functions", 2, CA_PUBLIC, LIST_FUNCTIONS},
{(char *)"globals", 1, CA_WIZARD, LIST_GLOBALS},
{(char *)"hashstats", 1, CA_WIZARD, LIST_HASHSTATS},
{(char *)"logging", 1, CA_GOD, LIST_LOGGING},
{(char *)"memory", 1, CA_WIZARD, LIST_MEMORY},
{(char *)"options", 1, CA_PUBLIC, LIST_OPTIONS},
{(char *)"params", 2, CA_PUBLIC, LIST_PARAMS},
{(char *)"permissions", 2, CA_WIZARD, LIST_PERMS},
{(char *)"powers", 2, CA_WIZARD, LIST_POWERS},
{(char *)"process", 2, CA_WIZARD, LIST_PROCESS},
{(char *)"raw_memory", 1, CA_WIZARD, LIST_RAWMEM},
{(char *)"site_information", 2, CA_WIZARD, LIST_SITEINFO},
{(char *)"switches", 2, CA_PUBLIC, LIST_SWITCHES},
{(char *)"textfiles", 1, CA_WIZARD, LIST_TEXTFILES},
{(char *)"user_attributes", 1, CA_WIZARD, LIST_VATTRS},
{ NULL, 0, 0, 0}};
/* *INDENT-ON* */
extern NAMETAB enable_names[];
extern NAMETAB logoptions_nametab[];
extern NAMETAB logdata_nametab[];
void do_list(player, cause, extra, arg)
dbref player, cause;
int extra;
char *arg;
{
int flagvalue;
flagvalue = search_nametab(player, list_names, arg);
switch (flagvalue) {
case LIST_ALLOCATOR:
list_bufstats(player);
break;
case LIST_BUFTRACE:
list_buftrace(player);
break;
case LIST_ATTRIBUTES:
list_attrtable(player);
break;
case LIST_COMMANDS:
list_cmdtable(player);
break;
case LIST_SWITCHES:
list_cmdswitches(player);
break;
case LIST_COSTS:
list_costs(player);
break;
case LIST_OPTIONS:
list_options(player);
break;
case LIST_HASHSTATS:
list_hashstats(player);
break;
case LIST_SITEINFO:
list_siteinfo(player);
break;
case LIST_FLAGS:
display_flagtab(player);
break;
case LIST_FUNCPERMS:
list_funcaccess(player);
break;
case LIST_FUNCTIONS:
list_functable(player);
break;
case LIST_GLOBALS:
interp_nametab(player, enable_names, mudconf.control_flags,
(char *)"Global parameters:", (char *)"enabled",
(char *)"disabled");
break;
case LIST_DF_FLAGS:
list_df_flags(player);
break;
case LIST_PERMS:
list_cmdaccess(player);
break;
case LIST_CONF_PERMS:
list_cf_access(player);
break;
case LIST_CF_RPERMS:
list_cf_read_access(player);
break;
case LIST_POWERS:
display_powertab(player);
break;
case LIST_ATTRPERMS:
list_attraccess(player);
break;
case LIST_VATTRS:
list_vattrs(player);
break;
case LIST_LOGGING:
interp_nametab(player, logoptions_nametab, mudconf.log_options,
(char *)"Events Logged:", (char *)"enabled",
(char *)"disabled");
interp_nametab(player, logdata_nametab, mudconf.log_info,
(char *)"Information Logged:", (char *)"yes",
(char *)"no");
break;
case LIST_DB_STATS:
list_db_stats(player);
break;
case LIST_PROCESS:
list_process(player);
break;
case LIST_BADNAMES:
badname_list(player, "Disallowed names:");
break;
case LIST_CACHEOBJS:
list_cached_objs(player);
break;
case LIST_TEXTFILES:
list_textfiles(player);
break;
case LIST_PARAMS:
list_params(player);
break;
case LIST_ATTRTYPES:
list_attrtypes(player);
break;
case LIST_MEMORY:
list_memory(player);
break;
case LIST_CACHEATTRS:
list_cached_attrs(player);
break;
case LIST_RAWMEM:
list_rawmemory(player);
break;
default:
display_nametab(player, list_names,
(char *)"Unknown option. Use one of:", 1);
}
}