/* CIFreadcell.c -
 *
 *	This file contains more routines to parse CIF files.  In
 *	particular, it contains the routines to handle cells,
 *	both definitions and calls, and user-defined features
 *	like labels.
 *
 *     ********************************************************************* 
 *     * Copyright (C) 1985, 1990 Regents of the University of California. * 
 *     * Permission to use, copy, modify, and distribute this              * 
 *     * software and its documentation for any purpose and without        * 
 *     * fee is hereby granted, provided that the above copyright          * 
 *     * notice appear in all copies.  The University of California        * 
 *     * makes no representations about the suitability of this            * 
 *     * software for any purpose.  It is provided "as is" without         * 
 *     * express or implied warranty.  Export of this software outside     * 
 *     * of the United States of America may require an export license.    * 
 *     *********************************************************************
 */

#ifndef lint
static char rcsid[] __attribute__ ((unused)) = "$Header: /usr/cvsroot/magic-7.5/cif/CIFrdcl.c,v 1.2 2006/04/15 01:04:45 anonymous Exp $";
#endif  /* not lint */

#include <stdio.h>
#include <string.h>
#include <ctype.h>

#include "utils/magic.h"
#include "utils/malloc.h"
#include "utils/geometry.h"
#include "tiles/tile.h"
#include "utils/hash.h"
#include "utils/undo.h"
#include "database/database.h"
#include "cif/CIFint.h"
#include "cif/CIFread.h"
#include "utils/utils.h"
#include "windows/windows.h"
#include "dbwind/dbwind.h"
#include "utils/main.h"
#include "drc/drc.h"

/* The following variable is made available to the outside world,
 * and is the cell definition currently being modified.
 */

CellDef *cifReadCellDef;

/*
 * The following hash table is used internally to keep track of
 * of all the cells we've seen definitions for or calls on.
 * The hash table entries contain pointers to cellDefs, and
 * are indexed by CIF cell number.  If the CDAVAILABLE bit is
 * set it means we've read the cell's contents.  If not set, it
 * means that the cell has been called but not yet defined.
 */
HashTable CifCellTable;

/* The following variable is used to save and restore current
 * paint layer information so that we can resume the correct
 * layer after a subcell definition.
 */

Plane *cifOldReadPlane = NULL;

/* The following boolean is TRUE if a subcell definition is being
 * read.  FALSE means we're working on the EditCell.
 */

bool cifSubcellBeingRead;

/* The following two collections of planes are used to hold CIF
 * information while cells are being read in (one set for the
 * outermost, unnamed cell, and one for the current subcell).
 * When a cell is complete, then geometrical operations are
 * performed on the layers and stuff is painted into Magic.
 */

Plane *cifEditCellPlanes[MAXCIFRLAYERS];
Plane *cifSubcellPlanes[MAXCIFRLAYERS];
Plane **cifCurReadPlanes = cifEditCellPlanes;	/* Set of planes currently
						 * in force.
						 */
TileType cifCurLabelType = TT_SPACE;	/* Magic layer on which to put '94'
					 * labels that aren't identified by
					 * type.
					 */

/* The following variable is used to hold a subcell id between
 * the 91 statement and the (immediately-following?) call statement.
 * The string this points to is dynamically allocated, so it must
 * also be freed explicitly.
 */

char *cifSubcellId = NULL;

/*
 * ----------------------------------------------------------------------------
 *
 * CIFReadCellInit --
 *
 * 	This procedure initializes the data structures in this
 *	module just prior to reading a CIF file.
 *
 *	If ptrkeys is 0, the keys used in this hash table will
 *	be strings; if it is 1, the keys will be CIF numbers.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	The cell hash table is initialized, and things are set up
 *	to put information in the EditCell first.
 *
 * ----------------------------------------------------------------------------
 */

void
CIFReadCellInit(ptrkeys)
    int ptrkeys;
{
    int i;

    HashInit(&CifCellTable, 32, ptrkeys);
    cifReadCellDef = EditCellUse->cu_def;
    cifSubcellBeingRead = FALSE;
    cifCurReadPlanes = cifEditCellPlanes;
    for (i = 0; i < MAXCIFRLAYERS; i += 1)
    {
	if (cifEditCellPlanes[i] == NULL)
	    cifEditCellPlanes[i] = DBNewPlane((ClientData) TT_SPACE);
	if (cifSubcellPlanes[i] == NULL)
	    cifSubcellPlanes[i] = DBNewPlane((ClientData) TT_SPACE);
    }
}

