/*
 * lefWrite.c --      
 *
 * This module incorporates the LEF/DEF format for standard-cell place and
 * route.
 *
 * Version 0.1 (May 1, 2003):  LEF output for cells, to include pointer to
 * GDS, automatic generation of GDS if not already made, bounding box export,
 * port export, export of irouter "fence", "magnet", and "rotate" layers    
 * for defining router hints, and generating areas for obstructions and
 * pin layers.
 *
 */

#ifndef lint
static char rcsid[] __attribute__ ((unused)) = "$Header: /usr/cvsroot/magic-7.5/lef/lefWrite.c,v 1.3 2008/02/10 19:06:32 tim Exp $";            
#endif  /* not lint */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.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/utils.h"
#include "utils/malloc.h"
#include "utils/stack.h"
#include "utils/signals.h"
#include "windows/windows.h"
#include "dbwind/dbwind.h"
#include "graphics/graphics.h"
#include "utils/main.h"
#include "utils/undo.h"
#include "cif/cif.h"
#include "lef/lefInt.h"

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

/* Stack of cell definitions */
Stack *lefDefStack;

/*
 * ---------------------------------------------------------------------
 *
 * lefFileOpen --
 *
 * Open the .lef file corresponding to a .mag file.
 * If def->cd_file is non-NULL, the .lef file is just def->cd_file with
 * the trailing .mag replaced by .lef.  Otherwise, the .lef file is just
 * def->cd_name followed by .lef.
 *
 * Results:
 *	Return a pointer to an open FILE, or NULL if the .lef
 *	file could not be opened in the specified mode.
 *
 * Side effects:
 *	Opens a file.
 *
 * ----------------------------------------------------------------------------
 */

FILE *
lefFileOpen(def, file, suffix, mode, prealfile)
    CellDef *def;	/* Cell whose .lef file is to be written.  Should
			 * be NULL if file is being opened for reading.
			 */
    char *file;		/* If non-NULL, open 'name'.lef; otherwise,
			 * derive filename from 'def' as described
			 * above.
			 */
    char *suffix;	/* Either ".lef" for LEF files or ".def" for DEF files */
    char *mode;		/* Either "r" or "w", the mode in which the LEF/DEF
			 * file is to be opened.
			 */
    char **prealfile;	/* If this is non-NULL, it gets set to point to
			 * a string holding the name of the LEF/DEF file.
			 */
{
    char namebuf[512], *name, *endp;
    int len;
    FILE *rfile;

    if (file) name = file;
    else if (def && def->cd_file)
    {
	name = def->cd_file;
	if (endp = rindex(def->cd_file, '.'))
	{
	    name = namebuf;
	    len = endp - def->cd_file;
	    if (len > sizeof namebuf - 1) len = sizeof namebuf - 1;
	    (void) strncpy(namebuf, def->cd_file, len);
	    namebuf[len] = '\0';
	}
    }
    else if (def) name = def->cd_name;
    else
    {
	TxError("LEF file open:  No file name or cell given\n");
	return NULL;
    }

    /* Try once as-is, and if this fails, try stripping any leading	*/
    /* path information in case cell is in a read-only directory (mode	*/
    /* "read" only, and if def is non-NULL).				*/

    if ((rfile = PaOpen(name, mode, suffix, Path, CellLibPath, prealfile)) != NULL)
	return rfile;

    if (def) 
    {
	if (name == def->cd_name) return NULL;
	name = def->cd_name;
	return (PaOpen(name, mode, suffix, Path, CellLibPath, prealfile));
    }
    else
	return NULL;
}


/*
 * ----------------------------------------------------------------------------
 *
 * lefWriteHeader --
 *
 * This routine generates LEF header output for a cell or cell hierarchy.
 * Although the LEF/DEF spec does not define a "header" per se, this is
 * considered to be all LEF output not including the MACRO calls.  The
 * header, therefore, defines layers, process routing rules, units
 * (lambda), and so forth.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	Writes output to the open file "f".
 *
 * ----------------------------------------------------------------------------
 */

