Merge tag 'for-6.4-rc7-tag' of git://git.kernel.org/pub/scm/linux/kernel/git/kdave...
[platform/kernel/linux-starfive.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 #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
294 static char filename[PATH_MAX+1];
295 static void set_config_filename(const char *config_filename)
296 {
297         static char menu_backtitle[PATH_MAX+128];
298
299         snprintf(menu_backtitle, sizeof(menu_backtitle), "%s - %s",
300                  config_filename, rootmenu.prompt->text);
301         set_dialog_backtitle(menu_backtitle);
302
303         snprintf(filename, sizeof(filename), "%s", config_filename);
304 }
305
306 struct subtitle_part {
307         struct list_head entries;
308         const char *text;
309 };
310 static LIST_HEAD(trail);
311
312 static struct subtitle_list *subtitles;
313 static void set_subtitle(void)
314 {
315         struct subtitle_part *sp;
316         struct subtitle_list *pos, *tmp;
317
318         for (pos = subtitles; pos != NULL; pos = tmp) {
319                 tmp = pos->next;
320                 free(pos);
321         }
322
323         subtitles = NULL;
324         list_for_each_entry(sp, &trail, entries) {
325                 if (sp->text) {
326                         if (pos) {
327                                 pos->next = xcalloc(1, sizeof(*pos));
328                                 pos = pos->next;
329                         } else {
330                                 subtitles = pos = xcalloc(1, sizeof(*pos));
331                         }
332                         pos->text = sp->text;
333                 }
334         }
335
336         set_dialog_subtitles(subtitles);
337 }
338
339 static void reset_subtitle(void)
340 {
341         struct subtitle_list *pos, *tmp;
342
343         for (pos = subtitles; pos != NULL; pos = tmp) {
344                 tmp = pos->next;
345                 free(pos);
346         }
347         subtitles = NULL;
348         set_dialog_subtitles(subtitles);
349 }
350
351 static int show_textbox_ext(const char *title, char *text, int r, int c, int
352                             *keys, int *vscroll, int *hscroll, update_text_fn
353                             update_text, void *data)
354 {
355         dialog_clear();
356         return dialog_textbox(title, text, r, c, keys, vscroll, hscroll,
357                               update_text, data);
358 }
359
360 static void show_textbox(const char *title, const char *text, int r, int c)
361 {
362         show_textbox_ext(title, (char *) text, r, c, (int []) {0}, NULL, NULL,
363                          NULL, NULL);
364 }
365
366 static void show_helptext(const char *title, const char *text)
367 {
368         show_textbox(title, text, 0, 0);
369 }
370
371 static void show_help(struct menu *menu)
372 {
373         struct gstr help = str_new();
374
375         help.max_width = getmaxx(stdscr) - 10;
376         menu_get_ext_help(menu, &help);
377
378         show_helptext(menu_get_prompt(menu), str_get(&help));
379         str_free(&help);
380 }
381
382 struct search_data {
383         struct list_head *head;
384         struct menu **targets;
385         int *keys;
386 };
387
388 static void update_text(char *buf, size_t start, size_t end, void *_data)
389 {
390         struct search_data *data = _data;
391         struct jump_key *pos;
392         int k = 0;
393
394         list_for_each_entry(pos, data->head, entries) {
395                 if (pos->offset >= start && pos->offset < end) {
396                         char header[4];
397
398                         if (k < JUMP_NB) {
399                                 int key = '0' + (pos->index % JUMP_NB) + 1;
400
401                                 sprintf(header, "(%c)", key);
402                                 data->keys[k] = key;
403                                 data->targets[k] = pos->target;
404                                 k++;
405                         } else {
406                                 sprintf(header, "   ");
407                         }
408
409                         memcpy(buf + pos->offset, header, sizeof(header) - 1);
410                 }
411         }
412         data->keys[k] = 0;
413 }
414
415 static void search_conf(void)
416 {
417         struct symbol **sym_arr;
418         struct gstr res;
419         struct gstr title;
420         char *dialog_input;
421         int dres, vscroll = 0, hscroll = 0;
422         bool again;
423         struct gstr sttext;
424         struct subtitle_part stpart;
425
426         title = str_new();
427         str_printf( &title, "Enter (sub)string or regexp to search for "
428                               "(with or without \"%s\")", CONFIG_);
429
430 again:
431         dialog_clear();
432         dres = dialog_inputbox("Search Configuration Parameter",
433                               str_get(&title),
434                               10, 75, "");
435         switch (dres) {
436         case 0:
437                 break;
438         case 1:
439                 show_helptext("Search Configuration", search_help);
440                 goto again;
441         default:
442                 str_free(&title);
443                 return;
444         }
445
446         /* strip the prefix if necessary */
447         dialog_input = dialog_input_result;
448         if (strncasecmp(dialog_input_result, CONFIG_, strlen(CONFIG_)) == 0)
449                 dialog_input += strlen(CONFIG_);
450
451         sttext = str_new();
452         str_printf(&sttext, "Search (%s)", dialog_input_result);
453         stpart.text = str_get(&sttext);
454         list_add_tail(&stpart.entries, &trail);
455
456         sym_arr = sym_re_search(dialog_input);
457         do {
458                 LIST_HEAD(head);
459                 struct menu *targets[JUMP_NB];
460                 int keys[JUMP_NB + 1], i;
461                 struct search_data data = {
462                         .head = &head,
463                         .targets = targets,
464                         .keys = keys,
465                 };
466                 struct jump_key *pos, *tmp;
467
468                 res = get_relations_str(sym_arr, &head);
469                 set_subtitle();
470                 dres = show_textbox_ext("Search Results", str_get(&res), 0, 0,
471                                         keys, &vscroll, &hscroll, &update_text,
472                                         &data);
473                 again = false;
474                 for (i = 0; i < JUMP_NB && keys[i]; i++)
475                         if (dres == keys[i]) {
476                                 conf(targets[i]->parent, targets[i]);
477                                 again = true;
478                         }
479                 str_free(&res);
480                 list_for_each_entry_safe(pos, tmp, &head, entries)
481                         free(pos);
482         } while (again);
483         free(sym_arr);
484         str_free(&title);
485         list_del(trail.prev);
486         str_free(&sttext);
487 }
488
489 static void build_conf(struct menu *menu)
490 {
491         struct symbol *sym;
492         struct property *prop;
493         struct menu *child;
494         int type, tmp, doint = 2;
495         tristate val;
496         char ch;
497         bool visible;
498
499         /*
500          * note: menu_is_visible() has side effect that it will
501          * recalc the value of the symbol.
502          */
503         visible = menu_is_visible(menu);
504         if (show_all_options && !menu_has_prompt(menu))
505                 return;
506         else if (!show_all_options && !visible)
507                 return;
508
509         sym = menu->sym;
510         prop = menu->prompt;
511         if (!sym) {
512                 if (prop && menu != current_menu) {
513                         const char *prompt = menu_get_prompt(menu);
514                         switch (prop->type) {
515                         case P_MENU:
516                                 child_count++;
517                                 if (single_menu_mode) {
518                                         item_make("%s%*c%s",
519                                                   menu->data ? "-->" : "++>",
520                                                   indent + 1, ' ', prompt);
521                                 } else
522                                         item_make("   %*c%s  %s",
523                                                   indent + 1, ' ', prompt,
524                                                   menu_is_empty(menu) ? "----" : "--->");
525                                 item_set_tag('m');
526                                 item_set_data(menu);
527                                 if (single_menu_mode && menu->data)
528                                         goto conf_childs;
529                                 return;
530                         case P_COMMENT:
531                                 if (prompt) {
532                                         child_count++;
533                                         item_make("   %*c*** %s ***", indent + 1, ' ', prompt);
534                                         item_set_tag(':');
535                                         item_set_data(menu);
536                                 }
537                                 break;
538                         default:
539                                 if (prompt) {
540                                         child_count++;
541                                         item_make("---%*c%s", indent + 1, ' ', prompt);
542                                         item_set_tag(':');
543                                         item_set_data(menu);
544                                 }
545                         }
546                 } else
547                         doint = 0;
548                 goto conf_childs;
549         }
550
551         type = sym_get_type(sym);
552         if (sym_is_choice(sym)) {
553                 struct symbol *def_sym = sym_get_choice_value(sym);
554                 struct menu *def_menu = NULL;
555
556                 child_count++;
557                 for (child = menu->list; child; child = child->next) {
558                         if (menu_is_visible(child) && child->sym == def_sym)
559                                 def_menu = child;
560                 }
561
562                 val = sym_get_tristate_value(sym);
563                 if (sym_is_changeable(sym)) {
564                         switch (type) {
565                         case S_BOOLEAN:
566                                 item_make("[%c]", val == no ? ' ' : '*');
567                                 break;
568                         case S_TRISTATE:
569                                 switch (val) {
570                                 case yes: ch = '*'; break;
571                                 case mod: ch = 'M'; break;
572                                 default:  ch = ' '; break;
573                                 }
574                                 item_make("<%c>", ch);
575                                 break;
576                         }
577                         item_set_tag('t');
578                         item_set_data(menu);
579                 } else {
580                         item_make("   ");
581                         item_set_tag(def_menu ? 't' : ':');
582                         item_set_data(menu);
583                 }
584
585                 item_add_str("%*c%s", indent + 1, ' ', menu_get_prompt(menu));
586                 if (val == yes) {
587                         if (def_menu) {
588                                 item_add_str(" (%s)", menu_get_prompt(def_menu));
589                                 item_add_str("  --->");
590                                 if (def_menu->list) {
591                                         indent += 2;
592                                         build_conf(def_menu);
593                                         indent -= 2;
594                                 }
595                         }
596                         return;
597                 }
598         } else {
599                 if (menu == current_menu) {
600                         item_make("---%*c%s", indent + 1, ' ', menu_get_prompt(menu));
601                         item_set_tag(':');
602                         item_set_data(menu);
603                         goto conf_childs;
604                 }
605                 child_count++;
606                 val = sym_get_tristate_value(sym);
607                 if (sym_is_choice_value(sym) && val == yes) {
608                         item_make("   ");
609                         item_set_tag(':');
610                         item_set_data(menu);
611                 } else {
612                         switch (type) {
613                         case S_BOOLEAN:
614                                 if (sym_is_changeable(sym))
615                                         item_make("[%c]", val == no ? ' ' : '*');
616                                 else
617                                         item_make("-%c-", val == no ? ' ' : '*');
618                                 item_set_tag('t');
619                                 item_set_data(menu);
620                                 break;
621                         case S_TRISTATE:
622                                 switch (val) {
623                                 case yes: ch = '*'; break;
624                                 case mod: ch = 'M'; break;
625                                 default:  ch = ' '; break;
626                                 }
627                                 if (sym_is_changeable(sym)) {
628                                         if (sym->rev_dep.tri == mod)
629                                                 item_make("{%c}", ch);
630                                         else
631                                                 item_make("<%c>", ch);
632                                 } else
633                                         item_make("-%c-", ch);
634                                 item_set_tag('t');
635                                 item_set_data(menu);
636                                 break;
637                         default:
638                                 tmp = 2 + strlen(sym_get_string_value(sym)); /* () = 2 */
639                                 item_make("(%s)", sym_get_string_value(sym));
640                                 tmp = indent - tmp + 4;
641                                 if (tmp < 0)
642                                         tmp = 0;
643                                 item_add_str("%*c%s%s", tmp, ' ', menu_get_prompt(menu),
644                                              (sym_has_value(sym) || !sym_is_changeable(sym)) ?
645                                              "" : " (NEW)");
646                                 item_set_tag('s');
647                                 item_set_data(menu);
648                                 goto conf_childs;
649                         }
650                 }
651                 item_add_str("%*c%s%s", indent + 1, ' ', menu_get_prompt(menu),
652                           (sym_has_value(sym) || !sym_is_changeable(sym)) ?
653                           "" : " (NEW)");
654                 if (menu->prompt->type == P_MENU) {
655                         item_add_str("  %s", menu_is_empty(menu) ? "----" : "--->");
656                         return;
657                 }
658         }
659
660 conf_childs:
661         indent += doint;
662         for (child = menu->list; child; child = child->next)
663                 build_conf(child);
664         indent -= doint;
665 }
666
667 static void conf_choice(struct menu *menu)
668 {
669         const char *prompt = menu_get_prompt(menu);
670         struct menu *child;
671         struct symbol *active;
672
673         active = sym_get_choice_value(menu->sym);
674         while (1) {
675                 int res;
676                 int selected;
677                 item_reset();
678
679                 current_menu = menu;
680                 for (child = menu->list; child; child = child->next) {
681                         if (!menu_is_visible(child))
682                                 continue;
683                         if (child->sym)
684                                 item_make("%s", menu_get_prompt(child));
685                         else {
686                                 item_make("*** %s ***", menu_get_prompt(child));
687                                 item_set_tag(':');
688                         }
689                         item_set_data(child);
690                         if (child->sym == active)
691                                 item_set_selected(1);
692                         if (child->sym == sym_get_choice_value(menu->sym))
693                                 item_set_tag('X');
694                 }
695                 dialog_clear();
696                 res = dialog_checklist(prompt ? prompt : "Main Menu",
697                                         radiolist_instructions,
698                                         MENUBOX_HEIGTH_MIN,
699                                         MENUBOX_WIDTH_MIN,
700                                         CHECKLIST_HEIGTH_MIN);
701                 selected = item_activate_selected();
702                 switch (res) {
703                 case 0:
704                         if (selected) {
705                                 child = item_data();
706                                 if (!child->sym)
707                                         break;
708
709                                 sym_set_tristate_value(child->sym, yes);
710                         }
711                         return;
712                 case 1:
713                         if (selected) {
714                                 child = item_data();
715                                 show_help(child);
716                                 active = child->sym;
717                         } else
718                                 show_help(menu);
719                         break;
720                 case KEY_ESC:
721                         return;
722                 case -ERRDISPLAYTOOSMALL:
723                         return;
724                 }
725         }
726 }
727
728 static void conf_string(struct menu *menu)
729 {
730         const char *prompt = menu_get_prompt(menu);
731
732         while (1) {
733                 int res;
734                 const char *heading;
735
736                 switch (sym_get_type(menu->sym)) {
737                 case S_INT:
738                         heading = inputbox_instructions_int;
739                         break;
740                 case S_HEX:
741                         heading = inputbox_instructions_hex;
742                         break;
743                 case S_STRING:
744                         heading = inputbox_instructions_string;
745                         break;
746                 default:
747                         heading = "Internal mconf error!";
748                 }
749                 dialog_clear();
750                 res = dialog_inputbox(prompt ? prompt : "Main Menu",
751                                       heading, 10, 75,
752                                       sym_get_string_value(menu->sym));
753                 switch (res) {
754                 case 0:
755                         if (sym_set_string_value(menu->sym, dialog_input_result))
756                                 return;
757                         show_textbox(NULL, "You have made an invalid entry.", 5, 43);
758                         break;
759                 case 1:
760                         show_help(menu);
761                         break;
762                 case KEY_ESC:
763                         return;
764                 }
765         }
766 }
767
768 static void conf_load(void)
769 {
770
771         while (1) {
772                 int res;
773                 dialog_clear();
774                 res = dialog_inputbox(NULL, load_config_text,
775                                       11, 55, filename);
776                 switch(res) {
777                 case 0:
778                         if (!dialog_input_result[0])
779                                 return;
780                         if (!conf_read(dialog_input_result)) {
781                                 set_config_filename(dialog_input_result);
782                                 conf_set_changed(true);
783                                 return;
784                         }
785                         show_textbox(NULL, "File does not exist!", 5, 38);
786                         break;
787                 case 1:
788                         show_helptext("Load Alternate Configuration", load_config_help);
789                         break;
790                 case KEY_ESC:
791                         return;
792                 }
793         }
794 }
795
796 static void conf_save(void)
797 {
798         while (1) {
799                 int res;
800                 dialog_clear();
801                 res = dialog_inputbox(NULL, save_config_text,
802                                       11, 55, filename);
803                 switch(res) {
804                 case 0:
805                         if (!dialog_input_result[0])
806                                 return;
807                         if (!conf_write(dialog_input_result)) {
808                                 set_config_filename(dialog_input_result);
809                                 return;
810                         }
811                         show_textbox(NULL, "Can't create file!", 5, 60);
812                         break;
813                 case 1:
814                         show_helptext("Save Alternate Configuration", save_config_help);
815                         break;
816                 case KEY_ESC:
817                         return;
818                 }
819         }
820 }
821
822 static void conf(struct menu *menu, struct menu *active_menu)
823 {
824         struct menu *submenu;
825         const char *prompt = menu_get_prompt(menu);
826         struct subtitle_part stpart;
827         struct symbol *sym;
828         int res;
829         int s_scroll = 0;
830
831         if (menu != &rootmenu)
832                 stpart.text = menu_get_prompt(menu);
833         else
834                 stpart.text = NULL;
835         list_add_tail(&stpart.entries, &trail);
836
837         while (1) {
838                 item_reset();
839                 current_menu = menu;
840                 build_conf(menu);
841                 if (!child_count)
842                         break;
843                 set_subtitle();
844                 dialog_clear();
845                 res = dialog_menu(prompt ? prompt : "Main Menu",
846                                   menu_instructions,
847                                   active_menu, &s_scroll);
848                 if (res == 1 || res == KEY_ESC || res == -ERRDISPLAYTOOSMALL)
849                         break;
850                 if (item_count() != 0) {
851                         if (!item_activate_selected())
852                                 continue;
853                         if (!item_tag())
854                                 continue;
855                 }
856                 submenu = item_data();
857                 active_menu = item_data();
858                 if (submenu)
859                         sym = submenu->sym;
860                 else
861                         sym = NULL;
862
863                 switch (res) {
864                 case 0:
865                         switch (item_tag()) {
866                         case 'm':
867                                 if (single_menu_mode)
868                                         submenu->data = (void *) (long) !submenu->data;
869                                 else
870                                         conf(submenu, NULL);
871                                 break;
872                         case 't':
873                                 if (sym_is_choice(sym) && sym_get_tristate_value(sym) == yes)
874                                         conf_choice(submenu);
875                                 else if (submenu->prompt->type == P_MENU)
876                                         conf(submenu, NULL);
877                                 break;
878                         case 's':
879                                 conf_string(submenu);
880                                 break;
881                         }
882                         break;
883                 case 2:
884                         if (sym)
885                                 show_help(submenu);
886                         else {
887                                 reset_subtitle();
888                                 show_helptext("README", mconf_readme);
889                         }
890                         break;
891                 case 3:
892                         reset_subtitle();
893                         conf_save();
894                         break;
895                 case 4:
896                         reset_subtitle();
897                         conf_load();
898                         break;
899                 case 5:
900                         if (item_is_tag('t')) {
901                                 if (sym_set_tristate_value(sym, yes))
902                                         break;
903                                 if (sym_set_tristate_value(sym, mod))
904                                         show_textbox(NULL, setmod_text, 6, 74);
905                         }
906                         break;
907                 case 6:
908                         if (item_is_tag('t'))
909                                 sym_set_tristate_value(sym, no);
910                         break;
911                 case 7:
912                         if (item_is_tag('t'))
913                                 sym_set_tristate_value(sym, mod);
914                         break;
915                 case 8:
916                         if (item_is_tag('t'))
917                                 sym_toggle_tristate_value(sym);
918                         else if (item_is_tag('m'))
919                                 conf(submenu, NULL);
920                         break;
921                 case 9:
922                         search_conf();
923                         break;
924                 case 10:
925                         show_all_options = !show_all_options;
926                         break;
927                 }
928         }
929
930         list_del(trail.prev);
931 }
932
933 static void conf_message_callback(const char *s)
934 {
935         if (save_and_exit) {
936                 if (!silent)
937                         printf("%s", s);
938         } else {
939                 show_textbox(NULL, s, 6, 60);
940         }
941 }
942
943 static int handle_exit(void)
944 {
945         int res;
946
947         save_and_exit = 1;
948         reset_subtitle();
949         dialog_clear();
950         if (conf_get_changed())
951                 res = dialog_yesno(NULL,
952                                    "Do you wish to save your new configuration?\n"
953                                      "(Press <ESC><ESC> to continue kernel configuration.)",
954                                    6, 60);
955         else
956                 res = -1;
957
958         end_dialog(saved_x, saved_y);
959
960         switch (res) {
961         case 0:
962                 if (conf_write(filename)) {
963                         fprintf(stderr, "\n\n"
964                                           "Error while writing of the configuration.\n"
965                                           "Your configuration changes were NOT saved."
966                                           "\n\n");
967                         return 1;
968                 }
969                 conf_write_autoconf(0);
970                 /* fall through */
971         case -1:
972                 if (!silent)
973                         printf("\n\n"
974                                  "*** End of the configuration.\n"
975                                  "*** Execute 'make' to start the build or try 'make help'."
976                                  "\n\n");
977                 res = 0;
978                 break;
979         default:
980                 if (!silent)
981                         fprintf(stderr, "\n\n"
982                                           "Your configuration changes were NOT saved."
983                                           "\n\n");
984                 if (res != KEY_ESC)
985                         res = 0;
986         }
987
988         return res;
989 }
990
991 static void sig_handler(int signo)
992 {
993         exit(handle_exit());
994 }
995
996 int main(int ac, char **av)
997 {
998         char *mode;
999         int res;
1000
1001         signal(SIGINT, sig_handler);
1002
1003         if (ac > 1 && strcmp(av[1], "-s") == 0) {
1004                 silent = 1;
1005                 /* Silence conf_read() until the real callback is set up */
1006                 conf_set_message_callback(NULL);
1007                 av++;
1008         }
1009         conf_parse(av[1]);
1010         conf_read(NULL);
1011
1012         mode = getenv("MENUCONFIG_MODE");
1013         if (mode) {
1014                 if (!strcasecmp(mode, "single_menu"))
1015                         single_menu_mode = 1;
1016         }
1017
1018         if (init_dialog(NULL)) {
1019                 fprintf(stderr, "Your display is too small to run Menuconfig!\n");
1020                 fprintf(stderr, "It must be at least 19 lines by 80 columns.\n");
1021                 return 1;
1022         }
1023
1024         set_config_filename(conf_get_configname());
1025         conf_set_message_callback(conf_message_callback);
1026         do {
1027                 conf(&rootmenu, NULL);
1028                 res = handle_exit();
1029         } while (res == KEY_ESC);
1030
1031         return res;
1032 }