/*
 * Electric(tm) VLSI Design System
 *
 * File: iolefi.c
 * Input/output aid: LEF (Library Exchange Format) reader
 * Written by: Steven M. Rubin, Static Free Software
 *
 * Copyright (c) 2000 Static Free Software.
 *
 * Electric(tm) is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * Electric(tm) is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with Electric(tm); see the file COPYING.  If not, write to
 * the Free Software Foundation, Inc., 59 Temple Place, Suite 330,
 * Boston, Mass 02111-1307, USA.
 *
 * Static Free Software
 * 4119 Alpine Road
 * Portola Valley, California 94028
 * info@staticfreesoft.com
 */

/*
 * Note that this reader was built by examining LEF files and reverse-engineering them.
 * It does not claim to be compliant with the LEF specification, but it also does not
 * claim to define a new specification.  It is merely incomplete.
 */

#include "config.h"
#if IOLEF

#include "global.h"
#include "egraphics.h"
#include "efunction.h"
#include "edialogs.h"
#include "iolefdef.h"
#include "tecart.h"
#include "tecgen.h"
#include "eio.h"
#include "usr.h"

/*************** LEF PATHS ***************/

#define NOLEFPATH ((LEFPATH *)-1)

typedef struct Ilefpath
{
	INTBIG     x[2], y[2];
	NODEINST  *ni[2];
	INTBIG     width;
	ARCPROTO  *arc;
	struct Ilefpath *nextlefpath;
} LEFPATH;

LEFPATH *io_leffreepath = NOLEFPATH;

/*************** MISCELLANEOUS ***************/

#define MAXLINE 2500

INTBIG  io_leffilesize;
INTBIG  io_leflineno;
char    io_lefline[MAXLINE];
INTBIG  io_leflinepos;
VIADEF *io_leffirstviadef = NOVIADEF;

void       io_leffreepaths(LEFPATH *firstlp);
ARCPROTO  *io_lefgetarc(INTBIG afunc);
char      *io_lefgetkeyword(FILE *f);
NODEPROTO *io_lefgetnode(INTBIG lfunc);
INTSML     io_lefignoretosemicolon(FILE *f, char *command);
void       io_lefkillpath(LEFPATH *lp);
LEFPATH   *io_lefnewpath(void);
PORTPROTO *io_lefnewport(NODEPROTO *facet, NODEINST *ni, PORTPROTO *pp, char *thename);
void       io_lefparselayer(char *layername, INTBIG *arcfunc, INTBIG *layerfunc);
INTSML     io_lefreadfile(FILE *f, LIBRARY *lib);
INTSML     io_lefreadlayer(FILE *f, LIBRARY *lib);
INTSML     io_lefreadmacro(FILE *f, LIBRARY *lib);
INTSML     io_lefreadvia(FILE *f, LIBRARY *lib);
INTSML     io_lefreadobs(FILE *f, NODEPROTO *facet);
INTSML     io_lefreadpin(FILE *f, NODEPROTO *facet);
INTSML     io_lefreadport(FILE *f, NODEPROTO *facet, char *portname, INTBIG portbits);
void       io_leffreevias(void);

/*
 * Routine to free all memory associated with this module.
 */
void io_freelefimemory(void)
{
	io_leffreevias();
}

INTSML io_readleflibrary(LIBRARY *lib)
{
	FILE *fp;
	REGISTER INTSML ret;
	char *filename;
	extern DIALOG us_progressdialog;

	/* open the file */
	fp = xopen(lib->libfile, FILETYPELEF, "", &filename);
	if (fp == NULL)
	{
		ttyputerr("File %s not found", lib->libfile);
		return(1);
	}

	/* remove any vias in the globals */
	io_leffreevias();

	/* prepare for input */
	io_leffilesize = filesize(fp);
	if (DiaInitDialog(&us_progressdialog) != 0)
	{
		xclose(fp);
		return(1);
	}
	DiaPercent(1, 0);
	DiaSetText(2, "Reading LEF file...");
	io_leflineno = 0;
	io_leflinepos = 0;
	io_lefline[0] = 0;

	/* read the file */
	ret = io_lefreadfile(fp, lib);

	/* clean up */
	DiaDoneDialog();
	xclose(fp);
	if (ret == 0) ttyputmsg("LEF file %s is read", lib->libfile); else
		ttyputmsg("Error reading LEF file %s", lib->libfile);
	return(0);
}

void io_leffreevias(void)
{
	VIADEF *vd;

	while (io_leffirstviadef != NOVIADEF)
	{
		vd = io_leffirstviadef;
		io_leffirstviadef = io_leffirstviadef->nextviadef;
		efree((char *)vd->vianame);
		efree((char *)vd);
	}
}

/*
 * Routine to read the LEF file in "f".
 */
INTSML io_lefreadfile(FILE *f, LIBRARY *lib)
{
	REGISTER char *key;

	for(;;)
	{
		/* get the next keyword */
		key = io_lefgetkeyword(f);
		if (key == 0) break;
		if (namesame(key, "LAYER") == 0)
		{
			if (io_lefreadlayer(f, lib) != 0) return(1);
		}
		if (namesame(key, "MACRO") == 0)
		{
			if (io_lefreadmacro(f, lib) != 0) return(1);
		}
		if (namesame(key, "VIA") == 0)
		{
			if (io_lefreadvia(f, lib) != 0) return(1);
		}
	}
	return(0);
}

