/*
 * Electric(tm) VLSI Design System
 *
 * File: dbvars.c
 * Database object variables module
 * Written by: Steven M. Rubin, Static Free Software
 *
 * Copyright (c) 2000 Static Free Software.
 *
 * Electric(tm) is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * Electric(tm) is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with Electric(tm); see the file COPYING.  If not, write to
 * the Free Software Foundation, Inc., 59 Temple Place, Suite 330,
 * Boston, Mass 02111-1307, USA.
 *
 * Static Free Software
 * 4119 Alpine Road
 * Portola Valley, California 94028
 * info@staticfreesoft.com
 */

#include "global.h"
#include "database.h"
#include "egraphics.h"
#include "usr.h"

/***** option saving structures *****/

/* this define should match the one in "usrdiacom.c" */
#define SAVEDBITWORDS 2

typedef struct
{
	INTBIG    dirty;
	INTBIG    saveflag;
	INTBIG   *addr;
	INTBIG    type;
	INTBIG    partial;
	char     *variablename;
} OPTIONVARPROTO;

typedef struct
{
	UINTBIG   bits[SAVEDBITWORDS];
	char     *command;
	OPTIONVARPROTO *variables;
} OPTIONVARCOMS;

typedef struct
{
	INTBIG    addr;
	INTBIG    type;
	char     *variablename;
	VARIABLE *var;
	INTBIG    oldtype;
} OPTIONVARCACHE;

OPTIONVARCACHE *db_optionvarcache;
INTBIG    db_optionvarcachecount = 0;

/***** for finding variables on objects *****/

#define	SPECNAME  0		/* look for special names in structure */
#define	VARNAME   1		/* look for variable names in structure */
#define	PORTCNAME 2		/* look for port names on nodeproto */
#define	ARCINAME  3		/* look for nodeinst/arcinst names in facet */
#define	NODEINAME 4		/* look for nodeinst/arcinst names in facet */
#define	PORTANAME 5		/* look for port arc names on nodeinst */
#define	PORTENAME 6		/* look for export names on nodeinst */
#define	ARCNAME   7		/* look for arcproto names in technology */
#define	NODENAME  8		/* look for nodeproto names in technology */
#define	NULLNAME  9		/* no more names in structure */

typedef struct
{
	char  *name;
	UINTBIG type;
	INTBIG *ptr;
} NAMEVARS;

#define	MAXAPACK	5				/* maximum nested attribute searches */
struct attrsearch
{
	INTSML      *numvar;			/* address of object variable count */
	VARIABLE   **firstvar;			/* address of object variable list */
	NAMEVARS    *vars;				/* special names in this object */
	INTBIG       proto;				/* address of object prototype */
	INTBIG       object;			/* address of this object */
	INTBIG       state;				/* current name type under test */
	INTSML       varindex;			/* current index for variable search */
	INTSML       specindex;			/* current index for name search */
	NODEPROTO   *nodeprotoname;		/* current facet object */
	ARCPROTO    *arcprotoname;		/* current arcproto object */
	PORTARCINST *portarcinstname;	/* current portarcinst object */
	PORTEXPINST *portexpinstname;	/* current portexpinst object */
	PORTPROTO   *portprotoname;		/* current portproto object */
	ARCINST     *arciname;			/* current arcinst object */
	NODEINST    *nodeiname;			/* current nodeinst object */
} db_attrpacket[MAXAPACK];
static INTSML db_apackindex = 0;
static struct attrsearch *db_thisapack;
static INTBIG db_realaddress;

INTSML db_dovarchangequietly = 0;	/* nonzero to do next variable change quietly */

/* prototypes for local routines */
void db_initnodeinstlist(NODEINST*);
void db_initnodeprotolist(NODEPROTO*);
void db_initportarcinstlist(PORTARCINST*);
void db_initportexpinstlist(PORTEXPINST*);
void db_initportprotolist(PORTPROTO*);
void db_initarcinstlist(ARCINST*);
void db_initarcprotolist(ARCPROTO*);
void db_initgeomlist(GEOM*);
void db_initliblist(LIBRARY*);
void db_inittechlist(TECHNOLOGY*);
void db_initaidlist(AIDENTRY*);
void db_initrtnodelist(RTNODE*);
void db_initnetworklist(NETWORK*);
void db_initcelllist(CELL*);
void db_initviewlist(VIEW*);
void db_initwindowlist(WINDOWPART*);
void db_initgraphicslist(GRAPHICS*);
void db_initconstraintlist(CONSTRAINT*);
void db_initwindowframelist(WINDOWFRAME*);
void db_initpolygonlist(POLYGON*);
INTSML db_getkey(char*);
VARIABLE *db_evalvar(VARIABLE*);
CLUSTER *db_whichcluster(INTBIG, INTBIG);
#ifdef DEBUGMEMORY
  INTSML db_setvalkey(INTSML*, VARIABLE**, INTSML, INTBIG, INTBIG, INTBIG, CLUSTER*, char*, INTBIG);
  INTSML db_setvalvar(VARIABLE*, INTBIG, INTBIG, CLUSTER*, char*, INTBIG);
#else
  INTSML db_setvalkey(INTSML*, VARIABLE**, INTSML, INTBIG, INTBIG, INTBIG, CLUSTER*);
  INTSML db_setvalvar(VARIABLE*, INTBIG, INTBIG, CLUSTER*);
#endif
INTSML db_getindvar(VARIABLE*, INTBIG, INTBIG*);
INTSML db_setindvar(INTBIG, INTBIG, VARIABLE*, INTBIG, INTBIG);
INTSML db_insindvar(INTBIG, INTBIG, VARIABLE*, INTBIG, INTBIG);
INTSML db_delindvar(INTBIG, INTBIG, VARIABLE*, INTBIG);
INTSML db_setval(char*, char*, INTBIG, CLUSTER*);
INTSML db_binarysearch(INTSML, VARIABLE*, INTBIG);
void db_renamevar(INTSML, VARIABLE*, INTBIG, INTBIG);
INTSML db_getdatasize(INTBIG);
char *db_describetype(INTBIG type);
INTBIG db_assignvalue(void *loc, INTSML datasize);

/*
 * Routine to free all memory associated with this module.
 */
void db_freevariablememory(void)
{
	REGISTER INTBIG i;

	for(i=0; i<el_numnames; i++) efree((char *)el_namespace[i]);
	if (el_numnames != 0) efree((char *)el_namespace);
	if (db_optionvarcachecount > 0) efree((char *)db_optionvarcache);
}

/************************* ACCESSING VARIABLES *************************/

/*
 * routine to find the variable with name "name", which must be of type
 * "want", on the object whose address is "addr" and type is "type".  If
 * "want" is -1 then any type will do.  If the variable does not exist or
 * is the wrong type, NOVARIABLE is returned.
 */
VARIABLE *getval(INTBIG addr, INTBIG type, INTBIG want, char *name)
{
	static VARIABLE *var;
	REGISTER char *pp;
	REGISTER INTBIG search;

	search = initobjlist(addr, type, 0);
	if (search == 0) return(NOVARIABLE);
	for(;;)
	{
		pp = nextobjectlist(&var, search);
		if (pp == 0) break;
		if (namesame(pp, name) == 0)
		{
			/* variable found: make sure it is right type */
			if (want != -1)
			{
				if (((INTBIG)(var->type & (VTYPE|VISARRAY))) != want) return(NOVARIABLE);
			}
			return(db_evalvar(var));
		}
	}
	return(NOVARIABLE);
}

/*
 * routine to find the variable with key "key", which must be of type "want",
 * on the object whose address is "addr" and type is "type".  If the
 * variable does not exist or is the wrong type, NOVARIABLE is returned.
 */
VARIABLE *getvalkey(INTBIG addr, INTBIG type, INTBIG want, INTBIG key)
{
	REGISTER INTSML med;
	INTSML *mynumvar;
	REGISTER VARIABLE *var;
	VARIABLE **myfirstvar;

	/* get the firstvar/numvar into the globals */
	if (db_getvarptr(addr, type, &myfirstvar, &mynumvar) != 0) return(NOVARIABLE);

	/* binary search variable space for the key */
	med = db_binarysearch(*mynumvar, *myfirstvar, key);
	if (med < 0) return(NOVARIABLE);

	/* get the variable */
	var = &(*myfirstvar)[med];

	/* make sure it is right type if a type was specified */
	if (want != -1 && ((INTBIG)(var->type & (VTYPE|VISARRAY))) != want) return(NOVARIABLE);
	return(db_evalvar(var));
}

/*
 * routine to get an entry in the array variable "name" in object "addr"
 * which is of type "type".  Entry "aindex" is placed into "value".  Returns
 * nonzero on error.
 */
INTSML getind(INTBIG addr, INTBIG type, char *name, INTBIG aindex, INTBIG *value)
{
	VARIABLE *var;
	REGISTER char *pp;
	REGISTER INTBIG search;

	search = initobjlist(addr, type, 0);
	if (search == 0) return(1);
	for(;;)
	{
		pp = nextobjectlist(&var, search);
		if (pp == 0) break;
		if (namesame(pp, name) == 0) break;
	}

	/* variable must exist */
	if (pp == 0) return(1);

	if ((var->type&VCANTSET) != 0) return(1);

	return(db_getindvar(var, aindex, value));
}

/*
 * routine to get an entry in the array variable whose key is "key" in object
 * "addr" which is of type "type".  Entry "aindex" is placed in "value".
 * The routine returns nonzero upon error.
 */
INTSML getindkey(INTBIG addr, INTBIG type, INTBIG key, INTBIG aindex, INTBIG *value)
{
	VARIABLE **myfirstvar;
	INTSML *mynumvar;
	REGISTER INTSML med;

	/* get the attribute list into the globals */
	if (db_getvarptr(addr, type, &myfirstvar, &mynumvar) != 0) return(1);

	/* binary search variable space for the key */
	med = db_binarysearch(*mynumvar, *myfirstvar, key);

	/* variable must exist */
	if (med < 0) return(1);

	if (((*myfirstvar)[med].type&VCANTSET) != 0) return(1);

	/* get the variable */
	return(db_getindvar(&(*myfirstvar)[med], aindex, value));
}

/*
 * routine to modify the text descriptor on variable "var", which resides on
 * object "addr" of type "type".  The new descriptor is set to "newdescript"
 */
void modifydescript(INTBIG addr, INTBIG type, VARIABLE *var, INTBIG newdescript)
{
	REGISTER INTBIG olddescript;

	if (var != NOVARIABLE)
	{
		olddescript = var->textdescript;
		var->textdescript = newdescript;
	}

	/* tell constraint system about killed variable */
	(*el_curconstraint->modifydescript)(addr, type, var, olddescript);

	db_setchangefacet(db_whichnodeproto(addr, type));

	/* record the change */
	(void)db_change(addr, DESCRIPTMOD, type, (INTBIG)var, olddescript, 0, 0, 0);
}

/*
 * routine to return the length of the array in variable "var".  If it is not
 * an array, the value -1 is returned.
 */
INTBIG getlength(VARIABLE *var)
{
	REGISTER INTBIG len;

	if ((var->type&VISARRAY) == 0) return(-1);
	len = (var->type&VLENGTH) >> VLENGTHSH;
	if (len != 0) return(len);

	if ((var->type&VTYPE) == VCHAR)
	{
		for(len=0; ((((char *)var->addr)[len])&0377) != 0377; len++) ;
	} else if ((var->type&VTYPE) == VFLOAT)
	{
		for(len=0; ((float *)var->addr)[len] != -1; len++) ;
	} else if ((var->type&VTYPE) == VDOUBLE)
	{
		for(len=0; ((double *)var->addr)[len] != -1; len++) ;
	} else if ((var->type&VTYPE) == VSHORT)
	{
		for(len=0; ((INTSML *)var->addr)[len] != -1; len++) ;
	} else
	{
		for(len=0; ((INTBIG *)var->addr)[len] != -1; len++) ;
	}
	return(len);
}

/*
 * routine to convert a variable name in "name" to a key and return it.
 * If an error is detected, the routine returns -1.
 */
INTBIG makekey(char *name)
{
	REGISTER char **newname;
	REGISTER INTSML med, i;

	/* search namespace for this name */
	med = db_getkey(name);
	if (med >= 0) return((INTBIG)el_namespace[med]);

	/* convert return value to proper index for new name */
	med = -med-1;

	/* make sure the name is valid */
	for(i=0; name[i] != 0; i++) if (name[i] == ' ' || name[i] == '\t')
		return(db_error(DBBADNAME|DBMAKEKEY));

	/* allocate space for new list */
	newname = (char **)emalloc(((el_numnames+1)*(sizeof (char *))), db_cluster);
	if (newname == 0) return(db_error(DBNOMEM|DBMAKEKEY));

	/* copy old list up to the new entry */
	for(i=0; i<med; i++) newname[i] = el_namespace[i];

	/* add the new entry */
	if (allocstring(&newname[med], name, db_cluster) != 0) return(-1);

	/* copy old list after the new entry */
	for(i=med; i<el_numnames; i++) newname[i+1] = el_namespace[i];

	/* clean-up */
	if (el_numnames != 0) efree((char *)el_namespace);
	el_namespace = newname;
	el_numnames++;
	return((INTBIG)el_namespace[med]);
}

/*
 * routine to convert a variable key in "key" to a name and return it.
 * Because of the techniques used, this is trivial.
 */
char *makename(INTBIG key)
{
	return((char *)key);
}

/*
 * routine to initialize a search of the variable names on object "addr"
 * of type "type".  Subsequent calls to "nextobjectlist" will return
 * variable names and fill the parameter with the variable addresses.
 * If "restrictdirect" is nonzero, only list those attributes directly on the
 * object (and not those in linked lists).  The routine returns zero upon
 * error, and otherwise returns a value that must be passed to the subsequent
 * "nextobjectlist" calls
 */
INTBIG initobjlist(INTBIG addr, INTBIG type, INTSML restrictdirect)
{
	db_thisapack = &db_attrpacket[db_apackindex++];
	if (db_apackindex >= MAXAPACK) db_apackindex = 0;
	switch (type&VTYPE)
	{
		case VNODEINST:    db_initnodeinstlist((NODEINST *)addr);       break;
		case VNODEPROTO:   db_initnodeprotolist((NODEPROTO *)addr);     break;
		case VPORTARCINST: db_initportarcinstlist((PORTARCINST *)addr); break;
		case VPORTEXPINST: db_initportexpinstlist((PORTEXPINST *)addr); break;
		case VPORTPROTO:   db_initportprotolist((PORTPROTO *)addr);     break;
		case VARCINST:     db_initarcinstlist((ARCINST *)addr);         break;
		case VARCPROTO:    db_initarcprotolist((ARCPROTO *)addr);       break;
		case VGEOM:        db_initgeomlist((GEOM *)addr);               break;
		case VLIBRARY:     db_initliblist((LIBRARY *)addr);             break;
		case VTECHNOLOGY:  db_inittechlist((TECHNOLOGY *)addr);         break;
		case VAID:         db_initaidlist((AIDENTRY *)addr);            break;
		case VRTNODE:      db_initrtnodelist((RTNODE *)addr);           break;
		case VNETWORK:     db_initnetworklist((NETWORK *)addr);         break;
		case VCELL:        db_initcelllist((CELL *)addr);               break;
		case VVIEW:        db_initviewlist((VIEW *)addr);               break;
		case VWINDOWPART:  db_initwindowlist((WINDOWPART *)addr);       break;
		case VGRAPHICS:    db_initgraphicslist((GRAPHICS *)addr);       break;
		case VCONSTRAINT:  db_initconstraintlist((CONSTRAINT *)addr);   break;
		case VWINDOWFRAME: db_initwindowframelist((WINDOWFRAME *)addr); break;
		case VPOLYGON:     db_initpolygonlist((POLYGON *)addr);         break;
		default:
			(void)db_error(DBBADOBJECT|DBINITOBJLIST);
			return(0);
	}
	if (restrictdirect != 0)
		if (db_thisapack->state != NULLNAME) db_thisapack->state = SPECNAME;
	return((INTBIG)db_thisapack);
}

/*
 * routine to return the next name in the list (and the associated variable)
 */
char *nextobjectlist(VARIABLE **var, INTBIG ain)
{
	struct attrsearch *apack;
	REGISTER INTSML datasize, indir;
	REGISTER char *retval;
	REGISTER NODEINST *ni;
	REGISTER ARCINST *ai;
	static char line[50];
	static VARIABLE v;
	REGISTER VARIABLE *nvar;

	apack = (struct attrsearch *)ain;
	for(;;) switch (apack->state)
	{
		case SPECNAME:	/* looking for special names in object structure */
			if (apack->vars[apack->specindex].name != 0)
			{
				/* get next special name */
				*var = &v;
				v.key = apack->specindex;
				v.type = apack->vars[apack->specindex].type;
				v.textdescript = TXTSMALL << VTSIZESH;
				v.addr = (INTBIG)apack->vars[apack->specindex].ptr - apack->proto + apack->object;
				db_realaddress = v.addr;
				indir = 0;
				if ((v.type&VISARRAY) == 0) indir++; else
					if ((v.type&VCREF) == 0) indir++;
				if (indir != 0)
				{
					datasize = db_getdatasize(v.type);
					v.addr = db_assignvalue((void *)v.addr, datasize);
				}
				v.type |= VCREF;
				return(apack->vars[apack->specindex++].name);
			}
			/* no more special names, go to next case */
			apack->state = VARNAME;
			break;
		case VARNAME:	/* looking for variable names in object */
			if (apack->varindex < *apack->numvar)
			{
				/* get next variable name */
				*var = &(*apack->firstvar)[apack->varindex];
				return((char *)(*apack->firstvar)[apack->varindex++].key);
			}
			/* no more variable names, go to terminal case */
			apack->state = NULLNAME;
			break;
		case ARCINAME:	/* looking for arcs in the nodeproto */
			if (apack->arciname != NOARCINST)
			{
				/* get next arcinst name */
				ai = apack->arciname;
				apack->arciname = apack->arciname->nextarcinst;
				*var = &v;   v.key = -1;
				v.type = VARCINST;
				v.textdescript = TXTSMALL << VTSIZESH;
				v.addr = (INTBIG)ai;
				nvar = getvalkey((INTBIG)ai, VARCINST, VSTRING, el_arc_name);
				if (nvar != NOVARIABLE) return((char *)nvar->addr);
				(void)sprintf(line, "arc%ld", (INTBIG)ai);
				return(line);
			}
			apack->state = NODEINAME;
			break;
		case NODEINAME:	/* looking for nodes in the nodeproto */
			if (apack->nodeiname != NONODEINST)
			{
				/* get next nodeinst name */
				ni = apack->nodeiname;
				apack->nodeiname = apack->nodeiname->nextnodeinst;
				*var = &v;   v.key = -1;
				v.type = VNODEINST;
				v.textdescript = TXTSMALL << VTSIZESH;
				v.addr = (INTBIG)ni;
				nvar = getvalkey((INTBIG)ni, VNODEINST, VSTRING, el_node_name);
				if (nvar != NOVARIABLE) return((char *)nvar->addr);
				(void)sprintf(line, "node%ld", (INTBIG)ni);
				return(line);
			}
			/* no more nodeinst names, go to next case */
			apack->state = PORTCNAME;
			break;
		case PORTCNAME:	/* looking for port names on nodeproto */
			if (apack->portprotoname != NOPORTPROTO)
			{
				/* get next port name */
				*var = &v;   v.key = -1;
				v.textdescript = TXTSMALL << VTSIZESH;
				v.type = VPORTPROTO;   v.addr = (INTBIG)apack->portprotoname;
				retval = apack->portprotoname->protoname;
				apack->portprotoname = apack->portprotoname->nextportproto;
				return(retval);
			}
			/* no more port names, go to next case */
			apack->state = SPECNAME;
			break;
		case PORTANAME:	/* looking for portarcinst names on nodeinst */
			if (apack->portarcinstname != NOPORTARCINST)
			{
				/* get next portarcinst name */
				*var = &v;   v.key = -1;
				v.type = VPORTARCINST;
				v.textdescript = TXTSMALL << VTSIZESH;
				v.addr = (INTBIG)apack->portarcinstname;
				retval = apack->portarcinstname->proto->protoname;
				apack->portarcinstname = apack->portarcinstname->nextportarcinst;
				return(retval);
			}
			/* no more portarcinst names, go to next case */
			apack->state = PORTENAME;
			break;
		case PORTENAME:	/* looking for portexpinst names on nodeinst */
			if (apack->portexpinstname != NOPORTEXPINST)
			{
				/* get next portexpinst name */
				*var = &v;   v.key = -1;
				v.type = VPORTEXPINST;
				v.textdescript = TXTSMALL << VTSIZESH;
				v.addr = (INTBIG)apack->portexpinstname;
				retval = apack->portexpinstname->proto->protoname;
				apack->portexpinstname = apack->portexpinstname->nextportexpinst;
				return(retval);
			}
			/* no more portexpinst names, go to next case */
			apack->state = SPECNAME;
			break;
		case ARCNAME:	/* looking for arcs in the technology */
			if (apack->arcprotoname != NOARCPROTO)
			{
				/* get next arcproto name */
				*var = &v;   v.key = -1;
				v.textdescript = TXTSMALL << VTSIZESH;
				v.type = VARCPROTO;   v.addr = (INTBIG)apack->arcprotoname;
				retval = apack->arcprotoname->protoname;
				apack->arcprotoname = apack->arcprotoname->nextarcproto;
				return(retval);
			}
			/* no more arcproto names, go to next case */
			apack->state = NODENAME;
			break;
		case NODENAME:	/* looking for facets in the tech/lib */
			if (apack->nodeprotoname != NONODEPROTO)
			{
				/* get next facet name */
				*var = &v;   v.key = -1;
				v.textdescript = TXTSMALL << VTSIZESH;
				v.type = VNODEPROTO;   v.addr = (INTBIG)apack->nodeprotoname;
				retval = describenodeproto(apack->nodeprotoname);
				apack->nodeprotoname = apack->nodeprotoname->nextnodeproto;
				return(retval);
			}
			/* no more nodeproto names, go to next case */
			apack->state = SPECNAME;
			break;
		case NULLNAME:
			*var = NOVARIABLE;
			return(0);
	}
}

