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