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