#include <sys/types.h>
#include <sys/stat.h>
#include <sys/dir.h>
#include <fcntl.h>
#include <setjmp.h>
#include <string.h>
#include <errno.h>
#include <stdio.h>
#include <memory.h>
#if defined(sun)
#include <alloca.h>
#endif
#ifdef M_UNIX
#include <dirent.h>
#endif

#include "lint.h"
#include "config.h"
#include "stdio.h"
#include "lang.h"
#include "interpret.h"
#include "object.h"
#include "sent.h"
#include "wiz_list.h"
#include "exec.h"
#include "comm.h"

extern int errno;
extern int comp_flag;
extern char *init_file;
char *inherit_file;

#if !defined(ultrix) && !defined(M_UNIX) && !defined(hpux) && !defined(sgi)
extern int rename PROT((char *, char *));
#endif
extern int readlink PROT((char *, char *, int));
extern int symlink PROT((char *, char *));
#ifndef rindex
extern char *rindex PROT((const char *, int));
#endif
#ifndef MSDOS
extern int lstat PROT((char *, struct stat *));
#else
#define lstat stat
#endif
extern int fchmod PROT((int, int));     
char *last_verb, *last_arg;

extern int special_parse PROT((char *)),
    set_call PROT((struct object *, struct sentence *, int)),
    legal_path PROT((char *));

void pre_compile PROT((char *)),
    remove_interactive PROT((struct object *)),
    add_light PROT((struct object *, int)),
    add_action PROT((char *, char *, int)),
    add_verb PROT((char *, int)),
    print_local_commands(), ipc_remove(),
    show_info_about PROT((char *, char *, struct interactive *)),
    set_snoop PROT((struct object *, struct object *)),
    print_lnode_status PROT((int)),
    remove_all_players(), start_new_file PROT((FILE *)), end_new_file(),
    move_or_destruct PROT((struct object *, struct object *)),
    load_ob_from_swap PROT((struct object *)), dump_malloc_data(),
    print_svalue PROT((struct svalue *)),
    debug_message_value(),
    destruct2();

extern int d_flag;

struct object *obj_list, *obj_list_destruct, *master_ob;

extern int current_line;
extern struct wiz_list *back_bone_uid;

struct object *current_object;      /* The object interpreting a function. */
struct object *command_giver;       /* Where the current command came from. */
struct object *current_interactive; /* The user who caused this execution */

int num_parse_error;		/* Number of errors in the parser. */

void shutdowngame();

extern void flush_all_player_mess();

struct variable *find_status(str, must_find)
    char *str;
    int must_find;
{
    int i;

    for (i=0; i < current_object->prog->num_variables; i++) {
	if (strcmp(current_object->prog->variable_names[i].name, str) == 0)
	    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->name);
    return 0;
}

/*
 * Give the correct uid and euid to a created object.
 */
int give_uid_to_object(ob)
    struct object *ob;
{
    struct object *tmp_ob;
#ifdef COMPAT_MODE
    char wiz_name[100];
#else
    struct svalue *ret;
    char *creator_name;
#endif

    if (master_ob == 0)
	tmp_ob = ob;
    else {
 	assert_master_ob_loaded();
	tmp_ob = master_ob;
    }
    
#ifdef COMPAT_MODE
    /* Is this object wizard defined ? */
    if (sscanf(ob->name, "players/%s", wiz_name) == 1) {
	char *np;
	np = strchr(wiz_name, '/');
	if (np)
	    *np = '\0';
	ob->user = add_name(wiz_name);
    } else {
	ob->user = 0;
    }
    ob->eff_user = ob->user;	/* Initial state */
    return 1;
#else

    if (!current_object || !current_object->user) {
	/*
	 * Only for the master and void object. Note that
	 * back_bone_uid is not defined when master.c is being loaded.
	 */
	ob->user = add_name("NONAME");
	ob->eff_user = 0;
	return 1;
    }

    /*
     * Ask master.c who the creator of this object is.
     */
    push_constant_string(ob->name);
    ret = apply("creator_file", tmp_ob, 1);
    if (!ret)
	error("No function 'creator_file' in master.c!\n");
    if (ret->type != T_STRING) {
	struct svalue arg;
	/* This can be the case for objects in /ftp and /open. */
	arg.type = T_OBJECT;
	arg.u.ob = ob;
	destruct_object(&arg, 0);
	error("Illegal object to load.\n");
    }
    creator_name = ret->u.string;
    /*
     * Now we are sure that we have a creator name.
     * Do not call apply() again, because creator_name will be lost !
     */
    if (strcmp(current_object->user->name, creator_name) == 0) {
	/* 
	 * The loaded object has the same uid as the loader.
	 */
	ob->user = current_object->eff_user;
	ob->eff_user = current_object->eff_user;
	return 1;
    }

    if (strcmp(back_bone_uid->name, creator_name) == 0) {
	/*
	 * The object is loaded from backbone. This is trusted, so we
	 * let it inherit the value of eff_user.
	 */
	ob->user = current_object->eff_user;
	ob->eff_user = current_object->eff_user;
	return 1;
    }

    /*
     * The object is not loaded from backbone, nor from 
     * from the loading objects path. That should be an object
     * defined by another wizard. It can't be trusted, so we give it the
     * same uid as the creator. Also give it eff_user 0, which means that
     * player 'a' can't use objects from player 'b' to load new objects nor
     * modify files owned by player 'b'.
     *
     * If this effect is wanted, player 'b' must let his object do
     * 'seteuid()' to himself. That is the case for most rooms.
     */
    ob->user = add_name(creator_name);
    ob->eff_user = (struct wiz_list *)0;
    return 1;
#endif /* COMPAT_MODE */
}

/*
 * Load an object definition from file. If the object wants to inherit
 * from an object that is not loaded, discard all, load the inherited object,
 * and reload again.
 *
 *
 * In mudlib3.0 when loading inherited objects, their reset() is not called.
 *
 * Save the command_giver, because reset() in the new object might change
 * it.
 *
 * 
 */
struct object *load_object(lname, dont_reset)
    char *lname;
    int dont_reset;
{
    static FILE *f = NULL;
    extern int total_lines;
    extern int approved_object;

    struct object *ob, *save_command_giver = command_giver;
    extern struct program *prog;
    extern char *current_file;
#ifdef LEX_lexical
    char *current_i_file;
    struct stat i_st;
#endif
    struct stat c_st;
    int name_length;
    char real_name[200], name[200];

    if (f) 
    {
       fclose(f); 
       f = NULL; 
    }
 
#ifndef COMPAT_MODE
    if (current_object && current_object->eff_user == 0)
       error("Can't load objects when no effective user.\n");
#endif
    /* Truncate possible .c in the object name. */
    /* Remove leading '/' if any. */
    while(lname[0] == '/')
	lname++;
    strncpy(name, lname, sizeof(name));
    name[sizeof(name)-1] = 0;
    name_length = strlen(name);
    if (name_length > sizeof name - 4)
	name_length = sizeof name - 4;
    name[name_length] = '\0';
    if (name[name_length-2] == '.' && name[name_length-1] == 'c') {
	name[name_length-2] = '\0';
	name_length -= 2;
    }
    /*
     * First check that the c-file exists.
     */
    (void)strcpy(real_name, name);
    (void)strcat(real_name, ".c");
    if (stat(real_name, &c_st) == -1) {
	fprintf(stderr, "Could not load descr for %s\n", real_name);
	error("Failed to load file.\n");
	return 0;
    }
    /*
     * Check if it's a legal name.
     */
    if (!legal_path(real_name)) {
	fprintf(stderr, "Illegal pathname: %s\n", real_name);
	error("Illegal path name.\n");
	return 0;
    }

#ifdef LEX_lexical
    real_name[name_length+1] = 'i';
    if (stat(real_name, &i_st) == -1 || i_st.st_mtime < c_st.st_mtime) {
       real_name[name_length+1] = 'c';
       pre_compile(name);
       real_name[name_length+1] = 'i';
    }
    if (stat(real_name, &i_st) == -1 || i_st.st_mtime < c_st.st_mtime) {
       if (command_giver)
          add_message("Failed to compile the new file.\n");
    }
    current_i_file = real_name;
#endif
    if (comp_flag)
	fprintf(stderr, " compiling %s ...", real_name);
    f = fopen(real_name, "r");
    if (f == 0) {
	perror(real_name);
	error("Could not read the file.\n");
    }
    start_new_file(f);
    current_file = string_copy(real_name);	/* This one is freed below */

#ifdef LEX_lexical
    current_line = 0;
    num_parse_error = 0;
#endif
    compile_file();
    end_new_file();
    if (comp_flag)
        fprintf(stderr, " done\n");
    update_compile_av(total_lines);
    total_lines = 0;
    (void)fclose(f);
    f = NULL;

#ifdef LEX_lexical
    unlink(current_i_file);
#endif

    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;
	}
	if (strcmp(inherit_file, name) == 0) {
	    free(inherit_file);
	    inherit_file = 0;
	    error("Illegal to inherit self.\n");
	}
	inherit_file = 0;
#ifdef MUDLIB3_NEED
#ifdef COMPAT_MODE
        load_object(tmp, 0);
#else
        load_object(tmp, 1);
#endif
#else
	load_object(tmp, 0);
#endif
	free(tmp);
	ob = load_object(name, dont_reset);
	return ob;
    }
    ob = get_empty_object(prog->num_variables);
    /*
     * Can we approve of this object ?
     */
    if (approved_object || strcmp(prog->name, "std/object.c") == 0)
	ob->flags |= O_APPROVED;
    ob->name = string_copy(name);	/* Shared string is no good here */
    add_ref(ob, "load_object");
    ob->prog = prog;
    ob->next_all = obj_list;
    obj_list = ob;
    enter_object_hash(ob);	/* add name to fast object lookup table */
    if (give_uid_to_object(ob) && !dont_reset)
       reset_object(ob, 0);
    if (!(ob->flags & O_DESTRUCTED) && function_exists("clean_up",ob) ) {
	ob->flags |= O_WILL_CLEAN_UP;
    }
    command_giver = save_command_giver;
    if (d_flag > 1 && ob)
	debug_message("--%s loaded\n", ob->name);
    return ob;
}

struct object *previous_ob;

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

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

/*
 * Save the command_giver, because reset() in the new object might change
 * it.
 */
