1 /*
2  * CalmaWrite.c --
3  *
4  * Output of Calma GDS-II stream format.
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/calma/CalmaWrite.c,v 1.8 2010/12/22 16:29:06 tim Exp $";
21 #endif  /* not lint */
22 
23 #include <stdio.h>
24 #include <stdint.h>
25 #include <stdlib.h>	/* for random() */
26 #include <string.h>
27 #include <ctype.h>
28 #include <arpa/inet.h>
29 #include <sys/types.h>
30 #include <arpa/inet.h>	/* for htons() */
31 #ifdef	SYSV
32 #include <time.h>
33 #else
34 #include <sys/time.h>
35 #endif
36 
37 #include "utils/magic.h"
38 #include "utils/malloc.h"
39 #include "utils/geometry.h"
40 #include "tiles/tile.h"
41 #include "utils/utils.h"
42 #include "utils/hash.h"
43 #include "database/database.h"
44 #include "database/databaseInt.h"
45 #include "utils/tech.h"
46 #include "cif/cif.h"
47 #include "cif/CIFint.h"
48 #include "utils/signals.h"
49 #include "windows/windows.h"
50 #include "dbwind/dbwind.h"
51 #include "utils/styles.h"
52 #include "textio/textio.h"
53 #include "calma/calmaInt.h"
54 #include "utils/main.h"		/* for Path and CellLibPath */
55 #include "utils/stack.h"
56 
57     /* Exports */
58 bool CalmaDoLibrary = FALSE;	  /* If TRUE, do not output the top level */
59 bool CalmaDoLabels = TRUE;	  /* If FALSE, don't output labels with GDS-II */
60 bool CalmaDoLower = TRUE;	  /* If TRUE, allow lowercase labels. */
61 bool CalmaFlattenArrays = FALSE;  /* If TRUE, output arrays as individual uses */
62 bool CalmaAddendum = FALSE;	  /* If TRUE, do not output readonly cell defs */
63 bool CalmaNoDateStamp = FALSE;	  /* If TRUE, output zero for creation date stamp */
64 bool CalmaAllowUndefined = FALSE; /* If TRUE, allow calls to undefined cells */
65 
66     /* Experimental stuff---not thoroughly tested (as of Sept. 2007)! */
67 bool CalmaContactArrays = FALSE; /* If TRUE, output contacts as subcell arrays */
68 bool CalmaMergeTiles = FALSE;	 /* If TRUE, merge tiles into polygons in output. */
69 
70     /* Forward declarations */
71 extern int calmaWriteInitFunc();
72 extern int calmaWriteMarkFunc();
73 extern int calmaWritePaintFunc();
74 extern int calmaMergePaintFunc();
75 extern int calmaWriteUseFunc();
76 extern int calmaPaintLabelFunc();
77 extern void calmaWriteContacts();
78 extern void calmaDelContacts();
79 extern void calmaOutFunc();
80 extern void calmaOutStructName();
81 extern void calmaWriteLabelFunc();
82 extern void calmaOutHeader();
83 extern void calmaOutDate();
84 extern void calmaOutStringRecord();
85 extern void calmaOut8();
86 extern void calmaOutR8();
87 extern void calmaProcessBoundary();
88 extern void calmaMergeBoundaries();
89 extern void calmaRemoveColinear();
90 extern void calmaRemoveDegenerate();
91 
92 /* Structure used by calmaWritePaintFunc() */
93 
94 typedef struct {
95    FILE *f;		/* File stream for output		*/
96    Rect *area;		/* Clipping area, in GDS coordinates	*/
97    int type;		/* Layer index				*/
98 } calmaOutputStruct;
99 
100 /*--------------------------------------------------------------*/
101 /* Structures used by the tile merging algorithm 		*/
102 /*--------------------------------------------------------------*/
103 
104 #define GDS_PENDING	0
105 #define GDS_UNPROCESSED CLIENTDEFAULT
106 #define GDS_PROCESSED	1
107 
108 #define PUSHTILE(tp) \
109     if ((tp)->ti_client == (ClientData) GDS_UNPROCESSED) { \
110 	(tp)->ti_client = (ClientData) GDS_PENDING; \
111 	STACKPUSH((ClientData) (tp), SegStack); \
112     }
113 
114 #define LB_EXTERNAL	0	/* Polygon external edge	*/
115 #define LB_INTERNAL	1	/* Polygon internal edge	*/
116 #define LB_INIT		2	/* Data not yet valid		*/
117 
118 typedef struct LB1 {
119     char lb_type;		/* Boundary Type (external or internal) */
120     Point lb_start;		/* Start point */
121     struct LB1 *lb_next;	/* Next point record */
122 } LinkedBoundary;
123 
124 typedef struct BT1 {
125     LinkedBoundary *bt_first;   /* Polygon list */
126     int bt_points;		/* Number of points in this list */
127     struct BT1 *bt_next;	/* Next polygon record */
128 } BoundaryTop;
129 
130 /*--------------------------------------------------------------*/
131 
132 /* Number assigned to each cell */
133 int calmaCellNum;
134 
135 /* Factor by which to scale Magic coordinates for cells and labels. */
136 int calmaWriteScale;
137 
138 /* Scale factor for outputting paint: */
139 int calmaPaintScale;
140 
141 /*
142  * Current layer number and "type".
143  * In GDS-II format, this is output with each rectangle.
144  */
145 int calmaPaintLayerNumber;
146 int calmaPaintLayerType;
147 
148 /*
149  * Hash table used to determine which GDS libraries have been output
150  */
151 HashTable calmaLibHash;
152 HashTable calmaPrefixHash;
153 HashTable calmaUndefHash;
154 
155 /* Imports */
156 extern time_t time();
157 
158 
159 /* -------------------------------------------------------------------- */
160 
161 /*
162  * Macros to output various pieces of Calma information.
163  * These are macros for speed.
164  */
165 
166 /* -------------------------------------------------------------------- */
167 
168 /*
169  * calmaOutRH --
170  *
171  * Output a Calma record header.
172  * This consists of a two-byte count of the number of bytes in the
173  * record (including the two count bytes), a one-byte record type,
174  * and a one-byte data type.
175  */
176 #define	calmaOutRH(count, type, datatype, f) \
177     { calmaOutI2(count, f); (void) putc(type, f); (void) putc(datatype, f); }
178 
179 /*
180  * calmaOutI2 --
181  *
182  * Output a two-byte integer.
183  * Calma byte order is the same as the network byte order used
184  * by the various network library procedures.
185  */
186 #define	calmaOutI2(n, f) \
187     { \
188 	union { short u_s; char u_c[2]; } u; \
189 	u.u_s = htons(n); \
190 	(void) putc(u.u_c[0], f); \
191 	(void) putc(u.u_c[1], f); \
192     }
193 /*
194  * calmaOutI4 --
195  *
196  * Output a four-byte integer.
197  * Calma byte order is the same as the network byte order used
198  * by the various network library procedures.
199  */
200 #define calmaOutI4(n, f) \
201     { \
202 	union { long u_i; char u_c[4]; } u; \
203 	u.u_i = htonl(n); \
204 	(void) putc(u.u_c[0], f); \
205 	(void) putc(u.u_c[1], f); \
206 	(void) putc(u.u_c[2], f); \
207 	(void) putc(u.u_c[3], f); \
208     }
209 
210 static char calmaMapTableStrict[] =
211 {
212       0,    0,    0,    0,    0,    0,    0,    0,	/* NUL - BEL */
213       0,    0,    0,    0,    0,    0,    0,    0,	/* BS  - SI  */
214       0,    0,    0,    0,    0,    0,    0,    0,	/* DLE - ETB */
215       0,    0,    0,    0,    0,    0,    0,    0,	/* CAN - US  */
216     '_',  '_',  '_',  '_',  '$',  '_',  '_',  '_',	/* SP  - '   */
217     '_',  '_',  '_',  '_',  '_',  '_',  '_',  '_',	/* (   - /   */
218     '0',  '1',  '2',  '3',  '4',  '5',  '6',  '7',	/* 0   - 7   */
219     '8',  '9',  '_',  '_',  '_',  '_',  '_',  '_',	/* 8   - ?   */
220     '_',  'A',  'B',  'C',  'D',  'E',  'F',  'G',	/* @   - G   */
221     'H',  'I',  'J',  'K',  'L',  'M',  'N',  'O',	/* H   - O   */
222     'P',  'Q',  'R',  'S',  'T',  'U',  'V',  'W',	/* P   - W   */
223     'X',  'Y',  'Z',  '_',  '_',  '_',  '_',  '_',	/* X   - _   */
224     '_',  'a',  'b',  'c',  'd',  'e',  'f',  'g',	/* `   - g   */
225     'h',  'i',  'j',  'k',  'l',  'm',  'n',  'o',	/* h   - o   */
226     'p',  'q',  'r',  's',  't',  'u',  'v',  'w',	/* p   - w   */
227     'x',  'y',  'z',  '_',  '_',  '_',  '_',  0,	/* x   - DEL */
228 };
229 
230 static char calmaMapTablePermissive[] =
231 {
232       0,    0,    0,    0,    0,    0,    0,    0,	/* NUL - BEL */
233       0,    0,    0,    0,    0,    0,    0,    0,	/* BS  - SI  */
234       0,    0,    0,    0,    0,    0,    0,    0,	/* DLE - ETB */
235       0,    0,    0,    0,    0,    0,    0,    0,	/* CAN - US  */
236     ' ',  '!',  '"',  '#',  '$',  '&',  '%', '\'',	/* SP  - '   */
237     '(',  ')',  '*',  '+',  ',',  '-',  '.',  '/',	/* (   - /   */
238     '0',  '1',  '2',  '3',  '4',  '5',  '6',  '7',	/* 0   - 7   */
239     '8',  '9',  ':',  ';',  '<',  '=',  '>',  '?',	/* 8   - ?   */
240     '@',  'A',  'B',  'C',  'D',  'E',  'F',  'G',	/* @   - G   */
241     'H',  'I',  'J',  'K',  'L',  'M',  'N',  'O',	/* H   - O   */
242     'P',  'Q',  'R',  'S',  'T',  'U',  'V',  'W',	/* P   - W   */
243     'X',  'Y',  'Z',  '[', '\\',  ']',  '^',  '_',	/* X   - _   */
244     '`',  'a',  'b',  'c',  'd',  'e',  'f',  'g',	/* `   - g   */
245     'h',  'i',  'j',  'k',  'l',  'm',  'n',  'o',	/* h   - o   */
246     'p',  'q',  'r',  's',  't',  'u',  'v',  'w',	/* p   - w   */
247     'x',  'y',  'z',  '{',  '|',  '}',  '~',    0,	/* x   - DEL */
248 };
249 
250 
251 /*
252  * ----------------------------------------------------------------------------
253  *
254  * CalmaWrite --
255  *
256  * Write out the entire tree rooted at the supplied CellDef in Calma
257  * GDS-II stream format, to the specified file.
258  *
259  * Results:
260  *	TRUE if the cell could be written successfully, FALSE otherwise.
261  *
262  * Side effects:
263  *	Writes a file to disk.
264  *	In the event of an error while writing out the cell,
265  *	the external integer errno is set to the UNIX error
266  *	encountered.
267  *
268  * Algorithm:
269  *
270  *	Calma names can be strings of up to CALMANAMELENGTH characters.
271  *	Because general names won't map into Calma names, we use the
272  *	original cell name only if it is legal Calma, and otherwise
273  *	generate a unique numeric name for the cell.
274  *
275  *	We make a depth-first traversal of the entire design tree, outputting
276  *	each cell to the Calma file.  If a given cell has not been read in
277  *	when we visit it, we read it in ourselves.
278  *
279  *	No hierarchical design rule checking or bounding box computation
280  *	occur during this traversal -- both are explicitly avoided.
281  *
282  * ----------------------------------------------------------------------------
283  */
284 
285 bool
CalmaWrite(rootDef,f)286 CalmaWrite(rootDef, f)
287     CellDef *rootDef;	/* Pointer to CellDef to be written */
288     FILE *f;		/* Open output file */
289 {
290     int oldCount = DBWFeedbackCount, problems;
291     bool good;
292     CellUse dummy;
293     HashEntry *he;
294     HashSearch hs;
295 
296     /*
297      * Do not attempt to write anything if a CIF/GDS output style
298      * has not been specified in the technology file.
299      */
300     if (!CIFCurStyle)
301     {
302 	TxError("No CIF/GDS output style set!\n");
303 	return FALSE;
304     }
305 
306     HashInit(&calmaLibHash, 32, 0);
307     HashInit(&calmaPrefixHash, 32, 0);
308     HashInit(&calmaUndefHash, 32, 0);
309 
310     /*
311      * Make sure that the entire hierarchy rooted at rootDef is
312      * read into memory and that timestamp mismatches are resolved
313      * (this is needed so that we know that bounding boxes are OK).
314      */
315 
316     dummy.cu_def = rootDef;
317     if (DBCellReadArea(&dummy, &rootDef->cd_bbox, !CalmaAllowUndefined))
318     {
319 	TxError("Failure to read entire subtree of the cell.\n");
320 	return FALSE;
321     }
322 
323     DBFixMismatch();
324 
325     /*
326      * Go through all cells currently having CellDefs in the
327      * def symbol table and mark them with negative numbers
328      * to show that they should be output, but haven't yet
329      * been.
330      */
331     (void) DBCellSrDefs(0, calmaWriteInitFunc, (ClientData) NULL);
332     rootDef->cd_client = (ClientData) -1;
333     calmaCellNum = -2;
334 
335     /* Output the header, identifying this file */
336     calmaOutHeader(rootDef, f);
337 
338     /*
339      * Write all contact cell definitions first
340      */
341     if (CalmaContactArrays) calmaWriteContacts(f);
342 
343     /*
344      * We perform a post-order traversal of the tree rooted at 'rootDef',
345      * to insure that each child cell is output before it is used.  The
346      * root cell is output last.
347      */
348     calmaProcessDef(rootDef, f, CalmaDoLibrary);
349 
350     /*
351      * Check for any cells that were instanced in the output definition
352      * (by dumping a GDS file from a read-only view) but were never
353      * defined (because the dumped GDS contained undefined references).
354      * If these are in the database but were not part of the tree of
355      * rootDef, then output them at the end.
356      */
357     HashStartSearch(&hs);
358     while ((he = HashNext(&calmaUndefHash, &hs)) != NULL)
359     {
360 	char *refname = (char *)HashGetValue(he);
361         if (refname && (refname[0] == '0'))
362 	{
363 	    CellDef *extraDef;
364 
365 	    extraDef = DBCellLookDef((char *)he->h_key.h_name);
366 	    if (extraDef != NULL)
367 		calmaProcessDef(extraDef, f, FALSE);
368 	    else
369 	    	TxError("Error:  Cell %s is not defined in the output file!\n",
370 				refname + 1);
371 	}
372     }
373 
374     /* Finish up by outputting the end-of-library marker */
375     calmaOutRH(4, CALMA_ENDLIB, CALMA_NODATA, f);
376     fflush(f);
377     good = !ferror(f);
378 
379     /* See if any problems occurred */
380     if (problems = (DBWFeedbackCount - oldCount))
381 	TxPrintf("%d problems occurred.  See feedback entries.\n", problems);
382 
383     /*
384      * Destroy all contact cell definitions
385      */
386     if (CalmaContactArrays) calmaDelContacts();
387 
388     HashFreeKill(&calmaLibHash);
389     HashKill(&calmaPrefixHash);
390     HashFreeKill(&calmaUndefHash);
391     return (good);
392 }
393 
394 
395 /*
396  * ----------------------------------------------------------------------------
397  *
398  * calmaDumpStructure --
399  *
400  * Parse a structure (cell) from the GDS file.  Check the name against the
401  * existing database and modify the name in case of a collision.  Then dump
402  * the entire cell verbatim.  The cell gets prefixed with the name "prefix"
403  * to prevent collisions with other unknown GDS files that may be dumped
404  * verbatim.
405  *
406  * ----------------------------------------------------------------------------
407  */
408 
409 bool
calmaDumpStructure(def,outf,calmaDefHash,filename)410 calmaDumpStructure(def, outf, calmaDefHash, filename)
411     CellDef *def;
412     FILE *outf;
413     HashTable *calmaDefHash;
414     char *filename;
415 {
416     int nbytes, rtype;
417     char *strname = NULL, *newnameptr;
418     HashEntry *he, *he2;
419     CellDef *edef;
420     char *prefix = NULL;
421 
422     /* Make sure this is a structure; if not, let the caller know we're done */
423     PEEKRH(nbytes, rtype);
424     if (nbytes <= 0) return (FALSE);
425 
426     if (rtype != CALMA_BGNSTR)
427     {
428 	calmaOutRH(nbytes, rtype, CALMA_I2, outf);
429         return (FALSE);
430     }
431 
432     /* Read the structure name */
433     if (!calmaSkipExact(CALMA_BGNSTR)) goto syntaxerror;
434     if (!calmaReadStringRecord(CALMA_STRNAME, &strname)) goto syntaxerror;
435     TxPrintf("Reading \"%s\".\n", strname);
436 
437     /* Output structure begin */
438     calmaOutRH(28, CALMA_BGNSTR, CALMA_I2, outf);
439     if (CalmaNoDateStamp)
440     	calmaOutDate(time((time_t *) 0), outf);
441     else
442     	calmaOutDate(def->cd_timestamp, outf);
443     calmaOutDate(time((time_t *) 0), outf);
444 
445     /* Do a quick check of the calmaUndefHash table to see if this cell	*/
446     /* was previously used in a GDS file that does not define it (a GDS	*/
447     /* addendum library).						*/
448 
449     he = HashLookOnly(&calmaUndefHash, strname);
450     if (he != NULL)
451     {
452 	HashSearch hs;
453 	char *undefname = (char *)HashGetValue(he);
454 
455     	HashStartSearch(&hs);
456     	while ((he2 = HashNext(&calmaLibHash, &hs)) != NULL)
457 	{
458 	    prefix = (char *)HashGetValue(he2);
459 	    if (!strncmp(prefix, undefname + 1, strlen(prefix)))
460 		break;
461 	}
462 	if (he2 == NULL)
463 	{
464 	    prefix = (char *)NULL;
465 	    TxError("Error:  Unreferenced cell %s prefix is unrecorded!\n",
466 			undefname);
467 	}
468 	else
469 	{
470 	    /* Remove this entry from the hash table */
471 	    freeMagic(undefname);
472 	    HashRemove(&calmaUndefHash, strname);
473 	}
474     }
475     else
476     {
477     	/* Find the structure's unique prefix, in case structure calls	*/
478     	/* subcells that are not yet defined.				*/
479 
480     	he2 = HashFind(&calmaLibHash, filename);
481     	if (he2 == NULL)
482 	    TxError("Fatal error:  Library %s not recorded!\n", filename);
483     	else
484 	    prefix = (char *)HashGetValue(he2);
485     }
486 
487     /* Prefix structure name with def name, and output new structure name */
488     he = HashFind(calmaDefHash, strname);
489     if ((newnameptr = (char *)HashGetValue(he)) != NULL)
490     {
491 	/* Structure is defined more than once */
492 	if (*newnameptr != '0')
493 	    TxError("Structure %s defined redundantly in GDS\n", strname);
494 	else
495 	    *newnameptr = '1';
496 	/* To be considered:  Should the structure be output more than once? */
497 	calmaOutStringRecord(CALMA_STRNAME, newnameptr + 1, outf);
498     }
499     else if (!strcmp(strname, def->cd_name))
500     {
501 	/* This is the top level cell being defined.  Its name	*/
502 	/* does not get modified.				*/
503 
504 	newnameptr = mallocMagic(strlen(strname) + 2);
505 	sprintf(newnameptr, "1%s", strname);
506 	calmaOutStringRecord(CALMA_STRNAME, newnameptr + 1, outf);
507 	HashSetValue(he, (char *)newnameptr);
508     }
509     else
510     {
511 	/* Check if the cellname is in the magic cell database.	*/
512 	/* If so, check if that cell is an abstract view and	*/
513 	/* calls the same library.  If so, the name does not	*/
514 	/* get prefixed.  Otherwise, the cell is limited to the	*/
515 	/* GDS library being read, and so takes the prefix.	*/
516 
517 	/* Modify the cellname by prefixing with "prefix", which is a	*/
518 	/* unique identifier for the library.				*/
519 
520 	/* Check if the cell is defined in the database */
521 	edef = DBCellLookDef(strname);
522 	if (edef != NULL)
523 	{
524 	    bool isAbstract, isReadOnly;
525 	    char *chklibname, *filenamesubbed = NULL;
526 
527 	    /* Is view abstract? */
528 	    DBPropGet(edef, "LEFview", &isAbstract);
529 	    chklibname = (char *)DBPropGet(edef, "GDS_FILE", &isReadOnly);
530 
531 	    if (isAbstract && isReadOnly)
532 	    {
533 		filenamesubbed = StrDup(NULL, filename);
534 		DBPathSubstitute(filename, filenamesubbed, edef);
535 	    }
536 
537 	    /* Is the library name the same as the filename? */
538 	    if (isAbstract && isReadOnly && !strcmp(filenamesubbed, chklibname))
539 	    {
540 		/* Same library, so keep the cellname and mark the cell */
541 		/* as having been written to GDS.			*/
542 
543 		newnameptr = mallocMagic(strlen(strname) + 2);
544 		sprintf(newnameptr, "1%s", strname);
545 		HashSetValue(he, (char *)newnameptr);
546 	    }
547 	    else
548 	    {
549 		/* Find the unique library prefix and prepend it to the cell name */
550 
551 		if (prefix == NULL)
552 		    newnameptr = strname;   /* Should never happen */
553 		else
554 		{
555 		    newnameptr = mallocMagic(strlen(strname) + strlen(prefix) + 8);
556 		    sprintf(newnameptr, "1%s%s", prefix, strname);
557 		    HashSetValue(he, (char *)newnameptr);
558 		}
559 	    }
560 	    if (filenamesubbed) freeMagic(filenamesubbed);
561 	}
562 	else
563 	{
564 	    /* Find the unique library prefix and prepend it to the cell name */
565 
566 	    if (prefix == NULL)
567 		newnameptr = strname;	    /* Should never happen */
568 	    else
569 	    {
570 		newnameptr = mallocMagic(strlen(strname) + strlen(prefix) + 8);
571 		sprintf(newnameptr, "1%s%s", prefix, strname);
572 		HashSetValue(he, (char *)newnameptr);
573 	    }
574 	}
575 	calmaOutStringRecord(CALMA_STRNAME, newnameptr + 1, outf);
576     }
577     freeMagic(strname);
578 
579     /* Read and output the structure until CALMA_ENDSTR, except */
580     /* for handling any AREF or SREF names, which need name	*/
581     /* checks.							*/
582 
583     while (1)
584     {
585 	int datatype;
586 
587 	READI2(nbytes);
588 	if (feof(calmaInputFile))
589 	{
590 	    /* Unexpected end-of-file */
591 	    fseek(calmaInputFile, -(CALMAHEADERLENGTH), SEEK_END);
592 	    break;
593 	}
594 	rtype = getc(calmaInputFile);
595 	datatype = getc(calmaInputFile);
596 	switch (rtype) {
597 	    case CALMA_BGNSTR:
598 		UNREADRH(nbytes, rtype);
599 		return (TRUE);
600 	    case CALMA_ENDLIB:
601 		UNREADRH(nbytes, rtype);
602 		return (FALSE);
603 
604 	    case CALMA_SNAME:
605 		UNREADRH(nbytes, rtype);
606 		if (!calmaReadStringRecord(CALMA_SNAME, &strname))
607 		    goto syntaxerror;
608 
609 		he = HashFind(calmaDefHash, strname);
610 		newnameptr = (char *)HashGetValue(he);
611 		if (newnameptr != NULL)
612 		{
613 		    calmaOutStringRecord(CALMA_SNAME, newnameptr + 1, outf);
614 		}
615 		else
616 		{
617 		    TxError("Diagnostic:  %s is a forward reference?\n", strname);
618 
619 		    /* Could be a forward reference, so do a rename in	*/
620 		    /* the same way used for structure definitions.	*/
621 
622 		    newnameptr = (char *)mallocMagic(strlen(strname) +
623 				strlen(prefix) + 8);
624 		    sprintf(newnameptr, "0%s%s", prefix, strname);
625 
626 		    edef = DBCellLookDef(newnameptr + 1);
627 		    if (edef != NULL)
628 			sprintf(newnameptr, "0%s%s[[0]]", prefix, strname);
629 		    HashSetValue(he, (char *)newnameptr);
630 		    calmaOutStringRecord(CALMA_SNAME, newnameptr + 1, outf);
631 		}
632 		break;
633 
634 	    default:
635 		calmaOutRH(nbytes, rtype, datatype, outf);
636 		nbytes -= 4;
637 
638 		/* Copy nbytes from input to output */
639 		while (nbytes-- > 0)
640 		{
641 		    int byte;
642 		    if ((byte = getc(calmaInputFile)) < 0)
643 		    {
644 			TxError("End of file with %d bytes remaining to be read.\n",
645 				nbytes);
646 			while (nbytes-- > 0)
647 			    putc(0, outf);	// zero-pad output
648 			return (FALSE);
649 		    }
650 		    else
651 			putc(byte, outf);
652 		}
653 		break;
654 	}
655     }
656     return (FALSE);
657 
658 syntaxerror:
659     /* Syntax error: skip to CALMA_ENDSTR */
660     calmaSkipTo(CALMA_ENDSTR);
661     return (FALSE);
662 }
663 
664 /*
665  * ----------------------------------------------------------------------------
666  *
667  * calmaFullDump --
668  *
669  * Read in a GDS-II stream format library and dump its contents to
670  * file "outf" verbatim except for cell references, which are renamed
671  * if there is a conflict with a cell def in memory.
672  *
673  * Because the dump is inside a larger output, the header and trailer
674  * are discarded.
675  *
676  * ----------------------------------------------------------------------------
677  */
678 
679 void
calmaFullDump(def,fi,outf,filename)680 calmaFullDump(def, fi, outf, filename)
681     CellDef *def;
682     FILE *fi;
683     FILE *outf;
684     char *filename;
685 {
686     int version, rval;
687     char *libname = NULL, *testlib, uniqlibname[4];
688     char *sptr, *viewopts;
689     bool isAbstract;
690     HashTable calmaDefHash;
691     HashSearch hs;
692     HashEntry *he, *he2;
693 
694     static int hdrSkip[] = { CALMA_FORMAT, CALMA_MASK, CALMA_ENDMASKS,
695 		CALMA_REFLIBS, CALMA_FONTS, CALMA_ATTRTABLE,
696 		CALMA_STYPTABLE, CALMA_GENERATIONS, CALMA_UNITS, -1 };
697     static int skipBeforeLib[] = { CALMA_LIBDIRSIZE, CALMA_SRFNAME,
698 		CALMA_LIBSECUR, -1 };
699 
700     HashInit(&calmaDefHash, 32, 0);
701 
702     calmaInputFile = fi;
703     cifReadCellDef = def;
704 
705     /* Read and ignore the GDS-II header */
706 
707     if (!calmaReadI2Record(CALMA_HEADER, &version)) goto done;
708     if (!calmaSkipExact(CALMA_BGNLIB)) goto done;
709     calmaSkipSet(skipBeforeLib);
710     if (!calmaReadStringRecord(CALMA_LIBNAME, &libname)) goto done;
711 
712     // NOTE:  CALMA_UNITS needs to be parsed to determine if units in
713     // the input file are compatible with units being used in the output
714     // file.
715     calmaSkipSet(hdrSkip);
716 
717     // Record the GDS library so it will not be processed again.
718     he = HashFind(&calmaLibHash, filename);
719     if ((char *)HashGetValue(he) != NULL)
720     {
721     	TxPrintf("Library %s has already been processed\n", libname);
722 	return;
723     }
724 
725     /* If property LEFview is defined as "no_prefix" instead of "TRUE",
726      * then do not create a unique prefix for subcells.  This is generally
727      * ill-advised, but can be needed if a foundry runs specific DRC checks
728      * on specific cell names, in which case adding a prefix can cause DRC
729      * errors to appear.  It is then incumbent on the user to ensure that
730      * names in the GDS file do not shadow any names in the database.
731      */
732 
733     viewopts = (char *)DBPropGet(def, "LEFview", &isAbstract);
734     if ((!isAbstract) || (strcasecmp(viewopts, "no_prefix")))
735     {
736 	/* Generate a SHORT name for this cell (else it is easy to run into the
737 	 * GDS 32-character cellname limit).  Save it in the hash record.  The
738 	 * chance of generating the same prefix for a library that has items
739 	 * with conflicting names is vanishingly small, but to be pedantic, store
740 	 * the prefix in a hash table and check to make sure that uniqueness is
741 	 * ensured.  NOTE:  The first character of a SPICE name cannot be a
742 	 * number.  Therefore the first character is alphabetical, and the second
743 	 * is alphanumeric.  There are only 936 possible combinations, but this
744 	 * is only meant to distinguish cells in large IP blocks of unknown
745 	 * origin, of which only a limited number would be expected.  Beware
746 	 * the implications for LVS, as the prefixed names from layout would
747 	 * need to be compared to un-prefixed names from another netlist.
748 	 */
749 	while (TRUE)
750 	{
751 	    rval = random() % 26;
752 	    rval = 'A' + rval;
753 	    uniqlibname[0] = (char)(rval & 127);
754 	    rval = random() % 36;
755 	    rval = (rval < 26) ? ('A' + rval) : ('0' + rval - 26);
756 	    uniqlibname[1] = (char)(rval & 127);
757 	    uniqlibname[2] = '_';
758 	    uniqlibname[3] = '\0';
759 	    he2 = HashLookOnly(&calmaPrefixHash, uniqlibname);
760 	    if (he2 == NULL)
761 	    {
762 		he2 = HashFind(&calmaPrefixHash, uniqlibname);
763 		break;
764 	    }
765 	}
766 	HashSetValue(he, StrDup(NULL, uniqlibname));
767     }
768     else
769 	HashSetValue(he, StrDup(NULL, ""));
770 
771     while (calmaDumpStructure(def, outf, &calmaDefHash, filename))
772 	if (SigInterruptPending)
773 	    goto done;
774     calmaSkipExact(CALMA_ENDLIB);
775 
776 done:
777 
778     /* Check that all references were resolved.  If not, then it is
779      * probably because a library was an "addendum"-type library
780      * referencing things in other libraries.  Move those cell
781      * references to the calmaUndefHash before killing calmaDefHash.
782      */
783 
784     HashStartSearch(&hs);
785     while ((he = HashNext(&calmaDefHash, &hs)) != NULL)
786     {
787 	char *refname = (char *)HashGetValue(he);
788         if (refname[0] == '0')
789 	{
790 	    he2 = HashFind(&calmaUndefHash, (char *)he->h_key.h_name);
791 	    HashSetValue(he2, StrDup(NULL, refname));
792 	}
793     }
794 
795     HashFreeKill(&calmaDefHash);
796     if (libname != NULL) freeMagic(libname);
797     return;
798 }
799 
800 /*
801  * ----------------------------------------------------------------------------
802  *
803  * calmaWriteInitFunc --
804  *
805  * Filter function called on behalf of CalmaWrite() above.
806  * Responsible for setting the cif number of each cell to zero.
807  *
808  * Results:
809  *	Returns 0 to indicate that the search should continue.
810  *
811  * Side effects:
812  *	Modify the calma numbers of the cells they are passed.
813  *
814  * ----------------------------------------------------------------------------
815  */
816 
817 int
calmaWriteInitFunc(def)818 calmaWriteInitFunc(def)
819     CellDef *def;
820 {
821     def->cd_client = (ClientData) 0;
822     return (0);
823 }
824 
825 /*
826  * ----------------------------------------------------------------------------
827  *
828  * calmaProcessUse --
829  * calmaProcessDef --
830  *
831  * Main loop of Calma generation.  Performs a post-order, depth-first
832  * traversal of the tree rooted at 'def'.  Only cells that have not
833  * already been output are processed.
834  *
835  * The procedure calmaProcessDef() is called initially; calmaProcessUse()
836  * is called internally by DBCellEnum().
837  *
838  * Results:
839  *	None.
840  *
841  * Side effects:
842  *	Causes Calma GDS-II stream-format to be output.
843  *	Returns when the stack is empty.
844  *
845  * ----------------------------------------------------------------------------
846  */
847 
848 int
calmaProcessUse(use,outf)849 calmaProcessUse(use, outf)
850     CellUse *use;	/* Process use->cu_def */
851     FILE *outf;		/* Stream file */
852 {
853     return (calmaProcessDef(use->cu_def, outf, FALSE));
854 }
855 
856 int
calmaProcessDef(def,outf,do_library)857 calmaProcessDef(def, outf, do_library)
858     CellDef *def;	/* Output this def's children, then the def itself */
859     FILE *outf;		/* Stream file */
860     bool do_library;	/* If TRUE, output only children of def, but not def */
861 {
862     char *filename;
863     bool isReadOnly, oldStyle, hasContent, isAbstract, hasGDSEnd;
864     HashEntry *he;
865 
866     /* Skip if already output */
867     if ((int) def->cd_client > 0)
868 	return (0);
869 
870     /* Assign it a (negative) number if it doesn't have one yet */
871     if ((int) def->cd_client == 0)
872 	def->cd_client = (ClientData) calmaCellNum--;
873 
874     /* Mark this cell */
875     def->cd_client = (ClientData) (- (int) def->cd_client);
876 
877     /* Read the cell in if it is not already available. */
878     if ((def->cd_flags & CDAVAILABLE) == 0)
879     {
880 	bool dereference = (def->cd_flags & CDDEREFERENCE) ? TRUE : FALSE;
881 	if (!DBCellRead(def, (char *) NULL, TRUE, dereference, NULL))
882 	    return (0);
883     }
884 
885     /*
886      * Flag an error if attempting to write the default (UNNAMED) cell
887      * into GDS.  This is not strictly an error but is almost certainly
888      * not what the user intended.
889      */
890 
891     if (!strcmp(def->cd_name, UNNAMED))
892     {
893 	TxError("Error:  Cell has the default name \"%s\"!\n", UNNAMED);
894     }
895 
896     /*
897      * Check if this is a read-only file that is supposed to be copied
898      * verbatim from input to output.  If so, do the direct copy.  If
899      * not, or if there is any problem obtaining the original cell
900      * definition, resort to writing out magic's version of the def,
901      * and print a warning message.
902      *
903      * Treat the lack of a GDS_START property as an indication
904      * that we should treat this cell like a reference-only
905      * cell.  That is, the instance will be called but no
906      * definition will appear in the output.
907      */
908 
909     DBPropGet(def, "LEFview", &isAbstract);
910     DBPropGet(def, "GDS_START", &hasContent);
911     DBPropGet(def, "GDS_END", &hasGDSEnd);
912     filename = (char *)DBPropGet(def, "GDS_FILE", &isReadOnly);
913 
914     /* When used with "calma addendum true", don't output the read-only	*/
915     /* cells.  This makes the library incomplete and dependent on the	*/
916     /* vendor libraries, so use with caution.				*/
917 
918     if (isReadOnly && hasContent && CalmaAddendum) return (0);
919 
920     /* Give a strongly-worded statement about writing abstract views */
921 
922     if (isAbstract && !isReadOnly)
923 	TxError("Warning:  Writing abstract view of \"%s\" to GDS.  This is"
924 			" probably not what you want to do.\n",
925 			def->cd_name);
926 
927     /*
928      * Output the definitions for any of our descendants that have
929      * not already been output.  Numbers are assigned to the subcells
930      * as they are output.  If the cell will get a "full dump" (by
931      * having GDS_START but no GDS_END), then do not output any subcells,
932      * as they are expected to be in the referenced GDS file.
933      */
934     if (!hasContent || hasGDSEnd)
935 	if (DBCellEnum(def, calmaProcessUse, (ClientData) outf) != 0)
936 	    return 1;
937 
938     /* Give some feedback to the user */
939     TxPrintf("   Generating output for cell %s\n", def->cd_name);
940 
941     if (isReadOnly && hasContent)
942     {
943 	char *buffer, *offptr, *retfilename;
944 	size_t defsize, numbytes;
945 	off_t cellstart, cellend, structstart;
946  	dlong cval;
947 	FILE *fi;
948 
949 	/* Use PaOpen() so the paths searched are the same as were	*/
950 	/* searched to find the .mag file that indicated this GDS file.	*/
951 
952 	fi = PaOpen(filename, "r", "", Path, CellLibPath, &retfilename);
953 	if (fi == NULL)
954 	{
955 	    /* This is a rare error, but if the subcell is inside	*/
956 	    /* another vendor GDS, it would not normally be output.	*/
957 
958 	    DBPropGet((def->cd_parents->cu_parent == NULL) ? def :
959 			def->cd_parents->cu_parent, "GDS_FILE", &isReadOnly);
960 	    if (isReadOnly)
961 	    {
962 		def->cd_flags |= CDVENDORGDS;
963 		return 0;	/* Ignore without raising an error */
964 	    }
965 
966 	    TxError("Calma output error:  Can't find GDS file \"%s\" "
967 				"for vendor cell \"%s\".  It will not be output.\n",
968 				filename, def->cd_name);
969 
970 	    if (CalmaAllowUndefined)
971 		return 0;
972 	    else
973 		return 1;
974 	}
975 	else if (isAbstract || (!hasGDSEnd))
976 	{
977 	    /* This is the trickiest part.  If the cell view is abstract then	*/
978 	    /* the cell view has no hierarchy, and there is no way to descend	*/
979 	    /* into the cell hierarchy and discover and write out all the	*/
980 	    /* dependencies.  The dependencies are in the file but not in the	*/
981 	    /* range GDS_START to GDS_END.  Furthermore, the dependencies have	*/
982 	    /* not been loaded so naming conflicts may exist.  So the file must	*/
983 	    /* be read end-to-end and parsed carefully.				*/
984 
985 	    he = HashLookOnly(&calmaLibHash, retfilename);
986 	    if (he == NULL)
987 		calmaFullDump(def, fi, outf, retfilename);
988 
989 	    fclose(fi);
990 	    def->cd_flags |= CDVENDORGDS;
991 	}
992 	else
993 	{
994 	    offptr = (char *)DBPropGet(def, "GDS_END", NULL);
995 	    sscanf(offptr, "%"DLONG_PREFIX"d", &cval);
996 	    cellend = (off_t)cval;
997 	    offptr = (char *)DBPropGet(def, "GDS_BEGIN", &oldStyle);
998 	    if (!oldStyle)
999 	    {
1000 		offptr = (char *)DBPropGet(def, "GDS_START", NULL);
1001 
1002 		/* Write our own header and string name, to ensure	*/
1003 		/* that the magic cell name and GDS name match.		*/
1004 
1005 		/* Output structure header */
1006 		calmaOutRH(28, CALMA_BGNSTR, CALMA_I2, outf);
1007     		if (CalmaNoDateStamp)
1008 		    calmaOutDate(time((time_t *) 0), outf);
1009 		else
1010 		    calmaOutDate(def->cd_timestamp, outf);
1011 		calmaOutDate(time((time_t *) 0), outf);
1012 
1013 		/* Name structure the same as the magic cellname */
1014 		calmaOutStructName(CALMA_STRNAME, def, outf);
1015 	    }
1016 
1017 	    sscanf(offptr, "%"DLONG_PREFIX"d", &cval);
1018 	    cellstart = (off_t)cval;
1019 
1020 	    /* GDS_START has been defined as the start of data after the cell	*/
1021 	    /* structure name.  However, a sanity check is wise, so move back	*/
1022 	    /* that far and make sure the structure name is there.		*/
1023 	    structstart = cellstart - (off_t)(strlen(def->cd_name));
1024 	    if (strlen(def->cd_name) & 0x1) structstart--;
1025 	    structstart -= 2;
1026 
1027 	    fseek(fi, structstart, SEEK_SET);
1028 
1029 	    /* Read the structure name and check against the CellDef name */
1030 	    defsize = (size_t)(cellstart - structstart);
1031 	    buffer = (char *)mallocMagic(defsize + 1);
1032 	    numbytes = fread(buffer, sizeof(char), (size_t)defsize, fi);
1033 	    if (numbytes == defsize)
1034 	    {
1035 		buffer[defsize] = '\0';
1036 		if (buffer[0] != 0x06 || buffer[1] != 0x06)
1037 		{
1038 		    TxError("Calma output error:  Structure name not found"
1039 				" at GDS file position %"DLONG_PREFIX"d\n", cellstart);
1040 		    TxError("Calma output error:  Can't write cell from vendor GDS."
1041 				"  Using magic's internal definition\n");
1042 		    isReadOnly = FALSE;
1043 		}
1044 		else if (strcmp(&buffer[2], def->cd_name))
1045 		{
1046 		    TxError("Calma output warning:  Structure definition has name"
1047 				" %s but cell definition has name %s.\n",
1048 				&buffer[2], def->cd_name);
1049 		    TxError("The structure definition will be given the cell name.\n");
1050 		}
1051 	    }
1052 	    else
1053 	    {
1054 		TxError("Calma output error:  Can't read cell from vendor GDS."
1055 				"  Using magic's internal definition\n");
1056 		isReadOnly = FALSE;
1057 	    }
1058 
1059 	    if (cellend < cellstart)	/* Sanity check */
1060 	    {
1061 		TxError("Calma output error:  Bad vendor GDS file reference!\n");
1062 		isReadOnly = FALSE;
1063 	    }
1064 	    else if (isReadOnly)
1065 	    {
1066 		/* Important note:  mallocMagic() is limited to size integer.	*/
1067 		/* This will fail on a structure larger than ~2GB.		*/
1068 
1069 		defsize = (size_t)(cellend - cellstart);
1070 		buffer = (char *)mallocMagic(defsize);
1071 
1072 		numbytes = fread(buffer, sizeof(char), (size_t)defsize, fi);
1073 
1074 		if (numbytes == defsize)
1075 		{
1076 		    /* Sanity check:  buffer must end with a structure	*/
1077 		    /* definition end (record 0x07).			*/
1078 
1079 		    if (buffer[defsize - 4] != 0x00 ||
1080 				buffer[defsize - 3] != 0x04 ||
1081 				buffer[defsize - 2] != 0x07 ||
1082 				buffer[defsize - 1] != 0x00)
1083 		    {
1084 			TxError("Calma output error:  Structure end definition not found"
1085 				" at GDS file position %"DLONG_PREFIX"d\n", cellend);
1086 			TxError("Calma output error:  Can't write cell from vendor GDS."
1087 				"  Using magic's internal definition\n");
1088 			isReadOnly = FALSE;
1089 		    }
1090 		    else
1091 		    {
1092 		    	numbytes = fwrite(buffer, sizeof(char), (size_t)defsize, outf);
1093 		    	if (numbytes <= 0)
1094 		    	{
1095 			    TxError("Calma output error:  Can't write cell from "
1096 					"vendor GDS.  Using magic's internal "
1097 					"definition\n");
1098 			    isReadOnly = FALSE;
1099 		    	}
1100 		    }
1101 		}
1102 		else
1103 		{
1104 		    TxError("Calma output error:  Can't read cell from vendor GDS."
1105 				"  Using magic's internal definition\n");
1106 
1107 		    /* Additional information as to why data did not match */
1108 		    TxError("Size of data requested: %"DLONG_PREFIX"d", defsize);
1109 		    TxError("Length of data read: %"DLONG_PREFIX"d", numbytes);
1110 		    isReadOnly = FALSE;
1111 		}
1112 		freeMagic(buffer);
1113 	    }
1114 	    fclose(fi);
1115 
1116 	    /* Mark the definition as vendor GDS so that magic doesn't	*/
1117 	    /* try to generate subcell interaction or array interaction	*/
1118 	    /* paint for it.						*/
1119 
1120 	    def->cd_flags |= CDVENDORGDS;
1121 	}
1122     }
1123 
1124     /* Output this cell definition from the Magic database */
1125     if (!isReadOnly)
1126 	if (!do_library)
1127 	    calmaOutFunc(def, outf, &TiPlaneRect);
1128 
1129     return 0;
1130 }
1131 
1132 
1133 /*
1134  * ----------------------------------------------------------------------------
1135  *
1136  * calmaOutFunc --
1137  *
1138  * Write out the definition for a single cell as a GDS-II stream format
1139  * structure.  We try to preserve the original cell's name if it is legal
1140  * in GDS-II; otherwise, we generate a unique name.
1141  *
1142  * Results:
1143  *	None.
1144  *
1145  * Side effects:
1146  *	Appends to the open Calma output file.
1147  *
1148  * ----------------------------------------------------------------------------
1149  */
1150 
1151 void
calmaOutFunc(def,f,cliprect)1152 calmaOutFunc(def, f, cliprect)
1153     CellDef *def;	/* Pointer to cell def to be written */
1154     FILE *f;		/* Open output file */
1155     Rect *cliprect;	/* Area to clip to (used for contact cells),
1156 			 * in CIF/GDS coordinates.
1157 			 */
1158 {
1159     Label *lab;
1160     CIFLayer *layer;
1161     Rect bigArea;
1162     int type;
1163     int dbunits;
1164     calmaOutputStruct cos;
1165 
1166     cos.f = f;
1167     cos.area = (cliprect == &TiPlaneRect) ? NULL : cliprect;
1168     cos.type = -1;
1169 
1170     /* Output structure begin */
1171     calmaOutRH(28, CALMA_BGNSTR, CALMA_I2, f);
1172     if (CalmaNoDateStamp)
1173     	calmaOutDate(time((time_t *) 0), f);
1174     else
1175 	calmaOutDate(def->cd_timestamp, f);
1176     calmaOutDate(time((time_t *) 0), f);
1177 
1178     /* Output structure name */
1179     calmaOutStructName(CALMA_STRNAME, def, f);
1180 
1181     /* Since Calma database units are nanometers, multiply all units by 10,
1182      * modified by the scale multiplier.
1183      */
1184 
1185     dbunits = (CIFCurStyle->cs_flags & CWF_ANGSTROMS) ? 100 : 10;
1186     if ((dbunits % CIFCurStyle->cs_expander) == 0)
1187     {
1188 	calmaWriteScale = CIFCurStyle->cs_scaleFactor * dbunits
1189 		/ CIFCurStyle->cs_expander;
1190 	calmaPaintScale = dbunits / CIFCurStyle->cs_expander;
1191     }
1192     else
1193     {
1194 	TxError("Calma output error:  Output scale units are %2.1f nanometers.\n",
1195 		(float)dbunits / (float)CIFCurStyle->cs_expander);
1196 	TxError("Magic Calma output will be scaled incorrectly!\n");
1197 	if ((dbunits == 10) && ((100 % CIFCurStyle->cs_expander) == 0))
1198 	{
1199 	    TxError("Please add \"units angstroms\" to the cifoutput section"
1200 			" of the techfile.\n");
1201 	}
1202 	else
1203 	{
1204 	    TxError("Magic GDS output is limited to a minimum dimension of"
1205 			" 1 angstrom.\n");
1206 	}
1207 	/* Set expander to 10 so output scales are not zero. */
1208 	calmaWriteScale = CIFCurStyle->cs_scaleFactor;
1209 	calmaPaintScale = 1;
1210     }
1211 
1212     /*
1213      * Output the calls that the child makes to its children.  For
1214      * arrays we output a single call, unlike CIF, since Calma
1215      * supports the notion of arrays.
1216      */
1217     (void) DBCellEnum(def, calmaWriteUseFunc, (ClientData) f);
1218 
1219     /* Output all the tiles associated with this cell; skip temporary layers */
1220     GEO_EXPAND(&def->cd_bbox, CIFCurStyle->cs_radius, &bigArea);
1221     CIFErrorDef = def;
1222     CIFGen(def, def, &bigArea, CIFPlanes, &DBAllTypeBits, TRUE, TRUE, FALSE,
1223 		(ClientData)f);
1224     if (!CIFHierWriteDisable)
1225 	CIFGenSubcells(def, &bigArea, CIFPlanes);
1226     if (!CIFArrayWriteDisable)
1227 	CIFGenArrays(def, &bigArea, CIFPlanes);
1228 
1229     for (type = 0; type < CIFCurStyle->cs_nLayers; type++)
1230     {
1231 	layer = CIFCurStyle->cs_layers[type];
1232 	if (layer->cl_flags & CIF_TEMP) continue;
1233 	if (!CalmaIsValidLayer(layer->cl_calmanum)) continue;
1234 	cos.type = type;
1235 	calmaPaintLayerNumber = layer->cl_calmanum;
1236 	calmaPaintLayerType = layer->cl_calmatype;
1237 
1238 	if (layer->cl_flags & CIF_LABEL)
1239 	    DBSrPaintArea((Tile *) NULL, CIFPlanes[type],
1240 		    cliprect, &CIFSolidBits, calmaPaintLabelFunc,
1241 		    (ClientData) &cos);
1242 	else
1243 	    DBSrPaintArea((Tile *) NULL, CIFPlanes[type],
1244 		    cliprect, &CIFSolidBits, (CalmaMergeTiles) ?
1245 		    calmaMergePaintFunc : calmaWritePaintFunc,
1246 		    (ClientData) &cos);
1247     }
1248 
1249     /* Output labels.  Do this in two passes, first for non-port labels	*/
1250     /* while finding the highest-numbered port.  Then output the port	*/
1251     /* labels (if any) in the order of the port index.			*/
1252 
1253     if (CalmaDoLabels)
1254     {
1255 	int i, maxport = -1;
1256 
1257 	for (lab = def->cd_labels; lab; lab = lab->lab_next)
1258 	{
1259 	    type = CIFCurStyle->cs_labelLayer[lab->lab_type];
1260 	    if ((type >= 0) && (lab->lab_flags & PORT_DIR_MASK) == 0)
1261 	    {
1262 		calmaWriteLabelFunc(lab, type, f);
1263 	    }
1264 	    else
1265 	    {
1266 		if ((int)lab->lab_port > maxport)
1267 		    maxport = (int)lab->lab_port;
1268 	    }
1269 	}
1270 	if (maxport >= 0)
1271 	    for (i = 0; i <= maxport; i++)
1272 		for (lab = def->cd_labels; lab; lab = lab->lab_next)
1273 		{
1274 		    type = CIFCurStyle->cs_portLayer[lab->lab_type];
1275 		    if ((type >= 0) && ((lab->lab_flags & PORT_DIR_MASK) != 0) &&
1276 				(lab->lab_port == i))
1277 		    {
1278 			calmaWriteLabelFunc(lab, type, f);
1279 			/* break; */  /* Do not limit to unique labels! */
1280 		    }
1281 		}
1282     }
1283 
1284     /* End of structure */
1285     calmaOutRH(4, CALMA_ENDSTR, CALMA_NODATA, f);
1286 }
1287 
1288 /*
1289  * ----------------------------------------------------------------------------
1290  *
1291  * calmaIsUseNameDefault --
1292  *
1293  * Determine if this use name is not default; that is, it is not the name
1294  * of the cell def followed by an underscore and a use index number.  If
1295  * it is not default, then we want to write out the use name as a property
1296  * in the GDS stream file so that we can recover the name when the file is
1297  * read back into magic.
1298  *
1299  * Results:
1300  *	TRUE if the cell use ID is a default name; FALSE if not.
1301  *
1302  * Side Effects:
1303  *	None.
1304  *
1305  * ----------------------------------------------------------------------------
1306  */
1307 
1308 bool
calmaIsUseNameDefault(defName,useName)1309 calmaIsUseNameDefault(defName, useName)
1310     char *defName;
1311     char *useName;
1312 {
1313     int idx, slen;
1314     char *sptr;
1315 
1316     if (useName == NULL) return TRUE;
1317     slen = strlen(defName);
1318     if (!strncmp(defName, useName, slen))
1319     {
1320 	sptr = useName + slen;
1321 	if (*sptr != '_') return FALSE;
1322 	else sptr++;
1323 	if (sscanf(sptr, "%d", &idx) != 1) return FALSE;
1324 	else return TRUE;
1325     }
1326     return FALSE;
1327 }
1328 
1329 /*
1330  * ----------------------------------------------------------------------------
1331  *
1332  * calmaWriteUseFunc --
1333  *
1334  * Filter function, called by DBCellEnum on behalf of calmaOutFunc above,
1335  * to write out each CellUse called by the CellDef being output.  If the
1336  * CellUse is an array, we output it as a single array instead of as
1337  * individual uses like CIF.
1338  *
1339  * Results:
1340  *	None.
1341  *
1342  * Side effects:
1343  *	Appends to the open Calma output file.
1344  *
1345  * ----------------------------------------------------------------------------
1346  */
1347 
1348 int
calmaWriteUseFunc(use,f)1349 calmaWriteUseFunc(use, f)
1350     CellUse *use;
1351     FILE *f;
1352 {
1353     /*
1354      * r90, r180, and r270 are Calma 8-byte real representations
1355      * of the angles 90, 180, and 270 degrees.  Because there are
1356      * only 4 possible values, it is faster to have them pre-computed
1357      * than to format with calmaOutR8().
1358      */
1359     static unsigned char r90[] = { 0x42, 0x5a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
1360     static unsigned char r180[] = { 0x42, 0xb4, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
1361     static unsigned char r270[] = { 0x43, 0x10, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00 };
1362     unsigned char *whichangle;
1363     int x, y, topx, topy, rows, cols, xxlate, yxlate, hdrsize;
1364     int rectype, stransflags;
1365     Transform *t;
1366     bool isArray = FALSE;
1367     Point p, p2;
1368 
1369     topx = use->cu_xhi - use->cu_xlo;
1370     if (topx < 0) topx = -topx;
1371     topy = use->cu_yhi - use->cu_ylo;
1372     if (topy < 0) topy = -topy;
1373 
1374     /*
1375      * The following translates from the abcdef transforms that
1376      * we use internally to the rotation and mirroring specification
1377      * used in Calma stream files.  It only works because orientations
1378      * are orthogonal in magic, and no scaling is allowed in cell use
1379      * transforms.  Thus the elements a, b, d, and e always have one
1380      * of the following forms:
1381      *
1382      *		a  d
1383      *		b  e
1384      *
1385      * (counterclockwise rotations of 0, 90, 180, 270 degrees)
1386      *
1387      *	1  0	0  1	-1  0	 0 -1
1388      *	0  1   -1  0	 0 -1	 1  0
1389      *
1390      * (mirrored across the x-axis before counterclockwise rotation
1391      * by 0, 90, 180, 270 degrees):
1392      *
1393      *	1  0    0  1    -1  0    0 -1
1394      *	0 -1    1  0     0  1   -1  0
1395      *
1396      * Note that mirroring must be done if either a != e, or
1397      * a == 0 and b == d.
1398      *
1399      */
1400     t = &use->cu_transform;
1401     stransflags = 0;
1402     whichangle = (t->t_a == -1) ? r180 : (unsigned char *) NULL;
1403     if (t->t_a != t->t_e || (t->t_a == 0 && t->t_b == t->t_d))
1404     {
1405 	stransflags |= CALMA_STRANS_UPSIDEDOWN;
1406 	if (t->t_a == 0)
1407 	{
1408 	    if (t->t_b == 1) whichangle = r90;
1409 	    else whichangle = r270;
1410 	}
1411     }
1412     else if (t->t_a == 0)
1413     {
1414 	if (t->t_b == -1) whichangle = r90;
1415 	else whichangle = r270;
1416     }
1417 
1418     if (CalmaFlattenArrays)
1419     {
1420 	for (x = 0; x <= topx; x++)
1421 	{
1422 	    for (y = 0; y <= topy; y++)
1423 	    {
1424 		/* Structure reference */
1425 		calmaOutRH(4, CALMA_SREF, CALMA_NODATA, f);
1426 		calmaOutStructName(CALMA_SNAME, use->cu_def, f);
1427 
1428 		/* Transformation flags */
1429 		calmaOutRH(6, CALMA_STRANS, CALMA_BITARRAY, f);
1430 		calmaOutI2(stransflags, f);
1431 
1432 		/* Rotation if there is one */
1433 		if (whichangle)
1434 		{
1435 		    calmaOutRH(12, CALMA_ANGLE, CALMA_R8, f);
1436 		    calmaOut8(whichangle, f);
1437 		}
1438 
1439 		/* Translation */
1440 		xxlate = t->t_c + t->t_a*(use->cu_xsep)*x
1441 				+ t->t_b*(use->cu_ysep)*y;
1442 		yxlate = t->t_f + t->t_d*(use->cu_xsep)*x
1443 				+ t->t_e*(use->cu_ysep)*y;
1444 		xxlate *= calmaWriteScale;
1445 		yxlate *= calmaWriteScale;
1446 		calmaOutRH(12, CALMA_XY, CALMA_I4, f);
1447 		calmaOutI4(xxlate, f);
1448 		calmaOutI4(yxlate, f);
1449 
1450 		/* End of element */
1451 		calmaOutRH(4, CALMA_ENDEL, CALMA_NODATA, f);
1452 	    }
1453 	}
1454     }
1455     else
1456     {
1457 	/* Is it an array? */
1458 	isArray = (topx > 0 || topy > 0);
1459 	rectype = isArray ? CALMA_AREF : CALMA_SREF;
1460 
1461 	/* Structure reference */
1462 	calmaOutRH(4, rectype, CALMA_NODATA, f);
1463 	calmaOutStructName(CALMA_SNAME, use->cu_def, f);
1464 
1465 	/* Transformation flags */
1466 	calmaOutRH(6, CALMA_STRANS, CALMA_BITARRAY, f);
1467 	calmaOutI2(stransflags, f);
1468 
1469 	/* Rotation if there is one */
1470 	if (whichangle)
1471 	{
1472 	    calmaOutRH(12, CALMA_ANGLE, CALMA_R8, f);
1473 	    calmaOut8(whichangle, f);
1474 	}
1475 
1476 	/* If array, number of columns and rows in the array */
1477 	if (isArray)
1478 	{
1479 	    calmaOutRH(8, CALMA_COLROW, CALMA_I2, f);
1480 	    cols = topx + 1;
1481 	    rows = topy + 1;
1482 	    calmaOutI2(cols, f);
1483 	    calmaOutI2(rows, f);
1484 	}
1485 
1486 	/* Translation */
1487 	xxlate = t->t_c * calmaWriteScale;
1488 	yxlate = t->t_f * calmaWriteScale;
1489 	hdrsize = isArray ? 28 : 12;
1490 	calmaOutRH(hdrsize, CALMA_XY, CALMA_I4, f);
1491 	calmaOutI4(xxlate, f);
1492 	calmaOutI4(yxlate, f);
1493 
1494 	/* Array sizes if an array */
1495 	if (isArray)
1496 	{
1497 	    /* Column reference point */
1498 	    p.p_x = use->cu_xsep * cols;
1499 	    p.p_y = 0;
1500 	    GeoTransPoint(t, &p, &p2);
1501 	    p2.p_x *= calmaWriteScale;
1502 	    p2.p_y *= calmaWriteScale;
1503 	    calmaOutI4(p2.p_x, f);
1504 	    calmaOutI4(p2.p_y, f);
1505 
1506 	    /* Row reference point */
1507 	    p.p_x = 0;
1508 	    p.p_y = use->cu_ysep * rows;
1509 	    GeoTransPoint(t, &p, &p2);
1510 	    p2.p_x *= calmaWriteScale;
1511 	    p2.p_y *= calmaWriteScale;
1512 	    calmaOutI4(p2.p_x, f);
1513 	    calmaOutI4(p2.p_y, f);
1514 	}
1515 
1516 	/* By NP */
1517 	/* Property attributes/value pairs. */
1518 	/* Add a CellUse ID property, if the CellUse has a non-default name */
1519 
1520 	if (!calmaIsUseNameDefault(use->cu_def->cd_name, use->cu_id))
1521 	{
1522 	    calmaOutRH(6, CALMA_PROPATTR, CALMA_I2, f);
1523 	    calmaOutI2(CALMA_PROP_USENAME_STD, f);
1524 	    calmaOutStringRecord(CALMA_PROPVALUE, use->cu_id, f);
1525 	}
1526 
1527 	/* Add an array limits property, if the CellUse is an array and */
1528 	/* limits of the array (xlo, ylo) are not zero (the default).	*/
1529 
1530 	if ((use->cu_xlo != 0) || (use->cu_ylo != 0))
1531 	{
1532 	    char arraystr[128];
1533 	    sprintf(arraystr, "%d_%d_%d_%d", use->cu_xlo, use->cu_xhi,
1534 			use->cu_ylo, use->cu_yhi);
1535 	    calmaOutRH(6, CALMA_PROPATTR, CALMA_I2, f);
1536 	    calmaOutI2(CALMA_PROP_ARRAY_LIMITS, f);
1537 	    calmaOutStringRecord(CALMA_PROPVALUE, arraystr, f);
1538 	}
1539 
1540 	/* End of element */
1541 	calmaOutRH(4, CALMA_ENDEL, CALMA_NODATA, f);
1542     }
1543 
1544     return (0);
1545 }
1546 
1547 /*
1548  * ----------------------------------------------------------------------------
1549  *
1550  * calmaOutStructName --
1551  *
1552  * Output the name of a cell def.
1553  * If the name is legal GDS-II, use it; otherwise, generate one
1554  * that is legal and unique.
1555  *
1556  * Results:
1557  *	None.
1558  *
1559  * Side effects:
1560  *	Writes to the disk file.
1561  *
1562  * ----------------------------------------------------------------------------
1563  */
1564 
1565 void
calmaOutStructName(type,def,f)1566 calmaOutStructName(type, def, f)
1567     int type;
1568     CellDef *def;
1569     FILE *f;
1570 {
1571     char *defname;
1572     unsigned char c;
1573     char *cp;
1574     int calmanum;
1575     char *table;
1576 
1577     if (CIFCurStyle->cs_flags & CWF_PERMISSIVE_LABELS)
1578     {
1579 	table = calmaMapTablePermissive;
1580     } else {
1581 	table = calmaMapTableStrict;
1582     }
1583 
1584     /* Is the def name a legal Calma name? */
1585     for (cp = def->cd_name; c = (unsigned char) *cp; cp++)
1586     {
1587 	if ((c > 127) || (table[c] == 0))
1588 	    goto bad;
1589 	else if ((unsigned char)table[c] != c)
1590 	{
1591 	    TxError("Warning: character \'%c\' changed to \'%c\' in"
1592 		" name %s\n", (char)c, table[c], def->cd_name);
1593 	}
1594 	/* We really should ensure that the new name is unique. . . */
1595     }
1596     if ((!(CIFCurStyle->cs_flags & CWF_STRING_LIMIT)) ||
1597 	    (cp <= def->cd_name + CALMANAMELENGTH))
1598     {
1599 	/* Yes, it's legal: use it */
1600 	defname = StrDup(NULL, def->cd_name);
1601     }
1602     else
1603     {
1604 	/* Bad name: use XXXXXcalmaNum */
1605 bad:
1606 	calmanum = (int) def->cd_client;
1607 	if (calmanum < 0) calmanum = -calmanum;
1608 	defname = (char *)mallocMagic(32);
1609 	(void) sprintf(defname, "XXXXX%d", calmanum);
1610 	TxError("Warning: string in output unprintable; changed to \'%s\'\n",
1611 		 defname);
1612     }
1613 
1614     calmaOutStringRecord(type, defname, f);
1615     freeMagic(defname);
1616 }
1617 
1618 /* Added by NP 8/21/2004 */
1619 /*
1620  * ----------------------------------------------------------------------------
1621  *
1622  * calmaGetContactCell --
1623  *
1624  *   This routine creates [if it hasn't been created yet] a cell definition
1625  *   containing the given TileType.  Cellname is "$$" + layer1_name + "_" +
1626  *   layer2_name... + "$$". Cellname contains the short name of all the
1627  *   residues of the layer "type".
1628  *
1629  * Results:
1630  *   Returns new celldef it doesn't exist else created one.
1631  *
1632  * Side effects:
1633  *	 New celldef created specially for contact type if it does not exist.
1634  *
1635  * ----------------------------------------------------------------------------
1636  */
1637 
1638 CellDef *
calmaGetContactCell(type,lookOnly)1639 calmaGetContactCell(type, lookOnly)
1640     TileType type;		/* magic contact tile type */
1641     bool lookOnly;		/* if true, don't generate any new cells */
1642 {
1643     TileType j;
1644     char contactCellName[100];
1645     TileTypeBitMask *rMask = DBResidueMask(type);
1646     CellDef *def;
1647     bool first = TRUE;
1648 
1649     strcpy(contactCellName, "$$");
1650     for (j = TT_SPACE + 1; j < DBNumUserLayers; j++)
1651 	if (TTMaskHasType(rMask, j))
1652 	{
1653             /* Cellname starts with "$$" to make it diffrent from
1654              * other database cells, and to be compatible with a
1655 	     * number of other EDA tools.
1656              */
1657 	    if (!first)
1658 		strcat(contactCellName, "_");
1659 	    else
1660 		first = FALSE;
1661             strcat(contactCellName, DBTypeShortName(j));
1662         }
1663     strcat(contactCellName, "$$");
1664 
1665     def = DBCellLookDef(contactCellName);
1666     if ((def == (CellDef *) NULL) && (lookOnly == FALSE))
1667     {
1668 	def = DBCellNewDef(contactCellName);
1669        	def->cd_flags &= ~(CDMODIFIED|CDGETNEWSTAMP);
1670         def->cd_flags |= CDAVAILABLE;
1671     }
1672     return def;
1673 }
1674 
1675 /*
1676  * ----------------------------------------------------------------------------
1677  *
1678  * CalmaGenerateArray --
1679  *
1680  *	This routine
1681  *
1682  * Results:
1683  *	TRUE on success, FALSE if no contact cell could be found.
1684  *
1685  * Side effects:
1686  *	Writes an AREF record to the GDS stream output.
1687  *
1688  * ----------------------------------------------------------------------------
1689  */
1690 
1691 bool
CalmaGenerateArray(f,type,llx,lly,pitch,cols,rows)1692 CalmaGenerateArray(f, type, llx, lly, pitch, cols, rows)
1693     FILE *f;		/* GDS output file */
1694     TileType type;	/* Magic tile type of contact */
1695     int llx, lly;	/* Lower-left hand coordinate of the array
1696 			 * (centered on contact cut)
1697 			 */
1698     int pitch;		/* Pitch of the array elements */
1699     int cols, rows;	/* Number of array elements in X and Y */
1700 {
1701     CellDef *child;	/* Cell definition of the contact cell */
1702     int xxlate, yxlate;
1703 
1704     child = calmaGetContactCell(type, TRUE);
1705     if (child == NULL) return FALSE;
1706 
1707     /* Structure reference */
1708     calmaOutRH(4, CALMA_AREF, CALMA_NODATA, f);
1709     calmaOutStructName(CALMA_SNAME, child, f);
1710 
1711     /* Transformation flags */
1712     calmaOutRH(6, CALMA_STRANS, CALMA_BITARRAY, f);
1713     calmaOutI2(0, f);
1714 
1715     /* Number of columns and rows in the array */
1716     calmaOutRH(8, CALMA_COLROW, CALMA_I2, f);
1717     calmaOutI2(cols, f);
1718     calmaOutI2(rows, f);
1719 
1720     /* Translation */
1721     xxlate = llx * calmaPaintScale;
1722     yxlate = lly * calmaPaintScale;
1723     calmaOutRH(28, CALMA_XY, CALMA_I4, f);
1724     calmaOutI4(xxlate, f);
1725     calmaOutI4(yxlate, f);
1726 
1727     /* Column reference point */
1728     calmaOutI4(xxlate + pitch * cols * calmaPaintScale, f);
1729     calmaOutI4(yxlate, f);
1730 
1731     /* Row reference point */
1732     calmaOutI4(xxlate, f);
1733     calmaOutI4(yxlate + pitch * rows * calmaPaintScale, f);
1734 
1735     /* End of AREF element */
1736     calmaOutRH(4, CALMA_ENDEL, CALMA_NODATA, f);
1737 
1738     return TRUE;
1739 }
1740 
1741 /* Added by NP 8/22/2004 */
1742 /*
1743  * ----------------------------------------------------------------------------
1744  *
1745  * calmaWriteContacts --
1746  *
1747  *  This routine creates a new cellDef for each contact type and writes to
1748  *  the GDS output stream file. It is called before processing all cell
1749  *  definitions while writing GDS output.
1750  *
1751  * Results:
1752  *	None.
1753  *
1754  * Side effects:
1755  *	Writes contact cell definition to the open Calma output file.
1756  *
1757  * ----------------------------------------------------------------------------
1758  */
1759 
1760 void
calmaWriteContacts(f)1761 calmaWriteContacts(f)
1762     FILE *f;
1763 {
1764     TileType type;
1765     TileTypeBitMask tMask, *rMask;
1766     CellDef *def, *cellDef;
1767     Rect area, cliprect;
1768     int halfwidth, halfsize;
1769     CIFOp *op;
1770 
1771     /* Turn off generation of contact arrays for the duration of this	*/
1772     /* subroutine, so that the contact definitions themselves will get	*/
1773     /* the proper contact cut drawn.  It is turned on again at the end	*/
1774     /* of the routine.  Note that this routine is not called unless	*/
1775     /* CalmaContactArrays is TRUE.					*/
1776 
1777     CalmaContactArrays = FALSE;
1778 
1779     DBEnumerateTypes(&tMask);
1780 
1781     /* Decompose stacking types */
1782     for (type = DBNumUserLayers; type < DBNumTypes; type++)
1783 	if (TTMaskHasType(&tMask, type))
1784 	{
1785 	    rMask = DBResidueMask(type);
1786 	    TTMaskSetMask(&tMask, rMask);
1787 	}
1788 
1789     for (type = TT_SPACE + 1; type < DBNumUserLayers; type++)
1790     {
1791 	/* We need to create cell array only for contact types */
1792 	if (DBIsContact(type) && TTMaskHasType(&tMask, type))
1793 	{
1794             /* Write definition of cell to GDS stream.	*/
1795 	    /* Get cell definition for Tiletype type */
1796             def = calmaGetContactCell(type, FALSE);
1797 
1798             /* Get clip bounds, so that residue surround is	*/
1799 	    /* minimum.  Note that these values are in CIF/GDS	*/
1800 	    /* units, and the clipping rectangle passed to	*/
1801 	    /* calmaOutFunc is also in CIF/GDS units.		*/
1802 
1803 	    halfsize = CIFGetContactSize(type, NULL, NULL, NULL) >> 1;
1804 
1805             /* Get minimum width for layer by rounding halfsize	*/
1806 	    /* to the nearest lambda value.			*/
1807             halfwidth = halfsize / CIFCurStyle->cs_scaleFactor;
1808 	    if ((halfsize % CIFCurStyle->cs_scaleFactor) != 0)
1809 		halfwidth++;
1810 
1811             area.r_xbot = area.r_ybot = -halfwidth;
1812             area.r_xtop = area.r_ytop = halfwidth;
1813        	    UndoDisable();
1814        	    DBPaint(def, &area, type);
1815        	    DBReComputeBbox(def);
1816     	    TTMaskSetType(&def->cd_types, type);
1817 
1818 	    /* Clip output to the bounds of "cliprect"	*/
1819 	    cliprect.r_xbot = cliprect.r_ybot = -halfsize;
1820 	    cliprect.r_xtop = cliprect.r_ytop = halfsize;
1821 
1822             calmaOutFunc(def, f, &cliprect);
1823             UndoEnable();
1824 	}
1825     }
1826     CalmaContactArrays = TRUE;
1827 }
1828 
1829 /*
1830  * ----------------------------------------------------------------------------
1831  *
1832  * calmaDelContacts --
1833  *
1834  *  This routine removes all cell definitions generated by
1835  *  calmaWriteContacts().
1836  *
1837  * Results:
1838  *	None.
1839  *
1840  * Side effects:
1841  *	Removes contact cell defs from the database.
1842  *
1843  * ----------------------------------------------------------------------------
1844  */
1845 
1846 void
calmaDelContacts()1847 calmaDelContacts()
1848 {
1849     TileType type;
1850     CellDef *def;
1851 
1852     for (type = TT_SPACE + 1; type < DBNumUserLayers; type++)
1853 	if (DBIsContact(type))
1854 	{
1855             def = calmaGetContactCell(type, TRUE);
1856 	    if (def != (CellDef *)NULL)
1857 		 DBCellDeleteDef(def);
1858 	}
1859 }
1860 
1861 /*
1862  * ----------------------------------------------------------------------------
1863  * calmaAddSegment ---
1864  *
1865  * Process a new polygon edge, inserting it into a polygon record as
1866  * required.  If the edge is between a GDS layer and TT_SPACE, then
1867  * we insert a point record.  If the edge is between two tiles of the
1868  * same layer, then we insert a tile record.
1869  *
1870  * Results:
1871  *	Return 1 if an internal segment was generated, 0 if an external
1872  *	segment was generate.  On error, return -1 (failure to find a
1873  *	connecting point; this shouldn't happen).
1874  *
1875  *	Returns the current segment in the original pointer position (1st
1876  *	argument).  If segments are added in counterclockwise order, then
1877  *	this should be most efficient.
1878  *
1879  * Side effects:
1880  *	May allocate memory.
1881  * ---------------------------------------------------------------------------
1882  */
1883 
1884 int
calmaAddSegment(lbptr,poly_edge,p1x,p1y,p2x,p2y)1885 calmaAddSegment(lbptr, poly_edge, p1x, p1y, p2x, p2y)
1886     LinkedBoundary **lbptr;
1887     bool poly_edge;
1888     int p1x, p1y, p2x, p2y;
1889 {
1890     LinkedBoundary *newseg, *curseg, *stopseg;
1891     bool startmatch = FALSE;
1892     bool endmatch = FALSE;
1893 
1894     stopseg = NULL;
1895     for (curseg = *lbptr; curseg != stopseg; curseg = curseg->lb_next)
1896     {
1897 	stopseg = *lbptr;
1898 	if (curseg->lb_type == LB_INIT)
1899 	{
1900 	    if ((p1x == curseg->lb_start.p_x) && (p1y == curseg->lb_start.p_y))
1901 		startmatch = TRUE;
1902 
1903 	    if ((p2x == curseg->lb_next->lb_start.p_x) &&
1904 			(p2y == curseg->lb_next->lb_start.p_y))
1905 		endmatch = TRUE;
1906 
1907 	    if (startmatch && endmatch)
1908 	    {
1909 		/* Segment completes this edge */
1910 		curseg->lb_type = (poly_edge) ? LB_EXTERNAL : LB_INTERNAL;
1911 		*lbptr = curseg;
1912 		return (int)curseg->lb_type;
1913 	    }
1914 	    else if (startmatch || endmatch)
1915 	    {
1916 		/* Insert a new segment after curseg */
1917 		newseg = (LinkedBoundary *)mallocMagic(sizeof(LinkedBoundary));
1918 		newseg->lb_next = curseg->lb_next;
1919 		curseg->lb_next = newseg;
1920 
1921 		if (startmatch)
1922 		{
1923 		    newseg->lb_type = curseg->lb_type;
1924 		    curseg->lb_type = (poly_edge) ? LB_EXTERNAL : LB_INTERNAL;
1925 		    newseg->lb_start.p_x = p2x;
1926 		    newseg->lb_start.p_y = p2y;
1927 		}
1928 		else
1929 		{
1930 		    newseg->lb_type = (poly_edge) ? LB_EXTERNAL : LB_INTERNAL;
1931 		    newseg->lb_start.p_x = p1x;
1932 		    newseg->lb_start.p_y = p1y;
1933 		}
1934 		curseg = newseg;
1935 		*lbptr = curseg;
1936 		return (int)curseg->lb_type;
1937 	    }
1938 	}
1939     }
1940     return -1;		/* This shouldn't happen, but isn't fatal. */
1941 }
1942 
1943 /*
1944  * ----------------------------------------------------------------------------
1945  * calmaRemoveDegenerate ---
1946  *
1947  *    This routine takes lists of polygons and removes any degenerate
1948  *    segments (those that backtrack on themselves) from each one.
1949  *
1950  * Results:
1951  *	None.
1952  *
1953  * Side Effects:
1954  *	Deallocates memory for any segments that are removed.
1955  *
1956  * ----------------------------------------------------------------------------
1957  */
1958 
1959 void
calmaRemoveDegenerate(blist)1960 calmaRemoveDegenerate(blist)
1961     BoundaryTop *blist;
1962 {
1963     bool segfound;
1964     LinkedBoundary *stopseg, *curseg, *lastseg;
1965     BoundaryTop *bounds;
1966 
1967     for (bounds = blist; bounds != NULL; bounds = bounds->bt_next)
1968     {
1969 	segfound = TRUE;
1970 	while (segfound)
1971 	{
1972 	    segfound = FALSE;
1973 	    stopseg = NULL;
1974 	    for (lastseg = bounds->bt_first; lastseg != stopseg;)
1975 	    {
1976 		stopseg = bounds->bt_first;
1977 		curseg = lastseg->lb_next;
1978 
1979 		if (GEO_SAMEPOINT(curseg->lb_start,
1980 			curseg->lb_next->lb_next->lb_start))
1981 		{
1982 		    segfound = TRUE;
1983 		    lastseg->lb_next = curseg->lb_next->lb_next;
1984 
1985 		    freeMagic(curseg->lb_next);
1986 		    freeMagic(curseg);
1987 
1988 		    /* Make sure record doesn't point to a free'd segment */
1989 		    bounds->bt_first = lastseg;
1990 		    bounds->bt_points -= 2;
1991 		    break;
1992 		}
1993 		else
1994 		    lastseg = lastseg->lb_next;
1995 	    }
1996 	}
1997     }
1998 }
1999 
2000 /*
2001  * ----------------------------------------------------------------------------
2002  * calmaRemoveColinear ---
2003  *
2004  *    This routine takes lists of polygons and removes any redundant
2005  *    (colinear) points.
2006  *
2007  * Results:
2008  *	None.
2009  *
2010  * Side Effects:
2011  *	Deallocates memory for any segments that are removed.
2012  *
2013  * ----------------------------------------------------------------------------
2014  */
2015 
2016 void
calmaRemoveColinear(blist)2017 calmaRemoveColinear(blist)
2018     BoundaryTop *blist;
2019 {
2020     LinkedBoundary *stopseg, *curseg, *lastseg;
2021     BoundaryTop *bounds;
2022 
2023     for (bounds = blist; bounds != NULL; bounds = bounds->bt_next)
2024     {
2025 	stopseg = NULL;
2026 	for (lastseg = bounds->bt_first; lastseg != stopseg;)
2027 	{
2028 	    stopseg = bounds->bt_first;
2029 	    curseg = lastseg->lb_next;
2030 
2031 	    if (((lastseg->lb_start.p_x == curseg->lb_start.p_x) &&
2032 		(lastseg->lb_start.p_x == curseg->lb_next->lb_start.p_x)) ||
2033 		((lastseg->lb_start.p_y == curseg->lb_start.p_y) &&
2034 		(lastseg->lb_start.p_y == curseg->lb_next->lb_start.p_y)))
2035 	    {
2036 		lastseg->lb_next = curseg->lb_next;
2037 
2038 		/* Make sure record doesn't point to a free'd segment */
2039 		if (curseg == bounds->bt_first) bounds->bt_first = lastseg;
2040 
2041 		freeMagic(curseg);
2042 		bounds->bt_points--;
2043 	    }
2044 	    else if ((lastseg->lb_start.p_x != curseg->lb_start.p_x) &&
2045 		(lastseg->lb_start.p_y != curseg->lb_start.p_y) &&
2046 		(curseg->lb_start.p_x != curseg->lb_next->lb_start.p_x) &&
2047 		(curseg->lb_start.p_y != curseg->lb_next->lb_start.p_y))
2048 	    {
2049 		/* Check colinearity of non-Manhattan edges */
2050 		int delx1, dely1, delx2, dely2, gcf;
2051 
2052 		delx1 = curseg->lb_start.p_x - lastseg->lb_start.p_x;
2053 		dely1 = curseg->lb_start.p_y - lastseg->lb_start.p_y;
2054 		delx2 = curseg->lb_next->lb_start.p_x - curseg->lb_start.p_x;
2055 		dely2 = curseg->lb_next->lb_start.p_y - curseg->lb_start.p_y;
2056 
2057 		if ((delx1 != delx2) || (dely1 != dely2))
2058 		{
2059 		    gcf = FindGCF(delx1, dely1);
2060 		    if (gcf > 1)
2061 		    {
2062 			delx1 /= gcf;
2063 			dely1 /= gcf;
2064 		    }
2065 		}
2066 		if ((delx1 != delx2) || (dely1 != dely2))
2067 		{
2068 		    gcf = FindGCF(delx2, dely2);
2069 		    if (gcf > 1)
2070 		    {
2071 			delx2 /= gcf;
2072 			dely2 /= gcf;
2073 		    }
2074 		}
2075 		if ((delx1 == delx2) && (dely1 == dely2))
2076 	 	{
2077 		    lastseg->lb_next = curseg->lb_next;
2078 		    if (curseg == bounds->bt_first) bounds->bt_first = lastseg;
2079 		    freeMagic(curseg);
2080 		    bounds->bt_points--;
2081 		}
2082 		else
2083 		    lastseg = lastseg->lb_next;
2084 	    }
2085 	    else
2086 		lastseg = lastseg->lb_next;
2087 	}
2088     }
2089 }
2090 
2091 /*
2092  * ----------------------------------------------------------------------------
2093  * calmaMergeSegments ---
2094  *
2095  *    Once a tile has been disassembled into segments, and it is not a simple
2096  *    rectangle (which would have been handled already), then merge it into
2097  *    the list of boundaries.
2098  *
2099  *    Note that this algorithm is O(N^2) and has lots of room for improvement!
2100  *    Still, each segment is never checked against more than 200 points,
2101  *    because when a boundary reaches this number (the maximum for GDS
2102  *    boundary records), the record will tend to be skipped (it should
2103  *    probably be output here. . .)
2104  *
2105  * Results:
2106  *	None.
2107  *
2108  * Side effects:
2109  *	Output, memory allocation and deallocation
2110  *
2111  * ----------------------------------------------------------------------------
2112  */
2113 
2114 void
calmaMergeSegments(edge,blist,num_points)2115 calmaMergeSegments(edge, blist, num_points)
2116     LinkedBoundary *edge;
2117     BoundaryTop **blist;
2118     int num_points;
2119 {
2120     LinkedBoundary *stopseg, *curseg, *lastseg;
2121     LinkedBoundary *compstop, *compseg, *complast;
2122     BoundaryTop *bounds, *newbounds;
2123 
2124     if (*blist == NULL) goto make_new_bound;
2125 
2126     /* Check each internal edge for an antiparallel match with	*/
2127     /* an internal edge in the boundary lists.			*/
2128 
2129     stopseg = NULL;
2130     for (lastseg = edge; lastseg != stopseg; lastseg = lastseg->lb_next)
2131     {
2132 	stopseg = edge;
2133 	curseg = lastseg->lb_next;
2134 	if (curseg->lb_type == LB_EXTERNAL) continue;
2135 
2136 	for (bounds = *blist; bounds != NULL; bounds = bounds->bt_next)
2137 	{
2138 	    /* Avoid overflow on GDS boundary point limit.  Note	*/
2139 	    /* that a merge will remove 2 points, but GDS requires	*/
2140 	    /* that we add the 1st point to the end of the list.	*/
2141 
2142 	    if (bounds->bt_points + num_points > 201) continue;
2143 
2144 	    compstop = NULL;
2145 	    for (complast = bounds->bt_first; complast != compstop;
2146 			complast = complast->lb_next)
2147 	    {
2148 	        compstop = bounds->bt_first;
2149 		compseg = complast->lb_next;
2150 		if (compseg->lb_type == LB_EXTERNAL) continue;
2151 
2152 		/* Edges match antiparallel only. Rect points are *not*	*/
2153 		/* canonical. r_ll and p1 are both 1st points traveling	*/
2154 		/* in a counterclockwise direction along the perimeter.	*/
2155 
2156 		if (GEO_SAMEPOINT(compseg->lb_start, curseg->lb_next->lb_start) &&
2157 		   GEO_SAMEPOINT(compseg->lb_next->lb_start, curseg->lb_start))
2158 		{
2159 		    lastseg->lb_next = compseg->lb_next;
2160 		    complast->lb_next = curseg->lb_next;
2161 
2162 		    freeMagic(compseg);
2163 		    freeMagic(curseg);
2164 
2165 		    /* Make sure the record doesn't point to the free'd segment */
2166 		    if (compseg == bounds->bt_first) bounds->bt_first = complast;
2167 		    bounds->bt_points += num_points - 2;
2168 		    return;
2169 		}
2170 	    }
2171 	}
2172     }
2173 
2174     /* If still no connecting edge was found, or if we overflowed the GDS max	*/
2175     /* number of records for a boundary, then start a new entry.		*/
2176 
2177 make_new_bound:
2178 
2179     newbounds = (BoundaryTop *)mallocMagic(sizeof(BoundaryTop));
2180     newbounds->bt_first = edge;
2181     newbounds->bt_next = *blist;
2182     newbounds->bt_points = num_points;
2183     *blist = newbounds;
2184 }
2185 
2186 /*
2187  * ----------------------------------------------------------------------------
2188  * Process a LinkedBoundary list into a polygon and generate GDS output.
2189  * Free the linked list when done.
2190  *
2191  * Results:
2192  *	None.
2193  *
2194  * Side effects:
2195  *	Output, memory deallocation.
2196  *
2197  * ----------------------------------------------------------------------------
2198  */
2199 
2200 void
calmaProcessBoundary(blist,cos)2201 calmaProcessBoundary(blist, cos)
2202     BoundaryTop *blist;
2203     calmaOutputStruct *cos;
2204 {
2205     FILE *f = cos->f;
2206     LinkedBoundary *listtop, *lbref, *lbstop, *lbfree;
2207     BoundaryTop *bounds;
2208     int sval;
2209     int chkcount;	/* diagnostic */
2210 
2211     for (bounds = blist; bounds != NULL; bounds = bounds->bt_next)
2212     {
2213 	/* Boundary */
2214 	calmaOutRH(4, CALMA_BOUNDARY, CALMA_NODATA, f);
2215 
2216 	/* Layer */
2217 	calmaOutRH(6, CALMA_LAYER, CALMA_I2, f);
2218 	calmaOutI2(calmaPaintLayerNumber, f);
2219 
2220 	/* Data type */
2221 	calmaOutRH(6, CALMA_DATATYPE, CALMA_I2, f);
2222 	calmaOutI2(calmaPaintLayerType, f);
2223 
2224 	/* Record length = ((#points + 1) * 2 values * 4 bytes) + 4 bytes header */
2225 	calmaOutRH(4 + (bounds->bt_points + 1) * 8, CALMA_XY, CALMA_I4, f);
2226 
2227 	/* Coordinates (repeat 1st point) */
2228 
2229 	listtop = bounds->bt_first;
2230 	lbstop = NULL;
2231 	chkcount = 0;
2232 	for (lbref = listtop; lbref != lbstop; lbref = lbref->lb_next)
2233 	{
2234 	    lbstop = listtop;
2235 	    calmaOutI4(lbref->lb_start.p_x * calmaPaintScale, f);
2236 	    calmaOutI4(lbref->lb_start.p_y * calmaPaintScale, f);
2237 	    chkcount++;
2238 	}
2239 	calmaOutI4(listtop->lb_start.p_x * calmaPaintScale, f);
2240 	calmaOutI4(listtop->lb_start.p_y * calmaPaintScale, f);
2241 
2242 	if (chkcount != bounds->bt_points)
2243 	    TxError("Points recorded=%d;  Points written=%d\n",
2244 			bounds->bt_points, chkcount);
2245 
2246 	/* End of element */
2247 	calmaOutRH(4, CALMA_ENDEL, CALMA_NODATA, f);
2248 
2249 #ifdef DEBUG
2250 	/* Diagnostic: report the contents of the list */
2251 	TxPrintf("Polygon path (%d points):\n", bounds->bt_points);
2252 
2253 	listtop = bounds->bt_first;
2254 	lbstop = NULL;
2255 
2256 	for (lbref = listtop; lbref != lbstop; lbref = lbref->lb_next)
2257 	{
2258 	    if (lbref != listtop)
2259 		TxPrintf("->");
2260 	    else
2261 		lbstop = listtop;
2262 
2263 	    switch(lbref->lb_type)
2264 	    {
2265 		case LB_EXTERNAL:
2266 		    TxPrintf("(%d %d)", lbref->lb_start.p_x, lbref->lb_start.p_y);
2267 		    break;
2268 		case LB_INTERNAL:
2269 		    TxPrintf("[[%d %d]]", lbref->lb_start.p_x, lbref->lb_start.p_y);
2270 		    break;
2271 		case LB_INIT:
2272 		    TxPrintf("XXXXX");
2273 		    break;
2274 	    }
2275 	}
2276 	TxPrintf("\n\n");
2277 #endif
2278 
2279 	/* Free the LinkedBoundary list */
2280 
2281 	lbref = listtop;
2282 	while (lbref->lb_next != listtop)
2283 	{
2284 	    freeMagic(lbref);
2285 	    lbref = lbref->lb_next;
2286 	}
2287 	freeMagic(lbref);
2288     }
2289 
2290     /* Free the BoundaryTop list */
2291 
2292     for (bounds = blist; bounds != NULL; bounds = bounds->bt_next)
2293 	freeMagic(bounds);
2294 }
2295 
2296 /*
2297  * ----------------------------------------------------------------------------
2298  *
2299  * calmaMergePaintFunc --
2300  *
2301  * Results:
2302  *	None.
2303  *
2304  * Side effects:
2305  *	Writes to the disk file.
2306  *
2307  * ----------------------------------------------------------------------------
2308  */
2309 
2310 int
calmaMergePaintFunc(tile,cos)2311 calmaMergePaintFunc(tile, cos)
2312     Tile *tile;			/* Tile to be written out. */
2313     calmaOutputStruct *cos;	/* Information needed by algorithm */
2314 {
2315     FILE *f = cos->f;
2316     Rect *clipArea = cos->area;
2317     Tile *t, *tp;
2318     TileType ttype;
2319     int i, llx, lly, urx, ury, intedges, num_points, split_type;
2320     bool is_ext;
2321     static Stack *SegStack = (Stack *)NULL;
2322 
2323     static LinkedBoundary *edge;
2324     LinkedBoundary *lb;
2325     BoundaryTop *bounds = NULL;
2326 
2327     /* Quick check for tiles that have already been processed */
2328     if (tile->ti_client == (ClientData)GDS_PROCESSED) return 0;
2329 
2330     if (SegStack == (Stack *)NULL)
2331 	SegStack = StackNew(64);
2332 
2333     PUSHTILE(tile);
2334     while (!StackEmpty(SegStack))
2335     {
2336 	t = (Tile *) STACKPOP(SegStack);
2337 	if (t->ti_client != (ClientData)GDS_PENDING) continue;
2338 	t->ti_client = (ClientData)GDS_PROCESSED;
2339 
2340 	split_type = -1;
2341 	if (IsSplit(t))
2342 	{
2343 	    /* If we use SplitSide, then we need to set it when	the	*/
2344 	    /* tile is pushed.  Since these are one-or-zero mask layers	*/
2345 	    /* I assume it is okay to just check which side is TT_SPACE	*/
2346 
2347 	    /* split_type = (SplitSide(t) << 1) | SplitDirection(t); */
2348 	    split_type = SplitDirection(t);
2349 	    if (TiGetLeftType(t) == TT_SPACE) split_type |= 2;
2350 	    num_points = 2;
2351 	    if (edge != NULL)
2352 	    {
2353 		/* Remove one point from the edge record for rectangles */
2354 		/* and relink the last entry back to the new head.	*/
2355 
2356 		lb = edge;
2357 		while (lb->lb_next != edge) lb = lb->lb_next;
2358 		lb->lb_next = edge->lb_next;
2359 	 	freeMagic(edge);
2360 		edge = edge->lb_next;
2361 	    }
2362 	}
2363 	else
2364 	    num_points = 3;
2365 
2366 	/* Create a new linked boundary structure with 4 unknown edges.	*/
2367 	/* This structure is reused when we encounter isolated tiles,	*/
2368 	/* so we avoid unnecessary overhead in the case of, for		*/
2369 	/* example, large contact cut arrays.				*/
2370 
2371 	if (edge == NULL)
2372 	{
2373 	    edge = (LinkedBoundary *)mallocMagic(sizeof(LinkedBoundary));
2374 	    lb = edge;
2375 
2376 	    for (i = 0; i < num_points; i++)
2377 	    {
2378 		lb->lb_type = LB_INIT;
2379 		lb->lb_next = (LinkedBoundary *)mallocMagic(sizeof(LinkedBoundary));
2380 		lb = lb->lb_next;
2381 	    }
2382 	    lb->lb_type = LB_INIT;
2383 	    lb->lb_next = edge;
2384 	}
2385 
2386 	lb = edge;
2387 	llx = LEFT(t);
2388 	lly = BOTTOM(t);
2389 	urx = RIGHT(t);
2390 	ury = TOP(t);
2391 	intedges = 0;
2392 
2393 	/* Initialize the "edge" record with the corner points of the	*/
2394 	/* tile.							*/
2395 
2396 	if (IsSplit(t))
2397 	{
2398 	    switch (split_type)
2399 	    {
2400 		case 0x0:
2401 		    lb->lb_start.p_x = urx;
2402 		    lb->lb_start.p_y = ury;
2403 		    lb->lb_type = LB_INIT;
2404 		    lb = lb->lb_next;
2405 		    lb->lb_start.p_x = llx;
2406 		    lb->lb_start.p_y = ury;
2407 		    lb->lb_type = LB_INIT;
2408 		    lb = lb->lb_next;
2409 		    lb->lb_start.p_x = llx;
2410 		    lb->lb_start.p_y = lly;
2411 		    lb->lb_type = LB_INIT;
2412 		    lb = lb->lb_next;
2413 		    break;
2414 		case 0x1:
2415 		    lb->lb_start.p_x = llx;
2416 		    lb->lb_start.p_y = ury;
2417 		    lb->lb_type = LB_INIT;
2418 		    lb = lb->lb_next;
2419 		    lb->lb_start.p_x = llx;
2420 		    lb->lb_start.p_y = lly;
2421 		    lb->lb_type = LB_INIT;
2422 		    lb = lb->lb_next;
2423 		    lb->lb_start.p_x = urx;
2424 		    lb->lb_start.p_y = lly;
2425 		    lb->lb_type = LB_INIT;
2426 		    lb = lb->lb_next;
2427 		    break;
2428 		case 0x2:
2429 		    lb->lb_start.p_x = urx;
2430 		    lb->lb_start.p_y = ury;
2431 		    lb->lb_type = LB_INIT;
2432 		    lb = lb->lb_next;
2433 		    lb->lb_start.p_x = llx;
2434 		    lb->lb_start.p_y = lly;
2435 		    lb->lb_type = LB_INIT;
2436 		    lb = lb->lb_next;
2437 		    lb->lb_start.p_x = urx;
2438 		    lb->lb_start.p_y = lly;
2439 		    lb->lb_type = LB_INIT;
2440 		    lb = lb->lb_next;
2441 		    break;
2442 		case 0x3:
2443 		    lb->lb_start.p_x = urx;
2444 		    lb->lb_start.p_y = ury;
2445 		    lb->lb_type = LB_INIT;
2446 		    lb = lb->lb_next;
2447 		    lb->lb_start.p_x = llx;
2448 		    lb->lb_start.p_y = ury;
2449 		    lb->lb_type = LB_INIT;
2450 		    lb = lb->lb_next;
2451 		    lb->lb_start.p_x = urx;
2452 		    lb->lb_start.p_y = lly;
2453 		    lb->lb_type = LB_INIT;
2454 		    lb = lb->lb_next;
2455 		    break;
2456 	    }
2457 	    num_points = 1;
2458 	}
2459 	else
2460 	{
2461 	    lb->lb_start.p_x = urx;
2462 	    lb->lb_start.p_y = ury;
2463 	    lb->lb_type = LB_INIT;
2464 	    lb = lb->lb_next;
2465 	    lb->lb_start.p_x = llx;
2466 	    lb->lb_start.p_y = ury;
2467 	    lb->lb_type = LB_INIT;
2468 	    lb = lb->lb_next;
2469 	    lb->lb_start.p_x = llx;
2470 	    lb->lb_start.p_y = lly;
2471 	    lb->lb_type = LB_INIT;
2472 	    lb = lb->lb_next;
2473 	    lb->lb_start.p_x = urx;
2474 	    lb->lb_start.p_y = lly;
2475 	    lb->lb_type = LB_INIT;
2476 	    lb = lb->lb_next;
2477 
2478 	    num_points = 0;
2479 	}
2480 	if (split_type == 0x1) goto left_search;
2481 
2482 	/* Search the tile boundary for connected and unconnected tiles.	*/
2483 	/* Generate segments in a counterclockwise cycle.			*/
2484 
2485 	if (split_type == 0x2)
2486 	{
2487 	    intedges += calmaAddSegment(&lb, TRUE, RIGHT(t), TOP(t),
2488 			LEFT(t), BOTTOM(t));
2489 	    goto bottom_search;
2490 	}
2491 
2492 	/* Search top */
2493 
2494 	ttype = TiGetTopType(t);
2495 	for (tp = RT(t); RIGHT(tp) > LEFT(t); tp = BL(tp), num_points++)
2496 	{
2497 	    is_ext = (TiGetBottomType(tp) != ttype) ? TRUE : FALSE;
2498 	    intedges += calmaAddSegment(&lb, is_ext,
2499 			MIN(RIGHT(t), RIGHT(tp)), TOP(t),
2500 			MAX(LEFT(t), LEFT(tp)), TOP(t));
2501 	    if (!is_ext) PUSHTILE(tp);
2502 	}
2503 
2504 	if (split_type == 0x3)
2505 	{
2506 	    intedges += calmaAddSegment(&lb, TRUE, LEFT(t), TOP(t),
2507 			RIGHT(t), BOTTOM(t));
2508 	    goto right_search;
2509 	}
2510 
2511 	/* Search left */
2512 
2513 left_search:
2514 	ttype = TiGetLeftType(t);
2515 	for (tp = BL(t); BOTTOM(tp) < TOP(t); tp = RT(tp), num_points++)
2516 	{
2517 	    is_ext = (TiGetRightType(tp) != ttype) ? TRUE : FALSE;
2518 	    intedges += calmaAddSegment(&lb, is_ext,
2519 			LEFT(t), MIN(TOP(t), TOP(tp)),
2520 			LEFT(t), MAX(BOTTOM(t), BOTTOM(tp)));
2521 	    if (!is_ext) PUSHTILE(tp);
2522 	}
2523 
2524 	if (split_type == 0x0)
2525 	{
2526 	    intedges += calmaAddSegment(&lb, TRUE, LEFT(t), BOTTOM(t),
2527 			RIGHT(t), TOP(t));
2528 	    goto done_searches;
2529 	}
2530 
2531 	/* Search bottom */
2532 
2533 bottom_search:
2534 	ttype = TiGetBottomType(t);
2535 	for (tp = LB(t); LEFT(tp) < RIGHT(t); tp = TR(tp), num_points++)
2536 	{
2537 	    is_ext = (TiGetTopType(tp) != ttype) ? TRUE : FALSE;
2538 	    intedges += calmaAddSegment(&lb, is_ext,
2539 			MAX(LEFT(t), LEFT(tp)), BOTTOM(t),
2540 			MIN(RIGHT(t), RIGHT(tp)), BOTTOM(t));
2541 	    if (!is_ext) PUSHTILE(tp);
2542 	}
2543 
2544 	if (split_type == 0x1)
2545 	{
2546 	    intedges += calmaAddSegment(&lb, TRUE, RIGHT(t), BOTTOM(t),
2547 			LEFT(t), TOP(t));
2548 	    goto done_searches;
2549 	}
2550 	/* Search right */
2551 
2552 right_search:
2553 	ttype = TiGetRightType(t);
2554 	for (tp = TR(t); TOP(tp) > BOTTOM(t); tp = LB(tp), num_points++)
2555 	{
2556 	    is_ext = (TiGetLeftType(tp) != ttype) ? TRUE : FALSE;
2557 	    intedges += calmaAddSegment(&lb, is_ext,
2558 			RIGHT(t), MAX(BOTTOM(t), BOTTOM(tp)),
2559 			RIGHT(t), MIN(TOP(t), TOP(tp)));
2560 	    if (!is_ext) PUSHTILE(tp);
2561 	}
2562 
2563 	/* If tile is isolated, process it now and we're done */
2564 
2565 done_searches:
2566 	if (intedges == 0)
2567 	{
2568 	    calmaWritePaintFunc(t, cos);
2569 
2570 	    /* Although calmaWritePaintFunc is called only on isolated	*/
2571 	    /* tiles, we may have expanded it.  This could use a LOT of	*/
2572 	    /* optimizing.  1) remove colinear points in calmaAddSegment */
2573 	    /* when both subsegments are external paths, and 2) here,	*/
2574 	    /* take the shortest path to making "edge" exactly 4 points.*/
2575 	    /* Note that in non-Manhattan mode, num_points may be 3.	*/
2576 
2577 	    if (num_points != 4)
2578 	    {
2579 		for (i = 0; i < num_points; i++)
2580 		{
2581 		    freeMagic(edge);
2582 		    edge = edge->lb_next;
2583 		}
2584 		edge = NULL;
2585 	    }
2586 	    if (!StackEmpty(SegStack))
2587 		TxError("ERROR:  Segment stack is supposed to be empty!\n");
2588 	    else
2589 		return 0;
2590 	}
2591 	else
2592 	{
2593 	    /* Merge boundary into existing record */
2594 
2595 	    calmaMergeSegments(edge, &bounds, num_points);
2596 	    edge = NULL;
2597 	}
2598     }
2599 
2600     /* Remove any degenerate points */
2601     calmaRemoveDegenerate(bounds);
2602 
2603     /* Remove any colinear points */
2604     calmaRemoveColinear(bounds);
2605 
2606     /* Output the boundary records */
2607     calmaProcessBoundary(bounds, cos);
2608 
2609     return 0;	/* Keep the search alive. . . */
2610 }
2611 
2612 /*
2613  * ----------------------------------------------------------------------------
2614  *
2615  * calmaWritePaintFunc --
2616  *
2617  * Filter function used to write out a single paint tile.
2618  *
2619  *			**** NOTE ****
2620  * There are loads of Calma systems out in the world that
2621  * don't understand CALMA_BOX, so we output CALMA_BOUNDARY
2622  * even though CALMA_BOX is more appropriate.  Bletch.
2623  *
2624  * Results:
2625  *	None.
2626  *
2627  * Side effects:
2628  *	Writes to the disk file.
2629  *
2630  * ----------------------------------------------------------------------------
2631  */
2632 
2633 int
calmaWritePaintFunc(tile,cos)2634 calmaWritePaintFunc(tile, cos)
2635     Tile *tile;			/* Tile to be written out. */
2636     calmaOutputStruct *cos;	/* File for output and clipping area */
2637 {
2638     FILE *f = cos->f;
2639     Rect *clipArea = cos->area;
2640     Rect r, r2;
2641 
2642     TiToRect(tile, &r);
2643     if (clipArea != NULL)
2644 	GeoClip(&r, clipArea);
2645 
2646     r.r_xbot *= calmaPaintScale;
2647     r.r_ybot *= calmaPaintScale;
2648     r.r_xtop *= calmaPaintScale;
2649     r.r_ytop *= calmaPaintScale;
2650 
2651     /* Boundary */
2652     calmaOutRH(4, CALMA_BOUNDARY, CALMA_NODATA, f);
2653 
2654     /* Layer */
2655     calmaOutRH(6, CALMA_LAYER, CALMA_I2, f);
2656     calmaOutI2(calmaPaintLayerNumber, f);
2657 
2658     /* Data type */
2659     calmaOutRH(6, CALMA_DATATYPE, CALMA_I2, f);
2660     calmaOutI2(calmaPaintLayerType, f);
2661 
2662     /* The inefficient use of CALMA_BOUNDARY for rectangles actually	*/
2663     /* makes it easy to implement triangles, since they must be defined */
2664     /* by CALMA_BOUNDARY.						*/
2665 
2666     if (IsSplit(tile))
2667     {
2668 	/* Coordinates */
2669 	calmaOutRH(36, CALMA_XY, CALMA_I4, f);
2670 
2671 	switch ((SplitSide(tile) << 1) | SplitDirection(tile))
2672 	{
2673 	    case 0x0:
2674 		calmaOutI4(r.r_xbot, f); calmaOutI4(r.r_ybot, f);
2675 		calmaOutI4(r.r_xbot, f); calmaOutI4(r.r_ytop, f);
2676 		calmaOutI4(r.r_xtop, f); calmaOutI4(r.r_ytop, f);
2677 		calmaOutI4(r.r_xbot, f); calmaOutI4(r.r_ybot, f);
2678 		break;
2679 	    case 0x1:
2680 		calmaOutI4(r.r_xbot, f); calmaOutI4(r.r_ytop, f);
2681 		calmaOutI4(r.r_xbot, f); calmaOutI4(r.r_ybot, f);
2682 		calmaOutI4(r.r_xtop, f); calmaOutI4(r.r_ybot, f);
2683 		calmaOutI4(r.r_xbot, f); calmaOutI4(r.r_ytop, f);
2684 		break;
2685 	    case 0x2:
2686 		calmaOutI4(r.r_xbot, f); calmaOutI4(r.r_ybot, f);
2687 		calmaOutI4(r.r_xtop, f); calmaOutI4(r.r_ybot, f);
2688 		calmaOutI4(r.r_xtop, f); calmaOutI4(r.r_ytop, f);
2689 		calmaOutI4(r.r_xbot, f); calmaOutI4(r.r_ybot, f);
2690 		break;
2691 	    case 0x3:
2692 		calmaOutI4(r.r_xbot, f); calmaOutI4(r.r_ytop, f);
2693 		calmaOutI4(r.r_xtop, f); calmaOutI4(r.r_ytop, f);
2694 		calmaOutI4(r.r_xtop, f); calmaOutI4(r.r_ybot, f);
2695 		calmaOutI4(r.r_xbot, f); calmaOutI4(r.r_ytop, f);
2696 		break;
2697 	}
2698     }
2699     else
2700     {
2701 	/* Coordinates */
2702 	calmaOutRH(44, CALMA_XY, CALMA_I4, f);
2703 	calmaOutI4(r.r_xbot, f); calmaOutI4(r.r_ybot, f);
2704 	calmaOutI4(r.r_xtop, f); calmaOutI4(r.r_ybot, f);
2705 	calmaOutI4(r.r_xtop, f); calmaOutI4(r.r_ytop, f);
2706 	calmaOutI4(r.r_xbot, f); calmaOutI4(r.r_ytop, f);
2707 	calmaOutI4(r.r_xbot, f); calmaOutI4(r.r_ybot, f);
2708     }
2709 
2710     /* End of element */
2711     calmaOutRH(4, CALMA_ENDEL, CALMA_NODATA, f);
2712 
2713     return 0;
2714 }
2715 
2716 /*
2717  * ----------------------------------------------------------------------------
2718  *
2719  * calmaWriteLabelFunc --
2720  *
2721  * Output a single label to the stream file 'f'.
2722  *
2723  * The CIF type to which this label is attached is 'type'; if this
2724  * is < 0 then the label is not output.
2725  *
2726  * Non-point labels are collapsed to point labels located at the center
2727  * of the original label.
2728  *
2729  * Results:
2730  *	None.
2731  *
2732  * Side effects:
2733  *	Writes to the FILE 'f'.
2734  *
2735  * ----------------------------------------------------------------------------
2736  */
2737 
2738 void
calmaWriteLabelFunc(lab,type,f)2739 calmaWriteLabelFunc(lab, type, f)
2740     Label *lab;	/* Label to output */
2741     int type;	/* CIF layer number, or -1 if not attached to a layer */
2742     FILE *f;	/* Stream file */
2743 {
2744     Point p;
2745     int calmanum, calmatype;
2746 
2747     if (type < 0)
2748 	return;
2749 
2750     calmanum = CIFCurStyle->cs_layers[type]->cl_calmanum;
2751     if (!CalmaIsValidLayer(calmanum))
2752 	return;
2753 
2754     calmaOutRH(4, CALMA_TEXT, CALMA_NODATA, f);
2755 
2756     calmaOutRH(6, CALMA_LAYER, CALMA_I2, f);
2757     calmaOutI2(calmanum, f);
2758 
2759     calmatype = CIFCurStyle->cs_layers[type]->cl_calmatype;
2760     calmaOutRH(6, CALMA_TEXTTYPE, CALMA_I2, f);
2761     calmaOutI2(calmatype, f);
2762 
2763     if (lab->lab_font >= 0)
2764     {
2765 	unsigned short textpres = 0;
2766 
2767 	/* A bit of a hack here.  Magic can have any number of fonts,	*/
2768 	/* but GDS only allows four of them.  So we just crop the font	*/
2769 	/* index to two bits.  We provide no other font information, so	*/
2770 	/* this is highly implementation-dependent.  But it allows us	*/
2771 	/* to retain font information when reading and writing our own	*/
2772 	/* GDS files.							*/
2773 
2774 	textpres = (lab->lab_font & 0x03) << 4;
2775 
2776 	switch(lab->lab_just)
2777 	{
2778 	    case GEO_SOUTH:
2779 		textpres |= 0x0001;
2780 		break;
2781 	    case GEO_SOUTHEAST:
2782 		textpres |= 0x0000;
2783 		break;
2784 	    case GEO_EAST:
2785 		textpres |= 0x0004;
2786 		break;
2787 	    case GEO_NORTHEAST:
2788 		textpres |= 0x0008;
2789 		break;
2790 	    case GEO_NORTH:
2791 		textpres |= 0x0009;
2792 		break;
2793 	    case GEO_NORTHWEST:
2794 		textpres |= 0x000a;
2795 		break;
2796 	    case GEO_WEST:
2797 		textpres |= 0x0006;
2798 		break;
2799 	    case GEO_SOUTHWEST:
2800 		textpres |= 0x0002;
2801 		break;
2802 	    case GEO_CENTER:
2803 		textpres |= 0x0005;
2804 		break;
2805 	}
2806 
2807 	calmaOutRH(6, CALMA_PRESENTATION, CALMA_BITARRAY, f);
2808 	calmaOutI2(textpres, f);
2809 
2810 	calmaOutRH(6, CALMA_STRANS, CALMA_BITARRAY, f);
2811 	calmaOutI2(0, f);	/* Any need for these bits? */
2812 
2813 	calmaOutRH(12, CALMA_MAG, CALMA_R8, f);
2814 	calmaOutR8(((double)lab->lab_size / 800)
2815 		* (double)CIFCurStyle->cs_scaleFactor
2816 		/ (double)CIFCurStyle->cs_expander, f);
2817 
2818 	if (lab->lab_rotate != 0)
2819 	{
2820 	    calmaOutRH(12, CALMA_ANGLE, CALMA_R8, f);
2821 	    calmaOutR8((double)lab->lab_rotate, f);
2822 	}
2823     }
2824 
2825     p.p_x = (lab->lab_rect.r_xbot + lab->lab_rect.r_xtop) * calmaWriteScale / 2;
2826     p.p_y = (lab->lab_rect.r_ybot + lab->lab_rect.r_ytop) * calmaWriteScale / 2;
2827     calmaOutRH(12, CALMA_XY, CALMA_I4, f);
2828     calmaOutI4(p.p_x, f);
2829     calmaOutI4(p.p_y, f);
2830 
2831     /* Text of label */
2832     calmaOutStringRecord(CALMA_STRING, lab->lab_text, f);
2833 
2834     /* End of element */
2835     calmaOutRH(4, CALMA_ENDEL, CALMA_NODATA, f);
2836 
2837     /* If the cifoutput layer is for labels only (has no operators),	*/
2838     /* and the label rectangle is not degenerate, then output the label	*/
2839     /* rectangle as a boundary with the label's layer:purpose pair.	*/
2840 
2841     /* Note that the check for whether the CIF_LABEL_NOPORT flag has	*/
2842     /* been set is done outside of this routine.			*/
2843 
2844     if ((CIFCurStyle->cs_layers[type]->cl_ops == NULL) &&
2845 		(lab->lab_rect.r_xtop > lab->lab_rect.r_xbot) &&
2846 		(lab->lab_rect.r_ytop > lab->lab_rect.r_ybot))
2847     {
2848 	Rect r;
2849 
2850 	r = lab->lab_rect;
2851 	r.r_xbot *= calmaWriteScale;
2852 	r.r_ybot *= calmaWriteScale;
2853 	r.r_xtop *= calmaWriteScale;
2854 	r.r_ytop *= calmaWriteScale;
2855 
2856 	/* Boundary */
2857 	calmaOutRH(4, CALMA_BOUNDARY, CALMA_NODATA, f);
2858 
2859 	/* Layer */
2860 	calmaOutRH(6, CALMA_LAYER, CALMA_I2, f);
2861 	calmaOutI2(calmanum, f);
2862 
2863 	/* Data type */
2864 	calmaOutRH(6, CALMA_DATATYPE, CALMA_I2, f);
2865 	calmaOutI2(calmatype, f);
2866 
2867 	/* Coordinates */
2868 	calmaOutRH(44, CALMA_XY, CALMA_I4, f);
2869 	calmaOutI4(r.r_xbot, f); calmaOutI4(r.r_ybot, f);
2870 	calmaOutI4(r.r_xtop, f); calmaOutI4(r.r_ybot, f);
2871 	calmaOutI4(r.r_xtop, f); calmaOutI4(r.r_ytop, f);
2872 	calmaOutI4(r.r_xbot, f); calmaOutI4(r.r_ytop, f);
2873 	calmaOutI4(r.r_xbot, f); calmaOutI4(r.r_ybot, f);
2874 
2875 	/* End of element */
2876 	calmaOutRH(4, CALMA_ENDEL, CALMA_NODATA, f);
2877     }
2878 }
2879 
2880 /*
2881  * ----------------------------------------------------------------------------
2882  *
2883  * calmaPaintLabelFunc --
2884  *
2885  * Filter function used to write out a single label corresponding to the
2886  * area of a paint tile, and having a text matching the CIF layer name.
2887  *
2888  * Results:
2889  *	None.
2890  *
2891  * Side effects:
2892  *	Writes to the disk file.
2893  *
2894  * ----------------------------------------------------------------------------
2895  */
2896 
2897 int
calmaPaintLabelFunc(tile,cos)2898 calmaPaintLabelFunc(tile, cos)
2899     Tile *tile;			/* Tile contains area for label. */
2900     calmaOutputStruct *cos;	/* File for output and clipping area */
2901 {
2902     FILE *f = cos->f;
2903     Rect *clipArea = cos->area;
2904     Rect r, r2;
2905     Point p;
2906     int len;
2907     CIFLayer *layer = CIFCurStyle->cs_layers[cos->type];
2908 
2909     if (IsSplit(tile)) return 0;    /* Ignore non-Manhattan geometry */
2910 
2911     if (!CalmaIsValidLayer(layer->cl_calmanum))
2912 	return 0;
2913 
2914     TiToRect(tile, &r);
2915 
2916     if (clipArea != NULL)
2917 	GeoClip(&r, clipArea);
2918 
2919     r.r_xbot *= calmaPaintScale;
2920     r.r_ybot *= calmaPaintScale;
2921     r.r_xtop *= calmaPaintScale;
2922     r.r_ytop *= calmaPaintScale;
2923 
2924     calmaOutRH(4, CALMA_TEXT, CALMA_NODATA, f);
2925 
2926     calmaOutRH(6, CALMA_LAYER, CALMA_I2, f);
2927     calmaOutI2(layer->cl_calmanum, f);
2928 
2929     calmaOutRH(6, CALMA_TEXTTYPE, CALMA_I2, f);
2930     calmaOutI2(layer->cl_calmatype, f);
2931 
2932     p.p_x = (r.r_xbot + r.r_xtop) * calmaWriteScale / 2;
2933     p.p_y = (r.r_ybot + r.r_ytop) * calmaWriteScale / 2;
2934     calmaOutRH(12, CALMA_XY, CALMA_I4, f);
2935     calmaOutI4(p.p_x, f);
2936     calmaOutI4(p.p_y, f);
2937 
2938     /* Text of label is the CIF layer name */
2939     calmaOutStringRecord(CALMA_STRING, layer->cl_name, f);
2940 
2941     /* End of element */
2942     calmaOutRH(4, CALMA_ENDEL, CALMA_NODATA, f);
2943 
2944     return 0;
2945 }
2946 
2947 /*
2948  * ----------------------------------------------------------------------------
2949  *
2950  * calmaOutHeader --
2951  *
2952  * Output the header description for a Calma file.
2953  *
2954  * Results:
2955  *	None.
2956  *
2957  * Side effects:
2958  *	Writes to the FILE 'f'.
2959  *
2960  * ----------------------------------------------------------------------------
2961  */
2962 
2963 void
calmaOutHeader(rootDef,f)2964 calmaOutHeader(rootDef, f)
2965     CellDef *rootDef;
2966     FILE *f;
2967 {
2968     static double useru = 0.001;
2969     static double mum = 1.0e-9;
2970 
2971     /* GDS II version 3.0 */
2972     calmaOutRH(6, CALMA_HEADER, CALMA_I2, f);
2973     calmaOutI2(3, f);
2974 
2975     /* Beginning of library */
2976     calmaOutRH(28, CALMA_BGNLIB, CALMA_I2, f);
2977     if (CalmaNoDateStamp)
2978     	calmaOutDate(time((time_t *) 0), f);
2979     else
2980     	calmaOutDate(rootDef->cd_timestamp, f);
2981     calmaOutDate(time((time_t *) 0), f);
2982 
2983     /* Library name (name of root cell) */
2984     calmaOutStructName(CALMA_LIBNAME, rootDef, f);
2985 
2986     /*
2987      * Units.
2988      * User units are microns; this is really unimportant.
2989      *
2990      * Database units are nanometers, since there are
2991      * programs that don't understand anything else.  If
2992      * the database units are *smaller* than nanometers, use
2993      * the actual database units.  Otherwise, stick with
2994      * nanometers, because anything larger may not input
2995      * properly with other software.
2996      */
2997 
2998     calmaOutRH(20, CALMA_UNITS, CALMA_R8, f);
2999     if (CIFCurStyle->cs_flags & CWF_ANGSTROMS) useru = 0.0001;
3000     calmaOutR8(useru, f);	/* User units per database unit */
3001 
3002     if (CIFCurStyle->cs_flags & CWF_ANGSTROMS) mum = 1e-10;
3003     calmaOutR8(mum, f);		/* Meters per database unit */
3004 }
3005 
3006 /*
3007  * ----------------------------------------------------------------------------
3008  *
3009  * calmaOutDate --
3010  *
3011  * Output a date/time specification to the FILE 'f'.
3012  * This consists of outputting 6 2-byte quantities,
3013  * or a total of 12 bytes.
3014  *
3015  * Results:
3016  *	None.
3017  *
3018  * Side effects:
3019  *	Writes to the FILE 'f'.
3020  *
3021  * ----------------------------------------------------------------------------
3022  */
3023 
3024 void
calmaOutDate(t,f)3025 calmaOutDate(t, f)
3026     time_t t;	/* Time (UNIX format) to be output */
3027     FILE *f;	/* Stream file */
3028 {
3029     struct tm *datep = localtime(&t);
3030 
3031     calmaOutI2(datep->tm_year, f);
3032     calmaOutI2(datep->tm_mon+1, f);
3033     calmaOutI2(datep->tm_mday, f);
3034     calmaOutI2(datep->tm_hour, f);
3035     calmaOutI2(datep->tm_min, f);
3036     calmaOutI2(datep->tm_sec, f);
3037 }
3038 
3039 /*
3040  * ----------------------------------------------------------------------------
3041  *
3042  * calmaOutStringRecord --
3043  *
3044  * Output a complete string-type record.  The actual record
3045  * type is given by 'type'.  Up to the first CALMANAMELENGTH characters
3046  * of the string 'str' are output.  Any characters in 'str'
3047  * not in the legal Calma stream character set are output as
3048  * 'X' instead.
3049  *
3050  * Results:
3051  *	None.
3052  *
3053  * Side effects:
3054  *	Writes to the FILE 'f'.
3055  *
3056  * ----------------------------------------------------------------------------
3057  */
3058 
3059 void
calmaOutStringRecord(type,str,f)3060 calmaOutStringRecord(type, str, f)
3061     int type;		/* Type of this record (data type is ASCII string) */
3062     char *str;	/* String to be output */
3063     FILE *f;	/* Stream file */
3064 {
3065     int len;
3066     unsigned char c;
3067     char *table, *locstr, *origstr = NULL;
3068     char *locstrprv; 	/* Added by BSI */
3069 
3070     if(CIFCurStyle->cs_flags & CWF_PERMISSIVE_LABELS)
3071     {
3072 	table = calmaMapTablePermissive;
3073     } else {
3074 	table = calmaMapTableStrict;
3075     }
3076 
3077     len = strlen(str);
3078     locstr = str;
3079 
3080     /*
3081      * Make sure length is even.
3082      * Output at most CALMANAMELENGTH characters.
3083      * If the name is longer than CALMANAMELENGTH, then output the
3084      * last CALMANAMELENGTH characters (since cell names are more
3085      * likely to be unique in the last characters than in the first
3086      * characters).
3087      *
3088      * NOTE:  GDS format has not used CALMANAMELENGTH restrictions
3089      * for ages.  Since this is a 2-byte record, then is it not
3090      * worth checking the 65536 - 4 character limit.  The CALMANAMELENGTH
3091      * restriction must be enabled in the cifoutput flags.
3092      */
3093 
3094 
3095     if (len & 01) len++;
3096     if ((CIFCurStyle->cs_flags & CWF_STRING_LIMIT) && (len > CALMANAMELENGTH))
3097     {
3098 	TxError("Warning:  Cellname %s truncated ", str);
3099 	TxError("to %s (GDS format limit)\n", str + len - CALMANAMELENGTH);
3100 	locstr = str + len - CALMANAMELENGTH;
3101 	len = CALMANAMELENGTH;
3102     }
3103     calmaOutI2(len+4, f);	/* Record length */
3104     (void) putc(type, f);		/* Record type */
3105     (void) putc(CALMA_ASCII, f);	/* Data type */
3106 
3107     /* Output the string itself */
3108     while (len--)
3109     {
3110 	locstrprv = locstr;
3111 	c = (unsigned char) *locstr++;
3112 	if (c == 0) putc('\0', f);
3113 	else
3114 	{
3115 	    if ((c > 127) || (c == 0))
3116 	    {
3117 		TxError("Warning: Unprintable character changed "
3118 			"to \'X\' in label.\n");
3119 		c = 'X';
3120 	    }
3121 	    else
3122 	    {
3123 		if (((unsigned char)table[c] != c) && (origstr == NULL))
3124 		    origstr = StrDup(NULL, str);
3125 
3126 		c = table[c];
3127 		locstrprv[0] = c;
3128 	    }
3129 	    if (!CalmaDoLower && islower(c))
3130 		(void) putc(toupper(c), f);
3131 	    else
3132 		(void) putc(c, f);
3133 	}
3134     }
3135     if (origstr != NULL)
3136     {
3137 	TxError("Warning: characters changed in string \'%s\'; "
3138 		"modified string is \'%s\'\n", origstr, str);
3139 	freeMagic(origstr);
3140     }
3141 }
3142 
3143 /*
3144  * ----------------------------------------------------------------------------
3145  *
3146  * calmaOutR8 --
3147  *
3148  * Write an 8-byte Real value in GDS-II format to the output stream
3149  * The value is passed as a double.
3150  *
3151  * Results:
3152  *	None.
3153  *
3154  * Side effects:
3155  *	8-byte value written to output stream FILE 'f'.
3156  *
3157  * ----------------------------------------------------------------------------
3158  */
3159 
3160 void
calmaOutR8(d,f)3161 calmaOutR8(d, f)
3162     double d;	/* Double value to write to output */
3163     FILE *f;	/* Stream file */
3164 {
3165     int c, i, sign, expon;
3166 
3167     /* mantissa must be 64 bits for this routine to work correctly */
3168     uint64_t mantissa;
3169 
3170     mantissa = 0;
3171     if (d == 0.0)
3172     {
3173 	sign = 0;
3174 	expon = 0;
3175     }
3176     else
3177     {
3178 	if (d > 0.0)
3179 	    sign = 0;
3180 	else
3181 	{
3182 	    sign = 1;
3183 	    d = -d;
3184 	}
3185 
3186 	expon = 64;
3187 	while (d >= 1.0)
3188 	{
3189 	    d /= 16.0;
3190 	    expon++;
3191 	}
3192 	while (d < 0.0625)
3193 	{
3194 	    d *= 16.0;
3195 	    expon--;
3196 	}
3197 
3198 	for (i = 0; i < 64; i++)
3199 	{
3200 	    mantissa <<= 1;
3201 	    if (d >= 0.5)
3202 	    {
3203 		mantissa |= 0x1;
3204 		d -= 0.5;
3205 	    }
3206 	    d *= 2.0;
3207 	}
3208     }
3209     c = (sign << 7) | expon;
3210     (void) putc(c, f);
3211     for (i = 1; i < 8; i++)
3212     {
3213 	c = (int)(0xff & (mantissa >> (64 - (8 * i))));
3214 	(void) putc(c, f);
3215     }
3216 }
3217 
3218 
3219 /*
3220  * ----------------------------------------------------------------------------
3221  *
3222  * calmaOut8 --
3223  *
3224  * Output 8 bytes.
3225  *
3226  * Results:
3227  *	None.
3228  *
3229  * Side effects:
3230  *	Writes to the FILE 'f'.
3231  *
3232  * ----------------------------------------------------------------------------
3233  */
3234 
3235 void
calmaOut8(str,f)3236 calmaOut8(str, f)
3237     char *str;	/* 8-byte string to be output */
3238     FILE *f;	/* Stream file */
3239 {
3240     int i;
3241 
3242     for (i = 0; i < 8; i++)
3243 	(void) putc(*str++, f);
3244 }
3245