/*** Include these files for Bub's VMS list_files () ***/
#include <descrip.h>
#include <rmsdef.h>
#include <types.h>
#include <stat.h>
#include "dir.h"
#include <setjmp.h>
#include <string.h>
#include <errno.h>
#include <stdio.h>
#include "lint.h"
#include "config.h"
/* What is this? (bub) */
/*#include "stdio.h"*/
#include "lnode.h"
#include "y-tab.h"
#include "interpret.h"
#include "object.h"
#include "sent.h"
#include "wiz_list.h"
/*** Who forgot this? (bub) ***/
#include <perror.h>
extern int errno, current_time;
char *inherit_file;
extern char *xalloc (), *string_copy (), *dump_trace (), *check_file_name (),
*xalloc ();
extern struct value *catch_value;
char *last_verb;
extern int special_parse PROT ((char *)), yyparse (),
set_call PROT ((struct object *, struct sentence *, int)),
parse_command PROT ((char *, struct object *, int)),
legal_path PROT ((char *)), print_call_out_usage ();
char *read_file PROT ((char *, int));
void pre_compile PROT ((char *)),
remove_interactive PROT ((struct object *)),
add_light PROT ((struct object *, int)),
do_write PROT ((struct value *)), add_action PROT ((char *, char *, int)),
add_verb PROT ((char *, int)),
print_local_commands (), ipc_remove (),
show_info_about PROT ((char *, char *, struct interactive *)),
ed_start PROT ((char *)), say PROT ((struct value *, struct object *)),
set_snoop PROT ((struct object *, struct object *)),
tell_room PROT ((struct object *, struct value *)),
print_lnode_status PROT ((int)),
remove_all_players (), start_new_file (),
move_or_destruct PROT ((struct object *, struct object *)),
startshutdowngame (),
load_ob_from_swap PROT ((struct object *)), dump_malloc_data (),
dump_all_objects (),
debug_message_value ();
struct value *contents (), *search_array ();
extern struct value *allocate_array ();
extern int d_flag;
struct object *obj_list, *obj_list_destruct;
extern struct object *get_interactive_object PROT ((int));
extern int current_line;
struct object *current_object; /* The object interpreting a function. */
struct object *command_giver; /* Where the current command came from. */
int num_parse_error; /* Number of errors in the parser. */
void shutdowngame ();
extern FILE *vpopen_grep ();
#define GREP "/bin/grep"
void
grep_file (options, regexp, path)
char *path, *options, *regexp;
{
char buff[500];
char buff2[100];
FILE *f;
int i;
if (!path)
error ("File name required to grep()\n");
path = check_file_name (path, 0);
if (path == 0)
return;
if (strchr (options, ' ') || strchr (options, 'f'))
error ("Bad argument 1 to grep()\n");
if (regexp[0] == 0)
error ("Bad argument 2 to grep()\n");
strcpy (buff2, "-");
strncat (buff2, options, (sizeof buff2) - 2);
if (options[0] == 0)
f = vpopen_grep (GREP, regexp, path, 0, "r");
else
f = vpopen_grep (GREP, buff2, regexp, path, "r");
if (f == 0)
return;
for (i = 0; i < MAX_LINES; i++)
{
if (fgets (buff, sizeof (buff), f) == 0)
break;
add_message ("%s", buff);
}
if (i == MAX_LINES)
add_message ("*****TRUNCATED*****\n");
vpclose (f);
}
struct svalue *
find_value (p)
struct lnode_variable *p;
{
if (p->number >= current_object->num_variables)
{
dump_trace ();
fatal ("Illegal variable access %d(%d). See trace above.\n",
p->number, current_object->num_variables);
}
return ¤t_object->variables[p->number];
}
struct lnode_var_def *
find_status (str, must_find)
char *str;
int must_find;
{
struct lnode_var_def *p;
for (p = current_object->status; p; p = p->next)
{
if (strcmp (p->name, str) == 0)
return p;
}
if (!must_find)
return 0;
error ("--Status %s not found in prog for %s\n", str,
current_object->name);
return 0;
}
/*
* Save the name of the current .i file, so we are able to remove it
* if it contains illegal include files.
*/
char *current_i_file;
void
remove_i_file ()
{
unlink (current_i_file);
}
/*
* 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.
*
* Save the command_giver, because reset() in the new object might change
* it.
*/
struct object *
load_object (lname)
char *lname;
{
FILE *f;
extern int total_lines;
struct object *ob, *save_command_giver = command_giver;
extern struct lnode *prog, *heart_beat;
extern char *current_file;
extern int variable_count, static_variable_flag;
extern struct object *inherit_ob;
struct stat i_st, c_st;
int i, name_length;
char real_name[200], name[200];
/* Remove leading '/' if any. */
while (lname[0] == '/')
lname++;
/*
* We must clear inherit_ob, to be sure it is initialized to 0.
* It can still be pointing to something, where an error aborted
* execution.
*/
inherit_ob = 0;
/* Truncate possible .c in the object name. */
strncpy (name, lname, sizeof (name) - 1);
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)
{
fprintf (stderr, "Could not load descr for %s\n", real_name);
error ("Failed to load file.\n");
return 0;
}
/*
* 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;
}
/*
* Now check if the i-file is newer or nonexisting.
*/
real_name[name_length + 1] = 'i';
if (stat (real_name, &i_st) == -1 || i_st.st_mtime < c_st.st_mtime)
{
real_name[name_length + 1] = 'c';
pre_compile (name);
real_name[name_length + 1] = 'i';
}
if (stat (real_name, &i_st) == -1 || i_st.st_mtime < c_st.st_mtime)
{
if (command_giver)
add_message ("Failed to compile the new file.\n");
}
current_i_file = real_name;
f = fopen (real_name, "r");
if (f == 0)
{
perror (real_name);
error ("Could not read the file.\n");
}
start_new_file (f);
current_file = string_copy (real_name); /* This one is freed below */
current_line = 0;
prog = heart_beat = 0;
prog_status = 0;
variable_count = 0;
num_parse_error = 0;
static_variable_flag = 0;
(void) yyparse ();
update_compile_av (total_lines);
total_lines = 0;
(void) fclose (f);
#ifdef REMOVE_I_FILES
unlink (current_i_file);
#else
/*
* Remove the i-file if there was an error. This
* is necessary, to protect against security problems.
*/
if (num_parse_error)
unlink (current_i_file);
#endif
free (current_file);
current_file = 0;
current_i_file = 0;
if (inherit_file == 0 && (num_parse_error > 0 || prog == 0))
{
if (inherit_file)
{
inherit_file = 0;
free (inherit_file);
}
if (prog)
{
/*
* Set the reference count to one.
* We don't want to confuse free_prog().
*/
add_prog_ref ((struct lnode_def *) prog);
if (!free_prog ((struct lnode_def *) prog, 0, 0))
fatal ("Failed to release prog area.\n");
}
if (num_parse_error == 0 && prog == 0)
error ("No program in object !\n");
error ("Error in loading object\n");
}
if (inherit_file)
{
char *tmp = inherit_file;
if (prog)
{
/*
* Set the reference count to one.
* We don't want to confuse free_prog().
*/
add_prog_ref ((struct lnode_def *) prog);
if (!free_prog ((struct lnode_def *) prog, 0, 0))
fatal ("Failed to release prog area.\n");
prog = 0;
}
if (strcmp (inherit_file, name) == 0)
{
free (inherit_file);
inherit_file = 0;
error ("Illegal to inherit self.\n");
}
inherit_file = 0;
load_object (tmp);
free (tmp);
ob = load_object (name);
return ob;
}
ob = get_empty_object (variable_count);
if (inherit_ob)
{
if (strcmp (inherit_ob->name, name) == 0)
{
free ((char *) ob);
inherit_ob = 0;
error ("Illegal to inherit self.\n");
}
ob->inherit = inherit_ob;
inherit_ob->inherited = 1;
add_ref (inherit_ob, "load_object inherit");
if (ob->heart_beat == 0)
ob->heart_beat = inherit_ob->heart_beat;
inherit_ob = 0;
}
ob->name = string_copy (name);
ob->name_length = strlen (name);
/* Is this object wizard defined ? */
{
char wiz_name[100];
if (sscanf (real_name, "players/%s", wiz_name) == 1)
{
char *np;
np = strchr (wiz_name, '/');
if (np)
*np = '\0';
ob->wl = add_name (wiz_name);
}
else
{
ob->wl = 0;
}
}
#if 0
/* No longer here. It is done in get_empty_object() */
/* Allocate space for local variables. */
if (variable_count > 0)
ob->variables =
(struct svalue *) xalloc (variable_count * sizeof (struct svalue));
ob->num_variables = variable_count;
/* Initialize variables... */
for (i = 0; i < variable_count; i++)
{
ob->variables[i].u.number = 0;
ob->variables[i].type = T_NUMBER;
}
#endif
ob->status = prog_status;
if (prog)
{
ob->prog = (struct lnode_def *) prog;
add_prog_ref ((struct lnode_def *) prog);
}
else
{
ob->prog = 0;
}
ob->next_all = obj_list;
obj_list = ob;
if (heart_beat)
ob->heart_beat = heart_beat;
enter_object_hash (ob); /* add name to fast object lookup table */
ob->reset = 1;
ob->last_reset = current_time;
(void) apply ("reset", ob, 0);
command_giver = save_command_giver;
if (d_flag)
debug_message ("--%s loaded\n", ob->name);
#if NUM_RESET_TO_SWAP > 0
swap (ob);
#endif
return ob;
}
struct object *previous_ob;
extern struct lnode_2 *next_arg_list_to_use;
/*
* Call function in another object. The object can be given both as a string
* and a pointer.
*
* Save the caller object in previous_ob, so some functions can
* see who called them.
*
* If find_object() is called, the object might be non-existing, and
* thus loaded. If the object is loaded, "reset" will be called in the object.
* next_arg_list_to_use must be hidden, so that the reset() won't
* get these arguments.
*/
struct value *
call_other (obj, name, arg)
struct value *obj;
char *name;
struct value *arg;
{
struct object *ob, *save_command_giver = command_giver;
struct value *ret;
if (obj->type == T_OBJECT)
{
ob = obj->u.ob;
}
else
{
struct lnode_2 *save_args = next_arg_list_to_use;
next_arg_list_to_use = 0;
ob = find_object (obj->u.string);
next_arg_list_to_use = save_args;
}
if (ob == 0)
error ("Could not find object %s\n", obj);
/*
* Test if this really is an existing object.
*/
if (ob->reset == 0)
{
next_arg_list_to_use = 0;
return &const0;
}
if (ob->last_reset < (current_time - RESET_TIME * 60))
{
struct lnode_2 *save_args = next_arg_list_to_use;
next_arg_list_to_use = 0;
ob->last_reset = current_time;
(void) apply ("reset", ob, &const1);
next_arg_list_to_use = save_args;
command_giver = save_command_giver;
if (ob->destructed)
{
next_arg_list_to_use = 0;
return &const0;
}
}
ret = apply (name, ob, arg);
command_giver = save_command_giver;
if (ret == 0)
return &const0;
return ret;
}
struct value *
this_player ()
{
struct value *p;
if (command_giver == 0 || command_giver->destructed)
return &const0;
p = alloc_value ();
p->type = T_OBJECT;
p->u.ob = command_giver;
add_ref (command_giver, "this_player()");
return p;
}
struct value *
caller ()
{
struct value *p;
extern struct object *frame_ob;
if (frame_ob == 0 || (frame_ob->destructed))
return &const0;
p = alloc_value ();
p->type = T_OBJECT;
p->u.ob = frame_ob;
add_ref (frame_ob, "caller()");
return p;
}
struct value *
this_object ()
{
struct value *p;
if (current_object->destructed)
return &const0;
p = alloc_value ();
p->type = T_OBJECT;
p->u.ob = current_object;
add_ref (current_object, "this_object()");
return p;
}
struct value *
call_local_function (name, arg)
char *name;
struct value *arg;
{
struct value *ret;
ret = apply (name, current_object, arg);
if (ret == 0)
error ("Local function %s not found.\n", name);
return ret;
}
char *
make_new_name (str)
char *str;
{
static int i;
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 value *
clone_object (str1)
char *str1;
{
struct object *ob, *new_ob;
struct value *ret;
int i;
struct object *save_command_giver = command_giver;
ob = find_object (str1);
if (!ob)
{
if (strcmp (str1, "obj/sing") == 0)
fatal ("No singularity.\n");
ret = clone_object ("obj/sing");
if (!ret)
fatal ("No singularity.\n");
return ret;
}
if (ob->super || ob->cloned)
error ("Cloning a used object !\n");
if (ob->enable_heart_beat)
(void) set_heart_beat (ob, 0);
ob->reset = 0;
new_ob = get_empty_object (ob->num_variables);
new_ob->name = make_new_name (ob->name);
new_ob->name_length = strlen (new_ob->name);
new_ob->cloned = 1;
new_ob->last_reset = current_time;
new_ob->prog = ob->prog;
new_ob->inherit = ob->inherit;
if (new_ob->inherit)
add_ref (new_ob->inherit, 0);
if (new_ob->prog)
add_prog_ref ((struct lnode_def *) (new_ob->prog));
if (current_object && current_object->wl && !ob->wl)
new_ob->wl = current_object->wl;
else
new_ob->wl = ob->wl; /* Possibly a null pointer */
new_ob->status = ob->status;
new_ob->heart_beat = ob->heart_beat;
new_ob->next_all = obj_list;
new_ob->block = ob->block;
new_ob->swap_size = ob->swap_size; /* Used only for statistics. */
obj_list = new_ob;
enter_object_hash (new_ob); /* add name to fast object lookup table */
#if 0 /* This was done in get_empty_object() */
new_ob->num_variables = ob->num_variables;
/* Allocate space for local variables. */
if (ob->num_variables > 0)
new_ob->variables =
(struct svalue *) xalloc (ob->num_variables *
sizeof (struct svalue));
/* Initialize variables... */
for (i = 0; i < ob->num_variables; i++)
{
new_ob->variables[i].u.number = 0;
new_ob->variables[i].type = T_NUMBER;
}
#endif
new_ob->reset = 1;
(void) apply ("reset", new_ob, 0);
command_giver = save_command_giver;
if (d_flag)
debug_message ("--%s cloned to %s\n", ob->name, new_ob->name);
if (new_ob->destructed)
return &const0;
ret = alloc_value ();
ret->type = T_OBJECT;
ret->u.ob = new_ob;
add_ref (new_ob, "clone_object");
return ret;
}
struct value *
environment (arg)
struct value *arg;
{
struct value *ret;
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);
else
ob = current_object;
if (ob == 0 || ob->super == 0)
return &const0;
if (ob->destructed)
error ("environment() off destructed object.\n");
ret = alloc_value ();
ret->type = T_OBJECT;
ret->u.ob = ob->super;
add_ref (ret->u.ob, "environment");
return ret;
}
/*
* 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.
*/
int
command_for_object (str, ob)
char *str;
struct object *ob;
{
char buff[1000];
if (strlen (str) > sizeof (buff) - 1)
error ("Too long command.\n");
#ifdef LOG_FORCE
if (ob && command_giver != ob && ob->interactive)
{
struct value *force, *forced;
FILE *f;
force = apply ("query_real_name", command_giver, 0);
forced = apply ("query_real_name", ob, 0);
f = fopen ("log/FORCE", "a");
if (f && force && forced)
{
if (force->type == T_STRING && forced->type == T_STRING)
fprintf (f, "%s %s: %s\n", force->u.string, forced->u.string,
str);
}
if (f)
fclose (f);
}
#endif
if (ob == 0)
ob = current_object;
strncpy (buff, str, sizeof buff);
return parse_command (buff, ob, (ob != current_object));
}
/*
* 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 value *object_present2 ();
struct value *
object_present (v, ob)
struct value *v;
struct object *ob;
{
struct value *ret;
int specific = 0;
if (ob == 0)
ob = current_object;
else
specific = 1;
if (v->type == T_OBJECT)
{
if (specific)
{
if (v->u.ob->super == ob)
return v;
else
return &const0;
}
if (v->u.ob->super == ob ||
(v->u.ob->super == ob->super && ob->super != 0))
return v;
return &const0;
}
ret = object_present2 (v, ob->contains);
if (ret)
return ret;
if (specific)
return &const0;
if (ob->super)
{
ret = apply ("id", ob->super, v);
if (ob->super->destructed)
return &const0;
if (ret && !(ret->type == T_NUMBER && ret->u.number == 0))
{
ret = alloc_value ();
ret->type = T_OBJECT;
ret->u.ob = ob->super;
add_ref (ret->u.ob, "present()");
return ret;
}
return object_present2 (v, ob->super->contains);
}
return &const0;
}
static struct value *
object_present2 (v, ob)
struct value *v;
struct object *ob;
{
struct value *ret;
char *p;
int count = 0, length;
struct value item;
item.type = T_STRING;
item.u.string = string_copy (v->u.string);
length = strlen (item.u.string);
p = item.u.string + length - 1;
if (*p >= '0' && *p <= '9')
{
while (p > item.u.string && *p >= '0' && *p <= '9')
p--;
if (p > item.u.string && *p == ' ')
{
count = atoi (p + 1) - 1;
*p = '\0';
length = p - item.u.string; /* This is never used again ! */
}
}
for (; ob; ob = ob->next_inv)
{
ret = apply ("id", ob, &item);
if (ob->destructed)
{
free (item.u.string);
return 0;
}
if (ret == 0 || (ret->type == T_NUMBER && ret->u.number == 0))
continue;
if (count-- > 0)
continue;
ret = alloc_value ();
ret->type = T_OBJECT;
ret->u.ob = ob;
add_ref (ret->u.ob, "object_present2");
free (item.u.string);
return ret;
}
free (item.u.string);
return 0;
}
/*
* Remove an object. It is first moved into the destruct list, and
* not really destructed until later. (destruct2()).
*
* If the object is inherited, it must not be destructed. So we just rename
* it to something new. Thus will all pointers to this object continue to
* work, and new objects that needs this object will load a new copy.
*/
void
destruct_object (v)
struct value *v;
{
struct object *ob, *super;
struct object **pp;
int removed;
struct value *weight, neg_weight;
extern struct object *current_reset;
if (v->type == T_OBJECT)
ob = v->u.ob;
else
{
ob = find_object2 (v->u.string);
if (ob == 0)
error ("destruct_object: Could not find %s\n", v->u.string);
}
if (ob->destructed)
error ("Destruct destructed object.\n");
if (d_flag)
debug_message ("Destruct object %s (ref %d)\n", ob->name, ob->ref);
/*
* Inherited objects will never be removed.
*/
if (ob->inherited)
{
char *p;
remove_object_hash (ob);
p = ob->name;
ob->name = make_new_name (p);
free (p);
ob->name_length = strlen (ob->name);
enter_object_hash (ob);
return;
}
if (ob == current_reset)
{
/*
* current_reset must be set to 0, not the next object in the list,
* as this next object migh be destructed from a recursive call of
* us from destruct_object()_ from above !
*/
current_reset = 0;
}
super = ob->super;
if (super == 0)
{
super = find_object ("room/void"); /* Take any existing void. */
}
else
{
/* Call exit in current room, if player or npc */
if (ob->enable_commands)
{
struct value v;
v.type = T_OBJECT;
v.u.ob = ob;
apply ("exit", super, &v);
}
weight = apply ("query_weight", ob, 0);
if (weight && weight->type == T_NUMBER)
{
neg_weight.type = T_NUMBER;
neg_weight.u.number = -weight->u.number;
(void) apply ("add_weight", super, &neg_weight);
}
}
if (super == 0)
fatal ("Could not find the void.\n");
set_heart_beat (ob, 0);
while (ob->contains)
move_or_destruct (ob->contains, super);
set_heart_beat (ob, 0);
/*
* 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->enable_commands)
remove_sent (ob, ob->super);
add_light (ob->super, -ob->total_light);
for (pp = &ob->super->contains; *pp;)
{
if ((*pp)->enable_commands)
remove_sent (ob, *pp);
if (*pp != ob)
pp = &(*pp)->next_inv;
else
*pp = (*pp)->next_inv;
}
}
if (ob->ed_buffer)
{
extern void save_buffer (), deallocate_buffer ();
save_buffer (ob);
deallocate_buffer (ob);
}
if (ob->inherit)
{
struct object *tmp = ob->inherit;
ob->inherit = 0;
free_object (tmp, "inherit");
}
/*
* 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->heart_beat = 0;
ob->contains = 0;
ob->enable_commands = 0;
ob->next_all = obj_list_destruct;
obj_list_destruct = ob;
ob->destructed = 1;
}
/*
* This one is called when no program is execuiting.
* The super pointer is still maintained.
*/
void
destruct2 (ob)
struct object *ob;
{
if (ob->interactive)
remove_interactive (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.
*/
if (ob->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 < ob->num_variables; i++)
{
free_svalue (&ob->variables[i]);
ob->variables[i].type = T_NUMBER;
ob->variables[i].u.number = 0;
}
}
free_object (ob, "destruct_object");
}
struct value *
create_wizard (owner)
char *owner;
{
struct stat st;
char name[200], cmd[200];
FILE *f;
struct value *ret;
struct object *owner_obj;
if (strncmp (current_object->name, "obj/", 4) != 0 &&
strncmp (current_object->name, "room/", 5) != 0)
{
error ("Illegal use of create_wizard()\n");
}
if (strchr (owner, '.') || strchr (owner, '/'))
error ("Bad name to create_wizard: %s\n", owner);
owner_obj = find_living_object (owner, 1);
if (owner_obj == 0)
{
fprintf (stderr,
"create_wizard: Could not find living object %s.\n", owner);
return 0;
}
if (owner_obj->super == 0 || owner_obj->super->cloned)
{
struct value v;
v.type = T_STRING;
v.u.string =
"There is a crash, the room collapses and the castle disappears.\n";
say (&v, 0);
return 0;
}
if (stat (PLAYER_DIR, &st) == -1)
if (mkdir (PLAYER_DIR, 0777) == -1)
error ("Could not mkdir %s\n", PLAYER_DIR);
(void) sprintf (name, "%s/%s", PLAYER_DIR, owner);
if (stat (name, &st) == 0)
error ("Player %s already has a castle!\n", owner);
if (mkdir (name, 0777) == -1)
{
perror (name);
error ("Could not mkdir %s\n", name);
}
(void) sprintf (name, "%s/%s/%s", PLAYER_DIR, owner, "castle.c");
f = fopen (name, "w");
if (f == NULL)
error ("Could not create a castle file %s!\n", name);
(void) fprintf (f, "#define NAME \"%s\"\n", owner);
(void) fprintf (f, "#define DEST \"%s\"\n", owner_obj->super->name);
(void) fclose (f);
(void) sprintf (cmd, "cat %s >> %s", DEFAULT_CASTLE, name);
(void) system (cmd);
f = fopen (INIT_FILE, "a");
if (f == NULL)
error ("Could not add the new castle to the %s\n", INIT_FILE);
(void) fprintf (f, "%s\n", name);
(void) fclose (f);
ret = alloc_value ();
ret->type = T_STRING;
ret->u.string = string_copy (name);
return ret;
}
/*
* A message from an object will reach
* all objects in the inventory,
* all objects in the same environment and
* the surrounding object.
* Non interactive objects gets no messages.
*
* There are two cases to take care of. If this routine is called from
* a player (indirectly), then the message goes to all in his
* environment. Otherwise, the message goes to all in the current_object's
* environment (as the case when called from a heart_beat()).
*
* If there is a second argument 'avoid_ob', then do not send the message
* to that object.
*/
void
say (v, avoid_ob)
struct value *v;
struct object *avoid_ob;
{
struct object *ob, *save_command_giver = command_giver;
struct object *origin;
char buff[256];
if (current_object->enable_commands)
command_giver = current_object;
if (command_giver)
origin = command_giver;
else
origin = current_object;
switch (v->type)
{
case T_STRING :
strncpy (buff, v->u.string, sizeof buff);
break;
case T_OBJECT :
strncpy (buff, v->u.ob->name, sizeof buff);
break;
case T_NUMBER :
sprintf (buff, "%d", v->u.number);
break;
default :
error ("Invalid argument %d to say()\n", v->type);
}
for (ob = origin->contains; ob; ob = ob->next_inv)
{
struct object *save_again;
if (ob->interactive == 0)
{
if (ob->enable_commands && ob != command_giver && ob != avoid_ob)
tell_npc (ob, buff);
if (ob->destructed)
break;
continue;
}
if (ob == save_command_giver || ob == avoid_ob)
continue;
save_again = command_giver;
command_giver = ob;
add_message ("%s", buff);
command_giver = save_again;
}
if (origin->super)
{
if (origin->super->interactive && origin != command_giver &&
origin->super != avoid_ob)
{
command_giver = origin->super;
add_message ("%s", buff);
}
else if (origin->super->interactive == 0 &&
origin->super != avoid_ob &&
origin->super->enable_commands && ob != command_giver)
{
tell_npc (origin->super, buff);
}
for (ob = origin->super->contains; ob; ob = ob->next_inv)
{
struct object *save_again;
if (ob == avoid_ob)
continue;
if (ob->interactive == 0)
{
if (ob->enable_commands && ob != command_giver)
tell_npc (ob, buff);
if (ob->interactive)
break;
continue;
}
if (ob == command_giver)
continue;
save_again = command_giver;
command_giver = ob;
add_message ("%s", buff);
command_giver = save_again;
}
}
command_giver = save_command_giver;
}
/*
* Send a message to all objects inside an object.
* Non interactive objects gets no messages.
* Compare with say().
*/
void
tell_room (room, v)
struct object *room;
struct value *v;
{
struct object *ob, *save_command_giver = command_giver;
char buff[256];
switch (v->type)
{
case T_STRING :
strncpy (buff, v->u.string, sizeof buff);
break;
case T_OBJECT :
strncpy (buff, v->u.ob->name, sizeof buff);
break;
case T_NUMBER :
sprintf (buff, "%d", v->u.number);
break;
default :
error ("Invalid argument %d to tell_room()\n", v->type);
}
for (ob = room->contains; ob; ob = ob->next_inv)
{
if (ob->interactive == 0)
{
if (ob->enable_commands)
tell_npc (ob, buff);
if (ob->destructed)
break;
continue;
}
command_giver = ob;
add_message ("%s", buff);
}
command_giver = save_command_giver;
}
void
shout_string (str)
char *str;
{
struct object *ob, *save_command_giver = command_giver;
extern int num_player;
FILE *f = 0;
char *p;
struct value *v, *muffled;
int emergency = 0, i;
if (str[0] == '!')
{
emergency = 1;
str++;
}
for (p = str; *p; p++)
{
if ((*p < ' ' || *p > '~') && *p != '\n')
*p = ' ';
}
p = 0;
#ifdef LOG_SHOUT
if (command_giver)
{
v = apply ("query_real_name", command_giver, 0);
if (v && v->type == T_STRING)
p = v->u.string;
}
else if (current_object && current_object->wl)
p = current_object->wl->name;
if (p)
f = fopen ("log/SHOUTS", "a");
if (f)
{
fprintf (f, "%s: %s\n", p, str);
fclose (f);
}
#endif
/* for (ob = obj_list; ob; ob = ob->next_all) { */
for (i = 0; i < num_player; i++)
{
ob = get_interactive_object (i);
if (ob->interactive == 0 || ob == save_command_giver)
continue;
muffled = apply ("query_muffled", ob, (struct value *) 0);
if (!emergency && muffled->type == T_NUMBER && muffled->u.number)
continue;
command_giver = ob;
add_message ("%s", str);
}
command_giver = save_command_giver;
}
struct value *
first_inventory (arg)
struct value *arg;
{
struct object *ob;
struct value *ret;
if (arg->type == T_STRING)
ob = find_object (arg->u.string);
else
ob = arg->u.ob;
if (ob == 0)
error ("No object to first_inventory()");
if (ob->contains == 0)
return 0;
ret = alloc_value ();
ret->type = T_OBJECT;
ret->u.ob = ob->contains;
add_ref (ret->u.ob, "first_inventory");
return ret;
}
struct value *
next_inventory (arg)
struct value *arg;
{
struct object *ob;
struct value *ret;
if (arg->type == T_STRING)
ob = find_object (arg->u.string);
else
ob = arg->u.ob;
if (ob == 0)
error ("No object to next_inventory()");
if (ob->next_inv == 0)
return 0;
ret = alloc_value ();
ret->type = T_OBJECT;
ret->u.ob = ob->next_inv;
add_ref (ret->u.ob, "next_inventory");
return ret;
}
/*
* 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 (num)
int num;
{
current_object->enable_commands = num;
command_giver = current_object;
}
/*
* Set up a function in this object to be called with the next
* user input string.
*/
struct value *
input_to (fun, flag)
char *fun;
int flag;
{
struct sentence *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");
return &const1;
}
free_sentence (s);
return &const0;
}
int
pstrcmp (p1, p2)
char **p1, **p2;
{
return strcmp (*p1 + 2 * sizeof (int), *p2 + 2 * sizeof (int));
}
/*
* List files in a directory. The standard 'ls' could be used, but
* it takes too much time.
* Prepared for flag decoding.
*
* Look at the the last component of the path name. If it is a regular
* expression, select only matching files.
*
* '.' and '..' are only listed if they match the regexp.
*
*/
/* * But of course, none of this works in VMS..... (bub)
* The problem is with the READDIR, OPENDIR, and CLOSEDIR ()
#define MAXLSCOLUMNS 6
void
list_files (path, flags)
char *path, *flags;
{
DIR *dirp;
struct direct *de;
struct stat st;
char regexp[80], *p, path2[150];
int do_match = 0;
char *name[MAXLSCOLUMNS * MAX_LINES];
char *nbuf;
int colwidth[MAXLSCOLUMNS];
char *fname;
int num, i, j, k, ncols, npercol, max, total, size;
if (!path)
path = ".";
path = check_file_name (path, 0);
if (path == 0)
return;
strncpy (path2, path, sizeof path2);
path2[sizeof path2 - 1] = '\0';
p = path2 + strlen (path2) - 2;
if (p >= path2 && p[0] == '/' && p[1] == '.')
p[0] = '\0';
if (stat (path2, &st) == -1)
{
* Either the directory does not exist, or it is a regexp
* file name. Strip of the last component.
*
p = strrchr (path2, '/');
if (p == 0 || p[1] == '\0')
return;
strncpy (regexp, p + 1, sizeof regexp);
regexp[sizeof regexp - 1] = '\0';
*p = '\0';
do_match = 1;
}
dirp = opendir (path2);
if (dirp == 0)
{
add_message ("No such directory '%s'\n", path2);
return;
}
num = 0;
total = 0;
if (path[0])
strcat (path2, "/");
i = strlen (path2);
fname = &path2[i];
i = sizeof (path2) - i - 1;
nbuf = (char *) xalloc ((34 + 2 * sizeof (int)) * MAXLSCOLUMNS * MAX_LINES);
for (j = 0; j < MAXLSCOLUMNS * MAX_LINES; j++)
name[j] = nbuf + (j * (34 + 2 * sizeof (int)));
for (de = readdir (dirp); (de && num < (MAXLSCOLUMNS * MAX_LINES)); de = readdir (dirp))
{
if (!do_match && (strcmp (de->d_name, ".") == 0 ||
strcmp (de->d_name, "..") == 0))
continue;
if (do_match && !match_string (regexp, de->d_name))
continue;
strncpy (fname, de->d_name, i);
j = 0;
if (stat (path2, &st) != -1)
{
size = (int) st.st_size / 1024 + ((int) st.st_size % 1024 > 0);
total += size;
if (S_IFDIR & st.st_mode)
j = 1;
}
else
size = 0;
strncpy (name[num] + 2 * sizeof (int), de->d_name, 32);
(name[num] + 2 * sizeof (int))[32] = 0;
((int *) name[num])[0] = de->d_namlen + j;
((int *) name[num])[1] = size;
if (j)
strcat (name[num] + 2 * sizeof (int), "/");
num++;
}
closedir (dirp);
if (num == 0)
{
add_message (do_match ? "No matching files.\n" : "No files.\n");
return;
}
* Sort the names. *
qsort ((char *) name, num, sizeof (char *), pstrcmp);
ncols = num / (num / MAXLSCOLUMNS + (num % MAXLSCOLUMNS > 0)) + ((num % (num / MAXLSCOLUMNS + (num % MAXLSCOLUMNS > 0))) > 0);
while (1)
{
npercol = num / ncols + (num % ncols > 0);
for (j = 0; j < ncols; j++)
{
max = 0;
for (i = j * npercol; i < (j + 1) * npercol && i < num; i++)
{
if (((int *) name[i])[0] > max)
max = ((int *) name[i])[0];
}
colwidth[j] = max + 6;
}
for (i = 0, max = 0; i < ncols; i++)
max += colwidth[i];
if (max < 79)
break;
ncols--;
if (ncols < 1)
{
ncols = 1;
npercol = num;
break;
}
}
for (i = 1; i < ncols; i++)
colwidth[i] += colwidth[i - 1];
add_message ("Total %d\n", total);
max = 0;
i = 0;
j = 0;
strcpy (path2,
" ");
while (max < num)
{
k = (i * npercol) + j;
if (i >= ncols)
{
j++;
i = 0;
add_message ("%s\n", path2);
strcpy (path2,
" ");
continue;
}
if (k < num)
{
if (((int *) name[k])[1] > 9999)
sprintf (&path2[(i > 0 ? colwidth[i - 1] : 0)],
"BIG! %s ", name[k] + 2 * sizeof (int));
else
sprintf (&path2[(i > 0 ? colwidth[i - 1] : 0)],
"%4d %s ",
((int *) name[k])[1],
name[k] + 2 * sizeof (int));
if ((i + 1) * npercol + j < num)
path2[(i > 0 ? colwidth[i - 1] : 0) +
6 + ((int *) name[k])[0]] = ' ';
max++;
}
i++;
}
add_message ("%s\n", path2);
if (num == MAXLSCOLUMNS * MAX_LINES)
add_message ("***Too many files, listing truncated***\n");
free (nbuf);
}
*/
/*** Insert Bub's list_files () ***/
/*** I just ignore the flags..... ***/
void list_files (char * path, char * flags)
{
/* Declare storage for the input file name, and the output file name */
/* The VAX will want these in stupid descriptor form, hence the strange */
/* declarations. */
static $DESCRIPTOR (filespec,
"ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ");
static $DESCRIPTOR (new_files,
"zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz");
/* The context for the find_file call, so I can get multiple files using */
/* wild card stuff. */
unsigned long context = 0;
/* The returned value from the call. */
long status;
/* number of files found */
int file_count = 0;
/* So we can re-initialize the length of the new_files descriptor */
int old_length;
/* string to help process the path. */
char temp_path [81];
/* Variable to keep track of tabs */
int three_counter;
/* Your basic dummy variables. */
int i, j, counter;
if (!path)
path = ".";
path = check_file_name(path, 0);
if (path == 0)
return;
/*
add_message ("\n");
add_message (path);
add_message ("\n");
*/
/*** Convert a /dir1/dir2 type path to a [mudlib.dir1.dir2]*.* type path ***/
strcpy (temp_path, "[MUDLIB.");
strcat (temp_path, path);
counter = 0;
while ((counter < 80) && (temp_path [counter] != 0))
{
if (temp_path [counter] == '/')
temp_path [counter] = '.';
counter ++;
}
counter --;
/*** Get rid of the extra dots... ***/
while ((temp_path [counter] == '.') && (counter > 0))
{
temp_path [counter] = 0;
counter --;
}
strcat (temp_path, "]*.*");
if (!temp_path)
strcpy (filespec.dsc$a_pointer, "*.*");
else
strcpy (filespec.dsc$a_pointer, temp_path);
filespec.dsc$w_length = strlen (filespec.dsc$a_pointer);
three_counter = 0;
old_length = new_files . dsc$w_length;
/*
add_message ("\n");
add_message (filespec . dsc$a_pointer);
add_message ("\n");
*/
/*** The top of the loop. ***/
again:
new_files . dsc$w_length = old_length;
/* Go get me a file that matches that filespec */
status = lib$find_file (&filespec, &new_files, &context, 0, 0, 0, 0);
if (status & 1)
{
/* Remove the disk, and directory from the name */
/* by searching for a ']' */
/* Eg. $disk:[harris.io]filename.c;45 */
/* will turn into filename.c;45 */
i = 0; /* i will hold the start of the filename */
while ((new_files . dsc$a_pointer [ i ] != ']') &&
(i < old_length))
i++;
i ++; /* So we don't include the ']' */
/* These names will have some spaces at the end of them. */
/* Also they will end with a semi-colon and a version number. */
/* Remove the semi-color and the version number. */
j = 0; /* j will hold the end of the filename */
while ((new_files . dsc$a_pointer [ j ] != ';') &&
(j < old_length))
j ++;
new_files . dsc$a_pointer [j] = 0;
new_files . dsc$w_length = j - i;
three_counter += strlen (& (new_files . dsc$a_pointer [i]));
add_message (& (new_files . dsc$a_pointer [i]));
/*** Here I need to print some tabs. ***/
if (three_counter < 8)
{
add_message ("\t\t\t");
three_counter = 24;
}
else if (three_counter < 16)
{
add_message ("\t\t");
three_counter = 24;
}
else if (three_counter < 24)
{
add_message ("\t");
three_counter = 24;
}
else if (three_counter < 32)
{
add_message ("\t\t\t");
three_counter = 48;
}
else if (three_counter < 40)
{
add_message ("\t\t");
three_counter = 48;
}
else if (three_counter < 48)
{
add_message ("\t");
three_counter = 48;
}
else
{
add_message ("\n");
three_counter = 0;
}
file_count ++;
goto again;
}
else if (status == RMS$_FNF)
add_message ("File not found!\n");
else if (status == RMS$_DNF)
add_message ("Directory not found!\n");
else if (status == RMS$_NMF)
; /* Do nothing. We go to the end of the list. */
else if (status == RMS$_SYN)
add_message ("File syntax error. (Don't confuse me!)\n");
else if (status == RMS$_FND)
add_message ("File messed up in a big way, call a god.\n");
else
if (! (status & 1))
{
printf ("list_files () error status code = %X\n", status);
add_message ("Wierd error code on list_files (), have a god check the log.\n");
/*** This crashes the game on all those wierd errors ***/
/*** I don't catch. ***/
/* lib$signal (status);*/
}
if (status == RMS$_NMF)
{
if (three_counter != 0)
add_message ("\n");
if (file_count == 1)
add_message ("One file found.\n");
else
add_message ("%d files found.\n", file_count);
}
/*** Free up file_find context memory. ***/
lib$find_file_end (&context);
}
void
copy_file (src, dst)
char *src, *dst;
{
FILE *src_f, *dst_f;
int c;
src = check_file_name (src, 0);
dst = check_file_name (dst, 1);
if (src == 0 || dst == 0)
return;
src_f = fopen (src, "r");
if (src_f == 0)
return;
dst_f = fopen (dst, "w");
if (dst_f == 0)
{
fclose (src_f);
return;
}
while ((c = fgetc (src_f)) != EOF)
fputc (c, dst_f);
fclose (src_f);
fclose (dst_f);
}
void
rename_file (src, dst)
char *src, *dst;
{
extern int sys_nerr;
extern char *sys_errlist[];
src = check_file_name (src, 0);
dst = check_file_name (dst, 1);
if (src == 0 || dst == 0)
return;
if (rename (src, dst) == -1)
add_message ("rename: %s\n",
errno < sys_nerr ? sys_errlist[errno] : "unknown error");
}
int
tail (path)
char *path;
{
char buff[1000];
FILE *f;
struct stat st;
int offset;
path = check_file_name (path, 0);
if (path == 0)
return 0;
f = fopen (path, "r");
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;
}
void
remove_file (path)
char *path;
{
path = check_file_name (path, 1);
if (path == 0)
return;
if (unlink (path) == -1)
add_message ("No such file: %s\n", path);
return;
}
void
people ()
{
struct object *ob;
struct value *player;
for (ob = obj_list; ob; ob = ob->next_all)
{
if (!ob->interactive)
continue;
player = apply ("query_name", ob, 0);
if (player == 0 || player->type != T_STRING)
continue;
if (!ob->super)
continue;
show_info_about (player->u.string, ob->super->name, ob->interactive);
}
}
void
log_file (file, str)
char *file, *str;
{
FILE *f;
char file_name[100];
if (strchr (file, '/') || file[0] == '.' || strlen (file) > 30)
error ("Illegal file name to log_file(%s)\n", file);
sprintf (file_name, "log/%s", file);
f = fopen (file_name, "a");
if (f == 0)
return;
fwrite (str, strlen (str), 1, f);
fclose (f);
}
/*VARARGS1*/
struct value *
call_indirect (fun, arg, arg2, arg3)
int fun;
int arg, arg2, arg3;
{
struct value *ret;
struct object *o1, *o2;
switch (fun)
{
case F_FIND_OBJECT :
o1 = find_object2 ((char *) arg);
if (!o1)
return &const0;
ret = alloc_value ();
ret->type = T_OBJECT;
ret->u.ob = o1;
add_ref (o1, "F_FIND_OBJECT");
return ret;
case F_CONTENTS :
return contents ((struct value *) arg);
case F_IN_EDITOR :
if (((struct object *) arg)->destructed)
return &const0;
if (((struct object *) arg)->interactive &&
((struct object *) arg)->ed_buffer)
return &const1;
return &const0;
case F_SEARCHA :
return search_array ((struct vector *) arg, (struct value *) arg2, arg3);
case F_SNOOP :
if (arg && ((struct object *) arg)->destructed)
error ("snoop() on destructed object.\n");
if (!current_object->interactive)
error ("snoop() from non-interactive object!\n");
set_snoop (current_object, (struct object *) arg);
return 0;
case F_SET_HEART_BEAT :
if (set_heart_beat (current_object, arg))
return &const1;
else
return &const0;
case F_READ_FILE :
{
char *str = read_file ((char *) arg, arg2);
if (str)
return make_string (str);
return &const0;
}
case F_LOG_FILE :
log_file ((char *) arg, (char *) arg2);
return 0;
case F_SHUTDOWN :
startshutdowngame ();
return 0;
case F_LIVING :
if (((struct object *) arg)->destructed)
return &const0;
ret = alloc_value ();
ret->type = T_NUMBER;
ret->u.number = ((struct object *) arg)->enable_commands;
return ret;
case F_ED :
if (arg && !legal_path ((char *) arg))
{
add_message ("Illegal path\n");
return 0;
}
ed_start ((char *) arg);
return 0;
case F_TELL_OBJECT :
if (((struct object *) arg)->destructed)
error ("Tell_object to destructed object./\n");
tell_object ((struct object *) arg, (char *) arg2);
return 0;
case F_RM :
remove_file ((char *) arg);
return 0;
case F_LS :
list_files ((char *) arg, (char *) 0);
return 0;
case F_CAT :
return make_number (print_file ((char *) arg, arg2, arg3));
case F_CP :
copy_file ((char *) arg, (char *) arg2);
return &const0;
case F_RENAME :
rename_file ((char *) arg, (char *) arg2);
return &const0;
case F_INPUT_TO :
if (current_object->destructed)
error ("input_to() on destructed object.\n");
return input_to ((char *) arg, arg2);
break;
case F_ENABLE_COMMANDS :
enable_commands (1);
return 0;
case F_INTERACTIVE :
if (((struct object *) arg)->destructed)
return &const0;
if (((struct object *) arg)->interactive)
return &const1;
return &const0;
case F_FIRST_INVENTORY :
return first_inventory ((struct value *) arg);
case F_NEXT_INVENTORY :
return next_inventory ((struct value *) arg);
case F_SHOUT :
shout_string ((char *) arg);
return 0;
case F_SAY :
say ((struct value *) arg, (struct object *) arg2);
return 0;
case F_TELL_ROOM :
if (((struct object *) arg)->destructed)
error ("tell_room to destructed object.\n");
tell_room ((struct object *) arg, (struct value *) arg2);
return 0;
case F_CREATE_WIZARD :
return create_wizard ((char *) arg);
case F_DESTRUCT :
destruct_object ((struct value *) arg);
return 0;
case F_SET_LIGHT :
add_light (current_object, arg);
ret = alloc_value ();
ret->type = T_NUMBER;
o1 = current_object;
while (o1->super)
o1 = o1->super;
ret->u.number = o1->total_light;
return ret;
case F_COMMAND :
if (command_for_object ((char *) arg, (struct object *) arg2))
return &const1;
else
return &const0;
case F_PRESENT :
if (arg2 && ((struct object *) arg2)->destructed)
error ("present() on destructed object.\n");
return object_present ((struct value *) arg, (struct object *) arg2);
case F_ENVIRONMENT :
return environment ((struct value *) arg);
case F_SAVE_OBJECT :
save_object (current_object, (char *) arg);
return 0;
case F_RESTORE_OBJECT :
ret = alloc_value ();
ret->type = T_NUMBER;
ret->u.number = restore_object (current_object, (char *) arg);
return ret;
case F_CLONE_OBJECT :
return clone_object ((char *) arg);
case F_FUNCTION :
return call_local_function ((char *) arg, (struct value *) arg2);
case F_CALL_OTHER :
return call_other ((struct value *) arg, (char *) arg2,
(struct value *) arg3);
case F_WRITE :
do_write ((struct value *) arg);
break;
case F_MOVE_OBJECT :
if (((struct value *) arg)->type == T_OBJECT)
o1 = ((struct value *) arg)->u.ob;
else
{
o1 = find_object (((struct value *) arg)->u.string);
if (o1 == 0)
error ("Object %s not found.\n", ((struct value *) arg)->u.string);
}
if (((struct value *) arg2)->type == T_OBJECT)
o2 = ((struct value *) arg2)->u.ob;
else
{
o2 = find_object (((struct value *) arg2)->u.string);
if (o2 == 0)
error ("Object %s not found.\n", ((struct value *) arg2)->u.string);
}
if (((struct object *) o1)->destructed)
error ("move_object() of destructed object.\n");
if (((struct object *) o2)->destructed)
error ("move_object() to destructed object.\n");
move_object (o1, o2);
break;
case F_ADD_VERB :
add_verb ((char *) arg, 0);
break;
case F_ADD_XVERB :
add_verb ((char *) arg, 1);
break;
case F_GREP :
grep_file ((char *) arg, (char *) arg2, (char *) arg3);
return &const0;
case F_THIS_PLAYER :
return this_player ();
case F_CALLER :
return caller ();
case F_THIS_OBJECT :
return this_object ();
default :
fatal ("Unimplemented hard linked function %d\n", fun);
abort ();
}
return 0;
}
void
do_write (arg)
struct value *arg;
{
if (arg == 0)
add_message ("<NULL>");
else if (arg->type == T_STRING)
{
if (strlen (arg->u.string) > 9500) /* Not pretty */
error ("Too long string.\n");
add_message ("%s", arg->u.string);
}
else if (arg->type == T_OBJECT)
add_message ("OBJ(%s)", arg->u.ob->name);
else if (arg->type == T_NUMBER)
add_message ("%d", arg->u.number);
else
add_message ("<UNKNOWN>");
}
/* Find an object. If not loaded, load it ! */
struct object *
find_object (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);
if (ob->destructed) /* *sigh* */
return 0;
if (ob->swapped)
load_ob_from_swap (ob);
return ob;
}
/* Look for a loaded object. */
struct object *
find_object2 (str)
char *str;
{
register struct object *ob;
register int length;
char *name;
/* Remove leading '/' if any. */
while (str[0] == '/')
str++;
/* Truncate possible .c in the object name. */
length = strlen (str);
name = string_copy (str);
if (name[length - 2] == '.' && name[length - 1] == 'c')
{
name[length - 2] = '\0';
length -= 2;
}
if (ob = lookup_object_hash (name))
{
free (name);
if (ob->swapped)
load_ob_from_swap (ob);
return ob;
}
free (name);
return 0;
}
void
apply_command (com)
char *com;
{
struct value *ret;
if (command_giver == 0)
error ("command_giver == 0 !\n");
ret = apply (com, command_giver->super, 0);
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);
}
}
#if 0
/* This is merged into move_object() */
void
set_current_room (ob, dest)
struct object *ob, *dest;
{
struct object **pp, *p;
struct object *save_command_giver = command_giver;
if (dest == 0)
dest = find_object ("room/void.c"); /* Get any existing void. */
if (dest == 0)
fatal ("Not even a void !\n");
command_giver = ob;
if (ob->super)
{
struct value v;
v.type = T_OBJECT;
v.u.ob = ob; /* No need to increment ref count */
(void) apply ("exit", ob->super, &v);
add_light (ob->super, -ob->total_light);
remove_sent (ob->super, ob);
/*
* Now we link the ob out of its list.
* Remove sentences tied to objects that stays in this room.
*/
for (pp = &ob->super->contains; *pp;)
{
if (*pp == ob)
*pp = (*pp)->next_inv;
else
{
remove_sent (*pp, ob);
pp = &(*pp)->next_inv;
}
}
}
ob->next_inv = dest->contains;
dest->contains = ob;
add_light (dest, ob->total_light);
ob->super = dest;
if (d_flag)
debug_message ("--Current room: %s\n", dest->name);
(void) apply ("init", dest, 0);
for (p = dest->contains; p; p = p->next_inv)
{
if (p == ob)
continue;
(void) apply ("init", p, 0);
}
command_giver = save_command_giver;
}
#endif
/*
* 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;
struct object *save_cmd = command_giver;
/* Recursive moves are not allowed. */
for (ob = dest; ob; ob = ob->super)
if (ob == item)
return;
if (item->last_reset < (current_time - RESET_TIME * 60))
{
item->last_reset = current_time;
(void) apply ("reset", item, &const1);
if (item->destructed)
return;
}
if (dest->last_reset < (current_time - RESET_TIME * 60))
{
dest->last_reset = current_time;
(void) apply ("reset", dest, &const1);
if (dest->destructed)
return;
}
if (item->super)
{
int okey = 0;
if (item->enable_commands)
{
struct value v;
v.type = T_OBJECT;
v.u.ob = item; /* No need to increment ref count */
command_giver = item;
(void) apply ("exit", item->super, &v);
remove_sent (item->super, item);
}
if (item->super->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)->enable_commands)
remove_sent (item, *pp);
if (item->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.
*/
if (item->enable_commands)
{
command_giver = item;
(void) apply ("init", dest, 0);
if (dest->destructed)
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 = ob->next_inv)
{
if (ob == item)
continue;
if (ob->enable_commands)
{
command_giver = ob;
(void) apply ("init", item, 0);
}
if (item->enable_commands)
{
command_giver = item;
(void) apply ("init", ob, 0);
}
}
if (dest->enable_commands)
{
command_giver = dest;
(void) apply ("init", item, 0);
}
command_giver = save_cmd;
add_light (dest, item->total_light);
if (d_flag)
debug_message ("--move_object: %s to %s\n", item->name, dest->name);
}
/*
* 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 *) xalloc (sizeof *p);
tot_alloc_sentence++;
}
else
{
p = sent_free;
sent_free = sent_free->next;
}
p->verb = 0;
p->function = 0;
p->next = 0;
p->no_space = 0;
p->short_verb = 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 player.
* Return success status.
*/
int
player_parser (buff)
char *buff;
{
struct sentence *s;
char *p;
int length;
struct object *save_current_object = current_object;
char verb_copy[20];
if (d_flag)
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;
if (special_parse (buff))
return 1;
p = strchr (buff, ' ');
if (p == 0)
length = strlen (buff);
else
length = p - buff;
clear_notify ();
for (s = command_giver->sent;
s && command_giver && !command_giver->destructed;
s = (s ? s->next : 0))
{
struct value *ret;
struct value arg;
int len;
if (s->verb == 0)
continue;
len = strlen (s->verb);
if (s->no_space)
{
if (strncmp (buff, s->verb, len) != 0)
continue;
}
else if (s->short_verb)
{
if (strncmp (s->verb, buff, len) != 0)
continue;
}
else
{
if (strlen (s->verb) != length)
continue;
if (strncmp (buff, s->verb, length) != 0)
continue;
}
/*
* Now we have found a special sentence !
*/
if (d_flag)
debug_message ("Local command %s on %s\n", s->function, s->ob->name);
if (length > sizeof verb_copy)
len = sizeof verb_copy - 1;
else
len = length;
strncpy (verb_copy, 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.
*/
if (current_object == 0)
current_object = s->ob;
if (s->no_space)
{
arg.type = T_STRING;
arg.u.string = &buff[strlen (s->verb)];
ret = apply (s->function, s->ob, &arg);
}
else if (buff[length] == ' ')
{
arg.type = T_STRING;
arg.u.string = &buff[length + 1];
ret = apply (s->function, s->ob, &arg);
}
else
{
ret = apply (s->function, s->ob, 0);
}
if (current_object->destructed)
s = command_giver->sent;/* Restart :-( */
last_verb = 0;
current_object = save_current_object;
/* If we get fail from the call, it was wrong second argument. */
if (ret && ret->type == T_NUMBER && ret->u.number == 0)
continue;
#if 0
if (s && s->ob && s->ob->wl && command_giver->interactive &&
!command_giver->is_wizard)
s->ob->wl->score++;
#endif
if (ret == 0)
add_message ("Error: function %s not found.\n", s->function);
break;
}
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. 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.
*/
void
add_action (str, cmd, flag)
char *str, *cmd;
int flag;
{
struct sentence *p;
if (command_giver == 0)
return;
if (current_object != command_giver &&
current_object->super != command_giver &&
current_object->super != command_giver->super &&
current_object != command_giver->super)
error ("add_action from object that was not present.\n");
if (d_flag)
debug_message ("--Add action %s\n", str);
if (strcmp (str, "exit") == 0)
error ("Illegal to define a command to the exit() function.\n");
p = alloc_sentence ();
p->function = make_shared_string (str);
p->ob = current_object;
p->next = command_giver->sent;
p->short_verb = flag;
p->no_space = 0;
if (cmd)
p->verb = make_shared_string (cmd);
else
p->verb = 0;
command_giver->sent = p;
}
void
add_verb (str, no_space)
char *str;
int no_space;
{
if (command_giver == 0)
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);
command_giver->sent->no_space = no_space;
command_giver->sent->short_verb = 0;
if (d_flag)
debug_message ("--Adding verb %s to action %s\n", str,
command_giver->sent->function);
}
void
remove_sent (ob, player)
struct object *ob, *player;
{
struct sentence **s;
for (s = &player->sent; *s;)
{
struct sentence *tmp;
if ((*s)->ob == ob)
{
if (d_flag)
debug_message ("--Unlinking sentence %s\n", (*s)->function);
tmp = *s;
*s = tmp->next;
free_sentence (tmp);
}
else
s = &((*s)->next);
}
}
#if 0
/* This function has been replaced by using users(). */
void
display_all_players ()
{
struct object *ob;
struct value *ret;
for (ob = obj_list; ob; ob = ob->next_all)
{
if (ob->interactive == 0)
continue;
ret = apply ("short", ob, 0);
if (ret && ret->type == T_STRING)
add_message ("%s.\n", ret->u.string);
}
}
#endif
char debug_parse_buff[50]; /* Used for debugging */
int
special_parse (buff)
char *buff;
{
/*
if (strcmp(buff, "who") == 0) {
display_all_players();
return 1;
}
*/
strncpy (debug_parse_buff, buff, sizeof debug_parse_buff);
if (strcmp (buff, "malloc") == 0)
{
#if defined(MALLOC_malloc) || defined(MALLOC_smalloc)
dump_malloc_data ();
#endif
#ifdef MALLOC_gmalloc
add_message ("Using Gnu malloc.\n");
#endif
return 1;
}
if (strcmp (buff, "dumpallobj") == 0)
{
dumpstat ();
return 1;
}
#if defined(MALLOC_malloc) || defined(MALLOC_smalloc)
if (strcmp (buff, "debugmalloc") == 0)
{
extern int debugmalloc;
debugmalloc = !debugmalloc;
if (debugmalloc)
add_message ("On.\n");
else
add_message ("Off.\n");
return 1;
}
#endif
if (strcmp (buff, "status") == 0)
{
extern int tot_alloc_sentence, tot_alloc_value, tot_alloc_object,
num_swapped, total_bytes_swapped, tot_string_space;
extern int total_num_prog_blocks, total_prog_block_size;
add_message ("Sentences: %5d %6d\n", tot_alloc_sentence,
tot_alloc_sentence * sizeof (struct sentence));
add_message ("Objects: %5d %6d (%d swapped, %d Kbyte)\n",
tot_alloc_object,
tot_alloc_object * sizeof (struct object), num_swapped,
total_bytes_swapped / 1024);
add_message ("Prog blocks: %5d %6d\n", total_num_prog_blocks,
total_prog_block_size);
add_message ("Values: %5d %6d\n\n", tot_alloc_value,
tot_alloc_value * sizeof (struct value));
status_lnodes_matched ();
stat_living_objects ();
print_lnode_status (tot_alloc_sentence * sizeof (struct sentence) +
tot_alloc_object * sizeof (struct object) +
tot_alloc_value * sizeof (struct value) +
total_prog_block_size +
show_otable_status () +
heart_beat_status () +
add_string_status () +
print_call_out_usage ());
return 1;
}
return 0;
}
void
print_local_commands ()
{
struct sentence *s;
add_message ("Current local commands:\n");
for (s = command_giver->sent; s; s = s->next)
add_message ("%s ", s->verb);
add_message ("\n");
}
/*
* 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];
/*VARARGS1*/
void
fatal (fmt, a, b, c, d, e, f, g, h)
char *fmt;
int a, b, c, d, e, f, g, h;
{
static int in_fatal = 0;
/* Prevent double fatal. */
if (in_fatal)
abort ();
in_fatal = 1;
(void) fprintf (stderr, fmt, a, b, c, d, e, f, g, h);
fflush (stderr);
if (current_object)
(void) fprintf (stderr, "Current object was %s\n",
current_object->name);
debug_message (fmt, a, b, c, d, e, f, g, h);
if (current_object)
debug_message ("Current object was %s\n", current_object->name);
debug_message ("Dump of variables:\n");
#ifdef TRACE
(void) dump_trace ();
#endif
abort ();
}
int num_error = 0;
/*VARARGS1*/
void
error (fmt, a, b, c, d, e, f, g, h)
char *fmt;
int a, b, c, d, e, f, g, h;
{
char *object_name;
extern int error_recovery_context_exists;
extern jmp_buf error_recovery_context;
extern struct object *current_heart_beat;
#ifdef TRACE
extern int trace_depth;
#endif
sprintf (emsg_buf + 1, fmt, a, b, c, d, e, f, g, h);
emsg_buf[0] = '*';
if (error_recovery_context_exists > 1)
{ /* user catches this error */
catch_value = make_string (emsg_buf);
longjmp (error_recovery_context, 1);
fatal ("Catch() longjump failed");
}
num_error++;
if (num_error > 1)
fatal ("Too many simultaneous errors.\n");
debug_message ("%s", emsg_buf + 1 /*fmt, a, b, c, d, e, f, g, h*/ );
if (current_object)
{
debug_message ("Current object was %s, line %d\n",
current_object->name, current_line);
save_error (emsg_buf + 1, current_object->name, current_line);
}
#ifdef TRACE
object_name = dump_trace ();
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);
}
}
trace_depth = 0;
#endif
if (command_giver)
{
add_message ("%s", emsg_buf + 1 /*fmt, a, b, c, d, e, f, g, h*/ );
if (current_object)
add_message ("Current object was %s, line %d\n",
current_object->name, current_line);
#if 0
if (!command_giver->destructed)
{
dest = find_object ("room/void.c"); /* Get any existing void. */
if (dest == 0)
fatal ("Could not find the void room.\n");
move_object (command_giver, dest);
}
#endif
}
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 ("Game driver tells you: You have no heart beat !\n");
command_giver = save_cmd;
}
current_heart_beat = 0;
}
#if 0
debug_message ("Dump of variables:\n");
if (current_object)
{
struct lnode_var_def *p;
for (p = current_object->status; p; p = p->next)
{
debug_message ("%20s: ", p->name);
debug_message_value (¤t_object->variables[p->num_var]);
debug_message ("\n");
}
}
#endif
num_error--;
if (error_recovery_context_exists)
longjmp (error_recovery_context, 1);
abort ();
}
#ifdef TRACE
char *
get_current_object_name ()
{
if (current_object == 0)
return "NONE";
return current_object->name;
}
char *
get_command_giver_name ()
{
if (command_giver == 0)
return "NONE";
return command_giver->name;
}
#endif
void
pre_compile (str)
char *str;
{
char *c_name, *i_name, buff[1000];
FILE *f;
if (!legal_path (str))
error ("Illegal attempt to access %s\n", str);
i_name = xalloc (strlen (str) + 3);
(void) strcpy (i_name, str);
(void) strcat (i_name, ".i");
c_name = (char *) xalloc (strlen (str) + 3);
(void) strcpy (c_name, str);
(void) strcat (c_name, ".c");
sprintf (buff, "%s %s %s", PRE_COMPILE, c_name, i_name);
f = (FILE *) vpopen (buff, "r");
if (f == 0)
{
error ("Unable to invoke precompiler!\n");
alarm (0);
}
while (1)
{
if (fgets (buff, sizeof buff, f) == 0)
break;
add_message ("%s", buff);
}
vpclose (f);
free (c_name);
free (i_name);
}
/*** It seems the VAX doesn't like a #ifdef 0 (bub) ***/
#ifdef ZERO0ZERO
void
pre_compile (str)
char *str;
{
char *i_name;
int pid;
int fd, fd2;
unsigned int save_alarm;
i_name = xalloc (strlen (str) + 3);
(void) strcpy (i_name, str);
(void) strcat (i_name, ".i");
/*
* We don't want the child process to have an alarm signal.
* The alarm might as well be removed here, instead of in the child
* after vfork(), because some Unix (read: apollo) will remove the alarm
* for the parent also when done in vfork before exec().
*/
save_alarm = alarm (0);
/*** in case alarm rounds down and returns a zero. ***/
if (save_alarm == 0)
save_alarm = 1;
#ifdef USE_VFORK
pid = vfork ();
#else
pid = fork ();
#endif
if (pid)
{
/* This is the parent. Wait for the childe to complete. */
int result;
/* Enable the alarm again. */
(void) alarm (save_alarm);
while (1)
{
if (wait (&result) != -1)
break;
if (errno == EINTR)
continue;
perror ("wait");
abort ();
}
#ifdef USE_VFORK
/*
* When using vfork(), i_name can have been changed by the child
* into name.c. Change this back.
*/
i_name[strlen (i_name) - 1] = 'i';
#endif
/*
* Remove the i-file here, because the file might contain include
* of files from ../../../ etc. The i-file will also be removed
* if there is a compilation error.
*/
if (result != 0)
unlink (i_name);
free (i_name);
if (result == 0)
return;
error ("Precompiler returned signal %d, return code %d\n",
result & 0xf, result >> 8);
}
fd = creat (i_name, 0666);
if (fd == -1)
{
perror (i_name);
/*
* Do not call exit() now, which would close all files for parent.
*/
_exit (1);
}
if ((fd2 = dup2 (fd, 1)) != 1)
{
extern int errno;
fprintf (stderr, "FATAL: failed to dup2. fd2 = %d, errno = %d, fd = %d\n",
fd2, errno, fd);
_exit (1);
}
close (fd);
(void) strcpy (i_name, str);
(void) strcat (i_name, ".c");
if (PRE_COMPILE (i_name) == -1)
{
perror ("precompile");
_exit (99);
}
_exit (0);
}
#endif
/*
* Check that it is an legal path. No '..' are allowed.
*/
int
legal_path (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;
}
void
smart_log (error_file, line, what)
char *error_file, *what;
int line;
{
char buff2[100], buff[100], *p;
int n;
if (error_file == 0)
return;
n = sscanf (error_file, "players/%s", buff2);
if (n != 1)
return;
p = strchr (buff2, '/');
if (p)
*p = '\0';
sprintf (buff, "%s line %d:%s\n", error_file, line, what);
log_file (buff2, buff);
}
/*
* Check that a file name is valid for read or write.
* Also change the name as if the current directory was at the players
* own directory.
* This is done by functions in the player object.
*/
char debug_check_file[50];
char *
check_file_name (file, writeflg)
char *file;
int writeflg;
{
struct value v, *ret;
if (!command_giver)
return 0;
v.type = T_STRING;
v.u.string = file;
/*
* We don't have to free the string in ret. This is done
* by the garbage collection.
*/
if (writeflg)
ret = apply ("valid_write", command_giver, &v);
else
ret = apply ("valid_read", command_giver, &v);
if (command_giver->destructed)
return 0;
if (ret->type != T_STRING)
{
add_message ("Bad file name.\n");
return 0;
}
strncpy (debug_check_file, ret->u.string, sizeof debug_check_file);
if (!legal_path (ret->u.string))
{
add_message ("Illegal path\n");
return 0;
}
return ret->u.string;
}
/*
* This one is called from HUP.
*/
int game_is_being_shut_down;
void
startshutdowngame ()
{
game_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
shutdowngame ()
{
shout_string ("Game driver shouts: LPmud shutting down immediately.\n");
save_wiz_file ();
ipc_remove ();
remove_all_players ();
#ifdef free
remove_all_objects ();
free_all_sent ();
remove_wiz_list ();
dump_malloc_data ();
find_alloced_data ();
#endif
exit (0);
}
/*
* Transfer an object from an object to an object.
* Call add_weight(), drop(), get(), prevent_insert(), add_weight(),
* and can_put_and_get() where needed.
* Return 0 on success, and special code on failure:
*
* 1: To heavy for destination.
* 2: Can't be dropped.
* 3: Can't take it out of it's container.
* 4: The object can't be inserted into bags etc.
* 5: The destination doesn't allow insertions of objects.
* 6: The object can't be picked up.
*/
int
transfer_object (ob, to)
struct object *ob, *to;
{
struct value *weight, neg_weight, *ret;
struct object *from = ob->super;
/*
* Get the weight of the object
*/
weight = apply ("query_weight", ob, 0);
if (weight && weight->type != T_NUMBER)
error ("Bad type the weight of object in transfer()\n");
if (ob->destructed)
return 3;
/*
* If the original place of the object is a living object,
* then we must call drop() to check that the object can be dropped.
*/
if (from && from->enable_commands)
{
ret = apply ("drop", ob, 0);
if (ret && (ret->type != T_NUMBER || ret->u.number != 0))
return 2;
/* This shold not happen, but we can't trust LPC hackers. :-) */
if (ob->destructed)
return 2;
}
/*
* If 'from' is not a room and not a player, check that we may
* remove things out of it.
*/
if (from && from->super && !from->enable_commands)
{
ret = apply ("can_put_and_get", from, 0);
if (!ret || (ret->type != T_NUMBER && ret->u.number != 1) ||
from->destructed)
return 3;
}
/*
* If the destination is not a room, and not a player,
* Then we must test 'prevent_insert', and 'can_put_and_get'.
*/
if (to->super && to->enable_commands == 0)
{
ret = apply ("prevent_insert", ob, 0);
if (ret && (ret->type != T_NUMBER || ret->u.number != 0))
return 4;
ret = apply ("can_put_and_get", to, 0);
if (!ret || (ret->type != T_NUMBER && ret->type != 0) ||
to->destructed || ob->destructed)
return 5;
}
/*
* If the destination is a player, check that he can pick it up.
*/
if (to->enable_commands)
{
ret = apply ("get", ob, 0);
if (!ret || (ret->type == T_NUMBER && ret->u.number == 0) ||
ob->destructed)
return 6;
}
/*
* If it is not a room, correct the total weight in the destination.
*/
if (to->super && weight)
{
/*
* Check if the destination can carry that much.
*/
ret = apply ("add_weight", to, weight);
if (ret && ret->type == T_NUMBER && ret->u.number == 0)
return 1;
if (to->destructed)
return 1;
}
/*
* If it is not a room, correct the weight in the 'from' object.
*/
if (from && from->super && weight)
{
neg_weight.type = T_NUMBER;
neg_weight.u.number = -weight->u.number;
(void) apply ("add_weight", from, &neg_weight);
}
move_object (ob, to);
return 0;
}
/*
* Move or destruct one object.
*/
void
move_or_destruct (what, to)
struct object *what, *to;
{
int res;
struct value v;
res = transfer_object (what, to);
if (res == 0 || what->destructed)
return;
if (res == 1 || res == 4 || res == 5)
{
move_or_destruct (what, to->super);
return;
}
/*
* No need to add the reference count of 'what', as this
* local 'v' is not deallocated by 'free_all_value()'
*/
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;
{
struct object *ob;
struct value v;
/*
* Swap out objects, and free some memory.
*/
#if 0
#if NUM_RESET_TO_SWAP > 0
for (ob = obj_list; ob; ob = ob->next_all)
{
if (!ob->swapped)
swap (ob);
}
#endif
#endif
ob = find_object ("obj/shut");
if (!ob)
{
struct object *save_current = current_object, *save_command = command_giver;
command_giver = 0;
current_object = 0;
shout_string ("Game driver shouts: Out of memory.\n");
command_giver = save_command;
current_object = save_current;
startshutdowngame ();
return;
}
shout_string ("Game driver shouts: The memory is getting low !\n");
v.type = T_NUMBER;
v.u.number = minutes;
(void) apply ("shut", ob, &v);
}
int
match_string (match, str)
char *match, *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;
}
}
char *
read_file (file, line)
char *file;
int line;
{
FILE *f;
static char buff[1024];
int i;
if (line <= 0)
return 0;
if (!(file = check_file_name (file, 0)))
return 0;
if (!(f = fopen (file, "r")))
return 0;
for (i = 1; i < line; i++)
{
if (!(fgets (buff, sizeof (buff), f)))
{
fclose (f);
return 0;
}
}
if (!(fgets (buff, sizeof (buff), f)))
{
fclose (f);
return 0;
}
fclose (f);
return buff;
}
struct value *
search_array (a, target, index)
struct vector *a;
struct value *target;
int index;
{
struct value *ret;
struct value orig_targ;
int i;
if (!a)
error ("Bad array pointer in searcha().\n");
if (target->type != T_STRING && target->type != T_OBJECT
&& target->type != T_NUMBER && target->type != T_POINTER)
error ("Bad argument 2 to searcha().\n");
if (index + 1 > a->size || index < 0 || index > MAX_ARRAY_SIZE)
error ("Illegal index to searcha().\n");
orig_targ = *target;
if (target->type == T_OBJECT && target->u.ob->destructed)
{
target->type = T_NUMBER;
target->u.number = 0;
}
ret = alloc_value ();
ret->type = T_NUMBER;
ret->u.number = -1;
for (i = index; i < a->size; i++)
{
if (a->item[i].type != target->type &&
!(a->item[i].type == T_OBJECT && a->item[i].u.ob->destructed &&
target->type == T_NUMBER))
continue;
switch (target->type)
{
case T_OBJECT :
if (a->item[i].u.ob == target->u.ob &&
!a->item[i].u.ob->destructed)
ret->u.number = i;
break;
case T_STRING :
if (strcmp (a->item[i].u.string, target->u.string) == 0)
ret->u.number = i;
break;
case T_POINTER :
if (a->item[i].u.vec == target->u.vec)
ret->u.number = i;
break;
case T_NUMBER :
if (a->item[i].u.number == target->u.number)
ret->u.number = i;
else if (a->item[i].type == T_OBJECT &&
a->item[i].u.ob->destructed &&
target->u.number == 0)
ret->u.number = i;
break;
default :
break;
}
if (ret->u.number != -1)
break;
}
*target = orig_targ;
return ret;
}
struct value *
contents (arg)
struct value *arg;
{
struct object *ob;
struct value *ret;
int count;
if (arg->type == T_STRING)
ob = find_object (arg->u.string);
else
ob = arg->u.ob;
if (ob == 0)
error ("No object to contents()");
if (ob->contains == 0)
return &const0;
for (ob = ob->contains, count = 0; ob; ob = ob->next_inv, count++) ;
ret = allocate_array (count);
for (ob = arg->u.ob->contains, count = 0; ob; ob = ob->next_inv, count++)
{
ret->u.vec->item[count].type = T_OBJECT;
ret->u.vec->item[count].u.ob = ob;
add_ref (ob, "contents");
}
return ret;
}
int
print_file (path, start, len)
char *path;
int start, len;
{
char buff[1000];
FILE *f;
int i, num_read = 0;;
if (len < 0)
return -1;
path = check_file_name (path, 0);
if (path == 0)
return -1;
f = fopen (path, "r");
if (f == 0)
return -1;
if (start < 0)
{
int lines = 0;
while (fgets (buff, sizeof buff, f) != 0)
lines++;
rewind (f);
start = lines - len + 1;
}
for (i = 1; i < start; i++)
{
if (fgets (buff, sizeof buff, f) == 0)
break;
}
if (!len || len > MAX_LINES)
len = MAX_LINES;
for (i = 0; i < len; i++)
{
if (fgets (buff, sizeof buff, f) == 0)
break;
num_read++;
add_message ("%s", buff);
}
if (i == MAX_LINES)
add_message ("*****TRUNCATED*****\n");
fclose (f);
return num_read;
}