/*
	eoperators.c: this file contains all of the operators called from
	inside eval_instruction() in interpret.c.
*/

#include <sys/types.h>
#include <sys/stat.h>
#include <sys/time.h>
#ifndef LATTICE
#include <sys/ioctl.h>
#include <netdb.h>
#endif
#include <fcntl.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <ctype.h>
#include <signal.h>
#ifndef LATTICE
#include <memory.h>
#else
#include "amiga.h"
#endif
#include <setjmp.h>
#include "config.h"
#include "lint.h"
#include "interpret.h"
#include "mapping.h"
#include "object.h"
#include "exec.h"
#include "efun_protos.h"
#include "comm.h"
#include "opcodes.h"
#include "switch.h"
#include "stralloc.h"
#include "debug.h"

/* Needed these from interpret.c. But what for? -SH */
#define TRACE_CALL_OTHER 2
#define TRACE_RETURN 4
#define TRACE_ARGS 8
#define TRACE_HEART_BEAT 32
#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))

extern int d_flag;
extern char *pc;
extern int tracedepth;
extern int current_time;
extern char *last_verb;
extern struct svalue *fp;	  /* Pointer to first argument. */
extern int function_index_offset; /* Needed for inheritance */
extern int variable_index_offset; /* Needed for inheritance */
extern struct object *previous_ob;
extern struct object *master_ob;
extern struct svalue *expected_stack;
extern userid_t *backbone_uid;
extern struct svalue const0, const1, const0u, const0n;
extern struct object *current_heart_beat, *current_interactive;
extern struct svalue catch_value;	/* Used to throw an error to a catch */
extern short *break_sp;		/* Points to address to branch to
				 * at next F_BREAK			*/
extern struct control_stack *csp;	/* Points to last element pushed */

/* Points to value of last push. */
extern struct svalue *sp;

extern int eval_cost;

static int i;
static float r;
static struct svalue *argp;

INLINE void
f_and(num_arg, instruction)
int num_arg, instruction;
{
  if (sp->type == T_POINTER && (sp-1)->type == T_POINTER)
    {
      extern struct vector *intersect_array
	PROT((struct vector *, struct vector *));
      (sp-1)->u.vec = intersect_array(sp->u.vec, (sp-1)->u.vec);
      sp--;
      return;
    }
  if ((sp-1)->type != T_NUMBER)
    bad_arg(1, instruction);
  if (sp->type != T_NUMBER)
    bad_arg(2, instruction);
  i = (sp-1)->u.number & sp->u.number;
  sp--;
  sp->u.number = i;
}

INLINE void
f_and_eq(num_arg, instruction)
int num_arg, instruction;
{
  if (sp[-1].type != T_LVALUE)
    bad_arg(1, instruction);
  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);
}

INLINE void
f_parse_command(num_arg, instruction)
int num_arg, instruction;
{
    struct svalue *arg;

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

    i = parse(arg[0].u.string, &arg[1], arg[2].u.string, &arg[3],
          num_arg-3);
    pop_n_elems(num_arg);   /* Get rid of all arguments */
    push_number(i);     /* Push the result value */
}

INLINE void
f_div_eq(num_arg, instruction)
int num_arg, instruction;
{
  if((sp - 1)->type != T_LVALUE)
    bad_arg(1, instruction);
  argp = (sp - 1)->u.lvalue;

  if(argp->type == T_NUMBER){
    if(sp->type == T_NUMBER){
      if(sp->u.number == 0)
	error("Division by 0nn\n");
      i = argp->u.number / sp->u.number;
      pop_n_elems(2);
      push_number(i);
      assign_svalue(argp, sp);
    }
    else if(sp->type == T_REAL){
      if(sp->u.real == 0.)
        error("Division by 0.nr\n");
      r = argp->u.number / sp->u.real;
      pop_n_elems(2);
      push_real(r);
      assign_svalue(argp, sp);
    }
    else {
      error("Bad right type to /=");
    }
  }
  else if(argp->type == T_REAL){
    if(sp->type == T_NUMBER){
      if(sp->u.number == 0)
        error("Division by 0rn\n");
      r = argp->u.real / sp->u.number;
      pop_n_elems(2);
      push_real(r);
      assign_svalue(argp, sp);
    }
    else if(sp->type == T_REAL){
      if(sp->u.real == 0.)
        error("Division by 0.rr\n");
      r = argp->u.real / sp->u.real;
      pop_n_elems(2);
      push_real(r);
      assign_svalue(argp, sp);
    }
    else {
      error("Bad right type to /=");
    }
  }
  else {
    error("Bad left type to /=\n");
  }
}

