#include <varargs.h>
#include <stdio.h>
#include <setjmp.h>
#include <string.h>
#include <ctype.h>
#include <sys/time.h>
#include <sys/types.h>		/* sys/types.h and netinet/in.h are here to enable include of comm.h below */
#include <sys/stat.h>
#ifdef MARK
#include <prof.h>
#endif
#include <memory.h>

#ifdef MARK
#define CASE(x) case x: MARK(x);
#else
#define CASE(x) case x:
#endif

#include "lint.h"
#include "lang.h"
#include "exec.h"
#include "interpret.h"
#include "config.h"
#include "object.h"
#include "wiz_list.h"
#include "instrs.h"
#include "patchlevel.h"
#include "comm.h"
#include "switch.h"

#ifdef RUSAGE			/* Defined in config.h */
#include <sys/resource.h>
extern int getrusage PROT((int, struct rusage *));
#ifdef sun
extern int getpagesize();
#endif
#ifndef RUSAGE_SELF
#define RUSAGE_SELF	0
#endif
#endif

#if defined(__GNUC__) && !defined(lint)
#define INLINE /* inline */ /* Another time ! */
#else
#define INLINE
#endif

extern struct object *master_ob;

extern void print_svalue PROT((struct svalue *));
extern void say PROT((struct svalue *, struct vector *));
static struct svalue *sapply PROT((char *, struct object *, int));
static void do_trace PROT((char *, char *, char *));
static int apply_low PROT((char *, struct object *, int));
static int inter_sscanf PROT((int));
extern int remote_command;
static int strpref PROT((char *, char *));
static void add_attribute PROT((char *, struct object *, int, int));
static Attribute *find_attribute PROT((char *, struct object *));
static int query_attribute PROT((char *, struct object *));
static int get_attribute PROT((char *, struct object *));
static struct vector *get_attr_vector PROT((struct object *));
static void delete_attribute PROT((char *, struct object *));

extern struct object *obj_list_destruct;
extern struct object *previous_ob;
extern char *last_verb, *last_arg;
extern struct svalue const0, const1;
struct program *current_prog;
struct object *frame_ob;
extern int current_time;
extern struct object *current_heart_beat, *current_interactive;

static int tracedepth;
#define TRACE_CALL 1
#define TRACE_CALL_OTHER 2
#define TRACE_RETURN 4
#define TRACE_ARGS 8
#define TRACE_EXEC 16
#define TRACE_HEART_BEAT 32
#define TRACE_APPLY 64
#define TRACE_OBJNAME 128
#define TRACETST(b) (command_giver->interactive->trace_level & (b))
#define TRACEP(b) \
    (command_giver && command_giver->interactive && TRACETST(b) && \
     (command_giver->interactive->trace_prefix == 0 || \
      (current_object && strpref(command_giver->interactive->trace_prefix, \
	      current_object->name))) )
#define TRACEHB (current_heart_beat == 0 || (command_giver->interactive->trace_level & TRACE_HEART_BEAT))

/*
 * Inheritance:
 * An object X can inherit from another object Y. This is done with
 * the statement 'inherit "file";'
 * The inherit statement will clone a copy of that file, call reset
 * in it, and set a pointer to Y from X.
 * Y has to be removed from the linked list of all objects.
 * All variables declared by Y will be copied to X, so that X has access
 * to them.
 *
 * If Y isn't loaded when it is needed, X will be discarded, and Y will be
 * loaded separetly. X will then be reloaded again.
 */
extern int d_flag;

extern int current_line, eval_cost;

/*
 * These are the registers used at runtime.
 * The control stack saves registers to be restored when a function
 * will return. That means that control_stack[0] will have almost no
 * interesting values, as it will terminate execution.
 */
static char *pc;		/* Program pointer. */
static struct svalue *fp;	/* Pointer to first argument. */
static struct svalue *sp;	/* Points to value of last push. */
static short *break_sp;		/* Points to address to branch to
				 * at next F_BREAK			*/
static int function_index_offset; /* Needed for inheritance */
static int variable_index_offset; /* Needed for inheritance */

static struct svalue start_of_stack[EVALUATOR_STACK_SIZE];
struct svalue catch_value;	/* Used to throw an error to a catch */

static struct control_stack control_stack[MAX_TRACE];
static struct control_stack *csp;	/* Points to last element pushed */

/*
 * May current_object shadow object 'ob' ? We rely heavily on the fact that
 * function names are pointers to shared strings, which means that equality
 * can be tested simply through pointer comparison.
 */
int validate_shadowing(ob)
    struct object *ob;
{
    int i, j;
    struct program *shadow = current_object->prog, *victim = ob->prog;
    struct svalue *ret;

    if (current_object->shadowing)
	error("shadow: Already shadowing.\n");
    if (current_object->shadowed)
	error("shadow: Can't shadow when shadowed.\n");
    if (current_object->super)
	error("The shadow must not reside inside another object.\n");
    if (ob->shadowing)
	error("Can't shadow a shadow.\n");
    for (i=0; i < shadow->num_functions; i++) {
	for (j=0; j < victim->num_functions; j++) {
	    if (shadow->functions[i].name != victim->functions[j].name)
		continue;
	    if (victim->functions[j].type & TYPE_MOD_NO_MASK)
		error("Illegal to shadow 'nomask' function \"%s\".\n",
		      victim->functions[j].name);
	}
    }
    push_object(ob, "validate_shadowing");
    ret = apply_master_ob("query_allow_shadow", 1);
    if (!(ob->flags & O_DESTRUCTED) &&
	ret && !(ret->type == T_NUMBER && ret->u.number == 0))
    {
	return 1;
    }
    return 0;
}

/*
 * Information about assignments of values:
 *
 * There are three types of l-values: Local variables, global variables
 * and vector elements.
 *
 * The local variables are allocated on the stack together with the arguments.
 * the register 'frame_pointer' points to the first argument.
 *
 * The global variables must keep their values between executions, and
 * have space allocated at the creation of the object.
 *
 * Elements in vectors are similar to global variables. There is a reference
 * count to the whole vector, that states when to deallocate the vector.
 * The elements consists of 'struct svalue's, and will thus have to be freed
 * immediately when over written.
 */
 
/*
 * Push an object pointer on the stack. Note that the reference count is
 * incremented. 
 * A destructed object must never be pushed on the stack.
 */
INLINE
void push_object(ob, from)
    struct object *ob;
    char *from;
{
    char buf[50];
    sp++;
    if (sp == &start_of_stack[EVALUATOR_STACK_SIZE])
	fatal("stack overflow\n");
    sp->type = T_OBJECT;
    sp->u.ob = ob;
    if (from)
       sprintf(buf, "push_object (%s)", from);
    else strcpy(buf, "push_object");
    add_ref(ob, buf);
}

/*
 * Push a number on the value stack.
 */
INLINE
void push_number(n)
    int n;
{
    sp++;
    if (sp == &start_of_stack[EVALUATOR_STACK_SIZE])
	fatal("stack overflow\n");
    sp->type = T_NUMBER;
    sp->u.number = n;
}

/*
 * Push a string on the value stack.
 */
INLINE
void push_string(p, type)
    char *p;
    int type;
{
    sp++;
    if (sp == &start_of_stack[EVALUATOR_STACK_SIZE])
	fatal("stack overflow\n");
    sp->type = T_STRING;
    sp->string_type = type;
    switch(type) {
    case STRING_MALLOC:
	sp->u.string = string_copy(p);
	break;
    case STRING_SHARED:
	sp->u.string = make_shared_string(p);
	break;
    case STRING_CONSTANT:
	sp->u.string = p;
	break;
    }
}

/*
 * Get address to a valid global variable.
 */
static INLINE struct svalue *find_value(num)
    int num;
{
#ifdef DEBUG
    if (num >= current_object->prog->num_variables) {
	fatal("Illegal variable access %d(%d). See trace above.\n",
	    num, current_object->prog->num_variables);
    }
#endif
    return &current_object->variables[num];
}


/*
 * Free the data that an svalue is pointing to. Not the svalue
 * itself.
 */
void free_svalue(v, from)
    struct svalue *v;
    char *from;
{
   switch(v->type) {
      case T_STRING:
         switch(v->string_type) {
            case STRING_MALLOC:
               free(v->u.string);
               break;
            case STRING_SHARED:
               free_string(v->u.string);
               break;
         }
         break;
      case T_OBJECT:
      {
         char buf[80]; strcpy(buf, "free_svalue ");
         if (from) { strcat(buf, "("); strcat(buf, from); strcat(buf, ")"); }
         free_object(v->u.ob, buf);
         break;
      }
      case T_POINTER:
         free_vector(v->u.vec);
         break;
      case T_MAPPING:
         free_mapping(v->u.map);
         break;
   }
   *v = const0;
}

#ifndef COMPAT_MODE
/*
 * Prepend a slash in front of a string.
 */
static char *add_slash(str)
    char *str;
{
    char *tmp;

    tmp = xalloc(strlen(str)+2);
    strcpy(tmp,"/"); strcat(tmp,str);
    return tmp;
}
#endif

/*
 * Assign to a svalue.
 * This is done either when element in vector, or when to an identifier
 * (as all identifiers are kept in a vector pointed to by the object).
 */

INLINE void assign_svalue_no_free(to, from)
    struct svalue *to;
    struct svalue *from;
{
#ifdef DEBUG
    if (from == 0)
	fatal("Null pointer to assign_svalue().\n");
#endif
    *to = *from;
    switch(from->type) {
    case T_STRING:
	switch(from->string_type) {
	case STRING_MALLOC:	/* No idea to make the string shared */
	    to->u.string = string_copy(from->u.string);
	    break;
	case STRING_CONSTANT:	/* Good idea to make it shared */
	    to->string_type = STRING_SHARED;
	    /* FALL THROUGH ! */
	case STRING_SHARED:	/* It already is shared */
	    to->u.string = make_shared_string(from->u.string);
	    break;
	default:
	    fatal("Bad string type %d\n", from->string_type);
	}
	break;
    case T_OBJECT:
	add_ref(to->u.ob, "ass to var");
	break;
    case T_POINTER:
	to->u.vec->ref++;
	break;
    case T_MAPPING:
        to->u.map->ref++;
        break;
    }
}

INLINE void assign_svalue(dest, v)
    struct svalue *dest;
    struct svalue *v;
{
    /* First deallocate the previous value. */
    free_svalue(dest, "assign_svalue");
    assign_svalue_no_free(dest, v);
}

void push_svalue(v)
    struct svalue *v;
{
    sp++;
    assign_svalue_no_free(sp, v);
}

static INLINE void pop_stack() {
#ifdef DEBUG
    if (sp < start_of_stack)
	fatal("Stack underflow.\n");
#endif
    free_svalue(sp, "pop_stack");
    sp--;
}

/*
 * Compute the address of an array element.
 */
static INLINE void push_indexed_lvalue()
{
    struct svalue *vec, *item;
    int ind;

    vec = sp - 1;

    if (vec->type == T_MAPPING)
    {
        struct mapping *m = vec->u.map;
        vec = find_for_insert(m, sp);
        pop_stack();
	free_svalue(sp, "push_indexed_lvalue");
        sp->type = T_LVALUE;
        sp->u.lvalue = vec;
        return;
    }
    ind = sp->u.number;
    if (sp->type != T_NUMBER || ind < 0)
	error("Illegal index\n");
    pop_stack();
    if (vec->type == T_STRING) {
	static struct svalue one_character;
	/* marion says: this is a crude part of code */
	one_character.type = T_NUMBER;
	if (ind > strlen(vec->u.string) || ind < 0)
	    one_character.u.number = 0;
	else
	    one_character.u.number = vec->u.string[ind];
	free_svalue(sp, "push_indexed_lvalue");
	sp->type = T_LVALUE;
	sp->u.lvalue = &one_character;
	return;
    }
    if (vec->type != T_POINTER) error("Indexing on illegal type.\n");
    if (ind >= vec->u.vec->size) error ("Index out of bounds\n");
    item = &vec->u.vec->item[ind];
    if (vec->u.vec->ref == 1) {
	static struct svalue quickfix = { T_NUMBER };
	/* marion says: but this is crude too */
	/* marion blushes. */
	assign_svalue (&quickfix, item);
	item = &quickfix;
    }
    free_svalue(sp, "push_indexed_lvalue");		
              /* This will make 'vec' invalid to use */
    sp->type = T_LVALUE;
    sp->u.lvalue = item;
}

#ifdef OPCPROF
#define MAXOPC 512
static int opcount[MAXOPC];
#endif

/*
 * Deallocate 'n' values from the stack.
 */
INLINE
void pop_n_elems(n)
    int n;
{
#ifdef DEBUG
    if (n < 0)
	fatal("pop_n_elems: %d elements.\n", n);
#endif
    for (; n>0; n--)
	pop_stack();
}

void bad_arg(arg, instr)
    int arg, instr;
{
    error("Bad argument %d to %s()\n", arg, get_f_name(instr));
}

INLINE
static void push_control_stack(funp)
    struct function *funp;
{
    if (csp == &control_stack[MAX_TRACE-1])
	error("Too deep recursion.\n");
    csp++;
    csp->funp = funp;	/* Only used for tracebacks */
    csp->ob = current_object;
    csp->prev_ob = previous_ob;
    csp->fp = fp;
    csp->prog = current_prog;
    /* csp->extern_call = 0; It is set by eval_instruction() */
    csp->pc = pc;
    csp->function_index_offset = function_index_offset;
    csp->variable_index_offset = variable_index_offset;
    csp->break_sp = break_sp;
}

/*
 * Pop the control stack one element, and restore registers.
 * extern_call must not be modified here, as it is used imediately after pop.
 */
static void pop_control_stack() {
#ifdef DEBUG
    if (csp == control_stack - 1)
	fatal("Popped out of the control stack");
#endif
    current_object = csp->ob;
    current_prog = csp->prog;
    previous_ob = csp->prev_ob;
    pc = csp->pc;
    fp = csp->fp;
    function_index_offset = csp->function_index_offset;
    variable_index_offset = csp->variable_index_offset;
    break_sp = csp->break_sp;
    csp--;
}

/*
 * Push a pointer to a vector on the stack. Note that the reference count
 * is incremented. Newly created vectors normally have a reference count
 * initialized to 1.
 */

INLINE void push_vector(v)
struct vector *v;
{
    v->ref++;
    sp++;
    sp->type = T_POINTER;
    sp->u.vec = v;
}

INLINE void push_mapping(m)
struct mapping *m;
{
    m->ref++;
    sp++;
    sp->type = T_MAPPING;
    sp->u.map = m;
}


/*
 * Push a string on the stack that is already shared.
 */
static void INLINE push_shared_string(p)
    char *p;
{
    sp++;
    sp->type = T_STRING;
    sp->u.string = p;
    sp->string_type = STRING_SHARED;
}

/*
 * Push a string on the stack that is already malloced.
 */
static void INLINE push_malloced_string(p)
    char *p;
{
    sp++;
    sp->type = T_STRING;
    sp->u.string = p;
    sp->string_type = STRING_MALLOC;
}

/*
 * Push a string on the stack that is already constant.
 */
INLINE
void push_constant_string(p)
    char *p;
{
    sp++;
    sp->type = T_STRING;
    sp->u.string = p;
    sp->string_type = STRING_CONSTANT;
}

static void do_trace_call(funp)
    struct function *funp;
{
    do_trace("Call direct ", funp->name, " ");
    if (TRACEHB) {
        if (TRACETST(TRACE_ARGS)) {
            int i;
            add_message(" with %d arguments: ", funp->num_arg);
            for(i = funp->num_arg-1; i >= 0; i--) {
                print_svalue(&sp[-i]);
                add_message(" ");
            }
        }
        add_message("\n");
    }
}

/*
 * Argument is the function to execute. If it is defined by inheritance,
 * then search for the reall definition, and return it.
 * There is a number of arguments on the stack. Normalize them and initialize
 * local variables, so that the called function is pleased.
 */
static struct function *setup_new_frame(funp)
    struct function *funp;
{
    function_index_offset = 0;
    variable_index_offset = 0;
    while(funp->flags & NAME_INHERITED) {
	function_index_offset +=
	    current_prog->inherit[funp->offset].function_index_offset;
	variable_index_offset +=
	    current_prog->inherit[funp->offset].variable_index_offset;
	current_prog =
	    current_prog->inherit[funp->offset].prog;
	funp = &current_prog->functions[funp->function_index_offset];
    }
    /* Remove excessive arguments */
    while(csp->num_local_variables > funp->num_arg) {
	pop_stack();
	csp->num_local_variables--;
    }
    /* Correct number of arguments and local variables */
    while(csp->num_local_variables < funp->num_arg + funp->num_local) {
	push_number(0);
	csp->num_local_variables++;
    }
    tracedepth++;
    if (TRACEP(TRACE_CALL)) {
	do_trace_call(funp);
    }
    fp = sp - csp->num_local_variables + 1;
    break_sp = (short*)(sp+1);
    return funp;
}

static void break_point()
{
    if (sp - fp - csp->num_local_variables + 1 != 0)
	fatal("Bad stack pointer.\n");
}

/* marion
 * maintain a small and inefficient stack of error recovery context
 * data structures.
 * This routine is called in three different ways:
 * push=-1	Pop the stack.
 * push=1	push the stack.
 * push=0	No error occured, so the pushed value does not have to be
 *		restored. The pushed value can simply be popped into the void.
 *
 * The stack is implemented as a linked list of stack-objects, allocated
 * from the heap, and deallocated when popped.
 */
void push_pop_error_context (push) 
int push;
{
    extern jmp_buf error_recovery_context;
    extern int error_recovery_context_exists;
    extern struct svalue catch_value;
    static struct error_context_stack {
	jmp_buf old_error_context;
	int old_exists_flag;
	struct control_stack *save_csp;
	struct object *save_command_giver;
	struct svalue *save_sp;
	struct error_context_stack *next;
    } *ecsp = 0, *p;

    if (push == 1) {
	/*
	 * Save some global variables that must be restored separately
	 * after a longjmp. The stack will have to be manually popped all
	 * the way.
	 */
	p = (struct error_context_stack *)xalloc (sizeof *p);
	p->save_sp = sp;
	p->save_csp = csp;	
	p->save_command_giver = command_giver;
	memcpy (
	    (char *)p->old_error_context,
	    (char *)error_recovery_context,
	    sizeof error_recovery_context);
	p->old_exists_flag = error_recovery_context_exists;
	p->next = ecsp;
	ecsp = p;
    } else {
	p = ecsp;
	if (p == 0)
	    fatal("Catch: error context stack underflow");
	if (push == 0) {
#ifdef DEBUG
	    if (csp != p->save_csp-1)
		fatal("Catch: Lost track of csp");
#if 0
	    /*
	     * This test is not valid! The statement catch(exec("...")) will
	     * change the value of command_giver.
	     */
	    if (command_giver != p->save_command_giver)
		fatal("Catch: Lost track of command_giver");
#endif
#endif
	} else {
	    /* push == -1 !
	     * They did a throw() or error. That means that the control
	     * stack must be restored manually here.
	     */
	    csp = p->save_csp;	
	    pop_n_elems (sp - p->save_sp);
	    command_giver = p->save_command_giver;
	}
	memcpy ((char *)error_recovery_context,
		(char *)p->old_error_context,
		sizeof error_recovery_context);
	error_recovery_context_exists = p->old_exists_flag;
	ecsp = p->next;
	free ((char *)p);
    }
}

static struct vector *append_vector(v, a)
struct vector *v;
struct svalue *a;
{
   int i;
   struct vector *new;

   new = allocate_array(v->size+1);
   for (i=0; i<v->size; ++i)
      assign_svalue(&new->item[i], &v->item[i]);
   assign_svalue(&new->item[i], a);
   return new;
}

static struct vector *prepend_vector(v, a)
struct vector *v;
struct svalue *a;
{
   int i;
   struct vector *new;

   new = allocate_array(v->size+1);
   assign_svalue(&new->item[0], a);
   for (i=0; i<v->size; ++i)
      assign_svalue(&new->item[i+1], &v->item[i]);
   return new;
}

/*
 * When a vector is given as argument to an efun, all items has to be
 * checked if there would be an destructed object.
 * A bad problem currently is that a vector can contain another vector, so this
 * should be tested too. But, there is currently no prevention against
 * recursive vectors, which means that this can not be tested. Thus, the game
 * may crash if a vector contains a vector that contains a destructed object
 * and this top-most vector is used as an argument to an efun.
 */
/* The game won't crash when doing simple operations like assign_svalue
 * on a destructed object. You have to watch out, of course, that you don't
 * apply a function to it.
 * to save space it is preferable that destructed objects are freed soon.
 *   amylaar
 */
void check_for_destr(v)
    struct vector *v;
{
    int i;

    for (i=0; i < v->size; i++) {
	if (v->item[i].type != T_OBJECT)
	    continue;
	if (!(v->item[i].u.ob->flags & O_DESTRUCTED))
	    continue;
	assign_svalue(&v->item[i], &const0);
    }
}

/*
 * Evaluate instructions at address 'p'. All program offsets are
 * to current_prog->program. 'current_prog' must be setup before
 * call of this function.
 *
 * There must not be destructed objects on the stack. The destruct_object()
 * function will automatically remove all occurences. The effect is that
 * all called efuns knows that they won't have destructed objects as
 * arguments.
 */