void nextvarchangequiet(void)
{
	db_dovarchangequietly = 1;
}

/********************** DELETING AND RENAMING VARIABLES *********************/

/*
 * delete the variable "name" from object whose address is "addr" and type
 * is "type".  Returns nonzero if there is an error.
 */
INTSML delval(INTBIG addr, INTBIG type, char *name)
{
	REGISTER INTBIG key;

	key = makekey(name);
	if (key == -1)
	{
		db_dovarchangequietly = 0;
		return(1);
	}

	return(delvalkey(addr, type, key));
}

/*
 * delete the variable with the key "key" from object whose address is
 * "addr" and type is "type".  Returns nonzero if there is an error.
 */
INTSML delvalkey(INTBIG addr, INTBIG type, INTBIG key)
{
	REGISTER VARIABLE *var;
	REGISTER char *varname;
	VARIABLE **myfirstvar;
	INTSML *mynumvar;
	REGISTER INTSML i, j;
	REGISTER INTBIG oldaddr, oldtype, olddescript;

	/* get the firstvar/numvar pointers */
	if (db_getvarptr(addr, type, &myfirstvar, &mynumvar) != 0)
	{
		db_dovarchangequietly = 0;
		return(1);
	}

	/* search for the varaible in the list */
	for(i=0; i<*mynumvar; i++)
		if ((*myfirstvar)[i].key == key) break;
	if (i >= *mynumvar)
	{
		if (key != -1) varname = makename(key); else
			varname = _("FIXED VARIABLE");
		ttyputmsg(_("Internal error: tried to delete %s on object of type %s.  No worry."),
			varname, db_describetype(type));
		db_dovarchangequietly = 0;
		return((INTSML)db_error(DBNOVAR|DBDELVALKEY));
	}

	/* delete the variable */
	var = &(*myfirstvar)[i];
	oldaddr = var->addr;
	oldtype = var->type;
	olddescript = var->textdescript;

	/* delete the entry in the variable list */
	(*mynumvar)--;
	for(j = i; j < *mynumvar; j++)
	{
		(*myfirstvar)[j].key  = (*myfirstvar)[j+1].key;
		(*myfirstvar)[j].type = (*myfirstvar)[j+1].type;
		(*myfirstvar)[j].textdescript = (*myfirstvar)[j+1].textdescript;
		(*myfirstvar)[j].addr = (*myfirstvar)[j+1].addr;
	}
	if (*mynumvar == 0) efree((char *)*myfirstvar);

	if (db_dovarchangequietly == 0)
	{
		/* tell constraint system about killed variable */
		(*el_curconstraint->killvariable)(addr, type, key, oldaddr, oldtype, olddescript);

		/* record the change */
		(void)db_change((INTBIG)addr, VARIABLEKILL, type, key, oldaddr, oldtype, olddescript, 0);
	} else
	{
		/* not going through change control, so delete the memory now */
		db_dovarchangequietly = 0;
		db_freevar(oldaddr, oldtype);
	}
	return(0);
}

/*
 * routine to rename variable "oldname" to "newname" everywhere in the
 * database.  Returns negative if the operation cannot be done.
 * This routine does not delete the old name from the namespace but it does
 * rename the variable wherever it exists, so the old name is no longer
 * in use.  NOTE: this does not check variables on R-tree nodes.
 */
INTSML renameval(char *oldname, char *newname)
{
	REGISTER INTBIG oldkey, newkey;
	REGISTER INTSML i;
	REGISTER NODEPROTO *np;
	REGISTER CELL *c;
	REGISTER ARCPROTO *ap;
	REGISTER PORTPROTO *pp;
	REGISTER NETWORK *net;
	REGISTER VIEW *v;
	REGISTER NODEINST *ni;
	REGISTER ARCINST *ai;
	REGISTER LIBRARY *lib;
	REGISTER TECHNOLOGY *tech;

	/* get key of old name */
	oldkey = db_getkey(oldname);
	if (oldkey < 0) return(-1);
	oldkey = (INTBIG)el_namespace[oldkey];

	/* make sure new name is different */
	if (namesame(oldname, newname) == 0) return(-1);

	/* make new name key */
	newkey = makekey(newname);
	if (newkey < 0) return(-1);

	/* search the libraries */
	for(lib = el_curlib; lib != NOLIBRARY; lib = lib->nextlibrary)
	{
		db_renamevar(lib->numvar, lib->firstvar, oldkey, newkey);
		for(c = lib->firstcell; c != NOCELL; c = c->nextcell)
			db_renamevar(c->numvar, c->firstvar, oldkey, newkey);
		for(np = lib->firstnodeproto; np != NONODEPROTO; np = np->nextnodeproto)
		{
			db_renamevar(np->numvar, np->firstvar, oldkey, newkey);
			for(pp = np->firstportproto; pp != NOPORTPROTO; pp = pp->nextportproto)
			{
				db_renamevar(pp->numvar, pp->firstvar, oldkey, newkey);
				db_renamevar(pp->subportexpinst->numvar,
					pp->subportexpinst->firstvar, oldkey, newkey);
			}
			for(ni = np->firstnodeinst; ni != NONODEINST; ni = ni->nextnodeinst)
			{
				db_renamevar(ni->numvar, ni->firstvar, oldkey, newkey);
				db_renamevar(ni->geom->numvar, ni->geom->firstvar, oldkey, newkey);
			}
			for(ai = np->firstarcinst; ai != NOARCINST; ai = ai->nextarcinst)
			{
				db_renamevar(ai->numvar, ai->firstvar, oldkey, newkey);
				db_renamevar(ai->geom->numvar, ai->geom->firstvar, oldkey, newkey);
				db_renamevar(ai->end[0].portarcinst->numvar,
					ai->end[0].portarcinst->firstvar, oldkey, newkey);
				db_renamevar(ai->end[1].portarcinst->numvar,
					ai->end[1].portarcinst->firstvar, oldkey, newkey);
			}
			for(net = np->firstnetwork; net != NONETWORK; net = net->nextnetwork)
				db_renamevar(net->numvar, net->firstvar, oldkey, newkey);
		}
	}

	/* search the aids */
	for(i=0; i<el_maxaid; i++)
		db_renamevar(el_aids[i].numvar, el_aids[i].firstvar, oldkey, newkey);

	/* search the views */
	for(v = el_views; v != NOVIEW; v = v->nextview)
		db_renamevar(v->numvar, v->firstvar, oldkey, newkey);

	/* search the technologies */
	for(tech = el_technologies; tech != NOTECHNOLOGY; tech = tech->nexttechnology)
	{
		db_renamevar(tech->numvar, tech->firstvar, oldkey, newkey);
		for(ap = tech->firstarcproto; ap != NOARCPROTO; ap = ap->nextarcproto)
			db_renamevar(ap->numvar, ap->firstvar, oldkey, newkey);
		for(np = tech->firstnodeproto; np != NONODEPROTO; np = np->nextnodeproto)
			db_renamevar(np->numvar, np->firstvar, oldkey, newkey);
	}
	return(0);
}

/************************* SETTING ENTIRE VARIABLES *************************/

/*
 * routine to set the value of entry "name" in object "addr" which is of
 * type "type".  The entry is set to "newaddr" which has type "newtype".
 * The routine returns the variable address (NOVARIABLE on error).
 */
#ifdef DEBUGMEMORY
VARIABLE *_setval(INTBIG addr, INTBIG type, char *name, INTBIG newaddr, INTBIG newtype, char *module, INTBIG line)
#else
VARIABLE *setval(INTBIG addr, INTBIG type, char *name, INTBIG newaddr, INTBIG newtype)
#endif
{
	VARIABLE *var;
	REGISTER char *pp;
	REGISTER INTBIG key, search;

	/* look for an attribute that already has this name */
	search = initobjlist(addr, type, 0);
	if (search == 0)
	{
		db_dovarchangequietly = 0;
		return(NOVARIABLE);
	}
	for(;;)
	{
		pp = nextobjectlist(&var, search);
		if (pp == 0) break;
		if (namesame(pp, name) == 0) break;
	}

	/* if the attribute exists, change its value */
	if (pp != 0)
	{
		if ((var->type&VCANTSET) != 0)
		{
			db_dovarchangequietly = 0;
			return((VARIABLE *)db_error(DBVARFIXED|DBSETVAL));
		}

		if (db_dovarchangequietly == 0)
		{
			/* tell constraint system about killed variable */
			(*el_curconstraint->killvariable)(addr, type, var->key, var->addr, var->type, var->textdescript);

			/* record a change for removal of the old variable */
			(void)db_change((INTBIG)addr, VARIABLEKILL, type, var->key,
				var->addr, var->type, var->textdescript, 0);
		}

#ifdef DEBUGMEMORY
		if (db_setvalvar(var, newaddr, newtype, db_whichcluster(addr, type), module, line) != 0)
#else
		if (db_setvalvar(var, newaddr, newtype, db_whichcluster(addr, type)) != 0)
#endif
		{
			db_dovarchangequietly = 0;
			return((VARIABLE *)db_error(DBNOMEM|DBSETVAL));
		}

		if (db_dovarchangequietly == 0)
		{
			/* tell constraint system about new variable */
			(*el_curconstraint->newvariable)(addr, type, var->key, var->type);

			(void)db_change((INTBIG)addr, VARIABLENEW, type, var->key, var->type, 0, 0, 0);
		} else db_dovarchangequietly = 0;
		return(var);
	}

	/* create new variable: first ensure the name starts with a letter */
	if (!isalpha(*name))
	{
		db_dovarchangequietly = 0;
		return((VARIABLE *)db_error(DBBADNAME|DBSETVAL));
	}

	/* get the key of the new name */
	key = makekey(name);
	if (key == -1)
	{
		db_dovarchangequietly = 0;
		return(NOVARIABLE);
	}

	/* set the variable by its key */
#ifdef DEBUGMEMORY
	return(_setvalkey(addr, type, key, newaddr, newtype, module, line));
#else
	return(setvalkey(addr, type, key, newaddr, newtype));
#endif
}

/*
 * routine to set the variable on the object whose address is "addr", type
 * is "type", and key is "key".  The variable is set to "newaddr" with type
 * "newtype".  The routine returns the variable address (NOVARIABLE on error).
 */
#ifdef DEBUGMEMORY
VARIABLE *_setvalkey(INTBIG addr, INTBIG type, INTBIG key, INTBIG newaddr,
	INTBIG newtype, char *module, INTBIG line)
#else
VARIABLE *setvalkey(INTBIG addr, INTBIG type, INTBIG key, INTBIG newaddr,
	INTBIG newtype)
#endif
{
	REGISTER INTSML med;
	VARIABLE **myfirstvar;
	INTSML *mynumvar;

	/* get the attribute list pointers */
	if (db_getvarptr(addr, type, &myfirstvar, &mynumvar) != 0)
	{
		db_dovarchangequietly = 0;
		return(NOVARIABLE);
	}

	/* binary search variable space for the key */
	med = db_binarysearch(*mynumvar, *myfirstvar, key);
	if (med >= 0)
	{
		if (((*myfirstvar)[med].type&VCANTSET) != 0)
		{
			db_dovarchangequietly = 0;
			return((VARIABLE *)db_error(DBVARFIXED|DBSETVALKEY));
		}

		if (db_dovarchangequietly == 0)
		{
			/* tell constraint system about killed variable */
			(*el_curconstraint->killvariable)(addr, type, key,
				(*myfirstvar)[med].addr, (*myfirstvar)[med].type, (*myfirstvar)[med].textdescript);

			/* record a change for removal of the old variable */
			(void)db_change((INTBIG)addr, VARIABLEKILL, type, key,
				(*myfirstvar)[med].addr, (*myfirstvar)[med].type, (*myfirstvar)[med].textdescript, 0);
		}
	}

	/* set the new variable */
#ifdef DEBUGMEMORY
	if (db_setvalkey(mynumvar, myfirstvar, med, key, newaddr, newtype, db_whichcluster(addr, type),
		module, line) != 0)
#else
	if (db_setvalkey(mynumvar, myfirstvar, med, key, newaddr, newtype, db_whichcluster(addr, type)) != 0)
#endif
	{
		db_dovarchangequietly = 0;
		return((VARIABLE *)db_error(DBNOMEM|DBSETVALKEY));
	}

	if (db_dovarchangequietly == 0)
	{
		/* tell constraint system about new variable */
		(*el_curconstraint->newvariable)(addr, type, key, newtype);

		/* record the change */
		(void)db_change((INTBIG)addr, VARIABLENEW, type, key, newtype, 0, 0, 0);
	} else db_dovarchangequietly = 0;
	if (med < 0) med = -1 - med;
	return(&(*myfirstvar)[med]);
}

/*
 * routine to copy the variables from object "fromaddr" (which has type
 * "fromtype") to object "toaddr" (which has type "totype").  Returns nonzero
 * on error.
 */
INTSML copyvars(INTBIG fromaddr, INTBIG fromtype, INTBIG toaddr, INTBIG totype)
{
	REGISTER INTSML i;
	REGISTER INTBIG key, addr, type;
	INTBIG lx, hx, ly, hy;
	REGISTER GEOM *geom;
	REGISTER NODEPROTO *np;
	REGISTER NODEINST *ni;
	REGISTER ARCINST *ai;
	INTSML *numvar;
	VARIABLE **firstvar;
	REGISTER VARIABLE *var;

	if (db_getvarptr(fromaddr, fromtype, &firstvar, &numvar) != 0) return(1);

	for(i=0; i<(*numvar); i++)
	{
		key = (*firstvar)[i].key;
		addr = (*firstvar)[i].addr;
		type = (*firstvar)[i].type;

		var = setvalkey(toaddr, totype, key, addr, type);
		if (var == NOVARIABLE) return(1);
		var->textdescript = (*firstvar)[i].textdescript;
	}

	/* variables may affect geometry size */
	if (totype == VNODEINST || totype == VARCINST)
	{
		if (totype == VNODEINST)
		{
			ni = (NODEINST *)toaddr;
			geom = ni->geom;
			np = ni->parent;
		} else
		{
			ai = (ARCINST *)toaddr;
			geom = ai->geom;
			np = ai->parent;
		}
		boundobj(geom, &lx, &hx, &ly, &hy);
		if (lx != geom->lowx || hx != geom->highx ||
			ly != geom->lowy || hy != geom->highy)
				updategeom(geom, np);
	}
	return(0);
}

/*********************** SETTING ENTRIES IN VARIABLES ***********************/

/*
 * routine to set an entry in the array variable "name" in object "addr"
 * which is of type "type".  Entry "aindex" is set to "newaddr".
 * The routine returns nonzero upon error.
 */
INTSML setind(INTBIG addr, INTBIG type, char *name, INTBIG aindex, INTBIG newaddr)
{
	VARIABLE *var;
	REGISTER char *pp;
	REGISTER INTBIG search;
	REGISTER INTSML retval;

	search = initobjlist(addr, type, 0);
	if (search == 0)
	{
		db_dovarchangequietly = 0;
		return(1);
	}
	for(;;)
	{
		pp = nextobjectlist(&var, search);
		if (pp == 0) break;
		if (namesame(pp, name) == 0) break;
	}

	/* variable must exist */
	if (pp == 0)
	{
		db_dovarchangequietly = 0;
		return((INTSML)db_error(DBNOVAR|DBSETIND));
	}

	if ((var->type&VCANTSET) != 0)
	{
		db_dovarchangequietly = 0;
		return((INTSML)db_error(DBVARFIXED|DBSETIND));
	}

	retval = db_setindvar(addr, type, var, aindex, newaddr);
	db_dovarchangequietly = 0;
	if (retval != 0) return((INTSML)db_error(DBNOMEM|DBSETIND));
	return(0);
}

/*
 * routine to set an entry in the array variable whose key is "key" in object
 * "addr" which is of type "type".  Entry "aindex" is set to "newaddr".
 * The routine returns nonzero upon error.
 */
INTSML setindkey(INTBIG addr, INTBIG type, INTBIG key, INTBIG aindex, INTBIG newaddr)
{
	VARIABLE **myfirstvar;
	INTSML *mynumvar;
	REGISTER INTSML med, retval;

	/* get the attribute list into the globals */
	if (db_getvarptr(addr, type, &myfirstvar, &mynumvar) != 0)
	{
		db_dovarchangequietly = 0;
		return(1);
	}

	/* binary search variable space for the key */
	med = db_binarysearch(*mynumvar, *myfirstvar, key);

	/* variable must exist */
	if (med < 0)
	{
		db_dovarchangequietly = 0;
		return((INTSML)db_error(DBNOVAR|DBSETINDKEY));
	}

	if (((*myfirstvar)[med].type&VCANTSET) != 0)
	{
		db_dovarchangequietly = 0;
		return((INTSML)db_error(DBVARFIXED|DBSETINDKEY));
	}

	/* set the variable */
	retval = db_setindvar(addr, type, &(*myfirstvar)[med], aindex, newaddr);
	db_dovarchangequietly = 0;
	if (retval != 0) return((INTSML)db_error(DBNOMEM|DBSETINDKEY));
	return(0);
}

/*
 * routine to insert an entry in the array variable "name" in object "addr"
 * which is of type "type".  Entry "aindex" is set to "newaddr" and all entries
 * equal to or greater than "aindex" are moved up (i.e. "newaddr" is inserted
 * before entry "aindex").  The routine returns nonzero upon error.
 */
INTSML insind(INTBIG addr, INTBIG type, char *name, INTBIG aindex, INTBIG newaddr)
{
	VARIABLE *var;
	REGISTER char *pp;
	REGISTER INTBIG search;
	REGISTER INTSML retval;

	search = initobjlist(addr, type, 0);
	if (search == 0)
	{
		db_dovarchangequietly = 0;
		return(1);
	}
	for(;;)
	{
		pp = nextobjectlist(&var, search);
		if (pp == 0) break;
		if (namesame(pp, name) == 0) break;
	}

	/* variable must exist */
	if (pp == 0)
	{
		db_dovarchangequietly = 0;
		return((INTSML)db_error(DBNOVAR|DBINSIND));
	}

	/* cannot insert if variable is frozen or is in C data structures */
	if ((var->type&VCANTSET) != 0 || (var->type&VCREF) != 0)
	{
		db_dovarchangequietly = 0;
		return((INTSML)db_error(DBVARFIXED|DBINSIND));
	}

	retval = db_insindvar(addr, type, var, aindex, newaddr);
	db_dovarchangequietly = 0;
	if (retval != 0) return((INTSML)db_error(DBNOMEM|DBINSIND));
	return(0);
}

/*
 * routine to insert an entry in the array variable whose key is "key" in object
 * "addr" which is of type "type".  Entry "aindex" is set to "newaddr" and all entries
 * equal to or greater than "aindex" are moved up (i.e. "newaddr" is inserted
 * before entry "aindex").  The routine returns nonzero upon error.
 */
INTSML insindkey(INTBIG addr, INTBIG type, INTBIG key, INTBIG aindex, INTBIG newaddr)
{
	VARIABLE **myfirstvar;
	INTSML *mynumvar;
	REGISTER INTSML med, retval;

	/* get the attribute list into the globals */
	if (db_getvarptr(addr, type, &myfirstvar, &mynumvar) != 0)
	{
		db_dovarchangequietly = 0;
		return(1);
	}

	/* binary search variable space for the key */
	med = db_binarysearch(*mynumvar, *myfirstvar, key);

	/* variable must exist */
	if (med < 0)
	{
		db_dovarchangequietly = 0;
		return((INTSML)db_error(DBNOVAR|DBINSINDKEY));
	}

	/* cannot insert if variable is frozen or is in C data structures */
	if (((*myfirstvar)[med].type&VCANTSET) != 0 || ((*myfirstvar)[med].type&VCREF) != 0)
	{
		db_dovarchangequietly = 0;
		return((INTSML)db_error(DBVARFIXED|DBINSINDKEY));
	}

	/* set the variable */
	retval = db_insindvar(addr, type, &(*myfirstvar)[med], aindex, newaddr);
	db_dovarchangequietly = 0;
	if (retval != 0) return((INTSML)db_error(DBNOMEM|DBINSINDKEY));
	return(0);
}

/*
 * routine to delete entry "aindex" in the array variable "name" in object "addr"
 * which is of type "type".  The routine returns nonzero upon error.
 */
INTSML delind(INTBIG addr, INTBIG type, char *name, INTBIG aindex)
{
	VARIABLE *var;
	REGISTER char *pp;
	REGISTER INTBIG search;
	REGISTER INTSML retval;

	search = initobjlist(addr, type, 0);
	if (search == 0)
	{
		db_dovarchangequietly = 0;
		return(1);
	}
	for(;;)
	{
		pp = nextobjectlist(&var, search);
		if (pp == 0) break;
		if (namesame(pp, name) == 0) break;
	}

	/* variable must exist */
	if (pp == 0)
	{
		db_dovarchangequietly = 0;
		return((INTSML)db_error(DBNOVAR|DBDELIND));
	}

	if ((var->type&VCANTSET) != 0)
	{
		db_dovarchangequietly = 0;
		return((INTSML)db_error(DBVARFIXED|DBDELIND));
	}

	retval = db_delindvar(addr, type, var, aindex);
	db_dovarchangequietly = 0;
	if (retval != 0) return((INTSML)db_error(DBNOMEM|DBDELIND));
	return(0);
}

