fbmuck-6.01/contrib/jresolver/
fbmuck-6.01/contrib/jresolver/org/
fbmuck-6.01/contrib/jresolver/org/fuzzball/
fbmuck-6.01/docs/devel/
fbmuck-6.01/game/
fbmuck-6.01/game/logs/
fbmuck-6.01/game/muf/
fbmuck-6.01/scripts/
fbmuck-6.01/src_docs/
/*
 *  Compile.c   (This is really a tokenizer, not a compiler)
 */

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

#include "db.h"
#include "props.h"
#include "interface.h"
#include "inst.h"
#include "externs.h"
#include "params.h"
#include "tune.h"
#include "match.h"
#include "interp.h"
#include <ctype.h>
#include <time.h>
#include <stdarg.h>

/* This file contains code for doing "byte-compilation" of
   mud-forth programs.  As such, it contains many internal
   data structures and other such which are not found in other
   parts of TinyMUCK.                                       */

/* The CONTROL_STACK is a stack for holding previous control statements.
   This is used to resolve forward references for IF/THEN and loops, as well
   as a placeholder for back references for loops. */

#define CTYPE_IF    1
#define CTYPE_ELSE  2
#define CTYPE_BEGIN 3
#define CTYPE_FOR   4			/* Get it?  CTYPE_FOUR!!  HAHAHAHAHA  -Fre'ta */
								/* C-4?  *BOOM!*  -Revar */
#define CTYPE_WHILE 5
#define CTYPE_TRY   6			/* reserved for exception handling */
#define CTYPE_CATCH 7			/* reserved for exception handling */


/* These would be constants, but their value isn't known until runtime. */
static int IN_FORITER;
static int IN_FOREACH;
static int IN_FORPOP;
static int IN_FOR;
static int IN_TRYPOP;


static hash_tab primitive_list[COMP_HASH_SIZE];

struct CONTROL_STACK {
	short type;
	struct INTERMEDIATE *place;
	struct CONTROL_STACK *next;
	struct CONTROL_STACK *extra;
};

/* This structure is an association list that contains both a procedure
   name and the place in the code that it belongs.  A lookup to the procedure
   will see both it's name and it's number and so we can generate a
   reference to it.  Since I want to disallow co-recursion,  I will not allow
   forward referencing.
   */

struct PROC_LIST {
	const char *name;
	int returntype;
	struct INTERMEDIATE *code;
	struct PROC_LIST *next;
};

/* The intermediate code is code generated as a linked list
   when there is no hint or notion of how much code there
   will be, and to help resolve all references.
   There is always a pointer to the current word that is
   being compiled kept.
   */

#define INTMEDFLG_DIVBYZERO 1
#define INTMEDFLG_MODBYZERO 2
#define INTMEDFLG_INTRY		4

struct INTERMEDIATE {
	int no;						/* which number instruction this is */
	struct inst in;				/* instruction itself */
	short line;					/* line number of instruction */
	short flags;
	struct INTERMEDIATE *next;	/* next instruction */
};


/* The state structure for a compile. */
typedef struct COMPILE_STATE_T {
	struct CONTROL_STACK *control_stack;
	struct PROC_LIST *procs;

	int nowords;				/* number of words compiled */
	struct INTERMEDIATE *curr_word;	/* word being compiled */
	struct INTERMEDIATE *first_word;	/* first word of the list */
	struct INTERMEDIATE *curr_proc;	/* first word of curr. proc. */
	struct publics *currpubs;
	int nested_fors;
	int nested_trys;

	/* Address resolution data.  Used to relink addresses after compile. */
	struct INTERMEDIATE **addrlist; /* list of addresses to resolve */
	int *addroffsets;               /* list of offsets from instrs */
	int addrmax;                    /* size of current addrlist array */
	int addrcount;                  /* number of allocated addresses */

	/* variable names.  The index into cstat->variables give you what position
	 * the variable holds.
	 */
	const char *variables[MAX_VAR];
	int variabletypes[MAX_VAR];
	const char *localvars[MAX_VAR];
	int localvartypes[MAX_VAR];
	const char *scopedvars[MAX_VAR];
	int scopedvartypes[MAX_VAR];

	struct line *curr_line;		/* current line */
	int lineno;			/* current line number */
	int start_comment;              /* Line last comment started at */
	int force_comment;              /* Only attempt certain compile. */
	const char *next_char;		/* next char * */
	dbref player, program;		/* player and program for this compile */

	int compile_err;			/* 1 if error occured */

	char *line_copy;
	int macrosubs;				/* Safeguard for macro-subst. infinite loops */
	int descr;					/* the descriptor that initiated compiling */
	int force_err_display;		/* If true, always show compiler errors. */
	struct INTERMEDIATE *nextinst;
	hash_tab defhash[DEFHASHSIZE];
} COMPSTATE;


int primitive(const char *s);	/* returns primitive_number if

								 * primitive */
void free_prog(dbref);
const char *next_token(COMPSTATE *);
const char *next_token_raw(COMPSTATE *);
struct INTERMEDIATE *next_word(COMPSTATE *, const char *);
struct INTERMEDIATE *process_special(COMPSTATE *, const char *);
struct INTERMEDIATE *primitive_word(COMPSTATE *, const char *);
struct INTERMEDIATE *string_word(COMPSTATE *, const char *);
struct INTERMEDIATE *number_word(COMPSTATE *, const char *);
struct INTERMEDIATE *float_word(COMPSTATE *, const char *);
struct INTERMEDIATE *object_word(COMPSTATE *, const char *);
struct INTERMEDIATE *quoted_word(COMPSTATE *, const char *);
struct INTERMEDIATE *call_word(COMPSTATE *, const char *);
struct INTERMEDIATE *var_word(COMPSTATE *, const char *);
struct INTERMEDIATE *lvar_word(COMPSTATE *, const char *);
struct INTERMEDIATE *svar_word(COMPSTATE *, const char *);
const char *do_string(COMPSTATE *);
void do_comment(COMPSTATE *, int);
void do_directive(COMPSTATE *, char *direct);
struct prog_addr *alloc_addr(COMPSTATE *, int, struct inst *);
struct INTERMEDIATE *prealloc_inst(COMPSTATE * cstat);
struct INTERMEDIATE *new_inst(COMPSTATE *);
void cleanpubs(struct publics *mypub);
void clean_mcpbinds(struct mcp_binding *mcpbinds);
void cleanup(COMPSTATE *);
void add_proc(COMPSTATE *, const char *, struct INTERMEDIATE *, int rettype);
void add_control_structure(COMPSTATE *, int typ, struct INTERMEDIATE *);
void add_loop_exit(COMPSTATE *, struct INTERMEDIATE *);
int in_loop(COMPSTATE * cstat);
int innermost_control_type(COMPSTATE * cstat);
int count_trys_inside_loop(COMPSTATE* cstat);
struct INTERMEDIATE *locate_control_structure(COMPSTATE* cstat, int type1, int type2);
struct INTERMEDIATE *innermost_control_place(COMPSTATE * cstat, int type1);
struct INTERMEDIATE *pop_control_structure(COMPSTATE * cstat, int type1, int type2);
struct INTERMEDIATE *pop_loop_exit(COMPSTATE *);
void resolve_loop_addrs(COMPSTATE *, int where);
int add_variable(COMPSTATE *, const char *, int valtype);
int add_localvar(COMPSTATE *, const char *, int valtype);
int add_scopedvar(COMPSTATE *, const char *, int valtype);
int special(const char *);
int call(COMPSTATE *, const char *);
int quoted(COMPSTATE *, const char *);
int object(const char *);
int string(const char *);
int variable(COMPSTATE *, const char *);
int localvar(COMPSTATE *, const char *);
int scopedvar(COMPSTATE *, const char *);
void copy_program(COMPSTATE *);
void set_start(COMPSTATE *);
void free_intermediate_node(struct INTERMEDIATE *wd);

/* Character defines */
#define BEGINCOMMENT '('
#define ENDCOMMENT ')'
#define BEGINSTRING '"'
#define ENDSTRING '"'
#define BEGINMACRO '.'
#define BEGINDIRECTIVE '$'
#define BEGINESCAPE '\\'

#define SUBSTITUTIONS 20		/* How many nested macros will we allow? */

void
do_abort_compile(COMPSTATE * cstat, const char *c)
{
	static char _buf[BUFFER_LEN];

	if (cstat->start_comment) {
	  snprintf(_buf, sizeof(_buf), "Error in line %d: %s  Comment starting at line %d.", cstat->lineno, c, cstat->start_comment);
	  cstat->start_comment = 0;
	} else {
	  snprintf(_buf, sizeof(_buf), "Error in line %d: %s", cstat->lineno, c);
	}
	if (cstat->line_copy) {
		free((void *) cstat->line_copy);
		cstat->line_copy = NULL;
	}
	if (((FLAGS(cstat->player) & INTERACTIVE) && !(FLAGS(cstat->player) & READMODE)) ||
		cstat->force_err_display) {
		notify_nolisten(cstat->player, _buf, 1);
	} else {
		log_muf("%s(#%d) [%s(#%d)] %s(#%d) %s\n",
			NAME(OWNER(cstat->program)), OWNER(cstat->program),
			NAME(cstat->program), cstat->program,
			NAME(cstat->player), cstat->player,
			_buf
		);
	}
	cstat->compile_err++;
	if (cstat->compile_err > 1) {
		return;
	}
	if (cstat->nextinst) {
		struct INTERMEDIATE* ptr;
		while (cstat->nextinst)
		{
			ptr = cstat->nextinst;
			cstat->nextinst = ptr->next;
			free(ptr);
		}
		cstat->nextinst = NULL;
	}
	cleanup(cstat);
	cleanpubs(cstat->currpubs);
	cstat->currpubs = NULL;
	free_prog(cstat->program);
	cleanpubs(PROGRAM_PUBS(cstat->program));
	PROGRAM_SET_PUBS(cstat->program, NULL);
	clean_mcpbinds(PROGRAM_MCPBINDS(cstat->program));
	PROGRAM_SET_MCPBINDS(cstat->program, NULL);
	PROGRAM_SET_PROFTIME(cstat->program, 0, 0);
}

/* abort compile macro */
#define abort_compile(ST,C) { do_abort_compile(ST,C); return 0; }

/* abort compile for void functions */
#define v_abort_compile(ST,C) { do_abort_compile(ST,C); return; }

void compiler_warning(COMPSTATE* cstat, char* text, ...)
{
	char buf[BUFFER_LEN];
	va_list vl;

	va_start(vl, text);
	vsnprintf(buf, sizeof(buf), text, vl);
	va_end(vl);

	notify_nolisten(cstat->player, buf, 1);
}

/*****************************************************************/


#define ADDRLIST_ALLOC_CHUNK_SIZE 256

int
get_address(COMPSTATE* cstat, struct INTERMEDIATE* dest, int offset)
{
	int i;

	if (!cstat->addrlist)
	{
		cstat->addrcount = 0;
		cstat->addrmax = ADDRLIST_ALLOC_CHUNK_SIZE;
		cstat->addrlist = (struct INTERMEDIATE**)
			malloc(cstat->addrmax * sizeof(struct INTERMEDIATE*));
		cstat->addroffsets = (int*)
			malloc(cstat->addrmax * sizeof(int));
	}

	for (i = 0; i < cstat->addrcount; i++)
		if (cstat->addrlist[i] == dest && cstat->addroffsets[i] == offset)
			return i;

    if (cstat->addrcount >= cstat->addrmax)
	{
		cstat->addrmax += ADDRLIST_ALLOC_CHUNK_SIZE;
		cstat->addrlist = (struct INTERMEDIATE**)
			realloc(cstat->addrlist, cstat->addrmax * sizeof(struct INTERMEDIATE*));
		cstat->addroffsets = (int*)
			realloc(cstat->addroffsets, cstat->addrmax * sizeof(int));
	}

	cstat->addrlist[cstat->addrcount] = dest;
	cstat->addroffsets[cstat->addrcount] = offset;
	return cstat->addrcount++;
}


void
fix_addresses(COMPSTATE* cstat)
{
	struct INTERMEDIATE* ptr;
	struct publics* pub;
	int count = 0;

	/* renumber the instruction chain */
	for (ptr = cstat->first_word; ptr; ptr = ptr->next)
		ptr->no = count++;

	/* repoint publics to targets */
	for (pub = cstat->currpubs; pub; pub = pub->next)
		pub->addr.no = cstat->addrlist[pub->addr.no]->no +
				cstat->addroffsets[pub->addr.no];

	/* repoint addresses to targets */
	for (ptr = cstat->first_word; ptr; ptr = ptr->next)
	{
		switch (ptr->in.type) {
		case PROG_ADD:
		case PROG_IF:
		case PROG_TRY:
		case PROG_JMP:
		case PROG_EXEC:
			ptr->in.data.number = cstat->addrlist[ptr->in.data.number]->no +
					cstat->addroffsets[ptr->in.data.number];
			break;
		default:
			break;
		}
	}
}


void
free_addresses(COMPSTATE* cstat)
{
	cstat->addrcount = 0;
	cstat->addrmax = 0;
	if (cstat->addrlist)
		free(cstat->addrlist);
	if (cstat->addroffsets)
		free(cstat->addroffsets);
	cstat->addrlist = NULL;
}


/*****************************************************************/


void
fixpubs(struct publics *mypubs, struct inst *offset)
{
	while (mypubs) {
		mypubs->addr.ptr = offset + mypubs->addr.no;
		mypubs = mypubs->next;
	}
}


int
size_pubs(struct publics *mypubs)
{
	int bytes = 0;

	while (mypubs) {
		bytes += sizeof(*mypubs);
		mypubs = mypubs->next;
	}
	return bytes;
}



char *
expand_def(COMPSTATE * cstat, const char *defname)
{
	hash_data *exp = find_hash(defname, cstat->defhash, DEFHASHSIZE);

	if (!exp) {
		if (*defname == BEGINMACRO) {
			return (macro_expansion(macrotop, &defname[1]));
		} else {
			return (NULL);
		}
	}
	return (string_dup((char *) exp->pval));
}


void
kill_def(COMPSTATE * cstat, const char *defname)
{
	hash_data *exp = find_hash(defname, cstat->defhash, DEFHASHSIZE);

	if (exp) {
		free(exp->pval);
		(void) free_hash(defname, cstat->defhash, DEFHASHSIZE);
	}
}


void
insert_def(COMPSTATE * cstat, const char *defname, const char *deff)
{
	hash_data hd;

	(void) kill_def(cstat, defname);
	hd.pval = (void *) string_dup(deff);
	(void) add_hash(defname, hd, cstat->defhash, DEFHASHSIZE);
}


void
insert_intdef(COMPSTATE * cstat, const char *defname, int deff)
{
	char buf[sizeof(int) * 3];

	snprintf(buf, sizeof(buf), "%d", deff);
	insert_def(cstat, defname, buf);
}


void
purge_defs(COMPSTATE * cstat)
{
	kill_hash(cstat->defhash, DEFHASHSIZE, 1);
}


void
include_defs(COMPSTATE * cstat, dbref i)
{
	char dirname[BUFFER_LEN];
	char temp[BUFFER_LEN];
	const char *tmpptr;
	PropPtr j, pptr;

	strcpy(dirname, "/_defs/");
	j = first_prop(i, dirname, &pptr, temp);
	while (j) {
		strcpy(dirname, "/_defs/");
		strcatn(dirname, sizeof(dirname), temp);
		tmpptr = uncompress(get_property_class(i, dirname));
		if (tmpptr && *tmpptr)
			insert_def(cstat, temp, (char *) tmpptr);
		j = next_prop(pptr, j, temp);
	}
}