INLINE void
f_eq(num_arg, instruction)
int num_arg, instruction;
{
  switch(sp->type){
  case T_NUMBER:
    if((sp-1)->type == T_NUMBER){
      i = ((sp-1)->u.number == sp->u.number);
    }
    else if((sp-1)->type == T_REAL){
      i = ((sp-1)->u.real == sp->u.number);
    }
    else {
      pop_stack();
      assign_svalue(sp, &const0);
      return;
    }
    break;
  case T_REAL:
    if((sp-1)->type == T_NUMBER){
      i = ((sp-1)->u.number == sp->u.real);
    }
    else if((sp-1)->type == T_REAL){
      i = ((sp-1)->u.real == sp->u.real);
    }
    else {
      pop_stack();
      assign_svalue(sp, &const0);
      return;
    }
    break;
  case T_POINTER:
    if((sp-1)->type == T_POINTER){
      i = ((sp-1)->u.vec == sp->u.vec);
    }
    else {
      pop_stack();
      assign_svalue(sp, &const0);
      return;
    }
    break;
  case T_STRING:
    if((sp-1)->type == T_STRING){
      i = (strcmp((sp-1)->u.string, sp->u.string) == 0);
    }
    else {
      pop_stack();
      assign_svalue(sp, &const0);
      return;
    }
    break;
  case T_OBJECT:
    if((sp-1)->type == T_OBJECT){
      i = ((sp-1)->u.ob == sp->u.ob);
    }
    else {
      pop_stack();
      assign_svalue(sp, &const0);
      return;
    }
    break;
  case T_MAPPING:
    if((sp-1)->type == T_MAPPING){
      i = ((sp-1)->u.map == sp->u.map);
    }
    else {
      pop_stack();
      assign_svalue(sp, &const0);
      return;
    }
    break;
  default:
    i = 0;
    break;
  }
  pop_n_elems(2);
  push_number(i);
}

INLINE void
f_lsh(num_arg, instruction)
int num_arg, instruction;
{
  if ((sp-1)->type != T_NUMBER)
    bad_arg(1, instruction);
  if (sp->type != T_NUMBER)
    bad_arg(2, instruction);
  i = (sp-1)->u.number << sp->u.number;
  sp--;
  sp->u.number = i;
}

INLINE void
f_lsh_eq(num_arg, instruction)
int num_arg, instruction;
{
  if (sp[-1].type != T_LVALUE)
    bad_arg(1, instruction);
  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);
}

INLINE void
f_mod_eq(num_arg, instruction)
int num_arg, instruction;
{
  if (sp[-1].type != T_LVALUE)
    bad_arg(1, instruction);
  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);
}

INLINE void
f_mult_eq(num_arg, instruction)
int num_arg, instruction;
{
  if((sp - 1)->type != T_LVALUE)
    bad_arg(1, instruction);
  argp = (sp - 1)->u.lvalue;

  if(argp->type == T_NUMBER){
    if(sp->type == T_NUMBER){
      i = argp->u.number * sp->u.number;
      pop_n_elems(2);
      push_number(i);
      assign_svalue(argp, sp);
    }
    else if(sp->type == T_REAL){
      r = argp->u.number * sp->u.real;
      pop_n_elems(2);
      push_real(r);
      assign_svalue(argp, sp);
    }
    else {
      error("Bad right type to *=");
    }
  }
  else if(argp->type == T_REAL){
    if(sp->type == T_NUMBER){
      r = argp->u.real * sp->u.number;
      pop_n_elems(2);
      push_real(r);
      assign_svalue(argp, sp);
    }
    else if(sp->type == T_REAL){
      r = argp->u.real * sp->u.real;
      pop_n_elems(2);
      push_real(r);
      assign_svalue(argp, sp);
    }
    else {
      error("Bad right type to *=");
    }
  }
  else if(argp->type == T_MAPPING){
    if(sp->type == T_MAPPING){
      struct mapping *m;

      m = compose_mapping(argp->u.map, sp->u.map);
      pop_n_elems(2);
      push_mapping(m);
      assign_svalue(argp, sp);
    }
    else {
      error("Bad right type to *=");
    }
  }
  else {
    error("Bad left type to *=\n");
  }
}

