/* CIFreadutils.c -
 *
 *	This file contains routines that parse a file in CIF
 *	format.  This file contains the top-level routine for
 *	reading CIF files, plus a bunch of utility routines
 *	for skipping white space, parsing numbers and points, etc.
 *
 *     ********************************************************************* 
 *     * 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/CIFrdutils.c,v 1.7 2010/05/26 21:35:22 tim Exp $";
#endif  /* not lint */

#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <ctype.h>

#include "utils/magic.h"
#include "utils/geometry.h"
#include "tiles/tile.h"
#include "utils/hash.h"
#include "database/database.h"
#include "cif/CIFint.h"
#include "cif/CIFread.h"
#include "cif/cif.h"
#include "textio/textio.h"
#include "utils/signals.h"
#include "utils/undo.h"
#include "utils/malloc.h"

/* The following variables are used to provide one character of
 * lookahead.  cifParseLaAvail is TRUE if cifParseLaChar contains
 * a valid character, FALSE otherwise.  The PEEK and TAKE macros
 * are used to manipulate this stuff.
 */

bool cifParseLaAvail = FALSE;
int cifParseLaChar = EOF;

/* Below is a variable pointing to the CIF input file.  It's used
 * by the PEEK and TAKE macros.  The other stuff is used to keep
 * track of our location in the CIF file for error reporting
 * purposes.
 */

FILE *cifInputFile;
FILE *cifErrorFile;
int cifLineNumber;		/* Number of current line. */
int cifTotalWarnings;		/* Number of warnings detected */
int cifTotalErrors;		/* Number of errors detected */

/* The variables used below hold general information about what
 * we're currently working on.
 */

int cifReadScale1;			/* Scale factor:  multiply by Scale1 */
int cifReadScale2;			/* then divide by Scale2. */
int CIFRescaleLimit = CIFMAXRESCALE;	/* Don't increase cifReadScale1 by more
					 * than this amount;  internal units
					 * finer than this will be rounded.
					 */
bool CIFRescaleAllow = TRUE;		/* Don't subdivide the magic internal
					 * grid if this is FALSE.
					 */
char *CIFErrorFilename;			/* Name of file for error redirection */

#ifdef NONMANHATTAN
int  CifPolygonCount;			/* Count of generated subcells
					 * containing polygons.  This number
					 * is used to create a unique cell name.
					 */
bool CIFSubcellPolygons = FALSE;	/* If TRUE, each non-Manhattan polygon
					 * will be put in a separate subcell
					 * to avoid too much tile splitting
					 */
#endif

Plane *cifReadPlane;			/* Plane into which to paint material
					 * NULL means no layer command has
					 * been seen for the current cell.
					 */

/*
 * ----------------------------------------------------------------------------
 *
 * CIFReadError --
 *
 * 	This procedure is called to print out error messages during
 *	CIF file reading.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	An error message is printed.
 *
 * ----------------------------------------------------------------------------
 */

    /* VARARGS1 */
void
CIFReadError(char *format, ...)
{
    va_list args;

    cifTotalErrors++;
    if (CIFWarningLevel == CIF_WARN_NONE) return;
    if ((cifTotalErrors < 100) || (CIFWarningLevel != CIF_WARN_LIMIT))
    {
	TxError("Error at line %d of CIF file: ", cifLineNumber);
	va_start(args, format);
	Vfprintf(stderr, format, args);
	va_end(args);
    }
    else if ((cifTotalErrors == 100) && (CIFWarningLevel == CIF_WARN_LIMIT))
    {
	TxError("Error limit set:  Remaining errors will not be reported.\n");
    }
}


void
CIFReadWarning(char *format, ...)
{
    va_list args;

    cifTotalWarnings++;
    if (CIFWarningLevel == CIF_WARN_NONE) return;
    if ((cifTotalWarnings < 100) || (CIFWarningLevel != CIF_WARN_LIMIT))
    {
	TxError("Warning at line %d of CIF file: ", cifLineNumber);
	va_start(args, format);
	Vfprintf(stderr, format, args);
	va_end(args);
    }
    else if ((cifTotalWarnings == 100) && (CIFWarningLevel == CIF_WARN_LIMIT))
    {
	TxError("Warning limit set:  Remaining warnings will not be reported.\n");
    }
}

/*
 * ----------------------------------------------------------------------------
 *
 *	CIFScaleCoord
 *
 * 	This procedure does rounding and division to convert from
 *	CIF units back into Magic units.
 *
 *	"snap_type" may be one of:
 *	    COORD_EXACT:  result must be an exact integer.  If not, the
 *		magic grid spacing is changed such that the result will
 *		be an integer.
 *	    COORD_HALF_U:  twice the result must be an exact integer.  If
 *		not, the magic grid spacing is changed as above.  If the
 *		result is 1/2, it is rounded up to the nearest integer.
 *	    COORD_HALF_L:  same as above, but result is rounded down.
 *	    COORD_ANY:  result may be fractional, and will be snapped to
 *		the nearest magic grid.  Generally, this is used for
 *		labels whose position need not be exact.
 *
 * Results:
 *	The result is the Magic unit equivalent to cifCoord.
 *
 * Side effects:
 *	None.
 *
 * ----------------------------------------------------------------------------
 */

