1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
1.2 +++ b/kconfig/menu.c Thu Aug 16 12:13:45 2007 +0000
1.3 @@ -0,0 +1,419 @@
1.4 +/*
1.5 + * Copyright (C) 2002 Roman Zippel <zippel@linux-m68k.org>
1.6 + * Released under the terms of the GNU GPL v2.0.
1.7 + */
1.8 +
1.9 +#include <stdlib.h>
1.10 +#include <string.h>
1.11 +
1.12 +#define LKC_DIRECT_LINK
1.13 +#include "lkc.h"
1.14 +
1.15 +struct menu rootmenu;
1.16 +static struct menu **last_entry_ptr;
1.17 +
1.18 +struct file *file_list;
1.19 +struct file *current_file;
1.20 +
1.21 +static void menu_warn(struct menu *menu, const char *fmt, ...)
1.22 +{
1.23 + va_list ap;
1.24 + va_start(ap, fmt);
1.25 + fprintf(stderr, "%s:%d:warning: ", menu->file->name, menu->lineno);
1.26 + vfprintf(stderr, fmt, ap);
1.27 + fprintf(stderr, "\n");
1.28 + va_end(ap);
1.29 +}
1.30 +
1.31 +static void prop_warn(struct property *prop, const char *fmt, ...)
1.32 +{
1.33 + va_list ap;
1.34 + va_start(ap, fmt);
1.35 + fprintf(stderr, "%s:%d:warning: ", prop->file->name, prop->lineno);
1.36 + vfprintf(stderr, fmt, ap);
1.37 + fprintf(stderr, "\n");
1.38 + va_end(ap);
1.39 +}
1.40 +
1.41 +void menu_init(void)
1.42 +{
1.43 + current_entry = current_menu = &rootmenu;
1.44 + last_entry_ptr = &rootmenu.list;
1.45 +}
1.46 +
1.47 +void menu_add_entry(struct symbol *sym)
1.48 +{
1.49 + struct menu *menu;
1.50 +
1.51 + menu = malloc(sizeof(*menu));
1.52 + memset(menu, 0, sizeof(*menu));
1.53 + menu->sym = sym;
1.54 + menu->parent = current_menu;
1.55 + menu->file = current_file;
1.56 + menu->lineno = zconf_lineno();
1.57 +
1.58 + *last_entry_ptr = menu;
1.59 + last_entry_ptr = &menu->next;
1.60 + current_entry = menu;
1.61 +}
1.62 +
1.63 +void menu_end_entry(void)
1.64 +{
1.65 +}
1.66 +
1.67 +struct menu *menu_add_menu(void)
1.68 +{
1.69 + menu_end_entry();
1.70 + last_entry_ptr = ¤t_entry->list;
1.71 + return current_menu = current_entry;
1.72 +}
1.73 +
1.74 +void menu_end_menu(void)
1.75 +{
1.76 + last_entry_ptr = ¤t_menu->next;
1.77 + current_menu = current_menu->parent;
1.78 +}
1.79 +
1.80 +struct expr *menu_check_dep(struct expr *e)
1.81 +{
1.82 + if (!e)
1.83 + return e;
1.84 +
1.85 + switch (e->type) {
1.86 + case E_NOT:
1.87 + e->left.expr = menu_check_dep(e->left.expr);
1.88 + break;
1.89 + case E_OR:
1.90 + case E_AND:
1.91 + e->left.expr = menu_check_dep(e->left.expr);
1.92 + e->right.expr = menu_check_dep(e->right.expr);
1.93 + break;
1.94 + case E_SYMBOL:
1.95 + /* change 'm' into 'm' && MODULES */
1.96 + if (e->left.sym == &symbol_mod)
1.97 + return expr_alloc_and(e, expr_alloc_symbol(modules_sym));
1.98 + break;
1.99 + default:
1.100 + break;
1.101 + }
1.102 + return e;
1.103 +}
1.104 +
1.105 +void menu_add_dep(struct expr *dep)
1.106 +{
1.107 + current_entry->dep = expr_alloc_and(current_entry->dep, menu_check_dep(dep));
1.108 +}
1.109 +
1.110 +void menu_set_type(int type)
1.111 +{
1.112 + struct symbol *sym = current_entry->sym;
1.113 +
1.114 + if (sym->type == type)
1.115 + return;
1.116 + if (sym->type == S_UNKNOWN) {
1.117 + sym->type = type;
1.118 + return;
1.119 + }
1.120 + menu_warn(current_entry, "type of '%s' redefined from '%s' to '%s'",
1.121 + sym->name ? sym->name : "<choice>",
1.122 + sym_type_name(sym->type), sym_type_name(type));
1.123 +}
1.124 +
1.125 +struct property *menu_add_prop(enum prop_type type, char *prompt, struct expr *expr, struct expr *dep)
1.126 +{
1.127 + struct property *prop = prop_alloc(type, current_entry->sym);
1.128 +
1.129 + prop->menu = current_entry;
1.130 + prop->expr = expr;
1.131 + prop->visible.expr = menu_check_dep(dep);
1.132 +
1.133 + if (prompt) {
1.134 + if (isspace(*prompt)) {
1.135 + prop_warn(prop, "leading whitespace ignored");
1.136 + while (isspace(*prompt))
1.137 + prompt++;
1.138 + }
1.139 + if (current_entry->prompt)
1.140 + prop_warn(prop, "prompt redefined");
1.141 + current_entry->prompt = prop;
1.142 + }
1.143 + prop->text = prompt;
1.144 +
1.145 + return prop;
1.146 +}
1.147 +
1.148 +struct property *menu_add_prompt(enum prop_type type, char *prompt, struct expr *dep)
1.149 +{
1.150 + return menu_add_prop(type, prompt, NULL, dep);
1.151 +}
1.152 +
1.153 +void menu_add_expr(enum prop_type type, struct expr *expr, struct expr *dep)
1.154 +{
1.155 + menu_add_prop(type, NULL, expr, dep);
1.156 +}
1.157 +
1.158 +void menu_add_symbol(enum prop_type type, struct symbol *sym, struct expr *dep)
1.159 +{
1.160 + menu_add_prop(type, NULL, expr_alloc_symbol(sym), dep);
1.161 +}
1.162 +
1.163 +void menu_add_option(int token, char *arg)
1.164 +{
1.165 + struct property *prop;
1.166 +
1.167 + switch (token) {
1.168 + case T_OPT_MODULES:
1.169 + prop = prop_alloc(P_DEFAULT, modules_sym);
1.170 + prop->expr = expr_alloc_symbol(current_entry->sym);
1.171 + break;
1.172 + case T_OPT_DEFCONFIG_LIST:
1.173 + if (!sym_defconfig_list)
1.174 + sym_defconfig_list = current_entry->sym;
1.175 + else if (sym_defconfig_list != current_entry->sym)
1.176 + zconf_error("trying to redefine defconfig symbol");
1.177 + break;
1.178 + }
1.179 +}
1.180 +
1.181 +static int menu_range_valid_sym(struct symbol *sym, struct symbol *sym2)
1.182 +{
1.183 + return sym2->type == S_INT || sym2->type == S_HEX ||
1.184 + (sym2->type == S_UNKNOWN && sym_string_valid(sym, sym2->name));
1.185 +}
1.186 +
1.187 +void sym_check_prop(struct symbol *sym)
1.188 +{
1.189 + struct property *prop;
1.190 + struct symbol *sym2;
1.191 + for (prop = sym->prop; prop; prop = prop->next) {
1.192 + switch (prop->type) {
1.193 + case P_DEFAULT:
1.194 + if ((sym->type == S_STRING || sym->type == S_INT || sym->type == S_HEX) &&
1.195 + prop->expr->type != E_SYMBOL)
1.196 + prop_warn(prop,
1.197 + "default for config symbol '%'"
1.198 + " must be a single symbol", sym->name);
1.199 + break;
1.200 + case P_SELECT:
1.201 + sym2 = prop_get_symbol(prop);
1.202 + if (sym->type != S_BOOLEAN && sym->type != S_TRISTATE)
1.203 + prop_warn(prop,
1.204 + "config symbol '%s' uses select, but is "
1.205 + "not boolean or tristate", sym->name);
1.206 + else if (sym2->type == S_UNKNOWN)
1.207 + prop_warn(prop,
1.208 + "'select' used by config symbol '%s' "
1.209 + "refer to undefined symbol '%s'",
1.210 + sym->name, sym2->name);
1.211 + else if (sym2->type != S_BOOLEAN && sym2->type != S_TRISTATE)
1.212 + prop_warn(prop,
1.213 + "'%s' has wrong type. 'select' only "
1.214 + "accept arguments of boolean and "
1.215 + "tristate type", sym2->name);
1.216 + break;
1.217 + case P_RANGE:
1.218 + if (sym->type != S_INT && sym->type != S_HEX)
1.219 + prop_warn(prop, "range is only allowed "
1.220 + "for int or hex symbols");
1.221 + if (!menu_range_valid_sym(sym, prop->expr->left.sym) ||
1.222 + !menu_range_valid_sym(sym, prop->expr->right.sym))
1.223 + prop_warn(prop, "range is invalid");
1.224 + break;
1.225 + default:
1.226 + ;
1.227 + }
1.228 + }
1.229 +}
1.230 +
1.231 +void menu_finalize(struct menu *parent)
1.232 +{
1.233 + struct menu *menu, *last_menu;
1.234 + struct symbol *sym;
1.235 + struct property *prop;
1.236 + struct expr *parentdep, *basedep, *dep, *dep2, **ep;
1.237 +
1.238 + sym = parent->sym;
1.239 + if (parent->list) {
1.240 + if (sym && sym_is_choice(sym)) {
1.241 + /* find the first choice value and find out choice type */
1.242 + for (menu = parent->list; menu; menu = menu->next) {
1.243 + if (menu->sym) {
1.244 + current_entry = parent;
1.245 + menu_set_type(menu->sym->type);
1.246 + current_entry = menu;
1.247 + menu_set_type(sym->type);
1.248 + break;
1.249 + }
1.250 + }
1.251 + parentdep = expr_alloc_symbol(sym);
1.252 + } else if (parent->prompt)
1.253 + parentdep = parent->prompt->visible.expr;
1.254 + else
1.255 + parentdep = parent->dep;
1.256 +
1.257 + for (menu = parent->list; menu; menu = menu->next) {
1.258 + basedep = expr_transform(menu->dep);
1.259 + basedep = expr_alloc_and(expr_copy(parentdep), basedep);
1.260 + basedep = expr_eliminate_dups(basedep);
1.261 + menu->dep = basedep;
1.262 + if (menu->sym)
1.263 + prop = menu->sym->prop;
1.264 + else
1.265 + prop = menu->prompt;
1.266 + for (; prop; prop = prop->next) {
1.267 + if (prop->menu != menu)
1.268 + continue;
1.269 + dep = expr_transform(prop->visible.expr);
1.270 + dep = expr_alloc_and(expr_copy(basedep), dep);
1.271 + dep = expr_eliminate_dups(dep);
1.272 + if (menu->sym && menu->sym->type != S_TRISTATE)
1.273 + dep = expr_trans_bool(dep);
1.274 + prop->visible.expr = dep;
1.275 + if (prop->type == P_SELECT) {
1.276 + struct symbol *es = prop_get_symbol(prop);
1.277 + es->rev_dep.expr = expr_alloc_or(es->rev_dep.expr,
1.278 + expr_alloc_and(expr_alloc_symbol(menu->sym), expr_copy(dep)));
1.279 + }
1.280 + }
1.281 + }
1.282 + for (menu = parent->list; menu; menu = menu->next)
1.283 + menu_finalize(menu);
1.284 + } else if (sym) {
1.285 + basedep = parent->prompt ? parent->prompt->visible.expr : NULL;
1.286 + basedep = expr_trans_compare(basedep, E_UNEQUAL, &symbol_no);
1.287 + basedep = expr_eliminate_dups(expr_transform(basedep));
1.288 + last_menu = NULL;
1.289 + for (menu = parent->next; menu; menu = menu->next) {
1.290 + dep = menu->prompt ? menu->prompt->visible.expr : menu->dep;
1.291 + if (!expr_contains_symbol(dep, sym))
1.292 + break;
1.293 + if (expr_depends_symbol(dep, sym))
1.294 + goto next;
1.295 + dep = expr_trans_compare(dep, E_UNEQUAL, &symbol_no);
1.296 + dep = expr_eliminate_dups(expr_transform(dep));
1.297 + dep2 = expr_copy(basedep);
1.298 + expr_eliminate_eq(&dep, &dep2);
1.299 + expr_free(dep);
1.300 + if (!expr_is_yes(dep2)) {
1.301 + expr_free(dep2);
1.302 + break;
1.303 + }
1.304 + expr_free(dep2);
1.305 + next:
1.306 + menu_finalize(menu);
1.307 + menu->parent = parent;
1.308 + last_menu = menu;
1.309 + }
1.310 + if (last_menu) {
1.311 + parent->list = parent->next;
1.312 + parent->next = last_menu->next;
1.313 + last_menu->next = NULL;
1.314 + }
1.315 + }
1.316 + for (menu = parent->list; menu; menu = menu->next) {
1.317 + if (sym && sym_is_choice(sym) && menu->sym) {
1.318 + menu->sym->flags |= SYMBOL_CHOICEVAL;
1.319 + if (!menu->prompt)
1.320 + menu_warn(menu, "choice value must have a prompt");
1.321 + for (prop = menu->sym->prop; prop; prop = prop->next) {
1.322 + if (prop->type == P_PROMPT && prop->menu != menu) {
1.323 + prop_warn(prop, "choice values "
1.324 + "currently only support a "
1.325 + "single prompt");
1.326 + }
1.327 + if (prop->type == P_DEFAULT)
1.328 + prop_warn(prop, "defaults for choice "
1.329 + "values not supported");
1.330 + }
1.331 + current_entry = menu;
1.332 + menu_set_type(sym->type);
1.333 + menu_add_symbol(P_CHOICE, sym, NULL);
1.334 + prop = sym_get_choice_prop(sym);
1.335 + for (ep = &prop->expr; *ep; ep = &(*ep)->left.expr)
1.336 + ;
1.337 + *ep = expr_alloc_one(E_CHOICE, NULL);
1.338 + (*ep)->right.sym = menu->sym;
1.339 + }
1.340 + if (menu->list && (!menu->prompt || !menu->prompt->text)) {
1.341 + for (last_menu = menu->list; ; last_menu = last_menu->next) {
1.342 + last_menu->parent = parent;
1.343 + if (!last_menu->next)
1.344 + break;
1.345 + }
1.346 + last_menu->next = menu->next;
1.347 + menu->next = menu->list;
1.348 + menu->list = NULL;
1.349 + }
1.350 + }
1.351 +
1.352 + if (sym && !(sym->flags & SYMBOL_WARNED)) {
1.353 + if (sym->type == S_UNKNOWN)
1.354 + menu_warn(parent, "config symbol defined without type");
1.355 +
1.356 + if (sym_is_choice(sym) && !parent->prompt)
1.357 + menu_warn(parent, "choice must have a prompt");
1.358 +
1.359 + /* Check properties connected to this symbol */
1.360 + sym_check_prop(sym);
1.361 + sym->flags |= SYMBOL_WARNED;
1.362 + }
1.363 +
1.364 + if (sym && !sym_is_optional(sym) && parent->prompt) {
1.365 + sym->rev_dep.expr = expr_alloc_or(sym->rev_dep.expr,
1.366 + expr_alloc_and(parent->prompt->visible.expr,
1.367 + expr_alloc_symbol(&symbol_mod)));
1.368 + }
1.369 +}
1.370 +
1.371 +bool menu_is_visible(struct menu *menu)
1.372 +{
1.373 + struct menu *child;
1.374 + struct symbol *sym;
1.375 + tristate visible;
1.376 +
1.377 + if (!menu->prompt)
1.378 + return false;
1.379 + sym = menu->sym;
1.380 + if (sym) {
1.381 + sym_calc_value(sym);
1.382 + visible = menu->prompt->visible.tri;
1.383 + } else
1.384 + visible = menu->prompt->visible.tri = expr_calc_value(menu->prompt->visible.expr);
1.385 +
1.386 + if (visible != no)
1.387 + return true;
1.388 + if (!sym || sym_get_tristate_value(menu->sym) == no)
1.389 + return false;
1.390 +
1.391 + for (child = menu->list; child; child = child->next)
1.392 + if (menu_is_visible(child))
1.393 + return true;
1.394 + return false;
1.395 +}
1.396 +
1.397 +const char *menu_get_prompt(struct menu *menu)
1.398 +{
1.399 + if (menu->prompt)
1.400 + return _(menu->prompt->text);
1.401 + else if (menu->sym)
1.402 + return _(menu->sym->name);
1.403 + return NULL;
1.404 +}
1.405 +
1.406 +struct menu *menu_get_root_menu(struct menu *menu)
1.407 +{
1.408 + return &rootmenu;
1.409 +}
1.410 +
1.411 +struct menu *menu_get_parent_menu(struct menu *menu)
1.412 +{
1.413 + enum prop_type type;
1.414 +
1.415 + for (; menu != &rootmenu; menu = menu->parent) {
1.416 + type = menu->prompt ? menu->prompt->type : 0;
1.417 + if (type == P_MENU)
1.418 + break;
1.419 + }
1.420 + return menu;
1.421 +}
1.422 +