/* * tkcond.c * * Eric Youngdale was the original author of xconfig. * Michael Elizabeth Chastain (mec@shout.net) is the current maintainer. * * 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. * * 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. * * 07 July 1999, Andrzej M. Krzysztofowicz * - kvariables removed; all variables are stored in a single table now * - some elimination of options non-valid for current architecture * implemented. * - negation (!) eliminated from conditions * * 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 "tkparse.h" /* * Mark variables which are defined anywhere. */ static void mark_variables( struct kconfig * scfg ) { struct kconfig * cfg; int i; for ( i = 1; i <= max_varnum; i++ ) vartable[i].defined = 0; for ( cfg = scfg; cfg != NULL; cfg = cfg->next ) { if ( cfg->token == token_bool || cfg->token == token_choice_item || cfg->token == token_define_bool || cfg->token == token_define_hex || cfg->token == token_define_int || cfg->token == token_define_string || cfg->token == token_define_tristate || cfg->token == token_dep_bool || cfg->token == token_dep_mbool || cfg->token == token_dep_tristate || cfg->token == token_hex || cfg->token == token_int || cfg->token == token_string || cfg->token == token_tristate || cfg->token == token_unset ) { if ( cfg->nameindex > 0 ) /* paranoid */ { vartable[cfg->nameindex].defined = 1; } } } } static void free_cond( struct condition *cond ) { struct condition *tmp, *tmp1; for ( tmp = cond; tmp; tmp = tmp1 ) { tmp1 = tmp->next; free( (void*)tmp ); } } /* * Remove the bang operator from a condition to avoid priority problems. * "!" has different priorities as "test" command argument and in * a tk script. */ static struct condition * remove_bang( struct condition * condition ) { struct condition * conda, * condb, * prev = NULL; for ( conda = condition; conda; conda = conda->next ) { if ( conda->op == op_bang && conda->next && ( condb = conda->next->next ) ) { if ( condb->op == op_eq || condb->op == op_neq ) { condb->op = (condb->op == op_eq) ? op_neq : op_eq; conda->op = op_nuked; if ( prev ) { prev->next = conda->next; } else { condition = conda->next; } conda->next = NULL; free_cond( conda ); conda = condb; } } prev = conda; } return condition; } /* * Make a new condition chain by joining the current condition stack with * the "&&" operator for glue. */ static struct condition * join_condition_stack( struct condition * conditions [], int depth ) { struct condition * cond_list; struct condition * cond_last; int i, is_first = 1; cond_list = cond_last = NULL; for ( i = 0; i < depth; i++ ) { if ( conditions[i]->op == op_false ) { struct condition * cnew; /* It is always false condition */ cnew = malloc( sizeof(*cnew) ); memset( cnew, 0, sizeof(*cnew) ); cnew->op = op_false; cond_list = cond_last = cnew; goto join_done; } } for ( i = 0; i < depth; i++ ) { struct condition * cond; struct condition * cnew; int add_paren; /* omit always true conditions */ if ( conditions[i]->op == op_true ) continue; /* if i have another condition, add an '&&' operator */ if ( !is_first ) { cnew = malloc( sizeof(*cnew) ); memset( cnew, 0, sizeof(*cnew) ); cnew->op = op_and; cond_last->next = cnew; cond_last = cnew; } if ( conditions[i]->op != op_lparen ) { /* add a '(' */ add_paren = 1; 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; } } else { add_paren = 0; } /* duplicate the chain */ for ( cond = conditions [i]; cond != NULL; cond = cond->next ) { cnew = malloc( sizeof(*cnew) ); cnew->next = NULL; cnew->op = cond->op; cnew->str = cond->str ? strdup( cond->str ) : NULL; cnew->nameindex = cond->nameindex; if ( cond_last == NULL ) { cond_list = cond_last = cnew; } else { cond_last->next = cnew; cond_last = cnew; } } if ( add_paren ) { /* add a ')' */ cnew = malloc( sizeof(*cnew) ); memset( cnew, 0, sizeof(*cnew) ); cnew->op = op_rparen; cond_last->next = cnew; cond_last = cnew; } is_first = 0; } /* * Remove duplicate conditions. */ { struct condition *cond1, *cond1b, *cond1c, *cond1d, *cond1e, *cond1f; for ( cond1 = cond_list; cond1 != NULL; cond1 = cond1->next ) { if ( cond1->op == op_lparen ) { 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_variable && ( cond1c->op == op_eq || cond1c->op == op_neq ) && cond1d->op == op_constant && cond1e->op == op_rparen ) { struct condition *cond2, *cond2b, *cond2c, *cond2d, *cond2e, *cond2f; for ( cond2 = cond1f->next; cond2 != NULL; cond2 = cond2->next ) { if ( cond2->op == op_lparen ) { 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_variable && cond2b->nameindex == cond1b->nameindex && 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; } } } } } } } } join_done: return cond_list; } static char * current_arch = NULL; /* * Eliminating conditions with ARCH = . */ static struct condition *eliminate_other_arch( struct condition *list ) { struct condition *cond1a = list, *cond1b = NULL, *cond1c = NULL, *cond1d = NULL; if ( current_arch == NULL ) current_arch = getenv( "ARCH" ); if ( current_arch == NULL ) { fprintf( stderr, "error: ARCH undefined\n" ); exit( 1 ); } if ( cond1a->op == op_variable && ! strcmp( vartable[cond1a->nameindex].name, "ARCH" ) ) { cond1b = cond1a->next; if ( cond1b == NULL ) goto done; cond1c = cond1b->next; if ( cond1c == NULL ) goto done; cond1d = cond1c->next; if ( cond1c->op == op_constant && cond1d == NULL ) { if ( (cond1b->op == op_eq && strcmp( cond1c->str, current_arch )) || (cond1b->op == op_neq && ! strcmp( cond1c->str, current_arch )) ) { /* This is for another architecture */ cond1a->op = op_false; cond1a->next = NULL; free_cond( cond1b ); return cond1a; } else if ( (cond1b->op == op_neq && strcmp( cond1c->str, current_arch )) || (cond1b->op == op_eq && ! strcmp( cond1c->str, current_arch )) ) { /* This is for current architecture */ cond1a->op = op_true; cond1a->next = NULL; free_cond( cond1b ); return cond1a; } } else if ( cond1c->op == op_constant && cond1d->op == op_or ) { if ( (cond1b->op == op_eq && strcmp( cond1c->str, current_arch )) || (cond1b->op == op_neq && ! strcmp( cond1c->str, current_arch )) ) { /* This is for another architecture */ cond1b = cond1d->next; cond1d->next = NULL; free_cond( cond1a ); return eliminate_other_arch( cond1b ); } else if ( (cond1b->op == op_neq && strcmp( cond1c->str, current_arch )) || (cond1b->op == op_eq && ! strcmp( cond1c->str, current_arch )) ) { /* This is for current architecture */ cond1a->op = op_true; cond1a->next = NULL; free_cond( cond1b ); return cond1a; } } else if ( cond1c->op == op_constant && cond1d->op == op_and ) { if ( (cond1b->op == op_eq && strcmp( cond1c->str, current_arch )) || (cond1b->op == op_neq && ! strcmp( cond1c->str, current_arch )) ) { /* This is for another architecture */ int l_par = 0; for ( cond1c = cond1d->next; cond1c; cond1c = cond1c->next ) { if ( cond1c->op == op_lparen ) l_par++; else if ( cond1c->op == op_rparen ) l_par--; else if ( cond1c->op == op_or && l_par == 0 ) /* Expression too complex - don't touch */ return cond1a; else if ( l_par < 0 ) { fprintf( stderr, "incorrect condition: programming error ?\n" ); exit( 1 ); } } cond1a->op = op_false; cond1a->next = NULL; free_cond( cond1b ); return cond1a; } else if ( (cond1b->op == op_neq && strcmp( cond1c->str, current_arch )) || (cond1b->op == op_eq && ! strcmp( cond1c->str, current_arch )) ) { /* This is for current architecture */ cond1b = cond1d->next; cond1d->next = NULL; free_cond( cond1a ); return eliminate_other_arch( cond1b ); } } } if ( cond1a->op == op_variable && ! vartable[cond1a->nameindex].defined ) { cond1b = cond1a->next; if ( cond1b == NULL ) goto done; cond1c = cond1b->next; if ( cond1c == NULL ) goto done; cond1d = cond1c->next; if ( cond1c->op == op_constant && ( cond1d == NULL || cond1d->op == op_and ) ) /*???*/ { if ( cond1b->op == op_eq && strcmp( cond1c->str, "" ) ) { cond1a->op = op_false; cond1a->next = NULL; free_cond( cond1b ); return cond1a; } } else if ( cond1c->op == op_constant && cond1d->op == op_or ) { if ( cond1b->op == op_eq && strcmp( cond1c->str, "" ) ) { cond1b = cond1d->next; cond1d->next = NULL; free_cond( cond1a ); return eliminate_other_arch( cond1b ); } } } done: return list; } /* * This is the main transformation function. */ void fix_conditionals( struct kconfig * scfg ) { struct kconfig * cfg; /* * Transform op_variable to op_kvariable. */ mark_variables( scfg ); /* * 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. */ { struct condition * cond_stack [32]; int depth = 0; struct kconfig * prev = NULL; for ( cfg = scfg; cfg != NULL; cfg = cfg->next ) { int good = 1; switch ( cfg->token ) { default: break; case token_if: cond_stack [depth++] = remove_bang( eliminate_other_arch( cfg->cond ) ); cfg->cond = NULL; break; case token_else: { /* * 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 ) { 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; case op_true: cond->op = op_false;break; case op_false:cond->op = op_true; break; } } } break; case token_fi: --depth; break; case token_bool: case token_choice_item: case token_choice_header: case token_comment: case token_define_bool: case token_define_hex: case token_define_int: case token_define_string: case token_define_tristate: case token_endmenu: case token_hex: case token_int: case token_mainmenu_option: case token_string: case token_tristate: case token_unset: cfg->cond = join_condition_stack( cond_stack, depth ); if ( cfg->cond && cfg->cond->op == op_false ) { good = 0; if ( prev ) prev->next = cfg->next; else scfg = cfg->next; } break; case token_dep_bool: case token_dep_mbool: case token_dep_tristate: /* * Same as the other simple statements, plus an additional * condition for the dependency. */ if ( cfg->cond ) { cond_stack [depth] = eliminate_other_arch( cfg->cond ); cfg->cond = join_condition_stack( cond_stack, depth+1 ); } else { cfg->cond = join_condition_stack( cond_stack, depth ); } if ( cfg->cond && cfg->cond->op == op_false ) { good = 0; if ( prev ) prev->next = cfg->next; else scfg = cfg->next; } break; } if ( good ) prev = cfg; } } } #if 0 void dump_condition( struct condition *list ) { struct condition *tmp; for ( tmp = list; tmp; tmp = tmp->next ) { switch (tmp->op) { default: break; case op_variable: printf( " %s", vartable[tmp->nameindex].name ); break; case op_constant: printf( " %s", tmp->str ); break; case op_eq: printf( " =" ); break; case op_bang: printf( " !" ); break; case op_neq: printf( " !=" ); break; case op_and: case op_and1: printf( " -a" ); break; case op_or: printf( " -o" ); break; case op_true: printf( " TRUE" ); break; case op_false: printf( " FALSE" ); break; case op_lparen: printf( " (" ); break; case op_rparen: printf( " )" ); break; } } printf( "\n" ); } #endif