/*
 * lefRead.c --
 *
 * This module incorporates the LEF/DEF format for standard-cell place and
 * route.
 *
 * Version 0.1 (September 26, 2003):  LEF input handling.  Includes creation
 * of cells from macro statements, handling of pins, ports, obstructions, and
 * associated geometry.
 *
 * Note that techfile compatibility requires that each layer name appearing
 * in the LEF file should be present as an alias for the appropriate magic
 * tile type in the technology file.  Layer names are not case sensitive.
 */

#ifndef lint
static const char rcsid[] __attribute__ ((unused)) = "$Header: /usr/cvsroot/magic-8.0/lef/lefRead.c,v 1.2 2008/12/17 18:40:04 tim Exp $";
#endif  /* not lint */

#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <string.h>
#include <strings.h>
#include <errno.h>
#include <stdarg.h>
#ifdef HAVE_SYS_TIME_H
#include <sys/time.h>
#endif
#include <math.h>		/* for roundf() function, if std=c99 */

#include "tcltk/tclmagic.h"
#include "utils/magic.h"
#include "utils/geometry.h"
#include "tiles/tile.h"
#include "utils/hash.h"
#include "utils/undo.h"
#include "utils/utils.h"	/* For StrDup() */
#include "database/database.h"
#include "windows/windows.h"
#include "graphics/graphics.h"
#include "dbwind/dbwind.h"
#include "utils/malloc.h"
#include "textio/textio.h"
#include "cif/cif.h"
#include "cif/CIFint.h"		/* Access to CIFCurStyle. . . */
#include "cif/CIFread.h"	/* Access to cifCurReadStyle. . . */
#include "lef/lefInt.h"

/* C99 compat */
#include "utils/signals.h"
#include "utils/signals.h"
#include "drc/drc.h"
#include "lef/lef.h"

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

/* Current line number for reading */
int lefCurrentLine;

/* Cell reading hash tables */
HashTable LefCellTable;
HashTable lefDefInitHash;

/*
 *------------------------------------------------------------
 *
 * LefEstimate --
 *
 *	Estimate the time to completion based on the time,
 *	the total number of items to process, and the number
 *	of items processed so far.  Attempts to report about
 *	every 5 seconds or so.
 *
 * Results:
 *	None.
 *
 * Side Effects:
 *	May print to output.
 *
 *------------------------------------------------------------
 */

#define PRINT_INTERVAL 5	/* print status at 4 second intervals */

void
LefEstimate(
   int processed,
   int total,
   const char *item_name)
{
    static struct timeval tv_start;
    struct timeval tv;
    struct timezone tz;
    float cur_time, time_left;

    if (!total) return;

    if (processed == 0)		/* Initialization */
    {
	gettimeofday(&tv_start, &tz);
	GrDisplayStatus = DISPLAY_IN_PROGRESS;
	SigSetTimer(PRINT_INTERVAL);
    }
    else if (processed == total - 1)
    {
	GrDisplayStatus = DISPLAY_IDLE;
	SigRemoveTimer();
    }
    else if (GrDisplayStatus == DISPLAY_BREAK_PENDING)
    {
	gettimeofday(&tv, &tz);
	cur_time = (float)(tv.tv_sec - tv_start.tv_sec)
		+ ((float)(tv.tv_usec - tv_start.tv_usec) / 1.0e6);
	time_left = (((float)total / (float)processed) - 1) * cur_time;

	/* not likely to happen, but we don't want a divide-by-0 error */
	if (cur_time == 0.0) cur_time = 1.0e-6;

	TxPrintf("  Processed %d of %d %s (%2.1f%%).", processed, total,
			item_name, (float)(100 * processed) / (float)total);
	TxPrintf("  Est. time remaining: %2.1fs\n", time_left);
	TxFlushOut();

#ifdef MAGIC_WRAPPER
	/* We need to let Tk paint the console display */
	while (Tcl_DoOneEvent(TCL_DONT_WAIT) != 0);
#endif

	GrDisplayStatus = DISPLAY_IN_PROGRESS;
	SigSetTimer(PRINT_INTERVAL);
    }
}

/* This is the original version, which doesn't use the system itimer	*/
/* and which is not very good at maintaining constant intervals.	*/

#if 0
void
LefEstimate(
   int processed,
   int total,
   const char *item_name)
{
    static int check_interval, partition;
    static struct timeval tv_start;
    static float last_time;
    struct timeval tv;
    struct timezone tz;
    float cur_time, time_left;

    if (!total) return;

    if (processed == 0)		/* Initialization */
    {
	GrDisplayStatus = DISPLAY_IN_PROGRESS;

	check_interval = 100;
	gettimeofday(&tv_start, &tz);
	last_time = 0.0;
    }

    if (processed > check_interval)
    {
	gettimeofday(&tv, &tz);
	cur_time = (float)(tv.tv_sec - tv_start.tv_sec)
		+ ((float)(tv.tv_usec - tv_start.tv_usec) / 1.0e6);
	time_left = (((float)total / (float)processed) - 1) * cur_time;

	/* not likely to happen, but we don't want a divide-by-0 error */
	if (cur_time == 0.0) cur_time = 1.0e-6;

	partition = (int)((float)processed * (float)PRINT_INTERVAL / cur_time);

	/* partition is never less than 1 nor greater than 5% of the total */
	if (partition == 0) partition = 1;
	if (partition > (total / 20)) partition = (total / 20);

	check_interval += partition;

	/* Don't print anything in intervals faster than 1 second */
	if ((cur_time - last_time) < 1.0) return;
	last_time = cur_time;

	TxPrintf("  Processed %d of %d %s (%2.1f%%).", processed, total,
			item_name, (float)(100 * processed) / (float)total);
	TxPrintf("  Est. time remaining: %2.1fs\n", time_left);
	TxFlushOut();
    }
}

#endif /* 0 */


/*
 *------------------------------------------------------------
 *
 * LefNextToken --
 *
 *	Move to the next token in the stream input.
 *	If "ignore_eol" is FALSE, then the end-of-line character
 *	"\n" will be returned as a token when encountered.
 *	Otherwise, end-of-line will be ignored.
 *
 * Results:
 *	Pointer to next token to parse
 *
 * Side Effects:
 *	May read a new line from the specified file.
 *
 * Warnings:
 *	The return result of LefNextToken will be overwritten by
 *	subsequent calls to LefNextToken if more than one line of
 *	input is parsed.
 *
 *------------------------------------------------------------
 */

const char *
LefNextToken(
    FILE *f,
    bool ignore_eol)
{
    static char line[LEF_LINE_MAX + 2];	/* input buffer */
    static char *nexttoken = NULL;	/* pointer to next token */
    static char *curtoken;		/* pointer to current token */
    static const char eol_token='\n';

    /* Read a new line if necessary */

    if (nexttoken == NULL)
    {
	for(;;)
	{
	    if (fgets(line, LEF_LINE_MAX + 1, f) == NULL) return NULL;
	    lefCurrentLine++;
	    curtoken = line;
	    while (isspace(*curtoken) && (*curtoken != '\n') && (*curtoken != '\0'))
		curtoken++;		/* skip leading whitespace */

	    if ((*curtoken != '#') && (*curtoken != '\n') && (*curtoken != '\0'))
	    {
		nexttoken = curtoken;
		break;
	    }
	}
	if (!ignore_eol)
	    return &eol_token;
    }
    else
	curtoken = nexttoken;

    /* Find the next token; set to NULL if none (end-of-line). */
    /* Treat quoted material as a single token. */

    if (*nexttoken == '\"') {
	nexttoken++;
	while (((*nexttoken != '\"') || (*(nexttoken - 1) == '\\')) &&
		(*nexttoken != '\0')) {
	    if (*nexttoken == '\n') {
		if (fgets(nexttoken + 1, LEF_LINE_MAX -
				(size_t)(nexttoken - line), f) == NULL)
		    return NULL;
	    }
	    nexttoken++;
	}
	if (*nexttoken == '\"')
	    nexttoken++;
    }
    else {
	while (!isspace(*nexttoken) && (*nexttoken != '\0') && (*nexttoken != '\n'))
	    nexttoken++;	/* skip non-whitespace (move past current token) */
    }

    /* Terminate the current token */
    if (*nexttoken != '\0') *nexttoken++ = '\0';

    while (isspace(*nexttoken) && (*nexttoken != '\0') && (*nexttoken != '\n'))
	nexttoken++;	/* skip any whitespace */

    if ((*nexttoken == '#') || (*nexttoken == '\n') || (*nexttoken == '\0'))
	nexttoken = NULL;

    return curtoken;
}

/*
 *------------------------------------------------------------
 *
 * LefDefError --
 *
 *	Print an error message (via TxError) giving the line
 *	number of the input file on which the error occurred.
 *
 *	"type" is one of the following (see lef.h):
 *	    LEF_ERROR, LEF_WARNING, LEF_INFO,
 *	    DEF_ERROR, DEF_WARNING, DEF_INFO
 *
 * Results:
 *	None.
 *
 * Side Effects:
 *	Prints to the output (stderr).
 *
 *------------------------------------------------------------
 */

void
LefError(
    int type,
    const char *fmt, ...)
{
    static int errors = 0, warnings = 0, messages = 0;
    va_list args;

    const char *lefdeftypes[] = {"LEF", "DEF", "techfile lef section"};

    int mode, level;
    const char *lefdeftype;

    switch (type) {
	case LEF_INFO:
	    mode = 0;
	    level = 0;
	    break;
	case LEF_WARNING:
	    mode = 0;
	    level = 1;
	    break;
	case LEF_ERROR:
	    mode = 0;
	    level = 2;
	    break;
	case LEF_SUMMARY:
	    mode = 0;
	    level = -1;
	    break;
	case DEF_INFO:
	    mode = 1;
	    level = 0;
	    break;
	case DEF_WARNING:
	    mode = 1;
	    level = 1;
	    break;
	case DEF_ERROR:
	    mode = 1;
	    level = 2;
	    break;
	case DEF_SUMMARY:
	    mode = 1;
	    level = -1;
	    break;
	default:
	    ASSERT(FALSE, "type");
	    return;
	    break;
    }
    lefdeftype = lefdeftypes[mode];

    if ((fmt == NULL) || (level == -1))
    {
	/* Special case:  report any errors and reset */

	if (errors)
	    TxPrintf("%s Read: encountered %d error%s total.\n",
			lefdeftype, errors, (errors == 1) ? "" : "s");

	if (warnings)
	    TxPrintf("%s Read: encountered %d warning%s total.\n",
			lefdeftype, warnings, (warnings == 1) ? "" : "s");

	errors = 0;
        warnings = 0;
	messages = 0;
	return;
    }

    switch (level) {

	case 2:
	    if (errors < LEF_MAX_ERRORS)
	    {
		if (lefCurrentLine >= 0)
		    TxError("%s read, Line %d (Error): ", lefdeftype, lefCurrentLine);
		else
		    TxError("%s read (Error): ", lefdeftype);
		va_start(args, fmt);
		Vfprintf(stderr, fmt, args);
		va_end(args);
		TxFlushErr();
	    }
	    else if (errors == LEF_MAX_ERRORS)
		TxError("%s Read:  Further errors will not be reported.\n",
			    lefdeftype);
	    errors++;
	    break;

	case 1:
	    if (warnings < LEF_MAX_ERRORS)
	    {
		if (lefCurrentLine >= 0)
		    TxError("%s read, Line %d (Warning): ", lefdeftype, lefCurrentLine);
		else
		    TxError("%s read (Warning): ", lefdeftype);
		va_start(args, fmt);
		Vfprintf(stderr, fmt, args);
		va_end(args);
		TxFlushErr();
	    }
	    else if (warnings == LEF_MAX_ERRORS)
		TxError("%s read:  Further warnings will not be reported.\n",
			    lefdeftype);
	    warnings++;
	    break;

	case 0:
	    if (messages < LEF_MAX_ERRORS)
	    {
		if (lefCurrentLine >= 0)
		    TxPrintf("%s read, Line %d (Message): ", lefdeftype, lefCurrentLine);
		else
		    TxPrintf("%s read (Message): ", lefdeftype);
		va_start(args, fmt);
		Vfprintf(stdout, fmt, args);
		va_end(args);
		TxFlushOut();
	    }
	    else if (messages == LEF_MAX_ERRORS)
		TxPrintf("%s read:  Further messages will not be reported.\n",
			    lefdeftype);
	    messages++;
	    break;
    }
}

/*
 *------------------------------------------------------------
 *
 * LefParseEndStatement --
 *
 *	Check if the string passed in "lineptr" contains the
 *	appropriate matching keyword.  Sections in LEF files
 *	should end with "END (keyword)" or "END".  To check
 *	against the latter case, "match" should be NULL.
 *
 * Results:
 *	1 if the line matches the expected end statement,
 *	0 if not.  Return -1 if the last token read was
 *	"END", indicating to the caller that either an
 *	error has occurred or, in the case of a section
 *	skip, the routine needs to be called again.
 *
 * Side effects:
 *	None.
 *
 *------------------------------------------------------------
 */