void io_lefparselayer(char *layername, INTBIG *arcfunc, INTBIG *layerfunc)
{
	REGISTER INTBIG laynum;

	*arcfunc = APUNKNOWN;
	*layerfunc = LFUNKNOWN;

	if (namesamen(layername, "METAL", 5) == 0)
	{
		laynum = atoi(&layername[5]);
		switch (laynum)
		{
			case 1: *arcfunc = APMETAL1;   *layerfunc = LFMETAL1;   break;
			case 2: *arcfunc = APMETAL2;   *layerfunc = LFMETAL2;   break;
			case 3: *arcfunc = APMETAL3;   *layerfunc = LFMETAL3;   break;
			case 4: *arcfunc = APMETAL4;   *layerfunc = LFMETAL4;   break;
			case 5: *arcfunc = APMETAL5;   *layerfunc = LFMETAL5;   break;
			case 6: *arcfunc = APMETAL6;   *layerfunc = LFMETAL6;   break;
			case 7: *arcfunc = APMETAL7;   *layerfunc = LFMETAL7;   break;
			case 8: *arcfunc = APMETAL8;   *layerfunc = LFMETAL8;   break;
		}
		return;
	}

	if (namesamen(layername, "POLY", 4) == 0)
	{
		laynum = atoi(&layername[4]);
		switch (laynum)
		{
			case 1: *arcfunc = APPOLY1;   *layerfunc = LFPOLY1;   break;
			case 2: *arcfunc = APPOLY2;   *layerfunc = LFPOLY2;   break;
			case 3: *arcfunc = APPOLY3;   *layerfunc = LFPOLY3;   break;
		}
		return;
	}

	if (namesame(layername, "PDIFF") == 0)
	{
		*arcfunc = APDIFFP;
		*layerfunc = LFDIFF | LFPTYPE;
		return;
	}
	if (namesame(layername, "NDIFF") == 0)
	{
		*arcfunc = APDIFFN;
		*layerfunc = LFDIFF | LFNTYPE;
		return;
	}
	if (namesame(layername, "DIFF") == 0)
	{
		*arcfunc = APDIFF;
		*layerfunc = LFDIFF;
		return;
	}

	if (namesame(layername, "CONT") == 0)
	{
		*layerfunc = LFCONTACT1;
		return;
	}
	if (namesamen(layername, "VIA", 3) == 0)
	{
		laynum = atoi(&layername[3]);
		switch (laynum)
		{
			case 1:
			case 12: *layerfunc = LFCONTACT2;   break;

			case 2:
			case 23: *layerfunc = LFCONTACT3;   break;

			case 3:
			case 34: *layerfunc = LFCONTACT4;   break;

			case 4:
			case 45: *layerfunc = LFCONTACT5;   break;

			case 5:
			case 56: *layerfunc = LFCONTACT6;   break;
		}
	}
}

INTSML io_lefreadlayer(FILE *f, LIBRARY *lib)
{
	REGISTER char *layername, *key;
	char curkey[200];
	REGISTER INTBIG defwidth;
	INTBIG afunc, layerfunc;
	REGISTER ARCPROTO *ap;
	float v;

	layername = io_lefgetkeyword(f);
	if (layername == 0)
	{
		ttyputerr("EOF parsing LAYER header");
		return(1);
	}
	io_lefparselayer(layername, &afunc, &layerfunc);
	if (afunc == APUNKNOWN) ap = NOARCPROTO; else
	{
		for(ap = el_curtech->firstarcproto; ap != NOARCPROTO; ap = ap->nextarcproto)
			if (((INTBIG)((ap->userbits&AFUNCTION)>>AFUNCTIONSH)) == afunc) break;
	}

	for(;;)
	{
		/* get the next keyword */
		key = io_lefgetkeyword(f);
		if (key == 0)
		{
			ttyputerr("EOF parsing LAYER");
			return(1);
		}

		if (namesame(key, "END") == 0)
		{
			key = io_lefgetkeyword(f);
			break;
		}

		if (namesame(key, "WIDTH") == 0)
		{
			key = io_lefgetkeyword(f);
			if (key == 0)
			{
				ttyputerr("EOF reading WIDTH");
				return(1);
			}
			v = (float)atof(key);
			defwidth = scalefromdispunit(v, DISPUNITMIC);
			if (ap != NOARCPROTO)
			{
				setval((INTBIG)ap, VARCPROTO, "IO_lef_width", defwidth, VINTEGER);
			}
			if (io_lefignoretosemicolon(f, "WIDTH") != 0) return(1);
			continue;
		}

		if (namesame(key, "TYPE") == 0 || namesame(key, "SPACING") == 0 ||
			namesame(key, "PITCH") == 0 || namesame(key, "DIRECTION") == 0 ||
			namesame(key, "CAPACITANCE") == 0 || namesame(key, "RESISTANCE") == 0)
		{
			strcpy(curkey, key);
			if (io_lefignoretosemicolon(f, curkey) != 0) return(1);
			continue;
		}
	}
	return(0);
}

