#include "std.h"

#ifdef LPC_TO_C
#include "cfuns.h"
#include "backend.h"
#include "lpc_to_c.h"
#include "eoperators.h"
#include "parse.h"
#include "qsort.h"

IF_DEBUG(extern int stack_in_use_as_temporary);

/* temporaries for LPC->C code */
int lpc_int;
svalue_t *lpc_svp;
array_t *lpc_arr;
mapping_t *lpc_map;

static svalue_t *lval;

void c_new_class P2(int, which, int, has_values) {
    array_t *cl;
    
    cl = allocate_class(&current_prog->classes[which], has_values);
    push_refed_class(cl);
}

void c_member P1(int, idx) {
    array_t *arr;

    if (sp->type != T_CLASS)
        error("Tried to take a member of something that isn't a class.\n");
    arr = sp->u.arr;
    if (idx >= arr->size) error("Class has no corresponding member.\n");
    assign_svalue_no_free(sp, &arr->item[idx]);
    free_class(arr);
}

void c_member_lvalue P1(int, idx) {
    array_t *arr;

    if (sp->type != T_CLASS)
        error("Tried to take a member of something that isn't a class.\n");
    arr = sp->u.arr;
    if (idx >= arr->size) error("Class has no corresponding member.\n");
    sp->type = T_LVALUE;
    sp->u.lvalue = arr->item + idx;
    free_class(arr);
}

void c_return() {
    svalue_t sv;

    sv = *sp--;
    pop_n_elems(csp->num_local_variables);
    sp++;
    DEBUG_CHECK(sp != fp, "Bad stack at c_return\n");
    *sp =sv;
    pop_control_stack();
}

void c_return_zero() {
    pop_n_elems(csp->num_local_variables);
    sp++;
    DEBUG_CHECK(sp != fp, "Bad stack at c_return\n");
    *sp = const0;
    pop_control_stack();
}

void c_foreach P3(int, flags, int, idx1, int, idx2) {
    IF_DEBUG(stack_in_use_as_temporary++);
    
    if (flags & FOREACH_MAPPING) {
	CHECK_TYPES(sp, T_MAPPING, 2, F_FOREACH);
	
	push_refed_array(mapping_indices(sp->u.map));

	STACK_INC;
	sp->type = T_NUMBER;
	sp->u.lvalue = (sp-1)->u.arr->item;
	sp->subtype = (sp-1)->u.arr->size;
		    
	STACK_INC;
	sp->type = T_LVALUE;
	if (flags & FOREACH_LEFT_GLOBAL) {
	    sp->u.lvalue = &current_object->variables[idx1 + variable_index_offset];
	} else {
	    sp->u.lvalue = fp + idx1;
	}
    } else 
    if (sp->type == T_STRING) {
	STACK_INC;
	sp->type = T_NUMBER;
	sp->u.lvalue_byte = (unsigned char *)((sp-1)->u.string);
	sp->subtype = SVALUE_STRLEN(sp - 1);
    } else {
	CHECK_TYPES(sp, T_ARRAY, 2, F_FOREACH);

	STACK_INC;
	sp->type = T_NUMBER;
	sp->u.lvalue = (sp-1)->u.arr->item;
	sp->subtype = (sp-1)->u.arr->size;
    }

    if (flags & FOREACH_RIGHT_GLOBAL) {
	STACK_INC;
	sp->type = T_LVALUE;
	sp->u.lvalue = &current_object->variables[idx2 + variable_index_offset];
    } else if (flags & FOREACH_REF) {
	ref_t *ref = make_ref();
	svalue_t *loc = fp + idx2;

	/* foreach guarantees our target remains valid */
	ref->lvalue = 0;
	ref->sv.type = T_NUMBER;
	STACK_INC;
	sp->type = T_REF;
	sp->u.ref = ref;
	DEBUG_CHECK(loc->type != T_NUMBER && loc->type != T_REF, "Somehow a reference in foreach acquired a value before coming into scope");
	loc->type = T_REF;
	loc->u.ref = ref;
	ref->ref++;
    } else {
	STACK_INC;
	sp->type = T_LVALUE;
	sp->u.lvalue = fp + idx2;
    }
}

void c_expand_varargs P1(int, where) {
    svalue_t *s, *t;
    array_t *arr;
    int n;
    
    s = sp - where;
    
    if (s->type != T_ARRAY)
	error("Item being expanded with ... is not an array\n");
		
    arr = s->u.arr;
    n = arr->size;
    num_varargs += n - 1;
    if (!n) {
	t = s;
	while (t < sp) {
	    *t = *(t + 1);
	    t++;
	}
	sp--;
    } else if (n == 1) {
	assign_svalue_no_free(s, &arr->item[0]);
    } else {
	t = sp;
	CHECK_STACK_OVERFLOW(n - 1);
	sp += n - 1;
	while (t > s) {
	    *(t + n - 1) = *t;
	    t--;
	}
	t = s + n - 1;
	if (arr->ref == 1) {
	    memcpy(s, arr->item, n * sizeof(svalue_t));
	    free_empty_array(arr);
	    return;
	} else {
	    while (n--)
		assign_svalue_no_free(t--, &arr->item[n]);
	}
    }
    free_array(arr);
}

void c_exit_foreach PROT((void)) {
    IF_DEBUG(stack_in_use_as_temporary--);
    if (sp->type == T_REF) {
	if (!(--sp->u.ref->ref) && sp->u.ref->lvalue == 0)
	    FREE(sp->u.ref);
    }
    if ((sp-1)->type == T_LVALUE) {
	/* mapping */
	sp -= 3;
	free_array((sp--)->u.arr);
	free_mapping((sp--)->u.map);
    } else {
	/* array or string */
	sp -= 2;
	if (sp->type == T_STRING)
	    free_string_svalue(sp--);
	else
	    free_array((sp--)->u.arr);
    }
}