/*
 * routine to delete entry "aindex" in the array variable whose key is "key" in object
 * "addr" which is of type "type".  The routine returns nonzero upon error.
 */
INTSML delindkey(INTBIG addr, INTBIG type, INTBIG key, INTBIG aindex)
{
	VARIABLE **myfirstvar;
	INTSML *mynumvar;
	REGISTER INTSML med, retval;

	/* get the attribute list into the globals */
	if (db_getvarptr(addr, type, &myfirstvar, &mynumvar) != 0)
	{
		db_dovarchangequietly = 0;
		return(1);
	}

	/* binary search variable space for the key */
	med = db_binarysearch(*mynumvar, *myfirstvar, key);

	/* variable must exist */
	if (med < 0)
	{
		db_dovarchangequietly = 0;
		return((INTSML)db_error(DBNOVAR|DBDELINDKEY));
	}

	if (((*myfirstvar)[med].type&VCANTSET) != 0)
	{
		db_dovarchangequietly = 0;
		return((INTSML)db_error(DBVARFIXED|DBDELINDKEY));
	}

	/* set the variable */
	retval = db_delindvar(addr, type, &(*myfirstvar)[med], aindex);
	db_dovarchangequietly = 0;
	if (retval != 0) return((INTSML)db_error(DBNOMEM|DBDELINDKEY));
	return(0);
}

/************************* OPTION VARIABLES *************************/

/*
 * Many user-settable options are stored in variables.  When libraries are saved,
 * these variables are saved, unless they are marked "VDONTSAVE".  To handle this
 * situation properly, the library "options" is saved with all option variables
 * intact, but all other libraries are saved with the option variables removed.
 * The routines below "remove" the option variables (by marking them VDONTSAVE)
 * and restore them after the library is written.
 */

#if COMAID
  extern AIDENTRY *com_aid;
#endif
#if DRCAID
  extern AIDENTRY *dr_aid;
#endif
#if ERCAID
  extern AIDENTRY *erc_aid;
#endif
#if LOGEFFAID
  extern AIDENTRY *le_aid;
#endif
#if ROUTAID
  extern AIDENTRY *ro_aid;
#endif
#if SCAID
  extern AIDENTRY *sc_aid;
#endif
#if SIMAID
  extern AIDENTRY *sim_aid;
#endif
#if VHDLAID
  extern AIDENTRY *vhdl_aid;
#endif

OPTIONVARPROTO db_ovpforeignfilelibrary[] =	/* FILE: IO Options: Library Options */
{
	{0, 0, (INTBIG*)&io_aid,   VAID,        0, "IO_state"},
	{0, 0, 0,                  0,           0, 0}
};
#if IOCIF
OPTIONVARPROTO db_ovpforeignfilecif[] =		/* FILE: IO Options: CIF Options */
{
	{0, 0, (INTBIG*)&io_aid,   VAID,        0, "IO_state"},
	{0, 0, 0,                  VTECHNOLOGY, 0, "IO_cif_layer_names"},
	{0, 0, 0,                  VTECHNOLOGY, 0, "IO_cif_resolution"},
	{0, 0, 0,                  0,           0, 0}
};
#endif
#if IOGDS
OPTIONVARPROTO db_ovpforeignfilegds[] =		/* FILE: IO Options: GDS Options */
{
	{0, 0, (INTBIG*)&io_aid,   VAID,        0, "IO_state"},
	{0, 0, 0,                  VTECHNOLOGY, 0, "IO_gds_layer_numbers"},
	{0, 0, (INTBIG*)&io_aid,   VAID,        0, "IO_curve_resolution"},
	{0, 0, (INTBIG*)&io_aid,   VAID,        0, "IO_curve_sag"},
	{0, 0, 0,                  0,           0, 0}
};
#endif
#if IODXF
OPTIONVARPROTO db_ovpforeignfiledxf[] =		/* FILE: IO Options: DXF Options */
{
	{0, 0, (INTBIG*)&io_aid,   VAID,        0, "IO_state"},
	{0, 0, 0,                  VTECHNOLOGY, 0, "IO_dxf_layer_names"},
	{0, 0, 0,                  0,           0, 0}
};
#endif
#if IOEDIF
OPTIONVARPROTO db_ovpforeignfileedif[] =	/* FILE: IO Options: EDIF Options */
{
	{0, 0, (INTBIG*)&io_aid,   VAID,        0, "IO_state"},
	{0, 0, 0,                  0,           0, 0}
};
#endif
#if IOSKILL
OPTIONVARPROTO db_ovpforeignfileskill[] =	/* FILE: IO Options: SKILL Options */
{
	{0, 0, 0,                  VTECHNOLOGY, 0, "IO_skill_layer_names"},
	{0, 0, 0,                  0,           0, 0}
};
#endif
#if IODEF
OPTIONVARPROTO db_ovpforeignfiledef[] =		/* FILE: IO Options: DEF Options */
{
	{0, 0, (INTBIG*)&io_aid,   VAID,        0, "IO_state"},
	{0, 0, 0,                  0,           0, 0}
};
#endif
OPTIONVARPROTO db_ovpprint[] =				/* FILE: Print Options */
{
/*	{0, 0, 0,                  VNODEPROTO,  0, "IO_postscript_EPS_scale"},  *** facet specific ***/
/*	{0, 0, 0,                  VNODEPROTO,  0, "IO_postscript_filename"},  *** facet specific ***/
	{0, 0, (INTBIG*)&io_aid,   VAID,        0, "IO_state"},
	{0, 0, (INTBIG*)&io_aid,   VAID,        0, "IO_postscript_width"},
	{0, 0, (INTBIG*)&io_aid,   VAID,        0, "IO_postscript_height"},
	{0, 0, (INTBIG*)&io_aid,   VAID,        0, "IO_postscript_margin"},
	{0, 0, (INTBIG*)&io_aid,   VAID,        0, "IO_hpgl2_scale"},
	{0, 0, (INTBIG*)&io_aid,   VAID,        0, "IO_default_printer"},
	{0, 0, 0,                  0,           0, 0}
};
OPTIONVARPROTO db_ovpselect[] =				/* EDIT: Selection: Selection Options */
{
	{0, 0, (INTBIG*)&us_aid,   VAID,        0, "USER_optionflags"},
	{0, 0, 0,                  0,           0, 0}
};
OPTIONVARPROTO db_ovpfacet[] =				/* FACETS: Facet Options */
{
/*	{0, 0, 0,                  VNODEPROTO,  0, "userbits"},  *** facet specific ***/
	{0, 0, (INTBIG*)&us_aid,   VAID,        0, "USER_optionflags"},
	{0, 0, (INTBIG*)&us_aid,   VAID,        0, "USER_tiny_lambda_per_pixel"},
	{0, 0, 0,                  0,           0, 0}
};
OPTIONVARPROTO db_ovpframe[] =				/* VIEW: Frame Options */
{
/*	{0, 0, 0,                  VNODEPROTO,  0, "FACET_schematic_page_size"},  *** facet specific ***/
	{0, 0, (INTBIG*)&us_aid,   VAID,        0, "USER_drawing_company_name"},
	{0, 0, (INTBIG*)&us_aid,   VAID,        0, "USER_drawing_designer_name"},
	{0, 0, 0,                  0,           0, 0}
};
OPTIONVARPROTO db_ovpicon[] =				/* VIEW: Icon Options */
{
	{0, 0, (INTBIG*)&us_aid,   VAID,        0, "USER_icon_style"},
	{0, 0, (INTBIG*)&us_aid,   VAID,        0, "USER_optionflags"},
	{0, 0, 0,                  0,           0, 0}
};
OPTIONVARPROTO db_ovpgrid[] =				/* WINDOWS: Grid Options */
{
	{0, 0, (INTBIG*)&us_aid,   VAID,        0, "USER_default_grid"},
	{0, 0, (INTBIG*)&us_aid,   VAID,        0, "USER_grid_bold_spacing"},
	{0, 0, (INTBIG*)&us_aid,   VAID,        0, "USER_grid_floats"},
	{0, 0, 0,                  0,           0, 0}
};
OPTIONVARPROTO db_ovpalign[] =				/* WINDOWS: Alignment Options */
{
	{0, 0, (INTBIG*)&us_aid,   VAID,        0, "USER_alignment_obj"},
	{0, 0, (INTBIG*)&us_aid,   VAID,        0, "USER_alignment_edge"},
	{0, 0, 0,                  0,           0, 0}
};
OPTIONVARPROTO db_ovplaypat[] =				/* WINDOWS: Layer Pattern Options */
{
	{0, 0, (INTBIG*)&us_aid,   VTECHNOLOGY, 1, "TECH_layer_pattern_"},
	{0, 0, 0,                  0,           0, 0}
};
OPTIONVARPROTO db_ovpcolor[] =				/* WINDOWS: Color Options */
{
	{0, 0, (INTBIG*)&us_aid,   VAID,        0, "USER_colormap_red"},
	{0, 0, (INTBIG*)&us_aid,   VAID,        0, "USER_colormap_green"},
	{0, 0, (INTBIG*)&us_aid,   VAID,        0, "USER_colormap_blue"},
	{0, 0, 0,                  0,           0, 0}
};
OPTIONVARPROTO db_ovpport[] =				/* WINDOWS: Port Display Options */
{
	{0, 0, (INTBIG*)&us_aid,   VAID,        0, "USER_optionflags"},
	{0, 0, 0,                  0,           0, 0}
};
OPTIONVARPROTO db_ovptext[] =				/* WINDOWS: Text Options */
{
	{0, 0, (INTBIG*)&us_aid,   VAID,        0, "USER_default_text_style"},
	{0, 0, (INTBIG*)&us_aid,   VAID,        0, "USER_default_text_smart_style"},
	{0, 0, (INTBIG*)&us_aid,   VAID,        0, "USER_default_text_size"},
	{0, 0, (INTBIG*)&us_aid,   VAID,        0, "USER_text_editor"},
	{0, 0, 0,                  0,           0, 0}
};
OPTIONVARPROTO db_ovp3d[] =					/* WINDOWS: 3D Options */
{
	{0, 0, 0,                  VTECHNOLOGY, 0, "TECH_layer_3dheight"},
	{0, 0, 0,                  VTECHNOLOGY, 0, "TECH_layer_3dthickness"},
	{0, 0, (INTBIG*)&us_aid,   VAID,        0, "USER_optionflags"},
	{0, 0, 0,                  0,           0, 0}
};
OPTIONVARPROTO db_ovpnewnode[] =			/* INFO: New Node Options */
{
/*	{0, 0, 0,                  VNODEPROTO,  0, "userbits"},  *** facet specific ***/
	{0, 0, 0,                  VNODEPROTO,  0, "NODE_default_size"},
	{0, 0, 0,                  VNODEPROTO,  0, "USER_placement_angle"},
	{0, 0, (INTBIG*)&us_aid,   VAID,        0, "USER_optionflags"},
	{0, 0, (INTBIG*)&us_aid,   VAID,        0, "USER_placement_angle"},
	{0, 0, 0,                  0,           0, 0}
};
OPTIONVARPROTO db_ovpnewarc[] =				/* INFO: New Arc Options */
{
	{0, 0, 0,                  VARCPROTO,   0, "ARC_default_width"},
	{0, 0, (INTBIG*)&us_aid,   VAID,        0, "USER_arc_style"},
	{0, 0, 0,                  VARCPROTO,   0, "USER_arc_style"},
	{0, 0, 0,                  VARCPROTO,   0, "ARC_Default_Pin"},
	{0, 0, 0,                  0,           0, 0}
};
OPTIONVARPROTO db_ovpquickkey[] =			/* INFO: Quick Key Options */
{
	{0, 0, (INTBIG*)&us_aid,   VAID,        0, "USER_quick_keys"},
	{0, 0, 0,                  0,           0, 0}
};
OPTIONVARPROTO db_ovptechopt[] =			/* TECHNOLOGY: Technology Options */
{
	{0, 0, 0,                  VTECHNOLOGY, 0, "TECH_state"},
	{0, 0, 0,                  0,           0, 0}
};
OPTIONVARPROTO db_ovptechunits[] =			/* TECHNOLOGY: Units */
{
	{0, 0, (INTBIG*)&us_aid,   VAID,        0, "USER_display_units"},
	{0, 0, 0,                  0,           0, 0}
};
#if DRCAID
OPTIONVARPROTO db_ovpdrc[] =				/* TOOLS: DRC: DRC Options */
{
	{0, 0, 0,                  VTECHNOLOGY, 0, "DRC_min_unconnected_distances"},
	{0, 0, 0,                  VTECHNOLOGY, 0, "DRC_min_connected_distances"},
	{0, 0, 0,                  VTECHNOLOGY, 0, "DRC_min_width"},
	{0, 0, (INTBIG*)&dr_aid,   VAID,        0, "DRC_pointout"},
	{0, 0, (INTBIG*)&dr_aid,   VAID,        0, "DRC_options"},
	{0, 0, (INTBIG*)&dr_aid,   VAID,        0, "DRC_incrementalon"},
	{0, 0, 0,                  VTECHNOLOGY, 0, "DRC_ecad_deck"},
	{0, 0, 0,                  0,           0, 0}
};
#endif
#if SIMAID
OPTIONVARPROTO db_ovpsimulation[] =			/* TOOLS: Simulation: Simulation Options */
{
	{0, 0, (INTBIG*)&sim_aid,  VAID,        0, "SIM_als_no_update"},
	{0, 0, (INTBIG*)&sim_aid,  VAID,        0, "SIM_als_num_events"},
	{0, 0, (INTBIG*)&sim_aid,  VAID,        0, "SIM_window_state"},
	{0, 0, 0,                  0,           0, 0}
};
OPTIONVARPROTO db_ovpsimspice[] =			/* TOOLS: Simulation Interface: Spice Options */
{
/*	{0, 0, 0,                  VNODEPROTO,  0, "SIM_spice_behave_file"},  *** facet specific ***/
	{0, 0, (INTBIG*)&sim_aid,  VAID,        0, "SIM_dontrun"},
	{0, 0, (INTBIG*)&sim_aid,  VAID,        0, "SIM_spice_level"},
	{0, 0, (INTBIG*)&sim_aid,  VAID,        0, "SIM_spice_state"},
	{0, 0, 0,                  VTECHNOLOGY, 0, "SIM_spice_model_file"},
	{0, 0, 0,                  VTECHNOLOGY, 0, "SIM_spice_trailer_file"},
	{0, 0, 0,                  VTECHNOLOGY, 0, "SIM_spice_header_level1"},
	{0, 0, 0,                  VTECHNOLOGY, 0, "SIM_spice_header_level2"},
	{0, 0, 0,                  VTECHNOLOGY, 0, "SIM_spice_header_level3"},
	{0, 0, 0,                  VTECHNOLOGY, 0, "SIM_spice_resistance"},
	{0, 0, 0,                  VTECHNOLOGY, 0, "SIM_spice_capacitance"},
	{0, 0, 0,                  VTECHNOLOGY, 0, "SIM_spice_edge_capacitance"},
	{0, 0, 0,                  VTECHNOLOGY, 0, "SIM_spice_min_resistance"},
	{0, 0, 0,                  VTECHNOLOGY, 0, "SIM_spice_min_capacitance"},
	{0, 0, 0,                  0,           0, 0}
};
OPTIONVARPROTO db_ovpsimverilog[] =			/* TOOLS: Simulation Interface: Verilog Options */
{
/*	{0, 0, 0,                  VNODEPROTO,  0, "SIM_verilog_behave_file"},  *** facet specific ***/
	{0, 0, (INTBIG*)&sim_aid,  VAID,        0, "SIM_verilog_state"},
	{0, 0, 0,                  0,           0, 0}
};
OPTIONVARPROTO db_ovpsimfasthenry[] =		/* TOOLS: Simulation Interface: FastHenry Options */
{
	{0, 0, (INTBIG*)&sim_aid,  VAID,        0, "SIM_fasthenry_state"},
	{0, 0, (INTBIG*)&sim_aid,  VAID,        0, "SIM_fasthenry_freqstart"},
	{0, 0, (INTBIG*)&sim_aid,  VAID,        0, "SIM_fasthenry_freqend"},
	{0, 0, (INTBIG*)&sim_aid,  VAID,        0, "SIM_fasthenry_freqstep"},
	{0, 0, (INTBIG*)&sim_aid,  VAID,        0, "SIM_fasthenry_numpoles"},
	{0, 0, (INTBIG*)&sim_aid,  VAID,        0, "SIM_fasthenry_seglimit"},
	{0, 0, (INTBIG*)&sim_aid,  VAID,        0, "SIM_fasthenry_segwidth"},
	{0, 0, (INTBIG*)&sim_aid,  VAID,        0, "SIM_fasthenry_segheight"},
	{0, 0, 0,                  0,           0, 0}
};
#endif
#if ERCAID
OPTIONVARPROTO db_ovperc[] =				/* TOOLS: ERC: ERC Options */
{
	{0, 0, (INTBIG*)&erc_aid,  VAID,        0, "ERC_options"},
	{0, 0, 0,                  0,           0, 0}
};
#endif
OPTIONVARPROTO db_ovpnetwork[] =			/* TOOLS: Network: Network Options */
{
	{0, 0, (INTBIG*)&net_aid,  VAID,        0, "NET_connect_PandG"},
	{0, 0, (INTBIG*)&net_aid,  VAID,        0, "NET_connect_common"},
	{0, 0, (INTBIG*)&net_aid,  VAID,        0, "NET_ncc_options"},
	{0, 0, (INTBIG*)&net_aid,  VAID,        0, "NET_auto_name"},
	{0, 0, 0,                  0,           0, 0}
};
#if LOGEFFAID
OPTIONVARPROTO db_ovplogeffort[] =			/* TOOLS: Logical Effort: Logical Effort Options */
{
	{0, 0, (INTBIG*)&le_aid,   VAID,        0, "LE_display_capacitance"},
	{0, 0, (INTBIG*)&le_aid,   VAID,        0, "LE_maximum_stage_effort"},
	{0, 0, 0,                  0,           0, 0}
};
#endif
#if ROUTAID
OPTIONVARPROTO db_ovprouting[] =			/* TOOLS: Routing: Routing Options */
{
	{0, 0, (INTBIG*)&ro_aid,   VAID,        0, "ROUT_prefered_arc"},
	{0, 0, (INTBIG*)&ro_aid,   VAID,        0, "ROUT_options"},
	{0, 0, 0,                  0,           0, 0}
};
#endif
#if VHDLAID
OPTIONVARPROTO db_ovpvhdl[] =				/* TOOLS: VHDL: VHDL Options */
{
	{0, 0, (INTBIG*)&vhdl_aid, VAID,        0, "VHDL_vhdl_on_disk"},
	{0, 0, (INTBIG*)&vhdl_aid, VAID,        0, "VHDL_netlist_on_disk"},
	{0, 0, 0,                  VTECHNOLOGY, 0, "TECH_vhdl_names"},
	{0, 0, 0,                  0,           0, 0}
};
#endif
#if SCAID
OPTIONVARPROTO db_ovpsilcomp[] =			/* TOOLS: Silicon Compiler: Silicon Compiler Options */
{
	{0, 0, (INTBIG*)&sc_aid,   VAID,        0, "SC_horiz_arc"},
	{0, 0, (INTBIG*)&sc_aid,   VAID,        0, "SC_vert_arc"},
	{0, 0, (INTBIG*)&sc_aid,   VAID,        0, "SC_l1_width"},
	{0, 0, (INTBIG*)&sc_aid,   VAID,        0, "SC_l2_width"},
	{0, 0, (INTBIG*)&sc_aid,   VAID,        0, "SC_pwr_width"},
	{0, 0, (INTBIG*)&sc_aid,   VAID,        0, "SC_main_pwr_width"},
	{0, 0, (INTBIG*)&sc_aid,   VAID,        0, "SC_pwell_size"},
	{0, 0, (INTBIG*)&sc_aid,   VAID,        0, "SC_pwell_offset"},
	{0, 0, (INTBIG*)&sc_aid,   VAID,        0, "SC_via_size"},
	{0, 0, (INTBIG*)&sc_aid,   VAID,        0, "SC_min_spacing"},
	{0, 0, (INTBIG*)&sc_aid,   VAID,        0, "SC_feedthru_size"},
	{0, 0, (INTBIG*)&sc_aid,   VAID,        0, "SC_port_x_min_dist"},
	{0, 0, (INTBIG*)&sc_aid,   VAID,        0, "SC_active_dist"},
	{0, 0, (INTBIG*)&sc_aid,   VAID,        0, "SC_num_rows"},
	{0, 0, 0,                  0,           0, 0}
};
#endif
#if COMAID
OPTIONVARPROTO db_ovpcompaction[] =			/* TOOLS: Compaction: Compaction Options */
{
	{0, 0, (INTBIG*)&com_aid,  VAID,        0, "COM_spread"},
	{0, 0, 0,                  0,           0, 0}
};
#endif

