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