INLINE void
f_ne(num_arg, instruction)
int num_arg, instruction;
{
  switch(sp->type){
  case T_NUMBER:
    if((sp-1)->type == T_NUMBER){
      i = ((sp-1)->u.number != sp->u.number);
    }
    else if((sp-1)->type == T_REAL){
      i = ((sp-1)->u.real != sp->u.number);
    }
    else {
      pop_stack();
      assign_svalue(sp, &const1);
      return;
    }
    break;
  case T_REAL:
    if((sp-1)->type == T_NUMBER){
      i = ((sp-1)->u.number != sp->u.real);
    }
    else if((sp-1)->type == T_REAL){
      i = ((sp-1)->u.real != sp->u.real);
    }
    else {
      pop_stack();
      assign_svalue(sp, &const1);
      return;
    }
    break;
  case T_POINTER:
    if((sp-1)->type == T_POINTER){
      i = ((sp-1)->u.vec != sp->u.vec);
    }
    else {
      pop_stack();
      assign_svalue(sp, &const1);
      return;
    }
    break;
  case T_STRING:
    if((sp-1)->type == T_STRING){
      i = strcmp((sp-1)->u.string, sp->u.string);
    }
    else {
      pop_stack();
      assign_svalue(sp, &const1);
      return;
    }
    break;
  case T_OBJECT:
    if((sp-1)->type == T_OBJECT){
      i = ((sp-1)->u.ob != sp->u.ob);
    }
    else {
      pop_stack();
      assign_svalue(sp, &const1);
      return;
    }
    break;
  case T_MAPPING:
    if((sp-1)->type == T_MAPPING){
      i = ((sp-1)->u.map != sp->u.map);
    }
    else {
      pop_stack();
      assign_svalue(sp, &const1);
      return;
    }
    break;
  default:
    i = 1;
    break;
  }
  pop_n_elems(2);
  push_number(i);
}

INLINE void
f_or(num_arg, instruction)
int num_arg, instruction;
{
  if ((sp-1)->type != T_NUMBER)
    bad_arg(1, instruction);
  if (sp->type != T_NUMBER)
    bad_arg(2, instruction);
  i = (sp-1)->u.number | sp->u.number;
  sp--;
  sp->u.number = i;
}

INLINE void
f_or_eq(num_arg, instruction)
int num_arg, instruction;
{
  if (sp[-1].type != T_LVALUE)
    bad_arg(1, instruction);
  argp = sp[-1].u.lvalue;
  if (sp[-1].type != T_LVALUE)
    bad_arg(1, instruction);
  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);
}

