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