/
MudOS_0.9.18/bin/
MudOS_0.9.18/doc/concepts/
MudOS_0.9.18/doc/driver/
MudOS_0.9.18/doc/efuns/bitstrings/
MudOS_0.9.18/doc/efuns/communication/
MudOS_0.9.18/doc/efuns/core/
MudOS_0.9.18/doc/efuns/mappings/
MudOS_0.9.18/doc/efuns/math/
MudOS_0.9.18/doc/efuns/security/
MudOS_0.9.18/doc/lpc/constructs/
MudOS_0.9.18/doc/lpc/types/
MudOS_0.9.18/doc/platforms/
MudOS_0.9.18/etc/
MudOS_0.9.18/mudlib/
MudOS_0.9.18/mudlib/lil/
MudOS_0.9.18/mudlib/lil/clone/
MudOS_0.9.18/mudlib/lil/command/
MudOS_0.9.18/mudlib/lil/data/
MudOS_0.9.18/mudlib/lil/etc/
MudOS_0.9.18/mudlib/lil/include/
MudOS_0.9.18/mudlib/lil/inherit/
MudOS_0.9.18/mudlib/lil/inherit/master/
MudOS_0.9.18/mudlib/lil/log/
MudOS_0.9.18/mudlib/lil/single/
MudOS_0.9.18/mudlib/lil/u/
MudOS_0.9.18/src/amiga/src/amiga/
#include "config.h"
#ifdef SunOS_5
#include <stdlib.h>
#endif
#include <sys/types.h>
#include <sys/stat.h>
#if !defined(SunOS_5)
#include <sys/dir.h>
#endif
#include <fcntl.h>
#include <setjmp.h>
#include <string.h>
#include <errno.h>
#include <stdio.h>
#ifndef LATTICE
#include <varargs.h>
#include <memory.h>
#else
#include <signal.h>
#include "amiga.h"
#endif
#if defined(sun)
#include <alloca.h>
#endif
#if defined(OSF) || defined(M_UNIX)
#include <dirent.h>
#endif

#include "lint.h"
#include "opcodes.h"
#include "interpret.h"
#include "object.h"
#include "sent.h"
#include "exec.h"
#include "comm.h"
#include "debug.h"

extern int errno;
extern int current_time;
extern int comp_flag;

char *inherit_file;

/* prevents infinite inherit loops.
   No, mark-and-sweep solution won't work.  Exercise for reader.  */
int num_objects_this_thread = 0; 

#ifndef NeXT
extern int readlink PROT((char *, char *, int));
extern int symlink PROT((char *, char *));
#endif /* NeXT */
     
#if !defined(hpux) && !defined(_AIX) && !defined(__386BSD__) \
	&& !defined(linux) && !defined(SunOS_5) && !defined(SVR4) \
	&& !defined(__bsdi__)
extern int fchmod PROT((int, int));
#endif /* !defined(hpux) && !defined(_AIX) */
char *last_verb = 0;
     
extern int set_call PROT((struct object *, struct sentence *, int, int)),
       legal_path PROT((char *));
     
void pre_compile PROT((char *)),
       remove_interactive PROT((struct object *)),
       add_light PROT((struct object *, int)),
       add_action PROT((char *, char *, int)),
       add_verb PROT((char *, int)),
       ipc_remove(),
       set_snoop PROT((struct object *, struct object *)),
       start_new_file PROT((FILE *)), end_new_file(),
       move_or_destruct PROT((struct object *, struct object *)),
       load_ob_from_swap PROT((struct object *)), dump_malloc_data(),
       print_svalue PROT((struct svalue *)),
       debug_message_value(),
       destruct2();
     
extern int d_flag;
     
struct object *obj_list, *obj_list_destruct, *master_ob;
     
extern userid_t *backbone_uid;

struct object *current_object;      /* The object interpreting a function. */
struct object *command_giver;       /* Where the current command came from. */
struct object *current_interactive; /* The user who caused this execution */
     
int num_parse_error;		/* Number of errors in the parser. */
     
struct variable *find_status(str, must_find)
     char *str;
     int must_find;
{
  int i;
  
  for (i=0; i < (int)current_object->prog->p.i.num_variables; i++) {
    if (strcmp(current_object->prog->p.i.variable_names[i].name, str) == 0)
      return &current_object->prog->p.i.variable_names[i];
  }
  if (!must_find)
    return 0;
  error("--Status %s not found in prog for %s\n", str,
	current_object->name);
  return 0;
}

#ifdef PRIVS
void
init_privs_for_object(ob)
    struct object *ob;
{
    struct object *tmp_ob;
    struct svalue *value;
    static char *privs_file_fname = (char *) 0;

    if (master_ob == NULL)
	tmp_ob = ob;
    else {
	assert_master_ob_loaded();
	tmp_ob = master_ob;
    }
    if (!current_object || !current_object->uid) {
	ob->privs = NULL;
	return;
    }
    push_string(ob->name, STRING_CONSTANT);
    if (!privs_file_fname)
	privs_file_fname = make_shared_string("privs_file");
    value = apply(privs_file_fname, tmp_ob, 1);
    if (value == NULL || value->type != T_STRING)
	ob->privs = NULL;
    else
	ob->privs = make_shared_string(value->u.string);
}
#endif /* PRIVS */

/*
 * Give the correct uid and euid to a created object.
 */
int give_uid_to_object(ob)
     struct object *ob;
{
  struct svalue *ret;
  char *creator_name;
  struct object *tmp_ob;
  static char *creator_file_fname = (char *) 0;
  
  if (master_ob == 0)
    tmp_ob = ob;
  else 
    {
      assert_master_ob_loaded();
      tmp_ob = master_ob;
    }
  
  if (!current_object || !current_object->uid) {
    /*
     * Only for the master and void object. Note that
     * back_bone_uid is not defined when master.c is being loaded.
     */
    ob->uid = add_uid ("NONAME");
    ob->euid = NULL;
    
    return 1;
  }
  
  /*
   * Ask master.c who the creator of this object is.
   */
  push_string(ob->name, STRING_CONSTANT);
  if (!creator_file_fname)
    creator_file_fname = make_shared_string("creator_file");
  ret = apply(creator_file_fname, tmp_ob, 1);
  if (!ret)
    error("No function 'creator_file' in master.c!\n");
  if (ret->type != T_STRING) {
    struct svalue arg;
    /* This can be the case for objects in /ftp and /open. */
    arg.type = T_OBJECT;
    arg.u.ob = ob;
    destruct_object(&arg);
    error("Illegal object to load.\n");
  }
  creator_name = ret->u.string;
  /*
   * Now we are sure that we have a creator name.
   * Do not call apply() again, because creator_name will be lost !
   */
  if (strcmp(current_object->uid->name, creator_name) == 0) {
    /* 
     * The loaded object has the same uid as the loader.
     */
    ob->uid = current_object->uid;
    ob->euid = current_object->euid; /* FIXME - is this right? */
    return 1;
  }
  
#ifdef AUTO_TRUST_BACKBONE
  if (strcmp(backbone_uid->name, creator_name) == 0) {
    /*
     * The object is loaded from backbone. This is trusted, so we
     * let it inherit the value of eff_user.
     */
    ob->uid = current_object->euid;
    ob->euid = current_object->euid;
    return 1;
  }
#endif
  
  /*
   * The object is not loaded from backbone, nor from 
   * from the loading objects path. That should be an object
   * defined by another wizard. It can't be trusted, so we give it the
   * same uid as the creator. Also give it eff_user 0, which means that
   * user 'a' can't use objects from user 'b' to load new objects nor
   * modify files owned by user 'b'.
   *
   * If this effect is wanted, user 'b' must let his object do
   * 'seteuid()' to himself. That is the case for most rooms.
   */
  ob->uid = add_uid(creator_name);
#ifdef AUTO_SETEUID
  ob->euid = ob->uid;
#else
  ob->euid = NULL;
#endif
  return 1;
}


