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