#define OPTIONCIF                 01		/* CIF Options */
#define OPTIONGDS                 02		/* GDS Options */
#define OPTIONDXF                 04		/* DXF Options */
#define OPTIONEDIF               010		/* EDIF Options */
#define OPTIONPRINT              020		/* Print Options */
#define OPTIONFRAME              040		/* Frame Options */
#define OPTIONALIGNMENT         0100		/* Alignment Options */
#define OPTIONTEXT              0200		/* Text Options */
#define OPTIONNEWNODE           0400		/* New Node Options */
#define OPTIONNEWARC           01000		/* New Arc Options */
#define OPTIONDRC              02000		/* DRC Options */
#define OPTIONSIMULATION       04000		/* Simulation Options */
#define OPTIONSPICE           010000		/* Spice Options */
#define OPTIONVERILOG         020000		/* Verilog Options */
#define OPTIONVHDL            040000		/* VHDL Options */
#define OPTIONSILCOMP        0100000		/* Silicon Compiler Options */
#define OPTIONNETWORK        0200000		/* Network Options */
#define OPTIONROUTING        0400000		/* Routing Options */
#define OPTIONCOMPACTION    01000000		/* Compaction Options */
#define OPTIONGRID          02000000		/* Grid Options */
#define OPTIONLOGEFFORT     04000000		/* Logical Effort Options */
#define OPTIONPORT         010000000		/* Port Display Options */
#define OPTIONCOLOR        020000000		/* Color Options */
#define OPTIONQUICKKEY     040000000		/* Quick Key Options */
#define OPTIONSKILL       0100000000		/* SKILL Options */
#define OPTIONFACET       0200000000		/* Facet Options */
#define OPTIONSELECT      0400000000		/* Selection Options */
#define OPTION3D         01000000000		/* 3D Options */
#define OPTIONTECH       02000000000		/* Technology Options */
#define OPTIONERC        04000000000		/* ERC Options */
#define OPTIONLAYPAT    010000000000		/* Layer Pattern Options */
#define OPTIONFASTHENRY 020000000000		/* FastHenry Options */
#define OPTIONDEF                 01		/* DEF Options */
#define OPTIONUNITS               02		/* Technology Units */
#define OPTIONICON                04		/* Icon Options */
#define OPTIONLIBRARY            010		/* Library Options */

OPTIONVARCOMS db_optionvarcoms[] =
{
	{{0,OPTIONLIBRARY},    N_("FILE: Library Options"), db_ovpforeignfilelibrary},
#if IOCIF
	{{OPTIONCIF,0},        N_("FILE: CIF Options"), db_ovpforeignfilecif},
#endif
#if IOGDS
	{{OPTIONGDS,0},        N_("FILE: GDS Options"), db_ovpforeignfilegds},
#endif
#if IODXF
	{{OPTIONDXF,0},        N_("FILE: DXF Options"), db_ovpforeignfiledxf},
#endif
#if IOEDIF
	{{OPTIONEDIF,0},       N_("FILE: EDIF Options"), db_ovpforeignfileedif},
#endif
#if IOSKILL
	{{OPTIONSKILL,0},      N_("FILE: SKILL Options"), db_ovpforeignfileskill},
#endif
#if IODEF
	{{0,OPTIONDEF},        N_("FILE: DEF Options"), db_ovpforeignfiledef},
#endif
	{{OPTIONPRINT,0},      N_("FILE: Print Options"), db_ovpprint},
	{{OPTIONSELECT,0},     N_("EDIT: Selection Options"), db_ovpselect},
	{{OPTIONFACET,0},      N_("FACET: Facet Options"), db_ovpfacet},
	{{OPTIONFRAME,0},      N_("VIEW: Frame Options"), db_ovpframe},
	{{0,OPTIONICON},       N_("VIEW: Icon Options"), db_ovpicon},
	{{OPTIONGRID,0},       N_("WINDOWS: Grid Options"), db_ovpgrid},
	{{OPTIONALIGNMENT,0},  N_("WINDOWS: Alignment Options"), db_ovpalign},
	{{OPTIONLAYPAT,0},     N_("WINDOWS: Layer Pattern Options"), db_ovplaypat},
	{{OPTIONCOLOR,0},      N_("WINDOWS: Color Options"), db_ovpcolor},
	{{OPTIONPORT,0},       N_("WINDOWS: Port Display Options"), db_ovpport},
	{{OPTIONTEXT,0},       N_("WINDOWS: Text Options"), db_ovptext},
	{{OPTION3D,0},         N_("WINDOWS: 3D Options"), db_ovp3d},
	{{OPTIONNEWNODE,0},    N_("INFO: New Node Options"), db_ovpnewnode},
	{{OPTIONNEWARC,0},     N_("INFO: New Arc Options"), db_ovpnewarc},
	{{OPTIONQUICKKEY,0},   N_("INFO: Quick Key Options"), db_ovpquickkey},
	{{OPTIONTECH,0},       N_("TECHNOLOGY: TECHNOLOGY Options"), db_ovptechopt},
	{{0,OPTIONUNITS},      N_("TECHNOLOGY: Units"), db_ovptechunits},
#if DRCAID
	{{OPTIONDRC,0},        N_("TOOLS: DRC Options"), db_ovpdrc},
#endif
#if SIMAID
	{{OPTIONSIMULATION,0}, N_("TOOLS: Simulation Options"), db_ovpsimulation},
	{{OPTIONSPICE,0},      N_("TOOLS: Spice Options"), db_ovpsimspice},
	{{OPTIONVERILOG,0},    N_("TOOLS: Verilog Options"), db_ovpsimverilog},
	{{OPTIONFASTHENRY,0},  N_("TOOLS: FastHenry Options"), db_ovpsimfasthenry},
#endif
#if ERCAID
	{{OPTIONERC,0},        N_("TOOLS: ERC Options"), db_ovperc},
#endif
	{{OPTIONNETWORK,0},    N_("TOOLS: Network Options"), db_ovpnetwork},
#if LOGEFFAID
	{{OPTIONLOGEFFORT,0},  N_("TOOLS: Logical Effort Options"), db_ovplogeffort},
#endif
#if ROUTAID
	{{OPTIONROUTING,0},    N_("TOOLS: Routing Options"), db_ovprouting},
#endif
#if VHDLAID
	{{OPTIONVHDL,0},       N_("TOOLS: VHDL Options"), db_ovpvhdl},
#endif
#if SCAID
	{{OPTIONSILCOMP,0},    N_("TOOLS: Silicon Compiler Options"), db_ovpsilcomp},
#endif
#if COMAID
	{{OPTIONCOMPACTION,0}, N_("TOOLS: Compaction Options"), db_ovpcompaction}
#endif
};

/*
 * Routine to return the "name" of option "aindex" and its "bits".
 * Returns nonzero if the aindex is out of range.
 */
INTSML describeoptions(INTBIG aindex, char **name, INTBIG *bits)
{
	REGISTER INTBIG optioncomcount, i;

	optioncomcount = sizeof(db_optionvarcoms) / sizeof(OPTIONVARCOMS);
	if (aindex < 0 || aindex >= optioncomcount) return(1);
	*name = _(db_optionvarcoms[aindex].command);
	for(i=0; i<SAVEDBITWORDS; i++)
		bits[i] = db_optionvarcoms[aindex].bits[i];
	return(0);
}

/*
 * Routine to return nonzero if variable "name" on object "addr" (type "type")
 * is an option variable.
 */
INTSML isoptionvariable(INTBIG addr, INTBIG type, char *name)
{
	REGISTER INTBIG i, j, len, optioncomcount;
	REGISTER OPTIONVARPROTO *ovp;
	REGISTER VARIABLE *var;

	optioncomcount = sizeof(db_optionvarcoms) / sizeof(OPTIONVARCOMS);
	for(i=0; i<optioncomcount; i++)
	{
		ovp = db_optionvarcoms[i].variables;
		for(j=0; ovp[j].variablename != 0; j++)
		{
			if (ovp[j].type != type) continue;
			if (ovp[j].partial != 0)
			{
				len = strlen(ovp[j].variablename);
				if (namesamen(ovp[j].variablename, name, len) != 0) continue;
			} else
			{
				if (namesame(ovp[j].variablename, name) != 0) continue;
			}

			/* see if option tracking has been disabled */
			var = getvalkey((INTBIG)us_aid, VAID, VINTEGER, us_ignoreoptionchanges);
			if (var != NOVARIABLE) continue;

			ovp[j].dirty = 1;
			return(1);
		}
	}
	return(0);
}

/*
 * Routine to display which options have changed.
 */
void explainoptionchanges(void)
{
	REGISTER INTBIG i, j, optioncomcount, namedoption;
	REGISTER OPTIONVARPROTO *ovp;

	optioncomcount = sizeof(db_optionvarcoms) / sizeof(OPTIONVARCOMS);
	for(i=0; i<optioncomcount; i++)
	{
		namedoption = 0;
		ovp = db_optionvarcoms[i].variables;
		for(j=0; ovp[j].variablename != 0; j++)
		{
			if (ovp[j].dirty == 0) continue;
			if (namedoption == 0)
			{
				ttyputmsg("%s:", _(db_optionvarcoms[i].command));
				namedoption = 1;
			}
			ttyputmsg("  %s", ovp[j].variablename);
		}
	}
}

/*
 * Routine to make all options "temporary" (set their "VDONTSAVE" bit so that they are
 * not saved to disk).  Any exceptions to this may be specified in the
 * "LIB_save_options" variable of "lib".  In addition, all such option variables are
 * remembered so that the "temporary" state can be restored later in
 * "us_restoreoptionstate()".  This is done before saving a library so that the
 * options are *NOT* saved with the data.  Only the "options" library has these
 * option variables saved with it.
 */
void makeoptionstemporary(LIBRARY *lib)
{
	REGISTER INTBIG teccount, arccount, nodecount, i, j, k, l, ind, optioncomcount,
		len;
	INTBIG savebits[SAVEDBITWORDS];
	REGISTER char *name;
	REGISTER VARIABLE *var;
	REGISTER TECHNOLOGY *tech;
	REGISTER NODEPROTO *np;
	REGISTER ARCPROTO *ap;
	REGISTER OPTIONVARPROTO *ovp, *ovp2;

	/* deallocate any former options variable cache */
	if (db_optionvarcachecount > 0) efree((char *)db_optionvarcache);
	db_optionvarcachecount = 0;

	/* see which options *SHOULD* be saved */
	for(i=0; i<SAVEDBITWORDS; i++) savebits[i] = 0;
	var = getval((INTBIG)el_curlib, VLIBRARY, -1, "LIB_save_options");
	if (var != NOVARIABLE)
	{
		if ((var->type&VISARRAY) == 0)
		{
			savebits[0] = var->addr;
		} else
		{
			len = getlength(var);
			for(i=0; i<len; i++) savebits[i] = ((INTBIG *)var->addr)[i];
		}
	}

	/* clear flags of which options to save */
	optioncomcount = sizeof(db_optionvarcoms) / sizeof(OPTIONVARCOMS);
	for(i=0; i<optioncomcount; i++)
	{
		ovp = db_optionvarcoms[i].variables;
		for(j=0; ovp[j].variablename != 0; j++) ovp[j].saveflag = 0;
	}

	/* set flags on options that should be saved */
	for(i=0; i<optioncomcount; i++)
	{
		/* see if this option package should be saved */
		for(j=0; j<SAVEDBITWORDS; j++)
			if ((db_optionvarcoms[i].bits[j]&savebits[j]) != 0) break;
		if (j >= SAVEDBITWORDS) continue;

		ovp = db_optionvarcoms[i].variables;
		for(j=0; ovp[j].variablename != 0; j++)
		{
			ovp[j].saveflag = 1;

			/* also save same option in other commands */
			for(k=0; k<optioncomcount; k++)
			{
				if (k == i) continue;
				ovp2 = db_optionvarcoms[k].variables;
				for(l=0; ovp2[l].variablename != 0; l++)
				{
					if (ovp[j].addr == ovp2[l].addr &&
						ovp[j].type == ovp2[l].type &&
						namesame(ovp[j].variablename, ovp2[l].variablename) == 0)
							ovp2[l].saveflag = 1;
				}
			}
		}
	}

	/* determine the number of option variables */
	teccount = arccount = nodecount = 0;
	for(tech = el_technologies; tech != NOTECHNOLOGY; tech = tech->nexttechnology)
	{
		teccount++;
		for(np = tech->firstnodeproto; np != NONODEPROTO; np = np->nextnodeproto) nodecount++;
		for(ap = tech->firstarcproto; ap != NOARCPROTO; ap = ap->nextarcproto) arccount++;
	}

	db_optionvarcachecount = 0;
	for(i=0; i<optioncomcount; i++)
	{
		ovp = db_optionvarcoms[i].variables;
		for(j=0; ovp[j].variablename != 0; j++)
		{
			if (ovp[j].saveflag != 0) continue;
			switch (ovp[j].type)
			{
				case VTECHNOLOGY:
					if (ovp[j].partial == 0) db_optionvarcachecount += teccount; else
					{
						len = strlen(ovp[j].variablename);
						for(tech = el_technologies; tech != NOTECHNOLOGY; tech = tech->nexttechnology)
						{
							for(k=0; k<tech->numvar; k++)
							{
								var = &tech->firstvar[k];
								name = makename(var->key);
								if (namesamen(ovp[j].variablename, name, len) == 0)
									db_optionvarcachecount++;
							}
						}
					}
					break;
				case VNODEPROTO:  db_optionvarcachecount += nodecount;  break;
				case VARCPROTO:   db_optionvarcachecount += arccount;   break;
				default:          db_optionvarcachecount++;             break;
			}
		}
	}

	/* allocate space for the option variable cache */
	db_optionvarcache = (OPTIONVARCACHE *)emalloc(db_optionvarcachecount * (sizeof (OPTIONVARCACHE)),
		us_aid->cluster);
	if (db_optionvarcache == 0)
	{
		db_optionvarcachecount = 0;
		return;
	}

	/* load the option variable cache */
	ind = 0;
	for(i=0; i<optioncomcount; i++)
	{
		ovp = db_optionvarcoms[i].variables;
		for(j=0; ovp[j].variablename != 0; j++)
		{
			if (ovp[j].saveflag != 0) continue;
			switch (ovp[j].type)
			{
				case VTECHNOLOGY:
					if (ovp[j].partial == 0)
					{
						for(tech = el_technologies; tech != NOTECHNOLOGY; tech = tech->nexttechnology)
						{
							db_optionvarcache[ind].addr = (INTBIG)tech;
							db_optionvarcache[ind].type = VTECHNOLOGY;
							db_optionvarcache[ind].variablename = ovp[j].variablename;
							ind++;
						}
					} else
					{
						len = strlen(ovp[j].variablename);
						for(tech = el_technologies; tech != NOTECHNOLOGY; tech = tech->nexttechnology)
						{
							for(k=0; k<tech->numvar; k++)
							{
								var = &tech->firstvar[k];
								name = makename(var->key);
								if (namesamen(ovp[j].variablename, name, len) != 0) continue;
								db_optionvarcache[ind].addr = (INTBIG)tech;
								db_optionvarcache[ind].type = VTECHNOLOGY;
								db_optionvarcache[ind].variablename = name;
								ind++;
							}
						}
					}
					break;
				case VNODEPROTO:
					for(tech = el_technologies; tech != NOTECHNOLOGY; tech = tech->nexttechnology)
					{
						for(np = tech->firstnodeproto; np != NONODEPROTO; np = np->nextnodeproto)
						{
							db_optionvarcache[ind].addr = (INTBIG)np;
							db_optionvarcache[ind].type = VNODEPROTO;
							db_optionvarcache[ind].variablename = ovp[j].variablename;
							ind++;
						}
					}
					break;
				case VARCPROTO:
					for(tech = el_technologies; tech != NOTECHNOLOGY; tech = tech->nexttechnology)
					{
						for(ap = tech->firstarcproto; ap != NOARCPROTO; ap = ap->nextarcproto)
						{
							db_optionvarcache[ind].addr = (INTBIG)ap;
							db_optionvarcache[ind].type = VARCPROTO;
							db_optionvarcache[ind].variablename = ovp[j].variablename;
							ind++;
						}
					}
					break;
				default:
					db_optionvarcache[ind].addr = *ovp[j].addr;
					db_optionvarcache[ind].type = ovp[j].type;
					db_optionvarcache[ind].variablename = ovp[j].variablename;
					ind++;
					break;
			}
		}
	}

	/* now cache the parameter state and make them non-savable */
	for(i=0; i<db_optionvarcachecount; i++)
	{
		db_optionvarcache[i].var = getval(db_optionvarcache[i].addr, db_optionvarcache[i].type, -1,
			db_optionvarcache[i].variablename);
		if (db_optionvarcache[i].var == NOVARIABLE) continue;
		db_optionvarcache[i].oldtype = db_optionvarcache[i].var->type;
	}
	for(i=0; i<db_optionvarcachecount; i++)
	{
		if (db_optionvarcache[i].var == NOVARIABLE) continue;
		db_optionvarcache[i].var->type |= VDONTSAVE;
	}
}

/*
 * Routine to restore the "temporary" state of all option variables (used after
 * the library has been written.
 */
void restoreoptionstate(void)
{
	REGISTER INTBIG i;

	/* restore the parameter state */
	for(i=0; i<db_optionvarcachecount; i++)
	{
		if (db_optionvarcache[i].var == NOVARIABLE) continue;
		db_optionvarcache[i].var->type = db_optionvarcache[i].oldtype;
	}
}

/************************* DATA FOR TYPES *************************/

/*
 * In the "type" field of a variable, the VCREF bit is typically set
 * to indicate that the variable points into the fixed C structures, and
 * is not part of the extendable attributes in "firstvar/numvar".  However,
 * in the tables below, the VCREF bit is used to indicate dereferencing
 * in array attributes.  Those arrays that are fixed in size or otherwise
 * part of the C structure need no dereferencing, and have the VCREF bit set
 * (see RTNODE->pointers, GRAPHICS->raster, and GRAPHICS->style).  Those
 * arrays that vary in length have only pointers in their C structures, and
 * need dereferencing (see PORTPROTO->connects, LIBRARY->lambda,
 * TECHNOLOGY->layers, NETWORK->arcaddr, and NETWORK->networklist).  These
 * do not have the VCREF bit set.
 */

static NODEINST db_ptni;
static NAMEVARS db_nodevars[] =
{
	{"firstportarcinst",VPORTARCINST|VCANTSET,(INTBIG*)&db_ptni.firstportarcinst},
	{"firstportexpinst",VPORTEXPINST|VCANTSET,(INTBIG*)&db_ptni.firstportexpinst},
	{"geom",            VGEOM|VCANTSET,       (INTBIG*)&db_ptni.geom},
	{"highx",           VINTEGER|VCANTSET,    (INTBIG*)&db_ptni.highx},
	{"highy",           VINTEGER|VCANTSET,    (INTBIG*)&db_ptni.highy},
	{"lastinst",        VNODEINST|VCANTSET,   (INTBIG*)&db_ptni.lastinst},
	{"lastnodeinst",    VNODEINST|VCANTSET,   (INTBIG*)&db_ptni.lastnodeinst},
	{"lowx",            VINTEGER|VCANTSET,    (INTBIG*)&db_ptni.lowx},
	{"lowy",            VINTEGER|VCANTSET,    (INTBIG*)&db_ptni.lowy},
	{"nextinst",        VNODEINST|VCANTSET,   (INTBIG*)&db_ptni.nextinst},
	{"nextnodeinst",    VNODEINST|VCANTSET,   (INTBIG*)&db_ptni.nextnodeinst},
	{"parent",          VNODEPROTO|VCANTSET,  (INTBIG*)&db_ptni.parent},
	{"proto",           VNODEPROTO|VCANTSET,  (INTBIG*)&db_ptni.proto},
	{"rotation",        VSHORT|VCANTSET,      (INTBIG*)&db_ptni.rotation},
	{"temp1",           VINTEGER,             (INTBIG*)&db_ptni.temp1},
	{"temp2",           VINTEGER,             (INTBIG*)&db_ptni.temp2},
	{"textdescript",    VINTEGER,             (INTBIG*)&db_ptni.textdescript},
	{"transpose",       VSHORT|VCANTSET,      (INTBIG*)&db_ptni.transpose},
	{"userbits",        VINTEGER,             (INTBIG*)&db_ptni.userbits},
	{NULL, 0, NULL}  /* 0 */
};
static NODEPROTO db_ptnp;
static NAMEVARS db_nodeprotovars[] =
{
	{"adirty",          VINTEGER|VCANTSET,    (INTBIG*)&db_ptnp.adirty},
	{"cell",            VCELL|VCANTSET,       (INTBIG*)&db_ptnp.cell},
	{"cellview",        VVIEW,                (INTBIG*)&db_ptnp.cellview},
	{"creationdate",    VINTEGER|VCANTSET,    (INTBIG*)&db_ptnp.creationdate},
	{"firstarcinst",    VARCINST|VCANTSET,    (INTBIG*)&db_ptnp.firstarcinst},
	{"firstinst",       VNODEINST|VCANTSET,   (INTBIG*)&db_ptnp.firstinst},
	{"firstnetwork",    VNETWORK|VCANTSET,    (INTBIG*)&db_ptnp.firstnetwork},
	{"firstnodeinst",   VNODEINST|VCANTSET,   (INTBIG*)&db_ptnp.firstnodeinst},
	{"firstportproto",  VPORTPROTO|VCANTSET,  (INTBIG*)&db_ptnp.firstportproto},
	{"highx",           VINTEGER,             (INTBIG*)&db_ptnp.highx},
	{"highy",           VINTEGER,             (INTBIG*)&db_ptnp.highy},
	{"lastnodeproto",   VNODEPROTO|VCANTSET,  (INTBIG*)&db_ptnp.lastnodeproto},
	{"lastversion",     VNODEPROTO|VCANTSET,  (INTBIG*)&db_ptnp.lastversion},
	{"lowx",            VINTEGER,             (INTBIG*)&db_ptnp.lowx},
	{"lowy",            VINTEGER,             (INTBIG*)&db_ptnp.lowy},
	{"newestversion",   VNODEPROTO|VCANTSET,  (INTBIG*)&db_ptnp.newestversion},
	{"nextincell",      VNODEPROTO|VCANTSET,  (INTBIG*)&db_ptnp.nextincell},
	{"nextnodeproto",   VNODEPROTO|VCANTSET,  (INTBIG*)&db_ptnp.nextnodeproto},
	{"primindex",       VSHORT|VCANTSET,      (INTBIG*)&db_ptnp.primindex},
	{"primname",        VSTRING,              (INTBIG*)&db_ptnp.primname},
	{"revisiondate",    VINTEGER|VCANTSET,    (INTBIG*)&db_ptnp.revisiondate},
	{"rtree",           VRTNODE|VCANTSET,     (INTBIG*)&db_ptnp.rtree},
	{"tech",            VTECHNOLOGY|VCANTSET, (INTBIG*)&db_ptnp.tech},
	{"temp1",           VINTEGER,             (INTBIG*)&db_ptnp.temp1},
	{"temp2",           VINTEGER,             (INTBIG*)&db_ptnp.temp2},
	{"userbits",        VINTEGER,             (INTBIG*)&db_ptnp.userbits},
	{"version",         VSHORT|VCANTSET,      (INTBIG*)&db_ptnp.version},
	{NULL, 0, NULL}  /* 0 */
};
static PORTARCINST db_ptpi;
static NAMEVARS db_portavars[] =
{
	{"conarcinst",      VARCINST|VCANTSET,    (INTBIG*)&db_ptpi.conarcinst},
	{"nextportarcinst", VPORTARCINST|VCANTSET,(INTBIG*)&db_ptpi.nextportarcinst},
	{"proto",           VPORTPROTO|VCANTSET,  (INTBIG*)&db_ptpi.proto},
	{NULL, 0, NULL}  /* 0 */
};
static PORTEXPINST db_ptpe;
static NAMEVARS db_portevars[] =
{
	{"exportproto",     VPORTPROTO|VCANTSET,  (INTBIG*)&db_ptpe.exportproto},
	{"nextportexpinst", VPORTEXPINST|VCANTSET,(INTBIG*)&db_ptpe.nextportexpinst},
	{"proto",           VPORTPROTO|VCANTSET,  (INTBIG*)&db_ptpe.proto},
	{NULL, 0, NULL}  /* 0 */
};