INLINE void
f_range(num_arg, instruction)
int num_arg, instruction;
{
  if (sp[-1].type != T_NUMBER)
    error("Bad type of start interval to [ .. ] range.\n");
  if (sp[0].type != T_NUMBER)
    error("Bad type of end interval to [ .. ] range.\n");
  if (sp[-2].type == T_POINTER)
    {
      struct vector *v;

      v = slice_array(sp[-2].u.vec, sp[-1].u.number, sp[0].u.number);
      pop_n_elems(3);
      if (v)
	{
	  push_vector(v);
	  v->ref--;		/* Will make ref count == 1 */
	}
      else
	push_number(0);
    }
  else if (sp[-2].type == T_STRING)
    {
      int len, from, to;
      char *res;
	    
      len = strlen(sp[-2].u.string);
      from = sp[-1].u.number;
      if (from < 0)
	from = len + from;
      if (from >= len)
	{
	  pop_n_elems(3);
	  push_string("", STRING_CONSTANT);
	  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 = DXALLOC(to - from + 2, 37, "f_range");
      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");
}

INLINE void
f_rsh(num_arg, instruction)
int num_arg, instruction;
{
  if ((sp-1)->type != T_NUMBER)
    bad_arg(1, instruction);
  if (sp->type != T_NUMBER)
    bad_arg(2, instruction);
  i = (sp-1)->u.number >> sp->u.number;
  sp--;
  sp->u.number = i;
}

INLINE void
f_rsh_eq(num_arg, instruction)
int num_arg, instruction;
{
  if (sp[-1].type != T_LVALUE)
    bad_arg(1, instruction);
  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);
}

INLINE void
f_sub_eq(num_arg, instruction)
     int num_arg, instruction;
{
  if((sp - 1)->type != T_LVALUE)
    bad_arg(1, instruction);
  argp = (sp - 1)->u.lvalue;

  switch(argp->type){
  case T_NUMBER:
    if(sp->type == T_NUMBER){
      argp->u.number -= sp->u.number;
      sp--;
    }
    else if(sp->type == T_REAL){
      argp->u.number -= sp->u.real;
      sp--;
    }
    else {
      error("Bad type number to rhs -=");
    }
    break;
  case T_REAL:
    if(sp->type == T_NUMBER){
      argp->u.real -= sp->u.number;
      sp--;
    }
    else if(sp->type == T_REAL){
      argp->u.real -= sp->u.real;
      sp--;
    }
    else {
      error("Bad type number to rhs -=");
    }
    break;
  case T_POINTER:
    if(sp->type == T_POINTER){
      struct vector *subtract_array PROT((struct vector*,struct vector*));
      struct vector *v, *w;

      v = sp->u.vec;
      if(v->ref > 1){
        v = slice_array(v, 0, v->size-1 );
        v->ref--;
      }
      w = subtract_array(argp->u.vec, v);
      free_vector(argp->u.vec);
      free_vector(v);  /* no longer freed in subtract_array() */
      eval_cost += (w->size << 3);
      sp--;
      argp->u.vec = w;
    }
    else {
      error("Bad type number to rhs -=");
    }
    break;
  default:
    error("Bad left type to -=\n");
  }
  assign_svalue_no_free(sp, argp);
}

/*
 * Structure of F_SWITCH:
 *   table type (1 byte)
 *   address of table (1 short)
 *   address of break (1 short)
 *   address of default (1 short)
 *     then all the switch code
 *   switch table (varies)
 *
 * Table type is either
 *   0xfe  - integer labels, direct lookup.
 *           Table is followed by 1 long that is minimum key value.
 *           Each table entry is a short address to jump to.
 *   0xfN  - integer labels.  N is size as a power of 2.
 *           Each table entry is 1 long (key) followed by 1 short (address).
 *   0xNf  - string labels.  Otherwise same as for integer labels.
 *
 * For normal string or integer tables, if the address is 0 or 1,
 * the key is the lower end of a range, and the upper end is in
 * the next entry.  If it's a 0, the second address indicates a
 * direct lookup table (currently this case is never generated by
 * the compiler).  If it's a 1, the second address is used for
 * all keys in the range (corresponds to 'case x..y:' labels).
 *
 * Binary search is used on the normal tables.
 */

/* offsets from 'pc' */
#define SW_TYPE		0
#define SW_TABLE	1
#define SW_BREAK	3
#define SW_DEFAULT	5

#define ENTRY_SIZE	6

/* offsets used for range (L_ for lower member, U_ for upper member) */
#define L_LOWER	0
#define L_TYPE	4
#define L_UPPER	6
#define L_ADDR	10
#define U_LOWER	-6
#define U_TYPE	-2
#define U_UPPER	0
#define U_ADDR	4

#define COPY_SHORT(dst, src) \
    { ((char *)(dst))[0] = ((char *)(src))[0]; \
      ((char *)(dst))[1] = ((char *)(src))[1]; }
#define COPY_LONG(dst, src) \
    { ((char *)(dst))[0] = ((char *)(src))[0]; \
      ((char *)(dst))[1] = ((char *)(src))[1]; \
      ((char *)(dst))[2] = ((char *)(src))[2]; \
      ((char *)(dst))[3] = ((char *)(src))[3]; }

INLINE void
f_switch(num_arg, instruction)
int num_arg, instruction;
{
  extern char* findstring PROT((char*));
  unsigned short offset, break_adr;
  int d, s = 0, r;
  char *l,*end_tab;
  static unsigned short off_tab[] = {
    0*ENTRY_SIZE, 1*ENTRY_SIZE, 3*ENTRY_SIZE, 7*ENTRY_SIZE, 15*ENTRY_SIZE,
    31*ENTRY_SIZE, 63*ENTRY_SIZE, 127*ENTRY_SIZE, 255*ENTRY_SIZE,
    511*ENTRY_SIZE, 1023*ENTRY_SIZE, 2047*ENTRY_SIZE, 4095*ENTRY_SIZE,
    8191*ENTRY_SIZE,
  };

  COPY_SHORT(&offset, pc+SW_TABLE);
  COPY_SHORT(&break_adr, pc+SW_BREAK);
  *--break_sp = break_adr;
  if ( ( i = EXTRACT_UCHAR(pc) >> 4 ) != 0xf )
  { /* String table, find correct key */
    if ( sp->type == T_NUMBER && !sp->u.number )
    {
      /* special case: 0 as a string */
      s = (int)ZERO_AS_STR_CASE_LABEL;
    }
    else if ( sp->type == T_STRING )
    {
      switch(sp->subtype)
      {
      case STRING_SHARED:
	s = (int)sp->u.string;
	break;
      default:
	s = (int)findstring(sp->u.string);
	break;
      }
      if (s == 0)
      {
	/* Take default case now - else we could be get confused
	 * with ZERO_AS_STR_CASE_LABEL.
	 */
	COPY_SHORT(&offset, pc+SW_DEFAULT);
	pop_stack();
	pc = current_prog->p.i.program + offset;
	return;
      }
    }
    else
    {
      bad_arg(1, instruction);
    }
  }
  else
  { /* Integer table, check type */
    if (sp->type != T_NUMBER)
      bad_arg(1, instruction);
    s = sp->u.number;
    i = (int)pc[0] &0xf ;
  }
  pop_stack();
  end_tab = current_prog->p.i.program + break_adr;
  /*
   * i is the table size as a power of 2.  Tells us where to
   * start searching.  i==14 is a special case.
   */
  if ( i >= 14 )
    if ( i == 14 )
    {
      /* fastest switch format : lookup table */
      l = current_prog->p.i.program + offset;
      COPY_LONG(&d, end_tab-4);
      /* d is minimum value - see if in range or not */
      if ( s >= d && l + (s=(s-d)*sizeof(short)) < (end_tab - 4) )
      {
	COPY_SHORT(&offset, &l[s]);
	if (offset) {
	  pc = current_prog->p.i.program + offset;
	  return;
	}
      }
      /* default */
      COPY_SHORT(&offset, pc+SW_DEFAULT);
      pc = current_prog->p.i.program + offset;
      return;
    }
    else
      fatal("unsupported switch table format.\n");

  /*
   * l - current entry we are looking at.
   * d - size to add/subtract from l each iteration.
   * s - key we're looking for
   * r - key l is pointing at
   */
  l = current_prog->p.i.program + offset + off_tab[i];
  d = (int)(off_tab[i]+ENTRY_SIZE) >> 1;
  if (d < ENTRY_SIZE) d=0;
  for(;;)
  {
    COPY_LONG(&r, l);
    if (s < r)
    {
      if (d < ENTRY_SIZE)
      {
	/* test if entry is part of a range */
	/* Don't worry about reading from F_BREAK (byte before table) */
	COPY_SHORT(&offset, l+U_TYPE);
	if (offset <= 1)
	{
	  COPY_LONG(&r, l+U_LOWER);
	  if (s >= r)
	  {
	    /* s is in the range */
	    COPY_SHORT(&offset, l+U_ADDR);
	    if (!offset)
	    {
	      /* range with lookup table */
	      l = current_prog->p.i.program + offset +
		(s-r) * sizeof(short);
	      COPY_SHORT(&offset, l);
	    }  /* else normal range and offset is correct */
	    break;
	  }
	}
	/* key not found, use default address */
	COPY_SHORT(&offset, pc+SW_DEFAULT);
	break;
      }
      else
      {
	/* d >= ENTRY_SIZE */
	l -= d;
	d >>= 1;
      }
    }
    else if (s > r)
    {
      if (d < ENTRY_SIZE) 
      {
	/* test if entry is part of a range */
	COPY_SHORT(&offset, l+L_TYPE);
	if (offset <= 1)
	{
	  COPY_LONG(&r, l+L_UPPER);
	  if (s <= r)
	  {
	    /* s is in the range */
	    COPY_SHORT(&offset, l+L_ADDR);
	    if (!offset)
	    {
	      /* range with lookup table */
	      l = current_prog->p.i.program + offset +
		(s-r) * sizeof(short);
	      COPY_SHORT(&offset, l);
	    }  /* else normal range and offset is correct */
	    break;
	  }
	}
	/* use default address */
	COPY_SHORT(&offset, pc+SW_DEFAULT);
	break;
      }
      else
      { /* d >= ENTRY_SIZE */
	l += d;
	/* if table isn't a power of 2 in size, fix us up */
	while (l >= end_tab)
	{
	  d >>= 1;
	  if (d < ENTRY_SIZE)
	  {
	    d = 0;
	    break;
	  }
	  l -= d;
	}
	if (l == end_tab) {
	  /* use default address */
	  COPY_SHORT(&offset, pc+SW_DEFAULT);
	  break;
	}
	d >>= 1;
      }
    }
    else
    {
      /* s == r */
      COPY_SHORT(&offset, l+U_ADDR);
      /* found the key - but could be part of a range... */
      if ( !l[U_TYPE] && !l[U_TYPE+1] )     
      {
	/* end of range with lookup table */
	COPY_LONG(&r, l+U_LOWER);
	l = current_prog->p.i.program + offset + (s-r)*sizeof(short);
	COPY_SHORT(&offset, l);
      }
      if (offset <= 1)
      {
	COPY_SHORT(&offset, l+L_ADDR);
	if (!offset)
	{
	  /* start of range with lookup table */
	  l = current_prog->p.i.program + offset;
	  COPY_SHORT(&offset, l);
	}  /* else normal range, offset is correct */
      }
      break;
    }
  }
  /* now do jump */
  pc = current_prog->p.i.program + offset;
}

INLINE void
f_xor(num_arg, instruction)
int num_arg, instruction;
{
  if ((sp-1)->type != T_NUMBER)
    bad_arg(1, instruction);
  if (sp->type != T_NUMBER)
    bad_arg(2, instruction);
  i = (sp-1)->u.number ^ sp->u.number;
  sp--;
  sp->u.number = i;
}

INLINE void
f_xor_eq(num_arg, instruction)
int num_arg, instruction;
{
  if (sp[-1].type != T_LVALUE)
    bad_arg(1, instruction);
  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);
}

