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 (c) 1997-1999 by Sun Microsystems, Inc. 24 * All rights reserved. 25 */ 26 #pragma ident "%Z%%M% %I% %E% SMI" 27 28 #include <stdio.h> 29 #include <string.h> 30 #include <ctype.h> 31 #include "xlator.h" 32 #include "util.h" 33 #include "bucket.h" 34 #include "errlog.h" 35 36 /* Types: */ 37 #define TRUE 1 38 #define FALSE 0 39 #define MAXLINE 1024 40 41 42 typedef enum { 43 PARENT, UNCLE 44 } RELATION; 45 46 47 /* Statics: */ 48 /* The parser is a dfa, driven by the following: */ 49 static FILE *Fp; 50 static const char *Filename; 51 static char Previous[MAXLINE]; 52 static char LeftMostChild[MAXLINE]; 53 static int Selected = FALSE; 54 static int Line; 55 static int Errors; 56 57 58 /* The grammar is: */ 59 static int arch(void); 60 static int comment(void); 61 static int arch_name(void); 62 static int set_list(void); 63 static int set(void); 64 65 /* The supporting code is: */ 66 static int accept_token(char *); 67 static void skip_to(char *); 68 69 /* And the tokenizer is: */ 70 static char *tokenize(char *); 71 static char *currtok(void); 72 static char *nexttok(void); 73 static char *skipb(char *); 74 static char *skipover(char *); 75 static char *CurrTok = NULL; 76 77 static int set_parents(void); 78 79 static table_t *Vers; 80 static table_t *Varch; 81 82 static void init_tables(void); 83 84 static void add_valid_arch(char *); 85 static void add_valid_version(char *vers_name); 86 87 88 #define in_specials(c) ((c) == '{' || (c) == '}' || (c) == '+' || \ 89 (c) == '-' || (c) == ';' || (c) == ':' || (c) == ',' || \ 90 (c) == '[' || (c) == ']') 91 92 #define eq(s1, s2) (strcmp((s1), (s2)) == 0) 93 94 95 /* 96 * parse_versions -- parse the file whose name is passed, return 97 * the number of (fatal) errors encountered. Currently only 98 * knows about reading set files and writing vers files. 99 */ 100 int 101 parse_versions(const char *fileName) 102 { 103 104 /* Prime the set-file parser dfa: */ 105 assert(fileName != NULL, "passed null filename to parse_versions"); 106 errlog(BEGIN, "parse_versions(%s) {", fileName); 107 108 109 if ((Fp = fopen(fileName, "r")) == NULL) { 110 (void) fprintf(stderr, "Cannot open version file \"%s\"\n", 111 fileName); 112 errlog(END, "} /* parse_versions */"); 113 return (1); 114 } 115 Filename = fileName; 116 Line = 0; 117 118 errlog(VERBOSE, "reading set file %s looking for architecture %s", 119 Filename, TargetArchStr); 120 121 /* Run the dfa. */ 122 while (arch()) 123 continue; 124 125 (void) fclose(Fp); 126 /* print_all_buckets(); */ 127 errlog(END, "} /* parse_versions */"); 128 return (Errors); 129 } 130 131 132 /* 133 * The parser. This implements the grammar: 134 * setfile::= (arch())+ <EOF> 135 * | <EOF> 136 * arch::= <ARCHITECTURE> "{" (set_list())* "}" 137 * set_list::= (set())+ ";" 138 * set::= <IDENTIFIER> ["[" "WEAK" "]"] ":" "{" (ancestors) "}" ";" 139 * ancestors::= <IDENTIFIER> | <ancestors> "," <IDENTIFIER> 140 * where <ARCHITECTURE> and <IDENTIFIER> are tokens. 141 */ 142 static int 143 arch(void) 144 { 145 int olderrors; 146 147 errlog(BEGIN, "arch() {"); 148 if (comment()) { 149 errlog(END, "} /* arch */"); 150 return (TRUE); 151 } 152 if (arch_name() == FALSE) { 153 errlog(END, "} /* arch */"); 154 return (FALSE); 155 } 156 if (accept_token("{") == FALSE) { 157 errlog(END, "} /* arch */"); 158 return (FALSE); 159 } 160 161 olderrors = Errors; 162 if (set_list() == FALSE) { 163 if (olderrors != Errors) { 164 errlog(END, "} /* arch */"); 165 return (FALSE); 166 } 167 } 168 169 errlog(END, "} /* arch */"); 170 return (TRUE); 171 } 172 173 static int 174 comment(void) 175 { 176 char *token = currtok(); 177 178 if (token == NULL || *token != '#') { 179 return (FALSE); 180 } else { 181 /* Swallow token. */ 182 token = nexttok(); 183 return (TRUE); 184 } 185 } 186 187 static int 188 arch_name(void) 189 { 190 char *token = currtok(); 191 192 errlog(BEGIN, "arch_name() {"); 193 errlog(VERBOSE, "token = '%s';", 194 token ? token : "<NULL>"); 195 196 if (token == NULL) { 197 errlog(END, "} /* arch_name */"); 198 return (FALSE); 199 200 } else if (in_specials(*token)) { 201 /* It's not an architecture */ 202 Selected = FALSE; 203 204 /* Report a syntax error: TBD */ 205 errlog(INPUT | ERROR, "found special char. %c " 206 "while looking for an architecture name", 207 *token); 208 209 skip_to("}"); /* The follower set for arch_name. */ 210 errlog(END, "} /* arch name */"); 211 212 Errors++; 213 return (FALSE); 214 215 } else if (!eq(token, TargetArchStr)) { 216 /* It's an architecture ... */ 217 errlog(VERBOSE, "Begin unselected architecture: %s", token); 218 add_valid_arch(token); 219 (void) nexttok(); 220 221 /* ... but the the wrong one. */ 222 Selected = FALSE; 223 errlog(END, "} /* arch name */"); 224 return (TRUE); 225 } else { 226 /* Found the right architecture. */ 227 errlog(VERBOSE, "Begin selected architecture: %s", token); 228 add_valid_arch(token); 229 (void) nexttok(); 230 Selected = TRUE; 231 errlog(END, "} /* arch name */"); 232 return (TRUE); 233 } 234 } 235 236 237 static int 238 set_list(void) 239 { 240 int olderrors; 241 char *token = currtok(); 242 243 errlog(BEGIN, "set_list() {"); 244 errlog(VERBOSE, "token = '%s'", 245 (token) ? token : "<NULL>"); 246 if (set() == FALSE) { 247 errlog(END, "} /* set_list */"); 248 return (FALSE); 249 } 250 251 olderrors = Errors; 252 while (set()) { 253 continue; 254 } 255 if (olderrors != Errors) { 256 errlog(END, "} /* set_list */"); 257 return (FALSE); 258 } 259 260 errlog(END, "} /* set_list */"); 261 return (TRUE); 262 } 263 264 265 static int 266 set(void) 267 { 268 char *token = currtok(); 269 int has_parent = 0; 270 271 errlog(BEGIN, "set() {"); 272 errlog(VERBOSE, "token = '%s'", 273 (token) ? token : "<NULL>"); 274 275 if (in_specials(*token)) { 276 errlog(INPUT|ERROR, "unexpected token \"%s\" found. " 277 "Version name expected", token); 278 Errors++; 279 errlog(END, "} /* set */"); 280 return (FALSE); 281 } 282 283 errlog(VERBOSE, "Begin Version: %s", token); 284 *Previous = '\0'; 285 if (Selected) { 286 if (add_parent(token, Previous, 0) == FALSE) { 287 errlog(INPUT | ERROR, "unable to add a parent version " 288 "from the set file"); 289 Errors++; 290 errlog(END, "} /* set */"); 291 return (FALSE); 292 } 293 } 294 295 add_valid_version(token); 296 (void) strncpy(LeftMostChild, token, MAXLINE); 297 LeftMostChild[MAXLINE-1] = '\0'; 298 (void) strncpy(Previous, token, MAXLINE); 299 Previous[MAXLINE-1] = '\0'; 300 301 token = nexttok(); 302 303 switch (*token) { 304 case ':': 305 errlog(VERBOSE, "token ':' found"); 306 (void) accept_token(":"); 307 if (set_parents() == FALSE) { 308 errlog(END, "} /* set */"); 309 return (FALSE); 310 } 311 if (accept_token(";") == FALSE) { 312 errlog(END, "} /* set */"); 313 return (FALSE); 314 } 315 errlog(VERBOSE, "End Version"); 316 break; 317 318 case ';': 319 errlog(VERBOSE, "token ';' found"); 320 (void) accept_token(";"); 321 errlog(VERBOSE, "End version ':'"); 322 break; 323 324 case '[': 325 (void) accept_token("["); 326 if (accept_token("WEAK") == FALSE) { 327 errlog(END, "} /* set */"); 328 return (FALSE); 329 } 330 if (accept_token("]") == FALSE) { 331 errlog(END, "} /* set */"); 332 return (FALSE); 333 } 334 token = currtok(); 335 if (eq(token, ":")) { 336 (void) accept_token(":"); 337 has_parent = 1; 338 } else if (eq(token, ";")) { 339 (void) accept_token(";"); 340 } else { 341 errlog(ERROR|INPUT, 342 "Unexpected token \"%s\" found. ':'" 343 "or ';' expected.", token); 344 Errors++; 345 errlog(END, "} /* set */"); 346 return (FALSE); 347 } 348 errlog(VERBOSE, "WEAK version detected\n"); 349 if (Selected) 350 set_weak(LeftMostChild, TRUE); 351 352 if (has_parent) { 353 if (set_parents() == FALSE) { 354 errlog(END, "} /* set */"); 355 return (FALSE); 356 } 357 if (accept_token(";") == FALSE) { 358 errlog(END, "} /* set */"); 359 return (FALSE); 360 } 361 } 362 errlog(VERBOSE, "End Version"); 363 break; 364 default: 365 /* CSTYLED */ 366 errlog(ERROR|INPUT, 367 "Unexpected token \"%s\" found. ';' expected.", 368 token); 369 Errors++; 370 errlog(END, "} /* set */"); 371 return (FALSE); 372 } 373 374 token = currtok(); 375 if (eq(token, "}")) { 376 (void) accept_token("}"); 377 errlog(VERBOSE, "End architecture"); 378 errlog(END, "} /* set */"); 379 return (FALSE); 380 } 381 382 errlog(END, "} /* set */"); 383 return (TRUE); 384 } 385 386 static int 387 set_parents(void) 388 { 389 char *token = currtok(); 390 int uncle; 391 392 errlog(BEGIN, "set_parents() {"); 393 errlog(VERBOSE, "token = '%s'", 394 (token) ? token : "<NULL>"); 395 396 if (accept_token("{") == FALSE) { 397 errlog(INPUT|ERROR, "set_parents(): Unexpected token: %s\n", 398 token); 399 Errors++; 400 errlog(END, "} /* set_parents */"); 401 return (FALSE); 402 } 403 404 token = currtok(); 405 406 if (in_specials(*token)) { 407 errlog(INPUT|ERROR, "set_parents(): Unexpected token: %c " 408 "found. Version token expected", *token); 409 Errors++; 410 errlog(END, "} /* set_parents */"); 411 return (FALSE); 412 } 413 414 uncle = 0; 415 while (token && *token != '}') { 416 errlog(VERBOSE, "Begin parent list: %s\n", token); 417 if (Selected) { 418 if (uncle) 419 (void) add_uncle(token, LeftMostChild, 0); 420 else 421 (void) add_parent(token, Previous, 0); 422 } 423 (void) strncpy(Previous, token, MAXLINE); 424 add_valid_version(token); 425 Previous[MAXLINE-1] = '\0'; 426 427 token = nexttok(); 428 429 if (*token == ',') { 430 token = nexttok(); 431 /* following identifiers are all uncles */ 432 uncle = 1; 433 continue; 434 } 435 436 if (*token == '}') { 437 if (accept_token("}") == FALSE) { 438 errlog(END, "} /* set_parents */"); 439 return (FALSE); 440 } 441 errlog(VERBOSE, "set_parent: End of parent list"); 442 errlog(END, "} /* set_parents */"); 443 return (TRUE); 444 } 445 446 errlog(INPUT|ERROR, 447 "set_parents(): Unexpected token \"%s\" " 448 "found. ',' or '}' were expected", token); 449 Errors++; 450 errlog(END, "} /* set_parents */"); 451 return (FALSE); 452 } 453 errlog(END, "} /* set_parents */"); 454 return (TRUE); 455 } 456 457 458 /* 459 * parser support routines 460 */ 461 462 463 /* 464 * accept_token -- get a specified token or complain loudly. 465 */ 466 static int 467 accept_token(char *expected) 468 { 469 char *token = currtok(); 470 471 assert(expected != NULL, "null token passed to accept_token"); 472 errlog(OTHER | TRACING, "accept_token, at %s expecting %s", 473 (token) ? token : "<NULL>", expected); 474 475 if (token == NULL) { 476 /* We're at EOF */ 477 return (TRUE); 478 } 479 if (eq(token, expected)) { 480 (void) nexttok(); 481 return (TRUE); 482 } else { 483 errlog(INPUT | ERROR, 484 "accept_token, found %s while looking for %s", 485 (token) ? token : "<NULL>", expected); 486 ++Errors; 487 return (FALSE); 488 } 489 } 490 491 static void 492 skip_to(char *target) 493 { 494 char *token = currtok(); 495 496 assert(target != NULL, "null target passed to skip_to"); 497 while (token && !eq(token, target)) { 498 errlog(VERBOSE, "skipping over %s", 499 (token) ? token : "<NULL>"); 500 token = nexttok(); 501 } 502 } 503 504 505 /* 506 * tokenizer -- below the grammar lives this, like a troll 507 * under a bridge. 508 */ 509 510 511 /* 512 * skipb -- skip over blanks (whitespace, actually), stopping 513 * on first non-blank. 514 */ 515 static char * 516 skipb(char *p) 517 { 518 519 while (*p && isspace(*p)) 520 ++p; 521 return (p); 522 } 523 524 /* 525 * skipover -- skip over non-separators (alnum, . and _, actually), 526 * stopping on first separator. 527 */ 528 static char * 529 skipover(char *p) 530 { 531 532 while (*p && (isalnum(*p) || (*p == '_' || *p == '.'))) 533 ++p; 534 return (p); 535 } 536 537 538 /* 539 * currtok/nexttok -- get the current/next token 540 */ 541 static char * 542 currtok(void) 543 { 544 545 if (CurrTok == NULL) { 546 (void) nexttok(); 547 } 548 return (CurrTok); 549 } 550 551 static char * 552 nexttok(void) 553 { 554 static char line[MAXLINE]; 555 char *p; 556 557 if ((p = tokenize(NULL)) == NULL) { 558 /* We're at an end of line. */ 559 do { 560 if (fgets(line, sizeof (line), Fp) == NULL) { 561 /* Which is also end of file. */ 562 CurrTok = NULL; 563 return (NULL); 564 } 565 ++Line; 566 seterrline(Line, Filename, "", line); 567 } while ((p = tokenize(line)) == NULL); 568 } 569 CurrTok = p; 570 return (p); 571 } 572 573 574 575 /* 576 * tokenize -- a version of the standard strtok with specific behavior. 577 */ 578 static char * 579 tokenize(char *line) 580 { 581 static char *p = NULL; 582 static char saved = 0; 583 char *q; 584 585 if (line == NULL && p == NULL) { 586 /* It's the very first time */ 587 return (NULL); 588 } else if (line != NULL) { 589 /* Initialize with a new line */ 590 q = skipb(line); 591 } else { 592 /* Restore previous line. */ 593 *p = saved; 594 q = skipb(p); 595 } 596 /* q is at the beginning of a token or at EOL, p is irrelevant. */ 597 598 if (*q == '\0') { 599 /* It's at EOL. */ 600 p = q; 601 } else if (in_specials(*q)) { 602 /* We have a special-character token. */ 603 p = q + 1; 604 } else if (*q == '#') { 605 /* The whole rest of the line is a comment token. */ 606 return (NULL); 607 } else { 608 /* We have a word token. */ 609 p = skipover(q); 610 } 611 saved = *p; 612 *p = '\0'; 613 614 if (p == q) { 615 /* End of line */ 616 return (NULL); 617 } else { 618 return (q); 619 } 620 } 621 622 623 /* 624 * valid_version -- see if a version string was mentioned in the set file. 625 */ 626 int 627 valid_version(const char *vers_name) 628 { 629 630 if (Vers == NULL) { 631 init_tables(); 632 } 633 return (in_stringtable(Vers, vers_name)); 634 } 635 636 /* 637 * valid_arch -- see if the arch was mentioned in the set file. 638 */ 639 int 640 valid_arch(const char *arch_name) 641 { 642 643 if (Vers == NULL) { 644 init_tables(); 645 } 646 return (in_stringtable(Varch, arch_name)); 647 } 648 649 /* 650 * add_valid_version and _arch -- add a name to the table. 651 */ 652 static void 653 add_valid_version(char *vers_name) 654 { 655 errlog(BEGIN, "add_valid_version(\"%s\") {", vers_name); 656 if (Vers == NULL) { 657 init_tables(); 658 } 659 Vers = add_to_stringtable(Vers, vers_name); 660 errlog(END, "}"); 661 } 662 663 static void 664 add_valid_arch(char *arch_name) 665 { 666 667 errlog(BEGIN, "add_valid_arch(\"%s\") {", arch_name); 668 if (Vers == NULL) { 669 init_tables(); 670 } 671 Varch = add_to_stringtable(Varch, arch_name); 672 errlog(END, "}"); 673 } 674 675 /* 676 * init_tables -- creat them when first used. 677 */ 678 static void 679 init_tables(void) 680 { 681 Vers = create_stringtable(TABLE_INITIAL); 682 Varch = create_stringtable(TABLE_INITIAL); 683 } 684