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