#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>
/* #include <netinet/in.h> Included in comm.h below */
#include <memory.h>

#include <math.h>

#include "config.h"
#include "lint.h"
#include "lang.h"
#include "exec.h"
#include "interpret.h"
#include "object.h"
#include "instrs.h"
#include "patchlevel.h"
#include "comm.h"
#include "switch.h"
#include "mapping.h"
#include "mudstat.h"
#include "lex.h"

#ifdef RUSAGE			/* Defined in config.h */
#ifndef SOLARIS
#include <sys/resource.h>
extern int getrusage (int, struct rusage *);
#ifdef sun
extern int getpagesize();
#endif
#ifndef RUSAGE_SELF
#define RUSAGE_SELF	0
#endif
#else /* SOLARIS */
#include <sys/times.h>
#include <limits.h>
#endif /* SOLARIS */
#endif
struct fkntab
{
    char *name;
    unsigned short inherit_index;
    unsigned short function_index;
};

extern struct object *master_ob;

extern void print_svalue (struct svalue *, struct object *);
struct svalue *sapply (char *, struct object *, int, int);
static void do_trace (char *, char *, char *);
static int apply_low (char *, struct object *, int, int);
static int strpref (char *, char *);
extern int do_rename (char *, char *);     
static int inter_sscanf (int);

extern struct object *previous_ob;
extern char *last_verb;
extern struct svalue const0, const1;
struct program *current_prog;
extern int current_time, s_flag;
extern struct object *current_heart_beat, *current_interactive;
int variable_index_found;
int variable_inherit_found;    
int variable_type_mod_found;
int cache_tries = 0, cache_hits = 0;

#ifdef CACHE_STATS
int call_cache_saves = 0;
int global_cache_saves = 0;
int searches_needed = 0;
int searches_done = 0;

int global_first_saves = 0;
int call_first_saves = 0;
#endif

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 isnt 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. */
    struct svalue *sp;	        /* Points to value of last push. */
    int inh_offset;            /* Needed for inheritance */

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];
struct control_stack *csp;	/* Points to last element pushed */

#ifdef COUNT_CALLS /* Temporary */
static int num_call_self, num_call_down, num_call_other;
#endif

/* These are set by search_for_function if it is successful 
 * function_inherit_found == num_inherit if in top program 
 * function_prog_found	  == Implied by inherit_found 
 */
int    		function_inherit_found 	= -1; 
struct 	program *function_prog_found	= 0; 	
int 		function_index_found	= -1;
unsigned  short	function_type_mod_found = 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 onto the stack.
 */
INLINE void 
push_object(struct object *ob)
{
    sp++;
    if (sp == &start_of_stack[EVALUATOR_STACK_SIZE])
	fatal("stack overflow\n");
    if (ob)
	{
	    sp->type = T_OBJECT;
	    sp->u.ob = ob;
	    add_ref(ob, "push_object");
	}
    else
	{
	    sp->type = T_NUMBER;
	    sp->u.number = 0;
	}
}

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

/*
 * Push a string on the value stack.
 */
INLINE void 
push_string(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(int inh, int rnum)
{
    int num;
    if ((inh == 255 && rnum == 255))
    {
	error("Referencing undefined variable (inh == %d, var_num == %d, variables are %s).\n",
	      inh, rnum, current_object->variables?"defined":"undefined");
    }
    if (inh == 255)
	inh = 0;
    else
	inh -= current_prog->num_inherited - 1;
#ifdef DEBUG
    if (inh > 0)
	fatal("Illegal variable access, %d(off %d). See trace above.\n",
	      inh, current_prog->num_inherited);
    if (rnum > current_object->prog->inherit[inh_offset + inh].prog->num_variables - 1)
	fatal("Illegal variable access, variable %d(off %d, in %d). See trace above.\n",
	      rnum, current_object->prog->inherit[inh_offset + inh].prog->num_variables,
	      inh_offset + inh);
#endif
    num =  current_object->prog->
	inherit[inh_offset + inh].variable_index_offset + rnum;
    
    return &current_object->variables[num];
}

/*
 * Free the data that an svalue is pointing to. Not the svalue
 * itself.
 */
void 
free_svalue(struct svalue *v)
{
    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:
	free_object(v->u.ob, "free_svalue");
	break;
    case T_POINTER:
	free_vector(v->u.vec);
	break;
    case T_MAPPING:
	free_mapping(v->u.map);
	break;
    case T_INVALID:
	fatal("Invalid value of variable!\n");
	break;
    }
    *v = const0; /* marion - clear this value all away */
}

/*
 * Prepend a slash in front of a string.
 */
char *
add_slash(char *str)
{
    char *tmp;
    
    tmp = xalloc(strlen(str)+2);
    strcpy(tmp,"/"); 
    strcat(tmp, str);
    return tmp;
}

/*
 * 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(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 */
#ifdef ALWAYS_SHARE
	    to->string_type = STRING_SHARED;
	    to->u.string = make_shared_string(from->u.string);
#else
	    to->u.string = string_copy(from->u.string);
#endif
	    break;
        case STRING_CONSTANT:
	    break;
	case STRING_SHARED:	/* It already is shared */
            increment_string_ref(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(struct svalue *dest, struct svalue *v)
{
    /* First deallocate the previous value. */
    free_svalue(dest);
    assign_svalue_no_free(dest, v);
}

#if 0
/* This function has been replaced with a macro to save speed. Macro is defined
 * in interpret.h
 */
void 
push_svalue(struct svalue *v)
{
    sp++;
    assign_svalue_no_free(sp, v);
}
#endif

/*
 * Pop the top-most value of the stack.
 * Don't do this if it is a value that will be used afterwards, as the
 * data may be sent to free(), and destroyed.
 */
INLINE 
void pop_stack() 
{
#ifdef DEBUG
    if (sp < start_of_stack)
	fatal("Stack underflow.\n");
#endif
    free_svalue(sp);
    sp--;
}

/*
 * Compute the address of an array element.
 */
INLINE static void 
push_indexed_lvalue(int needlval)
{
    struct svalue *i, *vec, *item;
    int ind;
    
    i = sp;
    vec = sp - 1;
    if (vec->type != T_MAPPING)
    {
	if (i->type != T_NUMBER || i->u.number < 0)
	    error("Illegal index\n");
	ind = i->u.number;
    }
    switch(vec->type) {
    case T_STRING: {
	static struct svalue one_character;
	/* marion says: this is a crude part of code */
	pop_stack();
	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);
	sp->type = T_LVALUE;
	sp->u.lvalue = &one_character;
	break;}
    case T_POINTER:
	pop_stack();
	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);	  /* This will make 'vec' invalid to use */
	sp->type = T_LVALUE;
	sp->u.lvalue = item;
	break;
    case T_MAPPING:
	item = get_map_lvalue(vec->u.map, i, needlval);
	pop_stack();
	if (vec->u.map->ref == 1) {
	    static struct svalue quickfix = { T_NUMBER };
	    assign_svalue (&quickfix, item);
	    item = &quickfix;
	}
	free_svalue(sp);	   /* This will make 'vec' invalid to use */
	sp->type = T_LVALUE;
	sp->u.lvalue = item;
	break;
    default:
	error("Indexing on illegal type.\n");
	break;
    }
}

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

/*
 * Deallocate 'n' values from the stack.
 */
INLINE
void pop_n_elems(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(int arg, int instr, struct svalue *sv)
{
    char *type_name = "Unknown";
    
    switch(sv->type)
    {
    case T_NUMBER:
	type_name = "Integer";
	break;
    case T_STRING:
	type_name = "String";
	break;
    case T_POINTER:
	type_name = "Array";
	break;
    case T_OBJECT:
	type_name = "Object";
	break;
    case T_MAPPING:
	type_name = "Mapping";
	break;
    case T_FLOAT:
	type_name = "Float";
	break;
    }
    
    error("Bad argument %d to %s(), received type was %s \n", arg, get_f_name(instr), type_name);
}

/*
 * Time spent in specific function
 */
void
time_funs(struct function *to, struct function *from)
{
#ifdef RUSAGE
#ifdef SOLARIS
    struct tms buffer;
    clock_t ticks;
    
    if (times(&buffer) != -1)
    {
	ticks = buffer.tms_utime + buffer.tms_stime;
	if (from)
	    from->time_spent +=
		(ticks - from->ticks_call);
	if (to)
	    to->ticks_call = ticks;
    }
#else
    struct rusage rus;
    int cpu_s, cpu_us;
    
    if (getrusage(RUSAGE_SELF, &rus) >= 0) 
    {                           
	cpu_s = rus.ru_utime.tv_sec + rus.ru_stime.tv_sec;
	cpu_us = rus.ru_utime.tv_usec + rus.ru_stime.tv_usec;
    }
    
    if (from)
    {
	from->time_spent += (cpu_s - from->stime_call) * 100000 +
	    (cpu_us - from->utime_call) / 10;
    }
    if (to)
    {
	to->stime_call = cpu_s;
	to->utime_call = cpu_us;
    }
#endif
#endif
}

void
save_control_context(struct control_stack *csp)
{
    csp->ob = current_object;
    csp->prev_ob = previous_ob;
    csp->fp = fp;
    csp->funp = 0;
    csp->prog = current_prog;
    /* csp->extern_call = 0; It is set by eval_instruction() */
    if (current_prog)
	csp->pc = pc - current_prog->program;
    csp->inh_offset = inh_offset;
}

void 
push_control_stack(struct function *funp)
{
    if (csp == &control_stack[MAX_TRACE-1])
	error("Too deep recursion.\n");
    csp++;
    save_control_context(csp);
    csp->funp = funp;	/* Only used for tracebacks */
#ifdef PROFILE_FUNS
    if (funp)
	funp->num_calls++;
    time_funs(csp->funp, (csp == control_stack) ? 0 : (csp-1)->funp);
#endif
}

/*
 * Pop the control stack one element, and restore registers.
 * extern_call must not be modified here, as it is used imediately after pop.
 */
void
restore_control_context(struct control_stack *csp)
{
    current_object = csp->ob;
    if (current_object)
	access_object(csp->ob);

    current_prog = csp->prog;
    if(current_prog)
    {
	access_program(csp->prog);
	pc = current_prog->program + csp->pc;
    }
    previous_ob = csp->prev_ob;
    fp = csp->fp;
    inh_offset = csp->inh_offset;
}

void 
pop_control_stack() 
{
#ifdef DEBUG
    if (csp == control_stack - 1)
	fatal("Popped out of the control stack");
#endif
    restore_control_context(csp);
#ifdef PROFILE_FUNS
    time_funs(csp == control_stack ? 0 : (csp-1)->funp, csp->funp);
#endif
    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(struct vector *v)
{
    v->ref++;
    sp++;
    sp->type = T_POINTER;
    sp->u.vec = v;
}

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

/*
 * Push a string on the stack that is already malloced.
 */
void INLINE 
push_malloced_string(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(char *p)
{
    sp++;
    sp->type = T_STRING;
    sp->u.string = p;
    sp->string_type = STRING_CONSTANT;
}

extern char *string_print_formatted (int , char *, int, struct svalue *);

static void 
do_trace_call(struct function *funp)
{
    do_trace("Call direct ", funp->name, " ");
    if (TRACEHB)
    {
        if (TRACETST(TRACE_ARGS))
	{
            int i;
	    char buff[1024];
	    
            sprintf(buff, " with %d arguments: ", funp->num_arg);
	    write_socket(buff, command_giver);
            for(i = 0; i < funp->num_arg; i++)
		write_socket(string_print_formatted(0, "%O ", 1, &fp[i]),
			     command_giver);
        }
        write_socket("\n", command_giver);
    }
}

static unsigned int previous_ob_access_time;

/*
 * Argument is the function to execute.
 * The function is located in current_prog.
 * There is a number of arguments on the stack. Normalize them and initialize
 * local variables, so that the called function is pleased.
 */
char *
setup_new_frame(struct function *funp)
{
    int called_args;
    unsigned short int npc;
    char *off;
    /* Remove excessive arguments, or put them in argv if applicable */

    previous_ob_access_time = current_object->time_of_ref;

    access_program(current_prog);
    access_object(current_object);

    if (funp->type_flags & TYPE_MOD_TRUE_VARARGS)
    {
	if (csp->num_local_variables >= funp->num_arg)
	{
	    struct vector *v;
	    int i, narg;
	    v = allocate_array(narg = csp->num_local_variables -
			       (funp->num_arg - 1));
	    for (i = narg - 1; i >= 0; i--)
		assign_svalue_no_free(&v->item[narg - 1 - i], &sp[-i]);
	    pop_n_elems(narg);
	    push_vector(v);
	    v->ref--; /* to make reference 1 again */
	    csp->num_local_variables -= narg - 1;
	}
    }
    else
	while(csp->num_local_variables > funp->num_arg)
	{
	    pop_stack();
	    csp->num_local_variables--;
	}
    /* Correct number of arguments and local variables */
    called_args = csp->num_local_variables;
    while(csp->num_local_variables < funp->num_arg + (int)funp->num_local)
    {
	push_number(0);
	csp->num_local_variables++;
    }
#ifdef DEBUG
    if (called_args > funp->num_arg)
	fatal("Error in seting up call frame!\n");
#endif
    if (called_args == funp->num_arg)
	npc = funp->offset + funp->num_arg * 2;
    else
    {
	off = current_prog->program + funp->offset + called_args * 2;
	((char *)&npc)[0] = off[0];
	((char *)&npc)[1] = off[1];
    }
    tracedepth++;
    fp = sp - csp->num_local_variables + 1;
#ifdef TRACE_CODE
    if (TRACEP(TRACE_CALL)) {
	do_trace_call(funp);
    }
#endif
    return current_prog->program + npc;
}

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 (int push)
{
    extern jmp_buf error_recovery_context;
    extern int error_recovery_context_exists;
    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;
	struct control_stack cstack;    
    } *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);
	save_control_context(&(p->cstack));
	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;
	restore_control_context(&(p->cstack));
	free ((char *)p);
    }
}

/*
 * 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(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");
    
    /* Loop structure copied from search_for_function... *shrug* /Dark */
    { 
	int inh;
	for (inh = ob->prog->num_inherited - 1; inh >= 0;--inh) 
	{
	    int fun;
	    struct program *progp =
		victim->inherit[inh].prog;
	    access_program(progp);
	    for (fun = progp->num_functions - 1; fun >= 0; fun--) 
	    {
		/* Should static functions 'shadowing' nomask functions
		   be allowed? They do not do any harm... 
		   */
		if ( (progp->functions[fun].type_flags & TYPE_MOD_NO_MASK) &&
		     search_for_function(progp->functions[fun].name, shadow) )
		    error("Illegal to shadow 'nomask' function \"%s\".\n",
			  progp->functions[fun].name);
	    }
        }
    }
    
    if (current_object == master_ob)
	return 1;
    
    push_object(ob);
    ret = apply_master_ob(M_QUERY_ALLOW_SHADOW, 1);
    if (!(ob->flags & O_DESTRUCTED) &&
	ret && !(ret->type == T_NUMBER && ret->u.number == 0))
    {
	return 1;
    }
    return 0;
}

/*
 * 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 wont crash when doing simple operations like assign_svalue
 * on a destructed object. You have to watch out, of course, that you dont
 * apply a function to it.
 * to save space it is preferable that destructed objects are freed soon.
 *   amylaar
 */
void 
check_for_destr(struct svalue *arg)
{
    int i, change;
    struct vector *v;
    struct mapping *m;
    struct apair *p, **pp;
    
    switch (arg->type)
    {
    case T_POINTER:
	v = arg->u.vec;
	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);
	}
	break;
	
    case T_MAPPING:
	m = arg->u.map;
	/* Value parts that have been destructed are kept but set = 0. */
	for (i = 0 ; i < m->size ; i++) {
	    for (p = m->pairs[i]; p ; p = p->next) 
	    {
		if (p->val.type == T_OBJECT && 
		    p->val.u.ob->flags & O_DESTRUCTED)
		    assign_svalue(&p->val, &const0);
	    }
	}
	/* Index parts that has been destructed is removed */
	change = 1;
	do 
	{
	    for (i = 0 ; i < m->size ; i++) 
	    {
		for (pp = &m->pairs[i]; *pp; )
		{
		    p = *pp;
		    if (p->arg.type == T_OBJECT &&
			p->arg.u.ob->flags & O_DESTRUCTED) 
		    {
			*pp = p->next;
			free_svalue(&p->arg);
			free_svalue(&p->val);
			free((char *)p);
			m->card--;
		    } 
		    else
		    {
			pp = &p->next;
		    }
		}
	    }
	    change = 0;
	} while (change != 0);
	break;
	
    default:
	error("Strange type to check_for_destr.\n");
	break;
    }
}