int init_object (ob)
     struct object *ob;
{
  init_stats_for_object (ob);
#ifdef PRIVS
  init_privs_for_object (ob);
#endif /* PRIVS */
  add_objects (&ob->stats, 1);
  return give_uid_to_object (ob);
}



struct svalue *
  load_virtual_object(name)
char *name;
{
  static int loading_virtual_object = 0;
  struct svalue *v;
  
  push_string(name, STRING_MALLOC);
  loading_virtual_object++;
  v = apply_master_ob("compile_object", 1);
  loading_virtual_object--;
  if (!v || (v->type != T_OBJECT)) {
    fprintf(stderr, "Could not load descr for %s\n", name);
    error("Failed to load file: %s\n",name);
    return 0;
  }
  return v;
}

/*
 * Load an object definition from file. If the object wants to inherit
 * from an object that is not loaded, discard all, load the inherited object,
 * and reload again.
 *
 * In mudlib3.0 when loading inherited objects, their reset() is not called.
 * - why is this??  it makes no sense and causes a problem when a developer
 * inherits code from a real used item.  Say a room for example.  In this case
 * the room is loaded but is never set up properly, so when someone enters it
 * it's all messed up.  Realistically, I know that it's pretty bad style to
 * inherit from an object that's actually being used and isn't just a building
 * block, but I see no reason for this limitation.  It happens, and when it
 * does occur, produces mysterious results than can be hard to track down.
 * for now, I've reenabled resetting.  We'll see if anything breaks. -WF
 *
 * Save the command_giver, because reset() in the new object might change
 * it.
 * 
 */
struct object *load_object(lname, dont_reset)
     char *lname;
     int dont_reset;
{
  FILE *f;
  extern int total_lines;
  
  struct object *ob, *save_command_giver = command_giver;
  extern struct program *prog;
  struct svalue *mret;
  extern char *current_file;
  struct stat c_st;
  int name_length;
  char real_name[200], name[200];
  char *p;
  
#ifdef VALID_CLONE
  if (current_object && master_ob) {
    char *orig;
    
    orig = lname;
    lname = check_valid_path(lname, current_object, "load_object", 0);
    if (!lname) {
      error("Insufficient permission to read file: %s\n", orig);
    }
  }
#endif
  
  if (++num_objects_this_thread > INHERIT_CHAIN_SIZE)
    error ("Inherit chain too deep: > %d\n",INHERIT_CHAIN_SIZE);
  if (current_object && current_object->euid == NULL)
    error("Can't load objects when no effective user.\n");
  /* don't allow consecutive "/"'s - Wayfarer */
  p = lname;
  while (*p)
    {
      if (*p == '/' && *(p + 1) == '/')
	{
	  error ("Filenames with consecutive /'s in them aren't allowed.\n");
	  return 0; 
	}
      p++;
    }
  /* Truncate possible .c in the object name. */
  /* Remove leading '/' if any. */
  while(lname[0] == '/')
    lname++;
  strncpy(name, lname, sizeof(name) - 1);
  name[sizeof name - 1] = '\0';
  name_length = strlen(name);
  if (name_length > sizeof name - 4)
    name_length = sizeof name - 4;
  name[name_length] = '\0';
  if (name[name_length-2] == '.' && name[name_length-1] == 'c') {
    name[name_length-2] = '\0';
    name_length -= 2;
  }
  /*
   * First check that the c-file exists.
   */
  (void) strcpy(real_name, name);
  (void) strcat(real_name, ".c");
  if (stat(real_name, &c_st) == -1) {
    struct svalue *v;
    
    if (!(v = load_virtual_object(name))) {
      return 0;
    }
    /* Now set the file name of the specified object correctly...*/
    ob = v->u.ob;
    remove_object_hash(ob);
    if (ob->name)
	FREE(ob->name);
    ob->name = string_copy(name);
    enter_object_hash(ob);
    ob->flags |= O_VIRTUAL;
    ob->load_time = current_time;
    return ob; 
  }
  /*
   * Check if it's a legal name.
   */
  if (!legal_path(real_name)) {
    fprintf(stderr, "Illegal pathname: %s\n", real_name);
    error("Illegal path name.\n");
    return 0;
  }
#ifdef SAVE_BINARIES
  if (!load_binary(real_name)) {
#endif
      /* maybe move this section into compile_file? */
      if (comp_flag)
	  fprintf(stderr, " compiling %s ...", real_name);
      f = fopen(real_name, "r");
      if (f == 0) {
	  perror(real_name);
	  error("Could not read the file.\n");
      }
      current_file = string_copy(real_name);	/* This one is freed below */
      start_new_file(f);
      compile_file();
      end_new_file();
      if (comp_flag)
	  fprintf(stderr, " done\n");
      update_compile_av(total_lines);
      total_lines = 0;
      (void)fclose(f);
      FREE(current_file);
      current_file = 0;
#ifdef SAVE_BINARIES
  }
#endif
  /* Sorry, can't handle objects without programs yet. */
  if (inherit_file == 0 && (num_parse_error > 0 || prog == 0)) {
    if (prog)
      free_prog(prog, 1);
    if (num_parse_error == 0 && prog == 0)
      error("No program in object!\n");
    error("Error in loading object\n");
  }
  /*
   * This is an iterative process. If this object wants to inherit an
   * unloaded object, then discard current object, load the object to be
   * inherited and reload the current object again. The global variable
   * "inherit_file" will be set by lang.y to point to a file name.
   */
  if (inherit_file) {
    char *tmp = inherit_file;
    if (prog) {
      free_prog(prog, 1);
      prog = 0;
    }
    if (strcmp(inherit_file, name) == 0) {
      FREE(inherit_file);
      inherit_file = 0;
      error("Illegal to inherit self.\n");
    }
    inherit_file = 0;
#if 0 /* MUDLIB3_NEED, It's very awkard to have to have a debug3 /JnA */
    load_object(tmp, 1);
#else
    load_object(tmp, 0);		/* Remove this feature for now */
#endif
    FREE(tmp);
    ob = load_object(name, dont_reset);
    ob->load_time = current_time;
    num_objects_this_thread--;
    return ob;
  }
  ob = get_empty_object(prog->p.i.num_variables);
  ob->name = string_copy(name);	/* Shared string is no good here */
  ob->prog = prog;
  ob->flags |= O_WILL_RESET; /* must be before reset is first called */
  ob->next_all = obj_list;
  obj_list = ob;
  enter_object_hash(ob);	/* add name to fast object lookup table */

	if (master_ob) {
		push_object(ob);
		mret = apply_master_ob("valid_object", 1);
		if (mret) {
			if (mret->type & T_NUMBER) {
				if (IS_ZERO(mret)) {
					void destruct_object_two();

					destruct_object_two(ob);
	error("master object (valid_object) denied permission to load %s.\n", name);
				}
			} else {
				free_svalue(mret);
			}
		}
	}
  
  if (init_object(ob) && !dont_reset)
    reset_object(ob, 0);
  if (!(ob->flags & O_DESTRUCTED) &&
      function_exists("clean_up", ob)) {
    ob->flags |= O_WILL_CLEAN_UP;
  }
  
  command_giver = save_command_giver;
  if (d_flag > 1 && ob)
    debug_message("--%s loaded\n", ob->name);
  ob->load_time = current_time;
  num_objects_this_thread--;
  return ob;
}

char *make_new_name(str)
     char *str;
{
  static int i;
  char *p = DXALLOC(strlen(str) + 10, 97, "make_new_name");
  
  (void)sprintf(p, "%s#%d", str, i);
  i++;
  return p;
}


/*
 * Save the command_giver, because reset() in the new object might change
 * it.
 */
