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