int
CIFScaleCoord(cifCoord, snap_type)
    int cifCoord;			/* A coordinate in CIF units. */
    int snap_type;			/* How to deal with fractional results */
{
    int result, scale, remain, denom;
    int mult, mfactor;

    /* If internal grid subdivision is disallowed, always round to the	*/
    /* nearest grid unit.						*/

    if (!CIFRescaleAllow)
	snap_type = COORD_ANY;

    scale = cifCurReadStyle->crs_scaleFactor;
    mult = cifCurReadStyle->crs_multiplier;

    /* Check for non-integer result and warn of fractional-lambda violation */

    if ((remain = (cifCoord % scale)) != 0)
    {
	int lgcf = FindGCF(abs(cifCoord), scale);

	remain = abs(remain) / lgcf;
	denom = scale / lgcf;

	if (CIFTechLimitScale(1, denom)) snap_type = COORD_ANY;

	switch (snap_type)
	{
	    case COORD_EXACT:
		CIFReadWarning("Input off lambda grid by %d/%d; grid redefined.\n",
			remain, denom);

		CIFTechInputScale(1, denom, FALSE);
		CIFTechOutputScale(1, denom);
		DRCTechScale(1, denom);
		PlowAfterTech();
		ExtTechScale(1, denom);
		WireTechScale(1, denom);
#ifdef LEF_MODULE
		LefTechScale(1, denom);
#endif
#ifdef ROUTE_MODULE
		RtrTechScale(1, denom);
		MZAfterTech();
		IRAfterTech();
#endif
		DBScaleEverything(denom, 1);
		DBLambda[1] *= denom;
		ReduceFraction(&DBLambda[0], &DBLambda[1]);
		scale = cifCurReadStyle->crs_scaleFactor;
		result = cifCoord / scale;
		break;
	    case COORD_HALF_U: case COORD_HALF_L:
		if (denom > 2)
		{
		    CIFReadWarning("Input off lambda grid by %d/%d; grid redefined.\n",
				remain, denom);

		    /* scale to nearest half-lambda */
		    if (!(denom & 0x1)) denom >>= 1;

		    CIFTechInputScale(1, denom, FALSE);
		    CIFTechOutputScale(1, denom);
		    DRCTechScale(1, denom);
		    PlowAfterTech();
		    ExtTechScale(1, denom);
		    WireTechScale(1, denom);
		    MZAfterTech();
		    IRAfterTech();
#ifdef LEF_MODULE
		    LefTechScale(1, denom);
#endif
#ifdef ROUTE_MODULE
		    RtrTechScale(1, denom);
#endif
		    DBScaleEverything(denom, 1);
		    DBLambda[1] *= denom;
		    ReduceFraction(&DBLambda[0], &DBLambda[1]);
		    scale = cifCurReadStyle->crs_scaleFactor;
		}

		if (snap_type == COORD_HALF_U)
		    result = cifCoord + (scale >> 1);
		else
		    result = cifCoord - (scale >> 1);
		result /= scale;

		break;
	    case COORD_ANY:
		CIFReadWarning("Input off lambda grid by %d/%d; snapped to grid.\n",
			abs(remain), abs(denom));

		/* Careful:  must round down a bit more for negative numbers, in
		 * order to ensure that a point exactly halfway between Magic units
		 * always gets rounded down, rather than towards zero (this would
		 * result in different treatment of the same paint, depending on
		 * where it is in the coordinate system.
		 */

		if (cifCoord < 0)
		    result = cifCoord - ((scale)>>1);
		else
		    result = cifCoord + ((scale-1)>>1);
		result /= scale;
		break;
	}
    }
    else
	result = cifCoord / scale;

    return result;
}

/*
 * ----------------------------------------------------------------------------
 *
 * cifIsBlank --
 *
 * 	Figures out whether a character qualifies as a blank in CIF.
 *	A blank is anything except a digit, an upper-case character,
 *	or the symbols "-", "(", "(", and ";".
 *
 * Results:
 *	Returns TRUE if ch is a CIF blank, FALSE otherwise.
 *
 * Side effects:
 *	None.
 *
 * ----------------------------------------------------------------------------
 */

bool
cifIsBlank(ch)
    int	ch;
{

    if (  isdigit(ch) || isupper(ch)
	|| (ch == '-') || (ch == ';')
	|| (ch == '(') || (ch == ')')
	|| (ch == EOF))
    {
	return FALSE;
    }
    else return TRUE;
}

/*
 * ----------------------------------------------------------------------------
 *
 * CIFSkipBlanks --
 *
 * 	This procedure skips over whitespace in the CIF file,
 *	keeping track of the line number and other information
 *	for error reporting.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	Advances through the CIF file.
 *
 * ----------------------------------------------------------------------------
 */

void
CIFSkipBlanks()
{
    
    while (cifIsBlank(PEEK())) {
	if (TAKE() == '\n')
	{
	    cifLineNumber++;
	}
    }
}

/*
 * ----------------------------------------------------------------------------
 *
 * CIFSkipSep --
 *
 * 	Skip over separators in the CIF file.  Blanks and upper-case
 *	characters are separators.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	Advances through the CIF file.
 *
 * ----------------------------------------------------------------------------
 */

void
CIFSkipSep()
{
    int	ch;

    for (ch = PEEK() ; isupper(ch) || cifIsBlank(ch) ; ch = PEEK()) {
	if (TAKE() == '\n')
	{
	    cifLineNumber++;
	}
    }
}

/*
 * ----------------------------------------------------------------------------
 *
 * CIFSkipToSemi --
 *
 * 	This procedure is called after errors.  It skips everything
 *	in the CIF file up to the next semi-colon.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	Advances through the CIF file.
 *
 * ----------------------------------------------------------------------------
 */

void
CIFSkipToSemi()
{
    int	ch;
    
    for (ch = PEEK() ; ((ch != ';') && (ch != EOF)) ; ch = PEEK()) {
	if (TAKE() == '\n')
	{
	    cifLineNumber++;
	}
    }
}

/*
 * ----------------------------------------------------------------------------
 *
 * CIFSkipSemi --
 *
 * 	Skips a semi-colon, including blanks around the semi-colon.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	Advances through the CIF file.
 *
 * ----------------------------------------------------------------------------
 */

void
CIFSkipSemi()
{
    
    CIFSkipBlanks();
    if (PEEK() != ';') {
	CIFReadError("`;\' expected.\n");
	return;
    }
    TAKE();
    CIFSkipBlanks();
}

/*
 * ----------------------------------------------------------------------------
 *
 * CIFParseSInteger --
 *
 * 	This procedure parses a signed integer from the CIF file.
 *
 * Results:
 *	TRUE is returned if the parse completed without error,
 *	FALSE otherwise.
 *
 * Side effects:
 *	The integer pointed to by valuep is modified with the
 *	value of the signed integer.
 *
 * ----------------------------------------------------------------------------
 */

bool
CIFParseSInteger(valuep)
    int		*valuep;
{
    bool	is_signed;
    char	buffer[ BUFSIZ ];
    char	*bufferp;

    *valuep = 0;
    CIFSkipSep();
    if (PEEK() == '-')
    {
	TAKE();
	is_signed = TRUE;
    }
    else is_signed = FALSE;
    bufferp = &buffer[0];
    while (isdigit(PEEK()))
	*bufferp++ = TAKE();
    if (bufferp == &buffer[0])
	return FALSE;
    *bufferp = '\0';
    *valuep = atoi(&buffer[0]);
    if (is_signed)
	*valuep = -(*valuep);
    return TRUE;
}