struct object *clone_object(str1)
    char *str1;
{
    struct object *ob, *new_ob;
    struct object *save_command_giver = command_giver;

#ifndef COMPAT_MODE
    if (current_object && current_object->eff_user == 0)
       error("Illegal to call clone_object() with no effective user.\n"); 
#endif
    ob = find_object(str1);
    /*
     * If the object was destructed...
     */
    if (ob == 0)
       return 0;
    if (ob->super || (ob->flags & O_CLONE))
	error("Cloning a bad object!\n");
    
    /* We do not want the heart beat to be running for unused copied objects */

    if (ob->flags & O_HEART_BEAT) 
	(void)set_heart_beat(ob, 0);
    new_ob = get_empty_object(ob->prog->num_variables);
    new_ob->name = make_new_name(ob->name);
    new_ob->flags |= O_CLONE | ob->flags & ( O_APPROVED | O_WILL_CLEAN_UP ) ;
#if 0
      if (ob->flags & O_APPROVED)
  	new_ob->flags |= O_APPROVED;
#endif
    add_ref(new_ob, "clone_object");
    new_ob->prog = ob->prog;
    reference_prog(ob->prog, "clone_object");
#ifdef COMPAT_MODE
    if (current_object && current_object->user && !ob->user)
        new_ob->user = current_object->user;
    else
        new_ob->user = ob->user;		/* Possibly a null pointer */
    new_ob->eff_user = new_ob->user;
#else
    if (!current_object)
          fatal("clone_object() from no current_object!\n");
    give_uid_to_object(new_ob);
#endif
    new_ob->next_all = obj_list;
    obj_list = new_ob;
    enter_object_hash(new_ob);	/* Add name to fast object lookup table */
    reset_object(new_ob, 0); 
    command_giver = save_command_giver;
    /* Never know what can happen ! :-( */
    if (new_ob->flags & O_DESTRUCTED)
	return 0;
    return new_ob;
}

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

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

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

    if (strlen(str) > sizeof(buff) - 1)
	error("Too long command.\n");
    if (ob == 0)
	ob = current_object;
    else if (ob->flags & O_DESTRUCTED)
	return 0;
    strncpy(buff, str, sizeof buff);
    buff[sizeof(buff)-1] = 0;
    if (parse_command(buff, ob, ob!=current_object))
	return eval_cost - save_eval_cost;
    else
	return 0;
}

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

static struct object *object_present2 PROT((char *, struct object *));

struct object *object_present(v, ob)
    struct svalue *v;
    struct object *ob;
{
    struct svalue *ret;
    struct object *ret_ob;
    int specific = 0;

    if (ob == 0)
	ob = current_object;
    else
	specific = 1;
    if (ob->flags & O_DESTRUCTED)
	return 0;
    if (v->type == T_OBJECT) {
	if (specific) {
	    if (v->u.ob->super == ob)
		return v->u.ob;
	    else
		return 0;
	}
	if (v->u.ob->super == ob ||
	    (v->u.ob->super == ob->super && ob->super != 0))
	    return v->u.ob;
	return 0;
    }
    ret_ob = object_present2(v->u.string, ob->contains);
    if (ret_ob)
	return ret_ob;
    if (specific)
	return 0;
    if (ob->super) {
	push_string(v->u.string, STRING_CONSTANT);
	ret = apply("id", ob->super, 1);
	if (ob->super->flags & O_DESTRUCTED)
	    return 0;
	if (ret && !(ret->type == T_NUMBER && ret->u.number == 0))
	    return ob->super;
	return object_present2(v->u.string, ob->super->contains);
    }
    return 0;
}

static struct object *object_present2(str, ob)
    char *str;
    struct object *ob;
{
    struct svalue *ret;
    char *p;
    int count = 0, length;
    char *item;

    item = string_copy(str);
    length = strlen(item);
    p = item + length - 1;
    if (*p >= '0' && *p <= '9') {
	while(p > item && *p >= '0' && *p <= '9')
	    p--;
	if (p > item && *p == ' ') {
	    count = atoi(p+1) - 1;
	    *p = '\0';
	    length = p - item;	/* This is never used again ! */
	}
    }
    for (; ob; ob = ob->next_inv) {
	push_string(item, STRING_CONSTANT);
	ret = apply("id", ob, 1);
	if (ob->flags & O_DESTRUCTED) {
	    free(item);
	    return 0;
	}
	if (ret == 0 || (ret->type == T_NUMBER && ret->u.number == 0))
	    continue;
	if (count-- > 0)
	    continue;
	free(item);
	return ob;
    }
    free(item);
    return 0;
}

/* KLUDGE KLUDGE KLUDGE KLUDGE KLUDGE      */
/* ALL THIS TO AVOID 1 BYTE PER OBJECT     */
/* not used very often however -- Raistlin */

struct destructor_stack
{
   struct object *element;
   struct destructor_stack *next;
} *d_stack = NULL;

static struct object *top_of_stack() 
{ 
   if (!d_stack) 
      return NULL; 
   else 
      return d_stack->element; 
}

/* cleans up things, just in case of errors... */
static void clean_stack()
{
   struct destructor_stack *current, *previous=NULL;

   current = d_stack;
   while(current)
   {
      struct destructor_stack *next;

      next = current->next;
      if (current->element->flags&O_DESTRUCTED)
      {
         if (!previous)  d_stack = current->next;
         else previous->next = current->next;
         free((char *)current);
      }
      else 
         previous = current;
      current = next;
   }
}

static void push_destructor(ob)
struct object *ob;
{
   struct destructor_stack *new;
   new = (struct destructor_stack *) xalloc(sizeof(struct destructor_stack));
   new->element = ob;
   new->next = NULL;
   if (d_stack)
      new->next = d_stack;
   d_stack = new;
   clean_stack();   
}

static struct object *pop_destructor()
{
   struct destructor_stack *popped;
   struct object *ob;
   popped = d_stack;
   d_stack = d_stack->next;
   ob = popped->element;
   free((char *)popped);
   clean_stack();
   return ob;
}

static int test_stack(ob)
struct object *ob;
{
   struct destructor_stack *current = d_stack;
   while(current)
   {
      if (current->element == ob) return 1;
      current = current->next;
   }
   return 0;
}

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

    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->flags & O_DESTRUCTED)
	return;
    if (ob->flags & O_SWAPPED)
        load_ob_from_swap(ob);
    remove_object_from_stack(ob);
/* 
 * Added 4/26/91 -- Raistlin.  destructor() is called on destructed objects 
 *
 * This means that the object is already in the process of being destructed 
 * and another destructor (or it's own) tried to destruct it. 
 * The "really" flag means "Don't call the destructor", in case of error 
 */
    if (!really && test_stack(ob)) return;
    if (!really && function_exists("destructor", ob))
    {
       push_destructor(ob);
       (void)apply("destructor", ob, 0);
       pop_destructor(); 
    } 

    /*
     * If this is the first object being shadowed by another object, then
     * destruct the whole list of shadows.
     */
    if (ob->shadowed && !ob->shadowing) {
	struct svalue svp;
	struct object *ob2;

	svp.type = T_OBJECT;
	for (ob2 = ob->shadowed; ob2; ) {
	    svp.u.ob = ob2;
	    ob2 = ob2->shadowed;
	    svp.u.ob->shadowed = 0;
	    svp.u.ob->shadowing = 0;
	    destruct_object(&svp, 0);
	}
    }
    /*
     * The chain of shadows is a double linked list. Take care to update
     * it correctly.
     */
    if (ob->shadowed)
	ob->shadowed->shadowing = ob->shadowing;
    if (ob->shadowing)
	ob->shadowing->shadowed = ob->shadowed;
    ob->shadowed = ob->shadowing = 0;

    if (d_flag > 1)
	debug_message("Destruct object %s (ref %d)\n", ob->name, ob->ref);
    super = ob->super;
#ifdef COMPAT_MODE
    if (super) {
	struct svalue *weight;
	/* Call exit in current room, if player or npc not in mudlib 3.0 */
	if((ob->flags & O_ENABLE_COMMANDS)) {
	    push_object(ob, "destruct_object");
	    (void)apply("exit",super,1);
	}
	weight = apply("query_weight", ob, 0);
	if (weight && weight->type == T_NUMBER) {
	    push_number(-weight->u.number);
	    (void)apply("add_weight", super, 1);
	}
    }
#endif
    if (super == 0)
    {
	struct svalue svp;
	svp.type = T_OBJECT;
	while(ob->contains) {
	    svp.u.ob = ob->contains;
	    push_object(ob->contains, "destruct");
	    /*
	     * An errer here will not leave destruct() in an inconsistant
             * stage.
	     */
	    apply_master_ob("destruct_environment_of", 1);
	    if (svp.u.ob == ob->contains)
	       destruct_object(&svp, 0);
	}
    }
    else
        while(ob->contains)
	    move_or_destruct(ob->contains, super);
    if ( ob->interactive ) {
	struct object *save=command_giver;

	command_giver=ob;
	if (ob->interactive->ed_buffer) {
	    extern void save_ed_buffer();

	    save_ed_buffer();
	}
	flush_all_player_mess();
	command_giver=save;
    }
    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);
	add_light(ob->super, - ob->total_light);
	for (pp = &ob->super->contains; *pp;) {
	    if ((*pp)->flags & O_ENABLE_COMMANDS)
		remove_sent(ob, *pp);
	    if (*pp != ob)
		pp = &(*pp)->next_inv;
	    else
		*pp = (*pp)->next_inv;
	}
    }
    /*
     * Now remove us out of the list of all objects.
     * This must be done last, because an error in the above code would
     * halt execution.
     */
    removed = 0;
    /*
     * If any of the calls above (move_or_destruct) caused a call 
     * to the efun destruct, then this object will already be destructed
     * and removed from the list, so everything after this for loop will
     * already have been done, so we can just return here.
     */
    if (ob->flags & O_DESTRUCTED)
        return;
    else
       for (pp = &obj_list; *pp; pp = &(*pp)->next_all) {
	if (*pp != ob)
	    continue;
	*pp = (*pp)->next_all;
	removed = 1;
	remove_object_hash(ob);
	break;
    }
    if (!removed)
        fatal("Failed to delete object.\n"); 
    if (ob->living_name)
	remove_living_name(ob);
    ob->super = 0;
    ob->next_inv = 0;
    ob->contains = 0;
    ob->flags &= ~O_ENABLE_COMMANDS;
    ob->next_all = obj_list_destruct;
    obj_list_destruct = ob;
    ob->flags |= O_DESTRUCTED;
}

/*
 * This one is called when no program is executing.
 * The super pointer is still maintained.
 */
void destruct2(ob)
    struct object *ob;
{
    if (d_flag > 1) {
	debug_message("Destruct-2 object %s (ref %d)\n", ob->name, ob->ref);
    }
    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->prog->num_variables > 0) {
	/*
	 * Deallocate variables in this object.
	 * The space of the variables are not deallocated until
	 * the object structure is freed in free_object().
	 */
	int i;
	for (i=0; i<ob->prog->num_variables; i++) {
	    free_svalue(&ob->variables[i], "destruct_object");
	    ob->variables[i].type = T_NUMBER;
	    ob->variables[i].u.number = 0;
	}
    }
    free_object(ob, "destruct_object");
}

#ifdef F_CREATE_WIZARD
/*
 * This is the efun create_wizard(). Create a home dir for a wizard,
 * and other files he may want.
 *
 * The real job is done by the master.c object, so the call could as well
 * have been done to it directly. But, this function remains for
 * compatibility.
 *
 * It should be replaced by a function in simul_efun.c !
 */
