lpc4/lib/
lpc4/lib/doc/efun/
lpc4/lib/doc/lfun/
lpc4/lib/doc/operators/
lpc4/lib/doc/simul_efuns/
lpc4/lib/doc/types/
lpc4/lib/etc/
lpc4/lib/include/
lpc4/lib/include/arpa/
lpc4/lib/obj/d/
lpc4/lib/save/
lpc4/lib/secure/
lpc4/lib/std/
lpc4/lib/std/living/
#include "global.h"
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <setjmp.h>
#include <errno.h>
#if defined(sun)
#include <alloca.h>
#endif
          
/* unistd.h defines _POSIX_VERSION on POSIX.1 systems.  */
#if defined(DIRENT) || defined(_POSIX_VERSION)
#include <dirent.h>
#define NLENGTH(dirent) (strlen((dirent)->d_name))
#else /* not (DIRENT or _POSIX_VERSION) */
#define dirent direct
#define NLENGTH(dirent) ((dirent)->d_namlen)
#ifdef SYSNDIR
#include <sys/ndir.h>
#endif /* SYSNDIR */
#ifdef SYSDIR
#include <sys/dir.h>
#endif /* SYSDIR */
#ifdef NDIR
#include <ndir.h>
#endif /* NDIR */
#endif /* not (DIRENT or _POSIX_VERSION) */

#include "array.h"
#include "simulate.h"
#include "interpret.h"
#include "object.h"
#include "sent.h"
#include "exec.h"
#include "comm.h"
#include "main.h"
#include "dbase_efuns.h"
#include "stralloc.h"
#include "call_out.h"
#include "lex.h"
#include "backend.h"
#include "dynamic_buffer.h"
#include "otable.h"
#include "socket_efuns.h"
#include "simul_efun.h"

extern int errno;
extern int comp_flag;

char *inherit_file;

#ifdef _DCC
#define strcasecmp stricmp
#endif

#ifndef HAVE_UNISTD_H
extern int readlink PROT((char *, char *, int));
extern int symlink PROT((char *, char *));
extern int fchmod PROT((int, int));     
#endif

#ifdef AMIGA_PORT
#define lstat stat
#endif

char *last_verb;

extern int special_parse PROT((char *)),
    legal_path PROT((char *));
extern void pop_stack();

char **lfuns=NULL;
int num_lfuns=0;

void remove_all_players(), end_new_file(),
    print_svalue PROT((struct svalue *)),
    destruct2();

extern int d_flag,current_time;
extern struct svalue *sp;
struct object *obj_list, *obj_list_destruct, *master_ob;
struct object *current_object;      /* The object interpreting a function. */
struct object *command_giver;       /* Where the current command came from. */
int num_parse_error;		/* Number of errors in the parser. */
int new_clone_number;


struct variable *find_status(char *str,int must_find)
{
  int i;
  if((str=findstring(str)))
  {
    for (i=0; i < current_object->prog->num_variables; i++)
    {
      if (current_object->prog->variable_names[i].name==str)
	return &current_object->prog->variable_names[i];
    }
  }
  if (!must_find)
    return 0;
  error("--Status %s not found in prog for %s\n", str,
	current_object->prog->name);
  return 0;
}

/*
 * Give the correct uid and euid to a created object.
 */
void give_uid_to_object(struct object *ob)
{
  struct svalue *ret;
  char *creator_name;

  if (!current_object || !current_object->user)
  {
    /*
     * Only temporary.
     */
    ob->user = make_shared_string("NONAME");
    ob->eff_user = 0;
  }

  if (master_ob == 0)
  {
    ret=apply("get_root_uid",ob,0,1);
    if (ret == 0 || ret->type != T_STRING)
    {
      fprintf(stderr, "get_root_uid() in secure/master.c does not work\n");
      exit(1);
    }
    if(ob->user) free_string(ob->user);
    ob->user = copy_shared_string(strptr(ret));
    if(ob->eff_user) free_string(ob->eff_user);
    ob->eff_user=copy_shared_string(ob->user);
    master_ob=ob;
    add_ref(ob,"master object");
    return;
  }

  /*
   * Ask master.c who the creator of this object is.
   */
  push_shared_string(copy_shared_string(ob->prog->name));
  APPLY_MASTER_OB(ret=,"creator_file", 1);
  if (!ret)
    error("No function 'creator_file' in master.c!\n");
  if (ret->type != T_STRING)
  {
    /* This can be the case for objects in /ftp and /open. */
    destruct_object(ob);
    error("Illegal object to load.\n");
  }
  creator_name = strptr(ret);

  if(ob->user) free_string(ob->user);
  ob->user = copy_shared_string(creator_name);
  if(ob->eff_user) free_string(ob->eff_user);
  ob->eff_user = copy_shared_string(creator_name);
  return;
}

#define IS_POSTFIX(X,Y) (strlen(X)>=strlen(Y) && !strcmp((X)+strlen(X)-strlen(Y),(Y)))


/*
 * 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.
 *
 */

struct program *load_object(char *lname,struct inherit_check *inherit_err)
{
  FILE *f;
  extern int total_lines;
  extern int approved_object;

  extern struct program *prog;
  extern char *current_file;
  struct stat c_st;
  int name_length;
  char *name,*real_name;
  struct inherit_check i_check,*cnt;

  if (current_object && current_object->eff_user == 0)
    error("Can't load objects when no effective user.\n");

  /* Truncate possible .c in the object name. */
  /* Remove leading '/' if any. */
  if(!batch_mode)
  {
    while(lname[0] == '/') lname++;
    name_length = strlen(lname);
    name=(char *)alloca(name_length+1);
    strcpy(name, lname);
  }else{
    real_name=combine_path(0,lname);
    name_length=strlen(real_name);
    name=(char *)alloca(name_length+1);
    strcpy(name,real_name);
    free(real_name);
  }

  real_name=(char *)alloca(name_length+strlen(LPC_SUFFIX)+6);
  if (IS_POSTFIX(name,LPC_SUFFIX))
  {
    (void)strcpy(real_name, name);
    if(batch_mode)
    {
      name[name_length-2] = '\0';
      name_length -= 2;
    }
  }else{
    (void)strcpy(real_name, name);
    if(!batch_mode)
      strcat(real_name,LPC_SUFFIX);
  }

  /*
   * First check that the c-file exists.
   */
  if (stat(real_name, &c_st) == -1)
  {
    if(batch_mode)
    {
      strcat(real_name,".c");
      if (stat(real_name, &c_st) == -1) return 0;
    }else{
      return 0;
    }
  }
  for(cnt=inherit_err;cnt;cnt=cnt->next)
  {
    if (!strcmp(name, cnt->name))
    {
      /* hmm, we might loose the memory of inherit_file here */
      error("Illegal to inherit self.\n");
    }
  }

  /*
   * 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;
  }

  if (comp_flag) fprintf(stderr, " compiling %s ...", real_name);
  f = fopen(real_name, "r");
  if (f == 0)
  {
    perror(real_name);
    error("Couldn't read the file.\n");
  }

  start_new_file(f);
  current_file = string_copy(name); /* This one is freed below */
  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;
  /* 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;
    }

    i_check.name=name;
    i_check.next=inherit_err;

    inherit_file = 0;

    if(!load_object(tmp,&i_check))
    {
      error("Couldn't find program '%s' for inherit.\n",tmp);
    }
    free(tmp);
    return load_object(name,0);
  }
  /*
   * Can we approve of this object ?
   */
  prog->flags=ref_cycle;
  if (approved_object || batch_mode)
  {
    prog->flags |= O_APPROVED;
    prog->flags |= O_APPROVES;
  }else{
    struct svalue *ret;
    if(!master_ob)
    {
      prog->flags |= O_APPROVED;
    }else{
      if(!(prog->flags & O_APPROVED))
      {
	push_shared_string(copy_shared_string(prog->name));
	APPLY_MASTER_OB(ret=,"query_approve",1);
	if(ret)
	{
	  if(ret->type==T_NUMBER && ret->u.number>0)
	  {
	    prog->flags |= O_APPROVED;
	    if(ret->u.number>1) prog->flags |= O_APPROVES;
	  }
	}
      }
    }
  }

