finally move buildroot-ng to trunk
[openwrt/staging/wigyori.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 }