#include "config.h"
#ifdef SunOS_5
#include <stdlib.h>
#endif
#include <sys/types.h>
#include <sys/stat.h>
#if !defined(SunOS_5)
#include <sys/dir.h>
#endif
#include <fcntl.h>
#include <setjmp.h>
#include <string.h>
#include <errno.h>
#include <stdio.h>
#ifndef LATTICE
#include <varargs.h>
#include <memory.h>
#else
#include <signal.h>
#include "amiga.h"
#endif
#if defined(sun)
#include <alloca.h>
#endif
#if defined(OSF) || defined(M_UNIX)
#include <dirent.h>
#endif
#include "lint.h"
#include "opcodes.h"
#include "interpret.h"
#include "object.h"
#include "sent.h"
#include "exec.h"
#include "comm.h"
#include "debug.h"
extern int errno;
extern int current_time;
extern int comp_flag;
char *inherit_file;
/* prevents infinite inherit loops.
No, mark-and-sweep solution won't work. Exercise for reader. */
int num_objects_this_thread = 0;
#ifndef NeXT
extern int readlink PROT((char *, char *, int));
extern int symlink PROT((char *, char *));
#endif /* NeXT */
#if !defined(hpux) && !defined(_AIX) && !defined(__386BSD__) \
&& !defined(linux) && !defined(SunOS_5) && !defined(SVR4) \
&& !defined(__bsdi__)
extern int fchmod PROT((int, int));
#endif /* !defined(hpux) && !defined(_AIX) */
char *last_verb = 0;
extern int set_call PROT((struct object *, struct sentence *, int, int)),
legal_path PROT((char *));
void pre_compile PROT((char *)),
remove_interactive PROT((struct object *)),
add_light PROT((struct object *, int)),
add_action PROT((char *, char *, int)),
add_verb PROT((char *, int)),
ipc_remove(),
set_snoop PROT((struct object *, struct object *)),
start_new_file PROT((FILE *)), end_new_file(),
move_or_destruct PROT((struct object *, struct object *)),
load_ob_from_swap PROT((struct object *)), dump_malloc_data(),
print_svalue PROT((struct svalue *)),
debug_message_value(),
destruct2();
extern int d_flag;
struct object *obj_list, *obj_list_destruct, *master_ob;
extern userid_t *backbone_uid;
struct object *current_object; /* The object interpreting a function. */
struct object *command_giver; /* Where the current command came from. */
struct object *current_interactive; /* The user who caused this execution */
int num_parse_error; /* Number of errors in the parser. */
struct variable *find_status(str, must_find)
char *str;
int must_find;
{
int i;
for (i=0; i < (int)current_object->prog->p.i.num_variables; i++) {
if (strcmp(current_object->prog->p.i.variable_names[i].name, str) == 0)
return ¤t_object->prog->p.i.variable_names[i];
}
if (!must_find)
return 0;
error("--Status %s not found in prog for %s\n", str,
current_object->name);
return 0;
}
#ifdef PRIVS
void
init_privs_for_object(ob)
struct object *ob;
{
struct object *tmp_ob;
struct svalue *value;
static char *privs_file_fname = (char *) 0;
if (master_ob == NULL)
tmp_ob = ob;
else {
assert_master_ob_loaded();
tmp_ob = master_ob;
}
if (!current_object || !current_object->uid) {
ob->privs = NULL;
return;
}
push_string(ob->name, STRING_CONSTANT);
if (!privs_file_fname)
privs_file_fname = make_shared_string("privs_file");
value = apply(privs_file_fname, tmp_ob, 1);
if (value == NULL || value->type != T_STRING)
ob->privs = NULL;
else
ob->privs = make_shared_string(value->u.string);
}
#endif /* PRIVS */
/*
* Give the correct uid and euid to a created object.
*/
int give_uid_to_object(ob)
struct object *ob;
{
struct svalue *ret;
char *creator_name;
struct object *tmp_ob;
static char *creator_file_fname = (char *) 0;
if (master_ob == 0)
tmp_ob = ob;
else
{
assert_master_ob_loaded();
tmp_ob = master_ob;
}
if (!current_object || !current_object->uid) {
/*
* Only for the master and void object. Note that
* back_bone_uid is not defined when master.c is being loaded.
*/
ob->uid = add_uid ("NONAME");
ob->euid = NULL;
return 1;
}
/*
* Ask master.c who the creator of this object is.
*/
push_string(ob->name, STRING_CONSTANT);
if (!creator_file_fname)
creator_file_fname = make_shared_string("creator_file");
ret = apply(creator_file_fname, tmp_ob, 1);
if (!ret)
error("No function 'creator_file' in master.c!\n");
if (ret->type != T_STRING) {
struct svalue arg;
/* This can be the case for objects in /ftp and /open. */
arg.type = T_OBJECT;
arg.u.ob = ob;
destruct_object(&arg);
error("Illegal object to load.\n");
}
creator_name = ret->u.string;
/*
* Now we are sure that we have a creator name.
* Do not call apply() again, because creator_name will be lost !
*/
if (strcmp(current_object->uid->name, creator_name) == 0) {
/*
* The loaded object has the same uid as the loader.
*/
ob->uid = current_object->uid;
ob->euid = current_object->euid; /* FIXME - is this right? */
return 1;
}
#ifdef AUTO_TRUST_BACKBONE
if (strcmp(backbone_uid->name, creator_name) == 0) {
/*
* The object is loaded from backbone. This is trusted, so we
* let it inherit the value of eff_user.
*/
ob->uid = current_object->euid;
ob->euid = current_object->euid;
return 1;
}
#endif
/*
* The object is not loaded from backbone, nor from
* from the loading objects path. That should be an object
* defined by another wizard. It can't be trusted, so we give it the
* same uid as the creator. Also give it eff_user 0, which means that
* user 'a' can't use objects from user 'b' to load new objects nor
* modify files owned by user 'b'.
*
* If this effect is wanted, user 'b' must let his object do
* 'seteuid()' to himself. That is the case for most rooms.
*/
ob->uid = add_uid(creator_name);
#ifdef AUTO_SETEUID
ob->euid = ob->uid;
#else
ob->euid = NULL;
#endif
return 1;
}
int init_object (ob)
struct object *ob;
{
init_stats_for_object (ob);
#ifdef PRIVS
init_privs_for_object (ob);
#endif /* PRIVS */
add_objects (&ob->stats, 1);
return give_uid_to_object (ob);
}
struct svalue *
load_virtual_object(name)
char *name;
{
static int loading_virtual_object = 0;
struct svalue *v;
push_string(name, STRING_MALLOC);
loading_virtual_object++;
v = apply_master_ob("compile_object", 1);
loading_virtual_object--;
if (!v || (v->type != T_OBJECT)) {
fprintf(stderr, "Could not load descr for %s\n", name);
error("Failed to load file: %s\n",name);
return 0;
}
return v;
}
/*
* Load an object definition from file. If the object wants to inherit
* from an object that is not loaded, discard all, load the inherited object,
* and reload again.
*
* In mudlib3.0 when loading inherited objects, their reset() is not called.
* - why is this?? it makes no sense and causes a problem when a developer
* inherits code from a real used item. Say a room for example. In this case
* the room is loaded but is never set up properly, so when someone enters it
* it's all messed up. Realistically, I know that it's pretty bad style to
* inherit from an object that's actually being used and isn't just a building
* block, but I see no reason for this limitation. It happens, and when it
* does occur, produces mysterious results than can be hard to track down.
* for now, I've reenabled resetting. We'll see if anything breaks. -WF
*
* Save the command_giver, because reset() in the new object might change
* it.
*
*/
struct object *load_object(lname, dont_reset)
char *lname;
int dont_reset;
{
FILE *f;
extern int total_lines;
struct object *ob, *save_command_giver = command_giver;
extern struct program *prog;
struct svalue *mret;
extern char *current_file;
struct stat c_st;
int name_length;
char real_name[200], name[200];
char *p;
#ifdef VALID_CLONE
if (current_object && master_ob) {
char *orig;
orig = lname;
lname = check_valid_path(lname, current_object, "load_object", 0);
if (!lname) {
error("Insufficient permission to read file: %s\n", orig);
}
}
#endif
if (++num_objects_this_thread > INHERIT_CHAIN_SIZE)
error ("Inherit chain too deep: > %d\n",INHERIT_CHAIN_SIZE);
if (current_object && current_object->euid == NULL)
error("Can't load objects when no effective user.\n");
/* don't allow consecutive "/"'s - Wayfarer */
p = lname;
while (*p)
{
if (*p == '/' && *(p + 1) == '/')
{
error ("Filenames with consecutive /'s in them aren't allowed.\n");
return 0;
}
p++;
}
/* Truncate possible .c in the object name. */
/* Remove leading '/' if any. */
while(lname[0] == '/')
lname++;
strncpy(name, lname, sizeof(name) - 1);
name[sizeof name - 1] = '\0';
name_length = strlen(name);
if (name_length > sizeof name - 4)
name_length = sizeof name - 4;
name[name_length] = '\0';
if (name[name_length-2] == '.' && name[name_length-1] == 'c') {
name[name_length-2] = '\0';
name_length -= 2;
}
/*
* First check that the c-file exists.
*/
(void) strcpy(real_name, name);
(void) strcat(real_name, ".c");
if (stat(real_name, &c_st) == -1) {
struct svalue *v;
if (!(v = load_virtual_object(name))) {
return 0;
}
/* Now set the file name of the specified object correctly...*/
ob = v->u.ob;
remove_object_hash(ob);
if (ob->name)
FREE(ob->name);
ob->name = string_copy(name);
enter_object_hash(ob);
ob->flags |= O_VIRTUAL;
ob->load_time = current_time;
return ob;
}
/*
* Check if it's a legal name.
*/
if (!legal_path(real_name)) {
fprintf(stderr, "Illegal pathname: %s\n", real_name);
error("Illegal path name.\n");
return 0;
}
#ifdef SAVE_BINARIES
if (!load_binary(real_name)) {
#endif
/* maybe move this section into compile_file? */
if (comp_flag)
fprintf(stderr, " compiling %s ...", real_name);
f = fopen(real_name, "r");
if (f == 0) {
perror(real_name);
error("Could not read the file.\n");
}
current_file = string_copy(real_name); /* This one is freed below */
start_new_file(f);
compile_file();
end_new_file();
if (comp_flag)
fprintf(stderr, " done\n");
update_compile_av(total_lines);
total_lines = 0;
(void)fclose(f);
FREE(current_file);
current_file = 0;
#ifdef SAVE_BINARIES
}
#endif
/* Sorry, can't handle objects without programs yet. */
if (inherit_file == 0 && (num_parse_error > 0 || prog == 0)) {
if (prog)
free_prog(prog, 1);
if (num_parse_error == 0 && prog == 0)
error("No program in object!\n");
error("Error in loading object\n");
}
/*
* This is an iterative process. If this object wants to inherit an
* unloaded object, then discard current object, load the object to be
* inherited and reload the current object again. The global variable
* "inherit_file" will be set by lang.y to point to a file name.
*/
if (inherit_file) {
char *tmp = inherit_file;
if (prog) {
free_prog(prog, 1);
prog = 0;
}
if (strcmp(inherit_file, name) == 0) {
FREE(inherit_file);
inherit_file = 0;
error("Illegal to inherit self.\n");
}
inherit_file = 0;
#if 0 /* MUDLIB3_NEED, It's very awkard to have to have a debug3 /JnA */
load_object(tmp, 1);
#else
load_object(tmp, 0); /* Remove this feature for now */
#endif
FREE(tmp);
ob = load_object(name, dont_reset);
ob->load_time = current_time;
num_objects_this_thread--;
return ob;
}
ob = get_empty_object(prog->p.i.num_variables);
ob->name = string_copy(name); /* Shared string is no good here */
ob->prog = prog;
ob->flags |= O_WILL_RESET; /* must be before reset is first called */
ob->next_all = obj_list;
obj_list = ob;
enter_object_hash(ob); /* add name to fast object lookup table */
if (master_ob) {
push_object(ob);
mret = apply_master_ob("valid_object", 1);
if (mret) {
if (mret->type & T_NUMBER) {
if (IS_ZERO(mret)) {
void destruct_object_two();
destruct_object_two(ob);
error("master object (valid_object) denied permission to load %s.\n", name);
}
} else {
free_svalue(mret);
}
}
}
if (init_object(ob) && !dont_reset)
reset_object(ob, 0);
if (!(ob->flags & O_DESTRUCTED) &&
function_exists("clean_up", ob)) {
ob->flags |= O_WILL_CLEAN_UP;
}
command_giver = save_command_giver;
if (d_flag > 1 && ob)
debug_message("--%s loaded\n", ob->name);
ob->load_time = current_time;
num_objects_this_thread--;
return ob;
}
char *make_new_name(str)
char *str;
{
static int i;
char *p = DXALLOC(strlen(str) + 10, 97, "make_new_name");
(void)sprintf(p, "%s#%d", str, i);
i++;
return p;
}
/*
* Save the command_giver, because reset() in the new object might change
* it.
*/
struct object *clone_object(str1)
char *str1;
{
struct object *ob, *new_ob;
struct object *save_command_giver = command_giver;
if(current_object && current_object->euid == 0){
error("Object must call seteuid() prior to calling clone_object().\n");
}
#ifdef VALID_CLONE
if(current_object){
char *orig;
orig = str1;
str1 = check_valid_path(str1, current_object, "clone_object", 0);
if(!str1){
error("Insufficient permission to read file: %s\n",orig);
}
}
#endif /* VALID_CLONE */
num_objects_this_thread = 0;
ob = find_object(str1);
if (ob && !object_visible(ob)) ob = 0;
/*
* If the object self-destructed...
*/
if(ob == 0) /* fix from 3.1.1 */
return(0);
if(ob == 0 || ob->super || (ob->flags & O_CLONE))
if(!(ob->flags & O_VIRTUAL) || strrchr(str1,'#'))
error("Cloning a bad object !\n");
else {
/*
* well... it's a virtual object. So now we're going to "clone" it.
*/
struct svalue *v;
char *p;
/* Remove leading '/' if any. */
while(str1[0] == '/')
str1++;
p = str1;
while(*p){
if(*p == '/' && *(p + 1) == '/'){
error("Filenames with consecutive /'s in them aren't allowed.\n");
return(0);
}
p++;
}
if (ob->ref == 1 && !ob->super && !ob->contains) {
/*
* ob unused so reuse it instead to save space.
* (possibly loaded just for cloning)
*/
new_ob = ob;
} else {
/* can't reuse, so load another */
if (!(v = load_virtual_object(str1)))
return 0;
new_ob = v->u.ob;
}
remove_object_hash(new_ob);
if (new_ob->name)
FREE(new_ob->name);
/* Now set the file name of the specified object correctly...*/
new_ob->name = make_new_name(str1);
enter_object_hash(new_ob);
new_ob->flags |= O_VIRTUAL;
new_ob->load_time = current_time;
command_giver = save_command_giver;
return(new_ob);
/* we can skip all of the stuff below since we were already cloned
once to have gotten to this stage. */
}
/* We do not want the heart beat to be running for unused copied objects */
if(ob->flags & O_HEART_BEAT)
(void)set_heart_beat(ob, 0);
new_ob = get_empty_object(ob->prog->p.i.num_variables);
new_ob->name = make_new_name(ob->name);
new_ob->flags |= (O_CLONE | (ob->flags & (O_WILL_CLEAN_UP | O_WILL_RESET)));
new_ob->load_time = ob->load_time;
new_ob->prog = ob->prog;
reference_prog(ob->prog, "clone_object");
if(!current_object)
fatal("clone_object() from no current_object !\n");
init_object(new_ob);
new_ob->next_all = obj_list;
obj_list = new_ob;
enter_object_hash(new_ob); /* Add name to fast object lookup table */
reset_object(new_ob, 0);
command_giver = save_command_giver;
/* Never know what can happen ! :-( */
if(new_ob->flags & O_DESTRUCTED)
return(0);
return(new_ob);
}
struct object *environment(arg)
struct svalue *arg;
{
struct object *ob = current_object;
if (arg && arg->type == T_OBJECT)
ob = arg->u.ob;
if (ob == 0 || ob->super == 0 || (ob->flags & O_DESTRUCTED))
return 0;
if (ob->flags & O_DESTRUCTED)
error("environment() off destructed object.\n");
return ob->super;
}
/*
* Execute a command for an object. Copy the command into a
* new buffer, because 'parse_command()' can modify the command.
* If the object is not current object, static functions will not
* be executed. This will prevent forcing users to do illegal things.
*
* Return cost of the command executed if success (> 0).
* When failure, return 0.
*/
int command_for_object(str, ob)
char *str;
struct object *ob;
{
char buff[1000];
extern int eval_cost;
int save_eval_cost = eval_cost;
if (strlen(str) > sizeof(buff) - 1)
error("Too long command.\n");
if (ob == 0)
ob = current_object;
else if (ob->flags & O_DESTRUCTED)
return 0;
strncpy(buff, str, sizeof buff);
buff[sizeof buff - 1] = '\0';
if (parse_command(buff, ob))
return eval_cost - save_eval_cost + 1000; /* why the + 1000 ? */
else
return 0;
}
/*
* To find if an object is present, we have to look in two inventory
* lists. The first list is the inventory of the current object.
* The second list is all things that have the same ->super as
* current_object.
* Also test the environment.
* If the second argument 'ob' is non zero, only search in the
* inventory of 'ob'. The argument 'ob' will be mandatory, later.
*/
static struct object *object_present2 PROT((char *, struct object *));
struct object *object_present(v, ob)
struct svalue *v;
struct object *ob;
{
struct svalue *ret;
struct object *ret_ob;
int specific = 0;
if (ob == 0)
ob = current_object;
else
specific = 1;
if (ob->flags & O_DESTRUCTED)
return 0;
if (v->type == T_OBJECT) {
if (specific) {
if (v->u.ob->super == ob)
return v->u.ob;
else
return 0;
}
if (v->u.ob->super == ob ||
(v->u.ob->super == ob->super && ob->super != 0))
return v->u.ob->super;
return 0;
}
ret_ob = object_present2(v->u.string, ob->contains);
if (ret_ob)
return ret_ob;
if (specific)
return 0;
if (ob->super) {
push_string(v->u.string, STRING_CONSTANT);
ret = apply("id", ob->super, 1);
if (ob->super->flags & O_DESTRUCTED)
return 0;
if (!IS_ZERO(ret)) {
/*
if id() returns a value of type object then query that object.
this will allow container objects to allow objects inside them
to be referred to (for attack or whatever).
*/
#ifndef OLD_PRESENT
if (ret->type == T_OBJECT)
return ret->u.ob;
else
#endif
return ob->super;
}
return object_present2(v->u.string, ob->super->contains);
}
return 0;
}
static struct object *object_present2(str, ob)
char *str;
struct object *ob;
{
struct svalue *ret;
char *p;
int count = 0, length;
char *item;
item = string_copy(str);
length = strlen(item);
p = item + length - 1;
if (*p >= '0' && *p <= '9') {
while(p > item && *p >= '0' && *p <= '9')
p--;
if (p > item && *p == ' ') {
count = atoi(p+1) - 1;
*p = '\0';
length = p - item; /* This is never used again ! */
}
}
for (; ob; ob = ob->next_inv) {
push_string(item, STRING_CONSTANT);
ret = apply("id", ob, 1);
if (ob->flags & O_DESTRUCTED) {
FREE(item);
return 0;
}
if (IS_ZERO(ret))
continue;
if (count-- > 0)
continue;
FREE(item);
#ifndef OLD_PRESENT
if (ret->type == T_OBJECT)
return ret->u.ob;
else
#endif
return ob;
}
FREE(item);
return 0;
}
/*
* Remove an object. It is first moved into the destruct list, and
* not really destructed until later. (see destruct2()).
*/
void destruct_object_two(ob)
struct object *ob;
{
struct object *super;
struct object **pp;
int removed;
#ifdef SOCKET_EFUNS
/*
* check if object has an efun socket referencing it for
* a callback. if so, close the efun socket.
*/
if (ob->flags & O_EFUN_SOCKET) {
close_referencing_sockets(ob);
}
#endif
if (ob->flags & O_DESTRUCTED)
return;
if (ob->flags & O_SWAPPED)
load_ob_from_swap(ob);
remove_object_from_stack(ob);
/*
* If this is the first object being shadowed by another object, then
* destruct the whole list of shadows.
*/
#ifndef NO_SHADOWS
if (ob->shadowed && !ob->shadowing) {
struct svalue svp;
struct object *ob2;
svp.type = T_OBJECT;
for (ob2 = ob->shadowed; ob2; ) {
svp.u.ob = ob2;
ob2 = ob2->shadowed;
svp.u.ob->shadowed = 0;
svp.u.ob->shadowing = 0;
destruct_object(&svp);
}
}
/*
* The chain of shadows is a double linked list. Take care to update
* it correctly.
*/
if (ob->shadowing)
ob->shadowing->shadowed = ob->shadowed;
if (ob->shadowed)
ob->shadowed->shadowing = ob->shadowing;
ob->shadowing = 0;
ob->shadowed = 0;
#endif
if (d_flag > 1)
debug_message("Destruct object %s (ref %d)\n", ob->name, ob->ref);
super = ob->super;
if (super == 0) {
/*
* There is nowhere to move the objects.
*/
struct svalue svp;
svp.type = T_OBJECT;
while(ob->contains) {
svp.u.ob = ob->contains;
push_object(ob->contains);
/* An error here will not leave destruct() in an inconsistent
* stage.
*/
apply_master_ob("destruct_environment_of",1);
if (svp.u.ob == ob->contains)
destruct_object(&svp);
}
} else {
while(ob->contains)
move_or_destruct(ob->contains, super);
}
add_objects (&ob->stats, -1);
if ( ob->interactive ) {
struct object *save=command_giver;
command_giver=ob;
#ifdef ED
if (ob->interactive->ed_buffer) {
extern void save_ed_buffer();
save_ed_buffer();
}
#endif
command_giver=save;
}
/*
* Remove us out of this current room (if any).
* Remove all sentences defined by this object from all objects here.
*/
if (ob->super) {
if (ob->super->flags & O_ENABLE_COMMANDS)
remove_sent(ob, ob->super);
add_light(ob->super, - ob->total_light);
for (pp = &ob->super->contains; *pp;) {
if ((*pp)->flags & O_ENABLE_COMMANDS)
remove_sent(ob, *pp);
if (*pp != ob)
pp = &(*pp)->next_inv;
else
*pp = (*pp)->next_inv;
}
}
/*
* Now remove us out of the list of all objects.
* This must be done last, because an error in the above code would
* halt execution.
*/
removed = 0;
for (pp = &obj_list; *pp; pp = &(*pp)->next_all) {
if (*pp != ob)
continue;
*pp = (*pp)->next_all;
removed = 1;
remove_object_hash(ob);
break;
}
if (!removed)
fatal("Failed to delete object.\n");
if (ob->living_name)
remove_living_name(ob);
ob->super = 0;
ob->next_inv = 0;
ob->contains = 0;
ob->flags &= ~O_ENABLE_COMMANDS;
ob->next_all = obj_list_destruct;
obj_list_destruct = ob;
set_heart_beat(ob, 0);
ob->flags |= O_DESTRUCTED;
/* moved this here from destruct2() -- see comments in destruct2() */
if (ob->interactive) {
remove_interactive(ob);
}
}
void destruct_object(v)
struct svalue *v;
{
struct object *ob = (struct object *)NULL;
if (v->type == T_OBJECT) {
ob = v->u.ob;
destruct_object_two(ob);
} else {
error("destruct_object: called without an object argument\n");
}
}
/*
* This one is called when no program is executing from the main loop.
*/
void destruct2(ob)
struct object *ob;
{
if (d_flag > 1) {
debug_message("Destruct-2 object %s (ref %d)\n", ob->name, ob->ref);
}
#if 0
/* moved this into destruct_object() to deal with the 0 in users() efun
output problem
*/
if (ob->interactive)
remove_interactive(ob);
#endif
/*
* We must deallocate variables here, not in 'free_object()'.
* That is because one of the local variables may point to this object,
* and deallocation of this pointer will also decrease the reference
* count of this object. Otherwise, an object with a variable pointing
* to itself, would never be freed.
* Just in case the program in this object would continue to
* execute, change string and object variables into the number 0.
*/
if (ob->prog->p.i.num_variables > 0) {
/*
* Deallocate variables in this object.
* The space of the variables are not deallocated until
* the object structure is freed in free_object().
*/
int i;
for (i=0; i<(int)ob->prog->p.i.num_variables; i++) {
free_svalue(&ob->variables[i]);
ob->variables[i].type = T_NUMBER;
ob->variables[i].u.number = 0;
ob->variables[i].subtype = T_NULLVALUE;
}
}
free_object(ob, "destruct_object");
}
/*
* say() efun - send a message to:
* all objects in the inventory of the source,
* all objects in the same environment as the source,
* and the object surrounding the source.
*
* when there is no command_giver, current_object is used as the source,
* otherwise, command_giver is used.
*
* message never goes to objects in the avoid vector, or the source itself.
*
* rewritten, bobf@metronet.com (Blackthorn) 9/6/93
*/
void send_say(ob, text, avoid)
struct object *ob;
char *text;
struct vector *avoid;
{
int valid, j;
for (valid = 1, j = 0; j < avoid->size; j++)
{
if (avoid->item[j].type != T_OBJECT)
continue;
if (avoid->item[j].u.ob == ob)
{
valid = 0;
break;
}
}
if (!valid)
return;
tell_object(ob, text);
}
void say(v, avoid)
struct svalue *v;
struct vector *avoid;
{
struct object *ob, *origin, *save_command_giver = command_giver;
char *buff;
check_legal_string(v->u.string);
buff = v->u.string;
if (current_object->flags & O_ENABLE_COMMANDS)
command_giver = current_object;
if (command_giver)
origin = command_giver;
else
origin = current_object;
/* To our surrounding object... */
if ((ob = origin->super))
{
if (ob->flags & O_ENABLE_COMMANDS || ob->interactive)
send_say(ob, buff, avoid);
/* And its inventory... */
for (ob = origin->super->contains; ob; ob = ob->next_inv)
{
if (ob != origin && (ob->flags & O_ENABLE_COMMANDS || ob->interactive))
{
send_say(ob, buff, avoid);
if (ob->flags & O_DESTRUCTED)
break;
}
}
}
/* Our inventory... */
for (ob = origin->contains; ob; ob = ob->next_inv)
{
if (ob->flags & O_ENABLE_COMMANDS || ob->interactive)
{
send_say(ob, buff, avoid);
if (ob->flags & O_DESTRUCTED)
break;
}
}
command_giver = save_command_giver;
}
/*
* Sends a string to all objects inside of a specific object.
* Revised, bobf@metronet.com 9/6/93
*/
void tell_room(room, v, avoid)
struct object *room;
struct svalue *v;
struct vector *avoid;
{
struct object *ob;
char *buff;
int valid, j;
static char txt_buf[LARGEST_PRINTABLE_STRING];
switch (v->type)
{
case T_STRING:
check_legal_string(v->u.string);
buff = v->u.string;
break;
case T_OBJECT:
buff = v->u.ob->name;
break;
case T_NUMBER:
buff = txt_buf;
sprintf(buff, "%d", v->u.number);
break;
case T_REAL:
buff = txt_buf;
sprintf(buff, "%g", v->u.real);
break;
default:
error("Bad arg 2 to tell_room()\n");
}
for (ob = room->contains; ob; ob = ob->next_inv)
{
if (!ob->interactive && !(ob->flags & O_ENABLE_COMMANDS))
continue;
for (valid = 1, j = 0; j < avoid->size; j++)
{
if (avoid->item[j].type != T_OBJECT)
continue;
if (avoid->item[j].u.ob == ob)
{
valid = 0;
break;
}
}
if (!valid)
continue;
if (!ob->interactive) {
tell_npc(ob, buff);
if (ob->flags & O_DESTRUCTED)
break;
} else {
tell_object(ob, buff);
if (ob->flags & O_DESTRUCTED)
break;
}
}
}
void shout_string(str)
char *str;
{
struct object *ob;
#ifdef LOG_SHOUT
FILE *f = 0;
char *tmpstr;
#endif
char *p;
str = string_copy(str); /* So that we can modify the string */
for (p=str; *p; p++) {
if ((*p < ' ' || *p > '~') && *p != '\n')
*p = ' ';
}
p = 0;
#ifdef LOG_SHOUT
if (command_giver) {
struct svalue *v;
v = apply("query_cap_name", command_giver, 0);
if (v && v->type == T_STRING)
p = v->u.string;
else {
v = apply("query_name", command_giver, 0);
if (v && v->type == T_STRING)
p = v->u.string;
}
} else if (current_object && current_object->uid)
p = current_object->uid->name;
if (p)
{
tmpstr = (char *)DMALLOC(strlen(LOG_DIR) + 8, 101, "shout_string: 1");
sprintf(tmpstr,"%s/shouts",LOG_DIR);
if (tmpstr[0] == '/')
strcpy (tmpstr, tmpstr+1);
f = fopen(tmpstr, "a");
FREE(tmpstr);
}
if (f) {
fprintf(f, "%s: %s\n", p, str);
fclose(f);
}
#endif
for (ob = obj_list; ob; ob = ob->next_all) {
if (!(ob->flags & O_ENABLE_COMMANDS) || (ob == command_giver)
|| !ob->super)
continue;
tell_object(ob,str);
}
FREE(str);
}
struct object *first_inventory(arg)
struct svalue *arg;
{
struct object *ob;
if (arg->type == T_STRING)
{
ob = find_object(arg->u.string);
if (ob && !object_visible(ob)) ob = 0;
}
else
ob = arg->u.ob;
if (ob == 0)
error("No object to first_inventory()");
ob = ob->contains;
while (ob)
{
if (ob->flags & O_HIDDEN)
{
if (object_visible(ob))
{
return ob;
}
} else return ob;
ob = ob->next_inv;
}
return 0;
}
/*
* This will enable an object to use commands normally only
* accessible by interactive users.
* Also check if the user is a wizard. Wizards must not affect the
* value of the wizlist ranking.
*/
void enable_commands(num)
int num;
{
if (current_object->flags & O_DESTRUCTED)
return;
if (d_flag > 1) {
debug_message("Enable commands %s (ref %d)\n",
current_object->name, current_object->ref);
}
if (num) {
current_object->flags |= O_ENABLE_COMMANDS;
command_giver = current_object;
} else {
current_object->flags &= ~O_ENABLE_COMMANDS;
command_giver = 0;
}
}
/*
* Set up a function in this object to be called with the next
* user input string.
*/
int input_to(fun, flag, num_arg, args)
char *fun;
int flag;
int num_arg;
struct svalue *args;
{
struct sentence *s;
struct svalue *x;
if(!command_giver || command_giver->flags & O_DESTRUCTED)
return 0;
/*
* If we have args, we have to copy them, so the svalues on the
* stack can be freed, which will be done automatically.
*/
if(num_arg){
if((x = (struct svalue *)
DMALLOC(num_arg * sizeof(struct svalue), 102, "input_to: 1")) == NULL)
fatal("Not enough memory to copy args from input_to.\n");
copy_some_svalues(x, args, num_arg);
}
else
x = NULL;
s = alloc_sentence();
if(set_call(command_giver, s, flag, 0)){
command_giver->interactive->carryover = x;
command_giver->interactive->num_carry = num_arg;
s->function = make_shared_string(fun);
s->ob = current_object;
add_ref(current_object, "input_to");
return 1;
}
if(x)
FREE(x);
free_sentence(s);
return 0;
}
/*
* Set up a function in this object to be called with the next
* user input character.
*/
int get_char (fun, flag)
char *fun;
int flag;
{
struct sentence *s;
if (!command_giver || command_giver->flags & O_DESTRUCTED)
return 0;
s = alloc_sentence();
if (set_call(command_giver, s, flag, 1)) {
s->function = make_shared_string(fun);
s->ob = current_object;
add_ref(current_object, "get_char");
return 1;
}
free_sentence(s);
return 0;
}
INLINE void
check_legal_string(s)
char *s;
{
if (strlen(s) >= LARGEST_PRINTABLE_STRING) {
error("Printable strings limited to length of %d.\n",
LARGEST_PRINTABLE_STRING);
}
}
void print_svalue(arg)
struct svalue *arg;
{
if (arg == 0)
{
add_message("<NULL>");
}
else switch (arg->type)
{
case T_STRING:
check_legal_string(arg->u.string);
tell_object(command_giver, arg->u.string);
break;
case T_OBJECT:
add_message("OBJ(%s)", arg->u.ob->name);
break;
case T_NUMBER:
add_message("%d", arg->u.number);
break;
case T_REAL:
add_message("%g", arg->u.real);
break;
case T_POINTER:
add_message("<ARRAY>");
break;
case T_MAPPING:
add_message("<MAPPING>");
break;
case T_FUNCTION:
add_message("<FUNCTION>");
break;
default:
add_message("<UNKNOWN>");
break;
}
return;
}
void do_write(arg)
struct svalue *arg;
{
struct object *save_command_giver = command_giver;
#ifndef NO_SHADOWS
if(command_giver == 0 && current_object->shadowing)
command_giver = current_object;
if(command_giver){
/* Send the message to the first object in the shadow list */
while(command_giver->shadowing)
command_giver = command_giver->shadowing;
}
#else
if(!command_giver)
command_giver = current_object;
#endif /* NO_SHADOWS */
print_svalue(arg);
command_giver = save_command_giver;
}
/* Find an object. If not loaded, load it !
* The object may selfdestruct, which is the only case when 0 will be
* returned.
*/
struct object *find_object(str)
char *str;
{
struct object *ob;
char *p;
/* don't allow consecutive "/"'s - Wayfarer */
p = str;
while (*p)
{
if (*p == '/' && *(p + 1) == '/')
return 0;
p++;
}
/* Remove leading '/' if any. */
while(str[0] == '/')
str++;
ob = find_object2(str);
if (ob)
return ob;
ob = load_object(str, 0);
if (!ob || (ob->flags & O_DESTRUCTED)) /* *sigh* */
return 0;
if (ob && ob->flags & O_SWAPPED)
load_ob_from_swap(ob);
return ob;
}
#define MAX_OBJECT_NAME_SIZE 2048
/* Look for a loaded object. Return 0 if non found. */
struct object *find_object2(str)
char *str;
{
register struct object *ob;
register int length;
char p[MAX_OBJECT_NAME_SIZE];
/* Remove leading '/' if any. */
while(str[0] == '/')
str++;
/* Truncate possible .c in the object name. */
length = strlen(str);
if (str[length-2] == '.' && str[length-1] == 'c') {
/* A new writeable copy of the name is needed. */
strncpy(p, str, MAX_OBJECT_NAME_SIZE);
str = p;
str[length-2] = '\0';
}
if ((ob = lookup_object_hash(str))) {
if (ob->flags & O_SWAPPED)
load_ob_from_swap(ob);
return ob;
}
return 0;
}
/*
* Transfer an object.
* The object has to be taken from one inventory list and added to another.
* The main work is to update all command definitions, depending on what is
* living or not. Note that all objects in the same inventory are affected.
*/
void move_object(item, dest)
struct object *item, *dest;
{
struct object **pp, *ob, *next_ob;
struct object *save_cmd = command_giver;
if (item != current_object)
error("Illegal to move other object than this_object()\n");
/* Recursive moves are not allowed. */
for (ob = dest; ob; ob = ob->super)
if (ob == item)
error("Can't move object inside itself.\n");
#ifndef NO_SHADOWS
if (item->shadowing)
error("Can't move an object that is shadowing.\n");
#endif
#ifdef LAZY_RESETS
try_reset(dest);
#endif
add_light(dest, item->total_light);
if (item->super) {
int okey = 0;
if (item->flags & O_ENABLE_COMMANDS) {
remove_sent(item->super, item);
}
if (item->super->flags & O_ENABLE_COMMANDS)
remove_sent(item, item->super);
add_light(item->super, - item->total_light);
for (pp = &item->super->contains; *pp;) {
if (*pp != item) {
if ((*pp)->flags & O_ENABLE_COMMANDS)
remove_sent(item, *pp);
if (item->flags & O_ENABLE_COMMANDS)
remove_sent(*pp, item);
pp = &(*pp)->next_inv;
continue;
}
*pp = item->next_inv;
okey = 1;
}
if (!okey)
fatal("Failed to find object %s in super list of %s.\n",
item->name, item->super->name);
}
item->next_inv = dest->contains;
dest->contains = item;
item->super = dest;
/*
* Setup the new commands. The order is very important, as commands
* in the room should override commands defined by the room.
* Beware that init() in the room may have moved 'item' !
*
* The call of init() should really be done by the object itself
* (except in the -o mode). It might be too slow, though :-(
*/
if (item->flags & O_ENABLE_COMMANDS) {
command_giver = item;
(void)apply("init", dest, 0);
if ((dest->flags & O_DESTRUCTED) || item->super != dest) {
command_giver = save_cmd; /* marion */
return;
}
}
/*
* Run init of the item once for every present user, and
* for the environment (which can be a user).
*/
for (ob = dest->contains; ob; ob=next_ob) {
next_ob = ob->next_inv;
if (ob == item)
continue;
if (ob->flags & O_DESTRUCTED)
error("An object was destructed at call of init()\n");
if (ob->flags & O_ENABLE_COMMANDS) {
command_giver = ob;
(void)apply("init", item, 0);
if (dest != item->super) {
command_giver = save_cmd; /* marion */
return;
}
}
if (item->flags & O_DESTRUCTED) /* marion */
error("The object to be moved was destructed at call of init()\n");
if (item->flags & O_ENABLE_COMMANDS) {
command_giver = item;
(void)apply("init", ob, 0);
if (dest != item->super) {
command_giver = save_cmd; /* marion */
return;
}
}
}
if (dest->flags & O_DESTRUCTED) /* marion */
error("The destination to move to was destructed at call of init()\n");
if (dest->flags & O_ENABLE_COMMANDS) {
command_giver = dest;
(void)apply("init", item, 0);
}
command_giver = save_cmd;
}
/*
* Every object as a count of number of light sources it contains.
* Update this.
*/
void add_light(p, n)
struct object *p;
int n;
{
if (n == 0)
return;
p->total_light += n;
if (p->super)
add_light(p->super, n);
}
struct sentence *sent_free = 0;
int tot_alloc_sentence;
struct sentence *alloc_sentence() {
struct sentence *p;
if (sent_free == 0) {
p = (struct sentence *)DXALLOC(sizeof *p, 103, "alloc_sentence");
tot_alloc_sentence++;
} else {
p = sent_free;
sent_free = sent_free->next;
}
p->verb = 0;
p->function = 0;
p->next = 0;
return p;
}
#ifdef free
void free_all_sent() {
struct sentence *p;
for (;sent_free; sent_free = p) {
p = sent_free->next;
FREE(sent_free);
}
}
#endif
void free_sentence(p)
struct sentence *p;
{
if (p->function)
free_string(p->function);
p->function = 0;
if (p->verb)
free_string(p->verb);
p->verb = 0;
p->next = sent_free;
sent_free = p;
}
/*
* Find the sentence for a command from the user.
* Return success status.
*/
#define MAX_VERB_BUFF 100
int user_parser(buff)
char *buff;
{
static char verb_buff[MAX_VERB_BUFF];
struct object *super;
struct sentence *s;
char *p;
int length;
struct object *save_current_object = current_object,
*save_command_giver = command_giver;
char *user_verb = 0;
if (d_flag > 1)
debug_message("cmd [%s]: %s\n", command_giver->name, buff);
/* strip trailing spaces. */
for (p = buff + strlen(buff) - 1; p >= buff; p--) {
if (*p != ' ')
break;
*p = '\0';
}
if (buff[0] == '\0')
return 0;
length = p - buff + 1;
p = strchr(buff, ' ');
if (p == 0) {
user_verb = findstring(buff);
} else {
*p = '\0';
user_verb = findstring(buff);
*p = ' ';
length = p - buff;
}
if (!user_verb) {
/* either an xverb or a verb without a specific add_action */
user_verb = buff;
}
/* copy user_verb into a static character buffer to be pointed to
by last_verb.
*/
strncpy(verb_buff, user_verb, MAX_VERB_BUFF - 1);
if (p) {
int pos;
pos = p - buff;
if (pos < MAX_VERB_BUFF) {
verb_buff[pos] = '\0';
}
}
/* clear_notify(); */ /* moved to process_user_command() */
s = save_command_giver->sent;
for ( ; s; s = s->next) {
struct svalue *ret;
struct object *command_object;
if (s->verb == 0)
error("No action linked to verb.\n");
if (s->flags & (V_NOSPACE | V_SHORT)) {
if (strncmp(buff, s->verb, strlen(s->verb)) != 0)
continue;
} else {
/* note: if was add_action(blah, "") then accept it */
if (s->verb[0] && (user_verb != s->verb)) continue;
}
/*
* Now we have found a special sentence !
*/
if (d_flag > 1)
debug_message("Local command %s on %s\n",
s->function, s->ob->name);
last_verb = verb_buff;
if (s->verb && s->verb[0]) {
strcpy(verb_buff, s->verb);
}
/*
* If the function is static and not defined by current object,
* then it will fail. If this is called directly from user input,
* then we set current_object so that static functions are allowed.
* current_object is reset just after the call to apply().
*/
if (current_object == 0)
current_object = s->ob;
/*
* Remember the object, to update moves.
*/
command_object = s->ob;
super = command_object->super;
if (s->flags & V_NOSPACE) {
push_constant_string(&buff[strlen(s->verb)]);
ret = apply(s->function,s->ob, 1);
} else if (buff[length] == ' ') {
push_constant_string(&buff[length+1]);
ret = apply(s->function, s->ob, 1);
} else {
ret = apply(s->function, s->ob, 0);
}
/* prevent an action from moving its associated object into another
another object prior to returning 0. closes a security hole
which was making the static keyword of no use on actions.
*/
if (IS_ZERO(ret) && (super != s->ob->super)) {
fprintf(stderr,
"** Check '%s' as a possible attempted breach of security **\n",
s->ob->name);
break;
}
if (current_object->flags & O_DESTRUCTED) {
/* If disable_commands() were called, then there is no
* command_giver any longer.
*/
if (command_giver == 0) {
return 1;
}
s = command_giver->sent; /* Restart :-( */
}
current_object = save_current_object;
last_verb = 0;
/* If we get fail from the call, it was wrong second argument. */
if (ret && ret->type == T_NUMBER && ret->u.number == 0)
continue;
if (command_giver) {
if (s && command_giver->interactive &&
!(command_giver->flags & O_IS_WIZARD))
add_moves (&command_object->stats, 1);
if (ret == 0)
add_message("Error: action %s not found.\n", s->function);
}
return 1;
}
notify_no_command();
return 0;
}
/*
* Associate a command with function in this object.
* The optional second argument is the command name. If the command name
* is not given here, it should be given with add_verb().
*
* The optinal third argument is a flag that will state that the verb should
* only match against leading characters.
*
* The object must be near the command giver, so that we ensure that the
* sentence is removed when the command giver leaves.
*
* If the call is from a shadow, make it look like it is really from
* the shadowed object.
*/
void add_action(str, cmd, flag)
char *str, *cmd;
int flag;
{
struct sentence *p;
struct object *ob;
if (str[0] == ':')
error("Illegal function name: %s\n", str);
if (current_object->flags & O_DESTRUCTED)
return;
ob = current_object;
#ifndef NO_SHADOWS
while (ob->shadowing) {
ob = ob->shadowing;
}
/* don't allow add_actions of a static function from a shadowing object */
if ((ob != current_object) && is_static(str, ob)) {
return;
}
#endif
if (command_giver == 0 || (command_giver->flags & O_DESTRUCTED))
return;
if (ob != command_giver && ob->super != command_giver &&
ob->super != command_giver->super && ob != command_giver->super)
return; /* No need for an error, they know what they did wrong. */
if (d_flag > 1)
debug_message("--Add action %s\n", str);
p = alloc_sentence();
p->function = make_shared_string(str);
p->ob = ob;
p->next = command_giver->sent;
p->flags = flag;
if (cmd)
p->verb = make_shared_string(cmd);
else
p->verb = 0;
command_giver->sent = p;
}
void add_verb(str, flag)
char *str;
int flag;
{
if (command_giver == 0 || (command_giver->flags & O_DESTRUCTED))
return;
if (command_giver->sent == 0)
error("No add_action().\n");
if (command_giver->sent->verb != 0)
error("Tried to set verb again.\n");
command_giver->sent->verb = make_shared_string(str);
if (flag)
command_giver->sent->flags |= V_NOSPACE;
if (d_flag > 1)
debug_message("--Adding verb %s to action %s\n", str,
command_giver->sent->function);
}
/*
* Remove sentence with specified verb and action. Return 1
* if success. If command_giver, remove his action, otherwise
* remove current_object's action.
*/
int remove_action(act, verb)
char *act, *verb;
{
struct object *ob;
struct sentence **s;
if (command_giver)
ob = command_giver;
else
ob = current_object;
if (!ob) return 0;
for (s = &ob->sent; *s; ) {
struct sentence *tmp;
if (((*s)->ob == ob) && !strcmp((*s)->function, act) &&
!strcmp((*s)->verb, verb)) {
tmp = *s;
*s = tmp->next;
free_sentence(tmp);
return 1;
}
s = &((*s)->next); /* Code look familiar? ;) */
}
return 0;
}
/*
* Remove all commands (sentences) defined by object 'ob' in object
* 'user'
*/
void remove_sent(ob, user)
struct object *ob, *user;
{
struct sentence **s;
for (s= &user->sent; *s;) {
struct sentence *tmp;
if ((*s)->ob == ob) {
if (d_flag > 1)
debug_message("--Unlinking sentence %s\n", (*s)->function);
tmp = *s;
*s = tmp->next;
free_sentence(tmp);
} else
s = &((*s)->next);
}
}
void debug_fatal(va_alist)
va_dcl
{
va_list args;
static char msg_buf[2049];
char *fmt;
static int in_fatal = 0;
/* Prevent double fatal. */
if (in_fatal)
abort();
in_fatal = 1;
va_start(args);
fmt = va_arg(args, char *);
vsprintf(msg_buf, fmt, args);
va_end(args);
fprintf(stderr, "%s", msg_buf);
fflush(stderr);
if (current_object)
(void)fprintf(stderr, "Current object was %s\n",
current_object->name);
debug_message("%s", msg_buf);
if (current_object)
debug_message("Current object was %s\n", current_object->name);
debug_message("Dump of variables:\n");
(void)dump_trace(1);
}
void fatal(va_alist)
va_dcl
{
va_list args;
char *fmt;
static char msg_buf[2049];
va_start(args);
fmt = va_arg(args, char *);
vsprintf(msg_buf, fmt, args);
debug_fatal("%s", msg_buf);
va_end(args);
#if !defined(DEBUG_NON_FATAL) || !defined(DEBUG)
abort();
#endif
}
int num_error = 0;
/*
* Error() has been "fixed" so that users can catch and throw them.
* To catch them nicely, we really have to provide decent error information.
* Hence, all errors that are to be caught
* (error_recovery_context_exists == 2) construct a string containing
* the error message, which is returned as the
* thrown value. Users can throw their own error values however they choose.
*/
/*
* This is here because throw constructs its own return value; we dont
* want to replace it with the system's error string.
*/
void throw_error() {
extern int error_recovery_context_exists;
extern jmp_buf error_recovery_context;
if (error_recovery_context_exists > 1) {
LONGJMP(error_recovery_context, 1);
fatal("Throw_error failed!");
}
error("Throw with no catch.\n");
}
static char emsg_buf[2000];
void error(va_alist)
va_dcl
{
extern int num_objects_this_thread;
extern int error_recovery_context_exists;
extern jmp_buf error_recovery_context;
extern struct object *current_heart_beat;
extern struct svalue catch_value;
extern int too_deep_error, max_eval_error;
char *object_name;
va_list args;
char *fmt;
va_start(args);
fmt = va_arg(args, char *);
vsprintf(emsg_buf + 1, fmt, args);
va_end(args);
emsg_buf[0] = '*'; /* all system errors get a * at the start */
num_objects_this_thread = 0; /* reset the count */
if (error_recovery_context_exists > 1) { /* user catches this error */
struct svalue v;
#ifdef LOG_CATCHES
/* This is added so that catches generate messages in the log file. */
debug_message("caught: %s", emsg_buf+1);
if (current_object)
debug_message("program: %s, object: %s line %d\n",
current_prog ? current_prog->name : "",
current_object->name,
get_line_number_if_any());
(void)dump_trace(0);
#endif
v.type = T_STRING;
v.u.string = emsg_buf;
v.subtype = STRING_MALLOC; /* Always reallocate */
assign_svalue(&catch_value, &v);
LONGJMP(error_recovery_context, 1);
fatal("Catch() longjump failed");
}
too_deep_error = max_eval_error = 0;
num_error++;
if (num_error > 1)
fatal("Too many simultaneous errors.\n");
debug_message("%s", emsg_buf+1);
if (current_object) {
add_errors (¤t_object->stats, 1);
debug_message("program: %s, object: %s line %d\n",
current_prog ? current_prog->name : "",
current_object->name,
get_line_number_if_any());
}
#if defined(DEBUG) && defined(TRACE_CODE)
object_name = dump_trace(1);
#else
object_name = dump_trace(0);
#endif
fflush(stdout);
if (object_name) {
struct object *ob;
ob = find_object2(object_name);
if (!ob) {
if (command_giver)
add_message("error when executing program in destroyed object %s\n",
object_name);
debug_message("error when executing program in destroyed object %s\n",
object_name);
}
}
if (command_giver && command_giver->interactive) {
num_error--;
/*
* The stack must be brought in a usable state. After the
* call to reset_machine(), all arguments to error() are invalid,
* and may not be used any more. The reason is that some strings
* may have been on the stack machine stack, and has been deallocated.
*/
reset_machine (0);
num_error++;
if ((command_giver->flags & O_IS_WIZARD) || !strlen(DEFAULT_ERROR_MESSAGE))
{
add_message("%s", emsg_buf+1);
if (current_object)
add_message("program: %s, object: %s line %d\n",
current_prog ? current_prog->name : "",
current_object->name,
get_line_number_if_any());
} else {
add_message("%s\n", DEFAULT_ERROR_MESSAGE);
}
}
if (current_heart_beat) {
set_heart_beat(current_heart_beat, 0);
debug_message("Heart beat in %s turned off.\n", current_heart_beat->name);
if (current_heart_beat->interactive) {
struct object *save_cmd = command_giver;
command_giver = current_heart_beat;
add_message("MudOS driver tells you: You have no heart beat!\n");
command_giver = save_cmd;
}
current_heart_beat = 0;
}
num_error--;
if (error_recovery_context_exists)
LONGJMP(error_recovery_context, 1);
abort();
}
/*
* This one is called from HUP.
*/
int MudOS_is_being_shut_down;
#if SIGNAL_FUNC_TAKES_INT
void startshutdownMudOS(arg)
int arg;
#else
void startshutdownMudOS()
#endif
{
MudOS_is_being_shut_down = 1;
}
/*
* This one is called from the command "shutdown".
* We don't call it directly from HUP, because it is dangerous when being
* in an interrupt.
*/
void shutdownMudOS(exit_code)
int exit_code;
{
shout_string("MudOS driver shouts: shutting down immediately.\n");
save_stat_files();
ipc_remove();
#ifdef LATTICE
signal(SIGUSR1, SIG_IGN);
signal(SIGTERM, SIG_IGN);
signal(SIGINT, SIG_IGN);
signal(SIGHUP, SIG_IGN);
signal(SIGALRM,SIG_IGN);
#endif
unlink_swap_file();
#ifdef DEALLOCATE_MEMORY_AT_SHUTDOWN
remove_all_objects();
free_all_sent();
free_mudlib_stats();
dump_malloc_data();
find_alloced_data();
#endif
#ifdef PROFILING
monitor(0,0,0,0,0); /* cause gmon.out to be written */
#endif
exit(exit_code);
}
/*
* Move or destruct one object.
*/
void move_or_destruct(what, to)
struct object *what, *to;
{
struct svalue v;
struct svalue *svp;
/* This is very dubious, why not just destruct them /JnA
*/
push_object(to);
push_number(1);
svp = apply("move", what, 2);
if (svp && svp->type == T_NUMBER && svp->u.number == 0)
return;
if (what->flags & O_DESTRUCTED)
return;
/*
* Failed to move the object. Then, it is destroyed.
*/
v.type = T_OBJECT;
v.u.ob = what;
destruct_object(&v);
}
/*
* Call this one when there is only little memory left. It will start
* Armageddon.
*/
void slow_shut_down(minutes)
int minutes;
{
/*
* Swap out objects, and free some memory.
*/
struct svalue *amo;
push_number(minutes);
amo=apply_master_ob("slow_shutdown",1);
if (IS_ZERO(amo))
/*if (IS_ZERO(apply_master_ob("slow_shutdown",1)))*/
{
struct object *save_current = current_object,
*save_command = command_giver;
command_giver = 0;
current_object = 0;
shout_string("MudOS driver shouts: Out of memory.\n");
command_giver = save_command;
current_object = save_current;
#if SIGNAL_FUNC_TAKES_INT
startshutdownMudOS(1);
#else
startshutdownMudOS();
#endif
return;
}
}
void do_message(class, msg, scope, exclude, recurse)
char *class, *msg;
struct vector *scope, *exclude;
int recurse;
{
int i, j, valid;
struct object *ob;
for (i = 0; i < scope->size; i++)
{
switch (scope->item[i].type)
{
case T_STRING:
ob = find_object(scope->item[i].u.string);
if (ob && !object_visible(ob))
ob = 0;
break;
case T_OBJECT:
ob = scope->item[i].u.ob;
break;
default:
ob = 0;
break;
}
if (!ob)
continue;
if (ob->flags & O_ENABLE_COMMANDS || ob->interactive)
{
for (valid = 1, j = 0; j < exclude->size; j++)
{
if (exclude->item[j].type != T_OBJECT)
continue;
if (exclude->item[j].u.ob == ob)
{
valid = 0;
break;
}
}
if (valid)
{
push_string(class, STRING_CONSTANT);
push_string(msg, STRING_CONSTANT);
apply("receive_message", ob, 2);
}
}
else if (recurse)
{
struct vector *tmp;
tmp = all_inventory(ob, 1);
do_message(class, msg, tmp, exclude, 0);
free_vector(tmp);
}
}
}
#ifdef LAZY_RESETS
INLINE void
try_reset(ob)
struct object *ob;
{
if ((ob->next_reset < current_time) && !(ob->flags & O_RESET_STATE)) {
if(d_flag) {
fprintf(stderr, "(lazy) RESET %s\n", ob->name);
}
/* need to set the flag here to prevent infinite loops in apply_low */
ob->flags |= O_RESET_STATE;
reset_object(ob, 1);
}
}
#endif