int c_next_foreach PROT((void)) {
    if ((sp-1)->type == T_LVALUE) {
	/* mapping */
	if ((sp-2)->subtype--) {
	    svalue_t *key = (sp-2)->u.lvalue++;
	    svalue_t *value = find_in_mapping((sp-4)->u.map, key);
		    
	    assign_svalue((sp-1)->u.lvalue, key);
	    if (sp->type == T_REF) {
		if (value == &const0u)
		    sp->u.ref->lvalue = 0;
		else
		    sp->u.ref->lvalue = value;
	    } else
		assign_svalue(sp->u.lvalue, value);
	    return 1;
	}
    } else {
	/* array or string */
	if ((sp-1)->subtype--) {
	    if ((sp-2)->type == T_STRING) {
		if (sp->type == T_REF) {
		    sp->u.ref->lvalue = &global_lvalue_byte;
		    global_lvalue_byte.u.lvalue_byte = (unsigned char *)((sp-1)->u.lvalue_byte++);
		} else {
		    free_svalue(sp->u.lvalue, "string foreach");
		    sp->u.lvalue->type = T_NUMBER;
		    sp->u.lvalue->subtype = 0;
		    sp->u.lvalue->u.number = *((sp-1)->u.lvalue_byte)++;
		}
	    } else {
		if (sp->type == T_REF)
		    sp->u.ref->lvalue = (sp-1)->u.lvalue++;
		else
		    assign_svalue(sp->u.lvalue, (sp-1)->u.lvalue++);
	    }
	    return 1;
	}
    }
    c_exit_foreach();
    return 0;
}

void c_call_inherited P3(int, inh, int, func, int, num_arg) {
    inherit_t *ip = current_prog->inherit + inh;
    program_t *temp_prog = ip->prog;
    function_t *funp;
    
    push_control_stack(FRAME_FUNCTION);

    caller_type = ORIGIN_LOCAL;
    current_prog = temp_prog;
		
    csp->num_local_variables = num_arg + num_varargs;
    num_varargs = 0;
    		
    function_index_offset += ip->function_index_offset;
    variable_index_offset += ip->variable_index_offset;
    
    funp = setup_inherited_frame(func);
    csp->pc = pc;

    call_program(current_prog, funp->address);
}

void c_call P2(int, func, int, num_arg) {
    function_t *funp;
    
    func += function_index_offset;
    /*
     * Find the function in the function table. As the
     * function may have been redefined by inheritance, we
     * must look in the last table, which is pointed to by
     * current_object.
     */
    DEBUG_CHECK(func >= current_object->prog->last_inherited +
		current_object->prog->num_functions_defined,
		"Illegal function index\n");
    
    if (current_object->prog->function_flags[func] & FUNC_UNDEFINED)
	error("Undefined function: %s\n", function_name(current_object->prog, func));
    /* Save all important global stack machine registers */
    push_control_stack(FRAME_FUNCTION);
    
    caller_type = ORIGIN_LOCAL;
    /* This assigment must be done after push_control_stack() */
    current_prog = current_object->prog;
    /*
     * If it is an inherited function, search for the real
     * definition.
     */
    csp->num_local_variables = num_arg + num_varargs;
    num_varargs = 0;
    funp = setup_new_frame(func);
    csp->pc = pc;	/* The corrected return address */
    call_program(current_prog, funp->address);
}

void c_efun_return P1(int, args) {
    svalue_t sv;
		
    sv = *sp--;
    pop_n_elems(args);
    *++sp = sv;
}

void c_void_assign() {
#ifdef DEBUG
    if (sp->type != T_LVALUE) fatal("Bad argument to F_VOID_ASSIGN\n");
#endif
    lval = (sp--)->u.lvalue;
    if (sp->type != T_INVALID) {
	switch(lval->type) {
	case T_LVALUE_BYTE:
	    {
		if (sp->type != T_NUMBER) {
		    error("Illegal rhs to char lvalue\n");
		} else {
		    *global_lvalue_byte.u.lvalue_byte = (sp--)->u.number & 0xff;
		}
		break;
	    }
	    
	case T_LVALUE_RANGE:
	    {
		copy_lvalue_range(sp--);
		break;
	    }
	    
	default:
	    {
		free_svalue(lval, "F_VOID_ASSIGN : 3");
		*lval = *sp--;
	    }
	}
    } else sp--;
}

void c_post_dec() {
    DEBUG_CHECK(sp->type != T_LVALUE,
		"non-lvalue argument to --\n");
    lval = sp->u.lvalue;
    switch(lval->type) {
    case T_NUMBER:
	sp->type = T_NUMBER;
	sp->u.number = lval->u.number--;
	break;
    case T_REAL:
	sp->type = T_REAL;
	sp->u.real = lval->u.real--;
	break;
    case T_LVALUE_BYTE:
	sp->type = T_NUMBER;
	sp->u.number = (*global_lvalue_byte.u.lvalue_byte)--;
	break;
    default:
	error("-- of non-numeric argument\n");
    }
}

void c_post_inc() {
    DEBUG_CHECK(sp->type != T_LVALUE,
		"non-lvalue argument to ++\n");
    lval = sp->u.lvalue;
    switch (lval->type) {
    case T_NUMBER:
	sp->type = T_NUMBER;
	sp->u.number = lval->u.number++;
	break;
    case T_REAL:
	sp->type = T_REAL;
	sp->u.real = lval->u.real++;
	break;
    case T_LVALUE_BYTE:
	sp->type = T_NUMBER;
	sp->u.number = (*global_lvalue_byte.u.lvalue_byte)++;
	break;
    default:
	error("++ of non-numeric argument\n");
    }
}

void c_pre_dec() {
    svalue_t *lval;

    DEBUG_CHECK(sp->type != T_LVALUE, 
		"non-lvalue argument to --\n");
    lval = sp->u.lvalue;
    switch (lval->type) {
    case T_NUMBER:
	sp->type = T_NUMBER;
	sp->u.number = --(lval->u.number);
	break;
    case T_REAL:
	sp->type = T_REAL;
	sp->u.real = --(lval->u.real);
	break;
    case T_LVALUE_BYTE:
	sp->type = T_NUMBER;
	sp->u.number = --(*global_lvalue_byte.u.lvalue_byte);
	break;
    default:
	error("-- of non-numeric argument\n");
    }
}

