/*
 * ExtTech.c --
 *
 * Circuit extraction.
 * Code to read and process the sections of a technology file
 * that are specific to circuit extraction.
 *
 *     *********************************************************************
 *     * Copyright (C) 1985, 1990 Regents of the University of California. *
 *     * Permission to use, copy, modify, and distribute this              *
 *     * software and its documentation for any purpose and without        *
 *     * fee is hereby granted, provided that the above copyright          *
 *     * notice appear in all copies.  The University of California        *
 *     * makes no representations about the suitability of this            *
 *     * software for any purpose.  It is provided "as is" without         *
 *     * express or implied warranty.  Export of this software outside     *
 *     * of the United States of America may require an export license.    *
 *     *********************************************************************
 */

#ifndef lint
static char sccsid[] = "@(#)ExtTech.c	4.8 MAGIC (Berkeley) 10/26/85";
#endif  /* not lint */

#include <stdio.h>
#include <stdlib.h>		/* for strtod() */
#include <string.h>
#include <strings.h>
#include <math.h>
#include <ctype.h>		/* for isspace() */

#include "tcltk/tclmagic.h"
#include "utils/magic.h"
#include "utils/utils.h"
#include "utils/geometry.h"
#include "tiles/tile.h"
#include "utils/hash.h"
#include "database/database.h"
#include "database/databaseInt.h"
#include "utils/malloc.h"
#include "textio/textio.h"
#include "utils/tech.h"
#include "debug/debug.h"
#include "extract/extract.h"
#include "extract/extractInt.h"
#include "cif/CIFint.h"
#include "cif/cif.h"

/* Whether we are converting units from microns to lambda */
bool doConvert;

/* Current extraction style */
ExtStyle *ExtCurStyle = NULL;

/* List of all styles */
ExtKeep *ExtAllStyles = NULL;

/* Mask of all types found in the extract section */
TileTypeBitMask *allExtractTypes = NULL;

/* Forward declarations */
void extTechFinalStyle();
void ExtLoadStyle();
void ExtTechScale(int, int);

/* This is a placeholder value;  it may be approximately
 * constant across processes, or it may need to be set
 * per process.
 */
#define FRINGE_MULT  0.02

/*
 * Table used for parsing the extract section of a .tech file
 * Each line in the extract section is of a type determined by
 * its first keyword.  There is one entry in the following table
 * for each such keyword.
 */

typedef enum
{
    AREAC, CONNECT, CONTACT, CSCALE,
    DEFAULTAREACAP, DEFAULTOVERLAP, DEFAULTPERIMETER, DEFAULTSIDEOVERLAP,
    DEFAULTSIDEWALL,
    DEVICE, DEVRESIST, DISCONNECT, FET, FETRESIST, FRINGESHIELDHALO,
    HEIGHT, ANTENNA, MODEL, TIEDOWN, LAMBDA, OVERC,
    PERIMC, PLANEORDER, NOPLANEORDER, RESIST, RSCALE, SIDEHALO, SIDEOVERLAP,
    SIDEWALL, STEP, STYLE, SUBSTRATE, UNITS, VARIANT
} Key;

typedef struct
{
    const char	*k_name;
    int		 k_key;
    int		 k_minargs;
    int		 k_maxargs;
    const char	*k_usage;
} keydesc;

static const keydesc keyTable[] = {
    {"areacap",		AREAC,		3,	3,
"types capacitance"},

    {"connect",		CONNECT,	3,	3,
"types1 types1"},

    {"contact",		CONTACT,	3,	6,
"type resistance"},

    {"cscale",		CSCALE,		2,	2,
"capacitance-scalefactor"},

    {"defaultareacap",	DEFAULTAREACAP,	4,	6,
"types plane capacitance"},

    {"defaultoverlap",	DEFAULTOVERLAP,	6,	6,
"types plane otertypes otherplane capacitance"},

    {"defaultperimeter",	DEFAULTPERIMETER, 4,	6,
"types plane capacitance"},

    {"defaultsideoverlap", DEFAULTSIDEOVERLAP, 6, 6,
"types plane othertypes otherplane capacitance"},

    {"defaultsidewall",	DEFAULTSIDEWALL, 4,	5,
"types plane capacitance [offset]"},

    {"device",		DEVICE,		4,	10,
"dev-type dev-name types options..."},

    {"devresist",	DEVRESIST,	4,	4,
"type region ohms-per-square"},

    {"disconnect",	DISCONNECT,	3,	3,
"types1 types1"},

    {"fet",		FET,		8,	9,
"types terminal-types min-#-terminals name [subs-types] subs-node gscap gate-chan-cap"},

    {"fetresist",	FETRESIST,	4,	4,
"type region ohms-per-square"},

    {"fringeshieldhalo",	FRINGESHIELDHALO,  2,	2,
"distance"},

    {"height",		HEIGHT,		4,	4,
"type height-above-subtrate thickness"},

    {"antenna",		ANTENNA,	4,	6,
"type [calc-type] [antenna-ratio-proportional] antenna-ratio-const"},

    {"model",		MODEL,		2,	3,
"partial-cumulative [area-sidewall]"},

    {"tiedown",		TIEDOWN,	2,	2,
"types"},

    {"lambda",		LAMBDA,		2,	2,
"units-per-lambda"},

    {"overlap",		OVERC,		4,	5,
"toptypes bottomtypes capacitance [shieldtypes]"},

    {"perimc",		PERIMC,		4,	4,
"intypes outtypes capacitance"},

    {"planeorder",	PLANEORDER,	3,	3,
"plane index"},
    {"noplaneordering",	NOPLANEORDER,	1,	1,
"(no arguments needed)"},

    {"resist",		RESIST,		3,	4,
"types resistance"},

    {"rscale",		RSCALE,		2,	2,
"resistance-scalefactor"},

    {"sidehalo",		SIDEHALO,	2,	2,
"distance"},

    {"sideoverlap",	SIDEOVERLAP,	5,	6,
"intypes outtypes ovtypes capacitance [shieldtypes]"},

    {"sidewall",		SIDEWALL,	6,	7,
"intypes outtypes neartypes fartypes capacitance [offset]"},

    {"step",		STEP,		2,	2,
"size"},

    {"style",		STYLE,		2,	4,
"stylename"},

    {"substrate",	SUBSTRATE,	3,	5,
"types plane [subs-node]"},

    {"units",		UNITS,		2,	2,
"lambda|microns"},

    {"variants",		VARIANT,	2,	2,
"style,..."},

    {0}
};


/*
 * Table used for parsing the "device" keyword types
 *
 * (Note: "11" for max types in subcircuit is arbitrary---the parser
 * ignores max types for DEV_SUBCKT, DEV_MSUBCKT, and DEV_VERILOGA).
 */

/* types are enumerated in extract.h */

static const keydesc devTable[] = {
    {"mosfet",		DEV_MOSFET,		5,	10,
"name gate-types src-types [drn-types] sub-types|None sub-node [gscap gccap]"},

    {"bjt",		DEV_BJT,		5,	5,
"name base-types emitter-types collector-types"},

    {"capacitor",	DEV_CAP,		4,	8,
"name top-types bottom-types [sub-types|None sub-node] [[perimcap] areacap]"},

    {"capreverse",	DEV_CAPREV,		4,	8,
"name bottom-types top-types [sub-types|None sub-node] [[perimcap] areacap]"},

    {"resistor",		DEV_RES,		4,	6,
"name|None res-types terminal-types [sub-types|None sub-node]"},

    {"diode",		DEV_DIODE,		4,	6,
"name pos-types neg-types [sub-types|None sub-node]"},

    {"pdiode",		DEV_PDIODE,		4,	6,
"name pos-types neg-types [sub-types|None sub-node]"},

    {"ndiode",		DEV_NDIODE,		4,	6,
"name neg-types pos-types [sub-types|None sub-node]"},

    {"subcircuit",	DEV_SUBCKT,		3,	11,
"name dev-types [N] [term1-types ... termN-types [sub-types|None sub-node]] [options]"},

    {"rsubcircuit",	DEV_RSUBCKT,		4,	7,
"name dev-types terminal-types [sub-types|None sub-node] [options]"},

    {"msubcircuit",	DEV_MSUBCKT,		3,	11,
"name dev-types [N] [term1-types ... termN-types [sub-types|None sub-node]] [options]"},

    {"csubcircuit",	DEV_CSUBCKT,		4,	7,
"name dev-types terminal-types [sub-types|None sub-node] [options]"},

    {"dsubcircuit",	DEV_DSUBCKT,		4,	7,
"name dev-types terminal-types [sub-types|None sub-node] [options]"},

    {"veriloga",	DEV_VERILOGA,		3,	11,
"name dev-types [N] [term1-types ... termN-types [sub-types|None sub-node]] [options]"},

    {0}
};

#ifdef MAGIC_WRAPPER

/*
 * ----------------------------------------------------------------------------
 *
 * ExtCompareStyle --
 *
 *	This routine is designed to work with embedded exttosim and
 *	exttospice.  It determines whether the current extract style
 *	matches the string (picked up from the .ext file).  If so, it
 *	returns TRUE.  If not, it checks whether the style exists in
 *	the list of known files for this technology.  If so, it loads
 *	the correct style and returns TRUE.  If not, it returns FALSE.
 *
 * ----------------------------------------------------------------------------
 */

bool
ExtCompareStyle(stylename)
    char *stylename;
{
    ExtKeep *style;

    if (!strcmp(stylename, ExtCurStyle->exts_name))
	return TRUE;
    else
    {
	for (style = ExtAllStyles; style != NULL; style = style->exts_next)
	{
	    if (!strcmp(stylename, style->exts_name))
	    {
		ExtLoadStyle(stylename);
		return TRUE;
	    }
	}
    }
    return FALSE;
}

#endif /* MAGIC_WRAPPER */

/*
 * ----------------------------------------------------------------------------
 *
 * ExtGetDevInfo --
 *
 *	This routine is designed to work with the embedded exttosim and
 *	exttospice commands under the Tcl-based magic, such that all
 *	device information needed by these commands can be picked up
 *	from the current extraction style (as it should!).  This
 *	really should be set up when the extract file is read, which is
 *	why the routine is here, although this is not a very efficient
 *	way to do it (but it needs to be this way to keep backward
 *	compatibility with the non-Tcl, standalone programs ext2sim and
 *	ext2spice).
 *
 *	Note that finding the device by index ("idx") is horribly
 *	inefficient, but keeps the netlist generator separated from
 *	the extractor.  Some of this code is seriously schizophrenic,
 *	and should not be investigated too closely.
 *
 * Results:
 *	Return FALSE if no device corresponds to index "idx".  TRUE
 *	otherwise.
 *
 * Side Effects:
 *	Fills values in the argument list.
 *
 * Notes:
 *	The original sd_rclassptr has been expanded to s_rclassptr and
 *	d_rclassptr to capture asymmetric devices, bipolars, etc.  Note
 *	that this is not a general-purpose method extending beyond two
 *	(non-gate) terminals, and should be updated.
 *	
 * ----------------------------------------------------------------------------
 */

bool
ExtGetDevInfo(idx, devnameptr, devtypeptr, s_rclassptr, d_rclassptr,
		sub_rclassptr, subnameptr)
    int idx;
    char **devnameptr;	    /* Name of extracted device model */
    TileType *devtypeptr;   /* Magic tile type of device */
    short *s_rclassptr;	    /* Source (1st terminal) type only */
    short *d_rclassptr;	    /* Drain (2nd terminal) type only */
    short *sub_rclassptr;
    char **subnameptr;
{
    TileType t;
    TileTypeBitMask *rmask, *tmask;
    int n, i = 0, j;
    bool repeat, found;
    ExtDevice *devptr;
    char *locdname;
    char **uniquenamelist = (char **)mallocMagic(DBNumTypes * sizeof(char *));

    found = FALSE;
    for (t = TT_TECHDEPBASE; t < DBNumTypes; t++)
    {
	for (devptr = ExtCurStyle->exts_device[t]; devptr; devptr = devptr->exts_next)
	{
	    locdname = devptr->exts_deviceName;
	    if (locdname != NULL)
	    {
		repeat = FALSE;
		for (j = 0; j < i; j++)
		    if (!strcmp(uniquenamelist[j], locdname))
		    {
			repeat = TRUE;
			break;
		    }
		if (repeat == FALSE)
		{
		    if (i == idx)
		    {
			found = TRUE;
			break;
		    }
		    uniquenamelist[i] = locdname;
		    i++;
		}
	    }
	}
	if (found == TRUE) break;
    }
    if (t == DBNumTypes)
    {
	freeMagic(uniquenamelist);
	return FALSE;
    }
    if (devptr == NULL)
    {
	freeMagic(uniquenamelist);
	return FALSE;
    }

    if (devnameptr) *devnameptr = locdname;
    if (subnameptr) *subnameptr = devptr->exts_deviceSubstrateName;
    if (devtypeptr) *devtypeptr = t;

    tmask = &devptr->exts_deviceSDTypes[0];
    if (s_rclassptr)
    {
        *s_rclassptr = (short)(-1);	/* NO_RESCLASS */

	for (n = 0; n < ExtCurStyle->exts_numResistClasses; n++)
	{
	    rmask = &ExtCurStyle->exts_typesByResistClass[n];
	    if (TTMaskIntersect(rmask, tmask))
	    {
		*s_rclassptr = (short)n;
		break;
	    }
	}
    }

    if (d_rclassptr)
    {
	tmask = &devptr->exts_deviceSDTypes[1];
	if (TTMaskIsZero(tmask))
	{
	    /* Set source and drain resistance classes to be the same */
	    *d_rclassptr = (short)n;
	}
	else
	{
	    *d_rclassptr = (short)(-1);	/* NO_RESCLASS */

	    for (n = 0; n < ExtCurStyle->exts_numResistClasses; n++)
	    {
		rmask = &ExtCurStyle->exts_typesByResistClass[n];
		if (TTMaskIntersect(rmask, tmask))
		{
		    *d_rclassptr = (short)n;
		    break;
		}
	    }
	}
    }

    if (sub_rclassptr)
    {
	tmask = &devptr->exts_deviceSubstrateTypes;
	*sub_rclassptr = (short)(-1);	/* NO_RESCLASS */

	for (n = 0; n < ExtCurStyle->exts_numResistClasses; n++)
	{
	    rmask = &ExtCurStyle->exts_typesByResistClass[n];
	    if (TTMaskIntersect(rmask, tmask))
	    {
		*sub_rclassptr = (short)(n);
		break;
	    }
	}
    }

    freeMagic(uniquenamelist);
    return TRUE;
}

/*
 * ----------------------------------------------------------------------------
 *
 * extGetDevType --
 *
 *	Given an extraction model device name (devname), return the associated
 *	magic tiletype for the device.
 *
 * Results:
 *	Tile type that represents the device "devname" in the magic database.
 *
 * Notes:
 *	It is possible for the extract section to define multiple tile types
 *	that produce the same extracted device name, so the returned TileType
 *	is not guaranteed to be unique to the device name.
 *
 * ----------------------------------------------------------------------------
 */

TileType
extGetDevType(devname)
    char *devname;
{
    TileType t;
    ExtDevice *devptr;

    for (t = TT_TECHDEPBASE; t < DBNumTypes; t++)
	for (devptr = ExtCurStyle->exts_device[t]; devptr; devptr = devptr->exts_next)
	    if (!strcmp(devptr->exts_deviceName, devname))
		return t;

    return -1;
}

/*
 * ----------------------------------------------------------------------------
 *
 * ExtGetGateTypesMask --
 *
 *	Put a mask of FET gate types in the mask pointer argument.
 *	Return 0 on success, 1 on failure (no extraction style set)
 *
 * ----------------------------------------------------------------------------
 */

int
ExtGetGateTypesMask(mask)
    TileTypeBitMask *mask;
{
    TileType ttype;

    if (ExtCurStyle == NULL) return 1;

    TTMaskZero(mask);
    for (ttype = TT_TECHDEPBASE; ttype < DBNumTypes; ttype++)
    {
        if (TTMaskHasType(&ExtCurStyle->exts_deviceMask, ttype))
        {
            ExtDevice *devptr = ExtCurStyle->exts_device[ttype];

            for (; devptr; devptr = devptr->exts_next)
                if ((devptr->exts_deviceClass == DEV_MOSFET) ||
                        (devptr->exts_deviceClass == DEV_FET) ||
                        (devptr->exts_deviceClass == DEV_ASYMMETRIC) ||
                        (devptr->exts_deviceClass == DEV_MSUBCKT))
                    TTMaskSetType(mask, ttype);
        }
    }
    return 0;
}

/*
 * ----------------------------------------------------------------------------
 *
 * ExtGetDiffTypesMask --
 *
 *	Put a mask of diffusion types in the mask pointer argument.
 *	Return 0 on success, 1 on failure (no extraction style set)
 *
 * ----------------------------------------------------------------------------
 */

int
ExtGetDiffTypesMask(mask)
    TileTypeBitMask *mask;
{
    TileType ttype;

    if (ExtCurStyle == NULL) return 1;

    TTMaskZero(mask);
    TTMaskSetMask(mask, &ExtCurStyle->exts_antennaTieTypes);

    return 0;
}

#ifdef THREE_D
/*
 * ----------------------------------------------------------------------------
 *
 * ExtGetZAxis --
 *
 *	Get the height and thickness parameters for a layer (used by the
 *	graphics module which does not have access to internal variables
 *	in the extract section).
 *
 * Results:
 *	None
 *
 * Side Effects:
 *	Fills values "height" and "thick" in argument list.
 *
 * ----------------------------------------------------------------------------
 */