INTSML io_lefreadmacro(FILE *f, LIBRARY *lib)
{
	REGISTER char *cellname, *key;
	REGISTER INTBIG ox, oy, blx, bhx, bly, bhy;
	INTBIG sx, sy;
	float x, y;
	REGISTER NODEPROTO *facet;
	REGISTER NODEINST *ni;
	char curkey[200];

	cellname = io_lefgetkeyword(f);
	if (cellname == 0)
	{
		ttyputerr("EOF parsing MACRO header");
		return(1);
	}
	(void)initinfstr();
	(void)addstringtoinfstr(cellname);
	(void)addstringtoinfstr("{sk}");
	cellname = returninfstr();
	facet = newnodeproto(cellname, lib);
	if (facet == NONODEPROTO)
	{
		ttyputerr("Cannot create facet '%s'", cellname);
		return(1);
	}

	for(;;)
	{
		/* get the next keyword */
		key = io_lefgetkeyword(f);
		if (key == 0)
		{
			ttyputerr("EOF parsing MACRO");
			return(1);
		}

		if (namesame(key, "END") == 0)
		{
			key = io_lefgetkeyword(f);
			break;
		}

		if (namesame(key, "SOURCE") == 0 || namesame(key, "FOREIGN") == 0 ||
			namesame(key, "SYMMETRY") == 0 || namesame(key, "SITE") == 0 ||
			namesame(key, "CLASS") == 0 || namesame(key, "LEQ") == 0)
		{
			strcpy(curkey, key);
			if (io_lefignoretosemicolon(f, curkey) != 0) return(1);
			continue;
		}

		if (namesame(key, "ORIGIN") == 0)
		{
			key = io_lefgetkeyword(f);
			if (key == 0)
			{
				ttyputerr("EOF reading ORIGIN X");
				return(1);
			}
			x = (float)atof(key);
			ox = scalefromdispunit(x, DISPUNITMIC);

			key = io_lefgetkeyword(f);
			if (key == 0)
			{
				ttyputerr("EOF reading ORIGIN Y");
				return(1);
			}
			y = (float)atof(key);
			oy = scalefromdispunit(y, DISPUNITMIC);
			if (io_lefignoretosemicolon(f, "ORIGIN") != 0) return(1);

			/* create the facet-center node */
			defaultnodesize(gen_facetcenterprim, &sx, &sy);
			blx = ox - sx/2;   bhx = blx + sx;
			bly = oy - sy/2;   bhy = bly + sy;
			ni = newnodeinst(gen_facetcenterprim, blx, bhx, bly, bhy, 0, 0, facet);
			if (ni == NONODEINST)
			{
				ttyputerr("Line %d: Cannot create facet center node", io_leflineno);
				return(1);
			}
			endobjectchange((INTBIG)ni, VNODEINST);
			continue;
		}

		if (namesame(key, "SIZE") == 0)
		{
			key = io_lefgetkeyword(f);
			if (key == 0)
			{
				ttyputerr("EOF reading SIZE X");
				return(1);
			}
			x = (float)atof(key);
			sx = scalefromdispunit(x, DISPUNITMIC);

			key = io_lefgetkeyword(f);
			if (key == 0)
			{
				ttyputerr("EOF reading SIZE 'BY'");
				return(1);
			}
			if (namesame(key, "BY") != 0)
			{
				ttyputerr("Line %d: Expected 'by' in SIZE", io_leflineno);
				return(1);
			}

			key = io_lefgetkeyword(f);
			if (key == 0)
			{
				ttyputerr("EOF reading SIZE Y");
				return(1);
			}
			y = (float)atof(key);
			sy = scalefromdispunit(y, DISPUNITMIC);
			if (io_lefignoretosemicolon(f, "SIZE") != 0) return(1);
			continue;
		}

		if (namesame(key, "PIN") == 0)
		{
			if (io_lefreadpin(f, facet) != 0) return(1);
			continue;
		}

		if (namesame(key, "OBS") == 0)
		{
			if (io_lefreadobs(f, facet) != 0) return(1);
			continue;
		}

		ttyputerr("Line %d: Unknown MACRO keyword (%s)", io_leflineno, key);
		return(1);
	}
	return(0);
}

