mdns: update to latest git head
[openwrt.git] / scripts / config / 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 "Some OpenWrt features may be built directly into the image.\n"
29 "Some may be made into installable ipkg packages. Some features\n"
30 "may be completely removed altogether.\n"
31 "\n"
32 "Menu items beginning with [*], <M> or [ ] represent features\n"
33 "configured to be included, built as package or removed respectively.\n"
34 "Pointed brackets <> represent packaging capable features.\n"
35 "\n"
36 "To change any of these features, highlight it with the cursor\n"
37 "keys and press <Y> to build it in, <M> to make it a module or\n"
38 "<N> to removed it.  You may also press the <Space Bar> to cycle\n"
39 "through the available options (ie. Y->N->M->Y).\n"
40 "\n"
41 "Some additional keyboard hints:\n"
42 "\n"
43 "Menus\n"
44 "----------\n"
45 "o  Use the Up/Down arrow keys (cursor keys) to highlight the item\n"
46 "   you wish to change or submenu wish to select and press <Enter>.\n"
47 "   Submenus are designated by \"--->\".\n"
48 "\n"
49 "   Shortcut: Press the option's highlighted letter (hotkey).\n"
50 "             Pressing a hotkey more than once will sequence\n"
51 "             through all visible items which use that hotkey.\n"
52 "\n"
53 "   You may also use the <PAGE UP> and <PAGE DOWN> keys to scroll\n"
54 "   unseen options into view.\n"
55 "\n"
56 "o  To exit a menu use the cursor keys to highlight the <Exit> button\n"
57 "   and press <ENTER>.\n"
58 "\n"
59 "   Shortcut: Press <ESC><ESC> or <E> or <X> if there is no hotkey\n"
60 "             using those letters.  You may press a single <ESC>, but\n"
61 "             there is a delayed response which you may find annoying.\n"
62 "\n"
63 "   Also, the <TAB> and cursor keys will cycle between <Select>,\n"
64 "   <Exit> and <Help>.\n"
65 "\n"
66 "o  To get help with an item, use the cursor keys to highlight <Help>\n"
67 "   and press <ENTER>.\n"
68 "\n"
69 "   Shortcut: Press <H> or <?>.\n"
70 "\n"
71 "o  To toggle the display of hidden options, press <Z>.\n"
72 "\n"
73 "\n"
74 "Radiolists  (Choice lists)\n"
75 "-----------\n"
76 "o  Use the cursor keys to select the option you wish to set and press\n"
77 "   <S> or the <SPACE BAR>.\n"
78 "\n"
79 "   Shortcut: Press the first letter of the option you wish to set then\n"
80 "             press <S> or <SPACE BAR>.\n"
81 "\n"
82 "o  To see available help for the item, use the cursor keys to highlight\n"
83 "   <Help> and Press <ENTER>.\n"
84 "\n"
85 "   Shortcut: Press <H> or <?>.\n"
86 "\n"
87 "   Also, the <TAB> and cursor keys will cycle between <Select> and\n"
88 "   <Help>\n"
89 "\n"
90 "\n"
91 "Data Entry\n"
92 "-----------\n"
93 "o  Enter the requested information and press <ENTER>\n"
94 "   If you are entering hexadecimal values, it is not necessary to\n"
95 "   add the '0x' prefix to the entry.\n"
96 "\n"
97 "o  For help, use the <TAB> or cursor keys to highlight the help option\n"
98 "   and press <ENTER>.  You can try <TAB><H> as well.\n"
99 "\n"
100 "\n"
101 "Text Box    (Help Window)\n"
102 "--------\n"
103 "o  Use the cursor keys to scroll up/down/left/right.  The VI editor\n"
104 "   keys h,j,k,l function here as do <u>, <d>, <SPACE BAR> and <B> for \n"
105 "   those who are familiar with less and lynx.\n"
106 "\n"
107 "o  Press <E>, <X>, <q>, <Enter> or <Esc><Esc> to exit.\n"
108 "\n"
109 "\n"
110 "Alternate Configuration Files\n"
111 "-----------------------------\n"
112 "Menuconfig supports the use of alternate configuration files for\n"
113 "those who, for various reasons, find it necessary to switch\n"
114 "between different configurations.\n"
115 "\n"
116 "At the end of the main menu you will find two options.  One is\n"
117 "for saving the current configuration to a file of your choosing.\n"
118 "The other option is for loading a previously saved alternate\n"
119 "configuration.\n"
120 "\n"
121 "Even if you don't use alternate configuration files, but you\n"
122 "find during a Menuconfig session that you have completely messed\n"
123 "up your settings, you may use the \"Load Alternate...\" option to\n"
124 "restore your previously saved settings from \".config\" without\n"
125 "restarting Menuconfig.\n"
126 "\n"
127 "Other information\n"
128 "-----------------\n"
129 "If you use Menuconfig in an XTERM window make sure you have your\n"
130 "$TERM variable set to point to a xterm definition which supports color.\n"
131 "Otherwise, Menuconfig will look rather bad.  Menuconfig will not\n"
132 "display correctly in a RXVT window because rxvt displays only one\n"
133 "intensity of color, bright.\n"
134 "\n"
135 "Menuconfig will display larger menus on screens or xterms which are\n"
136 "set to display more than the standard 25 row by 80 column geometry.\n"
137 "In order for this to work, the \"stty size\" command must be able to\n"
138 "display the screen's current row and column geometry.  I STRONGLY\n"
139 "RECOMMEND that you make sure you do NOT have the shell variables\n"
140 "LINES and COLUMNS exported into your environment.  Some distributions\n"
141 "export those variables via /etc/profile.  Some ncurses programs can\n"
142 "become confused when those variables (LINES & COLUMNS) don't reflect\n"
143 "the true screen size.\n"
144 "\n"
145 "Optional personality available\n"
146 "------------------------------\n"
147 "If you prefer to have all of the options listed in a single menu, rather\n"
148 "than the default multimenu hierarchy, run the menuconfig with\n"
149 "MENUCONFIG_MODE environment variable set to single_menu. Example:\n"
150 "\n"
151 "make MENUCONFIG_MODE=single_menu menuconfig\n"
152 "\n"
153 "<Enter> will then unroll the appropriate category, or enfold it if it\n"
154 "is already unrolled.\n"
155 "\n"
156 "Note that this mode can eventually be a little more CPU expensive\n"
157 "(especially with a larger number of unrolled categories) than the\n"
158 "default mode.\n"
159 "\n"
160 "Different color themes available\n"
161 "--------------------------------\n"
162 "It is possible to select different color themes using the variable\n"
163 "MENUCONFIG_COLOR. To select a theme use:\n"
164 "\n"
165 "make MENUCONFIG_COLOR=<theme> menuconfig\n"
166 "\n"
167 "Available themes are\n"
168 " mono       => selects colors suitable for monochrome displays\n"
169 " blackbg    => selects a color scheme with black background\n"
170 " classic    => theme with blue background. The classic look\n"
171 " bluetitle  => a LCD friendly version of classic. (default)\n"
172 "\n"),
173 menu_instructions[] = N_(
174         "Arrow keys navigate the menu.  "
175         "<Enter> selects submenus --->.  "
176         "Highlighted letters are hotkeys.  "
177         "Pressing <Y> includes, <N> excludes, <M> modularizes features.  "
178         "Press <Esc><Esc> to exit, <?> for Help, </> for Search.  "
179         "Legend: [*] built-in  [ ] excluded  <M> module  < > module capable"),
180 radiolist_instructions[] = N_(
181         "Use the arrow keys to navigate this window or "
182         "press the hotkey of the item you wish to select "
183         "followed by the <SPACE BAR>. "
184         "Press <?> for additional information about this option."),
185 inputbox_instructions_int[] = N_(
186         "Please enter a decimal value. "
187         "Fractions will not be accepted.  "
188         "Use the <TAB> key to move from the input field to the buttons below it."),
189 inputbox_instructions_hex[] = N_(
190         "Please enter a hexadecimal value. "
191         "Use the <TAB> key to move from the input field to the buttons below it."),
192 inputbox_instructions_string[] = N_(
193         "Please enter a string value. "
194         "Use the <TAB> key to move from the input field to the buttons below it."),
195 setmod_text[] = N_(
196         "This feature depends on another which has been configured as a module.\n"
197         "As a result, this feature will be built as a module."),
198 load_config_text[] = N_(
199         "Enter the name of the configuration file you wish to load.  "
200         "Accept the name shown to restore the configuration you "
201         "last retrieved.  Leave blank to abort."),
202 load_config_help[] = N_(
203         "\n"
204         "For various reasons, one may wish to keep several different\n"
205         "configurations available on a single machine.\n"
206         "\n"
207         "If you have saved a previous configuration in a file other than the\n"
208         "default one, entering its name here will allow you to modify that\n"
209         "configuration.\n"
210         "\n"
211         "If you are uncertain, then you have probably never used alternate\n"
212         "configuration files. You should therefore leave this blank to abort.\n"),
213 save_config_text[] = N_(
214         "Enter a filename to which this configuration should be saved "
215         "as an alternate.  Leave blank to abort."),
216 save_config_help[] = N_(
217         "\n"
218         "For various reasons, one may wish to keep different configurations\n"
219         "available on a single machine.\n"
220         "\n"
221         "Entering a file name here will allow you to later retrieve, modify\n"
222         "and use the current configuration as an alternate to whatever\n"
223         "configuration options you have selected at that time.\n"
224         "\n"
225         "If you are uncertain what all this means then you should probably\n"
226         "leave this blank.\n"),
227 search_help[] = N_(
228         "\n"
229         "Search for symbols and display their relations.\n"
230         "Regular expressions are allowed.\n"
231         "Example: search for \"^FOO\"\n"
232         "Result:\n"
233         "-----------------------------------------------------------------\n"
234         "Symbol: FOO [=m]\n"
235         "Type  : tristate\n"
236         "Prompt: Foo bus is used to drive the bar HW\n"
237         "  Defined at drivers/pci/Kconfig:47\n"
238         "  Depends on: X86_LOCAL_APIC && X86_IO_APIC || IA64\n"
239         "  Location:\n"
240         "    -> Bus options (PCI, PCMCIA, EISA, ISA)\n"
241         "      -> PCI support (PCI [=y])\n"
242         "(1)     -> PCI access mode (<choice> [=y])\n"
243         "  Selects: LIBCRC32\n"
244         "  Selected by: BAR\n"
245         "-----------------------------------------------------------------\n"
246         "o The line 'Type:' shows the type of the configuration option for\n"
247         "  this symbol (boolean, tristate, string, ...)\n"
248         "o The line 'Prompt:' shows the text used in the menu structure for\n"
249         "  this symbol\n"
250         "o The 'Defined at' line tell at what file / line number the symbol\n"
251         "  is defined\n"
252         "o The 'Depends on:' line tell what symbols needs to be defined for\n"
253         "  this symbol to be visible in the menu (selectable)\n"
254         "o The 'Location:' lines tell where in the menu structure this symbol\n"
255         "  is located\n"
256         "    A location followed by a [=y] indicates that this is a\n"
257         "    selectable menu item - and the current value is displayed inside\n"
258         "    brackets.\n"
259         "    Press the key in the (#) prefix to jump directly to that\n"
260         "    location. You will be returned to the current search results\n"
261         "    after exiting this new menu.\n"
262         "o The 'Selects:' line tell what symbol will be automatically\n"
263         "  selected if this symbol is selected (y or m)\n"
264         "o The 'Selected by' line tell what symbol has selected this symbol\n"
265         "\n"
266         "Only relevant lines are shown.\n"
267         "\n\n"
268         "Search examples:\n"
269         "Examples: USB  => find all symbols containing USB\n"
270         "          ^USB => find all symbols starting with USB\n"
271         "          USB$ => find all symbols ending with USB\n"
272         "\n");
273
274 static int indent;
275 static struct menu *current_menu;
276 static int child_count;
277 static int single_menu_mode;
278 static int show_all_options;
279 static int save_and_exit;
280
281 static void conf(struct menu *menu, struct menu *active_menu);
282 static void conf_choice(struct menu *menu);
283 static void conf_string(struct menu *menu);
284 static void conf_load(void);
285 static void conf_save(void);
286 static int show_textbox_ext(const char *title, char *text, int r, int c,
287                             int *keys, int *vscroll, int *hscroll,
288                             update_text_fn update_text, void *data);
289 static void show_textbox(const char *title, const char *text, int r, int c);
290 static void show_helptext(const char *title, const char *text);
291 static void show_help(struct menu *menu);
292
293 static char filename[PATH_MAX+1];
294 static void set_config_filename(const char *config_filename)
295 {
296         static char menu_backtitle[PATH_MAX+128];
297         int size;
298
299         size = snprintf(menu_backtitle, sizeof(menu_backtitle),
300                         "%s - %s", config_filename, rootmenu.prompt->text);
301         if (size >= sizeof(menu_backtitle))
302                 menu_backtitle[sizeof(menu_backtitle)-1] = '\0';
303         set_dialog_backtitle(menu_backtitle);
304
305         size = snprintf(filename, sizeof(filename), "%s", config_filename);
306         if (size >= sizeof(filename))
307                 filename[sizeof(filename)-1] = '\0';
308 }
309
310
311 struct search_data {
312         struct list_head *head;
313         struct menu **targets;
314         int *keys;
315 };
316
317 static void update_text(char *buf, size_t start, size_t end, void *_data)
318 {
319         struct search_data *data = _data;
320         struct jump_key *pos;
321         int k = 0;
322
323         list_for_each_entry(pos, data->head, entries) {
324                 if (pos->offset >= start && pos->offset < end) {
325                         char header[4];
326
327                         if (k < JUMP_NB) {
328                                 int key = '0' + (pos->index % JUMP_NB) + 1;
329
330                                 sprintf(header, "(%c)", key);
331                                 data->keys[k] = key;
332                                 data->targets[k] = pos->target;
333                                 k++;
334                         } else {
335                                 sprintf(header, "   ");
336                         }
337
338                         memcpy(buf + pos->offset, header, sizeof(header) - 1);
339                 }
340         }
341         data->keys[k] = 0;
342 }
343
344 static void search_conf(void)
345 {
346         struct symbol **sym_arr;
347         struct gstr res;
348         struct gstr title;
349         char *dialog_input;
350         int dres, vscroll = 0, hscroll = 0;
351         bool again;
352
353         title = str_new();
354         str_printf( &title, _("Enter %s (sub)string to search for "
355                               "(with or without \"%s\")"), CONFIG_, CONFIG_);
356
357 again:
358         dialog_clear();
359         dres = dialog_inputbox(_("Search Configuration Parameter"),
360                               str_get(&title),
361                               10, 75, "");
362         switch (dres) {
363         case 0:
364                 break;
365         case 1:
366                 show_helptext(_("Search Configuration"), search_help);
367                 goto again;
368         default:
369                 str_free(&title);
370                 return;
371         }
372
373         /* strip the prefix if necessary */
374         dialog_input = dialog_input_result;
375         if (strncasecmp(dialog_input_result, CONFIG_, strlen(CONFIG_)) == 0)
376                 dialog_input += strlen(CONFIG_);
377
378         sym_arr = sym_re_search(dialog_input);
379         do {
380                 LIST_HEAD(head);
381                 struct menu *targets[JUMP_NB];
382                 int keys[JUMP_NB + 1], i;
383                 struct search_data data = {
384                         .head = &head,
385                         .targets = targets,
386                         .keys = keys,
387                 };
388
389                 res = get_relations_str(sym_arr, &head);
390                 dres = show_textbox_ext(_("Search Results"), (char *)
391                                         str_get(&res), 0, 0, keys, &vscroll,
392                                         &hscroll, &update_text, (void *)
393                                         &data);
394                 again = false;
395                 for (i = 0; i < JUMP_NB && keys[i]; i++)
396                         if (dres == keys[i]) {
397                                 conf(targets[i]->parent, targets[i]);
398                                 again = true;
399                         }
400                 str_free(&res);
401         } while (again);
402         free(sym_arr);
403         str_free(&title);
404 }
405
406 static void build_conf(struct menu *menu)
407 {
408         struct symbol *sym;
409         struct property *prop;
410         struct menu *child;
411         int type, tmp, doint = 2;
412         tristate val;
413         char ch;
414         bool visible;
415
416         /*
417          * note: menu_is_visible() has side effect that it will
418          * recalc the value of the symbol.
419          */
420         visible = menu_is_visible(menu);
421         if (show_all_options && !menu_has_prompt(menu))
422                 return;
423         else if (!show_all_options && !visible)
424                 return;
425
426         sym = menu->sym;
427         prop = menu->prompt;
428         if (!sym) {
429                 if (prop && menu != current_menu) {
430                         const char *prompt = menu_get_prompt(menu);
431                         switch (prop->type) {
432                         case P_MENU:
433                                 child_count++;
434                                 prompt = _(prompt);
435                                 if (single_menu_mode) {
436                                         item_make("%s%*c%s",
437                                                   menu->data ? "-->" : "++>",
438                                                   indent + 1, ' ', prompt);
439                                 } else
440                                         item_make("   %*c%s  --->", indent + 1, ' ', prompt);
441
442                                 item_set_tag('m');
443                                 item_set_data(menu);
444                                 if (single_menu_mode && menu->data)
445                                         goto conf_childs;
446                                 return;
447                         case P_COMMENT:
448                                 if (prompt) {
449                                         child_count++;
450                                         item_make("   %*c*** %s ***", indent + 1, ' ', _(prompt));
451                                         item_set_tag(':');
452                                         item_set_data(menu);
453                                 }
454                                 break;
455                         default:
456                                 if (prompt) {
457                                         child_count++;
458                                         item_make("---%*c%s", indent + 1, ' ', _(prompt));
459                                         item_set_tag(':');
460                                         item_set_data(menu);
461                                 }
462                         }
463                 } else
464                         doint = 0;
465                 goto conf_childs;
466         }
467
468         type = sym_get_type(sym);
469         if (sym_is_choice(sym)) {
470                 struct symbol *def_sym = sym_get_choice_value(sym);
471                 struct menu *def_menu = NULL;
472
473                 child_count++;
474                 for (child = menu->list; child; child = child->next) {
475                         if (menu_is_visible(child) && child->sym == def_sym)
476                                 def_menu = child;
477                 }
478
479                 val = sym_get_tristate_value(sym);
480                 if (sym_is_changable(sym)) {
481                         switch (type) {
482                         case S_BOOLEAN:
483                                 item_make("[%c]", val == no ? ' ' : '*');
484                                 break;
485                         case S_TRISTATE:
486                                 switch (val) {
487                                 case yes: ch = '*'; break;
488                                 case mod: ch = 'M'; break;
489                                 default:  ch = ' '; break;
490                                 }
491                                 item_make("<%c>", ch);
492                                 break;
493                         }
494                         item_set_tag('t');
495                         item_set_data(menu);
496                 } else {
497                         item_make("   ");
498                         item_set_tag(def_menu ? 't' : ':');
499                         item_set_data(menu);
500                 }
501
502                 item_add_str("%*c%s", indent + 1, ' ', _(menu_get_prompt(menu)));
503                 if (val == yes) {
504                         if (def_menu) {
505                                 item_add_str(" (%s)", _(menu_get_prompt(def_menu)));
506                                 item_add_str("  --->");
507                                 if (def_menu->list) {
508                                         indent += 2;
509                                         build_conf(def_menu);
510                                         indent -= 2;
511                                 }
512                         }
513                         return;
514                 }
515         } else {
516                 if (menu == current_menu) {
517                         item_make("---%*c%s", indent + 1, ' ', _(menu_get_prompt(menu)));
518                         item_set_tag(':');
519                         item_set_data(menu);
520                         goto conf_childs;
521                 }
522                 child_count++;
523                 val = sym_get_tristate_value(sym);
524                 if (sym_is_choice_value(sym) && val == yes) {
525                         item_make("   ");
526                         item_set_tag(':');
527                         item_set_data(menu);
528                 } else {
529                         switch (type) {
530                         case S_BOOLEAN:
531                                 if (sym_is_changable(sym))
532                                         item_make("[%c]", val == no ? ' ' : '*');
533                                 else
534                                         item_make("-%c-", val == no ? ' ' : '*');
535                                 item_set_tag('t');
536                                 item_set_data(menu);
537                                 break;
538                         case S_TRISTATE:
539                                 switch (val) {
540                                 case yes: ch = '*'; break;
541                                 case mod: ch = 'M'; break;
542                                 default:  ch = ' '; break;
543                                 }
544                                 if (sym_is_changable(sym)) {
545                                         if (sym->rev_dep.tri == mod)
546                                                 item_make("{%c}", ch);
547                                         else
548                                                 item_make("<%c>", ch);
549                                 } else
550                                         item_make("-%c-", ch);
551                                 item_set_tag('t');
552                                 item_set_data(menu);
553                                 break;
554                         default:
555                                 tmp = 2 + strlen(sym_get_string_value(sym)); /* () = 2 */
556                                 item_make("(%s)", sym_get_string_value(sym));
557                                 tmp = indent - tmp + 4;
558                                 if (tmp < 0)
559                                         tmp = 0;
560                                 item_add_str("%*c%s%s", tmp, ' ', _(menu_get_prompt(menu)),
561                                              (sym_has_value(sym) || !sym_is_changable(sym)) ?
562                                              "" : _(" (NEW)"));
563                                 item_set_tag('s');
564                                 item_set_data(menu);
565                                 goto conf_childs;
566                         }
567                 }
568                 item_add_str("%*c%s%s", indent + 1, ' ', _(menu_get_prompt(menu)),
569                           (sym_has_value(sym) || !sym_is_changable(sym)) ?
570                           "" : _(" (NEW)"));
571                 if (menu->prompt->type == P_MENU) {
572                         item_add_str("  --->");
573                         return;
574                 }
575         }
576
577 conf_childs:
578         indent += doint;
579         for (child = menu->list; child; child = child->next)
580                 build_conf(child);
581         indent -= doint;
582 }
583
584 static void conf(struct menu *menu, struct menu *active_menu)
585 {
586         struct menu *submenu;
587         const char *prompt = menu_get_prompt(menu);
588         struct symbol *sym;
589         int res;
590         int s_scroll = 0;
591
592         while (1) {
593                 item_reset();
594                 current_menu = menu;
595                 build_conf(menu);
596                 if (!child_count)
597                         break;
598                 dialog_clear();
599                 res = dialog_menu(prompt ? _(prompt) : _("Main Menu"),
600                                   _(menu_instructions),
601                                   active_menu, &s_scroll);
602                 if (res == 1 || res == KEY_ESC || res == -ERRDISPLAYTOOSMALL)
603                         break;
604                 if (!item_activate_selected())
605                         continue;
606                 if (!item_tag())
607                         continue;
608
609                 submenu = item_data();
610                 active_menu = item_data();
611                 if (submenu)
612                         sym = submenu->sym;
613                 else
614                         sym = NULL;
615
616                 switch (res) {
617                 case 0:
618                         switch (item_tag()) {
619                         case 'm':
620                                 if (single_menu_mode)
621                                         submenu->data = (void *) (long) !submenu->data;
622                                 else
623                                         conf(submenu, NULL);
624                                 break;
625                         case 't':
626                                 if (sym_is_choice(sym) && sym_get_tristate_value(sym) == yes)
627                                         conf_choice(submenu);
628                                 else if (submenu->prompt->type == P_MENU)
629                                         conf(submenu, NULL);
630                                 break;
631                         case 's':
632                                 conf_string(submenu);
633                                 break;
634                         }
635                         break;
636                 case 2:
637                         if (sym)
638                                 show_help(submenu);
639                         else
640                                 show_helptext(_("README"), _(mconf_readme));
641                         break;
642                 case 3:
643                         conf_save();
644                         break;
645                 case 4:
646                         conf_load();
647                         break;
648                 case 5:
649                         if (item_is_tag('t')) {
650                                 if (sym_set_tristate_value(sym, yes))
651                                         break;
652                                 if (sym_set_tristate_value(sym, mod))
653                                         show_textbox(NULL, setmod_text, 6, 74);
654                         }
655                         break;
656                 case 6:
657                         if (item_is_tag('t'))
658                                 sym_set_tristate_value(sym, no);
659                         break;
660                 case 7:
661                         if (item_is_tag('t'))
662                                 sym_set_tristate_value(sym, mod);
663                         break;
664                 case 8:
665                         if (item_is_tag('t'))
666                                 sym_toggle_tristate_value(sym);
667                         else if (item_is_tag('m'))
668                                 conf(submenu, NULL);
669                         break;
670                 case 9:
671                         search_conf();
672                         break;
673                 case 10:
674                         show_all_options = !show_all_options;
675                         break;
676                 }
677         }
678 }
679
680 static int show_textbox_ext(const char *title, char *text, int r, int c, int
681                             *keys, int *vscroll, int *hscroll, update_text_fn
682                             update_text, void *data)
683 {
684         dialog_clear();
685         return dialog_textbox(title, text, r, c, keys, vscroll, hscroll,
686                               update_text, data);
687 }
688
689 static void show_textbox(const char *title, const char *text, int r, int c)
690 {
691         show_textbox_ext(title, (char *) text, r, c, (int []) {0}, NULL, NULL,
692                          NULL, NULL);
693 }
694
695 static void show_helptext(const char *title, const char *text)
696 {
697         show_textbox(title, text, 0, 0);
698 }
699
700 static void conf_message_callback(const char *fmt, va_list ap)
701 {
702         char buf[PATH_MAX+1];
703
704         vsnprintf(buf, sizeof(buf), fmt, ap);
705         if (save_and_exit)
706                 printf("%s", buf);
707         else
708                 show_textbox(NULL, buf, 6, 60);
709 }
710
711 static void show_help(struct menu *menu)
712 {
713         struct gstr help = str_new();
714
715         help.max_width = getmaxx(stdscr) - 10;
716         menu_get_ext_help(menu, &help);
717
718         show_helptext(_(menu_get_prompt(menu)), str_get(&help));
719         str_free(&help);
720 }
721
722 static void conf_choice(struct menu *menu)
723 {
724         const char *prompt = _(menu_get_prompt(menu));
725         struct menu *child;
726         struct symbol *active;
727         struct property *prop;
728
729         active = sym_get_choice_value(menu->sym);
730         while (1) {
731                 int res;
732                 int selected;
733                 item_reset();
734
735                 current_menu = menu;
736                 for (child = menu->list; child; child = child->next) {
737                         if (!menu_is_visible(child))
738                                 continue;
739                         if (child->sym)
740                                 item_make("%s", _(menu_get_prompt(child)));
741                         else {
742                                 item_make("*** %s ***", _(menu_get_prompt(child)));
743                                 item_set_tag(':');
744                         }
745                         item_set_data(child);
746                         if (child->sym == active)
747                                 item_set_selected(1);
748                         if (child->sym == sym_get_choice_value(menu->sym))
749                                 item_set_tag('X');
750                 }
751                 dialog_clear();
752                 res = dialog_checklist(prompt ? _(prompt) : _("Main Menu"),
753                                         _(radiolist_instructions),
754                                          15, 70, 6);
755                 selected = item_activate_selected();
756                 switch (res) {
757                 case 0:
758                         if (selected) {
759                                 child = item_data();
760                                 if (!child->sym)
761                                         break;
762
763                                 if (sym_get_tristate_value(child->sym) != yes) {
764                                         for_all_properties(menu->sym, prop, P_RESET) {
765                                                 if (expr_calc_value(prop->visible.expr) == no)
766                                                         continue;
767
768                                                 conf_reset(S_DEF_USER);
769                                                 break;
770                                         }
771                                 }
772                                 sym_set_tristate_value(child->sym, yes);
773                         }
774                         return;
775                 case 1:
776                         if (selected) {
777                                 child = item_data();
778                                 show_help(child);
779                                 active = child->sym;
780                         } else
781                                 show_help(menu);
782                         break;
783                 case KEY_ESC:
784                         return;
785                 case -ERRDISPLAYTOOSMALL:
786                         return;
787                 }
788         }
789 }
790
791 static void conf_string(struct menu *menu)
792 {
793         const char *prompt = menu_get_prompt(menu);
794
795         while (1) {
796                 int res;
797                 const char *heading;
798
799                 switch (sym_get_type(menu->sym)) {
800                 case S_INT:
801                         heading = _(inputbox_instructions_int);
802                         break;
803                 case S_HEX:
804                         heading = _(inputbox_instructions_hex);
805                         break;
806                 case S_STRING:
807                         heading = _(inputbox_instructions_string);
808                         break;
809                 default:
810                         heading = _("Internal mconf error!");
811                 }
812                 dialog_clear();
813                 res = dialog_inputbox(prompt ? _(prompt) : _("Main Menu"),
814                                       heading, 10, 75,
815                                       sym_get_string_value(menu->sym));
816                 switch (res) {
817                 case 0:
818                         if (sym_set_string_value(menu->sym, dialog_input_result))
819                                 return;
820                         show_textbox(NULL, _("You have made an invalid entry."), 5, 43);
821                         break;
822                 case 1:
823                         show_help(menu);
824                         break;
825                 case KEY_ESC:
826                         return;
827                 }
828         }
829 }
830
831 static void conf_load(void)
832 {
833
834         while (1) {
835                 int res;
836                 dialog_clear();
837                 res = dialog_inputbox(NULL, load_config_text,
838                                       11, 55, filename);
839                 switch(res) {
840                 case 0:
841                         if (!dialog_input_result[0])
842                                 return;
843                         if (!conf_read(dialog_input_result)) {
844                                 set_config_filename(dialog_input_result);
845                                 sym_set_change_count(1);
846                                 return;
847                         }
848                         show_textbox(NULL, _("File does not exist!"), 5, 38);
849                         break;
850                 case 1:
851                         show_helptext(_("Load Alternate Configuration"), load_config_help);
852                         break;
853                 case KEY_ESC:
854                         return;
855                 }
856         }
857 }
858
859 static void conf_save(void)
860 {
861         while (1) {
862                 int res;
863                 dialog_clear();
864                 res = dialog_inputbox(NULL, save_config_text,
865                                       11, 55, filename);
866                 switch(res) {
867                 case 0:
868                         if (!dialog_input_result[0])
869                                 return;
870                         if (!conf_write(dialog_input_result)) {
871                                 set_config_filename(dialog_input_result);
872                                 return;
873                         }
874                         show_textbox(NULL, _("Can't create file!  Probably a nonexistent directory."), 5, 60);
875                         break;
876                 case 1:
877                         show_helptext(_("Save Alternate Configuration"), save_config_help);
878                         break;
879                 case KEY_ESC:
880                         return;
881                 }
882         }
883 }
884
885 static int handle_exit(void)
886 {
887         int res;
888
889         save_and_exit = 1;
890         dialog_clear();
891         if (conf_get_changed())
892                 res = dialog_yesno(NULL,
893                                    _("Do you wish to save your new configuration ?\n"
894                                      "<ESC><ESC> to continue."),
895                                    6, 60);
896         else
897                 res = -1;
898
899         end_dialog(saved_x, saved_y);
900
901         switch (res) {
902         case 0:
903                 if (conf_write(filename)) {
904                         fprintf(stderr, _("\n\n"
905                                           "Error while writing of the configuration.\n"
906                                           "Your configuration changes were NOT saved."
907                                           "\n\n"));
908                         return 1;
909                 }
910                 /* fall through */
911         case -1:
912                 printf(_("\n\n"
913                          "*** End of the configuration.\n"
914                          "*** Execute 'make' to start the build or try 'make help'."
915                          "\n\n"));
916                 res = 0;
917                 break;
918         default:
919                 fprintf(stderr, _("\n\n"
920                                   "Your configuration changes were NOT saved."
921                                   "\n\n"));
922                 if (res != KEY_ESC)
923                         res = 0;
924         }
925
926         return res;
927 }
928
929 static void sig_handler(int signo)
930 {
931         exit(handle_exit());
932 }
933
934 int main(int ac, char **av)
935 {
936         char *mode;
937         int res;
938
939         setlocale(LC_ALL, "");
940         bindtextdomain(PACKAGE, LOCALEDIR);
941         textdomain(PACKAGE);
942
943         signal(SIGINT, sig_handler);
944
945         conf_parse(av[1]);
946         conf_read(NULL);
947
948         mode = getenv("MENUCONFIG_MODE");
949         if (mode) {
950                 if (!strcasecmp(mode, "single_menu"))
951                         single_menu_mode = 1;
952         }
953
954         if (init_dialog(NULL)) {
955                 fprintf(stderr, N_("Your display is too small to run Menuconfig!\n"));
956                 fprintf(stderr, N_("It must be at least 19 lines by 80 columns.\n"));
957                 return 1;
958         }
959
960         set_config_filename(conf_get_configname());
961         conf_set_message_callback(conf_message_callback);
962         do {
963                 conf(&rootmenu, NULL);
964                 res = handle_exit();
965         } while (res == KEY_ESC);
966
967         return res;
968 }
969