/*
 * 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 char rcsid[] __attribute__ ((unused)) = "$Header: /usr/cvsroot/magic-7.4/lef/lefRead.c,v 1.6 2006/10/04 17:31:44 tim Exp $";            
#endif  /* not lint */

#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <string.h>
#include <errno.h>
#include <stdarg.h>
#include <sys/time.h>
#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 "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 "lef/lefInt.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(processed, total, item_name)
   int processed;
   int total;
   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 */
    {
	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(processed, total, item_name)
   int processed;
   int total;
   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.
 *
 *------------------------------------------------------------
 */

char *
LefNextToken(f, ignore_eol)
    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 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). */

    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;
}

/*
 *------------------------------------------------------------
 *
 * LefError --
 *
 *	Print an error message (via TxError) giving the line
 *	number of the input file on which the error occurred.
 *
 * Results:
 *	None.
 *
 * Side Effects:
 *	Prints to the output (stderr).
 *
 *------------------------------------------------------------
 */

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

    if (fmt == NULL)  /* Special case:  report any errors and reset */
    {
	if (errors)
	{
	    TxPrintf("LEF Read: encountered %d error%s total.\n", errors,
			(errors == 1) ? "" : "s");
	    errors = 0;
	}
	return;
    }

    if (errors < LEF_MAX_ERRORS)
    {
	TxError("LEF Read, Line %d: ", lefCurrentLine);
	va_start(args, fmt);
	Vfprintf(stderr, fmt, args);
	va_end(args);
	TxFlushErr();
    }
    else if (errors == LEF_MAX_ERRORS)
	TxError("LEF Read:  Further errors will not be reported.\n");

    errors++;
}

/*
 *------------------------------------------------------------
 *
 * 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:
 *	TRUE if the line matches the expected end statement,
 *	FALSE if not. 
 *
 * Side effects:
 *	None.
 *
 *------------------------------------------------------------
 */

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

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

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

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

    /* END <section_name> */
    else {
	keyword = LookupFull(token, match_name);
	if (keyword == 0)
	    return TRUE;
	else
	    return FALSE;
    }
}

/*
 *------------------------------------------------------------
 *
 * 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(f, section)
    FILE *f;
    char *section;
{
    char *token;
    int keyword;
    static char *end_section[] = {
	"END",
	"ENDEXT",
	NULL
    };

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

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

/*
 *------------------------------------------------------------
 *
 * lefFindCell --
 *
 *------------------------------------------------------------
 */

CellDef *
lefFindCell(name)
    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, (char *)NULL);
	    DBReComputeBbox(def);
	}
	HashSetValue(h, def);
    }
    return (CellDef *) HashGetValue(h);
}

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

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

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

    return token;
}

/*
 *------------------------------------------------------------
 * 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(f, obstruct)
    FILE *f;
    bool obstruct;
{
    char *token;
    TileType curlayer = -1;

    token = LefNextToken(f, TRUE);
    if (*token == ';')
    {
	LefError("Bad Layer statement\n");
	return -1;
    }
    else
    {
	HashEntry *he = HashLookOnly(&LefInfo, token);
	if (he)
	{
	    lefLayer *lefl = (lefLayer *)HashGetValue(he);
	    if (lefl && obstruct)
	    {
		/* Use the obstruction type, if it is defined */
		curlayer = lefl->obsType;
		if (curlayer < 0)
		    curlayer = lefl->type;
	    }
	    else
		curlayer = lefl->type;
	}
	else
	{
	    /* fallback: compare name to magic types */

	    curlayer = DBTechNameType(token);
	    if (curlayer < 0)
	    {
		(void) LefLower(token);
		curlayer = DBTechNameType(token);
	    }
	}
	if (curlayer < 0)
	{
	    LefError("Don't know how to parse layer \"%s\"\n", token);
	    LefError("Try adding this name to the LEF techfile section\n");
	}
    }
    return curlayer;
}