void c_pre_inc() {
    svalue_t *lval;

    DEBUG_CHECK(sp->type != T_LVALUE,
		"non-lvalue argument to ++\n");
    lval = sp->u.lvalue;
    switch (lval->type) {
    case T_NUMBER:
	sp->type = T_NUMBER;
	sp->u.number = ++lval->u.number;
	break;
    case T_REAL:
	sp->type = T_REAL;
	sp->u.real = ++lval->u.number;
	break;
    case T_LVALUE_BYTE:
	sp->type = T_NUMBER;
	sp->u.number = ++*global_lvalue_byte.u.lvalue_byte;
	break;
    default:
	error("++ of non-numeric argument\n");
    }
}

void c_assign() {
#ifdef DEBUG
    if (sp->type != T_LVALUE) fatal("Bad argument to F_ASSIGN\n");
#endif
    switch(sp->u.lvalue->type) {
    case T_LVALUE_BYTE:
	if ((sp - 1)->type != T_NUMBER) {
	    error("Illegal rhs to char lvalue\n");
	} else {
	    *global_lvalue_byte.u.lvalue_byte = ((sp - 1)->u.number & 0xff);
	}
	break;
    default:
	assign_svalue(sp->u.lvalue, sp - 1);
	break;
    case T_LVALUE_RANGE:
	assign_lvalue_range(sp - 1);
	break;
    }
    sp--;              /* ignore lvalue */
    /* rvalue is already in the correct place */
}

void c_void_assign_local P1(svalue_t *, var) {
    if (sp->type == T_INVALID) {
	sp--;
	return;
    }
    free_svalue(var, "c_void_assign_local");
    *var = *sp--;
}

