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