4 * Eric Youngdale was the original author of xconfig.
5 * Michael Elizabeth Chastain (mec@shout.net) is the current maintainer.
7 * Parse a config.in file and translate it to a wish script.
8 * This task has three parts:
10 * tkparse.c tokenize the input
11 * tkcond.c transform 'if ...' statements
12 * tkgen.c generate output
16 * 7 January 1999, Michael Elizabeth Chastain, <mec@shout.net>
17 * - Teach dep_tristate about a few literals, such as:
18 * dep_tristate 'foo' CONFIG_FOO m
19 * Also have it print an error message and exit on some parse failures.
21 * 14 January 1999, Michael Elizabeth Chastain, <mec@shout.net>
22 * - Don't fclose stdin. Thanks to Tony Hoyle for nailing this one.
24 * 14 January 1999, Michael Elizabeth Chastain, <mec@shout.net>
25 * - Steam-clean this file. I tested this by generating kconfig.tk for
26 * every architecture and comparing it character-for-character against
27 * the output of the old tkparse.
29 * 23 January 1999, Michael Elizabeth Chastain, <mec@shout.net>
30 * - Remove bug-compatible code.
32 * 07 July 1999, Andrzej M. Krzysztofowicz, <ankry@mif.pg.gda.pl>
33 * - Submenus implemented,
34 * - plenty of option updating/displaying fixes,
35 * - dep_bool, define_hex, define_int, define_string, define_tristate and
37 * - dep_tristate fixed to support multiple dependencies,
38 * - handling of variables with an empty value implemented,
39 * - value checking for int and hex fields,
40 * - more checking during condition parsing; choice variables are treated as
44 * - xconfig is at the end of its life cycle. Contact <mec@shout.net> if
45 * you are interested in working on the replacement.
54 static struct kconfig * config_list = NULL;
55 static struct kconfig * config_last = NULL;
56 static const char * current_file = "<unknown file>";
57 static int lineno = 0;
59 static void do_source( const char * );
62 int my_strcmp( const char * s1, const char * s2 ) { return strcmp( s1, s2 ); }
63 #define strcmp my_strcmp
66 * Report a syntax error.
68 static void syntax_error( const char * msg )
70 fprintf( stderr, "%s: %d: %s\n", current_file, lineno, msg );
77 * Find index of a specific variable in the symbol table.
78 * Create a new entry if it does not exist yet.
80 struct variable *vartable;
82 static int vartable_size = 0;
84 int get_varnum( char * name )
88 for ( i = 1; i <= max_varnum; i++ )
89 if ( strcmp( vartable[i].name, name ) == 0 )
91 while (max_varnum+1 >= vartable_size) {
92 vartable = realloc(vartable, (vartable_size += 1000)*sizeof(*vartable));
94 fprintf(stderr, "tkparse realloc vartable failed\n");
98 vartable[++max_varnum].name = malloc( strlen( name )+1 );
99 strcpy( vartable[max_varnum].name, name );
108 static const char * get_string( const char * pnt, char ** label )
115 if ( *pnt == '\0' || *pnt == ' ' || *pnt == '\t' )
120 *label = malloc( pnt - word + 1 );
121 memcpy( *label, word, pnt - word );
122 (*label)[pnt - word] = '\0';
132 * Get a quoted string.
133 * Insert a '\' before any characters that need quoting.
135 static const char * get_qstring( const char * pnt, char ** label )
138 char newlabel [2048];
141 /* advance to the open quote */
147 if ( quote_char == '"' || quote_char == '\'' )
151 /* copy into an intermediate buffer */
156 syntax_error( "unterminated quoted string" );
157 if ( *pnt == quote_char && pnt[-1] != '\\' )
160 /* copy the character, quoting if needed */
161 if ( *pnt == '"' || *pnt == '\'' || *pnt == '[' || *pnt == ']' )
166 /* copy the label into a permanent location */
168 *label = (char *) malloc( pnt1 - newlabel );
169 memcpy( *label, newlabel, pnt1 - newlabel );
171 /* skip over last quote and next whitespace */
173 while ( *pnt == ' ' || *pnt == '\t' )
181 * Get a quoted or unquoted string. It is recognized by the first
182 * non-white character. '"' and '"' are not allowed inside the string.
184 static const char * get_qnqstring( const char * pnt, char ** label )
188 while ( *pnt == ' ' || *pnt == '\t' )
194 if ( quote_char == '"' || quote_char == '\'' )
195 return get_qstring( pnt, label );
197 return get_string( pnt, label );
203 * Tokenize an 'if' statement condition.
205 static struct condition * tokenize_if( const char * pnt )
207 struct condition * list;
208 struct condition * last;
209 struct condition * prev;
211 /* eat the open bracket */
212 while ( *pnt == ' ' || *pnt == '\t' )
215 syntax_error( "bad 'if' condition" );
221 struct condition * cond;
223 /* advance to the next token */
224 while ( *pnt == ' ' || *pnt == '\t' )
227 syntax_error( "unterminated 'if' condition" );
231 /* allocate a new token */
232 cond = malloc( sizeof(*cond) );
233 memset( cond, 0, sizeof(*cond) );
235 { list = last = cond; prev = NULL; }
237 { prev = last; last->next = cond; last = cond; }
239 /* determine the token value */
240 if ( *pnt == '-' && pnt[1] == 'a' )
242 if ( ! prev || ( prev->op != op_variable && prev->op != op_constant ) )
243 syntax_error( "incorrect argument" );
244 cond->op = op_and; pnt += 2; continue;
247 if ( *pnt == '-' && pnt[1] == 'o' )
249 if ( ! prev || ( prev->op != op_variable && prev->op != op_constant ) )
250 syntax_error( "incorrect argument" );
251 cond->op = op_or; pnt += 2; continue;
254 if ( *pnt == '!' && pnt[1] == '=' )
256 if ( ! prev || ( prev->op != op_variable && prev->op != op_constant ) )
257 syntax_error( "incorrect argument" );
258 cond->op = op_neq; pnt += 2; continue;
263 if ( ! prev || ( prev->op != op_variable && prev->op != op_constant ) )
264 syntax_error( "incorrect argument" );
265 cond->op = op_eq; pnt += 1; continue;
270 if ( prev && ( prev->op != op_and && prev->op != op_or
271 && prev->op != op_bang ) )
272 syntax_error( "incorrect argument" );
273 cond->op = op_bang; pnt += 1; continue;
280 if ( prev && ( prev->op == op_variable || prev->op == op_constant ) )
281 syntax_error( "incorrect argument" );
282 /* advance to the word */
285 { cond->op = op_variable; pnt++; }
287 { cond->op = op_constant; }
289 /* find the end of the word */
294 syntax_error( "unterminated double quote" );
300 /* store a copy of this word */
302 char * str = malloc( pnt - word + 1 );
303 memcpy( str, word, pnt - word );
304 str [pnt - word] = '\0';
305 if ( cond->op == op_variable )
307 cond->nameindex = get_varnum( str );
310 else /* op_constant */
321 syntax_error( "bad if condition" );
328 * Tokenize a choice list. Choices appear as pairs of strings;
329 * note that I am parsing *inside* the double quotes. Ugh.
331 static const char * tokenize_choices( struct kconfig * cfg_choose,
334 int default_checked = 0;
337 struct kconfig * cfg;
338 char * buffer = malloc( 64 );
340 /* skip whitespace */
341 while ( *pnt == ' ' || *pnt == '\t' )
346 /* allocate a new kconfig line */
347 cfg = malloc( sizeof(*cfg) );
348 memset( cfg, 0, sizeof(*cfg) );
349 if ( config_last == NULL )
350 { config_last = config_list = cfg; }
352 { config_last->next = cfg; config_last = cfg; }
354 /* fill out the line */
355 cfg->token = token_choice_item;
356 cfg->cfg_parent = cfg_choose;
357 pnt = get_string( pnt, &cfg->label );
358 if ( ! default_checked &&
359 ! strncmp( cfg->label, cfg_choose->value, strlen( cfg_choose->value ) ) )
362 free( cfg_choose->value );
363 cfg_choose->value = cfg->label;
365 while ( *pnt == ' ' || *pnt == '\t' )
367 pnt = get_string( pnt, &buffer );
368 cfg->nameindex = get_varnum( buffer );
370 if ( ! default_checked )
371 syntax_error( "bad 'choice' default value" );
380 static void tokenize_line( const char * pnt )
382 static struct kconfig * last_menuoption = NULL;
384 struct kconfig * cfg;
385 struct dependency ** dep_ptr;
386 char * buffer = malloc( 64 );
388 /* skip white space */
389 while ( *pnt == ' ' || *pnt == '\t' )
393 * categorize the next token
396 #define match_token(t, s) \
397 if (strncmp(pnt, s, strlen(s)) == 0) { token = t; pnt += strlen(s); break; }
399 token = token_UNKNOWN;
410 match_token( token_bool, "bool" );
414 match_token( token_choice_header, "choice" );
415 match_token( token_comment, "comment" );
419 match_token( token_define_bool, "define_bool" );
420 match_token( token_define_hex, "define_hex" );
421 match_token( token_define_int, "define_int" );
422 match_token( token_define_string, "define_string" );
423 match_token( token_define_tristate, "define_tristate" );
424 match_token( token_dep_bool, "dep_bool" );
425 match_token( token_dep_mbool, "dep_mbool" );
426 match_token( token_dep_tristate, "dep_tristate" );
430 match_token( token_else, "else" );
431 match_token( token_endmenu, "endmenu" );
435 match_token( token_fi, "fi" );
439 match_token( token_hex, "hex" );
443 match_token( token_if, "if" );
444 match_token( token_int, "int" );
448 match_token( token_mainmenu_name, "mainmenu_name" );
449 match_token( token_mainmenu_option, "mainmenu_option" );
453 match_token( token_source, "source" );
454 match_token( token_string, "string" );
458 match_token( token_then, "then" );
459 match_token( token_tristate, "tristate" );
463 match_token( token_unset, "unset" );
469 if ( token == token_source )
471 while ( *pnt == ' ' || *pnt == '\t' )
477 if ( token == token_then )
479 if ( config_last != NULL && config_last->token == token_if )
481 syntax_error( "bogus 'then'" );
485 if ( token == token_unset )
487 fprintf( stderr, "Ignoring 'unset' command\n" );
492 if ( token == token_UNKNOWN )
493 syntax_error( "unknown command" );
498 cfg = malloc( sizeof(*cfg) );
499 memset( cfg, 0, sizeof(*cfg) );
500 if ( config_last == NULL )
501 { config_last = config_list = cfg; }
503 { config_last->next = cfg; config_last = cfg; }
506 * Tokenize the arguments.
508 while ( *pnt == ' ' || *pnt == '\t' )
515 syntax_error( "unknown token" );
519 pnt = get_qstring ( pnt, &cfg->label );
520 pnt = get_string ( pnt, &buffer );
521 cfg->nameindex = get_varnum( buffer );
524 case token_choice_header:
526 static int choose_number = 0;
529 pnt = get_qstring ( pnt, &cfg->label );
530 pnt = get_qstring ( pnt, &choice_list );
531 pnt = get_string ( pnt, &cfg->value );
532 cfg->nameindex = -(choose_number++);
533 tokenize_choices( cfg, choice_list );
539 pnt = get_qstring(pnt, &cfg->label);
540 if ( last_menuoption != NULL )
542 pnt = get_qstring(pnt, &cfg->label);
543 if (cfg->label == NULL)
544 syntax_error( "missing comment text" );
545 last_menuoption->label = cfg->label;
546 last_menuoption = NULL;
550 case token_define_bool:
551 case token_define_tristate:
552 pnt = get_string( pnt, &buffer );
553 cfg->nameindex = get_varnum( buffer );
554 while ( *pnt == ' ' || *pnt == '\t' )
556 if ( ( pnt[0] == 'Y' || pnt[0] == 'M' || pnt[0] == 'N'
557 || pnt[0] == 'y' || pnt[0] == 'm' || pnt[0] == 'n' )
558 && ( pnt[1] == '\0' || pnt[1] == ' ' || pnt[1] == '\t' ) )
560 if ( *pnt == 'n' || *pnt == 'N' ) cfg->value = strdup( "CONSTANT_N" );
561 else if ( *pnt == 'y' || *pnt == 'Y' ) cfg->value = strdup( "CONSTANT_Y" );
562 else if ( *pnt == 'm' || *pnt == 'M' ) cfg->value = strdup( "CONSTANT_M" );
564 else if ( *pnt == '$' )
567 pnt = get_string( pnt, &cfg->value );
571 syntax_error( "unknown define_bool value" );
573 get_varnum( cfg->value );
576 case token_define_hex:
577 case token_define_int:
578 pnt = get_string( pnt, &buffer );
579 cfg->nameindex = get_varnum( buffer );
580 pnt = get_string( pnt, &cfg->value );
583 case token_define_string:
584 pnt = get_string( pnt, &buffer );
585 cfg->nameindex = get_varnum( buffer );
586 pnt = get_qnqstring( pnt, &cfg->value );
587 if (cfg->value == NULL)
588 syntax_error( "missing value" );
592 case token_dep_mbool:
593 case token_dep_tristate:
594 pnt = get_qstring ( pnt, &cfg->label );
595 pnt = get_string ( pnt, &buffer );
596 cfg->nameindex = get_varnum( buffer );
598 while ( *pnt == ' ' || *pnt == '\t' )
601 dep_ptr = &(cfg->depend);
604 *dep_ptr = (struct dependency *) malloc( sizeof( struct dependency ) );
605 (*dep_ptr)->next = NULL;
607 if ( ( pnt[0] == 'Y' || pnt[0] == 'M' || pnt[0] == 'N'
608 || pnt[0] == 'y' || pnt[0] == 'm' || pnt[0] == 'n' )
609 && ( pnt[1] == '\0' || pnt[1] == ' ' || pnt[1] == '\t' ) )
611 /* dep_tristate 'foo' CONFIG_FOO m */
612 if ( pnt[0] == 'Y' || pnt[0] == 'y' )
613 (*dep_ptr)->name = strdup( "CONSTANT_Y" );
614 else if ( pnt[0] == 'N' || pnt[0] == 'n' )
615 (*dep_ptr)->name = strdup( "CONSTANT_N" );
617 (*dep_ptr)->name = strdup( "CONSTANT_M" );
619 get_varnum( (*dep_ptr)->name );
621 else if ( *pnt == '$' )
624 pnt = get_string( pnt, &(*dep_ptr)->name );
625 get_varnum( (*dep_ptr)->name );
629 syntax_error( "can't handle dep_bool/dep_mbool/dep_tristate condition" );
631 dep_ptr = &(*dep_ptr)->next;
632 while ( *pnt == ' ' || *pnt == '\t' )
637 * Create a conditional for this object's dependencies.
641 struct dependency * dep;
642 struct condition ** cond_ptr;
645 cond_ptr = &(cfg->cond);
646 for ( dep = cfg->depend; dep; dep = dep->next )
648 if ( token == token_dep_tristate
649 && ! strcmp( dep->name, "CONSTANT_M" ) )
659 *cond_ptr = malloc( sizeof(struct condition) );
660 memset( *cond_ptr, 0, sizeof(struct condition) );
661 (*cond_ptr)->op = op_and;
662 cond_ptr = &(*cond_ptr)->next;
664 *cond_ptr = malloc( sizeof(struct condition) );
665 memset( *cond_ptr, 0, sizeof(struct condition) );
666 (*cond_ptr)->op = op_lparen;
667 if ( token == token_dep_bool )
668 sprintf( fake_if, "[ \"$%s\" = \"y\" -o \"$%s\" = \"\" ]; then",
669 dep->name, dep->name );
671 sprintf( fake_if, "[ \"$%s\" = \"y\" -o \"$%s\" = \"m\" -o \"$%s\" = \"\" ]; then",
672 dep->name, dep->name, dep->name );
673 (*cond_ptr)->next = tokenize_if( fake_if );
675 cond_ptr = &(*cond_ptr)->next;
676 *cond_ptr = malloc( sizeof(struct condition) );
677 memset( *cond_ptr, 0, sizeof(struct condition) );
678 (*cond_ptr)->op = op_rparen;
679 cond_ptr = &(*cond_ptr)->next;
691 pnt = get_qstring ( pnt, &cfg->label );
692 pnt = get_string ( pnt, &buffer );
693 cfg->nameindex = get_varnum( buffer );
694 pnt = get_string ( pnt, &cfg->value );
698 pnt = get_qstring ( pnt, &cfg->label );
699 pnt = get_string ( pnt, &buffer );
700 cfg->nameindex = get_varnum( buffer );
701 pnt = get_qnqstring ( pnt, &cfg->value );
702 if (cfg->value == NULL)
703 syntax_error( "missing initial value" );
707 cfg->cond = tokenize_if( pnt );
710 case token_mainmenu_name:
711 pnt = get_qstring( pnt, &cfg->label );
714 case token_mainmenu_option:
715 if ( strncmp( pnt, "next_comment", 12 ) == 0 )
716 last_menuoption = cfg;
718 pnt = get_qstring( pnt, &cfg->label );
722 pnt = get_string( pnt, &buffer );
723 cfg->nameindex = get_varnum( buffer );
724 while ( *pnt == ' ' || *pnt == '\t' )
728 cfg->next = (struct kconfig *) malloc( sizeof(struct kconfig) );
729 memset( cfg->next, 0, sizeof(struct kconfig) );
731 cfg->token = token_unset;
732 pnt = get_string( pnt, &buffer );
733 cfg->nameindex = get_varnum( buffer );
734 while ( *pnt == ' ' || *pnt == '\t' )
745 * Implement the "source" command.
747 static void do_source( const char * filename )
751 const char * old_file;
756 if ( strcmp( filename, "-" ) == 0 )
759 infile = fopen( filename, "r" );
761 /* if that failed, try ../filename */
762 if ( infile == NULL )
764 sprintf( buffer, "../%s", filename );
765 infile = fopen( buffer, "r" );
768 if ( infile == NULL )
770 sprintf( buffer, "unable to open %s", filename );
771 syntax_error( buffer );
774 /* push the new file name and line number */
775 old_file = current_file;
777 current_file = filename;
780 /* read and process lines */
781 for ( offset = 0; ; )
786 fgets( buffer + offset, sizeof(buffer) - offset, infile );
787 if ( feof( infile ) )
791 /* strip the trailing return character */
792 pnt = buffer + strlen(buffer) - 1;
799 offset = pnt - buffer;
803 /* tokenize this line */
804 tokenize_line( buffer );
808 /* that's all, folks */
809 if ( infile != stdin )
811 current_file = old_file;
821 int main( int argc, const char * argv [] )
824 fix_conditionals ( config_list );
825 dump_tk_script ( config_list );