lpc4/lib/
lpc4/lib/doc/efun/
lpc4/lib/doc/lfun/
lpc4/lib/doc/operators/
lpc4/lib/doc/simul_efuns/
lpc4/lib/doc/types/
lpc4/lib/etc/
lpc4/lib/include/
lpc4/lib/include/arpa/
lpc4/lib/obj/d/
lpc4/lib/save/
lpc4/lib/secure/
lpc4/lib/std/
lpc4/lib/std/living/
#include <arpa/telnet.h>
#include <math.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 <netinet/in.h> Included in comm.h below */

#include "efuns.h"
#include "array.h"
#include "simulate.h"
#include "simul_efun.h"
#include "instrs.h"
#include "comm.h"
#include "list.h"
#include "operators.h"
#include "debug.h"
#include "main.h"
#include "stralloc.h"
#include "save_objectII.h"
#include "opcodes.h"
#include "lex.h"
#include "regexp.h"
#ifdef LACIP
#include "lacip/op.h"
#endif

extern struct object *master_ob;

extern void print_svalue PROT((struct svalue *));
int strpref PROT((char *, char *));
extern int do_rename PROT((char *, char *));     
extern void event PROT((struct svalue *, char *, int, struct svalue *));
extern int file_length PROT((char *));
extern struct vector *actions_defined PROT((struct object *, struct object *,
int));
extern int remove_action PROT((char *, struct object *));

extern int T_flag;
extern char *last_verb;
struct program *current_prog;
extern int current_time;
extern struct object *current_heart_beat;

/*
 * Inheritance:
 * An object X can inherit from another object Y. This is done with
 * the statement 'inherit "file";'
 * The inherit statement will clone a copy of that file, call reset
 * in it, and set a pointer to Y from X.
 * Y has to be removed from the linked list of all objects.
 * All variables declared by Y will be copied to X, so that X has access
 * to them.
 *
 * If Y isn't loaded when it is needed, X will be discarded, and Y will be
 * loaded separetly. X will then be reloaded again.
 */
extern int d_flag;
extern int eval_cost,max_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. */
struct svalue *fp;	/* Pointer to first argument. */
struct svalue *sp;		/* Points to value of last push. */
static int function_index_offset; /* Needed for inheritance */
static int variable_index_offset; /* Needed for inheritance */

struct svalue start_of_stack[EVALUATOR_STACK_SIZE];
struct svalue catch_value={ T_NUMBER };	/* Used to throw an error to a catch */
typedef struct svalue *svaluep;
static svaluep mark_stack[EVALUATOR_STACK_SIZE]; /* Yet Another Stack */
static svaluep *mark_sp;

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

/*
 * 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_zero() { sp++; SET_TO_ZERO(*sp); }
INLINE void push_one() { sp++; SET_TO_ONE(*sp); }
INLINE void push_conditional(int f)
{ if(f) push_one(); else push_zero(); }

INLINE void push_object(struct object *ob)
{
  if(ob->flags & O_DESTRUCTED)
  {
    push_zero(); /* Try pushing something destructed now! Profezzorn */
  }else{  
    sp++;
    if (sp == &start_of_stack[EVALUATOR_STACK_SIZE])
	fatal("stack overflow\n");
    sp->type = T_OBJECT;
    sp->u.ob = ob;
    add_ref(ob, "push_object");
  }
}

/*
 * 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->subtype=NUMBER_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.fnum = f;
}

/*
 * Push a string on the value stack.
 */
INLINE void push_string(char *p)
{
  sp++;
  if (sp == &start_of_stack[EVALUATOR_STACK_SIZE])
    fatal("stack overflow\n");
  SET_STR(sp,make_shared_string(p));
}

/*
 * 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
#ifdef MALLOC_DEBUG
  check_sfltable();
#endif
  *to = *from;
  switch(from->type)
  {
  case T_FUNCTION:
    if(to->u.ob->flags & O_DESTRUCTED)
    {
      SET_TO_ZERO(*to);
      to->subtype=NUMBER_DESTRUCTED_FUNCTION;
    }else{
      add_ref(to->u.ob, "ass to var (function)");
    }
    break;

  case T_OBJECT:
    if(to->u.ob->flags & O_DESTRUCTED)
    {
      SET_TO_ZERO(*to);
      to->subtype=NUMBER_DESTRUCTED_OBJECT;
    }else{
      add_ref(to->u.ob, "ass to var");
    }
    break;

  case T_STRING:
  case T_REGEXP:
  case T_LIST:
  case T_POINTER:
  case T_MAPPING:
  case T_ALIST_PART:
    to->u.ref[0]++;
    break;
  }
}

INLINE void assign_short_svalue_no_free(union storage_union *to,
					struct svalue *from,
					unsigned short type)
{
#ifdef DEBUG
  if (from == 0)
    fatal("Null pointer to assign_svalue().\n");
#endif
#ifdef MALLOC_DEBUG
  check_sfltable();
#endif
  if(type==T_ANY)
  {
    assign_svalue_no_free((struct svalue *)to,from);
  }else if(from->type!=type)
  {
    if(IS_ZERO(from))
    {
      free_short_svalue(to,type);
      to->number=0;
    }else{
      error("Type error.\n");
    }
  }else{
    switch(type)
    {
    case T_OBJECT:
      *to = from->u;
      if(to->ob)
      {
	if(to->ob->flags & O_DESTRUCTED)
	  to->ob=0;
	else
	  add_ref(to->ob, "ass to var");
      }
      break;

    case T_STRING:
    case T_REGEXP:
    case T_LIST:
    case T_POINTER:
    case T_MAPPING:
    case T_ALIST_PART:
      *to = from->u;
      if(to->ref) to->ref[0]++;
      break;

    case T_FUNCTION:
      fatal("Cannot fit functionpointer into short svalue.\n");

    case T_NUMBER:
    case T_FLOAT:
      *to = from->u;
    }
  }
}

INLINE void free_short_svalue(union storage_union *u,unsigned short type)
{
  if(type==T_ANY)
  {
    free_svalue((struct svalue *)u);
  }else if(u->number){
    if(!u->number) return;
    switch(type)
    {
    case T_STRING:
      free_string(strptr2(*u));
      u->string=0;
      break;

    case T_FUNCTION:
    case T_OBJECT:
    {
      struct object *o;
      o=u->ob;
      u->ob=0;
      free_object(o, "free_short_svalue");
      return;
    }

    case T_ALIST_PART:
    case T_POINTER:
    case T_LIST:
    case T_MAPPING:
    {
      struct vector *vv;
      vv=u->vec;
      u->vec=0;
      free_vector(vv);
      break;
    }

    case T_REGEXP:
      if(!--(u->regexp->ref))
      {
	free(u->regexp->str);
	free((char *)u->regexp);
      }
      u->regexp=0;
      break;

#ifdef DEBUG
    case T_NOTHING:
    case T_NUMBER:
    case T_FLOAT:
      break;

    default:
      fatal("Unknown type in free svalue.\n");
#endif
    }
  }
}


INLINE void assign_short_svalue(union storage_union *to,
				struct svalue *from,
				unsigned short type)
{
  free_short_svalue(to,type);
  assign_short_svalue_no_free(to,from,type);
}

INLINE void assign_svalue_from_short_no_free(struct svalue *to,
				     union storage_union *from,
				     unsigned short type)
{
  struct svalue tmp;
  tmp.subtype=0;
  tmp.u=*from;
  if(tmp.u.ob)
    tmp.type=type;
  else
    tmp.type=T_NUMBER;
  assign_svalue_no_free(to,&tmp);
}

extern struct lvalue lvalues[LVALUES];

void init_stacks()
{
  int e;
  for(e=0;e<NELEM(start_of_stack);e++) SET_TO_ZERO(start_of_stack[e]);
  for(e=0;e<NELEM(lvalues);e++) SET_TO_ZERO(lvalues[e].ind);
}

INLINE void free_lvalue(struct lvalue *l)
{
  extern struct lvalue *lsp;
#ifdef DEBUG
  if(lsp+1<lvalues || l<lvalues)
    fatal("Arglebargle glop-glyf?\n");
#endif
  lsp=l-1;
  free_svalue(&(l->ind));
  switch(l->type)
  {
  case LVALUE_INDEX:
    free_lvalue(l-1);
  }
}

/*
 * Free the data that an svalue is pointing to. Not the svalue
 * itself.
 */
