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