/*
 * ext2sim.c --
 *
 * Program to flatten hierarchical .ext files and produce
 * a .sim file, suitable for use as input to simulators
 * such as esim and crystal.
 *
 * Flattens the tree rooted at file.ext, reading in additional .ext
 * files as specified by "use" lines in file.ext.  The output is left
 * in file.sim, unless '-o esSimFile' is specified, in which case the
 * output is left in 'esSimFile'.
 *
 */

#ifndef lint
static char rcsid[] = "$Header: /ufs/repository/magic/ext2sim/ext2sim.c,v 1.6 2006/02/08 03:46:19 fang Exp $";
#endif  /* not lint */

#include <stdio.h>
#include <ctype.h>
#include <stdlib.h>
#include <stdarg.h>
#include <string.h>
#include <strings.h>
#include "misc/magic.h"
#include "utils/geometry.h"
#include "utils/hash.h"
#include "utils/utils.h"
#include "extflat/extflat.h"
#include "utils/runstats.h"
#include "utils/malloc.h"
#include "math.h"	/* for sqrt() in bipolar L,W calculation */

/* Forward declarations */
/* int mainArgs(); */
static void mainArgs(int*, char***);
/* int fetVisit(), resistVisit(), capVisit(), nodeVisit(); */
static int capVisit(HierName*, HierName*, double);
static int fetVisit(Fet*, HierName*, Transform*, int, int);
static int nodeVisit(register EFNode*, int, double);
static int resistVisit(HierName*, HierName*, int);

static int nAP(EFNode*, int, int, FILE*);
static int nAPHier(FetTerm*, HierName*, int, int, FILE*);
static void fetSubstrate(HierName*, HierName*, int, int, bool, FILE*);
static void fetOutNode(HierName*, HierName*, char*, FILE*);

/* Options specific to ext2sim */
bool esFetNodesOnly = FALSE;
bool esNoAlias = FALSE;
bool esNoAttrs = FALSE;
bool esNoLabel = FALSE;
bool esHierAP = FALSE;
char esDefaultOut[FNSIZE], esDefaultAlias[FNSIZE], esDefaultLabel[FNSIZE];
char *esOutName = esDefaultOut, *esAliasName = esDefaultAlias;
char *esLabelName = esDefaultLabel;
int  esCapAccuracy = 1;
char esCapFormat[FNSIZE];
FILE *esSimF = NULL;
FILE *esAliasF = NULL;
FILE *esLabF = NULL;


#define	MIT 0
#define	LBL 1
#define	SU  2

static unsigned short esFormat = SU ;

struct {
   short resClassSD ;  /* the resistance class of the src/drn of the fet */
   short resClassSub ; /* the resistance class of the substrate of the fet */
   char  *defSubs ;    /* the default substrate node */
} fetInfo[MAXFETTYPES];

typedef struct {
	long    visitMask:MAXFETTYPES; 
} nodeClient;

typedef struct {
	HierName *lastPrefix;
	long    visitMask:MAXFETTYPES; 
} nodeClientHier;

#define NO_RESCLASS	-1

#define markVisited(client, rclass) \
  { (client)->visitMask |= (1<<rclass); }

#define clearVisited(client) \
   { (client)->visitMask = (long)0; }

#define beenVisited(client, rclass)  \
   ( (client)->visitMask & (1<<rclass))

#define initNodeClient(node) \
{ \
	MALLOC(ClientData,(node)->efnode_client,sizeof(nodeClient)); \
	(( nodeClient *)(node)->efnode_client)->visitMask = (long) 0; \
}


#define initNodeClientHier(node) \
{ \
	MALLOC(ClientData,(node)->efnode_client,sizeof(nodeClientHier)); \
	((nodeClientHier *) (node)->efnode_client)->visitMask = (long) 0; \
}



/* attributes controlling the Area/Perimeter extraction of fet terminals */
#define ATTR_FLATAP	"*[Ee][Xx][Tt]:[Aa][Pp][Ff]*"
#define ATTR_HIERAP	"*[Ee][Xx][Tt]:[Aa][Pp][Hh]*"
#define ATTR_SUBSAP	"*[Ee][Xx][Tt]:[Aa][Pp][Ss]*"

/*
 * ----------------------------------------------------------------------------
 *
 * main --
 *
 * Top level of ext2sim.
 *
 * ----------------------------------------------------------------------------
 */
