| /* |
| * 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, <mec@shout.net> |
| * - 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 <ankry@mif.pg.gda.pl> |
| * - 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 <mec@shout.net> if |
| * you are interested in working on the replacement. |
| */ |
| |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <string.h> |
| |
| #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 = <not current>. |
| */ |
| 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 |