1 /* -*- tab-width: 4 -*-
2 *
3 * Electric(tm) VLSI Design System
4 *
5 * File: conlinprs.c
6 * Linear inequality constraint system: text file parsing
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 /*
33 * Component syntax:
34 * declare INST1[DECLOPTS], INST2[DECLOPTS], ...,INSTN[DECLOPTS] PROTOTYPE;
35 * DECLOPTS: size=(X,Y)
36 * location=(X,Y)
37 * rotation=R[t]
38 * VARIABLE=VALUE
39 *
40 * Export syntax:
41 * export [EXPOPTS] PORTNAME is COMPONENT{:PORT};
42 * EXPOPTS: type={INPUT|OUTPUT|BIDIRECTIONAL|POWER|GROUND|
43 * CLOCK|CLOCK1|CLOCK2|CLOCK3|CLOCK4|CLOCK5|CLOCK6}
44 * VARIABLE=VALUE
45 *
46 * Connection syntax:
47 * connect [CONNOPTS] COMPONENT1{:PORT1}
48 * {DIR AMT {or more|less}} {[DX,DY]} to
49 * COMPONENT2{:PORT2};
50 * CONNOPTS: layer=LAYER
51 * width=WIDTH
52 * VARIABLE=VALUE
53 */
54 #include "global.h"
55 #include "conlin.h"
56
57 /* prototypes for local routines */
58 static BOOLEAN cli_doparseexport(CHAR*, BOOLEAN, EXPORT*);
59 static BOOLEAN cli_doparseconn(CHAR*, BOOLEAN, CONNECTION*);
60 static BOOLEAN cli_doparsecomp(CHAR**, CHAR*, BOOLEAN, COMPONENTDEC*);
61 static BOOLEAN cli_parsetwovalue(INTBIG*, INTBIG*, CHAR*, CHAR**, BOOLEAN);
62 static ATTR *cli_getattr(CHAR*, CHAR**, CHAR*, BOOLEAN);
63 static void cli_freeattr(ATTR*);
64
65 /*
66 * routine to determine the type of text in "line"
67 */
cli_linetype(CHAR * line)68 INTBIG cli_linetype(CHAR *line)
69 {
70 while (*line == ' ' || *line == '\t') line++;
71 if (*line == 0 || *line == ';') return(LINECOMM);
72 if (namesamen(line, x_("declare"), 7) == 0) return(LINEDECL);
73 if (namesamen(line, x_("connect"), 4) == 0) return(LINECONN);
74 if (namesamen(line, x_("export"), 6) == 0) return(LINEEXPORT);
75 if (namesamen(line, x_("begincell"), 9) == 0) return(LINEBEGIN);
76 if (namesamen(line, x_("endcell"), 7) == 0) return(LINEEND);
77 return(LINEUNKN);
78 }
79
80 /*
81 * routine to parse a "begincell" command and return the cell name. This
82 * cell name must be deallocated when done. If "quiet" is true, do not
83 * print error messages. Returns -1 if the line cannot be parsed
84 */
cli_parsebegincell(CHAR * str,BOOLEAN quiet)85 CHAR *cli_parsebegincell(CHAR *str, BOOLEAN quiet)
86 {
87 CHAR *pt;
88 REGISTER CHAR *st;
89
90 pt = str;
91
92 /* get the "begincell" keyword */
93 st = getkeyword(&pt, x_(" \t"));
94 if (st == NOSTRING) return(NOSTRING);
95 if (namesame(st, x_("begincell")) != 0)
96 {
97 if (!quiet) ttyputerr(M_("Missing 'begincell' keyword in '%s'"), str);
98 return(NOSTRING);
99 }
100
101 /* get the cell name */
102 st = getkeyword(&pt, x_(" \t;"));
103 if (st == NOSTRING) return(NOSTRING);
104
105 /* make sure it ends with a ";" */
106 if (tonextchar(&pt) != ';')
107 {
108 if (!quiet) ttyputerr(M_("Missing ';' in '%s'"), str);
109 return(NOSTRING);
110 }
111 if (tonextchar(&pt) != 0)
112 {
113 if (!quiet) ttyputerr(M_("Unexpected text after ';' in '%s'"), str);
114 return(NOSTRING);
115 }
116 (void)allocstring(&pt, st, el_tempcluster);
117 return(pt);
118 }
119
120 /*
121 * routine to parse an "export" command and return a structure describing
122 * it. This must be deallocated when done. If "quiet" is true, do not
123 * print error messages. Returns NOEXPORT if the line cannot be parsed.
124 */
cli_parseexport(CHAR * str,BOOLEAN quiet)125 EXPORT *cli_parseexport(CHAR *str, BOOLEAN quiet)
126 {
127 REGISTER EXPORT *e;
128
129 /* allocate an export object */
130 e = (EXPORT *)emalloc(sizeof (EXPORT), el_tempcluster);
131 if (e == 0)
132 {
133 if (!quiet) ttyputnomemory();
134 return(NOEXPORT);
135 }
136 e->portname = e->component = e->subport = 0;
137 e->flag = 0;
138 e->bits = 0;
139 e->firstattr = NOATTR;
140
141 if (cli_doparseexport(str, quiet, e))
142 {
143 cli_deleteexport(e);
144 return(NOEXPORT);
145 }
146 return(e);
147 }
148
149 /*
150 * routine to parse the bulk of an "export" statement, assuming that "str"
151 * points to the beginning of the line and "quiet" is the verbose error flag.
152 * Fills the "e" structure and returns false if successful.
153 */
cli_doparseexport(CHAR * str,BOOLEAN quiet,EXPORT * e)154 BOOLEAN cli_doparseexport(CHAR *str, BOOLEAN quiet, EXPORT *e)
155 {
156 CHAR *pt, *opt;
157 REGISTER CHAR *st;
158 REGISTER ATTR *a;
159 REGISTER INTBIG i;
160
161 pt = str;
162
163 /* get the "export" keyword */
164 st = getkeyword(&pt, x_(" \t"));
165 if (st == NOSTRING) return(TRUE);
166 if (namesame(st, x_("export")) != 0)
167 {
168 if (!quiet) ttyputerr(M_("Missing 'export' keyword in '%s'"), str);
169 return(TRUE);
170 }
171
172 /* get options */
173 if (seenextchar(&pt) == '[')
174 {
175 (void)tonextchar(&pt);
176 for(;;)
177 {
178 /* get name of option */
179 opt = getkeyword(&pt, x_(" \t="));
180 if (opt == NOSTRING) return(TRUE);
181 if (*opt == 0)
182 {
183 if (!quiet) ttyputerr(M_("Missing option name in '%s'"), str);
184 return(TRUE);
185 }
186 if (tonextchar(&pt) != '=')
187 {
188 if (!quiet) ttyputerr(M_("Missing '=' after %s option"), opt);
189 return(TRUE);
190 }
191
192 if (namesame(opt, x_("type")) == 0)
193 {
194 st = getkeyword(&pt, x_(" \t],"));
195 if (st == NOSTRING) return(TRUE);
196 if (*st == 0)
197 {
198 if (!quiet) ttyputerr(M_("Missing type in '%s'"), str);
199 return(TRUE);
200 }
201 if (namesame(st, x_("input")) == 0) e->bits |= INPORT; else
202 if (namesame(st, x_("output")) == 0) e->bits |= OUTPORT; else
203 if (namesame(st, x_("bidirectional"))== 0) e->bits |= BIDIRPORT; else
204 if (namesame(st, x_("power")) == 0) e->bits |= PWRPORT; else
205 if (namesame(st, x_("ground")) == 0) e->bits |= GNDPORT; else
206 if (namesame(st, x_("clock")) == 0) e->bits |= CLKPORT; else
207 if (namesame(st, x_("clock1")) == 0) e->bits |= C1PORT; else
208 if (namesame(st, x_("clock2")) == 0) e->bits |= C2PORT; else
209 if (namesame(st, x_("clock3")) == 0) e->bits |= C3PORT; else
210 if (namesame(st, x_("clock4")) == 0) e->bits |= C4PORT; else
211 if (namesame(st, x_("clock5")) == 0) e->bits |= C5PORT; else
212 if (namesame(st, x_("clock6")) == 0) e->bits |= C6PORT; else
213 if (namesame(st, x_("refout")) == 0) e->bits |= REFOUTPORT; else
214 if (namesame(st, x_("refin")) == 0) e->bits |= REFINPORT; else
215 if (namesame(st, x_("refbase")) == 0) e->bits |= REFBASEPORT; else
216 {
217 if (!quiet) ttyputerr(M_("Invalid port type in '%s'"), str);
218 return(TRUE);
219 }
220 e->flag |= EXPCHAR;
221 } else
222 {
223 /* arbitrary keyword processing */
224 a = cli_getattr(opt, &pt, str, quiet);
225 if (a == NOATTR) return(TRUE);
226 a->nextattr = e->firstattr;
227 e->firstattr = a;
228 e->flag |= EXPATTR;
229 }
230
231 /* clean up after option parsing */
232 i = tonextchar(&pt);
233 if (i == ']') break;
234 if (i != ',')
235 {
236 if (!quiet) ttyputerr(M_("Missing ',' separating options in '%s'"), str);
237 return(TRUE);
238 }
239 }
240 }
241
242 /* get the exported port name */
243 st = getkeyword(&pt, x_(" \t;"));
244 if (st == NOSTRING) return(TRUE);
245 (void)allocstring(&e->portname, st, el_tempcluster);
246
247 /* get the "is" keyword */
248 st = getkeyword(&pt, x_(" \t"));
249 if (st == NOSTRING) return(TRUE);
250 if (namesame(st, x_("is")) != 0)
251 {
252 if (!quiet) ttyputerr(M_("Missing 'is' keyword in '%s'"), str);
253 return(TRUE);
254 }
255
256 /* get the component name */
257 st = getkeyword(&pt, x_(" \t;:"));
258 if (st == NOSTRING) return(TRUE);
259 (void)allocstring(&e->component, st, el_tempcluster);
260
261 /* see if there is a ":" next */
262 if (seenextchar(&pt) == ':')
263 {
264 /* get the subport name */
265 (void)tonextchar(&pt);
266 st = getkeyword(&pt, x_(" \t;"));
267 if (st == NOSTRING) return(TRUE);
268 (void)allocstring(&e->subport, st, el_tempcluster);
269 }
270
271 /* make sure it ends with a ";" */
272 if (tonextchar(&pt) != ';')
273 {
274 if (!quiet) ttyputerr(M_("Missing ';' in '%s'"), str);
275 return(TRUE);
276 }
277 if (tonextchar(&pt) != 0)
278 {
279 if (!quiet) ttyputerr(M_("Unexpected text after ';' in '%s'"), str);
280 return(TRUE);
281 }
282 return(FALSE);
283 }
284
285 /*
286 * routine to parse the connection line in the string "str" and return a
287 * CONNECTION structure that describes it. If "quiet" is true, do not
288 * print error messages. Returns NOCONNECTION on error.
289 */
cli_parseconn(CHAR * str,BOOLEAN quiet)290 CONNECTION *cli_parseconn(CHAR *str, BOOLEAN quiet)
291 {
292 REGISTER CONNECTION *dcl;
293
294 /* allocate a declaration object */
295 dcl = (CONNECTION *)emalloc(sizeof (CONNECTION), el_tempcluster);
296 if (dcl == 0)
297 {
298 if (!quiet) ttyputnomemory();
299 return(NOCONNECTION);
300 }
301 dcl->firstcons = NOCONS;
302 dcl->flag = 0;
303 dcl->firstattr = NOATTR;
304
305 /* parse the remainder of the connection declaration */
306 if (cli_doparseconn(str, quiet, dcl))
307 {
308 /* parsing failed */
309 cli_deleteconnection(dcl);
310 return(NOCONNECTION);
311 }
312
313 /* parse succeeded */
314 return(dcl);
315 }
316
317 /*
318 * routine to parse the bulk of a "connect" statement, assuming that "str"
319 * points to the beginning of the line and "quiet" is the verbose error flag.
320 * Fills the "dcl" structure and returns false if successful.
321 */
cli_doparseconn(CHAR * str,BOOLEAN quiet,CONNECTION * dcl)322 BOOLEAN cli_doparseconn(CHAR *str, BOOLEAN quiet, CONNECTION *dcl)
323 {
324 REGISTER CHAR *st, *opt;
325 CHAR *pt;
326 REGISTER CONS *cons, *lastcons;
327 REGISTER INTBIG i;
328 REGISTER ATTR *a;
329
330 pt = str;
331
332 st = getkeyword(&pt, x_(" \t"));
333 if (st == NOSTRING) return(TRUE);
334 if (namesame(st, x_("connect")) != 0)
335 {
336 if (!quiet) ttyputerr(M_("Missing 'connect' keyword in '%s'"), str);
337 return(TRUE);
338 }
339
340 /* get options */
341 if (seenextchar(&pt) == '[')
342 {
343 (void)tonextchar(&pt);
344 for(;;)
345 {
346 /* get name of option */
347 opt = getkeyword(&pt, x_(" \t="));
348 if (opt == NOSTRING) return(TRUE);
349 if (*opt == 0)
350 {
351 if (!quiet) ttyputerr(M_("Missing option name in '%s'"), str);
352 return(TRUE);
353 }
354 if (tonextchar(&pt) != '=')
355 {
356 if (!quiet) ttyputerr(M_("Missing '=' after %s option"), opt);
357 return(TRUE);
358 }
359
360 if (namesame(opt, x_("layer")) == 0)
361 {
362 st = getkeyword(&pt, x_(" \t],"));
363 if (st == NOSTRING) return(TRUE);
364 if (*st == 0)
365 {
366 if (!quiet) ttyputerr(M_("Missing layer in '%s'"), str);
367 return(TRUE);
368 }
369 (void)allocstring(&dcl->layer, st, el_tempcluster);
370 dcl->flag |= LAYERVALID;
371 } else if (namesame(opt, x_("width")) == 0)
372 {
373 st = getkeyword(&pt, x_(" \t],"));
374 if (st == NOSTRING) return(TRUE);
375 if (*st == 0)
376 {
377 if (!quiet) ttyputerr(M_("Missing width in '%s'"), str);
378 return(TRUE);
379 }
380 dcl->width = atola(st, 0);
381 dcl->flag |= WIDTHVALID;
382 } else
383 {
384 /* arbitrary keyword processing */
385 a = cli_getattr(opt, &pt, str, quiet);
386 if (a == NOATTR) return(TRUE);
387 a->nextattr = dcl->firstattr;
388 dcl->firstattr = a;
389 dcl->flag |= ATTRVALID;
390 }
391
392 /* clean up after option parsing */
393 i = tonextchar(&pt);
394 if (i == ']') break;
395 if (i != ',')
396 {
397 if (!quiet) ttyputerr(M_("Missing ',' separating options in '%s'"), str);
398 return(TRUE);
399 }
400 }
401 }
402
403 /* get the name of the first node */
404 st = getkeyword(&pt, x_(" \t;:"));
405 if (st == NOSTRING) return(TRUE);
406 if (*st == 0)
407 {
408 if (!quiet) ttyputerr(M_("Incorrect prototype syntax in '%s'"), str);
409 return(TRUE);
410 }
411 (void)allocstring(&dcl->end1, st, el_tempcluster);
412 dcl->flag |= END1VALID;
413 if (seenextchar(&pt) == ':')
414 {
415 /* parse the port prototype for end 1 */
416 (void)tonextchar(&pt);
417 st = getkeyword(&pt, x_(" \t;"));
418 if (st == NOSTRING) return(TRUE);
419 if (*st == 0)
420 {
421 if (!quiet) ttyputerr(M_("Incorrect port syntax in '%s'"), str);
422 return(TRUE);
423 }
424 (void)allocstring(&dcl->port1, st, el_tempcluster);
425 dcl->flag |= PORT1VALID;
426 }
427
428 /* look for the constraints */
429 lastcons = NOCONS;
430 for(;;)
431 {
432 st = getkeyword(&pt, x_(" \t;["));
433 if (st == NOSTRING) return(TRUE);
434
435 /* termination with the keyword "to" */
436 if (namesame(st, x_("to")) == 0) break;
437
438 /* handle the "[X,Y]" construct */
439 if (*st == 0)
440 {
441 if (tonextchar(&pt) != '[')
442 {
443 if (!quiet) ttyputerr(M_("Incorrect constraint syntax in '%s'"), str);
444 return(TRUE);
445 }
446 st = getkeyword(&pt, x_(" \t,"));
447 if (st == NOSTRING) return(TRUE);
448 dcl->xoff = atola(st, 0);
449 dcl->flag |= OFFSETVALID;
450 if (tonextchar(&pt) != ',')
451 {
452 if (!quiet) ttyputerr(M_("Missing comma in arc extent '%s'"), str);
453 return(TRUE);
454 }
455 st = getkeyword(&pt, x_(" \t]"));
456 dcl->yoff = atola(st, 0);
457 if (tonextchar(&pt) != ']')
458 {
459 if (!quiet) ttyputerr(M_("Missing ']' in arc extent '%s'"), str);
460 return(TRUE);
461 }
462 continue;
463 }
464
465 /* handle constraint directions */
466 if (namesame(st, x_("left")) != 0 && namesame(st, x_("right")) != 0 &&
467 namesame(st, x_("up")) != 0 && namesame(st, x_("down")) != 0)
468 {
469 if (!quiet) ttyputerr(M_("Invalid relationship '%s' in '%s'"), st, str);
470 return(TRUE);
471 }
472
473 /* create a CONS object for this constraint */
474 cons = (CONS *)emalloc(sizeof (CONS), el_tempcluster);
475 if (cons == 0)
476 {
477 if (!quiet) ttyputnomemory();
478 return(TRUE);
479 }
480 cons->nextcons = NOCONS;
481 if (lastcons == NOCONS) dcl->firstcons = cons; else
482 lastcons->nextcons = cons;
483 lastcons = cons;
484 (void)allocstring(&cons->direction, st, el_tempcluster);
485
486 /* get amount */
487 st = getkeyword(&pt, x_(" \t;["));
488 if (st == NOSTRING) return(TRUE);
489 cons->amount = atofr(st);
490
491 /* see if an "or" follows */
492 cons->flag = EQUAL;
493 if (seenextchar(&pt) == 'o')
494 {
495 st = getkeyword(&pt, x_(" \t;["));
496 if (st == NOSTRING) return(TRUE);
497 if (namesame(st, x_("or")) != 0)
498 {
499 if (!quiet) ttyputerr(M_("Unknown qualifier '%s' in '%s'"), st, str);
500 return(TRUE);
501 }
502 st = getkeyword(&pt, x_(" \t;["));
503 if (st == NOSTRING) return(TRUE);
504 cons->flag = 999;
505 if (namesame(st, x_("more")) == 0) cons->flag = GEQ;
506 if (namesame(st, x_("less")) == 0) cons->flag = LEQ;
507 if (cons->flag == 999)
508 {
509 if (!quiet) ttyputerr(M_("Unknown qualifier '%s' in '%s'"), st, str);
510 return(TRUE);
511 }
512 }
513 }
514
515 /* get the name of the second node */
516 st = getkeyword(&pt, x_(" \t;:["));
517 if (st == NOSTRING) return(TRUE);
518 if (*st == 0)
519 {
520 if (!quiet) ttyputerr(M_("Incorrect prototype syntax in '%s'"), str);
521 return(TRUE);
522 }
523 (void)allocstring(&dcl->end2, st, el_tempcluster);
524 dcl->flag |= END2VALID;
525 if (seenextchar(&pt) == ':')
526 {
527 /* parse the port prototype for end 2 */
528 (void)tonextchar(&pt);
529 st = getkeyword(&pt, x_(" \t;["));
530 if (st == NOSTRING) return(TRUE);
531 if (*st == 0)
532 {
533 if (!quiet) ttyputerr(M_("Incorrect port syntax in '%s'"), str);
534 return(TRUE);
535 }
536 (void)allocstring(&dcl->port2, st, el_tempcluster);
537 dcl->flag |= PORT2VALID;
538 }
539 if (tonextchar(&pt) != ';')
540 {
541 if (!quiet) ttyputerr(M_("Missing semicolon in '%s'"), str);
542 return(TRUE);
543 }
544 i = tonextchar(&pt);
545 if (i == 0) return(FALSE);
546 if (!quiet) ttyputerr(M_("Line '%s' should end with a semicolon"), str);
547 return(TRUE);
548 }
549
550 /*
551 * routine to parse the component line in the string "str" and return a
552 * COMPONENTDEC structure that describes it. If "quiet" is true, do not
553 * print error messages. Returns NOCOMPONENTDEC on error.
554 */
cli_parsecomp(CHAR * str,BOOLEAN quiet)555 COMPONENTDEC *cli_parsecomp(CHAR *str, BOOLEAN quiet)
556 {
557 REGISTER CHAR *st;
558 CHAR *pt;
559 REGISTER COMPONENTDEC *dcl;
560
561 /* allocate a declaration object */
562 dcl = (COMPONENTDEC *)emalloc(sizeof (COMPONENTDEC), el_tempcluster);
563 if (dcl == 0)
564 {
565 if (!quiet) ttyputnomemory();
566 return(NOCOMPONENTDEC);
567 }
568 dcl->firstcomponent = NOCOMPONENT;
569 dcl->protoname = 0;
570
571 /* initialize pointer into string */
572 pt = str;
573
574 /* look for "declare" keyword */
575 st = getkeyword(&pt, x_(" \t"));
576 if (st == NOSTRING || namesame(st, x_("declare")) != 0)
577 {
578 if (!quiet) ttyputerr(M_("Missing 'declare' keyword in '%s'"), str);
579 efree((CHAR *)dcl);
580 return(NOCOMPONENTDEC);
581 }
582
583 /* parse the remainder of the component declaration */
584 if (cli_doparsecomp(&pt, str, quiet, dcl))
585 {
586 /* parsing failed */
587 cli_deletecomponentdec(dcl);
588 return(NOCOMPONENTDEC);
589 }
590
591 /* parse succeeded */
592 return(dcl);
593 }
594
595 /*
596 * routine to parse the bulk of a "declare" statement, assuming that "pt"
597 * points to the text after "declare". "str" is the entire line and "quiet"
598 * is the verbose error flag. Fills the "dcl" structure and returns false
599 * if successful
600 */
cli_doparsecomp(CHAR ** pt,CHAR * str,BOOLEAN quiet,COMPONENTDEC * dcl)601 BOOLEAN cli_doparsecomp(CHAR **pt, CHAR *str, BOOLEAN quiet, COMPONENTDEC *dcl)
602 {
603 REGISTER CHAR *st, *opt;
604 REGISTER COMPONENT *compo, *listpos;
605 REGISTER INTBIG i;
606 REGISTER ATTR *a;
607
608 /* parse the instances */
609 listpos = NOCOMPONENT;
610 dcl->count = 0;
611 for(;;)
612 {
613 st = getkeyword(pt, x_(" \t,;["));
614 if (st == NOSTRING) return(TRUE);
615 if (*st == 0)
616 {
617 if (!quiet) ttyputerr(M_("Missing component name in '%s'"), str);
618 return(TRUE);
619 }
620
621 /* allocate a structure for the declared component */
622 compo = (COMPONENT *)emalloc(sizeof (COMPONENT), el_tempcluster);
623 if (compo == 0)
624 {
625 if (!quiet) ttyputnomemory();
626 return(TRUE);
627 }
628 compo->flag = 0;
629 compo->firstattr = NOATTR;
630
631 /* place the component name in the structure */
632 (void)allocstring(&compo->name, st, el_tempcluster);
633
634 /* link the component object into the declaration */
635 if (listpos == NOCOMPONENT) dcl->firstcomponent = compo; else
636 listpos->nextcomponent = compo;
637 compo->nextcomponent = NOCOMPONENT;
638 listpos = compo;
639 dcl->count++;
640
641 /* look for options */
642 if (seenextchar(pt) == '[')
643 {
644 (void)tonextchar(pt);
645 for(;;)
646 {
647 /* get name of option */
648 opt = getkeyword(pt, x_(" \t="));
649 if (opt == NOSTRING) return(TRUE);
650 if (*opt == 0)
651 {
652 if (!quiet) ttyputerr(M_("Missing option name in '%s'"), str);
653 return(TRUE);
654 }
655 if (tonextchar(pt) != '=')
656 {
657 if (!quiet) ttyputerr(M_("Missing '=' after %s option"), opt);
658 return(TRUE);
659 }
660
661 if (namesame(opt, x_("size")) == 0)
662 {
663 if (cli_parsetwovalue(&compo->sizex, &compo->sizey, x_("size"), pt, quiet))
664 return(TRUE);
665 compo->flag |= COMPSIZE;
666 } else if (namesame(opt, x_("location")) == 0)
667 {
668 if (cli_parsetwovalue(&compo->locx, &compo->locy, x_("location"), pt, quiet))
669 return(TRUE);
670 compo->flag |= COMPLOC;
671 } else if (namesame(opt, x_("rotation")) == 0)
672 {
673 st = getkeyword(pt, x_(" \t,]"));
674 if (st == NOSTRING) return(TRUE);
675 compo->rot = atofr(st)*10/WHOLE;
676 i = estrlen(st)-1;
677 if (st[i] == 't' || st[i] == 'T') compo->trans = 1; else
678 compo->trans = 0;
679 compo->flag |= COMPROT;
680 } else
681 {
682 /* arbitrary keyword processing */
683 a = cli_getattr(opt, pt, str, quiet);
684 if (a == NOATTR) return(TRUE);
685 a->nextattr = compo->firstattr;
686 compo->firstattr = a;
687 compo->flag |= COMPATTR;
688 }
689
690 /* clean up after option parsing */
691 i = tonextchar(pt);
692 if (i == ']') break;
693 if (i != ',')
694 {
695 if (!quiet) ttyputerr(M_("Missing ',' separating options in '%s'"), str);
696 return(TRUE);
697 }
698 }
699 }
700
701 /* more instances to parse? */
702 if (seenextchar(pt) != ',') break;
703 (void)tonextchar(pt);
704 }
705
706 /* take last string as prototype name */
707 st = getkeyword(pt, x_(" \t;"));
708 if (st == NOSTRING) return(TRUE);
709 if (*st == 0)
710 {
711 if (!quiet) ttyputerr(M_("Missing prototype name at start of '%s'"), str);
712 return(TRUE);
713 }
714 (void)allocstring(&dcl->protoname, st, el_tempcluster);
715
716 /* make sure line terminates properly */
717 if (tonextchar(pt) != ';')
718 {
719 if (!quiet) ttyputerr(M_("Missing semicolon in '%s'"), str);
720 return(TRUE);
721 }
722 i = tonextchar(pt);
723 if (i == 0) return(FALSE);
724 if (!quiet) ttyputerr(M_("Line '%s' should end with a semicolon"), str);
725 return(TRUE);
726 }
727
728 /*
729 * routine to parse the expression "(X,Y)" at "pt" in the string, placing
730 * the values in "x" and "y". The name of this option is in "name" and the
731 * verbose error message flag is in "quiet". Returns true on error
732 */
cli_parsetwovalue(INTBIG * x,INTBIG * y,CHAR * name,CHAR ** pt,BOOLEAN quiet)733 BOOLEAN cli_parsetwovalue(INTBIG *x, INTBIG *y, CHAR *name, CHAR **pt, BOOLEAN quiet)
734 {
735 REGISTER CHAR *st;
736
737 if (tonextchar(pt) != '(')
738 {
739 if (!quiet) ttyputerr(M_("Missing '(' after %s option"), name);
740 return(TRUE);
741 }
742 st = getkeyword(pt, x_(" \t,)]"));
743 if (st == NOSTRING) return(TRUE);
744 *x = atola(st, 0);
745 if (tonextchar(pt) != ',')
746 {
747 if (!quiet) ttyputerr(M_("Missing ',' in %s option"), name);
748 return(TRUE);
749 }
750 st = getkeyword(pt, x_(" \t)]"));
751 if (st == NOSTRING) return(TRUE);
752 *y = atola(st, 0);
753 if (tonextchar(pt) != ')')
754 {
755 if (!quiet) ttyputerr(M_("Missing ')' in size option"));
756 return(TRUE);
757 }
758 return(FALSE);
759 }
760
761 /*
762 * routine to parse the "VALUE" part of a "VARIABLE=VALUE" clause. The
763 * string is at "pt", the "VARIABLE" is in "opt", the original string is
764 * in "str", and the error verbose flag is in "quiet". Returns the attribute
765 * strucutre (NOATTR on error).
766 */
cli_getattr(CHAR * opt,CHAR ** pt,CHAR * str,BOOLEAN quiet)767 ATTR *cli_getattr(CHAR *opt, CHAR **pt, CHAR *str, BOOLEAN quiet)
768 {
769 REGISTER ATTR *a;
770 REGISTER CHAR *st;
771
772 a = (ATTR *)emalloc(sizeof (ATTR), el_tempcluster);
773 if (a == 0) return(NOATTR);
774
775 /* set keyword name */
776 (void)allocstring(&a->name, opt, el_tempcluster);
777
778 /* get value */
779 if (seenextchar(pt) == '"')
780 {
781 /* quoted string */
782 (void)tonextchar(pt);
783 st = getkeyword(pt, x_("\""));
784 if (st == NOSTRING) return(NOATTR);
785 (void)allocstring((CHAR **)&a->value, st, el_tempcluster);
786 a->type = VSTRING;
787 (void)tonextchar(pt);
788 } else
789 {
790 st = getkeyword(pt, x_(" \t,]"));
791 if (st == NOSTRING) return(NOATTR);
792 if (*st == 0)
793 {
794 if (!quiet) ttyputerr(M_("Missing value in '%s'"), str);
795 return(NOATTR);
796 }
797 if (isanumber(st))
798 {
799 a->value = myatoi(st);
800 a->type = VINTEGER;
801 } else
802 {
803 (void)allocstring((CHAR **)&a->value, st, el_tempcluster);
804 a->type = VSTRING;
805 }
806 }
807 return(a);
808 }
809
810 /***************************** HIGHER-LEVEL *****************************/
811
812 /*
813 * routine to find the node with the name "name" in the current graphics cell.
814 * Returns that node (NONODEINST if not found).
815 */
cli_findnodename(CHAR * name)816 NODEINST *cli_findnodename(CHAR *name)
817 {
818 REGISTER NODEINST *ni, *numni;
819 REGISTER VARIABLE *var;
820
821 for(ni = cli_curcell->firstnodeinst; ni != NONODEINST; ni = ni->nextnodeinst)
822 {
823 var = getvalkey((INTBIG)ni, VNODEINST, VSTRING, el_node_name_key);
824 if (var == NOVARIABLE) continue;
825 if (namesame(name, (CHAR *)var->addr) == 0) return(ni);
826 }
827
828 /* look for the special case of "NODEdddd" */
829 if (namesamen(name, x_("node"), 4) != 0) return(NONODEINST);
830 numni = (NODEINST *)myatoi(&name[4]);
831 for(ni = cli_curcell->firstnodeinst; ni != NONODEINST; ni = ni->nextnodeinst)
832 if (ni == numni) break;
833 return(ni);
834 }
835
836 /*
837 * routine to find the arc described by the line "line". Returns that arc
838 * (NOARCINST if not found).
839 */
cli_findarcname(CHAR * line)840 ARCINST *cli_findarcname(CHAR *line)
841 {
842 NODEINST *nA, *nB;
843 PORTPROTO *pA, *pB;
844 ARCPROTO *ap;
845 REGISTER CONNECTION *dcl;
846 REGISTER ARCINST *ai, *pai;
847 REGISTER INTBIG aend, bend;
848 INTBIG wid;
849
850 /* parse the line and get the endpoints */
851 dcl = cli_parseconn(line, FALSE);
852 if (dcl == NOCONNECTION) return(NOARCINST);
853 if (cli_pickwire(dcl, &nA, &pA, &nB, &pB, &ap, &wid, NOARCINST, NONODEINST, NONODEINST))
854 {
855 cli_deleteconnection(dcl);
856 return(NOARCINST);
857 }
858
859 /* look for this arc */
860 pai = NOARCINST;
861 for(ai = cli_curcell->firstarcinst; ai != NOARCINST; ai = ai->nextarcinst)
862 {
863 aend = bend = -1;
864 if (ai->end[0].nodeinst == nA) aend = 0; else
865 if (ai->end[1].nodeinst == nA) aend = 1;
866 if (ai->end[1].nodeinst == nB) bend = 1; else
867 if (ai->end[0].nodeinst == nB) bend = 0;
868 if (aend < 0 || bend < 0) continue;
869 if (aend == bend)
870 {
871 ttyputmsg(M_("To and From found on end %ld of arc %s"), aend, describearcinst(ai));
872 continue;
873 }
874 if ((dcl->flag&PORT1VALID) != 0 &&
875 ai->end[aend].portarcinst->proto != pA) continue;
876 if ((dcl->flag&PORT2VALID) != 0 &&
877 ai->end[bend].portarcinst->proto != pB) continue;
878 if ((dcl->flag&LAYERVALID) != 0 && ai->proto != ap) continue;
879
880 /* save a possible arc */
881 if (pai == NOARCINST) pai = ai;
882
883 /* check the width, just to be sure */
884 if (ai->width != wid) continue;
885 break;
886 }
887 cli_deleteconnection(dcl);
888 if (ai == NOARCINST) ai = pai;
889 return(ai);
890 }
891
892 /*
893 * routine to evaluate the wire described by the structure "conn" and fill the
894 * reference parameters "nA", "pA", "nB", "pB", "ap", and "wid". If "ai" is
895 * NOARCINST, use creation defaults, otherwise take defaults from that arcinst.
896 * If "allownamechange" is nonzero, allow unrecognized node names (but set the
897 * nodeinst pointer to NONODEINST). Returns true if there is an error.
898 */
cli_pickwire(CONNECTION * dcl,NODEINST ** nA,PORTPROTO ** pA,NODEINST ** nB,PORTPROTO ** pB,ARCPROTO ** ap,INTBIG * wid,ARCINST * defai,NODEINST * defnA,NODEINST * defnB)899 BOOLEAN cli_pickwire(CONNECTION *dcl, NODEINST **nA, PORTPROTO **pA, NODEINST **nB, PORTPROTO **pB,
900 ARCPROTO **ap, INTBIG *wid, ARCINST *defai, NODEINST *defnA, NODEINST *defnB)
901 {
902 REGISTER INTBIG i, j;
903
904 /* find node A */
905 if ((dcl->flag&END1VALID) == 0)
906 {
907 if (defai != NOARCINST) *nA = defai->end[0].nodeinst; else
908 {
909 ttyputerr(M_("No end 0 specified"));
910 return(TRUE);
911 }
912 } else
913 {
914 *nA = cli_findnodename(dcl->end1);
915 if (*nA == NONODEINST)
916 {
917 if (defnA == NONODEINST)
918 {
919 ttyputerr(M_("Cannot find node %s"), dcl->end1);
920 return(TRUE);
921 }
922 *nA = defnA;
923 }
924 }
925
926 /* find node B */
927 if ((dcl->flag&END2VALID) == 0)
928 {
929 if (defai != NOARCINST) *nB = defai->end[1].nodeinst; else
930 {
931 ttyputerr(M_("No end 1 specified"));
932 return(TRUE);
933 }
934 } else
935 {
936 *nB = cli_findnodename(dcl->end2);
937 if (*nB == NONODEINST)
938 {
939 if (defnB == NONODEINST)
940 {
941 ttyputerr(M_("Cannot find node %s"), dcl->end2);
942 return(TRUE);
943 }
944 *nB = defnB;
945 }
946 }
947
948 /* find the port on node A */
949 if ((dcl->flag&PORT1VALID) == 0)
950 {
951 if (defai != NOARCINST) *pA = defai->end[0].portarcinst->proto; else
952 *pA = (*nA)->proto->firstportproto;
953 } else
954 {
955 *pA = getportproto((*nA)->proto, dcl->port1);
956 if (*pA == NOPORTPROTO)
957 {
958 ttyputerr(M_("Cannot find port %s"), dcl->port1);
959 return(TRUE);
960 }
961 }
962
963 /* find the port on node B */
964 if ((dcl->flag&PORT2VALID) == 0)
965 {
966 if (defai != NOARCINST) *pB = defai->end[1].portarcinst->proto; else
967 *pB = (*nB)->proto->firstportproto;
968 } else
969 {
970 *pB = getportproto((*nB)->proto, dcl->port2);
971 if (*pB == NOPORTPROTO)
972 {
973 ttyputerr(M_("Cannot find port %s"), dcl->port2);
974 return(TRUE);
975 }
976 }
977
978 /* find layer for running the wire */
979 if ((dcl->flag&LAYERVALID) == 0)
980 {
981 if (defai != NOARCINST) *ap = defai->proto; else
982 {
983 /* find a layer to use */
984 for(i=0; (*pA)->connects[i] != NOARCPROTO; i++)
985 {
986 for(j=0; (*pB)->connects[j] != NOARCPROTO; j++)
987 if ((*pA)->connects[i] == (*pB)->connects[j]) break;
988 if ((*pB)->connects[j] != NOARCPROTO) break;
989 }
990 if ((*pA)->connects[i] == NOARCPROTO)
991 {
992 ttyputerr(M_("Cannot find connecting layer"));
993 return(TRUE);
994 }
995 *ap = (*pA)->connects[i];
996 }
997 } else
998 {
999 *ap = getarcproto(dcl->layer);
1000 if (*ap == NOARCPROTO)
1001 {
1002 ttyputerr(M_("Cannot find connecting layer %s"), dcl->layer);
1003 return(TRUE);
1004 }
1005 }
1006
1007 /* find the proper width */
1008 if ((dcl->flag&WIDTHVALID) == 0)
1009 {
1010 if (defai != NOARCINST) *wid = defai->width; else
1011 *wid = defaultarcwidth(*ap);
1012 } else *wid = dcl->width;
1013
1014 return(FALSE);
1015 }
1016
1017 /***************************** UTILITIES *****************************/
1018
cli_deletecomponentdec(COMPONENTDEC * dec)1019 void cli_deletecomponentdec(COMPONENTDEC *dec)
1020 {
1021 REGISTER COMPONENT *compo, *nextcomp;
1022
1023 for(compo = dec->firstcomponent; compo != NOCOMPONENT; compo = nextcomp)
1024 {
1025 nextcomp = compo->nextcomponent;
1026 cli_freeattr(compo->firstattr);
1027 efree(compo->name);
1028 efree((CHAR *)compo);
1029 }
1030 if (dec->protoname != 0) efree(dec->protoname);
1031 efree((CHAR *)dec);
1032 }
1033
cli_deleteconnection(CONNECTION * dec)1034 void cli_deleteconnection(CONNECTION *dec)
1035 {
1036 REGISTER CONS *cons, *nextcons;
1037
1038 for(cons = dec->firstcons; cons != NOCONS; cons = nextcons)
1039 {
1040 nextcons = cons->nextcons;
1041 efree((CHAR *)cons);
1042 }
1043 if ((dec->flag&LAYERVALID) != 0) efree(dec->layer);
1044 if ((dec->flag&END1VALID) != 0) efree(dec->end1);
1045 if ((dec->flag&PORT1VALID) != 0) efree(dec->port1);
1046 if ((dec->flag&END2VALID) != 0) efree(dec->end2);
1047 if ((dec->flag&PORT2VALID) != 0) efree(dec->port2);
1048 cli_freeattr(dec->firstattr);
1049 efree((CHAR *)dec);
1050 }
1051
cli_deleteexport(EXPORT * e)1052 void cli_deleteexport(EXPORT *e)
1053 {
1054 if (e->portname != 0) efree(e->portname);
1055 if (e->component != 0) efree(e->component);
1056 if (e->subport != 0) efree(e->subport);
1057 cli_freeattr(e->firstattr);
1058 efree((CHAR *)e);
1059 }
1060
cli_freeattr(ATTR * a)1061 void cli_freeattr(ATTR *a)
1062 {
1063 REGISTER ATTR *nexta;
1064
1065 for( ; a != NOATTR; a = nexta)
1066 {
1067 nexta = a->nextattr;
1068 efree(a->name);
1069 if ((a->type&VTYPE) == VSTRING) efree((CHAR *)a->value);
1070 efree((CHAR *)a);
1071 }
1072 }
1073