/*
 * defWrite.c --
 *
 * This module incorporates the LEF/DEF format for standard-cell place and
 * route.
 *
 *
 * Version 0.1 (June 9, 2004):  DEF output for layouts, to include netlist
 * from the extracted layout.
 *
 */

#ifndef lint
static const char rcsid[] __attribute__ ((unused)) = "$Header: /usr/cvsroot/magic-8.0/lef/defWrite.c,v 1.2 2008/02/10 19:30:21 tim Exp $";
#endif  /* not lint */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>

#include "tcltk/tclmagic.h"
#include "utils/magic.h"
#include "utils/geometry.h"
#include "tiles/tile.h"
#include "utils/hash.h"
#include "database/database.h"
#include "utils/tech.h"
#include "utils/malloc.h"
#include "utils/undo.h"
#include "cif/cif.h"
#include "extract/extractInt.h"	/* for ExtCurStyle definition */
#include "extflat/extflat.h"
#include "lef/lefInt.h"
#include "drc/drc.h"		/* for querying width, spacing rules */

/* C99 compat */
#include "utils/signals.h"
#include "textio/textio.h"

/*----------------------------------------------------------------------*/
/* Structures used by various routines					*/
/*----------------------------------------------------------------------*/

typedef struct {
   float	scale;
   FILE		*f;
   CellDef	*def;

   Tile		*tile;		/* Values of the last calculated route */
   TileType	type;
   float	x, y, extlen;
   unsigned char orient;
   LefRules 	*ruleset;	/* Non-default ruleset or NULL */

   LefMapping	*MagicToLefTbl;
   HashTable	*defViaTable;
   int		outcolumn;	/* Current column of output in file */
   unsigned char specialmode;	/* What nets to write as SPECIALNETS */
} DefData;

typedef struct {
   CellDef	*def;
   int		nlayers;
   const char	**baseNames;
   TileTypeBitMask *blockMasks;
   LinkedRect **blockData;
} DefObsData;

typedef struct {
   CellDef *def;
   float scale;
   int total;
   int plane;
   TileTypeBitMask *mask;
   LefMapping *MagicToLefTbl;
   HashTable *defViaTable;
} CViaData;

/*----------------------------------------------------------------------*/

const char *defGetType(TileType ttype, lefLayer **lefptr, bool do_vias);		/* Forward declaration */

/*----------------------------------------------------------------------*/


/*
 * ----------------------------------------------------------------------------
 *
 * defWriteHeader --
 *
 * This routine generates DEF header output for a cell or cell hierarchy.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	Writes output to the open file "f".
 *
 * ----------------------------------------------------------------------------
 */

void
defWriteHeader(
    CellDef *def,	/* Def for which to generate DEF output */
    FILE *f,		/* Output to this file */
    float oscale,
    int units)		/* Units for UNITS; could be derived from oscale */
{
    PropertyRecord *proprec;
    bool propfound;

    TxPrintf("Diagnostic:  Write DEF header for cell %s\n", def->cd_name);

    /* NOTE:  This routine corresponds to Envisia LEF/DEF Language	*/
    /* Reference version 5.7 (November, 2009)				*/

    fprintf(f, "VERSION 5.7 ;\n");
    fprintf(f, "   NAMESCASESENSITIVE ON ;\n");
    fprintf(f, "   DIVIDERCHAR \"/\" ;\n");

    /* Declare that buses are denoted with parentheses, since magic	*/
    /* uses brackets for arrays and instances.				*/
    fprintf(f, "   BUSBITCHARS \"()\" ;\n");

    /* Design name, taken from the cell def name */
    fprintf(f, "   DESIGN %s ;\n", def->cd_name);

    /* Technology name, taken from the magic tech file.			*/
    /* (which may not be a good idea. . .  may need a tech definition	*/
    /* in the tech file "lef" section to specifically name the LEF/DEF	*/
    /* technology).							*/
    fprintf(f, "   TECHNOLOGY %s ;\n", DBTechName);

    /* The DEF scalefactor (conversion to microns) is always 1000	*/
    /* (nanometers) unless overridden on the command line.		*/

    fprintf(f, "   UNITS DISTANCE MICRONS %d ;\n", units);

    /* For DIEAREA, use the FIXED_BBOX property if present.  Otherwise,	*/
    /* use the extents of geometry (CellDef bounding box)		*/

    proprec = DBPropGet(def, "FIXED_BBOX", &propfound);
    if (propfound)
    {
	Rect bbox;

	/* Die area, taken from the declared FIXED_BBOX.		*/
	if ((proprec->prop_type == PROPERTY_TYPE_DIMENSION) &&
			(proprec->prop_len == 4))
	{
	    bbox.r_xbot = proprec->prop_value.prop_integer[0];
	    bbox.r_ybot = proprec->prop_value.prop_integer[1];
	    bbox.r_xtop = proprec->prop_value.prop_integer[2];
	    bbox.r_ytop = proprec->prop_value.prop_integer[3];

	     fprintf(f, "   DIEAREA ( %.10g %.10g ) ( %.10g %.10g ) ;\n",
			(float)bbox.r_xbot * oscale,
			(float)bbox.r_ybot * oscale,
			(float)bbox.r_xtop * oscale,
			(float)bbox.r_ytop * oscale);
	}
	else
	    propfound = FALSE;
    }

    if (!propfound)
    {
	/* Die area, taken from the cell def bounding box.		*/
	fprintf(f, "   DIEAREA ( %.10g %.10g ) ( %.10g %.10g ) ;\n",
		(float)def->cd_bbox.r_xbot * oscale,
		(float)def->cd_bbox.r_ybot * oscale,
		(float)def->cd_bbox.r_xtop * oscale,
		(float)def->cd_bbox.r_ytop * oscale);
    }

    fprintf(f, "\n");
}

/*
 *------------------------------------------------------------
 *
 * defTransPos --
 *
 *	Determine the DEF orientation of a specific magic
 *	transformation matrix.
 *
 * Results:
 *	The position, in DEF string format ("N" for north, etc.)
 *	This is a static string
 *
 * Side Effects:
 *	None.
 *
 *------------------------------------------------------------
 */

const char *
defTransPos(
    Transform *t)
{
    static const char * const def_orient[] = {
	"N", "S", "E", "W", "FN", "FS", "FE", "FW"
    };

    bool ew;  /* east-or-west identifier */
    bool sw;  /* south-or-west identifier */
    bool flip;
    int pos = 0;

    ew = ((t->t_a == 0) && (t->t_e == 0)) ? TRUE : FALSE;
    if (ew)
    {
       flip = ((t->t_b * t->t_d) > 0) ? TRUE : FALSE;
       sw = (t->t_d > 0) ? TRUE : FALSE;
    }
    else
    {
       flip = ((t->t_a * t->t_e) < 0) ? TRUE : FALSE;
       sw = (t->t_e > 0) ? FALSE : TRUE;
    }

    if (flip) pos += 4;
    if (ew) pos += 2;
    if (sw) pos += 1;

    return def_orient[pos];
}

/*
 *------------------------------------------------------------
 *
 * defCountNets --
 *
 *	First-pass function to count the number of different
 *	nets used.  If "allSpecial" is TRUE, consider all
 *	geometry to be SPECIALNETS.
 *
 * Results:
 *	A NetCount structure holding the regular and special
 *	net totals upon completion.
 *
 * Side Effects:
 *	None.
 *
 *------------------------------------------------------------
 */

NetCount
defCountNets(
    CellDef *rootDef,
    bool allSpecial)
{
    NetCount total;
    int defnodeCount(EFNode *node, int res, EFCapValue cap, NetCount *total);

    total.regular = (allSpecial) ? -1 : 0;
    total.special = 0;
    total.blockages = 0;
    total.has_nets = TRUE;

    TxPrintf("Diagnostic:  Finding all nets in cell %s\n", rootDef->cd_name);
    TxPrintf("(This can take a while!)\n");

    /* Read in the extracted file */
    EFInit();

    /* There are no arguments for extflat, but we need to call the	*/
    /* routine to initialize a few things such as the search path.	*/
    EFArgs(0, NULL, NULL, NULL, NULL);

    EFScale = 0.0;	/* Allows EFScale to be set to the scale value	*/

    if (EFReadFile(rootDef->cd_name, TRUE, FALSE, TRUE, FALSE))
    {
	EFFlatBuild(rootDef->cd_name, EF_FLATNODES | EF_NOFLATSUBCKT);
        EFVisitNodes(defnodeCount, (ClientData)&total);
    }
    else
    {
	TxError("Warning:  Circuit has no .ext file;  no nets written.\n");
	TxError("Run extract on this circuit if you want nets in the output.\n");
	EFDone(NULL);
	total.has_nets = FALSE;
    }

    if (allSpecial) total.regular = 0;
    return total;
}

/* Callback function used by defCountNets */

int
defnodeCount(
    EFNode *node,
    int res,			/* not used */
    EFCapValue cap,		/* not used */
    NetCount *total)
{
    HierName *hierName;
    char *cp, clast;

    /* Ignore the substrate node if it is not connected to any routing */
    if (node->efnode_type == TT_SPACE)
	return 0;

    /* Ignore power and ground lines, which we will treat	*/
    /* as SPECIALNETS types.					*/

    hierName = (HierName *) node->efnode_name->efnn_hier;

    if (!(hierName->hn_parent))  /* Extra processing of top-level nodes */
    {
	char *pwr;
	cp = hierName->hn_name;
	clast = *(cp + strlen(cp) - 1);

	/* Global nodes are marked as "special nets" */
	if (clast == '!')
	    node->efnode_flags |= EF_SPECIAL;

#ifdef MAGIC_WRAPPER
	/* Check if name is defined in array "globals" */
	pwr = (char *)Tcl_GetVar2(magicinterp, "globals", cp, TCL_GLOBAL_ONLY);
	if (pwr)
	{
	    /* Diagnostic */
	    TxPrintf("Node %s is defined in the \"globals\" array\n", cp);
	    node->efnode_flags |= EF_SPECIAL;
	}

	/* Check against Tcl variables $VDD and $GND */
	pwr = (char *)Tcl_GetVar(magicinterp, "VDD", TCL_GLOBAL_ONLY);
	if (pwr && (!strcmp(cp, pwr)))
	{
	    /* Diagnostic */
	    TxPrintf("Node %s matches VDD variable definition!\n", cp);
	    node->efnode_flags |= EF_SPECIAL;
	}

	pwr = (char *)Tcl_GetVar(magicinterp, "GND", TCL_GLOBAL_ONLY);
	if (pwr && (!strcmp(cp, pwr)))
	{
	    /* Diagnostic */
	    TxPrintf("Node %s matches GND variable definition!\n", cp);
	    node->efnode_flags |= EF_SPECIAL;
	}

	/* If a node has not been marked as SPECIAL, does not connect	*/
	/* to a port, and does not have an internally-generated	name,	*/
	/* then mark it as "special".					*/
	if (!(node->efnode_flags & (EF_SPECIAL | EF_PORT)) &&
		(clast != '#'))
	    node->efnode_flags |= EF_SPECIAL;
#endif
    }

    if (total->regular < 0)
    {
	/* "allspecial" options:  all nets written as SPECIALNETS */

	if ((node->efnode_flags & EF_SPECIAL) || (node->efnode_flags & EF_PORT))
	    total->special++;
	else
	    total->blockages++;
    }
    else
    {
	/* We only count nodes having a port connection as "regular" nets */

	if (node->efnode_flags & EF_SPECIAL)
	    total->special++;
	else if (node->efnode_flags & EF_PORT)
	    total->regular++;
	else
	    total->blockages++;
    }

    return 0;	/* Keep going. . . */
}

/*
 * ----------------------------------------------------------------------------
 *
 * defHNsprintf --
 *
 * Create a hierarchical node name for the DEF output file..
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	Changes the area pointed to by str
 *
 * ----------------------------------------------------------------------------
 */

void
defHNsprintf(
    char *str,
    const HierName *hierName,
    char divchar)
{
    const char *cp;
    char c;
    char *defHNsprintfPrefix(const HierName *hierName, char *str, char divchar);  /* Forward declaration */

    if (hierName->hn_parent) str = defHNsprintfPrefix(hierName->hn_parent, str,
		divchar);

    /* Make the name conform to valid LEF/DEF syntax.  This means	*/
    /* no pound signs or semicolons (which are illegal characters,	*/
    /* along with space and newline which won't be found in the 	*/
    /* magic name anyway), or dashes, asterisks, or percent signs	*/
    /* (which are interpreted as wildcard characters by LEF/DEF).	*/

    cp = hierName->hn_name;
    while ((c = *cp++))
    {
	switch (c)
	{
	    case '#':		/* Ignore---this is the final character */
				/* in internally-generated node names.	*/
		break;
	    case ';':
	    case '-':
	    case '*':
	    case '%':
		*str++ = '_';
		break;
	    default:
		*str++ = c;
		break;
	}
    }
    *str++ = '\0';
}

