gurba-0.40/
gurba-0.40/bin/
gurba-0.40/lib/
gurba-0.40/lib/cmds/guild/fighter/
gurba-0.40/lib/cmds/monster/
gurba-0.40/lib/cmds/race/catfolk/
gurba-0.40/lib/cmds/race/dwarf/
gurba-0.40/lib/cmds/verb/
gurba-0.40/lib/daemons/data/
gurba-0.40/lib/data/boards/
gurba-0.40/lib/data/messages/
gurba-0.40/lib/data/players/
gurba-0.40/lib/design/
gurba-0.40/lib/domains/gurba/
gurba-0.40/lib/domains/gurba/guilds/fighter/
gurba-0.40/lib/domains/gurba/monsters/
gurba-0.40/lib/domains/gurba/objects/armor/
gurba-0.40/lib/domains/gurba/objects/clothing/
gurba-0.40/lib/domains/gurba/objects/weapons/
gurba-0.40/lib/domains/gurba/vendors/
gurba-0.40/lib/kernel/cmds/admin/
gurba-0.40/lib/kernel/daemons/
gurba-0.40/lib/kernel/include/
gurba-0.40/lib/kernel/lib/
gurba-0.40/lib/kernel/net/
gurba-0.40/lib/kernel/sys/
gurba-0.40/lib/logs/
gurba-0.40/lib/pub/
gurba-0.40/lib/std/modules/languages/
gurba-0.40/lib/std/races/
gurba-0.40/lib/std/races/monsters/
gurba-0.40/lib/wiz/fudge/
gurba-0.40/lib/wiz/spud/
gurba-0.40/src/host/beos/
gurba-0.40/src/host/pc/res/
gurba-0.40/src/kfun/
gurba-0.40/src/lpc/
gurba-0.40/src/parser/
gurba-0.40/tmp/
# include "comp.h"
# include "str.h"
# include "array.h"
# include "object.h"
# include "xfloat.h"
# include "interpret.h"
# include "data.h"
# include "hash.h"
# include "table.h"
# include "node.h"
# include "compile.h"
# include "control.h"

typedef struct _oh_ {		/* object hash table */
    hte chain;			/* hash table chain */
    object *obj;		/* object */
    short index;		/* -1: direct; 0: new; 1: indirect */
    struct _oh_ **next;		/* next in linked list */
} oh;

static hashtab *otab;		/* object hash table */
static oh **olist;		/* list of all object hash table entries */

/*
 * NAME:	oh->init()
 * DESCRIPTION:	initialize the object hash table
 */
static void oh_init()
{
    otab = ht_new(OMERGETABSZ, OBJHASHSZ);
}

/*
 * NAME:	oh->new()
 * DESCRIPTION:	put an object in the hash table
 */
static oh *oh_new(name)
char *name;
{
    register oh **h;

    h = (oh **) ht_lookup(otab, name, FALSE);
    if (*h == (oh *) NULL) {
	/*
	 * new object
	 */
	*h = ALLOC(oh, 1);
	(*h)->chain.next = (hte *) NULL;
	(*h)->chain.name = name;
	(*h)->index = 0;		/* new object */
	(*h)->next = olist;
	olist = h;
    }

    return *h;
}

/*
 * NAME:	oh->clear()
 * DESCRIPTION:	clear the object hash table
 */
static void oh_clear()
{
    register oh **h, *f;

    for (h = olist; h != (oh **) NULL; ) {
	f = *h;
	h = f->next;
	FREE(f);
    }
    olist = (oh **) NULL;

    if (otab != (hashtab *) NULL) {
	ht_del(otab);
	otab = (hashtab *) NULL;
    }
}


# define VFH_CHUNK	64

typedef struct _vfh_ {		/* variable/function hash table */
    hte chain;			/* hash table chain */
    string *str;		/* name string */
    oh *ohash;			/* controlling object hash table entry */
    unsigned short ct;		/* function call, or variable type */
    short index;		/* definition table index */
} vfh;

typedef struct _vfhchunk_ {
    vfh vf[VFH_CHUNK];		/* vfh chunk */
    struct _vfhchunk_ *next;	/* next in linked list */
} vfhchunk;

static vfhchunk *vfhclist;	/* linked list of all vfh chunks */
static int vfhchunksz = VFH_CHUNK; /* size of current vfh chunk */

/*
 * NAME:	vfh->new()
 * DESCRIPTION:	create a new vfh table element
 */
static void vfh_new(str, ohash, ct, idx, addr)
string *str;
oh *ohash;
unsigned short ct;
short idx;
vfh **addr;
{
    register vfh *h;

    if (vfhchunksz == VFH_CHUNK) {
	register vfhchunk *l;

	l = ALLOC(vfhchunk, 1);
	l->next = vfhclist;
	vfhclist = l;
	vfhchunksz = 0;
    }
    h = &vfhclist->vf[vfhchunksz++];
    h->chain.next = (hte *) *addr;
    *addr = h;
    h->chain.name = str->text;
    str_ref(h->str = str);
    h->ohash = ohash;
    h->ct = ct;
    h->index = idx;
}

/*
 * NAME:	vfh->clear()
 * DESCRIPTION:	clear the vfh tables
 */
static void vfh_clear()
{
    register vfhchunk *l, *f;
    register vfh *vf;

    for (l = vfhclist; l != (vfhchunk *) NULL; ) {
	for (vf = l->vf; vfhchunksz != 0; vf++, --vfhchunksz) {
	    str_del(vf->str);
	}
	vfhchunksz = VFH_CHUNK;
	f = l;
	l = l->next;
	FREE(f);
    }
    vfhclist = (vfhchunk *) NULL;
}


typedef struct _lab_ {
    string *str;		/* label */
    oh *ohash;			/* entry in hash table */
    struct _lab_ *next;		/* next label */
} lab;

static lab *labels;		/* list of labeled inherited objects */

/*
 * NAME:	lab->new()
 * DESCRIPTION:	declare a new inheritance label
 */
static void lab_new(str, ohash)
string *str;
oh *ohash;
{
    register lab *l;

    l = ALLOC(lab, 1);
    str_ref(l->str = str);
    l->ohash = ohash;
    l->next = labels;
    labels = l;
}

/*
 * NAME:	lab->find()
 * DESCRIPTION:	find a labeled object in the list
 */
static oh *lab_find(name)
char *name;
{
    register lab *l;

    for (l = labels; l != (lab *) NULL; l = l->next) {
	if (strcmp(l->str->text, name) == 0) {
	    return l->ohash;
	}
    }
    return (oh *) NULL;
}

/*
 * NAME:	lab->clear()
 * DESCRIPTION:	wipe out all inheritance label declarations
 */
