// SPDX-License-Identifier: GPL-2.0 /* * Copyright (C) 2002 Roman Zippel */ #include #include #include #include #include #include #include #include "internal.h" #include "lkc.h" struct symbol symbol_yes = { .name = "y", .type = S_TRISTATE, .curr = { "y", yes }, .menus = LIST_HEAD_INIT(symbol_yes.menus), .flags = SYMBOL_CONST|SYMBOL_VALID, }; struct symbol symbol_mod = { .name = "m", .type = S_TRISTATE, .curr = { "m", mod }, .menus = LIST_HEAD_INIT(symbol_mod.menus), .flags = SYMBOL_CONST|SYMBOL_VALID, }; struct symbol symbol_no = { .name = "n", .type = S_TRISTATE, .curr = { "n", no }, .menus = LIST_HEAD_INIT(symbol_no.menus), .flags = SYMBOL_CONST|SYMBOL_VALID, }; struct symbol *modules_sym; static tristate modules_val; static int sym_warnings; enum symbol_type sym_get_type(const struct symbol *sym) { enum symbol_type type = sym->type; if (type == S_TRISTATE && modules_val == no) type = S_BOOLEAN; return type; } const char *sym_type_name(enum symbol_type type) { switch (type) { case S_BOOLEAN: return "bool"; case S_TRISTATE: return "tristate"; case S_INT: return "integer"; case S_HEX: return "hex"; case S_STRING: return "string"; case S_UNKNOWN: return "unknown"; } return "???"; } /** * sym_get_choice_menu - get the parent choice menu if present * * @sym: a symbol pointer * * Return: a choice menu if this function is called against a choice member. */ struct menu *sym_get_choice_menu(const struct symbol *sym) { struct menu *menu = NULL; struct menu *m; /* * Choice members must have a prompt. Find a menu entry with a prompt, * and assume it resides inside a choice block. */ list_for_each_entry(m, &sym->menus, link) if (m->prompt) { menu = m; break; } if (!menu) return NULL; do { menu = menu->parent; } while (menu && !menu->sym); if (menu && menu->sym && sym_is_choice(menu->sym)) return menu; return NULL; } static struct property *sym_get_default_prop(struct symbol *sym) { struct property *prop; for_all_defaults(sym, prop) { prop->visible.tri = expr_calc_value(prop->visible.expr); if (prop->visible.tri != no) return prop; } return NULL; } struct property *sym_get_range_prop(struct symbol *sym) { struct property *prop; for_all_properties(sym, prop, P_RANGE) { prop->visible.tri = expr_calc_value(prop->visible.expr); if (prop->visible.tri != no) return prop; } return NULL; } static long long sym_get_range_val(struct symbol *sym, int base) { sym_calc_value(sym); switch (sym->type) { case S_INT: base = 10; break; case S_HEX: base = 16; break; default: break; } return strtoll(sym->curr.val, NULL, base); } static void sym_validate_range(struct symbol *sym) { struct property *prop; struct symbol *range_sym; int base; long long val, val2; switch (sym->type) { case S_INT: base = 10; break; case S_HEX: base = 16; break; default: return; } prop = sym_get_range_prop(sym); if (!prop) return; val = strtoll(sym->curr.val, NULL, base); range_sym = prop->expr->left.sym; val2 = sym_get_range_val(range_sym, base); if (val >= val2) { range_sym = prop->expr->right.sym; val2 = sym_get_range_val(range_sym, base); if (val <= val2) return; } sym->curr.val = range_sym->curr.val; } static void sym_set_changed(struct symbol *sym) { struct menu *menu; list_for_each_entry(menu, &sym->menus, link) menu->flags |= MENU_CHANGED; } static void sym_set_all_changed(void) { struct symbol *sym; for_all_symbols(sym) sym_set_changed(sym); } static void sym_calc_visibility(struct symbol *sym) { struct property *prop; tristate tri; /* any prompt visible? */ tri = no; for_all_prompts(sym, prop) { prop->visible.tri = expr_calc_value(prop->visible.expr); tri = EXPR_OR(tri, prop->visible.tri); } if (tri == mod && (sym->type != S_TRISTATE || modules_val == no)) tri = yes; if (sym->visible != tri) { sym->visible = tri; sym_set_changed(sym); } if (sym_is_choice_value(sym)) return; /* defaulting to "yes" if no explicit "depends on" are given */ tri = yes; if (sym->dir_dep.expr) tri = expr_calc_value(sym->dir_dep.expr); if (tri == mod && sym_get_type(sym) == S_BOOLEAN) tri = yes; if (sym->dir_dep.tri != tri) { sym->dir_dep.tri = tri; sym_set_changed(sym); } tri = no; if (sym->rev_dep.expr) tri = expr_calc_value(sym->rev_dep.expr); if (tri == mod && sym_get_type(sym) == S_BOOLEAN) tri = yes; if (sym->rev_dep.tri != tri) { sym->rev_dep.tri = tri; sym_set_changed(sym); } tri = no; if (sym->implied.expr) tri = expr_calc_value(sym->implied.expr); if (tri == mod && sym_get_type(sym) == S_BOOLEAN) tri = yes; if (sym->implied.tri != tri) { sym->implied.tri = tri; sym_set_changed(sym); } } /* * Find the default symbol for a choice. * First try the default values for the choice symbol * Next locate the first visible choice value * Return NULL if none was found */ struct symbol *sym_choice_default(struct menu *choice) { struct menu *menu; struct symbol *def_sym; struct property *prop; /* any of the defaults visible? */ for_all_defaults(choice->sym, prop) { prop->visible.tri = expr_calc_value(prop->visible.expr); if (prop->visible.tri == no) continue; def_sym = prop_get_symbol(prop); if (def_sym->visible != no) return def_sym; } /* just get the first visible value */ menu_for_each_sub_entry(menu, choice) if (menu->sym && menu->sym->visible != no) return menu->sym; /* failed to locate any defaults */ return NULL; } /* * sym_calc_choice - calculate symbol values in a choice * * @choice: a menu of the choice * * Return: a chosen symbol */ struct symbol *sym_calc_choice(struct menu *choice) { struct symbol *res = NULL; struct symbol *sym; struct menu *menu; /* Traverse the list of choice members in the priority order. */ list_for_each_entry(sym, &choice->choice_members, choice_link) { sym_calc_visibility(sym); if (sym->visible == no) continue; /* The first visible symble with the user value 'y'. */ if (sym_has_value(sym) && sym->def[S_DEF_USER].tri == yes) { res = sym; break; } } /* * If 'y' is not found in the user input, use the default, unless it is * explicitly set to 'n'. */ if (!res) { res = sym_choice_default(choice); if (res && sym_has_value(res) && res->def[S_DEF_USER].tri == no) res = NULL; } /* Still not found. Pick up the first visible, user-unspecified symbol. */ if (!res) { menu_for_each_sub_entry(menu, choice) { sym = menu->sym; if (!sym || sym->visible == no || sym_has_value(sym)) continue; res = sym; break; } } /* * Still not found. Traverse the linked list in the _reverse_ order to * pick up the least prioritized 'n'. */ if (!res) { list_for_each_entry_reverse(sym, &choice->choice_members, choice_link) { if (sym->visible == no) continue; res = sym; break; } } menu_for_each_sub_entry(menu, choice) { tristate val; sym = menu->sym; if (!sym || sym->visible == no) continue; val = sym == res ? yes : no; if (sym->curr.tri != val) sym_set_changed(sym); sym->curr.tri = val; sym->flags |= SYMBOL_VALID | SYMBOL_WRITE; } return res; } static void sym_warn_unmet_dep(const struct symbol *sym) { struct gstr gs = str_new(); str_printf(&gs, "\nWARNING: unmet direct dependencies detected for %s\n", sym->name); str_printf(&gs, " Depends on [%c]: ", sym->dir_dep.tri == mod ? 'm' : 'n'); expr_gstr_print(sym->dir_dep.expr, &gs); str_printf(&gs, "\n"); expr_gstr_print_revdep(sym->rev_dep.expr, &gs, yes, " Selected by [y]:\n"); expr_gstr_print_revdep(sym->rev_dep.expr, &gs, mod, " Selected by [m]:\n"); fputs(str_get(&gs), stderr); sym_warnings++; } bool sym_dep_errors(void) { if (sym_warnings) return getenv("KCONFIG_WERROR"); return false; } void sym_calc_value(struct symbol *sym) { struct symbol_value newval, oldval; struct property *prop; struct menu *choice_menu; if (!sym) return; if (sym->flags & SYMBOL_VALID) return; sym->flags |= SYMBOL_VALID; oldval = sym->curr; newval.tri = no; switch (sym->type) { case S_INT: newval.val = "0"; break; case S_HEX: newval.val = "0x0"; break; case S_STRING: newval.val = ""; break; case S_BOOLEAN: case S_TRISTATE: newval.val = "n"; break; default: sym->curr.val = sym->name; sym->curr.tri = no; return; } sym->flags &= ~SYMBOL_WRITE; sym_calc_visibility(sym); if (sym->visible != no) sym->flags |= SYMBOL_WRITE; /* set default if recursively called */ sym->curr = newval; switch (sym_get_type(sym)) { case S_BOOLEAN: case S_TRISTATE: choice_menu = sym_get_choice_menu(sym); if (choice_menu) { sym_calc_choice(choice_menu); newval.tri = sym->curr.tri; } else { if (sym->visible != no) { /* if the symbol is visible use the user value * if available, otherwise try the default value */ if (sym_has_value(sym)) { newval.tri = EXPR_AND(sym->def[S_DEF_USER].tri, sym->visible); goto calc_newval; } } if (sym->rev_dep.tri != no) sym->flags |= SYMBOL_WRITE; if (!sym_is_choice(sym)) { prop = sym_get_default_prop(sym); if (prop) { newval.tri = EXPR_AND(expr_calc_value(prop->expr), prop->visible.tri); if (newval.tri != no) sym->flags |= SYMBOL_WRITE; } if (sym->implied.tri != no) { sym->flags |= SYMBOL_WRITE; newval.tri = EXPR_OR(newval.tri, sym->implied.tri); newval.tri = EXPR_AND(newval.tri, sym->dir_dep.tri); } } calc_newval: if (sym->dir_dep.tri < sym->rev_dep.tri) sym_warn_unmet_dep(sym); newval.tri = EXPR_OR(newval.tri, sym->rev_dep.tri); } if (newval.tri == mod && sym_get_type(sym) == S_BOOLEAN) newval.tri = yes; break; case S_STRING: case S_HEX: case S_INT: if (sym->visible != no && sym_has_value(sym)) { newval.val = sym->def[S_DEF_USER].val; break; } prop = sym_get_default_prop(sym); if (prop) { struct symbol *ds = prop_get_symbol(prop); if (ds) { sym->flags |= SYMBOL_WRITE; sym_calc_value(ds); newval.val = ds->curr.val; } } break; default: ; } sym->curr = newval; sym_validate_range(sym); if (memcmp(&oldval, &sym->curr, sizeof(oldval))) { sym_set_changed(sym); if (modules_sym == sym) { sym_set_all_changed(); modules_val = modules_sym->curr.tri; } } if (sym_is_choice(sym)) sym->flags &= ~SYMBOL_WRITE; } void sym_clear_all_valid(void) { struct symbol *sym; for_all_symbols(sym) sym->flags &= ~SYMBOL_VALID; expr_invalidate_all(); conf_set_changed(true); sym_calc_value(modules_sym); } bool sym_tristate_within_range(const struct symbol *sym, tristate val) { int type = sym_get_type(sym); if (sym->visible == no) return false; if (type != S_BOOLEAN && type != S_TRISTATE) return false; if (type == S_BOOLEAN && val == mod) return false; if (sym->visible <= sym->rev_dep.tri) return false; return val >= sym->rev_dep.tri && val <= sym->visible; } bool sym_set_tristate_value(struct symbol *sym, tristate val) { tristate oldval = sym_get_tristate_value(sym); if (!sym_tristate_within_range(sym, val)) return false; if (!(sym->flags & SYMBOL_DEF_USER) || sym->def[S_DEF_USER].tri != val) { sym->def[S_DEF_USER].tri = val; sym->flags |= SYMBOL_DEF_USER; sym_set_changed(sym); } if (oldval != val) sym_clear_all_valid(); return true; } /** * choice_set_value - set the user input to a choice * * @choice: menu entry for the choice * @sym: selected symbol */ void choice_set_value(struct menu *choice, struct symbol *sym) { struct menu *menu; bool changed = false; menu_for_each_sub_entry(menu, choice) { tristate val; if (!menu->sym) continue; if (menu->sym->visible == no) continue; val = menu->sym == sym ? yes : no; if (menu->sym->curr.tri != val) changed = true; menu->sym->def[S_DEF_USER].tri = val; menu->sym->flags |= SYMBOL_DEF_USER; /* * Now, the user has explicitly enabled or disabled this symbol, * it should be given the highest priority. We are possibly * setting multiple symbols to 'n', where the first symbol is * given the least prioritized 'n'. This works well when the * choice block ends up with selecting 'n' symbol. * (see sym_calc_choice()) */ list_move(&menu->sym->choice_link, &choice->choice_members); } if (changed) sym_clear_all_valid(); } tristate sym_toggle_tristate_value(struct symbol *sym) { struct menu *choice; tristate oldval, newval; choice = sym_get_choice_menu(sym); if (choice) { choice_set_value(choice, sym); return yes; } oldval = newval = sym_get_tristate_value(sym); do { switch (newval) { case no: newval = mod; break; case mod: newval = yes; break; case yes: newval = no; break; } if (sym_set_tristate_value(sym, newval)) break; } while (oldval != newval); return newval; } bool sym_string_valid(struct symbol *sym, const char *str) { signed char ch; switch (sym->type) { case S_STRING: return true; case S_INT: ch = *str++; if (ch == '-') ch = *str++; if (!isdigit(ch)) return false; if (ch == '0' && *str != 0) return false; while ((ch = *str++)) { if (!isdigit(ch)) return false; } return true; case S_HEX: if (str[0] == '0' && (str[1] == 'x' || str[1] == 'X')) str += 2; ch = *str++; do { if (!isxdigit(ch)) return false; } while ((ch = *str++)); return true; case S_BOOLEAN: case S_TRISTATE: switch (str[0]) { case 'y': case 'Y': case 'm': case 'M': case 'n': case 'N': return true; } return false; default: return false; } } bool sym_string_within_range(struct symbol *sym, const char *str) { struct property *prop; long long val; switch (sym->type) { case S_STRING: return sym_string_valid(sym, str); case S_INT: if (!sym_string_valid(sym, str)) return false; prop = sym_get_range_prop(sym); if (!prop) return true; val = strtoll(str, NULL, 10); return val >= sym_get_range_val(prop->expr->left.sym, 10) && val <= sym_get_range_val(prop->expr->right.sym, 10); case S_HEX: if (!sym_string_valid(sym, str)) return false; prop = sym_get_range_prop(sym); if (!prop) return true; val = strtoll(str, NULL, 16); return val >= sym_get_range_val(prop->expr->left.sym, 16) && val <= sym_get_range_val(prop->expr->right.sym, 16); case S_BOOLEAN: case S_TRISTATE: switch (str[0]) { case 'y': case 'Y': return sym_tristate_within_range(sym, yes); case 'm': case 'M': return sym_tristate_within_range(sym, mod); case 'n': case 'N': return sym_tristate_within_range(sym, no); } return false; default: return false; } } bool sym_set_string_value(struct symbol *sym, const char *newval) { const char *oldval; char *val; int size; switch (sym->type) { case S_BOOLEAN: case S_TRISTATE: switch (newval[0]) { case 'y': case 'Y': return sym_set_tristate_value(sym, yes); case 'm': case 'M': return sym_set_tristate_value(sym, mod); case 'n': case 'N': return sym_set_tristate_value(sym, no); } return false; default: ; } if (!sym_string_within_range(sym, newval)) return false; if (!(sym->flags & SYMBOL_DEF_USER)) { sym->flags |= SYMBOL_DEF_USER; sym_set_changed(sym); } oldval = sym->def[S_DEF_USER].val; size = strlen(newval) + 1; if (sym->type == S_HEX && (newval[0] != '0' || (newval[1] != 'x' && newval[1] != 'X'))) { size += 2; sym->def[S_DEF_USER].val = val = xmalloc(size); *val++ = '0'; *val++ = 'x'; } else if (!oldval || strcmp(oldval, newval)) sym->def[S_DEF_USER].val = val = xmalloc(size); else return true; strcpy(val, newval); free((void *)oldval); sym_clear_all_valid(); return true; } /* * Find the default value associated to a symbol. * For tristate symbol handle the modules=n case * in which case "m" becomes "y". * If the symbol does not have any default then fallback * to the fixed default values. */ const char *sym_get_string_default(struct symbol *sym) { struct property *prop; struct symbol *ds; const char *str = ""; tristate val; sym_calc_visibility(sym); sym_calc_value(modules_sym); val = symbol_no.curr.tri; /* If symbol has a default value look it up */ prop = sym_get_default_prop(sym); if (prop != NULL) { switch (sym->type) { case S_BOOLEAN: case S_TRISTATE: /* The visibility may limit the value from yes => mod */ val = EXPR_AND(expr_calc_value(prop->expr), prop->visible.tri); break; default: /* * The following fails to handle the situation * where a default value is further limited by * the valid range. */ ds = prop_get_symbol(prop); if (ds != NULL) { sym_calc_value(ds); str = (const char *)ds->curr.val; } } } /* Handle select statements */ val = EXPR_OR(val, sym->rev_dep.tri); /* transpose mod to yes if modules are not enabled */ if (val == mod) if (!sym_is_choice_value(sym) && modules_sym->curr.tri == no) val = yes; /* transpose mod to yes if type is bool */ if (sym->type == S_BOOLEAN && val == mod) val = yes; /* adjust the default value if this symbol is implied by another */ if (val < sym->implied.tri) val = sym->implied.tri; switch (sym->type) { case S_BOOLEAN: case S_TRISTATE: switch (val) { case no: return "n"; case mod: return "m"; case yes: return "y"; } case S_INT: if (!str[0]) str = "0"; break; case S_HEX: if (!str[0]) str = "0x0"; break; default: break; } return str; } const char *sym_get_string_value(struct symbol *sym) { tristate val; switch (sym->type) { case S_BOOLEAN: case S_TRISTATE: val = sym_get_tristate_value(sym); switch (val) { case no: return "n"; case mod: return "m"; case yes: return "y"; } break; default: ; } return (const char *)sym->curr.val; } bool sym_is_changeable(const struct symbol *sym) { return !sym_is_choice(sym) && sym->visible > sym->rev_dep.tri; } bool sym_is_choice_value(const struct symbol *sym) { return !list_empty(&sym->choice_link); } HASHTABLE_DEFINE(sym_hashtable, SYMBOL_HASHSIZE); struct symbol *sym_lookup(const char *name, int flags) { struct symbol *symbol; char *new_name; int hash; if (name) { if (name[0] && !name[1]) { switch (name[0]) { case 'y': return &symbol_yes; case 'm': return &symbol_mod; case 'n': return &symbol_no; } } hash = hash_str(name); hash_for_each_possible(sym_hashtable, symbol, node, hash) { if (symbol->name && !strcmp(symbol->name, name) && (flags ? symbol->flags & flags : !(symbol->flags & SYMBOL_CONST))) return symbol; } new_name = xstrdup(name); } else { new_name = NULL; hash = 0; } symbol = xmalloc(sizeof(*symbol)); memset(symbol, 0, sizeof(*symbol)); symbol->name = new_name; symbol->type = S_UNKNOWN; symbol->flags = flags; INIT_LIST_HEAD(&symbol->menus); INIT_LIST_HEAD(&symbol->choice_link); hash_add(sym_hashtable, &symbol->node, hash); return symbol; } struct symbol *sym_find(const char *name) { struct symbol *symbol = NULL; int hash = 0; if (!name) return NULL; if (name[0] && !name[1]) { switch (name[0]) { case 'y': return &symbol_yes; case 'm': return &symbol_mod; case 'n': return &symbol_no; } } hash = hash_str(name); hash_for_each_possible(sym_hashtable, symbol, node, hash) { if (symbol->name && !strcmp(symbol->name, name) && !(symbol->flags & SYMBOL_CONST)) break; } return symbol; } struct sym_match { struct symbol *sym; off_t so, eo; }; /* Compare matched symbols as thus: * - first, symbols that match exactly * - then, alphabetical sort */ static int sym_rel_comp(const void *sym1, const void *sym2) { const struct sym_match *s1 = sym1; const struct sym_match *s2 = sym2; int exact1, exact2; /* Exact match: * - if matched length on symbol s1 is the length of that symbol, * then this symbol should come first; * - if matched length on symbol s2 is the length of that symbol, * then this symbol should come first. * Note: since the search can be a regexp, both symbols may match * exactly; if this is the case, we can't decide which comes first, * and we fallback to sorting alphabetically. */ exact1 = (s1->eo - s1->so) == strlen(s1->sym->name); exact2 = (s2->eo - s2->so) == strlen(s2->sym->name); if (exact1 && !exact2) return -1; if (!exact1 && exact2) return 1; /* As a fallback, sort symbols alphabetically */ return strcmp(s1->sym->name, s2->sym->name); } struct symbol **sym_re_search(const char *pattern) { struct symbol *sym, **sym_arr = NULL; struct sym_match *sym_match_arr = NULL; int i, cnt, size; regex_t re; regmatch_t match[1]; cnt = size = 0; /* Skip if empty */ if (strlen(pattern) == 0) return NULL; if (regcomp(&re, pattern, REG_EXTENDED|REG_ICASE)) return NULL; for_all_symbols(sym) { if (sym->flags & SYMBOL_CONST || !sym->name) continue; if (regexec(&re, sym->name, 1, match, 0)) continue; if (cnt >= size) { void *tmp; size += 16; tmp = realloc(sym_match_arr, size * sizeof(struct sym_match)); if (!tmp) goto sym_re_search_free; sym_match_arr = tmp; } sym_calc_value(sym); /* As regexec returned 0, we know we have a match, so * we can use match[0].rm_[se]o without further checks */ sym_match_arr[cnt].so = match[0].rm_so; sym_match_arr[cnt].eo = match[0].rm_eo; sym_match_arr[cnt++].sym = sym; } if (sym_match_arr) { qsort(sym_match_arr, cnt, sizeof(struct sym_match), sym_rel_comp); sym_arr = malloc((cnt+1) * sizeof(struct symbol *)); if (!sym_arr) goto sym_re_search_free; for (i = 0; i < cnt; i++) sym_arr[i] = sym_match_arr[i].sym; sym_arr[cnt] = NULL; } sym_re_search_free: /* sym_match_arr can be NULL if no match, but free(NULL) is OK */ free(sym_match_arr); regfree(&re); return sym_arr; } /* * When we check for recursive dependencies we use a stack to save * current state so we can print out relevant info to user. * The entries are located on the call stack so no need to free memory. * Note insert() remove() must always match to properly clear the stack. */ static struct dep_stack { struct dep_stack *prev, *next; struct symbol *sym; struct property *prop; struct expr **expr; } *check_top; static void dep_stack_insert(struct dep_stack *stack, struct symbol *sym) { memset(stack, 0, sizeof(*stack)); if (check_top) check_top->next = stack; stack->prev = check_top; stack->sym = sym; check_top = stack; } static void dep_stack_remove(void) { check_top = check_top->prev; if (check_top) check_top->next = NULL; } /* * Called when we have detected a recursive dependency. * check_top point to the top of the stact so we use * the ->prev pointer to locate the bottom of the stack. */ static void sym_check_print_recursive(struct symbol *last_sym) { struct dep_stack *stack; struct symbol *sym, *next_sym; struct menu *choice; struct dep_stack cv_stack; enum prop_type type; choice = sym_get_choice_menu(last_sym); if (choice) { dep_stack_insert(&cv_stack, last_sym); last_sym = choice->sym; } for (stack = check_top; stack != NULL; stack = stack->prev) if (stack->sym == last_sym) break; if (!stack) { fprintf(stderr, "unexpected recursive dependency error\n"); return; } for (; stack; stack = stack->next) { sym = stack->sym; next_sym = stack->next ? stack->next->sym : last_sym; type = stack->prop ? stack->prop->type : P_UNKNOWN; if (stack->sym == last_sym) fprintf(stderr, "error: recursive dependency detected!\n"); if (sym_is_choice(next_sym)) { choice = list_first_entry(&next_sym->menus, struct menu, link); fprintf(stderr, "\tsymbol %s is part of choice block at %s:%d\n", sym->name ? sym->name : "", choice->filename, choice->lineno); } else if (stack->expr == &sym->dir_dep.expr) { fprintf(stderr, "\tsymbol %s depends on %s\n", sym->name ? sym->name : "", next_sym->name); } else if (stack->expr == &sym->rev_dep.expr) { fprintf(stderr, "\tsymbol %s is selected by %s\n", sym->name, next_sym->name); } else if (stack->expr == &sym->implied.expr) { fprintf(stderr, "\tsymbol %s is implied by %s\n", sym->name, next_sym->name); } else if (stack->expr) { fprintf(stderr, "\tsymbol %s %s value contains %s\n", sym->name ? sym->name : "", prop_get_type_name(type), next_sym->name); } else { fprintf(stderr, "\tsymbol %s %s is visible depending on %s\n", sym->name ? sym->name : "", prop_get_type_name(type), next_sym->name); } } fprintf(stderr, "For a resolution refer to Documentation/kbuild/kconfig-language.rst\n" "subsection \"Kconfig recursive dependency limitations\"\n" "\n"); if (check_top == &cv_stack) dep_stack_remove(); } static struct symbol *sym_check_expr_deps(const struct expr *e) { struct symbol *sym; if (!e) return NULL; switch (e->type) { case E_OR: case E_AND: sym = sym_check_expr_deps(e->left.expr); if (sym) return sym; return sym_check_expr_deps(e->right.expr); case E_NOT: return sym_check_expr_deps(e->left.expr); case E_EQUAL: case E_GEQ: case E_GTH: case E_LEQ: case E_LTH: case E_UNEQUAL: sym = sym_check_deps(e->left.sym); if (sym) return sym; return sym_check_deps(e->right.sym); case E_SYMBOL: return sym_check_deps(e->left.sym); default: break; } fprintf(stderr, "Oops! How to check %d?\n", e->type); return NULL; } /* return NULL when dependencies are OK */ static struct symbol *sym_check_sym_deps(struct symbol *sym) { struct symbol *sym2; struct property *prop; struct dep_stack stack; dep_stack_insert(&stack, sym); stack.expr = &sym->dir_dep.expr; sym2 = sym_check_expr_deps(sym->dir_dep.expr); if (sym2) goto out; stack.expr = &sym->rev_dep.expr; sym2 = sym_check_expr_deps(sym->rev_dep.expr); if (sym2) goto out; stack.expr = &sym->implied.expr; sym2 = sym_check_expr_deps(sym->implied.expr); if (sym2) goto out; stack.expr = NULL; for (prop = sym->prop; prop; prop = prop->next) { if (prop->type == P_SELECT || prop->type == P_IMPLY) continue; stack.prop = prop; sym2 = sym_check_expr_deps(prop->visible.expr); if (sym2) break; if (prop->type != P_DEFAULT || sym_is_choice(sym)) continue; stack.expr = &prop->expr; sym2 = sym_check_expr_deps(prop->expr); if (sym2) break; stack.expr = NULL; } out: dep_stack_remove(); return sym2; } static struct symbol *sym_check_choice_deps(struct symbol *choice) { struct menu *choice_menu, *menu; struct symbol *sym2; struct dep_stack stack; dep_stack_insert(&stack, choice); choice_menu = list_first_entry(&choice->menus, struct menu, link); menu_for_each_sub_entry(menu, choice_menu) { if (menu->sym) menu->sym->flags |= SYMBOL_CHECK | SYMBOL_CHECKED; } choice->flags |= (SYMBOL_CHECK | SYMBOL_CHECKED); sym2 = sym_check_sym_deps(choice); choice->flags &= ~SYMBOL_CHECK; if (sym2) goto out; menu_for_each_sub_entry(menu, choice_menu) { if (!menu->sym) continue; sym2 = sym_check_sym_deps(menu->sym); if (sym2) break; } out: menu_for_each_sub_entry(menu, choice_menu) if (menu->sym) menu->sym->flags &= ~SYMBOL_CHECK; if (sym2) { struct menu *choice_menu2; choice_menu2 = sym_get_choice_menu(sym2); if (choice_menu2 == choice_menu) sym2 = choice; } dep_stack_remove(); return sym2; } struct symbol *sym_check_deps(struct symbol *sym) { struct menu *choice; struct symbol *sym2; if (sym->flags & SYMBOL_CHECK) { sym_check_print_recursive(sym); return sym; } if (sym->flags & SYMBOL_CHECKED) return NULL; choice = sym_get_choice_menu(sym); if (choice) { struct dep_stack stack; /* for choice groups start the check with main choice symbol */ dep_stack_insert(&stack, sym); sym2 = sym_check_deps(choice->sym); dep_stack_remove(); } else if (sym_is_choice(sym)) { sym2 = sym_check_choice_deps(sym); } else { sym->flags |= (SYMBOL_CHECK | SYMBOL_CHECKED); sym2 = sym_check_sym_deps(sym); sym->flags &= ~SYMBOL_CHECK; } return sym2; } struct symbol *prop_get_symbol(const struct property *prop) { if (prop->expr && prop->expr->type == E_SYMBOL) return prop->expr->left.sym; return NULL; } const char *prop_get_type_name(enum prop_type type) { switch (type) { case P_PROMPT: return "prompt"; case P_COMMENT: return "comment"; case P_MENU: return "menu"; case P_DEFAULT: return "default"; case P_SELECT: return "select"; case P_IMPLY: return "imply"; case P_RANGE: return "range"; case P_UNKNOWN: break; } return "unknown"; }