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, Version 1.0 only 6 * (the "License"). You may not use this file except in compliance 7 * with the License. 8 * 9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10 * or http://www.opensolaris.org/os/licensing. 11 * See the License for the specific language governing permissions 12 * and limitations under the License. 13 * 14 * When distributing Covered Code, include this CDDL HEADER in each 15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16 * If applicable, add the following below this CDDL HEADER, with the 17 * fields enclosed by brackets "[]" replaced with your own identifying 18 * information: Portions Copyright [yyyy] [name of copyright owner] 19 * 20 * CDDL HEADER END 21 */ 22 /* 23 * Copyright 2005 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 * 26 * check.c -- routines for checking the prop tree 27 * 28 * this module provides semantic checks on the parse tree. most of 29 * these checks happen during the construction of the parse tree, 30 * when the various tree_X() routines call the various check_X() 31 * routines. in a couple of special cases, a check function will 32 * process the parse tree after it has been fully constructed. these 33 * cases are noted in the comments above the check function. 34 */ 35 36 #pragma ident "%Z%%M% %I% %E% SMI" 37 38 #include <stdio.h> 39 #include "out.h" 40 #include "stable.h" 41 #include "literals.h" 42 #include "lut.h" 43 #include "tree.h" 44 #include "ptree.h" 45 #include "check.h" 46 47 static int check_reportlist(enum nodetype t, const char *s, struct node *np); 48 static int check_num(enum nodetype t, const char *s, struct node *np); 49 static int check_quote(enum nodetype t, const char *s, struct node *np); 50 static int check_num_func(enum nodetype t, const char *s, struct node *np); 51 static int check_fru_asru(enum nodetype t, const char *s, struct node *np); 52 static int check_engine(enum nodetype t, const char *s, struct node *np); 53 static int check_timeval(enum nodetype t, const char *s, struct node *np); 54 static int check_id(enum nodetype t, const char *s, struct node *np); 55 static int check_serd_method(enum nodetype t, const char *s, struct node *np); 56 static int check_nork(struct node *np); 57 static void check_cycle_lhs(struct node *stmtnp, struct node *arrow); 58 static void check_cycle_lhs_try(struct node *stmtnp, struct node *lhs, 59 struct node *rhs); 60 static void check_cycle_rhs(struct node *rhs); 61 static void check_proplists_lhs(enum nodetype t, struct node *lhs); 62 63 static struct { 64 enum nodetype t; 65 const char *name; 66 int required; 67 int (*checker)(enum nodetype t, const char *s, struct node *np); 68 int outflags; 69 } Allowednames[] = { 70 { T_FAULT, "FITrate", 1, 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_UPSET, "engine", 0, check_engine, O_ERR }, 74 { T_DEFECT, "FRU", 0, check_fru_asru, O_ERR }, 75 { T_DEFECT, "ASRU", 0, check_fru_asru, O_ERR }, 76 { T_EREPORT, "poller", 0, check_id, O_ERR }, 77 { T_EREPORT, "delivery", 0, check_timeval, O_ERR }, 78 { T_SERD, "N", 1, check_num, O_ERR }, 79 { T_SERD, "T", 1, check_timeval, O_ERR }, 80 { T_SERD, "method", 1, check_serd_method, O_ERR }, 81 { T_SERD, "trip", 1, check_reportlist, O_ERR }, 82 { T_SERD, "FRU", 0, check_fru_asru, O_ERR }, 83 { T_ERROR, "ASRU", 0, check_fru_asru, O_ERR }, 84 { T_CONFIG, NULL, 0, check_quote, O_ERR }, 85 { 0, NULL, 0 }, 86 }; 87 88 void 89 check_init(void) 90 { 91 int i; 92 93 for (i = 0; Allowednames[i].t; i++) 94 if (Allowednames[i].name != NULL) 95 Allowednames[i].name = stable(Allowednames[i].name); 96 } 97 98 void 99 check_fini(void) 100 { 101 } 102 103 /*ARGSUSED*/ 104 void 105 check_report_combination(struct node *np) 106 { 107 /* nothing to check for here. poller is only prop and it is optional */ 108 } 109 110 /* 111 * check_path_iterators -- verify all iterators are explicit 112 */ 113 static void 114 check_path_iterators(struct node *np) 115 { 116 if (np == NULL) 117 return; 118 119 switch (np->t) { 120 case T_ARROW: 121 check_path_iterators(np->u.arrow.lhs); 122 check_path_iterators(np->u.arrow.rhs); 123 break; 124 125 case T_LIST: 126 check_path_iterators(np->u.expr.left); 127 check_path_iterators(np->u.expr.right); 128 break; 129 130 case T_EVENT: 131 check_path_iterators(np->u.event.epname); 132 break; 133 134 case T_NAME: 135 if (np->u.name.child == NULL) 136 outfl(O_DIE, np->file, np->line, 137 "internal error: check_path_iterators: " 138 "unexpected implicit iterator: %s", 139 np->u.name.s); 140 check_path_iterators(np->u.name.next); 141 break; 142 143 default: 144 outfl(O_DIE, np->file, np->line, 145 "internal error: check_path_iterators: " 146 "unexpected type: %s", 147 ptree_nodetype2str(np->t)); 148 } 149 } 150 151 void 152 check_arrow(struct node *np) 153 { 154 ASSERTinfo(np->t == T_ARROW, ptree_nodetype2str(np->t)); 155 156 if (np->u.arrow.lhs->t != T_ARROW && 157 np->u.arrow.lhs->t != T_LIST && 158 np->u.arrow.lhs->t != T_EVENT) { 159 outfl(O_ERR, 160 np->u.arrow.lhs->file, np->u.arrow.lhs->line, 161 "%s not allowed on left-hand side of arrow", 162 ptree_nodetype2str(np->u.arrow.lhs->t)); 163 } 164 165 if (!check_nork(np->u.arrow.nnp) || 166 !check_nork(np->u.arrow.knp)) 167 outfl(O_ERR, np->file, np->line, 168 "counts associated with propagation arrows " 169 "must be integers"); 170 171 check_path_iterators(np); 172 } 173 174 /* 175 * make sure the nork values are valid. 176 * Nork values must be "A" for all(T_NAME), 177 * a number(T_NUM), or a simple 178 * expression(T_SUB, T_ADD, T_MUL, T_DIV) 179 */ 180 static int 181 check_nork(struct node *np) 182 { 183 int rval = 0; 184 185 /* NULL means no nork value which is allowed */ 186 if (np == NULL) { 187 rval = 1; 188 } 189 else 190 { 191 /* if the nork is a name it must be A for "All" */ 192 if (np->t == T_NAME) 193 if (*np->u.name.s == 'A') 194 return (1); 195 196 /* T_NUM allowed */ 197 if (np->t == T_NUM) 198 rval = 1; 199 200 /* simple expressions allowed */ 201 if (np->t == T_SUB || 202 np->t == T_ADD || 203 np->t == T_MUL || 204 np->t == T_DIV) 205 rval = 1; 206 } 207 208 return (rval); 209 } 210 211 static int 212 check_reportlist(enum nodetype t, const char *s, struct node *np) 213 { 214 if (np == NULL) 215 return (1); 216 else if (np->t == T_EVENT) { 217 if (np->u.event.ename->u.name.t != N_EREPORT) { 218 outfl(O_ERR, np->file, np->line, 219 "%s %s property must begin with \"ereport.\"", 220 ptree_nodetype2str(t), s); 221 } else if (tree_event2np_lut_lookup(Ereports, np) == NULL) { 222 outfl(O_ERR, np->file, np->line, 223 "%s %s property contains undeclared name", 224 ptree_nodetype2str(t), s); 225 } 226 check_type_iterator(np); 227 } else if (np->t == T_LIST) { 228 (void) check_reportlist(t, s, np->u.expr.left); 229 (void) check_reportlist(t, s, np->u.expr.right); 230 } 231 return (1); 232 } 233 234 static int 235 check_num(enum nodetype t, const char *s, struct node *np) 236 { 237 ASSERTinfo(np != NULL, ptree_nodetype2str(t)); 238 if (np->t != T_NUM) 239 outfl(O_ERR, np->file, np->line, 240 "%s %s property must be a single number", 241 ptree_nodetype2str(t), s); 242 return (1); 243 } 244 245 /*ARGSUSED1*/ 246 static int 247 check_quote(enum nodetype t, const char *s, struct node *np) 248 { 249 ASSERTinfo(np != NULL, ptree_nodetype2str(t)); 250 if (np->t != T_QUOTE) 251 outfl(O_ERR, np->file, np->line, 252 "%s properties must be quoted strings", 253 ptree_nodetype2str(t)); 254 return (1); 255 } 256 257 static int 258 check_num_func(enum nodetype t, const char *s, struct node *np) 259 { 260 ASSERTinfo(np != NULL, ptree_nodetype2str(t)); 261 if (np->t != T_NUM && np->t != T_FUNC) 262 outfl(O_ERR, np->file, np->line, 263 "%s %s property must be a number or function", 264 ptree_nodetype2str(t), s); 265 return (1); 266 } 267 268 static int 269 check_fru_asru(enum nodetype t, const char *s, struct node *np) 270 { 271 ASSERT(s != NULL); 272 273 /* make sure it is a node type T_NAME? */ 274 if (np->t == T_NAME) { 275 if (s == L_ASRU) { 276 if (tree_name2np_lut_lookup_name(ASRUs, np) == NULL) 277 outfl(O_ERR, np->file, np->line, 278 "ASRU property contains undeclared asru"); 279 } else if (s == L_FRU) { 280 if (tree_name2np_lut_lookup_name(FRUs, np) == NULL) 281 outfl(O_ERR, np->file, np->line, 282 "FRU property contains undeclared fru"); 283 } else { 284 outfl(O_ERR, np->file, np->line, 285 "illegal property name in %s declaration: %s", 286 ptree_nodetype2str(t), s); 287 } 288 check_type_iterator(np); 289 } else 290 outfl(O_ERR, np->file, np->line, 291 "illegal type used for %s property: %s", 292 s, ptree_nodetype2str(np->t)); 293 return (1); 294 } 295 296 static int 297 check_engine(enum nodetype t, const char *s, struct node *np) 298 { 299 ASSERTinfo(np != NULL, ptree_nodetype2str(t)); 300 if (np->t != T_EVENT) 301 outfl(O_ERR, np->file, np->line, 302 "%s %s property must be an engine name " 303 "(i.e. serd.x or serd.x@a/b)", 304 ptree_nodetype2str(t), s); 305 306 return (1); 307 } 308 309 static int 310 check_timeval(enum nodetype t, const char *s, struct node *np) 311 { 312 ASSERTinfo(np != NULL, ptree_nodetype2str(t)); 313 if (np->t != T_TIMEVAL) 314 outfl(O_ERR, np->file, np->line, 315 "%s %s property must be a number with time units", 316 ptree_nodetype2str(t), s); 317 return (1); 318 } 319 320 static int 321 check_id(enum nodetype t, const char *s, struct node *np) 322 { 323 ASSERTinfo(np != NULL, ptree_nodetype2str(t)); 324 if (np->t != T_NAME || np->u.name.next || np->u.name.child) 325 outfl(O_ERR, np->file, np->line, 326 "%s %s property must be simple name", 327 ptree_nodetype2str(t), s); 328 return (1); 329 } 330 331 static int 332 check_serd_method(enum nodetype t, const char *s, struct node *np) 333 { 334 ASSERTinfo(np != NULL, ptree_nodetype2str(t)); 335 if (np->t != T_NAME || np->u.name.next || np->u.name.child || 336 (np->u.name.s != L_volatile && 337 np->u.name.s != L_persistent)) 338 outfl(O_ERR, np->file, np->line, 339 "%s %s property must be \"volatile\" or \"persistent\"", 340 ptree_nodetype2str(t), s); 341 return (1); 342 } 343 344 void 345 check_stmt_required_properties(struct node *stmtnp) 346 { 347 struct lut *lutp = stmtnp->u.stmt.lutp; 348 struct node *np = stmtnp->u.stmt.np; 349 int i; 350 351 for (i = 0; Allowednames[i].t; i++) 352 if (stmtnp->t == Allowednames[i].t && 353 Allowednames[i].required && 354 tree_s2np_lut_lookup(lutp, Allowednames[i].name) == NULL) 355 outfl(Allowednames[i].outflags, 356 np->file, np->line, 357 "%s statement missing property: %s", 358 ptree_nodetype2str(stmtnp->t), 359 Allowednames[i].name); 360 } 361 362 void 363 check_stmt_allowed_properties(enum nodetype t, 364 struct node *nvpairnp, struct lut *lutp) 365 { 366 int i; 367 const char *s = nvpairnp->u.expr.left->u.name.s; 368 struct node *np; 369 370 for (i = 0; Allowednames[i].t; i++) 371 if (t == Allowednames[i].t && Allowednames[i].name == NULL) { 372 /* NULL name means just call checker */ 373 (*Allowednames[i].checker)(t, s, 374 nvpairnp->u.expr.right); 375 return; 376 } else if (t == Allowednames[i].t && s == Allowednames[i].name) 377 break; 378 if (Allowednames[i].name == NULL) 379 outfl(O_ERR, nvpairnp->file, nvpairnp->line, 380 "illegal property name in %s declaration: %s", 381 ptree_nodetype2str(t), s); 382 else if ((np = tree_s2np_lut_lookup(lutp, s)) != NULL) { 383 /* 384 * redeclaring prop is allowed if value is the same 385 */ 386 if (np->t != nvpairnp->u.expr.right->t) 387 outfl(O_ERR, nvpairnp->file, nvpairnp->line, 388 "property redeclared (with differnt type) " 389 "in %s declaration: %s", 390 ptree_nodetype2str(t), s); 391 switch (np->t) { 392 case T_NUM: 393 case T_TIMEVAL: 394 if (np->u.ull == nvpairnp->u.expr.right->u.ull) 395 return; 396 break; 397 398 case T_NAME: 399 if (tree_namecmp(np, 400 nvpairnp->u.expr.right) == 0) 401 return; 402 break; 403 404 case T_EVENT: 405 if (tree_eventcmp(np, 406 nvpairnp->u.expr.right) == 0) 407 return; 408 break; 409 410 default: 411 outfl(O_ERR, nvpairnp->file, nvpairnp->line, 412 "value for property \"%s\" is an " 413 "invalid type: %s", 414 nvpairnp->u.expr.left->u.name.s, 415 ptree_nodetype2str(np->t)); 416 return; 417 } 418 outfl(O_ERR, nvpairnp->file, nvpairnp->line, 419 "property redeclared in %s declaration: %s", 420 ptree_nodetype2str(t), s); 421 } else 422 (*Allowednames[i].checker)(t, s, nvpairnp->u.expr.right); 423 } 424 425 void 426 check_propnames(enum nodetype t, struct node *np, int from, int to) 427 { 428 struct node *dnp; 429 struct lut *lutp; 430 431 ASSERT(np != NULL); 432 ASSERTinfo(np->t == T_EVENT || np->t == T_LIST || np->t == T_ARROW, 433 ptree_nodetype2str(np->t)); 434 435 if (np->t == T_EVENT) { 436 switch (np->u.event.ename->u.name.t) { 437 case N_UNSPEC: 438 outfl(O_ERR, np->file, np->line, 439 "name in %s statement must begin with " 440 "type (example: \"error.\")", 441 ptree_nodetype2str(t)); 442 return; 443 case N_FAULT: 444 lutp = Faults; 445 if (to) { 446 outfl(O_ERR, np->file, np->line, 447 "%s has fault on right side of \"->\"", 448 ptree_nodetype2str(t)); 449 return; 450 } 451 if (!from) { 452 outfl(O_DIE, np->file, np->line, 453 "internal error: %s has fault without " 454 "from flag", 455 ptree_nodetype2str(t)); 456 } 457 break; 458 case N_UPSET: 459 lutp = Upsets; 460 if (to) { 461 outfl(O_ERR, np->file, np->line, 462 "%s has upset on right side of \"->\"", 463 ptree_nodetype2str(t)); 464 return; 465 } 466 if (!from) 467 outfl(O_DIE, np->file, np->line, 468 "internal error: %s has upset without " 469 "from flag", 470 ptree_nodetype2str(t)); 471 break; 472 case N_DEFECT: 473 lutp = Defects; 474 if (to) { 475 outfl(O_ERR, np->file, np->line, 476 "%s has defect on right side of \"->\"", 477 ptree_nodetype2str(t)); 478 return; 479 } 480 if (!from) { 481 outfl(O_DIE, np->file, np->line, 482 "internal error: %s has defect without " 483 "from flag", 484 ptree_nodetype2str(t)); 485 } 486 break; 487 case N_ERROR: 488 lutp = Errors; 489 if (!from && !to) 490 outfl(O_DIE, np->file, np->line, 491 "%s has error without from or to flags", 492 ptree_nodetype2str(t)); 493 break; 494 case N_EREPORT: 495 lutp = Ereports; 496 if (from) { 497 outfl(O_ERR, np->file, np->line, 498 "%s has report on left side of \"->\"", 499 ptree_nodetype2str(t)); 500 return; 501 } 502 if (!to) 503 outfl(O_DIE, np->file, np->line, 504 "internal error: %s has report without " 505 "to flag", 506 ptree_nodetype2str(t)); 507 break; 508 default: 509 outfl(O_DIE, np->file, np->line, 510 "internal error: check_propnames: " 511 "unexpected type: %d", np->u.name.t); 512 } 513 514 if ((dnp = tree_event2np_lut_lookup(lutp, np)) == NULL) { 515 outfl(O_ERR, np->file, np->line, 516 "%s statement contains undeclared event", 517 ptree_nodetype2str(t)); 518 } else 519 dnp->u.stmt.flags |= STMT_REF; 520 np->u.event.declp = dnp; 521 } else if (np->t == T_LIST) { 522 check_propnames(t, np->u.expr.left, from, to); 523 check_propnames(t, np->u.expr.right, from, to); 524 } else if (np->t == T_ARROW) { 525 check_propnames(t, np->u.arrow.lhs, 1, to); 526 check_propnames(t, np->u.arrow.rhs, from, 1); 527 } 528 } 529 530 static struct lut * 531 record_iterators(struct node *np, struct lut *ex) 532 { 533 if (np == NULL) 534 return (ex); 535 536 switch (np->t) { 537 case T_ARROW: 538 ex = record_iterators(np->u.arrow.lhs, ex); 539 ex = record_iterators(np->u.arrow.rhs, ex); 540 break; 541 542 case T_LIST: 543 ex = record_iterators(np->u.expr.left, ex); 544 ex = record_iterators(np->u.expr.right, ex); 545 break; 546 547 case T_EVENT: 548 ex = record_iterators(np->u.event.epname, ex); 549 break; 550 551 case T_NAME: 552 if (np->u.name.child && np->u.name.child->t == T_NAME) 553 ex = lut_add(ex, (void *) np->u.name.child->u.name.s, 554 (void *) np, NULL); 555 ex = record_iterators(np->u.name.next, ex); 556 break; 557 558 default: 559 outfl(O_DIE, np->file, np->line, 560 "record_iterators: internal error: unexpected type: %s", 561 ptree_nodetype2str(np->t)); 562 } 563 564 return (ex); 565 } 566 567 void 568 check_exprscope(struct node *np, struct lut *ex) 569 { 570 if (np == NULL) 571 return; 572 573 switch (np->t) { 574 case T_EVENT: 575 check_exprscope(np->u.event.eexprlist, ex); 576 break; 577 578 case T_ARROW: 579 check_exprscope(np->u.arrow.lhs, ex); 580 check_exprscope(np->u.arrow.rhs, ex); 581 break; 582 583 case T_NAME: 584 if (np->u.name.child && np->u.name.child->t == T_NAME) { 585 if (lut_lookup(ex, 586 (void *) np->u.name.child->u.name.s, 587 NULL) == NULL) 588 outfl(O_ERR, np->file, np->line, 589 "constraint contains undefined" 590 " iterator: %s", 591 np->u.name.child->u.name.s); 592 } 593 check_exprscope(np->u.name.next, ex); 594 break; 595 596 case T_QUOTE: 597 case T_GLOBID: 598 break; 599 600 case T_ASSIGN: 601 case T_NE: 602 case T_EQ: 603 case T_LIST: 604 case T_AND: 605 case T_OR: 606 case T_NOT: 607 case T_ADD: 608 case T_SUB: 609 case T_MUL: 610 case T_DIV: 611 case T_MOD: 612 case T_LT: 613 case T_LE: 614 case T_GT: 615 case T_GE: 616 case T_BITAND: 617 case T_BITOR: 618 case T_BITXOR: 619 case T_BITNOT: 620 case T_LSHIFT: 621 case T_RSHIFT: 622 case T_CONDIF: 623 case T_CONDELSE: 624 check_exprscope(np->u.expr.left, ex); 625 check_exprscope(np->u.expr.right, ex); 626 break; 627 628 case T_FUNC: 629 check_exprscope(np->u.func.arglist, ex); 630 break; 631 632 case T_NUM: 633 case T_TIMEVAL: 634 break; 635 636 default: 637 outfl(O_DIE, np->file, np->line, 638 "check_exprscope: internal error: unexpected type: %s", 639 ptree_nodetype2str(np->t)); 640 } 641 } 642 643 /* 644 * check_propscope -- check constraints for out of scope variable refs 645 */ 646 void 647 check_propscope(struct node *np) 648 { 649 struct lut *ex; 650 651 ex = record_iterators(np, NULL); 652 check_exprscope(np, ex); 653 lut_free(ex, NULL, NULL); 654 } 655 656 /* 657 * check_upset_engine -- validate the engine property in an upset statement 658 * 659 * we do this after the full parse tree has been constructed rather than while 660 * building the parse tree because it is inconvenient for the user if we 661 * require SERD engines to be declared before used in an upset "engine" 662 * property. 663 */ 664 665 /*ARGSUSED*/ 666 void 667 check_upset_engine(struct node *lhs, struct node *rhs, void *arg) 668 { 669 enum nodetype t = (enum nodetype)arg; 670 struct node *engnp; 671 struct node *declp; 672 673 ASSERTeq(rhs->t, t, ptree_nodetype2str); 674 675 if ((engnp = tree_s2np_lut_lookup(rhs->u.stmt.lutp, L_engine)) == NULL) 676 return; 677 678 ASSERT(engnp->t == T_EVENT); 679 680 if ((declp = tree_event2np_lut_lookup(SERDs, engnp)) == NULL) { 681 outfl(O_ERR, engnp->file, engnp->line, 682 "%s %s property contains undeclared name", 683 ptree_nodetype2str(t), L_engine); 684 return; 685 } 686 engnp->u.event.declp = declp; 687 } 688 689 /* 690 * check_refcount -- see if declared names are used 691 * 692 * this is run after the entire parse tree is constructed, so a refcount 693 * of zero means the name has been declared but otherwise not used. 694 */ 695 696 void 697 check_refcount(struct node *lhs, struct node *rhs, void *arg) 698 { 699 enum nodetype t = (enum nodetype)arg; 700 701 ASSERTeq(rhs->t, t, ptree_nodetype2str); 702 703 if (rhs->u.stmt.flags & STMT_REF) 704 return; 705 706 outfl(O_WARN|O_NONL, rhs->file, rhs->line, 707 "%s name declared but not used: ", ptree_nodetype2str(t)); 708 ptree_name(O_WARN|O_NONL, lhs); 709 out(O_WARN, NULL); 710 } 711 712 /* 713 * set check_cycle_warninglevel only for val >= 0 714 */ 715 int 716 check_cycle_level(long long val) 717 { 718 static int check_cycle_warninglevel = -1; 719 720 if (val == 0) 721 check_cycle_warninglevel = 0; 722 else if (val > 0) 723 check_cycle_warninglevel = 1; 724 725 return (check_cycle_warninglevel); 726 } 727 728 /* 729 * check_cycle -- see props from an error have cycles 730 * 731 * this is run after the entire parse tree is constructed, for 732 * each error that has been declared. 733 */ 734 735 /*ARGSUSED*/ 736 void 737 check_cycle(struct node *lhs, struct node *rhs, void *arg) 738 { 739 struct node *np; 740 741 ASSERTeq(rhs->t, T_ERROR, ptree_nodetype2str); 742 743 if (rhs->u.stmt.flags & STMT_CYCLE) 744 return; /* already reported this cycle */ 745 746 if (rhs->u.stmt.flags & STMT_CYMARK) { 747 #ifdef ESC 748 int warninglevel; 749 750 warninglevel = check_cycle_level(-1); 751 if (warninglevel <= 0) { 752 int olevel = O_ERR; 753 754 if (warninglevel == 0) 755 olevel = O_WARN; 756 757 out(olevel|O_NONL, "cycle in propagation tree: "); 758 ptree_name(olevel|O_NONL, rhs->u.stmt.np); 759 out(olevel, NULL); 760 } 761 #endif /* ESC */ 762 763 rhs->u.stmt.flags |= STMT_CYCLE; 764 } 765 766 rhs->u.stmt.flags |= STMT_CYMARK; 767 768 /* for each propagation */ 769 for (np = Props; np; np = np->u.stmt.next) 770 check_cycle_lhs(rhs, np->u.stmt.np); 771 772 rhs->u.stmt.flags &= ~STMT_CYMARK; 773 } 774 775 /* 776 * check_cycle_lhs -- find the lhs of an arrow for cycle checking 777 */ 778 779 static void 780 check_cycle_lhs(struct node *stmtnp, struct node *arrow) 781 { 782 struct node *trylhs; 783 struct node *tryrhs; 784 785 /* handle cascaded arrows */ 786 switch (arrow->u.arrow.lhs->t) { 787 case T_ARROW: 788 /* first recurse left */ 789 check_cycle_lhs(stmtnp, arrow->u.arrow.lhs); 790 791 /* 792 * return if there's a list of events internal to 793 * cascaded props (which is not allowed) 794 */ 795 if (arrow->u.arrow.lhs->u.arrow.rhs->t != T_EVENT) 796 return; 797 798 /* then try this arrow (thing cascaded *to*) */ 799 trylhs = arrow->u.arrow.lhs->u.arrow.rhs; 800 tryrhs = arrow->u.arrow.rhs; 801 break; 802 803 case T_EVENT: 804 case T_LIST: 805 trylhs = arrow->u.arrow.lhs; 806 tryrhs = arrow->u.arrow.rhs; 807 break; 808 809 default: 810 out(O_DIE, "lhs: unexpected type: %s", 811 ptree_nodetype2str(arrow->u.arrow.lhs->t)); 812 /*NOTREACHED*/ 813 } 814 815 check_cycle_lhs_try(stmtnp, trylhs, tryrhs); 816 } 817 818 /* 819 * check_cycle_lhs_try -- try matching an event name on lhs of an arrow 820 */ 821 822 static void 823 check_cycle_lhs_try(struct node *stmtnp, struct node *lhs, struct node *rhs) 824 { 825 if (lhs->t == T_LIST) { 826 check_cycle_lhs_try(stmtnp, lhs->u.expr.left, rhs); 827 check_cycle_lhs_try(stmtnp, lhs->u.expr.right, rhs); 828 return; 829 } 830 831 ASSERT(lhs->t == T_EVENT); 832 833 if (tree_eventcmp(stmtnp->u.stmt.np, lhs) != 0) 834 return; /* no match */ 835 836 check_cycle_rhs(rhs); 837 } 838 839 /* 840 * check_cycle_rhs -- foreach error on rhs, see if we cycle to a marked error 841 */ 842 843 static void 844 check_cycle_rhs(struct node *rhs) 845 { 846 struct node *dnp; 847 848 if (rhs->t == T_LIST) { 849 check_cycle_rhs(rhs->u.expr.left); 850 check_cycle_rhs(rhs->u.expr.right); 851 return; 852 } 853 854 ASSERT(rhs->t == T_EVENT); 855 856 if (rhs->u.event.ename->u.name.t != N_ERROR) 857 return; 858 859 if ((dnp = tree_event2np_lut_lookup(Errors, rhs)) == NULL) { 860 outfl(O_ERR|O_NONL, 861 rhs->file, rhs->line, 862 "unexpected undeclared event during cycle check"); 863 ptree_name(O_ERR|O_NONL, rhs); 864 out(O_ERR, NULL); 865 return; 866 } 867 check_cycle(NULL, dnp, 0); 868 } 869 870 /* 871 * Force iterators to be simple names, expressions, or numbers 872 */ 873 void 874 check_name_iterator(struct node *np) 875 { 876 if (np->u.name.child->t != T_NUM && 877 np->u.name.child->t != T_NAME && 878 np->u.name.child->t != T_CONDIF && 879 np->u.name.child->t != T_SUB && 880 np->u.name.child->t != T_ADD && 881 np->u.name.child->t != T_MUL && 882 np->u.name.child->t != T_DIV && 883 np->u.name.child->t != T_MOD && 884 np->u.name.child->t != T_LSHIFT && 885 np->u.name.child->t != T_RSHIFT) { 886 outfl(O_ERR|O_NONL, np->file, np->line, 887 "invalid iterator: "); 888 ptree_name_iter(O_ERR|O_NONL, np); 889 out(O_ERR, NULL); 890 } 891 } 892 893 /* 894 * Iterators on a declaration may only be implicit 895 */ 896 void 897 check_type_iterator(struct node *np) 898 { 899 while (np != NULL) { 900 if (np->t == T_EVENT) { 901 np = np->u.event.epname; 902 } else if (np->t == T_NAME) { 903 if (np->u.name.child != NULL && 904 np->u.name.child->t != T_NUM) { 905 outfl(O_ERR|O_NONL, np->file, np->line, 906 "explicit iterators disallowed " 907 "in declarations: "); 908 ptree_name_iter(O_ERR|O_NONL, np); 909 out(O_ERR, NULL); 910 } 911 np = np->u.name.next; 912 } else { 913 break; 914 } 915 } 916 } 917 918 void 919 check_func(struct node *np) 920 { 921 ASSERTinfo(np->t == T_FUNC, ptree_nodetype2str(np->t)); 922 923 if (np->u.func.s == L_within) { 924 struct node *arglist = np->u.func.arglist; 925 switch (arglist->t) { 926 case T_NUM: 927 if (arglist->u.ull != 0ULL) 928 outfl(O_ERR, arglist->file, arglist->line, 929 "parameter of within must be 0" 930 ", \"infinity\" or a time value."); 931 break; 932 case T_NAME: 933 if (arglist->u.name.s != L_infinity) 934 outfl(O_ERR, arglist->file, arglist->line, 935 "parameter of within must be 0" 936 ", \"infinity\" or a time value."); 937 break; 938 case T_LIST: 939 /* 940 * if two parameters, the left or min must be 941 * either T_NUM or T_TIMEVAL 942 */ 943 if (arglist->u.expr.left->t != T_NUM && 944 arglist->u.expr.left->t != T_TIMEVAL) 945 outfl(O_ERR, 946 arglist->file, arglist->line, 947 "first parameter of within must be" 948 " either a time value or zero."); 949 950 /* 951 * if two parameters, the right or max must 952 * be either T_NUM, T_NAME or T_TIMEVAL 953 */ 954 if (arglist->u.expr.right->t != T_NUM && 955 arglist->u.expr.right->t != T_TIMEVAL && 956 arglist->u.expr.right->t != T_NAME) 957 outfl(O_ERR, 958 arglist->file, arglist->line, 959 "second parameter of within must " 960 "be 0, \"infinity\" " 961 "or time value."); 962 963 /* 964 * if right or left is a T_NUM it must 965 * be zero 966 */ 967 if (arglist->u.expr.left->t == T_NUM) 968 if (arglist->u.expr.left->u.ull != 0ULL) 969 outfl(O_ERR, 970 arglist->file, arglist->line, 971 "within parameter must be 0 or" 972 " a time value."); 973 if (arglist->u.expr.right->t == T_NUM) 974 if (arglist->u.expr.right->u.ull 975 != 0ULL) 976 outfl(O_ERR, 977 arglist->file, arglist->line, 978 "within parameter must be 0 or" 979 " a time value."); 980 981 /* if right is a T_NAME it must be "infinity" */ 982 if (arglist->u.expr.right->t == T_NAME) 983 if (arglist->u.expr.right->u.name.s 984 != L_infinity) 985 outfl(O_ERR, 986 arglist->file, arglist->line, 987 "\"infinity\" is the only valid" 988 " name for within parameter."); 989 990 /* 991 * the first parameter [min] must not be greater 992 * than the second parameter [max]. 993 */ 994 if (arglist->u.expr.left->u.ull > 995 arglist->u.expr.right->u.ull) 996 outfl(O_ERR, 997 arglist->file, arglist->line, 998 "the first value (min) of" 999 " within must be less than" 1000 " the second (max) value"); 1001 break; 1002 case T_TIMEVAL: 1003 break; /* no restrictions on T_TIMEVAL */ 1004 default: 1005 outfl(O_ERR, arglist->file, arglist->line, 1006 "parameter of within must be 0" 1007 ", \"infinity\" or a time value."); 1008 } 1009 } else if (np->u.func.s == L_call) { 1010 if (np->u.func.arglist->t != T_QUOTE && 1011 np->u.func.arglist->t != T_LIST && 1012 np->u.func.arglist->t != T_GLOBID && 1013 np->u.func.arglist->t != T_CONDIF && 1014 np->u.func.arglist->t != T_LIST && 1015 np->u.func.arglist->t != T_FUNC) 1016 outfl(O_ERR, np->u.func.arglist->file, 1017 np->u.func.arglist->line, 1018 "invalid first argument to call()"); 1019 } else if (np->u.func.s == L_fru) { 1020 if (np->u.func.arglist->t != T_NAME) 1021 outfl(O_ERR, np->u.func.arglist->file, 1022 np->u.func.arglist->line, 1023 "argument to fru() must be a path"); 1024 } else if (np->u.func.s == L_asru) { 1025 if (np->u.func.arglist->t != T_NAME) 1026 outfl(O_ERR, np->u.func.arglist->file, 1027 np->u.func.arglist->line, 1028 "argument to asru() must be a path"); 1029 } else if (np->u.func.s == L_is_connected || 1030 np->u.func.s == L_is_under) { 1031 if (np->u.func.arglist->t == T_LIST && 1032 (np->u.func.arglist->u.expr.left->t == T_NAME || 1033 (np->u.func.arglist->u.expr.left->t == T_FUNC && 1034 (np->u.func.arglist->u.expr.left->u.func.s == L_fru || 1035 np->u.func.arglist->u.expr.left->u.func.s == L_asru))) && 1036 (np->u.func.arglist->u.expr.right->t == T_NAME || 1037 (np->u.func.arglist->u.expr.right->t == T_FUNC && 1038 (np->u.func.arglist->u.expr.right->u.func.s == L_fru || 1039 np->u.func.arglist->u.expr.right->u.func.s == L_asru)))) { 1040 if (np->u.func.arglist->u.expr.left->t == T_FUNC) 1041 check_func(np->u.func.arglist->u.expr.left); 1042 if (np->u.func.arglist->u.expr.right->t == T_FUNC) 1043 check_func(np->u.func.arglist->u.expr.right); 1044 } else { 1045 outfl(O_ERR, np->u.func.arglist->file, 1046 np->u.func.arglist->line, 1047 "%s() must have paths or calls to " 1048 "fru() and/or asru() as arguments", 1049 np->u.func.s); 1050 } 1051 } else if (np->u.func.s == L_is_on) { 1052 if (np->u.func.arglist->t == T_FUNC && 1053 (np->u.func.arglist->u.func.s == L_fru || 1054 np->u.func.arglist->u.func.s == L_asru)) { 1055 check_func(np->u.func.arglist); 1056 } else { 1057 outfl(O_ERR, np->u.func.arglist->file, 1058 np->u.func.arglist->line, 1059 "argument to is_on() must be a call to " 1060 "fru() or asru()"); 1061 } 1062 } else if (np->u.func.s == L_is_present) { 1063 if (np->u.func.arglist->t == T_FUNC && 1064 (np->u.func.arglist->u.func.s == L_fru || 1065 np->u.func.arglist->u.func.s == L_asru)) { 1066 check_func(np->u.func.arglist); 1067 } else { 1068 outfl(O_ERR, np->u.func.arglist->file, 1069 np->u.func.arglist->line, 1070 "argument to is_present() must be a call to " 1071 "fru() or asru()"); 1072 } 1073 } else if (np->u.func.s == L_is_type) { 1074 if (np->u.func.arglist->t == T_FUNC && 1075 (np->u.func.arglist->u.func.s == L_fru || 1076 np->u.func.arglist->u.func.s == L_asru)) { 1077 check_func(np->u.func.arglist); 1078 } else { 1079 outfl(O_ERR, np->u.func.arglist->file, 1080 np->u.func.arglist->line, 1081 "argument to is_type() must be a call to " 1082 "fru() or asru()"); 1083 } 1084 } else if (np->u.func.s == L_confprop) { 1085 if (np->u.func.arglist->t == T_LIST && 1086 (np->u.func.arglist->u.expr.left->t == T_FUNC && 1087 (np->u.func.arglist->u.expr.left->u.func.s == L_fru || 1088 np->u.func.arglist->u.expr.left->u.func.s == L_asru)) && 1089 np->u.func.arglist->u.expr.right->t == T_QUOTE) { 1090 check_func(np->u.func.arglist->u.expr.left); 1091 } else { 1092 outfl(O_ERR, np->u.func.arglist->file, 1093 np->u.func.arglist->line, 1094 "confprop(): first argument must be a call to " 1095 "fru() or asru(); " 1096 "second argument must be a string"); 1097 } 1098 } else if (np->u.func.s == L_payloadprop) { 1099 if (np->u.func.arglist->t != T_QUOTE) 1100 outfl(O_ERR, np->u.func.arglist->file, 1101 np->u.func.arglist->line, 1102 "argument to payloadprop() must be a string"); 1103 } else if (np->u.func.s == L_envprop) { 1104 if (np->u.func.arglist->t != T_QUOTE) 1105 outfl(O_ERR, np->u.func.arglist->file, 1106 np->u.func.arglist->line, 1107 "argument to envprop() must be a string"); 1108 } else 1109 outfl(O_WARN, np->file, np->line, 1110 "possible platform-specific function: %s", 1111 np->u.func.s); 1112 } 1113 1114 void 1115 check_expr(struct node *np) 1116 { 1117 ASSERT(np != NULL); 1118 1119 switch (np->t) { 1120 case T_ASSIGN: 1121 ASSERT(np->u.expr.left != NULL); 1122 if (np->u.expr.left->t != T_GLOBID) 1123 outfl(O_ERR, np->file, np->line, 1124 "assignment only allowed to globals (e.g. $a)"); 1125 break; 1126 } 1127 } 1128 1129 void 1130 check_event(struct node *np) 1131 { 1132 ASSERT(np != NULL); 1133 ASSERT(np->t == T_EVENT); 1134 1135 if (np->u.event.epname == NULL) { 1136 outfl(O_ERR|O_NONL, np->file, np->line, 1137 "pathless events not allowed: "); 1138 ptree_name(O_ERR|O_NONL, np->u.event.ename); 1139 out(O_ERR, NULL); 1140 } 1141 } 1142 1143 /* 1144 * check for properties that are required on declarations. This 1145 * should be done after all declarations since they can be 1146 * redeclared with a different set of properties. 1147 */ 1148 /*ARGSUSED*/ 1149 void 1150 check_required_props(struct node *lhs, struct node *rhs, void *arg) 1151 { 1152 enum nodetype t = (enum nodetype)arg; 1153 1154 ASSERTeq(rhs->t, t, ptree_nodetype2str); 1155 1156 check_stmt_required_properties(rhs); 1157 } 1158 1159 /* 1160 * check that cascading prop statements do not contain lists internally. 1161 * the first and last event lists in the cascading prop may be single 1162 * events or lists of events. 1163 */ 1164 /*ARGSUSED*/ 1165 void 1166 check_proplists(enum nodetype t, struct node *np) 1167 { 1168 ASSERT(np->t == T_ARROW); 1169 /* 1170 * not checking the right hand side of the top level prop 1171 * since it is the last part of the propagation and can be 1172 * an event or list of events 1173 */ 1174 check_proplists_lhs(t, np->u.arrow.lhs); 1175 } 1176 1177 /*ARGSUSED*/ 1178 static void 1179 check_proplists_lhs(enum nodetype t, struct node *lhs) 1180 { 1181 if (lhs->t == T_ARROW) { 1182 if (lhs->u.arrow.rhs->t == T_LIST) { 1183 outfl(O_ERR, lhs->file, lhs->line, 1184 "lists are not allowed internally on" 1185 " cascading %s", 1186 (t == T_PROP) ? "propagations" : "masks"); 1187 } 1188 check_proplists_lhs(t, lhs->u.arrow.lhs); 1189 } 1190 } 1191