lpmud/mudlib/
lpmud/mudlib/doc/
lpmud/mudlib/doc/LPC/
lpmud/mudlib/log/
lpmud/mudlib/players/
lpmud/mudlib/room/maze1/
lpmud/mudlib/room/sub/
#include "os.h"
#include <setjmp.h>
#include "config.h"
#include "stdio.h"
#include "lnode.h"
#include "y.tab.h"
#include "interpret.h"
#include "object.h"
#include "sent.h"
#include "wiz_list.h"
#include "security.h"

/* main.c */
extern int d_flag;
extern void *xalloc(int size);
extern char *string_copy(char *str);
extern void debug_message(char *a, ...);
extern void debug_message_value(struct value *v);
extern char *escape_path (char *str);

/* lexical.l */
extern int current_line;
extern char *current_file;
extern void start_new_file(FILE *f);

/* backend.c */
extern struct object *current_reset;
extern int error_recovery_context_exists;
extern jmp_buf error_recovery_context;
extern struct object *current_heart_beat;
extern void parse_command(char *str, struct object *ob);

/* comm1.c */
extern void set_snoop(struct object *me, struct object *you);
extern void remove_all_players(void);
extern void show_info_about(char *str, char *room, struct interactive *i);
extern int set_call(struct object *ob, struct sentence *sent);
extern void remove_interactive(struct object *ob);
extern void add_message(char *fmt, ...);
extern void ipc_remove(void);

/* lang.y */
extern struct lnode *prog, *heart_beat;
extern int variable_count;
extern int yyparse(void);
extern void yyerror(char *str);

/* ed.c */
extern void ed_start (char *file_arg);

/* interpret.c */
extern struct value *alloc_value(void);
extern int tot_alloc_value;
extern struct value *apply(char *fun, struct object *ob, struct value *arg);
#ifdef TRACE
extern char *dump_trace (void);
#endif

/* object.c */
extern void save_object(struct object *ob, char *file);
extern int restore_object(struct object *ob, char *file);
extern struct object *find_living_object(char *name);
extern struct object *find_player(char *name);
extern void tell_npc(struct object *ob, char *str);
extern void tell_object(struct object *ob, char *str);
extern int free_object(struct object *ob, char *from);
extern void add_ref(struct object *ob, char *from);
extern struct object *get_empty_object(void);
extern void dump_all_objects(void);
extern int tot_alloc_object;

/* lnode.c */
extern int tot_alloc_strings;
extern int tot_alloc_lnode;

/* swap.c */
extern int num_swapped;
extern int total_bytes_swapped;

/* string_space.c */
extern int tot_string_space;

#ifdef TRACE
  extern int trace_depth;
#endif

struct object *obj_list;
struct object *obj_list_destruct;
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. */
int num_error = 0;

static struct sentence *sent_free = 0;
static int tot_alloc_sentence;


/* simulate.c */
struct value *find_value(struct lnode_variable *p);
struct lnode_var_def *find_status(char *str, int must_find);
struct object *load_object(char *name);
struct object *previous_ob;
struct value *clone_object(char *str1);
struct value *environment(struct value *arg);
void destruct2(struct object *ob);
struct value *call_indirect(int fun, ...);
void free_sentence(struct sentence *p);
void player_parser(char *buff);
void print_local_commands(void);
void fatal(char *fmt, ...);
void error(char *fmt, ...);
void smart_log(char *error_file, int line, char *what);
char *check_file_name(char *file, int writeflg);
void shutdowngame(int sig);
int transfer_object(struct object *ob, struct object *to);
struct object *find_object(char *str);

