2 * Copyright (C) 2002 Roman Zippel <zippel@linux-m68k.org>
3 * Released under the terms of the GNU GPL v2.0.
5 * Introduced single menu mode (show all sub-menus in one large tree).
6 * 2002-11-06 Petr Baudis <pasky@ucw.cz>
8 * i18n, 2005, Arnaldo Carvalho de Melo <acme@conectiva.com.br>
11 #define _XOPEN_SOURCE 700
12 /* On Darwin, this may be needed to get SIGWINCH: */
13 #define _DARWIN_C_SOURCE 1
15 #include <sys/ioctl.h>
25 #include <strings.h> /* for strcasecmp */
34 #define LKC_DIRECT_LINK
37 static char menu_backtitle[128];
38 static const char mconf_readme[] = 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"
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"
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"
56 "Some additional keyboard hints:\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"
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"
68 " You may also use the <PAGE UP> and <PAGE DOWN> keys to scroll\n"
69 " unseen options into view.\n"
71 "o To exit a menu use the cursor keys to highlight the <Exit> button\n"
72 " and press <ENTER>.\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"
78 " Also, the <TAB> and cursor keys will cycle between <Select>,\n"
79 " <Exit> and <Help>\n"
81 "o To get help with an item, use the cursor keys to highlight <Help>\n"
82 " and Press <ENTER>.\n"
84 " Shortcut: Press <H> or <?>.\n"
87 "Radiolists (Choice lists)\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"
92 " Shortcut: Press the first letter of the option you wish to set then\n"
93 " press <S> or <SPACE BAR>.\n"
95 "o To see available help for the item, use the cursor keys to highlight\n"
96 " <Help> and Press <ENTER>.\n"
98 " Shortcut: Press <H> or <?>.\n"
100 " Also, the <TAB> and cursor keys will cycle between <Select> and\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"
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"
114 "Text Box (Help Window)\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"
120 "o Press <E>, <X>, <Enter> or <Esc><Esc> to exit.\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"
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"
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"
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"
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"
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"
164 "make MENUCONFIG_MODE=single_menu menuconfig\n"
166 "<Enter> will then unroll the appropriate category, or enfold it if it\n"
167 "is already unrolled.\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"
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."),
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."),
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_(
205 "For various reasons, one may wish to keep several different\n"
206 "configurations available on a single machine.\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"
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_(
219 "For various reasons, one may wish to keep different\n"
220 "configurations available on a single machine.\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"
226 "If you are uncertain what all this means then you should probably\n"
227 "leave this blank.\n"),
230 "Search for CONFIG_ symbols and display their relations.\n"
231 "Regular expressions are allowed.\n"
232 "Example: search for \"^FOO\"\n"
234 "-----------------------------------------------------------------\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"
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"
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"
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"
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"
260 "Only relevant lines are shown.\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"
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;
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;
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);
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, ...);
295 static void init_wsize(void)
300 if (!ioctl(STDIN_FILENO, TIOCGWINSZ, &ws)) {
306 env = getenv("LINES");
313 env = getenv("COLUMNS");
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"));
330 static void cprint_init(void)
334 memset(args, 0, sizeof(args));
337 cprint("./scripts/kconfig/lxdialog/lxdialog");
338 cprint("--backtitle");
339 cprint(menu_backtitle);
342 static int cprint1(const char *fmt, ...)
350 res = vsprintf(bufptr, fmt, ap);
357 static void cprint_done(void)
363 static int cprint(const char *fmt, ...)
370 res = vsprintf(bufptr, fmt, ap);
378 static void get_prompt_str(struct gstr *r, struct property *prop)
381 struct menu *submenu[8], *menu;
383 str_printf(r, "Prompt: %s\n", prop->text);
384 str_printf(r, " Defined at %s:%d\n", prop->menu->file->name,
386 if (!expr_is_yes(prop->visible.expr)) {
387 str_append(r, " Depends on: ");
388 expr_gstr_print(prop->visible.expr, r);
391 menu = prop->menu->parent;
392 for (i = 0; menu != &rootmenu && i < 8; menu = menu->parent)
395 str_printf(r, " Location:\n");
396 for (j = 4; --i >= 0; j += 2) {
398 str_printf(r, "%*c-> %s", j, ' ', menu_get_prompt(menu));
400 str_printf(r, " (%s [=%s])", menu->sym->name ?
401 menu->sym->name : "<choice>",
402 sym_get_string_value(menu->sym));
409 static void get_symbol_str(struct gstr *r, struct symbol *sym)
412 struct property *prop;
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);
419 for_all_properties(sym, prop, P_SELECT) {
421 str_append(r, " Selects: ");
424 str_printf(r, " && ");
425 expr_gstr_print(prop->expr, r);
429 if (sym->rev_dep.expr) {
430 str_append(r, " Selected by: ");
431 expr_gstr_print(sym->rev_dep.expr, r);
434 str_append(r, "\n\n");
437 static struct gstr get_relations_str(struct symbol **sym_arr)
440 struct gstr res = str_new();
443 for (i = 0; sym_arr && (sym = sym_arr[i]); i++)
444 get_symbol_str(&res, sym);
446 str_append(&res, "No matches found.\n");
453 static void winch_handler(int sig)
462 static int exec_conf(void)
464 int pipefd[2], stat, size;
465 sigset_t sset, osset;
468 sigaddset(&sset, SIGINT);
469 sigprocmask(SIG_BLOCK, &sset, &osset);
471 signal(SIGINT, SIG_DFL);
476 sa.sa_handler = winch_handler;
477 sigemptyset(&sa.sa_mask);
478 sa.sa_flags = SA_RESTART;
479 sigaction(SIGWINCH, &sa, NULL);
489 sigprocmask(SIG_SETMASK, &osset, NULL);
493 execv(args[0], args);
500 size = input_buf + sizeof(input_buf) - bufptr;
501 size = read(pipefd[0], bufptr, size);
504 if (errno == EINTR || errno == EAGAIN)
514 waitpid(pid, &stat, 0);
519 sigprocmask(SIG_SETMASK, &osset, NULL);
522 if (WIFSIGNALED(stat)) {
523 printf("\finterrupted(%d)\n", WTERMSIG(stat));
527 printf("\fexit state: %d\nexit data: '%s'\n", WEXITSTATUS(stat), input_buf);
531 if (sigismember(&sset, SIGINT)) {
532 printf("\finterrupted\n");
535 sigprocmask(SIG_SETMASK, &osset, NULL);
537 return WEXITSTATUS(stat);
540 static void search_conf(void)
542 struct symbol **sym_arr;
549 cprint(_("Search Configuration Parameter"));
550 cprint("--inputbox");
551 cprint(_("Enter CONFIG_ (sub)string to search for (omit CONFIG_)"));
562 show_helptext(_("Search Configuration"), search_help);
568 sym_arr = sym_re_search(input_buf);
569 res = get_relations_str(sym_arr);
571 show_textbox(_("Search Results"), str_get(&res), 0, 0);
575 static void build_conf(struct menu *menu)
578 struct property *prop;
580 int type, tmp, doint = 2;
584 if (!menu_is_visible(menu))
590 if (prop && menu != current_menu) {
591 const char *prompt = menu_get_prompt(menu);
592 switch (prop->type) {
597 if (single_menu_mode) {
599 menu->data ? "-->" : "++>",
600 indent + 1, ' ', prompt);
602 cprint1(" %*c%s --->", indent + 1, ' ', prompt);
605 if (single_menu_mode && menu->data)
612 cprint("---%*c%s", indent + 1, ' ', prompt);
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;
626 for (child = menu->list; child; child = child->next) {
627 if (menu_is_visible(child) && child->sym == def_sym)
631 val = sym_get_tristate_value(sym);
632 if (sym_is_changable(sym)) {
636 cprint1("[%c]", val == no ? ' ' : '*');
640 case yes: ch = '*'; break;
641 case mod: ch = 'M'; break;
642 default: ch = ' '; break;
648 cprint("%c%p", def_menu ? 't' : ':', menu);
652 cprint1("%*c%s", indent + 1, ' ', menu_get_prompt(menu));
655 cprint1(" (%s)", menu_get_prompt(def_menu));
658 if (def_menu->list) {
660 build_conf(def_menu);
669 if (menu == current_menu) {
671 cprint("---%*c%s", indent + 1, ' ', menu_get_prompt(menu));
675 val = sym_get_tristate_value(sym);
676 if (sym_is_choice_value(sym) && val == yes) {
683 if (sym_is_changable(sym))
684 cprint1("[%c]", val == no ? ' ' : '*');
691 case yes: ch = '*'; break;
692 case mod: ch = 'M'; break;
693 default: ch = ' '; break;
695 if (sym_is_changable(sym))
702 tmp = cprint1("(%s)", sym_get_string_value(sym));
703 tmp = indent - tmp + 4;
706 cprint1("%*c%s%s", tmp, ' ', menu_get_prompt(menu),
707 (sym_has_value(sym) || !sym_is_changable(sym)) ?
713 cprint1("%*c%s%s", indent + 1, ' ', menu_get_prompt(menu),
714 (sym_has_value(sym) || !sym_is_changable(sym)) ?
716 if (menu->prompt->type == P_MENU) {
726 for (child = menu->list; child; child = child->next)
731 static void conf(struct menu *menu)
733 struct menu *submenu;
734 const char *prompt = menu_get_prompt(menu);
736 char active_entry[40];
739 unlink("lxdialog.scrltmp");
744 cprint("%s", prompt ? prompt : _("Main Menu"));
746 cprint(_(menu_instructions));
749 cprint("%d", rows - 10);
750 cprint("%s", active_entry);
755 if (menu == &rootmenu) {
759 cprint(_(" Load an Alternate Configuration File"));
761 cprint(_(" Save Configuration to an Alternate File"));
767 if (stat == 1 || stat == 255)
774 for (i = 0; input_buf[i] && !isspace(input_buf[i]); i++)
776 if (i >= sizeof(active_entry))
777 i = sizeof(active_entry) - 1;
779 strcpy(active_entry, input_buf);
783 if (sscanf(input_buf + 1, "%p", &submenu) == 1)
790 if (single_menu_mode)
791 submenu->data = (void *) (long) !submenu->data;
796 if (sym_is_choice(sym) && sym_get_tristate_value(sym) == yes)
797 conf_choice(submenu);
798 else if (submenu->prompt->type == P_MENU)
802 conf_string(submenu);
816 show_helptext("README", _(mconf_readme));
820 if (sym_set_tristate_value(sym, yes))
822 if (sym_set_tristate_value(sym, mod))
823 show_textbox(NULL, setmod_text, 6, 74);
828 sym_set_tristate_value(sym, no);
832 sym_set_tristate_value(sym, mod);
836 sym_toggle_tristate_value(sym);
837 else if (type == 'm')
847 static void show_textbox(const char *title, const char *text, int r, int c)
850 int len = strlen(text);
852 fd = creat(".help.tmp", 0777);
853 if (write(fd, text, len) != len)
856 show_file(".help.tmp", title, r, c);
860 static void show_helptext(const char *title, const char *text)
862 show_textbox(title, text, 0, 0);
865 static void show_help(struct menu *menu)
867 struct gstr help = str_new();
868 struct symbol *sym = menu->sym;
873 str_printf(&help, "CONFIG_%s:\n\n", sym->name);
874 str_append(&help, _(sym->help));
875 str_append(&help, "\n");
878 str_append(&help, nohelp_text);
880 get_symbol_str(&help, sym);
881 show_helptext(menu_get_prompt(menu), str_get(&help));
885 static void show_file(const char *filename, const char *title, int r, int c)
894 cprint("%s", filename);
895 cprint("%d", r ? r : rows);
896 cprint("%d", c ? c : cols);
897 } while (exec_conf() < 0);
900 static void conf_choice(struct menu *menu)
902 const char *prompt = menu_get_prompt(menu);
904 struct symbol *active;
907 active = sym_get_choice_value(menu->sym);
911 cprint("%s", prompt ? prompt : _("Main Menu"));
912 cprint("--radiolist");
913 cprint(_(radiolist_instructions));
919 for (child = menu->list; child; child = child->next) {
920 if (!menu_is_visible(child))
923 cprint("%s", menu_get_prompt(child));
924 if (child->sym == sym_get_choice_value(menu->sym))
926 else if (child->sym == active)
935 if (sscanf(input_buf, "%p", &child) != 1)
937 sym_set_tristate_value(child->sym, yes);
940 if (sscanf(input_buf, "%p", &child) == 1) {
952 static void conf_string(struct menu *menu)
954 const char *prompt = menu_get_prompt(menu);
960 cprint("%s", prompt ? prompt : _("Main Menu"));
961 cprint("--inputbox");
962 switch (sym_get_type(menu->sym)) {
964 cprint(_(inputbox_instructions_int));
967 cprint(_(inputbox_instructions_hex));
970 cprint(_(inputbox_instructions_string));
977 cprint("%s", sym_get_string_value(menu->sym));
981 if (sym_set_string_value(menu->sym, input_buf))
983 show_textbox(NULL, _("You have made an invalid entry."), 5, 43);
994 static void conf_load(void)
1000 cprint("--inputbox");
1001 cprint(load_config_text);
1004 cprint("%s", filename);
1010 if (!conf_read(input_buf))
1012 show_textbox(NULL, _("File does not exist!"), 5, 38);
1015 show_helptext(_("Load Alternate Configuration"), load_config_help);
1023 static void conf_save(void)
1029 cprint("--inputbox");
1030 cprint(save_config_text);
1033 cprint("%s", filename);
1039 if (!conf_write(input_buf))
1041 show_textbox(NULL, _("Can't create file! Probably a nonexistent directory."), 5, 60);
1044 show_helptext(_("Save Alternate Configuration"), save_config_help);
1052 static void conf_cleanup(void)
1054 tcsetattr(1, TCSAFLUSH, &ios_org);
1055 unlink(".help.tmp");
1056 unlink("lxdialog.scrltmp");
1059 int main(int ac, char **av)
1065 setlocale(LC_ALL, "");
1066 bindtextdomain(PACKAGE, LOCALEDIR);
1067 textdomain(PACKAGE);
1072 sym = sym_lookup("KERNELVERSION", 0);
1073 sym_calc_value(sym);
1074 sprintf(menu_backtitle, _("BusyBox %s Configuration"),
1075 sym_get_string_value(sym));
1077 mode = getenv("MENUCONFIG_MODE");
1079 if (!strcasecmp(mode, "single_menu"))
1080 single_menu_mode = 1;
1083 tcgetattr(1, &ios_org);
1084 atexit(conf_cleanup);
1091 cprint(_("Do you wish to save your new configuration?"));
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."
1106 "*** End of configuration.\n"
1107 "*** Execute 'make' to build the project or try 'make help'."
1110 fprintf(stderr, _("\n\n"
1111 "Your configuration changes were NOT saved."