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