OSDN Git Service

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