char *create_wizard(owner, domain)
    char *owner;
    char *domain;
{
    struct svalue *ret;
#if 0
    struct stat st;
    char cmd[200], lbuf[128];
    static char name[200], name2[200];
    FILE *f;
    struct object *owner_obj;
#endif

    /* 
     * Let the master object do the job.
     */
    push_constant_string(owner);
    push_constant_string(domain);
    push_object(current_object, "create_wizard");
    ret = apply_master_ob("master_create_wizard", 3);
    if (ret && ret->type == T_STRING)
        return ret->u.string;
    return 0;
#if 0
    fprintf(stderr, "c_w called with %s and %s from %s\n",
	owner, domain ? domain : "without domain", current_object->name);
    /*
     * Verify that it is a valid call of create_wizard(). This is done
     * by a function in master.c. It will take the calling object as
     * argument, and must return a non-zero value.
     */
    push_object(current_object, "create_wizard");
    ret = apply_master_ob("verify_create_wizard", 1);
    if (ret == 0)
	error("No wizards allowed ! (because no verify_create_wizard() in master.c)\n");
    if (ret->type == T_NUMBER && ret->u.number == 0)
	error("Illegal use of create_wizard() !\n");

    /*
     * Even if the object that called create_wizard() is trusted, we won't
     * allow it to use funny names for the owner.
     */
    if (!legal_path(owner))
	error("Bad name to create_wizard: %s\n", owner);
    owner_obj = find_living_object(owner, 1);
    if (owner_obj == 0) {
	fprintf(stderr,
		"create_wizard: Could not find living object %s.\n", owner);
	return 0;
    }
    (void)sprintf(name, "%s/%s", PLAYER_DIR, owner);
    if(domain) {
	(void)sprintf(name2, "%s/%s/%s", DOMAIN_DIR, domain, owner);
	fprintf(stderr, "name = %s, name2 =  %s\n", name, name2);
	if (stat(name, &st) == 0) {
	    if((st.st_mode & S_IFMT) == S_IFDIR) {
		rename(name, name2);
	    }
	} else {
	    if (mkdir(name2, 0777) == -1) {
		perror(name);
		error("Could not mkdir %s\n", name2);
	    }
	}
    } else {
	if (stat(name, &st) == 0)
		error("Player %s already has a castle!\n", owner);

	else
	    if (mkdir(name, 0777) == -1) {
		perror(name);
		error("Could not mkdir %s\n", name);
	    }
    }

    /* add castle */
    if(domain) {
	(void)sprintf(name, "%s/%s/common/domain.c", DOMAIN_DIR, domain);
    } else {
	(void)sprintf(name, "%s/%s/%s", PLAYER_DIR, owner, "castle.c");
    }
    if(stat(name, &st) == 0) {
	fprintf(stderr, "castle file %s already exists.\n", name);
    } else {
	f = fopen(name, "w");
	if (f == NULL)
	    error("Could not create a castle file %s!\n", name);
	(void)fprintf(f, "#define NAME \"%s\"\n", domain ? domain : owner);
#ifdef CASTLE_ROOM
	(void)fprintf(f, "#define DEST \"%s\"\n", CASTLE_ROOM);
#else
        (void)fprintf(f, "#define DEST \"%s\"\n", current_object->super->name);
#endif
	(void)fclose(f);
	(void)sprintf(cmd, "cat %s >> %s", DEFAULT_CASTLE, name);
	(void)system(cmd);
    }

#ifdef COMPAT_MODE
    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);
#endif
    return name;
#endif
}
#endif /* F_CREATE_WIZARD */

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

#if 0
void say(v, avoid_ob)
    struct svalue *v;
    struct object *avoid_ob;
{
    struct object *ob, *save_command_giver = command_giver;
    struct object *origin;
    char buff[257];

    if (current_object->flags & O_ENABLE_COMMANDS)
	command_giver = current_object;
    else if (current_object->shadowing)
	command_giver = current_object->shadowing;
    if (command_giver)
	origin = command_giver;
    else
	origin = current_object;
    switch(v->type) {
    case T_STRING:
	strncpy(buff, v->u.string, sizeof buff);
	buff[sizeof buff - 1] = '\0';
	break;
    case T_OBJECT:
	strncpy(buff, v->u.ob->name, sizeof buff);
	buff[sizeof buff - 1] = '\0';
	break;
    case T_NUMBER:
	sprintf(buff, "%d", v->u.number);
	break;
    default:
	error("Invalid argument %d to say()\n", v->type);
    }
    buff[sizeof buff - 1] = 0;
    for (ob = origin->contains; ob; ob = ob->next_inv) 
    {
	struct object *save_again;
	if (ob->interactive == 0) 
        {
	    if ((ob->flags & O_ENABLE_COMMANDS) && ob != command_giver &&
		ob != avoid_ob)
		tell_npc(ob, buff);
	    if (ob->flags & O_DESTRUCTED)
		break;
	    continue;
	}
	if (ob == save_command_giver || ob == avoid_ob)
	    continue;
	save_again = command_giver;
	command_giver = ob;
	add_message("%s", buff);
	command_giver = save_again;
    }
    if (origin->super) 
    {
	if (origin->super->interactive && origin != command_giver &&
		origin->super != avoid_ob) 
        {
	    command_giver = origin->super;
	    add_message("%s", buff);
	} 
        else if (origin->super->interactive == 0 && 
                 origin->super != avoid_ob &&
		(origin->super->flags & O_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->flags & O_ENABLE_COMMANDS) && ob != command_giver)
		    tell_npc(ob, buff);
		if (ob->interactive)
		    break;
		continue;
	    }
	    if (ob == command_giver)
		continue;
	    save_again = command_giver;
	    command_giver = ob;
	    add_message("%s", buff);
	    command_giver = save_again;
	}
    }
    command_giver = save_command_giver;
}
#else

void say(v, avoid)
    struct svalue *v;
    struct vector *avoid;
{
    extern struct vector *order_alist PROT((struct vector *));
    struct vector *vtmpp;
    static struct vector vtmp = { 1, 1,
#ifdef DEBUG
	1,
#endif
	(struct wiz_list *)NULL,
	{ { T_POINTER } }
	};

    extern int assoc PROT((struct svalue *key, struct vector *));
    struct object *ob, *save_command_giver = command_giver;
    struct object *origin;
    char buff[256];
#define INITIAL_MAX_RECIPIENTS 50
    int max_recipients = INITIAL_MAX_RECIPIENTS;
    struct object *first_recipients[INITIAL_MAX_RECIPIENTS];
    struct object **recipients = first_recipients;
    struct object **curr_recipient = first_recipients;
    struct object **last_recipients =
	&first_recipients[INITIAL_MAX_RECIPIENTS-1];

    struct object *save_again;
    static struct svalue stmp = { T_OBJECT };

    if (current_object->flags & O_ENABLE_COMMANDS)
	command_giver = current_object;
    else if (current_object->shadowing)
	command_giver = current_object->shadowing;
    if (command_giver) {
	origin = command_giver;
        if (avoid->item[0].type == T_NUMBER) {
            avoid->item[0].type = T_OBJECT;
            avoid->item[0].u.ob = command_giver;
            add_ref(command_giver, "ass to var");
        }
    } else
	origin = current_object;
    vtmp.item[0].u.vec = avoid;
    vtmpp = order_alist(&vtmp);
    avoid = vtmpp->item[0].u.vec;
    if (ob = origin->super) {
	if (ob->flags & O_ENABLE_COMMANDS || ob->interactive) {
	    *curr_recipient++ = ob;
	}
	for (ob = origin->super->contains; ob; ob = ob->next_inv) {
            if (ob->flags & O_ENABLE_COMMANDS || ob->interactive) {
                if (curr_recipient >= last_recipients) {
                    max_recipients <<= 1;
                    curr_recipient = (struct object **)
		      alloca(max_recipients * sizeof(struct object *));
                    memcpy((char*)curr_recipient, (char*)recipients,
                      max_recipients * sizeof(struct object *)>>1);
                    recipients = curr_recipient;
                    last_recipients = &recipients[max_recipients-1];
		    curr_recipient += (max_recipients>>1) - 1;
                }
                *curr_recipient++ = ob;
            }
	}
    }
    for (ob = origin->contains; ob; ob = ob->next_inv) {
	if (ob->flags & O_ENABLE_COMMANDS || ob->interactive) {
	    if (curr_recipient >= last_recipients) {
		max_recipients <<= 1;
		curr_recipient = (struct object **)alloca(max_recipients);
		memcpy((char*)curr_recipient, (char*)recipients,
		  max_recipients * sizeof(struct object *)>>1);
		recipients = curr_recipient;
		last_recipients = &recipients[max_recipients-1];
		curr_recipient += (max_recipients>>1) - 1;
	    }
	    *curr_recipient++ = ob;
	}
    }
    *curr_recipient = (struct object *)0;
    switch(v->type) {
    case T_STRING:
	strncpy(buff, v->u.string, sizeof buff);
	buff[sizeof buff - 1] = '\0';
	break;
    case T_OBJECT:
	strncpy(buff, v->u.ob->name, sizeof buff);
	buff[sizeof buff - 1] = '\0';
	break;
    case T_NUMBER:
	sprintf(buff, "%d", v->u.number);
	break;
    case T_POINTER:
	for (curr_recipient = recipients; ob = *curr_recipient++; ) {
	    extern void push_vector PROT((struct vector *));

	    if (ob->flags & O_DESTRUCTED) continue;
	    stmp.u.ob = ob;
	    if (assoc(&stmp, avoid) >= 0) continue;
	    push_vector(v->u.vec);
	    push_object(command_giver, "say");
	    apply("catch_msg", ob, 2);
	}
	break;
    default:
	error("Invalid argument %d to say()\n", v->type);
    }
    save_again = command_giver;
    for (curr_recipient = recipients; ob = *curr_recipient++; ) {
        if (ob->flags & O_DESTRUCTED) continue;
	stmp.u.ob = ob;
	if (assoc(&stmp, avoid) >= 0) continue;
	if (ob->interactive == 0) {
	    tell_npc(ob, buff);
	    continue;
	}
	command_giver = ob;
	add_message("%s", buff);
	command_giver = save_again;
    }
    free_vector(vtmpp);
    command_giver = save_command_giver;
}
#endif

/*
 * Send a message to all objects inside an object.
 * Non interactive objects gets no messages.
 * Compare with say().
 */

void tell_room(room, v, avoid)
    struct object *room;
    struct svalue *v;
    struct vector *avoid; /* has to be in alist order */
{
    struct object *ob, *save_command_giver = command_giver;
    char buff[257];

    switch(v->type) {
    case T_STRING:
	strncpy(buff, v->u.string, sizeof buff);
	buff[sizeof buff - 1] = '\0';
	break;
    case T_OBJECT:
	strncpy(buff, v->u.ob->name, sizeof buff);
	buff[sizeof buff - 1] = '\0';
	break;
    case T_NUMBER:
	sprintf(buff, "%d", v->u.number);
	break;
    default:
	error("Invalid argument %d to tell_room()\n", v->type);
    }
    buff[sizeof buff - 1] = 0;
    for (ob = room->contains; ob; ob = ob->next_inv) {
        int assoc PROT((struct svalue *key, struct vector *));
	static struct svalue stmp = { T_OBJECT, } ;

	stmp.u.ob = ob;
	if (assoc(&stmp, avoid) >= 0) continue;
	if (ob->interactive == 0) {
	    if (ob->flags & O_ENABLE_COMMANDS) {
		/*
		 * We want the monster code to have a correct this_player()
		 */
		command_giver = save_command_giver;
		tell_npc(ob, buff);
	    }
	    if (ob->flags & O_DESTRUCTED)
		break;
	    continue;
	}
	command_giver = ob;
	add_message("%s", buff);
    }
    command_giver = save_command_giver;
}