/*
 * 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 wont 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 int num_arg;
extern char *string_print_formatted (int , char *, int, struct svalue *);
extern char *break_string (char *, int, struct svalue *);
extern struct mapping *copy_mapping(struct mapping *);
extern struct mapping *filter_map (struct mapping *, char *, struct object *, struct svalue *);
extern char *query_ip_number (struct object *);
extern char *query_ip_name (struct object *);
extern struct vector *get_local_commands (struct object *);
extern struct vector *subtract_array (struct vector *,struct vector*);
extern struct vector *intersect_array (struct vector *, struct vector *);
extern char *string_print_formatted (int, char *, int, struct svalue *);
extern struct svalue *debug_command (char *, int, struct svalue *);
extern struct vector *subtract_array (struct vector*,struct vector*);
extern struct vector *intersect_array (struct vector*,struct vector*);
extern struct vector *make_unique (struct vector *arr,char *func, struct svalue *skipnum);
extern struct mapping *map_map (struct mapping *, char *, struct object *, struct svalue *);
static void eval_instruction(char *);

#ifdef RUSAGE
#define MAX_CPU_STACK 1000
static int progcpui;
static struct progcpuS { /* no good idea to do this with a list */
    struct program *prog;      /* an array is much faster... */
    long cpu;
} progcpu[MAX_CPU_STACK];
	    
void
clear_cpu_stack() 
{ 
    progcpui = 1; 
    progcpu[0].prog = 0;
    progcpu[0].cpu = (-1);
}
void 
remove_cpu_stack(struct program *prog)
{
    int i;
    for(i = progcpui; i >= 0; i--)
	if (progcpu[i].prog = prog) 
	    progcpu[i].prog = (struct program *)0;
}
	    
#endif


static void
f_last_reference_time(int num_arg)
{
    push_number(previous_ob_access_time);
}

static void
f_assertion(int num_arg)
{
  short offset;
  short assertion_type;
  ((char *)&assertion_type)[0] = *pc++;
  ((char *)&assertion_type)[1] = *pc++;
  ((char *)&offset)[0] = *pc++;
  ((char *)&offset)[1] = *pc++;
  if ((current_prog->debug_flags | current_object->debug_flags) &
      assertion_type)
      return;
  else
      pc = current_prog->program + offset;
}
static void
f_fail_precond(int num_arg)
{
  int val;
  val = sp->u.number;
  pop_stack();
  if (!val)
    error("Precondition failed!");
}
static void
f_fail_postcond(int num_arg)
{
  int val;
  val = sp->u.number;
  pop_stack();
  if (!val)
    error("Postcondition failed!");
}
static void
f_fail_invariant(int num_arg)
{
  int val;
  val = sp->u.number;
  pop_stack();
  if (!val)
    error("Invariant failed!");
}
static void
f_fail_assertion(int num_arg)
{
  int val;
  val = sp->u.number;
  pop_stack();
  if (!val)
    error("Assertion failed!");
}

static void
f_do_precond(int num_arg)
{
    unsigned short offset;
    if ((current_prog->debug_flags |
	 current_object->debug_flags) & 0x100)
    {
	((char *)&offset)[0] = pc[0];
	((char *)&offset)[1] = pc[1];
	pc = current_prog->program + offset;
    }
    else
	pc += 2;
}

static void
f_do_postcond(int num_arg)
{
    unsigned short offset;
    if ((current_prog->debug_flags |
	 current_object->debug_flags) & 0x200)
    {
	((char *)&offset)[0] = pc[0];
	((char *)&offset)[1] = pc[1];
	pc = current_prog->program + offset;
    }
    else
	pc += 2;
}

static void
f_do_invariants(int num_arg)
{
    int i;
    for (i = current_prog->num_inherited - 1; i >= 0; i--)
    {
	if ((current_prog->inherit[i].prog->debug_flags |
	     current_object->debug_flags) & 0x400 &&
	    current_prog->inherit[i].prog->invariant !=
	    (unsigned short) -1 &&
	    !(current_prog->inherit[i].type & TYPE_MOD_SECOND))
	{
	    call_function(current_object, inh_offset + i -
			  (current_prog->num_inherited - 1),
			  current_prog->inherit[i].prog->invariant, 0);
	    pop_stack();
	}
    }
}

static void
f_larrow(int num_arg)
{
    fatal("f_larrow should not be called.\n");
}

static void
f_darrow(int num_arg)
{
    fatal("f_darrow should not be called.\n");
}

static void
f_ext(int num_arg)
{
    fatal("f_ext should not be called.\n");
}

static void
f_call_virt(int num_arg)
{
    unsigned short func_name_index, fix, fiix;
    struct function *funp;
    int num_args;
    char *func;

#ifdef COUNT_CALLS
    num_call_self++;
#endif
    cache_tries++;
    
    fiix = EXTRACT_UCHAR(pc);
    pc++;
    ((char *)&fix)[0] = pc[0];
    ((char *)&fix)[1] = pc[1];
    pc += 2;
    
    num_args = EXTRACT_UCHAR(pc);
    pc++;

    if (current_object->prog == current_prog)
    {
	cache_hits++;
#ifdef CACHE_STATS
	call_first_saves += current_prog->num_inherited - fiix;
#endif	
	function_prog_found = current_object->prog->
	    inherit[fiix].prog;
	function_inherit_found = fiix;
	function_index_found = fix;
    }
    else
    {
	access_program(current_prog->inherit[fiix].prog);
	func = current_prog->inherit[fiix].prog->functions[fix].name;
	s_f_f(func, current_object->prog, current_prog,
	      fiix,
	      fix);
	if (function_type_mod_found & TYPE_MOD_PRIVATE &&
	    inh_offset < function_inherit_found -
	    function_prog_found->num_inherited + 1)
	    error("Atempted call of private function.\n");
    }
    
    
    access_program(function_prog_found);
    funp = &(function_prog_found->functions[function_index_found]);
    /* Urgle. There should probably be a function for all this. |D| 
     * See call-self for comments 
     */
    push_control_stack (funp);
    inh_offset = function_inherit_found;
    current_prog = function_prog_found;
    csp->ext_call = 0;
    csp->num_local_variables = num_args;
    pc = setup_new_frame(funp);
    csp->extern_call = 0;
    
#ifdef RUSAGE
    {
#ifdef SOLARIS
	struct tms buffer;
#else
	struct rusage rus;
#endif
	long cpu;
	
#ifdef SOLARIS
	if (times(&buffer) != -1)
	    cpu = (buffer.tms_utime + buffer.tms_stime);
#else
	if (getrusage(RUSAGE_SELF, &rus) >= 0) 
	{                           
	    cpu = rus.ru_utime.tv_sec * 1000 + rus.ru_utime.tv_usec / 1000 +
		rus.ru_stime.tv_sec * 1000 + rus.ru_stime.tv_usec / 1000; 
	}
#endif
	else
	    cpu = (-1);
	
	if (progcpui >= MAX_CPU_STACK) 
	    fatal("CPU-Stack overflow.\n");
	
	progcpu[progcpui].prog = current_prog;
	progcpu[progcpui++].cpu = cpu;
    }	
#endif
}

static void
f_call_selfv(int num_arg)
{
    static void f_call_self();
    struct vector *argv = sp->u.vec;
    int i;

    argv->ref++;
    pop_stack();
    num_arg = argv->size + 1;
    for(i = 0; i < argv->size; i++)
    {
	push_svalue(&argv->item[i]);
    }
    free_vector(argv);
    f_call_self(num_arg);
}

static void
f_call_self(int num_arg)
{
    struct function *funp;
    struct svalue *arg;

    arg = sp - num_arg + 1;

    if (search_for_function(arg->u.string, current_object->prog) == 0 ||
	function_type_mod_found & TYPE_MOD_PRIVATE &&
	inh_offset < function_inherit_found -
	function_prog_found->num_inherited + 1)
    {
	/* No such function */
	pop_n_elems(num_arg);
	push_number(0);
	return;
    }

    free_svalue(arg);
    num_arg--;
    memmove(arg, &arg[1], num_arg * sizeof(struct svalue));
    
    access_program(function_prog_found);
    funp = &(function_prog_found->functions[function_index_found]);
    /* Urgle. There should probably be a function for all this. |D| 
     * See call-self for comments 
     */
    push_control_stack (funp);
    inh_offset = function_inherit_found;
    current_prog = function_prog_found;
    csp->ext_call = 0;
    csp->num_local_variables = num_arg;
    pc = setup_new_frame(funp);
    csp->extern_call = 0;
    
#ifdef RUSAGE
    {
#ifdef SOLARIS
	struct tms buffer;
#else
	struct rusage rus;
#endif
	long cpu;
	
#ifdef SOLARIS
	if (times(&buffer) != -1)
	    cpu = (buffer.tms_utime + buffer.tms_stime);
#else
	if (getrusage(RUSAGE_SELF, &rus) >= 0) 
	{                           
	    cpu = rus.ru_utime.tv_sec * 1000 + rus.ru_utime.tv_usec / 1000 +
		rus.ru_stime.tv_sec * 1000 + rus.ru_stime.tv_usec / 1000; 
	}
#endif
	else
	    cpu = (-1);
	
	if (progcpui >= MAX_CPU_STACK) 
	    fatal("CPU-Stack overflow.\n");
	
	progcpu[progcpui].prog = current_prog;
	progcpu[progcpui++].cpu = cpu;
    }	
#endif
}

static void
f_call_non_virt(int num_arg)
{
    /* Receives: char   index into inherit-list
     *           short  function name (index into program strings).
     *           char   number of arguments
     */
    struct function *funp;
    int inh, num_args;
    unsigned short fix, fiix, fnix;
    
#ifdef COUNT_CALLS
    num_call_down++;
#endif
    
    fiix = EXTRACT_UCHAR(pc);
    pc++;
    ((char *)&fix)[0] = pc[0];
    ((char *)&fix)[1] = pc[1];
    pc += 2;
    
    num_args = EXTRACT_UCHAR(pc);
    pc++;
    inh = fiix - (current_prog->num_inherited - 1);
    function_prog_found = current_prog->inherit[fiix].prog;
    access_program(function_prog_found);
    funp = &(function_prog_found->functions[fix]);
    
    /* Urgle. There should probably be a function for all this. |D| 
     * See call-self for comments 
     */
    push_control_stack (funp);
    inh_offset += inh;
    current_prog = function_prog_found;
    csp->ext_call = 0;
    csp->num_local_variables = num_args;
    pc = setup_new_frame(funp);
    csp->extern_call = 0;
    
#ifdef RUSAGE
    {
#ifdef SOLARIS
	struct tms buffer;
#else
	struct rusage rus;
#endif
	long cpu;
	
#ifdef SOLARIS
	if (times(&buffer) != -1)
	    cpu = (buffer.tms_utime + buffer.tms_stime);
#else
	if (getrusage(RUSAGE_SELF, &rus) >= 0) 
	{                           
	    cpu = rus.ru_utime.tv_sec * 1000 + rus.ru_utime.tv_usec / 1000 +
		rus.ru_stime.tv_sec * 1000 + rus.ru_stime.tv_usec / 1000; 
	}
#endif
	else
	    cpu = (-1);
	
	if (progcpui >= MAX_CPU_STACK) 
	    fatal("CPU-Stack overflow.\n");
	
	progcpu[progcpui].prog = current_prog;
	progcpu[progcpui++].cpu = cpu;
    }	
#endif
}

static void
f_call_c(int num_arg)
{
    void (*func)();
    int f1 = 0;
#ifdef __alpha
    int f2 = 0;
#endif
    
    ((char *)&f1)[0] = pc[0];
    ((char *)&f1)[1] = pc[1];
    ((char *)&f1)[2] = pc[2];
    ((char *)&f1)[3] = pc[3];
    pc += 4;
#ifdef __alpha
    ((char *)&f2)[0] = pc[0];
    ((char *)&f2)[1] = pc[1];
    ((char *)&f2)[2] = pc[2];
    ((char *)&f2)[3] = pc[3];
    pc += 4;
    func = (void (*)())((long)f1 | ((long)f2 << 32));
#else
    func = (void (*)())f1;
#endif
    func(fp);
}

static void
f_call_simul(int num_arg)
{
    extern char *simul_efun_file_name;
    unsigned short func_name_index;
    int num_args, suc = 0;
    char *func;
    pc++;

    ((char *)&func_name_index)[0] = pc[0];
    ((char *)&func_name_index)[1] = pc[1];
    pc += 2;
    num_args = EXTRACT_UCHAR(pc);
    pc++;

    func = current_prog->rodata + func_name_index;
    suc = 0;
    if (!simul_efun_file_name ||
	current_prog->name == simul_efun_file_name ||
	!apply_low(func, (struct object *)query_simul_efun_ob(),
		   num_args, 1))
    {
	char buff[200];
	sprintf (buff, "Simulated efun %s not found", 
		 current_prog->rodata + func_name_index);
	error (buff);
    }
}

static void
f_previous_object(int num_arg)
{
    int n;
    struct control_stack *cspi;

    if (sp->u.number > 0 || (sp->u.number == 0 &&
			     (previous_ob == 0 || (previous_ob->flags & O_DESTRUCTED))))
    {
	pop_stack();
	push_number(0);
	return;
    }
    else if (sp->u.number == 0)
    {
	pop_stack();
	push_object(previous_ob);
	return;
    }
    n = sp->u.number;
    pop_stack();
    for (cspi = csp; n && cspi > control_stack; cspi--)
	if (cspi->ext_call)
	    n++;
    if (cspi == control_stack || cspi->ob == 0 ||
	cspi->ob->flags & O_DESTRUCTED)
	push_number(0);
    else
	push_object(cspi->ob);
}

static void
f_calling_program(int num_arg)
{
    int n = sp->u.number;
    struct control_stack *cspi;

    pop_stack();
    if (n > 0 || -n > MAX_TRACE)
    {
	push_number(0);
	return;
    }
    cspi = csp + n;
    if (cspi <= control_stack || cspi->prog == 0)
	push_number(0);
    else
	push_string(cspi->prog->name, STRING_MALLOC);
}

static void
f_calling_object(int num_arg)
{ 
    int n = sp->u.number;
    struct control_stack *cspi;
    
    pop_stack();
    if (n > 0 || -n > MAX_TRACE)
    {
	push_number(0);
	return;
    }
    cspi = csp + n;
    if (cspi <= control_stack || cspi->ob == 0 ||
	cspi->ob->flags & O_DESTRUCTED)
	push_number(0);
    else
	push_object(cspi->ob);
}

static void
f_calling_function(int num_arg)
{
    int n = sp->u.number;
    struct control_stack *cspi;

    pop_stack();
    if (n > 0 || -n > MAX_TRACE)
    {
	push_number(0);
	return;
    }
    cspi = csp + n - 1;
    if (cspi < control_stack)
	push_number(0);
    else if (cspi->funp)
	push_string(cspi->funp->name, STRING_MALLOC);
    else
	push_string("<internal>", STRING_CONSTANT);
}

static void
f_store(int num_arg)
{
    fatal("f_store should not be called.\n");
}

static void
f_if(int num_arg)
{
    fatal("f_if should not be called.\n");
}

static void
f_land(int num_arg)
{
    fatal("f_land should not be called.\n");
}

static void
f_lor(int num_arg)
{
    fatal("f_lor should not be called.\n");
}

static void
f_status(int num_arg)
{
    fatal("f_status should not be called.\n");
}

static void
f_comma(int num_arg)
{
    fatal("f_comma should not be called.\n");
}

static void
f_int(int num_arg)
{
    fatal("f_int should not be called.\n");
}
static void
f_string_decl(int num_arg)
{
    fatal("f_string_decl should not be called.\n");
}

static void
f_else(int num_arg)
{
    fatal("f_else should not be called.\n");
}

static void
f_describe(int num_arg)
{
    fatal("f_describe should not be called.\n");
}

static void
f_continue(int num_arg)
{
    fatal("f_continue should not be called.\n");
}

static void
f_inherit(int num_arg)
{
    fatal("f_inherit should not be called.\n");
}

static void
f_colon_colon(int num_arg)
{
    fatal("f_colon_colon should not be called.\n");
}

static void
f_static(int num_arg)
{
    fatal("f_static should not be called.\n");
}

static void
f_arrow(int num_arg)
{
    fatal("f_arrow should not be called.\n");
}

static void
f_object(int num_arg)
{
    fatal("f_object should not be called.\n");
}

