xref: /kernel/linux/linux-6.6/scripts/kconfig/mconf.c (revision 62306a36)
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
163"Search\n"
164"-------\n"
165"Pressing the forward-slash (/) anywhere brings up a search dialog box.\n"
166"\n"
167
168"Different color themes available\n"
169"--------------------------------\n"
170"It is possible to select different color themes using the variable\n"
171"MENUCONFIG_COLOR. To select a theme use:\n"
172"\n"
173"make MENUCONFIG_COLOR=<theme> menuconfig\n"
174"\n"
175"Available themes are\n"
176" mono       => selects colors suitable for monochrome displays\n"
177" blackbg    => selects a color scheme with black background\n"
178" classic    => theme with blue background. The classic look\n"
179" bluetitle  => an LCD friendly version of classic. (default)\n"
180"\n",
181menu_instructions[] =
182	"Arrow keys navigate the menu.  "
183	"<Enter> selects submenus ---> (or empty submenus ----).  "
184	"Highlighted letters are hotkeys.  "
185	"Pressing <Y> includes, <N> excludes, <M> modularizes features.  "
186	"Press <Esc><Esc> to exit, <?> for Help, </> for Search.  "
187	"Legend: [*] built-in  [ ] excluded  <M> module  < > module capable",
188radiolist_instructions[] =
189	"Use the arrow keys to navigate this window or "
190	"press the hotkey of the item you wish to select "
191	"followed by the <SPACE BAR>. "
192	"Press <?> for additional information about this option.",
193inputbox_instructions_int[] =
194	"Please enter a decimal value. "
195	"Fractions will not be accepted.  "
196	"Use the <TAB> key to move from the input field to the buttons below it.",
197inputbox_instructions_hex[] =
198	"Please enter a hexadecimal value. "
199	"Use the <TAB> key to move from the input field to the buttons below it.",
200inputbox_instructions_string[] =
201	"Please enter a string value. "
202	"Use the <TAB> key to move from the input field to the buttons below it.",
203setmod_text[] =
204	"This feature depends on another which has been configured as a module.\n"
205	"As a result, this feature will be built as a module.",
206load_config_text[] =
207	"Enter the name of the configuration file you wish to load.  "
208	"Accept the name shown to restore the configuration you "
209	"last retrieved.  Leave blank to abort.",
210load_config_help[] =
211	"\n"
212	"For various reasons, one may wish to keep several different\n"
213	"configurations available on a single machine.\n"
214	"\n"
215	"If you have saved a previous configuration in a file other than the\n"
216	"default one, entering its name here will allow you to modify that\n"
217	"configuration.\n"
218	"\n"
219	"If you are uncertain, then you have probably never used alternate\n"
220	"configuration files. You should therefore leave this blank to abort.\n",
221save_config_text[] =
222	"Enter a filename to which this configuration should be saved "
223	"as an alternate.  Leave blank to abort.",
224save_config_help[] =
225	"\n"
226	"For various reasons, one may wish to keep different configurations\n"
227	"available on a single machine.\n"
228	"\n"
229	"Entering a file name here will allow you to later retrieve, modify\n"
230	"and use the current configuration as an alternate to whatever\n"
231	"configuration options you have selected at that time.\n"
232	"\n"
233	"If you are uncertain what all this means then you should probably\n"
234	"leave this blank.\n",
235search_help[] =
236	"\n"
237	"Search for symbols and display their relations.\n"
238	"Regular expressions are allowed.\n"
239	"Example: search for \"^FOO\"\n"
240	"Result:\n"
241	"-----------------------------------------------------------------\n"
242	"Symbol: FOO [=m]\n"
243	"Type  : tristate\n"
244	"Prompt: Foo bus is used to drive the bar HW\n"
245	"  Location:\n"
246	"    -> Bus options (PCI, PCMCIA, EISA, ISA)\n"
247	"      -> PCI support (PCI [=y])\n"
248	"(1)     -> PCI access mode (<choice> [=y])\n"
249	"  Defined at drivers/pci/Kconfig:47\n"
250	"  Depends on: X86_LOCAL_APIC && X86_IO_APIC || IA64\n"
251	"  Selects: LIBCRC32\n"
252	"  Selected by: BAR [=n]\n"
253	"-----------------------------------------------------------------\n"
254	"o The line 'Type:' shows the type of the configuration option for\n"
255	"  this symbol (bool, tristate, string, ...)\n"
256	"o The line 'Prompt:' shows the text used in the menu structure for\n"
257	"  this symbol\n"
258	"o The 'Defined at' line tells at what file / line number the symbol\n"
259	"  is defined\n"
260	"o The 'Depends on:' line tells what symbols need to be defined for\n"
261	"  this symbol to be visible in the menu (selectable)\n"
262	"o The 'Location:' lines tells where in the menu structure this symbol\n"
263	"  is located\n"
264	"    A location followed by a [=y] indicates that this is a\n"
265	"    selectable menu item - and the current value is displayed inside\n"
266	"    brackets.\n"
267	"    Press the key in the (#) prefix to jump directly to that\n"
268	"    location. You will be returned to the current search results\n"
269	"    after exiting this new menu.\n"
270	"o The 'Selects:' line tells what symbols will be automatically\n"
271	"  selected if this symbol is selected (y or m)\n"
272	"o The 'Selected by' line tells what symbol has selected this symbol\n"
273	"\n"
274	"Only relevant lines are shown.\n"
275	"\n\n"
276	"Search examples:\n"
277	"Examples: USB	=> find all symbols containing USB\n"
278	"          ^USB => find all symbols starting with USB\n"
279	"          USB$ => find all symbols ending with USB\n"
280	"\n";
281
282static int indent;
283static struct menu *current_menu;
284static int child_count;
285static int single_menu_mode;
286static int show_all_options;
287static int save_and_exit;
288static int silent;
289static int jump_key_char;
290
291static void conf(struct menu *menu, struct menu *active_menu);
292
293static char filename[PATH_MAX+1];
294static void set_config_filename(const char *config_filename)
295{
296	static char menu_backtitle[PATH_MAX+128];
297
298	snprintf(menu_backtitle, sizeof(menu_backtitle), "%s - %s",
299		 config_filename, rootmenu.prompt->text);
300	set_dialog_backtitle(menu_backtitle);
301
302	snprintf(filename, sizeof(filename), "%s", config_filename);
303}
304
305struct subtitle_part {
306	struct list_head entries;
307	const char *text;
308};
309static LIST_HEAD(trail);
310
311static struct subtitle_list *subtitles;
312static void set_subtitle(void)
313{
314	struct subtitle_part *sp;
315	struct subtitle_list *pos, *tmp;
316
317	for (pos = subtitles; pos != NULL; pos = tmp) {
318		tmp = pos->next;
319		free(pos);
320	}
321
322	subtitles = NULL;
323	list_for_each_entry(sp, &trail, entries) {
324		if (sp->text) {
325			if (pos) {
326				pos->next = xcalloc(1, sizeof(*pos));
327				pos = pos->next;
328			} else {
329				subtitles = pos = xcalloc(1, sizeof(*pos));
330			}
331			pos->text = sp->text;
332		}
333	}
334
335	set_dialog_subtitles(subtitles);
336}
337
338static void reset_subtitle(void)
339{
340	struct subtitle_list *pos, *tmp;
341
342	for (pos = subtitles; pos != NULL; pos = tmp) {
343		tmp = pos->next;
344		free(pos);
345	}
346	subtitles = NULL;
347	set_dialog_subtitles(subtitles);
348}
349
350static int show_textbox_ext(const char *title, const char *text, int r, int c,
351			    int *vscroll, int *hscroll,
352			    int (*extra_key_cb)(int, size_t, size_t, void *),
353			    void *data)
354{
355	dialog_clear();
356	return dialog_textbox(title, text, r, c, vscroll, hscroll,
357			      extra_key_cb, data);
358}
359
360static void show_textbox(const char *title, const char *text, int r, int c)
361{
362	show_textbox_ext(title, text, r, c, NULL, NULL, NULL, NULL);
363}
364
365static void show_helptext(const char *title, const char *text)
366{
367	show_textbox(title, text, 0, 0);
368}
369
370static void show_help(struct menu *menu)
371{
372	struct gstr help = str_new();
373
374	help.max_width = getmaxx(stdscr) - 10;
375	menu_get_ext_help(menu, &help);
376
377	show_helptext(menu_get_prompt(menu), str_get(&help));
378	str_free(&help);
379}
380
381struct search_data {
382	struct list_head *head;
383	struct menu *target;
384};
385
386static int next_jump_key(int key)
387{
388	if (key < '1' || key > '9')
389		return '1';
390
391	key++;
392
393	if (key > '9')
394		key = '1';
395
396	return key;
397}
398
399static int handle_search_keys(int key, size_t start, size_t end, void *_data)
400{
401	struct search_data *data = _data;
402	struct jump_key *pos;
403	int index = 0;
404
405	if (key < '1' || key > '9')
406		return 0;
407
408	list_for_each_entry(pos, data->head, entries) {
409		index = next_jump_key(index);
410
411		if (pos->offset < start)
412			continue;
413
414		if (pos->offset >= end)
415			break;
416
417		if (key == index) {
418			data->target = pos->target;
419			return 1;
420		}
421	}
422
423	return 0;
424}
425
426int get_jump_key_char(void)
427{
428	jump_key_char = next_jump_key(jump_key_char);
429
430	return jump_key_char;
431}
432
433static void search_conf(void)
434{
435	struct symbol **sym_arr;
436	struct gstr res;
437	struct gstr title;
438	char *dialog_input;
439	int dres, vscroll = 0, hscroll = 0;
440	bool again;
441	struct gstr sttext;
442	struct subtitle_part stpart;
443
444	title = str_new();
445	str_printf( &title, "Enter (sub)string or regexp to search for "
446			      "(with or without \"%s\")", CONFIG_);
447
448again:
449	dialog_clear();
450	dres = dialog_inputbox("Search Configuration Parameter",
451			      str_get(&title),
452			      10, 75, "");
453	switch (dres) {
454	case 0:
455		break;
456	case 1:
457		show_helptext("Search Configuration", search_help);
458		goto again;
459	default:
460		str_free(&title);
461		return;
462	}
463
464	/* strip the prefix if necessary */
465	dialog_input = dialog_input_result;
466	if (strncasecmp(dialog_input_result, CONFIG_, strlen(CONFIG_)) == 0)
467		dialog_input += strlen(CONFIG_);
468
469	sttext = str_new();
470	str_printf(&sttext, "Search (%s)", dialog_input_result);
471	stpart.text = str_get(&sttext);
472	list_add_tail(&stpart.entries, &trail);
473
474	sym_arr = sym_re_search(dialog_input);
475	do {
476		LIST_HEAD(head);
477		struct search_data data = {
478			.head = &head,
479		};
480		struct jump_key *pos, *tmp;
481
482		jump_key_char = 0;
483		res = get_relations_str(sym_arr, &head);
484		set_subtitle();
485		dres = show_textbox_ext("Search Results", str_get(&res), 0, 0,
486					&vscroll, &hscroll,
487					handle_search_keys, &data);
488		again = false;
489		if (dres >= '1' && dres <= '9') {
490			assert(data.target != NULL);
491			conf(data.target->parent, data.target);
492			again = true;
493		}
494		str_free(&res);
495		list_for_each_entry_safe(pos, tmp, &head, entries)
496			free(pos);
497	} while (again);
498	free(sym_arr);
499	str_free(&title);
500	list_del(trail.prev);
501	str_free(&sttext);
502}
503
504static void build_conf(struct menu *menu)
505{
506	struct symbol *sym;
507	struct property *prop;
508	struct menu *child;
509	int type, tmp, doint = 2;
510	tristate val;
511	char ch;
512	bool visible;
513
514	/*
515	 * note: menu_is_visible() has side effect that it will
516	 * recalc the value of the symbol.
517	 */
518	visible = menu_is_visible(menu);
519	if (show_all_options && !menu_has_prompt(menu))
520		return;
521	else if (!show_all_options && !visible)
522		return;
523
524	sym = menu->sym;
525	prop = menu->prompt;
526	if (!sym) {
527		if (prop && menu != current_menu) {
528			const char *prompt = menu_get_prompt(menu);
529			switch (prop->type) {
530			case P_MENU:
531				child_count++;
532				if (single_menu_mode) {
533					item_make("%s%*c%s",
534						  menu->data ? "-->" : "++>",
535						  indent + 1, ' ', prompt);
536				} else
537					item_make("   %*c%s  %s",
538						  indent + 1, ' ', prompt,
539						  menu_is_empty(menu) ? "----" : "--->");
540				item_set_tag('m');
541				item_set_data(menu);
542				if (single_menu_mode && menu->data)
543					goto conf_childs;
544				return;
545			case P_COMMENT:
546				if (prompt) {
547					child_count++;
548					item_make("   %*c*** %s ***", indent + 1, ' ', prompt);
549					item_set_tag(':');
550					item_set_data(menu);
551				}
552				break;
553			default:
554				if (prompt) {
555					child_count++;
556					item_make("---%*c%s", indent + 1, ' ', prompt);
557					item_set_tag(':');
558					item_set_data(menu);
559				}
560			}
561		} else
562			doint = 0;
563		goto conf_childs;
564	}
565
566	type = sym_get_type(sym);
567	if (sym_is_choice(sym)) {
568		struct symbol *def_sym = sym_get_choice_value(sym);
569		struct menu *def_menu = NULL;
570
571		child_count++;
572		for (child = menu->list; child; child = child->next) {
573			if (menu_is_visible(child) && child->sym == def_sym)
574				def_menu = child;
575		}
576
577		val = sym_get_tristate_value(sym);
578		if (sym_is_changeable(sym)) {
579			switch (type) {
580			case S_BOOLEAN:
581				item_make("[%c]", val == no ? ' ' : '*');
582				break;
583			case S_TRISTATE:
584				switch (val) {
585				case yes: ch = '*'; break;
586				case mod: ch = 'M'; break;
587				default:  ch = ' '; break;
588				}
589				item_make("<%c>", ch);
590				break;
591			}
592			item_set_tag('t');
593			item_set_data(menu);
594		} else {
595			item_make("   ");
596			item_set_tag(def_menu ? 't' : ':');
597			item_set_data(menu);
598		}
599
600		item_add_str("%*c%s", indent + 1, ' ', menu_get_prompt(menu));
601		if (val == yes) {
602			if (def_menu) {
603				item_add_str(" (%s)", menu_get_prompt(def_menu));
604				item_add_str("  --->");
605				if (def_menu->list) {
606					indent += 2;
607					build_conf(def_menu);
608					indent -= 2;
609				}
610			}
611			return;
612		}
613	} else {
614		if (menu == current_menu) {
615			item_make("---%*c%s", indent + 1, ' ', menu_get_prompt(menu));
616			item_set_tag(':');
617			item_set_data(menu);
618			goto conf_childs;
619		}
620		child_count++;
621		val = sym_get_tristate_value(sym);
622		if (sym_is_choice_value(sym) && val == yes) {
623			item_make("   ");
624			item_set_tag(':');
625			item_set_data(menu);
626		} else {
627			switch (type) {
628			case S_BOOLEAN:
629				if (sym_is_changeable(sym))
630					item_make("[%c]", val == no ? ' ' : '*');
631				else
632					item_make("-%c-", val == no ? ' ' : '*');
633				item_set_tag('t');
634				item_set_data(menu);
635				break;
636			case S_TRISTATE:
637				switch (val) {
638				case yes: ch = '*'; break;
639				case mod: ch = 'M'; break;
640				default:  ch = ' '; break;
641				}
642				if (sym_is_changeable(sym)) {
643					if (sym->rev_dep.tri == mod)
644						item_make("{%c}", ch);
645					else
646						item_make("<%c>", ch);
647				} else
648					item_make("-%c-", ch);
649				item_set_tag('t');
650				item_set_data(menu);
651				break;
652			default:
653				tmp = 2 + strlen(sym_get_string_value(sym)); /* () = 2 */
654				item_make("(%s)", sym_get_string_value(sym));
655				tmp = indent - tmp + 4;
656				if (tmp < 0)
657					tmp = 0;
658				item_add_str("%*c%s%s", tmp, ' ', menu_get_prompt(menu),
659					     (sym_has_value(sym) || !sym_is_changeable(sym)) ?
660					     "" : " (NEW)");
661				item_set_tag('s');
662				item_set_data(menu);
663				goto conf_childs;
664			}
665		}
666		item_add_str("%*c%s%s", indent + 1, ' ', menu_get_prompt(menu),
667			  (sym_has_value(sym) || !sym_is_changeable(sym)) ?
668			  "" : " (NEW)");
669		if (menu->prompt->type == P_MENU) {
670			item_add_str("  %s", menu_is_empty(menu) ? "----" : "--->");
671			return;
672		}
673	}
674
675conf_childs:
676	indent += doint;
677	for (child = menu->list; child; child = child->next)
678		build_conf(child);
679	indent -= doint;
680}
681
682static void conf_choice(struct menu *menu)
683{
684	const char *prompt = menu_get_prompt(menu);
685	struct menu *child;
686	struct symbol *active;
687
688	active = sym_get_choice_value(menu->sym);
689	while (1) {
690		int res;
691		int selected;
692		item_reset();
693
694		current_menu = menu;
695		for (child = menu->list; child; child = child->next) {
696			if (!menu_is_visible(child))
697				continue;
698			if (child->sym)
699				item_make("%s", menu_get_prompt(child));
700			else {
701				item_make("*** %s ***", menu_get_prompt(child));
702				item_set_tag(':');
703			}
704			item_set_data(child);
705			if (child->sym == active)
706				item_set_selected(1);
707			if (child->sym == sym_get_choice_value(menu->sym))
708				item_set_tag('X');
709		}
710		dialog_clear();
711		res = dialog_checklist(prompt ? prompt : "Main Menu",
712					radiolist_instructions,
713					MENUBOX_HEIGTH_MIN,
714					MENUBOX_WIDTH_MIN,
715					CHECKLIST_HEIGTH_MIN);
716		selected = item_activate_selected();
717		switch (res) {
718		case 0:
719			if (selected) {
720				child = item_data();
721				if (!child->sym)
722					break;
723
724				sym_set_tristate_value(child->sym, yes);
725			}
726			return;
727		case 1:
728			if (selected) {
729				child = item_data();
730				show_help(child);
731				active = child->sym;
732			} else
733				show_help(menu);
734			break;
735		case KEY_ESC:
736			return;
737		case -ERRDISPLAYTOOSMALL:
738			return;
739		}
740	}
741}
742
743static void conf_string(struct menu *menu)
744{
745	const char *prompt = menu_get_prompt(menu);
746
747	while (1) {
748		int res;
749		const char *heading;
750
751		switch (sym_get_type(menu->sym)) {
752		case S_INT:
753			heading = inputbox_instructions_int;
754			break;
755		case S_HEX:
756			heading = inputbox_instructions_hex;
757			break;
758		case S_STRING:
759			heading = inputbox_instructions_string;
760			break;
761		default:
762			heading = "Internal mconf error!";
763		}
764		dialog_clear();
765		res = dialog_inputbox(prompt ? prompt : "Main Menu",
766				      heading, 10, 75,
767				      sym_get_string_value(menu->sym));
768		switch (res) {
769		case 0:
770			if (sym_set_string_value(menu->sym, dialog_input_result))
771				return;
772			show_textbox(NULL, "You have made an invalid entry.", 5, 43);
773			break;
774		case 1:
775			show_help(menu);
776			break;
777		case KEY_ESC:
778			return;
779		}
780	}
781}
782
783static void conf_load(void)
784{
785
786	while (1) {
787		int res;
788		dialog_clear();
789		res = dialog_inputbox(NULL, load_config_text,
790				      11, 55, filename);
791		switch(res) {
792		case 0:
793			if (!dialog_input_result[0])
794				return;
795			if (!conf_read(dialog_input_result)) {
796				set_config_filename(dialog_input_result);
797				conf_set_changed(true);
798				return;
799			}
800			show_textbox(NULL, "File does not exist!", 5, 38);
801			break;
802		case 1:
803			show_helptext("Load Alternate Configuration", load_config_help);
804			break;
805		case KEY_ESC:
806			return;
807		}
808	}
809}
810
811static void conf_save(void)
812{
813	while (1) {
814		int res;
815		dialog_clear();
816		res = dialog_inputbox(NULL, save_config_text,
817				      11, 55, filename);
818		switch(res) {
819		case 0:
820			if (!dialog_input_result[0])
821				return;
822			if (!conf_write(dialog_input_result)) {
823				set_config_filename(dialog_input_result);
824				return;
825			}
826			show_textbox(NULL, "Can't create file!", 5, 60);
827			break;
828		case 1:
829			show_helptext("Save Alternate Configuration", save_config_help);
830			break;
831		case KEY_ESC:
832			return;
833		}
834	}
835}
836
837static void conf(struct menu *menu, struct menu *active_menu)
838{
839	struct menu *submenu;
840	const char *prompt = menu_get_prompt(menu);
841	struct subtitle_part stpart;
842	struct symbol *sym;
843	int res;
844	int s_scroll = 0;
845
846	if (menu != &rootmenu)
847		stpart.text = menu_get_prompt(menu);
848	else
849		stpart.text = NULL;
850	list_add_tail(&stpart.entries, &trail);
851
852	while (1) {
853		item_reset();
854		current_menu = menu;
855		build_conf(menu);
856		if (!child_count)
857			break;
858		set_subtitle();
859		dialog_clear();
860		res = dialog_menu(prompt ? prompt : "Main Menu",
861				  menu_instructions,
862				  active_menu, &s_scroll);
863		if (res == 1 || res == KEY_ESC || res == -ERRDISPLAYTOOSMALL)
864			break;
865		if (item_count() != 0) {
866			if (!item_activate_selected())
867				continue;
868			if (!item_tag())
869				continue;
870		}
871		submenu = item_data();
872		active_menu = item_data();
873		if (submenu)
874			sym = submenu->sym;
875		else
876			sym = NULL;
877
878		switch (res) {
879		case 0:
880			switch (item_tag()) {
881			case 'm':
882				if (single_menu_mode)
883					submenu->data = (void *) (long) !submenu->data;
884				else
885					conf(submenu, NULL);
886				break;
887			case 't':
888				if (sym_is_choice(sym) && sym_get_tristate_value(sym) == yes)
889					conf_choice(submenu);
890				else if (submenu->prompt->type == P_MENU)
891					conf(submenu, NULL);
892				break;
893			case 's':
894				conf_string(submenu);
895				break;
896			}
897			break;
898		case 2:
899			if (sym)
900				show_help(submenu);
901			else {
902				reset_subtitle();
903				show_helptext("README", mconf_readme);
904			}
905			break;
906		case 3:
907			reset_subtitle();
908			conf_save();
909			break;
910		case 4:
911			reset_subtitle();
912			conf_load();
913			break;
914		case 5:
915			if (item_is_tag('t')) {
916				if (sym_set_tristate_value(sym, yes))
917					break;
918				if (sym_set_tristate_value(sym, mod))
919					show_textbox(NULL, setmod_text, 6, 74);
920			}
921			break;
922		case 6:
923			if (item_is_tag('t'))
924				sym_set_tristate_value(sym, no);
925			break;
926		case 7:
927			if (item_is_tag('t'))
928				sym_set_tristate_value(sym, mod);
929			break;
930		case 8:
931			if (item_is_tag('t'))
932				sym_toggle_tristate_value(sym);
933			else if (item_is_tag('m'))
934				conf(submenu, NULL);
935			break;
936		case 9:
937			search_conf();
938			break;
939		case 10:
940			show_all_options = !show_all_options;
941			break;
942		}
943	}
944
945	list_del(trail.prev);
946}
947
948static void conf_message_callback(const char *s)
949{
950	if (save_and_exit) {
951		if (!silent)
952			printf("%s", s);
953	} else {
954		show_textbox(NULL, s, 6, 60);
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		conf_write_autoconf(0);
985		/* fall through */
986	case -1:
987		if (!silent)
988			printf("\n\n"
989				 "*** End of the configuration.\n"
990				 "*** Execute 'make' to start the build or try 'make help'."
991				 "\n\n");
992		res = 0;
993		break;
994	default:
995		if (!silent)
996			fprintf(stderr, "\n\n"
997					  "Your configuration changes were NOT saved."
998					  "\n\n");
999		if (res != KEY_ESC)
1000			res = 0;
1001	}
1002
1003	return res;
1004}
1005
1006static void sig_handler(int signo)
1007{
1008	exit(handle_exit());
1009}
1010
1011int main(int ac, char **av)
1012{
1013	char *mode;
1014	int res;
1015
1016	signal(SIGINT, sig_handler);
1017
1018	if (ac > 1 && strcmp(av[1], "-s") == 0) {
1019		silent = 1;
1020		/* Silence conf_read() until the real callback is set up */
1021		conf_set_message_callback(NULL);
1022		av++;
1023	}
1024	conf_parse(av[1]);
1025	conf_read(NULL);
1026
1027	mode = getenv("MENUCONFIG_MODE");
1028	if (mode) {
1029		if (!strcasecmp(mode, "single_menu"))
1030			single_menu_mode = 1;
1031	}
1032
1033	if (init_dialog(NULL)) {
1034		fprintf(stderr, "Your display is too small to run Menuconfig!\n");
1035		fprintf(stderr, "It must be at least 19 lines by 80 columns.\n");
1036		return 1;
1037	}
1038
1039	set_config_filename(conf_get_configname());
1040	conf_set_message_callback(conf_message_callback);
1041	do {
1042		conf(&rootmenu, NULL);
1043		res = handle_exit();
1044	} while (res == KEY_ESC);
1045
1046	return res;
1047}
1048