1 /*
2  * tech.c --
3  *
4  * Read in a technology file.
5  *
6  *     *********************************************************************
7  *     * Copyright (C) 1985, 1990 Regents of the University of California. *
8  *     * Permission to use, copy, modify, and distribute this              *
9  *     * software and its documentation for any purpose and without        *
10  *     * fee is hereby granted, provided that the above copyright          *
11  *     * notice appear in all copies.  The University of California        *
12  *     * makes no representations about the suitability of this            *
13  *     * software for any purpose.  It is provided "as is" without         *
14  *     * express or implied warranty.  Export of this software outside     *
15  *     * of the United States of America may require an export license.    *
16  *     *********************************************************************
17  */
18 
19 #ifndef lint
20 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 $";
21 #endif  /* not lint */
22 
23 #include <stdio.h>
24 #include <string.h>
25 #include <stdarg.h>
26 #include <ctype.h>
27 
28 #include "database/database.h"
29 #include "utils/magic.h"
30 #include "utils/geometry.h"
31 #include "utils/utils.h"
32 #include "utils/tech.h"
33 #include "textio/textio.h"
34 #include "windows/windows.h"
35 #include "utils/malloc.h"
36 
37 global int  TechFormatVersion;
38 global bool TechOverridesDefault;
39 
40 /* Define a file stack so that "include" calls can be nested */
41 
42 typedef struct FStack		/* Linked FILE * pointers */
43 {
44     FILE *file;
45     struct FStack *next;          /* Pointer to another linked rectangle */
46 } filestack;
47 
48 int techLineNumber;
49 char *TechFileName = NULL;
50 
51 #define	iseol(c)	((c) == EOF || (c) == '\n')
52 
53 /*
54  * Each client of the technology module must make itself known by
55  * a call to TechAddClient().  These calls provide both the names
56  * of the sections of the technology file, as well as the procedures
57  * to be invoked with lines in these sections.
58  *
59  * The following table is used to record clients of the technology
60  * module.
61  */
62 
63     typedef struct tC
64     {
65 	bool		(*tc_proc)();	/* Procedure to be called for each
66 					 * line in section.
67 					 */
68 	void		(*tc_init)();	/* Procedure to be called before any
69 					 * lines in a section are processed.
70 					 */
71 	void		(*tc_final)();	/* Procedure to be called after all
72 					 * lines in section have been processed.
73 					 */
74 	struct tC	*tc_next;	/* Next client in section */
75     } techClient;
76 
77     typedef struct
78     {
79 	char		*ts_name;	/* Name of section */
80 	char		*ts_alias;	/* Alternative name of section */
81 	techClient	*ts_clients;	/* Pointer to list of clients */
82 	bool		 ts_read;	/* Flag: TRUE if section was read */
83 	bool		 ts_optional;	/* Flag: TRUE if section is optional */
84 	SectionID	 ts_thisSect;	/* SectionID of this section */
85 	SectionID	 ts_prevSects;	/* Mask of sections that must be
86 					 * read in before this one.  The
87 					 * mask is constructed from the
88 					 * section identifiers set by
89 					 * TechAddClient().
90 					 */
91     } techSection;
92 
93 #define	MAXSECTIONS	(8 * sizeof (int))	/* Not easily changeable */
94 #define	MAXARGS		30
95 #define MAXLINESIZE	1024
96 
97 #define	SectionToMaskBit(s)		(1 << (s))
98 #define SectionMaskHasSection(m, s)	(m & SectionToMaskBit(s))
99 
100 int techSectionNum;			/* ID of next new section */
101 SectionID techSectionMask;		/* Mask of sections already read */
102 
103 techSection techSectionTable[MAXSECTIONS];
104 techSection *techSectionFree;		/* Pointer to next free section */
105 techSection *techCurrentSection;	/* Pointer to current section */
106 
107 techSection *techFindSection();
108 
109 /*
110  * ----------------------------------------------------------------------------
111  *
112  * TechSectionGetMask --
113  *
114  * Get the SectionID mask for a specific section (specified by name).  The
115  * returned mask is inverted;  that is, it is a mask containing bits
116  * representing all the client sections except for the one sepcified.
117  * This return value can be passed to TechLoad to re-read a specific
118  * section.
119  *
120  * Results:
121  *	Returns the inverted mask for the selected section ID.
122  *
123  * Side effects:
124  *	If "depend" is non-NULL, the SectionID to which it points will be
125  *	set to a mask representing the mask of sections which depend on
126  *	the indicated section; that is, those sections which will be
127  *	invalidated if the indicated section is altered in any way.
128  *
129  * ----------------------------------------------------------------------------
130  */
131 
132 SectionID
TechSectionGetMask(sectionName,depend)133 TechSectionGetMask(sectionName, depend)
134     char *sectionName;
135     SectionID *depend;
136 {
137     techSection *tsp, *thissect;
138     SectionID invid = 0;
139     SectionID selected;
140 
141     thissect = techFindSection(sectionName);
142     if (thissect == NULL) return -1;
143 
144     selected = thissect->ts_thisSect;
145 
146     for (tsp = techSectionTable; tsp < techSectionFree; tsp++)
147     {
148 	if (tsp != thissect)
149 	{
150 	    invid |= tsp->ts_thisSect;
151 	    if (tsp->ts_prevSects & thissect->ts_thisSect)
152 		if (depend != NULL) *depend = tsp->ts_thisSect;
153 	}
154     }
155     return invid;
156 }
157 
158 /*
159  * ----------------------------------------------------------------------------
160  *
161  * TechInit --
162  *
163  * Initialize the technology module.
164  *
165  * Results:
166  *	None.
167  *
168  * Side effects:
169  *	Initializes the technology read-in module.
170  *	This function must be called before any other functions in
171  *	this module are called.  It is called exactly once at the start
172  *	of a magic session.
173  *
174  * ----------------------------------------------------------------------------
175  */
176 
177 void
TechInit()178 TechInit()
179 {
180     techCurrentSection = (techSection *) NULL;
181     techSectionFree = techSectionTable;
182     techSectionNum = 0;
183 }
184 
185 /*
186  * ----------------------------------------------------------------------------
187  *
188  * TechAddAlias --
189  *
190  *	Add an alternative name (alias) for a technology file section which
191  *	may be used in place of the primary name.
192  *
193  *	This has been added mainly to handle sections which have been
194  *	expanded beyond their original definition such that the section
195  *	name is no longer appropriate.  Case in point:  the "images"
196  *	section is broader in scope than the "contact" section, but
197  *	because contacts are a subset of images in version 7.3, it is
198  *	preferable to have an "images" section instead of a "contacts"
199  *	section, with allowances for backwards compatibility.
200  *
201  * Results:
202  *	None.
203  *
204  * Side effects:
205  *	Allocates string memory.
206  *
207  * ----------------------------------------------------------------------------
208  */
209 
210 void
TechAddAlias(primaryName,alias)211 TechAddAlias(primaryName, alias)
212     char *primaryName;
213     char *alias;
214 {
215     techSection *tsp;
216 
217     tsp = techFindSection(primaryName);
218     if (tsp == (techSection *) NULL)
219     {
220 	TxError("Unknown technology file section \"%s\" requested.\n",
221 		primaryName);
222     }
223     else
224     {
225 	if (tsp->ts_alias != NULL)
226 	    freeMagic(tsp->ts_alias);
227 	tsp->ts_alias = StrDup((char **)NULL, alias);
228     }
229 }
230 
231 /*
232  * ----------------------------------------------------------------------------
233  *
234  * changePlanesFunc() ---
235  *
236  * This function hacks the existing layout database in case a tech file
237  * is loaded which contains more or fewer planes than the exisiting
238  * technology.  This is doing nothing fancy; it is simply making sure
239  * that all memory allocation is accounted for.
240  *
241  * As a note for future implementation, it would be helpful to keep the
242  * old plane name definitions around and try to match up the old and new
243  * planes, so that it is possible to load a technology file which matches
244  * the existing technology except for the addition or subtraction of one
245  * or more planes (e.g., extra metal layer option) without completely
246  * invalidating an existing layout.
247  *
248  * As written, this function is inherently dangerous.  It is intended for
249  * use when loading a new tech file when there is no layout, just empty
250  * tile planes.
251  * ----------------------------------------------------------------------------
252  */
253 
254 int
changePlanesFunc(cellDef,arg)255 changePlanesFunc(cellDef, arg)
256     CellDef *cellDef;
257     int *arg;
258 {
259     int oldnumplanes = *arg;
260     int pNum;
261 
262     if (oldnumplanes < DBNumPlanes)
263     {
264 	/* New planes to be added */
265 	for (pNum = oldnumplanes; pNum < DBNumPlanes; pNum++)
266 	{
267 	    cellDef->cd_planes[pNum] = DBNewPlane((ClientData) TT_SPACE);
268 	}
269     }
270     else
271     {
272 	/* Old planes to be subtracted */
273 	for (pNum = DBNumPlanes; pNum < oldnumplanes; pNum++)
274 	{
275 	    if (cellDef->cd_planes[pNum] != NULL)
276 	    {
277 		DBFreePaintPlane(cellDef->cd_planes[pNum]);
278 		TiFreePlane(cellDef->cd_planes[pNum]);
279 		cellDef->cd_planes[pNum] = (Plane *) NULL;
280 	    }
281 	}
282     }
283     return 0;
284 }
285 
286 /*
287  * ----------------------------------------------------------------------------
288  *
289  * TechAddClient --
290  *
291  * Add a client to the technology module.
292  *
293  * Results:
294  *	None.
295  *
296  * Side effects:
297  *	Identifies "sectionName" as a valid name for a section of a .tech
298  *	file, and specifies that init() is the procedure to be called when
299  *	a new technology is loaded, proc() as the procedure to be called
300  *	for each line in the given section, and final() as the procedure to
301  *      be called after the last line in the given section.
302  *
303  *	The init() procedure takes no arguments.
304  *	The proc() procedure should be of the following form:
305  *		bool
306  *		proc(sectionName, argc, argv)
307  *			char *sectionName;
308  *			int argc;
309  *			char *argv[];
310  *		{
311  *		}
312  *	The final() procedure takes no arguments.
313  *
314  *	The argument prevSections should be a mask of the SectionID's
315  *	of all sections that must be read in before this one.
316  *
317  *	If the argument 'pSectionID' is non-NULL, it should point to
318  *	an int that will be set to the sectionID of this section.
319  *
320  *	It is legal for several procedures to be associated with a given
321  *	sectionName; this is accomplished through successive calls to
322  *	TechAddClient with the same sectionName.  The procedures will
323  *	be invoked in the order in which they were handed to TechAddClient().
324  *
325  *	If the procedure given is NULL for init(), proc(), or final(), no
326  *	procedure is invoked.
327  *
328  * ----------------------------------------------------------------------------
329  */
330 
331 void
TechAddClient(sectionName,init,proc,final,prevSections,pSectionID,opt)332 TechAddClient(sectionName, init, proc, final, prevSections, pSectionID, opt)
333     char *sectionName;
334     void (*init)();
335     bool (*proc)();
336     void (*final)();
337     SectionID prevSections;
338     SectionID *pSectionID;
339     bool opt; /* optional section */
340 {
341     techSection *tsp;
342     techClient *tcp, *tcl;
343 
344     tsp = techFindSection(sectionName);
345     if (tsp == (techSection *) NULL)
346     {
347 	tsp = techSectionFree++;
348 	ASSERT(tsp < &techSectionTable[MAXSECTIONS], "TechAddClient");
349 	tsp->ts_name = StrDup((char **) NULL, sectionName);
350 	tsp->ts_alias = NULL;
351 	tsp->ts_clients = (techClient *) NULL;
352 	tsp->ts_thisSect = SectionToMaskBit(techSectionNum);
353 	tsp->ts_prevSects = (SectionID) 0;
354 	tsp->ts_optional = opt;
355 	techSectionNum++;
356     }
357 
358     tsp->ts_prevSects |= prevSections;
359     if (pSectionID)
360 	*pSectionID = tsp->ts_thisSect;
361 
362     tcp = (techClient *) mallocMagic(sizeof (techClient));
363     ASSERT(tcp != (techClient *) NULL, "TechAddClient");
364     tcp->tc_init = init;
365     tcp->tc_proc = proc;
366     tcp->tc_final = final;
367     tcp->tc_next = (techClient *) NULL;
368 
369     if (tsp->ts_clients == (techClient *) NULL)
370 	tsp->ts_clients = tcp;
371     else
372     {
373 	for (tcl = tsp->ts_clients; tcl->tc_next; tcl = tcl->tc_next)
374 	    /* Nothing */;
375 	tcl->tc_next = tcp;
376     }
377 }
378 
379 /*
380  * ----------------------------------------------------------------------------
381  * TechLoad --
382  *
383  * Initialize technology description information from a file.
384  *
385  * Results:
386  *	TRUE if technology is successfully initialized (all required
387  *	sections present and error free); FALSE otherwise.  Unrecognized
388  *	sections cause an error message to be printed, but do not otherwise
389  *	affect the result returned by TechLoad().
390  *
391  * Side effects:
392  *	Calls technology initialization routines of other modules
393  *	to initialize technology-specific information.
394  *
395  * ----------------------------------------------------------------------------
396  */
397 
398 bool
TechLoad(filename,initmask)399 TechLoad(filename, initmask)
400     char *filename;
401     SectionID initmask;
402 {
403     FILE *tf;
404     techSection *tsp;
405     techClient *tcp;
406     char suffix[20], line[MAXLINESIZE], *realname;
407     char *argv[MAXARGS];
408     SectionID mask, badMask;
409     int argc, s;
410     bool retval, skip;
411     filestack *fstack, *newstack;
412     filestack topfile;
413 
414     fstack = NULL;
415     techLineNumber = 0;
416     badMask = (SectionID) 0;
417     int saveNumPlanes;
418 
419     int changePlanesFunc();	/* forward declaration */
420     int checkForPaintFunc();	/* forward declaration */
421 
422     if (initmask == -1)
423     {
424 	TxError("Invalid technology file section requested.\n");
425 	return (FALSE);
426     }
427 
428     /* If NULL is passed to argument "filename", this is a reload and	*/
429     /* we should read TechFileName verbatim.				*/
430 
431     if (filename == NULL)
432     {
433 	if (TechFileName != NULL)
434 	{
435 	    tf = PaOpen(TechFileName, "r", (char *)NULL, ".", SysLibPath, &realname);
436 	    if (tf == (FILE *) NULL)
437 	    {
438 		TxError("Could not find file '%s' in any of these "
439 			"directories:\n         %s\n",
440 			TechFileName, SysLibPath);
441 		return (FALSE);
442 	    }
443 	}
444 	else
445 	{
446 	    TxError("Invalid technology file load.\n");
447 	    return (FALSE);
448 	}
449     }
450     else
451     {
452 	char *sptr, *dptr;
453 
454 	/* TECH_VERSION in the filename is deprecated as of magic version	*/
455 	/* 7.2.27;  TECH_VERSION is no longer defined in the utils/Makefile.	*/
456 	/* It has been changed to TECH_FORMAT_VERSION, left at version 27,	*/
457 	/* and placed in utils/tech.h.  It is needed for backward		*/
458 	/* compatibility with *.tech27 files, of which there are many.	*/
459 
460 	(void) sprintf(suffix, ".tech");
461 
462 	/* Added 1/20/2015 to correspond to change to PaLockOpen();	*/
463 	/* Always strip suffix from filename when suffix is specified.	*/
464 
465 	sptr = strrchr(filename, '/');
466 	if (sptr == NULL)
467 	    sptr = filename;
468 	else
469 	    sptr++;
470 
471 	/* If the filename is ".tech", then remove the extension and	*/
472 	/* process like it was not there at all.			*/
473 	dptr = strrchr(sptr, '.');
474 	if ((dptr != NULL) && !strcmp(dptr, suffix))
475 	    *dptr = '\0';
476 
477 	/* If a non-standard extension was used, then honor it */
478 	if ((dptr != NULL) && (*dptr != '\0'))
479 	    tf = PaOpen(filename, "r", (char *)NULL, ".", SysLibPath, &realname);
480 	else
481 	    tf = PaOpen(filename, "r", suffix, ".", SysLibPath, &realname);
482 
483 	if (tf == (FILE *) NULL)
484 	{
485 	    /* Try looking for tech files from the last version to	*/
486 	    /* put the version number into the filename itself.	*/
487 
488 	    (void) sprintf(suffix, ".tech%d", TECH_FORMAT_VERSION);
489 
490 	    tf = PaOpen(filename, "r", suffix, ".", SysLibPath, &realname);
491 	    if (tf == (FILE *) NULL)
492 	    {
493 		TxError("Could not find file '%s.tech' in any of these "
494 			"directories:\n         %s\n",
495 			filename, SysLibPath);
496 		return (FALSE);
497 	    }
498 	}
499 	StrDup(&TechFileName, realname);
500 
501 	// In case filename is not a temporary string, put it back the
502 	// way it was.
503 	if (dptr != NULL) *dptr = '.';
504     }
505 
506     topfile.file = tf;
507     topfile.next = NULL;
508     fstack = &topfile;
509 
510     // If TechLoad is called with initmask == -2, test that the file
511     // exists and is readable, and that the first non-comment line
512     // is the keyword "tech".
513 
514     if (initmask == -2)
515     {
516 	argc = techGetTokens(line, sizeof line, &fstack, argv);
517 	fclose(tf);
518 	if (argc != 1) return (FALSE);
519 	if (strcmp(argv[0], "tech")) return (FALSE);
520 	return (TRUE);
521     }
522 
523     /*
524      * Mark all sections as being unread.
525      */
526     techSectionMask = initmask;
527     for (tsp = techSectionTable; tsp < techSectionFree; tsp++)
528     {
529 	tsp->ts_read = FALSE;
530     }
531 
532     /*
533      * Run section initializations if this is not a reload.
534      * CIF istyle, CIF ostyle, and extract sections need calls
535      * to the init functions which clean up memory devoted to
536      * remembering all the styles.
537      */
538 
539     if (filename != NULL)
540     {
541 #ifdef CIF_MODULE
542 	CIFTechInit();
543 	CIFReadTechInit();
544 #endif
545 	ExtTechInit();
546 	DRCTechInit();
547 	MZTechInit();
548 
549             /* Changing number of planes requires handling on every     */
550             /* celldef.  So we need to save the original number of      */
551             /* planes to see if it shrinks or expands.                  */
552 
553 
554 	saveNumPlanes = DBNumPlanes;
555     }
556 
557     /*
558      * Sections in a technology file begin with a single line containing
559      * the keyword identifying the section, and end with a single line
560      * containing the keyword "end".
561      */
562 
563     retval = TRUE;
564     skip = FALSE;
565     while ((argc = techGetTokens(line, sizeof line, &fstack, argv)) >= 0)
566     {
567 	/* Check for file inclusions (can be nested) */
568 	if ((argc > 1) && (!strcmp(argv[0], "include")))
569 	{
570 	    char *sptr;
571 
572 	    tf = PaOpen(argv[1], "r", suffix, ".", SysLibPath, NULL);
573 	    if (tf != NULL)
574 	    {
575 		newstack = (filestack *)mallocMagic(sizeof(filestack));
576 		newstack->file = tf;
577 		newstack->next = fstack;
578 		fstack = newstack;
579 		continue;
580 	    }
581 
582 	    /* Check the directory from which the tech file	*/
583 	    /* itself was read.					*/
584 
585 	    if ((sptr = strrchr(TechFileName, '/')) != NULL)
586 	    {
587 		*sptr = '\0';
588 		tf = PaOpen(argv[1], "r", suffix, TechFileName, NULL, NULL);
589 		*sptr = '/';
590 		if (tf != NULL)
591 		{
592 		    newstack = (filestack *)mallocMagic(sizeof(filestack));
593 		    newstack->file = tf;
594 		    newstack->next = fstack;
595 		    fstack = newstack;
596 		    continue;
597 		}
598 	    }
599 	    TechError("Warning: Couldn't find include file %s\n", argv[1]);
600 	}
601 
602 	if (!skip && techCurrentSection == NULL)
603 	{
604 	    if (argc != 1)
605 	    {
606 		TechError("Bad section header line\n");
607 		goto skipsection;
608 	    }
609 
610 	    tsp = techFindSection(argv[0]);
611 	    if (tsp == (techSection *) NULL)
612 	    {
613 		TechError("Unrecognized section name: %s\n", argv[0]);
614 		goto skipsection;
615 	    }
616 	    else if (initmask & tsp->ts_thisSect)
617 	    {
618 		skip = TRUE;
619 		continue;
620 	    }
621 	    if (mask = (tsp->ts_prevSects & ~techSectionMask))
622 	    {
623 		techSection *sp;
624 
625 		TechError("Section %s appears too early.\n", argv[0]);
626 		TxError("\tMissing prerequisite sections:\n");
627 		for (sp = techSectionTable; sp < techSectionFree; sp++)
628 		    if (mask & sp->ts_thisSect)
629 			TxError("\t\t%s\n", sp->ts_name);
630 		goto skipsection;
631 	    }
632 	    techCurrentSection = tsp;
633 
634 	    /* Invoke initialization routines for all clients that
635 	     * provided them.
636 	     */
637 
638 	    for (tcp = techCurrentSection->ts_clients;
639 		    tcp != NULL;
640 		    tcp = tcp->tc_next)
641 	    {
642 		if (tcp->tc_init)
643 		    (void) (*tcp->tc_init)();
644 	    }
645 	    continue;
646 	}
647 
648 	/* At the end of the section, invoke the finalization routine
649 	 * of the client's, if there is one.
650 	 */
651 
652 	if (argc == 1 && strcmp(argv[0], "end") == 0)
653 	{
654 	    if (!skip)
655 	    {
656 		techSectionMask |= techCurrentSection->ts_thisSect;
657 		techCurrentSection->ts_read = TRUE;
658 		for (tcp = techCurrentSection->ts_clients;
659 			tcp != NULL;
660 			tcp = tcp->tc_next)
661 		{
662 		    if (tcp->tc_final)
663 			(*tcp->tc_final)();
664 		}
665 	    }
666 	    techCurrentSection = (techSection *) NULL;
667 	    skip = FALSE;
668 	    continue;
669 	}
670 
671 	if (!skip)
672 	    for (tcp = techCurrentSection->ts_clients;
673 			tcp != NULL;
674 			tcp = tcp->tc_next)
675 		if (tcp->tc_proc)
676 		{
677 		    if (!(*tcp->tc_proc)(techCurrentSection->ts_name,argc,argv))
678 		    {
679 			retval = FALSE;
680 			badMask |= techCurrentSection->ts_thisSect;
681 		    }
682 		}
683 	continue;
684 
685 skipsection:
686 	TxError("[Skipping to \"end\"]\n");
687 	skip = TRUE;
688     }
689 
690     if (badMask)
691     {
692 	TxError("The following sections of %s contained errors:\n", TechFileName);
693 	for (s = 0; s < techSectionNum; s++)
694 	    if (SectionMaskHasSection(badMask, s))
695 		TxError("    %s\n", techSectionTable[s].ts_name);
696     }
697 
698     for (tsp = techSectionTable; tsp < techSectionFree; tsp++)
699     {
700 	if (!(initmask & tsp->ts_thisSect))
701 	{
702 	    if (!tsp->ts_read && !tsp->ts_optional)
703 	    {
704 		TxError("Section \"%s\" was missing from %s.\n",
705 			tsp->ts_name, TechFileName);
706 		retval = FALSE;
707 	    }
708 	}
709     }
710 
711     /* In case we hit an error in an included file. . . */
712     while ((fstack != NULL) && (fstack != &topfile))
713     {
714 	fclose(fstack->file);
715 	freeMagic(fstack);
716 	fstack = fstack->next;
717     }
718     if (fstack) fclose(fstack->file);
719 
720     /* Note:  If filename is NULL, then individual sections are	*/
721     /* being reloaded, and it is the responsibility of the	*/
722     /* calling routine to invoke any exit function specific to	*/
723     /* that section (e.g., DRCTechScale() when loading a new	*/
724     /* DRC style).						*/
725 
726     if ((filename != NULL) && (retval == TRUE))
727     {
728 	/* If internal scalefactor is not the default 1:1, then we  */
729 	/* need to scale the techfile numbers accordingly.          */
730 
731 	if ((DBLambda[0] != 1) || (DBLambda[1] != 1))
732 	{
733 	    int d = DBLambda[0];
734 	    int n = DBLambda[1];
735 
736 	    CIFTechInputScale(d, n, TRUE);
737 	    CIFTechOutputScale(d, n);
738 	    DRCTechScale(d, n);
739 	    ExtTechScale(d, n);
740 	    WireTechScale(d, n);
741 #ifdef LEF_MODULE
742 	    LefTechScale(d, n);
743 #endif
744 #ifdef ROUTE_MODULE
745 	    RtrTechScale(d, n);
746 #endif
747 	    TxPrintf("Scaled tech values by %d / %d to"
748 			" match internal grid scaling\n", n, d);
749 
750 	    /* Check if we're below the scale set by cifoutput gridlimit */
751 	    if (CIFTechLimitScale(1, 1))
752 		TxError("WARNING:  Current grid scale is smaller"
753 		" than the minimum for the process!\n");
754 	}
755 
756 	/* Post-technology reading routines */
757 
758 #ifdef ROUTE_MODULE
759 	MZAfterTech();
760 	IRAfterTech();
761 	GAMazeInitParms();
762 #endif
763 	PlowAfterTech();
764 
765 	if (DBCellSrDefs(0, checkForPaintFunc, (ClientData)&saveNumPlanes))
766 	{
767 	    if (saveNumPlanes != DBNumPlanes)
768 		TxError("Warning:  Number of planes has changed.  ");
769 	    TxError("Existing layout may be invalid.\n");
770 	}
771 	if (saveNumPlanes != DBNumPlanes)
772 	    DBCellSrDefs(0, changePlanesFunc, (ClientData) &saveNumPlanes);
773     }
774     else if (retval == FALSE)
775     {
776 	/* On error, remove any existing technology file name */
777 	DBNumPlanes = saveNumPlanes;
778 	freeMagic(TechFileName);
779 	TechFileName = NULL;
780     }
781 
782     return (retval);
783 }
784 
785 /*
786  * ----------------------------------------------------------------------------
787  *
788  * TechError --
789  *
790  * Print an error message referring to a given line number in the
791  * technology module.
792  *
793  * Results:
794  *	None.
795  *
796  * Side effects:
797  *	Prints an error message.
798  *
799  * ----------------------------------------------------------------------------
800  */
801 
802 void
TechPrintLine()803 TechPrintLine()
804 {
805     char *section;
806 
807     if (techCurrentSection)
808 	section = techCurrentSection->ts_name;
809     else
810 	section = "(none)";
811 
812     TxError("%s: line %d: section %s:\n\t",
813 		TechFileName, techLineNumber, section);
814 }
815 
816 void
TechError(char * fmt,...)817 TechError(char *fmt, ...)
818 {
819     va_list args;
820 
821     TechPrintLine();
822     va_start(args, fmt);
823     Vfprintf(stderr, fmt, args);
824     va_end(args);
825 }
826 
827 
828 /* ================== Functions local to this module ================== */
829 
830 /*
831  * ----------------------------------------------------------------------------
832  *
833  * techFindSection --
834  *
835  * Return a pointer to the entry in techSectionTable for the section
836  * of the given name.
837  *
838  * Results:
839  *	A pointer to the new entry, or NULL if none could be found.
840  *
841  * Side effects:
842  *	None.
843  *
844  * ----------------------------------------------------------------------------
845  */
846 
847 techSection *
techFindSection(sectionName)848 techFindSection(sectionName)
849     char *sectionName;
850 {
851     techSection *tsp;
852 
853     for (tsp = techSectionTable; tsp < techSectionFree; tsp++)
854     {
855 	if (!strcmp(tsp->ts_name, sectionName))
856 	    return (tsp);
857 	else if (tsp->ts_alias != NULL)
858 	{
859 	    if (!strcmp(tsp->ts_alias, sectionName))
860 		return (tsp);
861 	}
862     }
863     return ((techSection *) NULL);
864 }
865 
866 /*
867  * ----------------------------------------------------------------------------
868  *
869  * techGetTokens --
870  *
871  * Read a line from the technology file and split it up into tokens.
872  * Blank lines are ignored.  Lines ending in backslash are joined
873  * to their successor lines.
874  * We assume that all macro definition and comment elimination has
875  * been done by the C preprocessor.
876  *
877  * Results:
878  *	Returns the number of tokens into which the line was split, or
879  *	-1 on end of file.  Never returns 0.
880  *
881  * Side effects:
882  *	Copies the line just read into 'line'.  The trailing newline
883  *	is turned into a '\0'.  The line is broken into tokens which
884  *	are then placed into argv.
885  *
886  * ----------------------------------------------------------------------------
887  */
888 
889 int
techGetTokens(line,size,fstack,argv)890 techGetTokens(line, size, fstack, argv)
891     char *line;			/* Character array into which line is read */
892     int size;			/* Size of character array */
893     filestack **fstack;		/* Open technology file on top of stack */
894     char *argv[];		/* Vector of tokens built by techGetTokens() */
895 {
896     char *get, *put, *getp;
897     bool inquote;
898     int argc = 0;
899     int currspace;		/* chars remaining before end of line[size] */
900     FILE *file;			/* Current technology file */
901 
902     file = (*fstack)->file;
903 
904     /* Read one line into the buffer, joining lines when they end
905      * in backslashes.
906      */
907 
908     /* Code revision (MDG, Stanford):  Prevent the 1024-character limit due */
909     /* to unconditional decrement of size.  Long comment lists could cause  */
910     /* infinite looping.  New code interprets first non-space character '#' */
911     /* as a comment character, rather than requiring it to be in the first  */
912     /* column.								    */
913 
914     /* Code revision (RTE, Open Circuit Design):  Handle DOS-style CR/LF    */
915     /* Code revision (RTE, Open Circuit Design):  Handle "include" files    */
916 
917 start:
918      get = line;
919      currspace = size;
920      while (currspace > 0)
921      {
922 	techLineNumber += 1;
923 	while (fgets(get, currspace, file) == NULL) {
924 	    if ((*fstack)->next != NULL)
925 	    {
926 		fclose((*fstack)->file);
927 		*fstack = (*fstack)->next;
928 		file = (*fstack)->file;
929 	    }
930 	    else
931 		return (-1);
932 	}
933 	getp = get;
934 	while(isspace(*getp)) getp++;
935 	if (*getp == '#') continue;
936 	for (put = get; *put != '\n'; put++) currspace -= 1;
937 	if (put != get)
938 	{
939 	    put--;
940 	    if (*put == 0xd) put--;	/* Handle DOS-style CR/LF */
941 	    if (*put == '\\')
942 	    {
943 		get = put;
944 		continue;
945 	    }
946 	    put++;
947 	}
948 	*put= '\0';
949 	break;
950     }
951     if (currspace == 0) TechError("long line truncated\n");
952 
953     get = put = line;
954 
955     while (*get != '\0')
956     {
957 	/* Skip leading blanks */
958 
959 	while (isspace(*get)) get++;
960 
961 	/* Beginning of the token is here. */
962 
963 	argv[argc] = put = get;
964 	if (*get == '"')
965 	{
966 	    get++;
967 	    inquote = TRUE;
968 	} else inquote = FALSE;
969 
970 	/*
971 	 * Grab up characters to the end of the token.  Any character
972 	 * preceded by a backslash is taken literally.
973 	 */
974 
975 	while (*get != '\0')
976 	{
977 	    if (inquote)
978 	    {
979 		if (*get == '"') break;
980 	    }
981 	    else if (isspace(*get)) break;
982 
983 	    if (*get == '\\')	/* Process quoted characters literally */
984 	    {
985 		get += 1;
986 		if (*get == '\0') break;
987 	    }
988 
989 	    /* Copy into token receiving area */
990 	    *put++ = *get++;
991 	}
992 
993 	/*
994 	 * If we got no characters in the token, we must have been at
995 	 * the end of the line.
996 	 */
997 	if (get == argv[argc])
998 	    break;
999 
1000 	/* Terminate the token and advance over the terminating character. */
1001 
1002 	if (*get != '\0') get++;	/* Careful!  could be at end of line! */
1003 	*put++ = '\0';
1004 	argc++;
1005     }
1006 
1007     if (argc == 0)
1008 	goto start;
1009 
1010     return (argc);
1011 }
1012