void c_index() {
    int i;
    
    switch (sp->type) {
    case T_MAPPING:
	{
	    svalue_t *v;
	    mapping_t *m;
	    
	    v = find_in_mapping(m = sp->u.map, sp - 1);
	    assign_svalue(--sp, v);    /* v will always have a
					* value */
	    free_mapping(m);
	    break;
	}
#ifndef NO_BUFFER_TYPE
    case T_BUFFER:
	{
	    if ((sp-1)->type != T_NUMBER)
		error("Indexing a buffer with an illegal type.\n");
	    
	    i = (sp - 1)->u.number;
	    if ((i > sp->u.buf->size) || (i < 0))
		error("Buffer index out of bounds.\n");
	    i = sp->u.buf->item[i];
	    free_buffer(sp->u.buf);
	    (--sp)->u.number = i;
	    break;
	}
#endif
    case T_STRING:
	{
	    if ((sp-1)->type != T_NUMBER) {
		error("Indexing a string with an illegal type.\n");
	    }
	    i = (sp - 1)->u.number;
	    if ((i > SVALUE_STRLEN(sp)) || (i < 0))
		error("String index out of bounds.\n");
	    i = (unsigned char) sp->u.string[i];
	    free_string_svalue(sp);
	    (--sp)->u.number = i;
	    break;
	}
    case T_ARRAY:
	{
	    array_t *arr;
	    
	    if ((sp-1)->type != T_NUMBER)
		error("Indexing an array with an illegal type\n");
	    i = (sp - 1)->u.number;
	    if (i<0) error("Negative index passed to array.\n");
	    arr = sp->u.arr;
	    if (i >= arr->size) error("Array index out of bounds.\n");
	    assign_svalue_no_free(--sp, &arr->item[i]);
	    free_array(arr);
	    break;
	}
    default:
	error("Indexing on illegal type.\n");
    }
    
    /*
     * 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_object(sp->u.ob, "F_INDEX");
	*sp = const0u;
    }
}

void c_rindex() {
    int i;

    switch (sp->type) {
#ifndef NO_BUFFER_TYPE
    case T_BUFFER:
	{
	    if ((sp-1)->type != T_NUMBER)
		error("Indexing a buffer with an illegal type.\n");
	    
	    i = sp->u.buf->size - (sp - 1)->u.number;
	    if ((i > sp->u.buf->size) || (i < 0))
		error("Buffer index out of bounds.\n");

	    i = sp->u.buf->item[i];
	    free_buffer(sp->u.buf);
	    (--sp)->u.number = i;
	    break;
	}
#endif
    case T_STRING:
	{
	    int len = SVALUE_STRLEN(sp);
	    if ((sp-1)->type != T_NUMBER) {
		error("Indexing a string with an illegal type.\n");
	    }
	    i = len - (sp - 1)->u.number;
	    if ((i > len) || (i < 0))
		error("String index out of bounds.\n");
	    i = (unsigned char) sp->u.string[i];
	    free_string_svalue(sp);
	    (--sp)->u.number = i;
	    break;
	}
    case T_ARRAY:
	{
	    array_t *vec = sp->u.arr;
	    
	    if ((sp-1)->type != T_NUMBER)
		error("Indexing an array with an illegal type\n");
	    i = vec->size - (sp - 1)->u.number;
	    if (i < 0 || i >= vec->size) error("Array index out of bounds.\n");
	    assign_svalue_no_free(--sp, &vec->item[i]);
	    free_array(vec);
	    break;
	}
    default:
	error("Indexing from the right on illegal type.\n");
    }
    
    /*
     * 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_object(sp->u.ob, "F_RINDEX");
	*sp = const0u;
    }
}

void
c_functional P3(int, kind, int, num_arg, POINTER_INT, func) {
    funptr_t *fp;
    
    fp = (funptr_t *)DXALLOC(sizeof(funptr_hdr_t) + sizeof(functional_t),
			     TAG_FUNP, "c_functional");
    fp->hdr.owner = current_object;
    add_ref( current_object, "c_functional" );
    fp->hdr.type = kind;
    
    current_prog->func_ref++;
    
    fp->f.functional.prog = current_prog;
    fp->f.functional.offset = func;
    fp->f.functional.num_arg = num_arg;
    fp->f.functional.num_local = 0;
    fp->f.functional.fio = function_index_offset;
    fp->f.functional.vio = variable_index_offset;

    if (sp->type == T_ARRAY) {
	fp->hdr.args = sp->u.arr;
	fp->f.functional.num_arg += sp->u.arr->size;
    } else
	fp->hdr.args = 0;
    
    fp->hdr.ref = 1;

    sp->type = T_FUNCTION;
    sp->u.fp = fp;
}

void
c_anonymous P3(int, num_arg, int, num_local, POINTER_INT, func) {
    funptr_t *fp;
    
    fp = (funptr_t *)DXALLOC(sizeof(funptr_hdr_t) + sizeof(functional_t),
			     TAG_FUNP, "c_functional");
    fp->hdr.owner = current_object;
    add_ref( current_object, "c_functional" );
    if (num_arg & 0x10000)
	fp->hdr.type = FP_FUNCTIONAL | FP_NOT_BINDABLE;
    else
	fp->hdr.type = FP_FUNCTIONAL;
    
    current_prog->func_ref++;
    
    fp->f.functional.prog = current_prog;
    fp->f.functional.offset = func;
    fp->f.functional.num_arg = num_arg & 0xff;
    fp->f.functional.num_local = num_local;
    fp->f.functional.fio = function_index_offset;
    fp->f.functional.vio = variable_index_offset;

    fp->hdr.args = 0;

    fp->hdr.ref = 1;

    STACK_INC;
    sp->type = T_FUNCTION;
    sp->u.fp = fp;
}

void
c_function_constructor P2(int, kind, int, arg)
{
    funptr_t *fp;

    switch (kind) {
    case FP_EFUN:
	fp = make_efun_funp(arg, sp);
	pop_stack();
	break;
    case FP_LOCAL:
	fp = make_lfun_funp(arg, sp); 
	pop_stack();
	break;
    case FP_SIMUL:
	fp = make_simul_funp(arg, sp); 
	pop_stack();
	break;
    case FP_FUNCTIONAL:
    case FP_FUNCTIONAL | FP_NOT_BINDABLE:
    case FP_ANONYMOUS:
    case FP_ANONYMOUS | FP_NOT_BINDABLE:
	fatal("Wrong constructor called for LPC->C functional.\n");
    default:
	fatal("Tried to make unknown type of function pointer.\n");
    }
    push_refed_funp(fp);
}

void c_not() {
    if (sp->type == T_NUMBER)
	sp->u.number = !sp->u.number;
    else
	assign_svalue(sp, &const0);
}

void c_mod() {
    CHECK_TYPES(sp - 1, T_NUMBER, 1, F_MOD);
    CHECK_TYPES(sp, T_NUMBER, 2, F_MOD);
    if ((sp--)->u.number == 0)
	error("Modulus by zero.\n");
    sp->u.number %= (sp+1)->u.number;
}

void c_add_eq P1(int, is_void) {
    DEBUG_CHECK(sp->type != T_LVALUE,
		"non-lvalue argument to +=\n");
    lval = sp->u.lvalue;
    sp--; /* points to the RHS */
    switch (lval->type) {
    case T_STRING:
	if (sp->type == T_STRING) {
	    SVALUE_STRING_JOIN(lval, sp, "f_add_eq: 1");
	} else if (sp->type == T_NUMBER) {
	    char buff[20];
	    
	    sprintf(buff, "%d", sp->u.number);
	    EXTEND_SVALUE_STRING(lval, buff, "f_add_eq: 2");
	} else if (sp->type == T_REAL) {
	    char buff[40];
	    
	    sprintf(buff, "%f", sp->u.real);
	    EXTEND_SVALUE_STRING(lval, buff, "f_add_eq: 2");
	} else {
	    bad_argument(sp, T_STRING | T_NUMBER | T_REAL, 2,
			 (is_void ? F_VOID_ADD_EQ : F_ADD_EQ));
	}
	break;
    case T_NUMBER:
	if (sp->type == T_NUMBER) {
	    lval->u.number += sp->u.number;
	    /* both sides are numbers, no freeing required */
	} else if (sp->type == T_REAL) {
	    lval->u.number += sp->u.real;
	    /* both sides are numbers, no freeing required */
	} else {
	    error("Left hand side of += is a number (or zero); right side is not a number.\n");
	}
	break;
    case T_REAL:
	if (sp->type == T_NUMBER) {
	    lval->u.real += sp->u.number;
	    /* both sides are numerics, no freeing required */
	}
	if (sp->type == T_REAL) {
	    lval->u.real += sp->u.real;
	    /* both sides are numerics, no freeing required */
	} else {
	    error("Left hand side of += is a number (or zero); right side is not a number.\n");
	}
	break;
#ifndef NO_BUFFER_TYPE
    case T_BUFFER:
	if (sp->type != T_BUFFER) {
	    bad_argument(sp, T_BUFFER, 2, (is_void ? F_VOID_ADD_EQ : F_ADD_EQ));
	} else {
	    buffer_t *b;
	    
	    b = allocate_buffer(lval->u.buf->size + sp->u.buf->size);
	    memcpy(b->item, lval->u.buf->item, lval->u.buf->size);
	    memcpy(b->item + lval->u.buf->size, sp->u.buf->item,
		   sp->u.buf->size);
	    free_buffer(sp->u.buf);
	    free_buffer(lval->u.buf);
	    lval->u.buf = b;
	}
	break;
#endif
    case T_ARRAY:
	if (sp->type != T_ARRAY)
	    bad_argument(sp, T_ARRAY, 2, (is_void ? F_VOID_ADD_EQ : F_ADD_EQ));
	else {
	    /* add_array now frees the arrays */
	    lval->u.arr = add_array(lval->u.arr, sp->u.arr);
	}
	break;
    case T_MAPPING:
	if (sp->type != T_MAPPING)
	    bad_argument(sp, T_MAPPING, 2, (is_void ? F_VOID_ADD_EQ : F_ADD_EQ));
	else {
	    absorb_mapping(lval->u.map, sp->u.map);
	    free_mapping(sp->u.map);	/* free RHS */
	    /* LHS not freed because its being reused */
	}
	break;
    case T_LVALUE_BYTE:
	if (sp->type != T_NUMBER)
	    error("Bad right type to += of char lvalue.\n");
	else *global_lvalue_byte.u.lvalue_byte += sp->u.number;
	break;
    default:
	bad_arg(1, (is_void ? F_VOID_ADD_EQ : F_ADD_EQ));
    }
    
    if (!is_void) {	/* not void add_eq */
	assign_svalue_no_free(sp, lval);
    } else {
	/*
	 * but if (void)add_eq then no need to produce an
	 * rvalue
	 */
	sp--;
    }
}