void
ExtGetZAxis(tile, height, thick)
    Tile *tile;
    float *height, *thick;
{
    TileType ttype;

    if (ExtCurStyle == NULL) return;

    ttype = TiGetLeftType(tile);        /* Ignore non-Manhattan for now */

    /* Note that w_scale is multiplied by SUBPIXEL to get fixed-point accuracy. */
    /* However, we downshift by only 9 (divide by 512) so that heights are      */
    /* exaggerated in the layout by a factor of 8 (height exaggeration is       */
    /* standard practice for VLSI cross-sections).                              */

    *height = ExtCurStyle->exts_height[ttype];
    *thick = ExtCurStyle->exts_thick[ttype];
}
#endif  /* THREE_D */

/*
 * ----------------------------------------------------------------------------
 *
 * ExtPrintPath --
 *
 *	Print the path where extraction files will be written
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	Output.
 *
 * ----------------------------------------------------------------------------
 */

void
ExtPrintPath(dolist)
    bool dolist;
{
    if (ExtLocalPath == NULL)
    {
	if (dolist)
	{
#ifdef MAGIC_WRAPPER
	    Tcl_SetObjResult(magicinterp, Tcl_NewStringObj("(none)", -1));
#else
	    TxPrintf("(none)\n");
#endif
	}
	else
	    TxPrintf("(none)\n");
    }
    else
    {
	if (dolist)
	{
#ifdef MAGIC_WRAPPER
	    Tcl_SetObjResult(magicinterp, Tcl_NewStringObj(ExtLocalPath, -1));
#else
	    TxPrintf("%s\n", ExtLocalPath);
#endif
	}
	else
	    TxPrintf("The extraction path is: %s\n", ExtLocalPath);
    }
}

/*
 * ----------------------------------------------------------------------------
 *
 * ExtSetPath --
 *
 * Set the current extraction path to 'path', unless 'path' is "none" or
 * "(none)", in which case clear the path and set it to NULL.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	Just told you.
 *
 * ----------------------------------------------------------------------------
 */

void
ExtSetPath(path)
    char *path;
{
    if (path != NULL)
    {
	if (!strcasecmp(path, "none") || !strcasecmp(path, "(none)") ||
			!strcasecmp(path, "null"))
	{
	    StrDup(&ExtLocalPath, NULL);
	    return;
	}
    }
    StrDup(&ExtLocalPath, path);
    return;
}

/*
 * ----------------------------------------------------------------------------
 *
 * ExtPrintStyle --
 *
 *	Print the available and/or current extraction styles.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	Output.
 *
 * ----------------------------------------------------------------------------
 */

void
ExtPrintStyle(dolist, doforall, docurrent)
    bool dolist;
    bool doforall;
    bool docurrent;
{
    ExtKeep *style;

    if (docurrent)
    {
	if (ExtCurStyle == NULL)
	    TxError("Error: No style is set\n");
	else
	{
	    if (!dolist) TxPrintf("The current style is \"");
#ifdef MAGIC_WRAPPER
	    if (dolist)
		Tcl_SetResult(magicinterp, (char *) ExtCurStyle->exts_name, NULL);
	    else
#endif
	    TxPrintf("%s", ExtCurStyle->exts_name);
	    if (!dolist) TxPrintf("\".\n");
	}
    }

    if (doforall)
    {
	if (!dolist) TxPrintf("The extraction styles are: ");

	for (style = ExtAllStyles; style; style = style->exts_next)
	{
	    if (dolist)
	    {
#ifdef MAGIC_WRAPPER
		Tcl_AppendElement(magicinterp, style->exts_name);
#else
		if (style != ExtAllStyles) TxPrintf(" ");
		TxPrintf("%s", style->exts_name);
#endif
	    }
	    else
	    {
		if (style != ExtAllStyles) TxPrintf(", ");
		TxPrintf("%s", style->exts_name);
	    }
	}
	if (!dolist) TxPrintf(".\n");
    }
}

/*
 * ----------------------------------------------------------------------------
 *
 * ExtSetStyle --
 *
 * Set the current extraction style to 'name', or print
 * the available and current styles if 'name' is NULL.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	Just told you.
 *
 * ----------------------------------------------------------------------------
 */

void
ExtSetStyle(name)
    char *name;
{
    ExtKeep *style, *match;
    int length;

    if (name == NULL) return;

    match = NULL;
    length = strlen(name);
    for (style = ExtAllStyles; style; style = style->exts_next)
    {
	if (strncmp(name, style->exts_name, length) == 0)
	{
	    if (match != NULL)
	    {
		TxError("Extraction style \"%s\" is ambiguous.\n", name);
		ExtPrintStyle(FALSE, TRUE, TRUE);
		return;
	    }
	    match = style;
	}
    }

    if (match != NULL)
    {
	ExtLoadStyle(match->exts_name);
	TxPrintf("Extraction style is now \"%s\"\n", name);
	return;
    }

    TxError("\"%s\" is not one of the extraction styles Magic knows.\n", name);
    ExtPrintStyle(FALSE, TRUE, TRUE);
}

/*
 * ----------------------------------------------------------------------------
 *
 * extTechStyleAlloc --
 *
 * Allocate memory for a new extract style structure.
 *
 * ----------------------------------------------------------------------------
 */

ExtStyle *
extTechStyleAlloc()
{
    ExtStyle *style;
    TileType r;

    style = (ExtStyle *) mallocMagic(sizeof (ExtStyle));
    return style;
}


/*
 * ----------------------------------------------------------------------------
 *
 * extTechStyleInit --
 *
 * Fill in the extract style structure with initial values.
 *
 * ----------------------------------------------------------------------------
 */

void
extTechStyleInit(style)
    ExtStyle *style;
{
    TileType r, s;

    style->exts_name = NULL;
    style->exts_status = TECH_NOT_LOADED;

    style->exts_sidePlanes = style->exts_overlapPlanes = 0;
    TTMaskZero(&style->exts_deviceMask);
    TTMaskZero(&style->exts_subsDevTypes);
    style->exts_activeTypes = DBAllButSpaceAndDRCBits;

    for (r = 0; r < NP; r++)
    {
	TTMaskZero(&style->exts_sideTypes[r]);
	TTMaskZero(&style->exts_overlapTypes[r]);
	style->exts_planeOrder[r] = -1;
    }

    for (r = 0; r < NT; r++)
    {
	TTMaskZero(&style->exts_nodeConn[r]);
	TTMaskZero(&style->exts_resistConn[r]);
	TTMaskZero(&style->exts_deviceConn[r]);
	style->exts_allConn[r] = DBAllTypeBits;

	style->exts_sheetResist[r] = 0;
	style->exts_cornerChop[r] = 1.0;
	style->exts_viaResist[r] = (ResValue) 0;
	style->exts_height[r] = 0.0;
	style->exts_thick[r] = 0.0;
	style->exts_areaCap[r] = (CapValue) 0;
	style->exts_overlapOtherPlanes[r] = 0;
	TTMaskZero(&style->exts_overlapOtherTypes[r]);
	TTMaskZero(&style->exts_sideEdges[r]);
	for (s = 0; s < NT; s++)
	{
	    TTMaskZero(&style->exts_sideCoupleOtherEdges[r][s]);
	    TTMaskZero(&style->exts_sideOverlapOtherTypes[r][s]);
	    style->exts_sideOverlapOtherPlanes[r][s] = 0;
	    style->exts_sideCoupleCap[r][s] = (EdgeCap *) NULL;
	    style->exts_sideOverlapCap[r][s] = (EdgeCap *) NULL;
	    style->exts_perimCap[r][s] = (CapValue) 0;
	    style->exts_overlapCap[r][s] = (CapValue) 0;
	    style->exts_overlapMult[r][s] = (float) 0;

	    TTMaskZero(&style->exts_overlapShieldTypes[r][s]);
	    style->exts_overlapShieldPlanes[r][s] = 0;
	    style->exts_sideOverlapShieldPlanes[r][s] = 0;
	}

	TTMaskZero(&style->exts_perimCapMask[r]);
#ifdef ARIEL
	TTMaskZero(&style->exts_subsTransistorTypes[r]);
#endif
	if (style->exts_device[r] != NULL)
	{
	    ExtDevice *devptr;
	    free_magic1_t mm1 = freeMagic1_init();
	    for (devptr = style->exts_device[r]; devptr; devptr = devptr->exts_next)
	    {

		if (devptr->exts_deviceSDTypes != NULL)
		    freeMagic(devptr->exts_deviceSDTypes);
		if (devptr->exts_deviceSubstrateName != (char *) NULL)
		    freeMagic(devptr->exts_deviceSubstrateName);
		if (devptr->exts_deviceName != (char *) NULL)
		    freeMagic(devptr->exts_deviceName);
		while (devptr->exts_deviceParams != (ParamList *) NULL)
		{
		    /* Parameter lists are shared.  Only free the last one! */

		    if (devptr->exts_deviceParams->pl_count > 1)
		    {
			devptr->exts_deviceParams->pl_count--;
			devptr->exts_deviceParams = (ParamList *)NULL;
		    }
		    else
		    {
			if (devptr->exts_deviceParams->pl_name != NULL)
			    freeMagic(devptr->exts_deviceParams->pl_name);
			free_magic1_t mm1_ = freeMagic1_init();
			freeMagic1(&mm1_, devptr->exts_deviceParams);
			devptr->exts_deviceParams = devptr->exts_deviceParams->pl_next;
			freeMagic1_end(&mm1_);
		    }
		}
		if (devptr->exts_deviceResist.ht_table != (HashEntry **) NULL)
		    HashKill(&devptr->exts_deviceResist);

		freeMagic1(&mm1, devptr);
	    }
	    freeMagic1_end(&mm1);
	    style->exts_device[r] = (ExtDevice *)NULL;
	}
    }

    style->exts_sideCoupleHalo = 0;
    style->exts_stepSize = 100;
    style->exts_unitsPerLambda = 100.0;
    style->exts_resistScale = 1000;
    style->exts_capScale = 1000;
    style->exts_numResistClasses = 0;

    style->exts_planeOrderStatus = needPlaneOrder ;
    TTMaskZero(&style->exts_antennaTieTypes);

    for (r = 0; r < DBNumTypes; r++)
    {
	style->exts_antennaRatio[r].areaType = (char)0;
	style->exts_antennaRatio[r].ratioGate = 0.0;
	style->exts_antennaRatio[r].ratioDiffA = 0.0;
	style->exts_antennaRatio[r].ratioDiffB = 0.0;
	style->exts_resistByResistClass[r] = (ResValue) 0;
	TTMaskZero(&style->exts_typesByResistClass[r]);
	style->exts_typesResistChanged[r] = DBAllButSpaceAndDRCBits;
	TTMaskSetType(&style->exts_typesResistChanged[r], TT_SPACE);
	style->exts_typeToResistClass[r] = -1;

    }
    doConvert = FALSE;

    /* The exts_globSubstratePlane setting of -1 will be used to set a
     * backwards-compatibility mode matching previous behavior with
     * respect to the substrate when there is no "substrate" line in
     * the techfile.
     */

    style->exts_globSubstratePlane = -1;
    style->exts_globSubstrateDefaultType = -1;
    TTMaskZero(&style->exts_globSubstrateTypes);
    TTMaskZero(&style->exts_globSubstrateShieldTypes);
    style->exts_globSubstrateName = (char *)NULL;

    /* Initialize exts_nodeConn to match DBConnectTbl, so that "connect"
     * and "disconnect" statements in the extract section can be used to
     * modify the defaults.
     */
    for (r = 0; r < NT; r++)
	style->exts_nodeConn[r] = DBConnectTbl[r];
}


/*
 * ----------------------------------------------------------------------------
 *
 * extTechStyleNew --
 *
 * Allocate a new style with zeroed technology variables.
 *
 * Results:
 *	Allocates a new ExtStyle, initializes it, and returns it.
 *
 * Side effects:
 *	See above.
 *
 * ----------------------------------------------------------------------------
 */

ExtStyle *
extTechStyleNew()
{
    ExtStyle *style;
    TileType r;

    style = extTechStyleAlloc();

    /* This entry in ExtStyle is allocated and must be initialized */
    for (r = 0; r < NT; r++)
	style->exts_device[r] = NULL;

    extTechStyleInit(style);
    return style;
}

/*
 * ----------------------------------------------------------------------------
 *
 * aToCap --
 *
 *    Utility procedure for reading capacitance values.
 *
 * Returns:
 *    A value of type CapValue.
 *
 * Side effects:
 *    none.
 * ----------------------------------------------------------------------------
 */

CapValue
aToCap(str)
    char *str;
{
    CapValue capVal;
    if (sscanf(str, "%lf", &capVal) != 1) {
	capVal = (CapValue) 0;
	TechError("Capacitance value %s must be a number\n", str);
    }
    return capVal;
}

/*
 * ----------------------------------------------------------------------------
 *
 * aToRes --
 *
 *    Utility procedure for reading resistance values.
 *
 * Returns:
 *    A value of type ResValue.
 *
 * Side effects:
 *    none.
 * ----------------------------------------------------------------------------
 */

ResValue
aToRes(str)
    char *str;
{
    ResValue resVal;
    if (sscanf(str, "%d", &resVal) != 1) {
	resVal = (ResValue) 0;
	TechError("Resistance value %s must be a number\n", str);
    }
    return resVal;
}

/*
 * ----------------------------------------------------------------------------
 *
 * ExtLoadStyle --
 *
 * Re-read the technology file to load the specified technology extraction
 * style into structure ExtCurStyle.  This is much more memory-efficient than
 * keeping a separate (and huge!) ExtStyle structure for each extraction style.
 * It incurs a complete reading of the tech file on startup and every time the
 * extraction style is changed, but we can assume that this does not happen
 * often.  The first style in the technology file is assumed to be default, so
 * that re-reading the tech file is not necessary on startup unless the default
 * extraction style is changed by a call to "extract style".
 *
 * ----------------------------------------------------------------------------
 */
void
ExtLoadStyle(stylename)
   char *stylename;
{
    SectionID invext;

    extTechStyleInit(ExtCurStyle);	/* Reinitialize and mark as not	*/
    ExtCurStyle->exts_name = stylename; /* loaded.			*/

    /* Invalidate the extract section, and reload it. 			*/
    /* The second parameter to TechSectionGetMask is NULL because	*/
    /* no other tech client sections depend on the extract section.	*/

    invext = TechSectionGetMask("extract", NULL);

    /* If microns are used as units, then the TechLoad needs to convert	*/
    /* units based on *unscaled* dimensions.  Since it gets the scale	*/
    /* factor from CIFGetOutputScale(), the CIF units need to be	*/
    /* unscaled.  This is cumbersome but ensures that the right units	*/
    /* are obtained.							*/
    CIFTechOutputScale(DBLambda[1], DBLambda[0]);

    TechLoad(NULL, invext);

    /* Put the CIF output scale units back to what they were */
    CIFTechOutputScale(DBLambda[0], DBLambda[1]);

    /* extTechFinalStyle(ExtCurStyle); */  /* Taken care of by TechLoad() */
    ExtTechScale(DBLambda[0], DBLambda[1]);
}

/*
 * ----------------------------------------------------------------------------
 *
 * ExtTechInit --
 *
 *	Ensure that all memory allocated to the extract database has
 *	been freed up.  Called before loading a new technology.
 *
 * ----------------------------------------------------------------------------
 */
void
ExtTechInit()
{
    ExtKeep *style;
    int r;

    /* Delete everything in ExtCurStyle */

    if (ExtCurStyle != NULL)
    {
	extTechStyleInit(ExtCurStyle);
	freeMagic(ExtCurStyle);
	ExtCurStyle = NULL;
    }

    /* Forget all the extract style names */

    free_magic1_t mm1 = freeMagic1_init();
    for (style = ExtAllStyles; style != NULL; style = style->exts_next)
    {
	freeMagic(style->exts_name);
	freeMagic1(&mm1, style);
    }
    freeMagic1_end(&mm1);
    ExtAllStyles = NULL;

    if (allExtractTypes == NULL)
	allExtractTypes = (TileTypeBitMask *)mallocMagic(sizeof(TileTypeBitMask));
    TTMaskZero(allExtractTypes);
}

/*
 * ----------------------------------------------------------------------------
 *
 * ExtTechSimpleAreaCap --
 *
 *	Parse the techfile line for the "defaultareacap" keyword.
 *	This is equivalent to the "areacap" line but also applies
 *	to "overlap" of types on the second plane (if specified) and
 *	all planes below it, with appropriate intervening types.
 *
 * Usage:
 *	defaultareacap types plane [[subtypes] subplane] value
 *
 *	where "types" are the types for which to compute area cap,
 *	"plane" is the plane of "types", "subplane" is a plane
 *	containing wells or any types that have the same coupling
 *	as the substrate.  If absent, it is assumed that nothing
 *	shields "types" from the subtrate.  Additional optional
 *	"subtypes" is a list of types in "subplane" that shield.
 *	If absent, then all types in "subplane" are shields to the
 *	substrate.  All types specified in the "substrate" statement
 *	as shielding substrate are automatically included.  "value"
 *	is the area capacitance in aF/um^2.
 *
 * Results:
 *	None.
 *
 * Side Effects:
 *	Adds information into the ExtCurStyle records.
 *
 * ----------------------------------------------------------------------------
 */

