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 static 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 void menu_add_menu(void) 65 { 66 current_menu = current_entry; 67 last_entry_ptr = ¤t_entry->list; 68 } 69 70 void menu_end_menu(void) 71 { 72 last_entry_ptr = ¤t_menu->next; 73 current_menu = current_menu->parent; 74 } 75 76 struct expr *menu_check_dep(struct expr *e) 77 { 78 if (!e) 79 return e; 80 81 switch (e->type) { 82 case E_NOT: 83 e->left.expr = menu_check_dep(e->left.expr); 84 break; 85 case E_OR: 86 case E_AND: 87 e->left.expr = menu_check_dep(e->left.expr); 88 e->right.expr = menu_check_dep(e->right.expr); 89 break; 90 case E_SYMBOL: 91 /* change 'm' into 'm' && MODULES */ 92 if (e->left.sym == &symbol_mod) 93 return expr_alloc_and(e, expr_alloc_symbol(modules_sym)); 94 break; 95 default: 96 break; 97 } 98 return e; 99 } 100 101 void menu_add_dep(struct expr *dep) 102 { 103 current_entry->dep = expr_alloc_and(current_entry->dep, menu_check_dep(dep)); 104 } 105 106 void menu_set_type(int type) 107 { 108 struct symbol *sym = current_entry->sym; 109 110 if (sym->type == type) 111 return; 112 if (sym->type == S_UNKNOWN) { 113 sym->type = type; 114 return; 115 } 116 menu_warn(current_entry, "type of '%s' redefined from '%s' to '%s'\n", 117 sym->name ? sym->name : "<choice>", 118 sym_type_name(sym->type), sym_type_name(type)); 119 } 120 121 struct property *menu_add_prop(enum prop_type type, char *prompt, struct expr *expr, struct expr *dep) 122 { 123 struct property *prop = prop_alloc(type, current_entry->sym); 124 125 prop->menu = current_entry; 126 prop->text = prompt; 127 prop->expr = expr; 128 prop->visible.expr = menu_check_dep(dep); 129 130 if (prompt) { 131 if (current_entry->prompt) 132 menu_warn(current_entry, "prompt redefined\n"); 133 current_entry->prompt = prop; 134 } 135 136 return prop; 137 } 138 139 void menu_add_prompt(enum prop_type type, char *prompt, struct expr *dep) 140 { 141 menu_add_prop(type, prompt, NULL, dep); 142 } 143 144 void menu_add_expr(enum prop_type type, struct expr *expr, struct expr *dep) 145 { 146 menu_add_prop(type, NULL, expr, dep); 147 } 148 149 void menu_add_symbol(enum prop_type type, struct symbol *sym, struct expr *dep) 150 { 151 menu_add_prop(type, NULL, expr_alloc_symbol(sym), dep); 152 } 153 154 void sym_check_prop(struct symbol *sym) 155 { 156 struct property *prop; 157 struct symbol *sym2; 158 for (prop = sym->prop; prop; prop = prop->next) { 159 switch (prop->type) { 160 case P_DEFAULT: 161 if ((sym->type == S_STRING || sym->type == S_INT || sym->type == S_HEX) && 162 prop->expr->type != E_SYMBOL) 163 prop_warn(prop, 164 "default for config symbol '%'" 165 " must be a single symbol", sym->name); 166 break; 167 case P_SELECT: 168 sym2 = prop_get_symbol(prop); 169 if (sym->type != S_BOOLEAN && sym->type != S_TRISTATE) 170 prop_warn(prop, 171 "config symbol '%s' uses select, but is " 172 "not boolean or tristate", sym->name); 173 else if (sym2->type == S_UNKNOWN) 174 prop_warn(prop, 175 "'select' used by config symbol '%s' " 176 "refer to undefined symbol '%s'", 177 sym->name, sym2->name); 178 else if (sym2->type != S_BOOLEAN && sym2->type != S_TRISTATE) 179 prop_warn(prop, 180 "'%s' has wrong type. 'select' only " 181 "accept arguments of boolean and " 182 "tristate type", sym2->name); 183 break; 184 case P_RANGE: 185 if (sym->type != S_INT && sym->type != S_HEX) 186 prop_warn(prop, "range is only allowed " 187 "for int or hex symbols"); 188 if (!sym_string_valid(sym, prop->expr->left.sym->name) || 189 !sym_string_valid(sym, prop->expr->right.sym->name)) 190 prop_warn(prop, "range is invalid"); 191 break; 192 default: 193 ; 194 } 195 } 196 } 197 198 void menu_finalize(struct menu *parent) 199 { 200 struct menu *menu, *last_menu; 201 struct symbol *sym; 202 struct property *prop; 203 struct expr *parentdep, *basedep, *dep, *dep2, **ep; 204 205 sym = parent->sym; 206 if (parent->list) { 207 if (sym && sym_is_choice(sym)) { 208 /* find the first choice value and find out choice type */ 209 for (menu = parent->list; menu; menu = menu->next) { 210 if (menu->sym) { 211 current_entry = parent; 212 menu_set_type(menu->sym->type); 213 current_entry = menu; 214 menu_set_type(sym->type); 215 break; 216 } 217 } 218 parentdep = expr_alloc_symbol(sym); 219 } else if (parent->prompt) 220 parentdep = parent->prompt->visible.expr; 221 else 222 parentdep = parent->dep; 223 224 for (menu = parent->list; menu; menu = menu->next) { 225 basedep = expr_transform(menu->dep); 226 basedep = expr_alloc_and(expr_copy(parentdep), basedep); 227 basedep = expr_eliminate_dups(basedep); 228 menu->dep = basedep; 229 if (menu->sym) 230 prop = menu->sym->prop; 231 else 232 prop = menu->prompt; 233 for (; prop; prop = prop->next) { 234 if (prop->menu != menu) 235 continue; 236 dep = expr_transform(prop->visible.expr); 237 dep = expr_alloc_and(expr_copy(basedep), dep); 238 dep = expr_eliminate_dups(dep); 239 if (menu->sym && menu->sym->type != S_TRISTATE) 240 dep = expr_trans_bool(dep); 241 prop->visible.expr = dep; 242 if (prop->type == P_SELECT) { 243 struct symbol *es = prop_get_symbol(prop); 244 es->rev_dep.expr = expr_alloc_or(es->rev_dep.expr, 245 expr_alloc_and(expr_alloc_symbol(menu->sym), expr_copy(dep))); 246 } 247 } 248 } 249 for (menu = parent->list; menu; menu = menu->next) 250 menu_finalize(menu); 251 } else if (sym) { 252 basedep = parent->prompt ? parent->prompt->visible.expr : NULL; 253 basedep = expr_trans_compare(basedep, E_UNEQUAL, &symbol_no); 254 basedep = expr_eliminate_dups(expr_transform(basedep)); 255 last_menu = NULL; 256 for (menu = parent->next; menu; menu = menu->next) { 257 dep = menu->prompt ? menu->prompt->visible.expr : menu->dep; 258 if (!expr_contains_symbol(dep, sym)) 259 break; 260 if (expr_depends_symbol(dep, sym)) 261 goto next; 262 dep = expr_trans_compare(dep, E_UNEQUAL, &symbol_no); 263 dep = expr_eliminate_dups(expr_transform(dep)); 264 dep2 = expr_copy(basedep); 265 expr_eliminate_eq(&dep, &dep2); 266 expr_free(dep); 267 if (!expr_is_yes(dep2)) { 268 expr_free(dep2); 269 break; 270 } 271 expr_free(dep2); 272 next: 273 menu_finalize(menu); 274 menu->parent = parent; 275 last_menu = menu; 276 } 277 if (last_menu) { 278 parent->list = parent->next; 279 parent->next = last_menu->next; 280 last_menu->next = NULL; 281 } 282 } 283 for (menu = parent->list; menu; menu = menu->next) { 284 if (sym && sym_is_choice(sym) && menu->sym) { 285 menu->sym->flags |= SYMBOL_CHOICEVAL; 286 if (!menu->prompt) 287 menu_warn(menu, "choice value must have a prompt"); 288 for (prop = menu->sym->prop; prop; prop = prop->next) { 289 if (prop->type == P_PROMPT && prop->menu != menu) { 290 prop_warn(prop, "choice values " 291 "currently only support a " 292 "single prompt"); 293 } 294 if (prop->type == P_DEFAULT) 295 prop_warn(prop, "defaults for choice " 296 "values not supported"); 297 } 298 current_entry = menu; 299 menu_set_type(sym->type); 300 menu_add_symbol(P_CHOICE, sym, NULL); 301 prop = sym_get_choice_prop(sym); 302 for (ep = &prop->expr; *ep; ep = &(*ep)->left.expr) 303 ; 304 *ep = expr_alloc_one(E_CHOICE, NULL); 305 (*ep)->right.sym = menu->sym; 306 } 307 if (menu->list && (!menu->prompt || !menu->prompt->text)) { 308 for (last_menu = menu->list; ; last_menu = last_menu->next) { 309 last_menu->parent = parent; 310 if (!last_menu->next) 311 break; 312 } 313 last_menu->next = menu->next; 314 menu->next = menu->list; 315 menu->list = NULL; 316 } 317 } 318 319 if (sym && !(sym->flags & SYMBOL_WARNED)) { 320 if (sym->type == S_UNKNOWN) 321 menu_warn(parent, "config symbol defined " 322 "without type\n"); 323 324 if (sym_is_choice(sym) && !parent->prompt) 325 menu_warn(parent, "choice must have a prompt\n"); 326 327 /* Check properties connected to this symbol */ 328 sym_check_prop(sym); 329 sym->flags |= SYMBOL_WARNED; 330 } 331 332 if (sym && !sym_is_optional(sym) && parent->prompt) { 333 sym->rev_dep.expr = expr_alloc_or(sym->rev_dep.expr, 334 expr_alloc_and(parent->prompt->visible.expr, 335 expr_alloc_symbol(&symbol_mod))); 336 } 337 } 338 339 bool menu_is_visible(struct menu *menu) 340 { 341 struct menu *child; 342 struct symbol *sym; 343 tristate visible; 344 345 if (!menu->prompt) 346 return false; 347 sym = menu->sym; 348 if (sym) { 349 sym_calc_value(sym); 350 visible = menu->prompt->visible.tri; 351 } else 352 visible = menu->prompt->visible.tri = expr_calc_value(menu->prompt->visible.expr); 353 354 if (visible != no) 355 return true; 356 if (!sym || sym_get_tristate_value(menu->sym) == no) 357 return false; 358 359 for (child = menu->list; child; child = child->next) 360 if (menu_is_visible(child)) 361 return true; 362 return false; 363 } 364 365 const char *menu_get_prompt(struct menu *menu) 366 { 367 if (menu->prompt) 368 return menu->prompt->text; 369 else if (menu->sym) 370 return menu->sym->name; 371 return NULL; 372 } 373 374 struct menu *menu_get_root_menu(struct menu *menu) 375 { 376 return &rootmenu; 377 } 378 379 struct menu *menu_get_parent_menu(struct menu *menu) 380 { 381 enum prop_type type; 382 383 for (; menu != &rootmenu; menu = menu->parent) { 384 type = menu->prompt ? menu->prompt->type : 0; 385 if (type == P_MENU) 386 break; 387 } 388 return menu; 389 } 390 391