#ifdef TRACE_CODE
int previous_instruction[60];
int stack_size[60];
char *previous_pc[60];
static int last;
#endif
static void eval_instruction(p)
    char *p;
{
    struct object *ob;
    int i, num_arg;
    int instruction;
#ifdef DEBUG
    struct svalue *expected_stack;
#endif
    struct svalue *argp;

    /* Next F_RETURN at this level will return out of eval_instruction() */
    csp->extern_call = 1;
    pc = p;
again:
    instruction = EXTRACT_UCHAR(pc);
#ifdef TRACE_CODE
    previous_instruction[last] = instruction + F_OFFSET;
    previous_pc[last] = pc;
    stack_size[last] = sp - fp - csp->num_local_variables;
    last = (last + 1) % (sizeof previous_instruction / sizeof (int));
#endif
    pc++;
    if (current_object && current_object->user)
	current_object->user->cost++;
    eval_cost++;
    if (eval_cost > MAX_COST) {
	printf("eval_cost too big %d\n", eval_cost);
        eval_cost = 0;
	error("Too long evaluation. Execution aborted.\n");
    }
    /*
     * Execute current instruction. Note that all functions callable
     * from LPC must return a value. This does not apply to control
     * instructions, like F_JUMP.
     */
    if (instrs[instruction].min_arg != instrs[instruction].max_arg) {
	num_arg = EXTRACT_UCHAR(pc);
	pc++;
	if (num_arg > 0) {
	    if (instrs[instruction].type[0] != 0 &&
		(instrs[instruction].type[0] & (sp-num_arg+1)->type) == 0) {
		bad_arg(1, instruction + F_OFFSET);
	    }
	}
	if (num_arg > 1) {
	    if (instrs[instruction].type[1] != 0 &&
		(instrs[instruction].type[1] & (sp-num_arg+2)->type) == 0) {
		bad_arg(2, instruction + F_OFFSET);
	    }
	}
    } else {
	num_arg = instrs[instruction].min_arg;
	if (instrs[instruction].min_arg > 0) {
	    if (instrs[instruction].type[0] != 0 &&
		(instrs[instruction].type[0] & (sp-num_arg+1)->type) == 0) {
		bad_arg(1, instruction + F_OFFSET);
	    }
	}
	if (instrs[instruction].min_arg > 1) {
	    if (instrs[instruction].type[1] != 0 &&
		(instrs[instruction].type[1] & (sp-num_arg+2)->type) == 0) {
		bad_arg(2, instruction + F_OFFSET);
	    }
	}
	/*
	 * Safety measure. It is supposed that the evaluator knows
	 * the number of arguments.
	 */
	num_arg = -1;
    }
#ifdef DEBUG
    if (num_arg != -1) {
	expected_stack = sp - num_arg + 1;
    } else {
	expected_stack = 0;
    }
#endif
    instruction += F_OFFSET;
#ifdef OPCPROF
    if (instruction >= 0 && instruction < MAXOPC) opcount[instruction]++;
#endif
    /*
     * Execute the instructions. The number of arguments are correct,
     * and the type of the two first arguments are also correct.
     */
    if (TRACEP(TRACE_EXEC)) {
	do_trace("Exec ", get_f_name(instruction), "\n");
    }
    switch(instruction) {
    default:
	fatal("Undefined instruction %s (%d)\n", get_f_name(instruction),
	      instruction);
	/*NOTREACHED*/
    CASE(F_REMOVE_SHADOW);
    {
        ob = current_object;
        if (num_arg) { ob = sp->u.ob; pop_stack(); }
        if (!ob || !ob->shadowing)
           push_number(0);
        else
        {
           if (ob->shadowed)
              ob->shadowed->shadowing = ob->shadowing;
           if (ob->shadowing)
              ob->shadowing->shadowed = ob->shadowed;
           ob->shadowing = ob->shadowed = 0;
           push_object(ob, "F_REMOVE_SHADOW");
        }
        break;
    }
    CASE(F_REGEXP);
      {
	struct vector *v;
	v = match_regexp((sp-1)->u.vec, sp->u.string);
	pop_n_elems(2);
	if (v == 0)
	    push_number(0);
	else {
	    push_vector(v);
	    v->ref--;		/* Will make ref count == 1 */
	}
	break;
    }
    CASE(F_SHADOW);
    {
	struct svalue *ret;
        struct object *top;

	top = ob = (sp-1)->u.ob;
	if (sp->u.number == 0) {
	    ob = ob->shadowed;
	    pop_n_elems(2);
            if (ob)
	       push_object(ob, "F_SHADOW");
            else push_number(0);
	    break;
 	}
	if (validate_shadowing(ob)) {
	   /*
	    * The shadow is entered first in the chain.
	    */
	    while (ob->shadowed)
	        ob = ob->shadowed;
	    current_object->shadowing = ob;
	    ob->shadowed = current_object;
            current_object->shadowed = 0;
	    pop_n_elems(2);
	    push_object(top, "F_SHADOW");
	}
        else
        {
	   pop_n_elems(2);
	   push_number(0);
        }
	break;
    }
    CASE(F_POP_VALUE);
	pop_stack();
	break;
    CASE(F_DUP);
	sp++;
	assign_svalue_no_free(sp, sp-1);
	break;
    CASE(F_JUMP_WHEN_ZERO);
    {
	unsigned short offset;

	((char *)&offset)[0] = pc[0];
	((char *)&offset)[1] = pc[1];
	if (sp->type == T_NUMBER && sp->u.number == 0)
	    pc = current_prog->program + offset;
	else
	    pc += 2;
	pop_stack();
	break;
    }
    CASE(F_JUMP);
    {
	unsigned short offset;

	((char *)&offset)[0] = pc[0];
	((char *)&offset)[1] = pc[1];
	pc = current_prog->program + offset;
	break;
    }
    CASE(F_JUMP_WHEN_NON_ZERO);
    {
	unsigned short offset;

	((char *)&offset)[0] = pc[0];
	((char *)&offset)[1] = pc[1];
	if (sp->type == T_NUMBER && sp->u.number == 0)
	    pc += 2;
	else
	    pc = current_prog->program + offset;
	pop_stack();
	break;
    }
    CASE(F_INDIRECT);
#ifdef DEBUG
	if (sp->type != T_LVALUE)
	    fatal("Bad type to F_INDIRECT\n");
#endif
	assign_svalue(sp, sp->u.lvalue);
	/*
	 * Fetch value of a variable. It is possible that it is a variable
	 * that points to a destructed object. In that case, it has to
	 * be replaced by 0.
	 */
	if (sp->type == T_OBJECT && (sp->u.ob->flags & O_DESTRUCTED)) {
	    free_svalue(sp, "F_INDIRECT");
            *sp = const0;
	}
	break;
    CASE(F_IDENTIFIER);
	sp++;
	assign_svalue_no_free(sp, find_value((int)(EXTRACT_UCHAR(pc) +
						   variable_index_offset)));
	pc++;
	/*
	 * Fetch value of a variable. It is possible that it is a variable
	 * that points to a destructed object. In that case, it has to
	 * be replaced by 0.
	 */
	if (sp->type == T_OBJECT && (sp->u.ob->flags & O_DESTRUCTED)) {
	    free_svalue(sp, "F_ID");
            *sp = const0;
	}
	break;
    CASE(F_PUSH_IDENTIFIER_LVALUE);
	sp++;
	sp->type = T_LVALUE;
	sp->u.lvalue = find_value((int)(EXTRACT_UCHAR(pc) +
					variable_index_offset));
	pc++;
	break; 
    CASE(F_PUSH_INDEXED_LVALUE);
	push_indexed_lvalue();
	break;
    CASE(F_INDEX_INSTR);
        if ((sp-1)->type == T_MAPPING) 
        {
            struct svalue *v;
            v = find_in_mapping((sp-1)->u.map, sp);
            pop_stack();
            if (v)
                assign_svalue(sp, v);
            else
                assign_svalue(sp, &const0);
        }
        else
        {
	    push_indexed_lvalue();
	    assign_svalue_no_free(sp, sp->u.lvalue);
        }
	/*
	 * Fetch value of a variable. It is possible that it is a variable
	 * that points to a destructed object. In that case, it has to
	 * be replaced by 0.
	 */
	if (sp->type == T_OBJECT && (sp->u.ob->flags & O_DESTRUCTED)) {
	    free_svalue(sp, "F_INDEX_INSTR");
	    sp->type = T_NUMBER;
	    sp->u.number = 0;
	}
	break;
    CASE(F_LOCAL_NAME);
	sp++;
	assign_svalue_no_free(sp, fp + EXTRACT_UCHAR(pc));
	pc++;
	/*
	 * Fetch value of a variable. It is possible that it is a variable
	 * that points to a destructed object. In that case, it has to
	 * be replaced by 0.
	 */
	if (sp->type == T_OBJECT && (sp->u.ob->flags & O_DESTRUCTED)) {
	    free_svalue(sp, "F_LOCAL_NAME");
	    *sp = const0;
	}
	break;
    CASE(F_PUSH_LOCAL_VARIABLE_LVALUE);
	sp++;
	sp->type = T_LVALUE;
	sp->u.lvalue = fp + EXTRACT_UCHAR(pc);
	pc++;
	break;
    CASE(F_RETURN);
    {
	struct svalue sv;

	sv = *sp--;
	/*
	 * Deallocate frame and return.
	 */
	for (i=0; i < csp->num_local_variables; i++)
	    pop_stack();
	sp++;
#ifdef DEBUG
	if (sp != fp)
	    fatal("Bad stack at F_RETURN\n"); /* marion */
#endif
	*sp = sv;	/* This way, the same ref counts are maintained */
	pop_control_stack();
	tracedepth--;
	if (TRACEP(TRACE_RETURN)) {
	    do_trace("Return", "", "");
	    if (TRACEHB) {
		if (TRACETST(TRACE_ARGS)) {
		    add_message(" with value: ");
		    print_svalue(sp);
		}
		add_message("\n");
	    }
	}
	if (csp[1].extern_call)	/* The control stack was popped just before */
	    return;
	break;
    }
    CASE(F_BREAK_POINT);
	break_point();	/* generated by lang.y when -d. Will check stack. */
	break;
    CASE(F_CHECK_REF);
    {
       int a = -1;
       if (num_arg == 2)
       {
          if ((sp-1)->type == T_OBJECT)
             a = (sp-1)->u.ob->ref;
       } 
       else if (sp->type == T_OBJECT)
          a = sp->u.ob->prog->ref;
       else if (sp->type == T_POINTER)
          a = sp->u.vec->ref;
       pop_n_elems(num_arg);
       push_number(a);
       break;
    }
    CASE(F_CLONE_OBJECT);
	ob = clone_object(sp->u.string);
	pop_stack();
	if (ob) {
            push_object(ob, "F_CLONE_OBJECT");
	} else {
	    push_number(0);
	}
	break;
    CASE(F_AGGREGATE_ASSOC);
    {
        struct mapping *m;
        unsigned short num;

        ((char *)&num)[0] = *pc++;
        ((char *)&num)[1] = *pc++;
        m = load_mapping_from_aggregate(sp - num + 1, num);
        pop_n_elems((int)num);
        sp++;
        sp->type = T_MAPPING;
        sp->u.map = m;
        break;
    }
    CASE(F_AGGREGATE);
    {
	struct vector *v;
	unsigned short num;

	((char *)&num)[0] = pc[0];
	((char *)&num)[1] = pc[1];
	pc += 2;
	v = allocate_array((int)num);
	for (i=0; i < num; i++)
	    assign_svalue_no_free(&v->item[i], sp + i - num + 1);
	pop_n_elems((int)num);
	sp++;
	sp->type = T_POINTER;
	sp->u.vec = v;		/* Ref count already initialized */
	break;
    }
    CASE(F_TAIL);
	if (tail(sp->u.string))
	    assign_svalue(sp, &const1);
	else
	    assign_svalue(sp, &const0);
	break;
    CASE(F_CALL_FUNCTION_BY_ADDRESS);
    {
	unsigned short func_index;
	struct function *funp;

	((char *)&func_index)[0] = pc[0];
	((char *)&func_index)[1] = pc[1];
	pc += 2;
	func_index += function_index_offset;
	/*
	 * Find the function in the function table. As the function may have
	 * been redefined by inheritance, we must look in the last table,
	 * which is pointed to by current_object.
	 */
#ifdef DEBUG
	if (func_index >= current_object->prog->num_functions)
	    fatal("Illegal function index\n");
#endif

	/* NOT current_prog, which can be an inherited object. */
	funp = &current_object->prog->functions[func_index];

	if (funp->flags & NAME_UNDEFINED)
	    error("Undefined function: %s\n", funp->name);
	/* Save all important global stack machine registers */
	push_control_stack(funp);	/* return pc is adjusted later */

	/* This assigment must be done after push_control_stack() */
	current_prog = current_object->prog;
	/*
	 * If it is an inherited function, search for the real
	 * definition.
	 */
	csp->num_local_variables = EXTRACT_UCHAR(pc);
	pc++;
	funp = setup_new_frame(funp);
	csp->pc = pc;	/* The corrected return address */
	pc = current_prog->program + funp->offset;
	csp->extern_call = 0;
	break;
    }
    CASE(F_SAVE_OBJECT);
	save_object(current_object, sp->u.string);
	/* The argument is returned */
	break;
    CASE(F_FIND_OBJECT);
	ob = find_object2(sp->u.string);
	pop_stack();
	if (ob)
	    push_object(ob, "F_FIND_OBJECT");
	else
	    push_number(0);
	break;
    CASE(F_FIND_PLAYER);
	ob = find_living_object(sp->u.string, 1);
	pop_stack();
	if (!ob)
	    push_number(0);
	else
	    push_object(ob, "F_FIND_PLAYER");
	break;
    CASE(F_DUMPOBJS);
    {  
        int start, end;
        if (num_arg == 1)
        {
           start = sp->u.number;
           end = -1;
        }
        else
        {
           start = (sp-1)->u.number;
           end = sp->u.number;
        }
        dumpobjs(start, end);
        pop_n_elems(num_arg);
        push_number(0);
        break;
    }
    CASE(F_WRITE_FILE);
	i = write_file((sp-1)->u.string, sp->u.string);
        pop_n_elems(2);
        push_number(i);
	break;	/* Return first argument */
    CASE(F_READ_BYTES);
    {
	char *str;
	struct svalue *arg = sp- num_arg + 1;
	int start = 0, len = 0;

	if (num_arg > 1)
	    start = arg[1].u.number;
	if (num_arg == 3) {
	    if (arg[2].type != T_NUMBER)
		bad_arg(2, instruction);
	    len = arg[2].u.number;
	}

	str = read_bytes(arg[0].u.string, start, len);
	pop_n_elems(num_arg);
	if (str == 0)
	    push_number(0);
	else {
	    push_string(str, STRING_MALLOC);
	    free(str);
	}
	break;
    }
    CASE(F_WRITE_BYTES);
	i = write_bytes((sp-2)->u.string, (sp-1)->u.number, sp->u.string);
	pop_n_elems(3);
	push_number(i);
	break;
    CASE(F_FILE_SIZE);
	i = file_size(sp->u.string);
	pop_stack();
	push_number(i);
	break;
    CASE(F_FIND_LIVING);
	ob = find_living_object(sp->u.string, 0);
	pop_stack();
	if (!ob)
	    push_number(0);
	else
	    push_object(ob, "F_FIND_LIVING");
	break;
    CASE(F_TELL_OBJECT);
	tell_object((sp-1)->u.ob, sp->u.string);
	pop_stack();	/* Return first argument */
	break;
    CASE(F_RESTORE_OBJECT);
	i = restore_object(current_object, sp->u.string);
	pop_stack();
	push_number(i);
	break;
    CASE(F_THIS_PLAYER);
        if (num_arg && current_interactive &&
           !(current_interactive->flags & O_DESTRUCTED) )
            push_object(current_interactive, "F_THIS_PLAYER"); 
	else if (command_giver && !(command_giver->flags & O_DESTRUCTED))
	    push_object(command_giver, "F_THIS_PLAYER");
	else
	    push_number(0);
	break;
#ifdef F_FIRST_INVENTORY
    CASE(F_FIRST_INVENTORY);
	ob = first_inventory(sp);
	pop_stack();
	if (ob)
	    push_object(ob, "F_FIRST_INVENTORY");
	else
	    push_number(0);
	break;
#endif /* F_FIRST_INVENTORY */
    CASE(F_LIVING);
	if (sp->u.ob->flags & O_ENABLE_COMMANDS)
	    assign_svalue(sp, &const1);
	else
	    assign_svalue(sp, &const0);
	break;
#ifdef F_GETUID
    CASE(F_GETUID);
	ob = sp->u.ob;
#ifdef DEBUG
	if (ob->user == 0)
	    fatal("User is null pointer\n");
#endif
	{   char *tmp;
	    tmp = ob->user->name;
	    pop_stack();
	    push_string(tmp, STRING_CONSTANT);
	}
	break;
#endif /* F_GETUID */
#ifdef F_GETEUID
    CASE(F_GETEUID);
	/*
	 * Are there any reasons to support this one in -o mode ?
	 */
	ob = sp->u.ob;

	if (ob->eff_user) {
	    char *tmp;
	    tmp = ob->eff_user->name;
	    pop_stack();
	    push_string(tmp, STRING_CONSTANT);
	}
	else {
	    pop_stack();
	    push_number(0);
	}
	break;
#endif /* F_GETEUID */
#ifdef F_EXPORT_UID
    CASE(F_EXPORT_UID);
	if (current_object->eff_user==0)
	    error("Illegal to export uid.\n");
	ob = sp->u.ob;
	if (ob->eff_user)	/* Only allowed to export when null */
	    break;
	ob->user = current_object->eff_user;
	break;
#endif /* F_EXPORT_UID */
#ifdef F_SETEUID
    CASE(F_SETEUID);
    {
	struct svalue *ret;

	if (sp->type == T_NUMBER) {
	    if (sp->u.number != 0)
		bad_arg(1, F_SETEUID);
	    current_object->eff_user = 0;
	    pop_stack();
	    push_number(1);
	    break;
	}
	argp = sp;
	if (argp->type != T_STRING)
	    bad_arg(1, F_SETEUID);
	push_object(current_object, "F_SETEUID");
	push_string(argp->u.string, STRING_CONSTANT);
	ret = apply_master_ob("valid_seteuid", 2);
	if (ret == 0 || ret->type != T_NUMBER || ret->u.number != 1) {
	    pop_stack();
	    push_number(0);
	    break;
	}
	current_object->eff_user = add_name(argp->u.string);
	pop_stack();
	push_number(1);
	break;
    }
#endif
#ifdef F_SETUID
    CASE(F_SETUID);
       setuid();
       push_number(0);
       break;
#endif
#ifdef F_CREATOR
    CASE(F_CREATOR);
	ob = sp->u.ob;
	if (ob->user == 0) {
	    assign_svalue(sp, &const0);
	} else {
	    pop_stack();
	    push_string(ob->user->name, STRING_CONSTANT);
	}
	break;
#endif
    CASE(F_SHUTDOWN);
    {
        extern struct object *master_ob;
        assert_master_ob_loaded();
        if (current_object != master_ob)
            error("shutdown() can only be called from master.c\n");
	startshutdowngame();
	push_number(0);
	break;
    }
    CASE(F_EXPLODE);
    {
	struct vector *v;
	v = explode_string((sp-1)->u.string, sp->u.string);
	pop_n_elems(2);
	if (v) {
	    push_vector(v);	/* This will make ref count == 2 */
	    v->ref--;
	} else {
	    push_number(0);
	}
	break;
    }
    CASE(F_FILTER_ARRAY);
    {
	struct vector *v;
	struct svalue *arg;

	arg = sp - num_arg + 1; ob = 0;

        if (arg[2].type == T_OBJECT)
            ob = arg[2].u.ob;
        else if (arg[2].type == T_STRING)
            ob = find_object(arg[2].u.string); 
           
        if (!ob)
	    error("Bad type argument 3 to filter_array()\n");

	if (arg[0].type == T_POINTER) {
	    check_for_destr(arg[0].u.vec);
	    v = filter(arg[0].u.vec, arg[1].u.string, ob,
		       num_arg > 3 ? sp : (struct svalue *)0); 
	} else {
	    v = 0;
	}
	
	pop_n_elems(num_arg);
	if (v) {
	    push_vector(v); /* This will make ref count == 2 */
	    v->ref--;
	} else {
	    push_number(0);
	}
	break;
    }
    CASE(F_SET_BIT);
    {
	char *str;
	int len, old_len, ind;

	if (sp->u.number > MAX_BITS)
	    error("set_bit: too big bit number: %d\n", sp->u.number);
	len = strlen((sp-1)->u.string);
	old_len = len;
	ind = sp->u.number/6;
	if (ind >= len)
	    len = ind + 1;
	str = xalloc(len+1);
	str[len] = '\0';
	if (old_len)
	    memcpy(str, (sp-1)->u.string, old_len);
	if (len > old_len)
	    memset(str + old_len, ' ', len - old_len);
	if (str[ind] > 0x3f + ' ' || str[ind] < ' ')
	    error("Illegal bit pattern in set_bit character %d\n", ind);
	str[ind] = (str[ind] - ' ' | 1 << sp->u.number % 6) + ' ';
	pop_n_elems(2);
	sp++;
	sp->u.string = str;
	sp->string_type = STRING_MALLOC;
	sp->type = T_STRING;
	break;
    }
    CASE(F_CLEAR_BIT);
    {
	char *str;
	int len, ind;

	if (sp->u.number > MAX_BITS)
	    error("clear_bit: too big bit number: %d\n", sp->u.number);
	len = strlen((sp-1)->u.string);
	ind = sp->u.number/6;
	if (ind >= len) {
	    /* Return first argument unmodified ! */
	    pop_stack();
	    break;
	}
	str = xalloc(len+1);
	memcpy(str, (sp-1)->u.string, len+1);	/* Including null byte */
	if (str[ind] > 0x3f + ' ' || str[ind] < ' ')
	    error("Illegal bit pattern in clear_bit character %d\n", ind);
	str[ind] = (str[ind] - ' ' & ~(1 << sp->u.number % 6)) + ' ';
	pop_n_elems(2);
	sp++;
	sp->type = T_STRING;
	sp->string_type = STRING_MALLOC;
	sp->u.string = str;
	break;
    }
    CASE(F_TEST_BIT);
    {
	int len;

	len = strlen((sp-1)->u.string);
	if (sp->u.number/6 >= len) {
	    pop_n_elems(2);
	    push_number(0);
	    break;
	}
	if ((sp-1)->u.string[sp->u.number/6] - ' ' & 1 << sp->u.number % 6) {
	    pop_n_elems(2);
	    push_number(1);
	} else {
	    pop_n_elems(2);
	    push_number(0);
	}
	break;
    }
    CASE(F_QUERY_LOAD_AVERAGE);
	push_string(query_load_av(), STRING_SHARED); /**MALLOC**/
	break;
    CASE(F_CATCH);
	/*
	 * Catch/Throw - catch errors in system or other peoples routines.
	 */
    {
	extern jmp_buf error_recovery_context;
	extern int error_recovery_context_exists;
	extern struct svalue catch_value;
	unsigned short new_pc_offset;

	/*
	 * Compute address of next instruction after the CATCH statement.
	 */
	((char *)&new_pc_offset)[0] = pc[0];
	((char *)&new_pc_offset)[1] = pc[1];
	pc += 2;

	push_control_stack((struct function *)0);
	csp->num_local_variables = 0;	/* No extra variables */
	csp->pc = current_prog->program + new_pc_offset;
	csp->num_local_variables = (csp-1)->num_local_variables; /* marion */
	/*
	 * Save some global variables that must be restored separately
	 * after a longjmp. The stack will have to be manually popped all
	 * the way.
	 */
	push_pop_error_context (1);
	
	/* signal catch OK - print no err msg */
   	error_recovery_context_exists = 2;
	if (setjmp(error_recovery_context)) {
	    /*
	     * They did a throw() or error. That means that the control
	     * stack must be restored manually here.
	     * Restore the value of expected_stack also. It is always 0
	     * for catch().
	     */
#ifdef DEBUG
	    expected_stack = 0;
#endif
	    push_pop_error_context (-1);
	    pop_control_stack();
	    assign_svalue_no_free(++sp, &catch_value);
	}

	/* next error will return 1 by default */
	assign_svalue(&catch_value, &const1);
	break;
    }
    CASE(F_THROW);
	/* marion
	 * the return from catch is now done by a 0 throw
	 */
	assign_svalue(&catch_value, sp--);
	if (catch_value.type == T_NUMBER && catch_value.u.number == 0) {
	    /* We come here when no longjmp() was executed. */
	    pop_control_stack();
	    push_pop_error_context (0);
	    push_number(0);
	} else throw_error(); /* do the longjump, with extra checks... */
	break;
    CASE(F_NOTIFY_FAIL);
	set_notify_fail_message(sp->u.string);
	/* Return the argument */
	break;
    CASE(F_QUERY_IDLE);
	i = query_idle(sp->u.ob);
	pop_stack();
	push_number(i);
	break;
    CASE(F_IMPLODE);
    {
	char *str;
	check_for_destr((sp-1)->u.vec);
	str = implode_string((sp-1)->u.vec, sp->u.string);
	pop_n_elems(2);
	if (str) {
	    sp++;
	    sp->type = T_STRING;
	    sp->string_type = STRING_SHARED;
	    sp->u.string = make_shared_string(str); /**MALLOC**/
            free(str);
	} else {
	    push_number(0);
	}
	break;
    }
    CASE(F_QUERY_SNOOP);
    {
	struct svalue *arg1;
        extern struct object *master_ob;

	if (command_giver == 0 || sp->u.ob->interactive == 0) {
	    assign_svalue(sp, &const0);
	    break;
	}

#ifdef COMPAT_MODE
	arg1 = sapply("query_level", command_giver, 0);
	if (arg1 == 0 || arg1->type != T_NUMBER || 
            arg1->u.number < QUERY_SNOOP_LEVEL) 
        {
	    assign_svalue(sp, &const0);
	    break;
	} 
	ob = query_snoop(sp->u.ob);
#else
	assert_master_ob_loaded();
	if (current_object == master_ob)
	    ob = query_snoop(sp->u.ob);
	else
	    ob = 0;
#endif
        pop_stack();
	if (ob)
	    push_object(ob, "F_QUERY_SNOOP");
	else
	    push_number(0);
	break;
    }
    CASE(F_QUERY_IP_NUMBER);
    CASE(F_QUERY_IP_NAME);
    {
	extern char *query_ip_number PROT((struct object *));
	extern char *query_ip_name PROT((struct object *));
	char *tmp;

	if (num_arg == 1 && sp->type != T_OBJECT)
	    error("Bad optional argument to query_ip_number()\n");
        if (instruction == F_QUERY_IP_NAME)
	    tmp = query_ip_name(num_arg ? sp->u.ob : 0);
	else
	    tmp = query_ip_number(num_arg ? sp->u.ob : 0);
	   
	if (num_arg)
	    pop_stack();
	if (tmp == 0)
	    push_number(0);
	else
	    push_string(tmp, STRING_SHARED); /**MALLOC**/
	break;
    }
    CASE(F_QUERY_HOST_NAME);
    {
	extern char *query_host_name();
	char *tmp;

	tmp = query_host_name();
	if (tmp)
	    push_string(tmp, STRING_CONSTANT);
	else
	    push_number(0);
	break;
    }
#ifdef F_NEXT_INVENTORY
    CASE(F_NEXT_INVENTORY);
	ob = sp->u.ob;
	pop_stack();
	if (ob->next_inv)
	    push_object(ob->next_inv, "F_NEXT_INVENTORY");
	else
	    push_number(0);
	break;
#endif /* F_NEXT_INVENTORY */
    CASE(F_ALL_INVENTORY);
    case F_CONTENTS:
    {
	struct vector *vec;
        struct object *ob = current_object;

        if (num_arg) 
        {
            if (sp->type == T_OBJECT)
               ob = sp->u.ob;
            else 
               ob = find_object(sp->u.string);
            pop_stack();
            if (!ob) 
            {
               push_number(0); 
               break;
            } 
        }
                 
	vec = all_inventory(ob);
	if (vec == 0) {
	    push_number(0);
	} else {
	    push_vector(vec); /* This will make ref count == 2 */
	    vec->ref--;
	}
	break;
    }
    CASE(F_DEEP_INVENTORY);
    {
	struct vector *vec;

	vec = deep_inventory(sp->u.ob, 0);
	free_svalue(sp, "F_DEEP_INVENTORY");
	sp->type = T_POINTER;
	sp->u.vec = vec;
	break;
    }
    CASE(F_ENVIRONMENT);
	if (num_arg) {
	    ob = environment(sp);
	    pop_stack();
	} else if (!(current_object->flags & O_DESTRUCTED)) {
	    ob = current_object->super;
	} else
	    ob = 0;
	if (ob)
        {
	    push_object(ob, "F_ENVIRONMENT");
        }
	else
	    push_number(0);
	break;
    CASE(F_THIS_OBJECT);
        if (current_object->flags & O_DESTRUCTED)
           push_number(0);
        else
	   push_object(current_object, "F_THIS_OBJECT");
	break;
    CASE(F_PREVIOUS_OBJECT);
	if (previous_ob == 0 || (previous_ob->flags & O_DESTRUCTED))
	    push_number(0);
	else
	    push_object(previous_ob, "F_PREVIOUS_OBJECT");
	break;
#ifdef F_LOCALCMD
    CASE(F_LOCALCMD);
	print_local_commands();
	push_number(0);
	break;
#endif /* F_LOCALCMD */
    CASE(F_SWAP);
	(void)swap(sp->u.ob);
	break;
    CASE(F_TRACE);
    {
        int ot = -1;
        if (command_giver && command_giver->interactive) {
    	    struct svalue *arg;
	    push_constant_string("trace");
	    arg = apply_master_ob("query_player_level", 1);
	    if (arg && (arg->type != T_NUMBER || arg->u.number != 0)) {
	        ot = command_giver->interactive->trace_level;
	        command_giver->interactive->trace_level = sp->u.number;
	    }
	}
        pop_stack();
        push_number(ot);
    }
    break;
    CASE(F_TRACEPREFIX);
	{
	    char *old = 0;

	    if (command_giver && command_giver->interactive) {
		struct svalue *arg;
		push_constant_string("trace");
		arg = apply_master_ob("query_player_level",1);
		if (arg && (arg->type != T_NUMBER || arg->u.number)) {
		    old = command_giver->interactive->trace_prefix;
		    if (sp->type == T_STRING) {
		        command_giver->interactive->trace_prefix = 
			    make_shared_string(sp->u.string);
                    } else
		        command_giver->interactive->trace_prefix = 0;
		}
	    }
	    pop_stack();
	    if (old) {
		push_string(old, STRING_SHARED);   /* Will incr ref count */
		free_string(old);
	    } else {
		push_number(0);
	    }
	}
        break;
    CASE(F_TIME);
	push_number(current_time);
	break;
    CASE(F_WIZLIST);
	if (num_arg) {
	    wizlist(sp->u.string);
	} else {
	    wizlist((char *)0);
	    push_number(0);
	}
	break;
#ifdef F_TRANSFER
    CASE(F_TRANSFER);
    {
	struct object *dest;

	if (sp->type == T_STRING) {
	    dest = find_object(sp->u.string);
	    if (dest == 0)
		error("Object not found.\n");
	} else {
	    dest = sp->u.ob;
	}
	i = transfer_object((sp-1)->u.ob, dest);
	pop_n_elems(2);
	push_number(i);
	break;
    }
#endif
#ifdef F_ADD_WORTH
    CASE(F_ADD_WORTH);
	if (strncmp(current_object->name, "obj/", 4) != 0 &&
	    strncmp(current_object->name, "std/", 4) != 0 &&
	    strncmp(current_object->name, "room/", 5) != 0)
	    error("Illegal call of add_worth.\n");
	if (num_arg == 2) {
	    if (sp->u.ob->user)
		sp->u.ob->user->total_worth += (sp-1)->u.number;
	    pop_stack();
	} else {
	    if (previous_ob == 0)
		break;
	    if (previous_ob->user)
		previous_ob->user->total_worth += sp->u.number;
	}
	break;
#endif /* F_ADD_WORTH */
    CASE(F_ADD);
    {
       struct svalue ret;
       switch((sp-1)->type)
       {
          case T_STRING:
          {
             switch(sp->type)
             {
                case T_STRING:
                {
                   char *res;
	           res=xalloc(strlen((sp-1)->u.string)+strlen(sp->u.string)+1);
	           (void)strcpy(res, (sp-1)->u.string);
	           (void)strcat(res, sp->u.string);
                   ret.type = T_STRING;
                   ret.string_type = STRING_SHARED;
                   ret.u.string = make_shared_string(res);
                   free(res);
                   break;
                } 
                case T_NUMBER:
                {
	           char buff[20];
	           char *res;
	           sprintf(buff, "%d", sp->u.number);
	           res = xalloc(strlen((sp-1)->u.string) + strlen(buff) + 1);
	           strcpy(res, (sp-1)->u.string);
	           strcat(res, buff);
                   ret.type = T_STRING;
                   ret.string_type = STRING_SHARED;
                   ret.u.string = make_shared_string(res);
                   free(res);
                   break;
                }
                case T_POINTER:
                   ret.type = T_POINTER;
                   ret.u.vec = prepend_vector(sp->u.vec, sp-1);
                   break;
                default: error("Bad type argument to +. %d %d\n", 
                   (sp-1)->type, sp->type);
             }
             break;
          } 
          case T_NUMBER:
          {
             ret.type = sp->type;
             switch(sp->type)
             {
                case T_NUMBER:
                   ret.u.number = sp->u.number + (sp-1)->u.number;
                   break;
                case T_STRING:
                {
	           char buff[20], *res;
	           sprintf(buff, "%d", (sp-1)->u.number);
	           res = xalloc(strlen(sp->u.string) + strlen(buff) + 1);
	           strcpy(res, buff);
	           strcat(res, sp->u.string);
                   ret.string_type = STRING_SHARED;
                   ret.u.string = make_shared_string(res);
                   free(res);
                   break;
                }
                case T_POINTER:
                   ret.u.vec = prepend_vector(sp->u.vec, sp-1);
                   break;
                default: error("Bad type argument to +. %d %d\n", 
                   (sp-1)->type, sp->type);
             }
             break;
          }
          case T_POINTER:
          {
             ret.type = T_POINTER;
             switch(sp->type)
             {
                case T_POINTER:
                   ret.u.vec = add_array((sp-1)->u.vec, sp->u.vec);
                   break;
                case T_NUMBER:
                case T_STRING:
                   ret.u.vec = append_vector((sp-1)->u.vec, sp);
                   break;
                default: error("Bad type argument to +. %d %d\n", 
                   (sp-1)->type, sp->type);
             }
             break;
          } 
          case T_MAPPING:
          {
              switch(sp->type)
              {
                  case T_MAPPING:
                     ret.type = T_MAPPING;
                     ret.u.map = add_mapping((sp-1)->u.map, sp->u.map);
                     break;
                  default: 
                     error("Bad type argument to +. %d %d\n", 
                           (sp-1)->type, sp->type);
              }
              break;
          }
          default: error("Bad type argument to +. %d %d\n", 
                         (sp-1)->type, sp->type);
       }
       pop_n_elems(2);
       push_svalue(&ret);
       free_svalue(&ret, "F_ADD");
       break;
    }      
    CASE(F_SUBTRACT);
    {
       struct svalue ret;
       ret.type = (sp-1)->type;
       switch((sp-1)->type)
       {
          case T_NUMBER:
          {
             switch(sp->type)
             {
                case T_NUMBER:    
                   ret.u.number = (sp-1)->u.number - sp->u.number;
                   break;
                default:
                   bad_arg(2, F_SUBTRACT);
                   break;
             }
             break;
          }
          case T_POINTER:
#ifdef COMPAT_MODE
                ret.u.vec = subtract_array((sp-1)->u.vec, sp);
#else
             {
		if (sp->type == T_POINTER) {
		    struct vector *v;
	
		    v = sp->u.vec;
		    if (v->ref > 1) {
			v = slice_array(v, 0, v->size-1 );
			v->ref--;
	            }
		    sp--;
		    v = subtract_array(sp->u.vec, v);
		    free_vector(sp->u.vec);
		    sp->u.vec = v;
		}
                else bad_arg(2, F_SUBTRACT);
             }
#endif
             break;
          default: 
             bad_arg(1, F_SUBTRACT);
       }
       pop_n_elems(2);
       push_svalue(&ret);
       free_svalue(&ret, "F_SUBTRACT");
       break; 
    }
    CASE(F_AND);
	if (sp->type == T_POINTER && (sp-1)->type == T_POINTER) {
	    extern struct vector *intersect_array
	      PROT((struct vector *, struct vector *));
	    (sp-1)->u.vec = intersect_array(sp->u.vec, (sp-1)->u.vec);
	    sp--;
	    break;
	}
	if ((sp-1)->type != T_NUMBER)
	    bad_arg(1, F_AND);
	if (sp->type != T_NUMBER)
	    bad_arg(2, F_AND);
	i = (sp-1)->u.number & sp->u.number;
	sp--;
	sp->u.number = i;
	break;
    CASE(F_OR);
	if ((sp-1)->type != T_NUMBER)
	    bad_arg(1, F_OR);
	if (sp->type != T_NUMBER)
	    bad_arg(2, F_OR);
	i = (sp-1)->u.number | sp->u.number;
	sp--;
	sp->u.number = i;
	break;
    CASE(F_XOR);
	if ((sp-1)->type != T_NUMBER)
	    bad_arg(1, instruction);
	if (sp->type != T_NUMBER)
	    bad_arg(2, instruction);
	i = (sp-1)->u.number ^ sp->u.number;
	sp--;
	sp->u.number = i;
	break;
    CASE(F_LSH);
	if ((sp-1)->type != T_NUMBER)
	    bad_arg(1, instruction);
	if (sp->type != T_NUMBER)
	    bad_arg(2, instruction);
	i = (sp-1)->u.number << sp->u.number;
	sp--;
	sp->u.number = i;
	break;
    CASE(F_RSH);
	if ((sp-1)->type != T_NUMBER)
	    bad_arg(1, instruction);
	if (sp->type != T_NUMBER)
	    bad_arg(2, instruction);
	i = (sp-1)->u.number >> sp->u.number;
	sp--;
	sp->u.number = i;
	break;
    CASE(F_MULTIPLY);
        if ((sp-1)->type != sp->type)
            bad_arg(1, instruction);
        if (sp->type == T_NUMBER)
        {
           i = (sp-1)->u.number * sp->u.number;
           sp--;
           sp->u.number = i;
        }
        else if (sp->type == T_MAPPING)
        {
            struct mapping *m;
              
            m = compose_mapping((sp-1)->u.map, sp->u.map);
            pop_n_elems(2);
            push_mapping(m);
        }
        else
	    bad_arg(2, instruction);
	break;
    CASE(F_DIVIDE);
	if ((sp-1)->type != T_NUMBER)
	    bad_arg(1, instruction);
	if (sp->type != T_NUMBER)
	    bad_arg(2, instruction);
	if (sp->u.number == 0)
	    error("Division by zero\n");
	i = (sp-1)->u.number / sp->u.number;
	sp--;
	sp->u.number = i;
	break;
    CASE(F_MOD);
	if ((sp-1)->type != T_NUMBER)
	    bad_arg(1, instruction);
	if (sp->type != T_NUMBER)
	    bad_arg(2, instruction);
	if (sp->u.number == 0)
	    error("Modulus by zero.\n");
	i = (sp-1)->u.number % sp->u.number;
	sp--;
	sp->u.number = i;
	break;
    CASE(F_GT);
	if ((sp-1)->type == T_STRING && sp->type == T_STRING) {
	    i = strcmp((sp-1)->u.string, sp->u.string) > 0;
	    pop_n_elems(2);
	    push_number(i);
	    break;
	}
	if ((sp-1)->type != T_NUMBER)
	    bad_arg(1, instruction);
	if (sp->type != T_NUMBER)
	    bad_arg(2, instruction);
	i = (sp-1)->u.number > sp->u.number;
	sp--;
	sp->u.number = i;
	break;
    CASE(F_GE);
	if ((sp-1)->type == T_STRING && sp->type == T_STRING) {
	    i = strcmp((sp-1)->u.string, sp->u.string) >= 0;
	    pop_n_elems(2);
	    push_number(i);
	    break;
	}
	if ((sp-1)->type != T_NUMBER)
	    bad_arg(1, instruction);
	if (sp->type != T_NUMBER)
	    bad_arg(2, instruction);
	i = (sp-1)->u.number >= sp->u.number;
	sp--;
	sp->u.number = i;
	break;
    CASE(F_LT);
	if ((sp-1)->type == T_STRING && sp->type == T_STRING) {
	    i = strcmp((sp-1)->u.string, sp->u.string) < 0;
	    pop_n_elems(2);
	    push_number(i);
	    break;
	}
	if ((sp-1)->type != T_NUMBER)
	    bad_arg(1, instruction);
	if (sp->type != T_NUMBER)
	    bad_arg(2, instruction);
	i = (sp-1)->u.number < sp->u.number;
	sp--;
	sp->u.number = i;
	break;
    CASE(F_LE);
	if ((sp-1)->type == T_STRING && sp->type == T_STRING) {
	    i = strcmp((sp-1)->u.string, sp->u.string) <= 0;
	    pop_n_elems(2);
	    push_number(i);
	    break;
	}
	if ((sp-1)->type != T_NUMBER)
	    bad_arg(1, instruction);
	if (sp->type != T_NUMBER)
	    bad_arg(2, instruction);
	i = (sp-1)->u.number <= sp->u.number;
	sp--;
	sp->u.number = i;
	break;
    CASE(F_EQ);
	if ((sp-1)->type != sp->type) {
	    pop_stack();
	    assign_svalue(sp, &const0);
	    break;
	}
	switch(sp->type) {
	case T_NUMBER:
	    i = (sp-1)->u.number == sp->u.number;
	    break;
	case T_POINTER:
	    i = (sp-1)->u.vec == sp->u.vec;
	    break;
	case T_STRING:
	    i = strcmp((sp-1)->u.string, sp->u.string) == 0;
	    break;
	case T_OBJECT:
	    i = (sp-1)->u.ob == sp->u.ob;
	    break;
	default:
	    i = 0;
	    break;
	}
	pop_n_elems(2);
	push_number(i);
	break;
    CASE(F_NE);
	if ((sp-1)->type != sp->type) {
	    pop_stack();
	    assign_svalue(sp, &const1);
	    break;
	}
	switch(sp->type) {
	case T_NUMBER:
	    i = (sp-1)->u.number != sp->u.number;
	    break;
	case T_STRING:
	    i = strcmp((sp-1)->u.string, sp->u.string);
	    break;
	case T_POINTER:
	    i = (sp-1)->u.vec != sp->u.vec;
	    break;
	case T_OBJECT:
	    i = (sp-1)->u.ob != sp->u.ob;
	    break;
	default:
	    fatal("Illegal type to !=\n");
	}
	pop_n_elems(2);
	push_number(i);
	break;
#ifdef F_LOG_FILE
    CASE(F_LOG_FILE);
	log_file((sp-1)->u.string, sp->u.string);
	pop_stack();
	break;	/* Return first argument */
#endif /* F_LOG_FILE */
    CASE(F_NOT);
	if (sp->type == T_NUMBER && sp->u.number == 0)
	    sp->u.number = 1;
	else
	    assign_svalue(sp, &const0);
	break;
    CASE(F_COMPL);
	if (sp->type != T_NUMBER)
	    error("Bad argument to ~\n");
	sp->u.number = ~ sp->u.number;
	break;
    CASE(F_NEGATE);
	if (sp->type != T_NUMBER)
	    error("Bad argument to unary minus\n");
	sp->u.number = - sp->u.number;
	break;
    CASE(F_INC);
	if (sp->type != T_LVALUE)
	    error("Bad argument to ++\n");
	if (sp->u.lvalue->type != T_NUMBER)
	    error("++ of non-numeric argument\n");
	sp->u.lvalue->u.number++;
	assign_svalue(sp, sp->u.lvalue);
	break;
    CASE(F_DEC);
	if (sp->type != T_LVALUE)
	    error("Bad argument to --\n");
	if (sp->u.lvalue->type != T_NUMBER)
	    error("-- of non-numeric argument\n");
	sp->u.lvalue->u.number--;
	assign_svalue(sp, sp->u.lvalue);
	break;
    CASE(F_POST_INC);
	if (sp->type != T_LVALUE)
	    error("Bad argument to ++\n");
	if (sp->u.lvalue->type != T_NUMBER)
	    error("++ of non-numeric argument\n");
	sp->u.lvalue->u.number++;
	assign_svalue(sp, sp->u.lvalue);
	sp->u.number--;
	break;
    CASE(F_POST_DEC);
	if (sp->type != T_LVALUE)
	    error("Bad argument to --\n");
	if (sp->u.lvalue->type != T_NUMBER)
	    error("-- of non-numeric argument\n");
	sp->u.lvalue->u.number--;
	assign_svalue(sp, sp->u.lvalue);
	sp->u.number++;
	break;
    CASE(F_CALL_OTHER);
    {
	struct svalue *arg, tmp;
	
	arg = sp - num_arg + 1;
	if (arg[0].type == T_OBJECT)
	    ob = arg[0].u.ob;
	else {
	    ob = find_object(arg[0].u.string);
	    if (ob == 0)
                error("Could not find object %s\n", arg[0].u.string);
       /* Raistlin -- Was this 
		fatal("find_object() returned 0\n");  */
	}

	if ((current_object->flags & O_DESTRUCTED) ||
	  (ob->flags & O_DESTRUCTED)) {
	    /*
	     * No external calls may be done when this object is
	     * destructed.
	     */
	    pop_n_elems(num_arg);
	    push_number(0);
	    break;
	}
	if (arg[1].u.string[0] == ':')
	    error("Illegal function name in call_other: %s\n",
		  arg[1].u.string);
	/*
	 * Send the remaining arguments to the function.
	 */
	if (TRACEP(TRACE_CALL_OTHER)) {
	    do_trace("Call other ", arg[1].u.string, "\n");
	}
	if (apply_low(arg[1].u.string, ob, num_arg-2) == 0) {
	    /* Function not found */
	    pop_n_elems(2);
	    push_number(0);
	    break;
	}
	/*
	 * The result of the function call is on the stack. But, so
	 * is the function name and object that was called.
	 * These have to be removed.
	 */
	tmp = *sp--;	/* Copy the function call result */
	pop_n_elems(2);	/* Remove old arguments to call_other */
	*++sp = tmp;	/* Re-insert function result */
	break;
    }
    CASE(F_INTP);
	if (sp->type == T_NUMBER)
	    assign_svalue(sp, &const1);
	else
	    assign_svalue(sp, &const0);
	break;
    CASE(F_STRINGP);
	if (sp->type == T_STRING)
	    assign_svalue(sp, &const1);
	else
	    assign_svalue(sp, &const0);
	break;
    CASE(F_OBJECTP);
	if (sp->type == T_OBJECT)
	    assign_svalue(sp, &const1);
	else
	    assign_svalue(sp, &const0);
	break;
    CASE(F_POINTERP);
	if (sp->type == T_POINTER)
	    assign_svalue(sp, &const1);
	else
	    assign_svalue(sp, &const0);
	break;
    CASE(F_MAPP);
        if (sp->type == T_MAPPING)
            assign_svalue(sp, &const1);
        else
            assign_svalue(sp, &const0);
        break;
    CASE(F_EXTRACT);
    {
	int len, from, to;
	struct svalue *arg;
	char *res;

	arg = sp - num_arg + 1;
	if (num_arg == 1)
	    break;		/* Simply return argument */
        if (arg[0].type == T_POINTER)
        {
           struct vector *r;
           int from, to, i; 
           
           from = arg[1].u.number;
           if (num_arg == 3) to = arg[2].u.number;
           else to = arg[0].u.vec->size-1;

           if (from > arg[0].u.vec->size-1 || to < from) {
              pop_n_elems(num_arg);
              push_number(0);
              break;
           }  
           if (to > arg[0].u.vec->size-1)
              to = arg[0].u.vec->size-1;
           r = allocate_array(to - from + 1);
           for (i = from; i <= to; ++i)
              assign_svalue(&r->item[i-from], &arg[0].u.vec->item[i]);
           pop_n_elems(num_arg);
           push_vector(r);
           r->ref--;
           break;
        } 
	len = strlen(arg[0].u.string);
	from = arg[1].u.number;
	if (from < 0)
	    from = len + from;
	if (from >= len) {
	    pop_n_elems(num_arg);
	    push_string("", STRING_CONSTANT);
	    break;
	}
	if (num_arg == 2) {
	    res = make_shared_string(arg->u.string + from);
	    pop_n_elems(2);
	    push_shared_string(res); /**MALLOC**/
	    break;
	}
	if (arg[2].type != T_NUMBER)
	    error("Bad third argument to extract()\n");
	to = arg[2].u.number;
	if (to < 0)
	    to = len + to;
	if (to < from) {
	    pop_n_elems(3);
	    push_string("", STRING_CONSTANT);
	    break;
	}
	if (to >= len)
	    to = len-1;
	if (to == len-1) {
	    res = make_shared_string(arg->u.string + from);
	    pop_n_elems(3);
	    push_shared_string(res); /**MALLOC**/
	    break;
	}
	res = xalloc(to - from + 2);
	strncpy(res, arg[0].u.string + from, to - from + 1);
	res[to - from + 1] = '\0';
	pop_n_elems(3);
	push_shared_string(make_shared_string(res)); /*MALLOC*/
        free(res);
	break;
    }
    CASE(F_ATOI);
    {
       int i = atoi(sp->u.string);
       pop_stack();
       push_number(i);
       break;
    }
    CASE(F_RANGE);
    {
	if (sp[-1].type != T_NUMBER)
	    error("Bad type of start interval to [ .. ] range.\n");
	if (sp[0].type != T_NUMBER)
	    error("Bad type of end interval to [ .. ] range.\n");
	if (sp[-2].type == T_POINTER) {
	    struct vector *v;

	    v = slice_array(sp[-2].u.vec, sp[-1].u.number, sp[0].u.number);
	    pop_n_elems(3);
	    if (v) {
		push_vector(v);
		v->ref--;	/* Will make ref count == 1 */
	    } else {
		push_number(0);
	    }
	} else if (sp[-2].type == T_STRING) {
	    int len, from, to;
	    char *res;
	    
	    len = strlen(sp[-2].u.string);
	    from = sp[-1].u.number;
	    if (from < 0)
		from = len + from;
	    if (from >= len) {
		pop_n_elems(3);
		push_string("", STRING_CONSTANT);
		break;
	    }
	    to = sp[0].u.number;
	    if (to < 0)
		to = len + to;
	    if (to < from) {
		pop_n_elems(3);
		push_string("", STRING_CONSTANT);
		break;
	    }
	    if (to >= len)
		to = len-1;
	    if (to == len-1) {
		res = string_copy(sp[-2].u.string + from);
		pop_n_elems(3);
		push_malloced_string(res);
		break;
	    }
	    res = xalloc(to - from + 2);
	    strncpy(res, sp[-2].u.string + from, to - from + 1);
	    res[to - from + 1] = '\0';
	    pop_n_elems(3);
	    push_malloced_string(res);
	} else {
	    error("Bad argument to [ .. ] range operand.\n");
	}
	break;
    }
    CASE(F_QUERY_ARG);
    case F_THIS_ARG:
	if (last_arg == 0) {
	    push_number(0);
	    break;
	}
	push_string(last_arg, STRING_CONSTANT);
	break;
    CASE(F_QUERY_VERB);
    case F_THIS_VERB:
	if (last_verb == 0) {
	    push_number(0);
	    break;
	}
	push_string(last_verb, STRING_CONSTANT);
	break;
    CASE(F_EXEC);
	i =replace_interactive((sp-1)->u.ob, sp->u.ob, current_prog->name);
	pop_n_elems(2);
	push_number(i);
	break;

    CASE(F_FILE_NAME);
    {
	char *name,*res;

	name = sp->u.ob->name;
#ifdef COMPAT_MODE
	    res = string_copy(name); 
#else
	    res = add_slash(name);
#endif
	pop_stack();
	push_shared_string(make_shared_string(res)); /*MALLOC*/
        free(res);
	break;
    }
    CASE(F_USERS);
	push_vector(users());	/* users() has already set ref count to 1 */
	sp->u.vec->ref--;
	break;
    CASE(F_CALL_OUT);
	{
	    struct svalue *arg = sp - num_arg + 1;

	    if (!(current_object->flags & O_DESTRUCTED))
		new_call_out(current_object, arg[0].u.string, arg[1].u.number,
			     num_arg == 3 ? sp : 0);
	    pop_n_elems(num_arg);
	    push_number(0);
	}
	break;
    CASE(F_CALL_OUT_INFO);
	push_vector(get_all_call_outs());
	sp->u.vec->ref--;	/* Was set to 1 at allocation */
	break;
    CASE(F_REMOVE_CALL_OUT);
	i = remove_call_out(current_object, sp->u.string);
	pop_stack();
	push_number(i);
	break;
#ifdef F_INHERIT_LIST
    CASE(F_INHERIT_LIST)
    {
	struct vector *vec;
	extern struct vector *inherit_list PROT((struct object *));

	vec = inherit_list(sp->u.ob);
	pop_stack();
	push_vector(vec);
	break;
    }
#endif
    CASE(F_WRITE);
	do_write(sp);
	break;
    CASE(F_MOVE_OBJECT);
    {
	struct object *o1, *o2;

	if ((sp-1)->type == T_OBJECT)
	    o1 = (sp-1)->u.ob;
	else {
	    o1 = find_object((sp-1)->u.string);
	    if (o1 == 0)
		error("move_object failed\n");
	}
	if (sp->type == T_OBJECT)
	    o2 = sp->u.ob;
	else {
	    o2 = find_object(sp->u.string);
	    if (o2 == 0)
		error("move_object failed\n");
	}
	move_object(o1, o2);
	pop_stack();
	break;
    }
    CASE(F_FUNCTION_EXISTS);
    {
	char *str, *res;

	str = function_exists((sp-1)->u.string, sp->u.ob);
	pop_n_elems(2);
	if (str) {
	    extern char *strrchr();

/* Raistlin -- made it use shared strings */
            res = str;
	    /* marion - strip trailing .c */
	    if (str = strrchr (res, (int)'.')) *str = 0;
#ifdef COMPAT_MODE
               push_shared_string(make_shared_string(res));
#else
            {
	       res = add_slash(res);
               push_shared_string(make_shared_string(res));
               free(res);
             /**MALLOC**/
            } 
#endif
	} else {
	    push_number(0);
	}
	break;
    }
    CASE(F_SNOOP);
    {
	/* This one takes a variable number of arguments. It returns
	 * 0 or an object.
	 */
#ifdef COMPAT_MODE
        struct svalue *a;
	if (!command_giver || num_arg == 2) 
        {
	    pop_n_elems (num_arg);
	    push_number (0);
        }
        a = apply("query_level", command_giver, 0);
        if (!a || a->type != T_NUMBER || a->u.number < SNOOP_LEVEL) 
        {
           pop_n_elems(num_arg);
           push_number(0);
        }
        else if (num_arg == 0) 
        {
            set_snoop(command_giver, (struct object *)0);
            push_number(0);
        } 
        else
	   set_snoop(command_giver, sp->u.ob);
#else
	if (!command_giver) {
	    pop_n_elems(num_arg);
    	    push_number(0);
	} else {
	    ob = 0; /* Do not remove this, it is not 0 by default */
            switch (num_arg) {
	    case 1:
	        if (new_set_snoop(sp->u.ob, 0))
	    	    ob = sp->u.ob;
		break;
	    case 2:
		if (new_set_snoop((sp-1)->u.ob, sp->u.ob))
		    ob = sp->u.ob;
		break;
            default:
	        ob = 0;
	        break;
	    }
    	    pop_n_elems(num_arg);
	    if (ob)
	        push_object(ob, "F_SNOOP");
	    else
		push_number(0);
	}
#endif
	break;
    }
    CASE(F_REMOVE_ACTION);
    {
        if (!command_giver || (command_giver->flags & O_DESTRUCTED) ||
            !current_object || (current_object->flags & O_DESTRUCTED))
        {
           pop_stack();
           push_number(0);
        }
        else
        {
           i = remove_one_sent(current_object, command_giver, sp->u.string);
           pop_stack();
           push_number(i);
        }
        break; 
    }
    CASE(F_ADD_ACTION);
    {
	struct svalue *arg = sp - num_arg + 1;
	if (num_arg == 3) {
	    if (arg[2].type != T_NUMBER)
		bad_arg(3, instruction);
	}
	add_action(arg[0].u.string,
		   num_arg > 1 ? arg[1].u.string : 0,
		   num_arg > 2 ? arg[2].u.number : 0);
	pop_n_elems(num_arg-1);
	break;
    }
    CASE(F_ADD_VERB);
	add_verb(sp->u.string, 0);
	break;
    CASE(F_ADD_XVERB);
	add_verb(sp->u.string, 1);
	break;
    CASE(F_ALLOCATE);
    {
	struct vector *v;

	v = allocate_array(sp->u.number);	/* Will have ref count == 1 */
	pop_stack();
	push_vector(v);
	v->ref--;
	break;
    }
    CASE(F_ALLOCATE_MAPPING);
    {
        struct mapping *m;
       
        m = allocate_mapping(sp->u.number);
        pop_stack();
        push_mapping(m);
        m->ref--;
        break;
    }
    CASE(F_ED);
	if (num_arg == 0) {
	    struct svalue *arg;
	    char *err_file;

	    if (command_giver == 0 || command_giver->interactive == 0) {
		push_number(0);
		break;
	    }
	    arg = sapply("query_real_name", command_giver, 0);
	    if (arg == 0 || arg->type != T_STRING) {
		push_number(0);
		break;
	    }
	    err_file = get_error_file(arg->u.string);
	    if (err_file == 0) {
		push_number(0);
		break;
	    }
	    ed_start(err_file, 0, 0);
	    push_number(1);
	    break;
	} else if (num_arg == 1) {
	    ed_start(sp->u.string, 0, 0);
	} else {
	    if (sp->type == T_STRING)
	        ed_start((sp-1)->u.string, sp->u.string, current_object);
	    else
		ed_start((sp-1)->u.string, 0 , 0);
	    pop_stack();
	}
	break;
    CASE(F_CRYPT);
    {
	char salt[2];
	char *res;
	char *choise =
	    "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789./";

	if (sp->type == T_STRING && strlen(sp->u.string) >= 2) {
	    salt[0] = sp->u.string[0];
	    salt[1] = sp->u.string[1];
	} else {
	    salt[0] = choise[random_number(strlen(choise))];
	    salt[1] = choise[random_number(strlen(choise))];
	}
#ifdef sun
	res = make_shared_string(_crypt((sp-1)->u.string, salt)); /**MALLOC**/
#else
	res = make_shared_string(crypt((sp-1)->u.string, salt)); /**MALLOC**/
#endif
	pop_n_elems(2);
	push_shared_string(res);
	break;
    }
#ifdef F_CREATE_WIZARD
    CASE(F_CREATE_WIZARD);
    {
	char *str;
	struct svalue *arg = sp - num_arg + 1;
	str = create_wizard(arg[0].u.string,
			    num_arg == 2 ? arg[1].u.string : 0);
	pop_n_elems(num_arg);
	if (str)
	    push_string(str, STRING_CONSTANT);
	else
	    push_number(0);
	break;
    }
#endif
    CASE(F_DESTRUCT);
    {
        int really;

        if (num_arg == 2)
        {
           really = sp->u.number;
           pop_stack();
        }
        else really = 0;
	destruct_object(sp, really);
	break;
    }
    CASE(F_RANDOM);
	if (sp->u.number <= 0) {
	    sp->u.number = 0;
	    break;
	}
	sp->u.number = random_number(sp->u.number);
	break;
#ifdef F_SAY
    CASE(F_SAY);
        {
	    static struct {
		struct vector v;
		struct svalue second_item[1];
	    } vtmp = { { 2, 1,
#ifdef DEBUG
		1,
#endif
		(struct wiz_list *)NULL,
		{ { T_NUMBER } } }, { { T_OBJECT } }
	    };

	if (num_arg == 1) {
	    vtmp.v.item[0].type = T_NUMBER; /* this marks the place for the
						command_giver
					      */
	    vtmp.v.item[1].type = T_NUMBER; /* will not match any object... */
	    vtmp.v.item[1].u.number = 0;
	    say(sp, &vtmp.v);
	} else {
	    if ( sp->type == T_POINTER ) {
		if (sp->u.vec->ref > 1) {
		    struct vector *vtmpp =
			slice_array(sp->u.vec, 0, sp->u.vec->size-1);
		    say(sp-1, vtmpp);
		    free_vector(vtmpp);
		} else
	            say(sp-1, sp->u.vec);
	    } else {
	        vtmp.v.item[0].type = T_NUMBER;
		vtmp.v.item[1].type = T_OBJECT;
	        vtmp.v.item[1].u.ob = sp->u.ob;
                add_ref(sp->u.ob, "F_SAY");
	        say(sp-1, &vtmp.v);
                vtmp.v.size = 2;
                vtmp.v.ref = 1; 
#ifdef DEBUG
                vtmp.v.extra_ref = 1;
#endif
                vtmp.v.item[1].type = T_NUMBER;
                vtmp.v.item[1].u.number = 0;
	    }
	    pop_stack();
	}
	break;
    }
    break;
#endif /* F_SAY */
#ifdef F_TELL_ROOM
    CASE(F_TELL_ROOM);
    {
	extern struct vector null_vector;
	struct svalue *arg = sp- num_arg + 1;
	struct vector *avoid;

	if (arg[0].type == T_OBJECT)
	    ob = arg[0].u.ob;
	else {
	    ob = find_object(arg[0].u.string);
	    if (ob == 0)
		error("Object not found.\n");
	}
	if (num_arg == 2) {
	    avoid = &null_vector;
	    avoid->ref++;
	} else {
	    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 } }
		};

	    if (arg[2].type != T_POINTER)
		bad_arg(3, instruction);
	    vtmp.item[0].u.vec = arg[2].u.vec;
	    if (vtmp.item[0].u.vec->ref > 1) {
		vtmp.item[0].u.vec->ref--;
		vtmp.item[0].u.vec = slice_array(
		  vtmp.item[0].u.vec, 0, vtmp.item[0].u.vec->size-1);
	    }
	    sp--;
	    vtmpp = order_alist(&vtmp);
	    avoid = vtmpp->item[0].u.vec;
	    vtmpp->item[0].u.vec = vtmp.item[0].u.vec;
	    free_vector(vtmpp);
	}
	tell_room(ob, sp, avoid);
	free_vector(avoid);
	pop_stack();
	break;
    }
