kconfig/menu.c
branch1.2
changeset 731 65614732cfe7
child 943 1cca90ce0481
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/kconfig/menu.c	Sat Jul 26 15:14:48 2008 +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 = &current_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 = &current_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 +