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
,
331 struct kconfig
* cfg
;
332 char * buffer
= malloc( 64 );
334 /* skip whitespace */
335 while ( *pnt
== ' ' || *pnt
== '\t' )
340 /* allocate a new kconfig line */
341 cfg
= malloc( sizeof(*cfg
) );
342 memset( cfg
, 0, sizeof(*cfg
) );
343 if ( config_last
== NULL
)
344 { config_last
= config_list
= cfg
; }
346 { config_last
->next
= cfg
; config_last
= cfg
; }
348 /* fill out the line */
349 cfg
->token
= token_choice_item
;
350 cfg
->cfg_parent
= cfg_choose
;
351 pnt
= get_string( pnt
, &cfg
->label
);
352 while ( *pnt
== ' ' || *pnt
== '\t' )
354 pnt
= get_string( pnt
, &buffer
);
355 cfg
->nameindex
= get_varnum( buffer
);
366 static void tokenize_line( const char * pnt
)
368 static struct kconfig
* last_menuoption
= NULL
;
370 struct kconfig
* cfg
;
371 struct dependency
** dep_ptr
;
372 char * buffer
= malloc( 64 );
374 /* skip white space */
375 while ( *pnt
== ' ' || *pnt
== '\t' )
379 * categorize the next token
382 #define match_token(t, s) \
383 if (strncmp(pnt, s, strlen(s)) == 0) { token = t; pnt += strlen(s); break; }
385 token
= token_UNKNOWN
;
396 match_token( token_bool
, "bool" );
400 match_token( token_choice_header
, "choice" );
401 match_token( token_comment
, "comment" );
405 match_token( token_define_bool
, "define_bool" );
406 match_token( token_define_hex
, "define_hex" );
407 match_token( token_define_int
, "define_int" );
408 match_token( token_define_string
, "define_string" );
409 match_token( token_define_tristate
, "define_tristate" );
410 match_token( token_dep_bool
, "dep_bool" );
411 match_token( token_dep_mbool
, "dep_mbool" );
412 match_token( token_dep_tristate
, "dep_tristate" );
416 match_token( token_else
, "else" );
417 match_token( token_endmenu
, "endmenu" );
421 match_token( token_fi
, "fi" );
425 match_token( token_hex
, "hex" );
429 match_token( token_if
, "if" );
430 match_token( token_int
, "int" );
434 match_token( token_mainmenu_name
, "mainmenu_name" );
435 match_token( token_mainmenu_option
, "mainmenu_option" );
439 match_token( token_source
, "source" );
440 match_token( token_string
, "string" );
444 match_token( token_then
, "then" );
445 match_token( token_tristate
, "tristate" );
449 match_token( token_unset
, "unset" );
455 if ( token
== token_source
)
457 while ( *pnt
== ' ' || *pnt
== '\t' )
463 if ( token
== token_then
)
465 if ( config_last
!= NULL
&& config_last
->token
== token_if
)
467 syntax_error( "bogus 'then'" );
471 if ( token
== token_unset
)
473 fprintf( stderr
, "Ignoring 'unset' command\n" );
478 if ( token
== token_UNKNOWN
)
479 syntax_error( "unknown command" );
484 cfg
= malloc( sizeof(*cfg
) );
485 memset( cfg
, 0, sizeof(*cfg
) );
486 if ( config_last
== NULL
)
487 { config_last
= config_list
= cfg
; }
489 { config_last
->next
= cfg
; config_last
= cfg
; }
492 * Tokenize the arguments.
494 while ( *pnt
== ' ' || *pnt
== '\t' )
501 syntax_error( "unknown token" );
505 pnt
= get_qstring ( pnt
, &cfg
->label
);
506 pnt
= get_string ( pnt
, &buffer
);
507 cfg
->nameindex
= get_varnum( buffer
);
510 case token_choice_header
:
512 static int choose_number
= 0;
515 pnt
= get_qstring ( pnt
, &cfg
->label
);
516 pnt
= get_qstring ( pnt
, &choice_list
);
517 pnt
= get_string ( pnt
, &cfg
->value
);
519 cfg
->nameindex
= -(choose_number
++);
520 tokenize_choices( cfg
, choice_list
);
526 pnt
= get_qstring(pnt
, &cfg
->label
);
527 if ( last_menuoption
!= NULL
)
529 pnt
= get_qstring(pnt
, &cfg
->label
);
530 if (cfg
->label
== NULL
)
531 syntax_error( "missing comment text" );
532 last_menuoption
->label
= cfg
->label
;
533 last_menuoption
= NULL
;
537 case token_define_bool
:
538 case token_define_tristate
:
539 pnt
= get_string( pnt
, &buffer
);
540 cfg
->nameindex
= get_varnum( buffer
);
541 while ( *pnt
== ' ' || *pnt
== '\t' )
543 if ( ( pnt
[0] == 'Y' || pnt
[0] == 'M' || pnt
[0] == 'N'
544 || pnt
[0] == 'y' || pnt
[0] == 'm' || pnt
[0] == 'n' )
545 && ( pnt
[1] == '\0' || pnt
[1] == ' ' || pnt
[1] == '\t' ) )
547 if ( *pnt
== 'n' || *pnt
== 'N' ) cfg
->value
= strdup( "CONSTANT_N" );
548 else if ( *pnt
== 'y' || *pnt
== 'Y' ) cfg
->value
= strdup( "CONSTANT_Y" );
549 else if ( *pnt
== 'm' || *pnt
== 'M' ) cfg
->value
= strdup( "CONSTANT_M" );
551 else if ( *pnt
== '$' )
554 pnt
= get_string( pnt
, &cfg
->value
);
558 syntax_error( "unknown define_bool value" );
560 get_varnum( cfg
->value
);
563 case token_define_hex
:
564 case token_define_int
:
565 pnt
= get_string( pnt
, &buffer
);
566 cfg
->nameindex
= get_varnum( buffer
);
567 pnt
= get_string( pnt
, &cfg
->value
);
570 case token_define_string
:
571 pnt
= get_string( pnt
, &buffer
);
572 cfg
->nameindex
= get_varnum( buffer
);
573 pnt
= get_qnqstring( pnt
, &cfg
->value
);
574 if (cfg
->value
== NULL
)
575 syntax_error( "missing value" );
579 case token_dep_mbool
:
580 case token_dep_tristate
:
581 pnt
= get_qstring ( pnt
, &cfg
->label
);
582 pnt
= get_string ( pnt
, &buffer
);
583 cfg
->nameindex
= get_varnum( buffer
);
585 while ( *pnt
== ' ' || *pnt
== '\t' )
588 dep_ptr
= &(cfg
->depend
);
591 *dep_ptr
= (struct dependency
*) malloc( sizeof( struct dependency
) );
592 (*dep_ptr
)->next
= NULL
;
594 if ( ( pnt
[0] == 'Y' || pnt
[0] == 'M' || pnt
[0] == 'N'
595 || pnt
[0] == 'y' || pnt
[0] == 'm' || pnt
[0] == 'n' )
596 && ( pnt
[1] == '\0' || pnt
[1] == ' ' || pnt
[1] == '\t' ) )
598 /* dep_tristate 'foo' CONFIG_FOO m */
599 if ( pnt
[0] == 'Y' || pnt
[0] == 'y' )
600 (*dep_ptr
)->name
= strdup( "CONSTANT_Y" );
601 else if ( pnt
[0] == 'N' || pnt
[0] == 'n' )
602 (*dep_ptr
)->name
= strdup( "CONSTANT_N" );
604 (*dep_ptr
)->name
= strdup( "CONSTANT_M" );
606 get_varnum( (*dep_ptr
)->name
);
608 else if ( *pnt
== '$' )
611 pnt
= get_string( pnt
, &(*dep_ptr
)->name
);
612 get_varnum( (*dep_ptr
)->name
);
616 syntax_error( "can't handle dep_bool/dep_mbool/dep_tristate condition" );
618 dep_ptr
= &(*dep_ptr
)->next
;
619 while ( *pnt
== ' ' || *pnt
== '\t' )
624 * Create a conditional for this object's dependencies.
628 struct dependency
* dep
;
629 struct condition
** cond_ptr
;
632 cond_ptr
= &(cfg
->cond
);
633 for ( dep
= cfg
->depend
; dep
; dep
= dep
->next
)
635 if ( token
== token_dep_tristate
636 && ! strcmp( dep
->name
, "CONSTANT_M" ) )
646 *cond_ptr
= malloc( sizeof(struct condition
) );
647 memset( *cond_ptr
, 0, sizeof(struct condition
) );
648 (*cond_ptr
)->op
= op_and
;
649 cond_ptr
= &(*cond_ptr
)->next
;
651 *cond_ptr
= malloc( sizeof(struct condition
) );
652 memset( *cond_ptr
, 0, sizeof(struct condition
) );
653 (*cond_ptr
)->op
= op_lparen
;
654 if ( token
== token_dep_bool
)
655 sprintf( fake_if
, "[ \"$%s\" = \"y\" -o \"$%s\" = \"\" ]; then",
656 dep
->name
, dep
->name
);
658 sprintf( fake_if
, "[ \"$%s\" = \"y\" -o \"$%s\" = \"m\" -o \"$%s\" = \"\" ]; then",
659 dep
->name
, dep
->name
, dep
->name
);
660 (*cond_ptr
)->next
= tokenize_if( fake_if
);
662 cond_ptr
= &(*cond_ptr
)->next
;
663 *cond_ptr
= malloc( sizeof(struct condition
) );
664 memset( *cond_ptr
, 0, sizeof(struct condition
) );
665 (*cond_ptr
)->op
= op_rparen
;
666 cond_ptr
= &(*cond_ptr
)->next
;
678 pnt
= get_qstring ( pnt
, &cfg
->label
);
679 pnt
= get_string ( pnt
, &buffer
);
680 cfg
->nameindex
= get_varnum( buffer
);
681 pnt
= get_string ( pnt
, &cfg
->value
);
685 pnt
= get_qstring ( pnt
, &cfg
->label
);
686 pnt
= get_string ( pnt
, &buffer
);
687 cfg
->nameindex
= get_varnum( buffer
);
688 pnt
= get_qnqstring ( pnt
, &cfg
->value
);
689 if (cfg
->value
== NULL
)
690 syntax_error( "missing initial value" );
694 cfg
->cond
= tokenize_if( pnt
);
697 case token_mainmenu_name
:
698 pnt
= get_qstring( pnt
, &cfg
->label
);
701 case token_mainmenu_option
:
702 if ( strncmp( pnt
, "next_comment", 12 ) == 0 )
703 last_menuoption
= cfg
;
705 pnt
= get_qstring( pnt
, &cfg
->label
);
709 pnt
= get_string( pnt
, &buffer
);
710 cfg
->nameindex
= get_varnum( buffer
);
711 while ( *pnt
== ' ' || *pnt
== '\t' )
715 cfg
->next
= (struct kconfig
*) malloc( sizeof(struct kconfig
) );
716 memset( cfg
->next
, 0, sizeof(struct kconfig
) );
718 cfg
->token
= token_unset
;
719 pnt
= get_string( pnt
, &buffer
);
720 cfg
->nameindex
= get_varnum( buffer
);
721 while ( *pnt
== ' ' || *pnt
== '\t' )
732 * Implement the "source" command.
734 static void do_source( const char * filename
)
738 const char * old_file
;
743 if ( strcmp( filename
, "-" ) == 0 )
746 infile
= fopen( filename
, "r" );
748 /* if that failed, try ../filename */
749 if ( infile
== NULL
)
751 sprintf( buffer
, "../%s", filename
);
752 infile
= fopen( buffer
, "r" );
755 if ( infile
== NULL
)
757 sprintf( buffer
, "unable to open %s", filename
);
758 syntax_error( buffer
);
761 /* push the new file name and line number */
762 old_file
= current_file
;
764 current_file
= filename
;
767 /* read and process lines */
768 for ( offset
= 0; ; )
773 fgets( buffer
+ offset
, sizeof(buffer
) - offset
, infile
);
774 if ( feof( infile
) )
778 /* strip the trailing return character */
779 pnt
= buffer
+ strlen(buffer
) - 1;
786 offset
= pnt
- buffer
;
790 /* tokenize this line */
791 tokenize_line( buffer
);
795 /* that's all, folks */
796 if ( infile
!= stdin
)
798 current_file
= old_file
;
808 int main( int argc
, const char * argv
[] )
811 fix_conditionals ( config_list
);
812 dump_tk_script ( config_list
);