printf: fix printf "%u\n" +18446744073709551614
[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         pipe(pipefd);
486         pid = fork();
487         if (pid == 0) {
488                 sigprocmask(SIG_SETMASK, &osset, NULL);
489                 dup2(pipefd[1], 2);
490                 close(pipefd[0]);
491                 close(pipefd[1]);
492                 execv(args[0], args);
493                 _exit(EXIT_FAILURE);
494         }
495
496         close(pipefd[1]);
497         bufptr = input_buf;
498         while (1) {
499                 size = input_buf + sizeof(input_buf) - bufptr;
500                 size = read(pipefd[0], bufptr, size);
501                 if (size <= 0) {
502                         if (size < 0) {
503                                 if (errno == EINTR || errno == EAGAIN)
504                                         continue;
505                                 perror("read");
506                         }
507                         break;
508                 }
509                 bufptr += size;
510         }
511         *bufptr++ = 0;
512         close(pipefd[0]);
513         waitpid(pid, &stat, 0);
514
515         if (do_resize) {
516                 init_wsize();
517                 do_resize = 0;
518                 sigprocmask(SIG_SETMASK, &osset, NULL);
519                 return -1;
520         }
521         if (WIFSIGNALED(stat)) {
522                 printf("\finterrupted(%d)\n", WTERMSIG(stat));
523                 exit(1);
524         }
525 #if 0
526         printf("\fexit state: %d\nexit data: '%s'\n", WEXITSTATUS(stat), input_buf);
527         sleep(1);
528 #endif
529         sigpending(&sset);
530         if (sigismember(&sset, SIGINT)) {
531                 printf("\finterrupted\n");
532                 exit(1);
533         }
534         sigprocmask(SIG_SETMASK, &osset, NULL);
535
536         return WEXITSTATUS(stat);
537 }
538
539 static void search_conf(void)
540 {
541         struct symbol **sym_arr;
542         int stat;
543         struct gstr res;
544
545 again:
546         cprint_init();
547         cprint("--title");
548         cprint(_("Search Configuration Parameter"));
549         cprint("--inputbox");
550         cprint(_("Enter CONFIG_ (sub)string to search for (omit CONFIG_)"));
551         cprint("10");
552         cprint("75");
553         cprint("");
554         stat = exec_conf();
555         if (stat < 0)
556                 goto again;
557         switch (stat) {
558         case 0:
559                 break;
560         case 1:
561                 show_helptext(_("Search Configuration"), search_help);
562                 goto again;
563         default:
564                 return;
565         }
566
567         sym_arr = sym_re_search(input_buf);
568         res = get_relations_str(sym_arr);
569         free(sym_arr);
570         show_textbox(_("Search Results"), str_get(&res), 0, 0);
571         str_free(&res);
572 }
573
574 static void build_conf(struct menu *menu)
575 {
576         struct symbol *sym;
577         struct property *prop;
578         struct menu *child;
579         int type, tmp, doint = 2;
580         tristate val;
581         char ch;
582
583         if (!menu_is_visible(menu))
584                 return;
585
586         sym = menu->sym;
587         prop = menu->prompt;
588         if (!sym) {
589                 if (prop && menu != current_menu) {
590                         const char *prompt = menu_get_prompt(menu);
591                         switch (prop->type) {
592                         case P_MENU:
593                                 child_count++;
594                                 cprint("m%p", menu);
595
596                                 if (single_menu_mode) {
597                                         cprint1("%s%*c%s",
598                                                 menu->data ? "-->" : "++>",
599                                                 indent + 1, ' ', prompt);
600                                 } else
601                                         cprint1("   %*c%s  --->", indent + 1, ' ', prompt);
602
603                                 cprint_done();
604                                 if (single_menu_mode && menu->data)
605                                         goto conf_childs;
606                                 return;
607                         default:
608                                 if (prompt) {
609                                         child_count++;
610                                         cprint(":%p", menu);
611                                         cprint("---%*c%s", indent + 1, ' ', prompt);
612                                 }
613                         }
614                 } else
615                         doint = 0;
616                 goto conf_childs;
617         }
618
619         type = sym_get_type(sym);
620         if (sym_is_choice(sym)) {
621                 struct symbol *def_sym = sym_get_choice_value(sym);
622                 struct menu *def_menu = NULL;
623
624                 child_count++;
625                 for (child = menu->list; child; child = child->next) {
626                         if (menu_is_visible(child) && child->sym == def_sym)
627                                 def_menu = child;
628                 }
629
630                 val = sym_get_tristate_value(sym);
631                 if (sym_is_changable(sym)) {
632                         cprint("t%p", menu);
633                         switch (type) {
634                         case S_BOOLEAN:
635                                 cprint1("[%c]", val == no ? ' ' : '*');
636                                 break;
637                         case S_TRISTATE:
638                                 switch (val) {
639                                 case yes: ch = '*'; break;
640                                 case mod: ch = 'M'; break;
641                                 default:  ch = ' '; break;
642                                 }
643                                 cprint1("<%c>", ch);
644                                 break;
645                         }
646                 } else {
647                         cprint("%c%p", def_menu ? 't' : ':', menu);
648                         cprint1("   ");
649                 }
650
651                 cprint1("%*c%s", indent + 1, ' ', menu_get_prompt(menu));
652                 if (val == yes) {
653                         if (def_menu) {
654                                 cprint1(" (%s)", menu_get_prompt(def_menu));
655                                 cprint1("  --->");
656                                 cprint_done();
657                                 if (def_menu->list) {
658                                         indent += 2;
659                                         build_conf(def_menu);
660                                         indent -= 2;
661                                 }
662                         } else
663                                 cprint_done();
664                         return;
665                 }
666                 cprint_done();
667         } else {
668                 if (menu == current_menu) {
669                         cprint(":%p", menu);
670                         cprint("---%*c%s", indent + 1, ' ', menu_get_prompt(menu));
671                         goto conf_childs;
672                 }
673                 child_count++;
674                 val = sym_get_tristate_value(sym);
675                 if (sym_is_choice_value(sym) && val == yes) {
676                         cprint(":%p", menu);
677                         cprint1("   ");
678                 } else {
679                         switch (type) {
680                         case S_BOOLEAN:
681                                 cprint("t%p", menu);
682                                 if (sym_is_changable(sym))
683                                         cprint1("[%c]", val == no ? ' ' : '*');
684                                 else
685                                         cprint1("---");
686                                 break;
687                         case S_TRISTATE:
688                                 cprint("t%p", menu);
689                                 switch (val) {
690                                 case yes: ch = '*'; break;
691                                 case mod: ch = 'M'; break;
692                                 default:  ch = ' '; break;
693                                 }
694                                 if (sym_is_changable(sym))
695                                         cprint1("<%c>", ch);
696                                 else
697                                         cprint1("---");
698                                 break;
699                         default:
700                                 cprint("s%p", menu);
701                                 tmp = cprint1("(%s)", sym_get_string_value(sym));
702                                 tmp = indent - tmp + 4;
703                                 if (tmp < 0)
704                                         tmp = 0;
705                                 cprint1("%*c%s%s", tmp, ' ', menu_get_prompt(menu),
706                                         (sym_has_value(sym) || !sym_is_changable(sym)) ?
707                                         "" : " (NEW)");
708                                 cprint_done();
709                                 goto conf_childs;
710                         }
711                 }
712                 cprint1("%*c%s%s", indent + 1, ' ', menu_get_prompt(menu),
713                         (sym_has_value(sym) || !sym_is_changable(sym)) ?
714                         "" : " (NEW)");
715                 if (menu->prompt->type == P_MENU) {
716                         cprint1("  --->");
717                         cprint_done();
718                         return;
719                 }
720                 cprint_done();
721         }
722
723 conf_childs:
724         indent += doint;
725         for (child = menu->list; child; child = child->next)
726                 build_conf(child);
727         indent -= doint;
728 }
729
730 static void conf(struct menu *menu)
731 {
732         struct menu *submenu;
733         const char *prompt = menu_get_prompt(menu);
734         struct symbol *sym;
735         char active_entry[40];
736         int stat, type, i;
737
738         unlink("lxdialog.scrltmp");
739         active_entry[0] = 0;
740         while (1) {
741                 cprint_init();
742                 cprint("--title");
743                 cprint("%s", prompt ? prompt : _("Main Menu"));
744                 cprint("--menu");
745                 cprint(_(menu_instructions));
746                 cprint("%d", rows);
747                 cprint("%d", cols);
748                 cprint("%d", rows - 10);
749                 cprint("%s", active_entry);
750                 current_menu = menu;
751                 build_conf(menu);
752                 if (!child_count)
753                         break;
754                 if (menu == &rootmenu) {
755                         cprint(":");
756                         cprint("--- ");
757                         cprint("L");
758                         cprint(_("    Load an Alternate Configuration File"));
759                         cprint("S");
760                         cprint(_("    Save Configuration to an Alternate File"));
761                 }
762                 stat = exec_conf();
763                 if (stat < 0)
764                         continue;
765
766                 if (stat == 1 || stat == 255)
767                         break;
768
769                 type = input_buf[0];
770                 if (!type)
771                         continue;
772
773                 for (i = 0; input_buf[i] && !isspace(input_buf[i]); i++)
774                         ;
775                 if (i >= sizeof(active_entry))
776                         i = sizeof(active_entry) - 1;
777                 input_buf[i] = 0;
778                 strcpy(active_entry, input_buf);
779
780                 sym = NULL;
781                 submenu = NULL;
782                 if (sscanf(input_buf + 1, "%p", &submenu) == 1)
783                         sym = submenu->sym;
784
785                 switch (stat) {
786                 case 0:
787                         switch (type) {
788                         case 'm':
789                                 if (single_menu_mode)
790                                         submenu->data = (void *) (long) !submenu->data;
791                                 else
792                                         conf(submenu);
793                                 break;
794                         case 't':
795                                 if (sym_is_choice(sym) && sym_get_tristate_value(sym) == yes)
796                                         conf_choice(submenu);
797                                 else if (submenu->prompt->type == P_MENU)
798                                         conf(submenu);
799                                 break;
800                         case 's':
801                                 conf_string(submenu);
802                                 break;
803                         case 'L':
804                                 conf_load();
805                                 break;
806                         case 'S':
807                                 conf_save();
808                                 break;
809                         }
810                         break;
811                 case 2:
812                         if (sym)
813                                 show_help(submenu);
814                         else
815                                 show_helptext("README", _(mconf_readme));
816                         break;
817                 case 3:
818                         if (type == 't') {
819                                 if (sym_set_tristate_value(sym, yes))
820                                         break;
821                                 if (sym_set_tristate_value(sym, mod))
822                                         show_textbox(NULL, setmod_text, 6, 74);
823                         }
824                         break;
825                 case 4:
826                         if (type == 't')
827                                 sym_set_tristate_value(sym, no);
828                         break;
829                 case 5:
830                         if (type == 't')
831                                 sym_set_tristate_value(sym, mod);
832                         break;
833                 case 6:
834                         if (type == 't')
835                                 sym_toggle_tristate_value(sym);
836                         else if (type == 'm')
837                                 conf(submenu);
838                         break;
839                 case 7:
840                         search_conf();
841                         break;
842                 }
843         }
844 }
845
846 static void show_textbox(const char *title, const char *text, int r, int c)
847 {
848         int fd;
849
850         fd = creat(".help.tmp", 0777);
851         write(fd, text, strlen(text));
852         close(fd);
853         show_file(".help.tmp", title, r, c);
854         unlink(".help.tmp");
855 }
856
857 static void show_helptext(const char *title, const char *text)
858 {
859         show_textbox(title, text, 0, 0);
860 }
861
862 static void show_help(struct menu *menu)
863 {
864         struct gstr help = str_new();
865         struct symbol *sym = menu->sym;
866
867         if (sym->help)
868         {
869                 if (sym->name) {
870                         str_printf(&help, "CONFIG_%s:\n\n", sym->name);
871                         str_append(&help, _(sym->help));
872                         str_append(&help, "\n");
873                 }
874         } else {
875                 str_append(&help, nohelp_text);
876         }
877         get_symbol_str(&help, sym);
878         show_helptext(menu_get_prompt(menu), str_get(&help));
879         str_free(&help);
880 }
881
882 static void show_file(const char *filename, const char *title, int r, int c)
883 {
884         do {
885                 cprint_init();
886                 if (title) {
887                         cprint("--title");
888                         cprint("%s", title);
889                 }
890                 cprint("--textbox");
891                 cprint("%s", filename);
892                 cprint("%d", r ? r : rows);
893                 cprint("%d", c ? c : cols);
894         } while (exec_conf() < 0);
895 }
896
897 static void conf_choice(struct menu *menu)
898 {
899         const char *prompt = menu_get_prompt(menu);
900         struct menu *child;
901         struct symbol *active;
902         int stat;
903
904         active = sym_get_choice_value(menu->sym);
905         while (1) {
906                 cprint_init();
907                 cprint("--title");
908                 cprint("%s", prompt ? prompt : _("Main Menu"));
909                 cprint("--radiolist");
910                 cprint(_(radiolist_instructions));
911                 cprint("15");
912                 cprint("70");
913                 cprint("6");
914
915                 current_menu = menu;
916                 for (child = menu->list; child; child = child->next) {
917                         if (!menu_is_visible(child))
918                                 continue;
919                         cprint("%p", child);
920                         cprint("%s", menu_get_prompt(child));
921                         if (child->sym == sym_get_choice_value(menu->sym))
922                                 cprint("ON");
923                         else if (child->sym == active)
924                                 cprint("SELECTED");
925                         else
926                                 cprint("OFF");
927                 }
928
929                 stat = exec_conf();
930                 switch (stat) {
931                 case 0:
932                         if (sscanf(input_buf, "%p", &child) != 1)
933                                 break;
934                         sym_set_tristate_value(child->sym, yes);
935                         return;
936                 case 1:
937                         if (sscanf(input_buf, "%p", &child) == 1) {
938                                 show_help(child);
939                                 active = child->sym;
940                         } else
941                                 show_help(menu);
942                         break;
943                 case 255:
944                         return;
945                 }
946         }
947 }
948
949 static void conf_string(struct menu *menu)
950 {
951         const char *prompt = menu_get_prompt(menu);
952         int stat;
953
954         while (1) {
955                 cprint_init();
956                 cprint("--title");
957                 cprint("%s", prompt ? prompt : _("Main Menu"));
958                 cprint("--inputbox");
959                 switch (sym_get_type(menu->sym)) {
960                 case S_INT:
961                         cprint(_(inputbox_instructions_int));
962                         break;
963                 case S_HEX:
964                         cprint(_(inputbox_instructions_hex));
965                         break;
966                 case S_STRING:
967                         cprint(_(inputbox_instructions_string));
968                         break;
969                 default:
970                         /* panic? */;
971                 }
972                 cprint("10");
973                 cprint("75");
974                 cprint("%s", sym_get_string_value(menu->sym));
975                 stat = exec_conf();
976                 switch (stat) {
977                 case 0:
978                         if (sym_set_string_value(menu->sym, input_buf))
979                                 return;
980                         show_textbox(NULL, _("You have made an invalid entry."), 5, 43);
981                         break;
982                 case 1:
983                         show_help(menu);
984                         break;
985                 case 255:
986                         return;
987                 }
988         }
989 }
990
991 static void conf_load(void)
992 {
993         int stat;
994
995         while (1) {
996                 cprint_init();
997                 cprint("--inputbox");
998                 cprint(load_config_text);
999                 cprint("11");
1000                 cprint("55");
1001                 cprint("%s", filename);
1002                 stat = exec_conf();
1003                 switch(stat) {
1004                 case 0:
1005                         if (!input_buf[0])
1006                                 return;
1007                         if (!conf_read(input_buf))
1008                                 return;
1009                         show_textbox(NULL, _("File does not exist!"), 5, 38);
1010                         break;
1011                 case 1:
1012                         show_helptext(_("Load Alternate Configuration"), load_config_help);
1013                         break;
1014                 case 255:
1015                         return;
1016                 }
1017         }
1018 }
1019
1020 static void conf_save(void)
1021 {
1022         int stat;
1023
1024         while (1) {
1025                 cprint_init();
1026                 cprint("--inputbox");
1027                 cprint(save_config_text);
1028                 cprint("11");
1029                 cprint("55");
1030                 cprint("%s", filename);
1031                 stat = exec_conf();
1032                 switch(stat) {
1033                 case 0:
1034                         if (!input_buf[0])
1035                                 return;
1036                         if (!conf_write(input_buf))
1037                                 return;
1038                         show_textbox(NULL, _("Can't create file!  Probably a nonexistent directory."), 5, 60);
1039                         break;
1040                 case 1:
1041                         show_helptext(_("Save Alternate Configuration"), save_config_help);
1042                         break;
1043                 case 255:
1044                         return;
1045                 }
1046         }
1047 }
1048
1049 static void conf_cleanup(void)
1050 {
1051         tcsetattr(1, TCSAFLUSH, &ios_org);
1052         unlink(".help.tmp");
1053         unlink("lxdialog.scrltmp");
1054 }
1055
1056 int main(int ac, char **av)
1057 {
1058         struct symbol *sym;
1059         char *mode;
1060         int stat;
1061
1062         setlocale(LC_ALL, "");
1063         bindtextdomain(PACKAGE, LOCALEDIR);
1064         textdomain(PACKAGE);
1065
1066         conf_parse(av[1]);
1067         conf_read(NULL);
1068
1069         sym = sym_lookup("KERNELVERSION", 0);
1070         sym_calc_value(sym);
1071         sprintf(menu_backtitle, _("BusyBox %s Configuration"),
1072                 sym_get_string_value(sym));
1073
1074         mode = getenv("MENUCONFIG_MODE");
1075         if (mode) {
1076                 if (!strcasecmp(mode, "single_menu"))
1077                         single_menu_mode = 1;
1078         }
1079
1080         tcgetattr(1, &ios_org);
1081         atexit(conf_cleanup);
1082         init_wsize();
1083         conf(&rootmenu);
1084
1085         do {
1086                 cprint_init();
1087                 cprint("--yesno");
1088                 cprint(_("Do you wish to save your new configuration?"));
1089                 cprint("5");
1090                 cprint("60");
1091                 stat = exec_conf();
1092         } while (stat < 0);
1093
1094         if (stat == 0) {
1095                 if (conf_write(NULL)) {
1096                         fprintf(stderr, _("\n\n"
1097                                 "Error during writing of the configuration.\n"
1098                                 "Your configuration changes were NOT saved."
1099                                 "\n\n"));
1100                         return 1;
1101                 }
1102                 printf(_("\n\n"
1103                         "*** End of configuration.\n"
1104                         "*** Execute 'make' to build the project or try 'make help'."
1105                         "\n\n"));
1106         } else {
1107                 fprintf(stderr, _("\n\n"
1108                         "Your configuration changes were NOT saved."
1109                         "\n\n"));
1110         }
1111
1112         return 0;
1113 }