static PORTPROTO db_ptpp;
static NAMEVARS db_portprotovars[] =
{
	{"connects",        VARCPROTO|VISARRAY|VCANTSET, (INTBIG*)&db_ptpp.connects},
	{"network",         VNETWORK|VCANTSET,           (INTBIG*)&db_ptpp.network},
	{"nextportproto",   VPORTPROTO|VCANTSET,         (INTBIG*)&db_ptpp.nextportproto},
	{"parent",          VNODEPROTO|VCANTSET,         (INTBIG*)&db_ptpp.parent},
	{"protoname",       VSTRING,                     (INTBIG*)&db_ptpp.protoname},
	{"subnodeinst",     VNODEINST|VCANTSET,          (INTBIG*)&db_ptpp.subnodeinst},
	{"subportexpinst",  VPORTEXPINST|VCANTSET,       (INTBIG*)&db_ptpp.subportexpinst},
	{"subportproto",    VPORTPROTO|VCANTSET,         (INTBIG*)&db_ptpp.subportproto},
	{"temp1",           VINTEGER,                    (INTBIG*)&db_ptpp.temp1},
	{"temp2",           VINTEGER,                    (INTBIG*)&db_ptpp.temp2},
	{"textdescript",    VINTEGER,                    (INTBIG*)&db_ptpp.textdescript},
	{"userbits",        VINTEGER,                    (INTBIG*)&db_ptpp.userbits},
	{NULL, 0, NULL}  /* 0 */
};

static ARCINST db_ptai;
static NAMEVARS db_arcvars[] =
{
	{"endshrink",      VINTEGER|VCANTSET,    (INTBIG*)&db_ptai.endshrink},
	{"geom",           VGEOM|VCANTSET,       (INTBIG*)&db_ptai.geom},
	{"lastarcinst",    VARCINST|VCANTSET,    (INTBIG*)&db_ptai.lastarcinst},
	{"length",         VINTEGER|VCANTSET,    (INTBIG*)&db_ptai.length},
	{"network",        VNETWORK|VCANTSET,    (INTBIG*)&db_ptai.network},
	{"nextarcinst",    VARCINST|VCANTSET,    (INTBIG*)&db_ptai.nextarcinst},
	{"nodeinst1",      VNODEINST|VCANTSET,   (INTBIG*)&db_ptai.end[0].nodeinst},
	{"nodeinst2",      VNODEINST|VCANTSET,   (INTBIG*)&db_ptai.end[1].nodeinst},
	{"parent",         VNODEPROTO|VCANTSET,  (INTBIG*)&db_ptai.parent},
	{"portarcinst1",   VPORTARCINST|VCANTSET,(INTBIG*)&db_ptai.end[0].portarcinst},
	{"portarcinst2",   VPORTARCINST|VCANTSET,(INTBIG*)&db_ptai.end[1].portarcinst},
	{"proto",          VARCPROTO|VCANTSET,   (INTBIG*)&db_ptai.proto},
	{"temp1",          VINTEGER,             (INTBIG*)&db_ptai.temp1},
	{"temp2",          VINTEGER,             (INTBIG*)&db_ptai.temp2},
	{"userbits",       VINTEGER,             (INTBIG*)&db_ptai.userbits},
	{"width",          VINTEGER|VCANTSET,    (INTBIG*)&db_ptai.width},
	{"xpos1",          VINTEGER|VCANTSET,    (INTBIG*)&db_ptai.end[0].xpos},
	{"xpos2",          VINTEGER|VCANTSET,    (INTBIG*)&db_ptai.end[1].xpos},
	{"ypos1",          VINTEGER|VCANTSET,    (INTBIG*)&db_ptai.end[0].ypos},
	{"ypos2",          VINTEGER|VCANTSET,    (INTBIG*)&db_ptai.end[1].ypos},
	{NULL, 0, NULL}  /* 0 */
};

static ARCPROTO db_ptap;
static NAMEVARS db_arcprotovars[] =
{
	{"arcindex",        VSHORT|VCANTSET,      (INTBIG*)&db_ptap.arcindex},
	{"nextarcproto",    VARCPROTO|VCANTSET,   (INTBIG*)&db_ptap.nextarcproto},
	{"nominalwidth",    VINTEGER,             (INTBIG*)&db_ptap.nominalwidth},
	{"protoname",       VSTRING,              (INTBIG*)&db_ptap.protoname},
	{"tech",            VTECHNOLOGY|VCANTSET, (INTBIG*)&db_ptap.tech},
	{"temp1",           VINTEGER,             (INTBIG*)&db_ptap.temp1},
	{"temp2",           VINTEGER,             (INTBIG*)&db_ptap.temp2},
	{"userbits",        VINTEGER,             (INTBIG*)&db_ptap.userbits},
	{NULL, 0, NULL}  /* 0 */
};

static GEOM db_ptgeom;
static NAMEVARS db_geomvars[] =
{
	{"entrytype",       VSHORT|VCANTSET,      (INTBIG*)&db_ptgeom.entrytype},
	{"entryaddr",       VINTEGER|VCANTSET,    (INTBIG*)&db_ptgeom.entryaddr},
	{"highx",           VINTEGER|VCANTSET,    (INTBIG*)&db_ptgeom.highx},
	{"highy",           VINTEGER|VCANTSET,    (INTBIG*)&db_ptgeom.highy},
	{"lowx",            VINTEGER|VCANTSET,    (INTBIG*)&db_ptgeom.lowx},
	{"lowy",            VINTEGER|VCANTSET,    (INTBIG*)&db_ptgeom.lowy},
	{NULL, 0, NULL}  /* 0 */
};

static LIBRARY db_ptlib;
static NAMEVARS db_libvars[] =
{
	{"curnodeproto",    VNODEPROTO,                 (INTBIG*)&db_ptlib.curnodeproto},
	{"firstcell",       VCELL|VCANTSET,             (INTBIG*)&db_ptlib.firstcell},
	{"firstnodeproto",  VNODEPROTO|VCANTSET,        (INTBIG*)&db_ptlib.firstnodeproto},
	{"lambda",          VINTEGER|VISARRAY|VCANTSET, (INTBIG*)&db_ptlib.lambda},
	{"libname",         VSTRING,                    (INTBIG*)&db_ptlib.libname},
	{"libfile",         VSTRING,                    (INTBIG*)&db_ptlib.libfile},
	{"nextlibrary",     VLIBRARY|VCANTSET,          (INTBIG*)&db_ptlib.nextlibrary},
	{"temp1",           VINTEGER,                   (INTBIG*)&db_ptlib.temp1},
	{"temp2",           VINTEGER,                   (INTBIG*)&db_ptlib.temp2},
	{"userbits",        VINTEGER,                   (INTBIG*)&db_ptlib.userbits},
	{NULL, 0, NULL}  /* 0 */
};

/*
 * entry 6 of the table below ("layers") gets changed by "db_inittechlist"
 */
static TECHNOLOGY db_pttech;
static NAMEVARS db_techvars[] =
{
	{"arcprotocount",     VSHORT|VCANTSET,             (INTBIG*)&db_pttech.arcprotocount},
	{"deflambda",         VINTEGER|VCANTSET,           (INTBIG*)&db_pttech.deflambda},
	{"firstarcproto",     VARCPROTO|VCANTSET,          (INTBIG*)&db_pttech.firstarcproto},
	{"firstnodeproto",    VNODEPROTO|VCANTSET,         (INTBIG*)&db_pttech.firstnodeproto},
	{"layercount",        VSHORT|VCANTSET,             (INTBIG*)&db_pttech.layercount},
	{"layers",            VGRAPHICS|VISARRAY|VCANTSET, (INTBIG*)&db_pttech.layers},
	{"nexttechnology",    VTECHNOLOGY|VCANTSET,        (INTBIG*)&db_pttech.nexttechnology},
	{"nodeprotocount",    VSHORT|VCANTSET,             (INTBIG*)&db_pttech.nodeprotocount},
	{"techdescript",      VSTRING|VCANTSET,            (INTBIG*)&db_pttech.techdescript},
	{"techindex",         VSHORT|VCANTSET,             (INTBIG*)&db_pttech.techindex},
	{"techname",          VSTRING,                     (INTBIG*)&db_pttech.techname},
	{"temp1",             VINTEGER,                    (INTBIG*)&db_pttech.temp1},
	{"temp2",             VINTEGER,                    (INTBIG*)&db_pttech.temp2},
	{"userbits",          VINTEGER,                    (INTBIG*)&db_pttech.userbits},
	{NULL, 0, NULL}  /* 0 */
};

static AIDENTRY db_ptaid;
static NAMEVARS db_aidvars[] =
{
	{"aidindex",        VSHORT|VCANTSET,      (INTBIG*)&db_ptaid.aidindex},
	{"aidname",         VSTRING|VCANTSET,     (INTBIG*)&db_ptaid.aidname},
	{"aidstate",        VINTEGER,             (INTBIG*)&db_ptaid.aidstate},
	{NULL, 0, NULL}  /* 0 */
};

/*
 * entry 6 of the table below ("pointers") gets changed by "db_initrtnodelist"
 */
static RTNODE db_ptrtn;
static NAMEVARS db_rtnvars[] =
{
	{"flag",            VSHORT|VCANTSET,                 (INTBIG*)&db_ptrtn.flag},
	{"highx",           VINTEGER|VCANTSET,               (INTBIG*)&db_ptrtn.highx},
	{"highy",           VINTEGER|VCANTSET,               (INTBIG*)&db_ptrtn.highy},
	{"lowx",            VINTEGER|VCANTSET,               (INTBIG*)&db_ptrtn.lowx},
	{"lowy",            VINTEGER|VCANTSET,               (INTBIG*)&db_ptrtn.lowy},
	{"parent",          VRTNODE|VCANTSET,                (INTBIG*)&db_ptrtn.parent},
	{"pointers",        VRTNODE|VISARRAY|VCREF|VCANTSET, (INTBIG*)db_ptrtn.pointers},
	{"total",           VSHORT|VCANTSET,                 (INTBIG*)&db_ptrtn.total},
	{NULL, 0, NULL}  /* 0 */
};

/*
 * entry 0 of the table below ("arcaddr") gets changed by "db_initnetworklist"
 * entry 6 of the table below ("networklist") also gets changed
 */
static NETWORK db_ptnet;
static NAMEVARS db_netvars[] =
{
	{"arcaddr",         VARCINST|VISARRAY|VCANTSET,  (INTBIG*)&db_ptnet.arcaddr},
	{"arccount",        VSHORT|VCANTSET,             (INTBIG*)&db_ptnet.arccount},
	{"buslinkcount",    VSHORT|VCANTSET,             (INTBIG*)&db_ptnet.buslinkcount},
	{"lastnetwork",     VNETWORK|VCANTSET,           (INTBIG*)&db_ptnet.lastnetwork},
	{"namecount",       VSHORT|VCANTSET,             (INTBIG*)&db_ptnet.namecount},
	{"netname",         VSTRING|VCANTSET,            (INTBIG*)&db_ptnet.netname},
	{"networklist",     VNETWORK|VISARRAY|VCANTSET,  (INTBIG*)&db_ptnet.networklist},
	{"nextnetwork",     VNETWORK|VCANTSET,           (INTBIG*)&db_ptnet.nextnetwork},
	{"parent",          VNODEPROTO|VCANTSET,         (INTBIG*)&db_ptnet.parent},
	{"portcount",       VSHORT|VCANTSET,             (INTBIG*)&db_ptnet.portcount},
	{"refcount" ,       VSHORT|VCANTSET,             (INTBIG*)&db_ptnet.refcount},
	{"signals",         VSHORT|VCANTSET,             (INTBIG*)&db_ptnet.signals},
	{"temp1",           VINTEGER,                    (INTBIG*)&db_ptnet.temp1},
	{"temp2",           VINTEGER,                    (INTBIG*)&db_ptnet.temp2},
	{NULL, 0, NULL}  /* 0 */
};

static CELL db_ptcell;
static NAMEVARS db_cellvars[] =
{
	{"cellname",        VSTRING,              (INTBIG*)&db_ptcell.cellname},
	{"firstincell",     VNODEPROTO|VCANTSET,  (INTBIG*)&db_ptcell.firstincell},
	{"freenetwork",     VNETWORK|VCANTSET,    (INTBIG*)&db_ptcell.freenetwork},
	{"lib",             VLIBRARY|VCANTSET,    (INTBIG*)&db_ptcell.lib},
	{"nextcell",        VCELL|VCANTSET,       (INTBIG*)&db_ptcell.nextcell},
	{"temp1",           VINTEGER,             (INTBIG*)&db_ptcell.temp1},
	{"temp2",           VINTEGER,             (INTBIG*)&db_ptcell.temp2},
	{NULL, 0, NULL}  /* 0 */
};

static VIEW db_ptview;
static NAMEVARS db_viewvars[] =
{
	{"nextview",        VVIEW|VCANTSET,       (INTBIG*)&db_ptview.nextview},
	{"sviewname",       VSTRING|VCANTSET,     (INTBIG*)&db_ptview.sviewname},
	{"temp1",           VINTEGER,             (INTBIG*)&db_ptview.temp1},
	{"temp2",           VINTEGER,             (INTBIG*)&db_ptview.temp2},
	{"viewname",        VSTRING|VCANTSET,     (INTBIG*)&db_ptview.viewname},
	{"viewstate",       VINTEGER|VCANTSET,    (INTBIG*)&db_ptview.viewstate},
	{NULL, 0, NULL}  /* 0 */
};

static WINDOWPART db_ptwin;
static NAMEVARS db_winvars[] =
{
	{"buttonhandler",   VADDRESS,             (INTBIG*)&db_ptwin.buttonhandler},
	{"changehandler",   VADDRESS,             (INTBIG*)&db_ptwin.changehandler},
	{"charhandler",     VADDRESS,             (INTBIG*)&db_ptwin.charhandler},
	{"curnodeproto",    VNODEPROTO,           (INTBIG*)&db_ptwin.curnodeproto},
	{"editor",          VADDRESS,             (INTBIG*)&db_ptwin.editor},
	{"frame",           VWINDOWFRAME|VCANTSET,(INTBIG*)&db_ptwin.frame},
	{"framehx",         VSHORT,               (INTBIG*)&db_ptwin.framehx},
	{"framehy",         VSHORT,               (INTBIG*)&db_ptwin.framehy},
	{"framelx",         VSHORT,               (INTBIG*)&db_ptwin.framelx},
	{"framely",         VSHORT,               (INTBIG*)&db_ptwin.framely},
	{"gridx",           VINTEGER,             (INTBIG*)&db_ptwin.gridx},
	{"gridy",           VINTEGER,             (INTBIG*)&db_ptwin.gridy},
	{"hratio",          VSHORT,               (INTBIG*)&db_ptwin.hratio},
	{"lastwindowpart",  VWINDOWPART|VCANTSET, (INTBIG*)&db_ptwin.lastwindowpart},
	{"linkedwindowpart",VWINDOWPART|VCANTSET, (INTBIG*)&db_ptwin.linkedwindowpart},
	{"location",        VSTRING|VCANTSET,     (INTBIG*)&db_ptwin.location},
	{"nextwindowpart",  VWINDOWPART|VCANTSET, (INTBIG*)&db_ptwin.nextwindowpart},
	{"redisphandler",   VADDRESS,             (INTBIG*)&db_ptwin.redisphandler},
	{"scalex",          VFLOAT,               (INTBIG*)&db_ptwin.scalex},
	{"scaley",          VFLOAT,               (INTBIG*)&db_ptwin.scaley},
	{"screenhx",        VINTEGER,             (INTBIG*)&db_ptwin.screenhx},
	{"screenhy",        VINTEGER,             (INTBIG*)&db_ptwin.screenhy},
	{"screenlx",        VINTEGER,             (INTBIG*)&db_ptwin.screenlx},
	{"screenly",        VINTEGER,             (INTBIG*)&db_ptwin.screenly},
	{"state",           VINTEGER,             (INTBIG*)&db_ptwin.state},
	{"termhandler",     VADDRESS,             (INTBIG*)&db_ptwin.termhandler},
	{"thumbhx",         VSHORT,               (INTBIG*)&db_ptwin.thumbhx},
	{"thumbhy",         VSHORT,               (INTBIG*)&db_ptwin.thumbhy},
	{"thumblx",         VSHORT,               (INTBIG*)&db_ptwin.thumblx},
	{"thumbly",         VSHORT,               (INTBIG*)&db_ptwin.thumbly},
	{"usehx",           VSHORT,               (INTBIG*)&db_ptwin.usehx},
	{"usehy",           VSHORT,               (INTBIG*)&db_ptwin.usehy},
	{"uselx",           VSHORT,               (INTBIG*)&db_ptwin.uselx},
	{"usely",           VSHORT,               (INTBIG*)&db_ptwin.usely},
	{"vratio",          VSHORT,               (INTBIG*)&db_ptwin.vratio},
	{NULL, 0, NULL}  /* 0 */
};

static GRAPHICS db_ptgra;
static NAMEVARS db_gravars[] =
{
	{"bits",            VSHORT,                               (INTBIG*)&db_ptgra.bits},
	{"bwstyle",         VSHORT,                               (INTBIG*)&db_ptgra.bwstyle},
	{"col",             VSHORT,                               (INTBIG*)&db_ptgra.col},
	{"colstyle",        VSHORT,                               (INTBIG*)&db_ptgra.colstyle},
	{"raster",          VSHORT|VISARRAY|VCREF|(8<<VLENGTHSH), (INTBIG*)db_ptgra.raster},
	{NULL, 0, NULL}  /* 0 */
};

static CONSTRAINT db_ptcon;
static NAMEVARS db_convars[] =
{
	{"conname",         VSTRING|VCANTSET,     (INTBIG*)&db_ptcon.conname},
	{NULL, 0, NULL}  /* 0 */
};

static WINDOWFRAME db_ptwf;
static NAMEVARS db_wfvars[] =
{
	{"floating",        VSHORT,               (INTBIG*)&db_ptwf.floating},
	{"width",           VSHORT,               (INTBIG*)&db_ptwf.swid},
	{"height",          VSHORT,               (INTBIG*)&db_ptwf.shei},
	{"yreverse",        VSHORT,               (INTBIG*)&db_ptwf.revy},
	{NULL, 0, NULL}  /* 0 */
};

static POLYGON db_ptpoly;
static NAMEVARS db_polyvars[] =
{
	/* the first two entries here cannot be moved (see "db_initpolygonlist()") */
	{"xv",              VINTEGER|VISARRAY|VCANTSET, (INTBIG*)&db_ptpoly.xv},
	{"yv",              VINTEGER|VISARRAY|VCANTSET, (INTBIG*)&db_ptpoly.yv},
	{"limit",           VSHORT|VCANTSET,            (INTBIG*)&db_ptpoly.limit},
	{"count",           VSHORT|VCANTSET,            (INTBIG*)&db_ptpoly.count},
	{"style",           VSHORT,                     (INTBIG*)&db_ptpoly.style},
	{"string",          VSTRING|VCANTSET,           (INTBIG*)&db_ptpoly.string},
	{"font",            VSHORT,                     (INTBIG*)&db_ptpoly.font},
	{"tech",            VTECHNOLOGY,                (INTBIG*)&db_ptpoly.tech},
	{"desc",            VGRAPHICS|VCANTSET,         (INTBIG*)&db_ptpoly.desc},
	{"layer",           VSHORT,                     (INTBIG*)&db_ptpoly.layer},
	{"portproto",       VPORTPROTO,                 (INTBIG*)&db_ptpoly.portproto},
	{"nextpolygon",     VPOLYGON|VCANTSET,          (INTBIG*)&db_ptpoly.nextpolygon},
	{NULL, 0, NULL}  /* 0 */
};

