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