static void lab_clear()
{
    register lab *l, *f;

    l = labels;
    while (l != (lab *) NULL) {
	str_del(l->str);
	f = l;
	l = l->next;
	FREE(f);
    }
    labels = (lab *) NULL;
}


/*
 * NAME:	cmp_proto()
 * DESCRIPTION:	Compare two prototypes. Return TRUE if equal.
 */
static bool cmp_proto(prot1, prot2)
register char *prot1, *prot2;
{
    register int i;

    /* check if either prototype is implicit */
    if (PROTO_FTYPE(prot1) == T_IMPLICIT || PROTO_FTYPE(prot2) == T_IMPLICIT) {
	return TRUE;
    }

    /* check if classes are equal */
    if ((PROTO_CLASS(prot1) ^ PROTO_CLASS(prot2)) &
	UCHAR(~(C_COMPILED | C_UNDEFINED))) {
	return FALSE;
    }

    /* compare return type */
    if (PROTO_FTYPE(prot1) != PROTO_FTYPE(prot2)) {
	return FALSE;
    }

    /* check if the number of arguments is equal */
    if ((i=PROTO_NARGS(prot1)) != PROTO_NARGS(prot2)) {
	return FALSE;
    }

    /* compare argument types */
    for (prot1 = PROTO_ARGS(prot1), prot2 = PROTO_ARGS(prot2); i > 0; --i) {
	if (*prot1++ != *prot2++) {
	    return FALSE;
	}
    }

    return TRUE;	/* equal */
}


# define MAX_INHERITS		255

static oh *directs[MAX_INHERITS];	/* direct inherit table */
static int ndirects;			/* # directly inh. objects */
static int ninherits;			/* # inherited objects */
static hashtab *vtab;			/* variable merge table */
static hashtab *ftab;			/* function merge table */
static unsigned short nvars;		/* # variables */
static unsigned short nfloats;		/* # float variables */
static unsigned short nsymbs;		/* # symbols */
static int nfclash;			/* # prototype clashes */
static Uint nifcalls;			/* # inherited function calls */

/*
 * NAME:	control->init()
 * DESCRIPTION:	initialize control block construction
 */
void ctrl_init()
{
    oh_init();
    vtab = ht_new(VFMERGETABSZ, VFMERGEHASHSZ);
    ftab = ht_new(VFMERGETABSZ, VFMERGEHASHSZ);
}

/*
 * NAME:	control->vardefs()
 * DESCRIPTION:	put variable definitions from an inherited object into the
 *		variable merge table
 */
static void ctrl_vardefs(ohash, ctrl)
oh *ohash;
register control *ctrl;
{
    register dvardef *v;
    register int n;
    register string *str;
    register vfh **h;

    v = d_get_vardefs(ctrl);
    for (n = 0; n < ctrl->nvardefs; n++) {
	if (v->type == T_FLOAT) {
	    nfloats++;	/* inherited float variable */
	}
	/*
	 * Add only non-private variables, and check if a variable with the
	 * same name hasn't been inherited already.
	 */
	if (!(v->class & C_PRIVATE)) {
	    str = d_get_strconst(ctrl, v->inherit, v->index);
	    h = (vfh **) ht_lookup(vtab, str->text, FALSE);
	    if (*h == (vfh *) NULL) {
		/* new variable */
		vfh_new(str, ohash, v->type, n, h);
	    } else {
	       /* duplicate variable */
	       c_error("multiple inheritance of variable %s (/%s, /%s)",
		       str->text, (*h)->ohash->chain.name, ohash->chain.name);
	    }
	}
	v++;
    }
}

/*
 * NAME:	control->funcdef()
 * DESCRIPTION:	put a function definition from an inherited object into
 *		the function merge table
 */
static void ctrl_funcdef(ctrl, idx, ohash)
register control *ctrl;
register int idx;
oh *ohash;
{
    register vfh **h, **l;
    register dfuncdef *f;
    string *str;

    f = &ctrl->funcdefs[idx];
    str = d_get_strconst(ctrl, f->inherit, f->index);
    h = (vfh **) ht_lookup(ftab, str->text, FALSE);
    if (*h == (vfh *) NULL) {
	/*
	 * New function (-1: no calls to it yet)
	 */
	vfh_new(str, ohash, -1, idx, h);
	if (ctrl->ninherits != 1 ||
	    (f->class & (C_STATIC | C_UNDEFINED)) != C_STATIC) {
	    /* don't count static functions from the auto object */
	    nsymbs++;
	}
    } else if ((*h)->ohash != ohash) {
	register dinherit *inh;
	register int n;
	object *o;
	char *prot1, *prot2;
	vfh **marker;

	/*
	 * Different prototypes.
	 */
	if ((*h)->ohash == (oh *) NULL) {
	    /*
	     * Skip old clash marker.
	     */
	    marker = h;
	    h = (vfh **) &(*h)->chain.next;
	} else {
	    marker = (vfh **) NULL;
	}

	prot1 = ctrl->prog + f->offset;
	/*
	 * compare with the other prototypes
	 */
	for (l = h;
	     *l != (vfh *) NULL && strcmp((*l)->chain.name, str->text) == 0;
	     l = (vfh **) &(*l)->chain.next) {
	    /*
	     * First check if the function in the merge table is in
	     * an object inherited by the currently inherited object.
	     */
	    o = (*l)->ohash->obj;
	    ctrl = ohash->obj->ctrl;
	    inh = ctrl->inherits;
	    n = ctrl->ninherits;
	    ctrl = o->ctrl;
	    while (--n != 0) {
		if (o == inh->obj) {
		    /*
		     * redefined inherited function
		     */
		    if (marker == (vfh **) NULL && ctrl->ninherits == 1 &&
			(ctrl->funcdefs[(*l)->index].class &
				  (C_STATIC | C_UNDEFINED)) == C_STATIC) {
			/*
			 * redefine static function in auto object
			 */
			nsymbs++;
		    }
		    (*l)->ohash = ohash;
		    (*l)->index = idx;
		    return;
		}
		inh++;
	    }

	    /*
	     * Now check if the function in the currently inherited object is
	     * inherited by the object that defines the function in the merge
	     * table.
	     */
	    o = inh->obj;
	    inh = ctrl->inherits;
	    n = ctrl->ninherits;
	    while (--n != 0) {
		if (o == inh->obj) {
		    /*
		     * previously inherited, and redefined function
		     */
		    return;
		}
		inh++;
	    }

	    /*
	     * Check for clashes between the prototypes.
	     */
	    prot2 = ctrl->prog + ctrl->funcdefs[(*l)->index].offset;
	    if (((f->class | PROTO_CLASS(prot2)) & (C_NOMASK | C_UNDEFINED)) ==
								    C_NOMASK) {
		/*
		 * a nomask function is inherited more than once
		 */
		c_error("multiple inheritance of nomask function %s (/%s, /%s)",
			str->text, (*l)->ohash->chain.name, ohash->chain.name);
		return;
	    }
	    if (((f->class | PROTO_CLASS(prot2)) & C_UNDEFINED) &&
		!cmp_proto(prot1, prot2)) {
		/*
		 * prototype conflict
		 */
		c_error("unequal prototypes for function %s (/%s, /%s)",
			str->text, (*l)->ohash->chain.name, ohash->chain.name);
		return;
	    }
	}

	ctrl = (*h)->ohash->obj->ctrl;
	prot2 = ctrl->prog + ctrl->funcdefs[(*h)->index].offset;
	if (PROTO_CLASS(prot2) & C_UNDEFINED) {
	    /*
	     * replace undefined function
	     */
	    vfh_new(str, ohash, -1, idx, h);
	    return;
	}
	if (f->class & C_UNDEFINED) {
	    /*
	     * add undefined prototype at the end
	     */
	    vfh_new(str, ohash, -1, idx, l);
	    return;
	}

	if (marker != (vfh **) NULL) {
	    /*
	     * replace clash marker with function
	     */
	    h = marker;
	    (*h)->ohash = ohash;
	    (*h)->ct = -1;
	    (*h)->index = idx;
	} else {
	    /*
	     * first clash: insert before old prototype
	     */
	    nfclash++;
	    vfh_new(str, ohash, -1, idx, h);
	}

	/* insert new clash marker */
	vfh_new(str, (oh *) NULL, 0, 0, h);
    }
}