/*
 * ----------------------------------------------------------------------------
 *
 * cifForgetCell --
 *
 * 	This local procedure is used to find a cell in the subcell
 *	table and remove its CellDef entry. 
 *
 * Results:
 *	FALSE if no such entry was found, otherwise TRUE.
 *
 * Side effects:
 *	Mucks with the CIF cell name hash table.
 *
 * ----------------------------------------------------------------------------
 */

bool
cifForgetCell(cifNum)
    int cifNum;
{
    HashEntry *h;

    h = HashLookOnly(&CifCellTable, (char *)(spointertype)cifNum);
    if (h == NULL)
	return FALSE;
    else if (HashGetValue(h) == 0)
	return FALSE;

    HashSetValue(h, 0);
    return TRUE;
}

/*
 * ----------------------------------------------------------------------------
 *
 * cifUniqueCell --
 *
 *	Attempt to find a cell in the CIF subcell name hash table.
 *	If one exists, rename its definition so that it will not
 *	be overwritten when the cell is redefined.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	None.
 *
 * ----------------------------------------------------------------------------
 */

void
cifUniqueCell(cifNum)
   int cifNum;
{
    HashEntry *h;
    CellDef *def, *testdef;
    char name[17];
    int reused = 0;

    h = HashLookOnly(&CifCellTable, (char *)(spointertype)cifNum);

    if ((h == NULL) || HashGetValue(h) == 0)
    {
	/* Cell was deleted with "DD".  Don't rename anything */
	return;
    }

    sprintf(name, "%d", cifNum);
    def = DBCellLookDef(name);
    if (def == (CellDef *)NULL)
	return;

    /* Cell may have been called but not yet defined---this is okay. */
    else if ((def->cd_flags & CDAVAILABLE) == 0)
        return;

    testdef = def;
    while (testdef != NULL)
    {
	sprintf(name, "%d_%d", cifNum, ++reused);
	testdef = DBCellLookDef(name);
    }
    DBCellRenameDef(def, name);

    h = HashFind(&CifCellTable, (char *)(spointertype)cifNum);
    HashSetValue(h, 0);

    CIFReadError("Warning: cell definition %d reused.\n", cifNum);
}
	
/*
 * ----------------------------------------------------------------------------
 *
 * cifFindCell --
 *
 * 	This local procedure is used to find a cell in the subcell
 *	table, and create a new subcell if there isn't already
 *	one there.  If a new subcell is created, its CDAVAILABLE
 *	is left FALSE.
 *
 * Results:
 *	The return value is a pointer to the definition for the
 *	cell whose CIF number is cifNum.
 *
 * Side effects:
 *	A new CellDef may be created.
 *
 * ----------------------------------------------------------------------------
 */

CellDef *
cifFindCell(cifNum)
    int cifNum;			/* The CIF number of the desired cell. */
{
    HashEntry *h;
    CellDef *def, *testdef;
    int reused;

    h = HashFind(&CifCellTable, (char *)(spointertype)cifNum);

    if (HashGetValue(h) == 0)
    {
	char name[15];

	sprintf(name, "%d", cifNum);
	def = DBCellLookDef(name);
	if (def == NULL)
	{
	    def = DBCellNewDef(name, (char *) NULL);

	    /* Tricky point:  call DBReComputeBbox here to make SURE
	     * that the cell has a valid bounding box.  Otherwise,
	     * if the cell is used in a parent before being defined
	     * then it will cause a core dump.
	     */

	    DBReComputeBbox(def);
	}
	HashSetValue(h, def);
    }
    return (CellDef *) HashGetValue(h);
}

/*
 * ----------------------------------------------------------------------------
 *
 * CIFScalePlanes --
 *
 *   Scale all of the CIF planes by the amount (scalen / scaled)
 *
 * ----------------------------------------------------------------------------
 */

void
CIFScalePlanes(scalen, scaled, planearray)
    int scalen;
    int scaled;
    Plane **planearray;
{
    int pNum;
    Plane *newplane;

    for (pNum = 0; pNum < MAXCIFRLAYERS; pNum++)
    {
	if (planearray[pNum] != NULL)
	{
	    newplane = DBNewPlane((ClientData) TT_SPACE);
	    DBClearPaintPlane(newplane);
	    dbScalePlane(planearray[pNum], newplane, pNum,
			scalen, scaled, TRUE);
	    DBFreePaintPlane(planearray[pNum]);
	    TiFreePlane(planearray[pNum]);
	    planearray[pNum] = newplane;
	}
    }
}