struct object *clone_object(str1)
     char *str1;
{
  struct object *ob, *new_ob;
  struct object *save_command_giver = command_giver;
  
  if(current_object && current_object->euid == 0){
    error("Object must call seteuid() prior to calling clone_object().\n");
  }
  
#ifdef VALID_CLONE
  if(current_object){
    char *orig;
    
    orig = str1;
    str1 = check_valid_path(str1, current_object, "clone_object", 0);
    if(!str1){
      error("Insufficient permission to read file: %s\n",orig);
    }
  }
#endif /* VALID_CLONE */
  
  num_objects_this_thread = 0;
  ob = find_object(str1);
  if (ob && !object_visible(ob)) ob = 0;
  /*
   * If the object self-destructed...
   */
  if(ob == 0) /* fix from 3.1.1 */
    return(0);
  if(ob == 0 || ob->super || (ob->flags & O_CLONE))
    if(!(ob->flags & O_VIRTUAL) || strrchr(str1,'#'))
      error("Cloning a bad object !\n");
    else {
      /* 
       * well... it's a virtual object.  So now we're going to "clone" it.
       */
      struct svalue *v;
      char *p;
      
      /* Remove leading '/' if any. */
      while(str1[0] == '/')
	str1++;
      p = str1;
      while(*p){
	if(*p == '/' && *(p + 1) == '/'){
	  error("Filenames with consecutive /'s in them aren't allowed.\n");
	  return(0); 
	}
	p++;
      }
      if (ob->ref == 1 && !ob->super && !ob->contains) {
	  /*
	   * ob unused so reuse it instead to save space.
	   * (possibly loaded just for cloning)
	   */
	  new_ob = ob;
      } else {
	  /* can't reuse, so load another */
	  if (!(v = load_virtual_object(str1)))
	      return 0;
	  new_ob = v->u.ob;
      }
      remove_object_hash(new_ob);
      if (new_ob->name)
	  FREE(new_ob->name);
      /* Now set the file name of the specified object correctly...*/
      new_ob->name = make_new_name(str1);
      enter_object_hash(new_ob);
      new_ob->flags |= O_VIRTUAL;
      new_ob->load_time = current_time;
      command_giver = save_command_giver;
      return(new_ob);
      /* we can skip all of the stuff below since we were already cloned
	 once to have gotten to this stage.  */
    }
  /* We do not want the heart beat to be running for unused copied objects */
  if(ob->flags & O_HEART_BEAT) 
    (void)set_heart_beat(ob, 0);
  new_ob = get_empty_object(ob->prog->p.i.num_variables);
  new_ob->name = make_new_name(ob->name);
  new_ob->flags |= (O_CLONE | (ob->flags & (O_WILL_CLEAN_UP | O_WILL_RESET)));
  new_ob->load_time = ob->load_time;
  new_ob->prog = ob->prog;
  reference_prog(ob->prog, "clone_object");
  if(!current_object)
    fatal("clone_object() from no current_object !\n");
  
  init_object(new_ob);
  
  new_ob->next_all = obj_list;
  obj_list = new_ob;
  enter_object_hash(new_ob);	/* Add name to fast object lookup table */
  reset_object(new_ob, 0); 
  command_giver = save_command_giver;
  /* Never know what can happen ! :-( */
  if(new_ob->flags & O_DESTRUCTED)
    return(0);
  return(new_ob);
}

struct object *environment(arg)
     struct svalue *arg;
{
  struct object *ob = current_object;
  
  if (arg && arg->type == T_OBJECT)
    ob = arg->u.ob;
  if (ob == 0 || ob->super == 0 || (ob->flags & O_DESTRUCTED))
    return 0;
  if (ob->flags & O_DESTRUCTED)
    error("environment() off destructed object.\n");
  return ob->super;
}

/*
 * Execute a command for an object. Copy the command into a
 * new buffer, because 'parse_command()' can modify the command.
 * If the object is not current object, static functions will not
 * be executed. This will prevent forcing users to do illegal things.
 *
 * Return cost of the command executed if success (> 0).
 * When failure, return 0.
 */
int command_for_object(str, ob)
     char *str;
     struct object *ob;
{
  char buff[1000];
  extern int eval_cost;
  int save_eval_cost = eval_cost;
  
  if (strlen(str) > sizeof(buff) - 1)
    error("Too long command.\n");
  if (ob == 0)
    ob = current_object;
  else if (ob->flags & O_DESTRUCTED)
    return 0;
  strncpy(buff, str, sizeof buff);
  buff[sizeof buff - 1] = '\0';
  if (parse_command(buff, ob))
    return eval_cost - save_eval_cost + 1000;  /* why the + 1000 ? */
  else
    return 0;
}

/*
 * To find if an object is present, we have to look in two inventory
 * lists. The first list is the inventory of the current object.
 * The second list is all things that have the same ->super as
 * current_object.
 * Also test the environment.
 * If the second argument 'ob' is non zero, only search in the
 * inventory of 'ob'. The argument 'ob' will be mandatory, later.
 */

static struct object *object_present2 PROT((char *, struct object *));
     
struct object *object_present(v, ob)
     struct svalue *v;
     struct object *ob;
{
  struct svalue *ret;
  struct object *ret_ob;
  int specific = 0;
  
  if (ob == 0)
    ob = current_object;
  else
    specific = 1;
  if (ob->flags & O_DESTRUCTED)
    return 0;
  if (v->type == T_OBJECT) {
    if (specific) {
      if (v->u.ob->super == ob)
	return v->u.ob;
      else
	return 0;
    }
    if (v->u.ob->super == ob ||
	(v->u.ob->super == ob->super && ob->super != 0))
      return v->u.ob->super;
    return 0;
  }
  ret_ob = object_present2(v->u.string, ob->contains);
  if (ret_ob)
    return ret_ob;
  if (specific)
    return 0;
  if (ob->super) {
    push_string(v->u.string, STRING_CONSTANT);
    ret = apply("id", ob->super, 1);
    if (ob->super->flags & O_DESTRUCTED)
      return 0;
    if (!IS_ZERO(ret)) {
      /*
	if id() returns a value of type object then query that object.
	this will allow container objects to allow objects inside them
	to be referred to (for attack or whatever).
	*/
#ifndef OLD_PRESENT
      if (ret->type == T_OBJECT)
	return ret->u.ob;
      else
#endif
	return ob->super;
    }
    return object_present2(v->u.string, ob->super->contains);
  }
  return 0;
}

static struct object *object_present2(str, ob)
     char *str;
     struct object *ob;
{
  struct svalue *ret;
  char *p;
  int count = 0, length;
  char *item;
  
  item = string_copy(str);
  length = strlen(item);
  p = item + length - 1;
  if (*p >= '0' && *p <= '9') {
    while(p > item && *p >= '0' && *p <= '9')
      p--;
    if (p > item && *p == ' ') {
      count = atoi(p+1) - 1;
      *p = '\0';
      length = p - item;	/* This is never used again ! */
    }
  }
  for (; ob; ob = ob->next_inv) {
    push_string(item, STRING_CONSTANT);
    ret = apply("id", ob, 1);
    if (ob->flags & O_DESTRUCTED) {
      FREE(item);
      return 0;
    }
    if (IS_ZERO(ret))
      continue;
    if (count-- > 0)
      continue;
    FREE(item);
#ifndef OLD_PRESENT
    if (ret->type == T_OBJECT)
      return ret->u.ob;
    else
#endif
      return ob;
  }
  FREE(item);
  return 0;
}

/*
 * Remove an object. It is first moved into the destruct list, and
 * not really destructed until later. (see destruct2()).
 */
void destruct_object_two(ob)
     struct object *ob;
{
  struct object *super;
  struct object **pp;
  int removed;
  
#ifdef SOCKET_EFUNS
  /*
   * check if object has an efun socket referencing it for
   * a callback. if so, close the efun socket.
   */
  if (ob->flags & O_EFUN_SOCKET) {
    close_referencing_sockets(ob);
  }
#endif

