linux/scripts/kconfig/mconf.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2/*
   3 * Copyright (C) 2002 Roman Zippel <zippel@linux-m68k.org>
   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 <strings.h>
  19#include <signal.h>
  20#include <unistd.h>
  21
  22#include "lkc.h"
  23#include "lxdialog/dialog.h"
  24
  25static const char mconf_readme[] =
  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[] =
 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[] =
 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[] =
 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[] =
 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[] =
 195        "Please enter a string value. "
 196        "Use the <TAB> key to move from the input field to the buttons below it.",
 197setmod_text[] =
 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[] =
 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[] =
 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[] =
 216        "Enter a filename to which this configuration should be saved "
 217        "as an alternate.  Leave blank to abort.",
 218save_config_help[] =
 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[] =
 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 (bool, 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                                if (single_menu_mode) {
 495                                        item_make("%s%*c%s",
 496                                                  menu->data ? "-->" : "++>",
 497                                                  indent + 1, ' ', prompt);
 498                                } else
 499                                        item_make("   %*c%s  %s",
 500                                                  indent + 1, ' ', prompt,
 501                                                  menu_is_empty(menu) ? "----" : "--->");
 502                                item_set_tag('m');
 503                                item_set_data(menu);
 504                                if (single_menu_mode && menu->data)
 505                                        goto conf_childs;
 506                                return;
 507                        case P_COMMENT:
 508                                if (prompt) {
 509                                        child_count++;
 510                                        item_make("   %*c*** %s ***", indent + 1, ' ', prompt);
 511                                        item_set_tag(':');
 512                                        item_set_data(menu);
 513                                }
 514                                break;
 515                        default:
 516                                if (prompt) {
 517                                        child_count++;
 518                                        item_make("---%*c%s", indent + 1, ' ', prompt);
 519                                        item_set_tag(':');
 520                                        item_set_data(menu);
 521                                }
 522                        }
 523                } else
 524                        doint = 0;
 525                goto conf_childs;
 526        }
 527
 528        type = sym_get_type(sym);
 529        if (sym_is_choice(sym)) {
 530                struct symbol *def_sym = sym_get_choice_value(sym);
 531                struct menu *def_menu = NULL;
 532
 533                child_count++;
 534                for (child = menu->list; child; child = child->next) {
 535                        if (menu_is_visible(child) && child->sym == def_sym)
 536                                def_menu = child;
 537                }
 538
 539                val = sym_get_tristate_value(sym);
 540                if (sym_is_changeable(sym)) {
 541                        switch (type) {
 542                        case S_BOOLEAN:
 543                                item_make("[%c]", val == no ? ' ' : '*');
 544                                break;
 545                        case S_TRISTATE:
 546                                switch (val) {
 547                                case yes: ch = '*'; break;
 548                                case mod: ch = 'M'; break;
 549                                default:  ch = ' '; break;
 550                                }
 551                                item_make("<%c>", ch);
 552                                break;
 553                        }
 554                        item_set_tag('t');
 555                        item_set_data(menu);
 556                } else {
 557                        item_make("   ");
 558                        item_set_tag(def_menu ? 't' : ':');
 559                        item_set_data(menu);
 560                }
 561
 562                item_add_str("%*c%s", indent + 1, ' ', menu_get_prompt(menu));
 563                if (val == yes) {
 564                        if (def_menu) {
 565                                item_add_str(" (%s)", menu_get_prompt(def_menu));
 566                                item_add_str("  --->");
 567                                if (def_menu->list) {
 568                                        indent += 2;
 569                                        build_conf(def_menu);
 570                                        indent -= 2;
 571                                }
 572                        }
 573                        return;
 574                }
 575        } else {
 576                if (menu == current_menu) {
 577                        item_make("---%*c%s", indent + 1, ' ', menu_get_prompt(menu));
 578                        item_set_tag(':');
 579                        item_set_data(menu);
 580                        goto conf_childs;
 581                }
 582                child_count++;
 583                val = sym_get_tristate_value(sym);
 584                if (sym_is_choice_value(sym) && val == yes) {
 585                        item_make("   ");
 586                        item_set_tag(':');
 587                        item_set_data(menu);
 588                } else {
 589                        switch (type) {
 590                        case S_BOOLEAN:
 591                                if (sym_is_changeable(sym))
 592                                        item_make("[%c]", val == no ? ' ' : '*');
 593                                else
 594                                        item_make("-%c-", val == no ? ' ' : '*');
 595                                item_set_tag('t');
 596                                item_set_data(menu);
 597                                break;
 598                        case S_TRISTATE:
 599                                switch (val) {
 600                                case yes: ch = '*'; break;
 601                                case mod: ch = 'M'; break;
 602                                default:  ch = ' '; break;
 603                                }
 604                                if (sym_is_changeable(sym)) {
 605                                        if (sym->rev_dep.tri == mod)
 606                                                item_make("{%c}", ch);
 607                                        else
 608                                                item_make("<%c>", ch);
 609                                } else
 610                                        item_make("-%c-", ch);
 611                                item_set_tag('t');
 612                                item_set_data(menu);
 613                                break;
 614                        default:
 615                                tmp = 2 + strlen(sym_get_string_value(sym)); /* () = 2 */
 616                                item_make("(%s)", sym_get_string_value(sym));
 617                                tmp = indent - tmp + 4;
 618                                if (tmp < 0)
 619                                        tmp = 0;
 620                                item_add_str("%*c%s%s", tmp, ' ', menu_get_prompt(menu),
 621                                             (sym_has_value(sym) || !sym_is_changeable(sym)) ?
 622                                             "" : " (NEW)");
 623                                item_set_tag('s');
 624                                item_set_data(menu);
 625                                goto conf_childs;
 626                        }
 627                }
 628                item_add_str("%*c%s%s", indent + 1, ' ', menu_get_prompt(menu),
 629                          (sym_has_value(sym) || !sym_is_changeable(sym)) ?
 630                          "" : " (NEW)");
 631                if (menu->prompt->type == P_MENU) {
 632                        item_add_str("  %s", menu_is_empty(menu) ? "----" : "--->");
 633                        return;
 634                }
 635        }
 636
 637conf_childs:
 638        indent += doint;
 639        for (child = menu->list; child; child = child->next)
 640                build_conf(child);
 641        indent -= doint;
 642}
 643
 644static void conf(struct menu *menu, struct menu *active_menu)
 645{
 646        struct menu *submenu;
 647        const char *prompt = menu_get_prompt(menu);
 648        struct subtitle_part stpart;
 649        struct symbol *sym;
 650        int res;
 651        int s_scroll = 0;
 652
 653        if (menu != &rootmenu)
 654                stpart.text = menu_get_prompt(menu);
 655        else
 656                stpart.text = NULL;
 657        list_add_tail(&stpart.entries, &trail);
 658
 659        while (1) {
 660                item_reset();
 661                current_menu = menu;
 662                build_conf(menu);
 663                if (!child_count)
 664                        break;
 665                set_subtitle();
 666                dialog_clear();
 667                res = dialog_menu(prompt ? prompt : "Main Menu",
 668                                  menu_instructions,
 669                                  active_menu, &s_scroll);
 670                if (res == 1 || res == KEY_ESC || res == -ERRDISPLAYTOOSMALL)
 671                        break;
 672                if (item_count() != 0) {
 673                        if (!item_activate_selected())
 674                                continue;
 675                        if (!item_tag())
 676                                continue;
 677                }
 678                submenu = item_data();
 679                active_menu = item_data();
 680                if (submenu)
 681                        sym = submenu->sym;
 682                else
 683                        sym = NULL;
 684
 685                switch (res) {
 686                case 0:
 687                        switch (item_tag()) {
 688                        case 'm':
 689                                if (single_menu_mode)
 690                                        submenu->data = (void *) (long) !submenu->data;
 691                                else
 692                                        conf(submenu, NULL);
 693                                break;
 694                        case 't':
 695                                if (sym_is_choice(sym) && sym_get_tristate_value(sym) == yes)
 696                                        conf_choice(submenu);
 697                                else if (submenu->prompt->type == P_MENU)
 698                                        conf(submenu, NULL);
 699                                break;
 700                        case 's':
 701                                conf_string(submenu);
 702                                break;
 703                        }
 704                        break;
 705                case 2:
 706                        if (sym)
 707                                show_help(submenu);
 708                        else {
 709                                reset_subtitle();
 710                                show_helptext("README", mconf_readme);
 711                        }
 712                        break;
 713                case 3:
 714                        reset_subtitle();
 715                        conf_save();
 716                        break;
 717                case 4:
 718                        reset_subtitle();
 719                        conf_load();
 720                        break;
 721                case 5:
 722                        if (item_is_tag('t')) {
 723                                if (sym_set_tristate_value(sym, yes))
 724                                        break;
 725                                if (sym_set_tristate_value(sym, mod))
 726                                        show_textbox(NULL, setmod_text, 6, 74);
 727                        }
 728                        break;
 729                case 6:
 730                        if (item_is_tag('t'))
 731                                sym_set_tristate_value(sym, no);
 732                        break;
 733                case 7:
 734                        if (item_is_tag('t'))
 735                                sym_set_tristate_value(sym, mod);
 736                        break;
 737                case 8:
 738                        if (item_is_tag('t'))
 739                                sym_toggle_tristate_value(sym);
 740                        else if (item_is_tag('m'))
 741                                conf(submenu, NULL);
 742                        break;
 743                case 9:
 744                        search_conf();
 745                        break;
 746                case 10:
 747                        show_all_options = !show_all_options;
 748                        break;
 749                }
 750        }
 751
 752        list_del(trail.prev);
 753}
 754
 755static int show_textbox_ext(const char *title, char *text, int r, int c, int
 756                            *keys, int *vscroll, int *hscroll, update_text_fn
 757                            update_text, void *data)
 758{
 759        dialog_clear();
 760        return dialog_textbox(title, text, r, c, keys, vscroll, hscroll,
 761                              update_text, data);
 762}
 763
 764static void show_textbox(const char *title, const char *text, int r, int c)
 765{
 766        show_textbox_ext(title, (char *) text, r, c, (int []) {0}, NULL, NULL,
 767                         NULL, NULL);
 768}
 769
 770static void show_helptext(const char *title, const char *text)
 771{
 772        show_textbox(title, text, 0, 0);
 773}
 774
 775static void conf_message_callback(const char *s)
 776{
 777        if (save_and_exit) {
 778                if (!silent)
 779                        printf("%s", s);
 780        } else {
 781                show_textbox(NULL, s, 6, 60);
 782        }
 783}
 784
 785static void show_help(struct menu *menu)
 786{
 787        struct gstr help = str_new();
 788
 789        help.max_width = getmaxx(stdscr) - 10;
 790        menu_get_ext_help(menu, &help);
 791
 792        show_helptext(menu_get_prompt(menu), str_get(&help));
 793        str_free(&help);
 794}
 795
 796static void conf_choice(struct menu *menu)
 797{
 798        const char *prompt = menu_get_prompt(menu);
 799        struct menu *child;
 800        struct symbol *active;
 801
 802        active = sym_get_choice_value(menu->sym);
 803        while (1) {
 804                int res;
 805                int selected;
 806                item_reset();
 807
 808                current_menu = menu;
 809                for (child = menu->list; child; child = child->next) {
 810                        if (!menu_is_visible(child))
 811                                continue;
 812                        if (child->sym)
 813                                item_make("%s", menu_get_prompt(child));
 814                        else {
 815                                item_make("*** %s ***", menu_get_prompt(child));
 816                                item_set_tag(':');
 817                        }
 818                        item_set_data(child);
 819                        if (child->sym == active)
 820                                item_set_selected(1);
 821                        if (child->sym == sym_get_choice_value(menu->sym))
 822                                item_set_tag('X');
 823                }
 824                dialog_clear();
 825                res = dialog_checklist(prompt ? prompt : "Main Menu",
 826                                        radiolist_instructions,
 827                                        MENUBOX_HEIGTH_MIN,
 828                                        MENUBOX_WIDTH_MIN,
 829                                        CHECKLIST_HEIGTH_MIN);
 830                selected = item_activate_selected();
 831                switch (res) {
 832                case 0:
 833                        if (selected) {
 834                                child = item_data();
 835                                if (!child->sym)
 836                                        break;
 837
 838                                sym_set_tristate_value(child->sym, yes);
 839                        }
 840                        return;
 841                case 1:
 842                        if (selected) {
 843                                child = item_data();
 844                                show_help(child);
 845                                active = child->sym;
 846                        } else
 847                                show_help(menu);
 848                        break;
 849                case KEY_ESC:
 850                        return;
 851                case -ERRDISPLAYTOOSMALL:
 852                        return;
 853                }
 854        }
 855}
 856
 857static void conf_string(struct menu *menu)
 858{
 859        const char *prompt = menu_get_prompt(menu);
 860
 861        while (1) {
 862                int res;
 863                const char *heading;
 864
 865                switch (sym_get_type(menu->sym)) {
 866                case S_INT:
 867                        heading = inputbox_instructions_int;
 868                        break;
 869                case S_HEX:
 870                        heading = inputbox_instructions_hex;
 871                        break;
 872                case S_STRING:
 873                        heading = inputbox_instructions_string;
 874                        break;
 875                default:
 876                        heading = "Internal mconf error!";
 877                }
 878                dialog_clear();
 879                res = dialog_inputbox(prompt ? prompt : "Main Menu",
 880                                      heading, 10, 75,
 881                                      sym_get_string_value(menu->sym));
 882                switch (res) {
 883                case 0:
 884                        if (sym_set_string_value(menu->sym, dialog_input_result))
 885                                return;
 886                        show_textbox(NULL, "You have made an invalid entry.", 5, 43);
 887                        break;
 888                case 1:
 889                        show_help(menu);
 890                        break;
 891                case KEY_ESC:
 892                        return;
 893                }
 894        }
 895}
 896
 897static void conf_load(void)
 898{
 899
 900        while (1) {
 901                int res;
 902                dialog_clear();
 903                res = dialog_inputbox(NULL, load_config_text,
 904                                      11, 55, filename);
 905                switch(res) {
 906                case 0:
 907                        if (!dialog_input_result[0])
 908                                return;
 909                        if (!conf_read(dialog_input_result)) {
 910                                set_config_filename(dialog_input_result);
 911                                sym_set_change_count(1);
 912                                return;
 913                        }
 914                        show_textbox(NULL, "File does not exist!", 5, 38);
 915                        break;
 916                case 1:
 917                        show_helptext("Load Alternate Configuration", load_config_help);
 918                        break;
 919                case KEY_ESC:
 920                        return;
 921                }
 922        }
 923}
 924
 925static void conf_save(void)
 926{
 927        while (1) {
 928                int res;
 929                dialog_clear();
 930                res = dialog_inputbox(NULL, save_config_text,
 931                                      11, 55, filename);
 932                switch(res) {
 933                case 0:
 934                        if (!dialog_input_result[0])
 935                                return;
 936                        if (!conf_write(dialog_input_result)) {
 937                                set_config_filename(dialog_input_result);
 938                                return;
 939                        }
 940                        show_textbox(NULL, "Can't create file!", 5, 60);
 941                        break;
 942                case 1:
 943                        show_helptext("Save Alternate Configuration", save_config_help);
 944                        break;
 945                case KEY_ESC:
 946                        return;
 947                }
 948        }
 949}
 950
 951static int handle_exit(void)
 952{
 953        int res;
 954
 955        save_and_exit = 1;
 956        reset_subtitle();
 957        dialog_clear();
 958        if (conf_get_changed())
 959                res = dialog_yesno(NULL,
 960                                   "Do you wish to save your new configuration?\n"
 961                                     "(Press <ESC><ESC> to continue kernel configuration.)",
 962                                   6, 60);
 963        else
 964                res = -1;
 965
 966        end_dialog(saved_x, saved_y);
 967
 968        switch (res) {
 969        case 0:
 970                if (conf_write(filename)) {
 971                        fprintf(stderr, "\n\n"
 972                                          "Error while writing of the configuration.\n"
 973                                          "Your configuration changes were NOT saved."
 974                                          "\n\n");
 975                        return 1;
 976                }
 977                conf_write_autoconf(0);
 978                /* fall through */
 979        case -1:
 980                if (!silent)
 981                        printf("\n\n"
 982                                 "*** End of the configuration.\n"
 983                                 "*** Execute 'make' to start the build or try 'make help'."
 984                                 "\n\n");
 985                res = 0;
 986                break;
 987        default:
 988                if (!silent)
 989                        fprintf(stderr, "\n\n"
 990                                          "Your configuration changes were NOT saved."
 991                                          "\n\n");
 992                if (res != KEY_ESC)
 993                        res = 0;
 994        }
 995
 996        return res;
 997}
 998
 999static void sig_handler(int signo)
1000{
1001        exit(handle_exit());
1002}
1003
1004int main(int ac, char **av)
1005{
1006        char *mode;
1007        int res;
1008
1009        signal(SIGINT, sig_handler);
1010
1011        if (ac > 1 && strcmp(av[1], "-s") == 0) {
1012                silent = 1;
1013                /* Silence conf_read() until the real callback is set up */
1014                conf_set_message_callback(NULL);
1015                av++;
1016        }
1017        conf_parse(av[1]);
1018        conf_read(NULL);
1019
1020        mode = getenv("MENUCONFIG_MODE");
1021        if (mode) {
1022                if (!strcasecmp(mode, "single_menu"))
1023                        single_menu_mode = 1;
1024        }
1025
1026        if (init_dialog(NULL)) {
1027                fprintf(stderr, "Your display is too small to run Menuconfig!\n");
1028                fprintf(stderr, "It must be at least 19 lines by 80 columns.\n");
1029                return 1;
1030        }
1031
1032        set_config_filename(conf_get_configname());
1033        conf_set_message_callback(conf_message_callback);
1034        do {
1035                conf(&rootmenu, NULL);
1036                res = handle_exit();
1037        } while (res == KEY_ESC);
1038
1039        return res;
1040}
1041