/*
 * ----------------------------------------------------------------------------
 *
 * CIFInputRescale --
 *
 *	Scale all CIF distances by n / d.  Normally, rescaling is done in
 *	the upward direction (n > 1, d = 1) in response to a value in the
 *	CIF input that does not divide evenly into the database units of
 *	the CIF planes.  Currently there is no call to CIFInputRescale
 *	with d > 1, but it is left for the possibility that scalefactors
 *	could be restored after finishing a subcell definition, possibly
 *	preventing integer overflow in large designs.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	Many.  Adjusts the CIF input style scalefactor (CIF vs. magic
 *	units), multiplier (CIF units vs. centimicrons), distances of all
 *	CIF boolean operations, and the scale of all existing planes in
 *	the current CIF database.
 *
 * ----------------------------------------------------------------------------
 */
 
void
CIFInputRescale(n, d)
    int n, d;
{
    CIFReadStyle *istyle = cifCurReadStyle;
    CIFReadLayer *cl;
    CIFOp *op;
    int i;

    /* 2-step process for efficiency */

    if (n > 1)
    {
	istyle->crs_scaleFactor *= n;
	istyle->crs_multiplier *= n;

	for (i = 0; i < istyle->crs_nLayers; i++)
	{
	    cl = istyle->crs_layers[i];
	    for (op = cl->crl_ops; op != NULL; op = op->co_next)
		if (op->co_distance)
		    op->co_distance *= n;
	}
    }

    if (d > 1)
    {
	istyle->crs_scaleFactor /= d;
	istyle->crs_multiplier /= d;

	for (i = 0; i < istyle->crs_nLayers; i++)
	{
	    cl = istyle->crs_layers[i];
	    for (op = cl->crl_ops; op != NULL; op = op->co_next)
		if (op->co_distance)
		    op->co_distance /= d;
	}
    }

    CIFScalePlanes(n, d, cifEditCellPlanes);
    if (cifEditCellPlanes != cifSubcellPlanes)
	CIFScalePlanes(n, d, cifSubcellPlanes);

    CIFReadWarning("CIF style %s: units rescaled by factor of %d / %d\n",
		istyle->crs_name, n, d);
}

/*
 * ----------------------------------------------------------------------------
 *
 * CIFParseStart --
 *
 * 	Parse the beginning of a symbol (cell) definition.
 *	ds ::= D { blank } S integer [ sep integer sep integer ]
 *
 * Results:
 *	TRUE is returned if the parse completed successfully, and
 *	FALSE is returned otherwise.
 *
 * Side effects:
 *	Set up information for the new cell, including the CIF
 *	planes and creating a Magic cell (if one doesn't exist
 *	already).
 *
 * ----------------------------------------------------------------------------
 */

bool
CIFParseStart()
{
    int		number;
    
    if (cifSubcellBeingRead)
    {
	CIFReadError("definition start inside other definition; ignored.\n");
	CIFSkipToSemi();
	return FALSE;
    }
    if (cifSubcellId != NULL)
    {
	CIFReadError("pending call identifier %s discarded.\n", cifSubcellId);
	(void) StrDup(&cifSubcellId, (char *) NULL);
    }

    /* Take the `S'. */

    TAKE();
    if (!CIFParseInteger(&number))
    {
	CIFReadError("definition start, but no symbol number; ignored.\n");
	CIFSkipToSemi();
	return FALSE;
    }
    else if (number < 0)
    {
	CIFReadError("illegal negative symbol number; definition ignored.\n");
	CIFSkipToSemi();
	return FALSE;
    }

    if (!CIFParseInteger(&cifReadScale1))
    {
	cifReadScale1 = 1;
	cifReadScale2 = 1;
    }
    else
    {
	cifReadScale1 *= cifCurReadStyle->crs_multiplier;  /* Units not centimicrons */

	if (!CIFParseInteger(&cifReadScale2))
	{
	    CIFReadError(
	        "only one of two scale factors given; ignored.\n");
	    cifReadScale1 = 1;
	    cifReadScale2 = 1;
	}
    }

    if (cifReadScale1 <= 0 || cifReadScale2 <= 0)
    {
	CIFReadError("Illegal scale %d / %d changed to 1 / 1\n",
		cifReadScale1, cifReadScale2);
	cifReadScale1 = 1;
	cifReadScale2 = 1;
    }


    /*
     * Set up the cell definition.
     */

    cifUniqueCell(number);
    cifReadCellDef = cifFindCell(number);
    DBCellClearDef(cifReadCellDef);
    DBCellSetAvail(cifReadCellDef);

    cifOldReadPlane = cifReadPlane;
    cifReadPlane = (Plane *) NULL;
    cifSubcellBeingRead = TRUE;
    cifCurReadPlanes = cifSubcellPlanes;
    return TRUE;
}

