Import 2.1.115pre2
[davej-history.git] / scripts / tkparse.c
blobf91d0f3553ba03ce190e206ff59138aead2ccc90
1 /* parser config.in
3 * Version 1.0
4 * Eric Youngdale
5 * 10/95
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.
21 #include <stdlib.h>
22 #include <stdio.h>
23 #include <string.h>
24 #include "tkparse.h"
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++;
43 return 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)
52 char * opnt;
53 struct condition *list;
54 struct condition *last;
55 struct condition *cpnt;
56 char varname[64];
57 char * pnt1;
59 opnt = pnt;
62 * We need to find the various tokens, and build the linked list.
64 pnt = skip_whitespace(pnt);
65 if( *pnt != '[' ) return NULL;
66 pnt++;
67 pnt = skip_whitespace(pnt);
69 list = last = NULL;
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));
81 if( last == NULL )
83 list = last = cpnt;
85 else
87 last->next = cpnt;
88 last = cpnt;
92 * Determine what type of operation this token represents.
94 if( *pnt == '-' && pnt[1] == 'a' )
96 cpnt->op = op_and;
97 pnt += 2;
98 continue;
101 if( *pnt == '-' && pnt[1] == 'o' )
103 cpnt->op = op_or;
104 pnt += 2;
105 continue;
108 if( *pnt == '!' && pnt[1] == '=' )
110 cpnt->op = op_neq;
111 pnt += 2;
112 continue;
115 if( *pnt == '=')
117 cpnt->op = op_eq;
118 pnt += 1;
119 continue;
122 if( *pnt == '!')
124 cpnt->op = op_bang;
125 pnt += 1;
126 continue;
129 if( *pnt != '"' ) goto error; /* This cannot be right. */
130 pnt++;
131 if( *pnt == '`' )
133 cpnt->op = op_shellcmd;
134 pnt1 = varname;
135 pnt++;
136 while(*pnt && *pnt != '`') *pnt1++ = *pnt++;
137 *pnt1++ = '\0';
138 cpnt->variable.str = strdup(varname);
139 if( *pnt == '`' ) pnt++;
140 if( *pnt == '"' ) pnt++;
141 continue;
143 if( *pnt == '$' )
145 cpnt->op = op_variable;
146 pnt1 = varname;
147 pnt++;
148 while(*pnt && *pnt != '"') *pnt1++ = *pnt++;
149 *pnt1++ = '\0';
150 cpnt->variable.str = strdup(varname);
151 if( *pnt == '"' ) pnt++;
152 continue;
155 cpnt->op = op_constant;
156 pnt1 = varname;
157 while(*pnt && *pnt != '"') *pnt1++ = *pnt++;
158 *pnt1++ = '\0';
159 cpnt->variable.str = strdup(varname);
160 if( *pnt == '"' ) pnt++;
161 continue;
164 return list;
166 error:
167 if(current_file != NULL)
168 fprintf(stderr,
169 "Bad if clause at line %d(%s):%s\n", lineno, current_file, opnt);
170 else
171 fprintf(stderr,
172 "Bad if clause at line %d:%s\n", lineno, opnt);
173 return NULL;
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
181 * wish scripts.
183 static char * get_qstring(char *pnt, char ** labl)
185 char quotechar;
186 char newlabel[1024];
187 char * pnt1;
188 char * pnt2;
190 while( *pnt && *pnt != '"' && *pnt != '\'') pnt++;
191 if (*pnt == '\0') return pnt;
193 quotechar = *pnt++;
194 pnt1 = newlabel;
195 while(*pnt && *pnt != quotechar && pnt[-1] != '\\')
198 * Quote the character if we need to.
200 if( *pnt == '"' || *pnt == '\'' || *pnt == '[' || *pnt == ']')
201 *pnt1++ = '\\';
203 *pnt1++ = *pnt++;
205 *pnt1++ = '\0';
207 pnt2 = (char *) malloc(strlen(newlabel) + 1);
208 strcpy(pnt2, newlabel);
209 *labl = pnt2;
212 * Skip over last quote, and whitespace.
214 pnt++;
215 pnt = skip_whitespace(pnt);
216 return pnt;
219 static char * parse_choices(struct kconfig * choice_kcfg, char * pnt)
221 struct kconfig * kcfg;
222 int index = 1;
225 * Choices appear in pairs of strings. The parse is fairly trivial.
227 while(1)
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;
235 if( clast != NULL )
237 clast->next = kcfg;
238 clast = kcfg;
240 else
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;
254 return pnt;
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)
266 char newlabel[1024];
267 char * pnt1;
268 char * pnt2;
270 if (*pnt == '\0') return pnt;
272 pnt1 = newlabel;
273 while(*pnt && *pnt != ' ' && *pnt != '\t')
275 *pnt1++ = *pnt++;
277 *pnt1++ = '\0';
279 pnt2 = (char *) malloc(strlen(newlabel) + 1);
280 strcpy(pnt2, newlabel);
281 *labl = pnt2;
283 if( *pnt ) pnt++;
284 return pnt;
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) {
294 enum token tok;
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++;
304 if(! *pnt ) return;
305 if( *pnt == '#' ) return;
308 * Now categorize the next token.
310 tok = tok_unknown;
311 if (strncmp(pnt, "mainmenu_name", 13) == 0)
313 tok = tok_menuname;
314 pnt += 13;
316 else if (strncmp(pnt, "source", 6) == 0)
318 pnt += 7;
319 pnt = skip_whitespace(pnt);
320 do_source(pnt);
321 return;
323 else if (strncmp(pnt, "mainmenu_option", 15) == 0)
325 menus_seen++;
326 tok = tok_menuoption;
327 pnt += 15;
329 else if (strncmp(pnt, "comment", 7) == 0)
331 tok = tok_comment;
332 pnt += 7;
334 else if (strncmp(pnt, "choice", 6) == 0)
336 tok = tok_choose;
337 pnt += 6;
339 else if (strncmp(pnt, "define_bool", 11) == 0)
341 tok = tok_define;
342 pnt += 11;
344 else if (strncmp(pnt, "bool", 4) == 0)
346 tok = tok_bool;
347 pnt += 4;
349 else if (strncmp(pnt, "tristate", 8) == 0)
351 tok = tok_tristate;
352 pnt += 8;
354 else if (strncmp(pnt, "dep_tristate", 12) == 0)
356 tok = tok_dep_tristate;
357 pnt += 12;
359 else if (strncmp(pnt, "int", 3) == 0)
361 tok = tok_int;
362 pnt += 3;
364 else if (strncmp(pnt, "hex", 3) == 0)
366 tok = tok_hex;
367 pnt += 3;
369 else if (strncmp(pnt, "string", 6) == 0)
371 tok = tok_string;
372 pnt += 6;
374 else if (strncmp(pnt, "if", 2) == 0)
376 tok = tok_if;
377 pnt += 2;
379 else if (strncmp(pnt, "else", 4) == 0)
381 tok = tok_else;
382 pnt += 4;
384 else if (strncmp(pnt, "fi", 2) == 0)
386 tok = tok_fi;
387 pnt += 2;
389 else if (strncmp(pnt, "endmenu", 7) == 0)
391 tok = tok_endmenu;
392 pnt += 7;
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);
402 else
403 fprintf(stderr, "unknown command=%s(%d)\n", pnt,lineno);
404 return;
408 * Allocate memory for this item, and attach it to the end of the linked
409 * list.
411 kcfg = (struct kconfig *) malloc(sizeof(struct kconfig));
412 memset(kcfg, 0, sizeof(struct kconfig));
413 kcfg->tok = tok;
414 if( clast != NULL )
416 clast->next = kcfg;
417 clast = kcfg;
419 else
421 clast = config = kcfg;
424 pnt = skip_whitespace(pnt);
427 * Now parse the remaining parts of the option, and attach the results
428 * to the structure.
430 switch (tok)
432 case tok_choose:
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);
444 break;
445 case tok_define:
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";
450 break;
451 case tok_menuname:
452 pnt = get_qstring(pnt, &kcfg->label);
453 break;
454 case tok_bool:
455 case tok_tristate:
456 pnt = get_qstring(pnt, &kcfg->label);
457 pnt = get_string(pnt, &kcfg->optionname);
458 break;
459 case tok_int:
460 case tok_hex:
461 case tok_string:
462 pnt = get_qstring(pnt, &kcfg->label);
463 pnt = get_string(pnt, &kcfg->optionname);
464 pnt = get_string(pnt, &kcfg->value);
465 break;
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
479 * available. Whew.
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 )
490 exit(1);
492 break;
493 case tok_comment:
494 pnt = get_qstring(pnt, &kcfg->label);
495 if( koption != NULL )
497 pnt = get_qstring(pnt, &kcfg->label);
498 koption->label = kcfg->label;
499 koption = NULL;
501 break;
502 case tok_menuoption:
503 if( strncmp(pnt, "next_comment", 12) == 0)
505 koption = kcfg;
507 else
509 pnt = get_qstring(pnt, &kcfg->label);
511 break;
512 case tok_else:
513 case tok_fi:
514 case tok_endmenu:
515 break;
516 case tok_if:
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 )
524 exit(1);
526 break;
527 default:
528 exit(0);
531 return;
535 * Simple function to dump to the screen what the condition chain looks like.
537 void dump_if(struct condition * cond)
539 printf(" ");
540 while(cond != NULL )
542 switch(cond->op){
543 case op_eq:
544 printf(" = ");
545 break;
546 case op_bang:
547 printf(" ! ");
548 break;
549 case op_neq:
550 printf(" != ");
551 break;
552 case op_and:
553 printf(" -a ");
554 break;
555 case op_lparen:
556 printf("(");
557 break;
558 case op_rparen:
559 printf(")");
560 break;
561 case op_variable:
562 printf("$%s", cond->variable.str);
563 break;
564 case op_constant:
565 printf("'%s'", cond->variable.str);
566 break;
567 default:
568 break;
570 cond = cond->next;
573 printf("\n");
576 static int do_source(char * filename)
578 char buffer[1024];
579 int offset;
580 int old_lineno;
581 char * old_file = 0; /* superfluous, just for gcc */
582 char * pnt;
583 FILE * infile;
585 if( strcmp(filename, "-") == 0 )
586 infile = stdin;
587 else
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.
594 if(!infile) {
595 strcpy (buffer, "../");
596 strcat (buffer, filename);
597 infile = fopen(buffer,"r");
600 if(!infile) {
601 fprintf(stderr,"Unable to open file %s\n", filename);
602 return 1;
604 old_lineno = lineno;
605 lineno = 0;
606 if( infile != stdin ) {
607 old_file = current_file;
608 current_file = filename;
610 offset = 0;
611 while(1)
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;
621 lineno++;
622 if( *pnt == '\\' )
624 offset = pnt - buffer;
626 else
628 parse(buffer);
629 offset = 0;
632 fclose(infile);
633 if( infile != stdin ) {
634 current_file = old_file;
636 lineno = old_lineno;
637 return 0;
640 int main(int argc, char * argv[])
642 #if 0
643 char buffer[1024];
644 char * pnt;
645 struct kconfig * cfg;
646 int i;
647 #endif
650 * Read stdin to get the top level script.
652 do_source("-");
654 if( menus_seen == 0 )
656 fprintf(stderr,"The config.in file for this platform does not support\n");
657 fprintf(stderr,"menus.\n");
658 exit(1);
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);
673 #if 0
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)
682 dump_if(cfg->cond);
684 switch(cfg->tok)
686 case tok_menuname:
687 printf("main_menuname ");
688 break;
689 case tok_bool:
690 printf("bool ");
691 break;
692 case tok_tristate:
693 printf("tristate ");
694 break;
695 case tok_dep_tristate:
696 printf("dep_tristate ");
697 break;
698 case tok_int:
699 printf("int ");
700 break;
701 case tok_hex:
702 printf("hex ");
703 break;
704 case tok_string:
705 printf("istring ");
706 break;
707 case tok_comment:
708 printf("comment ");
709 break;
710 case tok_menuoption:
711 printf("menuoption ");
712 break;
713 case tok_else:
714 printf("else");
715 break;
716 case tok_fi:
717 printf("fi");
718 break;
719 case tok_if:
720 printf("if");
721 break;
722 default:
725 switch(cfg->tok)
727 case tok_menuoption:
728 case tok_comment:
729 case tok_menuname:
730 printf("%s\n", cfg->label);
731 break;
732 case tok_bool:
733 case tok_tristate:
734 case tok_dep_tristate:
735 case tok_int:
736 case tok_hex:
737 case tok_string:
738 printf("%s %s\n", cfg->label, cfg->optionname);
739 break;
740 case tok_if:
741 dump_if(cfg->cond);
742 break;
743 case tok_nop:
744 case tok_endmenu:
745 break;
746 default:
747 printf("\n");
750 #endif
752 return 0;