600 lines
14 KiB
C
600 lines
14 KiB
C
#ifndef lint
|
|
static char rcsid[] = "$Id: specfile.c,v 1.9 1994/04/05 23:57:31 deraadt Exp $";
|
|
#endif /* not lint */
|
|
|
|
#include <stdio.h>
|
|
#include <ctype.h>
|
|
#include <setjmp.h>
|
|
#include "y.tab.h"
|
|
#include "config.h"
|
|
#include "specfile.h"
|
|
|
|
#define is_paren(x) ((x == '(') || (x == ')'))
|
|
struct file_keyword {
|
|
char *key;
|
|
int token;
|
|
} file_kw_table[] = {
|
|
"compile-with", T_COMPILE_WITH,
|
|
"config-dependent", T_CONFIG_DEPENDENT,
|
|
"device-driver", T_DEVICE_DRIVER,
|
|
"optional", T_OPTIONAL,
|
|
"or", T_OR,
|
|
"not", T_NOT,
|
|
"requires", T_REQUIRES,
|
|
"standard", T_STANDARD,
|
|
"needs-count", T_NEEDS_COUNT,
|
|
};
|
|
|
|
extern struct file_list *fltail_lookup(),*new_fent(),*fl_lookup();
|
|
static char *current_file;
|
|
static int current_line;
|
|
static jmp_buf parse_problem;
|
|
|
|
int file_tok(token)
|
|
char *token;
|
|
{
|
|
int i, length;
|
|
|
|
if (is_paren(*token))
|
|
return (*token == '(' ? T_LEFTPAREN : T_RIGHTPAREN);
|
|
for (i =0; i <(sizeof(file_kw_table)/sizeof(file_kw_table[0])); i++) {
|
|
if (!strcmp(file_kw_table[i].key, token))
|
|
return file_kw_table[i].token;
|
|
}
|
|
length = strlen(token);
|
|
for (i = 0; i < length; i++) {
|
|
if (!isalnum(token[i]) && (token[i] != '_') &&
|
|
(token[i] != '-')) return -1;
|
|
}
|
|
return T_IDENTIFIER;
|
|
}
|
|
|
|
struct name_expr *
|
|
alloc_name_expr(name)
|
|
char *name;
|
|
{
|
|
struct name_expr *new;
|
|
int token,type;
|
|
|
|
type = 0;
|
|
token = file_tok(name);
|
|
switch (token) {
|
|
case T_OR:
|
|
case T_NOT:
|
|
case T_IDENTIFIER:
|
|
case T_LEFTPAREN:
|
|
case T_RIGHTPAREN:
|
|
type = token;
|
|
break;
|
|
default: return NULL;
|
|
}
|
|
new = malloc(sizeof(struct name_expr));
|
|
new->type = type;
|
|
if (type == T_IDENTIFIER) new->name = ns(name);
|
|
else new->name = NULL;
|
|
new->left = new->right = new->next = NULL;
|
|
return new;
|
|
}
|
|
|
|
void parse_err(char *emessage)
|
|
{
|
|
fprintf(stderr, "%s:%d: %s\n", current_file, current_line, emessage);
|
|
if (!fatal_errors) longjmp(parse_problem,1);
|
|
exit(1);
|
|
}
|
|
|
|
delete_expr(expr)
|
|
struct name_expr *expr;
|
|
{
|
|
if (expr->type == T_IDENTIFIER)
|
|
free(expr->name);
|
|
free(expr);
|
|
}
|
|
struct name_expr *yank_node(expr)
|
|
struct name_expr **expr;
|
|
{
|
|
struct name_expr *node;
|
|
|
|
if (*expr == NULL) return NULL;
|
|
node = *expr;
|
|
*expr = (*expr)->next;
|
|
return node;
|
|
}
|
|
struct name_expr *yank_expr(expr)
|
|
struct name_expr **expr;
|
|
{
|
|
struct name_expr *node,*tail, *subexpr,*next;
|
|
|
|
node = yank_node(expr);
|
|
if (node == NULL) return NULL;
|
|
if (node->type == T_LEFTPAREN)
|
|
for (tail = subexpr = NULL;;) {
|
|
next = yank_expr(expr);
|
|
if (!next) parse_err("missing ')'");
|
|
if (next->type == T_RIGHTPAREN) {
|
|
if (subexpr == NULL)
|
|
parse_err("empty inner expression i.e '()' ");
|
|
node->left = subexpr;
|
|
node->type = EXPR_GROUP;
|
|
break;
|
|
} else {
|
|
if (subexpr == NULL)
|
|
tail = subexpr = next;
|
|
else {
|
|
tail->next = next;
|
|
tail = next;
|
|
}
|
|
tail->next = NULL;
|
|
}
|
|
}
|
|
return(node);
|
|
}
|
|
|
|
struct name_expr *
|
|
paren_squish(tree)
|
|
struct name_expr *tree;
|
|
{
|
|
struct name_expr *result,*expr,*tail;
|
|
|
|
result = tail = NULL;
|
|
|
|
while ((expr = yank_expr(&tree)) != NULL) {
|
|
if (expr->type == T_RIGHTPAREN)
|
|
parse_err("unexpected ')'");
|
|
if (result == NULL) {
|
|
tail = result = expr;
|
|
}
|
|
else {
|
|
tail->next = expr;
|
|
tail = expr;
|
|
}
|
|
tail->next = NULL;
|
|
}
|
|
return(result);
|
|
}
|
|
|
|
struct name_expr *
|
|
not_squish(tree)
|
|
struct name_expr *tree;
|
|
{
|
|
struct name_expr *result,*next,*tail,*node;
|
|
|
|
tail = result = next = NULL;
|
|
|
|
while ((next = yank_node(&tree)) != NULL) {
|
|
if (next->type == EXPR_GROUP)
|
|
next->left = not_squish(next->left);
|
|
if (next->type == T_NOT) {
|
|
int notlevel = 1;
|
|
|
|
node = yank_node(&tree);
|
|
while (node->type == T_NOT) {
|
|
++notlevel;
|
|
node = yank_node(&tree);
|
|
}
|
|
if (node == NULL)
|
|
parse_err("no expression following 'not'");
|
|
if (node->type == T_OR)
|
|
parse_err("nothing between 'not' and 'or'");
|
|
if (notlevel % 2 != 1)
|
|
next = node;
|
|
else
|
|
next->left = node;
|
|
}
|
|
|
|
/* add the node to our result plan */
|
|
if (result == NULL)
|
|
tail = result = next;
|
|
else {
|
|
tail->next = next;
|
|
tail = next;
|
|
}
|
|
tail->next = NULL;
|
|
}
|
|
return(result);
|
|
}
|
|
struct name_expr *
|
|
or_squish(tree)
|
|
struct name_expr *tree;
|
|
{
|
|
struct name_expr *next, *tail,*result;
|
|
|
|
tail = result = next = NULL;
|
|
|
|
while ((next = yank_node(&tree)) != NULL) {
|
|
|
|
if (next->type == EXPR_GROUP)
|
|
next->left = or_squish(next->left);
|
|
|
|
if (next->type == T_NOT)
|
|
next->left = or_squish(next->left);
|
|
|
|
if (next->type == T_OR) {
|
|
if (result == NULL)
|
|
parse_err("no expression before 'or'");
|
|
next->left = result;
|
|
next->right = or_squish(tree);
|
|
if (next->right == NULL)
|
|
parse_err("no expression after 'or'");
|
|
next->next = NULL;
|
|
return(next);
|
|
}
|
|
|
|
/* add the node to our result plan */
|
|
if (result == NULL)
|
|
tail = result = next;
|
|
else {
|
|
tail->next = next;
|
|
tail = next;
|
|
}
|
|
tail->next = NULL;
|
|
}
|
|
return(result);
|
|
}
|
|
|
|
struct name_expr *
|
|
parse_name_expr(fp,seed, read_ahead)
|
|
FILE *fp;
|
|
char *seed, **read_ahead;
|
|
{
|
|
struct name_expr *list, *tail,*new,*current;
|
|
char *str;
|
|
|
|
|
|
list = NULL;
|
|
*read_ahead = NULL;
|
|
if (seed) {
|
|
list = alloc_name_expr(seed);
|
|
if (list == NULL) {
|
|
*read_ahead = seed;
|
|
return NULL;
|
|
}
|
|
}
|
|
tail = list;
|
|
for (;;) {
|
|
str = get_word(fp);
|
|
if ((str == (char *)EOF) || str == NULL) {
|
|
*read_ahead = str;
|
|
break;
|
|
}
|
|
new = alloc_name_expr(str);
|
|
if (!new) {
|
|
*read_ahead = str;
|
|
break;
|
|
}
|
|
if (tail) tail->next = new;
|
|
else {
|
|
list = new;
|
|
}
|
|
tail = new;
|
|
}
|
|
list = paren_squish(list);
|
|
list = not_squish(list);
|
|
list = or_squish(list);
|
|
return list;
|
|
}
|
|
int is_simple(expr)
|
|
struct name_expr *expr;
|
|
{
|
|
return expr && expr->type == T_IDENTIFIER;
|
|
}
|
|
|
|
int f_not(expr,explain)
|
|
struct name_expr *expr;
|
|
int explain;
|
|
{
|
|
int result;
|
|
|
|
result = !depend_check(expr->left,explain);
|
|
return result;
|
|
}
|
|
int f_or(expr,explain)
|
|
struct name_expr *expr;
|
|
int explain;
|
|
{
|
|
int result;
|
|
|
|
if (depend_check(expr->left,explain))
|
|
return 1;
|
|
if (depend_check(expr->right,explain))
|
|
return 1;
|
|
return 0;
|
|
}
|
|
int f_identifier(expr,explain)
|
|
struct name_expr *expr;
|
|
int explain;
|
|
{
|
|
struct opt *op;
|
|
struct device *dp;
|
|
struct cputype *cp;
|
|
|
|
for (op = opt; op != 0; op = op->op_next)
|
|
if (opteq(op->op_name, expr->name)) return 1;
|
|
for (cp = cputype; cp != 0; cp = cp->cpu_next)
|
|
if (opteq(cp->cpu_name, expr->name)) return 1;
|
|
for (dp = dtab; dp != 0; dp = dp->d_next)
|
|
if (eq(dp->d_name, expr->name) &&
|
|
!(dp->d_type == PSEUDO_DEVICE && dp->d_flags && dp->d_slave == 0)) return 1;
|
|
return 0;
|
|
}
|
|
print_expr(expr)
|
|
struct name_expr *expr;
|
|
{
|
|
struct name_expr *current;
|
|
for (current = expr; current != NULL; current= current->next) {
|
|
switch (current->type) {
|
|
case T_NOT:
|
|
fprintf (stderr, "not ");
|
|
print_expr(current->left);
|
|
break;
|
|
case T_OR:
|
|
print_expr(current->left);
|
|
fprintf (stderr, "or ");
|
|
print_expr(current->right);
|
|
break;
|
|
case EXPR_GROUP:
|
|
fprintf (stderr, "(");
|
|
print_expr(current->left);
|
|
fprintf (stderr, ")");
|
|
break;
|
|
case T_IDENTIFIER:
|
|
fprintf(stderr,"%s ", current->name);
|
|
break;
|
|
default:
|
|
parse_err("unknown expression type");
|
|
}
|
|
}
|
|
}
|
|
int depend_check(expr, explain)
|
|
struct name_expr *expr;
|
|
int explain;
|
|
{
|
|
struct name_expr *current;
|
|
int result;
|
|
|
|
for (current= expr; current; current= current->next) {
|
|
switch(current->type) {
|
|
case T_NOT:
|
|
result = f_not(current,0);
|
|
break;
|
|
case T_OR:
|
|
result = f_or(current,0);
|
|
break;
|
|
case EXPR_GROUP:
|
|
result = depend_check(current->left, 0);
|
|
break;
|
|
case T_IDENTIFIER:
|
|
result = f_identifier(current,0);
|
|
break;
|
|
}
|
|
if (result) continue;
|
|
return 0;
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
|
|
|
|
read_file(filename, fatal_on_open, override)
|
|
char *filename;
|
|
int fatal_on_open, override;
|
|
{
|
|
FILE *fp;
|
|
size_t length;
|
|
char ebuf[1024];
|
|
|
|
fp = fopen(filename, "r");
|
|
if (!fp)
|
|
if (fatal_on_open) {
|
|
perror(filename);
|
|
exit(1);
|
|
}
|
|
else return;
|
|
current_line = 0;
|
|
current_file = filename;
|
|
for (;;) {
|
|
char *str, *kf_name, *read_ahead,*compile_with;
|
|
extern char *get_word(),*get_quoted_word();
|
|
int token,optional,driver,needs_count,config_depend,
|
|
is_dup,filetype,is_option;
|
|
struct name_expr *depends_on,*requires;
|
|
struct file_list *tp,*tmp, *fl,*pf;
|
|
enum {BEFORE_FILENAME,BEFORE_SPEC,BEFORE_DEPENDS,PAST_DEPENDS,
|
|
SPECIALS} parse_state;
|
|
|
|
|
|
if (setjmp(parse_problem)) {
|
|
while (1) {
|
|
str = get_word(fp);
|
|
if (!str || (str == (char *) EOF)) break;
|
|
}
|
|
if (!str) current_line++;
|
|
continue;
|
|
}
|
|
str = get_word(fp);
|
|
current_line++;
|
|
if (str == NULL) continue;
|
|
if (str == (char *) EOF) break;
|
|
if (*str == '#') {
|
|
fprintf(stderr, "shouldn't get here");
|
|
exit(1);
|
|
}
|
|
parse_state = BEFORE_FILENAME;
|
|
kf_name = read_ahead = compile_with = NULL;
|
|
optional= driver = config_depend = filetype = needs_count = 0;
|
|
depends_on = requires = NULL;
|
|
is_dup = 0;
|
|
is_option = 0;
|
|
while ((str != NULL) && (str != (char *)EOF)) {
|
|
switch (parse_state) {
|
|
case BEFORE_FILENAME: {
|
|
kf_name = ns(str);
|
|
if (strncmp(kf_name, "OPTIONS/", 8) == 0) {
|
|
kf_name = ns(index(kf_name, '/') + 1);
|
|
is_option++;
|
|
}
|
|
parse_state = BEFORE_SPEC;
|
|
break;
|
|
}
|
|
case BEFORE_SPEC: {
|
|
token = file_tok(str);
|
|
if ((token != T_OPTIONAL) && (token != T_STANDARD))
|
|
parse_err("unexpected token starts inclusion specification");
|
|
optional = (token == T_OPTIONAL);
|
|
parse_state = BEFORE_DEPENDS;
|
|
break;
|
|
}
|
|
case BEFORE_DEPENDS: {
|
|
depends_on = parse_name_expr(fp,str, &read_ahead);
|
|
str = read_ahead;
|
|
parse_state = PAST_DEPENDS;
|
|
continue;
|
|
break;
|
|
}
|
|
case PAST_DEPENDS:
|
|
case SPECIALS:
|
|
token = file_tok(str);
|
|
switch (token) {
|
|
case T_COMPILE_WITH: {
|
|
str = get_quoted_word(fp);
|
|
if ((str == 0) || (str == (char *) EOF))
|
|
parse_err("missing compile command string");
|
|
compile_with = ns(str);
|
|
}
|
|
case T_CONFIG_DEPENDENT: {
|
|
config_depend = 1;
|
|
break;
|
|
}
|
|
case T_DEVICE_DRIVER: {
|
|
driver = 1;
|
|
needs_count = 0;
|
|
break;
|
|
}
|
|
case T_REQUIRES: {
|
|
requires = parse_name_expr(fp,NULL, &read_ahead);
|
|
if (!requires)
|
|
parse_err("'requires' but no expression");
|
|
str = read_ahead;
|
|
continue;
|
|
break;
|
|
}
|
|
case T_NEEDS_COUNT: {
|
|
if (!driver) needs_count = 1;
|
|
break;
|
|
}
|
|
default:
|
|
parse_err("unexpected token");
|
|
}
|
|
break;
|
|
default:
|
|
parse_err("unknown state");
|
|
}
|
|
str = get_word(fp);
|
|
}
|
|
if (parse_state == BEFORE_SPEC)
|
|
parse_err("filename, but no specification");
|
|
if (is_option) {
|
|
struct device dev;
|
|
register struct opt *op;
|
|
struct opt *lop = 0;
|
|
|
|
/*
|
|
* Allocate a pseudo-device entry which we will insert into
|
|
* the device list below. The flags field is set non-zero to
|
|
* indicate an internal entry rather than one generated from
|
|
* the configuration file. The slave field is set to define
|
|
* the corresponding symbol as 0 should we fail to find the
|
|
* option in the option list.
|
|
*/
|
|
init_dev(&dev);
|
|
dev.d_type = PSEUDO_DEVICE;
|
|
dev.d_name = ns(kf_name);
|
|
dev.d_slave = 0;
|
|
dev.d_flags++;
|
|
for (op=opt; op; lop=op, op=op->op_next) {
|
|
char *od = raisestr(ns(kf_name));
|
|
|
|
/*
|
|
* Found an option which matches the current device
|
|
* dependency identifier. Set the slave field to
|
|
* define the option in the header file.
|
|
*/
|
|
if (strcmp(op->op_name, od) == 0) {
|
|
dev.d_slave = 1;
|
|
if (lop == 0)
|
|
opt = op->op_next;
|
|
else
|
|
lop->op_next = op->op_next;
|
|
free(op);
|
|
op = 0;
|
|
}
|
|
free(od);
|
|
if (op == 0)
|
|
break;
|
|
}
|
|
newdev(&dev);
|
|
needs_count = 0;
|
|
driver = 1;
|
|
filetype = INVISIBLE;
|
|
}
|
|
else {
|
|
if (!kf_name)
|
|
parse_err("no filename specified");
|
|
fl = fl_lookup(kf_name);
|
|
if (fl && !override) {
|
|
(void) sprintf(ebuf, "duplicate file name '%s'", kf_name);
|
|
parse_err(ebuf);
|
|
}
|
|
if ((pf = fl_lookup(kf_name)) &&
|
|
(pf->f_type != INVISIBLE || (pf->f_flags | DUPLICATE)))
|
|
is_dup = 1;
|
|
else
|
|
is_dup = 0;
|
|
if (override && ((tmp = fltail_lookup(kf_name)) != 0)) {
|
|
fprintf(stderr, "%s:%d: Local file %s overrides %s.\n",
|
|
current_file, current_line, kf_name, tmp->f_fn);
|
|
tmp->f_type = INVISIBLE;
|
|
}
|
|
if (!optional) {
|
|
if (driver)
|
|
parse_err("'standard' incompatible with 'device-driver'");
|
|
if (depends_on && !needs_count)
|
|
parse_err("'standard' can't have dependencies");
|
|
}
|
|
else if (!depends_on)
|
|
parse_err("'optional' requires dependency specification");
|
|
if (is_simple(depends_on) &&
|
|
eq("profiling-routine", depends_on->name)) filetype = PROFILING;
|
|
else if (!optional || depend_check(depends_on,0)) filetype = NORMAL;
|
|
else filetype = INVISIBLE;
|
|
|
|
if (filetype == NORMAL && requires && !depend_check(requires,0)) {
|
|
fprintf(stderr, "%s:%d: requirement expression failed: ",
|
|
current_file, current_line);
|
|
print_expr(requires);
|
|
fprintf(stderr, "\n");
|
|
parse_err("requirements not met");
|
|
}
|
|
}
|
|
tp = new_fent();
|
|
tp->f_fn = kf_name;
|
|
tp->f_type = filetype;
|
|
if (driver)
|
|
tp->f_needs = depends_on;
|
|
else
|
|
tp->f_needs = NULL;
|
|
if (needs_count)
|
|
tp->f_countname = depends_on;
|
|
else
|
|
tp->f_countname = NULL;
|
|
tp->f_was_driver = driver;
|
|
tp->f_needs_count = needs_count;
|
|
tp->f_special = compile_with;
|
|
tp->f_flags = 0;
|
|
tp->f_flags |= (config_depend ? CONFIGDEP : 0);
|
|
tp->f_flags |= (is_dup ? DUPLICATE : 0);
|
|
}
|
|
return;
|
|
|
|
}
|