void
lefWriteHeader(def, f)
    CellDef *def;	/* Def for which to generate LEF output */
    FILE *f;		/* Output to this file */
{
    TileType type;

    UndoDisable();

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

    /* NOTE:  This routine corresponds to Envisia LEF/DEF Language	*/
    /* Reference version 5.3 (May 31, 2000)				*/

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

    /* As I understand it, this refers to the scalefactor of the GDS	*/
    /* file output.  Magic does all GDS in nanometers, so the LEF	*/
    /* scalefactor (conversion to microns) is always 1000.		*/

    fprintf(f, "UNITS\n");
    fprintf(f, "   DATABASE MICRONS 1000 ;\n");
    fprintf(f, "END UNITS\n");
    fprintf(f, "\n");

    /* Layers (minimal information) */

    if (LefInfo.ht_table != (HashEntry **)NULL)
    {
	HashSearch hs;
	HashEntry *he;
	lefLayer *lefl;

	float oscale = CIFGetOutputScale(1000);	/* lambda->micron conversion */

	HashStartSearch(&hs);
	while (he = HashNext(&LefInfo, &hs))
	{
	    lefl = (lefLayer *)HashGetValue(he);
	    if (!lefl) continue;

	    if (lefl->refCnt > 0)
	    {
		/* Avoid writing more than one entry per defined layer */
	        if (lefl->refCnt > 1) lefl->refCnt = -lefl->refCnt;

		/* Ignore obstruction-only layers */
		if (lefl->type == -1) continue;	
		/* Ignore VIA types, report only CUT types here */
		else if ((lefl->lefClass == CLASS_VIA)
			&& lefl->info.via.cell != NULL) continue;

		fprintf(f, "LAYER %s\n", lefl->canonName);
		if (lefl->lefClass == CLASS_VIA)
		{
		    int cutarea;
		    cutarea = (lefl->info.via.area.r_xtop - lefl->info.via.area.r_xbot);
		    cutarea *= (lefl->info.via.area.r_ytop - lefl->info.via.area.r_ybot);
		    fprintf(f, "   TYPE CUT ;\n");
		    if (cutarea > 0)
			fprintf(f, "   CUT AREA %f ;\n",
					(float)cutarea * oscale * oscale);
		}
		else if (lefl->lefClass == CLASS_ROUTE)
		{
		    fprintf(f, "   TYPE ROUTING ;\n");
		    if (lefl->info.route.pitch > 0)
			fprintf(f, "   PITCH %f ;\n", (float)(lefl->info.route.pitch)
				* oscale);
		    if (lefl->info.route.width > 0)
			fprintf(f, "   WIDTH %f ;\n", (float)(lefl->info.route.width)
				* oscale);
		    if (lefl->info.route.spacing > 0)
			fprintf(f, "   SPACING %f ;\n", (float)(lefl->info.route.spacing)
				* oscale);
		    /* No sense in providing direction info unless we know the width */
		    if (lefl->info.route.width > 0)
			fprintf(f, "   DIRECTION %s ;\n", (lefl->info.route.hdirection)
				?  "HORIZONTAL" : "VERTICAL");
		}
		else if (lefl->lefClass == CLASS_MASTER)
		{
		    fprintf(f, "   TYPE MASTERSLICE ;\n");
		}
		else if (lefl->lefClass == CLASS_OVERLAP)
		{
		    fprintf(f, "   TYPE OVERLAP ;\n");
		}
		fprintf(f, "END %s ;\n\n", lefl->canonName);
	    }
	}

	/* Return reference counts to normal */
	HashStartSearch(&hs);
	while (he = HashNext(&LefInfo, &hs))
	{
	    lefl = (lefLayer *)HashGetValue(he);
	    if (lefl && lefl->refCnt < 0)
		lefl->refCnt = -lefl->refCnt;
	}
    }

    /* Vias (to be completed, presumably) */
    /* Rules (to be completed, presumably) */

    UndoEnable();
}

#define LEF_MODE_PORT		0
#define LEF_MODE_OBSTRUCT	1

