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