int
LefParseEndStatement(
    FILE *f,
    const char *match)
{
    const char *token;
    int keyword;
    const char * match_name[2];

    static const char * const end_section[] = {
	"END",
	"ENDEXT",
	NULL
    };

    match_name[0] = match;
    match_name[1] = NULL;

    token = LefNextToken(f, (match == NULL) ? FALSE : TRUE);
    if (token == NULL)
    {
	LefError(LEF_ERROR, "Bad file read while looking for END statement\n");
	return 0;
    }

    /* END or ENDEXT */
    if ((*token == '\n') && (match == NULL)) return 1;

    /* END <section_name> */
    else {
	keyword = LookupFull(token, match_name);
	if (keyword == 0)
	    return 1;
	else
	{
	    /* Check for END followed by END */
	    keyword = LookupFull(token, end_section);
	    if (keyword == 0)
		return -1;
	    else
		return 0;
	}
    }
}

/*
 *------------------------------------------------------------
 *
 * LefSkipSection --
 *
 *	Skip to the "END" record of a LEF input section
 *	String "section" must follow the "END" statement in
 *	the file to be considered a match;  however, if
 *	section = NULL, then "END" must have no strings
 *	following.
 *
 * Results:
 *	None.
 *
 * Side Effects:
 *	reads input from the specified file.  Prints an
 *	error message if the expected END record cannot
 *	be found.
 *
 *------------------------------------------------------------
 */

void
LefSkipSection(
    FILE *f,
    const char *section)
{
    const char *token;
    int keyword, result;
    static const char * const end_section[] = {
	"END",
	"ENDEXT",
	NULL
    };

    while ((token = LefNextToken(f, TRUE)) != NULL)
    {
	if ((keyword = LookupFull(token, end_section)) == 0)
	{
	    result = -1;
	    while (result == -1)
	    {
		result = LefParseEndStatement(f, section);
		if (result == 1) return;
	    }
	}
	else if (keyword == 1)
	{
	    if (!strcmp(section, "BEGINEXT"))
		return;
	}
    }

    LefError(LEF_ERROR, "Section %s has no END record!\n", section);
    return;
}

/*
 *------------------------------------------------------------
 *
 * lefFindCell --
 *
 *	Search for an existing cell of the given name.  If
 *	it exists, return a pointer to the cell def.  If not,
 *	create a new cell def with the given name and return
 *	a pointer to it.
 *
 *------------------------------------------------------------
 */

CellDef *
lefFindCell(
    const char *name)		/* Name of the cell to search for */
{
    HashEntry *h;
    CellDef *def;

    h = HashFind(&LefCellTable, name);
    if (HashGetValue(h) == 0)
    {
	def = DBCellLookDef(name);
	if (def == NULL)
	{
	    def = DBCellNewDef(name);
	    DBReComputeBbox(def);
	}
	HashSetValue(h, def);
    }
    return (CellDef *) HashGetValue(h);
}

/*
 *------------------------------------------------------------
 *
 * LefLower --
 *
 *	Convert a token in a LEF or DEF file to all-lowercase.
 *
 *------------------------------------------------------------
 */

char *
LefLower(
    char *token)
{
    char *tptr;

    for (tptr = token; *tptr != '\0'; tptr++)
	*tptr = tolower(*tptr);

    return token;
}

/*
 *------------------------------------------------------------
 * LefHelper_DBTechNameType_LefLower --
 *
 *  Helper function to make other parts of module code more
 *  readable (one liner usage), for the common use of:
 *    tileType = DBTechNameType(LefLower(token));
 *
 *  This function allows token to be a (const char *) and
 *  original data source is not edited.
 *
 * Results:
 *  as per TileType from DBTechNameType() after lookup.
 *
 * Side Effects:
 :  None
 *
 *  Module internal API only.
 */

TileType
LefHelper_DBTechNameType_LefLower(const char *name)
{
    char tmp[256];
    strncpy(tmp, name, sizeof(tmp));
    /* after maybe helps codegen, only care to check overrun */
    ASSERT(strlen(name) < sizeof(tmp), "strlen(name)");
    tmp[sizeof(tmp)-1] = '\0';
    LefLower(tmp);
    return DBTechNameType(tmp);
}

/*
 *------------------------------------------------------------
 * LefRedefined --
 *
 *	In preparation for redefining a LEF layer, we need
 *	to first check if there are multiple names associated
 *	with the lefLayer entry.  If so, split the entry into
 *	two copies, so that the redefinition doesn't affect
 *	the other LEF names.
 *
 * Results:
 *	Pointer to a lefLayer, which may or may not be the
 *	same one presented to the subroutine.
 *
 * Side Effects:
 *	May add an entry to the list of LEF layers.
 *
 *------------------------------------------------------------
 */

lefLayer *
LefRedefined(
    lefLayer *lefl,
    const char *redefname)
{
    lefLayer *slef, *newlefl;
    const char *altName;
    LinkedRect *viaLR;
    HashSearch hs;
    HashEntry *he;
    int records;

    /* check if more than one entry points to the same	*/
    /* lefLayer record.	 If so, we will also record the	*/
    /* name of the first type that is not the same as	*/
    /* "redefname".					*/

    records = 0;
    altName = NULL;
    HashStartSearch(&hs);
    while ((he = HashNext(&LefInfo, &hs)))
    {
	slef = (lefLayer *)HashGetValue(he);
	if (slef == lefl)
	    records++;
	if (altName == NULL)
	    if (strcmp((const char *)he->h_key.h_name, redefname))
		altName = (const char *)he->h_key.h_name;
    }
    if (records == 1)
    {
	/* Only one name associated with the record, so	*/
	/* just clear all the allocated information.	*/

	free_magic1_t mm1 = freeMagic1_init();
	for (viaLR = lefl->info.via.lr; viaLR != NULL; viaLR = viaLR->r_next)
	    freeMagic1(&mm1, viaLR);
	freeMagic1_end(&mm1);
	newlefl = lefl;
    }
    else
    {
	he = HashFind(&LefInfo, redefname);
	newlefl = (lefLayer *)mallocMagic(sizeof(lefLayer));
	newlefl->refCnt = 1;
	newlefl->canonName = (const char *)he->h_key.h_name;
	HashSetValue(he, newlefl);

	/* If the canonical name of the original entry	*/
	/* is "redefname", then change it.		*/

	if (!strcmp(lefl->canonName, redefname))
	    if (altName != NULL)
		lefl->canonName = altName;
    }
    newlefl->type = -1;
    newlefl->obsType = -1;
    newlefl->info.via.area = GeoNullRect;
    newlefl->info.via.cell = (CellDef *)NULL;
    newlefl->info.via.lr = (LinkedRect *)NULL;

    return newlefl;
}

/*
 *------------------------------------------------------------
 * LefReadLayers --
 *
 *	Read a LEF "LAYER" record from the file, and
 *	match the LEF layer to a magic tile type.
 *	If "obstruct" is TRUE, returns the layer mapping
 *	for obstruction geometry as defined in the
 *	technology file (if it exists), and up to two
 *	types are returned (the second in the 3rd argument
 *	pointer).
 *
 * Results:
 *	Returns a magic TileType or -1 if no matching
 *	tile type is found.
 *
 * Side Effects:
 *	Reads input from file f;
 *
 *------------------------------------------------------------
 */

TileType
LefReadLayers(
    FILE *f,
    bool obstruct,
    TileType *lreturn,
    const Rect **rreturn)
{
    const char *token;
    TileType curlayer = -1;
    lefLayer *lefl = NULL;

    token = LefNextToken(f, TRUE);
    if (*token == ';')
    {
	LefError(LEF_ERROR, "Bad Layer statement\n");
	return -1;
    }
    else
    {
	HashEntry *he = HashLookOnly(&LefInfo, token);
	if (he)
	{
	    lefl = (lefLayer *)HashGetValue(he);
	    if (lefl && obstruct)
	    {
		/* Use the obstruction type, if it is defined */
		curlayer = lefl->obsType;
		if ((curlayer < 0) && (lefl->lefClass != CLASS_IGNORE))
		    curlayer = lefl->type;
		else if (lefl->lefClass == CLASS_VIA)
		    if (lreturn) *lreturn = lefl->info.via.obsType;
	    }
	    else if (lefl)
	    {
		if (lefl->lefClass != CLASS_IGNORE)
		    curlayer = lefl->type;
		if (lefl->lefClass == CLASS_VIA)
		    if (rreturn) *rreturn = &(lefl->info.via.area);
	    }

	    if (rreturn)
	    {
		if (lefl->lefClass == CLASS_VIA)
		    *rreturn = &(lefl->info.via.area);
		else
		    *rreturn = &GeoNullRect;
	    }
	}
	else
	{
	    /* fallback: compare name to magic types */

	    curlayer = DBTechNameType(token);
	    if (curlayer < 0)
	    {
		curlayer = LefHelper_DBTechNameType_LefLower(token);
	    }
	}
	if ((curlayer < 0) && ((!lefl) || (lefl->lefClass != CLASS_IGNORE)))
	{
	    LefError(LEF_ERROR, "Don't know how to parse layer \"%s\"\n", token);
	    LefError(LEF_ERROR, "Try adding this name to the LEF techfile section\n");
	}
    }
    return curlayer;
}

/*
 *------------------------------------------------------------
 * LefReadLayer --
 *
 *	Read a LEF "LAYER" record from the file, and
 *	match the LEF layer to a magic tile type.
 *	If "obstruct" is TRUE, returns the layer mapping
 *	for obstruction geometry as defined in the
 *	technology file (if it exists).
 *
 * Results:
 *	Returns a magic TileType or -1 if no matching
 *	tile type is found.
 *
 * Side Effects:
 *	Reads input from file f;
 *
 *------------------------------------------------------------
 */

TileType
LefReadLayer(
    FILE *f,
    bool obstruct)
{
    return LefReadLayers(f, obstruct, (TileType *)NULL, (const Rect **)NULL);
}

/*
 *------------------------------------------------------------
 * LefReadLefPoint --
 *
 *	Read a LEF point record from the file, and
 *	return the two coordinates in LEF units (floating-point).
 *
 * Results:
 *	Return 0 on success, 1 on error.  Return -1 if no
 *	coordinates are found in the input (used where
 *	coordinates may be optional).
 *
 * Side Effects:
 *	Reads input from file f;
 *	Returns values for the two point coordinates, as type
 *	float, in "xp" and "yp".
 *
 * Note:
 *	Most LEF files do not put parentheses around points, e.g.,
 *	in the ORIGIN statement, although the LEF specification
 *	definition of a point insists that it must.  Therefore
 *	we attempt to parse points correctly either way.
 *
 *------------------------------------------------------------
 */

int
LefReadLefPoint(
    FILE *f,
    float *xp,
    float *yp)
{
    const char *token;
    bool needMatch = FALSE;

    token = LefNextToken(f, TRUE);
    if (!token) return -1;
    if (*token == ';') return -1;
    if (*token == '(')
    {
	token = LefNextToken(f, TRUE);
	needMatch = TRUE;
    }
    if (!token || sscanf(token, "%f", xp) != 1) return 1;
    token = LefNextToken(f, TRUE);
    if (!token || sscanf(token, "%f", yp) != 1) return 1;
    if (needMatch)
    {
	token = LefNextToken(f, TRUE);
	if (*token != ')') return 1;
    }
    return 0;
}

/*
 *------------------------------------------------------------
 * LefReadRect --
 *
 *	Read a LEF "RECT" record from the file, and
 *	return a Rect in magic coordinates.
 *
 * Results:
 *	Returns a pointer to a Rect containing the magic
 *	coordinates, or NULL if an error occurred.
 *
 * Side Effects:
 *	Reads input from file f;
 *
 * Note:
 *	LEF/DEF does NOT define a RECT record as having (...)
 *	pairs, only routes.  However, at least one DEF file
 *	contains this syntax, so it is checked.
 *
 *------------------------------------------------------------
 */

