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/confdata.c b/scripts/config/confdata.c
new file mode 100644
index 0000000..44835bb
--- /dev/null
+++ b/scripts/config/confdata.c
@@ -0,0 +1,371 @@
+/*
+ * Copyright (C) 2002 Roman Zippel <zippel@linux-m68k.org>
+ * Released under the terms of the GNU GPL v2.0.
+ *
+ * Allow 'n' as a symbol value.
+ * 2002-11-05 Petr Baudis <pasky@ucw.cz>
+ */
+
+#include <ctype.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#define LKC_DIRECT_LINK
+#include "lkc.h"
+
+const char conf_def_filename[] = ".config";
+
+const char conf_defname[] = "extra/Configs/Config.$TARGET_ARCH.default";
+
+const char *conf_confnames[] = {
+	".config",
+	conf_defname,
+	NULL,
+};
+
+static char *conf_expand_value(const char *in)
+{
+	struct symbol *sym;
+	const char *src;
+	static char res_value[SYMBOL_MAXLENGTH];
+	char *dst, name[SYMBOL_MAXLENGTH];
+
+	res_value[0] = 0;
+	dst = name;
+	while ((src = strchr(in, '$'))) {
+		strncat(res_value, in, src - in);
+		src++;
+		dst = name;
+		while (isalnum((int)*src) || *src == '_')
+			*dst++ = *src++;
+		*dst = 0;
+		sym = sym_lookup(name, 0);
+		sym_calc_value(sym);
+		strcat(res_value, sym_get_string_value(sym));
+		in = src;
+	}
+	strcat(res_value, in);
+
+	return res_value;
+}
+
+char *conf_get_default_confname(void)
+{
+	return conf_expand_value(conf_defname);
+}
+
+int conf_read(const char *name)
+{
+	FILE *in = NULL;
+	char line[1024];
+	char *p, *p2;
+	int lineno = 0;
+	struct symbol *sym;
+	struct property *prop;
+	struct expr *e;
+	int i;
+
+	if (name) {
+		in = fopen(name, "r");
+	} else {
+		const char **names = conf_confnames;
+		while ((name = *names++)) {
+			name = conf_expand_value(name);
+			in = fopen(name, "r");
+			if (in) {
+				printf("#\n"
+				       "# using defaults found in %s\n"
+				       "#\n", name);
+				break;
+			}
+		}
+	}
+
+	if (!in)
+		return 1;
+
+	for_all_symbols(i, sym) {
+		sym->flags |= SYMBOL_NEW | SYMBOL_CHANGED;
+		sym->flags &= ~SYMBOL_VALID;
+		switch (sym->type) {
+		case S_INT:
+		case S_HEX:
+		case S_STRING:
+			if (S_VAL(sym->def))
+				free(S_VAL(sym->def));
+		default:
+			S_VAL(sym->def) = NULL;
+			S_TRI(sym->def) = no;
+			;
+		}
+	}
+
+	while (fgets(line, sizeof(line), in)) {
+		lineno++;
+		switch (line[0]) {
+		case '\n':
+			break;
+		case ' ':
+			break;
+		case '#':
+			p = strchr(line, ' ');
+			if (!p)
+				continue;
+			*p++ = 0;
+			p = strchr(p, ' ');
+			if (!p)
+				continue;
+			*p++ = 0;
+			if (strncmp(p, "is not set", 10))
+				continue;
+			sym = sym_lookup(line+2, 0);
+			switch (sym->type) {
+			case S_BOOLEAN:
+			case S_TRISTATE:
+				sym->def = symbol_no.curr;
+				sym->flags &= ~SYMBOL_NEW;
+				break;
+			default:
+				;
+			}
+			break;
+		case 'A' ... 'Z':
+			p = strchr(line, '=');
+			if (!p)
+				continue;
+			*p++ = 0;
+			p2 = strchr(p, '\n');
+			if (p2)
+				*p2 = 0;
+			sym = sym_find(line);
+			if (!sym) {
+				fprintf(stderr, "%s:%d: trying to assign nonexistent symbol %s\n", name, lineno, line);
+				break;
+			}
+			switch (sym->type) {
+  			case S_TRISTATE:
+				if (p[0] == 'm') {
+					S_TRI(sym->def) = mod;
+					sym->flags &= ~SYMBOL_NEW;
+					break;
+				}
+			case S_BOOLEAN:
+				if (p[0] == 'y') {
+					S_TRI(sym->def) = yes;
+					sym->flags &= ~SYMBOL_NEW;
+					break;
+				}
+				if (p[0] == 'n') {
+					S_TRI(sym->def) = no;
+					sym->flags &= ~SYMBOL_NEW;
+					break;
+				}
+  				break;
+			case S_STRING:
+				if (*p++ != '"')
+					break;
+				for (p2 = p; (p2 = strpbrk(p2, "\"\\")); p2++) {
+					if (*p2 == '"') {
+						*p2 = 0;
+						break;
+					}
+					memmove(p2, p2 + 1, strlen(p2));
+				}
+			case S_INT:
+			case S_HEX:
+				if (sym_string_valid(sym, p)) {
+					S_VAL(sym->def) = strdup(p);
+					sym->flags &= ~SYMBOL_NEW;
+				} else
+					fprintf(stderr, "%s:%d:symbol value '%s' invalid for %s\n", name, lineno, p, sym->name);
+				break;
+			default:
+				;
+			}
+			if (sym_is_choice_value(sym)) {
+				prop = sym_get_choice_prop(sym);
+				switch (S_TRI(sym->def)) {
+				case mod:
+					if (S_TRI(prop->def->def) == yes)
+						/* warn? */;
+					break;
+				case yes:
+					if (S_TRI(prop->def->def) != no)
+						/* warn? */;
+					S_VAL(prop->def->def) = sym;
+					break;
+				case no:
+					break;
+				}
+				S_TRI(prop->def->def) = S_TRI(sym->def);
+			}
+			break;
+		default:
+			continue;
+		}
+	}
+	fclose(in);
+
+	for_all_symbols(i, sym) {
+		if (!sym_is_choice(sym))
+			continue;
+		prop = sym_get_choice_prop(sym);
+		for (e = prop->dep; e; e = e->left.expr)
+			sym->flags |= e->right.sym->flags & SYMBOL_NEW;
+		sym->flags &= ~SYMBOL_NEW;
+	}
+
+	sym_change_count = 1;
+
+	return 0;
+}
+
+int conf_write(const char *name)
+{
+	FILE *out, *out_h;
+	struct symbol *sym;
+	struct menu *menu;
+	char oldname[128];
+	int type, l;
+	const char *str;
+
+	out = fopen(".tmpconfig", "w");
+	if (!out)
+		return 1;
+	out_h = fopen(".tmpconfig.h", "w");
+	if (!out_h)
+		return 1;
+	fprintf(out, "#\n"
+		     "# Automatically generated make config: don't edit\n"
+		     "#\n");
+	fprintf(out_h, "/*\n"
+		       " * Automatically generated header file: don't edit\n"
+		       " */\n\n"
+		       "#define AUTOCONF_INCLUDED\n\n"
+		       "/* Version Number */\n"
+		       "#define BB_VER \"%s\"\n"
+		       "#define BB_BT \"%s\"\n\n",
+		       getenv("VERSION"),
+		       getenv("BUILDTIME")
+       );
+
+	if (!sym_change_count)
+		sym_clear_all_valid();
+
+	menu = rootmenu.list;
+	while (menu) {
+		sym = menu->sym;
+		if (!sym) {
+			if (!menu_is_visible(menu))
+				goto next;
+			str = menu_get_prompt(menu);
+			fprintf(out, "\n"
+				     "#\n"
+				     "# %s\n"
+				     "#\n", str);
+			fprintf(out_h, "\n"
+				       "/*\n"
+				       " * %s\n"
+				       " */\n", str);
+		} else if (!(sym->flags & SYMBOL_CHOICE)) {
+			sym_calc_value(sym);
+			if (!(sym->flags & SYMBOL_WRITE))
+				goto next;
+			sym->flags &= ~SYMBOL_WRITE;
+			type = sym->type;
+			if (type == S_TRISTATE) {
+				sym_calc_value(modules_sym);
+				if (S_TRI(modules_sym->curr) == no)
+					type = S_BOOLEAN;
+			}
+			switch (type) {
+			case S_BOOLEAN:
+			case S_TRISTATE:
+				switch (sym_get_tristate_value(sym)) {
+				case no:
+					fprintf(out, "# %s is not set\n", sym->name);
+					fprintf(out_h, "#undef %s\n", sym->name);
+					break;
+				case mod:
+#if 0
+					fprintf(out, "%s=m\n", sym->name);
+					fprintf(out_h, "#define __%s__MODULE 1\n", sym->name);
+#endif
+					break;
+				case yes:
+					fprintf(out, "%s=y\n", sym->name);
+					fprintf(out_h, "#define %s 1\n", sym->name);
+					break;
+				}
+				break;
+			case S_STRING:
+				// fix me
+				str = sym_get_string_value(sym);
+				fprintf(out, "%s=\"", sym->name);
+				fprintf(out_h, "#define %s \"", sym->name);
+				do {
+					l = strcspn(str, "\"\\");
+					if (l) {
+						fwrite(str, l, 1, out);
+						fwrite(str, l, 1, out_h);
+					}
+					str += l;
+					while (*str == '\\' || *str == '"') {
+						fprintf(out, "\\%c", *str);
+						fprintf(out_h, "\\%c", *str);
+						str++;
+					}
+				} while (*str);
+				fputs("\"\n", out);
+				fputs("\"\n", out_h);
+				break;
+			case S_HEX:
+				str = sym_get_string_value(sym);
+				if (str[0] != '0' || (str[1] != 'x' && str[1] != 'X')) {
+					fprintf(out, "%s=%s\n", sym->name, str);
+					fprintf(out_h, "#define %s 0x%s\n", sym->name, str);
+					break;
+				}
+			case S_INT:
+				str = sym_get_string_value(sym);
+				fprintf(out, "%s=%s\n", sym->name, str);
+				fprintf(out_h, "#define %s %s\n", sym->name, str);
+				break;
+			}
+		}
+
+	next:
+		if (menu->list) {
+			menu = menu->list;
+			continue;
+		}
+		if (menu->next)
+			menu = menu->next;
+		else while ((menu = menu->parent)) {
+			if (menu->next) {
+				menu = menu->next;
+				break;
+			}
+		}
+	}
+	fclose(out);
+	fclose(out_h);
+
+	if (!name) {
+		rename(".tmpconfig.h", "include/config.h");
+		name = conf_def_filename;
+		file_write_dep(NULL);
+	} else
+		unlink(".tmpconfig.h");
+
+	sprintf(oldname, "%s.old", name);
+	rename(name, oldname);
+	if (rename(".tmpconfig", name))
+		return 1;
+
+	sym_change_count = 0;
+
+	return 0;
+}