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