char *defHNsprintfPrefix(
    const HierName *hierName,
    char *str,
    char divchar)
{
    const char *cp;

    if (hierName->hn_parent)
	str = defHNsprintfPrefix(hierName->hn_parent, str, divchar);

    cp = hierName->hn_name;
    while ((*str++ = *cp++)) ;
    *(--str) = divchar;
    return ++str;
}


/*
 *------------------------------------------------------------
 *
 * nodeDefName ---
 *
 *	Determine the node name to write to the DEF file
 *	for the given hierachical name structure from
 *	extflat.
 *
 *------------------------------------------------------------
 */

char *
nodeDefName(
    HierName *hname)
{
    EFNodeName *nn;
    HashEntry *he;
    EFNode *node;
    static char nodeName[256];

    he = EFHNLook(hname, (char *) NULL, "nodeName");
    if (he == NULL)
        return "errorNode";
    nn = (EFNodeName *) HashGetValue(he);
    node = nn->efnn_node;

    defHNsprintf(nodeName, node->efnode_name->efnn_hier, '/');
    return nodeName;
}

/*
 *------------------------------------------------------------
 *
 * defCheckForBreak --
 *
 *	Add the number "addlen" to the column value of
 *	the output.  If the DEF file output has reached or
 *	exceeds this value, write a newline character to
 *	the output and reset the column count.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	Output to DEF file; resets defdata->outcolumn
 *
 *------------------------------------------------------------
 */

#define MAX_DEF_COLUMNS 70

void
defCheckForBreak(
    int addlen,
    DefData *defdata)
{
    defdata->outcolumn += addlen;
    if (defdata->outcolumn > MAX_DEF_COLUMNS)
    {
	fprintf(defdata->f, "\n      ");
	defdata->outcolumn = 6 + addlen;
    }
}

/*
 *------------------------------------------------------------
 *
 * defWriteRouteWidth ---
 *
 *	Write the width of a SPECIALNET route to the output.
 *
 *------------------------------------------------------------
 */

void
defWriteRouteWidth(
    DefData *defdata,
    int width)
{
    char numstr[32];
    sprintf(numstr, "%.10g", ((float)width * defdata->scale));
    defCheckForBreak(strlen(numstr) + 1, defdata);
    fprintf(defdata->f, "%s ", numstr);
}

/*
 *------------------------------------------------------------
 *
 * defWriteCoord --
 *
 *	Output a coordinate pair in DEF syntax.  We supply the
 *	point to be written AND the previously written point
 *	so we can make use of the "*" notation in the DEF point
 *	format.  If the point to be written is not an extension
 *	of the previous point, "prevpt" should be NULL.
 *
 * Results:
 *	None.
 *
 * Side Effects:
 *	Output written to the DEF file.
 *
 *------------------------------------------------------------
 */

void
defWriteCoord(
    DefData *defdata,
    float x,
    float y,
    unsigned char orient)
{
    FILE *f = defdata->f;
    char numstr[32];
    int ctot = 4;

    /* The "12" here is just a fudge factor;  it is not crucial */
    /* to limit the output to exactly MAX_DEF_COLUMNS, and it 	*/
    /* is easier to assume that the output of a coordinate	*/
    /* pair is about 12 characters average rather than try to	*/
    /* predetermine what the actual output length will be.	*/

    if ((defdata->outcolumn + 12) > MAX_DEF_COLUMNS)
    {
	fprintf(f, "\n      ");
	defdata->outcolumn = 6;
    }

    fprintf(f, " ( ");
    if ((orient == GEO_NORTH) || (orient == GEO_SOUTH))
    {
	fprintf(f, "* ");
	ctot += 2;
    }
    else
    {
	sprintf(numstr, "%.10g", x);
	fprintf(f, "%s ", numstr);
	ctot += strlen(numstr) + 1;
    }

    if ((orient == GEO_EAST) || (orient == GEO_WEST))
    {
	fprintf(f, "* ");
	ctot += 2;
    }
    else
    {
	sprintf(numstr, "%.10g", y);
	fprintf(f, "%s ", numstr);
	ctot += strlen(numstr) + 1;
    }

    fprintf(f, ")");
    defdata->outcolumn += ctot;
}

/*
 *------------------------------------------------------------
 *
 * defWriteNets --
 *
 *	Output the NETS section of a DEF file.  We make use of
 *	the connectivity search routines used by "getnode" to
 *	determine unique notes and assign a net name to each.
 *	Then, we generate the geometry output for each NET
 *	entry.
 *
 * Results:
 *	None.
 *
 * Side Effects:
 *	Output written to the DEF output file.
 *
 *------------------------------------------------------------
 */

void
defWriteNets(
    FILE *f,				/* File to write to */
    CellDef *rootDef,			/* Cell definition to use */
    float oscale,			/* Output scale factor */
    LefMapping *MagicToLefTable,	/* Magic to LEF layer mapping */
    HashTable *defViaTable,		/* Hash table of contact positions */
    unsigned char specialmode)		/* What to write as a SPECIALNET */
{
    DefData defdata;
    int defnodeVisit(EFNode *node, int res, EFCapValue cap, DefData *defdata);

    defdata.f = f;
    defdata.scale = oscale;
    defdata.def = rootDef;
    defdata.MagicToLefTbl = MagicToLefTable;
    defdata.outcolumn = 0;
    defdata.ruleset = NULL;
    defdata.specialmode = specialmode;
    defdata.defViaTable = defViaTable;

    EFVisitNodes(defnodeVisit, (ClientData)&defdata);
}

int
defnodeVisit(
    EFNode *node,
    int res,
    EFCapValue cap,
    DefData *defdata)
{
    HierName *hierName;
    char *ndn;
    char ndn2[256];
    char locndn[256];
    FILE *f = defdata->f;
    CellDef *def = defdata->def;
    TileTypeBitMask tmask, *rmask;
    TileType magictype;
    LinkedRect *lr;
    EFNodeName *thisnn;

    /* Forward declaration */
    int defNetGeometryFunc(Tile *tile, TileType dinfo, int plane, DefData *defdata);

    /* For regular nets, only count those nodes having port	*/
    /* connections.  For special nets, only count those nodes	*/
    /* that were marked with the EF_SPECIAL flag while counting	*/
    /* nets.							*/

    if (defdata->specialmode == DO_REGULAR)
    {
	if (!(node->efnode_flags & EF_PORT))
	    return 0;
	else if (node->efnode_flags & EF_SPECIAL)
	    return 0;
    }
    else if (defdata->specialmode == DO_SPECIAL)
    {
	if (!(node->efnode_flags & EF_SPECIAL))
	    return 0;
    }
    else /* ALL_SPECIAL */
    {
	if (!(node->efnode_flags & EF_PORT) &&
		!(node->efnode_flags & EF_SPECIAL))
	    return 0;
    }

    hierName = (HierName *) node->efnode_name->efnn_hier;
    ndn = nodeDefName(hierName);
    defHNsprintf(ndn2, node->efnode_name->efnn_hier, '/');
    if (strcmp(ndn, ndn2))
    {
	TxError("Node mismatch: %s vs. %s\n", ndn, ndn2);
    }

    /* Avoid attempting to extract an implicit substrate into DEF */
    if (node->efnode_type == TT_SPACE)
	return 0;

    fprintf(f, "   - %s", ndn);
    defdata->outcolumn = 5 + strlen(ndn);

    /* If this net is marked as a PORT, then connect to the associated PIN */
    if (node->efnode_flags & EF_TOP_PORT)
	fprintf(f, " ( PIN %s )", ndn);

    /* Find all the node names that are port connections. 	*/
    /* For now, we will just use anything connecting one level	*/
    /* down in the hierarchy.  This is not definitive, however,	*/
    /* and we should confirm that the connection is an actual	*/
    /* port.							*/

    for (thisnn = node->efnode_name; thisnn != NULL; thisnn = thisnn->efnn_next)
    {
	hierName = thisnn->efnn_hier;
	if (hierName->hn_parent && !hierName->hn_parent->hn_parent)
	{
	    /* This is just another kludgy check for a non-port and	*/
	    /* will eventually be removed.				*/
	    char endc = *(hierName->hn_name + strlen(hierName->hn_name) - 1);
	    if (endc != '#')
	    {
		defHNsprintf(locndn, thisnn->efnn_hier, ' ');
		defCheckForBreak(5 + strlen(locndn), defdata);
		fprintf(f, " ( %s )", locndn);
	    }
	}
    }

    /* TT_SPACE indicates that a layer name must be the	*/
    /* next thing to be written to the DEF file.	*/
    defdata->type = TT_SPACE;
    defdata->tile = (Tile *)NULL;

    /* Process all disjoint segments of the node */

    for (lr = node->efnode_disjoint; lr; lr = lr->r_next)
    {
	/* Watch for entries created from the substrate node */
	if ((lr->r_r.r_ll.p_x <= (MINFINITY + 2)) ||
			(lr->r_r.r_ll.p_y <= (MINFINITY + 2)))
	    continue;

	/* Net geometry (this should be an option!)---		*/
	/* Use the DBconnect routines to find all geometry	*/
	/* connected to a specific node.  This is a		*/
	/* redundant search---we've already done this once	*/
	/* when extracting the circuit.  But, because the	*/
	/* DEF file requires a count of nodes up front, we	*/
	/* would have to do it twice anyway.  In this case,	*/
	/* we only do it once here, since the results of	*/
	/* the first pass are picked up from the .ext file.	*/

	magictype = DBTechNameType(EFLayerNames[lr->r_type]);
	ASSERT(magictype >= 0, "magictype<0");

	/* Note that the type of the node might be defined by the type */
	/* in the subcircuit itself, so we need to search for any type */
	/* that might validly connect to it, not just the type itself. */
	/* TTMaskSetOnlyType(&tmask, magictype); */
	TTMaskZero(&tmask);
	TTMaskSetMask(&tmask, &DBConnectTbl[magictype]);

	DBSrConnect(def, &lr->r_r, &tmask, DBConnectTbl, &TiPlaneRect,
			defNetGeometryFunc, (ClientData)defdata);

	if (defdata->tile == (Tile *)NULL)
	{
	    /* No route layer?  It's possible that something connects 	*/
	    /* to the port location but doesn't overlap.  Try painting	*/
	    /* the node type in def and trying again.			*/

	    Rect rport;
	    SearchContext scx;

	    /* Forward declaration */
	    int defPortTileFunc(Tile *tile, TileType dinfo, TreeContext *cx);

	    scx.scx_area = node->efnode_loc;
	    scx.scx_use = def->cd_parents;
	    scx.scx_trans = GeoIdentityTransform;

	    rport = GeoNullRect;
	    DBTreeSrUniqueTiles(&scx, &tmask, 0, defPortTileFunc, (ClientData)&rport);

	    /* Add the residue types to any contact type */
	    if (DBIsContact(magictype))
	    {
		rmask = DBResidueMask(magictype);
		TTMaskSetMask(&tmask, rmask);
		TTMaskSetType(&tmask, magictype);
	    }

	    /* Check if anything was found in the location (e.g., .ext file
	     * could be invalid or outdated).  However, if magictype is not
	     * an electrically active type, then don't issue any warning.
	     */
	    if (GEO_RECTNULL(&rport))
	    {
		if (TTMaskHasType(&ExtCurStyle->exts_activeTypes, magictype))
		    TxError("Nothing of type %s found at node %s location"
				" (floating label?)!\n",
				DBTypeLongNameTbl[magictype], ndn);
	    }
	    else
	    {
		/* Expand the rectangle around the port to overlap any	*/
		/* connecting material.					*/
		rport.r_xbot--;
		rport.r_ybot--;
		rport.r_xtop++;
		rport.r_ytop++;
  		DBSrConnect(def, &rport, &tmask, DBConnectTbl, &TiPlaneRect,
				defNetGeometryFunc, (ClientData)defdata);
	    }
        }
    }

    /* Was there a last record pending?  If so, write it. */
    if (defdata->tile != (Tile *)NULL)
    {
	if (defdata->orient != GEO_CENTER)
	    defWriteCoord(defdata, defdata->x, defdata->y, defdata->orient);

	defdata->outcolumn = 0;
    }
    fprintf(f, " ;\n");

    return 0; 	/* Keep going */
}

/* Callback function for defNetGeometryFunc().  Determines if any tile	*/
/* sets the lower bound of the clip line for extending a wire upward	*/

int
defMaxWireFunc(
    Tile *tile,
    TileType dinfo,	/* (unused) */
    int  *yclip)
{
    if (BOTTOM(tile) < (*yclip)) *yclip = BOTTOM(tile);
    return 0;
}

/* Callback function for defNetGeometryFunc().  Determines if any tile	*/
/* sets the upper bound of the clip line for extending a wire downward	*/

int
defMinWireFunc(
    Tile *tile,
    TileType dinfo,	/* (unused) */
    int  *yclip)
{
    if (TOP(tile) > (*yclip)) *yclip = TOP(tile);
    return 0;
}

/* Callback function for defNetGeometryFunc().  Determines if any tile	*/
/* is enclosed by rect, and if so, marks the tile client so that	*/
/* DBSrConnectFunc() will not apply the client function to that tile.	*/
/* clientdata value (ClientData)1 means the tile has already been	*/
/* processed;  (ClientData)CLIENTDEFAULT means that it has not been	*/
/* processed.  Any other value will cause it to skip the client		*/
/* function when it is processed.					*/