int
main(argc, argv)
    int argc;
    char *argv[];
{
    int i,flatFlags;
    char *inName;
    FILE *f;

    EFInit();
    /* create default fetinfo entries (MOSIS) which can be overriden by
       the command line arguments */
    for ( i = 0 ; i < MAXFETTYPES ; i++ ) {
	fetInfo[i].resClassSD = NO_RESCLASS;
	fetInfo[i].resClassSub = NO_RESCLASS;
	fetInfo[i].defSubs = NULL;
    }
    i = efBuildAddStr(EFFetTypes, &EFFetNumTypes, MAXFETTYPES, "nfet");
    fetInfo[i].resClassSD = 0 ;
    fetInfo[i].resClassSub = NO_RESCLASS ;
    fetInfo[i].defSubs = "Gnd!";
    i = efBuildAddStr(EFFetTypes, &EFFetNumTypes, MAXFETTYPES, "pfet");
    fetInfo[i].resClassSD = 1 ;
    fetInfo[i].resClassSub = 6 ;
    fetInfo[i].defSubs = "Vdd!";
    i = efBuildAddStr(EFFetTypes, &EFFetNumTypes, MAXFETTYPES, "nmos");
    fetInfo[i].resClassSD = 0 ;
    fetInfo[i].resClassSub = NO_RESCLASS ;
    fetInfo[i].defSubs = "Gnd!";
    i = efBuildAddStr(EFFetTypes, &EFFetNumTypes, MAXFETTYPES, "pmos");
    fetInfo[i].resClassSD = 1 ;
    fetInfo[i].resClassSub = 6 ;
    fetInfo[i].defSubs = "Vdd!";
    /* Process command line arguments */
    inName = EFArgs(argc, argv, mainArgs, (ClientData) NULL);
    if (inName == NULL)
	exit (1);

    /*
     * Initializations specific to this program.
     * Make output, alias, and label names be of the form
     * inName.suffix if they weren't explicitly specified,
     * where suffix is .sim, .al, or .nodes.
     */

    if (esOutName == esDefaultOut)
	(void) sprintf(esDefaultOut, "%s.sim", inName);
    if (esAliasName == esDefaultAlias)
	(void) sprintf(esDefaultAlias, "%s.al", inName);
    if (esLabelName == esDefaultLabel)
	(void) sprintf(esDefaultLabel, "%s.nodes", inName);
    if ((esSimF = fopen(esOutName, "w")) == NULL)
    {
	perror(esOutName);
	exit (1);
    }
    if (!esNoAlias && (esAliasF = fopen(esAliasName, "w")) == NULL)
    {
	perror(esAliasName);
	exit (1);
    }
    if (!esNoLabel && (esLabF = fopen(esLabelName, "w")) == NULL)
    {
	perror(esLabelName);
	exit (1);
    }

    /* Read the hierarchical description of the input circuit */
    EFReadFile(inName);

    fprintf(esSimF, "| units: %d tech: %s format: %s\n", EFScale, EFTech,
		   (esFormat == MIT) ? "MIT" :  
		      ( (esFormat == LBL) ? "LBL" : "SU" ) );

    /* Convert the hierarchical description to a flat one */
    flatFlags = EF_FLATNODES;
    if (EFCapThreshold < INFINITE_THRESHOLD) flatFlags |= EF_FLATCAPS;
    if (EFResistThreshold < INFINITE_THRESHOLD) flatFlags |= EF_FLATRESISTS;
    EFFlatBuild(inName, flatFlags);
	/* Fang: VERY suspicous incompatible function pointers passed */
    EFVisitFets(fetVisit, (ClientData) NULL);
    if (flatFlags & EF_FLATCAPS) {
    	(void) sprintf( esCapFormat, " %%.%dlf\n", esCapAccuracy);
	EFVisitCaps(capVisit, (ClientData) NULL);
    }
    EFVisitResists(resistVisit, (ClientData) NULL);
    (void) sprintf( esCapFormat, " GND %%.%dlf\n", esCapAccuracy);
    EFVisitNodes(nodeVisit, (ClientData) NULL);

#ifdef	free_all_mem
    EFFlatDone();
    EFDone();
#endif	/* free_all_mem */

    printf("Memory used: %s\n", RunStats(RS_MEM, NULL, NULL));
    /* exit (0); */
    return 0;
}