#endif /* F_TELL_ROOM */
#ifdef F_SHOUT
    CASE(F_SHOUT);
	shout_string(sp->u.string);
	break;
#endif /* F_SHOUT */
    CASE(F_WHILE);
	fatal("F_WHILE should not appear.\n");
    CASE(F_DO);
	fatal("F_DO should not appear.\n");
    CASE(F_FOR);
	fatal("F_FOR should not appear.\n");
    CASE(F_SWITCH);
    {
	extern char* findstring PROT((char*));
	unsigned short offset,break_adr;
	int d,s,r;
	char *l,*end_tab;
	static short off_tab[] = { 0*6,1*6,3*6,7*6,15*6,31*6,63*6,127*6,255*6,
					511*6,1023*6,2047*6,4095*6,8191*6 };

	((char *)&offset)[0] = pc[1];
	((char *)&offset)[1] = pc[2];
	((char *)&break_adr)[0] = pc[3];
	((char *)&break_adr)[1] = pc[4];
	*--break_sp = break_adr;
	if ( ( i = EXTRACT_UCHAR(pc) >> 4 ) != 0xf ) {
	    if ( sp->type == T_NUMBER && !sp->u.number ) {
		/* special case: uninitalized string */
		s = ZERO_AS_STR_CASE_LABEL;
	    } else if ( sp->type == T_STRING ) {
	        switch(sp->string_type) {
	        case STRING_SHARED:
	            s = (int)sp->u.string;
	            break;
		default:
		    s = (int)findstring(sp->u.string);
	            break;
	        }
	    } else {
		bad_arg(1, F_SWITCH);
	    }
	} else {
	    if (sp->type != T_NUMBER) bad_arg(1, F_SWITCH);
	    s = sp->u.number;
	    i = (int)pc[0] &0xf ;
	}
	pop_stack();
	end_tab = current_prog->program + break_adr;
	if ( i >= 14 )
	    if ( i == 14 ) {
		/* fastest switch format : lookup table */
    		l = current_prog->program + offset;
                ((char *)&d)[0] = end_tab[-4];
                ((char *)&d)[1] = end_tab[-3];
                ((char *)&d)[2] = end_tab[-2];
                ((char *)&d)[3] = end_tab[-1];
		if ( s >= d && l + (s=(s-d)*sizeof(short)) < end_tab - 4 ) {
		    ((char *)&offset)[0] = l[s];
		    ((char *)&offset)[1] = l[s+1];
		    if (offset) {
			pc = current_prog->program + offset;
			break;
		    }
		}
		/* default */
		((char *)&offset)[0] = pc[5];
		((char *)&offset)[1] = pc[6];
		pc = current_prog->program + offset;
		break;
	    } else
		fatal("unsupported switch table format.\n");
	l = current_prog->program + offset + off_tab[i];
	d = (off_tab[i]+6) >> 1;
	if (d == 3) d=0;
	for(;;) {
	    ((char *)&r)[0] = l[0];
	    ((char *)&r)[1] = l[1];
	    ((char *)&r)[2] = l[2];
	    ((char *)&r)[3] = l[3];
	    if (s < r)
                if (d < 6) {
                    if (!d) { /* test for range */
			((char *)&offset)[0] = l[-2];
			((char *)&offset)[1] = l[-1];

			/* F_BREAK is required to be > 1 */
			if (offset <= 1) {
		            ((char *)&r)[0] = l[-6];
		            ((char *)&r)[1] = l[-5];
		            ((char *)&r)[2] = l[-4];
		            ((char *)&r)[3] = l[-3];
			    if (s >= r) {
				/* s is in the range */
				if (!offset) {
				    /* range with lookup table */
                                    ((char *)&offset)[0] = l[4];
                                    ((char *)&offset)[1] = l[5];
				    l = current_prog->program + offset +
					(s-r) * sizeof(short);
                                    ((char *)&offset)[0] = l[0];
                                    ((char *)&offset)[1] = l[1];
				    break;
				}
				((char *)&offset)[0] = l[4];
				((char *)&offset)[1] = l[5];
				break;
			    }
			}
			/* use default address */
                        ((char *)&offset)[0] = pc[5];
                        ((char *)&offset)[1] = pc[6];
                        break;
                    } /* !d */
                    d = 0;
                } else {
		    /* d >= 6 */
                    l -= d;
                    d >>= 1;
		}
	    else if (s > r) {
                if (d < 6) {
                    if (!d) { /* test for range */
			((char *)&offset)[0] = l[4];
			((char *)&offset)[1] = l[5];
			if (offset <= 1) {
		            ((char *)&r)[0] = l[6];
		            ((char *)&r)[1] = l[7];
		            ((char *)&r)[2] = l[8];
		            ((char *)&r)[3] = l[9];
			    if (s <= r) {
				/* s is in the range */
				if (!offset) {
				    /* range with lookup table */
                                    ((char *)&offset)[0] = l[10];
                                    ((char *)&offset)[1] = l[11];
				    l = current_prog->program + offset +
					(s-r) * sizeof(short);
                                    ((char *)&offset)[0] = l[0];
                                    ((char *)&offset)[1] = l[1];
				    break;
				}
				((char *)&offset)[0] = l[10];
				((char *)&offset)[1] = l[11];
				break;
			    }
			}
			/* use default address */
                        ((char *)&offset)[0] = pc[5];
                        ((char *)&offset)[1] = pc[6];
                        break;
                    } /* !d */
                    d = 0;
                } else {
		    /* d >= 6 */
                    l += d;
                    while (l >= end_tab) {
                        d >>= 1;
                        if (d <= 3) {
                            if (!d) break;
                            d = 0;
                        }
                        l -= d;
                    }
		    d >>= 1;
		}
	    } else {
		/* s == r */
		((char *)&offset)[0] = l[4];
		((char *)&offset)[1] = l[5];
		if ( !l[-2] && !l[-1] ) {
		    /* end of range with lookup table */
		    ((char *)&r)[0] = l[-6];
		    ((char *)&r)[1] = l[-5];
		    ((char *)&r)[2] = l[-4];
		    ((char *)&r)[3] = l[-3];
		    l = current_prog->program + offset + (s-r)*sizeof(short);
                    ((char *)&offset)[0] = l[0];
                    ((char *)&offset)[1] = l[1];
		}
		if (offset <= 1) {
		    if (!offset) {
			/* start of range with lookup table */
                        ((char *)&offset)[0] = l[10];
                        ((char *)&offset)[1] = l[11];
			l = current_prog->program + offset;
                        ((char *)&offset)[0] = l[0];
                        ((char *)&offset)[1] = l[1];
		    } else {
                        ((char *)&offset)[0] = l[10];
                        ((char *)&offset)[1] = l[11];
		    }
		}
		break;
	    }
	}
	pc = current_prog->program + offset;
	break;
    }
    CASE(F_BREAK);
    {
	pc = current_prog->program + *break_sp++;
	break;
    }
    CASE(F_SUBSCRIPT);
	fatal("F_SUBSCRIPT should not appear.\n");
    CASE(F_STRLEN);
	i = strlen(sp->u.string);
	pop_stack();
	push_number(i);
	break;
    CASE(F_SIZEOF);
        if (sp->type == T_POINTER)
           i = sp->u.vec->size;
        else if (sp->type == T_MAPPING)
           i = sp->u.map->size;
        else
           i = 0;
	pop_stack();
	push_number(i);
	break;
    CASE(F_LOWER_CASE);
    {
	char *str = string_copy(sp->u.string);
	for (i = strlen(str)-1; i>=0; i--)
	    if (isalpha(str[i]))
		str[i] |= 'a' - 'A';
	pop_stack();
	push_shared_string(make_shared_string(str)); /*MALLOC*/
        free(str);
	break;
    }
    CASE(F_SET_HEART_BEAT);
	i = set_heart_beat(current_object, sp->u.number);
	sp->u.number = i;
	break;
    CASE(F_CAPITALIZE);
	if (islower(sp->u.string[0])) {
	    char *str;

	    str = string_copy(sp->u.string);
	    str[0] += 'A' - 'a';
	    pop_stack();
	    push_shared_string(make_shared_string(str));
            free(str);/*MALLOC*/
	}
	break;
    CASE(F_PROCESS_STRING);
    {
        extern char *process_string PROT((char *));
   
        char *str;
   
        str = process_string(sp->u.string);
        if (str != sp->u.string) {
            pop_stack();
            push_shared_string(make_shared_string(str));
        }
        break;
    }
    CASE(F_COMMAND);
    {
	struct svalue *arg = sp - num_arg + 1;

	if (num_arg == 1)
	    i = command_for_object(arg[0].u.string, 0);
	else
#ifdef COMPAT_MODE
	    i = command_for_object(arg[0].u.string, arg[1].u.ob);
#else
	    error("Too many arguments to command().\n");
#endif
	pop_n_elems(num_arg);
	push_number(i);
	break;
    }
    CASE(F_GET_DIR);
    {
	struct vector *v;
        v = get_dir(sp->u.string);
	pop_stack();
	if (v) { push_vector(v); v->ref--; } /* Will now be 1. */ 
        else push_number(0);
	break;
    }
    CASE(F_LS);
        if (sp->type == T_NUMBER)
           list_files(NULL);
        else
	   list_files(sp->u.string);
	break;
    CASE(F_RM);
	i = remove_file(sp->u.string);
	pop_stack();
	push_number(i);
	break;
    CASE(F_CAT);
    {
	struct svalue *arg = sp- num_arg + 1;
	int start = 0, len = 0;

	if (num_arg > 1)
	    start = arg[1].u.number;
	if (num_arg == 3) {
	    if (arg[2].type != T_NUMBER)
		bad_arg(2, instruction);
	    len = arg[2].u.number;
	}
	i = print_file(arg[0].u.string, start, len);
	pop_n_elems(num_arg);
	push_number(i);
	break;
    }
    CASE(F_MKDIR);
    {
	char *path;

#ifdef COMPAT_MODE
	path = check_file_name(sp->u.string, 1);
#else
        path = check_valid_path(sp->u.string, current_object->eff_user, 
                                "mkdir", 1);
#endif
	/* pop_stack(); marion - what the heck ??? */
	if (path == 0 || mkdir(path, 0770) == -1)
	    assign_svalue(sp, &const0);
	else
	    assign_svalue(sp, &const1);
	break;
    }
    CASE(F_RMDIR);
    {
	char *path;

#ifdef COMPAT_MODE
	path = check_file_name(sp->u.string, 1);
#else
	path = check_valid_path(sp->u.string, current_object->eff_user, 
                                "rmdir", 1);
#endif
	/* pop_stack(); */
	if (path == 0 || rmdir(path) == -1)
	    assign_svalue(sp, &const0);
	else
	    assign_svalue(sp, &const1);
	break;
    }
    CASE(F_KEYS);
    {
        struct vector *v;
        
        v = mapping_indices(sp->u.map);
        pop_stack();
        push_vector(v);
        break; 
    }
    CASE(F_INPUT_TO);
    {
	struct svalue *arg = sp - num_arg + 1;
	int flag = 1;
	
	if (num_arg == 1 || sp->type == T_NUMBER && sp->u.number == 0)
	    flag = 0;
	i = input_to(arg[0].u.string, flag);
	pop_n_elems(num_arg);
	push_number(i);
	break;
    }
    CASE(F_SET_LIVING_NAME);
	set_living_name(current_object, sp->u.string);
	break;
    CASE(F_PARSE_COMMAND);
    {
	struct svalue *arg;

	num_arg = EXTRACT_UCHAR(pc);
	pc++;
	arg = sp - num_arg + 1;
	if (arg[0].type != T_STRING)
	    bad_arg(1, F_PARSE_COMMAND);
	if (arg[1].type != T_OBJECT && arg[1].type != T_POINTER)
	    bad_arg(2, F_PARSE_COMMAND);
	if (arg[2].type != T_STRING)
	    bad_arg(3, F_PARSE_COMMAND);
	if (arg[1].type == T_POINTER)
	    check_for_destr(arg[1].u.vec);

	i = parse(arg[0].u.string, &arg[1], arg[2].u.string, &arg[3],
		  num_arg-3); 
	pop_n_elems(num_arg);	/* Get rid of all arguments */
	push_number(i);		/* Push the result value */
	break;
    }
    CASE(F_SSCANF);
	num_arg = EXTRACT_UCHAR(pc);
	pc++;
	i = inter_sscanf(num_arg);
	pop_n_elems(num_arg);
	push_number(i);
	break;
    CASE(F_ENABLE_COMMANDS);
	enable_commands(1);
	push_number(1);
	break;
    CASE(F_DISABLE_COMMANDS);
	enable_commands(0);
	push_number(0);
	break;
    CASE(F_PRESENT);
	{
	    struct svalue *arg = sp - num_arg + 1;
	    ob = object_present(arg, num_arg == 1 ? 0 : arg[1].u.ob);
	    pop_n_elems(num_arg);
	    if (ob)
		push_object(ob, "F_PRESENT");
	    else
		push_number(0);
	}
	break;