int
defExemptWireFunc(
    Tile *tile,
    TileType dinfo,	/* (unused) */
    Rect *rect)
{
    Rect r;

    /* Do not change the client data of tiles that have been processed! */
    if (TiGetClientINT(tile) != 1)
    {
	/* Ignore contacts, which need additional processing */
	if (DBIsContact(TiGetType(tile))) return 0;

	TiToRect(tile, &r);
	if (GEO_SURROUND(rect, &r))
	    TiSetClientINT(tile, 2);
    }
    return 0;
}

/* Callback function for DBTreeSrUniqueTiles.  When no routed areas	*/
/* were found, we assume that there was no routing material overlapping	*/
/* the port.  So, we need to find the area of a tile defining the port	*/
/* so we can look for attaching material.				*/

int
defPortTileFunc(
    Tile *tile,
    TileType dinfo,	/* (unused) */
    TreeContext *cx)
{
    SearchContext *scx = cx->tc_scx;
    Rect *rport = (Rect *)cx->tc_filter->tf_arg;
    Rect r;

    TiToRect(tile, &r);
    GeoTransRect(&scx->scx_trans, &r, rport);

    /* Diagnostic */
    /*
    TxPrintf("Port tile at (%d %d) to (%d %d)\n",
		rport->r_xbot, rport->r_ybot,
		rport->r_xtop, rport->r_ytop);
    */

    return 1;	/* No need to check further */
}

/* Callback function for writing geometry of the network to	*/
/* the DEF file.						*/

