xref: /openbmc/linux/scripts/kconfig/mconf.c (revision c21b37f6)
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 <sys/ioctl.h>
12 #include <sys/wait.h>
13 #include <ctype.h>
14 #include <errno.h>
15 #include <fcntl.h>
16 #include <limits.h>
17 #include <signal.h>
18 #include <stdarg.h>
19 #include <stdlib.h>
20 #include <string.h>
21 #include <termios.h>
22 #include <unistd.h>
23 #include <locale.h>
24 
25 #define LKC_DIRECT_LINK
26 #include "lkc.h"
27 #include "lxdialog/dialog.h"
28 
29 static const char mconf_readme[] = N_(
30 "Overview\n"
31 "--------\n"
32 "Some kernel features may be built directly into the kernel.\n"
33 "Some may be made into loadable runtime modules.  Some features\n"
34 "may be completely removed altogether.  There are also certain\n"
35 "kernel parameters which are not really features, but must be\n"
36 "entered in as decimal or hexadecimal numbers or possibly text.\n"
37 "\n"
38 "Menu items beginning with [*], <M> or [ ] represent features\n"
39 "configured to be built in, modularized or removed respectively.\n"
40 "Pointed brackets <> represent module capable features.\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 removed it.  You may also press the <Space Bar> to cycle\n"
45 "through the available options (ie. 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\n"
52 "   you wish to change or submenu wish to select and press <Enter>.\n"
53 "   Submenus are designated 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> and <Help>\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 "\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 <SPACE BAR> and <B> for those\n"
109 "   who are familiar with less and lynx.\n"
110 "\n"
111 "o  Press <E>, <X>, <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 kernel 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 kernel options listed in a single\n"
152 "menu, rather than the default multimenu hierarchy, run the menuconfig\n"
153 "with 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 nohelp_text[] = N_(
203 	"There is no help available for this kernel option.\n"),
204 load_config_text[] = N_(
205 	"Enter the name of the configuration file you wish to load.  "
206 	"Accept the name shown to restore the configuration you "
207 	"last retrieved.  Leave blank to abort."),
208 load_config_help[] = N_(
209 	"\n"
210 	"For various reasons, one may wish to keep several different kernel\n"
211 	"configurations available on a single machine.\n"
212 	"\n"
213 	"If you have saved a previous configuration in a file other than the\n"
214 	"kernel's default, entering the name of the file here will allow you\n"
215 	"to modify that configuration.\n"
216 	"\n"
217 	"If you are uncertain, then you have probably never used alternate\n"
218 	"configuration files.  You should therefor leave this blank to abort.\n"),
219 save_config_text[] = N_(
220 	"Enter a filename to which this configuration should be saved "
221 	"as an alternate.  Leave blank to abort."),
222 save_config_help[] = N_(
223 	"\n"
224 	"For various reasons, one may wish to keep different kernel\n"
225 	"configurations available on a single machine.\n"
226 	"\n"
227 	"Entering a file name here will allow you to later retrieve, modify\n"
228 	"and use the current configuration as an alternate to whatever\n"
229 	"configuration options you have selected at that time.\n"
230 	"\n"
231 	"If you are uncertain what all this means then you should probably\n"
232 	"leave this blank.\n"),
233 search_help[] = N_(
234 	"\n"
235 	"Search for CONFIG_ symbols and display their relations.\n"
236 	"Regular expressions are allowed.\n"
237 	"Example: search for \"^FOO\"\n"
238 	"Result:\n"
239 	"-----------------------------------------------------------------\n"
240 	"Symbol: FOO [=m]\n"
241 	"Prompt: Foo bus is used to drive the bar HW\n"
242 	"Defined at drivers/pci/Kconfig:47\n"
243 	"Depends on: X86_LOCAL_APIC && X86_IO_APIC || IA64\n"
244 	"Location:\n"
245 	"  -> Bus options (PCI, PCMCIA, EISA, MCA, ISA)\n"
246 	"    -> PCI support (PCI [=y])\n"
247 	"      -> PCI access mode (<choice> [=y])\n"
248 	"Selects: LIBCRC32\n"
249 	"Selected by: BAR\n"
250 	"-----------------------------------------------------------------\n"
251 	"o The line 'Prompt:' shows the text used in the menu structure for\n"
252 	"  this CONFIG_ symbol\n"
253 	"o The 'Defined at' line tell at what file / line number the symbol\n"
254 	"  is defined\n"
255 	"o The 'Depends on:' line tell what symbols needs to be defined for\n"
256 	"  this symbol to be visible in the menu (selectable)\n"
257 	"o The 'Location:' lines tell where in the menu structure this symbol\n"
258 	"  is located\n"
259 	"    A location followed by a [=y] indicate that this is a selectable\n"
260 	"    menu item - and current value is displayed inside brackets.\n"
261 	"o The 'Selects:' line tell what symbol will be automatically\n"
262 	"  selected if this symbol is selected (y or m)\n"
263 	"o The 'Selected by' line tell what symbol has selected this symbol\n"
264 	"\n"
265 	"Only relevant lines are shown.\n"
266 	"\n\n"
267 	"Search examples:\n"
268 	"Examples: USB	=> find all CONFIG_ symbols containing USB\n"
269 	"          ^USB => find all CONFIG_ symbols starting with USB\n"
270 	"          USB$ => find all CONFIG_ symbols ending with USB\n"
271 	"\n");
272 
273 static int indent;
274 static struct termios ios_org;
275 static int rows = 0, cols = 0;
276 static struct menu *current_menu;
277 static int child_count;
278 static int single_menu_mode;
279 
280 static void conf(struct menu *menu);
281 static void conf_choice(struct menu *menu);
282 static void conf_string(struct menu *menu);
283 static void conf_load(void);
284 static void conf_save(void);
285 static void show_textbox(const char *title, const char *text, int r, int c);
286 static void show_helptext(const char *title, const char *text);
287 static void show_help(struct menu *menu);
288 
289 static void init_wsize(void)
290 {
291 	struct winsize ws;
292 	char *env;
293 
294 	if (!ioctl(STDIN_FILENO, TIOCGWINSZ, &ws)) {
295 		rows = ws.ws_row;
296 		cols = ws.ws_col;
297 	}
298 
299 	if (!rows) {
300 		env = getenv("LINES");
301 		if (env)
302 			rows = atoi(env);
303 		if (!rows)
304 			rows = 24;
305 	}
306 	if (!cols) {
307 		env = getenv("COLUMNS");
308 		if (env)
309 			cols = atoi(env);
310 		if (!cols)
311 			cols = 80;
312 	}
313 
314 	if (rows < 19 || cols < 80) {
315 		fprintf(stderr, N_("Your display is too small to run Menuconfig!\n"));
316 		fprintf(stderr, N_("It must be at least 19 lines by 80 columns.\n"));
317 		exit(1);
318 	}
319 
320 	rows -= 4;
321 	cols -= 5;
322 }
323 
324 static void get_prompt_str(struct gstr *r, struct property *prop)
325 {
326 	int i, j;
327 	struct menu *submenu[8], *menu;
328 
329 	str_printf(r, "Prompt: %s\n", prop->text);
330 	str_printf(r, "  Defined at %s:%d\n", prop->menu->file->name,
331 		prop->menu->lineno);
332 	if (!expr_is_yes(prop->visible.expr)) {
333 		str_append(r, "  Depends on: ");
334 		expr_gstr_print(prop->visible.expr, r);
335 		str_append(r, "\n");
336 	}
337 	menu = prop->menu->parent;
338 	for (i = 0; menu != &rootmenu && i < 8; menu = menu->parent)
339 		submenu[i++] = menu;
340 	if (i > 0) {
341 		str_printf(r, "  Location:\n");
342 		for (j = 4; --i >= 0; j += 2) {
343 			menu = submenu[i];
344 			str_printf(r, "%*c-> %s", j, ' ', menu_get_prompt(menu));
345 			if (menu->sym) {
346 				str_printf(r, " (%s [=%s])", menu->sym->name ?
347 					menu->sym->name : "<choice>",
348 					sym_get_string_value(menu->sym));
349 			}
350 			str_append(r, "\n");
351 		}
352 	}
353 }
354 
355 static void get_symbol_str(struct gstr *r, struct symbol *sym)
356 {
357 	bool hit;
358 	struct property *prop;
359 
360 	str_printf(r, "Symbol: %s [=%s]\n", sym->name,
361 	                               sym_get_string_value(sym));
362 	for_all_prompts(sym, prop)
363 		get_prompt_str(r, prop);
364 	hit = false;
365 	for_all_properties(sym, prop, P_SELECT) {
366 		if (!hit) {
367 			str_append(r, "  Selects: ");
368 			hit = true;
369 		} else
370 			str_printf(r, " && ");
371 		expr_gstr_print(prop->expr, r);
372 	}
373 	if (hit)
374 		str_append(r, "\n");
375 	if (sym->rev_dep.expr) {
376 		str_append(r, "  Selected by: ");
377 		expr_gstr_print(sym->rev_dep.expr, r);
378 		str_append(r, "\n");
379 	}
380 	str_append(r, "\n\n");
381 }
382 
383 static struct gstr get_relations_str(struct symbol **sym_arr)
384 {
385 	struct symbol *sym;
386 	struct gstr res = str_new();
387 	int i;
388 
389 	for (i = 0; sym_arr && (sym = sym_arr[i]); i++)
390 		get_symbol_str(&res, sym);
391 	if (!i)
392 		str_append(&res, "No matches found.\n");
393 	return res;
394 }
395 
396 static char filename[PATH_MAX+1];
397 static void set_config_filename(const char *config_filename)
398 {
399 	static char menu_backtitle[PATH_MAX+128];
400 	int size;
401 	struct symbol *sym;
402 
403 	sym = sym_lookup("KERNELVERSION", 0);
404 	sym_calc_value(sym);
405 	size = snprintf(menu_backtitle, sizeof(menu_backtitle),
406 	                _("%s - Linux Kernel v%s Configuration"),
407 		        config_filename, sym_get_string_value(sym));
408 	if (size >= sizeof(menu_backtitle))
409 		menu_backtitle[sizeof(menu_backtitle)-1] = '\0';
410 	set_dialog_backtitle(menu_backtitle);
411 
412 	size = snprintf(filename, sizeof(filename), "%s", config_filename);
413 	if (size >= sizeof(filename))
414 		filename[sizeof(filename)-1] = '\0';
415 }
416 
417 
418 static void search_conf(void)
419 {
420 	struct symbol **sym_arr;
421 	struct gstr res;
422 	char *dialog_input;
423 	int dres;
424 again:
425 	dialog_clear();
426 	dres = dialog_inputbox(_("Search Configuration Parameter"),
427 			      _("Enter CONFIG_ (sub)string to search for "
428 				"(with or without \"CONFIG\")"),
429 			      10, 75, "");
430 	switch (dres) {
431 	case 0:
432 		break;
433 	case 1:
434 		show_helptext(_("Search Configuration"), search_help);
435 		goto again;
436 	default:
437 		return;
438 	}
439 
440 	/* strip CONFIG_ if necessary */
441 	dialog_input = dialog_input_result;
442 	if (strncasecmp(dialog_input_result, "CONFIG_", 7) == 0)
443 		dialog_input += 7;
444 
445 	sym_arr = sym_re_search(dialog_input);
446 	res = get_relations_str(sym_arr);
447 	free(sym_arr);
448 	show_textbox(_("Search Results"), str_get(&res), 0, 0);
449 	str_free(&res);
450 }
451 
452 static void build_conf(struct menu *menu)
453 {
454 	struct symbol *sym;
455 	struct property *prop;
456 	struct menu *child;
457 	int type, tmp, doint = 2;
458 	tristate val;
459 	char ch;
460 
461 	if (!menu_is_visible(menu))
462 		return;
463 
464 	sym = menu->sym;
465 	prop = menu->prompt;
466 	if (!sym) {
467 		if (prop && menu != current_menu) {
468 			const char *prompt = menu_get_prompt(menu);
469 			switch (prop->type) {
470 			case P_MENU:
471 				child_count++;
472 				if (single_menu_mode) {
473 					item_make("%s%*c%s",
474 						  menu->data ? "-->" : "++>",
475 						  indent + 1, ' ', prompt);
476 				} else
477 					item_make("   %*c%s  --->", indent + 1, ' ', prompt);
478 
479 				item_set_tag('m');
480 				item_set_data(menu);
481 				if (single_menu_mode && menu->data)
482 					goto conf_childs;
483 				return;
484 			default:
485 				if (prompt) {
486 					child_count++;
487 					item_make("---%*c%s", indent + 1, ' ', prompt);
488 					item_set_tag(':');
489 					item_set_data(menu);
490 				}
491 			}
492 		} else
493 			doint = 0;
494 		goto conf_childs;
495 	}
496 
497 	type = sym_get_type(sym);
498 	if (sym_is_choice(sym)) {
499 		struct symbol *def_sym = sym_get_choice_value(sym);
500 		struct menu *def_menu = NULL;
501 
502 		child_count++;
503 		for (child = menu->list; child; child = child->next) {
504 			if (menu_is_visible(child) && child->sym == def_sym)
505 				def_menu = child;
506 		}
507 
508 		val = sym_get_tristate_value(sym);
509 		if (sym_is_changable(sym)) {
510 			switch (type) {
511 			case S_BOOLEAN:
512 				item_make("[%c]", val == no ? ' ' : '*');
513 				break;
514 			case S_TRISTATE:
515 				switch (val) {
516 				case yes: ch = '*'; break;
517 				case mod: ch = 'M'; break;
518 				default:  ch = ' '; break;
519 				}
520 				item_make("<%c>", ch);
521 				break;
522 			}
523 			item_set_tag('t');
524 			item_set_data(menu);
525 		} else {
526 			item_make("   ");
527 			item_set_tag(def_menu ? 't' : ':');
528 			item_set_data(menu);
529 		}
530 
531 		item_add_str("%*c%s", indent + 1, ' ', menu_get_prompt(menu));
532 		if (val == yes) {
533 			if (def_menu) {
534 				item_add_str(" (%s)", menu_get_prompt(def_menu));
535 				item_add_str("  --->");
536 				if (def_menu->list) {
537 					indent += 2;
538 					build_conf(def_menu);
539 					indent -= 2;
540 				}
541 			}
542 			return;
543 		}
544 	} else {
545 		if (menu == current_menu) {
546 			item_make("---%*c%s", indent + 1, ' ', menu_get_prompt(menu));
547 			item_set_tag(':');
548 			item_set_data(menu);
549 			goto conf_childs;
550 		}
551 		child_count++;
552 		val = sym_get_tristate_value(sym);
553 		if (sym_is_choice_value(sym) && val == yes) {
554 			item_make("   ");
555 			item_set_tag(':');
556 			item_set_data(menu);
557 		} else {
558 			switch (type) {
559 			case S_BOOLEAN:
560 				if (sym_is_changable(sym))
561 					item_make("[%c]", val == no ? ' ' : '*');
562 				else
563 					item_make("---");
564 				item_set_tag('t');
565 				item_set_data(menu);
566 				break;
567 			case S_TRISTATE:
568 				switch (val) {
569 				case yes: ch = '*'; break;
570 				case mod: ch = 'M'; break;
571 				default:  ch = ' '; break;
572 				}
573 				if (sym_is_changable(sym))
574 					item_make("<%c>", ch);
575 				else
576 					item_make("---");
577 				item_set_tag('t');
578 				item_set_data(menu);
579 				break;
580 			default:
581 				tmp = 2 + strlen(sym_get_string_value(sym)); /* () = 2 */
582 				item_make("(%s)", sym_get_string_value(sym));
583 				tmp = indent - tmp + 4;
584 				if (tmp < 0)
585 					tmp = 0;
586 				item_add_str("%*c%s%s", tmp, ' ', menu_get_prompt(menu),
587 					     (sym_has_value(sym) || !sym_is_changable(sym)) ?
588 					     "" : " (NEW)");
589 				item_set_tag('s');
590 				item_set_data(menu);
591 				goto conf_childs;
592 			}
593 		}
594 		item_add_str("%*c%s%s", indent + 1, ' ', menu_get_prompt(menu),
595 			  (sym_has_value(sym) || !sym_is_changable(sym)) ?
596 			  "" : " (NEW)");
597 		if (menu->prompt->type == P_MENU) {
598 			item_add_str("  --->");
599 			return;
600 		}
601 	}
602 
603 conf_childs:
604 	indent += doint;
605 	for (child = menu->list; child; child = child->next)
606 		build_conf(child);
607 	indent -= doint;
608 }
609 
610 static void conf(struct menu *menu)
611 {
612 	struct menu *submenu;
613 	const char *prompt = menu_get_prompt(menu);
614 	struct symbol *sym;
615 	struct menu *active_menu = NULL;
616 	int res;
617 	int s_scroll = 0;
618 
619 	while (1) {
620 		item_reset();
621 		current_menu = menu;
622 		build_conf(menu);
623 		if (!child_count)
624 			break;
625 		if (menu == &rootmenu) {
626 			item_make("--- ");
627 			item_set_tag(':');
628 			item_make(_("    Load an Alternate Configuration File"));
629 			item_set_tag('L');
630 			item_make(_("    Save an Alternate Configuration File"));
631 			item_set_tag('S');
632 		}
633 		dialog_clear();
634 		res = dialog_menu(prompt ? prompt : _("Main Menu"),
635 				  _(menu_instructions),
636 				  active_menu, &s_scroll);
637 		if (res == 1 || res == KEY_ESC || res == -ERRDISPLAYTOOSMALL)
638 			break;
639 		if (!item_activate_selected())
640 			continue;
641 		if (!item_tag())
642 			continue;
643 
644 		submenu = item_data();
645 		active_menu = item_data();
646 		if (submenu)
647 			sym = submenu->sym;
648 		else
649 			sym = NULL;
650 
651 		switch (res) {
652 		case 0:
653 			switch (item_tag()) {
654 			case 'm':
655 				if (single_menu_mode)
656 					submenu->data = (void *) (long) !submenu->data;
657 				else
658 					conf(submenu);
659 				break;
660 			case 't':
661 				if (sym_is_choice(sym) && sym_get_tristate_value(sym) == yes)
662 					conf_choice(submenu);
663 				else if (submenu->prompt->type == P_MENU)
664 					conf(submenu);
665 				break;
666 			case 's':
667 				conf_string(submenu);
668 				break;
669 			case 'L':
670 				conf_load();
671 				break;
672 			case 'S':
673 				conf_save();
674 				break;
675 			}
676 			break;
677 		case 2:
678 			if (sym)
679 				show_help(submenu);
680 			else
681 				show_helptext("README", _(mconf_readme));
682 			break;
683 		case 3:
684 			if (item_is_tag('t')) {
685 				if (sym_set_tristate_value(sym, yes))
686 					break;
687 				if (sym_set_tristate_value(sym, mod))
688 					show_textbox(NULL, setmod_text, 6, 74);
689 			}
690 			break;
691 		case 4:
692 			if (item_is_tag('t'))
693 				sym_set_tristate_value(sym, no);
694 			break;
695 		case 5:
696 			if (item_is_tag('t'))
697 				sym_set_tristate_value(sym, mod);
698 			break;
699 		case 6:
700 			if (item_is_tag('t'))
701 				sym_toggle_tristate_value(sym);
702 			else if (item_is_tag('m'))
703 				conf(submenu);
704 			break;
705 		case 7:
706 			search_conf();
707 			break;
708 		}
709 	}
710 }
711 
712 static void show_textbox(const char *title, const char *text, int r, int c)
713 {
714 	dialog_clear();
715 	dialog_textbox(title, text, r, c);
716 }
717 
718 static void show_helptext(const char *title, const char *text)
719 {
720 	show_textbox(title, text, 0, 0);
721 }
722 
723 static void show_help(struct menu *menu)
724 {
725 	struct gstr help = str_new();
726 	struct symbol *sym = menu->sym;
727 
728 	if (menu_has_help(menu))
729 	{
730 		if (sym->name) {
731 			str_printf(&help, "CONFIG_%s:\n\n", sym->name);
732 			str_append(&help, _(menu_get_help(menu)));
733 			str_append(&help, "\n");
734 		}
735 	} else {
736 		str_append(&help, nohelp_text);
737 	}
738 	get_symbol_str(&help, sym);
739 	show_helptext(menu_get_prompt(menu), str_get(&help));
740 	str_free(&help);
741 }
742 
743 static void conf_choice(struct menu *menu)
744 {
745 	const char *prompt = menu_get_prompt(menu);
746 	struct menu *child;
747 	struct symbol *active;
748 
749 	active = sym_get_choice_value(menu->sym);
750 	while (1) {
751 		int res;
752 		int selected;
753 		item_reset();
754 
755 		current_menu = menu;
756 		for (child = menu->list; child; child = child->next) {
757 			if (!menu_is_visible(child))
758 				continue;
759 			item_make("%s", menu_get_prompt(child));
760 			item_set_data(child);
761 			if (child->sym == active)
762 				item_set_selected(1);
763 			if (child->sym == sym_get_choice_value(menu->sym))
764 				item_set_tag('X');
765 		}
766 		dialog_clear();
767 		res = dialog_checklist(prompt ? prompt : _("Main Menu"),
768 					_(radiolist_instructions),
769 					 15, 70, 6);
770 		selected = item_activate_selected();
771 		switch (res) {
772 		case 0:
773 			if (selected) {
774 				child = item_data();
775 				sym_set_tristate_value(child->sym, yes);
776 			}
777 			return;
778 		case 1:
779 			if (selected) {
780 				child = item_data();
781 				show_help(child);
782 				active = child->sym;
783 			} else
784 				show_help(menu);
785 			break;
786 		case KEY_ESC:
787 			return;
788 		case -ERRDISPLAYTOOSMALL:
789 			return;
790 		}
791 	}
792 }
793 
794 static void conf_string(struct menu *menu)
795 {
796 	const char *prompt = menu_get_prompt(menu);
797 
798 	while (1) {
799 		int res;
800 		char *heading;
801 
802 		switch (sym_get_type(menu->sym)) {
803 		case S_INT:
804 			heading = _(inputbox_instructions_int);
805 			break;
806 		case S_HEX:
807 			heading = _(inputbox_instructions_hex);
808 			break;
809 		case S_STRING:
810 			heading = _(inputbox_instructions_string);
811 			break;
812 		default:
813 			heading = "Internal mconf error!";
814 		}
815 		dialog_clear();
816 		res = dialog_inputbox(prompt ? prompt : _("Main Menu"),
817 				      heading, 10, 75,
818 				      sym_get_string_value(menu->sym));
819 		switch (res) {
820 		case 0:
821 			if (sym_set_string_value(menu->sym, dialog_input_result))
822 				return;
823 			show_textbox(NULL, _("You have made an invalid entry."), 5, 43);
824 			break;
825 		case 1:
826 			show_help(menu);
827 			break;
828 		case KEY_ESC:
829 			return;
830 		}
831 	}
832 }
833 
834 static void conf_load(void)
835 {
836 
837 	while (1) {
838 		int res;
839 		dialog_clear();
840 		res = dialog_inputbox(NULL, load_config_text,
841 				      11, 55, filename);
842 		switch(res) {
843 		case 0:
844 			if (!dialog_input_result[0])
845 				return;
846 			if (!conf_read(dialog_input_result)) {
847 				set_config_filename(dialog_input_result);
848 				return;
849 			}
850 			show_textbox(NULL, _("File does not exist!"), 5, 38);
851 			break;
852 		case 1:
853 			show_helptext(_("Load Alternate Configuration"), load_config_help);
854 			break;
855 		case KEY_ESC:
856 			return;
857 		}
858 	}
859 }
860 
861 static void conf_save(void)
862 {
863 	while (1) {
864 		int res;
865 		dialog_clear();
866 		res = dialog_inputbox(NULL, save_config_text,
867 				      11, 55, filename);
868 		switch(res) {
869 		case 0:
870 			if (!dialog_input_result[0])
871 				return;
872 			if (!conf_write(dialog_input_result)) {
873 				set_config_filename(dialog_input_result);
874 				return;
875 			}
876 			show_textbox(NULL, _("Can't create file!  Probably a nonexistent directory."), 5, 60);
877 			break;
878 		case 1:
879 			show_helptext(_("Save Alternate Configuration"), save_config_help);
880 			break;
881 		case KEY_ESC:
882 			return;
883 		}
884 	}
885 }
886 
887 static void conf_cleanup(void)
888 {
889 	tcsetattr(1, TCSAFLUSH, &ios_org);
890 }
891 
892 int main(int ac, char **av)
893 {
894 	char *mode;
895 	int res;
896 
897 	setlocale(LC_ALL, "");
898 	bindtextdomain(PACKAGE, LOCALEDIR);
899 	textdomain(PACKAGE);
900 
901 	conf_parse(av[1]);
902 	conf_read(NULL);
903 
904 	mode = getenv("MENUCONFIG_MODE");
905 	if (mode) {
906 		if (!strcasecmp(mode, "single_menu"))
907 			single_menu_mode = 1;
908 	}
909 
910 	tcgetattr(1, &ios_org);
911 	atexit(conf_cleanup);
912 	init_wsize();
913 	reset_dialog();
914 	init_dialog(NULL);
915 	set_config_filename(conf_get_configname());
916 	do {
917 		conf(&rootmenu);
918 		dialog_clear();
919 		if (conf_get_changed())
920 			res = dialog_yesno(NULL,
921 					   _("Do you wish to save your "
922 					     "new kernel configuration?\n"
923 					     "<ESC><ESC> to continue."),
924 					   6, 60);
925 		else
926 			res = -1;
927 	} while (res == KEY_ESC);
928 	end_dialog();
929 
930 	switch (res) {
931 	case 0:
932 		if (conf_write(filename)) {
933 			fprintf(stderr, _("\n\n"
934 				"Error during writing of the kernel configuration.\n"
935 				"Your kernel configuration changes were NOT saved."
936 				"\n\n"));
937 			return 1;
938 		}
939 	case -1:
940 		printf(_("\n\n"
941 			"*** End of Linux kernel configuration.\n"
942 			"*** Execute 'make' to build the kernel or try 'make help'."
943 			"\n\n"));
944 		break;
945 	default:
946 		fprintf(stderr, _("\n\n"
947 			"Your kernel configuration changes were NOT saved."
948 			"\n\n"));
949 	}
950 
951 	return 0;
952 }
953