/*
 * ----------------------------------------------------------------------------
 *
 * mainArgs --
 *
 * Process those arguments that are specific to ext2sim.
 * Assumes that *pargv[0][0] is '-', indicating a flag
 * argument.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	After processing an argument, updates *pargc and *pargv
 *	to point to after the argument.
 *
 *	May initialize various global variables based on the
 *	arguments given to us.
 *
 *	Exits in the event of an improper argument.
 *
 * ----------------------------------------------------------------------------
 */
/* Void */
void
mainArgs(pargc, pargv)
    int *pargc;
    char ***pargv;
{
    char **argv = *pargv, *cp;
    int argc = *pargc;

    switch (argv[0][1])
    {
	case 'A':
	    esNoAlias = TRUE;
	    break;
	case 'B':
	    esNoAttrs = TRUE;
	    break;
	case 'F':
	    esFetNodesOnly = TRUE;
	    break;
	case 'L':
	    esNoLabel = TRUE;
	    break;
	case 'a':
	    if ((esAliasName = ArgStr(&argc, &argv, "filename")) == NULL)
		goto usage;
	    break;
	case 'l':
	    if ((esLabelName = ArgStr(&argc, &argv, "filename")) == NULL)
		goto usage;
	    break;
	case 'o':
	    if ((esOutName = ArgStr(&argc, &argv, "filename")) == NULL)
		goto usage;
	    break;
	case 'f': {
	     char *ftmp ;

	     if ((ftmp = ArgStr(&argc, &argv, "format")) == NULL)
		goto usage;
	     if ( strcasecmp(ftmp,"MIT") == 0 )
	        esFormat = MIT ;
	     else if ( strcasecmp(ftmp,"LBL") == 0 )
		esFormat = LBL ;
	     else if ( strcasecmp(ftmp,"SU") == 0 )
		esFormat = SU ;
	     else goto usage;
	     break;
	     }
	case 'y': {
	      char *t;

	      if (( t =  ArgStr(&argc, &argv, "cap-accuracy") ) == NULL)
		goto usage;
	      esCapAccuracy = atoi(t);
	      break;
	      }
	case 'J':
	     {
	     char *ftmp ;

	     if ((ftmp = ArgStr(&argc, &argv, "hierAP_SD")) == NULL)
		goto usage;
	     if ( strcasecmp(ftmp, "HIER") == 0 )
		esHierAP = TRUE ;
	     else if ( strcasecmp(ftmp, "FLAT") == 0 )
		esHierAP = FALSE ;
	     else goto usage;

	     break;
	     }
	case 'j':
	    {
	    char *rp,  subsNode[80] ;
	    int   ndx, rClass, rClassSub = NO_RESCLASS;

	    if ((cp = ArgStr(&argc,&argv,"resistance class")) == NULL)
		goto usage;
	    if ( (rp = (char *)index(cp,':')) == NULL )
		goto usage;
	    else *rp++ = '\0';
	    if ( sscanf(rp, "%d/%d/%s", &rClass, &rClassSub, subsNode) != 3 ) {
		rClassSub = NO_RESCLASS ;
	    	if ( sscanf(rp, "%d/%s",  &rClass, subsNode) != 2 ) goto usage;
	    }
	    ndx = efBuildAddStr(EFFetTypes, &EFFetNumTypes, MAXFETTYPES, cp);
	    fetInfo[ndx].resClassSD = rClass;
	    fetInfo[ndx].resClassSub = rClassSub;
	    MALLOC(char *, fetInfo[ndx].defSubs, (strlen(subsNode)+1));
	    strcpy(fetInfo[ndx].defSubs,subsNode);
	    fprintf(stderr,
	    "Info: fet %s(%d) sdRclass=%d subRclass=%d dSub=%s\n", 
	    cp, ndx, fetInfo[ndx].resClassSD, fetInfo[ndx].resClassSub, 
            fetInfo[ndx].defSubs);
	    break;
	    }
	default:
	    fprintf(stderr, "Unrecognized flag: %s\n", argv[0]);
	    goto usage;
    }

    *pargv = argv;
    *pargc = argc;
    return;

usage:
    fprintf(stderr, "\
Usage: ext2sim [-a aliasfile] [-A] [-B] [-l labelfile] [-L]\n\
		[-o simfile] [-J flat|hier] [-y cap_digits]\n\
		[-j device:sdRclass[/subRclass]/defaultSubstrate]\n\
		[-f mit|lbl|su] file\n\
       or else see options to extcheck(1)\n");
    exit (1);
}


