1 // SPDX-License-Identifier: GPL-2.0 2 /* 3 * Copyright (C) 2002 Roman Zippel <zippel@linux-m68k.org> 4 */ 5 6 #include <ctype.h> 7 #include <stdarg.h> 8 #include <stdlib.h> 9 #include <string.h> 10 11 #include "lkc.h" 12 #include "internal.h" 13 #include "list.h" 14 15 static const char nohelp_text[] = "There is no help available for this option."; 16 17 struct menu rootmenu; 18 static struct menu **last_entry_ptr; 19 20 void menu_warn(struct menu *menu, const char *fmt, ...) 21 { 22 va_list ap; 23 va_start(ap, fmt); 24 fprintf(stderr, "%s:%d:warning: ", menu->filename, menu->lineno); 25 vfprintf(stderr, fmt, ap); 26 fprintf(stderr, "\n"); 27 va_end(ap); 28 } 29 30 static void prop_warn(struct property *prop, const char *fmt, ...) 31 { 32 va_list ap; 33 va_start(ap, fmt); 34 fprintf(stderr, "%s:%d:warning: ", prop->filename, prop->lineno); 35 vfprintf(stderr, fmt, ap); 36 fprintf(stderr, "\n"); 37 va_end(ap); 38 } 39 40 void _menu_init(void) 41 { 42 current_entry = current_menu = &rootmenu; 43 last_entry_ptr = &rootmenu.list; 44 } 45 46 void menu_add_entry(struct symbol *sym) 47 { 48 struct menu *menu; 49 50 menu = xmalloc(sizeof(*menu)); 51 memset(menu, 0, sizeof(*menu)); 52 menu->sym = sym; 53 menu->parent = current_menu; 54 menu->filename = cur_filename; 55 menu->lineno = cur_lineno; 56 57 *last_entry_ptr = menu; 58 last_entry_ptr = &menu->next; 59 current_entry = menu; 60 if (sym) 61 menu_add_symbol(P_SYMBOL, sym, NULL); 62 } 63 64 struct menu *menu_add_menu(void) 65 { 66 last_entry_ptr = ¤t_entry->list; 67 current_menu = current_entry; 68 return current_menu; 69 } 70 71 void menu_end_menu(void) 72 { 73 last_entry_ptr = ¤t_menu->next; 74 current_menu = current_menu->parent; 75 } 76 77 /* 78 * Rewrites 'm' to 'm' && MODULES, so that it evaluates to 'n' when running 79 * without modules 80 */ 81 static struct expr *rewrite_m(struct expr *e) 82 { 83 if (!e) 84 return e; 85 86 switch (e->type) { 87 case E_NOT: 88 e->left.expr = rewrite_m(e->left.expr); 89 break; 90 case E_OR: 91 case E_AND: 92 e->left.expr = rewrite_m(e->left.expr); 93 e->right.expr = rewrite_m(e->right.expr); 94 break; 95 case E_SYMBOL: 96 /* change 'm' into 'm' && MODULES */ 97 if (e->left.sym == &symbol_mod) 98 return expr_alloc_and(e, expr_alloc_symbol(modules_sym)); 99 break; 100 default: 101 break; 102 } 103 return e; 104 } 105 106 void menu_add_dep(struct expr *dep) 107 { 108 current_entry->dep = expr_alloc_and(current_entry->dep, dep); 109 } 110 111 void menu_set_type(int type) 112 { 113 struct symbol *sym = current_entry->sym; 114 115 if (sym->type == type) 116 return; 117 if (sym->type == S_UNKNOWN) { 118 sym->type = type; 119 return; 120 } 121 menu_warn(current_entry, 122 "ignoring type redefinition of '%s' from '%s' to '%s'", 123 sym->name ? sym->name : "<choice>", 124 sym_type_name(sym->type), sym_type_name(type)); 125 } 126 127 static struct property *menu_add_prop(enum prop_type type, struct expr *expr, 128 struct expr *dep) 129 { 130 struct property *prop; 131 132 prop = xmalloc(sizeof(*prop)); 133 memset(prop, 0, sizeof(*prop)); 134 prop->type = type; 135 prop->filename = cur_filename; 136 prop->lineno = cur_lineno; 137 prop->menu = current_entry; 138 prop->expr = expr; 139 prop->visible.expr = dep; 140 141 /* append property to the prop list of symbol */ 142 if (current_entry->sym) { 143 struct property **propp; 144 145 for (propp = ¤t_entry->sym->prop; 146 *propp; 147 propp = &(*propp)->next) 148 ; 149 *propp = prop; 150 } 151 152 return prop; 153 } 154 155 struct property *menu_add_prompt(enum prop_type type, char *prompt, 156 struct expr *dep) 157 { 158 struct property *prop = menu_add_prop(type, NULL, dep); 159 160 if (isspace(*prompt)) { 161 prop_warn(prop, "leading whitespace ignored"); 162 while (isspace(*prompt)) 163 prompt++; 164 } 165 if (current_entry->prompt) 166 prop_warn(prop, "prompt redefined"); 167 168 /* Apply all upper menus' visibilities to actual prompts. */ 169 if (type == P_PROMPT) { 170 struct menu *menu = current_entry; 171 172 while ((menu = menu->parent) != NULL) { 173 struct expr *dup_expr; 174 175 if (!menu->visibility) 176 continue; 177 /* 178 * Do not add a reference to the menu's visibility 179 * expression but use a copy of it. Otherwise the 180 * expression reduction functions will modify 181 * expressions that have multiple references which 182 * can cause unwanted side effects. 183 */ 184 dup_expr = expr_copy(menu->visibility); 185 186 prop->visible.expr = expr_alloc_and(prop->visible.expr, 187 dup_expr); 188 } 189 } 190 191 current_entry->prompt = prop; 192 prop->text = prompt; 193 194 return prop; 195 } 196 197 void menu_add_visibility(struct expr *expr) 198 { 199 current_entry->visibility = expr_alloc_and(current_entry->visibility, 200 expr); 201 } 202 203 void menu_add_expr(enum prop_type type, struct expr *expr, struct expr *dep) 204 { 205 menu_add_prop(type, expr, dep); 206 } 207 208 void menu_add_symbol(enum prop_type type, struct symbol *sym, struct expr *dep) 209 { 210 menu_add_prop(type, expr_alloc_symbol(sym), dep); 211 } 212 213 static int menu_validate_number(struct symbol *sym, struct symbol *sym2) 214 { 215 return sym2->type == S_INT || sym2->type == S_HEX || 216 (sym2->type == S_UNKNOWN && sym_string_valid(sym, sym2->name)); 217 } 218 219 static void sym_check_prop(struct symbol *sym) 220 { 221 struct property *prop; 222 struct symbol *sym2; 223 char *use; 224 225 for (prop = sym->prop; prop; prop = prop->next) { 226 switch (prop->type) { 227 case P_DEFAULT: 228 if ((sym->type == S_STRING || sym->type == S_INT || sym->type == S_HEX) && 229 prop->expr->type != E_SYMBOL) 230 prop_warn(prop, 231 "default for config symbol '%s'" 232 " must be a single symbol", sym->name); 233 if (prop->expr->type != E_SYMBOL) 234 break; 235 sym2 = prop_get_symbol(prop); 236 if (sym->type == S_HEX || sym->type == S_INT) { 237 if (!menu_validate_number(sym, sym2)) 238 prop_warn(prop, 239 "'%s': number is invalid", 240 sym->name); 241 } 242 if (sym_is_choice(sym)) { 243 struct property *choice_prop = 244 sym_get_choice_prop(sym2); 245 246 if (!choice_prop || 247 prop_get_symbol(choice_prop) != sym) 248 prop_warn(prop, 249 "choice default symbol '%s' is not contained in the choice", 250 sym2->name); 251 } 252 break; 253 case P_SELECT: 254 case P_IMPLY: 255 use = prop->type == P_SELECT ? "select" : "imply"; 256 sym2 = prop_get_symbol(prop); 257 if (sym->type != S_BOOLEAN && sym->type != S_TRISTATE) 258 prop_warn(prop, 259 "config symbol '%s' uses %s, but is " 260 "not bool or tristate", sym->name, use); 261 else if (sym2->type != S_UNKNOWN && 262 sym2->type != S_BOOLEAN && 263 sym2->type != S_TRISTATE) 264 prop_warn(prop, 265 "'%s' has wrong type. '%s' only " 266 "accept arguments of bool and " 267 "tristate type", sym2->name, use); 268 break; 269 case P_RANGE: 270 if (sym->type != S_INT && sym->type != S_HEX) 271 prop_warn(prop, "range is only allowed " 272 "for int or hex symbols"); 273 if (!menu_validate_number(sym, prop->expr->left.sym) || 274 !menu_validate_number(sym, prop->expr->right.sym)) 275 prop_warn(prop, "range is invalid"); 276 break; 277 default: 278 ; 279 } 280 } 281 } 282 283 void menu_finalize(struct menu *parent) 284 { 285 struct menu *menu, *last_menu; 286 struct symbol *sym; 287 struct property *prop; 288 struct expr *parentdep, *basedep, *dep, *dep2, **ep; 289 290 sym = parent->sym; 291 if (parent->list) { 292 /* 293 * This menu node has children. We (recursively) process them 294 * and propagate parent dependencies before moving on. 295 */ 296 297 if (sym && sym_is_choice(sym)) { 298 if (sym->type == S_UNKNOWN) { 299 /* find the first choice value to find out choice type */ 300 current_entry = parent; 301 for (menu = parent->list; menu; menu = menu->next) { 302 if (menu->sym && menu->sym->type != S_UNKNOWN) { 303 menu_set_type(menu->sym->type); 304 break; 305 } 306 } 307 } 308 309 /* 310 * Use the choice itself as the parent dependency of 311 * the contained items. This turns the mode of the 312 * choice into an upper bound on the visibility of the 313 * choice value symbols. 314 */ 315 parentdep = expr_alloc_symbol(sym); 316 } else { 317 /* Menu node for 'menu', 'if' */ 318 parentdep = parent->dep; 319 } 320 321 /* For each child menu node... */ 322 for (menu = parent->list; menu; menu = menu->next) { 323 /* 324 * Propagate parent dependencies to the child menu 325 * node, also rewriting and simplifying expressions 326 */ 327 basedep = rewrite_m(menu->dep); 328 basedep = expr_transform(basedep); 329 basedep = expr_alloc_and(expr_copy(parentdep), basedep); 330 basedep = expr_eliminate_dups(basedep); 331 menu->dep = basedep; 332 333 if (menu->sym) 334 /* 335 * Note: For symbols, all prompts are included 336 * too in the symbol's own property list 337 */ 338 prop = menu->sym->prop; 339 else 340 /* 341 * For non-symbol menu nodes, we just need to 342 * handle the prompt 343 */ 344 prop = menu->prompt; 345 346 /* For each property... */ 347 for (; prop; prop = prop->next) { 348 if (prop->menu != menu) 349 /* 350 * Two possibilities: 351 * 352 * 1. The property lacks dependencies 353 * and so isn't location-specific, 354 * e.g. an 'option' 355 * 356 * 2. The property belongs to a symbol 357 * defined in multiple locations and 358 * is from some other location. It 359 * will be handled there in that 360 * case. 361 * 362 * Skip the property. 363 */ 364 continue; 365 366 /* 367 * Propagate parent dependencies to the 368 * property's condition, rewriting and 369 * simplifying expressions at the same time 370 */ 371 dep = rewrite_m(prop->visible.expr); 372 dep = expr_transform(dep); 373 dep = expr_alloc_and(expr_copy(basedep), dep); 374 dep = expr_eliminate_dups(dep); 375 if (menu->sym && menu->sym->type != S_TRISTATE) 376 dep = expr_trans_bool(dep); 377 prop->visible.expr = dep; 378 379 /* 380 * Handle selects and implies, which modify the 381 * dependencies of the selected/implied symbol 382 */ 383 if (prop->type == P_SELECT) { 384 struct symbol *es = prop_get_symbol(prop); 385 es->rev_dep.expr = expr_alloc_or(es->rev_dep.expr, 386 expr_alloc_and(expr_alloc_symbol(menu->sym), expr_copy(dep))); 387 } else if (prop->type == P_IMPLY) { 388 struct symbol *es = prop_get_symbol(prop); 389 es->implied.expr = expr_alloc_or(es->implied.expr, 390 expr_alloc_and(expr_alloc_symbol(menu->sym), expr_copy(dep))); 391 } 392 } 393 } 394 395 if (sym && sym_is_choice(sym)) 396 expr_free(parentdep); 397 398 /* 399 * Recursively process children in the same fashion before 400 * moving on 401 */ 402 for (menu = parent->list; menu; menu = menu->next) 403 menu_finalize(menu); 404 } else if (sym) { 405 /* 406 * Automatic submenu creation. If sym is a symbol and A, B, C, 407 * ... are consecutive items (symbols, menus, ifs, etc.) that 408 * all depend on sym, then the following menu structure is 409 * created: 410 * 411 * sym 412 * +-A 413 * +-B 414 * +-C 415 * ... 416 * 417 * This also works recursively, giving the following structure 418 * if A is a symbol and B depends on A: 419 * 420 * sym 421 * +-A 422 * | +-B 423 * +-C 424 * ... 425 */ 426 427 basedep = parent->prompt ? parent->prompt->visible.expr : NULL; 428 basedep = expr_trans_compare(basedep, E_UNEQUAL, &symbol_no); 429 basedep = expr_eliminate_dups(expr_transform(basedep)); 430 431 /* Examine consecutive elements after sym */ 432 last_menu = NULL; 433 for (menu = parent->next; menu; menu = menu->next) { 434 dep = menu->prompt ? menu->prompt->visible.expr : menu->dep; 435 if (!expr_contains_symbol(dep, sym)) 436 /* No dependency, quit */ 437 break; 438 if (expr_depends_symbol(dep, sym)) 439 /* Absolute dependency, put in submenu */ 440 goto next; 441 442 /* 443 * Also consider it a dependency on sym if our 444 * dependencies contain sym and are a "superset" of 445 * sym's dependencies, e.g. '(sym || Q) && R' when sym 446 * depends on R. 447 * 448 * Note that 'R' might be from an enclosing menu or if, 449 * making this a more common case than it might seem. 450 */ 451 dep = expr_trans_compare(dep, E_UNEQUAL, &symbol_no); 452 dep = expr_eliminate_dups(expr_transform(dep)); 453 dep2 = expr_copy(basedep); 454 expr_eliminate_eq(&dep, &dep2); 455 expr_free(dep); 456 if (!expr_is_yes(dep2)) { 457 /* Not superset, quit */ 458 expr_free(dep2); 459 break; 460 } 461 /* Superset, put in submenu */ 462 expr_free(dep2); 463 next: 464 menu_finalize(menu); 465 menu->parent = parent; 466 last_menu = menu; 467 } 468 expr_free(basedep); 469 if (last_menu) { 470 parent->list = parent->next; 471 parent->next = last_menu->next; 472 last_menu->next = NULL; 473 } 474 475 sym->dir_dep.expr = expr_alloc_or(sym->dir_dep.expr, parent->dep); 476 } 477 for (menu = parent->list; menu; menu = menu->next) { 478 if (sym && sym_is_choice(sym) && 479 menu->sym && !sym_is_choice_value(menu->sym)) { 480 current_entry = menu; 481 menu->sym->flags |= SYMBOL_CHOICEVAL; 482 if (!menu->prompt) 483 menu_warn(menu, "choice value must have a prompt"); 484 for (prop = menu->sym->prop; prop; prop = prop->next) { 485 if (prop->type == P_DEFAULT) 486 prop_warn(prop, "defaults for choice " 487 "values not supported"); 488 if (prop->menu == menu) 489 continue; 490 if (prop->type == P_PROMPT && 491 prop->menu->parent->sym != sym) 492 prop_warn(prop, "choice value used outside its choice group"); 493 } 494 /* Non-tristate choice values of tristate choices must 495 * depend on the choice being set to Y. The choice 496 * values' dependencies were propagated to their 497 * properties above, so the change here must be re- 498 * propagated. 499 */ 500 if (sym->type == S_TRISTATE && menu->sym->type != S_TRISTATE) { 501 basedep = expr_alloc_comp(E_EQUAL, sym, &symbol_yes); 502 menu->dep = expr_alloc_and(basedep, menu->dep); 503 for (prop = menu->sym->prop; prop; prop = prop->next) { 504 if (prop->menu != menu) 505 continue; 506 prop->visible.expr = expr_alloc_and(expr_copy(basedep), 507 prop->visible.expr); 508 } 509 } 510 menu_add_symbol(P_CHOICE, sym, NULL); 511 prop = sym_get_choice_prop(sym); 512 for (ep = &prop->expr; *ep; ep = &(*ep)->left.expr) 513 ; 514 *ep = expr_alloc_one(E_LIST, NULL); 515 (*ep)->right.sym = menu->sym; 516 } 517 518 /* 519 * This code serves two purposes: 520 * 521 * (1) Flattening 'if' blocks, which do not specify a submenu 522 * and only add dependencies. 523 * 524 * (Automatic submenu creation might still create a submenu 525 * from an 'if' before this code runs.) 526 * 527 * (2) "Undoing" any automatic submenus created earlier below 528 * promptless symbols. 529 * 530 * Before: 531 * 532 * A 533 * if ... (or promptless symbol) 534 * +-B 535 * +-C 536 * D 537 * 538 * After: 539 * 540 * A 541 * if ... (or promptless symbol) 542 * B 543 * C 544 * D 545 */ 546 if (menu->list && (!menu->prompt || !menu->prompt->text)) { 547 for (last_menu = menu->list; ; last_menu = last_menu->next) { 548 last_menu->parent = parent; 549 if (!last_menu->next) 550 break; 551 } 552 last_menu->next = menu->next; 553 menu->next = menu->list; 554 menu->list = NULL; 555 } 556 } 557 558 if (sym && !(sym->flags & SYMBOL_WARNED)) { 559 if (sym->type == S_UNKNOWN) 560 menu_warn(parent, "config symbol defined without type"); 561 562 if (sym_is_choice(sym) && !parent->prompt) 563 menu_warn(parent, "choice must have a prompt"); 564 565 /* Check properties connected to this symbol */ 566 sym_check_prop(sym); 567 sym->flags |= SYMBOL_WARNED; 568 } 569 570 /* 571 * For non-optional choices, add a reverse dependency (corresponding to 572 * a select) of '<visibility> && m'. This prevents the user from 573 * setting the choice mode to 'n' when the choice is visible. 574 * 575 * This would also work for non-choice symbols, but only non-optional 576 * choices clear SYMBOL_OPTIONAL as of writing. Choices are implemented 577 * as a type of symbol. 578 */ 579 if (sym && !sym_is_optional(sym) && parent->prompt) { 580 sym->rev_dep.expr = expr_alloc_or(sym->rev_dep.expr, 581 expr_alloc_and(parent->prompt->visible.expr, 582 expr_alloc_symbol(&symbol_mod))); 583 } 584 } 585 586 bool menu_has_prompt(struct menu *menu) 587 { 588 if (!menu->prompt) 589 return false; 590 return true; 591 } 592 593 /* 594 * Determine if a menu is empty. 595 * A menu is considered empty if it contains no or only 596 * invisible entries. 597 */ 598 bool menu_is_empty(struct menu *menu) 599 { 600 struct menu *child; 601 602 for (child = menu->list; child; child = child->next) { 603 if (menu_is_visible(child)) 604 return(false); 605 } 606 return(true); 607 } 608 609 bool menu_is_visible(struct menu *menu) 610 { 611 struct menu *child; 612 struct symbol *sym; 613 tristate visible; 614 615 if (!menu->prompt) 616 return false; 617 618 if (menu->visibility) { 619 if (expr_calc_value(menu->visibility) == no) 620 return false; 621 } 622 623 sym = menu->sym; 624 if (sym) { 625 sym_calc_value(sym); 626 visible = menu->prompt->visible.tri; 627 } else 628 visible = menu->prompt->visible.tri = expr_calc_value(menu->prompt->visible.expr); 629 630 if (visible != no) 631 return true; 632 633 if (!sym || sym_get_tristate_value(menu->sym) == no) 634 return false; 635 636 for (child = menu->list; child; child = child->next) { 637 if (menu_is_visible(child)) { 638 if (sym) 639 sym->flags |= SYMBOL_DEF_USER; 640 return true; 641 } 642 } 643 644 return false; 645 } 646 647 const char *menu_get_prompt(struct menu *menu) 648 { 649 if (menu->prompt) 650 return menu->prompt->text; 651 else if (menu->sym) 652 return menu->sym->name; 653 return NULL; 654 } 655 656 struct menu *menu_get_parent_menu(struct menu *menu) 657 { 658 enum prop_type type; 659 660 for (; menu != &rootmenu; menu = menu->parent) { 661 type = menu->prompt ? menu->prompt->type : 0; 662 if (type == P_MENU) 663 break; 664 } 665 return menu; 666 } 667 668 static void get_def_str(struct gstr *r, struct menu *menu) 669 { 670 str_printf(r, "Defined at %s:%d\n", 671 menu->filename, menu->lineno); 672 } 673 674 static void get_dep_str(struct gstr *r, struct expr *expr, const char *prefix) 675 { 676 if (!expr_is_yes(expr)) { 677 str_append(r, prefix); 678 expr_gstr_print(expr, r); 679 str_append(r, "\n"); 680 } 681 } 682 683 int __attribute__((weak)) get_jump_key_char(void) 684 { 685 return -1; 686 } 687 688 static void get_prompt_str(struct gstr *r, struct property *prop, 689 struct list_head *head) 690 { 691 int i, j; 692 struct menu *submenu[8], *menu, *location = NULL; 693 struct jump_key *jump = NULL; 694 695 str_printf(r, " Prompt: %s\n", prop->text); 696 697 get_dep_str(r, prop->menu->dep, " Depends on: "); 698 /* 699 * Most prompts in Linux have visibility that exactly matches their 700 * dependencies. For these, we print only the dependencies to improve 701 * readability. However, prompts with inline "if" expressions and 702 * prompts with a parent that has a "visible if" expression have 703 * differing dependencies and visibility. In these rare cases, we 704 * print both. 705 */ 706 if (!expr_eq(prop->menu->dep, prop->visible.expr)) 707 get_dep_str(r, prop->visible.expr, " Visible if: "); 708 709 menu = prop->menu; 710 for (i = 0; menu != &rootmenu && i < 8; menu = menu->parent) { 711 submenu[i++] = menu; 712 if (location == NULL && menu_is_visible(menu)) 713 location = menu; 714 } 715 if (head && location) { 716 jump = xmalloc(sizeof(struct jump_key)); 717 jump->target = location; 718 list_add_tail(&jump->entries, head); 719 } 720 721 str_printf(r, " Location:\n"); 722 for (j = 0; --i >= 0; j++) { 723 int jk = -1; 724 int indent = 2 * j + 4; 725 726 menu = submenu[i]; 727 if (jump && menu == location) { 728 jump->offset = strlen(r->s); 729 jk = get_jump_key_char(); 730 } 731 732 if (jk >= 0) { 733 str_printf(r, "(%c)", jk); 734 indent -= 3; 735 } 736 737 str_printf(r, "%*c-> %s", indent, ' ', menu_get_prompt(menu)); 738 if (menu->sym) { 739 str_printf(r, " (%s [=%s])", menu->sym->name ? 740 menu->sym->name : "<choice>", 741 sym_get_string_value(menu->sym)); 742 } 743 str_append(r, "\n"); 744 } 745 } 746 747 static void get_symbol_props_str(struct gstr *r, struct symbol *sym, 748 enum prop_type tok, const char *prefix) 749 { 750 bool hit = false; 751 struct property *prop; 752 753 for_all_properties(sym, prop, tok) { 754 if (!hit) { 755 str_append(r, prefix); 756 hit = true; 757 } else 758 str_printf(r, " && "); 759 expr_gstr_print(prop->expr, r); 760 } 761 if (hit) 762 str_append(r, "\n"); 763 } 764 765 /* 766 * head is optional and may be NULL 767 */ 768 static void get_symbol_str(struct gstr *r, struct symbol *sym, 769 struct list_head *head) 770 { 771 struct property *prop; 772 773 if (sym && sym->name) { 774 str_printf(r, "Symbol: %s [=%s]\n", sym->name, 775 sym_get_string_value(sym)); 776 str_printf(r, "Type : %s\n", sym_type_name(sym->type)); 777 if (sym->type == S_INT || sym->type == S_HEX) { 778 prop = sym_get_range_prop(sym); 779 if (prop) { 780 str_printf(r, "Range : "); 781 expr_gstr_print(prop->expr, r); 782 str_append(r, "\n"); 783 } 784 } 785 } 786 787 /* Print the definitions with prompts before the ones without */ 788 for_all_properties(sym, prop, P_SYMBOL) { 789 if (prop->menu->prompt) { 790 get_def_str(r, prop->menu); 791 get_prompt_str(r, prop->menu->prompt, head); 792 } 793 } 794 795 for_all_properties(sym, prop, P_SYMBOL) { 796 if (!prop->menu->prompt) { 797 get_def_str(r, prop->menu); 798 get_dep_str(r, prop->menu->dep, " Depends on: "); 799 } 800 } 801 802 get_symbol_props_str(r, sym, P_SELECT, "Selects: "); 803 if (sym->rev_dep.expr) { 804 expr_gstr_print_revdep(sym->rev_dep.expr, r, yes, "Selected by [y]:\n"); 805 expr_gstr_print_revdep(sym->rev_dep.expr, r, mod, "Selected by [m]:\n"); 806 expr_gstr_print_revdep(sym->rev_dep.expr, r, no, "Selected by [n]:\n"); 807 } 808 809 get_symbol_props_str(r, sym, P_IMPLY, "Implies: "); 810 if (sym->implied.expr) { 811 expr_gstr_print_revdep(sym->implied.expr, r, yes, "Implied by [y]:\n"); 812 expr_gstr_print_revdep(sym->implied.expr, r, mod, "Implied by [m]:\n"); 813 expr_gstr_print_revdep(sym->implied.expr, r, no, "Implied by [n]:\n"); 814 } 815 816 str_append(r, "\n\n"); 817 } 818 819 struct gstr get_relations_str(struct symbol **sym_arr, struct list_head *head) 820 { 821 struct symbol *sym; 822 struct gstr res = str_new(); 823 int i; 824 825 for (i = 0; sym_arr && (sym = sym_arr[i]); i++) 826 get_symbol_str(&res, sym, head); 827 if (!i) 828 str_append(&res, "No matches found.\n"); 829 return res; 830 } 831 832 833 void menu_get_ext_help(struct menu *menu, struct gstr *help) 834 { 835 struct symbol *sym = menu->sym; 836 const char *help_text = nohelp_text; 837 838 if (menu->help) { 839 if (sym->name) 840 str_printf(help, "%s%s:\n\n", CONFIG_, sym->name); 841 help_text = menu->help; 842 } 843 str_printf(help, "%s\n", help_text); 844 if (sym) 845 get_symbol_str(help, sym, NULL); 846 } 847