int
defNetGeometryFunc(
    Tile *tile,			/* Tile being visited */
    TileType dinfo,		/* Split tile information (unused) */
    int plane,			/* Plane of the tile being visited */
    DefData *defdata)		/* Data passed to this function */
{
    FILE *f = defdata->f;
    CellDef *def = defdata->def;
    float oscale = defdata->scale;
    TileTypeBitMask *rMask, *r2Mask;
    TileType rtype, r2type, ttype = TiGetType(tile);
    Rect r, rorig;
    unsigned char orient;
    bool sameroute = FALSE;
    int routeWidth, w, h, midlinex2, topClip, botClip;
    float x1, y1, x2, y2, extlen;
    lefLayer *lefType, *lefl;
    const char *lefName;
    char viaName[128], posstr[24];
    LefRules *lastruleset = defdata->ruleset;
    HashEntry *he;
    HashTable *defViaTable = defdata->defViaTable;
    LefMapping *MagicToLefTable = defdata->MagicToLefTbl;

    if (ttype == TT_SPACE) return 0;
    TiToRect(tile, &r);

    /* Treat contacts here exactly the same way as defCountVias	*/

    if (DBIsContact(ttype))
    {
	Rect r2;
	Tile *tp;

	rMask = NULL;
	if (ttype >= DBNumUserLayers)
	{
	    /* Stacked contact types need to be broken into their	*/
	    /* constituent types.  Process only if we are on the home	*/
	    /* plane of one of the constituent types.			*/

	    rMask = DBResidueMask(ttype);
	    for (rtype = TT_TECHDEPBASE; rtype < DBNumUserLayers; rtype++)
		if (TTMaskHasType(rMask, rtype))
		    if (DBPlane(rtype) == plane)
		    {
			ttype = rtype;
			break;
		    }
	    if (rtype == DBNumUserLayers)
		return 0;

	}
	else
	    if (DBPlane(ttype) != plane)
		return 0;

	/* Boundary search on stacked contact types to include any	*/
	/* tile areas belonging to ttype.  ONLY check top and right.	*/

	for (tp = BL(tile); BOTTOM(tp) < TOP(tile); tp = RT(tp)) /* Left */
	{
	    r2type = TiGetRightType(tp);
	    if (r2type == ttype)
		return 0;
	    else if (r2type >= DBNumUserLayers)
	    {
		r2Mask = DBResidueMask(r2type);
		if (TTMaskHasType(r2Mask, ttype))
		    return 0;
	    }
	}

	for (tp = LB(tile); LEFT(tp) < RIGHT(tile); tp = TR(tp)) /* Bottom */
	{
	    r2type = TiGetTopType(tp);
	    if (r2type == ttype)
		return 0;
	    else if (r2type >= DBNumUserLayers)
	    {
		r2Mask = DBResidueMask(r2type);
		if (TTMaskHasType(r2Mask, ttype))
		    return 0;
	    }
	}

	/* Extend boundary to top and right */

	for (tp = RT(tile); ; tp = RT(tp)) /* Top */
	{
	    r2type = TiGetBottomType(tp);
	    if (r2type == ttype)
	    {
		TiToRect(tp, &r2);
		GeoInclude(&r2, &r);
	    }
	    else if (r2type >= DBNumUserLayers)
	    {
		r2Mask = DBResidueMask(r2type);
		if (TTMaskHasType(r2Mask, ttype))
		{
		    TiToRect(tp, &r2);
		    GeoInclude(&r2, &r);
		}
		else break;
	    }
	    else break;
	}

	for (tp = TR(tile); ; tp = TR(tp)) /* Right */
	{
	    r2type = TiGetLeftType(tp);
	    if (r2type == ttype)
	    {
		TiToRect(tp, &r2);
		GeoInclude(&r2, &r);
	    }
	    else if (r2type >= DBNumUserLayers)
	    {
		r2Mask = DBResidueMask(r2type);
		if (TTMaskHasType(r2Mask, ttype))
		{
		    TiToRect(tp, &r2);
		    GeoInclude(&r2, &r);
		}
		else break;
	    }
	    else break;
	}
    }
    rorig = r;

    /* Layer names are taken from the LEF database. */

    lefName = MagicToLefTable[ttype].lefName;
    if (lefName == NULL) return 0;	/* Do not write types not in LEF definition */
    lefType = MagicToLefTable[ttype].lefInfo;

    orient = GEO_EAST;
    w = r.r_xtop - r.r_xbot;
    h = r.r_ytop - r.r_ybot;
    midlinex2 = (r.r_ytop + r.r_ybot);

    if (defdata->specialmode != DO_REGULAR)
    {
	routeWidth = (h > w) ? w : h;
	if ((lefType && lefType->lefClass == CLASS_VIA)
			|| (!lefType && DBIsContact(ttype)))
	    orient = GEO_CENTER;
    }
    else
    {
	routeWidth = 0;
	if ((lefType && (lefType->lefClass == CLASS_VIA))
			|| (!lefType && DBIsContact(ttype)))
	    orient = GEO_CENTER;
	else if (lefType)
	    routeWidth = lefType->info.route.width;

	if (routeWidth == 0)
	    routeWidth = DRCGetDefaultLayerWidth(ttype);
    }

    if (orient != GEO_CENTER)	/* not a via type */
    {
	if (h != routeWidth)
	{
	    if ((w == routeWidth) || ((routeWidth == 0) && (h > w)))
	    {
		orient = GEO_NORTH;
		midlinex2 = (r.r_xtop + r.r_xbot);
	    }
	}

	/* Check for non-default widths and slivers */

	defdata->ruleset = NULL;
	if (((h != routeWidth) && (w != routeWidth)) || 
		((h == routeWidth) && (w < routeWidth)) ||
		((w == routeWidth) && (h < routeWidth)))
	{
	    /* Handle slivers.  There are two main cases:
	     * (1) Sliver is part of a via extension, so it can be ignored.
	     * (2) Sliver is a route split into several tiles due to geometry
	     *     to the left.  Expand up and down to include all tiles.
	     *
	     * Slivers that violate design rule widths are assumed to get
	     * merged with another tile to make a full shape.  Slivers that
	     * continue to violate minimum width will be ignored.
	     */

	    if (w < routeWidth) return 0;

	    if (h < routeWidth)
	    {
		/* Check upward. */
		r = rorig;
		r.r_ytop = r.r_ybot + routeWidth;
		r.r_ybot = rorig.r_ytop;
		do
		{
		    r.r_ytop += routeWidth;
		    topClip = r.r_ytop;
		    DBSrPaintArea(tile, def->cd_planes[plane],
				&r, &DBNotConnectTbl[ttype],
				defMaxWireFunc, (ClientData)&topClip);
		}
		while (topClip == r.r_ytop);

		/* Check downward */
		r = rorig;
		r.r_ybot = r.r_ytop - routeWidth;
		r.r_ytop = rorig.r_ybot;
		do
		{
		    r.r_ybot -= routeWidth;
		    botClip = r.r_ybot;
		    DBSrPaintArea(tile, def->cd_planes[plane],
				&r, &DBNotConnectTbl[ttype],
				defMinWireFunc, (ClientData)&botClip);
		}
		while (botClip == r.r_ybot);

		r = rorig;
		if (topClip > r.r_ytop) r.r_ytop = topClip;
		if (botClip < r.r_ybot) r.r_ybot = botClip;

		/* If height is still less that a route width, bail */
		h = r.r_ytop - r.r_ybot;
		if (h < routeWidth) return 0;

		/* If r is larger than rorig, then exempt all unprocessed	*/
		/* tiles contained in rorig from being checked again.		*/

		if (!GEO_SAMERECT(r, rorig))
		    DBSrPaintArea(tile, def->cd_planes[plane],
				&r, &DBConnectTbl[ttype],
				defExemptWireFunc, (ClientData)&r);
	    }

	    /* Handle non-default width regular nets (use TAPERRULE) */
	    if ((h != routeWidth) && (w != routeWidth) &&
			(defdata->specialmode == DO_REGULAR))
	    {
		int ndv = (h > w) ? w : h;
		char ndname[100];
		LefRules *ruleset;

		/*
	    	TxPrintf("Net at (%d, %d) has width %d, default width is %d\n",
			r.r_xbot, r.r_ybot,
			(h < w) ? h : w, routeWidth);
		 */

		/* Create a nondefault rule.  Use one rule per layer and width	*/
		/* for now (to do:  keep the same ruleset when possible)	*/

		sprintf(ndname, "%s_width_%d", lefName, (int)((float)ndv * oscale));
		he = HashFind(&LefNonDefaultRules, ndname);
		ruleset = (LefRules *)HashGetValue(he);
		if (ruleset == NULL)
		{
		    ruleset = (LefRules *)mallocMagic(sizeof(LefRules));
		    HashSetValue(he, ruleset);
		    ruleset->name = StrDup((char **)NULL, ndname);
		    ruleset->rule = (lefRule *)mallocMagic(sizeof(lefRule));
		    ruleset->rule->lefInfo = lefType;
		    ruleset->rule->width = ndv;
		    /* Non-default rules wire extension-at-via is not used. */
		    ruleset->rule->extend = 0;
		    /* Spacing is not needed, but set it to the layer default */
		    ruleset->rule->spacing = DRCGetDefaultLayerSpacing(ttype, ttype);
		    /* There will only be one rule in this ruleset */
		    ruleset->rule->next = NULL;
		}
		defdata->ruleset = ruleset;
	    }

	    /* Set orientation based on longest side */
	    if (h > w)
	    {
		orient = GEO_NORTH;
		midlinex2 = (r.r_xtop + r.r_xbot);
	    }
	    else
	    {
		orient = GEO_EAST;
		midlinex2 = (r.r_ytop + r.r_ybot);
	    }
	}
	else
	    /* Non-default width has ended and the route returns to default rules */
	    defdata->ruleset = NULL;

	/* Find the route orientation and centerline endpoint coordinates */

	if (orient == GEO_EAST)
	{
	    y1 = (midlinex2 * oscale) / 2;
	    y2 = y1;

	    x1 = r.r_xbot * oscale;
	    x2 = r.r_xtop * oscale;
	    if (routeWidth == 0) routeWidth = h;
	    
	    extlen = 0;
	    /* NOTE: non-default tapers are not using wire */
	    /* extension-at-via (need to implement)	   */

	    if (defdata->specialmode == DO_REGULAR)
	    {
		if (defdata->ruleset != NULL)
		{
		    x1 = x1 + (defdata->ruleset->rule->width / 2 * oscale);
		    x2 = x2 - (defdata->ruleset->rule->width / 2 * oscale);
		}
		else
		{
		    x1 = x1 + (routeWidth / 2 * oscale);
		    x2 = x2 - (routeWidth / 2 * oscale);
		}
	    }
	}
	else	/* vertical orientation */
	{
	    x1 = (midlinex2 * oscale) / 2;
	    x2 = x1;

	    y1 = r.r_ybot * oscale;
	    y2 = r.r_ytop * oscale;
	    if (routeWidth == 0) routeWidth = w;
		
	    extlen = 0;
	    /* NOTE: non-default tapers are not using wire */
	    /* extension-at-via (need to implement)	   */
	    if (defdata->specialmode == DO_REGULAR)
	    {
		if (defdata->ruleset != NULL)
		{
		    y1 = y1 + (defdata->ruleset->rule->width / 2 * oscale);
		    y2 = y2 - (defdata->ruleset->rule->width / 2 * oscale);
		}
		else
		{
		    y1 = y1 + (routeWidth / 2 * oscale);
		    y2 = y2 - (routeWidth / 2 * oscale);
		}
	    }
	}
    }
    else	/* Type is a via */
    {
	y1 = y2 = (midlinex2 * oscale) / 2;
	x1 = x2 = ((r.r_xtop + r.r_xbot) * oscale) / 2;
	extlen = 0;
    }

    /* For contact types, find the residues of the contact	*/

    if (orient == GEO_CENTER)
    {
	TileType stype;

	rtype = r2type = TT_SPACE;
	rMask = DBResidueMask(ttype);
	for (stype = TT_TECHDEPBASE; stype < DBNumUserLayers; stype++)
	{
	    if (TTMaskHasType(rMask, stype))
	    {
		if ((rtype == TT_SPACE) &&
			((stype == defdata->type) || (defdata->tile == (Tile *)NULL)))
		    rtype = stype;
		else
		   r2type = stype;
	    }
	}
    }

    /* If we previously visited a tile, write out its second	*/
    /* coordinate pair, adjusting the position if necessary to	*/
    /* make the wire extensions line up correctly.  If they	*/
    /* don't line up, we assume a dogleg route and add		*/
    /* coordinate pairs as necessary to generate the correct	*/
    /* geometry in the DEF output.				*/

    if (defdata->tile)
    {
	Rect r2;
	TiToRect(defdata->tile, &r2);
	/* Only consider the endpoint of the previous tile at X2,Y2 */
	/* And the endpoint of the current tile at X1,Y1 */
	if (defdata->orient == GEO_EAST)
	    r2.r_xbot = r2.r_xtop - 1;
	else if (defdata->orient == GEO_NORTH)
	    r2.r_ybot = r2.r_ytop - 1;

	if (orient == GEO_EAST)
	    r.r_xtop = r.r_xbot + 1;
	else if (orient == GEO_NORTH)
	    r.r_ytop = r.r_ybot + 1;

	/* "sameroute" is true only if rectangles touch in the		*/
	/* direction of the route.					*/
	/* NOTE:  We should compute this FIRST and use it to determine	*/
	/* the current route direction!					*/
	/* Another hack---for special nets, don't continue routes that	*/
	/* have different widths, even if they're connected in the	*/
	/* direction of travel.  A separate record will be written for	*/
	/* the segment of different width.				*/

	if (GEO_TOUCH(&r, &r2))
	{
	    if (defdata->orient == GEO_EAST)
	    {
		if ((r.r_xbot == r2.r_xtop) || (r.r_xtop == r2.r_xbot))
		{
		    sameroute = TRUE;
		    if ((defdata->specialmode != DO_REGULAR) &&
				(r.r_ytop != r2.r_ytop || r.r_ybot != r2.r_ybot))
			sameroute = FALSE;
		}
	    }
	    else if (defdata->orient == GEO_NORTH)
	    {
		if ((r.r_ybot == r2.r_ytop) || (r.r_ytop == r2.r_ybot))
		{
		    sameroute = TRUE;
		    if ((defdata->specialmode != DO_REGULAR) &&
				(r.r_xtop != r2.r_xtop || r.r_xbot != r2.r_xbot))
			sameroute = FALSE;
		}
	    }
	    else
		sameroute = TRUE;
	}

	/* We should NOT continue a route from a via for a special net,	*/
	/* because the spec for this situation is too vaguely defined.	*/

	if (sameroute && (defdata->specialmode != DO_REGULAR) &&
		defdata->orient == GEO_CENTER)
	    sameroute = FALSE;

	/* For now, placement of a via after a taper rule cancels the	*/
	/* taper rule (see above note about combining layer rules).	*/
	if (sameroute && (defdata->ruleset != NULL))
	{
	    lastruleset = NULL;
	    sameroute = FALSE;
	}
    }

    /* If a non-default rule has changed, then we start a new route */
    if (lastruleset != defdata->ruleset)
    {
	sameroute = FALSE;
	lastruleset = defdata->ruleset;
    }

    /* Determine if we need to write a NEW (type) record.  We do this	*/
    /* if 1) this is the first tile visited (except that we don't	*/
    /* write "NEW"), 2) the current tile doesn't touch the last tile	*/
    /* visited, 3) the current type is not equal to the last type, or	*/
    /* 4) a new non-default rule is needed.				*/

    if ((!sameroute) || (ttype != defdata->type))
    {
	/* This is not a continuation of the last route.  Output	*/
	/* the last route position, and start a NEW record.		*/

	if ((sameroute) && (ttype != defdata->type) &&
			(orient == GEO_CENTER) &&
			(rtype == defdata->type))
	{
	    /* Adjust previous route to centerpoint of the via.	 If the	*/
	    /* via is not centered on the route, add segments to create	*/
	    /* the proper alignment.					*/
	    if ((defdata->orient == GEO_NORTH) && (x1 == defdata->x))
		defWriteCoord(defdata, defdata->x, y1, defdata->orient);
	    else if ((defdata->orient == GEO_EAST) && (y1 == defdata->y))
		defWriteCoord(defdata, x1, defdata->y, defdata->orient);
	    else if (defdata->orient == GEO_EAST)
	    {
		defWriteCoord(defdata, x1, defdata->y, defdata->orient);
		defWriteCoord(defdata, x1, y1, GEO_NORTH);
	    }
	    else if (defdata->orient == GEO_NORTH)
	    {
		defWriteCoord(defdata, defdata->x, y1, defdata->orient);
		defWriteCoord(defdata, x1, y1, GEO_EAST);
	    }

	    /* Via type continues route */
    	    sprintf(posstr, "%s_%d_%d", DBPlaneShortName(DBPlane(ttype)),
			rorig.r_xbot, rorig.r_ybot);
    	    he = HashLookOnly(defViaTable, posstr);
	    if (he != NULL)
	    {
	    	lefl = (lefLayer *)HashGetValue(he);
	 	defCheckForBreak(strlen(lefl->canonName) + 2, defdata);
	    	fprintf(f, " %s ", lefl->canonName);
	    }
	    else
	    {
		TxError("Cannot find via name %s in table!\n", posstr);
	    	snprintf(viaName, (size_t)24, "_%.10g_%.10g",
			((float)w * oscale), ((float)h * oscale));
	    	defCheckForBreak(strlen(lefName) + strlen(viaName) + 2, defdata);
	    	fprintf(f, " %s%s ", lefName, viaName);
	    }
	}
	else
	{
	    /* New route segment.  Complete the last route segment. */
	    if (defdata->tile)
	    {
		/* Don't write out a segment for a via */
		if (defdata->orient != GEO_CENTER)
		    defWriteCoord(defdata,
				defdata->x - ((defdata->orient == GEO_EAST) ?
				defdata->extlen : 0),
				defdata->y - ((defdata->orient == GEO_NORTH) ?
				defdata->extlen : 0), defdata->orient);

		fprintf(f, "\n      NEW ");
		defdata->outcolumn = 10;
	    }
	    else
	    {
		/* First record printed for this node */
		fprintf(f, "\n      + ROUTED ");
		defdata->outcolumn = 15;
	    }

	    /* This is the first tile segment visited in the	*/
	    /* current type---use GEO_CENTER so that no		*/
	    /* coordinate wildcards ("*") get written.		*/

	    if (orient == GEO_CENTER)
	    {
		const char *rName;

		/* Type can be zero (space) if the first tile	*/
		/* encountered is a via.  If so, use the 1st	*/
		/* residue of the contact as the route layer	*/
		/* type.					*/

		rName = defGetType((rtype == TT_SPACE) ? r2type : rtype, NULL,
				LAYER_MAP_VIAS);

		/* The first layer in a record may not be a via name */

	        defCheckForBreak(strlen(rName) + 1, defdata);
		fprintf(f, "%s ", rName);
		if (defdata->ruleset != NULL)
		    fprintf(f, "TAPERRULE %s ", defdata->ruleset->name);
		if (defdata->specialmode != DO_REGULAR)
		    defWriteRouteWidth(defdata, routeWidth);
		defWriteCoord(defdata, x1, y1, GEO_CENTER);

    		sprintf(posstr, "%s_%d_%d", DBPlaneShortName(DBPlane(ttype)),
			rorig.r_xbot, rorig.r_ybot);
    		he = HashLookOnly(defViaTable, posstr);
		if (he != NULL)
		{
		    lefl = (lefLayer *)HashGetValue(he);
	 	    defCheckForBreak(strlen(lefl->canonName) + 2, defdata);
	    	    fprintf(f, " %s ", lefl->canonName);
	        }
		else
		{
		    TxError("Cannot find via name %s in table!\n", posstr);
		    snprintf(viaName, (size_t)24, "_%.10g_%.10g",
				((float)w * oscale), ((float)h * oscale));
	 	    defCheckForBreak(strlen(lefName) + strlen(viaName) + 2, defdata);
		    fprintf(f, " %s%s ", lefName, viaName);
		}
	    }
	    else
	    {
	        defCheckForBreak(strlen(lefName) + 1, defdata);
		fprintf(f, "%s ", lefName);
		if (defdata->ruleset != NULL)
		    fprintf(f, "TAPERRULE %s ", defdata->ruleset->name);
		if (defdata->specialmode != DO_REGULAR)
		    defWriteRouteWidth(defdata, routeWidth);

		/* defWriteCoord(defdata, x1, y1, GEO_CENTER); */
		defWriteCoord(defdata,
			x1 + ((orient == GEO_EAST) ?  extlen : 0),
			y1 + ((orient == GEO_NORTH) ?  extlen : 0),
			GEO_CENTER);
	    }
	}
    }
    else if (sameroute)
    {
	/* Adjust the previous route segment to match the new segment,	*/
	/* and write out the previous route segment record.		*/
	if ((orient == defdata->orient) && (defdata->x != x1) && (defdata->y != x2))
	{
	    /* Dogleg---insert extra segment */
	    defWriteCoord(defdata,
			defdata->x - ((defdata->orient == GEO_EAST) ?
			defdata->extlen : 0),
			defdata->y - ((defdata->orient == GEO_NORTH) ?
			defdata->extlen : 0), defdata->orient);
	    defWriteCoord(defdata, x1 + ((orient == GEO_EAST) ?  extlen : 0),
			y1 + ((orient == GEO_NORTH) ?  extlen : 0), orient);
	}
	else
	{
	    if (defdata->orient == GEO_EAST)
	    {
		if (((defdata->x + defdata->extlen) == x1) ||
			((defdata->x - defdata->extlen) == x1))
		    defWriteCoord(defdata, x1, defdata->y, defdata->orient);
		else
		{
		    /* Don't know how to connect the route segments. */
		    /* End the original route and start a new one. */
		    defWriteCoord(defdata,
				defdata->x - ((defdata->orient == GEO_EAST) ?
				defdata->extlen : 0),
				defdata->y - ((defdata->orient == GEO_NORTH) ?
				defdata->extlen : 0), defdata->orient);
		    fprintf(f, "\n      NEW %s", lefName);
		    defdata->outcolumn = 10 + strlen(lefName);
		    if (defdata->specialmode != DO_REGULAR)
		    {
			fprintf(f, " ");
			defdata->outcolumn++;
			defWriteRouteWidth(defdata, routeWidth);
		    }
		    defWriteCoord(defdata,
				x1 + ((orient == GEO_EAST) ?  extlen : 0),
				y1 + ((orient == GEO_NORTH) ?  extlen : 0),
				GEO_CENTER);
		}
	    }
	    else if (defdata->orient == GEO_NORTH)
	    {
		if (((defdata->y + defdata->extlen) == y1) ||
			((defdata->y - defdata->extlen) == y1))
		    defWriteCoord(defdata, defdata->x, y1, defdata->orient);
		else
		{
		    /* Don't know how to connect the route segments. */
		    /* End the original route and start a new one. */
		    defWriteCoord(defdata,
				defdata->x - ((defdata->orient == GEO_EAST) ?
				defdata->extlen : 0),
				defdata->y - ((defdata->orient == GEO_NORTH) ?
				defdata->extlen : 0), defdata->orient);
		    fprintf(f, "\n      NEW %s", lefName);
		    if (defdata->specialmode != DO_REGULAR) {
			fprintf(f, " ");
			defdata->outcolumn++;
			defWriteRouteWidth(defdata, routeWidth);
		    }
		    defdata->outcolumn = 10 + strlen(lefName);
		    defWriteCoord(defdata,
				x1 + ((orient == GEO_EAST) ?  extlen : 0),
				y1 + ((orient == GEO_NORTH) ?  extlen : 0),
				GEO_CENTER);
		}
	    }
	    else	/* last record was a via */
	    {
		/* Continuing route from via to other connecting layer type */
		/* Bend to meet via center---insert extra segment */

		if ((orient == GEO_NORTH) && (x1 != defdata->x))
		    defWriteCoord(defdata, x1, defdata->y, GEO_EAST);

		else if ((orient == GEO_EAST) && (y1 != defdata->y))
		    defWriteCoord(defdata, defdata->x, y1, GEO_NORTH);
	    }
	}
    }

    /* After a contact type, the route coordinates may continue in the	*/
    /* routing type connected by the contact to the type that was	*/
    /* previously seen connected to the contact.			*/

    /* NOTE!  The above comment matches the example on page 203 of the	*/
    /* LEF/DEF reference manual.  However, it is obvious that it is	*/
    /* logically fallacious.  A via can be declared to be multiple	*/
    /* types, and there is no way to know which type continues the	*/
    /* route without it being explicitly stated.  Nevertheless, that	*/
    /* is the way it's implemented. . .					*/

    if ((orient == GEO_CENTER) && (rtype != TT_SPACE) && (r2type != TT_SPACE))
	defdata->type = r2type;
    else if (orient == GEO_CENTER)
	defdata->type = TT_SPACE;
    else
	defdata->type = ttype;

    defdata->x = x2;
    defdata->y = y2;
    defdata->extlen = extlen;
    defdata->tile = tile;
    defdata->orient = orient;
    return 0;	/* Keep going */
}

