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