fbmuck-6.05/auto/
fbmuck-6.05/contrib/jresolver/
fbmuck-6.05/contrib/jresolver/org/
fbmuck-6.05/contrib/jresolver/org/fuzzball/
fbmuck-6.05/docs/devel/
fbmuck-6.05/game/
fbmuck-6.05/game/logs/
fbmuck-6.05/game/muf/
fbmuck-6.05/scripts/
fbmuck-6.05/src_docs/
/* Primitives package */

#include "copyright.h"
#include "config.h"

#include <stdio.h>
#include <ctype.h>

#include "db.h"
#include "inst.h"
#include "externs.h"
#include "match.h"
#include "interface.h"
#include "params.h"
#include "tune.h"
#include "fbstrings.h"
#include "interp.h"

static struct inst *oper1, *oper2, *oper3, *oper4;
static struct inst temp1, temp2, temp3;
static int result, tmp;
static dbref ref;
static char buf[BUFFER_LEN];

void
prim_pop(PRIM_PROTOTYPE)
{
	CHECKOP(1);
	oper1 = POP();
	CLEAR(oper1);
}

void
prim_dup(PRIM_PROTOTYPE)
{
	CHECKOP_READONLY(1);
	nargs = 0;
	CHECKOFLOW(1);
	copyinst(&arg[*top - 1], &arg[*top]);
	(*top)++;
}

void
prim_popn(PRIM_PROTOTYPE)
{
	int i, count;

	CHECKOP(1);
	oper1 = POP();
	if (oper1->type != PROG_INTEGER)
		abort_interp("Operand not an integer.");
	count = oper1->data.number;
	CLEAR(oper1);
	if (count < 0)
		abort_interp("Operand is negative.");
	for (i = count; i > 0; i--) {
		CHECKOP(1);
		oper1 = POP();
		CLEAR(oper1);
	}
}

void
prim_dupn(PRIM_PROTOTYPE)
{
	int i;

	CHECKOP(1);
	oper1 = POP();
	if (oper1->type != PROG_INTEGER)
		abort_interp("Operand is not an integer.");
	result = oper1->data.number;
	if (result < 0)
		abort_interp("Operand is negative.");
	CLEAR(oper1);
	CHECKOP(result);
	nargs = 0;
	CHECKOFLOW(result);
	for (i = result; i > 0; i--) {
		copyinst(&arg[*top - result], &arg[*top]);
		(*top)++;
	}
}


void
prim_ldup(PRIM_PROTOTYPE)
{
	int i;

	CHECKOP_READONLY(1);
	nargs = 0;

	if (arg[*top - 1].type != PROG_INTEGER)
		abort_interp("Operand is not an integer.");

	result = arg[*top - 1].data.number;

	if (result < 0)
		abort_interp("Operand is negative.");

	result++;
	CHECKOP_READONLY(result);
	nargs = 0;
	CHECKOFLOW(result);

	for (i = result; i > 0; i--) {
		copyinst(&arg[*top - result], &arg[*top]);
		(*top)++;
	}
}


void
prim_at(PRIM_PROTOTYPE)
{
	CHECKOP(1);
	temp1 = *(oper1 = POP());
	if ((temp1.type != PROG_VAR) && (temp1.type != PROG_LVAR) && (temp1.type != PROG_SVAR))
		abort_interp("Non-variable argument.");
	if (temp1.data.number >= MAX_VAR || temp1.data.number < 0)
		abort_interp("Variable number out of range.");
	if (temp1.type == PROG_LVAR) {
		/* LOCALVAR */
		struct localvars *tmp = localvars_get(fr, program);
		copyinst(&(tmp->lvars[temp1.data.number]), &arg[(*top)++]);
	} else if (temp1.type == PROG_VAR) {
		/* GLOBALVAR */
		copyinst(&(fr->variables[temp1.data.number]), &arg[(*top)++]);
	} else {
		/* SCOPEDVAR */
		struct inst *tmp;

		tmp = scopedvar_get(fr, 0, temp1.data.number);
		if (!tmp)
			abort_interp("Scoped variable number out of range.");
		copyinst(tmp, &arg[(*top)++]);
	}
	CLEAR(&temp1);
}