void shout_string(str)
    char *str;
{
    struct object *ob, *save_command_giver = command_giver;
    int emergency = 0, i;
    extern int num_player;
#ifdef LOG_SHOUT
    FILE *f = 0;
#endif
    char *p;

    if (str[0] == '!') { emergency = 1; }
    str = string_copy(str+emergency);

#ifdef LOG_SHOUT
    if (command_giver) {
	struct svalue *v = 0;
	v = apply("query_real_name", command_giver, 0);
	if (v && v->type == T_STRING)
	    p = v->u.string;
    } else if (current_object && current_object->user)
	p = current_object->user->name;
    if (p)
	f = fopen("log/SHOUTS", "a");
    if (f) {
	fprintf(f, "%s: %s\n", p, str);
	fclose(f);
    }
#endif
#if 0
    for (ob = obj_list; ob; ob = ob->next_all) {
        struct svalue *ret;
	if (!ob->interactive || ob == save_command_giver || !ob->super)
	    continue;
        push_object(ob, "shout_string");
        ret = apply_master_ob("valid_shout", 1);
        if (!emergency && (!ret || ret->type != T_NUMBER || ret->u.number == 0))
            continue;
	command_giver = ob;
	add_message("%s", str);
    }
#endif
    for (i = 0; i < num_player; ++i)
    {
       struct svalue *ret;
      
       ob = get_interactive_object(i);
       if (!ob->interactive || ob == save_command_giver || !ob->super)
          continue;

       ret = 0;
       if (!emergency)
       {
          push_object(ob, "shout_string");
          ret = apply_master_ob("valid_shout", 1);
          if (ret && ret->type == T_NUMBER && ret->u.number == 0)
             continue;
       }
       command_giver = ob;
       add_message("%s", str);
    } 
    command_giver = save_command_giver;
    free(str);
}

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

    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;
    return ob->contains;
}

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

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

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

    if (!command_giver || command_giver->flags & O_DESTRUCTED)
	return 0;
    s = alloc_sentence();
    if (set_call(command_giver, s, flag)) {
	s->function = make_shared_string(fun);
	s->ob = current_object;
	add_ref(current_object, "input_to");
	return 1;
    }
    free_sentence(s);
    return 0;
}

#define MAX_LINES 50
#define MAXLSCOLUMNS 6

struct entry { 
   int len;
   int size;
   char file[34];
};

int gstrcmp(p1, p2)
    char **p1, **p2;
{
    return strcmp(((struct svalue *)p1)->u.string, 
                  ((struct svalue *)p2)->u.string);
}
int pstrcmp(p1, p2)
    char **p1, **p2;
{
    return strcmp(((struct entry *)p1)->file,((struct entry *)p2)->file);
}

/*
 * List files in a directory. The standard 'ls' could be used, but
 * it takes too much time.
 * Prepared for flag decoding.
 *
 * Look at the the last component of the path name. It is a regular
 * expression, select only matching files.
 *
 * All .i files and '.' and '..' are only listed if they match the regexp.
 */

void list_files(path)
char *path;
{
   DIR *dirp;
#if defined(M_UNIX) || defined(_AIX)
   struct dirent *de;
#else
   struct direct *de;
#endif
   struct stat st;
   char regexp[80], *p, path2[150];
   int do_match = 0;
   struct entry name[MAXLSCOLUMNS*MAX_LINES];
/*
   struct entry *nbuf;
*/
   int colwidth[MAXLSCOLUMNS];
   char *fname;
   int num, i, j, k, ncols, npercol, max, total, size;
   
   if (!path)
      path = ".";
#ifdef COMPAT_MODE
   path = check_file_name(path, 0);
#else
   path = check_valid_path(path, current_object->eff_user,
			      "list_files", 0);
#endif
   if (path  ==  0)
      return;
   strncpy(path2, path, sizeof path2);
   path2[sizeof path2 - 1] = '\0';
   if(path2[0]==0) 
     strcpy(path2, "./");
   p = path2 + strlen(path2) - 2;
   if (p  >=  path2 && p[0]  ==  '/' && p[1]  ==  '.')
      p[0] = '\0';
   if (stat(path2, &st)  ==  -1) 
   {
      /* Either the directory does not exist, or it is a regexp
       * file name. Strip of the last component.
       */
      p = strrchr(path2, '/');
      if (p  ==  0 || p[1]  ==  '\0')
	 return;
      strncpy(regexp, p+1, sizeof regexp);
      regexp[sizeof regexp - 1] = '\0';
      *p = '\0';
      do_match = 1;
   }
   dirp = opendir(path2);
   if (dirp  ==  0) 
   {
      add_message("No such directory '%s'\n", path2);
      return;
   }
   num = 0;
   total = 0;
   if (path[0]) strcat(path2,"/");
   i = strlen(path2);
   fname = &path2[i];
   i = sizeof(path2) - i - 1;
/*
   nbuf = (struct entry *)xalloc((sizeof(struct entry))*MAXLSCOLUMNS*MAX_LINES);
   
   for (j=0; j<MAXLSCOLUMNS*MAX_LINES; j++)
      name[j] = nbuf+j;
*/
   
   for(de = readdir(dirp); (de && num<(MAXLSCOLUMNS*MAX_LINES)); 
       de = readdir(dirp)) 
   {
      if (!do_match && (strcmp(de-> d_name, ".")  ==  0 ||
			strcmp(de-> d_name, "..")  ==  0))
	 continue;
      if (do_match && !match_string(regexp, de-> d_name))
	 continue;
      strncpy(fname, de-> d_name, i);
      j = 0;
      if (stat(path2, &st) != -1) 
      {
	 size = (int)st.st_size / 1024 + ((int)st.st_size % 1024  >  0);
	 total += size;
	 if (S_IFDIR & st.st_mode) j = 1;
      }
      else size=0;
      strncpy(name[num].file, de->d_name, sizeof(name[num].file));
      name[num].file[sizeof(name[num].file)-1]=0;
#ifdef M_UNIX
      name[num].len = strlen(de->d_name)+j;
#else
      name[num].len = (int) (de-> d_namlen+j);
#endif
      name[num].size = (int) size;
      if (j)
	 strcat(name[num].file,"/");
      num++;
   }
   closedir(dirp);
   
   if (num == 0) 
   {
      add_message(do_match?"No matching files.\n":"No files.\n");
/*
      free(nbuf);
*/
      return;
   }
   
   /* Sort the names. */
   qsort((char *)name, num, sizeof (struct entry), pstrcmp);
   
   ncols = num/(num/MAXLSCOLUMNS+(num%MAXLSCOLUMNS > 0))+
           ((num%(num/MAXLSCOLUMNS+(num%MAXLSCOLUMNS > 0))) > 0);
   while (1) 
   {
      npercol = num / ncols + (num % ncols  >  0);		
      for (j=0; j<ncols; j++) 
      {
	 max = 0;
	 for (i=j*npercol; i<(j+1)*npercol && i<num; i++) 
	    if (name[i].len  >  max)
	       max=name[i].len;
	 colwidth[j] = max+6;
      }
      for (i=0, max=0; i<ncols; i++)
	 max+=colwidth[i];
      if (max<79)
	 break;
      ncols--;
      if (ncols<1) 
      {
	 ncols=1;
	 npercol=num;
	 break;
      }
   }
   
   for (i=1; i < ncols; i++)
      colwidth[i]+=colwidth[i-1];
   
   add_message("Total %d\n", total);
   
   max = 0; i = 0; j = 0;
   strcpy(path2,
	  "                                                                                ");
   while (max < num) 
   {
      k = (i * npercol) + j;
      if (i >= ncols) 
      {
	 j++; i=0;
	 add_message("%s\n", path2);
	 strcpy(path2,
		"                                                                                ");
	 continue;
      }			
      if (k < num) 
      {
	 if (name[k].size > 9999)
	    sprintf(&path2[(i > 0 ? colwidth[i - 1] : 0)],
		    "BIG! %s ", name[k].file);
	 else
	    sprintf(&path2[(i > 0 ? colwidth[i-1] : 0)],
		    "%4d %s ",
		    name[k].size,
		    name[k].file);
			if ((i + 1) * npercol + j < num)
			   path2[(i > 0 ? colwidth[i - 1] : 0) +
				 6 + name[k].len]=' ';
	 max++;
      }
      i++;	
   }
   add_message("%s\n", path2);
   if (num == MAXLSCOLUMNS * MAX_LINES)
      add_message("***Too many files, listing truncated***\n");
/*
   free(nbuf);
*/
}

int fstrcmp(a, b)
char **a, **b;
{
   return strcmp((char *)a, (char *)b);
}

struct vector *get_files(path)
char *path;
{
   DIR *dirp;
#if defined(M_UNIX) || defined(_AIX)
   struct dirent *de;
#else
   struct direct *de;
#endif
   struct stat st;
   char regexp[80], *p, path2[150], *fname;
   char name[MAX_ARRAY_SIZE][34];/*dunno where 34 comes from, but they use it */
   int do_match = 0;
   int num, i, j;
   struct vector *ret;
   
   if (!path)
      path = ".";
#ifdef COMPAT_MODE
   path = check_file_name(path, 0);
#else
   path = check_valid_path(path, current_object->eff_user, 
			      "files", 0);
#endif
   if (path  ==  0)
      return 0;
   strncpy(path2, path, sizeof path2);
   path2[sizeof path2 - 1] = '\0';
   p = path2 + strlen(path2) - 2;
   if (p  >=  path2 && p[0]  ==  '/' && p[1]  ==  '.')
      p[0] = '\0';
   if (stat(path2, &st)  ==  -1) 
   {
      /* Either the directory does not exist, or it is a regexp
       * file name. Strip of the last component.
       */
      p = strrchr(path2, '/');
      if (p  ==  0 || p[1]  ==  '\0')
	 return 0;
      strncpy(regexp, p+1, sizeof regexp);
      regexp[sizeof regexp - 1] = '\0';
      *p = '\0';
      do_match = 1;
   }
   dirp = opendir(path2);
   if (dirp  ==  0) 
      return 0;
   num = 0;
   if (path[0]) strcat(path2,"/");
   i = strlen(path2);
   fname = path2+i;
   i = sizeof(path2) - i - 1;
   
   for(de = readdir(dirp); de; de = readdir(dirp)) 
   {
      if (!do_match && (strcmp(de-> d_name, ".")  ==  0 ||
			strcmp(de-> d_name, "..")  ==  0))
	 continue;
      if (do_match && !match_string(regexp, de-> d_name))
	 continue;
      j = 0;
      strncpy(fname, de-> d_name, i);
      if (stat(path2, &st) != -1) 
         if (S_IFDIR & st.st_mode) j = 1;
      strncpy(name[num], de->d_name, sizeof name[num]);
      name[num][sizeof(name[num])-1] = 0;
      if (j)
	 strcat(name[num],"/");
      num++;
   }
   closedir(dirp);
   
   /* Sort the names. */
   if (num)
      qsort((char *)name, num, sizeof name[0], fstrcmp);
   
   ret = allocate_array(num);
   for (i=0; i<num; ++i)
   {
      ret->item[i].type = T_STRING;
      ret->item[i].u.string = make_shared_string(name[i]);
      ret->item[i].string_type = STRING_SHARED;
   }
   return ret;
} 

