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 (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 /* 22 * Copyright 2008 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 * 25 * esclex.c -- lexer for esc 26 * 27 * this module provides lexical analysis and error handling routine 28 * expected by the yacc-generated parser (i.e. yylex() and yyerror()). 29 * it also does lots of tracking of things like filenames, line numbers, 30 * and what tokens are seen on a line up to the point where a syntax error 31 * was found. this module also arranges for the input source files to 32 * be run through cpp. 33 */ 34 35 #pragma ident "%Z%%M% %I% %E% SMI" 36 37 #include <stdio.h> 38 #include <ctype.h> 39 #include <string.h> 40 #include <stdlib.h> 41 #include <unistd.h> 42 #include <time.h> 43 #include <errno.h> 44 #include "out.h" 45 #include "alloc.h" 46 #include "stats.h" 47 #include "stable.h" 48 #include "lut.h" 49 #include "literals.h" 50 #include "tree.h" 51 #include "esclex.h" 52 #include "eftread.h" 53 #include "check.h" 54 #include "y.tab.h" 55 56 /* ridiculously long token buffer -- disallow any token longer than this */ 57 #define MAXTOK 8192 58 static char Tok[MAXTOK]; 59 60 /* some misc stats we keep on the lexer & parser */ 61 static struct stats *Tokcount; 62 static struct stats *Lexelapse; 63 struct stats *Filecount; 64 struct filestats { 65 struct filestats *next; 66 struct stats *stats; 67 struct stats *idstats; 68 } *Fstats; 69 70 static int Errcount; 71 72 /* input file state */ 73 static char **Files; 74 static const char *Fileopened; 75 static FILE *Fp; 76 static int Line; 77 static const char *File; 78 static const char *Cpp = "/usr/bin/cpp"; 79 #ifdef ESC 80 static const char *Cppargs; 81 static const char *Cppstdargs = "-undef -Y."; 82 #endif /* ESC */ 83 84 /* for debugging */ 85 static int Lexecho; /* echo tokens as we read them */ 86 87 /* forward declarations of our internal routines */ 88 static int record(int tok, const char *s); 89 static void dumpline(int flags); 90 static void doident(); 91 static void dopragma(const char *tok); 92 93 /* 94 * table of reserved words. this table is only used by lex_init() 95 * to intialize the Rwords lookup table. 96 */ 97 static const struct { 98 const char *word; 99 const int val; 100 } Rwords[] = { 101 { "asru", ASRU }, 102 { "count", COUNT }, 103 { "div", DIV }, 104 { "engine", ENGINE }, 105 { "event", EVENT }, 106 { "fru", FRU }, 107 { "if", IF }, 108 { "mask", MASK }, 109 { "prop", PROP }, 110 { "config", CONFIG }, 111 /* 112 * PATHFUNC indicates functions that operate only on paths 113 * and quotes 114 */ 115 { "is_connected", PATHFUNC }, 116 { "is_under", PATHFUNC }, 117 { "is_on", PATHFUNC }, 118 { "is_present", PATHFUNC }, 119 { "is_type", PATHFUNC }, 120 { "confprop", PATHFUNC }, 121 { "confprop_defined", PATHFUNC }, 122 }; 123 124 /* 125 * Rwordslut is a lookup table of reserved words. lhs is the word 126 * (in the string table) and the rhs is the token value returned 127 * by the yylex() for that word. 128 */ 129 static struct lut *Rwordslut; 130 131 static const struct { 132 const char *suffix; 133 const unsigned long long nsec; 134 } Timesuffix[] = { 135 { "nanosecond", 1ULL }, 136 { "nanoseconds", 1ULL }, 137 { "nsec", 1ULL }, 138 { "nsecs", 1ULL }, 139 { "ns", 1ULL }, 140 { "microsecond", 1000ULL }, 141 { "microseconds", 1000ULL }, 142 { "usec", 1000ULL }, 143 { "usecs", 1000ULL }, 144 { "us", 1000ULL }, 145 { "millisecond", 1000000ULL }, 146 { "milliseconds", 1000000ULL }, 147 { "msec", 1000000ULL }, 148 { "msecs", 1000000ULL }, 149 { "ms", 1000000ULL }, 150 { "second", 1000000000ULL }, 151 { "seconds", 1000000000ULL }, 152 { "s", 1000000000ULL }, 153 { "minute", 1000000000ULL * 60 }, 154 { "minutes", 1000000000ULL * 60 }, 155 { "min", 1000000000ULL * 60 }, 156 { "mins", 1000000000ULL * 60 }, 157 { "m", 1000000000ULL * 60 }, 158 { "hour", 1000000000ULL * 60 * 60 }, 159 { "hours", 1000000000ULL * 60 * 60 }, 160 { "hr", 1000000000ULL * 60 * 60 }, 161 { "hrs", 1000000000ULL * 60 * 60 }, 162 { "h", 1000000000ULL * 60 * 60 }, 163 { "day", 1000000000ULL * 60 * 60 * 24 }, 164 { "days", 1000000000ULL * 60 * 60 * 24 }, 165 { "d", 1000000000ULL * 60 * 60 * 24 }, 166 { "week", 1000000000ULL * 60 * 60 * 24 * 7 }, 167 { "weeks", 1000000000ULL * 60 * 60 * 24 * 7 }, 168 { "wk", 1000000000ULL * 60 * 60 * 24 * 7 }, 169 { "wks", 1000000000ULL * 60 * 60 * 24 * 7 }, 170 { "month", 1000000000ULL * 60 * 60 * 24 * 30 }, 171 { "months", 1000000000ULL * 60 * 60 * 24 * 30 }, 172 { "year", 1000000000ULL * 60 * 60 * 24 * 365 }, 173 { "years", 1000000000ULL * 60 * 60 * 24 * 365 }, 174 { "yr", 1000000000ULL * 60 * 60 * 24 * 365 }, 175 { "yrs", 1000000000ULL * 60 * 60 * 24 * 365 }, 176 }; 177 178 /* 179 * some wrappers around the general lut functions to provide type checking... 180 */ 181 182 static struct lut * 183 lex_s2i_lut_add(struct lut *root, const char *s, intptr_t i) 184 { 185 return (lut_add(root, (void *)s, (void *)i, NULL)); 186 } 187 188 static int 189 lex_s2i_lut_lookup(struct lut *root, const char *s) 190 { 191 return ((intptr_t)lut_lookup(root, (void *)s, NULL)); 192 } 193 194 static struct lut * 195 lex_s2ullp_lut_add(struct lut *root, const char *s, 196 const unsigned long long *ullp) 197 { 198 return (lut_add(root, (void *)s, (void *)ullp, NULL)); 199 } 200 201 const unsigned long long * 202 lex_s2ullp_lut_lookup(struct lut *root, const char *s) 203 { 204 return ((unsigned long long *)lut_lookup(root, (void *)s, NULL)); 205 } 206 207 /* 208 * lex_init -- initialize the lexer with appropriate filenames & debug flags 209 */ 210 211 /*ARGSUSED*/ 212 void 213 lex_init(char **av, const char *cppargs, int lexecho) 214 { 215 int i; 216 #ifdef ESC 217 const char *ptr; 218 #endif /* ESC */ 219 220 Lexecho = lexecho; 221 Tokcount = stats_new_counter("lex.tokens", "total tokens in", 1); 222 Filecount = stats_new_counter("lex.files", "total files read", 0); 223 Lexelapse = stats_new_elapse("lex.time", "elapsed lex/parse time", 1); 224 225 #ifdef ESC 226 Cppargs = cppargs; 227 228 /* allow user to tell us where cpp is if it is some weird place */ 229 if (ptr = getenv("_ESC_CPP")) 230 Cpp = ptr; 231 232 /* and in case it takes some special stdargs */ 233 if (ptr = getenv("_ESC_CPP_STDARGS")) 234 Cppstdargs = ptr; 235 236 /* verify we can find cpp */ 237 if (access(Cpp, X_OK) < 0) { 238 Cpp = "/usr/lib/cpp"; 239 if (access(Cpp, X_OK) < 0) 240 out(O_DIE, "can't locate cpp"); 241 } 242 #endif /* ESC */ 243 244 Files = av; 245 246 /* verify we can find all the input files */ 247 while (*av) { 248 if (strlen(*av) >= MAXTOK - strlen(Cpp) - 3) 249 out(O_DIE, "filename too long: %.100s...", *av); 250 if (access(*av, R_OK) < 0) 251 out(O_DIE|O_SYS, "%s", *av); 252 av++; 253 stats_counter_bump(Filecount); 254 } 255 256 /* put reserved words into the string table & a lookup table */ 257 for (i = 0; i < sizeof (Rwords) / sizeof (*Rwords); i++) 258 Rwordslut = lex_s2i_lut_add(Rwordslut, 259 stable(Rwords[i].word), Rwords[i].val); 260 261 /* initialize table of timeval suffixes */ 262 for (i = 0; i < sizeof (Timesuffix) / sizeof (*Timesuffix); i++) { 263 Timesuffixlut = lex_s2ullp_lut_add(Timesuffixlut, 264 stable(Timesuffix[i].suffix), &Timesuffix[i].nsec); 265 } 266 267 /* record start time */ 268 stats_elapse_start(Lexelapse); 269 } 270 271 void 272 closefile(void) 273 { 274 if (Fp != NULL) { 275 #ifdef ESC 276 if (pclose(Fp) > 0) 277 out(O_DIE, "cpp errors while reading \"%s\", " 278 "bailing out.", Fileopened); 279 #else 280 (void) fclose(Fp); 281 #endif /* ESC */ 282 } 283 Fp = NULL; 284 } 285 286 /* 287 * yylex -- the lexer, called yylex() because that's what yacc wants 288 */ 289 290 int 291 yylex() 292 { 293 int c; 294 int nextc; 295 char *ptr = Tok; 296 char *eptr = &Tok[MAXTOK]; 297 const char *cptr; 298 int startline; 299 int val; 300 static int bol = 1; /* true if we're at beginning of line */ 301 302 for (;;) { 303 while (Fp == NULL) { 304 char ibuf[80]; 305 306 if (*Files == NULL) 307 return (record(EOF, NULL)); 308 Fileopened = stable(*Files++); 309 #ifdef ESC 310 sprintf(Tok, "%s %s %s %s", 311 Cpp, Cppstdargs, Cppargs, Fileopened); 312 if ((Fp = popen(Tok, "r")) == NULL) 313 out(O_DIE|O_SYS, "%s", Tok); 314 #else 315 Fp = eftread_fopen(Fileopened, ibuf, sizeof (ibuf)); 316 #endif /* ESC */ 317 Line = 1; 318 bol = 1; 319 320 /* add name to stats for visibility */ 321 if (Fp != NULL) { 322 static int fnum; 323 char nbuf[100]; 324 struct filestats *nfs = MALLOC(sizeof (*nfs)); 325 326 (void) sprintf(nbuf, "lex.file%d", fnum); 327 nfs->stats = stats_new_string(nbuf, "", 0); 328 stats_string_set(nfs->stats, Fileopened); 329 330 if (ibuf[0] != '\0') { 331 (void) sprintf(nbuf, "lex.file%d-ident", 332 fnum); 333 nfs->idstats = 334 stats_new_string(nbuf, "", 0); 335 stats_string_set(nfs->idstats, ibuf); 336 } else { 337 nfs->idstats = NULL; 338 } 339 340 nfs->next = Fstats; 341 Fstats = nfs; 342 fnum++; 343 } 344 } 345 346 switch (c = getc(Fp)) { 347 case '#': 348 /* enforce that we're at beginning of line */ 349 if (!bol) 350 return (record(c, NULL)); 351 352 while ((c = getc(Fp)) != EOF && 353 (c == ' ' || c == '\t')) 354 ; 355 if (!isdigit(c)) { 356 /* 357 * three cases here: 358 * #pragma 359 * #ident 360 * #something-we-don't-understand 361 * anything we don't expect we just ignore. 362 */ 363 *ptr++ = c; 364 while ((c = getc(Fp)) != EOF && isalnum(c)) 365 if (ptr < eptr - 1) 366 *ptr++ = c; 367 *ptr++ = '\0'; 368 if (strcmp(Tok, "pragma") == 0) { 369 /* skip white space */ 370 while ((c = getc(Fp)) != EOF && 371 (c == ' ' || c == '\t')) 372 ; 373 374 if (c == EOF || c == '\n') 375 outfl(O_DIE, File, Line, 376 "bad #pragma"); 377 378 /* pull in next token */ 379 ptr = Tok; 380 *ptr++ = c; 381 while ((c = getc(Fp)) != EOF && 382 !isspace(c)) 383 if (ptr < eptr - 1) 384 *ptr++ = c; 385 *ptr++ = '\0'; 386 (void) ungetc(c, Fp); 387 388 dopragma(Tok); 389 } else if (strcmp(Tok, "ident") == 0) 390 doident(); 391 } else { 392 /* handle file & line info from cpp */ 393 Line = 0; 394 do { 395 if (!isdigit(c)) 396 break; 397 Line = Line * 10 + c - '0'; 398 } while ((c = getc(Fp)) != EOF); 399 Line--; /* newline will increment it */ 400 while (c != EOF && isspace(c)) 401 c = getc(Fp); 402 if (c != '"') 403 outfl(O_DIE, File, Line, 404 "bad # statement (file name)"); 405 while ((c = getc(Fp)) != EOF && c != '"') 406 if (ptr < eptr - 1) 407 *ptr++ = c; 408 *ptr++ = '\0'; 409 if (c != '"') 410 outfl(O_DIE, File, Line, 411 "bad # statement (quotes)"); 412 File = stable(Tok); 413 } 414 /* skip the rest of the cpp line */ 415 while ((c = getc(Fp)) != EOF && c != '\n' && c != '\r') 416 ; 417 if (c == EOF) 418 return (record(c, NULL)); 419 else 420 (void) ungetc(c, Fp); 421 ptr = Tok; 422 break; 423 424 case EOF: 425 closefile(); 426 continue; 427 428 case '\n': 429 Line++; 430 bol = 1; 431 break; 432 433 case '\r': 434 case ' ': 435 case '\t': 436 bol = 0; 437 break; 438 439 case '/': 440 bol = 0; 441 /* comment handling */ 442 if ((nextc = getc(Fp)) == EOF) 443 outfl(O_DIE, File, Line, "unexpected EOF"); 444 else if (nextc == '*') { 445 startline = Line; 446 while ((c = getc(Fp)) != EOF) { 447 if (c == '\n') 448 Line++; 449 else if (c == '*' && 450 (((c = getc(Fp)) == EOF) || 451 (c == '/'))) 452 break; 453 } 454 if (c == EOF) { 455 outfl(O_DIE, File, Line, 456 "end of comment not seen " 457 "(started on line %d)", 458 startline); 459 } 460 } else { 461 /* wasn't a comment, return the '/' token */ 462 (void) ungetc(nextc, Fp); 463 return (record(c, NULL)); 464 } 465 break; 466 467 case '"': { 468 int prevc; 469 470 bol = 0; 471 prevc = '\0'; 472 /* quoted string handling */ 473 startline = Line; 474 for (;;) { 475 c = getc(Fp); 476 if (c == EOF) 477 outfl(O_DIE, File, Line, 478 "end of string not seen " 479 "(started on line %d)", 480 startline); 481 else if (c == '\n') 482 Line++; 483 else if (c == '"' && prevc != '\\') 484 break; 485 else if (ptr < eptr) 486 *ptr++ = c; 487 prevc = c; 488 } 489 if (ptr >= eptr) 490 out(O_DIE, File, Line, "string too long"); 491 *ptr++ = '\0'; 492 return (record(QUOTE, stable(Tok))); 493 } 494 case '&': 495 bol = 0; 496 /* && */ 497 if ((nextc = getc(Fp)) == '&') 498 return (record(AND, NULL)); 499 else { 500 (void) ungetc(nextc, Fp); 501 return (record(c, NULL)); 502 } 503 /*NOTREACHED*/ 504 break; 505 506 case '|': 507 bol = 0; 508 /* || */ 509 if ((nextc = getc(Fp)) == '|') 510 return (record(OR, NULL)); 511 else { 512 (void) ungetc(nextc, Fp); 513 return (record(c, NULL)); 514 } 515 /*NOTREACHED*/ 516 break; 517 518 case '!': 519 bol = 0; 520 /* ! or != */ 521 if ((nextc = getc(Fp)) == '=') 522 return (record(NE, NULL)); 523 else { 524 (void) ungetc(nextc, Fp); 525 return (record(c, NULL)); 526 } 527 /*NOTREACHED*/ 528 break; 529 530 case '=': 531 bol = 0; 532 /* == */ 533 if ((nextc = getc(Fp)) == '=') 534 return (record(EQ, NULL)); 535 else { 536 (void) ungetc(nextc, Fp); 537 return (record(c, NULL)); 538 } 539 /*NOTREACHED*/ 540 break; 541 542 case '-': 543 bol = 0; 544 /* -> */ 545 if ((nextc = getc(Fp)) == '>') 546 return (record(ARROW, stable(Tok))); 547 else { 548 (void) ungetc(nextc, Fp); 549 return (record(c, NULL)); 550 } 551 /*NOTREACHED*/ 552 break; 553 554 case '<': 555 bol = 0; 556 if ((nextc = getc(Fp)) == '=') 557 /* <= */ 558 return (record(LE, NULL)); 559 else if (nextc == '<') 560 /* << */ 561 return (record(LSHIFT, NULL)); 562 else { 563 (void) ungetc(nextc, Fp); 564 return (record(c, NULL)); 565 } 566 /*NOTREACHED*/ 567 break; 568 569 case '>': 570 bol = 0; 571 if ((nextc = getc(Fp)) == '=') 572 /* >= */ 573 return (record(GE, NULL)); 574 else if (nextc == '>') 575 /* >> */ 576 return (record(RSHIFT, NULL)); 577 else { 578 (void) ungetc(nextc, Fp); 579 return (record(c, NULL)); 580 } 581 /*NOTREACHED*/ 582 break; 583 584 default: 585 bol = 0; 586 if (isdigit(c)) { 587 int base; 588 589 /* collect rest of number */ 590 if (c == '0') { 591 *ptr++ = c; 592 if ((c = getc(Fp)) == EOF) { 593 *ptr++ = '\0'; 594 return (record(NUMBER, 595 stable(Tok))); 596 } else if (c == 'x' || c == 'X') { 597 *ptr++ = c; 598 base = 16; 599 } else { 600 (void) ungetc(c, Fp); 601 base = 8; 602 } 603 } else { 604 *ptr++ = c; 605 base = 10; 606 } 607 while ((c = getc(Fp)) != EOF) { 608 if (ptr >= eptr) 609 out(O_DIE, File, Line, 610 "number too long"); 611 612 switch (base) { 613 case 16: 614 if (c >= 'a' && c <= 'f' || 615 c >= 'A' && c <= 'F') { 616 *ptr++ = c; 617 continue; 618 } 619 /*FALLTHRU*/ 620 case 10: 621 if (c >= '8' && c <= '9') { 622 *ptr++ = c; 623 continue; 624 } 625 /*FALLTHRU*/ 626 case 8: 627 if (c >= '0' && c <= '7') { 628 *ptr++ = c; 629 continue; 630 } 631 /* not valid for this base */ 632 *ptr++ = '\0'; 633 (void) ungetc(c, Fp); 634 return (record(NUMBER, 635 stable(Tok))); 636 } 637 } 638 *ptr++ = '\0'; 639 return (record(NUMBER, stable(Tok))); 640 } else if (isalpha(c)) { 641 /* collect identifier */ 642 *ptr++ = c; 643 for (;;) { 644 c = getc(Fp); 645 if ((isalnum(c) || c == '_') && 646 ptr < eptr) 647 *ptr++ = c; 648 else { 649 (void) ungetc(c, Fp); 650 break; 651 } 652 } 653 if (ptr >= eptr) 654 out(O_DIE, File, Line, 655 "identifier too long"); 656 *ptr++ = '\0'; 657 cptr = stable(Tok); 658 if (val = lex_s2i_lut_lookup(Rwordslut, cptr)) { 659 return (record(val, cptr)); 660 } 661 return (record(ID, cptr)); 662 } else 663 return (record(c, NULL)); 664 } 665 /*NOTREACHED*/ 666 } 667 } 668 669 /* 670 * the record()/dumpline() routines are used to track & report 671 * the list of tokens seen on a given line. this is used in two ways. 672 * first, syntax errors found by the parser are reported by us (via 673 * yyerror()) and we tack on the tokens processed so far on the current 674 * line to help indicate exactly where the error is. second, if "lexecho" 675 * debugging is turned on, these routines provide it. 676 */ 677 #define MAXRECORD 1000 678 static int Recordedline; 679 static struct { 680 int tok; 681 const char *s; 682 } Recorded[MAXRECORD]; 683 static int Recordnext; 684 685 static int 686 record(int tok, const char *s) 687 { 688 stats_counter_bump(Tokcount); 689 if (Line != Recordedline) { 690 /* starting new line, dump out the previous line */ 691 if (Lexecho && Recordedline) { 692 outfl(O_NONL, File, Recordedline, "lex: "); 693 dumpline(O_OK); 694 } 695 Recordedline = Line; 696 Recordnext = 0; 697 } 698 if (Recordnext >= MAXRECORD) 699 outfl(O_DIE, File, Line, "line too long, bailing out"); 700 Recorded[Recordnext].tok = tok; 701 Recorded[Recordnext++].s = s; 702 703 yylval.tok.s = s; 704 yylval.tok.file = File; 705 yylval.tok.line = Line; 706 return (tok); 707 } 708 709 /*ARGSUSED*/ 710 static void 711 dumpline(int flags) 712 { 713 int i; 714 715 for (i = 0; i < Recordnext; i++) 716 if (Recorded[i].s && Recorded[i].tok != ARROW) 717 switch (Recorded[i].tok) { 718 case T_QUOTE: 719 out(flags|O_NONL, " \"%s\"", 720 Recorded[i].s); 721 break; 722 723 default: 724 out(flags|O_NONL, " %s", 725 Recorded[i].s); 726 break; 727 } 728 else 729 switch (Recorded[i].tok) { 730 case EOF: 731 out(flags|O_NONL, " EOF"); 732 break; 733 case ARROW: 734 out(flags|O_NONL, " ->%s", 735 Recorded[i].s); 736 break; 737 case EQ: 738 out(flags|O_NONL, " =="); 739 break; 740 case NE: 741 out(flags|O_NONL, " !="); 742 break; 743 case OR: 744 out(flags|O_NONL, " ||"); 745 break; 746 case AND: 747 out(flags|O_NONL, " &&"); 748 break; 749 case LE: 750 out(flags|O_NONL, " <="); 751 break; 752 case GE: 753 out(flags|O_NONL, " >="); 754 break; 755 case LSHIFT: 756 out(flags|O_NONL, " <<"); 757 break; 758 case RSHIFT: 759 out(flags|O_NONL, " >>"); 760 break; 761 default: 762 if (isprint(Recorded[i].tok)) 763 out(flags|O_NONL, " %c", 764 Recorded[i].tok); 765 else 766 out(flags|O_NONL, " '\\%03o'", 767 Recorded[i].tok); 768 break; 769 } 770 out(flags, NULL); 771 } 772 773 /* 774 * yyerror -- report a pareser error, called yyerror because yacc wants it 775 */ 776 777 void 778 yyerror(const char *s) 779 { 780 Errcount++; 781 outfl(O_ERR|O_NONL, File, Line, "%s, tokens: ", s); 782 dumpline(O_ERR); 783 } 784 785 /* 786 * doident -- handle "#pragma ident" directives 787 */ 788 static void 789 doident() 790 { 791 int c; 792 char *ptr = Tok; 793 char *eptr = &Tok[MAXTOK]; 794 795 /* skip white space and quotes */ 796 while ((c = getc(Fp)) != EOF && 797 (c == ' ' || c == '\t' || c == '"')) 798 ; 799 800 if (c == EOF || c == '\n') 801 outfl(O_DIE, File, Line, "bad ident"); 802 803 /* pull in next token */ 804 ptr = Tok; 805 *ptr++ = c; 806 while ((c = getc(Fp)) != EOF && c != '"' && c != '\n') 807 if (ptr < eptr - 1) 808 *ptr++ = c; 809 *ptr++ = '\0'; 810 if (c != '\n') { 811 /* skip to end of line (including close quote, if any) */ 812 while ((c = getc(Fp)) != EOF && c != '\n') 813 ; 814 } 815 (void) ungetc(c, Fp); 816 Ident = lut_add(Ident, (void *)stable(Tok), (void *)0, NULL); 817 818 outfl(O_VERB, File, Line, "pragma set: ident \"%s\"", Tok); 819 } 820 821 /* 822 * dodictionary -- handle "#pragma dictionary" directives 823 */ 824 static void 825 dodictionary() 826 { 827 int c; 828 char *ptr = Tok; 829 char *eptr = &Tok[MAXTOK]; 830 831 /* skip white space and quotes */ 832 while ((c = getc(Fp)) != EOF && 833 (c == ' ' || c == '\t' || c == '"')) 834 ; 835 836 if (c == EOF || c == '\n') 837 outfl(O_DIE, File, Line, "bad dictionary"); 838 839 /* pull in next token */ 840 ptr = Tok; 841 *ptr++ = c; 842 while ((c = getc(Fp)) != EOF && c != '"' && c != '\n') 843 if (ptr < eptr - 1) 844 *ptr++ = c; 845 *ptr++ = '\0'; 846 if (c != '\n') { 847 /* skip to end of line (including close quote, if any) */ 848 while ((c = getc(Fp)) != EOF && c != '\n') 849 ; 850 } 851 (void) ungetc(c, Fp); 852 Dicts = lut_add(Dicts, (void *)stable(Tok), (void *)0, NULL); 853 854 outfl(O_VERB, File, Line, "pragma set: dictionary \"%s\"", Tok); 855 } 856 857 /* 858 * doallow_cycles -- handle "#pragma allow_cycles" directives 859 */ 860 static void 861 doallow_cycles() 862 { 863 int c; 864 char *ptr = Tok; 865 char *eptr = &Tok[MAXTOK]; 866 unsigned long long newlevel; 867 868 /* 869 * by default the compiler does not allow cycles or loops 870 * in propagations. when cycles are encountered, the 871 * compiler prints out an error message. 872 * 873 * "#pragma allow_cycles" and 874 * "#pragma allow_cycles 0" 875 * allow cycles, but any such cycle will produce a warning 876 * message. 877 * 878 * "#pragma allow_cycles N" 879 * with N > 0 will allow cycles and not produce any 880 * warning messages. 881 */ 882 883 /* skip white space and quotes */ 884 while ((c = getc(Fp)) != EOF && 885 (c == ' ' || c == '\t' || c == '"')) 886 ; 887 888 if (c == EOF || c == '\n') 889 newlevel = 0ULL; 890 else { 891 892 /* pull in next token */ 893 ptr = Tok; 894 *ptr++ = c; 895 while ((c = getc(Fp)) != EOF && c != '"' && c != '\n') 896 if (ptr < eptr - 1) 897 *ptr++ = c; 898 *ptr++ = '\0'; 899 if (c != '\n') { 900 /* skip to end of line */ 901 while ((c = getc(Fp)) != EOF && c != '\n') 902 ; 903 } 904 newlevel = strtoll(Tok, NULL, 0); 905 } 906 (void) ungetc(c, Fp); 907 908 (void) check_cycle_level(newlevel); 909 outfl(O_VERB, File, Line, 910 "pragma set: allow_cycles (%s)", 911 newlevel ? "no warnings" : "with warnings"); 912 } 913 914 /* 915 * dopragma -- handle #pragma directives 916 */ 917 static void 918 dopragma(const char *tok) 919 { 920 if (strcmp(tok, "ident") == 0) 921 doident(); 922 else if (strcmp(tok, "dictionary") == 0) 923 dodictionary(); 924 else if (strcmp(tok, "new_errors_only") == 0) { 925 if (Pragma_new_errors_only++ == 0) 926 outfl(O_VERB, File, Line, 927 "pragma set: new_errors_only"); 928 } else if (strcmp(tok, "trust_ereports") == 0) { 929 if (Pragma_trust_ereports++ == 0) 930 outfl(O_VERB, File, Line, 931 "pragma set: trust_ereports"); 932 } else if (strcmp(tok, "allow_cycles") == 0) 933 doallow_cycles(); 934 else 935 outfl(O_VERB, File, Line, 936 "unknown pragma ignored: \"%s\"", tok); 937 } 938 939 /* 940 * lex_fini -- finalize the lexer 941 */ 942 943 int 944 lex_fini(void) 945 { 946 stats_elapse_stop(Lexelapse); 947 closefile(); 948 if (Lexecho) { 949 outfl(O_OK, File, Line, "lex: "); 950 dumpline(O_OK); 951 } 952 return (Errcount); 953 } 954 955 void 956 lex_free(void) 957 { 958 struct filestats *nfstats = Fstats; 959 960 /* 961 * Free up memory consumed by the lexer 962 */ 963 stats_delete(Tokcount); 964 stats_delete(Filecount); 965 stats_delete(Lexelapse); 966 while (nfstats != NULL) { 967 Fstats = nfstats->next; 968 stats_delete(nfstats->stats); 969 if (nfstats->idstats != NULL) 970 stats_delete(nfstats->idstats); 971 FREE(nfstats); 972 nfstats = Fstats; 973 } 974 lut_free(Timesuffixlut, NULL, NULL); 975 lut_free(Rwordslut, NULL, NULL); 976 lut_free(Ident, NULL, NULL); 977 lut_free(Dicts, NULL, NULL); 978 } 979