ftp://ftp.kernel.org/pub/linux/kernel/v2.6/linux-2.6.6.tar.bz2
[linux-2.6.git] / scripts / kconfig / 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
9 #include <sys/ioctl.h>
10 #include <sys/wait.h>
11 #include <ctype.h>
12 #include <errno.h>
13 #include <fcntl.h>
14 #include <limits.h>
15 #include <signal.h>
16 #include <stdarg.h>
17 #include <stdlib.h>
18 #include <string.h>
19 #include <termios.h>
20 #include <unistd.h>
21
22 #define LKC_DIRECT_LINK
23 #include "lkc.h"
24
25 static char menu_backtitle[128];
26 static const char menu_instructions[] =
27         "Arrow keys navigate the menu.  "
28         "<Enter> selects submenus --->.  "
29         "Highlighted letters are hotkeys.  "
30         "Pressing <Y> includes, <N> excludes, <M> modularizes features.  "
31         "Press <Esc><Esc> to exit, <?> for Help.  "
32         "Legend: [*] built-in  [ ] excluded  <M> module  < > module capable",
33 radiolist_instructions[] =
34         "Use the arrow keys to navigate this window or "
35         "press the hotkey of the item you wish to select "
36         "followed by the <SPACE BAR>. "
37         "Press <?> for additional information about this option.",
38 inputbox_instructions_int[] =
39         "Please enter a decimal value. "
40         "Fractions will not be accepted.  "
41         "Use the <TAB> key to move from the input field to the buttons below it.",
42 inputbox_instructions_hex[] =
43         "Please enter a hexadecimal value. "
44         "Use the <TAB> key to move from the input field to the buttons below it.",
45 inputbox_instructions_string[] =
46         "Please enter a string value. "
47         "Use the <TAB> key to move from the input field to the buttons below it.",
48 setmod_text[] =
49         "This feature depends on another which has been configured as a module.\n"
50         "As a result, this feature will be built as a module.",
51 nohelp_text[] =
52         "There is no help available for this kernel option.\n",
53 load_config_text[] =
54         "Enter the name of the configuration file you wish to load.  "
55         "Accept the name shown to restore the configuration you "
56         "last retrieved.  Leave blank to abort.",
57 load_config_help[] =
58         "\n"
59         "For various reasons, one may wish to keep several different kernel\n"
60         "configurations available on a single machine.\n"
61         "\n"
62         "If you have saved a previous configuration in a file other than the\n"
63         "kernel's default, entering the name of the file here will allow you\n"
64         "to modify that configuration.\n"
65         "\n"
66         "If you are uncertain, then you have probably never used alternate\n"
67         "configuration files.  You should therefor leave this blank to abort.\n",
68 save_config_text[] =
69         "Enter a filename to which this configuration should be saved "
70         "as an alternate.  Leave blank to abort.",
71 save_config_help[] =
72         "\n"
73         "For various reasons, one may wish to keep different kernel\n"
74         "configurations available on a single machine.\n"
75         "\n"
76         "Entering a file name here will allow you to later retrieve, modify\n"
77         "and use the current configuration as an alternate to whatever\n"
78         "configuration options you have selected at that time.\n"
79         "\n"
80         "If you are uncertain what all this means then you should probably\n"
81         "leave this blank.\n"
82 ;
83
84 static char buf[4096], *bufptr = buf;
85 static char input_buf[4096];
86 static char filename[PATH_MAX+1] = ".config";
87 static char *args[1024], **argptr = args;
88 static int indent;
89 static struct termios ios_org;
90 static int rows, cols;
91 static struct menu *current_menu;
92 static int child_count;
93 static int do_resize;
94 static int single_menu_mode;
95
96 static void conf(struct menu *menu);
97 static void conf_choice(struct menu *menu);
98 static void conf_string(struct menu *menu);
99 static void conf_load(void);
100 static void conf_save(void);
101 static void show_textbox(const char *title, const char *text, int r, int c);
102 static void show_helptext(const char *title, const char *text);
103 static void show_help(struct menu *menu);
104 static void show_readme(void);
105
106 static void cprint_init(void);
107 static int cprint1(const char *fmt, ...);
108 static void cprint_done(void);
109 static int cprint(const char *fmt, ...);
110
111 static void init_wsize(void)
112 {
113         struct winsize ws;
114         char *env;
115
116         if (ioctl(1, TIOCGWINSZ, &ws) == -1) {
117                 rows = 24;
118                 cols = 80;
119         } else {
120                 rows = ws.ws_row;
121                 cols = ws.ws_col;
122                 if (!rows) {
123                         env = getenv("LINES");
124                         if (env)
125                                 rows = atoi(env);
126                         if (!rows)
127                                 rows = 24;
128                 }
129                 if (!cols) {
130                         env = getenv("COLUMNS");
131                         if (env)
132                                 cols = atoi(env);
133                         if (!cols)
134                                 cols = 80;
135                 }
136         }
137
138         if (rows < 19 || cols < 80) {
139                 fprintf(stderr, "Your display is too small to run Menuconfig!\n");
140                 fprintf(stderr, "It must be at least 19 lines by 80 columns.\n");
141                 exit(1);
142         }
143
144         rows -= 4;
145         cols -= 5;
146 }
147
148 static void cprint_init(void)
149 {
150         bufptr = buf;
151         argptr = args;
152         memset(args, 0, sizeof(args));
153         indent = 0;
154         child_count = 0;
155         cprint("./scripts/lxdialog/lxdialog");
156         cprint("--backtitle");
157         cprint(menu_backtitle);
158 }
159
160 static int cprint1(const char *fmt, ...)
161 {
162         va_list ap;
163         int res;
164
165         if (!*argptr)
166                 *argptr = bufptr;
167         va_start(ap, fmt);
168         res = vsprintf(bufptr, fmt, ap);
169         va_end(ap);
170         bufptr += res;
171
172         return res;
173 }
174
175 static void cprint_done(void)
176 {
177         *bufptr++ = 0;
178         argptr++;
179 }
180
181 static int cprint(const char *fmt, ...)
182 {
183         va_list ap;
184         int res;
185
186         *argptr++ = bufptr;
187         va_start(ap, fmt);
188         res = vsprintf(bufptr, fmt, ap);
189         va_end(ap);
190         bufptr += res;
191         *bufptr++ = 0;
192
193         return res;
194 }
195
196 pid_t pid;
197
198 static void winch_handler(int sig)
199 {
200         if (!do_resize) {
201                 kill(pid, SIGINT);
202                 do_resize = 1;
203         }
204 }
205
206 static int exec_conf(void)
207 {
208         int pipefd[2], stat, size;
209         struct sigaction sa;
210         sigset_t sset, osset;
211
212         sigemptyset(&sset);
213         sigaddset(&sset, SIGINT);
214         sigprocmask(SIG_BLOCK, &sset, &osset);
215
216         signal(SIGINT, SIG_DFL);
217
218         sa.sa_handler = winch_handler;
219         sigemptyset(&sa.sa_mask);
220         sa.sa_flags = SA_RESTART;
221         sigaction(SIGWINCH, &sa, NULL);
222
223         *argptr++ = NULL;
224
225         pipe(pipefd);
226         pid = fork();
227         if (pid == 0) {
228                 sigprocmask(SIG_SETMASK, &osset, NULL);
229                 dup2(pipefd[1], 2);
230                 close(pipefd[0]);
231                 close(pipefd[1]);
232                 execv(args[0], args);
233                 _exit(EXIT_FAILURE);
234         }
235
236         close(pipefd[1]);
237         bufptr = input_buf;
238         while (1) {
239                 size = input_buf + sizeof(input_buf) - bufptr;
240                 size = read(pipefd[0], bufptr, size);
241                 if (size <= 0) {
242                         if (size < 0) {
243                                 if (errno == EINTR || errno == EAGAIN)
244                                         continue;
245                                 perror("read");
246                         }
247                         break;
248                 }
249                 bufptr += size;
250         }
251         *bufptr++ = 0;
252         close(pipefd[0]);
253         waitpid(pid, &stat, 0);
254
255         if (do_resize) {
256                 init_wsize();
257                 do_resize = 0;
258                 sigprocmask(SIG_SETMASK, &osset, NULL);
259                 return -1;
260         }
261         if (WIFSIGNALED(stat)) {
262                 printf("\finterrupted(%d)\n", WTERMSIG(stat));
263                 exit(1);
264         }
265 #if 0
266         printf("\fexit state: %d\nexit data: '%s'\n", WEXITSTATUS(stat), input_buf);
267         sleep(1);
268 #endif
269         sigpending(&sset);
270         if (sigismember(&sset, SIGINT)) {
271                 printf("\finterrupted\n");
272                 exit(1);
273         }
274         sigprocmask(SIG_SETMASK, &osset, NULL);
275
276         return WEXITSTATUS(stat);
277 }
278
279 static void build_conf(struct menu *menu)
280 {
281         struct symbol *sym;
282         struct property *prop;
283         struct menu *child;
284         int type, tmp, doint = 2;
285         tristate val;
286         char ch;
287
288         if (!menu_is_visible(menu))
289                 return;
290
291         sym = menu->sym;
292         prop = menu->prompt;
293         if (!sym) {
294                 if (prop && menu != current_menu) {
295                         const char *prompt = menu_get_prompt(menu);
296                         switch (prop->type) {
297                         case P_MENU:
298                                 child_count++;
299                                 cprint("m%p", menu);
300
301                                 if (single_menu_mode) {
302                                         cprint1("%s%*c%s",
303                                                 menu->data ? "-->" : "++>",
304                                                 indent + 1, ' ', prompt);
305                                 } else
306                                         cprint1("   %*c%s  --->", indent + 1, ' ', prompt);
307
308                                 cprint_done();
309                                 if (single_menu_mode && menu->data)
310                                         goto conf_childs;
311                                 return;
312                         default:
313                                 if (prompt) {
314                                         child_count++;
315                                         cprint(":%p", menu);
316                                         cprint("---%*c%s", indent + 1, ' ', prompt);
317                                 }
318                         }
319                 } else
320                         doint = 0;
321                 goto conf_childs;
322         }
323
324         type = sym_get_type(sym);
325         if (sym_is_choice(sym)) {
326                 struct symbol *def_sym = sym_get_choice_value(sym);
327                 struct menu *def_menu = NULL;
328
329                 child_count++;
330                 for (child = menu->list; child; child = child->next) {
331                         if (menu_is_visible(child) && child->sym == def_sym)
332                                 def_menu = child;
333                 }
334
335                 val = sym_get_tristate_value(sym);
336                 if (sym_is_changable(sym)) {
337                         cprint("t%p", menu);
338                         switch (type) {
339                         case S_BOOLEAN:
340                                 cprint1("[%c]", val == no ? ' ' : '*');
341                                 break;
342                         case S_TRISTATE:
343                                 switch (val) {
344                                 case yes: ch = '*'; break;
345                                 case mod: ch = 'M'; break;
346                                 default:  ch = ' '; break;
347                                 }
348                                 cprint1("<%c>", ch);
349                                 break;
350                         }
351                 } else {
352                         cprint("%c%p", def_menu ? 't' : ':', menu);
353                         cprint1("   ");
354                 }
355
356                 cprint1("%*c%s", indent + 1, ' ', menu_get_prompt(menu));
357                 if (val == yes) {
358                         if (def_menu) {
359                                 cprint1(" (%s)", menu_get_prompt(def_menu));
360                                 cprint1("  --->");
361                                 cprint_done();
362                                 if (def_menu->list) {
363                                         indent += 2;
364                                         build_conf(def_menu);
365                                         indent -= 2;
366                                 }
367                         } else
368                                 cprint_done();
369                         return;
370                 }
371                 cprint_done();
372         } else {
373                 if (menu == current_menu) {
374                         cprint(":%p", menu);
375                         cprint("---%*c%s", indent + 1, ' ', menu_get_prompt(menu));
376                         goto conf_childs;
377                 }
378                 child_count++;
379                 val = sym_get_tristate_value(sym);
380                 if (sym_is_choice_value(sym) && val == yes) {
381                         cprint(":%p", menu);
382                         cprint1("   ");
383                 } else {
384                         switch (type) {
385                         case S_BOOLEAN:
386                                 cprint("t%p", menu);
387                                 if (sym_is_changable(sym))
388                                         cprint1("[%c]", val == no ? ' ' : '*');
389                                 else
390                                         cprint1("---");
391                                 break;
392                         case S_TRISTATE:
393                                 cprint("t%p", menu);
394                                 switch (val) {
395                                 case yes: ch = '*'; break;
396                                 case mod: ch = 'M'; break;
397                                 default:  ch = ' '; break;
398                                 }
399                                 if (sym_is_changable(sym))
400                                         cprint1("<%c>", ch);
401                                 else
402                                         cprint1("---");
403                                 break;
404                         default:
405                                 cprint("s%p", menu);
406                                 tmp = cprint1("(%s)", sym_get_string_value(sym));
407                                 tmp = indent - tmp + 4;
408                                 if (tmp < 0)
409                                         tmp = 0;
410                                 cprint1("%*c%s%s", tmp, ' ', menu_get_prompt(menu),
411                                         (sym_has_value(sym) || !sym_is_changable(sym)) ?
412                                         "" : " (NEW)");
413                                 cprint_done();
414                                 goto conf_childs;
415                         }
416                 }
417                 cprint1("%*c%s%s", indent + 1, ' ', menu_get_prompt(menu),
418                         (sym_has_value(sym) || !sym_is_changable(sym)) ?
419                         "" : " (NEW)");
420                 if (menu->prompt->type == P_MENU) {
421                         cprint1("  --->");
422                         cprint_done();
423                         return;
424                 }
425                 cprint_done();
426         }
427
428 conf_childs:
429         indent += doint;
430         for (child = menu->list; child; child = child->next)
431                 build_conf(child);
432         indent -= doint;
433 }
434
435 static void conf(struct menu *menu)
436 {
437         struct menu *submenu;
438         const char *prompt = menu_get_prompt(menu);
439         struct symbol *sym;
440         char active_entry[40];
441         int stat, type, i;
442
443         unlink("lxdialog.scrltmp");
444         active_entry[0] = 0;
445         while (1) {
446                 cprint_init();
447                 cprint("--title");
448                 cprint("%s", prompt ? prompt : "Main Menu");
449                 cprint("--menu");
450                 cprint(menu_instructions);
451                 cprint("%d", rows);
452                 cprint("%d", cols);
453                 cprint("%d", rows - 10);
454                 cprint("%s", active_entry);
455                 current_menu = menu;
456                 build_conf(menu);
457                 if (!child_count)
458                         break;
459                 if (menu == &rootmenu) {
460                         cprint(":");
461                         cprint("--- ");
462                         cprint("L");
463                         cprint("    Load an Alternate Configuration File");
464                         cprint("S");
465                         cprint("    Save Configuration to an Alternate File");
466                 }
467                 stat = exec_conf();
468                 if (stat < 0)
469                         continue;
470
471                 if (stat == 1 || stat == 255)
472                         break;
473
474                 type = input_buf[0];
475                 if (!type)
476                         continue;
477
478                 for (i = 0; input_buf[i] && !isspace(input_buf[i]); i++)
479                         ;
480                 if (i >= sizeof(active_entry))
481                         i = sizeof(active_entry) - 1;
482                 input_buf[i] = 0;
483                 strcpy(active_entry, input_buf);
484
485                 sym = NULL;
486                 submenu = NULL;
487                 if (sscanf(input_buf + 1, "%p", &submenu) == 1)
488                         sym = submenu->sym;
489
490                 switch (stat) {
491                 case 0:
492                         switch (type) {
493                         case 'm':
494                                 if (single_menu_mode)
495                                         submenu->data = (void *) (long) !submenu->data;
496                                 else
497                                         conf(submenu);
498                                 break;
499                         case 't':
500                                 if (sym_is_choice(sym) && sym_get_tristate_value(sym) == yes)
501                                         conf_choice(submenu);
502                                 else if (submenu->prompt->type == P_MENU)
503                                         conf(submenu);
504                                 break;
505                         case 's':
506                                 conf_string(submenu);
507                                 break;
508                         case 'L':
509                                 conf_load();
510                                 break;
511                         case 'S':
512                                 conf_save();
513                                 break;
514                         }
515                         break;
516                 case 2:
517                         if (sym)
518                                 show_help(submenu);
519                         else
520                                 show_readme();
521                         break;
522                 case 3:
523                         if (type == 't') {
524                                 if (sym_set_tristate_value(sym, yes))
525                                         break;
526                                 if (sym_set_tristate_value(sym, mod))
527                                         show_textbox(NULL, setmod_text, 6, 74);
528                         }
529                         break;
530                 case 4:
531                         if (type == 't')
532                                 sym_set_tristate_value(sym, no);
533                         break;
534                 case 5:
535                         if (type == 't')
536                                 sym_set_tristate_value(sym, mod);
537                         break;
538                 case 6:
539                         if (type == 't')
540                                 sym_toggle_tristate_value(sym);
541                         else if (type == 'm')
542                                 conf(submenu);
543                         break;
544                 }
545         }
546 }
547
548 static void show_textbox(const char *title, const char *text, int r, int c)
549 {
550         int fd;
551
552         fd = creat(".help.tmp", 0777);
553         write(fd, text, strlen(text));
554         close(fd);
555         do {
556                 cprint_init();
557                 if (title) {
558                         cprint("--title");
559                         cprint("%s", title);
560                 }
561                 cprint("--textbox");
562                 cprint(".help.tmp");
563                 cprint("%d", r);
564                 cprint("%d", c);
565         } while (exec_conf() < 0);
566         unlink(".help.tmp");
567 }
568
569 static void show_helptext(const char *title, const char *text)
570 {
571         show_textbox(title, text, rows, cols);
572 }
573
574 static void show_help(struct menu *menu)
575 {
576         const char *help;
577         char *helptext;
578         struct symbol *sym = menu->sym;
579
580         help = sym->help;
581         if (!help)
582                 help = nohelp_text;
583         if (sym->name) {
584                 helptext = malloc(strlen(sym->name) + strlen(help) + 16);
585                 sprintf(helptext, "CONFIG_%s:\n\n%s", sym->name, help);
586                 show_helptext(menu_get_prompt(menu), helptext);
587                 free(helptext);
588         } else
589                 show_helptext(menu_get_prompt(menu), help);
590 }
591
592 static void show_readme(void)
593 {
594         do {
595                 cprint_init();
596                 cprint("--textbox");
597                 cprint("scripts/README.Menuconfig");
598                 cprint("%d", rows);
599                 cprint("%d", cols);
600         } while (exec_conf() == -1);
601 }
602
603 static void conf_choice(struct menu *menu)
604 {
605         const char *prompt = menu_get_prompt(menu);
606         struct menu *child;
607         struct symbol *active;
608         int stat;
609
610         active = sym_get_choice_value(menu->sym);
611         while (1) {
612                 cprint_init();
613                 cprint("--title");
614                 cprint("%s", prompt ? prompt : "Main Menu");
615                 cprint("--radiolist");
616                 cprint(radiolist_instructions);
617                 cprint("15");
618                 cprint("70");
619                 cprint("6");
620
621                 current_menu = menu;
622                 for (child = menu->list; child; child = child->next) {
623                         if (!menu_is_visible(child))
624                                 continue;
625                         cprint("%p", child);
626                         cprint("%s", menu_get_prompt(child));
627                         if (child->sym == sym_get_choice_value(menu->sym))
628                                 cprint("ON");
629                         else if (child->sym == active)
630                                 cprint("SELECTED");
631                         else
632                                 cprint("OFF");
633                 }
634
635                 stat = exec_conf();
636                 switch (stat) {
637                 case 0:
638                         if (sscanf(input_buf, "%p", &child) != 1)
639                                 break;
640                         sym_set_tristate_value(child->sym, yes);
641                         return;
642                 case 1:
643                         if (sscanf(input_buf, "%p", &child) == 1) {
644                                 show_help(child);
645                                 active = child->sym;
646                         } else
647                                 show_help(menu);
648                         break;
649                 case 255:
650                         return;
651                 }
652         }
653 }
654
655 static void conf_string(struct menu *menu)
656 {
657         const char *prompt = menu_get_prompt(menu);
658         int stat;
659
660         while (1) {
661                 cprint_init();
662                 cprint("--title");
663                 cprint("%s", prompt ? prompt : "Main Menu");
664                 cprint("--inputbox");
665                 switch (sym_get_type(menu->sym)) {
666                 case S_INT:
667                         cprint(inputbox_instructions_int);
668                         break;
669                 case S_HEX:
670                         cprint(inputbox_instructions_hex);
671                         break;
672                 case S_STRING:
673                         cprint(inputbox_instructions_string);
674                         break;
675                 default:
676                         /* panic? */;
677                 }
678                 cprint("10");
679                 cprint("75");
680                 cprint("%s", sym_get_string_value(menu->sym));
681                 stat = exec_conf();
682                 switch (stat) {
683                 case 0:
684                         if (sym_set_string_value(menu->sym, input_buf))
685                                 return;
686                         show_textbox(NULL, "You have made an invalid entry.", 5, 43);
687                         break;
688                 case 1:
689                         show_help(menu);
690                         break;
691                 case 255:
692                         return;
693                 }
694         }
695 }
696
697 static void conf_load(void)
698 {
699         int stat;
700
701         while (1) {
702                 cprint_init();
703                 cprint("--inputbox");
704                 cprint(load_config_text);
705                 cprint("11");
706                 cprint("55");
707                 cprint("%s", filename);
708                 stat = exec_conf();
709                 switch(stat) {
710                 case 0:
711                         if (!input_buf[0])
712                                 return;
713                         if (!conf_read(input_buf))
714                                 return;
715                         show_textbox(NULL, "File does not exist!", 5, 38);
716                         break;
717                 case 1:
718                         show_helptext("Load Alternate Configuration", load_config_help);
719                         break;
720                 case 255:
721                         return;
722                 }
723         }
724 }
725
726 static void conf_save(void)
727 {
728         int stat;
729
730         while (1) {
731                 cprint_init();
732                 cprint("--inputbox");
733                 cprint(save_config_text);
734                 cprint("11");
735                 cprint("55");
736                 cprint("%s", filename);
737                 stat = exec_conf();
738                 switch(stat) {
739                 case 0:
740                         if (!input_buf[0])
741                                 return;
742                         if (!conf_write(input_buf))
743                                 return;
744                         show_textbox(NULL, "Can't create file!  Probably a nonexistent directory.", 5, 60);
745                         break;
746                 case 1:
747                         show_helptext("Save Alternate Configuration", save_config_help);
748                         break;
749                 case 255:
750                         return;
751                 }
752         }
753 }
754
755 static void conf_cleanup(void)
756 {
757         tcsetattr(1, TCSAFLUSH, &ios_org);
758         unlink(".help.tmp");
759         unlink("lxdialog.scrltmp");
760 }
761
762 int main(int ac, char **av)
763 {
764         struct symbol *sym;
765         char *mode;
766         int stat;
767
768         conf_parse(av[1]);
769         conf_read(NULL);
770
771         sym = sym_lookup("KERNELRELEASE", 0);
772         sym_calc_value(sym);
773         sprintf(menu_backtitle, "Linux Kernel v%s Configuration",
774                 sym_get_string_value(sym));
775
776         mode = getenv("MENUCONFIG_MODE");
777         if (mode) {
778                 if (!strcasecmp(mode, "single_menu"))
779                         single_menu_mode = 1;
780         }
781
782         tcgetattr(1, &ios_org);
783         atexit(conf_cleanup);
784         init_wsize();
785         conf(&rootmenu);
786
787         do {
788                 cprint_init();
789                 cprint("--yesno");
790                 cprint("Do you wish to save your new kernel configuration?");
791                 cprint("5");
792                 cprint("60");
793                 stat = exec_conf();
794         } while (stat < 0);
795
796         if (stat == 0) {
797                 if (conf_write(NULL)) {
798                         fprintf(stderr, "\n\n"
799                                 "Error during writing of the kernel configuration.\n"
800                                 "Your kernel configuration changes were NOT saved."
801                                 "\n\n");
802                         return 1;
803                 }
804                 printf("\n\n"
805                         "*** End of Linux kernel configuration.\n"
806                         "*** Execute 'make' to build the kernel or try 'make help'."
807                         "\n\n");
808         } else {
809                 fprintf(stderr, "\n\n"
810                         "Your kernel configuration changes were NOT saved."
811                         "\n\n");
812         }
813
814         return 0;
815 }