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