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\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 "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\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"
89 static char filename
[PATH_MAX
+1] = ".config";
90 static int indent
= 0;
91 static struct termios ios_org
;
92 static int rows
, cols
;
93 struct menu
*current_menu
;
94 static int child_count
;
95 static int single_menu_mode
;
97 static struct dialog_list_item
*items
[16384];
100 static void conf(struct menu
*menu
);
101 static void conf_choice(struct menu
*menu
);
102 static void conf_string(struct menu
*menu
);
103 static void conf_load(void);
104 static void conf_save(void);
105 static void show_textbox(const char *title
, const char *text
, int r
, int c
);
106 static void show_helptext(const char *title
, const char *text
);
107 static void show_help(struct menu
*menu
);
108 static void show_readme(void);
110 static void init_wsize(void)
114 if (ioctl(1, TIOCGWINSZ
, &ws
) == -1) {
122 if (rows
< 19 || cols
< 80) {
123 fprintf(stderr
, "Your display is too small to run Menuconfig!\n");
124 fprintf(stderr
, "It must be at least 19 lines by 80 columns.\n");
132 static void cinit(void)
137 static void cmake(void)
139 items
[item_no
] = malloc(sizeof(struct dialog_list_item
));
140 memset(items
[item_no
], 0, sizeof(struct dialog_list_item
));
141 items
[item_no
]->tag
= malloc(32); items
[item_no
]->tag
[0] = 0;
142 items
[item_no
]->name
= malloc(512); items
[item_no
]->name
[0] = 0;
143 items
[item_no
]->namelen
= 0;
147 static int cprint_name(const char *fmt
, ...)
155 res
= vsnprintf(items
[item_no
- 1]->name
+ items
[item_no
- 1]->namelen
,
156 512 - items
[item_no
- 1]->namelen
, fmt
, ap
);
158 items
[item_no
- 1]->namelen
+= res
;
164 static int cprint_tag(const char *fmt
, ...)
172 res
= vsnprintf(items
[item_no
- 1]->tag
, 32, fmt
, ap
);
178 static void cdone(void)
182 for (i
= 0; i
< item_no
; i
++) {
184 free(items
[i
]->name
);
191 static void build_conf(struct menu
*menu
)
194 struct property
*prop
;
196 int type
, tmp
, doint
= 2;
200 if (!menu_is_visible(menu
))
206 if (prop
&& menu
!= current_menu
) {
207 const char *prompt
= menu_get_prompt(menu
);
208 switch (prop
->type
) {
212 cprint_tag("m%p", menu
);
214 if (single_menu_mode
) {
215 cprint_name("%s%*c%s",
216 menu
->data
? "-->" : "++>",
217 indent
+ 1, ' ', prompt
);
219 if (menu
->parent
!= &rootmenu
)
220 cprint_name(" %*c", indent
+ 1, ' ');
221 cprint_name("%s --->", prompt
);
224 if (single_menu_mode
&& menu
->data
)
231 cprint_tag(":%p", menu
);
232 cprint_name("---%*c%s", indent
+ 1, ' ', prompt
);
241 type
= sym_get_type(sym
);
242 if (sym_is_choice(sym
)) {
243 struct symbol
*def_sym
= sym_get_choice_value(sym
);
244 struct menu
*def_menu
= NULL
;
247 for (child
= menu
->list
; child
; child
= child
->next
) {
248 if (menu_is_visible(child
) && child
->sym
== def_sym
)
252 val
= sym_get_tristate_value(sym
);
253 if (sym_is_changable(sym
)) {
254 cprint_tag("t%p", menu
);
257 cprint_name("[%c]", val
== no
? ' ' : '*');
261 case yes
: ch
= '*'; break;
262 case mod
: ch
= 'M'; break;
263 default: ch
= ' '; break;
265 cprint_name("<%c>", ch
);
269 cprint_tag("%c%p", def_menu
? 't' : ':', menu
);
273 cprint_name("%*c%s", indent
+ 1, ' ', menu_get_prompt(menu
));
276 cprint_name(" (%s)", menu_get_prompt(def_menu
));
277 cprint_name(" --->");
278 if (def_menu
->list
) {
280 build_conf(def_menu
);
288 val
= sym_get_tristate_value(sym
);
289 if (sym_is_choice_value(sym
) && val
== yes
) {
290 cprint_tag(":%p", menu
);
295 cprint_tag("t%p", menu
);
296 cprint_name("[%c]", val
== no
? ' ' : '*');
299 cprint_tag("t%p", menu
);
301 case yes
: ch
= '*'; break;
302 case mod
: ch
= 'M'; break;
303 default: ch
= ' '; break;
305 cprint_name("<%c>", ch
);
308 cprint_tag("s%p", menu
);
309 tmp
= cprint_name("(%s)", sym_get_string_value(sym
));
310 tmp
= indent
- tmp
+ 4;
313 cprint_name("%*c%s%s", tmp
, ' ', menu_get_prompt(menu
),
314 sym_has_value(sym
) ? "" : " (NEW)");
318 cprint_name("%*c%s%s", indent
+ 1, ' ', menu_get_prompt(menu
),
319 sym_has_value(sym
) ? "" : " (NEW)");
324 for (child
= menu
->list
; child
; child
= child
->next
)
329 static void conf(struct menu
*menu
)
331 struct dialog_list_item
*active_item
= NULL
;
332 struct menu
*submenu
;
333 const char *prompt
= menu_get_prompt(menu
);
335 char active_entry
[40];
338 unlink("lxdialog.scrltmp");
348 if (menu
== &rootmenu
) {
349 cmake(); cprint_tag(":"); cprint_name("--- ");
350 cmake(); cprint_tag("L"); cprint_name("Load an Alternate Configuration File");
351 cmake(); cprint_tag("S"); cprint_name("Save Configuration to an Alternate File");
354 stat
= dialog_menu(prompt
? prompt
: "Main Menu",
355 menu_instructions
, rows
, cols
, rows
- 10,
356 active_entry
, item_no
, items
);
360 if (stat
== 1 || stat
== 255)
363 active_item
= first_sel_item(item_no
, items
);
366 active_item
->selected
= 0;
367 strncpy(active_entry
, active_item
->tag
, sizeof(active_entry
));
368 active_entry
[sizeof(active_entry
)-1] = 0;
369 type
= active_entry
[0];
375 if (sscanf(active_entry
+ 1, "%p", &submenu
) == 1)
382 if (single_menu_mode
)
383 submenu
->data
= (submenu
->data
)? NULL
: (void *)1;
388 if (sym_is_choice(sym
) && sym_get_tristate_value(sym
) == yes
)
389 conf_choice(submenu
);
392 conf_string(submenu
);
410 if (sym_set_tristate_value(sym
, yes
))
412 if (sym_set_tristate_value(sym
, mod
))
413 show_textbox(NULL
, setmod_text
, 6, 74);
418 sym_set_tristate_value(sym
, no
);
422 sym_set_tristate_value(sym
, mod
);
426 sym_toggle_tristate_value(sym
);
427 else if (type
== 'm')
434 static void show_textbox(const char *title
, const char *text
, int r
, int c
)
438 fd
= creat(".help.tmp", 0777);
439 write(fd
, text
, strlen(text
));
441 while (dialog_textbox(title
, ".help.tmp", r
, c
) < 0)
446 static void show_helptext(const char *title
, const char *text
)
448 show_textbox(title
, text
, rows
, cols
);
451 static void show_help(struct menu
*menu
)
455 struct symbol
*sym
= menu
->sym
;
461 helptext
= malloc(strlen(sym
->name
) + strlen(help
) + 16);
462 sprintf(helptext
, "%s:\n\n%s", sym
->name
, help
);
463 show_helptext(menu_get_prompt(menu
), helptext
);
466 show_helptext(menu_get_prompt(menu
), help
);
469 static void show_readme(void)
471 while (dialog_textbox(NULL
, "config/README.Menuconfig", rows
, cols
) < 0)
475 static void conf_choice(struct menu
*menu
)
477 const char *prompt
= menu_get_prompt(menu
);
479 struct symbol
*active
;
483 active
= sym_get_choice_value(menu
->sym
);
485 for (child
= menu
->list
; child
; child
= child
->next
) {
486 if (!menu_is_visible(child
))
489 cprint_tag("%p", child
);
490 cprint_name("%s", menu_get_prompt(child
));
491 items
[item_no
- 1]->selected
= (child
->sym
== active
);
494 switch (dialog_checklist(prompt
? prompt
: "Main Menu",
495 radiolist_instructions
, 15, 70, 6,
496 item_no
, items
, FLAG_RADIO
)) {
498 if (sscanf(first_sel_item(item_no
, items
)->tag
, "%p", &menu
) != 1)
500 sym_set_tristate_value(menu
->sym
, yes
);
511 static void conf_string(struct menu
*menu
)
513 const char *prompt
= menu_get_prompt(menu
);
518 switch (sym_get_type(menu
->sym
)) {
520 heading
= (char *) inputbox_instructions_int
;
523 heading
= (char *) inputbox_instructions_hex
;
526 heading
= (char *) inputbox_instructions_string
;
529 heading
= "Internal mconf error!";
533 switch (dialog_inputbox(prompt
? prompt
: "Main Menu",
535 sym_get_string_value(menu
->sym
))) {
537 if (sym_set_string_value(menu
->sym
, dialog_input_result
))
539 show_textbox(NULL
, "You have made an invalid entry.", 5, 43);
550 static void conf_load(void)
553 switch (dialog_inputbox(NULL
, load_config_text
, 11, 55,
556 if (!dialog_input_result
[0])
558 if (!conf_read(dialog_input_result
))
560 show_textbox(NULL
, "File does not exist!", 5, 38);
563 show_helptext("Load Alternate Configuration", load_config_help
);
571 static void conf_save(void)
574 switch (dialog_inputbox(NULL
, save_config_text
, 11, 55,
577 if (!dialog_input_result
[0])
579 if (!conf_write(dialog_input_result
))
581 show_textbox(NULL
, "Can't create file! Probably a nonexistent directory.", 5, 60);
584 show_helptext("Save Alternate Configuration", save_config_help
);
592 static void conf_cleanup(void)
594 tcsetattr(1, TCSAFLUSH
, &ios_org
);
596 unlink("lxdialog.scrltmp");
599 static void winch_handler(int sig
)
603 if (ioctl(1, TIOCGWINSZ
, &ws
) == -1) {
611 if (rows
< 19 || cols
< 80) {
613 fprintf(stderr
, "Your display is too small to run Menuconfig!\n");
614 fprintf(stderr
, "It must be at least 19 lines by 80 columns.\n");
623 int main(int ac
, char **av
)
632 backtitle
= malloc(128);
633 sym
= sym_lookup("VERSION", 0);
635 snprintf(backtitle
, 128, "Tomato Configuration");
637 mode
= getenv("MENUCONFIG_MODE");
639 if (!strcasecmp(mode
, "single_menu"))
640 single_menu_mode
= 1;
643 tcgetattr(1, &ios_org
);
644 atexit(conf_cleanup
);
647 signal(SIGWINCH
, winch_handler
);
651 /* Restart dialog to act more like when lxdialog was still separate */
654 stat
= dialog_yesno(NULL
,
655 "Do you wish to save your new configuration?", 5, 60);
662 "*** End of configuration.\n"
663 "*** Check the top-level Makefile for additional configuration options.\n\n");
665 printf("\n\nYour configuration changes were NOT saved.\n\n");