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>
26 #include "lxdialog/dialog.h"
28 #define LKC_DIRECT_LINK
31 static char menu_backtitle[128];
32 static const char mconf_readme[] =
35 "Some features may be built directly into axTLS. Some features\n"
36 "may be completely removed altogether. There are also certain\n"
37 "parameters which are not really features, but must be\n"
38 "entered in as decimal or hexadecimal numbers or possibly text.\n"
40 "Menu items beginning with [*] or [ ] represent features\n"
41 "configured to be built in or removed respectively.\n"
43 "To change any of these features, highlight it with the cursor\n"
44 "keys and press <Y> to build it in or <N> to removed it.\n"
45 "You may also press the <Space Bar> to cycle\n"
46 "through the available options (ie. Y->N->Y).\n"
48 "Some additional keyboard hints:\n"
52 "o Use the Up/Down arrow keys (cursor keys) to highlight the item\n"
53 " you wish to change or submenu wish to select and press <Enter>.\n"
54 " Submenus are designated by \"--->\".\n"
56 " Shortcut: Press the option's highlighted letter (hotkey).\n"
57 " Pressing a hotkey more than once will sequence\n"
58 " through all visible items which use that hotkey.\n"
60 " You may also use the <PAGE UP> and <PAGE DOWN> keys to scroll\n"
61 " unseen options into view.\n"
63 "o To exit a menu use the cursor keys to highlight the <Exit> button\n"
64 " and press <ENTER>.\n"
66 " Shortcut: Press <ESC><ESC> or <E> or <X> if there is no hotkey\n"
67 " using those letters. You may press a single <ESC>, but\n"
68 " there is a delayed response which you may find annoying.\n"
70 " Also, the <TAB> and cursor keys will cycle between <Select>,\n"
71 " <Exit> and <Help>\n"
73 "o To get help with an item, use the cursor keys to highlight <Help>\n"
74 " and Press <ENTER>.\n"
76 " Shortcut: Press <H> or <?>.\n"
79 "Radiolists (Choice lists)\n"
81 "o Use the cursor keys to select the option you wish to set and press\n"
82 " <S> or the <SPACE BAR>.\n"
84 " Shortcut: Press the first letter of the option you wish to set then\n"
85 " press <S> or <SPACE BAR>.\n"
87 "o To see available help for the item, use the cursor keys to highlight\n"
88 " <Help> and Press <ENTER>.\n"
90 " Shortcut: Press <H> or <?>.\n"
92 " Also, the <TAB> and cursor keys will cycle between <Select> and\n"
98 "o Enter the requested information and press <ENTER>\n"
99 " If you are entering hexadecimal values, it is not necessary to\n"
100 " add the '0x' prefix to the entry.\n"
102 "o For help, use the <TAB> or cursor keys to highlight the help option\n"
103 " and press <ENTER>. You can try <TAB><H> as well.\n"
106 "Text Box (Help Window)\n"
108 "o Use the cursor keys to scroll up/down/left/right. The VI editor\n"
109 " keys h,j,k,l function here as do <SPACE BAR> and <B> for those\n"
110 " who are familiar with less and lynx.\n"
112 "o Press <E>, <X>, <Enter> or <Esc><Esc> to exit.\n"
115 "Alternate Configuration Files\n"
116 "-----------------------------\n"
117 "Menuconfig supports the use of alternate configuration files for\n"
118 "those who, for various reasons, find it necessary to switch\n"
119 "between different configurations.\n"
121 "At the end of the main menu you will find two options. One is\n"
122 "for saving the current configuration to a file of your choosing.\n"
123 "The other option is for loading a previously saved alternate\n"
126 "Even if you don't use alternate configuration files, but you\n"
127 "find during a Menuconfig session that you have completely messed\n"
128 "up your settings, you may use the \"Load Alternate...\" option to\n"
129 "restore your previously saved settings from \".config\" without\n"
130 "restarting Menuconfig.\n"
132 "Other information\n"
133 "-----------------\n"
134 "If you use Menuconfig in an XTERM window make sure you have your\n"
135 "$TERM variable set to point to a xterm definition which supports color.\n"
136 "Otherwise, Menuconfig will look rather bad. Menuconfig will not\n"
137 "display correctly in a RXVT window because rxvt displays only one\n"
138 "intensity of color, bright.\n"
140 "Menuconfig will display larger menus on screens or xterms which are\n"
141 "set to display more than the standard 25 row by 80 column geometry.\n"
142 "In order for this to work, the \"stty size\" command must be able to\n"
143 "display the screen's current row and column geometry. I STRONGLY\n"
144 "RECOMMEND that you make sure you do NOT have the shell variables\n"
145 "LINES and COLUMNS exported into your environment. Some distributions\n"
146 "export those variables via /etc/profile. Some ncurses programs can\n"
147 "become confused when those variables (LINES & COLUMNS) don't reflect\n"
148 "the true screen size.\n"
150 "Optional personality available\n"
151 "------------------------------\n"
152 "If you prefer to have all of the options listed in a single\n"
153 "menu, rather than the default multimenu hierarchy, run the menuconfig\n"
154 "with MENUCONFIG_MODE environment variable set to single_menu. Example:\n"
156 "make MENUCONFIG_MODE=single_menu menuconfig\n"
158 "<Enter> will then unroll the appropriate category, or enfold it if it\n"
159 "is already unrolled.\n"
161 "Note that this mode can eventually be a little more CPU expensive\n"
162 "(especially with a larger number of unrolled categories) than the\n"
164 menu_instructions[] =
165 "Arrow keys navigate the menu. "
166 "<Enter> selects submenus --->. "
167 "Highlighted letters are hotkeys. "
168 "Pressing <Y> selectes a feature, while <N> will exclude a feature. "
169 "Press <Esc><Esc> to exit, <?> for Help, </> for Search. "
170 "Legend: [*] feature is selected [ ] feature is excluded",
171 radiolist_instructions[] =
172 "Use the arrow keys to navigate this window or "
173 "press the hotkey of the item you wish to select "
174 "followed by the <SPACE BAR>. "
175 "Press <?> for additional information about this option.",
176 inputbox_instructions_int[] =
177 "Please enter a decimal value. "
178 "Fractions will not be accepted. "
179 "Use the <TAB> key to move from the input field to the buttons below it.",
180 inputbox_instructions_hex[] =
181 "Please enter a hexadecimal value. "
182 "Use the <TAB> key to move from the input field to the buttons below it.",
183 inputbox_instructions_string[] =
184 "Please enter a string value. "
185 "Use the <TAB> key to move from the input field to the buttons below it.",
187 "This feature depends on another which has been configured as a module.\n"
188 "As a result, this feature will be built as a module.",
190 "There is no help available for this option.\n",
192 "Enter the name of the configuration file you wish to load. "
193 "Accept the name shown to restore the configuration you "
194 "last retrieved. Leave blank to abort.",
197 "For various reasons, one may wish to keep several different axTLS\n"
198 "configurations available on a single machine.\n"
200 "If you have saved a previous configuration in a file other than the\n"
201 "axTLS's default, entering the name of the file here will allow you\n"
202 "to modify that configuration.\n"
204 "If you are uncertain, then you have probably never used alternate\n"
205 "configuration files. You should therefor leave this blank to abort.\n",
207 "Enter a filename to which this configuration should be saved "
208 "as an alternate. Leave blank to abort.",
211 "For various reasons, one may wish to keep different axTLS\n"
212 "configurations available on a single machine.\n"
214 "Entering a file name here will allow you to later retrieve, modify\n"
215 "and use the current configuration as an alternate to whatever\n"
216 "configuration options you have selected at that time.\n"
218 "If you are uncertain what all this means then you should probably\n"
219 "leave this blank.\n",
222 "Search for CONFIG_ symbols and display their relations.\n"
223 "Example: search for \"^FOO\"\n"
225 "-----------------------------------------------------------------\n"
227 "Prompt: Foo bus is used to drive the bar HW\n"
228 "Defined at drivers/pci/Kconfig:47\n"
229 "Depends on: X86_LOCAL_APIC && X86_IO_APIC || IA64\n"
231 " -> Bus options (PCI, PCMCIA, EISA, MCA, ISA)\n"
232 " -> PCI support (PCI [=y])\n"
233 " -> PCI access mode (<choice> [=y])\n"
234 "Selects: LIBCRC32\n"
236 "-----------------------------------------------------------------\n"
237 "o The line 'Prompt:' shows the text used in the menu structure for\n"
238 " this CONFIG_ symbol\n"
239 "o The 'Defined at' line tell at what file / line number the symbol\n"
241 "o The 'Depends on:' line tell what symbols needs to be defined for\n"
242 " this symbol to be visible in the menu (selectable)\n"
243 "o The 'Location:' lines tell where in the menu structure this symbol\n"
245 " A location followed by a [=y] indicate that this is a selectable\n"
246 " menu item - and current value is displayed inside brackets.\n"
247 "o The 'Selects:' line tell what symbol will be automatically\n"
248 " selected if this symbol is selected (y or m)\n"
249 "o The 'Selected by' line tell what symbol has selected this symbol\n"
251 "Only relevant lines are shown.\n"
254 "Examples: USB => find all CONFIG_ symbols containing USB\n"
255 " ^USB => find all CONFIG_ symbols starting with USB\n"
256 " USB$ => find all CONFIG_ symbols ending with USB\n"
259 static char filename[PATH_MAX+1] = ".config";
261 static struct termios ios_org;
262 static int rows = 0, cols = 0;
263 static struct menu *current_menu;
264 static int child_count;
265 static int single_menu_mode;
267 static struct dialog_list_item *items[16384]; /* FIXME: This ought to be dynamic. */
270 static void conf(struct menu *menu);
271 static void conf_choice(struct menu *menu);
272 static void conf_string(struct menu *menu);
273 static void conf_load(void);
274 static void conf_save(void);
275 static void show_textbox(const char *title, const char *text, int r, int c);
276 static void show_helptext(const char *title, const char *text);
277 static void show_help(struct menu *menu);
278 static void show_file(const char *filename, const char *title, int r, int c);
280 static void init_wsize(void)
285 if (!ioctl(STDIN_FILENO, TIOCGWINSZ, &ws)) {
291 env = getenv("LINES");
298 env = getenv("COLUMNS");
305 if (rows < 19 || cols < 80) {
306 fprintf(stderr, "Your display is too small to run Menuconfig!\n");
307 fprintf(stderr, "It must be at least 19 lines by 80 columns.\n");
315 static void cinit(void)
320 static void cmake(void)
322 items[item_no] = malloc(sizeof(struct dialog_list_item));
323 memset(items[item_no], 0, sizeof(struct dialog_list_item));
324 items[item_no]->tag = malloc(32); items[item_no]->tag[0] = 0;
325 items[item_no]->name = malloc(512); items[item_no]->name[0] = 0;
326 items[item_no]->namelen = 0;
330 static int cprint_name(const char *fmt, ...)
338 res = vsnprintf(items[item_no - 1]->name + items[item_no - 1]->namelen,
339 512 - items[item_no - 1]->namelen, fmt, ap);
341 items[item_no - 1]->namelen += res;
347 static int cprint_tag(const char *fmt, ...)
355 res = vsnprintf(items[item_no - 1]->tag, 32, fmt, ap);
361 static void cdone(void)
365 for (i = 0; i < item_no; i++) {
367 free(items[i]->name);
374 static void get_prompt_str(struct gstr *r, struct property *prop)
377 struct menu *submenu[8], *menu;
379 str_printf(r, "Prompt: %s\n", prop->text);
380 str_printf(r, " Defined at %s:%d\n", prop->menu->file->name,
382 if (!expr_is_yes(prop->visible.expr)) {
383 str_append(r, " Depends on: ");
384 expr_gstr_print(prop->visible.expr, r);
387 menu = prop->menu->parent;
388 for (i = 0; menu != &rootmenu && i < 8; menu = menu->parent)
391 str_printf(r, " Location:\n");
392 for (j = 4; --i >= 0; j += 2) {
394 str_printf(r, "%*c-> %s", j, ' ', menu_get_prompt(menu));
396 str_printf(r, " (%s [=%s])", menu->sym->name ?
397 menu->sym->name : "<choice>",
398 sym_get_string_value(menu->sym));
405 static void get_symbol_str(struct gstr *r, struct symbol *sym)
408 struct property *prop;
410 str_printf(r, "Symbol: %s [=%s]\n", sym->name,
411 sym_get_string_value(sym));
412 for_all_prompts(sym, prop)
413 get_prompt_str(r, prop);
415 for_all_properties(sym, prop, P_SELECT) {
417 str_append(r, " Selects: ");
420 str_printf(r, " && ");
421 expr_gstr_print(prop->expr, r);
425 if (sym->rev_dep.expr) {
426 str_append(r, " Selected by: ");
427 expr_gstr_print(sym->rev_dep.expr, r);
430 str_append(r, "\n\n");
433 static struct gstr get_relations_str(struct symbol **sym_arr)
436 struct gstr res = str_new();
439 for (i = 0; sym_arr && (sym = sym_arr[i]); i++)
440 get_symbol_str(&res, sym);
442 str_append(&res, "No matches found.\n");
446 static void search_conf(void)
448 struct symbol **sym_arr;
452 switch (dialog_inputbox("Search Configuration Parameter",
453 "Enter Keyword", 10, 75,
458 show_helptext("Search Configuration", search_help);
464 sym_arr = sym_re_search(dialog_input_result);
465 res = get_relations_str(sym_arr);
467 show_textbox("Search Results", str_get(&res), 0, 0);
471 static void build_conf(struct menu *menu)
474 struct property *prop;
476 int type, tmp, doint = 2;
480 if (!menu_is_visible(menu))
486 if (prop && menu != current_menu) {
487 const char *prompt = menu_get_prompt(menu);
488 switch (prop->type) {
492 cprint_tag("m%p", menu);
494 if (single_menu_mode) {
495 cprint_name("%s%*c%s",
496 menu->data ? "-->" : "++>",
497 indent + 1, ' ', prompt);
499 cprint_name(" %*c%s --->", indent + 1, ' ', prompt);
502 if (single_menu_mode && menu->data)
509 cprint_tag(":%p", menu);
510 cprint_name("---%*c%s", indent + 1, ' ', prompt);
519 type = sym_get_type(sym);
520 if (sym_is_choice(sym)) {
521 struct symbol *def_sym = sym_get_choice_value(sym);
522 struct menu *def_menu = NULL;
525 for (child = menu->list; child; child = child->next) {
526 if (menu_is_visible(child) && child->sym == def_sym)
530 val = sym_get_tristate_value(sym);
531 if (sym_is_changable(sym)) {
532 cprint_tag("t%p", menu);
535 cprint_name("[%c]", val == no ? ' ' : '*');
539 case yes: ch = '*'; break;
540 case mod: ch = 'M'; break;
541 default: ch = ' '; break;
543 cprint_name("<%c>", ch);
547 cprint_tag("%c%p", def_menu ? 't' : ':', menu);
551 cprint_name("%*c%s", indent + 1, ' ', menu_get_prompt(menu));
554 cprint_name(" (%s)", menu_get_prompt(def_menu));
555 cprint_name(" --->");
556 if (def_menu->list) {
558 build_conf(def_menu);
565 if (menu == current_menu) {
566 cprint_tag(":%p", menu);
567 cprint_name("---%*c%s", indent + 1, ' ', menu_get_prompt(menu));
571 val = sym_get_tristate_value(sym);
572 if (sym_is_choice_value(sym) && val == yes) {
573 cprint_tag(":%p", menu);
578 cprint_tag("t%p", menu);
579 if (sym_is_changable(sym))
580 cprint_name("[%c]", val == no ? ' ' : '*');
585 cprint_tag("t%p", menu);
587 case yes: ch = '*'; break;
588 case mod: ch = 'M'; break;
589 default: ch = ' '; break;
591 if (sym_is_changable(sym))
592 cprint_name("<%c>", ch);
597 cprint_tag("s%p", menu);
598 tmp = cprint_name("(%s)", sym_get_string_value(sym));
599 tmp = indent - tmp + 4;
602 cprint_name("%*c%s%s", tmp, ' ', menu_get_prompt(menu),
603 (sym_has_value(sym) || !sym_is_changable(sym)) ?
608 cprint_name("%*c%s%s", indent + 1, ' ', menu_get_prompt(menu),
609 (sym_has_value(sym) || !sym_is_changable(sym)) ?
611 if (menu->prompt->type == P_MENU) {
612 cprint_name(" --->");
619 for (child = menu->list; child; child = child->next)
624 static void conf(struct menu *menu)
626 struct dialog_list_item *active_item = NULL;
627 struct menu *submenu;
628 const char *prompt = menu_get_prompt(menu);
630 char active_entry[40];
633 unlink("lxdialog.scrltmp");
643 if (menu == &rootmenu) {
644 cmake(); cprint_tag(":"); cprint_name("--- ");
645 cmake(); cprint_tag("L"); cprint_name("Load an Alternate Configuration File");
646 cmake(); cprint_tag("S"); cprint_name("Save Configuration to an Alternate File");
649 stat = dialog_menu(prompt ? prompt : "Main Menu",
650 menu_instructions, rows, cols, rows - 10,
651 active_entry, item_no, items);
655 if (stat == 1 || stat == 255)
658 active_item = first_sel_item(item_no, items);
661 active_item->selected = 0;
662 strncpy(active_entry, active_item->tag, sizeof(active_entry));
663 active_entry[sizeof(active_entry)-1] = 0;
664 type = active_entry[0];
670 if (sscanf(active_entry + 1, "%p", &submenu) == 1)
677 if (single_menu_mode)
678 submenu->data = (void *) (long) !submenu->data;
683 if (sym_is_choice(sym) && sym_get_tristate_value(sym) == yes)
684 conf_choice(submenu);
685 else if (submenu->prompt->type == P_MENU)
689 conf_string(submenu);
703 show_helptext("README", mconf_readme);
707 if (sym_set_tristate_value(sym, yes))
709 if (sym_set_tristate_value(sym, mod))
710 show_textbox(NULL, setmod_text, 6, 74);
715 sym_set_tristate_value(sym, no);
719 sym_set_tristate_value(sym, mod);
723 sym_toggle_tristate_value(sym);
724 else if (type == 'm')
734 static void show_textbox(const char *title, const char *text, int r, int c)
738 fd = creat(".help.tmp", 0777);
739 write(fd, text, strlen(text));
741 show_file(".help.tmp", title, r, c);
745 static void show_helptext(const char *title, const char *text)
747 show_textbox(title, text, 0, 0);
750 static void show_help(struct menu *menu)
752 struct gstr help = str_new();
753 struct symbol *sym = menu->sym;
758 str_printf(&help, "%s:\n\n", sym->name);
759 str_append(&help, sym->help);
760 str_append(&help, "\n");
763 str_append(&help, nohelp_text);
765 get_symbol_str(&help, sym);
766 show_helptext(menu_get_prompt(menu), str_get(&help));
770 static void show_file(const char *filename, const char *title, int r, int c)
772 while (dialog_textbox(title, filename, r ? r : rows, c ? c : cols) < 0)
776 static void conf_choice(struct menu *menu)
778 const char *prompt = menu_get_prompt(menu);
780 struct symbol *active;
782 active = sym_get_choice_value(menu->sym);
786 for (child = menu->list; child; child = child->next) {
787 if (!menu_is_visible(child))
790 cprint_tag("%p", child);
791 cprint_name("%s", menu_get_prompt(child));
792 if (child->sym == sym_get_choice_value(menu->sym))
793 items[item_no - 1]->selected = 1; /* ON */
794 else if (child->sym == active)
795 items[item_no - 1]->selected = 2; /* SELECTED */
797 items[item_no - 1]->selected = 0; /* OFF */
800 switch (dialog_checklist(prompt ? prompt : "Main Menu",
801 radiolist_instructions, 15, 70, 6,
802 item_no, items, FLAG_RADIO)) {
804 if (sscanf(first_sel_item(item_no, items)->tag, "%p", &child) != 1)
806 sym_set_tristate_value(child->sym, yes);
809 if (sscanf(first_sel_item(item_no, items)->tag, "%p", &child) == 1) {
821 static void conf_string(struct menu *menu)
823 const char *prompt = menu_get_prompt(menu);
828 switch (sym_get_type(menu->sym)) {
830 heading = (char *) inputbox_instructions_int;
833 heading = (char *) inputbox_instructions_hex;
836 heading = (char *) inputbox_instructions_string;
839 heading = "Internal mconf error!";
843 switch (dialog_inputbox(prompt ? prompt : "Main Menu",
845 sym_get_string_value(menu->sym))) {
847 if (sym_set_string_value(menu->sym, dialog_input_result))
849 show_textbox(NULL, "You have made an invalid entry.", 5, 43);
860 static void conf_load(void)
863 switch (dialog_inputbox(NULL, load_config_text, 11, 55,
866 if (!dialog_input_result[0])
868 if (!conf_read(dialog_input_result))
870 show_textbox(NULL, "File does not exist!", 5, 38);
873 show_helptext("Load Alternate Configuration", load_config_help);
881 static void conf_save(void)
884 switch (dialog_inputbox(NULL, save_config_text, 11, 55,
887 if (!dialog_input_result[0])
889 if (!conf_write(dialog_input_result))
891 show_textbox(NULL, "Can't create file! Probably a nonexistent directory.", 5, 60);
894 show_helptext("Save Alternate Configuration", save_config_help);
902 static void conf_cleanup(void)
904 tcsetattr(1, TCSAFLUSH, &ios_org);
908 static void winch_handler(int sig)
912 if (ioctl(1, TIOCGWINSZ, &ws) == -1) {
920 if (rows < 19 || cols < 80) {
922 fprintf(stderr, "Your display is too small to run Menuconfig!\n");
923 fprintf(stderr, "It must be at least 19 lines by 80 columns.\n");
932 int main(int ac, char **av)
941 sym = sym_lookup("VERSION", 0);
943 snprintf(menu_backtitle, 128, "axTLS v%s Configuration",
944 sym_get_string_value(sym));
946 mode = getenv("MENUCONFIG_MODE");
948 if (!strcasecmp(mode, "single_menu"))
949 single_menu_mode = 1;
952 tcgetattr(1, &ios_org);
953 atexit(conf_cleanup);
956 signal(SIGWINCH, winch_handler);
960 /* Restart dialog to act more like when lxdialog was still separate */
963 stat = dialog_yesno(NULL,
964 "Do you wish to save your new axTLS configuration?", 5, 60);
971 "*** End of axTLS configuration.\n"
972 "*** Check the top-level Makefile for additional configuration options.\n\n");
974 printf("\n\nYour axTLS configuration changes were NOT saved.\n\n");