menuconfig: Fix memory leak introduced by jump keys feature
[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 <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 <signal.h>
19 #include <unistd.h>
20 #include <locale.h>
21
22 #include "lkc.h"
23 #include "lxdialog/dialog.h"
24
25 static const char mconf_readme[] = N_(
26 "Overview\n"
27 "--------\n"
28 "This interface let you select features and parameters for the build.\n"
29 "Features can either be built-in, modularized, or ignored. Parameters\n"
30 "must be entered in as decimal or hexadecimal numbers or text.\n"
31 "\n"
32 "Menu items beginning with following braces represent features that\n"
33 "  [ ] can be built in or removed\n"
34 "  < > can be built in, modularized or removed\n"
35 "  { } can be built in or modularized (selected by other feature)\n"
36 "  - - are selected by other feature,\n"
37 "while *, M or whitespace inside braces means to build in, build as\n"
38 "a module or to exclude the feature respectively.\n"
39 "\n"
40 "To change any of these features, highlight it with the cursor\n"
41 "keys and press <Y> to build it in, <M> to make it a module or\n"
42 "<N> to removed it.  You may also press the <Space Bar> to cycle\n"
43 "through the available options (ie. Y->N->M->Y).\n"
44 "\n"
45 "Some additional keyboard hints:\n"
46 "\n"
47 "Menus\n"
48 "----------\n"
49 "o  Use the Up/Down arrow keys (cursor keys) to highlight the item\n"
50 "   you wish to change or submenu wish to select and press <Enter>.\n"
51 "   Submenus are designated by \"--->\".\n"
52 "\n"
53 "   Shortcut: Press the option's highlighted letter (hotkey).\n"
54 "             Pressing a hotkey more than once will sequence\n"
55 "             through all visible items which use that hotkey.\n"
56 "\n"
57 "   You may also use the <PAGE UP> and <PAGE DOWN> keys to scroll\n"
58 "   unseen options into view.\n"
59 "\n"
60 "o  To exit a menu use the cursor keys to highlight the <Exit> button\n"
61 "   and press <ENTER>.\n"
62 "\n"
63 "   Shortcut: Press <ESC><ESC> or <E> or <X> if there is no hotkey\n"
64 "             using those letters.  You may press a single <ESC>, but\n"
65 "             there is a delayed response which you may find annoying.\n"
66 "\n"
67 "   Also, the <TAB> and cursor keys will cycle between <Select>,\n"
68 "   <Exit> and <Help>.\n"
69 "\n"
70 "o  To get help with an item, use the cursor keys to highlight <Help>\n"
71 "   and press <ENTER>.\n"
72 "\n"
73 "   Shortcut: Press <H> or <?>.\n"
74 "\n"
75 "o  To toggle the display of hidden options, press <Z>.\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 <u>, <d>, <SPACE BAR> and <B> for \n"
109 "   those who are familiar with less and lynx.\n"
110 "\n"
111 "o  Press <E>, <X>, <q>, <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 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 options listed in a single menu, rather\n"
152 "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  => 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 load_config_text[] = N_(
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[] = N_(
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[] = N_(
218         "Enter a filename to which this configuration should be saved "
219         "as an alternate.  Leave blank to abort."),
220 save_config_help[] = N_(
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[] = N_(
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         "  Defined at drivers/pci/Kconfig:47\n"
242         "  Depends on: X86_LOCAL_APIC && X86_IO_APIC || IA64\n"
243         "  Location:\n"
244         "    -> Bus options (PCI, PCMCIA, EISA, ISA)\n"
245         "      -> PCI support (PCI [=y])\n"
246         "(1)     -> PCI access mode (<choice> [=y])\n"
247         "  Selects: LIBCRC32\n"
248         "  Selected by: BAR\n"
249         "-----------------------------------------------------------------\n"
250         "o The line 'Type:' shows the type of the configuration option for\n"
251         "  this symbol (boolean, 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 tell at what file / line number the symbol\n"
255         "  is defined\n"
256         "o The 'Depends on:' line tell what symbols needs to be defined for\n"
257         "  this symbol to be visible in the menu (selectable)\n"
258         "o The 'Location:' lines tell 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 tell what symbol will be automatically\n"
267         "  selected if this symbol is selected (y or m)\n"
268         "o The 'Selected by' line tell 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
285 static void conf(struct menu *menu, struct menu *active_menu);
286 static void conf_choice(struct menu *menu);
287 static void conf_string(struct menu *menu);
288 static void conf_load(void);
289 static void conf_save(void);
290 static int show_textbox_ext(const char *title, char *text, int r, int c,
291                             int *keys, int *vscroll, int *hscroll,
292                             update_text_fn update_text, void *data);
293 static void show_textbox(const char *title, const char *text, int r, int c);
294 static void show_helptext(const char *title, const char *text);
295 static void show_help(struct menu *menu);
296
297 static char filename[PATH_MAX+1];
298 static void set_config_filename(const char *config_filename)
299 {
300         static char menu_backtitle[PATH_MAX+128];
301         int size;
302
303         size = snprintf(menu_backtitle, sizeof(menu_backtitle),
304                         "%s - %s", config_filename, rootmenu.prompt->text);
305         if (size >= sizeof(menu_backtitle))
306                 menu_backtitle[sizeof(menu_backtitle)-1] = '\0';
307         set_dialog_backtitle(menu_backtitle);
308
309         size = snprintf(filename, sizeof(filename), "%s", config_filename);
310         if (size >= sizeof(filename))
311                 filename[sizeof(filename)-1] = '\0';
312 }
313
314
315 struct search_data {
316         struct list_head *head;
317         struct menu **targets;
318         int *keys;
319 };
320
321 static void update_text(char *buf, size_t start, size_t end, void *_data)
322 {
323         struct search_data *data = _data;
324         struct jump_key *pos;
325         int k = 0;
326
327         list_for_each_entry(pos, data->head, entries) {
328                 if (pos->offset >= start && pos->offset < end) {
329                         char header[4];
330
331                         if (k < JUMP_NB) {
332                                 int key = '0' + (pos->index % JUMP_NB) + 1;
333
334                                 sprintf(header, "(%c)", key);
335                                 data->keys[k] = key;
336                                 data->targets[k] = pos->target;
337                                 k++;
338                         } else {
339                                 sprintf(header, "   ");
340                         }
341
342                         memcpy(buf + pos->offset, header, sizeof(header) - 1);
343                 }
344         }
345         data->keys[k] = 0;
346 }
347
348 static void search_conf(void)
349 {
350         struct symbol **sym_arr;
351         struct gstr res;
352         struct gstr title;
353         char *dialog_input;
354         int dres, vscroll = 0, hscroll = 0;
355         bool again;
356
357         title = str_new();
358         str_printf( &title, _("Enter %s (sub)string to search for "
359                               "(with or without \"%s\")"), CONFIG_, CONFIG_);
360
361 again:
362         dialog_clear();
363         dres = dialog_inputbox(_("Search Configuration Parameter"),
364                               str_get(&title),
365                               10, 75, "");
366         switch (dres) {
367         case 0:
368                 break;
369         case 1:
370                 show_helptext(_("Search Configuration"), search_help);
371                 goto again;
372         default:
373                 str_free(&title);
374                 return;
375         }
376
377         /* strip the prefix if necessary */
378         dialog_input = dialog_input_result;
379         if (strncasecmp(dialog_input_result, CONFIG_, strlen(CONFIG_)) == 0)
380                 dialog_input += strlen(CONFIG_);
381
382         sym_arr = sym_re_search(dialog_input);
383         do {
384                 LIST_HEAD(head);
385                 struct menu *targets[JUMP_NB];
386                 int keys[JUMP_NB + 1], i;
387                 struct search_data data = {
388                         .head = &head,
389                         .targets = targets,
390                         .keys = keys,
391                 };
392                 struct jump_key *pos, *tmp;
393
394                 res = get_relations_str(sym_arr, &head);
395                 dres = show_textbox_ext(_("Search Results"), (char *)
396                                         str_get(&res), 0, 0, keys, &vscroll,
397                                         &hscroll, &update_text, (void *)
398                                         &data);
399                 again = false;
400                 for (i = 0; i < JUMP_NB && keys[i]; i++)
401                         if (dres == keys[i]) {
402                                 conf(targets[i]->parent, targets[i]);
403                                 again = true;
404                         }
405                 str_free(&res);
406                 list_for_each_entry_safe(pos, tmp, &head, entries)
407                         free(pos);
408         } while (again);
409         free(sym_arr);
410         str_free(&title);
411 }
412
413 static void build_conf(struct menu *menu)
414 {
415         struct symbol *sym;
416         struct property *prop;
417         struct menu *child;
418         int type, tmp, doint = 2;
419         tristate val;
420         char ch;
421         bool visible;
422
423         /*
424          * note: menu_is_visible() has side effect that it will
425          * recalc the value of the symbol.
426          */
427         visible = menu_is_visible(menu);
428         if (show_all_options && !menu_has_prompt(menu))
429                 return;
430         else if (!show_all_options && !visible)
431                 return;
432
433         sym = menu->sym;
434         prop = menu->prompt;
435         if (!sym) {
436                 if (prop && menu != current_menu) {
437                         const char *prompt = menu_get_prompt(menu);
438                         switch (prop->type) {
439                         case P_MENU:
440                                 child_count++;
441                                 prompt = _(prompt);
442                                 if (single_menu_mode) {
443                                         item_make("%s%*c%s",
444                                                   menu->data ? "-->" : "++>",
445                                                   indent + 1, ' ', prompt);
446                                 } else
447                                         item_make("   %*c%s  --->", indent + 1, ' ', prompt);
448
449                                 item_set_tag('m');
450                                 item_set_data(menu);
451                                 if (single_menu_mode && menu->data)
452                                         goto conf_childs;
453                                 return;
454                         case P_COMMENT:
455                                 if (prompt) {
456                                         child_count++;
457                                         item_make("   %*c*** %s ***", indent + 1, ' ', _(prompt));
458                                         item_set_tag(':');
459                                         item_set_data(menu);
460                                 }
461                                 break;
462                         default:
463                                 if (prompt) {
464                                         child_count++;
465                                         item_make("---%*c%s", indent + 1, ' ', _(prompt));
466                                         item_set_tag(':');
467                                         item_set_data(menu);
468                                 }
469                         }
470                 } else
471                         doint = 0;
472                 goto conf_childs;
473         }
474
475         type = sym_get_type(sym);
476         if (sym_is_choice(sym)) {
477                 struct symbol *def_sym = sym_get_choice_value(sym);
478                 struct menu *def_menu = NULL;
479
480                 child_count++;
481                 for (child = menu->list; child; child = child->next) {
482                         if (menu_is_visible(child) && child->sym == def_sym)
483                                 def_menu = child;
484                 }
485
486                 val = sym_get_tristate_value(sym);
487                 if (sym_is_changable(sym)) {
488                         switch (type) {
489                         case S_BOOLEAN:
490                                 item_make("[%c]", val == no ? ' ' : '*');
491                                 break;
492                         case S_TRISTATE:
493                                 switch (val) {
494                                 case yes: ch = '*'; break;
495                                 case mod: ch = 'M'; break;
496                                 default:  ch = ' '; break;
497                                 }
498                                 item_make("<%c>", ch);
499                                 break;
500                         }
501                         item_set_tag('t');
502                         item_set_data(menu);
503                 } else {
504                         item_make("   ");
505                         item_set_tag(def_menu ? 't' : ':');
506                         item_set_data(menu);
507                 }
508
509                 item_add_str("%*c%s", indent + 1, ' ', _(menu_get_prompt(menu)));
510                 if (val == yes) {
511                         if (def_menu) {
512                                 item_add_str(" (%s)", _(menu_get_prompt(def_menu)));
513                                 item_add_str("  --->");
514                                 if (def_menu->list) {
515                                         indent += 2;
516                                         build_conf(def_menu);
517                                         indent -= 2;
518                                 }
519                         }
520                         return;
521                 }
522         } else {
523                 if (menu == current_menu) {
524                         item_make("---%*c%s", indent + 1, ' ', _(menu_get_prompt(menu)));
525                         item_set_tag(':');
526                         item_set_data(menu);
527                         goto conf_childs;
528                 }
529                 child_count++;
530                 val = sym_get_tristate_value(sym);
531                 if (sym_is_choice_value(sym) && val == yes) {
532                         item_make("   ");
533                         item_set_tag(':');
534                         item_set_data(menu);
535                 } else {
536                         switch (type) {
537                         case S_BOOLEAN:
538                                 if (sym_is_changable(sym))
539                                         item_make("[%c]", val == no ? ' ' : '*');
540                                 else
541                                         item_make("-%c-", val == no ? ' ' : '*');
542                                 item_set_tag('t');
543                                 item_set_data(menu);
544                                 break;
545                         case S_TRISTATE:
546                                 switch (val) {
547                                 case yes: ch = '*'; break;
548                                 case mod: ch = 'M'; break;
549                                 default:  ch = ' '; break;
550                                 }
551                                 if (sym_is_changable(sym)) {
552                                         if (sym->rev_dep.tri == mod)
553                                                 item_make("{%c}", ch);
554                                         else
555                                                 item_make("<%c>", ch);
556                                 } else
557                                         item_make("-%c-", ch);
558                                 item_set_tag('t');
559                                 item_set_data(menu);
560                                 break;
561                         default:
562                                 tmp = 2 + strlen(sym_get_string_value(sym)); /* () = 2 */
563                                 item_make("(%s)", sym_get_string_value(sym));
564                                 tmp = indent - tmp + 4;
565                                 if (tmp < 0)
566                                         tmp = 0;
567                                 item_add_str("%*c%s%s", tmp, ' ', _(menu_get_prompt(menu)),
568                                              (sym_has_value(sym) || !sym_is_changable(sym)) ?
569                                              "" : _(" (NEW)"));
570                                 item_set_tag('s');
571                                 item_set_data(menu);
572                                 goto conf_childs;
573                         }
574                 }
575                 item_add_str("%*c%s%s", indent + 1, ' ', _(menu_get_prompt(menu)),
576                           (sym_has_value(sym) || !sym_is_changable(sym)) ?
577                           "" : _(" (NEW)"));
578                 if (menu->prompt->type == P_MENU) {
579                         item_add_str("  --->");
580                         return;
581                 }
582         }
583
584 conf_childs:
585         indent += doint;
586         for (child = menu->list; child; child = child->next)
587                 build_conf(child);
588         indent -= doint;
589 }
590
591 static void conf(struct menu *menu, struct menu *active_menu)
592 {
593         struct menu *submenu;
594         const char *prompt = menu_get_prompt(menu);
595         struct symbol *sym;
596         int res;
597         int s_scroll = 0;
598
599         while (1) {
600                 item_reset();
601                 current_menu = menu;
602                 build_conf(menu);
603                 if (!child_count)
604                         break;
605                 dialog_clear();
606                 res = dialog_menu(prompt ? _(prompt) : _("Main Menu"),
607                                   _(menu_instructions),
608                                   active_menu, &s_scroll);
609                 if (res == 1 || res == KEY_ESC || res == -ERRDISPLAYTOOSMALL)
610                         break;
611                 if (!item_activate_selected())
612                         continue;
613                 if (!item_tag())
614                         continue;
615
616                 submenu = item_data();
617                 active_menu = item_data();
618                 if (submenu)
619                         sym = submenu->sym;
620                 else
621                         sym = NULL;
622
623                 switch (res) {
624                 case 0:
625                         switch (item_tag()) {
626                         case 'm':
627                                 if (single_menu_mode)
628                                         submenu->data = (void *) (long) !submenu->data;
629                                 else
630                                         conf(submenu, NULL);
631                                 break;
632                         case 't':
633                                 if (sym_is_choice(sym) && sym_get_tristate_value(sym) == yes)
634                                         conf_choice(submenu);
635                                 else if (submenu->prompt->type == P_MENU)
636                                         conf(submenu, NULL);
637                                 break;
638                         case 's':
639                                 conf_string(submenu);
640                                 break;
641                         }
642                         break;
643                 case 2:
644                         if (sym)
645                                 show_help(submenu);
646                         else
647                                 show_helptext(_("README"), _(mconf_readme));
648                         break;
649                 case 3:
650                         conf_save();
651                         break;
652                 case 4:
653                         conf_load();
654                         break;
655                 case 5:
656                         if (item_is_tag('t')) {
657                                 if (sym_set_tristate_value(sym, yes))
658                                         break;
659                                 if (sym_set_tristate_value(sym, mod))
660                                         show_textbox(NULL, setmod_text, 6, 74);
661                         }
662                         break;
663                 case 6:
664                         if (item_is_tag('t'))
665                                 sym_set_tristate_value(sym, no);
666                         break;
667                 case 7:
668                         if (item_is_tag('t'))
669                                 sym_set_tristate_value(sym, mod);
670                         break;
671                 case 8:
672                         if (item_is_tag('t'))
673                                 sym_toggle_tristate_value(sym);
674                         else if (item_is_tag('m'))
675                                 conf(submenu, NULL);
676                         break;
677                 case 9:
678                         search_conf();
679                         break;
680                 case 10:
681                         show_all_options = !show_all_options;
682                         break;
683                 }
684         }
685 }
686
687 static int show_textbox_ext(const char *title, char *text, int r, int c, int
688                             *keys, int *vscroll, int *hscroll, update_text_fn
689                             update_text, void *data)
690 {
691         dialog_clear();
692         return dialog_textbox(title, text, r, c, keys, vscroll, hscroll,
693                               update_text, data);
694 }
695
696 static void show_textbox(const char *title, const char *text, int r, int c)
697 {
698         show_textbox_ext(title, (char *) text, r, c, (int []) {0}, NULL, NULL,
699                          NULL, NULL);
700 }
701
702 static void show_helptext(const char *title, const char *text)
703 {
704         show_textbox(title, text, 0, 0);
705 }
706
707 static void conf_message_callback(const char *fmt, va_list ap)
708 {
709         char buf[PATH_MAX+1];
710
711         vsnprintf(buf, sizeof(buf), fmt, ap);
712         if (save_and_exit)
713                 printf("%s", buf);
714         else
715                 show_textbox(NULL, buf, 6, 60);
716 }
717
718 static void show_help(struct menu *menu)
719 {
720         struct gstr help = str_new();
721
722         help.max_width = getmaxx(stdscr) - 10;
723         menu_get_ext_help(menu, &help);
724
725         show_helptext(_(menu_get_prompt(menu)), str_get(&help));
726         str_free(&help);
727 }
728
729 static void conf_choice(struct menu *menu)
730 {
731         const char *prompt = _(menu_get_prompt(menu));
732         struct menu *child;
733         struct symbol *active;
734
735         active = sym_get_choice_value(menu->sym);
736         while (1) {
737                 int res;
738                 int selected;
739                 item_reset();
740
741                 current_menu = menu;
742                 for (child = menu->list; child; child = child->next) {
743                         if (!menu_is_visible(child))
744                                 continue;
745                         if (child->sym)
746                                 item_make("%s", _(menu_get_prompt(child)));
747                         else {
748                                 item_make("*** %s ***", _(menu_get_prompt(child)));
749                                 item_set_tag(':');
750                         }
751                         item_set_data(child);
752                         if (child->sym == active)
753                                 item_set_selected(1);
754                         if (child->sym == sym_get_choice_value(menu->sym))
755                                 item_set_tag('X');
756                 }
757                 dialog_clear();
758                 res = dialog_checklist(prompt ? _(prompt) : _("Main Menu"),
759                                         _(radiolist_instructions),
760                                          15, 70, 6);
761                 selected = item_activate_selected();
762                 switch (res) {
763                 case 0:
764                         if (selected) {
765                                 child = item_data();
766                                 if (!child->sym)
767                                         break;
768
769                                 sym_set_tristate_value(child->sym, yes);
770                         }
771                         return;
772                 case 1:
773                         if (selected) {
774                                 child = item_data();
775                                 show_help(child);
776                                 active = child->sym;
777                         } else
778                                 show_help(menu);
779                         break;
780                 case KEY_ESC:
781                         return;
782                 case -ERRDISPLAYTOOSMALL:
783                         return;
784                 }
785         }
786 }
787
788 static void conf_string(struct menu *menu)
789 {
790         const char *prompt = menu_get_prompt(menu);
791
792         while (1) {
793                 int res;
794                 const char *heading;
795
796                 switch (sym_get_type(menu->sym)) {
797                 case S_INT:
798                         heading = _(inputbox_instructions_int);
799                         break;
800                 case S_HEX:
801                         heading = _(inputbox_instructions_hex);
802                         break;
803                 case S_STRING:
804                         heading = _(inputbox_instructions_string);
805                         break;
806                 default:
807                         heading = _("Internal mconf error!");
808                 }
809                 dialog_clear();
810                 res = dialog_inputbox(prompt ? _(prompt) : _("Main Menu"),
811                                       heading, 10, 75,
812                                       sym_get_string_value(menu->sym));
813                 switch (res) {
814                 case 0:
815                         if (sym_set_string_value(menu->sym, dialog_input_result))
816                                 return;
817                         show_textbox(NULL, _("You have made an invalid entry."), 5, 43);
818                         break;
819                 case 1:
820                         show_help(menu);
821                         break;
822                 case KEY_ESC:
823                         return;
824                 }
825         }
826 }
827
828 static void conf_load(void)
829 {
830
831         while (1) {
832                 int res;
833                 dialog_clear();
834                 res = dialog_inputbox(NULL, load_config_text,
835                                       11, 55, filename);
836                 switch(res) {
837                 case 0:
838                         if (!dialog_input_result[0])
839                                 return;
840                         if (!conf_read(dialog_input_result)) {
841                                 set_config_filename(dialog_input_result);
842                                 sym_set_change_count(1);
843                                 return;
844                         }
845                         show_textbox(NULL, _("File does not exist!"), 5, 38);
846                         break;
847                 case 1:
848                         show_helptext(_("Load Alternate Configuration"), load_config_help);
849                         break;
850                 case KEY_ESC:
851                         return;
852                 }
853         }
854 }
855
856 static void conf_save(void)
857 {
858         while (1) {
859                 int res;
860                 dialog_clear();
861                 res = dialog_inputbox(NULL, save_config_text,
862                                       11, 55, filename);
863                 switch(res) {
864                 case 0:
865                         if (!dialog_input_result[0])
866                                 return;
867                         if (!conf_write(dialog_input_result)) {
868                                 set_config_filename(dialog_input_result);
869                                 return;
870                         }
871                         show_textbox(NULL, _("Can't create file!  Probably a nonexistent directory."), 5, 60);
872                         break;
873                 case 1:
874                         show_helptext(_("Save Alternate Configuration"), save_config_help);
875                         break;
876                 case KEY_ESC:
877                         return;
878                 }
879         }
880 }
881
882 static int handle_exit(void)
883 {
884         int res;
885
886         save_and_exit = 1;
887         dialog_clear();
888         if (conf_get_changed())
889                 res = dialog_yesno(NULL,
890                                    _("Do you wish to save your new configuration ?\n"
891                                      "<ESC><ESC> to continue."),
892                                    6, 60);
893         else
894                 res = -1;
895
896         end_dialog(saved_x, saved_y);
897
898         switch (res) {
899         case 0:
900                 if (conf_write(filename)) {
901                         fprintf(stderr, _("\n\n"
902                                           "Error while writing of the configuration.\n"
903                                           "Your configuration changes were NOT saved."
904                                           "\n\n"));
905                         return 1;
906                 }
907                 /* fall through */
908         case -1:
909                 printf(_("\n\n"
910                          "*** End of the configuration.\n"
911                          "*** Execute 'make' to start the build or try 'make help'."
912                          "\n\n"));
913                 res = 0;
914                 break;
915         default:
916                 fprintf(stderr, _("\n\n"
917                                   "Your configuration changes were NOT saved."
918                                   "\n\n"));
919                 if (res != KEY_ESC)
920                         res = 0;
921         }
922
923         return res;
924 }
925
926 static void sig_handler(int signo)
927 {
928         exit(handle_exit());
929 }
930
931 int main(int ac, char **av)
932 {
933         char *mode;
934         int res;
935
936         setlocale(LC_ALL, "");
937         bindtextdomain(PACKAGE, LOCALEDIR);
938         textdomain(PACKAGE);
939
940         signal(SIGINT, sig_handler);
941
942         conf_parse(av[1]);
943         conf_read(NULL);
944
945         mode = getenv("MENUCONFIG_MODE");
946         if (mode) {
947                 if (!strcasecmp(mode, "single_menu"))
948                         single_menu_mode = 1;
949         }
950
951         if (init_dialog(NULL)) {
952                 fprintf(stderr, N_("Your display is too small to run Menuconfig!\n"));
953                 fprintf(stderr, N_("It must be at least 19 lines by 80 columns.\n"));
954                 return 1;
955         }
956
957         set_config_filename(conf_get_configname());
958         conf_set_message_callback(conf_message_callback);
959         do {
960                 conf(&rootmenu, NULL);
961                 res = handle_exit();
962         } while (res == KEY_ESC);
963
964         return res;
965 }
966