source: IOIPSL/trunk/tools/tkparse.c @ 15

Last change on this file since 15 was 15, checked in by bellier, 18 years ago

JB: add tools and examples
dd

File size: 15.9 KB
Line 
1/* parser config.in
2 * $Id$
3 * Version 1.0
4 * Eric Youngdale
5 * 10/95
6 *
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.
10 *
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.
18 *
19 * This file contains the code to do the first parse of config.in.
20 */
21#include <stdlib.h>
22#include <stdio.h>
23#include <string.h>
24#include "tkparse.h"
25
26struct kconfig * config = NULL;
27struct kconfig * clast = NULL;
28struct kconfig * koption = NULL;
29static int lineno = 0;
30static int menus_seen = 0;
31static char * current_file = NULL;
32static int do_source(char * filename);
33static char * get_string(char *pnt, char ** labl);
34static int choose_number = 0;
35
36
37/*
38 * Simple function just to skip over spaces and tabs in config.in.
39 */
40static char * skip_whitespace(char * pnt)
41{
42  while( *pnt && (*pnt == ' ' || *pnt == '\t')) pnt++;
43  return pnt;
44}
45
46/*
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.
49 */
50static struct condition * parse_if(char * pnt)
51{
52  char * opnt;
53  struct condition *list;
54  struct condition *last;
55  struct condition *cpnt;
56  char varname[64];
57  char * pnt1;
58
59  opnt = pnt;
60
61  /*
62   * We need to find the various tokens, and build the linked list.
63   */
64  pnt = skip_whitespace(pnt);
65  if( *pnt != '[' ) return NULL;
66  pnt++;
67  pnt = skip_whitespace(pnt);
68
69  list = last = NULL;
70  while(*pnt && *pnt != ']') {
71
72    pnt = skip_whitespace(pnt);
73    if(*pnt== '\0' || *pnt == ']') break;
74
75    /*
76     * Allocate memory for the token we are about to parse, and insert
77     * it in the linked list.
78     */
79    cpnt = (struct condition *) malloc(sizeof(struct condition));
80    memset(cpnt, 0, sizeof(struct condition));
81    if( last == NULL )
82      {
83        list = last = cpnt;
84      }
85    else
86      {
87        last->next = cpnt;
88        last = cpnt;
89      }
90
91    /*
92     * Determine what type of operation this token represents.
93     */
94    if( *pnt == '-' && pnt[1] == 'a' )
95      {
96        cpnt->op = op_and;
97        pnt += 2;
98        continue;
99      }
100
101    if( *pnt == '-' && pnt[1] == 'o' )
102      {
103        cpnt->op = op_or;
104        pnt += 2;
105        continue;
106      }
107
108    if( *pnt == '!' && pnt[1] == '=' )
109      {
110        cpnt->op = op_neq;
111        pnt += 2;
112        continue;
113      }
114
115    if( *pnt == '=')
116      {
117        cpnt->op = op_eq;
118        pnt += 1;
119        continue;
120      }
121
122    if( *pnt == '!')
123      {
124        cpnt->op = op_bang;
125        pnt += 1;
126        continue;
127      }
128
129    if( *pnt != '"' ) goto error;  /* This cannot be right. */
130    pnt++;
131    if( *pnt == '`' )
132      {
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;
142      }
143    if( *pnt == '$' )
144      {
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;
153      }
154
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;
162  }
163
164  return list;
165
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;
174}
175
176/*
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.
182 */
183static char * get_qstring(char *pnt, char ** labl)
184{
185  char quotechar;
186  char newlabel[1024];
187  char * pnt1;
188  char * pnt2;
189
190  while( *pnt && *pnt != '"' && *pnt != '\'') pnt++;
191  if (*pnt == '\0') return pnt;
192
193  quotechar = *pnt++;
194  pnt1 = newlabel;
195  while(*pnt && *pnt != quotechar && pnt[-1] != '\\')
196    {
197      /*
198       * Quote the character if we need to.
199       */
200      if( *pnt == '"' || *pnt == '\'' || *pnt == '[' || *pnt == ']')
201        *pnt1++ = '\\';
202
203      *pnt1++ = *pnt++;
204    }
205  *pnt1++ = '\0';
206
207  pnt2 = (char *) malloc(strlen(newlabel) + 1);
208  strcpy(pnt2, newlabel);
209  *labl = pnt2;
210
211  /*
212   * Skip over last quote, and whitespace.
213   */
214  pnt++;
215  pnt = skip_whitespace(pnt);
216  return pnt;
217}
218
219static char * parse_choices(struct kconfig * choice_kcfg, char * pnt)
220{
221  struct kconfig * kcfg;
222  int index = 1;
223
224  /*
225   * Choices appear in pairs of strings.  The parse is fairly trivial.
226   */
227  while(1)
228    {
229      pnt = skip_whitespace(pnt);
230      if(*pnt == '\0') break;
231
232      kcfg = (struct kconfig *) malloc(sizeof(struct kconfig));
233      memset(kcfg, 0, sizeof(struct kconfig));
234      kcfg->tok = tok_choice;
235      if( clast != NULL )
236        {
237          clast->next = kcfg;
238          clast = kcfg;
239        }
240      else
241        {
242          clast = config = kcfg;
243        }
244
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;
252    }
253   
254    return pnt;
255}
256
257
258/*
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.
263 */
264static char * get_string(char *pnt, char ** labl)
265{
266  char newlabel[1024];
267  char * pnt1;
268  char * pnt2;
269
270  if (*pnt == '\0') return pnt;
271
272  pnt1 = newlabel;
273  while(*pnt && *pnt != ' ' && *pnt != '\t')
274    {
275      *pnt1++ = *pnt++;
276    }
277  *pnt1++ = '\0';
278
279  pnt2 = (char *) malloc(strlen(newlabel) + 1);
280  strcpy(pnt2, newlabel);
281  *labl = pnt2;
282
283  if( *pnt ) pnt++;
284  return pnt;
285}
286
287
288/*
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.
292 */
293void parse(char * pnt) {
294  enum token tok;
295  struct kconfig * kcfg;
296  char tmpbuf[24],fake_if[1024];
297
298  /*
299   * Ignore comments and leading whitespace.
300   */
301
302  pnt = skip_whitespace(pnt);
303  while( *pnt && (*pnt == ' ' || *pnt == '\t')) pnt++;
304  if(! *pnt ) return;
305  if( *pnt == '#' ) return;
306
307  /*
308   * Now categorize the next token.
309   */
310  tok = tok_unknown;
311  if      (strncmp(pnt, "mainmenu_name", 13) == 0) 
312    {
313      tok = tok_menuname;
314      pnt += 13;
315    }
316  else if      (strncmp(pnt, "source", 6) == 0) 
317    {
318      pnt += 7;
319      pnt = skip_whitespace(pnt);
320      do_source(pnt);
321      return;
322    }
323  else if (strncmp(pnt, "mainmenu_option", 15) == 0) 
324    {
325      menus_seen++;
326      tok = tok_menuoption;
327      pnt += 15;
328    }
329  else if (strncmp(pnt, "$MAKE ", 6) == 0) 
330    {
331      tok = tok_make;
332    }
333  else if (strncmp(pnt, "comment", 7) == 0) 
334    {
335      tok = tok_comment;
336      pnt += 7;
337    }
338  else if (strncmp(pnt, "choice", 6) == 0) 
339    {
340      tok = tok_choose;
341      pnt += 6;
342    }
343  else if (strncmp(pnt, "define_bool", 11) == 0) 
344    {
345      tok = tok_define;
346      pnt += 11;
347    }
348  else if (strncmp(pnt, "bool", 4) == 0) 
349    {
350      tok = tok_bool;
351      pnt += 4;
352    }
353  else if (strncmp(pnt, "tristate", 8) == 0) 
354    {
355      tok = tok_tristate;
356      pnt += 8;
357    }
358  else if (strncmp(pnt, "dep_tristate", 12) == 0) 
359    {
360      tok = tok_dep_tristate;
361      pnt += 12;
362    }
363  else if (strncmp(pnt, "int", 3) == 0) 
364    {
365      tok = tok_int;
366      pnt += 3;
367    }
368  else if (strncmp(pnt, "hex", 3) == 0) 
369    {
370      tok = tok_hex;
371      pnt += 3;
372    }
373  else if (strncmp(pnt, "if", 2) == 0) 
374    {
375      tok = tok_if;
376      pnt += 2;
377    }
378  else if (strncmp(pnt, "else", 4) == 0) 
379    {
380      tok = tok_else;
381      pnt += 4;
382    }
383  else if (strncmp(pnt, "fi", 2) == 0) 
384    {
385      tok = tok_fi;
386      pnt += 2;
387    }
388  else if (strncmp(pnt, "endmenu", 7) == 0) 
389    {
390      tok = tok_endmenu;
391      pnt += 7;
392    }
393
394  if( tok == tok_unknown)
395    {
396      if( clast != NULL && clast->tok == tok_if
397          && strcmp(pnt,"then") == 0) return;
398      if( current_file != NULL )
399        fprintf(stderr, "unknown command=%s(%s %d)\n", pnt,
400                current_file, lineno);
401      else
402        fprintf(stderr, "unknown command=%s(%d)\n", pnt,lineno);
403      return;
404    }
405
406  /*
407   * Allocate memory for this item, and attach it to the end of the linked
408   * list.
409   */
410  kcfg = (struct kconfig *) malloc(sizeof(struct kconfig));
411  memset(kcfg, 0, sizeof(struct kconfig));
412  kcfg->tok = tok;
413  if( clast != NULL )
414    {
415      clast->next = kcfg;
416      clast = kcfg;
417    }
418  else
419    {
420      clast = config = kcfg;
421    }
422
423  pnt = skip_whitespace(pnt);
424
425  /*
426   * Now parse the remaining parts of the option, and attach the results
427   * to the structure.
428   */
429  switch (tok)
430    {
431    case tok_choose:
432      pnt = get_qstring(pnt, &kcfg->label);
433      pnt = get_qstring(pnt, &kcfg->optionname);
434      pnt = get_string(pnt, &kcfg->value);
435      /*
436       * Now we need to break apart the individual options into their
437       * own configuration structures.
438       */
439      parse_choices(kcfg, kcfg->optionname);
440      free(kcfg->optionname);
441      sprintf(tmpbuf, "tmpvar_%d", choose_number++);
442      kcfg->optionname = strdup(tmpbuf);
443      break;
444    case tok_define:
445      pnt = get_string(pnt, &kcfg->optionname);
446      if(*pnt == 'y' || *pnt == 'Y' ) kcfg->value = "1";
447      if(*pnt == 'n' || *pnt == 'N' ) kcfg->value = "0";
448      if(*pnt == 'm' || *pnt == 'M' ) kcfg->value = "2";
449      break;
450    case tok_menuname:
451      pnt = get_qstring(pnt, &kcfg->label);
452      break;
453    case tok_bool:
454    case tok_tristate:
455      pnt = get_qstring(pnt, &kcfg->label);
456      pnt = get_string(pnt, &kcfg->optionname);
457      break;
458    case tok_int:
459    case tok_hex:
460      pnt = get_qstring(pnt, &kcfg->label);
461      pnt = get_string(pnt, &kcfg->optionname);
462      pnt = get_string(pnt, &kcfg->value);
463      break;
464    case tok_dep_tristate:
465      pnt = get_qstring(pnt, &kcfg->label);
466      pnt = get_string(pnt, &kcfg->optionname);
467      pnt = skip_whitespace(pnt);
468      if( *pnt == '$') pnt++;
469      pnt = get_string(pnt, &kcfg->depend.str);
470
471      /*
472       * Create a conditional for this object's dependency.
473       *
474       * We can't use "!= n" because this is internally converted to "!= 0"
475       * and if UMSDOS depends on MSDOS which depends on FAT, then when FAT
476       * is disabled MSDOS has 16 added to its value, making UMSDOS fully
477       * available.  Whew.
478       *
479       * This is more of a hack than a fix.  Nested "if" conditionals are
480       * probably affected too - that +/- 16 affects things in too many
481       * places.  But this should do for now.
482       */
483      sprintf(fake_if,"[ \"$%s\" = \"y\" -o \"$%s\" = \"m\" ]; then",
484                kcfg->depend.str,kcfg->depend.str);
485      kcfg->cond = parse_if(fake_if);
486      if(kcfg->cond == NULL )
487        {
488          exit(1);
489        }
490      break;
491    case tok_comment:
492      pnt = get_qstring(pnt, &kcfg->label);
493      if( koption != NULL )
494        {
495          pnt = get_qstring(pnt, &kcfg->label);
496          koption->label = kcfg->label;
497          koption = NULL;
498        }
499      break;
500    case tok_menuoption:
501      if( strncmp(pnt, "next_comment", 12) == 0)
502        {
503          koption = kcfg;
504        }
505      else
506        {
507          pnt = get_qstring(pnt, &kcfg->label);
508        }
509      break;
510    case tok_make:
511      kcfg->value=strdup(pnt);
512      break;
513    case tok_else:
514    case tok_fi:
515    case tok_endmenu:
516      break;
517    case tok_if:
518      /*
519       * Conditionals are different.  For the first level parse, only
520       * tok_if and tok_dep_tristate items have a ->cond chain attached.
521       */
522      kcfg->cond = parse_if(pnt);
523      if(kcfg->cond == NULL )
524        {
525          exit(1);
526        }
527      break;
528    default:
529      exit(0);
530    }
531   
532    return;
533}
534
535/*
536 * Simple function to dump to the screen what the condition chain looks like.
537 */
538void dump_if(struct condition * cond)
539{
540  printf(" ");
541  while(cond != NULL )
542    {
543      switch(cond->op){
544      case op_eq:
545        printf(" = ");
546        break;
547      case op_bang:
548        printf(" ! ");
549        break;
550      case op_neq:
551        printf(" != ");
552        break;
553      case op_and:
554        printf(" -a ");
555        break;
556      case op_lparen:
557        printf("(");
558        break;
559      case op_rparen:
560        printf(")");
561        break;
562      case op_variable:
563        printf("$%s", cond->variable.str);
564        break;
565      case op_constant:
566        printf("'%s'", cond->variable.str);
567        break;
568      default:
569        break;
570      }
571      cond = cond->next;
572    }
573
574  printf("\n");
575}
576
577static int do_source(char * filename)
578{
579  char buffer[1024];
580  int  offset;
581  int old_lineno;
582  char * old_file;
583  char * pnt;
584  FILE * infile;
585
586  if( strcmp(filename, "-") == 0 )
587    infile = stdin;
588  else
589    infile = fopen(filename,"r");
590
591  /*
592   * If our cwd was in the scripts directory, we might have to go up one
593   * to find the sourced file.
594   */
595  if(!infile) {
596    strcpy (buffer, "../");
597    strcat (buffer, filename);
598    infile = fopen(buffer,"r");
599  }
600
601  if(!infile) {
602    fprintf(stderr,"Unable to open file %s\n", filename);
603    return 1;
604  }
605  old_lineno = lineno;
606  lineno = 0;
607  if( infile != stdin ) {
608    old_file = current_file;
609    current_file = filename;
610  }
611  offset = 0;
612  while(1)
613    {
614      fgets(&buffer[offset], sizeof(buffer) - offset, infile);
615      if(feof(infile)) break;
616
617      /*
618       * Strip the trailing return character.
619       */
620      pnt = buffer + strlen(buffer) - 1;
621      if( *pnt == '\n') *pnt-- = 0;
622      lineno++;
623      if( *pnt == '\\' )
624        {
625          offset = pnt - buffer;
626        }
627      else
628        {
629          parse(buffer);
630          offset = 0;
631        }
632    }
633  fclose(infile);
634  if( infile != stdin ) {
635    current_file = old_file;
636  }
637  lineno = old_lineno;
638  return 0;
639}
640
641int main(int argc, char * argv[])
642{
643#if 0
644  char buffer[1024];
645  char * pnt;
646  struct kconfig * cfg;
647  int    i;
648#endif
649
650  /*
651   * Read stdin to get the top level script.
652   */
653  do_source("-");
654
655  if( menus_seen == 0 )
656    {
657      fprintf(stderr,"The config.in file for this platform does not support\n");
658      fprintf(stderr,"menus.\n");
659      exit(1);
660    }
661  /*
662   * Input file is now parsed.  Next we need to go through and attach
663   * the correct conditions to each of the actual menu items and kill
664   * the if/else/endif tokens from the list.  We also flag the menu items
665   * that have other things that depend upon its setting.
666   */
667  fix_conditionals(config);
668
669  /*
670   * Finally, we generate the wish script.
671   */
672  dump_tk_script(config);
673
674#if 0
675  /*
676   * Now dump what we have so far.  This is only for debugging so that
677   * we can display what we think we have in the list.
678   */
679  for(cfg = config; cfg; cfg = cfg->next)
680    {
681
682      if(cfg->cond != NULL && cfg->tok != tok_if)
683        dump_if(cfg->cond);
684
685      switch(cfg->tok)
686        {
687        case tok_menuname:
688          printf("main_menuname ");
689          break;
690        case tok_bool:
691          printf("bool ");
692          break;
693        case tok_tristate:
694          printf("tristate ");
695          break;
696        case tok_dep_tristate:
697          printf("dep_tristate ");
698          break;
699        case tok_int:
700          printf("int ");
701          break;
702        case tok_hex:
703          printf("hex ");
704          break;
705        case tok_comment:
706          printf("comment ");
707          break;
708        case tok_menuoption:
709          printf("menuoption ");
710          break;
711        case tok_else:
712          printf("else");
713          break;
714        case tok_fi:
715          printf("fi");
716          break;
717        case tok_if:
718          printf("if");
719          break;
720        default:
721        }
722
723      switch(cfg->tok)
724        {
725        case tok_menuoption:
726        case tok_comment:
727        case tok_menuname:
728          printf("%s\n", cfg->label);
729          break;
730        case tok_bool:
731        case tok_tristate:
732        case tok_dep_tristate:
733        case tok_int:
734        case tok_hex:
735          printf("%s %s\n", cfg->label, cfg->optionname);
736          break;
737        case tok_if:
738          dump_if(cfg->cond);
739          break;
740        case tok_nop:
741        case tok_endmenu:
742          break;
743        default:
744          printf("\n");
745        }
746    }
747#endif
748
749  return 0;
750
751}
Note: See TracBrowser for help on using the repository browser.