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

Last change on this file since 451 was 386, checked in by bellier, 16 years ago

Added CeCILL License information

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