Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0
0002 /*
0003  * Copyright (C) 2002 Roman Zippel <zippel@linux-m68k.org>
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 = &current_entry->list;
0069     current_menu = current_entry;
0070     return current_menu;
0071 }
0072 
0073 void menu_end_menu(void)
0074 {
0075     last_entry_ptr = &current_menu->next;
0076     current_menu = current_menu->parent;
0077 }
0078 
0079 /*
0080  * Rewrites 'm' to 'm' && MODULES, so that it evaluates to 'n' when running
0081  * without modules
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         /* change 'm' into 'm' && MODULES */
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     /* append property to the prop list of symbol */
0144     if (current_entry->sym) {
0145         struct property **propp;
0146 
0147         for (propp = &current_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     /* Apply all upper menus' visibilities to actual prompts. */
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              * Do not add a reference to the menu's visibility
0181              * expression but use a copy of it. Otherwise the
0182              * expression reduction functions will modify
0183              * expressions that have multiple references which
0184              * can cause unwanted side effects.
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          * This menu node has children. We (recursively) process them
0296          * and propagate parent dependencies before moving on.
0297          */
0298 
0299         if (sym && sym_is_choice(sym)) {
0300             if (sym->type == S_UNKNOWN) {
0301                 /* find the first choice value to find out choice type */
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             /* set the type of the remaining choice values */
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              * Use the choice itself as the parent dependency of
0319              * the contained items. This turns the mode of the
0320              * choice into an upper bound on the visibility of the
0321              * choice value symbols.
0322              */
0323             parentdep = expr_alloc_symbol(sym);
0324         } else {
0325             /* Menu node for 'menu', 'if' */
0326             parentdep = parent->dep;
0327         }
0328 
0329         /* For each child menu node... */
0330         for (menu = parent->list; menu; menu = menu->next) {
0331             /*
0332              * Propagate parent dependencies to the child menu
0333              * node, also rewriting and simplifying expressions
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                  * Note: For symbols, all prompts are included
0344                  * too in the symbol's own property list
0345                  */
0346                 prop = menu->sym->prop;
0347             else
0348                 /*
0349                  * For non-symbol menu nodes, we just need to
0350                  * handle the prompt
0351                  */
0352                 prop = menu->prompt;
0353 
0354             /* For each property... */
0355             for (; prop; prop = prop->next) {
0356                 if (prop->menu != menu)
0357                     /*
0358                      * Two possibilities:
0359                      *
0360                      * 1. The property lacks dependencies
0361                      *    and so isn't location-specific,
0362                      *    e.g. an 'option'
0363                      *
0364                      * 2. The property belongs to a symbol
0365                      *    defined in multiple locations and
0366                      *    is from some other location. It
0367                      *    will be handled there in that
0368                      *    case.
0369                      *
0370                      * Skip the property.
0371                      */
0372                     continue;
0373 
0374                 /*
0375                  * Propagate parent dependencies to the
0376                  * property's condition, rewriting and
0377                  * simplifying expressions at the same time
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                  * Handle selects and implies, which modify the
0389                  * dependencies of the selected/implied symbol
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          * Recursively process children in the same fashion before
0408          * moving on
0409          */
0410         for (menu = parent->list; menu; menu = menu->next)
0411             menu_finalize(menu);
0412     } else if (sym) {
0413         /*
0414          * Automatic submenu creation. If sym is a symbol and A, B, C,
0415          * ... are consecutive items (symbols, menus, ifs, etc.) that
0416          * all depend on sym, then the following menu structure is
0417          * created:
0418          *
0419          *  sym
0420          *   +-A
0421          *   +-B
0422          *   +-C
0423          *   ...
0424          *
0425          * This also works recursively, giving the following structure
0426          * if A is a symbol and B depends on A:
0427          *
0428          *  sym
0429          *   +-A
0430          *   | +-B
0431          *   +-C
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         /* Examine consecutive elements after sym */
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                 /* No dependency, quit */
0445                 break;
0446             if (expr_depends_symbol(dep, sym))
0447                 /* Absolute dependency, put in submenu */
0448                 goto next;
0449 
0450             /*
0451              * Also consider it a dependency on sym if our
0452              * dependencies contain sym and are a "superset" of
0453              * sym's dependencies, e.g. '(sym || Q) && R' when sym
0454              * depends on R.
0455              *
0456              * Note that 'R' might be from an enclosing menu or if,
0457              * making this a more common case than it might seem.
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                 /* Not superset, quit */
0466                 expr_free(dep2);
0467                 break;
0468             }
0469             /* Superset, put in submenu */
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             /* Non-tristate choice values of tristate choices must
0503              * depend on the choice being set to Y. The choice
0504              * values' dependencies were propagated to their
0505              * properties above, so the change here must be re-
0506              * propagated.
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          * This code serves two purposes:
0528          *
0529          * (1) Flattening 'if' blocks, which do not specify a submenu
0530          *     and only add dependencies.
0531          *
0532          *     (Automatic submenu creation might still create a submenu
0533          *     from an 'if' before this code runs.)
0534          *
0535          * (2) "Undoing" any automatic submenus created earlier below
0536          *     promptless symbols.
0537          *
0538          * Before:
0539          *
0540          *  A
0541          *  if ... (or promptless symbol)
0542          *   +-B
0543          *   +-C
0544          *  D
0545          *
0546          * After:
0547          *
0548          *  A
0549          *  if ... (or promptless symbol)
0550          *  B
0551          *  C
0552          *  D
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         /* Check properties connected to this symbol */
0574         sym_check_prop(sym);
0575         sym->flags |= SYMBOL_WARNED;
0576     }
0577 
0578     /*
0579      * For non-optional choices, add a reverse dependency (corresponding to
0580      * a select) of '<visibility> && m'. This prevents the user from
0581      * setting the choice mode to 'n' when the choice is visible.
0582      *
0583      * This would also work for non-choice symbols, but only non-optional
0584      * choices clear SYMBOL_OPTIONAL as of writing. Choices are implemented
0585      * as a type of symbol.
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  * Determine if a menu is empty.
0603  * A menu is considered empty if it contains no or only
0604  * invisible entries.
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      * Most prompts in Linux have visibility that exactly matches their
0716      * dependencies. For these, we print only the dependencies to improve
0717      * readability. However, prompts with inline "if" expressions and
0718      * prompts with a parent that has a "visible if" expression have
0719      * differing dependencies and visibility. In these rare cases, we
0720      * print both.
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              * There is not enough room to put the hint at the
0739              * beginning of the "Prompt" line. Put the hint on the
0740              * last "Location" line even when it would belong on
0741              * the former.
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             /* The real rootmenu prompt is ugly */
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  * head is optional and may be NULL
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     /* Print the definitions with prompts before the ones without */
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 }