/*
 * ----------------------------------------------------------------------------
 *
 * GetNode --
 *
 * function to find a node given its hierarchical prefix and suffix
 *
 * Results:
 *  a pointer to the node struct or NULL
 *
 * ----------------------------------------------------------------------------
 */
EFNode *
GetNode(prefix, suffix)
HierName *prefix;
HierName *suffix;
{
	HashEntry *he;

	he = EFHNConcatLook(prefix, suffix, "output");
	return(((EFNodeName *) HashGetValue(he))->efnn_node);
}

/*
 * ----------------------------------------------------------------------------
 *
 * fetVisit --
 *
 * Procedure to output a single fet to the .sim file.
 * Called by EFVisitFets().
 *
 * Results:
 *	Returns 0 always.
 *
 * Side effects:
 *	Writes to the file esSimF.
 *
 * Format of a .sim fet line:
 *
 *	type gate source drain l w x y g= s= d=
 *
 * where
 *	type is a name identifying this type of transistor
 *	gate, source, and drain are the nodes to which these three
 *		terminals connect
 *	l, w are the length and width of the channel
 *	x, y are the x, y coordinates of a point within the channel.
 *	g=, s=, d= are the (optional) attributes; if present, each
 *		is followed by a comma-separated list of attributes.
 *
 * ----------------------------------------------------------------------------
 */