void
ExtTechSimpleAreaCap(argc, argv)
    int  argc;
    char *argv[];
{
    TileType s, t;
    TileTypeBitMask types, subtypes, shields;
    CapValue capVal;
    float multVal;
    int plane1, plane2, plane3, pnum1, pnum2, pnum3;
    PlaneMask pshield;

    if (ExtCurStyle->exts_planeOrderStatus != seenPlaneOrder)
    {
	TechError("Cannot parse area cap line without plane ordering!\n");
	return;
    }

    DBTechNoisyNameMask(argv[1], &types);
    TTMaskSetMask(allExtractTypes, &types);
    plane1 = DBTechNoisyNamePlane(argv[2]);
    TTMaskAndMask(&types, &DBPlaneTypes[plane1]);

    capVal = aToCap(argv[argc - 1]);

    if (argc == 4)
	plane2 = ExtCurStyle->exts_globSubstratePlane;
    else
	plane2 = DBTechNoisyNamePlane(argv[argc - 2]);

    if (argc > 5)
    {
	DBTechNoisyNameMask(argv[argc - 3], &subtypes);
	TTMaskSetMask(allExtractTypes, &subtypes);
    }
    else
	TTMaskZero(&subtypes);

    /* Part 1: Area cap */
    for (t = TT_TECHDEPBASE; t < DBNumTypes; t++)
	if (TTMaskHasType(&types, t))
	{
	    ExtCurStyle->exts_areaCap[t] = capVal;
	    ExtCurStyle->exts_overlapMult[t][0] = (float) capVal * FRINGE_MULT;
	    ExtCurStyle->exts_overlapMult[0][t] = (float) capVal * FRINGE_MULT;
	}

    if ((plane2 == -1) && (ExtCurStyle->exts_globSubstratePlane == -1)) return;
    else if (plane1 == plane2) return;  /* shouldn't happen */

    pnum1 = ExtCurStyle->exts_planeOrder[plane1];
    if (plane2 != -1)
	pnum2 = ExtCurStyle->exts_planeOrder[plane2];

    /* Part 2: Overlap cap on types equivalent to substrate */
    /* Find all types in or below plane2 (i.e., ~(space)/plane2)	   */
    /* Shield types are everything in the planes between plane1 and plane2 */

    TTMaskZero(&shields);
    pshield = 0;

    if (plane2 != -1)
    {
	for (plane3 = PL_TECHDEPBASE; plane3 < DBNumPlanes; plane3++)
	{
	    pnum3 = ExtCurStyle->exts_planeOrder[plane3];
	    if (pnum3 > pnum2 && pnum3 < pnum1)
	    {
		TTMaskSetMask(&shields, &DBPlaneTypes[plane3]);
		pshield |= PlaneNumToMaskBit(plane3);
	    }
	    else if (pnum3 <= pnum2)
	    {
		TTMaskAndMask(&subtypes, &DBPlaneTypes[plane3]);
		TTMaskClearType(&subtypes, TT_SPACE);
	    }
	    TTMaskClearType(&shields, TT_SPACE);
	}
    }

    /* Defaults from the "substrate" line, if used, and if the arguments */
    /* do not specify the plane and shielding types.			 */

    if ((ExtCurStyle->exts_globSubstratePlane != -1) && (argc == 4))
    {
	TTMaskSetMask(&subtypes, &ExtCurStyle->exts_globSubstrateTypes);
	TTMaskClearMask(&subtypes, &ExtCurStyle->exts_globSubstrateShieldTypes);
	TTMaskClearType(&subtypes, TT_SPACE);

	/* If the substrate type is used for isolated substrate regions,	*/
	/* then the substrate plane is also a shielding plane, and the 		*/
	/* default substrate type is a shielding type.				*/

	if (ExtCurStyle->exts_globSubstrateDefaultType != -1)
	{
	    pshield |= PlaneNumToMaskBit(ExtCurStyle->exts_globSubstratePlane);

	    /* The substrate default type now marks isolated regions	*/
	    /* and so is not itself a substrate type.			*/

	    TTMaskClearType(&subtypes, ExtCurStyle->exts_globSubstrateDefaultType);

	    /* All types on the substrate plane that are not substrate	*/
	    /* are by definition shielding types.			*/

	    for (t = TT_TECHDEPBASE; t < DBNumTypes; t++)
		if (DBPlane(t) == ExtCurStyle->exts_globSubstratePlane)
		    if (!TTMaskHasType(&ExtCurStyle->exts_globSubstrateTypes, t))
		    	TTMaskSetType(&shields, t);
	}
    }

    /* Now record all of the overlap capacitances */

    for (s = TT_TECHDEPBASE; s < DBNumTypes; s++)
    {
	if (TTMaskHasType(&types, s))
	{
	    /* Contact overlap caps are determined from residues */
    	    if (DBIsContact(s)) continue;

	    for (t = TT_TECHDEPBASE; t < DBNumTypes; t++)
	    {
		if (!TTMaskHasType(&subtypes, t))
		    continue;

		if (s == t) continue;	/* shouldn't happen */
		if (ExtCurStyle->exts_overlapCap[s][t] > (CapValue) 0)
		    continue;	/* redundant overlap */

		ExtCurStyle->exts_overlapCap[s][t] = capVal;
		ExtCurStyle->exts_overlapMult[s][t] = (float)capVal * FRINGE_MULT;
		ExtCurStyle->exts_overlapMult[t][s] = (float)capVal * FRINGE_MULT;
		ExtCurStyle->exts_overlapPlanes |= PlaneNumToMaskBit(plane1);
		if (plane2 != -1)
		    ExtCurStyle->exts_overlapOtherPlanes[s] |= PlaneNumToMaskBit(plane2);
		TTMaskSetType(&ExtCurStyle->exts_overlapTypes[plane1], s);
		TTMaskSetType(&ExtCurStyle->exts_overlapOtherTypes[s], t);

		ExtCurStyle->exts_overlapShieldPlanes[s][t] = pshield;
		ExtCurStyle->exts_overlapShieldTypes[s][t] = shields;
	    }
	}
    }
}

/*
 * ----------------------------------------------------------------------------
 *
 * ExtTechSimplePerimCap --
 *
 *	Parse the techfile line for the "defaultperimeter" keyword.
 *	This comprises both the "perimc" statement and the "sideoverlap"
 *	statement for overlaps to types that are effectively substrate
 *	(e.g., well, implant, marker layers, etc.)
 *
 * Usage:
 *	defaultperimeter types plane [[subtypes] subplane] value
 *
 *	where "types" are the types for which to compute fringing cap,
 *	"plane" is the plane of the types, "subplane" is an optional
 *	plane that shields "types" from substrate, and "value" is the
 *	fringing cap in aF/micron.  If "subplane" is omitted, then
 *	nothing shields "types" from the substrate.  Optional "subtypes"
 *	lists the types in "subplane" that shield.  Otherwise, it is
 *	assumed that all types in "subplane" shield "types" from the
 *	substrate.  Additionally, all types declared to shield the
 *	substrate are included.
 *
 * Results:
 *	None.
 *
 * Side Effects:
 *	Adds information into the ExtCurStyle records.
 *
 * ----------------------------------------------------------------------------
 */

void
ExtTechSimplePerimCap(argc, argv)
    int  argc;
    char *argv[];
{
    TileType r, s, t;
    TileTypeBitMask types, nottypes, subtypes, shields;
    CapValue capVal;
    int plane1, plane2, plane3, pnum1, pnum2, pnum3;
    PlaneMask pshield;
    EdgeCap *cnew;

    if (ExtCurStyle->exts_planeOrderStatus != seenPlaneOrder)
    {
	TechError("Cannot parse perimeter cap line without plane ordering!\n");
	return;
    }

    DBTechNoisyNameMask(argv[1], &types);
    TTMaskSetMask(allExtractTypes, &types);
    plane1 = DBTechNoisyNamePlane(argv[2]);

    /* As part of the "simple perimeter" simplifications, "nottypes" can
     * only be space.  This prevents perimeter edges from being seen
     * between, e.g., poly and transistor gates, or metal and metal
     * resistors.
     */
    TTMaskSetOnlyType(&nottypes, TT_SPACE);

    TTMaskAndMask(&types, &DBPlaneTypes[plane1]);

    capVal = aToCap(argv[argc - 1]);

    if (argc == 4)
	plane2 = ExtCurStyle->exts_globSubstratePlane;
    else
	plane2 = DBTechNoisyNamePlane(argv[argc - 2]);

    if (argc > 5)
    {
	DBTechNoisyNameMask(argv[argc - 3], &subtypes);
	TTMaskSetMask(allExtractTypes, &subtypes);
    }
    else if (ExtCurStyle->exts_globSubstratePlane != -1)
    {
	TTMaskZero(&subtypes);
	TTMaskSetMask(&subtypes, &ExtCurStyle->exts_globSubstrateTypes);
    }
    else
	TTMaskSetOnlyType(&subtypes, TT_SPACE);

    /* Part 1: Perimeter cap */

    for (s = TT_TECHDEPBASE; s < DBNumTypes; s++)
	for (t = 0; t < DBNumTypes; t++)
	    if (TTMaskHasType(&types, s) && TTMaskHasType(&nottypes, t))
	    {
		ExtCurStyle->exts_perimCap[s][t] = capVal;
		TTMaskSetType(&ExtCurStyle->exts_perimCapMask[s], t);
	    }

    if ((plane2 == -1) && (ExtCurStyle->exts_globSubstratePlane == -1)) return;
    else if (plane1 == plane2) return;  /* shouldn't happen */

    pnum1 = ExtCurStyle->exts_planeOrder[plane1];
    if (plane2 != -1)
	pnum2 = ExtCurStyle->exts_planeOrder[plane2];

    /* Part 2: Sidewall overlap cap on types equivalent to substrate	   */
    /* Find all types in or below plane2 (i.e., ~(space)/plane2)	   */
    /* Shield types are everything in the planes between plane1 and plane2 */

    TTMaskZero(&shields);
    pshield = 0;

    if (plane2 != -1)
    {
	for (plane3 = PL_TECHDEPBASE; plane3 < DBNumPlanes; plane3++)
	{
	    pnum3 = ExtCurStyle->exts_planeOrder[plane3];
	    if (pnum3 > pnum2 && pnum3 < pnum1)
	    {
		TTMaskSetMask(&shields, &DBPlaneTypes[plane3]);
		pshield |= PlaneNumToMaskBit(plane3);
	    }
	    else if (pnum3 <= pnum2)
	    {
		TTMaskAndMask(&subtypes, &DBPlaneTypes[plane3]);
	    }
	}
	TTMaskClearType(&shields, TT_SPACE);
	TTMaskClearType(&subtypes, TT_SPACE);
    }

    /* Defaults from the "substrate" line, if used, and if the arguments */
    /* do not specify the plane and shielding types.			 */

    if ((ExtCurStyle->exts_globSubstratePlane != -1) && (argc == 4))
    {
	TTMaskSetMask(&subtypes, &ExtCurStyle->exts_globSubstrateTypes);
	TTMaskClearMask(&subtypes, &ExtCurStyle->exts_globSubstrateShieldTypes);
	TTMaskClearType(&subtypes, TT_SPACE);

	/* If the substrate type is used for isolated substrate regions,	*/
	/* then the substrate plane is also a shielding plane, and the 		*/
	/* default substrate type is a shielding type.				*/

	if (ExtCurStyle->exts_globSubstrateDefaultType != -1)
	{
	    pshield |= PlaneNumToMaskBit(ExtCurStyle->exts_globSubstratePlane);

	    /* The substrate default type now marks isolated regions	*/
	    /* and so is not itself a substrate type.			*/

	    TTMaskClearType(&subtypes, ExtCurStyle->exts_globSubstrateDefaultType);

	    /* But space is */
	    TTMaskSetType(&subtypes, TT_SPACE);

	    /* All types on the substrate plane that are not substrate	*/
	    /* are by definition shielding types.			*/

	    for (t = TT_TECHDEPBASE; t < DBNumTypes; t++)
		if (DBPlane(t) == ExtCurStyle->exts_globSubstratePlane)
		    if (!TTMaskHasType(&ExtCurStyle->exts_globSubstrateTypes, t))
		    	TTMaskSetType(&shields, t);
	}
    }

    /* Record all of the sideoverlap capacitances */

    for (s = TT_TECHDEPBASE; s < DBNumTypes; s++)
    {
	/* Side overlap computed from residues */
    	if (DBIsContact(s)) continue;

	if (TTMaskHasType(&types, s))	// Corrected, 2/21/2017
	{
	    ExtCurStyle->exts_sidePlanes |= PlaneNumToMaskBit(plane1);
	    TTMaskSetType(&ExtCurStyle->exts_sideTypes[plane1], s);
	    TTMaskSetMask(&ExtCurStyle->exts_sideEdges[s], &nottypes);
	    for (t = 0; t < DBNumTypes; t++)
	    {
		if (!TTMaskHasType(&nottypes, t)) continue;
  		if (DBIsContact(t)) continue;

		TTMaskSetMask(&ExtCurStyle->exts_sideOverlapOtherTypes[s][t], &subtypes);
		if (plane2 != -1)
		    ExtCurStyle->exts_sideOverlapOtherPlanes[s][t] |=
				PlaneNumToMaskBit(plane2);
		cnew = (EdgeCap *) mallocMagic((unsigned) (sizeof (EdgeCap)));
		cnew->ec_cap = capVal;
		cnew->ec_offset = 0;		/* No offsets on perimeter caps */
		cnew->ec_far = shields;		/* Types that shield */
		cnew->ec_near = subtypes;	/* Types we create cap with */
		if (plane2 != -1)
		    cnew->ec_pmask = PlaneNumToMaskBit(plane2);
		else
		    cnew->ec_pmask = 0;
		cnew->ec_next = ExtCurStyle->exts_sideOverlapCap[s][t];
		ExtCurStyle->exts_sideOverlapCap[s][t] = cnew;

		for (r = TT_TECHDEPBASE; r < DBNumTypes; r++)
		    if (TTMaskHasType(&subtypes, r))
			ExtCurStyle->exts_sideOverlapShieldPlanes[s][r] |= pshield;
	    }
	}
    }
}

/*
 * ----------------------------------------------------------------------------
 *
 * ExtTechSimpleSidewallCap --
 *
 *	Parse the techfile line for the "defaultsidewall" keyword.
 *
 * Results:
 *	None.
 *
 * Side Effects:
 *	Adds information into the ExtCurStyle records.
 *
 * ----------------------------------------------------------------------------
 */

void
ExtTechSimpleSidewallCap(argc, argv)
    int   argc;
    char *argv[];
{
    /* Like ExtTechLine, but with near = types2 and far = types1 */

    TileType s, t;
    TileTypeBitMask types1, types2;
    CapValue capVal;
    EdgeCap *cnew;
    int offset;
    double doffset;
    int plane;

    DBTechNoisyNameMask(argv[1], &types1);
    TTMaskSetMask(allExtractTypes, &types1);
    plane = DBTechNoisyNamePlane(argv[2]);
    capVal = aToCap(argv[3]);

    if (argc == 5)
    {
	/* Save a value of 1000 * the offset, which will be converted
	 * appropriately to magic units like exts->sideCoupleHalo.
	 */
	sscanf(argv[4], "%lg", &doffset);
	offset = (int)(0.5 + doffset * 1000.0);
    }
    else
	offset = 0;

    // Like perimeter cap, treat only space and space-like types
    // TTMaskCom2(&types2, &types1);
    TTMaskZero(&types2);
    TTMaskSetType(&types2, TT_SPACE);

    TTMaskAndMask(&types1, &DBPlaneTypes[plane]);
    TTMaskAndMask(&types2, &DBPlaneTypes[plane]);

    if (TTMaskHasType(&types1, TT_SPACE))
	TechError("Can't have space on inside of edge [ignored]\n");

    for (s = TT_TECHDEPBASE; s < DBNumTypes; s++)
    {
	if (TTMaskHasType(&types1, s))
	{
	    ExtCurStyle->exts_sidePlanes |= PlaneNumToMaskBit(DBPlane(s));
	    TTMaskSetType(&ExtCurStyle->exts_sideTypes[DBPlane(s)], s);
	    TTMaskSetMask(&ExtCurStyle->exts_sideEdges[s], &types2);
	    for (t = 0; t < DBNumTypes; t++)
	    {
		if (!TTMaskHasType(&types2, t))
		    continue;
		TTMaskSetMask(&ExtCurStyle->exts_sideCoupleOtherEdges[s][t], &types1);
		cnew = (EdgeCap *) mallocMagic((unsigned) (sizeof (EdgeCap)));
		cnew->ec_cap = capVal;
		cnew->ec_offset = offset;
		cnew->ec_near = types2;
		cnew->ec_far = types1;
		cnew->ec_next = ExtCurStyle->exts_sideCoupleCap[s][t];
		cnew->ec_pmask = 0;
		ExtCurStyle->exts_sideCoupleCap[s][t] = cnew;
	    }
	}
    }
}

/*
 * ----------------------------------------------------------------------------
 *
 * ExtTechSimpleOverlapCap --
 *
 *	Parse the techfile line for the "defaultoverlap" keyword.
 *	This is the same as the "overlap" statement excet that shield
 *	types are determined automatically from the planeorder.
 *
 * Results:
 *	None.
 *
 * Side Effects:
 *	Adds information into the ExtCurStyle records.
 *
 * ----------------------------------------------------------------------------
 */