void c_divide() {
    switch((sp-1)->type|sp->type) {
    case T_NUMBER:
	{
	    if (!(sp--)->u.number) error("Division by zero\n");
	    sp->u.number /= (sp+1)->u.number;
	    break;
	}
	
    case T_REAL:
	{
	    if ((sp--)->u.real == 0.0) error("Division by zero\n");
	    sp->u.real /= (sp+1)->u.real;
	    break;
	}
	
    case T_NUMBER|T_REAL:
	{
	    if ((sp--)->type == T_NUMBER) {
		if (!((sp+1)->u.number)) error("Division by zero\n");
		sp->u.real /= (sp+1)->u.number;
	    } else {
		if ((sp+1)->u.real == 0.0) error("Division by 0.0\n");
		sp->type = T_REAL;
		sp->u.real = sp->u.number / (sp+1)->u.real;
	    }
	    break;
	}
	
    default:
	{
	    if (!((sp-1)->type & (T_NUMBER|T_REAL)))
		bad_argument(sp-1,T_NUMBER|T_REAL,1, F_DIVIDE);
	    if (!(sp->type & (T_NUMBER|T_REAL)))
		bad_argument(sp, T_NUMBER|T_REAL,2, F_DIVIDE);
	}
    }
}

void c_multiply() {
    switch((sp-1)->type|sp->type) {
    case T_NUMBER:
	{
	    sp--;
	    sp->u.number *= (sp+1)->u.number;
	    break;
	}
	
    case T_REAL:
	{
	    sp--;
	    sp->u.real *= (sp+1)->u.real;
	    break;
	}
	
    case T_NUMBER|T_REAL:
	{
	    if ((--sp)->type == T_NUMBER) {
		sp->type = T_REAL;
		sp->u.real = sp->u.number * (sp+1)->u.real;
	    }
	    else sp->u.real *= (sp+1)->u.number;
	    break;
	}
	
    case T_MAPPING:
	{
	    mapping_t *m;
	    m = compose_mapping((sp-1)->u.map, sp->u.map, 1);
	    pop_2_elems();
	    push_refed_mapping(m);
	    break;
	}
	
    default:
	{
	    if (!((sp-1)->type & (T_NUMBER|T_REAL|T_MAPPING)))
		bad_argument(sp-1, T_NUMBER|T_REAL|T_MAPPING,1, F_MULTIPLY);
	    if (!(sp->type & (T_NUMBER|T_REAL|T_MAPPING)))
		bad_argument(sp, T_NUMBER|T_REAL|T_MAPPING,2, F_MULTIPLY);
	    error("Args to * are not compatible.\n");
	}
    }
    
}

void c_inc() {
    DEBUG_CHECK(sp->type != T_LVALUE,
		"non-lvalue argument to ++\n");
    lval = (sp--)->u.lvalue;
    switch (lval->type) {
    case T_NUMBER:
	lval->u.number++;
	break;
    case T_REAL:
	lval->u.real++;
	break;
    case T_LVALUE_BYTE:
	++*global_lvalue_byte.u.lvalue_byte;
	break;
    default:
	error("++ of non-numeric argument\n");
    }
}

void c_dec() {
    svalue_t *lval;

    DEBUG_CHECK(sp->type != T_LVALUE,
		"non-lvalue argument to --\n");
    lval = (sp--)->u.lvalue;
    switch (lval->type) {
    case T_NUMBER:
	lval->u.number--;
	break;
    case T_REAL:
	lval->u.real--;
	break;
    case T_LVALUE_BYTE:
	--(*global_lvalue_byte.u.lvalue_byte);
	break;
    default:
	error("-- of non-numeric argument\n");
    }
}

void c_le() {
    int i = sp->type;

    switch((--sp)->type|i) {
    case T_NUMBER:
	sp->u.number = sp->u.number <= (sp+1)->u.number;
	break;
	
    case T_REAL:
	sp->u.number = sp->u.real <= (sp+1)->u.real;
	sp->type = T_NUMBER;
	break;
	
    case T_NUMBER|T_REAL:
	if (i == T_NUMBER) {
	    sp->type = T_NUMBER;
	    sp->u.number = sp->u.real <= (sp+1)->u.number;
	} else sp->u.number = sp->u.number <= (sp+1)->u.real;
	break;
	
    case T_STRING:
	i = strcmp(sp->u.string, (sp+1)->u.string) <= 0;
	free_string_svalue(sp+1);
	free_string_svalue(sp);
	sp->type = T_NUMBER;
	sp->u.number = i;
	break;
	
    default:
	{
	    switch((sp++)->type) {
	    case T_NUMBER:
	    case T_REAL:
		bad_argument(sp, T_NUMBER | T_REAL, 2, F_LE);
		
	    case T_STRING:
		bad_argument(sp, T_STRING, 2, F_LE);
		
	    default:
		bad_argument(sp - 1, T_NUMBER | T_STRING | T_REAL, 1, F_LE);
	    }
	}
    }
}

