magic/utils/tech.c

1056 lines
27 KiB
C

/*
* tech.c --
*
* Read in a technology file.
*
* *********************************************************************
* * 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-8.0/utils/tech.c,v 1.1.1.1 2008/02/03 20:43:50 tim Exp $";
#endif /* not lint */
#include <stdio.h>
#include <string.h>
#include <stdarg.h>
#include <ctype.h>
/*
* C99 compat
* Mind: tcltk/tclmagic.h must be included prior to all the other headers
*/
#include "tcltk/tclmagic.h"
#include "database/database.h"
#include "utils/magic.h"
#include "utils/geometry.h"
#include "utils/utils.h"
#include "utils/tech.h"
#include "textio/textio.h"
#include "windows/windows.h"
#include "utils/malloc.h"
/* C99 compat */
#include "utils/heap.h"
#include "cif/cif.h"
#include "cif/cif.h"
#include "cif/CIFint.h"
#include "drc/drc.h"
#include "mzrouter/mzrouter.h"
#include "wiring/wiring.h"
#include "lef/lef.h"
#include "router/router.h"
#include "irouter/irouter.h"
#include "garouter/garouter.h"
#include "extract/extract.h"
#include "plow/plow.h"
/* Cannot include tcltk/tclmagic.h for a clash with utils/magic.h
extern int Tcl_printf(FILE *, const char *, va_list); */
global int TechFormatVersion;
global bool TechOverridesDefault;
/* Define a file stack so that "include" calls can be nested */
typedef struct FStack /* Linked FILE * pointers */
{
FILE *file;
struct FStack *next; /* Pointer to another linked rectangle */
} filestack;
int techLineNumber;
char *TechFileName = NULL;
#define iseol(c) ((c) == EOF || (c) == '\n')
/*
* Each client of the technology module must make itself known by
* a call to TechAddClient(). These calls provide both the names
* of the sections of the technology file, as well as the procedures
* to be invoked with lines in these sections.
*
* The following table is used to record clients of the technology
* module.
*/
typedef struct tC
{
bool (*tc_proc)(); /* Procedure to be called for each
* line in section.
*/
void (*tc_init)(); /* Procedure to be called before any
* lines in a section are processed.
*/
void (*tc_final)(); /* Procedure to be called after all
* lines in section have been processed.
*/
struct tC *tc_next; /* Next client in section */
} techClient;
typedef struct
{
char *ts_name; /* Name of section */
char *ts_alias; /* Alternative name of section */
techClient *ts_clients; /* Pointer to list of clients */
bool ts_read; /* Flag: TRUE if section was read */
bool ts_optional; /* Flag: TRUE if section is optional */
SectionID ts_thisSect; /* SectionID of this section */
SectionID ts_prevSects; /* Mask of sections that must be
* read in before this one. The
* mask is constructed from the
* section identifiers set by
* TechAddClient().
*/
} techSection;
#define MAXSECTIONS (8 * sizeof (int)) /* Not easily changeable */
#define MAXARGS 30
#define MAXLINESIZE 1024
#define SectionToMaskBit(s) (1 << (s))
#define SectionMaskHasSection(m, s) (m & SectionToMaskBit(s))
int techSectionNum; /* ID of next new section */
SectionID techSectionMask; /* Mask of sections already read */
techSection techSectionTable[MAXSECTIONS];
techSection *techSectionFree; /* Pointer to next free section */
techSection *techCurrentSection; /* Pointer to current section */
techSection *techFindSection();
/*
* ----------------------------------------------------------------------------
*
* TechSectionGetMask --
*
* Get the SectionID mask for a specific section (specified by name). The
* returned mask is inverted; that is, it is a mask containing bits
* representing all the client sections except for the one sepcified.
* This return value can be passed to TechLoad to re-read a specific
* section.
*
* Results:
* Returns the inverted mask for the selected section ID.
*
* Side effects:
* If "depend" is non-NULL, the SectionID to which it points will be
* set to a mask representing the mask of sections which depend on
* the indicated section; that is, those sections which will be
* invalidated if the indicated section is altered in any way.
*
* ----------------------------------------------------------------------------
*/
SectionID
TechSectionGetMask(sectionName, depend)
char *sectionName;
SectionID *depend;
{
techSection *tsp, *thissect;
SectionID invid = 0;
SectionID selected;
thissect = techFindSection(sectionName);
if (thissect == NULL) return -1;
selected = thissect->ts_thisSect;
for (tsp = techSectionTable; tsp < techSectionFree; tsp++)
{
if (tsp != thissect)
{
invid |= tsp->ts_thisSect;
if (tsp->ts_prevSects & thissect->ts_thisSect)
if (depend != NULL) *depend = tsp->ts_thisSect;
}
}
return invid;
}
/*
* ----------------------------------------------------------------------------
*
* TechInit --
*
* Initialize the technology module.
*
* Results:
* None.
*
* Side effects:
* Initializes the technology read-in module.
* This function must be called before any other functions in
* this module are called. It is called exactly once at the start
* of a magic session.
*
* ----------------------------------------------------------------------------
*/
void
TechInit()
{
techCurrentSection = (techSection *) NULL;
techSectionFree = techSectionTable;
techSectionNum = 0;
}
/*
* ----------------------------------------------------------------------------
*
* TechAddAlias --
*
* Add an alternative name (alias) for a technology file section which
* may be used in place of the primary name.
*
* This has been added mainly to handle sections which have been
* expanded beyond their original definition such that the section
* name is no longer appropriate. Case in point: the "images"
* section is broader in scope than the "contact" section, but
* because contacts are a subset of images in version 7.3, it is
* preferable to have an "images" section instead of a "contacts"
* section, with allowances for backwards compatibility.
*
* Results:
* None.
*
* Side effects:
* Allocates string memory.
*
* ----------------------------------------------------------------------------
*/
void
TechAddAlias(primaryName, alias)
char *primaryName;
char *alias;
{
techSection *tsp;
tsp = techFindSection(primaryName);
if (tsp == (techSection *) NULL)
{
TxError("Unknown technology file section \"%s\" requested.\n",
primaryName);
}
else
{
if (tsp->ts_alias != NULL)
freeMagic(tsp->ts_alias);
tsp->ts_alias = StrDup((char **)NULL, alias);
}
}
/*
* ----------------------------------------------------------------------------
*
* changePlanesFunc() ---
*
* This function hacks the existing layout database in case a tech file
* is loaded which contains more or fewer planes than the exisiting
* technology. This is doing nothing fancy; it is simply making sure
* that all memory allocation is accounted for.
*
* As a note for future implementation, it would be helpful to keep the
* old plane name definitions around and try to match up the old and new
* planes, so that it is possible to load a technology file which matches
* the existing technology except for the addition or subtraction of one
* or more planes (e.g., extra metal layer option) without completely
* invalidating an existing layout.
*
* As written, this function is inherently dangerous. It is intended for
* use when loading a new tech file when there is no layout, just empty
* tile planes.
* ----------------------------------------------------------------------------
*/
int
changePlanesFunc(cellDef, arg)
CellDef *cellDef;
int *arg;
{
int oldnumplanes = *arg;
int pNum;
if (oldnumplanes < DBNumPlanes)
{
/* New planes to be added */
for (pNum = oldnumplanes; pNum < DBNumPlanes; pNum++)
{
cellDef->cd_planes[pNum] = DBNewPlane((ClientData) TT_SPACE);
}
}
else
{
/* Old planes to be subtracted */
for (pNum = DBNumPlanes; pNum < oldnumplanes; pNum++)
{
if (cellDef->cd_planes[pNum] != NULL)
{
DBFreePaintPlane(cellDef->cd_planes[pNum]);
TiFreePlane(cellDef->cd_planes[pNum]);
cellDef->cd_planes[pNum] = (Plane *) NULL;
}
}
}
return 0;
}
/*
* ----------------------------------------------------------------------------
*
* TechAddClient --
*
* Add a client to the technology module.
*
* Results:
* None.
*
* Side effects:
* Identifies "sectionName" as a valid name for a section of a .tech
* file, and specifies that init() is the procedure to be called when
* a new technology is loaded, proc() as the procedure to be called
* for each line in the given section, and final() as the procedure to
* be called after the last line in the given section.
*
* The init() procedure takes no arguments.
* The proc() procedure should be of the following form:
* bool
* proc(sectionName, argc, argv)
* char *sectionName;
* int argc;
* char *argv[];
* {
* }
* The final() procedure takes no arguments.
*
* The argument prevSections should be a mask of the SectionID's
* of all sections that must be read in before this one.
*
* If the argument 'pSectionID' is non-NULL, it should point to
* an int that will be set to the sectionID of this section.
*
* It is legal for several procedures to be associated with a given
* sectionName; this is accomplished through successive calls to
* TechAddClient with the same sectionName. The procedures will
* be invoked in the order in which they were handed to TechAddClient().
*
* If the procedure given is NULL for init(), proc(), or final(), no
* procedure is invoked.
*
* ----------------------------------------------------------------------------
*/
void
TechAddClient(sectionName, init, proc, final, prevSections, pSectionID, opt)
char *sectionName;
void (*init)();
bool (*proc)();
void (*final)();
SectionID prevSections;
SectionID *pSectionID;
bool opt; /* optional section */
{
techSection *tsp;
techClient *tcp, *tcl;
tsp = techFindSection(sectionName);
if (tsp == (techSection *) NULL)
{
tsp = techSectionFree++;
ASSERT(tsp < &techSectionTable[MAXSECTIONS], "TechAddClient");
tsp->ts_name = StrDup((char **) NULL, sectionName);
tsp->ts_alias = NULL;
tsp->ts_clients = (techClient *) NULL;
tsp->ts_thisSect = SectionToMaskBit(techSectionNum);
tsp->ts_prevSects = (SectionID) 0;
tsp->ts_optional = opt;
techSectionNum++;
}
tsp->ts_prevSects |= prevSections;
if (pSectionID)
*pSectionID = tsp->ts_thisSect;
tcp = (techClient *) mallocMagic(sizeof (techClient));
ASSERT(tcp != (techClient *) NULL, "TechAddClient");
tcp->tc_init = init;
tcp->tc_proc = proc;
tcp->tc_final = final;
tcp->tc_next = (techClient *) NULL;
if (tsp->ts_clients == (techClient *) NULL)
tsp->ts_clients = tcp;
else
{
for (tcl = tsp->ts_clients; tcl->tc_next; tcl = tcl->tc_next)
/* Nothing */;
tcl->tc_next = tcp;
}
}
/*
* ----------------------------------------------------------------------------
* TechLoad --
*
* Initialize technology description information from a file.
*
* Results:
* TRUE if technology is successfully initialized (all required
* sections present and error free); FALSE otherwise. Unrecognized
* sections cause an error message to be printed, but do not otherwise
* affect the result returned by TechLoad().
*
* Side effects:
* Calls technology initialization routines of other modules
* to initialize technology-specific information.
*
* ----------------------------------------------------------------------------
*/
bool
TechLoad(filename, initmask)
char *filename;
SectionID initmask;
{
FILE *tf;
techSection *tsp;
techClient *tcp;
char suffix[20], line[MAXLINESIZE], *realname;
char *argv[MAXARGS];
SectionID mask, badMask;
int argc, s;
bool retval, skip;
filestack *fstack, *newstack;
filestack topfile;
fstack = NULL;
techLineNumber = 0;
badMask = (SectionID) 0;
int saveNumPlanes;
int changePlanesFunc(); /* forward declaration */
int checkForPaintFunc(); /* forward declaration */
if (initmask == -1)
{
TxError("Invalid technology file section requested.\n");
return (FALSE);
}
/* If NULL is passed to argument "filename", this is a reload and */
/* we should read TechFileName verbatim. */
if (filename == NULL)
{
if (TechFileName != NULL)
{
tf = PaOpen(TechFileName, "r", (char *)NULL, ".", SysLibPath, &realname);
if (tf == (FILE *) NULL)
{
TxError("Could not find file '%s' in any of these "
"directories:\n %s\n",
TechFileName, SysLibPath);
return (FALSE);
}
}
else
{
TxError("Invalid technology file load.\n");
return (FALSE);
}
}
else
{
char *sptr, *dptr;
tf = (FILE *)NULL;
/* TECH_VERSION in the filename is deprecated as of magic version */
/* 7.2.27; TECH_VERSION is no longer defined in the utils/Makefile. */
/* It has been changed to TECH_FORMAT_VERSION, left at version 27, */
/* and placed in utils/tech.h. It is needed for backward */
/* compatibility with *.tech27 files, of which there are many. */
(void) sprintf(suffix, ".tech");
/* Added 1/20/2015 to correspond to change to PaLockOpen(); */
/* Always strip suffix from filename when suffix is specified. */
sptr = strrchr(filename, '/');
if (sptr == NULL)
sptr = filename;
else
sptr++;
/* If the filename is ".tech", then remove the extension and */
/* process like it was not there at all. */
dptr = strrchr(sptr, '.');
if ((dptr != NULL) && !strcmp(dptr, suffix))
*dptr = '\0';
/* If a non-standard extension was used, then honor it */
if ((dptr != NULL) && (*dptr != '\0'))
{
tf = PaOpen(filename, "r", (char *)NULL, ".", SysLibPath, &realname);
/* If that didn't work, fall back to trying the filename */
/* with the suffix. This is needed for some ill-considered */
/* names like "SCN4M_SUBM.20.tech". */
}
if (tf == (FILE *)NULL)
tf = PaOpen(filename, "r", suffix, ".", SysLibPath, &realname);
if (tf == (FILE *) NULL)
{
/* Try looking for tech files from the last version to */
/* put the version number into the filename itself. */
(void) sprintf(suffix, ".tech%d", TECH_FORMAT_VERSION);
tf = PaOpen(filename, "r", suffix, ".", SysLibPath, &realname);
if (tf == (FILE *) NULL)
{
TxError("Could not find file '%s.tech' in any of these "
"directories:\n %s\n",
filename, SysLibPath);
return (FALSE);
}
}
StrDup(&TechFileName, realname);
// In case filename is not a temporary string, put it back the
// way it was.
if (dptr != NULL) *dptr = '.';
}
topfile.file = tf;
topfile.next = NULL;
fstack = &topfile;
// If TechLoad is called with initmask == -2, test that the file
// exists and is readable, and that the first non-comment line
// is the keyword "tech".
if (initmask == -2)
{
argc = techGetTokens(line, sizeof line, &fstack, argv);
fclose(tf);
if (argc != 1) return (FALSE);
if (strcmp(argv[0], "tech")) return (FALSE);
return (TRUE);
}
/*
* Mark all sections as being unread.
*/
techSectionMask = initmask;
for (tsp = techSectionTable; tsp < techSectionFree; tsp++)
{
tsp->ts_read = FALSE;
}
/*
* Run section initializations if this is not a reload.
* CIF istyle, CIF ostyle, and extract sections need calls
* to the init functions which clean up memory devoted to
* remembering all the styles.
*/
if (filename != NULL)
{
#ifdef CIF_MODULE
CIFTechInit();
CIFReadTechInit();
#endif
ExtTechInit();
DRCTechInit();
#ifdef ROUTE_MODULE
MZTechInit();
#endif
/* Changing number of planes requires handling on every */
/* celldef. So we need to save the original number of */
/* planes to see if it shrinks or expands. */
saveNumPlanes = DBNumPlanes;
}
/*
* Sections in a technology file begin with a single line containing
* the keyword identifying the section, and end with a single line
* containing the keyword "end".
*/
retval = TRUE;
skip = FALSE;
while ((argc = techGetTokens(line, sizeof line, &fstack, argv)) >= 0)
{
/* Check for file inclusions (can be nested) */
if ((argc > 1) && (!strcmp(argv[0], "include")))
{
char *sptr;
tf = PaOpen(argv[1], "r", suffix, ".", SysLibPath, NULL);
if (tf != NULL)
{
newstack = (filestack *)mallocMagic(sizeof(filestack));
newstack->file = tf;
newstack->next = fstack;
fstack = newstack;
continue;
}
/* Check the directory from which the tech file */
/* itself was read. */
if ((sptr = strrchr(TechFileName, '/')) != NULL)
{
*sptr = '\0';
tf = PaOpen(argv[1], "r", suffix, TechFileName, NULL, NULL);
*sptr = '/';
if (tf != NULL)
{
newstack = (filestack *)mallocMagic(sizeof(filestack));
newstack->file = tf;
newstack->next = fstack;
fstack = newstack;
continue;
}
}
TechError("Warning: Couldn't find include file %s\n", argv[1]);
}
if (!skip && techCurrentSection == NULL)
{
if (argc != 1)
{
TechError("Bad section header line\n");
goto skipsection;
}
tsp = techFindSection(argv[0]);
if (tsp == (techSection *) NULL)
{
TechError("Unrecognized section name: %s\n", argv[0]);
goto skipsection;
}
else if (initmask & tsp->ts_thisSect)
{
skip = TRUE;
continue;
}
if ((mask = (tsp->ts_prevSects & ~techSectionMask)))
{
techSection *sp;
TechError("Section %s appears too early.\n", argv[0]);
TxError("\tMissing prerequisite sections:\n");
for (sp = techSectionTable; sp < techSectionFree; sp++)
if (mask & sp->ts_thisSect)
TxError("\t\t%s\n", sp->ts_name);
goto skipsection;
}
techCurrentSection = tsp;
/* Invoke initialization routines for all clients that
* provided them.
*/
for (tcp = techCurrentSection->ts_clients;
tcp != NULL;
tcp = tcp->tc_next)
{
if (tcp->tc_init)
(void) (*tcp->tc_init)();
}
continue;
}
/* At the end of the section, invoke the finalization routine
* of the client's, if there is one.
*/
if (argc == 1 && strcmp(argv[0], "end") == 0)
{
if (!skip)
{
techSectionMask |= techCurrentSection->ts_thisSect;
techCurrentSection->ts_read = TRUE;
for (tcp = techCurrentSection->ts_clients;
tcp != NULL;
tcp = tcp->tc_next)
{
if (tcp->tc_final)
(*tcp->tc_final)();
}
}
techCurrentSection = (techSection *) NULL;
skip = FALSE;
continue;
}
if (!skip)
for (tcp = techCurrentSection->ts_clients;
tcp != NULL;
tcp = tcp->tc_next)
if (tcp->tc_proc)
{
if (!(*tcp->tc_proc)(techCurrentSection->ts_name,argc,argv))
{
retval = FALSE;
badMask |= techCurrentSection->ts_thisSect;
}
}
continue;
skipsection:
TxError("[Skipping to \"end\"]\n");
skip = TRUE;
}
if (badMask)
{
TxError("The following sections of %s contained errors:\n", TechFileName);
for (s = 0; s < techSectionNum; s++)
if (SectionMaskHasSection(badMask, s))
TxError(" %s\n", techSectionTable[s].ts_name);
}
for (tsp = techSectionTable; tsp < techSectionFree; tsp++)
{
if (!(initmask & tsp->ts_thisSect))
{
if (!tsp->ts_read && !tsp->ts_optional)
{
TxError("Section \"%s\" was missing from %s.\n",
tsp->ts_name, TechFileName);
retval = FALSE;
}
}
}
/* In case we hit an error in an included file. . . */
while ((fstack != NULL) && (fstack != &topfile))
{
fclose(fstack->file);
freeMagic(fstack);
fstack = fstack->next;
}
if (fstack) fclose(fstack->file);
/* Note: If filename is NULL, then individual sections are */
/* being reloaded, and it is the responsibility of the */
/* calling routine to invoke any exit function specific to */
/* that section (e.g., DRCTechScale() when loading a new */
/* DRC style). */
if ((filename != NULL) && (retval == TRUE))
{
/* If the tech file insists that the grid should be set */
/* to the minimum, then enforce it. */
if (CIFCurStyle && (CIFCurStyle->cs_flags & CWF_MINIMUM_GRID))
{
DBLambda[0] = 1;
DBLambda[1] = CIFCurStyle->cs_scaleFactor / CIFCurStyle->cs_gridLimit;
}
/* If internal scalefactor is not the default 1:1, then we */
/* need to scale the techfile numbers accordingly. */
if ((DBLambda[0] != 1) || (DBLambda[1] != 1))
{
int d = DBLambda[0];
int n = DBLambda[1];
CIFTechInputScale(d, n, TRUE);
CIFTechOutputScale(d, n);
DRCTechScale(d, n);
ExtTechScale(d, n);
WireTechScale(d, n);
#ifdef LEF_MODULE
LefTechScale(d, n);
LefTechSetDefaults();
#endif
#ifdef ROUTE_MODULE
RtrTechScale(d, n);
#endif
TxPrintf("Scaled tech values by %d / %d to"
" match internal grid scaling\n", n, d);
/* Check if we're below the scale set by cifoutput gridlimit */
if (CIFTechLimitScale(1, 1))
TxError("WARNING: Current grid scale is smaller"
" than the minimum for the process!\n");
}
/* Post-technology reading routines */
#ifdef ROUTE_MODULE
MZAfterTech();
IRAfterTech();
GAMazeInitParms();
#endif
PlowAfterTech();
if (DBCellSrDefs(0, checkForPaintFunc, (ClientData)&saveNumPlanes))
{
if (saveNumPlanes != DBNumPlanes)
TxError("Warning: Number of planes has changed. ");
TxError("Existing layout may be invalid.\n");
}
if (saveNumPlanes != DBNumPlanes)
DBCellSrDefs(0, changePlanesFunc, (ClientData) &saveNumPlanes);
}
else if (retval == FALSE)
{
/* On error, remove any existing technology file name */
DBNumPlanes = saveNumPlanes;
freeMagic(TechFileName);
TechFileName = NULL;
}
return (retval);
}
/*
* ----------------------------------------------------------------------------
*
* TechError --
*
* Print an error message referring to a given line number in the
* technology module.
*
* Results:
* None.
*
* Side effects:
* Prints an error message.
*
* ----------------------------------------------------------------------------
*/
void
TechPrintLine()
{
char *section;
if (techCurrentSection)
section = techCurrentSection->ts_name;
else
section = "(none)";
TxError("%s: line %d: section %s:\n\t",
TechFileName, techLineNumber, section);
}
void
TechError(const char *fmt, ...)
{
va_list args;
TechPrintLine();
va_start(args, fmt);
Vfprintf(stderr, fmt, args);
va_end(args);
}
/* ================== Functions local to this module ================== */
/*
* ----------------------------------------------------------------------------
*
* techFindSection --
*
* Return a pointer to the entry in techSectionTable for the section
* of the given name.
*
* Results:
* A pointer to the new entry, or NULL if none could be found.
*
* Side effects:
* None.
*
* ----------------------------------------------------------------------------
*/
techSection *
techFindSection(sectionName)
char *sectionName;
{
techSection *tsp;
for (tsp = techSectionTable; tsp < techSectionFree; tsp++)
{
if (!strcmp(tsp->ts_name, sectionName))
return (tsp);
else if (tsp->ts_alias != NULL)
{
if (!strcmp(tsp->ts_alias, sectionName))
return (tsp);
}
}
return ((techSection *) NULL);
}
/*
* ----------------------------------------------------------------------------
*
* techGetTokens --
*
* Read a line from the technology file and split it up into tokens.
* Blank lines are ignored. Lines ending in backslash are joined
* to their successor lines.
* We assume that all macro definition and comment elimination has
* been done by the C preprocessor.
*
* Results:
* Returns the number of tokens into which the line was split, or
* -1 on end of file. Never returns 0.
*
* Side effects:
* Copies the line just read into 'line'. The trailing newline
* is turned into a '\0'. The line is broken into tokens which
* are then placed into argv.
*
* ----------------------------------------------------------------------------
*/
int
techGetTokens(line, size, fstack, argv)
char *line; /* Character array into which line is read */
int size; /* Size of character array */
filestack **fstack; /* Open technology file on top of stack */
char *argv[]; /* Vector of tokens built by techGetTokens() */
{
char *get, *put, *getp;
bool inquote;
int argc = 0;
int currspace; /* chars remaining before end of line[size] */
FILE *file; /* Current technology file */
file = (*fstack)->file;
/* Read one line into the buffer, joining lines when they end
* in backslashes.
*/
/* Code revision (MDG, Stanford): Prevent the 1024-character limit due */
/* to unconditional decrement of size. Long comment lists could cause */
/* infinite looping. New code interprets first non-space character '#' */
/* as a comment character, rather than requiring it to be in the first */
/* column. */
/* Code revision (RTE, Open Circuit Design): Handle DOS-style CR/LF */
/* Code revision (RTE, Open Circuit Design): Handle "include" files */
start:
get = line;
currspace = size;
while (currspace > 0)
{
techLineNumber += 1;
while (fgets(get, currspace, file) == NULL) {
if ((*fstack)->next != NULL)
{
fclose((*fstack)->file);
*fstack = (*fstack)->next;
file = (*fstack)->file;
}
else
return (-1);
}
getp = get;
while(isspace(*getp)) getp++;
if (*getp == '#') continue;
for (put = get; *put != '\n'; put++) currspace -= 1;
if (put != get)
{
put--;
if (*put == 0xd) put--; /* Handle DOS-style CR/LF */
if (*put == '\\')
{
get = put;
continue;
}
put++;
}
*put= '\0';
break;
}
if (currspace == 0) TechError("long line truncated\n");
get = put = line;
while (*get != '\0')
{
/* Skip leading blanks */
while (isspace(*get)) get++;
/* Beginning of the token is here. */
argv[argc] = put = get;
if (*get == '"')
{
get++;
inquote = TRUE;
} else inquote = FALSE;
/*
* Grab up characters to the end of the token. Any character
* preceded by a backslash is taken literally.
*/
while (*get != '\0')
{
if (inquote)
{
if (*get == '"') break;
}
else if (isspace(*get)) break;
if (*get == '\\') /* Process quoted characters literally */
{
get += 1;
if (*get == '\0') break;
}
/* Copy into token receiving area */
*put++ = *get++;
}
/*
* If we got no characters in the token, we must have been at
* the end of the line.
*/
if (get == argv[argc])
break;
/* Terminate the token and advance over the terminating character. */
if (*get != '\0') get++; /* Careful! could be at end of line! */
*put++ = '\0';
argc++;
}
if (argc == 0)
goto start;
return (argc);
}