void
ExtTechSimpleOverlapCap(argv)
    char *argv[];
{
    TileType s, t;
    TileTypeBitMask types1, types2, shields;
    CapValue capVal;
    int plane1, plane2, plane3, pnum1, pnum2, pnum3;
    PlaneMask pshield;

    if (ExtCurStyle->exts_planeOrderStatus != seenPlaneOrder)
    {
	TechError("Cannot parse overlap cap line without plane ordering!\n");
	return;
    }

    DBTechNoisyNameMask(argv[1], &types1);
    TTMaskSetMask(allExtractTypes, &types1);
    plane1 = DBTechNoisyNamePlane(argv[2]);
    TTMaskAndMask(&types1, &DBPlaneTypes[plane1]);

    DBTechNoisyNameMask(argv[3], &types2);
    TTMaskSetMask(allExtractTypes, &types2);
    plane2 = DBTechNoisyNamePlane(argv[4]);
    TTMaskAndMask(&types2, &DBPlaneTypes[plane2]);

    capVal = aToCap(argv[5]);

    pnum1 = ExtCurStyle->exts_planeOrder[plane1];
    pnum2 = ExtCurStyle->exts_planeOrder[plane2];

    /* Find all types in or below plane2 (i.e., ~(space)/plane2)	   */
    /* Shield types are everything in the planes between plane1 and plane2 */

    TTMaskZero(&shields);

    pshield = 0;
    for (plane3 = PL_TECHDEPBASE; plane3 < DBNumPlanes; plane3++)
    {
	pnum3 = ExtCurStyle->exts_planeOrder[plane3];
	if (pnum3 > pnum2 && pnum3 < pnum1)
	{
	    TTMaskSetMask(&shields, &DBPlaneTypes[plane3]);
	    pshield |= PlaneNumToMaskBit(plane3);
	}
    }
    TTMaskClearType(&shields, TT_SPACE);

    /* Now record all of the overlap capacitances */

    for (s = TT_TECHDEPBASE; s < DBNumTypes; s++)
    {
	if (TTMaskHasType(&types1, s))
	{
	    /* Contact overlap caps are determined from residues */
  	    if (DBIsContact(s)) continue;

	    for (t = TT_TECHDEPBASE; t < DBNumTypes; t++)
	    {
		if (!TTMaskHasType(&types2, t)) continue;
  		if (DBIsContact(t)) continue;

		if (s == t) continue;	/* shouldn't happen */
		if (plane1 == plane2) continue;  /* shouldn't happen */
		if (ExtCurStyle->exts_overlapCap[s][t] > (CapValue) 0)
		    continue;	/* redundant overlap */

		ExtCurStyle->exts_overlapCap[s][t] = capVal;
		ExtCurStyle->exts_overlapMult[s][t] = (float)capVal * FRINGE_MULT;
		ExtCurStyle->exts_overlapMult[t][s] = (float)capVal * FRINGE_MULT;
		ExtCurStyle->exts_overlapPlanes |= PlaneNumToMaskBit(plane1);
		ExtCurStyle->exts_overlapOtherPlanes[s] |= PlaneNumToMaskBit(plane2);
		TTMaskSetType(&ExtCurStyle->exts_overlapTypes[plane1], s);
		TTMaskSetType(&ExtCurStyle->exts_overlapOtherTypes[s], t);

		ExtCurStyle->exts_overlapShieldPlanes[s][t] = pshield;
		ExtCurStyle->exts_overlapShieldTypes[s][t] = shields;
	    }
	}
    }
}

/*
 * ----------------------------------------------------------------------------
 *
 * ExtTechSimpleSideOverlapCap --
 *
 *	Parse the techfile line for the "defaultsideoverlap" keyword.
 *
 * Results:
 *	None.
 *
 * Side Effects:
 *	Adds information into the ExtCurStyle records.
 *
 * ----------------------------------------------------------------------------
 */

void
ExtTechSimpleSideOverlapCap(argv)
    char *argv[];
{
    TileType r, s, t;
    TileTypeBitMask types, nottypes, ov, notov, shields;
    CapValue capVal;
    int plane1, plane2, plane3, pnum1, pnum2, pnum3;
    PlaneMask pshield;
    EdgeCap *cnew;
    bool forward;

    if (ExtCurStyle->exts_planeOrderStatus != seenPlaneOrder)
    {
	TechError("Cannot parse side overlap cap line without plane ordering!\n");
	return;
    }

    DBTechNoisyNameMask(argv[1], &types);
    TTMaskSetMask(allExtractTypes, &types);
    plane1 = DBTechNoisyNamePlane(argv[2]);

    /* As part of the "simple sideoverlap" simplifications, "nottypes"
     * can only be space.  This prevents perimeter edges from being
     * seen between, e.g., poly and transistor gates, or metal and
     * metal resistors.
     */
    TTMaskSetOnlyType(&nottypes, TT_SPACE);

    TTMaskAndMask(&types,    &DBPlaneTypes[plane1]);

    DBTechNoisyNameMask(argv[3], &ov);
    TTMaskSetMask(allExtractTypes, &ov);
    plane2 = DBTechNoisyNamePlane(argv[4]);

    // TTMaskCom2(&notov, &ov);
    TTMaskZero(&notov);
    TTMaskSetType(&notov, TT_SPACE);

    TTMaskAndMask(&ov,    &DBPlaneTypes[plane2]);
    TTMaskAndMask(&notov, &DBPlaneTypes[plane2]);

    capVal = aToCap(argv[5]);

    pnum1 = ExtCurStyle->exts_planeOrder[plane1];
    pnum2 = ExtCurStyle->exts_planeOrder[plane2];

    if (pnum1 == pnum2)
    {
	TechError("Cannot have fringing capacitance between "
		"types on the same plane\n");
	return;
    }

    /* Fringing cap works both directions.  Consider the case plane1 <	*/
    /* plane2 as the "forward" case, and plane1 > plane2 as the		*/
    /* "reverse" case.							*/

    forward = (plane1 < plane2) ? TRUE : FALSE;

    /* Find all types in or below plane2 (i.e., ~(space)/plane2)	   */
    /* Shield planes are the ones between plane1 and plane2 */

    TTMaskZero(&shields);
    pshield = 0;
    for (plane3 = PL_TECHDEPBASE; plane3 < DBNumPlanes; plane3++)
    {
	pnum3 = ExtCurStyle->exts_planeOrder[plane3];
	if ((forward == FALSE) && (pnum3 > pnum2 && pnum3 < pnum1))
	{
	    TTMaskSetMask(&shields, &DBPlaneTypes[plane3]);
	    pshield |= PlaneNumToMaskBit(plane3);
	}
	else if ((forward == TRUE) && (pnum3 < pnum2 && pnum3 > pnum1))
	{
	    TTMaskSetMask(&shields, &DBPlaneTypes[plane3]);
	    pshield |= PlaneNumToMaskBit(plane3);
	}
    }
    TTMaskClearType(&shields, TT_SPACE);

    /* Now record all of the sideoverlap capacitances */

    if (TTMaskHasType(&types, TT_SPACE) || TTMaskHasType(&ov, TT_SPACE))
    {
	TechError("Overlap types can't contain space [ignored]\n");
	return;
    }

    /* Record all of the sideoverlap capacitances */

    for (s = TT_TECHDEPBASE; s < DBNumTypes; s++)
    {
	/* Side overlap computed from residues */
  	if (DBIsContact(s)) continue;

	if (TTMaskHasType(&types, s))
	{
	    ExtCurStyle->exts_sidePlanes |= PlaneNumToMaskBit(plane1);
	    TTMaskSetType(&ExtCurStyle->exts_sideTypes[plane1], s);
	    TTMaskSetMask(&ExtCurStyle->exts_sideEdges[s], &nottypes);
	    for (t = TT_SPACE; t < DBNumTypes; t++)
	    {
		if (!TTMaskHasType(&nottypes, t)) continue;
		if (DBIsContact(t)) continue;

		TTMaskSetMask(&ExtCurStyle->exts_sideOverlapOtherTypes[s][t], &ov);
		ExtCurStyle->exts_sideOverlapOtherPlanes[s][t] |=
				PlaneNumToMaskBit(plane2);
		cnew = (EdgeCap *) mallocMagic((unsigned) (sizeof (EdgeCap)));
		cnew->ec_cap = capVal;
		cnew->ec_offset = 0;		/* No offsets on overlap caps */
		cnew->ec_far = shields;		/* Types that shield */
		cnew->ec_near = ov;		/* Types we create cap with */
		cnew->ec_pmask = PlaneNumToMaskBit(plane2);
		cnew->ec_next = ExtCurStyle->exts_sideOverlapCap[s][t];
		ExtCurStyle->exts_sideOverlapCap[s][t] = cnew;

		for (r = TT_TECHDEPBASE; r < DBNumTypes; r++)
		    if (TTMaskHasType(&ov, r))
			ExtCurStyle->exts_sideOverlapShieldPlanes[s][r] |= pshield;
	    }
	}

	/* There is no "reverse case".  Overlap from A->B will be different */
	/* than B->A because it depends on the thickness of A and B.  For   */
	/* the reverse case, the defaultsideoverlap statement must be	    */
	/* repeated with the correct capacitance.			    */
    }
}

/*
 * ----------------------------------------------------------------------------
 *
 * ExtTechLine --
 *
 * Process a line from the "extract" section of a technology file.
 *
 * Each line in the extract section of a technology begins
 * with a keyword that identifies the format of the rest of
 * the line.
 *
 * The following three kinds of lines are used to define the resistance
 * and parasitic capacitance to substrate of each tile type:
 *
 *	resist	 types resistance
 *	areacap	 types capacitance
 *	perimcap inside outside capacitance
 *
 * where 'types', 'inside', and 'outside' are comma-separated lists
 * of tile types, 'resistance' is an integer giving the resistance
 * per square in milli-ohms, and 'capacitance' is an integer giving
 * capacitance (per square lambda for areacap, or per lambda perimeter
 * for perimcap) in attofarads.
 *
 * The perimeter (sidewall) capacitance depends both on the types
 * inside and outside the perimeter.  For a given 'perimcap' line,
 * any segment of perimeter with a type in 'inside' inside the
 * perimeter and a type in 'outside' ontside the perimeter will
 * have the indicated capacitance.
 *
 * Both area and perimeter capacitance computed from the information
 * above apply between a given node and the substrate beneath it, as
 * determined by extSubstrate[].
 *
 * Contact resistances are specified by:
 *
 *	contact	type	minsize	resistance
 *
 * where type is the type of contact tile, minsize is chosen so that contacts
 * that are integer multiples of minsize get an additional contact cut for each
 * increment of minsize, and resistance is in milliohms.
 *
 * Overlap coupling capacitance is specified by:
 *
 *	overlap	 toptypes bottomtypes capacitance [shieldtypes]
 *
 * where 'toptypes' and 'bottomtypes' are comma-separated lists of tile types,
 * and 'capacitance' is an integer giving capacitance in attofarads per
 * square lambda of overlap.  The sets 'toptypes' and 'bottomtypes' should
 * be disjoint.  Also, the union of the planes of 'toptypes' should be disjoint
 * from the union of the planes of 'bottomtypes'.  If 'shieldtypes' are
 * present, they should also be a comma-separated list of types, on
 * planes disjoint from those of either 'toptypes' or 'bottomtypes'.
 *
 * Whenever a tile of a type in 'toptypes' overlaps one of a type in
 * 'bottomtypes', we deduct the capacitance to substrate of the 'toptypes'
 * tile for the area of the overlap, and create an overlap capacitance
 * between the two nodes based on 'capacitance'.  When material in
 * 'shieldtypes' appears over any of this overlap area, however, we
 * only deduct the substrate capacitance; we don't create an overlap
 * capacitor.
 *
 * Sidewall coupling capacitance is specified by:
 *
 *	sidewall  intypes outtypes neartypes fartypes capacitance
 *
 * where 'intypes', 'outtypes', 'neartypes', and 'fartypes' are all comma-
 * separated lists of types, and 'capacitance' is an integer giving capacitance
 * in attofarads.  All of the tiles in all four lists should be on the same
 * plane.
 *
 * Whenever an edge of the form i|j is seen, where 'i' is in intypes and
 * 'j' is in outtypes, we search on the 'j' side for a distance of
 * ExtCurStyle->exts_sideCoupleHalo for edges with 'neartypes' on the
 * close side and 'fartypes' on the far side.  We create a capacitance
 * equal to the length of overlap, times capacitance, divided by the
 * separation between the edges (poor approximation, but better than
 * none).
 *
 * Sidewall overlap coupling capacitance is specified by:
 *
 *	sideoverlap  intypes outtypes ovtypes capacitance
 *
 * where 'intypes', 'outtypes', and 'ovtypes' are comma-separated lists
 * of types, and 'capacitance' is an integer giving capacitance in attofarads
 * per lambda.  Both intypes and outtypes should be in the same plane, and
 * ovtypes should be in a different plane from intypes and outtypes.
 *
 * The next kind of line describes transistors:
 *
 *	fet	 types terminals min-#terminals names substrate gscap gccap
 *
 * where 'types' and 'terminals' are comma-separated lists of tile types.
 * The meaning is that each type listed in 'types' is a transistor, whose
 * source and drain connect to any of the types listed in 'terminals'.
 * These transistors must have exactly min-#terminals terminals, in addition
 * to the gate (whose connectivity is specified in the system-wide connectivity
 * table in the "connect" section of the .tech file).  Currently gscap and
 * gccap are unused, but refer to the gate-source (or gate-drain) capacitance
 * and the gate-channel capacitance in units of attofarads per lambda and
 * attofarads per square lambda respectively.
 *
 * The resistances of transistors is specified by:
 *
 *	fetresist type region ohms
 *
 * where type is a type of tile that is a fet, region is a string ("linear"
 * is treated specially), and ohms is the resistance per square of the fet
 * type while operating in "region".  The values of fets in the "linear"
 * region are stored in a separate table.
 *
 * Results:
 *	Returns TRUE normally, or FALSE if the line from the
 *	technology file is so malformed that Magic should abort.
 *	Currently, we always return TRUE.
 *
 * Side effects:
 *	Initializes the per-technology variables that appear at the
 *	beginning of this file.
 *
 * ----------------------------------------------------------------------------
 */

#define MAXSD 6

    /*ARGSUSED*/