void c_lt() {
    int i = sp->type;
    switch (i | (--sp)->type) {
    case T_NUMBER:
	sp->u.number = sp->u.number < (sp+1)->u.number;
	break;
    case T_REAL:
	sp->u.number = sp->u.real < (sp+1)->u.real;
	sp->type = T_NUMBER;
	break;
    case T_NUMBER|T_REAL:
	if (i == T_NUMBER) {
	    sp->type = T_NUMBER;
	    sp->u.number = sp->u.real < (sp+1)->u.number;
	} else sp->u.number = sp->u.number < (sp+1)->u.real;
	break;
    case T_STRING:
	i = (strcmp((sp - 1)->u.string, sp->u.string) < 0);
	free_string_svalue(sp+1);
	free_string_svalue(sp);
	sp->type = T_NUMBER;
	sp->u.number = i;
	break;
    default:
	switch ((sp++)->type) {
	case T_NUMBER:
	case T_REAL:
	    bad_argument(sp, T_NUMBER | T_REAL, 2, F_LT);
	case T_STRING:
	    bad_argument(sp, T_STRING, 2, F_LT);
	default:
	    bad_argument(sp-1, T_NUMBER | T_STRING | T_REAL, 1, F_LT);
	}
    }
}

void c_gt() {
    int i = sp->type;
    switch ((--sp)->type | i) {
    case T_NUMBER:
	sp->u.number = sp->u.number > (sp+1)->u.number;
	break;
    case T_REAL:
	sp->u.number = sp->u.real > (sp+1)->u.real;
	sp->type = T_NUMBER;
	break;
    case T_NUMBER | T_REAL:
	if (i == T_NUMBER) {
	    sp->type = T_NUMBER;
	    sp->u.number = sp->u.real > (sp+1)->u.number;
	} else sp->u.number = sp->u.number > (sp+1)->u.real;
	break;
    case T_STRING:
	i = strcmp(sp->u.string, (sp+1)->u.string) > 0;
	free_string_svalue(sp+1);
	free_string_svalue(sp);
	sp->type = T_NUMBER;
	sp->u.number = i;
	break;
    default:
	{
	    switch ((sp++)->type) {
	    case T_NUMBER:
	    case T_REAL:
		bad_argument(sp, T_NUMBER | T_REAL, 2, F_GT);
	    case T_STRING:
		bad_argument(sp, T_STRING, 2, F_GT);
	    default:
		bad_argument(sp-1, T_NUMBER | T_REAL | T_STRING, 1, F_GT);
	    }
	}
    }
}

void c_ge() {
    int i = sp->type;
    switch ((--sp)->type | i) {
    case T_NUMBER:
	sp->u.number = sp->u.number >= (sp+1)->u.number;
	break;
    case T_REAL:
	sp->u.number = sp->u.real >= (sp+1)->u.real;
	sp->type = T_NUMBER;
	break;
    case T_NUMBER | T_REAL:
	if (i == T_NUMBER) {
	    sp->type = T_NUMBER;
	    sp->u.number = sp->u.real >= (sp+1)->u.number;
	} else sp->u.number = sp->u.number >= (sp+1)->u.real;
	break;
    case T_STRING:
	i = strcmp(sp->u.string, (sp+1)->u.string) >= 0;
	free_string_svalue(sp + 1);
	free_string_svalue(sp);
	sp->type = T_NUMBER;
	sp->u.number = i;
	break;
    default:
	{
	    switch ((sp++)->type) {
	    case T_NUMBER:
	    case T_REAL:
		bad_argument(sp, T_NUMBER | T_REAL, 2, F_GE);
	    case T_STRING:
		bad_argument(sp, T_STRING, 2, F_GE);
	    default:
		bad_argument(sp - 1, T_NUMBER | T_STRING | T_REAL, 1, F_GE);
	    }
	}
    }
}

void c_subtract() {
    int i = (sp--)->type;
    switch (i | sp->type) {
    case T_NUMBER:
	sp->u.number -= (sp+1)->u.number;
	break;
	
    case T_REAL:
	sp->u.real -= (sp+1)->u.real;
	break;
	
    case T_NUMBER | T_REAL:
	if (sp->type == T_REAL) sp->u.real -= (sp+1)->u.number;
	else {
	    sp->type = T_REAL;
	    sp->u.real = sp->u.number - (sp+1)->u.real;
	}
	break;
	
    case T_ARRAY:
	{
	    /*
	     * subtract_array already takes care of
	     * destructed objects
	     */
	    sp->u.arr = subtract_array(sp->u.arr, (sp+1)->u.arr);
	    break;
	}
	
    default:
	if (!((sp++)->type & (T_NUMBER|T_REAL|T_ARRAY)))
	    error("Bad left type to -.\n");
	else if (!(sp->type & (T_NUMBER|T_REAL|T_ARRAY)))
	    error("Bad right type to -.\n");
	else error("Arguments to - do not have compatible types.\n");
    }
}

void c_negate() {
    if (sp->type == T_NUMBER)
	sp->u.number = -sp->u.number;
    else if (sp->type == T_REAL)
	sp->u.real = -sp->u.real;
    else
	error("Bad argument to unary minus\n");
}

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

