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