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