mirror of
https://github.com/torvalds/linux.git
synced 2024-11-30 08:01:59 +00:00
204c96f609
Recent changes to oldconfig have mixed up the silentoldconfig handling, so this fixes that by clearly separating that special mode, e.g. KCONFIG_NOSILENTUPDATE is only relevant here, the .config is written as needed. This will also properly close Bug 11230. Signed-off-by: Roman Zippel <zippel@linux-m68k.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
603 lines
11 KiB
C
603 lines
11 KiB
C
/*
|
|
* Copyright (C) 2002 Roman Zippel <zippel@linux-m68k.org>
|
|
* Released under the terms of the GNU GPL v2.0.
|
|
*/
|
|
|
|
#include <locale.h>
|
|
#include <ctype.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <time.h>
|
|
#include <unistd.h>
|
|
#include <sys/stat.h>
|
|
|
|
#define LKC_DIRECT_LINK
|
|
#include "lkc.h"
|
|
|
|
static void conf(struct menu *menu);
|
|
static void check_conf(struct menu *menu);
|
|
|
|
enum {
|
|
ask_all,
|
|
ask_new,
|
|
ask_silent,
|
|
set_default,
|
|
set_yes,
|
|
set_mod,
|
|
set_no,
|
|
set_random
|
|
} input_mode = ask_all;
|
|
char *defconfig_file;
|
|
|
|
static int indent = 1;
|
|
static int valid_stdin = 1;
|
|
static int sync_kconfig;
|
|
static int conf_cnt;
|
|
static char line[128];
|
|
static struct menu *rootEntry;
|
|
|
|
static char nohelp_text[] = N_("Sorry, no help available for this option yet.\n");
|
|
|
|
static const char *get_help(struct menu *menu)
|
|
{
|
|
if (menu_has_help(menu))
|
|
return _(menu_get_help(menu));
|
|
else
|
|
return nohelp_text;
|
|
}
|
|
|
|
static void strip(char *str)
|
|
{
|
|
char *p = str;
|
|
int l;
|
|
|
|
while ((isspace(*p)))
|
|
p++;
|
|
l = strlen(p);
|
|
if (p != str)
|
|
memmove(str, p, l + 1);
|
|
if (!l)
|
|
return;
|
|
p = str + l - 1;
|
|
while ((isspace(*p)))
|
|
*p-- = 0;
|
|
}
|
|
|
|
static void check_stdin(void)
|
|
{
|
|
if (!valid_stdin) {
|
|
printf(_("aborted!\n\n"));
|
|
printf(_("Console input/output is redirected. "));
|
|
printf(_("Run 'make oldconfig' to update configuration.\n\n"));
|
|
exit(1);
|
|
}
|
|
}
|
|
|
|
static int conf_askvalue(struct symbol *sym, const char *def)
|
|
{
|
|
enum symbol_type type = sym_get_type(sym);
|
|
|
|
if (!sym_has_value(sym))
|
|
printf(_("(NEW) "));
|
|
|
|
line[0] = '\n';
|
|
line[1] = 0;
|
|
|
|
if (!sym_is_changable(sym)) {
|
|
printf("%s\n", def);
|
|
line[0] = '\n';
|
|
line[1] = 0;
|
|
return 0;
|
|
}
|
|
|
|
switch (input_mode) {
|
|
case ask_new:
|
|
case ask_silent:
|
|
if (sym_has_value(sym)) {
|
|
printf("%s\n", def);
|
|
return 0;
|
|
}
|
|
check_stdin();
|
|
case ask_all:
|
|
fflush(stdout);
|
|
fgets(line, 128, stdin);
|
|
return 1;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
switch (type) {
|
|
case S_INT:
|
|
case S_HEX:
|
|
case S_STRING:
|
|
printf("%s\n", def);
|
|
return 1;
|
|
default:
|
|
;
|
|
}
|
|
printf("%s", line);
|
|
return 1;
|
|
}
|
|
|
|
int conf_string(struct menu *menu)
|
|
{
|
|
struct symbol *sym = menu->sym;
|
|
const char *def;
|
|
|
|
while (1) {
|
|
printf("%*s%s ", indent - 1, "", _(menu->prompt->text));
|
|
printf("(%s) ", sym->name);
|
|
def = sym_get_string_value(sym);
|
|
if (sym_get_string_value(sym))
|
|
printf("[%s] ", def);
|
|
if (!conf_askvalue(sym, def))
|
|
return 0;
|
|
switch (line[0]) {
|
|
case '\n':
|
|
break;
|
|
case '?':
|
|
/* print help */
|
|
if (line[1] == '\n') {
|
|
printf("\n%s\n", get_help(menu));
|
|
def = NULL;
|
|
break;
|
|
}
|
|
default:
|
|
line[strlen(line)-1] = 0;
|
|
def = line;
|
|
}
|
|
if (def && sym_set_string_value(sym, def))
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
static int conf_sym(struct menu *menu)
|
|
{
|
|
struct symbol *sym = menu->sym;
|
|
int type;
|
|
tristate oldval, newval;
|
|
|
|
while (1) {
|
|
printf("%*s%s ", indent - 1, "", _(menu->prompt->text));
|
|
if (sym->name)
|
|
printf("(%s) ", sym->name);
|
|
type = sym_get_type(sym);
|
|
putchar('[');
|
|
oldval = sym_get_tristate_value(sym);
|
|
switch (oldval) {
|
|
case no:
|
|
putchar('N');
|
|
break;
|
|
case mod:
|
|
putchar('M');
|
|
break;
|
|
case yes:
|
|
putchar('Y');
|
|
break;
|
|
}
|
|
if (oldval != no && sym_tristate_within_range(sym, no))
|
|
printf("/n");
|
|
if (oldval != mod && sym_tristate_within_range(sym, mod))
|
|
printf("/m");
|
|
if (oldval != yes && sym_tristate_within_range(sym, yes))
|
|
printf("/y");
|
|
if (menu_has_help(menu))
|
|
printf("/?");
|
|
printf("] ");
|
|
if (!conf_askvalue(sym, sym_get_string_value(sym)))
|
|
return 0;
|
|
strip(line);
|
|
|
|
switch (line[0]) {
|
|
case 'n':
|
|
case 'N':
|
|
newval = no;
|
|
if (!line[1] || !strcmp(&line[1], "o"))
|
|
break;
|
|
continue;
|
|
case 'm':
|
|
case 'M':
|
|
newval = mod;
|
|
if (!line[1])
|
|
break;
|
|
continue;
|
|
case 'y':
|
|
case 'Y':
|
|
newval = yes;
|
|
if (!line[1] || !strcmp(&line[1], "es"))
|
|
break;
|
|
continue;
|
|
case 0:
|
|
newval = oldval;
|
|
break;
|
|
case '?':
|
|
goto help;
|
|
default:
|
|
continue;
|
|
}
|
|
if (sym_set_tristate_value(sym, newval))
|
|
return 0;
|
|
help:
|
|
printf("\n%s\n", get_help(menu));
|
|
}
|
|
}
|
|
|
|
static int conf_choice(struct menu *menu)
|
|
{
|
|
struct symbol *sym, *def_sym;
|
|
struct menu *child;
|
|
int type;
|
|
bool is_new;
|
|
|
|
sym = menu->sym;
|
|
type = sym_get_type(sym);
|
|
is_new = !sym_has_value(sym);
|
|
if (sym_is_changable(sym)) {
|
|
conf_sym(menu);
|
|
sym_calc_value(sym);
|
|
switch (sym_get_tristate_value(sym)) {
|
|
case no:
|
|
return 1;
|
|
case mod:
|
|
return 0;
|
|
case yes:
|
|
break;
|
|
}
|
|
} else {
|
|
switch (sym_get_tristate_value(sym)) {
|
|
case no:
|
|
return 1;
|
|
case mod:
|
|
printf("%*s%s\n", indent - 1, "", _(menu_get_prompt(menu)));
|
|
return 0;
|
|
case yes:
|
|
break;
|
|
}
|
|
}
|
|
|
|
while (1) {
|
|
int cnt, def;
|
|
|
|
printf("%*s%s\n", indent - 1, "", _(menu_get_prompt(menu)));
|
|
def_sym = sym_get_choice_value(sym);
|
|
cnt = def = 0;
|
|
line[0] = 0;
|
|
for (child = menu->list; child; child = child->next) {
|
|
if (!menu_is_visible(child))
|
|
continue;
|
|
if (!child->sym) {
|
|
printf("%*c %s\n", indent, '*', _(menu_get_prompt(child)));
|
|
continue;
|
|
}
|
|
cnt++;
|
|
if (child->sym == def_sym) {
|
|
def = cnt;
|
|
printf("%*c", indent, '>');
|
|
} else
|
|
printf("%*c", indent, ' ');
|
|
printf(" %d. %s", cnt, _(menu_get_prompt(child)));
|
|
if (child->sym->name)
|
|
printf(" (%s)", child->sym->name);
|
|
if (!sym_has_value(child->sym))
|
|
printf(_(" (NEW)"));
|
|
printf("\n");
|
|
}
|
|
printf(_("%*schoice"), indent - 1, "");
|
|
if (cnt == 1) {
|
|
printf("[1]: 1\n");
|
|
goto conf_childs;
|
|
}
|
|
printf("[1-%d", cnt);
|
|
if (menu_has_help(menu))
|
|
printf("?");
|
|
printf("]: ");
|
|
switch (input_mode) {
|
|
case ask_new:
|
|
case ask_silent:
|
|
if (!is_new) {
|
|
cnt = def;
|
|
printf("%d\n", cnt);
|
|
break;
|
|
}
|
|
check_stdin();
|
|
case ask_all:
|
|
fflush(stdout);
|
|
fgets(line, 128, stdin);
|
|
strip(line);
|
|
if (line[0] == '?') {
|
|
printf("\n%s\n", get_help(menu));
|
|
continue;
|
|
}
|
|
if (!line[0])
|
|
cnt = def;
|
|
else if (isdigit(line[0]))
|
|
cnt = atoi(line);
|
|
else
|
|
continue;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
conf_childs:
|
|
for (child = menu->list; child; child = child->next) {
|
|
if (!child->sym || !menu_is_visible(child))
|
|
continue;
|
|
if (!--cnt)
|
|
break;
|
|
}
|
|
if (!child)
|
|
continue;
|
|
if (line[strlen(line) - 1] == '?') {
|
|
printf("\n%s\n", get_help(child));
|
|
continue;
|
|
}
|
|
sym_set_choice_value(sym, child->sym);
|
|
for (child = child->list; child; child = child->next) {
|
|
indent += 2;
|
|
conf(child);
|
|
indent -= 2;
|
|
}
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
static void conf(struct menu *menu)
|
|
{
|
|
struct symbol *sym;
|
|
struct property *prop;
|
|
struct menu *child;
|
|
|
|
if (!menu_is_visible(menu))
|
|
return;
|
|
|
|
sym = menu->sym;
|
|
prop = menu->prompt;
|
|
if (prop) {
|
|
const char *prompt;
|
|
|
|
switch (prop->type) {
|
|
case P_MENU:
|
|
if (input_mode == ask_silent && rootEntry != menu) {
|
|
check_conf(menu);
|
|
return;
|
|
}
|
|
case P_COMMENT:
|
|
prompt = menu_get_prompt(menu);
|
|
if (prompt)
|
|
printf("%*c\n%*c %s\n%*c\n",
|
|
indent, '*',
|
|
indent, '*', _(prompt),
|
|
indent, '*');
|
|
default:
|
|
;
|
|
}
|
|
}
|
|
|
|
if (!sym)
|
|
goto conf_childs;
|
|
|
|
if (sym_is_choice(sym)) {
|
|
conf_choice(menu);
|
|
if (sym->curr.tri != mod)
|
|
return;
|
|
goto conf_childs;
|
|
}
|
|
|
|
switch (sym->type) {
|
|
case S_INT:
|
|
case S_HEX:
|
|
case S_STRING:
|
|
conf_string(menu);
|
|
break;
|
|
default:
|
|
conf_sym(menu);
|
|
break;
|
|
}
|
|
|
|
conf_childs:
|
|
if (sym)
|
|
indent += 2;
|
|
for (child = menu->list; child; child = child->next)
|
|
conf(child);
|
|
if (sym)
|
|
indent -= 2;
|
|
}
|
|
|
|
static void check_conf(struct menu *menu)
|
|
{
|
|
struct symbol *sym;
|
|
struct menu *child;
|
|
|
|
if (!menu_is_visible(menu))
|
|
return;
|
|
|
|
sym = menu->sym;
|
|
if (sym && !sym_has_value(sym)) {
|
|
if (sym_is_changable(sym) ||
|
|
(sym_is_choice(sym) && sym_get_tristate_value(sym) == yes)) {
|
|
if (!conf_cnt++)
|
|
printf(_("*\n* Restart config...\n*\n"));
|
|
rootEntry = menu_get_parent_menu(menu);
|
|
conf(rootEntry);
|
|
}
|
|
}
|
|
|
|
for (child = menu->list; child; child = child->next)
|
|
check_conf(child);
|
|
}
|
|
|
|
int main(int ac, char **av)
|
|
{
|
|
int opt;
|
|
const char *name;
|
|
struct stat tmpstat;
|
|
|
|
setlocale(LC_ALL, "");
|
|
bindtextdomain(PACKAGE, LOCALEDIR);
|
|
textdomain(PACKAGE);
|
|
|
|
while ((opt = getopt(ac, av, "osdD:nmyrh")) != -1) {
|
|
switch (opt) {
|
|
case 'o':
|
|
input_mode = ask_silent;
|
|
break;
|
|
case 's':
|
|
input_mode = ask_silent;
|
|
sync_kconfig = 1;
|
|
break;
|
|
case 'd':
|
|
input_mode = set_default;
|
|
break;
|
|
case 'D':
|
|
input_mode = set_default;
|
|
defconfig_file = optarg;
|
|
break;
|
|
case 'n':
|
|
input_mode = set_no;
|
|
break;
|
|
case 'm':
|
|
input_mode = set_mod;
|
|
break;
|
|
case 'y':
|
|
input_mode = set_yes;
|
|
break;
|
|
case 'r':
|
|
input_mode = set_random;
|
|
srand(time(NULL));
|
|
break;
|
|
case 'h':
|
|
printf(_("See README for usage info\n"));
|
|
exit(0);
|
|
break;
|
|
default:
|
|
fprintf(stderr, _("See README for usage info\n"));
|
|
exit(1);
|
|
}
|
|
}
|
|
if (ac == optind) {
|
|
printf(_("%s: Kconfig file missing\n"), av[0]);
|
|
exit(1);
|
|
}
|
|
name = av[optind];
|
|
conf_parse(name);
|
|
//zconfdump(stdout);
|
|
if (sync_kconfig) {
|
|
if (stat(".config", &tmpstat)) {
|
|
fprintf(stderr, _("***\n"
|
|
"*** You have not yet configured your kernel!\n"
|
|
"*** (missing kernel .config file)\n"
|
|
"***\n"
|
|
"*** Please run some configurator (e.g. \"make oldconfig\" or\n"
|
|
"*** \"make menuconfig\" or \"make xconfig\").\n"
|
|
"***\n"));
|
|
exit(1);
|
|
}
|
|
}
|
|
|
|
switch (input_mode) {
|
|
case set_default:
|
|
if (!defconfig_file)
|
|
defconfig_file = conf_get_default_confname();
|
|
if (conf_read(defconfig_file)) {
|
|
printf(_("***\n"
|
|
"*** Can't find default configuration \"%s\"!\n"
|
|
"***\n"), defconfig_file);
|
|
exit(1);
|
|
}
|
|
break;
|
|
case ask_silent:
|
|
case ask_all:
|
|
case ask_new:
|
|
conf_read(NULL);
|
|
break;
|
|
case set_no:
|
|
case set_mod:
|
|
case set_yes:
|
|
case set_random:
|
|
name = getenv("KCONFIG_ALLCONFIG");
|
|
if (name && !stat(name, &tmpstat)) {
|
|
conf_read_simple(name, S_DEF_USER);
|
|
break;
|
|
}
|
|
switch (input_mode) {
|
|
case set_no: name = "allno.config"; break;
|
|
case set_mod: name = "allmod.config"; break;
|
|
case set_yes: name = "allyes.config"; break;
|
|
case set_random: name = "allrandom.config"; break;
|
|
default: break;
|
|
}
|
|
if (!stat(name, &tmpstat))
|
|
conf_read_simple(name, S_DEF_USER);
|
|
else if (!stat("all.config", &tmpstat))
|
|
conf_read_simple("all.config", S_DEF_USER);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
if (sync_kconfig) {
|
|
if (conf_get_changed()) {
|
|
name = getenv("KCONFIG_NOSILENTUPDATE");
|
|
if (name && *name) {
|
|
fprintf(stderr,
|
|
_("\n*** Kernel configuration requires explicit update.\n\n"));
|
|
return 1;
|
|
}
|
|
}
|
|
valid_stdin = isatty(0) && isatty(1) && isatty(2);
|
|
}
|
|
|
|
switch (input_mode) {
|
|
case set_no:
|
|
conf_set_all_new_symbols(def_no);
|
|
break;
|
|
case set_yes:
|
|
conf_set_all_new_symbols(def_yes);
|
|
break;
|
|
case set_mod:
|
|
conf_set_all_new_symbols(def_mod);
|
|
break;
|
|
case set_random:
|
|
conf_set_all_new_symbols(def_random);
|
|
break;
|
|
case set_default:
|
|
conf_set_all_new_symbols(def_default);
|
|
break;
|
|
case ask_new:
|
|
case ask_all:
|
|
rootEntry = &rootmenu;
|
|
conf(&rootmenu);
|
|
input_mode = ask_silent;
|
|
/* fall through */
|
|
case ask_silent:
|
|
/* Update until a loop caused no more changes */
|
|
do {
|
|
conf_cnt = 0;
|
|
check_conf(&rootmenu);
|
|
} while (conf_cnt);
|
|
break;
|
|
}
|
|
|
|
if (sync_kconfig) {
|
|
/* silentoldconfig is used during the build so we shall update autoconf.
|
|
* All other commands are only used to generate a config.
|
|
*/
|
|
if (conf_get_changed() && conf_write(NULL)) {
|
|
fprintf(stderr, _("\n*** Error during writing of the kernel configuration.\n\n"));
|
|
exit(1);
|
|
}
|
|
if (conf_write_autoconf()) {
|
|
fprintf(stderr, _("\n*** Error during update of the kernel configuration.\n\n"));
|
|
return 1;
|
|
}
|
|
} else {
|
|
if (conf_write(NULL)) {
|
|
fprintf(stderr, _("\n*** Error during writing of the kernel configuration.\n\n"));
|
|
exit(1);
|
|
}
|
|
}
|
|
return 0;
|
|
}
|