INTSML io_lefreadvia(FILE *f, LIBRARY *lib)
{
	REGISTER char *vianame, *key;
	REGISTER INTBIG lx, hx, ly, hy, i, lay1found, lay2found;
	INTBIG afunc, layerfunc;
	NODEPROTO *np;
	ARCPROTO *ap;
	float v;
	REGISTER PORTPROTO *pp;
	REGISTER VIADEF *vd;

	/* get the via name */
	vianame = io_lefgetkeyword(f);
	if (vianame == 0) return(1);

	/* create a new via definition */
	vd = (VIADEF *)emalloc(sizeof (VIADEF), io_aid->cluster);
	if (vd == 0) return(1);
	(void)allocstring(&vd->vianame, vianame, io_aid->cluster);
	vd->sx = vd->sy = 0;
	vd->via = NONODEPROTO;
	vd->lay1 = vd->lay2 = NOARCPROTO;
	vd->nextviadef = io_leffirstviadef;
	io_leffirstviadef = vd;

	/* ignore the "DEFAULT" keyword */
	key = io_lefgetkeyword(f);
	if (key == 0) return(1);
	if (namesame(key, "DEFAULT") != 0)
	{
		ttyputerr("Expected 'DEFAULT' after VIA name");
		return(1);
	}

	for(;;)
	{
		/* get the next keyword */
		key = io_lefgetkeyword(f);
		if (key == 0) return(1);
		if (namesame(key, "END") == 0)
		{
			key = io_lefgetkeyword(f);
			break;
		}
		if (namesame(key, "RESISTANCE") == 0)
		{
			if (io_lefignoretosemicolon(f, key) != 0) return(1);
			continue;
		}
		if (namesame(key, "LAYER") == 0)
		{
			key = io_lefgetkeyword(f);
			if (key == 0) return(1);
			io_lefparselayer(key, &afunc, &layerfunc);
			if (afunc == APUNKNOWN) ap = NOARCPROTO; else
			{
				for(ap = el_curtech->firstarcproto; ap != NOARCPROTO; ap = ap->nextarcproto)
					if (((INTBIG)((ap->userbits&AFUNCTION)>>AFUNCTIONSH)) == afunc) break;
			}
			if (ap != NOARCPROTO)
			{
				if (vd->lay1 == NOARCPROTO) vd->lay1 = ap; else
					vd->lay2 = ap;
			}
			if (io_lefignoretosemicolon(f, "LAYER") != 0) return(1);
			continue;
		}
		if (namesame(key, "RECT") == 0)
		{
			/* handle definition of a via rectangle */
			key = io_lefgetkeyword(f);
			if (key == 0) return(1);
			v = (float)atof(key);
			lx = scalefromdispunit(v, DISPUNITMIC);

			key = io_lefgetkeyword(f);
			if (key == 0) return(1);
			v = (float)atof(key);
			ly = scalefromdispunit(v, DISPUNITMIC);

			key = io_lefgetkeyword(f);
			if (key == 0) return(1);
			v = (float)atof(key);
			hx = scalefromdispunit(v, DISPUNITMIC);

			key = io_lefgetkeyword(f);
			if (key == 0) return(1);
			v = (float)atof(key);
			hy = scalefromdispunit(v, DISPUNITMIC);

			/* accumulate largest layer size */
			if (hx-lx > vd->sx) vd->sx = hx - lx;
			if (hy-ly > vd->sy) vd->sy = hy - ly;

			if (io_lefignoretosemicolon(f, "RECT") != 0) return(1);
			continue;
		}
	}
	if (vd->lay1 != NOARCPROTO && vd->lay2 != NOARCPROTO)
	{
		for(np = el_curtech->firstnodeproto; np != NONODEPROTO; np = np->nextnodeproto)
		{
			if (((np->userbits&NFUNCTION)>>NFUNCTIONSH) != NPCONTACT) continue;
			pp = np->firstportproto;
			lay1found = lay2found = 0;
			for(i=0; pp->connects[i] != NOARCPROTO; i++)
			{
				if (pp->connects[i] == vd->lay1) lay1found = 1;
				if (pp->connects[i] == vd->lay2) lay2found = 1;
			}
			if (lay1found != 0 && lay2found != 0) break;
		}
		vd->via = np;
	}
	return(0);
}

INTSML io_lefreadpin(FILE *f, NODEPROTO *facet)
{
	REGISTER char *key;
	REGISTER INTBIG portbits, usebits;
	char curkey[200], pinname[200];

	/* get the pin name */
	key = io_lefgetkeyword(f);
	if (key == 0)
	{
		ttyputerr("EOF parsing PIN name");
		return(1);
	}
	strcpy(pinname, key);

	portbits = usebits = 0;
	for(;;)
	{
		key = io_lefgetkeyword(f);
		if (key == 0)
		{
			ttyputerr("EOF parsing PIN");
			return(1);
		}

		if (namesame(key, "END") == 0)
		{
			key = io_lefgetkeyword(f);
			break;
		}

		if (namesame(key, "SHAPE") == 0 || namesame(key, "CAPACITANCE") == 0 ||
			namesame(key, "ANTENNASIZE") == 0)
		{
			strcpy(curkey, key);
			if (io_lefignoretosemicolon(f, curkey) != 0) return(1);
			continue;
		}

		if (namesame(key, "USE") == 0)
		{
			key = io_lefgetkeyword(f);
			if (key == 0)
			{
				ttyputerr("EOF reading USE clause");
				return(1);
			}
			if (namesame(key, "POWER") == 0) usebits = PWRPORT; else
			if (namesame(key, "GROUND") == 0) usebits = GNDPORT; else
			if (namesame(key, "CLOCK") == 0) usebits = CLKPORT; else
			{
				ttyputerr("Line %d: Unknown USE keyword (%s)",
					io_leflineno, key);
			}
			if (io_lefignoretosemicolon(f, "USE") != 0) return(1);
			continue;
		}

		if (namesame(key, "DIRECTION") == 0)
		{
			key = io_lefgetkeyword(f);
			if (key == 0)
			{
				ttyputerr("EOF reading DIRECTION clause");
				return(1);
			}
			if (namesame(key, "INPUT") == 0) portbits = INPORT; else
			if (namesame(key, "OUTPUT") == 0) portbits = OUTPORT; else
			if (namesame(key, "INOUT") == 0) portbits = BIDIRPORT; else
			{
				ttyputerr("Line %d: Unknown DIRECTION keyword (%s)",
					io_leflineno, key);
			}
			if (io_lefignoretosemicolon(f, "DIRECTION") != 0) return(1);
			continue;
		}

		if (namesame(key, "PORT") == 0)
		{
			if (usebits != 0) portbits = usebits;
			if (io_lefreadport(f, facet, pinname, portbits) != 0)
				return(1);
			continue;
		}

		ttyputerr("Line %d: Unknown PIN keyword (%s)", io_leflineno, key);
		return(1);
	}
	return(0);
}

