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