#ifdef F_SET_LIGHT
    CASE(F_SET_LIGHT);
    {
	struct object *o1;

	add_light(current_object, sp->u.number);
	o1 = current_object;
	while(o1->super)
	    o1 = o1->super;
	sp->u.number = o1->total_light;
	break;
    }
#endif /* F_SET_LIGHT */
    CASE(F_CONST0);
	push_number(0);
	break;
    CASE(F_CONST1);
	push_number(1);
	break;
    CASE(F_NUMBER);
	((char *)&i)[0] = pc[0];
	((char *)&i)[1] = pc[1];
	((char *)&i)[2] = pc[2];
	((char *)&i)[3] = pc[3];
	pc += 4;
	push_number(i);
	break;
    CASE(F_ASSIGN);
#ifdef DEBUG
	if (sp[-1].type != T_LVALUE)
	    fatal("Bad argument to F_ASSIGN\n");
#endif
	assign_svalue((sp-1)->u.lvalue, sp);
	assign_svalue(sp-1, sp);
	pop_stack();
	break;
    CASE(F_CTIME);
    {
	char *cp, *a;
        int n = current_time;
        if (num_arg) { n = sp->u.number; pop_stack(); }
        cp = string_copy(time_string(n));
	/* Now strip the newline. */
	a = strchr(cp, '\n');
	if (a)
	    *a = '\0';
	push_shared_string(make_shared_string(cp));
        free(cp);/*MALLOC*/
	break;
    }
    CASE(F_ADD_EQ);
	if (sp[-1].type != T_LVALUE)
	   bad_arg(1, F_ADD_EQ);
	argp = sp[-1].u.lvalue;
	switch(argp->type) 
        {
           case T_MAPPING:
           {
              if (sp->type == T_MAPPING)
              {
                 struct mapping *m;

                 m = add_mapping(argp->u.map, sp->u.map);
                 pop_n_elems(2);
                 push_mapping(m);
                 m->ref--;
              }
              else
                 bad_arg(2, F_ADD_EQ);  
              break;
           }
           case T_POINTER:
	   {
	      struct vector *v;
	      if (sp->type != T_POINTER)
		 v = append_vector(argp->u.vec, sp);
	      else 
   	         v = add_array(argp->u.vec, sp->u.vec);
	      pop_n_elems(2);
	      push_vector(v);
	      v->ref--;
	      break;
           }
	   case T_STRING:
	   {
	      char *new_str;
	      switch(sp->type)
	      {
	         case T_STRING: 
                 { 
		    new_str=xalloc(strlen(argp->u.string)+
                                   strlen(sp->u.string)+1);
		    strcpy(new_str, argp->u.string);
		    strcat(new_str, sp->u.string);
		    pop_n_elems(2);
		    push_shared_string(make_shared_string(new_str));
		    free(new_str);/**MALLOC**/
		    break;
		 } 
		 case T_NUMBER: 
                 {
		    char buff[20];
		    sprintf(buff, "%d", sp->u.number);
		    new_str = xalloc(strlen(argp->u.string)+strlen(buff)+1);
		    strcpy(new_str, argp->u.string);
		    strcat(new_str, buff);
		    pop_n_elems(2);
		    push_shared_string(make_shared_string(new_str));
		    free(new_str);/**MALLOC**/
		    break;
		 }
		 case T_POINTER: {
		    struct vector *v;
		    v = prepend_vector(sp->u.vec, argp);
		    pop_n_elems(2);
		    push_vector(v);
		    v->ref--;
		    break;
		 }
		 default: error("Bad type RHS to +=\n");
	      } 
	   }
	   break;
	   case T_NUMBER: {
	      switch(sp->type) 
	      {
	         case T_NUMBER:
		    i = argp->u.number + sp->u.number;
		    pop_n_elems(2);
		    push_number(i);
		    break;
	         case T_POINTER:
	         {
		    struct vector *v;
		    v = prepend_vector(sp->u.vec, argp);
		    pop_n_elems(2);
		    push_vector(v); /* This will make ref count == 2 */
		    v->ref--;
		    break;
		 }
 	         case T_STRING: 
                 {
		    char buff[20], *new_str;
		    sprintf(buff, "%d", argp->u.number);
		    new_str=xalloc(strlen(sp->u.string) + strlen(buff) + 1);
		    strcpy(new_str, buff);
		    strcat(new_str, sp->u.string);
		    pop_n_elems(2);
		    push_shared_string(make_shared_string(new_str));
		    free(new_str);/**MALLOC**/
		    break;
		 }
		 default:
		    error("Bad type number to rhs +=.\n");
	      }
	   }
	   break;
   	   default:
	      error("Bad type to lhs +=");
	}
	assign_svalue(argp, sp);
	break;
    CASE(F_SUB_EQ);
	if (sp[-1].type != T_LVALUE)
	    bad_arg(1, F_SUB_EQ);
	argp = sp[-1].u.lvalue;
        switch(argp->type)
        {
           case T_NUMBER:
	      if (sp->type != T_NUMBER)
	         error("Bad right type to -=\n");
	      i = argp->u.number - sp->u.number;
	      pop_n_elems(2);
	      push_number(i);
              break;
           case T_POINTER:
#ifdef COMPAT_MODE
           {
              struct vector *v;
              v = subtract_array(argp->u.vec, sp);
              pop_n_elems(2);
              if (v) { push_vector(v); v->ref--; }
              else push_number(0);
              break;
           }
#else
	  {
	    struct vector *v;

	    if (sp->type != T_POINTER)
	        error("Bad right type to -=");
	    v = sp->u.vec;
	    if (v->ref > 1) {
		v = slice_array(v, 0, v->size-1 );
		v->ref--;
            }
	    sp--;
	    v = subtract_array(argp->u.vec, v);
	    free_vector(argp->u.vec);
	    argp->u.vec = v;
	    break;
	  }
#endif
           default: error("Bad lhs to -=\n");
        }
	assign_svalue(argp, sp);
	break;
    CASE(F_MULT_EQ);
	if (sp[-1].type != T_LVALUE)
	    bad_arg(1, F_MULT_EQ);
	argp = sp[-1].u.lvalue;
        if (argp->type != sp->type)
            error("Type mismatch to operator *=\n");
        switch(sp->type)
        {
           case T_NUMBER:
           {
              i = argp->u.number * sp->u.number;
              pop_n_elems(2);
              push_number(i);
              break;
           }
           case T_MAPPING:
           {
              struct mapping *m;
              m = compose_mapping(argp->u.map, sp->u.map);
              pop_n_elems(2);
              push_mapping(m);
              break;
           }
           default: bad_arg(2, F_MULT_EQ);
        }
	assign_svalue(argp, sp);
	break;
    CASE(F_AND_EQ);
	if (sp[-1].type != T_LVALUE)
	    bad_arg(1, F_AND_EQ);
	argp = sp[-1].u.lvalue;
	if (argp->type != T_NUMBER)
	    error("Bad left type to &=.\n");
	if (sp->type != T_NUMBER)
	    error("Bad right type to &=");
	i = argp->u.number & sp->u.number;
	pop_n_elems(2);
	push_number(i);
	assign_svalue(argp, sp);
	break;
    CASE(F_OR_EQ);
	if (sp[-1].type != T_LVALUE)
	    bad_arg(1, F_OR_EQ);
	argp = sp[-1].u.lvalue;
	if (sp[-1].type != T_LVALUE)
	    bad_arg(1, F_OR_EQ);
	argp = sp[-1].u.lvalue;
	if (argp->type != T_NUMBER)
	    error("Bad left type to |=.\n");
	if (sp->type != T_NUMBER)
	    error("Bad right type to |=");
	i = argp->u.number | sp->u.number;
	pop_n_elems(2);
	push_number(i);
	assign_svalue(argp, sp);
	break;
    CASE(F_XOR_EQ);
	if (sp[-1].type != T_LVALUE)
	    bad_arg(1, F_XOR_EQ);
	argp = sp[-1].u.lvalue;
	if (argp->type != T_NUMBER)
	    error("Bad left type to ^=.\n");
	if (sp->type != T_NUMBER)
	    error("Bad right type to ^=");
	i = argp->u.number ^ sp->u.number;
	pop_n_elems(2);
	push_number(i);
	assign_svalue(argp, sp);
	break;
    CASE(F_LSH_EQ);
	if (sp[-1].type != T_LVALUE)
	    bad_arg(1, F_LSH_EQ);
	argp = sp[-1].u.lvalue;
	if (argp->type != T_NUMBER)
	    error("Bad left type to <<=.\n");
	if (sp->type != T_NUMBER)
	    error("Bad right type to <<=");
	i = argp->u.number << sp->u.number;
	pop_n_elems(2);
	push_number(i);
	assign_svalue(argp, sp);
	break;
    CASE(F_RSH_EQ);
	if (sp[-1].type != T_LVALUE)
	    bad_arg(1, F_RSH_EQ);
	argp = sp[-1].u.lvalue;
	if (argp->type != T_NUMBER)
	    error("Bad left type to >>=.\n");
	if (sp->type != T_NUMBER)
	    error("Bad right type to >>=");
	i = argp->u.number >> sp->u.number;
	pop_n_elems(2);
	push_number(i);
	assign_svalue(argp, sp);
	break;
