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