void
include_internal_defs(COMPSTATE * cstat)
{
	/* Create standard server defines */
	insert_def(cstat, "__version", VERSION);
	insert_def(cstat, "__muckname", tp_muckname);
	insert_intdef(cstat, "__fuzzball__", 1);
	insert_def(cstat, "strip", "striplead striptail");
	insert_def(cstat, "instring", "tolower swap tolower swap instr");
	insert_def(cstat, "rinstring", "tolower swap tolower swap rinstr");
	insert_intdef(cstat, "bg_mode", BACKGROUND);
	insert_intdef(cstat, "fg_mode", FOREGROUND);
	insert_intdef(cstat, "pr_mode", PREEMPT);
	insert_intdef(cstat, "max_variable_count", MAX_VAR);
	insert_intdef(cstat, "sorttype_caseinsens", SORTTYPE_CASEINSENS);
	insert_intdef(cstat, "sorttype_descending", SORTTYPE_DESCENDING);
	insert_intdef(cstat, "sorttype_case_ascend", SORTTYPE_CASE_ASCEND);
	insert_intdef(cstat, "sorttype_nocase_ascend", SORTTYPE_NOCASE_ASCEND);
	insert_intdef(cstat, "sorttype_case_descend", SORTTYPE_CASE_DESCEND);
	insert_intdef(cstat, "sorttype_nocase_descend", SORTTYPE_NOCASE_DESCEND);
	insert_intdef(cstat, "sorttype_shuffle", SORTTYPE_SHUFFLE);

	/* Make defines for compatability to removed primitives */
	insert_def(cstat, "desc", "\"_/de\" getpropstr");
	insert_def(cstat, "succ", "\"_/sc\" getpropstr");
	insert_def(cstat, "fail", "\"_/fl\" getpropstr");
	insert_def(cstat, "drop", "\"_/dr\" getpropstr");
	insert_def(cstat, "osucc", "\"_/osc\" getpropstr");
	insert_def(cstat, "ofail", "\"_/ofl\" getpropstr");
	insert_def(cstat, "odrop", "\"_/odr\" getpropstr");
	insert_def(cstat, "setdesc", "\"_/de\" swap 0 addprop");
	insert_def(cstat, "setsucc", "\"_/sc\" swap 0 addprop");
	insert_def(cstat, "setfail", "\"_/fl\" swap 0 addprop");
	insert_def(cstat, "setdrop", "\"_/dr\" swap 0 addprop");
	insert_def(cstat, "setosucc", "\"_/osc\" swap 0 addprop");
	insert_def(cstat, "setofail", "\"_/ofl\" swap 0 addprop");
	insert_def(cstat, "setodrop", "\"_/odr\" swap 0 addprop");
	insert_def(cstat, "preempt", "pr_mode setmode");
	insert_def(cstat, "background", "bg_mode setmode");
	insert_def(cstat, "foreground", "fg_mode setmode");
	insert_def(cstat, "notify_except", "1 swap notify_exclude");
	insert_def(cstat, "event_wait", "0 array_make event_waitfor");
	insert_def(cstat, "tread", "\"__tread\" timer_start { \"TIMER.__tread\" \"READ\" }list event_waitfor swap pop \"READ\" strcmp if \"\" 0 else read 1 \"__tread\" timer_stop then");

	/* MUF Error defines */
	insert_def(cstat, "err_divzero?", "0 is_set?");
	insert_def(cstat, "err_nan?", "1 is_set?");
	insert_def(cstat, "err_imaginary?", "2 is_set?");
	insert_def(cstat, "err_fbounds?", "3 is_set?");
	insert_def(cstat, "err_ibounds?", "4 is_set?");

	/* Array convenience defines */
	insert_def(cstat, "}array", "} array_make");
	insert_def(cstat, "}list", "} array_make");
	insert_def(cstat, "}dict", "} 2 / array_make_dict");
	insert_def(cstat, "}join", "} array_make \"\" array_join");
	insert_def(cstat, "}cat", "} array_make array_interpret");
	insert_def(cstat, "}tell", "} array_make me @ 1 array_make array_notify");
	insert_def(cstat, "[]", "array_getitem");
	insert_def(cstat, "->[]", "array_setitem");
	insert_def(cstat, "[]<-", "array_appenditem");
	insert_def(cstat, "[..]", "array_getrange");
	insert_def(cstat, "array_diff", "2 array_ndiff");
	insert_def(cstat, "array_union", "2 array_nunion");
	insert_def(cstat, "array_intersect", "2 array_nintersect");

	/* GUI dialog types */
	insert_def(cstat, "d_simple", "\"simple\"");
	insert_def(cstat, "d_tabbed", "\"tabbed\"");
	insert_def(cstat, "d_helper", "\"helper\"");

	/* GUI control types */
	insert_def(cstat, "c_menu",      "\"menu\"");
	insert_def(cstat, "c_datum",     "\"datum\"");
	insert_def(cstat, "c_label",     "\"text\"");
	insert_def(cstat, "c_image",     "\"image\"");
	insert_def(cstat, "c_hrule",     "\"hrule\"");
	insert_def(cstat, "c_vrule",     "\"vrule\"");
	insert_def(cstat, "c_button",    "\"button\"");
	insert_def(cstat, "c_checkbox",  "\"checkbox\"");
	insert_def(cstat, "c_radiobtn",  "\"radio\"");
	insert_def(cstat, "c_password",  "\"password\"");
	insert_def(cstat, "c_edit",      "\"edit\"");
	insert_def(cstat, "c_multiedit", "\"multiedit\"");
	insert_def(cstat, "c_combobox",  "\"combobox\"");
	insert_def(cstat, "c_spinner",   "\"spinner\"");
	insert_def(cstat, "c_scale",     "\"scale\"");
	insert_def(cstat, "c_listbox",   "\"listbox\"");
	insert_def(cstat, "c_tree",      "\"tree\"");
	insert_def(cstat, "c_frame",     "\"frame\"");
	insert_def(cstat, "c_notebook",  "\"notebook\"");

	/* Backwards compatibility for old GUI dialog creation prims */
	insert_def(cstat, "gui_dlog_simple", "d_simple 0 array_make_dict gui_dlog_create");
    insert_def(cstat, "gui_dlog_tabbed", "d_tabbed swap \"panes\" over array_keys array_make \"names\" 4 rotate array_vals array_make 2 array_make_dict gui_dlog_create");
    insert_def(cstat, "gui_dlog_helper", "d_helper swap \"panes\" over array_keys array_make \"names\" 4 rotate array_vals array_make 2 array_make_dict gui_dlog_create");

	/* Regex */
	insert_def(cstat, "reg_icase",		MUF_RE_ICASE_STR);
	insert_def(cstat, "reg_all",		MUF_RE_ALL_STR);
}


void
init_defs(COMPSTATE * cstat)
{
	/* initialize hash table */
	int i;

	for (i = 0; i < DEFHASHSIZE; i++) {
		cstat->defhash[i] = NULL;
	}

	/* Create standard server defines */
	include_internal_defs(cstat);

	/* Include any defines set in #0's _defs/ propdir. */
	include_defs(cstat, (dbref) 0);

	/* Include any defines set in program owner's _defs/ propdir. */
	include_defs(cstat, OWNER(cstat->program));
}


void
uncompile_program(dbref i)
{
	/* free program */
	(void) dequeue_prog(i, 1);
	free_prog(i);
	cleanpubs(PROGRAM_PUBS(i));
	PROGRAM_SET_PUBS(i, NULL);
	clean_mcpbinds(PROGRAM_MCPBINDS(i));
	PROGRAM_SET_MCPBINDS(i, NULL);
	PROGRAM_SET_PROFTIME(i, 0, 0);
	PROGRAM_SET_CODE(i, NULL);
	PROGRAM_SET_SIZ(i, 0);
	PROGRAM_SET_START(i, NULL);
}


void
do_uncompile(dbref player)
{
	dbref i;

	if (!Wizard(OWNER(player))) {
		notify_nolisten(player, "Permission denied.", 1);
		return;
	}
	for (i = 0; i < db_top; i++) {
		if (Typeof(i) == TYPE_PROGRAM) {
			uncompile_program(i);
		}
	}
	notify_nolisten(player, "All programs decompiled.", 1);
}

void
free_unused_programs()
{
	dbref i;
	time_t now = time(NULL);

	for (i = 0; i < db_top; i++) {
		if ((Typeof(i) == TYPE_PROGRAM) && !(FLAGS(i) & (ABODE | INTERNAL)) &&
			(now - DBFETCH(i)->ts.lastused > tp_clean_interval) && (PROGRAM_INSTANCES(i) == 0)) {
			uncompile_program(i);
		}
	}
}

/* Various flags for the IMMEDIATE instructions */

#define IMMFLAG_REFERENCED	1	/* Referenced by a jump */


/* Checks code for valid fetch-and-clear optim changes, and does them. */
void
MaybeOptimizeVarsAt(COMPSTATE * cstat, struct INTERMEDIATE* first, int AtNo, int BangNo)
{
	struct INTERMEDIATE* curr = first->next;
	struct INTERMEDIATE* ptr;
	int farthest = 0;
	int i;
	int lvarflag = 0;

	if (first->flags & INTMEDFLG_INTRY)
		return;

	if (first->in.type == PROG_LVAR_AT || first->in.type == PROG_LVAR_AT_CLEAR)
		lvarflag = 1;

	for(; curr; curr = curr->next) {
		if (curr->flags & INTMEDFLG_INTRY)
			return;

		switch(curr->in.type) {
			case PROG_PRIMITIVE:
				/* Don't trust any physical @ or !'s in the code, someone
					may be indirectly referencing the scoped variable */
				/* Don't trust any explicit jmp's in the code. */

				if ((curr->in.data.number == AtNo) || 
					(curr->in.data.number == BangNo) ||
					(curr->in.data.number == IN_JMP))
				{
					return;
				}

				if (lvarflag) {
					/* For lvars, don't trust the following prims... */
					/*   EXITs escape the code path without leaving lvar scope. */
					/*   EXECUTEs escape the code path without leaving lvar scope. */
					/*   CALLs cause re-entrancy problems. */
					if (curr->in.data.number == IN_RET ||
						curr->in.data.number == IN_EXECUTE ||
						curr->in.data.number == IN_CALL)
					{
						return;
					}
				}
				break;

			case PROG_LVAR_AT:
			case PROG_LVAR_AT_CLEAR:
				if (lvarflag) {
					if (curr->in.data.number == first->in.data.number) {
						/* Can't optimize if references to the variable found before a var! */
						return;
					}
				}
				break;

			case PROG_SVAR_AT:
			case PROG_SVAR_AT_CLEAR:
				if (!lvarflag) {
					if (curr->in.data.number == first->in.data.number) {
						/* Can't optimize if references to the variable found before a var! */
						return;
					}
				}
				break;

			case PROG_LVAR_BANG:
				if (lvarflag) {
					if (first->in.data.number == curr->in.data.number) {
						if (curr->no <= farthest) {
							/* cannot optimize as we are within a branch */
							return;
						} else {
							/* Optimize it! */
							first->in.type = PROG_LVAR_AT_CLEAR;
							return;
						}
					}
				}
				break;

			case PROG_SVAR_BANG:
				if (!lvarflag) {
					if (first->in.data.number == curr->in.data.number) {
						if (curr->no <= farthest) {
							/* cannot optimize as we are within a branch */
							return;
						} else {
							/* Optimize it! */
							first->in.type = PROG_SVAR_AT_CLEAR;
							return;
						}
					}
				}
				break;

			case PROG_EXEC:
				if (lvarflag) {
					/* Don't try to optimize lvars over execs */
					return;
				}
				break;

			case PROG_IF:
			case PROG_TRY:
			case PROG_JMP:
				ptr = cstat->addrlist[curr->in.data.number];
				i = cstat->addroffsets[curr->in.data.number];
				while (ptr->next && i-->0)
					ptr = ptr->next;
				if (ptr->no <= first->no) {
					/* Can't optimize as we've exited the code branch the @ is in. */
					return;
				}
				if (ptr->no > farthest)
					farthest = ptr->no;
				break;

			case PROG_FUNCTION:
				/* Don't try to optimize over functions */
				return;
		}
	}
}


void
RemoveNextIntermediate(COMPSTATE * cstat, struct INTERMEDIATE* curr)
{
	struct INTERMEDIATE* tmp;
	int i;

	if (!curr->next) {
		return;
	}

	tmp = curr->next;
	for (i = 0; i < cstat->addrcount; i++) {
		if (cstat->addrlist[i] == tmp) {
			cstat->addrlist[i] = curr;
		}
	}
	curr->next = curr->next->next;
	free_intermediate_node(tmp);
	cstat->nowords--;
}


void
RemoveIntermediate(COMPSTATE * cstat, struct INTERMEDIATE* curr)
{
	if (!curr->next) {
		return;
	}

	curr->no           = curr->next->no;
	curr->in.line      = curr->next->in.line;
	curr->in.type      = curr->next->in.type;
	switch(curr->in.type) {
		case PROG_STRING:
			curr->in.data.string = curr->next->in.data.string;
			break;
		case PROG_FLOAT:
			curr->in.data.fnumber = curr->next->in.data.fnumber;
			break;
		case PROG_FUNCTION:
			curr->in.data.mufproc = curr->next->in.data.mufproc;
			break;
		case PROG_ADD:
			curr->in.data.addr = curr->next->in.data.addr;
			break;
		case PROG_IF:
		case PROG_TRY:
		case PROG_JMP:
		case PROG_EXEC:
			curr->in.data.call = curr->next->in.data.call;
			break;
		default:
			curr->in.data.number = curr->next->in.data.number;
			break;
	}
	curr->next->in.type = PROG_INTEGER;
	curr->next->in.data.number = 0;
	RemoveNextIntermediate(cstat, curr);
}


int
ContiguousIntermediates(int* Flags, struct INTERMEDIATE* ptr, int count)
{
	while (count-->0) {
		if (!ptr) {
			return 0;
		}
		if ((Flags[ptr->no] & IMMFLAG_REFERENCED)) {
			return 0;
		}
		ptr = ptr->next;
	}
	return 1;
}


int
IntermediateIsPrimitive(struct INTERMEDIATE* ptr, int primnum)
{
	if (ptr && ptr->in.type == PROG_PRIMITIVE) {
		if (ptr->in.data.number == primnum) {
			return 1;
		}
	}
	return 0;
}


int
IntermediateIsInteger(struct INTERMEDIATE* ptr, int val)
{
	if (ptr && ptr->in.type == PROG_INTEGER) {
		if (ptr->in.data.number == val) {
			return 1;
		}
	}
	return 0;
}


int
IntermediateIsString(struct INTERMEDIATE* ptr, const char* val)
{
	const char* myval;

	if (ptr && ptr->in.type == PROG_STRING) {
		myval = ptr->in.data.string? ptr->in.data.string->data : "";
		if (!strcmp(myval, val)) {
			return 1;
		}
	}
	return 0;
}