#ifdef F_COMBINE_FREE_LIST
    CASE(F_COMBINE_FREE_LIST);
#ifdef MALLOC_malloc
	push_number(resort_free_list());
#else
	push_number(0);
#endif
	break;
#endif
    CASE(F_DIV_EQ);
	if (sp[-1].type != T_LVALUE)
	    bad_arg(1, F_DIV_EQ);
	argp = sp[-1].u.lvalue;
	if (argp->type != T_NUMBER)
	    error("Bad left type to /=.\n");
	if (sp->type != T_NUMBER)
	    error("Bad right type to /=");
	if (sp->u.number == 0)
	    error("Division by 0\n");
	i = argp->u.number / sp->u.number;
	pop_n_elems(2);
	push_number(i);
	assign_svalue(argp, sp);
	break;
    CASE(F_MOD_EQ);
	if (sp[-1].type != T_LVALUE)
	    bad_arg(1, F_MOD_EQ);
	argp = sp[-1].u.lvalue;
	if (argp->type != T_NUMBER)
	    error("Bad left type to %=.\n");
	if (sp->type != T_NUMBER)
	    error("Bad right type to %=");
	if (sp->u.number == 0)
	    error("Division by 0\n");
	i = argp->u.number % sp->u.number;
	pop_n_elems(2);
	push_number(i);
	assign_svalue(argp, sp);
	break;
    CASE(F_STRING);
    {
	unsigned short string_number;
	((char *)&string_number)[0] = pc[0];
	((char *)&string_number)[1] = pc[1];
	pc += 2;
	push_string(current_prog->strings[string_number],
		    STRING_CONSTANT);
	break;
    }
