From 86464aed71025541805e7b1515541aee89879e33 Mon Sep 17 00:00:00 2001 From: Ralf Baechle Date: Mon, 15 Feb 1999 02:15:32 +0000 Subject: Merge with Linux 2.2.1. --- scripts/tkcond.c | 753 +++++++++++++++++++++---------------------------------- 1 file changed, 284 insertions(+), 469 deletions(-) (limited to 'scripts/tkcond.c') diff --git a/scripts/tkcond.c b/scripts/tkcond.c index fc133295d..89069daad 100644 --- a/scripts/tkcond.c +++ b/scripts/tkcond.c @@ -1,544 +1,359 @@ -/* parser config.in - * - * Version 1.0 - * Eric Youngdale - * 10/95 - * - * The general idea here is that we want to parse a config.in file and - * from this, we generate a wish script which gives us effectively the - * same functionality that the original config.in script provided. - * - * This task is split roughly into 3 parts. The first parse is the parse - * of the input file itself. The second part is where we analyze the - * #ifdef clauses, and attach a linked list of tokens to each of the - * menu items. In this way, each menu item has a complete list of - * dependencies that are used to enable/disable the options. - * The third part is to take the configuration database we have build, - * and build the actual wish script. - * - * This file contains the code to further process the conditions from - * the "ifdef" clauses. +/* + * tkcond.c * - * The conditions are assumed to be one of the following formats + * Eric Youngdale was the original author of xconfig. + * Michael Elizabeth Chastain (mec@shout.net) is the current maintainer. * - * simple_condition:= "$VARIABLE" == y/n/m - * simple_condition:= "$VARIABLE != y/n/m + * This file takes the tokenized statement list and transforms 'if ...' + * statements. For each simple statement, I find all of the 'if' statements + * that enclose it, and attach the aggregate conditionals of those 'if' + * statements to the cond list of the simple statement. * - * simple_condition -a simple_condition + * 14 January 1999, Michael Elizabeth Chastain, + * - Steam-clean this file. I tested this by generating kconfig.tk for + * every architecture and comparing it character-for-character against + * the output of the old tkparse. * - * If the input condition contains '(' or ')' it would screw us up, but for now - * this is not a problem. + * TO DO: + * - xconfig is at the end of its life cycle. Contact if + * you are interested in working on the replacement. */ -#include + #include +#include #include + #include "tkparse.h" -/* - * Walk a condition chain and invert it so that the logical result is - * inverted. - */ -static void invert_condition(struct condition * cnd) -{ - /* - * This is simple. Just walk through the list, and invert - * all of the operators. - */ - for(;cnd; cnd = cnd->next) - { - switch(cnd->op) - { - case op_and: - cnd->op = op_or; - break; - case op_or: - /* - * This is not turned into op_and - we need to keep track - * of what operators were used here since we have an optimization - * later on to remove duplicate conditions, and having - * inverted ors in there would make it harder if we did not - * distinguish an inverted or from an and we inserted because - * of nested ifs. - */ - cnd->op = op_and1; - break; - case op_neq: - cnd->op = op_eq; - break; - case op_eq: - cnd->op = op_neq; - break; - default: - break; - } - } -} /* - * Walk a condition chain, and free the memory associated with it. - */ -static void free_condition(struct condition * cnd) -{ - struct condition * next; - for(;cnd; cnd = next) - { - next = cnd->next; - - if( cnd->variable.str != NULL ) - free(cnd->variable.str); - - free(cnd); - } -} - -/* - * Walk all of the conditions, and look for choice values. Convert - * the tokens into something more digestible. + * Transform op_variable to op_kvariable. + * + * This works, but it's gross, speed-wise. It would benefit greatly + * from a simple hash table that maps names to cfg. + * + * Note well: this is actually better than the loop structure xconfig + * has been staggering along with for three years, which performs + * this whole procedure inside *another* loop on active conditionals. */ -void fix_choice_cond(void) +void transform_to_kvariable( struct kconfig * scfg ) { - struct condition * cond; - struct condition * cond2; - struct kconfig * cfg; - char tmpbuf[255]; + struct kconfig * cfg; - for(cfg = config;cfg != NULL; cfg = cfg->next) + for ( cfg = scfg; cfg != NULL; cfg = cfg->next ) { - if( cfg->cond == NULL ) - { - continue; - } + struct condition * cond; - for(cond = cfg->cond; cond != NULL; cond = cond->next) + for ( cond = cfg->cond; cond != NULL; cond = cond->next ) { - if( cond->op != op_kvariable ) - continue; - - if( cond->variable.cfg->tok != tok_choice ) - continue; - - /* - * Look ahead for what we are comparing this to. There should - * be one operator in between. - */ - cond2 = cond->next->next; - strcpy(tmpbuf, cond->variable.cfg->label); - - if( strcmp(cond2->variable.str, "y") == 0 ) + if ( cond->op == op_variable ) { - cond->variable.cfg = cond->variable.cfg->choice_label; - cond2->variable.str = strdup(tmpbuf); + /* Here's where it gets DISGUSTING. */ + struct kconfig * cfg1; + + for ( cfg1 = scfg; cfg1 != NULL; cfg1 = cfg1->next ) + { + if ( cfg1->token == token_bool + || cfg1->token == token_choice_item + || cfg1->token == token_dep_tristate + || cfg1->token == token_hex + || cfg1->token == token_int + || cfg1->token == token_string + || cfg1->token == token_tristate ) + { + if ( strcmp( cond->str, cfg1->optionname ) == 0 ) + { + cond->op = op_kvariable; + cond->str = NULL; + cond->cfg = cfg1; + break; + } + } + } } - else + +#if 0 + /* + * Maybe someday this will be useful, but right now it + * gives a lot of false positives on files like + * drivers/video/Config.in that are meant for more + * than one architecture. Turn it on if you want to play + * with it though; it does work. -- mec + */ + if ( cond->op == op_variable ) { - fprintf(stderr,"Ooops\n"); - exit(0); + if ( strcmp( cond->str, "ARCH" ) != 0 + && strcmp( cond->str, "CONSTANT_Y" ) != 0 + && strcmp( cond->str, "CONSTANT_M" ) != 0 + && strcmp( cond->str, "CONSTANT_N" ) != 0 ) + { + fprintf( stderr, "warning: $%s used but not defined\n", + cond->str ); + } } +#endif } - } } + + /* - * Walk the stack of conditions, and clone all of them with "&&" operators - * gluing them together. The conditions from each level of the stack - * are wrapped in parenthesis so as to guarantee that the results - * are logically correct. + * Make a new condition chain by joining the current condition stack with + * the "&&" operator for glue. */ -struct condition * get_token_cond(struct condition ** cond, int depth) +struct condition * join_condition_stack( struct condition * conditions [], + int depth ) { - int i; - struct condition * newcond; - struct condition * tail; - struct condition * new; - struct condition * ocond; - struct kconfig * cfg; - - newcond = tail = NULL; - for(i=0; iop = op_lparen; - if( tail == NULL ) + struct condition * cond; + struct condition * cnew; + + /* add a '(' */ + cnew = malloc( sizeof(*cnew) ); + memset( cnew, 0, sizeof(*cnew) ); + cnew->op = op_lparen; + if ( cond_last == NULL ) + { cond_list = cond_last = cnew; } + else + { cond_last->next = cnew; cond_last = cnew; } + + /* duplicate the chain */ + for ( cond = conditions [i]; cond != NULL; cond = cond->next ) { - newcond = tail = new; + cnew = malloc( sizeof(*cnew) ); + cnew->next = NULL; + cnew->op = cond->op; + cnew->str = cond->str ? strdup( cond->str ) : NULL; + cnew->cfg = cond->cfg; + cond_last->next = cnew; + cond_last = cnew; } - else + + /* add a ')' */ + cnew = malloc( sizeof(*cnew) ); + memset( cnew, 0, sizeof(*cnew) ); + cnew->op = op_rparen; + cond_last->next = cnew; + cond_last = cnew; + + /* if i have another condition, add an '&&' operator */ + if ( i < depth - 1 ) { - tail->next = new; - tail = new; + cnew = malloc( sizeof(*cnew) ); + memset( cnew, 0, sizeof(*cnew) ); + cnew->op = op_and; + cond_last->next = cnew; + cond_last = cnew; } + } - /* - * Now duplicate the chain. - */ - ocond = *cond; - for(;ocond != NULL; ocond = ocond->next) + /* + * Remove duplicate conditions. + */ + { + struct condition *cond1, *cond1b, *cond1c, *cond1d, *cond1e, *cond1f; + + for ( cond1 = cond_list; cond1 != NULL; cond1 = cond1->next ) { - new = (struct condition *) malloc(sizeof(struct condition)); - memset(new, 0, sizeof(*new)); - new->op = ocond->op; - if( ocond->variable.str != NULL ) + if ( cond1->op == op_lparen ) { - if( ocond->op == op_variable ) + cond1b = cond1 ->next; if ( cond1b == NULL ) break; + cond1c = cond1b->next; if ( cond1c == NULL ) break; + cond1d = cond1c->next; if ( cond1d == NULL ) break; + cond1e = cond1d->next; if ( cond1e == NULL ) break; + cond1f = cond1e->next; if ( cond1f == NULL ) break; + + if ( cond1b->op == op_kvariable + && ( cond1c->op == op_eq || cond1c->op == op_neq ) + && cond1d->op == op_constant + && cond1e->op == op_rparen ) { - /* - * Search for structure to insert here. - */ - for(cfg = config;cfg != NULL; cfg = cfg->next) + struct condition *cond2, *cond2b, *cond2c, *cond2d, *cond2e, *cond2f; + + for ( cond2 = cond1f->next; cond2 != NULL; cond2 = cond2->next ) { - if( cfg->tok != tok_bool - && cfg->tok != tok_int - && cfg->tok != tok_hex - && cfg->tok != tok_string - && cfg->tok != tok_tristate - && cfg->tok != tok_choice - && cfg->tok != tok_dep_tristate) + if ( cond2->op == op_lparen ) { - continue; + cond2b = cond2 ->next; if ( cond2b == NULL ) break; + cond2c = cond2b->next; if ( cond2c == NULL ) break; + cond2d = cond2c->next; if ( cond2d == NULL ) break; + cond2e = cond2d->next; if ( cond2e == NULL ) break; + cond2f = cond2e->next; + + /* look for match */ + if ( cond2b->op == op_kvariable + && cond2b->cfg == cond1b->cfg + && cond2c->op == cond1c->op + && cond2d->op == op_constant + && strcmp( cond2d->str, cond1d->str ) == 0 + && cond2e->op == op_rparen ) + { + /* one of these must be followed by && */ + if ( cond1f->op == op_and + || ( cond2f != NULL && cond2f->op == op_and ) ) + { + /* nuke the first duplicate */ + cond1 ->op = op_nuked; + cond1b->op = op_nuked; + cond1c->op = op_nuked; + cond1d->op = op_nuked; + cond1e->op = op_nuked; + if ( cond1f->op == op_and ) + cond1f->op = op_nuked; + else + cond2f->op = op_nuked; + } + } } - if( strcmp(cfg->optionname, ocond->variable.str) == 0) - { - new->variable.cfg = cfg; - new->op = op_kvariable; - break; - } - } - if( cfg == NULL ) - { - new->variable.str = strdup(ocond->variable.str); } } - else - { - new->variable.str = strdup(ocond->variable.str); - } } - tail->next = new; - tail = new; } - - /* - * Next insert the left parenthesis - */ - new = (struct condition *) malloc(sizeof(struct condition)); - memset(new, 0, sizeof(*new)); - new->op = op_rparen; - tail->next = new; - tail = new; - - /* - * Insert an and operator, if we have another condition. - */ - if( i < depth - 1 ) - { - new = (struct condition *) malloc(sizeof(struct condition)); - memset(new, 0, sizeof(*new)); - new->op = op_and; - tail->next = new; - tail = new; - } - } - return newcond; + return cond_list; } -/* - * Walk a single chain of conditions and clone it. These are assumed - * to be created/processed by get_token_cond in a previous pass. - */ -struct condition * get_token_cond_frag(struct condition * cond, - struct condition ** last) -{ - struct condition * newcond; - struct condition * tail; - struct condition * new; - struct condition * ocond; - - newcond = tail = NULL; - - /* - * Now duplicate the chain. - */ - for(ocond = cond;ocond != NULL; ocond = ocond->next) - { - new = (struct condition *) malloc(sizeof(struct condition)); - memset(new, 0, sizeof(*new)); - new->op = ocond->op; - new->variable.cfg = ocond->variable.cfg; - if( tail == NULL ) - { - newcond = tail = new; - } - else - { - tail->next = new; - tail = new; - } - } - new = (struct condition *) malloc(sizeof(struct condition)); - memset(new, 0, sizeof(*new)); - new->op = op_and; - tail->next = new; - tail = new; - - *last = tail; - return newcond; -} /* - * Walk through the if conditionals and maintain a chain. + * This is the main transformation function. */ -void fix_conditionals(struct kconfig * scfg) +void fix_conditionals( struct kconfig * scfg ) { - int depth = 0; - int i; - struct kconfig * cfg; - struct kconfig * cfg1; - struct condition * conditions[25]; - struct condition * cnd; - struct condition * cnd1; - struct condition * cnd2; - struct condition * cnd3; - struct condition * newcond; - struct condition * last; - - /* - * Start by walking the chain. Every time we see an ifdef, push - * the condition chain on the stack. When we see an "else", we invert - * the condition at the top of the stack, and when we see an "endif" - * we free all of the memory for the condition at the top of the stack - * and remove the condition from the top of the stack. - * - * For any other type of token (i.e. a bool), we clone a new condition chain - * by anding together all of the conditions that are currently stored on - * the stack. In this way, we have a correct representation of whatever - * conditions govern the usage of each option. - */ - memset(conditions, 0, sizeof(conditions)); - for(cfg=scfg;cfg != NULL; cfg = cfg->next) + struct kconfig * cfg; + + /* + * Transform op_variable to op_kvariable. + */ + transform_to_kvariable( scfg ); + + /* + * Transform conditions that use variables from "choice" statements. + * Choice values appear to the user as a collection of booleans, and the + * script can test the individual booleans. But internally, all I have is + * the N-way value of an unnamed temporary for the whole statement. So I + * have to tranform '"$CONFIG_M386" != "y"' + * into '"$tmpvar_N" != "CONFIG_M386"'. + */ + for ( cfg = scfg; cfg != NULL; cfg = cfg->next ) { - switch(cfg->tok) - { - case tok_if: - /* - * Push this condition on the stack, and nuke the token - * representing the ifdef, since we no longer need it. - */ - conditions[depth] = cfg->cond; - depth++; - cfg->tok = tok_nop; - cfg->cond = NULL; - break; - case tok_else: - /* - * For an else, we just invert the condition at the top of - * the stack. This is done in place with no reallocation - * of memory taking place. - */ - invert_condition(conditions[depth-1]); - cfg->tok = tok_nop; - break; - case tok_fi: - depth--; - free_condition(conditions[depth]); - conditions[depth] = NULL; - cfg->tok = tok_nop; - break; - case tok_comment: - case tok_define: - case tok_menuoption: - case tok_bool: - case tok_tristate: - case tok_int: - case tok_hex: - case tok_string: - case tok_choice: - /* - * We need to duplicate the chain of conditions and attach them to - * this token. - */ - cfg->cond = get_token_cond(&conditions[0], depth); - break; - case tok_dep_tristate: - /* - * Same as tok_tristate et al except we have a temporary - * conditional. (Sort of a hybrid tok_if, tok_tristate, tok_fi - * option) - */ - conditions[depth] = cfg->cond; - depth++; - cfg->cond = get_token_cond(&conditions[0], depth); - depth--; - free_condition(conditions[depth]); - conditions[depth] = NULL; - default: - break; - } - } + struct condition * cond; - /* - * Fix any conditions involving the "choice" operator. - */ - fix_choice_cond(); - - /* - * Walk through and see if there are multiple options that control the - * same kvariable. If there are we need to treat them a little bit - * special. - */ - for(cfg=scfg;cfg != NULL; cfg = cfg->next) - { - switch(cfg->tok) + for ( cond = cfg->cond; cond != NULL; cond = cond->next ) { - case tok_bool: - case tok_tristate: - case tok_dep_tristate: - case tok_int: - case tok_hex: - case tok_string: - for(cfg1=cfg;cfg1 != NULL; cfg1 = cfg1->next) + if ( cond->op == op_kvariable && cond->cfg->token == token_choice_item ) { - switch(cfg1->tok) + /* + * Look two more tokens down for the comparison token. + * It has to be "y" for this trick to work. + * + * If you get this error, don't even think about relaxing the + * strcmp test. You will produce incorrect TK code. Instead, + * look for the place in your Config.in script where you are + * comparing a 'choice' variable to a value other than 'y', + * and rewrite the comparison to be '= "y"' or '!= "y"'. + */ + struct condition * cond2 = cond->next->next; + const char * label; + + if ( strcmp( cond2->str, "y" ) != 0 ) { - case tok_define: - case tok_bool: - case tok_tristate: - case tok_dep_tristate: - case tok_int: - case tok_hex: - case tok_string: - if( strcmp(cfg->optionname, cfg1->optionname) == 0) - { - cfg->flags |= CFG_DUP; - cfg1->flags |= CFG_DUP; - } - break; - default: - break; + fprintf( stderr, "tkparse choked in fix_choice_cond\n" ); + exit( 1 ); } + + label = cond->cfg->label; + cond->cfg = cond->cfg->cfg_parent; + cond2->str = strdup( label ); } - break; - default: - break; } } - /* - * Now go through the list, and every time we see a kvariable, check - * to see whether it also has some dependencies. If so, then - * append it to our list. The reason we do this is that we might have - * option CONFIG_FOO which is only used if CONFIG_BAR is set. It may - * turn out that in config.in that the default value for CONFIG_BAR is - * set to "y", but that CONFIG_BAR is not enabled because CONFIG_XYZZY - * is not set. The current condition chain does not reflect this, but - * we can fix this by searching for the tokens that this option depends - * upon and cloning the conditions and merging them with the list. - */ - for(cfg=scfg;cfg != NULL; cfg = cfg->next) + /* + * Walk the statement list, maintaining a stack of current conditions. + * token_if push its condition onto the stack. + * token_else invert the condition on the top of the stack. + * token_endif pop the stack. + * + * For a simple statement, create a condition chain by joining together + * all of the conditions on the stack. + */ { - /* - * Search for a token that has a condition list. - */ - if(cfg->cond == NULL) continue; - for(cnd = cfg->cond; cnd; cnd=cnd->next) - { - /* - * Now search the condition list for a known configuration variable - * that has conditions of its own. - */ - if(cnd->op != op_kvariable) continue; - if(cnd->variable.cfg->cond == NULL) continue; - - if(cnd->variable.cfg->flags & CFG_DUP) continue; - /* - * OK, we have some conditions to append to cfg. Make a clone - * of the conditions, - */ - newcond = get_token_cond_frag(cnd->variable.cfg->cond, &last); - - /* - * Finally, we splice it into our list. - */ - last->next = cfg->cond; - cfg->cond = newcond; - - } - } + struct condition * cond_stack [32]; + int depth = 0; - /* - * There is a strong possibility that we have duplicate conditions - * in here. It would make the script more efficient and readable to - * remove these. Here is where we assume here that there are no - * parenthesis in the input script. - */ - for(cfg=scfg;cfg != NULL; cfg = cfg->next) - { - /* - * Search for configuration options that have conditions. - */ - if(cfg->cond == NULL) continue; - for(cnd = cfg->cond; cnd; cnd=cnd->next) + for ( cfg = scfg; cfg != NULL; cfg = cfg->next ) { - /* - * Search for a left parenthesis. - */ - if(cnd->op != op_lparen) continue; - for(cnd1 = cnd->next; cnd1; cnd1=cnd1->next) + switch ( cfg->token ) { - /* - * Search after the previous left parenthesis, and try - * and find a second left parenthesis. - */ - if(cnd1->op != op_lparen) continue; - - /* - * Now compare the next 5 tokens to see if they are - * identical. We are looking for two chains that - * are like: '(' $VARIABLE operator constant ')'. - */ - cnd2 = cnd; - cnd3 = cnd1; - for(i=0; i<5; i++, cnd2=cnd2->next, cnd3=cnd3->next) - { - if(!cnd2 || !cnd3) break; - if(cnd2->op != cnd3->op) break; - if(i == 1 && (cnd2->op != op_kvariable - || cnd2->variable.cfg != cnd3->variable.cfg) ) break; - if(i==2 && cnd2->op != op_eq && cnd2->op != op_neq) break; - if(i == 3 && cnd2->op != op_constant && - strcmp(cnd2->variable.str, cnd3->variable.str) != 0) - break; - if(i==4 && cnd2->op != op_rparen) break; - } - /* - * If these match, and there is an and gluing these together, - * then we can nuke the second one. - */ - if(i==5 && ((cnd3 && cnd3->op == op_and) - ||(cnd2 && cnd2->op == op_and))) + default: + break; + + case token_if: + cond_stack [depth++] = cfg->cond; + cfg->cond = NULL; + break; + + case token_else: { - /* - * We have a duplicate. Nuke 5 ops. - */ - cnd3 = cnd1; - for(i=0; i<5; i++, cnd3=cnd3->next) + /* + * Invert the condition chain. + * + * Be careful to transfrom op_or to op_and1, not op_and. + * I will need this later in the code that removes + * duplicate conditions. + */ + struct condition * cond; + + for ( cond = cond_stack [depth-1]; + cond != NULL; + cond = cond->next ) { - cnd3->op = op_nuked; + switch( cond->op ) + { + default: break; + case op_and: cond->op = op_or; break; + case op_or: cond->op = op_and1; break; + case op_neq: cond->op = op_eq; break; + case op_eq: cond->op = op_neq; break; + } } - /* - * Nuke the and that glues the conditions together. - */ - if(cnd3 && cnd3->op == op_and) cnd3->op = op_nuked; - else if(cnd2 && cnd2->op == op_and) cnd2->op = op_nuked; } + break; + + case token_fi: + --depth; + break; + + case token_bool: + case token_choice_item: + case token_comment: + case token_define_bool: + case token_hex: + case token_int: + case token_mainmenu_option: + case token_string: + case token_tristate: + cfg->cond = join_condition_stack( cond_stack, depth ); + break; + + case token_dep_tristate: + /* + * Same as the other simple statements, plus an additional + * condition for the dependency. + */ + cond_stack [depth] = cfg->cond; + cfg->cond = join_condition_stack( cond_stack, depth+1 ); + break; } } } -- cgit v1.2.3