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