void
prim_bang(PRIM_PROTOTYPE)
{
	CHECKOP(2);
	oper1 = POP();
	oper2 = POP();
	if ((oper1->type != PROG_VAR) && (oper1->type != PROG_LVAR) && (oper1->type != PROG_SVAR))
		abort_interp("Non-variable argument (2)");
	if (oper1->data.number >= MAX_VAR || oper1->data.number < 0)
		abort_interp("Variable number out of range. (2)");
	if (oper1->type == PROG_LVAR) {
		/* LOCALVAR */
		struct localvars *tmp = localvars_get(fr, program);
		CLEAR(&(tmp->lvars[oper1->data.number]));
		copyinst(oper2, &(tmp->lvars[oper1->data.number]));
	} else if (oper1->type == PROG_VAR) {
		/* GLOBALVAR */
		CLEAR(&(fr->variables[oper1->data.number]));
		copyinst(oper2, &(fr->variables[oper1->data.number]));
	} else {
		/* SCOPEDVAR */
		struct inst *tmp;

		tmp = scopedvar_get(fr, 0, oper1->data.number);
		if (!tmp)
			abort_interp("Scoped variable number out of range.");
		CLEAR(tmp);
		copyinst(oper2, tmp);
	}
	CLEAR(oper1);
	CLEAR(oper2);
}

void
prim_var(PRIM_PROTOTYPE)
{
	CHECKOP(1);
	oper1 = POP();
	if (oper1->type != PROG_INTEGER)
		abort_interp("Non-integer argument.");
	result = oper1->data.number;
	CLEAR(oper1);
	push(arg, top, PROG_VAR, MIPSCAST & result);
}

void
prim_localvar(PRIM_PROTOTYPE)
{
	CHECKOP(1);
	oper1 = POP();
	if (oper1->type != PROG_INTEGER)
		abort_interp("Non-integer argument.");
	result = oper1->data.number;
	CLEAR(oper1);
	push(arg, top, PROG_LVAR, MIPSCAST & result);
}

void
prim_swap(PRIM_PROTOTYPE)
{
	CHECKOP(2);
	oper1 = POP();
	temp2 = *(oper2 = POP());
	arg[(*top)++] = *oper1;
	arg[(*top)++] = temp2;
	/* don't clean! */
}

void
prim_over(PRIM_PROTOTYPE)
{
	CHECKOP_READONLY(2);
	CHECKOFLOW(1);
	copyinst(&arg[*top - 2], &arg[*top]);
	(*top)++;
}

void
prim_pick(PRIM_PROTOTYPE)
{
	CHECKOP(1);
	temp1 = *(oper1 = POP());
	if (temp1.type != PROG_INTEGER || temp1.data.number <= 0)
		abort_interp("Operand not a positive integer.");
	CHECKOP_READONLY(temp1.data.number);
	copyinst(&arg[*top - temp1.data.number], &arg[*top]);
	(*top)++;
}

void
prim_put(PRIM_PROTOTYPE)
{
	CHECKOP(2);
	oper1 = POP();
	oper2 = POP();
	if (oper1->type != PROG_INTEGER || oper1->data.number <= 0)
		abort_interp("Operand not a positive integer.");
	tmp = oper1->data.number;
	CHECKOP(tmp);
	CLEAR(&arg[*top - tmp]);
	copyinst(oper2, &arg[*top - tmp]);
	CLEAR(oper1);
	CLEAR(oper2);
}

void
prim_rot(PRIM_PROTOTYPE)
{
	CHECKOP(3);
	oper1 = POP();
	oper2 = POP();
	temp3 = *(oper3 = POP());
	arg[(*top)++] = *oper2;
	arg[(*top)++] = *oper1;
	arg[(*top)++] = temp3;
}

void
prim_rotate(PRIM_PROTOTYPE)
{
	CHECKOP(1);
	oper1 = POP();
	if (oper1->type != PROG_INTEGER)
		abort_interp("Invalid argument type.");
	tmp = oper1->data.number;	/* Depth on stack */
	CHECKOP(abs(tmp));
	if (tmp > 0) {
		temp2 = arg[*top - tmp];
		for (; tmp > 0; tmp--)
			arg[*top - tmp] = arg[*top - tmp + 1];
		arg[*top - 1] = temp2;
	} else if (tmp < 0) {
		temp2 = arg[*top - 1];
		for (tmp = -1; tmp > oper1->data.number; tmp--)
			arg[*top + tmp] = arg[*top + tmp - 1];
		arg[*top + tmp] = temp2;
	}
	CLEAR(oper1);
}