bool
ExtTechLine(sectionName, argc, argv)
    char *sectionName;
    int argc;
    char *argv[];
{
    int n, l, i, j, size, val, p1, p2, p3, nterm, iterm, class;
    PlaneMask pshield, pov;
    CapValue capVal, gscap, gccap;
    ResValue resVal;
    TileTypeBitMask types1, types2, termtypes[MAXSD];
    TileTypeBitMask near, far, ov, shield, subsTypes, idTypes;
    char *subsName, *transName, *cp, *endptr, *paramName;
    TileType s, t, t2, r, o;
    const keydesc *kp, *dv;
    HashEntry *he;
    EdgeCap *cnew;
    ExtKeep *es, *newStyle;
    ParamList *subcktParams, *newParam;
    ExtDevice *devptr;
    int refcnt;
    int offset;
    double dhalo;
    double doffset;
    bool bad;

    if (argc < 1)
    {
	TechError("Each line must begin with a keyword\n");
	return (TRUE);
    }

    n = LookupStruct(argv[0], (const LookupTable *) keyTable, sizeof keyTable[0]);
    if (n < 0)
    {
	TechError("Illegal keyword.  Legal keywords are:\n\t");
	for (n = 0; keyTable[n].k_name; n++)
	    TxError(" %s", keyTable[n].k_name);
	TxError("\n");
	return (TRUE);
    }

    kp = &keyTable[n];
    if (argc < kp->k_minargs)
	goto usage;

    /* Handle maxargs for DEVICE type separately */
    if ((argc > kp->k_maxargs) && (kp->k_key != DEVICE))
	goto usage;

    else if (argc >= 2) l = strlen(argv[1]);

    /* If ExtCurStyle is NULL, this is a first pass, and we should	*/
    /* immediately load this style as default.  Otherwise, check if	*/
    /* the style name is in the table of styles, and add it if it is	*/
    /* not.								*/

    if (kp->k_key == STYLE)
    {
	if (argc != 2)
	    if ((argc != 4) || (strncmp(argv[2], "variant", 7)))
		goto usage;

	for (newStyle = ExtAllStyles; newStyle != NULL;
		newStyle = newStyle->exts_next)
	{
	    if (!strncmp(newStyle->exts_name, argv[1], l))
		break;
	}
	if (newStyle == NULL)
	{
	    if (argc == 2)
	    {
		newStyle = (ExtKeep *)mallocMagic(sizeof(ExtKeep));
		newStyle->exts_next = NULL;
		newStyle->exts_name = StrDup((char **) NULL, argv[1]);

		if (ExtAllStyles == NULL)
		    ExtAllStyles = newStyle;
		else
		{
	            /* Append to end of style list */
	            for (es = ExtAllStyles; es->exts_next; es = es->exts_next);
	            es->exts_next = newStyle;
		}
	    }
	    else	/* Handle style variants */
	    {
		ExtKeep *saveStyle = NULL;
		char *tptr, *cptr;

		/* 4th argument is a comma-separated list of variants.	*/
		/* In addition to the default name recorded above,	*/
		/* record each of the variants.				*/

		tptr = argv[3];
		while (*tptr != '\0')
		{
		    cptr = strchr(tptr, ',');
		    if (cptr != NULL) *cptr = '\0';
		    newStyle = (ExtKeep *)mallocMagic(sizeof(ExtKeep));
		    newStyle->exts_next = NULL;
		    newStyle->exts_name = (char *)mallocMagic(l
				+ strlen(tptr) + 1);
		    sprintf(newStyle->exts_name, "%s%s", argv[1], tptr);

		    /* Remember the first variant as the default */
		    if (saveStyle == NULL) saveStyle= newStyle;

		    /* Append to end of style list */
		    if (ExtAllStyles == NULL)
			ExtAllStyles = newStyle;
		    else
		    {
			for (es = ExtAllStyles; es->exts_next; es = es->exts_next);
			es->exts_next = newStyle;
		    }

		    if (cptr == NULL)
			break;
		    else
			tptr = cptr + 1;
		}
		newStyle = saveStyle;
	    }
	}

	/* Load style as default extraction style if this is the first	*/
	/* style encountered.  Otherwise, if we are changing styles, 	*/
	/* load this style only if the name matches that in ExtCurStyle.*/

	if (ExtCurStyle == NULL)
	{
	    ExtCurStyle = extTechStyleNew();
	    ExtCurStyle->exts_name = newStyle->exts_name;
	    ExtCurStyle->exts_status = TECH_PENDING;
	}
	else if ((ExtCurStyle->exts_status == TECH_PENDING) ||
		(ExtCurStyle->exts_status == TECH_SUSPENDED))
	    /* Finished loading; stop */
	    ExtCurStyle->exts_status = TECH_LOADED;
	else if (ExtCurStyle->exts_status == TECH_NOT_LOADED)
	{
	    if (ExtCurStyle->exts_name == NULL)
	        return (FALSE);		/* Don't know what to load! */
	    else if (argc == 2)
	    {
		if (!strcmp(argv[1], ExtCurStyle->exts_name))
		     ExtCurStyle->exts_status = TECH_PENDING; 	/* load pending */
	    }
	    else if (argc == 4)
	    {
		/* Verify that the style matches one variant */
		char *tptr, *cptr;
		if (!strncmp(ExtCurStyle->exts_name, argv[1], l))
		{
		    tptr = argv[3];
		    while (*tptr != '\0')
		    {
			cptr = strchr(tptr, ',');
			if (cptr != NULL) *cptr = '\0';
			if (!strcmp(ExtCurStyle->exts_name + l, tptr))
			{
			    ExtCurStyle->exts_status = TECH_PENDING;
			    return TRUE;
			}
			if (cptr == NULL)
			    return TRUE;
			else
			    tptr = cptr + 1;
		    }
		}
	    }
	}
	return (TRUE);
    }

    /* Only continue past this point if we are loading the extraction style */
    if (ExtCurStyle == NULL) return FALSE;
    if ((ExtCurStyle->exts_status != TECH_PENDING) &&
		(ExtCurStyle->exts_status != TECH_SUSPENDED))
	return TRUE;

    /* Process "variant" lines next */

    if (kp->k_key == VARIANT)
    {
	int l;
	char *cptr, *tptr;

	/* If our style variant is not one of the ones declared */
	/* on the line, then we ignore all input until we 	*/
	/* either reach the end of the style, the end of the	*/
	/* section, or another "variant" line.			*/

	if (argc != 2) goto usage;
	tptr = argv[1];
	while (*tptr != '\0')
	{
	    cptr = strchr(tptr, ',');
	    if (cptr != NULL)
	    {
		*cptr = '\0';
		for (j = 1; isspace(*(cptr - j)); j++)
		    *(cptr - j) = '\0';
	    }

	    if (*tptr == '*')	/* Wildcard for "all variants" */
	    {
		ExtCurStyle->exts_status = TECH_PENDING;
		return TRUE;
	    }
	    else
	    {
		l = strlen(ExtCurStyle->exts_name) - strlen(tptr);
		if (!strcmp(tptr, ExtCurStyle->exts_name + l))
		{
		    ExtCurStyle->exts_status = TECH_PENDING;
		    return TRUE;
		}
	    }

	    if (cptr == NULL)
		break;
	    else
		tptr = cptr + 1;
	}
	ExtCurStyle->exts_status = TECH_SUSPENDED;
    }

    /* Anything below this line is not parsed if we're in TECH_SUSPENDED mode */
    if (ExtCurStyle->exts_status != TECH_PENDING) return TRUE;

    switch (kp->k_key)
    {
	case AREAC:
	case CONNECT:
	case CONTACT:
	case DEVRESIST:
	case DISCONNECT:
	case FET:
	case FETRESIST:
	case HEIGHT:
	case ANTENNA:
	case TIEDOWN:
	case OVERC:
	case PERIMC:
	case RESIST:
	case SIDEWALL:
	case SIDEOVERLAP:
	case SUBSTRATE:
	    DBTechNoisyNameMask(argv[1], &types1);
	    TTMaskSetMask(allExtractTypes, &types1);
	    break;
	case DEVICE:
	    DBTechNoisyNameMask(argv[3], &types1);
	    TTMaskSetMask(allExtractTypes, &types1);
	    break;
	case PLANEORDER:
	case NOPLANEORDER:
	default:
	    break;
    }

    switch (kp->k_key)
    {
	case AREAC:
	    capVal = aToCap(argv[2]);
	    for (t = TT_TECHDEPBASE; t < DBNumTypes; t++)
		if (TTMaskHasType(&types1, t))
		{
		    ExtCurStyle->exts_areaCap[t] = capVal;
		    ExtCurStyle->exts_overlapMult[t][0] = (float) capVal * FRINGE_MULT;
		    ExtCurStyle->exts_overlapMult[0][t] = (float) capVal * FRINGE_MULT;
		}
	    break;
	case CONNECT:
	    /* Parse like a line from the "connect" section */
	    DBTechNoisyNameMask(argv[2], &types2);
	    TTMaskSetMask(allExtractTypes, &types2);
	    for (t = 0; t < DBNumTypes; t++)
		if (TTMaskHasType(&types1, t))
		    for (t2 = 0; t2 < DBNumTypes; t2++)
			if (TTMaskHasType(&types2, t2))
			{
			    TTMaskSetType(&ExtCurStyle->exts_nodeConn[t], t2);
			    TTMaskSetType(&ExtCurStyle->exts_nodeConn[t2], t);
			}
	    break;
	case CONTACT:
	    /* Contact size, border, spacing deprecated (now taken from	*/
	    /* cifoutput "squares" generation parameters).		*/
	    if (argc != 3)
	    {
		if (argc == 4)
		    TxPrintf("Contact size value ignored "
				"(using GDS generation rules).\n");
		else
		    TxPrintf("Contact size, spacing, and border values ignored "
				"(using GDS generation rules).\n");
	    }

	    if (!StrIsInt(argv[argc - 1]))
	    {
		TechError("Contact resistivity %s must be an integer value "
			"(in milliohms/square).\n", argv[argc - 1]);
		break;
	    }
	    resVal = aToRes(argv[argc - 1]);
	    for (t = TT_TECHDEPBASE; t < DBNumTypes; t++)
		if (TTMaskHasType(&types1, t))
		    ExtCurStyle->exts_viaResist[t] = resVal;
	    break;
	case CSCALE:
	    ExtCurStyle->exts_capScale = strtol(argv[1], &endptr, 10);
	    if (endptr == argv[1])
	    {
		TechError("Cannot parse cap scale value \"%s\"\n", argv[1]);
		ExtCurStyle->exts_capScale = 1;
	    }
	    break;
	case DISCONNECT:
	    /* Parse like a line from the "connect" section; however,	*/
	    /* "disconnect" overrides an existing connection in the	*/
	    /* DBConnectTbl array by removing the connection between	*/
	    /* layers.							*/
	    DBTechNoisyNameMask(argv[2], &types2);
	    TTMaskSetMask(allExtractTypes, &types2);
	    for (t = 0; t < DBNumTypes; t++)
		if (TTMaskHasType(&types1, t))
		    for (t2 = 0; t2 < DBNumTypes; t2++)
			if (TTMaskHasType(&types2, t2))
			{
			    TTMaskClearType(&ExtCurStyle->exts_nodeConn[t], t2);
			    TTMaskClearType(&ExtCurStyle->exts_nodeConn[t2], t);
			}
	    break;
	case FET:

	    /* Original FET format, kept for backwards compatibility */

	    DBTechNoisyNameMask(argv[2], &termtypes[0]);
	    TTMaskSetMask(allExtractTypes, &termtypes[0]);
	    nterm = atoi(argv[3]);
	    transName = argv[4];
	    subsName = argv[5];

	    // From magic version 8.1, subs name can be a nonfunctional
	    // throwaway (e.g., "error"), so don't throw a warning.

	    cp = strchr(subsName, '!');
	    if (cp == NULL || cp[1] != '\0')
	    {
		if (strcasecmp(subsName, "error"))
		{
		    TechError("Fet substrate node %s is not a global name\n",
				subsName);
		}
	    }

	    subsTypes = DBZeroTypeBits;
	    if (sscanf(argv[6], "%lf", &capVal) != 1)
	    {
		DBTechNoisyNameMask(argv[6], &subsTypes);
		TTMaskSetMask(allExtractTypes, &subsTypes);
		gscap = aToCap(argv[7]);
		gccap = (argc > 8) ? aToCap(argv[8]) : (CapValue) 0;
	    }
	    else
	    {
		gscap = aToCap(argv[6]);
		gccap = (argc > 7) ? aToCap(argv[7]) : (CapValue) 0;
	    }

	    TTMaskSetMask(&ExtCurStyle->exts_deviceMask, &types1);
	    if (!TTMaskIsZero(&subsTypes))
		TTMaskSetMask(&ExtCurStyle->exts_subsDevTypes, &types1);

	    for (t = TT_TECHDEPBASE; t < DBNumTypes; t++)
		if (TTMaskHasType(&types1, t))
		{
		    devptr = (ExtDevice *)mallocMagic(sizeof(ExtDevice));
		    devptr->exts_deviceSDTypes = (TileTypeBitMask *)
				mallocMagic(2 * sizeof(TileTypeBitMask));
		    devptr->exts_deviceSDTypes[0] = termtypes[0];
		    devptr->exts_deviceSDTypes[1] = DBZeroTypeBits;
		    devptr->exts_deviceSDCount = nterm;
		    devptr->exts_deviceSDCap = gscap;
		    devptr->exts_deviceGateCap = gccap;
		    devptr->exts_deviceClass = DEV_FET;
		    devptr->exts_deviceName = StrDup((char **) NULL, transName);
		    devptr->exts_deviceSubstrateName =
			    StrDup((char **) NULL, subsName);
		    devptr->exts_deviceSubstrateTypes = subsTypes;
		    devptr->exts_deviceIdentifierTypes = DBZeroTypeBits;
		    devptr->exts_deviceParams = (ParamList *) NULL;
		    devptr->exts_deviceResist.ht_table = (HashEntry **) NULL;
		    HashInit(&devptr->exts_deviceResist, 8, HT_STRINGKEYS);

		    TTMaskSetMask(ExtCurStyle->exts_deviceConn + t, &types1);

		    devptr->exts_next = ExtCurStyle->exts_device[t];
		    ExtCurStyle->exts_device[t] = devptr;

#ifdef ARIEL
		    {
			int z;

			for (z = TT_TECHDEPBASE; z < DBNumTypes; z++)
			{
			    if (TTMaskHasType(&subsTypes, z))
				TTMaskSetType(&ExtCurStyle->exts_subsTransistorTypes[z],
					t);
			}
		    }
#endif
		}
	    break;

	case DEFAULTAREACAP:
	    ExtTechSimpleAreaCap(argc, argv);
	    break;
	case DEFAULTOVERLAP:
	    ExtTechSimpleOverlapCap(argv);
	    break;
	case DEFAULTPERIMETER:
	    ExtTechSimplePerimCap(argc, argv);
	    break;
	case DEFAULTSIDEOVERLAP:
	    ExtTechSimpleSideOverlapCap(argv);
	    break;
	case DEFAULTSIDEWALL:
	    ExtTechSimpleSidewallCap(argc, argv);
	    break;
	case DEVICE:

	    /* Parse second argument for device type */

	    n = LookupStruct(argv[1], (const LookupTable *)devTable, sizeof devTable[0]);
	    if (n < 0)
	    {
		TechError("Illegal device.  Legal devices are:\n\t");
		for (n = 0; devTable[n].k_name; n++)
		    TxError(" %s", devTable[n].k_name);
		TxError("\n");
		return (TRUE);
	    }

	    dv = &devTable[n];
	    if ((argc - 1) < dv->k_minargs)
		goto usage;

	    /* Parse parameters from the end of the argument list.	*/
	    /* Parameters may be provided for any device.		*/

	    /* Check final arguments for "x=y" statements showing what	*/
	    /* parameter names the device uses.				*/

	    subcktParams = NULL;
	    while ((paramName = strchr(argv[argc - 1], '=')) != NULL)
	    {
		char *mult, *offset;
		double dval;

		/* Ignore ">=" and "<=", which are handled below */ 
		if (paramName > argv[argc - 1])
		    if ((*(paramName - 1) == '>') || (*(paramName - 1) == '<'))
			break;

		paramName++;
		newParam = (ParamList *)mallocMagic(sizeof(ParamList));
		newParam->pl_count = 0;
		newParam->pl_param[0] = *argv[argc - 1];
		newParam->pl_param[1] = '\0';
		newParam->pl_maximum = -1;
		newParam->pl_minimum = 0;
		newParam->pl_offset = 0;

		if (paramName - argv[argc - 1] == 3)
		    newParam->pl_param[1] = *(argv[argc - 1] + 1);

		else if (paramName - argv[argc - 1] > 3)
		    TechError("Parameter name %s can be no more than"
			"two characters.\n", argv[argc - 1]);

		// Parameter syntax "<type>=<name>*<scale>" indicates
		// that the subcircuit has internal scaling, and the
		// extractor should multiply the parameter by this value
		// before passing it to the subcircuit.

		if ((mult = strchr(paramName, '*')) != NULL)
		{
		    *mult = '\0';
		    mult++;
		    newParam->pl_scale = atof(mult);
		}
		else
		{
		    newParam->pl_scale = 1.0;

		    /* NOTE: If allowing both scale and offset, be sure
		     * to distinguish between +/- used for offsets and
		     * +/- used as sign.
		     */

		    if ((offset = strchr(paramName, '+')) != NULL)
		    {
			*offset = '\0';
			offset++;
			dval = atof(offset);
			newParam->pl_offset = (int)(0.5 + (dval * 1000));
		    }
		    else if ((offset = strchr(paramName, '-')) != NULL)
		    {
			*offset = '\0';
			offset++;
			dval = -atof(offset);
			newParam->pl_offset = (int)(0.5 + (dval * 1000));
		    }
		    else
			newParam->pl_offset = 0;
		}

		newParam->pl_name = StrDup((char **)NULL, paramName);
		newParam->pl_next = subcktParams;
		subcktParams = newParam;
		argc--;
	    }

	    /* Check for parameter range limits in one of these forms:	*/
	    /* x>y, x<y, x>=y, x<=y.					*/

	    while (TRUE)
	    {
		ParamList *chkParam;
		char *limitstr;
		char cond;
		bool equal = FALSE;
		double dval;
		int ival;

		limitstr = strchr(argv[argc - 1], '<');
		if (limitstr == NULL)
		    limitstr = strchr(argv[argc - 1], '>');
		if (limitstr == NULL) break;

		cond = *limitstr;
		*limitstr = '\0';

		/* If the parameter exists, then modify its min/max values.
		 * If not, then create a parameter and fill in only the
		 * min/max values.
		 */

		if (limitstr - argv[argc - 1] > 3)
		{
		    TechError("Parameter name %s can be no more than"
			"two characters.\n", argv[argc - 1]);
		    break;
		}

		for (chkParam = subcktParams; chkParam; chkParam =
			chkParam->pl_next)
		    if ((chkParam->pl_param[0] == argv[argc - 1][0]) &&
				(chkParam->pl_param[1] == argv[argc - 1][1]))
			break;

		/* If there is no defined parameter with the given name
		 * to be output, then create the parameter for checking
		 * limits only.
		 */

		if (chkParam == NULL)
		{
		    newParam = (ParamList *)mallocMagic(sizeof(ParamList));
		    newParam->pl_count = 0;
		    newParam->pl_param[0] = argv[argc - 1][0];
		    newParam->pl_param[1] = argv[argc - 1][1];
		    newParam->pl_maximum = -1;
		    newParam->pl_minimum = 0;
		    newParam->pl_name = NULL;
		    newParam->pl_scale = 1.0;
		    newParam->pl_offset = 0;

		    newParam->pl_next = subcktParams;
		    subcktParams = newParam;
		    chkParam = newParam;
		}

		/* Change limit */

		limitstr++;
		if (*limitstr == '=')
		{
		    equal = TRUE;
		    limitstr++;
		}
		if (sscanf(limitstr, "%lg", &dval) != 1)
		{
		    TxError("Non-numeric limit \"%s\" for parameter \"%c%s\".\n",
				limitstr, cond, argv[argc - 1]);
		    break;
		}

		/* Convert dval to internal (integer) units.  Scale up by */
		/* 1000 so the value can be converted if it's in microns  */
		/* without losing precision.				  */
		ival = (int)(0.5 + (dval * 1000));

		/* Make adjustment for greater than or less than */
		if (cond == '>')
		{
		    if (!equal) ival++;
		    chkParam->pl_minimum = ival;
		}
		else
		{
		    if (!equal) ival--;
		    chkParam->pl_maximum = ival;
		}

		/* Move to next argument */
		argc--;
	    }

	    /* If the last entry before any parameters starts with '+',	*/
	    /* then use it to set the identity marker.	Otherwise, the	*/
	    /* identity marker is NULL.					*/

	    idTypes = DBZeroTypeBits;
	    if (*argv[argc - 1] == '+')
	    {
		if ((DBTechNameMask(argv[argc - 1] + 1, &idTypes)) == 0)
		    idTypes = DBZeroTypeBits;
		argc--;
	    }

	    class = dv->k_key;

	    /* Note:  This check has been removed.  Parameters for non-	*/
	    /* subcircuit devices are allowed for support of CDL	*/
	    /* netlists, which uses arbitrary subcircuit-like		*/
	    /* parameters combined with a SPICE-like device prefix.	*/
#if 0
	    /* Check the number of arguments after splitting out	*/
	    /* parameter entries.  There is no limit on arguments in	*/
	    /* DEV_SUBCKT, DEV_MSUBCKT, and DEV_VERILOGA.		*/

	    switch (class)
	    {
		case DEV_SUBCKT:
		case DEV_MSUBCKT:
		case DEV_VERILOGA:
		    break;
		default:
		    /* If parameters were saved but the	*/
		    /* argument list indicates a bad	*/
		    /* device entry, then free up the	*/
		    /* parameters.			*/

		    if ((argc - 1) > dv->k_maxargs)
		    {
			while (subcktParams != NULL)
			{
			    if (subcktParams->pl_name != NULL)
				freeMagic(subcktParams->pl_name);
			    free_magic1_t mm1 = freeMagic1_init();
			    freeMagic1(&mm1, subcktParams);
			    subcktParams = subcktParams->pl_next;
			    freeMagic1_end(&mm1);
			}
			goto usage;
		    }
		    break;
	    }
#endif

	    gscap = (CapValue) 0;
	    gccap = (CapValue) 0;
	    subsName = NULL;
	    subsTypes = DBZeroTypeBits;
	    transName = argv[2];

	    switch (dv->k_key)
	    {
		case DEV_BJT:
		    DBTechNoisyNameMask(argv[4], &termtypes[0]); /* emitter */
		    TTMaskSetMask(allExtractTypes, &termtypes[0]);
		    termtypes[1] = DBZeroTypeBits;
		    DBTechNoisyNameMask(argv[5], &subsTypes);	 /* collector */
		    TTMaskSetMask(allExtractTypes, &subsTypes);
		    nterm = 1;	/* emitter is the only "terminal type" expected */
		    break;
		case DEV_MOSFET:
		    if ((argc > 7) && (!StrIsNumeric(argv[7])))
		    {
			/* Asymmetric device with different source and drain types */

			DBTechNoisyNameMask(argv[4], &termtypes[0]); /* source */
			TTMaskSetMask(allExtractTypes, &termtypes[0]);
			DBTechNoisyNameMask(argv[5], &termtypes[1]); /* drain */
			TTMaskSetMask(allExtractTypes, &termtypes[1]);
			TTMaskAndMask3(&termtypes[2], &termtypes[0], &termtypes[1]);

			if (TTMaskEqual(&termtypes[0], &termtypes[1]))
			    termtypes[1] = DBZeroTypeBits;	/* Make it symmetric */
			else if (!TTMaskIsZero(&termtypes[2]))
			{
			    class = DEV_ASYMMETRIC;
			    TechError("Device mosfet %s has overlapping drain"
				" and source types!\n", transName);
			    /* Should this device be disabled? */
			}
			else
			    class = DEV_ASYMMETRIC;
			termtypes[2] = DBZeroTypeBits;
			if (strcmp(argv[6], "None"))
		    	DBTechNoisyNameMask(argv[6], &subsTypes);   /* substrate */
			TTMaskSetMask(allExtractTypes, &subsTypes);
			subsName = argv[7];
			if (argc > 8) gscap = aToCap(argv[8]);
			if (argc > 9) gccap = aToCap(argv[9]);
			nterm = 2;
		    }
		    else
		    {
			/* Normal symmetric device with swappable source/drain */

			DBTechNoisyNameMask(argv[4], &termtypes[0]); /* source/drain */
			TTMaskSetMask(allExtractTypes, &termtypes[0]);
			termtypes[1] = DBZeroTypeBits;
			if (strcmp(argv[5], "None"))
			{
			    DBTechNoisyNameMask(argv[5], &subsTypes);   /* substrate */
			    TTMaskSetMask(allExtractTypes, &subsTypes);
			}
			if (argc > 6) subsName = argv[6];
			if (argc > 7) gscap = aToCap(argv[7]);
			if (argc > 8) gccap = aToCap(argv[8]);
			/* nterm = 1; */	/* Symmetric devices can be MOScaps */
			nterm = 2;
		    }
		    break;

		case DEV_DIODE:
		case DEV_PDIODE:
		case DEV_NDIODE:
		    DBTechNoisyNameMask(argv[4], &termtypes[0]); /* negative types */
		    TTMaskSetMask(allExtractTypes, &termtypes[0]);
		    termtypes[1] = DBZeroTypeBits;
		    nterm = 1;
		    if ((argc > 4) && strcmp(argv[4], "None"))
		    {
			DBTechNoisyNameMask(argv[4], &subsTypes);   /* substrate */
			TTMaskSetMask(allExtractTypes, &subsTypes);
		    }
		    else
			subsTypes = DBZeroTypeBits;
		    if (argc > 5) subsName = argv[5];
		    break;

		case DEV_RES:
		    DBTechNoisyNameMask(argv[4], &termtypes[0]); /* terminals */
		    TTMaskSetMask(allExtractTypes, &termtypes[0]);
		    termtypes[1] = DBZeroTypeBits;
		    nterm = 2;
		    if ((argc > 5) && strcmp(argv[5], "None"))
		    {
			DBTechNoisyNameMask(argv[5], &subsTypes);   /* substrate */
			TTMaskSetMask(allExtractTypes, &subsTypes);
		    }
		    else
			subsTypes = DBZeroTypeBits;
		    if (argc > 6) subsName = argv[6];
		    break;

		case DEV_CAP:
		case DEV_CAPREV:
		    DBTechNoisyNameMask(argv[4], &termtypes[0]); /* bottom */
		    TTMaskSetMask(allExtractTypes, &termtypes[0]);
		    termtypes[1] = DBZeroTypeBits;
		    nterm = 1;

		    /* If argv[argc - 1] is a numerical value, then it is
		     * an area cap value and may be followed by another
		     * numerical value, the perimeter cap.
		     */

		    if ((argc > 5) && StrIsNumeric(argv[argc - 1]))
		    {
			gccap = aToCap(argv[argc - 1]);		/* area cap */
			argc--;

			if ((argc > 5) && StrIsNumeric(argv[argc - 1]))
			{
			    gscap = aToCap(argv[argc - 1]);	/* perimeter cap */
			    argc--;
			}
		    }

		    if ((argc > 5) && strcmp(argv[5], "None"))
		    {
			/* substrate */
			DBTechNoisyNameMask(argv[5], &subsTypes);
			TTMaskSetMask(allExtractTypes, &subsTypes);
		    }
		    else
			subsTypes = DBZeroTypeBits;
		    if (argc > 6) subsName = argv[6];
		    break;

		case DEV_SUBCKT:
		case DEV_MSUBCKT:
		case DEV_VERILOGA:
		    // Determine if [substrate, name] optional arguments
		    // are present by checking if the last argument
		    // parses as a layer list.

		    if (DBTechNameMask(argv[argc - 1], &termtypes[0]) <= 0)
		    {
			if (strcmp(argv[argc - 2], "None"))
			{
			    DBTechNoisyNameMask(argv[argc - 2], &subsTypes);
			    TTMaskSetMask(allExtractTypes, &subsTypes);
			}
			else
			    subsTypes = DBZeroTypeBits;
			subsName = argv[argc - 1];
			argc -= 2;
		    }

		    if (StrIsInt(argv[4]))
		    {
			nterm = atoi(argv[4]);
			iterm = 5;
			if (nterm > argc - 5)
			{
			    TechError("Not enough terminals for subcircuit, "
					"%d were required, %d found.\n",
					nterm, argc - 5);
			    nterm = argc - 5;
			}
		    }
		    else
		    {
			nterm = argc - 4;
			iterm = 4;
		    }

		    /* terminals */
		    for (i = iterm; i < iterm + nterm; i++)
		    {
			DBTechNoisyNameMask(argv[i], &termtypes[i - iterm]);
			TTMaskSetMask(allExtractTypes, &termtypes[i - iterm]);
		    }
		    termtypes[nterm] = DBZeroTypeBits;

		    if (nterm == 0) i++;

		    // Type MSUBCKT:  If source and drain are symmetric (both
		    // have the same types), then they must both be declared,
		    // but only one is used (same policy as "device mosfet").

		    if ((nterm == 2) && TTMaskEqual(&termtypes[nterm - 1],
				&termtypes[nterm - 2]))
			termtypes[nterm - 1] = DBZeroTypeBits;

		    break;

		case DEV_RSUBCKT:
		case DEV_CSUBCKT:
		case DEV_DSUBCKT:
		    nterm = (dv->k_key == DEV_RSUBCKT) ? 2 : 1;
		    DBTechNoisyNameMask(argv[4], &termtypes[0]);	/* terminals */
		    TTMaskSetMask(allExtractTypes, &termtypes[0]);
		    termtypes[1] = DBZeroTypeBits;

		    if ((argc > 5) && strcmp(argv[5], "None") &&
				(strchr(argv[5], '=') == NULL))
		    {
			DBTechNoisyNameMask(argv[5], &subsTypes);   /* substrate */
			TTMaskSetMask(allExtractTypes, &subsTypes);
		    }
		    else
			subsTypes = DBZeroTypeBits;
		    if ((argc > 6) && (strchr(argv[6], '=') == NULL))
			subsName = argv[6];
		    break;
	    }

	    TTMaskSetMask(&ExtCurStyle->exts_deviceMask, &types1);
	    if (!TTMaskIsZero(&subsTypes))
		TTMaskSetMask(&ExtCurStyle->exts_subsDevTypes, &types1);

	    for (t = TT_TECHDEPBASE; t < DBNumTypes; t++)
	    {
		if (TTMaskHasType(&types1, t))
		{
		    devptr = (ExtDevice *)mallocMagic(sizeof(ExtDevice));

		    for (i = 0; !TTMaskIsZero(&termtypes[i]); i++);
		    devptr->exts_deviceSDTypes = (TileTypeBitMask *)
					mallocMagic((i + 1) * sizeof(TileTypeBitMask));

		    for (i = 0; !TTMaskIsZero(&termtypes[i]); i++)
			devptr->exts_deviceSDTypes[i] = termtypes[i];
		    devptr->exts_deviceSDTypes[i] = DBZeroTypeBits;

		    devptr->exts_deviceSDCount = nterm;
		    devptr->exts_deviceSDCap = gscap;
		    devptr->exts_deviceGateCap = gccap;
		    devptr->exts_deviceClass = class;
		    devptr->exts_deviceName = StrDup((char **) NULL, transName);
		    if (subsName != NULL)
			devptr->exts_deviceSubstrateName =
				StrDup((char **) NULL, subsName);
		    else
			devptr->exts_deviceSubstrateName = (char *)NULL;
		    devptr->exts_deviceSubstrateTypes = subsTypes;
		    devptr->exts_deviceIdentifierTypes = idTypes;
		    devptr->exts_deviceParams = (ParamList *) NULL;
		    if (subcktParams != NULL)
		    {
			devptr->exts_deviceParams = subcktParams;
			subcktParams->pl_count++;
		    }
		    devptr->exts_deviceResist.ht_table = (HashEntry **) NULL;
		    HashInit(&devptr->exts_deviceResist, 8, HT_STRINGKEYS);

		    devptr->exts_next = ExtCurStyle->exts_device[t];
		    ExtCurStyle->exts_device[t] = devptr;

		    TTMaskSetMask(ExtCurStyle->exts_deviceConn + t, &types1);
#ifdef ARIEL
		    {
			int z;

			for (z = TT_TECHDEPBASE; z < DBNumTypes; z++)
			{
			    if (TTMaskHasType(&subsTypes, z))
				TTMaskSetType(&ExtCurStyle->
					exts_subsTransistorTypes[z], t);
			}
		    }
#endif
		}
	    }
	    break;

	case DEVRESIST:
	case FETRESIST:
	    if (!StrIsNumeric(argv[3]))
	    {
		TechError("Device resistivity %s must be numeric\n", argv[3]);
		break;
	    }
	    resVal = aToRes(argv[3]);
	    for (t = TT_TECHDEPBASE; t < DBNumTypes; t++)
	    {
		ExtDevice *devptr;
		if (TTMaskHasType(&types1, t))
		{
		    for (devptr = ExtCurStyle->exts_device[t]; devptr; devptr = devptr->exts_next)
		    {
			he = HashFind(&devptr->exts_deviceResist, argv[2]);
			HashSetValue(he, (spointertype)resVal);
		    }
		}
	    }
	    break;
	case HEIGHT: {
	    float height, thick;

	    if (!StrIsNumeric(argv[2]))
	    {
		TechError("Layer height %s must be numeric\n", argv[2]);
		break;
	    }
	    if (!StrIsNumeric(argv[3]))
	    {
		TechError("Layer thickness %s must be numeric\n", argv[3]);
		break;
	    }
	    height = (float)strtod(argv[2], NULL);
	    thick = (float)strtod(argv[3], NULL);
	    for (t = TT_TECHDEPBASE; t < DBNumTypes; t++)
		if (TTMaskHasType(&types1, t))
		{
		    ExtCurStyle->exts_height[t] = height;
		    ExtCurStyle->exts_thick[t] = thick;
		}
	    }
	    break;
	case ANTENNA: {
	    float antennaratio;
	    char areaType;
	    bool hasModel = FALSE;
	    int argidx = 2;

	    if (!StrIsNumeric(argv[2]))
	    {
		if (!strcmp(argv[2], "surface") || !strcmp(argv[2], "area"))
		{
		    areaType = ANTENNAMODEL_SURFACE;
		    hasModel = TRUE;
		}
		else if (!strcmp(argv[2], "sidewall") || !strcmp(argv[2], "perimeter"))
		{
		    areaType = ANTENNAMODEL_SIDEWALL;
		    hasModel = TRUE;
		}
		else
		{
		    TechError("Error in layer antenna calculation type \"%s\"; "
			    " must be \"surface\" or \"sidewall\"\n", argv[2]);
		    break;
		}
	    }
	    if (hasModel == FALSE)
	    {
		if (ExtCurStyle->exts_antennaModel & ANTENNAMODEL_SURFACE)
		    areaType = ANTENNAMODEL_SURFACE;
		else if (ExtCurStyle->exts_antennaModel & ANTENNAMODEL_SIDEWALL)
		    areaType = ANTENNAMODEL_SIDEWALL;
		else
		    TechError("No antenna calculation type given for layer(s) %s "
			    " and no default calculation type found.\n", argv[1]);
	    }

	    for (t = TT_TECHDEPBASE; t < DBNumTypes; t++)
		if (TTMaskHasType(&types1, t))
		    ExtCurStyle->exts_antennaRatio[t].areaType = areaType;

	    if (hasModel == TRUE) argidx = 3;

	    if (!StrIsNumeric(argv[argidx]))
	    {
		TechError("Gate layer antenna ratio %s must be numeric\n", argv[argidx]);
		break;
	    }
	    antennaratio = (float)strtod(argv[argidx], NULL);

	    for (t = TT_TECHDEPBASE; t < DBNumTypes; t++)
		if (TTMaskHasType(&types1, t))
		    ExtCurStyle->exts_antennaRatio[t].ratioGate = antennaratio;

	    argidx++;
	    if (!StrIsNumeric(argv[argidx]))
	    {
		if (!strcasecmp(argv[argidx], "none"))
		    antennaratio = INFINITY;
		else
		{
		    TechError("Diff layer antenna ratio %s must be numeric\n",
			    argv[argidx]);
		    break;
		}
	    }
	    else
		antennaratio = (float)strtod(argv[argidx], NULL);

	    for (t = TT_TECHDEPBASE; t < DBNumTypes; t++)
		if (TTMaskHasType(&types1, t))
		    ExtCurStyle->exts_antennaRatio[t].ratioDiffB = antennaratio;

	    argidx++;
	    if (argidx < argc)
	    {
		if (!StrIsNumeric(argv[argidx]))
		{
		    TechError("Diff layer antenna ratio %s must be numeric\n",
				argv[argidx]);
		    break;
		}
		antennaratio = (float)strtod(argv[argidx], NULL);
	    }
	    else
		antennaratio = 0;

	    /* NOTE:  antennaratio is multiplied by diffusion area and so has
	     * units of (1/area^2) and so it should be scaled with other
	     * dimension-scaled units.
	     */
	    for (t = TT_TECHDEPBASE; t < DBNumTypes; t++)
		if (TTMaskHasType(&types1, t))
		    ExtCurStyle->exts_antennaRatio[t].ratioDiffA = antennaratio;

	    break;
	}
	case MODEL:
	    if (!strcmp(argv[1], "partial"))
		ExtCurStyle->exts_antennaModel |= ANTENNAMODEL_PARTIAL;
	    else if (!strcmp(argv[1], "cumulative"))
		ExtCurStyle->exts_antennaModel |= ANTENNAMODEL_CUMULATIVE;
	    else
		TxError("Unknown antenna model \"%s\":  Use \"partial\" or "
			    "\"cumulative\"", argv[1]);

	    if (argc > 2)
	    {
		if (!strcmp(argv[2], "surface") || !strcmp(argv[2], "area"))
		    ExtCurStyle->exts_antennaModel |= ANTENNAMODEL_SURFACE;
		else if (!strcmp(argv[2], "sidewall") || !strcmp(argv[2], "perimeter"))
		    ExtCurStyle->exts_antennaModel |= ANTENNAMODEL_SIDEWALL;
		else
		    TxError("Unknown antenna model \"%s\":  Use \"surface\" or "
				    "\"sidewall\"", argv[2]);
	    }
	    break;

	case TIEDOWN:
	    TTMaskSetMask(&ExtCurStyle->exts_antennaTieTypes, &types1);
	    break;
	case UNITS:
	    if (!strcmp(argv[1], "microns"))
		doConvert = TRUE;
	    else if (!strcmp(argv[1], "um"))
		doConvert = TRUE;
	    else if (strcmp(argv[1], "lambda"))
	 	TechError("Units must be microns or lambda.  Using the "
			"default value (lambda).\n");
	    break;
	case LAMBDA:
	    ExtCurStyle->exts_unitsPerLambda = (float)atof(argv[1]);
	    break;
	case OVERC:
	    DBTechNoisyNameMask(argv[2], &types2);
	    TTMaskSetMask(allExtractTypes, &types2);
	    capVal = aToCap(argv[3]);
	    bad = FALSE;
	    shield = DBZeroTypeBits;
	    if (argc > 4)
	    {
		DBTechNoisyNameMask(argv[4], &shield);
		TTMaskSetMask(allExtractTypes, &shield);
	    }
	    for (s = TT_TECHDEPBASE; s < DBNumTypes; s++)
	    {
		if (!TTMaskHasType(&types1, s)) continue;

		/* Contact overlap caps are determined from residues */
		if (DBIsContact(s)) continue;

		for (t = TT_TECHDEPBASE; t < DBNumTypes; t++)
		{
		    if (!TTMaskHasType(&types2, t)) continue;

		    /* Contact overlap caps are determined from residues */
		    if (DBIsContact(t)) continue;

		    if (s == t)
		    {
			TechError("Can't have overlap capacitance between"
				" tiles of the same type (%s)\n",
				DBTypeLongName(s));
			bad = TRUE;
			continue;
		    }
  		    p1 = DBPlane(s), p2 = DBPlane(t);
		    if (p1 == p2)
		    {
			TechError("Can't have overlap capacitance between"
				" tiles on the same plane (%s, %s)\n",
				DBTypeLongName(s), DBTypeLongName(t));
			bad = TRUE;
			continue;
		    }
		    if (ExtCurStyle->exts_overlapCap[s][t] > (CapValue) 0)
		    {
			TechError("Only one of \"overlap %s %s\" or"
				" \"overlap %s %s\" allowed\n",
				DBTypeLongName(s), DBTypeLongName(t),
				DBTypeLongName(t), DBTypeLongName(s));
			bad = TRUE;
			continue;
		    }
		    ExtCurStyle->exts_overlapCap[s][t] = capVal;
		    ExtCurStyle->exts_overlapMult[s][t] = (float)capVal * FRINGE_MULT;
		    ExtCurStyle->exts_overlapMult[t][s] = (float)capVal * FRINGE_MULT;
		    ExtCurStyle->exts_overlapPlanes |= PlaneNumToMaskBit(p1);
		    ExtCurStyle->exts_overlapOtherPlanes[s]
			    |= PlaneNumToMaskBit(p2);
		    TTMaskSetType(&ExtCurStyle->exts_overlapTypes[p1], s);
		    TTMaskSetType(&ExtCurStyle->exts_overlapOtherTypes[s], t);
		    if (argc == 4) continue;

		    /* Shielding */
		    pshield = 0;
		    for (r = TT_TECHDEPBASE; r < DBNumTypes; r++)
		    {
			if (TTMaskHasType(&shield, r))
			{
			    /* Shielding types are determined from residues */
			    if (DBIsContact(r)) continue;

			    p3 = DBPlane(r);
			    if (p3 == p1 || p3 == p2)
			    {
				TechError("Shielding type (%s) must be on a"
					" different plane from shielded types.\n",
					DBTypeLongName(r));
				bad = TRUE;
				continue;
			    }
			    pshield |= PlaneNumToMaskBit(p3);
			}
		    }
		    ExtCurStyle->exts_overlapShieldPlanes[s][t] = pshield;
		    ExtCurStyle->exts_overlapShieldTypes[s][t] = shield;
		}
	    }
	    if (bad)
		return (TRUE);
	    break;
	case SIDEOVERLAP:
	    bad = FALSE;
	    DBTechNoisyNameMask(argv[2], &types2);
	    TTMaskSetMask(allExtractTypes, &types2);
	    pov = DBTechNoisyNameMask(argv[3], &ov);
	    TTMaskSetMask(allExtractTypes, &ov);
	    capVal = aToCap(argv[4]);
	    shield = DBZeroTypeBits;
	    if (argc == 6)
	    {
		DBTechNoisyNameMask(argv[5], &shield);
		TTMaskSetMask(allExtractTypes, &shield);
	    }
	    if (TTMaskHasType(&types1, TT_SPACE))
		TechError("Can't have space on inside of edge [ignored]\n");
	    /* It's ok to have the overlap be to space as long as a plane is */
	    /* specified.						     */
	    if (TTMaskHasType(&ov, TT_SPACE))
	    {
		if ((cp = strchr(argv[3],'/')) == NULL)
		{
		    TechError("Must specify plane for sideoverlap to space\n");
		}
		cp++;
		p3 = (spointertype) dbTechNameLookup(cp, &dbPlaneNameLists);
		if (p3 < 0)
		    TechError("Unknown overlap plane %s\n",argv[3]);
		else
		    pov = PlaneNumToMaskBit(p3);
	    }
	    for (s = TT_TECHDEPBASE; s < DBNumTypes; s++)
	    {
		if (!TTMaskHasType(&types1, s))
		    continue;

		/* Side overlap computed from residues */
		if (DBIsContact(s)) continue;

		p1 = DBPlane(s);
		if (PlaneMaskHasPlane(pov, p1))
		    goto diffplane;
		ExtCurStyle->exts_sidePlanes |= PlaneNumToMaskBit(p1);
		TTMaskSetType(&ExtCurStyle->exts_sideTypes[p1], s);
		TTMaskSetMask(&ExtCurStyle->exts_sideEdges[s], &types2);
		for (t = 0; t < DBNumTypes; t++)
		{
		    if (!TTMaskHasType(&types2, t))
			continue;

		    /* Side overlap computed from residues */
		    if (DBIsContact(t)) continue;

		    p2 = DBPlane(t);
		    if (t != TT_SPACE && PlaneMaskHasPlane(pov, p2))
			goto diffplane;
		    TTMaskSetMask(&ExtCurStyle->exts_sideOverlapOtherTypes[s][t], &ov);
		    ExtCurStyle->exts_sideOverlapOtherPlanes[s][t] |= pov;
		    cnew = (EdgeCap *) mallocMagic((unsigned) (sizeof (EdgeCap)));
		    cnew->ec_cap = capVal;
		    cnew->ec_offset = 0;	/* No offsets on overlap caps */
		    cnew->ec_far = shield; /* Really types that shield */
		    cnew->ec_near = ov;  /* Really types we create cap with */
		    cnew->ec_pmask = pov;
		    cnew->ec_next = ExtCurStyle->exts_sideOverlapCap[s][t];
		    ExtCurStyle->exts_sideOverlapCap[s][t] = cnew;

		    /* Shielding */
		    pshield = 0;
		    for (r = TT_TECHDEPBASE; r < DBNumTypes; r++)
		    {
			if (TTMaskHasType(&shield, r))
			{
			    /* Side overlap shielding computed from residues */
			    if (DBIsContact(r)) continue;

			    p3 = DBPlane(r);
			    if (p3 == p1 || p3 == p2)
			    {
				TechError("Shielding type (%s) must be on"
					" a different plane from shielded types.\n",
					DBTypeLongName(r));
				bad = TRUE;
				continue;
			    }
			    pshield |= PlaneNumToMaskBit(p3);
			}
		    }
		    for (o = TT_TECHDEPBASE; o < DBNumTypes; o++)
		    {
			if (TTMaskHasType(&ov, o))
			{
			    ExtCurStyle->exts_sideOverlapShieldPlanes[s][o] |= pshield;
			}
		    }
		}
	    }
	    if (bad)
		return (TRUE);
	    break;
	case SIDEWALL:
	    DBTechNoisyNameMask(argv[2], &types2);
	    TTMaskSetMask(allExtractTypes, &types2);
	    DBTechNoisyNameMask(argv[3], &near);
	    TTMaskSetMask(allExtractTypes, &near);
	    DBTechNoisyNameMask(argv[4], &far);
	    TTMaskSetMask(allExtractTypes, &far);
	    if (TTMaskHasType(&types1, TT_SPACE))
		TechError("Can't have space on inside of edge [ignored]\n");
	    capVal = aToCap(argv[5]);
	    if (argc == 7)
	    {
		sscanf(argv[6], "%lg", &doffset);
		offset = (int)(0.5 + doffset * 1000.0);
	    }
	    else
		offset = 0;
	    for (s = TT_TECHDEPBASE; s < DBNumTypes; s++)
	    {
		if (!TTMaskHasType(&types1, s))
		    continue;
		ExtCurStyle->exts_sidePlanes |= PlaneNumToMaskBit(DBPlane(s));
		TTMaskSetType(&ExtCurStyle->exts_sideTypes[DBPlane(s)], s);
		TTMaskSetMask(&ExtCurStyle->exts_sideEdges[s], &types2);
		for (t = 0; t < DBNumTypes; t++)
		{
		    if (!TTMaskHasType(&types2, t))
			continue;
		    TTMaskSetMask(&ExtCurStyle->exts_sideCoupleOtherEdges[s][t], &far);
		    cnew = (EdgeCap *) mallocMagic((unsigned) (sizeof (EdgeCap)));
		    cnew->ec_cap = capVal;
		    cnew->ec_offset = offset;
		    cnew->ec_near = near;
		    cnew->ec_far = far;
		    cnew->ec_next = ExtCurStyle->exts_sideCoupleCap[s][t];
		    cnew->ec_pmask = 0;
		    ExtCurStyle->exts_sideCoupleCap[s][t] = cnew;
		}
	    }
	    break;
	case SIDEHALO:
	    /* Allow floating-point and increase by factor of 1000      */
	    /* to accommodate "units microns".                          */

	    /* Warning:  Due to some gcc bug with an i686 FPU, using a	*/
	    /* result from atof() with a static value like 1000		*/
	    /* produces a NaN result!  sscanf() seems to be safe. . .	*/

	    sscanf(argv[1], "%lg", &dhalo);
	    dhalo *= (double)1000.0;
	    ExtCurStyle->exts_sideCoupleHalo = (int)dhalo;
	    break;
	case FRINGESHIELDHALO:
	    /* This is deprecated. . . Ignore */
	    break;
	case PERIMC:
	    DBTechNoisyNameMask(argv[2], &types2);
	    TTMaskSetMask(allExtractTypes, &types2);
	    capVal = aToCap(argv[3]);
	    if (capVal == (CapValue) 0)
		break;
	    for (s = TT_TECHDEPBASE; s < DBNumTypes; s++)
		for (t = 0; t < DBNumTypes; t++)
		    if (TTMaskHasType(&types1, s) && TTMaskHasType(&types2, t))
		    {
			ExtCurStyle->exts_perimCap[s][t] = capVal;
			TTMaskSetType(&ExtCurStyle->exts_perimCapMask[s], t);
		    }
	    break;
	case RESIST: {
	    float chop = 1.0;

	    if (!StrIsInt(argv[2]))
	    {
		if (!strcmp(argv[2], "None"))
		{
		    for (t = TT_TECHDEPBASE; t < DBNumTypes; t++)
			if (TTMaskHasType(&types1, t))
			    TTMaskClearType(&ExtCurStyle->exts_activeTypes, t);
		    break;
		}
		else
		{
		    char *decimal;

		    if ((decimal = strchr(argv[2], '.')) != NULL)
		    {
			*decimal = '\0';
			if (StrIsInt(argv[2]))
			{
			    resVal = aToRes(argv[2]);
			    if (strcmp(decimal + 1, "0"))
				TxError("Resist argument truncated to integer value.\n");
			}
			else
			    TxError("Unparseable resist argument \"%s\"\n", argv[2]);
		    }
		    else
			TxError("Resist argument must be integer or \"None\".\n");
		    break;
		}
	    }
	    else
		resVal = aToRes(argv[2]);

	    if (argc == 4)
		chop = atof(argv[3]);
	    class = ExtCurStyle->exts_numResistClasses++;
	    for (t = TT_TECHDEPBASE; t < DBNumTypes; t++)
		if (TTMaskHasType(&types1, t))
		{
		    ExtCurStyle->exts_sheetResist[t] = resVal;
		    ExtCurStyle->exts_cornerChop[t] = chop;
		    ExtCurStyle->exts_typeToResistClass[t] = class;
		}
	    ExtCurStyle->exts_resistByResistClass[class] = resVal;
	    ExtCurStyle->exts_typesByResistClass[class] = types1;
	    }
	    break;
	case RSCALE:
	    ExtCurStyle->exts_resistScale = atoi(argv[1]);
	    break;
	case STEP:
	    val = (int)atof(argv[1]);
	    if (val <= 0)
	    {
		TechError("Hierarchical interaction step size must be > 0\n");
		return (FALSE);
	    }
	    ExtCurStyle->exts_stepSize = val;
	    break;
	case SUBSTRATE:
	    /* If the last entry starts with '-', then use it to set	*/
	    /* the shield types.   Otherwise, the shield types mask is	*/
	    /* NULL.							*/

	    idTypes = DBZeroTypeBits;
	    if (*argv[argc - 1] == '-')
	    {
		if ((DBTechNameMask(argv[argc - 1] + 1, &idTypes)) == 0)
		    idTypes = DBZeroTypeBits;
		else
		    TTMaskSetMask(allExtractTypes, &idTypes);
		argc--;
	    }

	    TTMaskZero(&ExtCurStyle->exts_globSubstrateTypes);
	    TTMaskZero(&ExtCurStyle->exts_globSubstrateShieldTypes);
	    TTMaskSetMask(&ExtCurStyle->exts_globSubstrateTypes, &types1);
	    ExtCurStyle->exts_globSubstrateShieldTypes = idTypes;
	    ExtCurStyle->exts_globSubstratePlane = DBTechNoisyNamePlane(argv[2]);

	    /* The "default" substrate type is a type that is in the	*/
	    /* list of substrate types and exists on the substrate	*/
	    /* plane, where space on the same plane is also declared to	*/
	    /* be the substrate type.					*/

	    if (ExtCurStyle->exts_globSubstratePlane != -1)
	    {
		TileType subType;
		for (subType = TT_TECHDEPBASE; subType < DBNumUserLayers; subType++)
		    if (TTMaskHasType(&ExtCurStyle->exts_globSubstrateTypes, subType))
			if (DBPlane(subType) == ExtCurStyle->exts_globSubstratePlane)
			{
			    ExtCurStyle->exts_globSubstrateDefaultType = subType;
			    break;
			}
	    }

	    /* Handle optional substrate node name */
	    if (argc == 4)
		ExtCurStyle->exts_globSubstrateName = StrDup((char **)NULL, argv[3]);

	    break;
	case NOPLANEORDER: {
	     if ( ExtCurStyle->exts_planeOrderStatus == seenPlaneOrder )
		TechError("\"noplaneordering\" specified after \"planeorder\".\n");
	     else
		ExtCurStyle->exts_planeOrderStatus = noPlaneOrder ;
	    }
	    break;
	case PLANEORDER: {
	    int pnum = (spointertype) dbTechNameLookup(argv[1], &dbPlaneNameLists);
	    int pos = atoi(argv[2]);

	    if ( ExtCurStyle->exts_planeOrderStatus == noPlaneOrder ) {
		TechError("\"planeorder\" specified after \"noplaneordering\".\n");
	    }
	    ExtCurStyle->exts_planeOrderStatus = seenPlaneOrder ;
	    if (pnum < 0)
		TechError("Unknown planeorder plane %s\n", argv[1]);
	    else if (pos < 0 || pos >= DBNumPlanes-PL_TECHDEPBASE)
		TechError("Planeorder index must be [0..%d]\n",
		    DBNumPlanes-PL_TECHDEPBASE-1);
	    else
		ExtCurStyle->exts_planeOrder[pnum] = pos;
	    }
	    break;
    }
    return (TRUE);

usage:
    TechError("Malformed line for keyword %s.  Correct usage:\n\t%s %s\n",
		    kp->k_name, kp->k_name, kp->k_usage);
    return (TRUE);

diffplane:
    TechError("Overlapped types in \"sideoverlap\" rule must be on a\n"
		"\tdifferent plane from intypes and outtypes.\n");
    return (TRUE);
}