Rect *
LefReadRect(
    FILE *f,
    TileType curlayer,
    float oscale)
{
    const char *token;
    float llx, lly, urx, ury;
    static Rect paintrect;
    Rect lefrect;
    bool needMatch = FALSE;

    token = LefNextToken(f, TRUE);
    if (*token == '(')
    {
	token = LefNextToken(f, TRUE);
	needMatch = TRUE;
    }
    if (!token || sscanf(token, "%f", &llx) != 1) goto parse_error;
    token = LefNextToken(f, TRUE);
    if (!token || sscanf(token, "%f", &lly) != 1) goto parse_error;
    token = LefNextToken(f, TRUE);
    if (needMatch)
    {
	if (*token != ')') goto parse_error;
	else token = LefNextToken(f, TRUE);
	needMatch = FALSE;
    }
    if (*token == '(')
    {
	token = LefNextToken(f, TRUE);
	needMatch = TRUE;
    }
    if (!token || sscanf(token, "%f", &urx) != 1) goto parse_error;
    token = LefNextToken(f, TRUE);
    if (!token || sscanf(token, "%f", &ury) != 1) goto parse_error;
    if (needMatch)
    {
	token = LefNextToken(f, TRUE);
	if (*token != ')') goto parse_error;
    }
    if (curlayer < 0)
    {
	LefError(LEF_ERROR, "No layer defined for RECT.\n");
	paintrect.r_xbot = paintrect.r_ybot = 0;
	paintrect.r_xtop = paintrect.r_ytop = 0;
    }
    else
    {
	/* Scale coordinates (microns to magic internal units)	*/
	/* Need to scale grid if necessary!			*/

	lefrect.r_xbot = (int)roundf(llx / oscale);
	lefrect.r_ybot = (int)roundf(lly / oscale);
	lefrect.r_xtop = (int)roundf(urx / oscale);
	lefrect.r_ytop = (int)roundf(ury / oscale);

	/* Insist on non-inverted rectangles */
	GeoCanonicalRect(&lefrect, &paintrect);

	/* Diagnostic */
	/*
	TxPrintf("   Painting %s at (%d, %d), (%d, %d) in cell %s.\n",
		DBTypeLongNameTbl[curlayer],
		paintrect.r_xbot, paintrect.r_ybot,
		paintrect.r_xtop, paintrect.r_ytop,
		lefMacro->cd_name);
	*/
    }
    return (&paintrect);

parse_error:
    LefError(LEF_ERROR, "Bad port geometry: RECT requires 4 values.\n");
    return (Rect *)NULL;
}

/*
 *------------------------------------------------------------
 * LefReadPolygon --
 *
 *	Read a LEF "POLYGON" record from the file, and
 *	return a linked point structure.
 *
 * Results:
 *	Returns a pointer to a Rect containing the magic
 *	coordinates, or NULL if an error occurred.
 *
 * Side Effects:
 *	Reads input from file f;
 *
 * Note:
 *	LEF/DEF does NOT define a RECT record as having (...)
 *	pairs, only routes.  However, at least one DEF file
 *	contains this syntax, so it is checked.
 *
 *------------------------------------------------------------
 */

Point *
LefReadPolygon(
    FILE *f,
    TileType curlayer,
    float oscale,
    Point *gdsOffset,
    int *ppoints)
{
    LinkedRect *lr = NULL, *newRect;
    Point *plist = NULL;
    const char *token;
    float px, py;
    int lpoints = 0;

    while (1)
    {
	token = LefNextToken(f, TRUE);
	if (token == NULL || *token == ';') break;
 	if (sscanf(token, "%f", &px) != 1)
	{
	    LefError(LEF_ERROR, "Bad X value in polygon.\n");
	    LefEndStatement(f);
	    break;
	}

	token = LefNextToken(f, TRUE);
	if (token == NULL || *token == ';')
	{
	    LefError(LEF_ERROR, "Missing Y value in polygon point!\n");
	    break;
	}
	if (sscanf(token, "%f", &py) != 1)
	{
	    LefError(LEF_ERROR, "Bad Y value in polygon.\n");
	    LefEndStatement(f);
	    break;
	}

	/* Use the rect structure for convenience;  we only	*/
	/* use the r_ll point of the rect to store each point	*/
	/* as we read it in.					*/

	newRect = (LinkedRect *)mallocMagic(sizeof(LinkedRect));
	newRect->r_r.r_xbot = (int)roundf(px / oscale) + gdsOffset->p_x;
	newRect->r_r.r_ybot = (int)roundf(py / oscale) + gdsOffset->p_y;
	newRect->r_next = lr;
	lr = newRect;
	lpoints++;
    }

    *ppoints = lpoints;
    if (lpoints == 0) return NULL;

    /* Convert LinkedRect structure into a simple point list */

    plist = (Point *)mallocMagic(lpoints * sizeof(Point));
    lpoints = 0;
    free_magic1_t mm1 = freeMagic1_init();
    while (lr != NULL)
    {
	plist[*ppoints - lpoints - 1].p_x = lr->r_r.r_xbot;
	plist[*ppoints - lpoints - 1].p_y = lr->r_r.r_ybot;
	freeMagic1(&mm1, lr);
	lpoints++;
	lr = lr->r_next;
    }
    freeMagic1_end(&mm1);
    return plist;
}

/*
 *------------------------------------------------------------
 * LefPaintPolygon --
 *
 *	Paint a polygon into the CellDef indicated by lefMacro.
 *
 *------------------------------------------------------------
 */

LinkedRect *
LefPaintPolygon(
    CellDef *lefMacro,
    Point *pointList,
    int points,
    TileType curlayer,
    bool keep)
{
    int pNum;
    PaintUndoInfo ui;
    LinkedRect *rlist = NULL, *rptr;

    ui.pu_def = lefMacro;
    for (pNum = PL_PAINTBASE; pNum < DBNumPlanes; pNum++)
    {
	if (DBPaintOnPlane(curlayer, pNum))
	{
	    ui.pu_pNum = pNum;
	    rlist = PaintPolygon(pointList, points, lefMacro->cd_planes[pNum],
			DBStdPaintTbl(curlayer, pNum), &ui, keep);

	    /* Annotate each rectangle in the list with the type painted */
	    if (keep)
		for (rptr = rlist; rptr; rptr = rptr->r_next)
		    rptr->r_type = curlayer;
	}
    }
    return rlist;
}

/* Structure used by lefConnectFunc(), below */

typedef struct _plane_type {
    Plane *pt_plane;
    TileType pt_type;
} PlaneType;

/*
 *------------------------------------------------------------
 * lefConnectFunc --
 *
 * Callback function used by LefReadGeometry when doing LEF
 * annotation to check if a port is compatible with the
 * existing layout.  Where a compatible type is found, paint
 * the type that is passed in the client data argument into
 * the plane that is also passed in the client data argument.
 * This builds up an area of the port type in the cell
 * hierarchy that can be checked against the port rectangle.
 *
 * Return 0 always to keep the search going.
 *------------------------------------------------------------
 */
int
lefConnectFunc(
    Tile *tile,
    TileType dinfo,	// Not handled, but should be.
    TreeContext *cxp)
{
    SearchContext *scx = cxp->tc_scx;
    PlaneType *pt = (PlaneType *)cxp->tc_filter->tf_arg;
    Rect rect, trect;

    TiToRect(tile, &rect);
    GeoTransRect(&scx->scx_trans, &rect, &trect);
    
    /* Where a compatible type is found, paint the type pass as
     * argument into the plane passed as argument.
     */

    DBPaintPlane(pt->pt_plane, &trect,
		DBStdPaintTbl(pt->pt_type, cxp->tc_plane),
		(PaintUndoInfo *)NULL);

    return 0;
}

/*
 *------------------------------------------------------------
 * lefUnconnectFunc --
 *
 * Simple search that returns 1 if any tile is found in the
 * plane constructed by lefConnectFunc() above in the area of
 * a LEF pin that does not have the same type as the pin.
 * 
 *------------------------------------------------------------
 */

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

/*
 *------------------------------------------------------------
 * LefReadGeometry --
 *
 *	Read Geometry information from a LEF file.
 *	Used for PORT records and OBS statements.
 *
 * Results:
 *	Returns a linked list of all areas and types
 *	painted.  However, if "do_list" is FALSE, then
 *	LefReadGeometry always returns NULL.
 *
 *	Note that do_list is always TRUE for PORT
 *	records and FALSE for OBS statements, and so in
 *	addition to determining what the return value is,
 *	it also determines what layer is returned by
 *	function LefReadLayer().
 *
 *	If is_imported is TRUE, then this is an annotation,
 *	and no geometry should ever be added over an area
 *	that does not have a compatible type.
 *
 * Side Effects:
 *	Reads input from file f;
 *	Paints into the CellDef lefMacro.
 *
 *------------------------------------------------------------
 */

enum lef_geometry_keys {LEF_LAYER = 0, LEF_WIDTH, LEF_PATH,
	LEF_RECT, LEF_POLYGON, LEF_VIA, LEF_PORT_CLASS,
	LEF_GEOMETRY_END};

LinkedRect *
LefReadGeometry(
    CellDef *lefMacro,
    FILE *f,
    float oscale,
    Point *gdsOffset,
    bool do_list,
    bool is_imported)
{
    TileType curlayer = -1, otherlayer = -1;

    const char *token;
    int keyword;
    LinkedRect *newRect, *rectList;
    Point *pointList;
    int points;
    Rect *paintrect;
    const Rect *contact = NULL;

    static const char * const geometry_keys[] = {
	"LAYER",
	"WIDTH",
	"PATH",
	"RECT",
	"POLYGON",
	"VIA",
	"CLASS",
	"END",
	NULL
    };

    rectList = NULL;

    while ((token = LefNextToken(f, TRUE)) != NULL)
    {
	keyword = LookupFull(token, geometry_keys);
	if (keyword < 0)
	{
	    LefError(LEF_INFO, "Unknown keyword \"%s\" in LEF file; ignoring.\n",
		    token);
	    LefEndStatement(f);
	    continue;
	}
	switch (keyword)
	{
	    case LEF_LAYER:
		curlayer = LefReadLayers(f, (do_list) ? FALSE : TRUE, &otherlayer,
				&contact);
		LefEndStatement(f);
		break;
	    case LEF_WIDTH:
		LefEndStatement(f);
		break;
	    case LEF_PATH:
		LefEndStatement(f);
		break;
	    case LEF_RECT:
		paintrect = (curlayer < 0) ? NULL : LefReadRect(f, curlayer, oscale);
		if (paintrect)
		{
		    paintrect->r_xbot += gdsOffset->p_x;
		    paintrect->r_ybot += gdsOffset->p_y;
		    paintrect->r_xtop += gdsOffset->p_x;
		    paintrect->r_ytop += gdsOffset->p_y;

		    if (is_imported)
		    {
			int pNum = DBPlane(curlayer); /* FIXME unused return value from call to function with no side-effects */
			SearchContext scx;
			CellUse	dummy;
			PlaneType pt;
			Plane *checkplane = DBNewPlane((ClientData)0);

			scx.scx_use = &dummy;
			dummy.cu_def = lefMacro;
			dummy.cu_id = NULL;
			scx.scx_trans = GeoIdentityTransform;
			scx.scx_area = *paintrect;
			pt.pt_type = curlayer;
			pt.pt_plane = checkplane;

			/* Check if layout area is compatible with the port.	*/
			/* Check all paint in this area in all subcells.	*/

			DBTreeSrTiles(&scx, &DBConnectTbl[curlayer], 0,
				lefConnectFunc, (ClientData)&pt);

			if (DBSrPaintArea((Tile *)NULL, checkplane,
				paintrect, &DBNotConnectTbl[curlayer],
				lefUnconnectFunc, (ClientData)NULL) == 1)
			{
			    LefEndStatement(f);
			    TiFreePlane(checkplane);
			    break;
			}
			TiFreePlane(checkplane);
		    }

		    /* Paint the area, if a CellDef is defined */
		    if (lefMacro)
		    {
			// Cut layers defined as contacts use the contact
			// dimensions, not the dimension from the LEF file
			if (DBIsContact(curlayer) && contact && !(GEO_RECTNULL(contact)))
			{
			    paintrect->r_xbot = (paintrect->r_xbot + paintrect->r_xtop);
			    paintrect->r_ybot = (paintrect->r_ybot + paintrect->r_ytop);
			    paintrect->r_xtop = paintrect->r_xbot + contact->r_xtop;
			    paintrect->r_ytop = paintrect->r_ybot + contact->r_ytop;
			    paintrect->r_xbot += contact->r_xbot;
			    paintrect->r_ybot += contact->r_ybot;
			    paintrect->r_xbot >>= 1;
			    paintrect->r_ybot >>= 1;
			    paintrect->r_xtop >>= 1;
			    paintrect->r_ytop >>= 1;
			}
			DBPaint(lefMacro, paintrect, curlayer);

			if ((!do_list) && (otherlayer != -1))
				DBPaint(lefMacro, paintrect, otherlayer);
		    }

		    /* Remember the area and layer */
		    if (do_list)
		    {
			newRect = (LinkedRect *)mallocMagic(sizeof(LinkedRect));
			newRect->r_type = curlayer;
			newRect->r_r = *paintrect;
			newRect->r_next = rectList;
			rectList = newRect;
		    }
		}
		LefEndStatement(f);
		break;
	    case LEF_POLYGON:
		pointList = LefReadPolygon(f, curlayer, oscale, gdsOffset, &points);
		if (pointList)
		{
		    if (lefMacro)
		    {
			LinkedRect *rectNew, *rectTest;

			rectNew = LefPaintPolygon(lefMacro, pointList, points,
					curlayer, TRUE);

			// Add the new list of rectangles to the current list
			// of rectangles to be made into port labels.

			if (rectList != NULL)
			{
			    for (rectTest = rectList; rectTest && rectTest->r_next;
					rectTest = rectTest->r_next);
			    rectTest->r_next = rectNew;
			}
			else
			    rectList = rectNew;

			if ((!do_list) && (otherlayer != -1))
			    LefPaintPolygon(lefMacro, pointList, points, otherlayer, FALSE);
		    }
		    freeMagic(pointList);
		}
		break;
	    case LEF_VIA:
		LefEndStatement(f);
		break;
	    case LEF_PORT_CLASS:
		LefEndStatement(f);
		break;
	    case LEF_GEOMETRY_END:
		if (LefParseEndStatement(f, NULL) == 0)
		{
		    LefError(LEF_ERROR, "Geometry (PORT or OBS) END "
			    "statement missing.\n");
		    keyword = -1;
		}
		break;
	}
	if (keyword == LEF_GEOMETRY_END) break;
    }
    return rectList;
}