#ifdef F_RUSAGE
    CASE(F_RUSAGE);
    {
        char buff[500];

	struct rusage rus;
	long utime, stime;
	int maxrss;

	if (getrusage(RUSAGE_SELF, &rus) < 0)
            buff[0] = 0;
	else {
	    utime = rus.ru_utime.tv_sec * 1000 + rus.ru_utime.tv_usec / 1000;
	    stime = rus.ru_stime.tv_sec * 1000 + rus.ru_stime.tv_usec / 1000;
	    maxrss = rus.ru_maxrss;
#ifdef sun
	    maxrss *= getpagesize() / 1024;
#endif
	    sprintf(buff, "%ld %ld %d %d %d %d %d %d %d %d %d %d %d %d %d %d",
		    utime, stime, maxrss, rus.ru_ixrss, rus.ru_idrss,
		    rus.ru_isrss, rus.ru_minflt, rus.ru_majflt, rus.ru_nswap,
		    rus.ru_inblock, rus.ru_oublock, rus.ru_msgsnd, 
		    rus.ru_msgrcv, rus.ru_nsignals, rus.ru_nvcsw, 
		    rus.ru_nivcsw);
	  }
	push_string(buff, STRING_SHARED);
	break;
    }
#endif
    CASE(F_CINDENT);
    {
	char *path;

#ifndef COMPAT_MODE
	    path = check_valid_path(sp->u.string, current_object->eff_user, "cindent", 1); 
#else
	    path = check_file_name(sp->u.string, 1);
#endif
	pop_stack();
	if (path) {
	    if (indent_program(path)) {
		push_number(1);
		break;
	    }
	} else {
	    add_message("Illegal attempt to indent\n");
	}
	push_number(0);
	break;
    }
    CASE(F_DESCRIBE);
    {
	char *str;
	int live;

	if (num_arg < 3) live = 0;
	else {
	    if (sp->type != T_NUMBER) bad_arg (3, F_DESCRIBE);
	    live = sp->u.number;
	    pop_stack ();
	}
	str = describe_items(sp-1, sp->u.string, live);
	pop_n_elems(2);
	if (str) push_shared_string (make_shared_string(str));
	else     push_number(0);
	break;
    }
    CASE(F_UNIQUE_ARRAY); {
	extern struct vector 
	    *make_unique PROT((struct vector *arr,char *func,
                struct svalue *skipnum));
	struct vector *res;

	if (num_arg < 3) 
	    res = make_unique((sp-1)->u.vec, sp->u.string, &const0);
	else {
	    res = make_unique ((sp-2)->u.vec, (sp-1)->u.string, sp);
	    pop_stack ();
	}
        pop_n_elems(2);
        if (res) { push_vector(res); res->ref--; }
        else     push_number(0);
	break;
    }
    CASE(F_VERSION); {
	char buff[12];
	sprintf(buff, "%5.5s%02d-DR", GAME_VERSION, PATCH_LEVEL);
	push_string(buff, STRING_SHARED);
	break;
    }
    CASE(F_MAP_ARRAY); {
	struct vector *res = NULL;
        struct mapping *map = NULL;
	struct svalue *arg;

	arg = sp - num_arg + 1; ob = 0;

	if (arg[2].type == T_OBJECT) 
	    ob = arg[2].u.ob;
        else if (arg[2].type == T_STRING)
            ob = find_object(arg[2].u.string);

        if (!ob)
            bad_arg (3, F_MAP_ARRAY);

	if (arg[0].type == T_POINTER)
	    res = map_array (arg[0].u.vec, arg[1].u.string, ob,
			     num_arg > 3 ? sp : (struct svalue *)0);
        else if (arg[0].type == T_MAPPING)
	    map = map_mapping (arg[0].u.map, arg[1].u.string, ob,
			     num_arg > 3 ? sp : (struct svalue *)0);
	else
	    res = 0;
	pop_n_elems (num_arg);
	if (res) { push_vector(res); sp->u.vec->ref--; }
        else if (map) { push_mapping(map); sp->u.map->ref--; }
	else     push_number (0);
	break;
    }
    /*  ADDED -- 5-91  Raistlin -- Americanized standard LP */
/*
    CASE(F_REMOVE_LIVING_NAME)
       remove_one_living_name(sp->u.string, current_object);
       break; 
*/
    CASE(F_ROOT);
    { 
       struct object *obj = current_object;
       int i;
       if (num_arg == 1)
          obj = sp->u.ob;
       i = (index(obj->name, '#') == NULL);
       pop_n_elems(num_arg);
       push_number(i);
       break;
    } 
    CASE(F_DELETEM);
    {
        mapping_delete((sp-1)->u.map, sp);
        pop_stack();
        break;
    }
    CASE(F_DELETEA);
    {
       struct svalue *arg = sp - num_arg + 1;
       struct vector *ret;
       int j = arg[1].u.number;
       
       if (num_arg == 3)
       {
          if (arg[2].type != T_NUMBER)
             bad_arg(3, F_DELETEA);
          j = arg[2].u.number;
       }
       ret = delete_elements(arg[0].u.vec, arg[1].u.number, j);
       pop_n_elems(num_arg);
       if (ret) { push_vector(ret); ret->ref--; }
       else push_number(0);
       break;
    }
    CASE(F_EXISTS);
    {
       struct stat st;
       int offset = 0;
       char *file = sp->u.string;
       if (*file == '/') offset = 1;
       pop_stack();
       if (stat(file+offset, &st) != -1)
          push_number(1);
       else push_number(0);
       break;
    }
    CASE(F_PAD);
    {
        struct svalue *arg = sp-num_arg+1;
        char *new, *str, numbuf[20];
        int len = arg[1].u.number, strl;
        int right_justify = 0, padchar = ' ';
         
        if (num_arg == 3)
        {
	   if (arg[2].type != T_NUMBER)
              bad_arg(3, F_PAD);
           padchar = arg[2].u.number;
        }
        if (arg[0].type == T_NUMBER)
        {
           sprintf(numbuf, "%d", arg[0].u.number);
           str = numbuf;
        } 
        else
           str = arg[0].u.string;


        if (len < 0)
        {
           len = -len;
           right_justify = 1;
        }

        if (len == 0)
        {
           /* Nothing to pad!! */
           pop_n_elems(num_arg);
           push_constant_string("");
           break;
        }

        strl = strlen(str);
        if (strl > len) strl = len;
        new = xalloc(len+1);
        
        if (right_justify)
        { 
           memset(new, padchar, len);
           if (strl)
               strncpy(new+len-strl, str, strl);
        }
        else
        {
           if (strl)
              strncpy(new, str, len);
           if (strl < len)
              memset(new+strl, padchar, len-strl);
        }
        new[len] = '\0';
        pop_n_elems(num_arg);
        push_shared_string(make_shared_string(new));
        free(new);
        break;
    } 
    CASE(F_REMOTE_COMMAND);
       push_number(remote_command);
       break;
    CASE(F_IN_EDITOR);
    {
        struct object *a = command_giver;
        if (num_arg==1) 
        {
           a = sp->u.ob;
           pop_stack();
        }
        if (a && a->interactive && a->interactive->ed_buffer)
           push_number(1);
        else 
           push_number(0);
        break;
    } 
    CASE(F_GRAB_FILE);
    {
	struct svalue *arg = sp - num_arg + 1;
        int start=0, len=0;
        struct vector *r;

        if (num_arg > 1)
           start = arg[1].u.number;
        if (num_arg == 3)
           len = arg[2].u.number; 

        r = grab_file(arg[0].u.string, start, len);
        pop_n_elems(num_arg);
        if (r) { push_vector(r); sp->u.vec->ref--; }
        else push_number(0);
        break;
    }
    CASE(F_FILES);
    {
        struct vector *r;

        r = get_files(sp->u.string);
        pop_stack();
        if (r) { push_vector(r); sp->u.vec->ref--; }
        else   push_number(0);
        break;
    }
#ifdef F_POSSESSIVE
    CASE(F_POSSESSIVE);
    {
        struct object *obj = command_giver;

        if (num_arg == 1) obj = sp->u.ob;
        pop_n_elems(num_arg);
        if (!obj || obj->flags & O_DESTRUCTED)
           push_number(0);
        else
        {
           struct svalue *r = sapply("query_gender", obj, 0);
           if (!r || r->type != T_STRING)
              push_constant_string("its");
           else
           {
              if (!strcmp(r->u.string, "male"))
                 push_constant_string("his");
              else if (!strcmp(r->u.string, "female"))
                 push_constant_string("her");
              else push_constant_string("its");
           }
        }
        break;
    }
#endif
#ifdef F_OBJECTIVE
    CASE(F_OBJECTIVE);
    {
        struct object *obj;

        if (num_arg == 1) obj = sp->u.ob;
        else obj = command_giver;
        pop_n_elems(num_arg);
        if (!obj || obj->flags & O_DESTRUCTED)
           push_number(0);
        else
        {
           struct svalue *r = sapply("query_gender", obj, 0);
           if (!r || r->type != T_STRING)
              push_constant_string("it");
           else
           {
              if (!strcmp(r->u.string, "male"))
                 push_constant_string("him");
              else if (!strcmp(r->u.string, "female"))
                 push_constant_string("her");
              else push_constant_string("it");
           }
        }
        break;
    }
#endif
#ifdef F_SUBJECTIVE
    CASE(F_SUBJECTIVE);
    {
        struct object *obj;

        if (num_arg == 1) obj = sp->u.ob;
        else obj = command_giver;
        pop_n_elems(num_arg);
        if (!obj || obj->flags & O_DESTRUCTED)
           push_number(0);
        else
        {
           struct svalue *r = sapply("query_gender", obj, 0);
           if (!r || r->type != T_STRING)
              push_constant_string("it");
           else
           {
              if (!strcmp(r->u.string, "male"))
                 push_constant_string("he");
              else if (!strcmp(r->u.string, "female"))
                 push_constant_string("she");
              else push_constant_string("it");
           }
        }
        break;
    }
#endif
    CASE(F_REMOVE_INTERACTIVE)
        remove_interactive_player(sp->u.ob);
        break;
    CASE(F_CP)
    {
        int i;
        i = copy_file((sp-1)->u.string, sp->u.string);
        pop_n_elems(2);
        push_number(i);
        break;
    } 
    CASE(F_RENAME);
    {
        int i;

        i = rename_file((sp-1)->u.string, sp->u.string);
        pop_n_elems(2);
        push_number(i);
        break;
    }
    CASE(F_TYPEOF);
    {
       char *ret;
       int t;
 
       t = sp->type;
       pop_stack();
       switch(t) {
       case T_OBJECT:
          ret = "object";
          break;
       case T_NUMBER:
          ret = "int";
          break;
       case T_POINTER:
          ret = "vector";
          break;
       case T_STRING:
          ret = "string";
          break;
       default: ret = "";
       }
       push_constant_string(ret);
       break;
    } 
    CASE(F_CALLER);
    {
       struct object *o = caller();

       if (o)
          push_object(o, "F_CALLER");
       else push_number(0);
       break;
    }
    CASE(F_READ_FILE);
    {
	char *str;
	struct svalue *arg = sp- num_arg + 1;
	int start = 0, len = 0;

	if (num_arg > 1)
	    start = arg[1].u.number;
	if (num_arg == 3) 
        {
	    if (arg[2].type != T_NUMBER)
	        bad_arg(2, instruction);
	    len = arg[2].u.number;
        }
        /* back compatibility with American versions */
#ifdef COMPAT_MODE
        else if (num_arg == 2) 
           len = 1;  
#endif

	str = read_file(arg[0].u.string, start, len);
	pop_n_elems(num_arg);
	if (str == 0)
	    push_number(0);
	else {
	    push_string(str, STRING_MALLOC);
	    free(str);
	}
	break;
    }
    CASE(F_INTERACTIVE)
    {
        int ret;
        if (sp->u.ob->flags&O_DESTRUCTED)
           ret = 0;
        else if (sp->u.ob->interactive) 
           ret = 1;
        else ret = 0;
        pop_stack();
        push_number(ret);
        break;
    }
    CASE(F_FIND_CALL_OUT);
    {
            int ret;
	    ret = find_call_out(current_object, sp->u.string);
            pop_stack();
            push_number(ret);
	    break;
    }
    CASE(F_MEMBER_ARRAY)
    {
       int ret;
       ret = search_array(sp, sp-1, 0);
       pop_n_elems(2);
       push_number(ret);
       break;
    }
    CASE(F_SEARCHA);
    case F_INDEX:
   	 {
            int ret;
            if (num_arg == 3)
            {
 		if (sp->type != T_NUMBER)
 		    error("Bad argument number 3 to searcha.\n");
 	        ret = search_array((sp-2), (sp-1), sp->u.number);
            }
            else
                ret = search_array((sp-1), sp, 0);
	    pop_n_elems(num_arg);
            push_number(ret);
            break;
 	}
    CASE(F_REALLOCATE);
        {
           struct vector *v;

           v = reallocate_array((sp-1)->u.vec, sp->u.number);
           pop_n_elems(2);
           push_vector(v);
           sp->u.vec->ref--;
        }
        break;
    CASE(F_MEMUSED);    /* Render 2/6/91 */
        push_number((int)memused()); 
	break;
    CASE(F_STRSUB);
    {
        char *ret;

        ret = strsub((sp-2)->u.string, (sp-1)->u.vec, sp->u.number);
        if (ret)
        {
           pop_n_elems(3);
           push_malloced_string(ret);
        } 
        else
           pop_n_elems(2);  /* on error, just return first arg */
    }
    break;
/* 
** modified 3/30/91 Raistlin add_attribute, query_attribute, 
** delete_attribute, get_attribute, add_prot_attribute, format, and print 
 */
    CASE(F_GET_ATTRIBUTE); {
   /* arguments  get_attribute(string, object)  
   **               returns value associated with string 
   **            get_attribute(object)
   **               returns an array of all attributes associated with object 
   **   low level split into get_attr_vector and get_attribute
    */
          struct vector *v;
          int ret;
	  char *attr = NULL;
          struct object *o = current_object;
          
          if (num_arg == 0 || (num_arg == 1 && sp->type == T_OBJECT &&
              (o = sp->u.ob))) /* yes, I want a single equal here */ 
          {
             v = get_attr_vector(o);
             pop_n_elems(num_arg);
             push_vector(v);
             sp->u.vec->ref--;
             break;
          }
          else 
          {
             if (num_arg == 2) 
             {
                attr = (sp-1)->u.string;
                o = sp->u.ob; 
             }
             else attr = sp->u.string;
             ret = get_attribute(attr, o);
             pop_n_elems(num_arg);
             push_number(ret);
          }
       }
       break;
    CASE(F_DELETE_ATTRIBUTE);
       {
          struct object *o;
          char *attr;
          
          if (num_arg == 2) 
          {
             o = sp->u.ob;
             attr = (sp-1)->u.string;
          }
          else 
          {
             o = current_object;
             attr = sp->u.string;
          }
          delete_attribute(attr, o);
          pop_n_elems(num_arg-1); /* delete is void, leave a return value */
       }
       break;
    case F_ADD_PROT_ATTRIBUTE:
    CASE(F_ADD_ATTRIBUTE);
       {
          struct object *t=current_object;
          char *attr; 
	  int val = 1; 
          char *c=(instruction==F_ADD_ATTRIBUTE?
                   "add_attribute":"add_prot_attribute");

          if (num_arg == 3)
          {
             if (sp->type != T_OBJECT)
                error("Bad type argument 3 to %s()\n", c);
             if ((sp-1)->type != T_NUMBER)
                error("Bad type argument 2 to %s()\n", c);
             t = sp->u.ob;
             val = (sp-1)->u.number;
             attr = (sp-2)->u.string;
          }
          else if (num_arg == 2)
          {
             if (sp->type == T_OBJECT)
                t = sp->u.ob;
             else val = sp->u.number;
             attr = (sp-1)->u.string;
          } 
          else attr = sp->u.string;
          add_attribute(attr, t, val, instruction == F_ADD_PROT_ATTRIBUTE);
          pop_n_elems(num_arg-1); /* add is void */
       }
       break;
    CASE(F_QUERY_ATTRIBUTE);
       {
          char *attr;
          int ret;
          struct object *o=current_object;
          
          if (num_arg == 2)
          {
             attr = (sp-1)->u.string;
             o = sp->u.ob;
          }
          else attr = sp->u.string;
          ret = query_attribute(attr, o);
          pop_n_elems(num_arg);
          push_number(ret);
       }
       break;
    case F_FORMAT:
	{
            char *c, *txt;
            int width=0;

            if (num_arg == 2)
            {
               width = sp->u.number;
               txt = (sp-1)->u.string;
            }
            else txt = sp->u.string;
/*
            if (width > TEXT_WIDTH)
               error("Invalid size for argument 2 to text()\n");
*/
            
            c = textformat(txt, width);
            if (!c)
               error("Error formatting arg 1 to format()\n");
 
            pop_n_elems(num_arg);
            push_string(c, STRING_SHARED);
         }
         break;
    CASE(F_PRINT);
	{
            char *txt;
            int width = 0;
            char *c;
            struct svalue v;
            
            if (num_arg == 2)
            {
               txt = (sp-1)->u.string;
               width = sp->u.number;
            }
            else
               txt = sp->u.string; 
            if (width > TEXT_WIDTH)
            {
               error("Invalid size for argument 2 to print()\n");
               exit(1);
            }
            c = textformat(txt, width);
            if (!c)
            {
               error("Error formatting arg 1 to print()\n");
               exit(1);
            }
            v.u.string = c;
            v.type = T_STRING;
            v.string_type = STRING_CONSTANT;
            (void)do_write(&v);
            pop_n_elems(num_arg-1); /* void */
            break;
 	}
    CASE(F_SORT_ARRAY); {
	extern struct vector *sort_array
	  PROT((struct vector*,char *,struct object *));
	struct vector *res;
	struct svalue *arg;

	arg = sp - 2; ob = 0;

	if (arg[2].type == T_OBJECT)
	    ob = arg[2].u.ob;
	else if (arg[2].type == T_STRING) 
	    ob = find_object(arg[2].u.string);

	if (!ob)
	    bad_arg (3, F_SORT_ARRAY);

	if (arg[0].type == T_POINTER) {
	    /* sort_array already takes care of destructed objects */
	    res = sort_array (
	      slice_array(arg[0].u.vec, 0, arg[0].u.vec->size-1),
	      arg[1].u.string, ob);
	} else
	    res = 0;
	pop_n_elems (3);
	sp++;
	if (res) {
	    sp->type = T_POINTER;
	    sp->u.vec = res;
	}
	else     *sp = const0;
	break;
    }
#ifdef F_ORDER_ALIST
    CASE(F_ORDER_ALIST);
    {
	extern struct vector *order_alist PROT((struct vector *));
	struct svalue *args;
	struct vector *list;
	int listsize,keynum;

	if (num_arg == 1 && sp->u.vec->size 
          && sp->u.vec->item[0].type == T_POINTER) {
            args     = sp->u.vec->item;
	    listsize = sp->u.vec->size;
	} else {
	    args = sp-num_arg+1;
	    listsize = num_arg;
	}
	keynum = args[0].u.vec->size;
	list = allocate_array(listsize);
	for (i=0; i<listsize; i++) {
	    if (args[i].type != T_POINTER
	     || args[i].u.vec->size != keynum) {
		free_vector(list);
		error("bad data array %d in call to order_alist",i);
	    }
	    list->item[i].type = T_POINTER;
	    list->item[i].u.vec = slice_array(args[i].u.vec,0,keynum-1);
        }
        pop_n_elems(num_arg);
	sp++;
	sp->type = T_POINTER;
        sp->u.vec = order_alist(list);
	free_vector(list);
        break;
    }
#endif /* F_ORDER_ALIST */
#ifdef F_INSERT_ALIST
    CASE(F_INSERT_ALIST)
    {
	/* When the key list of an alist contains destructed objects
	   it is better not to free them till the next reordering by
	   order_alist to retain the alist property.
	 */
	extern struct svalue *insert_alist
	  PROT((struct svalue *key,struct svalue *key_data,
	    struct vector *list));
	struct vector *list;
	int listsize,keynum;
	struct svalue *key,*key_data,*ret;
	static struct vector tempvec = { 1,1, };

	if (sp->type != T_POINTER)
	    bad_arg(num_arg,F_INSERT_ALIST);
	if ( !(listsize = sp->u.vec->size) ||
	  sp->u.vec->item[0].type != T_POINTER ) {
	    list = &tempvec;
	    assign_svalue_no_free(list->item,sp);
	    listsize = 1;
	} else
	    list = sp->u.vec;
	keynum = list->item[0].u.vec->size;
	for (i=1; i<listsize; i++) {
	    if (list->item[i].type != T_POINTER
	      ||list->item[i].u.vec->size != keynum)
		bad_arg(num_arg,F_INSERT_ALIST);
	}
	if (num_arg == 2) {
	    if (sp[-1].type != T_POINTER) {
		key_data = (struct svalue*)NULL;
		key = sp-1;
	    } else {
	        if (sp[-1].u.vec->size != listsize)
		    bad_arg(1,F_INSERT_ALIST);
	        key_data = key = sp[-1].u.vec->item;
	    }
	} else {
	    if (num_arg - 1 != listsize)
		bad_arg(1,F_INSERT_ALIST);
    	    key_data = key = sp-num_arg+1;
	}
	ret = insert_alist(key,key_data,list);
	pop_n_elems(num_arg);
	sp++;
	*sp = *ret;
	break;
    }