  if(!(prog->flags & O_APPROVED))
  {
    free_prog(prog,1);
    error("Object not approved %s\n",name);
  }

  enter_program_hash(prog);  /* add name to fast object lookup table */

  if (function_exists_in_prog("clean_up",prog))
    prog->flags |= O_WILL_CLEAN_UP;

  if (d_flag > 1 && prog)
    debug_message("--%s loaded\n", name);
  return prog;
}

struct program *make_object(char *str)
{
  extern int total_lines;
  extern int approved_object;
	
  extern struct program *prog;
  extern char *current_file;
  char name[200];
  struct inherit_check i_check;
	
  if (current_object && current_object->eff_user == 0)
    error("Can't create objects when no effective user.\n");
  if(batch_mode)
    sprintf(name,"/tmp/%s/%d",current_object->eff_user,++new_clone_number);
  else
    sprintf(name,"tmp/%s/%d",current_object->eff_user,++new_clone_number);

  if (comp_flag) fprintf(stderr, " compiling %s ...", name);

  start_new_string(str,strlen(str));
  current_file = string_copy(name); /* This one is freed below */
  compile_file();
  end_new_file();
  if (comp_flag)
    fprintf(stderr, " done\n");
  update_compile_av(total_lines);
  total_lines = 0;
  free(current_file);
  current_file = 0;
  /* 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;
    }

    i_check.name=name;
    i_check.next=0;

    inherit_file = 0;
    if(!load_object(tmp,&i_check))
    {
      error("Couldn't find program '%s' for inherit.\n",tmp);
    }
    free(tmp);
    return make_object(str);
  }

  prog->flags=ref_cycle;
  /*
   * Can we approve of this object ?
   */
  if (approved_object || batch_mode)
  {
    prog->flags |= O_APPROVED;
    prog->flags |= O_APPROVES;
  }else{
    struct svalue *ret;
    push_shared_string(copy_shared_string(prog->name));
    APPLY_MASTER_OB(ret=,"query_approve",1);
    if(ret)
    {
      if(ret->type==T_NUMBER && ret->u.number>0)
      {
	prog->flags |= O_APPROVED;
	if(ret->u.number>1) prog->flags |= O_APPROVES;
      }
    }
  }

  if(!(prog->flags & O_APPROVED))
  {
    free_prog(prog,1);
    error("Object not approved %s\n",name);
  }
  enter_program_hash(prog);  /* add name to fast object lookup table */

  if (function_exists_in_prog("clean_up",prog))
    prog->flags |= O_WILL_CLEAN_UP;

  if (d_flag > 1 && prog)
    debug_message("--%s loaded\n", name);
  return prog;
}


/*
 * Save the command_giver, because reset() in the new object might change
 * it.
 */
struct object *clone_object(char *str1,int nameit)
{
  struct object *new_ob;
  struct program *p;

  if(current_object && current_object->eff_user == 0)
    error("Illegal to call clone_object() with effective user 0\n");

  p=find_program(str1);
  if(!p)
  {
    struct svalue *ret;
    push_new_shared_string(str1);
    APPLY_MASTER_OB(ret=,"compile_object",1);
    if(!ret || ret->type!=T_OBJECT)
    {
      error("Couldn't clone object '%s' does file exist?\n",str1);
    }
    new_ob=ret->u.ob;
    if(nameit)
    {
      new_ob->obj_index=make_shared_string(str1);
      enter_my_object_hash(new_ob);
    }
    return new_ob;
  }

  new_ob = get_empty_object(p);

  if(nameit)
  {
    new_ob->obj_index=make_shared_string(str1);
    enter_my_object_hash(new_ob);
  }
  give_uid_to_object(new_ob);

  reset_object(new_ob, 0); 
  /* Never know what can happen ! :-( */
  if (new_ob->flags & O_DESTRUCTED) return 0;
  return new_ob;
}

struct object *environment(struct svalue *arg)
{
  struct object *ob = current_object;

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

int tot_alloc_sentence=0;

void free_sentence(struct sentences *s,int slot)
{
  free_string(s->sent[slot].match);
  free_svalue(&(s->sent[slot].function));
  s->sent[slot].match=NULL;
  s->used_slots--;
}

#ifdef DEBUG
void test_sentence(struct sentences *s)
{
  int e,num,num2;
  char *pos;
  if(s->used_slots>s->slots || s->slots>tot_alloc_sentence)
    fatal("Not a valid sentence structure.\n");
    
  num2=0;
  for(e=0;e<s->slots;e++)
  {
    if(!s->sent[e].match) continue;
    if(debug_findstring(s->sent[e].match)!=s->sent[e].match)
      fatal("Sentence not in shared string table.\n");
      
    for(num=0,pos=s->sent[e].match-1;(pos=STRCHR(pos+1,'%'));num++);
    if(num!=s->sent[e].num_arg)
    {
      fatal("Error in sentence.\n");
    }
    num2++;
  }
  if(num2!=s->used_slots)
    fatal("Error in sentence.\n");
}
#define TEST_SENTENCE(X) test_sentence(X)
#else
#define TEST_SENTENCE(X)
#endif

void free_sentences(struct sentences *s)
{
  int e;
  for(e=0;e<s->slots;e++)
    if(s->sent[e].match)
      free_sentence(s,e);
  free((char *)s);
}

struct sentences *cleanup_sentences(struct sentences *s)
{
  int e;
  struct sentences *s2;
  int size;
  TEST_SENTENCE(s);
  if(s->used_slots*3>s->slots || s->slots<=6) return s;
  size=s->slots/2;
  tot_alloc_sentence-=s->slots-size;
  s2=(struct sentences *)malloc(sizeof(struct sentences)+
		sizeof(struct sentence)*(size-1));
  s2->slots=size;
  s2->used_slots=0;
  for(e=0;e<s->slots;e++)
  {
    if(!s->sent[e].match) continue;
    s2->sent[s2->used_slots]=s->sent[e];
    s2->used_slots++;
  }
  for(e=s2->used_slots;e<s2->slots;e++)
    s2->sent[e].match=NULL;
  free((char *)s);
  TEST_SENTENCE(s2);
  return s2;
}

struct sentences *remove_sent_for(struct sentences *s,struct object *o)
{
  int e;
  TEST_SENTENCE(s);
  for(e=0;e<s->slots;e++)
  {
    if(!s->sent[e].match) continue;
    if(!s->sent[e].function.u.ob ||
       s->sent[e].function.u.ob==o ||
       (s->sent[e].function.u.ob->flags & O_DESTRUCTED))
      free_sentence(s,e);
  }
  return cleanup_sentences(s);
}

void remove_sent(struct object *ob,struct object *p)
{
  if(!p->sentences)
    return;
  p->sentences=remove_sent_for(p->sentences,ob);
}

int command_modification=0;

struct sentences *add_sentence(struct sentences *s,
		char *match,
		struct svalue *fun,
		int pri,
		int args)
{
  int unused,e;
  command_modification++;
  TEST_SENTENCE(s);
  if(s->used_slots==s->slots)
  {
    s=(struct sentences *)realloc((char *)s,sizeof(struct sentences)+
		sizeof(struct sentence)*(s->slots*2-1));
    tot_alloc_sentence+=s->slots;
    for(e=s->slots;e<s->slots*2;e++) s->sent[e].match=NULL;
    s->slots*=2;
  }

  TEST_SENTENCE(s);
  unused=-1;
  for(e=0;e<s->slots;e++)
  {
    if(s->sent[e].match==NULL) { unused=e; continue; }
    if(s->sent[e].pri<=pri) break;
  }
  if(unused==-1)
  {
    for(unused=e+1;s->sent[unused].match;unused++);
    for(;unused>e;unused--) s->sent[unused]=s->sent[unused-1];
  }else{
    e--;
    for(;unused<e;unused++) s->sent[unused]=s->sent[unused+1];
  }
  s->sent[e].pri=pri;
  s->sent[e].num_arg=args;
  s->sent[e].match=match;
  assign_svalue_no_free(&(s->sent[e].function),fun);
  s->used_slots++;
  TEST_SENTENCE(s);
  return s;
}


void add_action(struct svalue *s,struct svalue *fun,int priority)
{
  struct object *ob;
  char *pos;
  int num,e;
  char *str;

  str=copy_shared_string(strptr(s));
  
  if (current_object->flags & O_DESTRUCTED) return;
  ob = current_object;
  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)
    error("add_action from object that was not present.\n");
  