INLINE struct funp *
make_funp(sob, sfun)
struct svalue *sob, *sfun;
{
	struct funp *fp;

	fp = (struct funp *)DMALLOC(sizeof(struct funp), 38, "make_funp");
	assign_svalue_no_free(&fp->obj, sob);
	assign_svalue_no_free(&fp->fun, sfun);
	fp->ref = 1;
	return fp;
}

INLINE void
push_funp(fp)
struct funp *fp;
{
	fp->ref++;
	sp++;
	sp->type = T_FUNCTION;
	sp->u.fp = fp;
}

INLINE void
free_funp(fp)
struct funp *fp;
{
	fp->ref--;
	if (fp->ref > 0) {
		return;
	}
	free_svalue(&fp->obj);
	free_svalue(&fp->fun);
	FREE(fp);
}

INLINE void
f_function_constructor(num_arg, instruction)
int num_arg, instruction;
{
	struct funp *fp;

	fp = make_funp(sp - 1, sp);
	pop_n_elems(2);
	push_funp(fp);
	fp->ref--;
}

INLINE void
f_function_split(num_arg, instruction)
int num_arg, instruction;
{
	struct svalue *obj, *fun;
	struct funp *tmp;

	if (sp->type != T_FUNCTION) {
		bad_arg(1, instruction);
	}
	obj = &sp->u.fp->obj;
	fun = &sp->u.fp->fun;
	tmp = sp->u.fp;
	sp--; /* don't free the funp here since that would also free obj and fun */
	if ((obj->type == T_OBJECT) && (obj->u.ob->flags & O_DESTRUCTED)) {
		assign_svalue(obj, &const0n);
		push_null();
	} else {
		push_svalue(obj);
	}
	push_svalue(fun);
	free_funp(tmp); /* go ahead and free it here since the pushes make it ok */
}

INLINE void
f_aggregate_assoc(num_arg, instruction)
int num_arg, instruction;
{
  struct mapping *m;
  unsigned short num;

  ((char *)&num)[0] = pc[0];
  ((char *)&num)[1] = pc[1];
  pc += 2;
  m = load_mapping_from_aggregate(sp - num + 1, num);
  pop_n_elems((int)num);
  push_mapping(m);
  m->ref--;
}
   
INLINE void
f_aggregate(num_arg, instruction)
int num_arg, instruction;
{
  struct vector *v;
  unsigned short num;

  ((char *)&num)[0] = pc[0];
  ((char *)&num)[1] = pc[1];
  pc += 2;
  v = allocate_array((int)num);
  for (i=0; i < (int)num; i++)
    assign_svalue_no_free(&v->item[i], sp + i - num + 1);
  pop_n_elems((int)num);
  push_vector(v);
  v->ref--;
}

INLINE void
f_sscanf(num_arg, instruction)
int num_arg, instruction;
{
  num_arg = EXTRACT_UCHAR(pc);
  pc++;
  i = inter_sscanf(num_arg);
  pop_n_elems(num_arg);
  push_number(i);
}
