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