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>
23 #define LKC_DIRECT_LINK
26 static char menu_backtitle[128];
27 static const char menu_instructions[] =
28 "Arrow keys navigate the menu. "
29 "<Enter> selects submenus --->. "
30 "Highlighted letters are hotkeys. "
31 "Pressing <Y> includes, <N> excludes, <M> modularizes features. "
32 "Press <Esc><Esc> to exit, <?> for Help, </> for Search. "
33 "Legend: [*] built-in [ ] excluded <M> module < > module capable",
34 radiolist_instructions[] =
35 "Use the arrow keys to navigate this window or "
36 "press the hotkey of the item you wish to select "
37 "followed by the <SPACE BAR>. "
38 "Press <?> for additional information about this option.",
39 inputbox_instructions_int[] =
40 "Please enter a decimal value. "
41 "Fractions will not be accepted. "
42 "Use the <TAB> key to move from the input field to the buttons below it.",
43 inputbox_instructions_hex[] =
44 "Please enter a hexadecimal value. "
45 "Use the <TAB> key to move from the input field to the buttons below it.",
46 inputbox_instructions_string[] =
47 "Please enter a string value. "
48 "Use the <TAB> key to move from the input field to the buttons below it.",
50 "This feature depends on another which has been configured as a module.\n"
51 "As a result, this feature will be built as a module.",
53 "There is no help available for this kernel option.\n",
55 "Enter the name of the configuration file you wish to load. "
56 "Accept the name shown to restore the configuration you "
57 "last retrieved. Leave blank to abort.",
60 "For various reasons, one may wish to keep several different kernel\n"
61 "configurations available on a single machine.\n"
63 "If you have saved a previous configuration in a file other than the\n"
64 "kernel's default, entering the name of the file here will allow you\n"
65 "to modify that configuration.\n"
67 "If you are uncertain, then you have probably never used alternate\n"
68 "configuration files. You should therefor leave this blank to abort.\n",
70 "Enter a filename to which this configuration should be saved "
71 "as an alternate. Leave blank to abort.",
74 "For various reasons, one may wish to keep different kernel\n"
75 "configurations available on a single machine.\n"
77 "Entering a file name here will allow you to later retrieve, modify\n"
78 "and use the current configuration as an alternate to whatever\n"
79 "configuration options you have selected at that time.\n"
81 "If you are uncertain what all this means then you should probably\n"
85 static char buf[4096], *bufptr = buf;
86 static char input_buf[4096];
87 static char filename[PATH_MAX+1] = ".config";
88 static char *args[1024], **argptr = args;
90 static struct termios ios_org;
91 static int rows = 0, cols = 0;
92 static struct menu *current_menu;
93 static int child_count;
95 static int single_menu_mode;
97 static void conf(struct menu *menu);
98 static void conf_choice(struct menu *menu);
99 static void conf_string(struct menu *menu);
100 static void conf_load(void);
101 static void conf_save(void);
102 static void show_textbox(const char *title, const char *text, int r, int c);
103 static void show_helptext(const char *title, const char *text);
104 static void show_help(struct menu *menu);
105 static void show_readme(void);
106 static void show_file(const char *filename, const char *title, int r, int c);
107 static void show_expr(struct menu *menu, FILE *fp);
108 static void search_conf(char *pattern);
109 static int regex_match(const char *string, regex_t *re);
111 static void cprint_init(void);
112 static int cprint1(const char *fmt, ...);
113 static void cprint_done(void);
114 static int cprint(const char *fmt, ...);
116 static void init_wsize(void)
121 if (!ioctl(STDIN_FILENO, TIOCGWINSZ, &ws)) {
127 env = getenv("LINES");
134 env = getenv("COLUMNS");
141 if (rows < 19 || cols < 80) {
142 fprintf(stderr, "Your display is too small to run Menuconfig!\n");
143 fprintf(stderr, "It must be at least 19 lines by 80 columns.\n");
151 static void cprint_init(void)
155 memset(args, 0, sizeof(args));
158 cprint("./scripts/lxdialog/lxdialog");
159 cprint("--backtitle");
160 cprint(menu_backtitle);
163 static int cprint1(const char *fmt, ...)
171 res = vsprintf(bufptr, fmt, ap);
178 static void cprint_done(void)
184 static int cprint(const char *fmt, ...)
191 res = vsprintf(bufptr, fmt, ap);
201 static void winch_handler(int sig)
209 static int exec_conf(void)
211 int pipefd[2], stat, size;
213 sigset_t sset, osset;
216 sigaddset(&sset, SIGINT);
217 sigprocmask(SIG_BLOCK, &sset, &osset);
219 signal(SIGINT, SIG_DFL);
221 sa.sa_handler = winch_handler;
222 sigemptyset(&sa.sa_mask);
223 sa.sa_flags = SA_RESTART;
224 sigaction(SIGWINCH, &sa, NULL);
231 sigprocmask(SIG_SETMASK, &osset, NULL);
235 execv(args[0], args);
242 size = input_buf + sizeof(input_buf) - bufptr;
243 size = read(pipefd[0], bufptr, size);
246 if (errno == EINTR || errno == EAGAIN)
256 waitpid(pid, &stat, 0);
261 sigprocmask(SIG_SETMASK, &osset, NULL);
264 if (WIFSIGNALED(stat)) {
265 printf("\finterrupted(%d)\n", WTERMSIG(stat));
269 printf("\fexit state: %d\nexit data: '%s'\n", WEXITSTATUS(stat), input_buf);
273 if (sigismember(&sset, SIGINT)) {
274 printf("\finterrupted\n");
277 sigprocmask(SIG_SETMASK, &osset, NULL);
279 return WEXITSTATUS(stat);
282 static int regex_match(const char *string, regex_t *re)
286 rc = regexec(re, string, (size_t) 0, NULL, 0);
292 static void show_expr(struct menu *menu, FILE *fp)
295 fprintf(fp, "Depends:\n ");
296 if (menu->prompt->visible.expr) {
299 expr_fprint(menu->prompt->visible.expr, fp);
304 struct property *prop;
306 fprintf(fp, "\nSelects:\n ");
307 for_all_properties(menu->sym, prop, P_SELECT) {
310 expr_fprint(prop->expr, fp);
315 fprintf(fp, "\nSelected by:\n ");
316 if (menu->sym->rev_dep.expr) {
318 expr_fprint(menu->sym->rev_dep.expr, fp);
325 static void search_conf(char *pattern)
327 struct symbol *sym = NULL;
328 struct menu *menu[32] = { 0 };
329 struct property *prop = NULL;
335 if (regcomp(&re, pattern, REG_EXTENDED|REG_NOSUB))
338 fp = fopen(".search.tmp", "w");
343 for_all_symbols(i, sym) {
346 if (!regex_match(sym->name, &re))
348 for_all_prompts(sym, prop) {
349 struct menu *submenu = prop->menu;
356 submenu = submenu->parent;
361 fprintf(fp, "%s (%s)\n", prop->text, sym->name);
362 fprintf(fp, "Location:\n");
364 for (k = j-2, l=1; k > 0; k--, l++) {
365 const char *prompt = menu_get_prompt(menu[k]);
367 fprintf(fp, "%*c-> %s (%s)\n",
372 fprintf(fp, "%*c-> %s\n",
377 show_expr(menu[0], fp);
378 fprintf(fp, "\n\n\n");
383 fprintf(fp, "No matches found.");
386 show_file(".search.tmp", "Search Results", rows, cols);
387 unlink(".search.tmp");
390 static void build_conf(struct menu *menu)
393 struct property *prop;
395 int type, tmp, doint = 2;
399 if (!menu_is_visible(menu))
405 if (prop && menu != current_menu) {
406 const char *prompt = menu_get_prompt(menu);
407 switch (prop->type) {
412 if (single_menu_mode) {
414 menu->data ? "-->" : "++>",
415 indent + 1, ' ', prompt);
417 cprint1(" %*c%s --->", indent + 1, ' ', prompt);
420 if (single_menu_mode && menu->data)
427 cprint("---%*c%s", indent + 1, ' ', prompt);
435 type = sym_get_type(sym);
436 if (sym_is_choice(sym)) {
437 struct symbol *def_sym = sym_get_choice_value(sym);
438 struct menu *def_menu = NULL;
441 for (child = menu->list; child; child = child->next) {
442 if (menu_is_visible(child) && child->sym == def_sym)
446 val = sym_get_tristate_value(sym);
447 if (sym_is_changable(sym)) {
451 cprint1("[%c]", val == no ? ' ' : '*');
455 case yes: ch = '*'; break;
456 case mod: ch = 'M'; break;
457 default: ch = ' '; break;
463 cprint("%c%p", def_menu ? 't' : ':', menu);
467 cprint1("%*c%s", indent + 1, ' ', menu_get_prompt(menu));
470 cprint1(" (%s)", menu_get_prompt(def_menu));
473 if (def_menu->list) {
475 build_conf(def_menu);
484 if (menu == current_menu) {
486 cprint("---%*c%s", indent + 1, ' ', menu_get_prompt(menu));
490 val = sym_get_tristate_value(sym);
491 if (sym_is_choice_value(sym) && val == yes) {
498 if (sym_is_changable(sym))
499 cprint1("[%c]", val == no ? ' ' : '*');
506 case yes: ch = '*'; break;
507 case mod: ch = 'M'; break;
508 default: ch = ' '; break;
510 if (sym_is_changable(sym))
517 tmp = cprint1("(%s)", sym_get_string_value(sym));
518 tmp = indent - tmp + 4;
521 cprint1("%*c%s%s", tmp, ' ', menu_get_prompt(menu),
522 (sym_has_value(sym) || !sym_is_changable(sym)) ?
528 cprint1("%*c%s%s", indent + 1, ' ', menu_get_prompt(menu),
529 (sym_has_value(sym) || !sym_is_changable(sym)) ?
531 if (menu->prompt->type == P_MENU) {
541 for (child = menu->list; child; child = child->next)
546 static void conf(struct menu *menu)
548 struct menu *submenu;
549 const char *prompt = menu_get_prompt(menu);
551 char active_entry[40];
554 unlink("lxdialog.scrltmp");
559 cprint("%s", prompt ? prompt : "Main Menu");
561 cprint(menu_instructions);
564 cprint("%d", rows - 10);
565 cprint("%s", active_entry);
570 if (menu == &rootmenu) {
574 cprint(" Load an Alternate Configuration File");
576 cprint(" Save Configuration to an Alternate File");
582 if (!strlen(input_buf))
584 pattern = malloc(sizeof(char)*sizeof(input_buf));
585 if (pattern == NULL) {
589 for (i = 0; input_buf[i]; i++)
590 pattern[i] = toupper(input_buf[i]);
592 search_conf(pattern);
599 if (stat == 1 || stat == 255)
606 for (i = 0; input_buf[i] && !isspace(input_buf[i]); i++)
608 if (i >= sizeof(active_entry))
609 i = sizeof(active_entry) - 1;
611 strcpy(active_entry, input_buf);
615 if (sscanf(input_buf + 1, "%p", &submenu) == 1)
622 if (single_menu_mode)
623 submenu->data = (void *) (long) !submenu->data;
628 if (sym_is_choice(sym) && sym_get_tristate_value(sym) == yes)
629 conf_choice(submenu);
630 else if (submenu->prompt->type == P_MENU)
634 conf_string(submenu);
652 if (sym_set_tristate_value(sym, yes))
654 if (sym_set_tristate_value(sym, mod))
655 show_textbox(NULL, setmod_text, 6, 74);
660 sym_set_tristate_value(sym, no);
664 sym_set_tristate_value(sym, mod);
668 sym_toggle_tristate_value(sym);
669 else if (type == 'm')
676 static void show_textbox(const char *title, const char *text, int r, int c)
680 fd = creat(".help.tmp", 0777);
681 write(fd, text, strlen(text));
683 show_file(".help.tmp", title, r, c);
687 static void show_helptext(const char *title, const char *text)
689 show_textbox(title, text, rows, cols);
692 static void show_help(struct menu *menu)
696 struct symbol *sym = menu->sym;
702 helptext = malloc(strlen(sym->name) + strlen(help) + 16);
703 sprintf(helptext, "CONFIG_%s:\n\n%s", sym->name, help);
704 show_helptext(menu_get_prompt(menu), helptext);
707 show_helptext(menu_get_prompt(menu), help);
710 static void show_readme(void)
712 show_file("scripts/README.Menuconfig", NULL, rows, cols);
715 static void show_file(const char *filename, const char *title, int r, int c)
724 cprint("%s", filename);
727 } while (exec_conf() < 0);
730 static void conf_choice(struct menu *menu)
732 const char *prompt = menu_get_prompt(menu);
734 struct symbol *active;
737 active = sym_get_choice_value(menu->sym);
741 cprint("%s", prompt ? prompt : "Main Menu");
742 cprint("--radiolist");
743 cprint(radiolist_instructions);
749 for (child = menu->list; child; child = child->next) {
750 if (!menu_is_visible(child))
753 cprint("%s", menu_get_prompt(child));
754 if (child->sym == sym_get_choice_value(menu->sym))
756 else if (child->sym == active)
765 if (sscanf(input_buf, "%p", &child) != 1)
767 sym_set_tristate_value(child->sym, yes);
770 if (sscanf(input_buf, "%p", &child) == 1) {
782 static void conf_string(struct menu *menu)
784 const char *prompt = menu_get_prompt(menu);
790 cprint("%s", prompt ? prompt : "Main Menu");
791 cprint("--inputbox");
792 switch (sym_get_type(menu->sym)) {
794 cprint(inputbox_instructions_int);
797 cprint(inputbox_instructions_hex);
800 cprint(inputbox_instructions_string);
807 cprint("%s", sym_get_string_value(menu->sym));
811 if (sym_set_string_value(menu->sym, input_buf))
813 show_textbox(NULL, "You have made an invalid entry.", 5, 43);
824 static void conf_load(void)
830 cprint("--inputbox");
831 cprint(load_config_text);
834 cprint("%s", filename);
840 if (!conf_read(input_buf))
842 show_textbox(NULL, "File does not exist!", 5, 38);
845 show_helptext("Load Alternate Configuration", load_config_help);
853 static void conf_save(void)
859 cprint("--inputbox");
860 cprint(save_config_text);
863 cprint("%s", filename);
869 if (!conf_write(input_buf))
871 show_textbox(NULL, "Can't create file! Probably a nonexistent directory.", 5, 60);
874 show_helptext("Save Alternate Configuration", save_config_help);
882 static void conf_cleanup(void)
884 tcsetattr(1, TCSAFLUSH, &ios_org);
886 unlink("lxdialog.scrltmp");
889 int main(int ac, char **av)
898 sym = sym_lookup("KERNELRELEASE", 0);
900 sprintf(menu_backtitle, "Linux Kernel v%s Configuration",
901 sym_get_string_value(sym));
903 mode = getenv("MENUCONFIG_MODE");
905 if (!strcasecmp(mode, "single_menu"))
906 single_menu_mode = 1;
909 tcgetattr(1, &ios_org);
910 atexit(conf_cleanup);
917 cprint("Do you wish to save your new kernel configuration?");
924 if (conf_write(NULL)) {
925 fprintf(stderr, "\n\n"
926 "Error during writing of the kernel configuration.\n"
927 "Your kernel configuration changes were NOT saved."
932 "*** End of Linux kernel configuration.\n"
933 "*** Execute 'make' to build the kernel or try 'make help'."
936 fprintf(stderr, "\n\n"
937 "Your kernel configuration changes were NOT saved."