static void
f_void(int num_arg)
{
    fatal("f_void should not be called.\n");
}

static void
f_mixed(int num_arg)
{
    fatal("f_mixed should not be called.\n");
}

static void
f_private(int num_arg)
{
    fatal("f_private should not be called.\n");
}

static void
f_no_mask(int num_arg)
{
    fatal("f_no_mask should not be called.\n");
}

static void
f_mapping(int num_arg)
{
    fatal("f_mapping should not be called.\n");
}

static void
f_float(int num_arg)
{
    fatal("f_float should not be called.\n");
}

static void
f_protected(int num_arg)
{
    fatal("f_protected should not be called.\n");
}

static void
f_public(int num_arg)
{
    fatal("f_public should not be called.\n");
}

static void
f_varargs(int num_arg)
{
    fatal("f_varargs should not be called.\n");
}

static void
f_vararg(int num_arg)
{
    fatal("f_vararg should not be called.\n");
}

static void
f_case(int num_arg)
{
    fatal("f_case should not be called.\n");
}

static void
f_default(int num_arg)
{
    fatal("f_default should not be called.\n");
}

static void
f_itof(int num_arg)
{
    sp->type = T_FLOAT;
    sp->u.real = sp->u.number;
}

static void
f_ftoi(int num_arg)
{
    sp->type = T_NUMBER;
    sp->u.number = sp->u.real;
}

static void
f_sin(int num_arg)
{
    sp->u.real = sin(sp->u.real);
}

static void
f_cos(int num_arg)
{
    sp->u.real = cos(sp->u.real);
}

static void
f_tan(int num_arg)
{
    sp->u.real = tan(sp->u.real);
}

static void
f_asin(int num_arg)
{
    if (fabs(sp->u.real) > 1.0)
	error("Argument out of bounds to asin()");
    sp->u.real = asin(sp->u.real);
}

static void
f_acos(int num_arg)
{
    if (fabs(sp->u.real) > 1.0)
	error("Argument out of bounds to acos()");
    sp->u.real = acos(sp->u.real);
}

static void
f_atan(int num_arg)
{
    sp->u.real = atan(sp->u.real);
}

static void
f_atan2(int num_arg)
{
    (sp-1)->u.real = atan2((sp-1)->u.real, sp->u.real);
    sp--;
}

static void
f_exp(int num_arg)
{
    sp->u.real = exp(sp->u.real);
}

static void
f_log(int num_arg)
{
    sp->u.real = log(sp->u.real);
}

static void
f_pow(int num_arg)
{
    (sp-1)->u.real = pow((sp-1)->u.real, sp->u.real);
    sp--;
}

static void
f_sinh(int num_arg)
{
    sp->u.real = sinh(sp->u.real);
}

static void
f_cosh(int num_arg)
{
    sp->u.real = cosh(sp->u.real);
}

static void
f_tanh(int num_arg)
{
    sp->u.real = tanh(sp->u.real);
}

#ifdef F_ASINH
static void
f_asinh(int num_arg)
{
    sp->u.real = asinh(sp->u.real);
}
#endif

#ifdef F_ACOSH
static void
f_acosh(int num_arg)
{
    if (sp->u.real < 1.0)
	error("Argument out of bounds to acosh()");
    sp->u.real = acosh(sp->u.real);
}
#endif

#ifdef F_ATANH
static void
f_atanh(int num_arg)
{
    if (fabs(sp->u.real) > 1.0)
	error("Argument out of bounds to atanh()");
    sp->u.real = atanh(sp->u.real);
}
#endif

static void
f_abs(int num_arg)
{
    sp->u.real = fabs(sp->u.real);
}

static void
f_fact(int num_arg)
{
    if ( sp->u.real < 0.0)
	error("Argument out of bounds to fact()");
#ifdef ns32000
    sp->u.real = exp(gamma(sp->u.real + 1.0));
#else	    
    sp->u.real = exp(lgamma(sp->u.real + 1.0));
#endif
}

static void
f_rnd(int num_arg)
{
    extern double random_float();

    push_float(random_float());
}

static void
f_ftoa(int num_arg)
{
    char buffer[1024];

    sprintf(buffer,"%.8g",sp->u.real);
    sp--;
    push_string(buffer, STRING_MALLOC);
}

static void
f_floatc(int num_arg)
{
    float f;

    ((char *)&f)[0] = pc[0];
    ((char *)&f)[1] = pc[1];
    ((char *)&f)[2] = pc[2];
    ((char *)&f)[3] = pc[3];
    pc += 4;
    push_float(f);
}

static void
f_regexp(int num_arg)
{
    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 */
    }
}

static void 
f_shadow(int num_arg)
{
    struct object *ob;

    ob = (sp-1)->u.ob;
    if (sp->u.number == 0)
    {
	ob = ob->shadowed;
	pop_n_elems(2);
	if (ob)
	    push_object(ob);
	else
	    push_number(0);
	return;
    }
    if (validate_shadowing(ob)) 
    {
	/*
	 * The shadow is entered first in the chain.
	 */
	while (ob->shadowed)
	    ob = ob->shadowed;
	change_ref(current_object->shadowing, ob, "f_shadow-1");
	current_object->shadowing = ob;
	change_ref(ob->shadowed, current_object, "f_shadow-2");
	ob->shadowed = current_object;
	pop_n_elems(2);
	push_object(ob);
	return;
    }
    pop_n_elems(2);
    push_number(0);
}

static void
f_pop_value(int num_arg)
{
    pop_stack();
}

static void
f_dup(int num_arg)
{
    sp++;
    assign_svalue_no_free(sp, sp-1);
}

static void 
f_jump_when_zero(int num_arg)
{
    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();
}

static void 
f_jump(int num_arg)
{
    unsigned short offset;

    ((char *)&offset)[0] = pc[0];
    ((char *)&offset)[1] = pc[1];
    pc = current_prog->program + offset;
}

static void 
f_jump_when_non_zero(int num_arg)
{
    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();
}

static void 
f_indirect(int num_arg)
{
#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);
	*sp = const0;
	}
}

static void 
f_identifier(int num_arg)
{
    sp++;
    assign_svalue_no_free(sp, find_value((int)EXTRACT_UCHAR(pc),
					 (int)EXTRACT_UCHAR(pc + 1)));
    pc += 2;
    /*
     * 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);
	*sp = const0;
    }
}

static void
f_push_identifier_lvalue(int num_arg)
{
    sp++;
    sp->type = T_LVALUE;
    sp->u.lvalue = find_value((int)EXTRACT_UCHAR(pc),(int)EXTRACT_UCHAR(pc + 1));
    pc += 2;
}

static void
f_push_indexed_lvalue(int num_arg)
{
    push_indexed_lvalue(1);
}

static void 
f_index(int num_arg)
{
    push_indexed_lvalue(0);
    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);
	sp->type = T_NUMBER;
	sp->u.number = 0;
    }
}

static void 
f_local_name(int num_arg)
{
    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);
	*sp = const0;
    }
}

static void
f_push_local_variable_lvalue(int num_arg)
{
    sp++;
    sp->type = T_LVALUE;
    sp->u.lvalue = fp + EXTRACT_UCHAR(pc);
    pc++;
}

static void
f_return(int num_arg)
{
    fatal("f_return should not be called.\n");
}

static void
f_break_point(int num_arg)
{
    break_point();
}

static void 
f_break_string(int num_arg)
{
    struct svalue *arg = sp- num_arg + 1;
    char *str;

    if (arg[0].type == T_STRING)
	{
	    str = break_string(arg[0].u.string, arg[1].u.number, 
			       (num_arg > 2 ? &arg[2] : (struct svalue *)0));
	    pop_n_elems(num_arg);
	    push_malloced_string(str);
	}
    else
	{
	    pop_n_elems(num_arg);
	    push_number(0);
	}
}

static void 
f_clone_object(int num_arg)
{
    struct object *ob;

    ob = clone_object(sp->u.string);
    pop_stack();
    if (ob)
    {
	sp++;
	sp->type = T_OBJECT;
	sp->u.ob = ob;
	add_ref(ob, "F_CLONE_OBJECT");
    }
    else
	push_number(0);
}

static void 
f_aggregate(int num_arg)
{
    struct vector *v;
    unsigned short num;
    int i;

    ((char *)&num)[0] = pc[0];
    ((char *)&num)[1] = pc[1];
    pc += 2;
    v = allocate_array((int)num);
    for (i = 0; i < (int)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 */
}

static void 
f_m_aggregate(int num_arg)
{
    struct mapping *m;
    unsigned short num;
    struct svalue *arg;
    int i;

    ((char *)&num)[0] = pc[0];
    ((char *)&num)[1] = pc[1];
    pc += 2;
    m = allocate_map((int)num); /* Ref count = 1 */
    for (i = 0 ; i < (int)num ; i += 2)
    {
	arg = sp + i - num;
	assign_svalue(get_map_lvalue(m, arg + 1, 1), arg + 2);
    }
    pop_n_elems((int)num);
    sp++;
    sp->type = T_MAPPING;
    sp->u.map = m;		/* Ref count already initialized */
}

static void
f_tail(int num_arg)
{
    if (tail(sp->u.string))
	assign_svalue(sp, &const1);
    else
	assign_svalue(sp, &const0);
}

static void
f_save_map(int num_arg)
{
    save_map(current_object, (sp - 1)->u.map, sp->u.string);
    pop_stack();
}

static void
f_save_object(int num_arg)
{
    save_object(current_object, sp->u.string);
    /* The argument is returned */
}

static void
f_m_save_object(int num_arg)
{
    push_mapping(m_save_object(current_object));
    sp->u.map->ref--;
    return;
}

static void
f_find_object(int num_arg)
{
    struct object *ob;

    ob = find_object2(sp->u.string);
    pop_stack();
    if (ob) 
	push_object(ob);
    else
	push_number(0);
}

static void
f_write_file(int num_arg)
{
    int i;

    i = write_file((sp-1)->u.string, sp->u.string);
    pop_n_elems(2);
    push_number(i);
}

static void 
f_read_file(int num_arg)
{
    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, F_READ_FILE, &arg[2]);
	    len = arg[2].u.number;
	}

    str = read_file(arg[0].u.string, start, len);
    pop_n_elems(num_arg);
    if (str == 0)
	push_number(0);
    else {
	push_malloced_string(str);
    }
}

static void 
f_read_bytes(int num_arg)
{
    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, F_READ_BYTES, &arg[2]);
	    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_malloced_string(str);
	}
}

static void
f_write_bytes(int num_arg)
{
    int i;

    i = write_bytes((sp-2)->u.string, (sp-1)->u.number, sp->u.string);
    pop_n_elems(3);
    push_number(i);
}

static void
f_file_size(int num_arg)
{
    int i;

    i = file_size(sp->u.string);
    pop_stack();
    push_number(i);
}

static void
f_file_time(int num_arg)
{
    int i;

    i = file_time(sp->u.string);
    pop_stack();
    push_number(i);
}

static void
f_find_living(int num_arg)
{
    struct object *ob;

    ob = find_living_object(sp->u.string);
    pop_stack();
    if (ob)
	push_object(ob);
    else
	push_number(0);
}

static void 
f_write_socket(int num_arg)
{
    if (sp->type == T_NUMBER)
    {
	char tmpbuf[10];

	sprintf(tmpbuf, "%d", sp->u.number);
	if (current_object->interactive)
	    write_socket(tmpbuf, current_object);
	else if (current_object == master_ob)
	    write_socket(tmpbuf, 0);
    }
    else
    {
	if (current_object->interactive)
	    write_socket(sp->u.string, current_object);
	else if (current_object == master_ob)
	    write_socket(sp->u.string, 0);
    }
}

static void
f_str2val(int num_arg)
{
    struct svalue sval = *sp;
    char *str = sval.u.string;

    *sp = const0;
    restore_one(sp, &str);
    free_svalue(&sval);
}

static void    
f_val2str(int num_arg)
{
    extern char *valtostr(struct svalue *);
    char *ret;

    ret = valtostr(sp);
    pop_stack();
    push_malloced_string(ret);
    return;
}

static void 
f_restore_map(int num_arg)
{
    struct mapping *map = allocate_map(0);
	    
    restore_map(current_object, map, sp->u.string);
    pop_stack();
    push_mapping(map);
    map->ref--; /* to make ref == 1 */
}

static void
f_restore_object(int num_arg)
{
    int i;

    i = restore_object(current_object, sp->u.string);
    pop_stack();
    push_number(i);
}

static void
f_m_restore_object(int num_arg)
{
    int i;

    i = m_restore_object(current_object, sp->u.map);
    pop_stack();
    push_number(i);
}

static void
f_this_interactive(int num_arg)
{
    if (current_interactive && 
	!(current_interactive->flags & O_DESTRUCTED))
	push_object(current_interactive);
    else
	push_number(0);
}

static void
f_this_player(int num_arg)
{
    if (command_giver && !(command_giver->flags & O_DESTRUCTED))
	push_object(command_giver);
    else
	push_number(0);
}

static void 
f_set_this_player(int num_arg)
{
    if (sp->type == T_NUMBER)
    {
	if (sp->u.number != 0)
	    error("Bad argument 1 to set_this_player()");
	command_giver = 0;
    }
    else
	if (sp->u.ob->flags & O_ENABLE_COMMANDS)
	    command_giver = sp->u.ob;
}

static void
f_living(int num_arg)
{
    if (sp->type == T_NUMBER)
    {
	assign_svalue(sp, &const0);
	return;
    }
    if (sp->u.ob->flags & O_ENABLE_COMMANDS)
	assign_svalue(sp, &const1);
    else
	assign_svalue(sp, &const0);
}

static void 
f_set_auth(int num_arg)
{
    struct svalue *ret = 0;
    struct svalue *arg = sp - 1;
	    
    if (master_ob)
    {
	push_object(current_object);
	push_object(arg->u.ob);
	push_svalue(arg + 1);
	ret = apply_master_ob(M_VALID_SET_AUTH, 3);
    }
    if (!ret)
    {
	pop_n_elems(2);
	push_number(0);
	return;
    }
    access_object(arg->u.ob);
    assign_svalue(&(arg->u.ob->variables[-1]), ret);
    access_object(current_object);
    pop_n_elems(2);
    push_number(0);
}

static void 
f_query_auth(int num_arg)
{
    int i;
    struct object *ob = sp->u.ob;

    pop_stack();
    access_object(ob);
    switch(ob->variables[-1].type)
    {
    case T_POINTER:
	push_vector(allocate_array(ob->variables[-1].u.vec->size));
	for(i = 0; i < ob->variables[-1].u.vec->size; i++)
	    assign_svalue_no_free(&(sp->u.vec->item[i]),
				  &(ob->variables[-1].u.vec->item[i]));
	sp->u.vec--;
	break;
    case T_MAPPING:
	push_mapping(copy_mapping(ob->variables[-1].u.map));
	sp->u.map->ref--;
	break;
    default:
	push_svalue(&(ob->variables[-1]));
    }
    access_object(current_object);
}

static void
f_explode(int num_arg)
{
    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);
    }
}

static void 
f_filter(int num_arg)
{
    struct vector *v;
    struct svalue *arg;
    struct mapping *m;
    struct object *ob;

    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 third argument to filter_array()\n");
    
    if (arg[0].type == T_POINTER)
    {
	check_for_destr(&arg[0]);
	v = filter_arr(arg[0].u.vec, arg[1].u.string, ob,
		       num_arg > 3 ? sp : (struct svalue *)0); 
    }
    else
    {
	v = 0;
    }
    if (arg[0].type == T_MAPPING)
    {
	check_for_destr(&arg[0]);
	m = filter_map(arg[0].u.map, arg[1].u.string, ob,
		       num_arg >3 ? sp : (struct svalue *)0);
    }
    else
    {
	m = 0;
    }
    
    pop_n_elems(num_arg);
    if (v)
    {
	push_vector(v); /* This will make ref count == 2 */
	v->ref--;
    }
    else if (m)
    {
	push_mapping(m); /* This will make ref count == 2 */
	m->ref--;
    }
    else
    {
	push_number(0);
    }
}

static void
f_set_bit(int num_arg)
{
    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;
}

static void
f_clear_bit(int num_arg)
{
    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();
	return;
    }
    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;
}

static void
f_test_bit(int num_arg)
{
    int len;

    len = strlen((sp-1)->u.string);
    if (sp->u.number/6 >= len) {
	pop_n_elems(2);
	push_number(0);
	return;
    }
    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);
    }
}

static void
f_catch(int num_arg)
{
    fatal("f_catch should not be called.\n");
}