static void remove_file(char *path);
static struct value *call_other(struct value *obj, char *name, struct value *arg);
static struct value *this_player(void);
static struct value *this_object(void);
static struct value *call_local_function(char *name, struct value *arg);
static char *make_new_name(char *str);
static void command_for_object(char *str);
static struct value *object_present(struct value *v, struct object *ob);
static struct value *object_present2 (struct value *v, struct object *ob);
static void destruct_object(struct value *v);
static struct value *create_wizard(char *owner);
static void say(struct value *v, struct object *avoid_ob);
static void tell_room(struct object *room, struct value *v);
static void shout_string(char *str);
static struct value *first_inventory(struct value *arg);
static struct value *next_inventory(struct value *arg);
static void enable_commands(void);
static struct value *input_to(char *fun);
static void list_files(char *path);
static void print_file(char *path);
static void people(void);
static void log_file(char *file, char *str);
static void do_write(struct value *arg);
static void apply_command(char *com);
static void set_current_room(struct object *ob, struct object *dest);
static void move_object(struct object *item, struct object *dest);
static void add_light(struct object *p, int n);
static struct sentence *alloc_sentence(void);
static void add_action(char *str, struct value *id);
static void add_verb(char *str);
static void remove_sent(struct object *ob, struct object *player);
static void display_all_players(void);
static int special_parse(char *buff);
static void pre_compile(char *str);
static int legal_path(char *path);
static void move_or_destruct(struct object *what, struct object *to);
static struct object *find_object2 (char *str);


struct value *find_value (struct lnode_variable *p)
{
  return &current_object->variables[p->number];
}

struct lnode_var_def *find_status (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;
}

static void remove_file (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;
}

struct object *load_object (char *name)
{
  FILE *f;
/* Why is command giver saved and restored ? */
  struct object *ob, *save_command_giver = command_giver;
  struct stat i_st, c_st;
  int i, name_length;
  char *real_name;

  /* Truncate possible .c in the object name. */
  name_length = strlen (name);
  name = string_copy (name);
  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.
   */
  real_name = (char *) xalloc (name_length + 3);
  strcpy (real_name, name);
  strcat (real_name, ".c");
  escape_path (real_name);
  if (stat (real_name, &c_st) == -1) {
    free (name);
    free (real_name);
    error ("Could not load descr for %s\n", real_name);
    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");
  }
  f = fopen (real_name, "r");
  if (f == 0) {
    perror (real_name);
    free (name);
    free (real_name);
    error ("Could not read the file.\n");
  }
  start_new_file (f);
  current_file = string_copy (real_name);
  current_line = 0;
  prog = heart_beat = 0;
  prog_status = 0;
  variable_count = 0;
  num_parse_error = 0;
  (void) yyparse ();
  (void) fclose (f);
  if (num_parse_error > 0 || prog == 0) {
    free (name);
    free (real_name);
    free (current_file);
    if (prog) {
      /*
       * Set the reference count to one.
       * We don't want to confuse free_prog().
       */
      add_prog_ref ((struct lnode_def *) prog);
      free_prog ((struct lnode_def *) prog);
    }
    /* The following unlinks are security patches - Drax */
    if (num_parse_error == 0 && prog == 0) {
      unlink (real_name);
      error ("No program in object !\n");
    }
    unlink (real_name);
    error ("Error in loading %s\n", real_name);
  }
  ob = get_empty_object ();
  ob->name = string_copy (name);
  ob->name_length = (int) 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;
    }
  }
  /* Allocate space for local variables. */
  if (variable_count > 0)
    ob->variables =
      (struct value *) xalloc (variable_count * sizeof (struct value));
  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;
  }
  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;
  ob->heart_beat = heart_beat;
  ob->reset = 1;
  (void) apply ("reset", ob, 0);
  command_giver = save_command_giver;
  if (d_flag)
    debug_message ("--%s loaded\n", ob->name);
  free (real_name);
  free (name);
  return ob;
}

/*
 * Call function in another object.
 * Save the caller object in previous_ob, so some functions can
 * see who called them.
 */
static struct value *call_other (struct value *obj, char *name, struct value *arg)
{
  struct object *ob, *save_command_giver = command_giver,
    *save_previous_ob = previous_ob;
  struct value *ret;

  if (obj->type == T_OBJECT)
    ob = obj->u.ob;
  else
    ob = find_object (obj->u.string);
  if (ob == 0)
    error ("Could not find object %s\n", obj);
  /*
   * Test if this really is an existing object.
   */
  if (ob->reset == 0)
    return 0;
  ob->not_touched = 0;
  previous_ob = current_object;
  ret = apply (name, ob, arg);
  previous_ob = save_previous_ob;
  command_giver = save_command_giver;
  if (ret == 0)
    return &const0;
  return ret;
}