/*
 *------------------------------------------------------------
 * LefReadPort --
 *
 *	A wrapper for LefReadGeometry, which adds a label
 *	to the last rectangle generated by the geometry
 *	parsing.
 *
 * Results:
 *	None.
 *
 * Side Effects:
 *	Reads input from file f;
 *	Generates a new label entry in the CellDef lefMacro.
 *	If "lanno" is not NULL, then the label pointed to by
 *	lanno is modified.  If is_imported is TRUE, then only
 *	add a shape around lanno (which must be non-NULL).  If
 *	none of the geometry matches the position of the label,
 *	then move the label to the first area in the geometry
 *	list.
 *
 *------------------------------------------------------------
 */

void
LefReadPort(
    CellDef *lefMacro,
    FILE *f,
    char *pinName,
    int pinNum,
    int pinDir,
    int pinUse,
    int pinShape,
    float oscale,
    Point *gdsOffset,
    bool  is_imported,
    Label *lanno)
{
    Label *newlab;
    LinkedRect *rectList;

    rectList = LefReadGeometry(lefMacro, f, oscale, gdsOffset, TRUE, is_imported);

    free_magic1_t mm1 = freeMagic1_init();
    while (rectList != NULL)
    {
	if ((pinNum >= 0) || (lanno != NULL))
	{
	    /* Label this area */
	    if (lanno != NULL)
	    {
		/* Modify an existing label */
		lanno->lab_rect = rectList->r_r;
		lanno->lab_type = rectList->r_type;

		/* Pin number is not meaninful in LEF files, so keep	*/
		/* any existing pin number.  If original label was not	*/
		/* a port, then find a non-conflicting port number for	*/
		/* it.							*/

		if (lanno->lab_flags & PORT_DIR_MASK)
		    pinNum = lanno->lab_port;
		else
		{
		    Label *sl;
		    int idx;

		    pinNum = -1;
		    for (sl = lefMacro->cd_labels; sl != NULL; sl = sl->lab_next)
		    {
			if (sl->lab_flags & PORT_DIR_MASK)
			{
			    idx = sl->lab_port;
			    if (idx > pinNum) pinNum = idx;
			}
		    }
		    pinNum++;
		}
	    }
	    else
	    {
		/* If any other label is a port and has the same name,	*/
		/* then use its port number.				*/

		Label *sl;
		for (sl = lefMacro->cd_labels; sl != NULL; sl = sl->lab_next)
		{
		    if (sl->lab_flags & PORT_DIR_MASK)
			if (!strcmp(sl->lab_text, pinName))
			{
			    pinNum = sl->lab_port;
			    break;
			}
		}

		/* Create a new label (non-rendered) */
		DBPutLabel(lefMacro, &rectList->r_r, -1, pinName, rectList->r_type, 0, 0);
	    }

	    /* Set this label to be a port */

	    if (lefMacro->cd_labels == NULL)
		LefError(LEF_ERROR, "Internal error: No labels in cell!\n");
	    else
	    {
		newlab = (lanno != NULL) ? lanno : lefMacro->cd_lastLabel;
		if (strcmp(newlab->lab_text, pinName))
		    LefError(LEF_ERROR, "Internal error:  Can't find the label!\n");
		else
		{
		    /* Make this a port, and make it a sticky label so that */
		    /* it is guaranteed to be on the layer on which it is defined */
		    newlab->lab_flags = pinUse | pinDir | pinShape |
			    PORT_DIR_MASK | LABEL_STICKY;
		    newlab->lab_port = pinNum;
		}
	    }
	    /* If lanno is non-NULL then the first rectangle in the LEF	    */
	    /* port list is used to modify it.  All other LEF port geometry */
	    /* generates new labels in the CellDef.			    */
	    if (lanno != NULL) lanno = NULL;
	}

	freeMagic1(&mm1, (char *)rectList);
	rectList = rectList->r_next;
    }
    freeMagic1_end(&mm1);
}

/*
 *------------------------------------------------------------
 * LefReadPin --
 *
 *	Read a PIN statement from a LEF file.
 *
 * Results:
 *	None.
 *
 * Side Effects:
 *	Reads input from file f;
 *	Paints into the CellDef lefMacro.
 *
 *------------------------------------------------------------
 */

enum lef_pin_keys {LEF_DIRECTION = 0, LEF_USE, LEF_PORT, LEF_CAPACITANCE,
	LEF_ANTENNADIFF, LEF_ANTENNAGATE, LEF_ANTENNAMOD,
	LEF_ANTENNAPAR, LEF_ANTENNAPARSIDE, LEF_ANTENNAPARCUT,
	LEF_ANTENNAMAX, LEF_ANTENNAMAXSIDE,
	LEF_SHAPE, LEF_NETEXPR, LEF_PIN_END};

void
LefReadPin(
   CellDef *lefMacro,
   FILE *f,
   char *pinname,
   int pinNum,
   float oscale,
   Point *gdsOffset,
   bool is_imported)
{
    const char *token;
    char *testpin = pinname;
    int keyword, subkey;
    int pinDir = PORT_CLASS_DEFAULT;
    int pinUse = PORT_USE_DEFAULT;
    int pinShape = PORT_SHAPE_DEFAULT;
    Label *firstlab;
    bool firstport = TRUE;

    static const char * const pin_keys[] = {
	"DIRECTION",
	"USE",
	"PORT",
	"CAPACITANCE",
	"ANTENNADIFFAREA",
	"ANTENNAGATEAREA",
	"ANTENNAMODEL",
	"ANTENNAPARTIALMETALAREA",
	"ANTENNAPARTIALMETALSIDEAREA",
	"ANTENNAPARTIALCUTAREA",
	"ANTENNAMAXAREACAR",
	"ANTENNAMAXSIDEAREACAR",
	"SHAPE",
	"NETEXPR",
	"END",
	NULL
    };

    static const char * const pin_classes[] = {
	"DEFAULT",
	"INPUT",
	"OUTPUT",
	"OUTPUT TRISTATE",
	"INOUT",
	"FEEDTHRU",
	NULL
    };

    static const int lef_class_to_bitmask[] = {
	PORT_CLASS_DEFAULT,
	PORT_CLASS_INPUT,
	PORT_CLASS_OUTPUT,
	PORT_CLASS_TRISTATE,
	PORT_CLASS_BIDIRECTIONAL,
	PORT_CLASS_FEEDTHROUGH
    };

    static const char * const pin_uses[] = {
	"DEFAULT",
	"SIGNAL",
	"ANALOG",
	"POWER",
	"GROUND",
	"CLOCK",
	NULL
    };

    static const int lef_use_to_bitmask[] = {
	PORT_USE_DEFAULT,
	PORT_USE_SIGNAL,
	PORT_USE_ANALOG,
	PORT_USE_POWER,
	PORT_USE_GROUND,
	PORT_USE_CLOCK
    };

    static const char * const pin_shapes[] = {
	"DEFAULT",
	"ABUTMENT",
	"RING",
	"FEEDTHRU",
	NULL
    };

    static const int lef_shape_to_bitmask[] = {
	PORT_SHAPE_DEFAULT,
	PORT_SHAPE_ABUT,
	PORT_SHAPE_RING,
	PORT_SHAPE_THRU
    };


    /* If LEF file is being imported to annotate an existing layout, then
     * find the first label in the layout that matches the PIN record.
     * If there are no matches, then test for various issues with
     * delimiter translation and case sensitivity.
     */

    if (is_imported)
    {
	/* Find the first matching label */

	for (firstlab = lefMacro->cd_labels; firstlab; firstlab = firstlab->lab_next)
	    if (!strcmp(firstlab->lab_text, testpin))
		break;

	/* If no match was found, look for common issues    */
	/* such as bus delimiter translations or case	    */
	/* sensitivity.					    */

	if (firstlab == NULL)
	{
	    char *delim;

	    testpin = (char *)mallocMagic(strlen(pinname) + 1);
	    strcpy(testpin, pinname);

	    if ((delim = strchr(testpin, '<')) != NULL)
	    {
		*delim = '[';
		if ((delim = strchr(testpin, '>')) != NULL)
		    *delim = ']';
	    }
	    else if ((delim = strchr(testpin, '[')) != NULL)
	    {
		char *delim2;
		*delim = '<';
		if ((delim2 = strchr(testpin, ']')) != NULL)
		    *delim2 = '>';
	    }
	    for (firstlab = lefMacro->cd_labels; firstlab; firstlab = firstlab->lab_next)
		if (!strcmp(firstlab->lab_text, testpin))
		    break;

	    if (firstlab == NULL)    /* No luck, so return to original */
	    {
		freeMagic(testpin);
		testpin = pinname;
	    }
	}

	if (firstlab == NULL)
	{
	    for (firstlab = lefMacro->cd_labels; firstlab; firstlab = firstlab->lab_next)
		if (!strcasecmp(firstlab->lab_text, testpin))
		    break;

	    if (firstlab != NULL)    /* Case matching succeeded */
	    {
		if (testpin == pinname)
		    testpin = (char *)mallocMagic(strlen(pinname) + 1);
		strcpy(testpin, firstlab->lab_text);
	    }
	}

	if (firstlab == NULL) firstlab = lefMacro->cd_labels;
    }

    while ((token = LefNextToken(f, TRUE)) != NULL)
    {
	keyword = LookupFull(token, pin_keys);
	if (keyword < 0)
	{
	    LefError(LEF_INFO, "Unknown keyword \"%s\" in LEF file; ignoring.\n",
		    token);
	    LefEndStatement(f);
	    continue;
	}
	switch (keyword)
	{
	    case LEF_DIRECTION:
		token = LefNextToken(f, TRUE);
		subkey = LookupFull(token, pin_classes);
		if (subkey < 0)
		    LefError(LEF_ERROR, "Improper DIRECTION statement\n");
		else
		    pinDir = lef_class_to_bitmask[subkey];
		LefEndStatement(f);
		break;
	    case LEF_USE:
		token = LefNextToken(f, TRUE);
		subkey = LookupFull(token, pin_uses);
		if (subkey < 0)
		    LefError(LEF_ERROR, "Improper USE statement\n");
		else
		    pinUse = lef_use_to_bitmask[subkey];
		LefEndStatement(f);
		break;
	    case LEF_SHAPE:
		token = LefNextToken(f, TRUE);
		subkey = LookupFull(token, pin_shapes);
		if (subkey < 0)
		    LefError(LEF_ERROR, "Improper SHAPE statement\n");
		else
		    pinShape = lef_shape_to_bitmask[subkey];
		LefEndStatement(f);
		break;
	    case LEF_PORT:
		if (is_imported)
		{
		    bool needRect = TRUE;
		    bool hasPort = FALSE;
		    Label *lab;

		    /* Conflicting interests: One purpose of annotation */
		    /* is to make ports where none existed.  But if the	*/
		    /* cell has both port and non-port labels with the	*/
		    /* same string, then don't mess with the non-port	*/
		    /* label.						*/

		    for (lab = firstlab; lab; lab = lab->lab_next)
			if (lab->lab_flags & PORT_DIR_MASK)
			    if (!strcmp(lab->lab_text, testpin))
			    {
				hasPort = TRUE;
				break;
			    }

		    /* Skip the port geometry but find the pin name and	*/
		    /* annotate with the use and direction.  Note that	*/
		    /* there may be multiple instances of the label.	*/
		    /* However, if the label is a point label, then	*/
		    /* replace it with the geometry from the LEF file.	*/

		    if (hasPort == FALSE) lab = firstlab;
		    for (; lab; lab = lab->lab_next)
		    {
			if (!strcmp(lab->lab_text, testpin))
			{
			    /* If there is at least one port label with this	*/
			    /* name, then ignore all non-port labels with the	*/
			    /* same name.					*/
			    if ((hasPort) && (!(lab->lab_flags & PORT_DIR_MASK)))
				break;
			    else if (GEO_RECTNULL(&lab->lab_rect))
				break;
			    else
			    {
				if (lab->lab_flags & PORT_DIR_MASK)
				    pinNum = lab->lab_port;
				else
				{
				    Label *sl;
				    int idx;

				    pinNum = -1;
				    for (sl = lefMacro->cd_labels; sl != NULL;
						sl = sl->lab_next)
				    {
					if (sl->lab_flags & PORT_DIR_MASK)
					{
					    idx = sl->lab_port;
					    if (idx > pinNum) pinNum = idx;
					}
				    }
				    pinNum++;
				}
				needRect = FALSE;
				lab->lab_flags &= ~(PORT_USE_MASK | PORT_DIR_MASK |
					PORT_CLASS_MASK | PORT_SHAPE_MASK);
				lab->lab_flags |= pinUse | pinDir | pinShape |
					PORT_DIR_MASK;
				lab->lab_port = pinNum;
			    }
			}
		    }
		    firstlab = (lab == NULL) ? NULL : lab->lab_next;

		    if (needRect)
		    {
			if ((lab == NULL) && (firstport == TRUE))
			    DBEraseLabelsByContent(lefMacro, NULL, -1, testpin);

			LefReadPort(lefMacro, f, testpin, pinNum, pinDir, pinUse,
				pinShape, oscale, gdsOffset, TRUE, lab);
		    }
		    else
			LefSkipSection(f, NULL);
		    firstport = FALSE;
		}
		else
		    LefReadPort(lefMacro, f, testpin, pinNum, pinDir, pinUse,
			    pinShape, oscale, gdsOffset, FALSE, NULL);
		break;
	    case LEF_CAPACITANCE:
	    case LEF_ANTENNADIFF:
	    case LEF_ANTENNAGATE:
	    case LEF_ANTENNAMOD:
	    case LEF_ANTENNAPAR:
	    case LEF_ANTENNAPARSIDE:
	    case LEF_ANTENNAPARCUT:
	    case LEF_ANTENNAMAX:
	    case LEF_ANTENNAMAXSIDE:
	    case LEF_NETEXPR:
		LefEndStatement(f);	/* Ignore. . . */
		break;
	    case LEF_PIN_END:
		if (LefParseEndStatement(f, pinname) == 0)
		{
		    LefError(LEF_ERROR, "Pin END statement missing.\n");
		    keyword = -1;
		}
		break;
	}
	if (keyword == LEF_PIN_END) break;
    }
    if (testpin != pinname) freeMagic(testpin);
}