/*
 * List files in directory. This function do same as standard list_files
 * but instead writing files right away to player this returns an array
 * containing those files. Actually most of code is copied from list_files()
 * function.
 * Differenceies with list_files:
 *
 *   - file_list("/w"); returns ({ "w" })
 *
 *   - file_list("/w/"); and file_list("/w/."); return contents of directory
 *     "/w"
 *
 *   - file_list("/");, file_list("."); and file_list("/."); return contents
 *     of directory "/"
 */
struct vector *get_dir(path)
    char *path;
{
    struct vector *v;
    int i, count = 0;
    DIR *dirp;
    int namelen, do_match = 0;
#if defined(_AIX) || defined(M_UNIX)
    struct dirent *de;
#else
    struct direct *de;
#endif
    struct stat st;
    char *temppath;
    char *p;
    char *regexp = 0;

    if (!path)
	return 0;

#ifdef COMPAT_MODE
    path = check_file_name(path, 0);
#else
    path = check_valid_path(path, current_object->eff_user, "get_dir", 0);
#endif

    if (path == 0)
	return 0;

    /*
     * We need to modify the returned path, and thus to make a
     * writeable copy.
     * The path "" needs 2 bytes to store ".\0".
     */
    temppath = (char *)alloca(strlen(path)+2);
    if (strlen(path)<2) {
	temppath[0]=path[0]?path[0]:'.';
	temppath[1]='\000';
	p = temppath;
    } else {
	strcpy(temppath, path);
	/*
	 * If path ends with '/' or "/." remove it
	 */
	if ((p = strrchr(temppath, '/')) == 0)
	    p = temppath;
	if (p[0] == '/' && p[1] == '.' && p[2] == '\0' || 
	    p[0] == '/' && p[1] == '\0')
	    *p = '\0';
    }

    if (stat(temppath, &st) < 0) {
	if (*p == '\0')
	    return 0;
	regexp = (char *)alloca(strlen(p)+2);
	if (p != temppath) {
	    strcpy(regexp, p + 1);
	    *p = '\0';
	} else {
	    strcpy(regexp, p);
	    strcpy(temppath, ".");
	}
	do_match = 1;
    } else if (*p != '\0' && strcmp(temppath, ".")) {
	if (*p == '/' && *(p + 1) != '\0')
	    p++;
	v = allocate_array(1);
	v->item[0].type = T_STRING;
	v->item[0].string_type = STRING_MALLOC;
	v->item[0].u.string = string_copy(p);
	return v;
    }

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

    /*
     *  Count files
     */
    for (de = readdir(dirp); de; de = readdir(dirp)) {
#ifdef M_UNIX
	namelen = strlen(de->d_name);
#else
	namelen = de->d_namlen;
#endif
	if (!do_match && (strcmp(de->d_name, ".") == 0 ||
			  strcmp(de->d_name, "..") == 0))
	    continue;
	if (do_match && !match_string(regexp, de->d_name))
	    continue;
	count++;
	if ( count >= MAX_ARRAY_SIZE)
	    break;
    }
    /*
     * Make array and put files on it.
     */
    v = allocate_array(count);
    if (count == 0) {
	/* This is the easy case :-) */
	closedir(dirp);
	return v;
    }
    rewinddir(dirp);
    for(i = 0, de = readdir(dirp); i < count; de = readdir(dirp)) {
#ifdef M_UNIX
        namelen = strlen(de->d_name);
#else
	namelen = de->d_namlen;
#endif
	if (!do_match && (strcmp(de->d_name, ".") == 0 ||
			  strcmp(de->d_name, "..") == 0))
	    continue;
	if (do_match && !match_string(regexp, de->d_name))
	    continue;
	de->d_name[namelen] = '\0';
	v->item[i].type = T_STRING;
	v->item[i].string_type = STRING_MALLOC;
	v->item[i].u.string = string_copy(de->d_name);
	i++;
    }
    closedir(dirp);
    /* Sort the names. */
    qsort((char *)v->item, count, sizeof v->item[0], gstrcmp);
    return v;
}

int tail(path)
    char *path;
{
    char buff[1000];
    FILE *f;
    struct stat st;
    int offset;

#ifdef COMPAT_MODE
    path = check_file_name(path, 0);
#else
    path = check_valid_path(path, current_object->eff_user, "tail", 0);
#endif
    if (path == 0)
        return 0;
    f = fopen(path, "r");
    if (f == 0)
	return 0;
    if (fstat(fileno(f), &st) == -1)
	error("Could not stat an open file.\n");
    offset = st.st_size - 54 * 20;
    if (offset < 0)
	offset = 0;
    if (fseek(f, offset, 0) == -1)
	fatal("Could not seek.\n");
    /* Throw away the first incomplete line. */
    if (offset > 0)
	(void)fgets(buff, sizeof buff, f);
    while(fgets(buff, sizeof buff, f)) {
	add_message("%s", buff);
    }
    fclose(f);
    return 1;
}


int print_file(path, start, len)
    char *path;
    int start, len;
{
    char buff[1000];
    FILE *f;
    int i, num_read = 0;;

    if (len < 0)
	return -1;
#ifdef COMPAT_MODE
    path = check_file_name(path, 0);
#else
    path = check_valid_path(path, current_object->eff_user, 
                "print_file", 0);
#endif
    if (path == 0)
        return -1;
    f = fopen(path, "r");
    if (f == 0)
	return -1;
    if (start < 0) {
	int lines = 0;

	while (fgets(buff, sizeof buff, f) != 0)
	    lines++;
	rewind(f);
	start = lines - len + 1;
    }
    for (i = 1; i < start; i++) {
	if (fgets(buff, sizeof buff, f) == 0)
	    break;
    }
    if (!len || len > MAX_LINES)
	len = MAX_LINES;
    for (i = 0; i < len; i++) {
	if (fgets(buff, sizeof buff, f) == 0)
	    break;
        buff[sizeof buff - 1] = 0;
	num_read++;
	add_message("%s", buff);
    }
    if (i == MAX_LINES)
	add_message("*****TRUNCATED*****\n");
    fclose(f);
    return num_read;
}

int remove_file(path)
    char *path;
{
#ifdef COMPAT_MODE
    path = check_file_name(path, 1);
#else
    path = check_valid_path(path, current_object->eff_user,
                  "remove_file", 1);
#endif
    if (path == 0)
        return 0;
    if (unlink(path) == -1)
       return 0;
    return 1;
}

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

    sprintf(file_name, "/log/%s", file);
#ifdef COMPAT_MODE
    if (strchr(file, '/') || file[0] == '.' || strlen(file) > 30
#ifdef MSDOS
        || !valid_msdos(file)
#endif
        )
  	    error("Illegal file name to log_file(%s)\n", file);
#else
	if (!check_valid_path(file_name, current_object->eff_user, 
            "log_file", 1))
  	    return;
#endif
    if (stat(file_name+1, &st) != -1 && st.st_size > MAX_LOG_SIZE) {
  	char file_name2[sizeof file_name + 4];
	sprintf(file_name2, "%s.old", file_name+1);
	rename(file_name+1, file_name2);	/* No panic if failure */
    }
    f = fopen(file_name+1, "a");	/* Skip leading '/' */
    if (f == 0)
	return;
    fwrite(str, strlen(str), 1, f);
    fclose(f);
}

void
print_svalue(arg)
    struct svalue *arg;
{
    if (arg == 0)
	add_message("<NULL>");
    else if (arg->type == T_STRING) {
	if (strlen(arg->u.string) > 9500)	/* Not pretty */
	    error("Too long string.\n");
	/* Strings sent to monsters are now delivered */
	if (command_giver && (command_giver->flags & O_ENABLE_COMMANDS) &&
	          !command_giver->interactive)
	    tell_npc(command_giver, arg->u.string);
	else
	    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 if (arg->type == T_POINTER)
	add_message("<ARRAY>");
    else
	add_message("<UNKNOWN>");
}

void do_write(arg)
    struct svalue *arg;
{
    struct object *save_command_giver = command_giver;
    if (command_giver == 0 && current_object->shadowing)
       command_giver = current_object;
    if (command_giver)
	while (command_giver->shadowing)
	    command_giver = command_giver->shadowing;
    print_svalue(arg);
    command_giver = save_command_giver;
}

/* Find an object. If not loaded, load it ! */

struct object *find_object(str)
    char *str;
{
    struct object *ob;

    /* Remove leading '/' if any. */
    while(str[0] == '/')
	str++;
    ob = find_object2(str);
    if (ob)
	return ob;
    ob = load_object(str, 0);
    if (ob->flags & O_DESTRUCTED)		/* *sigh* */
       return 0;
    if (ob && ob->flags & O_SWAPPED)
	load_ob_from_swap(ob);
    return ob;
}

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

    /* Remove leading '/' if any. */
    while(str[0] == '/')
	str++;
    /* Truncate possible .c in the object name. */
    length = strlen(str);
    if (str[length-2] == '.' && str[length-1] == 'c') {
	/* A new writreable copy of the name is needed. */
	char *p;
	p = (char *)alloca(strlen(str)+1);
	strcpy(p, str);
	str = p;
	str[length-2] = '\0';
    }
    if (ob = lookup_object_hash(str)) {
	if (ob->flags & O_SWAPPED)
	    load_ob_from_swap(ob);
	return ob;
    }
    return 0;
}

#if 0

void apply_command(com)
    char *com;
{
    struct value *ret;

    if (command_giver == 0)
	error("command_giver == 0 !\n");
    ret = apply(com, command_giver->super, 0);
    if (ret != 0) {
	add_message("Result:");
	if (ret->type == T_STRING)
	    add_message("%s\n", ret->u.string);
	if (ret->type == T_NUMBER)
	    add_message("%d\n", ret->u.number);
    } else {
	add_message("Error apply_command: function %s not found.\n", com);
    }
}
#endif /* 0 */

/*
 * Transfer an object.
 * The object has to be taken from one inventory list and added to another.
 * The main work is to update all command definitions, depending on what is
 * living or not. Note that all objects in the same inventory are affected.
 */