/*
 * NAME:	control->funcdefs()
 * DESCRIPTION:	put function definitions from an inherited object into
 *		the function merge table
 */
static void ctrl_funcdefs(ctrl)
register control *ctrl;
{
    register unsigned short n;
    register object *o;
    register dsymbol *symb;

    /*
     * The symbol table rather than the function definition table is
     * used here.
     */
    for (n = ctrl->nsymbols, symb = d_get_symbols(ctrl); n > 0; --n, symb++) {
	o = ctrl->inherits[UCHAR(symb->inherit)].obj;
	ctrl_funcdef(o->ctrl, UCHAR(symb->index), oh_new(o->chain.name));
    }
}

/*
 * NAME:	control->inherit()
 * DESCRIPTION:	inherit an object
 */
bool ctrl_inherit(f, from, obj, label)
register frame *f;
char *from;
object *obj;
string *label;
{
    register oh *ohash;

    if (!(obj->flags & O_MASTER)) {
	c_error("cannot inherit cloned object");
	return TRUE;
    }
    if (O_UPGRADING(obj)) {
	c_error("cannot inherit object being upgraded");
	return TRUE;
    }

    ohash = oh_new(obj->chain.name);
    if (label != (string *) NULL) {
	/*
	 * use a label
	 */
	if (lab_find(label->text) != (oh *) NULL) {
	    c_error("redeclaration of label %s", label->text);
	}
	lab_new(label, ohash);
    }

    if (ohash->index == 0) {
	register control *ctrl;
	register object *o;
	dinherit *inh;
	register int i, n;

	/*
	 * new inherited object
	 */
	ctrl = o_control(obj);
	inh = ctrl->inherits;
	if (ndirects != 0 && strcmp(inh->obj->chain.name,
				    directs[0]->obj->chain.name) != 0) {
	    c_error("inherited different auto objects");
	}
	for (i = ctrl->ninherits; i > 0; --i) {
	    /*
	     * check all the objects inherited by the object now inherited
	     */
	    o = (inh++)->obj;
	    if (o->count == 0) {
		Uint ocount;

		if (strcmp(o->chain.name, from) == 0) {
		    /*
		     * inheriting old instance of the same object
		     */
		    c_error("cycle in inheritance");
		    return TRUE;
		}

		/*
		 * This object inherits an object that has been destructed.
		 * Give the driver object a chance to destruct it.
		 */
		(--f->sp)->type = T_OBJECT;
		f->sp->oindex = obj->index;
		f->sp->u.objcnt = ocount = obj->count;
		call_driver_object(f, "recompile", 1);
		i_del_value(f->sp++);
		if (obj->count != ocount) {
		    return FALSE;	/* recompile this object */
		}
	    }

	    ohash = oh_new(o->chain.name);
	    if (ohash->index == 0) {
		/*
		 * inherit a new object
		 */
		ohash->obj = o;
		ohash->index = 2;	/* indirect */
		ctrl = o_control(o);

		/*
		 * put variables in variable merge table
		 */
		ctrl_vardefs(ohash, ctrl);
		/*
		 * ensure that relevant parts are loaded
		 */
		d_get_prog(ctrl);
		d_get_funcdefs(ctrl);
	    } else if (ohash->obj != o) {
		/*
		 * inherited two different objects with same name
		 */
		c_error("inherited different instances of /%s",
			o->chain.name);
		return TRUE;
	    } else if (ohash->index < 0 && !(ohash->obj->flags & O_AUTO)) {
		/*
		 * Inherit an object which previously was inherited
		 * directly (but is not the auto object). Mark it as
		 * indirect now.
		 */
		ohash->index = 1;	/* indirect, but immediate */
		n = ohash->obj->ctrl->ninherits - 1;
		ninherits -= n;
	    }
	}

	n = ctrl->ninherits;
	if (n > 1) {
	    /*
	     * Don't count the auto object, unless it is the auto object
	     * only.
	     */
	    --n;
	}
	ninherits += n;
	directs[ndirects++] = ohash;
	ohash->index = -1;	/* direct */

	/*
	 * put functions in function merge table
	 */
	ctrl_funcdefs(ctrl);

    } else if (ohash->obj != obj) {
	/*
	 * inherited two objects with same name
	 */
	c_error("inherited different instances of /%s", obj->chain.name);
    } else if (ohash->index == 2) {
	/* not inherited directly before */
	directs[ndirects++] = ohash;
	ohash->index = 1;	/* indirect, but immediate */
    }

    if (ninherits >= MAX_INHERITS || ndirects == MAX_INHERITS) {
	c_error("too many objects inherited");
    }

    return TRUE;
}


# define STRING_CHUNK	64

typedef struct _strchunk_ {
    string *s[STRING_CHUNK];		/* chunk of strings */
    struct _strchunk_ *next;		/* next in string chunk list */
} strchunk;

# define FCALL_CHUNK	64

typedef struct _fcchunk_ {
    char *f[FCALL_CHUNK];		/* function reference */
    struct _fcchunk_ *next;		/* next in fcall chunk list */
} fcchunk;

typedef struct _cfunc_ {
    dfuncdef func;			/* function name/type */
    char *name;				/* function name */
    char *proto;			/* function prototype */
    char *prog;				/* function program */
    unsigned short progsize;		/* function program size */
} cfunc;