#endif /* F_INSERT_ALIST */
#ifdef F_ASSOC
    CASE(F_ASSOC);
    {
	/* When the key list of an alist contains destructed objects
	   it is better not to free them till the next reordering by
	   order_alist to retain the alist property.
	 */
	int assoc PROT((struct svalue *key, struct vector *keys));
	struct svalue *args = sp -num_arg +1;
	struct vector *keys,*data;
	struct svalue *fail_val;
	int ix;

	if ( !args[1].u.vec->size ||
	  args[1].u.vec->item[0].type != T_POINTER ) {
	    keys = args[1].u.vec;
	    if (num_arg == 2) {
		data = (struct vector *)NULL;
	    } else {
		if (args[2].type != T_POINTER ||
		  args[2].u.vec->size != keys->size) {
		    bad_arg(3,F_ASSOC);
		}
		data = args[2].u.vec;
	    }
	    if (num_arg == 4) {
		fail_val = &args[3];
	    } else {
		fail_val = &const0;
	    }
	} else {
	    keys = args[1].u.vec->item[0].u.vec;
	    if (args[1].u.vec->size > 1) {
		if (args[1].u.vec->item[1].type != T_POINTER ||
		    args[1].u.vec->item[1].u.vec->size != keys->size) {
			bad_arg(2,F_ASSOC);
	        }
		data = args[1].u.vec->item[1].u.vec;
	    } else {
		data = (struct vector *)NULL;
	    }
	    if (num_arg == 3) fail_val = &args[2];
	    else if (num_arg == 2) fail_val = &const0;
	    else {
		error ("Too many args to efun assoc().\n");
	    }
	}
	ix = assoc(&args[0],keys);
	if (data == (struct vector *)NULL) {
	    pop_n_elems(num_arg);
	    push_number(ix);
	} else {
	    assign_svalue(args, ix==-1 ? fail_val : &data->item[ix]);
	    pop_n_elems(num_arg-1);
	}
        break;
    }
#endif /* F_ASSOC */
#ifdef F_INTERSECT_ALIST
    CASE(F_INTERSECT_ALIST);
    {
	extern struct vector *intersect_alist
	  PROT((struct vector *, struct vector *));
	struct vector *tmp = intersect_alist( (sp-1)->u.vec, sp->u.vec );
	pop_stack();
	free_vector(sp->u.vec);
	sp->u.vec = tmp;
    }
#endif /* F_INTERSECT_ALIST */
#ifdef F_DEBUG_INFO
    CASE(F_DEBUG_INFO);
    {
	struct svalue *arg = sp-num_arg+1;
	struct svalue res;

	switch ( arg[0].u.number ) {
	    case 0:
	    {
		int flags;
		struct object *obj2;

		if (num_arg != 2)
	    	    error("bad number of arguments to debug_info");
		if ( arg[1].type != T_OBJECT)
		    bad_arg(1,instruction);
		ob = arg[1].u.ob;
		flags = ob->flags;
		add_message("O_HEART_BEAT      : %s\n",
		  flags&O_HEART_BEAT      ?"TRUE":"FALSE");
		add_message("O_IS_WIZARD       : %s\n",
		  flags&O_IS_WIZARD       ?"TRUE":"FALSE");
		add_message("O_ENABLE_COMMANDS : %s\n",
		  flags&O_ENABLE_COMMANDS ?"TRUE":"FALSE");
		add_message("O_CLONE           : %s\n",
		  flags&O_CLONE           ?"TRUE":"FALSE");
		add_message("O_DESTRUCTED      : %s\n",
		  flags&O_DESTRUCTED      ?"TRUE":"FALSE");
		add_message("O_SWAPPED         : %s\n",
		  flags&O_SWAPPED          ?"TRUE":"FALSE");
		add_message("O_ONCE_INTERACTIVE: %s\n",
		  flags&O_ONCE_INTERACTIVE?"TRUE":"FALSE");
		add_message("O_APPROVED        : %s\n",
		  flags&O_APPROVED        ?"TRUE":"FALSE");
		add_message("O_RESET_STATE     : %s\n",
		  flags&O_RESET_STATE     ?"TRUE":"FALSE");
		add_message("O_WILL_CLEAN_UP   : %s\n",
		  flags&O_WILL_CLEAN_UP   ?"TRUE":"FALSE");
	        add_message("total light : %d\n", ob->total_light);
		add_message("next_reset  : %d\n", ob->next_reset);
		add_message("time_of_ref : %d\n", ob->time_of_ref);
		add_message("ref         : %d\n", ob->ref);
#ifdef DEBUG
		add_message("extra_ref   : %d\n", ob->extra_ref);
#endif
		add_message("swap_num    : %ld\n", ob->swap_num);
		add_message("name        : '%s'\n", ob->name);
		add_message("next_all    : OBJ(%s)\n",
		  ob->next_all?ob->next_all->name:"NULL");
		if (obj_list == ob) add_message(
		    "This object is the head of the object list.\n");
		for (obj2=obj_list,i=1; obj2; obj2=obj2->next_all,i++)
		    if (obj2->next_all == ob) {
			add_message(
			    "Previous object in object list: OBJ(%s)\n",
			    obj2->name);
			add_message("position in object list:%d\n",i);
		    }
		assign_svalue_no_free(&res,&const0);
		break;
	    }
	    case 1: {
		if (num_arg != 2)
	    	    error("bad number of arguments to debug_info");
		if ( arg[1].type != T_OBJECT)
		    bad_arg(1,instruction);
		ob = arg[1].u.ob;
		
		add_message("program ref's %d\n", ob->prog->ref);
		add_message("Name %s\n", ob->prog->name);
		add_message("program size %d\n",
		ob->prog->program_size);
		add_message("num func's %d (%d) \n", ob->prog->num_functions
		,ob->prog->num_functions * sizeof(struct function));
		add_message("num strings %d\n", ob->prog->num_strings);
		add_message("num vars %d (%d)\n", ob->prog->num_variables
		,ob->prog->num_variables * sizeof(struct variable));
		add_message("num inherits %d (%d)\n", ob->prog->num_inherited
		,ob->prog->num_inherited * sizeof(struct inherit));
		add_message("total size %d\n", ob->prog->total_size);
		assign_svalue_no_free(&res,&const0);
		break;
		}
	    default: bad_arg(1,instruction);
	}
	pop_n_elems(num_arg);
	sp++;
	*sp=res;
	break;
    }
#endif /* F_DEBUG_INFO */
    }
#ifdef DEBUG
    if (expected_stack && expected_stack != sp ||
	sp < fp + csp->num_local_variables - 1)
    {
	fatal("Bad stack after evaluation. Instruction %d, num arg %d\n",
	      instruction, num_arg);
    }
#endif /* DEBUG */
    goto again;
}

/*
 * Apply a fun 'fun' to the program in object 'ob', with
 * 'num_arg' arguments (already pushed on the stack).
 * If the function is not found, search in the object pointed to by the
 * inherit pointer.
 * If the function name starts with '::', search in the object pointed out
 * through the inherit pointer by the current object. The 'current_object'
 * stores the base object, not the object that has the current function being
 * evaluated. Thus, the variable current_prog will normally be the same as
 * current_object->prog, but not when executing inherited code. Then,
 * it will point to the code of the inherited object. As more than one
 * object can be inherited, the call of function by index number has to
 * be adjusted. The function number 0 in a superclass object must not remain
 * number 0 when it is inherited from a subclass object. The same problem
 * exists for variables. The global variables function_index_offset and
 * variable_index_offset keep track of how much to adjust the index when
 * executing code in the superclass objects.
 *
 * There is a special case when called from the heart beat, as
 * current_prog will be 0. When it is 0, set current_prog
 * to the 'ob->prog' sent as argument.
 *
 * Arguments are always removed from the stack.
 * If the function is not found, return 0 and nothing on the stack.
 * Otherwise, return 1, and a pushed return value on the stack.
 *
 * Note that the object 'ob' can be destructed. This must be handled by
 * the caller of apply().
 *
 * If the function failed to be called, then arguments must be deallocated
 * manually !
 */

#define CACHE_SIZE 256
char debug_apply_fun[30]; /* For debugging */

static int apply_low(fun, ob, num_arg)
    char *fun;
    struct object *ob;
    int num_arg;
{
    static int cache_id[CACHE_SIZE];
    static char *cache_name[CACHE_SIZE];
    static struct function *cache_pr[CACHE_SIZE];
    static struct function *cache_pr_inherited[CACHE_SIZE];
    static struct program *cache_progp[CACHE_SIZE];
    static int cache_function_index_offset[CACHE_SIZE];
    static int cache_variable_index_offset[CACHE_SIZE];

    struct function *pr;
    struct program *progp;
    extern int num_error;
    struct control_stack *save_csp;
    int ix;

    ob->time_of_ref = current_time;	/* Used by the swapper */

#ifdef COMPAT_MODE
    if (ob->next_reset < current_time) 
    {
       struct object *s = command_giver;
       int save = eval_cost;
   /* if it's past due, reset immediately, else just mark it to be reset */
       eval_cost = 0;
       reset_object(ob, 1);
       eval_cost = save;
       command_giver = s;
    }
    else 
#endif
       ob->flags &= ~O_RESET_STATE;

#ifdef DEBUG
    strncpy(debug_apply_fun, fun, sizeof debug_apply_fun);
    debug_apply_fun[sizeof debug_apply_fun - 1] = '\0';
#endif
    if (num_error > 0)
	goto failure;
    if (fun[0] == ':')
	error("Illegal function call\n");
    /*
     * If there is a chain of objects shadowing, start with the first
     * of these.
     */
    while (ob->shadowed && ob->shadowed != current_object)
	ob = ob->shadowed;
retry_for_shadow:
    if (ob->flags & O_SWAPPED)
	load_ob_from_swap(ob);
    progp = ob->prog;
#ifdef DEBUG
    if (ob->flags & O_DESTRUCTED)
	fatal("apply() on destructed object\n");
#endif
    ix = ( progp->id_number ^ (int)fun ^ ( (int)fun >> 6 ) ) % CACHE_SIZE;
    if (cache_id[ix] == progp->id_number && !strcmp(cache_name[ix], fun) &&
	(!cache_progp[ix] || cache_progp[ix] == ob->prog)) {
      /* We have found a matching entry in the cache. The pointer to
	 the function name has to match, not only the contents.
	 This is because hashing the string in order to get a cache index
	 would be much more costly than hashing it's pointer.
	 If cache access would be costly, the cache would be useless.
       */
      if (cache_progp[ix]) {
        /* the cache will tell us in which program the function is, and
	 * where
	 */
	push_control_stack(cache_pr[ix]);
	csp->num_local_variables = num_arg;
	current_prog = cache_progp[ix];
	pr = cache_pr_inherited[ix];
        function_index_offset = cache_function_index_offset[ix];
        variable_index_offset = cache_variable_index_offset[ix];
        /* Remove excessive arguments */
        while(csp->num_local_variables > pr->num_arg) {
            pop_stack();
            csp->num_local_variables--;
        }
        /* Correct number of arguments and local variables */
        while(csp->num_local_variables < pr->num_arg + pr->num_local) {
            push_number(0);
            csp->num_local_variables++;
        }
        tracedepth++;
        if (TRACEP(TRACE_CALL)) {
	    do_trace_call(pr);
        }
        fp = sp - csp->num_local_variables + 1;
        break_sp = (short*)(sp+1);
#if defined(COMPAT_MODE) || defined(OLD_PREVIOUS_OBJECT_BEHAVIOR)
        if (current_object != ob)
#endif
            previous_ob = current_object;
        frame_ob = current_object;
        current_object = ob;
        save_csp = csp;
        eval_instruction(current_prog->program + pr->offset);
#ifdef DEBUG
        if (save_csp-1 != csp)
            fatal("Bad csp after execution in apply_low\n");
#endif
        /*
         * Arguments and local variables are now removed. One
         * resulting value is always returned on the stack.
         */
        return 1;
      } /* when we come here, the cache has told us that the function isn't
	 * defined in the object
	 */
    } else {
	/* we have to search the function */
	if (!cache_progp[ix] && cache_id[ix]) {
	    /* The old cache entry was for an undefined function, so the
	       name had to be malloced */
	    free(cache_name[ix]);
	}
	cache_id[ix] = progp->id_number;
        for(pr=progp->functions; pr < progp->functions + progp->num_functions;
            pr++)
        {
            eval_cost++;
            if (pr->name == 0 ||
                pr->name[0] != fun[0] ||
                strcmp(pr->name, fun) != 0 ||
                (pr->type & TYPE_MOD_PRIVATE))
            {
                continue;
            }
            if (pr->flags & NAME_UNDEFINED)
                continue;
            /* Static functions may not be called from outside. */
            if ((pr->type & (TYPE_MOD_STATIC|TYPE_MOD_PRIVATE)) &&
		current_object != ob)
	    {
                continue;
	    }
	    /* The searched function is found */
	    cache_pr[ix] = pr;
	    cache_name[ix] = pr->name;
	    push_control_stack(pr);
	    csp->num_local_variables = num_arg;
	    current_prog = progp;
	    pr = setup_new_frame(pr);
	    cache_pr_inherited[ix] = pr;
	    cache_progp[ix] = current_prog;
	    cache_variable_index_offset[ix] = variable_index_offset;
	    cache_function_index_offset[ix] = function_index_offset;
#if defined(COMPAT_MODE) || defined(OLD_PREVIOUS_OBJECT_BEHAVIOR)
            if (current_object != ob)
#endif
                previous_ob = current_object;
            frame_ob = current_object;
            current_object = ob;
            save_csp = csp;
            eval_instruction(current_prog->program + pr->offset);
#ifdef DEBUG
            if (save_csp-1 != csp)
                fatal("Bad csp after execution in apply_low\n");
#endif
            /*
             * Arguments and local variables are now removed. One
             * resulting value is always returned on the stack.
             */
            return 1;
	}
	/* We have to mark a function not to be in the object */
	cache_name[ix] = string_copy(fun);
	cache_progp[ix] = (struct program *)0;
    }
    if (ob->shadowing) {
	/*
	 * This is an object shadowing another. The function was not found,
	 * but can maybe be found in the object we are shadowing.
	 */
	ob = ob->shadowing;
	goto retry_for_shadow;
    }
failure:
    /* Failure. Deallocate stack. */
    pop_n_elems(num_arg);
    return 0;
}

/*
 * Arguments are supposed to be
 * pushed (using push_string() etc) before the call. A pointer to a
 * 'struct svalue' will be returned. It will be a null pointer if the called
 * function was not found. Otherwise, it will be a pointer to a static
 * area in apply(), which will be overwritten by the next call to apply.
 * Reference counts will be updated for this value, to ensure that no pointers
 * are deallocated.
 */

static struct svalue *sapply(fun, ob, num_arg)
    char *fun;
    struct object *ob;
    int num_arg;
{
#ifdef DEBUG
    struct svalue *expected_sp;
#endif
    static struct svalue ret_value = { T_NUMBER };

    if (TRACEP(TRACE_APPLY)) {
	do_trace("Apply", "", "\n");
    }
#ifdef DEBUG
    expected_sp = sp - num_arg;
#endif
    if (apply_low(fun, ob, num_arg) == 0)
	return 0;
    assign_svalue(&ret_value, sp);
    pop_stack();
#ifdef DEBUG
    if (expected_sp != sp)
	fatal("Corrupt stack pointer.\n");
#endif
    return &ret_value;
}

struct svalue *apply(fun, ob, num_arg)
    char *fun;
    struct object *ob;
    int num_arg;
{
    tracedepth = 0;
    return sapply(fun, ob, num_arg);
}

/*
 * This function is similar to apply(), except that it will not
 * call the function, only return object name if the function exists,
 * or 0 otherwise.
 */
char *function_exists(fun, ob)
    char *fun;
    struct object *ob;
{
    struct function *pr;

#ifdef DEBUG
    if (ob->flags & O_DESTRUCTED)
	fatal("function_exists() on destructed object\n");
#endif
    if (ob->flags & O_SWAPPED)
	load_ob_from_swap(ob);
    pr = ob->prog->functions;
    for(; pr < ob->prog->functions + ob->prog->num_functions; pr++) {
	struct program *progp;

	if (pr->name[0] != fun[0] || strcmp(pr->name, fun) != 0)
	    continue;
	/* Static functions may not be called from outside. */
	if ((pr->type & TYPE_MOD_STATIC) && current_object != ob)
	    continue;
	if (pr->flags & NAME_UNDEFINED)
	    return 0;
	for (progp = ob->prog; pr->flags & NAME_INHERITED;) {
	    progp = progp->inherit[pr->offset].prog;
	    pr = &progp->functions[pr->function_index_offset];
	}
	return progp->name;
    }
    return 0;
}

/*
 * Call a specific function address in an object. This is done with no
 * frame set up. It is expected that there are no arguments. Returned
 * values are removed.
 */

void call_function(progp, pr)
    struct program *progp;
    struct function *pr;
{
    if (pr->flags & NAME_UNDEFINED)
	return;
    push_control_stack(pr);
#ifdef DEBUG
    if (csp != control_stack)
	fatal("call_function with bad csp\n");
#endif
    csp->num_local_variables = 0;
    current_prog = progp;
    pr = setup_new_frame(pr);
    previous_ob = current_object;
    tracedepth = 0;
    eval_instruction(current_prog->program + pr->offset);
    pop_stack();	/* Throw away the returned result */
}

/*
 * This can be done much more efficiently, but the fix has
 * low priority.
 */
static int get_line_number(p, progp)
    char *p;
    struct program *progp;
{
    int offset;
    int i;
    if (progp == 0)
	return 0;
    offset = p - progp->program;
#ifdef DEBUG
    if (offset > progp->program_size)
	fatal("Illegal offset %d in object %s\n", offset, progp->name);
#endif
    for (i=0; offset > progp->line_numbers[i]; i++)
	;
    return i+1;
}
    
/*
 * Write out a trace. If there is an heart_beat(), then return the
 * object that had that heart beat.
 */
#if defined(DEBUG) && defined(TRACE_CODE)
char *dump_trace(how)
int how;
#else
char *dump_trace()
#endif
{
    struct control_stack *p;
    char *ret = 0;
#ifdef DEBUG
    int last_instructions PROT((void));
#endif

    if (current_prog == 0)
	return 0;
    if (csp < &control_stack[0]) {
	(void)printf("No trace.\n");
	debug_message("No trace.\n");
	return 0;
    }
#ifdef DEBUG
#ifdef TRACE_CODE
    if (how)
	(void)last_instructions();
#endif
#endif
    for (p = &control_stack[0]; p < csp; p++) {
	(void)printf("\"%s\" in \"%s\" (\"%s\") line %d\n",
		     p[0].funp ? p[0].funp->name : "CATCH",
		     p[1].prog->name, p[1].ob->name,
		     get_line_number(p[1].pc, p[1].prog));
	debug_message("\"%s\" in \"%s\" (\"%s\") line %d\n",
		     p[0].funp ? p[0].funp->name : "CATCH",
		     p[1].prog->name, p[1].ob->name,
		     get_line_number(p[1].pc, p[1].prog));
	if (p->funp && strcmp(p->funp->name, "heart_beat") == 0)
	    ret = p->ob?p->ob->name:0;
    }
    (void)printf("\"%s\" in \"%s\" (\"%s\") line %d\n",
		 p[0].funp ? p[0].funp->name : "CATCH",
		 current_prog->name, current_object->name,
		 get_line_number(pc, current_prog));
    debug_message("\"%s\" in \"%s\" (\"%s\") line %d\n",
		 p[0].funp ? p[0].funp->name : "CATCH",
		 current_prog->name, current_object->name,
		 get_line_number(pc, current_prog));
    return ret;
}

int get_line_number_if_any() {
    if (current_prog)
	return get_line_number(pc, current_prog);
    return 0;
}

static char *find_percent(str)
    char *str;
{
    while(1) {
	str = strchr(str, '%');
	if (str == 0)
	    return 0;
	if (str[1] != '%')
	    return str;
	str++;
    }
}

static int inter_sscanf(num_arg)
    int num_arg;
{
    char *fmt;		/* Format description */
    char *in_string;	/* The string to be parsed. */
    int number_of_matches;
    char *cp;
    struct svalue *arg = sp - num_arg + 1;

    /*
     * First get the string to be parsed.
     */
    if (arg[0].type != T_STRING && arg[0].type != T_OBJECT)
	bad_arg(1, F_SSCANF);
    if (arg[0].type == T_STRING)
       in_string = arg[0].u.string;
    else in_string = arg[0].u.ob->name;
    if (in_string == 0)
	return 0;
    /*
     * Now get the format description.
     */
    if (arg[1].type != T_STRING)
	bad_arg(2, F_SSCANF);
    fmt = arg[1].u.string;
    /*
     * First, skip and match leading text.
     */
    for (cp=find_percent(fmt); fmt != cp; fmt++, in_string++) {
	if (in_string[0] == '\0' || fmt[0] != in_string[0])
	    return 0;
    }
    /*
     * Loop for every % or substring in the format. Update num_arg and the
     * arg pointer continuosly. Assigning is done manually, for speed.
     */
    num_arg -= 2;
    arg += 2;
    for (number_of_matches=0; num_arg > 0;
	 number_of_matches++, num_arg--, arg++) {
	int i, type;

	if (fmt[0] == '\0') {
	    /*
	     * We have reached end of the format string.
	     * If there are any chars left in the in_string,
	     * then we put them in the last variable (if any).
	     */
	    if (in_string[0]) {
		free_svalue(arg->u.lvalue, "F_SSCANF");
		arg->u.lvalue->type = T_STRING;
		arg->u.lvalue->u.string = make_shared_string(in_string);
		arg->u.lvalue->string_type = STRING_SHARED;/**MALLOC**/
		number_of_matches++;
	    }
	    break;
	}
#ifdef DEBUG
	if (fmt[0] != '%')
	    fatal("Should be a %% now !\n");
#endif
	type = T_STRING;
	if (fmt[1] == 'd')
	    type = T_NUMBER;
	else if (fmt[1] != 's')
	    error("Bad type : '%%%c' in sscanf fmt string.", fmt[1]);
	fmt += 2;
	/*
	 * Parsing a number is the easy case. Just use strtol() to
	 * find the end of the number.
	 */
	if (type == T_NUMBER) {
	    char *tmp = in_string;
	    int tmp_num;

	    tmp_num = (int) strtol(in_string, &in_string, 10);
	    if(tmp == in_string) {
		/* No match */
		break;
	    }
	    free_svalue(arg->u.lvalue, "F_SSCANF");
	    arg->u.lvalue->type = T_NUMBER;
	    arg->u.lvalue->u.number = tmp_num;
	    while(fmt[0] && fmt[0] == in_string[0])
		fmt++, in_string++;
	    if (fmt[0] != '%') {
		number_of_matches++;
		break;
	    }
	    continue;
	}
	/*
	 * Now we have the string case.
	 */
	cp = find_percent(fmt);
	if (cp == fmt)
	    error("Illegal to have 2 adjacent %'s in fmt string in sscanf.");
	if (cp == 0)
	    cp = fmt + strlen(fmt);
	/*
	 * First case: There was no extra characters to match.
	 * Then this is the last match.
	 */
	if (cp == fmt) {
	    free_svalue(arg->u.lvalue, "F_SSCANF");
	    arg->u.lvalue->type = T_STRING;
	    arg->u.lvalue->u.string = make_shared_string(in_string);
	    arg->u.lvalue->string_type = STRING_SHARED; /**MALLOC**/
	    number_of_matches++;
	    break;
	}
	for (i=0; in_string[i]; i++) {
	    if (strncmp(in_string+i, fmt, cp - fmt) == 0) {
		char *match;
		/*
		 * Found a match !
		 */
		match = xalloc(i+1);
		(void)strncpy(match, in_string, i);
		in_string += i + cp - fmt;
		match[i] = '\0';
		free_svalue(arg->u.lvalue, "F_SSCANF");
		arg->u.lvalue->type = T_STRING;
		arg->u.lvalue->u.string = make_shared_string(match);
		arg->u.lvalue->string_type = STRING_SHARED;
                free(match); /**MALLOC**/
		fmt = cp;	/* Advance fmt to next % */
		break;
	    }
	}
	if (fmt == cp)	/* If match, then do continue. */
	    continue;
	/*
	 * No match was found. Then we stop here, and return
	 * the result so far !
	 */
	break;
    }
    return number_of_matches;
}