void move_object(item, dest)
    struct object *item, *dest;
{
    struct object **pp, *ob, *next_ob;
    struct object *save_cmd = command_giver;
    extern int current_time;

#ifndef COMPAT_MODE
    if (item != current_object)
	error("Illegal to move other object than this_object()\n");
#endif
    /* Recursive moves are not allowed. */
    for (ob = dest; ob; ob = ob->super)
	if (ob == item)
            return; 
    if (item->shadowing)
        error("Can't move an object that is shadowing.\n");
#if 0 /* Not now /Lars */
    /*
     * Objects must have inherited std/object if they are to be allowed to
     * be moved.
     */
#ifndef COMPAT_MODE
    if (!(item->flags & O_APPROVED) ||
		    !(dest->flags & O_APPROVED)) {
	error("Trying to move object where src or dest not inherit std/object\n");
	return;
    }
#endif
#endif
    /* if it's past due for a reset, reset the object immediately */
    /* else mark it to be reset.  Set command_giver to zero to    */
    /* simulate a normal reset, where there is no command_giver   */
    /* also, reset the eval_cost, since this shouldn't add to the */
    /* current execution thread's eval_cost                       */
#ifdef COMPAT_MODE
    {
       extern int eval_cost;
       if (item->next_reset < current_time)
       {
          struct object *s = command_giver;
          int save = eval_cost;
          eval_cost = 0;
          command_giver = 0;
          reset_object(item, 1);
          command_giver = s;
          eval_cost = save;
          if (item->flags & O_DESTRUCTED)
             return;
       }
       else
          item->flags &= ~O_RESET_STATE;

       if (dest->next_reset < current_time)
       {
          struct object *s = command_giver;
          int save = eval_cost;
          eval_cost = 0;
          command_giver = 0;
          reset_object(dest, 1);
          eval_cost = save;
          command_giver = s;
          if (dest->flags & O_DESTRUCTED)
             return;
       }
       else
          dest->flags &= ~O_RESET_STATE;
    }
#endif
    add_light(dest, item->total_light);
    if (item->super) {
	int okey = 0;
		
	if (item->flags & O_ENABLE_COMMANDS) {
#ifdef COMPAT_MODE
	    command_giver = item;
	    push_object(item, "move_object");
	    (void)apply("exit", item->super, 1);
	    if (item->flags & O_DESTRUCTED || dest->flags & O_DESTRUCTED)
	        return;	/* Give up */
#endif
	    remove_sent(item->super, item);
	}
	if (item->super->flags & O_ENABLE_COMMANDS)
	    remove_sent(item, item->super);
	add_light(item->super, - item->total_light);
	for (pp = &item->super->contains; *pp;) {
	    if (*pp != item) {
		if ((*pp)->flags & O_ENABLE_COMMANDS) 
		    remove_sent(item, *pp);
		if (item->flags & O_ENABLE_COMMANDS) 
		    remove_sent(*pp, item);
		pp = &(*pp)->next_inv;
		continue;
	    }
	    *pp = item->next_inv;
	    okey = 1;
	}
	if (!okey)
	    fatal("Failed to find object %s in super list of %s.\n",
		  item->name, item->super->name);
    }
    item->next_inv = dest->contains;
    dest->contains = item;
    item->super = dest;
    /*
     * Setup the new commands. The order is very important, as commands
     * in the room should override commands defined by the room.
     * Beware that init() in the room may have moved 'item' !
     *
     * The call of init() should really be done by the object itself
     * (except in the -o mode). It might be too slow, though :-(
     */
    if (item->flags & O_ENABLE_COMMANDS) {
	command_giver = item;
	(void)apply("init", dest, 0);
	if ((dest->flags & O_DESTRUCTED) || item->super != dest) {
	    command_giver = save_cmd; /* marion */
	    return;
	}
    }
    /*
     * Run init of the item once for every present player, and
     * for the environment (which can be a player).
     */
    for (ob = dest->contains; ob; ob=next_ob) {
	next_ob = ob->next_inv;
	if (ob == item)
	    continue;
	if (ob->flags & O_DESTRUCTED)
            continue;
#if 0 /* Raistlin */
	    error("An object was destructed at call of init()\n");
#endif
	if (ob->flags & O_ENABLE_COMMANDS) {
	    command_giver = ob;
	    (void)apply("init", item, 0);
	    if (dest != item->super) {
		command_giver = save_cmd; /* marion */
		return;
	    }
	}
	if (item->flags & O_DESTRUCTED) /* marion */
	    error("The object to be moved was destructed at call of init()\n");
	if (item->flags & O_ENABLE_COMMANDS) {
	    command_giver = item;
	    (void)apply("init", ob, 0);
	    if (dest != item->super) {
		command_giver = save_cmd; /* marion */
		return;
	    }
	}
    }
    if (dest->flags & O_DESTRUCTED) /* marion */
	error("The destination to move to was destructed at call of init()\n");
    if (dest->flags & O_ENABLE_COMMANDS) {
	command_giver = dest;
	(void)apply("init", item, 0);
    }
    command_giver = save_cmd;
}

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

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

struct sentence *sent_free = 0;
int tot_alloc_sentence;

struct sentence *alloc_sentence() {
    struct sentence *p;
    if (sent_free == 0) {
	p = (struct sentence *)xalloc(sizeof *p);
        tot_alloc_sentence++;
    } else {
	p = sent_free;
	sent_free = sent_free->next;
    }
/*
    p = (struct sentence *)xalloc(sizeof(struct sentence));
    tot_alloc_sentence++;
*/
    p->verb = 0;
    p->ob = 0;
    p->function = 0;
    p->next = 0;
    p->short_verb = 0;
    p->no_space = 0;
    return p;
}

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

void free_sentence(p)
    struct sentence *p;
{
    if (p->function)
	free_string(p->function);
    if (p->verb)
	free_string(p->verb);
/*
    memset((char *)p, '\0', sizeof(struct sentence));
    free((char *)p);
    tot_alloc_sentence--;
*/
    p->verb = 0;
    p->function = 0;
    p->ob = 0;
    p->next = sent_free;
    sent_free = p;
}

compress_spaces(buff)
char *buff;
{
   char *p, *q;
   
   p = buff;
   while(isspace(*p)) p++;
   strcpy(buff, p);
   p = buff;
   while (*p)
   {
      if (isspace(*p) && *p != ' ') *p = ' ';
      if (isspace(*p) && isspace(*(p+1)))
      {
         q = ++p;
         while (isspace(*q)) ++q;
         strcpy(p, q);
      }
      else
         ++p;
   }
}
       

/*
 * Find the sentence for a command from the player.
 * Return success status.
 */
int player_parser(buff)
    char *buff;
{
    struct sentence *s;
    char *p;
    int length;
    struct object *save_current_object = current_object;
    char verb_copy[100], verb_arg[1000];

    if (d_flag > 1)
	debug_message("cmd [%s]: %s\n", command_giver->name, buff);
#if 0
    /* strip trailing spaces. */
    compress_spaces(buff);
#endif /* not used yet */
    for (p = buff + strlen(buff) - 1; p >= buff; p--) {
	if (*p != ' ')
	    break;
	*p = '\0';
    }
    if (buff[0] == '\0')
	return 0;
    if (special_parse(buff))
	return 1;
    p = strchr(buff, ' ');
    if (p == 0)
	length = strlen(buff);
    else
	length = p - buff;
    clear_notify();
    for (s=command_giver->sent; s; s = s->next) {
	struct svalue *ret;
        struct object *command_object;
	int len;
	
	if (s->verb == 0)
	    error("An 'action' did something, but returned 0 or had an undefined verb.\n");
	len = strlen(s->verb);
        if (s->no_space) {
            if (strncmp(buff, s->verb, len) != 0)
               continue;
        }
	else if (s->short_verb) {
#ifdef OLD_ADD_ACTION
            if (strncmp(buff, s->verb, len))
#else
            if (length < s->short_verb || strncmp(buff, s->verb, length) != 0)
#endif
		continue;
	} else {
	    if (len != length) continue;
	    if (strncmp(buff, s->verb, length) != 0)
		continue;
	}
	/*
	 * Now we have found a special sentence !
	 */
	if (d_flag > 1)
	    debug_message("Local command %s on %s\n", s->function, s->ob->name);
        if (!s->no_space)
	   if (length < sizeof verb_copy)
	       len = length;
	   else
	       len = sizeof verb_copy - 1;
	strncpy(verb_copy, buff, len);
	verb_copy[len] = '\0';
	last_verb = verb_copy;
	/*
	 * If the function is static and not defined by current object,
	 * then it will fail. If this is called directly from player input,
	 * then we set current_object so that static functions are allowed.
	 */
#ifndef COMMAND_STATIC
	if (current_object == 0)  
#endif
	    current_object = s->ob;
        command_object = s->ob;
	if (s->no_space) {
    	   push_constant_string(&buff[strlen(s->verb)]);
           strncpy(verb_arg, buff+strlen(s->verb), sizeof verb_arg -1);
           verb_arg[sizeof verb_arg - 1] = 0;
           last_arg = verb_arg;
           ret = apply(s->function, s->ob, 1);
        }
        else if (buff[length] == ' ') {
           push_constant_string(&buff[length+1]);
           strncpy(verb_arg, buff+length+1, sizeof verb_arg -1);
           verb_arg[sizeof verb_arg - 1] = 0;
           last_arg = verb_arg;
           ret = apply(s->function, s->ob, 1);
        } else {
           last_arg = 0;
           ret = apply(s->function, s->ob, 0);
	}
	if (current_object->flags & O_DESTRUCTED)
        {
            if (!command_giver)
                return 1;
	    s = command_giver->sent;	/* Restart :-( */
        }
        current_object = save_current_object;
	last_verb = 0;
        last_arg = 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 && command_object->user && command_giver->interactive &&
	    !(command_giver->flags & O_IS_WIZARD)) 
	    command_object->user->score++;
	if (ret == 0)
	    add_message("Error: function %s not found.\n", s->function);
	break;
    }
    if (s == 0) {
	notify_no_command();
	return 0;
    }
    return 1;
}

/*
 * Associate a command with function in this object.
 * The optional second argument is the command name. If the command name
 * is not given here, it should be given with add_verb().
 *
 * The optinal third argument is a flag that will state that the verb should
 * only match against leading characters.
 *
 * The object must be near the command giver, so that we ensure that the
 * sentence is removed when the command giver leaves.
 */
void add_action(str, cmd, flag)
    char *str, *cmd;
    int flag;
{
    struct sentence *p;
    struct object *ob;

    if (str[0] == ':')
	error("Illegal function name: %s\n", str);
    if (current_object->flags & O_DESTRUCTED)
	return;
    ob = current_object;
    while (ob->shadowing) 
        ob = ob->shadowing;
    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");
    if (d_flag > 1)
	debug_message("--Add action %s\n", str);
#ifdef COMPAT_MODE
    if (strcmp(str, "exit") == 0)
	error("Illegal to define a command to the exit() function.\n");
#endif
    p = alloc_sentence();
    p->function = make_shared_string(str);
    p->ob = ob;
    p->next = command_giver->sent;
    p->short_verb = flag;
    p->no_space = 0;
    if (cmd)
	p->verb = make_shared_string(cmd);
    else
	p->verb = 0;
    command_giver->sent = p;
}

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

