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