Initial revision
[openwrt/svn-archive/archive.git] / openwrt / 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 static 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 child_count++;
312 val = sym_get_tristate_value(sym);
313 if (sym_is_choice_value(sym) && val == yes) {
314 cprint_tag(":%p", menu);
315 cprint_name(" ");
316 } else {
317 switch (type) {
318 case S_BOOLEAN:
319 cprint_tag("t%p", menu);
320 if (sym_is_changable(sym))
321 cprint_name("[%c]", val == no ? ' ' : '*');
322 else
323 cprint_name("---");
324 break;
325 case S_TRISTATE:
326 cprint_tag("t%p", menu);
327 switch (val) {
328 case yes: ch = '*'; break;
329 case mod: ch = 'M'; break;
330 default: ch = ' '; break;
331 }
332 if (sym_is_changable(sym))
333 cprint_name("<%c>", ch);
334 else
335 cprint_name("---");
336 break;
337 default:
338 cprint_tag("s%p", menu);
339 tmp = cprint_name("(%s)", sym_get_string_value(sym));
340 tmp = indent - tmp + 4;
341 if (tmp < 0)
342 tmp = 0;
343 cprint_name("%*c%s%s", tmp, ' ', menu_get_prompt(menu),
344 (sym_has_value(sym) || !sym_is_changable(sym)) ?
345 "" : " (NEW)");
346 goto conf_childs;
347 }
348 }
349 cprint_name("%*c%s%s", indent + 1, ' ', menu_get_prompt(menu),
350 (sym_has_value(sym) || !sym_is_changable(sym)) ?
351 "" : " (NEW)");
352 if (menu->prompt->type == P_MENU) {
353 cprint_name(" --->");
354 return;
355 }
356 }
357
358 conf_childs:
359 indent += doint;
360 for (child = menu->list; child; child = child->next)
361 build_conf(child);
362 indent -= doint;
363 }
364
365 static void conf(struct menu *menu)
366 {
367 struct dialog_list_item *active_item = NULL;
368 struct menu *submenu;
369 const char *prompt = menu_get_prompt(menu);
370 struct symbol *sym;
371 char active_entry[40];
372 int stat, type;
373
374 unlink("lxdialog.scrltmp");
375 active_entry[0] = 0;
376 while (1) {
377 indent = 0;
378 child_count = 0;
379 current_menu = menu;
380 cdone(); cinit();
381 build_conf(menu);
382 if (!child_count)
383 break;
384 if (menu == &rootmenu) {
385 cmake(); cprint_tag(":"); cprint_name("--- ");
386 cmake(); cprint_tag("L"); cprint_name("Load an Alternate Configuration File");
387 cmake(); cprint_tag("S"); cprint_name("Save Configuration to an Alternate File");
388 }
389 dialog_clear();
390 stat = dialog_menu(prompt ? prompt : "Main Menu",
391 menu_instructions, rows, cols, rows - 10,
392 active_entry, item_no, items);
393 if (stat < 0)
394 return;
395
396 if (stat == 1 || stat == 255)
397 break;
398
399 active_item = first_sel_item(item_no, items);
400 if (!active_item)
401 continue;
402 active_item->selected = 0;
403 strncpy(active_entry, active_item->tag, sizeof(active_entry));
404 active_entry[sizeof(active_entry)-1] = 0;
405 type = active_entry[0];
406 if (!type)
407 continue;
408
409 sym = NULL;
410 submenu = NULL;
411 if (sscanf(active_entry + 1, "%p", &submenu) == 1)
412 sym = submenu->sym;
413
414 switch (stat) {
415 case 0:
416 switch (type) {
417 case 'm':
418 if (single_menu_mode)
419 submenu->data = (void *) (long) !submenu->data;
420 else
421 conf(submenu);
422 break;
423 case 't':
424 if (sym_is_choice(sym) && sym_get_tristate_value(sym) == yes)
425 conf_choice(submenu);
426 else if (submenu->prompt->type == P_MENU)
427 conf(submenu);
428 break;
429 case 's':
430 conf_string(submenu);
431 break;
432 case 'L':
433 conf_load();
434 break;
435 case 'S':
436 conf_save();
437 break;
438 }
439 break;
440 case 2:
441 if (sym)
442 show_help(submenu);
443 else
444 show_readme();
445 break;
446 case 3:
447 if (type == 't') {
448 if (sym_set_tristate_value(sym, yes))
449 break;
450 if (sym_set_tristate_value(sym, mod))
451 show_textbox(NULL, setmod_text, 6, 74);
452 }
453 break;
454 case 4:
455 if (type == 't')
456 sym_set_tristate_value(sym, no);
457 break;
458 case 5:
459 if (type == 't')
460 sym_set_tristate_value(sym, mod);
461 break;
462 case 6:
463 if (type == 't')
464 sym_toggle_tristate_value(sym);
465 else if (type == 'm')
466 conf(submenu);
467 break;
468 }
469 }
470 }
471
472 static void show_textbox(const char *title, const char *text, int r, int c)
473 {
474 int fd;
475
476 fd = creat(".help.tmp", 0777);
477 write(fd, text, strlen(text));
478 close(fd);
479 while (dialog_textbox(title, ".help.tmp", r, c) < 0)
480 ;
481 unlink(".help.tmp");
482 }
483
484 static void show_helptext(const char *title, const char *text)
485 {
486 show_textbox(title, text, rows, cols);
487 }
488
489 static void show_help(struct menu *menu)
490 {
491 const char *help;
492 char *helptext;
493 struct symbol *sym = menu->sym;
494
495 help = sym->help;
496 if (!help)
497 help = nohelp_text;
498 if (sym->name) {
499 helptext = malloc(strlen(sym->name) + strlen(help) + 16);
500 sprintf(helptext, "%s:\n\n%s", sym->name, help);
501 show_helptext(menu_get_prompt(menu), helptext);
502 free(helptext);
503 } else
504 show_helptext(menu_get_prompt(menu), help);
505 }
506
507 static void show_readme(void)
508 {
509 show_helptext("Help", top_menu_help);
510 }
511
512 static void conf_choice(struct menu *menu)
513 {
514 const char *prompt = menu_get_prompt(menu);
515 struct menu *child;
516 struct symbol *active;
517
518 active = sym_get_choice_value(menu->sym);
519 while (1) {
520 current_menu = menu;
521 cdone(); cinit();
522 for (child = menu->list; child; child = child->next) {
523 if (!menu_is_visible(child))
524 continue;
525 cmake();
526 cprint_tag("%p", child);
527 cprint_name("%s", menu_get_prompt(child));
528 if (child->sym == sym_get_choice_value(menu->sym))
529 items[item_no - 1]->selected = 1; /* ON */
530 else if (child->sym == active)
531 items[item_no - 1]->selected = 2; /* SELECTED */
532 else
533 items[item_no - 1]->selected = 0; /* OFF */
534 }
535
536 switch (dialog_checklist(prompt ? prompt : "Main Menu",
537 radiolist_instructions, 15, 70, 6,
538 item_no, items, FLAG_RADIO)) {
539 case 0:
540 if (sscanf(first_sel_item(item_no, items)->tag, "%p", &child) != 1)
541 break;
542 sym_set_tristate_value(child->sym, yes);
543 return;
544 case 1:
545 if (sscanf(first_sel_item(item_no, items)->tag, "%p", &child) == 1) {
546 show_help(child);
547 active = child->sym;
548 } else
549 show_help(menu);
550 break;
551 case 255:
552 return;
553 }
554 }
555 }
556
557 static void conf_string(struct menu *menu)
558 {
559 const char *prompt = menu_get_prompt(menu);
560
561 while (1) {
562 char *heading;
563
564 switch (sym_get_type(menu->sym)) {
565 case S_INT:
566 heading = (char *) inputbox_instructions_int;
567 break;
568 case S_HEX:
569 heading = (char *) inputbox_instructions_hex;
570 break;
571 case S_STRING:
572 heading = (char *) inputbox_instructions_string;
573 break;
574 default:
575 heading = "Internal mconf error!";
576 /* panic? */;
577 }
578
579 switch (dialog_inputbox(prompt ? prompt : "Main Menu",
580 heading, 10, 75,
581 sym_get_string_value(menu->sym))) {
582 case 0:
583 if (sym_set_string_value(menu->sym, dialog_input_result))
584 return;
585 show_textbox(NULL, "You have made an invalid entry.", 5, 43);
586 break;
587 case 1:
588 show_help(menu);
589 break;
590 case 255:
591 return;
592 }
593 }
594 }
595
596 static void conf_load(void)
597 {
598 while (1) {
599 switch (dialog_inputbox(NULL, load_config_text, 11, 55,
600 filename)) {
601 case 0:
602 if (!dialog_input_result[0])
603 return;
604 if (!conf_read(dialog_input_result))
605 return;
606 show_textbox(NULL, "File does not exist!", 5, 38);
607 break;
608 case 1:
609 show_helptext("Load Alternate Configuration", load_config_help);
610 break;
611 case 255:
612 return;
613 }
614 }
615 }
616
617 static void conf_save(void)
618 {
619 while (1) {
620 switch (dialog_inputbox(NULL, save_config_text, 11, 55,
621 filename)) {
622 case 0:
623 if (!dialog_input_result[0])
624 return;
625 if (!conf_write(dialog_input_result))
626 return;
627 show_textbox(NULL, "Can't create file! Probably a nonexistent directory.", 5, 60);
628 break;
629 case 1:
630 show_helptext("Save Alternate Configuration", save_config_help);
631 break;
632 case 255:
633 return;
634 }
635 }
636 }
637
638 static void conf_cleanup(void)
639 {
640 tcsetattr(1, TCSAFLUSH, &ios_org);
641 unlink(".help.tmp");
642 }
643
644 static void winch_handler(int sig)
645 {
646 struct winsize ws;
647
648 if (ioctl(1, TIOCGWINSZ, &ws) == -1) {
649 rows = 24;
650 cols = 80;
651 } else {
652 rows = ws.ws_row;
653 cols = ws.ws_col;
654 }
655
656 if (rows < 19 || cols < 80) {
657 end_dialog();
658 fprintf(stderr, "Your display is too small to run Menuconfig!\n");
659 fprintf(stderr, "It must be at least 19 lines by 80 columns.\n");
660 exit(1);
661 }
662
663 rows -= 4;
664 cols -= 5;
665
666 }
667
668 int main(int ac, char **av)
669 {
670 int stat;
671 char *mode;
672 struct symbol *sym;
673
674 conf_parse(av[1]);
675 conf_read(NULL);
676
677 sym = sym_lookup("VERSION", 0);
678 sym_calc_value(sym);
679 snprintf(menu_backtitle, 128, "Buildroot v%s Configuration",
680 sym_get_string_value(sym));
681
682 mode = getenv("MENUCONFIG_MODE");
683 if (mode) {
684 if (!strcasecmp(mode, "single_menu"))
685 single_menu_mode = 1;
686 }
687
688 tcgetattr(1, &ios_org);
689 atexit(conf_cleanup);
690 init_wsize();
691 init_dialog();
692 signal(SIGWINCH, winch_handler);
693 conf(&rootmenu);
694 end_dialog();
695
696 /* Restart dialog to act more like when lxdialog was still separate */
697 init_dialog();
698 do {
699 stat = dialog_yesno(NULL,
700 "Do you wish to save your new Buildroot configuration?", 5, 60);
701 } while (stat < 0);
702 end_dialog();
703
704 if (stat == 0) {
705 conf_write(NULL);
706 printf("\n\n"
707 "*** End of Buildroot configuration.\n"
708 "*** Check the top-level Makefile for additional configuration options.\n\n");
709 } else
710 printf("\n\nYour Buildroot configuration changes were NOT saved.\n\n");
711
712 return 0;
713 }