  for(num=0,pos=str-1;(pos=STRCHR(pos+1,'%'));num++);
  if(!command_giver->sentences)
  {
    tot_alloc_sentence+=6;
    command_giver->sentences=(struct sentences *)malloc(
	sizeof(struct sentences)+sizeof(struct sentence)*5);
    command_giver->sentences->slots=6;
    command_giver->sentences->used_slots=0;
    for(e=0;e<6;e++)
      command_giver->sentences->sent[e].match=NULL;
  }
  command_giver->sentences=add_sentence(command_giver->sentences,
	str,fun,priority,num);
}


/*
 * Find the sentence for a command from the player.
 * Return success status.
 */
int player_parser(char *cmd)
{
  int e,d;
  struct sentences *se;
  struct sentence *s;

  char *p;
  char verb_copy[100];
  char buff[1100];
  struct svalue *ss,*save_sp;
  extern struct svalue *sp;

  se=command_giver->sentences;
  if(!se) return 0;
  TEST_SENTENCE(se);

  /* skip leading spaces */
  while(*cmd==' ') cmd++;

  strncpy(buff,cmd,1090);

  if (d_flag > 1)
    debug_message("cmd [%s]: %s\n", command_giver->prog->name, buff);

  /* strip trailing spaces. */
  for (p = buff + strlen(buff) - 1; p >= buff && *p==' '; p--)
    *p = '\0';

  if (buff[0] == '\0') return 0;

  save_sp=sp;
  for (e=0;e<se->slots;e++)
  {
    struct svalue *ret;
    int tr;

    s=se->sent+e;
    if(!s->match) continue;

    if(s->function.u.ob->flags & O_DESTRUCTED)
    {
      free_sentence(se,e);
      continue;
    }

    while(save_sp!=sp) pop_stack();
    save_sp=sp;
    if(s->num_arg)
    {
      /* Gotta allocate some phony lvalues to call sscanf() */
      /* Puke, puke, puke */

      ss=sp;
      for(d=0;d<s->num_arg;d++) push_zero();

      push_new_shared_string(buff); /* move outside looop */
      push_shared_string(copy_shared_string(s->match));
	
      for(d=0;d<s->num_arg;d++)
      {
	extern struct lvalue *lsp;
	extern struct lvalue lvalues[LVALUES];
	lsp++;
	if(lsp>=lvalues+LVALUES)
	  error("Lvalue stack overflow.\n");
        ss++;
	lsp->ptr.sval=ss;
	lsp->rttype=T_ANY;
	lsp->type=LVALUE_LOCAL;
        sp++;
        sp->type = T_LVALUE;
        sp->u.lvalue = lsp;
      }
	
      tr=inter_sscanf(s->num_arg+2);
      if(!tr) continue;
      pop_n_elems(s->num_arg*2+2-tr);
    }else{
      if(strcmp(buff,s->match)) continue;
      tr=0;
    }
    /*
     * Now we have found a special sentence !
     */
      
    if (strlen(s->match) >= sizeof verb_copy)
    {
      strncpy(verb_copy, s->match, sizeof verb_copy - 1);
      verb_copy[sizeof verb_copy -1] = '\0';
    }else{
      strcpy(verb_copy, s->match); /* what did the user type anyway? */
    }
    last_verb = verb_copy;
    command_modification=0;
    ret = apply_lambda(&s->function, tr,0);
    last_verb = 0;

    if(!ret)
      error("Function for '%s' not found.\n",s->match);

    if (ret->type == T_NUMBER && ret->u.number == 0)
    {
      if(command_modification)
        error("Command list changed in action that returned 0.\n");
      continue;
    }

/* this used to update wizlist 
    if (s && command_object->user && command_giver->interactive)
    {
      command_object->user->score++;
    }
*/

    while(save_sp!=sp) pop_stack();
    command_modification++;
    return 1;
  }
  while(save_sp!=sp) pop_stack();
  command_modification++;
  return 0;
}


struct vector *query_actions(struct object *foo,struct object *bar)
{
  int num,e;
  struct sentences *s;
  struct vector *v,*r;

  if(!(foo->flags & O_ENABLE_COMMANDS)) return 0;
  if(!(s=foo->sentences))
  {
    return allocate_array(0);
  }
  if(bar)
  {
    for(num=e=0;e<s->slots;e++)
    {
      if(!s->sent[e].match) continue;
      if(s->sent[e].function.u.ob==bar) num++;
    }
  }else{
    num=s->used_slots;
  }
  v=allocate_array_no_init(num,0);

  num=0;
  for(num=e=0;e<s->slots;e++)
  {
    if(!s->sent[e].match) continue;
    if(s->sent[e].function.u.ob->flags & O_DESTRUCTED)
    {
      free_sentence(s,e);
      continue;
    }

    if(bar && s->sent[e].function.u.ob!=bar) continue;
    r=allocate_array_no_init(3,0);
    SET_STR(r->item+0,copy_shared_string(s->sent[e].match));
    assign_svalue_no_free(r->item+1,&(s->sent[e].function));
    r->item[2].type=T_NUMBER;
    r->item[2].subtype=NUMBER_NUMBER;
    r->item[2].u.number=s->sent[e].pri;
    v->item[num].u.vec=r;
    v->item[num].type=T_POINTER;
    num++;
  }
  return v;
}

/*
 * Take a user command and parse it.
 * The command can also come from a NPC.
 * Beware that 'str' can be modified and extended !
 */
int parse_command(char *str,struct object * ob)
{
  struct object *save = command_giver;
  int res;
  /* disallow users to issue commands containing ansi escape codes */
#ifdef NO_ANSI
  char *c;

  for (c = str; *c; c++) {
    if (*c == 27) {
      *c = ' ';			/* replace ESC with ' ' */
    }
  }
#endif
  command_giver = ob;
  res = player_parser(str);
  command_giver = save;
  return(res);
}

/*
 * Execute a command for an object. Copy the command into a
 * new buffer, because 'parse_command()' can modify the command.
 * If the object is not current object, static functions will not
 * be executed. This will prevent forcing players to do illegal things.
 *
 * Return cost of the command executed if success (> 0).
 * When failure, return 0.
 */
