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