/*
 *------------------------------------------------------------
 * 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(f, curlayer, oscale)
    FILE *f;
    TileType curlayer;
    float oscale;
{
    char *token;
    float llx, lly, urx, ury;
    static Rect paintrect;
    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("No layer defined for RECT.\n");
    else
    {
	/* Scale coordinates (microns to magic internal units)	*/
	/* Need to scale grid if necessary!			*/
		
	paintrect.r_xbot = (int)roundf(llx / oscale);
	paintrect.r_ybot = (int)roundf(lly / oscale);
	paintrect.r_xtop = (int)roundf(urx / oscale);
	paintrect.r_ytop = (int)roundf(ury / oscale);

	/* 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("Bad port geometry: RECT requires 4 values.\n");
    return (Rect *)NULL;
}

/*
 *------------------------------------------------------------
 * 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().
 *
 * 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_GEOMETRY_END};

linkedRect *
LefReadGeometry(lefMacro, f, oscale, do_list)
    CellDef *lefMacro;
    FILE *f;
    float oscale;
    bool do_list;
{
    TileType curlayer = -1;

    char *token;
    int keyword;
    linkedRect *newRect, *rectList;
    Rect *paintrect;

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

    rectList = NULL;

    while ((token = LefNextToken(f, TRUE)) != NULL)
    {
	keyword = Lookup(token, geometry_keys);
	if (keyword < 0)
	{
	    LefError("Unknown keyword \"%s\" in LEF file; ignoring.\n", token);
	    LefEndStatement(f);
	    continue;
	}
	switch (keyword)
	{
	    case LEF_LAYER:
		curlayer = LefReadLayer(f, (do_list) ? FALSE : TRUE);
		LefEndStatement(f);
		break;
	    case LEF_WIDTH:
		LefEndStatement(f);
		break;
	    case LEF_PATH:
		LefEndStatement(f);
		break;
	    case LEF_RECT:
		paintrect = LefReadRect(f, curlayer, oscale);
		if (paintrect)
		{
		    /* Paint the area, if a CellDef is defined */
		    if (lefMacro)
			DBPaint(lefMacro, paintrect, curlayer);

		    /* Remember the area and layer */
		    if (do_list)
		    {
			newRect = (linkedRect *)mallocMagic(sizeof(linkedRect));
			newRect->type = curlayer;
			newRect->area = *paintrect;
			newRect->rect_next = rectList;
			rectList = newRect;
		    }
		}
		LefEndStatement(f);
		break;
	    case LEF_POLYGON:
		LefEndStatement(f);
		break;
	    case LEF_VIA:
		LefEndStatement(f);
		break;
	    case LEF_GEOMETRY_END:
		if (!LefParseEndStatement(f, NULL))
		{
		    LefError("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;
 *	Paints into the CellDef lefMacro.
 *
 *------------------------------------------------------------
 */

void
LefReadPort(lefMacro, f, pinName, pinNum, pinDir, pinUse, oscale)
    CellDef *lefMacro;
    FILE *f;
    char *pinName;
    int pinNum, pinDir, pinUse;
    float oscale;
{
    Label *newlab;
    linkedRect *rectList;

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

    while (rectList != NULL)
    {
	if (pinNum >= 0)
	{
	    /* Label this area */
	    DBPutLabel(lefMacro, &rectList->area, -1, pinName, rectList->type, 0);

	    /* Set this label to be a port */

	    if (lefMacro->cd_labels == NULL)
		LefError("Internal error: No labels in cell!\n");
	    else
	    {
		newlab = lefMacro->cd_lastLabel;
		if (strcmp(newlab->lab_text, pinName))
		    LefError("Internal error:  Can't find the label!\n");
		else /* Make this a port */
		    newlab->lab_flags = pinNum | pinUse | pinDir | PORT_DIR_MASK;
	    }
	    /* DBAdjustLabels(lefMacro, &rectList->area); */
	}

	freeMagic((char *)rectList);
	rectList = rectList->rect_next;
    }
}

/*
 *------------------------------------------------------------
 * 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_PIN_END};

void
LefReadPin(lefMacro, f, pinname, pinNum, oscale)
   CellDef *lefMacro;
   FILE *f;
   char *pinname;
   int pinNum;
   float oscale;
{
    char *token;
    int keyword, subkey;
    int pinDir = PORT_CLASS_DEFAULT;
    int pinUse = PORT_USE_DEFAULT;

    static char *pin_keys[] = {
	"DIRECTION",
	"USE",
	"PORT",
	"CAPACITANCE",
	"END",
	NULL
    };

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

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

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

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

    while ((token = LefNextToken(f, TRUE)) != NULL)
    {
	keyword = Lookup(token, pin_keys);
	if (keyword < 0)
	{
	    LefError("Unknown keyword \"%s\" in LEF file; ignoring.\n", token);
	    LefEndStatement(f);
	    continue;
	}
	switch (keyword)
	{
	    case LEF_DIRECTION:
		token = LefNextToken(f, TRUE);
		subkey = Lookup(token, pin_classes);
		if (subkey < 0)
		    LefError("Improper DIRECTION statement\n");
		else
		    pinDir = lef_class_to_bitmask[subkey];
		LefEndStatement(f);
		break;
	    case LEF_USE:
		token = LefNextToken(f, TRUE);
		subkey = Lookup(token, pin_uses);
		if (subkey < 0)
		    LefError("Improper USE statement\n");
		else
		    pinUse = lef_use_to_bitmask[subkey];
		LefEndStatement(f);
		break;
	    case LEF_PORT:
		LefReadPort(lefMacro, f, pinname, pinNum, pinDir, pinUse, oscale);
		break;
	    case LEF_CAPACITANCE:
		LefEndStatement(f);	/* Ignore. . . */
		break;
	    case LEF_PIN_END:
		if (!LefParseEndStatement(f, pinname))
		{
		    LefError("Pin END statement missing.\n");
		    keyword = -1;
		}
		break;
	}
	if (keyword == LEF_PIN_END) break;
    }
}

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

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

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

