kconfig/menu.c
author Arnaud Lacombe <lacombar@gmail.com>
Tue Aug 03 06:17:51 2010 +0200 (2010-08-03)
changeset 2064 f5ebe8c429dc
parent 943 1cca90ce0481
child 2448 a103abae1560
permissions -rw-r--r--
libc/uClibc: add uClibc 0.9.30.3

This version has been released a couple of month ago, but it never reached
crosstool-ng tree. This may be linked to the fact that the current 0.9.30.2,
once patched, has nothing much different from 0.9.30.3, released.

I'm not including any patch with this upgrade, on purpose.

Signed-off-by: Arnaud Lacombe <lacombar@gmail.com>
     1 /*
     2  * Copyright (C) 2002 Roman Zippel <zippel@linux-m68k.org>
     3  * Released under the terms of the GNU GPL v2.0.
     4  */
     5 
     6 #include <stdlib.h>
     7 #include <string.h>
     8 
     9 #define LKC_DIRECT_LINK
    10 #include "lkc.h"
    11 
    12 struct menu rootmenu;
    13 static struct menu **last_entry_ptr;
    14 
    15 struct file *file_list;
    16 struct file *current_file;
    17 
    18 void menu_warn(struct menu *menu, const char *fmt, ...)
    19 {
    20 	va_list ap;
    21 	va_start(ap, fmt);
    22 	fprintf(stderr, "%s:%d:warning: ", menu->file->name, menu->lineno);
    23 	vfprintf(stderr, fmt, ap);
    24 	fprintf(stderr, "\n");
    25 	va_end(ap);
    26 }
    27 
    28 static void prop_warn(struct property *prop, const char *fmt, ...)
    29 {
    30 	va_list ap;
    31 	va_start(ap, fmt);
    32 	fprintf(stderr, "%s:%d:warning: ", prop->file->name, prop->lineno);
    33 	vfprintf(stderr, fmt, ap);
    34 	fprintf(stderr, "\n");
    35 	va_end(ap);
    36 }
    37 
    38 void menu_init(void)
    39 {
    40 	current_entry = current_menu = &rootmenu;
    41 	last_entry_ptr = &rootmenu.list;
    42 }
    43 
    44 void menu_add_entry(struct symbol *sym)
    45 {
    46 	struct menu *menu;
    47 
    48 	menu = malloc(sizeof(*menu));
    49 	memset(menu, 0, sizeof(*menu));
    50 	menu->sym = sym;
    51 	menu->parent = current_menu;
    52 	menu->file = current_file;
    53 	menu->lineno = zconf_lineno();
    54 
    55 	*last_entry_ptr = menu;
    56 	last_entry_ptr = &menu->next;
    57 	current_entry = menu;
    58 }
    59 
    60 void menu_end_entry(void)
    61 {
    62 }
    63 
    64 struct menu *menu_add_menu(void)
    65 {
    66 	menu_end_entry();
    67 	last_entry_ptr = &current_entry->list;
    68 	return current_menu = current_entry;
    69 }
    70 
    71 void menu_end_menu(void)
    72 {
    73 	last_entry_ptr = &current_menu->next;
    74 	current_menu = current_menu->parent;
    75 }
    76 
    77 struct expr *menu_check_dep(struct expr *e)
    78 {
    79 	if (!e)
    80 		return e;
    81 
    82 	switch (e->type) {
    83 	case E_NOT:
    84 		e->left.expr = menu_check_dep(e->left.expr);
    85 		break;
    86 	case E_OR:
    87 	case E_AND:
    88 		e->left.expr = menu_check_dep(e->left.expr);
    89 		e->right.expr = menu_check_dep(e->right.expr);
    90 		break;
    91 	case E_SYMBOL:
    92 		/* change 'm' into 'm' && MODULES */
    93 		if (e->left.sym == &symbol_mod)
    94 			return expr_alloc_and(e, expr_alloc_symbol(modules_sym));
    95 		break;
    96 	default:
    97 		break;
    98 	}
    99 	return e;
   100 }
   101 
   102 void menu_add_dep(struct expr *dep)
   103 {
   104 	current_entry->dep = expr_alloc_and(current_entry->dep, menu_check_dep(dep));
   105 }
   106 
   107 void menu_set_type(int type)
   108 {
   109 	struct symbol *sym = current_entry->sym;
   110 
   111 	if (sym->type == type)
   112 		return;
   113 	if (sym->type == S_UNKNOWN) {
   114 		sym->type = type;
   115 		return;
   116 	}
   117 	menu_warn(current_entry, "type of '%s' redefined from '%s' to '%s'",
   118 	    sym->name ? sym->name : "<choice>",
   119 	    sym_type_name(sym->type), sym_type_name(type));
   120 }
   121 
   122 struct property *menu_add_prop(enum prop_type type, char *prompt, struct expr *expr, struct expr *dep)
   123 {
   124 	struct property *prop = prop_alloc(type, current_entry->sym);
   125 
   126 	prop->menu = current_entry;
   127 	prop->expr = expr;
   128 	prop->visible.expr = menu_check_dep(dep);
   129 
   130 	if (prompt) {
   131 		/* For crostool-NG, a leading pipe followed with spaces
   132 		 * means that pipe shall be removed, and the spaces should
   133 		 * not be trimmed.
   134 		 */
   135 		if (*prompt == '|')
   136 			prompt++;
   137 		else if (isspace(*prompt)) {
   138 			/* Silently trim leading spaces */
   139 			while (isspace(*prompt))
   140 				prompt++;
   141 		}
   142 		if (current_entry->prompt)
   143 			prop_warn(prop, "prompt redefined");
   144 		current_entry->prompt = prop;
   145 	}
   146 	prop->text = prompt;
   147 
   148 	return prop;
   149 }
   150 
   151 struct property *menu_add_prompt(enum prop_type type, char *prompt, struct expr *dep)
   152 {
   153 	return menu_add_prop(type, prompt, NULL, dep);
   154 }
   155 
   156 void menu_add_expr(enum prop_type type, struct expr *expr, struct expr *dep)
   157 {
   158 	menu_add_prop(type, NULL, expr, dep);
   159 }
   160 
   161 void menu_add_symbol(enum prop_type type, struct symbol *sym, struct expr *dep)
   162 {
   163 	menu_add_prop(type, NULL, expr_alloc_symbol(sym), dep);
   164 }
   165 
   166 void menu_add_option(int token, char *arg)
   167 {
   168 	struct property *prop;
   169 
   170 	switch (token) {
   171 	case T_OPT_MODULES:
   172 		prop = prop_alloc(P_DEFAULT, modules_sym);
   173 		prop->expr = expr_alloc_symbol(current_entry->sym);
   174 		break;
   175 	case T_OPT_DEFCONFIG_LIST:
   176 		if (!sym_defconfig_list)
   177 			sym_defconfig_list = current_entry->sym;
   178 		else if (sym_defconfig_list != current_entry->sym)
   179 			zconf_error("trying to redefine defconfig symbol");
   180 		break;
   181 	case T_OPT_ENV:
   182 		prop_add_env(arg);
   183 		break;
   184 	}
   185 }
   186 
   187 static int menu_range_valid_sym(struct symbol *sym, struct symbol *sym2)
   188 {
   189 	return sym2->type == S_INT || sym2->type == S_HEX ||
   190 	       (sym2->type == S_UNKNOWN && sym_string_valid(sym, sym2->name));
   191 }
   192 
   193 void sym_check_prop(struct symbol *sym)
   194 {
   195 	struct property *prop;
   196 	struct symbol *sym2;
   197 	for (prop = sym->prop; prop; prop = prop->next) {
   198 		switch (prop->type) {
   199 		case P_DEFAULT:
   200 			if ((sym->type == S_STRING || sym->type == S_INT || sym->type == S_HEX) &&
   201 			    prop->expr->type != E_SYMBOL)
   202 				prop_warn(prop,
   203 				    "default for config symbol '%'"
   204 				    " must be a single symbol", sym->name);
   205 			break;
   206 		case P_SELECT:
   207 			sym2 = prop_get_symbol(prop);
   208 			if (sym->type != S_BOOLEAN && sym->type != S_TRISTATE)
   209 				prop_warn(prop,
   210 				    "config symbol '%s' uses select, but is "
   211 				    "not boolean or tristate", sym->name);
   212 			else if (sym2->type != S_UNKNOWN &&
   213 			         sym2->type != S_BOOLEAN &&
   214 			         sym2->type != S_TRISTATE)
   215 				prop_warn(prop,
   216 				    "'%s' has wrong type. 'select' only "
   217 				    "accept arguments of boolean and "
   218 				    "tristate type", sym2->name);
   219 			break;
   220 		case P_RANGE:
   221 			if (sym->type != S_INT && sym->type != S_HEX)
   222 				prop_warn(prop, "range is only allowed "
   223 				                "for int or hex symbols");
   224 			if (!menu_range_valid_sym(sym, prop->expr->left.sym) ||
   225 			    !menu_range_valid_sym(sym, prop->expr->right.sym))
   226 				prop_warn(prop, "range is invalid");
   227 			break;
   228 		default:
   229 			;
   230 		}
   231 	}
   232 }
   233 
   234 void menu_finalize(struct menu *parent)
   235 {
   236 	struct menu *menu, *last_menu;
   237 	struct symbol *sym;
   238 	struct property *prop;
   239 	struct expr *parentdep, *basedep, *dep, *dep2, **ep;
   240 
   241 	sym = parent->sym;
   242 	if (parent->list) {
   243 		if (sym && sym_is_choice(sym)) {
   244 			if (sym->type == S_UNKNOWN) {
   245 				/* find the first choice value to find out choice type */
   246 				current_entry = parent;
   247 				for (menu = parent->list; menu; menu = menu->next) {
   248 					if (menu->sym && menu->sym->type != S_UNKNOWN) {
   249 						menu_set_type(menu->sym->type);
   250 						break;
   251 					}
   252 				}
   253 			}
   254 			/* set the type of the remaining choice values */
   255 			for (menu = parent->list; menu; menu = menu->next) {
   256 				current_entry = menu;
   257 				if (menu->sym && menu->sym->type == S_UNKNOWN)
   258 					menu_set_type(sym->type);
   259 			}
   260 			parentdep = expr_alloc_symbol(sym);
   261 		} else if (parent->prompt)
   262 			parentdep = parent->prompt->visible.expr;
   263 		else
   264 			parentdep = parent->dep;
   265 
   266 		for (menu = parent->list; menu; menu = menu->next) {
   267 			basedep = expr_transform(menu->dep);
   268 			basedep = expr_alloc_and(expr_copy(parentdep), basedep);
   269 			basedep = expr_eliminate_dups(basedep);
   270 			menu->dep = basedep;
   271 			if (menu->sym)
   272 				prop = menu->sym->prop;
   273 			else
   274 				prop = menu->prompt;
   275 			for (; prop; prop = prop->next) {
   276 				if (prop->menu != menu)
   277 					continue;
   278 				dep = expr_transform(prop->visible.expr);
   279 				dep = expr_alloc_and(expr_copy(basedep), dep);
   280 				dep = expr_eliminate_dups(dep);
   281 				if (menu->sym && menu->sym->type != S_TRISTATE)
   282 					dep = expr_trans_bool(dep);
   283 				prop->visible.expr = dep;
   284 				if (prop->type == P_SELECT) {
   285 					struct symbol *es = prop_get_symbol(prop);
   286 					es->rev_dep.expr = expr_alloc_or(es->rev_dep.expr,
   287 							expr_alloc_and(expr_alloc_symbol(menu->sym), expr_copy(dep)));
   288 				}
   289 			}
   290 		}
   291 		for (menu = parent->list; menu; menu = menu->next)
   292 			menu_finalize(menu);
   293 	} else if (sym) {
   294 		basedep = parent->prompt ? parent->prompt->visible.expr : NULL;
   295 		basedep = expr_trans_compare(basedep, E_UNEQUAL, &symbol_no);
   296 		basedep = expr_eliminate_dups(expr_transform(basedep));
   297 		last_menu = NULL;
   298 		for (menu = parent->next; menu; menu = menu->next) {
   299 			dep = menu->prompt ? menu->prompt->visible.expr : menu->dep;
   300 			if (!expr_contains_symbol(dep, sym))
   301 				break;
   302 			if (expr_depends_symbol(dep, sym))
   303 				goto next;
   304 			dep = expr_trans_compare(dep, E_UNEQUAL, &symbol_no);
   305 			dep = expr_eliminate_dups(expr_transform(dep));
   306 			dep2 = expr_copy(basedep);
   307 			expr_eliminate_eq(&dep, &dep2);
   308 			expr_free(dep);
   309 			if (!expr_is_yes(dep2)) {
   310 				expr_free(dep2);
   311 				break;
   312 			}
   313 			expr_free(dep2);
   314 		next:
   315 			menu_finalize(menu);
   316 			menu->parent = parent;
   317 			last_menu = menu;
   318 		}
   319 		if (last_menu) {
   320 			parent->list = parent->next;
   321 			parent->next = last_menu->next;
   322 			last_menu->next = NULL;
   323 		}
   324 	}
   325 	for (menu = parent->list; menu; menu = menu->next) {
   326 		if (sym && sym_is_choice(sym) &&
   327 		    menu->sym && !sym_is_choice_value(menu->sym)) {
   328 			current_entry = menu;
   329 			menu->sym->flags |= SYMBOL_CHOICEVAL;
   330 			if (!menu->prompt)
   331 				menu_warn(menu, "choice value must have a prompt");
   332 			for (prop = menu->sym->prop; prop; prop = prop->next) {
   333 				if (prop->type == P_DEFAULT)
   334 					prop_warn(prop, "defaults for choice "
   335 						  "values not supported");
   336 				if (prop->menu == menu)
   337 					continue;
   338 				if (prop->type == P_PROMPT &&
   339 				    prop->menu->parent->sym != sym)
   340 					prop_warn(prop, "choice value used outside its choice group");
   341 			}
   342 			/* Non-tristate choice values of tristate choices must
   343 			 * depend on the choice being set to Y. The choice
   344 			 * values' dependencies were propagated to their
   345 			 * properties above, so the change here must be re-
   346 			 * propagated.
   347 			 */
   348 			if (sym->type == S_TRISTATE && menu->sym->type != S_TRISTATE) {
   349 				basedep = expr_alloc_comp(E_EQUAL, sym, &symbol_yes);
   350 				menu->dep = expr_alloc_and(basedep, menu->dep);
   351 				for (prop = menu->sym->prop; prop; prop = prop->next) {
   352 					if (prop->menu != menu)
   353 						continue;
   354 					prop->visible.expr = expr_alloc_and(expr_copy(basedep),
   355 									    prop->visible.expr);
   356 				}
   357 			}
   358 			menu_add_symbol(P_CHOICE, sym, NULL);
   359 			prop = sym_get_choice_prop(sym);
   360 			for (ep = &prop->expr; *ep; ep = &(*ep)->left.expr)
   361 				;
   362 			*ep = expr_alloc_one(E_LIST, NULL);
   363 			(*ep)->right.sym = menu->sym;
   364 		}
   365 		if (menu->list && (!menu->prompt || !menu->prompt->text)) {
   366 			for (last_menu = menu->list; ; last_menu = last_menu->next) {
   367 				last_menu->parent = parent;
   368 				if (!last_menu->next)
   369 					break;
   370 			}
   371 			last_menu->next = menu->next;
   372 			menu->next = menu->list;
   373 			menu->list = NULL;
   374 		}
   375 	}
   376 
   377 	if (sym && !(sym->flags & SYMBOL_WARNED)) {
   378 		if (sym->type == S_UNKNOWN)
   379 			menu_warn(parent, "config symbol defined without type");
   380 
   381 		if (sym_is_choice(sym) && !parent->prompt)
   382 			menu_warn(parent, "choice must have a prompt");
   383 
   384 		/* Check properties connected to this symbol */
   385 		sym_check_prop(sym);
   386 		sym->flags |= SYMBOL_WARNED;
   387 	}
   388 
   389 	if (sym && !sym_is_optional(sym) && parent->prompt) {
   390 		sym->rev_dep.expr = expr_alloc_or(sym->rev_dep.expr,
   391 				expr_alloc_and(parent->prompt->visible.expr,
   392 					expr_alloc_symbol(&symbol_mod)));
   393 	}
   394 }
   395 
   396 bool menu_is_visible(struct menu *menu)
   397 {
   398 	struct menu *child;
   399 	struct symbol *sym;
   400 	tristate visible;
   401 
   402 	if (!menu->prompt)
   403 		return false;
   404 	sym = menu->sym;
   405 	if (sym) {
   406 		sym_calc_value(sym);
   407 		visible = menu->prompt->visible.tri;
   408 	} else
   409 		visible = menu->prompt->visible.tri = expr_calc_value(menu->prompt->visible.expr);
   410 
   411 	if (visible != no)
   412 		return true;
   413 	if (!sym || sym_get_tristate_value(menu->sym) == no)
   414 		return false;
   415 
   416 	for (child = menu->list; child; child = child->next)
   417 		if (menu_is_visible(child))
   418 			return true;
   419 	return false;
   420 }
   421 
   422 const char *menu_get_prompt(struct menu *menu)
   423 {
   424 	if (menu->prompt)
   425 		return menu->prompt->text;
   426 	else if (menu->sym)
   427 		return menu->sym->name;
   428 	return NULL;
   429 }
   430 
   431 struct menu *menu_get_root_menu(struct menu *menu)
   432 {
   433 	return &rootmenu;
   434 }
   435 
   436 struct menu *menu_get_parent_menu(struct menu *menu)
   437 {
   438 	enum prop_type type;
   439 
   440 	for (; menu != &rootmenu; menu = menu->parent) {
   441 		type = menu->prompt ? menu->prompt->type : 0;
   442 		if (type == P_MENU)
   443 			break;
   444 	}
   445 	return menu;
   446 }
   447 
   448 bool menu_has_help(struct menu *menu)
   449 {
   450 	return menu->help != NULL;
   451 }
   452 
   453 const char *menu_get_help(struct menu *menu)
   454 {
   455 	if (menu->help)
   456 		return menu->help;
   457 	else
   458 		return "";
   459 }