static void
f_throw(int num_arg)
{
    /* marion
     * the return from catch is now done by a 0 throw
     */
    assign_svalue(&catch_value, sp);
    free_svalue(sp--);

    if (catch_value.type == T_NUMBER && catch_value.u.number == 0) {
	/* We come here when no longjmp() was executed. */
	push_pop_error_context (0);
	push_number(0);
    }
    else
	throw_error(); /* do the longjump, with extra checks... */
}


static void
f_notify_fail(int num_arg)
{
    set_notify_fail_message(sp->u.string);
    /* Return 0 */
    pop_stack();
    push_number(0);
}

static void
f_query_idle(int num_arg)
{
    int i;

    i = query_idle(sp->u.ob);
    pop_stack();
    push_number(i);
}

static void
f_implode(int num_arg)
{
    char *str;

    if ((sp-1)->type == T_NUMBER)
    {
	pop_stack();
	return;
    }
    check_for_destr(sp-1);
    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_MALLOC;
	sp->u.string = str;
    } else {
	push_number(0);
    }
}

static void
f_query_snoop(int num_arg)
{
    struct object *ob;

    if (current_object == master_ob && sp->u.ob->interactive)
	ob = query_snoop(sp->u.ob);
    else
	ob = 0;
    pop_stack();
    if (ob)
	push_object(ob);
    else
	push_number(0);
}


static void
f_query_ip_number_name(int name, int num_arg)
{
    char *tmp;
	    
    if (num_arg == 1 && sp->type != T_OBJECT)
	error("Bad optional argument to query_ip_number()\n");
	    
    if (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_MALLOC);
}

static void
f_query_ip_number(int num_arg)
{
    f_query_ip_number_name(0, num_arg);
}

static void
f_query_ip_name(int num_arg)
{
    f_query_ip_number_name(1, num_arg);
}

static void
f_query_ip_ident(int num_arg)
{
    struct object *ob = sp->u.ob;
    
    pop_stack();
    if (!ob->interactive || !ob->interactive->rname)
	push_number(0);
    else
	push_string(ob->interactive->rname, STRING_MALLOC);
    return;
}

static void
f_query_host_name(int num_arg)
{
    extern char *query_host_name();
    char *tmp;
    tmp = query_host_name();
    if (tmp)
	push_string(tmp, STRING_MALLOC);
    else
	push_number(0);
}

static void
f_all_inventory(int num_arg)
{
    struct vector *vec;

    vec = all_inventory(sp->u.ob);
    pop_stack();
    if (vec == 0) {
	push_number(0);
    } else {
	push_vector(vec); /* This will make ref count == 2 */
	vec->ref--;
    }
}

static void
f_deep_inventory(int num_arg)
{
    struct vector *vec;

    if (sp->type == T_NUMBER)
	vec = allocate_array(0);
    else
	vec = deep_inventory(sp->u.ob, 0);
    free_svalue(sp);
    sp->type = T_POINTER;
    sp->u.vec = vec;
}

static void
f_environment(int num_arg)
{
    struct object *ob;

    ob = environment(sp);
    pop_stack();
    if (ob)
	push_object(ob);
    else
	push_number(0);
}

static void
f_this_object(int num_arg)
{
    push_object(current_object);
}

static void
f_object_clones(int num_arg)
{
    struct vector *v;
    struct object *ob;
    int i;

    if (sp->type == T_NUMBER)
	v = allocate_array(0);
    else
    {
      ob = obj_list;
      i = 0;
	do
	{
	    if (ob->prog == sp->u.ob->prog && (ob->flags & O_CLONE))
		i++;
	} while ((ob = ob->next_all) != obj_list);
        v = allocate_array(i);
	ob = obj_list;
        i = 0;
	do
	    if (ob->prog == sp->u.ob->prog && (ob->flags & O_CLONE))
	    {
		v->item[i].type = T_OBJECT;
		v->item[i++].u.ob = ob;
		add_ref(ob,"object_clones");
	    }
	while ((ob = ob->next_all) != obj_list) ;
    }
    pop_stack();
    push_vector(v);
    v->ref--; /* Refcount == 2 after push */
}

static void
f_commands(int num_arg)
{
    struct vector *vec;
	    
    if (sp->type == T_NUMBER)
	vec = allocate_array(0);
    else
	vec = get_local_commands(sp->u.ob);
    pop_stack();
    push_vector(vec);
    vec->ref--; /* Refcount = 2 after the push */
}

static void
f_time(int num_arg)
{
    push_number(current_time);
}

static void
f_add(int num_arg)
{
    int i;

    /*if (inadd==0) checkplus(p);*/
    if ((sp-1)->type == T_STRING && sp->type == T_STRING)
    {
	char *res;
	int l = strlen((sp-1)->u.string);
	res = xalloc(l + strlen(sp->u.string) + 1);
	(void)strcpy(res, (sp-1)->u.string);
	(void)strcpy(res+l, sp->u.string);
	pop_n_elems(2);
	push_malloced_string(res);
    }
    else if ((sp-1)->type == T_NUMBER && sp->type == 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);
	pop_n_elems(2);
	push_malloced_string(res);
    }
    else if (sp->type == T_NUMBER && (sp-1)->type == T_STRING)
    {
	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);
	pop_n_elems(2);
	push_malloced_string(res);
    }
    else if ((sp-1)->type == T_NUMBER && sp->type == T_NUMBER)
    {
	i = sp->u.number + (sp-1)->u.number;
	sp--;
	sp->u.number = i;
    }
    else if ((sp-1)->type == T_FLOAT && sp->type == T_FLOAT)
    {
	(sp-1)->u.real += sp->u.real;
	sp--;
    } 
    else if ((sp-1)->type == T_POINTER && sp->type == T_POINTER)
    {
	struct vector *v;
	check_for_destr(sp-1);
	check_for_destr(sp);
	v = add_array((sp-1)->u.vec,sp->u.vec);
	pop_n_elems(2);
	push_vector(v); /* This will make ref count == 2 */
	v->ref--;
    }
    else if ((sp-1)->type == T_MAPPING && sp->type == T_MAPPING)
    {
	struct mapping *m;
	check_for_destr(sp-1);
	check_for_destr(sp);
	m = add_mapping((sp-1)->u.map, sp->u.map);
	pop_n_elems(2);
	push_mapping(m); /* This will make ref count == 2 */
	m->ref--;
    }
    else
    {
	error("Bad type of arg to '+'\n");
    }
}

static void
f_subtract(int num_arg)
{
    int i;

    if ((sp-1)->type == T_POINTER && sp->type == T_POINTER) 
    {
	struct vector *v;

	v = subtract_array((sp-1)->u.vec, sp->u.vec);

	pop_stack();
	pop_stack();

	if (v == 0) 
	{
	    push_number(0);
	} 
	else 
	{
	    push_vector(v); /* This will make ref count == 2 */
	    v->ref--;
	}

	return;
    }
    if ((sp-1)->type == T_FLOAT && sp->type == T_FLOAT) 
    {
	(sp-1)->u.real -= sp->u.real;
	sp--;
	return;
    }
    if ((sp-1)->type != T_NUMBER)
	bad_arg(1, F_SUBTRACT, sp-1);
    if (sp->type != T_NUMBER)
	bad_arg(2, F_SUBTRACT, sp);
    i = (sp-1)->u.number - sp->u.number;
    sp--;
    sp->u.number = i;
}

static void
f_and(int num_arg)
{
    int i;

    if (sp->type == T_POINTER && (sp-1)->type == T_POINTER)
    {
	struct vector *v;

	v = intersect_array(sp->u.vec, (sp-1)->u.vec);

	pop_stack();
	pop_stack();
	if (v == 0) 
	{
	    push_number(0);
	} 
	else 
	{
	    push_vector(v); /* This will make ref count == 2 */
	    v->ref--;
	}
	return;
    }
    if ((sp-1)->type != T_NUMBER)
	bad_arg(1, F_AND, sp-1);
    if (sp->type != T_NUMBER)
	bad_arg(2, F_AND, sp);
    i = (sp-1)->u.number & sp->u.number;
    sp--;
    sp->u.number = i;
}

static void
f_or(int num_arg)
{
    int i;

    if ((sp-1)->type != T_NUMBER)
	bad_arg(1, F_OR, sp-1);
    if (sp->type != T_NUMBER)
	bad_arg(2, F_OR, sp);
    i = (sp-1)->u.number | sp->u.number;
    sp--;
    sp->u.number = i;
}

static void
f_xor(int num_arg)
{
    int i;

    if ((sp-1)->type != T_NUMBER)
	bad_arg(1, F_XOR, sp-1);
    if (sp->type != T_NUMBER)
	bad_arg(2, F_XOR, sp);
    i = (sp-1)->u.number ^ sp->u.number;
    sp--;
    sp->u.number = i;
}

static void
f_lsh(int num_arg)
{
    int i;

    if ((sp-1)->type != T_NUMBER)
	bad_arg(1, F_LSH, sp-1);
    if (sp->type != T_NUMBER)
	bad_arg(2, F_LSH, sp);
    i = (sp-1)->u.number << sp->u.number;
    sp--;
    sp->u.number = i;
}

static void
f_rsh(int num_arg)
{
    int i;

    if ((sp-1)->type != T_NUMBER)
	bad_arg(1, F_RSH, sp-1);
    if (sp->type != T_NUMBER)
	bad_arg(2, F_RSH, sp);
    i = (sp-1)->u.number >> sp->u.number;
    sp--;	
    sp->u.number = i;
}

static void
f_multiply(int num_arg)
{
    int i;

    if ((sp-1)->type == T_FLOAT && sp->type == T_FLOAT)
    {
	(sp-1)->u.real *= sp->u.real;
	sp--;
	return;
    }
    if ((sp-1)->type != T_NUMBER)
	bad_arg(1, F_MULTIPLY, sp-1);
    if (sp->type != T_NUMBER)
	bad_arg(2, F_MULTIPLY, sp);
    i = (sp-1)->u.number * sp->u.number;
    sp--;
    sp->u.number = i;
}

static void
f_divide(int num_arg)
{
    int i;

    if ((sp-1)->type == T_FLOAT && sp->type == T_FLOAT)
    {
        if (sp->u.real == 0.0)
	    error("Division by zero\n");
	(sp-1)->u.real /= sp->u.real;
	sp--;
	return;
    }
    if ((sp-1)->type != T_NUMBER)
	bad_arg(1, F_DIVIDE, sp-1);
    if (sp->type != T_NUMBER)
	bad_arg(2, F_DIVIDE, sp);
    if (sp->u.number == 0)
	error("Division by zero\n");
    i = (sp-1)->u.number / sp->u.number;
    sp--;
    sp->u.number = i;
}

static void
f_mod(int num_arg)
{
    int i;

    if ((sp-1)->type != T_NUMBER)
	bad_arg(1, F_MOD, sp-1);
    if (sp->type != T_NUMBER)
	bad_arg(2, F_MOD, sp);
    if (sp->u.number == 0)
	error("Modulus by zero.\n");
    i = (sp-1)->u.number % sp->u.number;
    sp--;
    sp->u.number = i;
}

static void
f_gt(int num_arg)
{
    int i;

    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);
	return;
    }
    if ((sp-1)->type == T_FLOAT && sp->type == T_FLOAT)
    {
	(sp-1)->u.number = (sp-1)->u.real > sp->u.real;
	sp--;
	sp->type = T_NUMBER;
	return;
    }
    if ((sp-1)->type != T_NUMBER)
	bad_arg(1, F_GT, sp-1);
    if (sp->type != T_NUMBER)
	bad_arg(2, F_GT, sp);
    i = (sp-1)->u.number > sp->u.number;
    sp--;
    sp->u.number = i;
}

static void
f_ge(int num_arg)
{
    int i;

    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);
	return;
    }
    if ((sp-1)->type == T_FLOAT && sp->type == T_FLOAT)
    {
	(sp-1)->u.number = (sp-1)->u.real >= sp->u.real;
	sp--;
	sp->type = T_NUMBER;
	return;
    }
    if ((sp-1)->type != T_NUMBER)
	bad_arg(1, F_GE, sp-1);
    if (sp->type != T_NUMBER)
	bad_arg(2, F_GE, sp);
    i = (sp-1)->u.number >= sp->u.number;
    sp--;
    sp->u.number = i;
}

static void
f_lt(int num_arg)
{
    int i;

    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);
	return;
    }
    if ((sp-1)->type == T_FLOAT && sp->type == T_FLOAT)
    {
	(sp-1)->u.number = (sp-1)->u.real < sp->u.real;
	sp--;
	sp->type = T_NUMBER;
	return;
    }
    if ((sp-1)->type != T_NUMBER)
	bad_arg(1, F_LT, sp-1);
    if (sp->type != T_NUMBER)
	bad_arg(2, F_LT, sp);
    i = (sp-1)->u.number < sp->u.number;
    sp--;
    sp->u.number = i;
}

static void
f_le(int num_arg)
{
    int i;

    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);
	return;
    }
    if ((sp-1)->type == T_FLOAT && sp->type == T_FLOAT)
    {
	(sp-1)->u.number = (sp-1)->u.real <= sp->u.real;
	sp--;
	sp->type = T_NUMBER;
	return;
    }
    if ((sp-1)->type != T_NUMBER)
	bad_arg(1, F_LE, sp-1);
    if (sp->type != T_NUMBER)
	bad_arg(2, F_LE, sp);
    i = (sp-1)->u.number <= sp->u.number;
    sp--;
    sp->u.number = i;
}

static void
f_eq(int num_arg)
{
    int i;

    if ((sp-1)->type != sp->type) {
	pop_stack();
	assign_svalue(sp, &const0);
	return;
    }
    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_MAPPING:
	    i = (sp-1)->u.map == sp->u.map;
	    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;
	case T_FLOAT:
	    i = (sp-1)->u.real == sp->u.real;
	    break;
	default:
	    i = 0;
	    break;
    }
    pop_n_elems(2);
    push_number(i);
}

static void
f_ne(int num_arg)
{
    int i;

    if ((sp-1)->type != sp->type) {
	pop_stack();
	assign_svalue(sp, &const1);
	return;
    }
    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_MAPPING:
	    i = (sp-1)->u.map != sp->u.map;
	    break;
	case T_OBJECT:
	    i = (sp-1)->u.ob != sp->u.ob;
	    break;
	case T_FLOAT:
	    i = (sp-1)->u.real != sp->u.real;
	    break;
	default:
	    fatal("Illegal type to !=\n");
    }
    pop_n_elems(2);
    push_number(i);
}

static void
f_not(int num_arg)
{
    if (sp->type == T_NUMBER && sp->u.number == 0)
	sp->u.number = 1;
    else
	assign_svalue(sp, &const0);
}

static void
f_compl(int num_arg)
{
    if (sp->type != T_NUMBER)
	error("Bad argument to ~\n");
    sp->u.number = ~ sp->u.number;
}

static void
f_negate(int num_arg)
{
    if (sp->type == T_FLOAT)
    {
	sp->u.real = - sp->u.real;
	return;
    }
    if (sp->type != T_NUMBER)
	error("Bad argument to unary minus\n");
    sp->u.number = - sp->u.number;
}

static void
f_inc(int num_arg)
{
    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);
}

static void
f_dec(int num_arg)
{
    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);
}

static void
f_post_inc(int num_arg)
{
    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--;
}

static void
f_post_dec(int num_arg)
{
    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++;
}

static void
f_call_otherv(int num_arg)
{
    static void f_call_other();
    struct vector *argv = sp->u.vec;
    int i;

    argv->ref++;
    pop_stack();
    num_arg = argv->size + 2;
    for(i = 0; i < argv->size; i++)
    {
	push_svalue(&argv->item[i]);
    }
    free_vector(argv);
    f_call_other(num_arg);
}