/*
 *------------------------------------------------------------
 *
 * defCountVias --
 *
 *	First-pass function to count the number of different
 *	vias used, and retain this information for the netlist
 *	output.
 *
 * Results:
 *	The total number of via definitions to be written.
 *
 * Side Effects:
 *	None.
 *
 *------------------------------------------------------------
 */

int
defCountVias(
    CellDef *rootDef,
    LefMapping *MagicToLefTable,
    HashTable *defViaTable,
    float oscale)
{
    TileTypeBitMask contactMask, *rmask;
    TileType ttype, stype;
    int pNum;
    CViaData cviadata;

    /* Forward declaration */
    int defCountViaFunc(Tile *tile, TileType dinfo, CViaData *cviadata);

    cviadata.scale = oscale;
    cviadata.total = 0;
    cviadata.MagicToLefTbl = MagicToLefTable;
    cviadata.defViaTable = defViaTable;
    cviadata.def = rootDef;

    for (pNum = PL_SELECTBASE; pNum < DBNumPlanes; pNum++)
    {
        /* Only search for contacts that are on their *home* plane */

	TTMaskZero(&contactMask);
	for (ttype = TT_TECHDEPBASE; ttype < DBNumUserLayers; ttype++)
	    if (DBIsContact(ttype) && TTMaskHasType(&DBPlaneTypes[pNum], ttype))
		TTMaskSetType(&contactMask, ttype);

	/* Also search all stacked types whose residue contact types	*/
	/* are in the mask just generated.				*/

	for (ttype = DBNumUserLayers; ttype < DBNumTypes; ttype++)
	{
	    if (!DBIsContact(ttype)) continue;
	    rmask = DBResidueMask(ttype);
	    for (stype = TT_TECHDEPBASE; stype < DBNumUserLayers; stype++)
		if (TTMaskHasType(rmask, stype))
		{
		    TTMaskSetType(&contactMask, ttype);
		    break;
		}
	}
	cviadata.mask = &contactMask;
	cviadata.plane = pNum;

	DBSrPaintArea((Tile *)NULL, rootDef->cd_planes[pNum],
			&TiPlaneRect, &contactMask,
			defCountViaFunc, (ClientData)&cviadata);
    }
    return cviadata.total;
}

/* Simple callback function used by defCountViaFunc */

int
defCheckFunc(
    Tile *tile,
    TileType dinfo,		/* (unused) */
    ClientData clientdata)	/* (unused) */
{
    return 1;
}

/* Callback function used by defCountVias */

int
defCountViaFunc(
    Tile *tile,
    TileType dinfo,		/* (unused) */
    CViaData *cviadata)
{
    TileType ttype = TiGetType(tile), ctype, rtype;
    TileTypeBitMask *rmask, *rmask2;
    Tile *tp;
    const char *lname;
    char vname[100], posstr[24];
    Rect r, r2, rorig;
    int w, h, offx, offy, sdist, lorient, horient;
    int ldist, hdist, sldist, shdist, pNum;
    TileType ltype, htype;
    float oscale = cviadata->scale;
    lefLayer *lefl;
    LinkedRect *newlr;
    CellDef *def = cviadata->def;
    HashEntry *he;
    HashTable *defViaTable = cviadata->defViaTable;
    LefMapping *MagicToLefTable = cviadata->MagicToLefTbl;

    /* Techfiles are allowed not to declare a LEF entry, in which */
    /* case we would need to initialize the hash table.		  */
    if (LefInfo.ht_table == (HashEntry **) NULL) LefTechInit();

    /* If type is a stacked contact, find the residue on the search plane */
    if (ttype >= DBNumUserLayers)
    {
	rmask = DBResidueMask(ttype);
	for (ctype = TT_TECHDEPBASE; ctype < DBNumUserLayers; ctype++)
	    if (TTMaskHasType(rmask, ctype))
		if (DBPlane(ctype) == cviadata->plane)
		    break;
	if (ctype == DBNumUserLayers)
	    return 1;			/* Error condition */
    }
    else
    {
	rmask = NULL;
	ctype = ttype;
    }

    /* Generate a via name from the layer name and tile size */

    lname = MagicToLefTable[ctype].lefName;
    if (lname == NULL) return 0;    /* Do not output undefined LEF layers */
    TiToRect(tile, &r);

    /* Boundary search.  WARNING:  This code is quite naive.  The	*/
    /* assumption is that all contacts are rectangular, and therefore	*/
    /* any contact area consisting of multiple tiles must be an amalgam	*/
    /* of regular and/or stacked types.  This whole thing should be	*/
    /* replaced by calls to generate layers via the CIF/Calma code.	*/

    /* If any matching tile exists to left or bottom, then return	*/
    /* immediately.  Only expand areas for which this is the bottom and	*/
    /* left-most contact tile.						*/

    /* Left */
    for (tp = BL(tile); BOTTOM(tp) < TOP(tile); tp = RT(tp))
    {
	rtype = TiGetRightType(tp);
	if (rtype == ctype)
	    return 0;
	else if (rtype >= DBNumUserLayers)
	{
	    rmask2 = DBResidueMask(rtype);
	    if (TTMaskHasType(rmask2, ctype))
		return 0;
	}
    }

    /* Bottom */
    for (tp = LB(tile); LEFT(tp) < RIGHT(tile); tp = TR(tp))
    {
	rtype = TiGetTopType(tp);
	if (rtype == ctype)
	    return 0;
	else if (rtype >= DBNumUserLayers)
	{
	    rmask2 = DBResidueMask(rtype);
	    if (TTMaskHasType(rmask2, ctype))
		return 0;
	}
    }

    /* Expand to top and right until the whole contact area has been found */

    /* Top */
    for (tp = RT(tile); ; tp = RT(tp))
    {
	rtype = TiGetBottomType(tp);
	if (rtype == ctype)
	{
	    TiToRect(tp, &r2);
	    GeoInclude(&r2, &r);
	}
	else if (rtype >= DBNumUserLayers)
	{
	    rmask2 = DBResidueMask(rtype);
	    if (TTMaskHasType(rmask2, ctype))
	    {
	    	TiToRect(tp, &r2);
	    	GeoInclude(&r2, &r);
	    }
	    else break;
	}
	else break;
    }

    /* Right */
    for (tp = TR(tile); ; tp = TR(tp))
    {
	rtype = TiGetLeftType(tp);
	if (rtype == ctype)
	{
	    TiToRect(tp, &r2);
	    GeoInclude(&r2, &r);
	}
	else if (rtype >= DBNumUserLayers)
	{
	    rmask2 = DBResidueMask(rtype);
	    if (TTMaskHasType(rmask2, ctype))
	    {
		TiToRect(tp, &r2);
		GeoInclude(&r2, &r);
	    }
	    else break;
	}
	else break;
    }

    /* All values for the via rect are in 1/2 lambda to account */
    /* for a centerpoint not on the internal grid.		*/

    rorig = r;
    r.r_xbot <<= 1;
    r.r_xtop <<= 1;
    r.r_ybot <<= 1;
    r.r_ytop <<= 1;

    w = r.r_xtop - r.r_xbot;
    h = r.r_ytop - r.r_ybot;
    offx = (w >> 1);
    offy = (h >> 1);

    /* Center the via area on the origin */
    r.r_xbot = -offx;
    r.r_ybot = -offy;
    r.r_xtop = -offx + w;
    r.r_ytop = -offy + h;

    /* If the via type has directional surround rules, then determine	*/
    /* the orientation of the lower and upper metal layers and add a	*/
    /* suffix to the via name.						*/

    rmask = DBResidueMask(ctype);
    ldist = hdist = 0;
    ltype = htype = TT_SPACE;
    for (rtype = TT_TECHDEPBASE; rtype < DBNumUserLayers; rtype++)
	if (TTMaskHasType(rmask, rtype))
	{
	    sdist = DRCGetDefaultLayerSurround(ctype, rtype);
	    if (ltype == TT_SPACE)
		sldist = sdist;
	    else
		shdist = sdist;

	    sdist = DRCGetDirectionalLayerSurround(ctype, rtype);
	    if (ltype == TT_SPACE)
	    {
		ldist = sdist;
		ltype = rtype;
	    }
	    else
	    {
		hdist = sdist;
		htype = rtype;
		break;
	    }
	}
    lorient = horient = 0;
    if (ldist > 0)
    {
	r2.r_ybot = rorig.r_ybot - sldist;
	r2.r_ytop = rorig.r_ytop + sldist;
	r2.r_xbot = rorig.r_xbot - ldist + sldist;
	r2.r_xtop = rorig.r_xtop + ldist + sldist;
	pNum = DBPlane(ltype);
	lorient = DBSrPaintArea((Tile *)NULL, def->cd_planes[pNum], &r2,
			&DBNotConnectTbl[ltype], defCheckFunc,
			(ClientData)NULL);
    }
    if (hdist > 0)
    {
	r2.r_ybot = rorig.r_ybot - shdist;
	r2.r_ytop = rorig.r_ytop + shdist;
	r2.r_xbot = rorig.r_xbot - hdist + shdist;
	r2.r_xtop = rorig.r_xtop + hdist + shdist;
	pNum = DBPlane(htype);
	horient = DBSrPaintArea((Tile *)NULL, def->cd_planes[pNum], &r2,
			&DBNotConnectTbl[htype], defCheckFunc,
			(ClientData)NULL);
    }
    if ((ldist > 0) || (hdist > 0))
    {
	sprintf(vname, "%s_%.10g_%.10g_%c%c", lname,
			((float)offx * oscale), ((float)offy * oscale),
			(ldist == 0) ? 'x' : (lorient == 0) ? 'h' : 'v',
			(hdist == 0) ? 'x' : (horient == 0) ? 'h' : 'v');
    }
    else
	sprintf(vname, "%s_%.10g_%.10g", lname,
			((float)offx * oscale), ((float)offy * oscale));

    he = HashFind(&LefInfo, vname);
    lefl = (lefLayer *)HashGetValue(he);
    if (lefl == NULL)
    {
	cviadata->total++;	/* Increment the count of uses */
	lefl = (lefLayer *)mallocMagic(sizeof(lefLayer));
	lefl->type = ctype;
	lefl->obsType = -1;
	lefl->lefClass = CLASS_VIA;
	lefl->info.via.area = r;
	lefl->info.via.cell = (CellDef *)NULL;
	lefl->info.via.lr = (LinkedRect *)NULL;
	lefl->refCnt = 0;	/* These entries will be removed after writing */
	HashSetValue(he, lefl);
	lefl->canonName = (const char *)he->h_key.h_name;

	if ((sldist > 0) || (ldist > 0))
	{
	    newlr = (LinkedRect *)mallocMagic(sizeof(LinkedRect));
	    newlr->r_next = lefl->info.via.lr;
	    lefl->info.via.lr = newlr;
	    newlr->r_type = ltype;
	    r2.r_xbot = r.r_xbot - 2 * sldist;
	    r2.r_xtop = r.r_xtop + 2 * sldist;
	    r2.r_ybot = r.r_ybot - 2 * sldist;
	    r2.r_ytop = r.r_ytop + 2 * sldist;
	    if (ldist > 0)
	    {
		if (lorient == 0)
		{
		    r2.r_xbot -= 2 * ldist;
		    r2.r_xtop += 2 * ldist;
		}
		else
		{
		    r2.r_ybot -= 2 * ldist;
		    r2.r_ytop += 2 * ldist;
		}
	    }
	    newlr->r_r = r2;
	}
	if ((shdist > 0) || (hdist > 0))
	{
	    newlr = (LinkedRect *)mallocMagic(sizeof(LinkedRect));
	    newlr->r_next = lefl->info.via.lr;
	    lefl->info.via.lr = newlr;
	    newlr->r_type = htype;
	    r2.r_xbot = r.r_xbot - 2 * shdist;
	    r2.r_xtop = r.r_xtop + 2 * shdist;
	    r2.r_ybot = r.r_ybot - 2 * shdist;
	    r2.r_ytop = r.r_ytop + 2 * shdist;
	    if (hdist > 0)
	    {
		if (horient == 0)
		{
		    r2.r_xbot -= 2 * hdist;
		    r2.r_xtop += 2 * hdist;
		}
		else
		{
		    r2.r_ybot -= 2 * hdist;
		    r2.r_ytop += 2 * hdist;
		}
	    }
	    newlr->r_r = r2;
	}
    }
    /* Record this tile position in the contact hash table */
    sprintf(posstr, "%s_%d_%d", DBPlaneShortName(DBPlane(ctype)),
		rorig.r_xbot, rorig.r_ybot);
    he = HashFind(defViaTable, posstr);
    HashSetValue(he, lefl);

    /* TxPrintf("Via name \"%s\" hashed as \"%s\"\n", lefl->canonName, posstr); */

    return 0;	/* Keep the search going */
}

