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);
488 sigprocmask(SIG_SETMASK, &osset, NULL);
492 execv(args[0], args);
499 size = input_buf + sizeof(input_buf) - bufptr;
500 size = read(pipefd[0], bufptr, size);
503 if (errno == EINTR || errno == EAGAIN)
513 waitpid(pid, &stat, 0);
518 sigprocmask(SIG_SETMASK, &osset, NULL);
521 if (WIFSIGNALED(stat)) {
522 printf("\finterrupted(%d)\n", WTERMSIG(stat));
526 printf("\fexit state: %d\nexit data: '%s'\n", WEXITSTATUS(stat), input_buf);
530 if (sigismember(&sset, SIGINT)) {
531 printf("\finterrupted\n");
534 sigprocmask(SIG_SETMASK, &osset, NULL);
536 return WEXITSTATUS(stat);
539 static void search_conf(void)
541 struct symbol **sym_arr;
548 cprint(_("Search Configuration Parameter"));
549 cprint("--inputbox");
550 cprint(_("Enter CONFIG_ (sub)string to search for (omit CONFIG_)"));
561 show_helptext(_("Search Configuration"), search_help);
567 sym_arr = sym_re_search(input_buf);
568 res = get_relations_str(sym_arr);
570 show_textbox(_("Search Results"), str_get(&res), 0, 0);
574 static void build_conf(struct menu *menu)
577 struct property *prop;
579 int type, tmp, doint = 2;
583 if (!menu_is_visible(menu))
589 if (prop && menu != current_menu) {
590 const char *prompt = menu_get_prompt(menu);
591 switch (prop->type) {
596 if (single_menu_mode) {
598 menu->data ? "-->" : "++>",
599 indent + 1, ' ', prompt);
601 cprint1(" %*c%s --->", indent + 1, ' ', prompt);
604 if (single_menu_mode && menu->data)
611 cprint("---%*c%s", indent + 1, ' ', prompt);
619 type = sym_get_type(sym);
620 if (sym_is_choice(sym)) {
621 struct symbol *def_sym = sym_get_choice_value(sym);
622 struct menu *def_menu = NULL;
625 for (child = menu->list; child; child = child->next) {
626 if (menu_is_visible(child) && child->sym == def_sym)
630 val = sym_get_tristate_value(sym);
631 if (sym_is_changable(sym)) {
635 cprint1("[%c]", val == no ? ' ' : '*');
639 case yes: ch = '*'; break;
640 case mod: ch = 'M'; break;
641 default: ch = ' '; break;
647 cprint("%c%p", def_menu ? 't' : ':', menu);
651 cprint1("%*c%s", indent + 1, ' ', menu_get_prompt(menu));
654 cprint1(" (%s)", menu_get_prompt(def_menu));
657 if (def_menu->list) {
659 build_conf(def_menu);
668 if (menu == current_menu) {
670 cprint("---%*c%s", indent + 1, ' ', menu_get_prompt(menu));
674 val = sym_get_tristate_value(sym);
675 if (sym_is_choice_value(sym) && val == yes) {
682 if (sym_is_changable(sym))
683 cprint1("[%c]", val == no ? ' ' : '*');
690 case yes: ch = '*'; break;
691 case mod: ch = 'M'; break;
692 default: ch = ' '; break;
694 if (sym_is_changable(sym))
701 tmp = cprint1("(%s)", sym_get_string_value(sym));
702 tmp = indent - tmp + 4;
705 cprint1("%*c%s%s", tmp, ' ', menu_get_prompt(menu),
706 (sym_has_value(sym) || !sym_is_changable(sym)) ?
712 cprint1("%*c%s%s", indent + 1, ' ', menu_get_prompt(menu),
713 (sym_has_value(sym) || !sym_is_changable(sym)) ?
715 if (menu->prompt->type == P_MENU) {
725 for (child = menu->list; child; child = child->next)
730 static void conf(struct menu *menu)
732 struct menu *submenu;
733 const char *prompt = menu_get_prompt(menu);
735 char active_entry[40];
738 unlink("lxdialog.scrltmp");
743 cprint("%s", prompt ? prompt : _("Main Menu"));
745 cprint(_(menu_instructions));
748 cprint("%d", rows - 10);
749 cprint("%s", active_entry);
754 if (menu == &rootmenu) {
758 cprint(_(" Load an Alternate Configuration File"));
760 cprint(_(" Save Configuration to an Alternate File"));
766 if (stat == 1 || stat == 255)
773 for (i = 0; input_buf[i] && !isspace(input_buf[i]); i++)
775 if (i >= sizeof(active_entry))
776 i = sizeof(active_entry) - 1;
778 strcpy(active_entry, input_buf);
782 if (sscanf(input_buf + 1, "%p", &submenu) == 1)
789 if (single_menu_mode)
790 submenu->data = (void *) (long) !submenu->data;
795 if (sym_is_choice(sym) && sym_get_tristate_value(sym) == yes)
796 conf_choice(submenu);
797 else if (submenu->prompt->type == P_MENU)
801 conf_string(submenu);
815 show_helptext("README", _(mconf_readme));
819 if (sym_set_tristate_value(sym, yes))
821 if (sym_set_tristate_value(sym, mod))
822 show_textbox(NULL, setmod_text, 6, 74);
827 sym_set_tristate_value(sym, no);
831 sym_set_tristate_value(sym, mod);
835 sym_toggle_tristate_value(sym);
836 else if (type == 'm')
846 static void show_textbox(const char *title, const char *text, int r, int c)
850 fd = creat(".help.tmp", 0777);
851 write(fd, text, strlen(text));
853 show_file(".help.tmp", title, r, c);
857 static void show_helptext(const char *title, const char *text)
859 show_textbox(title, text, 0, 0);
862 static void show_help(struct menu *menu)
864 struct gstr help = str_new();
865 struct symbol *sym = menu->sym;
870 str_printf(&help, "CONFIG_%s:\n\n", sym->name);
871 str_append(&help, _(sym->help));
872 str_append(&help, "\n");
875 str_append(&help, nohelp_text);
877 get_symbol_str(&help, sym);
878 show_helptext(menu_get_prompt(menu), str_get(&help));
882 static void show_file(const char *filename, const char *title, int r, int c)
891 cprint("%s", filename);
892 cprint("%d", r ? r : rows);
893 cprint("%d", c ? c : cols);
894 } while (exec_conf() < 0);
897 static void conf_choice(struct menu *menu)
899 const char *prompt = menu_get_prompt(menu);
901 struct symbol *active;
904 active = sym_get_choice_value(menu->sym);
908 cprint("%s", prompt ? prompt : _("Main Menu"));
909 cprint("--radiolist");
910 cprint(_(radiolist_instructions));
916 for (child = menu->list; child; child = child->next) {
917 if (!menu_is_visible(child))
920 cprint("%s", menu_get_prompt(child));
921 if (child->sym == sym_get_choice_value(menu->sym))
923 else if (child->sym == active)
932 if (sscanf(input_buf, "%p", &child) != 1)
934 sym_set_tristate_value(child->sym, yes);
937 if (sscanf(input_buf, "%p", &child) == 1) {
949 static void conf_string(struct menu *menu)
951 const char *prompt = menu_get_prompt(menu);
957 cprint("%s", prompt ? prompt : _("Main Menu"));
958 cprint("--inputbox");
959 switch (sym_get_type(menu->sym)) {
961 cprint(_(inputbox_instructions_int));
964 cprint(_(inputbox_instructions_hex));
967 cprint(_(inputbox_instructions_string));
974 cprint("%s", sym_get_string_value(menu->sym));
978 if (sym_set_string_value(menu->sym, input_buf))
980 show_textbox(NULL, _("You have made an invalid entry."), 5, 43);
991 static void conf_load(void)
997 cprint("--inputbox");
998 cprint(load_config_text);
1001 cprint("%s", filename);
1007 if (!conf_read(input_buf))
1009 show_textbox(NULL, _("File does not exist!"), 5, 38);
1012 show_helptext(_("Load Alternate Configuration"), load_config_help);
1020 static void conf_save(void)
1026 cprint("--inputbox");
1027 cprint(save_config_text);
1030 cprint("%s", filename);
1036 if (!conf_write(input_buf))
1038 show_textbox(NULL, _("Can't create file! Probably a nonexistent directory."), 5, 60);
1041 show_helptext(_("Save Alternate Configuration"), save_config_help);
1049 static void conf_cleanup(void)
1051 tcsetattr(1, TCSAFLUSH, &ios_org);
1052 unlink(".help.tmp");
1053 unlink("lxdialog.scrltmp");
1056 int main(int ac, char **av)
1062 setlocale(LC_ALL, "");
1063 bindtextdomain(PACKAGE, LOCALEDIR);
1064 textdomain(PACKAGE);
1069 sym = sym_lookup("KERNELVERSION", 0);
1070 sym_calc_value(sym);
1071 sprintf(menu_backtitle, _("BusyBox %s Configuration"),
1072 sym_get_string_value(sym));
1074 mode = getenv("MENUCONFIG_MODE");
1076 if (!strcasecmp(mode, "single_menu"))
1077 single_menu_mode = 1;
1080 tcgetattr(1, &ios_org);
1081 atexit(conf_cleanup);
1088 cprint(_("Do you wish to save your new configuration?"));
1095 if (conf_write(NULL)) {
1096 fprintf(stderr, _("\n\n"
1097 "Error during writing of the configuration.\n"
1098 "Your configuration changes were NOT saved."
1103 "*** End of configuration.\n"
1104 "*** Execute 'make' to build the project or try 'make help'."
1107 fprintf(stderr, _("\n\n"
1108 "Your configuration changes were NOT saved."