static struct value *this_player (void)
{
  struct value *p = alloc_value ();

  if (command_giver == 0)
    return &const0;
  p->type = T_OBJECT;
  p->u.ob = command_giver;
  add_ref (command_giver, "this_player()");
  return p;
}

static struct value *this_object (void)
{
  struct value *p = alloc_value ();

  p->type = T_OBJECT;
  p->u.ob = current_object;
  add_ref (current_object, "this_object()");
  return p;
}

static struct value *call_local_function (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;
}

static char *make_new_name (char *str)
{
  static int i;
  char *p = (char *) xalloc (strlen (str) + 10);

  sprintf (p, "%s%d/", str, i);
  i++;
  return p;
}

struct value *clone_object (char *str1)
{
  struct object *ob, *new_ob;
  struct value *ret;
  int i;

  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");
  ob->reset = 0;
  if (ob == 0)
    error ("Object %s not found\n", str1);
  new_ob = get_empty_object ();
  new_ob->name = make_new_name (ob->name);
  new_ob->cloned = 1;
  new_ob->prog = ob->prog;
  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;
  obj_list = new_ob;
  new_ob->num_variables = ob->num_variables;
  /* Allocate space for local variables. */
  if (ob->num_variables > 0)
    new_ob->variables =
      (struct value *) xalloc (ob->num_variables * sizeof (struct value));
  /* Initialize variables... */
  for (i = 0; i < ob->num_variables; i++) {
    new_ob->variables[i].u.number = 0;
    new_ob->variables[i].type = T_NUMBER;
  }
  new_ob->reset = 1;
  (void) apply ("reset", new_ob, 0);
  if (d_flag)
    debug_message ("--%s cloned to %s\n", ob->name, new_ob->name);
  ret = alloc_value ();
  ret->type = T_OBJECT;
  ret->u.ob = new_ob;
  add_ref (new_ob, "clone_object");
  return ret;
}

struct value *environment (struct value *arg)
{
  struct value *ret;
  struct object *ob;

  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.
 */
static void command_for_object (char *str)
{
  char buff[1000];
  struct object *ob;

  ob = current_object;
  strncpy (buff, str, sizeof buff);
  parse_command (buff, ob);
}

/*
 * 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_present (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 (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 (struct value *v, struct object *ob)
{
  struct value *ret;
  char *p, *tmp;
  int count = 0;
  struct value item;

  item.type = T_STRING;
  item.u.string = string_copy (v->u.string);
  p = item.u.string + strlen (item.u.string) - 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';
    }
  }
  /* We need to put the cropped string into v->u.string to pass it to apply */
  tmp = v->u.string;
  v->u.string = item.u.string;
  for (; ob; ob = ob->next_inv) {
    ret = apply ("id", ob, v);
    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");
    v->u.string = tmp;
    free (item.u.string);
    return ret;
  }
  v->u.string = tmp;
  free (item.u.string);
  return 0;
}

/*
 * Remove an object. It is first moved into the destruct list, and
 * not really destructed until later. (destruct2()).
 */
static void destruct_object (struct value *v)
{
  struct object *ob, *super;
  struct object **pp;
  int removed;
  struct value *weight, neg_weight;

  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);
  /*
   * Now remove us out of the list of all objects.
   */
  removed = 0;
  for (pp = &obj_list; *pp; pp = &(*pp)->next_all) {
    if (*pp != ob)
      continue;
    *pp = (*pp)->next_all;
    removed = 1;
    break;
  }
  if (!removed && command_giver)
    add_message ("Failed to delete object.\n");
  if (ob == current_reset)
    current_reset = current_reset->next_all;
  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 room;
      room.type = T_OBJECT;
      room.u.ob = ob;
      apply ("exit", super, &room);
    }
    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");

  while (ob->contains)
    move_or_destruct (ob->contains, super);
  /*
   * 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;
    }
  }
  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 (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->variables) {
    /*
     * 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++) {
      if (ob->variables[i].type == T_STRING) {
        free (ob->variables[i].u.string);
      } else if (ob->variables[i].type == T_OBJECT) {
        free_object (ob->variables[i].u.ob, "destruct_object var");
      }
      ob->variables[i].type = T_NUMBER;
      ob->variables[i].u.number = 0;
    }
  }
  free_object (ob, "destruct_object");
}

static struct value *create_wizard (char *owner)
{
  struct stat st;
  char name[200], cmd[200];
  FILE *f;
  struct value *ret;
  struct object *owner_obj;

  owner_obj = find_player (owner);
  if (owner_obj == 0)
    fatal ("create_wizard: Could not find the player %s.\n", owner);
  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)
#ifdef WIN32
    if (mkdir (PLAYER_DIR) == -1)
#else
    if (mkdir (PLAYER_DIR, 0777) == -1)
#endif
      error ("Could not mkdir %s\n", PLAYER_DIR);
  sprintf (name, "%s/%s", PLAYER_DIR, owner);
  escape_path (name);
  if (stat (name, &st) == 0)
    error ("Player %s already has a castle!\n", owner);
#ifdef WIN32
  if (mkdir (name) == -1) {
#else
  if (mkdir (name, 0777) == -1) {
#endif
    perror (name);
    error ("Could not mkdir %s\n", name);
  }
  sprintf (name, "%s/%s/%s", PLAYER_DIR, owner, "castle.c");
  escape_path (name);
  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.
 */
