/* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License, Version 1.0 only * (the "License"). You may not use this file except in compliance * with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright (c) 1997-1999 by Sun Microsystems, Inc. * All rights reserved. */ #pragma ident "%Z%%M% %I% %E% SMI" #include #include #include #include "xlator.h" #include "util.h" #include "bucket.h" #include "errlog.h" /* Types: */ #define TRUE 1 #define FALSE 0 #define MAXLINE 1024 typedef enum { PARENT, UNCLE } RELATION; /* Statics: */ /* The parser is a dfa, driven by the following: */ static FILE *Fp; static const char *Filename; static char Previous[MAXLINE]; static char LeftMostChild[MAXLINE]; static int Selected = FALSE; static int Line; static int Errors; /* The grammar is: */ static int arch(void); static int comment(void); static int arch_name(void); static int set_list(void); static int set(void); /* The supporting code is: */ static int accept_token(char *); static void skip_to(char *); /* And the tokenizer is: */ static char *tokenize(char *); static char *currtok(void); static char *nexttok(void); static char *skipb(char *); static char *skipover(char *); static char *CurrTok = NULL; static int set_parents(void); static table_t *Vers; static table_t *Varch; static void init_tables(void); static void add_valid_arch(char *); static void add_valid_version(char *vers_name); #define in_specials(c) ((c) == '{' || (c) == '}' || (c) == '+' || \ (c) == '-' || (c) == ';' || (c) == ':' || (c) == ',' || \ (c) == '[' || (c) == ']') #define eq(s1, s2) (strcmp((s1), (s2)) == 0) /* * parse_versions -- parse the file whose name is passed, return * the number of (fatal) errors encountered. Currently only * knows about reading set files and writing vers files. */ int parse_versions(const char *fileName) { /* Prime the set-file parser dfa: */ assert(fileName != NULL, "passed null filename to parse_versions"); errlog(BEGIN, "parse_versions(%s) {", fileName); if ((Fp = fopen(fileName, "r")) == NULL) { (void) fprintf(stderr, "Cannot open version file \"%s\"\n", fileName); errlog(END, "} /* parse_versions */"); return (1); } Filename = fileName; Line = 0; errlog(VERBOSE, "reading set file %s looking for architecture %s", Filename, TargetArchStr); /* Run the dfa. */ while (arch()) continue; (void) fclose(Fp); /* print_all_buckets(); */ errlog(END, "} /* parse_versions */"); return (Errors); } /* * The parser. This implements the grammar: * setfile::= (arch())+ * | * arch::= "{" (set_list())* "}" * set_list::= (set())+ ";" * set::= ["[" "WEAK" "]"] ":" "{" (ancestors) "}" ";" * ancestors::= | "," * where and are tokens. */ static int arch(void) { int olderrors; errlog(BEGIN, "arch() {"); if (comment()) { errlog(END, "} /* arch */"); return (TRUE); } if (arch_name() == FALSE) { errlog(END, "} /* arch */"); return (FALSE); } if (accept_token("{") == FALSE) { errlog(END, "} /* arch */"); return (FALSE); } olderrors = Errors; if (set_list() == FALSE) { if (olderrors != Errors) { errlog(END, "} /* arch */"); return (FALSE); } } errlog(END, "} /* arch */"); return (TRUE); } static int comment(void) { char *token = currtok(); if (token == NULL || *token != '#') { return (FALSE); } else { /* Swallow token. */ token = nexttok(); return (TRUE); } } static int arch_name(void) { char *token = currtok(); errlog(BEGIN, "arch_name() {"); errlog(VERBOSE, "token = '%s';", token ? token : ""); if (token == NULL) { errlog(END, "} /* arch_name */"); return (FALSE); } else if (in_specials(*token)) { /* It's not an architecture */ Selected = FALSE; /* Report a syntax error: TBD */ errlog(INPUT | ERROR, "found special char. %c " "while looking for an architecture name", *token); skip_to("}"); /* The follower set for arch_name. */ errlog(END, "} /* arch name */"); Errors++; return (FALSE); } else if (!eq(token, TargetArchStr)) { /* It's an architecture ... */ errlog(VERBOSE, "Begin unselected architecture: %s", token); add_valid_arch(token); (void) nexttok(); /* ... but the the wrong one. */ Selected = FALSE; errlog(END, "} /* arch name */"); return (TRUE); } else { /* Found the right architecture. */ errlog(VERBOSE, "Begin selected architecture: %s", token); add_valid_arch(token); (void) nexttok(); Selected = TRUE; errlog(END, "} /* arch name */"); return (TRUE); } } static int set_list(void) { int olderrors; char *token = currtok(); errlog(BEGIN, "set_list() {"); errlog(VERBOSE, "token = '%s'", (token) ? token : ""); if (set() == FALSE) { errlog(END, "} /* set_list */"); return (FALSE); } olderrors = Errors; while (set()) { continue; } if (olderrors != Errors) { errlog(END, "} /* set_list */"); return (FALSE); } errlog(END, "} /* set_list */"); return (TRUE); } static int set(void) { char *token = currtok(); int has_parent = 0; errlog(BEGIN, "set() {"); errlog(VERBOSE, "token = '%s'", (token) ? token : ""); if (in_specials(*token)) { errlog(INPUT|ERROR, "unexpected token \"%s\" found. " "Version name expected", token); Errors++; errlog(END, "} /* set */"); return (FALSE); } errlog(VERBOSE, "Begin Version: %s", token); *Previous = '\0'; if (Selected) { if (add_parent(token, Previous, 0) == FALSE) { errlog(INPUT | ERROR, "unable to add a parent version " "from the set file"); Errors++; errlog(END, "} /* set */"); return (FALSE); } } add_valid_version(token); (void) strncpy(LeftMostChild, token, MAXLINE); LeftMostChild[MAXLINE-1] = '\0'; (void) strncpy(Previous, token, MAXLINE); Previous[MAXLINE-1] = '\0'; token = nexttok(); switch (*token) { case ':': errlog(VERBOSE, "token ':' found"); (void) accept_token(":"); if (set_parents() == FALSE) { errlog(END, "} /* set */"); return (FALSE); } if (accept_token(";") == FALSE) { errlog(END, "} /* set */"); return (FALSE); } errlog(VERBOSE, "End Version"); break; case ';': errlog(VERBOSE, "token ';' found"); (void) accept_token(";"); errlog(VERBOSE, "End version ':'"); break; case '[': (void) accept_token("["); if (accept_token("WEAK") == FALSE) { errlog(END, "} /* set */"); return (FALSE); } if (accept_token("]") == FALSE) { errlog(END, "} /* set */"); return (FALSE); } token = currtok(); if (eq(token, ":")) { (void) accept_token(":"); has_parent = 1; } else if (eq(token, ";")) { (void) accept_token(";"); } else { errlog(ERROR|INPUT, "Unexpected token \"%s\" found. ':'" "or ';' expected.", token); Errors++; errlog(END, "} /* set */"); return (FALSE); } errlog(VERBOSE, "WEAK version detected\n"); if (Selected) set_weak(LeftMostChild, TRUE); if (has_parent) { if (set_parents() == FALSE) { errlog(END, "} /* set */"); return (FALSE); } if (accept_token(";") == FALSE) { errlog(END, "} /* set */"); return (FALSE); } } errlog(VERBOSE, "End Version"); break; default: /* CSTYLED */ errlog(ERROR|INPUT, "Unexpected token \"%s\" found. ';' expected.", token); Errors++; errlog(END, "} /* set */"); return (FALSE); } token = currtok(); if (eq(token, "}")) { (void) accept_token("}"); errlog(VERBOSE, "End architecture"); errlog(END, "} /* set */"); return (FALSE); } errlog(END, "} /* set */"); return (TRUE); } static int set_parents(void) { char *token = currtok(); int uncle; errlog(BEGIN, "set_parents() {"); errlog(VERBOSE, "token = '%s'", (token) ? token : ""); if (accept_token("{") == FALSE) { errlog(INPUT|ERROR, "set_parents(): Unexpected token: %s\n", token); Errors++; errlog(END, "} /* set_parents */"); return (FALSE); } token = currtok(); if (in_specials(*token)) { errlog(INPUT|ERROR, "set_parents(): Unexpected token: %c " "found. Version token expected", *token); Errors++; errlog(END, "} /* set_parents */"); return (FALSE); } uncle = 0; while (token && *token != '}') { errlog(VERBOSE, "Begin parent list: %s\n", token); if (Selected) { if (uncle) (void) add_uncle(token, LeftMostChild, 0); else (void) add_parent(token, Previous, 0); } (void) strncpy(Previous, token, MAXLINE); add_valid_version(token); Previous[MAXLINE-1] = '\0'; token = nexttok(); if (*token == ',') { token = nexttok(); /* following identifiers are all uncles */ uncle = 1; continue; } if (*token == '}') { if (accept_token("}") == FALSE) { errlog(END, "} /* set_parents */"); return (FALSE); } errlog(VERBOSE, "set_parent: End of parent list"); errlog(END, "} /* set_parents */"); return (TRUE); } errlog(INPUT|ERROR, "set_parents(): Unexpected token \"%s\" " "found. ',' or '}' were expected", token); Errors++; errlog(END, "} /* set_parents */"); return (FALSE); } errlog(END, "} /* set_parents */"); return (TRUE); } /* * parser support routines */ /* * accept_token -- get a specified token or complain loudly. */ static int accept_token(char *expected) { char *token = currtok(); assert(expected != NULL, "null token passed to accept_token"); errlog(OTHER | TRACING, "accept_token, at %s expecting %s", (token) ? token : "", expected); if (token == NULL) { /* We're at EOF */ return (TRUE); } if (eq(token, expected)) { (void) nexttok(); return (TRUE); } else { errlog(INPUT | ERROR, "accept_token, found %s while looking for %s", (token) ? token : "", expected); ++Errors; return (FALSE); } } static void skip_to(char *target) { char *token = currtok(); assert(target != NULL, "null target passed to skip_to"); while (token && !eq(token, target)) { errlog(VERBOSE, "skipping over %s", (token) ? token : ""); token = nexttok(); } } /* * tokenizer -- below the grammar lives this, like a troll * under a bridge. */ /* * skipb -- skip over blanks (whitespace, actually), stopping * on first non-blank. */ static char * skipb(char *p) { while (*p && isspace(*p)) ++p; return (p); } /* * skipover -- skip over non-separators (alnum, . and _, actually), * stopping on first separator. */ static char * skipover(char *p) { while (*p && (isalnum(*p) || (*p == '_' || *p == '.'))) ++p; return (p); } /* * currtok/nexttok -- get the current/next token */ static char * currtok(void) { if (CurrTok == NULL) { (void) nexttok(); } return (CurrTok); } static char * nexttok(void) { static char line[MAXLINE]; char *p; if ((p = tokenize(NULL)) == NULL) { /* We're at an end of line. */ do { if (fgets(line, sizeof (line), Fp) == NULL) { /* Which is also end of file. */ CurrTok = NULL; return (NULL); } ++Line; seterrline(Line, Filename, "", line); } while ((p = tokenize(line)) == NULL); } CurrTok = p; return (p); } /* * tokenize -- a version of the standard strtok with specific behavior. */ static char * tokenize(char *line) { static char *p = NULL; static char saved = 0; char *q; if (line == NULL && p == NULL) { /* It's the very first time */ return (NULL); } else if (line != NULL) { /* Initialize with a new line */ q = skipb(line); } else { /* Restore previous line. */ *p = saved; q = skipb(p); } /* q is at the beginning of a token or at EOL, p is irrelevant. */ if (*q == '\0') { /* It's at EOL. */ p = q; } else if (in_specials(*q)) { /* We have a special-character token. */ p = q + 1; } else if (*q == '#') { /* The whole rest of the line is a comment token. */ return (NULL); } else { /* We have a word token. */ p = skipover(q); } saved = *p; *p = '\0'; if (p == q) { /* End of line */ return (NULL); } else { return (q); } } /* * valid_version -- see if a version string was mentioned in the set file. */ int valid_version(const char *vers_name) { if (Vers == NULL) { init_tables(); } return (in_stringtable(Vers, vers_name)); } /* * valid_arch -- see if the arch was mentioned in the set file. */ int valid_arch(const char *arch_name) { if (Vers == NULL) { init_tables(); } return (in_stringtable(Varch, arch_name)); } /* * add_valid_version and _arch -- add a name to the table. */ static void add_valid_version(char *vers_name) { errlog(BEGIN, "add_valid_version(\"%s\") {", vers_name); if (Vers == NULL) { init_tables(); } Vers = add_to_stringtable(Vers, vers_name); errlog(END, "}"); } static void add_valid_arch(char *arch_name) { errlog(BEGIN, "add_valid_arch(\"%s\") {", arch_name); if (Vers == NULL) { init_tables(); } Varch = add_to_stringtable(Varch, arch_name); errlog(END, "}"); } /* * init_tables -- creat them when first used. */ static void init_tables(void) { Vers = create_stringtable(TABLE_INITIAL); Varch = create_stringtable(TABLE_INITIAL); }