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