static void 
f_call_other(int num_arg)
{
    struct object *ob;
    struct svalue *arg, tmp;

#ifdef COUNT_CALLS
    num_call_other++;
#endif	
    arg = sp - num_arg + 1;
    if (arg[0].type == T_NUMBER)
    {
	if (arg[0].u.number != 0)
	    error("Bad argument 1 to call_other()");
	pop_n_elems(num_arg);
	push_number(0);
	return;
    }
    if (arg[0].type == T_POINTER)
    {
	struct vector *w, *v = allocate_array(num_arg - 2);
	struct object *ob;
	int i, j;

	for (i = 0; i < num_arg - 2; i++)
	    assign_svalue_no_free(&v->item[i], &arg[i + 2]);
	pop_n_elems(num_arg - 2);
	w = allocate_array(arg[0].u.vec->size);
	for (i = 0; i < arg[0].u.vec->size; i++)
	{
	    if (arg[0].u.vec->item[i].type != T_OBJECT &&
		arg[0].u.vec->item[i].type != T_STRING)
		continue;
	    if (arg[0].u.vec->item[i].type == T_OBJECT)
		ob = arg[0].u.vec->item[i].u.ob;
	    else
		ob = find_object(arg[0].u.vec->item[i].u.string);
	    if (!ob || ob->flags & O_DESTRUCTED)
		continue;
	    for (j = 0; j < v->size; j++)
		push_svalue(&v->item[j]);
#ifdef TRACE_CODE
	    if (TRACEP(TRACE_CALL_OTHER)) 
	    {
		char buff[1024];
		sprintf(buff,"%s->%s", ob->name,arg[1].u.string);
		do_trace("Call other ", buff, "\n");
	    }
#endif
	    if (apply_low(arg[1].u.string, ob, v->size, 1) == 0)
		continue;	/* function not found */
	    w->item[i] = *(sp--);
	}
	
	pop_n_elems(2);
	push_vector(w);
	w->ref--;
	free_vector(v);
	return;
    }
    if (arg[0].type == T_MAPPING)
    {
	struct vector *w, *v = allocate_array(num_arg - 2);
	struct vector *ix, *o;
	struct object *ob;
	int i, j;

	ix = map_domain(arg[0].u.map);
	o = map_codomain(arg[0].u.map);
	for (i = 0; i < num_arg - 2; i++)
	    assign_svalue_no_free(&v->item[i], &arg[i + 2]);
	pop_n_elems(num_arg - 2);
	w = allocate_array(o->size);
	for (i = 0; i < o->size; i++)
	{
	    if (o->item[i].type != T_OBJECT &&
		o->item[i].type != T_STRING)
		continue;
	    if (o->item[i].type == T_OBJECT)
		ob = o->item[i].u.ob;
	    else
		ob = find_object(o->item[i].u.string);
	    if (!ob || ob->flags & O_DESTRUCTED)
		continue;
	    for (j = 0; j < v->size; j++)
		push_svalue(&v->item[j]);
#ifdef TRACE_CODE
	    if (TRACEP(TRACE_CALL_OTHER)) 
	    {
		char buff[1024];
		sprintf(buff,"%s->%s", ob->name, arg[1].u.string);
		do_trace("Call other ", buff, "\n");
	    }
#endif
	    if (apply_low(arg[1].u.string, ob, v->size, 1) == 0)
		continue;	/* function not found */
	    w->item[i] = *(sp--);
	}
	
	pop_n_elems(2);
	push_mapping(make_mapping(ix,w));
	sp->u.map->ref--;	/* Adjust ref counter */
	free_vector(o);
	free_vector(ix);
	free_vector(v);
	free_vector(w);
	return;
    }
    if (arg[0].type == T_OBJECT)
	ob = arg[0].u.ob;
    else 
    {
	ob = find_object(arg[0].u.string);
	if (ob == 0)
	    error("call_other() failed\n");
    }
    if (current_object->flags & O_DESTRUCTED) 
    {
	/*
	 * No external calls may be done when this object is
	 * destructed.
	 */
	pop_n_elems(num_arg);
	push_number(0);
	return;
    }
    /*
     * Send the remaining arguments to the function.
     */
#ifdef TRACE_CODE
    if (TRACEP(TRACE_CALL_OTHER)) 
    {
	char buff[1024];
	sprintf(buff,"%s->%s", ob->name, arg[1].u.string);
	do_trace("Call other ", buff, "\n");
    }
#endif
    
    if (apply_low(arg[1].u.string, ob, num_arg - 2, 1) == 0) 
    {
	/* Function not found */
	pop_n_elems(2);
	push_number(0);
	return;
    }
    /*
     * 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 */
}

static void
f_object_time(int num_arg)
{
    int i;

    if (sp->type == T_OBJECT)
    {
	i = sp->u.ob->created;
	pop_stack();
	push_number(i);
    }
    else
	assign_svalue(sp, &const0);
}

static void
f_intp(int num_arg)
{
    if (sp->type == T_NUMBER)
	assign_svalue(sp, &const1);
    else
	assign_svalue(sp, &const0);
}

static void
f_stringp(int num_arg)
{
    if (sp->type == T_STRING)
	assign_svalue(sp, &const1);
    else
	assign_svalue(sp, &const0);
}

static void
f_objectp(int num_arg)
{
    if (sp->type == T_OBJECT)
	assign_svalue(sp, &const1);
    else
	assign_svalue(sp, &const0);
}

static void
f_pointerp(int num_arg)
{
    if (sp->type == T_POINTER)
	assign_svalue(sp, &const1);
    else
	assign_svalue(sp, &const0);
}

static void
f_mappingp(int num_arg)
{
    if (sp->type == T_MAPPING)
	assign_svalue(sp, &const1);
    else
	assign_svalue(sp, &const0);
}

static void
f_floatp(int num_arg)
{
    if (sp->type == T_FLOAT)
	assign_svalue(sp, &const1);
    else
	assign_svalue(sp, &const0);
}

static void
f_extract(int num_arg)
{
    int len, from, to;
    struct svalue *arg;
    char *res;
	    
    arg = sp - num_arg + 1;
    len = strlen(arg[0].u.string);
    if (num_arg == 1)
	return;			/* Simply return argument */
    from = arg[1].u.number;
    if (from < 0)
	from = len + from;
    if (from >= len) {
	pop_n_elems(num_arg);
	push_string("", STRING_CONSTANT);
	return;
    }
    if (num_arg == 2) {
	res = string_copy(arg->u.string + from);
	pop_n_elems(2);
	push_malloced_string(res);
	return;
    }
    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);
	return;
    }
    if (to >= len)
	to = len-1;
    if (to == len-1) {
	res = string_copy(arg->u.string + from);
	pop_n_elems(3);
	push_malloced_string(res);
	return;
    }
    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_malloced_string(res);
}

static void
f_range(int num_arg)
{
    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 < 0)
	    from = 0;
	if (from >= len) {
	    pop_n_elems(3);
	    push_string("", STRING_CONSTANT);
	    return;
	}
	to = sp[0].u.number;
	if (to < 0)
	    to = len + to;
	if (to < from) {
	    pop_n_elems(3);
	    push_string("", STRING_CONSTANT);
	    return;
	}
	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);
	    return;
	}
	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");
    }
}

static void
f_query_verb(int num_arg)
{
    if (last_verb == 0) {
	push_number(0);
	return;
    }
    push_string(last_verb, STRING_MALLOC);
}

static void
f_exec(int num_arg)
{
    int i;

    if ((sp-1)->type == T_NUMBER)
	i = replace_interactive(0, sp->u.ob, current_prog->name);
    else
	i = replace_interactive((sp-1)->u.ob, sp->u.ob,
				current_prog->name);
    pop_stack();
    pop_stack();
    push_number(i);
}

static void
f_file_name(int num_arg)
{
    char *name,*res;
	    
    /* This function now returns a leading '/', except when -o flag */
    name = sp->u.ob->name;
    res = add_slash(name);
    pop_stack();
    push_malloced_string(res);
}

static void
f_users(int num_arg)
{
    struct svalue *ret;

    if (current_object != master_ob)
    {
	push_object(current_object);
	ret = apply_master_ob(M_VALID_USERS, 1);
	if (ret && ret->u.number == 0)
	{
	    push_number(0);
	    return;
	}
    }
    push_vector(users());	/* users() has already set ref count to 1 */
    sp->u.vec->ref--;
}

static void
f_set_alarm(int num_arg)
{
    struct svalue *arg = sp - num_arg + 1;
    int delay, reload, ret;
    struct vector *v;

    if (arg[2].type != T_STRING)
	error("Wrong argument 3 to set_alarm.\n");
    
    if (*(arg[2].u.string) == '.')
    {
	pop_n_elems(num_arg);
	push_number(0);
	return;
    }

    v = allocate_array(num_arg - 3);
    memcpy(&v->item[0], &arg[3],
	   (num_arg - 3) * sizeof(struct svalue));
    sp = &arg[2];

    if (!(current_object->flags & O_DESTRUCTED) &&
	search_for_function(arg[2].u.string, current_object->prog))
    {
	if (function_type_mod_found & TYPE_MOD_PRIVATE &&
	    inh_offset < function_inherit_found -
	    function_prog_found->num_inherited + 1)
	    error("Atempted callout of private function.\n");
	
	delay = (int) (arg[0].u.real * TIME_RES + 0.5);
	
	reload = (int) (arg[1].u.real * TIME_RES + 0.5);
	
	ret = new_call_out(current_object, function_index_found,
			   function_inherit_found,
			   delay, reload, v);
    }
    free_vector(v);
    pop_n_elems(3);
    push_number(ret);
}

static void
f_set_alarmv(int num_arg)
{
    struct svalue *arg = sp - 3;
    int delay, reload, ret;
    struct vector *v;

    if (arg[2].type != T_STRING)
	error("Wrong argument 3 to set_alarm.\n");
    if (arg[3].type != T_POINTER)
	error("Wrong argument 4 to set_alarm.\n");

    if (*(arg[2].u.string) == '.')
    {
	pop_n_elems(num_arg);
	push_number(0);
	return;
    }

    v = sp->u.vec;
    sp--;

    if (!(current_object->flags & O_DESTRUCTED) &&
	search_for_function(arg[2].u.string, current_object->prog))
    {
	if (function_type_mod_found & TYPE_MOD_PRIVATE &&
	    inh_offset < function_inherit_found -
	    function_prog_found->num_inherited + 1)
	    error("Atempted callout of private function.\n");
	
	delay = (int) (arg[0].u.real * TIME_RES + 0.5);
	
	reload = (int) (arg[1].u.real * TIME_RES + 0.5);
	
	ret = new_call_out(current_object, function_index_found,
			   function_inherit_found,
			   delay, reload, v);
    }
    free_vector(v);
    pop_n_elems(3);
    push_number(ret);
}

static void
f_call_out(int num_arg)
{
    struct svalue *arg = sp - num_arg + 1;
    int delay = 0, ret = 0, reload = 0;
    struct vector *v = NULL;

    if (arg[1].type == T_FLOAT)
    {
	delay = (int) (arg[1].u.real * TIME_RES +
		       (arg[1].u.real < 0.0 ? -0.5: 0.5));
	if (delay == 0 && arg[1].u.real < 0.0)
	    reload = 1;
    }
    else
    {
	delay = arg[1].u.number * TIME_RES;
    }

    if (delay < 0)
	reload = delay = -delay;
    
    if (*(arg[0].u.string) == '.')
    {
	pop_n_elems(num_arg);
	push_number(0);
	return;
    }

    if (num_arg == 3)
    {
	v = allocate_array(1);
	assign_svalue_no_free(&v->item[0], sp);
    }
    
    if (!(current_object->flags & O_DESTRUCTED))
	if (search_for_function(arg[0].u.string, current_object->prog))
	{
	    if (function_type_mod_found & TYPE_MOD_PRIVATE &&
		inh_offset < function_inherit_found -
		function_prog_found->num_inherited + 1)
		error("Atempted callout of private function.\n");
		
	    ret = new_call_out(current_object, function_index_found,
			       function_inherit_found,
			       delay, reload, v);
	}
    if (v)
	free_vector(v);
    pop_n_elems(num_arg);
    push_number(ret);
}

static void
f_remove_alarm(int num_arg)
{
    extern void delete_call(struct object *, int);
    
    delete_call(current_object, sp->u.number);
}

static void
f_get_all_alarms(int num_arg)
{
    struct vector *ret;
    extern struct vector *get_calls(struct object *);
    ret = get_calls(current_object);
    if (ret)
    {
	push_vector(ret);
	ret->ref--;
    }
    else
	push_number(0);
}
static void
f_get_alarm(int num_arg)
{
    struct vector *ret;
    extern struct vector *get_call(struct object *, int);
    ret = get_call(current_object, sp->u.number);
    pop_stack();
    if (ret)
    {
	push_vector(ret);
	ret->ref--;
    }
    else
	push_number(0);
}	


#ifdef WORD_WRAP
static void
f_set_screen_width(int num_arg)
{
    if (! current_object->interactive)
	return;
    if (sp->u.number < 0 || sp->u.number == 1)
	error("Nonsensical screen width\n");
    current_object->interactive->screen_width = sp->u.number;
    if (current_object->interactive->current_column >=
	sp->u.number)
	current_object->interactive->current_column = sp->u.number - 1;

    /* Return first argument */
}

static void
f_query_screen_width(int num_arg)
{
    int i;

    i = -1;
    if (current_object->interactive)
	i = current_object->interactive->screen_width;
    push_number(i);
}
#endif

static void
f_sprintf(int num_arg)
{
    char *s;

    /*
     * string_print_formatted() returns a pointer to it's internal
     * buffer, or to an internal constant...  Either way, it must
     * be copied before it's returned as a string.
     */

    s = string_print_formatted(1, (sp-num_arg+1)->u.string,
			       num_arg-1, sp-num_arg+2);
    pop_n_elems(num_arg);
    if (!s) 
	push_number(0); 
    else 
	push_malloced_string(string_copy(s));
}

static void
f_member_array(int num_arg)
{
    struct vector *v;
    int		  i;

    if (sp->type == T_NUMBER)
    {
	pop_n_elems(2);
	push_number(-1);
	return;
    }
    v = sp->u.vec;
    check_for_destr(sp);
    for (i=0; i < v->size; i++) {
	if (v->item[i].type != (sp-1)->type)
	    continue;
	switch((sp-1)->type) {
	    case T_STRING:
		if (strcmp((sp-1)->u.string, v->item[i].u.string) == 0)
		    break;
		continue;
	    case T_POINTER:
		if ((sp-1)->u.vec == v->item[i].u.vec)
		    break;
		continue;
	    case T_MAPPING:
		if ((sp-1)->u.map == v->item[i].u.map)
		    break;
		continue;
	    case T_OBJECT:
		if ((sp-1)->u.ob == v->item[i].u.ob)
		    break;
		continue;
	    case T_NUMBER:
		if ((sp-1)->u.number == v->item[i].u.number)
		    break;
		continue;
	    default:
		fatal("Bad type to member_array(): %d\n", (sp-1)->type);
	}
	break;
    }
    if (i == v->size)
	i = -1;			/* Return -1 for failure */
    pop_n_elems(2);
    push_number(i);
}

static void
f_move_object(int num_arg)
{
    struct object *ob;

    if (sp->type == T_OBJECT)
	ob = sp->u.ob;
    else
	ob = find_object(sp->u.string);
    move_object(ob);
}

static void
f_update_actions(int num_arg)
{
    update_actions(current_object);
}

static void
f_function_exists(int num_arg)
{
    char *str, *res;

    str = function_exists((sp-1)->u.string, sp->u.ob);
    pop_n_elems(2);
    if (str) {
	res = add_slash(str);
	if (str = strrchr(res, '.'))
	    *str = 0;
	push_malloced_string(res);
    } else {
	push_number(0);
    }
}

static void
f_snoop(int num_arg)
{
    struct object *ob;

    /* 
     * This one takes a variable number of arguments. It returns
     * 0 or an object.
     */
    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 (set_snoop(sp->u.ob, 0))
		    ob = sp->u.ob;
		break;
	    case 2:
		if (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);
	else
	    push_number(0);
    }
}

static void
f_add_action(int num_arg)
{
    struct svalue *arg = sp - num_arg + 1;

    if (num_arg == 3) {
	if (arg[2].type != T_NUMBER)
	    bad_arg(3, F_ADD_ACTION, &arg[2]);
    }
    add_action(&arg[0],
	       num_arg > 1 ? &arg[1] : (struct svalue *)0,
	       num_arg > 2 ? arg[2].u.number : 0);
    pop_n_elems(num_arg - 1);
}

static void
f_allocate(int num_arg)
{
    struct vector *v;

    v = allocate_array(sp->u.number); /* Will have ref count == 1 */
    pop_stack();
    push_vector(v);
    v->ref--;
}

static void
f_ed(int num_arg)
{
    if (num_arg == 0) {
	push_number(0);
    }
    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();
    }
}

static void
f_crypt(int num_arg)
{
    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), 0)];
	salt[1] = choise[random_number(strlen(choise), 0)];
    }
#ifdef sun
    res = string_copy(_crypt((sp-1)->u.string, salt));
#else
    res = string_copy(crypt((sp-1)->u.string, salt));
#endif
    pop_n_elems(2);
    push_malloced_string(res);
}

static void
f_destruct(int num_arg)
{
    destruct_object(current_object);
    push_number(0);
}

