Пример #1
0
static int expr_eq(struct expr *e1, struct expr *e2)
{
	int res, old_count;

	if (e1->type != e2->type)
		return 0;
	switch (e1->type) {
	case E_EQUAL:
	case E_GEQ:
	case E_GTH:
	case E_LEQ:
	case E_LTH:
	case E_UNEQUAL:
		return e1->left.sym == e2->left.sym && e1->right.sym == e2->right.sym;
	case E_SYMBOL:
		return e1->left.sym == e2->left.sym;
	case E_NOT:
		return expr_eq(e1->left.expr, e2->left.expr);
	case E_AND:
	case E_OR:
		e1 = expr_copy(e1);
		e2 = expr_copy(e2);
		old_count = trans_count;
		expr_eliminate_eq(&e1, &e2);
		res = (e1->type == E_SYMBOL && e2->type == E_SYMBOL &&
		       e1->left.sym == e2->left.sym);
		expr_free(e1);
		expr_free(e2);
		trans_count = old_count;
		return res;
	case E_LIST:
	case E_RANGE:
	case E_NONE:
		/* panic */;
	}

	if (DEBUG_EXPR) {
		expr_fprint(e1, stdout);
		printf(" = ");
		expr_fprint(e2, stdout);
		printf(" ?\n");
	}

	return 0;
}
Пример #2
0
int
expr_eq(struct expr *e1, struct expr *e2)
{
    int res, old_count;

    if (e1->type != e2->type)
	return 0;
    switch (e1->type) {
    case E_EQUAL:
    case E_UNEQUAL:
	return e1->left.sym == e2->left.sym && e1->right.sym == e2->right.sym;
    case E_SYMBOL:
	return e1->left.sym == e2->left.sym;
    case E_NOT:
	return expr_eq(e1->left.expr, e2->left.expr);
    case E_AND:
    case E_OR:
	e1 = expr_copy(e1);
	e2 = expr_copy(e2);
	old_count = trans_count;
	expr_eliminate_eq(&e1, &e2);
	res = (e1->type == E_SYMBOL && e2->type == E_SYMBOL &&
	       e1->left.sym == e2->left.sym);
	expr_free(e1);
	expr_free(e2);
	trans_count = old_count;
	return res;
    case E_CHOICE:
    case E_NONE:
	/* panic */ ;
    }

    print_expr(0, e1, 0);
    printf(" = ");
    print_expr(0, e2, 0);
    printf(" ?\n");

    return 0;
}
Пример #3
0
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)));
	}
}
Пример #4
0
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)));
    }
}
Пример #5
0
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;
	}
    }
}
Пример #6
0
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)));
	}
}