  if (ob->flags & O_DESTRUCTED)
    return;
  if (ob->flags & O_SWAPPED)
    load_ob_from_swap(ob);
  remove_object_from_stack(ob);
  /*
   * If this is the first object being shadowed by another object, then
   * destruct the whole list of shadows.
   */
#ifndef NO_SHADOWS 
  if (ob->shadowed && !ob->shadowing) {
    struct svalue svp;
    struct object *ob2;
    
    svp.type = T_OBJECT;
    for (ob2 = ob->shadowed; ob2; ) {
      svp.u.ob = ob2;
      ob2 = ob2->shadowed;
      svp.u.ob->shadowed = 0;
      svp.u.ob->shadowing = 0;
      destruct_object(&svp);
    }
  }
  /*
   * The chain of shadows is a double linked list. Take care to update
   * it correctly.
   */
  if (ob->shadowing)
    ob->shadowing->shadowed = ob->shadowed;
  if (ob->shadowed)
    ob->shadowed->shadowing = ob->shadowing;
  ob->shadowing = 0;
  ob->shadowed = 0;
#endif
  
  if (d_flag > 1)
    debug_message("Destruct object %s (ref %d)\n", ob->name, ob->ref);
  super = ob->super;
  if (super == 0) {
    /*
     * There is nowhere to move the objects.
     */
    struct svalue svp;

    svp.type = T_OBJECT;
    while(ob->contains) {
      svp.u.ob = ob->contains;
      push_object(ob->contains);
      /* An error here will not leave destruct() in an inconsistent
       * stage.
       */
      apply_master_ob("destruct_environment_of",1);
      if (svp.u.ob == ob->contains)
	destruct_object(&svp);
    }
  } else {
    while(ob->contains)
      move_or_destruct(ob->contains, super);
  }
  add_objects (&ob->stats, -1);
  if ( ob->interactive ) {
    struct object *save=command_giver;
    
    command_giver=ob;
#ifdef ED
    if (ob->interactive->ed_buffer) {
      extern void save_ed_buffer();
      
      save_ed_buffer();
    }
#endif
    command_giver=save;
  }
  /*
   * Remove us out of this current room (if any).
   * Remove all sentences defined by this object from all objects here.
   */
  if (ob->super) {
    if (ob->super->flags & O_ENABLE_COMMANDS)
      remove_sent(ob, ob->super);
    add_light(ob->super, - ob->total_light);
    for (pp = &ob->super->contains; *pp;) {
      if ((*pp)->flags & O_ENABLE_COMMANDS)
	remove_sent(ob, *pp);
      if (*pp != ob)
	pp = &(*pp)->next_inv;
      else
	*pp = (*pp)->next_inv;
    }
  }
  /*
   * Now remove us out of the list of all objects.
   * This must be done last, because an error in the above code would
   * halt execution.
   */
  removed = 0;
  for (pp = &obj_list; *pp; pp = &(*pp)->next_all) {
    if (*pp != ob)
      continue;
    *pp = (*pp)->next_all;
    removed = 1;
    remove_object_hash(ob);
    break;
  }
  if (!removed)
    fatal("Failed to delete object.\n");
  if (ob->living_name)
    remove_living_name(ob);
  ob->super = 0;
  ob->next_inv = 0;
  ob->contains = 0;
  ob->flags &= ~O_ENABLE_COMMANDS;
  ob->next_all = obj_list_destruct;
  obj_list_destruct = ob;
  set_heart_beat(ob, 0);
  ob->flags |= O_DESTRUCTED;
  /* moved this here from destruct2() -- see comments in destruct2() */
  if (ob->interactive) {
    remove_interactive(ob);
  }
}

void destruct_object(v)
     struct svalue *v;
{
	struct object *ob = (struct object *)NULL;
  
	if (v->type == T_OBJECT) {
		ob = v->u.ob;
		destruct_object_two(ob);
	} else {
		error("destruct_object: called without an object argument\n");
	}
}

/*
 * This one is called when no program is executing from the main loop.
 */
void destruct2(ob)
     struct object *ob;
{
  if (d_flag > 1) {
    debug_message("Destruct-2 object %s (ref %d)\n", ob->name, ob->ref);
  }
#if 0
  /* moved this into destruct_object() to deal with the 0 in users() efun
     output problem
     */
  if (ob->interactive)
    remove_interactive(ob);
#endif
  /*
   * We must deallocate variables here, not in 'free_object()'.
   * That is because one of the local variables may point to this object,
   * and deallocation of this pointer will also decrease the reference
   * count of this object. Otherwise, an object with a variable pointing
   * to itself, would never be freed.
   * Just in case the program in this object would continue to
   * execute, change string and object variables into the number 0.
   */
  if (ob->prog->p.i.num_variables > 0) {
    /*
     * Deallocate variables in this object.
     * The space of the variables are not deallocated until
     * the object structure is freed in free_object().
     */
    int i;
    for (i=0; i<(int)ob->prog->p.i.num_variables; i++) {
      free_svalue(&ob->variables[i]);
      ob->variables[i].type = T_NUMBER;
      ob->variables[i].u.number = 0;
      ob->variables[i].subtype = T_NULLVALUE;
    }
  }
  free_object(ob, "destruct_object");
}

/*
 * say() efun - send a message to:
 *  all objects in the inventory of the source,
 *  all objects in the same environment as the source,
 *  and the object surrounding the source.
 *
 * when there is no command_giver, current_object is used as the source,
 *  otherwise, command_giver is used.
 *
 * message never goes to objects in the avoid vector, or the source itself.
 *
 * rewritten, bobf@metronet.com (Blackthorn) 9/6/93
 */
 
void send_say(ob, text, avoid)
        struct object *ob;
        char *text;
        struct vector *avoid;
{
        int valid, j;
 
        for (valid = 1, j = 0; j < avoid->size; j++)
        {
                if (avoid->item[j].type != T_OBJECT)
                       continue;
                if (avoid->item[j].u.ob == ob)
                {
                       valid = 0;
                       break;
                }
        }
        
        if (!valid)
                return;
 
        tell_object(ob, text);
}
 
void say(v, avoid)
     struct svalue *v;
     struct vector *avoid;
{
  struct object *ob, *origin, *save_command_giver = command_giver;
  char *buff;
 
  check_legal_string(v->u.string);
  buff = v->u.string;
 
  if (current_object->flags & O_ENABLE_COMMANDS)
    command_giver = current_object;
  if (command_giver)
    origin = command_giver;
    else
    origin = current_object;
 
 /* To our surrounding object... */
  if ((ob = origin->super))
  {
    if (ob->flags & O_ENABLE_COMMANDS || ob->interactive) 
      send_say(ob, buff, avoid); 
 
 /* And its inventory... */
    for (ob = origin->super->contains; ob; ob = ob->next_inv)
    {
      if (ob != origin && (ob->flags & O_ENABLE_COMMANDS || ob->interactive))
      {
        send_say(ob, buff, avoid);
        if (ob->flags & O_DESTRUCTED)
                break;
      }
    }
  }
 
 /* Our inventory... */
  for (ob = origin->contains; ob; ob = ob->next_inv)
  {
    if (ob->flags & O_ENABLE_COMMANDS || ob->interactive) 
    {
      send_say(ob, buff, avoid);
      if (ob->flags & O_DESTRUCTED)
        break;
    }
  }
 
  command_giver = save_command_giver;
}

/*
 * Sends a string to all objects inside of a specific object.
 * Revised, bobf@metronet.com 9/6/93
 */
 
void tell_room(room, v, avoid)
     struct object *room;
     struct svalue *v;
     struct vector *avoid; 
{
  struct object *ob;
  char *buff;
  int valid, j;
  static char txt_buf[LARGEST_PRINTABLE_STRING];
  
