disable the automatic config reset if 'Advanced configuration options' is selected
[openwrt/svn-archive/archive.git] / scripts / config / mconf.c
1 /*
2 * Copyright (C) 2002 Roman Zippel <zippel@linux-m68k.org>
3 * Released under the terms of the GNU GPL v2.0.
4 *
5 * Introduced single menu mode (show all sub-menus in one large tree).
6 * 2002-11-06 Petr Baudis <pasky@ucw.cz>
7 *
8 * i18n, 2005, Arnaldo Carvalho de Melo <acme@conectiva.com.br>
9 */
10
11 #include <sys/ioctl.h>
12 #include <sys/wait.h>
13 #include <ctype.h>
14 #include <errno.h>
15 #include <fcntl.h>
16 #include <limits.h>
17 #include <signal.h>
18 #include <stdarg.h>
19 #include <stdlib.h>
20 #include <string.h>
21 #include <termios.h>
22 #include <unistd.h>
23 #include <locale.h>
24
25 #define BUFSIZE 524288
26 #define LKC_DIRECT_LINK
27 #include "lkc.h"
28
29 static char menu_backtitle[128];
30 static const char mconf_readme[] = N_(
31 "Overview\n"
32 "--------\n"
33 "Some OpenWrt features may be built directly into the image.\n"
34 "Some may be made into installable ipkg packages. Some features\n"
35 "may be completely removed altogether.\n"
36 "\n"
37 "Menu items beginning with [*], <M> or [ ] represent features\n"
38 "configured to be included, built as package or removed respectively.\n"
39 "Pointed brackets <> represent packaging capable features.\n"
40 "\n"
41 "To change any of these features, highlight it with the cursor\n"
42 "keys and press <Y> to include it, <M> to make it a package or\n"
43 "<N> to remove it. You may also press the <Space Bar> to cycle\n"
44 "through the available options (ie. Y->N->M->Y).\n"
45 "\n"
46 "Some additional keyboard hints:\n"
47 "\n"
48 "Menus\n"
49 "----------\n"
50 "o Use the Up/Down arrow keys (cursor keys) to highlight the item\n"
51 " you wish to change or submenu wish to select and press <Enter>.\n"
52 " Submenus are designated by \"--->\".\n"
53 "\n"
54 " Shortcut: Press the option's highlighted letter (hotkey).\n"
55 " Pressing a hotkey more than once will sequence\n"
56 " through all visible items which use that hotkey.\n"
57 "\n"
58 " You may also use the <PAGE UP> and <PAGE DOWN> keys to scroll\n"
59 " unseen options into view.\n"
60 "\n"
61 "o To exit a menu use the cursor keys to highlight the <Exit> button\n"
62 " and press <ENTER>.\n"
63 "\n"
64 " Shortcut: Press <ESC><ESC> or <E> or <X> if there is no hotkey\n"
65 " using those letters. You may press a single <ESC>, but\n"
66 " there is a delayed response which you may find annoying.\n"
67 "\n"
68 " Also, the <TAB> and cursor keys will cycle between <Select>,\n"
69 " <Exit> and <Help>\n"
70 "\n"
71 "o To get help with an item, use the cursor keys to highlight <Help>\n"
72 " and Press <ENTER>.\n"
73 "\n"
74 " Shortcut: Press <H> or <?>.\n"
75 "\n"
76 "\n"
77 "Radiolists (Choice lists)\n"
78 "-----------\n"
79 "o Use the cursor keys to select the option you wish to set and press\n"
80 " <S> or the <SPACE BAR>.\n"
81 "\n"
82 " Shortcut: Press the first letter of the option you wish to set then\n"
83 " press <S> or <SPACE BAR>.\n"
84 "\n"
85 "o To see available help for the item, use the cursor keys to highlight\n"
86 " <Help> and Press <ENTER>.\n"
87 "\n"
88 " Shortcut: Press <H> or <?>.\n"
89 "\n"
90 " Also, the <TAB> and cursor keys will cycle between <Select> and\n"
91 " <Help>\n"
92 "\n"
93 "\n"
94 "Data Entry\n"
95 "-----------\n"
96 "o Enter the requested information and press <ENTER>\n"
97 " If you are entering hexadecimal values, it is not necessary to\n"
98 " add the '0x' prefix to the entry.\n"
99 "\n"
100 "o For help, use the <TAB> or cursor keys to highlight the help option\n"
101 " and press <ENTER>. You can try <TAB><H> as well.\n"
102 "\n"
103 "\n"
104 "Text Box (Help Window)\n"
105 "--------\n"
106 "o Use the cursor keys to scroll up/down/left/right. The VI editor\n"
107 " keys h,j,k,l function here as do <SPACE BAR> and <B> for those\n"
108 " who are familiar with less and lynx.\n"
109 "\n"
110 "o Press <E>, <X>, <Enter> or <Esc><Esc> to exit.\n"
111 "\n"
112 "\n"
113 "Alternate Configuration Files\n"
114 "-----------------------------\n"
115 "Menuconfig supports the use of alternate configuration files for\n"
116 "those who, for various reasons, find it necessary to switch\n"
117 "between different OpenWrt configurations.\n"
118 "\n"
119 "At the end of the main menu you will find two options. One is\n"
120 "for saving the current configuration to a file of your choosing.\n"
121 "The other option is for loading a previously saved alternate\n"
122 "configuration.\n"
123 "\n"
124 "Even if you don't use alternate configuration files, but you\n"
125 "find during a Menuconfig session that you have completely messed\n"
126 "up your settings, you may use the \"Load Alternate...\" option to\n"
127 "restore your previously saved settings from \".config\" without\n"
128 "restarting Menuconfig.\n"
129 "\n"
130 "Other information\n"
131 "-----------------\n"
132 "If you use Menuconfig in an XTERM window make sure you have your\n"
133 "$TERM variable set to point to a xterm definition which supports color.\n"
134 "Otherwise, Menuconfig will look rather bad. Menuconfig will not\n"
135 "display correctly in a RXVT window because rxvt displays only one\n"
136 "intensity of color, bright.\n"
137 "\n"
138 "Menuconfig will display larger menus on screens or xterms which are\n"
139 "set to display more than the standard 25 row by 80 column geometry.\n"
140 "In order for this to work, the \"stty size\" command must be able to\n"
141 "display the screen's current row and column geometry. I STRONGLY\n"
142 "RECOMMEND that you make sure you do NOT have the shell variables\n"
143 "LINES and COLUMNS exported into your environment. Some distributions\n"
144 "export those variables via /etc/profile. Some ncurses programs can\n"
145 "become confused when those variables (LINES & COLUMNS) don't reflect\n"
146 "the true screen size.\n"
147 "\n"
148 "Optional personality available\n"
149 "------------------------------\n"
150 "If you prefer to have all of the build options listed in a single\n"
151 "menu, rather than the default multimenu hierarchy, run the menuconfig\n"
152 "with MENUCONFIG_MODE environment variable set to single_menu. Example:\n"
153 "\n"
154 "make MENUCONFIG_MODE=single_menu menuconfig\n"
155 "\n"
156 "<Enter> will then unroll the appropriate category, or enfold it if it\n"
157 "is already unrolled.\n"
158 "\n"
159 "Note that this mode can eventually be a little more CPU expensive\n"
160 "(especially with a larger number of unrolled categories) than the\n"
161 "default mode.\n"),
162 menu_instructions[] = N_(
163 "Arrow keys navigate the menu. "
164 "<Enter> selects submenus --->. "
165 "Highlighted letters are hotkeys. "
166 "Pressing <Y> includes, <N> excludes, <M> builds as package. "
167 "Press <Esc><Esc> to exit, <?> for Help, </> for Search. "
168 "Legend: [*] built-in [ ] excluded <M> package < > package capable"),
169 radiolist_instructions[] = N_(
170 "Use the arrow keys to navigate this window or "
171 "press the hotkey of the item you wish to select "
172 "followed by the <SPACE BAR>. "
173 "Press <?> for additional information about this option."),
174 inputbox_instructions_int[] = N_(
175 "Please enter a decimal value. "
176 "Fractions will not be accepted. "
177 "Use the <TAB> key to move from the input field to the buttons below it."),
178 inputbox_instructions_hex[] = N_(
179 "Please enter a hexadecimal value. "
180 "Use the <TAB> key to move from the input field to the buttons below it."),
181 inputbox_instructions_string[] = N_(
182 "Please enter a string value. "
183 "Use the <TAB> key to move from the input field to the buttons below it."),
184 setmod_text[] = N_(
185 "This feature depends on another which has been configured as a package.\n"
186 "As a result, this feature will be built as a package."),
187 nohelp_text[] = N_(
188 "There is no help available for this config option.\n"),
189 load_config_text[] = N_(
190 "Enter the name of the configuration file you wish to load. "
191 "Accept the name shown to restore the configuration you "
192 "last retrieved. Leave blank to abort."),
193 load_config_help[] = N_(
194 "\n"
195 "For various reasons, one may wish to keep several different OpenWrt\n"
196 "configurations available on a single machine.\n"
197 "\n"
198 "If you have saved a previous configuration in a file other than\n"
199 "OpenWrt's default, entering the name of the file here will allow you\n"
200 "to modify that configuration.\n"
201 "\n"
202 "If you are uncertain, then you have probably never used alternate\n"
203 "configuration files. You should therefor leave this blank to abort.\n"),
204 save_config_text[] = N_(
205 "Enter a filename to which this configuration should be saved "
206 "as an alternate. Leave blank to abort."),
207 save_config_help[] = N_(
208 "\n"
209 "For various reasons, one may wish to keep different OpenWrt\n"
210 "configurations available on a single machine.\n"
211 "\n"
212 "Entering a file name here will allow you to later retrieve, modify\n"
213 "and use the current configuration as an alternate to whatever\n"
214 "configuration options you have selected at that time.\n"
215 "\n"
216 "If you are uncertain what all this means then you should probably\n"
217 "leave this blank.\n"),
218 search_help[] = N_(
219 "\n"
220 "Search for CONFIG_ symbols and display their relations.\n"
221 "Regular expressions are allowed.\n"
222 "Example: search for \"^FOO\"\n"
223 "Result:\n"
224 "-----------------------------------------------------------------\n"
225 "Symbol: FOO [=m]\n"
226 "Prompt: Foo bus is used to drive the bar HW\n"
227 "Defined at drivers/pci/Kconfig:47\n"
228 "Depends on: X86_LOCAL_APIC && X86_IO_APIC || IA64\n"
229 "Location:\n"
230 " -> Bus options (PCI, PCMCIA, EISA, MCA, ISA)\n"
231 " -> PCI support (PCI [=y])\n"
232 " -> PCI access mode (<choice> [=y])\n"
233 "Selects: LIBCRC32\n"
234 "Selected by: BAR\n"
235 "-----------------------------------------------------------------\n"
236 "o The line 'Prompt:' shows the text used in the menu structure for\n"
237 " this CONFIG_ symbol\n"
238 "o The 'Defined at' line tell at what file / line number the symbol\n"
239 " is defined\n"
240 "o The 'Depends on:' line tell what symbols needs to be defined for\n"
241 " this symbol to be visible in the menu (selectable)\n"
242 "o The 'Location:' lines tell where in the menu structure this symbol\n"
243 " is located\n"
244 " A location followed by a [=y] indicate that this is a selectable\n"
245 " menu item - and current value is displayed inside brackets.\n"
246 "o The 'Selects:' line tell what symbol will be automatically\n"
247 " selected if this symbol is selected (y or m)\n"
248 "o The 'Selected by' line tell what symbol has selected this symbol\n"
249 "\n"
250 "Only relevant lines are shown.\n"
251 "\n\n"
252 "Search examples:\n"
253 "Examples: USB => find all CONFIG_ symbols containing USB\n"
254 " ^USB => find all CONFIG_ symbols starting with USB\n"
255 " USB$ => find all CONFIG_ symbols ending with USB\n"
256 "\n");
257
258 static char buf[BUFSIZE], *bufptr = buf;
259 static char input_buf[BUFSIZE];
260 static char filename[PATH_MAX+1] = ".config";
261 static char *args[BUFSIZE], **argptr = args;
262 static int indent;
263 static struct termios ios_org;
264 static int rows = 0, cols = 0;
265 static struct menu *current_menu;
266 static int child_count;
267 static int do_resize;
268 static int single_menu_mode;
269
270 static void conf(struct menu *menu);
271 static void conf_choice(struct menu *menu);
272 static void conf_string(struct menu *menu);
273 static void conf_load(void);
274 static void conf_save(void);
275 static void show_textbox(const char *title, const char *text, int r, int c);
276 static void show_helptext(const char *title, const char *text);
277 static void show_help(struct menu *menu);
278 static void show_file(const char *filename, const char *title, int r, int c);
279
280 static void cprint_init(void);
281 static int cprint1(const char *fmt, ...);
282 static void cprint_done(void);
283 static int cprint(const char *fmt, ...);
284
285 static void init_wsize(void)
286 {
287 struct winsize ws;
288 char *env;
289
290 if (!ioctl(STDIN_FILENO, TIOCGWINSZ, &ws)) {
291 rows = ws.ws_row;
292 cols = ws.ws_col;
293 }
294
295 if (!rows) {
296 env = getenv("LINES");
297 if (env)
298 rows = atoi(env);
299 if (!rows)
300 rows = 24;
301 }
302 if (!cols) {
303 env = getenv("COLUMNS");
304 if (env)
305 cols = atoi(env);
306 if (!cols)
307 cols = 80;
308 }
309
310 if (rows < 19 || cols < 80) {
311 fprintf(stderr, N_("Your display is too small to run Menuconfig!\n"));
312 fprintf(stderr, N_("It must be at least 19 lines by 80 columns.\n"));
313 exit(1);
314 }
315
316 rows -= 4;
317 cols -= 5;
318 }
319
320 static void cprint_init(void)
321 {
322 bufptr = buf;
323 argptr = args;
324 memset(args, 0, sizeof(args));
325 indent = 0;
326 child_count = 0;
327 cprint("./scripts/config/lxdialog/lxdialog");
328 cprint("--backtitle");
329 cprint(menu_backtitle);
330 }
331
332 static int cprint1(const char *fmt, ...)
333 {
334 va_list ap;
335 int res;
336
337 if (!*argptr)
338 *argptr = bufptr;
339 va_start(ap, fmt);
340 res = vsprintf(bufptr, fmt, ap);
341 va_end(ap);
342 bufptr += res;
343
344 return res;
345 }
346
347 static void cprint_done(void)
348 {
349 *bufptr++ = 0;
350 argptr++;
351 }
352
353 static int cprint(const char *fmt, ...)
354 {
355 va_list ap;
356 int res;
357
358 *argptr++ = bufptr;
359 va_start(ap, fmt);
360 res = vsprintf(bufptr, fmt, ap);
361 va_end(ap);
362 bufptr += res;
363 *bufptr++ = 0;
364
365 return res;
366 }
367
368 static void get_prompt_str(struct gstr *r, struct property *prop)
369 {
370 int i, j;
371 struct menu *submenu[8], *menu;
372
373 str_printf(r, "Prompt: %s\n", prop->text);
374 str_printf(r, " Defined at %s:%d\n", prop->menu->file->name,
375 prop->menu->lineno);
376 if (!expr_is_yes(prop->visible.expr)) {
377 str_append(r, " Depends on: ");
378 expr_gstr_print(prop->visible.expr, r);
379 str_append(r, "\n");
380 }
381 menu = prop->menu->parent;
382 for (i = 0; menu != &rootmenu && i < 8; menu = menu->parent)
383 submenu[i++] = menu;
384 if (i > 0) {
385 str_printf(r, " Location:\n");
386 for (j = 4; --i >= 0; j += 2) {
387 menu = submenu[i];
388 str_printf(r, "%*c-> %s", j, ' ', menu_get_prompt(menu));
389 if (menu->sym) {
390 str_printf(r, " (%s [=%s])", menu->sym->name ?
391 menu->sym->name : "<choice>",
392 sym_get_string_value(menu->sym));
393 }
394 str_append(r, "\n");
395 }
396 }
397 }
398
399 static void get_symbol_str(struct gstr *r, struct symbol *sym)
400 {
401 bool hit;
402 struct property *prop;
403
404 str_printf(r, "Symbol: %s [=%s]\n", sym->name,
405 sym_get_string_value(sym));
406 for_all_prompts(sym, prop)
407 get_prompt_str(r, prop);
408 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 struct property *prop;
892 int stat;
893
894 active = sym_get_choice_value(menu->sym);
895 while (1) {
896 cprint_init();
897 cprint("--title");
898 cprint("%s", prompt ? prompt : _("Main Menu"));
899 cprint("--radiolist");
900 cprint(_(radiolist_instructions));
901 cprint("15");
902 cprint("70");
903 cprint("6");
904
905 current_menu = menu;
906 for (child = menu->list; child; child = child->next) {
907 if (!menu_is_visible(child))
908 continue;
909 cprint("%p", child);
910 cprint("%s", menu_get_prompt(child));
911 if (child->sym == sym_get_choice_value(menu->sym))
912 cprint("ON");
913 else if (child->sym == active)
914 cprint("SELECTED");
915 else
916 cprint("OFF");
917 }
918
919 stat = exec_conf();
920 switch (stat) {
921 case 0:
922 if (sscanf(input_buf, "%p", &child) != 1)
923 break;
924
925 if (sym_get_tristate_value(child->sym) != yes) {
926 for_all_properties(menu->sym, prop, P_RESET) {
927 if (expr_calc_value(prop->visible.expr) != no)
928 conf_reset();
929 }
930 }
931 sym_set_tristate_value(child->sym, yes);
932 return;
933 case 1:
934 if (sscanf(input_buf, "%p", &child) == 1) {
935 show_help(child);
936 active = child->sym;
937 } else
938 show_help(menu);
939 break;
940 case 255:
941 return;
942 }
943 }
944 }
945
946 static void conf_string(struct menu *menu)
947 {
948 const char *prompt = menu_get_prompt(menu);
949 int stat;
950
951 while (1) {
952 cprint_init();
953 cprint("--title");
954 cprint("%s", prompt ? prompt : _("Main Menu"));
955 cprint("--inputbox");
956 switch (sym_get_type(menu->sym)) {
957 case S_INT:
958 cprint(_(inputbox_instructions_int));
959 break;
960 case S_HEX:
961 cprint(_(inputbox_instructions_hex));
962 break;
963 case S_STRING:
964 cprint(_(inputbox_instructions_string));
965 break;
966 default:
967 /* panic? */;
968 }
969 cprint("10");
970 cprint("75");
971 cprint("%s", sym_get_string_value(menu->sym));
972 stat = exec_conf();
973 switch (stat) {
974 case 0:
975 if (sym_set_string_value(menu->sym, input_buf))
976 return;
977 show_textbox(NULL, _("You have made an invalid entry."), 5, 43);
978 break;
979 case 1:
980 show_help(menu);
981 break;
982 case 255:
983 return;
984 }
985 }
986 }
987
988 static void conf_load(void)
989 {
990 int stat;
991
992 while (1) {
993 cprint_init();
994 cprint("--inputbox");
995 cprint(load_config_text);
996 cprint("11");
997 cprint("55");
998 cprint("%s", filename);
999 stat = exec_conf();
1000 switch(stat) {
1001 case 0:
1002 if (!input_buf[0])
1003 return;
1004 if (!conf_read(input_buf))
1005 return;
1006 show_textbox(NULL, _("File does not exist!"), 5, 38);
1007 break;
1008 case 1:
1009 show_helptext(_("Load Alternate Configuration"), load_config_help);
1010 break;
1011 case 255:
1012 return;
1013 }
1014 }
1015 }
1016
1017 static void conf_save(void)
1018 {
1019 int stat;
1020
1021 while (1) {
1022 cprint_init();
1023 cprint("--inputbox");
1024 cprint(save_config_text);
1025 cprint("11");
1026 cprint("55");
1027 cprint("%s", filename);
1028 stat = exec_conf();
1029 switch(stat) {
1030 case 0:
1031 if (!input_buf[0])
1032 return;
1033 if (!conf_write(input_buf))
1034 return;
1035 show_textbox(NULL, _("Can't create file! Probably a nonexistent directory."), 5, 60);
1036 break;
1037 case 1:
1038 show_helptext(_("Save Alternate Configuration"), save_config_help);
1039 break;
1040 case 255:
1041 return;
1042 }
1043 }
1044 }
1045
1046 static void conf_cleanup(void)
1047 {
1048 tcsetattr(1, TCSAFLUSH, &ios_org);
1049 unlink(".help.tmp");
1050 unlink("lxdialog.scrltmp");
1051 }
1052
1053 int main(int ac, char **av)
1054 {
1055 struct symbol *sym;
1056 char *mode;
1057 int stat;
1058
1059 setlocale(LC_ALL, "");
1060 bindtextdomain(PACKAGE, LOCALEDIR);
1061 textdomain(PACKAGE);
1062
1063 conf_parse(av[1]);
1064 conf_read(NULL);
1065
1066 sym = sym_lookup("OPENWRTVERSION", 0);
1067 sym_calc_value(sym);
1068 sprintf(menu_backtitle, _("OpenWrt %s Configuration"),
1069 sym_get_string_value(sym));
1070
1071 mode = getenv("MENUCONFIG_MODE");
1072 if (mode) {
1073 if (!strcasecmp(mode, "single_menu"))
1074 single_menu_mode = 1;
1075 }
1076
1077 tcgetattr(1, &ios_org);
1078 atexit(conf_cleanup);
1079 init_wsize();
1080 conf(&rootmenu);
1081
1082 do {
1083 cprint_init();
1084 cprint("--yesno");
1085 cprint(_("Do you wish to save your new OpenWrt configuration?"));
1086 cprint("5");
1087 cprint("60");
1088 stat = exec_conf();
1089 } while (stat < 0);
1090
1091 if (stat == 0) {
1092 if (conf_write(NULL)) {
1093 fprintf(stderr, _("\n\n"
1094 "Error during writing of the OpenWrt configuration.\n"
1095 "Your configuration changes were NOT saved."
1096 "\n\n"));
1097 return 1;
1098 }
1099 printf(_("\n\n"
1100 "*** End of OpenWrt configuration.\n"
1101 "*** Execute 'make' to build the OpenWrt or try 'make help'."
1102 "\n\n"));
1103 } else {
1104 fprintf(stderr, _("\n\n"
1105 "Your configuration changes were NOT saved."
1106 "\n\n"));
1107 }
1108
1109 return 0;
1110 }