0001
0002
0003
0004
0005
0006 #include <ctype.h>
0007 #include <stdarg.h>
0008 #include <stdlib.h>
0009 #include <string.h>
0010
0011 #include "lkc.h"
0012 #include "internal.h"
0013
0014 static const char nohelp_text[] = "There is no help available for this option.";
0015
0016 struct menu rootmenu;
0017 static struct menu **last_entry_ptr;
0018
0019 struct file *file_list;
0020 struct file *current_file;
0021
0022 void menu_warn(struct menu *menu, const char *fmt, ...)
0023 {
0024 va_list ap;
0025 va_start(ap, fmt);
0026 fprintf(stderr, "%s:%d:warning: ", menu->file->name, menu->lineno);
0027 vfprintf(stderr, fmt, ap);
0028 fprintf(stderr, "\n");
0029 va_end(ap);
0030 }
0031
0032 static void prop_warn(struct property *prop, const char *fmt, ...)
0033 {
0034 va_list ap;
0035 va_start(ap, fmt);
0036 fprintf(stderr, "%s:%d:warning: ", prop->file->name, prop->lineno);
0037 vfprintf(stderr, fmt, ap);
0038 fprintf(stderr, "\n");
0039 va_end(ap);
0040 }
0041
0042 void _menu_init(void)
0043 {
0044 current_entry = current_menu = &rootmenu;
0045 last_entry_ptr = &rootmenu.list;
0046 }
0047
0048 void menu_add_entry(struct symbol *sym)
0049 {
0050 struct menu *menu;
0051
0052 menu = xmalloc(sizeof(*menu));
0053 memset(menu, 0, sizeof(*menu));
0054 menu->sym = sym;
0055 menu->parent = current_menu;
0056 menu->file = current_file;
0057 menu->lineno = zconf_lineno();
0058
0059 *last_entry_ptr = menu;
0060 last_entry_ptr = &menu->next;
0061 current_entry = menu;
0062 if (sym)
0063 menu_add_symbol(P_SYMBOL, sym, NULL);
0064 }
0065
0066 struct menu *menu_add_menu(void)
0067 {
0068 last_entry_ptr = ¤t_entry->list;
0069 current_menu = current_entry;
0070 return current_menu;
0071 }
0072
0073 void menu_end_menu(void)
0074 {
0075 last_entry_ptr = ¤t_menu->next;
0076 current_menu = current_menu->parent;
0077 }
0078
0079
0080
0081
0082
0083 static struct expr *rewrite_m(struct expr *e)
0084 {
0085 if (!e)
0086 return e;
0087
0088 switch (e->type) {
0089 case E_NOT:
0090 e->left.expr = rewrite_m(e->left.expr);
0091 break;
0092 case E_OR:
0093 case E_AND:
0094 e->left.expr = rewrite_m(e->left.expr);
0095 e->right.expr = rewrite_m(e->right.expr);
0096 break;
0097 case E_SYMBOL:
0098
0099 if (e->left.sym == &symbol_mod)
0100 return expr_alloc_and(e, expr_alloc_symbol(modules_sym));
0101 break;
0102 default:
0103 break;
0104 }
0105 return e;
0106 }
0107
0108 void menu_add_dep(struct expr *dep)
0109 {
0110 current_entry->dep = expr_alloc_and(current_entry->dep, dep);
0111 }
0112
0113 void menu_set_type(int type)
0114 {
0115 struct symbol *sym = current_entry->sym;
0116
0117 if (sym->type == type)
0118 return;
0119 if (sym->type == S_UNKNOWN) {
0120 sym->type = type;
0121 return;
0122 }
0123 menu_warn(current_entry,
0124 "ignoring type redefinition of '%s' from '%s' to '%s'",
0125 sym->name ? sym->name : "<choice>",
0126 sym_type_name(sym->type), sym_type_name(type));
0127 }
0128
0129 static struct property *menu_add_prop(enum prop_type type, struct expr *expr,
0130 struct expr *dep)
0131 {
0132 struct property *prop;
0133
0134 prop = xmalloc(sizeof(*prop));
0135 memset(prop, 0, sizeof(*prop));
0136 prop->type = type;
0137 prop->file = current_file;
0138 prop->lineno = zconf_lineno();
0139 prop->menu = current_entry;
0140 prop->expr = expr;
0141 prop->visible.expr = dep;
0142
0143
0144 if (current_entry->sym) {
0145 struct property **propp;
0146
0147 for (propp = ¤t_entry->sym->prop;
0148 *propp;
0149 propp = &(*propp)->next)
0150 ;
0151 *propp = prop;
0152 }
0153
0154 return prop;
0155 }
0156
0157 struct property *menu_add_prompt(enum prop_type type, char *prompt,
0158 struct expr *dep)
0159 {
0160 struct property *prop = menu_add_prop(type, NULL, dep);
0161
0162 if (isspace(*prompt)) {
0163 prop_warn(prop, "leading whitespace ignored");
0164 while (isspace(*prompt))
0165 prompt++;
0166 }
0167 if (current_entry->prompt)
0168 prop_warn(prop, "prompt redefined");
0169
0170
0171 if (type == P_PROMPT) {
0172 struct menu *menu = current_entry;
0173
0174 while ((menu = menu->parent) != NULL) {
0175 struct expr *dup_expr;
0176
0177 if (!menu->visibility)
0178 continue;
0179
0180
0181
0182
0183
0184
0185
0186 dup_expr = expr_copy(menu->visibility);
0187
0188 prop->visible.expr = expr_alloc_and(prop->visible.expr,
0189 dup_expr);
0190 }
0191 }
0192
0193 current_entry->prompt = prop;
0194 prop->text = prompt;
0195
0196 return prop;
0197 }
0198
0199 void menu_add_visibility(struct expr *expr)
0200 {
0201 current_entry->visibility = expr_alloc_and(current_entry->visibility,
0202 expr);
0203 }
0204
0205 void menu_add_expr(enum prop_type type, struct expr *expr, struct expr *dep)
0206 {
0207 menu_add_prop(type, expr, dep);
0208 }
0209
0210 void menu_add_symbol(enum prop_type type, struct symbol *sym, struct expr *dep)
0211 {
0212 menu_add_prop(type, expr_alloc_symbol(sym), dep);
0213 }
0214
0215 static int menu_validate_number(struct symbol *sym, struct symbol *sym2)
0216 {
0217 return sym2->type == S_INT || sym2->type == S_HEX ||
0218 (sym2->type == S_UNKNOWN && sym_string_valid(sym, sym2->name));
0219 }
0220
0221 static void sym_check_prop(struct symbol *sym)
0222 {
0223 struct property *prop;
0224 struct symbol *sym2;
0225 char *use;
0226
0227 for (prop = sym->prop; prop; prop = prop->next) {
0228 switch (prop->type) {
0229 case P_DEFAULT:
0230 if ((sym->type == S_STRING || sym->type == S_INT || sym->type == S_HEX) &&
0231 prop->expr->type != E_SYMBOL)
0232 prop_warn(prop,
0233 "default for config symbol '%s'"
0234 " must be a single symbol", sym->name);
0235 if (prop->expr->type != E_SYMBOL)
0236 break;
0237 sym2 = prop_get_symbol(prop);
0238 if (sym->type == S_HEX || sym->type == S_INT) {
0239 if (!menu_validate_number(sym, sym2))
0240 prop_warn(prop,
0241 "'%s': number is invalid",
0242 sym->name);
0243 }
0244 if (sym_is_choice(sym)) {
0245 struct property *choice_prop =
0246 sym_get_choice_prop(sym2);
0247
0248 if (!choice_prop ||
0249 prop_get_symbol(choice_prop) != sym)
0250 prop_warn(prop,
0251 "choice default symbol '%s' is not contained in the choice",
0252 sym2->name);
0253 }
0254 break;
0255 case P_SELECT:
0256 case P_IMPLY:
0257 use = prop->type == P_SELECT ? "select" : "imply";
0258 sym2 = prop_get_symbol(prop);
0259 if (sym->type != S_BOOLEAN && sym->type != S_TRISTATE)
0260 prop_warn(prop,
0261 "config symbol '%s' uses %s, but is "
0262 "not bool or tristate", sym->name, use);
0263 else if (sym2->type != S_UNKNOWN &&
0264 sym2->type != S_BOOLEAN &&
0265 sym2->type != S_TRISTATE)
0266 prop_warn(prop,
0267 "'%s' has wrong type. '%s' only "
0268 "accept arguments of bool and "
0269 "tristate type", sym2->name, use);
0270 break;
0271 case P_RANGE:
0272 if (sym->type != S_INT && sym->type != S_HEX)
0273 prop_warn(prop, "range is only allowed "
0274 "for int or hex symbols");
0275 if (!menu_validate_number(sym, prop->expr->left.sym) ||
0276 !menu_validate_number(sym, prop->expr->right.sym))
0277 prop_warn(prop, "range is invalid");
0278 break;
0279 default:
0280 ;
0281 }
0282 }
0283 }
0284
0285 void menu_finalize(struct menu *parent)
0286 {
0287 struct menu *menu, *last_menu;
0288 struct symbol *sym;
0289 struct property *prop;
0290 struct expr *parentdep, *basedep, *dep, *dep2, **ep;
0291
0292 sym = parent->sym;
0293 if (parent->list) {
0294
0295
0296
0297
0298
0299 if (sym && sym_is_choice(sym)) {
0300 if (sym->type == S_UNKNOWN) {
0301
0302 current_entry = parent;
0303 for (menu = parent->list; menu; menu = menu->next) {
0304 if (menu->sym && menu->sym->type != S_UNKNOWN) {
0305 menu_set_type(menu->sym->type);
0306 break;
0307 }
0308 }
0309 }
0310
0311 for (menu = parent->list; menu; menu = menu->next) {
0312 current_entry = menu;
0313 if (menu->sym && menu->sym->type == S_UNKNOWN)
0314 menu_set_type(sym->type);
0315 }
0316
0317
0318
0319
0320
0321
0322
0323 parentdep = expr_alloc_symbol(sym);
0324 } else {
0325
0326 parentdep = parent->dep;
0327 }
0328
0329
0330 for (menu = parent->list; menu; menu = menu->next) {
0331
0332
0333
0334
0335 basedep = rewrite_m(menu->dep);
0336 basedep = expr_transform(basedep);
0337 basedep = expr_alloc_and(expr_copy(parentdep), basedep);
0338 basedep = expr_eliminate_dups(basedep);
0339 menu->dep = basedep;
0340
0341 if (menu->sym)
0342
0343
0344
0345
0346 prop = menu->sym->prop;
0347 else
0348
0349
0350
0351
0352 prop = menu->prompt;
0353
0354
0355 for (; prop; prop = prop->next) {
0356 if (prop->menu != menu)
0357
0358
0359
0360
0361
0362
0363
0364
0365
0366
0367
0368
0369
0370
0371
0372 continue;
0373
0374
0375
0376
0377
0378
0379 dep = rewrite_m(prop->visible.expr);
0380 dep = expr_transform(dep);
0381 dep = expr_alloc_and(expr_copy(basedep), dep);
0382 dep = expr_eliminate_dups(dep);
0383 if (menu->sym && menu->sym->type != S_TRISTATE)
0384 dep = expr_trans_bool(dep);
0385 prop->visible.expr = dep;
0386
0387
0388
0389
0390
0391 if (prop->type == P_SELECT) {
0392 struct symbol *es = prop_get_symbol(prop);
0393 es->rev_dep.expr = expr_alloc_or(es->rev_dep.expr,
0394 expr_alloc_and(expr_alloc_symbol(menu->sym), expr_copy(dep)));
0395 } else if (prop->type == P_IMPLY) {
0396 struct symbol *es = prop_get_symbol(prop);
0397 es->implied.expr = expr_alloc_or(es->implied.expr,
0398 expr_alloc_and(expr_alloc_symbol(menu->sym), expr_copy(dep)));
0399 }
0400 }
0401 }
0402
0403 if (sym && sym_is_choice(sym))
0404 expr_free(parentdep);
0405
0406
0407
0408
0409
0410 for (menu = parent->list; menu; menu = menu->next)
0411 menu_finalize(menu);
0412 } else if (sym) {
0413
0414
0415
0416
0417
0418
0419
0420
0421
0422
0423
0424
0425
0426
0427
0428
0429
0430
0431
0432
0433
0434
0435 basedep = parent->prompt ? parent->prompt->visible.expr : NULL;
0436 basedep = expr_trans_compare(basedep, E_UNEQUAL, &symbol_no);
0437 basedep = expr_eliminate_dups(expr_transform(basedep));
0438
0439
0440 last_menu = NULL;
0441 for (menu = parent->next; menu; menu = menu->next) {
0442 dep = menu->prompt ? menu->prompt->visible.expr : menu->dep;
0443 if (!expr_contains_symbol(dep, sym))
0444
0445 break;
0446 if (expr_depends_symbol(dep, sym))
0447
0448 goto next;
0449
0450
0451
0452
0453
0454
0455
0456
0457
0458
0459 dep = expr_trans_compare(dep, E_UNEQUAL, &symbol_no);
0460 dep = expr_eliminate_dups(expr_transform(dep));
0461 dep2 = expr_copy(basedep);
0462 expr_eliminate_eq(&dep, &dep2);
0463 expr_free(dep);
0464 if (!expr_is_yes(dep2)) {
0465
0466 expr_free(dep2);
0467 break;
0468 }
0469
0470 expr_free(dep2);
0471 next:
0472 menu_finalize(menu);
0473 menu->parent = parent;
0474 last_menu = menu;
0475 }
0476 expr_free(basedep);
0477 if (last_menu) {
0478 parent->list = parent->next;
0479 parent->next = last_menu->next;
0480 last_menu->next = NULL;
0481 }
0482
0483 sym->dir_dep.expr = expr_alloc_or(sym->dir_dep.expr, parent->dep);
0484 }
0485 for (menu = parent->list; menu; menu = menu->next) {
0486 if (sym && sym_is_choice(sym) &&
0487 menu->sym && !sym_is_choice_value(menu->sym)) {
0488 current_entry = menu;
0489 menu->sym->flags |= SYMBOL_CHOICEVAL;
0490 if (!menu->prompt)
0491 menu_warn(menu, "choice value must have a prompt");
0492 for (prop = menu->sym->prop; prop; prop = prop->next) {
0493 if (prop->type == P_DEFAULT)
0494 prop_warn(prop, "defaults for choice "
0495 "values not supported");
0496 if (prop->menu == menu)
0497 continue;
0498 if (prop->type == P_PROMPT &&
0499 prop->menu->parent->sym != sym)
0500 prop_warn(prop, "choice value used outside its choice group");
0501 }
0502
0503
0504
0505
0506
0507
0508 if (sym->type == S_TRISTATE && menu->sym->type != S_TRISTATE) {
0509 basedep = expr_alloc_comp(E_EQUAL, sym, &symbol_yes);
0510 menu->dep = expr_alloc_and(basedep, menu->dep);
0511 for (prop = menu->sym->prop; prop; prop = prop->next) {
0512 if (prop->menu != menu)
0513 continue;
0514 prop->visible.expr = expr_alloc_and(expr_copy(basedep),
0515 prop->visible.expr);
0516 }
0517 }
0518 menu_add_symbol(P_CHOICE, sym, NULL);
0519 prop = sym_get_choice_prop(sym);
0520 for (ep = &prop->expr; *ep; ep = &(*ep)->left.expr)
0521 ;
0522 *ep = expr_alloc_one(E_LIST, NULL);
0523 (*ep)->right.sym = menu->sym;
0524 }
0525
0526
0527
0528
0529
0530
0531
0532
0533
0534
0535
0536
0537
0538
0539
0540
0541
0542
0543
0544
0545
0546
0547
0548
0549
0550
0551
0552
0553
0554 if (menu->list && (!menu->prompt || !menu->prompt->text)) {
0555 for (last_menu = menu->list; ; last_menu = last_menu->next) {
0556 last_menu->parent = parent;
0557 if (!last_menu->next)
0558 break;
0559 }
0560 last_menu->next = menu->next;
0561 menu->next = menu->list;
0562 menu->list = NULL;
0563 }
0564 }
0565
0566 if (sym && !(sym->flags & SYMBOL_WARNED)) {
0567 if (sym->type == S_UNKNOWN)
0568 menu_warn(parent, "config symbol defined without type");
0569
0570 if (sym_is_choice(sym) && !parent->prompt)
0571 menu_warn(parent, "choice must have a prompt");
0572
0573
0574 sym_check_prop(sym);
0575 sym->flags |= SYMBOL_WARNED;
0576 }
0577
0578
0579
0580
0581
0582
0583
0584
0585
0586
0587 if (sym && !sym_is_optional(sym) && parent->prompt) {
0588 sym->rev_dep.expr = expr_alloc_or(sym->rev_dep.expr,
0589 expr_alloc_and(parent->prompt->visible.expr,
0590 expr_alloc_symbol(&symbol_mod)));
0591 }
0592 }
0593
0594 bool menu_has_prompt(struct menu *menu)
0595 {
0596 if (!menu->prompt)
0597 return false;
0598 return true;
0599 }
0600
0601
0602
0603
0604
0605
0606 bool menu_is_empty(struct menu *menu)
0607 {
0608 struct menu *child;
0609
0610 for (child = menu->list; child; child = child->next) {
0611 if (menu_is_visible(child))
0612 return(false);
0613 }
0614 return(true);
0615 }
0616
0617 bool menu_is_visible(struct menu *menu)
0618 {
0619 struct menu *child;
0620 struct symbol *sym;
0621 tristate visible;
0622
0623 if (!menu->prompt)
0624 return false;
0625
0626 if (menu->visibility) {
0627 if (expr_calc_value(menu->visibility) == no)
0628 return false;
0629 }
0630
0631 sym = menu->sym;
0632 if (sym) {
0633 sym_calc_value(sym);
0634 visible = menu->prompt->visible.tri;
0635 } else
0636 visible = menu->prompt->visible.tri = expr_calc_value(menu->prompt->visible.expr);
0637
0638 if (visible != no)
0639 return true;
0640
0641 if (!sym || sym_get_tristate_value(menu->sym) == no)
0642 return false;
0643
0644 for (child = menu->list; child; child = child->next) {
0645 if (menu_is_visible(child)) {
0646 if (sym)
0647 sym->flags |= SYMBOL_DEF_USER;
0648 return true;
0649 }
0650 }
0651
0652 return false;
0653 }
0654
0655 const char *menu_get_prompt(struct menu *menu)
0656 {
0657 if (menu->prompt)
0658 return menu->prompt->text;
0659 else if (menu->sym)
0660 return menu->sym->name;
0661 return NULL;
0662 }
0663
0664 struct menu *menu_get_parent_menu(struct menu *menu)
0665 {
0666 enum prop_type type;
0667
0668 for (; menu != &rootmenu; menu = menu->parent) {
0669 type = menu->prompt ? menu->prompt->type : 0;
0670 if (type == P_MENU)
0671 break;
0672 }
0673 return menu;
0674 }
0675
0676 bool menu_has_help(struct menu *menu)
0677 {
0678 return menu->help != NULL;
0679 }
0680
0681 const char *menu_get_help(struct menu *menu)
0682 {
0683 if (menu->help)
0684 return menu->help;
0685 else
0686 return "";
0687 }
0688
0689 static void get_def_str(struct gstr *r, struct menu *menu)
0690 {
0691 str_printf(r, "Defined at %s:%d\n",
0692 menu->file->name, menu->lineno);
0693 }
0694
0695 static void get_dep_str(struct gstr *r, struct expr *expr, const char *prefix)
0696 {
0697 if (!expr_is_yes(expr)) {
0698 str_append(r, prefix);
0699 expr_gstr_print(expr, r);
0700 str_append(r, "\n");
0701 }
0702 }
0703
0704 static void get_prompt_str(struct gstr *r, struct property *prop,
0705 struct list_head *head)
0706 {
0707 int i, j;
0708 struct menu *submenu[8], *menu, *location = NULL;
0709 struct jump_key *jump = NULL;
0710
0711 str_printf(r, " Prompt: %s\n", prop->text);
0712
0713 get_dep_str(r, prop->menu->dep, " Depends on: ");
0714
0715
0716
0717
0718
0719
0720
0721
0722 if (!expr_eq(prop->menu->dep, prop->visible.expr))
0723 get_dep_str(r, prop->visible.expr, " Visible if: ");
0724
0725 menu = prop->menu->parent;
0726 for (i = 0; menu && i < 8; menu = menu->parent) {
0727 bool accessible = menu_is_visible(menu);
0728
0729 submenu[i++] = menu;
0730 if (location == NULL && accessible)
0731 location = menu;
0732 }
0733 if (head && location) {
0734 jump = xmalloc(sizeof(struct jump_key));
0735
0736 if (menu_is_visible(prop->menu)) {
0737
0738
0739
0740
0741
0742
0743 jump->target = prop->menu;
0744 } else
0745 jump->target = location;
0746
0747 if (list_empty(head))
0748 jump->index = 0;
0749 else
0750 jump->index = list_entry(head->prev, struct jump_key,
0751 entries)->index + 1;
0752
0753 list_add_tail(&jump->entries, head);
0754 }
0755
0756 str_printf(r, " Location:\n");
0757 for (j = 4; --i >= 0; j += 2) {
0758 menu = submenu[i];
0759 if (jump && menu == location)
0760 jump->offset = strlen(r->s);
0761
0762 if (menu == &rootmenu)
0763
0764 str_printf(r, "%*cMain menu", j, ' ');
0765 else
0766 str_printf(r, "%*c-> %s", j, ' ', menu_get_prompt(menu));
0767
0768 if (menu->sym) {
0769 str_printf(r, " (%s [=%s])", menu->sym->name ?
0770 menu->sym->name : "<choice>",
0771 sym_get_string_value(menu->sym));
0772 }
0773 str_append(r, "\n");
0774 }
0775 }
0776
0777 static void get_symbol_props_str(struct gstr *r, struct symbol *sym,
0778 enum prop_type tok, const char *prefix)
0779 {
0780 bool hit = false;
0781 struct property *prop;
0782
0783 for_all_properties(sym, prop, tok) {
0784 if (!hit) {
0785 str_append(r, prefix);
0786 hit = true;
0787 } else
0788 str_printf(r, " && ");
0789 expr_gstr_print(prop->expr, r);
0790 }
0791 if (hit)
0792 str_append(r, "\n");
0793 }
0794
0795
0796
0797
0798 static void get_symbol_str(struct gstr *r, struct symbol *sym,
0799 struct list_head *head)
0800 {
0801 struct property *prop;
0802
0803 if (sym && sym->name) {
0804 str_printf(r, "Symbol: %s [=%s]\n", sym->name,
0805 sym_get_string_value(sym));
0806 str_printf(r, "Type : %s\n", sym_type_name(sym->type));
0807 if (sym->type == S_INT || sym->type == S_HEX) {
0808 prop = sym_get_range_prop(sym);
0809 if (prop) {
0810 str_printf(r, "Range : ");
0811 expr_gstr_print(prop->expr, r);
0812 str_append(r, "\n");
0813 }
0814 }
0815 }
0816
0817
0818 for_all_properties(sym, prop, P_SYMBOL) {
0819 if (prop->menu->prompt) {
0820 get_def_str(r, prop->menu);
0821 get_prompt_str(r, prop->menu->prompt, head);
0822 }
0823 }
0824
0825 for_all_properties(sym, prop, P_SYMBOL) {
0826 if (!prop->menu->prompt) {
0827 get_def_str(r, prop->menu);
0828 get_dep_str(r, prop->menu->dep, " Depends on: ");
0829 }
0830 }
0831
0832 get_symbol_props_str(r, sym, P_SELECT, "Selects: ");
0833 if (sym->rev_dep.expr) {
0834 expr_gstr_print_revdep(sym->rev_dep.expr, r, yes, "Selected by [y]:\n");
0835 expr_gstr_print_revdep(sym->rev_dep.expr, r, mod, "Selected by [m]:\n");
0836 expr_gstr_print_revdep(sym->rev_dep.expr, r, no, "Selected by [n]:\n");
0837 }
0838
0839 get_symbol_props_str(r, sym, P_IMPLY, "Implies: ");
0840 if (sym->implied.expr) {
0841 expr_gstr_print_revdep(sym->implied.expr, r, yes, "Implied by [y]:\n");
0842 expr_gstr_print_revdep(sym->implied.expr, r, mod, "Implied by [m]:\n");
0843 expr_gstr_print_revdep(sym->implied.expr, r, no, "Implied by [n]:\n");
0844 }
0845
0846 str_append(r, "\n\n");
0847 }
0848
0849 struct gstr get_relations_str(struct symbol **sym_arr, struct list_head *head)
0850 {
0851 struct symbol *sym;
0852 struct gstr res = str_new();
0853 int i;
0854
0855 for (i = 0; sym_arr && (sym = sym_arr[i]); i++)
0856 get_symbol_str(&res, sym, head);
0857 if (!i)
0858 str_append(&res, "No matches found.\n");
0859 return res;
0860 }
0861
0862
0863 void menu_get_ext_help(struct menu *menu, struct gstr *help)
0864 {
0865 struct symbol *sym = menu->sym;
0866 const char *help_text = nohelp_text;
0867
0868 if (menu_has_help(menu)) {
0869 if (sym->name)
0870 str_printf(help, "%s%s:\n\n", CONFIG_, sym->name);
0871 help_text = menu_get_help(menu);
0872 }
0873 str_printf(help, "%s\n", help_text);
0874 if (sym)
0875 get_symbol_str(help, sym, NULL);
0876 }