/*
 *------------------------------------------------------------
 * LefEndStatement --
 *
 *	Read file input to EOF or a ';' token (end-of-statement)
 *
 *------------------------------------------------------------
 */

void
LefEndStatement(
    FILE *f)
{
    const char *token;

    while ((token = LefNextToken(f, TRUE)) != NULL)
	if (*token == ';') break;
}

/*
 *------------------------------------------------------------
 *
 * LefReadNonDefaultRule --
 *
 *	Read in a NONDEFAULTRULE section from a LEF file.
 *
 * Results:
 *	None.
 *
 * Side Effects:
 *	Creates a new nondefaultrule in the associated hash
 *	table.
 *
 *------------------------------------------------------------
 */

enum lef_nondefprop_keys {
	LEF_NONDEFPROP_HARDSPACING = 0, LEF_NONDEFPROP_LAYER,
	LEF_NONDEFPROP_END, LEF_NONDEFPROP_VIA,
	LEF_NONDEFPROP_USEVIA, LEF_NONDEFPROP_USEVIARULE,
	LEF_NONDEFPROP_MINCUTS, LEF_NONDEFPROP_PROPERTY,
	LEF_NONDEFLAYER_WIDTH, LEF_NONDEFLAYER_DIAG,
	LEF_NONDEFLAYER_SPACE, LEF_NONDEFLAYER_EXT};

void
LefReadNonDefaultRule(
    FILE *f,			/* LEF file being read	*/
    char *rname,		/* name of the rule 	*/
    float oscale)		/* scale factor um->magic units */
{
    const char *token;
    char tsave[128];
    int keyword;
    HashEntry *he;
    LefRules *ruleset = NULL;
    lefRule *rule = NULL;
    lefLayer *lefl;
    float fvalue;
    char *newname = rname;
    int idx;
    bool inlayer, done;

    static const char * const nondef_property_keys[] = {
	"HARDSPACING",
	"LAYER",
	"END",
	"VIA",
	"USEVIA",
	"USEVIARULE",
	"MINCUTS",
	"PROPERTY",
	"WIDTH",
	"DIAGWIDTH",
	"SPACING",
	"WIREEXTENSION",
	NULL
    };

    /* Start by creating a new nondefault rule */

    he = HashFind(&LefNonDefaultRules, rname);
    idx = 0;
    if (HashGetValue(he) != NULL)
    {
	LefError(LEF_WARNING, "Nondefault rule \"%s\" was already defined.\n", rname);
    	newname = (char *)mallocMagic(strlen(rname) + 5);
    }
    while (HashGetValue(he))
    {
	sprintf(newname, "%s_%d", rname, idx);
	LefError(LEF_WARNING, "Renaming this rule \"%s\"\n", newname);
        he = HashFind(&LefNonDefaultRules, newname);
    }
    ruleset = (LefRules *)mallocMagic(sizeof(LefRules));
    HashSetValue(he, ruleset);
    ruleset->name = StrDup((char **)NULL, newname);
    ruleset->rule = NULL;

    done = FALSE;
    inlayer = FALSE;
    while ((token = LefNextToken(f, TRUE)) != NULL)
    {
	keyword = LookupFull(token, nondef_property_keys);
	if (keyword < 0)
	{
	    LefError(LEF_INFO, "Unknown keyword \"%s\" in NONDEFAULTRULES "
			"definition; ignoring.\n", token);
	    LefEndStatement(f);
	    continue;
	}

	switch (keyword)
	{
	    case LEF_NONDEFPROP_END:
		token = LefNextToken(f, TRUE);
		if (inlayer)
		    inlayer = FALSE;
		else
		    done = TRUE;
	    case LEF_NONDEFPROP_HARDSPACING:
		lefl = NULL;
		/* Ignore this */
		break;
	    case LEF_NONDEFPROP_VIA:
		/* Read in the via definition */
		token = LefNextToken(f, TRUE);
		sprintf(tsave, "%.127s", token);
		he = HashFind(&LefInfo, token);
		lefl = (lefLayer *)HashGetValue(he);
		if (lefl == NULL)
		{
		    lefl = (lefLayer *)mallocMagic(sizeof(lefLayer));
		    lefl->type = -1;
		    lefl->obsType = -1;
		    lefl->refCnt = 1;
		    lefl->lefClass = CLASS_VIA;
		    lefl->info.via.area = GeoNullRect;
		    lefl->info.via.cell = (CellDef *)NULL;
		    lefl->info.via.lr = (LinkedRect *)NULL;
		    HashSetValue(he, lefl);
		    LefReadLayerSection(f, tsave, keyword, lefl);
		    lefl->canonName = (const char *)he->h_key.h_name;
		}
		goto newrule;

	    case LEF_NONDEFPROP_USEVIA:
	    case LEF_NONDEFPROP_USEVIARULE:
		lefl = NULL;
		/* Fall through---vias and viarules are treated like layers */

	    case LEF_NONDEFPROP_LAYER:
		token = LefNextToken(f, TRUE);
		he = HashFind(&LefInfo, token);
		lefl = (lefLayer *)HashGetValue(he);
newrule:
		if (ruleset)
		{
		    /* Chain new layer rule to linked list */
		    rule = (lefRule *)mallocMagic(sizeof(lefRule));
		    rule->lefInfo = lefl;
		    rule->width = 0;
		    rule->spacing = 0;
		    rule->extend = 0;
		    rule->next = ruleset->rule;
		    ruleset->rule = rule;
		}
		else
		    LefError(LEF_INFO, "No non-default rule name for \"%s\" "
				"in NONDEFAULTRULE definition!.\n", token);
		if (keyword == LEF_NONDEFPROP_LAYER) inlayer = TRUE;
		break;

	    case LEF_NONDEFPROP_MINCUTS:
	    case LEF_NONDEFPROP_PROPERTY:
		lefl = NULL;
		/* Ignore the next two tokens */
		token = LefNextToken(f, TRUE);
		token = LefNextToken(f, TRUE);
		break;
	    case LEF_NONDEFLAYER_WIDTH:
		if (!inlayer)
	 	    LefError(LEF_INFO, "WIDTH specified without layer.\n");
		token = LefNextToken(f, TRUE);
		sscanf(token, "%f", &fvalue);
		if (rule == NULL)
		    LefError(LEF_INFO, "No rule for non-default width.\n");
		else if (lefl == NULL)
		    LefError(LEF_INFO, "No layer for non-default width.\n");
		else
		    rule->width = (int)roundf(fvalue / oscale);
	    	LefEndStatement(f);
		break;
	    case LEF_NONDEFLAYER_SPACE:
		if (!inlayer)
		    LefError(DEF_INFO, "SPACING specified without layer.\n");
		token = LefNextToken(f, TRUE);
		sscanf(token, "%f", &fvalue);
		if (rule == NULL)
		    LefError(LEF_INFO, "No rule for non-default spacing.\n");
		else if (lefl == NULL)
		    LefError(LEF_INFO, "No layer for non-default spacing.\n");
		else
		    rule->spacing = (int)roundf(fvalue / oscale);
	    	LefEndStatement(f);
		break;
	    case LEF_NONDEFLAYER_EXT:
		if (!inlayer)
		    LefError(DEF_INFO, "WIREEXT specified without layer.\n");
		token = LefNextToken(f, TRUE);
		sscanf(token, "%f", &fvalue);
		if (rule == NULL)
		    LefError(LEF_INFO, "No rule for non-default extension.\n");
		else if (lefl == NULL)
		    LefError(LEF_INFO, "No layer for non-default extension.\n");
		else
		    rule->extend = (int)roundf((2 * fvalue) / oscale);
	    	LefEndStatement(f);
		break;
	    case LEF_NONDEFLAYER_DIAG:
		if (!inlayer)
		    LefError(LEF_INFO,
				"Layer value specified without layer.\n");
		/* Absorb token and ignore */
		token = LefNextToken(f, TRUE);
	    	LefEndStatement(f);
		break;
	}

	if (done) break;
    }
 
    if (newname != rname) freeMagic(newname);
}

/*
 *------------------------------------------------------------
 *
 * LefReadMacro --
 *
 *	Read in a MACRO section from a LEF file.
 *
 * Results:
 *	None.
 *
 * Side Effects:
 *	Creates a new cell definition in the magic database.
 *
 *------------------------------------------------------------
 */

enum lef_macro_keys {LEF_CLASS = 0, LEF_SIZE, LEF_ORIGIN,
	LEF_SYMMETRY, LEF_SOURCE, LEF_SITE, LEF_PIN, LEF_OBS,
	LEF_TIMING, LEF_FOREIGN, LEF_PROPERTY, LEF_MACRO_END};

