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