/*
 * 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 "edialogs.h"
#include "usr.h"
#include "drc.h"
#include "erc.h"
#include "rout.h"
#include "network.h"
#include "logeffort.h"
#include "sim.h"
#include "simirsim.h"
#include "eio.h"
#include "tecart.h"
#include "tecmocmos.h"
#include "tecmocmossub.h"
#include "tecschem.h"

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

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

typedef struct
{
	BOOLEAN   dirty;
	INTBIG    saveflag;
	INTBIG   *addr;
	INTBIG    type;
	INTBIG    partial;
	char     *variablename;
	UINTBIG   initialbits1, initialbits2;
	UINTBIG   maskbits1, maskbits2;
	char     *meaning;
} OPTIONVARPROTO;

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

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

static OPTIONVARCACHE *db_optionvarcache;
static INTBIG    db_optionvarcachecount = 0;

/***** for parameterized cells *****/

#define NOPARCELL      ((PARCELL *)-1)

typedef struct Iparcell
{
	CELL            *cell;
	char            *parname;
	struct Iparcell *nextparcell;
} PARCELL;

static PARCELL *db_parcellfree = NOPARCELL;
static PARCELL *db_firstparcell = NOPARCELL;

/***** 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 */
static 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 */
	INTBIG       varindex;			/* current index for variable search */
	INTBIG       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 INTBIG db_apackindex = 0;
static struct attrsearch *db_thisapack;
static INTBIG db_realaddress;

/* prototypes for local routines */
static void     db_initnodeinstlist(NODEINST*);
static void     db_initnodeprotolist(NODEPROTO*);
static void     db_initportarcinstlist(PORTARCINST*);
static void     db_initportexpinstlist(PORTEXPINST*);
static void     db_initportprotolist(PORTPROTO*);
static void     db_initarcinstlist(ARCINST*);
static void     db_initarcprotolist(ARCPROTO*);
static void     db_initgeomlist(GEOM*);
static void     db_initliblist(LIBRARY*);
static void     db_inittechlist(TECHNOLOGY*);
static void     db_inittoollist(TOOL*);
static void     db_initrtnodelist(RTNODE*);
static void     db_initnetworklist(NETWORK*);
static void     db_initcelllist(CELL*);
static void     db_initviewlist(VIEW*);
static void     db_initwindowlist(WINDOWPART*);
static void     db_initgraphicslist(GRAPHICS*);
static void     db_initconstraintlist(CONSTRAINT*);
static void     db_initwindowframelist(WINDOWFRAME*);
static void     db_initpolygonlist(POLYGON*);
static PARCELL *db_allocparcell(void);
static void     db_freeparcell(PARCELL *pc);
static INTBIG   db_getkey(char*);
static CLUSTER *db_whichcluster(INTBIG, INTBIG);
#ifdef DEBUGMEMORY
  static BOOLEAN db_setvalkey(INTSML*, VARIABLE**, INTBIG, INTBIG, INTBIG, INTBIG, CLUSTER*, char*, INTBIG);
  static BOOLEAN db_setvalvar(VARIABLE*, INTBIG, INTBIG, CLUSTER*, char*, INTBIG);
  static BOOLEAN db_setval(char*, char*, INTBIG, CLUSTER*, char*, INTBIG);
  static BOOLEAN db_setindvar(INTBIG, INTBIG, VARIABLE*, INTBIG, INTBIG, char*, INTBIG);
#else
  static BOOLEAN db_setvalkey(INTSML*, VARIABLE**, INTBIG, INTBIG, INTBIG, INTBIG, CLUSTER*);
  static BOOLEAN db_setvalvar(VARIABLE*, INTBIG, INTBIG, CLUSTER*);
  static BOOLEAN db_setval(char*, char*, INTBIG, CLUSTER*);
  static BOOLEAN db_setindvar(INTBIG, INTBIG, VARIABLE*, INTBIG, INTBIG);
#endif
static BOOLEAN  db_getindvar(VARIABLE*, INTBIG, INTBIG*);
static BOOLEAN  db_insindvar(INTBIG, INTBIG, VARIABLE*, INTBIG, INTBIG);
static BOOLEAN  db_delindvar(INTBIG, INTBIG, VARIABLE*, INTBIG);
static INTBIG   db_binarysearch(INTSML, VARIABLE*, INTBIG);
static void     db_renamevar(INTSML, VARIABLE*, INTBIG, INTBIG);
static INTBIG   db_getdatasize(INTBIG);
static char    *db_describetype(INTBIG type);
static INTBIG   db_assignvalue(void *loc, INTBIG datasize);
static void     db_makefontassociationvar(INTSML numvar, VARIABLE *firstvar, INTBIG *fontusage, INTBIG numfonts);
static void     db_makefontassociationdescript(UINTBIG *descript, INTBIG *fontusage, INTBIG numfonts);
static void     db_makefontassociations(LIBRARY *lib);

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

	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);

	while (db_firstparcell != NOPARCELL)
	{
		pc = db_firstparcell;
		db_firstparcell = pc->nextparcell;
		efree((char *)pc->parname);
		db_freeparcell(pc);
	}
	while (db_parcellfree != NOPARCELL)
	{
		pc = db_parcellfree;
		db_parcellfree = pc->nextparcell;
		efree((char *)pc);
	}
}

/************************* 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.  If the variable is code, it
 * will be evaluated and a pseudo-variable will be returned that holds the
 * value.
 */
