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