int
fetVisit(fet, hierName, trans, l, w)
    Fet *fet;		/* Fet being output */
    HierName *hierName;	/* Hierarchical path down to this fet */
    Transform *trans;	/* Coordinate transform for output */
    int l, w;
{
    FetTerm *gate, *source, *drain;
    EFNode  *subnode, *snode, *dnode;
    int scale;
    Rect r;
    char name[12];

    sprintf(name, "output");

    /* If no terminals, can't do much of anything */
    if (fet->fet_nterm < 2)
	return 0;

    /* If only two terminals, connect the source to the drain */
    gate = &fet->fet_terms[0];
    source = drain = &fet->fet_terms[1];
    if (fet->fet_nterm >= 3)
	drain = &fet->fet_terms[2];
    subnode = fet->fet_subsnode;

    /* Kludge for .sim: transistor types can only be one character */
    fprintf(esSimF, "%c", EFFetTypes[fet->fet_type][0]);

    /* Output gate, source, and drain node names */
    fetOutNode(hierName, gate->fterm_node->efnode_name->efnn_hier, name, esSimF);
    fetOutNode(hierName, source->fterm_node->efnode_name->efnn_hier, name, esSimF);

    /* Hack for BiCMOS---see scmos.tech27 hack:  Tim, 7/16/96 */

    if (EFFetTypes[fet->fet_type][0] == 'b') {
        sprintf(name, "fet");
        fetOutNode(hierName, subnode->efnode_name->efnn_hier, name, esSimF); 
    }
    else
        fetOutNode(hierName, drain->fterm_node->efnode_name->efnn_hier, name, esSimF);

    if ( esFormat == LBL ) { /* support gemini's substrate comparison */
	putc(' ', esSimF);
	fetSubstrate(hierName, subnode->efnode_name->efnn_hier,
	             fet->fet_type, 0, FALSE, esSimF);
    }

    GeoTransRect(trans, &fet->fet_rect, &r);
    scale = GeoScale(trans);

    if (EFFetTypes[fet->fet_type][0] == 'b') {

       /* Bipolar sim format:  We don't have the length and width
	* of the collector well, but we can get it from the area
	* and perimeter measurements; hopefully any strict netlist
	* comparator will deal with any problem arising from
	* swapping L and W.
	*/
	
	int n;
	double cl, cw;
	double chp = 0.0;
	double ca = 0.0;

	for (n = 0; n < efNumResistClasses; n++) {
	    ca += (double)(subnode->efnode_pa[n].pa_area);
	    chp += 0.5 * (double)(subnode->efnode_pa[n].pa_perim);
	}

	cl = 0.5 * (chp + sqrt(chp * chp - 4 * ca));
	cw = ca / cl;

	fprintf(esSimF, " %d %d %d %d", (int)cl, (int)cw,
		r.r_xbot, r.r_ybot);
    }
    else {

       /*
        * Scale L and W appropriately by the same amount as distance
        * values in the transform.  The transform will have a scale
        * different from 1 only in the case when the scale factors of
        * some of the .ext files differed, making it necessary to scale
        * all dimensions explicitly instead of having a single scale
        * factor at the beginning of the .sim file.
        */

       fprintf(esSimF, " %d %d %d %d",
		l*scale, w*scale, r.r_xbot, r.r_ybot);

       /* Attributes, if present */
       if (!esNoAttrs)
       {
	   bool subAP= FALSE, hierS = esHierAP, hierD = esHierAP;

	   if (gate->fterm_attrs)
	       fprintf(esSimF, " g=%s", gate->fterm_attrs);
	   if ( esFormat == SU ) {
	       if ( gate->fterm_attrs ) {
	         subAP = Match(ATTR_SUBSAP, gate->fterm_attrs ) ;
	         fprintf(esSimF, ",");
	       } else
	         fprintf(esSimF, " g=");
	       fetSubstrate(hierName, subnode->efnode_name->efnn_hier,
		         fet->fet_type, scale, subAP, esSimF);
	   }
	   if (source->fterm_attrs) {
	       fprintf(esSimF, " s=%s", source->fterm_attrs);
	       if  ( Match(ATTR_HIERAP, source->fterm_attrs ) != FALSE )
		   hierS = TRUE ;
	       else if ( Match(ATTR_FLATAP, source->fterm_attrs ) != FALSE )
		   hierS = FALSE ;
	   }
	   if ( esFormat == SU ) {
	       fprintf(esSimF, "%s", (source->fterm_attrs) ? "," : " s=" );
	       if (hierS)
	         nAPHier(source, hierName, fetInfo[fet->fet_type].resClassSD, 
		      scale, esSimF);
	       else {
	         snode= GetNode(hierName,
			     source->fterm_node->efnode_name->efnn_hier);
	         nAP(snode, fetInfo[fet->fet_type].resClassSD, scale, esSimF);
	       }
	   }
	   if (drain->fterm_attrs) {
	       fprintf(esSimF, " d=%s", drain->fterm_attrs);
	       if  ( Match(ATTR_HIERAP, drain->fterm_attrs ) != FALSE )
		   hierD = TRUE ;
	       else if ( Match(ATTR_FLATAP, drain->fterm_attrs ) != FALSE )
		   hierD = FALSE ;
	   }
	   if ( esFormat == SU ) {
	       fprintf(esSimF, "%s", (drain->fterm_attrs) ? "," : " d=" );
	       if (hierD) 
	         nAPHier(drain, hierName, fetInfo[fet->fet_type].resClassSD, 
		      scale, esSimF);
	       else {
	         dnode = GetNode(hierName,
			      drain->fterm_node->efnode_name->efnn_hier);
	         nAP(dnode, fetInfo[fet->fet_type].resClassSD, 
		      scale, esSimF);
	       }
	   }
       }
    }
    fprintf(esSimF, "\n");

    return 0;
}

/* Void */
void
fetSubstrate( prefix, suffix, type, scale, doAP, outf)
HierName *prefix;
HierName *suffix;
int type, scale;
bool doAP;
FILE *outf;
{
    HashEntry *he;
    EFNodeName *nn;
    char *suf ;
    int  l ;
    EFNode *subnode;

    suf = EFHNToStr(suffix);
    if (fetInfo[type].defSubs && strcasecmp(suf,fetInfo[type].defSubs) == 0) {
    	l = strlen(suf) - 1;
	if (  (( EFTrimFlags & EF_TRIMGLOB ) && suf[l] =='!') ||
	      (( EFTrimFlags & EF_TRIMLOCAL ) && suf[l] == '#')  )
	      suf[l] = '\0' ;
	if ( esFormat == SU )
		fprintf(outf, "S_");
	fprintf(outf, "%s", suf);
    }
    else {
    	he = EFHNConcatLook(prefix, suffix, "substrate");
    	if (he == NULL)
    	{
		(void) fprintf(outf, "errGnd!");
		return;
    	}
    	/* Canonical name */
    	nn = (EFNodeName *) HashGetValue(he);
	subnode = nn->efnn_node;
	if ( esFormat == SU ) {
	  if ( doAP ) {
	    if ( fetInfo[type].resClassSub < 0 ) {
	     fprintf(stderr, "error: subap for fettype %d required but not specified on command line\n", type);
	     fprintf(outf,"A_0,P_0,");
	    }
	    else {
		nAP(subnode, fetInfo[type].resClassSub, scale, outf);
		putc(',', outf);
	    }
	  }
	  fprintf(outf, "S_");
	}
    	EFHNOut(nn->efnn_node->efnode_name->efnn_hier, outf);
   }
}