INLINE void free_svalue(struct svalue *v)
{
#ifdef DEBUG
  extern struct lvalue *lsp;

  if(v>sp && v<&start_of_stack[EVALUATOR_STACK_SIZE])
    fatal("Free svalue above stackpointer.\n");
#endif

  switch(v->type)
  {
  case T_LVALUE:
#ifdef DEBUG
    if(v->u.lvalue>lsp)
      fatal("Arglebargle glop-glyf!!\n");
#endif
    free_lvalue(v->u.lvalue);
    v->type=T_INT;
    break;

  case T_STRING:
    free_string(strptr(v));
    v->type=T_INT;
    break;

  case T_FUNCTION:
  case T_OBJECT:
  {
    struct object *o;
    /* note that free_object() can call destruct, which
       will call remove_object from stack, which will
       call free_svalue */
    o=v->u.ob;
    v->type=T_INT;
    free_object(o, "free_svalue");
    return;
  }
  case T_ALIST_PART:
  case T_POINTER:
  case T_LIST:
  case T_MAPPING:
  {
    struct vector *vv;
    vv=v->u.vec;
    v->type=T_INT;
    free_vector(vv);
    break;
  }

  case T_REGEXP:
    if(!--(v->u.regexp->ref))
    {
      free(v->u.regexp->str);
      free((char *)v->u.regexp);
    }
    v->type=T_INT;
    break;

#ifdef DEBUG
  case T_NOTHING:
  case T_NUMBER:
  case T_FLOAT:
    break;

  default:
    fatal("Unknown type in free svalue.\n");
#endif
  }
}

void free_svalues(struct svalue *s,int num)
{
  num++;
  while(--num>0) free_svalue(s++);
}

void copy_svalues_no_free(struct svalue *to,struct svalue *from,int num)
{
  num++;
  while(--num>0) assign_svalue_no_free(to++,from++);
}

#ifndef DEBUG

void copy_svalues_raw(struct svalue *to,struct svalue *from, int num)
{
  unsigned int a,b;
  int *c;
  a=MAX_REF_TYPE<<16;
  num++;
  while(--num>0)
  {
    *(((int *)to)++)=b=*(((int *)from)++);
    *(((int **)to)++)=c=*(((int **)from)++);
    if(b<=a) c[0]++;
  }
}
#else

void assign_svalue_raw(struct svalue *to,struct svalue *from)
{
  assign_svalue_no_free(to,from);
}

void copy_svalues_raw(struct svalue *to,struct svalue *from, int num)
{
  copy_svalues_no_free(to,from,num);
}
#endif

INLINE void move_svalue(struct svalue *to,struct svalue *from)
{
  free_svalue(to);
  *to=*from;
  SET_TO_ZERO(*from);
}

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

/*
 * 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 MALLOC_DEBUG
  check_sfltable();
#endif
#ifdef DEBUG
  if (sp < start_of_stack)
    fatal("Stack underflow.\n");
#endif
  free_svalue(sp);
  sp--;
}

INLINE void pop_push_conditional(int f)
{ 
  pop_stack();
  if(f) push_one(); else push_zero(); 
}

/*
 * 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->u.vec = v;
  sp->type = T_POINTER;
}

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

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

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

INLINE void push_shared_string(char *p)
{
#ifdef DEBUG
  if(p!=debug_findstring(p))
    fatal("push_shared_string on nonshared string.\n");
#endif
  sp++;
  SET_STR(sp,p);
}

INLINE void push_new_shared_string(char *p)
{
  push_shared_string(make_shared_string(p));
}


/*
 * 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 instruction)
{
  error("Bad argument %d to %s()\n",arg, get_instruction_name(instruction));
}

INLINE void push_control_stack(struct function *funp)
{
  if (csp == &control_stack[MAX_TRACE-1])
    error("Too deep recursion.\n");
  csp++;
  csp->funp = funp;		/* Only used for tracebacks */
  csp->ob = current_object;
  csp->comgiver=command_giver;
  csp->fp = fp;
  csp->prog = current_prog;
  csp->pc = pc;
  csp->function_index_offset = function_index_offset;
  csp->variable_index_offset = variable_index_offset;
}

/*
 * Pop the control stack one element, and restore registers.
 */
INLINE void pop_control_stack()
{
#if 0
  struct object *pobj;
  pobj=current_object;
  current_object = csp->ob;
  if(current_object && current_object!=pobj && 
     csp!=control_stack && current_object->flags & O_DESTRUCTED)
    error("Returning to destructed object.\n");
#else
#ifdef DEBUG
  if (csp == control_stack - 1)
    fatal("Popped out of the control stack");
#endif
  current_object = csp->ob;
#endif
  current_prog = csp->prog;
  command_giver=csp->comgiver;
  pc = csp->pc;
  fp = csp->fp;
  function_index_offset = csp->function_index_offset;
  variable_index_offset = csp->variable_index_offset;
  if(csp->va_args) free_vector(csp->va_args);
  csp--;
}

/*
 * Argument is the function to execute. If it is defined by inheritance,
 * then search for the real definition, and return it.
 * There is a number of arguments on the stack. Normalize them and initialize
 * local variables, so that the called function is pleased.
 */
static struct function *setup_new_frame(struct function_p *funp,
			    struct program *prog)
{
  struct function *fun;
  function_index_offset = prog->inherit[funp->prog].function_index_offset;
  variable_index_offset = prog->inherit[funp->prog].variable_index_offset;
  current_prog=prog->inherit[funp->prog].prog;
  fun=current_prog->functions+funp->fun;

  csp->func=funp-prog->function_ptrs;

  /* Remove excessive arguments */
  csp->num_of_arguments=csp->num_local_variables;
  if(funp->type & TYPE_MOD_VA_ARGS)
  {
    int a;
    a=csp->num_local_variables - fun->num_arg;
    csp->num_local_variables-=a;
    if(a<0) a=0;
    csp->va_args=allocate_array_no_init(a,0);
    while(a) 
    {
      csp->va_args->item[--a]=*sp;
      sp--;
    }
  }else{
    while(csp->num_local_variables > fun->num_arg)
    {
      pop_stack();
      csp->num_local_variables--;
    }
    csp->va_args=NULL;
  }
  /* Correct number of arguments and local variables */
  while(csp->num_local_variables < fun->num_arg + fun->num_local)
  {
    push_zero();
    csp->num_local_variables++;
  }
  fp = sp - csp->num_local_variables + 1;
  return fun;
}

void f_break_point(int num_arg,struct svalue *argp)
{
#if 0
  if (sp - fp - csp->num_local_variables + 1 != 0)
    fatal("Bad stack pointer.\n");
#else
  if (sp < fp - csp->num_local_variables + 1)
    fatal("Bad stack pointer.\n");
#endif
}

/* 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 svalue **mark_sp;
  } *ecsp = 0, *p;

  if (push == 1)
  {
    /*
     * Save some global variables that must be restored separately
     * after a longjmp. The stack will have to be manually popped all
     * the way.
     */
    p = (struct error_context_stack *)xalloc (sizeof *p);
    p->save_sp = sp;
    p->save_csp = csp;	
    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;
    p->mark_sp=mark_sp;
    ecsp = p;
  } else {
    p = ecsp;
    if (p == 0)
      fatal("Catch: error context stack underflow");
    if (push == 0) {
#ifdef DEBUG
      if(p->mark_sp!=mark_sp)
	fatal("Catch: mark sp }t finsp}ng.\n");

      if (csp != p->save_csp-1)
	fatal("Catch: Lost track of csp");
#endif
    } else {
      /* push == -1 !
       * They did a throw() or error. That means that the control
       * stack must be restored manually here.
       */
      while(csp>p->save_csp)
	pop_control_stack();

      pop_n_elems (sp - p->save_sp);
    }
    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;
    mark_sp=p->mark_sp;
    free ((char *)p);
  }
}

INLINE void check_arg(int e,struct svalue *ss,int i)
{
  struct instr *d;
  struct svalue *sv;
  sv=ss+e-1;
  d=&(instrs[i]);
  if (d->type[e-1] != 0 && !IS_TYPE(*sv,d->type[e-1]))
  {
     bad_arg(e,i+F_OFFSET);
  }
}

