yann@1: /* yann@1: * Copyright (C) 2002 Roman Zippel yann@1: * Released under the terms of the GNU GPL v2.0. yann@1: */ yann@1: yann@1: #include yann@1: #include yann@1: yann@1: #define LKC_DIRECT_LINK yann@1: #include "lkc.h" yann@1: yann@2448: static const char nohelp_text[] = N_( yann@2448: "There is no help available for this option.\n"); yann@2448: yann@1: struct menu rootmenu; yann@1: static struct menu **last_entry_ptr; yann@1: yann@1: struct file *file_list; yann@1: struct file *current_file; yann@1: yann@943: void menu_warn(struct menu *menu, const char *fmt, ...) yann@1: { yann@1: va_list ap; yann@1: va_start(ap, fmt); yann@1: fprintf(stderr, "%s:%d:warning: ", menu->file->name, menu->lineno); yann@1: vfprintf(stderr, fmt, ap); yann@1: fprintf(stderr, "\n"); yann@1: va_end(ap); yann@1: } yann@1: yann@1: static void prop_warn(struct property *prop, const char *fmt, ...) yann@1: { yann@1: va_list ap; yann@1: va_start(ap, fmt); yann@1: fprintf(stderr, "%s:%d:warning: ", prop->file->name, prop->lineno); yann@1: vfprintf(stderr, fmt, ap); yann@1: fprintf(stderr, "\n"); yann@1: va_end(ap); yann@1: } yann@1: yann@2448: void _menu_init(void) yann@1: { yann@1: current_entry = current_menu = &rootmenu; yann@1: last_entry_ptr = &rootmenu.list; yann@1: } yann@1: yann@1: void menu_add_entry(struct symbol *sym) yann@1: { yann@1: struct menu *menu; yann@1: yann@1: menu = malloc(sizeof(*menu)); yann@1: memset(menu, 0, sizeof(*menu)); yann@1: menu->sym = sym; yann@1: menu->parent = current_menu; yann@1: menu->file = current_file; yann@1: menu->lineno = zconf_lineno(); yann@1: yann@1: *last_entry_ptr = menu; yann@1: last_entry_ptr = &menu->next; yann@1: current_entry = menu; yann@2448: if (sym) yann@2448: menu_add_symbol(P_SYMBOL, sym, NULL); yann@1: } yann@1: yann@1: void menu_end_entry(void) yann@1: { yann@1: } yann@1: yann@1: struct menu *menu_add_menu(void) yann@1: { yann@1: menu_end_entry(); yann@1: last_entry_ptr = ¤t_entry->list; yann@1: return current_menu = current_entry; yann@1: } yann@1: yann@1: void menu_end_menu(void) yann@1: { yann@1: last_entry_ptr = ¤t_menu->next; yann@1: current_menu = current_menu->parent; yann@1: } yann@1: yann@2448: static struct expr *menu_check_dep(struct expr *e) yann@1: { yann@1: if (!e) yann@1: return e; yann@1: yann@1: switch (e->type) { yann@1: case E_NOT: yann@1: e->left.expr = menu_check_dep(e->left.expr); yann@1: break; yann@1: case E_OR: yann@1: case E_AND: yann@1: e->left.expr = menu_check_dep(e->left.expr); yann@1: e->right.expr = menu_check_dep(e->right.expr); yann@1: break; yann@1: case E_SYMBOL: yann@1: /* change 'm' into 'm' && MODULES */ yann@1: if (e->left.sym == &symbol_mod) yann@1: return expr_alloc_and(e, expr_alloc_symbol(modules_sym)); yann@1: break; yann@1: default: yann@1: break; yann@1: } yann@1: return e; yann@1: } yann@1: yann@1: void menu_add_dep(struct expr *dep) yann@1: { yann@1: current_entry->dep = expr_alloc_and(current_entry->dep, menu_check_dep(dep)); yann@1: } yann@1: yann@1: void menu_set_type(int type) yann@1: { yann@1: struct symbol *sym = current_entry->sym; yann@1: yann@1: if (sym->type == type) yann@1: return; yann@1: if (sym->type == S_UNKNOWN) { yann@1: sym->type = type; yann@1: return; yann@1: } yann@1: menu_warn(current_entry, "type of '%s' redefined from '%s' to '%s'", yann@1: sym->name ? sym->name : "", yann@1: sym_type_name(sym->type), sym_type_name(type)); yann@1: } yann@1: yann@1: struct property *menu_add_prop(enum prop_type type, char *prompt, struct expr *expr, struct expr *dep) yann@1: { yann@1: struct property *prop = prop_alloc(type, current_entry->sym); yann@1: yann@1: prop->menu = current_entry; yann@1: prop->expr = expr; yann@1: prop->visible.expr = menu_check_dep(dep); yann@1: yann@1: if (prompt) { yann@2450: /* For crostool-NG, a leading pipe followed with spaces yann@2450: * means that pipe shall be removed, and the spaces should yann@2450: * not be trimmed. yann@2450: */ yann@2450: if (*prompt == '|') yann@2450: prompt++; yann@2450: else if (isspace(*prompt)) { yann@2448: prop_warn(prop, "leading whitespace ignored"); yann@1: while (isspace(*prompt)) yann@1: prompt++; yann@1: } yann@2448: if (current_entry->prompt && current_entry != &rootmenu) yann@1: prop_warn(prop, "prompt redefined"); yann@2448: yann@2448: /* Apply all upper menus' visibilities to actual prompts. */ yann@2448: if(type == P_PROMPT) { yann@2448: struct menu *menu = current_entry; yann@2448: yann@2448: while ((menu = menu->parent) != NULL) { yann@2448: if (!menu->visibility) yann@2448: continue; yann@2448: prop->visible.expr yann@2448: = expr_alloc_and(prop->visible.expr, yann@2448: menu->visibility); yann@2448: } yann@2448: } yann@2448: yann@1: current_entry->prompt = prop; yann@1: } yann@1: prop->text = prompt; yann@1: yann@1: return prop; yann@1: } yann@1: yann@1: struct property *menu_add_prompt(enum prop_type type, char *prompt, struct expr *dep) yann@1: { yann@1: return menu_add_prop(type, prompt, NULL, dep); yann@1: } yann@1: yann@2448: void menu_add_visibility(struct expr *expr) yann@2448: { yann@2448: current_entry->visibility = expr_alloc_and(current_entry->visibility, yann@2448: expr); yann@2448: } yann@2448: yann@1: void menu_add_expr(enum prop_type type, struct expr *expr, struct expr *dep) yann@1: { yann@1: menu_add_prop(type, NULL, expr, dep); yann@1: } yann@1: yann@1: void menu_add_symbol(enum prop_type type, struct symbol *sym, struct expr *dep) yann@1: { yann@1: menu_add_prop(type, NULL, expr_alloc_symbol(sym), dep); yann@1: } yann@1: yann@1: void menu_add_option(int token, char *arg) yann@1: { yann@1: struct property *prop; yann@1: yann@1: switch (token) { yann@1: case T_OPT_MODULES: yann@1: prop = prop_alloc(P_DEFAULT, modules_sym); yann@1: prop->expr = expr_alloc_symbol(current_entry->sym); yann@1: break; yann@1: case T_OPT_DEFCONFIG_LIST: yann@1: if (!sym_defconfig_list) yann@1: sym_defconfig_list = current_entry->sym; yann@1: else if (sym_defconfig_list != current_entry->sym) yann@1: zconf_error("trying to redefine defconfig symbol"); yann@1: break; yann@943: case T_OPT_ENV: yann@943: prop_add_env(arg); yann@943: break; yann@1: } yann@1: } yann@1: yann@2448: static int menu_validate_number(struct symbol *sym, struct symbol *sym2) yann@1: { yann@1: return sym2->type == S_INT || sym2->type == S_HEX || yann@1: (sym2->type == S_UNKNOWN && sym_string_valid(sym, sym2->name)); yann@1: } yann@1: yann@2448: static void sym_check_prop(struct symbol *sym) yann@1: { yann@1: struct property *prop; yann@1: struct symbol *sym2; yann@1: for (prop = sym->prop; prop; prop = prop->next) { yann@1: switch (prop->type) { yann@1: case P_DEFAULT: yann@1: if ((sym->type == S_STRING || sym->type == S_INT || sym->type == S_HEX) && yann@1: prop->expr->type != E_SYMBOL) yann@1: prop_warn(prop, yann@2448: "default for config symbol '%s'" yann@1: " must be a single symbol", sym->name); yann@2448: if (prop->expr->type != E_SYMBOL) yann@2448: break; yann@2448: sym2 = prop_get_symbol(prop); yann@2448: if (sym->type == S_HEX || sym->type == S_INT) { yann@2448: if (!menu_validate_number(sym, sym2)) yann@2448: prop_warn(prop, yann@2448: "'%s': number is invalid", yann@2448: sym->name); yann@2448: } yann@1: break; yann@1: case P_SELECT: yann@1: sym2 = prop_get_symbol(prop); yann@1: if (sym->type != S_BOOLEAN && sym->type != S_TRISTATE) yann@1: prop_warn(prop, yann@1: "config symbol '%s' uses select, but is " yann@1: "not boolean or tristate", sym->name); yann@943: else if (sym2->type != S_UNKNOWN && yann@943: sym2->type != S_BOOLEAN && yann@943: sym2->type != S_TRISTATE) yann@1: prop_warn(prop, yann@1: "'%s' has wrong type. 'select' only " yann@1: "accept arguments of boolean and " yann@1: "tristate type", sym2->name); yann@1: break; yann@1: case P_RANGE: yann@1: if (sym->type != S_INT && sym->type != S_HEX) yann@1: prop_warn(prop, "range is only allowed " yann@1: "for int or hex symbols"); yann@2448: if (!menu_validate_number(sym, prop->expr->left.sym) || yann@2448: !menu_validate_number(sym, prop->expr->right.sym)) yann@1: prop_warn(prop, "range is invalid"); yann@1: break; yann@1: default: yann@1: ; yann@1: } yann@1: } yann@1: } yann@1: yann@1: void menu_finalize(struct menu *parent) yann@1: { yann@1: struct menu *menu, *last_menu; yann@1: struct symbol *sym; yann@1: struct property *prop; yann@1: struct expr *parentdep, *basedep, *dep, *dep2, **ep; yann@1: yann@1: sym = parent->sym; yann@1: if (parent->list) { yann@1: if (sym && sym_is_choice(sym)) { yann@943: if (sym->type == S_UNKNOWN) { yann@943: /* find the first choice value to find out choice type */ yann@943: current_entry = parent; yann@943: for (menu = parent->list; menu; menu = menu->next) { yann@943: if (menu->sym && menu->sym->type != S_UNKNOWN) { yann@943: menu_set_type(menu->sym->type); yann@943: break; yann@943: } yann@943: } yann@943: } yann@2451: if (parent->prompt && yann@2451: !expr_is_yes(parent->prompt->visible.expr)) { yann@2451: parent->visibility = expr_alloc_and (parent->visibility, yann@2451: parent->prompt->visible.expr); yann@2451: } yann@943: /* set the type of the remaining choice values */ yann@1: for (menu = parent->list; menu; menu = menu->next) { yann@943: current_entry = menu; yann@943: if (menu->sym && menu->sym->type == S_UNKNOWN) yann@1: menu_set_type(sym->type); yann@1: } yann@1: parentdep = expr_alloc_symbol(sym); yann@1: } else if (parent->prompt) yann@1: parentdep = parent->prompt->visible.expr; yann@1: else yann@1: parentdep = parent->dep; yann@1: yann@1: for (menu = parent->list; menu; menu = menu->next) { yann@1: basedep = expr_transform(menu->dep); yann@1: basedep = expr_alloc_and(expr_copy(parentdep), basedep); yann@1: basedep = expr_eliminate_dups(basedep); yann@1: menu->dep = basedep; yann@1: if (menu->sym) yann@1: prop = menu->sym->prop; yann@1: else yann@1: prop = menu->prompt; yann@1: for (; prop; prop = prop->next) { yann@1: if (prop->menu != menu) yann@1: continue; yann@1: dep = expr_transform(prop->visible.expr); yann@1: dep = expr_alloc_and(expr_copy(basedep), dep); yann@1: dep = expr_eliminate_dups(dep); yann@1: if (menu->sym && menu->sym->type != S_TRISTATE) yann@1: dep = expr_trans_bool(dep); yann@1: prop->visible.expr = dep; yann@1: if (prop->type == P_SELECT) { yann@1: struct symbol *es = prop_get_symbol(prop); yann@1: es->rev_dep.expr = expr_alloc_or(es->rev_dep.expr, yann@1: expr_alloc_and(expr_alloc_symbol(menu->sym), expr_copy(dep))); yann@1: } yann@1: } yann@1: } yann@1: for (menu = parent->list; menu; menu = menu->next) yann@1: menu_finalize(menu); yann@1: } else if (sym) { yann@1: basedep = parent->prompt ? parent->prompt->visible.expr : NULL; yann@1: basedep = expr_trans_compare(basedep, E_UNEQUAL, &symbol_no); yann@1: basedep = expr_eliminate_dups(expr_transform(basedep)); yann@1: last_menu = NULL; yann@1: for (menu = parent->next; menu; menu = menu->next) { yann@1: dep = menu->prompt ? menu->prompt->visible.expr : menu->dep; yann@1: if (!expr_contains_symbol(dep, sym)) yann@1: break; yann@1: if (expr_depends_symbol(dep, sym)) yann@1: goto next; yann@1: dep = expr_trans_compare(dep, E_UNEQUAL, &symbol_no); yann@1: dep = expr_eliminate_dups(expr_transform(dep)); yann@1: dep2 = expr_copy(basedep); yann@1: expr_eliminate_eq(&dep, &dep2); yann@1: expr_free(dep); yann@1: if (!expr_is_yes(dep2)) { yann@1: expr_free(dep2); yann@1: break; yann@1: } yann@1: expr_free(dep2); yann@1: next: yann@1: menu_finalize(menu); yann@1: menu->parent = parent; yann@1: last_menu = menu; yann@1: } yann@1: if (last_menu) { yann@1: parent->list = parent->next; yann@1: parent->next = last_menu->next; yann@1: last_menu->next = NULL; yann@1: } yann@2448: yann@2474: sym->dir_dep.expr = expr_alloc_or(sym->dir_dep.expr, parent->dep); yann@1: } yann@1: for (menu = parent->list; menu; menu = menu->next) { yann@943: if (sym && sym_is_choice(sym) && yann@943: menu->sym && !sym_is_choice_value(menu->sym)) { yann@943: current_entry = menu; yann@1: menu->sym->flags |= SYMBOL_CHOICEVAL; yann@1: if (!menu->prompt) yann@1: menu_warn(menu, "choice value must have a prompt"); yann@1: for (prop = menu->sym->prop; prop; prop = prop->next) { yann@1: if (prop->type == P_DEFAULT) yann@1: prop_warn(prop, "defaults for choice " yann@943: "values not supported"); yann@943: if (prop->menu == menu) yann@943: continue; yann@943: if (prop->type == P_PROMPT && yann@943: prop->menu->parent->sym != sym) yann@943: prop_warn(prop, "choice value used outside its choice group"); yann@1: } yann@943: /* Non-tristate choice values of tristate choices must yann@943: * depend on the choice being set to Y. The choice yann@943: * values' dependencies were propagated to their yann@943: * properties above, so the change here must be re- yann@943: * propagated. yann@943: */ yann@943: if (sym->type == S_TRISTATE && menu->sym->type != S_TRISTATE) { yann@943: basedep = expr_alloc_comp(E_EQUAL, sym, &symbol_yes); yann@943: menu->dep = expr_alloc_and(basedep, menu->dep); yann@943: for (prop = menu->sym->prop; prop; prop = prop->next) { yann@943: if (prop->menu != menu) yann@943: continue; yann@943: prop->visible.expr = expr_alloc_and(expr_copy(basedep), yann@943: prop->visible.expr); yann@943: } yann@943: } yann@1: menu_add_symbol(P_CHOICE, sym, NULL); yann@1: prop = sym_get_choice_prop(sym); yann@1: for (ep = &prop->expr; *ep; ep = &(*ep)->left.expr) yann@1: ; yann@943: *ep = expr_alloc_one(E_LIST, NULL); yann@1: (*ep)->right.sym = menu->sym; yann@1: } yann@1: if (menu->list && (!menu->prompt || !menu->prompt->text)) { yann@1: for (last_menu = menu->list; ; last_menu = last_menu->next) { yann@1: last_menu->parent = parent; yann@1: if (!last_menu->next) yann@1: break; yann@1: } yann@1: last_menu->next = menu->next; yann@1: menu->next = menu->list; yann@1: menu->list = NULL; yann@1: } yann@1: } yann@1: yann@1: if (sym && !(sym->flags & SYMBOL_WARNED)) { yann@1: if (sym->type == S_UNKNOWN) yann@1: menu_warn(parent, "config symbol defined without type"); yann@1: yann@1: if (sym_is_choice(sym) && !parent->prompt) yann@1: menu_warn(parent, "choice must have a prompt"); yann@1: yann@1: /* Check properties connected to this symbol */ yann@1: sym_check_prop(sym); yann@1: sym->flags |= SYMBOL_WARNED; yann@1: } yann@1: yann@1: if (sym && !sym_is_optional(sym) && parent->prompt) { yann@1: sym->rev_dep.expr = expr_alloc_or(sym->rev_dep.expr, yann@1: expr_alloc_and(parent->prompt->visible.expr, yann@1: expr_alloc_symbol(&symbol_mod))); yann@1: } yann@1: } yann@1: yann@2448: bool menu_has_prompt(struct menu *menu) yann@2448: { yann@2448: if (!menu->prompt) yann@2448: return false; yann@2448: return true; yann@2448: } yann@2448: yann@1: bool menu_is_visible(struct menu *menu) yann@1: { yann@1: struct menu *child; yann@1: struct symbol *sym; yann@1: tristate visible; yann@1: yann@1: if (!menu->prompt) yann@1: return false; yann@2448: yann@2448: if (menu->visibility) { yann@2448: if (expr_calc_value(menu->visibility) == no) yann@2448: return no; yann@2448: } yann@2448: yann@1: sym = menu->sym; yann@1: if (sym) { yann@1: sym_calc_value(sym); yann@1: visible = menu->prompt->visible.tri; yann@1: } else yann@1: visible = menu->prompt->visible.tri = expr_calc_value(menu->prompt->visible.expr); yann@1: yann@1: if (visible != no) yann@1: return true; yann@2448: yann@1: if (!sym || sym_get_tristate_value(menu->sym) == no) yann@1: return false; yann@1: yann@2448: for (child = menu->list; child; child = child->next) { yann@2448: if (menu_is_visible(child)) { yann@2448: if (sym) yann@2448: sym->flags |= SYMBOL_DEF_USER; yann@1: return true; yann@2448: } yann@2448: } yann@2448: yann@1: return false; yann@1: } yann@1: yann@1: const char *menu_get_prompt(struct menu *menu) yann@1: { yann@1: if (menu->prompt) yann@943: return menu->prompt->text; yann@1: else if (menu->sym) yann@943: return menu->sym->name; yann@1: return NULL; yann@1: } yann@1: yann@1: struct menu *menu_get_root_menu(struct menu *menu) yann@1: { yann@1: return &rootmenu; yann@1: } yann@1: yann@1: struct menu *menu_get_parent_menu(struct menu *menu) yann@1: { yann@1: enum prop_type type; yann@1: yann@1: for (; menu != &rootmenu; menu = menu->parent) { yann@1: type = menu->prompt ? menu->prompt->type : 0; yann@1: if (type == P_MENU) yann@1: break; yann@1: } yann@1: return menu; yann@1: } yann@1: yann@943: bool menu_has_help(struct menu *menu) yann@943: { yann@943: return menu->help != NULL; yann@943: } yann@943: yann@943: const char *menu_get_help(struct menu *menu) yann@943: { yann@943: if (menu->help) yann@943: return menu->help; yann@943: else yann@943: return ""; yann@943: } yann@2448: yann@2448: static void get_prompt_str(struct gstr *r, struct property *prop) yann@2448: { yann@2448: int i, j; yann@2448: struct menu *submenu[8], *menu; yann@2448: yann@2448: str_printf(r, _("Prompt: %s\n"), _(prop->text)); yann@2448: str_printf(r, _(" Defined at %s:%d\n"), prop->menu->file->name, yann@2448: prop->menu->lineno); yann@2448: if (!expr_is_yes(prop->visible.expr)) { yann@2448: str_append(r, _(" Depends on: ")); yann@2448: expr_gstr_print(prop->visible.expr, r); yann@2448: str_append(r, "\n"); yann@2448: } yann@2448: menu = prop->menu->parent; yann@2448: for (i = 0; menu != &rootmenu && i < 8; menu = menu->parent) yann@2448: submenu[i++] = menu; yann@2448: if (i > 0) { yann@2448: str_printf(r, _(" Location:\n")); yann@2448: for (j = 4; --i >= 0; j += 2) { yann@2448: menu = submenu[i]; yann@2448: str_printf(r, "%*c-> %s", j, ' ', _(menu_get_prompt(menu))); yann@2448: if (menu->sym) { yann@2448: str_printf(r, " (%s [=%s])", menu->sym->name ? yann@2448: menu->sym->name : _(""), yann@2448: sym_get_string_value(menu->sym)); yann@2448: } yann@2448: str_append(r, "\n"); yann@2448: } yann@2448: } yann@2448: } yann@2448: yann@2448: void get_symbol_str(struct gstr *r, struct symbol *sym) yann@2448: { yann@2448: bool hit; yann@2448: struct property *prop; yann@2448: yann@2448: if (sym && sym->name) { yann@2448: str_printf(r, "Symbol: %s [=%s]\n", sym->name, yann@2448: sym_get_string_value(sym)); yann@2448: str_printf(r, "Type : %s\n", sym_type_name(sym->type)); yann@2448: if (sym->type == S_INT || sym->type == S_HEX) { yann@2448: prop = sym_get_range_prop(sym); yann@2448: if (prop) { yann@2448: str_printf(r, "Range : "); yann@2448: expr_gstr_print(prop->expr, r); yann@2448: str_append(r, "\n"); yann@2448: } yann@2448: } yann@2448: } yann@2448: for_all_prompts(sym, prop) yann@2448: get_prompt_str(r, prop); yann@2448: hit = false; yann@2448: for_all_properties(sym, prop, P_SELECT) { yann@2448: if (!hit) { yann@2448: str_append(r, " Selects: "); yann@2448: hit = true; yann@2448: } else yann@2448: str_printf(r, " && "); yann@2448: expr_gstr_print(prop->expr, r); yann@2448: } yann@2448: if (hit) yann@2448: str_append(r, "\n"); yann@2448: if (sym->rev_dep.expr) { yann@2448: str_append(r, _(" Selected by: ")); yann@2448: expr_gstr_print(sym->rev_dep.expr, r); yann@2448: str_append(r, "\n"); yann@2448: } yann@2448: str_append(r, "\n\n"); yann@2448: } yann@2448: yann@2448: struct gstr get_relations_str(struct symbol **sym_arr) yann@2448: { yann@2448: struct symbol *sym; yann@2448: struct gstr res = str_new(); yann@2448: int i; yann@2448: yann@2448: for (i = 0; sym_arr && (sym = sym_arr[i]); i++) yann@2448: get_symbol_str(&res, sym); yann@2448: if (!i) yann@2448: str_append(&res, _("No matches found.\n")); yann@2448: return res; yann@2448: } yann@2448: yann@2448: yann@2448: void menu_get_ext_help(struct menu *menu, struct gstr *help) yann@2448: { yann@2448: struct symbol *sym = menu->sym; yann@2448: yann@2448: if (menu_has_help(menu)) { yann@2448: if (sym->name) { yann@2448: str_printf(help, "%s%s:\n\n", CONFIG_, sym->name); yann@2448: str_append(help, _(menu_get_help(menu))); yann@2448: str_append(help, "\n"); yann@2448: } yann@2448: } else { yann@2448: str_append(help, nohelp_text); yann@2448: } yann@2448: if (sym) yann@2448: get_symbol_str(help, sym); yann@2448: }