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