/*
 *------------------------------------------------------------
 *
 * defGetType --
 *
 *	Retrieve the LEF/DEF name of a magic layer from the
 *	LefInfo hash table.
 *
 * Results:
 *	The "official" LEF/DEF layer name of the magic type.
 *
 * Side Effects:
 *	If "lefptr" is non-NULL, it is filled with a pointer
 *	to the appropriate lefLayer entry, or NULL if there
 *	is no corresponding entry.
 *------------------------------------------------------------
 */

const char *
defGetType(
    TileType ttype,
    lefLayer **lefptr,
    bool do_vias)
{
    HashSearch hs;
    HashEntry *he;
    lefLayer *lefl;
    int contact = DBIsContact(ttype) ? CLASS_VIA : CLASS_ROUTE;

    /* Pick up information from the original LefInfo hash table	*/
    /* entries created during read-in of the tech file.		*/

    if (LefInfo.ht_table != (HashEntry **) NULL)
    {
	HashStartSearch(&hs);
	while ((he = HashNext(&LefInfo, &hs)))
	{
	    lefl = (lefLayer *)HashGetValue(he);
	    if (lefl && (do_vias == FALSE) && (contact == CLASS_VIA) &&
			(lefl->lefClass == CLASS_VIA))
		continue;	/* Skip VIA definitions if do_vias is FALSE */
	    
	    if (lefl && ((contact == lefl->lefClass) ||
			((contact == CLASS_ROUTE) && (lefl->lefClass == CLASS_MASTER))))
		if ((lefl->type == ttype) || (lefl->obsType == ttype))
		{
		    if (lefptr) *lefptr = lefl;
		    return lefl->canonName;
		}
	}
    }

    /* If we got here, there is no entry;  return NULL. */
    if (lefptr) *lefptr = (lefLayer *)NULL;
    return NULL;
}

/*
 *------------------------------------------------------------
 *
 * defWriteVias --
 *
 *	Output the VIAS section of a DEF file.  We equate magic
 *	contact areas with DEF "VIAS".  A separate via entry is
 *	generated for each unique geometry.  The exact output
 *	is determined from the CIF output rules.
 *
 * Results:
 *	None.
 *
 * Side Effects:
 *	Output written to the DEF output file.
 *
 *------------------------------------------------------------
 */

void
defWriteVias(
    FILE *f,				/* File to write to */
    CellDef *rootDef,			/* Cell definition to use */
    float oscale,			/* Output scale factor */
    LefMapping *lefMagicToLefLayer)
{
    HashSearch hs;
    HashEntry *he;
    lefLayer *lefl;
    TileTypeBitMask *rMask;
    TileType ttype;
    Rect *r;
    LinkedRect *lr;
    float cscale;

    /* Pick up information from the LefInfo hash table	*/
    /* created by fucntion defCountVias()		*/

    if (LefInfo.ht_table != (HashEntry **) NULL)
    {
	cscale = CIFGetOutputScale(1);

	HashStartSearch(&hs);
	while ((he = HashNext(&LefInfo, &hs)))
	{
	    int size, sep, border;
	    const char *us1, *us2;
	    lefl = (lefLayer *)HashGetValue(he);
	    if (!lefl) continue;

	    /* Only count the generated vias of the type name_sizex_sizey */

	    if ((us1 = strchr(lefl->canonName, '_')) == NULL ||
	    		(us2 = strrchr(lefl->canonName, '_')) == us1)
		continue;

	    if (lefl->lefClass == CLASS_VIA)
	    {
		fprintf(f, "   - %s", (char *)lefl->canonName);

		/* Generate squares for the area as determined	*/
		/* by the cifoutput section of the tech file	*/

		rMask = DBResidueMask(lefl->type);
		for (ttype = TT_TECHDEPBASE; ttype < DBNumUserLayers; ttype++)
		    if (TTMaskHasType(rMask, ttype))
		    {
			r = &lefl->info.via.area;

			/* If an lr entry was made, then it includes	*/
			/* any required surround distance, so use that	*/
			/* rectangle instead of the via area.		*/

			for (lr =lefl->info.via.lr; lr; lr = lr->r_next)
			    if (lr->r_type == ttype)
				r = &lr->r_r;

			fprintf(f, "\n      + RECT %s ( %.10g %.10g ) ( %.10g %.10g )",
				lefMagicToLefLayer[ttype].lefName,
				(float)(r->r_xbot) * oscale / 2,
				(float)(r->r_ybot) * oscale / 2,
				(float)(r->r_xtop) * oscale / 2,
				(float)(r->r_ytop) * oscale / 2);
		    }

		/* Handle the contact cuts.	*/

		if (CIFGetContactSize(lefl->type, &size, &sep, &border))
		{
		    int i, j, nAc, nUp, pitch, left;
		    Rect square, rect = lefl->info.via.area, *r;
		    r = &rect;

		    /* Scale contact dimensions to the output units */
		    size *= oscale;
		    sep *= oscale;
		    border *= oscale;

		    size /= cscale;
		    sep /= cscale;
		    border /= cscale;

		    /* Scale the area to output units */
		    r->r_xbot *= oscale;
		    r->r_ybot *= oscale;
		    r->r_xtop *= oscale;
		    r->r_ytop *= oscale;

		    r->r_xbot /= 2;
		    r->r_ybot /= 2;
		    r->r_xtop /= 2;
		    r->r_ytop /= 2;

		    pitch = size + sep;
		    nAc = (r->r_xtop - r->r_xbot + sep - (2 * border)) / pitch;
		    if (nAc == 0)
		    {
			left = (r->r_xbot + r->r_xtop - size) / 2;
			nAc = 1;
			if (left < r->r_xbot)
			{
			    TxError("Warning: via size is %d but area width is %d!\n",
					size, (r->r_xtop - r->r_xbot));
			}
		    }
		    else
			left = (r->r_xbot + r->r_xtop + sep - (nAc * pitch)) / 2;

		    nUp = (r->r_ytop - r->r_ybot + sep - (2 * border)) / pitch;
		    if (nUp == 0)
		    {
			square.r_ybot = (r->r_ybot + r->r_ytop - size) / 2;
			nUp = 1;
			if (square.r_ybot >= r->r_ybot)
			{
			    TxError("Warning: via size is %d but area height is %d!\n",
					size, (r->r_ytop - r->r_ybot));
			}
		    }
		    else
			square.r_ybot = (r->r_ybot + r->r_ytop + sep - (nUp * pitch)) / 2;

		    for (i = 0; i < nUp; i++)
		    {
			square.r_ytop = square.r_ybot + size;
			square.r_xbot = left;
			for (j = 0; j < nAc; j++)
			{
			     square.r_xtop = square.r_xbot + size;

			     fprintf(f, "\n      + RECT %s ( %.10g %.10g ) "
					"( %.10g %.10g )",
					lefMagicToLefLayer[lefl->type].lefName,
					(float)(square.r_xbot),
					(float)(square.r_ybot),
					(float)(square.r_xtop),
					(float)(square.r_ytop));
			     square.r_xbot += pitch;
			}
			square.r_ybot += pitch;
		    }
		}
		else
		    /* If we can't find the CIF/GDS parameters for cut	*/
		    /* generation, then output a single rectangle the	*/
		    /* size of the contact tile.			*/
		{
		    fprintf(f, "\n      + RECT %s ( %.10g %.10g ) ( %.10g %.10g )",
			lefMagicToLefLayer[lefl->type].lefName,
			(float)(lefl->info.via.area.r_xbot) * oscale / 2,
			(float)(lefl->info.via.area.r_ybot) * oscale / 2,
			(float)(lefl->info.via.area.r_xtop) * oscale / 2,
			(float)(lefl->info.via.area.r_ytop) * oscale / 2);
	 	}
		fprintf(f, " ;\n");
	    }
	}
    }
}

/*
 *------------------------------------------------------------
 *
 * defCountComponents --
 *
 *	First-pass function to count the number of cell
 *	uses (components) to be written to the DEF output
 *	file.
 *
 * Results:
 *	The total number of uses to be written.
 *
 * Side Effects:
 *	None.
 *
 *------------------------------------------------------------
 */

int
defCountComponents(
    CellDef *rootDef)
{
    pointertype total;
    int defCountCompFunc(CellUse *cellUse, pointertype *total);

    TxPrintf("Diagnostic:  Finding all components of cell %s\n", rootDef->cd_name);

    total = 0;
    DBCellEnum(rootDef, defCountCompFunc, (ClientData)&total);
    return (int)total;
}

/* Callback function used by defCountComponents */

int
defCountCompFunc(
    CellUse *cellUse,
    pointertype *total)
{
    /* Ignore any cellUse that does not have an identifier string. */
    if (cellUse->cu_id == NULL) return 0;

    /* Make sure that arrays are counted correctly */
    int sx = cellUse->cu_xhi - cellUse->cu_xlo + 1;
    int sy = cellUse->cu_yhi - cellUse->cu_ylo + 1;
    // TxPrintf("Diagnostic: cell %s %d %d\n", cellUse->cu_id, sx, sy);
    ASSERT(sx >= 0 && sy >= 0, "Valid array");
    (*total) += (unsigned long)sx * sy;	/* Increment the count of uses */

    return 0;	/* Keep the search going */
}

/*
 *------------------------------------------------------------
 *
 * defCountPins --
 *
 *	First-pass function to count the number of pins
 *	to be written to the DEF output file.
 *
 * Results:
 *	The total number of pins to be written.
 *
 * Side Effects:
 *	None.
 *
 *------------------------------------------------------------
 */

int
defCountPins(
    CellDef *rootDef)
{
    int total;
    Label *lab;

    TxPrintf("Diagnostic:  Finding all pins of cell %s\n", rootDef->cd_name);

    total = 0;
    for (lab = rootDef->cd_labels; lab; lab = lab->lab_next)
	if (lab->lab_flags & PORT_DIR_MASK)
	    total++;

    return total;
}

/*
 *------------------------------------------------------------
 *
 * defWritePins --
 *
 *	Output the PINS section of the DEF file.  This
 *	is a listing of all ports, their placement, and
 *	name.
 *
 * Results:
 *	None.
 *
 * Side Effects:
 *	Output to the DEF file.
 *
 *------------------------------------------------------------
 */

void
defWritePins(
    FILE *f,				/* File to write to */
    CellDef *rootDef,			/* Cell definition to use */
    LefMapping *lefMagicToLefLayer,	/* Magic to LEF layer name mapping */
    float oscale)			/* Output scale factor */
{
    Label *lab;
    int lwidth, lheight;
    int dcenterx, dcentery;

    for (lab = rootDef->cd_labels; lab; lab = lab->lab_next)
    {
	if (lab->lab_flags & PORT_DIR_MASK)
	{
	    fprintf(f, "   - %s + NET %s\n", lab->lab_text, lab->lab_text);
	    if (lab->lab_flags & PORT_CLASS_MASK)
	    {
		fprintf(f, "     + DIRECTION ");
		switch (lab->lab_flags & PORT_CLASS_MASK)
		{
		    case PORT_CLASS_INPUT:
			fprintf(f, "INPUT");
			break;
		    case PORT_CLASS_OUTPUT:
			fprintf(f, "OUTPUT");
			break;
		    case PORT_CLASS_TRISTATE:
		    case PORT_CLASS_BIDIRECTIONAL:
			fprintf(f, "INOUT");
			break;
		    case PORT_CLASS_FEEDTHROUGH:
			fprintf(f, "FEEDTHRU");
			break;
		}
		fprintf(f, "\n");
	    }
	    if (lab->lab_flags & PORT_USE_MASK)
	    {
		fprintf(f, "     + USE ");
		switch (lab->lab_flags & PORT_USE_MASK)
		{
		    case PORT_USE_SIGNAL:
			fprintf(f, "SIGNAL");
			break;
		    case PORT_USE_ANALOG:
			fprintf(f, "ANALOG");
			break;
		    case PORT_USE_POWER:
			fprintf(f, "POWER");
			break;
		    case PORT_USE_GROUND:
			fprintf(f, "GROUND");
			break;
		    case PORT_USE_CLOCK:
			fprintf(f, "CLOCK");
			break;
		}
		fprintf(f, "\n");
	    }

	    lwidth = lab->lab_rect.r_xtop - lab->lab_rect.r_xbot;
	    lheight = lab->lab_rect.r_ytop - lab->lab_rect.r_ybot;

	    dcenterx = lab->lab_rect.r_xtop + lab->lab_rect.r_xbot;
	    dcentery = lab->lab_rect.r_ytop + lab->lab_rect.r_ybot;

	    fprintf(f, "     + PORT\n");
    	    if (lefMagicToLefLayer[lab->lab_type].lefName == NULL)
		TxError("No LEF layer corresponding to layer %s of pin \"%s\".\n",
			lab->lab_text,
			DBTypeLongNameTbl[lab->lab_type]);
	    else
		fprintf(f, "        + LAYER %s ( %.10g %.10g ) ( %.10g %.10g )",
    	    	    	lefMagicToLefLayer[lab->lab_type].lefName,
		    	oscale * (float)(-lwidth) / 2.0, oscale * (float)(-lheight) / 2.0,
		    	oscale * (float)lwidth / 2.0, oscale * (float)lheight / 2.0);

	    fprintf(f, "        + PLACED ( %.10g %.10g ) N ;\n",
			oscale * (float)dcenterx / 2.0, oscale * (float)dcentery / 2.0);
	}
    }
}