void c_add() {
    switch (sp->type) {
#ifndef NO_BUFFER_TYPE
    case T_BUFFER:
	{
	    if (!((sp-1)->type == T_BUFFER)) {
		error("Bad type argument to +. Had %s and %s.\n",
		      type_name((sp - 1)->type), type_name(sp->type));
	    } else {
		buffer_t *b;
		
		b = allocate_buffer(sp->u.buf->size + (sp - 1)->u.buf->size);
		memcpy(b->item, (sp - 1)->u.buf->item, (sp - 1)->u.buf->size);
		memcpy(b->item + (sp - 1)->u.buf->size, sp->u.buf->item,
		       sp->u.buf->size);
		free_buffer((sp--)->u.buf);
		free_buffer(sp->u.buf);
		sp->u.buf = b;
	    }
	    break;
	} /* end of x + T_BUFFER */
#endif
    case T_NUMBER:
	{
	    switch ((--sp)->type) {
	    case T_NUMBER:
		sp->u.number += (sp+1)->u.number;
		break;
	    case T_REAL:
		sp->u.real += (sp+1)->u.number;
		break;
	    case T_STRING:
		{
		    char buff[20];
		    
		    sprintf(buff, "%d", (sp+1)->u.number);
		    EXTEND_SVALUE_STRING(sp, buff, "f_add: 2");
		    break;
		}
	    default:
		error("Bad type argument to +.  Had %s and %s.\n",
		      type_name(sp->type), type_name((sp+1)->type));
	    }
	    break;
	} /* end of x + NUMBER */
    case T_REAL:
	{
	    switch ((--sp)->type) {
	    case T_NUMBER:
		sp->type = T_REAL;
		sp->u.real = sp->u.number + (sp+1)->u.real;
		break;
	    case T_REAL:
		sp->u.real += (sp+1)->u.real;
		break;
	    case T_STRING:
		{
		    char buff[40];
		    
		    sprintf(buff, "%f", (sp+1)->u.real);
		    EXTEND_SVALUE_STRING(sp, buff, "f_add: 2");
		    break;
		}
	    default:
		error("Bad type argument to +. Had %s and %s\n",
		      type_name(sp->type), type_name((sp+1)->type));
	    }
	    break;
	} /* end of x + T_REAL */
    case T_ARRAY:
	{
	    if (!((sp-1)->type == T_ARRAY)) {
		error("Bad type argument to +. Had %s and %s\n",
		      type_name((sp - 1)->type), type_name(sp->type));
	    } else {
		/* add_array now free's the arrays */
		(sp-1)->u.arr = add_array((sp - 1)->u.arr, sp->u.arr);
		sp--;
		break;
	    }
	} /* end of x + T_ARRAY */
    case T_MAPPING:
	{
	    if ((sp-1)->type == T_MAPPING) {
		mapping_t *map;
		
		map = add_mapping((sp - 1)->u.map, sp->u.map);
		free_mapping((sp--)->u.map);
		free_mapping(sp->u.map);
		sp->u.map = map;
		break;
	    } else
		error("Bad type argument to +. Had %s and %s\n",
		      type_name((sp - 1)->type), type_name(sp->type));
	} /* end of x + T_MAPPING */
    case T_STRING:
	{
	    switch ((sp-1)->type) {
	    case T_NUMBER:
		{
		    char buff[20];
		    
		    sprintf(buff, "%d", (sp-1)->u.number);
		    SVALUE_STRING_ADD_LEFT(buff, "f_add: 3");
		    break;
		} /* end of T_NUMBER + T_STRING */
	    case T_REAL:
		{
		    char buff[40];
		    
		    sprintf(buff, "%f", (sp - 1)->u.real);
		    SVALUE_STRING_ADD_LEFT(buff, "f_add: 3");
		    break;
		} /* end of T_REAL + T_STRING */
	    case T_STRING:
		{
		    SVALUE_STRING_JOIN(sp-1, sp, "f_add: 1");
		    sp--;
		    break;
		} /* end of T_STRING + T_STRING */
	    default:
		error("Bad type argument to +. Had %s and %s\n",
		      type_name((sp - 1)->type), type_name(sp->type));
	    }
	    break;
	} /* end of x + T_STRING */
	
    default:
	error("Bad type argument to +.  Had %s and %s.\n",
	      type_name((sp-1)->type), type_name(sp->type));
    }
}

int c_loop_cond_compare P2(svalue_t *, s1, svalue_t *, s2) {
    switch (s1->type | s2->type) {
    case T_NUMBER: 
	return s1->u.number < s2->u.number;
    case T_REAL:
	return s1->u.real < s2->u.real;
    case T_STRING:
	return (strcmp(s1->u.string, s2->u.string) < 0);
    case T_NUMBER|T_REAL:
	if (s1->type == T_NUMBER) return s1->u.number < s2->u.real;
	else return s1->u.real < s2->u.number;
    default:
	if (s1->type == T_OBJECT && (s1->u.ob->flags & O_DESTRUCTED)) {
	    free_object(s1->u.ob, "do_loop_cond:1");
	    *s1 = const0u;
	}
	if (s2->type == T_OBJECT && (s2->u.ob->flags & O_DESTRUCTED)) {
	    free_object(s2->u.ob, "do_loop_cond:2");
	    *s2 = const0u;
	}
	if (s1->type == T_NUMBER && s2->type == T_NUMBER)
	    return 0;
	
	switch(s1->type) {
	case T_NUMBER:
	case T_REAL:
	    error("2nd argument to < is not numeric when the 1st is.\n");
	case T_STRING:
	    error("2nd argument to < is not string when the 1st is.\n");
	default:
	    error("Bad 1st argument to <.\n");
	}
    }
    return 0;
}

void c_sscanf P1(int, num_arg) {
    svalue_t *fp;
    int i;

    /*
     * allocate stack frame for rvalues and return value (number of matches);
     * perform some stack manipulation; note: source and template strings are
     * already on the stack by this time
     */
    fp = sp;
    CHECK_STACK_OVERFLOW(num_arg + 1);
    sp += num_arg + 1;
    *sp = *(fp--);		/* move format description to top of stack */
    *(sp - 1) = *(fp);		/* move source string just below the format
				 * desc. */
    fp->type = T_NUMBER;	/* this svalue isn't invalidated below, and
				 * if we don't change it to something safe,
				 * it will get freed twice if an error occurs */
    /*
     * prep area for rvalues
     */
    for (i = 1; i <= num_arg; i++)
	fp[i].type = T_INVALID;

    /*
     * do it...
     */
    i = inter_sscanf(sp - 2, sp - 1, sp, num_arg);

    /*
     * remove source & template strings from top of stack
     */
    pop_2_elems();

    /*
     * save number of matches on stack
     */
    fp->type = T_NUMBER;
    fp->u.number = i;
}

