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