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
44static int check_reportlist(enum nodetype t, const char *s, struct node *np);
45static int check_num(enum nodetype t, const char *s, struct node *np);
46static int check_quote(enum nodetype t, const char *s, struct node *np);
47static int check_action(enum nodetype t, const char *s, struct node *np);
48static int check_num_func(enum nodetype t, const char *s, struct node *np);
49static int check_fru_asru(enum nodetype t, const char *s, struct node *np);
50static int check_engine(enum nodetype t, const char *s, struct node *np);
51static int check_count(enum nodetype t, const char *s, struct node *np);
52static int check_timeval(enum nodetype t, const char *s, struct node *np);
53static int check_id(enum nodetype t, const char *s, struct node *np);
54static int check_serd_method(enum nodetype t, const char *s, struct node *np);
55static int check_serd_id(enum nodetype t, const char *s, struct node *np);
56static int check_nork(struct node *np);
57static void check_cycle_lhs(struct node *stmtnp, struct node *arrow);
58static void check_cycle_lhs_try(struct node *stmtnp, struct node *lhs,
59    struct node *rhs);
60static void check_cycle_rhs(struct node *rhs);
61static void check_proplists_lhs(enum nodetype t, struct node *lhs);
62
63static 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
98void
99check_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
108void
109check_fini(void)
110{
111}
112
113/*ARGSUSED*/
114void
115check_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 */
123static void
124check_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
161void
162check_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 */
190static int
191check_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
221static int
222check_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
244static int
245check_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*/
256static int
257check_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
267static int
268check_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
279static int
280check_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
290static int
291check_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
318static int
319check_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
331static int
332check_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
345static int
346check_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
356static int
357check_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
367static int
368check_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
380static int
381check_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
391void
392check_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
409void
410check_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
472void
473check_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
577static struct lut *
578record_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
614void
615check_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 */
692void
693check_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*/
712void
713check_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
742void
743check_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 */
761int
762check_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*/
782void
783check_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
825static void
826check_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
868static void
869check_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
889static void
890check_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 */
919void
920check_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 */
942void
943check_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
964void
965check_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
975void
976check_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
1219void
1220check_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
1234void
1235check_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*/
1254void
1255check_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*/
1268void
1269check_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*/
1281static void
1282check_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