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