/*
 * ----------------------------------------------------------------------------
 *
 * CIFPaintCurrent --
 *
 * 	This procedure does geometrical processing on the current
 *	set of CIF planes, and paints the results into the current
 *	CIF cell.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	Lots of information gets added to the current Magic cell.
 *
 * ----------------------------------------------------------------------------
 */

void
CIFPaintCurrent()
{
    Plane *plane;
    int i;

    for (i = 0; i < cifCurReadStyle->crs_nLayers; i += 1)
    {
	TileType type;
	extern int cifPaintCurrentFunc();	/* Forward declaration. */

	plane = CIFGenLayer(cifCurReadStyle->crs_layers[i]->crl_ops,
	    &TiPlaneRect, (CellDef *) NULL, cifCurReadPlanes);
	
	/* Generate a paint/erase table, then paint from the CIF
	 * plane into the current Magic cell.
	 */

	type = cifCurReadStyle->crs_layers[i]->crl_magicType;

	(void) DBSrPaintArea((Tile *) NULL, plane, &TiPlaneRect,
		&CIFSolidBits, cifPaintCurrentFunc, (ClientData) type);
	
	/* Recycle the plane, which was dynamically allocated. */

	DBFreePaintPlane(plane);
	TiFreePlane(plane);
    }

    /* Now go through all the current planes and zero them out. */

    for (i = 0; i < MAXCIFRLAYERS; i++)
	DBClearPaintPlane(cifCurReadPlanes[i]);
}

/* Below is the search function invoked for each CIF tile type
 * found for the current layer.
 */

int
cifPaintCurrentFunc(tile, type)
    Tile *tile;			/* Tile of CIF information. */
    TileType type;		/* Magic type to be painted. */
{
    Rect area;
    int pNum;
    int savescale;
    bool snap_type = COORD_EXACT;

    /* Contact types are allowed to be on half-lambda spacing, and are	*/
    /* snapped to the nearest magic coordinate if the result is a	*/
    /* fractional magic coordinate.					*/

    if (DBIsContact(type)) snap_type = COORD_HALF_U;

    /* Compute the area of the CIF tile, then scale it into
     * Magic coordinates.
     */

    TiToRect(tile, &area);
    area.r_xtop = CIFScaleCoord(area.r_xtop, snap_type);
    savescale = cifCurReadStyle->crs_scaleFactor;
    area.r_ytop = CIFScaleCoord(area.r_ytop, snap_type);
    if (snap_type == COORD_HALF_U) snap_type = COORD_HALF_L;
    if (savescale != cifCurReadStyle->crs_scaleFactor)
    {
	area.r_xtop *= (savescale / cifCurReadStyle->crs_scaleFactor);
	savescale = cifCurReadStyle->crs_scaleFactor;
    }
    area.r_xbot = CIFScaleCoord(area.r_xbot, snap_type);
    if (savescale != cifCurReadStyle->crs_scaleFactor)
    {
	area.r_xtop *= (savescale / cifCurReadStyle->crs_scaleFactor);
	area.r_ytop *= (savescale / cifCurReadStyle->crs_scaleFactor);
	savescale = cifCurReadStyle->crs_scaleFactor;
    }
    area.r_ybot = CIFScaleCoord(area.r_ybot, snap_type);
    if (savescale != cifCurReadStyle->crs_scaleFactor)
    {
	area.r_xtop *= (savescale / cifCurReadStyle->crs_scaleFactor);
	area.r_ytop *= (savescale / cifCurReadStyle->crs_scaleFactor);
	area.r_xbot *= (savescale / cifCurReadStyle->crs_scaleFactor);
    }

    /* Check for degenerate areas (from rescale limiting) before painting */
    if ((area.r_xbot == area.r_xtop) || (area.r_ybot == area.r_ytop))
	return 0;

    for (pNum = PL_PAINTBASE; pNum < DBNumPlanes; pNum++)
	if (DBPaintOnPlane(type, pNum))
	{
#ifdef NONMANHATTAN
	    DBNMPaintPlane(cifReadCellDef->cd_planes[pNum], TiGetTypeExact(tile),
		    &area, DBStdPaintTbl(type, pNum), (PaintUndoInfo *) NULL);
#else
	    DBPaintPlane(cifReadCellDef->cd_planes[pNum], &area,
		    DBStdPaintTbl(type, pNum), (PaintUndoInfo *) NULL);
#endif
	}

    return  0;		/* To keep the search alive. */
}

