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#include "lkc.h" 15 16#define printd(mask, fmt...) if (cdebug & (mask)) printf(fmt) 17 18#define PRINTD 0x0001 19#define DEBUG_PARSE 0x0002 20 21int cdebug = PRINTD; 22 23int yylex(void); 24static void yyerror(const char *err); 25static void zconfprint(const char *err, ...); 26static void zconf_error(const char *err, ...); 27static bool zconf_endtoken(const struct kconf_id *id, int starttoken, int endtoken); 28 29struct symbol *symbol_hash[SYMBOL_HASHSIZE]; 30 31static struct menu *current_menu, *current_entry; 32 33%} 34%expect 30 35 36%union 37{ 38 char *string; 39 struct file *file; 40 struct symbol *symbol; 41 struct expr *expr; 42 struct menu *menu; 43 const struct kconf_id *id; 44 enum variable_flavor flavor; 45} 46 47%token <id>T_MAINMENU 48%token <id>T_MENU 49%token <id>T_ENDMENU 50%token <id>T_SOURCE 51%token <id>T_CHOICE 52%token <id>T_ENDCHOICE 53%token <id>T_COMMENT 54%token <id>T_CONFIG 55%token <id>T_MENUCONFIG 56%token <id>T_HELP 57%token <string> T_HELPTEXT 58%token <id>T_IF 59%token <id>T_ENDIF 60%token <id>T_DEPENDS 61%token <id>T_OPTIONAL 62%token <id>T_PROMPT 63%token <id>T_TYPE 64%token <id>T_DEFAULT 65%token <id>T_SELECT 66%token <id>T_IMPLY 67%token <id>T_RANGE 68%token <id>T_VISIBLE 69%token <id>T_OPTION 70%token <id>T_ON 71%token <string> T_WORD 72%token <string> T_WORD_QUOTE 73%token T_UNEQUAL 74%token T_LESS 75%token T_LESS_EQUAL 76%token T_GREATER 77%token T_GREATER_EQUAL 78%token T_CLOSE_PAREN 79%token T_OPEN_PAREN 80%token T_EOL 81%token <string> T_VARIABLE 82%token <flavor> T_ASSIGN 83%token <string> T_ASSIGN_VAL 84 85%left T_OR 86%left T_AND 87%left T_EQUAL T_UNEQUAL 88%left T_LESS T_LESS_EQUAL T_GREATER T_GREATER_EQUAL 89%nonassoc T_NOT 90 91%type <string> prompt 92%type <symbol> nonconst_symbol 93%type <symbol> symbol 94%type <expr> expr 95%type <expr> if_expr 96%type <id> end 97%type <id> option_name 98%type <menu> if_entry menu_entry choice_entry 99%type <string> symbol_option_arg word_opt assign_val 100 101%destructor { 102 fprintf(stderr, "%s:%d: missing end statement for this entry\n", 103 $$->file->name, $$->lineno); 104 if (current_menu == $$) 105 menu_end_menu(); 106} if_entry menu_entry choice_entry 107 108%{ 109/* Include kconf_id.c here so it can see the token constants. */ 110#include "kconf_id.c" 111%} 112 113%% 114input: nl start | start; 115 116start: mainmenu_stmt stmt_list | stmt_list; 117 118/* mainmenu entry */ 119 120mainmenu_stmt: T_MAINMENU prompt T_EOL 121{ 122 menu_add_prompt(P_MENU, $2, NULL); 123}; 124 125stmt_list: 126 /* empty */ 127 | stmt_list common_stmt 128 | stmt_list choice_stmt 129 | stmt_list menu_stmt 130 | stmt_list end { zconf_error("unexpected end statement"); } 131 | stmt_list T_WORD error T_EOL { zconf_error("unknown statement \"%s\"", $2); } 132 | stmt_list option_name error T_EOL 133{ 134 zconf_error("unexpected option \"%s\"", $2->name); 135} 136 | stmt_list error T_EOL { zconf_error("invalid statement"); } 137; 138 139option_name: 140 T_DEPENDS | T_PROMPT | T_TYPE | T_SELECT | T_IMPLY | T_OPTIONAL | T_RANGE | T_DEFAULT | T_VISIBLE 141; 142 143common_stmt: 144 T_EOL 145 | if_stmt 146 | comment_stmt 147 | config_stmt 148 | menuconfig_stmt 149 | source_stmt 150 | assignment_stmt 151; 152 153option_error: 154 T_WORD error T_EOL { zconf_error("unknown option \"%s\"", $1); } 155 | error T_EOL { zconf_error("invalid option"); } 156; 157 158 159/* config/menuconfig entry */ 160 161config_entry_start: T_CONFIG nonconst_symbol T_EOL 162{ 163 $2->flags |= SYMBOL_OPTIONAL; 164 menu_add_entry($2); 165 printd(DEBUG_PARSE, "%s:%d:config %s\n", zconf_curname(), zconf_lineno(), $2->name); 166}; 167 168config_stmt: config_entry_start config_option_list 169{ 170 printd(DEBUG_PARSE, "%s:%d:endconfig\n", zconf_curname(), zconf_lineno()); 171}; 172 173menuconfig_entry_start: T_MENUCONFIG nonconst_symbol T_EOL 174{ 175 $2->flags |= SYMBOL_OPTIONAL; 176 menu_add_entry($2); 177 printd(DEBUG_PARSE, "%s:%d:menuconfig %s\n", zconf_curname(), zconf_lineno(), $2->name); 178}; 179 180menuconfig_stmt: menuconfig_entry_start config_option_list 181{ 182 if (current_entry->prompt) 183 current_entry->prompt->type = P_MENU; 184 else 185 zconfprint("warning: menuconfig statement without prompt"); 186 printd(DEBUG_PARSE, "%s:%d:endconfig\n", zconf_curname(), zconf_lineno()); 187}; 188 189config_option_list: 190 /* empty */ 191 | config_option_list config_option 192 | config_option_list symbol_option 193 | config_option_list depends 194 | config_option_list help 195 | config_option_list option_error 196 | config_option_list T_EOL 197; 198 199config_option: T_TYPE prompt_stmt_opt T_EOL 200{ 201 menu_set_type($1->stype); 202 printd(DEBUG_PARSE, "%s:%d:type(%u)\n", 203 zconf_curname(), zconf_lineno(), 204 $1->stype); 205}; 206 207config_option: T_PROMPT prompt if_expr T_EOL 208{ 209 menu_add_prompt(P_PROMPT, $2, $3); 210 printd(DEBUG_PARSE, "%s:%d:prompt\n", zconf_curname(), zconf_lineno()); 211}; 212 213config_option: T_DEFAULT expr if_expr T_EOL 214{ 215 menu_add_expr(P_DEFAULT, $2, $3); 216 if ($1->stype != S_UNKNOWN) 217 menu_set_type($1->stype); 218 printd(DEBUG_PARSE, "%s:%d:default(%u)\n", 219 zconf_curname(), zconf_lineno(), 220 $1->stype); 221}; 222 223config_option: T_SELECT nonconst_symbol if_expr T_EOL 224{ 225 menu_add_symbol(P_SELECT, $2, $3); 226 printd(DEBUG_PARSE, "%s:%d:select\n", zconf_curname(), zconf_lineno()); 227}; 228 229config_option: T_IMPLY nonconst_symbol if_expr T_EOL 230{ 231 menu_add_symbol(P_IMPLY, $2, $3); 232 printd(DEBUG_PARSE, "%s:%d:imply\n", zconf_curname(), zconf_lineno()); 233}; 234 235config_option: T_RANGE symbol symbol if_expr T_EOL 236{ 237 menu_add_expr(P_RANGE, expr_alloc_comp(E_RANGE,$2, $3), $4); 238 printd(DEBUG_PARSE, "%s:%d:range\n", zconf_curname(), zconf_lineno()); 239}; 240 241symbol_option: T_OPTION symbol_option_list T_EOL 242; 243 244symbol_option_list: 245 /* empty */ 246 | symbol_option_list T_WORD symbol_option_arg 247{ 248 const struct kconf_id *id = kconf_id_lookup($2, strlen($2)); 249 if (id && id->flags & TF_OPTION) { 250 menu_add_option(id->token, $3); 251 free($3); 252 } 253 else 254 zconfprint("warning: ignoring unknown option %s", $2); 255 free($2); 256}; 257 258symbol_option_arg: 259 /* empty */ { $$ = NULL; } 260 | T_EQUAL prompt { $$ = $2; } 261; 262 263/* choice entry */ 264 265choice: T_CHOICE word_opt T_EOL 266{ 267 struct symbol *sym = sym_lookup($2, SYMBOL_CHOICE); 268 sym->flags |= SYMBOL_NO_WRITE; 269 menu_add_entry(sym); 270 menu_add_expr(P_CHOICE, NULL, NULL); 271 free($2); 272 printd(DEBUG_PARSE, "%s:%d:choice\n", zconf_curname(), zconf_lineno()); 273}; 274 275choice_entry: choice choice_option_list 276{ 277 $$ = menu_add_menu(); 278}; 279 280choice_end: end 281{ 282 if (zconf_endtoken($1, T_CHOICE, T_ENDCHOICE)) { 283 menu_end_menu(); 284 printd(DEBUG_PARSE, "%s:%d:endchoice\n", zconf_curname(), zconf_lineno()); 285 } 286}; 287 288choice_stmt: choice_entry choice_block choice_end 289; 290 291choice_option_list: 292 /* empty */ 293 | choice_option_list choice_option 294 | choice_option_list depends 295 | choice_option_list help 296 | choice_option_list T_EOL 297 | choice_option_list option_error 298; 299 300choice_option: T_PROMPT prompt if_expr T_EOL 301{ 302 menu_add_prompt(P_PROMPT, $2, $3); 303 printd(DEBUG_PARSE, "%s:%d:prompt\n", zconf_curname(), zconf_lineno()); 304}; 305 306choice_option: T_TYPE prompt_stmt_opt T_EOL 307{ 308 if ($1->stype == S_BOOLEAN || $1->stype == S_TRISTATE) { 309 menu_set_type($1->stype); 310 printd(DEBUG_PARSE, "%s:%d:type(%u)\n", 311 zconf_curname(), zconf_lineno(), 312 $1->stype); 313 } else 314 YYERROR; 315}; 316 317choice_option: T_OPTIONAL T_EOL 318{ 319 current_entry->sym->flags |= SYMBOL_OPTIONAL; 320 printd(DEBUG_PARSE, "%s:%d:optional\n", zconf_curname(), zconf_lineno()); 321}; 322 323choice_option: T_DEFAULT nonconst_symbol if_expr T_EOL 324{ 325 if ($1->stype == S_UNKNOWN) { 326 menu_add_symbol(P_DEFAULT, $2, $3); 327 printd(DEBUG_PARSE, "%s:%d:default\n", 328 zconf_curname(), zconf_lineno()); 329 } else 330 YYERROR; 331}; 332 333choice_block: 334 /* empty */ 335 | choice_block common_stmt 336; 337 338/* if entry */ 339 340if_entry: T_IF expr T_EOL 341{ 342 printd(DEBUG_PARSE, "%s:%d:if\n", zconf_curname(), zconf_lineno()); 343 menu_add_entry(NULL); 344 menu_add_dep($2); 345 $$ = menu_add_menu(); 346}; 347 348if_end: end 349{ 350 if (zconf_endtoken($1, T_IF, T_ENDIF)) { 351 menu_end_menu(); 352 printd(DEBUG_PARSE, "%s:%d:endif\n", zconf_curname(), zconf_lineno()); 353 } 354}; 355 356if_stmt: if_entry if_block if_end 357; 358 359if_block: 360 /* empty */ 361 | if_block common_stmt 362 | if_block menu_stmt 363 | if_block choice_stmt 364; 365 366/* menu entry */ 367 368menu: T_MENU prompt T_EOL 369{ 370 menu_add_entry(NULL); 371 menu_add_prompt(P_MENU, $2, NULL); 372 printd(DEBUG_PARSE, "%s:%d:menu\n", zconf_curname(), zconf_lineno()); 373}; 374 375menu_entry: menu visibility_list depends_list 376{ 377 $$ = menu_add_menu(); 378}; 379 380menu_end: end 381{ 382 if (zconf_endtoken($1, T_MENU, T_ENDMENU)) { 383 menu_end_menu(); 384 printd(DEBUG_PARSE, "%s:%d:endmenu\n", zconf_curname(), zconf_lineno()); 385 } 386}; 387 388menu_stmt: menu_entry menu_block menu_end 389; 390 391menu_block: 392 /* empty */ 393 | menu_block common_stmt 394 | menu_block menu_stmt 395 | menu_block choice_stmt 396; 397 398source_stmt: T_SOURCE prompt T_EOL 399{ 400 printd(DEBUG_PARSE, "%s:%d:source %s\n", zconf_curname(), zconf_lineno(), $2); 401 zconf_nextfile($2); 402 free($2); 403}; 404 405/* comment entry */ 406 407comment: T_COMMENT prompt T_EOL 408{ 409 menu_add_entry(NULL); 410 menu_add_prompt(P_COMMENT, $2, NULL); 411 printd(DEBUG_PARSE, "%s:%d:comment\n", zconf_curname(), zconf_lineno()); 412}; 413 414comment_stmt: comment depends_list 415; 416 417/* help option */ 418 419help_start: T_HELP T_EOL 420{ 421 printd(DEBUG_PARSE, "%s:%d:help\n", zconf_curname(), zconf_lineno()); 422 zconf_starthelp(); 423}; 424 425help: help_start T_HELPTEXT 426{ 427 if (current_entry->help) { 428 free(current_entry->help); 429 zconfprint("warning: '%s' defined with more than one help text -- only the last one will be used", 430 current_entry->sym->name ?: "<choice>"); 431 } 432 433 /* Is the help text empty or all whitespace? */ 434 if ($2[strspn($2, " \f\n\r\t\v")] == '\0') 435 zconfprint("warning: '%s' defined with blank help text", 436 current_entry->sym->name ?: "<choice>"); 437 438 current_entry->help = $2; 439}; 440 441/* depends option */ 442 443depends_list: 444 /* empty */ 445 | depends_list depends 446 | depends_list T_EOL 447 | depends_list option_error 448; 449 450depends: T_DEPENDS T_ON expr T_EOL 451{ 452 menu_add_dep($3); 453 printd(DEBUG_PARSE, "%s:%d:depends on\n", zconf_curname(), zconf_lineno()); 454}; 455 456/* visibility option */ 457 458visibility_list: 459 /* empty */ 460 | visibility_list visible 461 | visibility_list T_EOL 462; 463 464visible: T_VISIBLE if_expr 465{ 466 menu_add_visibility($2); 467}; 468 469/* prompt statement */ 470 471prompt_stmt_opt: 472 /* empty */ 473 | prompt if_expr 474{ 475 menu_add_prompt(P_PROMPT, $1, $2); 476}; 477 478prompt: T_WORD 479 | T_WORD_QUOTE 480; 481 482end: T_ENDMENU T_EOL { $$ = $1; } 483 | T_ENDCHOICE T_EOL { $$ = $1; } 484 | T_ENDIF T_EOL { $$ = $1; } 485; 486 487nl: 488 T_EOL 489 | nl T_EOL 490; 491 492if_expr: /* empty */ { $$ = NULL; } 493 | T_IF expr { $$ = $2; } 494; 495 496expr: symbol { $$ = expr_alloc_symbol($1); } 497 | symbol T_LESS symbol { $$ = expr_alloc_comp(E_LTH, $1, $3); } 498 | symbol T_LESS_EQUAL symbol { $$ = expr_alloc_comp(E_LEQ, $1, $3); } 499 | symbol T_GREATER symbol { $$ = expr_alloc_comp(E_GTH, $1, $3); } 500 | symbol T_GREATER_EQUAL symbol { $$ = expr_alloc_comp(E_GEQ, $1, $3); } 501 | symbol T_EQUAL symbol { $$ = expr_alloc_comp(E_EQUAL, $1, $3); } 502 | symbol T_UNEQUAL symbol { $$ = expr_alloc_comp(E_UNEQUAL, $1, $3); } 503 | T_OPEN_PAREN expr T_CLOSE_PAREN { $$ = $2; } 504 | T_NOT expr { $$ = expr_alloc_one(E_NOT, $2); } 505 | expr T_OR expr { $$ = expr_alloc_two(E_OR, $1, $3); } 506 | expr T_AND expr { $$ = expr_alloc_two(E_AND, $1, $3); } 507; 508 509/* For symbol definitions, selects, etc., where quotes are not accepted */ 510nonconst_symbol: T_WORD { $$ = sym_lookup($1, 0); free($1); }; 511 512symbol: nonconst_symbol 513 | T_WORD_QUOTE { $$ = sym_lookup($1, SYMBOL_CONST); free($1); } 514; 515 516word_opt: /* empty */ { $$ = NULL; } 517 | T_WORD 518 519/* assignment statement */ 520 521assignment_stmt: T_VARIABLE T_ASSIGN assign_val T_EOL { variable_add($1, $3, $2); free($1); free($3); } 522 523assign_val: 524 /* empty */ { $$ = xstrdup(""); }; 525 | T_ASSIGN_VAL 526; 527 528%% 529 530void conf_parse(const char *name) 531{ 532 struct symbol *sym; 533 int i; 534 535 zconf_initscan(name); 536 537 _menu_init(); 538 539 if (getenv("ZCONF_DEBUG")) 540 yydebug = 1; 541 yyparse(); 542 543 /* Variables are expanded in the parse phase. We can free them here. */ 544 variable_all_del(); 545 546 if (yynerrs) 547 exit(1); 548 if (!modules_sym) 549 modules_sym = sym_find( "n" ); 550 551 if (!menu_has_prompt(&rootmenu)) { 552 current_entry = &rootmenu; 553 menu_add_prompt(P_MENU, "Main menu", NULL); 554 } 555 556 menu_finalize(&rootmenu); 557 for_all_symbols(i, sym) { 558 if (sym_check_deps(sym)) 559 yynerrs++; 560 } 561 if (yynerrs) 562 exit(1); 563 sym_set_change_count(1); 564} 565 566static const char *zconf_tokenname(int token) 567{ 568 switch (token) { 569 case T_MENU: return "menu"; 570 case T_ENDMENU: return "endmenu"; 571 case T_CHOICE: return "choice"; 572 case T_ENDCHOICE: return "endchoice"; 573 case T_IF: return "if"; 574 case T_ENDIF: return "endif"; 575 case T_DEPENDS: return "depends"; 576 case T_VISIBLE: return "visible"; 577 } 578 return "<token>"; 579} 580 581static bool zconf_endtoken(const struct kconf_id *id, int starttoken, int endtoken) 582{ 583 if (id->token != endtoken) { 584 zconf_error("unexpected '%s' within %s block", 585 id->name, zconf_tokenname(starttoken)); 586 yynerrs++; 587 return false; 588 } 589 if (current_menu->file != current_file) { 590 zconf_error("'%s' in different file than '%s'", 591 id->name, zconf_tokenname(starttoken)); 592 fprintf(stderr, "%s:%d: location of the '%s'\n", 593 current_menu->file->name, current_menu->lineno, 594 zconf_tokenname(starttoken)); 595 yynerrs++; 596 return false; 597 } 598 return true; 599} 600 601static void zconfprint(const char *err, ...) 602{ 603 va_list ap; 604 605 fprintf(stderr, "%s:%d: ", zconf_curname(), zconf_lineno()); 606 va_start(ap, err); 607 vfprintf(stderr, err, ap); 608 va_end(ap); 609 fprintf(stderr, "\n"); 610} 611 612static void zconf_error(const char *err, ...) 613{ 614 va_list ap; 615 616 yynerrs++; 617 fprintf(stderr, "%s:%d: ", zconf_curname(), zconf_lineno()); 618 va_start(ap, err); 619 vfprintf(stderr, err, ap); 620 va_end(ap); 621 fprintf(stderr, "\n"); 622} 623 624static void yyerror(const char *err) 625{ 626 fprintf(stderr, "%s:%d: %s\n", zconf_curname(), zconf_lineno() + 1, err); 627} 628 629static void print_quoted_string(FILE *out, const char *str) 630{ 631 const char *p; 632 int len; 633 634 putc('"', out); 635 while ((p = strchr(str, '"'))) { 636 len = p - str; 637 if (len) 638 fprintf(out, "%.*s", len, str); 639 fputs("\\\"", out); 640 str = p + 1; 641 } 642 fputs(str, out); 643 putc('"', out); 644} 645 646static void print_symbol(FILE *out, struct menu *menu) 647{ 648 struct symbol *sym = menu->sym; 649 struct property *prop; 650 651 if (sym_is_choice(sym)) 652 fprintf(out, "\nchoice\n"); 653 else 654 fprintf(out, "\nconfig %s\n", sym->name); 655 switch (sym->type) { 656 case S_BOOLEAN: 657 fputs(" bool\n", out); 658 break; 659 case S_TRISTATE: 660 fputs(" tristate\n", out); 661 break; 662 case S_STRING: 663 fputs(" string\n", out); 664 break; 665 case S_INT: 666 fputs(" integer\n", out); 667 break; 668 case S_HEX: 669 fputs(" hex\n", out); 670 break; 671 default: 672 fputs(" ???\n", out); 673 break; 674 } 675 for (prop = sym->prop; prop; prop = prop->next) { 676 if (prop->menu != menu) 677 continue; 678 switch (prop->type) { 679 case P_PROMPT: 680 fputs(" prompt ", out); 681 print_quoted_string(out, prop->text); 682 if (!expr_is_yes(prop->visible.expr)) { 683 fputs(" if ", out); 684 expr_fprint(prop->visible.expr, out); 685 } 686 fputc('\n', out); 687 break; 688 case P_DEFAULT: 689 fputs( " default ", out); 690 expr_fprint(prop->expr, out); 691 if (!expr_is_yes(prop->visible.expr)) { 692 fputs(" if ", out); 693 expr_fprint(prop->visible.expr, out); 694 } 695 fputc('\n', out); 696 break; 697 case P_CHOICE: 698 fputs(" #choice value\n", out); 699 break; 700 case P_SELECT: 701 fputs( " select ", out); 702 expr_fprint(prop->expr, out); 703 fputc('\n', out); 704 break; 705 case P_IMPLY: 706 fputs( " imply ", out); 707 expr_fprint(prop->expr, out); 708 fputc('\n', out); 709 break; 710 case P_RANGE: 711 fputs( " range ", out); 712 expr_fprint(prop->expr, out); 713 fputc('\n', out); 714 break; 715 case P_MENU: 716 fputs( " menu ", out); 717 print_quoted_string(out, prop->text); 718 fputc('\n', out); 719 break; 720 case P_SYMBOL: 721 fputs( " symbol ", out); 722 fprintf(out, "%s\n", prop->sym->name); 723 break; 724 default: 725 fprintf(out, " unknown prop %d!\n", prop->type); 726 break; 727 } 728 } 729 if (menu->help) { 730 int len = strlen(menu->help); 731 while (menu->help[--len] == '\n') 732 menu->help[len] = 0; 733 fprintf(out, " help\n%s\n", menu->help); 734 } 735} 736 737void zconfdump(FILE *out) 738{ 739 struct property *prop; 740 struct symbol *sym; 741 struct menu *menu; 742 743 menu = rootmenu.list; 744 while (menu) { 745 if ((sym = menu->sym)) 746 print_symbol(out, menu); 747 else if ((prop = menu->prompt)) { 748 switch (prop->type) { 749 case P_COMMENT: 750 fputs("\ncomment ", out); 751 print_quoted_string(out, prop->text); 752 fputs("\n", out); 753 break; 754 case P_MENU: 755 fputs("\nmenu ", out); 756 print_quoted_string(out, prop->text); 757 fputs("\n", out); 758 break; 759 default: 760 ; 761 } 762 if (!expr_is_yes(prop->visible.expr)) { 763 fputs(" depends ", out); 764 expr_fprint(prop->visible.expr, out); 765 fputc('\n', out); 766 } 767 } 768 769 if (menu->list) 770 menu = menu->list; 771 else if (menu->next) 772 menu = menu->next; 773 else while ((menu = menu->parent)) { 774 if (menu->prompt && menu->prompt->type == P_MENU) 775 fputs("\nendmenu\n", out); 776 if (menu->next) { 777 menu = menu->next; 778 break; 779 } 780 } 781 } 782} 783 784#include "zconf.lex.c" 785#include "util.c" 786#include "confdata.c" 787#include "expr.c" 788#include "symbol.c" 789#include "menu.c" 790#include "preprocess.c" 791