int
OptimizeIntermediate(COMPSTATE * cstat, int force_err_display)
{
	struct INTERMEDIATE* curr;
	int* Flags;
	int i;
	int count = 0;
	int old_instr_count = cstat->nowords;
	int AtNo = get_primitive("@"); /* Wince */
	int BangNo = get_primitive("!");
	int SwapNo = get_primitive("swap");
	int RotNo = get_primitive("rot");
	int NotNo = get_primitive("not");
	int StrcmpNo = get_primitive("strcmp");
	int EqualsNo = get_primitive("=");
	int PlusNo = get_primitive("+");
	int MinusNo = get_primitive("-");
	int MultNo = get_primitive("*");
	int DivNo = get_primitive("/");
	int ModNo = get_primitive("%");
	int DecrNo = get_primitive("--");
	int IncrNo = get_primitive("++");

	/* Code assumes everything is setup nicely, if not, bad things will happen */

	if (!cstat->first_word)
		return 0;

	/* renumber the instruction chain */
	for (curr = cstat->first_word; curr; curr = curr->next)
		curr->no = count++;

	if ((Flags = (int*)malloc(sizeof(int) * count)) == 0)
		return 0;

	memset(Flags, 0, sizeof(int) * count);

	/* Mark instructions which jumps reference */

	for(curr = cstat->first_word; curr; curr = curr->next) {
		switch(curr->in.type) {
			case PROG_ADD:
			case PROG_IF:
			case PROG_TRY:
			case PROG_JMP:
			case PROG_EXEC:
				i = cstat->addrlist[curr->in.data.number]->no +
						cstat->addroffsets[curr->in.data.number];
				Flags[i] |= IMMFLAG_REFERENCED;
				break;
		}
	}

	for(curr = cstat->first_word; curr; ) {
		int advance = 1;
		switch(curr->in.type) {
			case PROG_LVAR:
				/* lvar !  ==>  lvar! */
				/* lvar @  ==>  lvar@ */
				if (curr->next && curr->next->in.type == PROG_PRIMITIVE) {
					if (curr->next->in.data.number == AtNo) {
						if (ContiguousIntermediates(Flags, curr->next, 1)) {
							curr->in.type = PROG_LVAR_AT;
							RemoveNextIntermediate(cstat, curr);
							advance = 0;
							break;
						}
					}
					if (curr->next->in.data.number == BangNo) {
						if (ContiguousIntermediates(Flags, curr->next, 1)) {
							curr->in.type = PROG_LVAR_BANG;
							RemoveNextIntermediate(cstat, curr);
							advance = 0;
							break;
						}
					}
				}
				break;

			case PROG_SVAR:
				/* svar !  ==>  svar! */
				/* svar @  ==>  svar@ */
				if (curr->next && curr->next->in.type == PROG_PRIMITIVE) {
					if (curr->next->in.data.number == AtNo) {
						if (ContiguousIntermediates(Flags, curr->next, 1)) {
							curr->in.type = PROG_SVAR_AT;
							RemoveNextIntermediate(cstat, curr);
							advance = 0;
							break;
						}
					}
					if (curr->next->in.data.number == BangNo) {
						if (ContiguousIntermediates(Flags, curr->next, 1)) {
							curr->in.type = PROG_SVAR_BANG;
							RemoveNextIntermediate(cstat, curr);
							advance = 0;
							break;
						}
					}
				}
				break;

			case PROG_STRING:
				/* "" strcmp 0 =  ==>  not */
				if (IntermediateIsString(curr, "")) {
					if (ContiguousIntermediates(Flags, curr->next, 3)) {
						if (IntermediateIsPrimitive(curr->next, StrcmpNo)) {
							if (IntermediateIsInteger(curr->next->next, 0)) {
								if (IntermediateIsPrimitive(curr->next->next->next, EqualsNo)) {
									if (curr->in.data.string)
										free((void *) curr->in.data.string);
									curr->in.type = PROG_PRIMITIVE;
									curr->in.data.number = NotNo;
									RemoveNextIntermediate(cstat, curr);
									RemoveNextIntermediate(cstat, curr);
									RemoveNextIntermediate(cstat, curr);
									advance = 0;
									break;
								}
							}
						}
					}
				}
				break;

			case PROG_INTEGER:
				/* consolidate constant integer calculations */
				if (ContiguousIntermediates(Flags, curr->next, 2)) {
					if (curr->next->in.type == PROG_INTEGER) {
						/* Int Int +  ==>  Sum */
						if (IntermediateIsPrimitive(curr->next->next, PlusNo)) {
							curr->in.data.number += curr->next->in.data.number;
							RemoveNextIntermediate(cstat, curr);
							RemoveNextIntermediate(cstat, curr);
							advance = 0;
							break;
						}

						/* Int Int -  ==>  Diff */
						if (IntermediateIsPrimitive(curr->next->next, MinusNo)) {
							curr->in.data.number -= curr->next->in.data.number;
							RemoveNextIntermediate(cstat, curr);
							RemoveNextIntermediate(cstat, curr);
							advance = 0;
							break;
						}

						/* Int Int *  ==>  Prod */
						if (IntermediateIsPrimitive(curr->next->next, MultNo)) {
							curr->in.data.number *= curr->next->in.data.number;
							RemoveNextIntermediate(cstat, curr);
							RemoveNextIntermediate(cstat, curr);
							advance = 0;
							break;
						}

						/* Int Int /  ==>  Div  */
						if (IntermediateIsPrimitive(curr->next->next, DivNo)) {
							if (curr->next->in.data.number == 0)
							{
								if (!(curr->next->next->flags & INTMEDFLG_DIVBYZERO))
								{
									curr->next->next->flags |= INTMEDFLG_DIVBYZERO;

									if (force_err_display)
									{
										compiler_warning(
												cstat,
												"Warning on line %i: Divide by zero",
												curr->next->next->in.line
											);
									}
								}
							}
							else
							{
								curr->in.data.number /= curr->next->in.data.number;
								RemoveNextIntermediate(cstat, curr);
								RemoveNextIntermediate(cstat, curr);
								advance = 0;
							}

							break;
						}

						/* Int Int %  ==>  Div  */
						if (IntermediateIsPrimitive(curr->next->next, ModNo)) {
							if (curr->next->in.data.number == 0)
							{
								if (!(curr->next->next->flags & INTMEDFLG_MODBYZERO))
								{
									curr->next->next->flags |= INTMEDFLG_MODBYZERO;

									if (force_err_display)
									{
										compiler_warning(
												cstat,
											   	"Warning on line %i: Modulus by zero",
											   	curr->next->next->in.line
											);
									}
								}
							}
							else
							{
								curr->in.data.number %= curr->next->in.data.number;
								RemoveNextIntermediate(cstat, curr);
								RemoveNextIntermediate(cstat, curr);
								advance = 0;
							}

							break;
						}
					}
				}

				/* 0 =  ==>  not */
				if (IntermediateIsInteger(curr, 0)) {
					if (ContiguousIntermediates(Flags, curr->next, 1)) {
						if (IntermediateIsPrimitive(curr->next, EqualsNo)) {
							curr->in.type = PROG_PRIMITIVE;
							curr->in.data.number = NotNo;
							RemoveNextIntermediate(cstat, curr);
							advance = 0;
							break;
						}
					}
				}

				/* 1 +  ==>  ++ */
				if (IntermediateIsInteger(curr, 1)) {
					if (ContiguousIntermediates(Flags, curr->next, 1)) {
						if (IntermediateIsPrimitive(curr->next, PlusNo)) {
							curr->in.type = PROG_PRIMITIVE;
							curr->in.data.number = IncrNo;
							RemoveNextIntermediate(cstat, curr);
							advance = 0;
							break;
						}
					}
				}

				/* 1 -  ==>  -- */
				if (IntermediateIsInteger(curr, 1)) {
					if (ContiguousIntermediates(Flags, curr->next, 1)) {
						if (IntermediateIsPrimitive(curr->next, MinusNo)) {
							curr->in.type = PROG_PRIMITIVE;
							curr->in.data.number = DecrNo;
							RemoveNextIntermediate(cstat, curr);
							advance = 0;
							break;
						}
					}
				}
				break;

			case PROG_PRIMITIVE:
				/* rot rot swap  ==>  swap rot */
				if (IntermediateIsPrimitive(curr, RotNo)) {
					if (ContiguousIntermediates(Flags, curr->next, 2)) {
						if (IntermediateIsPrimitive(curr->next, RotNo)) {
							if (IntermediateIsPrimitive(curr->next->next, SwapNo)) {
								curr->in.data.number = SwapNo;
								curr->next->in.data.number = RotNo;
								RemoveNextIntermediate(cstat, curr->next);
								advance = 0;
								break;
							}
						}
					}
				}
				/* not not if  ==>  if */
				if (IntermediateIsPrimitive(curr, NotNo)) {
					if (ContiguousIntermediates(Flags, curr->next, 2)) {
						if (IntermediateIsPrimitive(curr->next, NotNo)) {
							if (curr->next->next->in.type == PROG_IF) {
								RemoveIntermediate(cstat, curr);
								RemoveIntermediate(cstat, curr);
								advance = 0;
								break;
							}
						}
					}
				}
				break;
		}

		if (advance) {
			curr = curr->next;
		}
	}

	/* Turn all var@'s which have a following var! into a var@-clear */

	for(curr = cstat->first_word; curr; curr = curr->next)
		if (curr->in.type == PROG_SVAR_AT || curr->in.type == PROG_LVAR_AT)
				MaybeOptimizeVarsAt(cstat, curr, AtNo, BangNo);

	free(Flags);
	return (old_instr_count - cstat->nowords);
}

/* Genericized Optimizer ideas:
 *
 * const int OI_ANY = -121314;   // arbitrary unlikely-to-be-needed value.
 *
 * typedef enum {
 *     OI_KEEP,
 *     OI_CHGVAL,
 *     OI_CHGTYPE,
 *     OI_REPLACE,
 *     OI_DELETE
 * } OI_ACTION;
 *
 * OPTIM* option_new();
 * void optim_free(OPTIM* optim);
 * void optim_add_raw  (OPTIM* optim, struct INTERMEDIATE* originst,
 *                      OI_ACTION action, struct INTERMEDIATE* newinst);
 * void optim_add_type (OPTIM* optim, int origtype,
 *                      OI_ACTION action, int newtype);
 * void optim_add_prim (OPTIM* optim, const char* origprim,
 *                      OI_ACTION action, int newval);
 * void optim_add_int  (OPTIM* optim, int origval,
 *                      OI_ACTION action, int newval);
 * void optim_add_str  (OPTIM* optim, const char* origval,
 *                      OI_ACTION action, int newval);
 *
 *
 * OPTIM* optim = optim_new(cstat);
 * optim_add_str (optim, "",       OI_DELETE, 0);
 * optim_add_prim(optim, "strcmp", OI_CHGVAL, get_primitive("not"));
 * optim_add_int (optim, 0,        OI_DELETE, 0);
 * optim_add_prim(optim, "=",      OI_DELETE, 0);
 *
 * OPTIM* optim = optim_new(cstat);
 * optim_add_str(optim, "",        OI_DELETE, 0);
 * optim_add_prim(optim, "strcmp", OI_DELETE, 0);
 * optim_add_prim(optim, "not",    OI_KEEP,   0);
 *
 * OPTIM* optim = optim_new(cstat);
 * optim_add_prim(optim, "rot",  OI_CHGVAL, get_primitive("swap"));
 * optim_add_prim(optim, "rot",  OI_KEEP,   0);
 * optim_add_prim(optim, "swap", OI_DELETE, 0);
 *
 * OPTIM* optim = optim_new(cstat);
 * optim_add_type(optim, PROG_SVAR, OI_CHGTYPE, PROG_SVAR_AT);
 * optim_add_prim(optim, "@",       OI_DELETE,   0);
 *
 * OPTIM* optim = optim_new(cstat);
 * optim_add_type(optim, PROG_SVAR, OI_CHGTYPE, PROG_SVAR_BANG);
 * optim_add_prim(optim, "!",       OI_DELETE,   0);
 *
 * OPTIM* optim = optim_new(cstat);
 * optim_add_int (optim, 0,   OI_DELETE, 0);
 * optim_add_prim(optim, "=", OI_CHGVAL, get_primitive("not"));
 *
 * OPTIM* optim = optim_new(cstat);
 * optim_add_prim(optim, "not",   OI_DELETE, 0);
 * optim_add_prim(optim, "not",   OI_DELETE, 0);
 * optim_add_type(optim, PROG_IF, OI_KEEP,   0);
 *
 * OPTIM* optim = optim_new(cstat);
 * optim_add_int (optim, 0,       OI_DELETE,  0);
 * optim_add_type(optim, PROG_IF, OI_CHGTYPE, PROG_JMP);
 *
 * OPTIM* optim = optim_new(cstat);
 * optim_add_int (optim, 1,       OI_DELETE, 0);
 * optim_add_type(optim, PROG_IF, OI_DELETE, 0);
 *
 * OPTIM* optim = optim_new(cstat);
 * optim_add_int (optim, 1,   OI_DELETE, 0);
 * optim_add_prim(optim, "+", OI_CHGVAL, get_primitive("++"));
 *
 * OPTIM* optim = optim_new(cstat);
 * optim_add_int (optim, 1,   OI_DELETE, 0);
 * optim_add_prim(optim, "-", OI_CHGVAL, get_primitive("--"));
 *
 */


/* overall control code.  Does piece-meal tokenization parsing and
   backward checking.                                            */
void
do_compile(int descr, dbref player_in, dbref program_in, int force_err_display)
{
	const char *token;
	struct INTERMEDIATE *new_word;
	int i;
	COMPSTATE cstat;

	/* set all compile state variables */
	cstat.force_err_display = force_err_display;
	cstat.descr = descr;
	cstat.control_stack = 0;
	cstat.procs = 0;
	cstat.nowords = 0;
	cstat.curr_word = cstat.first_word = NULL;
	cstat.curr_proc = NULL;
	cstat.currpubs = NULL;
	cstat.nested_fors = 0;
	cstat.nested_trys = 0;
	cstat.addrcount = 0;
	cstat.addrmax = 0;
	for (i = 0; i < MAX_VAR; i++) {
		cstat.variables[i] = NULL;
		cstat.variabletypes[i] = 0;
		cstat.localvars[i] = NULL;
		cstat.localvartypes[i] = 0;
		cstat.scopedvars[i] = NULL;
		cstat.scopedvartypes[i] = 0;
	}
	cstat.curr_line = PROGRAM_FIRST(program_in);
	cstat.lineno = 1;
	cstat.start_comment = 0;
	cstat.force_comment = tp_muf_comments_strict? 1 : 0;
	cstat.next_char = NULL;
	if (cstat.curr_line)
		cstat.next_char = cstat.curr_line->this_line;
	cstat.player = player_in;
	cstat.program = program_in;
	cstat.compile_err = 0;
	cstat.line_copy = NULL;
	cstat.macrosubs = 0;
	cstat.nextinst = NULL;
	cstat.addrlist = NULL;
	cstat.addroffsets = NULL;
	init_defs(&cstat);

	cstat.variables[0] = "ME";
	cstat.variabletypes[0] = PROG_OBJECT;
	cstat.variables[1] = "LOC";
	cstat.variabletypes[1] = PROG_OBJECT;
	cstat.variables[2] = "TRIGGER";
	cstat.variabletypes[2] = PROG_OBJECT;
	cstat.variables[3] = "COMMAND";
	cstat.variabletypes[3] = PROG_STRING;

	/* free old stuff */
	(void) dequeue_prog(cstat.program, 1);
	free_prog(cstat.program);
	cleanpubs(PROGRAM_PUBS(cstat.program));
	PROGRAM_SET_PUBS(cstat.program, NULL);
	clean_mcpbinds(PROGRAM_MCPBINDS(cstat.program));
	PROGRAM_SET_MCPBINDS(cstat.program, NULL);
	PROGRAM_SET_PROFTIME(cstat.program, 0, 0);
	PROGRAM_SET_PROFSTART(cstat.program, time(NULL));
	PROGRAM_SET_PROF_USES(cstat.program, 0);

	if (!cstat.curr_line)
		v_abort_compile(&cstat, "Missing program text.");

	/* do compilation */
	while ((token = next_token(&cstat))) {
		new_word = next_word(&cstat, token);

		/* test for errors */
		if (cstat.compile_err) {
		        free((void *) token);
			return;
		}

		if (new_word) {
			if (!cstat.first_word)
				cstat.first_word = cstat.curr_word = new_word;
			else {
				cstat.curr_word->next = new_word;
				cstat.curr_word = cstat.curr_word->next;
			}
		}
		while (cstat.curr_word && cstat.curr_word->next)
			cstat.curr_word = cstat.curr_word->next;

		free((void *) token);
	}

	if (cstat.curr_proc)
		v_abort_compile(&cstat, "Unexpected end of file.");

	if (!cstat.procs)
		v_abort_compile(&cstat, "Missing procedure definition.");

	if (tp_optimize_muf) {
		int maxpasses = 5;
		int passcount = 0;
		int optimcount = 0;
		int optcnt = 0;

		do {
			optcnt = OptimizeIntermediate(&cstat, force_err_display);
			optimcount += optcnt;
			passcount++;
		} while (optcnt > 0 && --maxpasses > 0);

		if (force_err_display && optimcount > 0) {
			char buf[BUFFER_LEN];
			snprintf(buf, sizeof(buf), "Program optimized by %d instructions in %d passes.", optimcount, passcount);
			notify_nolisten(cstat.player, buf, 1);
		}
	}

	/* do copying over */
	fix_addresses(&cstat);
	copy_program(&cstat);
	fixpubs(cstat.currpubs, PROGRAM_CODE(cstat.program));
	PROGRAM_SET_PUBS(cstat.program, cstat.currpubs);

	if (cstat.nextinst) {
		struct INTERMEDIATE* ptr;
		while (cstat.nextinst)
		{
			ptr = cstat.nextinst;
			cstat.nextinst = ptr->next;
			free(ptr);
		}
		cstat.nextinst = NULL;
	}
	if (cstat.compile_err)
		return;

	set_start(&cstat);
	cleanup(&cstat);

	/* Set PROGRAM_INSTANCES to zero (cuz they don't get set elsewhere) */
	PROGRAM_SET_INSTANCES(cstat.program, 0);

	/* restart AUTOSTART program. */
	if ((FLAGS(cstat.program) & ABODE) && TrueWizard(OWNER(cstat.program)))
		add_muf_queue_event(-1, OWNER(cstat.program), NOTHING, NOTHING,
							cstat.program, "Startup", "Queued Event.", 0);

	if (force_err_display)
		notify_nolisten(cstat.player, "Program compiled successfully.", 1);
}