static control *newctrl;		/* the new control block */
static oh *newohash;			/* fake ohash entry for new object */
static strchunk *str_list;		/* list of string chunks */
static int strchunksz = STRING_CHUNK;	/* size of current string chunk */
static Uint nstrs;			/* # of strings in all string chunks */
static fcchunk *fclist;			/* list of fcall chunks */
static int fcchunksz = FCALL_CHUNK;	/* size of current fcall chunk */
static cfunc *functions;		/* defined functions table */
static int nfdefs, fdef;		/* # defined functions, current func */
static Uint progsize;			/* size of all programs and protos */
static dvardef *variables;		/* defined variables */
static Uint nfcalls;			/* # function calls */

/*
 * NAME:	control->create()
 * DESCRIPTION:	make an initial control block
 */
void ctrl_create()
{
    register dinherit *new;
    register control *ctrl;
    register unsigned short n;
    register int i, count;

    /*
     * create a new control block
     */
    newohash = oh_new("/");		/* unique name */
    newohash->index = count = ninherits;
    newctrl = d_new_control();
    new = newctrl->inherits = ALLOC(dinherit, newctrl->ninherits = count + 1);
    new += count;
    nvars = 0;

    if (ninherits > 0) {
	/*
	 * initialize the virtually inherited objects
	 */
	for (n = ndirects; n > 0; ) {
	    register oh *ohash;
	    register dinherit *old;

	    ohash = directs[--n];
	    if (ohash->index < 0) {		/* directly inherited */
		ctrl = ohash->obj->ctrl;
		i = ctrl->ninherits - 1;
		old = ctrl->inherits + i;
		/*
		 * do this ctrl->ninherits - 1 times, but at least once
		 */
		do {
		    ohash = oh_new(old->obj->chain.name);
		    --old;
		    (--new)->obj = ohash->obj;
		    ohash->index = --count;	/* may happen more than once */
		} while (--i > 0);
	    }
	}

	/*
	 * Fix function offsets and variable offsets, and collect all string
	 * constants from inherited objects and put them in the string merge
	 * table.
	 */
	for (count = 0; count < ninherits; count++) {
	    i = oh_new(new->obj->chain.name)->index;
	    if (i == count) {
		ctrl = new->obj->ctrl;
		i = ctrl->ninherits - 1;
		new->funcoffset = nifcalls;
		n = ctrl->nfuncalls - ctrl->inherits[i].funcoffset;
		nifcalls += n;
		if (nifcalls > UINDEX_MAX && nifcalls - n <= UINDEX_MAX) {
		    c_error("inherited too many function calls");
		}
		new->varoffset = nvars;
		nvars += ctrl->nvardefs;

		for (n = ctrl->nstrings; n > 0; ) {
		    --n;
		    str_put(d_get_strconst(ctrl, i, n),
			    ((Uint) count << 16) | n);
		}
	    } else {
		new->funcoffset = newctrl->inherits[i].funcoffset;
		new->varoffset = newctrl->inherits[i].varoffset;
	    }
	    new++;
	}
    }

    /*
     * stats for new object
     */
    new->obj = (object *) NULL;
    new->funcoffset = nifcalls;
    new->varoffset = newctrl->nvariables = nvars;
    newctrl->nfloats = nfloats;

    /*
     * prepare for construction of a new control block
     */
    functions = ALLOC(cfunc, 256);
    variables = ALLOC(dvardef, 256);
    progsize = 0;
    nstrs = 0;
    nfdefs = 0;
    nvars = 0;
    nfcalls = 0;
    nfloats = 0;
}

/*
 * NAME:	control->dstring()
 * DESCRIPTION:	define a new (?) string constant
 */
long ctrl_dstring(str)
string *str;
{
    register Uint desc, new;

    desc = str_put(str, new = ((Uint) ninherits << 16) | nstrs);
    if (desc == new) {
	/*
	 * it is really a new string
	 */
	if (strchunksz == STRING_CHUNK) {
	    register strchunk *l;

	    l = ALLOC(strchunk, 1);
	    l->next = str_list;
	    str_list = l;
	    strchunksz = 0;
	}
	str_ref(str_list->s[strchunksz++] = str);
	if (nstrs == USHRT_MAX) {
	    c_error("too many string constants");
	}
	nstrs++;
    }
    if (desc >> 16 == ninherits) {
	desc |= 0x01000000L;	/* mark it as new */
    }
    return desc;
}

/*
 * NAME:	control->dproto()
 * DESCRIPTION:	define a new function prototype
 */
void ctrl_dproto(str, proto)
register string *str;
register char *proto;
{
    register vfh **h;
    register dfuncdef *func;
    register int i;
    long s;

    i = -1;	/* default: no calls yet */

    /* first check if prototype exists already */
    h = (vfh **) ht_lookup(ftab, str->text, FALSE);
    if (*h != (vfh *) NULL) {
	register char *proto2;
	register control *ctrl;

	/*
	 * redefinition
	 */
	if ((*h)->ohash == newohash) {
	    /*
	     * redefinition of new function
	     */
	    proto2 = functions[(*h)->index].proto;
	    if (!((PROTO_CLASS(proto) | PROTO_CLASS(proto2)) & C_UNDEFINED)) {
		/*
		 * both prototypes are from functions
		 */
		c_error("multiple declaration of function %s", str->text);
	    } else if (!cmp_proto(proto, proto2)) {
		if ((PROTO_CLASS(proto) ^ PROTO_CLASS(proto2)) & C_UNDEFINED) {
		    /*
		     * declaration does not match prototype
		     */
		    c_error("declaration does not match prototype of %s",
			    str->text);
		} else {
		    /*
		     * unequal prototypes
		     */
		    c_error("unequal prototypes for function %s", str->text);
		}
	    } else if (!(PROTO_CLASS(proto) & C_UNDEFINED) ||
		       PROTO_FTYPE(proto2) == T_IMPLICIT) {
		/*
		 * replace undefined prototype
		 */
		if (PROTO_FTYPE(proto2) == T_IMPLICIT &&
		    (PROTO_CLASS(proto) & C_PRIVATE)) {
		    /* private function replaces implicit prototype */
		    --nsymbs;
		}
		i = PROTO_SIZE(proto);
		progsize += i - PROTO_SIZE(proto2);
		functions[fdef = (*h)->index].proto =
			(char *) memcpy(REALLOC(proto2, char, 0, i), proto, i);
		functions[fdef].func.class = PROTO_CLASS(proto);
	    }
	    return;
	}

	/*
	 * redefinition of inherited function
	 */
	if ((*h)->ohash == (oh *) NULL) {
	    if (!(PROTO_CLASS(proto) & C_PRIVATE)) {
		/*
		 * mask inherited functions
		 */
		--nfclash;
	    }
	} else {
	    ctrl = (*h)->ohash->obj->ctrl;
	    proto2 = ctrl->prog + ctrl->funcdefs[(*h)->index].offset;
	    if ((PROTO_CLASS(proto2) & C_UNDEFINED) &&
		!cmp_proto(proto, proto2)) {
		/*
		 * declaration does not match inherited prototype
		 */
		c_error("inherited different prototype for %s (/%s)",
			str->text, (*h)->ohash->chain.name);
	    } else if ((PROTO_CLASS(proto) & C_UNDEFINED) &&
		       PROTO_FTYPE(proto2) != T_IMPLICIT &&
		       cmp_proto(proto, proto2)) {
		/*
		 * there is no point in replacing an identical prototype
		 */
		return;
	    } else if ((PROTO_CLASS(proto2) & (C_NOMASK | C_UNDEFINED)) ==
								    C_NOMASK) {
		/*
		 * attempt to redefine nomask function
		 */
		c_error("redeclaration of nomask function %s (/%s)",
			str->text, (*h)->ohash->chain.name);
	    }

	    if (!(PROTO_CLASS(proto) & C_PRIVATE) && ctrl->ninherits == 1 &&
		(PROTO_CLASS(proto2) & (C_STATIC | C_UNDEFINED)) == C_STATIC) {
		/*
		 * replace static function in auto object by non-private
		 * function
		 */
		nsymbs++;
	    }
	    i = (*h)->ct;	/* take old call index */
	}
	/*
	 * insert the definition before the old one in the hash table
	 */
    } else if (!(PROTO_CLASS(proto) & C_PRIVATE)) {
	/*
	 * add new prototype to symbol table
	 */
	nsymbs++;
    }

    if (nfdefs == 255) {
	c_error("too many functions declared");
	return;
    }

    /*
     * Actual definition.
     */
    vfh_new(str, newohash, i, nfdefs, h);
    s = ctrl_dstring(str);
    i = PROTO_SIZE(proto);
    functions[nfdefs].name = str->text;
    functions[nfdefs].proto = (char *) memcpy(ALLOC(char, i), proto, i);
    functions[nfdefs].progsize = 0;
    progsize += i;
    func = &functions[nfdefs++].func;
    func->class = PROTO_CLASS(proto);
    func->inherit = s >> 16;
    func->index = s;
}

