1/*
2 * Copyright (C) 2002 Roman Zippel <zippel@linux-m68k.org>
3 * Released under the terms of the GNU GPL v2.0.
4 */
5
6#include <ctype.h>
7#include <stdlib.h>
8#include <stdio.h>
9#include <string.h>
10#include <unistd.h>
11#include <time.h>
12#include <sys/stat.h>
13
14#define LKC_DIRECT_LINK
15#include "lkc.h"
16
17static void conf(struct menu *menu);
18static void check_conf(struct menu *menu);
19
20enum {
21	ask_all,
22	ask_new,
23	ask_silent,
24	set_default,
25	set_yes,
26	set_mod,
27	set_no,
28	set_random
29} input_mode = ask_all;
30char *defconfig_file;
31
32static int indent = 1;
33static int valid_stdin = 1;
34static int conf_cnt;
35static char line[128];
36static struct menu *rootEntry;
37
38static char nohelp_text[] = N_("Sorry, no help available for this option yet.\n");
39
40static void strip(char *str)
41{
42	char *p = str;
43	int l;
44
45	while ((isspace(*p)))
46		p++;
47	l = strlen(p);
48	if (p != str)
49		memmove(str, p, l + 1);
50	if (!l)
51		return;
52	p = str + l - 1;
53	while ((isspace(*p)))
54		*p-- = 0;
55}
56
57static void check_stdin(void)
58{
59	if (!valid_stdin && input_mode == ask_silent) {
60		printf(_("aborted!\n\n"));
61		printf(_("Console input/output is redirected. "));
62		printf(_("Run 'make oldconfig' to update configuration.\n\n"));
63		exit(1);
64	}
65}
66
67static void conf_askvalue(struct symbol *sym, const char *def)
68{
69	enum symbol_type type = sym_get_type(sym);
70	tristate val;
71
72	if (!sym_has_value(sym))
73		printf("(NEW) ");
74
75	line[0] = '\n';
76	line[1] = 0;
77
78	if (!sym_is_changable(sym)) {
79		printf("%s\n", def);
80		line[0] = '\n';
81		line[1] = 0;
82		return;
83	}
84
85	switch (input_mode) {
86	case set_no:
87	case set_mod:
88	case set_yes:
89	case set_random:
90		if (sym_has_value(sym)) {
91			printf("%s\n", def);
92			return;
93		}
94		break;
95	case ask_new:
96	case ask_silent:
97		if (sym_has_value(sym)) {
98			printf("%s\n", def);
99			return;
100		}
101		check_stdin();
102	case ask_all:
103		fflush(stdout);
104		fgets(line, 128, stdin);
105		return;
106	case set_default:
107		printf("%s\n", def);
108		return;
109	default:
110		break;
111	}
112
113	switch (type) {
114	case S_INT:
115	case S_HEX:
116	case S_STRING:
117		printf("%s\n", def);
118		return;
119	default:
120		;
121	}
122	switch (input_mode) {
123	case set_yes:
124		if (sym_tristate_within_range(sym, yes)) {
125			line[0] = 'y';
126			line[1] = '\n';
127			line[2] = 0;
128			break;
129		}
130	case set_mod:
131		if (type == S_TRISTATE) {
132			if (sym_tristate_within_range(sym, mod)) {
133				line[0] = 'm';
134				line[1] = '\n';
135				line[2] = 0;
136				break;
137			}
138		} else {
139			if (sym_tristate_within_range(sym, yes)) {
140				line[0] = 'y';
141				line[1] = '\n';
142				line[2] = 0;
143				break;
144			}
145		}
146	case set_no:
147		if (sym_tristate_within_range(sym, no)) {
148			line[0] = 'n';
149			line[1] = '\n';
150			line[2] = 0;
151			break;
152		}
153	case set_random:
154		do {
155			val = (tristate)(random() % 3);
156		} while (!sym_tristate_within_range(sym, val));
157		switch (val) {
158		case no: line[0] = 'n'; break;
159		case mod: line[0] = 'm'; break;
160		case yes: line[0] = 'y'; break;
161		}
162		line[1] = '\n';
163		line[2] = 0;
164		break;
165	default:
166		break;
167	}
168	printf("%s", line);
169}
170
171int conf_string(struct menu *menu)
172{
173	struct symbol *sym = menu->sym;
174	const char *def, *help;
175
176	while (1) {
177		printf("%*s%s ", indent - 1, "", menu->prompt->text);
178		printf("(%s) ", sym->name);
179		def = sym_get_string_value(sym);
180		if (sym_get_string_value(sym))
181			printf("[%s] ", def);
182		conf_askvalue(sym, def);
183		switch (line[0]) {
184		case '\n':
185			break;
186		case '?':
187			/* print help */
188			if (line[1] == '\n') {
189				help = nohelp_text;
190				if (menu->sym->help)
191					help = menu->sym->help;
192				printf("\n%s\n", menu->sym->help);
193				def = NULL;
194				break;
195			}
196		default:
197			line[strlen(line)-1] = 0;
198			def = line;
199		}
200		if (def && sym_set_string_value(sym, def))
201			return 0;
202	}
203}
204
205static int conf_sym(struct menu *menu)
206{
207	struct symbol *sym = menu->sym;
208	int type;
209	tristate oldval, newval;
210	const char *help;
211
212	while (1) {
213		printf("%*s%s ", indent - 1, "", menu->prompt->text);
214		if (sym->name)
215			printf("(%s) ", sym->name);
216		type = sym_get_type(sym);
217		putchar('[');
218		oldval = sym_get_tristate_value(sym);
219		switch (oldval) {
220		case no:
221			putchar('N');
222			break;
223		case mod:
224			putchar('M');
225			break;
226		case yes:
227			putchar('Y');
228			break;
229		}
230		if (oldval != no && sym_tristate_within_range(sym, no))
231			printf("/n");
232		if (oldval != mod && sym_tristate_within_range(sym, mod))
233			printf("/m");
234		if (oldval != yes && sym_tristate_within_range(sym, yes))
235			printf("/y");
236		if (sym->help)
237			printf("/?");
238		printf("] ");
239		conf_askvalue(sym, sym_get_string_value(sym));
240		strip(line);
241
242		switch (line[0]) {
243		case 'n':
244		case 'N':
245			newval = no;
246			if (!line[1] || !strcmp(&line[1], "o"))
247				break;
248			continue;
249		case 'm':
250		case 'M':
251			newval = mod;
252			if (!line[1])
253				break;
254			continue;
255		case 'y':
256		case 'Y':
257			newval = yes;
258			if (!line[1] || !strcmp(&line[1], "es"))
259				break;
260			continue;
261		case 0:
262			newval = oldval;
263			break;
264		case '?':
265			goto help;
266		default:
267			continue;
268		}
269		if (sym_set_tristate_value(sym, newval))
270			return 0;
271help:
272		help = nohelp_text;
273		if (sym->help)
274			help = sym->help;
275		printf("\n%s\n", help);
276	}
277}
278
279static int conf_choice(struct menu *menu)
280{
281	struct symbol *sym, *def_sym;
282	struct menu *child;
283	int type;
284	bool is_new;
285
286	sym = menu->sym;
287	type = sym_get_type(sym);
288	is_new = !sym_has_value(sym);
289	if (sym_is_changable(sym)) {
290		conf_sym(menu);
291		sym_calc_value(sym);
292		switch (sym_get_tristate_value(sym)) {
293		case no:
294			return 1;
295		case mod:
296			return 0;
297		case yes:
298			break;
299		}
300	} else {
301		switch (sym_get_tristate_value(sym)) {
302		case no:
303			return 1;
304		case mod:
305			printf("%*s%s\n", indent - 1, "", menu_get_prompt(menu));
306			return 0;
307		case yes:
308			break;
309		}
310	}
311
312	while (1) {
313		int cnt, def;
314
315		printf("%*s%s\n", indent - 1, "", menu_get_prompt(menu));
316		def_sym = sym_get_choice_value(sym);
317		cnt = def = 0;
318		line[0] = 0;
319		for (child = menu->list; child; child = child->next) {
320			if (!menu_is_visible(child))
321				continue;
322			if (!child->sym) {
323				printf("%*c %s\n", indent, '*', menu_get_prompt(child));
324				continue;
325			}
326			cnt++;
327			if (child->sym == def_sym) {
328				def = cnt;
329				printf("%*c", indent, '>');
330			} else
331				printf("%*c", indent, ' ');
332			printf(" %d. %s", cnt, menu_get_prompt(child));
333			if (child->sym->name)
334				printf(" (%s)", child->sym->name);
335			if (!sym_has_value(child->sym))
336				printf(" (NEW)");
337			printf("\n");
338		}
339		printf("%*schoice", indent - 1, "");
340		if (cnt == 1) {
341			printf("[1]: 1\n");
342			goto conf_childs;
343		}
344		printf("[1-%d", cnt);
345		if (sym->help)
346			printf("?");
347		printf("]: ");
348		switch (input_mode) {
349		case ask_new:
350		case ask_silent:
351			if (!is_new) {
352				cnt = def;
353				printf("%d\n", cnt);
354				break;
355			}
356			check_stdin();
357		case ask_all:
358			fflush(stdout);
359			fgets(line, 128, stdin);
360			strip(line);
361			if (line[0] == '?') {
362				printf("\n%s\n", menu->sym->help ?
363					menu->sym->help : nohelp_text);
364				continue;
365			}
366			if (!line[0])
367				cnt = def;
368			else if (isdigit(line[0]))
369				cnt = atoi(line);
370			else
371				continue;
372			break;
373		case set_random:
374			def = (random() % cnt) + 1;
375		case set_default:
376		case set_yes:
377		case set_mod:
378		case set_no:
379			cnt = def;
380			printf("%d\n", cnt);
381			break;
382		}
383
384	conf_childs:
385		for (child = menu->list; child; child = child->next) {
386			if (!child->sym || !menu_is_visible(child))
387				continue;
388			if (!--cnt)
389				break;
390		}
391		if (!child)
392			continue;
393		if (strlen(line) > 0 && line[strlen(line) - 1] == '?') {
394			printf("\n%s\n", child->sym->help ?
395				child->sym->help : nohelp_text);
396			continue;
397		}
398		sym_set_choice_value(sym, child->sym);
399		if (child->list) {
400			indent += 2;
401			conf(child->list);
402			indent -= 2;
403		}
404		return 1;
405	}
406}
407
408static void conf(struct menu *menu)
409{
410	struct symbol *sym;
411	struct property *prop;
412	struct menu *child;
413
414	if (!menu_is_visible(menu))
415		return;
416
417	sym = menu->sym;
418	prop = menu->prompt;
419	if (prop) {
420		const char *prompt;
421
422		switch (prop->type) {
423		case P_MENU:
424			if (input_mode == ask_silent && rootEntry != menu) {
425				check_conf(menu);
426				return;
427			}
428		case P_COMMENT:
429			prompt = menu_get_prompt(menu);
430			if (prompt)
431				printf("%*c\n%*c %s\n%*c\n",
432					indent, '*',
433					indent, '*', prompt,
434					indent, '*');
435		default:
436			;
437		}
438	}
439
440	if (!sym)
441		goto conf_childs;
442
443	if (sym_is_choice(sym)) {
444		conf_choice(menu);
445		if (sym->curr.tri != mod)
446			return;
447		goto conf_childs;
448	}
449
450	switch (sym->type) {
451	case S_INT:
452	case S_HEX:
453	case S_STRING:
454		conf_string(menu);
455		break;
456	default:
457		conf_sym(menu);
458		break;
459	}
460
461conf_childs:
462	if (sym)
463		indent += 2;
464	for (child = menu->list; child; child = child->next)
465		conf(child);
466	if (sym)
467		indent -= 2;
468}
469
470static void check_conf(struct menu *menu)
471{
472	struct symbol *sym;
473	struct menu *child;
474
475	if (!menu_is_visible(menu))
476		return;
477
478	sym = menu->sym;
479	if (sym && !sym_has_value(sym)) {
480		if (sym_is_changable(sym) ||
481		    (sym_is_choice(sym) && sym_get_tristate_value(sym) == yes)) {
482			if (!conf_cnt++)
483				printf(_("*\n* Restart config...\n*\n"));
484			rootEntry = menu_get_parent_menu(menu);
485			conf(rootEntry);
486		}
487	}
488
489	for (child = menu->list; child; child = child->next)
490		check_conf(child);
491}
492
493int main(int ac, char **av)
494{
495	int i = 1;
496	const char *name;
497	struct stat tmpstat;
498
499	if (ac > i && av[i][0] == '-') {
500		switch (av[i++][1]) {
501		case 'o':
502			input_mode = ask_new;
503			break;
504		case 's':
505			input_mode = ask_silent;
506			valid_stdin = isatty(0) && isatty(1) && isatty(2);
507			break;
508		case 'd':
509			input_mode = set_default;
510			break;
511		case 'D':
512			input_mode = set_default;
513			defconfig_file = av[i++];
514			if (!defconfig_file) {
515				printf(_("%s: No default config file specified\n"),
516					av[0]);
517				exit(1);
518			}
519			break;
520		case 'n':
521			input_mode = set_no;
522			break;
523		case 'm':
524			input_mode = set_mod;
525			break;
526		case 'y':
527			input_mode = set_yes;
528			break;
529		case 'r':
530			input_mode = set_random;
531			srandom(time(NULL));
532			break;
533		case 'h':
534		case '?':
535			fprintf(stderr, "See README for usage info\n");
536			exit(0);
537		}
538	}
539	name = av[i];
540	if (!name) {
541		printf(_("%s: Kconfig file missing\n"), av[0]);
542	}
543	conf_parse(name);
544	//zconfdump(stdout);
545	switch (input_mode) {
546	case set_default:
547		if (!defconfig_file)
548			defconfig_file = conf_get_default_confname();
549		if (conf_read(defconfig_file)) {
550			printf("***\n"
551				"*** Can't find default configuration \"%s\"!\n"
552				"***\n", defconfig_file);
553			exit(1);
554		}
555		break;
556	case ask_silent:
557		if (stat(".config", &tmpstat)) {
558			printf(_("***\n"
559				"*** You have not yet configured busybox!\n"
560				"***\n"
561				"*** Please run some configurator (e.g. \"make oldconfig\" or\n"
562				"*** \"make menuconfig\" or \"make defconfig\").\n"
563				"***\n"));
564			exit(1);
565		}
566	case ask_all:
567	case ask_new:
568		conf_read(NULL);
569		break;
570	case set_no:
571	case set_mod:
572	case set_yes:
573	case set_random:
574		name = getenv("KCONFIG_ALLCONFIG");
575		if (name && !stat(name, &tmpstat)) {
576			conf_read_simple(name);
577			break;
578		}
579		switch (input_mode) {
580		case set_no:	 name = "allno.config"; break;
581		case set_mod:	 name = "allmod.config"; break;
582		case set_yes:	 name = "allyes.config"; break;
583		case set_random: name = "allrandom.config"; break;
584		default: break;
585		}
586		if (!stat(name, &tmpstat))
587			conf_read_simple(name);
588		else if (!stat("all.config", &tmpstat))
589			conf_read_simple("all.config");
590		break;
591	default:
592		break;
593	}
594
595	if (input_mode != ask_silent) {
596		rootEntry = &rootmenu;
597		conf(&rootmenu);
598		if (input_mode == ask_all) {
599			input_mode = ask_silent;
600			valid_stdin = 1;
601		}
602	}
603	do {
604		conf_cnt = 0;
605		check_conf(&rootmenu);
606	} while (conf_cnt);
607	if (conf_write(NULL)) {
608		fprintf(stderr, _("\n*** Error during writing of the busybox configuration.\n\n"));
609		return 1;
610	}
611	return 0;
612}
613