xref: /openbmc/linux/scripts/kconfig/mconf.c (revision edb749f4)
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 static int save_and_exit;
284 
285 static void conf(struct menu *menu, struct menu *active_menu);
286 static void conf_choice(struct menu *menu);
287 static void conf_string(struct menu *menu);
288 static void conf_load(void);
289 static void conf_save(void);
290 static int show_textbox_ext(const char *title, char *text, int r, int c,
291 			    int *keys, int *vscroll, int *hscroll,
292 			    update_text_fn update_text, void *data);
293 static void show_textbox(const char *title, const char *text, int r, int c);
294 static void show_helptext(const char *title, const char *text);
295 static void show_help(struct menu *menu);
296 
297 static char filename[PATH_MAX+1];
298 static void set_config_filename(const char *config_filename)
299 {
300 	static char menu_backtitle[PATH_MAX+128];
301 	int size;
302 
303 	size = snprintf(menu_backtitle, sizeof(menu_backtitle),
304 	                "%s - %s", config_filename, rootmenu.prompt->text);
305 	if (size >= sizeof(menu_backtitle))
306 		menu_backtitle[sizeof(menu_backtitle)-1] = '\0';
307 	set_dialog_backtitle(menu_backtitle);
308 
309 	size = snprintf(filename, sizeof(filename), "%s", config_filename);
310 	if (size >= sizeof(filename))
311 		filename[sizeof(filename)-1] = '\0';
312 }
313 
314 
315 struct search_data {
316 	struct list_head *head;
317 	struct menu **targets;
318 	int *keys;
319 };
320 
321 static void update_text(char *buf, size_t start, size_t end, void *_data)
322 {
323 	struct search_data *data = _data;
324 	struct jump_key *pos;
325 	int k = 0;
326 
327 	list_for_each_entry(pos, data->head, entries) {
328 		if (pos->offset >= start && pos->offset < end) {
329 			char header[4];
330 
331 			if (k < JUMP_NB) {
332 				int key = '0' + (pos->index % JUMP_NB) + 1;
333 
334 				sprintf(header, "(%c)", key);
335 				data->keys[k] = key;
336 				data->targets[k] = pos->target;
337 				k++;
338 			} else {
339 				sprintf(header, "   ");
340 			}
341 
342 			memcpy(buf + pos->offset, header, sizeof(header) - 1);
343 		}
344 	}
345 	data->keys[k] = 0;
346 }
347 
348 static void search_conf(void)
349 {
350 	struct symbol **sym_arr;
351 	struct gstr res;
352 	struct gstr title;
353 	char *dialog_input;
354 	int dres, vscroll = 0, hscroll = 0;
355 	bool again;
356 
357 	title = str_new();
358 	str_printf( &title, _("Enter %s (sub)string to search for "
359 			      "(with or without \"%s\")"), CONFIG_, CONFIG_);
360 
361 again:
362 	dialog_clear();
363 	dres = dialog_inputbox(_("Search Configuration Parameter"),
364 			      str_get(&title),
365 			      10, 75, "");
366 	switch (dres) {
367 	case 0:
368 		break;
369 	case 1:
370 		show_helptext(_("Search Configuration"), search_help);
371 		goto again;
372 	default:
373 		str_free(&title);
374 		return;
375 	}
376 
377 	/* strip the prefix if necessary */
378 	dialog_input = dialog_input_result;
379 	if (strncasecmp(dialog_input_result, CONFIG_, strlen(CONFIG_)) == 0)
380 		dialog_input += strlen(CONFIG_);
381 
382 	sym_arr = sym_re_search(dialog_input);
383 	do {
384 		LIST_HEAD(head);
385 		struct menu *targets[JUMP_NB];
386 		int keys[JUMP_NB + 1], i;
387 		struct search_data data = {
388 			.head = &head,
389 			.targets = targets,
390 			.keys = keys,
391 		};
392 		struct jump_key *pos, *tmp;
393 
394 		res = get_relations_str(sym_arr, &head);
395 		dres = show_textbox_ext(_("Search Results"), (char *)
396 					str_get(&res), 0, 0, keys, &vscroll,
397 					&hscroll, &update_text, (void *)
398 					&data);
399 		again = false;
400 		for (i = 0; i < JUMP_NB && keys[i]; i++)
401 			if (dres == keys[i]) {
402 				conf(targets[i]->parent, targets[i]);
403 				again = true;
404 			}
405 		str_free(&res);
406 		list_for_each_entry_safe(pos, tmp, &head, entries)
407 			free(pos);
408 	} while (again);
409 	free(sym_arr);
410 	str_free(&title);
411 }
412 
413 static void build_conf(struct menu *menu)
414 {
415 	struct symbol *sym;
416 	struct property *prop;
417 	struct menu *child;
418 	int type, tmp, doint = 2;
419 	tristate val;
420 	char ch;
421 	bool visible;
422 
423 	/*
424 	 * note: menu_is_visible() has side effect that it will
425 	 * recalc the value of the symbol.
426 	 */
427 	visible = menu_is_visible(menu);
428 	if (show_all_options && !menu_has_prompt(menu))
429 		return;
430 	else if (!show_all_options && !visible)
431 		return;
432 
433 	sym = menu->sym;
434 	prop = menu->prompt;
435 	if (!sym) {
436 		if (prop && menu != current_menu) {
437 			const char *prompt = menu_get_prompt(menu);
438 			switch (prop->type) {
439 			case P_MENU:
440 				child_count++;
441 				prompt = _(prompt);
442 				if (single_menu_mode) {
443 					item_make("%s%*c%s",
444 						  menu->data ? "-->" : "++>",
445 						  indent + 1, ' ', prompt);
446 				} else
447 					item_make("   %*c%s  --->", indent + 1, ' ', prompt);
448 
449 				item_set_tag('m');
450 				item_set_data(menu);
451 				if (single_menu_mode && menu->data)
452 					goto conf_childs;
453 				return;
454 			case P_COMMENT:
455 				if (prompt) {
456 					child_count++;
457 					item_make("   %*c*** %s ***", indent + 1, ' ', _(prompt));
458 					item_set_tag(':');
459 					item_set_data(menu);
460 				}
461 				break;
462 			default:
463 				if (prompt) {
464 					child_count++;
465 					item_make("---%*c%s", indent + 1, ' ', _(prompt));
466 					item_set_tag(':');
467 					item_set_data(menu);
468 				}
469 			}
470 		} else
471 			doint = 0;
472 		goto conf_childs;
473 	}
474 
475 	type = sym_get_type(sym);
476 	if (sym_is_choice(sym)) {
477 		struct symbol *def_sym = sym_get_choice_value(sym);
478 		struct menu *def_menu = NULL;
479 
480 		child_count++;
481 		for (child = menu->list; child; child = child->next) {
482 			if (menu_is_visible(child) && child->sym == def_sym)
483 				def_menu = child;
484 		}
485 
486 		val = sym_get_tristate_value(sym);
487 		if (sym_is_changable(sym)) {
488 			switch (type) {
489 			case S_BOOLEAN:
490 				item_make("[%c]", val == no ? ' ' : '*');
491 				break;
492 			case S_TRISTATE:
493 				switch (val) {
494 				case yes: ch = '*'; break;
495 				case mod: ch = 'M'; break;
496 				default:  ch = ' '; break;
497 				}
498 				item_make("<%c>", ch);
499 				break;
500 			}
501 			item_set_tag('t');
502 			item_set_data(menu);
503 		} else {
504 			item_make("   ");
505 			item_set_tag(def_menu ? 't' : ':');
506 			item_set_data(menu);
507 		}
508 
509 		item_add_str("%*c%s", indent + 1, ' ', _(menu_get_prompt(menu)));
510 		if (val == yes) {
511 			if (def_menu) {
512 				item_add_str(" (%s)", _(menu_get_prompt(def_menu)));
513 				item_add_str("  --->");
514 				if (def_menu->list) {
515 					indent += 2;
516 					build_conf(def_menu);
517 					indent -= 2;
518 				}
519 			}
520 			return;
521 		}
522 	} else {
523 		if (menu == current_menu) {
524 			item_make("---%*c%s", indent + 1, ' ', _(menu_get_prompt(menu)));
525 			item_set_tag(':');
526 			item_set_data(menu);
527 			goto conf_childs;
528 		}
529 		child_count++;
530 		val = sym_get_tristate_value(sym);
531 		if (sym_is_choice_value(sym) && val == yes) {
532 			item_make("   ");
533 			item_set_tag(':');
534 			item_set_data(menu);
535 		} else {
536 			switch (type) {
537 			case S_BOOLEAN:
538 				if (sym_is_changable(sym))
539 					item_make("[%c]", val == no ? ' ' : '*');
540 				else
541 					item_make("-%c-", val == no ? ' ' : '*');
542 				item_set_tag('t');
543 				item_set_data(menu);
544 				break;
545 			case S_TRISTATE:
546 				switch (val) {
547 				case yes: ch = '*'; break;
548 				case mod: ch = 'M'; break;
549 				default:  ch = ' '; break;
550 				}
551 				if (sym_is_changable(sym)) {
552 					if (sym->rev_dep.tri == mod)
553 						item_make("{%c}", ch);
554 					else
555 						item_make("<%c>", ch);
556 				} else
557 					item_make("-%c-", ch);
558 				item_set_tag('t');
559 				item_set_data(menu);
560 				break;
561 			default:
562 				tmp = 2 + strlen(sym_get_string_value(sym)); /* () = 2 */
563 				item_make("(%s)", sym_get_string_value(sym));
564 				tmp = indent - tmp + 4;
565 				if (tmp < 0)
566 					tmp = 0;
567 				item_add_str("%*c%s%s", tmp, ' ', _(menu_get_prompt(menu)),
568 					     (sym_has_value(sym) || !sym_is_changable(sym)) ?
569 					     "" : _(" (NEW)"));
570 				item_set_tag('s');
571 				item_set_data(menu);
572 				goto conf_childs;
573 			}
574 		}
575 		item_add_str("%*c%s%s", indent + 1, ' ', _(menu_get_prompt(menu)),
576 			  (sym_has_value(sym) || !sym_is_changable(sym)) ?
577 			  "" : _(" (NEW)"));
578 		if (menu->prompt->type == P_MENU) {
579 			item_add_str("  --->");
580 			return;
581 		}
582 	}
583 
584 conf_childs:
585 	indent += doint;
586 	for (child = menu->list; child; child = child->next)
587 		build_conf(child);
588 	indent -= doint;
589 }
590 
591 static void conf(struct menu *menu, struct menu *active_menu)
592 {
593 	struct menu *submenu;
594 	const char *prompt = menu_get_prompt(menu);
595 	struct symbol *sym;
596 	int res;
597 	int s_scroll = 0;
598 
599 	while (1) {
600 		item_reset();
601 		current_menu = menu;
602 		build_conf(menu);
603 		if (!child_count)
604 			break;
605 		dialog_clear();
606 		res = dialog_menu(prompt ? _(prompt) : _("Main Menu"),
607 				  _(menu_instructions),
608 				  active_menu, &s_scroll);
609 		if (res == 1 || res == KEY_ESC || res == -ERRDISPLAYTOOSMALL)
610 			break;
611 		if (!item_activate_selected())
612 			continue;
613 		if (!item_tag())
614 			continue;
615 
616 		submenu = item_data();
617 		active_menu = item_data();
618 		if (submenu)
619 			sym = submenu->sym;
620 		else
621 			sym = NULL;
622 
623 		switch (res) {
624 		case 0:
625 			switch (item_tag()) {
626 			case 'm':
627 				if (single_menu_mode)
628 					submenu->data = (void *) (long) !submenu->data;
629 				else
630 					conf(submenu, NULL);
631 				break;
632 			case 't':
633 				if (sym_is_choice(sym) && sym_get_tristate_value(sym) == yes)
634 					conf_choice(submenu);
635 				else if (submenu->prompt->type == P_MENU)
636 					conf(submenu, NULL);
637 				break;
638 			case 's':
639 				conf_string(submenu);
640 				break;
641 			}
642 			break;
643 		case 2:
644 			if (sym)
645 				show_help(submenu);
646 			else
647 				show_helptext(_("README"), _(mconf_readme));
648 			break;
649 		case 3:
650 			conf_save();
651 			break;
652 		case 4:
653 			conf_load();
654 			break;
655 		case 5:
656 			if (item_is_tag('t')) {
657 				if (sym_set_tristate_value(sym, yes))
658 					break;
659 				if (sym_set_tristate_value(sym, mod))
660 					show_textbox(NULL, setmod_text, 6, 74);
661 			}
662 			break;
663 		case 6:
664 			if (item_is_tag('t'))
665 				sym_set_tristate_value(sym, no);
666 			break;
667 		case 7:
668 			if (item_is_tag('t'))
669 				sym_set_tristate_value(sym, mod);
670 			break;
671 		case 8:
672 			if (item_is_tag('t'))
673 				sym_toggle_tristate_value(sym);
674 			else if (item_is_tag('m'))
675 				conf(submenu, NULL);
676 			break;
677 		case 9:
678 			search_conf();
679 			break;
680 		case 10:
681 			show_all_options = !show_all_options;
682 			break;
683 		}
684 	}
685 }
686 
687 static int show_textbox_ext(const char *title, char *text, int r, int c, int
688 			    *keys, int *vscroll, int *hscroll, update_text_fn
689 			    update_text, void *data)
690 {
691 	dialog_clear();
692 	return dialog_textbox(title, text, r, c, keys, vscroll, hscroll,
693 			      update_text, data);
694 }
695 
696 static void show_textbox(const char *title, const char *text, int r, int c)
697 {
698 	show_textbox_ext(title, (char *) text, r, c, (int []) {0}, NULL, NULL,
699 			 NULL, NULL);
700 }
701 
702 static void show_helptext(const char *title, const char *text)
703 {
704 	show_textbox(title, text, 0, 0);
705 }
706 
707 static void conf_message_callback(const char *fmt, va_list ap)
708 {
709 	char buf[PATH_MAX+1];
710 
711 	vsnprintf(buf, sizeof(buf), fmt, ap);
712 	if (save_and_exit)
713 		printf("%s", buf);
714 	else
715 		show_textbox(NULL, buf, 6, 60);
716 }
717 
718 static void show_help(struct menu *menu)
719 {
720 	struct gstr help = str_new();
721 
722 	help.max_width = getmaxx(stdscr) - 10;
723 	menu_get_ext_help(menu, &help);
724 
725 	show_helptext(_(menu_get_prompt(menu)), str_get(&help));
726 	str_free(&help);
727 }
728 
729 static void conf_choice(struct menu *menu)
730 {
731 	const char *prompt = _(menu_get_prompt(menu));
732 	struct menu *child;
733 	struct symbol *active;
734 
735 	active = sym_get_choice_value(menu->sym);
736 	while (1) {
737 		int res;
738 		int selected;
739 		item_reset();
740 
741 		current_menu = menu;
742 		for (child = menu->list; child; child = child->next) {
743 			if (!menu_is_visible(child))
744 				continue;
745 			if (child->sym)
746 				item_make("%s", _(menu_get_prompt(child)));
747 			else {
748 				item_make("*** %s ***", _(menu_get_prompt(child)));
749 				item_set_tag(':');
750 			}
751 			item_set_data(child);
752 			if (child->sym == active)
753 				item_set_selected(1);
754 			if (child->sym == sym_get_choice_value(menu->sym))
755 				item_set_tag('X');
756 		}
757 		dialog_clear();
758 		res = dialog_checklist(prompt ? _(prompt) : _("Main Menu"),
759 					_(radiolist_instructions),
760 					 15, 70, 6);
761 		selected = item_activate_selected();
762 		switch (res) {
763 		case 0:
764 			if (selected) {
765 				child = item_data();
766 				if (!child->sym)
767 					break;
768 
769 				sym_set_tristate_value(child->sym, yes);
770 			}
771 			return;
772 		case 1:
773 			if (selected) {
774 				child = item_data();
775 				show_help(child);
776 				active = child->sym;
777 			} else
778 				show_help(menu);
779 			break;
780 		case KEY_ESC:
781 			return;
782 		case -ERRDISPLAYTOOSMALL:
783 			return;
784 		}
785 	}
786 }
787 
788 static void conf_string(struct menu *menu)
789 {
790 	const char *prompt = menu_get_prompt(menu);
791 
792 	while (1) {
793 		int res;
794 		const char *heading;
795 
796 		switch (sym_get_type(menu->sym)) {
797 		case S_INT:
798 			heading = _(inputbox_instructions_int);
799 			break;
800 		case S_HEX:
801 			heading = _(inputbox_instructions_hex);
802 			break;
803 		case S_STRING:
804 			heading = _(inputbox_instructions_string);
805 			break;
806 		default:
807 			heading = _("Internal mconf error!");
808 		}
809 		dialog_clear();
810 		res = dialog_inputbox(prompt ? _(prompt) : _("Main Menu"),
811 				      heading, 10, 75,
812 				      sym_get_string_value(menu->sym));
813 		switch (res) {
814 		case 0:
815 			if (sym_set_string_value(menu->sym, dialog_input_result))
816 				return;
817 			show_textbox(NULL, _("You have made an invalid entry."), 5, 43);
818 			break;
819 		case 1:
820 			show_help(menu);
821 			break;
822 		case KEY_ESC:
823 			return;
824 		}
825 	}
826 }
827 
828 static void conf_load(void)
829 {
830 
831 	while (1) {
832 		int res;
833 		dialog_clear();
834 		res = dialog_inputbox(NULL, load_config_text,
835 				      11, 55, filename);
836 		switch(res) {
837 		case 0:
838 			if (!dialog_input_result[0])
839 				return;
840 			if (!conf_read(dialog_input_result)) {
841 				set_config_filename(dialog_input_result);
842 				sym_set_change_count(1);
843 				return;
844 			}
845 			show_textbox(NULL, _("File does not exist!"), 5, 38);
846 			break;
847 		case 1:
848 			show_helptext(_("Load Alternate Configuration"), load_config_help);
849 			break;
850 		case KEY_ESC:
851 			return;
852 		}
853 	}
854 }
855 
856 static void conf_save(void)
857 {
858 	while (1) {
859 		int res;
860 		dialog_clear();
861 		res = dialog_inputbox(NULL, save_config_text,
862 				      11, 55, filename);
863 		switch(res) {
864 		case 0:
865 			if (!dialog_input_result[0])
866 				return;
867 			if (!conf_write(dialog_input_result)) {
868 				set_config_filename(dialog_input_result);
869 				return;
870 			}
871 			show_textbox(NULL, _("Can't create file!  Probably a nonexistent directory."), 5, 60);
872 			break;
873 		case 1:
874 			show_helptext(_("Save Alternate Configuration"), save_config_help);
875 			break;
876 		case KEY_ESC:
877 			return;
878 		}
879 	}
880 }
881 
882 static int handle_exit(void)
883 {
884 	int res;
885 
886 	save_and_exit = 1;
887 	dialog_clear();
888 	if (conf_get_changed())
889 		res = dialog_yesno(NULL,
890 				   _("Do you wish to save your new configuration ?\n"
891 				     "<ESC><ESC> to continue."),
892 				   6, 60);
893 	else
894 		res = -1;
895 
896 	end_dialog(saved_x, saved_y);
897 
898 	switch (res) {
899 	case 0:
900 		if (conf_write(filename)) {
901 			fprintf(stderr, _("\n\n"
902 					  "Error while writing of the configuration.\n"
903 					  "Your configuration changes were NOT saved."
904 					  "\n\n"));
905 			return 1;
906 		}
907 		/* fall through */
908 	case -1:
909 		printf(_("\n\n"
910 			 "*** End of the configuration.\n"
911 			 "*** Execute 'make' to start the build or try 'make help'."
912 			 "\n\n"));
913 		res = 0;
914 		break;
915 	default:
916 		fprintf(stderr, _("\n\n"
917 				  "Your configuration changes were NOT saved."
918 				  "\n\n"));
919 		if (res != KEY_ESC)
920 			res = 0;
921 	}
922 
923 	return res;
924 }
925 
926 static void sig_handler(int signo)
927 {
928 	exit(handle_exit());
929 }
930 
931 int main(int ac, char **av)
932 {
933 	char *mode;
934 	int res;
935 
936 	setlocale(LC_ALL, "");
937 	bindtextdomain(PACKAGE, LOCALEDIR);
938 	textdomain(PACKAGE);
939 
940 	signal(SIGINT, sig_handler);
941 
942 	conf_parse(av[1]);
943 	conf_read(NULL);
944 
945 	mode = getenv("MENUCONFIG_MODE");
946 	if (mode) {
947 		if (!strcasecmp(mode, "single_menu"))
948 			single_menu_mode = 1;
949 	}
950 
951 	if (init_dialog(NULL)) {
952 		fprintf(stderr, N_("Your display is too small to run Menuconfig!\n"));
953 		fprintf(stderr, N_("It must be at least 19 lines by 80 columns.\n"));
954 		return 1;
955 	}
956 
957 	set_config_filename(conf_get_configname());
958 	conf_set_message_callback(conf_message_callback);
959 	do {
960 		conf(&rootmenu, NULL);
961 		res = handle_exit();
962 	} while (res == KEY_ESC);
963 
964 	return res;
965 }
966 
967