/*
 * ----------------------------------------------------------------------------
 *
 * CIFParseFinish --
 *
 * 	This procedure is called at the end of a cell definition.
 *
 * Results:
 *	TRUE is returned if the parse completed successfully, and
 *	FALSE is returned otherwise.
 *
 * Side effects:
 *	Process the CIF planes and paint the results into the Magic
 *	cell.
 *
 * ----------------------------------------------------------------------------
 */

bool
CIFParseFinish()
{
    if (!cifSubcellBeingRead)
    {
	CIFReadError("definition finish without definition start; ignored.\n");
	CIFSkipToSemi();
	return FALSE;
    }
    if (cifSubcellId != NULL) 
    {
	CIFReadError("pending call identifier %s discarded.\n", cifSubcellId);
	(void) StrDup(&cifSubcellId, (char *) NULL);
    }
	
    /* Take the `F'. */

    TAKE();

    /* Do the geometrical processing and paint this material back into
     * the appropriate cell of the database.  Then restore the saved
     * layer info.
     */
    
    CIFPaintCurrent();

    DBAdjustLabels(cifReadCellDef, &TiPlaneRect);
    DBReComputeBbox(cifReadCellDef);
    cifReadCellDef = EditCellUse->cu_def;
    cifReadPlane = cifOldReadPlane;
    cifReadScale1 = 1;
    cifReadScale2 = 1;
    cifSubcellBeingRead = FALSE;
    cifCurReadPlanes = cifEditCellPlanes;
    return TRUE;
}

/*
 * ----------------------------------------------------------------------------
 *
 * CIFParseDelete --
 *
 * 	This procedure is called to handle delete-symbol statements.
 *
 * Results:
 *	TRUE is returned if the parse completed successfully, and
 *	FALSE is returned otherwise.
 *
 * Side effects:
 *	The mapping between numbers and cells is modified to eliminate
 *	some symbols.
 *
 * ----------------------------------------------------------------------------
 */

bool
CIFParseDelete()
{
    int		number;

    /* Take the `D'. */

    TAKE();
    if (!CIFParseInteger(&number))
    {
	CIFReadError("definition delete, but no symbol number; ignored.\n");
	CIFSkipToSemi();
	return FALSE;
    }

    /* Unlink the hash entry from its target definition */
    cifForgetCell(number);

    CIFSkipToSemi();
    return TRUE;
}

/*
 * ----------------------------------------------------------------------------
 *
 * cifParseName --
 *
 * 	Parse a name, which is a string of alphabetics, numerics,
 *	or underscores, possibly preceded by whitespace.
 *
 * Results:
 *	The return value is a pointer to the name read from the
 *	CIF file.  This is a statically-allocated area, so the
 *	caller should copy out of this area before invoking this
 *	procedure again.
 *
 * Side effects:
 *	None.
 *
 * ----------------------------------------------------------------------------
 */

char *
cifParseName()
{
    char	ch;
    char	*bufferp;
    static char	buffer[128];

    /* Skip white space. */

    for (ch = PEEK() ; ch == ' ' || ch == '\t' ; ch = PEEK())
	TAKE();
    
    /* Read the string. */

    bufferp = &buffer[0];
    for (ch = PEEK() ; (! isspace(ch)) && ch != ';' ; ch = PEEK())
    {
	*bufferp++ = TAKE();
    }
    *bufferp = '\0';
    return buffer;
}

/*
 * ----------------------------------------------------------------------------
 *
 * cifParseUser9 --
 *
 * 	This procedure processes user extension 9: the name of the
 *	current symbol (cell) definition.
 *
 * Results:
 *	TRUE is returned if the parse completed successfully, and
 *	FALSE is returned otherwise.
 *
 * Side effects:
 *	The current CIF symbol is renamed from its default "cifxx" name
 *	to the given name.
 *
 * ----------------------------------------------------------------------------
 */