VARIABLE *getval(INTBIG addr, INTBIG type, INTBIG want, char *name)
{
	static VARIABLE *var;
	REGISTER char *pp;
	REGISTER INTBIG search;

	search = initobjlist(addr, type, FALSE);
	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(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.
 * If the variable is code, it will be evaluated and a pseudo-variable
 * will be returned that holds the value.
 */
VARIABLE *getvalkey(INTBIG addr, INTBIG type, INTBIG want, INTBIG key)
{
	REGISTER INTBIG med;
	INTSML *mynumvar;
	REGISTER VARIABLE *var;
	VARIABLE **myfirstvar;

	/* get the firstvar/numvar into the globals */
	if (db_getvarptr(addr, type, &myfirstvar, &mynumvar)) 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(evalvar(var));
}

/*
 * 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.  If the variable is code, it
 * will not be evaluated.
 */
VARIABLE *getvalnoeval(INTBIG addr, INTBIG type, INTBIG want, char *name)
{
	static VARIABLE *var;
	REGISTER char *pp;
	REGISTER INTBIG search;

	search = initobjlist(addr, type, FALSE);
	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(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.
 * If the variable is code, it will not be evaluated.
 */
VARIABLE *getvalkeynoeval(INTBIG addr, INTBIG type, INTBIG want, INTBIG key)
{
	REGISTER INTBIG med;
	INTSML *mynumvar;
	REGISTER VARIABLE *var;
	VARIABLE **myfirstvar;

	/* get the firstvar/numvar into the globals */
	if (db_getvarptr(addr, type, &myfirstvar, &mynumvar)) 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(var);
}

/*
 * routine to evaluate variable "var" and replace it with its true value if it is code
 */
VARIABLE *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.type = var->type & ~(VCODE1|VCODE2);
	if (doquerry((char *)var->addr, language, &retvar.type, &retvar.addr))
		return(var);
	retvar.key = var->key;
	TDCOPY(retvar.textdescript, var->textdescript);
	return(&retvar);
}

/*
 * 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
 * true on error.
 */
BOOLEAN getind(INTBIG addr, INTBIG type, char *name, INTBIG aindex, INTBIG *value)
{
	VARIABLE *var;
	REGISTER char *pp;
	REGISTER INTBIG search;

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

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

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

	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 true upon error.
 */
BOOLEAN getindkey(INTBIG addr, INTBIG type, INTBIG key, INTBIG aindex, INTBIG *value)
{
	VARIABLE **myfirstvar;
	INTSML *mynumvar;
	REGISTER INTBIG med;

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

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

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

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

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

/*
 * Routine to examine the hierarchy and find the variable "name".
 * Looks as many as "height" levels up the hierarchy (arbitrary if zero).
 * Returns the variable if found.
 */
VARIABLE *getparentval(char *name, INTBIG height)
{
	REGISTER INTBIG key;

	key = makekey(name);
	return(getparentvalkey(key, height));
}

/*
 * Routine to examine the hierarchy and find the variable with key "key".
 * Looks as many as "height" levels up the hierarchy (arbitrary if zero).
 * Returns the variable if found.
 */
VARIABLE *getparentvalkey(INTBIG key, INTBIG height)
{
	NODEINST **localnilist;
	INTBIG localdepth, *localindexlist;
	REGISTER NODEINST *ni;
	REGISTER INTBIG i, climbed;
	REGISTER VARIABLE *var, *varne;

	/* search hierarchical path */
	gettraversalpath(&localnilist, &localindexlist, &localdepth);
	climbed = 0;
	for(i = localdepth - 1; i >= 0; i--)
	{
		ni = localnilist[i];

		/*
		 * because evaluation of this variable may involve examination of the
		 * hierarchy, the hierarchy stack must get shortened
		 */
		climbed++;
		popouthierarchy(climbed);

		/* look for the variable on the instance up the hierarchy */
		var = getvalkey((INTBIG)ni, VNODEINST, -1, key);

		/* restore hierarchy stack */
		popouthierarchy(0);

		/* return value if found */
		if (var != NOVARIABLE)
		{
			/* make sure the variable is of type "parameter" */
			if (TDGETISPARAM(var->textdescript) == 0)
			{
				ttyputmsg(_("Warning: Facet %s, node %s has attribute %s used as a parameter"),
					describenodeproto(ni->parent), describenodeinst(ni), truevariablename(var));

				/* repair the bit */
				popouthierarchy(climbed);
				varne = getvalkeynoeval((INTBIG)ni, VNODEINST, -1, key);
				popouthierarchy(0);
				if (varne != NOVARIABLE)
				{
					TDSETISPARAM(varne->textdescript, VTISPARAMETER);
					ni->parent->cell->lib->userbits |= LIBCHANGEDMINOR;
				}
			}
			return(var);
		}

		/* stop climbing hierarchy if limited */
		if (height != 0)
		{
			height--;
			if (height <= 0) break;
		}
	}
	return(NOVARIABLE);
}

/*
 * 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, UINTBIG *newdescript)
{
	UINTBIG olddescript[TEXTDESCRIPTSIZE];

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

	/* handle change control, constraint, and broadcast */
	if (!db_donextchangequietly && !db_dochangesquietly)
	{
		/* tell constraint system about killed variable */
		(*el_curconstraint->modifydescript)(addr, type, var->key, olddescript);

		db_setchangefacet(db_whichnodeproto(addr, type));

		/* record the change */
		(void)db_change(addr, DESCRIPTMOD, type, var->key, var->type, olddescript[0],
			olddescript[1], 0);
	}
	db_donextchangequietly = FALSE;

	/* mark a change to the database */
	db_changetimestamp++;
}

/*
 * 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 INTBIG 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)) 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, BOOLEAN 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 VTOOL:        db_inittoollist((TOOL *)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)
		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 INTBIG datasize;
	REGISTER BOOLEAN 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;
				TDCLEAR(v.textdescript);
				TDSETSIZE(v.textdescript, TXTSETQLAMBDA(4));
				v.addr = (INTBIG)apack->vars[apack->specindex].ptr - apack->proto + apack->object;
				db_realaddress = v.addr;
				indir = FALSE;
				if ((v.type&VISARRAY) == 0) indir = TRUE; else
					if ((v.type&VCREF) == 0) indir = TRUE;
				if (indir)
				{
					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;
				TDCLEAR(v.textdescript);
				TDSETSIZE(v.textdescript, TXTSETQLAMBDA(4));
				v.addr = (INTBIG)ai;
				nvar = getvalkey((INTBIG)ai, VARCINST, VSTRING, el_arc_name_key);
				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;
				TDCLEAR(v.textdescript);
				TDSETSIZE(v.textdescript, TXTSETQLAMBDA(4));
				v.addr = (INTBIG)ni;
				nvar = getvalkey((INTBIG)ni, VNODEINST, VSTRING, el_node_name_key);
				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;
				TDCLEAR(v.textdescript);
				TDSETSIZE(v.textdescript, TXTSETQLAMBDA(4));
				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;
				TDCLEAR(v.textdescript);
				TDSETSIZE(v.textdescript, TXTSETQLAMBDA(4));
				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;
				TDCLEAR(v.textdescript);
				TDSETSIZE(v.textdescript, TXTSETQLAMBDA(4));
				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;
				TDCLEAR(v.textdescript);
				TDSETSIZE(v.textdescript, TXTSETQLAMBDA(4));
				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;
				TDCLEAR(v.textdescript);
				TDSETSIZE(v.textdescript, TXTSETQLAMBDA(4));
				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);
	}
}

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

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

	key = makekey(name);
	if (key == -1)
	{
		db_donextchangequietly = FALSE;
		return(TRUE);
	}

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

/*
 * delete the variable with the key "key" from object whose address is
 * "addr" and type is "type".  Returns true if there is an error.
 */
BOOLEAN 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;
	UINTBIG olddescript[TEXTDESCRIPTSIZE];

	/* get the firstvar/numvar pointers */
	if (db_getvarptr(addr, type, &myfirstvar, &mynumvar))
	{
		db_donextchangequietly = FALSE;
		return(TRUE);
	}

	/* 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_donextchangequietly = FALSE;
		(void)db_error(DBNOVAR|DBDELVALKEY);
		return(TRUE);
	}

	/* delete the variable */
	var = &(*myfirstvar)[i];
	oldaddr = var->addr;
	oldtype = var->type;
	TDCOPY(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;
		TDCOPY((*myfirstvar)[j].textdescript, (*myfirstvar)[j+1].textdescript);
		(*myfirstvar)[j].addr = (*myfirstvar)[j+1].addr;
	}
	if (*mynumvar == 0) efree((char *)*myfirstvar);

	/* handle change control, constraint, and broadcast */
	if (!db_donextchangequietly && !db_dochangesquietly)
	{
		/* 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],
			olddescript[1]);
	} else
	{
		/* not going through change control, so delete the memory now */
		db_freevar(oldaddr, oldtype);
	}
	db_donextchangequietly = FALSE;

	/* mark a change to the database */
	if (type != VTOOL || addr != (INTBIG)us_tool) db_changetimestamp++;
	return(FALSE);
}

/*
 * routine to rename variable "oldname" to "newname" everywhere in the
 * database.
 * 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.
 */
void 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;
	oldkey = (INTBIG)el_namespace[oldkey];

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

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

	/* 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 tools */
	for(i=0; i<el_maxtools; i++)
		db_renamevar(el_tools[i].numvar, el_tools[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);
	}

	/* mark a change to the database */
	db_changetimestamp++;
}

/************************* 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, FALSE);
	if (search == 0)
	{
		db_donextchangequietly = FALSE;
		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_donextchangequietly = FALSE;
			return((VARIABLE *)db_error(DBVARFIXED|DBSETVAL));
		}

		/* handle change control, constraint, and broadcast */
		if (!db_donextchangequietly && !db_dochangesquietly)
		{
			/* 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], var->textdescript[1]);
		}

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

		/* handle change control, constraint, and broadcast */
		if (!db_donextchangequietly && !db_dochangesquietly)
		{
			/* 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);
		}
		db_donextchangequietly = FALSE;

		/* if cell name changed, rebuild hash table of cell names */
		if (type == VCELL && namesame(name, "cellname") == 0)
			db_buildcellhashtable(((CELL *)addr)->lib);

		/* mark a change to the database */
		if (type != VTOOL || addr != (INTBIG)us_tool) db_changetimestamp++;
		return(var);
	}

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

	/* get the key of the new name */
	key = makekey(name);
	if (key == -1)
	{
		db_donextchangequietly = FALSE;
		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 INTBIG med;
	VARIABLE **myfirstvar;
	INTSML *mynumvar;

	/* get the attribute list pointers */
	if (db_getvarptr(addr, type, &myfirstvar, &mynumvar))
	{
		db_donextchangequietly = FALSE;
		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_donextchangequietly = FALSE;
			return((VARIABLE *)db_error(DBVARFIXED|DBSETVALKEY));
		}

		/* handle change control, constraint, and broadcast */
		if (!db_donextchangequietly && !db_dochangesquietly)
		{
			/* 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],
					(*myfirstvar)[med].textdescript[1]);
		}
	}

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

	/* handle change control, constraint, and broadcast */
	if (!db_donextchangequietly && !db_dochangesquietly)
	{
		/* 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);
	}
	db_donextchangequietly = FALSE;
	if (med < 0) med = -1 - med;

	/* mark a change to the database */
	if (type != VTOOL || addr != (INTBIG)us_tool) db_changetimestamp++;
	return(&(*myfirstvar)[med]);
}

/*
 * routine to copy the variables from object "fromaddr" (which has type
 * "fromtype") to object "toaddr" (which has type "totype").  Returns true
 * on error.
 */
BOOLEAN 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)) return(TRUE);

	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(TRUE);
		TDCOPY(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(FALSE);
}

/*********************** 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 true upon error.
 */
#ifdef DEBUGMEMORY
BOOLEAN _setind(INTBIG addr, INTBIG type, char *name, INTBIG aindex, INTBIG newaddr,
	char *module, INTBIG line)
#else
BOOLEAN setind(INTBIG addr, INTBIG type, char *name, INTBIG aindex, INTBIG newaddr)
#endif
{
	VARIABLE *var;
	REGISTER char *pp;
	REGISTER INTBIG search;
	REGISTER BOOLEAN retval;

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

	/* variable must exist */
	if (pp == 0)
	{
		db_donextchangequietly = FALSE;
		(void)db_error(DBNOVAR|DBSETIND);
		return(TRUE);
	}

	if ((var->type&VCANTSET) != 0)
	{
		db_donextchangequietly = FALSE;
		(void)db_error(DBVARFIXED|DBSETIND);
		return(TRUE);
	}

#ifdef DEBUGMEMORY
	retval = db_setindvar(addr, type, var, aindex, newaddr, module, line);
#else
	retval = db_setindvar(addr, type, var, aindex, newaddr);
#endif
	db_donextchangequietly = FALSE;
	if (retval)
	{
		(void)db_error(DBNOMEM|DBSETIND);
		return(TRUE);
	}

	/* mark a change to the database */
	db_changetimestamp++;
	return(FALSE);
}

/*
 * 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.
 */
#ifdef DEBUGMEMORY
BOOLEAN _setindkey(INTBIG addr, INTBIG type, INTBIG key, INTBIG aindex, INTBIG newaddr,
	char *module, INTBIG line)
#else
BOOLEAN setindkey(INTBIG addr, INTBIG type, INTBIG key, INTBIG aindex, INTBIG newaddr)
#endif
{
	VARIABLE **myfirstvar;
	INTSML *mynumvar;
	REGISTER INTBIG med;
	REGISTER BOOLEAN retval;

	/* get the attribute list into the globals */
	if (db_getvarptr(addr, type, &myfirstvar, &mynumvar))
	{
		db_donextchangequietly = FALSE;
		return(TRUE);
	}

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

	/* variable must exist */
	if (med < 0)
	{
		db_donextchangequietly = FALSE;
		(void)db_error(DBNOVAR|DBSETINDKEY);
		return(TRUE);
	}

	if (((*myfirstvar)[med].type&VCANTSET) != 0)
	{
		db_donextchangequietly = FALSE;
		(void)db_error(DBVARFIXED|DBSETINDKEY);
		return(TRUE);
	}

	/* set the variable */
#ifdef DEBUGMEMORY
	retval = db_setindvar(addr, type, &(*myfirstvar)[med], aindex, newaddr, module, line);
#else
	retval = db_setindvar(addr, type, &(*myfirstvar)[med], aindex, newaddr);
#endif
	db_donextchangequietly = FALSE;
	if (retval != 0)
	{
		(void)db_error(DBNOMEM|DBSETINDKEY);
		return(TRUE);
	}

	/* mark a change to the database */
	db_changetimestamp++;
	return(FALSE);
}

/*
 * 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 true upon error.
 */
BOOLEAN insind(INTBIG addr, INTBIG type, char *name, INTBIG aindex, INTBIG newaddr)
{
	VARIABLE *var;
	REGISTER char *pp;
	REGISTER INTBIG search;
	REGISTER BOOLEAN retval;

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

	/* variable must exist */
	if (pp == 0)
	{
		db_donextchangequietly = FALSE;
		(void)db_error(DBNOVAR|DBINSIND);
		return(TRUE);
	}

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

	retval = db_insindvar(addr, type, var, aindex, newaddr);
	db_donextchangequietly = FALSE;
	if (retval != 0)
	{
		(void)db_error(DBNOMEM|DBINSIND);
		return(TRUE);
	}

	/* mark a change to the database */
	db_changetimestamp++;
	return(FALSE);
}

/*
 * 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 true upon error.
 */
BOOLEAN insindkey(INTBIG addr, INTBIG type, INTBIG key, INTBIG aindex, INTBIG newaddr)
{
	VARIABLE **myfirstvar;
	INTSML *mynumvar;
	REGISTER INTBIG med;
	REGISTER BOOLEAN retval;

	/* get the attribute list into the globals */
	if (db_getvarptr(addr, type, &myfirstvar, &mynumvar))
	{
		db_donextchangequietly = FALSE;
		return(TRUE);
	}

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

	/* variable must exist */
	if (med < 0)
	{
		db_donextchangequietly = FALSE;
		(void)db_error(DBNOVAR|DBINSINDKEY);
		return(TRUE);
	}

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

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

	/* mark a change to the database */
	db_changetimestamp++;
	return(FALSE);
}

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

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

	/* variable must exist */
	if (pp == 0)
	{
		db_donextchangequietly = FALSE;
		(void)db_error(DBNOVAR|DBDELIND);
		return(TRUE);
	}

	if ((var->type&VCANTSET) != 0)
	{
		db_donextchangequietly = FALSE;
		(void)db_error(DBVARFIXED|DBDELIND);
		return(TRUE);
	}

	retval = db_delindvar(addr, type, var, aindex);
	db_donextchangequietly = FALSE;
	if (retval)
	{
		(void)db_error(DBNOMEM|DBDELIND);
		return(TRUE);
	}

	/* mark a change to the database */
	db_changetimestamp++;
	return(FALSE);
}

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

	/* get the attribute list into the globals */
	if (db_getvarptr(addr, type, &myfirstvar, &mynumvar))
	{
		db_donextchangequietly = FALSE;
		return(TRUE);
	}

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

	/* variable must exist */
	if (med < 0)
	{
		db_donextchangequietly = FALSE;
		(void)db_error(DBNOVAR|DBDELINDKEY);
		return(TRUE);
	}

	if (((*myfirstvar)[med].type&VCANTSET) != 0)
	{
		db_donextchangequietly = FALSE;
		(void)db_error(DBVARFIXED|DBDELINDKEY);
		return(TRUE);
	}

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

	/* mark a change to the database */
	db_changetimestamp++;
	return(FALSE);
}

/************************* 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 COMTOOL
  extern TOOL *com_tool;
#endif
#if DRCTOOL
  extern TOOL *dr_tool;
#endif
#if ERCTOOL
  extern TOOL *erc_tool;
#endif
#if LOGEFFTOOL
  extern TOOL *le_tool;
#endif
#if ROUTTOOL
  extern TOOL *ro_tool;
#endif
#if SCTOOL
  extern TOOL *sc_tool;
#endif
#if SIMTOOL
  extern TOOL *sim_tool;
#endif
#if VHDLTOOL
  extern TOOL *vhdl_tool;
#endif

static OPTIONVARPROTO db_ovpforeignfilelibrary[] =	/* FILE: IO Options: Library Options */
{
	{0, 0, (INTBIG*)&io_tool,  VTOOL,       0, "IO_state", 0, 0, BINOUTBACKUP, 0, N_("Library file backup")},
	{0, 0, (INTBIG*)&io_tool,  VTOOL,       0, "IO_state", 0, 0, CHECKATWRITE, 0, N_("Check database after write")},
	{0, 0, 0,                  0,           0, 0, 0, 0, 0, 0, ""}
};
#if IOCIF
static OPTIONVARPROTO db_ovpforeignfilecif[] =		/* FILE: IO Options: CIF Options */
{
	{0, 0, (INTBIG*)&io_tool,  VTOOL,       0, "IO_state", 0, 0, CIFOUTEXACT, 0, N_("Output mimics display")},
	{0, 0, (INTBIG*)&io_tool,  VTOOL,       0, "IO_state", 0, 0, CIFOUTMERGE, 0, N_("Output merges boxes")},
	{0, 0, (INTBIG*)&io_tool,  VTOOL,       0, "IO_state", 0, 0, CIFINSQUARE, 0, N_("Input squares wires")},
	{0, 0, (INTBIG*)&io_tool,  VTOOL,       0, "IO_state", 0, 0, CIFOUTNOTOPCALL, 0, N_("Output Instantiates top level")},
	{0, 0, (INTBIG*)&io_tool,  VTOOL,       0, "IO_state", 0, 0, CIFOUTNORMALIZE, 0, N_("Normalize coordinates")},
	{0, 0, (INTBIG*)&io_tool,  VTOOL,       0, "IO_state", 0, 0, 0, CIFRESHIGH, N_("Resolution check")},
	{0, 0, 0,                  VTECHNOLOGY, 0, "IO_cif_layer_names", 0, 0, 0, 0, N_("CIF layers")},
	{0, 0, 0,                  VTECHNOLOGY, 0, "IO_cif_resolution", 0, 0, 0, 0, N_("CIF resolution")},
	{0, 0, 0,                  0,           0, 0, 0, 0, 0, 0, ""}
};
#endif
#if IOGDS
static OPTIONVARPROTO db_ovpforeignfilegds[] =		/* FILE: IO Options: GDS Options */
{
	{0, 0, (INTBIG*)&io_tool,  VTOOL,       0, "IO_state", 0, 0, GDSINTEXT, 0, N_("Input includes text")},
	{0, 0, (INTBIG*)&io_tool,  VTOOL,       0, "IO_state", 0, 0, GDSINEXPAND, 0, N_("Input expands facets")},
	{0, 0, (INTBIG*)&io_tool,  VTOOL,       0, "IO_state", 0, 0, GDSINARRAYS, 0, N_("Input instantiates arrays")},
	{0, 0, (INTBIG*)&io_tool,  VTOOL,       0, "IO_state", 0, 0, GDSOUTMERGE, 0, N_("Output merges boxes")},
	{0, 0, (INTBIG*)&io_tool,  VTOOL,       0, "IO_state", 0, 0, GDSINIGNOREUKN, 0, N_("Input ignores unknown layers")},
	{0, 0, 0,                  VTECHNOLOGY, 0, "IO_gds_layer_numbers", 0, 0, 0, 0, N_("GDS layers")},
	{0, 0, (INTBIG*)&io_tool,  VTOOL,       0, "IO_curve_resolution", 0, 0, 0, 0, N_("Maximum arc angle")},
	{0, 0, (INTBIG*)&io_tool,  VTOOL,       0, "IO_curve_sag", 0, 0, 0, 0, N_("Maximum arc sag")},
	{0, 0, 0,                  0,           0, 0, 0, 0, 0, 0, ""}
};
#endif
#if IODXF
static OPTIONVARPROTO db_ovpforeignfiledxf[] =		/* FILE: IO Options: DXF Options */
{
	{0, 0, (INTBIG*)&io_tool,  VTOOL,       0, "IO_state", 0, 0, DXFFLATTENINPUT, 0, N_("Input flattens hierarchy")},
	{0, 0, (INTBIG*)&io_tool,  VTOOL,       0, "IO_state", 0, 0, DXFALLLAYERS, 0, N_("Input reads all layers")},
	{0, 0, (INTBIG*)&io_tool,  VTOOL,       0, "IO_dxf_units", 0, 0, 0, 0, N_("DXF scale")},
	{0, 0, 0,                  VTECHNOLOGY, 0, "IO_dxf_layer_names", 0, 0, 0, 0, N_("DXF layers")},
	{0, 0, 0,                  0,           0, 0, 0, 0, 0, 0, ""}
};
#endif
#if IOEDIF
static OPTIONVARPROTO db_ovpforeignfileedif[] =		/* FILE: IO Options: EDIF Options */
{
	{0, 0, (INTBIG*)&io_tool,  VTOOL,       0, "IO_state", 0, 0, EDIFSCHEMATIC, 0, N_("Use schematic view")},
	{0, 0, (INTBIG*)&io_tool,  VTOOL,       0, "IO_edif_input_scale", 0, 0, 0, 0, N_("Input scale")},
	{0, 0, 0,                  0,           0, 0, 0, 0, 0, 0, ""}
};
#endif
#if IOSKILL
static OPTIONVARPROTO db_ovpforeignfileskill[] =	/* FILE: IO Options: SKILL Options */
{
	{0, 0, (INTBIG*)&io_tool,  VTOOL,       0, "IO_state", 0, 0, SKILLNOHIER, 0, N_("Do not include subfacets")},
	{0, 0, (INTBIG*)&io_tool,  VTOOL,       0, "IO_state", 0, 0, 0, SKILLFLATHIER, N_("Flatten hierarchy")},
	{0, 0, 0,                  VTECHNOLOGY, 0, "IO_skill_layer_names", 0, 0, 0, 0, N_("SKILL layers")},
	{0, 0, 0,                  0,           0, 0, 0, 0, 0, 0, ""}
};
#endif
static OPTIONVARPROTO db_ovpforeignfilecdl[] =	/* FILE: IO Options: CDL Options */
{
	{0, 0, (INTBIG*)&io_tool,  VTOOL,       0, "IO_cdl_library_name", 0, 0, 0, 0, N_("Cadence Library Name")},
	{0, 0, (INTBIG*)&io_tool,  VTOOL,       0, "IO_cdl_library_path", 0, 0, 0, 0, N_("Cadence Library Path")},
	{0, 0, (INTBIG*)&io_tool,  VTOOL,       0, "IO_state", 0, 0, 0, CDLNOBRACKETS, N_("Convert brackets")},
	{0, 0, 0,                  0,           0, 0, 0, 0, 0, 0, ""}
};
#if IODEF
static OPTIONVARPROTO db_ovpforeignfiledef[] =		/* FILE: IO Options: DEF Options */
{
	{0, 0, (INTBIG*)&io_tool,  VTOOL,       0, "IO_state", 0, 0, DEFNOLOGICAL, 0, N_("Place logical interconnect")},
	{0, 0, (INTBIG*)&io_tool,  VTOOL,       0, "IO_state", 0, 0, DEFNOPHYSICAL, 0, N_("Place physical interconnect")},
	{0, 0, 0,                  0,           0, 0, 0, 0, 0, 0, ""}
};
#endif

static OPTIONVARPROTO db_ovpprint[] =				/* FILE: Print Options */
{
/*	{0, 0, 0,                  VNODEPROTO,  0, "IO_postscript_EPS_scale", 0, 0, 0, 0, N_("")},  *** facet specific ***/
/*	{0, 0, 0,                  VNODEPROTO,  0, "IO_postscript_filename", 0, 0, 0, 0, N_("")},  *** facet specific ***/
	{0, 0, (INTBIG*)&io_tool,  VTOOL,       0, "IO_state", 0, 0, PLOTFOCUS|PLOTFOCUSDPY, 0, N_("Area to plot")},
	{0, 0, (INTBIG*)&io_tool,  VTOOL,       0, "IO_state", 0, 0, PLOTDATES, 0, N_("Plot date in corner")},
	{0, 0, (INTBIG*)&io_tool,  VTOOL,       0, "IO_state", 0, 0, EPSPSCRIPT, 0, N_("Encapsulated PostScript")},
	{0, 0, (INTBIG*)&io_tool,  VTOOL,       0, "IO_state", 0, 0, HPGL2, 0, N_("Write HPGL/2")},
	{0, 0, (INTBIG*)&io_tool,  VTOOL,       0, "IO_state", 0, 0, PSCOLOR, 0, N_("Color PostScript")},
	{0, 0, (INTBIG*)&io_tool,  VTOOL,       0, "IO_state", 0, 0, PSPLOTTER, 0, N_("Printer/plotter")},
	{0, 0, (INTBIG*)&io_tool,  VTOOL,       0, "IO_state", 0, 0, PSROTATE, 0, N_("Rotated plot 90 degrees")},
	{0, 0, (INTBIG*)&io_tool,  VTOOL,       0, "IO_state", 0, 0, PSCOLORSTIP, 0, N_("Stippled PostScript")},
	{0, 0, (INTBIG*)&io_tool,  VTOOL,       0, "IO_state", 0, 0, PSHIERARCHICAL, 0, N_("Flat PostScript")},
	{0, 0, (INTBIG*)&io_tool,  VTOOL,       0, "IO_postscript_width", 0, 0, 0, 0, N_("Page width")},
	{0, 0, (INTBIG*)&io_tool,  VTOOL,       0, "IO_postscript_height", 0, 0, 0, 0, N_("Page height")},
	{0, 0, (INTBIG*)&io_tool,  VTOOL,       0, "IO_postscript_margin", 0, 0, 0, 0, N_("Page margin")},
	{0, 0, (INTBIG*)&io_tool,  VTOOL,       0, "IO_hpgl2_scale", 0, 0, 0, 0, N_("HPGL/2 units per pixel")},
	{0, 0, (INTBIG*)&io_tool,  VTOOL,       0, "IO_default_printer", 0, 0, 0, 0, N_("Default printer")},
	{0, 0, 0,                  0,           0, 0, 0, 0, 0, 0, ""}
};
static OPTIONVARPROTO db_ovpnewnode[] =				/* EDIT: New Node Options */
{
/*	{0, 0, 0,                  VNODEPROTO,  0, "userbits", 0, 0, 0, 0, N_("")},  *** facet specific ***/
	{0, 0, (INTBIG*)&us_tool,  VTOOL,       0, "USER_optionflags", 0, 0, NOPRIMCHANGES, 0, N_("Disallow modification of locked primitives")},
	{0, 0, (INTBIG*)&us_tool,  VTOOL,       0, "USER_optionflags", 0, 0, NOMOVEAFTERDUP, 0, N_("Move after duplicate")},
	{0, 0, (INTBIG*)&us_tool,  VTOOL,       0, "USER_optionflags", 0, 0, DUPCOPIESPORTS, 0, N_("Duplicate/array/extract copies exports")},
	{0, 0, 0,                  VNODEPROTO,  0, "NODE_size_default", 0, 0, 0, 0, N_("Size of new primitives")},
	{0, 0, 0,                  VNODEPROTO,  0, "USER_placement_angle", 0, 0, 0, 0, N_("Rotation of new nodes")},
	{0, 0, (INTBIG*)&us_tool,  VTOOL,       0, "USER_placement_angle", 0, 0, 0, 0, N_("Rotation of new nodes")},
	{0, 0, (INTBIG*)&net_tool, VTOOL,       0, "NET_node_abbreviations", 0, 0, 0, 0, N_("Primitive function abbreviations")},
	{0, 0, (INTBIG*)&net_tool, VTOOL,       0, "NET_node_abbreviation_length", 0, 0, 0, 0, N_("Length of facet abbreviations")},
	{0, 0, 0,                  0,           0, 0, 0, 0, 0, 0, ""}
};
static OPTIONVARPROTO db_ovpselect[] =				/* EDIT: Selection: Selection Options */
{
	{0, 0, (INTBIG*)&us_tool,  VTOOL,       0, "USER_optionflags", 0, 0, NOINSTANCESELECT, 0, N_("Easy selection of facet instances")},
	{0, 0, (INTBIG*)&us_tool,  VTOOL,       0, "USER_optionflags", 0, 0, NOTEXTSELECT, 0, N_("Easy selection of annotation text")},
	{0, 0, (INTBIG*)&us_tool,  VTOOL,       0, "USER_optionflags", 0, 0, CENTEREDPRIMITIVES, 0, N_("Center-based primitives")},
	{0, 0, (INTBIG*)&us_tool,  VTOOL,       0, "USER_optionflags", 0, 0, MUSTENCLOSEALL, 0, N_("Dragging must enclose entire object")},
	{0, 0, 0,                  0,           0, 0, 0, 0, 0, 0, ""}
};
static OPTIONVARPROTO db_ovpfacet[] =				/* FACETS: Facet Options */
{
/*	{0, 0, 0,                  VNODEPROTO,  0, "userbits", 0, 0, 0, 0},  *** facet specific ***/
	{0, 0, (INTBIG*)&us_tool,  VTOOL,       0, "USER_optionflags", 0, 0, DRAWTINYFACETS, 0, N_("Tiny facet instances hashed out")},
	{0, 0, (INTBIG*)&us_tool,  VTOOL,       0, "USER_optionflags", 0, 0, CHECKDATE, 0, N_("Check facet dates during creation")},
	{0, 0, (INTBIG*)&us_tool,  VTOOL,       0, "USER_optionflags", 0, 0, AUTOSWITCHTECHNOLOGY, 0, N_("Switch technology to match current facet")},
	{0, 0, (INTBIG*)&us_tool,  VTOOL,       0, "USER_optionflags", 0, 0, FACETCENTERALWAYS, 0, N_("Place facet center in new facets")},
	{0, 0, (INTBIG*)&us_tool,  VTOOL,       0, "USER_tiny_lambda_per_pixel", 0, 0, 0, 0, N_("Hash out scale")},
	{0, 0, (INTBIG*)&us_tool,  VTOOL,       0, "USER_facet_explorer_textsize", 0, 0, 0, 0, N_("Facet explorer text size")},
	{0, 0, 0,                  0,           0, 0, 0, 0, 0, 0, ""}
};
static OPTIONVARPROTO db_ovpframe[] =				/* VIEW: Frame Options */
{
/*	{0, 0, 0,                  VNODEPROTO,  0, "FACET_schematic_page_size", 0, 0, 0, 0, N_("")},  *** facet specific ***/
	{0, 0, (INTBIG*)&us_tool,  VTOOL,       0, "USER_drawing_company_name", 0, 0, 0, 0, N_("Company name")},
	{0, 0, (INTBIG*)&us_tool,  VTOOL,       0, "USER_drawing_designer_name", 0, 0, 0, 0, N_("Designer name")},
	{0, 0, (INTBIG*)&us_tool,  VTOOL,       0, "USER_drawing_project_name", 0, 0, 0, 0, N_("Project name")},
	{0, 0, 0,                  0,           0, 0, 0, 0, 0, 0, ""}
};
static OPTIONVARPROTO db_ovpicon[] =				/* VIEW: Icon Options */
{
	{0, 0, (INTBIG*)&us_tool,  VTOOL,       0, "USER_icon_style", 0, 0, ICONSTYLESIDEIN, 0, N_("Inputs on which side")},
	{0, 0, (INTBIG*)&us_tool,  VTOOL,       0, "USER_icon_style", 0, 0, ICONSTYLESIDEOUT, 0, N_("Outputs on which side")},
	{0, 0, (INTBIG*)&us_tool,  VTOOL,       0, "USER_icon_style", 0, 0, ICONSTYLESIDEBIDIR, 0, N_("Bidir. on which side")},
	{0, 0, (INTBIG*)&us_tool,  VTOOL,       0, "USER_icon_style", 0, 0, ICONSTYLESIDEPOWER, 0, N_("Power on which side")},
	{0, 0, (INTBIG*)&us_tool,  VTOOL,       0, "USER_icon_style", 0, 0, ICONSTYLESIDEGROUND, 0, N_("Ground on which side")},
	{0, 0, (INTBIG*)&us_tool,  VTOOL,       0, "USER_icon_style", 0, 0, ICONSTYLESIDECLOCK, 0, N_("Clock on which side")},
	{0, 0, (INTBIG*)&us_tool,  VTOOL,       0, "USER_icon_style", 0, 0, ICONSTYLEPORTLOC, 0, N_("Export location")},
	{0, 0, (INTBIG*)&us_tool,  VTOOL,       0, "USER_icon_style", 0, 0, ICONSTYLEPORTSTYLE, 0, N_("Export style")},
	{0, 0, (INTBIG*)&us_tool,  VTOOL,       0, "USER_icon_style", 0, 0, ICONSTYLETECH, 0, N_("Export technology")},
	{0, 0, (INTBIG*)&us_tool,  VTOOL,       0, "USER_icon_style", 0, 0, ICONSTYLEDRAWNOLEADS, 0, N_("Draw leads")},
	{0, 0, (INTBIG*)&us_tool,  VTOOL,       0, "USER_icon_style", 0, 0, ICONSTYLEDRAWNOBODY, 0, N_("Draw body")},
	{0, 0, (INTBIG*)&us_tool,  VTOOL,       0, "USER_icon_style", 0, 0, ICONSTYLEREVEXPORT, 0, N_("Reverse export order")},
	{0, 0, (INTBIG*)&us_tool,  VTOOL,       0, "USER_icon_style", 0, 0, ICONINSTLOC, 0, N_("Instance location")},
	{0, 0, (INTBIG*)&us_tool,  VTOOL,       0, "USER_icon_lead_length", 0, 0, 0, 0, N_("Lead length")},
	{0, 0, (INTBIG*)&us_tool,  VTOOL,       0, "USER_icon_lead_spacing", 0, 0, 0, 0, N_("Lead spacing")},
	{0, 0, 0,                  0,           0, 0, 0, 0, 0, 0, ""}
};
static OPTIONVARPROTO db_ovpnewarc[] =				/* ARC: New Arc Options */
{
	{0, 0, 0,                  VARCPROTO,   0, "ARC_width_default", 0, 0, 0, 0, N_("Width")},
	{0, 0, (INTBIG*)&us_tool,  VTOOL,       0, "USER_arc_style", 0, 0, WANTFIX, 0, N_("All rigid")},
	{0, 0, (INTBIG*)&us_tool,  VTOOL,       0, "USER_arc_style", 0, 0, WANTFIXANG, 0, N_("All fixed angle")},
	{0, 0, (INTBIG*)&us_tool,  VTOOL,       0, "USER_arc_style", 0, 0, WANTCANTSLIDE, 0, N_("All slidable")},
	{0, 0, (INTBIG*)&us_tool,  VTOOL,       0, "USER_arc_style", 0, 0, WANTNEGATED, 0, N_("All negated")},
	{0, 0, (INTBIG*)&us_tool,  VTOOL,       0, "USER_arc_style", 0, 0, WANTNOEXTEND, 0, N_("All ends extended")},
	{0, 0, (INTBIG*)&us_tool,  VTOOL,       0, "USER_arc_style", 0, 0, WANTDIRECTIONAL, 0, N_("All directional")},
	{0, 0, (INTBIG*)&us_tool,  VTOOL,       0, "USER_arc_style", 0, 0, AANGLEINC, 0, N_("All angle")},
	{0, 0, 0,                  VARCPROTO,   0, "USER_arc_style", 0, 0, WANTFIX, 0, N_("Arc rigid")},
	{0, 0, 0,                  VARCPROTO,   0, "USER_arc_style", 0, 0, WANTFIXANG, 0, N_("Arc fixed angle")},
	{0, 0, 0,                  VARCPROTO,   0, "USER_arc_style", 0, 0, WANTCANTSLIDE, 0, N_("Arc slidable")},
	{0, 0, 0,                  VARCPROTO,   0, "USER_arc_style", 0, 0, WANTNEGATED, 0, N_("Arc negated")},
	{0, 0, 0,                  VARCPROTO,   0, "USER_arc_style", 0, 0, WANTNOEXTEND, 0, N_("Arc ends extended")},
	{0, 0, 0,                  VARCPROTO,   0, "USER_arc_style", 0, 0, WANTDIRECTIONAL, 0, N_("Arc directional")},
	{0, 0, 0,                  VARCPROTO,   0, "USER_arc_style", 0, 0, AANGLEINC, 0, N_("Arc angle")},
	{0, 0, 0,                  VARCPROTO,   0, "ARC_Default_Pin", 0, 0, 0, 0, N_("Pin")},
	{0, 0, 0,                  0,           0, 0, 0, 0, 0, 0, ""}
};

static OPTIONVARPROTO db_ovpgrid[] =				/* WINDOWS: Grid Options */
{
	{0, 0, (INTBIG*)&us_tool,  VTOOL,       0, "USER_default_grid", 0, 0, 0, 0, N_("Default grid spacing")},
	{0, 0, (INTBIG*)&us_tool,  VTOOL,       0, "USER_grid_bold_spacing", 0, 0, 0, 0, N_("Distance between bold dots")},
	{0, 0, (INTBIG*)&us_tool,  VTOOL,       0, "USER_grid_floats", 0, 0, 0, 0, N_("Align grid with circuit")},
	{0, 0, 0,                  0,           0, 0, 0, 0, 0, 0, ""}
};
static OPTIONVARPROTO db_ovpalign[] =				/* WINDOWS: Alignment Options */
{
	{0, 0, (INTBIG*)&us_tool,  VTOOL,       0, "USER_alignment_ratio", 0, 0, 0, 0, N_("Alignment of cursor to grid")},
	{0, 0, (INTBIG*)&us_tool,  VTOOL,       0, "USER_alignment_edge_ratio", 0, 0, 0, 0, N_("Alignment of edges to grid")},
	{0, 0, 0,                  0,           0, 0, 0, 0, 0, 0, ""}
};
static OPTIONVARPROTO db_ovplayvis[] =				/* WINDOWS: Layer Visibility Options */
{
	{0, 0, (INTBIG*)&us_tool,  VTOOL,       0, "USER_optionflags", 0, 0, HIDETXTNODE, 0, N_("Node text")},
	{0, 0, (INTBIG*)&us_tool,  VTOOL,       0, "USER_optionflags", 0, 0, HIDETXTARC, 0, N_("Arc text")},
	{0, 0, (INTBIG*)&us_tool,  VTOOL,       0, "USER_optionflags", 0, 0, HIDETXTPORT, 0, N_("Port text")},
	{0, 0, (INTBIG*)&us_tool,  VTOOL,       0, "USER_optionflags", 0, 0, HIDETXTEXPORT, 0, N_("Export text")},
	{0, 0, (INTBIG*)&us_tool,  VTOOL,       0, "USER_optionflags", 0, 0, HIDETXTNONLAY, 0, N_("Nonlayout text")},
	{0, 0, (INTBIG*)&us_tool,  VTOOL,       0, "USER_optionflags", 0, 0, HIDETXTINSTNAME, 0, N_("Instance names")},
	{0, 0, (INTBIG*)&us_tool,  VTOOL,       0, "USER_optionflags", 0, 0, HIDETXTFACET, 0, N_("Facet text")},
	{0, 0, 0,                  0,           0, 0, 0, 0, 0, 0, ""}
};
static OPTIONVARPROTO db_ovplaypat[] =				/* WINDOWS: Layer Display Options */
{
	{0, 0, 0,                  VTECHNOLOGY, 1, "TECH_layer_pattern_", 0, 0, 0, 0, N_("Layer pattern/color")},
	{0, 0, 0,                  0,           0, 0, 0, 0, 0, 0, ""}
};
static OPTIONVARPROTO db_ovpcolor[] =				/* WINDOWS: Color Options */
{
	{0, 0, (INTBIG*)&us_tool,  VTOOL,       0, "USER_colormap_red", 0, 0, 0, 0, N_("Red colormap")},
	{0, 0, (INTBIG*)&us_tool,  VTOOL,       0, "USER_colormap_green", 0, 0, 0, 0, N_("Green colormap")},
	{0, 0, (INTBIG*)&us_tool,  VTOOL,       0, "USER_colormap_blue", 0, 0, 0, 0, N_("Blue colormap")},
	{0, 0, 0,                  0,           0, 0, 0, 0, 0, 0, ""}
};
static OPTIONVARPROTO db_ovpport[] =				/* WINDOWS: Port Display Options */
{
	{0, 0, (INTBIG*)&us_tool,  VTOOL,       0, "USER_optionflags", 0, 0, PORTLABELS, 0, N_("Ports (on instances)")},
	{0, 0, (INTBIG*)&us_tool,  VTOOL,       0, "USER_optionflags", 0, 0, EXPORTLABELS, 0, N_("Exports (in facets)")},
	{0, 0, 0,                  0,           0, 0, 0, 0, 0, 0, ""}
};
static OPTIONVARPROTO db_ovptext[] =				/* WINDOWS: Text Options */
{
	{0, 0, (INTBIG*)&us_tool,  VTOOL,       0, "USER_default_text_style", 0, 0, 0, 0, N_("Text corner")},
	{0, 0, (INTBIG*)&us_tool,  VTOOL,       0, "USER_default_text_smart_style", 0, 0, 0, 0, N_("Smart placement")},
	{0, 0, (INTBIG*)&us_tool,  VTOOL,       0, "USER_default_node_text_size", 0, 0, 0, 0, N_("Node style")},
	{0, 0, (INTBIG*)&us_tool,  VTOOL,       0, "USER_default_arc_text_size", 0, 0, 0, 0, N_("Arc style")},
	{0, 0, (INTBIG*)&us_tool,  VTOOL,       0, "USER_default_export_text_size", 0, 0, 0, 0, N_("Export style")},
	{0, 0, (INTBIG*)&us_tool,  VTOOL,       0, "USER_default_nonlayout_text_size", 0, 0, 0, 0, N_("Nonlayout style")},
	{0, 0, (INTBIG*)&us_tool,  VTOOL,       0, "USER_default_instance_text_size", 0, 0, 0, 0, N_("Instance style")},
	{0, 0, (INTBIG*)&us_tool,  VTOOL,       0, "USER_default_facet_text_size", 0, 0, 0, 0, N_("Facet style")},
	{0, 0, (INTBIG*)&us_tool,  VTOOL,       0, "USER_text_editor", 0, 0, 0, 0, N_("Text editor")},
	{0, 0, 0,                  0,           0, 0, 0, 0, 0, 0, ""}
};
static OPTIONVARPROTO db_ovp3d[] =					/* WINDOWS: 3D Options */
{
	{0, 0, (INTBIG*)&us_tool,  VTOOL,       0, "USER_optionflags", 0, 0, NO3DPERSPECTIVE, 0, N_("Use perspective")},
	{0, 0, 0,                  VTECHNOLOGY, 0, "TECH_layer_3dheight", 0, 0, 0, 0, N_("Height")},
	{0, 0, 0,                  VTECHNOLOGY, 0, "TECH_layer_3dthickness", 0, 0, 0, 0, N_("Thickness")},
	{0, 0, 0,                  0,           0, 0, 0, 0, 0, 0, ""}
};
static OPTIONVARPROTO db_ovpmsgloc[] =				/* WINDOWS: Save Messages Location */
{
	{0, 0, (INTBIG*)&us_tool,  VTOOL,       0, "USER_messages_position", 0, 0, 0, 0, N_("Messages window location")},
	{0, 0, 0,                  0,           0, 0, 0, 0, 0, 0, ""}
};
static OPTIONVARPROTO db_ovpgeneral[] =				/* INFO: General Options */
{
	{0, 0, (INTBIG*)&us_tool,  VTOOL,       0, "USER_optionflags", 0, 0, NODATEORVERSION, 0, N_("Include date and version in output files")},
	{0, 0, (INTBIG*)&us_tool,  VTOOL,       0, "USER_optionflags", 0, 0, BEEPAFTERLONGJOB, 0, N_("Beep after long jobs")},
	{0, 0, (INTBIG*)&us_tool,  VTOOL,       0, "USER_maximum_errors", 0, 0, 0, 0, N_("Maximum errors to report")},
	{0, 0, 0,                  0,           0, 0, 0, 0, 0, 0, ""}
};
static OPTIONVARPROTO db_ovpquickkey[] =			/* INFO: Quick Key Options */
{
	{0, 0, (INTBIG*)&us_tool,  VTOOL,       0, "USER_quick_keys", 0, 0, 0, 0, N_("Quick keys")},
	{0, 0, 0,                  0,           0, 0, 0, 0, 0, 0, ""}
};
static OPTIONVARPROTO db_ovptechopt[] =				/* TECHNOLOGY: Technology Options */
{
	{0, 0, (INTBIG*)&mocmossub_tech,VTECHNOLOGY, 0, "TECH_state", 0, 0, MOCMOSSUBMETALS, 0, N_("mocmossub number of metal layers")},
	{0, 0, (INTBIG*)&mocmossub_tech,VTECHNOLOGY, 0, "TECH_state", 0, 0, MOCMOSSUBNOCONV, 0, N_("mocmossub converts to mocmos")},
	{0, 0, (INTBIG*)&mocmos_tech,VTECHNOLOGY, 0, "TECH_state", 0, 0, MOCMOSMETALS, 0, N_("mocmos number of metal layers")},
	{0, 0, (INTBIG*)&mocmos_tech,VTECHNOLOGY, 0, "TECH_state", 0, 0, MOCMOSSTICKFIGURE, 0, N_("mocmos stick figure")},
	{0, 0, (INTBIG*)&mocmos_tech,VTECHNOLOGY, 0, "TECH_state", 0, 0, MOCMOSNOSTACKEDVIAS, 0, N_("mocmos stacked vias")},
	{0, 0, (INTBIG*)&mocmos_tech,VTECHNOLOGY, 0, "TECH_state", 0, 0, MOCMOSALTAPRULES, 0, N_("mocmos alternate contact rules")},
	{0, 0, (INTBIG*)&mocmos_tech,VTECHNOLOGY, 0, "TECH_state", 0, 0, MOCMOSTWOPOLY, 0, N_("mocmos number of polysilicon layers")},
	{0, 0, (INTBIG*)&mocmos_tech,VTECHNOLOGY, 0, "TECH_state", 0, 0, MOCMOSRULESET, 0, N_("mocmos rule set")},
	{0, 0, (INTBIG*)&art_tech, VTECHNOLOGY, 0, "TECH_state", 0, 0, ARTWORKFILLARROWHEADS, 0, N_("artwork fills arrowheads")},
	{0, 0, (INTBIG*)&sch_tech, VTECHNOLOGY, 0, "TECH_layout_technology", 0, 0, 0, 0, N_("Technology to use for schematics")},
	{0, 0, 0,                  0,           0, 0, 0, 0, 0, 0, ""}
};
static OPTIONVARPROTO db_ovptechunits[] =			/* TECHNOLOGY: Units */
{
	{0, 0, (INTBIG*)&us_tool,  VTOOL,       0, "USER_display_units", 0, 0, DISPLAYUNITS, 0, N_("Display units")},
	{0, 0, (INTBIG*)&us_tool,  VTOOL,       0, "USER_display_units", 0, 0, INTERNALUNITS, 0, N_("Internal units")},
	{0, 0, 0,                  0,           0, 0, 0, 0, 0, 0, ""}
};
#if DRCTOOL
static OPTIONVARPROTO db_ovpdrc[] =					/* TOOLS: DRC: DRC Options */
{
	{0, 0, (INTBIG*)&dr_tool,  VTOOL,       0, "DRC_pointout", 0, 0, 0, 0, N_("Incremental DRC highlights errors")},
	{0, 0, (INTBIG*)&dr_tool,  VTOOL,       0, "DRC_options", 0, 0, DRCHIERDIALOG, 0, N_("Hierarchical DRC uses dialog")},
	{0, 0, (INTBIG*)&dr_tool,  VTOOL,       0, "DRC_options", 0, 0, DRCFIRSTERROR, 0, N_("Just 1 error per facet")},
	{0, 0, (INTBIG*)&dr_tool,  VTOOL,       0, "DRC_options", 0, 0, DRCREASONABLE, 0, N_("Ignore center cuts in large contacts")},
	{0, 0, (INTBIG*)&dr_tool,  VTOOL,       0, "DRC_incrementalon", 0, 0, 0, 0, N_("Incremental DRC on")},
	{0, 0, 0,                  VTECHNOLOGY, 0, "DRC_ecad_deck", 0, 0, 0, 0, N_("Dracula rules deck")},
	{0, 0, 0,                  0,           0, 0, 0, 0, 0, 0, ""}
};
static OPTIONVARPROTO db_ovpdrcr[] =				/* TOOLS: DRC: DRC Rules */
{
	{0, 0, 0,                  VTECHNOLOGY, 0, "DRC_wide_limit", 0, 0, 0, 0, N_("Wide rule size")},
	{0, 0, 0,                  VTECHNOLOGY, 0, "DRC_min_width", 0, 0, 0, 0, N_("Minimum width distance")},
	{0, 0, 0,                  VTECHNOLOGY, 0, "DRC_min_width_rule", 0, 0, 0, 0, N_("Minimum width rule")},
	{0, 0, 0,                  VTECHNOLOGY, 0, "DRC_min_connected_distances", 0, 0, 0, 0, N_("Normal connected distance")},
	{0, 0, 0,                  VTECHNOLOGY, 0, "DRC_min_connected_distances_rule", 0, 0, 0, 0, N_("Normal connected rule")},
	{0, 0, 0,                  VTECHNOLOGY, 0, "DRC_min_unconnected_distances", 0, 0, 0, 0, N_("Normal not-connected distance")},
	{0, 0, 0,                  VTECHNOLOGY, 0, "DRC_min_unconnected_distances_rule", 0, 0, 0, 0, N_("Normal not-connected rule")},
	{0, 0, 0,                  VTECHNOLOGY, 0, "DRC_min_connected_distances_wide", 0, 0, 0, 0, N_("Wide connected distance")},
	{0, 0, 0,                  VTECHNOLOGY, 0, "DRC_min_connected_distances_wide_rule", 0, 0, 0, 0, N_("Wide connected rule")},
	{0, 0, 0,                  VTECHNOLOGY, 0, "DRC_min_unconnected_distances_wide", 0, 0, 0, 0, N_("Wide not-connected spacing")},
	{0, 0, 0,                  VTECHNOLOGY, 0, "DRC_min_unconnected_distances_wide_rule", 0, 0, 0, 0, N_("Wide not-connected rule")},
	{0, 0, 0,                  VTECHNOLOGY, 0, "DRC_min_connected_distances_multi", 0, 0, 0, 0, N_("Multiple cuts connected distance")},
	{0, 0, 0,                  VTECHNOLOGY, 0, "DRC_min_connected_distances_multi_rule", 0, 0, 0, 0, N_("Multiple cuts connected rule")},
	{0, 0, 0,                  VTECHNOLOGY, 0, "DRC_min_unconnected_distances_multi", 0, 0, 0, 0, N_("Multiple cuts not-connected distance")},
	{0, 0, 0,                  VTECHNOLOGY, 0, "DRC_min_unconnected_distances_multi_rule", 0, 0, 0, 0, N_("Multiple cuts not-connected rule")},
	{0, 0, 0,                  VTECHNOLOGY, 0, "DRC_min_edge_distances", 0, 0, 0, 0, N_("Edge distance")},
	{0, 0, 0,                  VTECHNOLOGY, 0, "DRC_min_edge_distances_rule", 0, 0, 0, 0, N_("Edge rule")},
	{0, 0, 0,                  VTECHNOLOGY, 0, "DRC_min_node_size", 0, 0, 0, 0, N_("Minimum node size")},
	{0, 0, 0,                  VTECHNOLOGY, 0, "DRC_min_node_size_rule", 0, 0, 0, 0, N_("Minimum node size rule")},
	{0, 0, 0,                  0,           0, 0, 0, 0, 0, 0, ""}
};
#endif
#if SIMTOOL
static OPTIONVARPROTO db_ovpsimulation[] =			/* TOOLS: Simulation: Simulation Options */
{
	{0, 0, (INTBIG*)&sim_tool, VTOOL,       0, "SIM_als_no_update", 0, 0, 0, 0, N_("Resimulate each change")},
	{0, 0, (INTBIG*)&sim_tool, VTOOL,       0, "SIM_als_num_events", 0, 0, 0, 0, N_("ALS: maximum events")},
	{0, 0, (INTBIG*)&sim_tool, VTOOL,       0, "SIM_window_state", 0, 0, ADVANCETIME, 0, N_("Auto advance time")},
	{0, 0, (INTBIG*)&sim_tool, VTOOL,       0, "SIM_window_state", 0, 0, BUSBASEBITS, 0, N_("Base for bus values")},
	{0, 0, (INTBIG*)&sim_tool, VTOOL,       0, "SIM_window_state", 0, 0, SIMENGINE, 0, N_("Simulation engine")},
	{0, 0, (INTBIG*)&sim_tool, VTOOL,       0, "SIM_window_state", 0, 0, SHOWWAVEFORM, 0, N_("Show waveform window")},
	{0, 0, (INTBIG*)&sim_tool, VTOOL,       0, "SIM_window_state", 0, 0, WAVEPLACE, 0, N_("Place waveform window")},
	{0, 0, (INTBIG*)&sim_tool, VTOOL,       0, "SIM_window_state", 0, 0, FULLSTATE, 0, N_("Multistate display")},
	{0, 0, (INTBIG*)&sim_tool, VTOOL,       0, "SIM_irsim_state", 0, 0, IRSIMPARASITICS, 0, N_("IRSIM parasitics")},
	{0, 0, (INTBIG*)&sim_tool, VTOOL,       0, "SIM_irsim_parameter_file", 0, 0, 0, 0, N_("IRSIM parameter file")},
	{0, 0, 0,                  0,           0, 0, 0, 0, 0, 0, ""}
};
static OPTIONVARPROTO db_ovpsimspice[] =			/* TOOLS: Simulation Interface: Spice Options */
{
/*	{0, 0, 0,                  VNODEPROTO,  0, "SIM_spice_behave_file", 0, 0, 0, 0, N_("")},  *** facet specific ***/
	{0, 0, (INTBIG*)&sim_tool, VTOOL,       0, "SIM_dontrun", 0, 0, 0, 0, N_("Running SPICE")},
	{0, 0, (INTBIG*)&sim_tool, VTOOL,       0, "SIM_spice_level", 0, 0, 0, 0, N_("SPICE level")},
	{0, 0, (INTBIG*)&sim_tool, VTOOL,       0, "SIM_spice_state", 0, 0, SPICETYPE, 0, N_("SPICE engine")},
	{0, 0, (INTBIG*)&sim_tool, VTOOL,       0, "SIM_spice_state", 0, 0, SPICERAWFILE, 0, N_("Read rawfile output")},
	{0, 0, (INTBIG*)&sim_tool, VTOOL,       0, "SIM_spice_state", 0, 0, SPICEGLOBALPG, 0, N_("Force global VDD/GND")},
	{0, 0, 0,                  VTECHNOLOGY, 0, "SIM_spice_model_file", 0, 0, 0, 0, N_("Use header cards from file")},
	{0, 0, 0,                  VTECHNOLOGY, 0, "SIM_spice_trailer_file", 0, 0, 0, 0, N_("Use trailer cards from file")},
	{0, 0, 0,                  VTECHNOLOGY, 0, "SIM_spice_header_level1", 0, 0, 0, 0, N_("Level 1 built-in header cards")},
	{0, 0, 0,                  VTECHNOLOGY, 0, "SIM_spice_header_level2", 0, 0, 0, 0, N_("Level 2 built-in header cards")},
	{0, 0, 0,                  VTECHNOLOGY, 0, "SIM_spice_header_level3", 0, 0, 0, 0, N_("Level 3 built-in header cards")},
	{0, 0, 0,                  VTECHNOLOGY, 0, "SIM_spice_resistance", 0, 0, 0, 0, N_("Resistance")},
	{0, 0, 0,                  VTECHNOLOGY, 0, "SIM_spice_capacitance", 0, 0, 0, 0, N_("Capacitance")},
	{0, 0, 0,                  VTECHNOLOGY, 0, "SIM_spice_edge_capacitance", 0, 0, 0, 0, N_("Edge capacitance")},
	{0, 0, 0,                  VTECHNOLOGY, 0, "SIM_spice_min_resistance", 0, 0, 0, 0, N_("Min. resistance")},
	{0, 0, 0,                  VTECHNOLOGY, 0, "SIM_spice_min_capacitance", 0, 0, 0, 0, N_("Min. capacitance")},
	{0, 0, 0,                  0,           0, 0, 0, 0, 0, 0, ""}
};
static OPTIONVARPROTO db_ovpsimverilog[] =			/* TOOLS: Simulation Interface: Verilog Options */
{
/*	{0, 0, 0,                  VNODEPROTO,  0, "SIM_verilog_behave_file", 0, 0, 0, 0, N_("")},  *** facet specific ***/
	{0, 0, (INTBIG*)&sim_tool, VTOOL,       0, "SIM_verilog_state", 0, 0, VERILOGUSEASSIGN, 0, N_("Use assign construct")},
	{0, 0, (INTBIG*)&sim_tool, VTOOL,       0, "SIM_verilog_state", 0, 0, VERILOGUSETRIREG, 0, N_("Default wire is trireg")},
	{0, 0, 0,                  0,           0, 0, 0, 0, 0, 0, ""}
};
static OPTIONVARPROTO db_ovpsimfasthenry[] =		/* TOOLS: Simulation Interface: FastHenry Options */
{
	{0, 0, (INTBIG*)&sim_tool, VTOOL,       0, "SIM_fasthenry_state", 0, 0, FHUSESINGLEFREQ, 0, N_("Use single frequency")},
	{0, 0, (INTBIG*)&sim_tool, VTOOL,       0, "SIM_fasthenry_state", 0, 0, FHMAKEMULTIPOLECKT, 0, N_("Make multipole subcircuit")},
	{0, 0, (INTBIG*)&sim_tool, VTOOL,       0, "SIM_fasthenry_state", 0, 0, FHMAKEPOSTSCRIPTVIEW, 0, N_("Make PostScript view")},
	{0, 0, (INTBIG*)&sim_tool, VTOOL,       0, "SIM_fasthenry_state", 0, 0, FHMAKESPICESUBCKT, 0, N_("Make SPICE subcircuit")},
	{0, 0, (INTBIG*)&sim_tool, VTOOL,       0, "SIM_fasthenry_state", 0, 0, FHEXECUTETYPE, 0, N_("Action after writing deck")},
	{0, 0, (INTBIG*)&sim_tool, VTOOL,       0, "SIM_fasthenry_freqstart", 0, 0, 0, 0, N_("FastHenry start frequency")},
	{0, 0, (INTBIG*)&sim_tool, VTOOL,       0, "SIM_fasthenry_freqend", 0, 0, 0, 0, N_("FastHenry end frequency")},
	{0, 0, (INTBIG*)&sim_tool, VTOOL,       0, "SIM_fasthenry_runsperdecade", 0, 0, 0, 0, N_("FastHenry runs per decade")},
	{0, 0, (INTBIG*)&sim_tool, VTOOL,       0, "SIM_fasthenry_numpoles", 0, 0, 0, 0, N_("FastHenry number of poles")},
	{0, 0, (INTBIG*)&sim_tool, VTOOL,       0, "SIM_fasthenry_seglimit", 0, 0, 0, 0, N_("FastHenry segment limit")},
	{0, 0, (INTBIG*)&sim_tool, VTOOL,       0, "SIM_fasthenry_thickness", 0, 0, 0, 0, N_("FastHenry thickness")},
	{0, 0, (INTBIG*)&sim_tool, VTOOL,       0, "SIM_fasthenry_width_subdivs", 0, 0, 0, 0, N_("FastHenry width subdivisions")},
	{0, 0, (INTBIG*)&sim_tool, VTOOL,       0, "SIM_fasthenry_height_subdivs", 0, 0, 0, 0, N_("FastHenry height subdivisions")},
	{0, 0, 0,                  0,           0, 0, 0, 0, 0, 0, ""}
};
#endif
#if ERCTOOL
static OPTIONVARPROTO db_ovperc[] =					/* TOOLS: ERC: ERC Options */
{
	{0, 0, (INTBIG*)&erc_tool, VTOOL,       0, "ERC_options", 0, 0, PWELLCONOPTIONS, 0, N_("PWell contact checks")},
	{0, 0, (INTBIG*)&erc_tool, VTOOL,       0, "ERC_options", 0, 0, NWELLCONOPTIONS, 0, N_("NWell contact checks")},
	{0, 0, (INTBIG*)&erc_tool, VTOOL,       0, "ERC_options", 0, 0, PWELLONGROUND, 0, N_("Check PWell to ground")},
	{0, 0, (INTBIG*)&erc_tool, VTOOL,       0, "ERC_options", 0, 0, NWELLONPOWER, 0, N_("Check NWell to power")},
	{0, 0, 0,                  0,           0, 0, 0, 0, 0, 0, ""}
};
#endif
static OPTIONVARPROTO db_ovpncc[] =					/* TOOLS: Network: NCC Options */
{
	{0, 0, (INTBIG*)&net_tool, VTOOL,       0, "NET_ncc_options", 0, 0, NCCRECURSE, 0, N_("Recurse through hierarchy")},
	{0, 0, (INTBIG*)&net_tool, VTOOL,       0, "NET_ncc_options", 0, 0, NCCHIERARCHICAL, 0, N_("Expand hierarchy")},
	{0, 0, (INTBIG*)&net_tool, VTOOL,       0, "NET_ncc_options", 0, 0, NCCVERBOSETEXT, 0, N_("Verbose NCC (text)")},
	{0, 0, (INTBIG*)&net_tool, VTOOL,       0, "NET_ncc_options", 0, 0, NCCVERBOSEGRAPHICS, 0, N_("Verbose NCC (graphics)")},
	{0, 0, (INTBIG*)&net_tool, VTOOL,       0, "NET_ncc_options", 0, 0, NCCNOMERGEPARALLEL, 0, N_("Merge parallel components")},
	{0, 0, (INTBIG*)&net_tool, VTOOL,       0, "NET_ncc_options", 0, 0, NCCMERGESERIES, 0, N_("Merge series transistors")},
	{0, 0, (INTBIG*)&net_tool, VTOOL,       0, "NET_ncc_options", 0, 0, NCCIGNOREPWRGND, 0, N_("Ignore power and ground")},
	{0, 0, (INTBIG*)&net_tool, VTOOL,       0, "NET_ncc_options", 0, 0, NCCCHECKEXPORTNAMES, 0, N_("Check export names")},
	{0, 0, (INTBIG*)&net_tool, VTOOL,       0, "NET_ncc_options", 0, 0, NCCCHECKSIZE, 0, N_("Check component sizes")},
	{0, 0, (INTBIG*)&net_tool, VTOOL,       0, "NET_ncc_component_tolerance", 0, 0, 0, 0, N_("Size tolerance")},
	{0, 0, 0,                  0,           0, 0, 0, 0, 0, 0, ""}
};
static OPTIONVARPROTO db_ovpnetwork[] =				/* TOOLS: Network: Network Options */
{
	{0, 0, (INTBIG*)&net_tool, VTOOL,       0, "NET_options", 0, 0, NETCONPWRGND, 0, N_("Unify Power and Ground")},
	{0, 0, (INTBIG*)&net_tool, VTOOL,       0, "NET_options", 0, 0, NETCONCOMMONNAME, 0, N_("Unify all like-named nets")},
	{0, 0, (INTBIG*)&net_tool, VTOOL,       0, "NET_options", 0, 0, NETDEFBUSBASE1, 0, N_("Bus starting index")},
	{0, 0, (INTBIG*)&net_tool, VTOOL,       0, "NET_options", 0, 0, NETDEFBUSBASEDESC, 0, N_("Bus ascending/descending")},
	{0, 0, 0,                  0,           0, 0, 0, 0, 0, 0, ""}
};
#if LOGEFFTOOL
static OPTIONVARPROTO db_ovplogeffort[] =			/* TOOLS: Logical Effort: Logical Effort Options */
{
	{0, 0, (INTBIG*)&le_tool,  VTOOL,       0, "LE_state", 0, 0, DISPLAYCAPACITANCE, 0, N_("Display capacitance")},
	{0, 0, (INTBIG*)&le_tool,  VTOOL,       0, "LE_state", 0, 0, HIGHLIGHTCOMPONENTS, 0, N_("Highlight components")},
	{0, 0, (INTBIG*)&le_tool,  VTOOL,       0, "LE_maximum_stage_effort", 0, 0, 0, 0, N_("Maximum stage gain")},
	{0, 0, 0,                  VARCPROTO,   0, "LE_wire_ratio", 0, 0, 0, 0, N_("Wire ratio for each layer")},
	{0, 0, 0,                  0,           0, 0, 0, 0, 0, 0, ""}
};
#endif
#if ROUTTOOL
static OPTIONVARPROTO db_ovprouting[] =				/* TOOLS: Routing: Routing Options */
{
	{0, 0, (INTBIG*)&ro_tool,  VTOOL,       0, "ROUT_prefered_arc", 0, 0, 0, 0, N_("Prefered arc")},
	{0, 0, (INTBIG*)&ro_tool,  VTOOL,       0, "ROUT_options", 0, 0, MIMICUNROUTES, 0, N_("Mimic stitching can unstitch")},
	{0, 0, (INTBIG*)&ro_tool,  VTOOL,       0, "ROUT_options", 0, 0, MIMICIGNOREPORTS, 0, N_("Ports must match")},
	{0, 0, (INTBIG*)&ro_tool,  VTOOL,       0, "ROUT_options", 0, 0, MIMICIGNORENODETYPE, 0, N_("Node types must match")},
	{0, 0, (INTBIG*)&ro_tool,  VTOOL,       0, "ROUT_options", 0, 0, MIMICIGNORENODESIZE, 0, N_("Nodes sizes must match")},
	{0, 0, (INTBIG*)&ro_tool,  VTOOL,       0, "ROUT_options", 0, 0, MIMICINTERACTIVE, 0, N_("Mimic stitching runs interactively")},
	{0, 0, 0,                  0,           0, 0, 0, 0, 0, 0, ""}
};
#endif
#if VHDLTOOL
static OPTIONVARPROTO db_ovpvhdl[] =				/* TOOLS: VHDL: VHDL Options */
{
	{0, 0, (INTBIG*)&vhdl_tool,VTOOL,       0, "VHDL_vhdl_on_disk", 0, 0, 0, 0, N_("VHDL stored in facet")},
	{0, 0, (INTBIG*)&vhdl_tool,VTOOL,       0, "VHDL_netlist_on_disk", 0, 0, 0, 0, N_("Netlist stored in facet")},
	{0, 0, 0,                  VTECHNOLOGY, 0, "TECH_vhdl_names", 0, 0, 0, 0, N_("VHDL primitive names")},
	{0, 0, 0,                  0,           0, 0, 0, 0, 0, 0, ""}
};
#endif
#if SCTOOL
static OPTIONVARPROTO db_ovpsilcomp[] =				/* TOOLS: Silicon Compiler: Silicon Compiler Options */
{
	{0, 0, (INTBIG*)&sc_tool,  VTOOL,       0, "SC_horiz_arc", 0, 0, 0, 0, N_("Horizontal routing arc")},
	{0, 0, (INTBIG*)&sc_tool,  VTOOL,       0, "SC_vert_arc", 0, 0, 0, 0, N_("Vertical routing arc")},
	{0, 0, (INTBIG*)&sc_tool,  VTOOL,       0, "SC_l1_width", 0, 0, 0, 0, N_("Horizontal wire width")},
	{0, 0, (INTBIG*)&sc_tool,  VTOOL,       0, "SC_l2_width", 0, 0, 0, 0, N_("Vertical wire width")},
	{0, 0, (INTBIG*)&sc_tool,  VTOOL,       0, "SC_pwr_width", 0, 0, 0, 0, N_("Power wire width")},
	{0, 0, (INTBIG*)&sc_tool,  VTOOL,       0, "SC_main_pwr_width", 0, 0, 0, 0, N_("Main power wire width")},
	{0, 0, (INTBIG*)&sc_tool,  VTOOL,       0, "SC_main_pwr_rail", 0, 0, 0, 0, N_("Main power arc")},
	{0, 0, (INTBIG*)&sc_tool,  VTOOL,       0, "SC_pwell_size", 0, 0, 0, 0, N_("P-Well height")},
	{0, 0, (INTBIG*)&sc_tool,  VTOOL,       0, "SC_pwell_offset", 0, 0, 0, 0, N_("P-Well offset from bottom")},
	{0, 0, (INTBIG*)&sc_tool,  VTOOL,       0, "SC_nwell_size", 0, 0, 0, 0, N_("N-Well height")},
	{0, 0, (INTBIG*)&sc_tool,  VTOOL,       0, "SC_nwell_offset", 0, 0, 0, 0, N_("N-Well offset from top")},
	{0, 0, (INTBIG*)&sc_tool,  VTOOL,       0, "SC_via_size", 0, 0, 0, 0, N_("Via size")},
	{0, 0, (INTBIG*)&sc_tool,  VTOOL,       0, "SC_min_spacing", 0, 0, 0, 0, N_("Minimum metal spacing")},
	{0, 0, (INTBIG*)&sc_tool,  VTOOL,       0, "SC_feedthru_size", 0, 0, 0, 0, N_("Feedthrough size")},
	{0, 0, (INTBIG*)&sc_tool,  VTOOL,       0, "SC_port_x_min_dist", 0, 0, 0, 0, N_("Minimum port distance")},
	{0, 0, (INTBIG*)&sc_tool,  VTOOL,       0, "SC_active_dist", 0, 0, 0, 0, N_("Minimum active distance")},
	{0, 0, (INTBIG*)&sc_tool,  VTOOL,       0, "SC_num_rows", 0, 0, 0, 0, N_("Number of rows of cells")},
	{0, 0, 0,                  0,           0, 0, 0, 0, 0, 0, ""}
};
#endif
#if COMTOOL
static OPTIONVARPROTO db_ovpcompaction[] =			/* TOOLS: Compaction: Compaction Options */
{
	{0, 0, (INTBIG*)&com_tool, VTOOL,       0, "COM_spread", 0, 0, 0, 0, N_("Spreads")},
	{0, 0, 0,                  0,           0, 0, 0, 0, 0, 0, ""}
};
#endif
static OPTIONVARPROTO db_ovpjava[] =				/* WINDOWS: Java Options */
{
	{0, 0, (INTBIG*)&us_tool,  VTOOL,       0, "JAVA_flags", 0, 0, JAVANOCOMPILER, 0, N_("Disable compiler")},
	{0, 0, (INTBIG*)&us_tool,  VTOOL,       0, "JAVA_flags", 0, 0, JAVANOEVALUATE, 0, N_("Disable evaluation")},
	{0, 0, 0,                  0,           0, 0, 0, 0, 0, 0, ""}
};

#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 Display 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 */
#define OPTIONDRCR              0200		/* DRC Rules */
#define OPTIONJAVA              0400		/* Java Options */
#define OPTIONMSGLOC           01000		/* Save Messages Location */
#define OPTIONGENERAL          02000		/* General Options */
#define OPTIONLAYVIS           04000		/* Layer Visibility Options */
#define OPTIONNCC             010000		/* NCC Options */
#define OPTIONCDL             020000		/* CDL Options */

static 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
	{{0,OPTIONCDL},        N_("FILE: CDL Options"), db_ovpforeignfilecdl},
#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},
	{{OPTIONNEWNODE,0},    N_("EDIT: New Node Options"), db_ovpnewnode},
	{{OPTIONSELECT,0},     N_("EDIT: Selection Options"), db_ovpselect},
	{{OPTIONFACET,0},      N_("FACET: Facet Options"), db_ovpfacet},
	{{OPTIONPORT,0},       N_("EXPORT: Port Display Options"), db_ovpport},
	{{OPTIONNEWARC,0},     N_("ARC: New Arc Options"), db_ovpnewarc},
	{{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},
	{{0,OPTIONLAYVIS},     N_("WINDOWS: Layer Visibility"), db_ovplayvis},
	{{OPTIONCOLOR,0},      N_("WINDOWS: Color Options"), db_ovpcolor},
	{{OPTIONLAYPAT,0},     N_("WINDOWS: Layer Display Options"), db_ovplaypat},
	{{OPTIONTEXT,0},       N_("WINDOWS: Text Options"), db_ovptext},
	{{OPTION3D,0},         N_("WINDOWS: 3D Options"), db_ovp3d},
	{{0,OPTIONMSGLOC},     N_("WINDOWS: Messages Window Location"), db_ovpmsgloc},
	{{0,OPTIONGENERAL},    N_("INFO: General Options"), db_ovpgeneral},
	{{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 DRCTOOL
	{{OPTIONDRC,0},        N_("TOOLS: DRC Options"), db_ovpdrc},
	{{OPTIONDRCR,1},       N_("TOOLS: DRC Rules"), db_ovpdrcr},
#endif
#if SIMTOOL
	{{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 ERCTOOL
	{{OPTIONERC,0},        N_("TOOLS: ERC Options"), db_ovperc},
#endif
	{{0,OPTIONNCC},        N_("TOOLS: NCC Options"), db_ovpncc},
	{{OPTIONNETWORK,0},    N_("TOOLS: Network Options"), db_ovpnetwork},
#if LOGEFFTOOL
	{{OPTIONLOGEFFORT,0},  N_("TOOLS: Logical Effort Options"), db_ovplogeffort},
#endif
#if ROUTTOOL
	{{OPTIONROUTING,0},    N_("TOOLS: Routing Options"), db_ovprouting},
#endif
#if VHDLTOOL
	{{OPTIONVHDL,0},       N_("TOOLS: VHDL Options"), db_ovpvhdl},
#endif
#if SCTOOL
	{{OPTIONSILCOMP,0},    N_("TOOLS: Silicon Compiler Options"), db_ovpsilcomp},
#endif
#if COMTOOL
	{{OPTIONCOMPACTION,0}, N_("TOOLS: Compaction Options"), db_ovpcompaction},
#endif
	{{0,OPTIONJAVA},       N_("TOOLS: Java Options"), db_ovpjava}
};

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

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

/*
 * Routine to cache the current value of all options so that it can be determined
 * later what has changed.
 */
void cacheoptionbitvalues(void)
{
	REGISTER INTBIG i, j, 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].partial != 0) continue;
			if (ovp[j].addr == 0) continue;
			var = getval(*ovp[j].addr, ovp[j].type, -1, ovp[j].variablename);
			if (var == NOVARIABLE) continue;
			if ((var->type&VTYPE) != VINTEGER) continue;
			if ((var->type&VISARRAY) != 0)
			{
				ovp[j].initialbits1 = ((INTBIG *)var->addr)[0];
				ovp[j].initialbits2 = ((INTBIG *)var->addr)[1];
			} else
			{
				ovp[j].initialbits1 = var->addr;
				ovp[j].initialbits2 = 0;
			}
		}
	}
}

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

	retval = FALSE;
	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_tool, VTOOL, VINTEGER, us_ignoreoptionchangeskey);
			if (var != NOVARIABLE) continue;
			ovp[j].dirty = TRUE;
			retval = TRUE;
		}
	}
	return(retval);
}

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

	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) continue;
			if (ovp[j].maskbits1 != 0 || ovp[j].maskbits2 != 0)
			{
				var = getval(*ovp[j].addr, ovp[j].type, -1, ovp[j].variablename);
				if (var == NOVARIABLE) val1 = val2 = 0; else
				{
					if ((var->type&VTYPE) != VINTEGER) continue;
					if ((var->type&VISARRAY) != 0)
					{
						val1 = ((INTBIG *)var->addr)[0];
						val2 = ((INTBIG *)var->addr)[1];
					} else
					{
						val1 = var->addr;
						val2 = 0;
					}
				}
				same = 1;
				if (ovp[j].maskbits1 != 0)
				{
					if ((val1 & ovp[j].maskbits1) != (ovp[j].initialbits1&ovp[j].maskbits1)) same = 0;
				}
				if (ovp[j].maskbits2 != 0)
				{
					if ((val2 & ovp[j].maskbits2) != (ovp[j].initialbits2&ovp[j].maskbits2)) same = 0;
				}
				if (same != 0) continue;
			}
			if (namedoption == 0)
			{
				(void)initinfstr();
				(void)formatinfstr("%s:", _(db_optionvarcoms[i].command));
				if (item < 0) ttyputmsg("%s", returninfstr()); else
					DiaStuffLine(item, returninfstr());
				namedoption = 1;
			}
			(void)initinfstr();
			(void)formatinfstr("   %s", _(ovp[j].meaning));
			if (item < 0) ttyputmsg("%s", returninfstr()); else
				DiaStuffLine(item, returninfstr());
		}
	}
}

