kconfig: fix segv fault in menuconfig
[firefly-linux-kernel-4.4.55.git] / scripts / kconfig / mconf.c
1 /*
2  * Copyright (C) 2002 Roman Zippel <zippel@linux-m68k.org>
3  * Released under the terms of the GNU GPL v2.0.
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 <sys/ioctl.h>
12 #include <sys/wait.h>
13 #include <ctype.h>
14 #include <errno.h>
15 #include <fcntl.h>
16 #include <limits.h>
17 #include <signal.h>
18 #include <stdarg.h>
19 #include <stdlib.h>
20 #include <string.h>
21 #include <termios.h>
22 #include <unistd.h>
23 #include <locale.h>
24
25 #define LKC_DIRECT_LINK
26 #include "lkc.h"
27 #include "lxdialog/dialog.h"
28
29 static const char mconf_readme[] = N_(
30 "Overview\n"
31 "--------\n"
32 "Some kernel features may be built directly into the kernel.\n"
33 "Some may be made into loadable runtime modules.  Some features\n"
34 "may be completely removed altogether.  There are also certain\n"
35 "kernel parameters which are not really features, but must be\n"
36 "entered in as decimal or hexadecimal numbers or possibly text.\n"
37 "\n"
38 "Menu items beginning with [*], <M> or [ ] represent features\n"
39 "configured to be built in, modularized or removed respectively.\n"
40 "Pointed brackets <> represent module capable features.\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 removed it.  You may also press the <Space Bar> to cycle\n"
45 "through the available options (ie. 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\n"
52 "   you wish to change or submenu wish to select and press <Enter>.\n"
53 "   Submenus are designated 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> and <Help>\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 "\n"
78 "Radiolists  (Choice lists)\n"
79 "-----------\n"
80 "o  Use the cursor keys to select the option you wish to set and press\n"
81 "   <S> or the <SPACE BAR>.\n"
82 "\n"
83 "   Shortcut: Press the first letter of the option you wish to set then\n"
84 "             press <S> or <SPACE BAR>.\n"
85 "\n"
86 "o  To see available help for the item, use the cursor keys to highlight\n"
87 "   <Help> and Press <ENTER>.\n"
88 "\n"
89 "   Shortcut: Press <H> or <?>.\n"
90 "\n"
91 "   Also, the <TAB> and cursor keys will cycle between <Select> and\n"
92 "   <Help>\n"
93 "\n"
94 "\n"
95 "Data Entry\n"
96 "-----------\n"
97 "o  Enter the requested information and press <ENTER>\n"
98 "   If you are entering hexadecimal values, it is not necessary to\n"
99 "   add the '0x' prefix to the entry.\n"
100 "\n"
101 "o  For help, use the <TAB> or cursor keys to highlight the help option\n"
102 "   and press <ENTER>.  You can try <TAB><H> as well.\n"
103 "\n"
104 "\n"
105 "Text Box    (Help Window)\n"
106 "--------\n"
107 "o  Use the cursor keys to scroll up/down/left/right.  The VI editor\n"
108 "   keys h,j,k,l function here as do <SPACE BAR> and <B> for those\n"
109 "   who are familiar with less and lynx.\n"
110 "\n"
111 "o  Press <E>, <X>, <Enter> or <Esc><Esc> to exit.\n"
112 "\n"
113 "\n"
114 "Alternate Configuration Files\n"
115 "-----------------------------\n"
116 "Menuconfig supports the use of alternate configuration files for\n"
117 "those who, for various reasons, find it necessary to switch\n"
118 "between different kernel configurations.\n"
119 "\n"
120 "At the end of the main menu you will find two options.  One is\n"
121 "for saving the current configuration to a file of your choosing.\n"
122 "The other option is for loading a previously saved alternate\n"
123 "configuration.\n"
124 "\n"
125 "Even if you don't use alternate configuration files, but you\n"
126 "find during a Menuconfig session that you have completely messed\n"
127 "up your settings, you may use the \"Load Alternate...\" option to\n"
128 "restore your previously saved settings from \".config\" without\n"
129 "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 a xterm definition which supports color.\n"
135 "Otherwise, Menuconfig will look rather bad.  Menuconfig will not\n"
136 "display correctly in a 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 kernel options listed in a single\n"
152 "menu, rather than the default multimenu hierarchy, run the menuconfig\n"
153 "with 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  => a LCD friendly version of classic. (default)\n"
176 "\n"),
177 menu_instructions[] = N_(
178         "Arrow keys navigate the menu.  "
179         "<Enter> selects 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[] = N_(
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[] = N_(
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[] = N_(
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[] = N_(
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[] = N_(
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 nohelp_text[] = N_(
203         "There is no help available for this kernel option.\n"),
204 load_config_text[] = N_(
205         "Enter the name of the configuration file you wish to load.  "
206         "Accept the name shown to restore the configuration you "
207         "last retrieved.  Leave blank to abort."),
208 load_config_help[] = N_(
209         "\n"
210         "For various reasons, one may wish to keep several different kernel\n"
211         "configurations available on a single machine.\n"
212         "\n"
213         "If you have saved a previous configuration in a file other than the\n"
214         "kernel's default, entering the name of the file here will allow you\n"
215         "to modify that configuration.\n"
216         "\n"
217         "If you are uncertain, then you have probably never used alternate\n"
218         "configuration files.  You should therefor leave this blank to abort.\n"),
219 save_config_text[] = N_(
220         "Enter a filename to which this configuration should be saved "
221         "as an alternate.  Leave blank to abort."),
222 save_config_help[] = N_(
223         "\n"
224         "For various reasons, one may wish to keep different kernel\n"
225         "configurations available on a single machine.\n"
226         "\n"
227         "Entering a file name here will allow you to later retrieve, modify\n"
228         "and use the current configuration as an alternate to whatever\n"
229         "configuration options you have selected at that time.\n"
230         "\n"
231         "If you are uncertain what all this means then you should probably\n"
232         "leave this blank.\n"),
233 search_help[] = N_(
234         "\n"
235         "Search for CONFIG_ symbols and display their relations.\n"
236         "Regular expressions are allowed.\n"
237         "Example: search for \"^FOO\"\n"
238         "Result:\n"
239         "-----------------------------------------------------------------\n"
240         "Symbol: FOO [=m]\n"
241         "Prompt: Foo bus is used to drive the bar HW\n"
242         "Defined at drivers/pci/Kconfig:47\n"
243         "Depends on: X86_LOCAL_APIC && X86_IO_APIC || IA64\n"
244         "Location:\n"
245         "  -> Bus options (PCI, PCMCIA, EISA, MCA, ISA)\n"
246         "    -> PCI support (PCI [=y])\n"
247         "      -> PCI access mode (<choice> [=y])\n"
248         "Selects: LIBCRC32\n"
249         "Selected by: BAR\n"
250         "-----------------------------------------------------------------\n"
251         "o The line 'Prompt:' shows the text used in the menu structure for\n"
252         "  this CONFIG_ symbol\n"
253         "o The 'Defined at' line tell at what file / line number the symbol\n"
254         "  is defined\n"
255         "o The 'Depends on:' line tell what symbols needs to be defined for\n"
256         "  this symbol to be visible in the menu (selectable)\n"
257         "o The 'Location:' lines tell where in the menu structure this symbol\n"
258         "  is located\n"
259         "    A location followed by a [=y] indicate that this is a selectable\n"
260         "    menu item - and current value is displayed inside brackets.\n"
261         "o The 'Selects:' line tell what symbol will be automatically\n"
262         "  selected if this symbol is selected (y or m)\n"
263         "o The 'Selected by' line tell what symbol has selected this symbol\n"
264         "\n"
265         "Only relevant lines are shown.\n"
266         "\n\n"
267         "Search examples:\n"
268         "Examples: USB  => find all CONFIG_ symbols containing USB\n"
269         "          ^USB => find all CONFIG_ symbols starting with USB\n"
270         "          USB$ => find all CONFIG_ symbols ending with USB\n"
271         "\n");
272
273 static int indent;
274 static struct termios ios_org;
275 static int rows = 0, cols = 0;
276 static struct menu *current_menu;
277 static int child_count;
278 static int single_menu_mode;
279
280 static void conf(struct menu *menu);
281 static void conf_choice(struct menu *menu);
282 static void conf_string(struct menu *menu);
283 static void conf_load(void);
284 static void conf_save(void);
285 static void show_textbox(const char *title, const char *text, int r, int c);
286 static void show_helptext(const char *title, const char *text);
287 static void show_help(struct menu *menu);
288
289 static void init_wsize(void)
290 {
291         struct winsize ws;
292         char *env;
293
294         if (!ioctl(STDIN_FILENO, TIOCGWINSZ, &ws)) {
295                 rows = ws.ws_row;
296                 cols = ws.ws_col;
297         }
298
299         if (!rows) {
300                 env = getenv("LINES");
301                 if (env)
302                         rows = atoi(env);
303                 if (!rows)
304                         rows = 24;
305         }
306         if (!cols) {
307                 env = getenv("COLUMNS");
308                 if (env)
309                         cols = atoi(env);
310                 if (!cols)
311                         cols = 80;
312         }
313
314         if (rows < 19 || cols < 80) {
315                 fprintf(stderr, N_("Your display is too small to run Menuconfig!\n"));
316                 fprintf(stderr, N_("It must be at least 19 lines by 80 columns.\n"));
317                 exit(1);
318         }
319
320         rows -= 4;
321         cols -= 5;
322 }
323
324 static void get_prompt_str(struct gstr *r, struct property *prop)
325 {
326         int i, j;
327         struct menu *submenu[8], *menu;
328
329         str_printf(r, "Prompt: %s\n", prop->text);
330         str_printf(r, "  Defined at %s:%d\n", prop->menu->file->name,
331                 prop->menu->lineno);
332         if (!expr_is_yes(prop->visible.expr)) {
333                 str_append(r, "  Depends on: ");
334                 expr_gstr_print(prop->visible.expr, r);
335                 str_append(r, "\n");
336         }
337         menu = prop->menu->parent;
338         for (i = 0; menu != &rootmenu && i < 8; menu = menu->parent)
339                 submenu[i++] = menu;
340         if (i > 0) {
341                 str_printf(r, "  Location:\n");
342                 for (j = 4; --i >= 0; j += 2) {
343                         menu = submenu[i];
344                         str_printf(r, "%*c-> %s", j, ' ', menu_get_prompt(menu));
345                         if (menu->sym) {
346                                 str_printf(r, " (%s [=%s])", menu->sym->name ?
347                                         menu->sym->name : "<choice>",
348                                         sym_get_string_value(menu->sym));
349                         }
350                         str_append(r, "\n");
351                 }
352         }
353 }
354
355 static void get_symbol_str(struct gstr *r, struct symbol *sym)
356 {
357         bool hit;
358         struct property *prop;
359
360         if (sym && sym->name)
361                 str_printf(r, "Symbol: %s [=%s]\n", sym->name,
362                                                     sym_get_string_value(sym));
363         for_all_prompts(sym, prop)
364                 get_prompt_str(r, prop);
365         hit = false;
366         for_all_properties(sym, prop, P_SELECT) {
367                 if (!hit) {
368                         str_append(r, "  Selects: ");
369                         hit = true;
370                 } else
371                         str_printf(r, " && ");
372                 expr_gstr_print(prop->expr, r);
373         }
374         if (hit)
375                 str_append(r, "\n");
376         if (sym->rev_dep.expr) {
377                 str_append(r, "  Selected by: ");
378                 expr_gstr_print(sym->rev_dep.expr, r);
379                 str_append(r, "\n");
380         }
381         str_append(r, "\n\n");
382 }
383
384 static struct gstr get_relations_str(struct symbol **sym_arr)
385 {
386         struct symbol *sym;
387         struct gstr res = str_new();
388         int i;
389
390         for (i = 0; sym_arr && (sym = sym_arr[i]); i++)
391                 get_symbol_str(&res, sym);
392         if (!i)
393                 str_append(&res, "No matches found.\n");
394         return res;
395 }
396
397 static char filename[PATH_MAX+1];
398 static void set_config_filename(const char *config_filename)
399 {
400         static char menu_backtitle[PATH_MAX+128];
401         int size;
402         struct symbol *sym;
403
404         sym = sym_lookup("KERNELVERSION", 0);
405         sym_calc_value(sym);
406         size = snprintf(menu_backtitle, sizeof(menu_backtitle),
407                         _("%s - Linux Kernel v%s Configuration"),
408                         config_filename, sym_get_string_value(sym));
409         if (size >= sizeof(menu_backtitle))
410                 menu_backtitle[sizeof(menu_backtitle)-1] = '\0';
411         set_dialog_backtitle(menu_backtitle);
412
413         size = snprintf(filename, sizeof(filename), "%s", config_filename);
414         if (size >= sizeof(filename))
415                 filename[sizeof(filename)-1] = '\0';
416 }
417
418
419 static void search_conf(void)
420 {
421         struct symbol **sym_arr;
422         struct gstr res;
423         char *dialog_input;
424         int dres;
425 again:
426         dialog_clear();
427         dres = dialog_inputbox(_("Search Configuration Parameter"),
428                               _("Enter CONFIG_ (sub)string to search for "
429                                 "(with or without \"CONFIG\")"),
430                               10, 75, "");
431         switch (dres) {
432         case 0:
433                 break;
434         case 1:
435                 show_helptext(_("Search Configuration"), search_help);
436                 goto again;
437         default:
438                 return;
439         }
440
441         /* strip CONFIG_ if necessary */
442         dialog_input = dialog_input_result;
443         if (strncasecmp(dialog_input_result, "CONFIG_", 7) == 0)
444                 dialog_input += 7;
445
446         sym_arr = sym_re_search(dialog_input);
447         res = get_relations_str(sym_arr);
448         free(sym_arr);
449         show_textbox(_("Search Results"), str_get(&res), 0, 0);
450         str_free(&res);
451 }
452
453 static void build_conf(struct menu *menu)
454 {
455         struct symbol *sym;
456         struct property *prop;
457         struct menu *child;
458         int type, tmp, doint = 2;
459         tristate val;
460         char ch;
461
462         if (!menu_is_visible(menu))
463                 return;
464
465         sym = menu->sym;
466         prop = menu->prompt;
467         if (!sym) {
468                 if (prop && menu != current_menu) {
469                         const char *prompt = menu_get_prompt(menu);
470                         switch (prop->type) {
471                         case P_MENU:
472                                 child_count++;
473                                 if (single_menu_mode) {
474                                         item_make("%s%*c%s",
475                                                   menu->data ? "-->" : "++>",
476                                                   indent + 1, ' ', prompt);
477                                 } else
478                                         item_make("   %*c%s  --->", indent + 1, ' ', prompt);
479
480                                 item_set_tag('m');
481                                 item_set_data(menu);
482                                 if (single_menu_mode && menu->data)
483                                         goto conf_childs;
484                                 return;
485                         case P_COMMENT:
486                                 if (prompt) {
487                                         child_count++;
488                                         item_make("   %*c*** %s ***", indent + 1, ' ', prompt);
489                                         item_set_tag(':');
490                                         item_set_data(menu);
491                                 }
492                                 break;
493                         default:
494                                 if (prompt) {
495                                         child_count++;
496                                         item_make("---%*c%s", indent + 1, ' ', prompt);
497                                         item_set_tag(':');
498                                         item_set_data(menu);
499                                 }
500                         }
501                 } else
502                         doint = 0;
503                 goto conf_childs;
504         }
505
506         type = sym_get_type(sym);
507         if (sym_is_choice(sym)) {
508                 struct symbol *def_sym = sym_get_choice_value(sym);
509                 struct menu *def_menu = NULL;
510
511                 child_count++;
512                 for (child = menu->list; child; child = child->next) {
513                         if (menu_is_visible(child) && child->sym == def_sym)
514                                 def_menu = child;
515                 }
516
517                 val = sym_get_tristate_value(sym);
518                 if (sym_is_changable(sym)) {
519                         switch (type) {
520                         case S_BOOLEAN:
521                                 item_make("[%c]", val == no ? ' ' : '*');
522                                 break;
523                         case S_TRISTATE:
524                                 switch (val) {
525                                 case yes: ch = '*'; break;
526                                 case mod: ch = 'M'; break;
527                                 default:  ch = ' '; break;
528                                 }
529                                 item_make("<%c>", ch);
530                                 break;
531                         }
532                         item_set_tag('t');
533                         item_set_data(menu);
534                 } else {
535                         item_make("   ");
536                         item_set_tag(def_menu ? 't' : ':');
537                         item_set_data(menu);
538                 }
539
540                 item_add_str("%*c%s", indent + 1, ' ', menu_get_prompt(menu));
541                 if (val == yes) {
542                         if (def_menu) {
543                                 item_add_str(" (%s)", menu_get_prompt(def_menu));
544                                 item_add_str("  --->");
545                                 if (def_menu->list) {
546                                         indent += 2;
547                                         build_conf(def_menu);
548                                         indent -= 2;
549                                 }
550                         }
551                         return;
552                 }
553         } else {
554                 if (menu == current_menu) {
555                         item_make("---%*c%s", indent + 1, ' ', menu_get_prompt(menu));
556                         item_set_tag(':');
557                         item_set_data(menu);
558                         goto conf_childs;
559                 }
560                 child_count++;
561                 val = sym_get_tristate_value(sym);
562                 if (sym_is_choice_value(sym) && val == yes) {
563                         item_make("   ");
564                         item_set_tag(':');
565                         item_set_data(menu);
566                 } else {
567                         switch (type) {
568                         case S_BOOLEAN:
569                                 if (sym_is_changable(sym))
570                                         item_make("[%c]", val == no ? ' ' : '*');
571                                 else
572                                         item_make("---");
573                                 item_set_tag('t');
574                                 item_set_data(menu);
575                                 break;
576                         case S_TRISTATE:
577                                 switch (val) {
578                                 case yes: ch = '*'; break;
579                                 case mod: ch = 'M'; break;
580                                 default:  ch = ' '; break;
581                                 }
582                                 if (sym_is_changable(sym))
583                                         item_make("<%c>", ch);
584                                 else
585                                         item_make("---");
586                                 item_set_tag('t');
587                                 item_set_data(menu);
588                                 break;
589                         default:
590                                 tmp = 2 + strlen(sym_get_string_value(sym)); /* () = 2 */
591                                 item_make("(%s)", sym_get_string_value(sym));
592                                 tmp = indent - tmp + 4;
593                                 if (tmp < 0)
594                                         tmp = 0;
595                                 item_add_str("%*c%s%s", tmp, ' ', menu_get_prompt(menu),
596                                              (sym_has_value(sym) || !sym_is_changable(sym)) ?
597                                              "" : " (NEW)");
598                                 item_set_tag('s');
599                                 item_set_data(menu);
600                                 goto conf_childs;
601                         }
602                 }
603                 item_add_str("%*c%s%s", indent + 1, ' ', menu_get_prompt(menu),
604                           (sym_has_value(sym) || !sym_is_changable(sym)) ?
605                           "" : " (NEW)");
606                 if (menu->prompt->type == P_MENU) {
607                         item_add_str("  --->");
608                         return;
609                 }
610         }
611
612 conf_childs:
613         indent += doint;
614         for (child = menu->list; child; child = child->next)
615                 build_conf(child);
616         indent -= doint;
617 }
618
619 static void conf(struct menu *menu)
620 {
621         struct menu *submenu;
622         const char *prompt = menu_get_prompt(menu);
623         struct symbol *sym;
624         struct menu *active_menu = NULL;
625         int res;
626         int s_scroll = 0;
627
628         while (1) {
629                 item_reset();
630                 current_menu = menu;
631                 build_conf(menu);
632                 if (!child_count)
633                         break;
634                 if (menu == &rootmenu) {
635                         item_make("--- ");
636                         item_set_tag(':');
637                         item_make(_("    Load an Alternate Configuration File"));
638                         item_set_tag('L');
639                         item_make(_("    Save an Alternate Configuration File"));
640                         item_set_tag('S');
641                 }
642                 dialog_clear();
643                 res = dialog_menu(prompt ? prompt : _("Main Menu"),
644                                   _(menu_instructions),
645                                   active_menu, &s_scroll);
646                 if (res == 1 || res == KEY_ESC || res == -ERRDISPLAYTOOSMALL)
647                         break;
648                 if (!item_activate_selected())
649                         continue;
650                 if (!item_tag())
651                         continue;
652
653                 submenu = item_data();
654                 active_menu = item_data();
655                 if (submenu)
656                         sym = submenu->sym;
657                 else
658                         sym = NULL;
659
660                 switch (res) {
661                 case 0:
662                         switch (item_tag()) {
663                         case 'm':
664                                 if (single_menu_mode)
665                                         submenu->data = (void *) (long) !submenu->data;
666                                 else
667                                         conf(submenu);
668                                 break;
669                         case 't':
670                                 if (sym_is_choice(sym) && sym_get_tristate_value(sym) == yes)
671                                         conf_choice(submenu);
672                                 else if (submenu->prompt->type == P_MENU)
673                                         conf(submenu);
674                                 break;
675                         case 's':
676                                 conf_string(submenu);
677                                 break;
678                         case 'L':
679                                 conf_load();
680                                 break;
681                         case 'S':
682                                 conf_save();
683                                 break;
684                         }
685                         break;
686                 case 2:
687                         if (sym)
688                                 show_help(submenu);
689                         else
690                                 show_helptext("README", _(mconf_readme));
691                         break;
692                 case 3:
693                         if (item_is_tag('t')) {
694                                 if (sym_set_tristate_value(sym, yes))
695                                         break;
696                                 if (sym_set_tristate_value(sym, mod))
697                                         show_textbox(NULL, setmod_text, 6, 74);
698                         }
699                         break;
700                 case 4:
701                         if (item_is_tag('t'))
702                                 sym_set_tristate_value(sym, no);
703                         break;
704                 case 5:
705                         if (item_is_tag('t'))
706                                 sym_set_tristate_value(sym, mod);
707                         break;
708                 case 6:
709                         if (item_is_tag('t'))
710                                 sym_toggle_tristate_value(sym);
711                         else if (item_is_tag('m'))
712                                 conf(submenu);
713                         break;
714                 case 7:
715                         search_conf();
716                         break;
717                 }
718         }
719 }
720
721 static void show_textbox(const char *title, const char *text, int r, int c)
722 {
723         dialog_clear();
724         dialog_textbox(title, text, r, c);
725 }
726
727 static void show_helptext(const char *title, const char *text)
728 {
729         show_textbox(title, text, 0, 0);
730 }
731
732 static void show_help(struct menu *menu)
733 {
734         struct gstr help = str_new();
735         struct symbol *sym = menu->sym;
736
737         if (menu_has_help(menu))
738         {
739                 if (sym->name) {
740                         str_printf(&help, "CONFIG_%s:\n\n", sym->name);
741                         str_append(&help, _(menu_get_help(menu)));
742                         str_append(&help, "\n");
743                 }
744         } else {
745                 str_append(&help, nohelp_text);
746         }
747         get_symbol_str(&help, sym);
748         show_helptext(menu_get_prompt(menu), str_get(&help));
749         str_free(&help);
750 }
751
752 static void conf_choice(struct menu *menu)
753 {
754         const char *prompt = menu_get_prompt(menu);
755         struct menu *child;
756         struct symbol *active;
757
758         active = sym_get_choice_value(menu->sym);
759         while (1) {
760                 int res;
761                 int selected;
762                 item_reset();
763
764                 current_menu = menu;
765                 for (child = menu->list; child; child = child->next) {
766                         if (!menu_is_visible(child))
767                                 continue;
768                         item_make("%s", menu_get_prompt(child));
769                         item_set_data(child);
770                         if (child->sym == active)
771                                 item_set_selected(1);
772                         if (child->sym == sym_get_choice_value(menu->sym))
773                                 item_set_tag('X');
774                 }
775                 dialog_clear();
776                 res = dialog_checklist(prompt ? prompt : _("Main Menu"),
777                                         _(radiolist_instructions),
778                                          15, 70, 6);
779                 selected = item_activate_selected();
780                 switch (res) {
781                 case 0:
782                         if (selected) {
783                                 child = item_data();
784                                 sym_set_tristate_value(child->sym, yes);
785                         }
786                         return;
787                 case 1:
788                         if (selected) {
789                                 child = item_data();
790                                 show_help(child);
791                                 active = child->sym;
792                         } else
793                                 show_help(menu);
794                         break;
795                 case KEY_ESC:
796                         return;
797                 case -ERRDISPLAYTOOSMALL:
798                         return;
799                 }
800         }
801 }
802
803 static void conf_string(struct menu *menu)
804 {
805         const char *prompt = menu_get_prompt(menu);
806
807         while (1) {
808                 int res;
809                 char *heading;
810
811                 switch (sym_get_type(menu->sym)) {
812                 case S_INT:
813                         heading = _(inputbox_instructions_int);
814                         break;
815                 case S_HEX:
816                         heading = _(inputbox_instructions_hex);
817                         break;
818                 case S_STRING:
819                         heading = _(inputbox_instructions_string);
820                         break;
821                 default:
822                         heading = "Internal mconf error!";
823                 }
824                 dialog_clear();
825                 res = dialog_inputbox(prompt ? prompt : _("Main Menu"),
826                                       heading, 10, 75,
827                                       sym_get_string_value(menu->sym));
828                 switch (res) {
829                 case 0:
830                         if (sym_set_string_value(menu->sym, dialog_input_result))
831                                 return;
832                         show_textbox(NULL, _("You have made an invalid entry."), 5, 43);
833                         break;
834                 case 1:
835                         show_help(menu);
836                         break;
837                 case KEY_ESC:
838                         return;
839                 }
840         }
841 }
842
843 static void conf_load(void)
844 {
845
846         while (1) {
847                 int res;
848                 dialog_clear();
849                 res = dialog_inputbox(NULL, load_config_text,
850                                       11, 55, filename);
851                 switch(res) {
852                 case 0:
853                         if (!dialog_input_result[0])
854                                 return;
855                         if (!conf_read(dialog_input_result)) {
856                                 set_config_filename(dialog_input_result);
857                                 return;
858                         }
859                         show_textbox(NULL, _("File does not exist!"), 5, 38);
860                         break;
861                 case 1:
862                         show_helptext(_("Load Alternate Configuration"), load_config_help);
863                         break;
864                 case KEY_ESC:
865                         return;
866                 }
867         }
868 }
869
870 static void conf_save(void)
871 {
872         while (1) {
873                 int res;
874                 dialog_clear();
875                 res = dialog_inputbox(NULL, save_config_text,
876                                       11, 55, filename);
877                 switch(res) {
878                 case 0:
879                         if (!dialog_input_result[0])
880                                 return;
881                         if (!conf_write(dialog_input_result)) {
882                                 set_config_filename(dialog_input_result);
883                                 return;
884                         }
885                         show_textbox(NULL, _("Can't create file!  Probably a nonexistent directory."), 5, 60);
886                         break;
887                 case 1:
888                         show_helptext(_("Save Alternate Configuration"), save_config_help);
889                         break;
890                 case KEY_ESC:
891                         return;
892                 }
893         }
894 }
895
896 static void conf_cleanup(void)
897 {
898         tcsetattr(1, TCSAFLUSH, &ios_org);
899 }
900
901 int main(int ac, char **av)
902 {
903         char *mode;
904         int res;
905
906         setlocale(LC_ALL, "");
907         bindtextdomain(PACKAGE, LOCALEDIR);
908         textdomain(PACKAGE);
909
910         conf_parse(av[1]);
911         conf_read(NULL);
912
913         mode = getenv("MENUCONFIG_MODE");
914         if (mode) {
915                 if (!strcasecmp(mode, "single_menu"))
916                         single_menu_mode = 1;
917         }
918
919         tcgetattr(1, &ios_org);
920         atexit(conf_cleanup);
921         init_wsize();
922         reset_dialog();
923         init_dialog(NULL);
924         set_config_filename(conf_get_configname());
925         do {
926                 conf(&rootmenu);
927                 dialog_clear();
928                 if (conf_get_changed())
929                         res = dialog_yesno(NULL,
930                                            _("Do you wish to save your "
931                                              "new kernel configuration?\n"
932                                              "<ESC><ESC> to continue."),
933                                            6, 60);
934                 else
935                         res = -1;
936         } while (res == KEY_ESC);
937         end_dialog();
938
939         switch (res) {
940         case 0:
941                 if (conf_write(filename)) {
942                         fprintf(stderr, _("\n\n"
943                                 "Error during writing of the kernel configuration.\n"
944                                 "Your kernel configuration changes were NOT saved."
945                                 "\n\n"));
946                         return 1;
947                 }
948         case -1:
949                 printf(_("\n\n"
950                         "*** End of Linux kernel configuration.\n"
951                         "*** Execute 'make' to build the kernel or try 'make help'."
952                         "\n\n"));
953                 break;
954         default:
955                 fprintf(stderr, _("\n\n"
956                         "Your kernel configuration changes were NOT saved."
957                         "\n\n"));
958         }
959
960         return 0;
961 }