1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  */
22 /*
23  * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  *
26  * check.c -- routines for checking the prop tree
27  *
28  * this module provides semantic checks on the parse tree.  most of
29  * these checks happen during the construction of the parse tree,
30  * when the various tree_X() routines call the various check_X()
31  * routines.  in a couple of special cases, a check function will
32  * process the parse tree after it has been fully constructed.  these
33  * cases are noted in the comments above the check function.
34  */
35 
36 #pragma ident	"%Z%%M%	%I%	%E% SMI"
37 
38 #include <stdio.h>
39 #include "out.h"
40 #include "stable.h"
41 #include "literals.h"
42 #include "lut.h"
43 #include "tree.h"
44 #include "ptree.h"
45 #include "check.h"
46 
47 static int check_reportlist(enum nodetype t, const char *s, struct node *np);
48 static int check_num(enum nodetype t, const char *s, struct node *np);
49 static int check_quote(enum nodetype t, const char *s, struct node *np);
50 static int check_num_func(enum nodetype t, const char *s, struct node *np);
51 static int check_fru_asru(enum nodetype t, const char *s, struct node *np);
52 static int check_engine(enum nodetype t, const char *s, struct node *np);
53 static int check_timeval(enum nodetype t, const char *s, struct node *np);
54 static int check_id(enum nodetype t, const char *s, struct node *np);
55 static int check_serd_method(enum nodetype t, const char *s, struct node *np);
56 static int check_nork(struct node *np);
57 static void check_cycle_lhs(struct node *stmtnp, struct node *arrow);
58 static void check_cycle_lhs_try(struct node *stmtnp, struct node *lhs,
59     struct node *rhs);
60 static void check_cycle_rhs(struct node *rhs);
61 
62 static struct {
63 	enum nodetype t;
64 	const char *name;
65 	int required;
66 	int (*checker)(enum nodetype t, const char *s, struct node *np);
67 	int outflags;
68 } Allowednames[] = {
69 	{ T_FAULT, "FITrate", 1, check_num_func, O_ERR },
70 	{ T_FAULT, "FRU", 0, check_fru_asru, O_ERR },
71 	{ T_FAULT, "ASRU", 0, check_fru_asru, O_ERR },
72 	{ T_UPSET, "engine", 0, check_engine, O_ERR },
73 	{ T_DEFECT, "FRU", 0, check_fru_asru, O_ERR },
74 	{ T_DEFECT, "ASRU", 0, check_fru_asru, O_ERR },
75 	{ T_EREPORT, "poller", 0, check_id, O_ERR },
76 	{ T_EREPORT, "delivery", 0, check_timeval, O_ERR },
77 	{ T_SERD, "N", 1, check_num, O_ERR },
78 	{ T_SERD, "T", 1, check_timeval, O_ERR },
79 	{ T_SERD, "method", 1, check_serd_method, O_ERR },
80 	{ T_SERD, "trip", 1, check_reportlist, O_ERR },
81 	{ T_SERD, "FRU", 0, check_fru_asru, O_ERR },
82 	{ T_ERROR, "ASRU", 0, check_fru_asru, O_ERR },
83 	{ T_CONFIG, NULL, 0, check_quote, O_ERR },
84 	{ 0, NULL, 0 },
85 };
86 
87 void
88 check_init(void)
89 {
90 	int i;
91 
92 	for (i = 0; Allowednames[i].t; i++)
93 		if (Allowednames[i].name != NULL)
94 			Allowednames[i].name = stable(Allowednames[i].name);
95 }
96 
97 void
98 check_fini(void)
99 {
100 }
101 
102 /*ARGSUSED*/
103 void
104 check_report_combination(struct node *np)
105 {
106 	/* nothing to check for here.  poller is only prop and it is optional */
107 }
108 
109 /*
110  * check_path_iterators -- verify all iterators are explicit
111  */
112 static void
113 check_path_iterators(struct node *np)
114 {
115 	if (np == NULL)
116 		return;
117 
118 	switch (np->t) {
119 		case T_ARROW:
120 			check_path_iterators(np->u.arrow.lhs);
121 			check_path_iterators(np->u.arrow.rhs);
122 			break;
123 
124 		case T_LIST:
125 			check_path_iterators(np->u.expr.left);
126 			check_path_iterators(np->u.expr.right);
127 			break;
128 
129 		case T_EVENT:
130 			check_path_iterators(np->u.event.epname);
131 			break;
132 
133 		case T_NAME:
134 			if (np->u.name.child == NULL)
135 				outfl(O_DIE, np->file, np->line,
136 				    "internal error: check_path_iterators: "
137 				    "unexpected implicit iterator: %s",
138 				    np->u.name.s);
139 			check_path_iterators(np->u.name.next);
140 			break;
141 
142 		default:
143 			outfl(O_DIE, np->file, np->line,
144 			    "internal error: check_path_iterators: "
145 			    "unexpected type: %s",
146 			    ptree_nodetype2str(np->t));
147 	}
148 }
149 
150 void
151 check_arrow(struct node *np)
152 {
153 	ASSERTinfo(np->t == T_ARROW, ptree_nodetype2str(np->t));
154 
155 	if (np->u.arrow.lhs->t != T_ARROW &&
156 	    np->u.arrow.lhs->t != T_LIST &&
157 	    np->u.arrow.lhs->t != T_EVENT) {
158 		outfl(O_ERR,
159 		    np->u.arrow.lhs->file, np->u.arrow.lhs->line,
160 		    "%s not allowed on left-hand side of arrow",
161 		    ptree_nodetype2str(np->u.arrow.lhs->t));
162 	}
163 
164 	if (!check_nork(np->u.arrow.nnp) ||
165 		!check_nork(np->u.arrow.knp))
166 			outfl(O_ERR, np->file, np->line,
167 			    "counts associated with propagation arrows "
168 			    "must be integers");
169 
170 	check_path_iterators(np);
171 }
172 
173 /*
174  * make sure the nork values are valid.
175  * Nork values must be "A" for all(T_NAME),
176  * a number(T_NUM), or a simple
177  * expression(T_SUB, T_ADD, T_MUL, T_DIV)
178  */
179 static int
180 check_nork(struct node *np)
181 {
182 	int rval = 0;
183 
184 	/* NULL means no nork value which is allowed */
185 	if (np == NULL) {
186 		rval = 1;
187 	}
188 	else
189 	{
190 		/* if the nork is a name it must be A for "All" */
191 		if (np->t == T_NAME)
192 			if (*np->u.name.s == 'A')
193 				return (1);
194 
195 		/*  T_NUM allowed */
196 		if (np->t == T_NUM)
197 			rval = 1;
198 
199 		/*  simple expressions allowed */
200 		if (np->t == T_SUB ||
201 			np->t == T_ADD ||
202 			np->t == T_MUL ||
203 			np->t == T_DIV)
204 			rval = 1;
205 	}
206 
207 	return (rval);
208 }
209 
210 static int
211 check_reportlist(enum nodetype t, const char *s, struct node *np)
212 {
213 	if (np == NULL)
214 		return (1);
215 	else if (np->t == T_EVENT) {
216 		if (np->u.event.ename->u.name.t != N_EREPORT) {
217 			outfl(O_ERR, np->file, np->line,
218 			    "%s %s property must begin with \"ereport.\"",
219 			    ptree_nodetype2str(t), s);
220 		} else if (tree_event2np_lut_lookup(Ereports, np) == NULL) {
221 			outfl(O_ERR, np->file, np->line,
222 			    "%s %s property contains undeclared name",
223 			    ptree_nodetype2str(t), s);
224 		}
225 		check_type_iterator(np);
226 	} else if (np->t == T_LIST) {
227 		(void) check_reportlist(t, s, np->u.expr.left);
228 		(void) check_reportlist(t, s, np->u.expr.right);
229 	}
230 	return (1);
231 }
232 
233 static int
234 check_num(enum nodetype t, const char *s, struct node *np)
235 {
236 	ASSERTinfo(np != NULL, ptree_nodetype2str(t));
237 	if (np->t != T_NUM)
238 		outfl(O_ERR, np->file, np->line,
239 		    "%s %s property must be a single number",
240 		    ptree_nodetype2str(t), s);
241 	return (1);
242 }
243 
244 /*ARGSUSED1*/
245 static int
246 check_quote(enum nodetype t, const char *s, struct node *np)
247 {
248 	ASSERTinfo(np != NULL, ptree_nodetype2str(t));
249 	if (np->t != T_QUOTE)
250 		outfl(O_ERR, np->file, np->line,
251 		    "%s properties must be quoted strings",
252 		    ptree_nodetype2str(t));
253 	return (1);
254 }
255 
256 static int
257 check_num_func(enum nodetype t, const char *s, struct node *np)
258 {
259 	ASSERTinfo(np != NULL, ptree_nodetype2str(t));
260 	if (np->t != T_NUM && np->t != T_FUNC)
261 		outfl(O_ERR, np->file, np->line,
262 		    "%s %s property must be a number or function",
263 		    ptree_nodetype2str(t), s);
264 	return (1);
265 }
266 
267 static int
268 check_fru_asru(enum nodetype t, const char *s, struct node *np)
269 {
270 	ASSERT(s != NULL);
271 
272 	/* make sure it is a node type T_NAME? */
273 	if (np->t == T_NAME) {
274 	    if (s == L_ASRU) {
275 		if (tree_name2np_lut_lookup_name(ASRUs, np) == NULL)
276 			outfl(O_ERR, np->file, np->line,
277 			    "ASRU property contains undeclared asru");
278 	    } else if (s == L_FRU) {
279 		if (tree_name2np_lut_lookup_name(FRUs, np) == NULL)
280 			outfl(O_ERR, np->file, np->line,
281 			    "FRU property contains undeclared fru");
282 	    } else {
283 		    outfl(O_ERR, np->file, np->line,
284 			"illegal property name in %s declaration: %s",
285 			ptree_nodetype2str(t), s);
286 	    }
287 	    check_type_iterator(np);
288 	} else
289 		outfl(O_ERR, np->file, np->line,
290 		    "illegal type used for %s property: %s",
291 		    s, ptree_nodetype2str(np->t));
292 	return (1);
293 }
294 
295 static int
296 check_engine(enum nodetype t, const char *s, struct node *np)
297 {
298 	ASSERTinfo(np != NULL, ptree_nodetype2str(t));
299 	if (np->t != T_EVENT)
300 		outfl(O_ERR, np->file, np->line,
301 		    "%s %s property must be an engine name "
302 		    "(i.e. serd.x or serd.x@a/b)",
303 		    ptree_nodetype2str(t), s);
304 
305 	return (1);
306 }
307 
308 static int
309 check_timeval(enum nodetype t, const char *s, struct node *np)
310 {
311 	ASSERTinfo(np != NULL, ptree_nodetype2str(t));
312 	if (np->t != T_TIMEVAL)
313 		outfl(O_ERR, np->file, np->line,
314 		    "%s %s property must be a number with time units",
315 		    ptree_nodetype2str(t), s);
316 	return (1);
317 }
318 
319 static int
320 check_id(enum nodetype t, const char *s, struct node *np)
321 {
322 	ASSERTinfo(np != NULL, ptree_nodetype2str(t));
323 	if (np->t != T_NAME || np->u.name.next || np->u.name.child)
324 		outfl(O_ERR, np->file, np->line,
325 		    "%s %s property must be simple name",
326 		    ptree_nodetype2str(t), s);
327 	return (1);
328 }
329 
330 static int
331 check_serd_method(enum nodetype t, const char *s, struct node *np)
332 {
333 	ASSERTinfo(np != NULL, ptree_nodetype2str(t));
334 	if (np->t != T_NAME || np->u.name.next || np->u.name.child ||
335 	    (np->u.name.s != L_volatile &&
336 	    np->u.name.s != L_persistent))
337 		outfl(O_ERR, np->file, np->line,
338 		    "%s %s property must be \"volatile\" or \"persistent\"",
339 		    ptree_nodetype2str(t), s);
340 	return (1);
341 }
342 
343 void
344 check_stmt_required_properties(struct node *stmtnp)
345 {
346 	struct lut *lutp = stmtnp->u.stmt.lutp;
347 	struct node *np = stmtnp->u.stmt.np;
348 	int i;
349 
350 	for (i = 0; Allowednames[i].t; i++)
351 		if (stmtnp->t == Allowednames[i].t &&
352 		    Allowednames[i].required &&
353 		    tree_s2np_lut_lookup(lutp, Allowednames[i].name) == NULL)
354 			outfl(Allowednames[i].outflags,
355 			    np->file, np->line,
356 			    "%s statement missing property: %s",
357 			    ptree_nodetype2str(stmtnp->t),
358 			    Allowednames[i].name);
359 }
360 
361 void
362 check_stmt_allowed_properties(enum nodetype t,
363     struct node *nvpairnp, struct lut *lutp)
364 {
365 	int i;
366 	const char *s = nvpairnp->u.expr.left->u.name.s;
367 	struct node *np;
368 
369 	for (i = 0; Allowednames[i].t; i++)
370 		if (t == Allowednames[i].t && Allowednames[i].name == NULL) {
371 			/* NULL name means just call checker */
372 			(*Allowednames[i].checker)(t, s,
373 			    nvpairnp->u.expr.right);
374 			return;
375 		} else if (t == Allowednames[i].t && s == Allowednames[i].name)
376 			break;
377 	if (Allowednames[i].name == NULL)
378 		outfl(O_ERR, nvpairnp->file, nvpairnp->line,
379 		    "illegal property name in %s declaration: %s",
380 		    ptree_nodetype2str(t), s);
381 	else if ((np = tree_s2np_lut_lookup(lutp, s)) != NULL) {
382 		/*
383 		 * redeclaring prop is allowed if value is the same
384 		 */
385 		if (np->t != nvpairnp->u.expr.right->t)
386 			outfl(O_ERR, nvpairnp->file, nvpairnp->line,
387 			    "property redeclared (with differnt type) "
388 			    "in %s declaration: %s",
389 			    ptree_nodetype2str(t), s);
390 		switch (np->t) {
391 			case T_NUM:
392 			case T_TIMEVAL:
393 				if (np->u.ull == nvpairnp->u.expr.right->u.ull)
394 					return;
395 				break;
396 
397 			case T_NAME:
398 				if (tree_namecmp(np,
399 				    nvpairnp->u.expr.right) == 0)
400 					return;
401 				break;
402 
403 			case T_EVENT:
404 				if (tree_eventcmp(np,
405 				    nvpairnp->u.expr.right) == 0)
406 					return;
407 				break;
408 
409 			default:
410 				outfl(O_ERR, nvpairnp->file, nvpairnp->line,
411 				    "value for property \"%s\" is an "
412 				    "invalid type: %s",
413 				    nvpairnp->u.expr.left->u.name.s,
414 				    ptree_nodetype2str(np->t));
415 				return;
416 		}
417 		outfl(O_ERR, nvpairnp->file, nvpairnp->line,
418 		    "property redeclared in %s declaration: %s",
419 		    ptree_nodetype2str(t), s);
420 	} else
421 		(*Allowednames[i].checker)(t, s, nvpairnp->u.expr.right);
422 }
423 
424 void
425 check_propnames(enum nodetype t, struct node *np, int from, int to)
426 {
427 	struct node *dnp;
428 	struct lut *lutp;
429 
430 	ASSERT(np != NULL);
431 	ASSERTinfo(np->t == T_EVENT || np->t == T_LIST || np->t == T_ARROW,
432 				ptree_nodetype2str(np->t));
433 
434 	if (np->t == T_EVENT) {
435 		switch (np->u.event.ename->u.name.t) {
436 		case N_UNSPEC:
437 			outfl(O_ERR, np->file, np->line,
438 			    "name in %s statement must begin with "
439 			    "type (example: \"error.\")",
440 			    ptree_nodetype2str(t));
441 			return;
442 		case N_FAULT:
443 			lutp = Faults;
444 			if (to) {
445 				outfl(O_ERR, np->file, np->line,
446 				    "%s has fault on right side of \"->\"",
447 				    ptree_nodetype2str(t));
448 				return;
449 			}
450 			if (!from) {
451 				outfl(O_DIE, np->file, np->line,
452 				    "internal error: %s has fault without "
453 				    "from flag",
454 				    ptree_nodetype2str(t));
455 			}
456 			break;
457 		case N_UPSET:
458 			lutp = Upsets;
459 			if (to) {
460 				outfl(O_ERR, np->file, np->line,
461 				    "%s has upset on right side of \"->\"",
462 				    ptree_nodetype2str(t));
463 				return;
464 			}
465 			if (!from)
466 				outfl(O_DIE, np->file, np->line,
467 				    "internal error: %s has upset without "
468 				    "from flag",
469 				    ptree_nodetype2str(t));
470 			break;
471 		case N_DEFECT:
472 			lutp = Defects;
473 			if (to) {
474 				outfl(O_ERR, np->file, np->line,
475 				    "%s has defect on right side of \"->\"",
476 				    ptree_nodetype2str(t));
477 				return;
478 			}
479 			if (!from) {
480 				outfl(O_DIE, np->file, np->line,
481 				    "internal error: %s has defect without "
482 				    "from flag",
483 				    ptree_nodetype2str(t));
484 			}
485 			break;
486 		case N_ERROR:
487 			lutp = Errors;
488 			if (!from && !to)
489 				outfl(O_DIE, np->file, np->line,
490 				    "%s has error without from or to flags",
491 				    ptree_nodetype2str(t));
492 			break;
493 		case N_EREPORT:
494 			lutp = Ereports;
495 			if (from) {
496 				outfl(O_ERR, np->file, np->line,
497 				    "%s has report on left side of \"->\"",
498 				    ptree_nodetype2str(t));
499 				return;
500 			}
501 			if (!to)
502 				outfl(O_DIE, np->file, np->line,
503 				    "internal error: %s has report without "
504 				    "to flag",
505 				    ptree_nodetype2str(t));
506 			break;
507 		default:
508 			outfl(O_DIE, np->file, np->line,
509 			    "internal error: check_propnames: "
510 			    "unexpected type: %d", np->u.name.t);
511 		}
512 
513 		if ((dnp = tree_event2np_lut_lookup(lutp, np)) == NULL) {
514 			outfl(O_ERR, np->file, np->line,
515 			    "%s statement contains undeclared event",
516 			    ptree_nodetype2str(t));
517 		} else
518 			dnp->u.stmt.flags |= STMT_REF;
519 		np->u.event.declp = dnp;
520 	} else if (np->t == T_LIST) {
521 		check_propnames(t, np->u.expr.left, from, to);
522 		check_propnames(t, np->u.expr.right, from, to);
523 	} else if (np->t == T_ARROW) {
524 		check_propnames(t, np->u.arrow.lhs, 1, to);
525 		check_propnames(t, np->u.arrow.rhs, from, 1);
526 	}
527 }
528 
529 static struct lut *
530 record_iterators(struct node *np, struct lut *ex)
531 {
532 	if (np == NULL)
533 		return (ex);
534 
535 	switch (np->t) {
536 	case T_ARROW:
537 		ex = record_iterators(np->u.arrow.lhs, ex);
538 		ex = record_iterators(np->u.arrow.rhs, ex);
539 		break;
540 
541 	case T_LIST:
542 		ex = record_iterators(np->u.expr.left, ex);
543 		ex = record_iterators(np->u.expr.right, ex);
544 		break;
545 
546 	case T_EVENT:
547 		ex = record_iterators(np->u.event.epname, ex);
548 		break;
549 
550 	case T_NAME:
551 		if (np->u.name.child && np->u.name.child->t == T_NAME)
552 			ex = lut_add(ex, (void *) np->u.name.child->u.name.s,
553 			    (void *) np, NULL);
554 		ex = record_iterators(np->u.name.next, ex);
555 		break;
556 
557 	default:
558 		outfl(O_DIE, np->file, np->line,
559 		    "record_iterators: internal error: unexpected type: %s",
560 		    ptree_nodetype2str(np->t));
561 	}
562 
563 	return (ex);
564 }
565 
566 void
567 check_exprscope(struct node *np, struct lut *ex)
568 {
569 	if (np == NULL)
570 		return;
571 
572 	switch (np->t) {
573 	case T_EVENT:
574 		check_exprscope(np->u.event.eexprlist, ex);
575 		break;
576 
577 	case T_ARROW:
578 		check_exprscope(np->u.arrow.lhs, ex);
579 		check_exprscope(np->u.arrow.rhs, ex);
580 		break;
581 
582 	case T_NAME:
583 		if (np->u.name.child && np->u.name.child->t == T_NAME) {
584 			if (lut_lookup(ex,
585 					(void *) np->u.name.child->u.name.s,
586 					NULL) == NULL)
587 				outfl(O_ERR, np->file, np->line,
588 					"constraint contains undefined"
589 					" iterator: %s",
590 					np->u.name.child->u.name.s);
591 		}
592 		check_exprscope(np->u.name.next, ex);
593 		break;
594 
595 	case T_QUOTE:
596 	case T_GLOBID:
597 		break;
598 
599 	case T_ASSIGN:
600 	case T_NE:
601 	case T_EQ:
602 	case T_LIST:
603 	case T_AND:
604 	case T_OR:
605 	case T_NOT:
606 	case T_ADD:
607 	case T_SUB:
608 	case T_MUL:
609 	case T_DIV:
610 	case T_MOD:
611 	case T_LT:
612 	case T_LE:
613 	case T_GT:
614 	case T_GE:
615 	case T_BITAND:
616 	case T_BITOR:
617 	case T_BITXOR:
618 	case T_BITNOT:
619 	case T_LSHIFT:
620 	case T_RSHIFT:
621 	case T_CONDIF:
622 	case T_CONDELSE:
623 		check_exprscope(np->u.expr.left, ex);
624 		check_exprscope(np->u.expr.right, ex);
625 		break;
626 
627 	case T_FUNC:
628 		check_exprscope(np->u.func.arglist, ex);
629 		break;
630 
631 	case T_NUM:
632 	case T_TIMEVAL:
633 		break;
634 
635 	default:
636 		outfl(O_DIE, np->file, np->line,
637 		    "check_exprscope: internal error: unexpected type: %s",
638 		    ptree_nodetype2str(np->t));
639 	}
640 }
641 
642 /*
643  * check_propscope -- check constraints for out of scope variable refs
644  */
645 void
646 check_propscope(struct node *np)
647 {
648 	struct lut *ex;
649 
650 	ex = record_iterators(np, NULL);
651 	check_exprscope(np, ex);
652 	lut_free(ex, NULL, NULL);
653 }
654 
655 /*
656  * check_upset_engine -- validate the engine property in an upset statement
657  *
658  * we do this after the full parse tree has been constructed rather than while
659  * building the parse tree because it is inconvenient for the user if we
660  * require SERD engines to be declared before used in an upset "engine"
661  * property.
662  */
663 
664 /*ARGSUSED*/
665 void
666 check_upset_engine(struct node *lhs, struct node *rhs, void *arg)
667 {
668 	enum nodetype t = (enum nodetype)arg;
669 	struct node *engnp;
670 	struct node *declp;
671 
672 	ASSERTeq(rhs->t, t, ptree_nodetype2str);
673 
674 	if ((engnp = tree_s2np_lut_lookup(rhs->u.stmt.lutp, L_engine)) == NULL)
675 		return;
676 
677 	ASSERT(engnp->t == T_EVENT);
678 
679 	if ((declp = tree_event2np_lut_lookup(SERDs, engnp)) == NULL) {
680 		outfl(O_ERR, engnp->file, engnp->line,
681 		    "%s %s property contains undeclared name",
682 		    ptree_nodetype2str(t), L_engine);
683 		return;
684 	}
685 	engnp->u.event.declp = declp;
686 }
687 
688 /*
689  * check_refcount -- see if declared names are used
690  *
691  * this is run after the entire parse tree is constructed, so a refcount
692  * of zero means the name has been declared but otherwise not used.
693  */
694 
695 void
696 check_refcount(struct node *lhs, struct node *rhs, void *arg)
697 {
698 	enum nodetype t = (enum nodetype)arg;
699 
700 	ASSERTeq(rhs->t, t, ptree_nodetype2str);
701 
702 	if (rhs->u.stmt.flags & STMT_REF)
703 		return;
704 
705 	outfl(O_WARN|O_NONL, rhs->file, rhs->line,
706 	    "%s name declared but not used: ", ptree_nodetype2str(t));
707 	ptree_name(O_WARN|O_NONL, lhs);
708 	out(O_WARN, NULL);
709 }
710 
711 /*
712  * set check_cycle_warninglevel only for val >= 0
713  */
714 int
715 check_cycle_level(long long val)
716 {
717 	static int check_cycle_warninglevel = -1;
718 
719 	if (val == 0)
720 		check_cycle_warninglevel = 0;
721 	else if (val > 0)
722 		check_cycle_warninglevel = 1;
723 
724 	return (check_cycle_warninglevel);
725 }
726 
727 /*
728  * check_cycle -- see props from an error have cycles
729  *
730  * this is run after the entire parse tree is constructed, for
731  * each error that has been declared.
732  */
733 
734 /*ARGSUSED*/
735 void
736 check_cycle(struct node *lhs, struct node *rhs, void *arg)
737 {
738 	struct node *np;
739 
740 	ASSERTeq(rhs->t, T_ERROR, ptree_nodetype2str);
741 
742 	if (rhs->u.stmt.flags & STMT_CYCLE)
743 		return;		/* already reported this cycle */
744 
745 	if (rhs->u.stmt.flags & STMT_CYMARK) {
746 #ifdef ESC
747 		int warninglevel;
748 
749 		warninglevel = check_cycle_level(-1);
750 		if (warninglevel <= 0) {
751 			int olevel = O_ERR;
752 
753 			if (warninglevel == 0)
754 				olevel = O_WARN;
755 
756 			out(olevel|O_NONL, "cycle in propagation tree: ");
757 			ptree_name(olevel|O_NONL, rhs->u.stmt.np);
758 			out(olevel, NULL);
759 		}
760 #endif /* ESC */
761 
762 		rhs->u.stmt.flags |= STMT_CYCLE;
763 	}
764 
765 	rhs->u.stmt.flags |= STMT_CYMARK;
766 
767 	/* for each propagation */
768 	for (np = Props; np; np = np->u.stmt.next)
769 		check_cycle_lhs(rhs, np->u.stmt.np);
770 
771 	rhs->u.stmt.flags &= ~STMT_CYMARK;
772 }
773 
774 /*
775  * check_cycle_lhs -- find the lhs of an arrow for cycle checking
776  */
777 
778 static void
779 check_cycle_lhs(struct node *stmtnp, struct node *arrow)
780 {
781 	struct node *trylhs;
782 	struct node *tryrhs;
783 
784 	/* handle cascaded arrows */
785 	switch (arrow->u.arrow.lhs->t) {
786 	case T_ARROW:
787 		/* first recurse left */
788 		check_cycle_lhs(stmtnp, arrow->u.arrow.lhs);
789 
790 		ASSERT(arrow->u.arrow.lhs->u.arrow.rhs->t == T_EVENT);
791 
792 		/* then try this arrow (thing cascaded *to*) */
793 		trylhs = arrow->u.arrow.lhs->u.arrow.rhs;
794 		tryrhs = arrow->u.arrow.rhs;
795 		break;
796 
797 	case T_EVENT:
798 	case T_LIST:
799 		trylhs = arrow->u.arrow.lhs;
800 		tryrhs = arrow->u.arrow.rhs;
801 		break;
802 
803 	default:
804 		out(O_DIE, "lhs: unexpected type: %s",
805 		    ptree_nodetype2str(arrow->u.arrow.lhs->t));
806 		/*NOTREACHED*/
807 	}
808 
809 	check_cycle_lhs_try(stmtnp, trylhs, tryrhs);
810 }
811 
812 /*
813  * check_cycle_lhs_try -- try matching an event name on lhs of an arrow
814  */
815 
816 static void
817 check_cycle_lhs_try(struct node *stmtnp, struct node *lhs, struct node *rhs)
818 {
819 	if (lhs->t == T_LIST) {
820 		check_cycle_lhs_try(stmtnp, lhs->u.expr.left, rhs);
821 		check_cycle_lhs_try(stmtnp, lhs->u.expr.right, rhs);
822 		return;
823 	}
824 
825 	ASSERT(lhs->t == T_EVENT);
826 
827 	if (tree_eventcmp(stmtnp->u.stmt.np, lhs) != 0)
828 		return;		/* no match */
829 
830 	check_cycle_rhs(rhs);
831 }
832 
833 /*
834  * check_cycle_rhs -- foreach error on rhs, see if we cycle to a marked error
835  */
836 
837 static void
838 check_cycle_rhs(struct node *rhs)
839 {
840 	struct node *dnp;
841 
842 	if (rhs->t == T_LIST) {
843 		check_cycle_rhs(rhs->u.expr.left);
844 		check_cycle_rhs(rhs->u.expr.right);
845 		return;
846 	}
847 
848 	ASSERT(rhs->t == T_EVENT);
849 
850 	if (rhs->u.event.ename->u.name.t != N_ERROR)
851 		return;
852 
853 	if ((dnp = tree_event2np_lut_lookup(Errors, rhs)) == NULL) {
854 		outfl(O_ERR|O_NONL,
855 		    rhs->file, rhs->line,
856 		    "unexpected undeclared event during cycle check");
857 		ptree_name(O_ERR|O_NONL, rhs);
858 		out(O_ERR, NULL);
859 		return;
860 	}
861 	check_cycle(NULL, dnp, 0);
862 }
863 
864 /*
865  * Force iterators to be simple names, expressions, or numbers
866  */
867 void
868 check_name_iterator(struct node *np)
869 {
870 	if (np->u.name.child->t != T_NUM &&
871 	    np->u.name.child->t != T_NAME &&
872 	    np->u.name.child->t != T_CONDIF &&
873 	    np->u.name.child->t != T_SUB &&
874 	    np->u.name.child->t != T_ADD &&
875 	    np->u.name.child->t != T_MUL &&
876 	    np->u.name.child->t != T_DIV &&
877 	    np->u.name.child->t != T_MOD &&
878 	    np->u.name.child->t != T_LSHIFT &&
879 	    np->u.name.child->t != T_RSHIFT) {
880 		outfl(O_ERR|O_NONL, np->file, np->line,
881 		"invalid iterator: ");
882 		ptree_name_iter(O_ERR|O_NONL, np);
883 		out(O_ERR, NULL);
884 	}
885 }
886 
887 /*
888  * Iterators on a declaration may only be implicit
889  */
890 void
891 check_type_iterator(struct node *np)
892 {
893 	while (np != NULL) {
894 		if (np->t == T_EVENT) {
895 			np = np->u.event.epname;
896 		} else if (np->t == T_NAME) {
897 			if (np->u.name.child != NULL &&
898 			    np->u.name.child->t != T_NUM) {
899 				outfl(O_ERR|O_NONL, np->file, np->line,
900 				    "explicit iterators disallowed "
901 				    "in declarations: ");
902 				ptree_name_iter(O_ERR|O_NONL, np);
903 				out(O_ERR, NULL);
904 			}
905 			np = np->u.name.next;
906 		} else {
907 			break;
908 		}
909 	}
910 }
911 
912 void
913 check_func(struct node *np)
914 {
915 	ASSERTinfo(np->t == T_FUNC, ptree_nodetype2str(np->t));
916 
917 	if (np->u.func.s == L_within) {
918 		struct node *arglist = np->u.func.arglist;
919 		switch (arglist->t) {
920 			case T_NUM:
921 				if (arglist->u.ull != 0ULL)
922 				    outfl(O_ERR, arglist->file, arglist->line,
923 					"parameter of within must be 0"
924 					", \"infinity\" or a time value.");
925 				break;
926 			case T_NAME:
927 				if (arglist->u.name.s != L_infinity)
928 				    outfl(O_ERR, arglist->file, arglist->line,
929 					"parameter of within must be 0"
930 					", \"infinity\" or a time value.");
931 				break;
932 			case T_LIST:
933 				/*
934 				 * if two parameters, the left or min must be
935 				 * either T_NUM or T_TIMEVAL
936 				 */
937 				if (arglist->u.expr.left->t != T_NUM &&
938 				    arglist->u.expr.left->t != T_TIMEVAL)
939 					outfl(O_ERR,
940 					    arglist->file, arglist->line,
941 					    "first parameter of within must be"
942 					    " either a time value or zero.");
943 
944 				/*
945 				 * if two parameters, the right or max must
946 				 * be either T_NUM, T_NAME or T_TIMEVAL
947 				 */
948 				if (arglist->u.expr.right->t != T_NUM &&
949 				    arglist->u.expr.right->t != T_TIMEVAL &&
950 				    arglist->u.expr.right->t != T_NAME)
951 					outfl(O_ERR,
952 					    arglist->file, arglist->line,
953 					    "second parameter of within must "
954 					    "be 0, \"infinity\" "
955 					    "or time value.");
956 
957 				/*
958 				 * if right or left is a T_NUM it must
959 				 * be zero
960 				 */
961 				if (arglist->u.expr.left->t == T_NUM)
962 					if (arglist->u.expr.left->u.ull != 0ULL)
963 					    outfl(O_ERR,
964 						arglist->file, arglist->line,
965 						"within parameter must be 0 or"
966 						" a time value.");
967 				if (arglist->u.expr.right->t == T_NUM)
968 					if (arglist->u.expr.right->u.ull
969 					    != 0ULL)
970 					    outfl(O_ERR,
971 						arglist->file, arglist->line,
972 						"within parameter must be 0 or"
973 						" a time value.");
974 
975 				/* if right is a T_NAME it must be "infinity" */
976 				if (arglist->u.expr.right->t == T_NAME)
977 					if (arglist->u.expr.right->u.name.s
978 					    != L_infinity)
979 					    outfl(O_ERR,
980 						arglist->file, arglist->line,
981 						"\"infinity\" is the only valid"
982 						" name for within parameter.");
983 
984 				/*
985 				 * the first parameter [min] must not be greater
986 				 * than the second parameter [max].
987 				 */
988 				if (arglist->u.expr.left->u.ull >
989 				    arglist->u.expr.right->u.ull)
990 					outfl(O_ERR,
991 					    arglist->file, arglist->line,
992 					    "the first value (min) of"
993 					    " within must be less than"
994 					    " the second (max) value");
995 				break;
996 			case T_TIMEVAL:
997 				break; /* no restrictions on T_TIMEVAL */
998 			default:
999 				outfl(O_ERR, arglist->file, arglist->line,
1000 					"parameter of within must be 0"
1001 					", \"infinity\" or a time value.");
1002 		}
1003 	} else if (np->u.func.s == L_call) {
1004 		if (np->u.func.arglist->t != T_QUOTE &&
1005 		    np->u.func.arglist->t != T_LIST &&
1006 		    np->u.func.arglist->t != T_GLOBID &&
1007 		    np->u.func.arglist->t != T_CONDIF &&
1008 		    np->u.func.arglist->t != T_LIST &&
1009 		    np->u.func.arglist->t != T_FUNC)
1010 			outfl(O_ERR, np->u.func.arglist->file,
1011 				np->u.func.arglist->line,
1012 				"invalid first argument to call()");
1013 	} else if (np->u.func.s == L_fru) {
1014 		if (np->u.func.arglist->t != T_NAME)
1015 			outfl(O_ERR, np->u.func.arglist->file,
1016 				np->u.func.arglist->line,
1017 				"argument to fru() must be a path");
1018 	} else if (np->u.func.s == L_asru) {
1019 		if (np->u.func.arglist->t != T_NAME)
1020 			outfl(O_ERR, np->u.func.arglist->file,
1021 				np->u.func.arglist->line,
1022 				"argument to asru() must be a path");
1023 	} else if (np->u.func.s == L_is_connected ||
1024 	    np->u.func.s == L_is_under) {
1025 		if (np->u.func.arglist->t == T_LIST &&
1026 		    (np->u.func.arglist->u.expr.left->t == T_NAME ||
1027 		    (np->u.func.arglist->u.expr.left->t == T_FUNC &&
1028 		    (np->u.func.arglist->u.expr.left->u.func.s == L_fru ||
1029 		    np->u.func.arglist->u.expr.left->u.func.s == L_asru))) &&
1030 		    (np->u.func.arglist->u.expr.right->t == T_NAME ||
1031 		    (np->u.func.arglist->u.expr.right->t == T_FUNC &&
1032 		    (np->u.func.arglist->u.expr.right->u.func.s == L_fru ||
1033 		    np->u.func.arglist->u.expr.right->u.func.s == L_asru)))) {
1034 			if (np->u.func.arglist->u.expr.left->t == T_FUNC)
1035 				check_func(np->u.func.arglist->u.expr.left);
1036 			if (np->u.func.arglist->u.expr.right->t == T_FUNC)
1037 				check_func(np->u.func.arglist->u.expr.right);
1038 		} else {
1039 			outfl(O_ERR, np->u.func.arglist->file,
1040 			    np->u.func.arglist->line,
1041 			    "%s() must have paths or calls to "
1042 			    "fru() and/or asru() as arguments",
1043 			    np->u.func.s);
1044 		}
1045 	} else if (np->u.func.s == L_is_on) {
1046 		if (np->u.func.arglist->t == T_FUNC &&
1047 		    (np->u.func.arglist->u.func.s == L_fru ||
1048 		    np->u.func.arglist->u.func.s == L_asru)) {
1049 			check_func(np->u.func.arglist);
1050 		} else {
1051 			outfl(O_ERR, np->u.func.arglist->file,
1052 			    np->u.func.arglist->line,
1053 			    "argument to is_on() must be a call to "
1054 			    "fru() or asru()");
1055 		}
1056 	} else if (np->u.func.s == L_is_present) {
1057 		if (np->u.func.arglist->t == T_FUNC &&
1058 		    (np->u.func.arglist->u.func.s == L_fru ||
1059 		    np->u.func.arglist->u.func.s == L_asru)) {
1060 			check_func(np->u.func.arglist);
1061 		} else {
1062 			outfl(O_ERR, np->u.func.arglist->file,
1063 			    np->u.func.arglist->line,
1064 			    "argument to is_present() must be a call to "
1065 			    "fru() or asru()");
1066 		}
1067 	} else if (np->u.func.s == L_is_type) {
1068 		if (np->u.func.arglist->t == T_FUNC &&
1069 		    (np->u.func.arglist->u.func.s == L_fru ||
1070 		    np->u.func.arglist->u.func.s == L_asru)) {
1071 			check_func(np->u.func.arglist);
1072 		} else {
1073 			outfl(O_ERR, np->u.func.arglist->file,
1074 			    np->u.func.arglist->line,
1075 			    "argument to is_type() must be a call to "
1076 			    "fru() or asru()");
1077 		}
1078 	} else if (np->u.func.s == L_confprop) {
1079 		if (np->u.func.arglist->t == T_LIST &&
1080 		    (np->u.func.arglist->u.expr.left->t == T_FUNC &&
1081 		    (np->u.func.arglist->u.expr.left->u.func.s == L_fru ||
1082 		    np->u.func.arglist->u.expr.left->u.func.s == L_asru)) &&
1083 		    np->u.func.arglist->u.expr.right->t == T_QUOTE) {
1084 			check_func(np->u.func.arglist->u.expr.left);
1085 		} else {
1086 			outfl(O_ERR, np->u.func.arglist->file,
1087 			    np->u.func.arglist->line,
1088 			    "confprop(): first argument must be a call to "
1089 			    "fru() or asru(); "
1090 			    "second argument must be a string");
1091 		}
1092 	} else if (np->u.func.s == L_payloadprop) {
1093 		if (np->u.func.arglist->t != T_QUOTE)
1094 			outfl(O_ERR, np->u.func.arglist->file,
1095 				np->u.func.arglist->line,
1096 				"argument to payloadprop() must be a string");
1097 	} else if (np->u.func.s == L_envprop) {
1098 		if (np->u.func.arglist->t != T_QUOTE)
1099 			outfl(O_ERR, np->u.func.arglist->file,
1100 				np->u.func.arglist->line,
1101 				"argument to envprop() must be a string");
1102 	} else
1103 		outfl(O_WARN, np->file, np->line,
1104 			"possible platform-specific function: %s",
1105 			np->u.func.s);
1106 }
1107 
1108 void
1109 check_expr(struct node *np)
1110 {
1111 	ASSERT(np != NULL);
1112 
1113 	switch (np->t) {
1114 	case T_ASSIGN:
1115 		ASSERT(np->u.expr.left != NULL);
1116 		if (np->u.expr.left->t != T_GLOBID)
1117 			outfl(O_ERR, np->file, np->line,
1118 			    "assignment only allowed to globals (e.g. $a)");
1119 		break;
1120 	}
1121 }
1122 
1123 void
1124 check_event(struct node *np)
1125 {
1126 	ASSERT(np != NULL);
1127 	ASSERT(np->t == T_EVENT);
1128 
1129 	if (np->u.event.epname == NULL) {
1130 		outfl(O_ERR|O_NONL, np->file, np->line,
1131 		    "pathless events not allowed: ");
1132 		ptree_name(O_ERR|O_NONL, np->u.event.ename);
1133 		out(O_ERR, NULL);
1134 	}
1135 }
1136 
1137 /*
1138  * check for properties that are required on declarations. This
1139  * should be done after all declarations since they can be
1140  * redeclared with a different set of properties.
1141  */
1142 /*ARGSUSED*/
1143 void
1144 check_required_props(struct node *lhs, struct node *rhs, void *arg)
1145 {
1146 	enum nodetype t = (enum nodetype)arg;
1147 
1148 	ASSERTeq(rhs->t, t, ptree_nodetype2str);
1149 
1150 	check_stmt_required_properties(rhs);
1151 }
1152