/* predicates.c */
/* $Id: predicates.c,v 1.131 2004/10/09 16:41:14 tyrspace 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,
(char *) "-t", mudconf.txthome,
(char *) "-b", mudconf.binhome,
(char *) "-d", mudconf.dbhome,
(char *) "-g", mudconf.gdbm,
(char *) "-k", mudconf.crashdb,
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);
}