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