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