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