void c_parse_command P1(int, num_arg) {
    svalue_t *arg;
    svalue_t *fp;
    int i;

    /*
     * type checking on first three required parameters to parse_command()
     */
    arg = sp - 2;
    CHECK_TYPES(&arg[0], T_STRING, 1, F_PARSE_COMMAND);
    CHECK_TYPES(&arg[1], T_OBJECT | T_ARRAY, 2, F_PARSE_COMMAND);
    CHECK_TYPES(&arg[2], T_STRING, 3, F_PARSE_COMMAND);

    /*
     * allocate stack frame for rvalues and return value (number of matches);
     * perform some stack manipulation;
     */
    fp = sp;
    CHECK_STACK_OVERFLOW(num_arg + 1);
    sp += num_arg + 1;
    arg = sp;
    *(arg--) = *(fp--);		/* move pattern to top of stack */
    *(arg--) = *(fp--);		/* move source object or array to just below 
				   the pattern */
    *(arg) = *(fp);		/* move source string just below the object */
    fp->type = T_NUMBER;

    /*
     * prep area for rvalues
     */
    for (i = 1; i <= num_arg; i++)
	fp[i].type = T_INVALID;

    /*
     * do it...
     */
    i = parse(arg[0].u.string, &arg[1], arg[2].u.string, &fp[1], num_arg);

    /*
     * remove mandatory parameters
     */
    pop_3_elems();

    /*
     * save return value on stack
     */
    fp->u.number = i;
}

void c_prepare_catch P1(error_context_t *, econ) {
    if (!save_context(econ))
	error("Can't catch too deep recursion error.\n");
    push_control_stack(FRAME_CATCH);
#if defined(DEBUG) || defined(TRACE_CODE)
    csp->num_local_variables = (csp - 1)->num_local_variables;	/* marion */
#endif
    assign_svalue(&catch_value, &const1);
}

void c_caught_error P1(error_context_t *, econ) {
    restore_context(econ);
    STACK_INC;
    *sp = catch_value;
    catch_value = const1;

    /* if it's too deep or max eval, we can't let them catch it */
    pop_context(econ);
    if (max_eval_error)
	error("Can't catch eval cost too big error.\n");
    if (too_deep_error)
	error("Can't catch too deep recursion error.\n");
}
    
void c_end_catch P1(error_context_t *, econ) {
    free_svalue(&catch_value, "F_END_CATCH");
    catch_value = const0;
    /* We come here when no longjmp() was executed */
    pop_control_stack();
    push_number(0);
    pop_context(econ);
}

static int compare_switch_entries P2(string_switch_entry_t *, p1,
				     string_switch_entry_t *, p2) {
    return ((POINTER_INT)p1->string - (POINTER_INT)p2->string);
}

#ifdef DEBUGMALLOC_EXTENSIONS
typedef struct msl_s {
    struct msl_s *next;
    string_switch_entry_t **tables;
} msl_t;

static msl_t *g_msl_tables = 0;

static void add_switch_list P1(string_switch_entry_t **, tables) {
    msl_t *new;
        
    new = ALLOCATE(msl_t, TAG_DEBUGMALLOC, "add_switch_list");
    new->next = g_msl_tables;
    new->tables = tables;
}
	
void mark_switch_lists PROT((void)) {
    string_switch_entry_t *p, **tables;
    msl_t *msl = g_msl_tables;
    
    while (msl) {
	tables = msl->tables;
	msl = msl->next;
	while (*tables) {
	    p = *tables++;
	    while (p->string) {
		EXTRA_REF(BLOCK(p->string))++;
		p++;
	    }
	}
    }
}
#endif

void fix_switches P1(string_switch_entry_t **, tables) {
    string_switch_entry_t *p;

#ifdef DEBUGMALLOC_EXTENSIONS
    add_switch_list(tables);
#endif
    while (*tables) {
	p = *tables;
	while (p->string) {
	    p->string = make_shared_string(p->string);
	    p++;
	}
	quickSort((char *)(*tables), p - *tables , 
		  sizeof(string_switch_entry_t), compare_switch_entries);
	tables++;
    }
}

int c_string_switch_lookup P3(svalue_t *, str, string_switch_entry_t *, table,
			      int, table_size) {
    char *the_string;

    if (str->subtype == STRING_SHARED)
	the_string = str->u.string;
    else {
	if (!(the_string = findstring(str->u.string)))
	    return -1;
    }

    /* this should use a binary search, but for now ... */
    while (table->string) {
	if (the_string == table->string) return table->index;
	table++;
    }
    return -1;
}

void c_evaluate P1(int, num) {
    svalue_t *v;
    svalue_t *arg = sp - num + 1;

    if (arg->type != T_FUNCTION) {
	pop_n_elems(num-1);
	return;
    }
    if (current_object->flags & O_DESTRUCTED) {
	pop_n_elems(num);
	push_undefined();
	return;
    }
    v = call_function_pointer(arg->u.fp, num - 1);
    free_funp(arg->u.fp);
    assign_svalue_no_free(sp, v);
}

int c_range_switch_lookup P3(int, num, range_switch_entry_t *, table,
			     int, table_size) {
    /* this should also be a better search method */
    
    while (table->index2 != -2) {
	if (table->index2 == -1) {
	    if (table->index1 <= num && num <= (table+1)->index1)
		return (table+1)->index2;
	    table += 2;
	} else {
	    if (table->index1 == num) return table->index2;
	    table++;
	}
    }
    return 0;
}

void c_make_ref P1(int, op) {
    ref_t *ref;

    /* global and local refs need no protection since they are
     * guaranteed to outlive the current scope.  Lvalues inside
     * structures may not, however ...
     */
    ref->lvalue = sp->u.lvalue;
    if (op != F_GLOBAL_LVALUE && op != F_LOCAL_LVALUE) {
	ref->sv.type = lv_owner_type;
	ref->sv.subtype = STRING_MALLOC; /* ignored if non-string */
	if (lv_owner_type == T_STRING) {
	    ref->sv.u.string = (char *)lv_owner;
	    INC_COUNTED_REF(lv_owner);
	    ADD_STRING(MSTR_SIZE(lv_owner));
	    NDBG(BLOCK(lv_owner));
	} else {
	    ref->sv.u.refed = lv_owner;
	    lv_owner->ref++;
	    if (lv_owner_type == T_MAPPING)
		((mapping_t *)lv_owner)->count |= MAP_LOCKED;
	}
    } else {
	ref->sv.type = T_NUMBER;
    }
    sp->type = T_REF;
    sp->u.ref = ref;
}

void c_kill_refs P1(int, num) {
    while (num--)
	kill_ref(global_ref_list);
}

#endif
