1/*
2 * Copyright (C) 2002 Roman Zippel <zippel@linux-m68k.org>
3 * Released under the terms of the GNU GPL v2.0.
4 *
5 * Introduced single menu mode (show all sub-menus in one large tree).
6 * 2002-11-06 Petr Baudis <pasky@ucw.cz>
7 *
8 * i18n, 2005, Arnaldo Carvalho de Melo <acme@conectiva.com.br>
9 */
10
11#include <sys/ioctl.h>
12#include <sys/wait.h>
13#include <ctype.h>
14#include <errno.h>
15#include <fcntl.h>
16#include <limits.h>
17#include <signal.h>
18#include <stdarg.h>
19#include <stdlib.h>
20#include <string.h>
21#include <termios.h>
22#include <unistd.h>
23#include <locale.h>
24
25#define LKC_DIRECT_LINK
26#include "lkc.h"
27
28static char menu_backtitle[128];
29static const char mconf_readme[] = N_(
30"Overview\n"
31"--------\n"
32"Some features may be built directly into busybox.\n"
33"Some may be made into standalone applets.  Some features\n"
34"may be completely removed altogether.  There are also certain\n"
35"parameters which are not really features, but must be\n"
36"entered in as decimal or hexadecimal numbers or possibly text.\n"
37"\n"
38"Menu items beginning with [*], <M> or [ ] represent features\n"
39"configured to be built in, modularized or removed respectively.\n"
40"Pointed brackets <> represent module capable features.\n"
41"\n"
42"To change any of these features, highlight it with the cursor\n"
43"keys and press <Y> to build it in, <M> to make it a module or\n"
44"<N> to removed it.  You may also press the <Space Bar> to cycle\n"
45"through the available options (ie. Y->N->M->Y).\n"
46"\n"
47"Some additional keyboard hints:\n"
48"\n"
49"Menus\n"
50"----------\n"
51"o  Use the Up/Down arrow keys (cursor keys) to highlight the item\n"
52"   you wish to change or submenu wish to select and press <Enter>.\n"
53"   Submenus are designated by \"--->\".\n"
54"\n"
55"   Shortcut: Press the option's highlighted letter (hotkey).\n"
56"             Pressing a hotkey more than once will sequence\n"
57"             through all visible items which use that hotkey.\n"
58"\n"
59"   You may also use the <PAGE UP> and <PAGE DOWN> keys to scroll\n"
60"   unseen options into view.\n"
61"\n"
62"o  To exit a menu use the cursor keys to highlight the <Exit> button\n"
63"   and press <ENTER>.\n"
64"\n"
65"   Shortcut: Press <ESC><ESC> or <E> or <X> if there is no hotkey\n"
66"             using those letters.  You may press a single <ESC>, but\n"
67"             there is a delayed response which you may find annoying.\n"
68"\n"
69"   Also, the <TAB> and cursor keys will cycle between <Select>,\n"
70"   <Exit> and <Help>\n"
71"\n"
72"o  To get help with an item, use the cursor keys to highlight <Help>\n"
73"   and Press <ENTER>.\n"
74"\n"
75"   Shortcut: Press <H> or <?>.\n"
76"\n"
77"\n"
78"Radiolists  (Choice lists)\n"
79"-----------\n"
80"o  Use the cursor keys to select the option you wish to set and press\n"
81"   <S> or the <SPACE BAR>.\n"
82"\n"
83"   Shortcut: Press the first letter of the option you wish to set then\n"
84"             press <S> or <SPACE BAR>.\n"
85"\n"
86"o  To see available help for the item, use the cursor keys to highlight\n"
87"   <Help> and Press <ENTER>.\n"
88"\n"
89"   Shortcut: Press <H> or <?>.\n"
90"\n"
91"   Also, the <TAB> and cursor keys will cycle between <Select> and\n"
92"   <Help>\n"
93"\n"
94"\n"
95"Data Entry\n"
96"-----------\n"
97"o  Enter the requested information and press <ENTER>\n"
98"   If you are entering hexadecimal values, it is not necessary to\n"
99"   add the '0x' prefix to the entry.\n"
100"\n"
101"o  For help, use the <TAB> or cursor keys to highlight the help option\n"
102"   and press <ENTER>.  You can try <TAB><H> as well.\n"
103"\n"
104"\n"
105"Text Box    (Help Window)\n"
106"--------\n"
107"o  Use the cursor keys to scroll up/down/left/right.  The VI editor\n"
108"   keys h,j,k,l function here as do <SPACE BAR> and <B> for those\n"
109"   who are familiar with less and lynx.\n"
110"\n"
111"o  Press <E>, <X>, <Enter> or <Esc><Esc> to exit.\n"
112"\n"
113"\n"
114"Alternate Configuration Files\n"
115"-----------------------------\n"
116"Menuconfig supports the use of alternate configuration files for\n"
117"those who, for various reasons, find it necessary to switch\n"
118"between different configurations.\n"
119"\n"
120"At the end of the main menu you will find two options.  One is\n"
121"for saving the current configuration to a file of your choosing.\n"
122"The other option is for loading a previously saved alternate\n"
123"configuration.\n"
124"\n"
125"Even if you don't use alternate configuration files, but you\n"
126"find during a Menuconfig session that you have completely messed\n"
127"up your settings, you may use the \"Load Alternate...\" option to\n"
128"restore your previously saved settings from \".config\" without\n"
129"restarting Menuconfig.\n"
130"\n"
131"Other information\n"
132"-----------------\n"
133"If you use Menuconfig in an XTERM window make sure you have your\n"
134"$TERM variable set to point to a xterm definition which supports color.\n"
135"Otherwise, Menuconfig will look rather bad.  Menuconfig will not\n"
136"display correctly in a RXVT window because rxvt displays only one\n"
137"intensity of color, bright.\n"
138"\n"
139"Menuconfig will display larger menus on screens or xterms which are\n"
140"set to display more than the standard 25 row by 80 column geometry.\n"
141"In order for this to work, the \"stty size\" command must be able to\n"
142"display the screen's current row and column geometry.  I STRONGLY\n"
143"RECOMMEND that you make sure you do NOT have the shell variables\n"
144"LINES and COLUMNS exported into your environment.  Some distributions\n"
145"export those variables via /etc/profile.  Some ncurses programs can\n"
146"become confused when those variables (LINES & COLUMNS) don't reflect\n"
147"the true screen size.\n"
148"\n"
149"Optional personality available\n"
150"------------------------------\n"
151"If you prefer to have all of the options listed in a single\n"
152"menu, rather than the default multimenu hierarchy, run the menuconfig\n"
153"with MENUCONFIG_MODE environment variable set to single_menu. Example:\n"
154"\n"
155"make MENUCONFIG_MODE=single_menu menuconfig\n"
156"\n"
157"<Enter> will then unroll the appropriate category, or enfold it if it\n"
158"is already unrolled.\n"
159"\n"
160"Note that this mode can eventually be a little more CPU expensive\n"
161"(especially with a larger number of unrolled categories) than the\n"
162"default mode.\n"),
163menu_instructions[] = N_(
164	"Arrow keys navigate the menu.  "
165	"<Enter> selects submenus --->.  "
166	"Highlighted letters are hotkeys.  "
167	"Pressing <Y> includes, <N> excludes, <M> modularizes features.  "
168	"Press <Esc><Esc> to exit, <?> for Help, </> for Search.  "
169	"Legend: [*] built-in  [ ] excluded  <M> module  < > module capable"),
170radiolist_instructions[] = N_(
171	"Use the arrow keys to navigate this window or "
172	"press the hotkey of the item you wish to select "
173	"followed by the <SPACE BAR>. "
174	"Press <?> for additional information about this option."),
175inputbox_instructions_int[] = N_(
176	"Please enter a decimal value. "
177	"Fractions will not be accepted.  "
178	"Use the <TAB> key to move from the input field to the buttons below it."),
179inputbox_instructions_hex[] = N_(
180	"Please enter a hexadecimal value. "
181	"Use the <TAB> key to move from the input field to the buttons below it."),
182inputbox_instructions_string[] = N_(
183	"Please enter a string value. "
184	"Use the <TAB> key to move from the input field to the buttons below it."),
185setmod_text[] = N_(
186	"This feature depends on another which has been configured as a module.\n"
187	"As a result, this feature will be built as a module."),
188nohelp_text[] = N_(
189	"There is no help available for this option.\n"),
190load_config_text[] = N_(
191	"Enter the name of the configuration file you wish to load.  "
192	"Accept the name shown to restore the configuration you "
193	"last retrieved.  Leave blank to abort."),
194load_config_help[] = N_(
195	"\n"
196	"For various reasons, one may wish to keep several different\n"
197	"configurations available on a single machine.\n"
198	"\n"
199	"If you have saved a previous configuration in a file other than\n"
200	"default, entering the name of the file here will allow you\n"
201	"to modify that configuration.\n"
202	"\n"
203	"If you are uncertain, then you have probably never used alternate\n"
204	"configuration files.  You should therefor leave this blank to abort.\n"),
205save_config_text[] = N_(
206	"Enter a filename to which this configuration should be saved "
207	"as an alternate.  Leave blank to abort."),
208save_config_help[] = N_(
209	"\n"
210	"For various reasons, one may wish to keep different\n"
211	"configurations available on a single machine.\n"
212	"\n"
213	"Entering a file name here will allow you to later retrieve, modify\n"
214	"and use the current configuration as an alternate to whatever\n"
215	"configuration options you have selected at that time.\n"
216	"\n"
217	"If you are uncertain what all this means then you should probably\n"
218	"leave this blank.\n"),
219search_help[] = N_(
220	"\n"
221	"Search for CONFIG_ symbols and display their relations.\n"
222	"Regular expressions are allowed.\n"
223	"Example: search for \"^FOO\"\n"
224	"Result:\n"
225	"-----------------------------------------------------------------\n"
226	"Symbol: FOO [=m]\n"
227	"Prompt: Foo bus is used to drive the bar HW\n"
228	"Defined at drivers/pci/Kconfig:47\n"
229	"Depends on: X86_LOCAL_APIC && X86_IO_APIC || IA64\n"
230	"Location:\n"
231	"  -> Bus options (PCI, PCMCIA, EISA, MCA, ISA)\n"
232	"    -> PCI support (PCI [=y])\n"
233	"      -> PCI access mode (<choice> [=y])\n"
234	"Selects: LIBCRC32\n"
235	"Selected by: BAR\n"
236	"-----------------------------------------------------------------\n"
237	"o The line 'Prompt:' shows the text used in the menu structure for\n"
238	"  this CONFIG_ symbol\n"
239	"o The 'Defined at' line tell at what file / line number the symbol\n"
240	"  is defined\n"
241	"o The 'Depends on:' line tell what symbols needs to be defined for\n"
242	"  this symbol to be visible in the menu (selectable)\n"
243	"o The 'Location:' lines tell where in the menu structure this symbol\n"
244	"  is located\n"
245	"    A location followed by a [=y] indicate that this is a selectable\n"
246	"    menu item - and current value is displayed inside brackets.\n"
247	"o The 'Selects:' line tell what symbol will be automatically\n"
248	"  selected if this symbol is selected (y or m)\n"
249	"o The 'Selected by' line tell what symbol has selected this symbol\n"
250	"\n"
251	"Only relevant lines are shown.\n"
252	"\n\n"
253	"Search examples:\n"
254	"Examples: USB	=> find all CONFIG_ symbols containing USB\n"
255	"          ^USB => find all CONFIG_ symbols starting with USB\n"
256	"          USB$ => find all CONFIG_ symbols ending with USB\n"
257	"\n");
258
259static char buf[4096], *bufptr = buf;
260static char input_buf[4096];
261static const char filename[] = ".config";
262static char *args[1024], **argptr = args;
263static int indent;
264static struct termios ios_org;
265static int rows = 0, cols = 0;
266static struct menu *current_menu;
267static int child_count;
268static int do_resize;
269static int single_menu_mode;
270
271static void conf(struct menu *menu);
272static void conf_choice(struct menu *menu);
273static void conf_string(struct menu *menu);
274static void conf_load(void);
275static void conf_save(void);
276static void show_textbox(const char *title, const char *text, int r, int c);
277static void show_helptext(const char *title, const char *text);
278static void show_help(struct menu *menu);
279static void show_file(const char *filename, const char *title, int r, int c);
280
281static void cprint_init(void);
282static int cprint1(const char *fmt, ...);
283static void cprint_done(void);
284static int cprint(const char *fmt, ...);
285
286static void init_wsize(void)
287{
288	struct winsize ws;
289	char *env;
290
291	if (!ioctl(STDIN_FILENO, TIOCGWINSZ, &ws)) {
292		rows = ws.ws_row;
293		cols = ws.ws_col;
294	}
295
296	if (!rows) {
297		env = getenv("LINES");
298		if (env)
299			rows = atoi(env);
300		if (!rows)
301			rows = 24;
302	}
303	if (!cols) {
304		env = getenv("COLUMNS");
305		if (env)
306			cols = atoi(env);
307		if (!cols)
308			cols = 80;
309	}
310
311	if (rows < 19 || cols < 80) {
312		fprintf(stderr, N_("Your display is too small to run Menuconfig!\n"));
313		fprintf(stderr, N_("It must be at least 19 lines by 80 columns.\n"));
314		exit(1);
315	}
316
317	rows -= 4;
318	cols -= 5;
319}
320
321static void cprint_init(void)
322{
323	bufptr = buf;
324	argptr = args;
325	memset(args, 0, sizeof(args));
326	indent = 0;
327	child_count = 0;
328	cprint("./scripts/kconfig/lxdialog/lxdialog");
329	cprint("--backtitle");
330	cprint(menu_backtitle);
331}
332
333static int cprint1(const char *fmt, ...)
334{
335	va_list ap;
336	int res;
337
338	if (!*argptr)
339		*argptr = bufptr;
340	va_start(ap, fmt);
341	res = vsprintf(bufptr, fmt, ap);
342	va_end(ap);
343	bufptr += res;
344
345	return res;
346}
347
348static void cprint_done(void)
349{
350	*bufptr++ = 0;
351	argptr++;
352}
353
354static int cprint(const char *fmt, ...)
355{
356	va_list ap;
357	int res;
358
359	*argptr++ = bufptr;
360	va_start(ap, fmt);
361	res = vsprintf(bufptr, fmt, ap);
362	va_end(ap);
363	bufptr += res;
364	*bufptr++ = 0;
365
366	return res;
367}
368
369static void get_prompt_str(struct gstr *r, struct property *prop)
370{
371	int i, j;
372	struct menu *submenu[8], *menu;
373
374	str_printf(r, "Prompt: %s\n", prop->text);
375	str_printf(r, "  Defined at %s:%d\n", prop->menu->file->name,
376		prop->menu->lineno);
377	if (!expr_is_yes(prop->visible.expr)) {
378		str_append(r, "  Depends on: ");
379		expr_gstr_print(prop->visible.expr, r);
380		str_append(r, "\n");
381	}
382	menu = prop->menu->parent;
383	for (i = 0; menu != &rootmenu && i < 8; menu = menu->parent)
384		submenu[i++] = menu;
385	if (i > 0) {
386		str_printf(r, "  Location:\n");
387		for (j = 4; --i >= 0; j += 2) {
388			menu = submenu[i];
389			str_printf(r, "%*c-> %s", j, ' ', menu_get_prompt(menu));
390			if (menu->sym) {
391				str_printf(r, " (%s [=%s])", menu->sym->name ?
392					menu->sym->name : "<choice>",
393					sym_get_string_value(menu->sym));
394			}
395			str_append(r, "\n");
396		}
397	}
398}
399
400static void get_symbol_str(struct gstr *r, struct symbol *sym)
401{
402	bool hit;
403	struct property *prop;
404
405	str_printf(r, "Symbol: %s [=%s]\n", sym->name,
406	                               sym_get_string_value(sym));
407	for_all_prompts(sym, prop)
408		get_prompt_str(r, prop);
409	hit = false;
410	for_all_properties(sym, prop, P_SELECT) {
411		if (!hit) {
412			str_append(r, "  Selects: ");
413			hit = true;
414		} else
415			str_printf(r, " && ");
416		expr_gstr_print(prop->expr, r);
417	}
418	if (hit)
419		str_append(r, "\n");
420	if (sym->rev_dep.expr) {
421		str_append(r, "  Selected by: ");
422		expr_gstr_print(sym->rev_dep.expr, r);
423		str_append(r, "\n");
424	}
425	str_append(r, "\n\n");
426}
427
428static struct gstr get_relations_str(struct symbol **sym_arr)
429{
430	struct symbol *sym;
431	struct gstr res = str_new();
432	int i;
433
434	for (i = 0; sym_arr && (sym = sym_arr[i]); i++)
435		get_symbol_str(&res, sym);
436	if (!i)
437		str_append(&res, "No matches found.\n");
438	return res;
439}
440
441pid_t pid;
442
443static void winch_handler(int sig)
444{
445	if (!do_resize) {
446		kill(pid, SIGINT);
447		do_resize = 1;
448	}
449}
450
451static int exec_conf(void)
452{
453	int pipefd[2], stat, size;
454	struct sigaction sa;
455	sigset_t sset, osset;
456
457	sigemptyset(&sset);
458	sigaddset(&sset, SIGINT);
459	sigprocmask(SIG_BLOCK, &sset, &osset);
460
461	signal(SIGINT, SIG_DFL);
462
463	sa.sa_handler = winch_handler;
464	sigemptyset(&sa.sa_mask);
465	sa.sa_flags = SA_RESTART;
466	sigaction(SIGWINCH, &sa, NULL);
467
468	*argptr++ = NULL;
469
470	pipe(pipefd);
471	pid = fork();
472	if (pid == 0) {
473		sigprocmask(SIG_SETMASK, &osset, NULL);
474		dup2(pipefd[1], 2);
475		close(pipefd[0]);
476		close(pipefd[1]);
477		execv(args[0], args);
478		_exit(EXIT_FAILURE);
479	}
480
481	close(pipefd[1]);
482	bufptr = input_buf;
483	while (1) {
484		size = input_buf + sizeof(input_buf) - bufptr;
485		size = read(pipefd[0], bufptr, size);
486		if (size <= 0) {
487			if (size < 0) {
488				if (errno == EINTR || errno == EAGAIN)
489					continue;
490				perror("read");
491			}
492			break;
493		}
494		bufptr += size;
495	}
496	*bufptr++ = 0;
497	close(pipefd[0]);
498	waitpid(pid, &stat, 0);
499
500	if (do_resize) {
501		init_wsize();
502		do_resize = 0;
503		sigprocmask(SIG_SETMASK, &osset, NULL);
504		return -1;
505	}
506	if (WIFSIGNALED(stat)) {
507		printf("\finterrupted(%d)\n", WTERMSIG(stat));
508		exit(1);
509	}
510#if 0
511	printf("\fexit state: %d\nexit data: '%s'\n", WEXITSTATUS(stat), input_buf);
512	sleep(1);
513#endif
514	sigpending(&sset);
515	if (sigismember(&sset, SIGINT)) {
516		printf("\finterrupted\n");
517		exit(1);
518	}
519	sigprocmask(SIG_SETMASK, &osset, NULL);
520
521	return WEXITSTATUS(stat);
522}
523
524static void search_conf(void)
525{
526	struct symbol **sym_arr;
527	int stat;
528	struct gstr res;
529
530again:
531	cprint_init();
532	cprint("--title");
533	cprint(_("Search Configuration Parameter"));
534	cprint("--inputbox");
535	cprint(_("Enter CONFIG_ (sub)string to search for (omit CONFIG_)"));
536	cprint("10");
537	cprint("75");
538	cprint("");
539	stat = exec_conf();
540	if (stat < 0)
541		goto again;
542	switch (stat) {
543	case 0:
544		break;
545	case 1:
546		show_helptext(_("Search Configuration"), search_help);
547		goto again;
548	default:
549		return;
550	}
551
552	sym_arr = sym_re_search(input_buf);
553	res = get_relations_str(sym_arr);
554	free(sym_arr);
555	show_textbox(_("Search Results"), str_get(&res), 0, 0);
556	str_free(&res);
557}
558
559static void build_conf(struct menu *menu)
560{
561	struct symbol *sym;
562	struct property *prop;
563	struct menu *child;
564	int type, tmp, doint = 2;
565	tristate val;
566	char ch;
567
568	if (!menu_is_visible(menu))
569		return;
570
571	sym = menu->sym;
572	prop = menu->prompt;
573	if (!sym) {
574		if (prop && menu != current_menu) {
575			const char *prompt = menu_get_prompt(menu);
576			switch (prop->type) {
577			case P_MENU:
578				child_count++;
579				cprint("m%p", menu);
580
581				if (single_menu_mode) {
582					cprint1("%s%*c%s",
583						menu->data ? "-->" : "++>",
584						indent + 1, ' ', prompt);
585				} else
586					cprint1("   %*c%s  --->", indent + 1, ' ', prompt);
587
588				cprint_done();
589				if (single_menu_mode && menu->data)
590					goto conf_childs;
591				return;
592			default:
593				if (prompt) {
594					child_count++;
595					cprint(":%p", menu);
596					cprint("---%*c%s", indent + 1, ' ', prompt);
597				}
598			}
599		} else
600			doint = 0;
601		goto conf_childs;
602	}
603
604	type = sym_get_type(sym);
605	if (sym_is_choice(sym)) {
606		struct symbol *def_sym = sym_get_choice_value(sym);
607		struct menu *def_menu = NULL;
608
609		child_count++;
610		for (child = menu->list; child; child = child->next) {
611			if (menu_is_visible(child) && child->sym == def_sym)
612				def_menu = child;
613		}
614
615		val = sym_get_tristate_value(sym);
616		if (sym_is_changable(sym)) {
617			cprint("t%p", menu);
618			switch (type) {
619			case S_BOOLEAN:
620				cprint1("[%c]", val == no ? ' ' : '*');
621				break;
622			case S_TRISTATE:
623				switch (val) {
624				case yes: ch = '*'; break;
625				case mod: ch = 'M'; break;
626				default:  ch = ' '; break;
627				}
628				cprint1("<%c>", ch);
629				break;
630			}
631		} else {
632			cprint("%c%p", def_menu ? 't' : ':', menu);
633			cprint1("   ");
634		}
635
636		cprint1("%*c%s", indent + 1, ' ', menu_get_prompt(menu));
637		if (val == yes) {
638			if (def_menu) {
639				cprint1(" (%s)", menu_get_prompt(def_menu));
640				cprint1("  --->");
641				cprint_done();
642				if (def_menu->list) {
643					indent += 2;
644					build_conf(def_menu);
645					indent -= 2;
646				}
647			} else
648				cprint_done();
649			return;
650		}
651		cprint_done();
652	} else {
653		if (menu == current_menu) {
654			cprint(":%p", menu);
655			cprint("---%*c%s", indent + 1, ' ', menu_get_prompt(menu));
656			goto conf_childs;
657		}
658		child_count++;
659		val = sym_get_tristate_value(sym);
660		if (sym_is_choice_value(sym) && val == yes) {
661			cprint(":%p", menu);
662			cprint1("   ");
663		} else {
664			switch (type) {
665			case S_BOOLEAN:
666				cprint("t%p", menu);
667				if (sym_is_changable(sym))
668					cprint1("[%c]", val == no ? ' ' : '*');
669				else
670					cprint1("---");
671				break;
672			case S_TRISTATE:
673				cprint("t%p", menu);
674				switch (val) {
675				case yes: ch = '*'; break;
676				case mod: ch = 'M'; break;
677				default:  ch = ' '; break;
678				}
679				if (sym_is_changable(sym))
680					cprint1("<%c>", ch);
681				else
682					cprint1("---");
683				break;
684			default:
685				cprint("s%p", menu);
686				tmp = cprint1("(%s)", sym_get_string_value(sym));
687				tmp = indent - tmp + 4;
688				if (tmp < 0)
689					tmp = 0;
690				cprint1("%*c%s%s", tmp, ' ', menu_get_prompt(menu),
691					(sym_has_value(sym) || !sym_is_changable(sym)) ?
692					"" : " (NEW)");
693				cprint_done();
694				goto conf_childs;
695			}
696		}
697		cprint1("%*c%s%s", indent + 1, ' ', menu_get_prompt(menu),
698			(sym_has_value(sym) || !sym_is_changable(sym)) ?
699			"" : " (NEW)");
700		if (menu->prompt->type == P_MENU) {
701			cprint1("  --->");
702			cprint_done();
703			return;
704		}
705		cprint_done();
706	}
707
708conf_childs:
709	indent += doint;
710	for (child = menu->list; child; child = child->next)
711		build_conf(child);
712	indent -= doint;
713}
714
715static void conf(struct menu *menu)
716{
717	struct menu *submenu;
718	const char *prompt = menu_get_prompt(menu);
719	struct symbol *sym;
720	char active_entry[40];
721	int stat, type, i;
722
723	unlink("lxdialog.scrltmp");
724	active_entry[0] = 0;
725	while (1) {
726		cprint_init();
727		cprint("--title");
728		cprint("%s", prompt ? prompt : _("Main Menu"));
729		cprint("--menu");
730		cprint(_(menu_instructions));
731		cprint("%d", rows);
732		cprint("%d", cols);
733		cprint("%d", rows - 10);
734		cprint("%s", active_entry);
735		current_menu = menu;
736		build_conf(menu);
737		if (!child_count)
738			break;
739		if (menu == &rootmenu) {
740			cprint(":");
741			cprint("--- ");
742			cprint("L");
743			cprint(_("    Load an Alternate Configuration File"));
744			cprint("S");
745			cprint(_("    Save Configuration to an Alternate File"));
746		}
747		stat = exec_conf();
748		if (stat < 0)
749			continue;
750
751		if (stat == 1 || stat == 255)
752			break;
753
754		type = input_buf[0];
755		if (!type)
756			continue;
757
758		for (i = 0; input_buf[i] && !isspace(input_buf[i]); i++)
759			;
760		if (i >= sizeof(active_entry))
761			i = sizeof(active_entry) - 1;
762		input_buf[i] = 0;
763		strcpy(active_entry, input_buf);
764
765		sym = NULL;
766		submenu = NULL;
767		if (sscanf(input_buf + 1, "%p", &submenu) == 1)
768			sym = submenu->sym;
769
770		switch (stat) {
771		case 0:
772			switch (type) {
773			case 'm':
774				if (single_menu_mode)
775					submenu->data = (void *) (long) !submenu->data;
776				else
777					conf(submenu);
778				break;
779			case 't':
780				if (sym_is_choice(sym) && sym_get_tristate_value(sym) == yes)
781					conf_choice(submenu);
782				else if (submenu->prompt->type == P_MENU)
783					conf(submenu);
784				break;
785			case 's':
786				conf_string(submenu);
787				break;
788			case 'L':
789				conf_load();
790				break;
791			case 'S':
792				conf_save();
793				break;
794			}
795			break;
796		case 2:
797			if (sym)
798				show_help(submenu);
799			else
800				show_helptext("README", _(mconf_readme));
801			break;
802		case 3:
803			if (type == 't') {
804				if (sym_set_tristate_value(sym, yes))
805					break;
806				if (sym_set_tristate_value(sym, mod))
807					show_textbox(NULL, setmod_text, 6, 74);
808			}
809			break;
810		case 4:
811			if (type == 't')
812				sym_set_tristate_value(sym, no);
813			break;
814		case 5:
815			if (type == 't')
816				sym_set_tristate_value(sym, mod);
817			break;
818		case 6:
819			if (type == 't')
820				sym_toggle_tristate_value(sym);
821			else if (type == 'm')
822				conf(submenu);
823			break;
824		case 7:
825			search_conf();
826			break;
827		}
828	}
829}
830
831static void show_textbox(const char *title, const char *text, int r, int c)
832{
833	int fd;
834
835	fd = creat(".help.tmp", 0777);
836	write(fd, text, strlen(text));
837	close(fd);
838	show_file(".help.tmp", title, r, c);
839	unlink(".help.tmp");
840}
841
842static void show_helptext(const char *title, const char *text)
843{
844	show_textbox(title, text, 0, 0);
845}
846
847static void show_help(struct menu *menu)
848{
849	struct gstr help = str_new();
850	struct symbol *sym = menu->sym;
851
852	if (sym->help)
853	{
854		if (sym->name) {
855			str_printf(&help, "CONFIG_%s:\n\n", sym->name);
856			str_append(&help, _(sym->help));
857			str_append(&help, "\n");
858		}
859	} else {
860		str_append(&help, nohelp_text);
861	}
862	get_symbol_str(&help, sym);
863	show_helptext(menu_get_prompt(menu), str_get(&help));
864	str_free(&help);
865}
866
867static void show_file(const char *filename, const char *title, int r, int c)
868{
869	do {
870		cprint_init();
871		if (title) {
872			cprint("--title");
873			cprint("%s", title);
874		}
875		cprint("--textbox");
876		cprint("%s", filename);
877		cprint("%d", r ? r : rows);
878		cprint("%d", c ? c : cols);
879	} while (exec_conf() < 0);
880}
881
882static void conf_choice(struct menu *menu)
883{
884	const char *prompt = menu_get_prompt(menu);
885	struct menu *child;
886	struct symbol *active;
887	int stat;
888
889	active = sym_get_choice_value(menu->sym);
890	while (1) {
891		cprint_init();
892		cprint("--title");
893		cprint("%s", prompt ? prompt : _("Main Menu"));
894		cprint("--radiolist");
895		cprint(_(radiolist_instructions));
896		cprint("15");
897		cprint("70");
898		cprint("6");
899
900		current_menu = menu;
901		for (child = menu->list; child; child = child->next) {
902			if (!menu_is_visible(child))
903				continue;
904			cprint("%p", child);
905			cprint("%s", menu_get_prompt(child));
906			if (child->sym == sym_get_choice_value(menu->sym))
907				cprint("ON");
908			else if (child->sym == active)
909				cprint("SELECTED");
910			else
911				cprint("OFF");
912		}
913
914		stat = exec_conf();
915		switch (stat) {
916		case 0:
917			if (sscanf(input_buf, "%p", &child) != 1)
918				break;
919			sym_set_tristate_value(child->sym, yes);
920			return;
921		case 1:
922			if (sscanf(input_buf, "%p", &child) == 1) {
923				show_help(child);
924				active = child->sym;
925			} else
926				show_help(menu);
927			break;
928		case 255:
929			return;
930		}
931	}
932}
933
934static void conf_string(struct menu *menu)
935{
936	const char *prompt = menu_get_prompt(menu);
937	int stat;
938
939	while (1) {
940		cprint_init();
941		cprint("--title");
942		cprint("%s", prompt ? prompt : _("Main Menu"));
943		cprint("--inputbox");
944		switch (sym_get_type(menu->sym)) {
945		case S_INT:
946			cprint(_(inputbox_instructions_int));
947			break;
948		case S_HEX:
949			cprint(_(inputbox_instructions_hex));
950			break;
951		case S_STRING:
952			cprint(_(inputbox_instructions_string));
953			break;
954		default:
955			/* panic? */;
956		}
957		cprint("10");
958		cprint("75");
959		cprint("%s", sym_get_string_value(menu->sym));
960		stat = exec_conf();
961		switch (stat) {
962		case 0:
963			if (sym_set_string_value(menu->sym, input_buf))
964				return;
965			show_textbox(NULL, _("You have made an invalid entry."), 5, 43);
966			break;
967		case 1:
968			show_help(menu);
969			break;
970		case 255:
971			return;
972		}
973	}
974}
975
976static void conf_load(void)
977{
978	int stat;
979
980	while (1) {
981		cprint_init();
982		cprint("--inputbox");
983		cprint(load_config_text);
984		cprint("11");
985		cprint("55");
986		cprint("%s", filename);
987		stat = exec_conf();
988		switch(stat) {
989		case 0:
990			if (!input_buf[0])
991				return;
992			if (!conf_read(input_buf))
993				return;
994			show_textbox(NULL, _("File does not exist!"), 5, 38);
995			break;
996		case 1:
997			show_helptext(_("Load Alternate Configuration"), load_config_help);
998			break;
999		case 255:
1000			return;
1001		}
1002	}
1003}
1004
1005static void conf_save(void)
1006{
1007	int stat;
1008
1009	while (1) {
1010		cprint_init();
1011		cprint("--inputbox");
1012		cprint(save_config_text);
1013		cprint("11");
1014		cprint("55");
1015		cprint("%s", filename);
1016		stat = exec_conf();
1017		switch(stat) {
1018		case 0:
1019			if (!input_buf[0])
1020				return;
1021			if (!conf_write(input_buf))
1022				return;
1023			show_textbox(NULL, _("Can't create file!  Probably a nonexistent directory."), 5, 60);
1024			break;
1025		case 1:
1026			show_helptext(_("Save Alternate Configuration"), save_config_help);
1027			break;
1028		case 255:
1029			return;
1030		}
1031	}
1032}
1033
1034static void conf_cleanup(void)
1035{
1036	tcsetattr(1, TCSAFLUSH, &ios_org);
1037	unlink(".help.tmp");
1038	unlink("lxdialog.scrltmp");
1039}
1040
1041int main(int ac, char **av)
1042{
1043	struct symbol *sym;
1044	char *mode;
1045	int stat;
1046
1047	setlocale(LC_ALL, "");
1048	bindtextdomain(PACKAGE, LOCALEDIR);
1049	textdomain(PACKAGE);
1050
1051	conf_parse(av[1]);
1052	conf_read(NULL);
1053
1054	sym = sym_lookup("KERNELVERSION", 0);
1055	sym_calc_value(sym);
1056	sprintf(menu_backtitle, _("BusyBox %s Configuration"),
1057		sym_get_string_value(sym));
1058
1059	mode = getenv("MENUCONFIG_MODE");
1060	if (mode) {
1061		if (!strcasecmp(mode, "single_menu"))
1062			single_menu_mode = 1;
1063	}
1064
1065	tcgetattr(1, &ios_org);
1066	atexit(conf_cleanup);
1067	init_wsize();
1068	conf(&rootmenu);
1069
1070	do {
1071		cprint_init();
1072		cprint("--yesno");
1073		cprint(_("Do you wish to save your new configuration?"));
1074		cprint("5");
1075		cprint("60");
1076		stat = exec_conf();
1077	} while (stat < 0);
1078
1079	if (stat == 0) {
1080		if (conf_write(NULL)) {
1081			fprintf(stderr, _("\n\n"
1082				"Error during writing of the configuration.\n"
1083				"Your configuration changes were NOT saved."
1084				"\n\n"));
1085			return 1;
1086		}
1087		printf(_("\n\n"
1088			"*** End of configuration.\n"
1089			"*** Execute 'make' to build the project or try 'make help'."
1090			"\n\n"));
1091	} else {
1092		fprintf(stderr, _("\n\n"
1093			"Your configuration changes were NOT saved."
1094			"\n\n"));
1095	}
1096
1097	return 0;
1098}
1099