xref: /openbmc/linux/scripts/kconfig/mconf.c (revision 54618888)
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * Copyright (C) 2002 Roman Zippel <zippel@linux-m68k.org>
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 <strings.h>
19 #include <signal.h>
20 #include <unistd.h>
21 
22 #include "lkc.h"
23 #include "lxdialog/dialog.h"
24 
25 #define JUMP_NB			9
26 
27 static const char mconf_readme[] =
28 "Overview\n"
29 "--------\n"
30 "This interface lets you select features and parameters for the build.\n"
31 "Features can either be built-in, modularized, or ignored. Parameters\n"
32 "must be entered in as decimal or hexadecimal numbers or text.\n"
33 "\n"
34 "Menu items beginning with following braces represent features that\n"
35 "  [ ] can be built in or removed\n"
36 "  < > can be built in, modularized or removed\n"
37 "  { } can be built in or modularized (selected by other feature)\n"
38 "  - - are selected by other feature,\n"
39 "while *, M or whitespace inside braces means to build in, build as\n"
40 "a module or to exclude the feature respectively.\n"
41 "\n"
42 "To change any of these features, highlight it with the cursor\n"
43 "keys and press <Y> to build it in, <M> to make it a module or\n"
44 "<N> to remove it.  You may also press the <Space Bar> to cycle\n"
45 "through the available options (i.e. Y->N->M->Y).\n"
46 "\n"
47 "Some additional keyboard hints:\n"
48 "\n"
49 "Menus\n"
50 "----------\n"
51 "o  Use the Up/Down arrow keys (cursor keys) to highlight the item you\n"
52 "   wish to change or the submenu you wish to select and press <Enter>.\n"
53 "   Submenus are designated by \"--->\", empty ones by \"----\".\n"
54 "\n"
55 "   Shortcut: Press the option's highlighted letter (hotkey).\n"
56 "             Pressing a hotkey more than once will sequence\n"
57 "             through all visible items which use that hotkey.\n"
58 "\n"
59 "   You may also use the <PAGE UP> and <PAGE DOWN> keys to scroll\n"
60 "   unseen options into view.\n"
61 "\n"
62 "o  To exit a menu use the cursor keys to highlight the <Exit> button\n"
63 "   and press <ENTER>.\n"
64 "\n"
65 "   Shortcut: Press <ESC><ESC> or <E> or <X> if there is no hotkey\n"
66 "             using those letters.  You may press a single <ESC>, but\n"
67 "             there is a delayed response which you may find annoying.\n"
68 "\n"
69 "   Also, the <TAB> and cursor keys will cycle between <Select>,\n"
70 "   <Exit>, <Help>, <Save>, and <Load>.\n"
71 "\n"
72 "o  To get help with an item, use the cursor keys to highlight <Help>\n"
73 "   and press <ENTER>.\n"
74 "\n"
75 "   Shortcut: Press <H> or <?>.\n"
76 "\n"
77 "o  To toggle the display of hidden options, press <Z>.\n"
78 "\n"
79 "\n"
80 "Radiolists  (Choice lists)\n"
81 "-----------\n"
82 "o  Use the cursor keys to select the option you wish to set and press\n"
83 "   <S> or the <SPACE BAR>.\n"
84 "\n"
85 "   Shortcut: Press the first letter of the option you wish to set then\n"
86 "             press <S> or <SPACE BAR>.\n"
87 "\n"
88 "o  To see available help for the item, use the cursor keys to highlight\n"
89 "   <Help> and Press <ENTER>.\n"
90 "\n"
91 "   Shortcut: Press <H> or <?>.\n"
92 "\n"
93 "   Also, the <TAB> and cursor keys will cycle between <Select> and\n"
94 "   <Help>\n"
95 "\n"
96 "\n"
97 "Data Entry\n"
98 "-----------\n"
99 "o  Enter the requested information and press <ENTER>\n"
100 "   If you are entering hexadecimal values, it is not necessary to\n"
101 "   add the '0x' prefix to the entry.\n"
102 "\n"
103 "o  For help, use the <TAB> or cursor keys to highlight the help option\n"
104 "   and press <ENTER>.  You can try <TAB><H> as well.\n"
105 "\n"
106 "\n"
107 "Text Box    (Help Window)\n"
108 "--------\n"
109 "o  Use the cursor keys to scroll up/down/left/right.  The VI editor\n"
110 "   keys h,j,k,l function here as do <u>, <d>, <SPACE BAR> and <B> for\n"
111 "   those who are familiar with less and lynx.\n"
112 "\n"
113 "o  Press <E>, <X>, <q>, <Enter> or <Esc><Esc> to exit.\n"
114 "\n"
115 "\n"
116 "Alternate Configuration Files\n"
117 "-----------------------------\n"
118 "Menuconfig supports the use of alternate configuration files for\n"
119 "those who, for various reasons, find it necessary to switch\n"
120 "between different configurations.\n"
121 "\n"
122 "The <Save> button will let you save the current configuration to\n"
123 "a file of your choosing.  Use the <Load> button to load a previously\n"
124 "saved alternate configuration.\n"
125 "\n"
126 "Even if you don't use alternate configuration files, but you find\n"
127 "during a Menuconfig session that you have completely messed up your\n"
128 "settings, you may use the <Load> button to restore your previously\n"
129 "saved settings from \".config\" without 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 an xterm definition which supports\n"
135 "color.  Otherwise, Menuconfig will look rather bad.  Menuconfig will\n"
136 "not display correctly in an 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,\n"
152 "rather 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 
165 "Search\n"
166 "-------\n"
167 "Pressing the forward-slash (/) anywhere brings up a search dialog box.\n"
168 "\n"
169 
170 "Different color themes available\n"
171 "--------------------------------\n"
172 "It is possible to select different color themes using the variable\n"
173 "MENUCONFIG_COLOR. To select a theme use:\n"
174 "\n"
175 "make MENUCONFIG_COLOR=<theme> menuconfig\n"
176 "\n"
177 "Available themes are\n"
178 " mono       => selects colors suitable for monochrome displays\n"
179 " blackbg    => selects a color scheme with black background\n"
180 " classic    => theme with blue background. The classic look\n"
181 " bluetitle  => an LCD friendly version of classic. (default)\n"
182 "\n",
183 menu_instructions[] =
184 	"Arrow keys navigate the menu.  "
185 	"<Enter> selects submenus ---> (or empty submenus ----).  "
186 	"Highlighted letters are hotkeys.  "
187 	"Pressing <Y> includes, <N> excludes, <M> modularizes features.  "
188 	"Press <Esc><Esc> to exit, <?> for Help, </> for Search.  "
189 	"Legend: [*] built-in  [ ] excluded  <M> module  < > module capable",
190 radiolist_instructions[] =
191 	"Use the arrow keys to navigate this window or "
192 	"press the hotkey of the item you wish to select "
193 	"followed by the <SPACE BAR>. "
194 	"Press <?> for additional information about this option.",
195 inputbox_instructions_int[] =
196 	"Please enter a decimal value. "
197 	"Fractions will not be accepted.  "
198 	"Use the <TAB> key to move from the input field to the buttons below it.",
199 inputbox_instructions_hex[] =
200 	"Please enter a hexadecimal value. "
201 	"Use the <TAB> key to move from the input field to the buttons below it.",
202 inputbox_instructions_string[] =
203 	"Please enter a string value. "
204 	"Use the <TAB> key to move from the input field to the buttons below it.",
205 setmod_text[] =
206 	"This feature depends on another which has been configured as a module.\n"
207 	"As a result, this feature will be built as a module.",
208 load_config_text[] =
209 	"Enter the name of the configuration file you wish to load.  "
210 	"Accept the name shown to restore the configuration you "
211 	"last retrieved.  Leave blank to abort.",
212 load_config_help[] =
213 	"\n"
214 	"For various reasons, one may wish to keep several different\n"
215 	"configurations available on a single machine.\n"
216 	"\n"
217 	"If you have saved a previous configuration in a file other than the\n"
218 	"default one, entering its name here will allow you to modify that\n"
219 	"configuration.\n"
220 	"\n"
221 	"If you are uncertain, then you have probably never used alternate\n"
222 	"configuration files. You should therefore leave this blank to abort.\n",
223 save_config_text[] =
224 	"Enter a filename to which this configuration should be saved "
225 	"as an alternate.  Leave blank to abort.",
226 save_config_help[] =
227 	"\n"
228 	"For various reasons, one may wish to keep different configurations\n"
229 	"available on a single machine.\n"
230 	"\n"
231 	"Entering a file name here will allow you to later retrieve, modify\n"
232 	"and use the current configuration as an alternate to whatever\n"
233 	"configuration options you have selected at that time.\n"
234 	"\n"
235 	"If you are uncertain what all this means then you should probably\n"
236 	"leave this blank.\n",
237 search_help[] =
238 	"\n"
239 	"Search for symbols and display their relations.\n"
240 	"Regular expressions are allowed.\n"
241 	"Example: search for \"^FOO\"\n"
242 	"Result:\n"
243 	"-----------------------------------------------------------------\n"
244 	"Symbol: FOO [=m]\n"
245 	"Type  : tristate\n"
246 	"Prompt: Foo bus is used to drive the bar HW\n"
247 	"  Location:\n"
248 	"    -> Bus options (PCI, PCMCIA, EISA, ISA)\n"
249 	"      -> PCI support (PCI [=y])\n"
250 	"(1)     -> PCI access mode (<choice> [=y])\n"
251 	"  Defined at drivers/pci/Kconfig:47\n"
252 	"  Depends on: X86_LOCAL_APIC && X86_IO_APIC || IA64\n"
253 	"  Selects: LIBCRC32\n"
254 	"  Selected by: BAR [=n]\n"
255 	"-----------------------------------------------------------------\n"
256 	"o The line 'Type:' shows the type of the configuration option for\n"
257 	"  this symbol (bool, tristate, string, ...)\n"
258 	"o The line 'Prompt:' shows the text used in the menu structure for\n"
259 	"  this symbol\n"
260 	"o The 'Defined at' line tells at what file / line number the symbol\n"
261 	"  is defined\n"
262 	"o The 'Depends on:' line tells what symbols need to be defined for\n"
263 	"  this symbol to be visible in the menu (selectable)\n"
264 	"o The 'Location:' lines tells where in the menu structure this symbol\n"
265 	"  is located\n"
266 	"    A location followed by a [=y] indicates that this is a\n"
267 	"    selectable menu item - and the current value is displayed inside\n"
268 	"    brackets.\n"
269 	"    Press the key in the (#) prefix to jump directly to that\n"
270 	"    location. You will be returned to the current search results\n"
271 	"    after exiting this new menu.\n"
272 	"o The 'Selects:' line tells what symbols will be automatically\n"
273 	"  selected if this symbol is selected (y or m)\n"
274 	"o The 'Selected by' line tells what symbol has selected this symbol\n"
275 	"\n"
276 	"Only relevant lines are shown.\n"
277 	"\n\n"
278 	"Search examples:\n"
279 	"Examples: USB	=> find all symbols containing USB\n"
280 	"          ^USB => find all symbols starting with USB\n"
281 	"          USB$ => find all symbols ending with USB\n"
282 	"\n";
283 
284 static int indent;
285 static struct menu *current_menu;
286 static int child_count;
287 static int single_menu_mode;
288 static int show_all_options;
289 static int save_and_exit;
290 static int silent;
291 
292 static void conf(struct menu *menu, struct menu *active_menu);
293 static void conf_choice(struct menu *menu);
294 static void conf_string(struct menu *menu);
295 static void conf_load(void);
296 static void conf_save(void);
297 static int show_textbox_ext(const char *title, char *text, int r, int c,
298 			    int *keys, int *vscroll, int *hscroll,
299 			    update_text_fn update_text, void *data);
300 static void show_textbox(const char *title, const char *text, int r, int c);
301 static void show_helptext(const char *title, const char *text);
302 static void show_help(struct menu *menu);
303 
304 static char filename[PATH_MAX+1];
305 static void set_config_filename(const char *config_filename)
306 {
307 	static char menu_backtitle[PATH_MAX+128];
308 
309 	snprintf(menu_backtitle, sizeof(menu_backtitle), "%s - %s",
310 		 config_filename, rootmenu.prompt->text);
311 	set_dialog_backtitle(menu_backtitle);
312 
313 	snprintf(filename, sizeof(filename), "%s", config_filename);
314 }
315 
316 struct subtitle_part {
317 	struct list_head entries;
318 	const char *text;
319 };
320 static LIST_HEAD(trail);
321 
322 static struct subtitle_list *subtitles;
323 static void set_subtitle(void)
324 {
325 	struct subtitle_part *sp;
326 	struct subtitle_list *pos, *tmp;
327 
328 	for (pos = subtitles; pos != NULL; pos = tmp) {
329 		tmp = pos->next;
330 		free(pos);
331 	}
332 
333 	subtitles = NULL;
334 	list_for_each_entry(sp, &trail, entries) {
335 		if (sp->text) {
336 			if (pos) {
337 				pos->next = xcalloc(1, sizeof(*pos));
338 				pos = pos->next;
339 			} else {
340 				subtitles = pos = xcalloc(1, sizeof(*pos));
341 			}
342 			pos->text = sp->text;
343 		}
344 	}
345 
346 	set_dialog_subtitles(subtitles);
347 }
348 
349 static void reset_subtitle(void)
350 {
351 	struct subtitle_list *pos, *tmp;
352 
353 	for (pos = subtitles; pos != NULL; pos = tmp) {
354 		tmp = pos->next;
355 		free(pos);
356 	}
357 	subtitles = NULL;
358 	set_dialog_subtitles(subtitles);
359 }
360 
361 struct search_data {
362 	struct list_head *head;
363 	struct menu **targets;
364 	int *keys;
365 };
366 
367 static void update_text(char *buf, size_t start, size_t end, void *_data)
368 {
369 	struct search_data *data = _data;
370 	struct jump_key *pos;
371 	int k = 0;
372 
373 	list_for_each_entry(pos, data->head, entries) {
374 		if (pos->offset >= start && pos->offset < end) {
375 			char header[4];
376 
377 			if (k < JUMP_NB) {
378 				int key = '0' + (pos->index % JUMP_NB) + 1;
379 
380 				sprintf(header, "(%c)", key);
381 				data->keys[k] = key;
382 				data->targets[k] = pos->target;
383 				k++;
384 			} else {
385 				sprintf(header, "   ");
386 			}
387 
388 			memcpy(buf + pos->offset, header, sizeof(header) - 1);
389 		}
390 	}
391 	data->keys[k] = 0;
392 }
393 
394 static void search_conf(void)
395 {
396 	struct symbol **sym_arr;
397 	struct gstr res;
398 	struct gstr title;
399 	char *dialog_input;
400 	int dres, vscroll = 0, hscroll = 0;
401 	bool again;
402 	struct gstr sttext;
403 	struct subtitle_part stpart;
404 
405 	title = str_new();
406 	str_printf( &title, "Enter (sub)string or regexp to search for "
407 			      "(with or without \"%s\")", CONFIG_);
408 
409 again:
410 	dialog_clear();
411 	dres = dialog_inputbox("Search Configuration Parameter",
412 			      str_get(&title),
413 			      10, 75, "");
414 	switch (dres) {
415 	case 0:
416 		break;
417 	case 1:
418 		show_helptext("Search Configuration", search_help);
419 		goto again;
420 	default:
421 		str_free(&title);
422 		return;
423 	}
424 
425 	/* strip the prefix if necessary */
426 	dialog_input = dialog_input_result;
427 	if (strncasecmp(dialog_input_result, CONFIG_, strlen(CONFIG_)) == 0)
428 		dialog_input += strlen(CONFIG_);
429 
430 	sttext = str_new();
431 	str_printf(&sttext, "Search (%s)", dialog_input_result);
432 	stpart.text = str_get(&sttext);
433 	list_add_tail(&stpart.entries, &trail);
434 
435 	sym_arr = sym_re_search(dialog_input);
436 	do {
437 		LIST_HEAD(head);
438 		struct menu *targets[JUMP_NB];
439 		int keys[JUMP_NB + 1], i;
440 		struct search_data data = {
441 			.head = &head,
442 			.targets = targets,
443 			.keys = keys,
444 		};
445 		struct jump_key *pos, *tmp;
446 
447 		res = get_relations_str(sym_arr, &head);
448 		set_subtitle();
449 		dres = show_textbox_ext("Search Results", str_get(&res), 0, 0,
450 					keys, &vscroll, &hscroll, &update_text,
451 					&data);
452 		again = false;
453 		for (i = 0; i < JUMP_NB && keys[i]; i++)
454 			if (dres == keys[i]) {
455 				conf(targets[i]->parent, targets[i]);
456 				again = true;
457 			}
458 		str_free(&res);
459 		list_for_each_entry_safe(pos, tmp, &head, entries)
460 			free(pos);
461 	} while (again);
462 	free(sym_arr);
463 	str_free(&title);
464 	list_del(trail.prev);
465 	str_free(&sttext);
466 }
467 
468 static void build_conf(struct menu *menu)
469 {
470 	struct symbol *sym;
471 	struct property *prop;
472 	struct menu *child;
473 	int type, tmp, doint = 2;
474 	tristate val;
475 	char ch;
476 	bool visible;
477 
478 	/*
479 	 * note: menu_is_visible() has side effect that it will
480 	 * recalc the value of the symbol.
481 	 */
482 	visible = menu_is_visible(menu);
483 	if (show_all_options && !menu_has_prompt(menu))
484 		return;
485 	else if (!show_all_options && !visible)
486 		return;
487 
488 	sym = menu->sym;
489 	prop = menu->prompt;
490 	if (!sym) {
491 		if (prop && menu != current_menu) {
492 			const char *prompt = menu_get_prompt(menu);
493 			switch (prop->type) {
494 			case P_MENU:
495 				child_count++;
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_changeable(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_changeable(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_changeable(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_changeable(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_changeable(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 *s)
778 {
779 	if (save_and_exit) {
780 		if (!silent)
781 			printf("%s", s);
782 	} else {
783 		show_textbox(NULL, s, 6, 60);
784 	}
785 }
786 
787 static void show_help(struct menu *menu)
788 {
789 	struct gstr help = str_new();
790 
791 	help.max_width = getmaxx(stdscr) - 10;
792 	menu_get_ext_help(menu, &help);
793 
794 	show_helptext(menu_get_prompt(menu), str_get(&help));
795 	str_free(&help);
796 }
797 
798 static void conf_choice(struct menu *menu)
799 {
800 	const char *prompt = menu_get_prompt(menu);
801 	struct menu *child;
802 	struct symbol *active;
803 
804 	active = sym_get_choice_value(menu->sym);
805 	while (1) {
806 		int res;
807 		int selected;
808 		item_reset();
809 
810 		current_menu = menu;
811 		for (child = menu->list; child; child = child->next) {
812 			if (!menu_is_visible(child))
813 				continue;
814 			if (child->sym)
815 				item_make("%s", menu_get_prompt(child));
816 			else {
817 				item_make("*** %s ***", menu_get_prompt(child));
818 				item_set_tag(':');
819 			}
820 			item_set_data(child);
821 			if (child->sym == active)
822 				item_set_selected(1);
823 			if (child->sym == sym_get_choice_value(menu->sym))
824 				item_set_tag('X');
825 		}
826 		dialog_clear();
827 		res = dialog_checklist(prompt ? prompt : "Main Menu",
828 					radiolist_instructions,
829 					MENUBOX_HEIGTH_MIN,
830 					MENUBOX_WIDTH_MIN,
831 					CHECKLIST_HEIGTH_MIN);
832 		selected = item_activate_selected();
833 		switch (res) {
834 		case 0:
835 			if (selected) {
836 				child = item_data();
837 				if (!child->sym)
838 					break;
839 
840 				sym_set_tristate_value(child->sym, yes);
841 			}
842 			return;
843 		case 1:
844 			if (selected) {
845 				child = item_data();
846 				show_help(child);
847 				active = child->sym;
848 			} else
849 				show_help(menu);
850 			break;
851 		case KEY_ESC:
852 			return;
853 		case -ERRDISPLAYTOOSMALL:
854 			return;
855 		}
856 	}
857 }
858 
859 static void conf_string(struct menu *menu)
860 {
861 	const char *prompt = menu_get_prompt(menu);
862 
863 	while (1) {
864 		int res;
865 		const char *heading;
866 
867 		switch (sym_get_type(menu->sym)) {
868 		case S_INT:
869 			heading = inputbox_instructions_int;
870 			break;
871 		case S_HEX:
872 			heading = inputbox_instructions_hex;
873 			break;
874 		case S_STRING:
875 			heading = inputbox_instructions_string;
876 			break;
877 		default:
878 			heading = "Internal mconf error!";
879 		}
880 		dialog_clear();
881 		res = dialog_inputbox(prompt ? prompt : "Main Menu",
882 				      heading, 10, 75,
883 				      sym_get_string_value(menu->sym));
884 		switch (res) {
885 		case 0:
886 			if (sym_set_string_value(menu->sym, dialog_input_result))
887 				return;
888 			show_textbox(NULL, "You have made an invalid entry.", 5, 43);
889 			break;
890 		case 1:
891 			show_help(menu);
892 			break;
893 		case KEY_ESC:
894 			return;
895 		}
896 	}
897 }
898 
899 static void conf_load(void)
900 {
901 
902 	while (1) {
903 		int res;
904 		dialog_clear();
905 		res = dialog_inputbox(NULL, load_config_text,
906 				      11, 55, filename);
907 		switch(res) {
908 		case 0:
909 			if (!dialog_input_result[0])
910 				return;
911 			if (!conf_read(dialog_input_result)) {
912 				set_config_filename(dialog_input_result);
913 				conf_set_changed(true);
914 				return;
915 			}
916 			show_textbox(NULL, "File does not exist!", 5, 38);
917 			break;
918 		case 1:
919 			show_helptext("Load Alternate Configuration", load_config_help);
920 			break;
921 		case KEY_ESC:
922 			return;
923 		}
924 	}
925 }
926 
927 static void conf_save(void)
928 {
929 	while (1) {
930 		int res;
931 		dialog_clear();
932 		res = dialog_inputbox(NULL, save_config_text,
933 				      11, 55, filename);
934 		switch(res) {
935 		case 0:
936 			if (!dialog_input_result[0])
937 				return;
938 			if (!conf_write(dialog_input_result)) {
939 				set_config_filename(dialog_input_result);
940 				return;
941 			}
942 			show_textbox(NULL, "Can't create file!", 5, 60);
943 			break;
944 		case 1:
945 			show_helptext("Save Alternate Configuration", save_config_help);
946 			break;
947 		case KEY_ESC:
948 			return;
949 		}
950 	}
951 }
952 
953 static int handle_exit(void)
954 {
955 	int res;
956 
957 	save_and_exit = 1;
958 	reset_subtitle();
959 	dialog_clear();
960 	if (conf_get_changed())
961 		res = dialog_yesno(NULL,
962 				   "Do you wish to save your new configuration?\n"
963 				     "(Press <ESC><ESC> to continue kernel configuration.)",
964 				   6, 60);
965 	else
966 		res = -1;
967 
968 	end_dialog(saved_x, saved_y);
969 
970 	switch (res) {
971 	case 0:
972 		if (conf_write(filename)) {
973 			fprintf(stderr, "\n\n"
974 					  "Error while writing of the configuration.\n"
975 					  "Your configuration changes were NOT saved."
976 					  "\n\n");
977 			return 1;
978 		}
979 		conf_write_autoconf(0);
980 		/* fall through */
981 	case -1:
982 		if (!silent)
983 			printf("\n\n"
984 				 "*** End of the configuration.\n"
985 				 "*** Execute 'make' to start the build or try 'make help'."
986 				 "\n\n");
987 		res = 0;
988 		break;
989 	default:
990 		if (!silent)
991 			fprintf(stderr, "\n\n"
992 					  "Your configuration changes were NOT saved."
993 					  "\n\n");
994 		if (res != KEY_ESC)
995 			res = 0;
996 	}
997 
998 	return res;
999 }
1000 
1001 static void sig_handler(int signo)
1002 {
1003 	exit(handle_exit());
1004 }
1005 
1006 int main(int ac, char **av)
1007 {
1008 	char *mode;
1009 	int res;
1010 
1011 	signal(SIGINT, sig_handler);
1012 
1013 	if (ac > 1 && strcmp(av[1], "-s") == 0) {
1014 		silent = 1;
1015 		/* Silence conf_read() until the real callback is set up */
1016 		conf_set_message_callback(NULL);
1017 		av++;
1018 	}
1019 	conf_parse(av[1]);
1020 	conf_read(NULL);
1021 
1022 	mode = getenv("MENUCONFIG_MODE");
1023 	if (mode) {
1024 		if (!strcasecmp(mode, "single_menu"))
1025 			single_menu_mode = 1;
1026 	}
1027 
1028 	if (init_dialog(NULL)) {
1029 		fprintf(stderr, "Your display is too small to run Menuconfig!\n");
1030 		fprintf(stderr, "It must be at least 19 lines by 80 columns.\n");
1031 		return 1;
1032 	}
1033 
1034 	set_config_filename(conf_get_configname());
1035 	conf_set_message_callback(conf_message_callback);
1036 	do {
1037 		conf(&rootmenu, NULL);
1038 		res = handle_exit();
1039 	} while (res == KEY_ESC);
1040 
1041 	return res;
1042 }
1043