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 static 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 %% 109 input: mainmenu_stmt stmt_list | stmt_list; 110 111 /* mainmenu entry */ 112 113 mainmenu_stmt: T_MAINMENU T_WORD_QUOTE T_EOL 114 { 115 menu_add_prompt(P_MENU, $2, NULL); 116 }; 117 118 stmt_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 132 stmt_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 142 config_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 148 config_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 161 menuconfig_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 167 menuconfig_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 176 config_option_list: 177 /* empty */ 178 | config_option_list config_option 179 | config_option_list depends 180 | config_option_list help 181 ; 182 183 config_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 189 config_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 195 config_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 204 config_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 210 config_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 216 config_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 222 config_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 232 choice: 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 241 choice_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 254 choice_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 264 choice_stmt: choice_entry stmt_list_in_choice choice_end 265 ; 266 267 choice_option_list: 268 /* empty */ 269 | choice_option_list choice_option 270 | choice_option_list depends 271 | choice_option_list help 272 ; 273 274 choice_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 280 choice_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 286 choice_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 292 type: 293 logic_type 294 | T_INT { $$ = S_INT; } 295 | T_HEX { $$ = S_HEX; } 296 | T_STRING { $$ = S_STRING; } 297 298 logic_type: 299 T_BOOL { $$ = S_BOOLEAN; } 300 | T_TRISTATE { $$ = S_TRISTATE; } 301 302 default: 303 T_DEFAULT { $$ = S_UNKNOWN; } 304 | T_DEF_BOOL { $$ = S_BOOLEAN; } 305 | T_DEF_TRISTATE { $$ = S_TRISTATE; } 306 307 /* if entry */ 308 309 if_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 317 if_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 325 if_stmt: if_entry stmt_list if_end 326 ; 327 328 if_stmt_in_choice: if_entry stmt_list_in_choice if_end 329 ; 330 331 /* menu entry */ 332 333 menu: 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 340 menu_entry: menu menu_option_list 341 { 342 $$ = menu_add_menu(); 343 }; 344 345 menu_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 353 menu_stmt: menu_entry stmt_list menu_end 354 ; 355 356 menu_option_list: 357 /* empty */ 358 | menu_option_list visible 359 | menu_option_list depends 360 ; 361 362 source_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 371 comment: 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 378 comment_stmt: comment comment_option_list 379 ; 380 381 comment_option_list: 382 /* empty */ 383 | comment_option_list depends 384 ; 385 386 /* help option */ 387 388 help_start: T_HELP T_EOL 389 { 390 printd(DEBUG_PARSE, "%s:%d:help\n", cur_filename, cur_lineno); 391 zconf_starthelp(); 392 }; 393 394 help: 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 412 depends: 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 */ 419 visible: T_VISIBLE if_expr T_EOL 420 { 421 menu_add_visibility($2); 422 }; 423 424 /* prompt statement */ 425 426 prompt_stmt_opt: 427 /* empty */ 428 | T_WORD_QUOTE if_expr 429 { 430 menu_add_prompt(P_PROMPT, $1, $2); 431 }; 432 433 end: T_ENDMENU T_EOL { $$ = "menu"; } 434 | T_ENDCHOICE T_EOL { $$ = "choice"; } 435 | T_ENDIF T_EOL { $$ = "if"; } 436 ; 437 438 if_expr: /* empty */ { $$ = NULL; } 439 | T_IF expr { $$ = $2; } 440 ; 441 442 expr: 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 */ 456 nonconst_symbol: T_WORD { $$ = sym_lookup($1, 0); free($1); }; 457 458 symbol: nonconst_symbol 459 | T_WORD_QUOTE { $$ = sym_lookup($1, SYMBOL_CONST); free($1); } 460 ; 461 462 /* assignment statement */ 463 464 assignment_stmt: T_WORD assign_op assign_val T_EOL { variable_add($1, $3, $2); free($1); free($3); } 465 466 assign_op: 467 T_EQUAL { $$ = VAR_RECURSIVE; } 468 | T_COLON_EQUAL { $$ = VAR_SIMPLE; } 469 | T_PLUS_EQUAL { $$ = VAR_APPEND; } 470 ; 471 472 assign_val: 473 /* empty */ { $$ = xstrdup(""); }; 474 | T_ASSIGN_VAL 475 ; 476 477 %% 478 479 void conf_parse(const char *name) 480 { 481 struct menu *menu; 482 483 autoconf_cmd = str_new(); 484 485 str_printf(&autoconf_cmd, "\ndeps_config := \\\n"); 486 487 zconf_initscan(name); 488 489 _menu_init(); 490 491 if (getenv("ZCONF_DEBUG")) 492 yydebug = 1; 493 yyparse(); 494 495 /* 496 * FIXME: 497 * cur_filename and cur_lineno are used even after yyparse(); 498 * menu_finalize() calls menu_add_symbol(). This should be fixed. 499 */ 500 cur_filename = "<none>"; 501 cur_lineno = 0; 502 503 str_printf(&autoconf_cmd, 504 "\n" 505 "$(autoconfig): $(deps_config)\n" 506 "$(deps_config): ;\n"); 507 508 env_write_dep(&autoconf_cmd); 509 510 /* Variables are expanded in the parse phase. We can free them here. */ 511 variable_all_del(); 512 513 if (yynerrs) 514 exit(1); 515 if (!modules_sym) 516 modules_sym = &symbol_no; 517 518 if (!menu_has_prompt(&rootmenu)) { 519 current_entry = &rootmenu; 520 menu_add_prompt(P_MENU, "Main menu", NULL); 521 } 522 523 menu_finalize(); 524 525 menu_for_each_entry(menu) { 526 if (menu->sym && sym_check_deps(menu->sym)) 527 yynerrs++; 528 } 529 530 if (yynerrs) 531 exit(1); 532 conf_set_changed(true); 533 } 534 535 static bool zconf_endtoken(const char *tokenname, 536 const char *expected_tokenname) 537 { 538 if (strcmp(tokenname, expected_tokenname)) { 539 zconf_error("unexpected '%s' within %s block", 540 tokenname, expected_tokenname); 541 yynerrs++; 542 return false; 543 } 544 if (strcmp(current_menu->filename, cur_filename)) { 545 zconf_error("'%s' in different file than '%s'", 546 tokenname, expected_tokenname); 547 fprintf(stderr, "%s:%d: location of the '%s'\n", 548 current_menu->filename, current_menu->lineno, 549 expected_tokenname); 550 yynerrs++; 551 return false; 552 } 553 return true; 554 } 555 556 static void zconfprint(const char *err, ...) 557 { 558 va_list ap; 559 560 fprintf(stderr, "%s:%d: ", cur_filename, cur_lineno); 561 va_start(ap, err); 562 vfprintf(stderr, err, ap); 563 va_end(ap); 564 fprintf(stderr, "\n"); 565 } 566 567 static void zconf_error(const char *err, ...) 568 { 569 va_list ap; 570 571 yynerrs++; 572 fprintf(stderr, "%s:%d: ", cur_filename, cur_lineno); 573 va_start(ap, err); 574 vfprintf(stderr, err, ap); 575 va_end(ap); 576 fprintf(stderr, "\n"); 577 } 578 579 static void yyerror(const char *err) 580 { 581 fprintf(stderr, "%s:%d: %s\n", cur_filename, cur_lineno, err); 582 } 583 584 static void print_quoted_string(FILE *out, const char *str) 585 { 586 const char *p; 587 int len; 588 589 putc('"', out); 590 while ((p = strchr(str, '"'))) { 591 len = p - str; 592 if (len) 593 fprintf(out, "%.*s", len, str); 594 fputs("\\\"", out); 595 str = p + 1; 596 } 597 fputs(str, out); 598 putc('"', out); 599 } 600 601 static void print_symbol(FILE *out, struct menu *menu) 602 { 603 struct symbol *sym = menu->sym; 604 struct property *prop; 605 606 if (sym_is_choice(sym)) 607 fprintf(out, "\nchoice\n"); 608 else 609 fprintf(out, "\nconfig %s\n", sym->name); 610 switch (sym->type) { 611 case S_BOOLEAN: 612 fputs(" bool\n", out); 613 break; 614 case S_TRISTATE: 615 fputs(" tristate\n", out); 616 break; 617 case S_STRING: 618 fputs(" string\n", out); 619 break; 620 case S_INT: 621 fputs(" integer\n", out); 622 break; 623 case S_HEX: 624 fputs(" hex\n", out); 625 break; 626 default: 627 fputs(" ???\n", out); 628 break; 629 } 630 for (prop = sym->prop; prop; prop = prop->next) { 631 if (prop->menu != menu) 632 continue; 633 switch (prop->type) { 634 case P_PROMPT: 635 fputs(" prompt ", out); 636 print_quoted_string(out, prop->text); 637 if (!expr_is_yes(prop->visible.expr)) { 638 fputs(" if ", out); 639 expr_fprint(prop->visible.expr, out); 640 } 641 fputc('\n', out); 642 break; 643 case P_DEFAULT: 644 fputs( " default ", out); 645 expr_fprint(prop->expr, out); 646 if (!expr_is_yes(prop->visible.expr)) { 647 fputs(" if ", out); 648 expr_fprint(prop->visible.expr, out); 649 } 650 fputc('\n', out); 651 break; 652 case P_CHOICE: 653 fputs(" #choice value\n", out); 654 break; 655 case P_SELECT: 656 fputs( " select ", out); 657 expr_fprint(prop->expr, out); 658 fputc('\n', out); 659 break; 660 case P_IMPLY: 661 fputs( " imply ", out); 662 expr_fprint(prop->expr, out); 663 fputc('\n', out); 664 break; 665 case P_RANGE: 666 fputs( " range ", out); 667 expr_fprint(prop->expr, out); 668 fputc('\n', out); 669 break; 670 case P_MENU: 671 fputs( " menu ", out); 672 print_quoted_string(out, prop->text); 673 fputc('\n', out); 674 break; 675 case P_SYMBOL: 676 fputs( " symbol ", out); 677 fprintf(out, "%s\n", prop->menu->sym->name); 678 break; 679 default: 680 fprintf(out, " unknown prop %d!\n", prop->type); 681 break; 682 } 683 } 684 if (menu->help) { 685 int len = strlen(menu->help); 686 while (menu->help[--len] == '\n') 687 menu->help[len] = 0; 688 fprintf(out, " help\n%s\n", menu->help); 689 } 690 } 691 692 void zconfdump(FILE *out) 693 { 694 struct property *prop; 695 struct symbol *sym; 696 struct menu *menu; 697 698 menu = rootmenu.list; 699 while (menu) { 700 if ((sym = menu->sym)) 701 print_symbol(out, menu); 702 else if ((prop = menu->prompt)) { 703 switch (prop->type) { 704 case P_COMMENT: 705 fputs("\ncomment ", out); 706 print_quoted_string(out, prop->text); 707 fputs("\n", out); 708 break; 709 case P_MENU: 710 fputs("\nmenu ", out); 711 print_quoted_string(out, prop->text); 712 fputs("\n", out); 713 break; 714 default: 715 ; 716 } 717 if (!expr_is_yes(prop->visible.expr)) { 718 fputs(" depends ", out); 719 expr_fprint(prop->visible.expr, out); 720 fputc('\n', out); 721 } 722 } 723 724 if (menu->list) 725 menu = menu->list; 726 else if (menu->next) 727 menu = menu->next; 728 else while ((menu = menu->parent)) { 729 if (menu->prompt && menu->prompt->type == P_MENU) 730 fputs("\nendmenu\n", out); 731 if (menu->next) { 732 menu = menu->next; 733 break; 734 } 735 } 736 } 737 } 738