bool
cifParseUser9()
{
    char *name;

    name = cifParseName();
    if (!DBCellRenameDef(cifReadCellDef, name))
    {
	CIFReadError("%s already exists, so cell from CIF is named %s.\n",
		name, cifReadCellDef->cd_name);
    }
    return TRUE;
}

/*
 * ----------------------------------------------------------------------------
 *
 * CIFParseCall --
 *
 * 	This procedure processes subcell uses.  The syntax of a call is
 *	call ::= C integer transform
 *
 * Results:
 *	TRUE is returned if the parse completed successfully, and
 *	FALSE is returned otherwise.
 *
 * Side effects:
 *	A subcell is added to the current Magic cell we're generating.
 *
 * ----------------------------------------------------------------------------
 */

bool
CIFParseCall()
{
    int		called;
    Transform	transform;
    CellUse 	*use;
    CellDef	*def;
    
    /* Take the `C'. */

    TAKE();
    if (!CIFParseInteger(&called))
    {
	CIFReadError("call, but no symbol number; ignored.\n");
	CIFSkipToSemi();
	return FALSE;
    }

    /* Get optional transformation. */

    (void) CIFParseTransform(&transform);

    def = cifFindCell(called);

    /* avoid recursion
     */

    if (DBIsAncestor(def, cifReadCellDef))
    {
	CIFReadError("attempt to place cell use inside its own definition!\n");
	CIFSkipToSemi();
	return FALSE;
    }

    /* Find the use and add it to the current cell.  Give it an
     * id also.
     */

    use = DBCellNewUse(def, cifSubcellId);
    (void) DBLinkCell(use, cifReadCellDef);
    DBSetTrans(use, &transform);
    DBPlaceCell(use, cifReadCellDef);

    (void) StrDup(&cifSubcellId, (char *) NULL);
    return TRUE;
}

/*
 * ----------------------------------------------------------------------------
 *
 * cifParseUser91 --
 *
 * 	This procedure handles 91 user commands, which provide id's
 *	for following cell calls.  The syntax is:
 *	91 ::= 91 blanks name
 *
 * Results:
 *	TRUE is returned if the parse completed successfully, and
 *	FALSE is returned otherwise.
 *
 * Side effects:
 *	The identifier is saved until the call is read.  Then it is
 *	used as the identifier for the cell.
 *
 * ----------------------------------------------------------------------------
 */

bool
cifParseUser91()
{
    if (cifSubcellId != NULL)
    {
	CIFReadError("91 command with identifier %s pending; %s discarded.\n" ,
	    cifSubcellId , cifSubcellId);
    }
    (void) StrDup(&cifSubcellId, cifParseName());
    return TRUE;
}

/*
 * ----------------------------------------------------------------------------
 *
 * cifParseUser94 --
 *
 * 	This procedure parses 94 user commands, which are labelled
 *	points.  The syntax is:
 *	94 ::= 94 blanks name point
 *
 * Results:
 *	TRUE is returned if the parse completed successfully, and
 *	FALSE is returned otherwise.
 *
 * Side effects:
 *	A label is added to the current cell.
 *
 * ----------------------------------------------------------------------------
 */

bool
cifParseUser94()
{
    Rect rectangle;
    char *name = NULL;
    TileType type;
    int layer;
    int savescale;

    (void) StrDup(&name, cifParseName());
    if (! CIFParsePoint(&rectangle.r_ll, 1))
    {
	CIFReadError("94 command, but no location; ignored.\n");
	CIFSkipToSemi();
	return FALSE;
    }

    /* Scale the coordinates, then make the location into a
     * rectangle.
     */

    rectangle.r_xbot = CIFScaleCoord(rectangle.r_xbot, COORD_ANY);
    savescale = cifCurReadStyle->crs_scaleFactor;
    rectangle.r_ybot = CIFScaleCoord(rectangle.r_ybot, COORD_ANY);
    if (savescale != cifCurReadStyle->crs_scaleFactor)
	rectangle.r_xbot *= (savescale / cifCurReadStyle->crs_scaleFactor);

    rectangle.r_ur = rectangle.r_ll;

    /* Get a layer, lookup the layer, then add the label to the
     * current cell.  Tricky business: in order for the default
     * label location to be computed
     */
    
    CIFSkipBlanks();
    if (PEEK() != ';')
    {
	char *name = cifParseName();
	layer = CIFReadNameToType(name, FALSE);
	if (layer < 0)
	{
	    CIFReadError("label attached to unknown layer %s.\n",
		    name);
	    type = TT_SPACE;
	}
	else type = cifCurReadStyle->crs_labelLayer[layer];
    } else {
	type = cifCurLabelType;
    }
    if (type >=0 )
    {
    	 (void) DBPutLabel(cifReadCellDef, &rectangle, -1, name, type, 0);
    }
    freeMagic(name);
    return TRUE;
}

