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