/* parser config.in * $Id: tkcond.c 2281 2010-10-15 14:21:13Z smasson $ * * This software is governed by the CeCILL license * See IOIPSL/IOIPSL_License_CeCILL.txt * * 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. * * The conditions are assumed to be one of the following formats * * simple_condition:= "$VARIABLE" == y/n/m * simple_condition:= "$VARIABLE != y/n/m * * simple_condition -a simple_condition * * If the input condition contains '(' or ')' it would screw us up, but for now * this is not a problem. */ #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. */ void fix_choice_cond() { struct condition * cond; struct condition * cond2; struct kconfig * cfg; char tmpbuf[10]; for(cfg = config;cfg != NULL; cfg = cfg->next) { if( cfg->cond == NULL ) { continue; } 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 ) { cond->variable.cfg = cond->variable.cfg->choice_label; cond2->variable.str = strdup(tmpbuf); } else { fprintf(stderr,"Ooops\n"); exit(0); } } } } /* * 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. */ struct condition * get_token_cond(struct condition ** cond, 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 ) { newcond = tail = new; } else { tail->next = new; tail = new; } /* * Now duplicate the chain. */ ocond = *cond; for(;ocond != NULL; ocond = ocond->next) { new = (struct condition *) malloc(sizeof(struct condition)); memset(new, 0, sizeof(*new)); new->op = ocond->op; if( ocond->variable.str != NULL ) { if( ocond->op == op_variable ) { /* * Search for structure to insert here. */ for(cfg = config;cfg != NULL; cfg = cfg->next) { if( cfg->tok != tok_bool && cfg->tok != tok_int && cfg->tok != tok_hex && cfg->tok != tok_tristate && cfg->tok != tok_choice && cfg->tok != tok_dep_tristate) { continue; } 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; } /* * 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. */ 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) { 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_choice: case tok_make: /* * 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; } } /* * 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) { case tok_bool: case tok_tristate: case tok_dep_tristate: case tok_int: case tok_hex: for(cfg1=cfg;cfg1 != NULL; cfg1 = cfg1->next) { switch(cfg1->tok) { case tok_define: case tok_bool: case tok_tristate: case tok_dep_tristate: case tok_int: case tok_hex: if( strcmp(cfg->optionname, cfg1->optionname) == 0) { cfg->flags |= CFG_DUP; cfg1->flags |= CFG_DUP; } break; default: break; } } 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) { /* * 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; } } /* * 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) { /* * Search for a left parenthesis. */ if(cnd->op != op_lparen) continue; for(cnd1 = cnd->next; cnd1; cnd1=cnd1->next) { /* * 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))) { /* * We have a duplicate. Nuke 5 ops. */ cnd3 = cnd1; for(i=0; i<5; i++, cnd3=cnd3->next) { cnd3->op = op_nuked; } /* * 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; } } } } }