struct INTERMEDIATE *
next_word(COMPSTATE * cstat, const char *token)
{
	struct INTERMEDIATE *new_word;
	static char buf[BUFFER_LEN];

	if (!token)
		return 0;

	if (call(cstat, token))
		new_word = call_word(cstat, token);
	else if (scopedvar(cstat, token))
		new_word = svar_word(cstat, token);
	else if (localvar(cstat, token))
		new_word = lvar_word(cstat, token);
	else if (variable(cstat, token))
		new_word = var_word(cstat, token);
	else if (special(token))
		new_word = process_special(cstat, token);
	else if (primitive(token))
		new_word = primitive_word(cstat, token);
	else if (string(token))
		new_word = string_word(cstat, token + 1);
	else if (number(token))
		new_word = number_word(cstat, token);
	else if (ifloat(token))
		new_word = float_word(cstat, token);
	else if (object(token))
		new_word = object_word(cstat, token);
	else if (quoted(cstat, token))
		new_word = quoted_word(cstat, token + 1);
	else {
		snprintf(buf, sizeof(buf), "Unrecognized word %s.", token);
		abort_compile(cstat, buf);
	}
	return new_word;
}



/* Little routine to do the line_copy handling right */
void
advance_line(COMPSTATE * cstat)
{
	cstat->curr_line = cstat->curr_line->next;
	cstat->lineno++;
	cstat->macrosubs = 0;
	if (cstat->line_copy) {
		free((void *) cstat->line_copy);
		cstat->line_copy = NULL;
	}
	if (cstat->curr_line)
		cstat->next_char = (cstat->line_copy = alloc_string(cstat->curr_line->this_line));
	else
		cstat->next_char = (cstat->line_copy = NULL);
}

/* Skips comments, grabs strings, returns NULL when no more tokens to grab. */
const char *
next_token_raw(COMPSTATE * cstat)
{
	static char buf[BUFFER_LEN];
	int i;

	if (!cstat->curr_line)
		return (char *) 0;

	if (!cstat->next_char)
		return (char *) 0;

	/* skip white space */
	while (*cstat->next_char && isspace(*cstat->next_char))
		cstat->next_char++;

	if (!(*cstat->next_char)) {
		advance_line(cstat);
		return next_token_raw(cstat);
	}
	/* take care of comments */
	if (*cstat->next_char == BEGINCOMMENT) {
		cstat->start_comment = cstat->lineno;
		if (cstat->force_comment == 1) {
			do_comment(cstat, -1);
		} else {
			do_comment(cstat, 0);
		}
		cstat->start_comment = 0;
		return next_token_raw(cstat);
	}
	if (*cstat->next_char == BEGINSTRING)
		return do_string(cstat);

	for (i = 0; *cstat->next_char && !isspace(*cstat->next_char); i++) {
		buf[i] = *cstat->next_char;
		cstat->next_char++;
	}
	buf[i] = '\0';
	return alloc_string(buf);
}


const char *
next_token(COMPSTATE * cstat)
{
	char *expansion, *temp;

	temp = (char *) next_token_raw(cstat);
	if (!temp)
		return NULL;

	if (temp[0] == BEGINDIRECTIVE) {
		do_directive(cstat, temp);
		free(temp);
		return next_token(cstat);
	}
	if (temp[0] == BEGINESCAPE) {
		if (temp[1]) {
			expansion = temp;
			temp = (char *) malloc(strlen(expansion));
			strcpy(temp, (expansion + 1));
			free(expansion);
		}
		return (temp);
	}
	if ((expansion = expand_def(cstat, temp))) {
		free(temp);
		if (++cstat->macrosubs > SUBSTITUTIONS) {
			abort_compile(cstat, "Too many macro substitutions.");
		} else {
			int templen = strlen(cstat->next_char) + strlen(expansion) + 21;
			temp = (char *) malloc(templen);
			strcpy(temp, expansion);
			strcatn(temp, templen, cstat->next_char);
			free((void *) expansion);
			if (cstat->line_copy) {
				free((void *) cstat->line_copy);
			}
			cstat->next_char = cstat->line_copy = temp;
			return next_token(cstat);
		}
	} else {
		return (temp);
	}
}


/* Old-style comment parser */
int do_old_comment(COMPSTATE * cstat)
{
  while (*cstat->next_char && *cstat->next_char != ENDCOMMENT)
    cstat->next_char++;
  if (!(*cstat->next_char)) {
    advance_line(cstat);
    if (!cstat->curr_line) {
      return 1;
    }
    return do_old_comment(cstat);
  } else {
    cstat->next_char++;
    if (!(*cstat->next_char))
      advance_line(cstat);
  }
  return 0;
}

/* skip comments, recursive style */
int do_new_comment(COMPSTATE * cstat, int depth)
{
	int retval = 0;
	int in_str = 0;
	const char *ptr;

	if (!*cstat->next_char || *cstat->next_char != BEGINCOMMENT)
		return 2;
	if (depth >= 7 /*arbitrary*/)
		return 3;
	cstat->next_char++;  /* Advance past BEGINCOMMENT */

	while (*cstat->next_char != ENDCOMMENT) {
		if (!(*cstat->next_char)) {
			do {
				advance_line(cstat);
				if (!cstat->curr_line) {
					return 1;
				}
			} while(!(*cstat->next_char));
		} else if (*cstat->next_char == BEGINCOMMENT) {
			retval = do_new_comment(cstat, depth+1);
			if (retval) {
				return retval;
			}
		} else {
			cstat->next_char++;
		}
	};

	cstat->next_char++;  /* Advance past ENDCOMMENT */
	ptr = cstat->next_char;
	while (*ptr) {
		if (in_str) {
			if (*ptr == ENDSTRING) {
				in_str = 0;
			}
		} else {
			if (*ptr == BEGINSTRING) {
				in_str = 1;
			} else if (*ptr == ENDSTRING) {
				in_str = 1;
				break;
			}
		}
		ptr++;
	}
	if (in_str) {
		compiler_warning(
			cstat,
			"Warning on line %i: Unterminated string may indicate unterminated comment. Comment starts on line %i.",
			cstat->lineno, cstat->start_comment
		);
	}
	if (!(*cstat->next_char))
		advance_line(cstat);
	if (depth && !cstat->curr_line) /* EOF? Don't care if done (depth==0) */
		return 1;
	return 0;
}

/* skip comments */
void
do_comment(COMPSTATE * cstat, int depth)
{
	unsigned int next_char = 0;  /* Save state if needed. */
	int lineno = 0;
	struct line *curr_line = NULL;
	int macrosubs = 0;
	int retval = 0;

	if (!depth && !cstat->force_comment) {
		next_char = cstat->line_copy?
			cstat->next_char - cstat->line_copy : 0;
		macrosubs = cstat->macrosubs;
		lineno = cstat->lineno;
		curr_line = cstat->curr_line;
	}

	if (!depth) {
		if ((retval = do_new_comment(cstat, 0))) {
			if (cstat->force_comment) {
				switch (retval) {
				case 1:
					v_abort_compile(cstat, "Unterminated comment.");
					break;
				case 2:
					v_abort_compile(cstat, "Expected comment.");
					break;
				case 3:
					v_abort_compile(cstat, "Comments nested too deep (more than 7 levels).");
					break;
				}
				return;
			} else {
				/* Set back up, drop through for retry. */
				if (cstat->line_copy) {
					free((void *) cstat->line_copy);
					cstat->line_copy = NULL;
				}
				cstat->curr_line = curr_line;
				cstat->macrosubs = macrosubs;
				cstat->lineno = lineno;
				if (cstat->curr_line) {
					cstat->next_char = (cstat->line_copy = alloc_string(cstat->curr_line->this_line)) + next_char;
				} else {
					cstat->next_char = (cstat->line_copy = NULL);
				}
			}
		} else {
			/* Comment hunt worked, new-style. */
			return;
		}
	}

	if (do_old_comment(cstat)) {
	  v_abort_compile(cstat, "Unterminated comment.");
	}
}

int
is_preprocessor_conditional(const char* tmpptr)
{
	if (!string_compare(tmpptr, "$ifdef"))
		return 1;
	else if (!string_compare(tmpptr, "$ifndef"))
		return 1;
	else if (!string_compare(tmpptr, "$iflib"))
		return 1;
	else if (!string_compare(tmpptr, "$ifnlib"))
		return 1;
	else if (!string_compare(tmpptr, "$ifver"))
		return 1;
	else if (!string_compare(tmpptr, "$iflibver"))
		return 1;
	else if (!string_compare(tmpptr, "$ifnver"))
		return 1;
	else if (!string_compare(tmpptr, "$ifnlibver"))
		return 1;
	else if (!string_compare(tmpptr, "$ifcancall"))
		return 1;
	else if (!string_compare(tmpptr, "$ifncancall"))
		return 1;

	return 0;
}