int command_for_object(char *str,struct object *ob)
{
  char buff[1000];
  extern int eval_cost;
  int save_eval_cost = eval_cost - 1000;
  struct svalue *t;

  if (strlen(str) > (sizeof(buff) - 1))
    error("Too long command.\n");

  if(ob != current_object)
  {
    push_shared_string(copy_shared_string(str));
    t=apply("query_valid_force",ob,1,0);
    if(IS_ZERO(t)) return 0; 
  }

  if (ob->flags & O_DESTRUCTED)
    return 0;
  strncpy(buff, str, sizeof buff);
  buff[sizeof buff - 1] = '\0';
  if (parse_command(buff, ob))
    return eval_cost - save_eval_cost;
  else
    return 0;
}

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

  command_modification++;
  if (ob->flags & O_DESTRUCTED)
    return;
  remove_object_from_stack(ob);

  /*
   * 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);
  }

  if (d_flag > 1)
    debug_message("Destruct object %s (ref %d)\n", ob->prog->name, ob->ref);
  super = ob->super;

  while(ob->contains)
  {
    struct object *obj2;
    obj2 = ob->contains;
    push_object(ob->contains);
    /* An error here will not leave destruct() in an inconsistent
     * stage.
     */
    APPLY_MASTER_OB((void),"destruct_environment_of",1);
    if (obj2->super==ob)
      destruct_object(obj2);
  }
 
  set_heart_beat(ob, 0);
  /*
   * Remove us out of this current room (if any).
   * Remove all sentences defined by this object from all objects here.
   */
  if (ob->super) {
    if (ob->super->flags & O_ENABLE_COMMANDS)
      remove_sent(ob, ob->super);
    for (pp = &ob->super->contains; *pp;) {
      if ((*pp)->flags & O_ENABLE_COMMANDS)
	remove_sent(ob, *pp);
      if (*pp != ob)
	pp = &(*pp)->next_inv;
      else
	*pp = (*pp)->next_inv;
    }
  }
  if(ob->sentences)
    free_sentences(ob->sentences);
  ob->sentences=NULL;
  /*
   * 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;
    break;
  }
  if (!removed)
    fatal("Failed to delete object.\n");
  if(ob->obj_index)
  {
    remove_my_object_hash(ob);
    free_string(ob->obj_index);
  }
  if(ob==command_giver) command_giver=0;
  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;
  ob->flags |= O_DESTRUCTED;
}

/*
 * This one is called when no program is executing from the main loop.
 */
void destruct2(struct object *ob)
{
  extern int tot_alloc_object_size;
  struct object **hb;
  int i;
  if (d_flag > 1)
    debug_message("Destruct-2 object %s (ref %d)\n", ob->prog->name, ob->ref);

  if(ob->next_heart_beat)
  {
    extern struct object *hb_list;
    for(hb=&hb_list;*hb!=ob;hb=&((*hb)->next_heart_beat))
      if(!*hb)
	fatal("Dangling hb pointer\n");
    *hb=ob->next_heart_beat;
    ob->next_heart_beat=0;
  }

  /*
   * We must deallocate variables here, not in 'free_object()'.
   * That is because one of the local variables may point to this object,
   * and deallocation of this pointer will also decrease the reference
   * count of this object. Otherwise, an object with a variable pointing
   * to itself, would never be freed.
   * Just in case the program in this object would continue to
   * execute, change string and object variables into the number 0.
   */

  /*
   * Deallocate variables in this object.
   * The space of the variables are not deallocated until
   * the object structure is freed in free_object().
   */
  for (i=0; i<ob->prog->num_variables; i++)
  {
    free_short_svalue(&ob->variables[i],ob->prog->variable_names[i].rttype);
  }

  /* this might not be true, but later we don't know have the program
     so we won't know the number of variables  */
  tot_alloc_object_size -=
    (ob->prog->num_variables - 1) * sizeof (struct svalue);
  free_prog(ob->prog, 1);
  free_string(ob->user);
  free_string(ob->eff_user);
  ob->user=ob->eff_user;
  ob->prog=0;
  free_object(ob, "destruct_object");
}

struct object *first_inventory(struct svalue *arg)
{
  struct object *ob=0;

  if (arg->type == T_OBJECT) ob = arg->u.ob;
  if (ob == 0) error("No object to first_inventory()");
  if (ob->contains == 0) return 0;
  return ob->contains;
}

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

void enable_commands(int num)
{
  if (current_object->flags & O_DESTRUCTED) return;
  if (d_flag > 1)
    debug_message("Enable commands %s (ref %d)\n",
		  current_object->prog->name, current_object->ref);

  if (num)
  {
    current_object->flags |= O_ENABLE_COMMANDS;
    command_giver = current_object;
  } else {
    if(current_object->sentences)
      free_sentences(current_object->sentences);
    current_object->sentences=0;
    current_object->flags &= ~O_ENABLE_COMMANDS;
    if(current_object == command_giver)
    {
      command_giver = 0;
      command_modification++;
    }
  }
}


#define MAX_LINES 50

/* get dir stolen from MudOS */

/*
 * These are used by qsort in get_dir().
 */
static int pstrcmp(struct svalue *p1,struct svalue *p2)
{
  return my_strcmp(p1, p2);
}
static int parrcmp(struct svalue *p1,struct svalue * p2)
{
  return my_strcmp(p1->u.vec->item, p2->u.vec->item);
}

static void encode_stat(struct svalue *vp,int flags,char *str,struct stat *st)
{
  if (flags == -1)
  {
    struct vector *v = allocate_array_no_init(3,0);

    SET_STR(v->item,make_shared_string(str));
    v->item[1].type = T_NUMBER;
    v->item[1].subtype = NUMBER_NUMBER;
    v->item[1].u.number =
      ((st->st_mode & S_IFDIR) ? -2 : st->st_size);
    v->item[2].type = T_NUMBER; 
    v->item[2].subtype = NUMBER_NUMBER; 
    v->item[2].u.number = st->st_mtime;
    vp->type = T_POINTER;
    vp->u.vec = v;
  } else {
    SET_STR(vp,make_shared_string(str));
  }
}
int match_string(char *match,char * str)
{
  int i;

 again:
  if (*str == '\0' && *match == '\0')
    return 1;
  switch(*match) {
  case '?':
    if (*str == '\0')
      return 0;
    str++;
    match++;
    goto again;
  case '*':
    match++;
    if (*match == '\0')
      return 1;
    for (i=0; str[i] != '\0'; i++)
      if (match_string(match, str+i))
	return 1;
    return 0;
  case '\0':
    return 0;
  case '\\':
    match++;
    if (*match == '\0')
      return 0;
    /* Fall through ! */
  default:
    if (*match == *str) {
      match++;
      str++;
      goto again;
    }
    return 0;
  }
}

/*
 * List files in directory. This function do same as standard list_files did,
 * but instead writing files right away to user this returns an array
 * containing those files. Actually most of code is copied from list_files()
 * function.
 * Differences with list_files:
 *
 *   - file_list("/w"); returns ({ "w" })
 *
 *   - file_list("/w/"); and file_list("/w/."); return contents of directory
 *     "/w"
 *
 *   - file_list("/");, file_list("."); and file_list("/."); return contents
 *     of directory "/"
 *
 * With second argument equal to non-zero, instead of returning an array
 * of strings, the function will return an array of arrays about files.
 * The information in each array is supplied in the order:
 *    name of file,
 *    last update of file,
 *    size of file (-2 means file doesn't exist).
 */
#define MAX_FNAME_SIZE 255
#define MAX_PATH_LEN   1024
typedef int (*cmpfuntyp) (const void *,const void *);

struct vector *get_dir(char *path,int flags)
{
    struct vector *v;
    int i, count = 0;
    DIR *dirp;
    int namelen, do_match = 0;
    struct dirent *de;
    struct stat st;
    char *endtemp;
	char temppath[MAX_FNAME_SIZE + MAX_PATH_LEN + 2];
	char regexp[MAX_FNAME_SIZE + MAX_PATH_LEN + 2];
    char *p;

    if (!path) return 0;