void
LefReadMacro(
    FILE *f,			/* LEF file being read	*/
    char *mname,		/* name of the macro 	*/
    float oscale,		/* scale factor um->magic units */
    bool importForeign,		/* Whether we should try to read
				 * in a cell.
				 */
    bool doAnnotate,		/* If true, ignore all macros that are
				 * not already CellDefs.
				 */
    int lefTimestamp)		/* If not -1, use the value pointed to
				 * as the CellDef's timestamp.
				 */
{
    CellDef *lefMacro;
    HashEntry *he;

    const char *token;
    char tsave[128], *propval;
    int keyword, pinNum, propsize, result;
    float x, y;
    bool has_size, is_imported = FALSE, propfound;
    PropertyRecord *proprec;
    Rect lefBBox;
    Point gdsOffset;	/* Difference between GDS and LEF coordinates */

    static const char * const macro_keys[] = {
	"CLASS",
	"SIZE",
	"ORIGIN",
	"SYMMETRY",
	"SOURCE",
	"SITE",
	"PIN",
	"OBS",
	"TIMING",
	"FOREIGN",
	"PROPERTY",
	"END",
	NULL
    };

    /* Start by creating a new celldef */

    he = HashFind(&lefDefInitHash, mname);
    if (HashGetValue(he))
    {
	int suffix;
	char newname[256];

	for (suffix = 1; HashGetValue(he) != NULL; suffix++)
	{
	    snprintf(newname, 255, "%s_%d", mname, suffix);
	    he = HashFind(&lefDefInitHash, newname);
	}
	LefError(LEF_WARNING, "Cell \"%s\" was already defined in this file.  "
		"Renaming this cell \"%s\"\n", mname, newname);
        lefMacro = DBCellLookDef(newname);
        if (lefMacro == NULL)
	{
	    if (doAnnotate)
	    {
		/* Ignore any macro that does not correspond to an existing cell */
		LefSkipSection(f, "MACRO");
		return;
	    }
	    lefMacro = lefFindCell(newname);
	    DBCellClearDef(lefMacro);
	    DBCellSetAvail(lefMacro);
	    HashSetValue(he, lefMacro);
	    is_imported = FALSE;
	}
	else
	    is_imported = TRUE;
    }
    else
    {
        lefMacro = DBCellLookDef(mname);
	if (lefMacro == NULL)
	{
	    lefMacro = lefFindCell(mname);
	    DBCellClearDef(lefMacro);
	    DBCellSetAvail(lefMacro);
	    HashSetValue(he, lefMacro);
	    is_imported = FALSE;
	}
	else
	    is_imported = TRUE;
    }
    if (lefTimestamp != -1)
    {
	lefMacro->cd_timestamp = lefTimestamp;
	lefMacro->cd_flags |= CDFIXEDSTAMP;
    }

    /* Initial values */
    pinNum = 1;
    has_size = FALSE;
    lefBBox.r_xbot = 0;
    lefBBox.r_ybot = 0;
    gdsOffset.p_x = 0;
    gdsOffset.p_y = 0;

    while ((token = LefNextToken(f, TRUE)) != NULL)
    {
	keyword = LookupFull(token, macro_keys);
	if (keyword < 0)
	{
	    LefError(LEF_INFO, "Unknown keyword \"%s\" in LEF file; ignoring.\n",
		    token);
	    LefEndStatement(f);
	    continue;
	}
	switch (keyword)
	{
	    case LEF_CLASS:
		strcpy(tsave, "");
		token = LefNextToken(f, TRUE);
		while (*token != ';')
		{
		    sprintf(tsave + strlen(tsave), " %s", token);
		    token = LefNextToken(f, TRUE);
		}
		proprec = (PropertyRecord *)mallocMagic(sizeof(PropertyRecord) +
				strlen(tsave + 1) - 7);
		proprec->prop_type = PROPERTY_TYPE_STRING;
		proprec->prop_len = strlen(tsave + 1);
		strcpy(proprec->prop_value.prop_string, tsave + 1);
		DBPropPut(lefMacro, "LEFclass", proprec);
		break;
	    case LEF_SIZE:
		token = LefNextToken(f, TRUE);
		if (!token || sscanf(token, "%f", &x) != 1) goto size_error;
		token = LefNextToken(f, TRUE);		/* skip keyword "BY" */
		if (!token) goto size_error;
		token = LefNextToken(f, TRUE);
		if (!token || sscanf(token, "%f", &y) != 1) goto size_error;

		lefBBox.r_xtop = (int)roundf(x / oscale) + lefBBox.r_xbot;
		lefBBox.r_ytop = (int)roundf(y / oscale) + lefBBox.r_ybot;
		has_size = TRUE;
		LefEndStatement(f);
		break;
size_error:
		LefError(LEF_ERROR, "Bad macro SIZE; requires values X BY Y.\n");
		LefEndStatement(f);
		break;
	    case LEF_ORIGIN:
		if (LefReadLefPoint(f, &x, &y) != 0) goto origin_error;
		lefBBox.r_xbot = -(int)roundf(x / oscale);
		lefBBox.r_ybot = -(int)roundf(y / oscale);
		if (has_size)
		{
		    lefBBox.r_xtop += lefBBox.r_xbot;
		    lefBBox.r_ytop += lefBBox.r_ybot;
		}
		gdsOffset.p_x += lefBBox.r_xbot;
		gdsOffset.p_y += lefBBox.r_ybot;
		LefEndStatement(f);
		break;
origin_error:
		LefError(LEF_ERROR, "Bad macro ORIGIN; requires 2 values.\n");
		LefEndStatement(f);
		break;
	    case LEF_SYMMETRY:
		strcpy(tsave, "");
		token = LefNextToken(f, TRUE);
		while (*token != ';')
		{
		    sprintf(tsave + strlen(tsave), " %s", token);
		    token = LefNextToken(f, TRUE);
		}
		proprec = (PropertyRecord *)mallocMagic(sizeof(PropertyRecord) +
				strlen(tsave + 1) - 7);
		proprec->prop_type = PROPERTY_TYPE_STRING;
		proprec->prop_len = strlen(tsave + 1);
		strcpy(proprec->prop_value.prop_string, tsave + 1);
		DBPropPut(lefMacro, "LEFsymmetry", proprec);
		break;
	    case LEF_SOURCE:
		token = LefNextToken(f, TRUE);
		/* Ignore "SOURCE" as it is deprecated from LEF 5.6, and    */
		/* magic will write LEF version 5.7.			    */
		LefEndStatement(f);
		break;
	    case LEF_SITE:
		token = LefNextToken(f, TRUE);
		if (*token != '\n')
		{
		    proprec = (PropertyRecord *)mallocMagic(sizeof(PropertyRecord) +
				strlen(token) - 7);
		    proprec->prop_type = PROPERTY_TYPE_STRING;
		    proprec->prop_len = strlen(token);
		    strcpy(proprec->prop_value.prop_string, token);
		    DBPropPut(lefMacro, "LEFsite", proprec);
		}
		LefEndStatement(f);
		break;
	    case LEF_PROPERTY:
		/* Append property key:value pairs to the cell property LEFproperties */
		propval = DBPropGetString(lefMacro, "LEFproperties", &propfound);
		if (propfound)
		    propsize = strlen(propval);
		else
		    propsize = 0;

		token = LefNextToken(f, TRUE);
		if (*token != '\n')
		{
		    char *propext;
		    sprintf(tsave, "%.127s", token);
		    token = LefNextToken(f, TRUE);
		    
		    proprec = (PropertyRecord *)mallocMagic(sizeof(PropertyRecord) +
				propsize + strlen(tsave) + strlen(token) - 3);
		    proprec->prop_type = PROPERTY_TYPE_STRING;
		    proprec->prop_len = propsize + strlen(tsave) + strlen(token) + 4;

		    if (propsize > 0)
			sprintf(proprec->prop_value.prop_string, "%s %s %s",
					propval, tsave, token);
		    else
			sprintf(proprec->prop_value.prop_string, "%s %s", tsave, token);

		    DBPropPut(lefMacro, "LEFproperties", proprec);
		}
		LefEndStatement(f);
		break;
	    case LEF_PIN:
		token = LefNextToken(f, TRUE);
		/* Diagnostic */
		/*
		TxPrintf("   Macro defines pin %s\n", token);
		*/
		sprintf(tsave, "%.127s", token);
		LefReadPin(lefMacro, f, tsave, pinNum++, oscale, &gdsOffset, is_imported);
		break;
	    case LEF_OBS:
		/* Diagnostic */
		/*
		TxPrintf("   Macro defines obstruction\n");
		*/
		if (is_imported)
		    LefSkipSection(f, NULL);
		else
		    LefReadGeometry(lefMacro, f, oscale, &gdsOffset, FALSE, is_imported);
		break;
	    case LEF_TIMING:
		LefSkipSection(f, macro_keys[LEF_TIMING]);
		break;
	    case LEF_FOREIGN:
		token = LefNextToken(f, TRUE);
		sprintf(tsave, "%.127s", token);

		/* Read (optional) FOREIGN coordinate */
		result = LefReadLefPoint(f, &x, &y);
		if (result == 1) goto foreign_error;
		else if (result == 0)
		{
		    gdsOffset.p_x += -(int)roundf(x / oscale);
		    gdsOffset.p_y += -(int)roundf(y / oscale);
		}
		if (importForeign)
		{
		    /* There is no behavioral difference when using
		     * importForiegn.
		     */
		}
		if (result != -1) LefEndStatement(f);
		break;
foreign_error:
		LefError(LEF_ERROR, "Bad origin in macro FOREIGN; requires "
				"0 or 2 values.\n");
		LefEndStatement(f);
		break;
	    case LEF_MACRO_END:
		if (LefParseEndStatement(f, mname) == 0)
		{
		    LefError(LEF_ERROR, "Macro END statement missing.\n");
		    keyword = -1;
		}
		break;
	}
	if (keyword == LEF_MACRO_END) break;
    }

    /* Finish up creating the cell */
    lefBBox.r_xbot -= gdsOffset.p_x;
    lefBBox.r_ybot -= gdsOffset.p_y;
    lefBBox.r_xtop -= gdsOffset.p_x;
    lefBBox.r_ytop -= gdsOffset.p_y;
    DBReComputeBbox(lefMacro);

    if (is_imported)
    {
	/* Define the FIXED_BBOX property to match the LEF macro */

	if (has_size)
	{
	    lefMacro->cd_flags |= CDFIXEDBBOX;
	    proprec = (PropertyRecord *)mallocMagic(sizeof(PropertyRecord) +
			(2 * sizeof(int)));
	    proprec->prop_type = PROPERTY_TYPE_DIMENSION;
	    proprec->prop_len = 4;
	    proprec->prop_value.prop_integer[0] = lefBBox.r_xbot;
	    proprec->prop_value.prop_integer[1] = lefBBox.r_ybot;
	    proprec->prop_value.prop_integer[2] = lefBBox.r_xtop;
	    proprec->prop_value.prop_integer[3] = lefBBox.r_ytop;

	    DBPropPut(lefMacro, "FIXED_BBOX", proprec);
	}
    }
    else
    {
	DBAdjustLabelsNew(lefMacro, &TiPlaneRect);

	if (has_size)
	{
	    lefMacro->cd_flags |= CDFIXEDBBOX;
	    proprec = (PropertyRecord *)mallocMagic(sizeof(PropertyRecord) +
			(2 * sizeof(int)));
	    proprec->prop_type = PROPERTY_TYPE_DIMENSION;
	    proprec->prop_len = 4;
	    proprec->prop_value.prop_integer[0] = lefBBox.r_xbot;
	    proprec->prop_value.prop_integer[1] = lefBBox.r_ybot;
	    proprec->prop_value.prop_integer[2] = lefBBox.r_xtop;
	    proprec->prop_value.prop_integer[3] = lefBBox.r_ytop;

	    DBPropPut(lefMacro, "FIXED_BBOX", proprec);
	}
	else
	{
	    LefError(LEF_WARNING, "   Macro does not define size:  "
		    "computing from geometry\n");

	    /* Set the placement bounding box property to the current bounding box */
	    lefMacro->cd_flags |= CDFIXEDBBOX;
	    lefMacro->cd_flags |= CDFIXEDBBOX;
	    proprec = (PropertyRecord *)mallocMagic(sizeof(PropertyRecord) +
			(2 * sizeof(int)));
	    proprec->prop_type = PROPERTY_TYPE_DIMENSION;
	    proprec->prop_len = 4;
	    proprec->prop_value.prop_integer[0] = lefMacro->cd_bbox.r_xbot;
	    proprec->prop_value.prop_integer[1] = lefMacro->cd_bbox.r_ybot;
	    proprec->prop_value.prop_integer[2] = lefMacro->cd_bbox.r_xtop;
	    proprec->prop_value.prop_integer[3] = lefMacro->cd_bbox.r_ytop;

	    DBPropPut(lefMacro, "FIXED_BBOX", proprec);
	    DRCCheckThis(lefMacro, TT_CHECKPAINT, &lefMacro->cd_bbox);
	}
    }

    /* Note:  When the LEF view is used to annotate an		*/
    /* existing GDS view, then the view is not considered	*/
    /* "abstract";  otherwise, writing GDS output gets very	*/
    /* complicated and inefficient.				*/

    /* Note:  The value here is ignored, setting to "TRUE".	*/
    /* The "extract" command only cares that the key exists.	*/
    /* i.e., setting it to "FALSE" would be ineffective.	*/

    if (!is_imported)
    {
	proprec = (PropertyRecord *)mallocMagic(sizeof(PropertyRecord));
	proprec->prop_type = PROPERTY_TYPE_STRING;
	proprec->prop_len = 4;
	strcpy(proprec->prop_value.prop_string, "TRUE");
	DBPropPut(lefMacro, "LEFview", proprec);
    }

    DBWAreaChanged(lefMacro, &lefMacro->cd_bbox, DBW_ALLWINDOWS,
		&DBAllButSpaceBits);
}

/*
 *------------------------------------------------------------
 *
 * LefGrowVia ---
 *
 * For LEF contact types matching magic contact types, size the
 * LEF contact cut to cover the minimum	rectangle in the other
 * layers that satisfies the CIF/GDS contact generation.
 *
 * NOTE:  If a "cifinput" style is defined, then the via
 * remains the size declared in LEF or DEF file, and the
 * magic view of the via is generated by applying "cifinput"
 * rules when painting into the magic database.  If no input
 * style is defined, then the output style rules are used to
 * modify the cut size to match the way the via is defined in
 * magic, and the result is painted directly.
 *
 * If a "cifinput" style exists, then this routine does
 * nothing and has no side effects.
 *
 *------------------------------------------------------------
 */

