disable the automatic config reset if 'Advanced configuration options' is selected
[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         struct property *prop;
892         int stat;
893
894         active = sym_get_choice_value(menu->sym);
895         while (1) {
896                 cprint_init();
897                 cprint("--title");
898                 cprint("%s", prompt ? prompt : _("Main Menu"));
899                 cprint("--radiolist");
900                 cprint(_(radiolist_instructions));
901                 cprint("15");
902                 cprint("70");
903                 cprint("6");
904
905                 current_menu = menu;
906                 for (child = menu->list; child; child = child->next) {
907                         if (!menu_is_visible(child))
908                                 continue;
909                         cprint("%p", child);
910                         cprint("%s", menu_get_prompt(child));
911                         if (child->sym == sym_get_choice_value(menu->sym))
912                                 cprint("ON");
913                         else if (child->sym == active)
914                                 cprint("SELECTED");
915                         else
916                                 cprint("OFF");
917                 }
918
919                 stat = exec_conf();
920                 switch (stat) {
921                 case 0:
922                         if (sscanf(input_buf, "%p", &child) != 1)
923                                 break;
924                         
925                         if (sym_get_tristate_value(child->sym) != yes) {
926                                 for_all_properties(menu->sym, prop, P_RESET) {
927                                         if (expr_calc_value(prop->visible.expr) != no)
928                                                 conf_reset();
929                                 }
930                         }
931                         sym_set_tristate_value(child->sym, yes);
932                         return;
933                 case 1:
934                         if (sscanf(input_buf, "%p", &child) == 1) {
935                                 show_help(child);
936                                 active = child->sym;
937                         } else
938                                 show_help(menu);
939                         break;
940                 case 255:
941                         return;
942                 }
943         }
944 }
945
946 static void conf_string(struct menu *menu)
947 {
948         const char *prompt = menu_get_prompt(menu);
949         int stat;
950
951         while (1) {
952                 cprint_init();
953                 cprint("--title");
954                 cprint("%s", prompt ? prompt : _("Main Menu"));
955                 cprint("--inputbox");
956                 switch (sym_get_type(menu->sym)) {
957                 case S_INT:
958                         cprint(_(inputbox_instructions_int));
959                         break;
960                 case S_HEX:
961                         cprint(_(inputbox_instructions_hex));
962                         break;
963                 case S_STRING:
964                         cprint(_(inputbox_instructions_string));
965                         break;
966                 default:
967                         /* panic? */;
968                 }
969                 cprint("10");
970                 cprint("75");
971                 cprint("%s", sym_get_string_value(menu->sym));
972                 stat = exec_conf();
973                 switch (stat) {
974                 case 0:
975                         if (sym_set_string_value(menu->sym, input_buf))
976                                 return;
977                         show_textbox(NULL, _("You have made an invalid entry."), 5, 43);
978                         break;
979                 case 1:
980                         show_help(menu);
981                         break;
982                 case 255:
983                         return;
984                 }
985         }
986 }
987
988 static void conf_load(void)
989 {
990         int stat;
991
992         while (1) {
993                 cprint_init();
994                 cprint("--inputbox");
995                 cprint(load_config_text);
996                 cprint("11");
997                 cprint("55");
998                 cprint("%s", filename);
999                 stat = exec_conf();
1000                 switch(stat) {
1001                 case 0:
1002                         if (!input_buf[0])
1003                                 return;
1004                         if (!conf_read(input_buf))
1005                                 return;
1006                         show_textbox(NULL, _("File does not exist!"), 5, 38);
1007                         break;
1008                 case 1:
1009                         show_helptext(_("Load Alternate Configuration"), load_config_help);
1010                         break;
1011                 case 255:
1012                         return;
1013                 }
1014         }
1015 }
1016
1017 static void conf_save(void)
1018 {
1019         int stat;
1020
1021         while (1) {
1022                 cprint_init();
1023                 cprint("--inputbox");
1024                 cprint(save_config_text);
1025                 cprint("11");
1026                 cprint("55");
1027                 cprint("%s", filename);
1028                 stat = exec_conf();
1029                 switch(stat) {
1030                 case 0:
1031                         if (!input_buf[0])
1032                                 return;
1033                         if (!conf_write(input_buf))
1034                                 return;
1035                         show_textbox(NULL, _("Can't create file!  Probably a nonexistent directory."), 5, 60);
1036                         break;
1037                 case 1:
1038                         show_helptext(_("Save Alternate Configuration"), save_config_help);
1039                         break;
1040                 case 255:
1041                         return;
1042                 }
1043         }
1044 }
1045
1046 static void conf_cleanup(void)
1047 {
1048         tcsetattr(1, TCSAFLUSH, &ios_org);
1049         unlink(".help.tmp");
1050         unlink("lxdialog.scrltmp");
1051 }
1052
1053 int main(int ac, char **av)
1054 {
1055         struct symbol *sym;
1056         char *mode;
1057         int stat;
1058
1059         setlocale(LC_ALL, "");
1060         bindtextdomain(PACKAGE, LOCALEDIR);
1061         textdomain(PACKAGE);
1062
1063         conf_parse(av[1]);
1064         conf_read(NULL);
1065
1066         sym = sym_lookup("OPENWRTVERSION", 0);
1067         sym_calc_value(sym);
1068         sprintf(menu_backtitle, _("OpenWrt %s Configuration"),
1069                 sym_get_string_value(sym));
1070
1071         mode = getenv("MENUCONFIG_MODE");
1072         if (mode) {
1073                 if (!strcasecmp(mode, "single_menu"))
1074                         single_menu_mode = 1;
1075         }
1076
1077         tcgetattr(1, &ios_org);
1078         atexit(conf_cleanup);
1079         init_wsize();
1080         conf(&rootmenu);
1081
1082         do {
1083                 cprint_init();
1084                 cprint("--yesno");
1085                 cprint(_("Do you wish to save your new OpenWrt configuration?"));
1086                 cprint("5");
1087                 cprint("60");
1088                 stat = exec_conf();
1089         } while (stat < 0);
1090
1091         if (stat == 0) {
1092                 if (conf_write(NULL)) {
1093                         fprintf(stderr, _("\n\n"
1094                                 "Error during writing of the OpenWrt configuration.\n"
1095                                 "Your configuration changes were NOT saved."
1096                                 "\n\n"));
1097                         return 1;
1098                 }
1099                 printf(_("\n\n"
1100                         "*** End of OpenWrt configuration.\n"
1101                         "*** Execute 'make' to build the OpenWrt or try 'make help'."
1102                         "\n\n"));
1103         } else {
1104                 fprintf(stderr, _("\n\n"
1105                         "Your configuration changes were NOT saved."
1106                         "\n\n"));
1107         }
1108
1109         return 0;
1110 }