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