static void say (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);
      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);
        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().
 */
static void tell_room (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);
      continue;
    }
    command_giver = ob;
    add_message ("%s", buff);
  }
  command_giver = save_command_giver;
}

static void shout_string (char *str)
{
  struct object *ob, *save_command_giver = command_giver;
  int emergency;
  struct value *muffled;

  emergency = (str[0] == '!');
  if (emergency)
    str++;
  for (ob = obj_list; ob; ob = ob->next_all) {
    if (ob->interactive == 0 || ob == save_command_giver)
      continue;
    muffled = apply ("query_muffled", ob, &const0);
    if (!emergency && muffled->type == T_NUMBER && muffled->u.number)
      continue;
    command_giver = ob;
    add_message ("%s", str);
  }
  command_giver = save_command_giver;
}

static struct value *first_inventory (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;
}

static struct value *next_inventory (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.
 */
static void enable_commands (void)
{
  current_object->enable_commands++;
  command_giver = current_object;
}

/*
 * Set up a function in this object to be called with the next
 * user input string.
 */
static struct value *input_to (char *fun)
{
  struct sentence *s = alloc_sentence ();

  if (set_call (command_giver, s)) {
    s->function = string_copy (fun);
    s->ob = current_object;
    return &const1;
  }
  free_sentence (s);
  return &const0;
}

static void list_files (char *path)
{
  char buff[1000];
  FILE *f;

  if (!path)
    path = ".";
  path = check_file_name (path, 0);
  if (path == 0)
    return;
  escape_path (path);
  sprintf (buff, "%s %s", LIST_FILES, path);
//    f = vpopen(buff, "r");
#ifdef WIN32
  f = _popen (buff, "r");
#else
  f = popen (buff, "r");
#endif
  if (f == 0)
    return;
  while (1) {
    if (fgets (buff, sizeof buff, f) == 0)
      break;
    add_message ("%s", buff);
  }
//    vpclose(f);
#ifdef WIN32
  _pclose (f);
#else
  pclose (f);
#endif
}

static void print_file (char *path)
{
  char buff[1000];
  FILE *f;

  path = check_file_name (path, 0);
  if (path == 0)
    return;
  escape_path (path);
  f = fopen (path, "r");
  if (f == 0)
    return;
  while (1) {
    if (fgets (buff, sizeof buff, f) == 0)
      break;
    add_message ("%s", buff);
  }
  fclose (f);
}

/* 5/10/90 - version to deal with invisibility */
static void people (void)
{
  struct object *ob;
  struct value *player, *invis, *level;
  int level_of_user;
  char name[30];

  name[0] = 0;
  level = apply ("query_level", command_giver, 0);
  if (level->type == T_NUMBER)
    level_of_user = level->u.number;
  else
    level_of_user = 0;
  for (ob = obj_list; ob; ob = ob->next_all) {
    if (!ob->interactive)
      continue;
    if (!ob->super)
      continue;
    player = apply ("query_real_name", ob, 0);
    if (player == 0 || player->type != T_STRING)
      continue;
    player->u.string[0] -= 32;
    invis = apply ("query_invis", ob, level);
    if (invis->type != T_NUMBER)
      continue;
    if (!(invis->u.number))
      strcpy (name, player->u.string);
    if ((invis->u.number) < 0)
      sprintf (name, "(%s)  %d", player->u.string, -invis->u.number);
    if ((invis->u.number) > 0)
      strcpy (name, "Someone");
    if ((invis->u.number) < 100)
      show_info_about (name, ob->super->name, ob->interactive);
  }
}

static void log_file (char *file, char *str)
{
  FILE *f;
  char file_name[100];

  if (strchr (file, '/') || strchr (file, '\\') || file[0] == '.' || strlen (file) > 30)
    error ("Illegal file name to log_file(%s)\n", file);
  sprintf (file_name, "log/%s", file);
  escape_path (file_name);
  f = fopen (file_name, "a");
  if (f == 0)
    return;
  fwrite (str, strlen (str), 1, f);
  fclose (f);
}

/*VARARGS1*/
struct value *call_indirect (int fun, ...)
{
  struct value *ret;
  struct object *o1, *o2;
  va_list args;
  char *arg, *arg2, *arg3;

  va_start (args, fun);
  arg = va_arg (args, void *);
  arg2 = va_arg (args, void *);
  arg3 = va_arg (args, void *);
  va_end (args);

  switch (fun) {
  case F_WIZLIST:
    wizlist ();
    break;
  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_SNOOP:
    if (arg && ((struct object *) arg)->destructed)
      error ("snoop() on destructed object.\n");
    set_snoop (current_object, (struct object *) arg);
    return 0;
  case F_SET_HEART_BEAT:
    current_object->enable_heart_beat = (int) arg;
    return 0;
  case F_LOG_FILE:
    log_file ((char *) arg, (char *) arg2);
    return 0;
  case F_SHUTDOWN:
    shutdowngame (0);
    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_PEOPLE:
    people ();
    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_FIND_LIVING:
    o1 = find_living_object ((char *) arg);
    if (o1 == 0)
      return &const0;
    ret = alloc_value ();
    ret->type = T_OBJECT;
    ret->u.ob = o1;
    add_ref (ret->u.ob, "find_living_object");
    return ret;
  case F_FIND_PLAYER:
    o1 = find_player ((char *) arg);
    if (o1 == 0)
      return &const0;
    ret = alloc_value ();
    ret->type = T_OBJECT;
    ret->u.ob = o1;
    add_ref (ret->u.ob, "find_player");
    return ret;
  case F_RM:
    remove_file ((char *) arg);
    return 0;
  case F_LS:
    list_files ((char *) arg);
    return 0;
  case F_CAT:
    print_file ((char *) arg);
    return 0;
  case F_INPUT_TO:
    if (current_object->destructed)
      error ("input_to() on destructed object.\n");
    return input_to ((char *) arg);
  case F_ENABLE_COMMANDS:
    enable_commands ();
    return 0;
  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, (int) 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:
    command_for_object ((char *) arg);
    return 0;
  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_ACTION:
    add_action ((char *) arg, (struct value *) arg2);
    break;
  case F_ADD_VERB:
    add_verb ((char *) arg);
    break;
  case F_THIS_PLAYER:
    return this_player ();
  case F_THIS_OBJECT:
    return this_object ();
  default:
    fatal ("Unimplemented hard linked function %d\n", fun);
    WIN32CLEANUP
    abort ();
  }
  return 0;
}

static void do_write (struct value *arg)
{
  if (arg == 0)
    add_message ("<NULL>");
  else if (arg->type == T_STRING)
    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 (char *str)
{
  struct object *ob;

  ob = find_object2 (str);
  if (ob)
    return ob;
  ob = load_object (str);
  return ob;
}

/* Look for an loaded object. */
static struct object *find_object2 (char *str)
{
  register struct object *ob;
  register int length;
  char *name;

  /* 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;
  }
  for (ob = obj_list; ob; ob = ob->next_all) {
    if (length == ob->name_length && strcmp (ob->name, name) == 0) {
      free (name);
      return ob;
    }
  }
  free (name);
  return 0;
}

static void apply_command (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);
  }
}

static void set_current_room (struct object *ob, struct object *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");
  if (ob->enable_commands)
    command_giver = ob;
  if (ob->super) {
    if (!ob->super->destructed) {
      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;
}

/*
 * Transfer an object.
 */
static void move_object (struct object *item, struct object *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;
  item->not_touched = 0;
  dest->not_touched = 0;
  if (item->enable_commands) {
    set_current_room (item, dest);
    return;
  }
  if (item->super) {
    int okey = 0;
    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);
        pp = &(*pp)->next_inv;
        continue;
      }
      *pp = item->next_inv;
      okey = 1;
    }
    if (!okey)
      error ("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;
  /*
   * 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->enable_commands) {
      command_giver = ob;
      (void) apply ("init", item, 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.
 */

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

static struct sentence *alloc_sentence (void)
{
  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;
  return p;
}

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

void player_parser (char *buff)
{
  struct sentence *s;
  char *p;
  unsigned int length;

  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;
  if (special_parse (buff))
    return;
  p = strchr (buff, ' ');
  if (p == 0)
    length = strlen (buff);
  else
    length = p - buff;
  for (s = command_giver->sent; s; s = s->next) {
    struct value *ret;
    struct value arg;

    if (s->verb == 0)
      continue;
    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 we have a second argument which was not used for id()
     * verification, then we send it to the function.
     */
    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 we get fail from the call, it was wrong second argument. */
    if (ret && ret->type == T_NUMBER && ret->u.number == 0)
      continue;
    if (s->ob->wl && command_giver->interactive)
      s->ob->wl->score++;
    if (ret == 0)
      add_message ("Error: function %s not found.\n", s->function);
    break;
  }
  if (s == 0)
    add_message ("What?\n");
}

static void add_action (char *str, struct value *id)
{
  struct sentence *p;

  if (d_flag)
    debug_message ("--Add action %s\n", str);
  if (command_giver) {
    p = alloc_sentence ();
    p->function = string_copy (str);
    p->ob = current_object;
    p->next = command_giver->sent;
    p->verb = 0;
    command_giver->sent = p;
  } else
    yyerror ("Add_action called with no command_giver");
}

static void add_verb (char *str)
{
  if (d_flag)
    debug_message ("--Adding verb %s to action %s\n", str,
      command_giver->sent->function);
  if (command_giver) {
    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 = string_copy (str);
  } else
    yyerror ("Add_verb called with no command_giver");
}

static void remove_sent (struct object *ob, struct object *player)
{
  struct sentence **s;

  for (s = &player->sent; *s;) {
    struct sentence *tmp;
    if ((*s)->ob == ob) {
      if (d_flag)
        debug_message ("--Unlinking sentence %s\n", (*s)->function);
      tmp = *s;
      *s = tmp->next;
      free_sentence (tmp);
    } else
      s = &((*s)->next);
  }
}

static void display_all_players (void)
{
  struct object *ob;
  struct value *ret;

  for (ob = obj_list; ob; ob = ob->next_all) {
    if (ob->interactive == 0)
      continue;
    ret = apply ("who", ob, &const0);
    if (ret && ret->type == T_STRING)
      add_message ("%s.\n", ret->u.string);
  }
}

static int special_parse (char *buff)
{
  char *tmpbuff;
  struct value *player;

  if (buff[0] == '#')
    return 0;
  if (strcmp (buff, "who") == 0) {
    display_all_players ();
    return 1;
  }
  if (strcmp (buff, "dumpallobj") == 0) {
    if (!command_giver)
      return 0;
    player = apply ("query_level", command_giver, 0);
    if (player->type != T_NUMBER || player->u.number < ALL_POWER)
      return 0;
    dump_all_objects ();
    return 1;
  }
  if (strcmp (buff, "status") == 0) {
    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 ("Values:      %5d %6d\n\n", tot_alloc_value,
      tot_alloc_value * sizeof (struct value));
    add_message ("Strings:          %6d\n", tot_alloc_strings);
    print_lnode_status (tot_alloc_sentence * sizeof (struct sentence) +
      tot_alloc_object * sizeof (struct object) +
      tot_alloc_value * sizeof (struct value) + tot_alloc_strings);
    add_message ("String space saved: %d bytes.\n", tot_string_space);
    return 1;
  }
  /* This probably shouldn't be done this way, since buff is only 2000
     bytes long, and will therefore crash if the input line is over
     1996 characters long. */
  if ((*buff == '\'') || (*buff == '"')) {
    tmpbuff = string_copy (buff);
    (void) strcpy (buff, "say ");
    (void) strcpy (buff + 4, tmpbuff + 1);
    free (tmpbuff);
    return 0;
  }
  if (*buff == ':') {
    tmpbuff = string_copy (buff);
    (void) strcpy (buff, "emote ");
    (void) strcpy (buff + 6, tmpbuff + 1);
    free (tmpbuff);
    return 0;
  }
  if (strcmp (buff, "e") == 0) {
    (void) strcpy (buff, "east");
    return 0;
  }
  if (strcmp (buff, "w") == 0) {
    (void) strcpy (buff, "west");
    return 0;
  }
  if (strcmp (buff, "s") == 0) {
    (void) strcpy (buff, "south");
    return 0;
  }
  if (strcmp (buff, "n") == 0) {
    (void) strcpy (buff, "north");
    return 0;
  }
  if (strcmp (buff, "ne") == 0) {
    (void) strcpy (buff, "northeast");
    return 0;
  }
  if (strcmp (buff, "nw") == 0) {
    (void) strcpy (buff, "northwest");
    return 0;
  }
  if (strcmp (buff, "se") == 0) {
    (void) strcpy (buff, "southeast");
    return 0;
  }
  if (strcmp (buff, "sw") == 0) {
    (void) strcpy (buff, "southwest");
    return 0;
  }
  if (strcmp (buff, "d") == 0) {
    (void) strcpy (buff, "down");
    return 0;
  }
  if (strcmp (buff, "u") == 0) {
    (void) strcpy (buff, "up");
    return 0;
  }
  if (strcmp (buff, "nw") == 0) {
    (void) strcpy (buff, "northwest");
    return 0;
  }
  if (strcmp (buff, "ne") == 0) {
    (void) strcpy (buff, "northeast");
    return 0;
  }
  if (strcmp (buff, "sw") == 0) {
    (void) strcpy (buff, "southwest");
    return 0;
  }
  if (strcmp (buff, "se") == 0) {
    (void) strcpy (buff, "southeast");
    return 0;
  }
  return 0;
}

void print_local_commands (void)
{
  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");
}

/*VARARGS1*/
void fatal (char *fmt, ...)
{
  va_list args;

  va_start (args, fmt);
  vfprintf (stderr, fmt, args);
  va_end (args);

  (void) fprintf (stderr, "Current object was %s\n", current_object->name);
  va_start (args, fmt);
  debug_message (fmt, args);
  va_end (args);

  debug_message ("Current object was %s\n", current_object->name);
  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 (&current_object->variables[p->num_var]);
      debug_message ("\n");
    }
  }
#ifdef TRACE
  (void) dump_trace ();
#endif
  ipc_remove ();                /* Shut down the ipc communication. */
  WIN32CLEANUP
  abort ();
}

/*VARARGS1*/
void error (char *fmt, ...)
{
  va_list args;
  struct object *dest;
#ifdef TRACE
  char *object_name;
#endif

  num_error++;
  if (num_error > 2)
    fatal ("Too many simultaneous errors.\n");
  va_start (args, fmt);
  debug_message (fmt, args);
  va_end (args);
  if (current_object)
    debug_message ("Current object was %s, line %d\n", current_object->name, current_line);
#ifdef TRACE
  object_name = dump_trace ();
  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) {
    va_start (args, fmt);
    add_message (fmt, args);
    va_end (args);
    if (current_object)
      add_message ("Current object was %s, line %d\n",
        current_object->name, current_line);
    /*
     * If this is a player, send him to the church.  Otherwise, send it to
     * the void.  Also make sure we have a church!
     */
    if (!(command_giver->enable_commands &&
        apply ("is_player", command_giver, &const0) &&
        (dest = find_object ("room/church.c"))))
      dest = find_object ("room/void.c");
    if (dest == 0)
      fatal ("Could not find the void room.\n");
    move_object (command_giver, dest);
  }
  if (current_heart_beat) {
    current_heart_beat->heart_beat = 0;
    debug_message ("Heart beat in %s turned off.\n",
      current_heart_beat->name);
    current_heart_beat = 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 (&current_object->variables[p->num_var]);
      debug_message ("\n");
    }
  }
  num_error--;
  if (error_recovery_context_exists)
    longjmp (error_recovery_context, 1);
  WIN32CLEANUP
  abort ();
}