  switch (v->type)
  {
          case T_STRING:
   	    check_legal_string(v->u.string);
            buff = v->u.string;
            break;
          case T_OBJECT:
            buff = v->u.ob->name;
            break;
          case T_NUMBER:
            buff = txt_buf;
            sprintf(buff, "%d", v->u.number);
            break;
          case T_REAL:
            buff = txt_buf;
            sprintf(buff, "%g", v->u.real);
            break;
          default:
            error("Bad arg 2 to tell_room()\n");
  }
 
  for (ob = room->contains; ob; ob = ob->next_inv)
  {
    if (!ob->interactive && !(ob->flags & O_ENABLE_COMMANDS))
        continue;
 
    for (valid = 1, j = 0; j < avoid->size; j++)
    {
        if (avoid->item[j].type != T_OBJECT)
               continue;
        if (avoid->item[j].u.ob == ob)
        {
               valid = 0;
               break;
        }
    }
 
    if (!valid)
        continue;
 
    if (!ob->interactive) {
        tell_npc(ob, buff);
        if (ob->flags & O_DESTRUCTED)
              break;
    } else {
        tell_object(ob, buff);
        if (ob->flags & O_DESTRUCTED)
              break;
    }
  }
}

void shout_string(str)
     char *str;
{
  struct object *ob;
#ifdef LOG_SHOUT
  FILE *f = 0;
  char *tmpstr;
#endif
  char *p;
  
  str = string_copy(str);	/* So that we can modify the string */
  for (p=str; *p; p++) {
    if ((*p < ' ' || *p > '~') && *p != '\n')
      *p = ' ';
  }
  
  p = 0;
#ifdef LOG_SHOUT
  if (command_giver) {
    struct svalue *v;
    v = apply("query_cap_name", command_giver, 0);
    if (v && v->type == T_STRING)
      p = v->u.string;
    else {
      v = apply("query_name", command_giver, 0);
      if (v && v->type == T_STRING)
	p = v->u.string;
    }
  } else if (current_object && current_object->uid)
    p = current_object->uid->name;
  if (p)
    {
      tmpstr = (char *)DMALLOC(strlen(LOG_DIR) + 8, 101, "shout_string: 1");
      sprintf(tmpstr,"%s/shouts",LOG_DIR);
      if (tmpstr[0] == '/')
	strcpy (tmpstr, tmpstr+1);
      f = fopen(tmpstr, "a");
      FREE(tmpstr);
    }
  if (f) {
    fprintf(f, "%s: %s\n", p, str);
    fclose(f);
  }
#endif
  for (ob = obj_list; ob; ob = ob->next_all) {
    if (!(ob->flags & O_ENABLE_COMMANDS) || (ob == command_giver)
	|| !ob->super)
      continue;
    tell_object(ob,str);
  }
  FREE(str);
}

struct object *first_inventory(arg)
     struct svalue *arg;
{
  struct object *ob;
  
  if (arg->type == T_STRING)
    {
      ob = find_object(arg->u.string);
      if (ob && !object_visible(ob)) ob = 0;
    }
  else
    ob = arg->u.ob;
  if (ob == 0)
    error("No object to first_inventory()");
  ob = ob->contains;
  while (ob)
    {
      if (ob->flags & O_HIDDEN)
        {
	  if (object_visible(ob))
            {
	      return ob;
            }
        } else return ob;
      ob = ob->next_inv;
    }
  return 0;
}

/*
 * This will enable an object to use commands normally only
 * accessible by interactive users.
 * Also check if the user is a wizard. Wizards must not affect the
 * value of the wizlist ranking.
 */

void enable_commands(num)
     int num;
{
  if (current_object->flags & O_DESTRUCTED)
    return;
  if (d_flag > 1) {
    debug_message("Enable commands %s (ref %d)\n",
		  current_object->name, current_object->ref);
  }
  if (num) {
    current_object->flags |= O_ENABLE_COMMANDS;
    command_giver = current_object;
  } else {
    current_object->flags &= ~O_ENABLE_COMMANDS;
    command_giver = 0;
  }
}

/*
 * Set up a function in this object to be called with the next
 * user input string.
 */
int input_to(fun, flag, num_arg, args)
     char *fun;
     int flag;
     int num_arg;
     struct svalue *args;
{
  struct sentence *s;
  struct svalue *x;
  
  if(!command_giver || command_giver->flags & O_DESTRUCTED)
    return 0;
  /*
   * If we have args, we have to copy them, so the svalues on the
   * stack can be freed, which will be done automatically.
   */
  if(num_arg){
    if((x = (struct svalue *)
	DMALLOC(num_arg * sizeof(struct svalue), 102, "input_to: 1")) == NULL)
      fatal("Not enough memory to copy args from input_to.\n");	 
    copy_some_svalues(x, args, num_arg);
  }
  else
    x = NULL;
  s = alloc_sentence();
  if(set_call(command_giver, s, flag, 0)){
    command_giver->interactive->carryover = x;
    command_giver->interactive->num_carry = num_arg;
    s->function = make_shared_string(fun);
    s->ob = current_object;
    add_ref(current_object, "input_to");
    return 1;
  }
  if(x)
    FREE(x);
  free_sentence(s);
  return 0;
}

/*
 * Set up a function in this object to be called with the next
 * user input character.
 */
int get_char (fun, flag)
     char *fun;
     int flag;
{
  struct sentence *s;
  
  if (!command_giver || command_giver->flags & O_DESTRUCTED)
    return 0;
  s = alloc_sentence();
  if (set_call(command_giver, s, flag, 1)) {
    s->function = make_shared_string(fun);
    s->ob = current_object;
    add_ref(current_object, "get_char");
    return 1;
  }
  free_sentence(s);
  return 0;
}

INLINE void
  check_legal_string(s)
char *s;
{
  if (strlen(s) >= LARGEST_PRINTABLE_STRING) {
    error("Printable strings limited to length of %d.\n",
	  LARGEST_PRINTABLE_STRING);
  }
}

void print_svalue(arg)
     struct svalue *arg;
{
  if (arg == 0)
  {
    add_message("<NULL>");
  } 
  else switch (arg->type)
  {
        case T_STRING:
              check_legal_string(arg->u.string);
              tell_object(command_giver, arg->u.string);
              break;
        case T_OBJECT:
              add_message("OBJ(%s)", arg->u.ob->name);
              break;
        case T_NUMBER:
              add_message("%d", arg->u.number);
              break;
        case T_REAL:
              add_message("%g", arg->u.real);
              break;
        case T_POINTER:
              add_message("<ARRAY>");
              break;
        case T_MAPPING:
              add_message("<MAPPING>");
              break;
        case T_FUNCTION:
              add_message("<FUNCTION>");
              break;
        default:
              add_message("<UNKNOWN>");
              break;
  }
  return;
}

void do_write(arg)
     struct svalue *arg;
{
  struct object *save_command_giver = command_giver;
#ifndef NO_SHADOWS
  if(command_giver == 0 && current_object->shadowing)
    command_giver = current_object;
  if(command_giver){
    /* Send the message to the first object in the shadow list */
    while(command_giver->shadowing)
      command_giver = command_giver->shadowing;
  }
#else
  if(!command_giver)
    command_giver = current_object;
#endif /* NO_SHADOWS */
  print_svalue(arg);
  command_giver = save_command_giver;
}

/* Find an object. If not loaded, load it !
 * The object may selfdestruct, which is the only case when 0 will be
 * returned.
 */

