dmake: do not set MAKEFLAGS=k
[unleashed/tickless.git] / usr / src / cmd / fm / eversholt / common / check.c
blob21f9a9fe2ce0e96896030e592d94a20af8a874c4
1 /*
2 * CDDL HEADER START
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.
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.
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]
19 * CDDL HEADER END
22 * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
25 * check.c -- routines for checking the prop tree
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.
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"
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);
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 },
98 void
99 check_init(void)
101 int i;
103 for (i = 0; Allowednames[i].t; i++)
104 if (Allowednames[i].name != NULL)
105 Allowednames[i].name = stable(Allowednames[i].name);
108 void
109 check_fini(void)
113 /*ARGSUSED*/
114 void
115 check_report_combination(struct node *np)
117 /* nothing to check for here. poller is only prop and it is optional */
121 * check_path_iterators -- verify all iterators are explicit
123 static void
124 check_path_iterators(struct node *np)
126 if (np == NULL)
127 return;
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;
135 case T_LIST:
136 check_path_iterators(np->u.expr.left);
137 check_path_iterators(np->u.expr.right);
138 break;
140 case T_EVENT:
141 check_path_iterators(np->u.event.epname);
142 break;
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;
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));
161 void
162 check_arrow(struct node *np)
164 ASSERTinfo(np->t == T_ARROW, ptree_nodetype2str(np->t));
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));
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");
181 check_path_iterators(np);
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)
190 static int
191 check_nork(struct node *np)
193 int rval = 0;
195 /* NULL means no nork value which is allowed */
196 if (np == NULL) {
197 rval = 1;
199 else
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);
206 /* T_NUM allowed */
207 if (np->t == T_NUM)
208 rval = 1;
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;
218 return (rval);
221 static int
222 check_reportlist(enum nodetype t, const char *s, struct node *np)
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);
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);
241 return (1);
244 static int
245 check_num(enum nodetype t, const char *s, struct node *np)
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);
255 /*ARGSUSED1*/
256 static int
257 check_quote(enum nodetype t, const char *s, struct node *np)
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);
267 static int
268 check_action(enum nodetype t, const char *s, struct node *np)
270 ASSERTinfo(np != NULL, ptree_nodetype2str(t));
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);
279 static int
280 check_num_func(enum nodetype t, const char *s, struct node *np)
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);
290 static int
291 check_fru_asru(enum nodetype t, const char *s, struct node *np)
293 ASSERT(s != NULL);
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);
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);
318 static int
319 check_engine(enum nodetype t, const char *s, struct node *np)
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);
328 return (1);
331 static int
332 check_count(enum nodetype t, const char *s, struct node *np)
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);
341 /* XXX confirm engine has been declared */
342 return (1);
345 static int
346 check_timeval(enum nodetype t, const char *s, struct node *np)
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);
356 static int
357 check_id(enum nodetype t, const char *s, struct node *np)
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);
367 static int
368 check_serd_method(enum nodetype t, const char *s, struct node *np)
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);
380 static int
381 check_serd_id(enum nodetype t, const char *s, struct node *np)
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);
391 void
392 check_stmt_required_properties(struct node *stmtnp)
394 struct lut *lutp = stmtnp->u.stmt.lutp;
395 struct node *np = stmtnp->u.stmt.np;
396 int i;
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);
409 void
410 check_stmt_allowed_properties(enum nodetype t,
411 struct node *nvpairnp, struct lut *lutp)
413 int i;
414 const char *s = nvpairnp->u.expr.left->u.name.s;
415 struct node *np;
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) {
431 * redeclaring prop is allowed if value is the same
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;
445 case T_NAME:
446 if (tree_namecmp(np,
447 nvpairnp->u.expr.right) == 0)
448 return;
449 break;
451 case T_EVENT:
452 if (tree_eventcmp(np,
453 nvpairnp->u.expr.right) == 0)
454 return;
455 break;
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;
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);
472 void
473 check_propnames(enum nodetype t, struct node *np, int from, int to)
475 struct node *dnp;
476 struct lut *lutp;
478 ASSERT(np != NULL);
479 ASSERTinfo(np->t == T_EVENT || np->t == T_LIST || np->t == T_ARROW,
480 ptree_nodetype2str(np->t));
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;
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));
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;
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;
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));
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;
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);
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);
577 static struct lut *
578 record_iterators(struct node *np, struct lut *ex)
580 if (np == NULL)
581 return (ex);
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;
589 case T_LIST:
590 ex = record_iterators(np->u.expr.left, ex);
591 ex = record_iterators(np->u.expr.right, ex);
592 break;
594 case T_EVENT:
595 ex = record_iterators(np->u.event.epname, ex);
596 break;
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;
605 default:
606 outfl(O_DIE, np->file, np->line,
607 "record_iterators: internal error: unexpected type: %s",
608 ptree_nodetype2str(np->t));
611 return (ex);
614 void
615 check_exprscope(struct node *np, struct lut *ex)
617 if (np == NULL)
618 return;
620 switch (np->t) {
621 case T_EVENT:
622 check_exprscope(np->u.event.eexprlist, ex);
623 break;
625 case T_ARROW:
626 check_exprscope(np->u.arrow.lhs, ex);
627 check_exprscope(np->u.arrow.rhs, ex);
628 break;
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);
639 check_exprscope(np->u.name.next, ex);
640 break;
642 case T_QUOTE:
643 case T_GLOBID:
644 break;
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;
674 case T_FUNC:
675 check_exprscope(np->u.func.arglist, ex);
676 break;
678 case T_NUM:
679 case T_TIMEVAL:
680 break;
682 default:
683 outfl(O_DIE, np->file, np->line,
684 "check_exprscope: internal error: unexpected type: %s",
685 ptree_nodetype2str(np->t));
690 * check_propscope -- check constraints for out of scope variable refs
692 void
693 check_propscope(struct node *np)
695 struct lut *ex;
697 ex = record_iterators(np, NULL);
698 check_exprscope(np, ex);
699 lut_free(ex, NULL, NULL);
703 * check_upset_engine -- validate the engine property in an upset statement
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.
711 /*ARGSUSED*/
712 void
713 check_upset_engine(struct node *lhs, struct node *rhs, void *arg)
715 enum nodetype t = (enum nodetype)arg;
716 struct node *engnp;
717 struct node *declp;
719 ASSERTeq(rhs->t, t, ptree_nodetype2str);
721 if ((engnp = tree_s2np_lut_lookup(rhs->u.stmt.lutp, L_engine)) == NULL)
722 return;
724 ASSERT(engnp->t == T_EVENT);
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;
732 engnp->u.event.declp = declp;
736 * check_refcount -- see if declared names are used
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.
742 void
743 check_refcount(struct node *lhs, struct node *rhs, void *arg)
745 enum nodetype t = (enum nodetype)arg;
747 ASSERTeq(rhs->t, t, ptree_nodetype2str);
749 if (rhs->u.stmt.flags & STMT_REF)
750 return;
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);
759 * set check_cycle_warninglevel only for val >= 0
762 check_cycle_level(long long val)
764 static int check_cycle_warninglevel = -1;
766 if (val == 0)
767 check_cycle_warninglevel = 0;
768 else if (val > 0)
769 check_cycle_warninglevel = 1;
771 return (check_cycle_warninglevel);
775 * check_cycle -- see props from an error have cycles
777 * this is run after the entire parse tree is constructed, for
778 * each error that has been declared.
781 /*ARGSUSED*/
782 void
783 check_cycle(struct node *lhs, struct node *rhs, void *arg)
785 struct node *np;
787 ASSERTeq(rhs->t, T_ERROR, ptree_nodetype2str);
789 if (rhs->u.stmt.flags & STMT_CYCLE)
790 return; /* already reported this cycle */
792 if (rhs->u.stmt.flags & STMT_CYMARK) {
793 #ifdef ESC
794 int warninglevel;
796 warninglevel = check_cycle_level(-1);
797 if (warninglevel <= 0) {
798 int olevel = O_ERR;
800 if (warninglevel == 0)
801 olevel = O_WARN;
803 out(olevel|O_NONL, "cycle in propagation tree: ");
804 ptree_name(olevel|O_NONL, rhs->u.stmt.np);
805 out(olevel, NULL);
807 #endif /* ESC */
809 rhs->u.stmt.flags |= STMT_CYCLE;
812 rhs->u.stmt.flags |= STMT_CYMARK;
814 /* for each propagation */
815 for (np = Props; np; np = np->u.stmt.next)
816 check_cycle_lhs(rhs, np->u.stmt.np);
818 rhs->u.stmt.flags &= ~STMT_CYMARK;
822 * check_cycle_lhs -- find the lhs of an arrow for cycle checking
825 static void
826 check_cycle_lhs(struct node *stmtnp, struct node *arrow)
828 struct node *trylhs;
829 struct node *tryrhs;
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);
838 * return if there's a list of events internal to
839 * cascaded props (which is not allowed)
841 if (arrow->u.arrow.lhs->u.arrow.rhs->t != T_EVENT)
842 return;
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;
849 case T_EVENT:
850 case T_LIST:
851 trylhs = arrow->u.arrow.lhs;
852 tryrhs = arrow->u.arrow.rhs;
853 break;
855 default:
856 out(O_DIE, "lhs: unexpected type: %s",
857 ptree_nodetype2str(arrow->u.arrow.lhs->t));
858 /*NOTREACHED*/
861 check_cycle_lhs_try(stmtnp, trylhs, tryrhs);
865 * check_cycle_lhs_try -- try matching an event name on lhs of an arrow
868 static void
869 check_cycle_lhs_try(struct node *stmtnp, struct node *lhs, struct node *rhs)
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;
877 ASSERT(lhs->t == T_EVENT);
879 if (tree_eventcmp(stmtnp->u.stmt.np, lhs) != 0)
880 return; /* no match */
882 check_cycle_rhs(rhs);
886 * check_cycle_rhs -- foreach error on rhs, see if we cycle to a marked error
889 static void
890 check_cycle_rhs(struct node *rhs)
892 struct node *dnp;
894 if (rhs->t == T_LIST) {
895 check_cycle_rhs(rhs->u.expr.left);
896 check_cycle_rhs(rhs->u.expr.right);
897 return;
900 ASSERT(rhs->t == T_EVENT);
902 if (rhs->u.event.ename->u.name.t != N_ERROR)
903 return;
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;
913 check_cycle(NULL, dnp, 0);
917 * Force iterators to be simple names, expressions, or numbers
919 void
920 check_name_iterator(struct node *np)
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);
940 * Iterators on a declaration may only be implicit
942 void
943 check_type_iterator(struct node *np)
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);
957 np = np->u.name.next;
958 } else {
959 break;
964 void
965 check_cat_list(struct node *np)
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);
975 void
976 check_func(struct node *np)
978 struct node *arglist = np->u.func.arglist;
980 ASSERTinfo(np->t == T_FUNC, ptree_nodetype2str(np->t));
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.");
990 break;
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.");
998 break;
1000 case T_LIST:
1002 * if two parameters, the left or min must be
1003 * either T_NUM or T_TIMEVAL
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.");
1013 * if two parameters, the right or max must
1014 * be either T_NUM, T_NAME or T_TIMEVAL
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.");
1025 * if right or left is a T_NUM it must
1026 * be zero
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.");
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.");
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.");
1050 * the first parameter [min] must not be greater
1051 * than the second parameter [max].
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");
1060 break;
1062 case T_TIMEVAL:
1063 break; /* no restrictions on T_TIMEVAL */
1065 default:
1066 outfl(O_ERR, arglist->file, arglist->line,
1067 "parameter of within must be 0"
1068 ", \"infinity\" or a time value.");
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);
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()");
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()");
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);
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()");
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);
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");
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");
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);
1219 void
1220 check_expr(struct node *np)
1222 ASSERT(np != NULL);
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;
1234 void
1235 check_event(struct node *np)
1237 ASSERT(np != NULL);
1238 ASSERTinfo(np->t == T_EVENT, ptree_nodetype2str(np->t));
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);
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.
1253 /*ARGSUSED*/
1254 void
1255 check_required_props(struct node *lhs, struct node *rhs, void *arg)
1257 ASSERTeq(rhs->t, (enum nodetype)arg, ptree_nodetype2str);
1259 check_stmt_required_properties(rhs);
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.
1267 /*ARGSUSED*/
1268 void
1269 check_proplists(enum nodetype t, struct node *np)
1271 ASSERT(np->t == T_ARROW);
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
1277 check_proplists_lhs(t, np->u.arrow.lhs);
1280 /*ARGSUSED*/
1281 static void
1282 check_proplists_lhs(enum nodetype t, struct node *lhs)
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");
1290 check_proplists_lhs(t, lhs->u.arrow.lhs);