typedef struct
{
    FILE	*file;		/* file to write to */
    TileType	*lastType;	/* last type output, so we minimize LAYER
				 * statements.
				 */
    TileTypeBitMask rmask;	/* mask of routing layer types */
    Point	origin;		/* origin of cell */
    float	oscale;		/* units scale conversion factor */
    int		lefMode;	/* can be LEF_MODE_PORT when searching
				 * connections into ports, or
				 * LEF_MODE_OBSTRUCT when generating
				 * obstruction geometry.  LEF polyons
				 * must be manhattan, so if we find a
				 * split tile, LEF_MODE_PORT ignores it,
				 * and LEF_MODE_OBSTRUCT outputs the
				 * whole tile.
				 */
} lefClient;

/*
 * ----------------------------------------------------------------------------
 *
 * lefWriteGeometry --
 *
 * Function called from SimSrConnect() that outputs a RECT
 * record for each tile called.  Note that LEF does not define
 * nonmanhattan geometry (see above, comments in lefClient typedef).
 *
 * Return 0 to keep the search going.
 * ----------------------------------------------------------------------------
 */

int
lefWriteGeometry(tile, cdata)
    Tile *tile;
    ClientData cdata;
{
    lefClient *lefdata = (lefClient *)cdata;
    FILE *f = lefdata->file;
    float scale = lefdata->oscale;
    TileType ttype;

    /* To enumerate obstructions, we search all tiles in the	*/
    /* cell, and ignore all of those which were electrically	*/
    /* connected to any pin.  These will have the tile's	*/
    /* ti_client record set to 1.				*/

    /* Because DBSrPaintArea will look at each tile once and	*/
    /* only once, we can reset the ti_client record here, so	*/
    /* everything is back to normal after LEF output.		*/

    if (lefdata->lefMode == LEF_MODE_OBSTRUCT)
	if (tile->ti_client == (ClientData)1)
	{
	    tile->ti_client = (ClientData)CLIENTDEFAULT;
	    return 0;
	}

    ttype = TiGetTypeExact(tile);

#ifdef NONMANHATTAN
    if (IsSplit(tile))
	ttype = (ttype & TT_SIDE) ? SplitRightType(tile) :
			SplitLeftType(tile);
#endif

    /* Output geometry only for defined routing layers */
    if (!TTMaskHasType(&lefdata->rmask, ttype)) return 0;

    if (ttype != *(lefdata->lastType))
    {
	/* This is a temporary hack---avoid printing the image	*/
	/* plane designation for contact types.  Preferably,	*/
	/* the type names for LEF routing layers should be	*/
	/* spelled out in the tech file, or at least in the	*/
	/* LEF file header.					*/

	char layername[100], *slashptr;

	sprintf(layername, "%.99s", DBTypeLongName(ttype));
	if ((slashptr = strchr(layername, '/')) != NULL) *slashptr = '\0';

	fprintf(f, "         LAYER %s ;\n", layername);

	*(lefdata->lastType) = ttype;
    }

#ifdef NONMANHATTAN
    if (!IsSplit(tile))
#endif

    fprintf(f, "	    RECT %.4f %.4f %.4f %.4f ;\n",
			scale * (float)(LEFT(tile) - lefdata->origin.p_x),
			scale * (float)(BOTTOM(tile) - lefdata->origin.p_y),
			scale * (float)(RIGHT(tile) - lefdata->origin.p_x),
			scale * (float)(TOP(tile) - lefdata->origin.p_y));

    return 0;
}

/*
 * When called from SimSrConnect(), the callback function needs another
 * argument for the plane.
 */

int
lefWriteGeometry2(tile, plane, cdata)
    Tile *tile;
    Plane *plane;
    ClientData cdata;
{
    return lefWriteGeometry(tile, cdata);
}

/*
 * ----------------------------------------------------------------------------
 *
 * MakeLegalLEFSyntax --
 *
 *	Follow syntactical rules of the LEF spec.  Most notably, Magic
 *	node names often contain the hash mark '#', which is illegal
 *	in LEF output.  Other illegal LEF characters are space, newline,
 *	semicolon, and for literal names: dash, asterisk, and percent.
 *	All of the above will be replaced with underscores if found.
 *
 * Results:
 *	Returns an allocated string containing the modified result, or
 *	else returns the original string pointer.  It is the responsibility
 *	of the calling function to free the result if it is not equal to
 *	the argument.
 *
 * Side effects:
 *	Allocated memory.
 *
 * ----------------------------------------------------------------------------
 */

