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