/*
 *------------------------------------------------------------
 *
 * 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_MACRO_END};

void
LefReadMacro(f, mname, oscale, importForeign)
    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.
				 */
{
    CellDef *lefMacro;
    HashEntry *he;

    char *token, tsave[128];
    int keyword, pinNum;
    float x, y;
    bool has_size, is_imported = FALSE;
    Rect lefBBox;

    static char *macro_keys[] = {
	"CLASS",
	"SIZE",
	"ORIGIN",
	"SYMMETRY",
	"SOURCE",
	"SITE",
	"PIN",
	"OBS",
	"TIMING",
	"FOREIGN",
	"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++)
	{
	    sprintf(newname, "%250s_%d", mname, suffix);
	    he = HashFind(&lefDefInitHash, newname);
	}
	LefError("Cell \"%s\" was already defined in this file.  "
		"Renaming this cell \"%s\"\n", mname, newname);
	lefMacro = lefFindCell(newname);
    }
    else
	lefMacro = lefFindCell(mname);

    DBCellClearDef(lefMacro);
    DBCellSetAvail(lefMacro);
    HashSetValue(he, lefMacro);

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

    while ((token = LefNextToken(f, TRUE)) != NULL)
    {
	keyword = Lookup(token, macro_keys);
	if (keyword < 0)
	{
	    LefError("Unknown keyword \"%s\" in LEF file; ignoring.\n", token);
	    LefEndStatement(f);
	    continue;
	}
	switch (keyword)
	{
	    case LEF_CLASS:
		token = LefNextToken(f, TRUE);
		if (*token != '\n')
		    DBPropPut(lefMacro, "LEFclass", token);
		LefEndStatement(f);
		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("Bad macro SIZE; requires values X BY Y.\n");
		LefEndStatement(f);
		break;
	    case LEF_ORIGIN:
		token = LefNextToken(f, TRUE);
		if (!token || sscanf(token, "%f", &x) != 1) goto origin_error;
		token = LefNextToken(f, TRUE);
		if (!token || sscanf(token, "%f", &y) != 1) 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;
		}
		LefEndStatement(f);
		break;
origin_error:
		LefError("Bad macro ORIGIN; requires 2 values.\n");
		LefEndStatement(f);
		break;
	    case LEF_SYMMETRY:
		token = LefNextToken(f, TRUE);
		if (*token != '\n')
		    DBPropPut(lefMacro, "LEFsymmetry", token + strlen(token) + 1);
		LefEndStatement(f);
		break;
	    case LEF_SOURCE:
		token = LefNextToken(f, TRUE);
		if (*token != '\n')
		    DBPropPut(lefMacro, "LEFsource", token);
		LefEndStatement(f);
		break;
	    case LEF_SITE:
		token = LefNextToken(f, TRUE);
		if (*token != '\n')
		    DBPropPut(lefMacro, "LEFsite", token);
		LefEndStatement(f);
		break;
	    case LEF_PIN:
		token = LefNextToken(f, TRUE);
		/* Diagnostic */
		/*
		TxPrintf("   Macro defines pin %s\n", token);
		*/
		sprintf(tsave, "%.127s", token);
		if (is_imported)
		    LefSkipSection(f, tsave);
		else
		    LefReadPin(lefMacro, f, tsave, pinNum++, oscale);
		break;
	    case LEF_OBS:
		/* Diagnostic */
		/*
		TxPrintf("   Macro defines obstruction\n");
		*/
		if (is_imported)
		    LefSkipSection(f, NULL);
		else
		    LefReadGeometry(lefMacro, f, oscale, FALSE);
		break;
	    case LEF_TIMING:
		LefSkipSection(f, macro_keys[LEF_TIMING]);
		break;
	    case LEF_FOREIGN:
		if (importForeign)
		{
		    token = LefNextToken(f, TRUE);
		    sprintf(tsave, "%.127s", token);

		    /* Attempt to read a .mag file of this name */

		    DBCellClearAvail(lefMacro);
		    if (DBCellRead(lefMacro, tsave, TRUE))
			is_imported = TRUE;
		    else
		    {
			TxError("   Cannot find magic file %s.mag "
				"to import.\n", tsave);
			TxError("   Filling cell with geometry from "
				"the LEF file.\n");
		    }
		}
		LefEndStatement(f);
		break;
	    case LEF_MACRO_END:
		if (!LefParseEndStatement(f, mname))
		{
		    LefError("Macro END statement missing.\n");
		    keyword = -1;
		}
		break;
	}
	if (keyword == LEF_MACRO_END) break;
    }

    /* Finish up creating the cell */

    if (is_imported)
	DBReComputeBbox(lefMacro);
    else
    {
	DBAdjustLabelsNew(lefMacro, &TiPlaneRect, 1);

	if (!has_size)
	{
	    LefError("   Macro does not define size:  computing from geometry\n");
	    DBReComputeBbox(lefMacro);
	}
	else
	    lefMacro->cd_bbox = lefBBox;

	/* Fix the bounding box and do not allow edits */
	lefMacro->cd_flags |= /* CDNOEDIT | */ CDFIXEDBBOX;

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

/*
 *------------------------------------------------------------
 *
 * 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_SPACING,
	LEF_LAYER_PITCH, LEF_LAYER_DIRECTION, LEF_LAYER_OFFSET,
	LEF_VIA_DEFAULT, LEF_VIA_LAYER, LEF_VIA_RECT,
	LEF_VIARULE_VIA, LEF_LAYER_END};

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

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

    static char *layer_keys[] = {
	"TYPE",
	"WIDTH",
	"SPACING",
	"PITCH",
	"DIRECTION",
	"OFFSET",
	"DEFAULT",
	"LAYER",
	"RECT",
	"VIA",
	"END",
	NULL
    };

    oscale = CIFGetOutputScale(1000);
    viaArea = GeoNullRect;

    while ((token = LefNextToken(f, TRUE)) != NULL)
    {
	keyword = Lookup(token, layer_keys);
	if (keyword < 0)
	{
	    LefError("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 = Lookup(token, layer_type_keys);
		    if (typekey < 0)
			LefError("Unknown layer type \"%s\" in LEF file; "
				"ignoring.\n", token);
		}
		if (lefl->lefClass != typekey)
		    LefError("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);
		lefl->info.route.width = (int)roundf(fvalue / oscale);
		LefEndStatement(f);
		break;
	    case LEF_LAYER_SPACING:
		token = LefNextToken(f, TRUE);
		sscanf(token, "%f", &fvalue);
		lefl->info.route.spacing = (int)roundf(fvalue / oscale);
		LefEndStatement(f);
		break;
	    case LEF_LAYER_PITCH:
		token = LefNextToken(f, TRUE);
		sscanf(token, "%f", &fvalue);
		lefl->info.route.pitch = (int)roundf(fvalue / oscale);
		LefEndStatement(f);
		break;
	    case LEF_LAYER_DIRECTION:
		token = LefNextToken(f, TRUE);
		LefLower(token);
		lefl->info.route.hdirection = (token[0] == 'h') ? TRUE : FALSE;
		LefEndStatement(f);
		break;
	    case LEF_LAYER_OFFSET:
		LefEndStatement(f);
		break;
	    case LEF_VIA_DEFAULT:
		/* 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:
		/* Rectangles for vias are read in units of 1/2 lambda */
		currect = LefReadRect(f, curlayer, (oscale / 2));
		if (GEO_SAMERECT(lefl->info.via.area, GeoNullRect))
		{
		    lefl->info.via.area = *currect;
		    lefl->type = curlayer;
		}
		else 
		{
		    linkedRect *viaLR = (linkedRect *)mallocMagic(sizeof(linkedRect));
		    viaLR->rect_next = lefl->info.via.lr;
		    lefl->info.via.lr = viaLR;
		    viaLR->type = curlayer;
		    viaLR->area = *currect;

		    /* Make sure that the primary record is a contact type. */
		    if (DBIsContact(curlayer) && !DBIsContact(lefl->type))
		    {
			viaLR->type = lefl->type;
			lefl->type = curlayer;
			viaLR->area = lefl->info.via.area;
			lefl->info.via.area = *currect;
		    }
		}
		LefEndStatement(f);
		break;
	    case LEF_VIARULE_VIA:
		LefEndStatement(f);
		break;
	    case LEF_LAYER_END:
		if (!LefParseEndStatement(f, lname))
		{
		    LefError("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_NAMESCASESENSITIVE,
	LEF_PROPERTYDEFS, LEF_UNITS, LEF_SECTION_LAYER,
	LEF_SECTION_VIA, LEF_SECTION_VIARULE,
	LEF_SECTION_SPACING, LEF_SECTION_SITE, LEF_PROPERTY,
	LEF_NOISETABLE, LEF_CORRECTIONTABLE, LEF_IRDROP,
	LEF_ARRAY, LEF_SECTION_TIMING, LEF_EXTENSION, LEF_MACRO,
	LEF_END};

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

    static char *sections[] = {
	"VERSION",
	"NAMESCASESENSITIVE",
	"PROPERTYDEFINITIONS",
	"UNITS",
	"LAYER",
	"VIA",
	"VIARULE",
	"SPACING",
	"SITE",
	"PROPERTY",
	"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 is undoable.\n");
    UndoDisable();

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

    while ((token = LefNextToken(f, TRUE)) != NULL)
    {
	keyword = Lookup(token, sections);
	if (keyword < 0)
	{
	    LefError("Unknown keyword \"%s\" in LEF file; ignoring.\n", token);
	    LefEndStatement(f);
	    continue;
	}
	switch (keyword)
	{
	    case LEF_VERSION:
		LefEndStatement(f);
		break;
	    case LEF_NAMESCASESENSITIVE:
		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 = (char *)he->h_key.h_name;
		}
		else
		    /* 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);
		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 = DBTechNameType(LefLower(token));
		    if (mtype < 0)
		    {
			LefError("Layer %s cannot be mapped to any magic layer!\n",
				token);
			LefSkipSection(f, tsave);
			break;
		    }
		    else if (DBIsContact(mtype) && (keyword == LEF_SECTION_LAYER))
		    {
			LefError("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("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 = (char *)he->h_key.h_name;
		    }
		}
		else
		{
		    lefl = (lefLayer *)HashGetValue(he);
		    if (lefl && lefl->type < 0)
		    {
			LefError("Layer %s is only defined for obstructions!\n", token);
			LefSkipSection(f, tsave);
			break;
		    }
		}
		LefReadLayerSection(f, tsave, keyword, lefl);
		break;

	    case LEF_SECTION_SPACING:
		LefSkipSection(f, sections[LEF_SECTION_SPACING]);
		break;
	    case LEF_SECTION_SITE:
		token = LefNextToken(f, TRUE);
		TxPrintf("LEF file:  Defines site %s (ignored)\n", token);
		sprintf(tsave, "%.127s", token);
		LefSkipSection(f, tsave);
		break;
	    case LEF_PROPERTY:
		LefSkipSection(f, NULL);
		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_MACRO:
		token = LefNextToken(f, TRUE);
		/* Diagnostic */
		/*
		TxPrintf("LEF file:  Defines new cell %s\n", token);
		*/
		sprintf(tsave, "%.127s", token);
		LefReadMacro(f, tsave, oscale, importForeign);
		break;
	    case LEF_END:
		if (!LefParseEndStatement(f, "LIBRARY"))
		{
		    LefError("END statement out of context.\n");
		    keyword = -1;
		}
		break;
	}
	if (keyword == LEF_END) break;
    }
    TxPrintf("LEF read: Processed %d lines.\n", lefCurrentLine);
    LefError(NULL);	/* print statement of errors, if any */

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