/*
 * ----------------------------------------------------------------------------
 *
 * cifParseUser95 --
 *
 * 	This procedure parses 95 user commands, which are labelled
 *	points.  The syntax is:
 *	95 ::= 95 blanks name length width point
 *
 * Results:
 *	TRUE is returned if the parse completed successfully, and
 *	FALSE is returned otherwise.
 *
 * Side effects:
 *	An area label is added to the current cell.
 *
 * ----------------------------------------------------------------------------
 */

bool
cifParseUser95()
{
    Rect rectangle;
    Point size, center;
    char *name = NULL;
    TileType type;
    int layer;
    int savescale;

    (void) StrDup(&name, cifParseName());
    if (! CIFParsePoint(&size, 1))
    {
	CIFReadError("95 command, but no size; ignored.\n");
	CIFSkipToSemi();
	return FALSE;
    }
    if (! CIFParsePoint(&center, 1))
    {
	CIFReadError("95 command, but no location; ignored.\n");
	CIFSkipToSemi();
	return FALSE;
    }

    /* Scale the coordinates and create the rectangular area.		*/
    /* Remap center and size to lowerleft and upperright, respectively, */
    /* so that half-lambda centers are resolved before remapping to	*/
    /* magic coordinates.						*/

    center.p_x = CIFScaleCoord(center.p_x - size.p_x/2, COORD_ANY);
    savescale = cifCurReadStyle->crs_scaleFactor;
    center.p_y = CIFScaleCoord(center.p_y - size.p_y/2, COORD_ANY);
    if (savescale != cifCurReadStyle->crs_scaleFactor)
    {
	center.p_x *= (savescale / cifCurReadStyle->crs_scaleFactor);
	savescale = cifCurReadStyle->crs_scaleFactor;
    }
    size.p_x = CIFScaleCoord(center.p_x + (size.p_x - size.p_x/2), COORD_ANY);
    if (savescale != cifCurReadStyle->crs_scaleFactor)
    {
	center.p_x *= (savescale / cifCurReadStyle->crs_scaleFactor);
	center.p_y *= (savescale / cifCurReadStyle->crs_scaleFactor);
	savescale = cifCurReadStyle->crs_scaleFactor;
    }
    size.p_y = CIFScaleCoord(center.p_y + (size.p_y - size.p_y/2), COORD_ANY);
    if (savescale != cifCurReadStyle->crs_scaleFactor)
    {
	center.p_x *= (savescale / cifCurReadStyle->crs_scaleFactor);
	center.p_y *= (savescale / cifCurReadStyle->crs_scaleFactor);
	size.p_x *= (savescale / cifCurReadStyle->crs_scaleFactor);
    }
    rectangle.r_xbot = center.p_x;
    rectangle.r_ybot = center.p_y;
    rectangle.r_xtop = size.p_x;
    rectangle.r_ytop = size.p_y;

    /* Get a layer, lookup the layer, then add the label to the
     * current cell.  Tricky business: in order for the default
     * label location to be computed
     */
    CIFSkipBlanks();
    if (PEEK() != ';')
    {
	char *name = cifParseName();
	layer = CIFReadNameToType(name, FALSE);
	if (layer < 0)
	{
	    CIFReadError("label attached to unknown layer %s.\n",
		    name);
	    type = TT_SPACE;
	}
	else type = cifCurReadStyle->crs_labelLayer[layer];
    }
    else type = TT_SPACE;
    if (type >=0 )
    {
    	 (void) DBPutLabel(cifReadCellDef, &rectangle, -1, name, type, 0);
    }

    freeMagic(name);
    return TRUE;
}

