busybox/scripts/kconfig/zconf.y
<<
>>
Prefs
   1%{
   2/*
   3 * Copyright (C) 2002 Roman Zippel <zippel@linux-m68k.org>
   4 * Released under the terms of the GNU GPL v2.0.
   5 */
   6
   7#include <ctype.h>
   8#include <stdarg.h>
   9#include <stdio.h>
  10#include <stdlib.h>
  11#include <string.h>
  12#include <stdbool.h>
  13
  14#define LKC_DIRECT_LINK
  15#include "lkc.h"
  16
  17#define printd(mask, fmt...) if (cdebug & (mask)) printf(fmt)
  18
  19#define PRINTD          0x0001
  20#define DEBUG_PARSE     0x0002
  21
  22int cdebug = PRINTD;
  23
  24extern int zconflex(void);
  25static void zconfprint(const char *err, ...);
  26static void zconf_error(const char *err, ...);
  27static void zconferror(const char *err);
  28static bool zconf_endtoken(struct kconf_id *id, int starttoken, int endtoken);
  29
  30struct symbol *symbol_hash[257];
  31
  32static struct menu *current_menu, *current_entry;
  33
  34#define YYDEBUG 0
  35#if YYDEBUG
  36#define YYERROR_VERBOSE
  37#endif
  38%}
  39%expect 26
  40
  41%union
  42{
  43        char *string;
  44        struct file *file;
  45        struct symbol *symbol;
  46        struct expr *expr;
  47        struct menu *menu;
  48        struct kconf_id *id;
  49}
  50
  51%token <id>T_MAINMENU
  52%token <id>T_MENU
  53%token <id>T_ENDMENU
  54%token <id>T_SOURCE
  55%token <id>T_CHOICE
  56%token <id>T_ENDCHOICE
  57%token <id>T_COMMENT
  58%token <id>T_CONFIG
  59%token <id>T_MENUCONFIG
  60%token <id>T_HELP
  61%token <string> T_HELPTEXT
  62%token <id>T_IF
  63%token <id>T_ENDIF
  64%token <id>T_DEPENDS
  65%token <id>T_REQUIRES
  66%token <id>T_OPTIONAL
  67%token <id>T_PROMPT
  68%token <id>T_TYPE
  69%token <id>T_DEFAULT
  70%token <id>T_SELECT
  71%token <id>T_RANGE
  72%token <id>T_ON
  73%token <string> T_WORD
  74%token <string> T_WORD_QUOTE
  75%token T_UNEQUAL
  76%token T_CLOSE_PAREN
  77%token T_OPEN_PAREN
  78%token T_EOL
  79
  80%left T_OR
  81%left T_AND
  82%left T_EQUAL T_UNEQUAL
  83%nonassoc T_NOT
  84
  85%type <string> prompt
  86%type <symbol> symbol
  87%type <expr> expr
  88%type <expr> if_expr
  89%type <id> end
  90%type <id> option_name
  91%type <menu> if_entry menu_entry choice_entry
  92
  93%destructor {
  94        fprintf(stderr, "%s:%d: missing end statement for this entry\n",
  95                $$->file->name, $$->lineno);
  96        if (current_menu == $$)
  97                menu_end_menu();
  98} if_entry menu_entry choice_entry
  99
 100%{
 101#include "zconf.hash.c"
 102%}
 103
 104%%
 105input: stmt_list;
 106
 107stmt_list:
 108          /* empty */
 109        | stmt_list common_stmt
 110        | stmt_list choice_stmt
 111        | stmt_list menu_stmt
 112        | stmt_list T_MAINMENU prompt nl
 113        | stmt_list end                 { zconf_error("unexpected end statement"); }
 114        | stmt_list T_WORD error T_EOL  { zconf_error("unknown statement \"%s\"", $2); }
 115        | stmt_list option_name error T_EOL
 116{
 117        zconf_error("unexpected option \"%s\"", kconf_id_strings + $2->name);
 118}
 119        | stmt_list error T_EOL         { zconf_error("invalid statement"); }
 120;
 121
 122option_name:
 123        T_DEPENDS | T_PROMPT | T_TYPE | T_SELECT | T_OPTIONAL | T_RANGE | T_DEFAULT
 124;
 125
 126common_stmt:
 127          T_EOL
 128        | if_stmt
 129        | comment_stmt
 130        | config_stmt
 131        | menuconfig_stmt
 132        | source_stmt
 133;
 134
 135option_error:
 136          T_WORD error T_EOL            { zconf_error("unknown option \"%s\"", $1); }
 137        | error T_EOL                   { zconf_error("invalid option"); }
 138;
 139
 140
 141/* config/menuconfig entry */
 142
 143config_entry_start: T_CONFIG T_WORD T_EOL
 144{
 145        struct symbol *sym = sym_lookup($2, 0);
 146        sym->flags |= SYMBOL_OPTIONAL;
 147        menu_add_entry(sym);
 148        printd(DEBUG_PARSE, "%s:%d:config %s\n", zconf_curname(), zconf_lineno(), $2);
 149};
 150
 151config_stmt: config_entry_start config_option_list
 152{
 153        menu_end_entry();
 154        printd(DEBUG_PARSE, "%s:%d:endconfig\n", zconf_curname(), zconf_lineno());
 155};
 156
 157menuconfig_entry_start: T_MENUCONFIG T_WORD T_EOL
 158{
 159        struct symbol *sym = sym_lookup($2, 0);
 160        sym->flags |= SYMBOL_OPTIONAL;
 161        menu_add_entry(sym);
 162        printd(DEBUG_PARSE, "%s:%d:menuconfig %s\n", zconf_curname(), zconf_lineno(), $2);
 163};
 164
 165menuconfig_stmt: menuconfig_entry_start config_option_list
 166{
 167        if (current_entry->prompt)
 168                current_entry->prompt->type = P_MENU;
 169        else
 170                zconfprint("warning: menuconfig statement without prompt");
 171        menu_end_entry();
 172        printd(DEBUG_PARSE, "%s:%d:endconfig\n", zconf_curname(), zconf_lineno());
 173};
 174
 175config_option_list:
 176          /* empty */
 177        | config_option_list config_option
 178        | config_option_list depends
 179        | config_option_list help
 180        | config_option_list option_error
 181        | config_option_list T_EOL
 182;
 183
 184config_option: T_TYPE prompt_stmt_opt T_EOL
 185{
 186        menu_set_type($1->stype);
 187        printd(DEBUG_PARSE, "%s:%d:type(%u)\n",
 188                zconf_curname(), zconf_lineno(),
 189                $1->stype);
 190};
 191
 192config_option: T_PROMPT prompt if_expr T_EOL
 193{
 194        menu_add_prompt(P_PROMPT, $2, $3);
 195        printd(DEBUG_PARSE, "%s:%d:prompt\n", zconf_curname(), zconf_lineno());
 196};
 197
 198config_option: T_DEFAULT expr if_expr T_EOL
 199{
 200        menu_add_expr(P_DEFAULT, $2, $3);
 201        if ($1->stype != S_UNKNOWN)
 202                menu_set_type($1->stype);
 203        printd(DEBUG_PARSE, "%s:%d:default(%u)\n",
 204                zconf_curname(), zconf_lineno(),
 205                $1->stype);
 206};
 207
 208config_option: T_SELECT T_WORD if_expr T_EOL
 209{
 210        menu_add_symbol(P_SELECT, sym_lookup($2, 0), $3);
 211        printd(DEBUG_PARSE, "%s:%d:select\n", zconf_curname(), zconf_lineno());
 212};
 213
 214config_option: T_RANGE symbol symbol if_expr T_EOL
 215{
 216        menu_add_expr(P_RANGE, expr_alloc_comp(E_RANGE,$2, $3), $4);
 217        printd(DEBUG_PARSE, "%s:%d:range\n", zconf_curname(), zconf_lineno());
 218};
 219
 220/* choice entry */
 221
 222choice: T_CHOICE T_EOL
 223{
 224        struct symbol *sym = sym_lookup(NULL, 0);
 225        sym->flags |= SYMBOL_CHOICE;
 226        menu_add_entry(sym);
 227        menu_add_expr(P_CHOICE, NULL, NULL);
 228        printd(DEBUG_PARSE, "%s:%d:choice\n", zconf_curname(), zconf_lineno());
 229};
 230
 231choice_entry: choice choice_option_list
 232{
 233        $$ = menu_add_menu();
 234};
 235
 236choice_end: end
 237{
 238        if (zconf_endtoken($1, T_CHOICE, T_ENDCHOICE)) {
 239                menu_end_menu();
 240                printd(DEBUG_PARSE, "%s:%d:endchoice\n", zconf_curname(), zconf_lineno());
 241        }
 242};
 243
 244choice_stmt: choice_entry choice_block choice_end
 245;
 246
 247choice_option_list:
 248          /* empty */
 249        | choice_option_list choice_option
 250        | choice_option_list depends
 251        | choice_option_list help
 252        | choice_option_list T_EOL
 253        | choice_option_list option_error
 254;
 255
 256choice_option: T_PROMPT prompt if_expr T_EOL
 257{
 258        menu_add_prompt(P_PROMPT, $2, $3);
 259        printd(DEBUG_PARSE, "%s:%d:prompt\n", zconf_curname(), zconf_lineno());
 260};
 261
 262choice_option: T_TYPE prompt_stmt_opt T_EOL
 263{
 264        if ($1->stype == S_BOOLEAN || $1->stype == S_TRISTATE) {
 265                menu_set_type($1->stype);
 266                printd(DEBUG_PARSE, "%s:%d:type(%u)\n",
 267                        zconf_curname(), zconf_lineno(),
 268                        $1->stype);
 269        } else
 270                YYERROR;
 271};
 272
 273choice_option: T_OPTIONAL T_EOL
 274{
 275        current_entry->sym->flags |= SYMBOL_OPTIONAL;
 276        printd(DEBUG_PARSE, "%s:%d:optional\n", zconf_curname(), zconf_lineno());
 277};
 278
 279choice_option: T_DEFAULT T_WORD if_expr T_EOL
 280{
 281        if ($1->stype == S_UNKNOWN) {
 282                menu_add_symbol(P_DEFAULT, sym_lookup($2, 0), $3);
 283                printd(DEBUG_PARSE, "%s:%d:default\n",
 284                        zconf_curname(), zconf_lineno());
 285        } else
 286                YYERROR;
 287};
 288
 289choice_block:
 290          /* empty */
 291        | choice_block common_stmt
 292;
 293
 294/* if entry */
 295
 296if_entry: T_IF expr nl
 297{
 298        printd(DEBUG_PARSE, "%s:%d:if\n", zconf_curname(), zconf_lineno());
 299        menu_add_entry(NULL);
 300        menu_add_dep($2);
 301        $$ = menu_add_menu();
 302};
 303
 304if_end: end
 305{
 306        if (zconf_endtoken($1, T_IF, T_ENDIF)) {
 307                menu_end_menu();
 308                printd(DEBUG_PARSE, "%s:%d:endif\n", zconf_curname(), zconf_lineno());
 309        }
 310};
 311
 312if_stmt: if_entry if_block if_end
 313;
 314
 315if_block:
 316          /* empty */
 317        | if_block common_stmt
 318        | if_block menu_stmt
 319        | if_block choice_stmt
 320;
 321
 322/* menu entry */
 323
 324menu: T_MENU prompt T_EOL
 325{
 326        menu_add_entry(NULL);
 327        menu_add_prompt(P_MENU, $2, NULL);
 328        printd(DEBUG_PARSE, "%s:%d:menu\n", zconf_curname(), zconf_lineno());
 329};
 330
 331menu_entry: menu depends_list
 332{
 333        $$ = menu_add_menu();
 334};
 335
 336menu_end: end
 337{
 338        if (zconf_endtoken($1, T_MENU, T_ENDMENU)) {
 339                menu_end_menu();
 340                printd(DEBUG_PARSE, "%s:%d:endmenu\n", zconf_curname(), zconf_lineno());
 341        }
 342};
 343
 344menu_stmt: menu_entry menu_block menu_end
 345;
 346
 347menu_block:
 348          /* empty */
 349        | menu_block common_stmt
 350        | menu_block menu_stmt
 351        | menu_block choice_stmt
 352;
 353
 354source_stmt: T_SOURCE prompt T_EOL
 355{
 356        printd(DEBUG_PARSE, "%s:%d:source %s\n", zconf_curname(), zconf_lineno(), $2);
 357        zconf_nextfile($2);
 358};
 359
 360/* comment entry */
 361
 362comment: T_COMMENT prompt T_EOL
 363{
 364        menu_add_entry(NULL);
 365        menu_add_prompt(P_COMMENT, $2, NULL);
 366        printd(DEBUG_PARSE, "%s:%d:comment\n", zconf_curname(), zconf_lineno());
 367};
 368
 369comment_stmt: comment depends_list
 370{
 371        menu_end_entry();
 372};
 373
 374/* help option */
 375
 376help_start: T_HELP T_EOL
 377{
 378        printd(DEBUG_PARSE, "%s:%d:help\n", zconf_curname(), zconf_lineno());
 379        zconf_starthelp();
 380};
 381
 382help: help_start T_HELPTEXT
 383{
 384        current_entry->sym->help = $2;
 385};
 386
 387/* depends option */
 388
 389depends_list:
 390          /* empty */
 391        | depends_list depends
 392        | depends_list T_EOL
 393        | depends_list option_error
 394;
 395
 396depends: T_DEPENDS T_ON expr T_EOL
 397{
 398        menu_add_dep($3);
 399        printd(DEBUG_PARSE, "%s:%d:depends on\n", zconf_curname(), zconf_lineno());
 400}
 401        | T_DEPENDS expr T_EOL
 402{
 403        menu_add_dep($2);
 404        printd(DEBUG_PARSE, "%s:%d:depends\n", zconf_curname(), zconf_lineno());
 405}
 406        | T_REQUIRES expr T_EOL
 407{
 408        menu_add_dep($2);
 409        printd(DEBUG_PARSE, "%s:%d:requires\n", zconf_curname(), zconf_lineno());
 410};
 411
 412/* prompt statement */
 413
 414prompt_stmt_opt:
 415          /* empty */
 416        | prompt if_expr
 417{
 418        menu_add_prompt(P_PROMPT, $1, $2);
 419};
 420
 421prompt:   T_WORD
 422        | T_WORD_QUOTE
 423;
 424
 425end:      T_ENDMENU T_EOL       { $$ = $1; }
 426        | T_ENDCHOICE T_EOL     { $$ = $1; }
 427        | T_ENDIF T_EOL         { $$ = $1; }
 428;
 429
 430nl:
 431          T_EOL
 432        | nl T_EOL
 433;
 434
 435if_expr:  /* empty */                   { $$ = NULL; }
 436        | T_IF expr                     { $$ = $2; }
 437;
 438
 439expr:     symbol                                { $$ = expr_alloc_symbol($1); }
 440        | symbol T_EQUAL symbol                 { $$ = expr_alloc_comp(E_EQUAL, $1, $3); }
 441        | symbol T_UNEQUAL symbol               { $$ = expr_alloc_comp(E_UNEQUAL, $1, $3); }
 442        | T_OPEN_PAREN expr T_CLOSE_PAREN       { $$ = $2; }
 443        | T_NOT expr                            { $$ = expr_alloc_one(E_NOT, $2); }
 444        | expr T_OR expr                        { $$ = expr_alloc_two(E_OR, $1, $3); }
 445        | expr T_AND expr                       { $$ = expr_alloc_two(E_AND, $1, $3); }
 446;
 447
 448symbol:   T_WORD        { $$ = sym_lookup($1, 0); free($1); }
 449        | T_WORD_QUOTE  { $$ = sym_lookup($1, 1); free($1); }
 450;
 451
 452%%
 453
 454void conf_parse(const char *name)
 455{
 456        struct symbol *sym;
 457        int i;
 458
 459        zconf_initscan(name);
 460
 461        sym_init();
 462        menu_init();
 463        modules_sym = sym_lookup("MODULES", 0);
 464        rootmenu.prompt = menu_add_prompt(P_MENU, "Busybox Configuration", NULL);
 465
 466#if YYDEBUG
 467        if (getenv("ZCONF_DEBUG"))
 468                zconfdebug = 1;
 469#endif
 470        zconfparse();
 471        if (zconfnerrs)
 472                exit(1);
 473        menu_finalize(&rootmenu);
 474        for_all_symbols(i, sym) {
 475                sym_check_deps(sym);
 476        }
 477
 478        sym_change_count = 1;
 479}
 480
 481const char *zconf_tokenname(int token)
 482{
 483        switch (token) {
 484        case T_MENU:            return "menu";
 485        case T_ENDMENU:         return "endmenu";
 486        case T_CHOICE:          return "choice";
 487        case T_ENDCHOICE:       return "endchoice";
 488        case T_IF:              return "if";
 489        case T_ENDIF:           return "endif";
 490        case T_DEPENDS:         return "depends";
 491        }
 492        return "<token>";
 493}
 494
 495static bool zconf_endtoken(struct kconf_id *id, int starttoken, int endtoken)
 496{
 497        if (id->token != endtoken) {
 498                zconf_error("unexpected '%s' within %s block",
 499                        kconf_id_strings + id->name, zconf_tokenname(starttoken));
 500                zconfnerrs++;
 501                return false;
 502        }
 503        if (current_menu->file != current_file) {
 504                zconf_error("'%s' in different file than '%s'",
 505                        kconf_id_strings + id->name, zconf_tokenname(starttoken));
 506                fprintf(stderr, "%s:%d: location of the '%s'\n",
 507                        current_menu->file->name, current_menu->lineno,
 508                        zconf_tokenname(starttoken));
 509                zconfnerrs++;
 510                return false;
 511        }
 512        return true;
 513}
 514
 515static void zconfprint(const char *err, ...)
 516{
 517        va_list ap;
 518
 519        fprintf(stderr, "%s:%d: ", zconf_curname(), zconf_lineno());
 520        va_start(ap, err);
 521        vfprintf(stderr, err, ap);
 522        va_end(ap);
 523        fprintf(stderr, "\n");
 524}
 525
 526static void zconf_error(const char *err, ...)
 527{
 528        va_list ap;
 529
 530        zconfnerrs++;
 531        fprintf(stderr, "%s:%d: ", zconf_curname(), zconf_lineno());
 532        va_start(ap, err);
 533        vfprintf(stderr, err, ap);
 534        va_end(ap);
 535        fprintf(stderr, "\n");
 536}
 537
 538static void zconferror(const char *err)
 539{
 540#if YYDEBUG
 541        fprintf(stderr, "%s:%d: %s\n", zconf_curname(), zconf_lineno() + 1, err);
 542#endif
 543}
 544
 545void print_quoted_string(FILE *out, const char *str)
 546{
 547        const char *p;
 548        int len;
 549
 550        putc('"', out);
 551        while ((p = strchr(str, '"'))) {
 552                len = p - str;
 553                if (len)
 554                        fprintf(out, "%.*s", len, str);
 555                fputs("\\\"", out);
 556                str = p + 1;
 557        }
 558        fputs(str, out);
 559        putc('"', out);
 560}
 561
 562void print_symbol(FILE *out, struct menu *menu)
 563{
 564        struct symbol *sym = menu->sym;
 565        struct property *prop;
 566
 567        if (sym_is_choice(sym))
 568                fprintf(out, "choice\n");
 569        else
 570                fprintf(out, "config %s\n", sym->name);
 571        switch (sym->type) {
 572        case S_BOOLEAN:
 573                fputs("  boolean\n", out);
 574                break;
 575        case S_TRISTATE:
 576                fputs("  tristate\n", out);
 577                break;
 578        case S_STRING:
 579                fputs("  string\n", out);
 580                break;
 581        case S_INT:
 582                fputs("  integer\n", out);
 583                break;
 584        case S_HEX:
 585                fputs("  hex\n", out);
 586                break;
 587        default:
 588                fputs("  ???\n", out);
 589                break;
 590        }
 591        for (prop = sym->prop; prop; prop = prop->next) {
 592                if (prop->menu != menu)
 593                        continue;
 594                switch (prop->type) {
 595                case P_PROMPT:
 596                        fputs("  prompt ", out);
 597                        print_quoted_string(out, prop->text);
 598                        if (!expr_is_yes(prop->visible.expr)) {
 599                                fputs(" if ", out);
 600                                expr_fprint(prop->visible.expr, out);
 601                        }
 602                        fputc('\n', out);
 603                        break;
 604                case P_DEFAULT:
 605                        fputs( "  default ", out);
 606                        expr_fprint(prop->expr, out);
 607                        if (!expr_is_yes(prop->visible.expr)) {
 608                                fputs(" if ", out);
 609                                expr_fprint(prop->visible.expr, out);
 610                        }
 611                        fputc('\n', out);
 612                        break;
 613                case P_CHOICE:
 614                        fputs("  #choice value\n", out);
 615                        break;
 616                default:
 617                        fprintf(out, "  unknown prop %d!\n", prop->type);
 618                        break;
 619                }
 620        }
 621        if (sym->help) {
 622                int len = strlen(sym->help);
 623                while (sym->help[--len] == '\n')
 624                        sym->help[len] = 0;
 625                fprintf(out, "  help\n%s\n", sym->help);
 626        }
 627        fputc('\n', out);
 628}
 629
 630void zconfdump(FILE *out)
 631{
 632        struct property *prop;
 633        struct symbol *sym;
 634        struct menu *menu;
 635
 636        menu = rootmenu.list;
 637        while (menu) {
 638                if ((sym = menu->sym))
 639                        print_symbol(out, menu);
 640                else if ((prop = menu->prompt)) {
 641                        switch (prop->type) {
 642                        case P_COMMENT:
 643                                fputs("\ncomment ", out);
 644                                print_quoted_string(out, prop->text);
 645                                fputs("\n", out);
 646                                break;
 647                        case P_MENU:
 648                                fputs("\nmenu ", out);
 649                                print_quoted_string(out, prop->text);
 650                                fputs("\n", out);
 651                                break;
 652                        default:
 653                                ;
 654                        }
 655                        if (!expr_is_yes(prop->visible.expr)) {
 656                                fputs("  depends ", out);
 657                                expr_fprint(prop->visible.expr, out);
 658                                fputc('\n', out);
 659                        }
 660                        fputs("\n", out);
 661                }
 662
 663                if (menu->list)
 664                        menu = menu->list;
 665                else if (menu->next)
 666                        menu = menu->next;
 667                else while ((menu = menu->parent)) {
 668                        if (menu->prompt && menu->prompt->type == P_MENU)
 669                                fputs("\nendmenu\n", out);
 670                        if (menu->next) {
 671                                menu = menu->next;
 672                                break;
 673                        }
 674                }
 675        }
 676}
 677
 678#include "lex.zconf.c"
 679#include "util.c"
 680#include "confdata.c"
 681#include "expr.c"
 682#include "symbol.c"
 683#include "menu.c"
 684