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