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