void
prim_dbtop(PRIM_PROTOTYPE)
{
	CHECKOP(0);
	ref = (dbref) db_top;
	CHECKOFLOW(1);
	PushObject(ref);
}

void
prim_depth(PRIM_PROTOTYPE)
{
	CHECKOP(0);
	result = *top;
	CHECKOFLOW(1);
	PushInt(result);
}

void
prim_version(PRIM_PROTOTYPE)
{
	CHECKOP(0);
	CHECKOFLOW(1);
	PushString(VERSION);
}

void
prim_prog(PRIM_PROTOTYPE)
{
	CHECKOP(0);
	ref = (dbref) program;
	CHECKOFLOW(1);
	PushObject(ref);
}

void
prim_trig(PRIM_PROTOTYPE)
{
	CHECKOP(0);
	ref = (dbref) fr->trig;
	CHECKOFLOW(1);
	PushObject(ref);
}

void
prim_caller(PRIM_PROTOTYPE)
{
	CHECKOP(0);
	ref = (dbref) fr->caller.st[fr->caller.top - 1];
	CHECKOFLOW(1);
	PushObject(ref);
}

void
prim_intp(PRIM_PROTOTYPE)
{
	CHECKOP(1);
	oper1 = POP();
	result = (oper1->type == PROG_INTEGER);
	CLEAR(oper1);
	PushInt(result);
}

void
prim_floatp(PRIM_PROTOTYPE)
{
	CHECKOP(1);
	oper1 = POP();
	result = (oper1->type == PROG_FLOAT);
	CLEAR(oper1);
	PushInt(result);
}

void
prim_arrayp(PRIM_PROTOTYPE)
{
	CHECKOP(1);
	oper1 = POP();
	result = (oper1->type == PROG_ARRAY);
	CLEAR(oper1);
	PushInt(result);
}

void
prim_dictionaryp(PRIM_PROTOTYPE)
{
	CHECKOP(1);
	oper1 = POP();
	result = (oper1->type == PROG_ARRAY && oper1->data.array &&
			  oper1->data.array->type == ARRAY_DICTIONARY);
	CLEAR(oper1);
	PushInt(result);
}

void
prim_stringp(PRIM_PROTOTYPE)
{
	CHECKOP(1);
	oper1 = POP();
	result = (oper1->type == PROG_STRING);
	CLEAR(oper1);
	PushInt(result);
}

void
prim_dbrefp(PRIM_PROTOTYPE)
{
	CHECKOP(1);
	oper1 = POP();
	result = (oper1->type == PROG_OBJECT);
	CLEAR(oper1);
	PushInt(result);
}

void
prim_addressp(PRIM_PROTOTYPE)
{
	CHECKOP(1);
	oper1 = POP();
	result = (oper1->type == PROG_ADD);
	CLEAR(oper1);
	PushInt(result);
}

void
prim_lockp(PRIM_PROTOTYPE)
{
	CHECKOP(1);
	oper1 = POP();
	result = (oper1->type == PROG_LOCK);
	CLEAR(oper1);
	PushInt(result);
}


#define ABORT_CHECKARGS(msg) { if (*top == stackpos+1) snprintf(zbuf, sizeof(zbuf), "%s (top)", msg); else snprintf(zbuf, sizeof(zbuf), "%s (top-%d)", msg, ((*top)-stackpos-1));  abort_interp(zbuf); }

#define MaxComplexity 18		/* A truly ridiculously high number! */