/* 
 * removes one sentence structure from player, which matches verb.
 */

int remove_one_sent(ob, player, verb)
struct object *ob, *player;
char *verb;
{
   struct sentence **s;

   for (s = &player->sent; *s; )
   {
      struct sentence *tmp;

      if ((*s)->ob == ob && strcmp((*s)->verb, verb) == 0) 
      {
         if (d_flag > 1)
            debug_message("--Unlinking sentence %s\n", (*s)->verb);
         tmp = *s;
         *s = tmp->next;
         free_sentence(tmp);
         return 1;
      } 
      s = &((*s)->next);
   }
   return 0;
}

/*
 * Remove all commands (sentences) defined by object 'ob' in object
 * 'player'. 
 */
void remove_sent(ob, player)
struct object *ob, *player;
{
   struct sentence **s;
 
   for (s = &player->sent; *s;) 
   {
      struct sentence *tmp;

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

char debug_parse_buff[51]; /* 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(buff)
    char *buff;
{
    int verbose = 0;
    strncpy(debug_parse_buff, buff, sizeof debug_parse_buff-1);
    debug_parse_buff[sizeof debug_parse_buff - 1] = 0;
    if (strcmp(buff, "malloc") == 0) {
#if defined(MALLOC_malloc) || defined(MALLOC_smalloc)
	dump_malloc_data();
#endif
#ifdef MALLOC_gmalloc
	add_message("Using Gnu malloc.\n");
#endif
#ifdef MALLOC_sysmalloc
	add_message("Using system malloc.\n");
#endif
	return 1;
    }
/*
    if (strcmp(buff, "dumpallobj") == 0) {
        dumpstat();
	return 1;
    }
*/
#if defined(MALLOC_malloc) || defined(MALLOC_smalloc)
    if (strcmp(buff, "debugmalloc") == 0) {
	extern int debugmalloc;
	debugmalloc = !debugmalloc;
	if (debugmalloc)
	    add_message("On.\n");
	else
	    add_message("Off.\n");
	return 1;
    }
#endif
    if (strcmp(buff, "status") == 0 || 
        (strcmp(buff, "status tables") == 0 && (verbose = 1))) {
	int tot, res;
	extern int tot_alloc_sentence, tot_alloc_object,
		   num_swapped, total_bytes_swapped, reserved_area,
                   num_arrays, total_array_size, total_object_size,
                   num_mappings, total_mapping_size;
	extern int total_num_prog_blocks, total_prog_block_size;
#ifdef COMM_STAT
	extern int add_message_calls,inet_packets,inet_volume;
#endif

        if (reserved_area)
	    res = RESERVED_SIZE;
	else
	    res = 0; 
#ifdef COMM_STAT
        if (verbose)
	   add_message(
      "Calls to add_message: %d	Packets: %d	Average packet size: %f\n", 
            add_message_calls, inet_packets, (float)inet_volume/inet_packets);
#endif
        if (!verbose)
        {
	   add_message("Sentences:   %5d %7d\n", tot_alloc_sentence, 
		tot_alloc_sentence * sizeof (struct sentence)); 
	   add_message("Objects:     %5d %7d\n",
		    tot_alloc_object, total_object_size);
           add_message("Arrays:      %5d %7d\n", num_arrays, total_array_size);
           add_message("Mappings:    %5d %7d\n", num_mappings, 
                    total_mapping_size);
	   add_message("Prog blocks: %5d %7d (%d swapped, %d Kbyte)\n", 
                    total_num_prog_blocks, total_prog_block_size,
                    num_swapped, total_bytes_swapped / 1024);
	   add_message("Memory reserved:	%7d\n", res);
        }
	/* status_lnodes_matched(); */
        if (verbose)
	   stat_living_objects();
	tot =		   total_prog_block_size +
			   show_otable_status(verbose) +
			   heart_beat_status(verbose) +
			   add_string_status(verbose) +
			   print_call_out_usage(verbose) +
			   total_array_size +
                           total_object_size +
                           total_mapping_size +
			   res;
        if (!verbose)
        {
            add_message("\t\t\t\t\t --------\n");
            add_message("Total:\t\t\t\t\t %8d\n", tot);
        }
	return 1;
    }
#ifdef NEED_DIRECTIONS
    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, "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;
    }
#endif
    return 0;
}

void print_local_commands() {
    struct sentence *s;

    add_message("Current local commands:\n");
    for (s = command_giver->sent; s; s = s->next)
	add_message("%s ", s->verb);
    add_message("\n");
}

/*VARARGS1*/
void fatal(fmt, a, b, c, d, e, f, g, h)
    char *fmt;
    int a, b, c, d, e, f, g, h;
{
    static int in_fatal = 0;
    /* Prevent double fatal. */
    if (in_fatal)
	abort();
    in_fatal = 1;
    (void)fprintf(stderr, fmt, a, b, c, d, e, f, g, h);
    fflush(stderr);
    if (current_object)
	(void)fprintf(stderr, "Current object was %s\n",
		      current_object->name);
    debug_message(fmt, a, b, c, d, e, f, g, h);
    if (current_object)
	debug_message("Current object was %s\n", current_object->name);
    debug_message("Dump of variables:\n");
#if defined(DEBUG) && defined(TRACE_CODE)
    (void)dump_trace(1);
#else
    (void)dump_trace();
#endif
    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;
    if (error_recovery_context_exists > 1) {
	longjmp(error_recovery_context, 1);
	fatal("Throw_error failed!");
    }
    error("Throw with no catch.\n");
}

static char emsg_buf[2000];

/*VARARGS1*/
void error(fmt, a, b, c, d, e, f, g, h)
    char *fmt;
    int a, b, c, d, e, f, g, h;
{
    extern int error_recovery_context_exists;
    extern jmp_buf error_recovery_context;
    extern struct object *current_heart_beat;
    extern struct svalue catch_value;
    char *object_name;

    sprintf(emsg_buf+1, fmt, a, b, c, d, e, f, g, h);
    emsg_buf[0] = '*';	/* all system errors get a * at the start */

    if (error_recovery_context_exists > 1) { /* user catches this error */
	struct svalue v;
	v.type = T_STRING;
	v.u.string = emsg_buf;
	v.string_type = STRING_MALLOC;	/* Always reallocate */
	assign_svalue(&catch_value, &v);
   	longjmp(error_recovery_context, 1);
   	fatal("Catch() longjump failed");
    }
    num_error++;
    if (num_error > 1)
	error("Too many simultaneous errors.\n");
    debug_message("%s", emsg_buf+1);
    if (current_object) {
	debug_message("Current object was %s, line %d\n",
		      current_object->name, current_line);
	save_error(emsg_buf, current_object->name, current_line);
    }
#if defined(DEBUG) && defined(TRACE_CODE)
    object_name = dump_trace(1);
#else
    object_name = dump_trace();
#endif
    fflush(stdout);
    if (object_name) {
	struct object *ob;
	ob = find_object2(object_name);
	if (!ob) {
	    if (command_giver)
		add_message("error when executing program in destroyed object %s\n",
			    object_name);
	    debug_message("error when executing program in destroyed object %s\n",
			object_name);
	}
    }
    if (command_giver && command_giver->interactive) {
	struct svalue *v = 0;

	num_error--;
	/* 
	 * The stack must be brought in a usable state. After the
	 * call to reset_machine(), all arguments to error() are invalid,
	 * and may not be used any more. The reason is that some strings
	 * may have been on the stack machine stack, and has been deallocated.
	 */
	reset_machine (0);
	push_constant_string("error messages");
	v = apply_master_ob("query_player_level", 1);
	num_error++;
	if (v && v->u.number != 0) {
	    add_message("%s", emsg_buf+1);
	    if (current_object)
		add_message("program: %s, object: %s line %d\n",
			    current_prog ? current_prog->name : "",
			    current_object->name,
			    get_line_number_if_any());
	} else {
	    add_message("Your sensitive mind notices a wrongness in the fabric of space.\n");
	}
    }
    if (current_heart_beat) {
	set_heart_beat(current_heart_beat, 0);
	debug_message("Heart beat in %s turned off.\n",
		      current_heart_beat->name);
	if (current_heart_beat->interactive) {
	    struct object *save_cmd = command_giver;
	    command_giver = current_heart_beat;
	    add_message("Game driver tells you: You have no heart beat !\n");
	    command_giver = save_cmd;
	}
	current_heart_beat = 0;
    }
    num_error--;
    if (error_recovery_context_exists)
	longjmp(error_recovery_context, 1);
    abort();
}

/*
 * Check that it is an legal path. No '..' are allowed.
 */
int legal_path(path)
    char *path;
{
    char *p;

    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;
    }
    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(error_file, line, what)
     char *error_file, *what;
     int line;
{
    char buff[500];

    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 file name]...";
    sprintf(buff, "%s line %d:%s\n", error_file, line, what);
    push_constant_string(error_file);
    push_constant_string(buff);
    apply_master_ob("log_error", 2);
}


/*
 * 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.
 *
 * The master object (master.c) always have permissions to access
 * any file in the mudlib hierarchy, but only inside the mudlib.
 *
 * WARNING: The string returned will (mostly) be deallocated at next
 * call of apply().
 */
#ifdef COMPAT_MODE
char debug_check_file[50];

char *check_file_name(file, writeflg)
char *file;
int writeflg;
{
   struct svalue *ret;
   struct object *stuff = command_giver;
   char *str;
   
   if (current_object == master_ob)
   {
      /* Master object can read/write anywhere */
      if (file[0] == '/')
	 str = file+1;
      else
	 str = file;
   }
   else 
   {  
      if (!stuff)
	 stuff = find_object("room/church");
      if (!stuff)
      {
	 char *temp;
	 struct object *ob;
	 ob=(command_giver?command_giver:current_object);
	 if (!ob) return 0;
	 if (!legal_path(file)) return 0;
	 if (ob->user) 
	 {
	    int tl;
	    tl = strlen(ob->user->name) + 10;
	    temp=(char *)xalloc(tl);
	    strcpy(temp,"players/");
	    strcat(temp,ob->user->name);
	    strcat(temp,"/");
	    if (strncmp(file, temp, tl-1)==0 ||
		strncmp(file, "open/", 5)==0 ||
		(!strchr(file, '/') && !writeflg))
	    { 
	       free(temp); 
	       return file; 
	    }
	    else
	    { 
	       free(temp); 
	       return 0; 
	    }
	 }
	 else if (strncmp(ob->name, "open/", 5) == 0 &&
                  strncmp(file, "open/", 5) == 0)
	    return file;
	 else if (strncmp(ob->name, "obj/", 4) == 0||
                  strncmp(ob->name, "room/",5) == 0)
	    return file;
	 else return 0;
      }
      /*
       * We don't have to free the string in ret. This is done
       * by the garbage collection.
       */
      else
      {
         push_constant_string(file);
     	 if (writeflg)
     	    ret = apply("valid_write", stuff, 1);
         else
 	    ret = apply("valid_read",  stuff, 1);
         if (stuff->flags&O_DESTRUCTED)
	    return 0;
         if (!ret || ret->type != T_STRING) 
	 {
	    add_message("Bad file name %s.\n", file?file:"<null>");
	    return 0;
         }
         str = ret->u.string;
      } 
   }
   strncpy(debug_check_file, str, sizeof debug_check_file);
   debug_check_file[sizeof debug_check_file- 1] = 0;
   if (!legal_path(str)) 
   {
      add_message("Illegal path: %s\n", str?str:"<null>");
      return 0;
   }
   if (str[0] == '\0')
       return ".";
   return str;
}
#else