static void
f_random(int num_arg)
{
    int seed;
    struct svalue *arg = sp - num_arg + 1;

    if (num_arg > 1)
    {
	seed = arg[1].u.number;
	pop_stack();
	sp->u.number = random_number(arg[0].u.number, seed);
    }
    else
	sp->u.number = random_number(arg[0].u.number, 0);
}

static void
f_while(int num_arg)
{
    fatal("F_WHILE should not appear.\n");
}

static void
f_do(int num_arg)
{
    fatal("F_DO should not appear.\n");
}

static void
f_for(int num_arg)
{
    fatal("F_FOR should not appear.\n");
}

static unsigned short read_short(char *addr)
{
    unsigned short ret;

    ((char *)&ret)[0] = ((char *)addr)[0];
    ((char *)&ret)[1] = ((char *)addr)[1];

    return ret;
}
static int read_int(char *addr)
{
    int ret;

    ((char *)&ret)[0] = ((char *)addr)[0];
    ((char *)&ret)[1] = ((char *)addr)[1];
    ((char *)&ret)[2] = ((char *)addr)[2];
    ((char *)&ret)[3] = ((char *)addr)[3];

    return ret;
}

static int
cmp_values(long val, long cmp, int how)
{
    if (how)
	return strcmp((char *)val, current_prog->rodata + cmp);
    else
	return val - cmp;
}

struct case_entry
{
    int value;
    unsigned short offset;
};

static void
f_switch(int num_arg)
{
#define TABLE_OFF       1
#define TABLE_END_OFF   4
#define DEFAULT_OFF     6

#define STR_TABLE 1

#define E_VALUE  0
#define E_OFFSET 4
#define ENTRY_SIZE 6

#define RANGE_OFFSET ((unsigned short)-1)

    int tab_head, tab_tail, tab_mid;
    unsigned int tab_start, tab_end, offset;
    long search_val;
    short tab_type, is_str;

    tab_type = (*pc) & 0xff;
    is_str = tab_type & STR_TABLE;

    tab_start = read_short(pc + TABLE_OFF);
    tab_end = read_short(pc + TABLE_END_OFF);

    tab_head = 0;
    tab_tail = (tab_end - tab_start) / ENTRY_SIZE - 1;
    if (is_str)
    {
	/* This table has 0 as case label as first entry in the table */

	if (sp->type == T_NUMBER && !sp->u.number)
	{
	    pc = current_prog->program + read_short(current_prog->program + tab_start + E_OFFSET);
	    pop_stack();
	    return;
	}
	if (sp->type != T_STRING)
	    bad_arg(1, F_SWITCH, sp);
	search_val = (long) sp->u.string;
	tab_head++;
    }
    else if (sp->type == T_NUMBER)
	search_val = sp->u.number;
    else
	bad_arg(1, F_SWITCH, sp);

    while (tab_head <= tab_tail)
    {
	tab_mid = (tab_head + tab_tail) / 2;

	if (read_short(current_prog->program + tab_start +
		       tab_mid * ENTRY_SIZE + E_OFFSET) == RANGE_OFFSET ||
	    tab_mid != tab_head &&
	    read_short(current_prog->program + tab_start +
		       tab_mid * ENTRY_SIZE + E_OFFSET - ENTRY_SIZE) ==
	    RANGE_OFFSET && (tab_mid--, 1))
	{
	    /* It is a range entry */
	    int lo_value, hi_value;

	    lo_value = read_int(current_prog->program + tab_start +
				tab_mid * ENTRY_SIZE + E_VALUE);
	    hi_value = read_int(current_prog->program + tab_start +
				tab_mid * ENTRY_SIZE + E_VALUE + ENTRY_SIZE);
	    if (cmp_values(search_val, lo_value, is_str) < 0)
		tab_tail = tab_mid - 1;
	    else if (cmp_values(search_val, hi_value, is_str) > 0)
		tab_head = tab_mid + 2;
	    else
	    {
		pc = current_prog->program +
		    read_short(current_prog->program + tab_start +
			       tab_mid * ENTRY_SIZE + E_OFFSET + ENTRY_SIZE);
		pop_stack();
		return;
	    }
	}
	else 
	{
	    /* It is an ordinary entry */
	    int value, cmp;

	    value = read_int(current_prog->program + tab_start +
			     tab_mid * ENTRY_SIZE + E_VALUE);
	    if ((cmp = cmp_values(search_val, value, is_str)) == 0)
	    {
		pc = current_prog->program +
		    read_short(current_prog->program + tab_start +
			       tab_mid * ENTRY_SIZE + E_OFFSET);
		pop_stack();
		return;
	    }
	    else if (cmp < 0)
		tab_tail = tab_mid - 1;
	    else
		tab_head = tab_mid + 1;
	}
    }
    /* No match, use default */
    pc = current_prog->program + read_short(pc + DEFAULT_OFF);
    pop_stack();
    return;
}

static void
f_break(int num_arg)
{
    error("Bad break code, this should not happend.\n");
}

static void
f_subscript(int num_arg)
{
    fatal("F_SUBSCRIPT should not appear.\n");
}

static void
f_strlen(int num_arg)
{
    int i;

    if (sp->type == T_NUMBER)
	i = 0;
    else
	i = strlen(sp->u.string);
    pop_stack();
    push_number(i);
}

static void
f_mkmapping(int num_arg)
{
    struct mapping *m;

    if ((sp-1)->type == T_POINTER && sp->type == T_POINTER)
	m = make_mapping((sp-1)->u.vec, sp->u.vec);
    else if ((sp-1)->type == T_NUMBER && sp->type == T_POINTER)
	m = make_mapping(NULL, sp->u.vec);
    else if ((sp-1)->type == T_POINTER && sp->type == T_NUMBER)
	m = make_mapping((sp-1)->u.vec, NULL);
    else if ((sp-1)->type == T_NUMBER && sp->type == T_NUMBER)
    {
	error("One argument must be a pointer.");
	return;
    }
    pop_n_elems(2);
    push_mapping(m);
    m->ref--;	/* Will make ref count == 1 */
}

static void
f_m_sizeof(int num_arg)
{
    int i;

    if (sp->type == T_MAPPING)
	i = card_mapping(sp->u.map);
    else
	i = 0;
    pop_stack();
    push_number(i);
}

static void
f_m_indexes(int num_arg)
{
    struct vector *v;

    if (sp->type == T_MAPPING)
    {
	v = map_domain(sp->u.map);
	pop_stack();
	push_vector(v);
	v->ref--;	/* Will make ref count == 1 */
    }
    else
    {
	pop_stack();
	push_number(0);
    }
}

static void
f_m_values(int num_arg)
{
    struct vector *v;

    if (sp->type == T_MAPPING)
    {
	v = map_codomain(sp->u.map);
	pop_stack();
	push_vector(v);
	v->ref--;	/* Will make ref count == 1 */
    }
    else
    {
	pop_stack();
	push_number(0);
    }
}

static void
f_m_delete(int num_arg)
{
    struct mapping *m;

    if ((sp-1)->type == T_MAPPING)
    {
	m = remove_mapping((sp-1)->u.map, sp);
	pop_n_elems(2);
	push_mapping(m);
	m->ref--; /* Refcount is 2 after the push */
    }
    else
    {
	pop_n_elems(2);
	push_number(0);
    }
}

static void
f_sizeof(int num_arg)
{
    int i;

    if (sp->type == T_NUMBER)
	i = 0;
    else
	i = sp->u.vec->size;
    pop_stack();
    push_number(i);
}

static void
f_lower_case(int num_arg)
{
    char *str;
    int  i;

    if (sp->type == T_NUMBER)
	return;
    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_malloced_string(str);
}

static void
f_readable_string(int num_arg)
{
    char *str;
    int  i;

    if (sp->type == T_NUMBER)
	return;
    str = string_copy(sp->u.string);
    for (i = strlen(str)-1; i>=0; i--)
	if (str[i] < ' ' || !isprint(str[i]))
	    str[i] = '.';
    pop_stack();
    push_malloced_string(str);
}


static void
f_capitalize(int num_arg)
{
    if (sp->type == T_NUMBER)
	return;
    if (islower(sp->u.string[0])) {
	char *str;

	str = string_copy(sp->u.string);
	str[0] += 'A' - 'a';
	pop_stack();
	push_malloced_string(str);
    }
}

static void
f_process_string(int num_arg)
{
    extern char *process_string (char *, int);
    char *str;

    str = process_string(sp[-1].u.string, sp->u.number);
    pop_stack();
    if (str != sp->u.string)
    {
	pop_stack();
	push_malloced_string(str);
    }
}

static void
f_process_value(int num_arg)
{
    extern struct svalue *process_value (char *, int);
    struct svalue *ret;

    ret = process_value(sp[-1].u.string, sp->u.number);
    pop_stack();
    pop_stack();
    if (ret)
    {
	push_svalue(ret);
    }
    else
	push_number(0);
}

static void
f_command(int num_arg)
{
    int i;

    i = command_for_object(sp->u.string, 0);
    pop_stack();
    push_number(i);
}

static void
f_get_dir(int num_arg)
{
    struct vector *v = get_dir(sp->u.string);

    pop_stack();
    if (v) {
	push_vector(v);
	v->ref--;	/* Will now be 1. */
    } else
	push_number(0);
}

static void
f_rm(int num_arg)
{
    int i;

    i = remove_file(sp->u.string);
    pop_stack();
    push_number(i);
}

static void
f_mkdir(int num_arg)
{
    char *path;

    path = check_valid_path(sp->u.string, current_object, "mkdir", 1);
    /* pop_stack(); see comment above... */
    if (path == 0 || mkdir(path, 0774) == -1)
	assign_svalue(sp, &const0);
    else
	assign_svalue(sp, &const1);
}

static void
f_rmdir(int num_arg)
{
    char *path;

    path = check_valid_path(sp->u.string, current_object, "rmdir", 1);
    /* pop_stack(); rw - what the heck ??? */
    if (path == 0 || rmdir(path) == -1)
	assign_svalue(sp, &const0);
    else
	assign_svalue(sp, &const1);
}

static void
f_input_to(int num_arg)
{
    struct svalue *arg = sp - num_arg + 1;
    struct vector *v;
    int flag = 1, i;

    v = allocate_array((num_arg > 1) ? num_arg - 2:0);
    if (num_arg == 1 || (arg[1].type == T_NUMBER && arg[1].u.number == 0))
	flag = 0;
    for (i = 2; i < num_arg; i++)
	assign_svalue_no_free(&(v->item[i - 2]), &(arg[i]));
    i = input_to(arg[0].u.string, flag,v);
    pop_n_elems(num_arg);
    push_number(i);
}

static void
f_set_living_name(int num_arg)
{
    set_living_name(current_object, sp->u.string);
}

static void
f_parse_command(int num_arg)
{
    struct svalue *arg;
    int	i;

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

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

static void
f_debug(int num_arg)
{
    struct svalue *arg, *ret;
    int i;

    arg = sp - num_arg + 1;
    if (current_object != master_ob)
    {
	push_object(current_object);
	for (i = 0; i < num_arg; i++)
	    push_svalue(&arg[i]);
	ret = apply_master_ob(M_VALID_DEBUG, num_arg + 1);
	if (!ret || ret->u.number == 0)
	{
	    pop_n_elems(num_arg);	/* Get rid of all arguments */
	    push_number(0);
	}
    }
    arg = sp - num_arg + 1;
    if (arg[0].type != T_STRING)
	bad_arg(1, F_DEBUG, &arg[0]);

    ret = debug_command(arg[0].u.string,
			num_arg-1, sp-num_arg+2);
    pop_n_elems(num_arg - 1);	/* Get rid of all arguments */
    assign_svalue(sp, ret);
    free_svalue(ret);
}

static void
f_sscanf(int num_arg)
{
    int i;

    num_arg = EXTRACT_UCHAR(pc);
    pc++;
    i = inter_sscanf(num_arg);
    pop_n_elems(num_arg);
    push_number(i);
}

static void
f_enable_commands(int num_arg)
{
    enable_commands(1);
    push_number(1);
}

static void
f_disable_commands(int num_arg)
{
    enable_commands(0);
    push_number(0);
}

static void
f_present(int num_arg)
{
    struct object *ob;

    ob = object_present((sp-1), sp);
    pop_stack();
    pop_stack();
    if (ob)
	push_object(ob);
    else
	push_number(0);
}

static void
f_const0(int num_arg)
{
    push_number(0);
}

static void
f_const1(int num_arg)
{
    push_number(1);
}

static void
f_number(int num_arg)
{
    int i;

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

static void
f_assign(int num_arg)
{
#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();
}

static void
f_ctime(int num_arg)
{
    char *cp;

    cp = string_copy(time_string(sp->u.number));
    pop_stack();
    push_malloced_string(cp);
    /* Now strip the newline. */
    cp = strchr(cp, '\n');
    if (cp)
	*cp = '\0';
}

static void
f_add_eq(int num_arg)
{
    struct svalue *argp;
    char *new_str;
    int  i;
    float fl;

    if (sp[-1].type != T_LVALUE)
	bad_arg(1, F_ADD_EQ, sp-1);
    argp = sp[-1].u.lvalue;
    switch(argp->type) 
    {
	case T_STRING:
	    if (sp->type == T_STRING) 
	    {
		int l = strlen(argp->u.string);
		new_str = xalloc(l + strlen(sp->u.string) + 1);
		strcpy(new_str, argp->u.string);
		strcpy(new_str+l, sp->u.string);
		pop_n_elems(2);
		push_malloced_string(new_str);
	    } 
	    else if (sp->type == 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_malloced_string(new_str);
	    } 
	    else 
	    {
		bad_arg(2, F_ADD_EQ, sp);
	    }
	    break;
	case T_NUMBER:
	    if (sp->type == T_NUMBER) 
	    {
		i = argp->u.number + sp->u.number;
		pop_n_elems(2);
		push_number(i);
	    } 
	    else 
	    {
		error("Bad type number to rhs +=.\n");
	    }
	    break;
	case T_FLOAT:
	    if (sp->type == T_FLOAT) 
	    {
		fl = argp->u.real + sp->u.real;
		pop_n_elems(2);
		push_float(fl);
	    } 
	    else 
	    {
		error("Bad type number to rhs +=.\n");
	    }
	    break;
	case T_MAPPING:
	    if (sp->type != T_MAPPING) {
		error("Bad type to rhs +=.\n");
	    }
	    else {
		struct mapping *m;

		check_for_destr(argp);
		check_for_destr(sp);
		addto_mapping(argp->u.map, sp->u.map);
		m = argp->u.map;
		m->ref++;
		pop_n_elems(2);
		push_mapping(m);
		m->ref--; /* Fix ref count */
	    }
	    break;
	case T_POINTER:
	    if (sp->type != T_POINTER) {
		error("Bad type to rhs +=.\n");
	    }
	    else {
		struct vector *v;

		check_for_destr(argp);
		check_for_destr(sp);
		v = add_array(argp->u.vec,sp->u.vec);
		pop_n_elems(2);
		push_vector(v); /* This will make ref count == 2 */
		v->ref--;
	    }
	    break;	      
	default:
	    error("Bad type to lhs += ");
    }
    assign_svalue(argp, sp);
}

static void
f_sub_eq(int num_arg)
{
    struct svalue *argp;

    if (sp[-1].type != T_LVALUE)
	bad_arg(1, F_SUB_EQ, sp-1);
    argp = sp[-1].u.lvalue;
    switch (argp->type) {
	case T_NUMBER:
	    if (sp->type != T_NUMBER)
		error("Bad right type to -=");
	    argp->u.number -= sp->u.number;
	    sp--;
	    break;
	case T_FLOAT:
	    if (sp->type != T_FLOAT)
		error("Bad right type to -=");
	    argp->u.real -= sp->u.real;
	    sp--;
	    break;
	case T_POINTER:
	    {
		struct vector *v;
		
		if (sp->type != T_POINTER)
		    error("Bad right type to -=");
		
		v = subtract_array(argp->u.vec,  sp->u.vec);
		
		pop_stack();
		pop_stack();
		
		if (v == 0) 
		{
		    push_number(0);
		} 
		else 
		{
		    push_vector(v); /* This will make ref count == 2 */
		    v->ref--;
		}
		
		assign_svalue(argp, sp);
		break;
	    }
	default:
	    error("Bad left type to -=.\n");
    }
    assign_svalue(sp, argp);
}

static void
f_mult_eq(int num_arg)
{
    struct svalue *argp;
    float fl;
    int i;

    if (sp[-1].type != T_LVALUE)
	bad_arg(1, F_MULT_EQ, sp-1);
    argp = sp[-1].u.lvalue;
    if (argp->type == T_FLOAT && sp->type == T_FLOAT)
    {
	fl =argp->u.real * sp->u.real;
	pop_n_elems(2);
	push_float(fl);
	assign_svalue(argp, sp);
        return;
    }
    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);
}

static void
f_and_eq(int num_arg)
{
    struct svalue *argp;
    int i;

    if (sp[-1].type != T_LVALUE)
	bad_arg(1, F_AND_EQ, sp-1);
    argp = sp[-1].u.lvalue;
    switch (argp->type)
    {
	case T_NUMBER:
	    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 T_POINTER:
	    {
		struct vector *v;
		
		if (sp->type != T_POINTER)
		    error("Bad right type to &=");
		
		v = intersect_array(argp->u.vec,  sp->u.vec);
		
		pop_stack();
		pop_stack();
		
		if (v == 0) 
		{
		    push_number(0);
		} 
		else 
		{
		    push_vector(v); /* This will make ref count == 2 */
		    v->ref--;
		}
		
		assign_svalue(argp, sp);
		break;
	    }
	default:
	    error("Bad left type to &=.\n");
    }
}

static void
f_or_eq(int num_arg)
{
    struct svalue *argp;
    int i;

    if (sp[-1].type != T_LVALUE)
	bad_arg(1, F_OR_EQ, sp-1);
    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);
}

