static void expr_eliminate_dups2(enum expr_type type, struct expr **ep1, struct expr **ep2) { #define e1 (*ep1) #define e2 (*ep2) struct expr *tmp, *tmp1, *tmp2; if (e1->type == type) { expr_eliminate_dups2(type, &e1->left.expr, &e2); expr_eliminate_dups2(type, &e1->right.expr, &e2); return; } if (e2->type == type) { expr_eliminate_dups2(type, &e1, &e2->left.expr); expr_eliminate_dups2(type, &e1, &e2->right.expr); } if (e1 == e2) return; switch (e1->type) { case E_OR: expr_eliminate_dups2(e1->type, &e1, &e1); // (FOO || BAR) && (!FOO && !BAR) -> n tmp1 = expr_transform(expr_alloc_one(E_NOT, expr_copy(e1))); tmp2 = expr_copy(e2); tmp = expr_extract_eq_and(&tmp1, &tmp2); if (expr_is_yes(tmp1)) { expr_free(e1); e1 = expr_alloc_symbol(&symbol_no); trans_count++; } expr_free(tmp2); expr_free(tmp1); expr_free(tmp); break; case E_AND: expr_eliminate_dups2(e1->type, &e1, &e1); // (FOO && BAR) || (!FOO || !BAR) -> y tmp1 = expr_transform(expr_alloc_one(E_NOT, expr_copy(e1))); tmp2 = expr_copy(e2); tmp = expr_extract_eq_or(&tmp1, &tmp2); if (expr_is_no(tmp1)) { expr_free(e1); e1 = expr_alloc_symbol(&symbol_yes); trans_count++; } expr_free(tmp2); expr_free(tmp1); expr_free(tmp); break; default: ; } #undef e1 #undef e2 }
void menu_finalize(struct menu *parent) { struct menu *menu, *last_menu; struct symbol *sym; struct property *prop; struct expr *parentdep, *basedep, *dep, *dep2, **ep; sym = parent->sym; if (parent->list) { if (sym && sym_is_choice(sym)) { if (sym->type == S_UNKNOWN) { /* find the first choice value to find out choice type */ current_entry = parent; for (menu = parent->list; menu; menu = menu->next) { if (menu->sym && menu->sym->type != S_UNKNOWN) { menu_set_type(menu->sym->type); break; } } } if (parent->prompt && !expr_is_yes(parent->prompt->visible.expr)) { parent->visibility = expr_alloc_and (parent->visibility, parent->prompt->visible.expr); } /* set the type of the remaining choice values */ for (menu = parent->list; menu; menu = menu->next) { current_entry = menu; if (menu->sym && menu->sym->type == S_UNKNOWN) menu_set_type(sym->type); } parentdep = expr_alloc_symbol(sym); } else if (parent->prompt) parentdep = parent->prompt->visible.expr; else parentdep = parent->dep; for (menu = parent->list; menu; menu = menu->next) { basedep = expr_transform(menu->dep); basedep = expr_alloc_and(expr_copy(parentdep), basedep); basedep = expr_eliminate_dups(basedep); menu->dep = basedep; if (menu->sym) prop = menu->sym->prop; else prop = menu->prompt; for (; prop; prop = prop->next) { if (prop->menu != menu) continue; dep = expr_transform(prop->visible.expr); dep = expr_alloc_and(expr_copy(basedep), dep); dep = expr_eliminate_dups(dep); if (menu->sym && menu->sym->type != S_TRISTATE) dep = expr_trans_bool(dep); prop->visible.expr = dep; if (prop->type == P_SELECT) { struct symbol *es = prop_get_symbol(prop); es->rev_dep.expr = expr_alloc_or(es->rev_dep.expr, expr_alloc_and(expr_alloc_symbol(menu->sym), expr_copy(dep))); } } } for (menu = parent->list; menu; menu = menu->next) menu_finalize(menu); } else if (sym) { basedep = parent->prompt ? parent->prompt->visible.expr : NULL; basedep = expr_trans_compare(basedep, E_UNEQUAL, &symbol_no); basedep = expr_eliminate_dups(expr_transform(basedep)); last_menu = NULL; for (menu = parent->next; menu; menu = menu->next) { dep = menu->prompt ? menu->prompt->visible.expr : menu->dep; if (!expr_contains_symbol(dep, sym)) break; if (expr_depends_symbol(dep, sym)) goto next; dep = expr_trans_compare(dep, E_UNEQUAL, &symbol_no); dep = expr_eliminate_dups(expr_transform(dep)); dep2 = expr_copy(basedep); expr_eliminate_eq(&dep, &dep2); expr_free(dep); if (!expr_is_yes(dep2)) { expr_free(dep2); break; } expr_free(dep2); next: menu_finalize(menu); menu->parent = parent; last_menu = menu; } if (last_menu) { parent->list = parent->next; parent->next = last_menu->next; last_menu->next = NULL; } sym->dir_dep.expr = expr_alloc_or(sym->dir_dep.expr, parent->dep); } for (menu = parent->list; menu; menu = menu->next) { if (sym && sym_is_choice(sym) && menu->sym && !sym_is_choice_value(menu->sym)) { current_entry = menu; menu->sym->flags |= SYMBOL_CHOICEVAL; if (!menu->prompt) menu_warn(menu, "choice value must have a prompt"); for (prop = menu->sym->prop; prop; prop = prop->next) { if (prop->type == P_DEFAULT) prop_warn(prop, "defaults for choice " "values not supported"); if (prop->menu == menu) continue; if (prop->type == P_PROMPT && prop->menu->parent->sym != sym) prop_warn(prop, "choice value used outside its choice group"); } /* Non-tristate choice values of tristate choices must * depend on the choice being set to Y. The choice * values' dependencies were propagated to their * properties above, so the change here must be re- * propagated. */ if (sym->type == S_TRISTATE && menu->sym->type != S_TRISTATE) { basedep = expr_alloc_comp(E_EQUAL, sym, &symbol_yes); menu->dep = expr_alloc_and(basedep, menu->dep); for (prop = menu->sym->prop; prop; prop = prop->next) { if (prop->menu != menu) continue; prop->visible.expr = expr_alloc_and(expr_copy(basedep), prop->visible.expr); } } menu_add_symbol(P_CHOICE, sym, NULL); prop = sym_get_choice_prop(sym); for (ep = &prop->expr; *ep; ep = &(*ep)->left.expr) ; *ep = expr_alloc_one(E_LIST, NULL); (*ep)->right.sym = menu->sym; } if (menu->list && (!menu->prompt || !menu->prompt->text)) { for (last_menu = menu->list; ; last_menu = last_menu->next) { last_menu->parent = parent; if (!last_menu->next) break; } last_menu->next = menu->next; menu->next = menu->list; menu->list = NULL; } } if (sym && !(sym->flags & SYMBOL_WARNED)) { if (sym->type == S_UNKNOWN) menu_warn(parent, "config symbol defined without type"); if (sym_is_choice(sym) && !parent->prompt) menu_warn(parent, "choice must have a prompt"); /* Check properties connected to this symbol */ sym_check_prop(sym); sym->flags |= SYMBOL_WARNED; } if (sym && !sym_is_optional(sym) && parent->prompt) { sym->rev_dep.expr = expr_alloc_or(sym->rev_dep.expr, expr_alloc_and(parent->prompt->visible.expr, expr_alloc_symbol(&symbol_mod))); } }
struct expr *expr_transform(struct expr *e) { struct expr *tmp; if (!e) return NULL; switch (e->type) { case E_EQUAL: case E_UNEQUAL: case E_SYMBOL: case E_LIST: break; default: e->left.expr = expr_transform(e->left.expr); e->right.expr = expr_transform(e->right.expr); } switch (e->type) { case E_EQUAL: if (e->left.sym->type != S_BOOLEAN) break; if (e->right.sym == &symbol_no) { e->type = E_NOT; e->left.expr = expr_alloc_symbol(e->left.sym); e->right.sym = NULL; break; } if (e->right.sym == &symbol_mod) { printf("boolean symbol %s tested for 'm'? test forced to 'n'\n", e->left.sym->name); e->type = E_SYMBOL; e->left.sym = &symbol_no; e->right.sym = NULL; break; } if (e->right.sym == &symbol_yes) { e->type = E_SYMBOL; e->right.sym = NULL; break; } break; case E_UNEQUAL: if (e->left.sym->type != S_BOOLEAN) break; if (e->right.sym == &symbol_no) { e->type = E_SYMBOL; e->right.sym = NULL; break; } if (e->right.sym == &symbol_mod) { printf("boolean symbol %s tested for 'm'? test forced to 'y'\n", e->left.sym->name); e->type = E_SYMBOL; e->left.sym = &symbol_yes; e->right.sym = NULL; break; } if (e->right.sym == &symbol_yes) { e->type = E_NOT; e->left.expr = expr_alloc_symbol(e->left.sym); e->right.sym = NULL; break; } break; case E_NOT: switch (e->left.expr->type) { case E_NOT: // !!a -> a tmp = e->left.expr->left.expr; free(e->left.expr); free(e); e = tmp; e = expr_transform(e); break; case E_EQUAL: case E_UNEQUAL: // !a='x' -> a!='x' tmp = e->left.expr; free(e); e = tmp; e->type = e->type == E_EQUAL ? E_UNEQUAL : E_EQUAL; break; case E_OR: // !(a || b) -> !a && !b tmp = e->left.expr; e->type = E_AND; e->right.expr = expr_alloc_one(E_NOT, tmp->right.expr); tmp->type = E_NOT; tmp->right.expr = NULL; e = expr_transform(e); break; case E_AND: // !(a && b) -> !a || !b tmp = e->left.expr; e->type = E_OR; e->right.expr = expr_alloc_one(E_NOT, tmp->right.expr); tmp->type = E_NOT; tmp->right.expr = NULL; e = expr_transform(e); break; case E_SYMBOL: if (e->left.expr->left.sym == &symbol_yes) { // !'y' -> 'n' tmp = e->left.expr; free(e); e = tmp; e->type = E_SYMBOL; e->left.sym = &symbol_no; break; } if (e->left.expr->left.sym == &symbol_mod) { // !'m' -> 'm' tmp = e->left.expr; free(e); e = tmp; e->type = E_SYMBOL; e->left.sym = &symbol_mod; break; } if (e->left.expr->left.sym == &symbol_no) { // !'n' -> 'y' tmp = e->left.expr; free(e); e = tmp; e->type = E_SYMBOL; e->left.sym = &symbol_yes; break; } break; default: ; } break; default: ; } return e; }
void menu_finalize(struct menu *parent) { struct menu *menu, *last_menu; struct symbol *sym; struct property *prop; struct expr *parentdep, *basedep, *dep, *dep2, **ep; sym = parent->sym; if (parent->list) { if (sym && sym_is_choice(sym)) { /* find the first choice value and find out choice type */ for (menu = parent->list; menu; menu = menu->next) { if (menu->sym) { current_entry = parent; menu_set_type(menu->sym->type); current_entry = menu; menu_set_type(sym->type); break; } } parentdep = expr_alloc_symbol(sym); } else if (parent->prompt) { parentdep = parent->prompt->visible.expr; } else { parentdep = parent->dep; } for (menu = parent->list; menu; menu = menu->next) { basedep = expr_transform(menu->dep); basedep = expr_alloc_and(expr_copy(parentdep), basedep); basedep = expr_eliminate_dups(basedep); menu->dep = basedep; if (menu->sym) { prop = menu->sym->prop; } else { prop = menu->prompt; } for (; prop; prop = prop->next) { if (prop->menu != menu) { continue; } dep = expr_transform(prop->visible.expr); dep = expr_alloc_and(expr_copy(basedep), dep); dep = expr_eliminate_dups(dep); if (menu->sym && menu->sym->type != S_TRISTATE) { dep = expr_trans_bool(dep); } prop->visible.expr = dep; if (prop->type == P_SELECT) { struct symbol *es = prop_get_symbol(prop); es->rev_dep.expr = expr_alloc_or(es->rev_dep.expr, expr_alloc_and(expr_alloc_symbol(menu->sym), expr_copy(dep))); } } } for (menu = parent->list; menu; menu = menu->next) { menu_finalize(menu); } } else if (sym) { basedep = parent->prompt ? parent->prompt->visible.expr : NULL; basedep = expr_trans_compare(basedep, E_UNEQUAL, &symbol_no); basedep = expr_eliminate_dups(expr_transform(basedep)); last_menu = NULL; for (menu = parent->next; menu; menu = menu->next) { dep = menu->prompt ? menu->prompt->visible.expr : menu->dep; if (!expr_contains_symbol(dep, sym)) { break; } if (expr_depends_symbol(dep, sym)) { goto next; } dep = expr_trans_compare(dep, E_UNEQUAL, &symbol_no); dep = expr_eliminate_dups(expr_transform(dep)); dep2 = expr_copy(basedep); expr_eliminate_eq(&dep, &dep2); expr_free(dep); if (!expr_is_yes(dep2)) { expr_free(dep2); break; } expr_free(dep2); next: menu_finalize(menu); menu->parent = parent; last_menu = menu; } if (last_menu) { parent->list = parent->next; parent->next = last_menu->next; last_menu->next = NULL; } } for (menu = parent->list; menu; menu = menu->next) { if (sym && sym_is_choice(sym) && menu->sym) { menu->sym->flags |= SYMBOL_CHOICEVAL; if (!menu->prompt) { menu_warn(menu, "choice value must have a prompt"); } for (prop = menu->sym->prop; prop; prop = prop->next) { if (prop->type == P_PROMPT && prop->menu != menu) { prop_warn(prop, "choice values " "currently only support a " "single prompt"); } if (prop->type == P_DEFAULT) prop_warn(prop, "defaults for choice " "values not supported"); } current_entry = menu; menu_set_type(sym->type); menu_add_symbol(P_CHOICE, sym, NULL); prop = sym_get_choice_prop(sym); for (ep = &prop->expr; *ep; ep = &(*ep)->left.expr) ; *ep = expr_alloc_one(E_CHOICE, NULL); (*ep)->right.sym = menu->sym; } if (menu->list && (!menu->prompt || !menu->prompt->text)) { for (last_menu = menu->list; ; last_menu = last_menu->next) { last_menu->parent = parent; if (!last_menu->next) { break; } } last_menu->next = menu->next; menu->next = menu->list; menu->list = NULL; } } if (sym && !(sym->flags & SYMBOL_WARNED)) { if (sym->type == S_UNKNOWN) menu_warn(parent, "config symbol defined " "without type\n"); if (sym_is_choice(sym) && !parent->prompt) { menu_warn(parent, "choice must have a prompt\n"); } /* Check properties connected to this symbol */ sym_check_prop(sym); sym->flags |= SYMBOL_WARNED; } if (sym && !sym_is_optional(sym) && parent->prompt) { sym->rev_dep.expr = expr_alloc_or(sym->rev_dep.expr, expr_alloc_and(parent->prompt->visible.expr, expr_alloc_symbol(&symbol_mod))); } }
void menu_finalize(struct menu *parent) { struct menu *menu, *last_menu; struct symbol *sym; struct property *prop; struct expr *parentdep, *basedep, *dep, *dep2; sym = parent->sym; if (parent->list) { if (sym && sym_is_choice(sym)) { /* find the first choice value and find out choice type */ for (menu = parent->list; menu; menu = menu->next) { if (menu->sym) { current_entry = parent; menu_set_type(menu->sym->type); current_entry = menu; menu_set_type(sym->type); break; } } parentdep = expr_alloc_symbol(sym); } else if (parent->prompt) parentdep = E_EXPR(parent->prompt->visible); else parentdep = parent->dep; for (menu = parent->list; menu; menu = menu->next) { basedep = expr_transform(menu->dep); basedep = expr_alloc_and(expr_copy(parentdep), basedep); basedep = expr_eliminate_dups(basedep); menu->dep = basedep; if (menu->sym) prop = menu->sym->prop; else prop = menu->prompt; for (; prop; prop = prop->next) { if (prop->menu != menu) continue; dep = expr_transform(E_EXPR(prop->visible)); dep = expr_alloc_and(expr_copy(basedep), dep); dep = expr_eliminate_dups(dep); if (menu->sym && menu->sym->type != S_TRISTATE) dep = expr_trans_bool(dep); E_EXPR(prop->visible) = dep; } } for (menu = parent->list; menu; menu = menu->next) menu_finalize(menu); } else if (sym && parent->prompt) { basedep = E_EXPR(parent->prompt->visible); basedep = expr_trans_compare(basedep, E_UNEQUAL, &symbol_no); basedep = expr_eliminate_dups(expr_transform(basedep)); last_menu = NULL; for (menu = parent->next; menu; menu = menu->next) { dep = menu->prompt ? E_EXPR(menu->prompt->visible) : menu->dep; if (!expr_contains_symbol(dep, sym)) break; if (expr_depends_symbol(dep, sym)) goto next; dep = expr_trans_compare(dep, E_UNEQUAL, &symbol_no); dep = expr_eliminate_dups(expr_transform(dep)); dep2 = expr_copy(basedep); expr_eliminate_eq(&dep, &dep2); expr_free(dep); if (!expr_is_yes(dep2)) { expr_free(dep2); break; } expr_free(dep2); next: menu_finalize(menu); menu->parent = parent; last_menu = menu; } if (last_menu) { parent->list = parent->next; parent->next = last_menu->next; last_menu->next = NULL; } } for (menu = parent->list; menu; menu = menu->next) { if (sym && sym_is_choice(sym) && menu->sym) { menu->sym->flags |= SYMBOL_CHOICEVAL; current_entry = menu; menu_set_type(sym->type); menu_add_prop(P_CHOICE, NULL, parent->sym, NULL); prop = sym_get_choice_prop(parent->sym); //dep = expr_alloc_one(E_CHOICE, dep); //dep->right.sym = menu->sym; prop->dep = expr_alloc_one(E_CHOICE, prop->dep); prop->dep->right.sym = menu->sym; } if (menu->list && (!menu->prompt || !menu->prompt->text)) { for (last_menu = menu->list;; last_menu = last_menu->next) { last_menu->parent = parent; if (!last_menu->next) break; } last_menu->next = menu->next; menu->next = menu->list; menu->list = NULL; } } }
void menu_finalize(struct menu *parent) { struct menu *menu, *last_menu; struct symbol *sym; struct property *prop; struct expr *parentdep, *basedep, *dep, *dep2, **ep; sym = parent->sym; if (parent->list) { /* * This menu node has children. We (recursively) process them * and propagate parent dependencies before moving on. */ if (sym && sym_is_choice(sym)) { if (sym->type == S_UNKNOWN) { /* find the first choice value to find out choice type */ current_entry = parent; for (menu = parent->list; menu; menu = menu->next) { if (menu->sym && menu->sym->type != S_UNKNOWN) { menu_set_type(menu->sym->type); break; } } } /* set the type of the remaining choice values */ for (menu = parent->list; menu; menu = menu->next) { current_entry = menu; if (menu->sym && menu->sym->type == S_UNKNOWN) menu_set_type(sym->type); } /* * Use the choice itself as the parent dependency of * the contained items. This turns the mode of the * choice into an upper bound on the visibility of the * choice value symbols. */ parentdep = expr_alloc_symbol(sym); } else if (parent->prompt) /* Menu node for 'menu' */ parentdep = parent->prompt->visible.expr; else /* Menu node for 'if' */ parentdep = parent->dep; /* For each child menu node... */ for (menu = parent->list; menu; menu = menu->next) { /* * Propagate parent dependencies to the child menu * node, also rewriting and simplifying expressions */ basedep = rewrite_m(menu->dep); basedep = expr_transform(basedep); basedep = expr_alloc_and(expr_copy(parentdep), basedep); basedep = expr_eliminate_dups(basedep); menu->dep = basedep; if (menu->sym) /* * Note: For symbols, all prompts are included * too in the symbol's own property list */ prop = menu->sym->prop; else /* * For non-symbol menu nodes, we just need to * handle the prompt */ prop = menu->prompt; /* For each property... */ for (; prop; prop = prop->next) { if (prop->menu != menu) /* * Two possibilities: * * 1. The property lacks dependencies * and so isn't location-specific, * e.g. an 'option' * * 2. The property belongs to a symbol * defined in multiple locations and * is from some other location. It * will be handled there in that * case. * * Skip the property. */ continue; /* * Propagate parent dependencies to the * property's condition, rewriting and * simplifying expressions at the same time */ dep = rewrite_m(prop->visible.expr); dep = expr_transform(dep); dep = expr_alloc_and(expr_copy(basedep), dep); dep = expr_eliminate_dups(dep); if (menu->sym && menu->sym->type != S_TRISTATE) dep = expr_trans_bool(dep); prop->visible.expr = dep; /* * Handle selects and implies, which modify the * dependencies of the selected/implied symbol */ if (prop->type == P_SELECT) { struct symbol *es = prop_get_symbol(prop); es->rev_dep.expr = expr_alloc_or(es->rev_dep.expr, expr_alloc_and(expr_alloc_symbol(menu->sym), expr_copy(dep))); } else if (prop->type == P_IMPLY) { struct symbol *es = prop_get_symbol(prop); es->implied.expr = expr_alloc_or(es->implied.expr, expr_alloc_and(expr_alloc_symbol(menu->sym), expr_copy(dep))); } } } if (sym && sym_is_choice(sym)) expr_free(parentdep); /* * Recursively process children in the same fashion before * moving on */ for (menu = parent->list; menu; menu = menu->next) menu_finalize(menu); } else if (sym) { /* * Automatic submenu creation. If sym is a symbol and A, B, C, * ... are consecutive items (symbols, menus, ifs, etc.) that * all depend on sym, then the following menu structure is * created: * * sym * +-A * +-B * +-C * ... * * This also works recursively, giving the following structure * if A is a symbol and B depends on A: * * sym * +-A * | +-B * +-C * ... */ basedep = parent->prompt ? parent->prompt->visible.expr : NULL; basedep = expr_trans_compare(basedep, E_UNEQUAL, &symbol_no); basedep = expr_eliminate_dups(expr_transform(basedep)); /* Examine consecutive elements after sym */ last_menu = NULL; for (menu = parent->next; menu; menu = menu->next) { dep = menu->prompt ? menu->prompt->visible.expr : menu->dep; if (!expr_contains_symbol(dep, sym)) /* No dependency, quit */ break; if (expr_depends_symbol(dep, sym)) /* Absolute dependency, put in submenu */ goto next; /* * Also consider it a dependency on sym if our * dependencies contain sym and are a "superset" of * sym's dependencies, e.g. '(sym || Q) && R' when sym * depends on R. * * Note that 'R' might be from an enclosing menu or if, * making this a more common case than it might seem. */ dep = expr_trans_compare(dep, E_UNEQUAL, &symbol_no); dep = expr_eliminate_dups(expr_transform(dep)); dep2 = expr_copy(basedep); expr_eliminate_eq(&dep, &dep2); expr_free(dep); if (!expr_is_yes(dep2)) { /* Not superset, quit */ expr_free(dep2); break; } /* Superset, put in submenu */ expr_free(dep2); next: menu_finalize(menu); menu->parent = parent; last_menu = menu; } expr_free(basedep); if (last_menu) { parent->list = parent->next; parent->next = last_menu->next; last_menu->next = NULL; } sym->dir_dep.expr = expr_alloc_or(sym->dir_dep.expr, parent->dep); } for (menu = parent->list; menu; menu = menu->next) { if (sym && sym_is_choice(sym) && menu->sym && !sym_is_choice_value(menu->sym)) { current_entry = menu; menu->sym->flags |= SYMBOL_CHOICEVAL; if (!menu->prompt) menu_warn(menu, "choice value must have a prompt"); for (prop = menu->sym->prop; prop; prop = prop->next) { if (prop->type == P_DEFAULT) prop_warn(prop, "defaults for choice " "values not supported"); if (prop->menu == menu) continue; if (prop->type == P_PROMPT && prop->menu->parent->sym != sym) prop_warn(prop, "choice value used outside its choice group"); } /* Non-tristate choice values of tristate choices must * depend on the choice being set to Y. The choice * values' dependencies were propagated to their * properties above, so the change here must be re- * propagated. */ if (sym->type == S_TRISTATE && menu->sym->type != S_TRISTATE) { basedep = expr_alloc_comp(E_EQUAL, sym, &symbol_yes); menu->dep = expr_alloc_and(basedep, menu->dep); for (prop = menu->sym->prop; prop; prop = prop->next) { if (prop->menu != menu) continue; prop->visible.expr = expr_alloc_and(expr_copy(basedep), prop->visible.expr); } } menu_add_symbol(P_CHOICE, sym, NULL); prop = sym_get_choice_prop(sym); for (ep = &prop->expr; *ep; ep = &(*ep)->left.expr) ; *ep = expr_alloc_one(E_LIST, NULL); (*ep)->right.sym = menu->sym; } /* * This code serves two purposes: * * (1) Flattening 'if' blocks, which do not specify a submenu * and only add dependencies. * * (Automatic submenu creation might still create a submenu * from an 'if' before this code runs.) * * (2) "Undoing" any automatic submenus created earlier below * promptless symbols. * * Before: * * A * if ... (or promptless symbol) * +-B * +-C * D * * After: * * A * if ... (or promptless symbol) * B * C * D */ if (menu->list && (!menu->prompt || !menu->prompt->text)) { for (last_menu = menu->list; ; last_menu = last_menu->next) { last_menu->parent = parent; if (!last_menu->next) break; } last_menu->next = menu->next; menu->next = menu->list; menu->list = NULL; } } if (sym && !(sym->flags & SYMBOL_WARNED)) { if (sym->type == S_UNKNOWN) menu_warn(parent, "config symbol defined without type"); if (sym_is_choice(sym) && !parent->prompt) menu_warn(parent, "choice must have a prompt"); /* Check properties connected to this symbol */ sym_check_prop(sym); sym->flags |= SYMBOL_WARNED; } /* * For non-optional choices, add a reverse dependency (corresponding to * a select) of '<visibility> && m'. This prevents the user from * setting the choice mode to 'n' when the choice is visible. * * This would also work for non-choice symbols, but only non-optional * choices clear SYMBOL_OPTIONAL as of writing. Choices are implemented * as a type of symbol. */ if (sym && !sym_is_optional(sym) && parent->prompt) { sym->rev_dep.expr = expr_alloc_or(sym->rev_dep.expr, expr_alloc_and(parent->prompt->visible.expr, expr_alloc_symbol(&symbol_mod))); } }