/*
 * 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(path, eff_user, call_fun, writeflg)
    char *path;
    struct wiz_list *eff_user;
    char *call_fun;
    int writeflg;
{
    struct svalue *v;

    if (eff_user == 0)
	return 0;
    push_string(make_shared_string(path), STRING_SHARED);/**MALLOC**/
    push_string(eff_user->name, STRING_CONSTANT);
    push_string(call_fun, STRING_CONSTANT);
    if (writeflg)
	v = apply_master_ob("valid_write", 3);
    else
	v = apply_master_ob("valid_read", 3);
    if (v && v->type == T_NUMBER && v->u.number == 0)
	return 0;
    if (path[0] == '/')
       path++;
    if (legal_path(path)) 
        return path;
    else
        return 0;
}
#endif

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

#ifndef _AIX
void startshutdowngame() {
    game_is_being_shut_down = 1;
}
#else
void startshutdowngame(int arg) {
    game_is_being_shut_down = 1;
}
#endif

/*
 * This one is called from the command "shutdown".
 * We don't call it directly from HUP, because it is dangerous when being
 * in an interrupt.
 */
void shutdowngame() {
    shout_string("Game driver shouts: LPmud shutting down immediately.\n");
    save_wiz_file();
    ipc_remove();
    remove_all_players();
    unlink_swap_file();
#ifdef DEALLOCATE_MEMORY_AT_SHUTDOWN
    remove_all_objects();
    free_all_sent();
    free_all_call_outs();
    remove_wiz_list();
    dump_malloc_data();
#endif
#ifdef OPCPROF
    opcdump();
#endif
    exit(0);
}

/*
 * Transfer an object from an object to an object.
 * Call add_weight(), drop(), get(), prevent_insert(), add_weight(),
 * and can_put_and_get() where needed.
 * Return 0 on success, and special code on failure:
 *
 * 1: To heavy for destination.
 * 2: Can't be dropped.
 * 3: Can't take it out of it's container.
 * 4: The object can't be inserted into bags etc.
 * 5: The destination doesn't allow insertions of objects.
 * 6: The object can't be picked up.
 */
#ifdef F_TRANSFER
int transfer_object(ob, to)
    struct object *ob, *to;
{
    struct svalue *v_weight, *ret;
    int weight;
    struct object *from = ob->super;

    /*
     * Get the weight of the object
     */
#ifndef COMPAT_MODE
    error("transfer() not allowed.\n");
#endif
    if (ob->shadowing)
        error("transfer: cannot move object that is shadowing.\n");
    weight = 0;
    v_weight = apply("query_weight", ob, 0);
    if (v_weight && v_weight->type == T_NUMBER)
	weight = v_weight->u.number;
    if (ob->flags & O_DESTRUCTED)
	return 3;
    /*
     * If the original place of the object is a living object,
     * then we must call drop() to check that the object can be dropped.
     */
    if (from && (from->flags & O_ENABLE_COMMANDS)) {
	ret = apply("drop", ob, 0);
	if (ret && (ret->type != T_NUMBER || ret->u.number != 0))
	    return 2;
	/* This shold not happen, but we can not trust LPC hackers. :-) */
	if (ob->flags & O_DESTRUCTED)
	    return 2;
    }
    /*
     * If 'from' is not a room and not a player, check that we may
     * remove things out of it.
     */
    if (from && from->super && !(from->flags & O_ENABLE_COMMANDS)) {
	ret = apply("can_put_and_get", from, 0);
	if (!ret || (ret->type != T_NUMBER && ret->u.number != 1) ||
	  (from->flags & O_DESTRUCTED))
	    return 3;
    }
    /*
     * If the destination is not a room, and not a player,
     * Then we must test 'prevent_insert', and 'can_put_and_get'.
     */
    if (to->super && !(to->flags & O_ENABLE_COMMANDS)) {
	ret = apply("prevent_insert", ob, 0);
	if (ret && (ret->type != T_NUMBER || ret->u.number != 0))
	    return 4;
	ret = apply("can_put_and_get", to, 0);
	if (!ret || (ret->type != T_NUMBER && ret->type != 0) ||
	  (to->flags & O_DESTRUCTED) || (ob->flags & O_DESTRUCTED))
	    return 5;
    }
    /*
     * If the destination is a player, check that he can pick it up.
     */
    if (to->flags & O_ENABLE_COMMANDS) {
	ret = apply("get", ob, 0);
	if (!ret || (ret->type == T_NUMBER && ret->u.number == 0) ||
	  (ob->flags & O_DESTRUCTED))
	    return 6;
    }
    /*
     * If it is not a room, correct the total weight in the destination.
     */
    if (to->super && weight) {
	/*
	 * Check if the destination can carry that much.
	 */
	push_number(weight);
        ret = 0;
	ret = apply("add_weight", to, 1);
	if (ret && ret->type == T_NUMBER && ret->u.number == 0)
	    return 1;
	if (to->flags & O_DESTRUCTED)
	    return 1;
    }
    /*
     * If it is not a room, correct the weight in the 'from' object.
     */
    if (from && from->super && weight) {
	push_number(-weight);
	(void)apply("add_weight", from, 1);
        if (from->flags & O_DESTRUCTED)
           return 1;
    }
    move_object(ob, to);
    return 0;
}
#endif /* F_TRANSFER */

/*
 * Move or destruct one object.
 */
void move_or_destruct(what, to)
    struct object *what, *to;
{
    int res;
    struct svalue v;

#ifdef COMPAT_MODE
    res = transfer_object(what, to);
    if (res == 0 || (what->flags & O_DESTRUCTED))
        return;
    if (res == 1 || res == 4 || res == 5) {
        if (to->super) {
             move_or_destruct(what, to->super);
             return;
    	}
    }
#else
    {
	struct svalue *svp;
	push_object(to, "move_or_destruct");
        push_number(1);
	svp = apply("move", what, 2);
	if (svp && svp->type == T_NUMBER && svp->u.number == 0)
	    return;
	if (what->flags & O_DESTRUCTED)
	    return;
    }
#endif
	
    v.type = T_OBJECT;
    v.u.ob = what;
    destruct_object(&v, 0);
}

/*
 * Call this one when there is only little memory left. It will start
 * Armageddon.
 */
void slow_shut_down(minutes)
    int minutes;
{
    struct object *ob;

    /*
     * Swap out objects, and free some memory.
     */
    ob = find_object("obj/shut");
    if (!ob) {
	struct object *save_current = current_object,
	              *save_command = command_giver;
	command_giver = 0;
	current_object = 0;
	shout_string("Game driver shouts: Out of memory.\n");
	command_giver = save_command;
	current_object = save_current;
#ifndef _AIX
	startshutdowngame();
#else
        startshutdowngame(1);
#endif
	return;
    }
    shout_string("Game driver shouts: The memory is getting low !\n");
    push_number(minutes);
    (void)apply("shut", ob, 1);
}

int match_string(match, str)
    char *match, *str;
{
    int i;

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

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

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

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

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

void
strip_trailing_slashes (path)
     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;
    }
#ifndef FCHMOD_MISSING
  if (fchmod (ofd, from_stats.st_mode & 0777))
    {
      error ("%s: fchmod failed\n", to);
      close (ifd);
      close (ofd);
      unlink (to);
      return 1;
    }
#endif
  
  while ((len = read (ifd, buf, sizeof (buf))) > 0)
    {
      int wrote = 0;
      char *bp = buf;
      
      do
	{
	  wrote = write (ofd, bp, len);
	  if (wrote < 0)
	    {
	      error ("%s: write failed\n", to);
	      close (ifd);
	      close (ofd);
	      unlink (to);
	      return 1;
	    }
	  bp += wrote;
	  len -= wrote;
	} while (len > 0);
    }
  if (len < 0)
    {
      error ("%s: read failed\n", from);
      close (ifd);
      close (ofd);
      unlink (to);
      return 1;
    }

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

  return 0;
}

#if 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 (from, to)
     char *from;
     char *to;
{
  if (lstat (from, &from_stats) != 0)
      return 0;

  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
	  return 0;

      if (S_ISDIR (to_stats.st_mode))
	  return 0;

    }
  else if (errno != ENOENT)
      return 0;
#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 1;
    }

  if (errno != EXDEV)
      return 0;

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

  if (copy (from, to))
      return 0;
  
  if (unlink (from))
      return 0;

  return 1;
}
    
/*
 * do_rename is used by the efun rename. It is basically a combination
 * of the unix system call rename and the unix command mv. Please shoot
 * the people at ATT who made Sys V.
 */

#ifdef F_RENAME
int
do_rename(fr, t)
    char *fr, *t;
{
    char *from, *to;
    
    from = check_valid_path(fr, current_object->eff_user, "do_rename", 1);
    if(!from)
	return 1;
    to = check_valid_path(t, current_object->eff_user, "do_rename", 1);
    if(!to)
	return 1;
    if(!strlen(to) && !strcmp(t, "/")) {
	to = (char *)alloca(3);
	sprintf(to, "./");
    }
    strip_trailing_slashes (from);
    if (isdir (to))
	{
	    /* Target is a directory; build full target filename. */
	    char *cp;
	    char *newto;
	    
	    cp = strrchr (from, '/');
	    if (cp)
		cp++;
	    else
		cp = from;
	    
	    newto = (char *) alloca (strlen (to) + 1 + strlen (cp) + 1);
	    sprintf (newto, "%s/%s", to, cp);
	    return do_move (from, newto);
	}
    else
	return do_move (from, to);
}

#endif

#ifdef LEX_lexical
void pre_compile(str)
    char *str;
{
    char *c_name, *i_name, buff[1000];
    FILE *f;

    if(!legal_path(str))
	error("Illegal attempt to access %s\n",str);
    i_name = xalloc(strlen(str)+3);
    (void)strcpy(i_name, str);
    (void)strcat(i_name, ".i");
    c_name = (char *)xalloc(strlen(str)+3);
    (void)strcpy(c_name, str);
    (void)strcat(c_name, ".c");
    sprintf(buff, "%s %s %s", PRE_COMPILE, c_name, i_name);
    f = (FILE *)vpopen(buff, "r");
    if (f == 0) {
	error("Unable to invoke precompiler!\n");
	alarm(0);
    }
    while(1) {
	if (fgets(buff, sizeof buff, f) == 0)
	    break;
	add_message("%s", buff);
    }
    vpclose(f);
    free((char *)c_name);
    free((char *)i_name);
}
#endif
#endif /* F_RENAME */