/* handle compiler directives */
void
do_directive(COMPSTATE * cstat, char *direct)
{
	char temp[BUFFER_LEN];
	char *tmpname = NULL;
	char *tmpptr = NULL;
	int i = 0;
	int j;

	strcpy(temp, ++direct);

	if (!(temp[0])) {
		v_abort_compile(cstat, "I don't understand that compiler directive!");
	}
	if (!string_compare(temp, "define")) {
		tmpname = (char *) next_token_raw(cstat);
		if (!tmpname)
			v_abort_compile(cstat, "Unexpected end of file looking for $define name.");
		i = 0;
		while ((tmpptr = (char *) next_token_raw(cstat)) &&
			   (string_compare(tmpptr, "$enddef"))) {
			char *cp;

			for (cp = tmpptr; i < (BUFFER_LEN / 2) && *cp;) {
				if (*tmpptr == BEGINSTRING && cp != tmpptr &&
					(*cp == ENDSTRING || *cp == BEGINESCAPE)) {
					temp[i++] = BEGINESCAPE;
				}
				temp[i++] = *cp++;
			}
			if (*tmpptr == BEGINSTRING)
				temp[i++] = ENDSTRING;
			temp[i++] = ' ';
			free(tmpptr);
			if (i > (BUFFER_LEN / 2))
				v_abort_compile(cstat, "$define definition too long.");
		}
		if (i)
			i--;
		temp[i] = '\0';
		if (!tmpptr)
			v_abort_compile(cstat, "Unexpected end of file in $define definition.");
		free(tmpptr);
		(void) insert_def(cstat, tmpname, temp);
		free(tmpname);

	} else if (!string_compare(temp, "cleardefs")) {
		char nextToken[BUFFER_LEN];

		purge_defs(cstat); /* Get rid of all defs first. */
		include_internal_defs(cstat); /* Always include internal defs. */
		while(*cstat->next_char && isspace(*cstat->next_char))
			cstat->next_char++; /* eating leading spaces */
		strcpy(nextToken, cstat->next_char);
		tmpname = nextToken;
		while (*cstat->next_char)
			cstat->next_char++;
		advance_line(cstat);
		if (!tmpname || !*tmpname || MLevel(OWNER(cstat->program)) < 4)
		{
			include_defs(cstat, OWNER(cstat->program));
			include_defs(cstat, (dbref) 0);
		}

	} else if (!string_compare(temp, "enddef")) {
		v_abort_compile(cstat, "$enddef without a previous matching $define.");

	} else if (!string_compare(temp, "def")) {
		tmpname = (char *) next_token_raw(cstat);
		if (!tmpname)
			v_abort_compile(cstat, "Unexpected end of file looking for $def name.");
		(void) insert_def(cstat, tmpname, cstat->next_char);
		while (*cstat->next_char)
			cstat->next_char++;
		advance_line(cstat);
		free(tmpname);

	} else if (!string_compare(temp, "pubdef")) {
		char *holder = NULL;

		tmpname = (char *) next_token_raw(cstat);
		holder = tmpname;
		if (!tmpname)
			v_abort_compile(cstat, "Unexpected end of file looking for $pubdef name.");

		if (string_compare(tmpname, ":") &&
			(index(tmpname, '/') ||
			index(tmpname, ':') ||
			Prop_SeeOnly(tmpname) ||
			Prop_Hidden(tmpname) ||
			Prop_System(tmpname)))
		{
			free(tmpname);
			v_abort_compile(cstat, "Invalid $pubdef name.  No /, :, @ nor ~ are allowed.");
		} else {
			if (!string_compare(tmpname, ":")) {
				remove_property(cstat->program, "/_defs");
			} else {
				const char *defstr = NULL;
				char propname[BUFFER_LEN];
				int doitset = 1;

				while(*cstat->next_char && isspace(*cstat->next_char))
					cstat->next_char++; /* eating leading spaces */
				defstr = cstat->next_char;

				if (*tmpname == '\\') {
					char *temppropstr = NULL;

					(void) *tmpname++;
					snprintf(propname, sizeof(propname), "/_defs/%s", tmpname);
					temppropstr = (char *) get_property_class(cstat->program, propname);
					if (temppropstr ) {
						doitset = 0;
					}
				} else {
					snprintf(propname, sizeof(propname), "/_defs/%s", tmpname);
				}

				if (doitset) {
					if (defstr && *defstr) {
						add_property(cstat->program, propname, defstr, 0);
					} else {
						remove_property(cstat->program, propname);
					}
				}
			}

		}
		while (*cstat->next_char)
			cstat->next_char++;
		advance_line(cstat);
		free(holder);

    } else if (!string_compare(temp, "libdef")) {
		char *holder = NULL;

		tmpname = (char *) next_token_raw(cstat);
		holder = tmpname;

		if (!tmpname)
			v_abort_compile(cstat, "Unexpected end of file looking for $libdef name.");

		if (index(tmpname, '/') ||
			index(tmpname, ':') ||
			Prop_SeeOnly(tmpname) ||
			Prop_Hidden(tmpname) ||
			Prop_System(tmpname))
		{
			free(tmpname);
			v_abort_compile(cstat, "Invalid $libdef name.  No /, :, @, nor ~ are allowed.");
		} else {
			char propname[BUFFER_LEN];
			char defstr[BUFFER_LEN];
			int doitset = 1;

			while(*cstat->next_char && isspace(*cstat->next_char))
				cstat->next_char++; /* eating leading spaces */

			if (*tmpname == '\\') {
				char *temppropstr = NULL;

				(void) *tmpname++;
				snprintf(propname, sizeof(propname), "/_defs/%s", tmpname);
				temppropstr = (char *) get_property_class(cstat->program, propname);
				if (temppropstr ) {
					doitset = 0;
				}
			} else {
				snprintf(propname, sizeof(propname), "/_defs/%s", tmpname);
			}

			snprintf(defstr, sizeof(defstr), "#%i \"%s\" call", cstat->program, tmpname);

			if (doitset) {
				if (defstr && *defstr) {
					add_property(cstat->program, propname, defstr, 0);
				} else {
					remove_property(cstat->program, propname);
				}
			}
		}

		while (*cstat->next_char)
			cstat->next_char++;

		advance_line(cstat);

		free(holder);		
	} else if (!string_compare(temp, "include")) {
		struct match_data md;

		tmpname = (char *) next_token_raw(cstat);
		if (!tmpname)
			v_abort_compile(cstat, "Unexpected end of file while doing $include.");
		{
			char tempa[BUFFER_LEN], tempb[BUFFER_LEN];

			strcpy(tempa, match_args);
			strcpy(tempb, match_cmdname);
			init_match(cstat->descr, cstat->player, tmpname, NOTYPE, &md);
			match_registered(&md);
			match_absolute(&md);
			match_me(&md);
			i = (int) match_result(&md);
			strcpy(match_args, tempa);
			strcpy(match_cmdname, tempb);
		}
		free(tmpname);
		if (((dbref) i == NOTHING) || (i < 0) || (i > db_top)
			|| (Typeof(i) == TYPE_GARBAGE))
			v_abort_compile(cstat, "I don't understand what object you want to $include.");
		include_defs(cstat, (dbref) i);

	} else if (!string_compare(temp, "undef")) {
		tmpname = (char *) next_token_raw(cstat);
		if (!tmpname)
			v_abort_compile(cstat, "Unexpected end of file looking for name to $undef.");
		kill_def(cstat, tmpname);
		free(tmpname);

	} else if (!string_compare(temp, "echo")) {
		notify_nolisten(cstat->player, cstat->next_char, 1);
		while (*cstat->next_char)
			cstat->next_char++;
		advance_line(cstat);

	} else if (!string_compare(temp, "abort")) {
		while(*cstat->next_char && isspace(*cstat->next_char))
			cstat->next_char++; /* eating leading spaces */
		tmpname = (char *) cstat->next_char;
		if (tmpname && *tmpname) {
			v_abort_compile(cstat, tmpname);
		} else {
			v_abort_compile(cstat, "Forced abort for the compile.");
		}

	} else if (!string_compare(temp, "version")) {
		tmpname = (char *) next_token_raw(cstat); 
		if (!ifloat(tmpname))
			v_abort_compile(cstat, "Expected a floating point number for the version.");
		add_property(cstat->program, "_version", tmpname, 0);
		while (*cstat->next_char)
			cstat->next_char++;
		advance_line(cstat);
		free(tmpname);

	} else if (!string_compare(temp, "lib-version")) {
		tmpname = (char *) next_token_raw(cstat);
		if (!ifloat(tmpname))
			v_abort_compile(cstat, "Expected a floating point number for the version.");
		add_property(cstat->program, "_lib-version", tmpname, 0);
		while (*cstat->next_char)
			cstat->next_char++;
		advance_line(cstat);
		free(tmpname);

	} else if (!string_compare(temp, "author")) {
		while(*cstat->next_char && isspace(*cstat->next_char))
			cstat->next_char++; /* eating leading spaces */
		tmpname = (char *) cstat->next_char;
		while (*cstat->next_char)
			cstat->next_char++;
		add_property(cstat->program, "_author", tmpname, 0);
		advance_line(cstat);

	} else if (!string_compare(temp, "note")) {
		while(*cstat->next_char && isspace(*cstat->next_char))
			cstat->next_char++; /* eating leading spaces */
		tmpname = (char *) cstat->next_char;
		while (*cstat->next_char)
			cstat->next_char++;
		add_property(cstat->program, "_note", tmpname, 0);
		advance_line(cstat);

	} else if (!string_compare(temp, "ifdef") || !string_compare(temp, "ifndef")) {
		int invert_flag = !string_compare(temp, "ifndef");
		tmpname = (char *) next_token_raw(cstat);
		if (!tmpname) {
			v_abort_compile(cstat, "Unexpected end of file looking for $ifdef condition.");
		}
		if (*tmpname == '"') {
			strcpy(temp, tmpname+1);
		} else {
			strcpy(temp, tmpname);
		}
		free(tmpname);
		for (i = 1; temp[i] && (temp[i] != '=') && (temp[i] != '>') && (temp[i] != '<'); i++) ;
		tmpname = &(temp[i]);
		i = (temp[i] == '>') ? 1 : ((temp[i] == '=') ? 0 : ((temp[i] == '<') ? -1 : -2));
		*tmpname = '\0';
		tmpname++;
		tmpptr = (char *) expand_def(cstat, temp);
		if (i == -2) {
			j = (!tmpptr);
			if (tmpptr)
				free(tmpptr);
		} else {
			if (!tmpptr) {
				j = 1;
			} else {
				j = string_compare(tmpptr, tmpname);
				j = !((!i && !j) || ((i * j) > 0));
				free(tmpptr);
			}
		}
		if (invert_flag) {
			j = !j;
		}
		if (j) {
			i = 0;
			while ((tmpptr = (char *) next_token_raw(cstat)) &&
				   (i || ((string_compare(tmpptr, "$else"))
						  && (string_compare(tmpptr, "$endif"))))) {
				if (is_preprocessor_conditional(tmpptr))
					i++;
				else if (!string_compare(tmpptr, "$endif"))
					i--;
				free(tmpptr);
			}
			if (!tmpptr) {
				v_abort_compile(cstat, "Unexpected end of file in $ifdef clause.");
			}
			free(tmpptr);
		}

	} else if (!string_compare(temp, "ifcancall") || !string_compare(temp, "ifncancall")) {
		struct match_data md;

		tmpname = (char *) next_token_raw(cstat);
		if (!tmpname)
			v_abort_compile(cstat, "Unexpected end of file for ifcancall.");
		if (string_compare(tmpname, "this"))
		{
			char tempa[BUFFER_LEN], tempb[BUFFER_LEN];

			strcpy(tempa, match_args);
			strcpy(tempb, match_cmdname);
			init_match(cstat->descr, cstat->player, tmpname, NOTYPE, &md);
			match_registered(&md);
			match_absolute(&md);
			match_me(&md);
			i = (int) match_result(&md);
			strcpy(match_args, tempa);
			strcpy(match_cmdname, tempb);
		} else {
			i = cstat->program;
		}
		free(tmpname);
		if (((dbref) i == NOTHING) || (i < 0) || (i > db_top) || (Typeof(i) == TYPE_GARBAGE))
			v_abort_compile(cstat, "I don't understand what program you want to check in ifcancall.");
		tmpname = (char *) next_token_raw(cstat);
		if (!tmpname || !*tmpname)
		{
			if (tmpptr) {
				free(tmpptr);
			}
			v_abort_compile(cstat, "I don't understand what function you want to check for.");
		}
		while (*cstat->next_char)
			cstat->next_char++;
		advance_line(cstat);
		if (!PROGRAM_CODE(i)) {
			struct line *tmpline;

			tmpline = PROGRAM_FIRST(i);
			PROGRAM_SET_FIRST(i, ((struct line *) read_program(i)));
			do_compile(cstat->descr, OWNER(i), i, 0);
			free_prog_text(PROGRAM_FIRST(i));
			PROGRAM_SET_FIRST(i, tmpline);
		}
		j = 0;
		if (MLevel(OWNER(i)) > 0 &&
			(MLevel(OWNER(cstat->program)) >= 4 || OWNER(i) == OWNER(cstat->program) || Linkable(i))
		) {
			struct publics *pbs;
	
			pbs = PROGRAM_PUBS(i);
			while (pbs) {
				if (!string_compare(tmpname, pbs->subname))
					break;
				pbs = pbs->next;
			}
			if (pbs && MLevel(OWNER(cstat->program)) >= pbs->mlev)
				j = 1;
		}
		free(tmpname);
		if (!string_compare(temp, "ifncancall")) {
			j = !j;
		}
		if (!j) {
			i = 0;
			while ((tmpptr = (char *) next_token_raw(cstat)) &&
				   (i || ((string_compare(tmpptr, "$else"))
						  && (string_compare(tmpptr, "$endif"))))) {
				if (is_preprocessor_conditional(tmpptr))
					i++;
				else if (!string_compare(tmpptr, "$endif"))
					i--;
				free(tmpptr);
			}
			if (!tmpptr) {
				v_abort_compile(cstat, "Unexpected end of file in $ifcancall clause.");
			}
			free(tmpptr);
		}

	} else if (!string_compare(temp, "ifver")  || !string_compare(temp, "iflibver") ||
			   !string_compare(temp, "ifnver") || !string_compare(temp, "ifnlibver")) {
		struct match_data md;
		double verflt = 0;
		double checkflt = 0;
		int needFree = 0;

		tmpname = (char *) next_token_raw(cstat);
		if (!tmpname)
			v_abort_compile(cstat, "Unexpected end of file while doing $ifver.");
		if (string_compare(tmpname, "this"))
		{
			char tempa[BUFFER_LEN], tempb[BUFFER_LEN];

			strcpy(tempa, match_args);
			strcpy(tempb, match_cmdname);
			init_match(cstat->descr, cstat->player, tmpname, NOTYPE, &md);
			match_registered(&md);
			match_absolute(&md);
			match_me(&md);
			i = (int) match_result(&md);
			strcpy(match_args, tempa);
			strcpy(match_cmdname, tempb);
		} else {
			i = cstat->program;
		}
		free(tmpname);
		if (((dbref) i == NOTHING) || (i < 0) || (i > db_top) || (Typeof(i) == TYPE_GARBAGE))
			v_abort_compile(cstat, "I don't understand what object you want to check with $ifver.");
		if (!string_compare(temp, "ifver") || !string_compare(temp, "ifnver")) {
			tmpptr = (char *) get_property_class(i, "_version");
		} else {
			tmpptr = (char *) get_property_class(i, "_lib-version");
		}
		if (!tmpptr || !*tmpptr) {
			tmpptr = (char*)malloc(4 * sizeof(char));
			strcpy(tmpptr, "0.0");
			needFree = 1;
		} else { 
			uncompress(tmpptr);	
		}
		tmpname = (char *) next_token_raw(cstat);
		if (!tmpname || !*tmpname) {
			free(tmpptr);
			free(tmpname);
			v_abort_compile(cstat, "I don't understand what version you want to compare to with $ifver.");
		}
		if (!tmpptr || !ifloat(tmpptr)) {
			verflt = 0.0;
		} else {
			sscanf(tmpptr, "%lg", &verflt);
		}
		if ( needFree )
			free(tmpptr);
		if (!tmpname || !ifloat(tmpname)) {
			checkflt = 0.0;
		} else {
			sscanf(tmpname, "%lg", &checkflt);
		}
		free(tmpname);
		while (*cstat->next_char)
			cstat->next_char++;
		advance_line(cstat);
		j = checkflt <= verflt;
		if (!string_compare(temp, "ifnver") || !string_compare(temp, "ifnlibver")) {
			j = !j;
		}
		if (!j) {
			i = 0;
			while ((tmpptr = (char *) next_token_raw(cstat)) &&
				   (i || ((string_compare(tmpptr, "$else"))
						  && (string_compare(tmpptr, "$endif"))))) {
				if (is_preprocessor_conditional(tmpptr))
					i++;
				else if (!string_compare(tmpptr, "$endif"))
					i--;
				free(tmpptr);
			}
			if (!tmpptr) {
				v_abort_compile(cstat, "Unexpected end of file in $ifver clause.");
			}
			free(tmpptr);
		}

	} else if (!string_compare(temp, "iflib") || !string_compare(temp, "ifnlib")) {
		struct match_data md;
		char tempa[BUFFER_LEN], tempb[BUFFER_LEN];

		tmpname = (char *) next_token_raw(cstat);
		if (!tmpname)
			v_abort_compile(cstat, "Unexpected end of file in $iflib/$ifnlib clause.");

		strcpy(tempa, match_args);
		strcpy(tempb, match_cmdname);
		init_match(cstat->descr, cstat->player, tmpname, NOTYPE, &md);
		match_registered(&md);
		match_absolute(&md);
		match_me(&md);
		i = (int) match_result(&md);
		strcpy(match_args, tempa);
		strcpy(match_cmdname, tempb);

		free(tmpname);
		if ((((dbref) i == NOTHING) || (i < 0) || (i > db_top)
			|| (Typeof(i) == TYPE_GARBAGE)) ? 0 : (Typeof(i) == TYPE_PROGRAM)
		) {
			j = 1;
		} else {
			j = 0;
		}
		if (!string_compare(temp, "ifnlib")) {
			j = !j;
		}
		if (!j) {
			i = 0;
			while ((tmpptr = (char *) next_token_raw(cstat)) &&
				   (i || ((string_compare(tmpptr, "$else"))
						  && (string_compare(tmpptr, "$endif"))))) {
				if (is_preprocessor_conditional(tmpptr))
					i++;
				else if (!string_compare(tmpptr, "$endif"))
					i--;
				free(tmpptr);
			}
			if (!tmpptr) {
				v_abort_compile(cstat, "Unexpected end of file in $iflib clause.");
			}
			free(tmpptr);
		}

	} else if (!string_compare(temp, "else")) {
		i = 0;
		while ((tmpptr = (char *) next_token_raw(cstat)) &&
			   (i || (string_compare(tmpptr, "$endif")))) {
			if (is_preprocessor_conditional(tmpptr))
				i++;
			else if (!string_compare(tmpptr, "$endif"))
				i--;
			free(tmpptr);
		}
		if (!tmpptr) {
			v_abort_compile(cstat, "Unexpected end of file in $else clause.");
		}
		free(tmpptr);

	} else if (!string_compare(temp, "endif")) {

	} else if (!string_compare(temp, "pragma")) {
		/* TODO - move pragmas to its own section for easy expansion. */
		while (*cstat->next_char && isspace(*cstat->next_char))
			cstat->next_char++;
		if (!*cstat->next_char || !(tmpptr = (char *)next_token_raw(cstat)))
			v_abort_compile(cstat, "Pragma requires at least one argument.");
		if (!string_compare(tmpptr, "comment_strict")) {
			/* Do non-recursive comments (old style) */
			cstat->force_comment = 1;
		} else if (!string_compare(tmpptr, "comment_recurse")) {
			/* Do recursive comments ((new) style) */
			cstat->force_comment = 2;
		} else if (!string_compare(tmpptr, "comment_loose")) {
			/* Try to compile with recursive and non-recursive comments
			doing recursive first, then strict on a comment-based
			compile error.  Only throw an error if both fail.  This is
			the default mode. */
			cstat->force_comment = 0;
		} else {
			/* If the pragma is not recognized, it is ignored, with a warning. */
			compiler_warning(
					cstat,
					"Warning on line %i: Pragma %.64s unrecognized.  Ignoring.",
					cstat->lineno, tmpptr
				);
			while (*cstat->next_char)
				cstat->next_char++;
		}
		free(tmpptr);
		if (*cstat->next_char) {
			compiler_warning(
					cstat,
					"Warning on line %i: Ignoring extra pragma arguments: %.256s",
					cstat->lineno, cstat->next_char
				);
			advance_line(cstat);
		}
	} else {
		v_abort_compile(cstat, "Unrecognized compiler directive.");
	}
}


/* return string */
const char *
do_string(COMPSTATE * cstat)
{
	static char buf[BUFFER_LEN];
	int i = 0, quoted = 0;

	buf[i] = *cstat->next_char;
	cstat->next_char++;
	i++;
	while ((quoted || *cstat->next_char != ENDSTRING) && *cstat->next_char) {
		if (*cstat->next_char == '\\' && !quoted) {
			quoted++;
			cstat->next_char++;
		} else if (*cstat->next_char == 'r' && quoted) {
			buf[i++] = '\r';
			cstat->next_char++;
			quoted = 0;
		} else if (*cstat->next_char == '[' && quoted) {
			buf[i++] = ESCAPE_CHAR;
			cstat->next_char++;
			quoted = 0;
		} else {
			buf[i] = *cstat->next_char;
			i++;
			cstat->next_char++;
			quoted = 0;
		}
	}
	if (!*cstat->next_char) {
		abort_compile(cstat, "Unterminated string found at end of line.");
	}
	cstat->next_char++;
	buf[i] = '\0';
	return alloc_string(buf);
}