/*
 * ----------------------------------------------------------------------------
 *
 * nAP, nAPHier --
 *
 * Output the area perimeter of the node with type type if it has not 
 * been visited.
 * The nAPHier version outputs the area and perimeter only within the
 * local subcell with hierarchical name hierName.
 *
 * Side effects:
 *     Set the visited flags so that the node A/P will not be output multiple
 *     times
 *
 * ----------------------------------------------------------------------------
 */
 
int
nAP(node, resClass, scale, outf)
EFNode *node;
int  resClass, scale;
FILE *outf;
{
	int a, p;

	if ( node->efnode_client == (ClientData) NULL ) 
		initNodeClient(node);
	if ( resClass == NO_RESCLASS ||
	     beenVisited((nodeClient *)node->efnode_client, resClass) ) {
		fprintf(outf,"A_0,P_0");
		return FALSE;
	}
	markVisited((nodeClient *)node->efnode_client, resClass);
	a = node->efnode_pa[resClass].pa_area*scale*scale;
	p = node->efnode_pa[resClass].pa_perim*scale;
	if ( a < 0 ) a = 0;
	if ( p < 0 ) p = 0;
	fprintf(outf,"A_%d,P_%d", a, p);
	return TRUE;
}

int
nAPHier(fterm, hierName, resClass, scale, outf)
FetTerm *fterm;
HierName *hierName;
int  resClass, scale;
FILE *outf;
{
	EFNode *node = fterm->fterm_node;
	nodeClientHier   *nc ;
	int a, p;

	if ( node->efnode_client == (ClientData) NULL ) 
		initNodeClientHier(node);
	nc = (nodeClientHier *)node->efnode_client;
	if ( nc->lastPrefix != hierName ) {
		nc->visitMask = 0;
		nc->lastPrefix = hierName;
	}
	if ( resClass == NO_RESCLASS ||
	     beenVisited((nodeClientHier *)node->efnode_client, resClass) ) {
		fprintf(outf,"A_0,P_0");
		return FALSE;
	}
	markVisited((nodeClientHier *)node->efnode_client, resClass);
	a = node->efnode_pa[resClass].pa_area*scale*scale;
	p = node->efnode_pa[resClass].pa_perim*scale;
	if ( a < 0 ) a = 0;
	if ( p < 0 ) p = 0;
	fprintf(outf,"A_%d,P_%d", a, p);
	return TRUE;
}


/*
 * ----------------------------------------------------------------------------
 *
 * fetOutNode --
 *
 * Output the name of the node whose hierarchical prefix down to this
 * point is 'prefix' and whose name from the end of suffix down to the
 * leaves is 'suffix', just as in the arguments to EFHNConcat().
 *
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	Writes to the file 'outf'.
 *	Sets the efnode_client field as described above.
 *
 * ----------------------------------------------------------------------------
 */

/* Void */
void
fetOutNode(prefix, suffix, name, outf)
    HierName *prefix;
    HierName *suffix;
    char *name;
    FILE *outf;
{
    HashEntry *he;
    EFNodeName *nn;

    he = EFHNConcatLook(prefix, suffix, name);
    if (he == NULL)
    {
	(void) fprintf(outf, " GND");
	return;
    }

    /* Canonical name */
    nn = (EFNodeName *) HashGetValue(he);
    (void) putc(' ', outf);
    EFHNOut(nn->efnn_node->efnode_name->efnn_hier, outf);
    if ( nn->efnn_node->efnode_client == (ClientData) NULL )
	initNodeClient(nn->efnn_node);
}

/*
 * ----------------------------------------------------------------------------
 *
 * capVisit --
 *
 * Procedure to output a single capacitor to the .sim file.
 * Called by EFVisitCaps().
 *
 * Results:
 *	Returns 0 always.
 *
 * Side effects:
 *	Writes to the file esSimF.
 *
 * Format of a .sim cap line:
 *
 *	C node1 node2 cap
 *
 * where
 *	node1, node2 are the terminals of the capacitor
 *	cap is the capacitance in femtofarads (NOT attofarads).
 *
 * ----------------------------------------------------------------------------
 */