/* test stuff ... -- LA */
#ifdef OPCPROF
void opcdump()
{
    int i;

    for(i = 0; i < MAXOPC; i++)
	if (opcount[i]) printf("%d: %d\n", i, opcount[i]);
}
#endif

/*
 * Reset the virtual stack machine.
 */
void reset_machine(first)
    int first;
{
    csp = control_stack - 1;
    if (first)
	sp = start_of_stack - 1;
    else
	pop_n_elems(sp - start_of_stack + 1);
}

#ifdef TRACE_CODE

static char *get_arg(a, b)
    int a, b;
{
    static char buff[10];
    char *from, *to;

    from = previous_pc[a]; to = previous_pc[b];
    if (to - from < 2)
	return "";
    if (to - from == 2) {
	sprintf(buff, "%d", from[1]);
	return buff;
    }
    if (to - from == 3) {
	short arg;
	((char *)&arg)[0] = from[1];
	((char *)&arg)[1] = from[2];
	sprintf(buff, "%d", arg);
	return buff;
    }
    if (to - from == 5) {
	int arg;
	((char *)&arg)[0] = from[1];
	((char *)&arg)[1] = from[2];
	((char *)&arg)[2] = from[3];
	((char *)&arg)[3] = from[4];
	sprintf(buff, "%d", arg);
	return buff;
    }
    return "";
}

int last_instructions() {
    int i;
    i = last;
    do {
	if (previous_instruction[i] != 0)
	    printf("%6x: %3d %8s %-25s (%d)\n", previous_pc[i],
		   previous_instruction[i],
		   get_arg(i, (i+1) %
			   (sizeof previous_instruction / sizeof (int))),
		   get_f_name(previous_instruction[i]),
		   stack_size[i] + 1);
	i = (i + 1) % (sizeof previous_instruction / sizeof (int));
    } while (i != last);
    return last;
}

#endif /* TRACE_CODE */


#ifdef DEBUG

static void count_inherits(progp, search_prog)
    struct program *progp, *search_prog;
{
    int i;
    if (!progp || progp->extra_ref != 1) return;
    for (i=0; i< progp->num_inherited; i++) {
	progp->inherit[i].prog->extra_ref++;
	if (progp->inherit[i].prog == search_prog)
	    printf("Found prog, inherited by %s\n", progp->name);
        count_inherits(progp->inherit[i].prog, search_prog);
    }
}

static void count_ref_in_vector(svp, num)
    struct svalue *svp;
    int num;
{
    struct svalue *p;

    for (p = svp; p < svp+num; p++) {
	switch(p->type) {
	case T_OBJECT:
	    p->u.ob->extra_ref++;
	    continue;
	case T_POINTER:
	    count_ref_in_vector(&p->u.vec->item[0], p->u.vec->size);
	    p->u.vec->extra_ref++;
	    continue;
	}
    }
}

void clear_vector_refs(svp, num)
    struct svalue *svp;
    int num;
{
    struct svalue *p;

    for (p = svp; p < svp+num; p++) {
	switch(p->type) {
	case T_POINTER:
	    clear_vector_refs(&p->u.vec->item[0], p->u.vec->size);
	    p->u.vec->extra_ref=0;
	    continue;
	}
    }
}

/*
 * Loop through every object and variable in the game and check
 * all reference counts. This will surely take some time.
 */
void check_a_lot_ref_counts(search_prog)
    struct program *search_prog;
{
    extern struct object *master_ob;
    struct object *ob;
    struct svalue *p;

    /*
     * Pass 1: clear the ref counts.
     */
    for (ob=obj_list; ob; ob = ob->next_all) {
	ob->extra_ref = 0;
	ob->prog->extra_ref = 0;
	clear_vector_refs(ob->variables, ob->prog->num_variables);
    }
    for (ob=obj_list_destruct; ob; ob = ob->next_all) {
        ob->extra_ref = 0;
        ob->prog->extra_ref = 0;
	clear_vector_refs(ob->variables, ob->prog->num_variables);
    }

    clear_vector_refs(start_of_stack, sp - start_of_stack + 1);

    /*
     * Pass 2: Compute the ref counts.
     */

    /*
     * List of all objects.
     */
    for (ob=obj_list; ob; ob = ob->next_all) {
	ob->extra_ref++;
	count_ref_in_vector(ob->variables, ob->prog->num_variables);
	ob->prog->extra_ref++;
	if (ob->prog == search_prog)
	    printf("Found program for object %s\n", ob->name);
	/* Clones will not add to the ref count of inherited progs */
	if (ob->prog->extra_ref == 1)
	    count_inherits(ob->prog, search_prog);
    }
    for (ob=obj_list_destruct; ob; ob = ob->next_all) 
    {
	ob->extra_ref++;
	count_ref_in_vector(ob->variables, ob->prog->num_variables);
	ob->prog->extra_ref++;
	if (ob->prog == search_prog)
	    printf("Found program for destructed object %s\n", ob->name);
	/* Clones will not add to the ref count of inherited progs */
	if (ob->prog->extra_ref == 1)
	    count_inherits(ob->prog, search_prog);
    }

    /*
     * The current stack.
     */
    count_ref_in_vector(start_of_stack, sp - start_of_stack + 1);
    update_ref_counts_for_players();
    count_ref_from_call_outs();
    if (master_ob) master_ob->extra_ref++; /* marion */

    if (search_prog)
	return;

    /*
     * Pass 3: Check the ref counts.
     */
    for (ob=obj_list; ob; ob = ob->next_all) {
	if (ob->ref != ob->extra_ref)
 	    fatal("Bad ref count in object %s, %d - %d\n", ob->name,
		  ob->ref, ob->extra_ref);
	if (ob->prog->ref != ob->prog->extra_ref) {
	    check_a_lot_ref_counts(ob->prog);
	    fatal("Bad ref count in prog %s, %d - %d\n", ob->prog->name,
		  ob->prog->ref, ob->prog->extra_ref);
	}
    }
}

#endif /* DEBUG */

/*
 * Added 3/31/91 by Raistlin.
 * added attributes to objects.
 */

static Attribute *find_attribute(attr, obj)
char *attr;
struct object *obj;
{
   int i;

   for (i=0; i<obj->num_attributes; ++i)
      if (strcmp(attr, obj->attributes[i].attribute) == 0)
         return &(obj->attributes[i]);
   return NULL;
}

static void add_attribute(attr, obj, val, prot)
char *attr;
struct object *obj;
int val;
int prot;
{
   int size;
   Attribute *a;   

   
   if (a = find_attribute(attr, obj)) 
   { 
      a->value = val;
      if (prot) 
         a->protected = prot;
      return;
   }
   size = ++(obj->num_attributes);
   if (obj->attributes)
      obj->attributes = (Attribute *)
            realloc((char *)obj->attributes, size*sizeof(Attribute));
   else 
      obj->attributes = (Attribute *) xalloc(sizeof(Attribute));

   obj->attributes[size-1].attribute = make_shared_string(attr);
   obj->attributes[size-1].value = val;
   obj->attributes[size-1].protected = prot;
}

static int query_attribute(attr, obj)
char *attr;
struct object *obj;
{
   return find_attribute(attr, obj) != 0;
}

static int get_attribute(str, obj)
char *str;
struct object *obj;
{
   Attribute *attrib;
   if (attrib = find_attribute(str, obj))
       return attrib->value;
   else
       return -1;
}

static struct vector *get_attr_vector(obj)
struct object *obj;
{
   struct vector *vec;
   int i;

   vec = allocate_array(obj->num_attributes*2);
   for (i=0; i<obj->num_attributes; ++i)
   {
      vec->item[i*2].type = T_STRING;
      vec->item[i*2].string_type = STRING_SHARED;
      vec->item[i*2].u.string=make_shared_string(obj->attributes[i].attribute);
      vec->item[i*2+1].type = T_NUMBER;
      vec->item[i*2+1].u.number = obj->attributes[i].value;
   }
   return vec;
}

static void delete_attribute(attr, obj)
char *attr;
struct object *obj;
{
   Attribute *del;
   
   if (!(del = find_attribute(attr, obj))) return;
   if (del->protected)
   {
      error("Error, cannot delete protected attribute %s.\n", del->attribute);
      return;
   }

   if (obj->num_attributes == 1)
   {
      free_string(obj->attributes[0].attribute);
      free((char *)obj->attributes);
      obj->attributes = NULL;
      obj->num_attributes = 0;
      return;
   }
   free_string(del->attribute);
   memcpy(del, del+1, (obj->attributes+obj->num_attributes-del-1)*
          sizeof(Attribute));

   obj->num_attributes--;
   if (obj->num_attributes)
      obj->attributes = (Attribute *) realloc((char *)obj->attributes, 
            obj->num_attributes);
   else 
   {
      free((char *)obj->attributes); 
      obj->attributes = NULL;
   }
} 


static int search_string(str, what, where)
char *str;
int what, where;
{
   char *i;

   i = (char *) index(str+where, what);
   if (i) return i-str;
   else return -1;
}

int search_array(a, target, ind)
struct svalue *a;
struct svalue *target;
int ind;
{
    int ret;
    struct svalue orig_targ;
    int i;
 
    if (a->type == T_STRING)
    {
       if (target->type != T_NUMBER)
          error("Incompatible argument 2 to searcha on a string.\n");
       return search_string(a->u.string, target->u.number, ind);
    }   
    if(a->u.vec->size==0 && ind==0) return -1;
    if (ind+1 > a->u.vec->size || ind<0 || ind>MAX_ARRAY_SIZE)
        error("Illegal ind to searcha().\n");
    
    orig_targ= *target;
    if (target->type==T_OBJECT && (target->u.ob->flags&O_DESTRUCTED)) {
	target->type=T_NUMBER;
	target->u.number=0;
    }
    ret = -1;
    for (i=ind; i < a->u.vec->size; i++) {

	if (a->u.vec->item[i].type!=target->type &&
		!(a->u.vec->item[i].type==T_OBJECT && 
                  (a->u.vec->item[i].u.ob->flags&O_DESTRUCTED) && 
        	target->type==T_NUMBER))
	    continue;
	switch (target->type) {
	    case T_OBJECT:
		if (a->u.vec->item[i].u.ob==target->u.ob &&
			!(a->u.vec->item[i].u.ob->flags&O_DESTRUCTED))
		    ret = i;
		break;
	    case T_STRING:
		if (strcmp(a->u.vec->item[i].u.string,target->u.string)==0)
		    ret = i;
		break;
	    case T_POINTER:
		if (a->u.vec->item[i].u.vec==target->u.vec)
		    ret = i;
		break;
	    case T_NUMBER:
		if (a->u.vec->item[i].u.number==target->u.number)
		    ret = i;
		else
                    if (a->u.vec->item[i].type==T_OBJECT &&
			(a->u.vec->item[i].u.ob->flags&O_DESTRUCTED) &&
			    target->u.number==0)
			ret = i;
		break;
	    default:
		break;
	}
	if (ret != -1)
	    break;
    }
    *target=orig_targ;
    return ret;
}

struct vector *reallocate_array(a, n)
	struct vector *a;
	int n;
{
	int i,j;
	struct vector *p,*ap;
 
	if (n < 0 || n > MAX_ARRAY_SIZE) error("Illegal array size.\n");
        p = allocate_array(n);
/*
	p = ALLOC_VECTOR(n);
	p->ref = 1;
	p->size = n;
	p->user = current_object->user;
	if (p->user) p->user->size_array += n;
*/
	j = ((a->size) < n) ? (a->size) : n;
	ap = a;
	for (i=0; i<n; i++) {
 		if (i<j) {
 			p->item[i].type=ap->item[i].type;
 
 			switch (p->item[i].type) {
			case T_STRING:
				p->item[i].u.string =
				    make_shared_string(ap->item[i].u.string);
                                p->item[i].string_type = STRING_SHARED;
 				break;
 			case T_OBJECT:
				if (ap->item[i].u.ob->flags&O_DESTRUCTED) {
					p->item[i].type=T_NUMBER;
					p->item[i].u.number=0;
				} else {
					p->item[i].u.ob=ap->item[i].u.ob; 
 					add_ref(p->item[i].u.ob,
					    "reallocate_array");
				}
		 		break;
 			case T_POINTER:
				p->item[i].u.vec=ap->item[i].u.vec;
 				p->item[i].u.vec->ref++;
		 		break;
 			case T_NUMBER:
				p->item[i].u.number=ap->item[i].u.number;
				break;
			default:
				p->item[i].type=T_NUMBER;
				p->item[i].u.number=0;
				break;
 			}
	 	} else {
 			p->item[i].type=T_NUMBER;
 			p->item[i].u.number=0;
		}
	}
        return p;
}

struct object *caller()
{
   if (frame_ob == 0 || (frame_ob->flags&O_DESTRUCTED))
      return 0;
   return frame_ob;
}

int rename_file(src, dst)
    char *src, *dst;
{
    extern int sys_nerr, errno;
    extern char *sys_errlist[];
    char tmp1[500], tmp2[500];

#ifdef COMPAT_MODE
    {
       src = check_file_name(src, 0);
       if (!src) return 0;
       strncpy(tmp1, src, sizeof tmp1);
       tmp1[sizeof tmp1 - 1] = 0;
       dst = check_file_name(dst, 1);
       if (!dst) return 0;
       strncpy(tmp2, dst, sizeof tmp2);
       tmp2[sizeof tmp2 - 1] = 0;  
       src = tmp1; dst = tmp2;
    }
#else
    {
       src = check_valid_path(src, current_object->eff_user, "rename", 0);
       strcpy(tmp1, src);
       dst = check_valid_path(dst, current_object->eff_user, "rename", 1);
       strcpy(tmp2, dst);
    }
#endif
    if (rename(src, dst) == -1)
    {
	add_message("rename: %s\n",
		    errno < sys_nerr ? sys_errlist[errno] : "unknown error");
        return 0;
    } 
    return 1;
}


int copy_file(src, dst)
    char *src, *dst;
{
    FILE *src_f, *dst_f;
    int c;
    char tmp1[500], tmp2[500];

        /*
         * right about here, it should have an if (!o_flag) check_valid_path
         * call, else check_file_name!! Don't know about my syntax though...
         * --Buddha
         *
         * Changed to use COMPAT_MODE -- Raistlin
         */
#ifndef COMPAT_MODE
    src = check_valid_path(src, current_object->eff_user, "copy_file", 0);
#else
    src = check_file_name(src, 0);
#endif
    if (!src) return 0;
    strncpy(tmp1, src, sizeof tmp1);
    tmp1[sizeof tmp1 - 1] = 0;

        /*
         * Also there should be one here.
         */
#ifndef COMPAT_MODE
    dst = check_valid_path(dst, current_object->eff_user, "copy_file", 1);
#else
    dst = check_file_name(dst, 1);
#endif
    if (!dst) return 0;
    strncpy(tmp2, dst, sizeof tmp2);
    tmp1[sizeof tmp2 - 1] = 0;

    src = tmp1; dst = tmp2; 

    src_f = fopen(src, "r");
    if (src_f == 0)
	return 0;
    dst_f = fopen(dst, "w");
    if (dst_f == 0) {
	fclose(src_f);
	return 0;
    }
    while ((c = fgetc(src_f)) != EOF)
	fputc(c, dst_f);
    fclose(src_f);
    fclose(dst_f);
    return 1;
}

struct vector *grab_file(file, start, len)
char *file;
int start, len;
{
    FILE *f;
    char buf[257];
    int line_count=0, char_count=0, i;
    struct vector *ret;

#ifndef COMPAT_MODE
    file = check_valid_path(file, current_object->eff_user, "grab_file", 0);
#else
    file = check_file_name(file, 0);
#endif

    if (!file)
	return 0;
    f = fopen(file, "r");
    if (f == 0)
	return 0;
    if (start < 1) start = 1;
    if (len < 1 || len > MAX_ARRAY_SIZE) len = MAX_ARRAY_SIZE;
    while (!feof(f)) 
    { 
       fgets(buf, 256, f);
       if (feof(f)) break;
       line_count++;
       char_count += strlen(buf);
       if (char_count > READ_FILE_MAX_SIZE)
       {
          line_count--;
          break;
       }
    }
    if (start > line_count)
    {
       fclose(f);
       return 0;
    }
    rewind(f);
/* truncate if you try to read past end of file */
    if ((line_count-start+1) < len)
       len = line_count-start+1;
    ret = allocate_array(len);
    for (i=0; i<(len+start-1); ++i)
    {
       char *p;
       fgets(buf, 256, f); 
       if (feof(f)) break;
       if (i < start-1) continue;
       p = strchr(buf, '\n');
       if (p) 
          *p = 0; 
       else buf[sizeof buf -1] = 0;
       ret->item[i-start+1].type = T_STRING;
       ret->item[i-start+1].string_type = STRING_SHARED;
       ret->item[i-start+1].u.string = make_shared_string(buf);
    }
    fclose(f);
    return ret;
}

/* Generate a debug message to the player */
static void
do_trace(msg, fname, post)
char *msg, *fname, *post;
{
    char buf[10000];
    char *objname;

    if (!TRACEHB)
	return;

    if (TRACETST(TRACE_OBJNAME))
    {
	if (current_object && current_object->name)
	    objname = current_object->name;
        else objname = "??";
    }
    else objname = "";

    sprintf(buf, "*** %d %*s %s %s %s%s", tracedepth, tracedepth, "", 
                  msg, objname, fname, post);
    add_message(buf);
}

struct svalue *apply_master_ob(fun, num_arg)
    char *fun;
    int num_arg;
{
    extern struct object *master_ob;
    int save_eval_cost = eval_cost;
    struct svalue *ret;

    eval_cost = 0;
    assert_master_ob_loaded();
    /*
     * Maybe apply() should be called instead ?
     */
    ret = sapply(fun, master_ob, num_arg);
    eval_cost = save_eval_cost;
    return ret;
}

void assert_master_ob_loaded()
{
    extern struct object *master_ob;
    static int inside = 0;
#ifndef COMPAT_MODE
    struct svalue *ret;
#endif

    if (master_ob == 0 || master_ob->flags & O_DESTRUCTED) {
       /*
        * The master object has been destructed.  Free our reference,
        * and load a new one.
        *
	* This test is needed because the master object is called from
	* yyparse() at an error to find the wizard name. However, and error
	* when loading the master object will cause a recursive call to this
	* point.  
	*
	* The best solution would be if the yyparse() did not have to call
	* the master object to find the name of the wizard.
	*/
	if (inside) {
 	    fprintf(stderr, "Failed to load master object.\n");
 	    add_message("Failed to load master file !\n");
 	    exit(1);
 	}
      fprintf(stderr, "assert_master_ob_loaded: Reloading master.c\n");
      if (master_ob)
	  free_object(master_ob, "assert_master_ob_loaded");
      /*
       * Clear the pointer, in case the load failed.
       */
      master_ob = 0;
      inside = 1;
#ifdef COMPAT_MODE
      master_ob = load_object("obj/master", 0);
#else
      master_ob = load_object("secure/master", 0);

      ret = apply_master_ob("get_root_uid", 0);
      if (ret == 0 || ret->type != T_STRING) 
           fatal ("get_root_uid() in secure/master.c does not work\n");
      master_ob->user = add_name(ret->u.string);
      master_ob->eff_user = master_ob->user;
#endif
      inside = 0;
      add_ref(master_ob, "assert_master_ob_loaded");
      fprintf(stderr, "Reloading done.\n");
    }
}


/*
* When an object is destructed, all references to it must be removed
* from the stack.
*/
void remove_object_from_stack(ob)
   struct object *ob;
{
   struct svalue *svp;

   for (svp = start_of_stack; svp <= sp; svp++) {
	if (svp->type != T_OBJECT)
	    continue;
	if (svp->u.ob != ob)
	    continue;
	free_object(svp->u.ob, "remove_object_from_stack");
	svp->type = T_NUMBER;
	svp->u.number = 0;
   }
}

static int
strpref(p, s)
char *p, *s;
{
   while (*p)
	if (*p++ != *s++)
	    return 0;
   return 1;
}