/*
* 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 <stdio.h>
#include <string.h>
#include <ctype.h>
#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())+ <EOF>
* | <EOF>
* arch::= <ARCHITECTURE> "{" (set_list())* "}"
* set_list::= (set())+ ";"
* set::= <IDENTIFIER> ["[" "WEAK" "]"] ":" "{" (ancestors) "}" ";"
* ancestors::= <IDENTIFIER> | <ancestors> "," <IDENTIFIER>
* where <ARCHITECTURE> and <IDENTIFIER> 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 : "<NULL>");
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 : "<NULL>");
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 : "<NULL>");
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 : "<NULL>");
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 : "<NULL>", 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 : "<NULL>", 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 : "<NULL>");
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);
}