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