/*
 * NAME:	control->dfunc()
 * DESCRIPTION:	define a new function
 */
void ctrl_dfunc(str, proto)
string *str;
char *proto;
{
    fdef = nfdefs;
    ctrl_dproto(str, proto);
}

/*
 * NAME:	control->dprogram()
 * DESCRIPTION:	define a function body
 */
void ctrl_dprogram(prog, size)
char *prog;
unsigned int size;
{
    functions[fdef].prog = prog;
    functions[fdef].progsize = size;
    progsize += size;
}

/*
 * NAME:	control->dvar()
 * DESCRIPTION:	define a variable
 */
void ctrl_dvar(str, class, type)
string *str;
unsigned int class, type;
{
    register vfh **h;
    register dvardef *var;
    register long s;

    h = (vfh **) ht_lookup(vtab, str->text, FALSE);
    if (*h != (vfh *) NULL) {
	if ((*h)->ohash == newohash) {
	    c_error("redeclaration of variable %s", str->text);
	    return;
	} else if (!(class & C_PRIVATE)) {
	    /*
	     * non-private redeclaration of a variable
	     */
	    c_error("redeclaration of variable %s (/%s)", str->text,
		    (*h)->ohash->chain.name);
	    return;
	}
    }
    if (nvars == 255) {
	c_error("too many variables declared");
	return;
    }

    /* actually define the variable */
    vfh_new(str, newohash, type, nvars, h);
    s = ctrl_dstring(str);
    var = &variables[nvars++];
    var->class = class;
    var->inherit = s >> 16;
    var->index = s;
    var->type = type;

    if (type == T_FLOAT) {
	nfloats++;
    }
}

/*
 * NAME:	control->ifcall()
 * DESCRIPTION:	call an inherited function
 */
char *ctrl_ifcall(str, label, call)
string *str;
char *label;
long *call;
{
    register control *ctrl;
    register oh *ohash;
    register short index;
    short inherit;

    if (label != (char *) NULL) {
	register dsymbol *symb;

	/* first check if the label exists */
	ohash = lab_find(label);
	if (ohash == (oh *) NULL) {
	    c_error("undefined label %s", label);
	    return (char *) NULL;
	}
	inherit = ohash->index;
	symb = ctrl_symb(ctrl = ohash->obj->ctrl, str->text, str->len);
	if (symb == (dsymbol *) NULL) {
	    /*
	     * It may seem strange to allow label::kfun, but remember that they
	     * are supposed to be inherited in the auto object.
	     */
	    index = kf_func(str->text);
	    if (index >= 0) {
		/* kfun call */
		*call = ((long) KFCALL << 24) | index;
		return KFUN(index).proto;
	    }
	    c_error("undefined function %s::%s", label, str->text);
	    return (char *) NULL;
	}
	ohash = oh_new(ctrl->inherits[UCHAR(symb->inherit)].obj->chain.name);
	index = UCHAR(symb->index);
    } else {
	register vfh *h;

	/* check if the function exists */
	inherit = ninherits;
	h = *(vfh **) ht_lookup(ftab, str->text, FALSE);
	if (h == (vfh *) NULL || (h->ohash == newohash &&
	    ((h=(vfh *) h->chain.next) == (vfh *) NULL ||
	     strcmp(h->chain.name, str->text) != 0))) {

	    index = kf_func(str->text);
	    if (index >= 0) {
		/* kfun call */
		*call = ((long) KFCALL << 24) | index;
		return KFUN(index).proto;
	    }
	    c_error("undefined function ::%s", str->text);
	    return (char *) NULL;
	}
	ohash = h->ohash;
	if (ohash == (oh *) NULL) {
	    /*
	     * call to multiple inherited function
	     */
	    c_error("ambiguous call to function ::%s", str->text);
	    return (char *) NULL;
	}
	index = h->index;
	label = "";
    }

    ctrl = ohash->obj->ctrl;
    if (ctrl->funcdefs[index].class & C_UNDEFINED) {
	c_error("undefined function %s::%s", label, str->text);
	return (char *) NULL;
    }
    inherit = (ohash->index == 0) ? 0 : ninherits + 1 - ohash->index;
    *call = ((long) DFCALL << 24) | ((long) inherit << 8) | index;
    return ctrl->prog + ctrl->funcdefs[index].offset;
}

/*
 * NAME:	control->fcall()
 * DESCRIPTION:	call a function
 */