INTSML io_lefreadport(FILE *f, NODEPROTO *facet, char *portname, INTBIG portbits)
{
	REGISTER char *key;
	REGISTER INTBIG i, j, lx, hx, ly, hy, intx, inty, first,
		intwidth, lastintx, lastinty, sea, cx, cy;
	INTBIG afunc, layerfunc, sx, sy;
	REGISTER INTBIG afunc1, afunc2, found1, found2;
	REGISTER ARCPROTO *ap, *ap1, *ap2;
	REGISTER NODEPROTO *pin, *plnp;
	REGISTER ARCINST *ai;
	REGISTER NODEINST *ni;
	REGISTER PORTPROTO *pp;
	REGISTER GEOM *geom;
	float width, x, y;
	REGISTER LEFPATH *lefpaths, *lp, *olp;

	ap = NOARCPROTO;
	plnp = NONODEPROTO;
	lefpaths = NOLEFPATH;
	first = 1;
	for(;;)
	{
		key = io_lefgetkeyword(f);
		if (key == 0)
		{
			ttyputerr("EOF parsing PORT");
			return(1);
		}

		if (namesame(key, "END") == 0)
		{
			break;
		}

		if (namesame(key, "LAYER") == 0)
		{
			key = io_lefgetkeyword(f);
			if (key == 0)
			{
				ttyputerr("EOF reading LAYER clause");
				return(1);
			}
			io_lefparselayer(key, &afunc, &layerfunc);
			if (afunc == APUNKNOWN) ap = NOARCPROTO; else
				ap = io_lefgetarc(afunc);
			plnp = io_lefgetnode(layerfunc);
			if (io_lefignoretosemicolon(f, "LAYER") != 0) return(1);
			continue;
		}

		if (namesame(key, "WIDTH") == 0)
		{
			key = io_lefgetkeyword(f);
			if (key == 0)
			{
				ttyputerr("EOF reading WIDTH clause");
				return(1);
			}
			width = (float)atof(key);
			intwidth = scalefromdispunit(width, DISPUNITMIC);
			if (io_lefignoretosemicolon(f, "WIDTH") != 0) return(1);
			continue;
		}

		if (namesame(key, "RECT") == 0)
		{
			if (plnp == NONODEPROTO)
			{
				ttyputerr("Line %d: No layers for RECT", io_leflineno);
				return(1);
			}
			key = io_lefgetkeyword(f);
			if (key == 0)
			{
				ttyputerr("EOF reading RECT low X");
				return(1);
			}
			x = (float)atof(key);
			lx = scalefromdispunit(x, DISPUNITMIC);

			key = io_lefgetkeyword(f);
			if (key == 0)
			{
				ttyputerr("EOF reading RECT low Y");
				return(1);
			}
			y = (float)atof(key);
			ly = scalefromdispunit(y, DISPUNITMIC);

			key = io_lefgetkeyword(f);
			if (key == 0)
			{
				ttyputerr("EOF reading RECT high X");
				return(1);
			}
			x = (float)atof(key);
			hx = scalefromdispunit(x, DISPUNITMIC);

			key = io_lefgetkeyword(f);
			if (key == 0)
			{
				ttyputerr("EOF reading RECT high Y");
				return(1);
			}
			y = (float)atof(key);
			hy = scalefromdispunit(y, DISPUNITMIC);

			if (io_lefignoretosemicolon(f, "RECT") != 0) return(1);

			/* make the pin */
			if (hx < lx) { j = hx;   hx = lx;   lx = j; }
			if (hy < ly) { j = hy;   hy = ly;   ly = j; }
			ni = newnodeinst(plnp, lx, hx, ly, hy, 0, 0, facet);
			if (ni == NONODEINST)
			{
				ttyputerr("Line %d: Cannot create pin for RECT", io_leflineno);
				return(1);
			}
			endobjectchange((INTBIG)ni, VNODEINST);

			if (first != 0)
			{
				/* create the port on the first pin */
				first = 0;
				pp = io_lefnewport(facet, ni, plnp->firstportproto, portname);
				pp->userbits = (pp->userbits & ~STATEBITS) | portbits;
			}
			continue;
		}

		if (namesame(key, "PATH") == 0)
		{
			if (ap == NOARCPROTO)
			{
				ttyputerr("Line %d: No layers for PATH", io_leflineno);
				return(1);
			}
			for(i=0; ; i++)
			{
				key = io_lefgetkeyword(f);
				if (key == 0)
				{
					ttyputerr("EOF reading PATH clause");
					return(1);
				}
				if (strcmp(key, ";") == 0) break;
				x = (float)atof(key);

				key = io_lefgetkeyword(f);
				if (key == 0)
				{
					ttyputerr("EOF reading PATH clause");
					return(1);
				}
				y = (float)atof(key);

				/* plot this point */
				intx = scalefromdispunit(x, DISPUNITMIC);
				inty = scalefromdispunit(y, DISPUNITMIC);
				if (i != 0)
				{
					/* queue path */
					lp = io_lefnewpath();
					if (lp == NOLEFPATH) return(1);
					lp->x[0] = lastintx;     lp->x[1] = intx;
					lp->y[0] = lastinty;     lp->y[1] = inty;
					lp->ni[0] = NONODEINST;  lp->ni[1] = NONODEINST;
					lp->width = intwidth;
					lp->arc = ap;
					lp->nextlefpath = lefpaths;
					lefpaths = lp;
				}
				lastintx = intx;   lastinty = inty;
			}
			continue;
		}

		if (namesame(key, "VIA") == 0)
		{
			/* get the coordinates */
			key = io_lefgetkeyword(f);
			if (key == 0)
			{
				ttyputerr("EOF reading VIA clause");
				return(1);
			}
			x = (float)atof(key);
			intx = scalefromdispunit(x, DISPUNITMIC);

			key = io_lefgetkeyword(f);
			if (key == 0)
			{
				ttyputerr("EOF reading VIA clause");
				return(1);
			}
			y = (float)atof(key);
			inty = scalefromdispunit(y, DISPUNITMIC);

			/* find the proper via */
			key = io_lefgetkeyword(f);
			afunc1 = afunc2 = APUNKNOWN;
			if (namesame(key, "via1") == 0)
			{
				afunc1 = APMETAL1;
				afunc2 = APMETAL2;
			} else if (namesame(key, "via2") == 0)
			{
				afunc1 = APMETAL2;
				afunc2 = APMETAL3;
			} else if (namesame(key, "via3") == 0)
			{
				afunc1 = APMETAL3;
				afunc2 = APMETAL4;
			} else if (namesame(key, "via4") == 0)
			{
				afunc1 = APMETAL4;
				afunc2 = APMETAL5;
			} else
			{
				ttyputerr("Line %d: Unknown VIA type (%s)", io_leflineno, key);
				return(1);
			}
			ap1 = io_lefgetarc(afunc1);
			if (ap1 == NOARCPROTO) return(1);
			ap2 = io_lefgetarc(afunc2);
			if (ap2 == NOARCPROTO) return(1);
			for(pin = el_curtech->firstnodeproto; pin != NONODEPROTO; pin = pin->nextnodeproto)
			{
				if (((pin->userbits&NFUNCTION)>>NFUNCTIONSH) != NPCONTACT) continue;
				pp = pin->firstportproto;
				found1 = found2 = 0;
				for(i=0; pp->connects[i] != NOARCPROTO; i++)
				{
					if (pp->connects[i] == ap1) found1 = 1;
					if (pp->connects[i] == ap2) found2 = 1;
				}
				if (found1 != 0 && found2 != 0) break;
			}
			if (pin == NONODEPROTO)
			{
				ttyputerr("Line %d: No Via in current technology connects %s to %s",
					io_leflineno, arcfunctionname((INTSML)afunc1),
					arcfunctionname((INTSML)afunc2));
				return(1);
			}
			if (io_lefignoretosemicolon(f, "VIA") != 0) return(1);

			/* create the via */
			defaultnodesize(pin, &sx, &sy);
			lx = intx - sx / 2;   hx = lx + sx;
			ly = inty - sy / 2;   hy = ly + sy;
			ni = newnodeinst(pin, lx, hx, ly, hy, 0, 0, facet);
			if (ni == NONODEINST)
			{
				ttyputerr("Line %d: Cannot create VIA for PATH", io_leflineno);
				return(1);
			}
			endobjectchange((INTBIG)ni, VNODEINST);
			continue;
		}

		ttyputerr("Line %d: Unknown PORT keyword (%s)", io_leflineno, key);
		return(1);
	}

	/* look for paths that end at vias */
	for(lp = lefpaths; lp != NOLEFPATH; lp = lp->nextlefpath)
	{
		for(i=0; i<2; i++)
		{
			if (lp->ni[i] != NONODEINST) continue;
			sea = initsearch(lp->x[i], lp->x[i], lp->y[i], lp->y[i], facet);
			for(;;)
			{
				geom = nextobject(sea);
				if (geom == NOGEOM) break;
				if (geom->entrytype != OBJNODEINST) continue;
				ni = geom->entryaddr.ni;
				cx = (ni->lowx + ni->highx) / 2;
				cy = (ni->lowy + ni->highy) / 2;
				if (cx != lp->x[i] || cy != lp->y[i]) continue;
				lp->ni[i] = ni;
				break;
			}
			if (lp->ni[i] == NONODEINST) continue;

			/* use this via at other paths which meet here */
			for(olp = lefpaths; olp != NOLEFPATH; olp = olp->nextlefpath)
			{
				for(j=0; j<2; j++)
				{
					if (olp->ni[j] != NONODEINST) continue;
					if (olp->x[j] != lp->x[i] || olp->y[j] != lp->y[i]) continue;
					olp->ni[j] = lp->ni[i];
				}
			}
		}
	}

	/* create pins at all other path ends */
	for(lp = lefpaths; lp != NOLEFPATH; lp = lp->nextlefpath)
	{
		for(i=0; i<2; i++)
		{
			if (lp->ni[i] != NONODEINST) continue;
			pin = getpinproto(lp->arc);
			if (pin == NONODEPROTO) continue;
			defaultnodesize(pin, &sx, &sy);
			lx = lp->x[i] - sx / 2;   hx = lx + sx;
			ly = lp->y[i] - sy / 2;   hy = ly + sy;
			lp->ni[i] = newnodeinst(pin, lx, hx, ly, hy, 0, 0, facet);
			if (lp->ni[i] == NONODEINST)
			{
				ttyputerr("Line %d: Cannot create pin for PATH", io_leflineno);
				return(1);
			}
			endobjectchange((INTBIG)lp->ni[i], VNODEINST);

			/* use this pin at other paths which meet here */
			for(olp = lefpaths; olp != NOLEFPATH; olp = olp->nextlefpath)
			{
				for(j=0; j<2; j++)
				{
					if (olp->ni[j] != NONODEINST) continue;
					if (olp->x[j] != lp->x[i] || olp->y[j] != lp->y[i]) continue;
					olp->ni[j] = lp->ni[i];
				}
			}
		}
	}

	/* now instantiate the paths */
	for(lp = lefpaths; lp != NOLEFPATH; lp = lp->nextlefpath)
	{
		ai = newarcinst(lp->arc, lp->width, us_makearcuserbits(lp->arc),
			lp->ni[0], lp->ni[0]->proto->firstportproto, lp->x[0], lp->y[0],
				lp->ni[1], lp->ni[1]->proto->firstportproto, lp->x[1], lp->y[1], facet);
		if (ai == NOARCINST)
		{
			ttyputerr("Line %d: Cannot create arc for PATH", io_leflineno);
			return(1);
		}
	}
	io_leffreepaths(lefpaths);

	return(0);
}

