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
check_init(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
check_fini(void)109 check_fini(void)
110 {
111 }
112 
113 /*ARGSUSED*/
114 void
check_report_combination(struct node * np)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
check_path_iterators(struct node * np)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
check_arrow(struct node * np)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
check_nork(struct node * np)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
check_reportlist(enum nodetype t,const char * s,struct node * np)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
check_num(enum nodetype t,const char * s,struct node * np)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
check_quote(enum nodetype t,const char * s,struct node * np)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
check_action(enum nodetype t,const char * s,struct node * np)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
check_num_func(enum nodetype t,const char * s,struct node * np)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
check_fru_asru(enum nodetype t,const char * s,struct node * np)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
check_engine(enum nodetype t,const char * s,struct node * np)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
check_count(enum nodetype t,const char * s,struct node * np)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
check_timeval(enum nodetype t,const char * s,struct node * np)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
check_id(enum nodetype t,const char * s,struct node * np)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
check_serd_method(enum nodetype t,const char * s,struct node * np)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
check_serd_id(enum nodetype t,const char * s,struct node * np)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
check_stmt_required_properties(struct node * stmtnp)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
check_stmt_allowed_properties(enum nodetype t,struct node * nvpairnp,struct lut * lutp)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
check_propnames(enum nodetype t,struct node * np,int from,int to)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 *
record_iterators(struct node * np,struct lut * ex)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
check_exprscope(struct node * np,struct lut * ex)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
check_propscope(struct node * np)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
check_upset_engine(struct node * lhs,struct node * rhs,void * arg)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
check_refcount(struct node * lhs,struct node * rhs,void * arg)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
check_cycle_level(long long val)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
check_cycle(struct node * lhs,struct node * rhs,void * arg)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
check_cycle_lhs(struct node * stmtnp,struct node * arrow)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
check_cycle_lhs_try(struct node * stmtnp,struct node * lhs,struct node * rhs)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
check_cycle_rhs(struct node * rhs)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
check_name_iterator(struct node * np)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
check_type_iterator(struct node * np)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
check_cat_list(struct node * np)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
check_func(struct node * np)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
check_expr(struct node * np)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
check_event(struct node * np)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
check_required_props(struct node * lhs,struct node * rhs,void * arg)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
check_proplists(enum nodetype t,struct node * np)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
check_proplists_lhs(enum nodetype t,struct node * lhs)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