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