/*
 * ----------------------------------------------------------------------------
 *
 * CIFParseUser --
 *
 * 	This procedure is called to process user-defined statements.
 *	The syntax is user ::= digit usertext.
 *
 * Results:
 *	TRUE is returned if the parse completed successfully, and
 *	FALSE is returned otherwise.
 *
 * Side effects:
 *	Depends on the user command.
 *
 * ----------------------------------------------------------------------------
 */
bool
CIFParseUser()
{
    char	ch;

    ch = TAKE();
    switch (ch)
    {
	case '9':
		ch = PEEK();
		switch (ch)
		{
		    case '1':
			(void) TAKE();
			return cifParseUser91();
		    case '4':
			(void) TAKE();
			return cifParseUser94();
		    case '5':
			(void) TAKE();
			return cifParseUser95();
		    default:
			if (isspace(ch)) return cifParseUser9();
		}
	default:
		CIFReadError("unimplemented user extension; ignored.\n");
		CIFSkipToSemi();
		return FALSE;
    }
}

/*
 * ----------------------------------------------------------------------------
 *
 * CIFReadCellCleanup --
 *
 * 	This procedure is called after processing the CIF file.
 *	It performs various cleanup functions on the cells that
 *	have been read in.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	The area of each cell is DRC'ed and redisplayed.  Error
 *	messages are output for any cells whose contents weren't
 *	in the CIF file.  An error message is also output if
 *	we're still in the middle of reading a subcell.
 *
 * ----------------------------------------------------------------------------
 */

void
CIFReadCellCleanup()
{
    HashEntry *h;
    HashSearch hs;
    CellDef *def;
    MagWindow *window;

    if (cifSubcellBeingRead)
    {
	CIFReadError("CIF ended partway through a symbol definition.\n");
	(void) CIFParseFinish();
    }

    HashStartSearch(&hs);
    while (TRUE)
    {
	h = HashNext(&CifCellTable, &hs);
	if (h == NULL) break;

	def = (CellDef *) HashGetValue(h);
	if (def == NULL)
	{
	    CIFReadError("cell table has NULL entry (Magic error).\n");
	    continue;
	}
	if (!(def->cd_flags & CDAVAILABLE))
	{
	    CIFReadError("cell %s was used but not defined.\n", def->cd_name);
        }
	if (def->cd_flags & CDPROCESSEDGDS)
	{
	    def->cd_flags &= ~CDPROCESSEDGDS;
	}
	if (def->cd_flags & CDFLATGDS)
	{
	    /* These cells have been flattened and are no longer needed. */

	    char *savename = StrDup((char **)NULL, def->cd_name);
	    int pNum;
	    Plane **gdsplanes = (Plane **)def->cd_client;

	    UndoDisable();
	    for (pNum = 0; pNum < MAXCIFRLAYERS; pNum++)
	    {
	        if (gdsplanes[pNum] != NULL)
		{
		    DBFreePaintPlane(gdsplanes[pNum]);
		    TiFreePlane(gdsplanes[pNum]);
		}
	    }
	    freeMagic(def->cd_client);

	    if (def->cd_parents != (CellUse *)NULL)
	    {
		CellUse *cu_p = def->cd_parents;
		TxError("GDS read warning:  Cell %s has parent %s\n", savename,
				cu_p->cu_id);
		def->cd_parents = (CellUse *)NULL;
	    }
	    if (DBCellDeleteDef(def) == FALSE)
		TxError("GDS read error:  Unable to delete cell %s\n", savename);
	    else
		TxPrintf("GDS read:  Removed flattened cell %s\n", savename);
	    UndoEnable();
	    freeMagic(savename);
	}
	else
	{
	    DRCCheckThis(def, TT_CHECKPAINT, &def->cd_bbox);
	    DBWAreaChanged(def, &def->cd_bbox, DBW_ALLWINDOWS, &DBAllButSpaceBits);
	    DBCellSetModified(def, TRUE);
	}
    }

    HashKill(&CifCellTable);

    /* Finally, do geometrical processing on the top-level cell. */

    CIFPaintCurrent();
    DBAdjustLabels(EditCellUse->cu_def, &TiPlaneRect);
    DBReComputeBbox(EditCellUse->cu_def);
    DBWAreaChanged(EditCellUse->cu_def, &EditCellUse->cu_def->cd_bbox,
	    DBW_ALLWINDOWS, &DBAllButSpaceBits);
    DBCellSetModified(EditCellUse->cu_def, TRUE);
}
