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