struct object *find_object(str)
     char *str;
{
  struct object *ob;
  char *p;
  
  /* don't allow consecutive "/"'s - Wayfarer */
  p = str;
  while (*p)
    {
      if (*p == '/' && *(p + 1) == '/')
	return 0; 
      p++;
    }
  
  /* Remove leading '/' if any. */
  while(str[0] == '/')
    str++;
  ob = find_object2(str);
  if (ob)
    return ob;
  ob = load_object(str, 0);
  if (!ob || (ob->flags & O_DESTRUCTED))		/* *sigh* */
    return 0;
  if (ob && ob->flags & O_SWAPPED)
    load_ob_from_swap(ob);
  return ob;
}

#define MAX_OBJECT_NAME_SIZE 2048

/* Look for a loaded object. Return 0 if non found. */
struct object *find_object2(str)
     char *str;
{
  register struct object *ob;
  register int length;
  char p[MAX_OBJECT_NAME_SIZE];
  
  /* Remove leading '/' if any. */
  while(str[0] == '/')
    str++;
  /* Truncate possible .c in the object name. */
  length = strlen(str);
  if (str[length-2] == '.' && str[length-1] == 'c') {
    /* A new writeable copy of the name is needed. */
    strncpy(p, str, MAX_OBJECT_NAME_SIZE);
    str = p;
    str[length-2] = '\0';
  }
  if ((ob = lookup_object_hash(str))) {
    if (ob->flags & O_SWAPPED)
      load_ob_from_swap(ob);
    return ob;
  }
  return 0;
}



/*
 * Transfer an object.
 * The object has to be taken from one inventory list and added to another.
 * The main work is to update all command definitions, depending on what is
 * living or not. Note that all objects in the same inventory are affected.
 */
void move_object(item, dest)
     struct object *item, *dest;
{
  struct object **pp, *ob, *next_ob;
  struct object *save_cmd = command_giver;
  
  if (item != current_object)
    error("Illegal to move other object than this_object()\n");
  /* Recursive moves are not allowed. */
  for (ob = dest; ob; ob = ob->super)
    if (ob == item)
      error("Can't move object inside itself.\n");
#ifndef NO_SHADOWS
  if (item->shadowing)
    error("Can't move an object that is shadowing.\n");
#endif
  
#ifdef LAZY_RESETS
  try_reset(dest);
#endif
  add_light(dest, item->total_light);
  if (item->super) {
    int okey = 0;
    
    if (item->flags & O_ENABLE_COMMANDS) {
      remove_sent(item->super, item);
    }
    if (item->super->flags & O_ENABLE_COMMANDS)
      remove_sent(item, item->super);
    add_light(item->super, - item->total_light);
    for (pp = &item->super->contains; *pp;) {
      if (*pp != item) {
	if ((*pp)->flags & O_ENABLE_COMMANDS)
	  remove_sent(item, *pp);
	if (item->flags & O_ENABLE_COMMANDS)
	  remove_sent(*pp, item);
	pp = &(*pp)->next_inv;
	continue;
      }
      *pp = item->next_inv;
      okey = 1;
    }
    if (!okey)
      fatal("Failed to find object %s in super list of %s.\n",
	    item->name, item->super->name);
  }
  item->next_inv = dest->contains;
  dest->contains = item;
  item->super = dest;
  /*
   * Setup the new commands. The order is very important, as commands
   * in the room should override commands defined by the room.
   * Beware that init() in the room may have moved 'item' !
   *
   * The call of init() should really be done by the object itself
   * (except in the -o mode). It might be too slow, though :-(
   */
  if (item->flags & O_ENABLE_COMMANDS) {
    command_giver = item;
    (void)apply("init", dest, 0);
    if ((dest->flags & O_DESTRUCTED) || item->super != dest) {
      command_giver = save_cmd; /* marion */
      return;
    }
  }
  /*
   * Run init of the item once for every present user, and
   * for the environment (which can be a user).
   */
  for (ob = dest->contains; ob; ob=next_ob) {
    next_ob = ob->next_inv;
    if (ob == item)
      continue;
    if (ob->flags & O_DESTRUCTED)
      error("An object was destructed at call of init()\n");
    if (ob->flags & O_ENABLE_COMMANDS) {
      command_giver = ob;
      (void)apply("init", item, 0);
      if (dest != item->super) {
	command_giver = save_cmd; /* marion */
	return;
      }
    }
    if (item->flags & O_DESTRUCTED) /* marion */
      error("The object to be moved was destructed at call of init()\n");
    if (item->flags & O_ENABLE_COMMANDS) {
      command_giver = item;
      (void)apply("init", ob, 0);
      if (dest != item->super) {
	command_giver = save_cmd; /* marion */
	return;
      }
    }
  }
  if (dest->flags & O_DESTRUCTED) /* marion */
    error("The destination to move to was destructed at call of init()\n");
  if (dest->flags & O_ENABLE_COMMANDS) {
    command_giver = dest;
    (void)apply("init", item, 0);
  }
  command_giver = save_cmd;
}


/*
 * Every object as a count of number of light sources it contains.
 * Update this.
 */

void add_light(p, n)
     struct object *p;
     int n;
{
  if (n == 0)
    return;
  p->total_light += n;
  if (p->super)
    add_light(p->super, n);
}

struct sentence *sent_free = 0;
int tot_alloc_sentence;

struct sentence *alloc_sentence() {
  struct sentence *p;
  
  if (sent_free == 0) {
    p = (struct sentence *)DXALLOC(sizeof *p, 103, "alloc_sentence");
    tot_alloc_sentence++;
  } else {
    p = sent_free;
    sent_free = sent_free->next;
  }
  p->verb = 0;
  p->function = 0;
  p->next = 0;
  return p;
}

#ifdef free
void free_all_sent() {
  struct sentence *p;
  for (;sent_free; sent_free = p) {
    p = sent_free->next;
    FREE(sent_free);
  }
}
#endif

void free_sentence(p)
     struct sentence *p;
{
  if (p->function)
    free_string(p->function);
  p->function = 0;
  if (p->verb)
    free_string(p->verb);
  p->verb = 0;
  p->next = sent_free;
  sent_free = p;
}

/*
 * Find the sentence for a command from the user.
 * Return success status.
 */

#define MAX_VERB_BUFF 100

