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