static void
f_xor_eq(int num_arg)
{
    struct svalue *argp;
    int i;

    if (sp[-1].type != T_LVALUE)
	bad_arg(1, F_XOR_EQ, sp-1);
    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);
}

static void
f_lsh_eq(int num_arg)
{
    struct svalue *argp;
    int i;

    if (sp[-1].type != T_LVALUE)
	bad_arg(1, F_LSH_EQ, sp-1);
    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);
}

static void
f_rsh_eq(int num_arg)
{
    struct svalue *argp;
    int i;

    if (sp[-1].type != T_LVALUE)
	bad_arg(1, F_RSH_EQ, sp-1);
    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);
}

#ifdef F_COMBINE_FREE_LIST
static void
f_combine_free_list(int num_arg)
{
#ifdef MALLOC_malloc
    push_number(resort_free_list());
#else
    push_number(0);
#endif
}
#endif

static void
f_div_eq(int num_arg)
{
    struct svalue *argp;
    int i;
    float fl;

    if (sp[-1].type != T_LVALUE)
	bad_arg(1, F_DIV_EQ, sp-1);
    argp = sp[-1].u.lvalue;
    if (argp->type == T_FLOAT && sp->type == T_FLOAT)
    {
	if (sp->u.real == 0.0)
	    error("Division by 0\n");
        fl = argp->u.real / sp->u.real;
	pop_n_elems(2);
	push_float(fl);
	assign_svalue(argp, sp);
        return;
    }
    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);
}

static void
f_mod_eq(int num_arg)
{
    struct svalue *argp;
    int i;

    if (sp[-1].type != T_LVALUE)
	bad_arg(1, F_MOD_EQ, sp-1);
    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);
}

static void
f_string(int num_arg)
{
    unsigned short string_number;

    ((char *)&string_number)[0] = pc[0];
    ((char *)&string_number)[1] = pc[1];
    pc += 2;
    push_string(current_prog->rodata + string_number,
		STRING_MALLOC);
}

static void
f_cindent(int num_arg)
{
    char *path;

    path = check_valid_path(sp->u.string, current_object, "cindent", 1);
    if (path) {
	if (indent_program(path)) {
	    assign_svalue(sp, &const1);
	    return;
	}
    } else {
	add_message("Illegal attempt to indent\n");
    }
    assign_svalue(sp, &const0);
}

static void
f_unique_array(int num_arg)
{
    struct vector *res;

    if ((sp - (num_arg - 1))->type == T_NUMBER)
    {
	pop_n_elems(num_arg);
	push_number(0);
	return;
    }
    if (num_arg < 3) {
	check_for_destr(sp-1);
	res = make_unique((sp-1)->u.vec, sp->u.string, &const0);
    }
    else {
	check_for_destr(sp-2);
	res = make_unique((sp-2)->u.vec, (sp-1)->u.string, sp);
	pop_stack ();
    }
    pop_n_elems(2);
    if (res) {
	push_vector (res);	/* This will make ref count == 2 */
	res->ref--;
    }
    else
	push_number (0);
}

static void
f_rename(int num_arg)
{
    int i;

    i = do_rename((sp-1)->u.string, sp->u.string);
    pop_n_elems(2);
    push_number(i);
}

static void
f_map(int num_arg)
{
    struct vector *res;
    struct svalue *arg;
    struct mapping *m;
    struct object *ob;

    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, &arg[2]);

    if (arg[0].type == T_POINTER) {
	check_for_destr(&arg[0]);
	res = map_array(arg[0].u.vec, arg[1].u.string, ob,
			num_arg > 3 ? sp : (struct svalue *)0);
    }
    else
	res = 0;
    
    if (arg[0].type == T_MAPPING) {
	check_for_destr(&arg[0]);
	m = map_map(arg[0].u.map, arg[1].u.string, ob,
		    num_arg > 3 ? sp : (struct svalue *)0);
    } else {
	m = 0;
    }

    pop_n_elems (num_arg);
    if (res) {
	push_vector(res);	/* This will make ref count == 2 */
	res->ref--;
    } else if (m) {
	push_mapping(m);	/* This will make ref count == 2 */
	m->ref--;
    } else
    {
	push_number (0);
    }
}

static void
f_sqrt(int num_arg)
{
    extern double sqrt();

    sp->u.real = sqrt((double)sp->u.real);
}

#include "efun_table.h"

#ifdef USE_SWAP
void
access_object(struct object *ob)
{
    extern struct object *obj_list;
    extern struct object *swap_ob;
    if (ob->flags & O_SWAPPED)
	load_ob_from_swap(ob);
    ob->time_of_ref = current_time;

    if (ob != obj_list && !(ob->flags & O_DESTRUCTED))
    {
	ob->prev_all->next_all = ob->next_all;
	ob->next_all->prev_all = ob->prev_all;
	if (ob == swap_ob)
	    swap_ob = ob->prev_all;

	ob->next_all = obj_list;
	ob->prev_all = obj_list->prev_all;

	obj_list->prev_all->next_all = ob;
	obj_list->prev_all = ob;
	obj_list = ob;
    }
}
void
access_program(struct program *prog)
{
    extern struct program *prog_list;
    extern struct program *swap_prog;
    
    if (prog->program == (char *)0)
	load_prog_from_swap(prog);
    prog->time_of_ref = current_time;
    if (prog != prog_list)
    {
	prog->prev_all->next_all = prog->next_all;
	prog->next_all->prev_all = prog->prev_all;
	if (prog == swap_prog)
	    swap_prog = prog->prev_all;

	prog->next_all = prog_list;
	prog->prev_all = prog_list->prev_all;

	prog_list->prev_all->next_all = prog;
	prog_list->prev_all = prog;
	prog_list = prog;
    }
}
#endif


static void 
eval_instruction(char *p)
{
    struct object *ob;
    int i;
    int instruction;
    unsigned short ext_instr;
#ifdef DEBUG
    struct svalue *expected_stack;
#endif
    struct svalue *argp;
    float fl;

    /* Next F_RETURN at this level will return out of eval_instruction() */
    csp->extern_call = 1;

#ifdef RUSAGE
    {
#ifdef SOLARIS
	struct tms buffer;
#else
	struct rusage rus;
#endif
	long cpu;
	
#ifdef SOLARIS
	if (times(&buffer) != -1)
	    cpu = (buffer.tms_utime + buffer.tms_stime);
#else
	if (getrusage(RUSAGE_SELF, &rus) >= 0) 
	{                           
	    cpu = rus.ru_utime.tv_sec * 1000 + rus.ru_utime.tv_usec / 1000 +
		rus.ru_stime.tv_sec * 1000 + rus.ru_stime.tv_usec / 1000; 
	}
#endif
	else
	    cpu = (-1);
	
	if (progcpui >= MAX_CPU_STACK) 
	    fatal("CPU-Stack overflow.\n");
	
	progcpu[progcpui].prog = current_prog;
	progcpu[progcpui++].cpu = cpu;
    }	
#endif
    
    pc = p;
 again:
    i = instruction = EXTRACT_UCHAR(pc);

#ifdef EXTENDED_INSTRUCTIONS
    if (instruction == F_EXT - F_OFFSET)
    {
	((char *)&ext_instr)[0] = pc[1];
	((char *)&ext_instr)[1] = pc[2];
	instruction = ext_instr;
    }
#endif

#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

#ifdef EXTENDED_INSTRUCTIONS
    if (i == F_EXT - F_OFFSET)
	pc += 2;
#endif

    pc++;
    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.
     */
    { 
	int xnum_arg;
	if (instrs[instruction].min_arg != instrs[instruction].max_arg)
	{
	    xnum_arg = num_arg = EXTRACT_UCHAR(pc);
	    pc++;
	}
	else
	{
	    xnum_arg = -1;
	    num_arg = instrs[instruction].min_arg;
	}
	if (num_arg > 0)
	{
	    int type1 = (sp-num_arg+1)->type, type2 = (sp-num_arg+2)->type;
	    if (instrs[instruction].type[0] != 0 &&
		(instrs[instruction].type[0] & type1) == 0)
	    {
		bad_arg(1, instruction + F_OFFSET, sp-num_arg+1);
	    }
	    if (num_arg > 1)
	    {
		if (instrs[instruction].type[1] != 0 &&
		    (instrs[instruction].type[1] & type2) == 0)
		{
		    bad_arg(2, instruction + F_OFFSET, sp-num_arg+2);
		}
	    }
	}
	/*
         * Safety measure. It is supposed that the evaluator knows
	 * the number of arguments.
	 */
	num_arg = xnum_arg;
    }
#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.
     */
#ifdef TRACE_CODE
    if (TRACEP(TRACE_EXEC)) {
	do_trace("Exec ", get_f_name(instruction), "\n");
    }
#endif
    switch(instruction)
    {
    default:
#ifdef DEBUG
	if (instruction >= EFUN_FIRST && instruction <= EFUN_LAST)
#endif
	    efun_table[instruction - EFUN_FIRST](num_arg);
#ifdef DEBUG
	else
	    fatal("Undefined instruction %s (%d)\n", get_f_name(instruction),
		  instruction);
#endif
	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--;
#ifdef TRACE_CODE
	    if (TRACEP(TRACE_RETURN)) {
		do_trace("Return", "", "");
		if (TRACEHB) {
		    if (TRACETST(TRACE_ARGS)) {
			write_socket(string_print_formatted(0, " with value: %O", 1, sp),
				     command_giver);
		    }
		    write_socket("\n", command_giver);
		}
	    }
#endif

#ifdef RUSAGE
	    {
#ifdef SOLARIS
		struct tms buffer;
#else
		struct rusage rus;
#endif
		long cpu;
		int i;

#ifdef SOLARIS		
		if (times(&buffer) != -1)
		    cpu = (buffer.tms_utime + buffer.tms_stime);
#else
		if (getrusage(RUSAGE_SELF, &rus) >= 0) 
		{                      
		    cpu = rus.ru_utime.tv_sec * 1000 +
			rus.ru_utime.tv_usec / 1000 +
			    rus.ru_stime.tv_sec * 1000 +
				rus.ru_stime.tv_usec / 1000; 
		} 
#endif
		else cpu = (-1);
		
		if (--progcpui<0) 
		    fatal("CPU-Stack underflow.\n");
		
		if (cpu >= 0 && progcpu[progcpui].cpu >=0) 
		{
		    cpu = cpu - progcpu[progcpui].cpu;
		    for(i = progcpui-1; i >= 0; i--) 
			progcpu[i].cpu += cpu;
		    if (progcpu[progcpui].prog) 
			progcpu[progcpui].prog->cpu += cpu;
		}
	    }
#endif
	    if (csp[1].extern_call)	/* The control stack was popped just before */
		return;
	}
	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;
	    char *old_pc;
	    /*
	     * 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;
	    /*
	     * Save some global variables that must be restored separately
	     * after a longjmp. The stack will have to be manually popped all
	     * the way.
	     */
	    old_pc = pc;
	    pc = current_prog->program + new_pc_offset; /* save with pc == where to continue */
	    push_pop_error_context (1);
	    pc = old_pc;
	    /* 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);
		assign_svalue_no_free(++sp, &catch_value);
		
		/* If it was eval_cost too big when cant really catch it */
		if (eval_cost == 0)
		    eval_cost = MAX_COST;
	    }
	    
	    /* next error will return 1 by default */
	    assign_svalue(&catch_value, &const1);
	    break;
	}
    }
#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;
}
	    
#ifdef GLOBAL_CACHE
struct fcache1 {
    int		 	tp;
    char 		*fn;
    int			ff_inh;
    int			ff_ix;
};
#endif


int
s_f_f(char *name, struct program *prog, struct program *sprog,
		     int inherit_offset, int func_index)
{
    int probe = 0, i;
    struct program *cprog = prog;
    int type_mod;
#ifdef GLOBAL_CACHE
    static struct fcache1 fc[GLOBAL_CACHE];
    int global_hash_val;
    extern int globcache_hits;
    extern int globcache_tries;
    int hash_val;
#endif
    
    if (!name)
	return 0;
    
#ifdef GLOBAL_CACHE
	    
    /* 
     * Are we looking for the same function in the same program again?
     * This is common for map, filter etc
     */
    globcache_tries++;

    global_hash_val = (((unsigned int)prog / sizeof(void *)) ^
		       ((unsigned int)prog >> 16) ^
		       ((unsigned int)name / sizeof(void *)) ^
		       ((unsigned int)name >> 16)) & (GLOBAL_CACHE - 1);
    
    if (fc[global_hash_val].tp == prog->id_number &&
	fc[global_hash_val].fn == name)
    {
	globcache_hits++;
#ifdef CACHE_STATS
	global_first_saves += prog->num_inherited - fc[global_hash_val].ff_inh;
#endif
	function_inherit_found = fc[global_hash_val].ff_inh;
	function_index_found = fc[global_hash_val].ff_ix;
	if (function_inherit_found != -1)
	{
	    int type_mod = prog->inherit[function_inherit_found].type;
	    
	    function_prog_found = prog->inherit[function_inherit_found].prog;
	    access_program(function_prog_found);
	    function_type_mod_found = function_prog_found->
		functions[function_index_found].type_flags & TYPE_MOD_MASK ;

	    /* Correct function_type_mod_found */
	    if (function_type_mod_found & TYPE_MOD_PRIVATE)
		type_mod &= ~TYPE_MOD_PUBLIC;
	    if (function_type_mod_found & TYPE_MOD_PUBLIC)
		type_mod &= ~TYPE_MOD_PRIVATE;
	    function_type_mod_found |= type_mod;
	    return 1;
	}
	else
	{
	    function_prog_found = 0;
	    return 0;
	}
	
    }
    fc[global_hash_val].tp = prog->id_number;
    fc[global_hash_val].fn = name;
    fc[global_hash_val].ff_inh = -1;
#endif

#ifdef CACHE_STATS
    searches_needed += prog->num_inherited;
#endif
    i = prog->num_inherited - 1;
    cprog = prog;
    while (1)
    {
	
	/* Beware of empty function lists */
#ifdef CACHE_STATS
	    searches_done++;
#endif
	if (cprog->num_functions)
	{
	    /* hash 
	     */
	    probe = PTR_HASH(name, cprog->num_functions);
	    /* Select the right one from the chain 
	     */
	    while (name != cprog->func_hash[probe].name && probe >= 0)
		probe = cprog->func_hash[probe].next_hashed_function;
	    
	    if (probe >= 0)
	    {
		probe = cprog->func_hash[probe].func_index;
		break;
	    }
	}
	if (--i < 0)
	    return 0;
	
	cprog = prog->inherit[i].prog;

    }

    /* Found. Undefined prototypes cannot occur in compiled programs 
	*/
#ifdef CACHE_STATS
    searches_needed -= i;
#endif

#ifdef GLOBAL_CACHE
    fc[global_hash_val].ff_inh = 
#endif
	function_inherit_found = i;
    
    function_prog_found = prog->inherit[i].prog;
    access_program(function_prog_found);
#ifdef GLOBAL_CACHE
    fc[global_hash_val].ff_ix =
#endif
	function_index_found = probe;
    
    function_type_mod_found =
	prog->inherit[i].prog->functions[probe].type_flags &
	    TYPE_MOD_MASK ;
    
    /* Correct function_type_mod_found */
    type_mod = prog->inherit[i].type;
    
    if (function_type_mod_found & TYPE_MOD_PRIVATE)
	type_mod &= ~TYPE_MOD_PUBLIC;
    if (function_type_mod_found & TYPE_MOD_PUBLIC)
	type_mod &= ~TYPE_MOD_PRIVATE;
    function_type_mod_found |= type_mod;
    return 1;
}

