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 2008 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_func(struct node *np)
    966 {
    967 	struct node *arglist = np->u.func.arglist;
    968 
    969 	ASSERTinfo(np->t == T_FUNC, ptree_nodetype2str(np->t));
    970 
    971 	if (np->u.func.s == L_within) {
    972 		switch (arglist->t) {
    973 		case T_NUM:
    974 			if (arglist->u.ull != 0ULL) {
    975 				outfl(O_ERR, arglist->file, arglist->line,
    976 				    "parameter of within must be 0"
    977 				    ", \"infinity\" or a time value.");
    978 			}
    979 			break;
    980 
    981 		case T_NAME:
    982 			if (arglist->u.name.s != L_infinity) {
    983 				outfl(O_ERR, arglist->file, arglist->line,
    984 				    "parameter of within must be 0"
    985 				    ", \"infinity\" or a time value.");
    986 			}
    987 			break;
    988 
    989 		case T_LIST:
    990 			/*
    991 			 * if two parameters, the left or min must be
    992 			 * either T_NUM or T_TIMEVAL
    993 			 */
    994 			if (arglist->u.expr.left->t != T_NUM &&
    995 			    arglist->u.expr.left->t != T_TIMEVAL) {
    996 				outfl(O_ERR, arglist->file, arglist->line,
    997 				    "first parameter of within must be"
    998 				    " either a time value or zero.");
    999 			}
   1000 
   1001 			/*
   1002 			 * if two parameters, the right or max must
   1003 			 * be either T_NUM, T_NAME or T_TIMEVAL
   1004 			 */
   1005 			if (arglist->u.expr.right->t != T_NUM &&
   1006 			    arglist->u.expr.right->t != T_TIMEVAL &&
   1007 			    arglist->u.expr.right->t != T_NAME) {
   1008 				outfl(O_ERR, arglist->file, arglist->line,
   1009 				    "second parameter of within must "
   1010 				    "be 0, \"infinity\" or time value.");
   1011 			}
   1012 
   1013 			/*
   1014 			 * if right or left is a T_NUM it must
   1015 			 * be zero
   1016 			 */
   1017 			if ((arglist->u.expr.left->t == T_NUM) &&
   1018 			    (arglist->u.expr.left->u.ull != 0ULL)) {
   1019 				outfl(O_ERR, arglist->file, arglist->line,
   1020 				    "within parameter must be "
   1021 				    "0 or a time value.");
   1022 			}
   1023 			if ((arglist->u.expr.right->t == T_NUM) &&
   1024 			    (arglist->u.expr.right->u.ull != 0ULL)) {
   1025 				outfl(O_ERR, arglist->file, arglist->line,
   1026 				    "within parameter must be "
   1027 				    "0 or a time value.");
   1028 			}
   1029 
   1030 			/* if right is a T_NAME it must be "infinity" */
   1031 			if ((arglist->u.expr.right->t == T_NAME) &&
   1032 			    (arglist->u.expr.right->u.name.s != L_infinity)) {
   1033 				outfl(O_ERR, arglist->file, arglist->line,
   1034 				    "\"infinity\" is the only "
   1035 				    "valid name for within parameter.");
   1036 			}
   1037 
   1038 			/*
   1039 			 * the first parameter [min] must not be greater
   1040 			 * than the second parameter [max].
   1041 			 */
   1042 			if (arglist->u.expr.left->u.ull >
   1043 			    arglist->u.expr.right->u.ull) {
   1044 				outfl(O_ERR, arglist->file, arglist->line,
   1045 				    "the first value (min) of"
   1046 				    " within must be less than"
   1047 				    " the second (max) value");
   1048 			}
   1049 			break;
   1050 
   1051 		case T_TIMEVAL:
   1052 			break; /* no restrictions on T_TIMEVAL */
   1053 
   1054 		default:
   1055 			outfl(O_ERR, arglist->file, arglist->line,
   1056 			    "parameter of within must be 0"
   1057 			    ", \"infinity\" or a time value.");
   1058 		}
   1059 	} else if (np->u.func.s == L_call) {
   1060 		if (arglist->t != T_QUOTE &&
   1061 		    arglist->t != T_LIST &&
   1062 		    arglist->t != T_GLOBID &&
   1063 		    arglist->t != T_CONDIF &&
   1064 		    arglist->t != T_LIST &&
   1065 		    arglist->t != T_FUNC)
   1066 			outfl(O_ERR, arglist->file, arglist->line,
   1067 			    "invalid first argument to call()");
   1068 	} else if (np->u.func.s == L_fru) {
   1069 		if (arglist->t != T_NAME)
   1070 			outfl(O_ERR, arglist->file, arglist->line,
   1071 			    "argument to fru() must be a path");
   1072 	} else if (np->u.func.s == L_asru) {
   1073 		if (arglist->t != T_NAME)
   1074 			outfl(O_ERR, arglist->file, arglist->line,
   1075 			    "argument to asru() must be a path");
   1076 	} else if (np->u.func.s == L_is_connected ||
   1077 	    np->u.func.s == L_is_under) {
   1078 		if (arglist->t == T_LIST &&
   1079 		    (arglist->u.expr.left->t == T_NAME ||
   1080 		    (arglist->u.expr.left->t == T_FUNC &&
   1081 		    (arglist->u.expr.left->u.func.s == L_fru ||
   1082 		    arglist->u.expr.left->u.func.s == L_asru))) &&
   1083 		    (arglist->u.expr.right->t == T_NAME ||
   1084 		    (arglist->u.expr.right->t == T_FUNC &&
   1085 		    (arglist->u.expr.right->u.func.s == L_fru ||
   1086 		    arglist->u.expr.right->u.func.s == L_asru)))) {
   1087 			if (arglist->u.expr.left->t == T_FUNC)
   1088 				check_func(arglist->u.expr.left);
   1089 			if (arglist->u.expr.right->t == T_FUNC)
   1090 				check_func(arglist->u.expr.right);
   1091 		} else {
   1092 			outfl(O_ERR, arglist->file, arglist->line,
   1093 			    "%s() must have paths or calls to "
   1094 			    "fru() and/or asru() as arguments",
   1095 			    np->u.func.s);
   1096 		}
   1097 	} else if (np->u.func.s == L_is_on) {
   1098 		if (arglist->t == T_NAME ||
   1099 		    (arglist->t == T_FUNC &&
   1100 		    (arglist->u.func.s == L_fru ||
   1101 		    arglist->u.func.s == L_asru))) {
   1102 			if (arglist->t == T_FUNC)
   1103 				check_func(arglist);
   1104 		} else {
   1105 			outfl(O_ERR, arglist->file, arglist->line,
   1106 			    "argument to is_on() must be a path or a call to "
   1107 			    "fru() or asru()");
   1108 		}
   1109 	} else if (np->u.func.s == L_is_present) {
   1110 		if (arglist->t == T_NAME ||
   1111 		    (arglist->t == T_FUNC &&
   1112 		    (arglist->u.func.s == L_fru ||
   1113 		    arglist->u.func.s == L_asru))) {
   1114 			if (arglist->t == T_FUNC)
   1115 				check_func(arglist);
   1116 		} else {
   1117 			outfl(O_ERR, arglist->file, arglist->line,
   1118 			    "argument to is_present() must be a path or a call "
   1119 			    "to fru() or asru()");
   1120 		}
   1121 	} else if (np->u.func.s == L_has_fault) {
   1122 		if (arglist->t == T_LIST &&
   1123 		    (arglist->u.expr.left->t == T_NAME ||
   1124 		    (arglist->u.expr.left->t == T_FUNC &&
   1125 		    (arglist->u.expr.left->u.func.s == L_fru ||
   1126 		    arglist->u.expr.left->u.func.s == L_asru))) &&
   1127 		    arglist->u.expr.right->t == T_QUOTE) {
   1128 			if (arglist->u.expr.left->t == T_FUNC)
   1129 				check_func(arglist->u.expr.left);
   1130 		} else {
   1131 			outfl(O_ERR, arglist->file, arglist->line,
   1132 			    "%s() must have path or call to "
   1133 			    "fru() and/or asru() as first argument; "
   1134 			    "second argument must be a string", np->u.func.s);
   1135 		}
   1136 	} else if (np->u.func.s == L_is_type) {
   1137 		if (arglist->t == T_NAME ||
   1138 		    (arglist->t == T_FUNC &&
   1139 		    (arglist->u.func.s == L_fru ||
   1140 		    arglist->u.func.s == L_asru))) {
   1141 			if (arglist->t == T_FUNC)
   1142 				check_func(arglist);
   1143 		} else {
   1144 			outfl(O_ERR, arglist->file, arglist->line,
   1145 			    "argument to is_type() must be a path or a call to "
   1146 			    "fru() or asru()");
   1147 		}
   1148 	} else if (np->u.func.s == L_confcall) {
   1149 		if (arglist->t != T_QUOTE &&
   1150 		    (arglist->t != T_LIST ||
   1151 		    arglist->u.expr.left->t != T_QUOTE))
   1152 			outfl(O_ERR, arglist->file, arglist->line,
   1153 			    "confcall(): first argument must be a string "
   1154 			    "(the name of the operation)");
   1155 	} else if (np->u.func.s == L_confprop ||
   1156 	    np->u.func.s == L_confprop_defined) {
   1157 		if (arglist->t == T_LIST &&
   1158 		    (arglist->u.expr.left->t == T_NAME ||
   1159 		    (arglist->u.expr.left->t == T_FUNC &&
   1160 		    (arglist->u.expr.left->u.func.s == L_fru ||
   1161 		    arglist->u.expr.left->u.func.s == L_asru))) &&
   1162 		    arglist->u.expr.right->t == T_QUOTE) {
   1163 			if (arglist->u.expr.left->t == T_FUNC)
   1164 				check_func(arglist->u.expr.left);
   1165 		} else {
   1166 			outfl(O_ERR, arglist->file, arglist->line,
   1167 			    "%s(): first argument must be a path or a call to "
   1168 			    "fru() or asru(); "
   1169 			    "second argument must be a string", np->u.func.s);
   1170 		}
   1171 	} else if (np->u.func.s == L_count) {
   1172 		if (arglist->t != T_EVENT) {
   1173 			outfl(O_ERR, arglist->file, arglist->line,
   1174 			    "count(): argument must be an engine name");
   1175 		}
   1176 	} else if (np->u.func.s == L_defined) {
   1177 		if (arglist->t != T_GLOBID)
   1178 			outfl(O_ERR, arglist->file, arglist->line,
   1179 			    "argument to defined() must be a global");
   1180 	} else if (np->u.func.s == L_payloadprop) {
   1181 		if (arglist->t != T_QUOTE)
   1182 			outfl(O_ERR, arglist->file, arglist->line,
   1183 			    "argument to payloadprop() must be a string");
   1184 	} else if (np->u.func.s == L_payloadprop_contains) {
   1185 		if (arglist->t != T_LIST ||
   1186 		    arglist->u.expr.left->t != T_QUOTE ||
   1187 		    arglist->u.expr.right == NULL)
   1188 			outfl(O_ERR, arglist->file, arglist->line,
   1189 			    "args to payloadprop_contains(): must be a quoted "
   1190 			    "string (property name) and an expression "
   1191 			    "(to match)");
   1192 	} else if (np->u.func.s == L_payloadprop_defined) {
   1193 		if (arglist->t != T_QUOTE)
   1194 			outfl(O_ERR, arglist->file, arglist->line,
   1195 			    "arg to payloadprop_defined(): must be a quoted "
   1196 			    "string");
   1197 	} else if (np->u.func.s == L_setpayloadprop) {
   1198 		if (arglist->t == T_LIST &&
   1199 		    arglist->u.expr.left->t == T_QUOTE) {
   1200 			if (arglist->u.expr.right->t == T_FUNC)
   1201 				check_func(arglist->u.expr.right);
   1202 		} else {
   1203 			outfl(O_ERR, arglist->file, arglist->line,
   1204 			    "setpayloadprop(): "
   1205 			    "first arg must be a string, "
   1206 			    "second arg a value");
   1207 		}
   1208 	} else if (np->u.func.s == L_setserdn || np->u.func.s == L_setserdt ||
   1209 	    np->u.func.s == L_setserdsuffix || np->u.func.s ==
   1210 	    L_setserdincrement) {
   1211 		if (arglist->t == T_FUNC)
   1212 			check_func(arglist);
   1213 	} else if (np->u.func.s == L_envprop) {
   1214 		if (arglist->t != T_QUOTE)
   1215 			outfl(O_ERR, arglist->file, arglist->line,
   1216 			    "argument to envprop() must be a string");
   1217 	} else
   1218 		outfl(O_WARN, np->file, np->line,
   1219 		    "possible platform-specific function: %s",
   1220 		    np->u.func.s);
   1221 }
   1222 
   1223 void
   1224 check_expr(struct node *np)
   1225 {
   1226 	ASSERT(np != NULL);
   1227 
   1228 	switch (np->t) {
   1229 	case T_ASSIGN:
   1230 		ASSERT(np->u.expr.left != NULL);
   1231 		if (np->u.expr.left->t != T_GLOBID)
   1232 			outfl(O_ERR, np->file, np->line,
   1233 			    "assignment only allowed to globals (e.g. $a)");
   1234 		break;
   1235 	}
   1236 }
   1237 
   1238 void
   1239 check_event(struct node *np)
   1240 {
   1241 	ASSERT(np != NULL);
   1242 	ASSERTinfo(np->t == T_EVENT, ptree_nodetype2str(np->t));
   1243 
   1244 	if (np->u.event.epname == NULL) {
   1245 		outfl(O_ERR|O_NONL, np->file, np->line,
   1246 		    "pathless events not allowed: ");
   1247 		ptree_name(O_ERR|O_NONL, np->u.event.ename);
   1248 		out(O_ERR, NULL);
   1249 	}
   1250 }
   1251 
   1252 /*
   1253  * check for properties that are required on declarations. This
   1254  * should be done after all declarations since they can be
   1255  * redeclared with a different set of properties.
   1256  */
   1257 /*ARGSUSED*/
   1258 void
   1259 check_required_props(struct node *lhs, struct node *rhs, void *arg)
   1260 {
   1261 	ASSERTeq(rhs->t, (enum nodetype)arg, ptree_nodetype2str);
   1262 
   1263 	check_stmt_required_properties(rhs);
   1264 }
   1265 
   1266 /*
   1267  * check that cascading prop statements do not contain lists internally.
   1268  * the first and last event lists in the cascading prop may be single
   1269  * events or lists of events.
   1270  */
   1271 /*ARGSUSED*/
   1272 void
   1273 check_proplists(enum nodetype t, struct node *np)
   1274 {
   1275 	ASSERT(np->t == T_ARROW);
   1276 	/*
   1277 	 * not checking the right hand side of the top level prop
   1278 	 * since it is the last part of the propagation and can be
   1279 	 * an event or list of events
   1280 	 */
   1281 	check_proplists_lhs(t, np->u.arrow.lhs);
   1282 }
   1283 
   1284 /*ARGSUSED*/
   1285 static void
   1286 check_proplists_lhs(enum nodetype t, struct node *lhs)
   1287 {
   1288 	if (lhs->t == T_ARROW) {
   1289 		if (lhs->u.arrow.rhs->t == T_LIST) {
   1290 			outfl(O_ERR, lhs->file, lhs->line,
   1291 			    "lists are not allowed internally on cascading %s",
   1292 			    (t == T_PROP) ? "propagations" : "masks");
   1293 		}
   1294 		check_proplists_lhs(t, lhs->u.arrow.lhs);
   1295 	}
   1296 }
   1297