/*
 * Routine to return true if options have really changed.
 */
BOOLEAN optionshavechanged(void)
{
	REGISTER INTBIG i, j, optioncomcount, val1, val2, same;
	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].dirty) continue;
			if (ovp[j].maskbits1 != 0 || ovp[j].maskbits2 != 0)
			{
				var = getval(*ovp[j].addr, ovp[j].type, -1, ovp[j].variablename);
				if (var == NOVARIABLE) val1 = val2 = 0; else
				{
					if ((var->type&VTYPE) != VINTEGER) continue;
					if ((var->type&VISARRAY) != 0)
					{
						val1 = ((INTBIG *)var->addr)[0];
						val2 = ((INTBIG *)var->addr)[1];
					} else
					{
						val1 = var->addr;
						val2 = 0;
					}
				}
				same = 1;
				if (ovp[j].maskbits1 != 0)
				{
					if ((val1 & ovp[j].maskbits1) != (ovp[j].initialbits1&ovp[j].maskbits1)) same = 0;
				}
				if (ovp[j].maskbits2 != 0)
				{
					if ((val2 & ovp[j].maskbits2) != (ovp[j].initialbits2&ovp[j].maskbits2)) same = 0;
				}
				if (same != 0) continue;
			}
			return(TRUE);
		}
	}
	return(FALSE);
}