char *ctrl_fcall(str, call, typechecking)
string *str;
long *call;
int typechecking;
{
    register vfh *h;
    char *proto;

    h = *(vfh **) ht_lookup(ftab, str->text, FALSE);
    if (h == (vfh *) NULL) {
	static char uproto[] = { (char) C_UNDEFINED, T_IMPLICIT, 0 };
	register short kf;

	/*
	 * undefined function
	 */
	kf = kf_func(str->text);
	if (kf >= 0) {
	    /* kfun call */
	    *call = ((long) KFCALL << 24) | kf;
	    return KFUN(kf).proto;
	}

	/* create an undefined prototype for the function */
	if (nfdefs == 255) {
	    c_error("too many undefined functions");
	    return (char *) NULL;
	}
	ctrl_dproto(str, proto = uproto);
	h = *(vfh **) ht_lookup(ftab, str->text, FALSE);
    } else if (h->ohash == newohash) {
	/*
	 * call to new function
	 */
	proto = functions[h->index].proto;
    } else if (h->ohash == (oh *) NULL) {
	/*
	 * call to multiple inherited function
	 */
	c_error("ambiguous call to function %s", str->text);
	return (char *) NULL;
    } else {
	register control *ctrl;

	/*
	 * call to inherited function
	 */
	ctrl = h->ohash->obj->ctrl;
	proto = ctrl->prog + ctrl->funcdefs[h->index].offset;
    }

    if (typechecking && PROTO_FTYPE(proto) == T_IMPLICIT) {
	/* don't allow calls to implicit prototypes when typechecking */
	c_error("undefined function %s", str->text);
	return (char *) NULL;
    }

    if ((PROTO_CLASS(proto) & C_PRIVATE) ||
	(PROTO_CLASS(proto) & (C_NOMASK | C_UNDEFINED)) == C_NOMASK ||
	((PROTO_CLASS(proto) & (C_STATIC | C_UNDEFINED)) == C_STATIC &&
	 h->ohash->index == 0)) {
	/* direct call */
	if (h->ohash->index == 0) {
	    *call = ((long) DFCALL << 24) | h->index;
	} else {
	    *call = ((long) DFCALL << 24) |
		    ((ninherits + 1L - h->ohash->index) << 8) |
		    h->index;
	}
    } else {
	/* ordinary function call */
	*call = ((long) FCALL << 24) | ((long) h->ohash->index << 8) | h->index;
    }
    return proto;
}

/*
 * NAME:	control->gencall()
 * DESCRIPTION:	generate a function call
 */
unsigned short ctrl_gencall(call)
long call;
{
    register vfh *h;
    char *name;
    short inherit, index;

    inherit = (call >> 8) & 0xff;
    index = call & 0xff;
    if (inherit == ninherits) {
	name = functions[index].name;
    } else {
	control *ctrl;
	dfuncdef *f;

	ctrl = newctrl->inherits[inherit].obj->ctrl;
	f = ctrl->funcdefs + index;
	name = d_get_strconst(ctrl, f->inherit, f->index)->text;
    }
    h = *(vfh **) ht_lookup(ftab, name, FALSE);
    if (h->ct == (unsigned short) -1) {
	/*
	 * add to function call table
	 */
	if (fcchunksz == FCALL_CHUNK) {
	    register fcchunk *l;

	    l = ALLOC(fcchunk, 1);
	    l->next = fclist;
	    fclist = l;
	    fcchunksz = 0;
	}
	fclist->f[fcchunksz++] = name;
	if (nifcalls + nfcalls == UINDEX_MAX) {
	    c_error("too many function calls");
	}
	h->ct = nfcalls++;
    }
    return h->ct;
}

/*
 * NAME:	control->var()
 * DESCRIPTION:	handle a variable reference
 */
unsigned short ctrl_var(str, ref)
string *str;
long *ref;
{
    register vfh *h;

    /* check if the variable exists */
    h = *(vfh **) ht_lookup(vtab, str->text, TRUE);
    if (h == (vfh *) NULL) {
	c_error("undeclared variable %s", str->text);
	if (nvars < 255) {
	    /* don't repeat this error */
	    ctrl_dvar(str, 0, T_MIXED);
	}
	return T_MIXED;
    }

    if (h->ohash->index == 0 && ninherits != 0) {
	*ref = h->index;
    } else {
	*ref = ((ninherits + 1L - h->ohash->index) << 8) | h->index;
    }
    return h->ct;	/* the variable type */
}


/*
 * NAME:	control->chkfuncs()
 * DESCRIPTION:	check for multiple function definitions
 */
bool ctrl_chkfuncs()
{
    if (nfclash != 0) {
	register hte **t;
	register unsigned short sz;
	register vfh *f, *n;

	c_error("inherited multiple instances of:");
	for (t = ftab->table, sz = ftab->size; nfclash > 0 && sz > 0; t++, --sz)
	{
	    for (f = (vfh *) *t; f != NULL; f = n) {
		n = (vfh *) f->chain.next;
		if (f->ohash == (oh *) NULL) {
		    /*
		     * list a clash (only the first two)
		     */
		    c_error("  %s (/%s, %s)", f->chain.name,
			    n->ohash->chain.name,
			    ((vfh *) n->chain.next)->ohash->chain.name);
		    --nfclash;
		} else if (n != (vfh *) NULL && n->ohash == (oh *) NULL &&
			   strcmp(n->str->text, f->str->text) == 0 &&
			   !(PROTO_CLASS(functions[f->index].proto) &C_PRIVATE))
		{
		    /*
		     * this function was redefined, and must not be listed
		     */
		    n = (vfh *) n->chain.next;
		}
	    }
	}
	return FALSE;
    }
    return TRUE;
}


/*
 * NAME:	control->mkstrings()
 * DESCRIPTION:	create the string table for the new control block
 */
static void ctrl_mkstrings()
{
    register string **s;
    register strchunk *l, *f;
    register unsigned short i;
    register long strsize;

    strsize = 0;
    if ((newctrl->nstrings = nstrs) != 0) {
	newctrl->strings = ALLOC(string*, newctrl->nstrings);
	s = newctrl->strings + nstrs;
	i = strchunksz;
	for (l = str_list; l != (strchunk *) NULL; ) {
	    while (i > 0) {
		*--s = l->s[--i];	/* already referenced */
		strsize += (*s)->len;
	    }
	    i = STRING_CHUNK;
	    f = l;
	    l = l->next;
	    FREE(f);
	}
	str_list = (strchunk *) NULL;
	strchunksz = i;
    }
    newctrl->strsize = strsize;
}

/*
 * NAME:	control->mkfuncs()
 * DESCRIPTION:	make the function definition table for the control block
 */
