termios.h is already included so dont include sys/termios.h as well
[oweals/busybox.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  * Directly use liblxdialog library routines.
9  * 2002-11-14 Petr Baudis <pasky@ucw.cz>
10  */
11
12 #include <sys/ioctl.h>
13 #include <sys/wait.h>
14 #include <ctype.h>
15 #include <errno.h>
16 #include <fcntl.h>
17 #include <limits.h>
18 #include <signal.h>
19 #include <stdarg.h>
20 #include <stdlib.h>
21 #include <string.h>
22 #include <termios.h>
23 #include <unistd.h>
24
25 #include "lxdialog/dialog.h"
26
27 #define LKC_DIRECT_LINK
28 #include "lkc.h"
29
30 static char menu_backtitle[128];
31 static const char mconf_readme[] =
32 "Overview\n"
33 "--------\n"
34 "Some features may be built directly into BusyBox.  Some features\n"
35 "may be completely removed altogether.  There are also certain\n"
36 "parameters which are not really features, but must be\n"
37 "entered in as decimal or hexadecimal numbers or possibly text.\n"
38 "\n"
39 "Menu items beginning with [*] or [ ] represent features\n"
40 "configured to be built in or removed respectively.\n"
41 "\n"
42 "To change any of these features, highlight it with the cursor\n"
43 "keys and press <Y> to build it in or <N> to removed it.\n"
44 "You may also press the <Space Bar> to cycle\n"
45 "through the available options (ie. Y->N->Y).\n"
46 "\n"
47 "Some additional keyboard hints:\n"
48 "\n"
49 "Menus\n"
50 "----------\n"
51 "o  Use the Up/Down arrow keys (cursor keys) to highlight the item\n"
52 "   you wish to change or submenu wish to select and press <Enter>.\n"
53 "   Submenus are designated by \"--->\".\n"
54 "\n"
55 "   Shortcut: Press the option's highlighted letter (hotkey).\n"
56 "             Pressing a hotkey more than once will sequence\n"
57 "             through all visible items which use that hotkey.\n"
58 "\n"
59 "   You may also use the <PAGE UP> and <PAGE DOWN> keys to scroll\n"
60 "   unseen options into view.\n"
61 "\n"
62 "o  To exit a menu use the cursor keys to highlight the <Exit> button\n"
63 "   and press <ENTER>.\n"
64 "\n"
65 "   Shortcut: Press <ESC><ESC> or <E> or <X> if there is no hotkey\n"
66 "             using those letters.  You may press a single <ESC>, but\n"
67 "             there is a delayed response which you may find annoying.\n"
68 "\n"
69 "   Also, the <TAB> and cursor keys will cycle between <Select>,\n"
70 "   <Exit> and <Help>\n"
71 "\n"
72 "o  To get help with an item, use the cursor keys to highlight <Help>\n"
73 "   and Press <ENTER>.\n"
74 "\n"
75 "   Shortcut: Press <H> or <?>.\n"
76 "\n"
77 "\n"
78 "Radiolists  (Choice lists)\n"
79 "-----------\n"
80 "o  Use the cursor keys to select the option you wish to set and press\n"
81 "   <S> or the <SPACE BAR>.\n"
82 "\n"
83 "   Shortcut: Press the first letter of the option you wish to set then\n"
84 "             press <S> or <SPACE BAR>.\n"
85 "\n"
86 "o  To see available help for the item, use the cursor keys to highlight\n"
87 "   <Help> and Press <ENTER>.\n"
88 "\n"
89 "   Shortcut: Press <H> or <?>.\n"
90 "\n"
91 "   Also, the <TAB> and cursor keys will cycle between <Select> and\n"
92 "   <Help>\n"
93 "\n"
94 "\n"
95 "Data Entry\n"
96 "-----------\n"
97 "o  Enter the requested information and press <ENTER>\n"
98 "   If you are entering hexadecimal values, it is not necessary to\n"
99 "   add the '0x' prefix to the entry.\n"
100 "\n"
101 "o  For help, use the <TAB> or cursor keys to highlight the help option\n"
102 "   and press <ENTER>.  You can try <TAB><H> as well.\n"
103 "\n"
104 "\n"
105 "Text Box    (Help Window)\n"
106 "--------\n"
107 "o  Use the cursor keys to scroll up/down/left/right.  The VI editor\n"
108 "   keys h,j,k,l function here as do <SPACE BAR> and <B> for those\n"
109 "   who are familiar with less and lynx.\n"
110 "\n"
111 "o  Press <E>, <X>, <Enter> or <Esc><Esc> to exit.\n"
112 "\n"
113 "\n"
114 "Alternate Configuration Files\n"
115 "-----------------------------\n"
116 "Menuconfig supports the use of alternate configuration files for\n"
117 "those who, for various reasons, find it necessary to switch\n"
118 "between different 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\n"
152 "menu, rather than the default multimenu hierarchy, run the menuconfig\n"
153 "with MENUCONFIG_MODE environment variable set to single_menu. Example:\n"
154 "\n"
155 "make MENUCONFIG_MODE=single_menu menuconfig\n"
156 "\n"
157 "<Enter> will then unroll the appropriate category, or enfold it if it\n"
158 "is already unrolled.\n"
159 "\n"
160 "Note that this mode can eventually be a little more CPU expensive\n"
161 "(especially with a larger number of unrolled categories) than the\n"
162 "default mode.\n",
163 menu_instructions[] =
164         "Arrow keys navigate the menu.  "
165         "<Enter> selects submenus --->.  "
166         "Highlighted letters are hotkeys.  "
167         "Pressing <Y> selectes a feature, while <N> will exclude a feature.  "
168         "Press <Esc><Esc> to exit, <?> for Help, </> for Search.  "
169         "Legend: [*] feature is selected  [ ] feature is excluded",
170 radiolist_instructions[] =
171         "Use the arrow keys to navigate this window or "
172         "press the hotkey of the item you wish to select "
173         "followed by the <SPACE BAR>. "
174         "Press <?> for additional information about this option.",
175 inputbox_instructions_int[] =
176         "Please enter a decimal value. "
177         "Fractions will not be accepted.  "
178         "Use the <TAB> key to move from the input field to the buttons below it.",
179 inputbox_instructions_hex[] =
180         "Please enter a hexadecimal value. "
181         "Use the <TAB> key to move from the input field to the buttons below it.",
182 inputbox_instructions_string[] =
183         "Please enter a string value. "
184         "Use the <TAB> key to move from the input field to the buttons below it.",
185 setmod_text[] =
186         "This feature depends on another which has been configured as a module.\n"
187         "As a result, this feature will be built as a module.",
188 nohelp_text[] =
189         "There is no help available for this option.\n",
190 load_config_text[] =
191         "Enter the name of the configuration file you wish to load.  "
192         "Accept the name shown to restore the configuration you "
193         "last retrieved.  Leave blank to abort.",
194 load_config_help[] =
195         "\n"
196         "For various reasons, one may wish to keep several different BusyBox\n"
197         "configurations available on a single machine.\n"
198         "\n"
199         "If you have saved a previous configuration in a file other than the\n"
200         "BusyBox's default, entering the name of the file here will allow you\n"
201         "to modify that configuration.\n"
202         "\n"
203         "If you are uncertain, then you have probably never used alternate\n"
204         "configuration files.  You should therefor leave this blank to abort.\n",
205 save_config_text[] =
206         "Enter a filename to which this configuration should be saved "
207         "as an alternate.  Leave blank to abort.",
208 save_config_help[] =
209         "\n"
210         "For various reasons, one may wish to keep different BusyBox\n"
211         "configurations available on a single machine.\n"
212         "\n"
213         "Entering a file name here will allow you to later retrieve, modify\n"
214         "and use the current configuration as an alternate to whatever\n"
215         "configuration options you have selected at that time.\n"
216         "\n"
217         "If you are uncertain what all this means then you should probably\n"
218         "leave this blank.\n",
219 search_help[] =
220         "\n"
221         "Search for CONFIG_ symbols and display their relations.\n"
222         "Example: search for \"^FOO\"\n"
223         "Result:\n"
224         "-----------------------------------------------------------------\n"
225         "Symbol: FOO [=m]\n"
226         "Prompt: Foo bus is used to drive the bar HW\n"
227         "Defined at drivers/pci/Kconfig:47\n"
228         "Depends on: X86_LOCAL_APIC && X86_IO_APIC || IA64\n"
229         "Location:\n"
230         "  -> Bus options (PCI, PCMCIA, EISA, MCA, ISA)\n"
231         "    -> PCI support (PCI [=y])\n"
232         "      -> PCI access mode (<choice> [=y])\n"
233         "Selects: LIBCRC32\n"
234         "Selected by: BAR\n"
235         "-----------------------------------------------------------------\n"
236         "o The line 'Prompt:' shows the text used in the menu structure for\n"
237         "  this CONFIG_ symbol\n"
238         "o The 'Defined at' line tell at what file / line number the symbol\n"
239         "  is defined\n"
240         "o The 'Depends on:' line tell what symbols needs to be defined for\n"
241         "  this symbol to be visible in the menu (selectable)\n"
242         "o The 'Location:' lines tell where in the menu structure this symbol\n"
243         "  is located\n"
244         "    A location followed by a [=y] indicate that this is a selectable\n"
245         "    menu item - and current value is displayed inside brackets.\n"
246         "o The 'Selects:' line tell what symbol will be automatically\n"
247         "  selected if this symbol is selected (y or m)\n"
248         "o The 'Selected by' line tell what symbol has selected this symbol\n"
249         "\n"
250         "Only relevant lines are shown.\n"
251         "\n\n"
252         "Search examples:\n"
253         "Examples: USB  => find all CONFIG_ symbols containing USB\n"
254         "          ^USB => find all CONFIG_ symbols starting with USB\n"
255         "          USB$ => find all CONFIG_ symbols ending with USB\n"
256         "\n";
257
258 static char filename[PATH_MAX+1] = ".config";
259 static int indent;
260 static struct termios ios_org;
261 static int rows = 0, cols = 0;
262 static struct menu *current_menu;
263 static int child_count;
264 static int single_menu_mode;
265
266 static struct dialog_list_item *items[16384]; /* FIXME: This ought to be dynamic. */
267 static int item_no;
268
269 static void conf(struct menu *menu);
270 static void conf_choice(struct menu *menu);
271 static void conf_string(struct menu *menu);
272 static void conf_load(void);
273 static void conf_save(void);
274 static void show_textbox(const char *title, const char *text, int r, int c);
275 static void show_helptext(const char *title, const char *text);
276 static void show_help(struct menu *menu);
277 static void show_file(const char *filename, const char *title, int r, int c);
278
279 static void init_wsize(void)
280 {
281         struct winsize ws;
282         char *env;
283
284         if (!ioctl(STDIN_FILENO, TIOCGWINSZ, &ws)) {
285                 rows = ws.ws_row;
286                 cols = ws.ws_col;
287         }
288
289         if (!rows) {
290                 env = getenv("LINES");
291                 if (env)
292                         rows = atoi(env);
293                 if (!rows)
294                         rows = 24;
295         }
296         if (!cols) {
297                 env = getenv("COLUMNS");
298                 if (env)
299                         cols = atoi(env);
300                 if (!cols)
301                         cols = 80;
302         }
303
304         if (rows < 19 || cols < 80) {
305                 fprintf(stderr, "Your display is too small to run Menuconfig!\n");
306                 fprintf(stderr, "It must be at least 19 lines by 80 columns.\n");
307                 exit(1);
308         }
309
310         rows -= 4;
311         cols -= 5;
312 }
313
314 static void cinit(void)
315 {
316         item_no = 0;
317 }
318
319 static void cmake(void)
320 {
321         items[item_no] = malloc(sizeof(struct dialog_list_item));
322         memset(items[item_no], 0, sizeof(struct dialog_list_item));
323         items[item_no]->tag = malloc(32); items[item_no]->tag[0] = 0;
324         items[item_no]->name = malloc(512); items[item_no]->name[0] = 0;
325         items[item_no]->namelen = 0;
326         item_no++;
327 }
328
329 static int cprint_name(const char *fmt, ...)
330 {
331         va_list ap;
332         int res;
333
334         if (!item_no)
335                 cmake();
336         va_start(ap, fmt);
337         res = vsnprintf(items[item_no - 1]->name + items[item_no - 1]->namelen,
338                         512 - items[item_no - 1]->namelen, fmt, ap);
339         if (res > 0)
340                 items[item_no - 1]->namelen += res;
341         va_end(ap);
342
343         return res;
344 }
345
346 static int cprint_tag(const char *fmt, ...)
347 {
348         va_list ap;
349         int res;
350
351         if (!item_no)
352                 cmake();
353         va_start(ap, fmt);
354         res = vsnprintf(items[item_no - 1]->tag, 32, fmt, ap);
355         va_end(ap);
356
357         return res;
358 }
359
360 static void cdone(void)
361 {
362         int i;
363
364         for (i = 0; i < item_no; i++) {
365                 free(items[i]->tag);
366                 free(items[i]->name);
367                 free(items[i]);
368         }
369
370         item_no = 0;
371 }
372
373 static void get_prompt_str(struct gstr *r, struct property *prop)
374 {
375         int i, j;
376         struct menu *submenu[8], *menu;
377
378         str_printf(r, "Prompt: %s\n", prop->text);
379         str_printf(r, "  Defined at %s:%d\n", prop->menu->file->name,
380                 prop->menu->lineno);
381         if (!expr_is_yes(prop->visible.expr)) {
382                 str_append(r, "  Depends on: ");
383                 expr_gstr_print(prop->visible.expr, r);
384                 str_append(r, "\n");
385         }
386         menu = prop->menu->parent;
387         for (i = 0; menu != &rootmenu && i < 8; menu = menu->parent)
388                 submenu[i++] = menu;
389         if (i > 0) {
390                 str_printf(r, "  Location:\n");
391                 for (j = 4; --i >= 0; j += 2) {
392                         menu = submenu[i];
393                         str_printf(r, "%*c-> %s", j, ' ', menu_get_prompt(menu));
394                         if (menu->sym) {
395                                 str_printf(r, " (%s [=%s])", menu->sym->name ?
396                                         menu->sym->name : "<choice>",
397                                         sym_get_string_value(menu->sym));
398                         }
399                         str_append(r, "\n");
400                 }
401         }
402 }
403
404 static void get_symbol_str(struct gstr *r, struct symbol *sym)
405 {
406         bool hit;
407         struct property *prop;
408
409         str_printf(r, "Symbol: %s [=%s]\n", sym->name,
410                                        sym_get_string_value(sym));
411         for_all_prompts(sym, prop)
412                 get_prompt_str(r, prop);
413         hit = false;
414         for_all_properties(sym, prop, P_SELECT) {
415                 if (!hit) {
416                         str_append(r, "  Selects: ");
417                         hit = true;
418                 } else
419                         str_printf(r, " && ");
420                 expr_gstr_print(prop->expr, r);
421         }
422         if (hit)
423                 str_append(r, "\n");
424         if (sym->rev_dep.expr) {
425                 str_append(r, "  Selected by: ");
426                 expr_gstr_print(sym->rev_dep.expr, r);
427                 str_append(r, "\n");
428         }
429         str_append(r, "\n\n");
430 }
431
432 static struct gstr get_relations_str(struct symbol **sym_arr)
433 {
434         struct symbol *sym;
435         struct gstr res = str_new();
436         int i;
437
438         for (i = 0; sym_arr && (sym = sym_arr[i]); i++)
439                 get_symbol_str(&res, sym);
440         if (!i)
441                 str_append(&res, "No matches found.\n");
442         return res;
443 }
444
445 static void search_conf(void)
446 {
447         struct symbol **sym_arr;
448         struct gstr res;
449
450 again:
451         switch (dialog_inputbox("Search Configuration Parameter",
452                                 "Enter Keyword", 10, 75,
453                                 NULL)) {
454         case 0:
455                 break;
456         case 1:
457                 show_helptext("Search Configuration", search_help);
458                 goto again;
459         default:
460                 return;
461         }
462
463         sym_arr = sym_re_search(dialog_input_result);
464         res = get_relations_str(sym_arr);
465         free(sym_arr);
466         show_textbox("Search Results", str_get(&res), 0, 0);
467         str_free(&res);
468 }
469
470 static void build_conf(struct menu *menu)
471 {
472         struct symbol *sym;
473         struct property *prop;
474         struct menu *child;
475         int type, tmp, doint = 2;
476         tristate val;
477         char ch;
478
479         if (!menu_is_visible(menu))
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                                 cmake();
491                                 cprint_tag("m%p", menu);
492
493                                 if (single_menu_mode) {
494                                         cprint_name("%s%*c%s",
495                                                 menu->data ? "-->" : "++>",
496                                                 indent + 1, ' ', prompt);
497                                 } else {
498                                         cprint_name("   %*c%s  --->", indent + 1, ' ', prompt);
499                                 }
500
501                                 if (single_menu_mode && menu->data)
502                                         goto conf_childs;
503                                 return;
504                         default:
505                                 if (prompt) {
506                                         child_count++;
507                                         cmake();
508                                         cprint_tag(":%p", menu);
509                                         cprint_name("---%*c%s", indent + 1, ' ', prompt);
510                                 }
511                         }
512                 } else
513                         doint = 0;
514                 goto conf_childs;
515         }
516
517         cmake();
518         type = sym_get_type(sym);
519         if (sym_is_choice(sym)) {
520                 struct symbol *def_sym = sym_get_choice_value(sym);
521                 struct menu *def_menu = NULL;
522
523                 child_count++;
524                 for (child = menu->list; child; child = child->next) {
525                         if (menu_is_visible(child) && child->sym == def_sym)
526                                 def_menu = child;
527                 }
528
529                 val = sym_get_tristate_value(sym);
530                 if (sym_is_changable(sym)) {
531                         cprint_tag("t%p", menu);
532                         switch (type) {
533                         case S_BOOLEAN:
534                                 cprint_name("[%c]", val == no ? ' ' : '*');
535                                 break;
536                         case S_TRISTATE:
537                                 switch (val) {
538                                 case yes: ch = '*'; break;
539                                 case mod: ch = 'M'; break;
540                                 default:  ch = ' '; break;
541                                 }
542                                 cprint_name("<%c>", ch);
543                                 break;
544                         }
545                 } else {
546                         cprint_tag("%c%p", def_menu ? 't' : ':', menu);
547                         cprint_name("   ");
548                 }
549
550                 cprint_name("%*c%s", indent + 1, ' ', menu_get_prompt(menu));
551                 if (val == yes) {
552                         if (def_menu) {
553                                 cprint_name(" (%s)", menu_get_prompt(def_menu));
554                                 cprint_name("  --->");
555                                 if (def_menu->list) {
556                                         indent += 2;
557                                         build_conf(def_menu);
558                                         indent -= 2;
559                                 }
560                         }
561                         return;
562                 }
563         } else {
564                 if (menu == current_menu) {
565                         cprint_tag(":%p", menu);
566                         cprint_name("---%*c%s", indent + 1, ' ', menu_get_prompt(menu));
567                         goto conf_childs;
568                 }
569                 child_count++;
570                 val = sym_get_tristate_value(sym);
571                 if (sym_is_choice_value(sym) && val == yes) {
572                         cprint_tag(":%p", menu);
573                         cprint_name("   ");
574                 } else {
575                         switch (type) {
576                         case S_BOOLEAN:
577                                 cprint_tag("t%p", menu);
578                                 if (sym_is_changable(sym))
579                                         cprint_name("[%c]", val == no ? ' ' : '*');
580                                 else
581                                         cprint_name("---");
582                                 break;
583                         case S_TRISTATE:
584                                 cprint_tag("t%p", menu);
585                                 switch (val) {
586                                 case yes: ch = '*'; break;
587                                 case mod: ch = 'M'; break;
588                                 default:  ch = ' '; break;
589                                 }
590                                 if (sym_is_changable(sym))
591                                         cprint_name("<%c>", ch);
592                                 else
593                                         cprint_name("---");
594                                 break;
595                         default:
596                                 cprint_tag("s%p", menu);
597                                 tmp = cprint_name("(%s)", sym_get_string_value(sym));
598                                 tmp = indent - tmp + 4;
599                                 if (tmp < 0)
600                                         tmp = 0;
601                                 cprint_name("%*c%s%s", tmp, ' ', menu_get_prompt(menu),
602                                         (sym_has_value(sym) || !sym_is_changable(sym)) ?
603                                         "" : " (NEW)");
604                                 goto conf_childs;
605                         }
606                 }
607                 cprint_name("%*c%s%s", indent + 1, ' ', menu_get_prompt(menu),
608                         (sym_has_value(sym) || !sym_is_changable(sym)) ?
609                         "" : " (NEW)");
610                 if (menu->prompt->type == P_MENU) {
611                         cprint_name("  --->");
612                         return;
613                 }
614         }
615
616 conf_childs:
617         indent += doint;
618         for (child = menu->list; child; child = child->next)
619                 build_conf(child);
620         indent -= doint;
621 }
622
623 static void conf(struct menu *menu)
624 {
625         struct dialog_list_item *active_item = NULL;
626         struct menu *submenu;
627         const char *prompt = menu_get_prompt(menu);
628         struct symbol *sym;
629         char active_entry[40];
630         int stat, type;
631
632         unlink("lxdialog.scrltmp");
633         active_entry[0] = 0;
634         while (1) {
635                 indent = 0;
636                 child_count = 0;
637                 current_menu = menu;
638                 cdone(); cinit();
639                 build_conf(menu);
640                 if (!child_count)
641                         break;
642                 if (menu == &rootmenu) {
643                         cmake(); cprint_tag(":"); cprint_name("--- ");
644                         cmake(); cprint_tag("L"); cprint_name("Load an Alternate Configuration File");
645                         cmake(); cprint_tag("S"); cprint_name("Save Configuration to an Alternate File");
646                 }
647                 dialog_clear();
648                 stat = dialog_menu(prompt ? prompt : "Main Menu",
649                                 menu_instructions, rows, cols, rows - 10,
650                                 active_entry, item_no, items);
651                 if (stat < 0)
652                         return;
653
654                 if (stat == 1 || stat == 255)
655                         break;
656
657                 active_item = first_sel_item(item_no, items);
658                 if (!active_item)
659                         continue;
660                 active_item->selected = 0;
661                 strncpy(active_entry, active_item->tag, sizeof(active_entry));
662                 active_entry[sizeof(active_entry)-1] = 0;
663                 type = active_entry[0];
664                 if (!type)
665                         continue;
666
667                 sym = NULL;
668                 submenu = NULL;
669                 if (sscanf(active_entry + 1, "%p", &submenu) == 1)
670                         sym = submenu->sym;
671
672                 switch (stat) {
673                 case 0:
674                         switch (type) {
675                         case 'm':
676                                 if (single_menu_mode)
677                                         submenu->data = (void *) (long) !submenu->data;
678                                 else
679                                         conf(submenu);
680                                 break;
681                         case 't':
682                                 if (sym_is_choice(sym) && sym_get_tristate_value(sym) == yes)
683                                         conf_choice(submenu);
684                                 else if (submenu->prompt->type == P_MENU)
685                                         conf(submenu);
686                                 break;
687                         case 's':
688                                 conf_string(submenu);
689                                 break;
690                         case 'L':
691                                 conf_load();
692                                 break;
693                         case 'S':
694                                 conf_save();
695                                 break;
696                         }
697                         break;
698                 case 2:
699                         if (sym)
700                                 show_help(submenu);
701                         else
702                                 show_helptext("README", mconf_readme);
703                         break;
704                 case 3:
705                         if (type == 't') {
706                                 if (sym_set_tristate_value(sym, yes))
707                                         break;
708                                 if (sym_set_tristate_value(sym, mod))
709                                         show_textbox(NULL, setmod_text, 6, 74);
710                         }
711                         break;
712                 case 4:
713                         if (type == 't')
714                                 sym_set_tristate_value(sym, no);
715                         break;
716                 case 5:
717                         if (type == 't')
718                                 sym_set_tristate_value(sym, mod);
719                         break;
720                 case 6:
721                         if (type == 't')
722                                 sym_toggle_tristate_value(sym);
723                         else if (type == 'm')
724                                 conf(submenu);
725                         break;
726                 case 7:
727                         search_conf();
728                         break;
729                 }
730         }
731 }
732
733 static void show_textbox(const char *title, const char *text, int r, int c)
734 {
735         int fd;
736
737         fd = creat(".help.tmp", 0777);
738         write(fd, text, strlen(text));
739         close(fd);
740         show_file(".help.tmp", title, r, c);
741         unlink(".help.tmp");
742 }
743
744 static void show_helptext(const char *title, const char *text)
745 {
746         show_textbox(title, text, 0, 0);
747 }
748
749 static void show_help(struct menu *menu)
750 {
751         struct gstr help = str_new();
752         struct symbol *sym = menu->sym;
753
754         if (sym->help)
755         {
756                 if (sym->name) {
757                         str_printf(&help, "%s:\n\n", sym->name);
758                         str_append(&help, sym->help);
759                         str_append(&help, "\n");
760                 }
761         } else {
762                 str_append(&help, nohelp_text);
763         }
764         get_symbol_str(&help, sym);
765         show_helptext(menu_get_prompt(menu), str_get(&help));
766         str_free(&help);
767 }
768
769 static void show_file(const char *filename, const char *title, int r, int c)
770 {
771         while (dialog_textbox(title, filename, r ? r : rows, c ? c : cols) < 0)
772                 ;
773 }
774
775 static void conf_choice(struct menu *menu)
776 {
777         const char *prompt = menu_get_prompt(menu);
778         struct menu *child;
779         struct symbol *active;
780
781         active = sym_get_choice_value(menu->sym);
782         while (1) {
783                 current_menu = menu;
784                 cdone(); cinit();
785                 for (child = menu->list; child; child = child->next) {
786                         if (!menu_is_visible(child))
787                                 continue;
788                         cmake();
789                         cprint_tag("%p", child);
790                         cprint_name("%s", menu_get_prompt(child));
791                         if (child->sym == sym_get_choice_value(menu->sym))
792                                 items[item_no - 1]->selected = 1; /* ON */
793                         else if (child->sym == active)
794                                 items[item_no - 1]->selected = 2; /* SELECTED */
795                         else
796                                 items[item_no - 1]->selected = 0; /* OFF */
797                 }
798
799                 switch (dialog_checklist(prompt ? prompt : "Main Menu",
800                                         radiolist_instructions, 15, 70, 6,
801                                         item_no, items, FLAG_RADIO)) {
802                 case 0:
803                         if (sscanf(first_sel_item(item_no, items)->tag, "%p", &child) != 1)
804                                 break;
805                         sym_set_tristate_value(child->sym, yes);
806                         return;
807                 case 1:
808                         if (sscanf(first_sel_item(item_no, items)->tag, "%p", &child) == 1) {
809                                 show_help(child);
810                                 active = child->sym;
811                         } else
812                                 show_help(menu);
813                         break;
814                 case 255:
815                         return;
816                 }
817         }
818 }
819
820 static void conf_string(struct menu *menu)
821 {
822         const char *prompt = menu_get_prompt(menu);
823
824         while (1) {
825                 char *heading;
826
827                 switch (sym_get_type(menu->sym)) {
828                 case S_INT:
829                         heading = (char *) inputbox_instructions_int;
830                         break;
831                 case S_HEX:
832                         heading = (char *) inputbox_instructions_hex;
833                         break;
834                 case S_STRING:
835                         heading = (char *) inputbox_instructions_string;
836                         break;
837                 default:
838                         heading = "Internal mconf error!";
839                         /* panic? */;
840                 }
841
842                 switch (dialog_inputbox(prompt ? prompt : "Main Menu",
843                                         heading, 10, 75,
844                                         sym_get_string_value(menu->sym))) {
845                 case 0:
846                         if (sym_set_string_value(menu->sym, dialog_input_result))
847                                 return;
848                         show_textbox(NULL, "You have made an invalid entry.", 5, 43);
849                         break;
850                 case 1:
851                         show_help(menu);
852                         break;
853                 case 255:
854                         return;
855                 }
856         }
857 }
858
859 static void conf_load(void)
860 {
861         while (1) {
862                 switch (dialog_inputbox(NULL, load_config_text, 11, 55,
863                                         filename)) {
864                 case 0:
865                         if (!dialog_input_result[0])
866                                 return;
867                         if (!conf_read(dialog_input_result))
868                                 return;
869                         show_textbox(NULL, "File does not exist!", 5, 38);
870                         break;
871                 case 1:
872                         show_helptext("Load Alternate Configuration", load_config_help);
873                         break;
874                 case 255:
875                         return;
876                 }
877         }
878 }
879
880 static void conf_save(void)
881 {
882         while (1) {
883                 switch (dialog_inputbox(NULL, save_config_text, 11, 55,
884                                         filename)) {
885                 case 0:
886                         if (!dialog_input_result[0])
887                                 return;
888                         if (!conf_write(dialog_input_result))
889                                 return;
890                         show_textbox(NULL, "Can't create file!  Probably a nonexistent directory.", 5, 60);
891                         break;
892                 case 1:
893                         show_helptext("Save Alternate Configuration", save_config_help);
894                         break;
895                 case 255:
896                         return;
897                 }
898         }
899 }
900
901 static void conf_cleanup(void)
902 {
903         tcsetattr(1, TCSAFLUSH, &ios_org);
904         unlink(".help.tmp");
905 }
906
907 static void winch_handler(int sig)
908 {
909         struct winsize ws;
910
911         if (ioctl(1, TIOCGWINSZ, &ws) == -1) {
912                 rows = 24;
913                 cols = 80;
914         } else {
915                 rows = ws.ws_row;
916                 cols = ws.ws_col;
917         }
918
919         if (rows < 19 || cols < 80) {
920                 end_dialog();
921                 fprintf(stderr, "Your display is too small to run Menuconfig!\n");
922                 fprintf(stderr, "It must be at least 19 lines by 80 columns.\n");
923                 exit(1);
924         }
925
926         rows -= 4;
927         cols -= 5;
928
929 }
930
931 int main(int ac, char **av)
932 {
933         struct symbol *sym;
934         char *mode;
935         int stat;
936
937         conf_parse(av[1]);
938         conf_read(NULL);
939
940         sym = sym_lookup("VERSION", 0);
941         sym_calc_value(sym);
942         snprintf(menu_backtitle, 128, "BusyBox v%s Configuration",
943                 sym_get_string_value(sym));
944
945         mode = getenv("MENUCONFIG_MODE");
946         if (mode) {
947                 if (!strcasecmp(mode, "single_menu"))
948                         single_menu_mode = 1;
949         }
950
951         tcgetattr(1, &ios_org);
952         atexit(conf_cleanup);
953         init_wsize();
954         init_dialog();
955         signal(SIGWINCH, winch_handler);
956         conf(&rootmenu);
957         end_dialog();
958
959         /* Restart dialog to act more like when lxdialog was still separate */
960         init_dialog();
961         do {
962                 stat = dialog_yesno(NULL,
963                                     "Do you wish to save your new BusyBox configuration?", 5, 60);
964         } while (stat < 0);
965         end_dialog();
966
967         if (stat == 0) {
968                 conf_write(NULL);
969                 printf("\n"
970                         "*** End of BusyBox configuration.\n");
971         } else
972                 printf("\n\nYour BusyBox configuration changes were NOT saved.\n\n");
973
974         return 0;
975 }