int user_parser(buff)
     char *buff;
{
  static char verb_buff[MAX_VERB_BUFF];
  struct object *super;
  struct sentence *s;
  char *p;
  int length;
  struct object *save_current_object = current_object,
     *save_command_giver = command_giver;
  char *user_verb = 0;
  
  if (d_flag > 1)
    debug_message("cmd [%s]: %s\n", command_giver->name, buff);
  /* strip trailing spaces. */
  for (p = buff + strlen(buff) - 1; p >= buff; p--) {
    if (*p != ' ')
      break;
    *p = '\0';
  }
  if (buff[0] == '\0')
    return 0;
  length = p - buff + 1;
  p = strchr(buff, ' ');
  if (p == 0) {
    user_verb = findstring(buff);
  } else {
    *p = '\0';
    user_verb = findstring(buff);
    *p = ' ';
    length = p - buff;
  }
  if (!user_verb) {
      /* either an xverb or a verb without a specific add_action */
      user_verb = buff;
  }
  /* copy user_verb into a static character buffer to be pointed to
     by last_verb.
  */
  strncpy(verb_buff, user_verb, MAX_VERB_BUFF - 1);
  if (p) {
     int pos;

     pos = p - buff;
     if (pos < MAX_VERB_BUFF) {
         verb_buff[pos] = '\0';
     }
  }
  /* clear_notify(); */ /* moved to process_user_command() */
  s = save_command_giver->sent;
  for ( ; s; s = s->next) {
    struct svalue *ret;
    struct object *command_object;

    if (s->verb == 0)
      error("No action linked to verb.\n");
    if (s->flags & (V_NOSPACE | V_SHORT)) {
      if (strncmp(buff, s->verb, strlen(s->verb)) != 0)
	      continue;
    } else {
      /* note: if was add_action(blah, "") then accept it */
      if (s->verb[0] && (user_verb != s->verb)) continue;
    }
    /*
     * Now we have found a special sentence !
     */
    if (d_flag > 1)
      debug_message("Local command %s on %s\n",
		    s->function, s->ob->name);
    last_verb = verb_buff;
    if (s->verb && s->verb[0]) {
        strcpy(verb_buff, s->verb);
    }
    /*
     * If the function is static and not defined by current object,
     * then it will fail. If this is called directly from user input,
     * then we set current_object so that static functions are allowed.
     * current_object is reset just after the call to apply().
     */
    if (current_object == 0)
      current_object = s->ob;
    /*
     * Remember the object, to update moves.
     */
    command_object = s->ob;
    super = command_object->super;
    if (s->flags & V_NOSPACE) {
      push_constant_string(&buff[strlen(s->verb)]);
      ret = apply(s->function,s->ob, 1);
    } else if (buff[length] == ' ') {
      push_constant_string(&buff[length+1]);
      ret = apply(s->function, s->ob, 1);
    } else {
      ret = apply(s->function, s->ob, 0);
    }
	/* prevent an action from moving its associated object into another
	   another object prior to returning 0.  closes a security hole
	   which was making the static keyword of no use on actions.
	*/
	if (IS_ZERO(ret) && (super != s->ob->super)) {
		fprintf(stderr,
		"** Check '%s' as a possible attempted breach of security **\n",
			s->ob->name);
		break;
	}
    if (current_object->flags & O_DESTRUCTED) {
      /* If disable_commands() were called, then there is no
       * command_giver any longer.
       */
      if (command_giver == 0) {
	     return 1;
      }
      s = command_giver->sent;	/* Restart :-( */
    }
    current_object = save_current_object;
    last_verb = 0;
    /* If we get fail from the call, it was wrong second argument. */
    if (ret && ret->type == T_NUMBER && ret->u.number == 0)
      continue;
    if (command_giver) {
      if (s && command_giver->interactive &&
	  !(command_giver->flags & O_IS_WIZARD))
        add_moves (&command_object->stats, 1);
      if (ret == 0)
        add_message("Error: action %s not found.\n", s->function);
    }
    return 1;
  }
  notify_no_command();
  return 0;
}

/*
 * Associate a command with function in this object.
 * The optional second argument is the command name. If the command name
 * is not given here, it should be given with add_verb().
 *
 * The optinal third argument is a flag that will state that the verb should
 * only match against leading characters.
 *
 * The object must be near the command giver, so that we ensure that the
 * sentence is removed when the command giver leaves.
 *
 * If the call is from a shadow, make it look like it is really from
 * the shadowed object.
 */
void add_action(str, cmd, flag)
     char *str, *cmd;
     int flag;
{
  struct sentence *p;
  struct object *ob;
  
  if (str[0] == ':')
    error("Illegal function name: %s\n", str);
  if (current_object->flags & O_DESTRUCTED)
    return;
  ob = current_object;
#ifndef NO_SHADOWS 
	while (ob->shadowing) {
		ob = ob->shadowing;
	}
	/* don't allow add_actions of a static function from a shadowing object */
	if ((ob != current_object) && is_static(str, ob)) {
		return;
	}
#endif
  if (command_giver == 0 || (command_giver->flags & O_DESTRUCTED))
    return;
  if (ob != command_giver && ob->super != command_giver &&
      ob->super != command_giver->super && ob != command_giver->super)
    return;  /* No need for an error, they know what they did wrong. */
  if (d_flag > 1)
    debug_message("--Add action %s\n", str);
  p = alloc_sentence();
  p->function = make_shared_string(str);
  p->ob = ob;
  p->next = command_giver->sent;
  p->flags = flag;
  if (cmd)
    p->verb = make_shared_string(cmd);
  else
    p->verb = 0;
  command_giver->sent = p;
}

void add_verb(str, flag)
     char *str;
     int flag;
{
  if (command_giver == 0 || (command_giver->flags & O_DESTRUCTED))
    return;
  if (command_giver->sent == 0)
    error("No add_action().\n");
  if (command_giver->sent->verb != 0)
    error("Tried to set verb again.\n");
  command_giver->sent->verb = make_shared_string(str);
  if (flag)
    command_giver->sent->flags |= V_NOSPACE;
  if (d_flag > 1)
    debug_message("--Adding verb %s to action %s\n", str,
		  command_giver->sent->function);
}


/*
 * Remove sentence with specified verb and action.  Return 1
 * if success.  If command_giver, remove his action, otherwise
 * remove current_object's action.
 */
int remove_action(act, verb)
     char *act, *verb;
{
  struct object *ob;
  struct sentence **s;
  if (command_giver)
    ob = command_giver;
  else
    ob = current_object;
  if (!ob) return 0;
  for (s = &ob->sent; *s; ) {
    struct sentence *tmp;
    if (((*s)->ob == ob) && !strcmp((*s)->function, act) &&
	!strcmp((*s)->verb, verb)) {
      tmp = *s;
      *s = tmp->next;
      free_sentence(tmp);
      return 1;
    }
    s = &((*s)->next);   /* Code look familiar? ;) */
  }
  return 0;
}

/*
 * Remove all commands (sentences) defined by object 'ob' in object
 * 'user'
 */
void remove_sent(ob, user)
     struct object *ob, *user;
{
  struct sentence **s;
  
  for (s= &user->sent; *s;) {
    struct sentence *tmp;
    if ((*s)->ob == ob) {
      if (d_flag > 1)
	debug_message("--Unlinking sentence %s\n", (*s)->function);
      tmp = *s;
      *s = tmp->next;
      free_sentence(tmp);
    } else
      s = &((*s)->next);
  }
}

void debug_fatal(va_alist)
  va_dcl
{
  va_list args;
  static char msg_buf[2049];
  char *fmt;
  static int in_fatal = 0;
  /* Prevent double fatal. */
  if (in_fatal)
    abort();
  in_fatal = 1;
  va_start(args);
  fmt = va_arg(args, char *);
  vsprintf(msg_buf, fmt, args);
  va_end(args);
  fprintf(stderr, "%s", msg_buf);
  fflush(stderr);
  if (current_object)
    (void)fprintf(stderr, "Current object was %s\n",
		  current_object->name);
  debug_message("%s", msg_buf);
  if (current_object)
    debug_message("Current object was %s\n", current_object->name);
  debug_message("Dump of variables:\n");
  (void)dump_trace(1);
}

void fatal(va_alist)
  va_dcl
{
  va_list args;
  char *fmt;
  static char msg_buf[2049];

  va_start(args);
  fmt = va_arg(args, char *);
  vsprintf(msg_buf, fmt, args);
  debug_fatal("%s", msg_buf);
  va_end(args);
#if !defined(DEBUG_NON_FATAL) || !defined(DEBUG)
  abort();
#endif
}

int num_error = 0;

/*
 * Error() has been "fixed" so that users can catch and throw them.
 * To catch them nicely, we really have to provide decent error information.
 * Hence, all errors that are to be caught
 * (error_recovery_context_exists == 2) construct a string containing
 * the error message, which is returned as the
 * thrown value.  Users can throw their own error values however they choose.
 */

/*
 * This is here because throw constructs its own return value; we dont
 * want to replace it with the system's error string.
 */

void throw_error() {
  extern int error_recovery_context_exists;
  extern jmp_buf error_recovery_context;
  if (error_recovery_context_exists > 1) {
    LONGJMP(error_recovery_context, 1);
    fatal("Throw_error failed!");
  }
  error("Throw with no catch.\n");
}

static char emsg_buf[2000];

