linux/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 <ctype.h>
  12#include <errno.h>
  13#include <fcntl.h>
  14#include <limits.h>
  15#include <stdarg.h>
  16#include <stdlib.h>
  17#include <string.h>
  18#include <signal.h>
  19#include <unistd.h>
  20#include <locale.h>
  21
  22#include "lkc.h"
  23#include "lxdialog/dialog.h"
  24
  25static const char mconf_readme[] = N_(
  26"Overview\n"
  27"--------\n"
  28"This interface lets you select features and parameters for the build.\n"
  29"Features can either be built-in, modularized, or ignored. Parameters\n"
  30"must be entered in as decimal or hexadecimal numbers or text.\n"
  31"\n"
  32"Menu items beginning with following braces represent features that\n"
  33"  [ ] can be built in or removed\n"
  34"  < > can be built in, modularized or removed\n"
  35"  { } can be built in or modularized (selected by other feature)\n"
  36"  - - are selected by other feature,\n"
  37"while *, M or whitespace inside braces means to build in, build as\n"
  38"a module or to exclude the feature respectively.\n"
  39"\n"
  40"To change any of these features, highlight it with the cursor\n"
  41"keys and press <Y> to build it in, <M> to make it a module or\n"
  42"<N> to remove it.  You may also press the <Space Bar> to cycle\n"
  43"through the available options (i.e. Y->N->M->Y).\n"
  44"\n"
  45"Some additional keyboard hints:\n"
  46"\n"
  47"Menus\n"
  48"----------\n"
  49"o  Use the Up/Down arrow keys (cursor keys) to highlight the item you\n"
  50"   wish to change or the submenu you wish to select and press <Enter>.\n"
  51"   Submenus are designated by \"--->\", empty ones by \"----\".\n"
  52"\n"
  53"   Shortcut: Press the option's highlighted letter (hotkey).\n"
  54"             Pressing a hotkey more than once will sequence\n"
  55"             through all visible items which use that hotkey.\n"
  56"\n"
  57"   You may also use the <PAGE UP> and <PAGE DOWN> keys to scroll\n"
  58"   unseen options into view.\n"
  59"\n"
  60"o  To exit a menu use the cursor keys to highlight the <Exit> button\n"
  61"   and press <ENTER>.\n"
  62"\n"
  63"   Shortcut: Press <ESC><ESC> or <E> or <X> if there is no hotkey\n"
  64"             using those letters.  You may press a single <ESC>, but\n"
  65"             there is a delayed response which you may find annoying.\n"
  66"\n"
  67"   Also, the <TAB> and cursor keys will cycle between <Select>,\n"
  68"   <Exit>, <Help>, <Save>, and <Load>.\n"
  69"\n"
  70"o  To get help with an item, use the cursor keys to highlight <Help>\n"
  71"   and press <ENTER>.\n"
  72"\n"
  73"   Shortcut: Press <H> or <?>.\n"
  74"\n"
  75"o  To toggle the display of hidden options, press <Z>.\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 <u>, <d>, <SPACE BAR> and <B> for\n"
 109"   those who are familiar with less and lynx.\n"
 110"\n"
 111"o  Press <E>, <X>, <q>, <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"The <Save> button will let you save the current configuration to\n"
 121"a file of your choosing.  Use the <Load> button to load a previously\n"
 122"saved alternate configuration.\n"
 123"\n"
 124"Even if you don't use alternate configuration files, but you find\n"
 125"during a Menuconfig session that you have completely messed up your\n"
 126"settings, you may use the <Load> button to restore your previously\n"
 127"saved settings from \".config\" without restarting Menuconfig.\n"
 128"\n"
 129"Other information\n"
 130"-----------------\n"
 131"If you use Menuconfig in an XTERM window, make sure you have your\n"
 132"$TERM variable set to point to an xterm definition which supports\n"
 133"color.  Otherwise, Menuconfig will look rather bad.  Menuconfig will\n"
 134"not display correctly in an RXVT window because rxvt displays only one\n"
 135"intensity of color, bright.\n"
 136"\n"
 137"Menuconfig will display larger menus on screens or xterms which are\n"
 138"set to display more than the standard 25 row by 80 column geometry.\n"
 139"In order for this to work, the \"stty size\" command must be able to\n"
 140"display the screen's current row and column geometry.  I STRONGLY\n"
 141"RECOMMEND that you make sure you do NOT have the shell variables\n"
 142"LINES and COLUMNS exported into your environment.  Some distributions\n"
 143"export those variables via /etc/profile.  Some ncurses programs can\n"
 144"become confused when those variables (LINES & COLUMNS) don't reflect\n"
 145"the true screen size.\n"
 146"\n"
 147"Optional personality available\n"
 148"------------------------------\n"
 149"If you prefer to have all of the options listed in a single menu,\n"
 150"rather than the default multimenu hierarchy, run the menuconfig with\n"
 151"MENUCONFIG_MODE environment variable set to single_menu. Example:\n"
 152"\n"
 153"make MENUCONFIG_MODE=single_menu menuconfig\n"
 154"\n"
 155"<Enter> will then unroll the appropriate category, or enfold it if it\n"
 156"is already unrolled.\n"
 157"\n"
 158"Note that this mode can eventually be a little more CPU expensive\n"
 159"(especially with a larger number of unrolled categories) than the\n"
 160"default mode.\n"
 161"\n"
 162"Different color themes available\n"
 163"--------------------------------\n"
 164"It is possible to select different color themes using the variable\n"
 165"MENUCONFIG_COLOR. To select a theme use:\n"
 166"\n"
 167"make MENUCONFIG_COLOR=<theme> menuconfig\n"
 168"\n"
 169"Available themes are\n"
 170" mono       => selects colors suitable for monochrome displays\n"
 171" blackbg    => selects a color scheme with black background\n"
 172" classic    => theme with blue background. The classic look\n"
 173" bluetitle  => an LCD friendly version of classic. (default)\n"
 174"\n"),
 175menu_instructions[] = N_(
 176        "Arrow keys navigate the menu.  "
 177        "<Enter> selects submenus ---> (or empty submenus ----).  "
 178        "Highlighted letters are hotkeys.  "
 179        "Pressing <Y> includes, <N> excludes, <M> modularizes features.  "
 180        "Press <Esc><Esc> to exit, <?> for Help, </> for Search.  "
 181        "Legend: [*] built-in  [ ] excluded  <M> module  < > module capable"),
 182radiolist_instructions[] = N_(
 183        "Use the arrow keys to navigate this window or "
 184        "press the hotkey of the item you wish to select "
 185        "followed by the <SPACE BAR>. "
 186        "Press <?> for additional information about this option."),
 187inputbox_instructions_int[] = N_(
 188        "Please enter a decimal value. "
 189        "Fractions will not be accepted.  "
 190        "Use the <TAB> key to move from the input field to the buttons below it."),
 191inputbox_instructions_hex[] = N_(
 192        "Please enter a hexadecimal value. "
 193        "Use the <TAB> key to move from the input field to the buttons below it."),
 194inputbox_instructions_string[] = N_(
 195        "Please enter a string value. "
 196        "Use the <TAB> key to move from the input field to the buttons below it."),
 197setmod_text[] = N_(
 198        "This feature depends on another which has been configured as a module.\n"
 199        "As a result, this feature will be built as a module."),
 200load_config_text[] = N_(
 201        "Enter the name of the configuration file you wish to load.  "
 202        "Accept the name shown to restore the configuration you "
 203        "last retrieved.  Leave blank to abort."),
 204load_config_help[] = N_(
 205        "\n"
 206        "For various reasons, one may wish to keep several different\n"
 207        "configurations available on a single machine.\n"
 208        "\n"
 209        "If you have saved a previous configuration in a file other than the\n"
 210        "default one, entering its name here will allow you to modify that\n"
 211        "configuration.\n"
 212        "\n"
 213        "If you are uncertain, then you have probably never used alternate\n"
 214        "configuration files. You should therefore leave this blank to abort.\n"),
 215save_config_text[] = N_(
 216        "Enter a filename to which this configuration should be saved "
 217        "as an alternate.  Leave blank to abort."),
 218save_config_help[] = N_(
 219        "\n"
 220        "For various reasons, one may wish to keep different configurations\n"
 221        "available on a single machine.\n"
 222        "\n"
 223        "Entering a file name here will allow you to later retrieve, modify\n"
 224        "and use the current configuration as an alternate to whatever\n"
 225        "configuration options you have selected at that time.\n"
 226        "\n"
 227        "If you are uncertain what all this means then you should probably\n"
 228        "leave this blank.\n"),
 229search_help[] = N_(
 230        "\n"
 231        "Search for symbols and display their relations.\n"
 232        "Regular expressions are allowed.\n"
 233        "Example: search for \"^FOO\"\n"
 234        "Result:\n"
 235        "-----------------------------------------------------------------\n"
 236        "Symbol: FOO [=m]\n"
 237        "Type  : tristate\n"
 238        "Prompt: Foo bus is used to drive the bar HW\n"
 239        "  Location:\n"
 240        "    -> Bus options (PCI, PCMCIA, EISA, ISA)\n"
 241        "      -> PCI support (PCI [=y])\n"
 242        "(1)     -> PCI access mode (<choice> [=y])\n"
 243        "  Defined at drivers/pci/Kconfig:47\n"
 244        "  Depends on: X86_LOCAL_APIC && X86_IO_APIC || IA64\n"
 245        "  Selects: LIBCRC32\n"
 246        "  Selected by: BAR [=n]\n"
 247        "-----------------------------------------------------------------\n"
 248        "o The line 'Type:' shows the type of the configuration option for\n"
 249        "  this symbol (boolean, tristate, string, ...)\n"
 250        "o The line 'Prompt:' shows the text used in the menu structure for\n"
 251        "  this symbol\n"
 252        "o The 'Defined at' line tells at what file / line number the symbol\n"
 253        "  is defined\n"
 254        "o The 'Depends on:' line tells what symbols need to be defined for\n"
 255        "  this symbol to be visible in the menu (selectable)\n"
 256        "o The 'Location:' lines tells where in the menu structure this symbol\n"
 257        "  is located\n"
 258        "    A location followed by a [=y] indicates that this is a\n"
 259        "    selectable menu item - and the current value is displayed inside\n"
 260        "    brackets.\n"
 261        "    Press the key in the (#) prefix to jump directly to that\n"
 262        "    location. You will be returned to the current search results\n"
 263        "    after exiting this new menu.\n"
 264        "o The 'Selects:' line tells what symbols will be automatically\n"
 265        "  selected if this symbol is selected (y or m)\n"
 266        "o The 'Selected by' line tells what symbol has selected this symbol\n"
 267        "\n"
 268        "Only relevant lines are shown.\n"
 269        "\n\n"
 270        "Search examples:\n"
 271        "Examples: USB  => find all symbols containing USB\n"
 272        "          ^USB => find all symbols starting with USB\n"
 273        "          USB$ => find all symbols ending with USB\n"
 274        "\n");
 275
 276static int indent;
 277static struct menu *current_menu;
 278static int child_count;
 279static int single_menu_mode;
 280static int show_all_options;
 281static int save_and_exit;
 282static int silent;
 283
 284static void conf(struct menu *menu, struct menu *active_menu);
 285static void conf_choice(struct menu *menu);
 286static void conf_string(struct menu *menu);
 287static void conf_load(void);
 288static void conf_save(void);
 289static int show_textbox_ext(const char *title, char *text, int r, int c,
 290                            int *keys, int *vscroll, int *hscroll,
 291                            update_text_fn update_text, void *data);
 292static void show_textbox(const char *title, const char *text, int r, int c);
 293static void show_helptext(const char *title, const char *text);
 294static void show_help(struct menu *menu);
 295
 296static char filename[PATH_MAX+1];
 297static void set_config_filename(const char *config_filename)
 298{
 299        static char menu_backtitle[PATH_MAX+128];
 300        int size;
 301
 302        size = snprintf(menu_backtitle, sizeof(menu_backtitle),
 303                        "%s - %s", config_filename, rootmenu.prompt->text);
 304        if (size >= sizeof(menu_backtitle))
 305                menu_backtitle[sizeof(menu_backtitle)-1] = '\0';
 306        set_dialog_backtitle(menu_backtitle);
 307
 308        size = snprintf(filename, sizeof(filename), "%s", config_filename);
 309        if (size >= sizeof(filename))
 310                filename[sizeof(filename)-1] = '\0';
 311}
 312
 313struct subtitle_part {
 314        struct list_head entries;
 315        const char *text;
 316};
 317static LIST_HEAD(trail);
 318
 319static struct subtitle_list *subtitles;
 320static void set_subtitle(void)
 321{
 322        struct subtitle_part *sp;
 323        struct subtitle_list *pos, *tmp;
 324
 325        for (pos = subtitles; pos != NULL; pos = tmp) {
 326                tmp = pos->next;
 327                free(pos);
 328        }
 329
 330        subtitles = NULL;
 331        list_for_each_entry(sp, &trail, entries) {
 332                if (sp->text) {
 333                        if (pos) {
 334                                pos->next = xcalloc(1, sizeof(*pos));
 335                                pos = pos->next;
 336                        } else {
 337                                subtitles = pos = xcalloc(1, sizeof(*pos));
 338                        }
 339                        pos->text = sp->text;
 340                }
 341        }
 342
 343        set_dialog_subtitles(subtitles);
 344}
 345
 346static void reset_subtitle(void)
 347{
 348        struct subtitle_list *pos, *tmp;
 349
 350        for (pos = subtitles; pos != NULL; pos = tmp) {
 351                tmp = pos->next;
 352                free(pos);
 353        }
 354        subtitles = NULL;
 355        set_dialog_subtitles(subtitles);
 356}
 357
 358struct search_data {
 359        struct list_head *head;
 360        struct menu **targets;
 361        int *keys;
 362};
 363
 364static void update_text(char *buf, size_t start, size_t end, void *_data)
 365{
 366        struct search_data *data = _data;
 367        struct jump_key *pos;
 368        int k = 0;
 369
 370        list_for_each_entry(pos, data->head, entries) {
 371                if (pos->offset >= start && pos->offset < end) {
 372                        char header[4];
 373
 374                        if (k < JUMP_NB) {
 375                                int key = '0' + (pos->index % JUMP_NB) + 1;
 376
 377                                sprintf(header, "(%c)", key);
 378                                data->keys[k] = key;
 379                                data->targets[k] = pos->target;
 380                                k++;
 381                        } else {
 382                                sprintf(header, "   ");
 383                        }
 384
 385                        memcpy(buf + pos->offset, header, sizeof(header) - 1);
 386                }
 387        }
 388        data->keys[k] = 0;
 389}
 390
 391static void search_conf(void)
 392{
 393        struct symbol **sym_arr;
 394        struct gstr res;
 395        struct gstr title;
 396        char *dialog_input;
 397        int dres, vscroll = 0, hscroll = 0;
 398        bool again;
 399        struct gstr sttext;
 400        struct subtitle_part stpart;
 401
 402        title = str_new();
 403        str_printf( &title, _("Enter (sub)string or regexp to search for "
 404                              "(with or without \"%s\")"), CONFIG_);
 405
 406again:
 407        dialog_clear();
 408        dres = dialog_inputbox(_("Search Configuration Parameter"),
 409                              str_get(&title),
 410                              10, 75, "");
 411        switch (dres) {
 412        case 0:
 413                break;
 414        case 1:
 415                show_helptext(_("Search Configuration"), search_help);
 416                goto again;
 417        default:
 418                str_free(&title);
 419                return;
 420        }
 421
 422        /* strip the prefix if necessary */
 423        dialog_input = dialog_input_result;
 424        if (strncasecmp(dialog_input_result, CONFIG_, strlen(CONFIG_)) == 0)
 425                dialog_input += strlen(CONFIG_);
 426
 427        sttext = str_new();
 428        str_printf(&sttext, "Search (%s)", dialog_input_result);
 429        stpart.text = str_get(&sttext);
 430        list_add_tail(&stpart.entries, &trail);
 431
 432        sym_arr = sym_re_search(dialog_input);
 433        do {
 434                LIST_HEAD(head);
 435                struct menu *targets[JUMP_NB];
 436                int keys[JUMP_NB + 1], i;
 437                struct search_data data = {
 438                        .head = &head,
 439                        .targets = targets,
 440                        .keys = keys,
 441                };
 442                struct jump_key *pos, *tmp;
 443
 444                res = get_relations_str(sym_arr, &head);
 445                set_subtitle();
 446                dres = show_textbox_ext(_("Search Results"), (char *)
 447                                        str_get(&res), 0, 0, keys, &vscroll,
 448                                        &hscroll, &update_text, (void *)
 449                                        &data);
 450                again = false;
 451                for (i = 0; i < JUMP_NB && keys[i]; i++)
 452                        if (dres == keys[i]) {
 453                                conf(targets[i]->parent, targets[i]);
 454                                again = true;
 455                        }
 456                str_free(&res);
 457                list_for_each_entry_safe(pos, tmp, &head, entries)
 458                        free(pos);
 459        } while (again);
 460        free(sym_arr);
 461        str_free(&title);
 462        list_del(trail.prev);
 463        str_free(&sttext);
 464}
 465
 466static void build_conf(struct menu *menu)
 467{
 468        struct symbol *sym;
 469        struct property *prop;
 470        struct menu *child;
 471        int type, tmp, doint = 2;
 472        tristate val;
 473        char ch;
 474        bool visible;
 475
 476        /*
 477         * note: menu_is_visible() has side effect that it will
 478         * recalc the value of the symbol.
 479         */
 480        visible = menu_is_visible(menu);
 481        if (show_all_options && !menu_has_prompt(menu))
 482                return;
 483        else if (!show_all_options && !visible)
 484                return;
 485
 486        sym = menu->sym;
 487        prop = menu->prompt;
 488        if (!sym) {
 489                if (prop && menu != current_menu) {
 490                        const char *prompt = menu_get_prompt(menu);
 491                        switch (prop->type) {
 492                        case P_MENU:
 493                                child_count++;
 494                                prompt = _(prompt);
 495                                if (single_menu_mode) {
 496                                        item_make("%s%*c%s",
 497                                                  menu->data ? "-->" : "++>",
 498                                                  indent + 1, ' ', prompt);
 499                                } else
 500                                        item_make("   %*c%s  %s",
 501                                                  indent + 1, ' ', prompt,
 502                                                  menu_is_empty(menu) ? "----" : "--->");
 503                                item_set_tag('m');
 504                                item_set_data(menu);
 505                                if (single_menu_mode && menu->data)
 506                                        goto conf_childs;
 507                                return;
 508                        case P_COMMENT:
 509                                if (prompt) {
 510                                        child_count++;
 511                                        item_make("   %*c*** %s ***", indent + 1, ' ', _(prompt));
 512                                        item_set_tag(':');
 513                                        item_set_data(menu);
 514                                }
 515                                break;
 516                        default:
 517                                if (prompt) {
 518                                        child_count++;
 519                                        item_make("---%*c%s", indent + 1, ' ', _(prompt));
 520                                        item_set_tag(':');
 521                                        item_set_data(menu);
 522                                }
 523                        }
 524                } else
 525                        doint = 0;
 526                goto conf_childs;
 527        }
 528
 529        type = sym_get_type(sym);
 530        if (sym_is_choice(sym)) {
 531                struct symbol *def_sym = sym_get_choice_value(sym);
 532                struct menu *def_menu = NULL;
 533
 534                child_count++;
 535                for (child = menu->list; child; child = child->next) {
 536                        if (menu_is_visible(child) && child->sym == def_sym)
 537                                def_menu = child;
 538                }
 539
 540                val = sym_get_tristate_value(sym);
 541                if (sym_is_changable(sym)) {
 542                        switch (type) {
 543                        case S_BOOLEAN:
 544                                item_make("[%c]", val == no ? ' ' : '*');
 545                                break;
 546                        case S_TRISTATE:
 547                                switch (val) {
 548                                case yes: ch = '*'; break;
 549                                case mod: ch = 'M'; break;
 550                                default:  ch = ' '; break;
 551                                }
 552                                item_make("<%c>", ch);
 553                                break;
 554                        }
 555                        item_set_tag('t');
 556                        item_set_data(menu);
 557                } else {
 558                        item_make("   ");
 559                        item_set_tag(def_menu ? 't' : ':');
 560                        item_set_data(menu);
 561                }
 562
 563                item_add_str("%*c%s", indent + 1, ' ', _(menu_get_prompt(menu)));
 564                if (val == yes) {
 565                        if (def_menu) {
 566                                item_add_str(" (%s)", _(menu_get_prompt(def_menu)));
 567                                item_add_str("  --->");
 568                                if (def_menu->list) {
 569                                        indent += 2;
 570                                        build_conf(def_menu);
 571                                        indent -= 2;
 572                                }
 573                        }
 574                        return;
 575                }
 576        } else {
 577                if (menu == current_menu) {
 578                        item_make("---%*c%s", indent + 1, ' ', _(menu_get_prompt(menu)));
 579                        item_set_tag(':');
 580                        item_set_data(menu);
 581                        goto conf_childs;
 582                }
 583                child_count++;
 584                val = sym_get_tristate_value(sym);
 585                if (sym_is_choice_value(sym) && val == yes) {
 586                        item_make("   ");
 587                        item_set_tag(':');
 588                        item_set_data(menu);
 589                } else {
 590                        switch (type) {
 591                        case S_BOOLEAN:
 592                                if (sym_is_changable(sym))
 593                                        item_make("[%c]", val == no ? ' ' : '*');
 594                                else
 595                                        item_make("-%c-", val == no ? ' ' : '*');
 596                                item_set_tag('t');
 597                                item_set_data(menu);
 598                                break;
 599                        case S_TRISTATE:
 600                                switch (val) {
 601                                case yes: ch = '*'; break;
 602                                case mod: ch = 'M'; break;
 603                                default:  ch = ' '; break;
 604                                }
 605                                if (sym_is_changable(sym)) {
 606                                        if (sym->rev_dep.tri == mod)
 607                                                item_make("{%c}", ch);
 608                                        else
 609                                                item_make("<%c>", ch);
 610                                } else
 611                                        item_make("-%c-", ch);
 612                                item_set_tag('t');
 613                                item_set_data(menu);
 614                                break;
 615                        default:
 616                                tmp = 2 + strlen(sym_get_string_value(sym)); /* () = 2 */
 617                                item_make("(%s)", sym_get_string_value(sym));
 618                                tmp = indent - tmp + 4;
 619                                if (tmp < 0)
 620                                        tmp = 0;
 621                                item_add_str("%*c%s%s", tmp, ' ', _(menu_get_prompt(menu)),
 622                                             (sym_has_value(sym) || !sym_is_changable(sym)) ?
 623                                             "" : _(" (NEW)"));
 624                                item_set_tag('s');
 625                                item_set_data(menu);
 626                                goto conf_childs;
 627                        }
 628                }
 629                item_add_str("%*c%s%s", indent + 1, ' ', _(menu_get_prompt(menu)),
 630                          (sym_has_value(sym) || !sym_is_changable(sym)) ?
 631                          "" : _(" (NEW)"));
 632                if (menu->prompt->type == P_MENU) {
 633                        item_add_str("  %s", menu_is_empty(menu) ? "----" : "--->");
 634                        return;
 635                }
 636        }
 637
 638conf_childs:
 639        indent += doint;
 640        for (child = menu->list; child; child = child->next)
 641                build_conf(child);
 642        indent -= doint;
 643}
 644
 645static void conf(struct menu *menu, struct menu *active_menu)
 646{
 647        struct menu *submenu;
 648        const char *prompt = menu_get_prompt(menu);
 649        struct subtitle_part stpart;
 650        struct symbol *sym;
 651        int res;
 652        int s_scroll = 0;
 653
 654        if (menu != &rootmenu)
 655                stpart.text = menu_get_prompt(menu);
 656        else
 657                stpart.text = NULL;
 658        list_add_tail(&stpart.entries, &trail);
 659
 660        while (1) {
 661                item_reset();
 662                current_menu = menu;
 663                build_conf(menu);
 664                if (!child_count)
 665                        break;
 666                set_subtitle();
 667                dialog_clear();
 668                res = dialog_menu(prompt ? _(prompt) : _("Main Menu"),
 669                                  _(menu_instructions),
 670                                  active_menu, &s_scroll);
 671                if (res == 1 || res == KEY_ESC || res == -ERRDISPLAYTOOSMALL)
 672                        break;
 673                if (item_count() != 0) {
 674                        if (!item_activate_selected())
 675                                continue;
 676                        if (!item_tag())
 677                                continue;
 678                }
 679                submenu = item_data();
 680                active_menu = item_data();
 681                if (submenu)
 682                        sym = submenu->sym;
 683                else
 684                        sym = NULL;
 685
 686                switch (res) {
 687                case 0:
 688                        switch (item_tag()) {
 689                        case 'm':
 690                                if (single_menu_mode)
 691                                        submenu->data = (void *) (long) !submenu->data;
 692                                else
 693                                        conf(submenu, NULL);
 694                                break;
 695                        case 't':
 696                                if (sym_is_choice(sym) && sym_get_tristate_value(sym) == yes)
 697                                        conf_choice(submenu);
 698                                else if (submenu->prompt->type == P_MENU)
 699                                        conf(submenu, NULL);
 700                                break;
 701                        case 's':
 702                                conf_string(submenu);
 703                                break;
 704                        }
 705                        break;
 706                case 2:
 707                        if (sym)
 708                                show_help(submenu);
 709                        else {
 710                                reset_subtitle();
 711                                show_helptext(_("README"), _(mconf_readme));
 712                        }
 713                        break;
 714                case 3:
 715                        reset_subtitle();
 716                        conf_save();
 717                        break;
 718                case 4:
 719                        reset_subtitle();
 720                        conf_load();
 721                        break;
 722                case 5:
 723                        if (item_is_tag('t')) {
 724                                if (sym_set_tristate_value(sym, yes))
 725                                        break;
 726                                if (sym_set_tristate_value(sym, mod))
 727                                        show_textbox(NULL, setmod_text, 6, 74);
 728                        }
 729                        break;
 730                case 6:
 731                        if (item_is_tag('t'))
 732                                sym_set_tristate_value(sym, no);
 733                        break;
 734                case 7:
 735                        if (item_is_tag('t'))
 736                                sym_set_tristate_value(sym, mod);
 737                        break;
 738                case 8:
 739                        if (item_is_tag('t'))
 740                                sym_toggle_tristate_value(sym);
 741                        else if (item_is_tag('m'))
 742                                conf(submenu, NULL);
 743                        break;
 744                case 9:
 745                        search_conf();
 746                        break;
 747                case 10:
 748                        show_all_options = !show_all_options;
 749                        break;
 750                }
 751        }
 752
 753        list_del(trail.prev);
 754}
 755
 756static int show_textbox_ext(const char *title, char *text, int r, int c, int
 757                            *keys, int *vscroll, int *hscroll, update_text_fn
 758                            update_text, void *data)
 759{
 760        dialog_clear();
 761        return dialog_textbox(title, text, r, c, keys, vscroll, hscroll,
 762                              update_text, data);
 763}
 764
 765static void show_textbox(const char *title, const char *text, int r, int c)
 766{
 767        show_textbox_ext(title, (char *) text, r, c, (int []) {0}, NULL, NULL,
 768                         NULL, NULL);
 769}
 770
 771static void show_helptext(const char *title, const char *text)
 772{
 773        show_textbox(title, text, 0, 0);
 774}
 775
 776static void conf_message_callback(const char *fmt, va_list ap)
 777{
 778        char buf[PATH_MAX+1];
 779
 780        vsnprintf(buf, sizeof(buf), fmt, ap);
 781        if (save_and_exit) {
 782                if (!silent)
 783                        printf("%s", buf);
 784        } else {
 785                show_textbox(NULL, buf, 6, 60);
 786        }
 787}
 788
 789static void show_help(struct menu *menu)
 790{
 791        struct gstr help = str_new();
 792
 793        help.max_width = getmaxx(stdscr) - 10;
 794        menu_get_ext_help(menu, &help);
 795
 796        show_helptext(_(menu_get_prompt(menu)), str_get(&help));
 797        str_free(&help);
 798}
 799
 800static void conf_choice(struct menu *menu)
 801{
 802        const char *prompt = _(menu_get_prompt(menu));
 803        struct menu *child;
 804        struct symbol *active;
 805
 806        active = sym_get_choice_value(menu->sym);
 807        while (1) {
 808                int res;
 809                int selected;
 810                item_reset();
 811
 812                current_menu = menu;
 813                for (child = menu->list; child; child = child->next) {
 814                        if (!menu_is_visible(child))
 815                                continue;
 816                        if (child->sym)
 817                                item_make("%s", _(menu_get_prompt(child)));
 818                        else {
 819                                item_make("*** %s ***", _(menu_get_prompt(child)));
 820                                item_set_tag(':');
 821                        }
 822                        item_set_data(child);
 823                        if (child->sym == active)
 824                                item_set_selected(1);
 825                        if (child->sym == sym_get_choice_value(menu->sym))
 826                                item_set_tag('X');
 827                }
 828                dialog_clear();
 829                res = dialog_checklist(prompt ? _(prompt) : _("Main Menu"),
 830                                        _(radiolist_instructions),
 831                                        MENUBOX_HEIGTH_MIN,
 832                                        MENUBOX_WIDTH_MIN,
 833                                        CHECKLIST_HEIGTH_MIN);
 834                selected = item_activate_selected();
 835                switch (res) {
 836                case 0:
 837                        if (selected) {
 838                                child = item_data();
 839                                if (!child->sym)
 840                                        break;
 841
 842                                sym_set_tristate_value(child->sym, yes);
 843                        }
 844                        return;
 845                case 1:
 846                        if (selected) {
 847                                child = item_data();
 848                                show_help(child);
 849                                active = child->sym;
 850                        } else
 851                                show_help(menu);
 852                        break;
 853                case KEY_ESC:
 854                        return;
 855                case -ERRDISPLAYTOOSMALL:
 856                        return;
 857                }
 858        }
 859}
 860
 861static void conf_string(struct menu *menu)
 862{
 863        const char *prompt = menu_get_prompt(menu);
 864
 865        while (1) {
 866                int res;
 867                const char *heading;
 868
 869                switch (sym_get_type(menu->sym)) {
 870                case S_INT:
 871                        heading = _(inputbox_instructions_int);
 872                        break;
 873                case S_HEX:
 874                        heading = _(inputbox_instructions_hex);
 875                        break;
 876                case S_STRING:
 877                        heading = _(inputbox_instructions_string);
 878                        break;
 879                default:
 880                        heading = _("Internal mconf error!");
 881                }
 882                dialog_clear();
 883                res = dialog_inputbox(prompt ? _(prompt) : _("Main Menu"),
 884                                      heading, 10, 75,
 885                                      sym_get_string_value(menu->sym));
 886                switch (res) {
 887                case 0:
 888                        if (sym_set_string_value(menu->sym, dialog_input_result))
 889                                return;
 890                        show_textbox(NULL, _("You have made an invalid entry."), 5, 43);
 891                        break;
 892                case 1:
 893                        show_help(menu);
 894                        break;
 895                case KEY_ESC:
 896                        return;
 897                }
 898        }
 899}
 900
 901static void conf_load(void)
 902{
 903
 904        while (1) {
 905                int res;
 906                dialog_clear();
 907                res = dialog_inputbox(NULL, load_config_text,
 908                                      11, 55, filename);
 909                switch(res) {
 910                case 0:
 911                        if (!dialog_input_result[0])
 912                                return;
 913                        if (!conf_read(dialog_input_result)) {
 914                                set_config_filename(dialog_input_result);
 915                                sym_set_change_count(1);
 916                                return;
 917                        }
 918                        show_textbox(NULL, _("File does not exist!"), 5, 38);
 919                        break;
 920                case 1:
 921                        show_helptext(_("Load Alternate Configuration"), load_config_help);
 922                        break;
 923                case KEY_ESC:
 924                        return;
 925                }
 926        }
 927}
 928
 929static void conf_save(void)
 930{
 931        while (1) {
 932                int res;
 933                dialog_clear();
 934                res = dialog_inputbox(NULL, save_config_text,
 935                                      11, 55, filename);
 936                switch(res) {
 937                case 0:
 938                        if (!dialog_input_result[0])
 939                                return;
 940                        if (!conf_write(dialog_input_result)) {
 941                                set_config_filename(dialog_input_result);
 942                                return;
 943                        }
 944                        show_textbox(NULL, _("Can't create file!  Probably a nonexistent directory."), 5, 60);
 945                        break;
 946                case 1:
 947                        show_helptext(_("Save Alternate Configuration"), save_config_help);
 948                        break;
 949                case KEY_ESC:
 950                        return;
 951                }
 952        }
 953}
 954
 955static int handle_exit(void)
 956{
 957        int res;
 958
 959        save_and_exit = 1;
 960        reset_subtitle();
 961        dialog_clear();
 962        if (conf_get_changed())
 963                res = dialog_yesno(NULL,
 964                                   _("Do you wish to save your new configuration?\n"
 965                                     "(Press <ESC><ESC> to continue kernel configuration.)"),
 966                                   6, 60);
 967        else
 968                res = -1;
 969
 970        end_dialog(saved_x, saved_y);
 971
 972        switch (res) {
 973        case 0:
 974                if (conf_write(filename)) {
 975                        fprintf(stderr, _("\n\n"
 976                                          "Error while writing of the configuration.\n"
 977                                          "Your configuration changes were NOT saved."
 978                                          "\n\n"));
 979                        return 1;
 980                }
 981                /* fall through */
 982        case -1:
 983                if (!silent)
 984                        printf(_("\n\n"
 985                                 "*** End of the configuration.\n"
 986                                 "*** Execute 'make' to start the build or try 'make help'."
 987                                 "\n\n"));
 988                res = 0;
 989                break;
 990        default:
 991                if (!silent)
 992                        fprintf(stderr, _("\n\n"
 993                                          "Your configuration changes were NOT saved."
 994                                          "\n\n"));
 995                if (res != KEY_ESC)
 996                        res = 0;
 997        }
 998
 999        return res;
1000}
1001
1002static void sig_handler(int signo)
1003{
1004        exit(handle_exit());
1005}
1006
1007int main(int ac, char **av)
1008{
1009        char *mode;
1010        int res;
1011
1012        setlocale(LC_ALL, "");
1013        bindtextdomain(PACKAGE, LOCALEDIR);
1014        textdomain(PACKAGE);
1015
1016        signal(SIGINT, sig_handler);
1017
1018        if (ac > 1 && strcmp(av[1], "-s") == 0) {
1019                silent = 1;
1020                /* Silence conf_read() until the real callback is set up */
1021                conf_set_message_callback(NULL);
1022                av++;
1023        }
1024        conf_parse(av[1]);
1025        conf_read(NULL);
1026
1027        mode = getenv("MENUCONFIG_MODE");
1028        if (mode) {
1029                if (!strcasecmp(mode, "single_menu"))
1030                        single_menu_mode = 1;
1031        }
1032
1033        if (init_dialog(NULL)) {
1034                fprintf(stderr, N_("Your display is too small to run Menuconfig!\n"));
1035                fprintf(stderr, N_("It must be at least 19 lines by 80 columns.\n"));
1036                return 1;
1037        }
1038
1039        set_config_filename(conf_get_configname());
1040        conf_set_message_callback(conf_message_callback);
1041        do {
1042                conf(&rootmenu, NULL);
1043                res = handle_exit();
1044        } while (res == KEY_ESC);
1045
1046        return res;
1047}
1048