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