    if (strlen(path)<2) {
	temppath[0]=path[0]?path[0]:'.';
	temppath[1]='\000';
	p = temppath;
    } else {
	strncpy(temppath, path, MAX_FNAME_SIZE + MAX_PATH_LEN + 2);
	/*
	 * If path ends with '/' or "/." remove it
	 */
	if ((p = STRRCHR(temppath, '/')) == 0)
	    p = temppath;
	if (p[0] == '/' && ((p[1] == '.' && p[2] == '\0') || p[1] == '\0'))
	  *p = '\0';
    }

    if (stat(temppath, &st) < 0) {
	if (*p == '\0')
	    return 0;
	if (p != temppath) {
	    strcpy(regexp, p + 1);
	    *p = '\0';
	} else {
	    strcpy(regexp, p);
	    strcpy(temppath, ".");
	}
	do_match = 1;
    } else if (*p != '\0' && strcmp(temppath, ".")) {
	if (*p == '/' && *(p + 1) != '\0')
	    p++;
	v = allocate_array(1);
        encode_stat(&v->item[0], flags, p, &st);
	return v;
    }

    if ((dirp = opendir(temppath)) == 0)
	return 0;

    /*
     *  Count files
     */
    for (de = readdir(dirp); de; de = readdir(dirp))
    {
      namelen = NLENGTH(de);

      if (!do_match && (strcmp(de->d_name, ".") == 0 ||
			strcmp(de->d_name, "..") == 0))
	continue;
      if (do_match && !match_string(regexp, de->d_name))
	continue;
      count++;
      if (count >= MAX_ARRAY_SIZE)
	break;
    }
    /*
     * Make array and put files on it.
     */
    v = allocate_array(count);
    if (count == 0) {
	/* This is the easy case :-) */
	closedir(dirp);
	return v;
    }
    rewinddir(dirp);
    endtemp = temppath + strlen(temppath);
    strcat(endtemp++, "/");
    for(i = 0, de = readdir(dirp); i < count; de = readdir(dirp))
    {
      namelen = NLENGTH(de);
      if (!do_match && (strcmp(de->d_name, ".") == 0 ||
			strcmp(de->d_name, "..") == 0))
	continue;
      if (do_match && !match_string(regexp, de->d_name))
	continue;
      de->d_name[namelen] = '\0';
      if (flags == -1) {
	/* We'll have to .... sigh.... stat() the file to
	   get some add'tl info.  */
	strcpy(endtemp, de->d_name);
	stat(temppath, &st);	/* We assume it works. */
      }
      encode_stat(&v->item[i], flags, de->d_name, &st);
      i++;
    }
    closedir(dirp);
    /* Sort the names. */
    qsort((char *)v->item, count, sizeof v->item[0],
	  (cmpfuntyp)(  (flags == -1) ? parrcmp : pstrcmp ));
    return v;
}

/* Profezzorn */
void print_svalue(struct svalue *arg)
{
  char buf[300];

#ifdef DEBUG
  if (!arg)
    fatal("Null argument to print svalue.\n");
#endif
  switch(arg->type)
  {
  case T_STRING:
    push_svalue(arg);
    break;

  case T_FLOAT:
    sprintf(buf,"%0.8f", arg->u.fnum);
    push_new_shared_string(buf);
    break;

  case T_OBJECT:
    sprintf(buf,"OBJ(%d)", arg->u.ob->clone_number);
    push_new_shared_string(buf);
    break;

  case T_NUMBER:
    sprintf(buf,"%d", arg->u.number);
    push_new_shared_string(buf);
    break;

  case T_POINTER:
    sprintf(buf,"ARRAY[%d]",arg->u.vec->size);
    push_new_shared_string(buf);
    break;

  case T_LIST:
    sprintf(buf,"LIST[%d]",arg->u.vec->size);
    push_new_shared_string(buf);
    break;

  case T_MAPPING:
    sprintf(buf,"MAPPING[%d]",arg->u.vec->size);
    push_new_shared_string(buf);
    break;

  default:
    push_new_shared_string("<UNKNOWN>");
  }
  if(!command_giver)
  {
    fwrite(strptr(sp),1,my_strlen(sp),stdout);
    fflush(stdout);
    pop_stack();
    return;
  }
  (void)apply_lfun(LF_CATCH_TELL,command_giver,1,0);
}

void do_write(struct svalue *arg)
{
  struct object *save_command_giver = command_giver;
  print_svalue(arg);
  command_giver = save_command_giver;
}

struct object *find_hashed(char *name)
{
  struct object *ob;
  extern struct object *lookup_my_object_hash(char *);

  if(batch_mode)
  {
    name=combine_path(0,name);
    if(!(ob=lookup_my_object_hash(name)))
      ob=clone_object(name,1);
    free(name);
  }else{
    if(name[0]=='/') name++;
    if(!(ob=lookup_my_object_hash(name)))
      ob=clone_object(name,1);
  }
  return ob;
}

struct program *find_program(char *str)
{
  struct program *p;

  /* Remove leading '/' if any. */
  if(!batch_mode)
    while(str[0] == '/') str++;

  p = find_program2(str);
  if(!p)
    p = load_object(str,0);
  return p;
}

/* Look for a loaded object. Return 0 if non found. */
struct program *find_program2(char *str)
{
  register struct program *p;
  register int length;

  /* Remove leading '/' if any. */
  if(!batch_mode)
  {
    while(str[0] == '/') str++;
    /* Truncate possible .c in the object name. */
    length = strlen(str);
    if (IS_POSTFIX(str,LPC_SUFFIX))
    {
      /* A new writreable copy of the name is needed. */
      char *p;
      p = (char *)alloca(strlen(str)+1);
      strcpy(p, str);
      str = p;
      str[length-strlen(LPC_SUFFIX)] = '\0';
    }
    p=lookup_program_hash(str);
    return p;
  }else{
    str=combine_path(0,str);
    length=strlen(str);
    if (IS_POSTFIX(str,LPC_SUFFIX))
      str[length-strlen(LPC_SUFFIX)]=0;
    p=lookup_program_hash(str);
    free(str);
    return p;
  }
}

/*
 * 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.
 *
 * There are some boring compatibility to handle. When -o flag is specified,
 * several functions are called in some objects. This is dangerous, as
 * object might self-destruct when called.
 */