/* process special.  Performs special processing.
   It sets up FOR and IF structures.  Remember --- for those,
   we've got to set aside an extra argument space.         */
struct INTERMEDIATE *
process_special(COMPSTATE * cstat, const char *token)
{
	static char buf[BUFFER_LEN];
	const char *tok;
	struct INTERMEDIATE *nu;

	if (!string_compare(token, ":")) {
		const char *proc_name;
		int argsflag = 0;

		if (cstat->curr_proc)
			abort_compile(cstat, "Definition within definition.");
		proc_name = next_token(cstat);
		if (!proc_name)
			abort_compile(cstat, "Unexpected end of file within procedure.");

		strcpy(buf, proc_name);
		if (proc_name)
			free((void *) proc_name);
		proc_name = buf;

		if (*proc_name && buf[strlen(buf)-1] == '[') {
			argsflag = 1;
			buf[strlen(buf)-1] = '\0';
			if (!*proc_name)
				abort_compile(cstat, "Bad procedure name.");
		}

		nu = new_inst(cstat);
		nu->no = cstat->nowords++;
		nu->in.type = PROG_FUNCTION;
		nu->in.line = cstat->lineno;
		nu->in.data.mufproc = (struct muf_proc_data*)malloc(sizeof(struct muf_proc_data));
		nu->in.data.mufproc->procname = string_dup(proc_name);
		nu->in.data.mufproc->vars = 0;
		nu->in.data.mufproc->args = 0;
		nu->in.data.mufproc->varnames = NULL;

		cstat->curr_proc = nu;

		if (argsflag) {
			const char* varspec;
			const char* varname;
			int argsdone = 0;
			int outflag = 0;

			do {
				varspec = next_token(cstat);
				if (!varspec)
					abort_compile(cstat, "Unexpected end of file within procedure arguments declaration.");

				if (!strcmp(varspec, "]")) {
					argsdone = 1;
				} else if (!strcmp(varspec, "--")) {
					outflag = 1;
				} else if (!outflag) {
					varname = index(varspec, ':');
					if (varname) {
						varname++;
					} else {
						varname = varspec;
					}
					if (*varname) {
						if (add_scopedvar(cstat, varname, PROG_UNTYPED) < 0)
							abort_compile(cstat, "Variable limit exceeded.");

						nu->in.data.mufproc->vars++;
						nu->in.data.mufproc->args++;
					}
				}
				if (varspec) {
					free((void *) varspec);
				}
			} while(!argsdone);
		}

		add_proc(cstat, proc_name, nu, PROG_UNTYPED);

		return nu;
	} else if (!string_compare(token, ";")) {
		int i, varcnt;

		if (cstat->control_stack)
			abort_compile(cstat, "Unexpected end of procedure definition.");
		if (!cstat->curr_proc)
			abort_compile(cstat, "Procedure end without body.");

		nu = new_inst(cstat);
		nu->no = cstat->nowords++;
		nu->in.type = PROG_PRIMITIVE;
		nu->in.line = cstat->lineno;
		nu->in.data.number = IN_RET;

		varcnt = cstat->curr_proc->in.data.mufproc->vars;
		if (varcnt) {
		    cstat->curr_proc->in.data.mufproc->varnames =
			(const char**)calloc(varcnt, sizeof(char*));
		    for (i = 0; i < varcnt; i++) {
			cstat->curr_proc->in.data.mufproc->varnames[i] = cstat->scopedvars[i];
			cstat->scopedvars[i] = 0;
		    }
		}
		cstat->curr_proc = 0;
		return nu;
	} else if (!string_compare(token, "IF")) {
		nu = new_inst(cstat);
		nu->no = cstat->nowords++;
		nu->in.type = PROG_IF;
		nu->in.line = cstat->lineno;
		nu->in.data.call = 0;
		add_control_structure(cstat, CTYPE_IF, nu);
		return nu;
	} else if (!string_compare(token, "ELSE")) {
		struct INTERMEDIATE *eef;
		int ctrltype = innermost_control_type(cstat);

		switch (ctrltype) {
			case CTYPE_IF:
				break;
			case CTYPE_TRY:
				abort_compile(cstat, "Unterminated TRY-CATCH block at ELSE.");
				break;
			case CTYPE_CATCH:
				abort_compile(cstat, "Unterminated CATCH-ENDCATCH block at ELSE.");
				break;
			case CTYPE_FOR:
			case CTYPE_BEGIN:
				abort_compile(cstat, "Unterminated Loop at ELSE.");
				break;
			default:
				abort_compile(cstat, "ELSE without IF.");
				break;
		}

		nu = new_inst(cstat);
		nu->no = cstat->nowords++;
		nu->in.type = PROG_JMP;
		nu->in.line = cstat->lineno;
		nu->in.data.call = 0;

		eef = pop_control_structure(cstat, CTYPE_IF, 0);
		add_control_structure(cstat, CTYPE_ELSE, nu);
		eef->in.data.number = get_address(cstat, nu, 1);
		return nu;
	} else if (!string_compare(token, "THEN")) {
		/* can't use 'if' because it's a reserved word */
		struct INTERMEDIATE *eef;
		int ctrltype = innermost_control_type(cstat);

		switch (ctrltype) {
			case CTYPE_IF:
			case CTYPE_ELSE:
				break;
			case CTYPE_TRY:
				abort_compile(cstat, "Unterminated TRY-CATCH block at THEN.");
				break;
			case CTYPE_CATCH:
				abort_compile(cstat, "Unterminated CATCH-ENDCATCH block at THEN.");
				break;
			case CTYPE_FOR:
			case CTYPE_BEGIN:
				abort_compile(cstat, "Unterminated Loop at THEN.");
				break;
			default:
				abort_compile(cstat, "THEN without IF.");
				break;
		}

		prealloc_inst(cstat);
		eef = pop_control_structure(cstat, CTYPE_IF, CTYPE_ELSE);
		eef->in.data.number = get_address(cstat, cstat->nextinst, 0);
		return NULL;
	} else if (!string_compare(token, "BEGIN")) {
		prealloc_inst(cstat);
		add_control_structure(cstat, CTYPE_BEGIN, cstat->nextinst);
		return NULL;
	} else if (!string_compare(token, "FOR")) {
		struct INTERMEDIATE *new2, *new3;

		nu = new_inst(cstat);
		nu->no = cstat->nowords++;
		nu->in.type = PROG_PRIMITIVE;
		nu->in.line = cstat->lineno;
		nu->in.data.number = IN_FOR;
		new2 = (nu->next = new_inst(cstat));
		new2->no = cstat->nowords++;
		new2->in.type = PROG_PRIMITIVE;
		new2->in.line = cstat->lineno;
		new2->in.data.number = IN_FORITER;
		new3 = (new2->next = new_inst(cstat));
		new3->no = cstat->nowords++;
		new3->in.line = cstat->lineno;
		new3->in.type = PROG_IF;
		new3->in.data.number = 0;

		add_control_structure(cstat, CTYPE_FOR, new2);
		cstat->nested_fors++;
		return nu;
	} else if (!string_compare(token, "FOREACH")) {
		struct INTERMEDIATE *new2, *new3;

		nu = new_inst(cstat);
		nu->no = cstat->nowords++;
		nu->in.type = PROG_PRIMITIVE;
		nu->in.line = cstat->lineno;
		nu->in.data.number = IN_FOREACH;
		new2 = (nu->next = new_inst(cstat));
		new2->no = cstat->nowords++;
		new2->in.type = PROG_PRIMITIVE;
		new2->in.line = cstat->lineno;
		new2->in.data.number = IN_FORITER;
		new3 = (new2->next = new_inst(cstat));
		new3->no = cstat->nowords++;
		new3->in.line = cstat->lineno;
		new3->in.type = PROG_IF;
		new3->in.data.number = 0;

		add_control_structure(cstat, CTYPE_FOR, new2);
		cstat->nested_fors++;
		return nu;
	} else if (!string_compare(token, "UNTIL")) {
		/* can't use 'if' because it's a reserved word */
		struct INTERMEDIATE *eef;
		struct INTERMEDIATE *curr;
		int ctrltype = innermost_control_type(cstat);

		switch (ctrltype) {
			case CTYPE_FOR:
				cstat->nested_fors--;
			case CTYPE_BEGIN:
				break;
			case CTYPE_TRY:
				abort_compile(cstat, "Unterminated TRY-CATCH block at UNTIL.");
				break;
			case CTYPE_CATCH:
				abort_compile(cstat, "Unterminated CATCH-ENDCATCH block at UNTIL.");
				break;
			case CTYPE_IF:
			case CTYPE_ELSE:
				abort_compile(cstat, "Unterminated IF-THEN at UNTIL.");
				break;
			default:
				abort_compile(cstat, "Loop start not found for UNTIL.");
				break;
		}

		prealloc_inst(cstat);
		resolve_loop_addrs(cstat, get_address(cstat, cstat->nextinst, 1));
		eef = pop_control_structure(cstat, CTYPE_BEGIN, CTYPE_FOR);

		nu = new_inst(cstat);
		nu->no = cstat->nowords++;
		nu->in.type = PROG_IF;
		nu->in.line = cstat->lineno;
		nu->in.data.number = get_address(cstat, eef, 0);

		if (ctrltype == CTYPE_FOR) {
			curr = (nu->next = new_inst(cstat));
			curr->no = cstat->nowords++;
			curr->in.type = PROG_PRIMITIVE;
			curr->in.line = cstat->lineno;
			curr->in.data.number = IN_FORPOP;
		}
		return nu;
	} else if (!string_compare(token, "WHILE")) {
		struct INTERMEDIATE *curr;
		int trycount;
		if (!in_loop(cstat))
			abort_compile(cstat, "Can't have a WHILE outside of a loop.");

		trycount = count_trys_inside_loop(cstat);
		nu = curr = NULL;
		while (trycount-->0) {
			if (!nu) {
				nu = curr = new_inst(cstat);
			} else {
				nu = (nu->next = new_inst(cstat));
			}
			nu->no = cstat->nowords++;
			nu->in.type = PROG_PRIMITIVE;
			nu->in.line = cstat->lineno;
			nu->in.data.number = IN_TRYPOP;
		}
		if (nu) {
			nu = (nu->next = new_inst(cstat));
		} else {
			curr = nu = new_inst(cstat);
		}
		nu->no = cstat->nowords++;
		nu->in.type = PROG_IF;
		nu->in.line = cstat->lineno;
		nu->in.data.number = 0;

		add_loop_exit(cstat, nu);
		return curr;
	} else if (!string_compare(token, "BREAK")) {
		int trycount;
		struct INTERMEDIATE *curr;
		if (!in_loop(cstat))
			abort_compile(cstat, "Can't have a BREAK outside of a loop.");

		trycount = count_trys_inside_loop(cstat);
		nu = curr = NULL;
		while (trycount-->0) {
			if (!nu) {
				nu = curr = new_inst(cstat);
			} else {
				nu = (nu->next = new_inst(cstat));
			}
			nu->no = cstat->nowords++;
			nu->in.type = PROG_PRIMITIVE;
			nu->in.line = cstat->lineno;
			nu->in.data.number = IN_TRYPOP;
		}
		if (nu) {
			nu = (nu->next = new_inst(cstat));
		} else {
			curr = nu = new_inst(cstat);
		}
		nu->no = cstat->nowords++;
		nu->in.type = PROG_JMP;
		nu->in.line = cstat->lineno;
		nu->in.data.number = 0;

		add_loop_exit(cstat, nu);
		return curr;
	} else if (!string_compare(token, "CONTINUE")) {
		/* can't use 'if' because it's a reserved word */
		struct INTERMEDIATE *beef;
		struct INTERMEDIATE *curr;
		int trycount;

		if (!in_loop(cstat))
			abort_compile(cstat, "Can't CONTINUE outside of a loop.");

		beef = locate_control_structure(cstat, CTYPE_FOR, CTYPE_BEGIN);
		trycount = count_trys_inside_loop(cstat);
		nu = curr = NULL;
		while (trycount-->0) {
			if (!nu) {
				nu = curr = new_inst(cstat);
			} else {
				nu = (nu->next = new_inst(cstat));
			}
			nu->no = cstat->nowords++;
			nu->in.type = PROG_PRIMITIVE;
			nu->in.line = cstat->lineno;
			nu->in.data.number = IN_TRYPOP;
		}
		if (nu) {
			nu = (nu->next = new_inst(cstat));
		} else {
			curr = nu = new_inst(cstat);
		}
		nu->no = cstat->nowords++;
		nu->in.type = PROG_JMP;
		nu->in.line = cstat->lineno;
		nu->in.data.number = get_address(cstat, beef, 0);

		return curr;
	} else if (!string_compare(token, "REPEAT")) {
		/* can't use 'if' because it's a reserved word */
		struct INTERMEDIATE *eef;
		struct INTERMEDIATE *curr;
		int ctrltype = innermost_control_type(cstat);

		switch (ctrltype) {
			case CTYPE_FOR:
				cstat->nested_fors--;
			case CTYPE_BEGIN:
				break;
			case CTYPE_TRY:
				abort_compile(cstat, "Unterminated TRY-CATCH block at REPEAT.");
				break;
			case CTYPE_CATCH:
				abort_compile(cstat, "Unterminated CATCH-ENDCATCH block at REPEAT.");
				break;
			case CTYPE_IF:
			case CTYPE_ELSE:
				abort_compile(cstat, "Unterminated IF-THEN at REPEAT.");
				break;
			default:
				abort_compile(cstat, "Loop start not found for REPEAT.");
				break;
		}

		prealloc_inst(cstat);
		resolve_loop_addrs(cstat, get_address(cstat, cstat->nextinst, 1));
		eef = pop_control_structure(cstat, CTYPE_BEGIN, CTYPE_FOR);

		nu = new_inst(cstat);
		nu->no = cstat->nowords++;
		nu->in.type = PROG_JMP;
		nu->in.line = cstat->lineno;
		nu->in.data.number = get_address(cstat, eef, 0);

		if (ctrltype == CTYPE_FOR) {
			curr = (nu->next = new_inst(cstat));
			curr->no = cstat->nowords++;
			curr->in.type = PROG_PRIMITIVE;
			curr->in.line = cstat->lineno;
			curr->in.data.number = IN_FORPOP;
		}

		return nu;
	} else if (!string_compare(token, "TRY")) {
		nu = new_inst(cstat);
		nu->no = cstat->nowords++;
		nu->in.type = PROG_TRY;
		nu->in.line = cstat->lineno;
		nu->in.data.number = 0;

		add_control_structure(cstat, CTYPE_TRY, nu);
		cstat->nested_trys++;

		return nu;
	} else if (!string_compare(token, "CATCH") || !string_compare(token, "CATCH_DETAILED")) {
		/* can't use 'if' because it's a reserved word */
		struct INTERMEDIATE *eef;
		struct INTERMEDIATE *curr;
		struct INTERMEDIATE *jump;
		int ctrltype = innermost_control_type(cstat);

		switch (ctrltype) {
			case CTYPE_TRY:
				break;
			case CTYPE_FOR:
			case CTYPE_BEGIN:
				abort_compile(cstat, "Unterminated Loop at CATCH.");
				break;
			case CTYPE_IF:
			case CTYPE_ELSE:
				abort_compile(cstat, "Unterminated IF-THEN at CATCH.");
				break;
			case CTYPE_CATCH:
			default:
				abort_compile(cstat, "No TRY found for CATCH.");
				break;
		}

		nu = new_inst(cstat);
		nu->no = cstat->nowords++;
		nu->in.type = PROG_PRIMITIVE;
		nu->in.line = cstat->lineno;
		nu->in.data.number = IN_TRYPOP;

		jump = (nu->next = new_inst(cstat));
		jump->no = cstat->nowords++;
		jump->in.type = PROG_JMP;
		jump->in.line = cstat->lineno;
		jump->in.data.number = 0;

		curr = (jump->next = new_inst(cstat));
		curr->no = cstat->nowords++;
		curr->in.type = PROG_PRIMITIVE;
		curr->in.line = cstat->lineno;
		curr->in.data.number = IN_CATCH;
		if (!string_compare(token, "CATCH_DETAILED")) {
			curr->in.data.number = IN_CATCH_DETAILED;
		}

		eef = pop_control_structure(cstat, CTYPE_TRY, 0);
		cstat->nested_trys--;
		eef->in.data.number = get_address(cstat, curr, 0);
		add_control_structure(cstat, CTYPE_CATCH, jump);

		return nu;
	} else if (!string_compare(token, "ENDCATCH")) {
		/* can't use 'if' because it's a reserved word */
		struct INTERMEDIATE *eef;
		int ctrltype = innermost_control_type(cstat);

		switch (ctrltype) {
			case CTYPE_CATCH:
				break;
			case CTYPE_FOR:
			case CTYPE_BEGIN:
				abort_compile(cstat, "Unterminated Loop at ENDCATCH.");
				break;
			case CTYPE_IF:
			case CTYPE_ELSE:
				abort_compile(cstat, "Unterminated IF-THEN at ENDCATCH.");
				break;
			case CTYPE_TRY:
			default:
				abort_compile(cstat, "No CATCH found for ENDCATCH.");
				break;
		}

		prealloc_inst(cstat);
		eef = pop_control_structure(cstat, CTYPE_CATCH, 0);
		eef->in.data.number = get_address(cstat, cstat->nextinst, 0);
		return NULL;
	} else if (!string_compare(token, "CALL")) {
		nu = new_inst(cstat);
		nu->no = cstat->nowords++;
		nu->in.type = PROG_PRIMITIVE;
		nu->in.line = cstat->lineno;
		nu->in.data.number = IN_CALL;
		return nu;
	} else if (!string_compare(token, "WIZCALL") || !string_compare(token, "PUBLIC")) {
		struct PROC_LIST *p;
		struct publics *pub;
		int wizflag = 0;

		if (!string_compare(token, "WIZCALL"))
			wizflag = 1;
		if (cstat->curr_proc)
			abort_compile(cstat, "PUBLIC  or WIZCALL declaration within procedure.");
		tok = next_token(cstat);
		if ((!tok) || !call(cstat, tok))
			abort_compile(cstat, "Subroutine unknown in PUBLIC or WIZCALL declaration.");
		for (p = cstat->procs; p; p = p->next)
			if (!string_compare(p->name, tok))
				break;
		if (!p)
			abort_compile(cstat, "Subroutine unknown in PUBLIC or WIZCALL declaration.");
		if (!cstat->currpubs) {
			cstat->currpubs = (struct publics *) malloc(sizeof(struct publics));

			cstat->currpubs->next = NULL;
			cstat->currpubs->subname = (char *) string_dup(tok);
			if (tok)
				free((void *) tok);
			cstat->currpubs->addr.no = get_address(cstat, p->code, 0);
			cstat->currpubs->mlev = wizflag ? 4 : 1;
			return 0;
		}
		for (pub = cstat->currpubs; pub;) {
			if (!string_compare(tok, pub->subname)) {
				abort_compile(cstat, "Function already declared public.");
			} else {
				if (pub->next) {
					pub = pub->next;
				} else {
					pub->next = (struct publics *) malloc(sizeof(struct publics));

					pub = pub->next;
					pub->next = NULL;
					pub->subname = (char *) string_dup(tok);
					if (tok)
						free((void *) tok);
					pub->addr.no = get_address(cstat, p->code, 0);
					pub->mlev = wizflag ? 4 : 1;
					pub = NULL;
				}
			}
		}
		return 0;
	} else if (!string_compare(token, "VAR")) {
		if (cstat->curr_proc) {
			tok = next_token(cstat);
			if (!tok)
				abort_compile(cstat, "Unexpected end of program.");
			if (add_scopedvar(cstat, tok, PROG_UNTYPED) < 0)
				abort_compile(cstat, "Variable limit exceeded.");
			if (tok)
				free((void *) tok);
			cstat->curr_proc->in.data.mufproc->vars++;
		} else {
			tok = next_token(cstat);
			if (!tok)
				abort_compile(cstat, "Unexpected end of program.");
			if (!add_variable(cstat, tok, PROG_UNTYPED))
				abort_compile(cstat, "Variable limit exceeded.");
			if (tok)
				free((void *) tok);
		}
		return 0;
	} else if (!string_compare(token, "VAR!")) {
		if (cstat->curr_proc) {
			struct INTERMEDIATE *nu;

			tok = next_token(cstat);
			if (!tok)
				abort_compile(cstat, "Unexpected end of program.");
			if (add_scopedvar(cstat, tok, PROG_UNTYPED) < 0)
				abort_compile(cstat, "Variable limit exceeded.");
			if (tok)
				free((void *) tok);

			nu = new_inst(cstat);
			nu->no = cstat->nowords++;
			nu->in.type = PROG_SVAR_BANG;
			nu->in.line = cstat->lineno;
			nu->in.data.number = cstat->curr_proc->in.data.mufproc->vars++;

			return nu;
		} else {
			abort_compile(cstat, "VAR! used outside of procedure.");
		}
		return 0;
	} else if (!string_compare(token, "LVAR")) {
		if (cstat->curr_proc)
			abort_compile(cstat, "Local variable declared within procedure.");
		tok = next_token(cstat);
		if (!tok || (add_localvar(cstat, tok, PROG_UNTYPED) == -1))
			abort_compile(cstat, "Local variable limit exceeded.");
		if (tok)
			free((void *) tok);
		return 0;
	} else {
		snprintf(buf, sizeof(buf), "Unrecognized special form %s found. (%d)", token, cstat->lineno);
		abort_compile(cstat, buf);
	}
}

