at master 18 kB view raw
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 <xalloc.h> 15#include "lkc.h" 16#include "internal.h" 17#include "preprocess.h" 18 19#define printd(mask, fmt...) if (cdebug & (mask)) printf(fmt) 20 21#define PRINTD 0x0001 22#define DEBUG_PARSE 0x0002 23 24int cdebug = PRINTD; 25 26static void yyerror(const char *err); 27static void zconf_error(const char *err, ...); 28static bool zconf_endtoken(const char *tokenname, 29 const char *expected_tokenname); 30 31struct menu *current_menu, *current_entry, *current_choice; 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_PLUS_EQUAL 73%token T_PROMPT 74%token T_RANGE 75%token T_SELECT 76%token T_SOURCE 77%token T_STRING 78%token T_TRANSITIONAL 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 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> assign_val 98%type <flavor> assign_op 99 100%destructor { 101 fprintf(stderr, "%s:%d: missing end statement for this entry\n", 102 $$->filename, $$->lineno); 103 if (current_menu == $$) 104 menu_end_menu(); 105} if_entry menu_entry choice_entry 106 107%% 108input: mainmenu_stmt stmt_list | stmt_list; 109 110/* mainmenu entry */ 111 112mainmenu_stmt: T_MAINMENU T_WORD_QUOTE T_EOL 113{ 114 menu_add_prompt(P_MENU, $2, NULL); 115}; 116 117stmt_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 131stmt_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 141config_entry_start: T_CONFIG nonconst_symbol T_EOL 142{ 143 menu_add_entry($2, M_NORMAL); 144 printd(DEBUG_PARSE, "%s:%d:config %s\n", cur_filename, cur_lineno, $2->name); 145}; 146 147config_stmt: config_entry_start config_option_list 148{ 149 if (current_choice) { 150 if (!current_entry->prompt) { 151 fprintf(stderr, "%s:%d: error: choice member must have a prompt\n", 152 current_entry->filename, current_entry->lineno); 153 yynerrs++; 154 } 155 156 if (current_entry->sym->type != S_BOOLEAN) { 157 fprintf(stderr, "%s:%d: error: choice member must be bool\n", 158 current_entry->filename, current_entry->lineno); 159 yynerrs++; 160 } 161 162 /* 163 * If the same symbol appears twice in a choice block, the list 164 * node would be added twice, leading to a broken linked list. 165 * list_empty() ensures that this symbol has not yet added. 166 */ 167 if (list_empty(&current_entry->sym->choice_link)) 168 list_add_tail(&current_entry->sym->choice_link, 169 &current_choice->choice_members); 170 } 171 172 printd(DEBUG_PARSE, "%s:%d:endconfig\n", cur_filename, cur_lineno); 173}; 174 175menuconfig_entry_start: T_MENUCONFIG nonconst_symbol T_EOL 176{ 177 menu_add_entry($2, M_MENU); 178 printd(DEBUG_PARSE, "%s:%d:menuconfig %s\n", cur_filename, cur_lineno, $2->name); 179}; 180 181menuconfig_stmt: menuconfig_entry_start config_option_list 182{ 183 if (current_entry->prompt) 184 current_entry->prompt->type = P_MENU; 185 else 186 zconf_error("menuconfig statement without prompt"); 187 printd(DEBUG_PARSE, "%s:%d:endconfig\n", cur_filename, cur_lineno); 188}; 189 190config_option_list: 191 /* empty */ 192 | config_option_list config_option 193 | config_option_list depends 194 | config_option_list help 195; 196 197config_option: type prompt_stmt_opt T_EOL 198{ 199 menu_set_type($1); 200 printd(DEBUG_PARSE, "%s:%d:type(%u)\n", cur_filename, cur_lineno, $1); 201}; 202 203config_option: T_PROMPT T_WORD_QUOTE if_expr T_EOL 204{ 205 menu_add_prompt(P_PROMPT, $2, $3); 206 printd(DEBUG_PARSE, "%s:%d:prompt\n", cur_filename, cur_lineno); 207}; 208 209config_option: T_TRANSITIONAL T_EOL 210{ 211 current_entry->sym->flags |= SYMBOL_TRANS; 212 printd(DEBUG_PARSE, "%s:%d:transitional\n", cur_filename, cur_lineno); 213}; 214 215config_option: default expr if_expr T_EOL 216{ 217 menu_add_expr(P_DEFAULT, $2, $3); 218 if ($1 != S_UNKNOWN) 219 menu_set_type($1); 220 printd(DEBUG_PARSE, "%s:%d:default(%u)\n", cur_filename, cur_lineno, 221 $1); 222}; 223 224config_option: T_SELECT nonconst_symbol if_expr T_EOL 225{ 226 menu_add_symbol(P_SELECT, $2, $3); 227 printd(DEBUG_PARSE, "%s:%d:select\n", cur_filename, cur_lineno); 228}; 229 230config_option: T_IMPLY nonconst_symbol if_expr T_EOL 231{ 232 menu_add_symbol(P_IMPLY, $2, $3); 233 printd(DEBUG_PARSE, "%s:%d:imply\n", cur_filename, cur_lineno); 234}; 235 236config_option: T_RANGE symbol symbol if_expr T_EOL 237{ 238 menu_add_expr(P_RANGE, expr_alloc_comp(E_RANGE,$2, $3), $4); 239 printd(DEBUG_PARSE, "%s:%d:range\n", cur_filename, cur_lineno); 240}; 241 242config_option: T_MODULES T_EOL 243{ 244 if (modules_sym) 245 zconf_error("symbol '%s' redefines option 'modules' already defined by symbol '%s'", 246 current_entry->sym->name, modules_sym->name); 247 modules_sym = current_entry->sym; 248}; 249 250/* choice entry */ 251 252choice: T_CHOICE T_EOL 253{ 254 struct symbol *sym = sym_lookup(NULL, 0); 255 256 menu_add_entry(sym, M_CHOICE); 257 menu_set_type(S_BOOLEAN); 258 INIT_LIST_HEAD(&current_entry->choice_members); 259 260 printd(DEBUG_PARSE, "%s:%d:choice\n", cur_filename, cur_lineno); 261}; 262 263choice_entry: choice choice_option_list 264{ 265 if (!current_entry->prompt) { 266 fprintf(stderr, "%s:%d: error: choice must have a prompt\n", 267 current_entry->filename, current_entry->lineno); 268 yynerrs++; 269 } 270 271 $$ = menu_add_menu(); 272 273 current_choice = current_entry; 274}; 275 276choice_end: end 277{ 278 current_choice = NULL; 279 280 if (zconf_endtoken($1, "choice")) { 281 menu_end_menu(); 282 printd(DEBUG_PARSE, "%s:%d:endchoice\n", cur_filename, cur_lineno); 283 } 284}; 285 286choice_stmt: choice_entry stmt_list_in_choice choice_end 287; 288 289choice_option_list: 290 /* empty */ 291 | choice_option_list choice_option 292 | choice_option_list depends 293 | choice_option_list help 294; 295 296choice_option: T_PROMPT T_WORD_QUOTE if_expr T_EOL 297{ 298 menu_add_prompt(P_PROMPT, $2, $3); 299 printd(DEBUG_PARSE, "%s:%d:prompt\n", cur_filename, cur_lineno); 300}; 301 302choice_option: T_DEFAULT nonconst_symbol if_expr T_EOL 303{ 304 menu_add_symbol(P_DEFAULT, $2, $3); 305 printd(DEBUG_PARSE, "%s:%d:default\n", cur_filename, cur_lineno); 306}; 307 308type: 309 T_BOOL { $$ = S_BOOLEAN; } 310 | T_TRISTATE { $$ = S_TRISTATE; } 311 | T_INT { $$ = S_INT; } 312 | T_HEX { $$ = S_HEX; } 313 | T_STRING { $$ = S_STRING; } 314 315default: 316 T_DEFAULT { $$ = S_UNKNOWN; } 317 | T_DEF_BOOL { $$ = S_BOOLEAN; } 318 | T_DEF_TRISTATE { $$ = S_TRISTATE; } 319 320/* if entry */ 321 322if_entry: T_IF expr T_EOL 323{ 324 printd(DEBUG_PARSE, "%s:%d:if\n", cur_filename, cur_lineno); 325 menu_add_entry(NULL, M_IF); 326 menu_add_dep($2); 327 $$ = menu_add_menu(); 328}; 329 330if_end: end 331{ 332 if (zconf_endtoken($1, "if")) { 333 menu_end_menu(); 334 printd(DEBUG_PARSE, "%s:%d:endif\n", cur_filename, cur_lineno); 335 } 336}; 337 338if_stmt: if_entry stmt_list if_end 339; 340 341if_stmt_in_choice: if_entry stmt_list_in_choice if_end 342; 343 344/* menu entry */ 345 346menu: T_MENU T_WORD_QUOTE T_EOL 347{ 348 menu_add_entry(NULL, M_MENU); 349 menu_add_prompt(P_MENU, $2, NULL); 350 printd(DEBUG_PARSE, "%s:%d:menu\n", cur_filename, cur_lineno); 351}; 352 353menu_entry: menu menu_option_list 354{ 355 $$ = menu_add_menu(); 356}; 357 358menu_end: end 359{ 360 if (zconf_endtoken($1, "menu")) { 361 menu_end_menu(); 362 printd(DEBUG_PARSE, "%s:%d:endmenu\n", cur_filename, cur_lineno); 363 } 364}; 365 366menu_stmt: menu_entry stmt_list menu_end 367; 368 369menu_option_list: 370 /* empty */ 371 | menu_option_list visible 372 | menu_option_list depends 373; 374 375source_stmt: T_SOURCE T_WORD_QUOTE T_EOL 376{ 377 printd(DEBUG_PARSE, "%s:%d:source %s\n", cur_filename, cur_lineno, $2); 378 zconf_nextfile($2); 379 free($2); 380}; 381 382/* comment entry */ 383 384comment: T_COMMENT T_WORD_QUOTE T_EOL 385{ 386 menu_add_entry(NULL, M_COMMENT); 387 menu_add_prompt(P_COMMENT, $2, NULL); 388 printd(DEBUG_PARSE, "%s:%d:comment\n", cur_filename, cur_lineno); 389}; 390 391comment_stmt: comment comment_option_list 392; 393 394comment_option_list: 395 /* empty */ 396 | comment_option_list depends 397; 398 399/* help option */ 400 401help_start: T_HELP T_EOL 402{ 403 printd(DEBUG_PARSE, "%s:%d:help\n", cur_filename, cur_lineno); 404 zconf_starthelp(); 405}; 406 407help: help_start T_HELPTEXT 408{ 409 if (current_entry->help) { 410 free(current_entry->help); 411 zconf_error("'%s' defined with more than one help text", 412 current_entry->sym->name ?: "<choice>"); 413 } 414 415 /* Is the help text empty or all whitespace? */ 416 if ($2[strspn($2, " \f\n\r\t\v")] == '\0') 417 zconf_error("'%s' defined with blank help text", 418 current_entry->sym->name ?: "<choice>"); 419 420 current_entry->help = $2; 421}; 422 423/* depends option */ 424 425depends: T_DEPENDS T_ON expr T_EOL 426{ 427 menu_add_dep($3); 428 printd(DEBUG_PARSE, "%s:%d:depends on\n", cur_filename, cur_lineno); 429}; 430 431/* visibility option */ 432visible: T_VISIBLE if_expr T_EOL 433{ 434 menu_add_visibility($2); 435}; 436 437/* prompt statement */ 438 439prompt_stmt_opt: 440 /* empty */ 441 | T_WORD_QUOTE if_expr 442{ 443 menu_add_prompt(P_PROMPT, $1, $2); 444}; 445 446end: T_ENDMENU T_EOL { $$ = "menu"; } 447 | T_ENDCHOICE T_EOL { $$ = "choice"; } 448 | T_ENDIF T_EOL { $$ = "if"; } 449; 450 451if_expr: /* empty */ { $$ = NULL; } 452 | T_IF expr { $$ = $2; } 453; 454 455expr: symbol { $$ = expr_alloc_symbol($1); } 456 | symbol T_LESS symbol { $$ = expr_alloc_comp(E_LTH, $1, $3); } 457 | symbol T_LESS_EQUAL symbol { $$ = expr_alloc_comp(E_LEQ, $1, $3); } 458 | symbol T_GREATER symbol { $$ = expr_alloc_comp(E_GTH, $1, $3); } 459 | symbol T_GREATER_EQUAL symbol { $$ = expr_alloc_comp(E_GEQ, $1, $3); } 460 | symbol T_EQUAL symbol { $$ = expr_alloc_comp(E_EQUAL, $1, $3); } 461 | symbol T_UNEQUAL symbol { $$ = expr_alloc_comp(E_UNEQUAL, $1, $3); } 462 | T_OPEN_PAREN expr T_CLOSE_PAREN { $$ = $2; } 463 | T_NOT expr { $$ = expr_alloc_one(E_NOT, $2); } 464 | expr T_OR expr { $$ = expr_alloc_two(E_OR, $1, $3); } 465 | expr T_AND expr { $$ = expr_alloc_two(E_AND, $1, $3); } 466; 467 468/* For symbol definitions, selects, etc., where quotes are not accepted */ 469nonconst_symbol: T_WORD { $$ = sym_lookup($1, 0); free($1); }; 470 471symbol: nonconst_symbol 472 | T_WORD_QUOTE { $$ = sym_lookup($1, SYMBOL_CONST); free($1); } 473; 474 475/* assignment statement */ 476 477assignment_stmt: T_WORD assign_op assign_val T_EOL { variable_add($1, $3, $2); free($1); free($3); } 478 479assign_op: 480 T_EQUAL { $$ = VAR_RECURSIVE; } 481 | T_COLON_EQUAL { $$ = VAR_SIMPLE; } 482 | T_PLUS_EQUAL { $$ = VAR_APPEND; } 483; 484 485assign_val: 486 /* empty */ { $$ = xstrdup(""); }; 487 | T_ASSIGN_VAL 488; 489 490%% 491 492/** 493 * transitional_check_sanity - check transitional symbols have no other 494 * properties 495 * 496 * @menu: menu of the potentially transitional symbol 497 * 498 * Return: -1 if an error is found, 0 otherwise. 499 */ 500static int transitional_check_sanity(const struct menu *menu) 501{ 502 struct property *prop; 503 504 if (!menu->sym || !(menu->sym->flags & SYMBOL_TRANS)) 505 return 0; 506 507 /* Check for depends and visible conditions. */ 508 if ((menu->dep && !expr_is_yes(menu->dep)) || 509 (menu->visibility && !expr_is_yes(menu->visibility))) { 510 fprintf(stderr, "%s:%d: error: %s", 511 menu->filename, menu->lineno, 512 "transitional symbols can only have help sections\n"); 513 return -1; 514 } 515 516 /* Check for any property other than "help". */ 517 for (prop = menu->sym->prop; prop; prop = prop->next) { 518 if (prop->type != P_COMMENT) { 519 fprintf(stderr, "%s:%d: error: %s", 520 prop->filename, prop->lineno, 521 "transitional symbols can only have help sections\n"); 522 return -1; 523 } 524 } 525 526 return 0; 527} 528 529/** 530 * choice_check_sanity - check sanity of a choice member 531 * 532 * @menu: menu of the choice member 533 * 534 * Return: -1 if an error is found, 0 otherwise. 535 */ 536static int choice_check_sanity(const struct menu *menu) 537{ 538 struct property *prop; 539 int ret = 0; 540 541 for (prop = menu->sym->prop; prop; prop = prop->next) { 542 if (prop->type == P_DEFAULT) { 543 fprintf(stderr, "%s:%d: error: %s", 544 prop->filename, prop->lineno, 545 "defaults for choice values not supported\n"); 546 ret = -1; 547 } 548 549 if (prop->menu != menu && prop->type == P_PROMPT && 550 prop->menu->parent != menu->parent) { 551 fprintf(stderr, "%s:%d: error: %s", 552 prop->filename, prop->lineno, 553 "choice value has a prompt outside its choice group\n"); 554 ret = -1; 555 } 556 } 557 558 return ret; 559} 560 561void conf_parse(const char *name) 562{ 563 struct menu *menu; 564 565 autoconf_cmd = str_new(); 566 567 str_printf(&autoconf_cmd, "\ndeps_config := \\\n"); 568 569 zconf_initscan(name); 570 571 _menu_init(); 572 573 if (getenv("ZCONF_DEBUG")) 574 yydebug = 1; 575 yyparse(); 576 577 str_printf(&autoconf_cmd, 578 "\n" 579 "$(autoconfig): $(deps_config)\n" 580 "$(deps_config): ;\n"); 581 582 env_write_dep(&autoconf_cmd); 583 584 /* Variables are expanded in the parse phase. We can free them here. */ 585 variable_all_del(); 586 587 if (yynerrs) 588 exit(1); 589 if (!modules_sym) 590 modules_sym = &symbol_no; 591 592 if (!menu_has_prompt(&rootmenu)) { 593 current_entry = &rootmenu; 594 menu_add_prompt(P_MENU, "Main menu", NULL); 595 } 596 597 menu_finalize(); 598 599 menu_for_each_entry(menu) { 600 struct menu *child; 601 602 if (menu->sym && sym_check_deps(menu->sym)) 603 yynerrs++; 604 605 if (transitional_check_sanity(menu)) 606 yynerrs++; 607 608 if (menu->sym && sym_is_choice(menu->sym)) { 609 menu_for_each_sub_entry(child, menu) 610 if (child->sym && choice_check_sanity(child)) 611 yynerrs++; 612 } 613 } 614 615 if (yynerrs) 616 exit(1); 617 conf_set_changed(true); 618} 619 620static bool zconf_endtoken(const char *tokenname, 621 const char *expected_tokenname) 622{ 623 if (strcmp(tokenname, expected_tokenname)) { 624 zconf_error("unexpected '%s' within %s block", 625 tokenname, expected_tokenname); 626 yynerrs++; 627 return false; 628 } 629 if (strcmp(current_menu->filename, cur_filename)) { 630 zconf_error("'%s' in different file than '%s'", 631 tokenname, expected_tokenname); 632 fprintf(stderr, "%s:%d: location of the '%s'\n", 633 current_menu->filename, current_menu->lineno, 634 expected_tokenname); 635 yynerrs++; 636 return false; 637 } 638 return true; 639} 640 641static void zconf_error(const char *err, ...) 642{ 643 va_list ap; 644 645 yynerrs++; 646 fprintf(stderr, "%s:%d: ", cur_filename, cur_lineno); 647 va_start(ap, err); 648 vfprintf(stderr, err, ap); 649 va_end(ap); 650 fprintf(stderr, "\n"); 651} 652 653static void yyerror(const char *err) 654{ 655 fprintf(stderr, "%s:%d: %s\n", cur_filename, cur_lineno, err); 656} 657 658static void print_quoted_string(FILE *out, const char *str) 659{ 660 const char *p; 661 int len; 662 663 putc('"', out); 664 while ((p = strchr(str, '"'))) { 665 len = p - str; 666 if (len) 667 fprintf(out, "%.*s", len, str); 668 fputs("\\\"", out); 669 str = p + 1; 670 } 671 fputs(str, out); 672 putc('"', out); 673} 674 675static void print_symbol(FILE *out, const struct menu *menu) 676{ 677 struct symbol *sym = menu->sym; 678 struct property *prop; 679 680 if (sym_is_choice(sym)) 681 fprintf(out, "\nchoice\n"); 682 else 683 fprintf(out, "\nconfig %s\n", sym->name); 684 switch (sym->type) { 685 case S_BOOLEAN: 686 fputs(" bool\n", out); 687 break; 688 case S_TRISTATE: 689 fputs(" tristate\n", out); 690 break; 691 case S_STRING: 692 fputs(" string\n", out); 693 break; 694 case S_INT: 695 fputs(" integer\n", out); 696 break; 697 case S_HEX: 698 fputs(" hex\n", out); 699 break; 700 default: 701 fputs(" ???\n", out); 702 break; 703 } 704 for (prop = sym->prop; prop; prop = prop->next) { 705 if (prop->menu != menu) 706 continue; 707 switch (prop->type) { 708 case P_PROMPT: 709 fputs(" prompt ", out); 710 print_quoted_string(out, prop->text); 711 if (!expr_is_yes(prop->visible.expr)) { 712 fputs(" if ", out); 713 expr_fprint(prop->visible.expr, out); 714 } 715 fputc('\n', out); 716 break; 717 case P_DEFAULT: 718 fputs( " default ", out); 719 expr_fprint(prop->expr, out); 720 if (!expr_is_yes(prop->visible.expr)) { 721 fputs(" if ", out); 722 expr_fprint(prop->visible.expr, out); 723 } 724 fputc('\n', out); 725 break; 726 case P_SELECT: 727 fputs( " select ", out); 728 expr_fprint(prop->expr, out); 729 fputc('\n', out); 730 break; 731 case P_IMPLY: 732 fputs( " imply ", out); 733 expr_fprint(prop->expr, out); 734 fputc('\n', out); 735 break; 736 case P_RANGE: 737 fputs( " range ", out); 738 expr_fprint(prop->expr, out); 739 fputc('\n', out); 740 break; 741 case P_MENU: 742 fputs( " menu ", out); 743 print_quoted_string(out, prop->text); 744 fputc('\n', out); 745 break; 746 default: 747 fprintf(out, " unknown prop %d!\n", prop->type); 748 break; 749 } 750 } 751 if (menu->help) { 752 int len = strlen(menu->help); 753 while (menu->help[--len] == '\n') 754 menu->help[len] = 0; 755 fprintf(out, " help\n%s\n", menu->help); 756 } 757} 758 759void zconfdump(FILE *out) 760{ 761 struct property *prop; 762 struct symbol *sym; 763 struct menu *menu; 764 765 menu = rootmenu.list; 766 while (menu) { 767 if ((sym = menu->sym)) 768 print_symbol(out, menu); 769 else if ((prop = menu->prompt)) { 770 switch (prop->type) { 771 case P_COMMENT: 772 fputs("\ncomment ", out); 773 print_quoted_string(out, prop->text); 774 fputs("\n", out); 775 break; 776 case P_MENU: 777 fputs("\nmenu ", out); 778 print_quoted_string(out, prop->text); 779 fputs("\n", out); 780 break; 781 default: 782 ; 783 } 784 if (!expr_is_yes(prop->visible.expr)) { 785 fputs(" depends ", out); 786 expr_fprint(prop->visible.expr, out); 787 fputc('\n', out); 788 } 789 } 790 791 if (menu->list) 792 menu = menu->list; 793 else if (menu->next) 794 menu = menu->next; 795 else while ((menu = menu->parent)) { 796 if (menu->prompt && menu->prompt->type == P_MENU) 797 fputs("\nendmenu\n", out); 798 if (menu->next) { 799 menu = menu->next; 800 break; 801 } 802 } 803 } 804}