/*
 * 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)lib, 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_tool->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;
	}

	/* finally, cache the font association information */
	db_makefontassociations(lib);
}

/*
 * Routine to look for non-default font usage and write the font name table.
 */
void db_makefontassociations(LIBRARY *lib)
{
	REGISTER NODEPROTO *np;
	REGISTER NODEINST *ni;
	REGISTER ARCINST *ai;
	REGISTER PORTARCINST *pi;
	REGISTER PORTEXPINST *pe;
	REGISTER PORTPROTO *pp;
	REGISTER CELL *cell;
	REGISTER INTBIG *fontusage, numfonts, i, used;
	char **list, **fontassociations;

	numfonts = screengetfacelist(&list);
	fontusage = (INTBIG *)emalloc(numfonts * SIZEOFINTBIG, db_cluster);
	if (fontusage == 0) return;
	for(i=0; i<numfonts; i++) fontusage[i] = 0;
	for(np = lib->firstnodeproto; np != NONODEPROTO; np = np->nextnodeproto)
	{
		for(ni = np->firstnodeinst; ni != NONODEINST; ni = ni->nextnodeinst)
		{
			db_makefontassociationvar(ni->numvar, ni->firstvar, fontusage, numfonts);
			for(pi = ni->firstportarcinst; pi != NOPORTARCINST; pi = pi->nextportarcinst)
				db_makefontassociationvar(pi->numvar, pi->firstvar, fontusage, numfonts);
			for(pe = ni->firstportexpinst; pe != NOPORTEXPINST; pe = pe->nextportexpinst)
				db_makefontassociationvar(pe->numvar, pe->firstvar, fontusage, numfonts);
			if (ni->proto->primindex == 0)
				db_makefontassociationdescript(ni->textdescript, fontusage, numfonts);
		}
		for(ai = np->firstarcinst; ai != NOARCINST; ai = ai->nextarcinst)
			db_makefontassociationvar(ai->numvar, ai->firstvar, fontusage, numfonts);
		for(pp = np->firstportproto; pp != NOPORTPROTO; pp = pp->nextportproto)
		{
			db_makefontassociationvar(pp->numvar, pp->firstvar, fontusage, numfonts);
			db_makefontassociationdescript(pp->textdescript, fontusage, numfonts);
		}
		db_makefontassociationvar(np->numvar, np->firstvar, fontusage, numfonts);
	}
	for(cell = lib->firstcell; cell != NOCELL; cell = cell->nextcell)
		db_makefontassociationvar(cell->numvar, cell->firstvar, fontusage, numfonts);
	db_makefontassociationvar(lib->numvar, lib->firstvar, fontusage, numfonts);

	/* now look for font that are in use */
	used = 0;
	for(i=1; i<numfonts; i++)
	{
		if (fontusage[i] != 0) used++;
	}
	if (used > 0)
	{
		fontassociations = (char **)emalloc(used * (sizeof (char *)), db_cluster);
		if (fontassociations == 0) return;
		used = 0;
		for(i=1; i<numfonts; i++)
		{
			if (fontusage[i] == 0) continue;
			(void)initinfstr();
			(void)formatinfstr("%ld/%s", i, list[i]);
			(void)allocstring(&fontassociations[used], returninfstr(), db_cluster);
			used++;
		}
		nextchangequiet();
		(void)setval((INTBIG)lib, VLIBRARY, "LIB_font_associations",
			(INTBIG)fontassociations, VSTRING|VISARRAY|(used<<VLENGTHSH));
		efree((char *)fontassociations);
	}
	efree((char *)fontusage);
}