/************************* CODE FOR TYPES *************************/

void db_initnodeinstlist(NODEINST *ni)
{
	if (ni == NONODEINST)
	{
		db_thisapack->state = NULLNAME;
		return;
	}
	db_thisapack->state = PORTANAME;		/* then PORTENAME, SPECNAME, VARNAME */
	db_thisapack->portarcinstname = ni->firstportarcinst;	/* PORTANAME */
	db_thisapack->portexpinstname = ni->firstportexpinst;	/* PORTENAME */
	db_thisapack->vars = db_nodevars;						/* SPECNAME */
	db_thisapack->proto = (INTBIG)&db_ptni;					/* SPECNAME */
	db_thisapack->object = (INTBIG)ni;						/* SPECNAME */
	db_thisapack->specindex = 0;							/* SPECNAME */
	db_thisapack->numvar = &ni->numvar;						/* VARNAME */
	db_thisapack->firstvar = &ni->firstvar;					/* VARNAME */
	db_thisapack->varindex = 0;								/* VARNAME */
}

void db_initnodeprotolist(NODEPROTO *np)
{
	if (np == NONODEPROTO)
	{
		db_thisapack->state = NULLNAME;
		return;
	}
	if (np->primindex == 0)
	{
		db_thisapack->state = ARCINAME;		/* then NODEINAME, PORTCNAME, SPECNAME, VARNAME */
		db_thisapack->arciname  = np->firstarcinst;			/* ARCINAME */
		db_thisapack->nodeiname  = np->firstnodeinst;		/* NODEINAME */
	} else db_thisapack->state = PORTCNAME;	/* then SPECNAME, VARNAME */
	db_thisapack->portprotoname = np->firstportproto;		/* PORTCNAME */
	db_thisapack->vars = db_nodeprotovars;					/* SPECNAME */
	db_thisapack->proto = (INTBIG)&db_ptnp;					/* SPECNAME */
	db_thisapack->object = (INTBIG)np;						/* SPECNAME */
	db_thisapack->specindex = 0;							/* SPECNAME */
	db_thisapack->numvar = &np->numvar;						/* VARNAME */
	db_thisapack->firstvar = &np->firstvar;					/* VARNAME */
	db_thisapack->varindex = 0;								/* VARNAME */
}

void db_initportarcinstlist(PORTARCINST *pi)
{
	if (pi == NOPORTARCINST)
	{
		db_thisapack->state = NULLNAME;
		return;
	}
	db_thisapack->state = SPECNAME;			/* then VARNAME */
	db_thisapack->vars = db_portavars;					/* SPECNAME */
	db_thisapack->proto = (INTBIG)&db_ptpi;				/* SPECNAME */
	db_thisapack->object = (INTBIG)pi;					/* SPECNAME */
	db_thisapack->specindex = 0;						/* SPECNAME */
	db_thisapack->numvar = &pi->numvar;					/* VARNAME */
	db_thisapack->firstvar = &pi->firstvar;				/* VARNAME */
	db_thisapack->varindex = 0;							/* VARNAME */
}

void db_initportexpinstlist(PORTEXPINST *pe)
{
	if (pe == NOPORTEXPINST)
	{
		db_thisapack->state = NULLNAME;
		return;
	}
	db_thisapack->state = SPECNAME;			/* then VARNAME */
	db_thisapack->vars = db_portevars;					/* SPECNAME */
	db_thisapack->proto = (INTBIG)&db_ptpe;				/* SPECNAME */
	db_thisapack->object = (INTBIG)pe;					/* SPECNAME */
	db_thisapack->specindex = 0;						/* SPECNAME */
	db_thisapack->numvar = &pe->numvar;					/* VARNAME */
	db_thisapack->firstvar = &pe->firstvar;				/* VARNAME */
	db_thisapack->varindex = 0;							/* VARNAME */
}

void db_initportprotolist(PORTPROTO *pp)
{
	if (pp == NOPORTPROTO)
	{
		db_thisapack->state = NULLNAME;
		return;
	}
	db_thisapack->state = SPECNAME;			/* then VARNAME */
	db_thisapack->vars = db_portprotovars;				/* SPECNAME */
	db_thisapack->proto = (INTBIG)&db_ptpp;				/* SPECNAME */
	db_thisapack->object = (INTBIG)pp;					/* SPECNAME */
	db_thisapack->specindex = 0;						/* SPECNAME */
	db_thisapack->numvar = &pp->numvar;					/* VARNAME */
	db_thisapack->firstvar = &pp->firstvar;				/* VARNAME */
	db_thisapack->varindex = 0;							/* VARNAME */
}

void db_initarcinstlist(ARCINST *ai)
{
	if (ai == NOARCINST)
	{
		db_thisapack->state = NULLNAME;
		return;
	}
	db_thisapack->state = SPECNAME;			/* then VARNAME */
	db_thisapack->vars = db_arcvars;					/* SPECNAME */
	db_thisapack->proto = (INTBIG)&db_ptai;				/* SPECNAME */
	db_thisapack->object = (INTBIG)ai;					/* SPECNAME */
	db_thisapack->specindex = 0;						/* SPECNAME */
	db_thisapack->numvar = &ai->numvar;					/* VARNAME */
	db_thisapack->firstvar = &ai->firstvar;				/* VARNAME */
	db_thisapack->varindex = 0;							/* VARNAME */
}

void db_initarcprotolist(ARCPROTO *ap)
{
	if (ap == NOARCPROTO)
	{
		db_thisapack->state = NULLNAME;
		return;
	}
	db_thisapack->state = SPECNAME;			/* then VARNAME */
	db_thisapack->vars = db_arcprotovars;				/* SPECNAME */
	db_thisapack->proto = (INTBIG)&db_ptap;				/* SPECNAME */
	db_thisapack->object = (INTBIG)ap;					/* SPECNAME */
	db_thisapack->specindex = 0;						/* SPECNAME */
	db_thisapack->numvar = &ap->numvar;					/* VARNAME */
	db_thisapack->firstvar = &ap->firstvar;				/* VARNAME */
	db_thisapack->varindex = 0;							/* VARNAME */
}

void db_initgeomlist(GEOM *geom)
{
	if (geom == NOGEOM)
	{
		db_thisapack->state = NULLNAME;
		return;
	}
	switch(geom->entrytype)
	{
		case OBJNODEINST: db_geomvars[1].type = VNODEINST|VCANTSET;   break;
		case OBJARCINST:  db_geomvars[1].type = VARCINST|VCANTSET;    break;
	}
	db_thisapack->state = SPECNAME;			/* then VARNAME */
	db_thisapack->vars = db_geomvars;					/* SPECNAME */
	db_thisapack->proto = (INTBIG)&db_ptgeom;			/* SPECNAME */
	db_thisapack->object = (INTBIG)geom;				/* SPECNAME */
	db_thisapack->specindex = 0;						/* SPECNAME */
	db_thisapack->numvar = &geom->numvar;				/* VARNAME */
	db_thisapack->firstvar = &geom->firstvar;			/* VARNAME */
	db_thisapack->varindex = 0;							/* VARNAME */
}

void db_initliblist(LIBRARY *lib)
{
	if (lib == NOLIBRARY)
	{
		db_thisapack->state = NULLNAME;
		return;
	}
	db_thisapack->state = NODENAME;			/* then SPECNAME, VARNAME */
	db_thisapack->nodeprotoname = lib->firstnodeproto;	/* NODENAME */
	db_thisapack->vars = db_libvars;					/* SPECNAME */
	db_thisapack->proto = (INTBIG)&db_ptlib;			/* SPECNAME */
	db_thisapack->object = (INTBIG)lib;					/* SPECNAME */
	db_thisapack->specindex = 0;						/* SPECNAME */
	db_thisapack->numvar = &lib->numvar;				/* VARNAME */
	db_thisapack->firstvar = &lib->firstvar;			/* VARNAME */
	db_thisapack->varindex = 0;							/* VARNAME */
}

void db_inittechlist(TECHNOLOGY *tech)
{
	if (tech == NOTECHNOLOGY)
	{
		db_thisapack->state = NULLNAME;
		return;
	}
	db_techvars[6].type = VGRAPHICS|VISARRAY|(tech->layercount<<VLENGTHSH)|VCANTSET;
	db_thisapack->state = ARCNAME;		/* then NODENAME, SPECNAME, VARNAME */
	db_thisapack->arcprotoname = tech->firstarcproto;	/* ARCNAME */
	db_thisapack->nodeprotoname = tech->firstnodeproto;	/* NODENAME */
	db_thisapack->vars = db_techvars;					/* SPECNAME */
	db_thisapack->proto = (INTBIG)&db_pttech;			/* SPECNAME */
	db_thisapack->object = (INTBIG)tech;				/* SPECNAME */
	db_thisapack->specindex = 0;						/* SPECNAME */
	db_thisapack->numvar = &tech->numvar;				/* VARNAME */
	db_thisapack->firstvar = &tech->firstvar;			/* VARNAME */
	db_thisapack->varindex = 0;							/* VARNAME */
}

void db_initaidlist(AIDENTRY *aid)
{
	if (aid == NOAID)
	{
		db_thisapack->state = NULLNAME;
		return;
	}
	db_thisapack->state = SPECNAME;			/* then VARNAME */
	db_thisapack->vars = db_aidvars;					/* SPECNAME */
	db_thisapack->proto = (INTBIG)&db_ptaid;			/* SPECNAME */
	db_thisapack->object = (INTBIG)aid;					/* SPECNAME */
	db_thisapack->specindex = 0;						/* SPECNAME */
	db_thisapack->numvar = &aid->numvar;				/* VARNAME */
	db_thisapack->firstvar = &aid->firstvar;			/* VARNAME */
	db_thisapack->varindex = 0;							/* VARNAME */
}

void db_initrtnodelist(RTNODE *rtn)
{
	if (rtn == NORTNODE)
	{
		db_thisapack->state = NULLNAME;
		return;
	}
	db_rtnvars[6].type = VISARRAY | (rtn->total<<VLENGTHSH) | VCANTSET | VCREF;
	if (rtn->flag == 0) db_rtnvars[6].type |= VRTNODE; else
		db_rtnvars[6].type |= VGEOM;
	db_thisapack->state = SPECNAME;			/* then VARNAME */
	db_thisapack->vars = db_rtnvars;					/* SPECNAME */
	db_thisapack->proto = (INTBIG)&db_ptrtn;			/* SPECNAME */
	db_thisapack->object = (INTBIG)rtn;					/* SPECNAME */
	db_thisapack->specindex = 0;						/* SPECNAME */
	db_thisapack->numvar = &rtn->numvar;				/* VARNAME */
	db_thisapack->firstvar = &rtn->firstvar;			/* VARNAME */
	db_thisapack->varindex = 0;							/* VARNAME */
}

void db_initnetworklist(NETWORK *net)
{
	if (net == NONETWORK)
	{
		db_thisapack->state = NULLNAME;
		return;
	}
	if (net->arccount < 1) db_netvars[0].type = VADDRESS|VCANTSET; else
		if (net->arccount == 1) db_netvars[0].type = VARCINST|VCANTSET; else
			db_netvars[0].type = VARCINST|VISARRAY|(net->arccount<<VLENGTHSH)|VCANTSET;
	if (net->signals <= 1) db_netvars[6].type = VADDRESS|VCANTSET; else
		db_netvars[6].type = VNETWORK|VISARRAY|(net->signals<<VLENGTHSH)|VCANTSET;
	db_thisapack->state = SPECNAME;			/* then VARNAME */
	db_thisapack->vars = db_netvars;					/* SPECNAME */
	db_thisapack->proto = (INTBIG)&db_ptnet;			/* SPECNAME */
	db_thisapack->object = (INTBIG)net;					/* SPECNAME */
	db_thisapack->specindex = 0;						/* SPECNAME */
	db_thisapack->numvar = &net->numvar;				/* VARNAME */
	db_thisapack->firstvar = &net->firstvar;			/* VARNAME */
	db_thisapack->varindex = 0;							/* VARNAME */
}

void db_initcelllist(CELL *cell)
{
	if (cell == NOCELL)
	{
		db_thisapack->state = NULLNAME;
		return;
	}
	db_thisapack->state = SPECNAME;			/* then VARNAME */
	db_thisapack->vars = db_cellvars;					/* SPECNAME */
	db_thisapack->proto = (INTBIG)&db_ptcell;			/* SPECNAME */
	db_thisapack->object = (INTBIG)cell;				/* SPECNAME */
	db_thisapack->specindex = 0;						/* SPECNAME */
	db_thisapack->numvar = &cell->numvar;				/* VARNAME */
	db_thisapack->firstvar = &cell->firstvar;			/* VARNAME */
	db_thisapack->varindex = 0;							/* VARNAME */
}

void db_initviewlist(VIEW *view)
{
	if (view == NOVIEW)
	{
		db_thisapack->state = NULLNAME;
		return;
	}
	db_thisapack->state = SPECNAME;			/* then VARNAME */
	db_thisapack->vars = db_viewvars;					/* SPECNAME */
	db_thisapack->proto = (INTBIG)&db_ptview;			/* SPECNAME */
	db_thisapack->object = (INTBIG)view;				/* SPECNAME */
	db_thisapack->specindex = 0;						/* SPECNAME */
	db_thisapack->numvar = &view->numvar;				/* VARNAME */
	db_thisapack->firstvar = &view->firstvar;			/* VARNAME */
	db_thisapack->varindex = 0;							/* VARNAME */
}

void db_initwindowlist(WINDOWPART *win)
{
	if (win == NOWINDOWPART)
	{
		db_thisapack->state = NULLNAME;
		return;
	}
	db_thisapack->state = SPECNAME;			/* then VARNAME */
	db_thisapack->vars = db_winvars;					/* SPECNAME */
	db_thisapack->proto = (INTBIG)&db_ptwin;			/* SPECNAME */
	db_thisapack->object = (INTBIG)win;					/* SPECNAME */
	db_thisapack->specindex = 0;						/* SPECNAME */
	db_thisapack->numvar = &win->numvar;				/* VARNAME */
	db_thisapack->firstvar = &win->firstvar;			/* VARNAME */
	db_thisapack->varindex = 0;							/* VARNAME */
}

void db_initgraphicslist(GRAPHICS *gra)
{
	if (gra == NOGRAPHICS)
	{
		db_thisapack->state = NULLNAME;
		return;
	}
	db_thisapack->state = SPECNAME;			/* then VARNAME */
	db_thisapack->vars = db_gravars;					/* SPECNAME */
	db_thisapack->proto = (INTBIG)&db_ptgra;			/* SPECNAME */
	db_thisapack->object = (INTBIG)gra;					/* SPECNAME */
	db_thisapack->specindex = 0;						/* SPECNAME */
	db_thisapack->numvar = &gra->numvar;				/* VARNAME */
	db_thisapack->firstvar = &gra->firstvar;			/* VARNAME */
	db_thisapack->varindex = 0;							/* VARNAME */
}

void db_initconstraintlist(CONSTRAINT *con)
{
	if (con == NOCONSTRAINT)
	{
		db_thisapack->state = NULLNAME;
		return;
	}
	db_thisapack->state = SPECNAME;			/* then VARNAME */
	db_thisapack->vars = db_convars;					/* SPECNAME */
	db_thisapack->proto = (INTBIG)&db_ptcon;			/* SPECNAME */
	db_thisapack->object = (INTBIG)con;					/* SPECNAME */
	db_thisapack->specindex = 0;						/* SPECNAME */
	db_thisapack->numvar = &con->numvar;				/* VARNAME */
	db_thisapack->firstvar = &con->firstvar;			/* VARNAME */
	db_thisapack->varindex = 0;							/* VARNAME */
}

void db_initwindowframelist(WINDOWFRAME *wf)
{
	if (wf == NOWINDOWFRAME)
	{
		db_thisapack->state = NULLNAME;
		return;
	}
	db_thisapack->state = SPECNAME;			/* then VARNAME */
	db_thisapack->vars = db_wfvars;						/* SPECNAME */
	db_thisapack->proto = (INTBIG)&db_ptwf;				/* SPECNAME */
	db_thisapack->object = (INTBIG)wf;					/* SPECNAME */
	db_thisapack->specindex = 0;						/* SPECNAME */
	db_thisapack->numvar = &wf->numvar;					/* VARNAME */
	db_thisapack->firstvar = &wf->firstvar;				/* VARNAME */
	db_thisapack->varindex = 0;							/* VARNAME */
}

void db_initpolygonlist(POLYGON *poly)
{
	if (poly == NOPOLYGON)
	{
		db_thisapack->state = NULLNAME;
		return;
	}
	db_thisapack->state = SPECNAME;			/* then VARNAME */
	db_thisapack->vars = db_polyvars;					/* SPECNAME */
	db_thisapack->proto = (INTBIG)&db_ptpoly;			/* SPECNAME */
	db_thisapack->object = (INTBIG)poly;				/* SPECNAME */
	db_thisapack->specindex = 0;						/* SPECNAME */
	db_thisapack->numvar = &poly->numvar;				/* VARNAME */
	db_thisapack->firstvar = &poly->firstvar;			/* VARNAME */
	db_thisapack->varindex = 0;							/* VARNAME */
	db_polyvars[0].type = VINTEGER|VCANTSET|VISARRAY|(poly->count << VLENGTHSH);
	db_polyvars[1].type = VINTEGER|VCANTSET|VISARRAY|(poly->count << VLENGTHSH);
}

/************************* DISPLAYABLE VARIABLE ROUTINES *************************/

/*
 * this routine is used to determine the screen location of
 * letters in displayable text.  The displayable variable is
 * "var"; it resides on object "addr" of type "type"; uses technology
 * "tech"; and is being displayed in window "win".  The routine
 * calculates the position of line "line" and returns it in (x, y).
 * If "lowleft" is nonzero, the returned position is the lower-left
 * corner of the text, otherwise it is the position of the center of the text.
 */
void getdisparrayvarlinepos(INTBIG addr, INTBIG type, TECHNOLOGY *tech, WINDOWPART *win, VARIABLE *var,
	INTBIG line, INTBIG *x, INTBIG *y, INTSML lowleft)
{
	char *string;
	INTBIG cx, cy, distx, disty;
	INTSML tsx, tsy, style;
	REGISTER INTSML font, tf, saverot, savetrn;
	REGISTER GEOM *geom;
	REGISTER NODEPROTO *np;
	REGISTER PORTPROTO *pp;
	REGISTER INTBIG screen, realscreen, len, lambda;
	REGISTER NODEINST *ni;
	XARRAY trans;

	switch (type)
	{
		case VNODEINST:
			ni = (NODEINST *)addr;
			makerot(ni, trans);
			geom = ni->geom;
			cx = (geom->lowx + geom->highx) / 2;
			cy = (geom->lowy + geom->highy) / 2;
			break;
		case VARCINST:
			geom = ((ARCINST *)addr)->geom;
			cx = (geom->lowx + geom->highx) / 2;
			cy = (geom->lowy + geom->highy) / 2;
			break;
		case VPORTPROTO:
			pp = (PORTPROTO *)addr;
			ni = pp->subnodeinst;
			saverot = ni->rotation;   savetrn = ni->transpose;
			ni->rotation = ni->transpose = 0;
			portposition(pp->subnodeinst, pp->subportproto, &cx, &cy);
			ni->rotation = saverot;   ni->transpose = savetrn;
			ni = pp->subnodeinst;
			makerot(ni, trans);
			break;
		case VNODEPROTO:
			np = (NODEPROTO *)addr;
			cx = (np->lowx + np->highx) / 2;
			cy = (np->lowy + np->highy) / 2;
			break;
	}
	if (lowleft != 0)
	{
		distx = (var->textdescript&VTXOFF) >> VTXOFFSH;
		if ((var->textdescript&VTXOFFNEG) != 0) distx = -distx;
		lambda = el_curlib->lambda[tech->techindex];
		distx = distx * lambda / 4;
		disty = (var->textdescript&VTYOFF) >> VTYOFFSH;
		if ((var->textdescript&VTYOFFNEG) != 0) disty = -disty;
		disty = disty * lambda / 4;
		cx += distx;   cy += disty;
		if (type == VNODEINST || type == VPORTPROTO)
			xform(cx, cy, &cx, &cy, trans);
	}

	string = describevariable(var, (INTSML)line, -1);
	font = (INTSML)((var->textdescript & VTSIZE) >> VTSIZESH);
	tf = truefontsize(font, win, tech);
	screensettextsize(win, tf);
	if (*string != 0) screengettextsize(win, string, &tsx, &tsy); else
	{
		screengettextsize(win, "X", &tsx, &tsy);
		tsx = 0;
	}

	switch (var->textdescript&VTPOSITION)
	{
		case VTPOSCENT:      style = TEXTCENT;      break;
		case VTPOSBOXED:     style = TEXTBOX;       break;
		case VTPOSUP:        style = TEXTBOT;       break;
		case VTPOSDOWN:      style = TEXTTOP;       break;
		case VTPOSLEFT:      style = TEXTRIGHT;     break;
		case VTPOSRIGHT:     style = TEXTLEFT;      break;
		case VTPOSUPLEFT:    style = TEXTBOTRIGHT;  break;
		case VTPOSUPRIGHT:   style = TEXTBOTLEFT;   break;
		case VTPOSDOWNLEFT:  style = TEXTTOPRIGHT;  break;
		case VTPOSDOWNRIGHT: style = TEXTTOPLEFT;   break;
	}
	if (type == VNODEINST || type == VPORTPROTO)
		style = rotatelabel(style, trans);

	screen = applyyscale(win, cy - win->screenly) + win->usely;
	if ((var->type&VISARRAY) == 0) len = 1; else
		len = getlength(var);
	switch (style)
	{
		case TEXTBOX:
		case TEXTCENT:				/* text is centered about grab point */
		case TEXTLEFT:				/* text is to right of grab point */
		case TEXTRIGHT:				/* text is to left of grab point */
			if (lowleft != 0) realscreen = screen - ((line+1)*tsy - len*tsy/2); else
				realscreen = screen - (line*tsy - len*tsy/2) - tsy/2;
			break;
		case TEXTBOT:				/* text is above grab point */
		case TEXTBOTRIGHT:			/* text is to upper-left of grab point */
		case TEXTBOTLEFT:			/* text is to upper-right of grab point */
			realscreen = screen + (len-line-1) * tsy;
			break;
		case TEXTTOP:				/* text is below grab point */
		case TEXTTOPRIGHT:			/* text is to lower-left of grab point */
		case TEXTTOPLEFT:		/* text is to lower-right of grab point */
			if (lowleft != 0) realscreen = screen - (line+1) * tsy; else
				realscreen = screen - line * tsy;
			break;
	}
	*y = (INTBIG)((realscreen - win->usely) / win->scaley) + win->screenly;

	realscreen = screen = applyxscale(win, cx - win->screenlx) + win->uselx;
	switch (style)
	{
		case TEXTBOX:
		case TEXTCENT:				/* text is centered about grab point */
		case TEXTBOT:				/* text is above grab point */
		case TEXTTOP:				/* text is below grab point */
			if (lowleft != 0) realscreen = screen - tsx/2;
			break;
		case TEXTRIGHT:				/* text is to left of grab point */
		case TEXTBOTRIGHT:			/* text is to upper-left of grab point */
		case TEXTTOPRIGHT:			/* text is to lower-left of grab point */
			if (lowleft != 0) realscreen = screen - tsx;
			break;
	}
	*x = (INTBIG)((realscreen - win->uselx) / win->scalex) + win->screenlx;
}

