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 specyfic variable in the symbol table.
78 * Create a new entry if it does not exist yet.
80 #define VARTABLE_SIZE 2048
81 struct variable vartable
[VARTABLE_SIZE
];
84 int get_varnum( char * name
)
88 for ( i
= 1; i
<= max_varnum
; i
++ )
89 if ( strcmp( vartable
[i
].name
, name
) == 0 )
91 if (max_varnum
> VARTABLE_SIZE
-1)
92 syntax_error( "Too many variables defined." );
93 vartable
[++max_varnum
].name
= malloc( strlen( name
)+1 );
94 strcpy( vartable
[max_varnum
].name
, name
);
103 static const char * get_string( const char * pnt
, char ** label
)
110 if ( *pnt
== '\0' || *pnt
== ' ' || *pnt
== '\t' )
115 *label
= malloc( pnt
- word
+ 1 );
116 memcpy( *label
, word
, pnt
- word
);
117 (*label
)[pnt
- word
] = '\0';
127 * Get a quoted string.
128 * Insert a '\' before any characters that need quoting.
130 static const char * get_qstring( const char * pnt
, char ** label
)
133 char newlabel
[2048];
136 /* advance to the open quote */
142 if ( quote_char
== '"' || quote_char
== '\'' )
146 /* copy into an intermediate buffer */
151 syntax_error( "unterminated quoted string" );
152 if ( *pnt
== quote_char
&& pnt
[-1] != '\\' )
155 /* copy the character, quoting if needed */
156 if ( *pnt
== '"' || *pnt
== '\'' || *pnt
== '[' || *pnt
== ']' )
161 /* copy the label into a permanent location */
163 *label
= (char *) malloc( pnt1
- newlabel
);
164 memcpy( *label
, newlabel
, pnt1
- newlabel
);
166 /* skip over last quote and next whitespace */
168 while ( *pnt
== ' ' || *pnt
== '\t' )
176 * Get a quoted or unquoted string. It is recognized by the first
177 * non-white character. '"' and '"' are not allowed inside the string.
179 static const char * get_qnqstring( const char * pnt
, char ** label
)
183 while ( *pnt
== ' ' || *pnt
== '\t' )
189 if ( quote_char
== '"' || quote_char
== '\'' )
190 return get_qstring( pnt
, label
);
192 return get_string( pnt
, label
);
198 * Tokenize an 'if' statement condition.
200 static struct condition
* tokenize_if( const char * pnt
)
202 struct condition
* list
;
203 struct condition
* last
;
204 struct condition
* prev
;
206 /* eat the open bracket */
207 while ( *pnt
== ' ' || *pnt
== '\t' )
210 syntax_error( "bad 'if' condition" );
216 struct condition
* cond
;
218 /* advance to the next token */
219 while ( *pnt
== ' ' || *pnt
== '\t' )
222 syntax_error( "unterminated 'if' condition" );
226 /* allocate a new token */
227 cond
= malloc( sizeof(*cond
) );
228 memset( cond
, 0, sizeof(*cond
) );
230 { list
= last
= cond
; prev
= NULL
; }
232 { prev
= last
; last
->next
= cond
; last
= cond
; }
234 /* determine the token value */
235 if ( *pnt
== '-' && pnt
[1] == 'a' )
237 if ( ! prev
|| ( prev
->op
!= op_variable
&& prev
->op
!= op_constant
) )
238 syntax_error( "incorrect argument" );
239 cond
->op
= op_and
; pnt
+= 2; continue;
242 if ( *pnt
== '-' && pnt
[1] == 'o' )
244 if ( ! prev
|| ( prev
->op
!= op_variable
&& prev
->op
!= op_constant
) )
245 syntax_error( "incorrect argument" );
246 cond
->op
= op_or
; pnt
+= 2; continue;
249 if ( *pnt
== '!' && pnt
[1] == '=' )
251 if ( ! prev
|| ( prev
->op
!= op_variable
&& prev
->op
!= op_constant
) )
252 syntax_error( "incorrect argument" );
253 cond
->op
= op_neq
; pnt
+= 2; continue;
258 if ( ! prev
|| ( prev
->op
!= op_variable
&& prev
->op
!= op_constant
) )
259 syntax_error( "incorrect argument" );
260 cond
->op
= op_eq
; pnt
+= 1; continue;
265 if ( prev
&& ( prev
->op
!= op_and
&& prev
->op
!= op_or
266 && prev
->op
!= op_bang
) )
267 syntax_error( "incorrect argument" );
268 cond
->op
= op_bang
; pnt
+= 1; continue;
275 if ( prev
&& ( prev
->op
== op_variable
|| prev
->op
== op_constant
) )
276 syntax_error( "incorrect argument" );
277 /* advance to the word */
280 { cond
->op
= op_variable
; pnt
++; }
282 { cond
->op
= op_constant
; }
284 /* find the end of the word */
289 syntax_error( "unterminated double quote" );
295 /* store a copy of this word */
297 char * str
= malloc( pnt
- word
+ 1 );
298 memcpy( str
, word
, pnt
- word
);
299 str
[pnt
- word
] = '\0';
300 if ( cond
->op
== op_variable
)
302 cond
->nameindex
= get_varnum( str
);
305 else /* op_constant */
316 syntax_error( "bad if condition" );
323 * Tokenize a choice list. Choices appear as pairs of strings;
324 * note that I am parsing *inside* the double quotes. Ugh.
326 static const char * tokenize_choices( struct kconfig
* cfg_choose
,
329 int default_checked
= 0;
332 struct kconfig
* cfg
;
333 char * buffer
= malloc( 64 );
335 /* skip whitespace */
336 while ( *pnt
== ' ' || *pnt
== '\t' )
341 /* allocate a new kconfig line */
342 cfg
= malloc( sizeof(*cfg
) );
343 memset( cfg
, 0, sizeof(*cfg
) );
344 if ( config_last
== NULL
)
345 { config_last
= config_list
= cfg
; }
347 { config_last
->next
= cfg
; config_last
= cfg
; }
349 /* fill out the line */
350 cfg
->token
= token_choice_item
;
351 cfg
->cfg_parent
= cfg_choose
;
352 pnt
= get_string( pnt
, &cfg
->label
);
353 if ( ! default_checked
&&
354 ! strncmp( cfg
->label
, cfg_choose
->value
, strlen( cfg_choose
->value
) ) )
357 free( cfg_choose
->value
);
358 cfg_choose
->value
= cfg
->label
;
360 while ( *pnt
== ' ' || *pnt
== '\t' )
362 pnt
= get_string( pnt
, &buffer
);
363 cfg
->nameindex
= get_varnum( buffer
);
365 if ( ! default_checked
)
366 syntax_error( "bad 'choice' default value" );
375 static void tokenize_line( const char * pnt
)
377 static struct kconfig
* last_menuoption
= NULL
;
379 struct kconfig
* cfg
;
380 struct dependency
** dep_ptr
;
381 char * buffer
= malloc( 64 );
383 /* skip white space */
384 while ( *pnt
== ' ' || *pnt
== '\t' )
388 * categorize the next token
391 #define match_token(t, s) \
392 if (strncmp(pnt, s, strlen(s)) == 0) { token = t; pnt += strlen(s); break; }
394 token
= token_UNKNOWN
;
405 match_token( token_bool
, "bool" );
409 match_token( token_choice_header
, "choice" );
410 match_token( token_comment
, "comment" );
414 match_token( token_define_bool
, "define_bool" );
415 match_token( token_define_hex
, "define_hex" );
416 match_token( token_define_int
, "define_int" );
417 match_token( token_define_string
, "define_string" );
418 match_token( token_define_tristate
, "define_tristate" );
419 match_token( token_dep_bool
, "dep_bool" );
420 match_token( token_dep_mbool
, "dep_mbool" );
421 match_token( token_dep_tristate
, "dep_tristate" );
425 match_token( token_else
, "else" );
426 match_token( token_endmenu
, "endmenu" );
430 match_token( token_fi
, "fi" );
434 match_token( token_hex
, "hex" );
438 match_token( token_if
, "if" );
439 match_token( token_int
, "int" );
443 match_token( token_mainmenu_name
, "mainmenu_name" );
444 match_token( token_mainmenu_option
, "mainmenu_option" );
448 match_token( token_source
, "source" );
449 match_token( token_string
, "string" );
453 match_token( token_then
, "then" );
454 match_token( token_tristate
, "tristate" );
458 match_token( token_unset
, "unset" );
464 if ( token
== token_source
)
466 while ( *pnt
== ' ' || *pnt
== '\t' )
472 if ( token
== token_then
)
474 if ( config_last
!= NULL
&& config_last
->token
== token_if
)
476 syntax_error( "bogus 'then'" );
480 if ( token
== token_unset
)
482 fprintf( stderr
, "Ignoring 'unset' command\n" );
487 if ( token
== token_UNKNOWN
)
488 syntax_error( "unknown command" );
493 cfg
= malloc( sizeof(*cfg
) );
494 memset( cfg
, 0, sizeof(*cfg
) );
495 if ( config_last
== NULL
)
496 { config_last
= config_list
= cfg
; }
498 { config_last
->next
= cfg
; config_last
= cfg
; }
501 * Tokenize the arguments.
503 while ( *pnt
== ' ' || *pnt
== '\t' )
510 syntax_error( "unknown token" );
514 pnt
= get_qstring ( pnt
, &cfg
->label
);
515 pnt
= get_string ( pnt
, &buffer
);
516 cfg
->nameindex
= get_varnum( buffer
);
519 case token_choice_header
:
521 static int choose_number
= 0;
524 pnt
= get_qstring ( pnt
, &cfg
->label
);
525 pnt
= get_qstring ( pnt
, &choice_list
);
526 pnt
= get_string ( pnt
, &cfg
->value
);
527 cfg
->nameindex
= -(choose_number
++);
528 tokenize_choices( cfg
, choice_list
);
534 pnt
= get_qstring(pnt
, &cfg
->label
);
535 if ( last_menuoption
!= NULL
)
537 pnt
= get_qstring(pnt
, &cfg
->label
);
538 if (cfg
->label
== NULL
)
539 syntax_error( "missing comment text" );
540 last_menuoption
->label
= cfg
->label
;
541 last_menuoption
= NULL
;
545 case token_define_bool
:
546 case token_define_tristate
:
547 pnt
= get_string( pnt
, &buffer
);
548 cfg
->nameindex
= get_varnum( buffer
);
549 while ( *pnt
== ' ' || *pnt
== '\t' )
551 if ( ( pnt
[0] == 'Y' || pnt
[0] == 'M' || pnt
[0] == 'N'
552 || pnt
[0] == 'y' || pnt
[0] == 'm' || pnt
[0] == 'n' )
553 && ( pnt
[1] == '\0' || pnt
[1] == ' ' || pnt
[1] == '\t' ) )
555 if ( *pnt
== 'n' || *pnt
== 'N' ) cfg
->value
= strdup( "CONSTANT_N" );
556 else if ( *pnt
== 'y' || *pnt
== 'Y' ) cfg
->value
= strdup( "CONSTANT_Y" );
557 else if ( *pnt
== 'm' || *pnt
== 'M' ) cfg
->value
= strdup( "CONSTANT_M" );
559 else if ( *pnt
== '$' )
562 pnt
= get_string( pnt
, &cfg
->value
);
566 syntax_error( "unknown define_bool value" );
568 get_varnum( cfg
->value
);
571 case token_define_hex
:
572 case token_define_int
:
573 pnt
= get_string( pnt
, &buffer
);
574 cfg
->nameindex
= get_varnum( buffer
);
575 pnt
= get_string( pnt
, &cfg
->value
);
578 case token_define_string
:
579 pnt
= get_string( pnt
, &buffer
);
580 cfg
->nameindex
= get_varnum( buffer
);
581 pnt
= get_qnqstring( pnt
, &cfg
->value
);
582 if (cfg
->value
== NULL
)
583 syntax_error( "missing value" );
587 case token_dep_mbool
:
588 case token_dep_tristate
:
589 pnt
= get_qstring ( pnt
, &cfg
->label
);
590 pnt
= get_string ( pnt
, &buffer
);
591 cfg
->nameindex
= get_varnum( buffer
);
593 while ( *pnt
== ' ' || *pnt
== '\t' )
596 dep_ptr
= &(cfg
->depend
);
599 *dep_ptr
= (struct dependency
*) malloc( sizeof( struct dependency
) );
600 (*dep_ptr
)->next
= NULL
;
602 if ( ( pnt
[0] == 'Y' || pnt
[0] == 'M' || pnt
[0] == 'N'
603 || pnt
[0] == 'y' || pnt
[0] == 'm' || pnt
[0] == 'n' )
604 && ( pnt
[1] == '\0' || pnt
[1] == ' ' || pnt
[1] == '\t' ) )
606 /* dep_tristate 'foo' CONFIG_FOO m */
607 if ( pnt
[0] == 'Y' || pnt
[0] == 'y' )
608 (*dep_ptr
)->name
= strdup( "CONSTANT_Y" );
609 else if ( pnt
[0] == 'N' || pnt
[0] == 'n' )
610 (*dep_ptr
)->name
= strdup( "CONSTANT_N" );
612 (*dep_ptr
)->name
= strdup( "CONSTANT_M" );
614 get_varnum( (*dep_ptr
)->name
);
616 else if ( *pnt
== '$' )
619 pnt
= get_string( pnt
, &(*dep_ptr
)->name
);
620 get_varnum( (*dep_ptr
)->name
);
624 syntax_error( "can't handle dep_bool/dep_mbool/dep_tristate condition" );
626 dep_ptr
= &(*dep_ptr
)->next
;
627 while ( *pnt
== ' ' || *pnt
== '\t' )
632 * Create a conditional for this object's dependencies.
636 struct dependency
* dep
;
637 struct condition
** cond_ptr
;
640 cond_ptr
= &(cfg
->cond
);
641 for ( dep
= cfg
->depend
; dep
; dep
= dep
->next
)
643 if ( token
== token_dep_tristate
644 && ! strcmp( dep
->name
, "CONSTANT_M" ) )
654 *cond_ptr
= malloc( sizeof(struct condition
) );
655 memset( *cond_ptr
, 0, sizeof(struct condition
) );
656 (*cond_ptr
)->op
= op_and
;
657 cond_ptr
= &(*cond_ptr
)->next
;
659 *cond_ptr
= malloc( sizeof(struct condition
) );
660 memset( *cond_ptr
, 0, sizeof(struct condition
) );
661 (*cond_ptr
)->op
= op_lparen
;
662 if ( token
== token_dep_bool
)
663 sprintf( fake_if
, "[ \"$%s\" = \"y\" -o \"$%s\" = \"\" ]; then",
664 dep
->name
, dep
->name
);
666 sprintf( fake_if
, "[ \"$%s\" = \"y\" -o \"$%s\" = \"m\" -o \"$%s\" = \"\" ]; then",
667 dep
->name
, dep
->name
, dep
->name
);
668 (*cond_ptr
)->next
= tokenize_if( fake_if
);
670 cond_ptr
= &(*cond_ptr
)->next
;
671 *cond_ptr
= malloc( sizeof(struct condition
) );
672 memset( *cond_ptr
, 0, sizeof(struct condition
) );
673 (*cond_ptr
)->op
= op_rparen
;
674 cond_ptr
= &(*cond_ptr
)->next
;
686 pnt
= get_qstring ( pnt
, &cfg
->label
);
687 pnt
= get_string ( pnt
, &buffer
);
688 cfg
->nameindex
= get_varnum( buffer
);
689 pnt
= get_string ( pnt
, &cfg
->value
);
693 pnt
= get_qstring ( pnt
, &cfg
->label
);
694 pnt
= get_string ( pnt
, &buffer
);
695 cfg
->nameindex
= get_varnum( buffer
);
696 pnt
= get_qnqstring ( pnt
, &cfg
->value
);
697 if (cfg
->value
== NULL
)
698 syntax_error( "missing initial value" );
702 cfg
->cond
= tokenize_if( pnt
);
705 case token_mainmenu_name
:
706 pnt
= get_qstring( pnt
, &cfg
->label
);
709 case token_mainmenu_option
:
710 if ( strncmp( pnt
, "next_comment", 12 ) == 0 )
711 last_menuoption
= cfg
;
713 pnt
= get_qstring( pnt
, &cfg
->label
);
717 pnt
= get_string( pnt
, &buffer
);
718 cfg
->nameindex
= get_varnum( buffer
);
719 while ( *pnt
== ' ' || *pnt
== '\t' )
723 cfg
->next
= (struct kconfig
*) malloc( sizeof(struct kconfig
) );
724 memset( cfg
->next
, 0, sizeof(struct kconfig
) );
726 cfg
->token
= token_unset
;
727 pnt
= get_string( pnt
, &buffer
);
728 cfg
->nameindex
= get_varnum( buffer
);
729 while ( *pnt
== ' ' || *pnt
== '\t' )
740 * Implement the "source" command.
742 static void do_source( const char * filename
)
746 const char * old_file
;
751 if ( strcmp( filename
, "-" ) == 0 )
754 infile
= fopen( filename
, "r" );
756 /* if that failed, try ../filename */
757 if ( infile
== NULL
)
759 sprintf( buffer
, "../%s", filename
);
760 infile
= fopen( buffer
, "r" );
763 if ( infile
== NULL
)
765 sprintf( buffer
, "unable to open %s", filename
);
766 syntax_error( buffer
);
769 /* push the new file name and line number */
770 old_file
= current_file
;
772 current_file
= filename
;
775 /* read and process lines */
776 for ( offset
= 0; ; )
781 fgets( buffer
+ offset
, sizeof(buffer
) - offset
, infile
);
782 if ( feof( infile
) )
786 /* strip the trailing return character */
787 pnt
= buffer
+ strlen(buffer
) - 1;
794 offset
= pnt
- buffer
;
798 /* tokenize this line */
799 tokenize_line( buffer
);
803 /* that's all, folks */
804 if ( infile
!= stdin
)
806 current_file
= old_file
;
816 int main( int argc
, const char * argv
[] )
819 fix_conditionals ( config_list
);
820 dump_tk_script ( config_list
);