/*
 * ----------------------------------------------------------------------------
 *
 * ExtTechFinal --
 *
 * Postprocess the technology specific information for extraction.
 * Builds the connectivity tables exts_nodeConn[], exts_resistConn[],
 * and exts_deviceConn[].
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	Initializes the tables mentioned above.
 *	Leaves ExtCurStyle pointing to the first style in the list
 *	ExtAllStyles.
 *
 * ----------------------------------------------------------------------------
 */

void
ExtTechFinal()
{
    ExtStyle *es;
    TileType s, t;

    /* Create a "default" style if there isn't one */
    if (ExtAllStyles == NULL)
    {
	ExtAllStyles = (ExtKeep *)mallocMagic(sizeof(ExtKeep));
	ExtAllStyles->exts_next = NULL;
	ExtAllStyles->exts_name = StrDup((char **) NULL, "default");

	ExtCurStyle = extTechStyleNew();
	ExtCurStyle->exts_name = ExtAllStyles->exts_name;
	ExtCurStyle->exts_status = TECH_LOADED;
    }
    extTechFinalStyle(ExtCurStyle);

    /* Any type in the connection tables that is connected to	    */
    /* something other than itself is added to the list of	    */
    /* extractable types.					    */

    for (t = TT_TECHDEPBASE; t < DBNumUserLayers; t++)
    {
	TileTypeBitMask mask;
	TTMaskZero(&mask);
	TTMaskSetMask(&mask, &DBConnectTbl[t]);
	TTMaskClearType(&mask, t);
	if (!TTMaskIsZero(&mask))
	    TTMaskSetType(allExtractTypes, t);
    }

    /* Any type that wasn't found anywhere in the extract section   */
    /* is considered non-electrical.				    */

    for (s = TT_TECHDEPBASE; s < DBNumUserLayers; s++)
	if (!TTMaskHasType(allExtractTypes, s))
	{
	    TxPrintf("The following types are not handled by extraction and will"
		    " be treated as non-electrical types:\n");
	    TxPrintf("    ");
	    for (t = TT_TECHDEPBASE; t < DBNumUserLayers; t++)
		if (!TTMaskHasType(allExtractTypes, t))
		{
		    TxPrintf("%s ", DBTypeLongNameTbl[t]);
		    TTMaskClearType(&ExtCurStyle->exts_activeTypes, t);
		}
	    TxPrintf("\n");
	    break;
	}
}