void
prim_checkargs(PRIM_PROTOTYPE)
{
	int currpos, stackpos;
	int rngstktop = 0;
	enum {
		itsarange, itsarepeat
	} rngstktyp[MaxComplexity];
	int rngstkpos[MaxComplexity];
	int rngstkcnt[MaxComplexity];
	char zbuf[BUFFER_LEN];

	CHECKOP(1);
	oper1 = POP();				/* string argument */
	if (oper1->type != PROG_STRING)
		abort_interp("Non string argument.");
	if (!oper1->data.string) {
		/* if null string, then no args expected. */
		CLEAR(oper1);
		return;
	}
	strcpy(buf, oper1->data.string->data);	/* copy into local buffer */
	currpos = strlen(buf) - 1;
	stackpos = *top - 1;

	while (currpos >= 0) {
		if (isdigit(buf[currpos])) {
			if (rngstktop >= MaxComplexity)
				abort_interp("Argument expression ridiculously complex.");
			tmp = 1;
			result = 0;
			while ((currpos >= 0) && isdigit(buf[currpos])) {
				result = result + (tmp * (buf[currpos] - '0'));
				tmp = tmp * 10;
				currpos--;
			}
			if (result == 0)
				abort_interp("Bad multiplier '0' in argument expression.");
			if (result >= STACK_SIZE)
				abort_interp("Multiplier too large in argument expression.");
			rngstktyp[rngstktop] = itsarepeat;
			rngstkcnt[rngstktop] = result;
			rngstkpos[rngstktop] = currpos;
			rngstktop++;
		} else if (buf[currpos] == '}') {
			if (rngstktop >= MaxComplexity)
				abort_interp("Argument expression ridiculously complex.");
			if (stackpos < 0)
				ABORT_CHECKARGS("Stack underflow.");
			if (arg[stackpos].type != PROG_INTEGER)
				ABORT_CHECKARGS("Expected an integer range counter.");
			result = arg[stackpos].data.number;
			if (result < 0)
				ABORT_CHECKARGS("Range counter should be non-negative.");
			rngstkpos[rngstktop] = currpos - 1;
			rngstkcnt[rngstktop] = result;
			rngstktyp[rngstktop] = itsarange;
			rngstktop++;
			currpos--;
			if (result == 0) {
				while ((currpos > 0) && (buf[currpos] != '{'))
					currpos--;
			}
			stackpos--;
		} else if (buf[currpos] == '{') {
			if (rngstktop <= 0)
				abort_interp("Mismatched { in argument expression");
			if (rngstktyp[rngstktop - 1] != itsarange)
				abort_interp("Misformed argument expression.");
			if (--rngstkcnt[rngstktop - 1] > 0) {
				currpos = rngstkpos[rngstktop - 1];
			} else {
				rngstktop--;
				currpos--;
				if (rngstktop && (rngstktyp[rngstktop - 1] == itsarepeat)) {
					if (--rngstkcnt[rngstktop - 1] > 0) {
						currpos = rngstkpos[rngstktop - 1];
					} else {
						rngstktop--;
					}
				}
			}
		} else {
			switch (buf[currpos]) {
			case 'i':
				if (stackpos < 0)
					ABORT_CHECKARGS("Stack underflow.");
				if (arg[stackpos].type != PROG_INTEGER)
					ABORT_CHECKARGS("Expected an integer.");
				break;
			case 'n':
				if (stackpos < 0)
					ABORT_CHECKARGS("Stack underflow.");
				if (arg[stackpos].type != PROG_FLOAT)
					ABORT_CHECKARGS("Expected a float.");
				break;
			case 's':
			case 'S':
				if (stackpos < 0)
					ABORT_CHECKARGS("Stack underflow.");
				if (arg[stackpos].type != PROG_STRING)
					ABORT_CHECKARGS("Expected a string.");
				if (buf[currpos] == 'S' && !arg[stackpos].data.string)
					ABORT_CHECKARGS("Expected a non-null string.");
				break;
			case 'd':
			case 'p':
			case 'r':
			case 't':
			case 'e':
			case 'f':
			case 'D':
			case 'P':
			case 'R':
			case 'T':
			case 'E':
			case 'F':
				if (stackpos < 0)
					ABORT_CHECKARGS("Stack underflow.");
				if (arg[stackpos].type != PROG_OBJECT)
					ABORT_CHECKARGS("Expected a dbref.");
				ref = arg[stackpos].data.objref;
				if ((ref >= db_top) || (ref < HOME))
					ABORT_CHECKARGS("Invalid dbref.");
				switch (buf[currpos]) {
				case 'D':
					if ((ref < 0) && (ref != HOME))
						ABORT_CHECKARGS("Invalid dbref.");
					if (Typeof(ref) == TYPE_GARBAGE)
						ABORT_CHECKARGS("Invalid dbref.");
				case 'd':
					if (ref < HOME)
						ABORT_CHECKARGS("Invalid dbref.");
					break;

				case 'P':
					if (ref < 0)
						ABORT_CHECKARGS("Expected player dbref.");
				case 'p':
					if ((ref >= 0) && (Typeof(ref) != TYPE_PLAYER))
						ABORT_CHECKARGS("Expected player dbref.");
					if (ref == HOME)
						ABORT_CHECKARGS("Expected player dbref.");
					break;

				case 'R':
					if ((ref < 0) && (ref != HOME))
						ABORT_CHECKARGS("Expected room dbref.");
				case 'r':
					if ((ref >= 0) && (Typeof(ref) != TYPE_ROOM))
						ABORT_CHECKARGS("Expected room dbref.");
					break;

				case 'T':
					if (ref < 0)
						ABORT_CHECKARGS("Expected thing dbref.");
				case 't':
					if ((ref >= 0) && (Typeof(ref) != TYPE_THING))
						ABORT_CHECKARGS("Expected thing dbref.");
					if (ref == HOME)
						ABORT_CHECKARGS("Expected player dbref.");
					break;

				case 'E':
					if (ref < 0)
						ABORT_CHECKARGS("Expected exit dbref.");
				case 'e':
					if ((ref >= 0) && (Typeof(ref) != TYPE_EXIT))
						ABORT_CHECKARGS("Expected exit dbref.");
					if (ref == HOME)
						ABORT_CHECKARGS("Expected player dbref.");
					break;

				case 'F':
					if (ref < 0)
						ABORT_CHECKARGS("Expected program dbref.");
				case 'f':
					if ((ref >= 0) && (Typeof(ref) != TYPE_PROGRAM))
						ABORT_CHECKARGS("Expected program dbref.");
					if (ref == HOME)
						ABORT_CHECKARGS("Expected player dbref.");
					break;
				}
				break;
			case '?':
				if (stackpos < 0)
					ABORT_CHECKARGS("Stack underflow.");
				break;
			case 'l':
				if (stackpos < 0)
					ABORT_CHECKARGS("Stack underflow.");
				if (arg[stackpos].type != PROG_LOCK)
					ABORT_CHECKARGS("Expected a lock boolean expression.");
				break;
			case 'v':
				if (stackpos < 0)
					ABORT_CHECKARGS("Stack underflow.");
				if (arg[stackpos].type != PROG_VAR &&
					arg[stackpos].type != PROG_LVAR &&
					arg[stackpos].type != PROG_SVAR
				) {
					ABORT_CHECKARGS("Expected a variable.");
				}
				break;
			case 'a':
				if (stackpos < 0)
					ABORT_CHECKARGS("Stack underflow.");
				if (arg[stackpos].type != PROG_ADD)
					ABORT_CHECKARGS("Expected a function address.");
				break;
			case ' ':
				/* this is meaningless space.  Ignore it. */
				stackpos++;
				break;
			default:
				abort_interp("Unkown argument type in expression.");
				break;
			}

			currpos--;			/* decrement string index */
			stackpos--;			/* move on to next stack item down */

			/* are we expecting a repeat of the last argument or range? */
			if ((rngstktop > 0) && (rngstktyp[rngstktop - 1] == itsarepeat)) {
				/* is the repeat is done yet? */
				if (--rngstkcnt[rngstktop - 1] > 0) {
					/* no, repeat last argument or range */
					currpos = rngstkpos[rngstktop - 1];
				} else {
					/* yes, we're done with this repeat */
					rngstktop--;
				}
			}
		}
	}							/* while loop */

	if (rngstktop > 0)
		abort_interp("Badly formed argument expression.");
	/* Oops. still haven't finished a range or repeat expression. */

	CLEAR(oper1);				/* clear link to shared string */
}

