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