void
extTechFinalStyle(style)
    ExtStyle *style;
{
    TileTypeBitMask maskBits;
    TileType r, s, t;
    int p, p1, missing, conflict;
    int indicis[NP];

    for (r = TT_TECHDEPBASE; r < DBNumTypes; r++)
    {
	maskBits = style->exts_nodeConn[r];
	if (!TTMaskHasType(&style->exts_deviceMask, r))
	{
	     TTMaskZero(&style->exts_deviceConn[r]);
	}
	for (s = TT_TECHDEPBASE; s < DBNumTypes; s++)
	{
	    if (TTMaskHasType(&maskBits, s))
		if (style->exts_typeToResistClass[s]
			!= style->exts_typeToResistClass[r])
		    TTMaskClearType(&maskBits, s);
	}
	style->exts_resistConn[r] = maskBits;
    }

    /* r ranges over types, s over resistance entries */
    for (r = TT_TECHDEPBASE; r < DBNumTypes; r++)
    {
	s = style->exts_typeToResistClass[r];
	if (s >= 0)
	    TTMaskClearMask(&style->exts_typesResistChanged[r],
			&style->exts_typesByResistClass[s]);
    }

    /*
     * Residue check:
     * We have ignored all contact types when parsing parasitic
     * capacitances.  Now we need to add them.  For each contact
     * type, add the contact type to the types lists accordingly.
     * Note that we don't have to record any cap values, since the
     * extraction routine dissolves contacts into their residue
     * types when computing the parasitics.  But, the type must be
     * in the type lists or contact tiles will be passed over during
     * searches.
     */

    for (s = TT_TECHDEPBASE; s < DBNumTypes; s++)
    {
	TileTypeBitMask rmask;
	PlaneMask pMask;
	TileType q;

	if (!DBIsContact(s)) continue;

	pMask = DBTypePlaneMaskTbl[s];
	for (p = 0; p < DBNumPlanes; p++)
	{
	    if (PlaneMaskHasPlane(pMask, p))
	    {
		TTMaskSetType(&style->exts_overlapTypes[p], s);
		TTMaskSetType(&style->exts_sideTypes[p], s);
	    }
	}
	DBFullResidueMask(s, &rmask);
	for (r = TT_TECHDEPBASE; r < DBNumUserLayers; r++)
	{
	    if (!TTMaskHasType(&rmask, r)) continue;

	    TTMaskSetMask(&style->exts_sideEdges[s], &style->exts_sideEdges[r]);

	    for (q = TT_TECHDEPBASE; q < DBNumUserLayers; q++)
	    {
		if (TTMaskHasType(&style->exts_overlapOtherTypes[q], r))
		    TTMaskSetType(&style->exts_overlapOtherTypes[q], s);

	        for (t = TT_TECHDEPBASE; t < DBNumUserLayers; t++)
		    if (TTMaskHasType(&style->exts_overlapShieldTypes[q][t], r)
				&& !TTMaskHasType(&rmask, q)
				&& !TTMaskHasType(&rmask, t))
			TTMaskSetType(&style->exts_overlapShieldTypes[q][t], s);

		/* For sideOverlap, t is "outtypes" and includes space, so we	*/
		/* must count from TT_SPACE, not TT_TECHDEPBASE.		*/

	        for (t = TT_SPACE; t < DBNumUserLayers; t++)
		    if (TTMaskHasType(&style->exts_sideOverlapOtherTypes[q][t], r))
			TTMaskSetType(&style->exts_sideOverlapOtherTypes[q][t], s);
	    }
	}
    }

    /*
     * Consistency check:
     * If a type R shields S from T, make sure that R is listed as
     * being in the list of overlapped types for S, even if there
     * was no overlap capacitance explicitly specified for this
     * pair of types in an "overlap" line.  This guarantees that
     * R will shield S from substrate even if there is no capacitance
     * associated with the overlap.
     */
    for (s = TT_TECHDEPBASE; s < DBNumTypes; s++)
	for (t = TT_TECHDEPBASE; t < DBNumTypes; t++)
	{
	    if (style->exts_overlapShieldPlanes[s][t] == 0)
		continue;
	    for (r = TT_TECHDEPBASE; r < DBNumTypes; r++)
	    {
		if (!TTMaskHasType(&style->exts_overlapShieldTypes[s][t], r))
		    continue;
		p1 = DBPlane(s);
		style->exts_overlapPlanes |= PlaneNumToMaskBit(p1);
		style->exts_overlapOtherPlanes[s]
			|= PlaneNumToMaskBit(DBPlane(r));
		TTMaskSetType(&style->exts_overlapTypes[p1], s);
		TTMaskSetType(&style->exts_overlapOtherTypes[s], r);
	    }
	}

    /* Finally, for all coupling type masks, remove those types	 */
    /* that have been declared not to participate in extraction. */

    for (s = TT_TECHDEPBASE; s < DBNumTypes; s++)
    {
	TTMaskAndMask(&style->exts_overlapOtherTypes[s], &style->exts_activeTypes);
	TTMaskAndMask(&style->exts_perimCapMask[s], &style->exts_activeTypes);

	for (t = TT_TECHDEPBASE; t < DBNumTypes; t++)
	{
	    TTMaskAndMask(&style->exts_overlapShieldTypes[s][t],
			&style->exts_activeTypes);
	    TTMaskAndMask(&style->exts_sideOverlapOtherTypes[s][t],
			&style->exts_activeTypes);
	    TTMaskAndMask(&style->exts_sideCoupleOtherEdges[s][t],
			&style->exts_activeTypes);
	}
    }

    for (p = 0; p < DBNumPlanes; p++)
    {
	TTMaskAndMask(&style->exts_overlapTypes[p], &style->exts_activeTypes);
	TTMaskAndMask(&style->exts_sideTypes[p], &style->exts_activeTypes);
    }

    if ( style->exts_planeOrderStatus == noPlaneOrder )
    	return /* no need to check */ ;
    /* Else Check to make sure the plane order is a permutation of the
       numbers 0..DBNumPlanes-DBNumPlanes-1 */
    for (p1 = PL_TECHDEPBASE; p1 < DBNumPlanes; p1++) {
	indicis[p1] = 0;
    }
    for (p1 = PL_TECHDEPBASE; p1 < DBNumPlanes; p1++) {
	int pn = style->exts_planeOrder[p1]+PL_TECHDEPBASE;
	if (pn >= PL_TECHDEPBASE && pn < DBNumPlanes)
	    indicis[pn]++;
    }
    conflict = FALSE;
    missing = FALSE;
    for (p1 = PL_TECHDEPBASE; p1 < DBNumPlanes; p1++) {
	if (indicis[p1] > 1) conflict = TRUE ;
	if (indicis[p1] < 1) missing = TRUE ;
    }
    if (!conflict && !missing)		/* Everything was ok */
	goto zinit;

    TxError ("\nWarning: Extraction Style %s\n", style -> exts_name);
    if (conflict) {
	TxError ("  Conflicting planeorder for plane(s):\n   ");
	for (p1 = PL_TECHDEPBASE; p1 < DBNumPlanes; p1++) {
	    if (indicis[p1] > 1)
		TxError (" %s,", DBPlaneLongNameTbl[p1]);
	}
    	TxError("\n");
    }
    if (missing) {
	TxError ("  Missing planeorder for plane(s):\n   ");
	for (p1 = PL_TECHDEPBASE; p1 < DBNumPlanes; p1++) {
	    if (indicis[p1] < 1)
		TxError (" %s,", DBPlaneLongNameTbl[p1]);
	}
    	TxError("\n");
    }
    TxError("  Magic will use the default planeorder for this style:\n   ");
    for (p1 = PL_TECHDEPBASE; p1 < DBNumPlanes; p1++) {
    	style->exts_planeOrder[p1] = p1 - PL_TECHDEPBASE ;
	TxError(" %s=%d,",DBPlaneLongNameTbl[p1], style->exts_planeOrder[p1]);
    }
    TxError("\n");

    /* Now that we have a plane ordering, we can apply default height	*/
    /* and thickness values for those layers.				*/

zinit:
    for (r = TT_TECHDEPBASE; r < DBNumTypes; r++)
    {
	if (style->exts_thick[r] == 0)
	    style->exts_thick[r] = 0.05;
	if (style->exts_height[r] == 0)
	    style->exts_height[r] = 0.1 * style->exts_planeOrder[DBPlane(r)];
    }

    /* If global variable "doConvert" is TRUE, then we convert from	*/
    /* microns to lambda and microns^2 to lambda^2.			*/

    if (doConvert)
    {
	/* Use current CIF output scale for determining the scale	*/
	/* factor between micron units in the extract section and 	*/
	/* lambda units of the database (conversion from lambda to	*/
	/* internal units is done separately).				*/

	float dscale = CIFGetOutputScale(1000);
	float dsq = dscale * dscale;

	CapValue scalefac = (CapValue)dscale;
	CapValue sqfac = scalefac * scalefac;

	for (r = 0; r < DBNumTypes; r++)
	{
	    ExtDevice *devptr;

	    for (devptr = style->exts_device[r]; devptr; devptr = devptr->exts_next)
	    {	
		ParamList *chkParam;
		HashEntry *he;
		ResValue res;

		devptr->exts_deviceSDCap *= sqfac;
		devptr->exts_deviceGateCap *= sqfac;

		for (chkParam = devptr->exts_deviceParams; chkParam;
				chkParam = chkParam->pl_next)
		{
		    if (chkParam->pl_offset != 0)
		    {
			if (chkParam->pl_param[0] == 'a')
			    chkParam->pl_offset /= dsq;
			else
			    chkParam->pl_offset /= dscale;
		    }

		    if (chkParam->pl_minimum > chkParam->pl_maximum) continue;
		    else if (chkParam->pl_param[0] == 'a')
		    {
			chkParam->pl_maximum /= dsq;
			chkParam->pl_minimum /= dsq;
		    }
		    else
		    {
			chkParam->pl_maximum /= dscale;
			chkParam->pl_minimum /= dscale;
		    }
		}

		he = HashLookOnly(&devptr->exts_deviceResist, "area");
		if (he != NULL)
		{
		    res = (ResValue)(spointertype)(HashGetValue(he));
		    res /= dsq;
		    HashSetValue(he, (spointertype)res);
		}
		he = HashLookOnly(&devptr->exts_deviceResist, "perimeter");
		if (he != NULL)
		{
		    res = (ResValue)(spointertype)(HashGetValue(he));
		    res /= dscale;
		    HashSetValue(he, (spointertype)res);
		}
		he = HashLookOnly(&devptr->exts_deviceResist, "linear");
		if (he != NULL)
		{
		    res = (ResValue)(spointertype)(HashGetValue(he));
		    res /= dscale;
		    HashSetValue(he, (spointertype)res);
		}
	    }

	    style->exts_areaCap[r] *= sqfac;
	    for (s = 0; s < DBNumTypes; s++)
	    {
		EdgeCap *ec;

		style->exts_perimCap[r][s] *= scalefac;
		style->exts_overlapCap[r][s] *= sqfac;
		style->exts_overlapMult[r][s] *= scalefac;
		for (ec = style->exts_sideOverlapCap[r][s]; ec != NULL;
				ec = ec->ec_next)
		    ec->ec_cap *= scalefac;

		// Note that because sidewall caps are referred to
		// a specific distance, the value (run / separation)
		// is unscaled, so the capacitance does not get
		// modified by the scalefactor.  However, the lambda
		// reference for sidewall cap is 2 lambda, so if
		// the reference is to be interpreted as 1 micron,
		// the value needs to be divided by 2 (the factor of
		// 2 is made up by the fact that the sidewall is
		// independently accumulated on each plate of the
		// capacitor).  ALSO:  ec_offset was multiplied up by
		// 1000 so that micron distances could be saved as
		// integer values, so that factor needs to be divided out.

		for (ec = style->exts_sideCoupleCap[r][s]; ec != NULL;
				ec = ec->ec_next)
		{
		    ec->ec_cap *= 0.5;
		    ec->ec_offset = (int)(((float)ec->ec_offset / dscale) + 0.5);
		    ec->ec_offset /= 1000;
		}
	    }

	    /* Layer thickness and height are in microns, but are floating-point */
	    style->exts_thick[r] /= dscale;
	    style->exts_height[r] /= dscale;

	    /* Only this antenna coefficient has dimensioned units */
	    style->exts_antennaRatio[r].ratioDiffA *= dsq;
	}

	/* side halo, fringe shield halo, and step size are also in microns */

	style->exts_sideCoupleHalo = (int)(((float)style->exts_sideCoupleHalo
		/ dscale) + 0.5);
	style->exts_stepSize = (int)(((float)style->exts_stepSize
		/ dscale) + 0.5);
    }

    /* Avoid setting stepSize to zero, or extraction will hang! */
    if (style->exts_stepSize <= 0)
    {
	TxError("Warning:  zero step size!  Resetting to default.\n");
	style->exts_stepSize = 100;		/* Revert to default */
    }

    /* We had multiplied sideCoupleHalo by 1000 to accommodate a 	*/
    /* floating-point value in microns, whether or not doConvert was	*/
    /* needed, so normalize it back to lambda units.			*/

    style->exts_sideCoupleHalo /= 1000;

    /* Ditto for the parameter maximum/minimum limits */
    for (r = 0; r < DBNumTypes; r++)
    {
	ExtDevice *devptr;
	ParamList *chkParam;

	for (devptr = style->exts_device[r]; devptr; devptr = devptr->exts_next)
	{
	    for (chkParam = devptr->exts_deviceParams; chkParam;
				chkParam = chkParam->pl_next)
	    {
		if (chkParam->pl_offset != 0)
		    chkParam->pl_offset /= 1000;
		if (chkParam->pl_minimum > chkParam->pl_maximum) continue;
		chkParam->pl_maximum /= 1000;
		chkParam->pl_minimum /= 1000;
	    }
	}
    }
}

