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 busybox 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 busybox 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 busybox\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	"busybox's 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 busybox\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 char filename[PATH_MAX+1] = ".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	sigpending(&sset);
511	if (sigismember(&sset, SIGINT)) {
512		printf("\finterrupted\n");
513		exit(1);
514	}
515	sigprocmask(SIG_SETMASK, &osset, NULL);
516
517	return WEXITSTATUS(stat);
518}
519
520static void search_conf(void)
521{
522	struct symbol **sym_arr;
523	int stat;
524	struct gstr res;
525
526again:
527	cprint_init();
528	cprint("--title");
529	cprint(_("Search Configuration Parameter"));
530	cprint("--inputbox");
531	cprint(_("Enter CONFIG_ (sub)string to search for (omit CONFIG_)"));
532	cprint("10");
533	cprint("75");
534	cprint("");
535	stat = exec_conf();
536	if (stat < 0)
537		goto again;
538	switch (stat) {
539	case 0:
540		break;
541	case 1:
542		show_helptext(_("Search Configuration"), search_help);
543		goto again;
544	default:
545		return;
546	}
547
548	sym_arr = sym_re_search(input_buf);
549	res = get_relations_str(sym_arr);
550	free(sym_arr);
551	show_textbox(_("Search Results"), str_get(&res), 0, 0);
552	str_free(&res);
553}
554
555static void build_conf(struct menu *menu)
556{
557	struct symbol *sym;
558	struct property *prop;
559	struct menu *child;
560	int type, tmp, doint = 2;
561	tristate val;
562	char ch;
563
564	if (!menu_is_visible(menu))
565		return;
566
567	sym = menu->sym;
568	prop = menu->prompt;
569	if (!sym) {
570		if (prop && menu != current_menu) {
571			const char *prompt = menu_get_prompt(menu);
572			switch (prop->type) {
573			case P_MENU:
574				child_count++;
575				cprint("m%p", menu);
576
577				if (single_menu_mode) {
578					cprint1("%s%*c%s",
579						menu->data ? "-->" : "++>",
580						indent + 1, ' ', prompt);
581				} else
582					cprint1("   %*c%s  --->", indent + 1, ' ', prompt);
583
584				cprint_done();
585				if (single_menu_mode && menu->data)
586					goto conf_childs;
587				return;
588			default:
589				if (prompt) {
590					child_count++;
591					cprint(":%p", menu);
592					cprint("---%*c%s", indent + 1, ' ', prompt);
593				}
594			}
595		} else
596			doint = 0;
597		goto conf_childs;
598	}
599
600	type = sym_get_type(sym);
601	if (sym_is_choice(sym)) {
602		struct symbol *def_sym = sym_get_choice_value(sym);
603		struct menu *def_menu = NULL;
604
605		child_count++;
606		for (child = menu->list; child; child = child->next) {
607			if (menu_is_visible(child) && child->sym == def_sym)
608				def_menu = child;
609		}
610
611		val = sym_get_tristate_value(sym);
612		if (sym_is_changable(sym)) {
613			cprint("t%p", menu);
614			switch (type) {
615			case S_BOOLEAN:
616				cprint1("[%c]", val == no ? ' ' : '*');
617				break;
618			case S_TRISTATE:
619				switch (val) {
620				case yes: ch = '*'; break;
621				case mod: ch = 'M'; break;
622				default:  ch = ' '; break;
623				}
624				cprint1("<%c>", ch);
625				break;
626			}
627		} else {
628			cprint("%c%p", def_menu ? 't' : ':', menu);
629			cprint1("   ");
630		}
631
632		cprint1("%*c%s", indent + 1, ' ', menu_get_prompt(menu));
633		if (val == yes) {
634			if (def_menu) {
635				cprint1(" (%s)", menu_get_prompt(def_menu));
636				cprint1("  --->");
637				cprint_done();
638				if (def_menu->list) {
639					indent += 2;
640					build_conf(def_menu);
641					indent -= 2;
642				}
643			} else
644				cprint_done();
645			return;
646		}
647		cprint_done();
648	} else {
649		if (menu == current_menu) {
650			cprint(":%p", menu);
651			cprint("---%*c%s", indent + 1, ' ', menu_get_prompt(menu));
652			goto conf_childs;
653		}
654		child_count++;
655		val = sym_get_tristate_value(sym);
656		if (sym_is_choice_value(sym) && val == yes) {
657			cprint(":%p", menu);
658			cprint1("   ");
659		} else {
660			switch (type) {
661			case S_BOOLEAN:
662				cprint("t%p", menu);
663				if (sym_is_changable(sym))
664					cprint1("[%c]", val == no ? ' ' : '*');
665				else
666					cprint1("---");
667				break;
668			case S_TRISTATE:
669				cprint("t%p", menu);
670				switch (val) {
671				case yes: ch = '*'; break;
672				case mod: ch = 'M'; break;
673				default:  ch = ' '; break;
674				}
675				if (sym_is_changable(sym))
676					cprint1("<%c>", ch);
677				else
678					cprint1("---");
679				break;
680			default:
681				cprint("s%p", menu);
682				tmp = cprint1("(%s)", sym_get_string_value(sym));
683				tmp = indent - tmp + 4;
684				if (tmp < 0)
685					tmp = 0;
686				cprint1("%*c%s%s", tmp, ' ', menu_get_prompt(menu),
687					(sym_has_value(sym) || !sym_is_changable(sym)) ?
688					"" : " (NEW)");
689				cprint_done();
690				goto conf_childs;
691			}
692		}
693		cprint1("%*c%s%s", indent + 1, ' ', menu_get_prompt(menu),
694			(sym_has_value(sym) || !sym_is_changable(sym)) ?
695			"" : " (NEW)");
696		if (menu->prompt->type == P_MENU) {
697			cprint1("  --->");
698			cprint_done();
699			return;
700		}
701		cprint_done();
702	}
703
704conf_childs:
705	indent += doint;
706	for (child = menu->list; child; child = child->next)
707		build_conf(child);
708	indent -= doint;
709}
710
711static void conf(struct menu *menu)
712{
713	struct menu *submenu;
714	const char *prompt = menu_get_prompt(menu);
715	struct symbol *sym;
716	char active_entry[40];
717	int stat, type, i;
718
719	unlink("lxdialog.scrltmp");
720	active_entry[0] = 0;
721	while (1) {
722		cprint_init();
723		cprint("--title");
724		cprint("%s", prompt ? prompt : _("Main Menu"));
725		cprint("--menu");
726		cprint(_(menu_instructions));
727		cprint("%d", rows);
728		cprint("%d", cols);
729		cprint("%d", rows - 10);
730		cprint("%s", active_entry);
731		current_menu = menu;
732		build_conf(menu);
733		if (!child_count)
734			break;
735		if (menu == &rootmenu) {
736			cprint(":");
737			cprint("--- ");
738			cprint("L");
739			cprint(_("    Load an Alternate Configuration File"));
740			cprint("S");
741			cprint(_("    Save Configuration to an Alternate File"));
742		}
743		stat = exec_conf();
744		if (stat < 0)
745			continue;
746
747		if (stat == 1 || stat == 255)
748			break;
749
750		type = input_buf[0];
751		if (!type)
752			continue;
753
754		for (i = 0; input_buf[i] && !isspace(input_buf[i]); i++)
755			;
756		if (i >= sizeof(active_entry))
757			i = sizeof(active_entry) - 1;
758		input_buf[i] = 0;
759		strcpy(active_entry, input_buf);
760
761		sym = NULL;
762		submenu = NULL;
763		if (sscanf(input_buf + 1, "%p", &submenu) == 1)
764			sym = submenu->sym;
765
766		switch (stat) {
767		case 0:
768			switch (type) {
769			case 'm':
770				if (single_menu_mode)
771					submenu->data = (void *) (long) !submenu->data;
772				else
773					conf(submenu);
774				break;
775			case 't':
776				if (sym_is_choice(sym) && sym_get_tristate_value(sym) == yes)
777					conf_choice(submenu);
778				else if (submenu->prompt->type == P_MENU)
779					conf(submenu);
780				break;
781			case 's':
782				conf_string(submenu);
783				break;
784			case 'L':
785				conf_load();
786				break;
787			case 'S':
788				conf_save();
789				break;
790			}
791			break;
792		case 2:
793			if (sym)
794				show_help(submenu);
795			else
796				show_helptext("README", _(mconf_readme));
797			break;
798		case 3:
799			if (type == 't') {
800				if (sym_set_tristate_value(sym, yes))
801					break;
802				if (sym_set_tristate_value(sym, mod))
803					show_textbox(NULL, setmod_text, 6, 74);
804			}
805			break;
806		case 4:
807			if (type == 't')
808				sym_set_tristate_value(sym, no);
809			break;
810		case 5:
811			if (type == 't')
812				sym_set_tristate_value(sym, mod);
813			break;
814		case 6:
815			if (type == 't')
816				sym_toggle_tristate_value(sym);
817			else if (type == 'm')
818				conf(submenu);
819			break;
820		case 7:
821			search_conf();
822			break;
823		}
824	}
825}
826
827static void show_textbox(const char *title, const char *text, int r, int c)
828{
829	int fd;
830
831	fd = creat(".help.tmp", 0777);
832	write(fd, text, strlen(text));
833	close(fd);
834	show_file(".help.tmp", title, r, c);
835	unlink(".help.tmp");
836}
837
838static void show_helptext(const char *title, const char *text)
839{
840	show_textbox(title, text, 0, 0);
841}
842
843static void show_help(struct menu *menu)
844{
845	struct gstr help = str_new();
846	struct symbol *sym = menu->sym;
847
848	if (sym->help)
849	{
850		if (sym->name) {
851			str_printf(&help, "CONFIG_%s:\n\n", sym->name);
852			str_append(&help, _(sym->help));
853			str_append(&help, "\n");
854		}
855	} else {
856		str_append(&help, nohelp_text);
857	}
858	get_symbol_str(&help, sym);
859	show_helptext(menu_get_prompt(menu), str_get(&help));
860	str_free(&help);
861}
862
863static void show_file(const char *filename, const char *title, int r, int c)
864{
865	do {
866		cprint_init();
867		if (title) {
868			cprint("--title");
869			cprint("%s", title);
870		}
871		cprint("--textbox");
872		cprint("%s", filename);
873		cprint("%d", r ? r : rows);
874		cprint("%d", c ? c : cols);
875	} while (exec_conf() < 0);
876}
877
878static void conf_choice(struct menu *menu)
879{
880	const char *prompt = menu_get_prompt(menu);
881	struct menu *child;
882	struct symbol *active;
883	int stat;
884
885	active = sym_get_choice_value(menu->sym);
886	while (1) {
887		cprint_init();
888		cprint("--title");
889		cprint("%s", prompt ? prompt : _("Main Menu"));
890		cprint("--radiolist");
891		cprint(_(radiolist_instructions));
892		cprint("15");
893		cprint("70");
894		cprint("6");
895
896		current_menu = menu;
897		for (child = menu->list; child; child = child->next) {
898			if (!menu_is_visible(child))
899				continue;
900			cprint("%p", child);
901			cprint("%s", menu_get_prompt(child));
902			if (child->sym == sym_get_choice_value(menu->sym))
903				cprint("ON");
904			else if (child->sym == active)
905				cprint("SELECTED");
906			else
907				cprint("OFF");
908		}
909
910		stat = exec_conf();
911		switch (stat) {
912		case 0:
913			if (sscanf(input_buf, "%p", &child) != 1)
914				break;
915			sym_set_tristate_value(child->sym, yes);
916			return;
917		case 1:
918			if (sscanf(input_buf, "%p", &child) == 1) {
919				show_help(child);
920				active = child->sym;
921			} else
922				show_help(menu);
923			break;
924		case 255:
925			return;
926		}
927	}
928}
929
930static void conf_string(struct menu *menu)
931{
932	const char *prompt = menu_get_prompt(menu);
933	int stat;
934
935	while (1) {
936		cprint_init();
937		cprint("--title");
938		cprint("%s", prompt ? prompt : _("Main Menu"));
939		cprint("--inputbox");
940		switch (sym_get_type(menu->sym)) {
941		case S_INT:
942			cprint(_(inputbox_instructions_int));
943			break;
944		case S_HEX:
945			cprint(_(inputbox_instructions_hex));
946			break;
947		case S_STRING:
948			cprint(_(inputbox_instructions_string));
949			break;
950		default:
951			/* panic? */;
952		}
953		cprint("10");
954		cprint("75");
955		cprint("%s", sym_get_string_value(menu->sym));
956		stat = exec_conf();
957		switch (stat) {
958		case 0:
959			if (sym_set_string_value(menu->sym, input_buf))
960				return;
961			show_textbox(NULL, _("You have made an invalid entry."), 5, 43);
962			break;
963		case 1:
964			show_help(menu);
965			break;
966		case 255:
967			return;
968		}
969	}
970}
971
972static void conf_load(void)
973{
974	int stat;
975
976	while (1) {
977		cprint_init();
978		cprint("--inputbox");
979		cprint(load_config_text);
980		cprint("11");
981		cprint("55");
982		cprint("%s", filename);
983		stat = exec_conf();
984		switch(stat) {
985		case 0:
986			if (!input_buf[0])
987				return;
988			if (!conf_read(input_buf))
989				return;
990			show_textbox(NULL, _("File does not exist!"), 5, 38);
991			break;
992		case 1:
993			show_helptext(_("Load Alternate Configuration"), load_config_help);
994			break;
995		case 255:
996			return;
997		}
998	}
999}
1000
1001static void conf_save(void)
1002{
1003	int stat;
1004
1005	while (1) {
1006		cprint_init();
1007		cprint("--inputbox");
1008		cprint(save_config_text);
1009		cprint("11");
1010		cprint("55");
1011		cprint("%s", filename);
1012		stat = exec_conf();
1013		switch(stat) {
1014		case 0:
1015			if (!input_buf[0])
1016				return;
1017			if (!conf_write(input_buf))
1018				return;
1019			show_textbox(NULL, _("Can't create file!  Probably a nonexistent directory."), 5, 60);
1020			break;
1021		case 1:
1022			show_helptext(_("Save Alternate Configuration"), save_config_help);
1023			break;
1024		case 255:
1025			return;
1026		}
1027	}
1028}
1029
1030static void conf_cleanup(void)
1031{
1032	tcsetattr(1, TCSAFLUSH, &ios_org);
1033	unlink(".help.tmp");
1034	unlink("lxdialog.scrltmp");
1035}
1036
1037int main(int ac, char **av)
1038{
1039	struct symbol *sym;
1040	char *mode;
1041	int stat;
1042
1043	setlocale(LC_ALL, "");
1044	bindtextdomain(PACKAGE, LOCALEDIR);
1045	textdomain(PACKAGE);
1046
1047	conf_parse(av[1]);
1048	conf_read(NULL);
1049
1050	sym = sym_lookup("KERNELVERSION", 0);
1051	sym_calc_value(sym);
1052	sprintf(menu_backtitle, _("BusyBox %s Configuration"),
1053		sym_get_string_value(sym));
1054
1055	mode = getenv("MENUCONFIG_MODE");
1056	if (mode) {
1057		if (!strcasecmp(mode, "single_menu"))
1058			single_menu_mode = 1;
1059	}
1060
1061	tcgetattr(1, &ios_org);
1062	atexit(conf_cleanup);
1063	init_wsize();
1064	conf(&rootmenu);
1065
1066	do {
1067		cprint_init();
1068		cprint("--yesno");
1069		cprint(_("Do you wish to save your new busybox configuration?"));
1070		cprint("5");
1071		cprint("60");
1072		stat = exec_conf();
1073	} while (stat < 0);
1074
1075	if (stat == 0) {
1076		if (conf_write(NULL)) {
1077			fprintf(stderr, _("\n\n"
1078				"Error during writing of the busybox configuration.\n"
1079				"Your busybox configuration changes were NOT saved."
1080				"\n\n"));
1081			return 1;
1082		}
1083		printf(_("\n\n"
1084			"*** End of busybox configuration.\n"
1085			"*** Execute 'make' to build busybox or try 'make help'."
1086			"\n\n"));
1087	} else {
1088		fprintf(stderr, _("\n\n"
1089			"Your busybox configuration changes were NOT saved."
1090			"\n\n"));
1091	}
1092
1093	return 0;
1094}
1095