7 * The general idea here is that we want to parse a config.in file and
8 * from this, we generate a wish script which gives us effectively the
9 * same functionality that the original config.in script provided.
11 * This task is split roughly into 3 parts. The first parse is the parse
12 * of the input file itself. The second part is where we analyze the
13 * #ifdef clauses, and attach a linked list of tokens to each of the
14 * menu items. In this way, each menu item has a complete list of
15 * dependencies that are used to enable/disable the options.
16 * The third part is to take the configuration database we have build,
17 * and build the actual wish script.
19 * This file contains the code to do the first parse of config.in.
26 struct kconfig
* config
= NULL
;
27 struct kconfig
* clast
= NULL
;
28 struct kconfig
* koption
= NULL
;
29 static int lineno
= 0;
30 static int menus_seen
= 0;
31 static char * current_file
= NULL
;
32 static int do_source(char * filename
);
33 static char * get_string(char *pnt
, char ** labl
);
34 static int choose_number
= 0;
38 * Simple function just to skip over spaces and tabs in config.in.
40 static char * skip_whitespace(char * pnt
)
42 while( *pnt
&& (*pnt
== ' ' || *pnt
== '\t')) pnt
++;
47 * This function parses a conditional from a config.in (i.e. from an ifdef)
48 * and generates a linked list of tokens that describes the conditional.
50 static struct condition
* parse_if(char * pnt
)
53 struct condition
*list
;
54 struct condition
*last
;
55 struct condition
*cpnt
;
62 * We need to find the various tokens, and build the linked list.
64 pnt
= skip_whitespace(pnt
);
65 if( *pnt
!= '[' ) return NULL
;
67 pnt
= skip_whitespace(pnt
);
70 while(*pnt
&& *pnt
!= ']') {
72 pnt
= skip_whitespace(pnt
);
73 if(*pnt
== '\0' || *pnt
== ']') break;
76 * Allocate memory for the token we are about to parse, and insert
77 * it in the linked list.
79 cpnt
= (struct condition
*) malloc(sizeof(struct condition
));
80 memset(cpnt
, 0, sizeof(struct condition
));
92 * Determine what type of operation this token represents.
94 if( *pnt
== '-' && pnt
[1] == 'a' )
101 if( *pnt
== '-' && pnt
[1] == 'o' )
108 if( *pnt
== '!' && pnt
[1] == '=' )
129 if( *pnt
!= '"' ) goto error
; /* This cannot be right. */
133 cpnt
->op
= op_shellcmd
;
136 while(*pnt
&& *pnt
!= '`') *pnt1
++ = *pnt
++;
138 cpnt
->variable
.str
= strdup(varname
);
139 if( *pnt
== '`' ) pnt
++;
140 if( *pnt
== '"' ) pnt
++;
145 cpnt
->op
= op_variable
;
148 while(*pnt
&& *pnt
!= '"') *pnt1
++ = *pnt
++;
150 cpnt
->variable
.str
= strdup(varname
);
151 if( *pnt
== '"' ) pnt
++;
155 cpnt
->op
= op_constant
;
157 while(*pnt
&& *pnt
!= '"') *pnt1
++ = *pnt
++;
159 cpnt
->variable
.str
= strdup(varname
);
160 if( *pnt
== '"' ) pnt
++;
167 if(current_file
!= NULL
)
169 "Bad if clause at line %d(%s):%s\n", lineno
, current_file
, opnt
);
172 "Bad if clause at line %d:%s\n", lineno
, opnt
);
177 * This function looks for a quoted string, from the input buffer, and
178 * returns a pointer to a copy of this string. Any characters in
179 * the string that need to be "quoted" have a '\' character inserted
180 * in front - this way we can directly write these strings into
183 static char * get_qstring(char *pnt
, char ** labl
)
190 while( *pnt
&& *pnt
!= '"' && *pnt
!= '\'') pnt
++;
191 if (*pnt
== '\0') return pnt
;
195 while(*pnt
&& *pnt
!= quotechar
&& pnt
[-1] != '\\')
198 * Quote the character if we need to.
200 if( *pnt
== '"' || *pnt
== '\'' || *pnt
== '[' || *pnt
== ']')
207 pnt2
= (char *) malloc(strlen(newlabel
) + 1);
208 strcpy(pnt2
, newlabel
);
212 * Skip over last quote, and whitespace.
215 pnt
= skip_whitespace(pnt
);
219 static char * parse_choices(struct kconfig
* choice_kcfg
, char * pnt
)
221 struct kconfig
* kcfg
;
225 * Choices appear in pairs of strings. The parse is fairly trivial.
229 pnt
= skip_whitespace(pnt
);
230 if(*pnt
== '\0') break;
232 kcfg
= (struct kconfig
*) malloc(sizeof(struct kconfig
));
233 memset(kcfg
, 0, sizeof(struct kconfig
));
234 kcfg
->tok
= tok_choice
;
242 clast
= config
= kcfg
;
245 pnt
= get_string(pnt
, &kcfg
->label
);
246 pnt
= skip_whitespace(pnt
);
247 pnt
= get_string(pnt
, &kcfg
->optionname
);
248 kcfg
->choice_label
= choice_kcfg
;
249 kcfg
->choice_value
= index
++;
250 if( strcmp(kcfg
->label
, choice_kcfg
->value
) == 0 )
251 choice_kcfg
->choice_value
= kcfg
->choice_value
;
259 * This function grabs one text token from the input buffer
260 * and returns a pointer to a copy of just the identifier.
261 * This can be either a variable name (i.e. CONFIG_NET),
262 * or it could be the default value for the option.
264 static char * get_string(char *pnt
, char ** labl
)
270 if (*pnt
== '\0') return pnt
;
273 while(*pnt
&& *pnt
!= ' ' && *pnt
!= '\t')
279 pnt2
= (char *) malloc(strlen(newlabel
) + 1);
280 strcpy(pnt2
, newlabel
);
289 * Top level parse function. Input pointer is one complete line from config.in
290 * and the result is that we create a token that describes this line
291 * and insert it into our linked list.
293 void parse(char * pnt
) {
295 struct kconfig
* kcfg
;
296 char tmpbuf
[24],fake_if
[1024];
299 * Ignore comments and leading whitespace.
302 pnt
= skip_whitespace(pnt
);
303 while( *pnt
&& (*pnt
== ' ' || *pnt
== '\t')) pnt
++;
305 if( *pnt
== '#' ) return;
308 * Now categorize the next token.
311 if (strncmp(pnt
, "mainmenu_name", 13) == 0)
316 else if (strncmp(pnt
, "source", 6) == 0)
319 pnt
= skip_whitespace(pnt
);
323 else if (strncmp(pnt
, "mainmenu_option", 15) == 0)
326 tok
= tok_menuoption
;
329 else if (strncmp(pnt
, "comment", 7) == 0)
334 else if (strncmp(pnt
, "choice", 6) == 0)
339 else if (strncmp(pnt
, "define_bool", 11) == 0)
344 else if (strncmp(pnt
, "bool", 4) == 0)
349 else if (strncmp(pnt
, "tristate", 8) == 0)
354 else if (strncmp(pnt
, "dep_tristate", 12) == 0)
356 tok
= tok_dep_tristate
;
359 else if (strncmp(pnt
, "int", 3) == 0)
364 else if (strncmp(pnt
, "hex", 3) == 0)
369 else if (strncmp(pnt
, "string", 6) == 0)
374 else if (strncmp(pnt
, "if", 2) == 0)
379 else if (strncmp(pnt
, "else", 4) == 0)
384 else if (strncmp(pnt
, "fi", 2) == 0)
389 else if (strncmp(pnt
, "endmenu", 7) == 0)
395 if( tok
== tok_unknown
)
397 if( clast
!= NULL
&& clast
->tok
== tok_if
398 && strcmp(pnt
,"then") == 0) return;
399 if( current_file
!= NULL
)
400 fprintf(stderr
, "unknown command=%s(%s %d)\n", pnt
,
401 current_file
, lineno
);
403 fprintf(stderr
, "unknown command=%s(%d)\n", pnt
,lineno
);
408 * Allocate memory for this item, and attach it to the end of the linked
411 kcfg
= (struct kconfig
*) malloc(sizeof(struct kconfig
));
412 memset(kcfg
, 0, sizeof(struct kconfig
));
421 clast
= config
= kcfg
;
424 pnt
= skip_whitespace(pnt
);
427 * Now parse the remaining parts of the option, and attach the results
433 pnt
= get_qstring(pnt
, &kcfg
->label
);
434 pnt
= get_qstring(pnt
, &kcfg
->optionname
);
435 pnt
= get_string(pnt
, &kcfg
->value
);
437 * Now we need to break apart the individual options into their
438 * own configuration structures.
440 parse_choices(kcfg
, kcfg
->optionname
);
441 free(kcfg
->optionname
);
442 sprintf(tmpbuf
, "tmpvar_%d", choose_number
++);
443 kcfg
->optionname
= strdup(tmpbuf
);
446 pnt
= get_string(pnt
, &kcfg
->optionname
);
447 if(*pnt
== 'y' || *pnt
== 'Y' ) kcfg
->value
= "1";
448 if(*pnt
== 'n' || *pnt
== 'N' ) kcfg
->value
= "0";
449 if(*pnt
== 'm' || *pnt
== 'M' ) kcfg
->value
= "2";
452 pnt
= get_qstring(pnt
, &kcfg
->label
);
456 pnt
= get_qstring(pnt
, &kcfg
->label
);
457 pnt
= get_string(pnt
, &kcfg
->optionname
);
462 pnt
= get_qstring(pnt
, &kcfg
->label
);
463 pnt
= get_string(pnt
, &kcfg
->optionname
);
464 pnt
= get_string(pnt
, &kcfg
->value
);
466 case tok_dep_tristate
:
467 pnt
= get_qstring(pnt
, &kcfg
->label
);
468 pnt
= get_string(pnt
, &kcfg
->optionname
);
469 pnt
= skip_whitespace(pnt
);
470 if( *pnt
== '$') pnt
++;
471 pnt
= get_string(pnt
, &kcfg
->depend
.str
);
474 * Create a conditional for this object's dependency.
476 * We can't use "!= n" because this is internally converted to "!= 0"
477 * and if UMSDOS depends on MSDOS which depends on FAT, then when FAT
478 * is disabled MSDOS has 16 added to its value, making UMSDOS fully
481 * This is more of a hack than a fix. Nested "if" conditionals are
482 * probably affected too - that +/- 16 affects things in too many
483 * places. But this should do for now.
485 sprintf(fake_if
,"[ \"$%s\" = \"y\" -o \"$%s\" = \"m\" ]; then",
486 kcfg
->depend
.str
,kcfg
->depend
.str
);
487 kcfg
->cond
= parse_if(fake_if
);
488 if(kcfg
->cond
== NULL
)
494 pnt
= get_qstring(pnt
, &kcfg
->label
);
495 if( koption
!= NULL
)
497 pnt
= get_qstring(pnt
, &kcfg
->label
);
498 koption
->label
= kcfg
->label
;
503 if( strncmp(pnt
, "next_comment", 12) == 0)
509 pnt
= get_qstring(pnt
, &kcfg
->label
);
518 * Conditionals are different. For the first level parse, only
519 * tok_if and tok_dep_tristate items have a ->cond chain attached.
521 kcfg
->cond
= parse_if(pnt
);
522 if(kcfg
->cond
== NULL
)
535 * Simple function to dump to the screen what the condition chain looks like.
537 void dump_if(struct condition
* cond
)
562 printf("$%s", cond
->variable
.str
);
565 printf("'%s'", cond
->variable
.str
);
576 static int do_source(char * filename
)
581 char * old_file
= 0; /* superfluous, just for gcc */
585 if( strcmp(filename
, "-") == 0 )
588 infile
= fopen(filename
,"r");
591 * If our cwd was in the scripts directory, we might have to go up one
592 * to find the sourced file.
595 strcpy (buffer
, "../");
596 strcat (buffer
, filename
);
597 infile
= fopen(buffer
,"r");
601 fprintf(stderr
,"Unable to open file %s\n", filename
);
606 if( infile
!= stdin
) {
607 old_file
= current_file
;
608 current_file
= filename
;
613 fgets(&buffer
[offset
], sizeof(buffer
) - offset
, infile
);
614 if(feof(infile
)) break;
617 * Strip the trailing return character.
619 pnt
= buffer
+ strlen(buffer
) - 1;
620 if( *pnt
== '\n') *pnt
-- = 0;
624 offset
= pnt
- buffer
;
633 if( infile
!= stdin
) {
634 current_file
= old_file
;
640 int main(int argc
, char * argv
[])
645 struct kconfig
* cfg
;
650 * Read stdin to get the top level script.
654 if( menus_seen
== 0 )
656 fprintf(stderr
,"The config.in file for this platform does not support\n");
657 fprintf(stderr
,"menus.\n");
661 * Input file is now parsed. Next we need to go through and attach
662 * the correct conditions to each of the actual menu items and kill
663 * the if/else/endif tokens from the list. We also flag the menu items
664 * that have other things that depend upon its setting.
666 fix_conditionals(config
);
669 * Finally, we generate the wish script.
671 dump_tk_script(config
);
675 * Now dump what we have so far. This is only for debugging so that
676 * we can display what we think we have in the list.
678 for(cfg
= config
; cfg
; cfg
= cfg
->next
)
681 if(cfg
->cond
!= NULL
&& cfg
->tok
!= tok_if
)
687 printf("main_menuname ");
695 case tok_dep_tristate
:
696 printf("dep_tristate ");
711 printf("menuoption ");
730 printf("%s\n", cfg
->label
);
734 case tok_dep_tristate
:
738 printf("%s %s\n", cfg
->label
, cfg
->optionname
);