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