#ifdef TRACE
char *get_current_object_name (void)
{
  if (current_object == 0)
    return "NONE";
  return current_object->name;
}

char *get_command_giver_name (void)
{
  if (command_giver == 0)
    return "NONE";
  return command_giver->name;
}
#endif

static void pre_compile (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 = (char *) xalloc (strlen (str) + 3);
  strcpy (i_name, str);
  strcat (i_name, ".i");
  c_name = (char *) xalloc (strlen (str) + 3);
  strcpy (c_name, str);
  strcat (c_name, ".c");
  escape_path (i_name);
  escape_path (c_name);
#if defined _MSC_VER
  sprintf (buff, "%s %s > %s", PRE_COMPILE, c_name, i_name);
#elif defined __BORLANDC__
  sprintf (buff, "%s -o%s %s", PRE_COMPILE, i_name, c_name);
#else
  sprintf (buff, "%s %s -o %s", PRE_COMPILE, c_name, i_name);
#endif
//    f = (FILE *)vpopen(buff, "r");
#ifdef WIN32
  f = _popen (buff, "r");
#else
  f = (FILE *) popen (buff, "r");
#endif
  if (f == 0) {
    error ("Unable to invoke precompiler!\n");
    WIN32CLEANUP
    abort ();
  }
  while (1) {
    if (fgets (buff, sizeof buff, f) == 0)
      break;
    add_message ("%s", buff);
  }
//    vpclose(f);
#ifdef WIN32
  _pclose (f);
#else
  pclose (f);
#endif
}

