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 = ¤t_entry->list; 68 return current_menu = current_entry; 69 } 70 71 void menu_end_menu(void) 72 { 73 last_entry_ptr = ¤t_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 if (isspace(*prompt)) { 132 prop_warn(prop, "leading whitespace ignored"); 133 while (isspace(*prompt)) 134 prompt++; 135 } 136 if (current_entry->prompt) 137 prop_warn(prop, "prompt redefined"); 138 current_entry->prompt = prop; 139 } 140 prop->text = prompt; 141 142 return prop; 143 } 144 145 struct property *menu_add_prompt(enum prop_type type, char *prompt, struct expr *dep) 146 { 147 return menu_add_prop(type, prompt, NULL, dep); 148 } 149 150 void menu_add_expr(enum prop_type type, struct expr *expr, struct expr *dep) 151 { 152 menu_add_prop(type, NULL, expr, dep); 153 } 154 155 void menu_add_symbol(enum prop_type type, struct symbol *sym, struct expr *dep) 156 { 157 menu_add_prop(type, NULL, expr_alloc_symbol(sym), dep); 158 } 159 160 void menu_add_option(int token, char *arg) 161 { 162 struct property *prop; 163 164 switch (token) { 165 case T_OPT_MODULES: 166 prop = prop_alloc(P_DEFAULT, modules_sym); 167 prop->expr = expr_alloc_symbol(current_entry->sym); 168 break; 169 case T_OPT_DEFCONFIG_LIST: 170 if (!sym_defconfig_list) 171 sym_defconfig_list = current_entry->sym; 172 else if (sym_defconfig_list != current_entry->sym) 173 zconf_error("trying to redefine defconfig symbol"); 174 break; 175 case T_OPT_ENV: 176 prop_add_env(arg); 177 break; 178 } 179 } 180 181 static int menu_range_valid_sym(struct symbol *sym, struct symbol *sym2) 182 { 183 return sym2->type == S_INT || sym2->type == S_HEX || 184 (sym2->type == S_UNKNOWN && sym_string_valid(sym, sym2->name)); 185 } 186 187 void sym_check_prop(struct symbol *sym) 188 { 189 struct property *prop; 190 struct symbol *sym2; 191 for (prop = sym->prop; prop; prop = prop->next) { 192 switch (prop->type) { 193 case P_DEFAULT: 194 if ((sym->type == S_STRING || sym->type == S_INT || sym->type == S_HEX) && 195 prop->expr->type != E_SYMBOL) 196 prop_warn(prop, 197 "default for config symbol '%'" 198 " must be a single symbol", sym->name); 199 break; 200 case P_SELECT: 201 sym2 = prop_get_symbol(prop); 202 if (sym->type != S_BOOLEAN && sym->type != S_TRISTATE) 203 prop_warn(prop, 204 "config symbol '%s' uses select, but is " 205 "not boolean or tristate", sym->name); 206 else if (sym2->type == S_UNKNOWN) 207 prop_warn(prop, 208 "'select' used by config symbol '%s' " 209 "refers to undefined symbol '%s'", 210 sym->name, sym2->name); 211 else if (sym2->type != S_BOOLEAN && sym2->type != S_TRISTATE) 212 prop_warn(prop, 213 "'%s' has wrong type. 'select' only " 214 "accept arguments of boolean and " 215 "tristate type", sym2->name); 216 break; 217 case P_RANGE: 218 if (sym->type != S_INT && sym->type != S_HEX) 219 prop_warn(prop, "range is only allowed " 220 "for int or hex symbols"); 221 if (!menu_range_valid_sym(sym, prop->expr->left.sym) || 222 !menu_range_valid_sym(sym, prop->expr->right.sym)) 223 prop_warn(prop, "range is invalid"); 224 break; 225 default: 226 ; 227 } 228 } 229 } 230 231 void menu_finalize(struct menu *parent) 232 { 233 struct menu *menu, *last_menu; 234 struct symbol *sym; 235 struct property *prop; 236 struct expr *parentdep, *basedep, *dep, *dep2, **ep; 237 238 sym = parent->sym; 239 if (parent->list) { 240 if (sym && sym_is_choice(sym)) { 241 /* find the first choice value and find out choice type */ 242 for (menu = parent->list; menu; menu = menu->next) { 243 if (menu->sym) { 244 current_entry = parent; 245 if (sym->type == S_UNKNOWN) 246 menu_set_type(menu->sym->type); 247 current_entry = menu; 248 if (menu->sym->type == S_UNKNOWN) 249 menu_set_type(sym->type); 250 break; 251 } 252 } 253 parentdep = expr_alloc_symbol(sym); 254 } else if (parent->prompt) 255 parentdep = parent->prompt->visible.expr; 256 else 257 parentdep = parent->dep; 258 259 for (menu = parent->list; menu; menu = menu->next) { 260 basedep = expr_transform(menu->dep); 261 basedep = expr_alloc_and(expr_copy(parentdep), basedep); 262 basedep = expr_eliminate_dups(basedep); 263 menu->dep = basedep; 264 if (menu->sym) 265 prop = menu->sym->prop; 266 else 267 prop = menu->prompt; 268 for (; prop; prop = prop->next) { 269 if (prop->menu != menu) 270 continue; 271 dep = expr_transform(prop->visible.expr); 272 dep = expr_alloc_and(expr_copy(basedep), dep); 273 dep = expr_eliminate_dups(dep); 274 if (menu->sym && menu->sym->type != S_TRISTATE) 275 dep = expr_trans_bool(dep); 276 prop->visible.expr = dep; 277 if (prop->type == P_SELECT) { 278 struct symbol *es = prop_get_symbol(prop); 279 es->rev_dep.expr = expr_alloc_or(es->rev_dep.expr, 280 expr_alloc_and(expr_alloc_symbol(menu->sym), expr_copy(dep))); 281 } 282 } 283 } 284 for (menu = parent->list; menu; menu = menu->next) 285 menu_finalize(menu); 286 } else if (sym) { 287 basedep = parent->prompt ? parent->prompt->visible.expr : NULL; 288 basedep = expr_trans_compare(basedep, E_UNEQUAL, &symbol_no); 289 basedep = expr_eliminate_dups(expr_transform(basedep)); 290 last_menu = NULL; 291 for (menu = parent->next; menu; menu = menu->next) { 292 dep = menu->prompt ? menu->prompt->visible.expr : menu->dep; 293 if (!expr_contains_symbol(dep, sym)) 294 break; 295 if (expr_depends_symbol(dep, sym)) 296 goto next; 297 dep = expr_trans_compare(dep, E_UNEQUAL, &symbol_no); 298 dep = expr_eliminate_dups(expr_transform(dep)); 299 dep2 = expr_copy(basedep); 300 expr_eliminate_eq(&dep, &dep2); 301 expr_free(dep); 302 if (!expr_is_yes(dep2)) { 303 expr_free(dep2); 304 break; 305 } 306 expr_free(dep2); 307 next: 308 menu_finalize(menu); 309 menu->parent = parent; 310 last_menu = menu; 311 } 312 if (last_menu) { 313 parent->list = parent->next; 314 parent->next = last_menu->next; 315 last_menu->next = NULL; 316 } 317 } 318 for (menu = parent->list; menu; menu = menu->next) { 319 if (sym && sym_is_choice(sym) && menu->sym) { 320 menu->sym->flags |= SYMBOL_CHOICEVAL; 321 if (!menu->prompt) 322 menu_warn(menu, "choice value must have a prompt"); 323 for (prop = menu->sym->prop; prop; prop = prop->next) { 324 if (prop->type == P_PROMPT && prop->menu != menu) { 325 prop_warn(prop, "choice values " 326 "currently only support a " 327 "single prompt"); 328 } 329 if (prop->type == P_DEFAULT) 330 prop_warn(prop, "defaults for choice " 331 "values not supported"); 332 } 333 current_entry = menu; 334 if (menu->sym->type == S_UNKNOWN) 335 menu_set_type(sym->type); 336 /* Non-tristate choice values of tristate choices must 337 * depend on the choice being set to Y. The choice 338 * values' dependencies were propagated to their 339 * properties above, so the change here must be re- 340 * propagated. */ 341 if (sym->type == S_TRISTATE && menu->sym->type != S_TRISTATE) { 342 basedep = expr_alloc_comp(E_EQUAL, sym, &symbol_yes); 343 basedep = expr_alloc_and(basedep, menu->dep); 344 basedep = expr_eliminate_dups(basedep); 345 menu->dep = basedep; 346 for (prop = menu->sym->prop; prop; prop = prop->next) { 347 if (prop->menu != menu) 348 continue; 349 dep = expr_alloc_and(expr_copy(basedep), 350 prop->visible.expr); 351 dep = expr_eliminate_dups(dep); 352 dep = expr_trans_bool(dep); 353 prop->visible.expr = dep; 354 if (prop->type == P_SELECT) { 355 struct symbol *es = prop_get_symbol(prop); 356 dep2 = expr_alloc_symbol(menu->sym); 357 dep = expr_alloc_and(dep2, 358 expr_copy(dep)); 359 dep = expr_alloc_or(es->rev_dep.expr, dep); 360 dep = expr_eliminate_dups(dep); 361 es->rev_dep.expr = dep; 362 } 363 } 364 } 365 menu_add_symbol(P_CHOICE, sym, NULL); 366 prop = sym_get_choice_prop(sym); 367 for (ep = &prop->expr; *ep; ep = &(*ep)->left.expr) 368 ; 369 *ep = expr_alloc_one(E_LIST, NULL); 370 (*ep)->right.sym = menu->sym; 371 } 372 if (menu->list && (!menu->prompt || !menu->prompt->text)) { 373 for (last_menu = menu->list; ; last_menu = last_menu->next) { 374 last_menu->parent = parent; 375 if (!last_menu->next) 376 break; 377 } 378 last_menu->next = menu->next; 379 menu->next = menu->list; 380 menu->list = NULL; 381 } 382 } 383 384 if (sym && !(sym->flags & SYMBOL_WARNED)) { 385 if (sym->type == S_UNKNOWN) 386 menu_warn(parent, "config symbol defined without type"); 387 388 if (sym_is_choice(sym) && !parent->prompt) 389 menu_warn(parent, "choice must have a prompt"); 390 391 /* Check properties connected to this symbol */ 392 sym_check_prop(sym); 393 sym->flags |= SYMBOL_WARNED; 394 } 395 396 if (sym && !sym_is_optional(sym) && parent->prompt) { 397 sym->rev_dep.expr = expr_alloc_or(sym->rev_dep.expr, 398 expr_alloc_and(parent->prompt->visible.expr, 399 expr_alloc_symbol(&symbol_mod))); 400 } 401 } 402 403 bool menu_is_visible(struct menu *menu) 404 { 405 struct menu *child; 406 struct symbol *sym; 407 tristate visible; 408 409 if (!menu->prompt) 410 return false; 411 sym = menu->sym; 412 if (sym) { 413 sym_calc_value(sym); 414 visible = menu->prompt->visible.tri; 415 } else 416 visible = menu->prompt->visible.tri = expr_calc_value(menu->prompt->visible.expr); 417 418 if (visible != no) 419 return true; 420 if (!sym || sym_get_tristate_value(menu->sym) == no) 421 return false; 422 423 for (child = menu->list; child; child = child->next) 424 if (menu_is_visible(child)) 425 return true; 426 return false; 427 } 428 429 const char *menu_get_prompt(struct menu *menu) 430 { 431 if (menu->prompt) 432 return menu->prompt->text; 433 else if (menu->sym) 434 return menu->sym->name; 435 return NULL; 436 } 437 438 struct menu *menu_get_root_menu(struct menu *menu) 439 { 440 return &rootmenu; 441 } 442 443 struct menu *menu_get_parent_menu(struct menu *menu) 444 { 445 enum prop_type type; 446 447 for (; menu != &rootmenu; menu = menu->parent) { 448 type = menu->prompt ? menu->prompt->type : 0; 449 if (type == P_MENU) 450 break; 451 } 452 return menu; 453 } 454 455 bool menu_has_help(struct menu *menu) 456 { 457 return menu->help != NULL; 458 } 459 460 const char *menu_get_help(struct menu *menu) 461 { 462 if (menu->help) 463 return menu->help; 464 else 465 return ""; 466 } 467