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