root/scripts/tkparse.c

/* [previous][next][first][last][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. skip_whitespace
  2. parse_if
  3. get_qstring
  4. parse_choices
  5. get_string
  6. parse
  7. dump_if
  8. do_source
  9. main

   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 
  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;
  35 
  36 
  37 /*
  38  * Simple function just to skip over spaces and tabs in config.in.
  39  */
  40 static char * skip_whitespace(char * pnt)
     /* [previous][next][first][last][top][bottom][index][help] */
  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  */
  50 static struct condition * parse_if(char * pnt)
     /* [previous][next][first][last][top][bottom][index][help] */
  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  */
 183 static char * get_qstring(char *pnt, char ** labl)
     /* [previous][next][first][last][top][bottom][index][help] */
 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 
 219 static char * parse_choices(struct kconfig * choice_kcfg, char * pnt)
     /* [previous][next][first][last][top][bottom][index][help] */
 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  */
 264 static char * get_string(char *pnt, char ** labl)
     /* [previous][next][first][last][top][bottom][index][help] */
 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  */
 293 void parse(char * pnt) {
     /* [previous][next][first][last][top][bottom][index][help] */
 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  */
 538 void dump_if(struct condition * cond)
     /* [previous][next][first][last][top][bottom][index][help] */
 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 
 577 static int do_source(char * filename)
     /* [previous][next][first][last][top][bottom][index][help] */
 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 
 641 int main(int argc, char * argv[])
     /* [previous][next][first][last][top][bottom][index][help] */
 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 it's 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 }

/* [previous][next][first][last][top][bottom][index][help] */