Merge with /pub/scm/linux/kernel/git/torvalds/linux-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         "Regular expressions are allowed.\n"
223         "Example: search for \"^FOO\"\n"
224         "Result:\n"
225         "-----------------------------------------------------------------\n"
226         "Symbol: FOO [=m]\n"
227         "Prompt: Foo bus is used to drive the bar HW\n"
228         "Defined at drivers/pci/Kconfig:47\n"
229         "Depends on: X86_LOCAL_APIC && X86_IO_APIC || IA64\n"
230         "Location:\n"
231         "  -> Bus options (PCI, PCMCIA, EISA, MCA, ISA)\n"
232         "    -> PCI support (PCI [=y])\n"
233         "      -> PCI access mode (<choice> [=y])\n"
234         "Selects: LIBCRC32\n"
235         "Selected by: BAR\n"
236         "-----------------------------------------------------------------\n"
237         "o The line 'Prompt:' shows the text used in the menu structure for\n"
238         "  this CONFIG_ symbol\n"
239         "o The 'Defined at' line tell at what file / line number the symbol\n"
240         "  is defined\n"
241         "o The 'Depends on:' line tell what symbols needs to be defined for\n"
242         "  this symbol to be visible in the menu (selectable)\n"
243         "o The 'Location:' lines tell where in the menu structure this symbol\n"
244         "  is located\n"
245         "    A location followed by a [=y] indicate that this is a selectable\n"
246         "    menu item - and current value is displayed inside brackets.\n"
247         "o The 'Selects:' line tell what symbol will be automatically\n"
248         "  selected if this symbol is selected (y or m)\n"
249         "o The 'Selected by' line tell what symbol has selected this symbol\n"
250         "\n"
251         "Only relevant lines are shown.\n"
252         "\n\n"
253         "Search examples:\n"
254         "Examples: USB  => find all CONFIG_ symbols containing USB\n"
255         "          ^USB => find all CONFIG_ symbols starting with USB\n"
256         "          USB$ => find all CONFIG_ symbols ending with USB\n"
257         "\n");
258
259 static char buf[4096], *bufptr = buf;
260 static char input_buf[4096];
261 static char filename[PATH_MAX+1] = ".config";
262 static char *args[1024], **argptr = args;
263 static int indent;
264 static struct termios ios_org;
265 static int rows = 0, cols = 0;
266 static struct menu *current_menu;
267 static int child_count;
268 static int do_resize;
269 static int single_menu_mode;
270
271 static void conf(struct menu *menu);
272 static void conf_choice(struct menu *menu);
273 static void conf_string(struct menu *menu);
274 static void conf_load(void);
275 static void conf_save(void);
276 static void show_textbox(const char *title, const char *text, int r, int c);
277 static void show_helptext(const char *title, const char *text);
278 static void show_help(struct menu *menu);
279 static void show_file(const char *filename, const char *title, int r, int c);
280
281 static void cprint_init(void);
282 static int cprint1(const char *fmt, ...);
283 static void cprint_done(void);
284 static int cprint(const char *fmt, ...);
285
286 static void init_wsize(void)
287 {
288         struct winsize ws;
289         char *env;
290
291         if (!ioctl(STDIN_FILENO, TIOCGWINSZ, &ws)) {
292                 rows = ws.ws_row;
293                 cols = ws.ws_col;
294         }
295
296         if (!rows) {
297                 env = getenv("LINES");
298                 if (env)
299                         rows = atoi(env);
300                 if (!rows)
301                         rows = 24;
302         }
303         if (!cols) {
304                 env = getenv("COLUMNS");
305                 if (env)
306                         cols = atoi(env);
307                 if (!cols)
308                         cols = 80;
309         }
310
311         if (rows < 19 || cols < 80) {
312                 fprintf(stderr, N_("Your display is too small to run Menuconfig!\n"));
313                 fprintf(stderr, N_("It must be at least 19 lines by 80 columns.\n"));
314                 exit(1);
315         }
316
317         rows -= 4;
318         cols -= 5;
319 }
320
321 static void cprint_init(void)
322 {
323         bufptr = buf;
324         argptr = args;
325         memset(args, 0, sizeof(args));
326         indent = 0;
327         child_count = 0;
328         cprint("./scripts/kconfig/lxdialog/lxdialog");
329         cprint("--backtitle");
330         cprint(menu_backtitle);
331 }
332
333 static int cprint1(const char *fmt, ...)
334 {
335         va_list ap;
336         int res;
337
338         if (!*argptr)
339                 *argptr = bufptr;
340         va_start(ap, fmt);
341         res = vsprintf(bufptr, fmt, ap);
342         va_end(ap);
343         bufptr += res;
344
345         return res;
346 }
347
348 static void cprint_done(void)
349 {
350         *bufptr++ = 0;
351         argptr++;
352 }
353
354 static int cprint(const char *fmt, ...)
355 {
356         va_list ap;
357         int res;
358
359         *argptr++ = bufptr;
360         va_start(ap, fmt);
361         res = vsprintf(bufptr, fmt, ap);
362         va_end(ap);
363         bufptr += res;
364         *bufptr++ = 0;
365
366         return res;
367 }
368
369 static void get_prompt_str(struct gstr *r, struct property *prop)
370 {
371         int i, j;
372         struct menu *submenu[8], *menu;
373
374         str_printf(r, "Prompt: %s\n", prop->text);
375         str_printf(r, "  Defined at %s:%d\n", prop->menu->file->name,
376                 prop->menu->lineno);
377         if (!expr_is_yes(prop->visible.expr)) {
378                 str_append(r, "  Depends on: ");
379                 expr_gstr_print(prop->visible.expr, r);
380                 str_append(r, "\n");
381         }
382         menu = prop->menu->parent;
383         for (i = 0; menu != &rootmenu && i < 8; menu = menu->parent)
384                 submenu[i++] = menu;
385         if (i > 0) {
386                 str_printf(r, "  Location:\n");
387                 for (j = 4; --i >= 0; j += 2) {
388                         menu = submenu[i];
389                         str_printf(r, "%*c-> %s", j, ' ', menu_get_prompt(menu));
390                         if (menu->sym) {
391                                 str_printf(r, " (%s [=%s])", menu->sym->name ?
392                                         menu->sym->name : "<choice>",
393                                         sym_get_string_value(menu->sym));
394                         }
395                         str_append(r, "\n");
396                 }
397         }
398 }
399
400 static void get_symbol_str(struct gstr *r, struct symbol *sym)
401 {
402         bool hit;
403         struct property *prop;
404
405         str_printf(r, "Symbol: %s [=%s]\n", sym->name,
406                                        sym_get_string_value(sym));
407         for_all_prompts(sym, prop)
408                 get_prompt_str(r, prop);
409         hit = false;
410         for_all_properties(sym, prop, P_SELECT) {
411                 if (!hit) {
412                         str_append(r, "  Selects: ");
413                         hit = true;
414                 } else
415                         str_printf(r, " && ");
416                 expr_gstr_print(prop->expr, r);
417         }
418         if (hit)
419                 str_append(r, "\n");
420         if (sym->rev_dep.expr) {
421                 str_append(r, "  Selected by: ");
422                 expr_gstr_print(sym->rev_dep.expr, r);
423                 str_append(r, "\n");
424         }
425         str_append(r, "\n\n");
426 }
427
428 static struct gstr get_relations_str(struct symbol **sym_arr)
429 {
430         struct symbol *sym;
431         struct gstr res = str_new();
432         int i;
433
434         for (i = 0; sym_arr && (sym = sym_arr[i]); i++)
435                 get_symbol_str(&res, sym);
436         if (!i)
437                 str_append(&res, "No matches found.\n");
438         return res;
439 }
440
441 pid_t pid;
442
443 static void winch_handler(int sig)
444 {
445         if (!do_resize) {
446                 kill(pid, SIGINT);
447                 do_resize = 1;
448         }
449 }
450
451 static int exec_conf(void)
452 {
453         int pipefd[2], stat, size;
454         struct sigaction sa;
455         sigset_t sset, osset;
456
457         sigemptyset(&sset);
458         sigaddset(&sset, SIGINT);
459         sigprocmask(SIG_BLOCK, &sset, &osset);
460
461         signal(SIGINT, SIG_DFL);
462
463         sa.sa_handler = winch_handler;
464         sigemptyset(&sa.sa_mask);
465         sa.sa_flags = SA_RESTART;
466         sigaction(SIGWINCH, &sa, NULL);
467
468         *argptr++ = NULL;
469
470         pipe(pipefd);
471         pid = fork();
472         if (pid == 0) {
473                 sigprocmask(SIG_SETMASK, &osset, NULL);
474                 dup2(pipefd[1], 2);
475                 close(pipefd[0]);
476                 close(pipefd[1]);
477                 execv(args[0], args);
478                 _exit(EXIT_FAILURE);
479         }
480
481         close(pipefd[1]);
482         bufptr = input_buf;
483         while (1) {
484                 size = input_buf + sizeof(input_buf) - bufptr;
485                 size = read(pipefd[0], bufptr, size);
486                 if (size <= 0) {
487                         if (size < 0) {
488                                 if (errno == EINTR || errno == EAGAIN)
489                                         continue;
490                                 perror("read");
491                         }
492                         break;
493                 }
494                 bufptr += size;
495         }
496         *bufptr++ = 0;
497         close(pipefd[0]);
498         waitpid(pid, &stat, 0);
499
500         if (do_resize) {
501                 init_wsize();
502                 do_resize = 0;
503                 sigprocmask(SIG_SETMASK, &osset, NULL);
504                 return -1;
505         }
506         if (WIFSIGNALED(stat)) {
507                 printf("\finterrupted(%d)\n", WTERMSIG(stat));
508                 exit(1);
509         }
510 #if 0
511         printf("\fexit state: %d\nexit data: '%s'\n", WEXITSTATUS(stat), input_buf);
512         sleep(1);
513 #endif
514         sigpending(&sset);
515         if (sigismember(&sset, SIGINT)) {
516                 printf("\finterrupted\n");
517                 exit(1);
518         }
519         sigprocmask(SIG_SETMASK, &osset, NULL);
520
521         return WEXITSTATUS(stat);
522 }
523
524 static void search_conf(void)
525 {
526         struct symbol **sym_arr;
527         int stat;
528         struct gstr res;
529
530 again:
531         cprint_init();
532         cprint("--title");
533         cprint(_("Search Configuration Parameter"));
534         cprint("--inputbox");
535         cprint(_("Enter CONFIG_ (sub)string to search for (omit CONFIG_)"));
536         cprint("10");
537         cprint("75");
538         cprint("");
539         stat = exec_conf();
540         if (stat < 0)
541                 goto again;
542         switch (stat) {
543         case 0:
544                 break;
545         case 1:
546                 show_helptext(_("Search Configuration"), search_help);
547                 goto again;
548         default:
549                 return;
550         }
551
552         sym_arr = sym_re_search(input_buf);
553         res = get_relations_str(sym_arr);
554         free(sym_arr);
555         show_textbox(_("Search Results"), str_get(&res), 0, 0);
556         str_free(&res);
557 }
558
559 static void build_conf(struct menu *menu)
560 {
561         struct symbol *sym;
562         struct property *prop;
563         struct menu *child;
564         int type, tmp, doint = 2;
565         tristate val;
566         char ch;
567
568         if (!menu_is_visible(menu))
569                 return;
570
571         sym = menu->sym;
572         prop = menu->prompt;
573         if (!sym) {
574                 if (prop && menu != current_menu) {
575                         const char *prompt = menu_get_prompt(menu);
576                         switch (prop->type) {
577                         case P_MENU:
578                                 child_count++;
579                                 cprint("m%p", menu);
580
581                                 if (single_menu_mode) {
582                                         cprint1("%s%*c%s",
583                                                 menu->data ? "-->" : "++>",
584                                                 indent + 1, ' ', prompt);
585                                 } else
586                                         cprint1("   %*c%s  --->", indent + 1, ' ', prompt);
587
588                                 cprint_done();
589                                 if (single_menu_mode && menu->data)
590                                         goto conf_childs;
591                                 return;
592                         default:
593                                 if (prompt) {
594                                         child_count++;
595                                         cprint(":%p", menu);
596                                         cprint("---%*c%s", indent + 1, ' ', prompt);
597                                 }
598                         }
599                 } else
600                         doint = 0;
601                 goto conf_childs;
602         }
603
604         type = sym_get_type(sym);
605         if (sym_is_choice(sym)) {
606                 struct symbol *def_sym = sym_get_choice_value(sym);
607                 struct menu *def_menu = NULL;
608
609                 child_count++;
610                 for (child = menu->list; child; child = child->next) {
611                         if (menu_is_visible(child) && child->sym == def_sym)
612                                 def_menu = child;
613                 }
614
615                 val = sym_get_tristate_value(sym);
616                 if (sym_is_changable(sym)) {
617                         cprint("t%p", menu);
618                         switch (type) {
619                         case S_BOOLEAN:
620                                 cprint1("[%c]", val == no ? ' ' : '*');
621                                 break;
622                         case S_TRISTATE:
623                                 switch (val) {
624                                 case yes: ch = '*'; break;
625                                 case mod: ch = 'M'; break;
626                                 default:  ch = ' '; break;
627                                 }
628                                 cprint1("<%c>", ch);
629                                 break;
630                         }
631                 } else {
632                         cprint("%c%p", def_menu ? 't' : ':', menu);
633                         cprint1("   ");
634                 }
635
636                 cprint1("%*c%s", indent + 1, ' ', menu_get_prompt(menu));
637                 if (val == yes) {
638                         if (def_menu) {
639                                 cprint1(" (%s)", menu_get_prompt(def_menu));
640                                 cprint1("  --->");
641                                 cprint_done();
642                                 if (def_menu->list) {
643                                         indent += 2;
644                                         build_conf(def_menu);
645                                         indent -= 2;
646                                 }
647                         } else
648                                 cprint_done();
649                         return;
650                 }
651                 cprint_done();
652         } else {
653                 if (menu == current_menu) {
654                         cprint(":%p", menu);
655                         cprint("---%*c%s", indent + 1, ' ', menu_get_prompt(menu));
656                         goto conf_childs;
657                 }
658                 child_count++;
659                 val = sym_get_tristate_value(sym);
660                 if (sym_is_choice_value(sym) && val == yes) {
661                         cprint(":%p", menu);
662                         cprint1("   ");
663                 } else {
664                         switch (type) {
665                         case S_BOOLEAN:
666                                 cprint("t%p", menu);
667                                 if (sym_is_changable(sym))
668                                         cprint1("[%c]", val == no ? ' ' : '*');
669                                 else
670                                         cprint1("---");
671                                 break;
672                         case S_TRISTATE:
673                                 cprint("t%p", menu);
674                                 switch (val) {
675                                 case yes: ch = '*'; break;
676                                 case mod: ch = 'M'; break;
677                                 default:  ch = ' '; break;
678                                 }
679                                 if (sym_is_changable(sym))
680                                         cprint1("<%c>", ch);
681                                 else
682                                         cprint1("---");
683                                 break;
684                         default:
685                                 cprint("s%p", menu);
686                                 tmp = cprint1("(%s)", sym_get_string_value(sym));
687                                 tmp = indent - tmp + 4;
688                                 if (tmp < 0)
689                                         tmp = 0;
690                                 cprint1("%*c%s%s", tmp, ' ', menu_get_prompt(menu),
691                                         (sym_has_value(sym) || !sym_is_changable(sym)) ?
692                                         "" : " (NEW)");
693                                 cprint_done();
694                                 goto conf_childs;
695                         }
696                 }
697                 cprint1("%*c%s%s", indent + 1, ' ', menu_get_prompt(menu),
698                         (sym_has_value(sym) || !sym_is_changable(sym)) ?
699                         "" : " (NEW)");
700                 if (menu->prompt->type == P_MENU) {
701                         cprint1("  --->");
702                         cprint_done();
703                         return;
704                 }
705                 cprint_done();
706         }
707
708 conf_childs:
709         indent += doint;
710         for (child = menu->list; child; child = child->next)
711                 build_conf(child);
712         indent -= doint;
713 }
714
715 static void conf(struct menu *menu)
716 {
717         struct menu *submenu;
718         const char *prompt = menu_get_prompt(menu);
719         struct symbol *sym;
720         char active_entry[40];
721         int stat, type, i;
722
723         unlink("lxdialog.scrltmp");
724         active_entry[0] = 0;
725         while (1) {
726                 cprint_init();
727                 cprint("--title");
728                 cprint("%s", prompt ? prompt : _("Main Menu"));
729                 cprint("--menu");
730                 cprint(_(menu_instructions));
731                 cprint("%d", rows);
732                 cprint("%d", cols);
733                 cprint("%d", rows - 10);
734                 cprint("%s", active_entry);
735                 current_menu = menu;
736                 build_conf(menu);
737                 if (!child_count)
738                         break;
739                 if (menu == &rootmenu) {
740                         cprint(":");
741                         cprint("--- ");
742                         cprint("L");
743                         cprint(_("    Load an Alternate Configuration File"));
744                         cprint("S");
745                         cprint(_("    Save Configuration to an Alternate File"));
746                 }
747                 stat = exec_conf();
748                 if (stat < 0)
749                         continue;
750
751                 if (stat == 1 || stat == 255)
752                         break;
753
754                 type = input_buf[0];
755                 if (!type)
756                         continue;
757
758                 for (i = 0; input_buf[i] && !isspace(input_buf[i]); i++)
759                         ;
760                 if (i >= sizeof(active_entry))
761                         i = sizeof(active_entry) - 1;
762                 input_buf[i] = 0;
763                 strcpy(active_entry, input_buf);
764
765                 sym = NULL;
766                 submenu = NULL;
767                 if (sscanf(input_buf + 1, "%p", &submenu) == 1)
768                         sym = submenu->sym;
769
770                 switch (stat) {
771                 case 0:
772                         switch (type) {
773                         case 'm':
774                                 if (single_menu_mode)
775                                         submenu->data = (void *) (long) !submenu->data;
776                                 else
777                                         conf(submenu);
778                                 break;
779                         case 't':
780                                 if (sym_is_choice(sym) && sym_get_tristate_value(sym) == yes)
781                                         conf_choice(submenu);
782                                 else if (submenu->prompt->type == P_MENU)
783                                         conf(submenu);
784                                 break;
785                         case 's':
786                                 conf_string(submenu);
787                                 break;
788                         case 'L':
789                                 conf_load();
790                                 break;
791                         case 'S':
792                                 conf_save();
793                                 break;
794                         }
795                         break;
796                 case 2:
797                         if (sym)
798                                 show_help(submenu);
799                         else
800                                 show_helptext("README", _(mconf_readme));
801                         break;
802                 case 3:
803                         if (type == 't') {
804                                 if (sym_set_tristate_value(sym, yes))
805                                         break;
806                                 if (sym_set_tristate_value(sym, mod))
807                                         show_textbox(NULL, setmod_text, 6, 74);
808                         }
809                         break;
810                 case 4:
811                         if (type == 't')
812                                 sym_set_tristate_value(sym, no);
813                         break;
814                 case 5:
815                         if (type == 't')
816                                 sym_set_tristate_value(sym, mod);
817                         break;
818                 case 6:
819                         if (type == 't')
820                                 sym_toggle_tristate_value(sym);
821                         else if (type == 'm')
822                                 conf(submenu);
823                         break;
824                 case 7:
825                         search_conf();
826                         break;
827                 }
828         }
829 }
830
831 static void show_textbox(const char *title, const char *text, int r, int c)
832 {
833         int fd;
834
835         fd = creat(".help.tmp", 0777);
836         write(fd, text, strlen(text));
837         close(fd);
838         show_file(".help.tmp", title, r, c);
839         unlink(".help.tmp");
840 }
841
842 static void show_helptext(const char *title, const char *text)
843 {
844         show_textbox(title, text, 0, 0);
845 }
846
847 static void show_help(struct menu *menu)
848 {
849         struct gstr help = str_new();
850         struct symbol *sym = menu->sym;
851
852         if (sym->help)
853         {
854                 if (sym->name) {
855                         str_printf(&help, "CONFIG_%s:\n\n", sym->name);
856                         str_append(&help, _(sym->help));
857                         str_append(&help, "\n");
858                 }
859         } else {
860                 str_append(&help, nohelp_text);
861         }
862         get_symbol_str(&help, sym);
863         show_helptext(menu_get_prompt(menu), str_get(&help));
864         str_free(&help);
865 }
866
867 static void show_file(const char *filename, const char *title, int r, int c)
868 {
869         do {
870                 cprint_init();
871                 if (title) {
872                         cprint("--title");
873                         cprint("%s", title);
874                 }
875                 cprint("--textbox");
876                 cprint("%s", filename);
877                 cprint("%d", r ? r : rows);
878                 cprint("%d", c ? c : cols);
879         } while (exec_conf() < 0);
880 }
881
882 static void conf_choice(struct menu *menu)
883 {
884         const char *prompt = menu_get_prompt(menu);
885         struct menu *child;
886         struct symbol *active;
887         int stat;
888
889         active = sym_get_choice_value(menu->sym);
890         while (1) {
891                 cprint_init();
892                 cprint("--title");
893                 cprint("%s", prompt ? prompt : _("Main Menu"));
894                 cprint("--radiolist");
895                 cprint(_(radiolist_instructions));
896                 cprint("15");
897                 cprint("70");
898                 cprint("6");
899
900                 current_menu = menu;
901                 for (child = menu->list; child; child = child->next) {
902                         if (!menu_is_visible(child))
903                                 continue;
904                         cprint("%p", child);
905                         cprint("%s", menu_get_prompt(child));
906                         if (child->sym == sym_get_choice_value(menu->sym))
907                                 cprint("ON");
908                         else if (child->sym == active)
909                                 cprint("SELECTED");
910                         else
911                                 cprint("OFF");
912                 }
913
914                 stat = exec_conf();
915                 switch (stat) {
916                 case 0:
917                         if (sscanf(input_buf, "%p", &child) != 1)
918                                 break;
919                         sym_set_tristate_value(child->sym, yes);
920                         return;
921                 case 1:
922                         if (sscanf(input_buf, "%p", &child) == 1) {
923                                 show_help(child);
924                                 active = child->sym;
925                         } else
926                                 show_help(menu);
927                         break;
928                 case 255:
929                         return;
930                 }
931         }
932 }
933
934 static void conf_string(struct menu *menu)
935 {
936         const char *prompt = menu_get_prompt(menu);
937         int stat;
938
939         while (1) {
940                 cprint_init();
941                 cprint("--title");
942                 cprint("%s", prompt ? prompt : _("Main Menu"));
943                 cprint("--inputbox");
944                 switch (sym_get_type(menu->sym)) {
945                 case S_INT:
946                         cprint(_(inputbox_instructions_int));
947                         break;
948                 case S_HEX:
949                         cprint(_(inputbox_instructions_hex));
950                         break;
951                 case S_STRING:
952                         cprint(_(inputbox_instructions_string));
953                         break;
954                 default:
955                         /* panic? */;
956                 }
957                 cprint("10");
958                 cprint("75");
959                 cprint("%s", sym_get_string_value(menu->sym));
960                 stat = exec_conf();
961                 switch (stat) {
962                 case 0:
963                         if (sym_set_string_value(menu->sym, input_buf))
964                                 return;
965                         show_textbox(NULL, _("You have made an invalid entry."), 5, 43);
966                         break;
967                 case 1:
968                         show_help(menu);
969                         break;
970                 case 255:
971                         return;
972                 }
973         }
974 }
975
976 static void conf_load(void)
977 {
978         int stat;
979
980         while (1) {
981                 cprint_init();
982                 cprint("--inputbox");
983                 cprint(load_config_text);
984                 cprint("11");
985                 cprint("55");
986                 cprint("%s", filename);
987                 stat = exec_conf();
988                 switch(stat) {
989                 case 0:
990                         if (!input_buf[0])
991                                 return;
992                         if (!conf_read(input_buf))
993                                 return;
994                         show_textbox(NULL, _("File does not exist!"), 5, 38);
995                         break;
996                 case 1:
997                         show_helptext(_("Load Alternate Configuration"), load_config_help);
998                         break;
999                 case 255:
1000                         return;
1001                 }
1002         }
1003 }
1004
1005 static void conf_save(void)
1006 {
1007         int stat;
1008
1009         while (1) {
1010                 cprint_init();
1011                 cprint("--inputbox");
1012                 cprint(save_config_text);
1013                 cprint("11");
1014                 cprint("55");
1015                 cprint("%s", filename);
1016                 stat = exec_conf();
1017                 switch(stat) {
1018                 case 0:
1019                         if (!input_buf[0])
1020                                 return;
1021                         if (!conf_write(input_buf))
1022                                 return;
1023                         show_textbox(NULL, _("Can't create file!  Probably a nonexistent directory."), 5, 60);
1024                         break;
1025                 case 1:
1026                         show_helptext(_("Save Alternate Configuration"), save_config_help);
1027                         break;
1028                 case 255:
1029                         return;
1030                 }
1031         }
1032 }
1033
1034 static void conf_cleanup(void)
1035 {
1036         tcsetattr(1, TCSAFLUSH, &ios_org);
1037         unlink(".help.tmp");
1038         unlink("lxdialog.scrltmp");
1039 }
1040
1041 int main(int ac, char **av)
1042 {
1043         struct symbol *sym;
1044         char *mode;
1045         int stat;
1046
1047         setlocale(LC_ALL, "");
1048         bindtextdomain(PACKAGE, LOCALEDIR);
1049         textdomain(PACKAGE);
1050
1051         conf_parse(av[1]);
1052         conf_read(NULL);
1053
1054         sym = sym_lookup("KERNELVERSION", 0);
1055         sym_calc_value(sym);
1056         sprintf(menu_backtitle, _("Linux Kernel v%s Configuration"),
1057                 sym_get_string_value(sym));
1058
1059         mode = getenv("MENUCONFIG_MODE");
1060         if (mode) {
1061                 if (!strcasecmp(mode, "single_menu"))
1062                         single_menu_mode = 1;
1063         }
1064
1065         tcgetattr(1, &ios_org);
1066         atexit(conf_cleanup);
1067         init_wsize();
1068         conf(&rootmenu);
1069
1070         do {
1071                 cprint_init();
1072                 cprint("--yesno");
1073                 cprint(_("Do you wish to save your new kernel configuration?"));
1074                 cprint("5");
1075                 cprint("60");
1076                 stat = exec_conf();
1077         } while (stat < 0);
1078
1079         if (stat == 0) {
1080                 if (conf_write(NULL)) {
1081                         fprintf(stderr, _("\n\n"
1082                                 "Error during writing of the kernel configuration.\n"
1083                                 "Your kernel configuration changes were NOT saved."
1084                                 "\n\n"));
1085                         return 1;
1086                 }
1087                 printf(_("\n\n"
1088                         "*** End of Linux kernel configuration.\n"
1089                         "*** Execute 'make' to build the kernel or try 'make help'."
1090                         "\n\n"));
1091         } else {
1092                 fprintf(stderr, _("\n\n"
1093                         "Your kernel configuration changes were NOT saved."
1094                         "\n\n"));
1095         }
1096
1097         return 0;
1098 }