/* * Get a quoted or unquoted string. It is recognized by the first * non-white character. '"' and '"' are not allowed inside the string. */ static const char * get_qnqstring( const char * pnt, char ** label ) { char quote_char; while ( *pnt == ' ' || *pnt == '\t' ) pnt++; if ( *pnt == '\0' ) return pnt; quote_char = *pnt; if ( quote_char == '"' || quote_char == '\'' ) return get_qstring( pnt, label ); else return get_string( pnt, label ); }
/* * Tokenize one line. */ static void tokenize_line( const char * pnt ) { static struct kconfig * last_menuoption = NULL; enum e_token token; struct kconfig * cfg; struct dependency ** dep_ptr; char * buffer = malloc( 64 ); /* skip white space */ while ( *pnt == ' ' || *pnt == '\t' ) pnt++; /* * categorize the next token */ #define match_token(t, s) \ if (strncmp(pnt, s, strlen(s)) == 0) { token = t; pnt += strlen(s); break; } token = token_UNKNOWN; switch ( *pnt ) { default: break; case '#': case '\0': return; case 'b': match_token( token_bool, "bool" ); break; case 'c': match_token( token_choice_header, "choice" ); match_token( token_comment, "comment" ); break; case 'd': match_token( token_define_bool, "define_bool" ); match_token( token_define_hex, "define_hex" ); match_token( token_define_int, "define_int" ); match_token( token_define_string, "define_string" ); match_token( token_define_tristate, "define_tristate" ); match_token( token_dep_bool, "dep_bool" ); match_token( token_dep_mbool, "dep_mbool" ); match_token( token_dep_tristate, "dep_tristate" ); break; case 'e': match_token( token_else, "else" ); match_token( token_endmenu, "endmenu" ); break; case 'f': match_token( token_fi, "fi" ); break; case 'h': match_token( token_hex, "hex" ); break; case 'i': match_token( token_if, "if" ); match_token( token_int, "int" ); break; case 'm': match_token( token_mainmenu_name, "mainmenu_name" ); match_token( token_mainmenu_option, "mainmenu_option" ); break; case 's': match_token( token_source, "source" ); match_token( token_string, "string" ); break; case 't': match_token( token_then, "then" ); match_token( token_tristate, "tristate" ); break; case 'u': match_token( token_unset, "unset" ); break; } #undef match_token if ( token == token_source ) { while ( *pnt == ' ' || *pnt == '\t' ) pnt++; do_source( pnt ); return; } if ( token == token_then ) { if ( config_last != NULL && config_last->token == token_if ) return; syntax_error( "bogus 'then'" ); } #if 0 if ( token == token_unset ) { fprintf( stderr, "Ignoring 'unset' command\n" ); return; } #endif if ( token == token_UNKNOWN ) syntax_error( "unknown command" ); /* * Allocate an item. */ cfg = malloc( sizeof(*cfg) ); memset( cfg, 0, sizeof(*cfg) ); if ( config_last == NULL ) { config_last = config_list = cfg; } else { config_last->next = cfg; config_last = cfg; } /* * Tokenize the arguments. */ while ( *pnt == ' ' || *pnt == '\t' ) pnt++; cfg->token = token; switch ( token ) { default: syntax_error( "unknown token" ); case token_bool: case token_tristate: pnt = get_qstring ( pnt, &cfg->label ); pnt = get_string ( pnt, &buffer ); cfg->nameindex = get_varnum( buffer ); break; case token_choice_header: { static int choose_number = 0; char * choice_list; pnt = get_qstring ( pnt, &cfg->label ); pnt = get_qstring ( pnt, &choice_list ); pnt = get_string ( pnt, &cfg->value ); cfg->nameindex = -(choose_number++); tokenize_choices( cfg, choice_list ); free( choice_list ); } break; case token_comment: pnt = get_qstring(pnt, &cfg->label); if ( last_menuoption != NULL ) { pnt = get_qstring(pnt, &cfg->label); if (cfg->label == NULL) syntax_error( "missing comment text" ); last_menuoption->label = cfg->label; last_menuoption = NULL; } break; case token_define_bool: case token_define_tristate: pnt = get_string( pnt, &buffer ); cfg->nameindex = get_varnum( buffer ); while ( *pnt == ' ' || *pnt == '\t' ) pnt++; if ( ( pnt[0] == 'Y' || pnt[0] == 'M' || pnt[0] == 'N' || pnt[0] == 'y' || pnt[0] == 'm' || pnt[0] == 'n' ) && ( pnt[1] == '\0' || pnt[1] == ' ' || pnt[1] == '\t' ) ) { if ( *pnt == 'n' || *pnt == 'N' ) cfg->value = strdup( "CONSTANT_N" ); else if ( *pnt == 'y' || *pnt == 'Y' ) cfg->value = strdup( "CONSTANT_Y" ); else if ( *pnt == 'm' || *pnt == 'M' ) cfg->value = strdup( "CONSTANT_M" ); } else if ( *pnt == '$' ) { pnt++; pnt = get_string( pnt, &cfg->value ); } else { syntax_error( "unknown define_bool value" ); } get_varnum( cfg->value ); break; case token_define_hex: case token_define_int: pnt = get_string( pnt, &buffer ); cfg->nameindex = get_varnum( buffer ); pnt = get_string( pnt, &cfg->value ); break; case token_define_string: pnt = get_string( pnt, &buffer ); cfg->nameindex = get_varnum( buffer ); pnt = get_qnqstring( pnt, &cfg->value ); if (cfg->value == NULL) syntax_error( "missing value" ); break; case token_dep_bool: case token_dep_mbool: case token_dep_tristate: pnt = get_qstring ( pnt, &cfg->label ); pnt = get_string ( pnt, &buffer ); cfg->nameindex = get_varnum( buffer ); while ( *pnt == ' ' || *pnt == '\t' ) pnt++; dep_ptr = &(cfg->depend); do { *dep_ptr = (struct dependency *) malloc( sizeof( struct dependency ) ); (*dep_ptr)->next = NULL; if ( ( pnt[0] == 'Y' || pnt[0] == 'M' || pnt[0] == 'N' || pnt[0] == 'y' || pnt[0] == 'm' || pnt[0] == 'n' ) && ( pnt[1] == '\0' || pnt[1] == ' ' || pnt[1] == '\t' ) ) { /* dep_tristate 'foo' CONFIG_FOO m */ if ( pnt[0] == 'Y' || pnt[0] == 'y' ) (*dep_ptr)->name = strdup( "CONSTANT_Y" ); else if ( pnt[0] == 'N' || pnt[0] == 'n' ) (*dep_ptr)->name = strdup( "CONSTANT_N" ); else (*dep_ptr)->name = strdup( "CONSTANT_M" ); pnt++; get_varnum( (*dep_ptr)->name ); } else if ( *pnt == '$' ) { pnt++; pnt = get_string( pnt, &(*dep_ptr)->name ); get_varnum( (*dep_ptr)->name ); } else { syntax_error( "can't handle dep_bool/dep_mbool/dep_tristate condition" ); } dep_ptr = &(*dep_ptr)->next; while ( *pnt == ' ' || *pnt == '\t' ) pnt++; } while ( *pnt ); /* * Create a conditional for this object's dependencies. */ { char fake_if [1024]; struct dependency * dep; struct condition ** cond_ptr; int first = 1; cond_ptr = &(cfg->cond); for ( dep = cfg->depend; dep; dep = dep->next ) { if ( token == token_dep_tristate && ! strcmp( dep->name, "CONSTANT_M" ) ) { continue; } if ( first ) { first = 0; } else { *cond_ptr = malloc( sizeof(struct condition) ); memset( *cond_ptr, 0, sizeof(struct condition) ); (*cond_ptr)->op = op_and; cond_ptr = &(*cond_ptr)->next; } *cond_ptr = malloc( sizeof(struct condition) ); memset( *cond_ptr, 0, sizeof(struct condition) ); (*cond_ptr)->op = op_lparen; if ( token == token_dep_bool ) sprintf( fake_if, "[ \"$%s\" = \"y\" -o \"$%s\" = \"\" ]; then", dep->name, dep->name ); else sprintf( fake_if, "[ \"$%s\" = \"y\" -o \"$%s\" = \"m\" -o \"$%s\" = \"\" ]; then", dep->name, dep->name, dep->name ); (*cond_ptr)->next = tokenize_if( fake_if ); while ( *cond_ptr ) cond_ptr = &(*cond_ptr)->next; *cond_ptr = malloc( sizeof(struct condition) ); memset( *cond_ptr, 0, sizeof(struct condition) ); (*cond_ptr)->op = op_rparen; cond_ptr = &(*cond_ptr)->next; } } break; case token_else: case token_endmenu: case token_fi: break; case token_hex: case token_int: pnt = get_qstring ( pnt, &cfg->label ); pnt = get_string ( pnt, &buffer ); cfg->nameindex = get_varnum( buffer ); pnt = get_string ( pnt, &cfg->value ); break; case token_string: pnt = get_qstring ( pnt, &cfg->label ); pnt = get_string ( pnt, &buffer ); cfg->nameindex = get_varnum( buffer ); pnt = get_qnqstring ( pnt, &cfg->value ); if (cfg->value == NULL) syntax_error( "missing initial value" ); break; case token_if: cfg->cond = tokenize_if( pnt ); break; case token_mainmenu_name: pnt = get_qstring( pnt, &cfg->label ); break; case token_mainmenu_option: if ( strncmp( pnt, "next_comment", 12 ) == 0 ) last_menuoption = cfg; else pnt = get_qstring( pnt, &cfg->label ); break; case token_unset: pnt = get_string( pnt, &buffer ); cfg->nameindex = get_varnum( buffer ); while ( *pnt == ' ' || *pnt == '\t' ) pnt++; while (*pnt) { cfg->next = (struct kconfig *) malloc( sizeof(struct kconfig) ); memset( cfg->next, 0, sizeof(struct kconfig) ); cfg = cfg->next; cfg->token = token_unset; pnt = get_string( pnt, &buffer ); cfg->nameindex = get_varnum( buffer ); while ( *pnt == ' ' || *pnt == '\t' ) pnt++; } break; } return; }
/* * Top level parse function. Input pointer is one complete line from config.in * and the result is that we create a token that describes this line * and insert it into our linked list. */ void parse(char * pnt) { enum token tok; struct kconfig * kcfg; char tmpbuf[24],fake_if[1024]; /* * Ignore comments and leading whitespace. */ pnt = skip_whitespace(pnt); while( *pnt && (*pnt == ' ' || *pnt == '\t')) pnt++; if(! *pnt ) return; if( *pnt == '#' ) return; /* * Now categorize the next token. */ tok = tok_unknown; if (strncmp(pnt, "mainmenu_name", 13) == 0) { tok = tok_menuname; pnt += 13; } else if (strncmp(pnt, "source", 6) == 0) { pnt += 7; pnt = skip_whitespace(pnt); do_source(pnt); return; } else if (strncmp(pnt, "mainmenu_option", 15) == 0) { menus_seen++; tok = tok_menuoption; pnt += 15; } else if (strncmp(pnt, "$MAKE ", 6) == 0) { tok = tok_make; } else if (strncmp(pnt, "comment", 7) == 0) { tok = tok_comment; pnt += 7; } else if (strncmp(pnt, "choice", 6) == 0) { tok = tok_choose; pnt += 6; } else if (strncmp(pnt, "define_bool", 11) == 0) { tok = tok_define; pnt += 11; } else if (strncmp(pnt, "bool", 4) == 0) { tok = tok_bool; pnt += 4; } else if (strncmp(pnt, "tristate", 8) == 0) { tok = tok_tristate; pnt += 8; } else if (strncmp(pnt, "dep_tristate", 12) == 0) { tok = tok_dep_tristate; pnt += 12; } else if (strncmp(pnt, "int", 3) == 0) { tok = tok_int; pnt += 3; } else if (strncmp(pnt, "hex", 3) == 0) { tok = tok_hex; pnt += 3; } else if (strncmp(pnt, "if", 2) == 0) { tok = tok_if; pnt += 2; } else if (strncmp(pnt, "else", 4) == 0) { tok = tok_else; pnt += 4; } else if (strncmp(pnt, "fi", 2) == 0) { tok = tok_fi; pnt += 2; } else if (strncmp(pnt, "endmenu", 7) == 0) { tok = tok_endmenu; pnt += 7; } if( tok == tok_unknown) { if( clast != NULL && clast->tok == tok_if && strcmp(pnt,"then") == 0) return; if( current_file != NULL ) fprintf(stderr, "unknown command=%s(%s %d)\n", pnt, current_file, lineno); else fprintf(stderr, "unknown command=%s(%d)\n", pnt,lineno); return; } /* * Allocate memory for this item, and attach it to the end of the linked * list. */ kcfg = (struct kconfig *) malloc(sizeof(struct kconfig)); memset(kcfg, 0, sizeof(struct kconfig)); kcfg->tok = tok; if( clast != NULL ) { clast->next = kcfg; clast = kcfg; } else { clast = config = kcfg; } pnt = skip_whitespace(pnt); /* * Now parse the remaining parts of the option, and attach the results * to the structure. */ switch (tok) { case tok_choose: pnt = get_qstring(pnt, &kcfg->label); pnt = get_qstring(pnt, &kcfg->optionname); pnt = get_string(pnt, &kcfg->value); /* * Now we need to break apart the individual options into their * own configuration structures. */ parse_choices(kcfg, kcfg->optionname); free(kcfg->optionname); sprintf(tmpbuf, "tmpvar_%d", choose_number++); kcfg->optionname = strdup(tmpbuf); break; case tok_define: pnt = get_string(pnt, &kcfg->optionname); if(*pnt == 'y' || *pnt == 'Y' ) kcfg->value = "1"; if(*pnt == 'n' || *pnt == 'N' ) kcfg->value = "0"; if(*pnt == 'm' || *pnt == 'M' ) kcfg->value = "2"; break; case tok_menuname: pnt = get_qstring(pnt, &kcfg->label); break; case tok_bool: case tok_tristate: pnt = get_qstring(pnt, &kcfg->label); pnt = get_string(pnt, &kcfg->optionname); break; case tok_int: case tok_hex: pnt = get_qstring(pnt, &kcfg->label); pnt = get_string(pnt, &kcfg->optionname); pnt = get_string(pnt, &kcfg->value); break; case tok_dep_tristate: pnt = get_qstring(pnt, &kcfg->label); pnt = get_string(pnt, &kcfg->optionname); pnt = skip_whitespace(pnt); if( *pnt == '$') pnt++; pnt = get_string(pnt, &kcfg->depend.str); /* * Create a conditional for this object's dependency. * * We can't use "!= n" because this is internally converted to "!= 0" * and if UMSDOS depends on MSDOS which depends on FAT, then when FAT * is disabled MSDOS has 16 added to its value, making UMSDOS fully * available. Whew. * * This is more of a hack than a fix. Nested "if" conditionals are * probably affected too - that +/- 16 affects things in too many * places. But this should do for now. */ sprintf(fake_if,"[ \"$%s\" = \"y\" -o \"$%s\" = \"m\" ]; then", kcfg->depend.str,kcfg->depend.str); kcfg->cond = parse_if(fake_if); if(kcfg->cond == NULL ) { exit(1); } break; case tok_comment: pnt = get_qstring(pnt, &kcfg->label); if( koption != NULL ) { pnt = get_qstring(pnt, &kcfg->label); koption->label = kcfg->label; koption = NULL; } break; case tok_menuoption: if( strncmp(pnt, "next_comment", 12) == 0) { koption = kcfg; } else { pnt = get_qstring(pnt, &kcfg->label); } break; case tok_make: kcfg->value=strdup(pnt); break; case tok_else: case tok_fi: case tok_endmenu: break; case tok_if: /* * Conditionals are different. For the first level parse, only * tok_if and tok_dep_tristate items have a ->cond chain attached. */ kcfg->cond = parse_if(pnt); if(kcfg->cond == NULL ) { exit(1); } break; default: exit(0); } return; }