/*
 * Helper routine for "db_makefontassociations"
 */
void db_makefontassociationvar(INTSML numvar, VARIABLE *firstvar, INTBIG *fontusage, INTBIG numfonts)
{
	REGISTER INTBIG i;
	REGISTER VARIABLE *var;

	for(i=0; i<numvar; i++)
	{
		var = &firstvar[i];
		if ((var->type&VDISPLAY) != 0)
			db_makefontassociationdescript(var->textdescript, fontusage, numfonts);
	}
}

/*
 * Helper routine for "db_makefontassociations"
 */
void db_makefontassociationdescript(UINTBIG *descript, INTBIG *fontusage, INTBIG numfonts)
{
	REGISTER INTBIG face;

	face = TDGETFACE(descript);
	if (face != 0)
	{
		if (face < numfonts)
			fontusage[face]++;
	}
}

/*
 * Routine to restore the "temporary" state of all option variables (used after
 * the library has been written.
 */
void restoreoptionstate(LIBRARY *lib)
{
	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;
	}
	if (getval((INTBIG)lib, VLIBRARY, VSTRING|VISARRAY, "LIB_font_associations") != NOVARIABLE)
	{
		nextchangequiet();
		(void)delval((INTBIG)lib, VLIBRARY, "LIB_font_associations");
	}
}

