00580bd666ac19625f633934fc591a3987a0a778
[openwrt.git] / package / config / mconf.c
1 /*
2 * Copyright (C) 2002 Roman Zippel <zippel@linux-m68k.org>
3 * Released under the terms of the GNU GPL v2.0.
4 *
5 * Introduced single menu mode (show all sub-menus in one large tree).
6 * 2002-11-06 Petr Baudis <pasky@ucw.cz>
7 *
8 * Directly use liblxdialog library routines.
9 * 2002-11-14 Petr Baudis <pasky@ucw.cz>
10 */
11
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>
25
26 #include "dialog.h"
27
28 #define LKC_DIRECT_LINK
29 #include "lkc.h"
30
31 static char menu_backtitle[128];
32 static const char menu_instructions[] =
33 "Arrow keys navigate the menu. "
34 "<Enter> selects submenus --->. "
35 "Highlighted letters are hotkeys. "
36 "Pressing <Y> selectes a feature, while <N> will exclude a feature. "
37 "Press <Esc><Esc> to exit, <?> for Help. "
38 "Legend: [*] feature is selected [ ] feature is excluded",
39 radiolist_instructions[] =
40 "Use the arrow keys to navigate this window or "
41 "press the hotkey of the item you wish to select "
42 "followed by the <SPACE BAR>. "
43 "Press <?> for additional information about this option.",
44 inputbox_instructions_int[] =
45 "Please enter a decimal value. "
46 "Fractions will not be accepted. "
47 "Use the <TAB> key to move from the input field to the buttons below it.",
48 inputbox_instructions_hex[] =
49 "Please enter a hexadecimal value. "
50 "Use the <TAB> key to move from the input field to the buttons below it.",
51 inputbox_instructions_string[] =
52 "Please enter a string value. "
53 "Use the <TAB> key to move from the input field to the buttons below it.",
54 setmod_text[] =
55 "This feature depends on another which has been configured as a module.\n"
56 "As a result, this feature will be built as a module.",
57 nohelp_text[] =
58 "There is no help available for this option.\n",
59 load_config_text[] =
60 "Enter the name of the configuration file you wish to load. "
61 "Accept the name shown to restore the configuration you "
62 "last retrieved. Leave blank to abort.",
63 load_config_help[] =
64 "\n"
65 "For various reasons, one may wish to keep several different Buildroot\n"
66 "configurations available on a single machine.\n"
67 "\n"
68 "If you have saved a previous configuration in a file other than the\n"
69 "Buildroot's default, entering the name of the file here will allow you\n"
70 "to modify that configuration.\n"
71 "\n"
72 "If you are uncertain, then you have probably never used alternate\n"
73 "configuration files. You should therefor leave this blank to abort.\n",
74 save_config_text[] =
75 "Enter a filename to which this configuration should be saved "
76 "as an alternate. Leave blank to abort.",
77 save_config_help[] =
78 "\n"
79 "For various reasons, one may wish to keep different Buildroot\n"
80 "configurations available on a single machine.\n"
81 "\n"
82 "Entering a file name here will allow you to later retrieve, modify\n"
83 "and use the current configuration as an alternate to whatever\n"
84 "configuration options you have selected at that time.\n"
85 "\n"
86 "If you are uncertain what all this means then you should probably\n"
87 "leave this blank.\n",
88 top_menu_help[] =
89 "\n"
90 "Use the Up/Down arrow keys (cursor keys) to highlight the item\n"
91 "you wish to change or submenu wish to select and press <Enter>.\n"
92 "Submenus are designated by \"--->\".\n"
93 "\n"
94 "Shortcut: Press the option's highlighted letter (hotkey).\n"
95 "\n"
96 "You may also use the <PAGE UP> and <PAGE DOWN> keys to scroll\n"
97 "unseen options into view.\n"
98 ;
99
100 static char filename[PATH_MAX+1] = ".config";
101 static int indent = 0;
102 static struct termios ios_org;
103 static int rows, cols;
104 struct menu *current_menu;
105 static int child_count;
106 static int single_menu_mode;
107
108 static struct dialog_list_item *items[16384]; /* FIXME: This ought to be dynamic. */
109 static int item_no;
110
111 static void conf(struct menu *menu);
112 static void conf_choice(struct menu *menu);
113 static void conf_string(struct menu *menu);
114 static void conf_load(void);
115 static void conf_save(void);
116 static void show_textbox(const char *title, const char *text, int r, int c);
117 static void show_helptext(const char *title, const char *text);
118 static void show_help(struct menu *menu);
119 static void show_readme(void);
120
121 static void init_wsize(void)
122 {
123 struct winsize ws;
124 char *env;
125
126 if (ioctl(1, TIOCGWINSZ, &ws) == -1) {
127 rows = 24;
128 cols = 80;
129 } else {
130 rows = ws.ws_row;
131 cols = ws.ws_col;
132 if (!rows) {
133 env = getenv("LINES");
134 if (env)
135 rows = atoi(env);
136 if (!rows)
137 rows = 24;
138 }
139 if (!cols) {
140 env = getenv("COLUMNS");
141 if (env)
142 cols = atoi(env);
143 if (!cols)
144 cols = 80;
145 }
146 }
147
148 if (rows < 19 || cols < 80) {
149 fprintf(stderr, "Your display is too small to run Menuconfig!\n");
150 fprintf(stderr, "It must be at least 19 lines by 80 columns.\n");
151 exit(1);
152 }
153
154 rows -= 4;
155 cols -= 5;
156 }
157
158 static void cinit(void)
159 {
160 item_no = 0;
161 }
162
163 static void cmake(void)
164 {
165 items[item_no] = malloc(sizeof(struct dialog_list_item));
166 memset(items[item_no], 0, sizeof(struct dialog_list_item));
167 items[item_no]->tag = malloc(32); items[item_no]->tag[0] = 0;
168 items[item_no]->name = malloc(512); items[item_no]->name[0] = 0;
169 items[item_no]->namelen = 0;
170 item_no++;
171 }
172
173 static int cprint_name(const char *fmt, ...)
174 {
175 va_list ap;
176 int res;
177
178 if (!item_no)
179 cmake();
180 va_start(ap, fmt);
181 res = vsnprintf(items[item_no - 1]->name + items[item_no - 1]->namelen,
182 512 - items[item_no - 1]->namelen, fmt, ap);
183 if (res > 0)
184 items[item_no - 1]->namelen += res;
185 va_end(ap);
186
187 return res;
188 }
189
190 static int cprint_tag(const char *fmt, ...)
191 {
192 va_list ap;
193 int res;
194
195 if (!item_no)
196 cmake();
197 va_start(ap, fmt);
198 res = vsnprintf(items[item_no - 1]->tag, 32, fmt, ap);
199 va_end(ap);
200
201 return res;
202 }
203
204 static void cdone(void)
205 {
206 int i;
207
208 for (i = 0; i < item_no; i++) {
209 free(items[i]->tag);
210 free(items[i]->name);
211 free(items[i]);
212 }
213
214 item_no = 0;
215 }
216
217 static void build_conf(struct menu *menu)
218 {
219 struct symbol *sym;
220 struct property *prop;
221 struct menu *child;
222 int type, tmp, doint = 2;
223 tristate val;
224 char ch;
225
226 if (!menu_is_visible(menu))
227 return;
228
229 sym = menu->sym;
230 prop = menu->prompt;
231 if (!sym) {
232 if (prop && menu != current_menu) {
233 const char *prompt = menu_get_prompt(menu);
234 switch (prop->type) {
235 case P_MENU:
236 child_count++;
237 cmake();
238 cprint_tag("m%p", menu);
239
240 if (single_menu_mode) {
241 cprint_name("%s%*c%s",
242 menu->data ? "-->" : "++>",
243 indent + 1, ' ', prompt);
244 } else {
245 cprint_name(" %*c%s --->", indent + 1, ' ', prompt);
246 }
247
248 if (single_menu_mode && menu->data)
249 goto conf_childs;
250 return;
251 default:
252 if (prompt) {
253 child_count++;
254 cmake();
255 cprint_tag(":%p", menu);
256 cprint_name("---%*c%s", indent + 1, ' ', prompt);
257 }
258 }
259 } else
260 doint = 0;
261 goto conf_childs;
262 }
263
264 cmake();
265 type = sym_get_type(sym);
266 if (sym_is_choice(sym)) {
267 struct symbol *def_sym = sym_get_choice_value(sym);
268 struct menu *def_menu = NULL;
269
270 child_count++;
271 for (child = menu->list; child; child = child->next) {
272 if (menu_is_visible(child) && child->sym == def_sym)
273 def_menu = child;
274 }
275
276 val = sym_get_tristate_value(sym);
277 if (sym_is_changable(sym)) {
278 cprint_tag("t%p", menu);
279 switch (type) {
280 case S_BOOLEAN:
281 cprint_name("[%c]", val == no ? ' ' : '*');
282 break;
283 case S_TRISTATE:
284 switch (val) {
285 case yes: ch = '*'; break;
286 case mod: ch = 'M'; break;
287 default: ch = ' '; break;
288 }
289 cprint_name("<%c>", ch);
290 break;
291 }
292 } else {
293 cprint_tag("%c%p", def_menu ? 't' : ':', menu);
294 cprint_name(" ");
295 }
296
297 cprint_name("%*c%s", indent + 1, ' ', menu_get_prompt(menu));
298 if (val == yes) {
299 if (def_menu) {
300 cprint_name(" (%s)", menu_get_prompt(def_menu));
301 cprint_name(" --->");
302 if (def_menu->list) {
303 indent += 2;
304 build_conf(def_menu);
305 indent -= 2;
306 }
307 }
308 return;
309 }
310 } else {
311 if (menu == current_menu) {
312 cprint_tag(":%p", menu);
313 cprint_name("---%*c%s", indent + 1, ' ', menu_get_prompt(menu));
314 goto conf_childs;
315 }
316
317 child_count++;
318 val = sym_get_tristate_value(sym);
319 if (sym_is_choice_value(sym) && val == yes) {
320 cprint_tag(":%p", menu);
321 cprint_name(" ");
322 } else {
323 switch (type) {
324 case S_BOOLEAN:
325 cprint_tag("t%p", menu);
326 if (sym_is_changable(sym))
327 cprint_name("[%c]", val == no ? ' ' : '*');
328 else
329 cprint_name("---");
330 break;
331 case S_TRISTATE:
332 cprint_tag("t%p", menu);
333 switch (val) {
334 case yes: ch = '*'; break;
335 case mod: ch = 'M'; break;
336 default: ch = ' '; break;
337 }
338 if (sym_is_changable(sym))
339 cprint_name("<%c>", ch);
340 else
341 cprint_name("---");
342 break;
343 default:
344 cprint_tag("s%p", menu);
345 tmp = cprint_name("(%s)", sym_get_string_value(sym));
346 tmp = indent - tmp + 4;
347 if (tmp < 0)
348 tmp = 0;
349 cprint_name("%*c%s%s", tmp, ' ', menu_get_prompt(menu),
350 (sym_has_value(sym) || !sym_is_changable(sym)) ?
351 "" : " (NEW)");
352 goto conf_childs;
353 }
354 }
355 cprint_name("%*c%s%s", indent + 1, ' ', menu_get_prompt(menu),
356 (sym_has_value(sym) || !sym_is_changable(sym)) ?
357 "" : " (NEW)");
358 if (menu->prompt->type == P_MENU) {
359 cprint_name(" --->");
360 return;
361 }
362 }
363
364 conf_childs:
365 indent += doint;
366 for (child = menu->list; child; child = child->next)
367 build_conf(child);
368 indent -= doint;
369 }
370
371 static void conf(struct menu *menu)
372 {
373 struct dialog_list_item *active_item = NULL;
374 struct menu *submenu;
375 const char *prompt = menu_get_prompt(menu);
376 struct symbol *sym;
377 char active_entry[40];
378 int stat, type;
379
380 unlink("lxdialog.scrltmp");
381 active_entry[0] = 0;
382 while (1) {
383 indent = 0;
384 child_count = 0;
385 current_menu = menu;
386 cdone(); cinit();
387 build_conf(menu);
388 if (!child_count)
389 break;
390 if (menu == &rootmenu) {
391 cmake(); cprint_tag(":"); cprint_name("--- ");
392 cmake(); cprint_tag("L"); cprint_name("Load an Alternate Configuration File");
393 cmake(); cprint_tag("S"); cprint_name("Save Configuration to an Alternate File");
394 }
395 dialog_clear();
396 stat = dialog_menu(prompt ? prompt : "Main Menu",
397 menu_instructions, rows, cols, rows - 10,
398 active_entry, item_no, items);
399 if (stat < 0)
400 return;
401
402 if (stat == 1 || stat == 255)
403 break;
404
405 active_item = first_sel_item(item_no, items);
406 if (!active_item)
407 continue;
408 active_item->selected = 0;
409 strncpy(active_entry, active_item->tag, sizeof(active_entry));
410 active_entry[sizeof(active_entry)-1] = 0;
411 type = active_entry[0];
412 if (!type)
413 continue;
414
415 sym = NULL;
416 submenu = NULL;
417 if (sscanf(active_entry + 1, "%p", &submenu) == 1)
418 sym = submenu->sym;
419
420 switch (stat) {
421 case 0:
422 switch (type) {
423 case 'm':
424 if (single_menu_mode)
425 submenu->data = (void *) (long) !submenu->data;
426 else
427 conf(submenu);
428 break;
429 case 't':
430 if (sym_is_choice(sym) && sym_get_tristate_value(sym) == yes)
431 conf_choice(submenu);
432 else if (submenu->prompt->type == P_MENU)
433 conf(submenu);
434 break;
435 case 's':
436 conf_string(submenu);
437 break;
438 case 'L':
439 conf_load();
440 break;
441 case 'S':
442 conf_save();
443 break;
444 }
445 break;
446 case 2:
447 if (sym)
448 show_help(submenu);
449 else
450 show_readme();
451 break;
452 case 3:
453 if (type == 't') {
454 if (sym_set_tristate_value(sym, yes))
455 break;
456 if (sym_set_tristate_value(sym, mod))
457 show_textbox(NULL, setmod_text, 6, 74);
458 }
459 break;
460 case 4:
461 if (type == 't')
462 sym_set_tristate_value(sym, no);
463 break;
464 case 5:
465 if (type == 't')
466 sym_set_tristate_value(sym, mod);
467 break;
468 case 6:
469 if (type == 't')
470 sym_toggle_tristate_value(sym);
471 else if (type == 'm')
472 conf(submenu);
473 break;
474 }
475 }
476 }
477
478 static void show_textbox(const char *title, const char *text, int r, int c)
479 {
480 int fd;
481
482 fd = creat(".help.tmp", 0777);
483 write(fd, text, strlen(text));
484 close(fd);
485 while (dialog_textbox(title, ".help.tmp", r, c) < 0)
486 ;
487 unlink(".help.tmp");
488 }
489
490 static void show_helptext(const char *title, const char *text)
491 {
492 show_textbox(title, text, rows, cols);
493 }
494
495 static void show_help(struct menu *menu)
496 {
497 const char *help;
498 char *helptext;
499 struct symbol *sym = menu->sym;
500
501 help = sym->help;
502 if (!help)
503 help = nohelp_text;
504 if (sym->name) {
505 helptext = malloc(strlen(sym->name) + strlen(help) + 16);
506 sprintf(helptext, "%s:\n\n%s", sym->name, help);
507 show_helptext(menu_get_prompt(menu), helptext);
508 free(helptext);
509 } else
510 show_helptext(menu_get_prompt(menu), help);
511 }
512
513 static void show_readme(void)
514 {
515 show_helptext("Help", top_menu_help);
516 }
517
518 static void conf_choice(struct menu *menu)
519 {
520 const char *prompt = menu_get_prompt(menu);
521 struct menu *child;
522 struct symbol *active;
523
524 active = sym_get_choice_value(menu->sym);
525 while (1) {
526 current_menu = menu;
527 cdone(); cinit();
528 for (child = menu->list; child; child = child->next) {
529 if (!menu_is_visible(child))
530 continue;
531 cmake();
532 cprint_tag("%p", child);
533 cprint_name("%s", menu_get_prompt(child));
534 if (child->sym == sym_get_choice_value(menu->sym))
535 items[item_no - 1]->selected = 1; /* ON */
536 else if (child->sym == active)
537 items[item_no - 1]->selected = 2; /* SELECTED */
538 else
539 items[item_no - 1]->selected = 0; /* OFF */
540 }
541
542 switch (dialog_checklist(prompt ? prompt : "Main Menu",
543 radiolist_instructions, 15, 70, 6,
544 item_no, items, FLAG_RADIO)) {
545 case 0:
546 if (sscanf(first_sel_item(item_no, items)->tag, "%p", &child) != 1)
547 break;
548 sym_set_tristate_value(child->sym, yes);
549 return;
550 case 1:
551 if (sscanf(first_sel_item(item_no, items)->tag, "%p", &child) == 1) {
552 show_help(child);
553 active = child->sym;
554 } else
555 show_help(menu);
556 break;
557 case 255:
558 return;
559 }
560 }
561 }
562
563 static void conf_string(struct menu *menu)
564 {
565 const char *prompt = menu_get_prompt(menu);
566
567 while (1) {
568 char *heading;
569
570 switch (sym_get_type(menu->sym)) {
571 case S_INT:
572 heading = (char *) inputbox_instructions_int;
573 break;
574 case S_HEX:
575 heading = (char *) inputbox_instructions_hex;
576 break;
577 case S_STRING:
578 heading = (char *) inputbox_instructions_string;
579 break;
580 default:
581 heading = "Internal mconf error!";
582 /* panic? */;
583 }
584
585 switch (dialog_inputbox(prompt ? prompt : "Main Menu",
586 heading, 10, 75,
587 sym_get_string_value(menu->sym))) {
588 case 0:
589 if (sym_set_string_value(menu->sym, dialog_input_result))
590 return;
591 show_textbox(NULL, "You have made an invalid entry.", 5, 43);
592 break;
593 case 1:
594 show_help(menu);
595 break;
596 case 255:
597 return;
598 }
599 }
600 }
601
602 static void conf_load(void)
603 {
604 while (1) {
605 switch (dialog_inputbox(NULL, load_config_text, 11, 55,
606 filename)) {
607 case 0:
608 if (!dialog_input_result[0])
609 return;
610 if (!conf_read(dialog_input_result))
611 return;
612 show_textbox(NULL, "File does not exist!", 5, 38);
613 break;
614 case 1:
615 show_helptext("Load Alternate Configuration", load_config_help);
616 break;
617 case 255:
618 return;
619 }
620 }
621 }
622
623 static void conf_save(void)
624 {
625 while (1) {
626 switch (dialog_inputbox(NULL, save_config_text, 11, 55,
627 filename)) {
628 case 0:
629 if (!dialog_input_result[0])
630 return;
631 if (!conf_write(dialog_input_result))
632 return;
633 show_textbox(NULL, "Can't create file! Probably a nonexistent directory.", 5, 60);
634 break;
635 case 1:
636 show_helptext("Save Alternate Configuration", save_config_help);
637 break;
638 case 255:
639 return;
640 }
641 }
642 }
643
644 static void conf_cleanup(void)
645 {
646 tcsetattr(1, TCSAFLUSH, &ios_org);
647 unlink(".help.tmp");
648 }
649
650 static void winch_handler(int sig)
651 {
652 struct winsize ws;
653
654 if (ioctl(1, TIOCGWINSZ, &ws) == -1) {
655 rows = 24;
656 cols = 80;
657 } else {
658 rows = ws.ws_row;
659 cols = ws.ws_col;
660 }
661
662 if (rows < 19 || cols < 80) {
663 end_dialog();
664 fprintf(stderr, "Your display is too small to run Menuconfig!\n");
665 fprintf(stderr, "It must be at least 19 lines by 80 columns.\n");
666 exit(1);
667 }
668
669 rows -= 4;
670 cols -= 5;
671
672 }
673
674 int main(int ac, char **av)
675 {
676 int stat;
677 char *mode;
678 struct symbol *sym;
679
680 conf_parse(av[1]);
681 conf_read(NULL);
682
683 sym = sym_lookup("VERSION", 0);
684 sym_calc_value(sym);
685 snprintf(menu_backtitle, 128, "Buildroot v%s Configuration",
686 sym_get_string_value(sym));
687
688 mode = getenv("MENUCONFIG_MODE");
689 if (mode) {
690 if (!strcasecmp(mode, "single_menu"))
691 single_menu_mode = 1;
692 }
693
694 tcgetattr(1, &ios_org);
695 atexit(conf_cleanup);
696 init_wsize();
697 init_dialog();
698 signal(SIGWINCH, winch_handler);
699 conf(&rootmenu);
700 end_dialog();
701
702 /* Restart dialog to act more like when lxdialog was still separate */
703 init_dialog();
704 do {
705 stat = dialog_yesno(NULL,
706 "Do you wish to save your new Buildroot configuration?", 5, 60);
707 } while (stat < 0);
708 end_dialog();
709
710 if (stat == 0) {
711 conf_write(NULL);
712 printf("\n\n"
713 "*** End of Buildroot configuration.\n"
714 "*** Check the top-level Makefile for additional configuration options.\n\n");
715 } else
716 printf("\n\nYour Buildroot configuration changes were NOT saved.\n\n");
717
718 return 0;
719 }
This page took 0.083045 seconds and 3 git commands to generate.