1 /* -*- tab-width: 4 -*-
2  *
3  * Electric(tm) VLSI Design System
4  *
5  * File: dbtext.c
6  * Database text and file support module
7  * Written by: Steven M. Rubin, Static Free Software
8  *
9  * Copyright (c) 2000 Static Free Software.
10  *
11  * Electric(tm) is free software; you can redistribute it and/or modify
12  * it under the terms of the GNU General Public License as published by
13  * the Free Software Foundation; either version 2 of the License, or
14  * (at your option) any later version.
15  *
16  * Electric(tm) is distributed in the hope that it will be useful,
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19  * GNU General Public License for more details.
20  *
21  * You should have received a copy of the GNU General Public License
22  * along with Electric(tm); see the file COPYING.  If not, write to
23  * the Free Software Foundation, Inc., 59 Temple Place, Suite 330,
24  * Boston, Mass 02111-1307, USA.
25  *
26  * Static Free Software
27  * 4119 Alpine Road
28  * Portola Valley, California 94028
29  * info@staticfreesoft.com
30  */
31 
32 #include "global.h"
33 #include "database.h"
34 #include "egraphics.h"
35 #include "edialogs.h"
36 #include "network.h"
37 #include "usr.h"
38 #include "tecgen.h"
39 #ifdef WIN32
40 #  include <io.h>
41 #endif
42 #ifdef HAVE_UNISTD_H
43 #  include <unistd.h>
44 #endif
45 
46 #define PUREUNITGIGA    0		/* giga:  x 1000000000 */
47 #define PUREUNITMEGA    1		/* mega:  x 1000000 */
48 #define PUREUNITKILO    2		/* kilo:  x 1000 */
49 #define PUREUNITNONE    3		/* -:     x 1 */
50 #define PUREUNITMILLI   4		/* milli: / 1000 */
51 #define PUREUNITMICRO   5		/* micro: / 1000000 */
52 #define PUREUNITNANO    6		/* nano:  / 1000000000 */
53 #define PUREUNITPICO    7		/* pico:  / 1000000000000 */
54 #define PUREUNITFEMTO   8		/* femto: / 1000000000000000 */
55 
56 #define NUMDESCRIPTIONBUFS 4
57 
58 static CHAR  *db_keywordbuffer;
59 static INTBIG db_keywordbufferlength = 0;
60 
61 /* working memory for "db_tryfile()" */
62 static CHAR  *db_tryfilename;
63 static INTBIG db_tryfilenamelen = 0;
64 
65 /* working memory for "describenodeproto()" */
66 static INTBIG db_descnpnodeswitch = 0;
67 static CHAR  *db_descnpoutput[NUMDESCRIPTIONBUFS];
68 static INTBIG db_descnpoutputsize[NUMDESCRIPTIONBUFS] = {0, 0};
69 
70 /* working memory for "describenodeinst()" */
71 static INTBIG db_descninodeswitch = 0;
72 static CHAR  *db_descnioutput[NUMDESCRIPTIONBUFS];
73 static INTBIG db_descnioutputsize[NUMDESCRIPTIONBUFS] = {0, 0};
74 
75 /* working memory for "describearcinst()" */
76 static INTBIG db_descaiarcswitch = 0;
77 static CHAR  *db_descaioutput[NUMDESCRIPTIONBUFS];
78 static INTBIG db_descaioutputsize[NUMDESCRIPTIONBUFS] = {0, 0};
79 
80 /* working memory for "describearcproto()" */
81 static INTBIG db_descaparcswitch = 0;
82 static CHAR  *db_descapoutput[NUMDESCRIPTIONBUFS];
83 static INTBIG db_descapoutputsize[NUMDESCRIPTIONBUFS] = {0, 0};
84 
85 /* working memory for "makeplural()" */
86 static INTBIG db_pluralbufsize = 0;
87 static CHAR  *db_pluralbuffer;
88 
89 /* working memory for "getcomplexnetworks()" */
90 static LISTINTBIG *db_networklist = 0;
91 
92 /* working memory for types of files */
93 typedef struct
94 {
95 	CHAR   *extension;
96 	CHAR   *winfilter;
97 	INTBIG  mactype;
98 	BOOLEAN binary;
99 	CHAR   *shortname;
100 	CHAR   *longname;
101 } FILETYPES;
102 static INTBIG     db_filetypecount = 0;
103 static FILETYPES *db_filetypeinfo;
104 
105 /* the infinite string package */
106 #define INFSTRCOUNT     8				/* number of infinite strings */
107 #define INFSTRDEFAULT 200				/* default infinite string length */
108 #define NOINFSTR ((INFSTR *)-1)
109 typedef struct Iinfstr
110 {
111 	CHAR  *infstr;						/* the string address */
112 	INTBIG infstrlength;				/* the length of the string */
113 	INTBIG infstrptr;					/* the location of the string end */
114 	struct Iinfstr *nextinfstr;			/* next in linked list */
115 } INFSTR;
116 static BOOLEAN db_firstinf = FALSE;		/* initialization flag */
117 static INFSTR *db_firstfreeinfstr;		/* first free infinite string block */
118 static INFSTR *db_lastfreeinfstr;		/* last free infinite string block */
119 static void   *db_infstrmutex = 0;		/* mutex for infinite string modules */
120 
121 /* for case-insensitive string compare ("namesame()" and "namesamen()") */
122 static BOOLEAN db_namesamefirst = TRUE;
123 static CHAR    db_namesametable[256];
124 
125 /* prototypes for local routines */
126 static BOOLEAN db_beginsearch(CHAR**);
127 static void    db_makestringvar(INTBIG, INTBIG, INTBIG, INTBIG, void*);
128 static void    db_addstring(CHAR*, void*);
129 static FILE   *db_tryfile(CHAR*, CHAR*, CHAR*, CHAR*, CHAR**);
130 static void    db_shuffle(CHAR*, CHAR*);
131 static void    db_initnamesame(void);
132 static CHAR   *db_makeunits(float value, INTBIG units);
133 static INTBIG  db_getpureunit(INTBIG unittype, INTBIG unitscale);
134 
135 /*
136  * Routine to free all memory associated with this module.
137  */
db_freetextmemory(void)138 void db_freetextmemory(void)
139 {
140 	REGISTER INTBIG i;
141 	REGISTER INFSTR *inf;
142 
143 	if (db_pluralbufsize > 0) efree(db_pluralbuffer);
144 	if (db_keywordbufferlength != 0) efree(db_keywordbuffer);
145 	if (db_tryfilenamelen != 0) efree(db_tryfilename);
146 	if (db_networklist != 0) killintlistobj(db_networklist);
147 
148 	while (db_firstfreeinfstr != NOINFSTR)
149 	{
150 		inf = db_firstfreeinfstr;
151 		db_firstfreeinfstr = inf->nextinfstr;
152 		efree((CHAR *)inf->infstr);
153 		efree((CHAR *)inf);
154 	}
155 
156 	for(i=0; i<NUMDESCRIPTIONBUFS; i++)
157 		if (db_descnpoutputsize[i] != 0) efree(db_descnpoutput[i]);
158 	for(i=0; i<NUMDESCRIPTIONBUFS; i++)
159 		if (db_descnioutputsize[i] != 0) efree(db_descnioutput[i]);
160 	for(i=0; i<NUMDESCRIPTIONBUFS; i++)
161 		if (db_descaioutputsize[i] != 0) efree(db_descaioutput[i]);
162 	for(i=0; i<NUMDESCRIPTIONBUFS; i++)
163 		if (db_descapoutputsize[i] != 0) efree(db_descapoutput[i]);
164 
165 	/* free file type memory */
166 	for(i=0; i<db_filetypecount; i++)
167 	{
168 		efree((CHAR *)db_filetypeinfo[i].extension);
169 		efree((CHAR *)db_filetypeinfo[i].winfilter);
170 		efree((CHAR *)db_filetypeinfo[i].shortname);
171 		efree((CHAR *)db_filetypeinfo[i].longname);
172 	}
173 	if (db_filetypecount > 0) efree((CHAR *)db_filetypeinfo);
174 }
175 
176 /************************* STRING PARSING *************************/
177 
178 /*
179  * routine to parse a lambda value of the form "nn.dd[u | " | cm | mil | mm | cu | nm]"
180  * where the unlabeled number defaults to the current DISPLAYUNITS of the
181  * current technology but trailing letters can override.  The input is in
182  * the string "pp".
183  */
atola(CHAR * pp,INTBIG lambda)184 INTBIG atola(CHAR *pp, INTBIG lambda)
185 {
186 	REGISTER INTBIG hipart, lonum, loden, retval, units;
187 	REGISTER INTBIG neg;
188 	REGISTER CHAR *ptr;
189 	double scale;
190 
191 	/* determine default scale amount */
192 	ptr = pp;
193 
194 	if (*ptr == '-') { neg = -1;   ptr++; } else neg = 1;
195 	hipart = eatoi(ptr);
196 	while (isdigit(*ptr)) ptr++;
197 	lonum = 0;   loden = 1;
198 	if (*ptr == '.')
199 	{
200 		ptr++;
201 		while (isdigit(*ptr)) { lonum = lonum * 10 + (*ptr++ - '0'); loden *= 10; }
202 	}
203 
204 	/* determine units */
205 	units = el_units;
206 	if (ptr[0] == '"') units = (units & ~DISPLAYUNITS) | DISPUNITINCH; else
207 	if (ptr[0] == 'c' && ptr[1] == 'm') units = (units & ~DISPLAYUNITS) | DISPUNITCM; else
208 	if (ptr[0] == 'm' && ptr[1] == 'm') units = (units & ~DISPLAYUNITS) | DISPUNITMM;
209 	if (ptr[0] == 'm' && ptr[1] == 'i' && ptr[2] == 'l') units = (units & ~DISPLAYUNITS) | DISPUNITMIL;
210 	if (ptr[0] == 'u') units = (units & ~DISPLAYUNITS) | DISPUNITMIC; else
211 	if (ptr[0] == 'c' && ptr[1] == 'u') units = (units & ~DISPLAYUNITS) | DISPUNITCMIC; else
212 	if (ptr[0] == 'n' && ptr[1] == 'm') units = (units & ~DISPLAYUNITS) | DISPUNITNM;
213 
214 	/* convert to database units */
215 	if (lambda == 0) lambda = el_curlib->lambda[el_curtech->techindex];
216 	scale = db_getcurrentscale(el_units&INTERNALUNITS, units, lambda);
217 	retval = rounddouble(((double)hipart) * scale + ((double)lonum)*scale / ((double)loden));
218 	return(retval*neg);
219 }
220 
221 /*
222  * routine to parse a fixed point value of the form "n.d" where
223  * "d" is evaluated to the nearest 120th (the value of WHOLE).
224  * The number is returned scaled by a factor of WHOLE.  The input is in
225  * the string "pp".
226  */
atofr(CHAR * pp)227 INTBIG atofr(CHAR *pp)
228 {
229 	REGISTER INTBIG i, j, k;
230 	REGISTER INTBIG n;
231 
232 	if (*pp == '-') { n = -1;   pp++; } else n = 1;
233 	i = eatoi(pp) * WHOLE;
234 	while (isdigit(*pp)) pp++;
235 	if (*pp++ != '.') return(i*n);
236 	j = 0;   k = 1;
237 	while (isdigit(*pp)) { j = j * 10 + (*pp++ - '0'); k *= 10; }
238 	i += (j*WHOLE + k/2)/k;
239 	return(i*n);
240 }
241 
242 /* routine to convert ascii to integer */
myatoi(CHAR * pp)243 INTBIG myatoi(CHAR *pp)
244 {
245 	REGISTER INTBIG num;
246 	REGISTER INTBIG base, sign;
247 
248 	base = 10;
249 	num = 0;
250 	sign = 1;
251 	if (*pp == '-')
252 	{
253 		pp++;
254 		sign = -1;
255 	}
256 	if (*pp == '0')
257 	{
258 		pp++;
259 		base = 8;
260 		if (*pp == 'x')
261 		{
262 			pp++;
263 			base = 16;
264 		}
265 	}
266 	for(;;)
267 	{
268 		if ((*pp >= 'a' && *pp <= 'f') || (*pp >= 'A' && *pp <= 'F'))
269 		{
270 			if (base != 16) break;
271 			num = num * 16;
272 			if (*pp >= 'a' && *pp <= 'f') num += *pp++ - 'a' + 10; else
273 				num += *pp++ - 'A' + 10;
274 			continue;
275 		} else if (isdigit(*pp))
276 		{
277 			if (*pp >= '8' && base == 8) break;
278 			num = num * base + *pp++ - '0';
279 			continue;
280 		}
281 		break;
282 	}
283 	return(num * sign);
284 }
285 
286 /*
287  * Routine to convert a HUGE integer (64 bits) to a string.
288  * This routine does the work by hand, but it can be done with
289  * special "printf" format conversions, of which these are known:
290  *    Windows: "%I64d"
291  *    Sun:     "%PRIx64"
292  *    Linux:   "%lld"
293  */
hugeinttoa(INTHUGE a)294 CHAR *hugeinttoa(INTHUGE a)
295 {
296 	static CHAR ret[NUMDESCRIPTIONBUFS][40];
297 	static INTBIG which = 0;
298 	REGISTER CHAR *curbuf, digit;
299 	REGISTER INTBIG i, neg;
300 
301 	/* code cannot be called by multiple procesors: uses globals */
302 	NOT_REENTRANT;
303 
304 	curbuf = ret[which++];
305 	if (which >= NUMDESCRIPTIONBUFS) which = 0;
306 
307 	if (a >= 0) neg = 0; else
308 	{
309 		neg = 1;
310 		a = -a;
311 	}
312 
313 	curbuf[i=39] = 0;
314 	while (i > 0)
315 	{
316 		digit = (CHAR)(a % 10);
317 		curbuf[--i] = '0' + digit;
318 		a /= 10;
319 		if (a == 0) break;
320 	}
321 	if (neg != 0)
322 		curbuf[--i] = '-';
323 	return(&curbuf[i]);
324 }
325 
explainduration(float duration)326 CHAR *explainduration(float duration)
327 {
328 	static CHAR elapsed[200];
329 	CHAR temp[50];
330 	INTBIG hours, minutes;
331 
332 	/* code cannot be called by multiple procesors: uses globals */
333 	NOT_REENTRANT;
334 
335 	elapsed[0] = 0;
336 	if (duration >= 3600.0)
337 	{
338 		hours = (INTBIG)(duration / 3600.0f);
339 		duration -= (float)(hours * 3600);
340 		esnprintf(temp, 50, x_("%ld hours, "), hours);
341 		estrcat(elapsed, temp);
342 	}
343 	if (duration >= 60.0)
344 	{
345 		minutes = (INTBIG)(duration / 60.0f);
346 		duration -= (float)(minutes * 60);
347 		esnprintf(temp, 50, x_("%ld minutes, "), minutes);
348 		estrcat(elapsed, temp);
349 	}
350 	esnprintf(temp, 50, x_("%g seconds"), duration);
351 	estrcat(elapsed, temp);
352 	return(elapsed);
353 }
354 
355 /*
356  * Routine to get the pure unit to use for electrical unit type "unittype", scale
357  * "unitscale".
358  */
db_getpureunit(INTBIG unittype,INTBIG unitscale)359 INTBIG db_getpureunit(INTBIG unittype, INTBIG unitscale)
360 {
361 	switch (unittype)
362 	{
363 		case VTUNITSRES:		/* resistance */
364 			switch (unitscale)
365 			{
366 				case INTRESUNITKOHM: return(PUREUNITKILO);
367 				case INTRESUNITMOHM: return(PUREUNITMEGA);
368 				case INTRESUNITGOHM: return(PUREUNITGIGA);
369 			}
370 			break;
371 
372 		case VTUNITSCAP:		/* capacitance */
373 			switch (unitscale)
374 			{
375 				case INTCAPUNITMFARAD: return(PUREUNITMILLI);
376 				case INTCAPUNITUFARAD: return(PUREUNITMICRO);
377 				case INTCAPUNITNFARAD: return(PUREUNITNANO);
378 				case INTCAPUNITPFARAD: return(PUREUNITPICO);
379 				case INTCAPUNITFFARAD: return(PUREUNITFEMTO);
380 			}
381 			break;
382 
383 		case VTUNITSIND:		/* inductance */
384 			switch (unitscale)
385 			{
386 				case INTINDUNITMHENRY: return(PUREUNITMILLI);
387 				case INTINDUNITUHENRY: return(PUREUNITMICRO);
388 				case INTINDUNITNHENRY: return(PUREUNITNANO);
389 			}
390 			break;
391 
392 		case VTUNITSCUR:		/* current */
393 			switch (unitscale)
394 			{
395 				case INTCURUNITMAMP: return(PUREUNITMILLI);
396 				case INTCURUNITUAMP: return(PUREUNITMICRO);
397 			}
398 			break;
399 
400 		case VTUNITSVOLT:		/* voltage */
401 			switch (unitscale)
402 			{
403 				case INTVOLTUNITKVOLT: return(PUREUNITKILO);
404 				case INTVOLTUNITMVOLT: return(PUREUNITMILLI);
405 				case INTVOLTUNITUVOLT: return(PUREUNITMICRO);
406 			}
407 			break;
408 
409 		case VTUNITSTIME:		/* time */
410 			switch (unitscale)
411 			{
412 				case INTTIMEUNITMSEC: return(PUREUNITMILLI);
413 				case INTTIMEUNITUSEC: return(PUREUNITMICRO);
414 				case INTTIMEUNITNSEC: return(PUREUNITNANO);
415 				case INTTIMEUNITPSEC: return(PUREUNITPICO);
416 				case INTTIMEUNITFSEC: return(PUREUNITFEMTO);
417 			}
418 			break;
419 	}
420 	return(PUREUNITNONE);
421 }
422 
423 /*
424  * Routine to express "value" as a string in "unittype" electrical units.
425  * The scale of the units is in "unitscale".
426  */
displayedunits(float value,INTBIG unittype,INTBIG unitscale)427 CHAR *displayedunits(float value, INTBIG unittype, INTBIG unitscale)
428 {
429 	static CHAR line[100];
430 	REGISTER INTBIG pureunit;
431 	CHAR *postfix;
432 
433 	/* code cannot be called by multiple procesors: uses globals */
434 	NOT_REENTRANT;
435 
436 	pureunit = db_getpureunit(unittype, unitscale);
437 	switch (pureunit)
438 	{
439 		case PUREUNITGIGA:		/* giga:  x 1000000000 */
440 			value /= 1000000000.0f;
441 			postfix = x_("g");
442 			break;
443 		case PUREUNITMEGA:		/* mega:  x 1000000 */
444 			value /= 1000000.0f;
445 			postfix = x_("meg");		/* SPICE wants "x" */
446 			break;
447 		case PUREUNITKILO:		/* kilo:  x 1000 */
448 			value /= 1000.0f;
449 			postfix = x_("k");
450 			break;
451 		case PUREUNITNONE:		/* -:     x 1 */
452 			postfix = x_("");
453 			break;
454 		case PUREUNITMILLI:		/* milli: / 1000 */
455 			value *= 1000.0f;
456 			postfix = x_("m");
457 			break;
458 		case PUREUNITMICRO:		/* micro: / 1000000 */
459 			value *= 1000000.0f;
460 			postfix = x_("u");
461 			break;
462 		case PUREUNITNANO:		/* nano:  / 1000000000 */
463 			value *= 1000000000.0f;
464 			postfix = x_("n");
465 			break;
466 		case PUREUNITPICO:		/* pico:  / 1000000000000 */
467 			value *= 1000000000000.0f;
468 			postfix = x_("p");
469 			break;
470 		case PUREUNITFEMTO:		/* femto: / 1000000000000000 */
471 			value *= 1000000000000000.0f;
472 			postfix = x_("f");
473 			break;
474 	}
475 	esnprintf(line, 100, x_("%g%s"), value, postfix);
476 	return(line);
477 }
478 
479 /*
480  * Routine to convert the string "str" to electrical units of type "unittype",
481  * presuming that the scale is in "unitscale".
482  */
figureunits(CHAR * str,INTBIG unittype,INTBIG unitscale)483 float figureunits(CHAR *str, INTBIG unittype, INTBIG unitscale)
484 {
485 	float value;
486 	REGISTER INTBIG len, pureunit;
487 
488 	len = estrlen(str);
489 	value = (float)eatof(str);
490 	pureunit = db_getpureunit(unittype, unitscale);
491 	if (len > 1)
492 	{
493 		if (namesame(&str[len-1], x_("g")) == 0) pureunit = PUREUNITGIGA;
494 		if (namesame(&str[len-1], x_("k")) == 0) pureunit = PUREUNITKILO;
495 		if (namesame(&str[len-1], x_("m")) == 0) pureunit = PUREUNITMILLI;
496 		if (namesame(&str[len-1], x_("u")) == 0) pureunit = PUREUNITMICRO;
497 		if (namesame(&str[len-1], x_("n")) == 0) pureunit = PUREUNITNANO;
498 		if (namesame(&str[len-1], x_("p")) == 0) pureunit = PUREUNITPICO;
499 		if (namesame(&str[len-1], x_("f")) == 0) pureunit = PUREUNITFEMTO;
500 	}
501 	if (len > 3)
502 	{
503 		if (namesame(&str[len-3], x_("meg")) == 0) pureunit = PUREUNITMEGA;
504 	}
505 	switch (pureunit)
506 	{
507 		case PUREUNITGIGA:		/* giga:  x 1000000000 */
508 			value *= 1000000000.0f;
509 			break;
510 		case PUREUNITMEGA:		/* mega:  x 1000000 */
511 			value *= 1000000.0f;
512 			break;
513 		case PUREUNITKILO:		/* kilo:  x 1000 */
514 			value *= 1000.0f;
515 			break;
516 		case PUREUNITMILLI:		/* milli: / 1000 */
517 			value /= 1000.0f;
518 			break;
519 		case PUREUNITMICRO:		/* micro: / 1000000 */
520 			value /= 1000000.0f;
521 			break;
522 		case PUREUNITNANO:		/* nano:  / 1000000000 */
523 			value /= 1000000000.0f;
524 			break;
525 		case PUREUNITPICO:		/* pico:  / 1000000000000 */
526 			value /= 1000000000000.0f;
527 			break;
528 		case PUREUNITFEMTO:		/* femto: / 1000000000000000 */
529 			value /= 1000000000000000.0f;
530 			break;
531 	}
532 	return(value);
533 }
534 
535 /*
536  * Routine to parse the version of Electric in "version" into three fields:
537  * the major version number, minor version, and a detail version number.
538  * The detail version number can be letters.  If it is omitted, it is
539  * assumed to be 999.  If it is a number, it is beyond 1000.  For example:
540  *    "6.02a"     major=6, minor=2, detail=1       (a Prerelease)
541  *    "6.02z"     major=6, minor=2, detail=26      (a Prerelease)
542  *    "6.02aa"    major=6, minor=2, detail=27      (a Prerelease)
543  *    "6.02az"    major=6, minor=2, detail=52      (a Prerelease)
544  *    "6.02ba"    major=6, minor=2, detail=53      (a Prerelease)
545  *    "6.02"      major=6, minor=2, detail=999     (a Release)
546  *    "6.02.1"    major=6, minor=2, detail=1001    (a PostRelease, update)
547  */
parseelectricversion(CHAR * version,INTBIG * major,INTBIG * minor,INTBIG * detail)548 void parseelectricversion(CHAR *version, INTBIG *major, INTBIG *minor, INTBIG *detail)
549 {
550 	REGISTER CHAR *pt;
551 
552 	/* parse the version fields */
553 	pt = version;
554 	*major = eatoi(pt);
555 	while (isdigit(*pt) != 0) pt++;
556 	if (*pt++ != '.') { *minor = *detail = 0;   return; }
557 	*minor = eatoi(pt);
558 	while (isdigit(*pt) != 0) pt++;
559 	if (*pt == 0) { *detail = 999;   return; }
560 	if (*pt == '.')
561 	{
562 		*detail = eatoi(&pt[1]) + 1000;
563 	} else
564 	{
565 		*detail = 0;
566 		while (isalpha(*pt))
567 		{
568 			*detail = (*detail * 26) + tolower(*pt) - 'a' + 1;
569 			pt++;
570 		}
571 	}
572 }
573 
574 /*
575  * routine to determine which node prototype is referred to by "line"
576  * and return that nodeproto.  The routine returns NONODEPROTO if the
577  * prototype cannot be determined.
578  */
getnodeproto(CHAR * initline)579 NODEPROTO *getnodeproto(CHAR *initline)
580 {
581 	REGISTER NODEPROTO *np;
582 	REGISTER TECHNOLOGY *tech, *t;
583 	REGISTER INTBIG wantversion, save, saidtech, saidlib;
584 	REGISTER LIBRARY *lib, *l;
585 	REGISTER VIEW *wantview, *v;
586 	REGISTER CHAR *pt, *line, *nameend, *viewname;
587 	REGISTER void *infstr;
588 
589 	/* make a copy of the argument so that it can be modified */
590 	infstr = initinfstr();
591 	addstringtoinfstr(infstr, initline);
592 	line = returninfstr(infstr);
593 
594 	tech = el_curtech;   lib = el_curlib;
595 	saidtech = saidlib = 0;
596 	for(pt = line; *pt != 0; pt++) if (*pt == ':') break;
597 	if (*pt != ':') pt = line; else
598 	{
599 		*pt = 0;
600 		t = gettechnology(line);
601 		if (t != NOTECHNOLOGY)
602 		{
603 			tech = t;
604 			saidtech++;
605 		}
606 		l = getlibrary(line);
607 		if (l != NOLIBRARY)
608 		{
609 			lib = l;
610 			saidlib++;
611 		}
612 		*pt++ = ':';
613 		line = pt;
614 	}
615 
616 	/* try primitives in the technology */
617 	if (saidlib == 0 || saidtech != 0)
618 	{
619 		for(np = tech->firstnodeproto; np != NONODEPROTO; np = np->nextnodeproto)
620 			if (namesame(line, np->protoname) == 0) return(np);
621 	}
622 
623 	/* get the view and version information */
624 	for(pt = line; *pt != 0; pt++) if (*pt == ';' || *pt == '{') break;
625 	nameend = pt;
626 	wantversion = -1;
627 	wantview = el_unknownview;
628 	if (*pt == ';')
629 	{
630 		wantversion = myatoi(pt+1);
631 		for(pt++; *pt != 0; pt++) if (*pt == '{') break;
632 	}
633 	if (*pt == '{')
634 	{
635 		viewname = pt = (pt + 1);
636 		for(; *pt != 0; pt++) if (*pt == '}') break;
637 		if (*pt != '}') return(NONODEPROTO);
638 		*pt = 0;
639 		for(v = el_views; v != NOVIEW; v = v->nextview)
640 			if (namesame(v->sviewname, viewname) == 0 || namesame(v->viewname, viewname) == 0) break;
641 		*pt = '}';
642 		if (v == NOVIEW) return(NONODEPROTO);
643 		wantview = v;
644 	}
645 	save = *nameend;
646 	*nameend = 0;
647 	np = db_findnodeprotoname(line, wantview, lib);
648 	if (np == NONODEPROTO && wantview == el_unknownview)
649 	{
650 		/* search for any view */
651 		for(np = lib->firstnodeproto; np != NONODEPROTO; np = np->nextnodeproto)
652 			if (namesame(line, np->protoname) == 0) break;
653 	}
654 	*nameend = (CHAR)save;
655 	if (np == NONODEPROTO) return(NONODEPROTO);
656 	if (wantversion < 0 || np->version == wantversion) return(np);
657 	for(np = np->prevversion; np != NONODEPROTO; np = np->prevversion)
658 		if (np->version == wantversion) return(np);
659 	return(NONODEPROTO);
660 }
661 
662 /*
663  * routine to find technology "techname".  Returns NOTECHNOLOGY if it cannot
664  * be found
665  */
666 static COMCOMP db_technologyp = {NOKEYWORD, topoftechs, nexttechs, NOPARAMS,
667 	0, x_(" \t"), M_("technology"), 0};
gettechnology(CHAR * techname)668 TECHNOLOGY *gettechnology(CHAR *techname)
669 {
670 	REGISTER INTBIG i;
671 	REGISTER TECHNOLOGY *tech;
672 
673 	i = parse(techname, &db_technologyp, FALSE);
674 	if (i < 0) return(NOTECHNOLOGY);
675 	for(tech = el_technologies; tech != NOTECHNOLOGY; tech = tech->nexttechnology)
676 		if (tech->techindex == i) return(tech);
677 	return(NOTECHNOLOGY);
678 }
679 
680 /*
681  * routine to find view "viewname".  Returns NOVIEW if it cannot be found
682  */
683 static COMCOMP db_viewp = {NOKEYWORD, topofviews, nextviews, NOPARAMS,
684 	0, x_(" \t"), M_("view"), 0};
getview(CHAR * viewname)685 VIEW *getview(CHAR *viewname)
686 {
687 	REGISTER INTBIG i, j;
688 	REGISTER VIEW *v;
689 
690 	i = parse(viewname, &db_viewp, FALSE);
691 	if (i < 0) return(NOVIEW);
692 	for(j=0, v = el_views; v != NOVIEW; v = v->nextview, j++)
693 		if (j == i) return(v);
694 	return(NOVIEW);
695 }
696 
697 /*
698  * routine to find network "netname" in cell "cell".  Returns NONETWORK
699  * if it cannot be found
700  */
getnetwork(CHAR * netname,NODEPROTO * cell)701 NETWORK *getnetwork(CHAR *netname, NODEPROTO *cell)
702 {
703 	REGISTER INTBIG i, k;
704 	REGISTER CHAR *pt;
705 	REGISTER NETWORK *net;
706 
707 	net = net_getnetwork(netname, cell);
708 	if (net != NONETWORK) return(net);
709 
710 	/* see if a global name is specified */
711 	pt = _("Global");
712 	k = estrlen(pt);
713 	if (namesamen(netname, pt, k) == 0 && netname[k] == '-')
714 	{
715 		for(i=0; i<cell->globalnetcount; i++)
716 			if (namesame(&netname[k+1], cell->globalnetnames[i]) == 0)
717 				return(cell->globalnetworks[i]);
718 	}
719 	return(NONETWORK);
720 }
721 
722 /*
723  * routine to find network "netname" in cell "cell".  Returns a list of
724  * networks, terminated by NONETWORK.  This routine allows for variations of the network
725  * name that may occur due to simulation, VHDL compilation, etc.
726  */
getcomplexnetworks(CHAR * name,NODEPROTO * np)727 NETWORK **getcomplexnetworks(CHAR *name, NODEPROTO *np)
728 {
729 	REGISTER INTBIG len, i, l, k, c1, c2, addr;
730 	INTBIG count;
731 	REGISTER NETWORK *net;
732 	REGISTER CHAR *pt, save, *lastslash;
733 
734 	/* make sure there is a list of networks */
735 	if (db_networklist == 0) db_networklist = newintlistobj(db_cluster);
736 	clearintlistobj(db_networklist);
737 
738 	/* try the direct approach */
739 	net = getnetwork(name, np);
740 	if (net != NONETWORK)
741 	{
742 		addtointlistobj(db_networklist, (INTBIG)net, FALSE);
743 		addtointlistobj(db_networklist, (INTBIG)NONETWORK, FALSE);
744 		return((NETWORK **)getintlistobj(db_networklist, &count));
745 	}
746 
747 	/* some netlisters generate the net name as "NET" followed by the object address */
748 	if (name[0] == 'N' && name[1] == 'E' && name[2] == 'T')
749 	{
750 		addr = eatoi(&name[3]);
751 		for(net = np->firstnetwork; net != NONETWORK; net = net->nextnetwork)
752 		{
753 			if (net == (NETWORK *)addr)
754 			{
755 				addtointlistobj(db_networklist, (INTBIG)net, FALSE);
756 				addtointlistobj(db_networklist, (INTBIG)NONETWORK, FALSE);
757 				return((NETWORK **)getintlistobj(db_networklist, &count));
758 			}
759 		}
760 	}
761 
762 	/* if the name ends with "NV", try removing that */
763 	len = estrlen(name);
764 	if (name[len-2] == 'N' && name[len-1] == 'V')
765 	{
766 		name[len-2] = 0;
767 		net = getnetwork(name, np);
768 		name[len-2] = 'N';
769 		if (net != NONETWORK)
770 		{
771 			addtointlistobj(db_networklist, (INTBIG)net, FALSE);
772 			addtointlistobj(db_networklist, (INTBIG)NONETWORK, FALSE);
773 			return((NETWORK **)getintlistobj(db_networklist, &count));
774 		}
775 	}
776 
777 	/* check for prefix "v(" and "l(" HSPICE prefixes (see also "simwindow.c:sim_window_findtrace()")*/
778 	if ((name[0] == 'l' || name[0] == 'v') && name[1] == '(')
779 	{
780 		for(pt = &name[2]; pt != 0; pt++)
781 			if (*pt == ')') break;
782 		save = *pt;   *pt = 0;
783 		net = getnetwork(&name[2], np);
784 		*pt = save;
785 		if (net != NONETWORK)
786 		{
787 			addtointlistobj(db_networklist, (INTBIG)net, FALSE);
788 			addtointlistobj(db_networklist, (INTBIG)NONETWORK, FALSE);
789 			return((NETWORK **)getintlistobj(db_networklist, &count));
790 		}
791 	}
792 
793 	/* if there are underscores, see if they match original names */
794 	for(pt = name; *pt != 0; pt++) if (*pt == '_') break;
795 	if (*pt != 0)
796 	{
797 		len = estrlen(name);
798 		for(net = np->firstnetwork; net != NONETWORK; net = net->nextnetwork)
799 		{
800 			for(k=0; k<net->namecount; k++)
801 			{
802 				pt = networkname(net, k);
803 				l = estrlen(pt);
804 				if (l == len)
805 				{
806 					for(i=0; i<len; i++)
807 					{
808 						c1 = tolower(name[i]);
809 						c2 = tolower(pt[i]);
810 						if (c1 == c2) continue;
811 						if (c1 == '_' && !isalnum(c2)) continue;
812 						break;
813 					}
814 					if (i >= len)
815 					{
816 						addtointlistobj(db_networklist, (INTBIG)net, FALSE);
817 						addtointlistobj(db_networklist, (INTBIG)NONETWORK, FALSE);
818 						return((NETWORK **)getintlistobj(db_networklist, &count));
819 					}
820 				}
821 			}
822 		}
823 	}
824 
825 	/* if this is a simple name, let it match anything indexed from that name */
826 	for(pt = name; *pt != 0; pt++) if (*pt == '[') break;
827 	if (*pt == 0)
828 	{
829 		l = estrlen(name);
830 		for(net = np->firstnetwork; net != NONETWORK; net = net->nextnetwork)
831 		{
832 			for(k=0; k<net->namecount; k++)
833 			{
834 				pt = networkname(net, k);
835 				if (namesamen(pt, name, l) == 0 && pt[l] == '[')
836 					addtointlistobj(db_networklist, (INTBIG)net, TRUE);
837 			}
838 		}
839 	}
840 
841 	/* if there are slashes in the name, see if the end part matches */
842 	lastslash = 0;
843 	for(pt = name; *pt != 0; pt++) if (*pt == '/') lastslash = pt;
844 	if (lastslash != 0)
845 	{
846 		net = getnetwork(&lastslash[1], np);
847 		if (net != NONETWORK)
848 		{
849 			addtointlistobj(db_networklist, (INTBIG)net, FALSE);
850 			addtointlistobj(db_networklist, (INTBIG)NONETWORK, FALSE);
851 			return((NETWORK **)getintlistobj(db_networklist, &count));
852 		}
853 	}
854 
855 	addtointlistobj(db_networklist, (INTBIG)NONETWORK, FALSE);
856 	return((NETWORK **)getintlistobj(db_networklist, &count));
857 }
858 
859 /*
860  * routine to determine which arc prototype is referred to by "line"
861  * and return that arcproto.  The routine returns NOARCPROTO if the prototype
862  * cannot be determined.
863  */
getarcproto(CHAR * initline)864 ARCPROTO *getarcproto(CHAR *initline)
865 {
866 	REGISTER ARCPROTO *ap;
867 	REGISTER TECHNOLOGY *tech, *t;
868 	REGISTER CHAR *pt, *line;
869 	REGISTER void *infstr;
870 
871 	/* make a copy of the argument so that it can be modified */
872 	infstr = initinfstr();
873 	addstringtoinfstr(infstr, initline);
874 	line = returninfstr(infstr);
875 
876 	tech = el_curtech;
877 	for(pt = line; *pt != 0; pt++) if (*pt == ':') break;
878 	if (*pt != ':') pt = line; else
879 	{
880 		*pt = 0;
881 		t = gettechnology(line);
882 		if (t != NOTECHNOLOGY) tech = t;
883 		*pt++ = ':';
884 	}
885 	for(ap = tech->firstarcproto; ap != NOARCPROTO; ap = ap->nextarcproto)
886 		if (namesame(pt, ap->protoname) == 0) return(ap);
887 	return(NOARCPROTO);
888 }
889 
890 /*
891  * routine to find portproto "portname" on cell "cell".  Returns NOPORTPROTO
892  * if it cannot be found
893  */
getportproto(NODEPROTO * cell,CHAR * portname)894 PORTPROTO *getportproto(NODEPROTO *cell, CHAR *portname)
895 {
896 	REGISTER PORTPROTO *pp;
897 	REGISTER INTBIG i, j;
898 
899 	if (cell->portprotohashtablesize > 0)
900 	{
901 		i = db_namehash(portname) % cell->portprotohashtablesize;
902 		for(j=1; j<=cell->portprotohashtablesize; j += 2)
903 		{
904 			pp = cell->portprotohashtable[i];
905 			if (pp == NOPORTPROTO) break;
906 			if (namesame(portname, pp->protoname) == 0) return(pp);
907 			i += j;
908 			if (i >= cell->portprotohashtablesize) i -= cell->portprotohashtablesize;
909 		}
910 	} else
911 	{
912 		for (pp = cell->firstportproto; pp != NOPORTPROTO; pp = pp->nextportproto)
913 			if (namesame(portname, pp->protoname) == 0) return(pp);
914 	}
915 	return(NOPORTPROTO);
916 }
917 
918 /*
919  * routine to find library "libname".  Returns NOLIBRARY if it cannot be found
920  */
getlibrary(CHAR * libname)921 LIBRARY *getlibrary(CHAR *libname)
922 {
923 	REGISTER LIBRARY *lib;
924 
925 	for(lib = el_curlib; lib != NOLIBRARY; lib = lib->nextlibrary)
926 		if (namesame(libname, lib->libname) == 0) return(lib);
927 	return(NOLIBRARY);
928 }
929 
930 /*
931  * routine to find tool "toolname".  Returns NOTOOL if it cannot be found
932  */
gettool(CHAR * toolname)933 TOOL *gettool(CHAR *toolname)
934 {
935 	REGISTER INTBIG i;
936 
937 	for(i=0; i<el_maxtools; i++)
938 		if (namesame(toolname, el_tools[i].toolname) == 0) return(&el_tools[i]);
939 	return(NOTOOL);
940 }
941 
942 static struct
943 {
944 	CHAR  *name;
945 	CHAR  *symbol;
946 	INTBIG value;
947 } db_colors[] =
948 {
949 	{N_("none"),           x_("ALLOFF"),  ALLOFF},
950 	{N_("transparent-1"),  x_("COLORT1"), COLORT1},
951 	{N_("transparent-2"),  x_("COLORT2"), COLORT2},
952 	{N_("transparent-3"),  x_("COLORT3"), COLORT3},
953 	{N_("transparent-4"),  x_("COLORT4"), COLORT4},
954 	{N_("transparent-5"),  x_("COLORT5"), COLORT5},
955 	{N_("overlappable-1"), x_("COLORT1"), COLORT1},
956 	{N_("overlappable-2"), x_("COLORT2"), COLORT2},
957 	{N_("overlappable-3"), x_("COLORT3"), COLORT3},
958 	{N_("overlappable-4"), x_("COLORT4"), COLORT4},
959 	{N_("overlappable-5"), x_("COLORT5"), COLORT5},
960 	{N_("white"),          x_("WHITE"),   WHITE},
961 	{N_("black"),          x_("BLACK"),   BLACK},
962 	{N_("red"),            x_("RED"),     RED},
963 	{N_("blue"),           x_("BLUE"),    BLUE},
964 	{N_("green"),          x_("GREEN"),   GREEN},
965 	{N_("cyan"),           x_("CYAN"),    CYAN},
966 	{N_("magenta"),        x_("MAGENTA"), MAGENTA},
967 	{N_("yellow"),         x_("YELLOW"),  YELLOW},
968 	{N_("gray"),           x_("GRAY"),    GRAY},
969 	{N_("orange"),         x_("ORANGE"),  ORANGE},
970 	{N_("purple"),         x_("PURPLE"),  PURPLE},
971 	{N_("brown"),          x_("BROWN"),   BROWN},
972 	{N_("light-gray"),     x_("LGRAY"),   LGRAY},
973 	{N_("dark-gray"),      x_("DGRAY"),   DGRAY},
974 	{N_("light-red"),      x_("LRED"),    LRED},
975 	{N_("dark-red"),       x_("DRED"),    DRED},
976 	{N_("light-green"),    x_("LGREEN"),  LGREEN},
977 	{N_("dark-green"),     x_("DGREEN"),  DGREEN},
978 	{N_("light-blue"),     x_("LBLUE"),   LBLUE},
979 	{N_("dark-blue"),      x_("DBLUE"),   DBLUE},
980 	{NULL, NULL, 0}
981 };
982 
983 /*
984  * Routine to convert the color name "colorname" to a color.  Returns negative on error.
985  */
getecolor(CHAR * colorname)986 INTBIG getecolor(CHAR *colorname)
987 {
988 	REGISTER INTBIG i;
989 	REGISTER CHAR *truecolorname;
990 
991 	truecolorname = TRANSLATE(colorname);
992 	for(i=0; db_colors[i].name != 0; i++)
993 		if (namesame(truecolorname, TRANSLATE(db_colors[i].name)) == 0)
994 			return(db_colors[i].value);
995 	return(-1);
996 }
997 
998 /*
999  * Routine to convert color "color" to a full name (i.e. "light-gray") in "colorname" and a
1000  * symbol name (i.e. "LGRAY") in "colorsymbol".  Returns true if the color is unknown.
1001  */
ecolorname(INTBIG color,CHAR ** colorname,CHAR ** colorsymbol)1002 BOOLEAN ecolorname(INTBIG color, CHAR **colorname, CHAR **colorsymbol)
1003 {
1004 	REGISTER INTBIG i;
1005 
1006 	for(i=0; db_colors[i].name != 0; i++)
1007 		if (db_colors[i].value == color)
1008 	{
1009 		*colorname = TRANSLATE(db_colors[i].name);
1010 		*colorsymbol = db_colors[i].symbol;
1011 		return(FALSE);
1012 	}
1013 	return(TRUE);
1014 }
1015 
1016 /*
1017  * routine to parse a set of commands in "list" against the keyword in
1018  * "keyword" and return the index in the list of the keyword.  A return of
1019  * -1 indicates failure to parse the command and an error message will be
1020  * issued if "noise" is nonzero.
1021  */
parse(CHAR * keyword,COMCOMP * list,BOOLEAN noise)1022 INTBIG parse(CHAR *keyword, COMCOMP *list, BOOLEAN noise)
1023 {
1024 	REGISTER INTBIG i, j;
1025 	BOOLEAN (*toplist)(CHAR**);
1026 	INTBIG w, bst;
1027 	REGISTER CHAR *pp, *(*nextinlist)(void);
1028 	REGISTER void *infstr;
1029 
1030 	us_pathiskey = list->ifmatch;
1031 	toplist = list->toplist;
1032 	nextinlist = list->nextcomcomp;
1033 
1034 	(void)(*toplist)(&keyword);
1035 	for(i=0, w=0; (pp = (*nextinlist)()) != 0; w++)
1036 	{
1037 		j = stringmatch(pp, keyword);
1038 		if (j == -2) return(w);
1039 		if (j < 0) continue;
1040 		if (j > i)
1041 		{
1042 			i = j;   bst = w;
1043 		} else if (j == i) bst = -1;
1044 	}
1045 
1046 	/* if nothing found, give an error */
1047 	if (i == 0)
1048 	{
1049 		if (noise != 0) ttyputerr(_("Unknown command: %s"), keyword);
1050 		return(-1);
1051 	}
1052 
1053 	/* if there is unambiguous match, return it */
1054 	if (bst >= 0) return(bst);
1055 
1056 	/* print ambiguities */
1057 	if (noise != 0)
1058 	{
1059 		infstr = initinfstr();
1060 		(void)(*toplist)(&keyword);
1061 		for( ; (pp = (*nextinlist)()) != 0; )
1062 		{
1063 			if ((j = stringmatch(pp, keyword)) < 0) continue;
1064 			if (j < i) continue;
1065 			addtoinfstr(infstr, ' ');
1066 			addstringtoinfstr(infstr, pp);
1067 		}
1068 		ttyputerr(_("%s ambiguous:%s"), keyword, returninfstr(infstr));
1069 	}
1070 	return(-1);
1071 }
1072 
1073 /*
1074  * routine to report the amount of match that string "keyword" and "input"
1075  * have in common.  Returns the number of characters that match.  Returns -2
1076  * if they are equal, -1 if there are extra characters at the end of "input"
1077  * which make the match erroneous.  Ignores case distinction.
1078  */
stringmatch(CHAR * keyword,CHAR * input)1079 INTBIG stringmatch(CHAR *keyword, CHAR *input)
1080 {
1081 	REGISTER INTBIG j;
1082 	REGISTER CHAR c, d;
1083 
1084 	for(j=0; (c = input[j]) != 0; j++)
1085 	{
1086 		if (isupper(c)) c = tolower(c);
1087 		d = keyword[j];  if (isupper(d)) d = tolower(d);
1088 		if (c != d) break;
1089 	}
1090 	if (c != 0) return(-1);
1091 	if (keyword[j] == 0) return(-2);
1092 	return(j);
1093 }
1094 
1095 /************************* COMMAND COMPLETION CODE *************************/
1096 
1097 static INTBIG  db_filestrlen;
1098 static CHAR    db_filekey[100];
1099 static CHAR    db_directorypath[256];
1100 static CHAR    db_fileextension[10];
1101 static INTBIG  db_filecount, db_filetotal;
1102 static CHAR  **db_filesindir;
1103 
requiredextension(CHAR * extension)1104 void requiredextension(CHAR *extension)
1105 {
1106 	(void)estrcpy(db_fileextension, extension);
1107 }
1108 
topoffile(CHAR ** a)1109 BOOLEAN topoffile(CHAR **a)
1110 {
1111 	db_fileextension[0] = 0;
1112 	return(db_beginsearch(a));
1113 }
1114 
topoflibfile(CHAR ** a)1115 BOOLEAN topoflibfile(CHAR **a)
1116 {
1117 	(void)estrcpy(db_fileextension, x_(".elib"));
1118 	return(db_beginsearch(a));
1119 }
1120 
db_beginsearch(CHAR ** a)1121 BOOLEAN db_beginsearch(CHAR **a)
1122 {
1123 	INTBIG i;
1124 	static CHAR file[256];
1125 	CHAR *pt;
1126 
1127 	/* build the full file name */
1128 	(void)estrcpy(file, truepath(*a));
1129 	*a = file;
1130 
1131 	/* search for directory specifications */
1132 	for(i=estrlen(file)-1; i > 0; i--) if (file[i] == DIRSEP) break;
1133 	if (file[i] == DIRSEP) i++;
1134 	(void)estrcpy(db_filekey, &file[i]);
1135 	db_filestrlen = estrlen(db_filekey);
1136 	file[i] = 0;
1137 	estrcpy(db_directorypath, file);
1138 	db_filecount = 0;
1139 	db_filetotal = filesindirectory(file, &db_filesindir);
1140 
1141 	/* advance pointer to after the directory separator */
1142 	pt = *a;
1143 	for(i=estrlen(pt)-1; i > 0; i--) if (pt[i] == DIRSEP) break;
1144 	if (i > 0) *a = &pt[i+1];
1145 	return(TRUE);
1146 }
1147 
nextfile(void)1148 CHAR *nextfile(void)
1149 {
1150 	CHAR *pt;
1151 	static CHAR testfile[256];
1152 
1153 	for(;;)
1154 	{
1155 		if (db_filecount >= db_filetotal) break;
1156 		pt = db_filesindir[db_filecount];
1157 		db_filecount++;
1158 
1159 		/* see if the file is valid */
1160 		if (pt[0] == '.') continue;
1161 		if (estrncmp(db_filekey, pt, db_filestrlen) != 0) continue;
1162 		(void)estrcpy(testfile, db_directorypath);
1163 		(void)estrcat(testfile, pt);
1164 		if (fileexistence(testfile) == 2)
1165 		{
1166 			estrcpy(testfile, pt);
1167 			estrcat(testfile, DIRSEPSTR);
1168 			return(testfile);
1169 		}
1170 		if (db_fileextension[0] != 0)
1171 		{
1172 			if (estrcmp(&pt[estrlen(pt)-estrlen(db_fileextension)], db_fileextension) != 0)
1173 				continue;
1174 		}
1175 		return(pt);
1176 	}
1177 	return(0);
1178 }
1179 
1180 /*
1181  * routines to do command completion on technology names
1182  */
1183 static TECHNOLOGY *db_postechcomcomp;
topoftechs(CHAR ** c)1184 BOOLEAN topoftechs(CHAR **c) { Q_UNUSED( c ); db_postechcomcomp = el_technologies; return(TRUE); }
nexttechs(void)1185 CHAR *nexttechs(void)
1186 {
1187 	REGISTER CHAR *retname;
1188 
1189 	if (db_postechcomcomp == NOTECHNOLOGY) return(0);
1190 	retname = db_postechcomcomp->techname;
1191 	db_postechcomcomp = db_postechcomcomp->nexttechnology;
1192 	return(retname);
1193 }
1194 
1195 /*
1196  * routines to do command completion on view names
1197  */
1198 static VIEW *db_posviewcomcomp;
topofviews(CHAR ** c)1199 BOOLEAN topofviews(CHAR **c) { Q_UNUSED( c ); db_posviewcomcomp = el_views; return(TRUE); }
nextviews(void)1200 CHAR *nextviews(void)
1201 {
1202 	REGISTER CHAR *retname;
1203 
1204 	if (db_posviewcomcomp == NOVIEW) return(0);
1205 	retname = db_posviewcomcomp->viewname;
1206 	db_posviewcomcomp = db_posviewcomcomp->nextview;
1207 	return(retname);
1208 }
1209 
1210 /*
1211  * routines to do command completion on library names
1212  */
1213 static LIBRARY *db_poslibcomcomp;
topoflibs(CHAR ** c)1214 BOOLEAN topoflibs(CHAR **c)
1215 {
1216 	Q_UNUSED( c );
1217 	db_poslibcomcomp = el_curlib;
1218 	return(TRUE);
1219 }
nextlibs(void)1220 CHAR *nextlibs(void)
1221 {
1222 	REGISTER CHAR *retname;
1223 
1224 	for(;;)
1225 	{
1226 		if (db_poslibcomcomp == NOLIBRARY) return(0);
1227 		if ((db_poslibcomcomp->userbits&HIDDENLIBRARY) == 0)
1228 		{
1229 			retname = db_poslibcomcomp->libname;
1230 			db_poslibcomcomp = db_poslibcomcomp->nextlibrary;
1231 			break;
1232 		} else
1233 		{
1234 			db_poslibcomcomp = db_poslibcomcomp->nextlibrary;
1235 		}
1236 	}
1237 	return(retname);
1238 }
1239 
1240 /*
1241  * routines to do command completion on tool names
1242  */
1243 static INTBIG db_poscomcomp;
topoftools(CHAR ** c)1244 BOOLEAN topoftools(CHAR **c) { Q_UNUSED( c ); db_poscomcomp = 0; return(TRUE); }
nexttools(void)1245 CHAR *nexttools(void)
1246 {
1247 	if (db_poscomcomp >= el_maxtools) return(0);
1248 	return(el_tools[db_poscomcomp++].toolname);
1249 }
1250 
1251 /*
1252  * routines to do command completion on cell names
1253  */
1254 static NODEPROTO *db_posnodeprotos;
topofcells(CHAR ** c)1255 BOOLEAN topofcells(CHAR **c)
1256 {
1257 	REGISTER CHAR *pt;
1258 	REGISTER LIBRARY *lib;
1259 
1260 	/* by default, assume the current library */
1261 	db_posnodeprotos = el_curlib->firstnodeproto;
1262 
1263 	/* see if a library specification was given */
1264 	for(pt = *c; *pt != 0; pt++) if (*pt == ':') break;
1265 	if (*pt == ':')
1266 	{
1267 		*pt = 0;
1268 		lib = getlibrary(*c);
1269 		*pt++ = ':';
1270 		if (lib != NOLIBRARY)
1271 		{
1272 			*c = pt;
1273 			db_posnodeprotos = lib->firstnodeproto;
1274 		}
1275 	}
1276 	return(TRUE);
1277 }
nextcells(void)1278 CHAR *nextcells(void)
1279 {
1280 	REGISTER CHAR *ret;
1281 
1282 	if (db_posnodeprotos != NONODEPROTO)
1283 	{
1284 		ret = describenodeproto(db_posnodeprotos);
1285 		db_posnodeprotos = db_posnodeprotos->nextnodeproto;
1286 		return(ret);
1287 	}
1288 	return(0);
1289 }
1290 
1291 /*
1292  * routines to do command completion on arc names
1293  */
1294 static ARCPROTO *db_posarcs;
topofarcs(CHAR ** c)1295 BOOLEAN topofarcs(CHAR **c)
1296 {
1297 	REGISTER CHAR *pt;
1298 	REGISTER TECHNOLOGY *t;
1299 
1300 	/* by default, assume the current technology */
1301 	db_posarcs = el_curtech->firstarcproto;
1302 
1303 	/* see if a technology specification was given */
1304 	for(pt = *c; *pt != 0; pt++) if (*pt == ':') break;
1305 	if (*pt == ':')
1306 	{
1307 		*pt = 0;
1308 		t = gettechnology(*c);
1309 		*pt++ = ':';
1310 		if (t != NOTECHNOLOGY)
1311 		{
1312 			*c = pt;
1313 			db_posarcs = t->firstarcproto;
1314 		}
1315 	}
1316 	return(TRUE);
1317 }
nextarcs(void)1318 CHAR *nextarcs(void)
1319 {
1320 	REGISTER CHAR *ret;
1321 
1322 	if (db_posarcs != NOARCPROTO)
1323 	{
1324 		ret = describearcproto(db_posarcs);
1325 		db_posarcs = db_posarcs->nextarcproto;
1326 		return(ret);
1327 	}
1328 	return(0);
1329 }
1330 
1331 /*
1332  * routines to do command completion on network names
1333  */
1334 static NETWORK *db_posnets;
1335 static INTBIG db_posinnet;
1336 
topofnets(CHAR ** c)1337 BOOLEAN topofnets(CHAR **c)
1338 {
1339 	REGISTER NODEPROTO *np;
1340 	Q_UNUSED( c );
1341 
1342 	db_posnets = NONETWORK;
1343 	np = getcurcell();
1344 	if (np == NONODEPROTO) return(FALSE);
1345 	db_posnets = np->firstnetwork;
1346 	if (db_posnets == NONETWORK) return(FALSE);
1347 	db_posinnet = 0;
1348 	return(TRUE);
1349 }
nextnets(void)1350 CHAR *nextnets(void)
1351 {
1352 	REGISTER CHAR *ret;
1353 
1354 	for(;;)
1355 	{
1356 		if (db_posnets == NONETWORK) return(0);
1357 		if (db_posinnet >= db_posnets->namecount)
1358 		{
1359 			db_posnets = db_posnets->nextnetwork;
1360 			if (db_posnets == NONETWORK) return(0);
1361 			db_posinnet = 0;
1362 		} else
1363 		{
1364 			ret = networkname(db_posnets, db_posinnet);
1365 			db_posinnet++;
1366 			break;
1367 		}
1368 	}
1369 	return(ret);
1370 }
1371 
1372 /************************* OUTPUT PREPARATION *************************/
1373 
1374 /*
1375  * routine to return the full name of node "ni", including its local name.
1376  * Technology considerations are ignored.
1377  */
ntdescribenodeinst(NODEINST * ni)1378 CHAR *ntdescribenodeinst(NODEINST *ni)
1379 {
1380 	REGISTER TECHNOLOGY *curtech;
1381 	REGISTER CHAR *ret;
1382 
1383 	if (ni == NONODEINST) return(x_("***NONODEINST***"));
1384 
1385 	if (ni->proto->primindex == 0) return(describenodeinst(ni));
1386 	curtech = el_curtech;
1387 	el_curtech = ni->proto->tech;
1388 	ret = describenodeinst(ni);
1389 	el_curtech = curtech;
1390 	return(ret);
1391 }
1392 
1393 /* routine to return the name of nodeinst "ni" */
describenodeinst(NODEINST * ni)1394 CHAR *describenodeinst(NODEINST *ni)
1395 {
1396 	REGISTER CHAR *name, *protoname;
1397 	REGISTER VARIABLE *var;
1398 	REGISTER INTBIG len;
1399 
1400 	if (ni == NONODEINST) return(x_("***NONODEINST***"));
1401 
1402 	/* see if there is a local name on the node */
1403 	protoname = describenodeproto(ni->proto);
1404 	var = getvalkey((INTBIG)ni, VNODEINST, VSTRING, el_node_name_key);
1405 	if (var == NOVARIABLE) return(protoname);
1406 
1407 	/* code cannot be called by multiple procesors: uses globals */
1408 	NOT_REENTRANT;
1409 
1410 	/* get output buffer */
1411 	db_descninodeswitch++;
1412 	if (db_descninodeswitch >= NUMDESCRIPTIONBUFS) db_descninodeswitch = 0;
1413 
1414 	/* make sure buffer has enough room */
1415 	len = estrlen(protoname) + estrlen((CHAR *)var->addr) + 3;
1416 	if (len > db_descnioutputsize[db_descninodeswitch])
1417 	{
1418 		if (db_descnioutputsize[db_descninodeswitch] != 0)
1419 			efree(db_descnioutput[db_descninodeswitch]);
1420 		db_descnioutputsize[db_descninodeswitch] = 0;
1421 		db_descnioutput[db_descninodeswitch] = (CHAR *)emalloc(len * SIZEOFCHAR, db_cluster);
1422 		if (db_descnioutput[db_descninodeswitch] == 0) return(x_(""));
1423 		db_descnioutputsize[db_descninodeswitch] = len;
1424 	}
1425 
1426 	/* store the name */
1427 	name = db_descnioutput[db_descninodeswitch];
1428 	(void)estrcpy(name, protoname);
1429 	(void)estrcat(name, x_("["));
1430 	(void)estrcat(name, (CHAR *)var->addr);
1431 	(void)estrcat(name, x_("]"));
1432 	return(name);
1433 }
1434 
1435 /* routine to return the name of arcinst "ai" */
describearcinst(ARCINST * ai)1436 CHAR *describearcinst(ARCINST *ai)
1437 {
1438 	REGISTER CHAR *name, *pname;
1439 	REGISTER VARIABLE *var;
1440 	REGISTER INTBIG len;
1441 
1442 	if (ai == NOARCINST) return(x_("***NOARCINST***"));
1443 
1444 	/* get local arc name */
1445 	pname = describearcproto(ai->proto);
1446 	var = getvalkey((INTBIG)ai, VARCINST, VSTRING, el_arc_name_key);
1447 	if (var == NOVARIABLE) return(pname);
1448 
1449 	/* code cannot be called by multiple procesors: uses globals */
1450 	NOT_REENTRANT;
1451 
1452 	/* get output buffer */
1453 	db_descaiarcswitch++;
1454 	if (db_descaiarcswitch >= NUMDESCRIPTIONBUFS) db_descaiarcswitch = 0;
1455 
1456 	/* make sure buffer has enough room */
1457 	len = estrlen(pname) + estrlen((CHAR *)var->addr) + 3;
1458 	if (len > db_descaioutputsize[db_descaiarcswitch])
1459 	{
1460 		if (db_descaioutputsize[db_descaiarcswitch] != 0)
1461 			efree(db_descaioutput[db_descaiarcswitch]);
1462 		db_descaioutputsize[db_descaiarcswitch] = 0;
1463 		db_descaioutput[db_descaiarcswitch] = (CHAR *)emalloc(len * SIZEOFCHAR, db_cluster);
1464 		if (db_descaioutput[db_descaiarcswitch] == 0) return(x_(""));
1465 		db_descaioutputsize[db_descaiarcswitch] = len;
1466 	}
1467 
1468 	name = db_descaioutput[db_descaiarcswitch];
1469 	(void)estrcpy(name, pname);
1470 	(void)estrcat(name, x_("["));
1471 	(void)estrcat(name, (CHAR *)var->addr);
1472 	(void)estrcat(name, x_("]"));
1473 	return(name);
1474 }
1475 
1476 /*
1477  * routine to return the full name of cell "np", including its view type
1478  * (if any) and version (if not most recent).  Library considerations are
1479  * ignored.
1480  */
nldescribenodeproto(NODEPROTO * np)1481 CHAR *nldescribenodeproto(NODEPROTO *np)
1482 {
1483 	REGISTER LIBRARY *curlib;
1484 	REGISTER CHAR *ret;
1485 
1486 	if (np == NONODEPROTO) return(x_("***NONODEPROTO***"));
1487 
1488 	if (np->primindex != 0) return(np->protoname);
1489 
1490 	/* code cannot be called by multiple procesors: uses globals */
1491 	NOT_REENTRANT;
1492 
1493 	curlib = el_curlib;
1494 	el_curlib = np->lib;
1495 	ret = describenodeproto(np);
1496 	el_curlib = curlib;
1497 	return(ret);
1498 }
1499 
1500 /*
1501  * routine to return the full name of cell "np", including its library name
1502  * (if different from the current), view type (if any), and version (if not
1503  * most recent).
1504  */
describenodeproto(NODEPROTO * np)1505 CHAR *describenodeproto(NODEPROTO *np)
1506 {
1507 	CHAR line[50];
1508 	REGISTER CHAR *name;
1509 	REGISTER INTBIG len;
1510 
1511 	if (np == NONODEPROTO) return(x_("***NONODEPROTO***"));
1512 
1513 	/* simple tests for direct name use */
1514 	if (np->primindex != 0)
1515 	{
1516 		/* if a primitive in the current technology, simply use name */
1517 		if (np->tech == el_curtech) return(np->protoname);
1518 	} else
1519 	{
1520 		/* if view unknown, version recent, library current, simply use name */
1521 		if (*np->cellview->sviewname == 0 && np->newestversion == np && np->lib == el_curlib)
1522 			return(np->protoname);
1523 	}
1524 
1525 	/* code cannot be called by multiple procesors: uses globals */
1526 	NOT_REENTRANT;
1527 
1528 	/* get output buffer */
1529 	db_descnpnodeswitch++;
1530 	if (db_descnpnodeswitch >= NUMDESCRIPTIONBUFS) db_descnpnodeswitch = 0;
1531 
1532 	if (np->primindex != 0)
1533 	{
1534 		len = estrlen(np->protoname) + estrlen(np->tech->techname) + 2;
1535 	} else
1536 	{
1537 		/* compute size of buffer */
1538 		if (np->protoname == 0) return(x_("***BOGUS***"));
1539 		len = estrlen(np->protoname) + 1;
1540 		if (np->lib != el_curlib) len += estrlen(np->lib->libname) + 1;
1541 		if (np->newestversion != np)
1542 		{
1543 			(void)esnprintf(line, 50, x_(";%ld"), np->version);
1544 			len += estrlen(line);
1545 		}
1546 		if (*np->cellview->sviewname != 0) len += estrlen(np->cellview->sviewname) + 2;
1547 	}
1548 
1549 	/* make sure buffer has enough room */
1550 	if (len > db_descnpoutputsize[db_descnpnodeswitch])
1551 	{
1552 		if (db_descnpoutputsize[db_descnpnodeswitch] != 0)
1553 			efree(db_descnpoutput[db_descnpnodeswitch]);
1554 		db_descnpoutputsize[db_descnpnodeswitch] = 0;
1555 		db_descnpoutput[db_descnpnodeswitch] = (CHAR *)emalloc(len * SIZEOFCHAR, db_cluster);
1556 		if (db_descnpoutput[db_descnpnodeswitch] == 0) return(x_(""));
1557 		db_descnpoutputsize[db_descnpnodeswitch] = len;
1558 	}
1559 
1560 	/* construct complete name */
1561 	name = db_descnpoutput[db_descnpnodeswitch];
1562 	if (np->primindex != 0)
1563 	{
1564 		(void)estrcpy(name, np->tech->techname);
1565 		(void)estrcat(name, x_(":"));
1566 		(void)estrcat(name, np->protoname);
1567 	} else
1568 	{
1569 		if (np->lib != el_curlib)
1570 		{
1571 			(void)estrcpy(name, np->lib->libname);
1572 			(void)estrcat(name, x_(":"));
1573 			(void)estrcat(name, np->protoname);
1574 		} else (void)estrcpy(name, np->protoname);
1575 		if (np->newestversion != np) (void)estrcat(name, line);
1576 		if (*np->cellview->sviewname != 0)
1577 		{
1578 			(void)estrcat(name, x_("{"));
1579 			(void)estrcat(name, np->cellview->sviewname);
1580 			(void)estrcat(name, x_("}"));
1581 		}
1582 	}
1583 	return(name);
1584 }
1585 
1586 /* routine to return the name of arcproto "ap" */
describearcproto(ARCPROTO * ap)1587 CHAR *describearcproto(ARCPROTO *ap)
1588 {
1589 	REGISTER CHAR *name;
1590 	REGISTER INTBIG len;
1591 
1592 	if (ap == NOARCPROTO) return(x_("***NOARCPROTO***"));
1593 
1594 	if (ap->tech == el_curtech) return(ap->protoname);
1595 
1596 	/* code cannot be called by multiple procesors: uses globals */
1597 	NOT_REENTRANT;
1598 
1599 	/* get output buffer */
1600 	db_descaparcswitch++;
1601 	if (db_descaparcswitch >= NUMDESCRIPTIONBUFS) db_descaparcswitch = 0;
1602 
1603 	/* make sure buffer has enough room */
1604 	len = estrlen(ap->tech->techname) + estrlen(ap->protoname) + 2;
1605 	if (len > db_descapoutputsize[db_descaparcswitch])
1606 	{
1607 		if (db_descapoutputsize[db_descaparcswitch] != 0)
1608 			efree(db_descapoutput[db_descaparcswitch]);
1609 		db_descapoutputsize[db_descaparcswitch] = 0;
1610 		db_descapoutput[db_descaparcswitch] = (CHAR *)emalloc(len * SIZEOFCHAR, db_cluster);
1611 		if (db_descapoutput[db_descaparcswitch] == 0) return(x_(""));
1612 		db_descapoutputsize[db_descaparcswitch] = len;
1613 	}
1614 
1615 	name = db_descapoutput[db_descaparcswitch];
1616 	(void)estrcpy(name, ap->tech->techname);
1617 	(void)estrcat(name, x_(":"));
1618 	(void)estrcat(name, ap->protoname);
1619 	return(name);
1620 }
1621 
1622 /* routine to return the name of the object whose geometry module is "geom" */
geomname(GEOM * geom)1623 CHAR *geomname(GEOM *geom)
1624 {
1625 	if (geom == NOGEOM) return(x_("***NOGEOM***"));
1626 	if (geom->entryisnode) return(describenodeinst(geom->entryaddr.ni));
1627 	return(describearcinst(geom->entryaddr.ai));
1628 }
1629 
1630 /*
1631  * routine to convert network "net" into a string
1632  */
describenetwork(NETWORK * net)1633 CHAR *describenetwork(NETWORK *net)
1634 {
1635 	REGISTER NODEPROTO *np;
1636 	static CHAR gennetname[50];
1637 	REGISTER INTBIG i, namecount;
1638 	REGISTER void *infstr;
1639 
1640 	if (net == NONETWORK) return(x_("***NONETWORK***"));
1641 
1642 	if (net->globalnet >= 0)
1643 	{
1644 		infstr = initinfstr();
1645 		addstringtoinfstr(infstr, _("Global"));
1646 		addtoinfstr(infstr, '-');
1647 		if (net->globalnet == GLOBALNETGROUND) addstringtoinfstr(infstr, _("Ground")); else
1648 		if (net->globalnet == GLOBALNETPOWER) addstringtoinfstr(infstr, _("Power")); else
1649 		{
1650 			np = net->parent;
1651 			if (net->globalnet >= np->globalnetcount)
1652 				addstringtoinfstr(infstr, _("UNKNOWN")); else
1653 					addstringtoinfstr(infstr, np->globalnetnames[net->globalnet]);
1654 		}
1655 		return(returninfstr(infstr));
1656 	}
1657 	if (net->namecount == 0)
1658 	{
1659 		esnprintf(gennetname, 50, _("UNNAMED%ld"), (INTBIG)net);
1660 		return(gennetname);
1661 	}
1662 	if (net->namecount == 1) return(networkname(net, 0));
1663 
1664 	infstr = initinfstr();
1665 	namecount = net->namecount;
1666 	if (namecount > 10) namecount = 10;
1667 	for(i=0; i<net->namecount; i++)
1668 	{
1669 		if (i != 0) addtoinfstr(infstr, '/');
1670 		addstringtoinfstr(infstr, networkname(net, i));
1671 	}
1672 	if (net->namecount > namecount)
1673 		addstringtoinfstr(infstr, x_("/..."));
1674 	return(returninfstr(infstr));
1675 }
1676 
describeportbits(INTBIG userbits)1677 CHAR *describeportbits(INTBIG userbits)
1678 {
1679 	switch (userbits&STATEBITS)
1680 	{
1681 		case INPORT:      return(_("Input"));
1682 		case OUTPORT:     return(_("Output"));
1683 		case BIDIRPORT:   return(_("Bidirectional"));
1684 		case PWRPORT:     return(_("Power"));
1685 		case GNDPORT:     return(_("Ground"));
1686 		case CLKPORT:     return(_("Clock"));
1687 		case C1PORT:      return(_("Clock Phase 1"));
1688 		case C2PORT:      return(_("Clock Phase 2"));
1689 		case C3PORT:      return(_("Clock Phase 3"));
1690 		case C4PORT:      return(_("Clock Phase 4"));
1691 		case C5PORT:      return(_("Clock Phase 5"));
1692 		case C6PORT:      return(_("Clock Phase 6"));
1693 		case REFOUTPORT:  return(_("Reference Output"));
1694 		case REFINPORT:   return(_("Reference Input"));
1695 		case REFBASEPORT: return(_("Reference Base"));
1696 	}
1697 	return(x_("Unknown"));
1698 }
1699 
1700 /*
1701  * routine to name the variable at "addr" of type "type".  It is assumed
1702  * to be an object that can hold other variables
1703  */
describeobject(INTBIG addr,INTBIG type)1704 CHAR *describeobject(INTBIG addr, INTBIG type)
1705 {
1706 	REGISTER NODEINST *ni;
1707 	REGISTER NODEPROTO *np;
1708 	REGISTER PORTARCINST *pi;
1709 	REGISTER PORTEXPINST *pe;
1710 	REGISTER PORTPROTO *pp;
1711 	REGISTER ARCINST *ai;
1712 	REGISTER ARCPROTO *ap;
1713 	REGISTER GEOM *geom;
1714 	REGISTER LIBRARY *lib;
1715 	REGISTER TECHNOLOGY *tech;
1716 	REGISTER TOOL *tool;
1717 	REGISTER RTNODE *rtn;
1718 	REGISTER VIEW *v;
1719 	REGISTER WINDOWPART *win;
1720 	REGISTER WINDOWFRAME *wf;
1721 	REGISTER GRAPHICS *gra;
1722 	REGISTER CONSTRAINT *con;
1723 	REGISTER POLYGON *poly;
1724 	REGISTER void *infstr;
1725 
1726 	infstr = initinfstr();
1727 	switch (type&VTYPE)
1728 	{
1729 		case VNODEINST:
1730 			ni = (NODEINST *)addr;
1731 			formatinfstr(infstr, x_("NodeInstance(%s)"), describenodeinst(ni));
1732 			break;
1733 		case VNODEPROTO:
1734 			np = (NODEPROTO *)addr;
1735 			if (np->primindex == 0) formatinfstr(infstr, x_("Cell(%s)"), describenodeproto(np)); else
1736 				formatinfstr(infstr, x_("Primitive(%s)"), describenodeproto(np));
1737 			break;
1738 		case VPORTARCINST:
1739 			pi = (PORTARCINST *)addr;
1740 			if (pi == NOPORTARCINST) addstringtoinfstr(infstr, x_("PortArcInstance(NULL)")); else
1741 				formatinfstr(infstr, x_("PortArcInstance(%ld)"), (INTBIG)pi);
1742 			break;
1743 		case VPORTEXPINST:
1744 			pe = (PORTEXPINST *)addr;
1745 			if (pe == NOPORTEXPINST) addstringtoinfstr(infstr, x_("PortExpInstance(NULL)")); else
1746 				formatinfstr(infstr, x_("PortExpInstance(%ld)"), (INTBIG)pe);
1747 			break;
1748 		case VPORTPROTO:
1749 			pp = (PORTPROTO *)addr;
1750 			if (pp == NOPORTPROTO) addstringtoinfstr(infstr, x_("PortPrototype(NULL)")); else
1751 				formatinfstr(infstr, x_("PortPrototype(%s)"), pp->protoname);
1752 			break;
1753 		case VARCINST:
1754 			ai = (ARCINST *)addr;
1755 			formatinfstr(infstr, x_("ArcInstance(%s)"), describearcinst(ai));
1756 			break;
1757 		case VARCPROTO:
1758 			ap = (ARCPROTO *)addr;
1759 			formatinfstr(infstr, x_("ArcPrototype(%s:%s)"), ap->tech->techname, ap->protoname);
1760 			break;
1761 		case VGEOM:
1762 			geom = (GEOM *)addr;
1763 			if (geom == NOGEOM) addstringtoinfstr(infstr, x_("Geom(NULL)")); else
1764 				formatinfstr(infstr, x_("Geom(%ld)"), (INTBIG)geom);
1765 			break;
1766 		case VLIBRARY:
1767 			lib = (LIBRARY *)addr;
1768 			if (lib == NOLIBRARY) addstringtoinfstr(infstr, x_("Library(NULL)")); else
1769 				formatinfstr(infstr, x_("Library(%s)"), lib->libname);
1770 			break;
1771 		case VTECHNOLOGY:
1772 			tech = (TECHNOLOGY *)addr;
1773 			if (tech == NOTECHNOLOGY) addstringtoinfstr(infstr, x_("Technology(NULL)")); else
1774 				formatinfstr(infstr, x_("Technology(%s)"), tech->techname);
1775 			break;
1776 		case VTOOL:
1777 			tool = (TOOL *)addr;
1778 			if (tool == NOTOOL) addstringtoinfstr(infstr, x_("Tool(NULL)")); else
1779 				formatinfstr(infstr, x_("Tool(%s)"), tool->toolname);
1780 			break;
1781 		case VRTNODE:
1782 			rtn = (RTNODE *)addr;
1783 			if (rtn == NORTNODE) addstringtoinfstr(infstr, x_("Rtnode(NULL)")); else
1784 				formatinfstr(infstr, x_("Rtnode(%ld)"), (INTBIG)rtn);
1785 			break;
1786 		case VVIEW:
1787 			v = (VIEW *)addr;
1788 			if (v == NOVIEW) addstringtoinfstr(infstr, x_("View(NULL)")); else
1789 				formatinfstr(infstr, x_("View(%s)"), v->viewname);
1790 			break;
1791 		case VWINDOWPART:
1792 			win = (WINDOWPART *)addr;
1793 			if (win != NOWINDOWPART) formatinfstr(infstr, x_("WindowPart(%s)"), win->location); else
1794 				formatinfstr(infstr, x_("WindowPart(%ld)"), (INTBIG)win);
1795 			break;
1796 		case VGRAPHICS:
1797 			gra = (GRAPHICS *)addr;
1798 			if (gra == NOGRAPHICS) addstringtoinfstr(infstr, x_("Graphics(NULL)")); else
1799 				formatinfstr(infstr, x_("Graphics(%ld)"), (INTBIG)gra);
1800 			break;
1801 		case VCONSTRAINT:
1802 			con = (CONSTRAINT *)addr;
1803 			if (con == NOCONSTRAINT) addstringtoinfstr(infstr, x_("Constraint(NULL)")); else
1804 				formatinfstr(infstr, x_("Constraint(%s)"), con->conname);
1805 			break;
1806 		case VWINDOWFRAME:
1807 			wf = (WINDOWFRAME *)addr;
1808 			formatinfstr(infstr, x_("WindowFrame(%ld)"), (INTBIG)wf);
1809 			break;
1810 		case VPOLYGON:
1811 			poly = (POLYGON *)addr;
1812 			formatinfstr(infstr, x_("Polygon(%ld)"), (INTBIG)poly);
1813 			break;
1814 		default:
1815 			addstringtoinfstr(infstr, x_("UNKNOWN(?)"));
1816 			break;
1817 	}
1818 	return(returninfstr(infstr));
1819 }
1820 
1821 /*
1822  * routine to convert a lambda number to ascii
1823  */
1824 #define OUTBUFS 12
latoa(INTBIG i,INTBIG lambda)1825 CHAR *latoa(INTBIG i, INTBIG lambda)
1826 {
1827 	static INTBIG latoaswitch = 0;
1828 	static CHAR output[OUTBUFS][20];
1829 	double scale, number;
1830 	REGISTER INTBIG len;
1831 	REGISTER CHAR *cur;
1832 
1833 	/* code cannot be called by multiple procesors: uses globals */
1834 	NOT_REENTRANT;
1835 
1836 	/* get output buffer */
1837 	cur = output[latoaswitch++];
1838 	if (latoaswitch >= OUTBUFS) latoaswitch = 0;
1839 
1840 	/* determine scaling */
1841 	if (lambda == 0) lambda = el_curlib->lambda[el_curtech->techindex];
1842 	scale = db_getcurrentscale(el_units&INTERNALUNITS, el_units&DISPLAYUNITS, lambda);
1843 	number = ((double)i) / scale;
1844 	(void)esnprintf(cur, 20, x_("%.4f"), number);
1845 
1846 	/* remove trailing zeros */
1847 	len = estrlen(cur);
1848 	while (cur[len-1] == '0') cur[--len] = 0;
1849 	if (cur[len-1] == '.') cur[--len] = 0;
1850 
1851 	switch (el_units&DISPLAYUNITS)
1852 	{
1853 		case DISPUNITINCH: estrcat(cur, x_("\""));  break;
1854 		case DISPUNITCM:   estrcat(cur, x_("cm"));  break;
1855 		case DISPUNITMM:   estrcat(cur, x_("mm"));  break;
1856 		case DISPUNITMIL:  estrcat(cur, x_("mil")); break;
1857 		case DISPUNITMIC:  estrcat(cur, x_("u"));   break;
1858 		case DISPUNITCMIC: estrcat(cur, x_("cu"));  break;
1859 		case DISPUNITNM:   estrcat(cur, x_("nm"));  break;
1860 	}
1861 	return(cur);
1862 }
1863 
1864 /*
1865  * routine to convert a fractional number to ascii
1866  */
1867 #define FRTOANUMBUFS 10
1868 
frtoa(INTBIG i)1869 CHAR *frtoa(INTBIG i)
1870 {
1871 	static INTBIG latoaswitch = 0;
1872 	static CHAR output[FRTOANUMBUFS][30];
1873 	REGISTER INTBIG fra;
1874 	CHAR temp[3];
1875 	REGISTER CHAR *pp, *cur, *start;
1876 
1877 	/* code cannot be called by multiple procesors: uses globals */
1878 	NOT_REENTRANT;
1879 
1880 	/* get output buffer */
1881 	start = cur = &output[latoaswitch++][0];
1882 	if (latoaswitch >= FRTOANUMBUFS) latoaswitch = 0;
1883 
1884 	/* handle negative values */
1885 	if (i < 0)
1886 	{
1887 		*cur++ = '-';
1888 		i = -i;
1889 	}
1890 
1891 	/* get the part to the left of the decimal point */
1892 	(void)esnprintf(cur, 30, x_("%ld"), i/WHOLE);
1893 
1894 	/* see if there is anything to the right of the decimal point */
1895 	if ((i%WHOLE) != 0)
1896 	{
1897 		(void)estrcat(cur, x_("."));
1898 		fra = i % WHOLE;
1899 		fra = (fra*100 + WHOLE/2) / WHOLE;
1900 		(void)esnprintf(temp, 3, x_("%02ld"), fra);
1901 		(void)estrcat(cur, temp);
1902 		pp = cur;   while (*pp != 0) pp++;
1903 		while (*--pp == '0') *pp = 0;
1904 		if (*pp == '.') *pp = 0;
1905 	}
1906 	return(start);
1907 }
1908 
1909 /*
1910  * routine to determine whether or not the string in "pp" is a number.
1911  * Returns true if it is.
1912  */
isanumber(CHAR * pp)1913 BOOLEAN isanumber(CHAR *pp)
1914 {
1915 	INTBIG xflag, founddigits;
1916 
1917 	/* ignore the minus sign */
1918 	if (*pp == '+' || *pp == '-') pp++;
1919 
1920 	/* special case for hexadecimal prefix */
1921 	if (*pp == '0' && (pp[1] == 'x' || pp[1] == 'X'))
1922 	{
1923 		pp += 2;
1924 		xflag = 1;
1925 	} else xflag = 0;
1926 
1927 	/* there must be something to check */
1928 	if (*pp == 0) return(FALSE);
1929 
1930 	founddigits = 0;
1931 	if (xflag != 0)
1932 	{
1933 		while (isxdigit(*pp))
1934 		{
1935 			pp++;
1936 			founddigits = 1;
1937 		}
1938 	} else
1939 	{
1940 		while (isdigit(*pp) || *pp == '.')
1941 		{
1942 			if (*pp != '.') founddigits = 1;
1943 			pp++;
1944 		}
1945 	}
1946 	if (founddigits == 0) return(FALSE);
1947 	if (*pp == 0) return(TRUE);
1948 
1949 	/* handle exponent of floating point numbers */
1950 	if (xflag != 0 || founddigits == 0 || (*pp != 'e' && *pp != 'E')) return(FALSE);
1951 	pp++;
1952 	if (*pp == '+' || *pp == '-') pp++;
1953 	if (*pp == 0) return(FALSE);
1954 	while (isdigit(*pp)) pp++;
1955 	if (*pp == 0) return(TRUE);
1956 
1957 	return(FALSE);
1958 }
1959 
1960 /*
1961  * routine to convert relative or absolute font values to absolute, in the
1962  * window "w"
1963  */
truefontsize(INTBIG font,WINDOWPART * w,TECHNOLOGY * tech)1964 INTBIG truefontsize(INTBIG font, WINDOWPART *w, TECHNOLOGY *tech)
1965 {
1966 	REGISTER INTBIG pixperlam, lambda, height;
1967 	REGISTER LIBRARY *lib;
1968 
1969 	/* keep special font codes */
1970 	if (font == TXTEDITOR || font == TXTMENU) return(font);
1971 
1972 	/* absolute font sizes are easy */
1973 	if ((font&TXTPOINTS) != 0) return((font&TXTPOINTS) >> TXTPOINTSSH);
1974 
1975 	/* detemine default, min, and max size of font */
1976 	if (w->curnodeproto == NONODEPROTO) lib = el_curlib; else
1977 		lib = w->curnodeproto->lib;
1978 	if (tech == NOTECHNOLOGY) tech = el_curtech;
1979 	lambda = lib->lambda[tech->techindex];
1980 	if ((font&TXTQLAMBDA) != 0)
1981 	{
1982 		height = TXTGETQLAMBDA(font);
1983 		height = height * lambda / 4;
1984 		pixperlam = applyyscale(w, height);
1985 		return(pixperlam);
1986 	}
1987 	return(applyyscale(w, lambda));
1988 }
1989 
1990 /*
1991  * routine to set the default text descriptor into "td".  This text will be
1992  * placed on "geom".
1993  */
defaulttextdescript(UINTBIG * descript,GEOM * geom)1994 void defaulttextdescript(UINTBIG *descript, GEOM *geom)
1995 {
1996 	REGISTER VARIABLE *txtvar;
1997 	REGISTER INTBIG dx, dy, goleft, goright, goup, godown;
1998 	INTBIG *defdescript;
1999 	REGISTER NODEINST *ni;
2000 	REGISTER ARCINST *ai;
2001 	static INTBIG user_default_text_style_key = 0, user_default_text_smart_style_key = 0;
2002 
2003 	if (user_default_text_style_key == 0)
2004 		user_default_text_style_key = makekey(x_("USER_default_text_style"));
2005 	if (user_default_text_smart_style_key == 0)
2006 		user_default_text_smart_style_key = makekey(x_("USER_default_text_smart_style"));
2007 
2008 	txtvar = getvalkey((INTBIG)us_tool, VTOOL, VINTEGER|VISARRAY, user_default_text_style_key);
2009 	if (txtvar == NOVARIABLE)
2010 	{
2011 		TDSETPOS(descript, VTPOSCENT);
2012 	} else
2013 	{
2014 		defdescript = (INTBIG *)txtvar->addr;
2015 		TDSETPOS(descript, TDGETPOS(defdescript));
2016 		TDSETSIZE(descript, TDGETSIZE(defdescript));
2017 		TDSETFACE(descript, TDGETFACE(defdescript));
2018 		TDSETITALIC(descript, TDGETITALIC(defdescript));
2019 		TDSETBOLD(descript, TDGETBOLD(defdescript));
2020 		TDSETUNDERLINE(descript, TDGETUNDERLINE(defdescript));
2021 	}
2022 	if (geom != NOGEOM)
2023 	{
2024 		/* set text size */
2025 		if (geom->entryisnode)
2026 		{
2027 			ni = geom->entryaddr.ni;
2028 			if (ni->proto == gen_invispinprim)
2029 			{
2030 				defaulttextsize(2, descript);
2031 			} else
2032 			{
2033 				defaulttextsize(3, descript);
2034 			}
2035 		} else
2036 		{
2037 			defaulttextsize(4, descript);
2038 		}
2039 
2040 		/* handle smart text placement relative to attached object */
2041 		txtvar = getvalkey((INTBIG)us_tool, VTOOL, VINTEGER, user_default_text_smart_style_key);
2042 		if (txtvar != NOVARIABLE)
2043 		{
2044 			/* figure out location of object relative to environment */
2045 			dx = dy = 0;
2046 			if (geom->entryisnode)
2047 			{
2048 				ni = geom->entryaddr.ni;
2049 				if (ni->firstportarcinst != NOPORTARCINST)
2050 				{
2051 					ai = ni->firstportarcinst->conarcinst;
2052 					dx = (ai->end[0].xpos+ai->end[1].xpos)/2 -
2053 						(ni->lowx+ni->highx)/2;
2054 					dy = (ai->end[0].ypos+ai->end[1].ypos)/2 -
2055 						(ni->lowy+ni->highy)/2;
2056 				}
2057 			}
2058 
2059 			/* first move placement horizontally */
2060 			goleft = goright = goup = godown = 0;
2061 			if ((txtvar->addr&03) == 1)
2062 			{
2063 				/* place label inside (towards center) */
2064 				if (dx > 0) goright++; else
2065 					if (dx < 0) goleft++;
2066 			} else if ((txtvar->addr&03) == 2)
2067 			{
2068 				/* place label outside (away from center) */
2069 				if (dx > 0) goleft++; else
2070 					if (dx < 0) goright++;
2071 			}
2072 
2073 			/* next move placement vertically */
2074 			if (((txtvar->addr>>2)&03) == 1)
2075 			{
2076 				/* place label inside (towards center) */
2077 				if (dy > 0) goup++; else
2078 					if (dy < 0) godown++;
2079 			} else if (((txtvar->addr>>2)&03) == 2)
2080 			{
2081 				/* place label outside (away from center) */
2082 				if (dy > 0) godown++; else
2083 					if (dy < 0) goup++;
2084 			}
2085 			if (goleft != 0)
2086 			{
2087 				switch (TDGETPOS(descript))
2088 				{
2089 					case VTPOSCENT:
2090 					case VTPOSRIGHT:
2091 					case VTPOSLEFT:
2092 						TDSETPOS(descript, VTPOSLEFT);
2093 						break;
2094 					case VTPOSUP:
2095 					case VTPOSUPLEFT:
2096 					case VTPOSUPRIGHT:
2097 						TDSETPOS(descript, VTPOSUPLEFT);
2098 						break;
2099 					case VTPOSDOWN:
2100 					case VTPOSDOWNLEFT:
2101 					case VTPOSDOWNRIGHT:
2102 						TDSETPOS(descript, VTPOSDOWNLEFT);
2103 						break;
2104 				}
2105 			}
2106 			if (goright != 0)
2107 			{
2108 				switch (TDGETPOS(descript))
2109 				{
2110 					case VTPOSCENT:
2111 					case VTPOSRIGHT:
2112 					case VTPOSLEFT:
2113 						TDSETPOS(descript, VTPOSRIGHT);
2114 						break;
2115 					case VTPOSUP:
2116 					case VTPOSUPLEFT:
2117 					case VTPOSUPRIGHT:
2118 						TDSETPOS(descript, VTPOSUPRIGHT);
2119 						break;
2120 					case VTPOSDOWN:
2121 					case VTPOSDOWNLEFT:
2122 					case VTPOSDOWNRIGHT:
2123 						TDSETPOS(descript, VTPOSDOWNRIGHT);
2124 						break;
2125 				}
2126 			}
2127 			if (goup != 0)
2128 			{
2129 				switch (TDGETPOS(descript))
2130 				{
2131 					case VTPOSCENT:
2132 					case VTPOSUP:
2133 					case VTPOSDOWN:
2134 						TDSETPOS(descript, VTPOSUP);
2135 						break;
2136 					case VTPOSRIGHT:
2137 					case VTPOSUPRIGHT:
2138 					case VTPOSDOWNRIGHT:
2139 						TDSETPOS(descript, VTPOSUPRIGHT);
2140 						break;
2141 					case VTPOSLEFT:
2142 					case VTPOSUPLEFT:
2143 					case VTPOSDOWNLEFT:
2144 						TDSETPOS(descript, VTPOSUPLEFT);
2145 						break;
2146 				}
2147 			}
2148 			if (godown != 0)
2149 			{
2150 				switch (TDGETPOS(descript))
2151 				{
2152 					case VTPOSCENT:
2153 					case VTPOSUP:
2154 					case VTPOSDOWN:
2155 						TDSETPOS(descript, VTPOSDOWN);
2156 						break;
2157 					case VTPOSRIGHT:
2158 					case VTPOSUPRIGHT:
2159 					case VTPOSDOWNRIGHT:
2160 						TDSETPOS(descript, VTPOSDOWNRIGHT);
2161 						break;
2162 					case VTPOSLEFT:
2163 					case VTPOSUPLEFT:
2164 					case VTPOSDOWNLEFT:
2165 						TDSETPOS(descript, VTPOSDOWNLEFT);
2166 						break;
2167 				}
2168 			}
2169 		}
2170 	}
2171 }
2172 
2173 #define DEFNODETEXTSIZE      TXTSETQLAMBDA(4)
2174 #define DEFARCTEXTSIZE       TXTSETQLAMBDA(4)
2175 #define DEFEXPORTSIZE        TXTSETQLAMBDA(8)
2176 #define DEFNONLAYOUTTEXTSIZE TXTSETQLAMBDA(4)
2177 #define DEFINSTTEXTSIZE      TXTSETQLAMBDA(16)
2178 #define DEFCELLTEXTSIZE      TXTSETQLAMBDA(4)
2179 
2180 /*
2181  * Routine to determine the size of text to use for a particular type of text:
2182  * 3:  node name
2183  * 4:  arc name
2184  * 1:  export label
2185  * 2:  nonlayout text (text on an invisible pin, cell variables)
2186  * 5:  cell instance name
2187  * 6:  cell text
2188  */
defaulttextsize(INTBIG texttype,UINTBIG * descript)2189 void defaulttextsize(INTBIG texttype, UINTBIG *descript)
2190 {
2191 	REGISTER VARIABLE *var;
2192 	REGISTER INTBIG i;
2193 	static INTBIG node_size_key = 0, arc_size_key = 0, export_size_key = 0,
2194 		nonlayout_size_key = 0, instance_size_key = 0, cell_size_key = 0;
2195 
2196 	if (node_size_key == 0)
2197 		node_size_key = makekey(x_("USER_default_node_text_size"));
2198 	if (arc_size_key == 0)
2199 		arc_size_key = makekey(x_("USER_default_arc_text_size"));
2200 	if (export_size_key == 0)
2201 		export_size_key = makekey(x_("USER_default_export_text_size"));
2202 	if (nonlayout_size_key == 0)
2203 		nonlayout_size_key = makekey(x_("USER_default_nonlayout_text_size"));
2204 	if (instance_size_key == 0)
2205 		instance_size_key = makekey(x_("USER_default_instance_text_size"));
2206 	if (cell_size_key == 0)
2207 		cell_size_key = makekey(x_("USER_default_facet_text_size"));
2208 
2209 	for(i=0; i<TEXTDESCRIPTSIZE; i++) descript[i] = 0;
2210 	TDSETSIZE(descript, TXTSETQLAMBDA(4));
2211 	switch (texttype)
2212 	{
2213 		case 3:
2214 			var = getvalkey((INTBIG)us_tool, VTOOL, -1, node_size_key);
2215 			if (var == NOVARIABLE) TDSETSIZE(descript, DEFNODETEXTSIZE); else
2216 			{
2217 				if ((var->type&VISARRAY) != 0) TDCOPY(descript, (UINTBIG *)var->addr); else
2218 					TDSETSIZE(descript, var->addr);
2219 			}
2220 			break;
2221 		case 4:
2222 			var = getvalkey((INTBIG)us_tool, VTOOL, -1, arc_size_key);
2223 			if (var == NOVARIABLE) TDSETSIZE(descript, DEFARCTEXTSIZE); else
2224 			{
2225 				if ((var->type&VISARRAY) != 0) TDCOPY(descript, (UINTBIG *)var->addr); else
2226 					TDSETSIZE(descript, var->addr);
2227 			}
2228 			break;
2229 		case 1:
2230 			var = getvalkey((INTBIG)us_tool, VTOOL, -1, export_size_key);
2231 			if (var == NOVARIABLE) TDSETSIZE(descript, DEFEXPORTSIZE); else
2232 			{
2233 				if ((var->type&VISARRAY) != 0) TDCOPY(descript, (UINTBIG *)var->addr); else
2234 					TDSETSIZE(descript, var->addr);
2235 			}
2236 			break;
2237 		case 2:
2238 			var = getvalkey((INTBIG)us_tool, VTOOL, -1, nonlayout_size_key);
2239 			if (var == NOVARIABLE) TDSETSIZE(descript, DEFNONLAYOUTTEXTSIZE); else
2240 			{
2241 				if ((var->type&VISARRAY) != 0) TDCOPY(descript, (UINTBIG *)var->addr); else
2242 					TDSETSIZE(descript, var->addr);
2243 			}
2244 			break;
2245 		case 5:
2246 			var = getvalkey((INTBIG)us_tool, VTOOL, -1, instance_size_key);
2247 			if (var == NOVARIABLE) TDSETSIZE(descript, DEFINSTTEXTSIZE); else
2248 			{
2249 				if ((var->type&VISARRAY) != 0) TDCOPY(descript, (UINTBIG *)var->addr); else
2250 					TDSETSIZE(descript, var->addr);
2251 			}
2252 			break;
2253 		case 6:
2254 			var = getvalkey((INTBIG)us_tool, VTOOL, -1, cell_size_key);
2255 			if (var == NOVARIABLE) TDSETSIZE(descript, DEFCELLTEXTSIZE); else
2256 			{
2257 				if ((var->type&VISARRAY) != 0) TDCOPY(descript, (UINTBIG *)var->addr); else
2258 					TDSETSIZE(descript, var->addr);
2259 			}
2260 			break;
2261 	}
2262 }
2263 
2264 /*
2265  * Routine to get the X offset field of text descriptor words "td".  This routine
2266  * implements the macro "TDGETXOFF".
2267  */
gettdxoffset(UINTBIG * td)2268 INTBIG gettdxoffset(UINTBIG *td)
2269 {
2270 	REGISTER INTBIG offset, scale;
2271 
2272 	offset = (td[0] & VTXOFF) >> VTXOFFSH;
2273 	if ((td[0]&VTXOFFNEG) != 0) offset = -offset;
2274 	scale = TDGETOFFSCALE(td) + 1;
2275 	return(offset * scale);
2276 }
2277 
2278 /*
2279  * Routine to get the Y offset field of text descriptor words "td".  This routine
2280  * implements the macro "TDGETYOFF".
2281  */
gettdyoffset(UINTBIG * td)2282 INTBIG gettdyoffset(UINTBIG *td)
2283 {
2284 	REGISTER INTBIG offset, scale;
2285 
2286 	offset = (td[0] & VTYOFF) >> VTYOFFSH;
2287 	if ((td[0]&VTYOFFNEG) != 0) offset = -offset;
2288 	scale = TDGETOFFSCALE(td) + 1;
2289 	return(offset * scale);
2290 }
2291 
2292 /*
2293  * Routine to set the X offset field of text descriptor words "td" to "value".
2294  * This routine implements the macro "TDSETOFF".
2295  */
settdoffset(UINTBIG * td,INTBIG x,INTBIG y)2296 void settdoffset(UINTBIG *td, INTBIG x, INTBIG y)
2297 {
2298 	REGISTER INTBIG scale;
2299 
2300 	td[0] = td[0] & ~(VTXOFF|VTYOFF|VTXOFFNEG|VTYOFFNEG);
2301 	if (x < 0)
2302 	{
2303 		x = -x;
2304 		td[0] |= VTXOFFNEG;
2305 	}
2306 	if (y < 0)
2307 	{
2308 		y = -y;
2309 		td[0] |= VTYOFFNEG;
2310 	}
2311 	scale = maxi(x,y) >> VTOFFMASKWID;
2312 	x /= (scale + 1);
2313 	y /= (scale + 1);
2314 	td[0] |= (x << VTXOFFSH);
2315 	td[0] |= (y << VTYOFFSH);
2316 	TDSETOFFSCALE(td, scale);
2317 }
2318 
2319 /*
2320  * Routine to validate the variable offset (x,y) and clip or grid it
2321  * to acceptable values.
2322  */
propervaroffset(INTBIG * x,INTBIG * y)2323 void propervaroffset(INTBIG *x, INTBIG *y)
2324 {
2325 	REGISTER INTBIG scale, realx, realy, negx, negy;
2326 
2327 	negx = negy = 1;
2328 	realx = *x;   realy = *y;
2329 	if (realx < 0) { realx = -realx;   negx = -1; }
2330 	if (realy < 0) { realy = -realy;   negy = -1; }
2331 	if (realx > 077777) realx = 077777;
2332 	if (realy > 077777) realy = 077777;
2333 	scale = (maxi(realx, realy) >> VTOFFMASKWID) + 1;
2334 	realx /= scale;
2335 	realy /= scale;
2336 	*x = realx * scale * negx;
2337 	*y = realy * scale * negy;
2338 }
2339 
2340 /*
2341  * Routine to clear the text descriptor array in "t".
2342  * This routine implements the macro "TDCLEAR".
2343  */
tdclear(UINTBIG * t)2344 void tdclear(UINTBIG *t)
2345 {
2346 	REGISTER INTBIG i;
2347 
2348 	for(i=0; i<TEXTDESCRIPTSIZE; i++) t[i] = 0;
2349 }
2350 
2351 /*
2352  * Routine to copy the text descriptor array in "s" to "d".
2353  * This routine implements the macro "TDCOPY".
2354  */
tdcopy(UINTBIG * d,UINTBIG * s)2355 void tdcopy(UINTBIG *d, UINTBIG *s)
2356 {
2357 	REGISTER INTBIG i;
2358 
2359 	for(i=0; i<TEXTDESCRIPTSIZE; i++) d[i] = s[i];
2360 }
2361 
2362 /*
2363  * Routine to compare the text descriptor arrays in "t1" to "t2"
2364  * and return true if they are different.
2365  * This routine implements the macro "TDDIFF".
2366  */
tddiff(UINTBIG * t1,UINTBIG * t2)2367 BOOLEAN tddiff(UINTBIG *t1, UINTBIG *t2)
2368 {
2369 	REGISTER INTBIG i;
2370 
2371 	for(i=0; i<TEXTDESCRIPTSIZE; i++)
2372 		if (t1[i] != t2[i]) return(TRUE);
2373 	return(FALSE);
2374 }
2375 
2376 /*
2377  * routine to make a printable string from variable "val", array index
2378  * "aindex".  If "aindex" is negative, print the entire array.  If "purpose" is
2379  * zero, the conversion is for human reading and should be easy to understand.
2380  * If "purpose" is positive, the conversion is for machine reading and should
2381  * be easy to parse.  If "purpose" is negative, the conversion is for
2382  * parameter substitution and should be easy to understand but not hard to
2383  * parse (a combination of the two).  If the variable is displayable and the name
2384  * is to be displayed, that part is added.
2385  */
describedisplayedvariable(VARIABLE * var,INTBIG aindex,INTBIG purpose)2386 CHAR *describedisplayedvariable(VARIABLE *var, INTBIG aindex, INTBIG purpose)
2387 {
2388 	REGISTER CHAR *name, *parstr;
2389 	REGISTER INTBIG len;
2390 	REGISTER VARIABLE *parval;
2391 	REGISTER void *infstr;
2392 
2393 	if (var == NOVARIABLE)
2394 	{
2395 		if (purpose <= 0) return(x_("***UNKNOWN***")); else return(x_(""));
2396 	}
2397 
2398 	infstr = initinfstr();
2399 
2400 	/* add variable name if requested */
2401 	if ((var->type&VDISPLAY) != 0)
2402 	{
2403 		if ((var->type&VISARRAY) == 0) len = 1; else
2404 			len = getlength(var);
2405 		name = truevariablename(var);
2406 		switch (TDGETDISPPART(var->textdescript))
2407 		{
2408 			case VTDISPLAYNAMEVALUE:
2409 				if (len > 1 && aindex >= 0)
2410 				{
2411 					formatinfstr(infstr, x_("%s[%ld]="), name, aindex);
2412 				} else
2413 				{
2414 					formatinfstr(infstr, x_("%s="), name);
2415 				}
2416 				break;
2417 			case VTDISPLAYNAMEVALINH:
2418 				/* setvariablewindow(el_curwindowpart); */
2419 				parval = getparentvalkey(var->key, 1);
2420 				/* setvariablewindow(NOWINDOWPART); */
2421 				if (parval == NOVARIABLE) parstr = x_("?"); else
2422 					parstr = describevariable(parval, -1, purpose);
2423 				if (len > 1 && aindex >= 0)
2424 				{
2425 					formatinfstr(infstr, x_("%s[%ld]=%s;def="), name, aindex, parstr);
2426 				} else
2427 				{
2428 					formatinfstr(infstr, x_("%s=%s;def="), name, parstr);
2429 				}
2430 				break;
2431 			case VTDISPLAYNAMEVALINHALL:
2432 				/* setvariablewindow(el_curwindowpart); */
2433 				parval = getparentvalkey(var->key, 0);
2434 				/* setvariablewindow(NOWINDOWPART); */
2435 				if (parval == NOVARIABLE) parstr = x_("?"); else
2436 					parstr = describevariable(parval, -1, purpose);
2437 				if (len > 1 && aindex >= 0)
2438 				{
2439 					formatinfstr(infstr, x_("%s[%ld]=%s;def="), name, aindex, parstr);
2440 				} else
2441 				{
2442 					formatinfstr(infstr, x_("%s=%s;def="), name, parstr);
2443 				}
2444 				break;
2445 		}
2446 	}
2447 	addstringtoinfstr(infstr, describevariable(var, aindex, purpose));
2448 	return(returninfstr(infstr));
2449 }
2450 
2451 /*
2452  * Routine to return the true name of variable "var".  Leading "ATTR_" or "ATTRP_"
2453  * markers are removed.
2454  */
truevariablename(VARIABLE * var)2455 CHAR *truevariablename(VARIABLE *var)
2456 {
2457 	REGISTER CHAR *name;
2458 	REGISTER INTBIG i, len;
2459 
2460 	name = makename(var->key);
2461 	if (estrncmp(name, x_("ATTR_"), 5) == 0)
2462 		return(name + 5);
2463 	if (estrncmp(name, x_("ATTRP_"), 6) == 0)
2464 	{
2465 		len = estrlen(name);
2466 		for(i=len-1; i>=0; i--) if (name[i] == '_') break;
2467 		return(name + i);
2468 	}
2469 	return(name);
2470 }
2471 
2472 /*
2473  * routine to make a printable string from variable "val", array index
2474  * "aindex".  If "aindex" is negative, print the entire array.  If "purpose" is
2475  * zero, the conversion is for human reading and should be easy to understand.
2476  * If "purpose" is positive, the conversion is for machine reading and should
2477  * be easy to parse.  If "purpose" is negative, the conversion is for
2478  * parameter substitution and should be easy to understand but not hard to
2479  * parse (a combination of the two).
2480  */
describevariable(VARIABLE * var,INTBIG aindex,INTBIG purpose)2481 CHAR *describevariable(VARIABLE *var, INTBIG aindex, INTBIG purpose)
2482 {
2483 	REGISTER INTBIG i, len, *addr, units;
2484 	REGISTER void *infstr;
2485 
2486 	if (var == NOVARIABLE)
2487 	{
2488 		if (purpose <= 0) return(x_("***UNKNOWN***")); else return(x_(""));
2489 	}
2490 
2491 	infstr = initinfstr();
2492 
2493 	units = TDGETUNITS(var->textdescript);
2494 	if ((var->type & (VCODE1|VCODE2)) != 0)
2495 	{
2496 		/* special case for code: it is a string, the type applies to the result */
2497 		db_makestringvar(VSTRING, var->addr, purpose, units, infstr);
2498 	} else
2499 	{
2500 		if ((var->type&VISARRAY) != 0)
2501 		{
2502 			/* compute the array length */
2503 			len = getlength(var);
2504 			addr = (INTBIG *)var->addr;
2505 
2506 			/* if asking for a single entry, get it */
2507 			if (aindex >= 0)
2508 			{
2509 				/* special case when the variable is a general array of objects */
2510 				if ((var->type&VTYPE) == VGENERAL)
2511 				{
2512 					/* index the array in pairs */
2513 					aindex *= 2;
2514 					if (aindex < len)
2515 						db_makestringvar(addr[aindex+1], addr[aindex], purpose, units, infstr);
2516 				} else
2517 				{
2518 					/* normal array indexing */
2519 					if (aindex < len)
2520 						switch ((var->type&VTYPE))
2521 					{
2522 						case VCHAR:
2523 							db_makestringvar(var->type,
2524 								((INTBIG)((CHAR *)addr)[aindex]), purpose, units, infstr);
2525 							break;
2526 
2527 						case VDOUBLE:
2528 							db_makestringvar(var->type,
2529 								((INTBIG)((double *)addr)[aindex]), purpose, units, infstr);
2530 							break;
2531 
2532 						case VSHORT:
2533 							db_makestringvar(var->type,
2534 								((INTBIG)((INTSML *)addr)[aindex]), purpose, units, infstr);
2535 							break;
2536 
2537 						default:
2538 							db_makestringvar(var->type, addr[aindex], purpose, units, infstr);
2539 							break;
2540 					}
2541 				}
2542 			} else
2543 			{
2544 				/* in an array, quote strings */
2545 				if (purpose < 0) purpose = 0;
2546 				addtoinfstr(infstr, '[');
2547 				for(i=0; i<len; i++)
2548 				{
2549 					if (i != 0) addtoinfstr(infstr, ',');
2550 
2551 					/* special case when the variable is a general array of objects */
2552 					if ((var->type&VTYPE) == VGENERAL)
2553 					{
2554 						/* index the array in pairs */
2555 						if (i+1 < len)
2556 							db_makestringvar(addr[i+1], addr[i], purpose, units, infstr);
2557 						i++;
2558 					} else
2559 					{
2560 						/* normal array indexing */
2561 						switch ((var->type&VTYPE))
2562 						{
2563 							case VCHAR:
2564 								db_makestringvar(var->type,
2565 									((INTBIG)((CHAR *)addr)[i]), purpose, units, infstr);
2566 								break;
2567 
2568 							case VDOUBLE:
2569 								db_makestringvar(var->type,
2570 									((INTBIG)((double *)addr)[i]), purpose, units, infstr);
2571 								break;
2572 
2573 							case VSHORT:
2574 								db_makestringvar(var->type,
2575 									((INTBIG)((INTSML *)addr)[i]), purpose, units, infstr);
2576 								break;
2577 
2578 							default:
2579 								db_makestringvar(var->type, addr[i], purpose, units, infstr);
2580 								break;
2581 						}
2582 					}
2583 				}
2584 				addtoinfstr(infstr, ']');
2585 			}
2586 		} else db_makestringvar(var->type, var->addr, purpose, units, infstr);
2587 	}
2588 	return(returninfstr(infstr));
2589 }
2590 
2591 /*
2592  * routine to make a string from the value in "addr" which has a type in
2593  * "type".  "purpose" is an indicator of the purpose of this conversion:
2594  * zero indicates conversion for humans to read, positive indicates
2595  * conversion for a program to read (more terse) and negative indicates human
2596  * reading for parameter substitution (don't quote strings).  Returns true
2597  * if there is a memory allocation error.
2598  */
db_makestringvar(INTBIG type,INTBIG addr,INTBIG purpose,INTBIG units,void * infstr)2599 void db_makestringvar(INTBIG type, INTBIG addr, INTBIG purpose, INTBIG units, void *infstr)
2600 {
2601 	CHAR line[100];
2602 	REGISTER NODEINST *ni;
2603 	REGISTER NODEPROTO *np;
2604 	REGISTER PORTARCINST *pi;
2605 	REGISTER PORTEXPINST *pe;
2606 	REGISTER PORTPROTO *pp;
2607 	REGISTER ARCINST *ai;
2608 	REGISTER ARCPROTO *ap;
2609 	REGISTER GEOM *geom;
2610 	REGISTER RTNODE *rtn;
2611 	REGISTER LIBRARY *lib;
2612 	REGISTER TECHNOLOGY *tech;
2613 	REGISTER TOOL *tool;
2614 	REGISTER NETWORK *net;
2615 	REGISTER VIEW *view;
2616 	REGISTER WINDOWPART *win;
2617 	REGISTER GRAPHICS *gra;
2618 	REGISTER VARIABLE *var;
2619 	REGISTER CONSTRAINT *con;
2620 	REGISTER WINDOWFRAME *wf;
2621 	REGISTER POLYGON *poly;
2622 
2623 	switch (type&VTYPE)
2624 	{
2625 		case VINTEGER:
2626 		case VSHORT:
2627 		case VBOOLEAN:
2628 			if (units == 0)
2629 			{
2630 				(void)esnprintf(line, 100, x_("%ld"), addr);
2631 				addstringtoinfstr(infstr, line);
2632 				return;
2633 			}
2634 			addstringtoinfstr(infstr, db_makeunits((float)addr, units));
2635 			return;
2636 		case VADDRESS:
2637 			if (purpose == 0) addstringtoinfstr(infstr, x_("Address="));
2638 			(void)esnprintf(line, 100, x_("0%lo"), addr);
2639 			addstringtoinfstr(infstr, line);
2640 			return;
2641 		case VCHAR:
2642 			addtoinfstr(infstr, (CHAR)addr);
2643 			return;
2644 		case VSTRING:
2645 			if ((CHAR *)addr == NOSTRING || (CHAR *)addr == 0)
2646 			{
2647 				addstringtoinfstr(infstr, x_("***NOSTRING***"));
2648 				return;
2649 			}
2650 			if (purpose >= 0) addtoinfstr(infstr, '"');
2651 			if (purpose <= 0)  addstringtoinfstr(infstr, (CHAR *)addr); else
2652 				db_addstring((CHAR *)addr, infstr);
2653 			if (purpose >= 0)addtoinfstr(infstr, '"');
2654 			return;
2655 		case VFLOAT:
2656 		case VDOUBLE:
2657 			if (units == 0)
2658 			{
2659 				(void)esnprintf(line, 100, x_("%g"), castfloat(addr));
2660 				addstringtoinfstr(infstr, line);
2661 				return;
2662 			}
2663 			addstringtoinfstr(infstr, db_makeunits(castfloat(addr), units));
2664 			return;
2665 		case VNODEINST:
2666 			ni = (NODEINST *)addr;
2667 			if (ni == NONODEINST)
2668 			{
2669 				addstringtoinfstr(infstr, x_("***NONODEINST***"));
2670 				return;
2671 			}
2672 			if (purpose == 0)
2673 			{
2674 				var = getvalkey((INTBIG)ni, VNODEINST, VSTRING, el_node_name_key);
2675 				if (var != NOVARIABLE)
2676 				{
2677 					addstringtoinfstr(infstr, (CHAR *)var->addr);
2678 					return;
2679 				}
2680 				(void)esnprintf(line, 100, x_("node%ld"), (INTBIG)ni);
2681 				addstringtoinfstr(infstr, line);
2682 				return;
2683 			}
2684 			(void)esnprintf(line, 100, x_("node%ld"), (INTBIG)ni);
2685 			addstringtoinfstr(infstr, line);
2686 			return;
2687 		case VNODEPROTO:
2688 			np = (NODEPROTO *)addr;
2689 			if (purpose <= 0)
2690 			{
2691 				addstringtoinfstr(infstr, describenodeproto(np));
2692 				return;
2693 			}
2694 			db_addstring(describenodeproto(np), infstr);
2695 			return;
2696 		case VPORTARCINST:
2697 			pi = (PORTARCINST *)addr;
2698 			if (pi == NOPORTARCINST)
2699 			{
2700 				addstringtoinfstr(infstr, x_("***NOPORTARCINST***"));
2701 				return;
2702 			}
2703 			(void)esnprintf(line, 100, x_("portarc%ld"), (INTBIG)pi);
2704 			addstringtoinfstr(infstr, line);
2705 			return;
2706 		case VPORTEXPINST:
2707 			pe = (PORTEXPINST *)addr;
2708 			if (pe == NOPORTEXPINST)
2709 			{
2710 				addstringtoinfstr(infstr, x_("***NOPORTEXPINST***"));
2711 				return;
2712 			}
2713 			(void)esnprintf(line, 100, x_("portexp%ld"), (INTBIG)pe);
2714 			addstringtoinfstr(infstr, line);
2715 			return;
2716 		case VPORTPROTO:
2717 			pp = (PORTPROTO *)addr;
2718 			if (pp == NOPORTPROTO)
2719 			{
2720 				addstringtoinfstr(infstr, x_("***NOPORTPROTO***"));
2721 				return;
2722 			}
2723 			if (purpose <= 0)
2724 			{
2725 				addstringtoinfstr(infstr, pp->protoname);
2726 				return;
2727 			}
2728 			db_addstring(pp->protoname, infstr);
2729 			return;
2730 		case VARCINST:
2731 			ai = (ARCINST *)addr;
2732 			if (ai == NOARCINST)
2733 			{
2734 				addstringtoinfstr(infstr, x_("***NOARCINST***"));
2735 				return;
2736 			}
2737 			(void)esnprintf(line, 100, x_("arc%ld"), (INTBIG)ai);
2738 			addstringtoinfstr(infstr, line);
2739 			return;
2740 		case VARCPROTO:
2741 			ap = (ARCPROTO *)addr;
2742 			if (purpose <= 0)
2743 			{
2744 				addstringtoinfstr(infstr, describearcproto(ap));
2745 				return;
2746 			}
2747 			db_addstring(describearcproto(ap), infstr);
2748 			return;
2749 		case VGEOM:
2750 			geom = (GEOM *)addr;
2751 			if (geom == NOGEOM)
2752 			{
2753 				addstringtoinfstr(infstr, x_("***NOGEOM***"));
2754 				return;
2755 			}
2756 			(void)esnprintf(line, 100, x_("geom%ld"), (INTBIG)geom);
2757 			addstringtoinfstr(infstr, line);
2758 			return;
2759 		case VLIBRARY:
2760 			lib = (LIBRARY *)addr;
2761 			if (lib == NOLIBRARY)
2762 			{
2763 				addstringtoinfstr(infstr, x_("***NOLIBRARY***"));
2764 				return;
2765 			}
2766 			if (purpose <= 0)
2767 			{
2768 				addstringtoinfstr(infstr, lib->libname);
2769 				return;
2770 			}
2771 			db_addstring(lib->libname, infstr);
2772 			return;
2773 		case VTECHNOLOGY:
2774 			tech = (TECHNOLOGY *)addr;
2775 			if (tech == NOTECHNOLOGY)
2776 			{
2777 				addstringtoinfstr(infstr, x_("***NOTECHNOLOGY***"));
2778 				return;
2779 			}
2780 			if (purpose <= 0)
2781 			{
2782 				addstringtoinfstr(infstr, tech->techname);
2783 				return;
2784 			}
2785 			db_addstring(tech->techname, infstr);
2786 			return;
2787 		case VTOOL:
2788 			tool = (TOOL *)addr;
2789 			if (tool == NOTOOL)
2790 			{
2791 				addstringtoinfstr(infstr, x_("***NOTOOL***"));
2792 				return;
2793 			}
2794 			if (purpose <= 0)
2795 			{
2796 				addstringtoinfstr(infstr, tool->toolname);
2797 				return;
2798 			}
2799 			db_addstring(tool->toolname, infstr);
2800 			return;
2801 		case VRTNODE:
2802 			rtn = (RTNODE *)addr;
2803 			if (rtn == NORTNODE)
2804 			{
2805 				addstringtoinfstr(infstr, x_("***NORTNODE***"));
2806 				return;
2807 			}
2808 			(void)esnprintf(line, 100, x_("rtn%ld"), (INTBIG)rtn);
2809 			addstringtoinfstr(infstr, line);
2810 			return;
2811 		case VFRACT:
2812 			addstringtoinfstr(infstr, frtoa(addr));
2813 			return;
2814 		case VNETWORK:
2815 			net = (NETWORK *)addr;
2816 			if (net == NONETWORK)
2817 			{
2818 				addstringtoinfstr(infstr, x_("***NONETWORK***"));
2819 				return;
2820 			}
2821 			if (purpose <= 0)
2822 			{
2823 				addstringtoinfstr(infstr, describenetwork(net));
2824 				return;
2825 			}
2826 			db_addstring(describenetwork(net), infstr);
2827 			return;
2828 		case VVIEW:
2829 			view = (VIEW *)addr;
2830 			if (view == NOVIEW)
2831 			{
2832 				addstringtoinfstr(infstr, x_("***NOVIEW***"));
2833 				return;
2834 			}
2835 			if (purpose <= 0)
2836 			{
2837 				addstringtoinfstr(infstr, view->viewname);
2838 				return;
2839 			}
2840 			db_addstring(view->viewname, infstr);
2841 			return;
2842 		case VWINDOWPART:
2843 			win = (WINDOWPART *)addr;
2844 			if (win == NOWINDOWPART)
2845 			{
2846 				addstringtoinfstr(infstr, x_("***NOWINDOWPART***"));
2847 				return;
2848 			}
2849 			if (purpose <= 0)
2850 			{
2851 				addstringtoinfstr(infstr, win->location);
2852 				return;
2853 			}
2854 			db_addstring(win->location, infstr);
2855 			return;
2856 		case VGRAPHICS:
2857 			gra = (GRAPHICS *)addr;
2858 			if (gra == NOGRAPHICS)
2859 			{
2860 				addstringtoinfstr(infstr, x_("***NOGRAPHICS***"));
2861 				return;
2862 			}
2863 			(void)esnprintf(line, 100, x_("gra%ld"), (INTBIG)gra);
2864 			addstringtoinfstr(infstr, line);
2865 			return;
2866 		case VCONSTRAINT:
2867 			con = (CONSTRAINT *)addr;
2868 			if (con == NOCONSTRAINT)
2869 			{
2870 				addstringtoinfstr(infstr, x_("***NOCONSTRAINT***"));
2871 				return;
2872 			}
2873 			if (purpose <= 0)
2874 			{
2875 				addstringtoinfstr(infstr, con->conname);
2876 				return;
2877 			}
2878 			db_addstring(con->conname, infstr);
2879 			return;
2880 		case VGENERAL:
2881 			(void)esnprintf(line, 100, x_("***%ld-LONG-GENERAL-ARRAY***"),
2882 				((type&VLENGTH)>>VLENGTHSH) / 2);
2883 			addstringtoinfstr(infstr, line);
2884 			return;
2885 		case VWINDOWFRAME:
2886 			wf = (WINDOWFRAME *)addr;
2887 			if (wf == NOWINDOWFRAME)
2888 			{
2889 				addstringtoinfstr(infstr, x_("***NOWINDOWFRAME***"));
2890 				return;
2891 			}
2892 			(void)esnprintf(line, 100, x_("wf%ld"), (INTBIG)wf);
2893 			addstringtoinfstr(infstr, line);
2894 			return;
2895 		case VPOLYGON:
2896 			poly = (POLYGON *)addr;
2897 			if (poly == NOPOLYGON)
2898 			{
2899 				addstringtoinfstr(infstr, x_("***NOPOLYGON***"));
2900 				return;
2901 			}
2902 			(void)esnprintf(line, 100, x_("poly%ld"), (INTBIG)poly);
2903 			addstringtoinfstr(infstr, line);
2904 			return;
2905 	}
2906 }
2907 
db_makeunits(float value,INTBIG units)2908 CHAR *db_makeunits(float value, INTBIG units)
2909 {
2910 	static CHAR line[100];
2911 
2912 	switch (units)
2913 	{
2914 		case VTUNITSRES:
2915 			return(displayedunits(value, units,
2916 				(us_electricalunits&INTERNALRESUNITS) >> INTERNALRESUNITSSH));
2917 		case VTUNITSCAP:
2918 			return(displayedunits(value, units,
2919 				(us_electricalunits&INTERNALCAPUNITS) >> INTERNALCAPUNITSSH));
2920 		case VTUNITSIND:
2921 			return(displayedunits(value, units,
2922 				(us_electricalunits&INTERNALINDUNITS) >> INTERNALINDUNITSSH));
2923 		case VTUNITSCUR:
2924 			return(displayedunits(value, units,
2925 				(us_electricalunits&INTERNALCURUNITS) >> INTERNALCURUNITSSH));
2926 		case VTUNITSVOLT:
2927 			return(displayedunits(value, units,
2928 				(us_electricalunits&INTERNALVOLTUNITS) >> INTERNALVOLTUNITSSH));
2929 		case VTUNITSTIME:
2930 			return(displayedunits(value, units,
2931 				(us_electricalunits&INTERNALTIMEUNITS) >> INTERNALTIMEUNITSSH));
2932 	}
2933 	esnprintf(line, 100, x_("%g"), value);
2934 	return(line);
2935 }
2936 
2937 /*
2938  * routine to add the string "str" to the infinite string and to quote the
2939  * special characters '[', ']', '"', and '^'.  The routine returns nonzero
2940  * if there is memory problem with the infinite string package.
2941  */
db_addstring(CHAR * str,void * infstr)2942 void db_addstring(CHAR *str, void *infstr)
2943 {
2944 	while (*str != 0)
2945 	{
2946 		if (*str == '[' || *str == ']' || *str == '"' || *str == '^')
2947 			addtoinfstr(infstr, '^');
2948 		addtoinfstr(infstr, *str++);
2949 	}
2950 }
2951 
2952 /*
2953  * Routine to describe a simple variable "var".  There can be no arrays, code,
2954  * or pointers to Electric objects in this variable.
2955  */
describesimplevariable(VARIABLE * var)2956 CHAR *describesimplevariable(VARIABLE *var)
2957 {
2958 	static CHAR line[50];
2959 
2960 	/* code cannot be called by multiple procesors: uses globals */
2961 	NOT_REENTRANT;
2962 
2963 	switch (var->type&VTYPE)
2964 	{
2965 		case VSTRING:
2966 			return((CHAR *)var->addr);
2967 		case VINTEGER:
2968 			esnprintf(line, 50, x_("%ld"), var->addr);
2969 			return(line);
2970 		case VFRACT:
2971 			estrcpy(line, frtoa(var->addr));
2972 			return(line);
2973 		case VSHORT:
2974 			esnprintf(line, 50, x_("%ld"), var->addr&0xFFFF);
2975 			return(line);
2976 		case VBOOLEAN:
2977 			esnprintf(line, 50, x_("%ld"), var->addr&0xFF);
2978 			return(line);
2979 		case VFLOAT:
2980 		case VDOUBLE:
2981 			esnprintf(line, 50, x_("%g"), castfloat(var->addr));
2982 			return(line);
2983 	}
2984 	return(x_(""));
2985 }
2986 
2987 /*
2988  * Routine to determine the variable type of "pt"; either an integer, float,
2989  * or string; and return the type and value in "type" and "addr".  If "units"
2990  * is nonzero, this value is converted into that type of unit.
2991  */
getsimpletype(CHAR * pt,INTBIG * type,INTBIG * addr,INTBIG units)2992 void getsimpletype(CHAR *pt, INTBIG *type, INTBIG *addr, INTBIG units)
2993 {
2994 	REGISTER CHAR *opt;
2995 	REGISTER INTBIG i;
2996 	float f;
2997 
2998 	if (!isanumber(pt))
2999 	{
3000 		*type = VSTRING;
3001 		*addr = (INTBIG)pt;
3002 		return;
3003 	}
3004 	switch (units)
3005 	{
3006 		case VTUNITSRES:
3007 			*addr = castint(figureunits(pt, VTUNITSRES,
3008 				(us_electricalunits&INTERNALRESUNITS) >> INTERNALRESUNITSSH));
3009 			*type = VFLOAT;
3010 			return;
3011 		case VTUNITSCAP:
3012 			*addr = castint(figureunits(pt, VTUNITSCAP,
3013 				(us_electricalunits&INTERNALCAPUNITS) >> INTERNALCAPUNITSSH));
3014 			*type = VFLOAT;
3015 			return;
3016 		case VTUNITSIND:
3017 			*addr = castint(figureunits(pt, VTUNITSIND,
3018 				(us_electricalunits&INTERNALINDUNITS) >> INTERNALINDUNITSSH));
3019 			*type = VFLOAT;
3020 			return;
3021 		case VTUNITSCUR:
3022 			*addr = castint(figureunits(pt, VTUNITSCUR,
3023 				(us_electricalunits&INTERNALCURUNITS) >> INTERNALCURUNITSSH));
3024 			*type = VFLOAT;
3025 			return;
3026 		case VTUNITSVOLT:
3027 			*addr = castint(figureunits(pt, VTUNITSVOLT,
3028 				(us_electricalunits&INTERNALVOLTUNITS) >> INTERNALVOLTUNITSSH));
3029 			*type = VFLOAT;
3030 			return;
3031 		case VTUNITSTIME:
3032 			*addr = castint(figureunits(pt, VTUNITSTIME,
3033 				(us_electricalunits&INTERNALTIMEUNITS) >> INTERNALTIMEUNITSSH));
3034 			*type = VFLOAT;
3035 			return;
3036 	}
3037 
3038 	*type = VINTEGER;
3039 	*addr = eatoi(pt);
3040 	for(opt = pt; *opt != 0; opt++) if (*opt == '.' || *opt == 'e' || *opt == 'E')
3041 	{
3042 		f = (float)eatof(pt);
3043 		i = (INTBIG)(f * WHOLE);
3044 		if (i / WHOLE == f)
3045 		{
3046 			*type = VFRACT;
3047 			*addr = i;
3048 		} else
3049 		{
3050 			*type = VFLOAT;
3051 			*addr = castint(f);
3052 		}
3053 		break;
3054 	}
3055 }
3056 
3057 /*
3058  * routine to make an abbreviation for the string "pt" in upper case if
3059  * "upper" is true
3060  */
makeabbrev(CHAR * pt,BOOLEAN upper)3061 CHAR *makeabbrev(CHAR *pt, BOOLEAN upper)
3062 {
3063 	REGISTER void *infstr;
3064 
3065 	/* generate an abbreviated name for this prototype */
3066 	infstr = initinfstr();
3067 	while (*pt != 0)
3068 	{
3069 		if (isalpha(*pt))
3070 		{
3071 			if (isupper(*pt))
3072 			{
3073 				if (upper) addtoinfstr(infstr, *pt); else
3074 					addtoinfstr(infstr, (CHAR)(tolower(*pt)));
3075 			} else
3076 			{
3077 				if (!upper) addtoinfstr(infstr, *pt); else
3078 					addtoinfstr(infstr, (CHAR)(toupper(*pt)));
3079 			}
3080 			while (isalpha(*pt)) pt++;
3081 		}
3082 		while (!isalpha(*pt) && *pt != 0) pt++;
3083 	}
3084 	return(returninfstr(infstr));
3085 }
3086 
3087 /*
3088  * routine to report the name of the internal unit in "units".
3089  */
unitsname(INTBIG units)3090 CHAR *unitsname(INTBIG units)
3091 {
3092 	switch (units & INTERNALUNITS)
3093 	{
3094 		case INTUNITHNM:   return(_("half-nanometer"));
3095 		case INTUNITHDMIC: return(_("half-decimicron"));
3096 	}
3097 	return(x_("?"));
3098 }
3099 
3100 /************************* INTERNATIONALIZATION *************************/
3101 
3102 CHAR *db_stoppingreason[] =
3103 {
3104 	N_("Contour gather"),		/* STOPREASONCONTOUR */
3105 	N_("DRC"),					/* STOPREASONDRC */
3106 	N_("Playback"),				/* STOPREASONPLAYBACK */
3107 	N_("Binary"),				/* STOPREASONBINARY */
3108 	N_("CIF"),					/* STOPREASONCIF */
3109 	N_("DXF"),					/* STOPREASONDXF */
3110 	N_("EDIF"),					/* STOPREASONEDIF */
3111 	N_("VHDL"),					/* STOPREASONVHDL */
3112 	N_("Compaction"),			/* STOPREASONCOMPACT */
3113 	N_("ERC"),					/* STOPREASONERC */
3114 	N_("Check-in"),				/* STOPREASONCHECKIN */
3115 	N_("Lock wait"),			/* STOPREASONLOCK */
3116 	N_("Network comparison"),	/* STOPREASONNCC */
3117 	N_("Port exploration"),		/* STOPREASONPORT */
3118 	N_("Routing"),				/* STOPREASONROUTING */
3119 	N_("Silicon compiler"),		/* STOPREASONSILCOMP */
3120 	N_("Display"),				/* STOPREASONDISPLAY */
3121 	N_("Simulation"),			/* STOPREASONSIMULATE */
3122 	N_("Deck generation"),		/* STOPREASONDECK */
3123 	N_("SPICE"),				/* STOPREASONSPICE */
3124 	N_("Check"),				/* STOPREASONCHECK */
3125 	N_("Array"),				/* STOPREASONARRAY */
3126 	N_("Iteration"),			/* STOPREASONITERATE */
3127 	N_("Replace"),				/* STOPREASONREPLACE */
3128 	N_("Spread"),				/* STOPREASONSPREAD */
3129 	N_("Execution"),			/* STOPREASONEXECUTE */
3130 	N_("Command file"),			/* STOPREASONCOMFILE */
3131 	N_("Selection"),			/* STOPREASONSELECT */
3132 	N_("Tracking"),				/* STOPREASONTRACK */
3133 	N_("Network evaluation"),	/* STOPREASONNETWORK */
3134 	0
3135 };
3136 
3137 /*
3138  * Routine to translate internal strings in the database.
3139  */
db_inittranslation(void)3140 void db_inittranslation(void)
3141 {
3142 	REGISTER INTBIG i;
3143 
3144 	/* pretranslate the reasons for stopping */
3145 	for(i=0; db_stoppingreason[i] != 0; i++)
3146 		db_stoppingreason[i] = TRANSLATE(db_stoppingreason[i]);
3147 }
3148 
3149 /*
3150  * Routine to ensure that dialog "dia" is translated.
3151  */
DiaTranslate(DIALOG * dia)3152 void DiaTranslate(DIALOG *dia)
3153 {
3154 	REGISTER INTBIG j;
3155 	DIALOGITEM *item;
3156 
3157 	if (dia->translated != 0) return;
3158 	dia->translated = 1;
3159 	if (dia->movable != 0) dia->movable = TRANSLATE(dia->movable);
3160 	for(j=0; j<dia->items; j++)
3161 	{
3162 		item = &dia->list[j];
3163 		switch (item->type&ITEMTYPE)
3164 		{
3165 			case BUTTON:
3166 			case DEFBUTTON:
3167 			case CHECK:
3168 			case RADIO:
3169 			case MESSAGE:
3170 			case EDITTEXT:
3171 				if (*item->msg != 0)
3172 					item->msg = TRANSLATE(item->msg);
3173 				break;
3174 		}
3175 	}
3176 }
3177 
3178 /*
3179  * routine to convert "word" to its plural form, depending on the value
3180  * of "amount" (if "amount" is zero or multiple, pluralize the word).
3181  */
makeplural(CHAR * word,INTBIG amount)3182 CHAR *makeplural(CHAR *word, INTBIG amount)
3183 {
3184 	INTBIG needed, len;
3185 
3186 	if (amount == 1) return(word);
3187 	len = estrlen(word);
3188 	if (len == 0) return(word);
3189 
3190 	/* code cannot be called by multiple procesors: uses globals */
3191 	NOT_REENTRANT;
3192 
3193 	needed = len + 2;
3194 	if (needed > db_pluralbufsize)
3195 	{
3196 		if (db_pluralbufsize > 0) efree(db_pluralbuffer);
3197 		db_pluralbufsize = 0;
3198 		db_pluralbuffer = (CHAR *)emalloc(needed * SIZEOFCHAR, db_cluster);
3199 		if (db_pluralbuffer == 0) return(word);
3200 		db_pluralbufsize = needed;
3201 	}
3202 	estrcpy(db_pluralbuffer, word);
3203 	if (isupper(word[len-1])) estrcat(db_pluralbuffer, x_("S")); else
3204 		estrcat(db_pluralbuffer, x_("s"));
3205 	return(db_pluralbuffer);
3206 }
3207 
3208 /************************* STRING MANIPULATION *************************/
3209 
3210 /*
3211  * this routine dynamically allocates a string to hold "str" and places
3212  * that string at "*addr".  The routine returns true if the allocation
3213  * cannot be done.  Memory is allocated from virtual cluster "cluster".
3214  */
3215 #ifdef DEBUGMEMORY
_allocstring(CHAR ** addr,CHAR * str,CLUSTER * cluster,CHAR * module,INTBIG line)3216 BOOLEAN _allocstring(CHAR **addr, CHAR *str, CLUSTER *cluster, CHAR *module, INTBIG line)
3217 {
3218 	*addr = (CHAR *)_emalloc((estrlen(str)+1) * SIZEOFCHAR, cluster, module, line);
3219 	if (*addr == 0)
3220 	{
3221 		*addr = NOSTRING;
3222 		(void)db_error(DBNOMEM|DBALLOCSTRING);
3223 		return(TRUE);
3224 	}
3225 	(void)estrcpy(*addr, str);
3226 	return(FALSE);
3227 }
3228 #else
allocstring(CHAR ** addr,CHAR * str,CLUSTER * cluster)3229 BOOLEAN allocstring(CHAR **addr, CHAR *str, CLUSTER *cluster)
3230 {
3231 	*addr = (CHAR *)emalloc((estrlen(str)+1) * SIZEOFCHAR, cluster);
3232 	if (*addr == 0)
3233 	{
3234 		*addr = NOSTRING;
3235 		(void)db_error(DBNOMEM|DBALLOCSTRING);
3236 		return(TRUE);
3237 	}
3238 	(void)estrcpy(*addr, str);
3239 	return(FALSE);
3240 }
3241 #endif
3242 
3243 /*
3244  * this routine assumes that there is a dynamically allocated string at
3245  * "*addr" and that it is to be replaced with another dynamically allocated
3246  * string from "str".  The routine returns true if the allocation
3247  * cannot be done.  Memory is allocated from virtual cluster "cluster".
3248  */
3249 #ifdef DEBUGMEMORY
_reallocstring(CHAR ** addr,CHAR * str,CLUSTER * cluster,CHAR * module,INTBIG line)3250 BOOLEAN _reallocstring(CHAR **addr, CHAR *str, CLUSTER *cluster, CHAR *module, INTBIG line)
3251 {
3252 	efree(*addr);
3253 	return(_allocstring(addr, str, cluster, module, line));
3254 }
3255 #else
reallocstring(CHAR ** addr,CHAR * str,CLUSTER * cluster)3256 BOOLEAN reallocstring(CHAR **addr, CHAR *str, CLUSTER *cluster)
3257 {
3258 	efree(*addr);
3259 	return(allocstring(addr, str, cluster));
3260 }
3261 #endif
3262 
3263 /*
3264  * Routine to convert character "c" to a case-insensitive character, with
3265  * special characters all ordered properly before the letters.
3266  */
db_initnamesame(void)3267 void db_initnamesame(void)
3268 {
3269 	REGISTER INTBIG i;
3270 
3271 	db_namesamefirst = FALSE;
3272 	for(i=0; i<256; i++)
3273 	{
3274 		db_namesametable[i] = (CHAR)i;
3275 		if (isupper(i)) db_namesametable[i] = tolower(i);
3276 		db_namesametable['[']  = 1;
3277 		db_namesametable['\\'] = 2;
3278 		db_namesametable[']']  = 3;
3279 		db_namesametable['^']  = 4;
3280 		db_namesametable['_']  = 5;
3281 		db_namesametable['`']  = 6;
3282 		db_namesametable['{']  = 7;
3283 		db_namesametable['|']  = 8;
3284 		db_namesametable['}']  = 9;
3285 		db_namesametable['~']  = 10;
3286 	}
3287 }
3288 
3289 /*
3290  * name matching routine: ignores case
3291  */
namesame(CHAR * pt1,CHAR * pt2)3292 INTBIG namesame(CHAR *pt1, CHAR *pt2)
3293 {
3294 	REGISTER INTBIG c1, c2;
3295 
3296 	if (db_namesamefirst) db_initnamesame();
3297 	for(;;)
3298 	{
3299 #ifdef _UNICODE
3300 		c1 = *pt1++;   c2 = *pt2++;
3301 		if (c1 < 256) c1 = db_namesametable[c1];
3302 		if (c2 < 256) c2 = db_namesametable[c2];
3303 #else
3304 		c1 = db_namesametable[*pt1++ & 0377];
3305 		c2 = db_namesametable[*pt2++ & 0377];
3306 #endif
3307 		if (c1 != c2) return(c1-c2);
3308 		if (c1 == 0) break;
3309 	}
3310 	return(0);
3311 }
3312 
namesamen(CHAR * pt1,CHAR * pt2,INTBIG count)3313 INTBIG namesamen(CHAR *pt1, CHAR *pt2, INTBIG count)
3314 {
3315 	REGISTER INTBIG c1, c2, i;
3316 
3317 	if (db_namesamefirst) db_initnamesame();
3318 	for(i=0; i<count; i++)
3319 	{
3320 #ifdef _UNICODE
3321 		c1 = *pt1++;   c2 = *pt2++;
3322 		if (c1 < 256) c1 = db_namesametable[c1];
3323 		if (c2 < 256) c2 = db_namesametable[c2];
3324 #else
3325 		c1 = db_namesametable[*pt1++ & 0377];
3326 		c2 = db_namesametable[*pt2++ & 0377];
3327 #endif
3328 		if (c1 != c2) return(c1-c2);
3329 		if (c1 == 0) break;
3330 	}
3331 	return(0);
3332 }
3333 
3334 /*
3335  * Routine to compare two names "name1" and "name2" and return an
3336  * integer giving their sorting order (0 if equal, nonzero according
3337  * to order) in the same manner as any other string compare EXCEPT
3338  * that it considers numbers properly, so that the string "in10" comes
3339  * after the string "in9".
3340  */
namesamenumeric(CHAR * name1,CHAR * name2)3341 INTBIG namesamenumeric(CHAR *name1, CHAR *name2)
3342 {
3343 	REGISTER CHAR *pt1, *pt2, pt1save, pt2save, *number1, *number2,
3344 		*nameend1, *nameend2;
3345 	REGISTER INTBIG pt1index, pt2index, pt1number, pt2number;
3346 	REGISTER INTBIG compare;
3347 
3348 	number1 = 0;
3349 	for(pt1 = name1; *pt1 != 0; pt1++)
3350 	{
3351 		if (*pt1 == '[') break;
3352 		if (isdigit(*pt1) == 0) number1 = 0; else
3353 		{
3354 			if (number1 == 0) number1 = pt1;
3355 		}
3356 	}
3357 	for(pt1 = name1; *pt1 != 0; pt1++)
3358 		if (*pt1 == '[' && isdigit(pt1[1])) break;
3359 	number2 = 0;
3360 	for(pt2 = name2; *pt2 != 0; pt2++)
3361 	{
3362 		if (*pt2 == '[') break;
3363 		if (isdigit(*pt2) == 0) number2 = 0; else
3364 		{
3365 			if (number2 == 0) number2 = pt2;
3366 		}
3367 	}
3368 	for(pt2 = name2; *pt2 != 0; pt2++)
3369 		if (*pt2 == '[' && isdigit(pt2[1])) break;
3370 	nameend1 = pt1;   nameend2 = pt2;
3371 	pt1number = pt2number = 0;
3372 	pt1index = pt2index = 0;
3373 	if (number1 != 0 && number2 != 0)
3374 	{
3375 		pt1number = myatoi(number1);
3376 		pt2number = myatoi(number2);
3377 		if (pt1number != pt2number) compare = 0; else
3378 		{
3379 			/* make sure the text is the same if the numbers are the same */
3380 			pt1save = *nameend1;   *nameend1 = 0;
3381 			pt2save = *nameend2;   *nameend2 = 0;
3382 			compare = namesame(name1, name2);
3383 			*nameend2 = pt2save;   *nameend1 = pt1save;
3384 		}
3385 		if (compare == 0)
3386 		{
3387 			nameend1 = number1;
3388 			nameend2 = number2;
3389 		}
3390 	}
3391 	if (*pt1 == '[') pt1index = myatoi(&pt1[1]);
3392 	if (*pt2 == '[') pt2index = myatoi(&pt2[1]);
3393 
3394 	pt1save = *nameend1;   *nameend1 = 0;
3395 	pt2save = *nameend2;   *nameend2 = 0;
3396 	compare = namesame(name1, name2);
3397 	*nameend2 = pt2save;   *nameend1 = pt1save;
3398 	if (compare != 0) return(compare);
3399 	if (pt1number != pt2number) return(pt1number - pt2number);
3400 	return(pt1index - pt2index);
3401 }
3402 
3403 #ifdef _UNICODE
3404 /*
3405  * Convert "string" from wide to 8-bit.
3406  */
estring1byte(CHAR * string)3407 CHAR1 *estring1byte(CHAR *string)
3408 {
3409 	REGISTER INTBIG len;
3410 	static CHAR1 *retbuf;
3411 	static INTBIG retbufsize = 0;
3412 
3413 	len = estrlen(string) + 1;
3414 	if (len * 3 > retbufsize)
3415 	{
3416 		if (retbufsize > 0) efree((CHAR *)retbuf);
3417 		retbufsize = 0;
3418 		retbuf = (CHAR1 *)emalloc(len*3, us_tool->cluster);
3419 		if (retbuf == 0) return(b_(""));
3420 	}
3421 	wcstombs(retbuf, string, retbufsize);
3422 	return(retbuf);
3423 }
3424 
3425 #define STRLEN1BYTE strlen
3426 
3427 /*
3428  * Convert "string" from 8-bit to wide.
3429  */
estring2byte(CHAR1 * string)3430 CHAR *estring2byte(CHAR1 *string)
3431 {
3432 	REGISTER INTBIG len;
3433 	static CHAR *retbuf;
3434 	static INTBIG retbufsize = 0;
3435 
3436 	len = STRLEN1BYTE(string) + 1;
3437 	if (len > retbufsize)
3438 	{
3439 		if (retbufsize > 0) efree((CHAR *)retbuf);
3440 		retbufsize = 0;
3441 		retbuf = (CHAR *)emalloc(len*SIZEOFCHAR, us_tool->cluster);
3442 		if (retbuf == 0) return(x_(""));
3443 	}
3444 	mbtowc(retbuf, string, len);
3445 	return(retbuf);
3446 }
3447 #endif
3448 
3449 /******************** STRING PARSING ROUTINES ********************/
3450 
3451 /*
3452  * routine to scan off the next keyword in the string at "*ptin".  The string
3453  * is terminated by any of the characters in "brk".  The string is returned
3454  * (-1 if error) and the string pointer is advanced to the break character
3455  */
getkeyword(CHAR ** ptin,CHAR * brk)3456 CHAR *getkeyword(CHAR **ptin, CHAR *brk)
3457 {
3458 	REGISTER CHAR *pt2, *b, *pt;
3459 	REGISTER INTBIG len;
3460 
3461 	/* code cannot be called by multiple procesors: uses globals */
3462 	NOT_REENTRANT;
3463 
3464 	/* skip leading blanks */
3465 	pt = *ptin;
3466 	while (*pt == ' ' || *pt == '\t') pt++;
3467 
3468 	/* remember starting point */
3469 	pt2 = pt;
3470 	for(;;)
3471 	{
3472 		if (*pt2 == 0) break;
3473 		for(b = brk; *b != 0; b++) if (*pt2 == *b) break;
3474 		if (*b != 0) break;
3475 		pt2++;
3476 	}
3477 	len = pt2 - pt;
3478 	if (len+1 > db_keywordbufferlength)
3479 	{
3480 		if (db_keywordbufferlength != 0) efree(db_keywordbuffer);
3481 		db_keywordbufferlength = 0;
3482 		db_keywordbuffer = (CHAR *)emalloc((len+1) * SIZEOFCHAR, el_tempcluster);
3483 		if (db_keywordbuffer == 0)
3484 		{
3485 			ttyputnomemory();
3486 			return(NOSTRING);
3487 		}
3488 		db_keywordbufferlength = len+1;
3489 	}
3490 	(void)estrncpy(db_keywordbuffer, pt, len);
3491 	db_keywordbuffer[len] = 0;
3492 	*ptin = pt2;
3493 	return(db_keywordbuffer);
3494 }
3495 
3496 /*
3497  * routine to return the next nonblank character in the string pointed
3498  * to by "ptin".  The pointer is advanced past that character.
3499  */
tonextchar(CHAR ** ptin)3500 CHAR tonextchar(CHAR **ptin)
3501 {
3502 	REGISTER CHAR *pt, ret;
3503 
3504 	/* skip leading blanks */
3505 	pt = *ptin;
3506 	while (*pt == ' ' || *pt == '\t') pt++;
3507 	ret = *pt;
3508 	if (ret != 0) pt++;
3509 	*ptin = pt;
3510 	return(ret);
3511 }
3512 
3513 /*
3514  * routine to return the next nonblank character in the string pointed
3515  * to by "ptin".  The pointer is advanced to that character.
3516  */
seenextchar(CHAR ** ptin)3517 CHAR seenextchar(CHAR **ptin)
3518 {
3519 	REGISTER CHAR *pt;
3520 
3521 	/* skip leading blanks */
3522 	pt = *ptin;
3523 	while (*pt == ' ' || *pt == '\t') pt++;
3524 	*ptin = pt;
3525 	return(*pt);
3526 }
3527 
3528 /******************** INFINITE STRING PACKAGE ********************/
3529 
3530 /*
3531  * this package provides the ability to build an arbitrarily large string.
3532  * The first routine called must be "initinfstr()" which returns a "string object".
3533  * After that, make any number of calls to "addtoinfstr()", "addstringtoinfstr()",
3534  * or "formatinfstr()".  At any time, call "getinfstr()" to return the string.
3535  * When done, call "doneinfstr".
3536  */
3537 
3538 /*
3539  * Routine to initialize a new infinite string.  Returns the address of the
3540  * infinite string object (zero on error).
3541  */
initinfstr(void)3542 void *initinfstr(void)
3543 {
3544 	REGISTER INTBIG i;
3545 	REGISTER INFSTR *inf;
3546 
3547 	if (!db_firstinf)
3548 	{
3549 		/* first time through: build a free-list with many blocks */
3550 		if (ensurevalidmutex(&db_infstrmutex, TRUE)) return(0);
3551 		db_firstinf = TRUE;
3552 		db_firstfreeinfstr = NOINFSTR;
3553 		db_lastfreeinfstr = NOINFSTR;
3554 		for(i=0; i<INFSTRCOUNT; i++)
3555 		{
3556 			inf = (INFSTR *)emalloc(sizeof (INFSTR), db_cluster);
3557 			if (inf == 0)
3558 			{
3559 				(void)db_error(DBNOMEM|DBINITINFSTR);
3560 				return(0);
3561 			}
3562 			inf->infstr = (CHAR *)emalloc((INFSTRDEFAULT+1) * SIZEOFCHAR, db_cluster);
3563 			if (inf->infstr == 0)
3564 			{
3565 				(void)db_error(DBNOMEM|DBINITINFSTR);
3566 				return(0);
3567 			}
3568 			inf->infstrlength = INFSTRDEFAULT;
3569 
3570 			/* append it to the end of the list */
3571 			inf->nextinfstr = NOINFSTR;
3572 			if (db_lastfreeinfstr == NOINFSTR)
3573 			{
3574 				db_firstfreeinfstr = inf;
3575 			} else
3576 			{
3577 				db_lastfreeinfstr->nextinfstr = inf;
3578 			}
3579 			db_lastfreeinfstr = inf;
3580 		}
3581 	}
3582 
3583 	/* see if there is a free one on the list */
3584 	inf = NOINFSTR;
3585 	if (db_multiprocessing) emutexlock(db_infstrmutex);
3586 	if (db_firstfreeinfstr != NOINFSTR)
3587 	{
3588 		inf = db_firstfreeinfstr;
3589 		db_firstfreeinfstr = inf->nextinfstr;
3590 		if (db_firstfreeinfstr == NOINFSTR)
3591 			db_lastfreeinfstr = NOINFSTR;
3592 	}
3593 	if (db_multiprocessing) emutexunlock(db_infstrmutex);
3594 
3595 	/* if none in free list, allocate a new one */
3596 	if (inf == NOINFSTR)
3597 	{
3598 		inf = (INFSTR *)emalloc(sizeof (INFSTR), db_cluster);
3599 		if (inf == 0)
3600 		{
3601 			(void)db_error(DBNOMEM|DBINITINFSTR);
3602 			return(0);
3603 		}
3604 	}
3605 	inf->infstrptr = 0;
3606 	inf->infstr[inf->infstrptr] = 0;
3607 	return(inf);
3608 }
3609 
addtoinfstr(void * vinf,CHAR ch)3610 void addtoinfstr(void *vinf, CHAR ch)
3611 {
3612 	REGISTER CHAR *str;
3613 	REGISTER INFSTR *inf;
3614 
3615 	if (vinf == 0) return;
3616 	inf = (INFSTR *)vinf;
3617 	if (inf->infstrptr >= inf->infstrlength)
3618 	{
3619 		str = (CHAR *)emalloc((inf->infstrlength*2+1) * SIZEOFCHAR, db_cluster);
3620 		if (str == 0)
3621 		{
3622 			(void)db_error(DBNOMEM|DBADDTOINFSTR);
3623 			return;
3624 		}
3625 		inf->infstrlength *= 2;
3626 		(void)estrcpy(str, inf->infstr);
3627 		efree(inf->infstr);
3628 		inf->infstr = str;
3629 	}
3630 	inf->infstr[inf->infstrptr++] = ch;
3631 	inf->infstr[inf->infstrptr] = 0;
3632 }
3633 
addstringtoinfstr(void * vinf,CHAR * pp)3634 void addstringtoinfstr(void *vinf, CHAR *pp)
3635 {
3636 	REGISTER CHAR *str;
3637 	REGISTER INTBIG l, i, ori;
3638 	REGISTER INFSTR *inf;
3639 
3640 	if (vinf == 0) return;
3641 	inf = (INFSTR *)vinf;
3642 
3643 	if (pp == 0) l = 0; else
3644 		l = estrlen(pp);
3645 	if (inf->infstrptr+l >= inf->infstrlength)
3646 	{
3647 		ori = inf->infstrlength;
3648 		while (inf->infstrptr+l >= inf->infstrlength)
3649 			inf->infstrlength *= 2;
3650 		str = (CHAR *)emalloc((inf->infstrlength+1) * SIZEOFCHAR, db_cluster);
3651 		if (str == 0)
3652 		{
3653 			inf->infstrlength = ori;
3654 			(void)db_error(DBNOMEM|DBADDSTRINGTOINFSTR);
3655 			return;
3656 		}
3657 		(void)estrcpy(str, inf->infstr);
3658 		efree(inf->infstr);
3659 		inf->infstr = str;
3660 	}
3661 	for(i=0; i<l; i++) inf->infstr[inf->infstrptr++] = pp[i];
3662 	inf->infstr[inf->infstrptr] = 0;
3663 }
3664 
3665 /*
3666  * Routine to do a variable-arguments "sprintf" to line "line" which
3667  * is "len" in size.
3668  */
evsnprintf(CHAR * line,INTBIG len,CHAR * msg,va_list ap)3669 void evsnprintf(CHAR *line, INTBIG len, CHAR *msg, va_list ap)
3670 {
3671 #ifdef HAVE_VSNPRINTF
3672 #  ifdef _UNICODE
3673 #    ifdef WIN32
3674 	(void)_vsnwprintf(line, len-1, msg, ap);
3675 #    else
3676 	(void)vswprintf(line, len-1, msg, ap);
3677 #    endif
3678 #  else
3679 	(void)vsnprintf(line, len-1, msg, ap);
3680 #  endif
3681 #else
3682 	(void)evsprintf(line, msg, ap);
3683 #endif
3684 }
3685 
formatinfstr(void * vinf,CHAR * msg,...)3686 void formatinfstr(void *vinf, CHAR *msg, ...)
3687 {
3688 	va_list ap;
3689 	CHAR line[8192];
3690 
3691 	var_start(ap, msg);
3692 	evsnprintf(line, 8192, msg, ap);
3693 	va_end(ap);
3694 	addstringtoinfstr(vinf, line);
3695 }
3696 
returninfstr(void * vinf)3697 CHAR *returninfstr(void *vinf)
3698 {
3699 	REGISTER CHAR *retval;
3700 	REGISTER INFSTR *inf;
3701 
3702 	if (vinf == 0) return(x_(""));
3703 	inf = (INFSTR *)vinf;
3704 
3705 	/* get the inifinite string */
3706 	retval = inf->infstr;
3707 
3708 	/* append it to the end of the list */
3709 	if (db_multiprocessing) emutexlock(db_infstrmutex);
3710 	inf->nextinfstr = NOINFSTR;
3711 	if (db_lastfreeinfstr == NOINFSTR)
3712 	{
3713 		db_firstfreeinfstr = inf;
3714 	} else
3715 	{
3716 		db_lastfreeinfstr->nextinfstr = inf;
3717 	}
3718 	db_lastfreeinfstr = inf;
3719 	if (db_multiprocessing) emutexunlock(db_infstrmutex);
3720 
3721 	/* return the string */
3722 	return(retval);
3723 }
3724 
3725 /****************************** FILES IN CELLS ******************************/
3726 
3727 typedef struct
3728 {
3729 	CHAR   **strings;
3730 	INTBIG   stringcount;
3731 	INTBIG   stringlimit;
3732 	CLUSTER *cluster;
3733 } STRINGARRAY;
3734 
3735 /*
3736  * Routine to create an object for gathering arrays of strings.
3737  * Returns a pointer that can be used with "addtostringarray", "keepstringarray", "stringarraytotextcell", and
3738  * "killstringarray"
3739  * Returns zero one error.
3740  */
newstringarray(CLUSTER * cluster)3741 void *newstringarray(CLUSTER *cluster)
3742 {
3743 	STRINGARRAY *sa;
3744 
3745 	sa = (STRINGARRAY *)emalloc(sizeof (STRINGARRAY), cluster);
3746 	if (sa == 0) return(0);
3747 	sa->stringlimit = 0;
3748 	sa->stringcount = 0;
3749 	sa->cluster = cluster;
3750 	return((void *)sa);
3751 }
3752 
killstringarray(void * vsa)3753 void killstringarray(void *vsa)
3754 {
3755 	STRINGARRAY *sa;
3756 
3757 	sa = (STRINGARRAY *)vsa;
3758 	if (sa == 0) return;
3759 	clearstrings(sa);
3760 	if (sa->stringlimit > 0) efree((CHAR *)sa->strings);
3761 	efree((CHAR *)sa);
3762 }
3763 
clearstrings(void * vsa)3764 void clearstrings(void *vsa)
3765 {
3766 	STRINGARRAY *sa;
3767 	REGISTER INTBIG i;
3768 
3769 	sa = (STRINGARRAY *)vsa;
3770 	if (sa == 0) return;
3771 	for(i=0; i<sa->stringcount; i++) efree(sa->strings[i]);
3772 	sa->stringcount = 0;
3773 }
3774 
addtostringarray(void * vsa,CHAR * string)3775 void addtostringarray(void *vsa, CHAR *string)
3776 {
3777 	REGISTER CHAR **newbuf;
3778 	REGISTER INTBIG i, newlimit;
3779 	STRINGARRAY *sa;
3780 
3781 	sa = (STRINGARRAY *)vsa;
3782 	if (sa == 0) return;
3783 	if (sa->stringcount >= sa->stringlimit)
3784 	{
3785 		newlimit = sa->stringlimit * 2;
3786 		if (newlimit <= 0) newlimit = 10;
3787 		if (newlimit < sa->stringcount) newlimit = sa->stringcount;
3788 		newbuf = (CHAR **)emalloc(newlimit * (sizeof (CHAR *)), sa->cluster);
3789 		if (newbuf == 0) return;
3790 		for(i=0; i<sa->stringcount; i++) newbuf[i] = sa->strings[i];
3791 		if (sa->stringlimit > 0) efree((CHAR *)sa->strings);
3792 		sa->strings = newbuf;
3793 		sa->stringlimit += 10;
3794 	}
3795 	if (allocstring(&sa->strings[sa->stringcount], string, sa->cluster)) return;
3796 	sa->stringcount++;
3797 }
3798 
3799 /*
3800  * routine called when done adding lines to string array "vsa".  The collection of lines is
3801  * stored in the "FACET_message" variable on the cell "np".  It is made permanent if
3802  * "permanent" is true.
3803  */
stringarraytotextcell(void * vsa,NODEPROTO * np,BOOLEAN permanent)3804 void stringarraytotextcell(void *vsa, NODEPROTO *np, BOOLEAN permanent)
3805 {
3806 	STRINGARRAY *sa;
3807 	REGISTER INTBIG type;
3808 
3809 	sa = (STRINGARRAY *)vsa;
3810 	if (sa == 0) return;
3811 	if (sa->stringcount <= 0) return;
3812 	type = VSTRING|VISARRAY|(sa->stringcount<<VLENGTHSH);
3813 	if (!permanent) type |= VDONTSAVE;
3814 	(void)setvalkey((INTBIG)np, VNODEPROTO, el_cell_message_key, (INTBIG)sa->strings, type);
3815 }
3816 
3817 /*
3818  * routine called when done adding lines to string array "vsa".  The collection of lines is
3819  * returned to you.
3820  */
getstringarray(void * vsa,INTBIG * count)3821 CHAR **getstringarray(void *vsa, INTBIG *count)
3822 {
3823 	STRINGARRAY *sa;
3824 
3825 	sa = (STRINGARRAY *)vsa;
3826 	if (sa == 0) { *count = 0;   return(0); }
3827 	*count = sa->stringcount;
3828 	return(sa->strings);
3829 }
3830 
3831 /************************* TIME HANDLING *************************/
3832 
3833 /*
3834  * Time functions are centralized here to account for different time
3835  * systems on different computers.
3836  */
3837 
3838 #define TRUECTIME ctime
3839 #define TRUEFOPEN fopen
3840 
3841 /*
3842  * return the current time.
3843  */
ectime(time_t * curtime)3844 CHAR *ectime(time_t *curtime)
3845 {
3846 #ifdef _UNICODE
3847 #  ifdef WIN32
3848 	return(_wctime(curtime));
3849 #  else
3850 	CHAR1 *result;
3851 
3852 	result = TRUECTIME(curtime);
3853 	return(string2byte(result));
3854 #  endif
3855 #else
3856 	return(TRUECTIME(curtime));
3857 #endif
3858 }
3859 
3860 /*
3861  * open the file.
3862  */
efopen(CHAR * filename,CHAR * mode)3863 FILE *efopen(CHAR *filename, CHAR *mode)
3864 {
3865 #ifdef _UNICODE
3866 #  ifdef WIN32
3867 	return(_wfopen(filename, mode));
3868 #  else
3869 	REGISTER CHAR1 *filename1, *mode1;
3870 
3871 	filename1 = string1byte(filename);
3872 	mode1 = string1byte(mode);
3873 	return(TRUEFOPEN(filename1, mode1));
3874 #  endif
3875 #else
3876 	return(TRUEFOPEN(filename, mode));
3877 #endif
3878 }
3879 
3880 /*
3881  * Routine to return the current time in seconds since January 1, 1970.
3882  */
getcurrenttime(void)3883 time_t getcurrenttime(void)
3884 {
3885 	time_t curtime;
3886 
3887 	(void)time(&curtime);
3888 	curtime -= machinetimeoffset();
3889 	return(curtime);
3890 }
3891 
3892 /*
3893  * Routine to convert the time "curtime" to a string describing the date.
3894  * This string does *NOT* have a carriage-return after it.
3895  */
timetostring(time_t curtime)3896 CHAR *timetostring(time_t curtime)
3897 {
3898 	static CHAR timebuf[30];
3899 
3900 	/* code cannot be called by multiple procesors: uses globals */
3901 	NOT_REENTRANT;
3902 
3903 	curtime += machinetimeoffset();
3904 	estrcpy(timebuf, ectime(&curtime));
3905 	timebuf[24] = 0;
3906 	return(timebuf);
3907 }
3908 
3909 /*
3910  * Routine to parse the time in "curtime" and convert it to the year,
3911  * month (0 = January), and day of month.
3912  */
parsetime(time_t curtime,INTBIG * year,INTBIG * month,INTBIG * mday,INTBIG * hour,INTBIG * minute,INTBIG * second)3913 void parsetime(time_t curtime, INTBIG *year, INTBIG *month, INTBIG *mday,
3914 	INTBIG *hour, INTBIG *minute, INTBIG *second)
3915 {
3916 	struct tm *tm;
3917 
3918 	curtime += machinetimeoffset();
3919 	tm = localtime(&curtime);
3920 	*month = tm->tm_mon;
3921 	*mday = tm->tm_mday;
3922 	*year = tm->tm_year + 1900;
3923 	*hour = tm->tm_hour;
3924 	*minute = tm->tm_min;
3925 	*second = tm->tm_sec;
3926 }
3927 
parsemonth(CHAR * monthname)3928 INTBIG parsemonth(CHAR *monthname)
3929 {
3930 	REGISTER INTBIG mon;
3931 	static CHAR *name[] = {x_("Jan"), x_("Feb"), x_("Mar"), x_("Apr"), x_("May"), x_("Jun"), x_("Jul"),
3932 		x_("Aug"), x_("Sep"), x_("Oct"), x_("Nov"), x_("Dec")};
3933 
3934 	for (mon=0; mon<12; mon++)
3935 		if (namesame(name[mon], monthname) == 0) break;
3936 	if (mon < 12) return(mon+1);
3937 	return(0);
3938 }
3939 
3940 /******************** SUBROUTINES FOR FILE I/O ****************/
3941 
setupfiletype(CHAR * extension,CHAR * winfilter,INTBIG mactype,BOOLEAN binary,CHAR * shortname,CHAR * longname)3942 INTBIG setupfiletype(CHAR *extension, CHAR *winfilter, INTBIG mactype, BOOLEAN binary,
3943 	CHAR *shortname, CHAR *longname)
3944 {
3945 	FILETYPES *newlist;
3946 	INTBIG oldcount, i;
3947 
3948 	oldcount = db_filetypecount;
3949 	db_filetypecount++;
3950 	newlist = (FILETYPES *)emalloc(db_filetypecount * (sizeof (FILETYPES)), db_cluster);
3951 	for(i=0; i<oldcount; i++)
3952 	{
3953 		newlist[i].extension = db_filetypeinfo[i].extension;
3954 		newlist[i].winfilter = db_filetypeinfo[i].winfilter;
3955 		newlist[i].mactype   = db_filetypeinfo[i].mactype;
3956 		newlist[i].binary    = db_filetypeinfo[i].binary;
3957 		newlist[i].shortname = db_filetypeinfo[i].shortname;
3958 		newlist[i].longname  = db_filetypeinfo[i].longname;
3959 	}
3960 	if (oldcount > 0) efree((CHAR *)db_filetypeinfo);
3961 	db_filetypeinfo = newlist;
3962 	(void)allocstring(&db_filetypeinfo[oldcount].extension, extension, db_cluster);
3963 	(void)allocstring(&db_filetypeinfo[oldcount].winfilter, winfilter, db_cluster);
3964 	db_filetypeinfo[oldcount].mactype = mactype;
3965 	db_filetypeinfo[oldcount].binary = binary;
3966 	(void)allocstring(&db_filetypeinfo[oldcount].shortname, shortname, db_cluster);
3967 	(void)allocstring(&db_filetypeinfo[oldcount].longname, longname, db_cluster);
3968 	return(oldcount);
3969 }
3970 
3971 /*
3972  * Routine to return the extension, short name, and long name of file type "filetype".
3973  */
describefiletype(INTBIG filetype,CHAR ** extension,CHAR ** winfilter,INTBIG * mactype,BOOLEAN * binary,CHAR ** shortname,CHAR ** longname)3974 void describefiletype(INTBIG filetype, CHAR **extension, CHAR **winfilter, INTBIG *mactype,
3975 	BOOLEAN *binary, CHAR **shortname, CHAR **longname)
3976 {
3977 	REGISTER INTBIG index;
3978 
3979 	index = filetype & FILETYPE;
3980 	*extension = db_filetypeinfo[index].extension;
3981 	*winfilter = db_filetypeinfo[index].winfilter;
3982 	*mactype   = db_filetypeinfo[index].mactype;
3983 	*binary    = db_filetypeinfo[index].binary;
3984 	*shortname = db_filetypeinfo[index].shortname;
3985 	*longname  = db_filetypeinfo[index].longname;
3986 }
3987 
3988 /*
3989  * Routine to return the filetype associated with short name "shortname".
3990  * Returns zero on error.
3991  */
getfiletype(CHAR * shortname)3992 INTBIG getfiletype(CHAR *shortname)
3993 {
3994 	REGISTER INTBIG i;
3995 
3996 	for(i=0; i<db_filetypecount; i++)
3997 		if (namesame(shortname, db_filetypeinfo[i].shortname) == 0) return(i);
3998 	return(0);
3999 }
4000 
4001 /*
4002  * routine to find the actual file name in a path/file specification.  The
4003  * input path name is in "file" and a pointer to the file name is returned.
4004  * Note that this routine handles the directory separating characters for
4005  * all operating systems ('\' on Windows, '/' on UNIX, and ':' on Macintosh)
4006  * because the strings may be from other systems.
4007  */
skippath(CHAR * file)4008 CHAR *skippath(CHAR *file)
4009 {
4010 	REGISTER CHAR *pt;
4011 
4012 	pt = &file[estrlen(file)-1];
4013 	while (pt != file && pt[-1] != '\\' && pt[-1] != '/' && pt[-1] != ':') pt--;
4014 	return(pt);
4015 }
4016 
4017 /*
4018  * routine to return the size of file whose stream is "f"
4019  */
filesize(FILE * f)4020 INTBIG filesize(FILE *f)
4021 {
4022 	INTBIG savepos, endpos;
4023 
4024 	savepos = ftell(f);
4025 	(void)fseek(f, 0L, 2);	/* SEEK_END */
4026 	endpos = ftell(f);
4027 	(void)fseek(f, savepos, 0);	/* SEEK_SET */
4028 	return(endpos);
4029 }
4030 
4031 /*
4032  * routine to open a file whose name is "file".  The type of the file is in "filetype"
4033  * (a descriptor created by "setupfiletype()").  The file may be in the directory "otherdir".
4034  * The stream is returned (NULL if it can't be found), and the true name of the file (with
4035  * extensions and proper directory) is returned in "truename".
4036  */
xopen(CHAR * file,INTBIG filetype,CHAR * otherdir,CHAR ** truename)4037 FILE *xopen(CHAR *file, INTBIG filetype, CHAR *otherdir, CHAR **truename)
4038 {
4039 	REGISTER FILE *f;
4040 	REGISTER CHAR *pp;
4041 	INTBIG mactype;
4042 	BOOLEAN binary;
4043 	CHAR rarg[3], *extension, *winfilter, *shortname, *longname;
4044 
4045 	/* determine extension to use */
4046 	describefiletype(filetype&FILETYPE, &extension, &winfilter, &mactype, &binary, &shortname, &longname);
4047 
4048 	/* determine argument to "fopen" */
4049 	if ((filetype&FILETYPEWRITE) != 0) estrcpy(rarg, x_("w")); else
4050 		if ((filetype&FILETYPEAPPEND) != 0) estrcpy(rarg, x_("a")); else
4051 			estrcpy(rarg, x_("r"));
4052 	if (binary != 0) estrcat(rarg, x_("b"));
4053 
4054 	/* remove "~" from the file name */
4055 	pp = truepath(file);
4056 
4057 	/* add the extension and look for the file */
4058 	if (*extension != 0)
4059 	{
4060 		f = db_tryfile(pp, rarg, extension, (CHAR *)0, truename);
4061 		if (f != NULL) return(f);
4062 	}
4063 
4064 	/* try the file directly */
4065 	f = db_tryfile(pp, rarg, (CHAR *)0, (CHAR *)0, truename);
4066 	if (f != NULL) return(f);
4067 
4068 	/* if no other directory given, stop now */
4069 	if (otherdir == 0 || *otherdir == 0) return(NULL);
4070 
4071 	/* if directory path is in file name, stop now */
4072 	if (*pp == '/') return(NULL);
4073 
4074 	/* try the file in the other directory with the extension */
4075 	f = db_tryfile(pp, rarg, extension, otherdir, truename);
4076 	if (f != NULL) return(f);
4077 
4078 	/* try the file in the other directory with no extension */
4079 	f = db_tryfile(pp, rarg, (CHAR *)0, otherdir, truename);
4080 	if (f != NULL) return(f);
4081 	return(NULL);
4082 }
4083 
4084 /*
4085  * routine to try to find a file with name "pp", read with argument "rarg",
4086  * with extension "extension" (if it is nonzero) and in directory "otherdir"
4087  * (if it is nonzero).  Returns the file descriptor, and a pointer to the
4088  * actual file name in "truename"
4089  */
db_tryfile(CHAR * pp,CHAR * rarg,CHAR * extension,CHAR * otherdir,CHAR ** truename)4090 FILE *db_tryfile(CHAR *pp, CHAR *rarg, CHAR *extension, CHAR *otherdir, CHAR **truename)
4091 {
4092 	REGISTER INTBIG len;
4093 	REGISTER CHAR *pt;
4094 
4095 	len = estrlen(pp) + 1;
4096 	if (extension != 0) len += estrlen(extension) + 1;
4097 	if (otherdir != 0) len += estrlen(otherdir) + 1;
4098 	if (len > db_tryfilenamelen)
4099 	{
4100 		if (db_tryfilenamelen != 0) efree(db_tryfilename);
4101 		db_tryfilenamelen = 0;
4102 		db_tryfilename = (CHAR *)emalloc(len * SIZEOFCHAR, db_cluster);
4103 		if (db_tryfilename == 0) return(NULL);
4104 		db_tryfilenamelen = len;
4105 	}
4106 	db_tryfilename[0] = 0;
4107 	if (otherdir != 0)
4108 	{
4109 		(void)estrcat(db_tryfilename, otherdir);
4110 		len = estrlen(db_tryfilename);
4111 		if (db_tryfilename[len-1] != DIRSEP)
4112 		{
4113 			db_tryfilename[len++] = DIRSEP;
4114 			db_tryfilename[len] = 0;
4115 		}
4116 	}
4117 	(void)estrcat(db_tryfilename, pp);
4118 	if (extension != 0)
4119 	{
4120 		(void)estrcat(db_tryfilename, x_("."));
4121 		(void)estrcat(db_tryfilename, extension);
4122 	}
4123 
4124 	/* now extend this to the full path name */
4125 	pt = fullfilename(db_tryfilename);
4126 	len = estrlen(pt) + 1;
4127 	if (len > db_tryfilenamelen)
4128 	{
4129 		efree(db_tryfilename);
4130 		db_tryfilenamelen = 0;
4131 		db_tryfilename = (CHAR *)emalloc(len * SIZEOFCHAR, db_cluster);
4132 		if (db_tryfilename == 0) return(NULL);
4133 		db_tryfilenamelen = len;
4134 	}
4135 	estrcpy(db_tryfilename, pt);
4136 	*truename = db_tryfilename;
4137 	return(efopen(db_tryfilename, rarg));
4138 }
4139 
4140 /*
4141  * Routine to create the file "name" and return a stream pointer.  "filetype" is the type
4142  * of file being created (a descriptor created by "setupfiletype()").  If "prompt" is zero, the path
4143  * is already fully set and the user should not be further querried.  Otherwise,
4144  * "prompt" is the string to use when describing this file (and the final name selected
4145  * by the user is returned in "truename").  The routine returns zero on error.  However,
4146  * to differentiate errors in file creation from user aborts, the "truename" is set to zero
4147  * if the user aborts the command.
4148  */
xcreate(CHAR * name,INTBIG filetype,CHAR * prompt,CHAR ** truename)4149 FILE *xcreate(CHAR *name, INTBIG filetype, CHAR *prompt, CHAR **truename)
4150 {
4151 	REGISTER FILE *f;
4152 	REGISTER CHAR *pt, *next;
4153 	CHAR warg[3], *extension, *winfilter, *shortname, *longname;
4154 	static CHAR truenamelocal[256];
4155 	INTBIG mactype;
4156 	BOOLEAN binary;
4157 
4158 	/* determine extension to use */
4159 	describefiletype(filetype&FILETYPE, &extension, &winfilter, &mactype, &binary, &shortname, &longname);
4160 	estrcpy(warg, x_("w"));
4161 	if (binary) estrcat(warg, x_("b"));
4162 
4163 	if ((filetype&FILETYPETEMPFILE) != 0)
4164 	{
4165 #ifdef HAVE_MKSTEMP
4166 		int chn;
4167 		chn = mkstemp(name);
4168 		if (chn < 0) return(0);
4169 		f = fdopen(chn, warg);
4170 #else
4171 #  ifdef HAVE_MKTEMP
4172 		mktemp(name);
4173 #  endif
4174 		f = efopen(name, warg);
4175 #endif
4176 		*truename = name;
4177 	} else
4178 	{
4179 		pt = truepath(name);
4180 		if (prompt != 0 && (us_tool->toolstate&USEDIALOGS) != 0)
4181 		{
4182 			next = (CHAR *)fileselect(prompt, filetype | FILETYPEWRITE, pt);
4183 			if (next == 0 || *next == 0)
4184 			{
4185 				if (truename != 0) *truename = 0;
4186 				return(NULL);
4187 			}
4188 			setdefaultcursortype(NULLCURSOR);		/* the hourglass cursor */
4189 			pt = next;
4190 		}
4191 		if (truename != 0)
4192 		{
4193 			(void)estrcpy(truenamelocal, pt);
4194 			*truename = truenamelocal;
4195 		}
4196 
4197 		/* determine argument to "fopen" */
4198 		f = efopen(pt, warg);
4199 	}
4200 #ifdef	MACOS
4201 	if (f != NULL)
4202 	{
4203 		void mac_settypecreator(CHAR*, INTBIG, INTBIG);
4204 
4205 		if (mactype == 'TEXT')
4206 		{
4207 			mac_settypecreator(pt, 'TEXT', 'ttxt');
4208 		} else
4209 		{
4210 			mac_settypecreator(pt, mactype, 'Elec');
4211 		}
4212 	}
4213 #endif
4214 	return(f);
4215 }
4216 
4217 /*
4218  * Routine to append to the file "name" and return a stream pointer
4219  */
xappend(CHAR * name)4220 FILE *xappend(CHAR *name)
4221 {
4222 	return(efopen(truepath(name), x_("a")));
4223 }
4224 
4225 /*
4226  * Routine to close stream "f"
4227  */
xclose(FILE * f)4228 void xclose(FILE *f)
4229 {
4230 	(void)fclose(f);
4231 }
4232 
4233 /*
4234  * Routine to flush stream "f"
4235  */
xflushbuf(FILE * f)4236 void xflushbuf(FILE *f)
4237 {
4238 	(void)fflush(f);
4239 }
4240 
4241 /*
4242  * Routine to report the EOF condition on stream "f"
4243  */
xeof(FILE * f)4244 BOOLEAN xeof(FILE *f)
4245 {
4246 	if (feof(f) != 0) return(TRUE);
4247 	return(FALSE);
4248 }
4249 
4250 /*
4251  * Routine to seek to position "pos", nature "nat", on stream "f"
4252  */
xseek(FILE * f,INTBIG pos,INTBIG nat)4253 void xseek(FILE *f, INTBIG pos, INTBIG nat)
4254 {
4255 	fseek(f, pos, nat);
4256 }
4257 
4258 /*
4259  * Routine to return the current position in stream "f"
4260  */
xtell(FILE * f)4261 INTBIG xtell(FILE *f)
4262 {
4263 	return(ftell(f));
4264 }
4265 
4266 /*
4267  * Routine to write the formatted message "s" with parameters "p1" through "p9"
4268  * to stream "f".
4269  */
xprintf(FILE * f,CHAR * s,...)4270 void xprintf(FILE *f, CHAR *s, ...)
4271 {
4272 	va_list ap;
4273 
4274 	var_start(ap, s);
4275 	(void)evfprintf(f, s, ap);
4276 	va_end(ap);
4277 }
4278 
4279 /*
4280  * Routine to get the next character in stream "f"
4281  */
xgetc(FILE * f)4282 INTSML xgetc(FILE *f)
4283 {
4284 	return(getc(f));
4285 }
4286 
4287 /*
4288  * Routine to "unget" the character "c" back to stream "f"
4289  */
xungetc(CHAR c,FILE * f)4290 void xungetc(CHAR c, FILE *f)
4291 {
4292 	(void)ungetc(c, f);
4293 }
4294 
4295 /*
4296  * Routine to put the character "c" into stream "f"
4297  */
xputc(CHAR c,FILE * f)4298 void xputc(CHAR c, FILE *f)
4299 {
4300 	putc(c, f);
4301 }
4302 
4303 /*
4304  * Routine to put the string "s" into stream "f"
4305  */
xputs(CHAR * s,FILE * f)4306 void xputs(CHAR *s, FILE *f)
4307 {
4308 	(void)efputs(s, f);
4309 }
4310 
4311 /*
4312  * Routine to read "count" elements of size "size" from stream "f" into the buffer
4313  * at "buf".  Returns the number of objects read (ideally, "size").
4314  */
xfread(UCHAR1 * buf,INTBIG size,INTBIG count,FILE * f)4315 INTBIG xfread(UCHAR1 *buf, INTBIG size, INTBIG count, FILE *f)
4316 {
4317 	REGISTER INTBIG ret;
4318 
4319 	for(;;)
4320 	{
4321 		ret = fread(buf, size, count, f);
4322 		if (ret == count || feof(f) != 0) break;
4323 		clearerr(f);
4324 	}
4325 	return(ret);
4326 }
4327 
4328 /*
4329  * Routine to write "count" elements of size "size" to stream "f" from the buffer
4330  * at "buf".  Returns the number of bytes written.
4331  */
xfwrite(UCHAR1 * buf,INTBIG size,INTBIG count,FILE * f)4332 INTBIG xfwrite(UCHAR1 *buf, INTBIG size, INTBIG count, FILE *f)
4333 {
4334 	REGISTER INTBIG ret;
4335 
4336 	for(;;)
4337 	{
4338 		ret = fwrite(buf, size, count, f);
4339 		if (ret == count || feof(f) != 0) break;
4340 		clearerr(f);
4341 	}
4342 	return(ret);
4343 }
4344 
4345 /*
4346  * routine to read a line of text from a file.  The file is in stream "file"
4347  * and the text is placed in the array "line" which is only "limit" characters
4348  * long.  The routine returns false if sucessful, true if end-of-file is
4349  * reached.
4350  */
xfgets(CHAR * line,INTBIG limit,FILE * file)4351 BOOLEAN xfgets(CHAR *line, INTBIG limit, FILE *file)
4352 {
4353 	REGISTER CHAR *pp;
4354 	REGISTER INTBIG c, total;
4355 
4356 	pp = line;
4357 	total = 1;
4358 	for(;;)
4359 	{
4360 		c = xgetc(file);
4361 		if (c == EOF)
4362 		{
4363 			if (pp == line) return(TRUE);
4364 			break;
4365 		}
4366 		*pp = (CHAR)c;
4367 		if (*pp == '\n' || *pp == '\r') break;
4368 		pp++;
4369 		if ((++total) >= limit) break;
4370 	}
4371 	*pp = 0;
4372 	return(FALSE);
4373 }
4374 
4375 /******************** SUBROUTINES FOR ENCRYPTION ****************/
4376 
4377 /*
4378  * A one-rotor machine designed along the lines of Enigma but considerably trivialized
4379  */
4380 # define ROTORSZ 256		/* a power of two */
4381 # define MASK    (ROTORSZ-1)
myencrypt(CHAR * text,CHAR * key)4382 void myencrypt(CHAR *text, CHAR *key)
4383 {
4384 	INTBIG ic, i, k, temp, n1, n2, nr1, nr2;
4385 	UINTBIG random;
4386 	INTBIG seed;
4387 	CHAR *pt, t1[ROTORSZ], t2[ROTORSZ], t3[ROTORSZ], deck[ROTORSZ], readable[ROTORSZ];
4388 
4389 	/* first setup the machine */
4390 	estrcpy(readable, x_("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789+-"));
4391 	seed = 123;
4392 	for (i=0; i<13; i++) seed = seed*key[i] + i;
4393 	for(i=0; i<ROTORSZ; i++)
4394 	{
4395 		t1[i] = (CHAR)i;
4396 		t3[i] = 0;
4397 		deck[i] = (CHAR)i;
4398 	}
4399 	for(i=0; i<ROTORSZ; i++)
4400 	{
4401 		seed = 5*seed + key[i%13];
4402 		random = seed % 65521;
4403 		k = ROTORSZ-1 - i;
4404 		ic = (random&MASK) % (k+1);
4405 		random >>= 8;
4406 		temp = t1[k];
4407 		t1[k] = t1[ic];
4408 		t1[ic] = (CHAR)temp;
4409 		if (t3[k] != 0) continue;
4410 		ic = (random&MASK) % k;
4411 		while (t3[ic] != 0) ic = (ic+1) % k;
4412 		t3[k] = (CHAR)ic;
4413 		t3[ic] = (CHAR)k;
4414 	}
4415 	for(i=0; i<ROTORSZ; i++) t2[t1[i]&MASK] = (CHAR)i;
4416 
4417 	/* now run the machine */
4418 	n1 = 0;
4419 	n2 = 0;
4420 	nr2 = 0;
4421 	for(pt = text; *pt; pt++)
4422 	{
4423 		nr1 = deck[n1]&MASK;
4424 		nr2 = deck[nr1]&MASK;
4425 		i = t2[(t3[(t1[(*pt+nr1)&MASK]+nr2)&MASK]-nr2)&MASK]-nr1;
4426 		*pt = readable[i&63];
4427 		n1++;
4428 		if (n1 == ROTORSZ)
4429 		{
4430 			n1 = 0;
4431 			n2++;
4432 			if (n2 == ROTORSZ) n2 = 0;
4433 			db_shuffle(deck, key);
4434 		}
4435 	}
4436 }
4437 
db_shuffle(CHAR * deck,CHAR * key)4438 void db_shuffle(CHAR *deck, CHAR *key)
4439 {
4440 	INTBIG i, ic, k, temp;
4441 	UINTBIG random;
4442 	static INTBIG seed = 123;
4443 
4444 	for(i=0; i<ROTORSZ; i++)
4445 	{
4446 		seed = 5*seed + key[i%13];
4447 		random = seed % 65521;
4448 		k = ROTORSZ-1 - i;
4449 		ic = (random&MASK) % (k+1);
4450 		temp = deck[k];
4451 		deck[k] = deck[ic];
4452 		deck[ic] = (CHAR)temp;
4453 	}
4454 }
4455