/*
 * ----------------------------------------------------------------------------
 *
 * CIFParseInteger --
 *
 * 	Parses a positive integer from the CIF file.
 *
 * Results:
 *	TRUE is returned if the parse was completed successfully,
 *	FALSE otherwise.
 *
 * Side effects:
 *	The value pointed to by valuep is modified to hold the integer.
 *
 * ----------------------------------------------------------------------------
 */

bool
CIFParseInteger(valuep)
    int *valuep;
{

    if (!CIFParseSInteger(valuep))
	return FALSE;
    if (*valuep < 0)
	CIFReadError("negative integer not permitted.\n");
    return TRUE;
}

/*
 * ----------------------------------------------------------------------------
 *
 * CIFParsePoint --
 *
 * 	Parse a point from a CIF file.  A point is two integers
 *	separated by CIF separators.
 *	parameter "iscale" (internal scale factor) is usually 1, but
 *	can be 2 to deal with half-lambda entries in the CIF by
 *	returning double the result.
 *
 * Results:
 *	TRUE is returned if the point was parsed correctly, otherwise
 *	FALSE is returned.
 *
 * Side effects:
 *	The parameter pointp is filled in with the coordinates of
 *	the point.
 *
 *	If the CIF scalefactors are such that the result would be a
 *	fractional value, the definition of the CIF scale is altered
 *	such that the result is integer, and all geometry read so far
 *	is altered to match.  This does not immediately affect the geometry
 *	in the magic database;  if that also appears to have fractional
 *	units, it will be discovered by CIFScaleCoord and corrected.
 *
 * ----------------------------------------------------------------------------
 */

bool
CIFParsePoint(pointp, iscale)
    Point *pointp;
    int iscale;
{
    int rescale;

    pointp->p_x = 0;
    pointp->p_y = 0;
    if (!CIFParseSInteger(&pointp->p_x))
	return FALSE;
    pointp->p_x *= (cifReadScale1 * iscale);
    if (pointp->p_x % cifReadScale2 != 0)
    {
	rescale = cifReadScale2 / FindGCF(cifReadScale2, abs(pointp->p_x));
	if ((cifReadScale1 * rescale) > CIFRescaleLimit)
	{
	    CIFReadWarning("CIF units at maximum scale; value is rounded\n");
	    /* prepare for nearest-integer rounding */
	    if (pointp->p_x < 0)
		pointp->p_x -= ((cifReadScale2 - 1) >> 1);
	    else
		pointp->p_x += (cifReadScale2  >> 1);
	}
	else
	{
	    cifReadScale1 *= rescale;
	    CIFInputRescale(rescale, 1);
	    pointp->p_x *= rescale;
	}
    }
    pointp->p_x /= cifReadScale2;
    if (!CIFParseSInteger(&pointp->p_y))
	return FALSE;
    pointp->p_y *= (cifReadScale1 * iscale);
    if (pointp->p_y % cifReadScale2 != 0)
    {
	rescale = cifReadScale2 / FindGCF(cifReadScale2, abs(pointp->p_y));
	if ((cifReadScale1 * rescale) > CIFRescaleLimit)
	{
	    CIFReadWarning("CIF units at maximum scale; value is rounded\n");
	    /* prepare for nearest-integer rounding */
	    if (pointp->p_y < 0)
		pointp->p_y -= ((cifReadScale2 - 1) >> 1);
	    else
		pointp->p_y += (cifReadScale2  >> 1);
	}
	else
	{
	    cifReadScale1 *= rescale;
	    CIFInputRescale(rescale, 1);
	    pointp->p_x *= rescale;
	    pointp->p_y *= rescale;
	}
    }
    pointp->p_y /= cifReadScale2;
    return TRUE;
}


/*
 * ----------------------------------------------------------------------------
 *
 * CIFParsePath --
 *
 * 	This procedure parses a CIF path, which is sequence of
 *	one or more points.
 *
 * Results:
 *	TRUE is returned if the path was parsed successfully,
 *	FALSE otherwise.
 *
 * Side effects:
 *	Modifies the parameter pathheadpp to point to the path
 *	that is constructed.
 *
 * Corrections:
 *	CIF coordinates are multiplied by 2 to cover the case where
 *	the path centerline lies on the half lambda grid but the line
 * 	itself is on-grid.  This can't be done for polygons, so a
 *	parameter "iscale" (internal scale) is added, and set to 1 for
 *	polygons, 2 for wires when calling CIFParsePath().
 *
 * ----------------------------------------------------------------------------
 */

bool
CIFParsePath(pathheadpp, iscale)
    CIFPath **pathheadpp;
    int iscale;
{
    CIFPath *pathtailp, *newpathp;
    bool nonManhattan = FALSE;		/* diagnostic only */
    CIFPath path;
    int savescale;

    *pathheadpp = NULL;
    pathtailp = NULL;
    path.cifp_next = NULL;
    while (TRUE)
    {
	CIFSkipSep();
	if (PEEK() == ';')
	    break;

	savescale = cifReadScale1;
	if (!CIFParsePoint(&path.cifp_point, iscale))
	{
	    CIFFreePath(*pathheadpp);
	    return FALSE;
	}
	if (savescale != cifReadScale1)
	{
	    CIFPath *phead = *pathheadpp;
	    int newscale = cifReadScale1 / savescale;
	    while (phead != NULL)
	    {
		phead->cifp_x *= newscale;
		phead->cifp_y *= newscale;
		phead = phead->cifp_next;
	    }
	}
	newpathp = (CIFPath *) mallocMagic((unsigned) (sizeof (CIFPath)));
	*newpathp = path;
	if (*pathheadpp)
	{
	    /*
	     * Check that this segment is Manhattan.  If not, remember the
	     * fact and later introduce extra stair-steps to make the path
	     * Manhattan.  We don't do the stair-step introduction here for
	     * two reasons: first, the same code is also used by the Calma
	     * module, and second, it is important to know which side of
	     * the polygon is the outside when generating the stair steps.
	     */
	    if (pathtailp->cifp_x != newpathp->cifp_x
		    && pathtailp->cifp_y != (newpathp->cifp_y))
	    {
#ifndef NONMANHATTAN
		if (!nonManhattan)
		    CIFReadWarning("non-Manhattan path; using stairstep.\n");
#endif
		nonManhattan = TRUE;
	    }
	    pathtailp->cifp_next = newpathp;
	}
	else *pathheadpp = newpathp;
	pathtailp = newpathp;
    }
    return (*pathheadpp != NULL);
}

#ifdef NONMANHATTAN