/*
 *------------------------------------------------------------
 *
 * defWriteBlockages --
 *
 *	Output the BLOCKAGES section of a DEF file.  Write
 *	geometry for any layer that is defined as an
 *	obstruction layer in the technology file "lef"
 *	section.
 *
 * Results:
 *	None.
 *
 * Side Effects:
 *	Output written to the DEF output file.
 *
 *------------------------------------------------------------
 */

void
defWriteBlockages(
    FILE *f,				/* File to write to */
    CellDef *rootDef,			/* Cell definition to use */
    float oscale,			/* Output scale factor */
    LefMapping *MagicToLefTable)	/* Magic to LEF layer mapping */
{
    DefObsData defobsdata;
    lefLayer *lefl;
    int i, numblocks, nonempty, pNum;
    LinkedRect *lr;
    HashSearch hs;
    HashEntry *he;
    TileTypeBitMask ExtraObsLayersMask;

    /* Forward declarations */
    int defSimpleBlockageFunc(Tile *tile, TileType dinfo, DefObsData *defobsdata);
    int defblockageVisit(EFNode *node, int res, EFCapValue cap, DefObsData *defobsdata);

    defobsdata.def = rootDef;
    defobsdata.nlayers = 0;

    TTMaskZero(&ExtraObsLayersMask);

    /* Blockages are done by layer.  Create one blockage per route	*/
    /* layer, and ignore vias.						*/

    numblocks = 0;
    if (LefInfo.ht_table != (HashEntry **) NULL)
    {
	HashStartSearch(&hs);
	while ((he = HashNext(&LefInfo, &hs)))
	{
	    lefl = (lefLayer *)HashGetValue(he);
	    if (lefl != NULL)
		if ((lefl->lefClass == CLASS_ROUTE) || (lefl->lefClass == CLASS_VIA))
		    numblocks++;
	}

	defobsdata.nlayers = numblocks;
	defobsdata.blockMasks = (TileTypeBitMask *)mallocMagic(numblocks *
			sizeof(TileTypeBitMask));
	defobsdata.blockData = (LinkedRect **)mallocMagic(numblocks *
			sizeof(LinkedRect *));
	defobsdata.baseNames = (const char **)mallocMagic(numblocks *
			sizeof(char *));

	if (numblocks > 0)
	{
	    numblocks = 0;
	    HashStartSearch(&hs);
	    while ((he = HashNext(&LefInfo, &hs)))
	    {
		lefl = (lefLayer *)HashGetValue(he);
		if ((lefl != NULL) && ((lefl->lefClass == CLASS_ROUTE) ||
				(lefl->lefClass == CLASS_VIA)))
		{
		    const char *llayer;
		    if (lefl->lefClass == CLASS_ROUTE)
			llayer = lefl->canonName;
		    else
			llayer = MagicToLefTable[lefl->type].lefName;

		    defobsdata.baseNames[numblocks] = llayer;
		    TTMaskSetOnlyType(&defobsdata.blockMasks[numblocks], lefl->type);
		    if (lefl->obsType != -1)
		    {
		    	TTMaskSetType(&defobsdata.blockMasks[numblocks], lefl->obsType);
			/* If the obstruction type is not an active electrical
			 * type, then add it to the mask of layers to search
			 * separately from extracted nodes.
			 */
			if (!TTMaskHasType(&ExtCurStyle->exts_activeTypes,
					lefl->obsType))
			    TTMaskSetType(&ExtraObsLayersMask, lefl->obsType);
		    }
		    defobsdata.blockData[numblocks] = NULL;
		    numblocks++;
		}
	    }
	}
    }
    if (numblocks > 0)
	EFVisitNodes(defblockageVisit, (ClientData)&defobsdata);

    /* If obstruction types are marked as non-electrical layers (which
     * normally they are), then they will not be extracted as nodes.
     * Do a separate search on obstruction areas and add them to the
     * list of blockages.  To be considered, the layer must have a
     * corresponding LEF route or via layer type, that LEF record must
     * define an obstruction type, and that obstruction type must not
     * be in the active layer list.
     */

    for (pNum = PL_SELECTBASE; pNum < DBNumPlanes; pNum++)
    {
    	DBSrPaintArea((Tile *)NULL, rootDef->cd_planes[pNum], &TiPlaneRect,
		&ExtraObsLayersMask, defSimpleBlockageFunc,
		(ClientData)&defobsdata);
    }

    /* Quick check for presence of data to write */
 
    nonempty = 0;
    for (i = 0; i < numblocks; i++)
	if (defobsdata.blockData[i] != NULL)
	    nonempty++;

    if (nonempty > 0)
    {
    	fprintf(f, "BLOCKAGES %d ;\n", nonempty);

    	for (i = 0; i < numblocks; i++)
    	{
	    if (defobsdata.blockData[i] == NULL) continue;
	    fprintf(f, "   - LAYER %s", defobsdata.baseNames[i]);
	    free_magic1_t mm1 = freeMagic1_init();
	    for (lr = defobsdata.blockData[i]; lr; lr = lr->r_next)
	    {
	    	fprintf(f, "\n      RECT ( %.10g %.10g ) ( %.10g %.10g )",
				(float)(lr->r_r.r_xbot * oscale),
				(float)(lr->r_r.r_ybot * oscale),
				(float)(lr->r_r.r_xtop * oscale),
				(float)(lr->r_r.r_ytop * oscale));
		freeMagic1(&mm1, lr);
	    }
	    freeMagic1_end(&mm1);
            fprintf(f, " ;\n");
    	}
    	fprintf(f, "END BLOCKAGES\n\n");
    }
    freeMagic(defobsdata.blockData);
    freeMagic(defobsdata.blockMasks);
    freeMagic(defobsdata.baseNames);

}

int
defblockageVisit(
    EFNode *node,
    int res,
    EFCapValue cap,
    DefObsData *defobsdata)
{
    CellDef *def = defobsdata->def;
    TileType magictype;
    TileTypeBitMask tmask;
    int defBlockageGeometryFunc(Tile *tile, int plane, DefObsData *defobsdata);	/* Forward declaration */

    /* For regular nets, only count those nodes having port	*/
    /* connections.  For special nets, only count those nodes	*/
    /* that were marked with the EF_SPECIAL flag while counting	*/
    /* nets.							*/

    if ((node->efnode_flags & EF_PORT) || (node->efnode_flags & EF_SPECIAL))
	return 0;

    magictype = DBTechNameType(EFLayerNames[node->efnode_type]);
    ASSERT(magictype >= 0, "magictype<0");
    TTMaskZero(&tmask);
    TTMaskSetMask(&tmask, &DBConnectTbl[magictype]);

    /* Avoid attempting to extract an implicit substrate into DEF */
    if (node->efnode_type == TT_SPACE) return 0;

    DBSrConnect(def, &node->efnode_loc, &tmask, DBConnectTbl,
		&TiPlaneRect, defBlockageGeometryFunc,
		(ClientData)defobsdata);

    return 0;
}

/* Callback function for generating a linked list of blockage geometry	*/
/* for a net.								*/

int
defBlockageGeometryFunc(
    Tile *tile,			/* Tile being visited */
    int plane,			/* Plane of the tile being visited */
    DefObsData *defobsdata)	/* Data passed to this function */
{
    TileType ttype = TiGetTypeExact(tile);
    TileType loctype;
    Rect r;
    LinkedRect *lr;
    int i;

    if (IsSplit(tile))
	loctype = (ttype & TT_SIDE) ? SplitRightType(tile) : SplitLeftType(tile); 
    else
	loctype = ttype;

    if (loctype == TT_SPACE) return 0;

    /* Dissolve stacked contacts */

    if (loctype >= DBNumUserLayers)
    {
	TileTypeBitMask *rMask;
	TileType rtype;

	rMask = DBResidueMask(loctype);
	for (rtype = TT_TECHDEPBASE; rtype < DBNumUserLayers; rtype++)
	    if (TTMaskHasType(rMask, rtype))
		if (DBPlane(rtype) == plane)
		{
		    loctype = rtype;
		    break;
		}
	if (rtype == DBNumUserLayers)
	    return 0;
    }

    for (i = 0; i < defobsdata->nlayers; i++)
	if (TTMaskHasType(&(defobsdata->blockMasks[i]), loctype))
	    break;

    if (i < defobsdata->nlayers)
    {
    	TiToRect(tile, &r);

	lr = (LinkedRect *)mallocMagic(sizeof(LinkedRect));
	lr->r_next = defobsdata->blockData[i];
	lr->r_type = loctype;
	lr->r_r = r;
	defobsdata->blockData[i] = lr;
    }

    return 0;
}

/* Callback function for generating a linked list of blockage geometry	*/
/* pulled from all non-electrical obstruction types.			*/

int
defSimpleBlockageFunc(
    Tile *tile,			/* Tile being visited */
    TileType dinfo,		/* Split tile information */
    DefObsData *defobsdata)	/* Data passed to this function */
{
    TileType ttype = TiGetTypeExact(tile) | dinfo;
    TileType loctype;
    Rect r;
    LinkedRect *lr;
    int i;

    if (IsSplit(tile))
	loctype = (ttype & TT_SIDE) ? SplitRightType(tile) : SplitLeftType(tile); 
    else
	loctype = ttype;

    if (loctype == TT_SPACE) return 0;

    for (i = 0; i < defobsdata->nlayers; i++)
	if (TTMaskHasType(&(defobsdata->blockMasks[i]), loctype))
	    break;

    if (i < defobsdata->nlayers)
    {
    	TiToRect(tile, &r);

	lr = (LinkedRect *)mallocMagic(sizeof(LinkedRect));
	lr->r_next = defobsdata->blockData[i];
	lr->r_type = loctype;
	lr->r_r = r;
	defobsdata->blockData[i] = lr;
    }

    return 0;
}

/*
 *------------------------------------------------------------
 *
 * defWriteComponents --
 *
 *	Output the COMPONENTS section of the DEF file.  This
 *	is a listing of all cell uses, their placement, and
 *	orientation.
 *
 * Results:
 *	None.
 *
 * Side Effects:
 *	Output to the DEF file.
 *
 *------------------------------------------------------------
 */

void
defWriteComponents(
    FILE *f,				/* File to write to */
    CellDef *rootDef,			/* Cell definition to use */
    float oscale)			/* Output scale factor */
{
    DefData defdata;
    int defComponentFunc(CellUse *cellUse, DefData *defdata);		/* Forward declaration */

    defdata.f = f;
    defdata.scale = oscale;

    DBCellEnum(rootDef, defComponentFunc, (ClientData)&defdata);
}

/* Callback function used by defWriteComponents for array members */

int
arrayDefFunc(
    CellUse *use,		/* CellUse for array element */
    Transform *transform,	/* Transform from use to parent */
    int x,			/* Indices of element */
    int y,
    DefData *defdata)
{
    int sx = use->cu_xhi - use->cu_xlo;
    int sy = use->cu_yhi - use->cu_ylo;
    char idx[32];
    Rect box, rect, *r;

    idx[0] = 0;

    if (sy) sprintf(idx, "%d%s", y, sx ? "," : "");
    if (sx) sprintf(idx + strlen(idx), "%d", x);

    r = &use->cu_def->cd_bbox;

    if (use->cu_def->cd_flags & CDFIXEDBBOX)
    {
	PropertyRecord *proprec;
	bool found;

	proprec = DBPropGet(use->cu_def, "FIXED_BBOX", &found);
	if (found)
	{
	    if ((proprec->prop_type == PROPERTY_TYPE_DIMENSION) &&
			(proprec->prop_len == 4))
	    {
		rect.r_xbot = proprec->prop_value.prop_integer[0];
		rect.r_ybot = proprec->prop_value.prop_integer[1];
		rect.r_xtop = proprec->prop_value.prop_integer[2];
		rect.r_ytop = proprec->prop_value.prop_integer[3];
		r = &rect;
	    }
	}
    }

    GeoTransRect(transform, r, &box);

    fprintf(defdata->f, "   - %s[%s] %s\n      + PLACED ( %.10g %.10g ) %s ;\n",
		use->cu_id, idx, use->cu_def->cd_name,
		(float)(box.r_xbot) * defdata->scale,
		(float)(box.r_ybot) * defdata->scale,
		defTransPos(&use->cu_transform));
    return 0;
}

