xref: /openbmc/linux/scripts/kconfig/mconf.c (revision 22b6e7f3)
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 
25 static 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",
181 menu_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",
188 radiolist_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.",
193 inputbox_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.",
197 inputbox_instructions_hex[] =
198 	"Please enter a hexadecimal value. "
199 	"Use the <TAB> key to move from the input field to the buttons below it.",
200 inputbox_instructions_string[] =
201 	"Please enter a string value. "
202 	"Use the <TAB> key to move from the input field to the buttons below it.",
203 setmod_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.",
206 load_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.",
210 load_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",
221 save_config_text[] =
222 	"Enter a filename to which this configuration should be saved "
223 	"as an alternate.  Leave blank to abort.",
224 save_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",
235 search_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 
282 static int indent;
283 static struct menu *current_menu;
284 static int child_count;
285 static int single_menu_mode;
286 static int show_all_options;
287 static int save_and_exit;
288 static int silent;
289 static int jump_key_char;
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 struct search_data {
382 	struct list_head *head;
383 	struct menu *target;
384 };
385 
386 static 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 
399 static 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 
426 int get_jump_key_char(void)
427 {
428 	jump_key_char = next_jump_key(jump_key_char);
429 
430 	return jump_key_char;
431 }
432 
433 static 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 
448 again:
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 
504 static 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 
675 conf_childs:
676 	indent += doint;
677 	for (child = menu->list; child; child = child->next)
678 		build_conf(child);
679 	indent -= doint;
680 }
681 
682 static 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 
743 static 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 
783 static 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 
811 static 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 
837 static 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 
948 static 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 
958 static 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 
1006 static void sig_handler(int signo)
1007 {
1008 	exit(handle_exit());
1009 }
1010 
1011 int 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