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