/******************** PARAMETERIZED CELLS ********************/

/*
 * Routine to initialize the current list of parameterized cells.
 */
void initparameterizedcells(void)
{
	REGISTER PARCELL *pc;

	while (db_firstparcell != NOPARCELL)
	{
		pc = db_firstparcell;
		db_firstparcell = pc->nextparcell;
		efree((char *)pc->parname);
		db_freeparcell(pc);
	}
}

/*
 * Routine to check to see if parameterization "parname" of cell "cell" is in the
 * list of parameterized cells.  Returns nonzero if so.
 */
INTBIG inparameterizedcells(CELL *cell, char *parname)
{
	REGISTER PARCELL *pc;

	for(pc = db_firstparcell; pc != NOPARCELL; pc = pc->nextparcell)
	{
		if (pc->cell != cell) continue;
		if (namesame(pc->parname, parname) == 0) return(1);
	}
	return(0);
}

/*
 * Routine to add parameterization "parname" of cell "cell" to the
 * list of parameterized cells.
 */
void addtoparameterizedcells(CELL *cell, char *parname)
{
	REGISTER PARCELL *pc;

	pc = db_allocparcell();
	if (pc == NOPARCELL) return;
	pc->cell = cell;
	(void)allocstring(&pc->parname, parname, db_cluster);
	pc->nextparcell = db_firstparcell;
	db_firstparcell = pc;
}

