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