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