struct processing
{
  struct processing *next;
  struct vector *v;
  struct svalue *s;
};

void low_copy_svalue(struct svalue *dest,struct svalue *src,struct processing *p)
{
  *dest=*src;
  eval_cost++;
  switch(dest->type)
  {
    case T_NUMBER:
    case T_FLOAT:
      break;

    case T_POINTER:
    case T_MAPPING:
    case T_LIST:
    case T_ALIST_PART:
    {
      struct processing curr;
      int e;
      struct vector *vs,*vd;

      curr.next=p;
      curr.v=vs=src->u.vec;
      curr.s=dest;

      for(;p;p=p->next)
      {
	if(vs==p->v)
	{
	  assign_svalue(dest,p->s);
	  return;
	}
      }

      vd=dest->u.vec=allocate_array_no_init(vs->size,0);
      for(e=0;e<vs->size;e++)
        low_copy_svalue(vd->item+e,vs->item+e,&curr);
      break;
    }

    case T_STRING:
      dest->u.string=BASE(copy_shared_string(strptr(dest)));
      break;

    case T_REGEXP:
      dest->u.regexp->ref++;
      break;

    case T_OBJECT:
    case T_FUNCTION:
     dest->u.ob->ref++;
     break;
  }
}

void copy_svalue(struct svalue *dest,struct svalue *src)
{
  low_copy_svalue(dest,src,0);
}


INLINE void check_eval_cost()
{
  extern void check_signals();
  extern unsigned long signals;
  if (eval_cost > max_eval_cost)
  {
    if(batch_mode)
    {
      max_eval_cost=0x7fffffff;
      return; /* who cares? */
    }
    fprintf(stderr,"eval_cost too big %d\n", eval_cost);
    error("Too long evaluation. Execution aborted.\n");
  }
  if(signals) check_signals();
}


/*
 * Evaluate instructions at address 'p'. All program offsets are
 * to current_prog->program. 'current_prog' must be setup before
 * call of this function.
 *
 * There must not be destructed objects on the stack. The destruct_object()
 * function will automatically remove all occurences. The effect is that
 * all called efuns knows that they won't have destructed objects as
 * arguments.
 */
#ifdef DEBUG
#define BACKLOG 200
static int previous_instruction[BACKLOG];
static int previous_instruction_offset[BACKLOG];
static int stack_size[BACKLOG];
static char *previous_pc[BACKLOG];
static unsigned int last;
#endif

#define CASE(X) case X-F_OFFSET: eval_cost+=instrs[X-F_OFFSET].eval_cost;

#ifdef HANDLES_UNALIGNED_MEMORY_ACCESS
#define EXTRACT_UWORD(X) (*((unsigned short *)(X)))
#define EXTRACT_WORD(X) (*((short *)(X)))
#define EXTRACT_UINT(X) (*((unsigned int *)(X)))
#define EXTRACT_INT(X) (*((int *)(X)))
#else
#define EXTRACT_UWORD(X) ((EXTRACT_UCHAR(X)<<8) | (EXTRACT_UCHAR((X)+1)))
#define EXTRACT_WORD(X) ((short)EXTRACT_UWORD(X))
#define EXTRACT_UINT(X) ((EXTRACT_UWORD(X)<<16) | (EXTRACT_UWORD((X)+2)))
#define EXTRACT_INT(X) ((int)EXTRACT_UINT(X))
#endif

#define get_relative_offset() (tmp2=EXTRACT_INT(pc),pc+=sizeof(int),pc+tmp2-sizeof(int))