static void ctrl_mkfuncs()
{
    register char *p;
    register dfuncdef *d;
    register cfunc *f;
    register int i;
    register unsigned int len;

    newctrl->progsize = progsize;
    if ((newctrl->nfuncdefs = nfdefs) != 0) {
	p = newctrl->prog = ALLOC(char, progsize);
	d = newctrl->funcdefs = ALLOC(dfuncdef, nfdefs);
	f = functions;
	for (i = nfdefs; i > 0; --i) {
	    *d = f->func;
	    d->offset = p - newctrl->prog;
	    memcpy(p, f->proto, len = PROTO_SIZE(f->proto));
	    p += len;
	    if (f->progsize != 0) {
		/* more than just a prototype */
		memcpy(p, f->prog, f->progsize);
		p += f->progsize;
	    }
	    d++;
	    f++;
	}
    }
}

/*
 * NAME:	control->mkvars()
 * DESCRIPTION:	make the variable definition table for the control block
 */
static void ctrl_mkvars()
{
    if ((newctrl->nvardefs = nvars) != 0) {
	newctrl->vardefs = ALLOC(dvardef, nvars);
	memcpy(newctrl->vardefs, variables, nvars * sizeof(dvardef));
    }
}

/*
 * NAME:	control->mkfcalls()
 * DESCRIPTION:	make the function call table for the control block
 */
static void ctrl_mkfcalls()
{
    register char *fc;
    register int i;
    register vfh *h;
    register fcchunk *l;

    newctrl->nfuncalls = nifcalls + nfcalls;
    if (newctrl->nfuncalls == 0) {
	return;
    }
    fc = newctrl->funcalls = ALLOC(char, 2 * newctrl->nfuncalls);
    for (i = 0; i < ninherits; i++) {
	/*
	 * Walk through the list of inherited objects, starting with the auto
	 * object, and fill in the function call table segment for each object
	 * once.
	 */
	if (oh_new(newctrl->inherits[i].obj->chain.name)->index == i) {
	    register char *ofc;
	    register dfuncdef *f;
	    register control *ctrl, *ctrl2;
	    register uindex j, n;

	    /*
	     * build the function call segment, based on the function call
	     * table of the inherited object
	     */
	    ctrl = newctrl->inherits[i].obj->ctrl;
	    j = ctrl->ninherits - 1;
	    ofc = d_get_funcalls(ctrl) + 2L * ctrl->inherits[j].funcoffset;
	    for (n = ctrl->nfuncalls - ctrl->inherits[j].funcoffset; n > 0; --n)
	    {
		ctrl2 = ctrl->inherits[UCHAR(ofc[0])].obj->ctrl;
		f = &ctrl2->funcdefs[UCHAR(ofc[1])];
		if ((f->class & C_PRIVATE) ||
		    (f->class & (C_NOMASK | C_UNDEFINED)) == C_NOMASK ||
		    ((f->class & (C_STATIC | C_UNDEFINED)) == C_STATIC &&
		     ofc[0] == 0)) {
		    /*
		     * keep old call
		     */
		    *fc++ = (ofc[0] == 0) ? 0 : ofc[0] + i - j;
		    *fc++ = ofc[1];
		} else {
		    h = *(vfh **) ht_lookup(ftab,
					    d_get_strconst(ctrl2, f->inherit,
							   f->index)->text,
					    FALSE);
		    if (h->ohash->index == ninherits &&
			(functions[h->index].func.class & C_PRIVATE)) {
			/*
			 * private redefinition of (guaranteed non-private)
			 * inherited function
			 */
			h = (vfh *) h->chain.next;
		    }
		    *fc++ = h->ohash->index;
		    *fc++ = h->index;
		}
		ofc += 2;
	    }
	}
    }

    /*
     * Now fill in the function call entries for the object just compiled.
     */
    fc += 2L * nfcalls;
    i = fcchunksz;
    for (l = fclist; l != (fcchunk *) NULL; l = l->next) {
	do {
	    h = *(vfh **) ht_lookup(ftab, l->f[--i], FALSE);
	    *--fc = h->index;
	    *--fc = h->ohash->index;
	} while (i != 0);
	i = FCALL_CHUNK;
    }
}

/*
 * NAME:	control->mksymbs()
 * DESCRIPTION:	make the symbol table for the control block
 */
static void ctrl_mksymbs()
{
    register unsigned short i, n, x, ncoll;
    register dsymbol *symtab, *coll;

    if ((newctrl->nsymbols = nsymbs) == 0) {
	return;
    }

    /* initialize */
    symtab = newctrl->symbols = ALLOC(dsymbol, nsymbs);
    for (i = nsymbs; i > 0; --i) {
	symtab->next = -1;	/* mark as unused */
	symtab++;
    }
    symtab = newctrl->symbols;
    coll = ALLOCA(dsymbol, nsymbs);
    ncoll = 0;

    /*
     * Go down the list of inherited objects, adding the functions of each
     * object once.
     */
    for (i = 0; i <= ninherits; i++) {
	register dfuncdef *f;
	register control *ctrl;

	if (i == ninherits) {
	    ctrl = newctrl;
	} else if (oh_new(newctrl->inherits[i].obj->chain.name)->index == i) {
	    ctrl = newctrl->inherits[i].obj->ctrl;
	} else {
	    continue;
	}

	for (f = ctrl->funcdefs, n = 0; n < ctrl->nfuncdefs; f++, n++) {
	    register vfh *h;
	    register char *name;

	    if ((f->class & C_PRIVATE) ||
		(i == 0 && ninherits != 0 &&
		 (f->class & (C_STATIC | C_UNDEFINED)) == C_STATIC)) {
		/* not in symbol table */
		continue;
	    }
	    name = d_get_strconst(ctrl, f->inherit, f->index)->text;
	    h = *(vfh **) ht_lookup(ftab, name, FALSE);
	    if (h->ohash->index == ninherits &&
		(functions[h->index].func.class & C_PRIVATE)) {
		/*
		 * private redefinition of inherited function:
		 * use inherited function
		 */
		h = (vfh *) h->chain.next;
	    }
	    if (i == h->ohash->index) {
		/*
		 * all non-private functions are put into the hash table
		 */
		x = hashstr(name, VFMERGEHASHSZ) % nsymbs;
		if (symtab[x].next == (unsigned short) -1) {
		    /*
		     * new entry
		     */
		    symtab[x].inherit = i;
		    symtab[x].index = n;
		    symtab[x].next = x;
		} else {
		    /*
		     * collision
		     */
		    coll[ncoll].inherit = i;
		    coll[ncoll].index = n;
		    coll[ncoll++].next = x;
		}
	    }
	}
    }

    /*
     * Now deal with the collisions.
     */
    n = 0;
    for (i = 0; i < ncoll; i++) {
	/* find a free slot */
	while (symtab[n].next != (unsigned short) -1) {
	    n++;
	}
	x = coll[i].next;
	/* add new entry to list */
	symtab[n] = symtab[x];
	if (symtab[n].next == x) {
	    symtab[n].next = n;	/* adjust list terminator */
	}
	symtab[x].inherit = coll[i].inherit;
	symtab[x].index = coll[i].index;
	symtab[x].next = n++;	/* link to previous slot */
    }

    AFREE(coll);
}

