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