xref: /third_party/toybox/kconfig/mconf.c (revision 0f66f451)
1/*
2 * Copyright (C) 2002 Roman Zippel <zippel@linux-m68k.org>
3 * Released under the terms of the GNU GPL v2.0.
4 *
5 * Introduced single menu mode (show all sub-menus in one large tree).
6 * 2002-11-06 Petr Baudis <pasky@ucw.cz>
7 *
8 * i18n, 2005, Arnaldo Carvalho de Melo <acme@conectiva.com.br>
9 */
10
11#include <sys/ioctl.h>
12#include <sys/wait.h>
13#include <ctype.h>
14#include <errno.h>
15#include <fcntl.h>
16#include <limits.h>
17#include <signal.h>
18#include <stdarg.h>
19#include <stdlib.h>
20#include <string.h>
21#include <termios.h>
22#include <unistd.h>
23#include <locale.h>
24
25#define LKC_DIRECT_LINK
26#include "lkc.h"
27#include "lxdialog/dialog.h"
28
29static char menu_backtitle[128];
30static const char mconf_readme[] = N_(
31"Overview\n"
32"--------\n"
33"Some features may be built directly into the project.\n"
34"Some may be made into loadable runtime modules.  Some features\n"
35"may be completely removed altogether.  There are also certain\n"
36"parameters which are not really features, but must be\n"
37"entered in as decimal or hexadecimal numbers or possibly text.\n"
38"\n"
39"Menu items beginning with [*], <M> or [ ] represent features\n"
40"configured to be built in, modularized or removed respectively.\n"
41"Pointed brackets <> represent module capable features.\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, <M> to make it a module or\n"
45"<N> to removed it.  You may also press the <Space Bar> to cycle\n"
46"through the available options (ie. Y->N->M->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"\n"
165"Different color themes available\n"
166"--------------------------------\n"
167"It is possible to select different color themes using the variable\n"
168"MENUCONFIG_COLOR. To select a theme use:\n"
169"\n"
170"make MENUCONFIG_COLOR=<theme> menuconfig\n"
171"\n"
172"Available themes are\n"
173" mono       => selects colors suitable for monochrome displays\n"
174" blackbg    => selects a color scheme with black background\n"
175" classic    => theme with blue background. The classic look\n"
176" bluetitle  => a LCD friendly version of classic. (default)\n"
177"\n"),
178menu_instructions[] = N_(
179	"Arrow keys navigate the menu.  "
180	"<Enter> selects submenus --->.  "
181	"Highlighted letters are hotkeys.  "
182	"Pressing <Y> includes, <N> excludes, <M> modularizes features.  "
183	"Press <Esc><Esc> to exit, <?> for Help, </> for Search.  "
184	"Legend: [*] built-in  [ ] excluded  <M> module  < > module capable"),
185radiolist_instructions[] = N_(
186	"Use the arrow keys to navigate this window or "
187	"press the hotkey of the item you wish to select "
188	"followed by the <SPACE BAR>. "
189	"Press <?> for additional information about this option."),
190inputbox_instructions_int[] = N_(
191	"Please enter a decimal value. "
192	"Fractions will not be accepted.  "
193	"Use the <TAB> key to move from the input field to the buttons below it."),
194inputbox_instructions_hex[] = N_(
195	"Please enter a hexadecimal value. "
196	"Use the <TAB> key to move from the input field to the buttons below it."),
197inputbox_instructions_string[] = N_(
198	"Please enter a string value. "
199	"Use the <TAB> key to move from the input field to the buttons below it."),
200setmod_text[] = N_(
201	"This feature depends on another which has been configured as a module.\n"
202	"As a result, this feature will be built as a module."),
203nohelp_text[] = N_(
204	"There is no help available for this option.\n"),
205load_config_text[] = N_(
206	"Enter the name of the configuration file you wish to load.  "
207	"Accept the name shown to restore the configuration you "
208	"last retrieved.  Leave blank to abort."),
209load_config_help[] = N_(
210	"\n"
211	"For various reasons, one may wish to keep several different\n"
212	"configurations available on a single machine.\n"
213	"\n"
214	"If you have saved a previous configuration in a file other than the\n"
215	"default, entering the name of the file here will allow you\n"
216	"to modify that configuration.\n"
217	"\n"
218	"If you are uncertain, then you have probably never used alternate\n"
219	"configuration files.  You should therefor leave this blank to abort.\n"),
220save_config_text[] = N_(
221	"Enter a filename to which this configuration should be saved "
222	"as an alternate.  Leave blank to abort."),
223save_config_help[] = N_(
224	"\n"
225	"For various reasons, one may wish to keep different\n"
226	"configurations available on a single machine.\n"
227	"\n"
228	"Entering a file name here will allow you to later retrieve, modify\n"
229	"and use the current configuration as an alternate to whatever\n"
230	"configuration options you have selected at that time.\n"
231	"\n"
232	"If you are uncertain what all this means then you should probably\n"
233	"leave this blank.\n"),
234search_help[] = N_(
235	"\n"
236	"Search for CONFIG_ symbols and display their relations.\n"
237	"Regular expressions are allowed.\n"
238	"Example: search for \"^FOO\"\n"
239	"Result:\n"
240	"-----------------------------------------------------------------\n"
241	"Symbol: FOO [=m]\n"
242	"Prompt: Foo bus is used to drive the bar HW\n"
243	"Defined at drivers/pci/Kconfig:47\n"
244	"Depends on: X86_LOCAL_APIC && X86_IO_APIC || IA64\n"
245	"Location:\n"
246	"  -> Bus options (PCI, PCMCIA, EISA, MCA, ISA)\n"
247	"    -> PCI support (PCI [=y])\n"
248	"      -> PCI access mode (<choice> [=y])\n"
249	"Selects: LIBCRC32\n"
250	"Selected by: BAR\n"
251	"-----------------------------------------------------------------\n"
252	"o The line 'Prompt:' shows the text used in the menu structure for\n"
253	"  this CONFIG_ symbol\n"
254	"o The 'Defined at' line tell at what file / line number the symbol\n"
255	"  is defined\n"
256	"o The 'Depends on:' line tell what symbols needs to be defined for\n"
257	"  this symbol to be visible in the menu (selectable)\n"
258	"o The 'Location:' lines tell where in the menu structure this symbol\n"
259	"  is located\n"
260	"    A location followed by a [=y] indicate that this is a selectable\n"
261	"    menu item - and current value is displayed inside brackets.\n"
262	"o The 'Selects:' line tell what symbol will be automatically\n"
263	"  selected if this symbol is selected (y or m)\n"
264	"o The 'Selected by' line tell what symbol has selected this symbol\n"
265	"\n"
266	"Only relevant lines are shown.\n"
267	"\n\n"
268	"Search examples:\n"
269	"Examples: USB	=> find all CONFIG_ symbols containing USB\n"
270	"          ^USB => find all CONFIG_ symbols starting with USB\n"
271	"          USB$ => find all CONFIG_ symbols ending with USB\n"
272	"\n");
273
274static char filename[PATH_MAX+1] = ".config";
275static int indent;
276static struct termios ios_org;
277static int rows = 0, cols = 0;
278static struct menu *current_menu;
279static int child_count;
280static int single_menu_mode;
281
282static void conf(struct menu *menu);
283static void conf_choice(struct menu *menu);
284static void conf_string(struct menu *menu);
285static void conf_load(void);
286static void conf_save(void);
287static void show_textbox(const char *title, const char *text, int r, int c);
288static void show_helptext(const char *title, const char *text);
289static void show_help(struct menu *menu);
290
291static void init_wsize(void)
292{
293	struct winsize ws;
294	char *env;
295
296	if (!ioctl(STDIN_FILENO, TIOCGWINSZ, &ws)) {
297		rows = ws.ws_row;
298		cols = ws.ws_col;
299	}
300
301	if (!rows) {
302		env = getenv("LINES");
303		if (env)
304			rows = atoi(env);
305		if (!rows)
306			rows = 24;
307	}
308	if (!cols) {
309		env = getenv("COLUMNS");
310		if (env)
311			cols = atoi(env);
312		if (!cols)
313			cols = 80;
314	}
315
316	if (rows < 19 || cols < 80) {
317		fprintf(stderr, N_("Your display is too small to run Menuconfig!\n"));
318		fprintf(stderr, N_("It must be at least 19 lines by 80 columns.\n"));
319		exit(1);
320	}
321
322	rows -= 4;
323	cols -= 5;
324}
325
326static void get_prompt_str(struct gstr *r, struct property *prop)
327{
328	int i, j;
329	struct menu *submenu[8], *menu;
330
331	str_printf(r, "Prompt: %s\n", prop->text);
332	str_printf(r, "  Defined at %s:%d\n", prop->menu->file->name,
333		prop->menu->lineno);
334	if (!expr_is_yes(prop->visible.expr)) {
335		str_append(r, "  Depends on: ");
336		expr_gstr_print(prop->visible.expr, r);
337		str_append(r, "\n");
338	}
339	menu = prop->menu->parent;
340	for (i = 0; menu != &rootmenu && i < 8; menu = menu->parent)
341		submenu[i++] = menu;
342	if (i > 0) {
343		str_printf(r, "  Location:\n");
344		for (j = 4; --i >= 0; j += 2) {
345			menu = submenu[i];
346			str_printf(r, "%*c-> %s", j, ' ', menu_get_prompt(menu));
347			if (menu->sym) {
348				str_printf(r, " (%s [=%s])", menu->sym->name ?
349					menu->sym->name : "<choice>",
350					sym_get_string_value(menu->sym));
351			}
352			str_append(r, "\n");
353		}
354	}
355}
356
357static void get_symbol_str(struct gstr *r, struct symbol *sym)
358{
359	bool hit;
360	struct property *prop;
361
362	str_printf(r, "Symbol: %s [=%s]\n", sym->name,
363	                               sym_get_string_value(sym));
364	for_all_prompts(sym, prop)
365		get_prompt_str(r, prop);
366	hit = false;
367	for_all_properties(sym, prop, P_SELECT) {
368		if (!hit) {
369			str_append(r, "  Selects: ");
370			hit = true;
371		} else
372			str_printf(r, " && ");
373		expr_gstr_print(prop->expr, r);
374	}
375	if (hit)
376		str_append(r, "\n");
377	if (sym->rev_dep.expr) {
378		str_append(r, "  Selected by: ");
379		expr_gstr_print(sym->rev_dep.expr, r);
380		str_append(r, "\n");
381	}
382	str_append(r, "\n\n");
383}
384
385static struct gstr get_relations_str(struct symbol **sym_arr)
386{
387	struct symbol *sym;
388	struct gstr res = str_new();
389	int i;
390
391	for (i = 0; sym_arr && (sym = sym_arr[i]); i++)
392		get_symbol_str(&res, sym);
393	if (!i)
394		str_append(&res, "No matches found.\n");
395	return res;
396}
397
398static void search_conf(void)
399{
400	struct symbol **sym_arr;
401	struct gstr res;
402	int dres;
403again:
404	dialog_clear();
405	dres = dialog_inputbox(_("Search Configuration Parameter"),
406			      _("Enter CONFIG_ (sub)string to search for (omit CONFIG_)"),
407			      10, 75, "");
408	switch (dres) {
409	case 0:
410		break;
411	case 1:
412		show_helptext(_("Search Configuration"), search_help);
413		goto again;
414	default:
415		return;
416	}
417
418	sym_arr = sym_re_search(dialog_input_result);
419	res = get_relations_str(sym_arr);
420	free(sym_arr);
421	show_textbox(_("Search Results"), str_get(&res), 0, 0);
422	str_free(&res);
423}
424
425static void build_conf(struct menu *menu)
426{
427	struct symbol *sym;
428	struct property *prop;
429	struct menu *child;
430	int type, tmp, doint = 2;
431	tristate val;
432	char ch;
433
434	if (!menu_is_visible(menu))
435		return;
436
437	sym = menu->sym;
438	prop = menu->prompt;
439	if (!sym) {
440		if (prop && menu != current_menu) {
441			const char *prompt = menu_get_prompt(menu);
442			switch (prop->type) {
443			case P_MENU:
444				child_count++;
445				if (single_menu_mode) {
446					item_make("%s%*c%s",
447						  menu->data ? "-->" : "++>",
448						  indent + 1, ' ', prompt);
449				} else
450					item_make("   %*c%s  --->", indent + 1, ' ', prompt);
451
452				item_set_tag('m');
453				item_set_data(menu);
454				if (single_menu_mode && menu->data)
455					goto conf_childs;
456				return;
457			default:
458				if (prompt) {
459					child_count++;
460					item_make("---%*c%s", indent + 1, ' ', prompt);
461					item_set_tag(':');
462					item_set_data(menu);
463				}
464			}
465		} else
466			doint = 0;
467		goto conf_childs;
468	}
469
470	type = sym_get_type(sym);
471	if (sym_is_choice(sym)) {
472		struct symbol *def_sym = sym_get_choice_value(sym);
473		struct menu *def_menu = NULL;
474
475		child_count++;
476		for (child = menu->list; child; child = child->next) {
477			if (menu_is_visible(child) && child->sym == def_sym)
478				def_menu = child;
479		}
480
481		val = sym_get_tristate_value(sym);
482		if (sym_is_changable(sym)) {
483			switch (type) {
484			case S_BOOLEAN:
485				item_make("[%c]", val == no ? ' ' : '*');
486				break;
487			case S_TRISTATE:
488				switch (val) {
489				case yes: ch = '*'; break;
490				case mod: ch = 'M'; break;
491				default:  ch = ' '; break;
492				}
493				item_make("<%c>", ch);
494				break;
495			}
496			item_set_tag('t');
497			item_set_data(menu);
498		} else {
499			item_make("   ");
500			item_set_tag(def_menu ? 't' : ':');
501			item_set_data(menu);
502		}
503
504		item_add_str("%*c%s", indent + 1, ' ', menu_get_prompt(menu));
505		if (val == yes) {
506			if (def_menu) {
507				item_add_str(" (%s)", menu_get_prompt(def_menu));
508				item_add_str("  --->");
509				if (def_menu->list) {
510					indent += 2;
511					build_conf(def_menu);
512					indent -= 2;
513				}
514			}
515			return;
516		}
517	} else {
518		if (menu == current_menu) {
519			item_make("---%*c%s", indent + 1, ' ', menu_get_prompt(menu));
520			item_set_tag(':');
521			item_set_data(menu);
522			goto conf_childs;
523		}
524		child_count++;
525		val = sym_get_tristate_value(sym);
526		if (sym_is_choice_value(sym) && val == yes) {
527			item_make("   ");
528			item_set_tag(':');
529			item_set_data(menu);
530		} else {
531			switch (type) {
532			case S_BOOLEAN:
533				if (sym_is_changable(sym))
534					item_make("[%c]", val == no ? ' ' : '*');
535				else
536					item_make("---");
537				item_set_tag('t');
538				item_set_data(menu);
539				break;
540			case S_TRISTATE:
541				switch (val) {
542				case yes: ch = '*'; break;
543				case mod: ch = 'M'; break;
544				default:  ch = ' '; break;
545				}
546				if (sym_is_changable(sym))
547					item_make("<%c>", ch);
548				else
549					item_make("---");
550				item_set_tag('t');
551				item_set_data(menu);
552				break;
553			default:
554				tmp = 2 + strlen(sym_get_string_value(sym)); /* () = 2 */
555				item_make("(%s)", sym_get_string_value(sym));
556				tmp = indent - tmp + 4;
557				if (tmp < 0)
558					tmp = 0;
559				item_add_str("%*c%s%s", tmp, ' ', menu_get_prompt(menu),
560					     (sym_has_value(sym) || !sym_is_changable(sym)) ?
561					     "" : " (NEW)");
562				item_set_tag('s');
563				item_set_data(menu);
564				goto conf_childs;
565			}
566		}
567		item_add_str("%*c%s%s", indent + 1, ' ', menu_get_prompt(menu),
568			  (sym_has_value(sym) || !sym_is_changable(sym)) ?
569			  "" : " (NEW)");
570		if (menu->prompt->type == P_MENU) {
571			item_add_str("  --->");
572			return;
573		}
574	}
575
576conf_childs:
577	indent += doint;
578	for (child = menu->list; child; child = child->next)
579		build_conf(child);
580	indent -= doint;
581}
582
583static void conf(struct menu *menu)
584{
585	struct menu *submenu;
586	const char *prompt = menu_get_prompt(menu);
587	struct symbol *sym;
588	struct menu *active_menu = NULL;
589	int res;
590	int s_scroll = 0;
591
592	while (1) {
593		item_reset();
594		current_menu = menu;
595		build_conf(menu);
596		if (!child_count)
597			break;
598		if (menu == &rootmenu) {
599			item_make("--- ");
600			item_set_tag(':');
601			item_make(_("    Load an Alternate Configuration File"));
602			item_set_tag('L');
603			item_make(_("    Save an Alternate Configuration File"));
604			item_set_tag('S');
605		}
606		dialog_clear();
607		res = dialog_menu(prompt ? prompt : _("Main Menu"),
608				  _(menu_instructions),
609				  active_menu, &s_scroll);
610		if (res == 1 || res == KEY_ESC || res == -ERRDISPLAYTOOSMALL)
611			break;
612		if (!item_activate_selected())
613			continue;
614		if (!item_tag())
615			continue;
616
617		submenu = item_data();
618		active_menu = item_data();
619		if (submenu)
620			sym = submenu->sym;
621		else
622			sym = NULL;
623
624		switch (res) {
625		case 0:
626			switch (item_tag()) {
627			case 'm':
628				if (single_menu_mode)
629					submenu->data = (void *) (long) !submenu->data;
630				else
631					conf(submenu);
632				break;
633			case 't':
634				if (sym_is_choice(sym) && sym_get_tristate_value(sym) == yes)
635					conf_choice(submenu);
636				else if (submenu->prompt->type == P_MENU)
637					conf(submenu);
638				break;
639			case 's':
640				conf_string(submenu);
641				break;
642			case 'L':
643				conf_load();
644				break;
645			case 'S':
646				conf_save();
647				break;
648			}
649			break;
650		case 2:
651			if (sym)
652				show_help(submenu);
653			else
654				show_helptext("README", _(mconf_readme));
655			break;
656		case 3:
657			if (item_is_tag('t')) {
658				if (sym_set_tristate_value(sym, yes))
659					break;
660				if (sym_set_tristate_value(sym, mod))
661					show_textbox(NULL, setmod_text, 6, 74);
662			}
663			break;
664		case 4:
665			if (item_is_tag('t'))
666				sym_set_tristate_value(sym, no);
667			break;
668		case 5:
669			if (item_is_tag('t'))
670				sym_set_tristate_value(sym, mod);
671			break;
672		case 6:
673			if (item_is_tag('t'))
674				sym_toggle_tristate_value(sym);
675			else if (item_is_tag('m'))
676				conf(submenu);
677			break;
678		case 7:
679			search_conf();
680			break;
681		}
682	}
683}
684
685static void show_textbox(const char *title, const char *text, int r, int c)
686{
687	dialog_clear();
688	dialog_textbox(title, text, r, c);
689}
690
691static void show_helptext(const char *title, const char *text)
692{
693	show_textbox(title, text, 0, 0);
694}
695
696static void show_help(struct menu *menu)
697{
698	struct gstr help = str_new();
699	struct symbol *sym = menu->sym;
700
701	if (sym->help)
702	{
703		if (sym->name) {
704			str_printf(&help, "CONFIG_%s:\n\n", sym->name);
705			str_append(&help, _(sym->help));
706			str_append(&help, "\n");
707		}
708	} else {
709		str_append(&help, nohelp_text);
710	}
711	get_symbol_str(&help, sym);
712	show_helptext(menu_get_prompt(menu), str_get(&help));
713	str_free(&help);
714}
715
716static void conf_choice(struct menu *menu)
717{
718	const char *prompt = menu_get_prompt(menu);
719	struct menu *child;
720	struct symbol *active;
721
722	active = sym_get_choice_value(menu->sym);
723	while (1) {
724		int res;
725		int selected;
726		item_reset();
727
728		current_menu = menu;
729		for (child = menu->list; child; child = child->next) {
730			if (!menu_is_visible(child))
731				continue;
732			item_make("%s", menu_get_prompt(child));
733			item_set_data(child);
734			if (child->sym == active)
735				item_set_selected(1);
736			if (child->sym == sym_get_choice_value(menu->sym))
737				item_set_tag('X');
738		}
739		dialog_clear();
740		res = dialog_checklist(prompt ? prompt : _("Main Menu"),
741					_(radiolist_instructions),
742					 15, 70, 6);
743		selected = item_activate_selected();
744		switch (res) {
745		case 0:
746			if (selected) {
747				child = item_data();
748				sym_set_tristate_value(child->sym, yes);
749			}
750			return;
751		case 1:
752			if (selected) {
753				child = item_data();
754				show_help(child);
755				active = child->sym;
756			} else
757				show_help(menu);
758			break;
759		case KEY_ESC:
760			return;
761		case -ERRDISPLAYTOOSMALL:
762			return;
763		}
764	}
765}
766
767static void conf_string(struct menu *menu)
768{
769	const char *prompt = menu_get_prompt(menu);
770
771	while (1) {
772		int res;
773		char *heading;
774
775		switch (sym_get_type(menu->sym)) {
776		case S_INT:
777			heading = (char *)_(inputbox_instructions_int);
778			break;
779		case S_HEX:
780			heading = (char *)_(inputbox_instructions_hex);
781			break;
782		case S_STRING:
783			heading = (char *)_(inputbox_instructions_string);
784			break;
785		default:
786			heading = "Internal mconf error!";
787		}
788		dialog_clear();
789		res = dialog_inputbox(prompt ? prompt : _("Main Menu"),
790				      heading, 10, 75,
791				      sym_get_string_value(menu->sym));
792		switch (res) {
793		case 0:
794			if (sym_set_string_value(menu->sym, dialog_input_result))
795				return;
796			show_textbox(NULL, _("You have made an invalid entry."), 5, 43);
797			break;
798		case 1:
799			show_help(menu);
800			break;
801		case KEY_ESC:
802			return;
803		}
804	}
805}
806
807static void conf_load(void)
808{
809
810	while (1) {
811		int res;
812		dialog_clear();
813		res = dialog_inputbox(NULL, load_config_text,
814				      11, 55, filename);
815		switch(res) {
816		case 0:
817			if (!dialog_input_result[0])
818				return;
819			if (!conf_read(dialog_input_result))
820				return;
821			show_textbox(NULL, _("File does not exist!"), 5, 38);
822			break;
823		case 1:
824			show_helptext(_("Load Alternate Configuration"), load_config_help);
825			break;
826		case KEY_ESC:
827			return;
828		}
829	}
830}
831
832static void conf_save(void)
833{
834	while (1) {
835		int res;
836		dialog_clear();
837		res = dialog_inputbox(NULL, save_config_text,
838				      11, 55, filename);
839		switch(res) {
840		case 0:
841			if (!dialog_input_result[0])
842				return;
843			if (!conf_write(dialog_input_result))
844				return;
845			show_textbox(NULL, _("Can't create file!  Probably a nonexistent directory."), 5, 60);
846			break;
847		case 1:
848			show_helptext(_("Save Alternate Configuration"), save_config_help);
849			break;
850		case KEY_ESC:
851			return;
852		}
853	}
854}
855
856static void conf_cleanup(void)
857{
858	tcsetattr(1, TCSAFLUSH, &ios_org);
859}
860
861int main(int ac, char **av)
862{
863	struct symbol *sym;
864	char *mode;
865	int res;
866
867	setlocale(LC_ALL, "");
868	bindtextdomain(PACKAGE, LOCALEDIR);
869	textdomain(PACKAGE);
870
871	conf_parse(av[1] ? av[1] : "");
872	conf_read(NULL);
873
874	sym = sym_lookup("KERNELVERSION", 0);
875	sym_calc_value(sym);
876	sprintf(menu_backtitle, _(PROJECT_NAME" v%s Configuration"),
877		sym_get_string_value(sym));
878
879	mode = getenv("MENUCONFIG_MODE");
880	if (mode) {
881		if (!strcasecmp(mode, "single_menu"))
882			single_menu_mode = 1;
883	}
884
885	tcgetattr(1, &ios_org);
886	atexit(conf_cleanup);
887	init_wsize();
888	reset_dialog();
889	init_dialog(menu_backtitle);
890	do {
891		conf(&rootmenu);
892		dialog_clear();
893		res = dialog_yesno(NULL,
894				   _("Do you wish to save your "
895				     "new "PROJECT_NAME" configuration?\n"
896				     "<ESC><ESC> to continue."),
897				   6, 60);
898	} while (res == KEY_ESC);
899	end_dialog();
900	if (res == 0) {
901		if (conf_write(NULL)) {
902			fprintf(stderr, _("\n\n"
903				"Error writing "PROJECT_NAME" configuration.\n"
904				"Your configuration changes were NOT saved."
905				"\n\n"));
906			return 1;
907		}
908		printf(_("\n\n"
909			"*** End of "PROJECT_NAME" configuration.\n"
910			"*** Execute 'make' to build, or try 'make help'."
911			"\n\n"));
912	} else {
913		fprintf(stderr, _("\n\n"
914			"Your configuration changes were NOT saved."
915			"\n\n"));
916	}
917
918	return 0;
919}
920