void move_object(struct object *item,struct object *dest)
{
  struct object **pp, *ob, *next_ob;
  struct object *save_cmd = command_giver;

  command_modification++;
  /* Recursive moves are not allowed. */
  for (ob = dest; ob; ob = ob->super)
    if (ob == item)
      error("Can't move object inside itself.\n");

  /*
   * Objects must have inherited std/object if they are to be allowed to
   * be moved.
   */
  if(!(item->flags & O_APPROVED) ||
     !(dest->flags & O_APPROVED)) {
    error("Trying to move object where src or dest not inherit std/object\n");
    return;
  }

  if (item->super) {
    int okey = 0;
		
    push_object(item);
    (void)apply_lfun(LF_EXIT, item->super, 1,0);
    if (item->flags & O_DESTRUCTED || dest->flags & O_DESTRUCTED)
      return;			/* Give up */

    remove_sent(item->super, item);

    if (item->super->flags & O_ENABLE_COMMANDS)
      remove_sent(item, item->super);
    for (pp = &item->super->contains; *pp;) {
      if (*pp != item) {
	if ((*pp)->flags & O_ENABLE_COMMANDS)
	  remove_sent(item, *pp);
	if (item->flags & O_ENABLE_COMMANDS)
	  remove_sent(*pp, item);
	pp = &(*pp)->next_inv;
	continue;
      }
      *pp = item->next_inv;
      okey = 1;
    }
    if (!okey)
      fatal("Failed to find object %s in super list of %s.\n",
	    item->prog->name, item->super->prog->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_lfun(LF_INIT, dest, 0,0);
    if ((dest->flags & O_DESTRUCTED) || item->super != dest) {
      command_giver = save_cmd; /* marion */
      return;
    }
  }
   /*
    * Run init of the item once for every present player, and
    * for the environment (which can be a player).
    */
  for (ob = dest->contains; ob && !(item->flags & O_DESTRUCTED); 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_lfun(LF_INIT, item, 0,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_lfun(LF_INIT, ob, 0,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 (item->flags & O_DESTRUCTED) /* Profezzorn */
    error("The moved object was destructed.\n");
  if (dest->flags & O_ENABLE_COMMANDS) {
    command_giver = dest;
    (void)apply_lfun(LF_INIT, item, 0,0);
  }
  command_giver = save_cmd;
}

char debug_parse_buff[50]; /* Used for debugging */

/*
 * Hard coded commands, that will be available to all players. They can not
 * be redefined, so the command name should be something obscure, not likely
 * to be used in the game.
 */
int special_parse(char *buff)
{
#ifdef DEBUG
  strncpy(debug_parse_buff, buff, sizeof debug_parse_buff);
  debug_parse_buff[sizeof debug_parse_buff - 1] = '\0';
#endif
#if defined(MALLOC_smalloc)
  if (strcmp(buff, "debugmalloc") == 0) {
    extern int debugmalloc;
    debugmalloc = !debugmalloc;
    if (debugmalloc)
      vtell_object("On.\n");
    else
      vtell_object("Off.\n");
    return 1;
  }
#endif
  if (strcmp(buff, "status") == 0 || strcmp(buff, "status tables") == 0)
  {
    int tot, res, verbose = 0;
    extern char *reserved_area;
    extern int tot_alloc_sentence, tot_alloc_object,
    tot_alloc_object_size;
    extern int num_arrays, total_array_size;
    extern int total_num_prog_blocks, total_prog_block_size;

    if (strcmp(buff, "status tables") == 0)
      verbose = 1;
    if (reserved_area)
      res = RESERVED_SIZE;
    else
      res = 0;

    if (!verbose) {
      vtell_object("Sentences:\t\t\t%8d %8d\n", tot_alloc_sentence,
		   (int)(tot_alloc_sentence * sizeof (struct sentence)));
      vtell_object("Objects:\t\t\t%8d %8d\n",
		   tot_alloc_object, tot_alloc_object_size);
      vtell_object("Arrays:\t\t\t\t%8d %8d\n", num_arrays,
		   total_array_size);
      vtell_object("Prog blocks:\t\t\t%8d %8d\n",
		   total_num_prog_blocks, total_prog_block_size);
      vtell_object("Memory reserved:\t\t\t %8d\n", res);
    }
    tot = total_prog_block_size +
      total_array_size +
	tot_alloc_object_size +
	  res;

    if (!verbose) {
      vtell_object("\t\t\t\t\t --------\n");
      vtell_object("Total:\t\t\t\t\t %8d\n", tot);
    }
    return 1;
  }
  return 0;
}


void fatal(char *fmt, ...)
{
  extern void debug_message_va(char *a,va_list args);
  va_list args;
  static int in_fatal = 0;

  va_start(args,fmt);
  /* Prevent double fatal. */
  if (in_fatal) abort();

  in_fatal = 1;
  (void)VFPRINTF(stderr, fmt, args);
  fflush(stderr);
  if (current_object)
    (void)fprintf(stderr, "Current object was %s\n",
		  current_object->prog->name);
  debug_message_va(fmt, args);
  if (current_object)
    debug_message("Current object was %s\n", current_object->prog->name);
  debug_message("Dump of variables:\n");

  (void)dump_trace(1); 
  abort();
}

int num_error = 0;

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

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

void throw_error()
{
  extern int error_recovery_context_exists;
  extern jmp_buf error_recovery_context;
  extern struct svalue catch_value;
  if (error_recovery_context_exists > 1) {
    longjmp(error_recovery_context, 1);
    fatal("Throw_error failed!");
  }
  if(catch_value.type==T_STRING)
  {
    error("Throw: %s.\n",strptr(&catch_value));
  }
}

static char emsg_buf[2000];

void va_error(char *fmt, va_list args)
{
  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 eval_cost,max_eval_cost;
  struct object *ob,*init_ob;
  char buf[200];
  extern struct control_stack control_stack[MAX_TRACE];

#ifdef OPCPROF
  check_cost_for_instr(-1);
#endif
  VSPRINTF(emsg_buf+1, fmt, args);

  emsg_buf[0] = '*';	/* all system errors get a * at the start */
  if (error_recovery_context_exists==2) /* user catches this error */
  {
    struct svalue v;
    
    SET_STR(&v,make_shared_string(emsg_buf));
    assign_svalue(&catch_value, &v);
    longjmp(error_recovery_context, 1);
    fatal("Catch() longjump failed");
  }

  eval_cost=0;
  max_eval_cost=MAX_COST;
  num_error++;
  if (num_error > 1)
    fatal("Too many simultaneous errors.\n");
  debug_message("%s", emsg_buf+1);
  if (current_object)
  {
    debug_message("program: %s, object: %s line %d\n",
		  current_prog ? current_prog->name : "",
		  current_object->prog->name,
		  get_line_number_if_any());
  }
  fprintf(stderr,"%s",emsg_buf+1);

  if (current_object)
  {
    fprintf(stderr,"program: %s, object: %s line %d\n",
	    current_prog ? current_prog->name : "",
	    current_object->prog->name,
	    get_line_number_if_any());
  }
  ob=dump_trace(0);
  fflush(stderr);
  if (ob && ob->flags & O_DESTRUCTED)
  {
    if(command_giver)
      vtell_object("error when executing program in destroyed object %s\n",
		   ob->prog->name);
    debug_message("error when executing program in destroyed object %s\n",
		  ob->prog->name);
  }

  if (error_recovery_context_exists==3) /* user catches this error */
  {
    num_error--;
    free_svalue(&catch_value);
    longjmp(error_recovery_context, 1);
    fatal("Catch() longjump failed");
  }


  if(!control_stack[1].ob || control_stack[1].ob->flags & O_DESTRUCTED)
  {
    if(!current_object || current_object->flags & O_DESTRUCTED)
    {
      init_ob=NULL;
    }else{
      init_ob=current_object;
    }
  }else{
    init_ob=control_stack[1].ob;
  }

  reset_machine (0);
  push_new_shared_string(emsg_buf);
  if(current_object && !(current_object->flags & O_DESTRUCTED))
  {
    push_object(current_object);
  }else{
    push_zero();
  }
  if(current_prog)
  {
    push_shared_string(copy_shared_string(current_prog->name));
  }else{
    push_zero();
  }
  if(current_object)
  {
    if(current_object->obj_index)
    {
      push_new_shared_string(current_object->prog->name);
    }else{
      sprintf(buf,"%s(%d)",current_object->prog->name,
	      current_object->clone_number);
      push_new_shared_string(buf);
    }
  }else{
    push_zero();
  }
  if(init_ob) push_object(init_ob);
  else push_zero();

  current_object=NULL;
  push_number(get_line_number_if_any());
  APPLY_MASTER_OB((void),"handle_error",6);

  if (current_heart_beat)
  {
    set_heart_beat(current_heart_beat, 0);
    debug_message("Heart beat in %s turned off.\n",
		  current_heart_beat->prog->name);
    push_object(current_heart_beat);
    APPLY_MASTER_OB((void),"kill_heart_beat",1);
    current_heart_beat = 0;
  }
  num_error--;
  if (error_recovery_context_exists)
    longjmp(error_recovery_context, 1);
  if(batch_mode)
    exit(10);
  abort();
}

void error(char *fmt,...)
{
  va_list args;
  va_start(args,fmt);
  va_error(fmt,args);
  va_end(args);
}

/*
 * Check that it is an legal path. No '..' are allowed.
 */
int legal_path(char *path)
{
    char *p;
    if(batch_mode) return 1;
    if (path == NULL || STRCHR(path, ' '))
	return 0;
    if (path[0] == '/')
        return 0;
#ifdef MSDOS
    if (!valid_msdos(path)) return(0);
#endif
    for(p = STRCHR(path, '.'); p; p = STRCHR(p+1, '.')) {
	if (p[1] == '.')
	    return 0;
    }
    return 1;
}

/*
 * There is an error in a specific file. Ask the game driver to log the
 * message somewhere.
 */
void smart_log(char *error_file,int line,char * what)
{
  char buff[500];

  if(!batch_mode)
  {
    if (error_file == 0)
      return;
    if (strlen(what) + strlen(error_file) > sizeof buff - 100)
      what = "...[too long error message]...";
    if (strlen(what) + strlen(error_file) > sizeof buff - 100)
      error_file = "...[too long filename]...";
    sprintf(buff, "%s line %d:%s\n", error_file, line, what);
    push_new_shared_string(error_file);
    push_new_shared_string(buff);
    APPLY_MASTER_OB((void),"log_error", 2);
  }
}


/*
 * Check that a path to a file is valid for read or write.
 * This is done by functions in the master object.
 * The path is always treated as an absolute path, and is returned without
 * a leading '/'.
 * If the path was '/', then '.' is returned.
 * The returned string may or may not be residing inside the argument 'path',
 * so don't deallocate arg 'path' until the returned result is used no longer.
 * Otherwise, the returned path is temporarily allocated by apply(), which
 * means it will be dealocated at next apply().
 */

char *check_valid_path(char *path,int shared_string,
		       char *eff_user,
		       char *call_fun,
		       int writeflg)
{
  struct svalue *v;

  if(batch_mode)		/* who cares in batchmode */
  {
    return path;
  }else{
    if (eff_user == 0)
      return 0;
    if(shared_string)
      push_shared_string(copy_shared_string(path));
    else
      push_new_shared_string(path);
    push_shared_string(copy_shared_string(eff_user));
    push_new_shared_string(call_fun); /* fix? */

    if (writeflg)
    {
      APPLY_MASTER_OB(v=,"valid_write", 3);
    } else{
      APPLY_MASTER_OB(v=,"valid_read", 3);
    }
    if (!v || (v->type == T_NUMBER && v->u.number == 0))
      return 0;

    if(v->type==T_STRING) path=strptr(v);

    if (path[0] == '/')
      path++;
    if (path[0] == '\0')
      path = ".";
    if (legal_path(path))
      return path;
    return 0;
  }
}

char *simple_check_valid_path(struct svalue *s,char *fun,int writeflag)
{
  return check_valid_path(strptr(s),
			  1,
			  current_object->eff_user,
			  fun,writeflag);
}

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

void startshutdowngame() {   game_is_being_shut_down = 1;  }

/*
 * This one is called from the command "shutdown".
 * We don't call it directly from HUP, because it is dangerous when being
 * in an interrupt.
 */
#define DEALLOCATE_MEMORY_AT_SHUTDOWN

void shutdowngame()
{
#ifdef ADD_CACHE_SIZE
  extern void free_add_cache();
#endif
  extern void do_gc(int);
  extern char *reserved_area;
  extern struct svalue catch_value;
  char *s;
  int e;

  APPLY_MASTER_OB((void),"game_shutting_down",0);
#ifdef OPCPROF
  dump_opcode_info();
#endif
  do_close_database();
#ifdef DEALLOCATE_MEMORY_AT_SHUTDOWN
  free_svalue(&catch_value);
  free_all_call_outs();
  remove_all_objects();
  free_object(master_ob,"shutdown");
  free_string(hostname); hostname=NULL;
  do_gc(4711);
  for(e=0;e<num_lfuns;e++) free_string(lfuns[e]);
  free((char*)lfuns);
  free_simul_efun();
  free_reswords();
  if(reserved_area) free(reserved_area);
  free_memory_in_lex_at_exit();
  free_svalue(&const_empty_string);
  if((s=simple_free_buf())) free(s);
#ifdef ADD_CACHE_SIZE
  free_add_cache();
#endif
  if(d_flag)
  {
    int e;
    struct object *o;
    struct program *p;
    extern void dump_stralloc_strings();
    extern struct program *obj_table[OTABLE_SIZE];
    for(o=obj_list;o;o=o->next_all)
    {
      printf("Object not destructed: %s(%d) %d refs\n",
	     o->prog ? o->prog->name : "<program freed>",
	     o->clone_number,
	     o->ref);
    }
    for(e=0;e<OTABLE_SIZE;e++)
    {
      for(p=obj_table[e];p;p=p->next_hash)
      {
	printf("Program not freed: %s %d refs\n", p->name, p->ref);
      }
    }
    
    dump_stralloc_strings();
  }
#endif
  exit(0);
}

/*
 * Credits for some of the code below goes to Free Software Foundation
 * Copyright (C) 1990 Free Software Foundation, Inc.
 * See the GNU General Public License for more details.
 */
#ifndef S_ISDIR
#define	S_ISDIR(m)	(((m)&S_IFMT) == S_IFDIR)
#endif

#ifndef S_ISREG
#define	S_ISREG(m)	(((m)&S_IFMT) == S_IFREG)
#endif

int isdir (char *path)
{
  struct stat stats;

  return stat (path, &stats) == 0 && S_ISDIR (stats.st_mode);
}

void strip_trailing_slashes (char *path)
{
  int last;

  last = strlen (path) - 1;
  while (last > 0 && path[last] == '/')
    path[last--] = '\0';
}

struct stat to_stats, from_stats;

int
copy (from, to)
     char *from, *to;
{
  int ifd;
  int ofd;
  char buf[1024 * 8];
  int len;			/* Number of bytes read into `buf'. */
  
  if (!S_ISREG (from_stats.st_mode))
    {
      error ("cannot move `%s' across filesystems: Not a regular file\n", from);
      return 1;
    }
  
  if (unlink (to) && errno != ENOENT)
    {
      error ("cannot remove `%s'\n", to);
      return 1;
    }

  ifd = open (from, O_RDONLY, 0);
  if (ifd < 0)
    {
      error ("%s: open failed\n", from);
      return errno;
    }
  ofd = open (to, O_WRONLY | O_CREAT | O_TRUNC, 0600);
  if (ofd < 0)
    {
      error ("%s: open failed\n", to);
      close (ifd);
      return 1;
    }
#ifdef HAVE_FCHMOD
  if (fchmod (ofd, from_stats.st_mode & 0777))
    {
      error ("%s: fchmod failed\n", to);
      close (ifd);
      close (ofd);
      unlink (to);
      return 1;
    }
#endif
  
  while ((len = read (ifd, buf, sizeof (buf))) > 0)
    {
      int wrote = 0;
      char *bp = buf;
      
      do
	{
	  wrote = write (ofd, bp, len);
	  if (wrote < 0)
	    {
	      error ("%s: write failed\n", to);
	      close (ifd);
	      close (ofd);
	      unlink (to);
	      return 1;
	    }
	  bp += wrote;
	  len -= wrote;
	} while (len > 0);
    }
  if (len < 0)
    {
      error ("%s: read failed\n", from);
      close (ifd);
      close (ofd);
      unlink (to);
      return 1;
    }

  if (close (ifd) < 0)
    {
      error ("%s: close failed", from);
      close (ofd);
      return 1;
    }
  if (close (ofd) < 0)
    {
      error ("%s: close failed", to);
      return 1;
    }
  
#ifndef HAVE_FCHMOD
  if (chmod (to, from_stats.st_mode & 0777))
    {
      error ("%s: chmod failed\n", to,0,0,0,0,0,0,0,0);
      return 1;
    }
#endif

  return 0;
}

/* Move FROM onto TO.  Handles cross-filesystem moves.
   If TO is a directory, FROM must be also.
   Return 0 if successful, 1 if an error occurred.  */

int do_move (char *from,char * to)
{
  if (lstat (from, &from_stats) != 0)
    {
      error ("%s: lstat failed\n", from);
      return 1;
    }

  if (lstat (to, &to_stats) == 0)
    {
#ifndef MSDOS
      if (from_stats.st_dev == to_stats.st_dev
	  && from_stats.st_ino == to_stats.st_ino)
#else
      if (same_file(from,to))
#endif
	{
	  error ("`%s' and `%s' are the same file", from, to);
	  return 1;
	}

      if (S_ISDIR (to_stats.st_mode))
	{
	  error ("%s: cannot overwrite directory", to);
	  return 1;
	}

    }
  else if (errno != ENOENT)
    {
      error ("%s: unknown error\n", to);
      return 1;
    }
#ifdef SYSV
  if (isdir(from)) {
      char cmd_buf[100];
      sprintf(cmd_buf, "/usr/lib/mv_dir %s %s", from, to);
      return system(cmd_buf);
  } else
#endif /* SYSV */      
  if (rename (from, to) == 0)
    {
      return 0;
    }

  if (errno != EXDEV)
    {
      error ("cannot move `%s' to `%s'", from, to);
      return 1;
    }

  /* rename failed on cross-filesystem link.  Copy the file instead. */

  if (copy (from, to))
      return 1;
  
  if (unlink (from))
    {
      error ("cannot remove `%s'", from);
      return 1;
    }

  return 0;

}

char *read_file(char *file,int start,int len)
{
  struct stat st;
  FILE *f;
  char *str,*p,*p2,*end,c;
  int size;

  if (len < 0) return 0;

  if (!file)
    return 0;
  f = fopen(file, "r");
  if (f == 0)
    return 0;
  if (fstat(fileno(f), &st) == -1)
    fatal("Could not stat an open file.\n");
  size = st.st_size;
  if (size > READ_FILE_MAX_SIZE) {
    if ( start || len ) size = READ_FILE_MAX_SIZE;
    else {
      fclose(f);
      return 0;
    }
  }
  if (!start) start = 1;
  if (!len) len = READ_FILE_MAX_SIZE;
  str = xalloc(size + 1);
  str[size] = '\0';
  do {
    if (size > st.st_size)
      size = st.st_size;
    if (fread(str, size, 1, f) != 1) {
      fclose(f);
      free(str);
      return 0;
    }
    st.st_size -= size;
    end = str+size;
    for (p=str; ( p2=MEMCHR(p,'\n',end-p) ) && --start; ) p=p2+1;
  } while ( start > 1 );
  for (p2=str; p != end; ) {
    c = *p++;
    if (!c) c=' ';
    *p2++=c;
    if ( c == '\n' )
      if (!--len) break;
  }
  if ( len && st.st_size ) {
    size -= ( p2-str) ; 
    if (size > st.st_size)
      size = st.st_size;
    if (fread(p2, size, 1, f) != 1) {
      fclose(f);
      free(str);
      return 0;
    }
    st.st_size -= size;
    end = p2+size;
    for (; p2 != end; ) {
      c = *p2;
      if (!c) *p2=' ';
      p2++;
      if ( c == '\n' )
	if (!--len) break;
    }
    if ( st.st_size && len ) {
      /* tried to read more than READ_MAX_FILE_SIZE */
      fclose(f);
      free(str);
      return 0;
    }
  }
  *p2='\0';
  fclose(f);
#if 0				/* caller immediately frees the string again,
				 * so there's no use to make it smaller now
				 */
  if ( st.st_size > (p2-str) ) {
    /* can't allocate shared string when string type isn't passed to the caller */
    p2=strdup(str);
    free(str);
    return p2;
  }
#endif
  return str;
}

/* returns a shared string */
char *read_bytes(char *file,int start,int len)
{
  struct stat st;

  char *str,*p;
  int size, f;
#ifndef HAVE_UNISTD_H
  int lseek();
#endif

  if (len < 0) return 0;
  if(!batch_mode && len > MAX_BYTE_TRANSFER)
    len=MAX_BYTE_TRANSFER;
  if (!file)
    return 0;
  f = open(file, O_RDONLY);
  if (f < 0)
    return 0;

  if (fstat(f, &st) == -1)
    fatal("Could not stat an open file.\n");
  size = st.st_size;
  if(start < 0) 
    start = size + start;

  if (start >= size) {
    close(f);
    return 0;
  }
  if ((start+len) > size) 
    len = (size - start);

  if ((size = lseek(f,start, 0)) < 0)
    return 0;

  str = xalloc(len + 1);

  size = read(f, str, len);

  close(f);

  if (size <= 0) {
    free(str);
    return 0;
  }

  /*
   * The string has to end to '\0'!!!
   */
  str[size] = '\0';

  p = make_shared_binary_string(str,size);
  free(str);

  return p;
}

int write_bytes(char *file,int start,struct svalue *string)
{
  struct stat st;

  int size, f,len;
#ifndef HAVE_UNISTD_H
  int lseek();
#endif
  char *str;
  str=strptr(string);
  len=my_strlen(string);

  if (!file)return 0;
  if(!batch_mode && len > MAX_BYTE_TRANSFER)
    return 0;
  f = open(file, O_WRONLY | O_CREAT);
  if (f < 0)
    return 0;

  if (fstat(f, &st) == -1)
    fatal("Could not stat an open file.\n");
  size = st.st_size;

  if(start < 0) start += size;
  if(start < 0) return 0;

  if ((size = lseek(f,start, 0)) < 0)
    return 0;

  size = write(f, str, len);

  close(f);

  if (size <= 0) return 0;
  return size;
}
    
#include <sys/param.h>

char *combine_path(char *cwd,char *file)
{
  /* cwd is supposed to be combined already */
  register char *p,*pp,*ppp,*pppp;
  char *my_cwd;
  my_cwd=0;

  if(file[0]=='/')
  {
    cwd="/";
    file++;
  }else{
    
    if(!cwd)
    {
#ifdef HAVE_GETWD
      cwd=(char *)getwd(my_cwd=(char *)malloc(MAXPATHLEN+1));
      if(!cwd)
	fatal("Couldn't fetch current path.\n");
#else
#ifdef HAVE_GETCWD
      my_cwd=cwd=(char *)getcwd(0,1000); 
#else
      /* maybe autoconf was wrong.... if not, insert your method here */
      my_cwd=cwd=(char *)getcwd(0,1000); 
#endif
#endif
    }
  }
  if(cwd[strlen(cwd)-1]=='/')
  {
    p=(char *)xalloc(strlen(cwd)+strlen(file)+1);
    strcpy(p,cwd);
    strcat(p,file);
  }else{
    p=(char *)xalloc(strlen(cwd)+strlen(file)+2);
    strcpy(p,cwd);
    strcat(p,"/");
    strcat(p,file);
  }
  for(pp=STRCHR(p,'/');pp && *pp;)
  {
    if(pp[1]=='.')
    {
      if(pp[2]=='.' && (pp[3]=='/' || pp[3]==0))
      {
	pppp=pp+3;
	for(ppp=pp-1;ppp>=p && *ppp!='/';ppp--);

	pp=ppp;
	/* note: assignment, not comparisment */
	while((*(ppp++)=*(pppp++)));

	continue;
      }else if(pp[2]=='/' || pp[2]==0){
	ppp=pp;
	pppp=pp+2;
	while((*(ppp++)=*(pppp++))); /* note: assignment, not comparisment */
	continue;
      }
    }
    pp=STRCHR(pp+1,'/');
  }
  if(my_cwd) free(my_cwd);
  return p;
}