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