/*
 * Check that it is an legal path. It must not contain a space
 * or '..' or begin with '/' or '\'.
 */
static int legal_path (char *path)
{
  char *p;

  if (path == NULL || strchr (path, ' '))
    return 0;
  if (path[0] == '\\' || path[0] == '/')
    return 0;
  for (p = strchr (path, '.'); p; p = strchr (p + 1, '.')) {
    if (p[1] == '.')
      return 0;
  }
  return 1;
}

void smart_log (char *error_file, int line, char *what)
{
  char buff2[100], buff[100], *p;
  int n;

  if (error_file == 0)
    return;
#ifdef WIN32
  n = sscanf (error_file, "players\\%s", buff2);
#else
  n = sscanf (error_file, "players/%s", buff2);
#endif
  if (n != 1)
    return;
#ifdef WIN32
  p = strchr (buff2, '\\');
#else
  p = strchr (buff2, '/');
#endif
  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 *check_file_name (char *file, int writeflg)
{
  struct value v, *ret;

  if (!command_giver) {
    yyerror ("Check_file_name called with no 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 (!ret || ret->type != T_STRING) {
    add_message ("Bad file name.\n");
    return 0;
  }
  if (!legal_path (ret->u.string)) {
    add_message ("Illegal path\n");
    return 0;
  }
  return ret->u.string;
}

/*
 * This one is called from HUP, and from the command "shutdown".
 */
void shutdowngame (int sig)
{
  struct value *player;

  if (command_giver) {
    player = apply ("query_level", command_giver, 0);
    if (player->type != T_NUMBER || player->u.number < SHUTDOWN)
      return;
  }
  shout_string ("LPmud shutting down immediately.\n");
  fprintf (stderr, "simulate.c: shutdowngame: LP-mud shut down.\n");
  remove_all_players ();
  ipc_remove ();
  save_wiz_file ();
  WIN32CLEANUP
  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: Too 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 (struct object *ob, struct object *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 of weight of object in transfer()\n");
  /*
   * 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;
  }
  /*
   * 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))
      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))
      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))
      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 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.
 */
static void move_or_destruct (struct object *what, struct object *to)
{
  int res;
  struct value v;

  res = transfer_object (what, to);
  if (res == 0)
    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);
}