#undef ABORT_CHECKARGS


void
prim_mode(PRIM_PROTOTYPE)
{
	CHECKOP(0);
	result = fr->multitask;
	CHECKOFLOW(1);
	PushInt(result);
}

void
prim_mark(PRIM_PROTOTYPE)
{
	CHECKOP(0);
	CHECKOFLOW(1);
	PushMark();
}

void
prim_findmark(PRIM_PROTOTYPE)
{
	int depth, height, count;

	CHECKOP(0);
	depth = 1;
	height = *top - 1;
	while (height >= 0 && arg[height].type != PROG_MARK) {
		height--;
		depth++;
	}
	count = depth - 1;
	if (height < 0)
		abort_interp("No matching mark on stack!");
	if (depth > 1) {
		temp2 = arg[*top - depth];
		for (; depth > 1; depth--)
			arg[*top - depth] = arg[*top - depth + 1];
		arg[*top - 1] = temp2;
	}
	oper1 = POP();
	CLEAR(oper1);
	PushInt(count);
}


void
prim_setmode(PRIM_PROTOTYPE)
{
	CHECKOP(1);
	oper1 = POP();
	if (oper1->type != PROG_INTEGER)
		abort_interp("Invalid argument type.");
	result = oper1->data.number;
	switch (result) {
	case BACKGROUND:
		fr->been_background = 1;
		fr->writeonly = 1;
		break;
	case FOREGROUND:
		if (fr->been_background)
			abort_interp("Cannot FOREGROUND a BACKGROUNDed program.");
		break;
	case PREEMPT:
		break;
	default:
		abort_interp("Invalid mode.");
		break;
	}
	fr->multitask = result;
	CLEAR(oper1);
}