void eval_instruction(char *p)
{
  extern struct lvalue lvalues[LVALUES];
  extern struct svalue *fp;
  extern struct lvalue *lsp;

  int num_arg,tmp2;
  unsigned short tmp;
  unsigned int opcode;
  struct svalue *argp;

#ifdef DEBUG
  struct svalue *expected_stack;
#endif

  for(pc=p;;)
  {
#ifdef MALLOC_DEBUG
    check_sfltable();
#endif
    opcode=EXTRACT_UCHAR(pc);
    pc++;

  da_switch:
#ifdef DEBUG
    previous_instruction[last] = opcode + F_OFFSET;
    previous_instruction_offset[last] = pc - current_prog->program;
    previous_pc[last] = pc-1;
    stack_size[last] = sp - fp - csp->num_local_variables;
    last = (last + 1) % (sizeof previous_instruction / sizeof (int));
#ifdef WARN    
    sp[1].type=2000;
    sp[2].type=2000;
    sp[3].type=2000;
    sp[4].type=2000;
    sp[5].type=2000;
    sp[6].type=2000;
    sp[7].type=2000;
#endif
#endif

#ifdef OPCPROF
    check_cost_for_instr(opcode);
#endif
    switch(opcode)
    {
      CASE(F_ADD_256);
      opcode=EXTRACT_UCHAR(pc)+256;
      pc++;
      goto da_switch;

      CASE(F_ADD_512);
      opcode=EXTRACT_UCHAR(pc)+512;
      pc++;
      goto da_switch;

      CASE(F_ADD_768);
      opcode=EXTRACT_UCHAR(pc)+768;
      pc++;
      goto da_switch;

      CASE(F_ADD_1024);
      opcode=EXTRACT_UCHAR(pc)+1024;
      pc++;
      goto da_switch;

      CASE(F_ADD_256X);
      opcode=EXTRACT_UWORD(pc);
      pc+=sizeof(short);
      goto da_switch;

      CASE(F_WRITE_OPCODE);
      opcode=EXTRACT_UCHAR(pc);
      pc++;
    da_switch2:
      switch(opcode)
      {
	CASE(F_ADD_256);
	opcode=EXTRACT_UCHAR(pc)+256;
	pc++;
	goto da_switch2;

	CASE(F_ADD_512);
	opcode=EXTRACT_UCHAR(pc)+512;
	pc++;
	goto da_switch2;

	CASE(F_ADD_768);
	opcode=EXTRACT_UCHAR(pc)+768;
	pc++;
	goto da_switch2;

	CASE(F_ADD_1024);
	opcode=EXTRACT_UCHAR(pc)+1024;
	pc++;
	goto da_switch2;

	CASE(F_ADD_256X);
        opcode=EXTRACT_UWORD(pc);
        pc+=sizeof(short);
	goto da_switch2;
      }
      if(T_flag>2)
      {
	putc('%',stderr);
	for(tmp2=-1;tmp2<csp-control_stack;tmp2++) putc(' ',stderr);
	fprintf(stderr,"%s\n",get_instruction_name(opcode+F_OFFSET));
      }
      goto da_switch;
      
    default:
      if(opcode<F_MAX_OPCODE-F_OFFSET)
      {
	eval_cost+=instrs[opcode].eval_cost;
	if(instrs[opcode].efunc)
	{
	  if(instrs[opcode].min_arg != instrs[opcode].max_arg)
	  {
	    argp=*(mark_sp--);
	    num_arg=sp-argp;
	    argp++;
	    if (num_arg > 0)
	    {
	      check_arg(1,argp,opcode);
	      if (num_arg > 1)
		check_arg(2,argp,opcode);
	    }
	  } else {
	    num_arg = instrs[opcode].min_arg;
	    argp= sp - num_arg + 1;
	    if (instrs[opcode].min_arg > 0)
	    {
	      check_arg(1,argp,opcode);
	      if (instrs[opcode].min_arg > 1)
		check_arg(2,argp,opcode);
	    }
	  }

	  if(num_arg<instrs[opcode].min_arg)
	  {
	    error("Too few arguments to %s.\n",
		  get_instruction_name(F_OFFSET+opcode));
	  }

#ifdef DEBUG
	  /* Support the actual void /profezzorn */
	  if((instrs[opcode].ret_type & TYPE_MOD_MASK)==TYPE_VOID)
	    expected_stack=argp-1;
	  else
	    expected_stack=argp;
#endif

	  if(T_flag>1)
	  {
	    int e;

	    init_buf();
	    putc('%',stderr);
	    for(e=-1;e<csp-control_stack;e++)
	      putc(' ',stderr);
	    my_strcat(get_instruction_name(opcode+F_OFFSET));
	    my_putchar('(');
	    e=num_arg>-1?num_arg:instrs[opcode].min_arg;
	    save_style=SAVE_AS_ONE_LINE;
	    for(e=-e+1;e<=0;e++)
	    {
	      save_svalue(sp+e);
	      if(e) my_putchar(',');
	    }
	    my_putchar(')');
	    fprintf(stderr,"%s\n",return_buf());
	  }

	  /*
	   * Execute the opcodes. The number of arguments are correct,
	   * and the type of the two first arguments are also correct.
	   */

	  (instrs[opcode].efunc)(num_arg,argp);

	  if(T_flag>1)
	  {
	    int e;

	    putc('%',stderr);
	    for(e=-1;e<csp-control_stack;e++)
	      putc(' ',stderr);
	    save_style=SAVE_AS_ONE_LINE;
	    if((instrs[opcode].ret_type & TYPE_MOD_MASK)!=TYPE_VOID)
	    {
	      init_buf();
	      save_svalue(sp);
	      fprintf(stderr,"return: %s\n",return_buf());
	    }
	  }
#ifdef DEBUG
	  if ((expected_stack && expected_stack != sp) ||
	      sp < fp + csp->num_local_variables - 1)
	  {
	    fatal("Bad stack after evaluation. Opcode %d, num arg %d\n",
		  opcode+F_OFFSET, num_arg);
	  }
#endif /* DEBUG */
	}else{
	  fatal("Undefined opcode %s (%d)\n",
		get_instruction_name(opcode+F_OFFSET),opcode);
	}
      }else if(opcode-(F_MAX_OPCODE-F_OFFSET)<
	       current_object->prog->num_function_ptrs){
	opcode+=F_OFFSET-F_MAX_OPCODE;
	check_eval_cost();
	argp=*(mark_sp--);
	num_arg=sp-argp;
	argp++;
	apply_lambda_low(current_object,opcode+function_index_offset,num_arg,0);
	if(sp<argp) push_zero();
      }else{
	fatal("Undefined opcode %s (%d)\n",
	      get_instruction_name(opcode+F_OFFSET),opcode);
      }
      break;
      
      CASE(F_ASSIGN); f_assign(); break;
      CASE(F_ASSIGN_AND_POP); f_assign_and_pop(); break;

      CASE(F_NOT);
      if(sp->type==T_NUMBER)
      {
	sp->u.number=!sp->u.number;
      }else{
	free_svalue(sp);
	SET_TO_ZERO(*sp);
      }
      break;

      CASE(F_COMPL);
      if(sp->type!=T_NUMBER) error("Bad argument to ~\n");
      sp->u.number=~sp->u.number;
      break;

      CASE(F_NEGATE);
      if(sp->type==T_NUMBER) sp->u.number=-sp->u.number;
      else if(sp->type==T_FLOAT) sp->u.fnum=-sp->u.fnum;
      else error("Bad argument to unary -\n");
      break;

      CASE(F_EQ); tmp= is_eq(sp-1,sp); pop_n_elems(2); push_number(tmp); break;
      CASE(F_NE); tmp=!is_eq(sp-1,sp); pop_n_elems(2); push_number(tmp); break;
      CASE(F_LT); tmp= is_lt(sp-1,sp); pop_n_elems(2); push_number(tmp); break;
      CASE(F_LE); tmp=!is_gt(sp-1,sp); pop_n_elems(2); push_number(tmp); break;
      CASE(F_GT); tmp= is_gt(sp-1,sp); pop_n_elems(2); push_number(tmp); break;
      CASE(F_GE); tmp=!is_lt(sp-1,sp); pop_n_elems(2); push_number(tmp); break;

      CASE(F_CAST_TO_OBJECT); f_cast_to_object(); break;
      CASE(F_CAST_TO_STRING); f_cast_to_string(); break;
      CASE(F_CAST_TO_INT); f_cast_to_int(); break;
      CASE(F_CAST_TO_FLOAT); f_cast_to_float(); break;
      CASE(F_CAST_TO_FUNCTION); f_cast_to_function(); break;
      CASE(F_CAST_TO_REGEXP); f_cast_to_regexp(); break;

      CASE(F_RSH);
      if (sp->type != T_NUMBER) bad_arg(2,F_RSH);
      sp--;
      if (sp->type != T_NUMBER) bad_arg(1,F_RSH);
      sp->u.number = sp[0].u.number >> sp[1].u.number;
      break;

      CASE(F_LSH);
      if (sp->type != T_NUMBER) bad_arg(2,F_LSH);
      sp--;
      if (sp->type != T_NUMBER) bad_arg(1,F_LSH);
      sp->u.number = sp[0].u.number << sp[1].u.number;
      break;

      CASE(F_MULTIPLY); f_multiply(); break;
      CASE(F_DIVIDE); f_divide(); break;
      CASE(F_MOD); f_mod(); break;
      CASE(F_SUBTRACT); f_subtract(); break;
      CASE(F_OR); f_or(); break;
      CASE(F_AND); f_and(); break;
      CASE(F_XOR); f_xor(); break;

      CASE(F_INC_AND_POP); f_inc_and_pop(); break;
      CASE(F_DEC_AND_POP); f_dec_and_pop(); break;
      CASE(F_POST_DEC); f_post_dec(); break;
      CASE(F_POST_INC); f_post_inc(); break;
      CASE(F_INC); f_inc(); break;
      CASE(F_DEC); f_dec(); break;

      CASE(F_PUSH_ARRAY); f_push_array(); break;
      CASE(F_SWAP_VARIABLES); f_swap_variables(); break;
      CASE(F_SWAP); f_swap(); break;
      CASE(F_INDIRECT); f_indirect(); break;

      CASE(F_PUSH_INDEXED_LVALUE)
      {
	struct lvalue *l;
	l=push_indexed_lvalue(sp-1,sp);
	pop_stack();
	sp->type=T_LVALUE;
	sp->u.lvalue=l;
	break;
      }

      CASE(F_INDEX)
      {
	struct svalue s;
	struct lvalue *l;
	l=push_indexed_lvalue(sp-1,sp);
	lvalue_to_svalue_no_free(&s,l);
	free_lvalue(l);
	pop_stack();
	pop_stack();
	sp++;
	*sp=s;
	break;
      }

      CASE(F_ADD_INT);
      if(sp[0].type==T_NUMBER || sp[-1].type==T_NUMBER)
      {
	sp--;
	sp[0].u.number+=sp[1].u.number;
      }else{
	f_sum(2,sp-1);
      }
      break;

      CASE(F_ADD);
      if(sp[0].type==sp[-1].type)
      {
	switch(sp[0].type)
	{
	case T_NUMBER: 
	  sp--;
	  sp[0].u.number+=sp[1].u.number;
	  break;
	case T_FLOAT:
	  sp--;
	  sp[0].u.fnum+=sp[1].u.fnum;
	  break;
	default:
	  f_sum(2,sp-1);
	}
      }else{
	f_sum(2,sp-1);
      }
      break;

      CASE(F_PUSH_LTOSVAL);
      if(sp->type!=T_LVALUE) error("RHS: not lvalue!\n");
      sp++;
      lvalue_to_svalue_no_free(sp,sp[-1].u.lvalue);
      break;

      CASE(F_PUSH_LTOSVAL2);
      if(sp[-1].type!=T_LVALUE)	error("RHS: not lvalue!\n");
      sp[1]=*sp;
      lvalue_to_svalue_no_free(sp,sp[-1].u.lvalue);
      sp++;
      /* this will make some things faster */
      if(sp[-2].u.lvalue->ptr.sval)
      {
	if(sp[-2].u.lvalue->rttype==T_ANY &&
	   sp[-2].u.lvalue->ptr.sval->type==T_POINTER)
	  free_svalue(sp[-2].u.lvalue->ptr.sval);
	else
	  if(sp[-2].u.lvalue->rttype==T_POINTER)
	    free_short_svalue(sp[-2].u.lvalue->ptr.uval,T_POINTER);
      }
      break;

      CASE(F_CATCH)
      {
	/*
	 * Catch/Throw errors in system or other peoples routines.
	 */
	extern jmp_buf error_recovery_context;
	extern int error_recovery_context_exists;
	extern struct svalue catch_value;
	char *new_pc;
	struct svalue *save_sp;

	new_pc=get_relative_offset();

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

	  free_svalue(&catch_value);
	  SET_TO_ONE(catch_value);
	  eval_instruction(pc);
	  pop_control_stack();
	  push_pop_error_context(0);
	  while(sp>save_sp) pop_stack();
	  push_zero();
	}
	break;
      }

      CASE(F_STRING);
      push_shared_string(copy_shared_string(current_prog->strings[EXTRACT_UWORD(pc)]));
      pc+=sizeof(short);
      break;

      CASE(F_SHORT_STRING);
      push_shared_string(copy_shared_string(current_prog->strings[EXTRACT_UCHAR(pc)]));
      pc++;
      break;

      CASE(F_CONST0); push_zero(); break;
      CASE(F_CONST1); push_one(); break;
      CASE(F_CONST_1); push_number(-1); break;
      CASE(F_BYTE);
      push_number(EXTRACT_UCHAR(pc)+2);
      pc++;
      break;

      CASE(F_NEG_BYTE);
      push_number(-EXTRACT_UCHAR(pc)-2);
      pc++;
      break;

      CASE(F_SHORT);
      push_number(EXTRACT_WORD(pc));
      pc+=sizeof(short);
      break;

      CASE(F_NUMBER)
 	push_number(EXTRACT_INT(pc));
	pc+=sizeof(int);
	break;
      
      CASE(F_POP_N_ELEMS); pop_n_elems(EXTRACT_UCHAR(pc)); pc++; break;
      CASE(F_PUSH_COST); push_number(eval_cost); break;
      CASE(F_POP_VALUE); pop_stack(); break;
      CASE(F_MARK); *++mark_sp=sp; break;

      CASE(F_SSCANF)
      {
	int i;
	num_arg = EXTRACT_UCHAR(pc);
	pc++;
      
	i = inter_sscanf(num_arg);
	pop_n_elems(num_arg);
	push_number(i);
	break;
      }

      CASE(F_SWITCH)
      { 
	/* Ta-da, and the wonderful wizard of oz waved his wand, */ 
	/* and all amylaars code was gone.        /Profezzorn    */
	struct vector *m,*val,*ind;

	tmp=EXTRACT_UWORD(pc);
	pc+=sizeof(short);
	m=current_prog->switch_mappings[tmp];
	ind=m->item[0].u.vec;
	val=m->item[1].u.vec;

	tmp=search_alist(sp,ind);
	if(tmp<ind->size &&
	   (!alist_cmp(ind->item+tmp,sp) || val->item[tmp].subtype==2))
	{
	  pc=current_prog->program+val->item[tmp].u.number;
	}else{
	  pc=current_prog->program+m->item[2].u.number;
	}
	pop_stack();
	break;
      }

      CASE(F_ASSIGN_LOCAL);
      assign_svalue(fp+EXTRACT_UCHAR(pc),sp);
      pc++;
      break;

      CASE(F_ASSIGN_LOCAL_AND_POP);
      assign_svalue(fp+EXTRACT_UCHAR(pc),sp);
      pc++;
      pop_stack();
      break;

      CASE(F_ASSIGN_GLOBAL);
      tmp=EXTRACT_UCHAR(pc)+variable_index_offset;
#ifdef DEBUG
      if(tmp>current_object->prog->num_variables)
	fatal("Illegal variable access %d.\n",tmp);
#endif
      pc++;
      assign_short_svalue(current_object->variables+tmp,
			  sp,
			  current_object->prog->variable_names[tmp].rttype);
      break;

      CASE(F_ASSIGN_GLOBAL_AND_POP);
      tmp=EXTRACT_UCHAR(pc)+variable_index_offset;
#ifdef DEBUG
      if(tmp>current_object->prog->num_variables)
	fatal("Illegal variable access %d.\n",tmp);
#endif
      pc++;
      assign_short_svalue(current_object->variables+tmp,
			  sp,
			  current_object->prog->variable_names[tmp].rttype);
      pop_stack();
      break;
      
      CASE(F_PUSH_LOCAL_LVALUE);
      lsp++;
      if(lsp>=lvalues+LVALUES)
	error("Lvalue stack overflow.\n");
      lsp->ptr.sval = fp + EXTRACT_UCHAR(pc);
      lsp->type=LVALUE_LOCAL;
      lsp->rttype=T_ANY;
      pc++;
      sp++;
      sp->type = T_LVALUE;
      sp->u.lvalue=lsp;
      break;

      CASE(F_LOCAL);
      assign_svalue_no_free(++sp, fp + EXTRACT_UCHAR(pc));
      pc++;
      break;

      CASE(F_PUSH_GLOBAL_LVALUE);
      tmp=EXTRACT_UCHAR(pc)+variable_index_offset;
#ifdef DEBUG
      if(tmp>current_object->prog->num_variables)
	fatal("Illegal variable access %d.\n",(int)tmp);
#endif
      pc++;
      lsp++;
      if(lsp>=lvalues+LVALUES)	error("Lvalue stack overflow.\n");
      lsp->ptr.sval=(struct svalue *)(current_object->variables+tmp);
      if(current_object->prog->variable_names[tmp].rttype==T_ANY)
      {
	lsp->type=LVALUE_GLOBAL;
	lsp->rttype=T_ANY;
      }else{
	lsp->type=LVALUE_SHORT_GLOBAL;
	lsp->rttype=current_object->prog->variable_names[tmp].rttype;
      }
      sp++;
      sp->type = T_LVALUE;
      sp->u.lvalue=lsp;
      break;

      CASE(F_GLOBAL);
      tmp=EXTRACT_UCHAR(pc)+variable_index_offset;
#ifdef DEBUG
      if(tmp>current_object->prog->num_variables)
	fatal("Illegal variable access %d.\n",(int)tmp);
#endif
      pc++;
      sp++;
      if(current_object->prog->variable_names[tmp].rttype==T_ANY)
      {
	assign_svalue_no_free(sp,(struct svalue *)
			      (current_object->variables+tmp));
      }else{
	assign_svalue_from_short_no_free(sp,
					 current_object->variables+tmp,
					 current_object->prog->variable_names[tmp].rttype);
      }
      break;

      CASE(F_SHORT_BRANCH);
      pc-=EXTRACT_UCHAR(pc);
      check_eval_cost();
      break;

      CASE(F_SHORT_BRANCH_WHEN_NON_ZERO);
      if (sp->type != T_NUMBER || sp->u.number != 0)
	pc-=EXTRACT_UCHAR(pc);
      else
	pc++;
      pop_stack();
      check_eval_cost();
      break;

      CASE(F_SHORT_BRANCH_WHEN_ZERO);
      if (sp->type == T_NUMBER && sp->u.number == 0)
	pc-=EXTRACT_UCHAR(pc);
      else
	pc++;
      pop_stack();
      check_eval_cost();
      break;

      CASE(F_BRANCH_WHEN_NON_ZERO);
      if (sp->type != T_NUMBER || sp->u.number != 0)
	pc=get_relative_offset();
      else
	pc+=4;
      pop_stack();
      check_eval_cost();
      break;

      CASE(F_BRANCH_WHEN_ZERO); 
      if (sp->type == T_NUMBER && sp->u.number == 0)
	pc=get_relative_offset();
      else
	pc+=4;
      pop_stack();
      check_eval_cost();
      break;

      CASE(F_BRANCH);
      pc=get_relative_offset();
      check_eval_cost();
      break;

      CASE(F_PUSH_SIMUL_EFUN);
      tmp=EXTRACT_UWORD(pc);
      pc+=sizeof(short);
      if(simul_efuns[tmp].fun.type==T_FUNCTION)
      {
	if(simul_efuns[tmp].fun.u.ob->flags & O_DESTRUCTED)
	{
	  free_svalue(&(simul_efuns[tmp].fun));
	  push_zero();
	}else{
	  *(++sp)=simul_efuns[tmp].fun;
	  add_ref(sp->u.ob,"Push simul efun");
	}
      }else{
	push_zero();
      }
      break;

      CASE(F_CONSTANT_FUNCTION);
      tmp=EXTRACT_UWORD(pc);
      pc+=sizeof(short);

    push_function:
      if(current_object->flags & O_DESTRUCTED)
      {
	push_zero();
      }else{
	sp++;
	sp->type=T_FUNCTION;
	sp->subtype=tmp+function_index_offset;
	sp->u.ob=current_object;
	add_ref(current_object,"constant_function");
      }
      break;

      CASE(F_SHORT_CONSTANT_FUNCTION);
      tmp=EXTRACT_UCHAR(pc);
      pc++;
      goto push_function;

      CASE(F_LOR); 
      if (sp->type != T_NUMBER || sp->u.number != 0)
      {
	pc=get_relative_offset();
      }else{
	pop_stack();
	pc+=4;
      }
      break;

      CASE(F_LAND);
      if (sp->type == T_NUMBER && sp->u.number == 0)
      {
	pc = get_relative_offset();
      }else{
	pop_stack();
	pc+=4;
      }
      break;

      CASE(F_DUP);
      sp++;
      assign_svalue_raw(sp, sp-1);
      break;

      CASE(F_FLOAT);
      sp++;
      sp->type=T_FLOAT;
      MEMCPY((char *)&(sp->u.fnum),pc,sizeof(float));
      pc+=sizeof(float);
      break;

      CASE(F_CONSTANT);
      tmp=EXTRACT_UWORD(pc);
      pc+=sizeof(short);
      assign_svalue_no_free(++sp,current_prog->constants+tmp);
      break;

      CASE(F_SHORT_CONSTANT);
      assign_svalue_no_free(++sp,current_prog->constants+EXTRACT_UCHAR(pc));
      pc++;
      break;

      CASE(F_FOREACH);
      if((sp-2)->type != T_POINTER) bad_arg(1,F_FOREACH);
      if((sp-1)->type != T_LVALUE) bad_arg(2,F_FOREACH);
      if(sp->type != T_NUMBER) bad_arg(3,F_FOREACH);
      
      if(sp->u.number<(sp-2)->u.vec->size)
      {
	assign((sp-1)->u.lvalue,&(sp-2)->u.vec->item[sp->u.number++]);
	pc=get_relative_offset();
      }else{
	pc+=4;
      }
      check_eval_cost();
      break;

      CASE(F_INC_LOOP)
      {
	int *i;
	if(sp[-1].type != T_NUMBER) bad_arg(1,F_INC_LOOP);
	if(sp->type != T_LVALUE) bad_arg(2,F_INC_LOOP);
	i=lvalue_to_intp(sp->u.lvalue,"++Loop over non-integer.\n");
	if(!i) error("Too complex value for ++loop.\n");
    
	if((++*i)<sp[-1].u.number)
	  pc=get_relative_offset();
	else
	  pc+=4;
	check_eval_cost();
	break;
      }

      CASE(F_DEC_LOOP)
      {
	int *i;
	if(sp[-1].type != T_NUMBER) bad_arg(1,F_DEC_LOOP);
	if(sp->type != T_LVALUE) bad_arg(2,F_DEC_LOOP);
	i=lvalue_to_intp(sp->u.lvalue,"--Loop over non-integer.\n");
	if(!i) error("Too complex value for --loop.\n");
    
	if((--*i)>sp[-1].u.number)
	  pc=get_relative_offset();
	else
	  pc+=4;
	check_eval_cost();
	break;
      }

      CASE(F_INC_NEQ_LOOP)
      {
	int *i;
	if(sp[-1].type != T_NUMBER) bad_arg(1,F_INC_NEQ_LOOP);
	if(sp->type != T_LVALUE) bad_arg(2,F_INC_NEQ_LOOP);
	i=lvalue_to_intp(sp->u.lvalue,"++Loop over non-integer.\n");
	if(!i) error("Too complex value for ++loop.\n");

	if((++*i)!=sp[-1].u.number)
	  pc=get_relative_offset();
	else
	  pc+=4;
	check_eval_cost();
	break;
      }

      CASE(F_DEC_NEQ_LOOP)
      {
	int *i;
	if(sp[-1].type != T_NUMBER) bad_arg(1,F_DEC_NEQ_LOOP);
	if(sp->type != T_LVALUE) bad_arg(2,F_DEC_NEQ_LOOP);
	i=lvalue_to_intp(sp->u.lvalue,"--Loop over non-integer.\n");
	if(!i) error("Too complex value for --loop.\n");

	if((--*i)!=sp[-1].u.number)
	  pc=get_relative_offset();
	else
	  pc+=4;
	check_eval_cost();
	break;
      }

      CASE(F_TAILRECURSE)
      {
	int fun,e,num_arg;
	struct object *ob;
	struct svalue *argp;
	extern struct svalue *fp;
	extern int T_flag;

	argp=*(mark_sp--);
	num_arg=sp-argp;
	argp++;
	fun=argp[0].subtype;

	if(!IS_TYPE(argp[0],BT_NUMBER | BT_FUNCTION | BT_POINTER))
	  bad_arg(1,F_CALL_FUNCTION);
	if(argp[0].type==T_NUMBER || (argp[0].type==T_FUNCTION && fun==-1))
	{
	  while(sp>=fp) pop_stack();
	  push_zero();
	  return;
	}
	
	check_eval_cost();
	ob=argp[0].u.ob;
  
	/* can't tailrecurse here */
	if(argp[0].type!=T_FUNCTION && ob!=current_object)
	{
	  /* in this case we still saved some stack,
	   * and one revolution in the mainloop
	   */
	  /* get rid of local variables, move arguments down the stack */
	  if(fp!=argp)
	    for(e=0;e<num_arg;e++)
	      assign_svalue(fp+e,argp+e);
	  while(fp+num_arg<=sp) pop_stack();

	  f_call_function(num_arg,fp);
	  return;
	}else{
	  struct function_p *pr;
	  struct function *func;

	  num_arg--;
	  argp++;
	  if(fp!=argp)
	    for(e=0;e<num_arg;e++)
	      assign_svalue(fp+e,argp+e);
	  while(fp+num_arg<=sp) pop_stack();

	  pr=ob->prog->function_ptrs+fun;
	  func=ob->prog->inherit[pr->prog].prog->functions+pr->fun;
	  if(T_flag)
	  {
	    int e;
	    init_buf();
	    putc('%',stderr);
	    for(e=-1;e<csp-control_stack;e++) putc(' ',stderr);
	    save_object_desc(ob);
	    my_strcat("->");
	    my_strcat(current_prog->inherit[pr->prog].prog->name);
	    my_strcat("::");
	    my_strcat(func->name);
	    my_putchar('(');
	    save_style=SAVE_AS_ONE_LINE;
	    for(e=-num_arg+1;e<=0;e++)
	    {
	      save_svalue(sp+e);
	      if(e) my_putchar(',');
	    }
	    my_putchar(')');
	    fprintf(stderr,"%s\n",simple_free_buf());
	  }

	  pop_control_stack();
	  push_control_stack(func);
	  csp->num_local_variables = num_arg;
	  setup_new_frame(pr,ob->prog);
	  current_object=ob;
	  pc=current_prog->program+func->offset;
	}
	break;
      }

      CASE(F_RETURN);
      if(sp>fp)
      {
	assign_svalue(fp,sp);
	pop_n_elems(sp-fp);
      }
      /* fall through */
      CASE(F_DUMB_RETURN);
      return;
    }
  }
}