/*
 * ----------------------------------------------------------------------------
 *
 * test_insideness --
 *
 *	Determine if a point is inside a rectangle defined by the
 *	first three points in the given CIF path.
 *
 * Results:
 *	TRUE if point is inside, FALSE if outside or on the border 	
 *
 * Side effects:
 *	None.
 * ----------------------------------------------------------------------------
 */

bool
test_insideness(start, tpoint)
    CIFPath *start;
    Point *tpoint;
{
    Rect tmprect, irect;

    tmprect.r_xbot = start->cifp_x;
    tmprect.r_ybot = start->cifp_y;
    tmprect.r_xtop = start->cifp_next->cifp_next->cifp_x;
    tmprect.r_ytop = start->cifp_next->cifp_next->cifp_y;

    GeoCanonicalRect(&tmprect, &irect);

    return ((tpoint->p_x > irect.r_xbot)
		&& (tpoint->p_x < irect.r_xtop)
		&& (tpoint->p_y > irect.r_ybot)
		&& (tpoint->p_y < irect.r_ytop)) ?  TRUE : FALSE;
}

/*
 * ----------------------------------------------------------------------------
 *
 * seg_intersect --
 *
 *	Determine if two line segments intersect or touch
 *	Expects first line to be manhattan.
 *
 * Results:
 *	returns TRUE if segments intersect, FALSE otherwise
 *
 * Side effects:
 *	value of respt contains point to which segment will be
 *	truncated.
 *	
 * ----------------------------------------------------------------------------
 */

bool
seg_intersect(tstart, bf, bs, respt)
    CIFPath *tstart;
    Point *bf, *bs;
    Point *respt;
{
    int afx = tstart->cifp_x;
    int afy = tstart->cifp_y;
    int asx = tstart->cifp_next->cifp_x;
    int asy = tstart->cifp_next->cifp_y;
    int adx, ady;

    if (afx == asx)	/* "a" is a vertical line */
    {
	adx = afx + ((tstart->cifp_next->cifp_next->cifp_x > afx) ? 1 : -1);

	/* Ignore if b does not cross the x boundary of ad */
	if ((bf->p_x > adx && bs->p_x > adx) ||
	    	(bf->p_x < adx && bs->p_x < adx))
	    return FALSE;

	if (bs->p_x == bf->p_x)		/* nonintersecting vertical lines */
	    return FALSE;

	respt->p_x = afx;
        respt->p_y = bf->p_y + (int)
			(((dlong)(bs->p_y - bf->p_y) * (dlong)(afx - bf->p_x)) /
			(dlong)(bs->p_x - bf->p_x));
	if (((respt->p_y > afy) && (respt->p_y < asy)) ||
		((respt->p_y < afy) && (respt->p_y > asy)))
	    return TRUE;
    }
    else 	/* (afy == asy), "a" is a horizontal line */
    {
	ady = afy + ((tstart->cifp_next->cifp_next->cifp_y > afy) ? 1 : -1);

	/* Ignore if b does not cross the y boundary of ad */
	if ((bf->p_y > ady && bs->p_y > ady) ||
	    	(bf->p_y < ady && bs->p_y < ady))
	    return FALSE;

	if (bs->p_y == bf->p_y)		/* nonintersecting horizontal lines */
	    return FALSE;

	respt->p_y = afy;
        respt->p_x = bf->p_x + (int)
			(((dlong)(bs->p_x - bf->p_x) * (dlong)(afy - bf->p_y)) /
			(dlong)(bs->p_y - bf->p_y));
	if (((respt->p_x > afx) && (respt->p_x < asx)) ||
		((respt->p_x < afx) && (respt->p_x > asx)))
	    return TRUE;
    }
    return FALSE;
}

/*
 * ----------------------------------------------------------------------------
 *
 * path_intersect --
 *
 *   Determine if a path intersects the given line segment.
 *   A path sharing a portion of the segment is not an intersection.
 *
 * ----------------------------------------------------------------------------
 */

bool
path_intersect(pathHead, start, respt)
    CIFPath *pathHead, *start;
    Point *respt;
{
    CIFPath *path, *segcrossed, *new;
    Point tmppt;
    bool does_cross = FALSE, diagonal = FALSE;
    int tdist, newdist;

    tdist = newdist = INFINITY;
    for (path = pathHead; path->cifp_next; path = path->cifp_next)
    {
	/* don't compare with self */
	if (path == start || path == start->cifp_next) continue;

	/* Does the path intersect the first line of the	*/
	/* right triangle, continuing in the direction of	*/
	/* the last point on the triangle?			*/

	if (seg_intersect(start, &path->cifp_point,
		&path->cifp_next->cifp_point, &tmppt))
	{
	    newdist = (start->cifp_x - tmppt.p_x) + (start->cifp_y - tmppt.p_y);
	    diagonal = TRUE;
	}

	/* Is the point inside the triangle, and the path is Manhattan?	*/
	/* (Note that *both* tests can be true, in which case the one	*/
	/* with the smaller absolute distance takes precedence.)	*/

	if (test_insideness(start, &path->cifp_point)) {
	    int tmpdist = abs(newdist);		/* save this value */	
	    if (path->cifp_x == path->cifp_next->cifp_x ||
			path->cifp_y == path->cifp_next->cifp_y)
	    {
		if (start->cifp_x == start->cifp_next->cifp_x)
		{
		    newdist = path->cifp_y - start->cifp_y;
		    if (abs(newdist) < tmpdist)
		    {
			tmppt.p_x = start->cifp_x;
			tmppt.p_y = path->cifp_y;
			diagonal = FALSE;
		    }
		}
		else
		{
		    newdist = path->cifp_x - start->cifp_x;
		    if (abs(newdist) < tmpdist)
		    {
			tmppt.p_y = start->cifp_y;
			tmppt.p_x = path->cifp_x;
			diagonal = FALSE;
		    }
		}
	    }
	}
	else if (diagonal == FALSE)
	    continue;

	newdist = abs(newdist);
	if ((!does_cross) || (newdist < tdist))
	{
	    does_cross = TRUE;
	    respt->p_x = tmppt.p_x;
	    respt->p_y = tmppt.p_y;
	    tdist = newdist;
	    segcrossed = (diagonal) ? path : NULL;
	}
    }

    /* If we're limited by another side of the polygon, then we're */
    /* guaranteed that we'll have to add another point there.  By  */
    /* doing it here, we avoid problems due to roundoff errors.	   */

    if (does_cross && segcrossed)
    {
	new = (CIFPath *) mallocMagic((unsigned) (sizeof (CIFPath)));
	new->cifp_next = segcrossed->cifp_next;
	segcrossed->cifp_next = new;
 	new->cifp_x = respt->p_x;
 	new->cifp_y = respt->p_y;
    }
    return does_cross;
}

