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