void
prim_interp(PRIM_PROTOTYPE)
{
	struct inst *oper1 = NULL; /* prevents re-entrancy issues! */
	struct inst *oper2 = NULL; /* prevents re-entrancy issues! */
	struct inst *oper3 = NULL; /* prevents re-entrancy issues! */

	struct inst *rv=NULL;
	char buf[BUFFER_LEN];
	struct frame *tmpfr;

	CHECKOP(3);
	oper3 = POP();				/* string -- top stack argument */
	oper2 = POP();				/* dbref  --  trigger */
	oper1 = POP();				/* dbref  --  Program to run */

	if (!valid_object(oper1) || Typeof(oper1->data.objref) != TYPE_PROGRAM)
		abort_interp("Bad program reference. (1)");
	if (!valid_object(oper2))
		abort_interp("Bad object. (2)");
	if ((mlev < 3) && !permissions(ProgUID, oper2->data.objref))
		abort_interp("Permission denied.");
	if (fr->level > 8)
		abort_interp("Interp call loops not allowed.");
	CHECKREMOTE(oper2->data.objref);

	strcpy(buf, match_args);
	strcpy(match_args, oper3->data.string ? oper3->data.string->data : "");
	tmpfr = interp(fr->descr, player, DBFETCH(player)->location, oper1->data.objref,
				   oper2->data.objref, PREEMPT, STD_HARDUID, 0);
	if (tmpfr) {
		rv = interp_loop(player, oper1->data.objref, tmpfr, 1);
	}
	strcpy(match_args, buf);

	CLEAR(oper3);
	CLEAR(oper2);
	CLEAR(oper1);

	if (rv) {
		if (rv->type < PROG_STRING) {
			push(arg, top, rv->type, MIPSCAST(&rv->data.number));
		} else {
			push(arg, top, rv->type, MIPSCAST(rv->data.string));
		}
	} else {
		PushNullStr;
	}

}


/* Internal stack primitives */

struct forvars *push_for(struct forvars *);
struct forvars *pop_for(struct forvars *);

void
prim_for(PRIM_PROTOTYPE)
{
	int fortop;

	CHECKOP(3);
	oper3 = POP();				/* step */
	oper2 = POP();				/* end */
	oper1 = POP();				/* start */

	if (oper1->type != PROG_INTEGER)
		abort_interp("Starting count expected. (1)");
	if (oper2->type != PROG_INTEGER)
		abort_interp("Ending count expected. (2)");
	if (oper3->type != PROG_INTEGER)
		abort_interp("Step count expected. (3)");
	if (fr->fors.top >= STACK_SIZE)
		abort_interp("Too many nested FOR loops.");

	fortop = fr->fors.top++;
	fr->fors.st = push_for(fr->fors.st);
	copyinst(oper1, &fr->fors.st->cur);
	copyinst(oper2, &fr->fors.st->end);
	fr->fors.st->step = oper3->data.number;
	fr->fors.st->didfirst = 0;

	if (fr->trys.st)
		fr->trys.st->for_count++;

	CLEAR(oper1);
	CLEAR(oper2);
	CLEAR(oper3);
}


void
prim_foreach(PRIM_PROTOTYPE)
{
	int fortop;

	CHECKOP(1);
	oper1 = POP();

	if (oper1->type != PROG_ARRAY)
		abort_interp("Array argument expected. (1)");
	if (fr->fors.top >= STACK_SIZE)
		abort_interp("Too many nested FOR loops.");

	fortop = fr->fors.top++;
	fr->fors.st = push_for(fr->fors.st);

	fr->fors.st->cur.type = PROG_INTEGER;
	fr->fors.st->cur.line = 0;
	fr->fors.st->cur.data.number = 0;

	if (fr->trys.st)
		fr->trys.st->for_count++;

	copyinst(oper1, &fr->fors.st->end);
	fr->fors.st->step = 0;
	fr->fors.st->didfirst = 0;

	CLEAR(oper1);
}