/*
 * NAME:	control->symb()
 * DESCRIPTION:	return the entry in the symbol table for func, or NULL
 */
dsymbol *ctrl_symb(ctrl, func, len)
register control *ctrl;
char *func;
unsigned int len;
{
    register dsymbol *symb;
    register dfuncdef *f;
    register unsigned int i, j;
    register string *str;
    dsymbol *symtab, *symb1;
    dinherit *inherits;

    if ((i=ctrl->nsymbols) == 0) {
	return (dsymbol *) NULL;
    }

    inherits = ctrl->inherits;
    symtab = d_get_symbols(ctrl);
    i = hashstr(func, VFMERGEHASHSZ) % i;
    symb1 = symb = &symtab[i];
    ctrl = o_control(inherits[UCHAR(symb->inherit)].obj);
    f = d_get_funcdefs(ctrl) + UCHAR(symb->index);
    str = d_get_strconst(ctrl, f->inherit, f->index);
    if (len == str->len && memcmp(func, str->text, len) == 0) {
	/* found it */
	return (f->class & C_UNDEFINED) ? (dsymbol *) NULL : symb1;
    }
    while (i != symb->next) {
	symb = &symtab[i = symb->next];
	ctrl = o_control(inherits[UCHAR(symb->inherit)].obj);
	f = d_get_funcdefs(ctrl) + UCHAR(symb->index);
	str = d_get_strconst(ctrl, f->inherit, f->index);
	if (len == str->len && memcmp(func, str->text, len) == 0) {
	    /* found it: put symbol first in linked list */
	    i = symb1->inherit;
	    j = symb1->index;
	    symb1->inherit = symb->inherit;
	    symb1->index = symb->index;
	    symb->inherit = i;
	    symb->index = j;
	    return (f->class & C_UNDEFINED) ? (dsymbol *) NULL : symb1;
	}
    }
    return (dsymbol *) NULL;
}

/*
 * NAME:	control->construct()
 * DESCRIPTION:	construct and return a control block for the object just
 *		compiled
 */
control *ctrl_construct()
{
    register control *ctrl;

    ctrl = newctrl;
    ctrl->nvariables += nvars;

    ctrl_mkstrings();
    ctrl_mkfuncs();
    ctrl_mkvars();
    ctrl_mkfcalls();
    ctrl_mksymbs();
    ctrl->nfloatdefs = nfloats;
    ctrl->nfloats += nfloats;
    ctrl->compiled = P_time();

    newctrl = (control *) NULL;
    return ctrl;
}

/*
 * NAME:	control->clear()
 * DESCRIPTION:	clean up
 */
void ctrl_clear()
{
    oh_clear();
    vfh_clear();
    if (vtab != (hashtab *) NULL) {
	ht_del(vtab);
	ht_del(ftab);
	vtab = (hashtab *) NULL;
	ftab = (hashtab *) NULL;
    }
    lab_clear();

    ndirects = 0;
    ninherits = 0;
    nfloats = 0;
    nsymbs = 0;
    nfclash = 0;
    nifcalls = 0;

    if (newctrl != (control *) NULL) {
	d_del_control(newctrl);
	newctrl = (control *) NULL;
    }
    str_clear();
    while (str_list != (strchunk *) NULL) {
	register strchunk *l;
	register string **s;

	l = str_list;
	s = &l->s[strchunksz];
	while (--strchunksz >= 0) {
	    str_del(*--s);
	}
	strchunksz = STRING_CHUNK;
	str_list = l->next;
	FREE(l);
    }
    while (fclist != (fcchunk *) NULL) {
	register fcchunk *l;

	l = fclist;
	fclist = l->next;
	FREE(l);
    }
    fcchunksz = FCALL_CHUNK;
    if (functions != (cfunc *) NULL) {
	register int i;
	register cfunc *f;

	for (i = nfdefs, f = functions; i > 0; --i, f++) {
	    FREE(f->proto);
	    if (f->progsize != 0) {
		FREE(f->prog);
	    }
	}
	FREE(functions);
	functions = (cfunc *) NULL;
    }
    if (variables != (dvardef *) NULL) {
	FREE(variables);
	variables = (dvardef *) NULL;
    }
}

/*
 * NAME:	control->varmap()
 * DESCRIPTION:	create a variable mapping from the old control block to the new
 */
unsigned short *ctrl_varmap(old, new)
register control *old, *new;
{
    register unsigned short j, k;
    register dvardef *v;
    register long n;
    register unsigned short *vmap;
    register dinherit *inh, *inh2;
    register control *ctrl, *ctrl2;
    unsigned short i, voffset;

    /*
     * make variable mapping from old to new, with new just compiled
     */

    vmap = ALLOC(unsigned short, new->nvariables + 1);

    voffset = 0;
    for (i = new->ninherits, inh = new->inherits; i > 0; --i, inh++) {
	ctrl = (i == 1) ? new : inh->obj->ctrl;
	if (inh->varoffset < voffset || ctrl->nvardefs == 0) {
	    continue;
	}
	voffset = inh->varoffset + ctrl->nvardefs;

	for (j = old->ninherits, inh2 = old->inherits; j > 0; --j, inh2++) {
	    if (strcmp(inh->obj->chain.name, inh2->obj->chain.name) == 0) {
		/*
		 * put var names from old control block in string merge table
		 */
		ctrl2 = o_control(inh2->obj);
		v = d_get_vardefs(ctrl2);
		for (k = 0; k < ctrl2->nvardefs; k++, v++) {
		    str_put(d_get_strconst(ctrl2, v->inherit, v->index),
			    ((Uint) k << 8) | v->type);
		}
	    } else if (j != 1) {
		continue;
	    }

	    /*
	     * map new variables to old ones
	     */
	    for (k = 0, v = ctrl->vardefs; k < ctrl->nvardefs; k++, v++) {
		n = str_put(d_get_strconst(ctrl, v->inherit, v->index),
			    (Uint) 0);
		if ((n & 0xff) == v->type) {
		    *vmap = inh2->varoffset + (n >> 8);
		} else if (v->type == T_FLOAT) {
		    *vmap = NEW_FLOAT;
		} else {
		    *vmap = NEW_INT;
		}
		vmap++;
	    }
	    str_clear();
	    break;
	}
    }

    /*
     * check if any variable changed
     */
    *vmap = old->nvariables;
    vmap -= new->nvariables;
    if (old->nvariables != new->nvariables) {
	return vmap;		/* changed */
    }
    for (i = 0; i <= new->nvariables; i++) {
	if (vmap[i] != i) {
	    return vmap;	/* changed */
	}
    }
    /* no variable remapping needed */
    FREE(vmap);
    return (unsigned short *) NULL;
}