Allow IPv6 address entry in tools>ping - Loosens valid character check
[tomato/davidwu.git] / release / src / router / config / mconf.c
blobd2e7d250e51aee69fb5942e70ab41fb628efcc57
1 /*
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>
13 #include <sys/wait.h>
14 #include <sys/termios.h>
15 #include <ctype.h>
16 #include <errno.h>
17 #include <fcntl.h>
18 #include <limits.h>
19 #include <signal.h>
20 #include <stdarg.h>
21 #include <stdlib.h>
22 #include <string.h>
23 #include <termios.h>
24 #include <unistd.h>
26 #include "dialog.h"
28 #define LKC_DIRECT_LINK
29 #include "lkc.h"
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.",
53 setmod_text[] =
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.",
56 nohelp_text[] =
57 "There is no help available for this option.\n",
58 load_config_text[] =
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.",
62 load_config_help[] =
63 "\n"
64 "For various reasons, one may wish to keep several different\n"
65 "configurations available on a single machine.\n"
66 "\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"
70 "\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",
73 save_config_text[] =
74 "Enter a filename to which this configuration should be saved "
75 "as an alternate. Leave blank to abort.",
76 save_config_help[] =
77 "\n"
78 "For various reasons, one may wish to keep different\n"
79 "configurations available on a single machine.\n"
80 "\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"
84 "\n"
85 "If you are uncertain what all this means then you should probably\n"
86 "leave this blank.\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];
98 static int item_no;
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)
112 struct winsize ws;
114 if (ioctl(1, TIOCGWINSZ, &ws) == -1) {
115 rows = 24;
116 cols = 80;
117 } else {
118 rows = ws.ws_row;
119 cols = ws.ws_col;
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");
125 exit(1);
128 rows -= 4;
129 cols -= 5;
132 static void cinit(void)
134 item_no = 0;
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;
144 item_no++;
147 static int cprint_name(const char *fmt, ...)
149 va_list ap;
150 int res;
152 if (!item_no)
153 cmake();
154 va_start(ap, fmt);
155 res = vsnprintf(items[item_no - 1]->name + items[item_no - 1]->namelen,
156 512 - items[item_no - 1]->namelen, fmt, ap);
157 if (res > 0)
158 items[item_no - 1]->namelen += res;
159 va_end(ap);
161 return res;
164 static int cprint_tag(const char *fmt, ...)
166 va_list ap;
167 int res;
169 if (!item_no)
170 cmake();
171 va_start(ap, fmt);
172 res = vsnprintf(items[item_no - 1]->tag, 32, fmt, ap);
173 va_end(ap);
175 return res;
178 static void cdone(void)
180 int i;
182 for (i = 0; i < item_no; i++) {
183 free(items[i]->tag);
184 free(items[i]->name);
185 free(items[i]);
188 item_no = 0;
191 static void build_conf(struct menu *menu)
193 struct symbol *sym;
194 struct property *prop;
195 struct menu *child;
196 int type, tmp, doint = 2;
197 tristate val;
198 char ch;
200 if (!menu_is_visible(menu))
201 return;
203 sym = menu->sym;
204 prop = menu->prompt;
205 if (!sym) {
206 if (prop && menu != current_menu) {
207 const char *prompt = menu_get_prompt(menu);
208 switch (prop->type) {
209 case P_MENU:
210 child_count++;
211 cmake();
212 cprint_tag("m%p", menu);
214 if (single_menu_mode) {
215 cprint_name("%s%*c%s",
216 menu->data ? "-->" : "++>",
217 indent + 1, ' ', prompt);
218 } else {
219 if (menu->parent != &rootmenu)
220 cprint_name(" %*c", indent + 1, ' ');
221 cprint_name("%s --->", prompt);
224 if (single_menu_mode && menu->data)
225 goto conf_childs;
226 return;
227 default:
228 if (prompt) {
229 child_count++;
230 cmake();
231 cprint_tag(":%p", menu);
232 cprint_name("---%*c%s", indent + 1, ' ', prompt);
235 } else
236 doint = 0;
237 goto conf_childs;
240 cmake();
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;
246 child_count++;
247 for (child = menu->list; child; child = child->next) {
248 if (menu_is_visible(child) && child->sym == def_sym)
249 def_menu = child;
252 val = sym_get_tristate_value(sym);
253 if (sym_is_changable(sym)) {
254 cprint_tag("t%p", menu);
255 switch (type) {
256 case S_BOOLEAN:
257 cprint_name("[%c]", val == no ? ' ' : '*');
258 break;
259 case S_TRISTATE:
260 switch (val) {
261 case yes: ch = '*'; break;
262 case mod: ch = 'M'; break;
263 default: ch = ' '; break;
265 cprint_name("<%c>", ch);
266 break;
268 } else {
269 cprint_tag("%c%p", def_menu ? 't' : ':', menu);
270 cprint_name(" ");
273 cprint_name("%*c%s", indent + 1, ' ', menu_get_prompt(menu));
274 if (val == yes) {
275 if (def_menu) {
276 cprint_name(" (%s)", menu_get_prompt(def_menu));
277 cprint_name(" --->");
278 if (def_menu->list) {
279 indent += 2;
280 build_conf(def_menu);
281 indent -= 2;
284 return;
286 } else {
287 child_count++;
288 val = sym_get_tristate_value(sym);
289 if (sym_is_choice_value(sym) && val == yes) {
290 cprint_tag(":%p", menu);
291 cprint_name(" ");
292 } else {
293 switch (type) {
294 case S_BOOLEAN:
295 cprint_tag("t%p", menu);
296 cprint_name("[%c]", val == no ? ' ' : '*');
297 break;
298 case S_TRISTATE:
299 cprint_tag("t%p", menu);
300 switch (val) {
301 case yes: ch = '*'; break;
302 case mod: ch = 'M'; break;
303 default: ch = ' '; break;
305 cprint_name("<%c>", ch);
306 break;
307 default:
308 cprint_tag("s%p", menu);
309 tmp = cprint_name("(%s)", sym_get_string_value(sym));
310 tmp = indent - tmp + 4;
311 if (tmp < 0)
312 tmp = 0;
313 cprint_name("%*c%s%s", tmp, ' ', menu_get_prompt(menu),
314 sym_has_value(sym) ? "" : " (NEW)");
315 goto conf_childs;
318 cprint_name("%*c%s%s", indent + 1, ' ', menu_get_prompt(menu),
319 sym_has_value(sym) ? "" : " (NEW)");
322 conf_childs:
323 indent += doint;
324 for (child = menu->list; child; child = child->next)
325 build_conf(child);
326 indent -= doint;
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);
334 struct symbol *sym;
335 char active_entry[40];
336 int stat, type;
338 unlink("lxdialog.scrltmp");
339 active_entry[0] = 0;
340 while (1) {
341 indent = 0;
342 child_count = 0;
343 current_menu = menu;
344 cdone(); cinit();
345 build_conf(menu);
346 if (!child_count)
347 break;
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");
353 dialog_clear();
354 stat = dialog_menu(prompt ? prompt : "Main Menu",
355 menu_instructions, rows, cols, rows - 10,
356 active_entry, item_no, items);
357 if (stat < 0)
358 return;
360 if (stat == 1 || stat == 255)
361 break;
363 active_item = first_sel_item(item_no, items);
364 if (!active_item)
365 continue;
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];
370 if (!type)
371 continue;
373 sym = NULL;
374 submenu = NULL;
375 if (sscanf(active_entry + 1, "%p", &submenu) == 1)
376 sym = submenu->sym;
378 switch (stat) {
379 case 0:
380 switch (type) {
381 case 'm':
382 if (single_menu_mode)
383 submenu->data = (submenu->data)? NULL : (void *)1;
384 else
385 conf(submenu);
386 break;
387 case 't':
388 if (sym_is_choice(sym) && sym_get_tristate_value(sym) == yes)
389 conf_choice(submenu);
390 break;
391 case 's':
392 conf_string(submenu);
393 break;
394 case 'L':
395 conf_load();
396 break;
397 case 'S':
398 conf_save();
399 break;
401 break;
402 case 2:
403 if (sym)
404 show_help(submenu);
405 else
406 show_readme();
407 break;
408 case 3:
409 if (type == 't') {
410 if (sym_set_tristate_value(sym, yes))
411 break;
412 if (sym_set_tristate_value(sym, mod))
413 show_textbox(NULL, setmod_text, 6, 74);
415 break;
416 case 4:
417 if (type == 't')
418 sym_set_tristate_value(sym, no);
419 break;
420 case 5:
421 if (type == 't')
422 sym_set_tristate_value(sym, mod);
423 break;
424 case 6:
425 if (type == 't')
426 sym_toggle_tristate_value(sym);
427 else if (type == 'm')
428 conf(submenu);
429 break;
434 static void show_textbox(const char *title, const char *text, int r, int c)
436 int fd;
438 fd = creat(".help.tmp", 0777);
439 write(fd, text, strlen(text));
440 close(fd);
441 while (dialog_textbox(title, ".help.tmp", r, c) < 0)
443 unlink(".help.tmp");
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)
453 const char *help;
454 char *helptext;
455 struct symbol *sym = menu->sym;
457 help = sym->help;
458 if (!help)
459 help = nohelp_text;
460 if (sym->name) {
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);
464 free(helptext);
465 } else
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);
478 struct menu *child;
479 struct symbol *active;
481 while (1) {
482 current_menu = menu;
483 active = sym_get_choice_value(menu->sym);
484 cdone(); cinit();
485 for (child = menu->list; child; child = child->next) {
486 if (!menu_is_visible(child))
487 continue;
488 cmake();
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)) {
497 case 0:
498 if (sscanf(first_sel_item(item_no, items)->tag, "%p", &menu) != 1)
499 break;
500 sym_set_tristate_value(menu->sym, yes);
501 return;
502 case 1:
503 show_help(menu);
504 break;
505 case 255:
506 return;
511 static void conf_string(struct menu *menu)
513 const char *prompt = menu_get_prompt(menu);
515 while (1) {
516 char *heading;
518 switch (sym_get_type(menu->sym)) {
519 case S_INT:
520 heading = (char *) inputbox_instructions_int;
521 break;
522 case S_HEX:
523 heading = (char *) inputbox_instructions_hex;
524 break;
525 case S_STRING:
526 heading = (char *) inputbox_instructions_string;
527 break;
528 default:
529 heading = "Internal mconf error!";
530 /* panic? */;
533 switch (dialog_inputbox(prompt ? prompt : "Main Menu",
534 heading, 10, 75,
535 sym_get_string_value(menu->sym))) {
536 case 0:
537 if (sym_set_string_value(menu->sym, dialog_input_result))
538 return;
539 show_textbox(NULL, "You have made an invalid entry.", 5, 43);
540 break;
541 case 1:
542 show_help(menu);
543 break;
544 case 255:
545 return;
550 static void conf_load(void)
552 while (1) {
553 switch (dialog_inputbox(NULL, load_config_text, 11, 55,
554 filename)) {
555 case 0:
556 if (!dialog_input_result[0])
557 return;
558 if (!conf_read(dialog_input_result))
559 return;
560 show_textbox(NULL, "File does not exist!", 5, 38);
561 break;
562 case 1:
563 show_helptext("Load Alternate Configuration", load_config_help);
564 break;
565 case 255:
566 return;
571 static void conf_save(void)
573 while (1) {
574 switch (dialog_inputbox(NULL, save_config_text, 11, 55,
575 filename)) {
576 case 0:
577 if (!dialog_input_result[0])
578 return;
579 if (!conf_write(dialog_input_result))
580 return;
581 show_textbox(NULL, "Can't create file! Probably a nonexistent directory.", 5, 60);
582 break;
583 case 1:
584 show_helptext("Save Alternate Configuration", save_config_help);
585 break;
586 case 255:
587 return;
592 static void conf_cleanup(void)
594 tcsetattr(1, TCSAFLUSH, &ios_org);
595 unlink(".help.tmp");
596 unlink("lxdialog.scrltmp");
599 static void winch_handler(int sig)
601 struct winsize ws;
603 if (ioctl(1, TIOCGWINSZ, &ws) == -1) {
604 rows = 24;
605 cols = 80;
606 } else {
607 rows = ws.ws_row;
608 cols = ws.ws_col;
611 if (rows < 19 || cols < 80) {
612 end_dialog();
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");
615 exit(1);
618 rows -= 4;
619 cols -= 5;
623 int main(int ac, char **av)
625 int stat;
626 char *mode;
627 struct symbol *sym;
629 conf_parse(av[1]);
630 conf_read(NULL);
632 backtitle = malloc(128);
633 sym = sym_lookup("VERSION", 0);
634 sym_calc_value(sym);
635 snprintf(backtitle, 128, "Tomato Configuration");
637 mode = getenv("MENUCONFIG_MODE");
638 if (mode) {
639 if (!strcasecmp(mode, "single_menu"))
640 single_menu_mode = 1;
643 tcgetattr(1, &ios_org);
644 atexit(conf_cleanup);
645 init_wsize();
646 init_dialog();
647 signal(SIGWINCH, winch_handler);
648 conf(&rootmenu);
649 end_dialog();
651 /* Restart dialog to act more like when lxdialog was still separate */
652 init_dialog();
653 do {
654 stat = dialog_yesno(NULL,
655 "Do you wish to save your new configuration?", 5, 60);
656 } while (stat < 0);
657 end_dialog();
659 if (stat == 0) {
660 conf_write(NULL);
661 printf("\n\n"
662 "*** End of configuration.\n"
663 "*** Check the top-level Makefile for additional configuration options.\n\n");
664 } else
665 printf("\n\nYour configuration changes were NOT saved.\n\n");
667 return 0;