void
prim_foriter(PRIM_PROTOTYPE)
{
	CHECKOP(0);

	if (!fr->fors.st)
		abort_interp("Internal error; FOR stack underflow.");

	if (fr->fors.st->end.type == PROG_ARRAY) {
		stk_array *arr = fr->fors.st->end.data.array;

		if (fr->fors.st->didfirst) {
			result = array_next(arr, &fr->fors.st->cur);
		} else {
			result = array_first(arr, &fr->fors.st->cur);
			fr->fors.st->didfirst = 1;
		}
		if (result) {
			array_data *val = array_getitem(arr, &fr->fors.st->cur);

			if (val) {
				CHECKOFLOW(2);
				PushInst(&fr->fors.st->cur);	/* push key onto stack */
				PushInst(val);	/* push value onto stack */
				tmp = 1;		/* tell following IF to NOT branch out of loop */
			} else {
				tmp = 0;		/* tell following IF to branch out of loop */
			}
		} else {
			fr->fors.st->cur.type = PROG_INTEGER;
			fr->fors.st->cur.line = 0;
			fr->fors.st->cur.data.number = 0;
			tmp = 0;			/* tell following IF to branch out of loop */
		}
	} else {
		int cur = fr->fors.st->cur.data.number;
		int end = fr->fors.st->end.data.number;

		tmp = fr->fors.st->step > 0;
		if (tmp)
			tmp = !(cur > end);
		else
			tmp = !(cur < end);
		if (tmp) {
			CHECKOFLOW(1);
			result = cur;
			fr->fors.st->cur.data.number += fr->fors.st->step;
			PushInt(result);
		}
	}
	CHECKOFLOW(1);
	PushInt(tmp);
}


void
prim_forpop(PRIM_PROTOTYPE)
{
	CHECKOP(0);

	if (!(fr->fors.top))
		abort_interp("Internal error; FOR stack underflow.");

	CLEAR(&fr->fors.st->cur);
	CLEAR(&fr->fors.st->end);

	if (fr->trys.st)
		fr->trys.st->for_count--;
	fr->fors.top--;
	fr->fors.st = pop_for(fr->fors.st);
}


struct tryvars *push_try(struct tryvars *);
struct tryvars *pop_try(struct tryvars *);

void
prim_trypop(PRIM_PROTOTYPE)
{
	CHECKOP(0);

	if (!(fr->trys.top))
		abort_interp("Internal error; TRY stack underflow.");

	fr->trys.top--;
	fr->trys.st = pop_try(fr->trys.st);
}


void
prim_reverse(PRIM_PROTOTYPE)
{
	int i;

	CHECKOP(1);
	oper1 = POP();
	if (oper1->type != PROG_INTEGER)
		abort_interp("Invalid argument type.");
	tmp = oper1->data.number;	/* Depth on stack */
	if (tmp < 0)
		abort_interp("Argument must be positive.");
	CHECKOP(tmp);
	if (tmp > 0) {
		for (i = 0; i < (tmp / 2); i++) {
			temp2 = arg[*top - (tmp - i)];
			arg[*top - (tmp - i)] = arg[*top - (i + 1)];
			arg[*top - (i + 1)] = temp2;
		}
	}
	CLEAR(oper1);
}


void
prim_lreverse(PRIM_PROTOTYPE)
{
	int i;

	CHECKOP(1);
	oper1 = POP();
	if (oper1->type != PROG_INTEGER)
		abort_interp("Invalid argument type.");
	tmp = oper1->data.number;	/* Depth on stack */
	if (tmp < 0)
		abort_interp("Argument must be positive.");
	CHECKOP(tmp);
	if (tmp > 0) {
		for (i = 0; i < (tmp / 2); i++) {
			temp2 = arg[*top - (tmp - i)];
			arg[*top - (tmp - i)] = arg[*top - (i + 1)];
			arg[*top - (i + 1)] = temp2;
		}
	}
	CLEAR(oper1);
	PushInt(tmp);
}