/*
 * ----------------------------------------------------------------------------
 * ExtTechScale --
 *
 *	Scale all extraction values appropriately when rescaling the grid.
 * ----------------------------------------------------------------------------
 */

void
ExtTechScale(scalen, scaled)
    int scalen;			/* Scale numerator */
    int scaled;			/* Scale denominator */
{
    ExtStyle *style = ExtCurStyle;
    EdgeCap *ec;
    int i, j;
    float sqn, sqd;

    if (style == NULL) return;

    sqn = (float)(scalen * scalen);
    sqd = (float)(scaled * scaled);

    style->exts_unitsPerLambda = style->exts_unitsPerLambda * (float)scalen
		/ (float)scaled;
    DBScaleValue(&style->exts_sideCoupleHalo, scaled, scalen);
    DBScaleValue(&style->exts_stepSize, scaled, scalen);

    for (i = 0; i < DBNumTypes; i++)
    {
	ExtDevice *devptr;
	ParamList *chkParam;

	style->exts_areaCap[i] *= sqn;
	style->exts_areaCap[i] /= sqd;

	for (devptr = style->exts_device[i]; devptr; devptr = devptr->exts_next)
	{
	    devptr->exts_deviceSDCap *= sqn;
	    devptr->exts_deviceSDCap /= sqd;
	    devptr->exts_deviceGateCap *= sqn;
	    devptr->exts_deviceGateCap /= sqd;

	    for (chkParam = devptr->exts_deviceParams; chkParam;
				chkParam = chkParam->pl_next)
	    {
		if (chkParam->pl_offset != 0)
		{
		    if (chkParam->pl_param[0] == 'a')
		    {
			chkParam->pl_offset *= sqd;
			chkParam->pl_offset /= sqn;
		    }
		    else
		    {
			chkParam->pl_offset *= scaled;
			chkParam->pl_offset /= scalen;
		    }
		}
		if (chkParam->pl_minimum > chkParam->pl_maximum) continue;
		else if (chkParam->pl_param[0] == 'a')
		{
		    chkParam->pl_maximum *= sqd;
		    chkParam->pl_maximum /= sqn;
		    chkParam->pl_minimum *= sqd;
		    chkParam->pl_minimum /= sqn;
		}
		else
		{
		    chkParam->pl_maximum *= scaled;
		    chkParam->pl_maximum /= scalen;
		    chkParam->pl_minimum *= scaled;
		    chkParam->pl_minimum /= scalen;
		}
	    }
	}

	style->exts_height[i] *= scaled;
	style->exts_height[i] /= scalen;
	style->exts_thick[i] *= scaled;
	style->exts_thick[i] /= scalen;

	style->exts_antennaRatio[i].ratioDiffA *= sqn;
	style->exts_antennaRatio[i].ratioDiffA /= sqd;

	for (j = 0; j < DBNumTypes; j++)
	{
	    style->exts_perimCap[i][j] *= scalen;
	    style->exts_perimCap[i][j] /= scaled;
	    style->exts_overlapCap[i][j] *= sqn;
	    style->exts_overlapCap[i][j] /= sqd;    /* Typo fixed in 7.2.57 */
	    style->exts_overlapMult[i][j] *= scalen;
	    style->exts_overlapMult[i][j] /= scaled;

	    // Do not scale sidewall cap, for while the value is
	    // per distance, the distance is referred to a separation
	    // distance in the same units, so the cap never scales.

	    for (ec = style->exts_sideCoupleCap[i][j]; ec != NULL;
	    			ec = ec->ec_next)
	    {
	    	// ec->ec_cap *= scalen;
	    	// ec->ec_cap /= scaled;
    		DBScaleValue(&(ec->ec_offset), scaled, scalen);
	    }
	    for (ec = style->exts_sideOverlapCap[i][j]; ec != NULL;
				ec = ec->ec_next)
	    {
		ec->ec_cap *= scalen;
		ec->ec_cap /= scaled;
	    }
	}
    }

    return;
}

