fix typo
[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 included, built as package or removed respectively.\n"
38 "Pointed brackets <> represent packaging capable features.\n"
39 "\n"
40 "To change any of these features, highlight it with the cursor\n"
41 "keys and press <Y> to include it, <M> to make it a package or\n"
42 "<N> to remove 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> builds as package.  "
166         "Press <Esc><Esc> to exit, <?> for Help, </> for Search.  "
167         "Legend: [*] built-in  [ ] excluded  <M> package  < > package 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 package.\n"
185         "As a result, this feature will be built as a package."),
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("D");
741                         cprint(_("    Reset to defaults"));
742                         cprint("L");
743                         cprint(_("    Load an Alternate Configuration File"));
744                         cprint("S");
745                         cprint(_("    Save Configuration to an Alternate File"));
746                 }
747                 stat = exec_conf();
748                 if (stat < 0)
749                         continue;
750
751                 if (stat == 1 || stat == 255)
752                         break;
753
754                 type = input_buf[0];
755                 if (!type)
756                         continue;
757
758                 for (i = 0; input_buf[i] && !isspace(input_buf[i]); i++)
759                         ;
760                 if (i >= sizeof(active_entry))
761                         i = sizeof(active_entry) - 1;
762                 input_buf[i] = 0;
763                 strcpy(active_entry, input_buf);
764
765                 sym = NULL;
766                 submenu = NULL;
767                 if (sscanf(input_buf + 1, "%p", &submenu) == 1)
768                         sym = submenu->sym;
769
770                 switch (stat) {
771                 case 0:
772                         switch (type) {
773                         case 'm':
774                                 if (single_menu_mode)
775                                         submenu->data = (void *) (long) !submenu->data;
776                                 else
777                                         conf(submenu);
778                                 break;
779                         case 't':
780                                 if (sym_is_choice(sym) && sym_get_tristate_value(sym) == yes)
781                                         conf_choice(submenu);
782                                 else if (submenu->prompt->type == P_MENU)
783                                         conf(submenu);
784                                 break;
785                         case 's':
786                                 conf_string(submenu);
787                                 break;
788                         case 'D':
789                                 conf_reset();
790                                 break;
791                         case 'L':
792                                 conf_load();
793                                 break;
794                         case 'S':
795                                 conf_save();
796                                 break;
797                         }
798                         break;
799                 case 2:
800                         if (sym)
801                                 show_help(submenu);
802                         else
803                                 show_helptext("README", _(mconf_readme));
804                         break;
805                 case 3:
806                         if (type == 't') {
807                                 if (sym_set_tristate_value(sym, yes))
808                                         break;
809                                 if (sym_set_tristate_value(sym, mod))
810                                         show_textbox(NULL, setmod_text, 6, 74);
811                         }
812                         break;
813                 case 4:
814                         if (type == 't')
815                                 sym_set_tristate_value(sym, no);
816                         break;
817                 case 5:
818                         if (type == 't')
819                                 sym_set_tristate_value(sym, mod);
820                         break;
821                 case 6:
822                         if (type == 't')
823                                 sym_toggle_tristate_value(sym);
824                         else if (type == 'm')
825                                 conf(submenu);
826                         break;
827                 case 7:
828                         search_conf();
829                         break;
830                 }
831         }
832 }
833
834 static void show_textbox(const char *title, const char *text, int r, int c)
835 {
836         int fd;
837
838         fd = creat(".help.tmp", 0777);
839         write(fd, text, strlen(text));
840         close(fd);
841         show_file(".help.tmp", title, r, c);
842         unlink(".help.tmp");
843 }
844
845 static void show_helptext(const char *title, const char *text)
846 {
847         show_textbox(title, text, 0, 0);
848 }
849
850 static void show_help(struct menu *menu)
851 {
852         struct gstr help = str_new();
853         struct symbol *sym = menu->sym;
854
855         if (sym->help)
856         {
857                 if (sym->name) {
858                         str_printf(&help, "CONFIG_%s:\n\n", sym->name);
859                         str_append(&help, _(sym->help));
860                         str_append(&help, "\n");
861                 }
862         } else {
863                 str_append(&help, nohelp_text);
864         }
865         get_symbol_str(&help, sym);
866         show_helptext(menu_get_prompt(menu), str_get(&help));
867         str_free(&help);
868 }
869
870 static void show_file(const char *filename, const char *title, int r, int c)
871 {
872         do {
873                 cprint_init();
874                 if (title) {
875                         cprint("--title");
876                         cprint("%s", title);
877                 }
878                 cprint("--textbox");
879                 cprint("%s", filename);
880                 cprint("%d", r ? r : rows);
881                 cprint("%d", c ? c : cols);
882         } while (exec_conf() < 0);
883 }
884
885 static void conf_choice(struct menu *menu)
886 {
887         const char *prompt = menu_get_prompt(menu);
888         struct menu *child;
889         struct symbol *active;
890         int stat;
891
892         active = sym_get_choice_value(menu->sym);
893         while (1) {
894                 cprint_init();
895                 cprint("--title");
896                 cprint("%s", prompt ? prompt : _("Main Menu"));
897                 cprint("--radiolist");
898                 cprint(_(radiolist_instructions));
899                 cprint("15");
900                 cprint("70");
901                 cprint("6");
902
903                 current_menu = menu;
904                 for (child = menu->list; child; child = child->next) {
905                         if (!menu_is_visible(child))
906                                 continue;
907                         cprint("%p", child);
908                         cprint("%s", menu_get_prompt(child));
909                         if (child->sym == sym_get_choice_value(menu->sym))
910                                 cprint("ON");
911                         else if (child->sym == active)
912                                 cprint("SELECTED");
913                         else
914                                 cprint("OFF");
915                 }
916
917                 stat = exec_conf();
918                 switch (stat) {
919                 case 0:
920                         if (sscanf(input_buf, "%p", &child) != 1)
921                                 break;
922                         sym_set_tristate_value(child->sym, yes);
923                         return;
924                 case 1:
925                         if (sscanf(input_buf, "%p", &child) == 1) {
926                                 show_help(child);
927                                 active = child->sym;
928                         } else
929                                 show_help(menu);
930                         break;
931                 case 255:
932                         return;
933                 }
934         }
935 }
936
937 static void conf_string(struct menu *menu)
938 {
939         const char *prompt = menu_get_prompt(menu);
940         int stat;
941
942         while (1) {
943                 cprint_init();
944                 cprint("--title");
945                 cprint("%s", prompt ? prompt : _("Main Menu"));
946                 cprint("--inputbox");
947                 switch (sym_get_type(menu->sym)) {
948                 case S_INT:
949                         cprint(_(inputbox_instructions_int));
950                         break;
951                 case S_HEX:
952                         cprint(_(inputbox_instructions_hex));
953                         break;
954                 case S_STRING:
955                         cprint(_(inputbox_instructions_string));
956                         break;
957                 default:
958                         /* panic? */;
959                 }
960                 cprint("10");
961                 cprint("75");
962                 cprint("%s", sym_get_string_value(menu->sym));
963                 stat = exec_conf();
964                 switch (stat) {
965                 case 0:
966                         if (sym_set_string_value(menu->sym, input_buf))
967                                 return;
968                         show_textbox(NULL, _("You have made an invalid entry."), 5, 43);
969                         break;
970                 case 1:
971                         show_help(menu);
972                         break;
973                 case 255:
974                         return;
975                 }
976         }
977 }
978
979 static void conf_load(void)
980 {
981         int stat;
982
983         while (1) {
984                 cprint_init();
985                 cprint("--inputbox");
986                 cprint(load_config_text);
987                 cprint("11");
988                 cprint("55");
989                 cprint("%s", filename);
990                 stat = exec_conf();
991                 switch(stat) {
992                 case 0:
993                         if (!input_buf[0])
994                                 return;
995                         if (!conf_read(input_buf))
996                                 return;
997                         show_textbox(NULL, _("File does not exist!"), 5, 38);
998                         break;
999                 case 1:
1000                         show_helptext(_("Load Alternate Configuration"), load_config_help);
1001                         break;
1002                 case 255:
1003                         return;
1004                 }
1005         }
1006 }
1007
1008 static void conf_save(void)
1009 {
1010         int stat;
1011
1012         while (1) {
1013                 cprint_init();
1014                 cprint("--inputbox");
1015                 cprint(save_config_text);
1016                 cprint("11");
1017                 cprint("55");
1018                 cprint("%s", filename);
1019                 stat = exec_conf();
1020                 switch(stat) {
1021                 case 0:
1022                         if (!input_buf[0])
1023                                 return;
1024                         if (!conf_write(input_buf))
1025                                 return;
1026                         show_textbox(NULL, _("Can't create file!  Probably a nonexistent directory."), 5, 60);
1027                         break;
1028                 case 1:
1029                         show_helptext(_("Save Alternate Configuration"), save_config_help);
1030                         break;
1031                 case 255:
1032                         return;
1033                 }
1034         }
1035 }
1036
1037 static void conf_cleanup(void)
1038 {
1039         tcsetattr(1, TCSAFLUSH, &ios_org);
1040         unlink(".help.tmp");
1041         unlink("lxdialog.scrltmp");
1042 }
1043
1044 int main(int ac, char **av)
1045 {
1046         struct symbol *sym;
1047         char *mode;
1048         int stat;
1049
1050         setlocale(LC_ALL, "");
1051         bindtextdomain(PACKAGE, LOCALEDIR);
1052         textdomain(PACKAGE);
1053
1054         conf_parse(av[1]);
1055         conf_read(NULL);
1056
1057         sym = sym_lookup("OPENWRTVERSION", 0);
1058         sym_calc_value(sym);
1059         sprintf(menu_backtitle, _("OpenWrt %s Configuration"),
1060                 sym_get_string_value(sym));
1061
1062         mode = getenv("MENUCONFIG_MODE");
1063         if (mode) {
1064                 if (!strcasecmp(mode, "single_menu"))
1065                         single_menu_mode = 1;
1066         }
1067
1068         tcgetattr(1, &ios_org);
1069         atexit(conf_cleanup);
1070         init_wsize();
1071         conf(&rootmenu);
1072
1073         do {
1074                 cprint_init();
1075                 cprint("--yesno");
1076                 cprint(_("Do you wish to save your new OpenWrt configuration?"));
1077                 cprint("5");
1078                 cprint("60");
1079                 stat = exec_conf();
1080         } while (stat < 0);
1081
1082         if (stat == 0) {
1083                 if (conf_write(NULL)) {
1084                         fprintf(stderr, _("\n\n"
1085                                 "Error during writing of the OpenWrt configuration.\n"
1086                                 "Your configuration changes were NOT saved."
1087                                 "\n\n"));
1088                         return 1;
1089                 }
1090                 printf(_("\n\n"
1091                         "*** End of OpenWrt configuration.\n"
1092                         "*** Execute 'make' to build the OpenWrt or try 'make help'."
1093                         "\n\n"));
1094         } else {
1095                 fprintf(stderr, _("\n\n"
1096                         "Your configuration changes were NOT saved."
1097                         "\n\n"));
1098         }
1099
1100         return 0;
1101 }