char *
MakeLegalLEFSyntax(text)
    char *text;
{
    static char *badLEFchars = ";# -*$\n";
    char *cptr, *bptr;
    char *rstr;

    for (cptr = text; *cptr != '\0'; cptr++)
	for (bptr = badLEFchars; *bptr != '\0'; bptr++)
	    if (*cptr == *bptr) break;

    if (*cptr == '\0' && *bptr == '\0')
	return text;

    rstr = StrDup((char **)NULL, text); 
    
    for (cptr = rstr; *cptr != '\0'; cptr++)
	for (bptr = badLEFchars; bptr != '\0'; bptr++)
	    if (*cptr == *bptr) 
	    {
		*cptr = '_';
		break;
	    }

    return rstr;
}
	    
/*
 * ----------------------------------------------------------------------------
 *
 * lefWriteMacro --
 *
 * This routine generates LEF output for a cell in the form of a LEF
 * "MACRO" block.  Includes information on cell dimensions, pins,
 * ports (physical layout associated with pins), and routing obstructions.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	Writes output to the open file "f".
 *
 * ----------------------------------------------------------------------------
 */

void
lefWriteMacro(def, f, scale)
    CellDef *def;	/* Def for which to generate LEF output */
    FILE *f;		/* Output to this file */
    float scale;	/* Output distance units conversion factor */
{
    bool propfound;
    char *propvalue, *class = NULL;
    Label *lab, *clab;
    TileTypeBitMask lmask;
    TileType ttype;
    lefClient lc;
    int idx, pNum;
    char *LEFtext;
    HashSearch hs;
    HashEntry *he;

    UndoDisable();

    TxPrintf("Diagnostic:  Writing LEF output for cell %s\n", def->cd_name);

    /* Set up client record. */

    lc.file = f;
    lc.lastType = &ttype;
    lc.oscale = scale;

    TxPrintf("Diagnostic:  Scale value is %f\n", lc.oscale);

    /* Which layers are routing layers are defined in the tech file.	*/

    TTMaskZero(&lc.rmask);
    HashStartSearch(&hs);
    while (he = HashNext(&LefInfo, &hs))
    {
	TileTypeBitMask *lmask;
	lefLayer *lefl = (lefLayer *)HashGetValue(he);
	if (lefl && (lefl->lefClass == CLASS_ROUTE || lefl->lefClass == CLASS_VIA))
	    if (lefl->type != -1)
	    {
		TTMaskSetType(&lc.rmask, lefl->type);
		if (DBIsContact(lefl->type))
		{
		    lmask = DBResidueMask(lefl->type);
		    TTMaskSetMask(&lc.rmask, lmask);
		}
	    }
    }

    /* Any layer which has a port label attached to it should by	*/
    /* necessity be considered a routing layer.	 Usually this will not	*/
    /* add anything to the mask already created.			*/

    for (lab = def->cd_labels; lab != NULL; lab = lab->lab_next)
	if (lab->lab_flags & PORT_DIR_MASK)
	    TTMaskSetType(&lc.rmask, lab->lab_type);

    /* NOTE:  This routine corresponds to Envisia LEF/DEF Language	*/
    /* Reference version 5.3 (May 31, 2000)				*/

    /* Macro header information (to be completed) */

    fprintf(f, "MACRO %s\n", def->cd_name); 

    /* LEF data is stored in the "cd_props" hash table.  If the hash	*/
    /* table is NULL or a specific property undefined, then the LEF	*/
    /* value takes the default.  Generally, LEF properties which have	*/
    /* default values are optional, so in this case we will leave those	*/
    /* entries blank.							*/

    propvalue = (char *)DBPropGet(def, "LEFclass", &propfound);
    if (propfound)
    {
	fprintf(f, "   CLASS %s\n", propvalue);
	class = propvalue;
    }

    propvalue = (char *)DBPropGet(def, "LEFsource", &propfound);
    if (propfound)
	fprintf(f, "   SOURCE %s\n", propvalue);

    fprintf(f, "   FOREIGN %s ;\n", def->cd_name);

    /* Apparently ORIGIN is not reliable!  Assume origin must be (0,0)	*/
    /* and adjust all geometry instead of adjusting the origin.		*/

/*
    fprintf(f, "   ORIGIN %.4f %.4f ;\n",
		lc.oscale * (float)def->cd_bbox.r_xbot,
		lc.oscale * (float)def->cd_bbox.r_ybot);
*/
    lc.origin = def->cd_bbox.r_ll;

    fprintf(f, "   SIZE %.4f BY %.4f ;\n",
		lc.oscale * (float)(def->cd_bbox.r_xtop - def->cd_bbox.r_xbot),
		lc.oscale * (float)(def->cd_bbox.r_ytop - def->cd_bbox.r_ybot));

    propvalue = (char *)DBPropGet(def, "LEFsymmetry", &propfound);
    if (propfound)
	fprintf(f, "   SYMMETRY %s\n", propvalue);

    /* List of pins (ports) (to be refined?) */

    lc.lefMode = LEF_MODE_PORT;

    for (lab = def->cd_labels; lab != NULL; lab = lab->lab_next)
    {
	if (lab->lab_flags & PORT_DIR_MASK)
	{
	    /* Ignore ports which we have already visited. */
	    if (lab->lab_flags & PORT_VISITED) continue;

	    idx = lab->lab_flags & PORT_NUM_MASK;
	    fprintf(f, "   PIN %s\n", 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:
			fprintf(f, "OUTPUT TRISTATE");
			break;
		    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");
	    }

	    /* Query pin geometry for SHAPE (to be done?) */

	    /* Generate port layout geometry using SimSrConnect()	*/
	    /* Selects all electrically-connected material into the	*/
	    /* select def.  Output all the layers and geometries of	*/
	    /* the select def.						*/
	    /*								*/
	    /* We use SimSrConnect() and not DBSrConnect() because	*/
	    /* SimSrConnect() leaves "marks" (tile->ti_client = 1)	*/
	    /* which allows us to later search through all tiles for	*/
	    /* anything that is not connected to a port, and generate	*/
	    /* an "obstruction" record for it.				*/
	    /*								*/
	    /* Note: Use DBIsContact() to check if the layer is a VIA.	*/
	    /* Presently, I am treating contacts like any other layer.	*/

	    for (clab = lab; clab != NULL; clab = clab->lab_next)
	    {
		if ((clab->lab_flags & PORT_NUM_MASK) == idx)
		{
		    Rect labr = clab->lab_rect;

		    /* Deal with degenerate (line or point) labels	*/
		    /* by growing by 1 in each direction.		*/

		    if (labr.r_xtop - labr.r_xbot == 0)
		    {
			labr.r_xtop++;
			labr.r_xbot--;
		    }
		    if (labr.r_ytop - labr.r_ybot == 0)
		    {
			labr.r_ytop++;
			labr.r_ybot--;
		    }

		    fprintf(f, "      PORT\n");

		    TTMaskSetOnlyType(&lmask, clab->lab_type);

		    ttype = TT_SPACE;
		    SimSrConnect(def, &labr, &lmask, DBConnectTbl,
			&TiPlaneRect, lefWriteGeometry2, (ClientData) &lc);

		    fprintf(f, "      END\n");	/* end of port geometries */
		    clab->lab_flags |= PORT_VISITED;
		}
	    }
	    LEFtext = MakeLegalLEFSyntax(lab->lab_text);
	    fprintf(f, "   END %s\n", lab->lab_text);	/* end of pin */
	    if (LEFtext != lab->lab_text) freeMagic(LEFtext);
	}
    }

    /* Clear all PORT_VISITED bits in labels */
    for (lab = def->cd_labels; lab != NULL; lab = lab->lab_next)
	if (lab->lab_flags & PORT_DIR_MASK)
	    lab->lab_flags &= ~(PORT_VISITED);

    /* List of routing obstructions */

    lc.lefMode = LEF_MODE_OBSTRUCT;
    ttype = TT_SPACE;

    /* Restrict to routing planes only */

    fprintf(f, "   OBS\n");
    for (pNum = PL_PAINTBASE; pNum < DBNumPlanes; pNum++)
	(void) DBSrPaintArea((Tile *)NULL, def->cd_planes[pNum], 
		&TiPlaneRect, &lc.rmask,
		lefWriteGeometry, (ClientData) &lc);
    fprintf(f, "   END\n");	/* end of obstruction geometries */

    fprintf(f, "END %s\n", def->cd_name);	/* end of macro */

    UndoEnable();
}

