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