Yet another major rework of the BusyBox config system, using the considerably
modified Kbuild system I put into uClibc.  With this, there should be no more
need to modify Rules.mak since I've moved all the interesting options into the
config system.  I think I've got everything updated, but you never know, I may
have made some mistakes, so watch closely.
 -Erik
diff --git a/scripts/config/symbol.c b/scripts/config/symbol.c
new file mode 100644
index 0000000..f2d0015
--- /dev/null
+++ b/scripts/config/symbol.c
@@ -0,0 +1,629 @@
+/*
+ * Copyright (C) 2002 Roman Zippel <zippel@linux-m68k.org>
+ * Released under the terms of the GNU GPL v2.0.
+ */
+
+#include <ctype.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/utsname.h>
+
+#define LKC_DIRECT_LINK
+#include "lkc.h"
+
+struct symbol symbol_yes = {
+	name: "y",
+	curr: { "y", yes },
+	flags: SYMBOL_YES|SYMBOL_VALID,
+}, symbol_mod = {
+	name: "m",
+	curr: { "m", mod },
+	flags: SYMBOL_MOD|SYMBOL_VALID,
+}, symbol_no = {
+	name: "n",
+	curr: { "n", no },
+	flags: SYMBOL_NO|SYMBOL_VALID,
+}, symbol_empty = {
+	name: "",
+	curr: { "", no },
+	flags: SYMBOL_VALID,
+};
+
+int sym_change_count;
+struct symbol *modules_sym;
+
+void sym_add_default(struct symbol *sym, const char *def)
+{
+	struct property *prop = create_prop(P_DEFAULT);
+	struct property **propp;
+
+	prop->sym = sym;
+	prop->def = sym_lookup(def, 1);
+
+	/* append property to the prop list of symbol */
+	if (prop->sym) {
+		for (propp = &prop->sym->prop; *propp; propp = &(*propp)->next)
+			;
+		*propp = prop;
+	}
+}
+
+void sym_init(void)
+{
+	struct symbol *sym;
+	struct utsname uts;
+	char *p;
+	static bool inited = false;
+
+	if (inited)
+		return;
+	inited = true;
+
+	uname(&uts);
+
+#if 0
+	sym = sym_lookup("ARCH", 0);
+	sym->type = S_STRING;
+	sym->flags |= SYMBOL_AUTO;
+	p = getenv("ARCH");
+	if (p)
+		sym_add_default(sym, p);
+#endif
+
+	sym = sym_lookup("VERSION", 0);
+	sym->type = S_STRING;
+	sym->flags |= SYMBOL_AUTO;
+	p = getenv("VERSION");
+	if (p)
+		sym_add_default(sym, p);
+
+#if 0
+	sym = sym_lookup("UNAME_RELEASE", 0);
+	sym->type = S_STRING;
+	sym->flags |= SYMBOL_AUTO;
+	sym_add_default(sym, uts.release);
+#endif
+
+	sym = sym_lookup("TARGET_ARCH", 0);
+	sym->type = S_STRING;
+	sym->flags |= SYMBOL_AUTO;
+	p = getenv("TARGET_ARCH");
+	if (p)
+		sym_add_default(sym, p);
+}
+
+int sym_get_type(struct symbol *sym)
+{
+	int type = sym->type;
+	if (type == S_TRISTATE) {
+		if (sym_is_choice_value(sym) && sym->visible == yes)
+			type = S_BOOLEAN;
+		else {
+			sym_calc_value(modules_sym);
+			if (S_TRI(modules_sym->curr) == no)
+				type = S_BOOLEAN;
+		}
+	}
+	return type;
+}
+
+const char *sym_type_name(int type)
+{
+	switch (type) {
+	case S_BOOLEAN:
+		return "boolean";
+	case S_TRISTATE:
+		return "tristate";
+	case S_INT:
+		return "integer";
+	case S_HEX:
+		return "hex";
+	case S_STRING:
+		return "string";
+	case S_UNKNOWN:
+		return "unknown";
+	}
+	return "???";
+}
+
+struct property *sym_get_choice_prop(struct symbol *sym)
+{
+	struct property *prop;
+
+	for_all_choices(sym, prop)
+		return prop;
+	return NULL;
+}
+
+struct property *sym_get_default_prop(struct symbol *sym)
+{
+	struct property *prop;
+	tristate visible;
+
+	for_all_defaults(sym, prop) {
+		visible = E_CALC(prop->visible);
+		if (visible != no)
+			return prop;
+	}
+	return NULL;
+}
+
+void sym_calc_visibility(struct symbol *sym)
+{
+	struct property *prop;
+	tristate visible, oldvisible;
+
+	/* any prompt visible? */
+	oldvisible = sym->visible;
+	visible = no;
+	for_all_prompts(sym, prop)
+		visible = E_OR(visible, E_CALC(prop->visible));
+	if (oldvisible != visible) {
+		sym->visible = visible;
+		sym->flags |= SYMBOL_CHANGED;
+	}
+}
+
+void sym_calc_value(struct symbol *sym)
+{
+	struct symbol_value newval, oldval;
+	struct property *prop, *def_prop;
+	struct symbol *def_sym;
+	struct expr *e;
+
+	if (sym->flags & SYMBOL_VALID)
+		return;
+
+	oldval = sym->curr;
+
+	switch (sym->type) {
+	case S_INT:
+	case S_HEX:
+	case S_STRING:
+		newval = symbol_empty.curr;
+		break;
+	case S_BOOLEAN:
+	case S_TRISTATE:
+		newval = symbol_no.curr;
+		break;
+	default:
+		S_VAL(newval) = sym->name;
+		S_TRI(newval) = no;
+		if (sym->flags & SYMBOL_CONST) {
+			goto out;
+		}
+		//newval = symbol_empty.curr;
+		// generate warning somewhere here later
+		//S_TRI(newval) = yes;
+		goto out;
+	}
+	sym->flags |= SYMBOL_VALID;
+	if (!sym_is_choice_value(sym))
+		sym->flags &= ~SYMBOL_WRITE;
+
+	sym_calc_visibility(sym);
+
+	/* set default if recursively called */
+	sym->curr = newval;
+
+	if (sym->visible != no) {
+		sym->flags |= SYMBOL_WRITE;
+		if (!sym_has_value(sym)) {
+			if (!sym_is_choice(sym)) {
+				prop = sym_get_default_prop(sym);
+				if (prop) {
+					sym_calc_value(prop->def);
+					newval = prop->def->curr;
+				}
+			}
+		} else
+			newval = sym->def;
+
+		S_TRI(newval) = E_AND(S_TRI(newval), sym->visible);
+		/* if the symbol is visible and not optionial,
+		 * possibly ignore old user choice. */
+		if (!sym_is_optional(sym) && S_TRI(newval) == no)
+			S_TRI(newval) = sym->visible;
+		if (sym_is_choice_value(sym) && sym->visible == yes) {
+			prop = sym_get_choice_prop(sym);
+			S_TRI(newval) = (S_VAL(prop->def->curr) == sym) ? yes : no;
+		}
+	} else {
+		prop = sym_get_default_prop(sym);
+		if (prop) {
+			sym->flags |= SYMBOL_WRITE;
+			sym_calc_value(prop->def);
+			newval = prop->def->curr;
+		}
+	}
+
+	switch (sym_get_type(sym)) {
+	case S_TRISTATE:
+		if (S_TRI(newval) != mod)
+			break;
+		sym_calc_value(modules_sym);
+		if (S_TRI(modules_sym->curr) == no)
+			S_TRI(newval) = yes;
+		break;
+	case S_BOOLEAN:
+		if (S_TRI(newval) == mod)
+			S_TRI(newval) = yes;
+	}
+
+out:
+	sym->curr = newval;
+
+	if (sym_is_choice(sym) && S_TRI(newval) == yes) {
+		def_sym = S_VAL(sym->def);
+		if (def_sym) {
+			sym_calc_visibility(def_sym);
+			if (def_sym->visible == no)
+				def_sym = NULL;
+		}
+		if (!def_sym) {
+			for_all_defaults(sym, def_prop) {
+				if (E_CALC(def_prop->visible) == no)
+					continue;
+				sym_calc_visibility(def_prop->def);
+				if (def_prop->def->visible != no) {
+					def_sym = def_prop->def;
+					break;
+				}
+			}
+		}
+
+		if (!def_sym) {
+			prop = sym_get_choice_prop(sym);
+			for (e = prop->dep; e; e = e->left.expr) {
+				sym_calc_visibility(e->right.sym);
+				if (e->right.sym->visible != no) {
+					def_sym = e->right.sym;
+					break;
+				}
+			}
+		}
+
+		S_VAL(newval) = def_sym;
+	}
+
+	if (memcmp(&oldval, &newval, sizeof(newval)))
+		sym->flags |= SYMBOL_CHANGED;
+	sym->curr = newval;
+
+	if (sym_is_choice(sym)) {
+		int flags = sym->flags & (SYMBOL_CHANGED | SYMBOL_WRITE);
+		prop = sym_get_choice_prop(sym);
+		for (e = prop->dep; e; e = e->left.expr)
+			e->right.sym->flags |= flags;
+	}
+}
+
+void sym_clear_all_valid(void)
+{
+	struct symbol *sym;
+	int i;
+
+	for_all_symbols(i, sym)
+		sym->flags &= ~SYMBOL_VALID;
+	sym_change_count++;
+}
+
+void sym_set_all_changed(void)
+{
+	struct symbol *sym;
+	int i;
+
+	for_all_symbols(i, sym)
+		sym->flags |= SYMBOL_CHANGED;
+}
+
+bool sym_tristate_within_range(struct symbol *sym, tristate val)
+{
+	int type = sym_get_type(sym);
+
+	if (sym->visible == no)
+		return false;
+
+	if (type != S_BOOLEAN && type != S_TRISTATE)
+		return false;
+
+	switch (val) {
+	case no:
+		if (sym_is_choice_value(sym) && sym->visible == yes)
+			return false;
+		return sym_is_optional(sym);
+	case mod:
+		if (sym_is_choice_value(sym) && sym->visible == yes)
+			return false;
+		return type == S_TRISTATE;
+	case yes:
+		return type == S_BOOLEAN || sym->visible == yes;
+	}
+	return false;
+}
+
+bool sym_set_tristate_value(struct symbol *sym, tristate val)
+{
+	tristate oldval = sym_get_tristate_value(sym);
+
+	if (oldval != val && !sym_tristate_within_range(sym, val))
+		return false;
+
+	if (sym->flags & SYMBOL_NEW) {
+		sym->flags &= ~SYMBOL_NEW;
+		sym->flags |= SYMBOL_CHANGED;
+	}
+	if (sym_is_choice_value(sym) && val == yes) {
+		struct property *prop = sym_get_choice_prop(sym);
+
+		S_VAL(prop->def->def) = sym;
+		prop->def->flags &= ~SYMBOL_NEW;
+	}
+
+	S_TRI(sym->def) = val;
+	if (oldval != val) {
+		sym_clear_all_valid();
+		if (sym == modules_sym)
+			sym_set_all_changed();
+	}
+
+	return true;
+}
+
+tristate sym_toggle_tristate_value(struct symbol *sym)
+{
+	tristate oldval, newval;
+
+	oldval = newval = sym_get_tristate_value(sym);
+	do {
+		switch (newval) {
+		case no:
+			newval = mod;
+			break;
+		case mod:
+			newval = yes;
+			break;
+		case yes:
+			newval = no;
+			break;
+		}
+		if (sym_set_tristate_value(sym, newval))
+			break;
+	} while (oldval != newval);
+	return newval;
+}
+
+bool sym_string_valid(struct symbol *sym, const char *str)
+{
+	char ch;
+
+	switch (sym->type) {
+	case S_STRING:
+		return true;
+	case S_INT:
+		ch = *str++;
+		if (ch == '-')
+			ch = *str++;
+		if (!isdigit((int)ch))
+			return false;
+		if (ch == '0' && *str != 0)
+			return false;
+		while ((ch = *str++)) {
+			if (!isdigit((int)ch))
+				return false;
+		}
+		return true;
+	case S_HEX:
+		if (str[0] == '0' && (str[1] == 'x' || str[1] == 'X'))
+			str += 2;
+		ch = *str++;
+		do {
+			if (!isxdigit((int)ch))
+				return false;
+		} while ((ch = *str++));
+		return true;
+	case S_BOOLEAN:
+	case S_TRISTATE:
+		switch (str[0]) {
+		case 'y':
+		case 'Y':
+			return sym_tristate_within_range(sym, yes);
+		case 'm':
+		case 'M':
+			return sym_tristate_within_range(sym, mod);
+		case 'n':
+		case 'N':
+			return sym_tristate_within_range(sym, no);
+		}
+		return false;
+	default:
+		return false;
+	}
+}
+
+bool sym_set_string_value(struct symbol *sym, const char *newval)
+{
+	const char *oldval;
+	char *val;
+	int size;
+
+	switch (sym->type) {
+	case S_BOOLEAN:
+	case S_TRISTATE:
+		switch (newval[0]) {
+		case 'y':
+		case 'Y':
+			return sym_set_tristate_value(sym, yes);
+		case 'm':
+		case 'M':
+			return sym_set_tristate_value(sym, mod);
+		case 'n':
+		case 'N':
+			return sym_set_tristate_value(sym, no);
+		}
+		return false;
+	default:
+		;
+	}
+
+	if (!sym_string_valid(sym, newval))
+		return false;
+
+	if (sym->flags & SYMBOL_NEW) {
+		sym->flags &= ~SYMBOL_NEW;
+		sym->flags |= SYMBOL_CHANGED;
+	}
+
+	oldval = S_VAL(sym->def);
+	size = strlen(newval) + 1;
+	if (sym->type == S_HEX && (newval[0] != '0' || (newval[1] != 'x' && newval[1] != 'X'))) {
+		size += 2;
+		S_VAL(sym->def) = val = malloc(size);
+		*val++ = '0';
+		*val++ = 'x';
+	} else if (!oldval || strcmp(oldval, newval))
+		S_VAL(sym->def) = val = malloc(size);
+	else
+		return true;
+
+	strcpy(val, newval);
+	free((void *)oldval);
+	sym_clear_all_valid();
+
+	return true;
+}
+
+const char *sym_get_string_value(struct symbol *sym)
+{
+	tristate val;
+
+	switch (sym->type) {
+	case S_BOOLEAN:
+	case S_TRISTATE:
+		val = sym_get_tristate_value(sym);
+		switch (val) {
+		case no:
+			return "n";
+		case mod:
+			return "m";
+		case yes:
+			return "y";
+		}
+		break;
+	default:
+		;
+	}
+	return (const char *)S_VAL(sym->curr);
+}
+
+bool sym_is_changable(struct symbol *sym)
+{
+	if (sym->visible == no)
+		return false;
+	/* at least 'n' and 'y'/'m' is selectable */
+	if (sym_is_optional(sym))
+		return true;
+	/* no 'n', so 'y' and 'm' must be selectable */
+	if (sym_get_type(sym) == S_TRISTATE && sym->visible == yes)
+		return true;
+	return false;
+}
+
+struct symbol *sym_lookup(const char *name, int isconst)
+{
+	struct symbol *symbol;
+	const char *ptr;
+	char *new_name;
+	int hash = 0;
+
+	//printf("lookup: %s -> ", name);
+	if (name) {
+		if (name[0] && !name[1]) {
+			switch (name[0]) {
+			case 'y': return &symbol_yes;
+			case 'm': return &symbol_mod;
+			case 'n': return &symbol_no;
+			}
+		}
+		for (ptr = name; *ptr; ptr++)
+			hash += *ptr;
+		hash &= 0xff;
+
+		for (symbol = symbol_hash[hash]; symbol; symbol = symbol->next) {
+			if (!strcmp(symbol->name, name)) {
+				if ((isconst && symbol->flags & SYMBOL_CONST) ||
+				    (!isconst && !(symbol->flags & SYMBOL_CONST))) {
+					//printf("h:%p\n", symbol);
+					return symbol;
+				}
+			}
+		}
+		new_name = strdup(name);
+	} else {
+		new_name = NULL;
+		hash = 256;
+	}
+
+	symbol = malloc(sizeof(*symbol));
+	memset(symbol, 0, sizeof(*symbol));
+	symbol->name = new_name;
+	symbol->type = S_UNKNOWN;
+	symbol->flags = SYMBOL_NEW;
+	if (isconst)
+		symbol->flags |= SYMBOL_CONST;
+
+	symbol->next = symbol_hash[hash];
+	symbol_hash[hash] = symbol;
+
+	//printf("n:%p\n", symbol);
+	return symbol;
+}
+
+struct symbol *sym_find(const char *name)
+{
+	struct symbol *symbol = NULL;
+	const char *ptr;
+	int hash = 0;
+
+	if (!name)
+		return NULL;
+
+	if (name[0] && !name[1]) {
+		switch (name[0]) {
+		case 'y': return &symbol_yes;
+		case 'm': return &symbol_mod;
+		case 'n': return &symbol_no;
+		}
+	}
+	for (ptr = name; *ptr; ptr++)
+		hash += *ptr;
+	hash &= 0xff;
+
+	for (symbol = symbol_hash[hash]; symbol; symbol = symbol->next) {
+		if (!strcmp(symbol->name, name) &&
+		    !(symbol->flags & SYMBOL_CONST))
+				break;
+	}
+
+	return symbol;
+}
+
+const char *prop_get_type_name(enum prop_type type)
+{
+	switch (type) {
+	case P_PROMPT:
+		return "prompt";
+	case P_COMMENT:
+		return "comment";
+	case P_MENU:
+		return "menu";
+	case P_ROOTMENU:
+		return "rootmenu";
+	case P_DEFAULT:
+		return "default";
+	case P_CHOICE:
+		return "choice";
+	default:
+		return "unknown";
+	}
+}