/*
 *------------------------------------------------------------
 *
 * LefWriteAll --
 *
 *	Write LEF-format output for each cell, beginning with
 *	the top-level cell use "rootUse".
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	Writes a .lef file to disk.
 *
 *------------------------------------------------------------
 */

void
LefWriteAll(rootUse, writeTopCell)
    CellUse *rootUse;
    bool writeTopCell;
{
    CellDef *def, *rootdef;
    FILE *f;
    char *filename;
    float scale = CIFGetOutputScale(1000);	/* conversion to microns */

    rootdef = rootUse->cu_def;

    /* Make sure the entire subtree is read in */
    DBCellReadArea(rootUse, &rootdef->cd_bbox);

    /* Fix up bounding boxes if they've changed */
    DBFixMismatch();

    /* Mark all defs as being unvisited */
    (void) DBCellSrDefs(0, lefDefInitFunc, (ClientData) 0);

    /* Recursively visit all defs in the tree and push on stack */
    lefDefStack = StackNew(100);
    (void) lefDefPushFunc(rootUse);

    /* Open the file for output */

    f = lefFileOpen(rootdef, (char *)NULL, ".lef", "w", &filename);

    TxPrintf("Generating LEF output %s for hierarchy rooted at cell %s:\n",
		filename, rootdef->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;
    }

    /* Now generate LEF output for all the cells we just found */

    lefWriteHeader(rootdef, f);

    while (def = (CellDef *) StackPop(lefDefStack))
    {
	def->cd_client = (ClientData) 0;
	if (!SigInterruptPending)
	    if ((writeTopCell == TRUE) || (def != rootdef))
		lefWriteMacro(def, f, scale);
    }

    fclose(f);
    StackFree(lefDefStack);
}
 