/*
 * Routine to create a port called "thename" on port "pp" of node "ni" in facet "facet".
 * The name is modified if it already exists.
 */
PORTPROTO *io_lefnewport(NODEPROTO *facet, NODEINST *ni, PORTPROTO *pp, char *thename)
{
	REGISTER PORTPROTO *ppt;
	REGISTER INTBIG i;
	REGISTER char *newname, *portname;

	portname = thename;
	newname = 0;
	for(i=0; ; i++)
	{
		for(ppt = facet->firstportproto; ppt != NOPORTPROTO; ppt = ppt->nextportproto)
			if (namesame(portname, ppt->protoname) == 0) break;
		if (ppt == NOPORTPROTO)
		{
			ppt = newportproto(facet, ni, pp, portname);
			break;
		}

		/* make space for modified name */
		if (newname == 0) newname = (char *)emalloc(strlen(thename)+10, el_tempcluster);
		sprintf(newname, "%s-%d", thename, i);
		portname = newname;
	}
	if (newname != 0) efree(newname);
	return(ppt);
}

INTSML io_lefreadobs(FILE *f, NODEPROTO *facet)
{
	REGISTER char *key;
	INTBIG lfunc, arcfunc;
	REGISTER INTBIG lx, hx, ly, hy, j;
	float x, y;
	REGISTER NODEINST *ni;
	REGISTER NODEPROTO *np;

	np = NONODEPROTO;
	for(;;)
	{
		key = io_lefgetkeyword(f);
		if (key == 0)
		{
			ttyputerr("EOF parsing OBS");
			return(1);
		}

		if (namesame(key, "END") == 0) break;

		if (namesame(key, "LAYER") == 0)
		{
			key = io_lefgetkeyword(f);
			if (key == 0)
			{
				ttyputerr("EOF reading LAYER clause");
				return(1);
			}
			io_lefparselayer(key, &arcfunc, &lfunc);
			if (lfunc == LFUNKNOWN)
			{
				ttyputerr("Line %d: Unknown layer name (%s)", io_leflineno, key);
				return(1);
			}
			np = io_lefgetnode(lfunc);
			if (np == NONODEPROTO) return(1);
			if (io_lefignoretosemicolon(f, "LAYER") != 0) return(1);
			continue;
		}

		if (namesame(key, "RECT") == 0)
		{
			if (np == NONODEPROTO)
			{
				ttyputerr("Line %d: No layers for RECT", io_leflineno);
				return(1);
			}
			key = io_lefgetkeyword(f);
			if (key == 0)
			{
				ttyputerr("EOF reading RECT low X");
				return(1);
			}
			x = (float)atof(key);
			lx = scalefromdispunit(x, DISPUNITMIC);

			key = io_lefgetkeyword(f);
			if (key == 0)
			{
				ttyputerr("EOF reading RECT low Y");
				return(1);
			}
			y = (float)atof(key);
			ly = scalefromdispunit(y, DISPUNITMIC);

			key = io_lefgetkeyword(f);
			if (key == 0)
			{
				ttyputerr("EOF reading RECT high X");
				return(1);
			}
			x = (float)atof(key);
			hx = scalefromdispunit(x, DISPUNITMIC);

			key = io_lefgetkeyword(f);
			if (key == 0)
			{
				ttyputerr("EOF reading RECT high Y");
				return(1);
			}
			y = (float)atof(key);
			hy = scalefromdispunit(y, DISPUNITMIC);

			if (io_lefignoretosemicolon(f, "RECT") != 0) return(1);

			/* make the pin */
			if (hx < lx) { j = hx;   hx = lx;   lx = j; }
			if (hy < ly) { j = hy;   hy = ly;   ly = j; }
			ni = newnodeinst(np, lx, hx, ly, hy, 0, 0, facet);
			if (ni == NONODEINST)
			{
				ttyputerr("Line %d: Cannot create node for RECT", io_leflineno);
				return(1);
			}
			endobjectchange((INTBIG)ni, VNODEINST);
			continue;
		}
	}
	return(0);
}

