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