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