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