static int ccsave;
#ifdef LACIP
int refused=0;
#endif

int apply_lambda_low(struct object *ob,int fun,int num_arg,int ignorestatic)
{
  struct function_p *pr;
  struct function *func;
  struct program *progp;
  struct control_stack *save_csp;
  int csave=eval_cost;
  int cccsave=ccsave;

#ifdef LACIP
  if(!refused) refusemouse();
  refused=1;
#endif

#ifdef MALLOC_DEBUG
  check_sfltable();
#endif

  if(fun==-1)
  {
    pop_n_elems(num_arg);
    return 0;
  }
  ob->time_of_ref = current_time;
  /*
   * This object will now be used, and is thus a target for
   * reset later on (when time due).
   */
  ob->flags &= ~O_RESET_STATE;

  progp = ob->prog;
#ifdef DEBUG
  if (ob->flags & O_DESTRUCTED)
    fatal("apply_lambda() on destructed object\n");
#endif

  pr=progp->function_ptrs+fun;
  /* Static functions may not be called from outside. */
  /* now they may be called by ROOT (supersture) */
  if(pr->flags & NAME_UNDEFINED)
    error("Calling undefined function.\n");

  if(!ignorestatic &&
     current_object && 
     (pr->type & (TYPE_MOD_STATIC|TYPE_MOD_PRIVATE)) &&
     current_object != ob)
  {
    assert_master_ob_loaded();
    if(!current_object->eff_user || current_object->eff_user!=master_ob->user)
    {
      pop_n_elems(num_arg);
      return 0;
    }
  }
  func=progp->inherit[pr->prog].prog->functions+pr->fun;
  if(T_flag)
  {
    int e;

    init_buf();
    putc('%',stderr);
    for(e=-1;e<csp-control_stack;e++) putc(' ',stderr);
    save_object_desc(ob);
    my_strcat("->");
    my_strcat(progp->inherit[pr->prog].prog->name);
    my_strcat("::");
    my_strcat(func->name);
    my_putchar('(');
    save_style=SAVE_AS_ONE_LINE;
    for(e=-num_arg+1;e<=0;e++)
    {
      save_svalue(sp+e);
      if(e) my_putchar(',');
    }
    my_putchar(')');
    fprintf(stderr,"%s\n",return_buf());
  }
  push_control_stack(func);
  csp->num_local_variables = num_arg;
  current_prog = progp;
  setup_new_frame(pr,progp);
  current_object = ob;
  save_csp = csp;
  eval_instruction(current_prog->program + func->offset);
  pop_control_stack();
#ifdef DEBUG
  if (save_csp-1 != csp)
    fatal("Bad csp after execution in apply_lambda_low\n");
#endif
  csave-=eval_cost+cccsave-ccsave;
  ob->cpu-=csave;
  ccsave-=csave;
  if(T_flag)
  {
    int e;
    save_style=SAVE_AS_ONE_LINE;
    putc('%',stderr);
    for(e=-1;e<csp-control_stack;e++) putc(' ',stderr);
    init_buf();
    save_svalue(sp);
    fprintf(stderr,"return: %s\n",return_buf());
  }
  return 1;
}

