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        if (pipe(pipefd))
 486                _exit(EXIT_FAILURE);
 487        pid = fork();
 488        if (pid == 0) {
 489                sigprocmask(SIG_SETMASK, &osset, NULL);
 490                dup2(pipefd[1], 2);
 491                close(pipefd[0]);
 492                close(pipefd[1]);
 493                execv(args[0], args);
 494                _exit(EXIT_FAILURE);
 495        }
 496
 497        close(pipefd[1]);
 498        bufptr = input_buf;
 499        while (1) {
 500                size = input_buf + sizeof(input_buf) - bufptr;
 501                size = read(pipefd[0], bufptr, size);
 502                if (size <= 0) {
 503                        if (size < 0) {
 504                                if (errno == EINTR || errno == EAGAIN)
 505                                        continue;
 506                                perror("read");
 507                        }
 508                        break;
 509                }
 510                bufptr += size;
 511        }
 512        *bufptr++ = 0;
 513        close(pipefd[0]);
 514        waitpid(pid, &stat, 0);
 515
 516        if (do_resize) {
 517                init_wsize();
 518                do_resize = 0;
 519                sigprocmask(SIG_SETMASK, &osset, NULL);
 520                return -1;
 521        }
 522        if (WIFSIGNALED(stat)) {
 523                printf("\finterrupted(%d)\n", WTERMSIG(stat));
 524                exit(1);
 525        }
 526#if 0
 527        printf("\fexit state: %d\nexit data: '%s'\n", WEXITSTATUS(stat), input_buf);
 528        sleep(1);
 529#endif
 530        sigpending(&sset);
 531        if (sigismember(&sset, SIGINT)) {
 532                printf("\finterrupted\n");
 533                exit(1);
 534        }
 535        sigprocmask(SIG_SETMASK, &osset, NULL);
 536
 537        return WEXITSTATUS(stat);
 538}
 539
 540static void search_conf(void)
 541{
 542        struct symbol **sym_arr;
 543        int stat;
 544        struct gstr res;
 545
 546again:
 547        cprint_init();
 548        cprint("--title");
 549        cprint(_("Search Configuration Parameter"));
 550        cprint("--inputbox");
 551        cprint(_("Enter CONFIG_ (sub)string to search for (omit CONFIG_)"));
 552        cprint("10");
 553        cprint("75");
 554        cprint("");
 555        stat = exec_conf();
 556        if (stat < 0)
 557                goto again;
 558        switch (stat) {
 559        case 0:
 560                break;
 561        case 1:
 562                show_helptext(_("Search Configuration"), search_help);
 563                goto again;
 564        default:
 565                return;
 566        }
 567
 568        sym_arr = sym_re_search(input_buf);
 569        res = get_relations_str(sym_arr);
 570        free(sym_arr);
 571        show_textbox(_("Search Results"), str_get(&res), 0, 0);
 572        str_free(&res);
 573}
 574
 575static void build_conf(struct menu *menu)
 576{
 577        struct symbol *sym;
 578        struct property *prop;
 579        struct menu *child;
 580        int type, tmp, doint = 2;
 581        tristate val;
 582        char ch;
 583
 584        if (!menu_is_visible(menu))
 585                return;
 586
 587        sym = menu->sym;
 588        prop = menu->prompt;
 589        if (!sym) {
 590                if (prop && menu != current_menu) {
 591                        const char *prompt = menu_get_prompt(menu);
 592                        switch (prop->type) {
 593                        case P_MENU:
 594                                child_count++;
 595                                cprint("m%p", menu);
 596
 597                                if (single_menu_mode) {
 598                                        cprint1("%s%*c%s",
 599                                                menu->data ? "-->" : "++>",
 600                                                indent + 1, ' ', prompt);
 601                                } else
 602                                        cprint1("   %*c%s  --->", indent + 1, ' ', prompt);
 603
 604                                cprint_done();
 605                                if (single_menu_mode && menu->data)
 606                                        goto conf_childs;
 607                                return;
 608                        default:
 609                                if (prompt) {
 610                                        child_count++;
 611                                        cprint(":%p", menu);
 612                                        cprint("---%*c%s", indent + 1, ' ', prompt);
 613                                }
 614                        }
 615                } else
 616                        doint = 0;
 617                goto conf_childs;
 618        }
 619
 620        type = sym_get_type(sym);
 621        if (sym_is_choice(sym)) {
 622                struct symbol *def_sym = sym_get_choice_value(sym);
 623                struct menu *def_menu = NULL;
 624
 625                child_count++;
 626                for (child = menu->list; child; child = child->next) {
 627                        if (menu_is_visible(child) && child->sym == def_sym)
 628                                def_menu = child;
 629                }
 630
 631                val = sym_get_tristate_value(sym);
 632                if (sym_is_changable(sym)) {
 633                        cprint("t%p", menu);
 634                        switch (type) {
 635                        case S_BOOLEAN:
 636                                cprint1("[%c]", val == no ? ' ' : '*');
 637                                break;
 638                        case S_TRISTATE:
 639                                switch (val) {
 640                                case yes: ch = '*'; break;
 641                                case mod: ch = 'M'; break;
 642                                default:  ch = ' '; break;
 643                                }
 644                                cprint1("<%c>", ch);
 645                                break;
 646                        }
 647                } else {
 648                        cprint("%c%p", def_menu ? 't' : ':', menu);
 649                        cprint1("   ");
 650                }
 651
 652                cprint1("%*c%s", indent + 1, ' ', menu_get_prompt(menu));
 653                if (val == yes) {
 654                        if (def_menu) {
 655                                cprint1(" (%s)", menu_get_prompt(def_menu));
 656                                cprint1("  --->");
 657                                cprint_done();
 658                                if (def_menu->list) {
 659                                        indent += 2;
 660                                        build_conf(def_menu);
 661                                        indent -= 2;
 662                                }
 663                        } else
 664                                cprint_done();
 665                        return;
 666                }
 667                cprint_done();
 668        } else {
 669                if (menu == current_menu) {
 670                        cprint(":%p", menu);
 671                        cprint("---%*c%s", indent + 1, ' ', menu_get_prompt(menu));
 672                        goto conf_childs;
 673                }
 674                child_count++;
 675                val = sym_get_tristate_value(sym);
 676                if (sym_is_choice_value(sym) && val == yes) {
 677                        cprint(":%p", menu);
 678                        cprint1("   ");
 679                } else {
 680                        switch (type) {
 681                        case S_BOOLEAN:
 682                                cprint("t%p", menu);
 683                                if (sym_is_changable(sym))
 684                                        cprint1("[%c]", val == no ? ' ' : '*');
 685                                else
 686                                        cprint1("---");
 687                                break;
 688                        case S_TRISTATE:
 689                                cprint("t%p", menu);
 690                                switch (val) {
 691                                case yes: ch = '*'; break;
 692                                case mod: ch = 'M'; break;
 693                                default:  ch = ' '; break;
 694                                }
 695                                if (sym_is_changable(sym))
 696                                        cprint1("<%c>", ch);
 697                                else
 698                                        cprint1("---");
 699                                break;
 700                        default:
 701                                cprint("s%p", menu);
 702                                tmp = cprint1("(%s)", sym_get_string_value(sym));
 703                                tmp = indent - tmp + 4;
 704                                if (tmp < 0)
 705                                        tmp = 0;
 706                                cprint1("%*c%s%s", tmp, ' ', menu_get_prompt(menu),
 707                                        (sym_has_value(sym) || !sym_is_changable(sym)) ?
 708                                        "" : " (NEW)");
 709                                cprint_done();
 710                                goto conf_childs;
 711                        }
 712                }
 713                cprint1("%*c%s%s", indent + 1, ' ', menu_get_prompt(menu),
 714                        (sym_has_value(sym) || !sym_is_changable(sym)) ?
 715                        "" : " (NEW)");
 716                if (menu->prompt->type == P_MENU) {
 717                        cprint1("  --->");
 718                        cprint_done();
 719                        return;
 720                }
 721                cprint_done();
 722        }
 723
 724conf_childs:
 725        indent += doint;
 726        for (child = menu->list; child; child = child->next)
 727                build_conf(child);
 728        indent -= doint;
 729}
 730
 731static void conf(struct menu *menu)
 732{
 733        struct menu *submenu;
 734        const char *prompt = menu_get_prompt(menu);
 735        struct symbol *sym;
 736        char active_entry[40];
 737        int stat, type, i;
 738
 739        unlink("lxdialog.scrltmp");
 740        active_entry[0] = 0;
 741        while (1) {
 742                cprint_init();
 743                cprint("--title");
 744                cprint("%s", prompt ? prompt : _("Main Menu"));
 745                cprint("--menu");
 746                cprint(_(menu_instructions));
 747                cprint("%d", rows);
 748                cprint("%d", cols);
 749                cprint("%d", rows - 10);
 750                cprint("%s", active_entry);
 751                current_menu = menu;
 752                build_conf(menu);
 753                if (!child_count)
 754                        break;
 755                if (menu == &rootmenu) {
 756                        cprint(":");
 757                        cprint("--- ");
 758                        cprint("L");
 759                        cprint(_("    Load an Alternate Configuration File"));
 760                        cprint("S");
 761                        cprint(_("    Save Configuration to an Alternate File"));
 762                }
 763                stat = exec_conf();
 764                if (stat < 0)
 765                        continue;
 766
 767                if (stat == 1 || stat == 255)
 768                        break;
 769
 770                type = input_buf[0];
 771                if (!type)
 772                        continue;
 773
 774                for (i = 0; input_buf[i] && !isspace(input_buf[i]); i++)
 775                        ;
 776                if (i >= sizeof(active_entry))
 777                        i = sizeof(active_entry) - 1;
 778                input_buf[i] = 0;
 779                strcpy(active_entry, input_buf);
 780
 781                sym = NULL;
 782                submenu = NULL;
 783                if (sscanf(input_buf + 1, "%p", &submenu) == 1)
 784                        sym = submenu->sym;
 785
 786                switch (stat) {
 787                case 0:
 788                        switch (type) {
 789                        case 'm':
 790                                if (single_menu_mode)
 791                                        submenu->data = (void *) (long) !submenu->data;
 792                                else
 793                                        conf(submenu);
 794                                break;
 795                        case 't':
 796                                if (sym_is_choice(sym) && sym_get_tristate_value(sym) == yes)
 797                                        conf_choice(submenu);
 798                                else if (submenu->prompt->type == P_MENU)
 799                                        conf(submenu);
 800                                break;
 801                        case 's':
 802                                conf_string(submenu);
 803                                break;
 804                        case 'L':
 805                                conf_load();
 806                                break;
 807                        case 'S':
 808                                conf_save();
 809                                break;
 810                        }
 811                        break;
 812                case 2:
 813                        if (sym)
 814                                show_help(submenu);
 815                        else
 816                                show_helptext("README", _(mconf_readme));
 817                        break;
 818                case 3:
 819                        if (type == 't') {
 820                                if (sym_set_tristate_value(sym, yes))
 821                                        break;
 822                                if (sym_set_tristate_value(sym, mod))
 823                                        show_textbox(NULL, setmod_text, 6, 74);
 824                        }
 825                        break;
 826                case 4:
 827                        if (type == 't')
 828                                sym_set_tristate_value(sym, no);
 829                        break;
 830                case 5:
 831                        if (type == 't')
 832                                sym_set_tristate_value(sym, mod);
 833                        break;
 834                case 6:
 835                        if (type == 't')
 836                                sym_toggle_tristate_value(sym);
 837                        else if (type == 'm')
 838                                conf(submenu);
 839                        break;
 840                case 7:
 841                        search_conf();
 842                        break;
 843                }
 844        }
 845}
 846
 847static void show_textbox(const char *title, const char *text, int r, int c)
 848{
 849        int fd;
 850        int len = strlen(text);
 851
 852        fd = creat(".help.tmp", 0777);
 853        if (write(fd, text, len) != len)
 854                exit(1);
 855        close(fd);
 856        show_file(".help.tmp", title, r, c);
 857        unlink(".help.tmp");
 858}
 859
 860static void show_helptext(const char *title, const char *text)
 861{
 862        show_textbox(title, text, 0, 0);
 863}
 864
 865static void show_help(struct menu *menu)
 866{
 867        struct gstr help = str_new();
 868        struct symbol *sym = menu->sym;
 869
 870        if (sym->help)
 871        {
 872                if (sym->name) {
 873                        str_printf(&help, "CONFIG_%s:\n\n", sym->name);
 874                        str_append(&help, _(sym->help));
 875                        str_append(&help, "\n");
 876                }
 877        } else {
 878                str_append(&help, nohelp_text);
 879        }
 880        get_symbol_str(&help, sym);
 881        show_helptext(menu_get_prompt(menu), str_get(&help));
 882        str_free(&help);
 883}
 884
 885static void show_file(const char *filename, const char *title, int r, int c)
 886{
 887        do {
 888                cprint_init();
 889                if (title) {
 890                        cprint("--title");
 891                        cprint("%s", title);
 892                }
 893                cprint("--textbox");
 894                cprint("%s", filename);
 895                cprint("%d", r ? r : rows);
 896                cprint("%d", c ? c : cols);
 897        } while (exec_conf() < 0);
 898}
 899
 900static void conf_choice(struct menu *menu)
 901{
 902        const char *prompt = menu_get_prompt(menu);
 903        struct menu *child;
 904        struct symbol *active;
 905        int stat;
 906
 907        active = sym_get_choice_value(menu->sym);
 908        while (1) {
 909                cprint_init();
 910                cprint("--title");
 911                cprint("%s", prompt ? prompt : _("Main Menu"));
 912                cprint("--radiolist");
 913                cprint(_(radiolist_instructions));
 914                cprint("15");
 915                cprint("70");
 916                cprint("6");
 917
 918                current_menu = menu;
 919                for (child = menu->list; child; child = child->next) {
 920                        if (!menu_is_visible(child))
 921                                continue;
 922                        cprint("%p", child);
 923                        cprint("%s", menu_get_prompt(child));
 924                        if (child->sym == sym_get_choice_value(menu->sym))
 925                                cprint("ON");
 926                        else if (child->sym == active)
 927                                cprint("SELECTED");
 928                        else
 929                                cprint("OFF");
 930                }
 931
 932                stat = exec_conf();
 933                switch (stat) {
 934                case 0:
 935                        if (sscanf(input_buf, "%p", &child) != 1)
 936                                break;
 937                        sym_set_tristate_value(child->sym, yes);
 938                        return;
 939                case 1:
 940                        if (sscanf(input_buf, "%p", &child) == 1) {
 941                                show_help(child);
 942                                active = child->sym;
 943                        } else
 944                                show_help(menu);
 945                        break;
 946                case 255:
 947                        return;
 948                }
 949        }
 950}
 951
 952static void conf_string(struct menu *menu)
 953{
 954        const char *prompt = menu_get_prompt(menu);
 955        int stat;
 956
 957        while (1) {
 958                cprint_init();
 959                cprint("--title");
 960                cprint("%s", prompt ? prompt : _("Main Menu"));
 961                cprint("--inputbox");
 962                switch (sym_get_type(menu->sym)) {
 963                case S_INT:
 964                        cprint(_(inputbox_instructions_int));
 965                        break;
 966                case S_HEX:
 967                        cprint(_(inputbox_instructions_hex));
 968                        break;
 969                case S_STRING:
 970                        cprint(_(inputbox_instructions_string));
 971                        break;
 972                default:
 973                        /* panic? */;
 974                }
 975                cprint("10");
 976                cprint("75");
 977                cprint("%s", sym_get_string_value(menu->sym));
 978                stat = exec_conf();
 979                switch (stat) {
 980                case 0:
 981                        if (sym_set_string_value(menu->sym, input_buf))
 982                                return;
 983                        show_textbox(NULL, _("You have made an invalid entry."), 5, 43);
 984                        break;
 985                case 1:
 986                        show_help(menu);
 987                        break;
 988                case 255:
 989                        return;
 990                }
 991        }
 992}
 993
 994static void conf_load(void)
 995{
 996        int stat;
 997
 998        while (1) {
 999                cprint_init();
1000                cprint("--inputbox");
1001                cprint(load_config_text);
1002                cprint("11");
1003                cprint("55");
1004                cprint("%s", filename);
1005                stat = exec_conf();
1006                switch(stat) {
1007                case 0:
1008                        if (!input_buf[0])
1009                                return;
1010                        if (!conf_read(input_buf))
1011                                return;
1012                        show_textbox(NULL, _("File does not exist!"), 5, 38);
1013                        break;
1014                case 1:
1015                        show_helptext(_("Load Alternate Configuration"), load_config_help);
1016                        break;
1017                case 255:
1018                        return;
1019                }
1020        }
1021}
1022
1023static void conf_save(void)
1024{
1025        int stat;
1026
1027        while (1) {
1028                cprint_init();
1029                cprint("--inputbox");
1030                cprint(save_config_text);
1031                cprint("11");
1032                cprint("55");
1033                cprint("%s", filename);
1034                stat = exec_conf();
1035                switch(stat) {
1036                case 0:
1037                        if (!input_buf[0])
1038                                return;
1039                        if (!conf_write(input_buf))
1040                                return;
1041                        show_textbox(NULL, _("Can't create file!  Probably a nonexistent directory."), 5, 60);
1042                        break;
1043                case 1:
1044                        show_helptext(_("Save Alternate Configuration"), save_config_help);
1045                        break;
1046                case 255:
1047                        return;
1048                }
1049        }
1050}
1051
1052static void conf_cleanup(void)
1053{
1054        tcsetattr(1, TCSAFLUSH, &ios_org);
1055        unlink(".help.tmp");
1056        unlink("lxdialog.scrltmp");
1057}
1058
1059int main(int ac, char **av)
1060{
1061        struct symbol *sym;
1062        char *mode;
1063        int stat;
1064
1065        setlocale(LC_ALL, "");
1066        bindtextdomain(PACKAGE, LOCALEDIR);
1067        textdomain(PACKAGE);
1068
1069        conf_parse(av[1]);
1070        conf_read(NULL);
1071
1072        sym = sym_lookup("KERNELVERSION", 0);
1073        sym_calc_value(sym);
1074        sprintf(menu_backtitle, _("BusyBox %s Configuration"),
1075                sym_get_string_value(sym));
1076
1077        mode = getenv("MENUCONFIG_MODE");
1078        if (mode) {
1079                if (!strcasecmp(mode, "single_menu"))
1080                        single_menu_mode = 1;
1081        }
1082
1083        tcgetattr(1, &ios_org);
1084        atexit(conf_cleanup);
1085        init_wsize();
1086        conf(&rootmenu);
1087
1088        do {
1089                cprint_init();
1090                cprint("--yesno");
1091                cprint(_("Do you wish to save your new configuration?"));
1092                cprint("5");
1093                cprint("60");
1094                stat = exec_conf();
1095        } while (stat < 0);
1096
1097        if (stat == 0) {
1098                if (conf_write(NULL)) {
1099                        fprintf(stderr, _("\n\n"
1100                                "Error during writing of the configuration.\n"
1101                                "Your configuration changes were NOT saved."
1102                                "\n\n"));
1103                        return 1;
1104                }
1105                printf(_("\n\n"
1106                        "*** End of configuration.\n"
1107                        "*** Execute 'make' to build the project or try 'make help'."
1108                        "\n\n"));
1109        } else {
1110                fprintf(stderr, _("\n\n"
1111                        "Your configuration changes were NOT saved."
1112                        "\n\n"));
1113        }
1114
1115        return 0;
1116}
1117