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