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