int search_for_function(char *name, struct program *prog)
{
    return s_f_f(findstring(name), prog, NULL, 0, 0);
}
	    
/*
 * 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 !
 */
	    
char debug_apply_fun[30]; /* For debugging */
	    
int globcache_tries = 0, globcache_hits = 0, funmap_tries = 0, funmap_hits = 0;
	    
	    
	    
static int
apply_low(char *fun, struct object *ob, int num_arg, int external)
{
#ifdef CALL_WARNINGS
    extern int call_warnings;
    char *funtmp = fun;
#endif
    char *npc;
#ifdef DEBUG
    struct control_stack *save_csp;
#endif
    struct program *progp;
    extern int num_error;
    int ix;
    short fix;
    char *sfun;

    /*
     * This object will now be used, and is thus a target for
     * reset later on (when time due).
     */
	    
#ifdef DEBUG
    strncpy(debug_apply_fun, fun, sizeof debug_apply_fun);
    debug_apply_fun[sizeof debug_apply_fun - 1] = '\0';
#endif
    if (*fun == '.')
	goto failure;
	    
    /*
     * If there is a chain of objects shadowing, start with the first
     * of these.
     */
    while (ob->shadowed && ob->shadowed != current_object)
	ob = ob->shadowed;
	 
    sfun = findstring(fun);

 retry_for_shadow:
    progp = ob->prog;
	    
#ifdef DEBUG
    if (ob->flags & O_DESTRUCTED)
	fatal("apply() on destructed object\n");
#endif
    if (!(ob->flags & O_CREATED))
	create_object(ob);
    if (ob->flags & O_DESTRUCTED)
	goto failure;
    
    if (s_f_f(sfun, progp, NULL, 0, 0)) 
    {
	/* Static or private functions may not be called from outside. */
	if (((ob != current_object || external) &&
	     function_type_mod_found & (TYPE_MOD_STATIC | TYPE_MOD_PRIVATE)) ||
	    (function_type_mod_found & TYPE_MOD_PRIVATE && 
	     function_prog_found != ob->prog))
	    ; /* Do nothing */
	else
        {
	    call_function(ob, function_inherit_found, function_index_found,
			  num_arg);
	    
	    return 1;
	    
 	}
	    
    }
    
    /* Not found */
    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:
#ifdef CALL_WARNINGS
    /* This is code for mudlib debugging purposes. */
    if (call_warnings && strcmp(funtmp, ".CTOR") && strcmp(funtmp, "??") &&
	strncmp(funtmp, "add_prop", 8) && strcmp(funtmp, "reset") &&
	strcmp(funtmp, "init_living") && strcmp(funtmp, "replace_soul")&&
	strcmp(funtmp, "teleledningsanka"))
	printf("Failed to call %s in %s.\n", funtmp, ob->name);
#endif
    /* 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.
 */
	    
struct svalue *
sapply(char *fun, struct object *ob, int num_arg, int ext)
{
	    
#ifdef DEBUG
    struct svalue *expected_sp;
#endif
    static struct svalue ret_value = { T_NUMBER };
	    
	    
#ifdef TRACE_CODE
    if (TRACEP(TRACE_APPLY)) 
    {
	char buff[1024];
	sprintf(buff,"%s->%s", ob->name, fun);
	do_trace("Apply", "", "\n");
    }
#endif

#ifdef DEBUG
    expected_sp = sp - num_arg;
#endif
    if (!ob || ob->flags & O_DESTRUCTED) {
        pop_n_elems(num_arg);
	return 0;
    }
    
    if (apply_low(fun, ob, num_arg, ext) == 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(char *fun, struct object *ob, int num_arg, int ext)
{
    tracedepth = 0;
    return sapply(fun, ob, num_arg, ext);
}
	    
/*
 * 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(char *fun, struct object *ob)
{
    struct function *pr;
    extern char *findstring (char *);
	    
#ifdef DEBUG
    if (ob->flags & O_DESTRUCTED)
	fatal("function_exists() on destructed object\n");
#endif
    if (*fun == '.')
	return 0;
    if ( search_for_function (fun, ob->prog)
	&& (!(function_type_mod_found & (TYPE_MOD_STATIC|TYPE_MOD_PRIVATE))
	    || current_object == ob) ) 
	return function_prog_found->name;
    /* Not found */
    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(struct object *ob, int inh_index, unsigned int fun, int num_arg)
{
    struct function *funp;
    struct program *progp;
    
    if (inh_index < 0 || fun < 0 || inh_index >= ob->prog->num_inherited ||
	fun >= ob->prog->inherit[inh_index].prog->num_functions)
    {
	/* invalid function */
	pop_n_elems(num_arg);
	push_number(0);
	return;
    }
    progp = ob->prog->inherit[inh_index].prog;
    access_program(progp);
    funp = &progp->functions[fun];
    
    if (funp->type_flags & NAME_PROTOTYPE) /* Cannot happen. */
	return;
    
    push_control_stack(funp);
    csp->ext_call = 1;
    csp->num_local_variables = num_arg;
    current_prog = progp;
    inh_offset = inh_index;
    previous_ob = current_object;
    current_object = ob;
#ifdef DEBUG
    if (current_object->prog->inherit[inh_offset].prog != current_prog)
	fatal("Corrupt inherit offset!\n");
#endif
    eval_instruction(setup_new_frame(funp));
}
	    
/*
 * Get srccode position including runtime errors in included files.
 * 
 */

char *
inner_get_srccode_position(int offset, char *line_numbers, int lineno_size,
		     char *include_files, char *name)
{
    int inc_file, lineno, tmp_offset, i;
    unsigned int delta;
    char *st;
    static char buff[200];


    tmp_offset = 0;
    lineno = 0;
    inc_file = 0;

    for (i = 0; i < lineno_size &&
	 tmp_offset < offset; i++)
    {
	switch (delta = (unsigned int)line_numbers[i] & 0xff)
	{
	case 0xff:
	    {
		int dl;
		
		dl = (unsigned int)line_numbers[++i] & 0xff;
		lineno += dl;
		break;
	    }
	case 0xfe:
	    inc_file = (unsigned int)line_numbers[++i];
	    lineno = ((unsigned int)line_numbers[++i] & 0xff) << 8;
	    lineno += ((unsigned int)line_numbers[++i] & 0xff);
	    break;
	default:
	    tmp_offset += delta;
	    lineno++;
	    break;
	}
    }
    if (tmp_offset = offset)
	lineno++;

    if (!inc_file)
	st = name;
    else
    {
	/*
	 *  Find the name of the include file.
	 * All includefilenames are stored after eachother on the form:
	 * date:filename<null>date:filename<null> etc
	 */
	st = include_files;
	while (inc_file > 1)
	{
	    st = &st[strlen(st) + 1];
	    inc_file--;
	}
	st = strchr(st, ':');
	st++;
    }
    
    sprintf(buff, "/%s Line: %d", st, lineno);
    return buff;
}
char *
get_srccode_position(int offset, struct program *progp)
{
    char *ret;

    if (progp == 0)
	return "";
    
#ifdef DEBUG
    if (offset > progp->program_size)
	fatal("Illegal offset %d in object %s\n", offset, progp->name);
#endif
    load_lineno_from_swap(progp);

    ret = inner_get_srccode_position(offset, progp->line_numbers,
				     progp->sizeof_line_numbers,
				     progp->include_files,
				     progp->name);
    if (progp->swap_lineno_index > 0)
	swap_lineno(progp);

    return ret;
}

char *
old_get_srccode_position(int offset, struct program *progp)
{
    int i, top, bot, mid, inc, lin;
    static char posstr[200];
    char 	*st;
    
    if (progp == 0)
	return "";

    load_lineno_from_swap(progp);

#ifdef DEBUG
    if (offset > progp->program_size)
	fatal("Illegal offset %d in object %s\n", offset, progp->name);
#endif
	    
#define EOFS(x) progp->line_numbers[x*3]
    
    /* 
      The linenumber info is an array with three unsigned shorts / entry:
      [entry * ix + 0]   Offset in bytecode
      [entry * ix + 1]   Include file number (0 means main file)
      [entry * ix + 2]   Line number in above file
      
      The entries are sorted in offset order. Below is a simple
      binary search to find the correct srccode position.
      */
    top = (progp->sizeof_line_numbers / ((sizeof(unsigned short)) * 3));
    bot = 0;
    mid = ((top - bot) >> 1) + bot;
    
    while (mid > bot && mid < top)
    {
	if (offset > (int)EOFS(mid))
	    bot = mid;
	else
	    top = mid;
	mid = ((top - bot) >> 1) + bot;
    }
	    
    inc = progp->line_numbers[mid * 3 + 1];
    lin = progp->line_numbers[mid * 3 + 2];
    
    if (!inc)
	st = progp->name;
    else
    {
	    /*
	      Find the name of the include file.
	      All includefilenames are stored after eachother on the form:
	      date:filename<null>date:filename<null> etc
	      */
	st = progp->include_files;
	while (inc > 1)
	{
	    st = &st[strlen(st) + 1];
	    inc--;
	}
	st = strchr(st, ':');
	st++;
    }
    
    sprintf(posstr, "/%s Line: %d", st, lin);
    if (progp->swap_lineno_index > 0)
	swap_lineno(progp);
    
    return posstr;
}
	    
/*
 * Write out a trace. If there is an heart_beat(), then return the
 * object that had that heart beat.
 */
char *
dump_trace(int how)
{
    struct control_stack *p;
    char *ret = 0;
#ifdef DEBUG
    int last_instructions (void);
#endif
    char *line;

    if (current_prog == 0)
	return 0;
    if (csp < &control_stack[0])
    {
	(void) printf("No trace.\n");
	debug_message("No trace.\n");
	return 0;
    }
#if defined(DEBUG) && defined(TRACE_CODE)
    if (how)
	(void) last_instructions();
#endif
    for (p = &control_stack[0]; p < csp; p++) 
    {
#define FORM "%-15s in /%s\n                   /%s\n                   %s\n"
	line = get_srccode_position(p[1].pc, p[1].prog);
	debug_message(FORM,
		      p[0].funp ? p[0].funp->name : "CATCH",
		      p[1].prog->name, p[1].ob->name,
		      line);
	if (p->funp && strcmp(p->funp->name, "heart_beat") == 0)
	    ret = p->ob?p->ob->name:0; /*crash unliked gc*/
    }
    line = get_srccode_position(pc - current_prog->program,
				current_prog);
    debug_message(FORM,
		  p[0].funp ? p[0].funp->name : "CATCH",
		  current_prog->name, current_object->name,
		  line);
    return ret;
}

char *
get_srccode_position_if_any() 
{
    char *ret = "";

    if (current_prog)
	ret = (char *)get_srccode_position(pc - current_prog->program, current_prog);

    return ret;
}
	    
static char *
find_percent(char *str)
{
    while(1)
    {
	str = strchr(str, '%');
	if (str == 0)
	    return 0;
	if (str[1] != '%')
	    return str;
	str++;
    }
}
	    
static int
inter_sscanf(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)
	bad_arg(1, F_SSCANF, &arg[0]);
    in_string = arg[0].u.string;
    if (in_string == 0)
	return 0;
    /*
     * Now get the format description.
     */
    if (arg[1].type != T_STRING)
	bad_arg(2, F_SSCANF, &arg[1]);
    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);
		arg->u.lvalue->type = T_STRING;
		arg->u.lvalue->u.string = string_copy(in_string);
		arg->u.lvalue->string_type = STRING_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] == 'f')
	    type = T_FLOAT;
	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);
	    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;
	}
	if (type == T_FLOAT)
	{
	    extern double strtod();
	    char *tmp = in_string;
	    float tmp_num;
	    
	    tmp_num = (float) strtod(in_string, &in_string);
	    if(tmp == in_string)
	    {
		/* No match */
		break;
	    }
	    free_svalue(arg->u.lvalue);
	    arg->u.lvalue->type = T_FLOAT;
	    arg->u.lvalue->u.real = 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);
	    
	    arg->u.lvalue->type = T_STRING;
	    arg->u.lvalue->u.string = string_copy(in_string);
	    arg->u.lvalue->string_type = STRING_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);
		arg->u.lvalue->type = T_STRING;
		arg->u.lvalue->u.string = match;
		arg->u.lvalue->string_type = STRING_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(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(int a, int 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(struct program *progp, struct program *search_prog)
{
    int i;
	    
    /* Clones will not add to the ref count of inherited progs */
    if (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(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;
	}
    }
}
	    
/*
 * Clear the extra debug ref count for vectors
 */
void
clear_vector_refs(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, and should
 * only be used for debugging.
 */
void
check_a_lot_ref_counts(struct program *search_prog)
{
    extern struct object *master_ob;
    struct object *ob;
    extern struct object *swap_ob;
    
    /*
     * Pass 1: clear the ref counts.
     */
    ob = obj_list;
    do
    {
	ob->extra_ref = 0;
	ob->prog->extra_ref = 0;
	if (ob->flags & O_SWAPPED)
	    load_ob_from_swap(ob);
	    
	clear_vector_refs(ob->variables, ob->prog->num_variables +
			  ob->prog->inherit[ob->prog->num_inherited - 1]
			  .variable_index_offset);
	ob = ob->next_all;
    } while (ob != obj_list);
    swap_ob = obj_list->prev_all;

    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->inherit[ob->prog->num_inherited - 1]
			    .variable_index_offset);
	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);
    }
	    
    /*
     * 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++;
	    
    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 */
	    
/* Generate a debug message to the player */
static void
do_trace(char *msg, char *fname, char *post)
{
    char buf[10000];
    char *objname;
	    
    if (!TRACEHB)
	return;
    objname = TRACETST(TRACE_OBJNAME) ? (current_object && current_object->name ? current_object->name : "??")  : "";
    sprintf(buf, "*** %d %*s %s %s %s%s", tracedepth, tracedepth, "", msg, objname, fname, post);
    write_socket(buf, command_giver);
}


static void
resolve_table(struct program *prog, struct fkntab *tab)
{
    int i;

    for (i = 0; tab[i].name; i++)
    {
	if (search_for_function(tab[i].name, prog))
	{
	    tab[i].inherit_index = function_inherit_found;
	    tab[i].function_index = function_index_found;
	}
	else
	{
	    tab[i].inherit_index = (unsigned short)-1;
	    tab[i].function_index = (unsigned short)-1;
	}
    }
}

#include "master.t"
	    
struct svalue *
apply_master_ob(int fun, int num_arg)
{
    extern struct object *master_ob;
    FILE *cf;
    static struct svalue retval = { T_NUMBER };

    if (s_flag)
	num_mcall++;
    if (!master_ob || master_fkntab[fun].inherit_index == (unsigned short)-1 &&
	master_fkntab[fun].function_index == (unsigned short)-1)
    {
	pop_n_elems(num_arg);
	return 0; /* No such function */
    }
    call_function(master_ob, master_fkntab[fun].inherit_index,
		  master_fkntab[fun].function_index, num_arg);

    free_svalue(&retval);
    retval = *sp--;
    return &retval;
}

void
master_ob_loaded()
{
    extern struct object *master_ob;

    resolve_table(master_ob->prog, master_fkntab);
}
	    
/*EOT*/
	    
/*
 * When an object is destructed, all references to it must be removed
 * from the stack.
 */
void
remove_object_from_stack(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;
    }
}
void
stack_swap_objects(struct object *ob1, struct object *ob2)
{
    struct control_stack *cspi;

    if (current_object == ob1)
	current_object = ob2;
    else if (current_object == ob2)
	current_object = ob1;

    if (previous_ob == ob1)
	previous_ob = ob2;
    else if (previous_ob == ob2)
	previous_ob = ob1;
    
    for (cspi = csp; cspi >= control_stack; cspi--)
    {
	if (cspi->ob == ob1)
	    cspi->ob = ob2;
	else if (cspi->ob == ob2)
	    cspi->ob = ob1;
    
	if (cspi->prev_ob == ob1)
	    cspi->prev_ob = ob2;
	else if (cspi->prev_ob == ob2)
	    cspi->prev_ob = ob1;
    }
}    
static int
strpref(char *p, char *s)
{
    while (*p)
	if (*p++ != *s++)
	    return 0;
    return 1;
}