/* return primitive word. */
struct INTERMEDIATE *
primitive_word(COMPSTATE * cstat, const char *token)
{
	struct INTERMEDIATE *nu, *cur;
	int pnum, loop;

	pnum = get_primitive(token);
	cur = nu = new_inst(cstat);
	if (pnum == IN_RET || pnum == IN_JMP) {
		for (loop = 0; loop < cstat->nested_trys; loop++) {
			cur->no = cstat->nowords++;
			cur->in.type = PROG_PRIMITIVE;
			cur->in.line = cstat->lineno;
			cur->in.data.number = IN_TRYPOP;
			cur->next = new_inst(cstat);
			cur = cur->next;
		}
		for (loop = 0; loop < cstat->nested_fors; loop++) {
			cur->no = cstat->nowords++;
			cur->in.type = PROG_PRIMITIVE;
			cur->in.line = cstat->lineno;
			cur->in.data.number = IN_FORPOP;
			cur->next = new_inst(cstat);
			cur = cur->next;
		}
	}

	cur->no = cstat->nowords++;
	cur->in.type = PROG_PRIMITIVE;
	cur->in.line = cstat->lineno;
	cur->in.data.number = pnum;

	return nu;
}

/* return self pushing word (string) */
struct INTERMEDIATE *
string_word(COMPSTATE * cstat, const char *token)
{
	struct INTERMEDIATE *nu;

	nu = new_inst(cstat);
	nu->no = cstat->nowords++;
	nu->in.type = PROG_STRING;
	nu->in.line = cstat->lineno;
	nu->in.data.string = alloc_prog_string(token);
	return nu;
}

/* return self pushing word (float) */
struct INTERMEDIATE *
float_word(COMPSTATE * cstat, const char *token)
{
	struct INTERMEDIATE *nu;

	nu = new_inst(cstat);
	nu->no = cstat->nowords++;
	nu->in.type = PROG_FLOAT;
	nu->in.line = cstat->lineno;
	sscanf(token, "%lg", &(nu->in.data.fnumber));
	return nu;
}

/* return self pushing word (number) */
struct INTERMEDIATE *
number_word(COMPSTATE * cstat, const char *token)
{
	struct INTERMEDIATE *nu;

	nu = new_inst(cstat);
	nu->no = cstat->nowords++;
	nu->in.type = PROG_INTEGER;
	nu->in.line = cstat->lineno;
	nu->in.data.number = atoi(token);
	return nu;
}

/* do a subroutine call --- push address onto stack, then make a primitive
   CALL.
   */
struct INTERMEDIATE *
call_word(COMPSTATE * cstat, const char *token)
{
	struct INTERMEDIATE *nu;
	struct PROC_LIST *p;

	nu = new_inst(cstat);
	nu->no = cstat->nowords++;
	nu->in.type = PROG_EXEC;
	nu->in.line = cstat->lineno;
	for (p = cstat->procs; p; p = p->next)
		if (!string_compare(p->name, token))
			break;

	nu->in.data.number = get_address(cstat, p->code, 0);
	return nu;
}

struct INTERMEDIATE *
quoted_word(COMPSTATE * cstat, const char *token)
{
	struct INTERMEDIATE *nu;
	struct PROC_LIST *p;

	nu = new_inst(cstat);
	nu->no = cstat->nowords++;
	nu->in.type = PROG_ADD;
	nu->in.line = cstat->lineno;
	for (p = cstat->procs; p; p = p->next)
		if (!string_compare(p->name, token))
			break;

	nu->in.data.number = get_address(cstat, p->code, 0);
	return nu;
}

/* returns number corresponding to variable number.
   We assume that it DOES exist */
struct INTERMEDIATE *
var_word(COMPSTATE * cstat, const char *token)
{
	struct INTERMEDIATE *nu;
	int i, var_no;

	nu = new_inst(cstat);
	nu->no = cstat->nowords++;
	nu->in.type = PROG_VAR;
	nu->in.line = cstat->lineno;
	for (var_no = i = 0; i < MAX_VAR; i++) {
		if (!cstat->variables[i])
			break;
		if (!string_compare(token, cstat->variables[i]))
			var_no = i;
	}
	nu->in.data.number = var_no;

	return nu;
}

struct INTERMEDIATE *
svar_word(COMPSTATE * cstat, const char *token)
{
	struct INTERMEDIATE *nu;
	int i, var_no;

	nu = new_inst(cstat);
	nu->no = cstat->nowords++;
	nu->in.type = PROG_SVAR;
	nu->in.line = cstat->lineno;
	for (i = var_no = 0; i < MAX_VAR; i++) {
		if (!cstat->scopedvars[i])
			break;
		if (!string_compare(token, cstat->scopedvars[i]))
			var_no = i;
	}
	nu->in.data.number = var_no;

	return nu;
}

struct INTERMEDIATE *
lvar_word(COMPSTATE * cstat, const char *token)
{
	struct INTERMEDIATE *nu;
	int i, var_no;

	nu = new_inst(cstat);
	nu->no = cstat->nowords++;
	nu->in.type = PROG_LVAR;
	nu->in.line = cstat->lineno;
	for (i = var_no = 0; i < MAX_VAR; i++) {
		if (!cstat->localvars[i])
			break;
		if (!string_compare(token, cstat->localvars[i]))
			var_no = i;
	}
	nu->in.data.number = var_no;

	return nu;
}

/* check if object is in database before putting it in */
struct INTERMEDIATE *
object_word(COMPSTATE * cstat, const char *token)
{
	struct INTERMEDIATE *nu;
	int objno;

	objno = atol(token + 1);
	nu = new_inst(cstat);
	nu->no = cstat->nowords++;
	nu->in.type = PROG_OBJECT;
	nu->in.line = cstat->lineno;
	nu->in.data.objref = objno;
	return nu;
}



/* support routines for internal data structures. */

/* add procedure to procedures list */
void
add_proc(COMPSTATE * cstat, const char *proc_name, struct INTERMEDIATE *place, int rettype)
{
	struct PROC_LIST *nu;

	nu = (struct PROC_LIST *) malloc(sizeof(struct PROC_LIST));

	nu->name = alloc_string(proc_name);
	nu->returntype = rettype;
	nu->code = place;
	nu->next = cstat->procs;
	cstat->procs = nu;
}

/* add if to control stack */
void
add_control_structure(COMPSTATE * cstat, int typ, struct INTERMEDIATE *place)
{
	struct CONTROL_STACK *nu;

	nu = (struct CONTROL_STACK *) malloc(sizeof(struct CONTROL_STACK));

	nu->place = place;
	nu->type = typ;
	nu->next = cstat->control_stack;
	nu->extra = 0;
	cstat->control_stack = nu;
}

/* add while to current loop's list of exits remaining to be resolved. */
void
add_loop_exit(COMPSTATE * cstat, struct INTERMEDIATE *place)
{
	struct CONTROL_STACK *nu;
	struct CONTROL_STACK *loop;

	loop = cstat->control_stack;

	while (loop && loop->type != CTYPE_BEGIN && loop->type != CTYPE_FOR) {
		loop = loop->next;
	}

	if (!loop)
		return;

	nu = (struct CONTROL_STACK *) malloc(sizeof(struct CONTROL_STACK));

	nu->place = place;
	nu->type = CTYPE_WHILE;
	nu->next = 0;
	nu->extra = loop->extra;
	loop->extra = nu;
}

/* Returns true if a loop start is in the control structure stack. */
int
in_loop(COMPSTATE * cstat)
{
	struct CONTROL_STACK *loop;

	loop = cstat->control_stack;
	while (loop && loop->type != CTYPE_BEGIN && loop->type != CTYPE_FOR) {
		loop = loop->next;
	}
	return (loop != NULL);
}

/* Returns the type of the innermost nested control structure. */
int
innermost_control_type(COMPSTATE * cstat)
{
	struct CONTROL_STACK *ctrl;

	ctrl = cstat->control_stack;
	if (!ctrl)
		return 0;

	return ctrl->type;
}

/* Returns number of TRYs before topmost Loop */
int
count_trys_inside_loop(COMPSTATE* cstat)
{
	struct CONTROL_STACK *loop;
	int count = 0;

	loop = cstat->control_stack;

	while (loop) {
		if (loop->type == CTYPE_FOR || loop->type == CTYPE_BEGIN) {
			break;
		}
		if (loop->type == CTYPE_TRY) {
			count++;
		}
		loop = loop->next;
	}

	return count;
}

/* returns topmost begin or for off the stack */
struct INTERMEDIATE *
locate_control_structure(COMPSTATE* cstat, int type1, int type2)
{
	struct CONTROL_STACK *loop;

	loop = cstat->control_stack;

	while (loop) {
		if (loop->type == type1 || loop->type == type2) {
			return loop->place;
		}
		loop = loop->next;
	}

	return 0;
}

/* checks if topmost loop stack item is a for */
struct INTERMEDIATE *
innermost_control_place(COMPSTATE * cstat, int type1)
{
	struct CONTROL_STACK *ctrl;

	ctrl = cstat->control_stack;
	if (!ctrl)
		return 0;
	if (ctrl->type != type1)
		return 0;

	return ctrl->place;
}

/* Pops off the innermost control structure and returns the place. */
struct INTERMEDIATE *
pop_control_structure(COMPSTATE * cstat, int type1, int type2)
{
	struct CONTROL_STACK *ctrl;
	struct INTERMEDIATE *place;

	ctrl = cstat->control_stack;
	if (!ctrl)
		return NULL;
	if (ctrl->type != type1 && ctrl->type != type2)
		return NULL;

	place = ctrl->place;
	cstat->control_stack = ctrl->next;
	free(ctrl);

	return place;
}

