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