void LefGrowVia(curlayer, currect, lefl)
    TileType curlayer;
    Rect *currect;
    lefLayer *lefl;
{
    /* To be completed:  This should be deprecated by moving the entire	*/
    /* LEF and DEF read routines to use the cifinput style.		*/

    if (DBIsContact(curlayer) && CIFCurStyle != NULL)
    {
	int edgeSize = 0, contSize, halfSize;

	/* Get the minimum size of a contact (cut + borders) from cifoutput */
	contSize = CIFGetContactSize(curlayer, &edgeSize, NULL, NULL);

	/* All internal LEF via geometry values are doubled */
	contSize <<= 1;
	edgeSize <<= 1;

        if (contSize % CIFCurStyle->cs_scaleFactor == 0)
	   contSize /= CIFCurStyle->cs_scaleFactor;
	else
	   contSize = contSize / CIFCurStyle->cs_scaleFactor + 1;

        if (edgeSize % CIFCurStyle->cs_scaleFactor == 0)
	   edgeSize /= CIFCurStyle->cs_scaleFactor;
	else
	   edgeSize = edgeSize / CIFCurStyle->cs_scaleFactor + 1;

	if (edgeSize > 0 && contSize > 0)
	{
	    /* Flag a warning if the cut size is different from what's expected */
	    if ((currect->r_xtop - currect->r_xbot != edgeSize) ||
			(currect->r_ytop - currect->r_ybot != edgeSize))
	    {
		LefError(LEF_WARNING, "Cut size for magic type \"%s\" (%d x %d) does "
			"not match LEF/DEF\n",
			DBTypeLongNameTbl[lefl->type],
			edgeSize, edgeSize);
		LefError(LEF_WARNING, "Via cut size (%d x %d).  Magic layer "
			"cut size will be used!\n",
			currect->r_xtop - currect->r_xbot,
			currect->r_ytop - currect->r_ybot);
	    }

	    halfSize = contSize >> 1;
	    currect->r_xbot = ((currect->r_xbot + currect->r_xtop) / 2) - halfSize;
	    currect->r_ybot = ((currect->r_ybot + currect->r_ytop) / 2) - halfSize;
	    currect->r_xtop = currect->r_xbot + contSize;
	    currect->r_ytop = currect->r_ybot + contSize;
	}
    }
}

/*
 *------------------------------------------------------------
 *
 * LefGenViaGeometry --
 *
 *	Create geometry for a VIA section from a DEF file
 *	using via generation parameters.
 *
 * Results:
 *	None.
 *
 * Side Effects:
 *	Adds to the lefLayer record for a via definition.
 *
 *------------------------------------------------------------
 */

void
LefGenViaGeometry(
    FILE *f,			/* LEF file being read	*/
    lefLayer *lefl,		/* pointer to via info	*/
    int sizex,			/* cut size */
    int sizey,
    int spacex,			/* cut spacing */
    int spacey,
    int encbx,			/* bottom enclosure of cuts */
    int encby,
    int enctx,			/* top enclosure of cuts */
    int encty,
    int rows,			/* number of cut rows and columns */
    int cols,
    TileType tlayer,		/* Top layer type */
    TileType clayer,		/* Cut layer type */
    TileType blayer,		/* Bottom layer type */
    float oscale)		/* output scaling	*/
{
    Rect rect;
    int i, j, x, y, w, h;
    LinkedRect *viaLR;
    float hscale = oscale / 2;

    /* Compute top layer rect */

    w = (sizex * cols) + (spacex * (cols - 1)) + 2 * enctx;
    h = (sizey * rows) + (spacey * (rows - 1)) + 2 * encty;

    rect.r_xtop = (int)roundf(w / oscale);
    rect.r_xbot = -rect.r_xtop;
    rect.r_ytop = (int)roundf(h / oscale);
    rect.r_ybot = -rect.r_ytop;

    /* Set via area to the top layer */
    lefl->info.via.area = rect;
    lefl->type = tlayer;

    /* Compute bottom layer rect */

    w = (sizex * cols) + (spacex * (cols - 1)) + 2 * encbx;
    h = (sizey * rows) + (spacey * (rows - 1)) + 2 * encby;

    rect.r_xtop = (int)roundf(w / oscale);
    rect.r_xbot = -rect.r_xtop;
    rect.r_ytop = (int)roundf(h / oscale);
    rect.r_ybot = -rect.r_ytop;

    viaLR = (LinkedRect *)mallocMagic(sizeof(LinkedRect));
    viaLR->r_next = lefl->info.via.lr;
    lefl->info.via.lr = viaLR;
    viaLR->r_type = blayer;
    viaLR->r_r = rect;

    w = (sizex * cols) + (spacex * (cols - 1));
    h = (sizey * rows) + (spacey * (rows - 1));
    x = -w / 2;
    y = -h / 2;

    for (i = 0; i < cols; i++)
    {
	for (j = 0; j < rows; j++)
	{
	    rect.r_xbot = (int)roundf(x / hscale);
	    rect.r_ybot = (int)roundf(y / hscale);
	    rect.r_xtop = rect.r_xbot + (int)roundf(sizex / hscale);
	    rect.r_ytop = rect.r_ybot + (int)roundf(sizey / hscale);

	    /* Expand via to the size used by magic */
	    LefGrowVia(clayer, &rect, lefl);

	    viaLR = (LinkedRect *)mallocMagic(sizeof(LinkedRect));
	    viaLR->r_next = lefl->info.via.lr;
	    lefl->info.via.lr = viaLR;
	    viaLR->r_type = clayer;
	    viaLR->r_r = rect;

	    y += sizey + spacey;
	}
	x += sizex + spacex;
	y = -h / 2;
    }
}

/*
 *------------------------------------------------------------
 *
 * LefAddViaGeometry --
 *
 *	Read in geometry for a VIA section from a LEF or DEF
 *	file.
 *
 * Results:
 *	None.
 *
 * Side Effects:
 *	Adds to the lefLayer record for a via definition.
 *
 *------------------------------------------------------------
 */

void
LefAddViaGeometry(
    FILE *f,			/* LEF file being read	*/
    lefLayer *lefl,		/* pointer to via info	*/
    TileType curlayer,		/* current tile type	*/
    float oscale)		/* output scaling	*/
{
    Rect *currect;
    LinkedRect *viaLR;

    /* Rectangles for vias are read in units of 1/2 lambda */
    currect = LefReadRect(f, curlayer, (oscale / 2));
    if (currect == NULL) return;

    /* Don't create any geometry for unknown layers! */
    if (curlayer < 0) return;

    /* Expand via to the size used by magic */
    LefGrowVia(curlayer, currect, lefl);

    if (GEO_SAMERECT(lefl->info.via.area, GeoNullRect))
    {
	lefl->info.via.area = *currect;
	lefl->type = curlayer;
    }
    else
    {
	viaLR = (LinkedRect *)mallocMagic(sizeof(LinkedRect));
	viaLR->r_next = lefl->info.via.lr;
	lefl->info.via.lr = viaLR;
	viaLR->r_type = curlayer;
	viaLR->r_r = *currect;

	/* Make sure that the primary record is a contact type. */
	if (DBIsContact(curlayer) && !DBIsContact(lefl->type))
	{
	    viaLR->r_type = lefl->type;
	    lefl->type = curlayer;
	    viaLR->r_r = lefl->info.via.area;
	    lefl->info.via.area = *currect;
	}
    }
}

/*
 *------------------------------------------------------------
 *
 * LefReadLayerSection --
 *
 *	Read in a LAYER, VIA, or VIARULE section from a LEF file.
 *
 * Results:
 *	None.
 *
 * Side Effects:
 *	Adds to the LEF layer info hash table.
 *
 *------------------------------------------------------------
 */

enum lef_layer_keys {LEF_LAYER_TYPE=0, LEF_LAYER_WIDTH,
	LEF_LAYER_MAXWIDTH, LEF_LAYER_AREA,
	LEF_LAYER_SPACING, LEF_LAYER_SPACINGTABLE,
	LEF_LAYER_PITCH, LEF_LAYER_DIRECTION, LEF_LAYER_OFFSET,
	LEF_LAYER_WIREEXT,
	LEF_LAYER_RES, LEF_LAYER_CAP, LEF_LAYER_EDGECAP,
	LEF_LAYER_THICKNESS, LEF_LAYER_HEIGHT,
	LEF_LAYER_PROPERTY, LEF_LAYER_ACDENSITY, LEF_LAYER_DCDENSITY,
	LEF_LAYER_MINDENSITY, LEF_LAYER_ANTENNADIFF,
	LEF_LAYER_ANTENNAAREA, LEF_LAYER_ANTENNASIDE,
	LEF_VIA_DEFAULT, LEF_VIA_LAYER, LEF_VIA_RECT, LEF_VIA_FOREIGN,
	LEF_VIA_ENCLOSURE, LEF_VIA_PREFERENCLOSURE,
	LEF_VIARULE_OVERHANG,
	LEF_VIARULE_METALOVERHANG, LEF_VIARULE_VIA,
	LEF_VIARULE_GENERATE, LEF_LAYER_END};

void
LefReadLayerSection(
    FILE *f,			/* LEF file being read	  */
    const char *lname,		/* name of the layer 	  */
    int mode,			/* layer, via, or viarule */
    lefLayer *lefl)		/* pointer to layer info  */
{
    const char *token;
    int keyword, typekey;
    TileType curlayer = -1;
    float fvalue, oscale;

    /* These are defined in the order of CLASS_* in lefInt.h */
    static const char * const layer_type_keys[] = {
	"ROUTING",
	"CUT",
	"MASTERSLICE",
	"OVERLAP",
	NULL
    };

    static const char * const layer_keys[] = {
	"TYPE",
	"WIDTH",
	"MAXWIDTH",
	"AREA",
	"SPACING",
	"SPACINGTABLE",
	"PITCH",
	"DIRECTION",
	"OFFSET",
	"WIREEXTENSION",
	"RESISTANCE",
	"CAPACITANCE",
	"EDGECAPACITANCE",
	"THICKNESS",
	"HEIGHT",
	"PROPERTY",
	"ACCURRENTDENSITY",
	"DCCURRENTDENSITY",
	"MINIMUMDENSITY",
	"ANTENNAAREARATIO",
	"ANTENNADIFFAREARATIO",
	"ANTENNASIDEAREARATIO",
	"DEFAULT",
	"LAYER",
	"RECT",
	"FOREIGN",
	"ENCLOSURE",
	"PREFERENCLOSURE",
	"OVERHANG",
	"METALOVERHANG",
	"VIA",
	"GENERATE",
	"END",
	NULL
    };

    static const char * const spacing_keys[] = {
	"RANGE",
	";",
	NULL
    };

    oscale = CIFGetOutputScale(1000);

    while ((token = LefNextToken(f, TRUE)) != NULL)
    {
	keyword = LookupFull(token, layer_keys);
	if (keyword < 0)
	{
	    LefError(LEF_INFO, "Unknown keyword \"%s\" in LEF file; ignoring.\n",
		    token);
	    LefEndStatement(f);
	    continue;
	}
	switch (keyword)
	{
	    case LEF_LAYER_TYPE:
		token = LefNextToken(f, TRUE);
		if (*token != '\n')
		{
		    typekey = LookupFull(token, layer_type_keys);
		    if (typekey < 0)
			LefError(LEF_WARNING, "Unknown layer type \"%s\" in LEF file; "
				"ignoring.\n", token);
		}
		if (lefl->lefClass != typekey)
		{
		    /* ROUTE and VIA are taken from techfile lef section */
		    /* and so having a different TYPE is an error.	 */
		    /* Otherwise just ignore the type.			 */
		    if (typekey == CLASS_ROUTE || typekey == CLASS_VIA)
			LefError(LEF_ERROR, "Attempt to reclassify layer %s "
					"from %s to %s\n",
					lname, layer_type_keys[lefl->lefClass],
					layer_type_keys[typekey]);
		}
		LefEndStatement(f);
		break;
	    case LEF_LAYER_WIDTH:
		token = LefNextToken(f, TRUE);
		sscanf(token, "%f", &fvalue);
		if (lefl->lefClass == CLASS_ROUTE)
		    lefl->info.route.width = (int)roundf(fvalue / oscale);
		LefEndStatement(f);
		break;
	    case LEF_LAYER_MAXWIDTH:
	    case LEF_LAYER_AREA:
		/* Not handled */
		LefEndStatement(f);
		break;
	    case LEF_LAYER_SPACING:
		/* To do:  Handle RANGE */
		token = LefNextToken(f, TRUE);
		sscanf(token, "%f", &fvalue);
		if (lefl->lefClass == CLASS_ROUTE)
		    lefl->info.route.spacing = (int)roundf(fvalue / oscale);
		LefEndStatement(f);
		break;
	    case LEF_LAYER_PROPERTY:
		/* Ignoring property statements */
		LefEndStatement(f);
		break;
	    case LEF_LAYER_ACDENSITY:
		/* Stupid syntax */
		token = LefNextToken(f, TRUE);	    /* value type */
		token = LefNextToken(f, TRUE);	    /* value, FREQUENCY */
		if (!strcmp(token, "FREQUENCY")) {
		    LefEndStatement(f);
		    token = LefNextToken(f, TRUE);
		    if (!strcmp(token, "WIDTH"))    /* Optional width */
			LefEndStatement(f);	/* Additional TABLEENTRIES */
		}
		LefEndStatement(f);
		break;
	    case LEF_LAYER_DCDENSITY:
		/* Stupid syntax */
		token = LefNextToken(f, TRUE);	    /* value type */
		token = LefNextToken(f, TRUE);	    /* value, WIDTH */
		if (!strcmp(token, "WIDTH"))
		    LefEndStatement(f);	    /* Additional TABLEENTRIES */
		LefEndStatement(f);
		break;
	    case LEF_LAYER_SPACINGTABLE:
		/* To do:  Handle spacing tables */
		LefEndStatement(f);
		break;
	    case LEF_LAYER_PITCH:
		token = LefNextToken(f, TRUE);
		sscanf(token, "%f", &fvalue);
		if (lefl->lefClass == CLASS_ROUTE)
		    lefl->info.route.pitch = (int)roundf(fvalue / oscale);
		LefEndStatement(f);
		break;
	    case LEF_LAYER_DIRECTION:
		token = LefNextToken(f, TRUE);
		if (lefl->lefClass == CLASS_ROUTE)
		    lefl->info.route.hdirection = (tolower(token[0]) == 'h') ? TRUE : FALSE;
		LefEndStatement(f);
		break;
	    case LEF_LAYER_OFFSET:
	    case LEF_LAYER_RES:
	    case LEF_LAYER_CAP:
	    case LEF_LAYER_EDGECAP:
	    case LEF_LAYER_THICKNESS:
	    case LEF_LAYER_HEIGHT:
	    case LEF_LAYER_MINDENSITY:
	    case LEF_LAYER_ANTENNAAREA:
	    case LEF_LAYER_ANTENNADIFF:
	    case LEF_LAYER_ANTENNASIDE:
	    case LEF_LAYER_WIREEXT:
		LefEndStatement(f);
		break;
	    case LEF_VIA_DEFAULT:
	    case LEF_VIARULE_GENERATE:
		/* Do nothing; especially, don't look for end-of-statement! */
		break;
	    case LEF_VIA_LAYER:
		curlayer = LefReadLayer(f, FALSE);
		LefEndStatement(f);
		break;
	    case LEF_VIA_RECT:
		LefAddViaGeometry(f, lefl, curlayer, oscale);
		LefEndStatement(f);
		break;
	    case LEF_VIA_FOREIGN:
	    case LEF_VIARULE_VIA:
	    case LEF_VIA_ENCLOSURE:
	    case LEF_VIA_PREFERENCLOSURE:
	    case LEF_VIARULE_OVERHANG:
	    case LEF_VIARULE_METALOVERHANG:
		LefEndStatement(f);
		break;
	    case LEF_LAYER_END:
		if (LefParseEndStatement(f, lname) == 0)
		{
		    LefError(LEF_ERROR, "Layer END statement missing.\n");
		    keyword = -1;
		}
		break;
	}
	if (keyword == LEF_LAYER_END) break;
    }
}