/*
 * ----------------------------------------------------------------------------
 *
 * is_clockwise --
 *
 *	Determine if a path is clockwise or counterclockwise.
 *
 * Results:
 *	TRUE if the path is clockwise, FALSE otherwise.
 *
 * Side effects:
 *	None.
 *
 * ----------------------------------------------------------------------------
 */

bool
is_clockwise(pathHead)
    CIFPath *pathHead;
{
    CIFPath *path, *midx = NULL, *last;
    Point *p1, *p2, *p3;
    dlong sdir;
    int minx = INFINITY;

    /* Find out if this is a clockwise or counterclockwise path by	*/
    /* finding the (a) leftmost point and assuming the polygon to fill	*/
    /* is to the right.							*/

    for (path = pathHead; path->cifp_next; path = path->cifp_next)
    {
	if (path->cifp_next->cifp_x < minx)
	{
	    minx = path->cifp_next->cifp_x;
	    midx = path->cifp_next;
	    last = path;
	}
    }

    if (!midx) return TRUE;	/* one-point polygon? */

    /* Rare case of colinear points (implies degenerate polygon) requires */
    /* moving along pointlist until points are not colinear and repeating */
    /* the search for the minimum.					  */

    if (last->cifp_x == midx->cifp_x)
    {
	for (path = pathHead; path && path->cifp_x == minx;
			path = path->cifp_next);
	if (!path) return TRUE; /* completely degenerate; direc. irrelevant */
	minx = INFINITY;
	for (; path->cifp_next; path = path->cifp_next)
	{
	    if (path->cifp_next->cifp_x < minx)
	    {
		minx = path->cifp_next->cifp_x;
		midx = path->cifp_next;
		last = path;
	    }
	}
    }

    if (!(midx->cifp_next)) midx = pathHead;

    /* p2 is the (a) leftmost point; p1 and p3 are the points before	*/
    /* and after in the CIF path, respectively.				*/

    p1 = &(last->cifp_point);
    p2 = &(midx->cifp_point);
    p3 = &(midx->cifp_next->cifp_point);

    /* Find which side p3 falls on relative to the line p1-p2.  This	*/
    /* determines whether the path is clockwise or counterclockwise.	*/
    /* Use type dlong to avoid integer overflow.			*/

    sdir = ((dlong)(p2->p_x - p1->p_x) * (dlong)(p3->p_y - p1->p_y) -
		(dlong)(p2->p_y - p1->p_y) * (dlong)(p3->p_x - p1->p_x));

    return (sdir < 0) ? TRUE : FALSE;
}

/*
 * ----------------------------------------------------------------------------
 *
 * CIFMakeManhattanPath --
 *
 *	Convert a non-Manhattan path into a Manhattan one by
 *	breaking out triangles and leaving all Manhattan edges.
 *	Additional points are added which reroute the CIF path
 *	around the triangle.  In the simplest case, each non-Manhattan
 *	edge becomes a split tile bounding the edge endpoints.
 *	However, if that split tile would extend beyond the boundary
 *	of the CIF path, the edge is subdivided into as many
 *	triangles as are necessary to complete the path while remaining
 *	within the polygon boundary.  Unfortunately, for non-45-degree
 *	edges, the edge subdivision might not fall on an integer lambda
 *	value, so the resulting edge could be off by as much as 1/2
 *	lambda.  In this case, flag a warning.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	May insert additional points in the path.
 *	May alter the intended geometry of a non-manhattan edge by as
 *		much as 1/2 lambda.
 *
 * ----------------------------------------------------------------------------
 */

void
CIFMakeManhattanPath(pathHead, plane, resultTbl, ui)
    CIFPath *pathHead;
    Plane *plane;
    PaintResultType *resultTbl;
    PaintUndoInfo *ui;
{
    CIFPath *new, *new2, *next, *path;
    int xinit, xdiff, xincr, xlast, x;
    int yinit, ydiff, yincr, ylast, y;

    bool clockwise;
    CIFPath *first, *last;
    Rect tt, tr;
    TileType type;

    clockwise = is_clockwise(pathHead);

    for (path = pathHead; path->cifp_next; path = path->cifp_next)
    {
	Point clipbase;
	int edir;
	next = path->cifp_next;

	/* No work if this segment is Manhattan */
	if (path->cifp_x == next->cifp_x || path->cifp_y == next->cifp_y)
	    continue;

	/* Otherwise, break out the triangle, then adjust as necessary */

	new = (CIFPath *) mallocMagic((unsigned) (sizeof (CIFPath)));
	path->cifp_next = new;
	new->cifp_next = next;

	/* Generate split tiles as necessary to reach next->cifp_y */

	if (clockwise)
	{
	    first = next;
	    last = path;
	}
	else
	{
	    first = path;
	    last = next;
	}
	edir = CIFEdgeDirection(first, last);
	if (edir == CIF_DIAG_DL || edir == CIF_DIAG_UR)
	{
	    new->cifp_x = first->cifp_x; 
	    new->cifp_y = last->cifp_y;
	}
	else  /* edir == CIF_DIAG_DR || edir == CIF_DIAG_UL */
	{
	    new->cifp_x = last->cifp_x;
	    new->cifp_y = first->cifp_y;
	}

	/* Check if the segment from first to base intersects	*/
	/* the polygon edge 				  	*/

	if (path_intersect(pathHead, path, &clipbase))
	{
	    new->cifp_x = clipbase.p_x;
	    new->cifp_y = clipbase.p_y;

	    new2 = (CIFPath *) mallocMagic((unsigned) (sizeof (CIFPath)));
	    new->cifp_next = new2;
	    new2->cifp_next = next;

	    /* Use double long for the multiplication and	*/
	    /* division, or else integer overflow can occur.	*/

	    if (path->cifp_x == new->cifp_x)	/* vertical line */
	    {
		new2->cifp_y = new->cifp_y;
		new2->cifp_x = path->cifp_x + (int)
			 ((dlong)(new2->cifp_y - path->cifp_y)
			* (dlong)(next->cifp_x - path->cifp_x)
			/ (dlong)(next->cifp_y - path->cifp_y));
	    }
	    else
	    {
		new2->cifp_x = new->cifp_x;
		new2->cifp_y = path->cifp_y + (int)
			 ((dlong)(new2->cifp_x - path->cifp_x)
			* (dlong)(next->cifp_y - path->cifp_y)
			/ (dlong)(next->cifp_x - path->cifp_x));
	    }
	}

	/* Break out the diagonal tile from the polygon and paint it. */

        type = (edir == CIF_DIAG_UR || edir == CIF_DIAG_UL) ? 0 : TT_SIDE;
        type |= (edir == CIF_DIAG_UR || edir == CIF_DIAG_DL) ? 0 : TT_DIRECTION;
	type |= TT_DIAGONAL;

	tt.r_ll = path->cifp_point;
	tt.r_ur = path->cifp_next->cifp_next->cifp_point;
	GeoCanonicalRect(&tt, &tr);

//	TxPrintf("CIF read: Triangle %s %c at (%d, %d) plane %x\n",
//		(type & TT_SIDE) ? "right" : "left", (type & TT_DIRECTION)
//		? '\\' : '/', tt.r_xbot, tt.r_ybot, plane);

	/* Final check---ensure that rectangle is not degenerate */

        if (plane && (tr.r_xtop - tr.r_xbot > 0) && (tr.r_ytop - tr.r_ybot > 0))
            DBNMPaintPlane(plane, type, &tr, resultTbl, ui);
    }
}