/*
 * this routine fills polygon poly with an outline of a displayable
 * variable's text.  The displayable variable is "var", it resides
 * on object "geom", and is being displayed in window "win".
 * It is used to draw variable highlighting.
 */
void makedisparrayvarpoly(GEOM *geom, WINDOWPART *win, VARIABLE *var, POLYGON *poly)
{
	INTBIG centerobjx, centerobjy, realobjwidth, realobjheight, len, j,
		screenwidth, screenheight, lx, hx, ly, hy;
	REGISTER INTBIG distx, disty, lambda;
	REGISTER TECHNOLOGY *tech;
	INTSML tsx, tsy, font, i, lineheight;
	char *string;

	if (poly->limit < 4) (void)extendpolygon(poly, 4);
	boundobj(geom, &lx, &hx, &ly, &hy);
	distx = (var->textdescript&VTXOFF) >> VTXOFFSH;
	if ((var->textdescript&VTXOFFNEG) != 0) distx = -distx;
	lambda = figurelambda(geom);
	distx = distx * lambda / 4;
	disty = (var->textdescript&VTYOFF) >> VTYOFFSH;
	if ((var->textdescript&VTYOFFNEG) != 0) disty = -disty;
	disty = disty * lambda / 4;
	centerobjx = (lx + hx) / 2 + distx;
	centerobjy = (ly + hy) / 2 + disty;
	font = (INTSML)((var->textdescript & VTSIZE) >> VTSIZESH);
	if (geom->entrytype == OBJARCINST) tech = geom->entryaddr.ai->proto->tech; else
		tech = geom->entryaddr.ni->proto->tech;
	i = truefontsize(font, win, tech);
	screensettextsize(win, i);
	screengettextsize(win, "X", &tsx, &lineheight);
	if ((var->type&VISARRAY) == 0) len = 1; else
		len = getlength(var);
	screenwidth = 0;
	for(j=0; j<len; j++)
	{
		string = describevariable(var, (INTSML)j, -1);
		screengettextsize(win, string, &tsx, &tsy);
		if (tsx > screenwidth) screenwidth = tsx;
	}
	screenheight = lineheight * len;
	realobjwidth = (INTBIG)(screenwidth / win->scalex);
	realobjheight = (INTBIG)(screenheight / win->scaley);

	/* set X position */
	switch (var->textdescript&VTPOSITION)
	{
		case VTPOSCENT:				/* text is centered about grab point */
		case VTPOSUP:				/* text is above grab point */
		case VTPOSDOWN:				/* text is below grab point */
			poly->xv[0] = poly->xv[1] = centerobjx - realobjwidth/2;
			poly->xv[2] = poly->xv[3] = centerobjx + realobjwidth/2;
			break;
		case VTPOSRIGHT:			/* text is to right of grab point */
		case VTPOSUPRIGHT:			/* text is to upper-right of grab point */
		case VTPOSDOWNRIGHT:		/* text is to lower-right of grab point */
			poly->xv[0] = poly->xv[1] = lx;
			poly->xv[2] = poly->xv[3] = lx + realobjwidth;
			break;
		case VTPOSLEFT:				/* text is to left of grab point */
		case VTPOSUPLEFT:			/* text is to upper-left of grab point */
		case VTPOSDOWNLEFT:			/* text is to lower-left of grab point */
			poly->xv[0] = poly->xv[1] = hx - realobjwidth;
			poly->xv[2] = poly->xv[3] = hx;
			break;
	}

	/* set Y position */
	switch (var->textdescript&VTPOSITION)
	{
		case VTPOSCENT:				/* text is centered about grab point */
		case VTPOSRIGHT:			/* text is to right of grab point */
		case VTPOSLEFT:				/* text is to left of grab point */
			poly->yv[0] = poly->yv[3] = centerobjy - realobjheight/2;
			poly->yv[1] = poly->yv[2] = centerobjy + realobjheight/2;
			break;
		case VTPOSUP:				/* text is above grab point */
		case VTPOSUPLEFT:			/* text is to upper-left of grab point */
		case VTPOSUPRIGHT:			/* text is to upper-right of grab point */
			poly->yv[0] = poly->yv[3] = hy;
			poly->yv[1] = poly->yv[2] = hy + realobjheight;
			break;
		case VTPOSDOWN:				/* text is below grab point */
		case VTPOSDOWNLEFT:			/* text is to lower-left of grab point */
		case VTPOSDOWNRIGHT:		/* text is to lower-right of grab point */
			poly->yv[0] = poly->yv[3] = ly - realobjheight;
			poly->yv[1] = poly->yv[2] = ly;
			break;
	}
	poly->count = 4;
}

/*
 * routine to return the name of the variable whose key is "key" and whose
 * type is "type"
 */
char *changedvariablename(INTBIG type, INTBIG key, INTBIG subtype)
{
	if ((subtype&VCREF) == 0)
	{
		if (key == -1) return(_("NULL"));
		return(makename(key));
	}

	switch (type&VTYPE)
	{
		case VNODEINST:    return(db_nodevars[key].name);
		case VNODEPROTO:   return(db_nodeprotovars[key].name);
		case VPORTARCINST: return(db_portavars[key].name);
		case VPORTEXPINST: return(db_portevars[key].name);
		case VPORTPROTO:   return(db_portprotovars[key].name);
		case VARCINST:     return(db_arcvars[key].name);
		case VARCPROTO:    return(db_arcprotovars[key].name);
		case VGEOM:        return(db_geomvars[key].name);
		case VRTNODE:      return(db_rtnvars[key].name);
		case VLIBRARY:     return(db_libvars[key].name);
		case VTECHNOLOGY:  return(db_techvars[key].name);
		case VAID:         return(db_aidvars[key].name);
		case VVIEW:        return(db_viewvars[key].name);
		case VCELL:        return(db_cellvars[key].name);
		case VNETWORK:     return(db_netvars[key].name);
		case VWINDOWPART:  return(db_winvars[key].name);
		case VGRAPHICS:    return(db_gravars[key].name);
		case VCONSTRAINT:  return(db_convars[key].name);
		case VWINDOWFRAME: return(db_wfvars[key].name);
	}
	return(_("NULL"));
}

/*
 * routine to adjust the coordinate values in "poly" to account for the
 * display offset in the type field "textdescription".  Assumes that the
 * initial values are the center of the object.  The object on which this
 * text resides is in "geom".  Routine also sets the style and the font of
 * text message to use with this coordinate.
 */
void adjustdisoffset(INTBIG addr, INTBIG type, TECHNOLOGY *tech,
	POLYGON *poly, INTBIG textdescription)
{
	REGISTER INTBIG lambda;
	INTBIG distx, disty, plx, phx, ply, phy;
	REGISTER INTSML i;
	REGISTER NODEINST *ni;
	XARRAY trans;

	lambda = el_curlib->lambda[tech->techindex];
	switch (type&VTYPE)
	{
		case VNODEINST: lambda = lambdaofnode((NODEINST *)addr);   break;
		case VARCINST:  lambda = lambdaofarc((ARCINST *)addr);     break;
	}
	distx = (textdescription&VTXOFF) >> VTXOFFSH;
	if ((textdescription&VTXOFFNEG) != 0) distx = -distx;
	distx = distx * lambda / 4;
	disty = (textdescription&VTYOFF) >> VTYOFFSH;
	if ((textdescription&VTYOFFNEG) != 0) disty = -disty;
	disty = disty * lambda / 4;
	if ((textdescription&VTPOSITION) == VTPOSBOXED)
	{
		getbbox(poly, &plx, &phx, &ply, &phy);
		if (distx > 0) plx += distx*2; else
			if (distx < 0) phx += distx*2;
		if (disty > 0) ply += disty*2; else
			if (disty < 0) phy += disty*2;
		makerectpoly(plx, phx, ply, phy, poly);
	} else
	{
		/* just shift the polygon */
		for(i=0; i<poly->count; i++)
		{
			poly->xv[i] += distx;
			poly->yv[i] += disty;
		}
	}

	/* determine the text message style */
	switch (textdescription&VTPOSITION)
	{
		case VTPOSCENT:      poly->style = TEXTCENT;      break;
		case VTPOSBOXED:     poly->style = TEXTBOX;       break;
		case VTPOSUP:        poly->style = TEXTBOT;       break;
		case VTPOSDOWN:      poly->style = TEXTTOP;       break;
		case VTPOSLEFT:      poly->style = TEXTRIGHT;     break;
		case VTPOSRIGHT:     poly->style = TEXTLEFT;      break;
		case VTPOSUPLEFT:    poly->style = TEXTBOTRIGHT;  break;
		case VTPOSUPRIGHT:   poly->style = TEXTBOTLEFT;   break;
		case VTPOSDOWNLEFT:  poly->style = TEXTTOPRIGHT;  break;
		case VTPOSDOWNRIGHT: poly->style = TEXTTOPLEFT;   break;
	}
	if (type == VNODEINST)
	{
		ni = (NODEINST *)addr;
		makeangle(ni->rotation, ni->transpose, trans);
		poly->style = rotatelabel(poly->style, trans);
	}
	if (type == VPORTPROTO)
	{
		ni = ((PORTPROTO *)addr)->subnodeinst;
		makeangle(ni->rotation, ni->transpose, trans);
		poly->style = rotatelabel(poly->style, trans);
	}
	poly->font = (textdescription & VTSIZE) >> VTSIZESH;
	poly->tech = tech;
}

/************************* HELPER ROUTINES *************************/

/*
 * routine to convert a variable name in "name" to a key and return it.
 * If the name is not in the database, the routine returns a negative value
 * that is one less than the negative table index entry.
 */
INTSML db_getkey(char *name)
{
	REGISTER INTSML hi, lo, med, lastmed, i;

	/* binary search name space for the variable name */
	i = lo = 0;   hi = lastmed = el_numnames;
	for(;;)
	{
		/* find mid-point: quit if done */
		med = (hi + lo) / 2;
		if (med == lastmed) break;
		lastmed = med;

		/* test the entry: return the key if a match */
		i = namesame(name, el_namespace[med]);
		if (i == 0) return(med);

		/* decide which way to search in list */
		if (i < 0) hi = med; else lo = med;
	}

	/* create a new position: adjust for position location */
	if (i > 0) med++;
	return(-med-1);
}

/*
 * routine to evaluate variable "var" and replace it with its true value if it is code
 */
VARIABLE *db_evalvar(VARIABLE *var)
{
	REGISTER INTBIG language;
	static VARIABLE retvar;

	if (var == NOVARIABLE) return(var);
	language = var->type & (VCODE1|VCODE2);
	if (language == 0) return(var);
	retvar.key = var->key;
	retvar.type = var->type & ~(VCODE1|VCODE2);
	retvar.textdescript = var->textdescript;
	if (doquerry((char *)var->addr, language, retvar.type, &retvar.addr) != 0)
		return(var);
	return(&retvar);
}

/*
 * routine to determine the appropriate memory cluster to use for the
 * variable whose address is "addr" and type is "type".
 */
CLUSTER *db_whichcluster(INTBIG addr, INTBIG type)
{
	REGISTER GEOM *geom;
	REGISTER NODEPROTO *np;

	switch (type&VTYPE)
	{
		case VNODEINST: return(((NODEINST *)addr)->parent->cell->cluster);
		case VNODEPROTO:
			np = (NODEPROTO *)addr;
			if (np->primindex == 0) return(np->cell->cluster);
			return(np->tech->cluster);
		case VPORTARCINST:
			return(((PORTARCINST *)addr)->conarcinst->parent->cell->cluster);
		case VPORTEXPINST:
			return(((PORTEXPINST *)addr)->exportproto->parent->cell->cluster);
		case VPORTPROTO:
			np = ((PORTPROTO *)addr)->parent;
			if (np->primindex == 0) return(np->cell->cluster);
			return(np->tech->cluster);
		case VARCINST: return(((ARCINST *)addr)->parent->cell->cluster);
		case VARCPROTO: return(((ARCPROTO *)addr)->tech->cluster);
		case VGEOM: geom = (GEOM *)addr;
			if (geom->entrytype == OBJNODEINST)
				return(geom->entryaddr.ni->parent->cell->cluster);
			return(geom->entryaddr.ai->parent->cell->cluster);
		case VTECHNOLOGY: return(((TECHNOLOGY *)addr)->cluster);
		case VAID: return(((AIDENTRY *)addr)->cluster);
		case VNETWORK: return(((NETWORK *)addr)->parent->cell->cluster);
		case VCELL: return(((CELL *)addr)->cluster);
		case VWINDOWPART: return(us_aid->cluster);
		case VWINDOWFRAME: return(us_aid->cluster);
	}
	return(db_cluster);
}

/*
 * routine to determine the appropriate facet associated with the
 * variable whose address is "addr" and type is "type".
 */
NODEPROTO *db_whichnodeproto(INTBIG addr, INTBIG type)
{
	REGISTER GEOM *geom;
	REGISTER NODEPROTO *np;

	switch (type&VTYPE)
	{
		case VNODEINST: return(((NODEINST *)addr)->parent);
		case VNODEPROTO:
			np = (NODEPROTO *)addr;
			if (np->primindex == 0) return(np); else return(NONODEPROTO);
		case VPORTARCINST: return(((PORTARCINST *)addr)->conarcinst->parent);
		case VPORTEXPINST: return(((PORTEXPINST *)addr)->exportproto->parent);
		case VPORTPROTO: return(((PORTPROTO *)addr)->parent);
		case VARCINST: return(((ARCINST *)addr)->parent);
		case VGEOM: geom = (GEOM *)addr;
			if (geom->entrytype == OBJNODEINST) return(geom->entryaddr.ni->parent);
			return(geom->entryaddr.ai->parent);
		case VNETWORK: return(((NETWORK *)addr)->parent);
	}
	return(NONODEPROTO);
}

/*
 * routine to determine the appropriate library associated with the
 * variable whose address is "addr" and type is "type".
 */
LIBRARY *db_whichlibrary(INTBIG addr, INTBIG type)
{
	REGISTER GEOM *geom;
	REGISTER NODEPROTO *np;

	switch (type&VTYPE)
	{
		case VNODEINST: return(((NODEINST *)addr)->parent->cell->lib);
		case VNODEPROTO:
			np = (NODEPROTO *)addr;
			if (np->primindex == 0) return(np->cell->lib);
			return(NOLIBRARY);
		case VPORTARCINST:
			return(((PORTARCINST *)addr)->conarcinst->parent->cell->lib);
		case VPORTEXPINST:
			return(((PORTEXPINST *)addr)->exportproto->parent->cell->lib);
		case VPORTPROTO:
			np = ((PORTPROTO *)addr)->parent;
			if (np->primindex == 0) return(np->cell->lib);
			return(NOLIBRARY);
		case VARCINST: return(((ARCINST *)addr)->parent->cell->lib);
		case VGEOM: geom = (GEOM *)addr;
			if (geom->entrytype == OBJNODEINST)
				return(geom->entryaddr.ni->parent->cell->lib);
			return(geom->entryaddr.ai->parent->cell->lib);
		case VLIBRARY: return((LIBRARY *)addr);
		case VNETWORK: return(((NETWORK *)addr)->parent->cell->lib);
		case VCELL: return(((CELL *)addr)->lib);
	}
	return(NOLIBRARY);
}

/*
 * internal routine to set entry "med" in the list of variables at "*firstvar"
 * with "*numvar" entries (this entry has the key "key").  If "med" is negative
 * then the entry does not exist in the list and must be created.  The entry is
 * set to the address "newaddr" and type "newtype".  Memory is allocated from
 * cluster "cluster".  Returns nonzero if there is an error.
 */
#ifdef DEBUGMEMORY
INTSML db_setvalkey(INTSML *numvar, VARIABLE **firstvar, INTSML med, INTBIG key,
	INTBIG newaddr, INTBIG newtype, CLUSTER *cluster, char *module, INTBIG line)
#else
INTSML db_setvalkey(INTSML *numvar, VARIABLE **firstvar, INTSML med, INTBIG key,
	INTBIG newaddr, INTBIG newtype, CLUSTER *cluster)
#endif
{
	REGISTER VARIABLE *newv, *var;
	REGISTER INTSML i;

	/* if there is no existing variable, create it */
	if (med < 0)
	{
		/* get the entry position in the list */
		med = -med - 1;

		/* allocate space for new list */
		newv = (VARIABLE *)emalloc(((*numvar+1)*(sizeof (VARIABLE))), cluster);
		if (newv == 0) return(1);

		/* copy old list up to the new entry */
		for(i=0; i<med; i++)
		{
			newv[i].key  = (*firstvar)[i].key;
			newv[i].type = (*firstvar)[i].type;
			newv[i].textdescript = (*firstvar)[i].textdescript;
			newv[i].addr = (*firstvar)[i].addr;
		}

		/* add the new entry */
		newv[med].key = key;
		newv[med].type = VUNKNOWN|VDONTSAVE;
		newv[med].textdescript = TXTSMALL << VTSIZESH;
		newv[med].addr = 0;
		var = &newv[med];

		/* copy old list after the new entry */
		for(i = med; i < *numvar; i++)
		{
			newv[i+1].key  = (*firstvar)[i].key;
			newv[i+1].type = (*firstvar)[i].type;
			newv[i+1].textdescript = (*firstvar)[i].textdescript;
			newv[i+1].addr = (*firstvar)[i].addr;
		}

		/* clean-up */
		if (*numvar != 0) efree((char *)*firstvar);
		*firstvar = newv;
		(*numvar)++;
	} else var = &(*firstvar)[med];

	/* set the variable */
#ifdef DEBUGMEMORY
	if (db_setvalvar(var, newaddr, newtype, cluster, module, line) != 0) return(1);
#else
	if (db_setvalvar(var, newaddr, newtype, cluster) != 0) return(1);
#endif

	return(0);
}

/*
 * routine to set the value of the variable "var" to the value "newaddr" and
 * type "newtype".  Memory is allocated from cluster "cluster".  Returns
 * nonzero upon error.
 */
#ifdef DEBUGMEMORY
INTSML db_setvalvar(VARIABLE *var, INTBIG newaddr, INTBIG newtype,
	CLUSTER *cluster, char *module, INTBIG line)
#else
INTSML db_setvalvar(VARIABLE *var, INTBIG newaddr, INTBIG newtype,
	CLUSTER *cluster)