INTSML io_lefignoretosemicolon(FILE *f, char *command)
{
	REGISTER char *key;

	/* ignore up to the next semicolon */
	for(;;)
	{
		key = io_lefgetkeyword(f);
		if (key == 0)
		{
			ttyputerr("EOF parsing %s", command);
			return(1);
		}
		if (strcmp(key, ";") == 0) break;
	}
	return(0);
}

char *io_lefgetkeyword(FILE *f)
{
	REGISTER char *ret;
	REGISTER INTBIG filepos, i;

	/* keep reading from file until something is found on a line */
	while (io_lefline[io_leflinepos] == 0)
	{
		/* read a line from the file, exit at EOF */
		if (xfgets(io_lefline, MAXLINE, f) != 0) return(0);
		io_leflineno++;
		if ((io_leflineno%50) == 0)
		{
			filepos = xtell(f);
			DiaPercent(1, filepos*100/io_leffilesize);
		}

		/* remove comments from the line */
		for(i=0; io_lefline[i] != 0; i++)
			if (io_lefline[i] == '#')
		{
			io_lefline[i] = 0;
			break;
		}

		/* look for the first text on the line */
		io_leflinepos = 0;
		while (io_lefline[io_leflinepos] == ' ' || io_lefline[io_leflinepos] == '\t')
			io_leflinepos++;
	}

	/* remember where the keyword begins */
	ret = &io_lefline[io_leflinepos];

	/* scan to the end of the keyword */
	while (io_lefline[io_leflinepos] != 0 && io_lefline[io_leflinepos] != ' ' &&
		io_lefline[io_leflinepos] != '\t') io_leflinepos++;
	if (io_lefline[io_leflinepos] != 0) io_lefline[io_leflinepos++] = 0;

	/* advance to the start of the next keyword */
	while (io_lefline[io_leflinepos] == ' ' || io_lefline[io_leflinepos] == '\t')
		io_leflinepos++;
	return(ret);
}

