/* predicates.c */ /* $Id: predicates.c,v 1.130 2004/02/23 04:35:14 rmg Exp $ */ #include "copyright.h" #include "autoconf.h" #include "config.h" #include <signal.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 "match.h" /* required by code */ #include "command.h" /* required by code */ #include "attrs.h" /* required by code */ #include "powers.h" /* required by code */ #include "ansi.h" /* required by code */ #include "functions.h" /* required by code */ #include "db_sql.h" /* required by code */ extern int FDECL(do_command, (DESC *, char *, int)); extern void NDECL(dump_database); extern LOGFILETAB logfds_table[]; extern volatile int slave_pid; extern volatile int slave_socket; extern void FDECL(load_quota, (int *, dbref, int)); extern void FDECL(save_quota, (int *, dbref, int)); extern int FDECL(get_gender, (dbref)); static int FDECL(type_quota, (int)); static int FDECL(pay_quota, (dbref, int, int)); extern INLINE void FDECL(queue_rawstring, (DESC *, const char *)); #ifdef HAVE_VSNPRINTF static char tprintf_buff[LBUF_SIZE]; #else static char tprintf_buff[20000]; #endif #if defined(__STDC__) && defined(STDC_HEADERS) char *tprintf(const char *format,...) #else char *tprintf(va_alist) va_dcl #endif { va_list ap; #if defined(__STDC__) && defined(STDC_HEADERS) va_start(ap, format); #else char *format; va_start(ap); format = va_arg(ap, char *); #endif #ifdef HAVE_VSNPRINTF vsnprintf(tprintf_buff, LBUF_SIZE, format, ap); #else vsprintf(tprintf_buff, format, ap); #endif va_end(ap); tprintf_buff[LBUF_SIZE - 1] = '\0'; return tprintf_buff; } char *tvprintf(format, ap) const char *format; va_list ap; { #ifdef HAVE_VSNPRINTF vsnprintf(tprintf_buff, LBUF_SIZE, format, ap); #else vsprintf(tprintf_buff, format, ap); #endif tprintf_buff[LBUF_SIZE - 1] = '\0'; return tprintf_buff; } #if defined(__STDC__) && defined(STDC_HEADERS) void safe_tprintf_str(char *str, char **bp, const char *format,...) #else void safe_tprintf_str(va_alist) va_dcl #endif { #ifdef HAVE_VSNPRINTF int len, n; #else static char buff[20000]; #endif va_list ap; #if defined(__STDC__) && defined(STDC_HEADERS) va_start(ap, format); #else char *str; char **bp; char *format; va_start(ap); str = va_arg(ap, char *); bp = va_arg(ap, char **); format = va_arg(ap, char *); #endif /* Sigh, don't we wish _all_ vsprintf's returned int... */ #ifdef HAVE_VSNPRINTF n = LBUF_SIZE - (*bp - str); if (n <= 0) { **bp = '\0'; return; } vsnprintf(*bp, n, format, ap); va_end(ap); len = strlen(*bp); n = ((len < n) ? len : n); *bp += n; **bp = '\0'; #else vsprintf(buff, format, ap); va_end(ap); buff[LBUF_SIZE - 1] = '\0'; safe_str(buff, str, bp); **bp = '\0'; #endif } /* --------------------------------------------------------------------------- * insert_first, remove_first: Insert or remove objects from lists. */ dbref insert_first(head, thing) dbref head, thing; { s_Next(thing, head); return thing; } dbref remove_first(head, thing) dbref head, thing; { dbref prev; if (head == thing) return (Next(thing)); DOLIST(prev, head) { if (Next(prev) == thing) { s_Next(prev, Next(thing)); return head; } } return head; } /* --------------------------------------------------------------------------- * reverse_list: Reverse the order of members in a list. */ dbref reverse_list(list) dbref list; { dbref newlist, rest; newlist = NOTHING; while (list != NOTHING) { rest = Next(list); s_Next(list, newlist); newlist = list; list = rest; } return newlist; } /* --------------------------------------------------------------------------- * member - indicate if thing is in list */ int member(thing, list) dbref thing, list; { DOLIST(list, list) { if (list == thing) return 1; } return 0; } /* --------------------------------------------------------------------------- * is_integer, is_number: see if string contains just a number. */ int is_integer(str) char *str; { while (*str && isspace(*str)) str++; /* Leading spaces */ if ((*str == '-') || (*str == '+')) { /* Leading minus or plus */ str++; if (!*str) return 0; /* but not if just a minus or plus*/ } if (!isdigit(*str)) /* Need at least 1 integer */ return 0; while (*str && isdigit(*str)) str++; /* The number (int) */ while (*str && isspace(*str)) str++; /* Trailing spaces */ return (*str ? 0 : 1); } int is_number(str) char *str; { int got_one; while (*str && isspace(*str)) str++; /* Leading spaces */ if ((*str == '-') || (*str == '+')) { /* Leading minus or plus */ str++; if (!*str) return 0; /* but not if just a minus or plus */ } got_one = 0; if (isdigit(*str)) got_one = 1; /* Need at least one digit */ while (*str && isdigit(*str)) str++; /* The number (int) */ if (*str == '.') str++; /* decimal point */ if (isdigit(*str)) got_one = 1; /* Need at least one digit */ while (*str && isdigit(*str)) str++; /* The number (fract) */ while (*str && isspace(*str)) str++; /* Trailing spaces */ return ((*str || !got_one) ? 0 : 1); } int could_doit(player, thing, locknum) dbref player, thing; int locknum; { char *key; dbref aowner; int aflags, alen, doit; /* no if nonplayer tries to get key */ if (!isPlayer(player) && Key(thing)) { return 0; } if (Pass_Locks(player)) return 1; key = atr_get(thing, locknum, &aowner, &aflags, &alen); doit = eval_boolexp_atr(player, thing, thing, key); free_lbuf(key); return doit; } static int canpayquota(player, who, cost, objtype) dbref player, who; int cost, objtype; { register int quota; int q_list[5]; /* If no cost, succeed */ if (cost <= 0) return 1; /* determine basic quota */ load_quota(q_list, Owner(who), A_RQUOTA); quota = q_list[QTYPE_ALL]; /* enough to build? Wizards always have enough. */ quota -= cost; if ((quota < 0) && !Free_Quota(who) && !Free_Quota(Owner(who))) return 0; if (mudconf.typed_quotas) { quota = q_list[type_quota(objtype)]; if ((quota <= 0) && !Free_Quota(player) && !Free_Quota(Owner(player))) return 0; } return 1; } static int pay_quota(who, cost, objtype) dbref who; int cost, objtype; { /* If no cost, succeed. Negative costs /must/ be managed, however */ if (cost == 0) return 1; add_quota(who, -cost, type_quota(objtype)); return 1; } int canpayfees(player, who, pennies, quota, objtype) dbref player, who; int pennies, quota, objtype; { if (!Wizard(who) && !Wizard(Owner(who)) && !Free_Money(who) && !Free_Money(Owner(who)) && (Pennies(Owner(who)) < pennies)) { if (player == who) { notify(player, tprintf("Sorry, you don't have enough %s.", mudconf.many_coins)); } else { notify(player, tprintf("Sorry, that player doesn't have enough %s.", mudconf.many_coins)); } return 0; } if (mudconf.quotas) { if (!canpayquota(player, who, quota, objtype)) { if (player == who) { notify(player, "Sorry, your building contract has run out."); } else { notify(player, "Sorry, that player's building contract has run out."); } return 0; } } return 1; } static int type_quota(objtype) int objtype; { int qtype; /* determine typed quota */ switch (objtype) { case TYPE_ROOM: qtype = QTYPE_ROOM; break; case TYPE_EXIT: qtype = QTYPE_EXIT; break; case TYPE_PLAYER: qtype = QTYPE_PLAYER; break; default: qtype = QTYPE_THING; } return (qtype); } int payfor(who, cost) dbref who; int cost; { dbref tmp; if (Wizard(who) || Wizard(Owner(who)) || Free_Money(who) || Free_Money(Owner(who)) || Immortal(who) || Immortal(Owner(who))) { return 1; } who = Owner(who); if ((tmp = Pennies(who)) >= cost) { s_Pennies(who, tmp - cost); return 1; } return 0; } int payfees(who, pennies, quota, objtype) dbref who; int pennies, quota, objtype; { /* You /must/ have called canpayfees() first. If not, your * database will be eaten by rabid squirrels. */ if (mudconf.quotas) pay_quota(who, quota, objtype); return payfor(who, pennies); } void add_quota(who, payment, type) dbref who; int payment, type; { int q_list[5]; load_quota(q_list, Owner(who), A_RQUOTA); q_list[QTYPE_ALL] += payment; if (mudconf.typed_quotas) q_list[type] += payment; save_quota(q_list, Owner(who), A_RQUOTA); } void giveto(who, pennies) dbref who; int pennies; { if (Wizard(who) || Wizard(Owner(who)) || Free_Money(who) || Free_Money(Owner(who)) || Immortal(who) || Immortal(Owner(who))) { return; } who = Owner(who); s_Pennies(who, Pennies(who) + pennies); } int ok_name(name) const char *name; { const char *cp; char *purename = strip_ansi(name); /* Disallow pure ANSI names */ if (strlen(purename) == 0) return 0; /* Disallow leading spaces */ if (isspace(*purename)) return 0; /* Only printable characters outside of escape codes */ for (cp = purename; *cp; ++cp) { if (!isprint(*cp)) return 0; } /* Disallow trailing spaces */ cp--; if (isspace(*cp)) return 0; /* Exclude names that start with or contain certain magic cookies */ return (*purename != LOOKUP_TOKEN && *purename != NUMBER_TOKEN && *purename != NOT_TOKEN && !strchr(name, ARG_DELIMITER) && !strchr(name, AND_TOKEN) && !strchr(name, OR_TOKEN) && string_compare(purename, "me") && string_compare(purename, "home") && string_compare(purename, "here")); } int ok_player_name(name) const char *name; { const char *cp, *good_chars; /* Not too long and a good name for a thing */ if (!ok_name(name) || (strlen(name) >= PLAYER_NAME_LIMIT)) return 0; if (mudconf.name_spaces || mudstate.standalone) good_chars = " `$_-.,'"; else good_chars = "`$_-.,'"; /* Make sure name only contains legal characters */ for (cp = name; cp && *cp; cp++) { if (isalnum(*cp)) continue; if (!strchr(good_chars, *cp)) return 0; } return 1; } int ok_attr_name(attrname) const char *attrname; { const char *scan; if (!isalpha(*attrname) && (*attrname != '_')) return 0; for (scan = attrname; *scan; scan++) { if (isalnum(*scan)) continue; if (!strchr("'?!`/-_.@#$^&~=+<>()%", *scan)) return 0; } return 1; } int ok_password(password, player) const char *password; dbref player; { const char *scan; int num_upper = 0; int num_special = 0; int num_lower = 0; if (*password == '\0') { if (!mudstate.standalone) notify_quiet(player, "Null passwords are not allowed."); return 0; } for (scan = password; *scan; scan++) { if (!(isprint(*scan) && !isspace(*scan))) { if (!mudstate.standalone) notify_quiet(player, "Illegal character in password."); return 0; } if (isupper(*scan)) num_upper++; else if (islower(*scan)) num_lower++; else if ((*scan != '\'') && (*scan != '-')) num_special++; } /* Needed. Change it if you like, but be sure yours is the same. */ if ((strlen(password) == 13) && (password[0] == 'X') && (password[1] == 'X')) { if (!mudstate.standalone) notify_quiet(player, "Please choose another password."); return 0; } if (!mudstate.standalone && mudconf.safer_passwords) { if (num_upper < 1) { notify_quiet(player, "The password must contain at least one capital letter."); return 0; } if (num_lower < 1) { notify_quiet(player, "The password must contain at least one lowercase letter."); return 0; } if (num_special < 1) { notify_quiet(player, "The password must contain at least one number or a symbol other than the apostrophe or dash."); return 0; } } return 1; } /* --------------------------------------------------------------------------- * handle_ears: Generate the 'grows ears' and 'loses ears' messages. */ void handle_ears(thing, could_hear, can_hear) dbref thing; int could_hear, can_hear; { char *buff, *bp; int gender; if (!could_hear && can_hear) { bp = buff = alloc_lbuf("handle_ears.grow"); if (isExit(thing)) safe_exit_name(thing, buff, &bp); else safe_name(thing, buff, &bp); gender = get_gender(thing); notify_check(thing, thing, tprintf("%s %s now listening.", buff, (gender == 4) ? "are" : "is"), (MSG_ME | MSG_NBR | MSG_LOC | MSG_INV)); free_lbuf(buff); } else if (could_hear && !can_hear) { bp = buff = alloc_lbuf("handle_ears.lose"); if (isExit(thing)) safe_exit_name(thing, buff, &bp); else safe_name(thing, buff, &bp); gender = get_gender(thing); notify_check(thing, thing, tprintf("%s %s no longer listening.", buff, (gender == 4) ? "are" : "is"), (MSG_ME | MSG_NBR | MSG_LOC | MSG_INV)); free_lbuf(buff); } } /* for lack of better place the @switch code is here */ void do_switch(player, cause, key, expr, args, nargs, cargs, ncargs) dbref player, cause; int key, nargs, ncargs; char *expr, *args[], *cargs[]; { int a, any, now; char *buff, *tbuf, *bp, *str; if (!expr || (nargs <= 0)) return; now = key & SWITCH_NOW; key &= ~SWITCH_NOW; if (key == SWITCH_DEFAULT) { if (mudconf.switch_df_all) key = SWITCH_ANY; else key = SWITCH_ONE; } /* now try a wild card match of buff with stuff in args */ any = 0; buff = bp = alloc_lbuf("do_switch"); for (a = 0; (a < (nargs - 1)) && args[a] && args[a + 1]; a += 2) { bp = buff; str = args[a]; exec(buff, &bp, player, cause, cause, EV_FCHECK | EV_EVAL | EV_TOP, &str, cargs, ncargs); if (wild_match(buff, expr)) { tbuf = replace_string(SWITCH_VAR, expr, args[a+1]); if (now) { process_cmdline(player, cause, tbuf, cargs, ncargs, NULL); } else { wait_que(player, cause, 0, NOTHING, 0, tbuf, cargs, ncargs, mudstate.rdata); } free_lbuf(tbuf); if (key == SWITCH_ONE) { free_lbuf(buff); return; } any = 1; } } free_lbuf(buff); if ((a < nargs) && !any && args[a]) { tbuf = replace_string(SWITCH_VAR, expr, args[a]); if (now) { process_cmdline(player, cause, tbuf, cargs, ncargs, NULL); } else { wait_que(player, cause, 0, NOTHING, 0, tbuf, cargs, ncargs, mudstate.rdata); } free_lbuf(tbuf); } } /* --------------------------------------------------------------------------- * Command hooks. */ void do_hook(player, cause, key, cmdname, target) dbref player, cause; int key; char *cmdname, *target; { CMDENT *cmdp; char *p; ATTR *ap; HOOKENT *hp; dbref thing, aowner; int atr, aflags; for (p = cmdname; p && *p; p++) *p = tolower(*p); if (!cmdname || ((cmdp = (CMDENT *) hashfind(cmdname, &mudstate.command_htab)) == NULL) || (cmdp->callseq & CS_ADDED)) { notify(player, "That is not a valid built-in command."); return; } if (key == 0) { /* List hooks only */ if (cmdp->pre_hook) { ap = atr_num(cmdp->pre_hook->atr); if (!ap) { notify(player, "Before Hook contains bad attribute number."); } else { notify(player, tprintf("Before Hook: #%d/%s", cmdp->pre_hook->thing, ap->name)); } } else { notify(player, "Before Hook: none"); } if (cmdp->post_hook) { ap = atr_num(cmdp->post_hook->atr); if (!ap) { notify(player, "After Hook contains bad attribute number."); } else { notify(player, tprintf("After Hook: #%d/%s", cmdp->post_hook->thing, ap->name)); } } else { notify(player, "After Hook: none"); } if (cmdp->userperms) { ap = atr_num(cmdp->userperms->atr); if (!ap) { notify(player, "User Permissions contains bad attribute number."); } else { notify(player, tprintf("User Permissions: #%d/%s", cmdp->userperms->thing, ap->name)); } } else { notify(player, "User Permissions: none"); } return; } /* Check for the hook flags. */ if (key & HOOK_PRESERVE) { cmdp->callseq |= CS_PRESERVE; notify(player, "Hooks will preserve the state of the global registers."); return; } if (key & HOOK_NOPRESERVE) { cmdp->callseq &= ~CS_PRESERVE; notify(player, "Hooks will not preserve the state of the global registers."); return; } /* If we didn't get a target, this is a hook deletion. */ if (!target || !*target) { if (key & HOOK_BEFORE) { if (cmdp->pre_hook) { XFREE(cmdp->pre_hook, "do_hook"); cmdp->pre_hook = NULL; } notify(player, "Hook removed."); } else if (key & HOOK_AFTER) { if (cmdp->post_hook) { XFREE(cmdp->post_hook, "do_hook"); cmdp->post_hook = NULL; } notify(player, "Hook removed."); } else if (key & HOOK_PERMIT) { if (cmdp->userperms) { XFREE(cmdp->userperms, "do_hook"); cmdp->userperms = NULL; } notify(player, "User-defined permissions removed."); } else { notify(player, "Unknown command switch."); } return; } /* Find target object and attribute. Make sure it can be read, and * that we control the object. */ if (!parse_attrib(player, target, &thing, &atr, 0)) { notify(player, NOMATCH_MESSAGE); return; } if (!Controls(player, thing)) { notify(player, NOPERM_MESSAGE); return; } if (atr == NOTHING) { notify(player, "No such attribute."); return; } ap = atr_num(atr); if (!ap) { notify(player, "No such attribute."); return; } atr_get_info(thing, atr, &aowner, &aflags); if (!See_attr(player, thing, ap, aowner, aflags)) { notify(player, NOPERM_MESSAGE); return; } /* All right, we have what we need. Go allocate a hook. */ hp = (HOOKENT *) XMALLOC(sizeof(HOOKENT), "do_hook"); hp->thing = thing; hp->atr = atr; /* If that kind of hook already existed, get rid of it. Put in the * new one. */ if (key & HOOK_BEFORE) { if (cmdp->pre_hook) { XFREE(cmdp->pre_hook, "do_hook"); } cmdp->pre_hook = hp; notify(player, "Hook added."); } else if (key & HOOK_AFTER) { if (cmdp->post_hook) { XFREE(cmdp->post_hook, "do_hook"); } cmdp->post_hook = hp; notify(player, "Hook added."); } else if (key & HOOK_PERMIT) { if (cmdp->userperms) { XFREE(cmdp->userperms, "do_hook"); } cmdp->userperms = hp; notify(player, "User-defined permissions will now be checked."); } else { XFREE(hp, "do_hook"); notify(player, "Unknown command switch."); } } /* --------------------------------------------------------------------------- * Command overriding and friends. */ void do_addcommand(player, cause, key, name, command) dbref player, cause; int key; char *name, *command; { CMDENT *old, *cmd; ADDENT *add, *nextp; dbref thing; int atr; char *s; /* Sanity-check the command name and make it case-insensitive. */ if (!*name || (name[0] == '_' && name[1] == '_')) { notify(player, "That is not a valid command name."); return; } for (s = name; *s; s++) { if (isspace(*s) || (*s == ESC_CHAR)) { notify(player, "That is not a valid command name."); return; } *s = tolower(*s); } if (!parse_attrib(player, command, &thing, &atr, 0) || (atr == NOTHING)) { notify(player, "No such attribute."); return; } old = (CMDENT *)hashfind(name, &mudstate.command_htab); if (old && (old->callseq & CS_ADDED)) { /* If it's already found in the hash table, and it's being added using the same object and attribute... */ for (nextp = (ADDENT *)old->info.added; nextp != NULL; nextp = nextp->next) { if ((nextp->thing == thing) && (nextp->atr == atr)) { notify(player, tprintf("%s already added.", name)); return; } } /* else tack it on to the existing entry... */ add = (ADDENT *)XMALLOC(sizeof(ADDENT), "addcommand.add"); add->thing = thing; add->atr = atr; add->name = XSTRDUP(name, "addcommand.addname"); add->next = (ADDENT *)old->info.added; if (key & ADDCMD_PRESERVE) old->callseq |= CS_ACTOR; else old->callseq &= ~CS_ACTOR; old->info.added = add; } else { if (old) { /* Delete the old built-in */ hashdelete(name, &mudstate.command_htab); } cmd = (CMDENT *) XMALLOC(sizeof(CMDENT), "addcommand.cmd"); cmd->cmdname = XSTRDUP(name, "addcommand.cmdname"); cmd->switches = NULL; cmd->perms = 0; cmd->extra = 0; cmd->pre_hook = NULL; cmd->post_hook = NULL; cmd->userperms = NULL; cmd->callseq = CS_ADDED | CS_ONE_ARG | ((old && (old->callseq & CS_LEADIN)) ? CS_LEADIN : 0) | ((key & ADDCMD_PRESERVE) ? CS_ACTOR : 0); add = (ADDENT *)XMALLOC(sizeof(ADDENT), "addcommand.add"); add->thing = thing; add->atr = atr; add->name = XSTRDUP(name, "addcommand.addname"); add->next = NULL; cmd->info.added = add; hashadd(cmd->cmdname, (int *)cmd, &mudstate.command_htab, 0); if (old) { /* If this command was the canonical form of the * command (not an alias), point its aliases to * the added command, while keeping the __ alias. */ if (!strcmp(name, old->cmdname)) { hashdelete(tprintf("__%s", old->cmdname), &mudstate.command_htab); hashreplall((int *)old, (int *)cmd, &mudstate.command_htab); hashadd(tprintf("__%s", old->cmdname), (int *)old, &mudstate.command_htab, 0); } } } /* We reset the one letter commands here so you can overload them */ reset_prefix_cmds(); notify(player, tprintf("Command %s added.", name)); } void do_listcommands(player, cause, key, name) dbref player, cause; int key; char *name; { CMDENT *old; ADDENT *nextp; int didit = 0; char *s, *keyname; /* Let's make this case insensitive... */ for (s = name; *s; s++) { *s = tolower(*s); } if (*name) { old = (CMDENT *)hashfind(name, &mudstate.command_htab); if (old && (old->callseq & CS_ADDED)) { if (strcmp(name, old->cmdname)) { notify(player, tprintf("%s: alias for %s", name, old->cmdname)); return; } for (nextp = (ADDENT *)old->info.added; nextp != NULL; nextp = nextp->next) { notify(player, tprintf("%s: #%d/%s", nextp->name, nextp->thing, ((ATTR *)atr_num(nextp->atr))->name)); } } else { notify(player, tprintf("%s not found in command table.", name)); } return; } else { for (keyname = hash_firstkey(&mudstate.command_htab); keyname != NULL; keyname = hash_nextkey(&mudstate.command_htab)) { old = (CMDENT *)hashfind(keyname, &mudstate.command_htab); if (old && (old->callseq & CS_ADDED)) { if (strcmp(keyname, old->cmdname)) { notify(player, tprintf("%s: alias for %s", keyname, old->cmdname)); continue; } for (nextp = (ADDENT *)old->info.added; nextp != NULL; nextp = nextp->next) { notify(player, tprintf("%s: #%d/%s", nextp->name, nextp->thing, ((ATTR *)atr_num(nextp->atr))->name)); didit = 1; } } } } if (!didit) notify(player, "No added commands found in command table."); } void do_delcommand(player, cause, key, name, command) dbref player, cause; int key; char *name, *command; { CMDENT *old, *cmd; ADDENT *prev = NULL, *nextp; dbref thing; int atr; char *s; if (!*name) { notify(player, "Sorry."); return; } if (*command) { if (!parse_attrib(player, command, &thing, &atr, 0) || (atr == NOTHING)) { notify(player, "No such attribute."); return; } } /* Let's make this case insensitive... */ for (s = name; *s; s++) { *s = tolower(*s); } old = (CMDENT *)hashfind(name, &mudstate.command_htab); if (old && (old->callseq & CS_ADDED)) { if (!*command) { for (prev = (ADDENT *)old->info.added; prev != NULL; prev = nextp) { nextp = prev->next; /* Delete it! */ XFREE(prev->name, "delcommand.name"); XFREE(prev, "delcommand.addent"); } hashdelete(name, &mudstate.command_htab); if ((cmd = (CMDENT *)hashfind(tprintf("__%s", old->cmdname), &mudstate.command_htab)) != NULL) { hashadd(cmd->cmdname, (int *)cmd, &mudstate.command_htab, 0); /* in case we deleted by alias */ if (strcmp(name, cmd->cmdname)) { hashadd(name, (int *)cmd, &mudstate.command_htab, HASH_ALIAS); } /* the __ alias may have been temporarily * marked as the original hash entry */ hashdelete(tprintf("__%s", cmd->cmdname), &mudstate.command_htab); hashadd(tprintf("__%s", cmd->cmdname), (int *)cmd, &mudstate.command_htab, HASH_ALIAS); hashreplall((int *)old, (int *)cmd, &mudstate.command_htab); } else { hashdelall((int *)old, &mudstate.command_htab); } XFREE(old->cmdname, "delcommand.cmdname"); XFREE(old, "delcommand.cmdp"); reset_prefix_cmds(); notify(player, "Done."); return; } else { for (nextp = (ADDENT *)old->info.added; nextp != NULL; nextp = nextp->next) { if ((nextp->thing == thing) && (nextp->atr == atr)) { /* Delete it! */ XFREE(nextp->name, "delcommand.name"); if (!prev) { if (!nextp->next) { hashdelete(name, &mudstate.command_htab); if ((cmd = (CMDENT *)hashfind(tprintf("__%s", name), &mudstate.command_htab)) != NULL) { hashadd(cmd->cmdname, (int *)cmd, &mudstate.command_htab, 0); /* in case we deleted by alias */ if (strcmp(name, cmd->cmdname)) { hashadd(name, (int *)cmd, &mudstate.command_htab, HASH_ALIAS); } /* the __ alias may have been temporarily * marked as the original hash entry */ hashdelete(tprintf("__%s", cmd->cmdname), &mudstate.command_htab); hashadd(tprintf("__%s", cmd->cmdname), (int *)cmd, &mudstate.command_htab, HASH_ALIAS); hashreplall((int *)old, (int *)cmd, &mudstate.command_htab); } else { hashdelall((int *)old, &mudstate.command_htab); } XFREE(old->cmdname, "delcommand.cmdname"); XFREE(old, "delcommand.cmdp"); } else { old->info.added = nextp->next; XFREE(nextp, "delcommand.addent"); } } else { prev->next = nextp->next; XFREE(nextp, "delcommand.addent"); } reset_prefix_cmds(); notify(player, "Done."); return; } prev = nextp; } notify(player, "Command not found in command table."); } } else { notify(player, "Command not found in command table."); } } /* @program 'glues' a user's input to a command. Once executed, the first * string input from any of the doer's logged in descriptors will be * substituted in <command> as %0. Commands already queued by the doer * will be processed normally. */ void handle_prog(d, message) DESC *d; char *message; { DESC *all, *dsave; char *cmd; dbref aowner; int aflags, alen; /* Allow the player to pipe a command while in interactive mode. * Use telnet protocol's GOAHEAD command to show prompt */ if (*message == '|') { dsave = d; do_command(d, message + 1, 1); if (dsave == d) { /* We MUST check if we still have a descriptor, and it's * the same one, since we could have piped a LOGOUT or * QUIT! */ /* Use telnet protocol's GOAHEAD command to show prompt, make sure that we haven't been issues an @quitprogram */ if (d->program_data != NULL) { queue_rawstring(d, (char *) "> \377\371"); } return; } } cmd = atr_get(d->player, A_PROGCMD, &aowner, &aflags, &alen); wait_que(d->program_data->wait_cause, d->player, 0, NOTHING, 0, cmd, (char **)&message, 1, d->program_data->wait_data); /* First, set 'all' to a descriptor we find for this player */ all = (DESC *)nhashfind(d->player, &mudstate.desc_htab) ; Free_RegData(all->program_data->wait_data); XFREE(all->program_data, "program_data"); /* Set info for all player descriptors to NULL */ DESC_ITER_PLAYER(d->player, all) all->program_data = NULL; atr_clr(d->player, A_PROGCMD); free_lbuf(cmd); } static int ok_program(player, doer) dbref player; dbref doer; { if ((!(Prog(player) || Prog(Owner(player))) && !Controls(player, doer)) || (God(doer) && !God(player))) { notify(player, NOPERM_MESSAGE); return 0; } if (!isPlayer(doer) || !Good_obj(doer)) { notify(player, "No such player."); return 0; } if (!Connected(doer)) { notify(player, "Sorry, that player is not connected."); return 0; } return 1; } void do_quitprog(player, cause, key, name) dbref player, cause; int key; char *name; { DESC *d; dbref doer; int isprog = 0; if (*name) { doer = match_thing(player, name); } else { doer = player; } if (!ok_program(player, doer)) return; DESC_ITER_PLAYER(doer, d) { if (d->program_data != NULL) { isprog = 1; } } if (!isprog) { notify(player, "Player is not in an @program."); return; } d = (DESC *)nhashfind(doer, &mudstate.desc_htab); Free_RegData(d->program_data->wait_data); XFREE(d->program_data, "program_data"); /* Set info for all player descriptors to NULL */ DESC_ITER_PLAYER(doer, d) d->program_data = NULL; atr_clr(doer, A_PROGCMD); notify(player, "@program cleared."); notify(doer, "Your @program has been terminated."); } void do_prog(player, cause, key, name, command) dbref player, cause; int key; char *name, *command; { DESC *d; PROG *program; int atr, aflags, lev, found; dbref doer, thing, aowner, parent; ATTR *ap; char *attrib, *msg; if (!name || !*name) { notify(player, "No players specified."); return; } doer = match_thing(player, name); if (!ok_program(player, doer)) return; msg = command; attrib = parse_to(&msg, ':', 1); if (msg && *msg) { notify(doer, msg); } parse_attrib(player, attrib, &thing, &atr, 0); if (atr != NOTHING) { if (!atr_pget_info(thing, atr, &aowner, &aflags)) { notify(player, "Attribute not present on object."); return; } ap = atr_num(atr); /* We've got to find this attribute in the object's * parent chain, somewhere. */ found = 0; ITER_PARENTS(thing, parent, lev) { if (atr_get_info(parent, atr, &aowner, &aflags)) { found = 1; break; } } if (!found) { notify(player, "Attribute not present on object."); return; } if (God(player) || (!God(thing) && See_attr(player, thing, ap, aowner, aflags) && (Wizard(player) || (aowner == Owner(player))))) { atr_add_raw(doer, A_PROGCMD, atr_get_raw(parent, atr)); } else { notify(player, NOPERM_MESSAGE); return; } } else { notify(player, "No such attribute."); return; } /* Check to see if the cause already has an @prog input pending */ DESC_ITER_PLAYER(doer, d) { if (d->program_data != NULL) { notify(player, "Input already pending."); return; } } program = (PROG *) XMALLOC(sizeof(PROG), "do_prog"); program->wait_cause = player; if (mudstate.rdata) { Alloc_RegData("do_prog.gdata", mudstate.rdata, program->wait_data); Copy_RegData("do_prog.regs", mudstate.rdata, program->wait_data); } else { program->wait_data = NULL; } /* Now, start waiting. */ DESC_ITER_PLAYER(doer, d) { d->program_data = program; /* Use telnet protocol's GOAHEAD command to show prompt */ queue_rawstring(d, (char *) "> \377\371"); } } /* --------------------------------------------------------------------------- * do_restart: Restarts the game. */ void do_restart(player, cause, key) dbref player, cause; int key; { LOGFILETAB *lp; MODULE *mp; if (mudstate.dumping) { notify(player, "Dumping. Please try again later."); return; } /* Make sure what follows knows we're restarting. No need to clear * this, since this process is going away-- this is also set on * startup when the restart.db is read. */ mudstate.restarting = 1; raw_broadcast(0, "GAME: Restart by %s, please wait.", Name(Owner(player))); STARTLOG(LOG_ALWAYS, "WIZ", "RSTRT") log_printf("Restart by "); log_name(player); ENDLOG; /* Do a dbck first so we don't end up with an inconsistent state. * Otherwise, since we don't write out GOING objects, the initial * dbck at startup won't have valid data to work with in order to * clean things out. */ do_dbck(NOTHING, NOTHING, 0); /* Dump databases, etc. */ dump_database_internal(DUMP_DB_RESTART); SYNC; CLOSE; sql_shutdown(); if (slave_socket != -1) { shutdown(slave_socket, 2); close(slave_socket); slave_socket = -1; } if (slave_pid != 0) { kill(slave_pid, SIGKILL); } for (lp = logfds_table; lp->log_flag; lp++) { if (lp->filename && lp->fileptr) { fclose(lp->fileptr); rename(lp->filename, tprintf("%s.%ld", lp->filename, (long) mudstate.now)); } } if (mainlog_fp != stderr) { fclose(mainlog_fp); rename(mudconf.mudlogname, tprintf("%s.%ld", mudconf.mudlogname, (long) mudstate.now)); } alarm(0); dump_restart_db(); WALK_ALL_MODULES(mp) { lt_dlclose(mp->handle); } execl(mudconf.exec_path, mudconf.exec_path, (char *) "-c", mudconf.config_file, (char *) "-l", mudconf.mudlogname, (char *) "-p", mudconf.pid_file, NULL); } /* --------------------------------------------------------------------------- * do_comment: Implement the @@ (comment) command. Very cpu-intensive :-) * do_eval is similar, except it gets passed on arg. */ void do_comment(player, cause, key) dbref player, cause; int key; { } void do_eval(player, cause, key, str) dbref player, cause; int key; char *str; { } /* --------------------------------------------------------------------------- */ static dbref promote_dflt(old, new) dbref old, new; { switch (new) { case NOPERM: return NOPERM; case AMBIGUOUS: if (old == NOPERM) return old; else return new; } if ((old == NOPERM) || (old == AMBIGUOUS)) return old; return NOTHING; } dbref match_possessed(player, thing, target, dflt, check_enter) dbref player, thing, dflt; char *target; int check_enter; { dbref result, result1; int control; char *buff, *start, *place, *s1, *d1, *temp; /* First, check normally */ if (Good_obj(dflt)) return dflt; /* Didn't find it directly. Recursively do a contents check */ start = target; while (*target) { /* Fail if no ' characters */ place = target; target = strchr(place, '\''); if ((target == NULL) || !*target) return dflt; /* If string started with a ', skip past it */ if (place == target) { target++; continue; } /* If next character is not an s or a space, skip past */ temp = target++; if (!*target) return dflt; if ((*target != 's') && (*target != 'S') && (*target != ' ')) continue; /* If character was not a space make sure the following * character is a space. */ if (*target != ' ') { target++; if (!*target) return dflt; if (*target != ' ') continue; } /* Copy the container name to a new buffer so we can * terminate it. */ buff = alloc_lbuf("match_possessed"); for (s1 = start, d1 = buff; *s1 && (s1 < temp); *d1++ = (*s1++)) ; *d1 = '\0'; /* Look for the container here and in our inventory. Skip * past if we can't find it. */ init_match(thing, buff, NOTYPE); if (player == thing) { match_neighbor(); match_possession(); } else { match_possession(); } result1 = match_result(); free_lbuf(buff); if (!Good_obj(result1)) { dflt = promote_dflt(dflt, result1); continue; } /* If we don't control it and it is either dark or opaque, * skip past. */ control = Controls(player, result1); if ((Dark(result1) || Opaque(result1)) && !control) { dflt = promote_dflt(dflt, NOTHING); continue; } /* Validate object has the ENTER bit set, if requested */ if ((check_enter) && !Enter_ok(result1) && !control) { dflt = promote_dflt(dflt, NOPERM); continue; } /* Look for the object in the container */ init_match(result1, target, NOTYPE); match_possession(); result = match_result(); result = match_possessed(player, result1, target, result, check_enter); if (Good_obj(result)) return result; dflt = promote_dflt(dflt, result); } return dflt; } /* --------------------------------------------------------------------------- * parse_range: break up <what>,<low>,<high> syntax */ void parse_range(name, low_bound, high_bound) char **name; dbref *low_bound, *high_bound; { char *buff1, *buff2; buff1 = *name; if (buff1 && *buff1) *name = parse_to(&buff1, ',', EV_STRIP_TS); if (buff1 && *buff1) { buff2 = parse_to(&buff1, ',', EV_STRIP_TS); if (buff1 && *buff1) { while (*buff1 && isspace(*buff1)) buff1++; if (*buff1 == NUMBER_TOKEN) buff1++; *high_bound = atoi(buff1); if (*high_bound >= mudstate.db_top) *high_bound = mudstate.db_top - 1; } else { *high_bound = mudstate.db_top - 1; } while (*buff2 && isspace(*buff2)) buff2++; if (*buff2 == NUMBER_TOKEN) buff2++; *low_bound = atoi(buff2); if (*low_bound < 0) *low_bound = 0; } else { *low_bound = 0; *high_bound = mudstate.db_top - 1; } } int parse_thing_slash(player, thing, after, it) dbref player, *it; char *thing, **after; { char *str; /* get name up to / */ for (str = thing; *str && (*str != '/'); str++) ; /* If no / in string, return failure */ if (!*str) { *after = NULL; *it = NOTHING; return 0; } *str++ = '\0'; *after = str; /* Look for the object */ init_match(player, thing, NOTYPE); match_everything(MAT_EXIT_PARENTS); *it = match_result(); /* Return status of search */ return (Good_obj(*it)); } extern NAMETAB lock_sw[]; int get_obj_and_lock(player, what, it, attr, errmsg, bufc) dbref player, *it; char *what, *errmsg, **bufc; ATTR **attr; { char *str, *tbuf; int anum; tbuf = alloc_lbuf("get_obj_and_lock"); strcpy(tbuf, what); if (parse_thing_slash(player, tbuf, &str, it)) { /* <obj>/<lock> syntax, use the named lock */ anum = search_nametab(player, lock_sw, str); if (anum == -1) { free_lbuf(tbuf); safe_str("#-1 LOCK NOT FOUND", errmsg, bufc); return 0; } } else { /* Not <obj>/<lock>, do a normal get of the default lock */ *it = match_thing(player, what); if (!Good_obj(*it)) { free_lbuf(tbuf); safe_str("#-1 NOT FOUND", errmsg, bufc); return 0; } anum = A_LOCK; } /* Get the attribute definition, fail if not found */ free_lbuf(tbuf); *attr = atr_num(anum); if (!(*attr)) { safe_str("#-1 LOCK NOT FOUND", errmsg, bufc); return 0; } return 1; } /* --------------------------------------------------------------------------- * where_is: Returns place where obj is linked into a list. * ie. location for players/things, source for exits, NOTHING for rooms. */ dbref where_is(what) dbref what; { dbref loc; if (!Good_obj(what)) return NOTHING; switch (Typeof(what)) { case TYPE_PLAYER: case TYPE_THING: case TYPE_ZONE: loc = Location(what); break; case TYPE_EXIT: loc = Exits(what); break; default: loc = NOTHING; break; } return loc; } /* --------------------------------------------------------------------------- * where_room: Return room containing player, or NOTHING if no room or * recursion exceeded. If player is a room, returns itself. */ dbref where_room(what) dbref what; { int count; for (count = mudconf.ntfy_nest_lim; count > 0; count--) { if (!Good_obj(what)) break; if (isRoom(what)) return what; if (!Has_location(what)) break; what = Location(what); } return NOTHING; } int locatable(player, it, cause) dbref player, it, cause; { dbref loc_it, room_it; int findable_room; /* No sense if trying to locate a bad object */ if (!Good_obj(it)) return 0; loc_it = where_is(it); /* Succeed if we can examine the target, if we are the target, if * we can examine the location, if a wizard caused the lookup, * or if the target caused the lookup. */ if (Examinable(player, it) || Find_Unfindable(player) || (loc_it == player) || ((loc_it != NOTHING) && (Examinable(player, loc_it) || loc_it == where_is(player))) || Wizard(cause) || (it == cause)) return 1; room_it = where_room(it); if (Good_obj(room_it)) findable_room = !Hideout(room_it); else findable_room = 1; /* Succeed if we control the containing room or if the target is * findable and the containing room is not unfindable. */ if (((room_it != NOTHING) && Examinable(player, room_it)) || Find_Unfindable(player) || (Findable(it) && findable_room)) return 1; /* We can't do it. */ return 0; } /* --------------------------------------------------------------------------- * nearby: Check if thing is nearby player (in inventory, in same room, or * IS the room. */ int nearby(player, thing) dbref player, thing; { int thing_loc, player_loc; if (!Good_obj(player) || !Good_obj(thing)) return 0; thing_loc = where_is(thing); if (thing_loc == player) return 1; player_loc = where_is(player); if ((thing_loc == player_loc) || (thing == player_loc)) return 1; return 0; } /* --------------------------------------------------------------------------- * master_attr: Get the evaluated text string of a master attribute. * Note that this returns an lbuf, which must be freed by * the caller. */ char *master_attr(player, thing, what, sargs, nsargs, f_ptr) dbref player, thing; int what; char **sargs; int nsargs; int *f_ptr; { /* If the attribute exists, evaluate it and return pointer to lbuf. * If not, return NULL. * Respect global overrides. * what is assumed to be more than 0. */ char *d, *m, *buff, *bp, *str, *tbuf, *tp, *sp, *list, *bb_p, *lp; int t, aflags, alen, is_ok, lev; dbref aowner, master, parent, obj; ATTR *ap; GDATA *preserve; if (NoDefault(thing)) { master = NOTHING; } else { switch (Typeof(thing)) { case TYPE_ROOM: master = mudconf.room_defobj; break; case TYPE_EXIT: master = mudconf.exit_defobj; break; case TYPE_PLAYER: master = mudconf.player_defobj; break; case TYPE_GARBAGE: return NULL; break; /* NOTREACHED */ default: master = mudconf.thing_defobj; } if (master == thing) master = NOTHING; } m = NULL; d = atr_pget(thing, what, &aowner, &aflags, &alen); if (Good_obj(master)) { ap = atr_num(what); t = (ap && (ap->flags & AF_DEFAULT)) ? 1 : 0; } else { t = 0; } if (t) { m = atr_pget(master, what, &aowner, &aflags, &alen); } if (f_ptr) *f_ptr = aflags; if (!(*d || (t && *m))) { free_lbuf(d); if (m) { free_lbuf(m); } return NULL; } /* Construct any arguments that we're going to pass along on the * stack. */ switch (what) { case A_LEXITS_FMT: list = alloc_lbuf("master_attr.list"); bb_p = lp = list; is_ok = Darkened(player, thing); ITER_PARENTS(thing, parent, lev) { if (!Has_exits(parent)) continue; DOLIST(obj, Exits(parent)) { if (Can_See_Exit(player, obj, is_ok)) { if (lp != bb_p) { safe_chr(' ', list, &lp); } safe_dbref(list, &lp, obj); } } } *lp = '\0'; is_ok = 1; break; case A_LCON_FMT: list = alloc_lbuf("master_attr.list"); bb_p = lp = list; is_ok = Sees_Always(player, thing); DOLIST(obj, Contents(thing)) { if (Can_See(player, obj, is_ok)) { if (lp != bb_p) { safe_chr(' ', list, &lp); } safe_dbref(list, &lp, obj); } } *lp = '\0'; is_ok = 1; break; default: list = NULL; is_ok = nsargs; break; } /* Go do it. */ preserve = save_global_regs("master_attr_save"); buff = bp = alloc_lbuf("master_attr.1"); if (t && *m) { str = m; if (*d) { sp = d; tbuf = tp = alloc_lbuf("master_attr.deval"); exec(tbuf, &tp, thing, player, player, EV_EVAL | EV_FIGNORE | EV_TOP, &sp, ((list == NULL) ? sargs : &list), is_ok); exec(buff, &bp, thing, player, player, EV_EVAL | EV_FIGNORE | EV_TOP, &str, &tbuf, 1); free_lbuf(tbuf); } else { exec(buff, &bp, thing, player, player, EV_EVAL | EV_FIGNORE | EV_TOP, &str, ((list == NULL) ? sargs : &list), is_ok); } } else if (*d) { str = d; exec(buff, &bp, thing, player, player, EV_EVAL | EV_FIGNORE | EV_TOP, &str, ((list == NULL) ? sargs : &list), is_ok); } *bp = '\0'; free_lbuf(d); if (m) { free_lbuf(m); } if (list) { free_lbuf(list); } restore_global_regs("master_attr_restore", preserve); return buff; } /* --------------------------------------------------------------------------- * did_it: Have player do something to/with thing */ void did_it(player, thing, what, def, owhat, odef, awhat, ctrl_flags, args, nargs, msg_key) dbref player, thing; int what, owhat, awhat, ctrl_flags, nargs, msg_key; char *args[]; const char *def, *odef; { GDATA *preserve; char *d, *m, *buff, *act, *charges, *bp, *str; char *tbuf, *tp, *sp; dbref loc, aowner; int t, num, aflags, alen, need_pres; dbref master; ATTR *ap; int retval = 0; /* If we need to call exec() from within this function, we first save * the state of the global registers, in order to avoid munging them * inappropriately. Do note that the restoration to their original * values occurs BEFORE the execution of the @a-attribute. Therefore, * any changing of setq() values done in the @-attribute and @o-attribute * will NOT be passed on. This prevents odd behaviors that result from * odd @verbs and so forth (the idea is to preserve the caller's control * of the global register values). */ need_pres = 0; if (NoDefault(thing)) { master = NOTHING; } else { switch (Typeof(thing)) { case TYPE_ROOM: master = mudconf.room_defobj; break; case TYPE_EXIT: master = mudconf.exit_defobj; break; case TYPE_PLAYER: master = mudconf.player_defobj; break; default: master = mudconf.thing_defobj; } if (master == thing || !Good_obj(master)) master = NOTHING; } /* Module call. Modules can return a negative number, zero, or a * positive number. * Positive: Stop calling modules. Return; do not execute normal did_it(). * Zero: Continue calling modules. Execute normal did_it() if we get * to the end of the modules and nothing has returned non-zero. * Negative: Stop calling modules. Execute normal did_it(). */ CALL_SOME_MODULES(retval, did_it, (player, thing, master, what, def, owhat, def, awhat, ctrl_flags, args, nargs, msg_key)); if (retval > 0) return; /* message to player */ m = NULL; if (what > 0) { /* Check for global attribute format override. If it exists, * use that. The actual attribute text we were provided * will be passed to that as %0. (Note that if a global * override exists, we never use a supplied server default.) * * Otherwise, we just go evaluate what we've got, and * if that's nothing, we go do the default. */ d = atr_pget(thing, what, &aowner, &aflags, &alen); if (Good_obj(master)) { ap = atr_num(what); t = (ap && (ap->flags & AF_DEFAULT)) ? 1 : 0; } else { t = 0; } if (t) { m = atr_pget(master, what, &aowner, &aflags, &alen); } if (*d || (t && *m)) { need_pres = 1; preserve = save_global_regs("did_it_save"); buff = bp = alloc_lbuf("did_it.1"); if (t && *m) { str = m; if (*d) { sp = d; tbuf = tp = alloc_lbuf("did_it.deval"); exec(tbuf, &tp, thing, player, player, EV_EVAL | EV_FIGNORE | EV_TOP, &sp, args, nargs); exec(buff, &bp, thing, player, player, EV_EVAL | EV_FIGNORE | EV_TOP, &str, &tbuf, 1); free_lbuf(tbuf); } else { exec(buff, &bp, thing, player, player, EV_EVAL | EV_FIGNORE | EV_TOP, &str, (char **) NULL, 0); } } else if (*d) { str = d; exec(buff, &bp, thing, player, player, EV_EVAL | EV_FIGNORE | EV_TOP, &str, args, nargs); } *bp = '\0'; #ifdef PUEBLO_SUPPORT if ((aflags & AF_HTML) && Html(player)) { char *buff_cp = buff + strlen(buff); safe_crlf(buff, &buff_cp); notify_html(player, buff); } else notify(player, buff); #else notify(player, buff); #endif /* PUEBLO_SUPPORT */ free_lbuf(buff); } else if (def) { notify(player, def); } free_lbuf(d); } else if ((what < 0) && def) { notify(player, def); } if (m) { free_lbuf(m); } /* message to neighbors */ m = NULL; if ((owhat > 0) && Has_location(player) && Good_obj(loc = Location(player))) { d = atr_pget(thing, owhat, &aowner, &aflags, &alen); if (Good_obj(master)) { ap = atr_num(owhat); t = (ap && (ap->flags & AF_DEFAULT)) ? 1 : 0; } else { t = 0; } if (t) { m = atr_pget(master, owhat, &aowner, &aflags, &alen); } if (*d || (t && *m)) { if (!need_pres) { need_pres = 1; preserve = save_global_regs("did_it_save"); } buff = bp = alloc_lbuf("did_it.2"); if (t && *m) { str = m; if (*d) { sp = d; tbuf = tp = alloc_lbuf("did_it.deval"); exec(tbuf, &tp, thing, player, player, EV_EVAL | EV_FIGNORE | EV_TOP, &sp, args, nargs); exec(buff, &bp, thing, player, player, EV_EVAL | EV_FIGNORE | EV_TOP, &str, &tbuf, 1); free_lbuf(tbuf); } else if (odef) { exec(buff, &bp, thing, player, player, EV_EVAL | EV_FIGNORE | EV_TOP, &str, (char **) &odef, 1); } else { exec(buff, &bp, thing, player, player, EV_EVAL | EV_FIGNORE | EV_TOP, &str, (char **) NULL, 0); } } else if (*d) { str = d; exec(buff, &bp, thing, player, player, EV_EVAL | EV_FIGNORE | EV_TOP, &str, args, nargs); } *bp = '\0'; if (*buff) { if (aflags & AF_NONAME) { notify_except2(loc, player, player, thing, buff, msg_key); } else { notify_except2(loc, player, player, thing, tprintf("%s %s", Name(player), buff), msg_key); } } free_lbuf(buff); } else if (odef) { if (ctrl_flags & VERB_NONAME) { notify_except2(loc, player, player, thing, odef, msg_key); } else { notify_except2(loc, player, player, thing, tprintf("%s %s", Name(player), odef), msg_key); } } free_lbuf(d); } else if ((owhat < 0) && odef && Has_location(player) && Good_obj(loc = Location(player))) { if (ctrl_flags & VERB_NONAME) { notify_except2(loc, player, player, thing, odef, msg_key); } else { notify_except2(loc, player, player, thing, tprintf("%s %s", Name(player), odef), msg_key); } } if (m) { free_lbuf(m); } /* If we preserved the state of the global registers, restore them. */ if (need_pres) restore_global_regs("did_it_restore", preserve); /* do the action attribute */ if (awhat > 0) { if (*(act = atr_pget(thing, awhat, &aowner, &aflags, &alen))) { charges = atr_pget(thing, A_CHARGES, &aowner, &aflags, &alen); if (*charges) { num = atoi(charges); if (num > 0) { buff = alloc_sbuf("did_it.charges"); sprintf(buff, "%d", num - 1); atr_add_raw(thing, A_CHARGES, buff); free_sbuf(buff); } else if (*(buff = atr_pget(thing, A_RUNOUT, &aowner, &aflags, &alen))) { free_lbuf(act); act = buff; } else { free_lbuf(act); free_lbuf(buff); free_lbuf(charges); return; } } free_lbuf(charges); /* Skip any leading $<command>: or ^<monitor>: pattern. * If we run off the end of string without finding an unescaped * ':' (or there's nothing after it), go back to the beginning * of the string and just use that. */ if ((*act == '$') || (*act == '^')) { for (tp = act + 1; *tp && ((*tp != ':') || (*(tp - 1) == '\\')); tp++) ; if (!*tp) { tp = act; } else { tp++; /* must advance past the ':' */ } } else { tp = act; } /* Go do it. */ if (ctrl_flags & (VERB_NOW|TRIG_NOW)) { preserve = save_global_regs("did_it_save2"); process_cmdline(thing, player, tp, args, nargs, NULL); restore_global_regs("did_it_restore2", preserve); } else { wait_que(thing, player, 0, NOTHING, 0, tp, args, nargs, mudstate.rdata); } } free_lbuf(act); } } /* * --------------------------------------------------------------------------- * * do_verb: Command interface to did_it. */ void do_verb(player, cause, key, victim_str, args, nargs) dbref player, cause; int key, nargs; char *victim_str, *args[]; { dbref actor, victim, aowner; int what, owhat, awhat, nxargs, restriction, aflags, i; ATTR *ap; const char *whatd, *owhatd; char *xargs[NUM_ENV_VARS]; /* * Look for the victim */ if (!victim_str || !*victim_str) { notify(player, "Nothing to do."); return; } /* * Get the victim */ init_match(player, victim_str, NOTYPE); match_everything(MAT_EXIT_PARENTS); victim = noisy_match_result(); if (!Good_obj(victim)) return; /* * Get the actor. Default is my cause */ if ((nargs >= 1) && args[0] && *args[0]) { init_match(player, args[0], NOTYPE); match_everything(MAT_EXIT_PARENTS); actor = noisy_match_result(); if (!Good_obj(actor)) return; } else { actor = cause; } /* * Check permissions. There are two possibilities * 1: Player * * * * controls both victim and actor. In this case victim runs * his * * * * * * * action list. * 2: Player controls actor. In this case * * victim * does * not run his * action list and any attributes * * that * player cannot * read from * victim are defaulted. */ if (!controls(player, actor)) { notify_quiet(player, NOPERM_MESSAGE); return; } restriction = !controls(player, victim); what = -1; owhat = -1; awhat = -1; whatd = NULL; owhatd = NULL; nxargs = 0; /* * Get invoker message attribute */ if (nargs >= 2) { ap = atr_str(args[1]); if (ap && (ap->number > 0)) what = ap->number; } /* * Get invoker message default */ if ((nargs >= 3) && args[2] && *args[2]) { whatd = args[2]; } /* * Get others message attribute */ if (nargs >= 4) { ap = atr_str(args[3]); if (ap && (ap->number > 0)) owhat = ap->number; } /* * Get others message default */ if ((nargs >= 5) && args[4] && *args[4]) { owhatd = args[4]; } /* * Get action attribute */ if (nargs >= 6) { ap = atr_str(args[5]); if (ap) awhat = ap->number; } /* * Get arguments */ if (nargs >= 7) { parse_arglist(victim, actor, actor, args[6], '\0', EV_STRIP_LS | EV_STRIP_TS, xargs, NUM_ENV_VARS, (char **)NULL, 0); for (nxargs = 0; (nxargs < NUM_ENV_VARS) && xargs[nxargs]; nxargs++) ; } /* * If player doesn't control both, enforce visibility restrictions. * Regardless of control we still check if the player can read the * attribute, since we don't want him getting wiz-readable-only attrs. */ atr_get_info(victim, what, &aowner, &aflags); if (what != -1) { ap = atr_num(what); if (!ap || !Read_attr(player, victim, ap, aowner, aflags) || (restriction && ((ap->number == A_DESC) && !mudconf.read_rem_desc && !Examinable(player, victim) && !nearby(player, victim)))) what = -1; } atr_get_info(victim, owhat, &aowner, &aflags); if (owhat != -1) { ap = atr_num(owhat); if (!ap || !Read_attr(player, victim, ap, aowner, aflags) || (restriction && ((ap->number == A_DESC) && !mudconf.read_rem_desc && !Examinable(player, victim) && !nearby(player, victim)))) owhat = -1; } if (restriction) awhat = 0; /* * Go do it */ did_it(actor, victim, what, whatd, owhat, owhatd, awhat, key & (VERB_NOW | VERB_NONAME), xargs, nxargs, (((key & VERB_SPEECH) ? MSG_SPEECH : 0) | ((key & VERB_MOVE) ? MSG_MOVE : 0) | ((key & VERB_PRESENT) ? MSG_PRESENCE : 0))); /* * Free user args */ for (i = 0; i < nxargs; i++) free_lbuf(xargs[i]); } void do_sql_connect(player, cause, key) dbref player, cause; int key; { if (sql_init() < 0) { notify(player, "Database connection attempt failed."); } else { notify(player, "Database connection succeeded."); } } /* --------------------------------------------------------------------------- * do_redirect: Redirect PUPPET, TRACE, VERBOSE output to another player. */ void do_redirect(player, cause, key, from_name, to_name) dbref player, cause; int key; char *from_name, *to_name; { dbref from_ref, to_ref; NUMBERTAB *np; /* Find what object we're redirecting from. We must either control it, * or it must be REDIR_OK. */ init_match(player, from_name, NOTYPE); match_everything(0); from_ref = noisy_match_result(); if (!Good_obj(from_ref)) return; /* If we have no second argument, we are un-redirecting something * which is already redirected. We can get rid of it if we control * the object being redirected, or we control the target of the * redirection. */ if (!to_name || !*to_name) { if (!H_Redirect(from_ref)) { notify(player, "That object is not being redirected."); return; } np = (NUMBERTAB *) nhashfind(from_ref, &mudstate.redir_htab); if (np) { /* This should always be true -- if we have the flag the * hashtable lookup should succeed -- but just in case, * we check. (We clear the flag upon startup.) * If we have a weird situation, we don't care whether * or not the control criteria gets met; we just fix it. */ if (!Controls(player, from_ref) && (np->num != player)) { notify(player, NOPERM_MESSAGE); return; } if (np->num != player) { notify(np->num, tprintf("Output from %s(#%d) is no being redirected to you.", Name(from_ref), from_ref)); } XFREE(np, "redir_struct"); nhashdelete(from_ref, &mudstate.redir_htab); } s_Flags3(from_ref, Flags3(from_ref) & ~HAS_REDIRECT); notify(player, "Redirection stopped."); if (from_ref != player) { notify(from_ref, "You are no longer being redirected."); } return; } /* If the object is already being redirected, we cannot do so again. */ if (H_Redirect(from_ref)) { notify(player, "That object is already being redirected."); return; } /* To redirect something, it needs to either be REDIR_OK or we * need to control it. */ if (!Controls(player, from_ref) && !Redir_ok(from_ref)) { notify(player, NOPERM_MESSAGE); return; } /* Find the player that we're redirecting to. We must control the * player. */ to_ref = lookup_player(player, to_name, 1); if (!Good_obj(to_ref)) { notify(player, "No such player."); return; } if (!Controls(player, to_ref)) { notify(player, NOPERM_MESSAGE); return; } /* Insert it into the hashtable. */ np = (NUMBERTAB *) XMALLOC(sizeof(NUMBERTAB), "redir_struct"); np->num = to_ref; nhashadd(from_ref, (int *) np, &mudstate.redir_htab); s_Flags3(from_ref, Flags3(from_ref) | HAS_REDIRECT); if (from_ref != player) { notify(from_ref, tprintf("You have been redirected to %s.", Name(to_ref))); } if (to_ref != player) { notify(to_ref, tprintf("Output from %s(#%d) has been redirected to you.", Name(from_ref), from_ref)); } notify(player, "Redirected."); } /* --------------------------------------------------------------------------- * do_reference: Manipulate nrefs. */ void do_reference(player, cause, key, ref_name, obj_name) dbref player, cause; int key; char *ref_name, *obj_name; { HASHENT *hptr; HASHTAB *htab; int i, len, total, is_global; char tbuf[LBUF_SIZE], outbuf[LBUF_SIZE], *tp, *bp, *buff, *s; dbref target, *np; if (key & NREF_LIST) { htab = &mudstate.nref_htab; if (!ref_name || !*ref_name) { /* Global only. */ is_global = 1; tbuf[0] = '_'; tbuf[1] = '\0'; len = 1; } else { is_global = 0; if (!string_compare(ref_name, "me")) { target = player; } else { target = lookup_player(player, ref_name, 1); if (target == NOTHING) { notify(player, "No such player."); return; } if (!Controls(player, target)) { notify(player, NOPERM_MESSAGE); return; } } tp = tbuf; safe_ltos(tbuf, &tp, player); safe_chr('.', tbuf, &tp); *tp = '\0'; len = strlen(tbuf); } total = 0; for (i = 0; i < htab->hashsize; i++) { for (hptr = htab->entry[i]; hptr != NULL; hptr = hptr->next) { if (!strncmp(tbuf, hptr->target.s, len)) { total++; bp = outbuf; safe_tprintf_str(outbuf, &bp, "%s: ", ((is_global) ? hptr->target.s : strchr(hptr->target.s, '.') + 1)); buff = unparse_object(player, *(hptr->data), 0); safe_str(buff, outbuf, &bp); free_lbuf(buff); if (Owner(player) != Owner(*(hptr->data))) { safe_str((char *) " [owner: ", outbuf, &bp); buff = unparse_object(player, Owner(*(hptr->data)), 0); safe_str(buff, outbuf, &bp); free_lbuf(buff); safe_chr(']', outbuf, &bp); } *bp = '\0'; notify(player, outbuf); } } } notify(player, tprintf("Total references: %d", total)); return; } /* We can only reference objects that we can examine. */ if (obj_name && *obj_name) { target = match_thing(player, obj_name); if (!Good_obj(target)) return; if (!Examinable(player, target)) { notify(player, NOPERM_MESSAGE); return; } } else { target = NOTHING; /* indicates clear */ } /* If the reference name starts with an underscore, it's global. * Only wizards can do that. */ tp = tbuf; if (*ref_name == '_') { if (!Wizard(player)) { notify(player, NOPERM_MESSAGE); return; } } else { safe_ltos(tbuf, &tp, player); safe_chr('.', tbuf, &tp); } for (s = ref_name; *s; s++) safe_chr(tolower(*s), tbuf, &tp); *tp = '\0'; /* Does this reference name exist already? */ np = (int *) hashfind(tbuf, &mudstate.nref_htab); if (np) { if (target == NOTHING) { XFREE(np, "nref"); hashdelete(tbuf, &mudstate.nref_htab); notify(player, "Reference cleared."); } else if (*np == target) { /* Already got it. */ notify(player, "That reference has already been made."); } else { /* Replace it. */ XFREE(np, "nref"); np = (dbref *) XMALLOC(sizeof(dbref), "nref"); *np = target; hashrepl(tbuf, np, &mudstate.nref_htab); notify(player, "Reference updated."); } return; } /* Didn't find it. We've got a new one (or an error if we have no * target but the reference didn't exist). */ if (target == NOTHING) { notify(player, "No such reference to clear."); return; } np = (dbref *) XMALLOC(sizeof(dbref), "nref"); *np = target; hashadd(tbuf, np, &mudstate.nref_htab, 0); notify(player, "Referenced."); } /* --------------------------------------------------------------------------- * Miscellaneous stuff below. */ void do_sql(player, cause, key, name) dbref player, cause; int key; char *name; { sql_query(player, name, NULL, NULL, &SPACE_DELIM, &SPACE_DELIM); }