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