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 * Directly use liblxdialog library routines.
9 * 2002-11-14 Petr Baudis <pasky@ucw.cz>
12 #include <sys/ioctl.h>
14 #include <sys/termios.h>
28 #define LKC_DIRECT_LINK
31 static const char menu_instructions[] =
32 "Arrow keys navigate the menu. "
33 "<Enter> selects submenus --->. "
34 "Highlighted letters are hotkeys. "
35 "Pressing <Y> selectes a feature, while <N> will exclude a feature. "
36 "Press <Esc><Esc> to exit, <?> for Help. "
37 "Legend: [*] feature is selected [ ] feature is excluded",
38 radiolist_instructions[] =
39 "Use the arrow keys to navigate this window or "
40 "press the hotkey of the item you wish to select "
41 "followed by the <SPACE BAR>. "
42 "Press <?> for additional information about this option.",
43 inputbox_instructions_int[] =
44 "Please enter a decimal value. "
45 "Fractions will not be accepted. "
46 "Use the <TAB> key to move from the input field to the buttons below it.",
47 inputbox_instructions_hex[] =
48 "Please enter a hexadecimal value. "
49 "Use the <TAB> key to move from the input field to the buttons below it.",
50 inputbox_instructions_string[] =
51 "Please enter a string value. "
52 "Use the <TAB> key to move from the input field to the buttons below it.",
54 "This feature depends on another which has been configured as a module.\n"
55 "As a result, this feature will be built as a module.",
57 "There is no help available for this option.\n",
59 "Enter the name of the configuration file you wish to load. "
60 "Accept the name shown to restore the configuration you "
61 "last retrieved. Leave blank to abort.",
64 "For various reasons, one may wish to keep several different BusyBox\n"
65 "configurations available on a single machine.\n"
67 "If you have saved a previous configuration in a file other than the\n"
68 "BusyBox default, entering the name of the file here will allow you\n"
69 "to modify that configuration.\n"
71 "If you are uncertain, then you have probably never used alternate\n"
72 "configuration files. You should therefor leave this blank to abort.\n",
74 "Enter a filename to which this configuration should be saved "
75 "as an alternate. Leave blank to abort.",
78 "For various reasons, one may wish to keep different BusyBox\n"
79 "configurations available on a single machine.\n"
81 "Entering a file name here will allow you to later retrieve, modify\n"
82 "and use the current configuration as an alternate to whatever\n"
83 "configuration options you have selected at that time.\n"
85 "If you are uncertain what all this means then you should probably\n"
86 "leave this blank.\n",
89 "Use the Up/Down arrow keys (cursor keys) to highlight the item\n"
90 "you wish to change or submenu wish to select and press <Enter>.\n"
91 "Submenus are designated by \"--->\".\n"
93 "Shortcut: Press the option's highlighted letter (hotkey).\n"
95 "You may also use the <PAGE UP> and <PAGE DOWN> keys to scroll\n"
96 "unseen options into view.\n"
99 static char filename[PATH_MAX+1] = ".config";
100 static int indent = 0;
101 static struct termios ios_org;
102 static int rows, cols;
103 static struct menu *current_menu;
104 static int child_count;
105 static int single_menu_mode;
107 static struct dialog_list_item *items[16384]; /* FIXME: This ought to be dynamic. */
110 static void conf(struct menu *menu);
111 static void conf_choice(struct menu *menu);
112 static void conf_string(struct menu *menu);
113 static void conf_load(void);
114 static void conf_save(void);
115 static void show_textbox(const char *title, const char *text, int r, int c);
116 static void show_helptext(const char *title, const char *text);
117 static void show_help(struct menu *menu);
118 static void show_readme(void);
120 static void init_wsize(void)
124 if (ioctl(1, TIOCGWINSZ, &ws) == -1) {
132 if (rows < 19 || cols < 80) {
133 fprintf(stderr, "Your display is too small to run Menuconfig!\n");
134 fprintf(stderr, "It must be at least 19 lines by 80 columns.\n");
142 static void cinit(void)
147 static void cmake(void)
149 items[item_no] = malloc(sizeof(struct dialog_list_item));
150 memset(items[item_no], 0, sizeof(struct dialog_list_item));
151 items[item_no]->tag = malloc(32); items[item_no]->tag[0] = 0;
152 items[item_no]->name = malloc(512); items[item_no]->name[0] = 0;
153 items[item_no]->namelen = 0;
157 static int cprint_name(const char *fmt, ...)
165 res = vsnprintf(items[item_no - 1]->name + items[item_no - 1]->namelen,
166 512 - items[item_no - 1]->namelen, fmt, ap);
168 items[item_no - 1]->namelen += res;
174 static int cprint_tag(const char *fmt, ...)
182 res = vsnprintf(items[item_no - 1]->tag, 32, fmt, ap);
188 static void cdone(void)
192 for (i = 0; i < item_no; i++) {
194 free(items[i]->name);
201 static void build_conf(struct menu *menu)
204 struct property *prop;
206 int type, tmp, doint = 2;
210 if (!menu_is_visible(menu))
216 if (prop && menu != current_menu) {
217 const char *prompt = menu_get_prompt(menu);
218 switch (prop->type) {
222 cprint_tag("m%p", menu);
224 if (single_menu_mode) {
225 cprint_name("%s%*c%s",
226 menu->data ? "-->" : "++>",
227 indent + 1, ' ', prompt);
229 if (menu->parent != &rootmenu)
230 cprint_name(" %*c", indent + 1, ' ');
231 cprint_name("%s --->", prompt);
234 if (single_menu_mode && menu->data)
241 cprint_tag(":%p", menu);
242 cprint_name("---%*c%s", indent + 1, ' ', prompt);
251 type = sym_get_type(sym);
252 if (sym_is_choice(sym)) {
253 struct symbol *def_sym = sym_get_choice_value(sym);
254 struct menu *def_menu = NULL;
257 for (child = menu->list; child; child = child->next) {
258 if (menu_is_visible(child) && child->sym == def_sym)
262 val = sym_get_tristate_value(sym);
263 if (sym_is_changable(sym)) {
264 cprint_tag("t%p", menu);
267 cprint_name("[%c]", val == no ? ' ' : '*');
271 case yes: ch = '*'; break;
272 case mod: ch = 'M'; break;
273 default: ch = ' '; break;
275 cprint_name("<%c>", ch);
279 cprint_tag("%c%p", def_menu ? 't' : ':', menu);
283 cprint_name("%*c%s", indent + 1, ' ', menu_get_prompt(menu));
286 cprint_name(" (%s)", menu_get_prompt(def_menu));
287 cprint_name(" --->");
288 if (def_menu->list) {
290 build_conf(def_menu);
298 val = sym_get_tristate_value(sym);
299 if (sym_is_choice_value(sym) && val == yes) {
300 cprint_tag(":%p", menu);
305 cprint_tag("t%p", menu);
306 cprint_name("[%c]", val == no ? ' ' : '*');
309 cprint_tag("t%p", menu);
311 case yes: ch = '*'; break;
312 case mod: ch = 'M'; break;
313 default: ch = ' '; break;
315 cprint_name("<%c>", ch);
318 cprint_tag("s%p", menu);
319 tmp = cprint_name("(%s)", sym_get_string_value(sym));
320 tmp = indent - tmp + 4;
323 cprint_name("%*c%s%s", tmp, ' ', menu_get_prompt(menu),
324 sym_has_value(sym) ? "" : " (NEW)");
328 cprint_name("%*c%s%s", indent + 1, ' ', menu_get_prompt(menu),
329 sym_has_value(sym) ? "" : " (NEW)");
334 for (child = menu->list; child; child = child->next)
339 static void conf(struct menu *menu)
341 struct dialog_list_item *active_item = NULL;
342 struct menu *submenu;
343 const char *prompt = menu_get_prompt(menu);
345 char active_entry[40];
348 unlink("lxdialog.scrltmp");
358 if (menu == &rootmenu) {
359 cmake(); cprint_tag(":"); cprint_name("--- ");
360 cmake(); cprint_tag("L"); cprint_name("Load an Alternate Configuration File");
361 cmake(); cprint_tag("S"); cprint_name("Save Configuration to an Alternate File");
364 stat = dialog_menu(prompt ? prompt : "Main Menu",
365 menu_instructions, rows, cols, rows - 10,
366 active_entry, item_no, items);
370 if (stat == 1 || stat == 255)
373 active_item = first_sel_item(item_no, items);
376 active_item->selected = 0;
377 strncpy(active_entry, active_item->tag, sizeof(active_entry));
378 active_entry[sizeof(active_entry)-1] = 0;
379 type = active_entry[0];
385 if (sscanf(active_entry + 1, "%p", &submenu) == 1)
392 if (single_menu_mode)
393 submenu->data = (submenu->data)? NULL : (void *)1;
398 if (sym_is_choice(sym) && sym_get_tristate_value(sym) == yes)
399 conf_choice(submenu);
402 conf_string(submenu);
420 if (sym_set_tristate_value(sym, yes))
422 if (sym_set_tristate_value(sym, mod))
423 show_textbox(NULL, setmod_text, 6, 74);
428 sym_set_tristate_value(sym, no);
432 sym_set_tristate_value(sym, mod);
436 sym_toggle_tristate_value(sym);
437 else if (type == 'm')
444 static void show_textbox(const char *title, const char *text, int r, int c)
448 fd = creat(".help.tmp", 0777);
449 write(fd, text, strlen(text));
451 while (dialog_textbox(title, ".help.tmp", r, c) < 0)
456 static void show_helptext(const char *title, const char *text)
458 show_textbox(title, text, rows, cols);
461 static void show_help(struct menu *menu)
465 struct symbol *sym = menu->sym;
471 helptext = malloc(strlen(sym->name) + strlen(help) + 16);
472 sprintf(helptext, "%s:\n\n%s", sym->name, help);
473 show_helptext(menu_get_prompt(menu), helptext);
476 show_helptext(menu_get_prompt(menu), help);
479 static void show_readme(void)
481 show_helptext("Help", top_menu_help);
484 static void conf_choice(struct menu *menu)
486 const char *prompt = menu_get_prompt(menu);
488 struct symbol *active;
492 active = sym_get_choice_value(menu->sym);
494 for (child = menu->list; child; child = child->next) {
495 if (!menu_is_visible(child))
498 cprint_tag("%p", child);
499 cprint_name("%s", menu_get_prompt(child));
500 items[item_no - 1]->selected = (child->sym == active);
503 switch (dialog_checklist(prompt ? prompt : "Main Menu",
504 radiolist_instructions, 15, 70, 6,
505 item_no, items, FLAG_RADIO)) {
507 if (sscanf(first_sel_item(item_no, items)->tag, "%p", &menu) != 1)
509 sym_set_tristate_value(menu->sym, yes);
520 static void conf_string(struct menu *menu)
522 const char *prompt = menu_get_prompt(menu);
527 switch (sym_get_type(menu->sym)) {
529 heading = (char *) inputbox_instructions_int;
532 heading = (char *) inputbox_instructions_hex;
535 heading = (char *) inputbox_instructions_string;
538 heading = "Internal mconf error!";
542 switch (dialog_inputbox(prompt ? prompt : "Main Menu",
544 sym_get_string_value(menu->sym))) {
546 if (sym_set_string_value(menu->sym, dialog_input_result))
548 show_textbox(NULL, "You have made an invalid entry.", 5, 43);
559 static void conf_load(void)
562 switch (dialog_inputbox(NULL, load_config_text, 11, 55,
565 if (!dialog_input_result[0])
567 if (!conf_read(dialog_input_result))
569 show_textbox(NULL, "File does not exist!", 5, 38);
572 show_helptext("Load Alternate Configuration", load_config_help);
580 static void conf_save(void)
583 switch (dialog_inputbox(NULL, save_config_text, 11, 55,
586 if (!dialog_input_result[0])
588 if (!conf_write(dialog_input_result))
590 show_textbox(NULL, "Can't create file! Probably a nonexistent directory.", 5, 60);
593 show_helptext("Save Alternate Configuration", save_config_help);
601 static void conf_cleanup(void)
603 tcsetattr(1, TCSAFLUSH, &ios_org);
605 unlink("lxdialog.scrltmp");
608 static void winch_handler(int sig)
612 if (ioctl(1, TIOCGWINSZ, &ws) == -1) {
620 if (rows < 19 || cols < 80) {
622 fprintf(stderr, "Your display is too small to run Menuconfig!\n");
623 fprintf(stderr, "It must be at least 19 lines by 80 columns.\n");
632 int main(int ac, char **av)
641 backtitle = malloc(128);
642 sym = sym_lookup("VERSION", 0);
644 snprintf(backtitle, 128, "BusyBox v%s Configuration",
645 sym_get_string_value(sym));
647 mode = getenv("MENUCONFIG_MODE");
649 if (!strcasecmp(mode, "single_menu"))
650 single_menu_mode = 1;
653 tcgetattr(1, &ios_org);
654 atexit(conf_cleanup);
657 signal(SIGWINCH, winch_handler);
661 /* Restart dialog to act more like when lxdialog was still separate */
664 stat = dialog_yesno(NULL,
665 "Do you wish to save your new BusyBox configuration?", 5, 60);
672 "*** End of BusyBox configuration.\n"
673 "*** Check the top-level Makefile for additional configuration options.\n\n");
675 printf("\n\nYour BusyBox configuration changes were NOT saved.\n\n");