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