/*
 * Function to initialize the client data field of all
 * cell defs, in preparation for generating LEF output
 * for a subtree rooted at a particular def.
 */

int 
lefDefInitFunc(def)
    CellDef *def;
{
    def->cd_client = (ClientData) 0;
    return (0);
}

/*
 * Function to push each cell def on lefDefStack
 * if it hasn't already been pushed, and then recurse
 * on all that def's children.
 */

int
lefDefPushFunc(use)
    CellUse *use;
{
    CellDef *def = use->cu_def;

    if (def->cd_client || (def->cd_flags & CDINTERNAL))
	return (0);

    def->cd_client = (ClientData) 1;
    StackPush((ClientData) def, lefDefStack);
    (void) DBCellEnum(def, lefDefPushFunc, (ClientData) 0);
    return (0);
}

/*
 *------------------------------------------------------------
 *
 * LefWriteCell --
 *
 *	Write LEF-format output for the indicated cell.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	Writes a single .lef file to disk.
 *
 *------------------------------------------------------------
 */

void
LefWriteCell(def, outName, isRoot)
    CellDef *def;		/* Cell being written */
    char *outName;		/* Name of output file, or NULL. */
    bool isRoot;		/* Is this the root cell? */
{
    char *filename;
    FILE *f;
    float scale = CIFGetOutputScale(1000);

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

    TxPrintf("Generating LEF 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;
    }

    if (isRoot)
	lefWriteHeader(def, f);
    lefWriteMacro(def, f, scale);
    fclose(f);
}

