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]
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.
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
,
60 static void check_cycle_rhs(struct node
*rhs
);
61 static void check_proplists_lhs(enum nodetype t
, struct node
*lhs
);
67 int (*checker
)(enum nodetype t
, const char *s
, struct node
*np
);
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
},
103 for (i
= 0; Allowednames
[i
].t
; i
++)
104 if (Allowednames
[i
].name
!= NULL
)
105 Allowednames
[i
].name
= stable(Allowednames
[i
].name
);
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
124 check_path_iterators(struct node
*np
)
131 check_path_iterators(np
->u
.arrow
.lhs
);
132 check_path_iterators(np
->u
.arrow
.rhs
);
136 check_path_iterators(np
->u
.expr
.left
);
137 check_path_iterators(np
->u
.expr
.right
);
141 check_path_iterators(np
->u
.event
.epname
);
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",
150 check_path_iterators(np
->u
.name
.next
);
154 outfl(O_DIE
, np
->file
, np
->line
,
155 "internal error: check_path_iterators: "
156 "unexpected type: %s",
157 ptree_nodetype2str(np
->t
));
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
) {
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 "
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)
191 check_nork(struct node
*np
)
195 /* NULL means no nork value which is allowed */
201 /* if the nork is a name it must be A for "All" */
203 if (*np
->u
.name
.s
== 'A')
210 /* simple expressions allowed */
211 if (np
->t
== T_SUB
||
222 check_reportlist(enum nodetype t
, const char *s
, struct node
*np
)
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
);
245 check_num(enum nodetype t
, const char *s
, struct node
*np
)
247 ASSERTinfo(np
!= NULL
, ptree_nodetype2str(t
));
249 outfl(O_ERR
, np
->file
, np
->line
,
250 "%s %s property must be a single number",
251 ptree_nodetype2str(t
), s
);
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
));
268 check_action(enum nodetype t
, const char *s
, struct node
*np
)
270 ASSERTinfo(np
!= NULL
, ptree_nodetype2str(t
));
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
);
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
);
291 check_fru_asru(enum nodetype t
, const char *s
, struct node
*np
)
295 /* make sure it is a node type T_NAME? */
296 if (np
->t
== T_NAME
) {
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");
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
);
312 outfl(O_ERR
, np
->file
, np
->line
,
313 "illegal type used for %s property: %s",
314 s
, ptree_nodetype2str(np
->t
));
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
);
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 */
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
);
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
);
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
);
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
);
392 check_stmt_required_properties(struct node
*stmtnp
)
394 struct lut
*lutp
= stmtnp
->u
.stmt
.lutp
;
395 struct node
*np
= stmtnp
->u
.stmt
.np
;
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
,
404 "%s statement missing property: %s",
405 ptree_nodetype2str(stmtnp
->t
),
406 Allowednames
[i
].name
);
410 check_stmt_allowed_properties(enum nodetype t
,
411 struct node
*nvpairnp
, struct lut
*lutp
)
414 const char *s
= nvpairnp
->u
.expr
.left
->u
.name
.s
;
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
);
423 } else if (t
== Allowednames
[i
].t
&& s
== Allowednames
[i
].name
)
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
);
441 if (np
->u
.ull
== nvpairnp
->u
.expr
.right
->u
.ull
)
447 nvpairnp
->u
.expr
.right
) == 0)
452 if (tree_eventcmp(np
,
453 nvpairnp
->u
.expr
.right
) == 0)
458 outfl(O_ERR
, nvpairnp
->file
, nvpairnp
->line
,
459 "value for property \"%s\" is an "
461 nvpairnp
->u
.expr
.left
->u
.name
.s
,
462 ptree_nodetype2str(np
->t
));
465 outfl(O_ERR
, nvpairnp
->file
, nvpairnp
->line
,
466 "property redeclared in %s declaration: %s",
467 ptree_nodetype2str(t
), s
);
469 (*Allowednames
[i
].checker
)(t
, s
, nvpairnp
->u
.expr
.right
);
473 check_propnames(enum nodetype t
, struct node
*np
, int from
, int to
)
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
) {
485 outfl(O_ERR
, np
->file
, np
->line
,
486 "name in %s statement must begin with "
487 "type (example: \"error.\")",
488 ptree_nodetype2str(t
));
493 outfl(O_ERR
, np
->file
, np
->line
,
494 "%s has fault on right side of \"->\"",
495 ptree_nodetype2str(t
));
499 outfl(O_DIE
, np
->file
, np
->line
,
500 "internal error: %s has fault without "
502 ptree_nodetype2str(t
));
508 outfl(O_ERR
, np
->file
, np
->line
,
509 "%s has upset on right side of \"->\"",
510 ptree_nodetype2str(t
));
514 outfl(O_DIE
, np
->file
, np
->line
,
515 "internal error: %s has upset without "
517 ptree_nodetype2str(t
));
522 outfl(O_ERR
, np
->file
, np
->line
,
523 "%s has defect on right side of \"->\"",
524 ptree_nodetype2str(t
));
528 outfl(O_DIE
, np
->file
, np
->line
,
529 "internal error: %s has defect without "
531 ptree_nodetype2str(t
));
537 outfl(O_DIE
, np
->file
, np
->line
,
538 "%s has error without from or to flags",
539 ptree_nodetype2str(t
));
544 outfl(O_ERR
, np
->file
, np
->line
,
545 "%s has report on left side of \"->\"",
546 ptree_nodetype2str(t
));
550 outfl(O_DIE
, np
->file
, np
->line
,
551 "internal error: %s has report without "
553 ptree_nodetype2str(t
));
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
));
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);
578 record_iterators(struct node
*np
, struct lut
*ex
)
585 ex
= record_iterators(np
->u
.arrow
.lhs
, ex
);
586 ex
= record_iterators(np
->u
.arrow
.rhs
, ex
);
590 ex
= record_iterators(np
->u
.expr
.left
, ex
);
591 ex
= record_iterators(np
->u
.expr
.right
, ex
);
595 ex
= record_iterators(np
->u
.event
.epname
, ex
);
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
,
602 ex
= record_iterators(np
->u
.name
.next
, ex
);
606 outfl(O_DIE
, np
->file
, np
->line
,
607 "record_iterators: internal error: unexpected type: %s",
608 ptree_nodetype2str(np
->t
));
615 check_exprscope(struct node
*np
, struct lut
*ex
)
622 check_exprscope(np
->u
.event
.eexprlist
, ex
);
626 check_exprscope(np
->u
.arrow
.lhs
, ex
);
627 check_exprscope(np
->u
.arrow
.rhs
, ex
);
631 if (np
->u
.name
.child
&& np
->u
.name
.child
->t
== T_NAME
) {
633 (void *) np
->u
.name
.child
->u
.name
.s
, NULL
) == NULL
)
634 outfl(O_ERR
, np
->file
, np
->line
,
635 "constraint contains undefined"
637 np
->u
.name
.child
->u
.name
.s
);
639 check_exprscope(np
->u
.name
.next
, ex
);
670 check_exprscope(np
->u
.expr
.left
, ex
);
671 check_exprscope(np
->u
.expr
.right
, ex
);
675 check_exprscope(np
->u
.func
.arglist
, ex
);
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
693 check_propscope(struct node
*np
)
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"
713 check_upset_engine(struct node
*lhs
, struct node
*rhs
, void *arg
)
715 enum nodetype t
= (enum nodetype
)arg
;
719 ASSERTeq(rhs
->t
, t
, ptree_nodetype2str
);
721 if ((engnp
= tree_s2np_lut_lookup(rhs
->u
.stmt
.lutp
, L_engine
)) == NULL
)
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
);
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.
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
)
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
);
759 * set check_cycle_warninglevel only for val >= 0
762 check_cycle_level(long long val
)
764 static int check_cycle_warninglevel
= -1;
767 check_cycle_warninglevel
= 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.
783 check_cycle(struct node
*lhs
, struct node
*rhs
, void *arg
)
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
) {
796 warninglevel
= check_cycle_level(-1);
797 if (warninglevel
<= 0) {
800 if (warninglevel
== 0)
803 out(olevel
|O_NONL
, "cycle in propagation tree: ");
804 ptree_name(olevel
|O_NONL
, rhs
->u
.stmt
.np
);
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
826 check_cycle_lhs(struct node
*stmtnp
, struct node
*arrow
)
831 /* handle cascaded arrows */
832 switch (arrow
->u
.arrow
.lhs
->t
) {
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
)
844 /* then try this arrow (thing cascaded *to*) */
845 trylhs
= arrow
->u
.arrow
.lhs
->u
.arrow
.rhs
;
846 tryrhs
= arrow
->u
.arrow
.rhs
;
851 trylhs
= arrow
->u
.arrow
.lhs
;
852 tryrhs
= arrow
->u
.arrow
.rhs
;
856 out(O_DIE
, "lhs: unexpected type: %s",
857 ptree_nodetype2str(arrow
->u
.arrow
.lhs
->t
));
861 check_cycle_lhs_try(stmtnp
, trylhs
, tryrhs
);
865 * check_cycle_lhs_try -- try matching an event name on lhs of an arrow
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
);
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
890 check_cycle_rhs(struct node
*rhs
)
894 if (rhs
->t
== T_LIST
) {
895 check_cycle_rhs(rhs
->u
.expr
.left
);
896 check_cycle_rhs(rhs
->u
.expr
.right
);
900 ASSERT(rhs
->t
== T_EVENT
);
902 if (rhs
->u
.event
.ename
->u
.name
.t
!= N_ERROR
)
905 if ((dnp
= tree_event2np_lut_lookup(Errors
, rhs
)) == NULL
) {
907 rhs
->file
, rhs
->line
,
908 "unexpected undeclared event during cycle check");
909 ptree_name(O_ERR
|O_NONL
, rhs
);
913 check_cycle(NULL
, dnp
, 0);
917 * Force iterators to be simple names, expressions, or numbers
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
);
940 * Iterators on a declaration may only be implicit
943 check_type_iterator(struct node
*np
)
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
);
957 np
= np
->u
.name
.next
;
965 check_cat_list(struct node
*np
)
969 else if (np
->t
== T_LIST
) {
970 check_cat_list(np
->u
.expr
.left
);
971 check_cat_list(np
->u
.expr
.right
);
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
) {
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.");
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.");
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
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");
1063 break; /* no restrictions on T_TIMEVAL */
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
);
1099 outfl(O_ERR
, arglist
->file
, arglist
->line
,
1100 "%s() must have paths or calls to "
1101 "fru() and/or asru() as arguments",
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
);
1109 outfl(O_ERR
, arglist
->file
, arglist
->line
,
1110 "argument to is_on() must be a path or a call to "
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
);
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
);
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
);
1140 outfl(O_ERR
, arglist
->file
, arglist
->line
,
1141 "argument to is_type() must be a path or a call to "
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
);
1160 outfl(O_ERR
, arglist
->file
, arglist
->line
,
1161 "%s(): first argument must be a path or a call to "
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 "
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 "
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
);
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");
1214 outfl(O_WARN
, np
->file
, np
->line
,
1215 "possible platform-specific function: %s",
1220 check_expr(struct node
*np
)
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)");
1235 check_event(struct node
*np
)
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
);
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.
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.
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
);
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
);