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