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