NODEPROTO *io_lefgetnode(INTBIG lfunc)
{
	REGISTER NODEPROTO *np;

	np = getpurelayernode(el_curtech, -1, lfunc);
	if (np == NONODEPROTO)
	{
		ttyputerr("Line %d: Layer not in current technology",
			io_leflineno);
		return(NONODEPROTO);
	}
	return(np);
}

ARCPROTO *io_lefgetarc(INTBIG afunc)
{
	REGISTER ARCPROTO *ap;

	for(ap = el_curtech->firstarcproto; ap != NOARCPROTO; ap = ap->nextarcproto)
		if (((INTSML)((ap->userbits&AFUNCTION)>>AFUNCTIONSH)) == afunc) break;
	if (ap == NOARCPROTO)
	{
		ttyputerr("Line %d: Layer %s not in current technology",
			io_leflineno, arcfunctionname((INTSML)afunc));
		return(NOARCPROTO);
	}
	return(ap);
}

LEFPATH *io_lefnewpath(void)
{
	LEFPATH *lp;

	if (io_leffreepath != NOLEFPATH)
	{
		lp = io_leffreepath;
		io_leffreepath = lp->nextlefpath;
	} else
	{
		lp = (LEFPATH *)emalloc(sizeof(LEFPATH), io_aid->cluster);
		if (lp == 0) lp = NOLEFPATH;
	}
	return(lp);
}

void io_lefkillpath(LEFPATH *lp)
{
	lp->nextlefpath = io_leffreepath;
	io_leffreepath = lp;
}

void io_leffreepaths(LEFPATH *firstlp)
{
	LEFPATH *lp, *nextlp;

	for(lp = firstlp; lp != NOLEFPATH; lp = nextlp)
	{
		nextlp = lp->nextlefpath;
		io_lefkillpath(lp);
	}
}

#endif  /* IOLEF - at top */