#else


/*
 * ----------------------------------------------------------------------------
 *
 * CIFMakeManhattanPath --
 *
 *	Convert a non-Manhattan path into a Manhattan one by adding
 *	additional points.  These points are added using a simple
 *	scan-conversion algorithm that generates a series of stair
 *	steps that are at least cifCurReadStyle->crs_scaleFactor
 *	units high and wide (but which may be higher or wider).
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	May insert additional points in the path.
 *
 * ----------------------------------------------------------------------------
 */

void
CIFMakeManhattanPath(pathHead)
    CIFPath *pathHead;
{
    CIFPath *new, *next, *path;
    int xinit, xdiff, xincr, xlast, x;
    int yinit, ydiff, yincr, ylast, y;

    for (path = pathHead; path->cifp_next; path = path->cifp_next)
    {
	next = path->cifp_next;

	/* No work if this segment is Manhattan */
	if (path->cifp_x == next->cifp_x || path->cifp_y == next->cifp_y)
	    continue;

	/*
	 * The major loop will be over whichever difference (x or y)
	 * is the SMALLER of the two; for each iteration over the smaller
	 * dimension, we will add the number of units of the larger
	 * dimension per unit of the smaller dimension to the larger
	 * dimension.
	 */
	xdiff = next->cifp_x - path->cifp_x;
	ydiff = next->cifp_y - path->cifp_y;
	xinit = path->cifp_x;
	yinit = path->cifp_y;
	if (ABS(xdiff) > ABS(ydiff))
	{
	    /* Iterate over y, stopping before next->cifp_y */
	    yincr = cifCurReadStyle->crs_scaleFactor;
	    ylast = yinit;
	    if (ydiff < 0) yincr = -yincr;
	    for (y = yinit + yincr, x = xinit;
			(yincr > 0 && y < next->cifp_y)
		     || (yincr < 0 && y > next->cifp_y);
		    y += yincr)
	    {
		/* Move by one in y first */
		new = (CIFPath *) mallocMagic((unsigned) (sizeof (CIFPath)));
		new->cifp_x = x;
		new->cifp_y = y;
		path->cifp_next = new;
		path = new;

		/*
		 * Now move in x.
		 * Note that ((y - yinit) / ydiff) >= 0 always.
		 * Also, as long as y has not reached next->cifp_y,
		 * this quantity will be < 1, so x will range from
		 * path->cifp_x up to but not reaching next->cifp_x.
		 */
		x = xinit + (xdiff * (y - yinit)) / ydiff;
		new = (CIFPath *) mallocMagic ((unsigned) (sizeof (CIFPath)));
		new->cifp_x = x;
		new->cifp_y = y;
		new->cifp_next = next;
		path->cifp_next = new;
		path = new;
		ylast = y;
	    }

	    /*
	     * The last y processed was short of next->cifp_y.
	     * If x was not yet at next->cifp_x, add one more point
	     * to bridge the gap.
	     */
	    if (x != next->cifp_x)
	    {
		new = (CIFPath *) mallocMagic((unsigned) (sizeof (CIFPath)));
		new->cifp_x = next->cifp_x;
		new->cifp_y = ylast;;
		new->cifp_next = next;
		path->cifp_next = new;
		path = new;
	    }
	}
	else
	{
	    /* Iterate over x, stopping before next->cifp_x */
	    xincr = cifCurReadStyle->crs_scaleFactor;
	    xlast = xinit;
	    if (xdiff < 0) xincr = -xincr;
	    for (x = xinit + xincr, y = yinit;
			(xincr > 0 && x < next->cifp_x)
		     || (xincr < 0 && x > next->cifp_x);
		x += xincr)
	    {
		/* Move by one in x first */
		new = (CIFPath *) mallocMagic((unsigned) (sizeof (CIFPath)));
		new->cifp_x = x;
		new->cifp_y = y;
		path->cifp_next = new;
		path = new;

		/*
		 * Now move in y.
		 * Note that ((x - xinit) / xdiff) >= 0 always.
		 * Also, as long as x has not reached next->cifp_x,
		 * this quantity will be < 1, so y will range from
		 * path->cifp_y up to but not reaching next->cifp_y.
		 */
		y = yinit + (ydiff * (x - xinit)) / xdiff;
		new = (CIFPath *) mallocMagic((unsigned) (sizeof (CIFPath)));
		new->cifp_x = x;
		new->cifp_y = y;
		new->cifp_next = next;
		path->cifp_next = new;
		path = new;
		xlast = x;
	    }

	    /*
	     * The last x processed was short of next->cifp_x.
	     * If y was not yet at next->cifp_y, add one more point
	     * to bridge the gap.
	     */
	    if (y != next->cifp_y)
	    {
		new = (CIFPath *) mallocMagic((unsigned) (sizeof (CIFPath)));
		new->cifp_x = xlast;
		new->cifp_y = next->cifp_y;
		new->cifp_next = next;
		path->cifp_next = new;
		path = new;
	    }
	}
    }
}

#endif /* NONMANHATTAN */