/*
 *------------------------------------------------------------
 *
 * LefRead --
 *
 *	Read a .lef file into a magic layout.
 *
 * Results:
 *	None.
 *
 * Side Effects:
 *	Many.  Cell definitions and uses are created and added to
 *	the database.
 *
 *------------------------------------------------------------
 */

enum lef_sections {LEF_VERSION = 0,
	LEF_BUSBITCHARS, LEF_DIVIDERCHAR, LEF_MANUFACTURINGGRID,
	LEF_USEMINSPACING, LEF_CLEARANCEMEASURE,
	LEF_NAMESCASESENSITIVE,
	LEF_PROPERTYDEFS, LEF_UNITS, LEF_SECTION_LAYER,
	LEF_SECTION_VIA, LEF_SECTION_VIARULE, LEF_SECTION_NONDEFAULTRULE,
	LEF_NOWIREEXTENSIONATPIN, LEF_SECTION_SPACING, LEF_SECTION_SITE,
	LEF_NOISETABLE, LEF_CORRECTIONTABLE, LEF_IRDROP,
	LEF_ARRAY, LEF_SECTION_TIMING, LEF_EXTENSION, LEF_MACRO,
	LEF_END};

void
LefRead(
    const char *inName,
    bool importForeign,
    bool doAnnotate,
    int lefTimestamp)
{
    FILE *f;
    char *filename;
    const char *token;
    char tsave[128];
    int keyword;
    float oscale;
    HashEntry *he;
    lefLayer *lefl;

    static const char * const sections[] = {
	"VERSION",
	"BUSBITCHARS",
	"DIVIDERCHAR",
	"MANUFACTURINGGRID",
	"USEMINSPACING",
	"CLEARANCEMEASURE",
	"NAMESCASESENSITIVE",
	"PROPERTYDEFINITIONS",
	"UNITS",
	"LAYER",
	"VIA",
	"VIARULE",
	"NONDEFAULTRULE",
	"NOWIREEXTENSIONATPIN",
	"SPACING",
	"SITE",
	"NOISETABLE",
	"CORRECTIONTABLE",
	"IRDROP",
	"ARRAY",
	"TIMING",
	"BEGINEXT",
	"MACRO",
	"END",
	NULL
    };

    /* Make sure we have a valid LefInfo hash table, even if it's empty */
    if (LefInfo.ht_table == (HashEntry **) NULL)
	LefTechInit();

    f = lefFileOpen(NULL, inName, ".lef", "r", &filename);

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

    TxPrintf("Reading LEF data from file %s.\n", filename);
    TxPrintf("This action cannot be undone.\n");
    UndoDisable();

    /* Initialize */
    HashInit(&LefCellTable, 32, HT_STRINGKEYS);
    HashInit(&lefDefInitHash, 32, HT_STRINGKEYS);
    oscale = CIFGetOutputScale(1000);
    lefCurrentLine = 0;

    while ((token = LefNextToken(f, TRUE)) != NULL)
    {
	keyword = LookupFull(token, sections);
	if (keyword < 0)
	{
	    LefError(LEF_INFO, "Unknown keyword \"%s\" in LEF file; ignoring.\n",
		    token);
	    LefEndStatement(f);
	    continue;
	}
	switch (keyword)
	{
	    case LEF_VERSION:
	    case LEF_BUSBITCHARS:
	    case LEF_DIVIDERCHAR:
	    case LEF_CLEARANCEMEASURE:
	    case LEF_MANUFACTURINGGRID:
	    case LEF_USEMINSPACING:
		LefEndStatement(f);
		break;
	    case LEF_NAMESCASESENSITIVE:
	    case LEF_NOWIREEXTENSIONATPIN:
		LefEndStatement(f);
		break;
	    case LEF_PROPERTYDEFS:
		LefSkipSection(f, sections[LEF_PROPERTYDEFS]);
		break;
	    case LEF_UNITS:
		LefSkipSection(f, sections[LEF_UNITS]);
		break;

	    case LEF_SECTION_VIA:
	    case LEF_SECTION_VIARULE:
		token = LefNextToken(f, TRUE);
		sprintf(tsave, "%.127s", token);
		he = HashFind(&LefInfo, token);
		lefl = (lefLayer *)HashGetValue(he);
		if (lefl == NULL)
		{
		    lefl = (lefLayer *)mallocMagic(sizeof(lefLayer));
		    lefl->type = -1;
		    lefl->obsType = -1;
		    lefl->refCnt = 1;
		    lefl->lefClass = CLASS_VIA;
		    lefl->info.via.area = GeoNullRect;
		    lefl->info.via.cell = (CellDef *)NULL;
		    lefl->info.via.lr = (LinkedRect *)NULL;
		    HashSetValue(he, lefl);
		    LefReadLayerSection(f, tsave, keyword, lefl);
		    lefl->canonName = (const char *)he->h_key.h_name;
		}
		else if (keyword == LEF_SECTION_VIARULE)
		    /* If we've already seen this via, don't reprocess. */
		    /* This deals with VIA followed by VIARULE.  We	*/
		    /* really ought to have special processing for the	*/
		    /* VIARULE section. . .				*/
		    LefSkipSection(f, tsave);
		else
		{
		    LefError(LEF_WARNING, "Cut type \"%s\" redefined.\n", token);
		    lefl = LefRedefined(lefl, token);
		    LefReadLayerSection(f, tsave, keyword, lefl);
		}
		break;

	    case LEF_SECTION_LAYER:
		token = LefNextToken(f, TRUE);
		sprintf(tsave, "%.127s", token);
		he = HashLookOnly(&LefInfo, token);
		if (he == NULL)
		{
		    TileType mtype = DBTechNameType(token);
		    if (mtype < 0)
			mtype = LefHelper_DBTechNameType_LefLower(token);
		    if (mtype < 0)
		    {
			/* Ignore.  This is probably a masterslice or	*/
			/* overlap, but that hasn't been parsed yet.	*/
			LefSkipSection(f, tsave);
			break;
		    }
		    else if (DBIsContact(mtype) && (keyword == LEF_SECTION_LAYER))
		    {
			LefError(LEF_ERROR, "Layer %s maps to a magic contact layer; "
				"must be defined in lef section of techfile\n",
				token);
			LefSkipSection(f, tsave);
			break;
		    }
		    else if (!DBIsContact(mtype) && (keyword != LEF_SECTION_LAYER))
		    {
			LefError(LEF_ERROR, "Via %s maps to a non-contact magic layer; "
				"must be defined in lef section of techfile\n",
				token);
			LefSkipSection(f, tsave);
			break;
		    }
		    else
		    {
			he = HashFind(&LefInfo, token);
			lefl = (lefLayer *)mallocMagic(sizeof(lefLayer));
			lefl->type = mtype;
			lefl->obsType = -1;
			lefl->refCnt = 1;
			lefl->lefClass = (DBIsContact(mtype)) ? CLASS_VIA : CLASS_ROUTE;
			HashSetValue(he, lefl);
		        lefl->canonName = (const char *)he->h_key.h_name;
		    }
		}
		else
		{
		    lefl = (lefLayer *)HashGetValue(he);
		    if (lefl && lefl->type < 0)
		    {
			LefError(LEF_ERROR, "Layer %s is only defined for "
				"obstructions!\n", token);
			LefSkipSection(f, tsave);
			break;
		    }
		}
		LefReadLayerSection(f, tsave, keyword, lefl);
		break;

	    case LEF_SECTION_NONDEFAULTRULE:
		token = LefNextToken(f, TRUE);
		sprintf(tsave, "%.127s", token);
		LefReadNonDefaultRule(f, tsave, oscale);
		break;
	    case LEF_SECTION_SPACING:
		LefSkipSection(f, sections[LEF_SECTION_SPACING]);
		break;
	    case LEF_NOISETABLE:
		LefSkipSection(f, sections[LEF_NOISETABLE]);
		break;
	    case LEF_CORRECTIONTABLE:
		LefSkipSection(f, sections[LEF_CORRECTIONTABLE]);
		break;
	    case LEF_IRDROP:
		LefSkipSection(f, sections[LEF_IRDROP]);
		break;
	    case LEF_ARRAY:
		LefSkipSection(f, sections[LEF_ARRAY]);
		break;
	    case LEF_SECTION_TIMING:
		LefSkipSection(f, sections[LEF_SECTION_TIMING]);
		break;
	    case LEF_EXTENSION:
		LefSkipSection(f, sections[LEF_EXTENSION]);
		break;
	    case LEF_SECTION_SITE:
	    case LEF_MACRO:
		token = LefNextToken(f, TRUE);
		/* Diagnostic */
		/*
		TxPrintf("LEF file:  Defines new cell %s\n", token);
		*/
		sprintf(tsave, "%.127s", token);
		LefReadMacro(f, tsave, oscale, importForeign, doAnnotate, lefTimestamp);
		break;
	    case LEF_END:
		if (LefParseEndStatement(f, "LIBRARY") == 0)
		{
		    LefError(LEF_ERROR, "END statement out of context.\n");
		    keyword = -1;
		}
		break;
	}
	if (keyword == LEF_END) break;
    }
    TxPrintf("LEF read: Processed %d lines.\n", lefCurrentLine);
    LefError(LEF_SUMMARY, NULL);	/* print statement of errors, if any */

    /* Cleanup */
    HashKill(&LefCellTable);
    HashKill(&lefDefInitHash);
    if (f != NULL) fclose(f);
    UndoEnable();
}