void error(va_alist)
  va_dcl
{
  extern int num_objects_this_thread;
  extern int error_recovery_context_exists;
  extern jmp_buf error_recovery_context;
  extern struct object *current_heart_beat;
  extern struct svalue catch_value;
  extern int too_deep_error, max_eval_error;
  char *object_name;
  va_list args;
  char *fmt;

  va_start(args);
  fmt = va_arg(args, char *);
  vsprintf(emsg_buf + 1, fmt, args);
  va_end(args);
  emsg_buf[0] = '*';	/* all system errors get a * at the start */
  num_objects_this_thread = 0; /* reset the count */
  if (error_recovery_context_exists > 1) { /* user catches this error */
    struct svalue v;

#ifdef LOG_CATCHES
/* This is added so that catches generate messages in the log file. */
    debug_message("caught: %s", emsg_buf+1);
    if (current_object)
      debug_message("program: %s, object: %s line %d\n",
		    current_prog ? current_prog->name : "",
		    current_object->name,
		    get_line_number_if_any());
    (void)dump_trace(0);
#endif

    v.type = T_STRING;
    v.u.string = emsg_buf;
    v.subtype = STRING_MALLOC;	/* Always reallocate */
    assign_svalue(&catch_value, &v);
    LONGJMP(error_recovery_context, 1);
    fatal("Catch() longjump failed");
  }
  too_deep_error = max_eval_error = 0;
  num_error++;
  if (num_error > 1)
    fatal("Too many simultaneous errors.\n");
  debug_message("%s", emsg_buf+1);
  if (current_object) {
    add_errors (&current_object->stats, 1);
    debug_message("program: %s, object: %s line %d\n",
		  current_prog ? current_prog->name : "",
		  current_object->name,
		  get_line_number_if_any());
  }
#if defined(DEBUG) && defined(TRACE_CODE)
  object_name = dump_trace(1);
#else
  object_name = dump_trace(0);
#endif
  fflush(stdout);
  if (object_name) {
    struct object *ob;
    ob = find_object2(object_name);
    if (!ob) {
      if (command_giver)
	add_message("error when executing program in destroyed object %s\n",
		    object_name);
      debug_message("error when executing program in destroyed object %s\n",
		    object_name);
    }
  }
  if (command_giver && command_giver->interactive) {
    num_error--;
    /* 
     * The stack must be brought in a usable state. After the
     * call to reset_machine(), all arguments to error() are invalid,
     * and may not be used any more. The reason is that some strings
     * may have been on the stack machine stack, and has been deallocated.
     */
    reset_machine (0);
    num_error++;
    if ((command_giver->flags & O_IS_WIZARD) || !strlen(DEFAULT_ERROR_MESSAGE))
      {
	add_message("%s", emsg_buf+1);
	if (current_object)
	  add_message("program: %s, object: %s line %d\n",
		      current_prog ? current_prog->name : "",
		      current_object->name,
		      get_line_number_if_any());
      } else {
	add_message("%s\n", DEFAULT_ERROR_MESSAGE);
      }
  }
  if (current_heart_beat) {
    set_heart_beat(current_heart_beat, 0);
    debug_message("Heart beat in %s turned off.\n", current_heart_beat->name);
    if (current_heart_beat->interactive) {
      struct object *save_cmd = command_giver;
      command_giver = current_heart_beat;
      add_message("MudOS driver tells you: You have no heart beat!\n");
      command_giver = save_cmd;
    }
    current_heart_beat = 0;
  }
  num_error--;
  if (error_recovery_context_exists)
    LONGJMP(error_recovery_context, 1);
  abort();
}

/*
 * This one is called from HUP.
 */
int MudOS_is_being_shut_down;

#if SIGNAL_FUNC_TAKES_INT
void startshutdownMudOS(arg)
     int arg;
#else
void startshutdownMudOS()
#endif
{
  MudOS_is_being_shut_down = 1;
}

/*
 * This one is called from the command "shutdown".
 * We don't call it directly from HUP, because it is dangerous when being
 * in an interrupt.
 */
void shutdownMudOS(exit_code)
     int exit_code;
{
  shout_string("MudOS driver shouts: shutting down immediately.\n");
  save_stat_files();
  ipc_remove();
#ifdef LATTICE
	signal(SIGUSR1, SIG_IGN);
	signal(SIGTERM, SIG_IGN);
	signal(SIGINT, SIG_IGN);
	signal(SIGHUP, SIG_IGN);
	signal(SIGALRM,SIG_IGN);
#endif
  unlink_swap_file();
#ifdef DEALLOCATE_MEMORY_AT_SHUTDOWN
  remove_all_objects();
  free_all_sent();
  free_mudlib_stats();
  dump_malloc_data();
  find_alloced_data();
#endif
#ifdef PROFILING
  monitor(0,0,0,0,0);  /* cause gmon.out to be written */
#endif
  exit(exit_code);
}

/*
 * Move or destruct one object.
 */
void move_or_destruct(what, to)
     struct object *what, *to;
{
  struct svalue v;
  
  struct svalue *svp;
  /* This is very dubious, why not just destruct them /JnA 
   */
  push_object(to);
  push_number(1);
  svp = apply("move", what, 2);
  if (svp && svp->type == T_NUMBER && svp->u.number == 0)
    return;
  if (what->flags & O_DESTRUCTED)
    return;
  
  /*
   * Failed to move the object. Then, it is destroyed.
   */
  v.type = T_OBJECT;
  v.u.ob = what;
  destruct_object(&v);
}

/*
 * Call this one when there is only little memory left. It will start
 * Armageddon.
 */
void slow_shut_down(minutes)
     int minutes;
{
  /*
   * Swap out objects, and free some memory.
   */
  struct svalue *amo;
  push_number(minutes);
  amo=apply_master_ob("slow_shutdown",1);
  if (IS_ZERO(amo))
  /*if (IS_ZERO(apply_master_ob("slow_shutdown",1)))*/
    {
      struct object *save_current = current_object,
      *save_command = command_giver;
      command_giver = 0;
      current_object = 0;
      shout_string("MudOS driver shouts: Out of memory.\n");
      command_giver = save_command;
      current_object = save_current;
#if SIGNAL_FUNC_TAKES_INT
      startshutdownMudOS(1);
#else
      startshutdownMudOS();
#endif
      return;
    }
}

void do_message(class, msg, scope, exclude, recurse)
     char *class, *msg;
     struct vector *scope, *exclude;
     int recurse;
{
  int i, j, valid;
  struct object *ob;
 
  for (i = 0; i < scope->size; i++)
  {
        switch (scope->item[i].type)
        {
                case T_STRING:
                       ob = find_object(scope->item[i].u.string);
                       if (ob && !object_visible(ob))
                        ob = 0;
                       break;
                case T_OBJECT:
                       ob = scope->item[i].u.ob;
                       break;
                default:
                       ob = 0;
                       break;
        }
        if (!ob)
                continue;
        if (ob->flags & O_ENABLE_COMMANDS || ob->interactive)
        {
          for (valid = 1, j = 0; j < exclude->size; j++)
          {
            if (exclude->item[j].type != T_OBJECT) 
                continue;
            if (exclude->item[j].u.ob == ob)
            {
                valid = 0;
                break;
            }
          }
          if (valid)
          {
              push_string(class, STRING_CONSTANT);
              push_string(msg, STRING_CONSTANT);
              apply("receive_message", ob, 2);
          }
        }
        else if (recurse)
        {
              struct vector *tmp;
              tmp = all_inventory(ob, 1);
              do_message(class, msg, tmp, exclude, 0);
              free_vector(tmp);
        }
  }
}

#ifdef LAZY_RESETS
INLINE void
  try_reset(ob)
struct object *ob;
{
  if ((ob->next_reset < current_time) && !(ob->flags & O_RESET_STATE)) {
    if(d_flag) {
      fprintf(stderr, "(lazy) RESET %s\n", ob->name);
    }
    /* need to set the flag here to prevent infinite loops in apply_low */
    ob->flags |= O_RESET_STATE;
    reset_object(ob, 1);
  }
}
#endif