root/scripts/tkparse.c

/* [<][>][^][v][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)
     /* [<][>][^][v][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)
     /* [<][>][^][v][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 = 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 = 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 = 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)
     /* [<][>][^][v][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)
     /* [<][>][^][v][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 }
 255 
 256 
 257 /*
 258  * This function grabs one text token from the input buffer
 259  * and returns a pointer to a copy of just the identifier.
 260  * This can be either a variable name (i.e. CONFIG_NET),
 261  * or it could be the default value for the option.
 262  */
 263 static char * get_string(char *pnt, char ** labl)
     /* [<][>][^][v][top][bottom][index][help] */
 264 {
 265   char quotechar;
 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 int parse(char * pnt) {
     /* [<][>][^][v][top][bottom][index][help] */
 294   enum token tok;
 295   struct kconfig * kcfg;
 296   char tmpbuf[24];
 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 -C drivers/sound", 22) == 0) 
 330     {
 331       pnt += 22;
 332       tok = tok_sound;
 333     }
 334   else if (strncmp(pnt, "comment", 7) == 0) 
 335     {
 336       tok = tok_comment;
 337       pnt += 7;
 338     }
 339   else if (strncmp(pnt, "choice", 6) == 0) 
 340     {
 341       tok = tok_choose;
 342       pnt += 6;
 343     }
 344   else if (strncmp(pnt, "define_bool", 11) == 0) 
 345     {
 346       tok = tok_define;
 347       pnt += 11;
 348     }
 349   else if (strncmp(pnt, "bool", 4) == 0) 
 350     {
 351       tok = tok_bool;
 352       pnt += 4;
 353     }
 354   else if (strncmp(pnt, "tristate", 8) == 0) 
 355     {
 356       tok = tok_tristate;
 357       pnt += 8;
 358     }
 359   else if (strncmp(pnt, "dep_tristate", 12) == 0) 
 360     {
 361       tok = tok_dep_tristate;
 362       pnt += 12;
 363     }
 364   else if (strncmp(pnt, "int", 3) == 0) 
 365     {
 366       tok = tok_int;
 367       pnt += 3;
 368     }
 369   else if (strncmp(pnt, "if", 2) == 0) 
 370     {
 371       tok = tok_if;
 372       pnt += 2;
 373     }
 374   else if (strncmp(pnt, "else", 4) == 0) 
 375     {
 376       tok = tok_else;
 377       pnt += 4;
 378     }
 379   else if (strncmp(pnt, "fi", 2) == 0) 
 380     {
 381       tok = tok_fi;
 382       pnt += 2;
 383     }
 384   else if (strncmp(pnt, "endmenu", 7) == 0) 
 385     {
 386       tok = tok_endmenu;
 387       pnt += 7;
 388     }
 389 
 390   if( tok == tok_unknown)
 391     {
 392       if( clast != NULL && clast->tok == tok_if 
 393           && strcmp(pnt,"then") == 0) return 0;
 394       if( current_file != NULL )
 395         fprintf(stderr, "unknown command=%s(%s %d)\n", pnt,
 396                 current_file, lineno);
 397       else
 398         fprintf(stderr, "unknown command=%s(%d)\n", pnt,lineno);
 399       return 1;
 400     }
 401 
 402   /*
 403    * Allocate memory for this item, and attach it to the end of the linked
 404    * list.
 405    */
 406   kcfg = (struct kconfig *) malloc(sizeof(struct kconfig));
 407   memset(kcfg, 0, sizeof(struct kconfig));
 408   kcfg->tok = tok;
 409   if( clast != NULL )
 410     {
 411       clast->next = kcfg;
 412       clast = kcfg;
 413     }
 414   else
 415     {
 416       clast = config = kcfg;
 417     }
 418 
 419   pnt = skip_whitespace(pnt);
 420 
 421   /*
 422    * Now parse the remaining parts of the option, and attach the results
 423    * to the structure.
 424    */
 425   switch (tok)
 426     {
 427     case tok_choose:
 428       pnt = get_qstring(pnt, &kcfg->label);
 429       pnt = get_qstring(pnt, &kcfg->optionname);
 430       pnt = get_string(pnt, &kcfg->value);
 431       /*
 432        * Now we need to break apart the individual options into their
 433        * own configuration structures.
 434        */
 435       parse_choices(kcfg, kcfg->optionname);
 436       free(kcfg->optionname);
 437       sprintf(tmpbuf, "tmpvar_%d", choose_number++);
 438       kcfg->optionname = strdup(tmpbuf);
 439       break;
 440     case tok_define:
 441       pnt = get_string(pnt, &kcfg->optionname);
 442       if(*pnt == 'y' || *pnt == 'Y' ) kcfg->value = "1";
 443       if(*pnt == 'n' || *pnt == 'N' ) kcfg->value = "0";
 444       if(*pnt == 'm' || *pnt == 'M' ) kcfg->value = "2";
 445       break;
 446     case tok_menuname:
 447       pnt = get_qstring(pnt, &kcfg->label);
 448       break;
 449     case tok_bool:
 450     case tok_tristate:
 451     case tok_int:
 452       pnt = get_qstring(pnt, &kcfg->label);
 453       pnt = get_string(pnt, &kcfg->optionname);
 454       break;
 455     case tok_dep_tristate:
 456       pnt = get_qstring(pnt, &kcfg->label);
 457       pnt = get_string(pnt, &kcfg->optionname);
 458       pnt = skip_whitespace(pnt);
 459       if( *pnt == '$') pnt++;
 460       pnt = get_string(pnt, &kcfg->depend.str);
 461       break;
 462     case tok_comment:
 463       pnt = get_qstring(pnt, &kcfg->label);
 464       if( koption != NULL )
 465         {
 466           pnt = get_qstring(pnt, &kcfg->label);
 467           koption->label = kcfg->label;
 468           koption = NULL;
 469         }
 470       break;
 471     case tok_menuoption:
 472       if( strncmp(pnt, "next_comment", 12) == 0)
 473         {
 474           koption = kcfg;
 475         }
 476       else
 477         {
 478           pnt = get_qstring(pnt, &kcfg->label);
 479         }
 480       break;
 481     case tok_else:
 482     case tok_fi:
 483     case tok_sound:
 484     case tok_endmenu:
 485       break;
 486     case tok_if:
 487       /*
 488        * Conditionals are different.  For the first level parse, only
 489        * tok_if items have a ->cond chain attached.
 490        */
 491       kcfg->cond = parse_if(pnt);
 492       if(kcfg->cond == NULL )
 493         {
 494           exit(1);
 495         }
 496       break;
 497     default:
 498       exit(0);
 499 
 500     }
 501 }
 502 
 503 /*
 504  * Simple function to dump to the screen what the condition chain looks like.
 505  */
 506 dump_if(struct condition * cond)
     /* [<][>][^][v][top][bottom][index][help] */
 507 {
 508   printf(" ");
 509   while(cond != NULL )
 510     {
 511       switch(cond->op){
 512       case op_eq:
 513         printf(" = ");
 514         break;
 515       case op_bang:
 516         printf(" ! ");
 517         break;
 518       case op_neq:
 519         printf(" != ");
 520         break;
 521       case op_and:
 522         printf(" -a ");
 523         break;
 524       case op_lparen:
 525         printf("(");
 526         break;
 527       case op_rparen:
 528         printf(")");
 529         break;
 530       case op_variable:
 531         printf("$%s", cond->variable);
 532         break;
 533       case op_constant:
 534         printf("'%s'", cond->variable);
 535         break;
 536       }
 537       cond = cond->next;
 538     }
 539 
 540   printf("\n");
 541 }
 542 
 543 static int do_source(char * filename)
     /* [<][>][^][v][top][bottom][index][help] */
 544 {
 545   char buffer[1024];
 546   int  offset;
 547   int old_lineno;
 548   char * old_file;
 549   char * pnt;
 550   FILE * infile;
 551 
 552   if( strcmp(filename, "-") == 0 )
 553     infile = stdin;
 554   else
 555     infile = fopen(filename,"r");
 556 
 557   /*
 558    * If our cwd was in the scripts directory, we might have to go up one
 559    * to find the sourced file.
 560    */
 561   if(!infile) {
 562     strcpy (buffer, "../");
 563     strcat (buffer, filename);
 564     infile = fopen(buffer,"r");
 565   }
 566 
 567   if(!infile) {
 568     fprintf(stderr,"Unable to open file %s\n", filename);
 569     return 1;
 570   }
 571   old_lineno = lineno;
 572   lineno = 0;
 573   if( infile != stdin ) {
 574     old_file = current_file;
 575     current_file = filename;
 576   }
 577   offset = 0;
 578   while(1)
 579     {
 580       fgets(&buffer[offset], sizeof(buffer) - offset, infile);
 581       if(feof(infile)) break;
 582 
 583       /*
 584        * Strip the trailing return character.
 585        */
 586       pnt = buffer + strlen(buffer) - 1;
 587       if( *pnt == '\n') *pnt-- = 0;
 588       lineno++;
 589       if( *pnt == '\\' )
 590         {
 591           offset = pnt - buffer;
 592         }
 593       else
 594         {
 595           parse(buffer);
 596           offset = 0;
 597         }
 598     }
 599   fclose(infile);
 600   if( infile != stdin ) {
 601     current_file = old_file;
 602   }
 603   lineno = old_lineno;
 604   return 0;
 605 }
 606 
 607 main(int argc, char * argv[])
     /* [<][>][^][v][top][bottom][index][help] */
 608 {
 609   char buffer[1024];
 610   char * pnt;
 611   struct kconfig * cfg;
 612   int    i;
 613 
 614   /*
 615    * Read stdin to get the top level script.
 616    */
 617   do_source("-");
 618 
 619   if( menus_seen == 0 )
 620     {
 621       fprintf(stderr,"The config.in file for this platform does not support\n");
 622       fprintf(stderr,"menus.\n");
 623       exit(1);
 624     }
 625   /*
 626    * Input file is now parsed.  Next we need to go through and attach
 627    * the correct conditions to each of the actual menu items and kill
 628    * the if/else/endif tokens from the list.  We also flag the menu items
 629    * that have other things that depend upon it's setting.
 630    */
 631   fix_conditionals(config);
 632 
 633   /*
 634    * Finally, we generate the wish script.
 635    */
 636   dump_tk_script(config);
 637 
 638 #if 0
 639   /*
 640    * Now dump what we have so far.  This is only for debugging so that
 641    * we can display what we think we have in the list.
 642    */
 643   for(cfg = config; cfg; cfg = cfg->next)
 644     {
 645 
 646       if(cfg->cond != NULL && cfg->tok != tok_if)
 647         dump_if(cfg->cond);
 648 
 649       switch(cfg->tok)
 650         {
 651         case tok_menuname:
 652           printf("main_menuname ");
 653           break;
 654         case tok_bool:
 655           printf("bool ");
 656           break;
 657         case tok_tristate:
 658           printf("tristate ");
 659           break;
 660         case tok_dep_tristate:
 661           printf("dep_tristate ");
 662           break;
 663         case tok_int:
 664           printf("int ");
 665           break;
 666         case tok_comment:
 667           printf("comment ");
 668           break;
 669         case tok_menuoption:
 670           printf("menuoption ");
 671           break;
 672         case tok_else:
 673           printf("else");
 674           break;
 675         case tok_fi:
 676           printf("fi");
 677           break;
 678         case tok_if:
 679           printf("if");
 680           break;
 681         default:
 682         }
 683 
 684       switch(cfg->tok)
 685         {
 686         case tok_menuoption:
 687         case tok_comment:
 688         case tok_menuname:
 689           printf("%s\n", cfg->label);
 690           break;
 691         case tok_bool:
 692         case tok_tristate:
 693         case tok_dep_tristate:
 694         case tok_int:
 695           printf("%s %s\n", cfg->label, cfg->optionname);
 696           break;
 697         case tok_if:
 698           dump_if(cfg->cond);
 699           break;
 700         case tok_nop:
 701         case tok_endmenu:
 702           break;
 703         default:
 704           printf("\n");
 705         }
 706     }
 707 #endif
 708 
 709   return 0;
 710 
 711 }

/* [<][>][^][v][top][bottom][index][help] */