#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <setjmp.h>
#include <string.h>
#include <errno.h>
#include <stdio.h>
#include <memory.h>
#include <varargs.h>
#if defined(sun)
#include <alloca.h>
#endif
#if defined(M_UNIX) || defined(_SEQUENT_) || defined(SOLARIS) || defined(__NetBSD__)
#include <dirent.h>
#else
#include <sys/dir.h>
#endif
#include "config.h"
#include "lint.h"
#include "stdio.h"
#include "interpret.h"
#include "object.h"
#include "sent.h"
#include "exec.h"
#include "comm.h"
#include "mudstat.h"
#include "incralloc.h"
extern int errno;
extern int comp_flag;
char *inherit_file;
#if !defined(NeXT) && !defined(LINUX)
extern int lstat (const char *, struct stat *);
#else
#define lstat stat
#endif
#ifndef LINUX
extern int fchmod (int, mode_t);
#endif
char *last_verb;
extern int current_time, tot_alloc_dest_object, tot_removed_object;
extern int special_parse (char *),
set_call (struct object *, struct sentence *, int),
legal_path (char *);
void pre_compile (char *),
remove_interactive (struct object *, int);
int add_action (struct svalue *, struct svalue *, int);
void ipc_remove(),
show_info_about (char *, char *, struct interactive *),
print_lnode_status (int),
remove_all_players(), end_new_file(),
load_ob_from_swap (struct object *),
print_svalue (struct svalue *, struct object *),
debug_message_value(),
destruct2();
char *dump_malloc_data();
void start_new_file (FILE *);
extern int d_flag, s_flag;
struct object *obj_list, *obj_list_destruct, *master_ob;
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. */
void shutdowngame();
extern void flush_all_player_mess();
struct object* find_object_no_create();
int
find_status(struct program *prog, char *str, int not_type)
{
int i, j;
char *super_name = 0;
char *sub_name;
char *real_name;
char *search;
variable_index_found = variable_inherit_found = 255;
real_name = strrchr(str, ':') + 1;
sub_name = strchr(str, ':') + 2;
if(!(real_name = (findstring((real_name == (char *)1) ? str : real_name))))
return -1;
if (sub_name == (char *)2)
{
access_program(prog);
for (i = 0; i < (int)prog->num_variables; i++)
{
if (prog->variable_names[i].name == real_name &&
!(prog->variable_names[i].type & not_type))
{
variable_index_found = i;
variable_inherit_found = prog->num_inherited - 1;
variable_type_mod_found = prog->variable_names[i].type;
return prog->inherit[variable_inherit_found].variable_index_offset + i;
}
}
}
else if (sub_name - str > 2)
{
super_name = xalloc(sub_name - str - 1);
memcpy(super_name, str, sub_name - str - 2);
super_name[sub_name - str - 2] = 0;
if (!strcmp(super_name, "this"))
return find_status(prog, sub_name, not_type);
}
else
str = sub_name;
for(j = prog->num_inherited - 2; j >= 0 ;j -= prog->inherit[j].prog->num_inherited)
{
int type;
if (super_name &&
!strcmp(super_name, prog->inherit[j].name))
search = sub_name;
else
search = str;
if (find_status(prog->inherit[j].prog, search, not_type) != -1)
{
int type = prog->inherit[j].type;
if (variable_type_mod_found & TYPE_MOD_PUBLIC)
type &= ~TYPE_MOD_PRIVATE;
if (variable_type_mod_found & TYPE_MOD_PRIVATE)
type &= ~TYPE_MOD_PUBLIC;
variable_type_mod_found |= type & TYPE_MOD_MASK;
if (variable_type_mod_found & not_type)
continue;
variable_inherit_found += j -
(prog->inherit[j].prog->num_inherited - 1);
return prog->inherit[variable_inherit_found].variable_index_offset +
variable_index_found;
}
}
return -1;
}
void
swap_objects(struct object *ob1, struct object *ob2)
{
extern void call_out_swap_objects(struct object *, struct object *);
extern void stack_swap_objects(struct object *, struct object *);
struct program *tmp_prog;
struct svalue *tmp_var;
remove_ob_from_swap(ob1, 1);
remove_ob_from_swap(ob2, 1);
/* swap the objects */
call_out_swap_objects(ob1, ob2);
stack_swap_objects(ob1, ob2);
tmp_var = ob1->variables;
ob1->variables = ob2->variables;
ob2->variables = tmp_var;
tmp_prog = ob1->prog;
ob1->prog = ob2->prog;
ob2->prog = tmp_prog;
/* Swap the O_CREATED bit */
ob1->flags ^= ob2->flags & O_CREATED;
ob2->flags ^= ob1->flags & O_CREATED;
ob1->flags ^= ob2->flags & O_CREATED;
}
/*
* 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.
*
* Save the command_giver, because reset() in the new object might change
* it.
*
*
*/
char *current_loaded_file = 0;
#ifdef BINARIES
extern int save_binary(struct program *);
#endif
struct object *
load_object(char *lname, int dont_reset, struct object *old_ob)
{
FILE *f;
extern int total_lines;
extern int approved_object;
struct object *ob, *save_command_giver = command_giver;
extern struct program *prog;
extern char *current_file;
struct stat c_st;
int name_length;
char real_name[200], name[200];
extern struct object *swap_ob;
char new_ob_name[200];
extern int pragma_resident;
extern void init_smart_log(), dump_smart_log();
#ifdef BINARIES
extern int pragma_save_binary, driver_mtime;
int errbin = 1;
int file_mtime;
char *binname;
FILE *binfile;
#endif
/* 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;
}
if (old_ob)
{
struct object *invalid_ob;
strcpy(new_ob_name, name);
strcat(new_ob_name, "#0");
if (invalid_ob = find_object2(new_ob_name))
{
destruct_object(invalid_ob);
}
}
/*
* First check that the c-file exists.
*/
(void)strcpy(real_name, name);
(void)strcat(real_name, ".c");
if (stat(real_name, &c_st) == -1)
{
fprintf(stderr, "Could not load descr for %s\n", real_name);
error("Failed to load file.\n");
return 0;
}
#ifdef BINARIES
file_mtime = c_st.st_mtime;
#endif
/*
* Check if it is a legal name.
*/
if (!legal_path(real_name))
{
fprintf(stderr, "Illegal pathname: %s\n", real_name);
error("Illegal path name.\n");
return 0;
}
#ifdef BINARIES
binname = (char *)xalloc(strlen(BINARIES) +
strlen(real_name) + 1);
sprintf(binname, "%s%s", BINARIES, real_name);
errbin = 1;
if (stat(binname, &c_st) != -1)
if ((file_mtime < c_st.st_mtime))
if ((binfile = fopen(binname, "r")) != NULL)
{
errbin = load_binary(binfile, real_name);
if (!errbin && !inherit_file)
{
if (comp_flag)
fprintf(stderr," %s loaded from binary: %s\n",
real_name, binname);
}
}
free(binname);
pragma_save_binary = 0;
if (errbin) {
#endif /* BINARIES */
if (comp_flag)
fprintf(stderr, " compiling %s ...", real_name);
f = fopen(real_name, "r");
if (s_flag)
{
num_fileread++;
num_compile++;
}
if (f == 0)
{
perror(real_name);
error("Could not read the file.\n");
}
init_smart_log();
start_new_file(f);
current_file = string_copy(real_name); /* This one is freed below */
current_loaded_file = real_name;
compile_file();
end_new_file();
current_loaded_file = 0;
if (comp_flag)
{
if (inherit_file == 0)
fprintf(stderr, " done\n");
else
fprintf(stderr, " suspended, compiling inherited file(s)\n");
}
update_compile_av(total_lines);
total_lines = 0;
(void)fclose(f);
free(current_file);
current_file = 0;
dump_smart_log();
/* Sorry, can not 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");
}
#ifdef BINARIES
}
#endif
/*
* 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;
}
{
char *t1 = tmp, *t2 = strrchr(tmp,'\000');
while (*t1=='/')
t1++;
if (t2-t1 > 1)
if (*(--t2)=='c')
if (*(--t2)=='.')
*t2='\000';
if (strcmp(t1, name) == 0)
{
free(inherit_file);
inherit_file = 0;
error("Illegal to inherit self.\n");
}
}
/* Extreme ugliness begins. inherit_file must be 0 when we call
load_object, but tmp is used as a parameter, so we can not free
the string until after the call
*/
inherit_file = 0;
load_object(tmp, 1, 0);
free(tmp);
/* Extreme ugliness ends */
ob = load_object(name, dont_reset, old_ob);
return ob;
}
ob = get_empty_object();
if (!old_ob)
ob->name = string_copy(name); /* Shared string is no good here */
else
{
ob->name = xalloc(strlen(name) + 3);
strcpy(ob->name, name);
strcat(ob->name, "#0");
}
ob->prog = prog;
#ifdef BINARIES
if (pragma_save_binary)
{
save_binary(ob->prog);
pragma_save_binary = 0;
}
#endif /* BINARIES */
swap_lineno(ob->prog);
if (obj_list)
{
ob->next_all = obj_list;
ob->prev_all = obj_list->prev_all;
obj_list->prev_all->next_all = ob;
obj_list->prev_all = ob;
obj_list = ob;
}
else
obj_list = swap_ob = ob->next_all = ob->prev_all = ob;
if (current_object)
access_object(current_object);
/*
add name to fast object lookup table
*/
enter_object_hash(ob);
{
int num_var, i;
extern int tot_alloc_variable_size;
extern struct svalue const0;
num_var = ob->prog->num_variables +
ob->prog->inherit[ob->prog->num_inherited - 1]
.variable_index_offset + 1;
if (ob->variables)
fatal("Object allready initialized!\n");
ob->variables = (struct svalue *)
xalloc(num_var * sizeof(struct svalue));
tot_alloc_variable_size += num_var * sizeof(struct svalue);
for (i= 0; i<num_var; i++)
ob->variables[i] = const0;
ob->variables++;
}
/*
We ought to add a flag here marking the object as unfinished
so it can be removed if the following code causes an LPC error
*/
if (master_ob)
{
push_object(current_object);
push_object(ob);
apply_master_ob(M_LOADED_OBJECT, 2);
}
if (!(ob->flags & (O_CREATED | O_DESTRUCTED)) && !dont_reset)
create_object(ob);
if (ob->flags & O_DESTRUCTED)
return 0;
command_giver = save_command_giver;
if (d_flag & DEBUG_LOAD && ob)
debug_message("--%s loaded\n", ob->name);
if (master_ob && ob->prog->flags & PRAGMA_RESIDENT)
{
struct svalue *ret;
push_object(ob);
ret = apply_master_ob(M_VALID_RESIDENT, 1);
if (!ret || ret->u.number == 0)
ob->prog->flags &= ~PRAGMA_RESIDENT;
}
return ob;
}
char *
make_new_name(char *str)
{
static int i = 1;
char *p = xalloc(strlen(str) + 10);
(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(char *str1)
{
struct object *ob, *new_ob;
struct object *save_command_giver = command_giver;
extern struct object *swap_ob;
ob = find_object_no_create(str1);
if (ob == 0 || ob->super || (ob->flags & O_CLONE) || ob->prog->flags & PRAGMA_NO_CLONE)
error("Cloning a bad object! (No Master, Master in environment or Master is clone)\n");
/* We do not want the heart beat to be running for unused copied objects */
new_ob = get_empty_object();
new_ob->name = make_new_name(ob->name);
new_ob->flags |= O_CLONE;
new_ob->prog = ob->prog;
reference_prog (ob->prog, "clone_object");
if (!current_object)
fatal("clone_object() from no current_object !\n");
if (obj_list)
{
new_ob->next_all = obj_list;
new_ob->prev_all = obj_list->prev_all;
obj_list->prev_all->next_all = new_ob;
obj_list->prev_all = new_ob;
obj_list = new_ob;
}
else
obj_list = swap_ob = new_ob->next_all = new_ob->prev_all = new_ob;
enter_object_hash(new_ob); /* Add name to fast object lookup table */
{
int num_var, i;
extern int tot_alloc_variable_size;
extern struct svalue const0;
num_var = new_ob->prog->num_variables +
new_ob->prog->inherit[ob->prog->num_inherited - 1]
.variable_index_offset + 1;
if (new_ob->variables)
fatal("Object allready initialized!\n");
new_ob->variables = (struct svalue *)
xalloc(num_var * sizeof(struct svalue));
tot_alloc_variable_size += num_var * sizeof(struct svalue);
for (i= 0; i<num_var; i++)
new_ob->variables[i] = const0;
new_ob->variables++;
}
if (master_ob)
{
push_object(current_object);
push_object(new_ob);
apply_master_ob(M_CLONED_OBJECT, 2);
if (new_ob->flags & O_DESTRUCTED)
return 0;
}
if (!(ob->flags & O_CREATED))
create_object(new_ob);
command_giver = save_command_giver;
/* Never know what can happen ! :-( */
if (new_ob->flags & O_DESTRUCTED)
return 0;
return new_ob;
}
struct object *
environment(struct svalue *arg)
{
struct object *ob = current_object;
if (arg && arg->type == T_OBJECT)
ob = arg->u.ob;
else if (arg && arg->type == T_STRING)
ob = find_object2(arg->u.string);
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 players to do illegal things.
*
* Return cost of the command executed if success (> 0).
* When failure, return 0.
*/
int
command_for_object(char *str, struct object *ob)
{
char buff[1000];
extern int eval_cost;
int save_eval_cost = eval_cost - 1000;
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;
else
return 0;
}
/*
* Search the inventory of the second argument 'ob' for instances
* of the first.
*/
static struct object *object_present2 (char *, struct object *);
struct object *
object_present(struct svalue *v, struct svalue *where)
{
int i;
struct object *ret;
if (v->type == T_OBJECT)
if (v->u.ob->flags & O_DESTRUCTED)
return 0;
switch (where->type)
{
case T_OBJECT:
if (where->u.ob->flags & O_DESTRUCTED)
return 0;
if (v->type == T_OBJECT)
{
if (v->u.ob->super == where->u.ob)
return v->u.ob;
else
return 0;
}
else
return object_present2(v->u.string, where->u.ob->contains);
break;
case T_POINTER:
if (v->type == T_OBJECT)
{
for (i = 0 ; i < where->u.vec->size ; i++)
{
if (where->u.vec->item[i].type != T_OBJECT)
continue;
if (v->u.ob->super == where->u.vec->item[i].u.ob)
return v->u.ob;
}
}
else
{
for (i = 0 ; i < where->u.vec->size ; i++)
{
if (where->u.vec->item[i].type != T_OBJECT)
continue;
if (ret =
object_present2(v->u.string,
where->u.vec->item[i].u.ob->contains))
return ret;
}
}
return 0;
break;
default:
error("Strange type %d in object_present.\n", where->type);
return 0;
break;
}
}
static struct object *
object_present2(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_MALLOC);
ret = apply("id", ob, 1, 1);
if (ob->flags & O_DESTRUCTED)
{
free(item);
return 0;
}
if (ret == 0 || (ret->type == T_NUMBER && ret->u.number == 0))
continue;
if (count-- > 0)
continue;
free(item);
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()).
*
* We will simply not allow the master object to be destructed, Ha!
*/
#ifdef BINARIES
extern int save_binary(struct program *);
#endif
void
destruct_object(struct object *ob)
{
extern struct object *swap_ob;
extern struct object *vbfc_object;
struct svalue *ret;
struct object *super;
struct object **pp;
int removed;
char *name = 0;
int i;
FILE *f;
if (!ob || ob->flags & O_DESTRUCTED)
return;
/*
* If we are to destruct the master ob or the simul_efun ob
* it must be reloadable.
*/
if ( (!(ob->flags & O_CLONE) && ob->prog->flags & PRAGMA_RESIDENT) ||
ob == master_ob || ob == (struct object *)query_simul_efun_ob())
{
struct object *new_ob;
new_ob = load_object(ob->name, 1, ob);
if (!new_ob)
error("Can not compile the new %s, aborting destruction of the old.\n", ob->prog->name);
/* Make sure that the master object and the simul_efun object
* stays loaded */
if (ob == master_ob || ob == (struct object *)query_simul_efun_ob())
ob->prog->flags |= PRAGMA_RESIDENT;
swap_objects(ob, new_ob);
if (ob == master_ob)
{
extern void master_ob_loaded(void);
master_ob_loaded();
}
if (new_ob && new_ob->flags & O_CREATED)
recreate_object(ob, new_ob);
ob = new_ob;
ob->prog->flags &= ~PRAGMA_RESIDENT;
}
push_object(ob);
apply_master_ob(M_DESTRUCTED_OBJECT, 1);
if (ob->flags & O_DESTRUCTED) return;
/*
* If this is the first object being shadowed by another object, then
* destruct the whole list of 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;
if (svp.u.ob->shadowed) {
free_object(svp.u.ob->shadowed, "destruct_object-5");
svp.u.ob->shadowed = 0;
}
if (svp.u.ob->shadowing) {
free_object(svp.u.ob->shadowing, "destruct_object-6");
svp.u.ob->shadowing = 0;
}
destruct_object(ob2);
if (ob->flags & O_DESTRUCTED) return;
}
}
/*
* The chain of shadows is a double linked list. Take care to update
* it correctly.
*/
if (ob->shadowing) {
change_ref(ob->shadowing->shadowed, ob->shadowed, "destruct_object-1");
ob->shadowing->shadowed = ob->shadowed;
}
if (ob->shadowed) {
change_ref(ob->shadowed->shadowing, ob->shadowing, "destruct_object-2");
ob->shadowed->shadowing = ob->shadowing;
}
if (ob->shadowing) {
free_object(ob->shadowing, "destruct_object-3");
ob->shadowing = 0;
}
if (ob->shadowed) {
free_object(ob->shadowed, "destruct_object-4");
ob->shadowed = 0;
}
if (d_flag & DEBUG_DESTRUCT)
debug_message("Destruct object %s (ref %d)\n", ob->name, ob->ref);
super = ob->super;
/*
* 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(M_DESTRUCT_ENVIRONMENT_OF,1);
if (svp.u.ob == ob->contains)
destruct_object(ob->contains);
if (ob->flags & O_DESTRUCTED) return;
}
}
if (ob->interactive)
{
struct object *save=command_giver;
command_giver=ob;
if (ob->interactive->ed_buffer)
{
extern void save_ed_buffer();
save_ed_buffer();
}
flush_all_player_mess();
command_giver=save;
remove_interactive(ob, 0);
if (ob->flags & O_DESTRUCTED) return;
}
/*
* 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);
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;
}
}
/* Call destructors */
if (ob->flags & O_CREATED)
{
for (i = ob->prog->num_inherited - 1; i >= 0; i--)
if (!(ob->prog->inherit[i].type & TYPE_MOD_SECOND) &&
ob->prog->inherit[i].prog->dtor_index !=
(unsigned short) -1)
{
call_function(ob, i, ob->prog->inherit[i].prog->
dtor_index, 0);
pop_stack();
if (ob->flags & O_DESTRUCTED) return;
}
}
remove_object_from_stack(ob);
/*
* 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.
*/
if (ob == swap_ob)
swap_ob = ob->prev_all;
if (ob == obj_list)
obj_list = ob->next_all;
ob->prev_all->next_all = ob->next_all;
ob->next_all->prev_all = ob->prev_all;
if (ob->next_all == ob)
obj_list = swap_ob = 0;
delete_all_calls(ob);
remove_object_hash(ob);
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;
ob->prev_all = 0;
obj_list_destruct = ob;
ob->flags |= O_DESTRUCTED;
if (!(ob->flags & O_CLONE))
ob->prog->flags |= P_OLD;
tot_alloc_dest_object++;
if (ob == vbfc_object)
{
free_object(vbfc_object, "destruct_object");
vbfc_object = 0;
ret = apply_master_ob(M_GET_VBFC_OBJECT, 0);
if (ret && ret->type == T_OBJECT)
{
vbfc_object = ret->u.ob;
vbfc_object->ref++;
}
}
}
/*
* This one is called when no program is executing from the main loop.
*/
void
destruct2(struct object *ob)
{
struct sentence *s;
int num_var;
extern int tot_alloc_variable_size;
if (d_flag & DEBUG_DESTRUCT)
debug_message("Destruct-2 object %s (ref %d)\n", ob->name, ob->ref);
if (ob->interactive)
remove_interactive(ob, 0);
remove_ob_from_swap(ob);
/*
* 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.
*/
{
/*
* Deallocate variables in this object.
*/
int i;
num_var = ob->prog->num_variables +
ob->prog->inherit[ob->prog->num_inherited - 1].
variable_index_offset + 1;
ob->variables--;
for (i=0; i < num_var; i++) {
free_svalue(&ob->variables[i]);
ob->variables[i].type = T_NUMBER;
ob->variables[i].u.number = 0;
}
free((char *)(ob->variables));
ob->variables = 0;
tot_alloc_variable_size -= num_var * sizeof(struct svalue);
}
free_prog(ob->prog, 1);
ob->prog = 0;
for (s = ob->sent; s;)
{
struct sentence *next;
next = s->next;
free_sentence(s);
s = next;
}
delete_all_calls(ob);
free_object(ob, "destruct_object");
}
/*
* This will enable an object to use commands normally only
* accessible by interactive players.
* Also check if the player is a wizard. Wizards must not affect the
* value of the wizlist ranking.
*/
void
enable_commands(int num)
{
if (current_object->flags & O_DESTRUCTED)
return;
if (d_flag & DEBUG_LIVING)
{
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;
if (command_giver == current_object)
command_giver = 0;
}
}
/*
* Set up a function in this object to be called with the next
* user input string.
*/
int
input_to(char *fun, int flag, struct vector *v)
{
struct sentence *s;
struct svalue *ret;
if (!command_giver || command_giver->flags & O_DESTRUCTED)
return 0;
s = alloc_sentence();
if (set_call(command_giver, s, flag))
{
s->function = make_shared_string(fun);
s->ob = current_object;
add_ref(current_object, "input_to");
free_vector(command_giver->interactive->carryover);
command_giver->interactive->carryover = v;
return 1;
}
else
free_sentence(s);
return 0;
}
#define MAX_LINES 50
/*
* This one is used by qsort in get_dir().
*/
static int
pstrcmp(struct svalue *p1, struct svalue *p2)
{
return strcmp(p1->u.string, p2->u.string);
}
/*
* List files in directory. This function do same as standard list_files did,
* but instead writing files right away to player this returns an array
* containing those files. Actually most of code is copied from list_files()
* function.
* Differences with list_files:
*
* - file_list("/w"); returns ({ "w" })
*
* - file_list("/w/"); and file_list("/w/."); return contents of directory
* "/w"
*
* - file_list("/");, file_list("."); and file_list("/."); return contents
* of directory "/"
*/
struct vector *
get_dir(char *path)
{
struct vector *v;
int i, count = 0;
DIR *dirp;
int namelen, do_match = 0;
#if defined(_AIX) || defined(M_UNIX) || defined(__osf__) || defined(_SEQUENT_) || defined(SOLARIS) || defined(__NetBSD__)
struct dirent *de;
#else
struct direct *de;
#endif
struct stat st;
char *temppath;
char *p;
char *regexp = 0;
if (!path)
return 0;
path = check_valid_path(path, current_object, "get_dir", 0);
if (path == 0)
return 0;
/*
* We need to modify the returned path, and thus to make a
* writeable copy.
* The path "" needs 2 bytes to store ".\0".
*/
temppath = (char *) alloca(strlen(path) + 2);
if (strlen(path)<2) {
temppath[0] = path[0] ? path[0] : '.';
temppath[1] = '\000';
p = temppath;
} else {
strcpy(temppath, path);
/*
* If path ends with '/' or "/." remove it
*/
if ((p = strrchr(temppath, '/')) == 0)
p = temppath;
if ((p[0] == '/' && p[1] == '.' && p[2] == '\0') ||
(p[0] == '/' && p[1] == '\0'))
*p = '\0';
}
if (stat(temppath, &st) < 0)
{
if (*p == '\0')
return 0;
regexp = (char *)alloca(strlen(p)+2);
if (p != temppath)
{
strcpy(regexp, p + 1);
*p = '\0';
}
else
{
strcpy(regexp, p);
strcpy(temppath, ".");
}
do_match = 1;
}
else if (*p != '\0' && strcmp(temppath, "."))
{
if (*p == '/' && *(p + 1) != '\0')
p++;
v = allocate_array(1);
v->item[0].type = T_STRING;
v->item[0].string_type = STRING_MALLOC;
v->item[0].u.string = string_copy(p);
return v;
}
if ((dirp = opendir(temppath)) == 0)
return 0;
/*
* Count files
*/
for (de = readdir(dirp); de; de = readdir(dirp))
{
#if defined(M_UNIX) || defined(_SEQUENT_) || defined(SOLARIS)
namelen = strlen(de->d_name);
#else
namelen = de->d_namlen;
#endif
if (!do_match && (strcmp(de->d_name, ".") == 0 ||
strcmp(de->d_name, "..") == 0))
continue;
if (do_match && !match_string(regexp, de->d_name))
continue;
count++;
if ( count >= MAX_ARRAY_SIZE)
break;
}
/*
* Make array and put files on it.
*/
v = allocate_array(count);
if (count == 0)
{
/* This is the easy case */
closedir(dirp);
return v;
}
rewinddir(dirp);
for(i = 0, de = readdir(dirp); i < count; de = readdir(dirp))
{
#if defined(M_UNIX) || defined(_SEQUENT_) || defined(SOLARIS)
namelen = strlen(de->d_name);
#else
namelen = de->d_namlen;
#endif
if (!do_match && (strcmp(de->d_name, ".") == 0 ||
strcmp(de->d_name, "..") == 0))
continue;
if (do_match && !match_string(regexp, de->d_name))
continue;
de->d_name[namelen] = '\0';
v->item[i].type = T_STRING;
v->item[i].string_type = STRING_MALLOC;
v->item[i].u.string = string_copy(de->d_name);
i++;
}
closedir(dirp);
/* Sort the names. */
qsort((char *)v->item, count, sizeof v->item[0], pstrcmp);
return v;
}
int
tail(char *path)
{
char buff[1000];
FILE *f;
struct stat st;
int offset;
path = check_valid_path(path, current_object, "tail", 0);
if (path == 0)
return 0;
f = fopen(path, "r");
if (s_flag)
num_fileread++;
if (f == 0)
return 0;
if (fstat(fileno(f), &st) == -1)
fatal("Could not stat an open file.\n");
offset = st.st_size - 54 * 20;
if (offset < 0)
offset = 0;
if (fseek(f, offset, 0) == -1)
fatal("Could not seek.\n");
/* Throw away the first incomplete line. */
if (offset > 0)
(void) fgets(buff, sizeof buff, f);
while(fgets(buff, sizeof buff, f))
add_message("%s", buff);
fclose(f);
return 1;
}
int
remove_file(char *path)
{
path = check_valid_path(path, current_object, "remove_file", 1);
if (path == 0)
return 0;
if (unlink(path) == -1)
return 0;
return 1;
}
void
print_svalue(struct svalue *arg, struct object *ob)
{
if (arg == 0)
a_msg(ob,"<NULL>");
else if (arg->type == T_STRING)
{
if (strlen(arg->u.string) > 9500) /* Not pretty */
error("Too long string.\n");
/* Strings sent to monsters are now delivered */
if (ob && (ob->flags & O_ENABLE_COMMANDS) &&
!ob->interactive)
tell_npc(ob, arg->u.string);
else
a_msg(ob,"%s", arg->u.string);
} else if (arg->type == T_OBJECT)
a_msg(ob,"OBJ(%s)", arg->u.ob->name);
else if (arg->type == T_NUMBER)
a_msg(ob,"%d", arg->u.number);
else if (arg->type == T_POINTER)
a_msg(ob,"<ARRAY>");
else
a_msg(ob,"<UNKNOWN>");
}
a_msg(ob,a,b,c,d,e)
struct object *ob;
char *a;
int b,c,d,e;
{
struct object *save_cmd_giver = command_giver;
command_giver = ob;
add_message(a,b,c,d,e);
command_giver = save_cmd_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(char *str)
{
struct object *ob;
/* Remove leading '/' if any. */
while(str[0] == '/')
str++;
ob = find_object2(str);
if (ob)
return ob;
ob = load_object(str, 0, 0);
if (!ob || ob->flags & O_DESTRUCTED) /* *sigh* */
return 0;
return ob;
}
/* same as find_object, but don't call 'create()' if not loaded */
struct object *find_object_no_create(str)
char *str;
{
struct object *ob;
/* Remove leading '/' if any. */
while(str[0] == '/')
str++;
ob = find_object2(str);
if (ob)
return ob;
ob = load_object(str, 1, 0);
if (!ob || ob->flags & O_DESTRUCTED) /* *sigh* */
return 0;
return ob;
}
/* Look for a loaded object. Return 0 if non found. */
struct object *
find_object2(char *str)
{
register struct object *ob;
register int length;
/* 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 writreable copy of the name is needed. */
char *p;
p = (char *)alloca(strlen(str)+1);
strcpy(p, str);
str = p;
str[length-2] = '\0';
}
if (ob = lookup_object_hash(str)) {
return ob;
}
return 0;
}
#if 0
void
apply_command(char *com)
{
struct value *ret;
if (command_giver == 0)
error("command_giver == 0 !\n");
ret = apply(com, command_giver->super, 0, 1);
if (ret != 0)
{
add_message("Result:");
if (ret->type == T_STRING)
add_message("%s\n", ret->u.string);
if (ret->type == T_NUMBER)
add_message("%d\n", ret->u.number);
}
else
add_message("Error apply_command: function %s not found.\n", com);
}
#endif /* 0 */
/*
* Update actions
*
* Update an objects set of actions in all 'nearby' living objects.
*
* NOTE!
* It removes ALL actions defined by itself, including those added
* to itself. As actions added to onself is normally not done in init()
* this will have to be taken care of specifically. This should be of
* little problem as living objects seldom export actions and would
* therefore have little use of this efun.
*/
void
update_actions(struct object *aob)
{
struct object *pp, *ob, *next_ob;
struct object *save_cmd = command_giver, *item = aob;
struct svalue *ret;
/*
* Remove actions in objects environment and from objects in the
* same environment. (parent and siblings)
*/
if (item->super)
{
if (item->super->flags & O_ENABLE_COMMANDS)
remove_sent(item, item->super);
for (pp = item->super->contains; pp;)
{
if (pp != item && (pp->flags & O_ENABLE_COMMANDS))
remove_sent(item, pp);
pp = item->next_inv;
}
}
/*
* Remove actions from objects inside the object. (children)
*/
for (pp = item->contains; pp;)
{
if (pp->flags & O_ENABLE_COMMANDS)
remove_sent(item, pp);
pp = item->next_inv;
}
/*
* Setup the new commands. The order is very important, as commands
* in the room should override commands defined by the room.
* Beware that init() may have moved 'item' !
*/
/* Save where object is, so as to note a move in init()
*/
ob = item->super;
/*
* Add actions in objects environment and to objects in the
* same environment. (parent and siblings)
*/
if (item->super)
{
if (item->super->flags & O_ENABLE_COMMANDS)
{
command_giver = item->super;
#ifdef USE_ENCOUNTER_NOT_INIT
push_object(item);
ret = apply("encounter", item->super, 1, 1);
if (!ret)
(void)apply("init", item, 0, 1);
#else
(void)apply("init", item, 0, 1);
#endif
if (ob != item->super ||
item->flags & O_DESTRUCTED ||
ob->flags & O_DESTRUCTED)
{
command_giver = save_cmd; /* marion */
return;
}
}
for (pp = item->super->contains; pp;)
{
next_ob = pp->next_inv;
if (pp != item && (pp->flags & O_ENABLE_COMMANDS))
{
command_giver = pp;
#ifdef USE_ENCOUNTER_NOT_INIT
push_object(item);
ret = apply("encounter", pp, 1, 1);
if (!ret)
(void)apply("init", item, 0, 1);
#else
(void)apply("init", item, 0, 1);
#endif
if (ob != item->super ||
item->flags & O_DESTRUCTED ||
ob->flags & O_DESTRUCTED ||
next_ob->flags & O_DESTRUCTED)
{
command_giver = save_cmd; /* marion */
return;
}
}
pp = next_ob;
}
}
/*
* Add actions to objects inside the object. (children)
*/
for (pp = item->contains; pp;)
{
next_ob = pp->next_inv;
if (pp->flags & O_ENABLE_COMMANDS)
{
command_giver = pp;
#ifdef USE_ENCOUNTER_NOT_INIT
push_object(item);
ret = apply("encounter", pp, 1, 1);
if (!ret)
(void)apply("init", item, 0, 1);
#else
(void)apply("init", item, 0, 1);
#endif
if (ob != item->super ||
item->flags & O_DESTRUCTED ||
ob->flags & O_DESTRUCTED ||
next_ob->flags & O_DESTRUCTED)
{
command_giver = save_cmd; /* marion */
return;
}
}
pp = next_ob;
}
}
/*
* 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(struct object *dest)
{
struct object **pp, *ob, *next_ob;
struct object *save_cmd = command_giver, *item = current_object;
struct svalue *ret;
if (item->flags & O_DESTRUCTED)
return;
/* Recursive moves are not allowed. */
for (ob = dest; ob; ob = ob->super)
if (ob == item)
error("Can't move object inside itself.\n");
if (item->shadowing)
error("Can't move an object that is shadowing.\n");
/*
* The master object approves objects. Approved and nonapproved
* objects can not be moved into each other.
*/
if (s_flag)
num_move++;
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);
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
* It might be too slow, though :-(
*/
if (item->flags & O_ENABLE_COMMANDS)
{
command_giver = item;
#ifdef USE_ENCOUNTER_NOT_INIT
push_object(dest);
ret = apply("encounter", item, 1, 1);
if (!ret)
(void)apply("init", dest, 0, 1);
#else
(void)apply("init", dest, 0, 1);
#endif
if ((dest->flags & O_DESTRUCTED) || item->super != dest)
{
command_giver = save_cmd; /* marion */
return;
}
}
/*
* Run init of the item once for every present player, and
* for the environment (which can be a player).
*/
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;
#ifdef USE_ENCOUNTER_NOT_INIT
push_object(item);
ret = apply("encounter", ob, 1, 1);
if (!ret)
(void)apply("init", item, 0, 1);
#else
(void)apply("init", item, 0, 1);
#endif
if (dest != item->super)
{
command_giver = save_cmd;
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;
#ifdef USE_ENCOUNTER_NOT_INIT
push_object(ob);
ret = apply("encounter", item, 1, 1);
if (!ret)
(void)apply("init", ob, 0, 1);
#else
(void) apply("init", ob, 0, 1);
#endif
if (dest != item->super)
{
command_giver = save_cmd;
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;
#ifdef USE_ENCOUNTER_NOT_INIT
push_object(item);
ret = apply("encounter", dest, 1, 1);
if (!ret)
(void)apply("init", item, 0, 1);
#else
(void)apply("init", item, 0, 1);
#endif
}
command_giver = save_cmd;
}
struct sentence *sent_free = 0;
int tot_alloc_sentence;
int tot_current_alloc_sentence;
struct sentence *
alloc_sentence()
{
struct sentence *p;
p = (struct sentence *) xalloc(sizeof *p);
tot_alloc_sentence++;
tot_current_alloc_sentence++;
p->verb = 0;
p->function = 0;
p->next = 0;
p->ob = 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(struct sentence *p)
{
if (p->ob)
free_object(p->ob, "free_sentence");
p->ob = 0;
if (p->function)
free_string(p->function);
p->function = 0;
if (p->verb)
free_string(p->verb);
p->verb = 0;
free((char *)p);
tot_current_alloc_sentence--;
tot_alloc_sentence--;
}
/*
* Find the sentence for a command from the player.
* Return success status.
*/
int
player_parser(char *buff)
{
struct sentence *s;
char *p;
int length;
struct object *save_current_object = current_object;
char verb_copy[100], *subst_buff = NULL;
struct svalue *ret;
struct object *cmd_giver = command_giver;
#ifdef DEBUG
if (d_flag & DEBUG_COMMAND)
debug_message("cmd [%s]: %s\n", cmd_giver->name, buff);
#endif
/*
* Strip trailing spaces.
*/
for (p = buff + strlen(buff) - 1; p >= buff; p--)
{
if (*p != ' ')
break;
*p = '\0';
}
/*
* Strip leading spaces.
*/
while(*buff == ' ')
buff++;
if (buff[0] == '\0')
return 0;
/*
* Quicktyper hook.
*/
push_string(buff, STRING_MALLOC);
push_object(cmd_giver);
ret = apply_master_ob(M_MODIFY_COMMAND, 2);
if (ret && ret->type == T_STRING)
subst_buff = string_copy(ret->u.string);
if (subst_buff == NULL)
return 1;
p = strchr(subst_buff, ' ');
if (p == 0)
length = strlen(subst_buff);
else
length = p - subst_buff;
if (!*subst_buff ||
!cmd_giver || cmd_giver->flags & O_DESTRUCTED)
return 1;
clear_notify();
for (s = cmd_giver->sent; s; s = s->next)
{
struct svalue *ret;
int len;
struct object *command_object;
if (s->verb == 0)
error("An 'action' did something, but returned 0 or had an undefined verb.\n");
len = strlen(s->verb);
if (s->short_verb)
{
if (strncmp(s->verb, subst_buff, len) != 0)
continue;
}
else
{
if (len != length) continue;
if (strncmp(subst_buff, s->verb, length))
continue;
}
/*
* Now we have found a special sentence !
*/
#ifdef DEBUG
if (d_flag & DEBUG_COMMAND)
debug_message("Local command %s on %s\n", s->function, s->ob->name);
#endif
if (length >= sizeof verb_copy)
len = sizeof verb_copy - 1;
else
len = length;
strncpy(verb_copy, subst_buff, len);
verb_copy[len] = '\0';
last_verb = verb_copy;
/*
* If the function is static and not defined by current object,
* then it will fail. If this is called directly from player 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 score.
*/
command_object = s->ob;
command_giver = cmd_giver;
#ifdef STATIC_ADD_ACTIONS
if (search_for_function(s->function, s->ob->prog))
{
if (function_type_mod_found & TYPE_MOD_PRIVATE
/* && inh_offset < function_inherit_found -
function_prog_found->num_inherited + 1 */ )
error("Atempted call of private function.\n");
if (subst_buff[length] == ' ')
{
push_string(&subst_buff[length+1], STRING_MALLOC);
call_function(s->ob, function_inherit_found,
function_index_found, 1);
}
else
{
call_function(s->ob, function_inherit_found,
function_index_found, 0);
}
if (cmd_giver == 0)
{
if (subst_buff)
free(subst_buff);
return 1;
}
current_object = save_current_object;
last_verb = 0;
/* If we get fail from the call, it was wrong second argument. */
if (sp->type == T_NUMBER && sp->u.number == 0)
{
/* Has the sentences been lost? */
/* Does anyone know a better way of doing this? */
struct sentence *s0;
pop_stack();
for (s0 = cmd_giver->sent;
s0 && s0 != s;
s0 = s0->next) ;
if (!s0)
{
s = 0;
break;
}
continue;
}
else
pop_stack();
}
else
add_message("Error: function %s not found.\n", s->function);
#else
if (subst_buff[length] == ' ')
{
push_string(&subst_buff[length+1], STRING_MALLOC);
ret = apply(s->function, s->ob, 1, 0);
}
else
{
ret = apply(s->function, s->ob, 0, 0);
}
if (cmd_giver == 0)
{
if (subst_buff)
free(subst_buff);
return 1;
}
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)
{
/* Has the sentences been lost? */
/* Does anyone know a better way of doing this? */
struct sentence *s0;
for (s0 = cmd_giver->sent;
s0 && s0 != s;
s0 = s0->next) ;
if (!s0)
{
s = 0;
break;
}
continue;
}
if (ret == 0)
add_message("Error: function %s not found.\n", s->function);
#endif
break;
}
if (subst_buff)
free(subst_buff);
if (s == 0)
{
notify_no_command();
return 0;
}
return 1;
}
/*
* Associate a command with function in this object.
* The optional second argument is the command name.
*
* The optional 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.
*/
int
add_action(struct svalue *func, struct svalue *cmd, int flag)
{
struct sentence *p;
struct object *ob;
if (current_object->flags & O_DESTRUCTED)
return -1;
ob = current_object;
if (command_giver == 0 || (command_giver->flags & O_DESTRUCTED))
return -1;
if (ob != command_giver && ob->super != command_giver &&
ob->super != command_giver->super && ob != command_giver->super)
error("add_action from object that was not present.\n");
#ifdef DEBUG
if (d_flag & DEBUG_ADD_ACTION)
debug_message("--Add action %s\n", func);
#endif
if (func->u.string[0] == ':' || func->u.string[0] == '.')
error("Illegal function name: %s\n", func->u.string);
p = alloc_sentence();
p->function = make_shared_string(func->u.string);
p->ob = ob;
add_ref(ob, "add_action");
p->next = command_giver->sent;
p->short_verb = flag;
if (cmd)
p->verb = make_shared_string(cmd->u.string);
else
p->verb = 0;
command_giver->sent = p;
return 0;
}
/*
* Remove all commands (sentences) defined by object 'ob' in object
* 'player'
*/
void
remove_sent(struct object *ob, struct object *player)
{
struct sentence **s;
for (s= &player->sent; *s;)
{
struct sentence *tmp;
if ((*s)->ob == ob) {
if (d_flag & DEBUG_SENTENCE)
debug_message("--Unlinking sentence %s\n", (*s)->function);
tmp = *s;
*s = tmp->next;
free_sentence(tmp);
} else
s = &((*s)->next);
}
}
static char debinf[8192];
int swap_out_obj_sec, swap_out_obj_min, swap_out_obj_hour;
int swap_out_prog_sec, swap_out_prog_min, swap_out_prog_hour;
int swap_in_obj_sec, swap_in_obj_min, swap_in_obj_hour;
int swap_in_prog_sec, swap_in_prog_min, swap_in_prog_hour;
char *
get_gamedriver_info(char *str)
{
char tmp[1024];
int tries, hits;
float proc;
int tot, tot_num, res, verbose = 0;
extern char *reserved_area;
extern int tot_alloc_sentence, tot_current_alloc_sentence,
tot_alloc_object, tot_alloc_object_size, num_swapped,
total_bytes_swapped, num_arrays, total_array_size, num_mappings,
total_mapping_size;
extern int num_strings_swapped, size_strings_swapped;
extern int obj_swapped, obj_bytes_swapped, total_lineno_swapped;
extern int total_num_prog_blocks, total_prog_block_size;
extern int num_distinct_strings, bytes_distinct_strings;
extern int overhead_bytes();
extern int num_call, call_out_size;
extern int globcache_tries, globcache_hits, funmap_tries, funmap_hits;
extern struct vector null_vector;
extern int cache_tries, cache_hits;
extern int program_bytes_swapped, total_program_size, programs_swapped;
extern int tot_alloc_variable_size;
extern int allocated_swap, allocated_swap_blocks;
extern int last_address;
extern int num_swapped_arrays, size_swapped_arrays;
extern int num_swapped_mappings, size_swapped_mappings;
extern int max_swap_memory, min_swap_memory;
extern int max_swap_time, min_swap_time;
int meg, kilo, byte, hour, min, sec;
extern int used_memory;
extern struct program *swap_prog;
extern struct object *swap_ob;
#ifdef COMM_STAT
extern int add_message_calls,inet_packets,inet_volume;
#endif
strcpy(debinf,"");
if (strcmp(str, "status") == 0 || strcmp(str, "status tables") == 0)
{
#ifdef COMM_STAT
extern int add_message_calls,inet_packets,inet_volume;
#endif
if (strcmp(str, "status tables") == 0)
verbose = 1;
if (reserved_area)
res = RESERVED_SIZE;
else
res = 0;
#ifdef COMM_STAT
if (verbose)
{
sprintf(tmp,"Calls to add_message: %d Packets: %d Average packet size: %f\n\n",add_message_calls,inet_packets,(float)inet_volume/inet_packets);
strcat(debinf, tmp);
}
#endif
if (!verbose)
{
sprintf(tmp,"Sentences:\t\t\t%8d %8d\n", tot_alloc_sentence,
tot_alloc_sentence * sizeof (struct sentence));
strcat(debinf, tmp);
sprintf(tmp,"Objects:\t\t\t%8d %8d (%d dest, %d rmd)\n",
tot_alloc_object, tot_alloc_object_size,
tot_alloc_dest_object, tot_removed_object);
strcat(debinf, tmp);
sprintf(tmp,"Variables:\t\t\t\t %8d (%d swapped, %d Kbytes)\n",
tot_alloc_variable_size,
obj_swapped, (unsigned int)obj_bytes_swapped / 1024);
strcat(debinf, tmp);
sprintf(tmp,"Arrays:\t\t\t\t%8d %8d (%d swapped, %d Kbytes)\n",
num_arrays, total_array_size,
num_swapped_arrays, size_swapped_arrays / 1024);
strcat(debinf, tmp);
sprintf(tmp,"Call_outs:\t\t\t%8d %8d\n", num_call,
call_out_size);
strcat(debinf, tmp);
sprintf(tmp,"Mappings:\t\t\t%8d %8d (%d swapped, %d Kbytes)\n",
num_mappings, total_mapping_size,
num_swapped_mappings, size_swapped_mappings / 1024);
strcat(debinf, tmp);
sprintf(tmp,"Strings:\t\t\t%8d %8d (%d overhead)\n",
num_distinct_strings,
bytes_distinct_strings + overhead_bytes(),
overhead_bytes());
strcat(debinf, tmp);
sprintf(tmp,"\t\t\t\t\t\t (%d swapped, %d Kbytes)\n",
num_strings_swapped, size_strings_swapped >> 10);
strcat(debinf, tmp);
sprintf(tmp,"Prog blocks:\t\t\t%8d %8d\n",
total_num_prog_blocks, total_prog_block_size);
strcat(debinf, tmp);
sprintf(tmp,"Programs:\t\t\t\t %8d (%d swapped, %d Kbytes)\n",
total_program_size, programs_swapped, program_bytes_swapped / 1024);
strcat(debinf, tmp);
sprintf(tmp,"Memory reserved:\t\t\t %8d\n", res);
strcat(debinf, tmp);
}
if (verbose)
{
#ifdef CACHE_STATS
extern int call_cache_saves;
extern int global_cache_saves;
extern int searches_needed;
extern int searches_done;
extern int global_first_saves;
extern int call_first_saves;
#endif
if (globcache_tries < 1)
globcache_tries = 1;
if (funmap_tries < 1)
funmap_tries = 1;
strcat(debinf, stat_living_objects());
strcat(debinf, "\nFunction calls:\n--------------------------\n");
proc = 100.0 * ((cache_hits * 1.0) / cache_tries);
sprintf(tmp,"Call cache, Tries: %10d Hits: %10d Miss: %10d Rate: %3.1f%%\n",
cache_tries, cache_hits,
cache_tries - cache_hits, proc);
strcat(debinf, tmp);
#ifdef CACHE_STATS
sprintf(tmp, "searches saved %10d %3d%%\n",
call_first_saves,
(int)(100.0 * ((call_first_saves * 1.0) /
(searches_needed +
global_first_saves
+ call_first_saves))));
strcat(debinf, tmp);
#endif
#ifdef GLOBAL_CACHE
proc = 100.0 * ((globcache_hits * 1.0) / globcache_tries);
sprintf(tmp,"Global cache, Tries: %10d Hits: %10d Miss: %10d Rate: %3.1f%%\n",
globcache_tries, globcache_hits,
globcache_tries -globcache_hits, proc);
strcat(debinf, tmp);
#ifdef CACHE_STATS
sprintf(tmp, "searches saved %10d %3d%%\n",
global_first_saves,
(int)(100.0 * ((global_first_saves * 1.0) /
(searches_needed +
global_first_saves
+ call_first_saves))));
strcat(debinf, tmp);
#endif
#endif
#ifdef CACHE_STATS
strcat(debinf, "Secondary hits:\n");
sprintf(tmp,"searches needed : %10d\n", searches_needed);
strcat(debinf, tmp);
sprintf(tmp,"call cache saves : %10d %3d%%\n",
call_cache_saves, (int)(100.0 * ((call_cache_saves * 1.0) /
searches_needed)));
strcat(debinf, tmp);
sprintf(tmp,"global cache saves : %10d %3d%%\n",
global_cache_saves,
(int)(100.0 * ((global_cache_saves * 1.0) /
searches_needed)));
strcat(debinf, tmp);
sprintf(tmp,"searches done : %10d\n", searches_done);
strcat(debinf, tmp);
sprintf(tmp,"Total needed : %10d %3d%% done\n",
searches_needed + global_first_saves + call_first_saves,
(int)(100.0 * ((searches_done * 1.0) /
(searches_needed +
global_first_saves
+ call_first_saves))));
strcat(debinf, tmp);
#endif
#ifdef USE_SWAP
sprintf(tmp, "\nSwap:\n--------------------------------------\n");
strcat(debinf, tmp);
sprintf(tmp, "Allocated: %8d Kbytes in %8d blocks.\n",
allocated_swap >> 10, allocated_swap_blocks);
strcat(debinf, tmp);
sprintf(tmp, "Swapped: %8d Kbytes in %8d blocks.\n",
total_bytes_swapped >> 10, num_swapped);
strcat(debinf, tmp);
sprintf(tmp, "File: %8d Kbytes.\n",
last_address >> 10);
strcat(debinf, tmp);
sprintf(tmp, "Time: %8d seconds.\n", current_time -
(swap_prog->time_of_ref < swap_ob->time_of_ref ?
swap_prog->time_of_ref :
swap_prog->time_of_ref));
strcat(debinf, tmp);
sprintf(tmp,"Objects: %d/%d/%d in, %d/%d/%d out.\n",
swap_in_obj_sec, swap_in_obj_min, swap_in_obj_hour,
swap_out_obj_sec, swap_out_obj_min, swap_out_obj_hour);
strcat(debinf, tmp);
sprintf(tmp,"Programs: %d/%d/%d in, %d/%d/%d out.\n",
swap_in_prog_sec, swap_in_prog_min, swap_in_prog_hour,
swap_out_prog_sec, swap_out_prog_min, swap_out_prog_hour);
strcat(debinf, tmp);
#endif
add_string_status(debinf);
sprintf(tmp, "\nReferences:\n--------------------------------\n");
strcat(debinf, tmp);
sprintf(tmp, "Null vector: %d\n", null_vector.ref);
strcat(debinf, tmp);
}
tot = total_prog_block_size +
total_program_size +
tot_alloc_sentence * sizeof(struct sentence) +
total_array_size +
tot_alloc_variable_size +
call_out_size +
total_mapping_size +
bytes_distinct_strings + overhead_bytes() +
tot_alloc_object_size + res;
#if 0
+
show_otable_status(verbose) +
heart_beat_status(verbose) +
print_call_out_usage(verbose) +
res;
#endif
if (!verbose)
{
#ifdef USE_SWAP
if (used_memory)
{
sprintf(tmp,"Other:\t\t\t\t\t %8d\n", used_memory - tot);
strcat(debinf, tmp);
tot = used_memory;
}
#endif
sprintf(tmp,"\t\t\t\t\t --------\n");
strcat(debinf, tmp);
sprintf(tmp,"Total:\t\t\t\t\t %8d\n", tot);
strcat(debinf, tmp);
}
}
return string_copy(debinf);
}
struct vector *
get_local_commands(struct object *ob)
{
struct sentence *s;
struct vector *ret;
struct vector *ret2;
int num;
for (num = 0, s = ob->sent; s; s = s->next)
num++;
ret2 = allocate_array(num);
for (num = 0, s = ob->sent; s; s = s->next, num++)
{
ret2->item[num].type = T_POINTER;
ret2->item[num].u.vec = ret = allocate_array(4);
ret->item[0].type = T_STRING;
ret->item[0].string_type = STRING_SHARED;
ret->item[0].u.string = s->verb;
increment_string_ref(s->verb);
ret->item[1].type = T_NUMBER;
ret->item[1].u.number = s->short_verb;
ret->item[2].type = T_OBJECT;
ret->item[2].u.ob = s->ob;
add_ref(s->ob,"get_local_commands");
ret->item[3].type = T_STRING;
ret->item[3].string_type = STRING_SHARED;
ret->item[3].u.string = s->function;
increment_string_ref(s->function);
}
return ret2;
}
/*VARARGS1*/
void
fatal(va_alist)
va_dcl
{
va_list argp;
char *fmt, *f;
char buf[512];
static int in_fatal = 0;
/* Prevent double fatal. */
if (in_fatal)
abort();
in_fatal = 1;
va_start(argp);
fmt = va_arg(argp, char *);
va_end(argp);
vfprintf(stderr, fmt, argp);
fflush(stderr);
if (current_object)
(void) fprintf(stderr, "Current object was %s\n",
current_object->name);
vsprintf(buf, fmt, argp);
debug_message("%s", buf);
if (current_object)
debug_message("Current object was %s\n", current_object->name);
debug_message("Dump of variables:\n");
(void) dump_trace(1);
abort();
}
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;
extern struct svalue catch_value;
if (error_recovery_context_exists > 1)
{
longjmp(error_recovery_context, 1);
fatal("Throw_error failed!");
}
if (catch_value.type == T_STRING)
error(catch_value.u.string);
else
error("Throw with no catch.\n");
}
static char emsg_buf[2000];
/*VARARGS1*/
void
error(va_alist)
va_dcl
{
extern int error_recovery_context_exists;
extern jmp_buf error_recovery_context;
extern struct object *current_heart_beat;
extern struct svalue catch_value;
char *object_name;
va_list argp;
char *fmt, *f;
int i;
va_start(argp);
fmt = va_arg(argp, char *);
vsprintf(emsg_buf + 1, fmt, argp);
va_end(argp);
emsg_buf[0] = '*'; /* all system errors get a * at the start */
if (error_recovery_context_exists > 1)
{ /* user catches this error */
struct svalue v;
v.type = T_STRING;
v.u.string = emsg_buf;
v.string_type = STRING_MALLOC; /* Always reallocate */
assign_svalue(&catch_value, &v);
longjmp(error_recovery_context, 1);
fatal("Catch() longjump failed");
}
num_error++;
if (num_error <= 1)
{
debug_message("%s", emsg_buf+1);
object_name = dump_trace(0);
fflush(stdout);
if (object_name) {
struct object *ob;
ob = find_object2(object_name);
if (!ob) {
debug_message("error when executing program in destroyed object %s\n",
object_name);
}
}
/*
* 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);
push_string(emsg_buf, STRING_MALLOC);
if (current_object)
{
push_object(current_object);
if (current_prog)
push_string(current_prog->name, STRING_MALLOC);
else
push_constant_string("<???>");
push_string(get_srccode_position_if_any(), STRING_MALLOC);
}
else
{
push_number(0);
push_constant_string("<???>");
push_constant_string("");
}
apply_master_ob(M_RUNTIME_ERROR, 4);
}
else
{
debug_message("Too many simultaneous errors.\n");
}
num_error--;
if (error_recovery_context_exists)
longjmp(error_recovery_context, 1);
abort();
}
/*
* Check that it is an legal path. No '..' are allowed.
*/
int
legal_path(char *path)
{
char *p;
if (path == NULL || strchr(path, ' '))
return 0;
if (path[0] == '/')
return 0;
for(p = strchr(path, '.'); p; p = strchr(p+1, '.'))
{
if (p[1] == '.')
return 0;
}
return 1;
}
/*
* There is an error in a specific file. Ask the game driver to log the
* message somewhere.
*/
static struct error_msg {
struct error_msg *next;
char *file;
char *msg;
} *smart_log_msg = NULL;
void
init_smart_log()
{
struct error_msg *prev;
while (smart_log_msg)
{
prev = smart_log_msg;
smart_log_msg = smart_log_msg->next;
free(prev->file);
free(prev->msg);
free((char *)prev);
}
}
void
dump_smart_log()
{
struct error_msg *prev;
if (!master_ob)
{
init_smart_log();
return;
}
while (smart_log_msg)
{
prev = smart_log_msg;
smart_log_msg = smart_log_msg->next;
push_malloced_string(prev->file);
push_malloced_string(prev->msg);
free((char *)prev);
apply_master_ob(M_LOG_ERROR, 2);
}
}
void
smart_log(char *error_file, int line, char *what)
{
char buff[500];
struct error_msg **last;
for (last = &smart_log_msg; *last; last = &(*last)->next)
if (error_file == 0)
return;
if (strlen(what) + strlen(error_file) > sizeof buff - 100)
what = "...[too long error message]...";
if (strlen(what) + strlen(error_file) > sizeof buff - 100)
error_file = "...[too long filename]...";
sprintf(buff, "%s line %d:%s\n", error_file, line, what);
*last = (struct error_msg *)xalloc(sizeof(struct error_msg));
(*last)->next = NULL;
(*last)->file = string_copy(error_file);
(*last)->msg = string_copy(buff);
}
/*
* Check that a path to a file is valid for read or write.
* This is done by functions in the master object.
* The path is always treated as an absolute path, and is returned without
* a leading '/'.
* If the path was '/', then '.' is returned.
* The returned string may or may not be residing inside the argument 'path',
* so don't deallocate arg 'path' until the returned result is used no longer.
* Otherwise, the returned path is temporarily allocated by apply(), which
* means it will be dealocated at next apply().
*/
char *
check_valid_path(char *path, struct object *ob,
char *call_fun, int writeflg)
{
struct svalue *v;
push_string(path, STRING_MALLOC);
push_object(ob);
push_string(call_fun, STRING_MALLOC);
if (writeflg)
v = apply_master_ob(M_VALID_WRITE, 3);
else
v = apply_master_ob(M_VALID_READ, 3);
if (v && v->type == T_NUMBER && v->u.number == 0)
return 0;
if (path[0] == '/')
path++;
if (path[0] == '\0')
path = ".";
if (legal_path(path))
return path;
return 0;
}
/*
* This one is called from HUP.
*/
int game_is_being_shut_down;
extern volatile int interupted;
void
startshutdowngame(int arg)
{
game_is_being_shut_down = 1;
interupted = 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
shutdowngame()
{
ipc_remove();
remove_all_players();
unlink_swap_file();
#ifdef DEALLOCATE_MEMORY_AT_SHUTDOWN
remove_all_objects();
free_all_sent();
#ifndef MALLOC_sysmalloc
dump_malloc_data();
#endif
find_alloced_data();
#endif
#ifdef OPCPROF
opcdump();
#endif
exit(0);
}
/*
* Call this one when there is only little memory left.
* We tell master object of our troubles and hope it does something
* intelligent, like starting Armageddon.
*/
void
slow_shut_down(int minutes)
{
struct object *ob;
/*
* Swap out objects, and free some memory.
*/
push_number(minutes);
(void) apply_master_ob(M_MEMORY_FAILURE, 1);
}
int
match_string(char *match, char *str)
{
int i;
again:
if (*str == '\0' && *match == '\0')
return 1;
switch(*match)
{
case '?':
if (*str == '\0')
return 0;
str++;
match++;
goto again;
case '*':
match++;
if (*match == '\0')
return 1;
for (i=0; str[i] != '\0'; i++)
if (match_string(match, str+i))
return 1;
return 0;
case '\0':
return 0;
case '\\':
match++;
if (*match == '\0')
return 0;
/* Fall through ! */
default:
if (*match == *str) {
match++;
str++;
goto again;
}
return 0;
}
}
/*
* Credits for some of the code below goes to Free Software Foundation
* Copyright (C) 1990 Free Software Foundation, Inc.
* See the GNU General Public License for more details.
*/
#ifndef S_ISDIR
#define S_ISDIR(m) (((m)&S_IFMT) == S_IFDIR)
#endif
#ifndef S_ISREG
#define S_ISREG(m) (((m)&S_IFMT) == S_IFREG)
#endif
int
isdir(char *path)
{
struct stat stats;
return stat (path, &stats) == 0 && S_ISDIR (stats.st_mode);
}
void
strip_trailing_slashes(char *path)
{
int last;
last = strlen (path) - 1;
while (last > 0 && path[last] == '/')
path[last--] = '\0';
}
struct stat to_stats, from_stats;
int
copy (char *from, char *to)
{
int ifd;
int ofd;
char buf[1024 * 8];
int len; /* Number of bytes read into buf. */
if (!S_ISREG (from_stats.st_mode))
{
error("cannot move `%s' across filesystems: Not a regular file\n", from);
return 1;
}
if (unlink (to) && errno != ENOENT)
{
error ("cannot remove `%s'\n", to);
return 1;
}
ifd = open (from, O_RDONLY, 0);
if (ifd < 0)
{
error ("%s: open failed\n", from);
return errno;
}
ofd = open (to, O_WRONLY | O_CREAT | O_TRUNC, 0600);
if (ofd < 0)
{
error ("%s: open failed\n", to);
close (ifd);
return 1;
}
#ifndef FCHMOD_MISSING
if (fchmod (ofd, from_stats.st_mode & 0777))
{
error ("%s: fchmod failed\n", to);
close (ifd);
close (ofd);
unlink (to);
return 1;
}
#endif
while ((len = read (ifd, buf, sizeof (buf))) > 0)
{
int wrote = 0;
char *bp = buf;
do
{
wrote = write (ofd, bp, len);
if (wrote < 0)
{
error ("%s: write failed\n", to);
close (ifd);
close (ofd);
unlink (to);
return 1;
}
bp += wrote;
len -= wrote;
} while (len > 0);
}
if (len < 0)
{
error ("%s: read failed\n", from);
close (ifd);
close (ofd);
unlink (to);
return 1;
}
if (close (ifd) < 0)
{
error ("%s: close failed", from);
close (ofd);
return 1;
}
if (close (ofd) < 0)
{
error ("%s: close failed", to);
return 1;
}
#ifdef FCHMOD_MISSING
if (chmod (to, from_stats.st_mode & 0777))
{
error ("%s: chmod failed\n", to);
return 1;
}
#endif
return 0;
}
/* Move FROM onto TO. Handles cross-filesystem moves.
If TO is a directory, FROM must be also.
Return 0 if successful, 1 if an error occurred. */
int
do_move (char *from, char *to)
{
if (lstat (from, &from_stats) != 0)
{
error ("%s: lstat failed\n", from);
return 1;
}
if (lstat (to, &to_stats) == 0)
{
if (from_stats.st_dev == to_stats.st_dev
&& from_stats.st_ino == to_stats.st_ino)
{
error ("`%s' and `%s' are the same file", from, to);
return 1;
}
if (S_ISDIR (to_stats.st_mode))
{
error ("%s: cannot overwrite directory", to);
return 1;
}
}
else if (errno != ENOENT)
{
error ("%s: unknown error\n", to);
return 1;
}
#if defined(SYSV) && !defined(_SEQUENT_)
if (isdir(from))
{
char cmd_buf[100];
sprintf(cmd_buf, "/usr/lib/mv_dir %s %s", from, to);
return system(cmd_buf);
} else
#endif /* SYSV */
if (rename (from, to) == 0)
{
return 0;
}
if (errno != EXDEV)
{
error ("cannot move `%s' to `%s'", from, to);
return 1;
}
/* rename failed on cross-filesystem link. Copy the file instead. */
if (copy (from, to))
return 1;
if (unlink (from))
{
error ("cannot remove `%s'", from);
return 1;
}
return 0;
}
/*
* do_rename is used by the efun rename. It is basically a combination
* of the unix system call rename and the unix command mv. Please shoot
* the people at ATT who made Sys V.
*
* WARNING! (Thanks to Sul@Genesis, pmidden@tolkien.helios.nd.edu)
*
* If you have a typical 'lpmud ftpdaemon' that allow wizards to ftp
* to their homedirs and if they are allowed to move subdirs from
* under their wizdirs to for example /open then they can use ftp to
* access the entire host machine.
*
* It is therefore important to disallow these directory moves in
* your /secure/master.c
*/
int
do_rename(char * fr, char *t)
{
char *from, *to;
from = check_valid_path(fr, current_object, "do_rename_from", 1);
if(!from)
return 1;
to = check_valid_path(t, current_object, "do_rename_to", 1);
if(!to)
return 1;
if(!strlen(to) && !strcmp(t, "/"))
{
to = (char *)alloca(3);
sprintf(to, "./");
}
strip_trailing_slashes (from);
if (isdir (to))
{
/* Target is a directory; build full target filename. */
char *cp;
char *newto;
cp = strrchr (from, '/');
if (cp)
cp++;
else
cp = from;
newto = (char *) alloca (strlen (to) + 1 + strlen (cp) + 1);
sprintf (newto, "%s/%s", to, cp);
return do_move (from, newto);
}
else
return do_move (from, to);
}