/* Callback function used by defWriteComponents */

int
defComponentFunc(
    CellUse *cellUse,
    DefData *defdata)
{
    FILE *f = defdata->f;
    float oscale = defdata->scale;
    char *nameroot;
    Rect *r, rect, bbrect, defrect;
    int xoff, yoff;

    /* Ignore any cellUse that does not have an identifier string. */
    if (cellUse->cu_id == NULL) return 0;

    if (cellUse->cu_xlo != cellUse->cu_xhi || cellUse->cu_ylo != cellUse->cu_yhi) {
	/* expand the array */
	DBArraySr(cellUse, &cellUse->cu_bbox, arrayDefFunc, defdata);
	return 0;
    }

    /* In case the cd_name contains a path component (it's not supposed to),	*/
    /* remove it.								*/

    nameroot = strrchr(cellUse->cu_def->cd_name, '/');
    if (nameroot != NULL)
	nameroot++;
    else
	nameroot = cellUse->cu_def->cd_name;

    r = &cellUse->cu_def->cd_bbox;

    xoff = yoff = 0;
    if (cellUse->cu_def->cd_flags & CDFIXEDBBOX)
    {
	PropertyRecord *proprec;
	bool found;

	proprec = DBPropGet(cellUse->cu_def, "FIXED_BBOX", &found);
	if (found)
	{
	    if ((proprec->prop_type == PROPERTY_TYPE_DIMENSION) &&
			(proprec->prop_len == 4))
	    {
	 	rect.r_xbot = proprec->prop_value.prop_integer[0];
	 	rect.r_ybot = proprec->prop_value.prop_integer[1];
	 	rect.r_xtop = proprec->prop_value.prop_integer[2];
	 	rect.r_ytop = proprec->prop_value.prop_integer[3];

		r = &rect;
		GeoTransRect(&cellUse->cu_transform, &rect, &bbrect);
		GeoTransRect(&cellUse->cu_transform, &cellUse->cu_def->cd_bbox, &defrect);
		xoff = bbrect.r_xbot - defrect.r_xbot;
		yoff = bbrect.r_ybot - defrect.r_ybot;
	    }
	}
    }

    fprintf(f, "   - %s %s\n      + PLACED ( %.10g %.10g ) %s ;\n",
		cellUse->cu_id, nameroot,
		(float)(cellUse->cu_bbox.r_xbot - r->r_ll.p_x + xoff)
		* oscale,
		(float)(cellUse->cu_bbox.r_ybot - r->r_ll.p_y + yoff)
		* oscale,
		defTransPos(&cellUse->cu_transform));

    return 0;	/* Keep the search going */
}

/*
 *------------------------------------------------------------
 *
 * defMakeInverseLayerMap ---
 *
 *	Generate an array of pointers to lefLayer structures for each
 *	magic type so we can do a quick lookup when searching over tiles.
 *
 * Results:
 *	Pointer to the inverse layer map.
 *
 * Side effects:
 *	Memory is allocated for the map structure array.
 *
 *------------------------------------------------------------
 */

LefMapping *
defMakeInverseLayerMap(
    bool do_vias)
{
    LefMapping *lefMagicToLefLayer;
    lefLayer *lefl;
    TileType i;
    const char *lefname;

    lefMagicToLefLayer = (LefMapping *)mallocMagic(DBNumTypes
		* sizeof(LefMapping));
    memset(lefMagicToLefLayer, 0, sizeof(LefMapping) * TT_TECHDEPBASE);
    for (i = TT_TECHDEPBASE; i < DBNumTypes; i++)
    {
	lefname = defGetType(i, &lefl, do_vias);
	lefMagicToLefLayer[i].lefName = lefname;
	lefMagicToLefLayer[i].lefInfo = lefl;
    }
    return lefMagicToLefLayer;
}

/*
 *------------------------------------------------------------
 *
 * DefWriteAll --
 *
 * Results:
 *
 * Side Effects:
 *
 *------------------------------------------------------------
 */

/* To do:  routine DefWriteAll().					*/

/* DEF does not handle hierarchy.  However, we should assume that we	*/
/* want to write out a DEF file for each cell in the hierarchy.  But,	*/
/* we should stop at any cells defining ports, assuming that they are	*/
/* standard cells and not part of the routing.				*/

/* Maybe there should be a method for specifying that any hierarchy	*/
/* should be flattened when writing to the DEF file output.		*/


/*
 *------------------------------------------------------------
 *
 * DefWriteCell --
 *
 *	Write DEF-format output for the indicated cell.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	Writes a single .def file to disk.
 *
 *------------------------------------------------------------
 */

void
DefWriteCell(
    CellDef *def,		/* Cell being written */
    char *outName,		/* Name of output file, or NULL. */
    bool allSpecial,		/* Treat all nets as SPECIALNETS? */
    int units,			/* Force units to this value (default 1000) */
    bool analRetentive)		/* Force compatibility with stupid tools */
{
    char *filename, *filename1, *filename2;
    char line[2048];
    FILE *f, *f2;		/* Break output file into parts */
    NetCount nets;
    int total, numrules;
    float scale;
    HashTable defViaTable;

    LefMapping *lefMagicToLefLayer;
    HashEntry *he;
    HashSearch hs;

    /* Note that "1" corresponds to "1000" in the header UNITS line,	*/
    /* or units of nanometers.  10 = centimicrons, 1000 = microns.	*/

    scale = CIFGetOutputScale(1000) * units;

    if (!strcmp(def->cd_name, UNNAMED))
    {
	TxError("Please name the cell before generating DEF.\n");
	return;
    }

    f = lefFileOpen(def, outName, ".def", "w", &filename);

    TxPrintf("Generating DEF output %s for cell %s:\n", filename, def->cd_name);

    if (f == NULL)
    {
#ifdef MAGIC_WRAPPER
	TxError("Cannot open output file %s (%s).\n", filename,
		strerror(errno));
#else
	TxError("Cannot open output file: ");
	perror(filename);
#endif
	return;
    }
    filename1 = StrDup((char **)NULL, filename);

    char *outName_part;
    {   /* use the 'filename' given back from lefFileOpen() */
        outName_part = mallocMagic(strlen(filename)+strlen("_part")+1);
        ASSERT(outName_part, "outName_part");
        strcpy(outName_part, filename);
        char *cp = strrchr(outName_part, '.');
        if (cp)
           *cp = '\0'; /* remove extn */
        strcat(outName_part, "_part");
    }

    defWriteHeader(def, f, scale, units);

    HashInit(&defViaTable, 256, HT_STRINGKEYS);

    lefMagicToLefLayer = defMakeInverseLayerMap(LAYER_MAP_VIAS);

    /* Vias---magic contact areas are reported as vias. */
    total = defCountVias(def, lefMagicToLefLayer, &defViaTable, scale);
    fprintf(f, "VIAS %d ;\n", total);
    if (total > 0)
	defWriteVias(f, def, scale, lefMagicToLefLayer);
    fprintf(f, "END VIAS\n\n");

    /* Components (i.e., cell uses) */
    total = defCountComponents(def);
    fprintf(f, "COMPONENTS %d ;\n", total);
    if (total > 0)
	defWriteComponents(f, def, scale);
    fprintf(f, "END COMPONENTS\n\n");

    /* Pins---assume no pins (for now) */
    total = defCountPins(def);
    fprintf(f, "PINS %d ;\n", total);
    if (total > 0)
	defWritePins(f, def, lefMagicToLefLayer, scale);
    fprintf(f, "END PINS\n\n");

    /* Count the number of nets and "special" nets */
    nets = defCountNets(def, allSpecial);

    /* Not done yet with output, so keep this file open. . . */

    f2 = lefFileOpen(def, outName_part, ".def", "w", &filename);

    if (f2 == NULL)
    {
#ifdef MAGIC_WRAPPER
	TxError("Cannot open output file %s (%s).\n", filename,
		strerror(errno));
#else
	TxError("Cannot open output file: ");
	perror(filename);
#endif
	/* If part 2 cannot be opened, remove part 1 */
	fclose(f);
	unlink(filename1);
	freeMagic(filename1);
	freeMagic(outName_part);
	return;
    }
    filename2 = StrDup((char **)NULL, filename);

    /* "Special" nets---nets matching $GND, $VDD, or $globals(*) 	*/
    if (nets.special > 0)
    {
	fprintf(f2, "SPECIALNETS %d ;\n", nets.special);
	defWriteNets(f2, def, scale, lefMagicToLefLayer, &defViaTable,
		(allSpecial) ?  ALL_SPECIAL : DO_SPECIAL);
	fprintf(f2, "END SPECIALNETS\n\n");
    }

    /* "Regular" nets */
    if (nets.regular > 0)
    {
	fprintf(f2, "NETS %d ;\n", nets.regular);
	defWriteNets(f2, def, scale, lefMagicToLefLayer, &defViaTable, DO_REGULAR);
	fprintf(f2, "END NETS\n\n");
    }
    fclose(f2);

    /* Now that nets have been written, the nondefault rules can be generated */

    /* Nondefault rules */
    numrules = LefNonDefaultRules.ht_nEntries;
    if (numrules > 0)
    {
	LefRules *nrules;
	lefRule *rule;

	fprintf(f, "NONDEFAULTRULES %d ;\n", numrules);
	HashStartSearch(&hs);
	while ((he = HashNext(&LefNonDefaultRules, &hs)))
	{
	    nrules = (LefRules *)HashGetValue(he);
	    fprintf(f, "  - %s", nrules->name);

	    if (analRetentive)
	    {
		/* Some tools can crash or throw an error if all layers
		 * are not represented in the non-default rule, which
		 * is an anal retentive interpretation of the DEF spec.
		 */
		if (LefInfo.ht_table != (HashEntry **)NULL)
		{
		    HashSearch hs2;
		    HashEntry *he2;
		    lefLayer *lefl2;

		    HashStartSearch(&hs2);
		    while ((he2 = HashNext(&LefInfo, &hs2)))
		    {
			lefl2 = (lefLayer *)HashGetValue(he2);
			if (lefl2->lefClass == CLASS_ROUTE)
			{
			    /* Avoid duplicate entries per route layer */
			    if (lefl2->refCnt < 0) continue;
			    lefl2->refCnt = -lefl2->refCnt;

			    /* Ignore obstruction layers */
			    if (lefl2->type == -1) continue;

			    /* Only output rules here for routing layers that
			     * are not represented in the non-default ruleset.
			     */
	    		    for (rule = nrules->rule; rule; rule = rule->next)
				if (lefl2->type == rule->lefInfo->type)
				    break;
			    if (rule != NULL) continue;
			    fprintf(f, "\n     + LAYER %s WIDTH %.10g",
					lefl2->canonName, 
					(float)(lefl2->info.route.width) * scale);
			}
		    }

		    /* Put the reference counts back to the way they were */
		    HashStartSearch(&hs2);
		    while ((he2 = HashNext(&LefInfo, &hs2)))
		    {
			lefl2 = (lefLayer *)HashGetValue(he2);
			if (lefl2->refCnt < 0)
			    lefl2->refCnt = -lefl2->refCnt;
		    }
		}
	    }

	    for (rule = nrules->rule; rule; rule = rule->next)
	    {
		fprintf(f, "\n     + LAYER %s WIDTH %.10g",
			nrules->rule->lefInfo->canonName, 
			((float)nrules->rule->width * scale));
		if (nrules->rule->extend > 0)
	    	    fprintf(f, " WIREEXT %.10g", (float)nrules->rule->extend / 2.0);
	    }
	    fprintf(f, " ;\n");
	}
	fprintf(f, "END NONDEFAULTRULES\n\n");
    }

    /* Append contents of file with NETS and SPECIALNETS sections */

    f2 = lefFileOpen(def, outName_part, ".def", "r", &filename);
    if (f2 == NULL)
    {
	/* This should not happen because the file was just written. . . */
#ifdef MAGIC_WRAPPER
	TxError("Cannot open input file %s (%s).\n", filename,
		strerror(errno));
#else
	TxError("Cannot open input file: ");
	perror(filename);
#endif
	/* If part 2 cannot be opened, remove part 1 */
	fclose(f);
	unlink(filename1);
        freeMagic(filename1);
        freeMagic(filename2);
	freeMagic(outName_part);
	return;
    }
    while (fgets(line, sizeof line, f2) != NULL) fprintf(f, "%s", line);
    fclose(f2);
    freeMagic(outName_part);

    /* Blockages */
    defWriteBlockages(f, def, scale, lefMagicToLefLayer);

    fprintf(f, "END DESIGN\n\n");
    fclose(f);

    /* Remove the temporary file of nets */
    unlink(filename2);

    freeMagic(filename1);
    freeMagic(filename2);

    if (nets.has_nets) {
	EFFlatDone(NULL);
	EFDone(NULL);
    }

    /* To do:  Clean up nondefault rules tables */

    freeMagic((char *)lefMagicToLefLayer);
    HashKill(&defViaTable);
    lefRemoveGeneratedVias();
}