int capVisit(hierName1, hierName2, cap)
    HierName *hierName1;
    HierName *hierName2;
    double cap;
{
    cap = cap / 1000;
    if (cap <= EFCapThreshold)
	return 0;

    (void) fprintf(esSimF, "C ");
    EFHNOut(hierName1, esSimF);
    (void) fprintf(esSimF, " ");
    EFHNOut(hierName2, esSimF);
    (void) fprintf(esSimF, esCapFormat, cap);
    return 0;
}

/*
 * ----------------------------------------------------------------------------
 *
 * resistVisit --
 *
 * Procedure to output a single resistor to the .sim file.
 * Called by EFVisitResists().
 *
 * Results:
 *	Returns 0 always.
 *
 * Side effects:
 *	Writes to the file esSimF.
 *
 * Format of a .sim resistor line:
 *
 *	r node1 node2 res
 *
 * where
 *	node1, node2 are the terminals of the resistor
 *	res is the resistance in ohms (NOT milliohms)
 *
 *
 * ----------------------------------------------------------------------------
 */

int
resistVisit(hierName1, hierName2, res)
    HierName *hierName1;
    HierName *hierName2;
    int res;
{
    res = (res+ 500)/1000;

    (void) fprintf(esSimF, "r ");
    EFHNOut(hierName1, esSimF);
    (void) fprintf(esSimF, " ");
    EFHNOut(hierName2, esSimF);
    (void) fprintf(esSimF, " %d\n", res);
    return 0;
}

/*
 * ----------------------------------------------------------------------------
 *
 * nodeVisit --
 *
 * Procedure to output a single node to the .sim file, along with
 * its aliases to the .al file and its location to the .nodes file.
 * Called by EFVisitNodes().
 *
 * Results:
 *	Returns 0 always.
 *
 * Side effects:
 *	Writes to the files esSimF, esAliasF, and esLabF.
 *
 * ----------------------------------------------------------------------------
 */

int nodeVisit(node, res, cap)
    register EFNode *node;
    int res;
    double cap;
{
    register EFNodeName *nn;
    HierName *hierName;
    bool isGlob;
    char *fmt;
    EFAttr *ap;

    if (esFetNodesOnly && node->efnode_client == (ClientData) NULL)
	return 0;

    hierName = (HierName *) node->efnode_name->efnn_hier;
    cap = cap  / 1000;
    res = (res + 500) / 1000;
    if (cap > EFCapThreshold)
    {
	(void) fprintf(esSimF, "C ");
	EFHNOut(hierName, esSimF);
	(void) fprintf(esSimF, esCapFormat, cap);
    }
    if (res > EFResistThreshold)
    {
	(void) fprintf(esSimF, "R ");
	EFHNOut(hierName, esSimF);
	(void) fprintf(esSimF, " %d\n", res);
    }
    if (node->efnode_attrs && !esNoAttrs)
    {
	(void) fprintf(esSimF, "A ");
	EFHNOut(hierName, esSimF);
	for (fmt = " %s", ap = node->efnode_attrs; ap; ap = ap->efa_next)
	{
	    (void) fprintf(esSimF, fmt, ap->efa_text);
	    fmt = ",%s";
	}
	putc('\n', esSimF);
    }

    if (esAliasF)
    {
	isGlob = EFHNIsGlob(hierName);
	for (nn = node->efnode_name->efnn_next; nn; nn = nn->efnn_next)
	{
	    if (isGlob && EFHNIsGlob(nn->efnn_hier))
		continue;
	    (void) fprintf(esAliasF, "= ");
	    EFHNOut(hierName, esAliasF);
	    (void) fprintf(esAliasF, " ");
	    EFHNOut(nn->efnn_hier, esAliasF);
	    (void) fprintf(esAliasF, "\n");
	}
    }

    if (esLabF)
    {
	(void) fprintf(esLabF, "94 ");
	EFHNOut(hierName, esLabF);
	(void) fprintf(esLabF, " %d %d %s;\n",
		    node->efnode_loc.r_xbot, node->efnode_loc.r_ybot,
		    EFLayerNames[node->efnode_type]);
    }

    return 0;
}

