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