#endif
{
	REGISTER INTBIG i, len;
	REGISTER INTSML datasize;
	INTBIG longval;
	REGISTER char *inaddr;

	/* if the variable or key is not valid then ignore this */
	if (var == NOVARIABLE)
	{
		ttyputerr(_("No valid variable"));
		return(1);
	}
	if ((var->type&VCREF) != 0)
	{
		/* setting a fixed attribute on an object */
		if ((newtype&VISARRAY) != 0)
		{
			ttyputmsg(_("Cannot set array in C structure"));
			return(1);
		}
		if (((INTBIG)(var->type&(VTYPE|VISARRAY))) != (newtype&(VTYPE|VISARRAY)))
		{
			ttyputerr(_("Type mismatch"));
			return(1);
		}
		if (db_setval((char *)&newaddr, (char *)db_realaddress, newtype, cluster) != 0) return(1);
		return(0);
	}

	/* if this change isn't subject to undo, free previous memory now */
	if (db_dovarchangequietly != 0)
	{
		db_freevar(var->addr, var->type);
	}

	/* change the variable type */
	var->type = newtype;

	/* allocate and fill space on the new variables */
	if ((newtype&(VCODE1|VCODE2)) != 0)
	{
		if (db_setval((char *)&newaddr, (char *)&var->addr, VSTRING, cluster) != 0) return(1);
	} else if ((newtype&VISARRAY) != 0)
	{
		var->addr = newaddr;
		len = getlength(var);
		datasize = db_getdatasize(newtype);
		if ((newtype&VLENGTH) == 0)
		{
#ifdef DEBUGMEMORY
			var->addr = (INTBIG)_emalloc((len+1)*datasize, cluster, module, line);
#else
			var->addr = (INTBIG)emalloc((len+1)*datasize, cluster);
#endif
			if (var->addr == 0) return(1);
			for(i=0; i<datasize; i++)
				((char *)var->addr)[len*datasize+i] = -1;
		} else
		{
#ifdef DEBUGMEMORY
			var->addr = (INTBIG)_emalloc(len*datasize, cluster, module, line);
#else
			var->addr = (INTBIG)emalloc(len*datasize, cluster);
#endif
			if (var->addr == 0) return(1);
		}
		for(i=0; i<len; i++)
		{
			if ((newtype&VTYPE) == VSHORT)
			{
				longval = ((INTSML *)newaddr)[i];
				if (db_setval((char *)&longval, &((char *)var->addr)[i*datasize], newtype,
					cluster) != 0) return(1);
			} else
			{
				inaddr = (char *)(newaddr + i*datasize);
				if (db_setval(inaddr, &((char *)var->addr)[i*datasize], newtype, cluster) != 0)
					return(1);
			}
		}
	} else
		if (db_setval((char *)&newaddr, (char *)&var->addr, newtype, cluster)
			!= 0) return(1);

	return(0);
}

/*
 * routine to get entry "aindex" of array variable "var" and place it in "value".
 * Returns nonzero upon error.
 */
INTSML db_getindvar(VARIABLE *var, INTBIG aindex, INTBIG *value)
{
	INTBIG type, len;
	REGISTER INTSML datasize;
	REGISTER void *loc;

	/* if the variable or key is not valid then ignore this */
	if (var == NOVARIABLE) return(1);

	/* if the variable is not an array, quit now */
	type = var->type;
	if ((type&VISARRAY) == 0) return(1);

	/* ensure that the index is within the range of the array */
	len = getlength(var);
	if (aindex < 0) aindex = 0;
	if (aindex >= len) aindex = len - 1;

	/* determine the size of the array entries */
	datasize = db_getdatasize(type);

	/* set the entry */
	if ((type&VCREF) != 0)
	{
		/* get the address of the old array entry (fixed attributes) */
		loc = (void *)(db_realaddress + aindex*datasize);
	} else
	{
		/* get the address of the old array entry (variable attributes) */
		loc = &((char *)var->addr)[aindex*datasize];
	}

	/* set an arbitrary attribute on an object */
	*value = db_assignvalue(loc, datasize);
	return(0);
}

/*
 * routine to set entry "aindex" of array variable "var" (which is on object
 * "objaddr" of type "objtype") to the value "newaddr".  Returns nonzero
 * upon error.
 */
INTSML db_setindvar(INTBIG objaddr, INTBIG objtype, VARIABLE *var, INTBIG aindex,
	INTBIG newaddr)
{
	REGISTER INTSML datasize;
	REGISTER INTBIG len, oldvalue, type;
	REGISTER void *loc;

	/* if the variable or key is not valid then ignore this */
	if (var == NOVARIABLE) return(1);

	/* if the variable is not an array, quit now */
	type = var->type;
	if ((type&VISARRAY) == 0) return(1);

	/* ensure that the index is within the range of the array */
	len = getlength(var);
	if (aindex < 0) aindex = 0;
	if (aindex >= len) aindex = len - 1;

	/* determine the size of the array entries */
	datasize = db_getdatasize(type);

	/* set the entry */
	if ((type&VCREF) != 0)
	{
		/* get the address of the old array entry (fixed attributes) */
		loc = (void *)(db_realaddress + aindex*datasize);
	} else
	{
		/* get the address of the old array entry (variable attributes) */
		loc = &((char *)var->addr)[aindex*datasize];
	}

	/* set an arbitrary attribute on an object */
	oldvalue = db_assignvalue(loc, datasize);
	if (db_setval((char *)&newaddr, (char *)loc, type,
		db_whichcluster(objaddr, objtype)) != 0) return(1);

	if (db_dovarchangequietly == 0)
	{
		/* tell constraint system about modified variable */
		(*el_curconstraint->modifyvariable)(objaddr, objtype, var->key, type, aindex, oldvalue);

		/* mark a change */
		(void)db_change((INTBIG)objaddr, VARIABLEMOD, objtype, var->key, type, aindex, oldvalue, 0);
	}

	return(0);
}

/*
 * routine to insert the value "newaddr" before entry "aindex" of array
 * variable "var" (which is on object "objaddr" of type "objtype").
 * Returns nonzero upon error.
 */
INTSML db_insindvar(INTBIG objaddr, INTBIG objtype, VARIABLE *var, INTBIG aindex,
	INTBIG newaddr)
{
	REGISTER INTBIG i, j, len, truelen, type, *newdata;
	REGISTER INTSML datasize;
	CLUSTER *cluster;

	/* if the variable or key is not valid then ignore this */
	if (var == NOVARIABLE) return(1);

	/* if the variable is not an array, quit now */
	type = var->type;
	if ((type&VISARRAY) == 0) return(1);

	/* ensure that the index is within the range of the array */
	truelen = len = getlength(var);
	if (aindex < 0) aindex = 0;
	if (aindex > len) aindex = len;

	/* if this is a variable-length array, include the terminator */
	if ((var->type&VLENGTH) == 0) len++;

	/* determine the size of the array entries */
	datasize = db_getdatasize(type);

	/* allocate a new array */
	cluster = db_whichcluster(objaddr, objtype);
	newdata = (INTBIG *)emalloc(datasize * (len+1), cluster);
	if (newdata == 0) return(1);

	/* copy, shifting */
	for(i=j=0; i<len; i++)
	{
		if (i == aindex) j++;
		switch (type&VTYPE)
		{
			case VCHAR:
				((char *)newdata)[j] = ((char *)var->addr)[i];
				break;
			case VFLOAT:
				((float *)newdata)[j] = ((float *)var->addr)[i];
				break;
			case VDOUBLE:
				((double *)newdata)[j] = ((double *)var->addr)[i];
				break;
			case VSHORT:
				((INTSML *)newdata)[j] = ((INTSML *)var->addr)[i];
				break;
			default:
				((INTBIG *)newdata)[j] = ((INTBIG *)var->addr)[i];
				break;
		}
		j++;
	}
	if (db_setval((char *)&newaddr, &((char *)newdata)[aindex*datasize], type, cluster) != 0)
		return(1);
	efree((char *)var->addr);
	var->addr = (INTBIG)newdata;

	/* bump the array size */
	if ((var->type&VLENGTH) != 0)
		var->type = (var->type & ~VLENGTH) | ((truelen+1) << VLENGTHSH);

	if (db_dovarchangequietly == 0)
	{
		/* tell constraint system about inserted variable */
		(*el_curconstraint->insertvariable)(objaddr, objtype, var->key, aindex);

		/* mark a change */
		(void)db_change((INTBIG)objaddr, VARIABLEINS, objtype, var->key, type, aindex, 0, 0);
	}

	return(0);
}

/*
 * routine to delete entry "aindex" of array variable "var" (which is on object
 * "objaddr" of type "objtype").  Returns nonzero upon error.
 */
INTSML db_delindvar(INTBIG objaddr, INTBIG objtype, VARIABLE *var, INTBIG aindex)
{
	REGISTER INTBIG i, len, truelen, oldvalue, type;
	REGISTER INTSML datasize;
	REGISTER void *loc;

	/* if the variable or key is not valid then ignore this */
	if (var == NOVARIABLE) return(1);

	/* if the variable is not an array, quit now */
	type = var->type;
	if ((type&VISARRAY) == 0) return(1);

	/* ensure that the index is within the range of the array */
	truelen = len = getlength(var);

	/* can only delete valid line number */
	if (aindex < 0 || aindex >= len) return(1);

	/* cannot delete last entry */
	if (truelen == 1) return(1);

	/* if this is a variable-length array, include the terminator */
	if ((var->type&VLENGTH) == 0) len++;

	/* determine the size of the array entries */
	datasize = db_getdatasize(type);

	/* get the address of the old array entry */
	loc = &((char *)var->addr)[aindex*datasize];
	oldvalue = db_assignvalue(loc, datasize);

	/* shift the data */
	for(i=aindex; i<len-1; i++)
	{
		switch (type&VTYPE)
		{
			case VCHAR:
				((char *)var->addr)[i] = ((char *)var->addr)[i+1];
				break;
			case VFLOAT:
				((float *)var->addr)[i] = ((float *)var->addr)[i+1];
				break;
			case VDOUBLE:
				((double *)var->addr)[i] = ((double *)var->addr)[i+1];
				break;
			case VSHORT:
				((INTSML *)var->addr)[i] = ((INTSML *)var->addr)[i+1];
				break;
			default:
				((INTBIG *)var->addr)[i] = ((INTBIG *)var->addr)[i+1];
				break;
		}
	}

	/* decrement the array size */
	if ((var->type&VLENGTH) != 0)
		var->type = (var->type & ~VLENGTH) | ((truelen-1) << VLENGTHSH);

	if (db_dovarchangequietly == 0)
	{
		/* tell constraint system about deleted variable */
		(*el_curconstraint->deletevariable)(objaddr, objtype, var->key, aindex, oldvalue);

		/* mark a change */
		(void)db_change((INTBIG)objaddr, VARIABLEDEL, objtype, var->key, type, aindex, oldvalue, 0);
	}

	return(0);
}

/*
 * routine to set an individual variable entry at address "to" to the value
 * at address "from".  This type is of type "type" and if allocatable, is
 * filled from cluster "cluster".
 */
INTSML db_setval(char *from, char *to, INTBIG type, CLUSTER *cluster)
{
	/* character sized variable */
	if ((type&VTYPE) == VCHAR)
	{
		*to = *from;
		return(0);
	}

	/* double sized variable */
	if ((type&VTYPE) == VDOUBLE)
	{
		((double *)to)[0] = ((double *)from)[0];
		return(0);
	}

	/* float sized variable */
	if ((type&VTYPE) == VFLOAT)
	{
		((float *)to)[0] = ((float *)from)[0];
		return(0);
	}

	/* short sized variable */
	if ((type&VTYPE) == VSHORT)
	{
		((INTSML *)to)[0] = (INTSML)(((INTBIG *)from)[0]);
		return(0);
	}

	/* integer sized variable */
	if ((type&(VCODE1|VCODE2)) == 0 && (type&VTYPE) != VSTRING)
	{
		((INTBIG *)to)[0] = ((INTBIG *)from)[0];
		return(0);
	}

	/* string or code variable */
	return(allocstring((char **)to, ((char **)from)[0], cluster));
}

/*
 * routine to erase a list of variables when the object is deleted
 */
void db_freevars(VARIABLE **firstvar, INTSML *numvar)
{
	REGISTER INTSML i;
	REGISTER VARIABLE *var;

	if (*numvar == 0) return;
	for(i = 0; i < *numvar; i++)
	{
		var = &(*firstvar)[i];
		db_freevar(var->addr, var->type);
	}
	efree((char *)*firstvar);
	*numvar = 0;
}

/*
 * routine to free any memory allocated to the variable whose address is "addr"
 * and whose type is "type"
 */
void db_freevar(INTBIG addr, INTBIG type)
{
	REGISTER INTBIG i, len;

	/* determine whether individual elements are allocated */
	if ((type&(VCODE1|VCODE2)) != 0 || (type&VTYPE) == VSTRING)
	{
		/* the old variable was allocated */
		if ((type&VISARRAY) != 0)
		{
			len = (type&VLENGTH) >> VLENGTHSH;
			if (len == 0)
			{
				for(i=0; ((INTBIG *)addr)[i] != -1; i++)
					efree((char *)((INTBIG *)addr)[i]);
			} else for(i=0; i<len; i++) efree((char *)((INTBIG *)addr)[i]);
		} else efree((char *)addr);
	}
	if ((type&VISARRAY) != 0) efree((char *)addr);
}

/*
 * routine to do a binary search on the variables in "numvar" and "firstvar"
 * for an entry with the key "key".  If found, its index is returned,
 * otherwise a negative value is returned that, when negated, is the position
 * in the list BEFORE which the key should be inserted (i.e. the value -1
 * means that the key belongs in the first entry and the value -3 means that
 * the key belongs after the second entry).
 */
INTSML db_binarysearch(INTSML numvar, VARIABLE *firstvar, INTBIG key)
{
	REGISTER INTSML hi, lo, med, lastmed;
	REGISTER INTBIG i;

	if (numvar == 0) return(-1);
	lo = 0;   hi = lastmed = numvar;
	for(;;)
	{
		/* find mid-point: quit if done */
		med = (hi + lo) / 2;
		if (med == lastmed) break;
		lastmed = med;

		/* test the entry: return the value if a match */
		i = firstvar[med].key;
		if (i == key) return(med);

		/* decide which way to search in list */
		if (i > key) hi = med; else lo = med;
	}
	if (i < key) med++;
	return(-med-1);
}

void db_renamevar(INTSML numvar, VARIABLE *firstvar, INTBIG oldkey,
	INTBIG newkey)
{
	REGISTER INTSML i, j, k;
	REGISTER INTBIG oldaddr, oldtype, olddescript;
	REGISTER VARIABLE *var;

	if (numvar == 0) return;

	for(i=0; i<numvar; i++)
	{
		var = &firstvar[i];
		if (var->key != oldkey) continue;

		/* save the old information about this variable */
		oldtype = var->type;
		olddescript = var->textdescript;
		oldaddr = var->addr;

		/* compact out the old key */
		for(j=i+1; j<numvar; j++)
		{
			firstvar[j-1].key  = firstvar[j].key;
			firstvar[j-1].type = firstvar[j].type;
			firstvar[j-1].textdescript = firstvar[j].textdescript;
			firstvar[j-1].addr = firstvar[j].addr;
		}

		/* now insert the new key */
		for(j=0; j<numvar-1; j++) if (newkey < firstvar[j].key) break;
		for(k=numvar-1; k>j; k--)
		{
			firstvar[k].key  = firstvar[k-1].key;
			firstvar[k].type = firstvar[k-1].type;
			firstvar[k].textdescript = firstvar[k-1].textdescript;
			firstvar[k].addr = firstvar[k-1].addr;
		}

		/* insert the renamed variable in the right place */
		firstvar[j].key = newkey;
		firstvar[j].type = oldtype;
		firstvar[j].textdescript = olddescript;
		firstvar[j].addr = oldaddr;
	}
}

/*
 * routine to fill the parameters "fir" and "num" with the "firstvar" and
 * "numvar" attributes on object "addr", type "type".  Returns zero if
 * successful, nonzero if the object has no "firstvar/numvar".
 */
INTSML db_getvarptr(INTBIG addr, INTBIG type, VARIABLE ***fir, INTSML **num)
{
	REGISTER NODEINST *ni;
	REGISTER NODEPROTO *np;
	REGISTER PORTARCINST *pi;
	REGISTER PORTEXPINST *pe;
	REGISTER PORTPROTO *pp;
	REGISTER ARCINST *ai;
	REGISTER ARCPROTO *ap;
	REGISTER GEOM *g;
	REGISTER LIBRARY *lib;
	REGISTER TECHNOLOGY *tech;
	REGISTER AIDENTRY *aid;
	REGISTER RTNODE *rtn;
	REGISTER NETWORK *net;
	REGISTER CELL *c;
	REGISTER VIEW *v;

	switch (type&VTYPE)
	{
		case VNODEINST:
			ni = (NODEINST *)addr;
			*fir = &ni->firstvar;    *num = &ni->numvar;    break;
		case VNODEPROTO:
			np = (NODEPROTO *)addr;
			*fir = &np->firstvar;    *num = &np->numvar;    break;
		case VPORTARCINST:
			pi = (PORTARCINST *)addr;
			*fir = &pi->firstvar;    *num = &pi->numvar;    break;
		case VPORTEXPINST:
			pe = (PORTEXPINST *)addr;
			*fir = &pe->firstvar;    *num = &pe->numvar;    break;
		case VPORTPROTO:
			pp = (PORTPROTO *)addr;
			*fir = &pp->firstvar;    *num = &pp->numvar;    break;
		case VARCINST:
			ai = (ARCINST *)addr;
			*fir = &ai->firstvar;    *num = &ai->numvar;    break;
		case VARCPROTO:
			ap = (ARCPROTO *)addr;
			*fir = &ap->firstvar;    *num = &ap->numvar;    break;
		case VGEOM:
			g = (GEOM *)addr;
			*fir = &g->firstvar;     *num = &g->numvar;     break;
		case VLIBRARY:
			lib = (LIBRARY *)addr;
			*fir = &lib->firstvar;   *num = &lib->numvar;   break;
		case VTECHNOLOGY:
			tech = (TECHNOLOGY *)addr;
			*fir = &tech->firstvar;  *num = &tech->numvar;  break;
		case VAID:
			aid = (AIDENTRY *)addr;
			*fir = &aid->firstvar;   *num = &aid->numvar;   break;
		case VRTNODE:
			rtn = (RTNODE *)addr;
			*fir = &rtn->firstvar;   *num = &rtn->numvar;   break;
		case VNETWORK:
			net = (NETWORK *)addr;
			*fir = &net->firstvar;   *num = &net->numvar;   break;
		case VCELL:
			c = (CELL *)addr;
			*fir = &c->firstvar;     *num = &c->numvar;     break;
		case VVIEW:
			v = (VIEW *)addr;
			*fir = &v->firstvar;     *num = &v->numvar;     break;
		default: return(1);
	}
	return(0);
}

INTSML db_getdatasize(INTBIG type)
{
	if ((type&VTYPE) == VCHAR) return(1);
	if ((type&VTYPE) == VSHORT) return(SIZEOFINTSML);
	if ((type&VTYPE) == VFLOAT) return(sizeof(float));
	if ((type&VTYPE) == VDOUBLE) return(sizeof(double));
	return(SIZEOFINTBIG);
}

INTBIG db_assignvalue(void *loc, INTSML datasize)
{
	if (datasize == SIZEOFINTBIG)   return(*((INTBIG *)loc));
	if (datasize == SIZEOFINTSML)   return(*((INTSML *)loc));
	if (datasize == 1)              return(*((char *)loc));
	if (datasize == sizeof(float))  return(castint(*((float *)loc)));
	if (datasize == sizeof(double)) return(castint((float)(*((double *)loc))));
	return(*((INTBIG *)loc));
}

char *db_describetype(INTBIG type)
{
	(void)initinfstr();
	switch (type&VTYPE)
	{
		case VINTEGER:     (void)addstringtoinfstr(_("Integer"));       break;
		case VADDRESS:     (void)addstringtoinfstr(_("Address"));       break;
		case VCHAR:        (void)addstringtoinfstr(_("Character"));     break;
		case VSTRING:      (void)addstringtoinfstr(_("String"));        break;
		case VFLOAT:       (void)addstringtoinfstr(_("Float"));         break;
		case VDOUBLE:      (void)addstringtoinfstr(_("Double"));        break;
		case VNODEINST:    (void)addstringtoinfstr(_("NodeInst"));      break;
		case VNODEPROTO:   (void)addstringtoinfstr(_("NodeProto"));     break;

		case VPORTARCINST: (void)addstringtoinfstr(_("PortArcInst"));   break;
		case VPORTEXPINST: (void)addstringtoinfstr(_("PortExpInst"));   break;
		case VPORTPROTO:   (void)addstringtoinfstr(_("PortProto"));     break;
		case VARCINST:     (void)addstringtoinfstr(_("ArcInst"));       break;
		case VARCPROTO:    (void)addstringtoinfstr(_("ArcProto"));      break;
		case VGEOM:        (void)addstringtoinfstr(_("Geom"));          break;
		case VLIBRARY:     (void)addstringtoinfstr(_("Library"));       break;
		case VTECHNOLOGY:  (void)addstringtoinfstr(_("Technology"));    break;
		case VAID:         (void)addstringtoinfstr(_("Aid"));           break;
		case VRTNODE:      (void)addstringtoinfstr(_("RTNode"));        break;
		case VFRACT:       (void)addstringtoinfstr(_("Fixed-Point"));   break;
		case VNETWORK:     (void)addstringtoinfstr(_("Network"));       break;
		case VCELL:        (void)addstringtoinfstr(_("Cell"));          break;
		case VVIEW:        (void)addstringtoinfstr(_("View"));          break;
		case VWINDOWPART:  (void)addstringtoinfstr(_("Window"));        break;
		case VGRAPHICS:    (void)addstringtoinfstr(_("Graphics"));      break;
		case VSHORT:       (void)addstringtoinfstr(_("Short"));         break;
		case VCONSTRAINT:  (void)addstringtoinfstr(_("Constraint"));    break;
		case VWINDOWFRAME: (void)addstringtoinfstr(_("WindowFrame"));   break;
		case VGENERAL:     (void)addstringtoinfstr(_("General"));       break;
		default:           (void)addstringtoinfstr(_("Unknown"));       break;
	}
	if ((type&VISARRAY) != 0) (void)addstringtoinfstr(_(" array"));
	return(returninfstr());
}
