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