/*
 * Routine to create a parameterized name for node instance "ni".
 * If the node is not parameterized, returns zero.
 * If it returns a name, that name must be deallocated when done.
 */
char *parameterizedname(NODEINST *ni)
{
	REGISTER INTBIG i;
	REGISTER VARIABLE *var, *evar;
	char *name;

	if (ni->proto->primindex != 0) return(0);
	for(i=0; i<ni->numvar; i++)
	{
		var = &ni->firstvar[i];
		if (TDGETISPARAM(var->textdescript) != 0) break;
	}
	if (i >= ni->numvar) return(0);
	(void)initinfstr();
	(void)addstringtoinfstr(ni->proto->cell->cellname);
	for(i=0; i<ni->numvar; i++)
	{
		var = &ni->firstvar[i];
		if (TDGETISPARAM(var->textdescript) == 0) continue;
		evar = evalvar(var);
		(void)formatinfstr("-%s-%s", truevariablename(var),
			describevariable(evar, -1, -1));
	}
	(void)allocstring(&name, returninfstr(), el_tempcluster);
	return(name);
}

/*
 * Routine to implement a parameterized cell object.  Returns
 * NOPARCELL on error.
 */
PARCELL *db_allocparcell(void)
{
	REGISTER PARCELL *pc;

	if (db_parcellfree != NOPARCELL)
	{
		pc = db_parcellfree;
		db_parcellfree = pc->nextparcell;
	} else
	{
		pc = (PARCELL *)emalloc(sizeof (PARCELL), db_cluster);
		if (pc == 0) return(NOPARCELL);
	}
	return(pc);
}

/*
 * Routine to return parameterized cell object "pc" to the free list.
 */
void db_freeparcell(PARCELL *pc)
{
	pc->nextparcell = db_parcellfree;
	db_parcellfree = pc;
}