/*
 * ----------------------------------------------------------------------------
 *
 * CIFEdgeDirection --
 *
 * 	This procedure assigns a direction to the given edge.
 *
 * Results:
 *	CIF_ZERO	if the two points are the same
 *	CIF_LEFT	if the edge goes left
 *	CIF_UP		if the edge goes up
 *	CIF_RIGHT	if the edge goes right
 *	CIF_DOWN	if the edge goes down
 *	CIF_DIAG	if the edge is non-manhattan
 *
 * Side effects:
 *	None.
 *
 * ----------------------------------------------------------------------------
 */

int
CIFEdgeDirection(first, last)
    CIFPath *first, *last;		/* Edge to be categorized. */
{

    if (first->cifp_x < last->cifp_x)
    {
	if (first->cifp_y < last->cifp_y)
	    return CIF_DIAG_UR;
	if (first->cifp_y > last->cifp_y)
	    return CIF_DIAG_DR;
	return CIF_RIGHT;
    }
    if (first->cifp_x > last->cifp_x)
    {
	if (first->cifp_y < last->cifp_y)
	    return CIF_DIAG_UL;
	if (first->cifp_y > last->cifp_y)
	    return CIF_DIAG_DL;
	return CIF_LEFT;
    }
    if (first->cifp_y < last->cifp_y)
	return CIF_UP;
    if (first->cifp_y > last->cifp_y)
	return CIF_DOWN;
    return CIF_ZERO;
}

/*
 * ----------------------------------------------------------------------------
 *
 * CIFCleanPath --
 *
 *	Removes a edge in a path if it has zero length (repeated points).
 *	Combines two consecutive edges if their direction is the same,
 *	and their direction is manhattan.
 *	CIFCleanPath assumes that the path is closed, and will eliminate
 *	the last edge if its direction is the same as the first.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	May delete points in the path.
 *
 * ----------------------------------------------------------------------------
 */

void
CIFCleanPath(pathHead)
    CIFPath *pathHead;
{
    CIFPath *next, *path, *prev, *last;
    int dir1, dir2;

    if (!pathHead) return;
    prev = pathHead;
    path = prev->cifp_next;
    if (!path) return;
    while((dir1 = CIFEdgeDirection(prev, path)) == CIF_ZERO)
    {
	/* This is a repeated point. */
	next = path->cifp_next;
	prev->cifp_next = next;
	freeMagic((char *) path);
	path = next;
	if (!path) return;
    }
	 
    while (next = path->cifp_next)
    {
	if ((dir2 = CIFEdgeDirection(path, next)) == CIF_ZERO)
	{
	    /* This is a repeated point. */
	    path->cifp_next = next->cifp_next;
	    freeMagic((char *) next);
	    continue;
	}

	/* Skip any non-manhattan (diagonal) edges. */
	if (dir2 >= CIF_DIAG)
	    goto path_inc;

	if (dir1 == dir2)
	{
	    /* The middle point must go. */
	    prev->cifp_next = next;
	    freeMagic((char *) path);
	    path = next;
	    dir1 = CIFEdgeDirection(prev, path);
	    continue;
	}
path_inc:
	dir1 = dir2;
	prev = path;
	path = next;
    }

    /* Ensure that the path has more than one point. */
    if (!pathHead->cifp_next)
    {
	/* Ensure that the resulting path is closed. */
	if ((pathHead->cifp_x != path->cifp_x) ||	
	    (pathHead->cifp_y != path->cifp_y))
	{
	    next = (CIFPath *) mallocMagic((unsigned) (sizeof (CIFPath)));
	    next->cifp_x = pathHead->cifp_x;
	    next->cifp_y = pathHead->cifp_y;
	    next->cifp_next = (CIFPath *) 0;
	    path->cifp_next = next;
	    prev = path;
	    path = next;
	    dir1 = CIFEdgeDirection(prev, path);
	}
	if ((dir2 = CIFEdgeDirection(pathHead, pathHead->cifp_next)) <
	    CIF_DIAG)
	{
	    /* We have at least two edges in the path. We have to */
	    /* fix the first edge and eliminate the last edge if */
	    /* the first and last edge have the same direction. */
	    if (dir1 == dir2)
	    {
		pathHead->cifp_x = prev->cifp_x;
		pathHead->cifp_y = prev->cifp_y;
		prev->cifp_next = (CIFPath *) 0;
		freeMagic((char *) path);
	    }
	}
    }
}

/*
 * ----------------------------------------------------------------------------
 *
 * CIFFreePath --
 *
 * 	This procedure frees up a path once it has been used.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	All the elements of path are returned to the storage allocator.
 *
 * ----------------------------------------------------------------------------
 */

void
CIFFreePath(path)
    CIFPath *path;		/* Path to be freed. */
{
    while (path != NULL)
    {
	freeMagic((char *) path);
	path = path->cifp_next;
    }
}

/*
 * ----------------------------------------------------------------------------
 *
 * cifCommandError --
 *
 * 	This procedure is called when unknown CIF commands are found
 *	in CIF files.  It skips the command and advances to the next
 *	command.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	None.
 *
 * ----------------------------------------------------------------------------
 */

void
cifCommandError()
{
    CIFReadError("unknown command `%c'; ignored.\n" , PEEK());
    CIFSkipToSemi();
}


/*
 * ----------------------------------------------------------------------------
 *
 * cifParseEnd --
 *
 * 	This procedure processes the "end" statement in a CIF file
 *	(it ignores it).
 *
 * Results:
 *	Always returns TRUE.
 *
 * Side effects:
 *	None.
 *
 * ----------------------------------------------------------------------------
 */

bool
cifParseEnd()
{
    TAKE();
    CIFSkipBlanks();
    if (PEEK() != EOF)
    {
	CIFReadError("End command isn't at end of file.\n");
	return FALSE;
    }
    return TRUE;
}

/*
 * ----------------------------------------------------------------------------
 *
 * cifParseComment --
 *
 * 	This command skips over user comments in CIF files.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	None.
 *
 * ----------------------------------------------------------------------------
 */

bool
cifParseComment()
{
    int		opens;
    int		ch;
    
	/*
	 *	take the '('
	 */
    TAKE();
    opens = 1;
    do
    {
	ch = TAKE();
	if (ch == '(')
	    opens++;
	else if (ch == ')')
	    opens--;
	else if (ch == '\n')
	{
	    cifLineNumber++;
	}
	else if (ch == EOF)
	{
	    CIFReadError("(comment) extends to end of file.\n");
	    return FALSE;
	}
    } while (opens > 0);
    return TRUE;
}

