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