/************************* 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, NODEINST->textdescript, PORTPROTO->textdescript,
 * and POLYGON->textdescript).  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[] =
{
	{"arraysize",       VINTEGER|VCANTSET,    (INTBIG*)&db_ptni.arraysize},
	{"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|VISARRAY|(TEXTDESCRIPTSIZE<<VLENGTHSH)|VCREF, (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},
	{"globalnetcount",  VINTEGER|VCANTSET,          (INTBIG*)&db_ptnp.globalnetcount},
	{"globalnetchar",   VINTEGER|VISARRAY|VCANTSET, (INTBIG*)&db_ptnp.globalnetchar},
	{"globalnetworks",  VNETWORK|VISARRAY|VCANTSET, (INTBIG*)&db_ptnp.globalnetworks},
	{"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|VISARRAY|(TEXTDESCRIPTSIZE<<VLENGTHSH)|VCREF,(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",        VINTEGER|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[] =
{
	{"entryisnode",     VSHORT|VCANTSET,      (INTBIG*)&db_ptgeom.entryisnode},
	{"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[] =
{
	{"cellhashtablesize", VINTEGER|VCANTSET,        (INTBIG*)&db_ptlib.cellhashtablesize},
	{"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},
	{"numcells",        VINTEGER|VCANTSET,          (INTBIG*)&db_ptlib.numcells},
	{"tailcell",        VCELL|VCANTSET,             (INTBIG*)&db_ptlib.tailcell},
	{"tailnodeproto",   VNODEPROTO|VCANTSET,        (INTBIG*)&db_ptlib.tailnodeproto},
	{"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",         VINTEGER|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 TOOL db_pttool;
static NAMEVARS db_toolvars[] =
{
	{"toolindex",       VINTEGER|VCANTSET,    (INTBIG*)&db_pttool.toolindex},
	{"toolname",        VSTRING|VCANTSET,     (INTBIG*)&db_pttool.toolname},
	{"toolstate",       VINTEGER,             (INTBIG*)&db_pttool.toolstate},
	{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|VCANTSET|VCREF, (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},
	{"globalnet",       VSHORT|VCANTSET,             (INTBIG*)&db_ptnet.globalnet},
	{"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|(8<<VLENGTHSH)|VCREF, (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},
	{"textdescript",    VINTEGER|VISARRAY|(TEXTDESCRIPTSIZE<<VLENGTHSH)|VCREF,(INTBIG*)db_ptpoly.textdescript},
	{"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->entryisnode)
	{
		case TRUE:  db_geomvars[1].type = VNODEINST|VCANTSET;   break;
		case FALSE: 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[5].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_inittoollist(TOOL *tool)
{
	if (tool == NOTOOL)
	{
		db_thisapack->state = NULLNAME;
		return;
	}
	db_thisapack->state = SPECNAME;			/* then VARNAME */
	db_thisapack->vars = db_toolvars;					/* SPECNAME */
	db_thisapack->proto = (INTBIG)&db_pttool;			/* SPECNAME */
	db_thisapack->object = (INTBIG)tool;				/* SPECNAME */
	db_thisapack->specindex = 0;						/* SPECNAME */
	db_thisapack->numvar = &tool->numvar;				/* VARNAME */
	db_thisapack->firstvar = &tool->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 true, 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, BOOLEAN lowleft)
{
	char *string;
	INTBIG cx, cy, distx, disty;
	INTBIG tsx, tsy, style;
	REGISTER INTSML saverot, savetrn;
	REGISTER GEOM *geom;
	REGISTER NODEPROTO *np;
	REGISTER PORTPROTO *pp;
	REGISTER INTBIG screen, realscreen, len, lambda;
	REGISTER NODEINST *ni;
	REGISTER ARCINST *ai;
	REGISTER LIBRARY *lib;
	REGISTER TECHNOLOGY *lambdatech;
	XARRAY trans;

	switch (type)
	{
		case VNODEINST:
			ni = (NODEINST *)addr;
			makerot(ni, trans);
			geom = ni->geom;
			lambdatech = ni->proto->tech;
			lib = ni->parent->cell->lib;
			cx = (geom->lowx + geom->highx) / 2;
			cy = (geom->lowy + geom->highy) / 2;
			break;
		case VARCINST:
			ai = (ARCINST *)addr;
			geom = ai->geom;
			lambdatech = ai->proto->tech;
			lib = ai->parent->cell->lib;
			cx = (geom->lowx + geom->highx) / 2;
			cy = (geom->lowy + geom->highy) / 2;
			break;
		case VPORTPROTO:
			pp = (PORTPROTO *)addr;
			ni = pp->subnodeinst;
			lambdatech = ni->proto->tech;
			lib = pp->parent->cell->lib;
			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;
			lambdatech = np->tech;
			lib = np->cell->lib;
			cx = cy = 0;		/* facet variables are offset from (0,0) */
			break;
	}
	if (lowleft)
	{
		distx = TDGETXOFF(var->textdescript);
		lambda = lib->lambda[lambdatech->techindex];
		distx = distx * lambda / 4;
		disty = TDGETYOFF(var->textdescript);
		disty = disty * lambda / 4;
		cx += distx;   cy += disty;
		if (type == VNODEINST || type == VPORTPROTO)
			xform(cx, cy, &cx, &cy, trans);
	}

	/* add variable name if requested */
	string = describedisplayedvariable(var, line, -1);
	screensettextinfo(win, tech, var->textdescript);
	if (*string != 0) screengettextsize(win, string, &tsx, &tsy); else
	{
		screengettextsize(win, "Xy", &tsx, &tsy);
		tsx = 0;
	}

	switch (TDGETPOS(var->textdescript))
	{
		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, TDGETROTATION(var->textdescript), 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) 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) 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) 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) 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, sslx, sshx, ssly, sshy;
	float sscalex, sscaley;
	static BOOLEAN canscalefonts, fontscalingunknown = TRUE;
	REGISTER BOOLEAN reltext;
	REGISTER INTBIG distx, disty, lambda;
	REGISTER TECHNOLOGY *tech;
	REGISTER NODEPROTO *np;
	XARRAY trans;
	INTBIG tsx, tsy, lineheight, style;
	char *string;

	if (poly->limit < 4) (void)extendpolygon(poly, 4);
	if (geom == NOGEOM)
	{
		/* facet variables are offset from (0,0) */
		lx = hx = ly = hy = 0;
		tech = el_curtech;
		lambda = el_curlib->lambda[tech->techindex];
	} else
	{
		boundobj(geom, &lx, &hx, &ly, &hy);
		lambda = figurelambda(geom);
		np = geomparent(geom);
		if (np->tech == NOTECHNOLOGY)
			np->tech = whattech(np);
		tech = np->tech;
	}
	distx = TDGETXOFF(var->textdescript);
	distx = distx * lambda / 4;
	disty = TDGETYOFF(var->textdescript);
	disty = disty * lambda / 4;
	centerobjx = (lx + hx) / 2 + distx;
	centerobjy = (ly + hy) / 2 + disty;

	if (fontscalingunknown)
	{
		fontscalingunknown = FALSE;
		canscalefonts = graphicshas(CANSCALEFONTS);
	}
	reltext = FALSE;
	if (canscalefonts)
	{
		if ((TDGETSIZE(var->textdescript)&TXTQLAMBDA) != 0) reltext = TRUE;
	}
	if (reltext)
	{
		/* set window to a sensible range */
		sslx = win->screenlx;   win->screenlx = win->uselx * lambda / 12;
		sshx = win->screenhx;   win->screenhx = win->usehx * lambda / 12;
		ssly = win->screenly;   win->screenly = win->usely * lambda / 12;
		sshy = win->screenhy;   win->screenhy = win->usehy * lambda / 12;
		sscalex = win->scalex;   sscaley = win->scaley;
		computewindowscale(win);
	}

	screensettextinfo(win, tech, var->textdescript);
	screengettextsize(win, "Xy", &tsx, &lineheight);
	if ((var->type&VISARRAY) == 0) len = 1; else
		len = getlength(var);
	screenwidth = 0;
	for(j=0; j<len; j++)
	{
		string = describedisplayedvariable(var, 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);

	if (reltext)
	{
		/* restore window */
		win->screenlx = sslx;   win->screenhx = sshx;
		win->screenly = ssly;   win->screenhy = sshy;
		win->scalex = sscalex;  win->scaley = sscaley;
	}

	switch (TDGETPOS(var->textdescript))
	{
		case VTPOSCENT:
		case VTPOSBOXED:     style = TEXTCENT;      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 (geom != NOGEOM && geom->entryisnode)
	{
		makerot(geom->entryaddr.ni, trans);
		style = rotatelabel(style, TDGETROTATION(var->textdescript), trans);
	}

	/* set X position */
	switch (style)
	{
		case TEXTCENT:				/* text is centered about grab point */
		case TEXTBOT:				/* text is above grab point */
		case TEXTTOP:				/* 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 TEXTLEFT:				/* text is to right of grab point */
		case TEXTBOTLEFT:			/* text is to upper-right of grab point */
		case TEXTTOPLEFT:			/* text is to lower-right of grab point */
			poly->xv[0] = poly->xv[1] = centerobjx;
			poly->xv[2] = poly->xv[3] = centerobjx + realobjwidth;
			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 */
			poly->xv[0] = poly->xv[1] = centerobjx - realobjwidth;
			poly->xv[2] = poly->xv[3] = centerobjx;
			break;
	}

	/* set Y position */
	switch (style)
	{
		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 */
			poly->yv[0] = poly->yv[3] = centerobjy - realobjheight/2;
			poly->yv[1] = poly->yv[2] = centerobjy + realobjheight/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 */
			poly->yv[0] = poly->yv[3] = centerobjy;
			poly->yv[1] = poly->yv[2] = centerobjy + realobjheight;
			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 */
			poly->yv[0] = poly->yv[3] = centerobjy - realobjheight;
			poly->yv[1] = poly->yv[2] = centerobjy;
			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 VTOOL:         return(db_toolvars[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, UINTBIG *textdescription)
{
	REGISTER INTBIG lambda;
	INTBIG distx, disty, plx, phx, ply, phy;
	REGISTER INTBIG 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 = TDGETXOFF(textdescription);
	distx = distx * lambda / 4;
	disty = TDGETYOFF(textdescription);
	disty = disty * lambda / 4;
	if (TDGETPOS(textdescription) == 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 (TDGETPOS(textdescription))
	{
		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, TDGETROTATION(textdescription), trans);
	}
	if (type == VPORTPROTO)
	{
		ni = ((PORTPROTO *)addr)->subnodeinst;
		makeangle(ni->rotation, ni->transpose, trans);
		poly->style = rotatelabel(poly->style, TDGETROTATION(textdescription), trans);
	}
	TDCOPY(poly->textdescript, textdescription);
	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.
 */
INTBIG db_getkey(char *name)
{
	REGISTER INTBIG 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 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;
	REGISTER NODEINST *ni;

	switch (type&VTYPE)
	{
		case VNODEINST:
			ni = (NODEINST *)addr;
			if (ni->parent == NONODEPROTO) return(db_cluster);
			return(ni->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->entryisnode)
				return(geom->entryaddr.ni->parent->cell->cluster);
			return(geom->entryaddr.ai->parent->cell->cluster);
		case VTECHNOLOGY: return(((TECHNOLOGY *)addr)->cluster);
		case VTOOL: return(((TOOL *)addr)->cluster);
		case VNETWORK: return(((NETWORK *)addr)->parent->cell->cluster);
		case VCELL: return(((CELL *)addr)->cluster);
		case VWINDOWPART: return(us_tool->cluster);
		case VWINDOWFRAME: return(us_tool->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->entryisnode) return(geom->entryaddr.ni->parent);
			return(geom->entryaddr.ai->parent);
		case VNETWORK: return(((NETWORK *)addr)->parent);
	}
	return(NONODEPROTO);
}

/*
 * 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 true if there is an error.
 */
#ifdef DEBUGMEMORY
BOOLEAN db_setvalkey(INTSML *numvar, VARIABLE **firstvar, INTBIG med, INTBIG key,
	INTBIG newaddr, INTBIG newtype, CLUSTER *cluster, char *module, INTBIG line)
#else
BOOLEAN db_setvalkey(INTSML *numvar, VARIABLE **firstvar, INTBIG med, INTBIG key,
	INTBIG newaddr, INTBIG newtype, CLUSTER *cluster)
#endif
{
	REGISTER VARIABLE *newv, *var;
	REGISTER INTBIG 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(TRUE);

		/* 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;
			TDCOPY(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;
		TDCLEAR(newv[med].textdescript);
		TDSETSIZE(newv[med].textdescript, TXTSETQLAMBDA(4));
		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;
			TDCOPY(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(TRUE);
#else
	if (db_setvalvar(var, newaddr, newtype, cluster) != 0) return(TRUE);
#endif

	return(FALSE);
}

/*
 * routine to set the value of the variable "var" to the value "newaddr" and
 * type "newtype".  Memory is allocated from cluster "cluster".  Returns
 * true upon error.
 */
#ifdef DEBUGMEMORY
BOOLEAN db_setvalvar(VARIABLE *var, INTBIG newaddr, INTBIG newtype,
	CLUSTER *cluster, char *module, INTBIG line)
#else
BOOLEAN db_setvalvar(VARIABLE *var, INTBIG newaddr, INTBIG newtype,
	CLUSTER *cluster)
#endif
{
	REGISTER INTBIG i, len;
	REGISTER INTBIG 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(TRUE);
	}
	if ((var->type&VCREF) != 0)
	{
		/* setting a fixed attribute on an object */
		if ((newtype&VISARRAY) != 0)
		{
			ttyputmsg(_("Cannot set array in C structure"));
			return(TRUE);
		}
		if (((INTBIG)(var->type&(VTYPE|VISARRAY))) != (newtype&(VTYPE|VISARRAY)))
		{
			ttyputerr(_("Type mismatch"));
			return(TRUE);
		}
#ifdef DEBUGMEMORY
		if (db_setval((char *)&newaddr, (char *)db_realaddress, newtype, cluster,
			module, line)) return(TRUE);
#else
		if (db_setval((char *)&newaddr, (char *)db_realaddress, newtype, cluster))
			return(TRUE);
#endif
		return(FALSE);
	}

	/* if this change isn't subject to undo, free previous memory now */
	if (db_donextchangequietly || db_dochangesquietly)
	{
		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)
	{
#ifdef DEBUGMEMORY
		if (db_setval((char *)&newaddr, (char *)&var->addr, VSTRING, cluster,
			module, line)) return(TRUE);
#else
		if (db_setval((char *)&newaddr, (char *)&var->addr, VSTRING, cluster))
			return(TRUE);
#endif
	} 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(TRUE);
			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(TRUE);
		}
		for(i=0; i<len; i++)
		{
			if ((newtype&VTYPE) == VSHORT)
			{
				longval = ((INTSML *)newaddr)[i];
#ifdef DEBUGMEMORY
				if (db_setval((char *)&longval, &((char *)var->addr)[i*datasize], newtype,
					cluster, module, line)) return(TRUE);
#else
				if (db_setval((char *)&longval, &((char *)var->addr)[i*datasize], newtype,
					cluster)) return(TRUE);
#endif
			} else
			{
				inaddr = (char *)(newaddr + i*datasize);
#ifdef DEBUGMEMORY
				if (db_setval(inaddr, &((char *)var->addr)[i*datasize], newtype,
					cluster, module, line)) return(TRUE);
#else
				if (db_setval(inaddr, &((char *)var->addr)[i*datasize], newtype,
					cluster)) return(TRUE);
#endif
			}
		}
	} else
	{
#ifdef DEBUGMEMORY
		if (db_setval((char *)&newaddr, (char *)&var->addr, newtype, cluster,
			module, line)) return(TRUE);
#else
		if (db_setval((char *)&newaddr, (char *)&var->addr, newtype, cluster))
			return(TRUE);
#endif
	}

	return(FALSE);
}

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

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

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

	/* 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(FALSE);
}

/*
 * 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.
 */
#ifdef DEBUGMEMORY
BOOLEAN db_setindvar(INTBIG objaddr, INTBIG objtype, VARIABLE *var, INTBIG aindex,
	INTBIG newaddr, char *module, INTBIG line)
#else
BOOLEAN db_setindvar(INTBIG objaddr, INTBIG objtype, VARIABLE *var, INTBIG aindex,
	INTBIG newaddr)
#endif
{
	REGISTER INTBIG datasize;
	REGISTER INTBIG len, oldvalue, type;
	REGISTER void *loc;

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

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

	/* 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);
#ifdef DEBUGMEMORY
	if (db_setval((char *)&newaddr, (char *)loc, type,
		db_whichcluster(objaddr, objtype), module, line)) return(TRUE);
#else
	if (db_setval((char *)&newaddr, (char *)loc, type,
		db_whichcluster(objaddr, objtype))) return(TRUE);
#endif

	/* handle change control, constraint, and broadcast */
	if (!db_donextchangequietly && !db_dochangesquietly)
	{
		/* 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);
	} else
	{
		if ((type&(VCODE1|VCODE2)) != 0 || (type&VTYPE) == VSTRING)
			efree((char *)oldvalue);
	}

	return(FALSE);
}

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

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

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

	/* 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(TRUE);

	/* 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++;
	}
#ifdef DEBUGMEMORY
	if (db_setval((char *)&newaddr, &((char *)newdata)[aindex*datasize], type,
		cluster, (char *)__FILE__, (INTBIG)__LINE__)) return(TRUE);
#else
	if (db_setval((char *)&newaddr, &((char *)newdata)[aindex*datasize], type,
		cluster)) return(TRUE);
#endif
	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);

	/* handle change control, constraint, and broadcast */
	if (!db_donextchangequietly && !db_dochangesquietly)
	{
		/* 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(FALSE);
}

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

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

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

	/* 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(TRUE);

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

	/* 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);

	/* handle change control, constraint, and broadcast */
	if (!db_donextchangequietly && !db_dochangesquietly)
	{
		/* 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(FALSE);
}

/*
 * 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".
 */
#ifdef DEBUGMEMORY
BOOLEAN db_setval(char *from, char *to, INTBIG type, CLUSTER *cluster,
	char *module, INTBIG line)
#else
BOOLEAN db_setval(char *from, char *to, INTBIG type, CLUSTER *cluster)
#endif
{
	/* character sized variable */
	if ((type&VTYPE) == VCHAR)
	{
		*to = *from;
		return(FALSE);
	}

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

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

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

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

	/* string or code variable */
#ifdef DEBUGMEMORY
	return(_allocstring((char **)to, ((char **)from)[0], cluster, module, line));
#else
	return(allocstring((char **)to, ((char **)from)[0], cluster));
#endif
}

/*
 * routine to erase a list of variables when the object is deleted
 */
void db_freevars(VARIABLE **firstvar, INTSML *numvar)
{
	REGISTER INTBIG 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).
 */
INTBIG db_binarysearch(INTSML numvar, VARIABLE *firstvar, INTBIG key)
{
	REGISTER INTBIG 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 INTBIG i, j, k;
	REGISTER INTBIG oldaddr, oldtype;
	UINTBIG olddescript[TEXTDESCRIPTSIZE];
	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;
		TDCOPY(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;
			TDCOPY(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;
			TDCOPY(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;
		TDCOPY(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 false if
 * successful, true if the object has no "firstvar/numvar".
 */
BOOLEAN 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 TOOL *tool;
	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 VTOOL:
			tool = (TOOL *)addr;
			*fir = &tool->firstvar;  *num = &tool->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(TRUE);
	}
	return(FALSE);
}

INTBIG 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, INTBIG 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 VTOOL:        (void)addstringtoinfstr(_("Tool"));          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());
}