/* pops first while off the innermost control structure, if it's a loop. */
struct INTERMEDIATE *
pop_loop_exit(COMPSTATE * cstat)
{
	struct INTERMEDIATE *temp;
	struct CONTROL_STACK *tofree;
	struct CONTROL_STACK *parent;

	parent = cstat->control_stack;

	if (!parent)
		return 0;
	if (parent->type != CTYPE_BEGIN && parent->type != CTYPE_FOR)
		return 0;
	if (!parent->extra)
		return 0;
	if (parent->extra->type != CTYPE_WHILE)
		return 0;

	temp = parent->extra->place;
	tofree = parent->extra;
	parent->extra = parent->extra->extra;
	free((void *) tofree);
	return temp;
}

void
resolve_loop_addrs(COMPSTATE * cstat, int where)
{
	struct INTERMEDIATE *eef;

	while ((eef = pop_loop_exit(cstat)))
		eef->in.data.number = where;
	eef = innermost_control_place(cstat, CTYPE_FOR);
	if (eef) {
		eef->next->in.data.number = where;
	}
}

/* adds variable.  Return 0 if no space left */
int
add_variable(COMPSTATE * cstat, const char *varname, int valtype)
{
	int i;

	for (i = RES_VAR; i < MAX_VAR; i++)
		if (!cstat->variables[i])
			break;

	if (i == MAX_VAR)
		return 0;

	cstat->variables[i] = alloc_string(varname);
	cstat->variabletypes[i] = valtype;
	return i;
}


/* adds local variable.  Return 0 if no space left */
int
add_scopedvar(COMPSTATE * cstat, const char *varname, int valtype)
{
	int i;

	for (i = 0; i < MAX_VAR; i++)
		if (!cstat->scopedvars[i])
			break;

	if (i == MAX_VAR)
		return -1;

	cstat->scopedvars[i] = alloc_string(varname);
	cstat->scopedvartypes[i] = valtype;
	return i;
}


/* adds local variable.  Return 0 if no space left */
int
add_localvar(COMPSTATE * cstat, const char *varname, int valtype)
{
	int i;

	for (i = 0; i < MAX_VAR; i++)
		if (!cstat->localvars[i])
			break;

	if (i == MAX_VAR)
		return -1;

	cstat->localvars[i] = alloc_string(varname);
	cstat->localvartypes[i] = valtype;
	return i;
}


/* predicates for procedure calls */
int
special(const char *token)
{
	return (token && !(string_compare(token, ":")
					   && string_compare(token, ";")
					   && string_compare(token, "IF")
					   && string_compare(token, "ELSE")
					   && string_compare(token, "THEN")
					   && string_compare(token, "BEGIN")
					   && string_compare(token, "FOR")
					   && string_compare(token, "FOREACH")
					   && string_compare(token, "UNTIL")
					   && string_compare(token, "WHILE")
					   && string_compare(token, "BREAK")
					   && string_compare(token, "CONTINUE")
					   && string_compare(token, "REPEAT")
					   && string_compare(token, "TRY")
					   && string_compare(token, "CATCH")
					   && string_compare(token, "CATCH_DETAILED")
					   && string_compare(token, "ENDCATCH")
					   && string_compare(token, "CALL")
					   && string_compare(token, "PUBLIC")
					   && string_compare(token, "WIZCALL")
					   && string_compare(token, "LVAR")
					   && string_compare(token, "VAR!")
					   && string_compare(token, "VAR")));
}

/* see if procedure call */
int
call(COMPSTATE * cstat, const char *token)
{
	struct PROC_LIST *i;

	for (i = cstat->procs; i; i = i->next)
		if (!string_compare(i->name, token))
			return 1;

	return 0;
}

/* see if it's a quoted procedure name */
int
quoted(COMPSTATE * cstat, const char *token)
{
	return (*token == '\'' && call(cstat, token + 1));
}

/* see if it's an object # */
int
object(const char *token)
{
	if (*token == NUMBER_TOKEN && number(token + 1))
		return 1;
	else
		return 0;
}

/* see if string */
int
string(const char *token)
{
	return (token[0] == '"');
}

int
variable(COMPSTATE * cstat, const char *token)
{
	int i;

	for (i = 0; i < MAX_VAR && cstat->variables[i]; i++)
		if (!string_compare(token, cstat->variables[i]))
			return 1;

	return 0;
}

int
scopedvar(COMPSTATE * cstat, const char *token)
{
	int i;

	for (i = 0; i < MAX_VAR && cstat->scopedvars[i]; i++)
		if (!string_compare(token, cstat->scopedvars[i]))
			return 1;

	return 0;
}

int
localvar(COMPSTATE * cstat, const char *token)
{
	int i;

	for (i = 0; i < MAX_VAR && cstat->localvars[i]; i++)
		if (!string_compare(token, cstat->localvars[i]))
			return 1;

	return 0;
}

/* see if token is primitive */
int
primitive(const char *token)
{
	int primnum;

	primnum = get_primitive(token);
	return (primnum && primnum <= BASE_MAX - PRIMS_INTERNAL_CNT);
}

/* return primitive instruction */
int
get_primitive(const char *token)
{
	hash_data *hd;

	if ((hd = find_hash(token, primitive_list, COMP_HASH_SIZE)) == NULL)
		return 0;
	else {
		return (hd->ival);
	}
}



/* clean up as nicely as we can. */

void
cleanpubs(struct publics *mypub)
{
	struct publics *tmppub;

	while (mypub) {
		tmppub = mypub->next;
		free(mypub->subname);
		free(mypub);
		mypub = tmppub;
	}
}

void
clean_mcpbinds(struct mcp_binding *mypub)
{
	struct mcp_binding *tmppub;

	while (mypub) {
		tmppub = mypub->next;
		free(mypub->pkgname);
		free(mypub->msgname);
		free(mypub);
		mypub = tmppub;
	}
}


void
append_intermediate_chain(struct INTERMEDIATE *chain, struct INTERMEDIATE *add)
{
	while (chain->next)
		chain = chain->next;
	chain->next = add;
}


void
free_intermediate_node(struct INTERMEDIATE *wd)
{
	int varcnt, j;

	if (wd->in.type == PROG_STRING) {
		if (wd->in.data.string)
			free((void *) wd->in.data.string);
	}
	if (wd->in.type == PROG_FUNCTION) {
		free((void*)wd->in.data.mufproc->procname);
		varcnt = wd->in.data.mufproc->vars;
		if (wd->in.data.mufproc->varnames) {
			for (j = 0; j < varcnt; j++) {
				free((void*)wd->in.data.mufproc->varnames[j]);
			}
			free((void*)wd->in.data.mufproc->varnames);
		}
		free((void*)wd->in.data.mufproc);
	}
	free((void *) wd);
}

void
free_intermediate_chain(struct INTERMEDIATE *wd)
{
	struct INTERMEDIATE* tempword;
	while (wd) {
		tempword = wd->next;
		free_intermediate_node(wd);
		wd = tempword;
	}
}

void
cleanup(COMPSTATE * cstat)
{
/*	struct INTERMEDIATE *wd, *tempword; */
	struct CONTROL_STACK *eef, *tempif;
	struct PROC_LIST *p, *tempp;
	int i;

	free_intermediate_chain(cstat->first_word);
	cstat->first_word = 0;

	for (eef = cstat->control_stack; eef; eef = tempif) {
		tempif = eef->next;
		free((void *) eef);
	}
	cstat->control_stack = 0;

	for (p = cstat->procs; p; p = tempp) {
		tempp = p->next;
		free((void *) p->name);
		free((void *) p);
	}
	cstat->procs = 0;

	purge_defs(cstat);
	free_addresses(cstat);

	for (i = RES_VAR; i < MAX_VAR && cstat->variables[i]; i++) {
		free((void *) cstat->variables[i]);
		cstat->variables[i] = 0;
	}

	for (i = 0; i < MAX_VAR && cstat->scopedvars[i]; i++) {
		free((void *) cstat->scopedvars[i]);
		cstat->scopedvars[i] = 0;
	}

	for (i = 0; i < MAX_VAR && cstat->localvars[i]; i++) {
		free((void *) cstat->localvars[i]);
		cstat->localvars[i] = 0;
	}
}



/* copy program to an array */
void
copy_program(COMPSTATE * cstat)
{
	/*
	 * Everything should be peachy keen now, so we don't do any error checking
	 */
	struct INTERMEDIATE *curr;
	struct inst *code;
	int i, j, varcnt;

	if (!cstat->first_word)
		v_abort_compile(cstat, "Nothing to compile.");

	code = (struct inst *) malloc(sizeof(struct inst) * (cstat->nowords + 1));

	i = 0;
	for (curr = cstat->first_word; curr; curr = curr->next) {
		code[i].type = curr->in.type;
		code[i].line = curr->in.line;
		switch (code[i].type) {
		case PROG_PRIMITIVE:
		case PROG_INTEGER:
		case PROG_SVAR:
		case PROG_SVAR_AT:
		case PROG_SVAR_AT_CLEAR:
		case PROG_SVAR_BANG:
		case PROG_LVAR:
		case PROG_LVAR_AT:
		case PROG_LVAR_AT_CLEAR:
		case PROG_LVAR_BANG:
		case PROG_VAR:
			code[i].data.number = curr->in.data.number;
			break;
		case PROG_FLOAT:
			code[i].data.fnumber = curr->in.data.fnumber;
			break;
		case PROG_STRING:
			code[i].data.string = curr->in.data.string ?
					alloc_prog_string(curr->in.data.string->data) : 0;
			break;
		case PROG_FUNCTION:
			code[i].data.mufproc = (struct muf_proc_data*)malloc(sizeof(struct muf_proc_data));
			code[i].data.mufproc->procname = string_dup(curr->in.data.mufproc->procname);
			code[i].data.mufproc->vars = varcnt = curr->in.data.mufproc->vars;
			code[i].data.mufproc->args = curr->in.data.mufproc->args;
			if (varcnt) {
			    if (curr->in.data.mufproc->varnames) {
				code[i].data.mufproc->varnames = (const char**)calloc(varcnt, sizeof(char*));
				for (j = 0; j < varcnt; j++) {
				    code[i].data.mufproc->varnames[j] = string_dup(curr->in.data.mufproc->varnames[j]);
				}
			    } else {
				code[i].data.mufproc->varnames = NULL;
			    }
			} else {
				code[i].data.mufproc->varnames = NULL;
			}
			break;
		case PROG_OBJECT:
			code[i].data.objref = curr->in.data.objref;
			break;
		case PROG_ADD:
			code[i].data.addr = alloc_addr(cstat, curr->in.data.number, code);
			break;
		case PROG_IF:
		case PROG_JMP:
		case PROG_EXEC:
		case PROG_TRY:
			code[i].data.call = code + curr->in.data.number;
			break;
		default:
			v_abort_compile(cstat, "Unknown type compile!  Internal error.");
			break;
		}
		i++;
	}
	PROGRAM_SET_CODE(cstat->program, code);
}

void
set_start(COMPSTATE * cstat)
{
	PROGRAM_SET_SIZ(cstat->program, cstat->nowords);

	/* address instr no is resolved before this gets called. */
	PROGRAM_SET_START(cstat->program, (PROGRAM_CODE(cstat->program) + cstat->procs->code->no));
}


/* allocate and initialize data linked structure. */
struct INTERMEDIATE *
alloc_inst(void)
{
	struct INTERMEDIATE *nu;

	nu = (struct INTERMEDIATE *) malloc(sizeof(struct INTERMEDIATE));

	nu->next = 0;
	nu->no = 0;
	nu->line = 0;
	nu->flags = 0;
	nu->in.type = 0;
	nu->in.line = 0;
	nu->in.data.number = 0;
	return nu;
}

struct INTERMEDIATE *
prealloc_inst(COMPSTATE * cstat)
{
	struct INTERMEDIATE *ptr;
	struct INTERMEDIATE *nu;

	/* only allocate at most one extra instr */
	if (cstat->nextinst)
		return NULL;

	nu = alloc_inst();

	nu->flags |= (cstat->nested_trys > 0) ? INTMEDFLG_INTRY : 0;

	if (!cstat->nextinst) {
		cstat->nextinst = nu;
	} else {
		for (ptr = cstat->nextinst; ptr->next; ptr = ptr->next);
		ptr->next = nu;
	}

	nu->no = cstat->nowords;

	return nu;
}

struct INTERMEDIATE *
new_inst(COMPSTATE * cstat)
{
	struct INTERMEDIATE *nu;

	nu = cstat->nextinst;
	if (!nu) {
		nu = alloc_inst();
	}
	cstat->nextinst = nu->next;
	nu->next = NULL;

	nu->flags |= (cstat->nested_trys > 0) ? INTMEDFLG_INTRY : 0;

	return nu;
}

/* allocate an address */
struct prog_addr *
alloc_addr(COMPSTATE * cstat, int offset, struct inst *codestart)
{
	struct prog_addr *nu;

	nu = (struct prog_addr *) malloc(sizeof(struct prog_addr));

	nu->links = 1;
	nu->progref = cstat->program;
	nu->data = codestart + offset;
	return nu;
}

void
free_prog(dbref prog)
{
	int i;
	struct inst *c = PROGRAM_CODE(prog);
	int siz = PROGRAM_SIZ(prog);

	if (c) {
		if (PROGRAM_INSTANCES(prog)) {
			fprintf(stderr, "Freeing program %s with %d instances reported\n",
					unparse_object(GOD, prog), PROGRAM_INSTANCES(prog));
		}
		i = scan_instances(prog);
		if (i) {
			fprintf(stderr, "Freeing program %s with %d instances found\n",
					unparse_object(GOD, prog), i);
		}
		for (i = 0; i < siz; i++) {
			if (c[i].type == PROG_ADD) {
				if (c[i].data.addr->links != 1) {
					fprintf(stderr, "Freeing address in %s with link count %d\n",
							unparse_object(GOD, prog), c[i].data.addr->links);
				}
				free(c[i].data.addr);
			}
			else
			{
				CLEAR(c + i);
			}
		}
		free((void *) c);
	}
	PROGRAM_SET_CODE(prog, 0);
	PROGRAM_SET_SIZ(prog, 0);
	PROGRAM_SET_START(prog, 0);
}

long
size_prog(dbref prog)
{
	struct inst *c;
	long i, j, varcnt, siz, byts = 0;

	c = PROGRAM_CODE(prog);
	if (!c)
		return 0;
	siz = PROGRAM_SIZ(prog);
	for (i = 0L; i < siz; i++) {
		byts += sizeof(*c);
		if (c[i].type == PROG_FUNCTION) {
			byts += strlen(c[i].data.mufproc->procname) + 1;
			varcnt = c[i].data.mufproc->vars;
			if (c[i].data.mufproc->varnames) {
				for (j = 0; j < varcnt; j++) {
					byts += strlen(c[i].data.mufproc->varnames[j]) + 1;
				}
				byts += sizeof(char**) * varcnt;
			}
			byts += sizeof(struct muf_proc_data);
		} else if (c[i].type == PROG_STRING && c[i].data.string) {
			byts += strlen(c[i].data.string->data) + 1;
			byts += sizeof(struct shared_string);
		} else if ((c[i].type == PROG_ADD))
			byts += sizeof(struct prog_addr);
	}
	byts += size_pubs(PROGRAM_PUBS(prog));
	return byts;
}

static void
add_primitive(int val)
{
	hash_data hd;

	hd.ival = val;
	if (add_hash(base_inst[val - BASE_MIN], hd, primitive_list, COMP_HASH_SIZE) == NULL)
		panic("Out of memory");
	else
		return;
}

void
clear_primitives(void)
{
	kill_hash(primitive_list, COMP_HASH_SIZE, 0);
	return;
}

void
init_primitives(void)
{
	int i;

	clear_primitives();
	for (i = BASE_MIN; i <= BASE_MAX; i++) {
		add_primitive(i);
	}
	IN_FORPOP = get_primitive(" FORPOP");
	IN_FORITER = get_primitive(" FORITER");
	IN_FOR = get_primitive(" FOR");
	IN_FOREACH = get_primitive(" FOREACH");
	IN_TRYPOP = get_primitive(" TRYPOP");
	log_status("MUF: %d primitives exist.\n", BASE_MAX);
}