linux-old/scripts/tkparse.c
<<
>>
Prefs
   1/* parser config.in
   2 *
   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, "string", 6) == 0) 
 374    {
 375      tok = tok_string;
 376      pnt += 6;
 377    }
 378  else if (strncmp(pnt, "if", 2) == 0) 
 379    {
 380      tok = tok_if;
 381      pnt += 2;
 382    }
 383  else if (strncmp(pnt, "else", 4) == 0) 
 384    {
 385      tok = tok_else;
 386      pnt += 4;
 387    }
 388  else if (strncmp(pnt, "fi", 2) == 0) 
 389    {
 390      tok = tok_fi;
 391      pnt += 2;
 392    }
 393  else if (strncmp(pnt, "endmenu", 7) == 0) 
 394    {
 395      tok = tok_endmenu;
 396      pnt += 7;
 397    }
 398
 399  if( tok == tok_unknown)
 400    {
 401      if( clast != NULL && clast->tok == tok_if 
 402          && strcmp(pnt,"then") == 0) return;
 403      if( current_file != NULL )
 404        fprintf(stderr, "unknown command=%s(%s %d)\n", pnt,
 405                current_file, lineno);
 406      else
 407        fprintf(stderr, "unknown command=%s(%d)\n", pnt,lineno);
 408      return;
 409    }
 410
 411  /*
 412   * Allocate memory for this item, and attach it to the end of the linked
 413   * list.
 414   */
 415  kcfg = (struct kconfig *) malloc(sizeof(struct kconfig));
 416  memset(kcfg, 0, sizeof(struct kconfig));
 417  kcfg->tok = tok;
 418  if( clast != NULL )
 419    {
 420      clast->next = kcfg;
 421      clast = kcfg;
 422    }
 423  else
 424    {
 425      clast = config = kcfg;
 426    }
 427
 428  pnt = skip_whitespace(pnt);
 429
 430  /*
 431   * Now parse the remaining parts of the option, and attach the results
 432   * to the structure.
 433   */
 434  switch (tok)
 435    {
 436    case tok_choose:
 437      pnt = get_qstring(pnt, &kcfg->label);
 438      pnt = get_qstring(pnt, &kcfg->optionname);
 439      pnt = get_string(pnt, &kcfg->value);
 440      /*
 441       * Now we need to break apart the individual options into their
 442       * own configuration structures.
 443       */
 444      parse_choices(kcfg, kcfg->optionname);
 445      free(kcfg->optionname);
 446      sprintf(tmpbuf, "tmpvar_%d", choose_number++);
 447      kcfg->optionname = strdup(tmpbuf);
 448      break;
 449    case tok_define:
 450      pnt = get_string(pnt, &kcfg->optionname);
 451      if(*pnt == 'y' || *pnt == 'Y' ) kcfg->value = "1";
 452      if(*pnt == 'n' || *pnt == 'N' ) kcfg->value = "0";
 453      if(*pnt == 'm' || *pnt == 'M' ) kcfg->value = "2";
 454      break;
 455    case tok_menuname:
 456      pnt = get_qstring(pnt, &kcfg->label);
 457      break;
 458    case tok_bool:
 459    case tok_tristate:
 460      pnt = get_qstring(pnt, &kcfg->label);
 461      pnt = get_string(pnt, &kcfg->optionname);
 462      break;
 463    case tok_int:
 464    case tok_hex:
 465    case tok_string:
 466      pnt = get_qstring(pnt, &kcfg->label);
 467      pnt = get_string(pnt, &kcfg->optionname);
 468      pnt = get_string(pnt, &kcfg->value);
 469      break;
 470    case tok_dep_tristate:
 471      pnt = get_qstring(pnt, &kcfg->label);
 472      pnt = get_string(pnt, &kcfg->optionname);
 473      pnt = skip_whitespace(pnt);
 474      if( *pnt == '$') pnt++;
 475      pnt = get_string(pnt, &kcfg->depend.str);
 476
 477      /*
 478       * Create a conditional for this object's dependency.
 479       *
 480       * We can't use "!= n" because this is internally converted to "!= 0"
 481       * and if UMSDOS depends on MSDOS which depends on FAT, then when FAT
 482       * is disabled MSDOS has 16 added to its value, making UMSDOS fully
 483       * available.  Whew.
 484       *
 485       * This is more of a hack than a fix.  Nested "if" conditionals are
 486       * probably affected too - that +/- 16 affects things in too many
 487       * places.  But this should do for now.
 488       */
 489      sprintf(fake_if,"[ \"$%s\" = \"y\" -o \"$%s\" = \"m\" ]; then",
 490                kcfg->depend.str,kcfg->depend.str);
 491      kcfg->cond = parse_if(fake_if);
 492      if(kcfg->cond == NULL )
 493        {
 494          exit(1);
 495        }
 496      break;
 497    case tok_comment:
 498      pnt = get_qstring(pnt, &kcfg->label);
 499      if( koption != NULL )
 500        {
 501          pnt = get_qstring(pnt, &kcfg->label);
 502          koption->label = kcfg->label;
 503          koption = NULL;
 504        }
 505      break;
 506    case tok_menuoption:
 507      if( strncmp(pnt, "next_comment", 12) == 0)
 508        {
 509          koption = kcfg;
 510        }
 511      else
 512        {
 513          pnt = get_qstring(pnt, &kcfg->label);
 514        }
 515      break;
 516    case tok_make:
 517      kcfg->value=strdup(pnt);
 518      break;
 519    case tok_else:
 520    case tok_fi:
 521    case tok_endmenu:
 522      break;
 523    case tok_if:
 524      /*
 525       * Conditionals are different.  For the first level parse, only
 526       * tok_if and tok_dep_tristate items have a ->cond chain attached.
 527       */
 528      kcfg->cond = parse_if(pnt);
 529      if(kcfg->cond == NULL )
 530        {
 531          exit(1);
 532        }
 533      break;
 534    default:
 535      exit(0);
 536    }
 537    
 538    return;
 539}
 540
 541/*
 542 * Simple function to dump to the screen what the condition chain looks like.
 543 */
 544void dump_if(struct condition * cond)
 545{
 546  printf(" ");
 547  while(cond != NULL )
 548    {
 549      switch(cond->op){
 550      case op_eq:
 551        printf(" = ");
 552        break;
 553      case op_bang:
 554        printf(" ! ");
 555        break;
 556      case op_neq:
 557        printf(" != ");
 558        break;
 559      case op_and:
 560        printf(" -a ");
 561        break;
 562      case op_lparen:
 563        printf("(");
 564        break;
 565      case op_rparen:
 566        printf(")");
 567        break;
 568      case op_variable:
 569        printf("$%s", cond->variable.str);
 570        break;
 571      case op_constant:
 572        printf("'%s'", cond->variable.str);
 573        break;
 574      default:
 575        break;
 576      }
 577      cond = cond->next;
 578    }
 579
 580  printf("\n");
 581}
 582
 583static int do_source(char * filename)
 584{
 585  char buffer[1024];
 586  int  offset;
 587  int old_lineno;
 588  char * old_file;
 589  char * pnt;
 590  FILE * infile;
 591
 592  if( strcmp(filename, "-") == 0 )
 593    infile = stdin;
 594  else
 595    infile = fopen(filename,"r");
 596
 597  /*
 598   * If our cwd was in the scripts directory, we might have to go up one
 599   * to find the sourced file.
 600   */
 601  if(!infile) {
 602    strcpy (buffer, "../");
 603    strcat (buffer, filename);
 604    infile = fopen(buffer,"r");
 605  }
 606
 607  if(!infile) {
 608    fprintf(stderr,"Unable to open file %s\n", filename);
 609    return 1;
 610  }
 611  old_lineno = lineno;
 612  lineno = 0;
 613  if( infile != stdin ) {
 614    old_file = current_file;
 615    current_file = filename;
 616  }
 617  offset = 0;
 618  while(1)
 619    {
 620      fgets(&buffer[offset], sizeof(buffer) - offset, infile);
 621      if(feof(infile)) break;
 622
 623      /*
 624       * Strip the trailing return character.
 625       */
 626      pnt = buffer + strlen(buffer) - 1;
 627      if( *pnt == '\n') *pnt-- = 0;
 628      lineno++;
 629      if( *pnt == '\\' )
 630        {
 631          offset = pnt - buffer;
 632        }
 633      else
 634        {
 635          parse(buffer);
 636          offset = 0;
 637        }
 638    }
 639  fclose(infile);
 640  if( infile != stdin ) {
 641    current_file = old_file;
 642  }
 643  lineno = old_lineno;
 644  return 0;
 645}
 646
 647int main(int argc, char * argv[])
 648{
 649#if 0
 650  char buffer[1024];
 651  char * pnt;
 652  struct kconfig * cfg;
 653  int    i;
 654#endif
 655
 656  /*
 657   * Read stdin to get the top level script.
 658   */
 659  do_source("-");
 660
 661  if( menus_seen == 0 )
 662    {
 663      fprintf(stderr,"The config.in file for this platform does not support\n");
 664      fprintf(stderr,"menus.\n");
 665      exit(1);
 666    }
 667  /*
 668   * Input file is now parsed.  Next we need to go through and attach
 669   * the correct conditions to each of the actual menu items and kill
 670   * the if/else/endif tokens from the list.  We also flag the menu items
 671   * that have other things that depend upon its setting.
 672   */
 673  fix_conditionals(config);
 674
 675  /*
 676   * Finally, we generate the wish script.
 677   */
 678  dump_tk_script(config);
 679
 680#if 0
 681  /*
 682   * Now dump what we have so far.  This is only for debugging so that
 683   * we can display what we think we have in the list.
 684   */
 685  for(cfg = config; cfg; cfg = cfg->next)
 686    {
 687
 688      if(cfg->cond != NULL && cfg->tok != tok_if)
 689        dump_if(cfg->cond);
 690
 691      switch(cfg->tok)
 692        {
 693        case tok_menuname:
 694          printf("main_menuname ");
 695          break;
 696        case tok_bool:
 697          printf("bool ");
 698          break;
 699        case tok_tristate:
 700          printf("tristate ");
 701          break;
 702        case tok_dep_tristate:
 703          printf("dep_tristate ");
 704          break;
 705        case tok_int:
 706          printf("int ");
 707          break;
 708        case tok_hex:
 709          printf("hex ");
 710          break;
 711        case tok_string:
 712          printf("istring ");
 713          break;
 714        case tok_comment:
 715          printf("comment ");
 716          break;
 717        case tok_menuoption:
 718          printf("menuoption ");
 719          break;
 720        case tok_else:
 721          printf("else");
 722          break;
 723        case tok_fi:
 724          printf("fi");
 725          break;
 726        case tok_if:
 727          printf("if");
 728          break;
 729        default:
 730        }
 731
 732      switch(cfg->tok)
 733        {
 734        case tok_menuoption:
 735        case tok_comment:
 736        case tok_menuname:
 737          printf("%s\n", cfg->label);
 738          break;
 739        case tok_bool:
 740        case tok_tristate:
 741        case tok_dep_tristate:
 742        case tok_int:
 743        case tok_hex:
 744        case tok_string:
 745          printf("%s %s\n", cfg->label, cfg->optionname);
 746          break;
 747        case tok_if:
 748          dump_if(cfg->cond);
 749          break;
 750        case tok_nop:
 751        case tok_endmenu:
 752          break;
 753        default:
 754          printf("\n");
 755        }
 756    }
 757#endif
 758
 759  return 0;
 760
 761}
 762
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.