xref: /openbmc/linux/scripts/kconfig/mconf.c (revision 45897213)
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 
28 static char menu_backtitle[128];
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. (default)\n"
175 "\n"),
176 menu_instructions[] = N_(
177 	"Arrow keys navigate the menu.  "
178 	"<Enter> selects submenus --->.  "
179 	"Highlighted letters are hotkeys.  "
180 	"Pressing <Y> includes, <N> excludes, <M> modularizes features.  "
181 	"Press <Esc><Esc> to exit, <?> for Help, </> for Search.  "
182 	"Legend: [*] built-in  [ ] excluded  <M> module  < > module capable"),
183 radiolist_instructions[] = N_(
184 	"Use the arrow keys to navigate this window or "
185 	"press the hotkey of the item you wish to select "
186 	"followed by the <SPACE BAR>. "
187 	"Press <?> for additional information about this option."),
188 inputbox_instructions_int[] = N_(
189 	"Please enter a decimal value. "
190 	"Fractions will not be accepted.  "
191 	"Use the <TAB> key to move from the input field to the buttons below it."),
192 inputbox_instructions_hex[] = N_(
193 	"Please enter a hexadecimal value. "
194 	"Use the <TAB> key to move from the input field to the buttons below it."),
195 inputbox_instructions_string[] = N_(
196 	"Please enter a string value. "
197 	"Use the <TAB> key to move from the input field to the buttons below it."),
198 setmod_text[] = N_(
199 	"This feature depends on another which has been configured as a module.\n"
200 	"As a result, this feature will be built as a module."),
201 nohelp_text[] = N_(
202 	"There is no help available for this kernel option.\n"),
203 load_config_text[] = N_(
204 	"Enter the name of the configuration file you wish to load.  "
205 	"Accept the name shown to restore the configuration you "
206 	"last retrieved.  Leave blank to abort."),
207 load_config_help[] = N_(
208 	"\n"
209 	"For various reasons, one may wish to keep several different kernel\n"
210 	"configurations available on a single machine.\n"
211 	"\n"
212 	"If you have saved a previous configuration in a file other than the\n"
213 	"kernel's default, entering the name of the file here will allow you\n"
214 	"to modify that configuration.\n"
215 	"\n"
216 	"If you are uncertain, then you have probably never used alternate\n"
217 	"configuration files.  You should therefor leave this blank to abort.\n"),
218 save_config_text[] = N_(
219 	"Enter a filename to which this configuration should be saved "
220 	"as an alternate.  Leave blank to abort."),
221 save_config_help[] = N_(
222 	"\n"
223 	"For various reasons, one may wish to keep different kernel\n"
224 	"configurations available on a single machine.\n"
225 	"\n"
226 	"Entering a file name here will allow you to later retrieve, modify\n"
227 	"and use the current configuration as an alternate to whatever\n"
228 	"configuration options you have selected at that time.\n"
229 	"\n"
230 	"If you are uncertain what all this means then you should probably\n"
231 	"leave this blank.\n"),
232 search_help[] = N_(
233 	"\n"
234 	"Search for CONFIG_ symbols and display their relations.\n"
235 	"Regular expressions are allowed.\n"
236 	"Example: search for \"^FOO\"\n"
237 	"Result:\n"
238 	"-----------------------------------------------------------------\n"
239 	"Symbol: FOO [=m]\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, MCA, ISA)\n"
245 	"    -> PCI support (PCI [=y])\n"
246 	"      -> PCI access mode (<choice> [=y])\n"
247 	"Selects: LIBCRC32\n"
248 	"Selected by: BAR\n"
249 	"-----------------------------------------------------------------\n"
250 	"o The line 'Prompt:' shows the text used in the menu structure for\n"
251 	"  this CONFIG_ symbol\n"
252 	"o The 'Defined at' line tell at what file / line number the symbol\n"
253 	"  is defined\n"
254 	"o The 'Depends on:' line tell what symbols needs to be defined for\n"
255 	"  this symbol to be visible in the menu (selectable)\n"
256 	"o The 'Location:' lines tell where in the menu structure this symbol\n"
257 	"  is located\n"
258 	"    A location followed by a [=y] indicate that this is a selectable\n"
259 	"    menu item - and current value is displayed inside brackets.\n"
260 	"o The 'Selects:' line tell what symbol will be automatically\n"
261 	"  selected if this symbol is selected (y or m)\n"
262 	"o The 'Selected by' line tell what symbol has selected this symbol\n"
263 	"\n"
264 	"Only relevant lines are shown.\n"
265 	"\n\n"
266 	"Search examples:\n"
267 	"Examples: USB	=> find all CONFIG_ symbols containing USB\n"
268 	"          ^USB => find all CONFIG_ symbols starting with USB\n"
269 	"          USB$ => find all CONFIG_ symbols ending with USB\n"
270 	"\n");
271 
272 static char buf[4096], *bufptr = buf;
273 static char input_buf[4096];
274 static char filename[PATH_MAX+1] = ".config";
275 static char *args[1024], **argptr = args;
276 static int indent;
277 static struct termios ios_org;
278 static int rows = 0, cols = 0;
279 static struct menu *current_menu;
280 static int child_count;
281 static int do_resize;
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 static void show_file(const char *filename, const char *title, int r, int c);
293 
294 static void cprint_init(void);
295 static int cprint1(const char *fmt, ...);
296 static void cprint_done(void);
297 static int cprint(const char *fmt, ...);
298 
299 static void init_wsize(void)
300 {
301 	struct winsize ws;
302 	char *env;
303 
304 	if (!ioctl(STDIN_FILENO, TIOCGWINSZ, &ws)) {
305 		rows = ws.ws_row;
306 		cols = ws.ws_col;
307 	}
308 
309 	if (!rows) {
310 		env = getenv("LINES");
311 		if (env)
312 			rows = atoi(env);
313 		if (!rows)
314 			rows = 24;
315 	}
316 	if (!cols) {
317 		env = getenv("COLUMNS");
318 		if (env)
319 			cols = atoi(env);
320 		if (!cols)
321 			cols = 80;
322 	}
323 
324 	if (rows < 19 || cols < 80) {
325 		fprintf(stderr, N_("Your display is too small to run Menuconfig!\n"));
326 		fprintf(stderr, N_("It must be at least 19 lines by 80 columns.\n"));
327 		exit(1);
328 	}
329 
330 	rows -= 4;
331 	cols -= 5;
332 }
333 
334 static void cprint_init(void)
335 {
336 	bufptr = buf;
337 	argptr = args;
338 	memset(args, 0, sizeof(args));
339 	indent = 0;
340 	child_count = 0;
341 	cprint("./scripts/kconfig/lxdialog/lxdialog");
342 	cprint("--backtitle");
343 	cprint(menu_backtitle);
344 }
345 
346 static int cprint1(const char *fmt, ...)
347 {
348 	va_list ap;
349 	int res;
350 
351 	if (!*argptr)
352 		*argptr = bufptr;
353 	va_start(ap, fmt);
354 	res = vsprintf(bufptr, fmt, ap);
355 	va_end(ap);
356 	bufptr += res;
357 
358 	return res;
359 }
360 
361 static void cprint_done(void)
362 {
363 	*bufptr++ = 0;
364 	argptr++;
365 }
366 
367 static int cprint(const char *fmt, ...)
368 {
369 	va_list ap;
370 	int res;
371 
372 	*argptr++ = bufptr;
373 	va_start(ap, fmt);
374 	res = vsprintf(bufptr, fmt, ap);
375 	va_end(ap);
376 	bufptr += res;
377 	*bufptr++ = 0;
378 
379 	return res;
380 }
381 
382 static void get_prompt_str(struct gstr *r, struct property *prop)
383 {
384 	int i, j;
385 	struct menu *submenu[8], *menu;
386 
387 	str_printf(r, "Prompt: %s\n", prop->text);
388 	str_printf(r, "  Defined at %s:%d\n", prop->menu->file->name,
389 		prop->menu->lineno);
390 	if (!expr_is_yes(prop->visible.expr)) {
391 		str_append(r, "  Depends on: ");
392 		expr_gstr_print(prop->visible.expr, r);
393 		str_append(r, "\n");
394 	}
395 	menu = prop->menu->parent;
396 	for (i = 0; menu != &rootmenu && i < 8; menu = menu->parent)
397 		submenu[i++] = menu;
398 	if (i > 0) {
399 		str_printf(r, "  Location:\n");
400 		for (j = 4; --i >= 0; j += 2) {
401 			menu = submenu[i];
402 			str_printf(r, "%*c-> %s", j, ' ', menu_get_prompt(menu));
403 			if (menu->sym) {
404 				str_printf(r, " (%s [=%s])", menu->sym->name ?
405 					menu->sym->name : "<choice>",
406 					sym_get_string_value(menu->sym));
407 			}
408 			str_append(r, "\n");
409 		}
410 	}
411 }
412 
413 static void get_symbol_str(struct gstr *r, struct symbol *sym)
414 {
415 	bool hit;
416 	struct property *prop;
417 
418 	str_printf(r, "Symbol: %s [=%s]\n", sym->name,
419 	                               sym_get_string_value(sym));
420 	for_all_prompts(sym, prop)
421 		get_prompt_str(r, prop);
422 	hit = false;
423 	for_all_properties(sym, prop, P_SELECT) {
424 		if (!hit) {
425 			str_append(r, "  Selects: ");
426 			hit = true;
427 		} else
428 			str_printf(r, " && ");
429 		expr_gstr_print(prop->expr, r);
430 	}
431 	if (hit)
432 		str_append(r, "\n");
433 	if (sym->rev_dep.expr) {
434 		str_append(r, "  Selected by: ");
435 		expr_gstr_print(sym->rev_dep.expr, r);
436 		str_append(r, "\n");
437 	}
438 	str_append(r, "\n\n");
439 }
440 
441 static struct gstr get_relations_str(struct symbol **sym_arr)
442 {
443 	struct symbol *sym;
444 	struct gstr res = str_new();
445 	int i;
446 
447 	for (i = 0; sym_arr && (sym = sym_arr[i]); i++)
448 		get_symbol_str(&res, sym);
449 	if (!i)
450 		str_append(&res, "No matches found.\n");
451 	return res;
452 }
453 
454 pid_t pid;
455 
456 static void winch_handler(int sig)
457 {
458 	if (!do_resize) {
459 		kill(pid, SIGINT);
460 		do_resize = 1;
461 	}
462 }
463 
464 static int exec_conf(void)
465 {
466 	int pipefd[2], stat, size;
467 	struct sigaction sa;
468 	sigset_t sset, osset;
469 
470 	sigemptyset(&sset);
471 	sigaddset(&sset, SIGINT);
472 	sigprocmask(SIG_BLOCK, &sset, &osset);
473 
474 	signal(SIGINT, SIG_DFL);
475 
476 	sa.sa_handler = winch_handler;
477 	sigemptyset(&sa.sa_mask);
478 	sa.sa_flags = SA_RESTART;
479 	sigaction(SIGWINCH, &sa, NULL);
480 
481 	*argptr++ = NULL;
482 
483 	pipe(pipefd);
484 	pid = fork();
485 	if (pid == 0) {
486 		sigprocmask(SIG_SETMASK, &osset, NULL);
487 		dup2(pipefd[1], 2);
488 		close(pipefd[0]);
489 		close(pipefd[1]);
490 		execv(args[0], args);
491 		_exit(EXIT_FAILURE);
492 	}
493 
494 	close(pipefd[1]);
495 	bufptr = input_buf;
496 	while (1) {
497 		size = input_buf + sizeof(input_buf) - bufptr;
498 		size = read(pipefd[0], bufptr, size);
499 		if (size <= 0) {
500 			if (size < 0) {
501 				if (errno == EINTR || errno == EAGAIN)
502 					continue;
503 				perror("read");
504 			}
505 			break;
506 		}
507 		bufptr += size;
508 	}
509 	*bufptr++ = 0;
510 	close(pipefd[0]);
511 	waitpid(pid, &stat, 0);
512 
513 	if (do_resize) {
514 		init_wsize();
515 		do_resize = 0;
516 		sigprocmask(SIG_SETMASK, &osset, NULL);
517 		return -1;
518 	}
519 	if (WIFSIGNALED(stat)) {
520 		printf("\finterrupted(%d)\n", WTERMSIG(stat));
521 		exit(1);
522 	}
523 #if 0
524 	printf("\fexit state: %d\nexit data: '%s'\n", WEXITSTATUS(stat), input_buf);
525 	sleep(1);
526 #endif
527 	sigpending(&sset);
528 	if (sigismember(&sset, SIGINT)) {
529 		printf("\finterrupted\n");
530 		exit(1);
531 	}
532 	sigprocmask(SIG_SETMASK, &osset, NULL);
533 
534 	return WEXITSTATUS(stat);
535 }
536 
537 static void search_conf(void)
538 {
539 	struct symbol **sym_arr;
540 	int stat;
541 	struct gstr res;
542 
543 again:
544 	cprint_init();
545 	cprint("--title");
546 	cprint(_("Search Configuration Parameter"));
547 	cprint("--inputbox");
548 	cprint(_("Enter CONFIG_ (sub)string to search for (omit CONFIG_)"));
549 	cprint("10");
550 	cprint("75");
551 	cprint("");
552 	stat = exec_conf();
553 	if (stat < 0)
554 		goto again;
555 	switch (stat) {
556 	case 0:
557 		break;
558 	case 1:
559 		show_helptext(_("Search Configuration"), search_help);
560 		goto again;
561 	default:
562 		return;
563 	}
564 
565 	sym_arr = sym_re_search(input_buf);
566 	res = get_relations_str(sym_arr);
567 	free(sym_arr);
568 	show_textbox(_("Search Results"), str_get(&res), 0, 0);
569 	str_free(&res);
570 }
571 
572 static void build_conf(struct menu *menu)
573 {
574 	struct symbol *sym;
575 	struct property *prop;
576 	struct menu *child;
577 	int type, tmp, doint = 2;
578 	tristate val;
579 	char ch;
580 
581 	if (!menu_is_visible(menu))
582 		return;
583 
584 	sym = menu->sym;
585 	prop = menu->prompt;
586 	if (!sym) {
587 		if (prop && menu != current_menu) {
588 			const char *prompt = menu_get_prompt(menu);
589 			switch (prop->type) {
590 			case P_MENU:
591 				child_count++;
592 				cprint("m%p", menu);
593 
594 				if (single_menu_mode) {
595 					cprint1("%s%*c%s",
596 						menu->data ? "-->" : "++>",
597 						indent + 1, ' ', prompt);
598 				} else
599 					cprint1("   %*c%s  --->", indent + 1, ' ', prompt);
600 
601 				cprint_done();
602 				if (single_menu_mode && menu->data)
603 					goto conf_childs;
604 				return;
605 			default:
606 				if (prompt) {
607 					child_count++;
608 					cprint(":%p", menu);
609 					cprint("---%*c%s", indent + 1, ' ', prompt);
610 				}
611 			}
612 		} else
613 			doint = 0;
614 		goto conf_childs;
615 	}
616 
617 	type = sym_get_type(sym);
618 	if (sym_is_choice(sym)) {
619 		struct symbol *def_sym = sym_get_choice_value(sym);
620 		struct menu *def_menu = NULL;
621 
622 		child_count++;
623 		for (child = menu->list; child; child = child->next) {
624 			if (menu_is_visible(child) && child->sym == def_sym)
625 				def_menu = child;
626 		}
627 
628 		val = sym_get_tristate_value(sym);
629 		if (sym_is_changable(sym)) {
630 			cprint("t%p", menu);
631 			switch (type) {
632 			case S_BOOLEAN:
633 				cprint1("[%c]", val == no ? ' ' : '*');
634 				break;
635 			case S_TRISTATE:
636 				switch (val) {
637 				case yes: ch = '*'; break;
638 				case mod: ch = 'M'; break;
639 				default:  ch = ' '; break;
640 				}
641 				cprint1("<%c>", ch);
642 				break;
643 			}
644 		} else {
645 			cprint("%c%p", def_menu ? 't' : ':', menu);
646 			cprint1("   ");
647 		}
648 
649 		cprint1("%*c%s", indent + 1, ' ', menu_get_prompt(menu));
650 		if (val == yes) {
651 			if (def_menu) {
652 				cprint1(" (%s)", menu_get_prompt(def_menu));
653 				cprint1("  --->");
654 				cprint_done();
655 				if (def_menu->list) {
656 					indent += 2;
657 					build_conf(def_menu);
658 					indent -= 2;
659 				}
660 			} else
661 				cprint_done();
662 			return;
663 		}
664 		cprint_done();
665 	} else {
666 		if (menu == current_menu) {
667 			cprint(":%p", menu);
668 			cprint("---%*c%s", indent + 1, ' ', menu_get_prompt(menu));
669 			goto conf_childs;
670 		}
671 		child_count++;
672 		val = sym_get_tristate_value(sym);
673 		if (sym_is_choice_value(sym) && val == yes) {
674 			cprint(":%p", menu);
675 			cprint1("   ");
676 		} else {
677 			switch (type) {
678 			case S_BOOLEAN:
679 				cprint("t%p", menu);
680 				if (sym_is_changable(sym))
681 					cprint1("[%c]", val == no ? ' ' : '*');
682 				else
683 					cprint1("---");
684 				break;
685 			case S_TRISTATE:
686 				cprint("t%p", menu);
687 				switch (val) {
688 				case yes: ch = '*'; break;
689 				case mod: ch = 'M'; break;
690 				default:  ch = ' '; break;
691 				}
692 				if (sym_is_changable(sym))
693 					cprint1("<%c>", ch);
694 				else
695 					cprint1("---");
696 				break;
697 			default:
698 				cprint("s%p", menu);
699 				tmp = cprint1("(%s)", sym_get_string_value(sym));
700 				tmp = indent - tmp + 4;
701 				if (tmp < 0)
702 					tmp = 0;
703 				cprint1("%*c%s%s", tmp, ' ', menu_get_prompt(menu),
704 					(sym_has_value(sym) || !sym_is_changable(sym)) ?
705 					"" : " (NEW)");
706 				cprint_done();
707 				goto conf_childs;
708 			}
709 		}
710 		cprint1("%*c%s%s", indent + 1, ' ', menu_get_prompt(menu),
711 			(sym_has_value(sym) || !sym_is_changable(sym)) ?
712 			"" : " (NEW)");
713 		if (menu->prompt->type == P_MENU) {
714 			cprint1("  --->");
715 			cprint_done();
716 			return;
717 		}
718 		cprint_done();
719 	}
720 
721 conf_childs:
722 	indent += doint;
723 	for (child = menu->list; child; child = child->next)
724 		build_conf(child);
725 	indent -= doint;
726 }
727 
728 static void conf(struct menu *menu)
729 {
730 	struct menu *submenu;
731 	const char *prompt = menu_get_prompt(menu);
732 	struct symbol *sym;
733 	char active_entry[40];
734 	int stat, type, i;
735 
736 	unlink("lxdialog.scrltmp");
737 	active_entry[0] = 0;
738 	while (1) {
739 		cprint_init();
740 		cprint("--title");
741 		cprint("%s", prompt ? prompt : _("Main Menu"));
742 		cprint("--menu");
743 		cprint(_(menu_instructions));
744 		cprint("%d", rows);
745 		cprint("%d", cols);
746 		cprint("%d", rows - 10);
747 		cprint("%s", active_entry);
748 		current_menu = menu;
749 		build_conf(menu);
750 		if (!child_count)
751 			break;
752 		if (menu == &rootmenu) {
753 			cprint(":");
754 			cprint("--- ");
755 			cprint("L");
756 			cprint(_("    Load an Alternate Configuration File"));
757 			cprint("S");
758 			cprint(_("    Save Configuration to an Alternate File"));
759 		}
760 		stat = exec_conf();
761 		if (stat < 0)
762 			continue;
763 
764 		if (stat == 1 || stat == 255)
765 			break;
766 
767 		type = input_buf[0];
768 		if (!type)
769 			continue;
770 
771 		for (i = 0; input_buf[i] && !isspace(input_buf[i]); i++)
772 			;
773 		if (i >= sizeof(active_entry))
774 			i = sizeof(active_entry) - 1;
775 		input_buf[i] = 0;
776 		strcpy(active_entry, input_buf);
777 
778 		sym = NULL;
779 		submenu = NULL;
780 		if (sscanf(input_buf + 1, "%p", &submenu) == 1)
781 			sym = submenu->sym;
782 
783 		switch (stat) {
784 		case 0:
785 			switch (type) {
786 			case 'm':
787 				if (single_menu_mode)
788 					submenu->data = (void *) (long) !submenu->data;
789 				else
790 					conf(submenu);
791 				break;
792 			case 't':
793 				if (sym_is_choice(sym) && sym_get_tristate_value(sym) == yes)
794 					conf_choice(submenu);
795 				else if (submenu->prompt->type == P_MENU)
796 					conf(submenu);
797 				break;
798 			case 's':
799 				conf_string(submenu);
800 				break;
801 			case 'L':
802 				conf_load();
803 				break;
804 			case 'S':
805 				conf_save();
806 				break;
807 			}
808 			break;
809 		case 2:
810 			if (sym)
811 				show_help(submenu);
812 			else
813 				show_helptext("README", _(mconf_readme));
814 			break;
815 		case 3:
816 			if (type == 't') {
817 				if (sym_set_tristate_value(sym, yes))
818 					break;
819 				if (sym_set_tristate_value(sym, mod))
820 					show_textbox(NULL, setmod_text, 6, 74);
821 			}
822 			break;
823 		case 4:
824 			if (type == 't')
825 				sym_set_tristate_value(sym, no);
826 			break;
827 		case 5:
828 			if (type == 't')
829 				sym_set_tristate_value(sym, mod);
830 			break;
831 		case 6:
832 			if (type == 't')
833 				sym_toggle_tristate_value(sym);
834 			else if (type == 'm')
835 				conf(submenu);
836 			break;
837 		case 7:
838 			search_conf();
839 			break;
840 		}
841 	}
842 }
843 
844 static void show_textbox(const char *title, const char *text, int r, int c)
845 {
846 	int fd;
847 
848 	fd = creat(".help.tmp", 0777);
849 	write(fd, text, strlen(text));
850 	close(fd);
851 	show_file(".help.tmp", title, r, c);
852 	unlink(".help.tmp");
853 }
854 
855 static void show_helptext(const char *title, const char *text)
856 {
857 	show_textbox(title, text, 0, 0);
858 }
859 
860 static void show_help(struct menu *menu)
861 {
862 	struct gstr help = str_new();
863 	struct symbol *sym = menu->sym;
864 
865 	if (sym->help)
866 	{
867 		if (sym->name) {
868 			str_printf(&help, "CONFIG_%s:\n\n", sym->name);
869 			str_append(&help, _(sym->help));
870 			str_append(&help, "\n");
871 		}
872 	} else {
873 		str_append(&help, nohelp_text);
874 	}
875 	get_symbol_str(&help, sym);
876 	show_helptext(menu_get_prompt(menu), str_get(&help));
877 	str_free(&help);
878 }
879 
880 static void show_file(const char *filename, const char *title, int r, int c)
881 {
882 	do {
883 		cprint_init();
884 		if (title) {
885 			cprint("--title");
886 			cprint("%s", title);
887 		}
888 		cprint("--textbox");
889 		cprint("%s", filename);
890 		cprint("%d", r ? r : rows);
891 		cprint("%d", c ? c : cols);
892 	} while (exec_conf() < 0);
893 }
894 
895 static void conf_choice(struct menu *menu)
896 {
897 	const char *prompt = menu_get_prompt(menu);
898 	struct menu *child;
899 	struct symbol *active;
900 	int stat;
901 
902 	active = sym_get_choice_value(menu->sym);
903 	while (1) {
904 		cprint_init();
905 		cprint("--title");
906 		cprint("%s", prompt ? prompt : _("Main Menu"));
907 		cprint("--radiolist");
908 		cprint(_(radiolist_instructions));
909 		cprint("15");
910 		cprint("70");
911 		cprint("6");
912 
913 		current_menu = menu;
914 		for (child = menu->list; child; child = child->next) {
915 			if (!menu_is_visible(child))
916 				continue;
917 			cprint("%p", child);
918 			cprint("%s", menu_get_prompt(child));
919 			if (child->sym == sym_get_choice_value(menu->sym))
920 				cprint("ON");
921 			else if (child->sym == active)
922 				cprint("SELECTED");
923 			else
924 				cprint("OFF");
925 		}
926 
927 		stat = exec_conf();
928 		switch (stat) {
929 		case 0:
930 			if (sscanf(input_buf, "%p", &child) != 1)
931 				break;
932 			sym_set_tristate_value(child->sym, yes);
933 			return;
934 		case 1:
935 			if (sscanf(input_buf, "%p", &child) == 1) {
936 				show_help(child);
937 				active = child->sym;
938 			} else
939 				show_help(menu);
940 			break;
941 		case 255:
942 			return;
943 		}
944 	}
945 }
946 
947 static void conf_string(struct menu *menu)
948 {
949 	const char *prompt = menu_get_prompt(menu);
950 	int stat;
951 
952 	while (1) {
953 		cprint_init();
954 		cprint("--title");
955 		cprint("%s", prompt ? prompt : _("Main Menu"));
956 		cprint("--inputbox");
957 		switch (sym_get_type(menu->sym)) {
958 		case S_INT:
959 			cprint(_(inputbox_instructions_int));
960 			break;
961 		case S_HEX:
962 			cprint(_(inputbox_instructions_hex));
963 			break;
964 		case S_STRING:
965 			cprint(_(inputbox_instructions_string));
966 			break;
967 		default:
968 			/* panic? */;
969 		}
970 		cprint("10");
971 		cprint("75");
972 		cprint("%s", sym_get_string_value(menu->sym));
973 		stat = exec_conf();
974 		switch (stat) {
975 		case 0:
976 			if (sym_set_string_value(menu->sym, input_buf))
977 				return;
978 			show_textbox(NULL, _("You have made an invalid entry."), 5, 43);
979 			break;
980 		case 1:
981 			show_help(menu);
982 			break;
983 		case 255:
984 			return;
985 		}
986 	}
987 }
988 
989 static void conf_load(void)
990 {
991 	int stat;
992 
993 	while (1) {
994 		cprint_init();
995 		cprint("--inputbox");
996 		cprint(load_config_text);
997 		cprint("11");
998 		cprint("55");
999 		cprint("%s", filename);
1000 		stat = exec_conf();
1001 		switch(stat) {
1002 		case 0:
1003 			if (!input_buf[0])
1004 				return;
1005 			if (!conf_read(input_buf))
1006 				return;
1007 			show_textbox(NULL, _("File does not exist!"), 5, 38);
1008 			break;
1009 		case 1:
1010 			show_helptext(_("Load Alternate Configuration"), load_config_help);
1011 			break;
1012 		case 255:
1013 			return;
1014 		}
1015 	}
1016 }
1017 
1018 static void conf_save(void)
1019 {
1020 	int stat;
1021 
1022 	while (1) {
1023 		cprint_init();
1024 		cprint("--inputbox");
1025 		cprint(save_config_text);
1026 		cprint("11");
1027 		cprint("55");
1028 		cprint("%s", filename);
1029 		stat = exec_conf();
1030 		switch(stat) {
1031 		case 0:
1032 			if (!input_buf[0])
1033 				return;
1034 			if (!conf_write(input_buf))
1035 				return;
1036 			show_textbox(NULL, _("Can't create file!  Probably a nonexistent directory."), 5, 60);
1037 			break;
1038 		case 1:
1039 			show_helptext(_("Save Alternate Configuration"), save_config_help);
1040 			break;
1041 		case 255:
1042 			return;
1043 		}
1044 	}
1045 }
1046 
1047 static void conf_cleanup(void)
1048 {
1049 	tcsetattr(1, TCSAFLUSH, &ios_org);
1050 	unlink(".help.tmp");
1051 	unlink("lxdialog.scrltmp");
1052 }
1053 
1054 int main(int ac, char **av)
1055 {
1056 	struct symbol *sym;
1057 	char *mode;
1058 	int stat;
1059 
1060 	setlocale(LC_ALL, "");
1061 	bindtextdomain(PACKAGE, LOCALEDIR);
1062 	textdomain(PACKAGE);
1063 
1064 	conf_parse(av[1]);
1065 	conf_read(NULL);
1066 
1067 	sym = sym_lookup("KERNELVERSION", 0);
1068 	sym_calc_value(sym);
1069 	sprintf(menu_backtitle, _("Linux Kernel v%s Configuration"),
1070 		sym_get_string_value(sym));
1071 
1072 	mode = getenv("MENUCONFIG_MODE");
1073 	if (mode) {
1074 		if (!strcasecmp(mode, "single_menu"))
1075 			single_menu_mode = 1;
1076 	}
1077 
1078 	tcgetattr(1, &ios_org);
1079 	atexit(conf_cleanup);
1080 	init_wsize();
1081 	conf(&rootmenu);
1082 
1083 	do {
1084 		cprint_init();
1085 		cprint("--yesno");
1086 		cprint(_("Do you wish to save your new kernel configuration?"));
1087 		cprint("5");
1088 		cprint("60");
1089 		stat = exec_conf();
1090 	} while (stat < 0);
1091 
1092 	if (stat == 0) {
1093 		if (conf_write(NULL)) {
1094 			fprintf(stderr, _("\n\n"
1095 				"Error during writing of the kernel configuration.\n"
1096 				"Your kernel configuration changes were NOT saved."
1097 				"\n\n"));
1098 			return 1;
1099 		}
1100 		printf(_("\n\n"
1101 			"*** End of Linux kernel configuration.\n"
1102 			"*** Execute 'make' to build the kernel or try 'make help'."
1103 			"\n\n"));
1104 	} else {
1105 		fprintf(stderr, _("\n\n"
1106 			"Your kernel configuration changes were NOT saved."
1107 			"\n\n"));
1108 	}
1109 
1110 	return 0;
1111 }
1112