Linux-2.6.12-rc2
[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 mconf_readme[] =
27 "Overview\n"
28 "--------\n"
29 "Some kernel features may be built directly into the kernel.\n"
30 "Some may be made into loadable runtime modules.  Some features\n"
31 "may be completely removed altogether.  There are also certain\n"
32 "kernel parameters which are not really features, but must be\n"
33 "entered in as decimal or hexadecimal numbers or possibly text.\n"
34 "\n"
35 "Menu items beginning with [*], <M> or [ ] represent features\n"
36 "configured to be built in, modularized or removed respectively.\n"
37 "Pointed brackets <> represent module capable features.\n"
38 "\n"
39 "To change any of these features, highlight it with the cursor\n"
40 "keys and press <Y> to build it in, <M> to make it a module or\n"
41 "<N> to removed it.  You may also press the <Space Bar> to cycle\n"
42 "through the available options (ie. Y->N->M->Y).\n"
43 "\n"
44 "Some additional keyboard hints:\n"
45 "\n"
46 "Menus\n"
47 "----------\n"
48 "o  Use the Up/Down arrow keys (cursor keys) to highlight the item\n"
49 "   you wish to change or submenu wish to select and press <Enter>.\n"
50 "   Submenus are designated by \"--->\".\n"
51 "\n"
52 "   Shortcut: Press the option's highlighted letter (hotkey).\n"
53 "             Pressing a hotkey more than once will sequence\n"
54 "             through all visible items which use that hotkey.\n"
55 "\n"
56 "   You may also use the <PAGE UP> and <PAGE DOWN> keys to scroll\n"
57 "   unseen options into view.\n"
58 "\n"
59 "o  To exit a menu use the cursor keys to highlight the <Exit> button\n"
60 "   and press <ENTER>.\n"
61 "\n"
62 "   Shortcut: Press <ESC><ESC> or <E> or <X> if there is no hotkey\n"
63 "             using those letters.  You may press a single <ESC>, but\n"
64 "             there is a delayed response which you may find annoying.\n"
65 "\n"
66 "   Also, the <TAB> and cursor keys will cycle between <Select>,\n"
67 "   <Exit> and <Help>\n"
68 "\n"
69 "o  To get help with an item, use the cursor keys to highlight <Help>\n"
70 "   and Press <ENTER>.\n"
71 "\n"
72 "   Shortcut: Press <H> or <?>.\n"
73 "\n"
74 "\n"
75 "Radiolists  (Choice lists)\n"
76 "-----------\n"
77 "o  Use the cursor keys to select the option you wish to set and press\n"
78 "   <S> or the <SPACE BAR>.\n"
79 "\n"
80 "   Shortcut: Press the first letter of the option you wish to set then\n"
81 "             press <S> or <SPACE BAR>.\n"
82 "\n"
83 "o  To see available help for the item, use the cursor keys to highlight\n"
84 "   <Help> and Press <ENTER>.\n"
85 "\n"
86 "   Shortcut: Press <H> or <?>.\n"
87 "\n"
88 "   Also, the <TAB> and cursor keys will cycle between <Select> and\n"
89 "   <Help>\n"
90 "\n"
91 "\n"
92 "Data Entry\n"
93 "-----------\n"
94 "o  Enter the requested information and press <ENTER>\n"
95 "   If you are entering hexadecimal values, it is not necessary to\n"
96 "   add the '0x' prefix to the entry.\n"
97 "\n"
98 "o  For help, use the <TAB> or cursor keys to highlight the help option\n"
99 "   and press <ENTER>.  You can try <TAB><H> as well.\n"
100 "\n"
101 "\n"
102 "Text Box    (Help Window)\n"
103 "--------\n"
104 "o  Use the cursor keys to scroll up/down/left/right.  The VI editor\n"
105 "   keys h,j,k,l function here as do <SPACE BAR> and <B> for those\n"
106 "   who are familiar with less and lynx.\n"
107 "\n"
108 "o  Press <E>, <X>, <Enter> or <Esc><Esc> to exit.\n"
109 "\n"
110 "\n"
111 "Alternate Configuration Files\n"
112 "-----------------------------\n"
113 "Menuconfig supports the use of alternate configuration files for\n"
114 "those who, for various reasons, find it necessary to switch\n"
115 "between different kernel configurations.\n"
116 "\n"
117 "At the end of the main menu you will find two options.  One is\n"
118 "for saving the current configuration to a file of your choosing.\n"
119 "The other option is for loading a previously saved alternate\n"
120 "configuration.\n"
121 "\n"
122 "Even if you don't use alternate configuration files, but you\n"
123 "find during a Menuconfig session that you have completely messed\n"
124 "up your settings, you may use the \"Load Alternate...\" option to\n"
125 "restore your previously saved settings from \".config\" without\n"
126 "restarting Menuconfig.\n"
127 "\n"
128 "Other information\n"
129 "-----------------\n"
130 "If you use Menuconfig in an XTERM window make sure you have your\n"
131 "$TERM variable set to point to a xterm definition which supports color.\n"
132 "Otherwise, Menuconfig will look rather bad.  Menuconfig will not\n"
133 "display correctly in a RXVT window because rxvt displays only one\n"
134 "intensity of color, bright.\n"
135 "\n"
136 "Menuconfig will display larger menus on screens or xterms which are\n"
137 "set to display more than the standard 25 row by 80 column geometry.\n"
138 "In order for this to work, the \"stty size\" command must be able to\n"
139 "display the screen's current row and column geometry.  I STRONGLY\n"
140 "RECOMMEND that you make sure you do NOT have the shell variables\n"
141 "LINES and COLUMNS exported into your environment.  Some distributions\n"
142 "export those variables via /etc/profile.  Some ncurses programs can\n"
143 "become confused when those variables (LINES & COLUMNS) don't reflect\n"
144 "the true screen size.\n"
145 "\n"
146 "Optional personality available\n"
147 "------------------------------\n"
148 "If you prefer to have all of the kernel options listed in a single\n"
149 "menu, rather than the default multimenu hierarchy, run the menuconfig\n"
150 "with MENUCONFIG_MODE environment variable set to single_menu. Example:\n"
151 "\n"
152 "make MENUCONFIG_MODE=single_menu menuconfig\n"
153 "\n"
154 "<Enter> will then unroll the appropriate category, or enfold it if it\n"
155 "is already unrolled.\n"
156 "\n"
157 "Note that this mode can eventually be a little more CPU expensive\n"
158 "(especially with a larger number of unrolled categories) than the\n"
159 "default mode.\n",
160 menu_instructions[] =
161         "Arrow keys navigate the menu.  "
162         "<Enter> selects submenus --->.  "
163         "Highlighted letters are hotkeys.  "
164         "Pressing <Y> includes, <N> excludes, <M> modularizes features.  "
165         "Press <Esc><Esc> to exit, <?> for Help, </> for Search.  "
166         "Legend: [*] built-in  [ ] excluded  <M> module  < > module capable",
167 radiolist_instructions[] =
168         "Use the arrow keys to navigate this window or "
169         "press the hotkey of the item you wish to select "
170         "followed by the <SPACE BAR>. "
171         "Press <?> for additional information about this option.",
172 inputbox_instructions_int[] =
173         "Please enter a decimal value. "
174         "Fractions will not be accepted.  "
175         "Use the <TAB> key to move from the input field to the buttons below it.",
176 inputbox_instructions_hex[] =
177         "Please enter a hexadecimal value. "
178         "Use the <TAB> key to move from the input field to the buttons below it.",
179 inputbox_instructions_string[] =
180         "Please enter a string value. "
181         "Use the <TAB> key to move from the input field to the buttons below it.",
182 setmod_text[] =
183         "This feature depends on another which has been configured as a module.\n"
184         "As a result, this feature will be built as a module.",
185 nohelp_text[] =
186         "There is no help available for this kernel option.\n",
187 load_config_text[] =
188         "Enter the name of the configuration file you wish to load.  "
189         "Accept the name shown to restore the configuration you "
190         "last retrieved.  Leave blank to abort.",
191 load_config_help[] =
192         "\n"
193         "For various reasons, one may wish to keep several different kernel\n"
194         "configurations available on a single machine.\n"
195         "\n"
196         "If you have saved a previous configuration in a file other than the\n"
197         "kernel's default, entering the name of the file here will allow you\n"
198         "to modify that configuration.\n"
199         "\n"
200         "If you are uncertain, then you have probably never used alternate\n"
201         "configuration files.  You should therefor leave this blank to abort.\n",
202 save_config_text[] =
203         "Enter a filename to which this configuration should be saved "
204         "as an alternate.  Leave blank to abort.",
205 save_config_help[] =
206         "\n"
207         "For various reasons, one may wish to keep different kernel\n"
208         "configurations available on a single machine.\n"
209         "\n"
210         "Entering a file name here will allow you to later retrieve, modify\n"
211         "and use the current configuration as an alternate to whatever\n"
212         "configuration options you have selected at that time.\n"
213         "\n"
214         "If you are uncertain what all this means then you should probably\n"
215         "leave this blank.\n",
216 search_help[] =
217         "\n"
218         "Search for CONFIG_ symbols and display their relations.\n"
219         "Example: search for \"^FOO\"\n"
220         "Result:\n"
221         "-----------------------------------------------------------------\n"
222         "Symbol: FOO [=m]\n"
223         "Prompt: Foo bus is used to drive the bar HW\n"
224         "Defined at drivers/pci/Kconfig:47\n"
225         "Depends on: X86_LOCAL_APIC && X86_IO_APIC || IA64\n"
226         "Location:\n"
227         "  -> Bus options (PCI, PCMCIA, EISA, MCA, ISA)\n"
228         "    -> PCI support (PCI [=y])\n"
229         "      -> PCI access mode (<choice> [=y])\n"
230         "Selects: LIBCRC32\n"
231         "Selected by: BAR\n"
232         "-----------------------------------------------------------------\n"
233         "o The line 'Prompt:' shows the text used in the menu structure for\n"
234         "  this CONFIG_ symbol\n"
235         "o The 'Defined at' line tell at what file / line number the symbol\n"
236         "  is defined\n"
237         "o The 'Depends on:' line tell what symbols needs to be defined for\n"
238         "  this symbol to be visible in the menu (selectable)\n"
239         "o The 'Location:' lines tell where in the menu structure this symbol\n"
240         "  is located\n"
241         "    A location followed by a [=y] indicate that this is a selectable\n"
242         "    menu item - and current value is displayed inside brackets.\n"
243         "o The 'Selects:' line tell what symbol will be automatically\n"
244         "  selected if this symbol is selected (y or m)\n"
245         "o The 'Selected by' line tell what symbol has selected this symbol\n"
246         "\n"
247         "Only relevant lines are shown.\n"
248         "\n\n"
249         "Search examples:\n"
250         "Examples: USB  => find all CONFIG_ symbols containing USB\n"
251         "          ^USB => find all CONFIG_ symbols starting with USB\n"
252         "          USB$ => find all CONFIG_ symbols ending with USB\n"
253         "\n";
254
255 static signed char buf[4096], *bufptr = buf;
256 static signed char input_buf[4096];
257 static char filename[PATH_MAX+1] = ".config";
258 static char *args[1024], **argptr = args;
259 static int indent;
260 static struct termios ios_org;
261 static int rows = 0, cols = 0;
262 static struct menu *current_menu;
263 static int child_count;
264 static int do_resize;
265 static int single_menu_mode;
266
267 static void conf(struct menu *menu);
268 static void conf_choice(struct menu *menu);
269 static void conf_string(struct menu *menu);
270 static void conf_load(void);
271 static void conf_save(void);
272 static void show_textbox(const char *title, const char *text, int r, int c);
273 static void show_helptext(const char *title, const char *text);
274 static void show_help(struct menu *menu);
275 static void show_file(const char *filename, const char *title, int r, int c);
276
277 static void cprint_init(void);
278 static int cprint1(const char *fmt, ...);
279 static void cprint_done(void);
280 static int cprint(const char *fmt, ...);
281
282 static void init_wsize(void)
283 {
284         struct winsize ws;
285         char *env;
286
287         if (!ioctl(STDIN_FILENO, TIOCGWINSZ, &ws)) {
288                 rows = ws.ws_row;
289                 cols = ws.ws_col;
290         }
291
292         if (!rows) {
293                 env = getenv("LINES");
294                 if (env)
295                         rows = atoi(env);
296                 if (!rows)
297                         rows = 24;
298         }
299         if (!cols) {
300                 env = getenv("COLUMNS");
301                 if (env)
302                         cols = atoi(env);
303                 if (!cols)
304                         cols = 80;
305         }
306
307         if (rows < 19 || cols < 80) {
308                 fprintf(stderr, "Your display is too small to run Menuconfig!\n");
309                 fprintf(stderr, "It must be at least 19 lines by 80 columns.\n");
310                 exit(1);
311         }
312
313         rows -= 4;
314         cols -= 5;
315 }
316
317 static void cprint_init(void)
318 {
319         bufptr = buf;
320         argptr = args;
321         memset(args, 0, sizeof(args));
322         indent = 0;
323         child_count = 0;
324         cprint("./scripts/lxdialog/lxdialog");
325         cprint("--backtitle");
326         cprint(menu_backtitle);
327 }
328
329 static int cprint1(const char *fmt, ...)
330 {
331         va_list ap;
332         int res;
333
334         if (!*argptr)
335                 *argptr = bufptr;
336         va_start(ap, fmt);
337         res = vsprintf(bufptr, fmt, ap);
338         va_end(ap);
339         bufptr += res;
340
341         return res;
342 }
343
344 static void cprint_done(void)
345 {
346         *bufptr++ = 0;
347         argptr++;
348 }
349
350 static int cprint(const char *fmt, ...)
351 {
352         va_list ap;
353         int res;
354
355         *argptr++ = bufptr;
356         va_start(ap, fmt);
357         res = vsprintf(bufptr, fmt, ap);
358         va_end(ap);
359         bufptr += res;
360         *bufptr++ = 0;
361
362         return res;
363 }
364
365 static void get_prompt_str(struct gstr *r, struct property *prop)
366 {
367         int i, j;
368         struct menu *submenu[8], *menu;
369
370         str_printf(r, "Prompt: %s\n", prop->text);
371         str_printf(r, "  Defined at %s:%d\n", prop->menu->file->name,
372                 prop->menu->lineno);
373         if (!expr_is_yes(prop->visible.expr)) {
374                 str_append(r, "  Depends on: ");
375                 expr_gstr_print(prop->visible.expr, r);
376                 str_append(r, "\n");
377         }
378         menu = prop->menu->parent;
379         for (i = 0; menu != &rootmenu && i < 8; menu = menu->parent)
380                 submenu[i++] = menu;
381         if (i > 0) {
382                 str_printf(r, "  Location:\n");
383                 for (j = 4; --i >= 0; j += 2) {
384                         menu = submenu[i];
385                         str_printf(r, "%*c-> %s", j, ' ', menu_get_prompt(menu));
386                         if (menu->sym) {
387                                 str_printf(r, " (%s [=%s])", menu->sym->name ?
388                                         menu->sym->name : "<choice>",
389                                         sym_get_string_value(menu->sym));
390                         }
391                         str_append(r, "\n");
392                 }
393         }
394 }
395
396 static void get_symbol_str(struct gstr *r, struct symbol *sym)
397 {
398         bool hit;
399         struct property *prop;
400
401         str_printf(r, "Symbol: %s [=%s]\n", sym->name,
402                                        sym_get_string_value(sym));
403         for_all_prompts(sym, prop)
404                 get_prompt_str(r, prop);
405         hit = false;
406         for_all_properties(sym, prop, P_SELECT) {
407                 if (!hit) {
408                         str_append(r, "  Selects: ");
409                         hit = true;
410                 } else
411                         str_printf(r, " && ");
412                 expr_gstr_print(prop->expr, r);
413         }
414         if (hit)
415                 str_append(r, "\n");
416         if (sym->rev_dep.expr) {
417                 str_append(r, "  Selected by: ");
418                 expr_gstr_print(sym->rev_dep.expr, r);
419                 str_append(r, "\n");
420         }
421         str_append(r, "\n\n");
422 }
423
424 static struct gstr get_relations_str(struct symbol **sym_arr)
425 {
426         struct symbol *sym;
427         struct gstr res = str_new();
428         int i;
429
430         for (i = 0; sym_arr && (sym = sym_arr[i]); i++)
431                 get_symbol_str(&res, sym);
432         if (!i)
433                 str_append(&res, "No matches found.\n");
434         return res;
435 }
436
437 pid_t pid;
438
439 static void winch_handler(int sig)
440 {
441         if (!do_resize) {
442                 kill(pid, SIGINT);
443                 do_resize = 1;
444         }
445 }
446
447 static int exec_conf(void)
448 {
449         int pipefd[2], stat, size;
450         struct sigaction sa;
451         sigset_t sset, osset;
452
453         sigemptyset(&sset);
454         sigaddset(&sset, SIGINT);
455         sigprocmask(SIG_BLOCK, &sset, &osset);
456
457         signal(SIGINT, SIG_DFL);
458
459         sa.sa_handler = winch_handler;
460         sigemptyset(&sa.sa_mask);
461         sa.sa_flags = SA_RESTART;
462         sigaction(SIGWINCH, &sa, NULL);
463
464         *argptr++ = NULL;
465
466         pipe(pipefd);
467         pid = fork();
468         if (pid == 0) {
469                 sigprocmask(SIG_SETMASK, &osset, NULL);
470                 dup2(pipefd[1], 2);
471                 close(pipefd[0]);
472                 close(pipefd[1]);
473                 execv(args[0], args);
474                 _exit(EXIT_FAILURE);
475         }
476
477         close(pipefd[1]);
478         bufptr = input_buf;
479         while (1) {
480                 size = input_buf + sizeof(input_buf) - bufptr;
481                 size = read(pipefd[0], bufptr, size);
482                 if (size <= 0) {
483                         if (size < 0) {
484                                 if (errno == EINTR || errno == EAGAIN)
485                                         continue;
486                                 perror("read");
487                         }
488                         break;
489                 }
490                 bufptr += size;
491         }
492         *bufptr++ = 0;
493         close(pipefd[0]);
494         waitpid(pid, &stat, 0);
495
496         if (do_resize) {
497                 init_wsize();
498                 do_resize = 0;
499                 sigprocmask(SIG_SETMASK, &osset, NULL);
500                 return -1;
501         }
502         if (WIFSIGNALED(stat)) {
503                 printf("\finterrupted(%d)\n", WTERMSIG(stat));
504                 exit(1);
505         }
506 #if 0
507         printf("\fexit state: %d\nexit data: '%s'\n", WEXITSTATUS(stat), input_buf);
508         sleep(1);
509 #endif
510         sigpending(&sset);
511         if (sigismember(&sset, SIGINT)) {
512                 printf("\finterrupted\n");
513                 exit(1);
514         }
515         sigprocmask(SIG_SETMASK, &osset, NULL);
516
517         return WEXITSTATUS(stat);
518 }
519
520 static void search_conf(void)
521 {
522         struct symbol **sym_arr;
523         int stat;
524         struct gstr res;
525
526 again:
527         cprint_init();
528         cprint("--title");
529         cprint("Search Configuration Parameter");
530         cprint("--inputbox");
531         cprint("Enter Keyword");
532         cprint("10");
533         cprint("75");
534         cprint("");
535         stat = exec_conf();
536         if (stat < 0)
537                 goto again;
538         switch (stat) {
539         case 0:
540                 break;
541         case 1:
542                 show_helptext("Search Configuration", search_help);
543                 goto again;
544         default:
545                 return;
546         }
547
548         sym_arr = sym_re_search(input_buf);
549         res = get_relations_str(sym_arr);
550         free(sym_arr);
551         show_textbox("Search Results", str_get(&res), 0, 0);
552         str_free(&res);
553 }
554
555 static void build_conf(struct menu *menu)
556 {
557         struct symbol *sym;
558         struct property *prop;
559         struct menu *child;
560         int type, tmp, doint = 2;
561         tristate val;
562         char ch;
563
564         if (!menu_is_visible(menu))
565                 return;
566
567         sym = menu->sym;
568         prop = menu->prompt;
569         if (!sym) {
570                 if (prop && menu != current_menu) {
571                         const char *prompt = menu_get_prompt(menu);
572                         switch (prop->type) {
573                         case P_MENU:
574                                 child_count++;
575                                 cprint("m%p", menu);
576
577                                 if (single_menu_mode) {
578                                         cprint1("%s%*c%s",
579                                                 menu->data ? "-->" : "++>",
580                                                 indent + 1, ' ', prompt);
581                                 } else
582                                         cprint1("   %*c%s  --->", indent + 1, ' ', prompt);
583
584                                 cprint_done();
585                                 if (single_menu_mode && menu->data)
586                                         goto conf_childs;
587                                 return;
588                         default:
589                                 if (prompt) {
590                                         child_count++;
591                                         cprint(":%p", menu);
592                                         cprint("---%*c%s", indent + 1, ' ', prompt);
593                                 }
594                         }
595                 } else
596                         doint = 0;
597                 goto conf_childs;
598         }
599
600         type = sym_get_type(sym);
601         if (sym_is_choice(sym)) {
602                 struct symbol *def_sym = sym_get_choice_value(sym);
603                 struct menu *def_menu = NULL;
604
605                 child_count++;
606                 for (child = menu->list; child; child = child->next) {
607                         if (menu_is_visible(child) && child->sym == def_sym)
608                                 def_menu = child;
609                 }
610
611                 val = sym_get_tristate_value(sym);
612                 if (sym_is_changable(sym)) {
613                         cprint("t%p", menu);
614                         switch (type) {
615                         case S_BOOLEAN:
616                                 cprint1("[%c]", val == no ? ' ' : '*');
617                                 break;
618                         case S_TRISTATE:
619                                 switch (val) {
620                                 case yes: ch = '*'; break;
621                                 case mod: ch = 'M'; break;
622                                 default:  ch = ' '; break;
623                                 }
624                                 cprint1("<%c>", ch);
625                                 break;
626                         }
627                 } else {
628                         cprint("%c%p", def_menu ? 't' : ':', menu);
629                         cprint1("   ");
630                 }
631
632                 cprint1("%*c%s", indent + 1, ' ', menu_get_prompt(menu));
633                 if (val == yes) {
634                         if (def_menu) {
635                                 cprint1(" (%s)", menu_get_prompt(def_menu));
636                                 cprint1("  --->");
637                                 cprint_done();
638                                 if (def_menu->list) {
639                                         indent += 2;
640                                         build_conf(def_menu);
641                                         indent -= 2;
642                                 }
643                         } else
644                                 cprint_done();
645                         return;
646                 }
647                 cprint_done();
648         } else {
649                 if (menu == current_menu) {
650                         cprint(":%p", menu);
651                         cprint("---%*c%s", indent + 1, ' ', menu_get_prompt(menu));
652                         goto conf_childs;
653                 }
654                 child_count++;
655                 val = sym_get_tristate_value(sym);
656                 if (sym_is_choice_value(sym) && val == yes) {
657                         cprint(":%p", menu);
658                         cprint1("   ");
659                 } else {
660                         switch (type) {
661                         case S_BOOLEAN:
662                                 cprint("t%p", menu);
663                                 if (sym_is_changable(sym))
664                                         cprint1("[%c]", val == no ? ' ' : '*');
665                                 else
666                                         cprint1("---");
667                                 break;
668                         case S_TRISTATE:
669                                 cprint("t%p", menu);
670                                 switch (val) {
671                                 case yes: ch = '*'; break;
672                                 case mod: ch = 'M'; break;
673                                 default:  ch = ' '; break;
674                                 }
675                                 if (sym_is_changable(sym))
676                                         cprint1("<%c>", ch);
677                                 else
678                                         cprint1("---");
679                                 break;
680                         default:
681                                 cprint("s%p", menu);
682                                 tmp = cprint1("(%s)", sym_get_string_value(sym));
683                                 tmp = indent - tmp + 4;
684                                 if (tmp < 0)
685                                         tmp = 0;
686                                 cprint1("%*c%s%s", tmp, ' ', menu_get_prompt(menu),
687                                         (sym_has_value(sym) || !sym_is_changable(sym)) ?
688                                         "" : " (NEW)");
689                                 cprint_done();
690                                 goto conf_childs;
691                         }
692                 }
693                 cprint1("%*c%s%s", indent + 1, ' ', menu_get_prompt(menu),
694                         (sym_has_value(sym) || !sym_is_changable(sym)) ?
695                         "" : " (NEW)");
696                 if (menu->prompt->type == P_MENU) {
697                         cprint1("  --->");
698                         cprint_done();
699                         return;
700                 }
701                 cprint_done();
702         }
703
704 conf_childs:
705         indent += doint;
706         for (child = menu->list; child; child = child->next)
707                 build_conf(child);
708         indent -= doint;
709 }
710
711 static void conf(struct menu *menu)
712 {
713         struct menu *submenu;
714         const char *prompt = menu_get_prompt(menu);
715         struct symbol *sym;
716         char active_entry[40];
717         int stat, type, i;
718
719         unlink("lxdialog.scrltmp");
720         active_entry[0] = 0;
721         while (1) {
722                 cprint_init();
723                 cprint("--title");
724                 cprint("%s", prompt ? prompt : "Main Menu");
725                 cprint("--menu");
726                 cprint(menu_instructions);
727                 cprint("%d", rows);
728                 cprint("%d", cols);
729                 cprint("%d", rows - 10);
730                 cprint("%s", active_entry);
731                 current_menu = menu;
732                 build_conf(menu);
733                 if (!child_count)
734                         break;
735                 if (menu == &rootmenu) {
736                         cprint(":");
737                         cprint("--- ");
738                         cprint("L");
739                         cprint("    Load an Alternate Configuration File");
740                         cprint("S");
741                         cprint("    Save Configuration to an Alternate File");
742                 }
743                 stat = exec_conf();
744                 if (stat < 0)
745                         continue;
746
747                 if (stat == 1 || stat == 255)
748                         break;
749
750                 type = input_buf[0];
751                 if (!type)
752                         continue;
753
754                 for (i = 0; input_buf[i] && !isspace(input_buf[i]); i++)
755                         ;
756                 if (i >= sizeof(active_entry))
757                         i = sizeof(active_entry) - 1;
758                 input_buf[i] = 0;
759                 strcpy(active_entry, input_buf);
760
761                 sym = NULL;
762                 submenu = NULL;
763                 if (sscanf(input_buf + 1, "%p", &submenu) == 1)
764                         sym = submenu->sym;
765
766                 switch (stat) {
767                 case 0:
768                         switch (type) {
769                         case 'm':
770                                 if (single_menu_mode)
771                                         submenu->data = (void *) (long) !submenu->data;
772                                 else
773                                         conf(submenu);
774                                 break;
775                         case 't':
776                                 if (sym_is_choice(sym) && sym_get_tristate_value(sym) == yes)
777                                         conf_choice(submenu);
778                                 else if (submenu->prompt->type == P_MENU)
779                                         conf(submenu);
780                                 break;
781                         case 's':
782                                 conf_string(submenu);
783                                 break;
784                         case 'L':
785                                 conf_load();
786                                 break;
787                         case 'S':
788                                 conf_save();
789                                 break;
790                         }
791                         break;
792                 case 2:
793                         if (sym)
794                                 show_help(submenu);
795                         else
796                                 show_helptext("README", mconf_readme);
797                         break;
798                 case 3:
799                         if (type == 't') {
800                                 if (sym_set_tristate_value(sym, yes))
801                                         break;
802                                 if (sym_set_tristate_value(sym, mod))
803                                         show_textbox(NULL, setmod_text, 6, 74);
804                         }
805                         break;
806                 case 4:
807                         if (type == 't')
808                                 sym_set_tristate_value(sym, no);
809                         break;
810                 case 5:
811                         if (type == 't')
812                                 sym_set_tristate_value(sym, mod);
813                         break;
814                 case 6:
815                         if (type == 't')
816                                 sym_toggle_tristate_value(sym);
817                         else if (type == 'm')
818                                 conf(submenu);
819                         break;
820                 case 7:
821                         search_conf();
822                         break;
823                 }
824         }
825 }
826
827 static void show_textbox(const char *title, const char *text, int r, int c)
828 {
829         int fd;
830
831         fd = creat(".help.tmp", 0777);
832         write(fd, text, strlen(text));
833         close(fd);
834         show_file(".help.tmp", title, r, c);
835         unlink(".help.tmp");
836 }
837
838 static void show_helptext(const char *title, const char *text)
839 {
840         show_textbox(title, text, 0, 0);
841 }
842
843 static void show_help(struct menu *menu)
844 {
845         struct gstr help = str_new();
846         struct symbol *sym = menu->sym;
847
848         if (sym->help)
849         {
850                 if (sym->name) {
851                         str_printf(&help, "CONFIG_%s:\n\n", sym->name);
852                         str_append(&help, sym->help);
853                         str_append(&help, "\n");
854                 }
855         } else {
856                 str_append(&help, nohelp_text);
857         }
858         get_symbol_str(&help, sym);
859         show_helptext(menu_get_prompt(menu), str_get(&help));
860         str_free(&help);
861 }
862
863 static void show_file(const char *filename, const char *title, int r, int c)
864 {
865         do {
866                 cprint_init();
867                 if (title) {
868                         cprint("--title");
869                         cprint("%s", title);
870                 }
871                 cprint("--textbox");
872                 cprint("%s", filename);
873                 cprint("%d", r ? r : rows);
874                 cprint("%d", c ? c : cols);
875         } while (exec_conf() < 0);
876 }
877
878 static void conf_choice(struct menu *menu)
879 {
880         const char *prompt = menu_get_prompt(menu);
881         struct menu *child;
882         struct symbol *active;
883         int stat;
884
885         active = sym_get_choice_value(menu->sym);
886         while (1) {
887                 cprint_init();
888                 cprint("--title");
889                 cprint("%s", prompt ? prompt : "Main Menu");
890                 cprint("--radiolist");
891                 cprint(radiolist_instructions);
892                 cprint("15");
893                 cprint("70");
894                 cprint("6");
895
896                 current_menu = menu;
897                 for (child = menu->list; child; child = child->next) {
898                         if (!menu_is_visible(child))
899                                 continue;
900                         cprint("%p", child);
901                         cprint("%s", menu_get_prompt(child));
902                         if (child->sym == sym_get_choice_value(menu->sym))
903                                 cprint("ON");
904                         else if (child->sym == active)
905                                 cprint("SELECTED");
906                         else
907                                 cprint("OFF");
908                 }
909
910                 stat = exec_conf();
911                 switch (stat) {
912                 case 0:
913                         if (sscanf(input_buf, "%p", &child) != 1)
914                                 break;
915                         sym_set_tristate_value(child->sym, yes);
916                         return;
917                 case 1:
918                         if (sscanf(input_buf, "%p", &child) == 1) {
919                                 show_help(child);
920                                 active = child->sym;
921                         } else
922                                 show_help(menu);
923                         break;
924                 case 255:
925                         return;
926                 }
927         }
928 }
929
930 static void conf_string(struct menu *menu)
931 {
932         const char *prompt = menu_get_prompt(menu);
933         int stat;
934
935         while (1) {
936                 cprint_init();
937                 cprint("--title");
938                 cprint("%s", prompt ? prompt : "Main Menu");
939                 cprint("--inputbox");
940                 switch (sym_get_type(menu->sym)) {
941                 case S_INT:
942                         cprint(inputbox_instructions_int);
943                         break;
944                 case S_HEX:
945                         cprint(inputbox_instructions_hex);
946                         break;
947                 case S_STRING:
948                         cprint(inputbox_instructions_string);
949                         break;
950                 default:
951                         /* panic? */;
952                 }
953                 cprint("10");
954                 cprint("75");
955                 cprint("%s", sym_get_string_value(menu->sym));
956                 stat = exec_conf();
957                 switch (stat) {
958                 case 0:
959                         if (sym_set_string_value(menu->sym, input_buf))
960                                 return;
961                         show_textbox(NULL, "You have made an invalid entry.", 5, 43);
962                         break;
963                 case 1:
964                         show_help(menu);
965                         break;
966                 case 255:
967                         return;
968                 }
969         }
970 }
971
972 static void conf_load(void)
973 {
974         int stat;
975
976         while (1) {
977                 cprint_init();
978                 cprint("--inputbox");
979                 cprint(load_config_text);
980                 cprint("11");
981                 cprint("55");
982                 cprint("%s", filename);
983                 stat = exec_conf();
984                 switch(stat) {
985                 case 0:
986                         if (!input_buf[0])
987                                 return;
988                         if (!conf_read(input_buf))
989                                 return;
990                         show_textbox(NULL, "File does not exist!", 5, 38);
991                         break;
992                 case 1:
993                         show_helptext("Load Alternate Configuration", load_config_help);
994                         break;
995                 case 255:
996                         return;
997                 }
998         }
999 }
1000
1001 static void conf_save(void)
1002 {
1003         int stat;
1004
1005         while (1) {
1006                 cprint_init();
1007                 cprint("--inputbox");
1008                 cprint(save_config_text);
1009                 cprint("11");
1010                 cprint("55");
1011                 cprint("%s", filename);
1012                 stat = exec_conf();
1013                 switch(stat) {
1014                 case 0:
1015                         if (!input_buf[0])
1016                                 return;
1017                         if (!conf_write(input_buf))
1018                                 return;
1019                         show_textbox(NULL, "Can't create file!  Probably a nonexistent directory.", 5, 60);
1020                         break;
1021                 case 1:
1022                         show_helptext("Save Alternate Configuration", save_config_help);
1023                         break;
1024                 case 255:
1025                         return;
1026                 }
1027         }
1028 }
1029
1030 static void conf_cleanup(void)
1031 {
1032         tcsetattr(1, TCSAFLUSH, &ios_org);
1033         unlink(".help.tmp");
1034         unlink("lxdialog.scrltmp");
1035 }
1036
1037 int main(int ac, char **av)
1038 {
1039         struct symbol *sym;
1040         char *mode;
1041         int stat;
1042
1043         conf_parse(av[1]);
1044         conf_read(NULL);
1045
1046         sym = sym_lookup("KERNELRELEASE", 0);
1047         sym_calc_value(sym);
1048         sprintf(menu_backtitle, "Linux Kernel v%s Configuration",
1049                 sym_get_string_value(sym));
1050
1051         mode = getenv("MENUCONFIG_MODE");
1052         if (mode) {
1053                 if (!strcasecmp(mode, "single_menu"))
1054                         single_menu_mode = 1;
1055         }
1056
1057         tcgetattr(1, &ios_org);
1058         atexit(conf_cleanup);
1059         init_wsize();
1060         conf(&rootmenu);
1061
1062         do {
1063                 cprint_init();
1064                 cprint("--yesno");
1065                 cprint("Do you wish to save your new kernel configuration?");
1066                 cprint("5");
1067                 cprint("60");
1068                 stat = exec_conf();
1069         } while (stat < 0);
1070
1071         if (stat == 0) {
1072                 if (conf_write(NULL)) {
1073                         fprintf(stderr, "\n\n"
1074                                 "Error during writing of the kernel configuration.\n"
1075                                 "Your kernel configuration changes were NOT saved."
1076                                 "\n\n");
1077                         return 1;
1078                 }
1079                 printf("\n\n"
1080                         "*** End of Linux kernel configuration.\n"
1081                         "*** Execute 'make' to build the kernel or try 'make help'."
1082                         "\n\n");
1083         } else {
1084                 fprintf(stderr, "\n\n"
1085                         "Your kernel configuration changes were NOT saved."
1086                         "\n\n");
1087         }
1088
1089         return 0;
1090 }