struct svalue ret_value = { T_NUMBER };

struct svalue *apply_lfun(int fun,
			  struct object *ob,
			  int num_arg,
			  int ignorestatic)
{
#ifdef DEBUG
    struct svalue *expected_sp;
    expected_sp = sp - num_arg;
    if(ob->flags & O_DESTRUCTED)
      fatal("Destructed object in apply_lfun.\n");
#endif
    if(fun<0 || fun>=num_lfuns)
      error("Number to apply_lfun out of range.\n");

    if(fun>=ob->prog->num_lfuns)
      return apply_shared(lfuns[fun],ob,num_arg,ignorestatic);

#ifdef MALLOC_DEBUG
  check_sfltable();
#endif
    fun=ob->prog->lfuns[fun];
    if(fun<0)
    {
      pop_n_elems(num_arg);
      return 0; /* flash */
    }

    if (apply_lambda_low(ob,fun, num_arg,ignorestatic) == 0)
    {
      fatal("Something went wrong when calling lfun!\n");
    }
    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_numbered_fun(struct object *o,
				  int fun,
				  int num_arg,
				  int ignorestatic)
{
#ifdef DEBUG
    struct svalue *expected_sp;
    expected_sp = sp - num_arg;
#endif
#ifdef MALLOC_DEBUG
  check_sfltable();
#endif

    if(o->flags & O_DESTRUCTED)
      error("Apply_lambda to function fun in destructed object.\n");

    if (apply_lambda_low(o,fun, num_arg,ignorestatic) == 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_lambda(struct svalue *lambda,
			    int num_arg,
			    int ignorestatic)
{
  if(lambda->type!=T_FUNCTION)
    error("Apply_lambda to non-function.\n");

  return apply_numbered_fun(lambda->u.ob,lambda->subtype, num_arg,ignorestatic);
}


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

INLINE static int low_find_shared_string_function(char *name,
						  struct program *prog)
{
  int max,min,tst;
  struct function_p *funcs,*funp;
  struct function *fun;
  unsigned short *funindex;

#ifdef MALLOC_DEBUG
  check_sfltable();
#endif

  if((funindex=prog->funindex))
  {
    funcs=prog->function_ptrs;
    max=prog->num_funindex;
    min=0;
    while(max!=min)
    {
      tst=(max+min)>>1;
      funp=funcs+funindex[tst];
      fun=prog->inherit[funp->prog].prog->functions+funp->fun;
      if(fun->name==name) return funindex[tst];
      if(fun->name>name)
      {
	max=tst;
      }else{
	min=tst+1;
      }
    }
  }else{
    int i,t;
    for(i=0;i<prog->num_function_ptrs;i++)
    {
      funp=prog->function_ptrs+i;
      if(funp->flags & (NAME_HIDDEN|NAME_UNDEFINED)) continue;
      fun=prog->inherit[funp->prog].prog->functions+funp->fun;
      if(fun->name!=name) continue;
      if(funp->flags & NAME_INHERITED)
      {
        if(funp->type & TYPE_MOD_PRIVATE) continue;
	for(t=0;t>=0 && t<prog->num_function_ptrs;t++)
	{
	  struct function_p *funpb;
	  struct function *funb;

	  if(t==i) continue;
	  funpb=prog->function_ptrs+i;
	  funb=prog->inherit[funpb->prog].prog->functions+funpb->fun;

	  if(fun->name==funb->name) t=-10;
	}
	if(t<0) continue;
      }
      return i;
    }
  }
  return -1;
}

#ifdef FIND_FUNCTION_HASHSIZE
struct ff_hash
{
  char *name;
  int id;
  int fun;
};
#endif

INLINE int find_shared_string_function(char *name,struct program *prog)
{
#ifdef FIND_FUNCTION_HASHSIZE
  static struct ff_hash cache[FIND_FUNCTION_HASHSIZE];
  extern struct program fake_prog;
  if(prog!=&fake_prog)
  {
    unsigned int hashval;
    hashval=(unsigned int)name;
    hashval+=prog->id;
    hashval^=(unsigned int)prog;
    hashval-=*name;
    hashval%=FIND_FUNCTION_HASHSIZE;
    if(cache[hashval].name==name && cache[hashval].id==prog->id)
      return cache[hashval].fun;

    cache[hashval].name=name;
    cache[hashval].id=prog->id;
    return cache[hashval].fun=low_find_shared_string_function(name,prog);
  }
#endif /* FIND_FUNCTION_HASHSIZE */

  return low_find_shared_string_function(name,prog);
}

int find_function(char *name,struct program *prog)
{
  name=findstring(name);
  if(!name) return -1;
  return find_shared_string_function(name,prog);
}

/* needs fun to be a shared string */
int apply_lower(char *fun, struct object *ob, int num_arg,int ignorestatic)
{
  int function;

  if (fun[0] == ':')
    error("Illegal function call (colon in function name)\n");

#ifdef DEBUG
  if (ob->flags & O_DESTRUCTED)
    fatal("apply() on destructed object\n");

  if(fun!=debug_findstring(fun))
    fatal("apply_lower on nonshared string\n");
#endif

  function=find_shared_string_function(fun,ob->prog);
#ifdef DEBUG
  if(function>=0)
  {
    struct function_p *funp;
    struct function *func;
    funp=ob->prog->function_ptrs+function;
    func=ob->prog->inherit[funp->prog].prog->functions+funp->fun;
	
    if(strcmp(func->name,fun))
      fatal("I didn't call that!\n");
  }
#endif
  return apply_lambda_low(ob,function,num_arg,ignorestatic);
}

INLINE int apply_low(char *fun, struct object *ob, int num_arg,int ignorestatic)
{
  fun=findstring(fun);
  if(!fun)
  {
    pop_n_elems(num_arg);
    return 0;
  }
  return apply_lower(fun,ob,num_arg,ignorestatic);
}

/*
 * 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 *apply(char *fun,struct object *ob,int num_arg,int ignorestatic)
{
#ifdef DEBUG
  struct svalue *expected_sp;
  expected_sp = sp - num_arg;
#endif
#ifdef MALLOC_DEBUG
  check_sfltable();
#endif
  if(ob->flags & O_DESTRUCTED)
    error("Apply to fun: %s  in destructed object.",fun);
  if (apply_low(fun, ob, num_arg,ignorestatic) == 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_shared(char *fun,
			    struct object *ob,
			    int num_arg,
			    int ignorestatic)
{
#ifdef DEBUG
  struct svalue *expected_sp;
  expected_sp = sp - num_arg;
#endif
#ifdef MALLOC_DEBUG
  check_sfltable();
#endif
  if(!fun) return 0;
  if(ob->flags & O_DESTRUCTED)
    error("Apply to fun: %s  in destructed object.",fun);
  if (apply_lower(fun, ob, num_arg,ignorestatic) == 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;
}

/*
 * this is a "safe" version of apply
 * this allows you to have dangerous driver mudlib dependencies
 * and not have to worry about causing serious bugs when errors occur in the
 * applied function and the driver depends on being able to do something
 * after the apply. (such as the ed exit function, and the net_dead function)
 */

int safe_apply_lambda_low(struct object *ob,
			  int fun,
			  int num_arg)
{
  extern jmp_buf error_recovery_context;
  extern int error_recovery_context_exists;
  VOLATILE int ret;
  struct svalue *save_sp;

  debug(32768, ("safe_apply: before sp = %d\n", sp));
  ret = 0;

  save_sp=sp-num_arg;

  push_control_stack(0);
  sp-=num_arg; /* fool push_pop_error_context */
  push_pop_error_context(1);
  sp+=num_arg;
  error_recovery_context_exists=3;
  if (!setjmp(error_recovery_context))
  {
    if(!(ob->flags & O_DESTRUCTED))
      ret = apply_lambda_low(ob,fun,num_arg,1);
    pop_control_stack();
    push_pop_error_context(0);
  }else{
    ret = 0;
    push_pop_error_context(-1);
    pop_control_stack();

    fprintf(stderr,"Error within safe apply call.\n");
    if(catch_value.type==T_STRING)
      fprintf(stderr,strptr(&catch_value));

  }
  debug(32768, ("safe_apply: after sp = %d\n", sp));
  return ret;
}

struct svalue *safe_apply_lambda(struct svalue *lambda,int num_arg)
{
  int ret;

  if (lambda->type!=T_FUNCTION)
  {
    fprintf(stderr,"safe_apply_lambda() on non-function\n");
    pop_n_elems(num_arg);
    return NULL;
  }
  if (lambda->u.ob->flags & O_DESTRUCTED)
  {
    fprintf(stderr,"safe_apply_lambda() on destructed object\n");
    pop_n_elems(num_arg);
    return NULL;
  }

  ret=safe_apply_lambda_low(lambda->u.ob,lambda->subtype,num_arg);
  if (!ret) return 0;
  assign_svalue(&ret_value, sp);
  pop_stack();
  return &ret_value;
}

struct svalue *safe_apply(char *fun,struct object *ob,int num_arg)
{
  int function,ret;

  if (fun[0] == ':')
  {
    fprintf(stderr,"Illegal safe_apply call (colon in function name)\n");
    pop_n_elems(num_arg);
    return NULL;
  }

  if (ob->flags & O_DESTRUCTED)
  {
    fprintf(stderr,"safe_apply() on destructed object\n");
    pop_n_elems(num_arg);
    return NULL;
  }

  function=find_function(fun,ob->prog);
#ifdef DEBUG
  if(function>=0 && strcmp(ob->prog->functions[function].name,fun))
  {
    fatal("I didn't call that!\n");
  }
#endif
  ret=safe_apply_lambda_low(ob,function,num_arg);
  if (!ret) return 0;
  assign_svalue(&ret_value, sp);
  pop_stack();
  return &ret_value;
}

char *function_exists_in_prog(char *fun,struct program *prog)
{
  int function;
  struct function_p *funp;
  struct function *func;

  function=find_function(fun,prog);
  if(function==-1)
  {
    return 0;
  }else{
    funp=prog->function_ptrs+function;
    func=prog->inherit[funp->prog].prog->functions+funp->fun;
    return func->name;
  }
}

char *function_exists(char *fun, struct object *ob)
{
#ifdef DEBUG
  if (ob->flags & O_DESTRUCTED)
    fatal("function_exists() on destructed object\n");
#endif
  return function_exists_in_prog(fun,ob->prog);
}


static int get_small_number(signed char **q)
{
  int ret;
  switch(ret=(*q)++[0])
  {
  case -127:
    ret=EXTRACT_WORD(*q);
    *q+=2;
    return ret;

  case -128:
    ret=EXTRACT_INT(*q);
    *q+=4;
    return ret;

  default:
    return ret;
  }
}


static int get_line_number(char *pc,struct program *prog)
{
  int off,line,offset;
  signed char *cnt;

  if (prog == 0) return 0;
  offset = pc - prog->program;

#ifdef DEBUG
  if (offset > prog->program_size || offset<0)
    fatal("Illegal offset %d in object %s\n", offset, prog->name);
#endif

  cnt=prog->line_numbers;
  off=line=0;

  while(cnt < prog->line_numbers + prog->num_line_numbers)
  {
    off+=get_small_number(&cnt);
    if(off > offset) return line;
    line+=get_small_number(&cnt);
  }
  return line;
}
   
/*
 * Write out a trace. If there is an heart_beat(), then return the
 * object that had that heart beat.
 */
struct object *dump_trace(int how)
{
  struct control_stack *p;
  struct object *ret = 0;
#ifdef DEBUG
  int last_instructions PROT((void));
#endif

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

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

/*
 * Reset the virtual stack machine.
 */
void reset_machine(int first)
{
  extern struct lvalue *lsp;
  extern struct lvalue lvalues[LVALUES];
  mark_sp=mark_stack-1;
  max_eval_cost=MAX_COST;
  csp = control_stack - 1;
  if (first)
    sp = start_of_stack - 1;
  else
    pop_n_elems(sp - start_of_stack + 1);
  lsp=lvalues-1;
}

#ifdef DEBUG

char *get_arg(int a,int b)
{
  int iarg;
  short sarg;
  static char buff[30];
  char *from;

  from = previous_pc[a];
  switch(previous_pc[b]-from)
  {
  case 2:
    iarg=(int)EXTRACT_UCHAR(from+1);
    break;

  case 3:
    ((char *)&sarg)[0] = from[1];
    ((char *)&sarg)[1] = from[2];
    iarg=(int)sarg;
    break;

  case 5:
    ((char *)&iarg)[0] = from[1];
    ((char *)&iarg)[1] = from[2];
    ((char *)&iarg)[2] = from[3];
    ((char *)&iarg)[3] = from[4];
    break;

  default: return "        ";
  }

  sprintf(buff, "%d", iarg);
  return buff;
}

int last_instructions()
{
  char *s,*s2;
  
  int i;
  i = last;
  do
  {
    if (previous_instruction[i] != 0)
    {
      s=get_arg(i, (i+1) % NELEM(previous_instruction));
      s2=get_instruction_name(previous_instruction[i]);

      fprintf(stderr,"%6x (%4d): %3d %8s %-25s (%d)\n",
	      (int)(previous_pc[i]),
	      previous_instruction[i],
	      previous_instruction_offset[i],s,s2,
	      stack_size[i] + 1);
    }
    i = (i + 1) % (sizeof previous_instruction / sizeof (int));
  } while (i != last);
  return last;
}

#endif /* DEBUG */

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

  assert_master_ob_loaded();
  return apply(fun, master_ob, num_arg,1);
}

void assert_master_ob_loaded()
{
  char *tmp;
  extern struct object *master_ob;
  static int inside = 0;

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

    tmp=getenv("LPC_MASTER");
    if(!tmp)
    {
      if(batch_mode)
      {
	tmp=(char *)alloca(strlen(BINDIR)+30);
	sprintf(tmp,"%s/master.c",BINDIR);
      }else{
	tmp="secure/master";
      }
    }

    /* master_ob is automatically set in give_uid_to_object */
    clone_object(tmp,0);
 
    if (master_ob == 0)
    {
	fprintf(stderr, "The file %s must be loadable.\n",tmp);
	exit(1);
    }

    inside = 0;
  }
}

/*
 * 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 (IS_TYPE(*svp,BT_OBJECT | BT_FUNCTION))
    {
      int tmp=svp->type==T_OBJECT;
      if (svp->u.ob != ob)
	continue;
      free_object(svp->u.ob, "remove_object_from_stack");
      svp->type = T_NUMBER;
      svp->subtype = tmp?NUMBER_DESTRUCTED_OBJECT:NUMBER_DESTRUCTED_FUNCTION;
      svp->u.number = 0;
    }
  }
}

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