/*
 * ----------------------------------------------------------------------------
 *
 * CIFDirectionToTrans --
 *
 * 	This procedure is used to convert from a direction vector
 *	to a Magic transformation.  The direction vector is a point
 *	giving a direction from the origin.  It better be along
 *	one of the axes.
 *
 * Results:
 *	The return value is the transformation corresponding to
 *	the direction, or the identity transform if the direction
 *	isn't along one of the axes.
 *
 * Side effects:
 *	None.
 *
 * ----------------------------------------------------------------------------
 */

Transform *
CIFDirectionToTrans(point)
    Point *point;		/* Direction vector from origin. */
{
    if ((point->p_x != 0) && (point->p_y == 0))
    {
	if (point->p_x > 0)
	    return &GeoIdentityTransform;
	else return &Geo180Transform;
    }
    else if ((point->p_y != 0) && (point->p_x == 0))
    {
	if (point->p_y > 0)
	    return &Geo270Transform;
	else return &Geo90Transform;
    }
    CIFReadError("non-manhattan direction vector (%d, %d); ignored.\n",
	point->p_x, point->p_y);
    return &GeoIdentityTransform;
}

/*
 * ----------------------------------------------------------------------------
 *
 * CIFParseTransform --
 *
 * 	This procedure is called to read in a transform from a
 *	CIF file.
 *
 * Results:
 *	TRUE is returned if the parse completed successfully, and
 *	FALSE is returned otherwise.
 *
 * Side effects:
 *	The parameter pointed to by transformp is modified to
 *	contain the transform indicated by the CIF file.
 *
 * ----------------------------------------------------------------------------
 */

bool
CIFParseTransform(transformp)
    Transform	*transformp;
{
    char	ch;
    Point	point;
    Transform	tmp;
    int		savescale;

    *transformp = GeoIdentityTransform;
    CIFSkipBlanks();
    for (ch = PEEK() ; ch != ';' ; ch = PEEK())
    {
	switch (ch)
	{
	    case 'T':
		    TAKE();
		    if (!CIFParsePoint(&point, 1))
		    {
			CIFReadError("translation, but no point.\n");
			CIFSkipToSemi();
			return FALSE;
		    }
		    GeoTranslateTrans(transformp, point.p_x, point.p_y, &tmp);
		    *transformp = tmp;
		    break;
	    case 'M':
		    TAKE();
		    CIFSkipBlanks();
		    ch = PEEK();
		    if (ch == 'X')
		        GeoTransTrans(transformp, &GeoSidewaysTransform, &tmp);
		    else if (ch == 'Y')
		        GeoTransTrans(transformp, &GeoUpsideDownTransform,
				&tmp);
		    else
		    {
			CIFReadError("mirror, but not in X or Y.\n");
			CIFSkipToSemi();
			return FALSE;
		    }
		    TAKE();
		    *transformp = tmp;
		    break;
	    case 'R':
		    TAKE();
		    if (!CIFParseSInteger(&point.p_x) ||
			    !CIFParseSInteger(&point.p_y))
		    {
			CIFReadError("rotation, but no direction.\n");
			CIFSkipToSemi();
			return FALSE;
		    }
		    GeoTransTrans(transformp, CIFDirectionToTrans(&point),
			    &tmp);
		    *transformp = tmp;
		    break;
	    default:
		    CIFReadError("transformation expected.\n");
		    CIFSkipToSemi();
		    return FALSE;
	}
	CIFSkipBlanks();
    }

    /* Before returning, we must scale the transform into Magic units. */

    transformp->t_c = CIFScaleCoord(transformp->t_c, COORD_EXACT);
    savescale = cifCurReadStyle->crs_scaleFactor;
    transformp->t_f = CIFScaleCoord(transformp->t_f, COORD_EXACT);
    if (savescale != cifCurReadStyle->crs_scaleFactor)
	transformp->t_c *= (savescale / cifCurReadStyle->crs_scaleFactor);

    return TRUE;
}

/*
 * ----------------------------------------------------------------------------
 *
 * CIFParseCommand --
 *
 * 	Parse one CIF command and farm it out to a routine to handle
 *	that command.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	May modify the contents of cifReadCellDef by painting or adding
 *	new uses or labels.  May also create new CellDefs.
 *
 * ----------------------------------------------------------------------------
 */

void
CIFReadFile(file)
    FILE *file;			/* File from which to read CIF. */
{
    /* We will use 1-word CIF numbers as keys in this hash table */
    CIFReadCellInit(1);

    if (cifCurReadStyle == NULL)
    {
	TxError("Don't know how to read CIF:  nothing in tech file.\n");
	return;
    }
    TxPrintf("Warning: CIF reading is not undoable!  I hope that's OK.\n");
    UndoDisable();

    cifTotalWarnings = 0;
    cifTotalErrors = 0;
#ifdef NONMANHATTAN
    CifPolygonCount = 0;
#endif

    cifInputFile = file;
    cifReadScale1 = 1;
    cifReadScale2 = 1;
    cifParseLaAvail = FALSE;
    cifLineNumber = 1;
    cifReadPlane = (Plane *) NULL;
    cifCurLabelType = TT_SPACE;
    while (PEEK() != EOF)
    {
	if (SigInterruptPending) goto done;
	CIFSkipBlanks();
	switch (PEEK())
	{
	    case EOF:
		    break;
	    case ';':
		    break;
	    case 'B':
		    (void) CIFParseBox();
		    break;
	    case 'C':
		    (void) CIFParseCall();
		    break;
	    case 'D':
		    TAKE();
		    CIFSkipBlanks();
		    switch (PEEK())
		    {
			case 'D':
				    (void) CIFParseDelete();
				    break;
			case 'F':
				    (void) CIFParseFinish();
				    break;
			case 'S':
				    (void) CIFParseStart();
				    break;
			default:
				    cifCommandError();
				    break;
		    }
		    break;
	    case 'E':
		    (void) cifParseEnd();
		    goto done;
	    case 'L':
		    (void) CIFParseLayer();
		    break;
	    case 'P':
		    (void) CIFParsePoly();
		    break;
	    case 'R':
		    (void) CIFParseFlash();
		    break;
	    case 'W':
		    (void) CIFParseWire();
		    break;
	    case '(':
		    (void) cifParseComment();
		    break;
	    case '0': case '1': case '2': case '3': case '4':
	    case '5': case '6': case '7': case '8': case '9':
		    (void) CIFParseUser();
		    break;
	    default:
		    cifCommandError();
		    break;
	}
	CIFSkipSemi();
    }

    CIFReadError("no \"End\" statement.\n");

    done:
    CIFReadCellCleanup();
    UndoEnable();
}
