// predicates.cpp
//
// $Id: predicates.cpp,v 1.20 2000/09/17 21:31:20 sdennis Exp $
//
#include "copyright.h"
#include "autoconf.h"
#include "config.h"
#include "externs.h"
#include <signal.h>
#include "mudconf.h"
#include "db.h"
#include "interface.h"
#include "match.h"
#include "command.h"
#include "alloc.h"
#include "attrs.h"
#include "powers.h"
#include "ansi.h"
#include "htab.h"
extern int FDECL(do_command, (DESC *, char *, int));
extern void NDECL(dump_database);
char * DCL_CDECL tprintf(const char *fmt,...)
{
static char buff[LBUF_SIZE];
va_list ap;
va_start(ap, fmt);
Tiny_vsnprintf(buff, LBUF_SIZE, fmt, ap);
va_end(ap);
return buff;
}
void DCL_CDECL safe_tprintf_str(char *str, char **bp, char *fmt,...)
{
va_list ap;
va_start(ap, fmt);
int nAvailable = LBUF_SIZE - (*bp - str);
int len = Tiny_vsnprintf(*bp, nAvailable, fmt, ap);
va_end(ap);
*bp += len;
}
/*
* ---------------------------------------------------------------------------
* * insert_first, remove_first: Insert or remove objects from lists.
*/
dbref insert_first(dbref head, dbref thing)
{
s_Next(thing, head);
return thing;
}
dbref remove_first(dbref head, dbref 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(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(dbref thing, dbref 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(char *str, int *pDigits)
{
int nDigits = 0;
if (pDigits)
{
*pDigits = 0;
}
// Leading spaces.
//
while (Tiny_IsSpace[(unsigned char)*str])
str++;
// Leading minus
//
if (*str == '-')
{
str++;
// Just a minus by itself isn't an integer.
//
if (!*str)
return 0;
}
// Need at least 1 integer
//
if (!Tiny_IsDigit[(unsigned char)*str])
{
return 0;
}
// The number (int)
//
while (Tiny_IsDigit[(unsigned char)*str])
{
nDigits++;
str++;
}
if (pDigits)
{
*pDigits = nDigits;
}
// Trailing Spaces.
//
while (Tiny_IsSpace[(unsigned char)*str])
str++;
return (*str ? 0 : 1);
}
int is_number(char *str)
{
int got_one;
// Leading spaces.
//
while (Tiny_IsSpace[(unsigned char)*str])
str++;
// Leading minus
//
if (*str == '-')
{
str++;
// But not if just a minus
//
if (!*str)
return 0;
}
// Need at least one digit.
//
got_one = 0;
if (Tiny_IsDigit[(unsigned char)*str])
{
got_one = 1;
}
// The number (int)
//
while (Tiny_IsDigit[(unsigned char)*str])
{
str++;
}
// Decimal point.
//
if (*str == '.')
str++;
// Need at least one digit
//
if (Tiny_IsDigit[(unsigned char)*str])
{
got_one = 1;
}
// The number (fract)
//
while (Tiny_IsDigit[(unsigned char)*str])
{
str++;
}
// Trailing spaces.
//
while (Tiny_IsSpace[(unsigned char)*str])
str++;
return ((*str || !got_one) ? 0 : 1);
}
#ifndef STANDALONE
int could_doit(dbref player, dbref thing, int locknum)
{
char *key;
dbref aowner;
int aflags, doit;
/*
* no if nonplayer trys to get key
*/
if (!isPlayer(player) && Key(thing)) {
return 0;
}
if (Pass_Locks(player))
return 1;
key = atr_get(thing, locknum, &aowner, &aflags);
doit = eval_boolexp_atr(player, thing, thing, key);
free_lbuf(key);
return doit;
}
int can_see(dbref player, dbref thing, int can_see_loc)
{
/*
* Don't show if all the following apply: * Sleeping players should *
*
* * * * not be seen. * The thing is a disconnected player. * The
* player * is * * * not a puppet.
*/
if (mudconf.dark_sleepers && isPlayer(thing) &&
!Connected(thing) && !Puppet(thing))
{
return 0;
}
/*
* You don't see yourself or exits
*/
if ((player == thing) || isExit(thing))
{
return 0;
}
/*
* If loc is not dark, you see it if it's not dark or you control it.
* * * * * If loc is dark, you see it if you control it. Seeing your
* * own * * * dark objects is controlled by mudconf.see_own_dark. *
* In * dark * * locations, you also see things that are LIGHT and
* !DARK.
*/
if (can_see_loc)
{
return (!Dark(thing) ||
(mudconf.see_own_dark && MyopicExam(player, thing)));
}
else
{
return ((Light(thing) && !Dark(thing)) ||
(mudconf.see_own_dark && MyopicExam(player, thing)));
}
}
static int pay_quota(dbref who, int cost)
{
dbref aowner;
int quota, aflags;
char buf[20], *quota_str;
/*
* If no cost, succeed
*/
if (cost <= 0)
return 1;
/*
* determine quota
*/
quota = Tiny_atol(quota_str = atr_get(Owner(who), A_RQUOTA, &aowner, &aflags));
free_lbuf(quota_str);
/*
* enough to build? Wizards always have enough.
*/
quota -= cost;
if ((quota < 0) && !Free_Quota(who) && !Free_Quota(Owner(who)))
return 0;
// Dock the quota.
//
Tiny_ltoa(quota, buf);
atr_add_raw(Owner(who), A_RQUOTA, buf);
return 1;
}
int canpayfees(dbref player, dbref who, int pennies, int quota)
{
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 (!pay_quota(who, quota)) {
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;
}
}
payfor(who, pennies);
return 1;
}
int payfor(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;
}
#endif /*
* STANDALONE
*/
void add_quota(dbref who, int payment)
{
dbref aowner;
int aflags;
char buf[20], *quota;
quota = atr_get(who, A_RQUOTA, &aowner, &aflags);
Tiny_ltoa(Tiny_atol(quota) + payment, buf);
free_lbuf(quota);
atr_add_raw(who, A_RQUOTA, buf);
}
void giveto(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);
}
// The following function validates that the object names (which will be
// used for things, exits, and rooms, but not for players) and generates
// a canonical form of that name (with optimized ANSI).
//
char *MakeCanonicalObjectName(const char *pName, int *pnName, BOOL *pbValid)
{
static char Buf[MBUF_SIZE];
*pnName = 0;
*pbValid = FALSE;
if (!pName)
{
return NULL;
}
// Build up what the real name would be. If we pass all the
// checks, this is what we will return as a result.
//
int nVisualWidth;
int nBuf = ANSI_TruncateToField(pName, sizeof(Buf), Buf, MBUF_SIZE, &nVisualWidth, 0);
// Disallow pure ANSI names. There must be at least -something-
// visible.
//
if (nVisualWidth <= 0)
{
return NULL;
}
// Get the stripped version (Visible parts without color info).
//
unsigned int nStripped;
char *pStripped = strip_ansi(Buf, &nStripped);
// Do not allow LOOKUP_TOKEN, NUMBER_TOKEN, NOT_TOKEN, or SPACE
// as the first character, or SPACE as the last character
//
if ( strchr("*!#", *pStripped)
|| Tiny_IsSpace[(unsigned char)pStripped[0]]
|| Tiny_IsSpace[(unsigned char)pStripped[nStripped-1]])
{
return NULL;
}
// Only printable characters besides ARG_DELIMITER, AND_TOKEN,
// and OR_TOKEN are allowed.
//
for (unsigned int i = 0; i < nStripped; i++)
{
if (!Tiny_IsObjectNameCharacter[(unsigned char)pStripped[i]])
{
return NULL;
}
}
// Special names are specifically dis-allowed.
//
if ( (nStripped == 2 && memcmp("me", pStripped, 2) == 0)
|| (nStripped == 4 && ( memcmp("home", pStripped, 4) == 0
|| memcmp("here", pStripped, 4) == 0)))
{
return NULL;
}
*pnName = nBuf;
*pbValid = TRUE;
return Buf;
}
// The following function validates the player name. ANSI is not
// allowed in player names. However, a player name must satisfy
// the requirements of a regular name as well.
//
BOOL ValidatePlayerName(const char *pName)
{
if (!pName)
{
return FALSE;
}
unsigned int nName = strlen(pName);
// Verify that name is not empty, but not too long, either.
//
if (nName <= 0 || PLAYER_NAME_LIMIT <= nName)
{
return FALSE;
}
// Do not allow LOOKUP_TOKEN, NUMBER_TOKEN, NOT_TOKEN, or SPACE
// as the first character, or SPACE as the last character
//
if ( strchr("*!#", *pName)
|| Tiny_IsSpace[(unsigned char)pName[0]]
|| Tiny_IsSpace[(unsigned char)pName[nName-1]])
{
return FALSE;
}
#ifndef STANDALONE
if (mudconf.name_spaces)
{
Tiny_IsPlayerNameCharacter[(unsigned char)' '] = 1;
}
else
{
Tiny_IsPlayerNameCharacter[(unsigned char)' '] = 0;
}
#endif
// Only printable characters besides ARG_DELIMITER, AND_TOKEN,
// and OR_TOKEN are allowed.
//
for (unsigned int i = 0; i < nName; i++)
{
if (!Tiny_IsObjectNameCharacter[(unsigned char)pName[i]])
{
return FALSE;
}
}
// Special names are specifically dis-allowed.
//
if ( (nName == 2 && memcmp("me", pName, 2) == 0)
|| (nName == 4 && ( memcmp("home", pName, 4) == 0
|| memcmp("here", pName, 4) == 0)))
{
return FALSE;
}
return TRUE;
}
int ok_password(const char *password, dbref player)
{
const char *scan;
int num_upper = 0;
int num_special = 0;
int num_lower = 0;
if (*password == '\0')
{
#ifndef STANDALONE
notify_quiet(player, "Null passwords are not allowed.");
#endif
return 0;
}
for (scan = password; *scan; scan++)
{
if ( !Tiny_IsPrint[(unsigned char)*scan]
|| Tiny_IsSpace[(unsigned char)*scan])
{
#ifndef STANDALONE
notify_quiet(player, "Illegal character in password.");
#endif
return 0;
}
if (Tiny_IsUpper[(unsigned char)*scan])
num_upper++;
else if (Tiny_IsLower[(unsigned char)*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'))
{
#ifndef STANDALONE
notify_quiet(player, "Please choose another password.");
#endif
return 0;
}
#ifndef STANDALONE
if (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;
}
}
#endif // STANDALONE
return 1;
}
#ifndef STANDALONE
/*
* ---------------------------------------------------------------------------
* * handle_ears: Generate the 'grows ears' and 'loses ears' messages.
*/
void handle_ears(dbref thing, int could_hear, int can_hear)
{
char *buff, *bp;
int gender;
static const char *poss[5] =
{"", "its", "her", "his", "their"};
if (!could_hear && can_hear) {
buff = alloc_lbuf("handle_ears.grow");
StringCopy(buff, Name(thing));
if (isExit(thing)) {
for (bp = buff; *bp && (*bp != ';'); bp++) ;
*bp = '\0';
}
gender = get_gender(thing);
notify_check(thing, thing,
tprintf("%s grow%s ears and can now hear.",
buff, (gender == 4) ? "" : "s"),
(MSG_ME | MSG_NBR | MSG_LOC | MSG_INV));
free_lbuf(buff);
} else if (could_hear && !can_hear) {
buff = alloc_lbuf("handle_ears.lose");
StringCopy(buff, Name(thing));
if (isExit(thing)) {
for (bp = buff; *bp && (*bp != ';'); bp++) ;
*bp = '\0';
}
gender = get_gender(thing);
notify_check(thing, thing,
tprintf("%s lose%s %s ears and become%s deaf.",
buff, (gender == 4) ? "" : "s",
poss[gender], (gender == 4) ? "" : "s"),
(MSG_ME | MSG_NBR | MSG_LOC | MSG_INV));
free_lbuf(buff);
}
}
/*
* for lack of better place the @switch code is here
*/
void do_switch(dbref player, dbref cause, int key, char *expr, char *args[], int nargs, char *cargs[], int ncargs)
{
int a, any;
char *buff, *bp, *str;
if (!expr || (nargs <= 0))
return;
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 coms
*/
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];
TinyExec(buff, &bp, 0, player, cause, EV_FCHECK | EV_EVAL | EV_TOP, &str, cargs, ncargs);
*bp = '\0';
if (wild_match(buff, expr)) {
wait_que(player, cause, 0, NOTHING, 0, args[a + 1],
cargs, ncargs, mudstate.global_regs);
if (key == SWITCH_ONE) {
free_lbuf(buff);
return;
}
any = 1;
}
}
free_lbuf(buff);
if ((a < nargs) && !any && args[a])
wait_que(player, cause, 0, NOTHING, 0, args[a], cargs, ncargs,
mudstate.global_regs);
}
void do_addcommand(dbref player, dbref cause, int key, char *name, char *command)
{
CMDENT *old, *cmd;
ADDENT *add, *nextp;
dbref thing;
int atr;
if (!*name) {
notify(player, "Sorry.");
return;
}
if (!parse_attrib(player, command, &thing, &atr) || (atr == NOTHING)) {
notify(player, "No such attribute.");
return;
}
// Let's make this case insensitive...
//
_strlwr(name);
old = (CMDENT *)hashfindLEN(name, strlen(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->handler; 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 *)MEMALLOC(sizeof(ADDENT));
ISOUTOFMEMORY(add);
add->thing = thing;
add->atr = atr;
add->name = StringClone(name);
add->next = old->addent;
old->addent = add;
}
else
{
if (old)
{
/* Delete the old built-in and rename it __name */
hashdeleteLEN(name, strlen(name), &mudstate.command_htab);
}
cmd = (CMDENT *)MEMALLOC(sizeof(CMDENT));
ISOUTOFMEMORY(cmd);
cmd->cmdname = StringClone(name);
cmd->switches = NULL;
cmd->perms = 0;
cmd->extra = 0;
if (old && (old->callseq & CS_LEADIN))
{
cmd->callseq = CS_ADDED|CS_ONE_ARG|CS_LEADIN;
}
else
{
cmd->callseq = CS_ADDED|CS_ONE_ARG;
}
add = (ADDENT *)MEMALLOC(sizeof(ADDENT));
ISOUTOFMEMORY(add);
add->thing = thing;
add->atr = atr;
add->name = StringClone(name);
add->next = NULL;
cmd->addent = add;
hashaddLEN(name, strlen(name), (int *)cmd, &mudstate.command_htab);
if (old)
{
// Fix any aliases of this command.
//
hashreplall((int *)old, (int *)cmd, &mudstate.command_htab);
char *p = tprintf("__%s", name);
hashaddLEN(p, strlen(p), (int *)old, &mudstate.command_htab);
}
}
/* We reset the one letter commands here so you can overload them */
set_prefix_cmds();
notify(player, tprintf("%s added.", name));
}
void do_listcommands(dbref player, dbref cause, int key, char *name)
{
CMDENT *old;
ADDENT *nextp;
int didit = 0;
char *keyname;
// Let's make this case insensitive...
//
_strlwr(name);
if (*name)
{
old = (CMDENT *)hashfindLEN(name, strlen(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->handler; 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
{
int nKeyLength;
for (keyname = hash_firstkey(&mudstate.command_htab, &nKeyLength); keyname != NULL;
keyname = hash_nextkey(&mudstate.command_htab, &nKeyLength))
{
old = (CMDENT *)hashfindLEN(keyname, nKeyLength, &mudstate.command_htab);
if (old && (old->callseq & CS_ADDED)) {
for (nextp = (ADDENT *)old->handler; nextp != NULL; nextp = nextp->next) {
if (strcmp(keyname, nextp->name))
continue;
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(dbref player, dbref cause, int key, char *name, char *command)
{
CMDENT *old, *cmd;
ADDENT *prev = NULL, *nextp;
dbref thing = NOTHING;
int atr = NOTHING;
if (!*name)
{
notify(player, "Sorry.");
return;
}
if (*command)
{
if (!parse_attrib(player, command, &thing, &atr) || (atr == NOTHING))
{
notify(player, "No such attribute.");
return;
}
}
// Let's make this case insensitive...
//
_strlwr(name);
old = (CMDENT *)hashfindLEN(name, strlen(name), &mudstate.command_htab);
if (old && (old->callseq & CS_ADDED))
{
char *p__Name = tprintf("__%s", name);
unsigned int n__Name = strlen(p__Name);
unsigned int nName = strlen(name);
if (!*command)
{
for (prev = (ADDENT *)old->handler; prev != NULL; prev = nextp)
{
nextp = prev->next;
/* Delete it! */
MEMFREE(prev->name);
MEMFREE(prev);
}
hashdeleteLEN(name, nName, &mudstate.command_htab);
if ((cmd = (CMDENT *)hashfindLEN(p__Name, n__Name, &mudstate.command_htab)) != NULL)
{
hashdeleteLEN(p__Name, n__Name, &mudstate.command_htab);
hashaddLEN(name, nName, (int *)cmd, &mudstate.command_htab);
hashreplall((int *)old, (int *)cmd, &mudstate.command_htab);
}
MEMFREE(old);
set_prefix_cmds();
notify(player, "Done.");
return;
}
else
{
for (nextp = (ADDENT *)old->handler; nextp != NULL; nextp = nextp->next)
{
if ((nextp->thing == thing) && (nextp->atr == atr))
{
/* Delete it! */
MEMFREE(nextp->name);
if (!prev)
{
if (!nextp->next)
{
hashdeleteLEN(name, nName, &mudstate.command_htab);
if ((cmd = (CMDENT *)hashfindLEN(p__Name, n__Name, &mudstate.command_htab)) != NULL)
{
hashdeleteLEN(p__Name, n__Name, &mudstate.command_htab);
hashaddLEN(name, nName, (int *)cmd, &mudstate.command_htab);
hashreplall((int *)old, (int *)cmd, &mudstate.command_htab);
}
MEMFREE(old);
}
else
{
old->addent = nextp->next;
MEMFREE(nextp);
}
}
else
{
prev->next = nextp->next;
MEMFREE(nextp);
}
set_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.");
}
}
/*
* @prog 'glues' a user's input to a command. Once executed, the first string
* input from any of the doers's logged in descriptors, will go into
* A_PROGMSG, which can be substituted in <command> with %0. Commands already
* queued by the doer will be processed normally.
*/
void handle_prog(DESC *d, char *message)
{
DESC *all;
char *cmd;
dbref aowner;
int aflags, i;
/*
* Allow the player to pipe a command while in interactive mode.
*/
if (*message == '|')
{
do_command(d, message + 1, 1);
// Use telnet protocol's GOAHEAD command to show prompt
//
if (d->program_data != NULL)
{
queue_string(d, tprintf("%s>%s \377\371", ANSI_HILITE, ANSI_NORMAL));
}
return;
}
cmd = atr_get(d->player, A_PROGCMD, &aowner, &aflags);
wait_que(d->program_data->wait_cause, d->player, 0, NOTHING, 0, cmd, (char **)&message,
1, (char **)d->program_data->wait_regs);
/* First, set 'all' to a descriptor we find for this player */
all = (DESC *)hashfindLEN(&(d->player), sizeof(d->player), &mudstate.desc_htab) ;
if (all && all->program_data)
{
for (i = 0; i < MAX_GLOBAL_REGS; i++)
{
if (all->program_data->wait_regs[i])
{
free_lbuf(all->program_data->wait_regs[i]);
all->program_data->wait_regs[i] = NULL;
}
}
MEMFREE(all->program_data);
all->program_data = NULL;
// 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);
}
void do_quitprog(dbref player, dbref cause, int key, char *name)
{
DESC *d;
dbref doer;
int i, isprog = 0;
if (*name) {
doer = match_thing(player, name);
} else {
doer = player;
}
if (!(Prog(player) || Prog(Owner(player))) && (player != doer)) {
notify(player, "Permission denied.");
return;
}
if (!isPlayer(doer) || !Good_obj(doer)) {
notify(player, "That is not a player.");
return;
}
if (!Connected(doer)) {
notify(player, "That player is not connected.");
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 *)hashfindLEN(&doer, sizeof(doer), &mudstate.desc_htab) ;
if (d && d->program_data)
{
for (i = 0; i < MAX_GLOBAL_REGS; i++)
{
if (d->program_data->wait_regs[i])
{
free_lbuf(d->program_data->wait_regs[i]);
d->program_data->wait_regs[i] = NULL;
}
}
MEMFREE(d->program_data);
d->program_data = NULL;
/* 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(dbref player, dbref cause, int key, char *name, char *command)
{
DESC *d;
PROG *program;
int i, atr, aflags;
dbref doer, thing, aowner;
ATTR *ap;
char *attrib, *msg;
if (!name || !*name) {
notify(player, "No players specified.");
return;
}
doer = match_thing(player, name);
if (!(Prog(player) || Prog(Owner(player))) && (player != doer)) {
notify(player, NOPERM_MESSAGE);
return;
}
if (!isPlayer(doer) || !Good_obj(doer)) {
notify(player, "That is not a player.");
return;
}
if (!Connected(doer)) {
notify(player, "That player is not connected.");
return;
}
msg = command;
attrib = parse_to(&msg, ':', 1);
if (msg && *msg)
{
notify(doer, msg);
}
parse_attrib(player, attrib, &thing, &atr);
if (atr != NOTHING)
{
char *pBuffer = atr_get(thing, atr, &aowner, &aflags);
if (*pBuffer)
{
ap = atr_num(atr);
if ( God(player)
|| ( !God(thing)
&& See_attr(player, thing, ap, aowner, aflags)
&& (Wizard(player) || (aowner == Owner(player)))))
{
atr_add_raw(doer, A_PROGCMD, pBuffer);
}
else
{
notify(player, NOPERM_MESSAGE);
free_lbuf(pBuffer);
return;
}
free_lbuf(pBuffer);
}
else
{
notify(player, "Attribute not present on object.");
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 *)MEMALLOC(sizeof(PROG));
ISOUTOFMEMORY(program);
program->wait_cause = player;
for (i = 0; i < MAX_GLOBAL_REGS; i++)
{
program->wait_regs[i] = alloc_lbuf("prog_regs");
memcpy(program->wait_regs[i], mudstate.global_regs[i], mudstate.glob_reg_len[i]+1);
}
// Now, start waiting.
//
DESC_ITER_PLAYER(doer, d)
{
d->program_data = program;
// Use telnet protocol's GOAHEAD command to show prompt.
//
queue_string(d, tprintf("%s>%s \377\371", ANSI_HILITE, ANSI_NORMAL));
}
}
/*
* ---------------------------------------------------------------------------
* * do_restart: Restarts the game.
*/
void do_restart(dbref player, dbref cause, int key)
{
#ifndef WIN32
if (mudstate.dumping)
{
notify(player, "Dumping. Please try again later.");
return;
}
#endif // WIN32
raw_broadcast(0, "Game: Restart by %s, please wait.", Name(Owner(player)));
STARTLOG(LOG_ALWAYS, "WIZ", "RSTRT");
log_text((char *)"Restart by ");
log_name(player);
ENDLOG;
SYNC;
dump_database_internal(DUMP_I_RESTART);
CLOSE;
#ifdef WIN32 // WIN32
WSACleanup();
exit(12345678);
#else // WIN32
extern int slave_pid;
extern SOCKET slave_socket;
shutdown(slave_socket, SD_BOTH);
close(slave_socket);
slave_socket = INVALID_SOCKET;
if (slave_pid > 0)
{
kill(slave_pid, SIGKILL);
}
slave_pid = 0;
dump_restart_db();
#ifdef GAME_DOOFERMUX
execl("bin/netmux", mudconf.mud_name, mudconf.config_file, NULL);
#else
execl("bin/netmux", "netmux", mudconf.config_file, NULL);
#endif // GAME_DOOFERMUX
#endif // !WIN32
}
/*
* ---------------------------------------------------------------------------
* * do_comment: Implement the @@ (comment) command. Very cpu-intensive :-)
*/
void do_comment(dbref player, dbref cause, int key)
{
}
static dbref promote_dflt(dbref old, dbref new0)
{
switch (new0) {
case NOPERM:
return NOPERM;
case AMBIGUOUS:
if (old == NOPERM)
return old;
else
return new0;
}
if ((old == NOPERM) || (old == AMBIGUOUS))
return old;
return NOTHING;
}
dbref match_possessed(dbref player, dbref thing, char *target, dbref dflt, 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 = (char *)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("is_posess");
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(char **name, dbref *low_bound, dbref *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 (Tiny_IsSpace[(unsigned char)*buff1])
buff1++;
if (*buff1 == NUMBER_TOKEN)
buff1++;
*high_bound = Tiny_atol(buff1);
if (*high_bound >= mudstate.db_top)
*high_bound = mudstate.db_top - 1;
}
else
{
*high_bound = mudstate.db_top - 1;
}
while (Tiny_IsSpace[(unsigned char)*buff2])
buff2++;
if (*buff2 == NUMBER_TOKEN)
buff2++;
*low_bound = Tiny_atol(buff2);
if (*low_bound < 0)
*low_bound = 0;
}
else
{
*low_bound = 0;
*high_bound = mudstate.db_top - 1;
}
}
int parse_thing_slash(dbref player, char *thing, char **after, dbref *it)
{
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(dbref player, char *what, dbref *it, ATTR **attr, char *errmsg, char **bufc)
{
char *str, *tbuf;
int anum;
tbuf = alloc_lbuf("get_obj_and_lock");
StringCopy(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 < 0) {
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;
}
#endif /*
* STANDALONE
*/
/*
* ---------------------------------------------------------------------------
* * 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(dbref what)
{
dbref loc;
if (!Good_obj(what))
return NOTHING;
switch (Typeof(what)) {
case TYPE_PLAYER:
case TYPE_THING:
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(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(dbref player, dbref it, dbref 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(dbref player, dbref 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;
}
/*
* ---------------------------------------------------------------------------
* * exit_visible, exit_displayable: Is exit visible?
*/
int exit_visible(dbref exit, dbref player, int key)
{
#if defined(WOD_REALMS) && !defined(STANDALONE)
int iRealmDirective = DoThingToThingVisibility(player, exit, ACTION_IS_STATIONARY);
if (REALM_DO_HIDDEN_FROM_YOU == iRealmDirective)
{
return 0;
}
#endif
// Exam exit's location
//
if (key & VE_LOC_XAM)
return 1;
// Exam exit
//
if (Examinable(player, exit))
return 1;
// Exit is light
//
if (Light(exit))
return 1;
// Dark location or base
//
if (key & (VE_LOC_DARK | VE_BASE_DARK))
return 0;
// Dark exit
//
if (Dark(exit))
return 0;
// Default
//
return 1;
}
// Exit visible to look
//
int exit_displayable(dbref exit, dbref player, int key)
{
// Dark exit
//
if (Dark(exit))
return 0;
#if defined(WOD_REALMS) && !defined(STANDALONE)
int iRealmDirective = DoThingToThingVisibility(player, exit, ACTION_IS_STATIONARY);
if (REALM_DO_HIDDEN_FROM_YOU == iRealmDirective)
{
return 0;
}
#endif
// Light exit
//
if (Light(exit))
return 1;
// Dark location or base.
//
if (key & (VE_LOC_DARK | VE_BASE_DARK))
return 0;
// Default
//
return 1;
}
/*
* ---------------------------------------------------------------------------
* * next_exit: return next exit that is ok to see.
*/
dbref next_exit(dbref player, dbref this0, int exam_here)
{
if (isRoom(this0))
return NOTHING;
if (isExit(this0) && exam_here)
return this0;
while ((this0 != NOTHING) && Dark(this0) && !Light(this0) &&
!Examinable(player, this0))
this0 = Next(this0);
return this0;
}
#ifndef STANDALONE
/*
* ---------------------------------------------------------------------------
* * did_it: Have player do something to/with thing
*/
void did_it(dbref player, dbref thing, int what, const char *def, int owhat, const char *odef, int awhat, char *args[], int nargs)
{
char *d, *buff, *act, *charges, *bp, *str;
dbref loc, aowner;
int num, aflags;
char *preserve[MAX_GLOBAL_REGS];
int preserve_len[MAX_GLOBAL_REGS];
// 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).
//
int need_pres = 0;
// message to player.
//
if (what > 0)
{
d = atr_pget(thing, what, &aowner, &aflags);
if (*d)
{
need_pres = 1;
save_global_regs("did_it_save", preserve, preserve_len);
buff = bp = alloc_lbuf("did_it.1");
str = d;
TinyExec(buff, &bp, 0, thing, player, EV_EVAL | EV_FIGNORE | EV_TOP, &str, args, nargs);
*bp = '\0';
if (what == A_HTDESC) {
safe_str("\r\n", buff, &bp);
*bp = '\0';
notify_html(player, buff);
} else
notify(player, buff);
free_lbuf(buff);
} else if (def) {
notify(player, def);
}
free_lbuf(d);
} else if ((what < 0) && def) {
notify(player, def);
}
/*
* message to neighbors
*/
if ((owhat > 0) && Has_location(player) && Good_obj(loc = Location(player)))
{
d = atr_pget(thing, owhat, &aowner, &aflags);
if (*d)
{
if (!need_pres)
{
need_pres = 1;
save_global_regs("did_it_save", preserve, preserve_len);
}
buff = bp = alloc_lbuf("did_it.2");
str = d;
TinyExec(buff, &bp, 0, thing, player, EV_EVAL | EV_FIGNORE | EV_TOP, &str, args, nargs);
*bp = '\0';
if (*buff)
{
notify_except2(loc, player, player, thing, tprintf("%s %s", Name(player), buff));
}
free_lbuf(buff);
}
else if (odef)
{
notify_except2(loc, player, player, thing, tprintf("%s %s", Name(player), odef));
}
free_lbuf(d);
}
else if ((owhat < 0) && odef && Has_location(player) && Good_obj(loc = Location(player)))
{
notify_except2(loc, player, player, thing, tprintf("%s %s", Name(player), odef));
}
// If we preserved the state of the global registers, restore them.
//
if (need_pres)
{
restore_global_regs("did_it_restore", preserve, preserve_len);
}
// do the action attribute.
//
if (awhat > 0)
{
if (*(act = atr_pget(thing, awhat, &aowner, &aflags)))
{
charges = atr_pget(thing, A_CHARGES, &aowner, &aflags);
if (*charges)
{
num = Tiny_atol(charges);
if (num > 0)
{
buff = alloc_sbuf("did_it.charges");
Tiny_ltoa(num-1, buff);
atr_add_raw(thing, A_CHARGES, buff);
free_sbuf(buff);
}
else if (*(buff = atr_pget(thing, A_RUNOUT, &aowner, &aflags)))
{
free_lbuf(act);
act = buff;
}
else
{
free_lbuf(act);
free_lbuf(buff);
free_lbuf(charges);
return;
}
}
free_lbuf(charges);
wait_que(thing, player, 0, NOTHING, 0, act, args, nargs,
mudstate.global_regs);
}
free_lbuf(act);
}
}
/*
* ---------------------------------------------------------------------------
* * do_verb: Command interface to did_it.
*/
void do_verb(dbref player, dbref cause, int key, char *victim_str, char *args[], int nargs)
{
dbref actor, victim;
dbref aowner = NOTHING;
int what, owhat, awhat, nxargs, restriction;
int aflags = NOTHING;
ATTR *ap;
const char *whatd, *owhatd;
char *xargs[10];
// 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, "Permission denied,");
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, args[6], '\0',
EV_STRIP_LS | EV_STRIP_TS, xargs, 10, (char **)NULL, 0, &nxargs);
}
// If player doesn't control both, enforce visibility restrictions.
//
if (restriction)
{
ap = NULL;
if (what != -1)
{
atr_get_info(victim, what, &aowner, &aflags);
ap = atr_num(what);
}
if (!ap || !Read_attr(player, victim, ap, aowner, aflags) ||
((ap->number == A_DESC) && !mudconf.read_rem_desc &&
!Examinable(player, victim) && !nearby(player, victim)))
{
what = -1;
}
ap = NULL;
if (owhat != -1)
{
atr_get_info(victim, owhat, &aowner, &aflags);
ap = atr_num(owhat);
}
if (!ap || !Read_attr(player, victim, ap, aowner, aflags) ||
((ap->number == A_DESC) && !mudconf.read_rem_desc &&
!Examinable(player, victim) && !nearby(player, victim)))
{
owhat = -1;
}
awhat = 0;
}
// Go do it.
//
did_it(actor, victim, what, whatd, owhat, owhatd, awhat,
xargs, nxargs);
// Free user args.
//
for (int i = 0; i < nxargs; i++)
{
free_lbuf(xargs[i]);
}
}
#endif // STANDALONE
// --------------------------------------------------------------------------
// OutOfMemory: handle an out of memory condition.
//
BOOL OutOfMemory(const char *SourceFile, unsigned int LineNo)
{
Log.printf("%s(%u): Out of memory.\n", SourceFile, LineNo);
Log.Flush();
#ifdef STANDALONE
abort();
#else // STANDALONE
if (mudstate.initializing)
{
abort();
}
else
{
do_restart(1,1,0);
}
#endif // STANDALONE
return TRUE;
}
// --------------------------------------------------------------------------
// AssertionFailed: A logical assertion has failed.
//
BOOL AssertionFailed(const char *SourceFile, unsigned int LineNo)
{
Log.printf("%s(%u): Assertion failed.\n", SourceFile, LineNo);
Log.Flush();
#ifdef STANDALONE
abort();
#else // STANDALONE
if (mudstate.initializing)
{
abort();
}
else
{
do_restart(1,1,0);
}
#endif // STANDALONE
return FALSE;
}