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 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_PLUS_EQUAL 73 %token T_PROMPT 74 %token T_RANGE 75 %token T_SELECT 76 %token T_SOURCE 77 %token T_STRING 78 %token T_TRISTATE 79 %token T_VISIBLE 80 %token T_EOL 81 %token <string> T_ASSIGN_VAL 82 83 %left T_OR 84 %left T_AND 85 %left T_EQUAL T_UNEQUAL 86 %left T_LESS T_LESS_EQUAL T_GREATER T_GREATER_EQUAL 87 %nonassoc T_NOT 88 89 %type <symbol> nonconst_symbol 90 %type <symbol> symbol 91 %type <type> type logic_type default 92 %type <expr> expr 93 %type <expr> if_expr 94 %type <string> end 95 %type <menu> if_entry menu_entry choice_entry 96 %type <string> assign_val 97 %type <flavor> assign_op 98 99 %destructor { 100 fprintf(stderr, "%s:%d: missing end statement for this entry\n", 101 $$->filename, $$->lineno); 102 if (current_menu == $$) 103 menu_end_menu(); 104 } if_entry menu_entry choice_entry 105 106 %% 107 input: mainmenu_stmt stmt_list | stmt_list; 108 109 /* mainmenu entry */ 110 111 mainmenu_stmt: T_MAINMENU T_WORD_QUOTE T_EOL 112 { 113 menu_add_prompt(P_MENU, $2, NULL); 114 }; 115 116 stmt_list: 117 /* empty */ 118 | stmt_list assignment_stmt 119 | stmt_list choice_stmt 120 | stmt_list comment_stmt 121 | stmt_list config_stmt 122 | stmt_list if_stmt 123 | stmt_list menu_stmt 124 | stmt_list menuconfig_stmt 125 | stmt_list source_stmt 126 | stmt_list T_WORD error T_EOL { zconf_error("unknown statement \"%s\"", $2); } 127 | stmt_list error T_EOL { zconf_error("invalid statement"); } 128 ; 129 130 stmt_list_in_choice: 131 /* empty */ 132 | stmt_list_in_choice comment_stmt 133 | stmt_list_in_choice config_stmt 134 | stmt_list_in_choice if_stmt_in_choice 135 | stmt_list_in_choice error T_EOL { zconf_error("invalid statement"); } 136 ; 137 138 /* config/menuconfig entry */ 139 140 config_entry_start: T_CONFIG nonconst_symbol T_EOL 141 { 142 menu_add_entry($2); 143 printd(DEBUG_PARSE, "%s:%d:config %s\n", cur_filename, cur_lineno, $2->name); 144 }; 145 146 config_stmt: config_entry_start config_option_list 147 { 148 printd(DEBUG_PARSE, "%s:%d:endconfig\n", cur_filename, cur_lineno); 149 }; 150 151 menuconfig_entry_start: T_MENUCONFIG nonconst_symbol T_EOL 152 { 153 menu_add_entry($2); 154 printd(DEBUG_PARSE, "%s:%d:menuconfig %s\n", cur_filename, cur_lineno, $2->name); 155 }; 156 157 menuconfig_stmt: menuconfig_entry_start config_option_list 158 { 159 if (current_entry->prompt) 160 current_entry->prompt->type = P_MENU; 161 else 162 zconfprint("warning: menuconfig statement without prompt"); 163 printd(DEBUG_PARSE, "%s:%d:endconfig\n", cur_filename, cur_lineno); 164 }; 165 166 config_option_list: 167 /* empty */ 168 | config_option_list config_option 169 | config_option_list depends 170 | config_option_list help 171 ; 172 173 config_option: type prompt_stmt_opt T_EOL 174 { 175 menu_set_type($1); 176 printd(DEBUG_PARSE, "%s:%d:type(%u)\n", cur_filename, cur_lineno, $1); 177 }; 178 179 config_option: T_PROMPT T_WORD_QUOTE if_expr T_EOL 180 { 181 menu_add_prompt(P_PROMPT, $2, $3); 182 printd(DEBUG_PARSE, "%s:%d:prompt\n", cur_filename, cur_lineno); 183 }; 184 185 config_option: default expr if_expr T_EOL 186 { 187 menu_add_expr(P_DEFAULT, $2, $3); 188 if ($1 != S_UNKNOWN) 189 menu_set_type($1); 190 printd(DEBUG_PARSE, "%s:%d:default(%u)\n", cur_filename, cur_lineno, 191 $1); 192 }; 193 194 config_option: T_SELECT nonconst_symbol if_expr T_EOL 195 { 196 menu_add_symbol(P_SELECT, $2, $3); 197 printd(DEBUG_PARSE, "%s:%d:select\n", cur_filename, cur_lineno); 198 }; 199 200 config_option: T_IMPLY nonconst_symbol if_expr T_EOL 201 { 202 menu_add_symbol(P_IMPLY, $2, $3); 203 printd(DEBUG_PARSE, "%s:%d:imply\n", cur_filename, cur_lineno); 204 }; 205 206 config_option: T_RANGE symbol symbol if_expr T_EOL 207 { 208 menu_add_expr(P_RANGE, expr_alloc_comp(E_RANGE,$2, $3), $4); 209 printd(DEBUG_PARSE, "%s:%d:range\n", cur_filename, cur_lineno); 210 }; 211 212 config_option: T_MODULES T_EOL 213 { 214 if (modules_sym) 215 zconf_error("symbol '%s' redefines option 'modules' already defined by symbol '%s'", 216 current_entry->sym->name, modules_sym->name); 217 modules_sym = current_entry->sym; 218 }; 219 220 /* choice entry */ 221 222 choice: T_CHOICE T_EOL 223 { 224 struct symbol *sym = sym_lookup(NULL, 0); 225 226 menu_add_entry(sym); 227 menu_add_expr(P_CHOICE, NULL, NULL); 228 printd(DEBUG_PARSE, "%s:%d:choice\n", cur_filename, cur_lineno); 229 }; 230 231 choice_entry: choice choice_option_list 232 { 233 if (!current_entry->prompt) { 234 fprintf(stderr, "%s:%d: error: choice must have a prompt\n", 235 current_entry->filename, current_entry->lineno); 236 yynerrs++; 237 } 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_DEFAULT nonconst_symbol if_expr T_EOL 273 { 274 menu_add_symbol(P_DEFAULT, $2, $3); 275 printd(DEBUG_PARSE, "%s:%d:default\n", cur_filename, cur_lineno); 276 }; 277 278 type: 279 logic_type 280 | T_INT { $$ = S_INT; } 281 | T_HEX { $$ = S_HEX; } 282 | T_STRING { $$ = S_STRING; } 283 284 logic_type: 285 T_BOOL { $$ = S_BOOLEAN; } 286 | T_TRISTATE { $$ = S_TRISTATE; } 287 288 default: 289 T_DEFAULT { $$ = S_UNKNOWN; } 290 | T_DEF_BOOL { $$ = S_BOOLEAN; } 291 | T_DEF_TRISTATE { $$ = S_TRISTATE; } 292 293 /* if entry */ 294 295 if_entry: T_IF expr T_EOL 296 { 297 printd(DEBUG_PARSE, "%s:%d:if\n", cur_filename, cur_lineno); 298 menu_add_entry(NULL); 299 menu_add_dep($2); 300 $$ = menu_add_menu(); 301 }; 302 303 if_end: end 304 { 305 if (zconf_endtoken($1, "if")) { 306 menu_end_menu(); 307 printd(DEBUG_PARSE, "%s:%d:endif\n", cur_filename, cur_lineno); 308 } 309 }; 310 311 if_stmt: if_entry stmt_list if_end 312 ; 313 314 if_stmt_in_choice: if_entry stmt_list_in_choice if_end 315 ; 316 317 /* menu entry */ 318 319 menu: T_MENU T_WORD_QUOTE T_EOL 320 { 321 menu_add_entry(NULL); 322 menu_add_prompt(P_MENU, $2, NULL); 323 printd(DEBUG_PARSE, "%s:%d:menu\n", cur_filename, cur_lineno); 324 }; 325 326 menu_entry: menu menu_option_list 327 { 328 $$ = menu_add_menu(); 329 }; 330 331 menu_end: end 332 { 333 if (zconf_endtoken($1, "menu")) { 334 menu_end_menu(); 335 printd(DEBUG_PARSE, "%s:%d:endmenu\n", cur_filename, cur_lineno); 336 } 337 }; 338 339 menu_stmt: menu_entry stmt_list menu_end 340 ; 341 342 menu_option_list: 343 /* empty */ 344 | menu_option_list visible 345 | menu_option_list depends 346 ; 347 348 source_stmt: T_SOURCE T_WORD_QUOTE T_EOL 349 { 350 printd(DEBUG_PARSE, "%s:%d:source %s\n", cur_filename, cur_lineno, $2); 351 zconf_nextfile($2); 352 free($2); 353 }; 354 355 /* comment entry */ 356 357 comment: T_COMMENT T_WORD_QUOTE T_EOL 358 { 359 menu_add_entry(NULL); 360 menu_add_prompt(P_COMMENT, $2, NULL); 361 printd(DEBUG_PARSE, "%s:%d:comment\n", cur_filename, cur_lineno); 362 }; 363 364 comment_stmt: comment comment_option_list 365 ; 366 367 comment_option_list: 368 /* empty */ 369 | comment_option_list depends 370 ; 371 372 /* help option */ 373 374 help_start: T_HELP T_EOL 375 { 376 printd(DEBUG_PARSE, "%s:%d:help\n", cur_filename, cur_lineno); 377 zconf_starthelp(); 378 }; 379 380 help: help_start T_HELPTEXT 381 { 382 if (current_entry->help) { 383 free(current_entry->help); 384 zconfprint("warning: '%s' defined with more than one help text -- only the last one will be used", 385 current_entry->sym->name ?: "<choice>"); 386 } 387 388 /* Is the help text empty or all whitespace? */ 389 if ($2[strspn($2, " \f\n\r\t\v")] == '\0') 390 zconfprint("warning: '%s' defined with blank help text", 391 current_entry->sym->name ?: "<choice>"); 392 393 current_entry->help = $2; 394 }; 395 396 /* depends option */ 397 398 depends: T_DEPENDS T_ON expr T_EOL 399 { 400 menu_add_dep($3); 401 printd(DEBUG_PARSE, "%s:%d:depends on\n", cur_filename, cur_lineno); 402 }; 403 404 /* visibility option */ 405 visible: T_VISIBLE if_expr T_EOL 406 { 407 menu_add_visibility($2); 408 }; 409 410 /* prompt statement */ 411 412 prompt_stmt_opt: 413 /* empty */ 414 | T_WORD_QUOTE if_expr 415 { 416 menu_add_prompt(P_PROMPT, $1, $2); 417 }; 418 419 end: T_ENDMENU T_EOL { $$ = "menu"; } 420 | T_ENDCHOICE T_EOL { $$ = "choice"; } 421 | T_ENDIF T_EOL { $$ = "if"; } 422 ; 423 424 if_expr: /* empty */ { $$ = NULL; } 425 | T_IF expr { $$ = $2; } 426 ; 427 428 expr: symbol { $$ = expr_alloc_symbol($1); } 429 | symbol T_LESS symbol { $$ = expr_alloc_comp(E_LTH, $1, $3); } 430 | symbol T_LESS_EQUAL symbol { $$ = expr_alloc_comp(E_LEQ, $1, $3); } 431 | symbol T_GREATER symbol { $$ = expr_alloc_comp(E_GTH, $1, $3); } 432 | symbol T_GREATER_EQUAL symbol { $$ = expr_alloc_comp(E_GEQ, $1, $3); } 433 | symbol T_EQUAL symbol { $$ = expr_alloc_comp(E_EQUAL, $1, $3); } 434 | symbol T_UNEQUAL symbol { $$ = expr_alloc_comp(E_UNEQUAL, $1, $3); } 435 | T_OPEN_PAREN expr T_CLOSE_PAREN { $$ = $2; } 436 | T_NOT expr { $$ = expr_alloc_one(E_NOT, $2); } 437 | expr T_OR expr { $$ = expr_alloc_two(E_OR, $1, $3); } 438 | expr T_AND expr { $$ = expr_alloc_two(E_AND, $1, $3); } 439 ; 440 441 /* For symbol definitions, selects, etc., where quotes are not accepted */ 442 nonconst_symbol: T_WORD { $$ = sym_lookup($1, 0); free($1); }; 443 444 symbol: nonconst_symbol 445 | T_WORD_QUOTE { $$ = sym_lookup($1, SYMBOL_CONST); free($1); } 446 ; 447 448 /* assignment statement */ 449 450 assignment_stmt: T_WORD assign_op assign_val T_EOL { variable_add($1, $3, $2); free($1); free($3); } 451 452 assign_op: 453 T_EQUAL { $$ = VAR_RECURSIVE; } 454 | T_COLON_EQUAL { $$ = VAR_SIMPLE; } 455 | T_PLUS_EQUAL { $$ = VAR_APPEND; } 456 ; 457 458 assign_val: 459 /* empty */ { $$ = xstrdup(""); }; 460 | T_ASSIGN_VAL 461 ; 462 463 %% 464 465 void conf_parse(const char *name) 466 { 467 struct menu *menu; 468 469 autoconf_cmd = str_new(); 470 471 str_printf(&autoconf_cmd, "\ndeps_config := \\\n"); 472 473 zconf_initscan(name); 474 475 _menu_init(); 476 477 if (getenv("ZCONF_DEBUG")) 478 yydebug = 1; 479 yyparse(); 480 481 /* 482 * FIXME: 483 * cur_filename and cur_lineno are used even after yyparse(); 484 * menu_finalize() calls menu_add_symbol(). This should be fixed. 485 */ 486 cur_filename = "<none>"; 487 cur_lineno = 0; 488 489 str_printf(&autoconf_cmd, 490 "\n" 491 "$(autoconfig): $(deps_config)\n" 492 "$(deps_config): ;\n"); 493 494 env_write_dep(&autoconf_cmd); 495 496 /* Variables are expanded in the parse phase. We can free them here. */ 497 variable_all_del(); 498 499 if (yynerrs) 500 exit(1); 501 if (!modules_sym) 502 modules_sym = &symbol_no; 503 504 if (!menu_has_prompt(&rootmenu)) { 505 current_entry = &rootmenu; 506 menu_add_prompt(P_MENU, "Main menu", NULL); 507 } 508 509 menu_finalize(); 510 511 menu_for_each_entry(menu) { 512 if (menu->sym && sym_check_deps(menu->sym)) 513 yynerrs++; 514 } 515 516 if (yynerrs) 517 exit(1); 518 conf_set_changed(true); 519 } 520 521 static 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 (strcmp(current_menu->filename, cur_filename)) { 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->filename, current_menu->lineno, 535 expected_tokenname); 536 yynerrs++; 537 return false; 538 } 539 return true; 540 } 541 542 static void zconfprint(const char *err, ...) 543 { 544 va_list ap; 545 546 fprintf(stderr, "%s:%d: ", cur_filename, cur_lineno); 547 va_start(ap, err); 548 vfprintf(stderr, err, ap); 549 va_end(ap); 550 fprintf(stderr, "\n"); 551 } 552 553 static void zconf_error(const char *err, ...) 554 { 555 va_list ap; 556 557 yynerrs++; 558 fprintf(stderr, "%s:%d: ", cur_filename, cur_lineno); 559 va_start(ap, err); 560 vfprintf(stderr, err, ap); 561 va_end(ap); 562 fprintf(stderr, "\n"); 563 } 564 565 static void yyerror(const char *err) 566 { 567 fprintf(stderr, "%s:%d: %s\n", cur_filename, cur_lineno, err); 568 } 569 570 static 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 587 static 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 678 void 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