linux/scripts/kconfig/mconf.c
<<
>>
Prefs
   1/*
   2 * Copyright (C) 2002 Roman Zippel <zippel@linux-m68k.org>
   3 * Released under the terms of the GNU GPL v2.0.
   4 *
   5 * Introduced single menu mode (show all sub-menus in one large tree).
   6 * 2002-11-06 Petr Baudis <pasky@ucw.cz>
   7 *
   8 * i18n, 2005, Arnaldo Carvalho de Melo <acme@conectiva.com.br>
   9 */
  10
  11#include <ctype.h>
  12#include <errno.h>
  13#include <fcntl.h>
  14#include <limits.h>
  15#include <stdarg.h>
  16#include <stdlib.h>
  17#include <string.h>
  18#include <signal.h>
  19#include <unistd.h>
  20
  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                                prompt = prompt;
 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_changable(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_changable(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_changable(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_changable(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_changable(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 *fmt, va_list ap)
 776{
 777        char buf[PATH_MAX+1];
 778
 779        vsnprintf(buf, sizeof(buf), fmt, ap);
 780        if (save_and_exit) {
 781                if (!silent)
 782                        printf("%s", buf);
 783        } else {
 784                show_textbox(NULL, buf, 6, 60);
 785        }
 786}
 787
 788static void show_help(struct menu *menu)
 789{
 790        struct gstr help = str_new();
 791
 792        help.max_width = getmaxx(stdscr) - 10;
 793        menu_get_ext_help(menu, &help);
 794
 795        show_helptext(menu_get_prompt(menu), str_get(&help));
 796        str_free(&help);
 797}
 798
 799static void conf_choice(struct menu *menu)
 800{
 801        const char *prompt = menu_get_prompt(menu);
 802        struct menu *child;
 803        struct symbol *active;
 804
 805        active = sym_get_choice_value(menu->sym);
 806        while (1) {
 807                int res;
 808                int selected;
 809                item_reset();
 810
 811                current_menu = menu;
 812                for (child = menu->list; child; child = child->next) {
 813                        if (!menu_is_visible(child))
 814                                continue;
 815                        if (child->sym)
 816                                item_make("%s", menu_get_prompt(child));
 817                        else {
 818                                item_make("*** %s ***", menu_get_prompt(child));
 819                                item_set_tag(':');
 820                        }
 821                        item_set_data(child);
 822                        if (child->sym == active)
 823                                item_set_selected(1);
 824                        if (child->sym == sym_get_choice_value(menu->sym))
 825                                item_set_tag('X');
 826                }
 827                dialog_clear();
 828                res = dialog_checklist(prompt ? prompt : "Main Menu",
 829                                        radiolist_instructions,
 830                                        MENUBOX_HEIGTH_MIN,
 831                                        MENUBOX_WIDTH_MIN,
 832                                        CHECKLIST_HEIGTH_MIN);
 833                selected = item_activate_selected();
 834                switch (res) {
 835                case 0:
 836                        if (selected) {
 837                                child = item_data();
 838                                if (!child->sym)
 839                                        break;
 840
 841                                sym_set_tristate_value(child->sym, yes);
 842                        }
 843                        return;
 844                case 1:
 845                        if (selected) {
 846                                child = item_data();
 847                                show_help(child);
 848                                active = child->sym;
 849                        } else
 850                                show_help(menu);
 851                        break;
 852                case KEY_ESC:
 853                        return;
 854                case -ERRDISPLAYTOOSMALL:
 855                        return;
 856                }
 857        }
 858}
 859
 860static void conf_string(struct menu *menu)
 861{
 862        const char *prompt = menu_get_prompt(menu);
 863
 864        while (1) {
 865                int res;
 866                const char *heading;
 867
 868                switch (sym_get_type(menu->sym)) {
 869                case S_INT:
 870                        heading = inputbox_instructions_int;
 871                        break;
 872                case S_HEX:
 873                        heading = inputbox_instructions_hex;
 874                        break;
 875                case S_STRING:
 876                        heading = inputbox_instructions_string;
 877                        break;
 878                default:
 879                        heading = "Internal mconf error!";
 880                }
 881                dialog_clear();
 882                res = dialog_inputbox(prompt ? prompt : "Main Menu",
 883                                      heading, 10, 75,
 884                                      sym_get_string_value(menu->sym));
 885                switch (res) {
 886                case 0:
 887                        if (sym_set_string_value(menu->sym, dialog_input_result))
 888                                return;
 889                        show_textbox(NULL, "You have made an invalid entry.", 5, 43);
 890                        break;
 891                case 1:
 892                        show_help(menu);
 893                        break;
 894                case KEY_ESC:
 895                        return;
 896                }
 897        }
 898}
 899
 900static void conf_load(void)
 901{
 902
 903        while (1) {
 904                int res;
 905                dialog_clear();
 906                res = dialog_inputbox(NULL, load_config_text,
 907                                      11, 55, filename);
 908                switch(res) {
 909                case 0:
 910                        if (!dialog_input_result[0])
 911                                return;
 912                        if (!conf_read(dialog_input_result)) {
 913                                set_config_filename(dialog_input_result);
 914                                sym_set_change_count(1);
 915                                return;
 916                        }
 917                        show_textbox(NULL, "File does not exist!", 5, 38);
 918                        break;
 919                case 1:
 920                        show_helptext("Load Alternate Configuration", load_config_help);
 921                        break;
 922                case KEY_ESC:
 923                        return;
 924                }
 925        }
 926}
 927
 928static void conf_save(void)
 929{
 930        while (1) {
 931                int res;
 932                dialog_clear();
 933                res = dialog_inputbox(NULL, save_config_text,
 934                                      11, 55, filename);
 935                switch(res) {
 936                case 0:
 937                        if (!dialog_input_result[0])
 938                                return;
 939                        if (!conf_write(dialog_input_result)) {
 940                                set_config_filename(dialog_input_result);
 941                                return;
 942                        }
 943                        show_textbox(NULL, "Can't create file!  Probably a nonexistent directory.", 5, 60);
 944                        break;
 945                case 1:
 946                        show_helptext("Save Alternate Configuration", save_config_help);
 947                        break;
 948                case KEY_ESC:
 949                        return;
 950                }
 951        }
 952}
 953
 954static int handle_exit(void)
 955{
 956        int res;
 957
 958        save_and_exit = 1;
 959        reset_subtitle();
 960        dialog_clear();
 961        if (conf_get_changed())
 962                res = dialog_yesno(NULL,
 963                                   "Do you wish to save your new configuration?\n"
 964                                     "(Press <ESC><ESC> to continue kernel configuration.)",
 965                                   6, 60);
 966        else
 967                res = -1;
 968
 969        end_dialog(saved_x, saved_y);
 970
 971        switch (res) {
 972        case 0:
 973                if (conf_write(filename)) {
 974                        fprintf(stderr, "\n\n"
 975                                          "Error while writing of the configuration.\n"
 976                                          "Your configuration changes were NOT saved."
 977                                          "\n\n");
 978                        return 1;
 979                }
 980                /* fall through */
 981        case -1:
 982                if (!silent)
 983                        printf("\n\n"
 984                                 "*** End of the configuration.\n"
 985                                 "*** Execute 'make' to start the build or try 'make help'."
 986                                 "\n\n");
 987                res = 0;
 988                break;
 989        default:
 990                if (!silent)
 991                        fprintf(stderr, "\n\n"
 992                                          "Your configuration changes were NOT saved."
 993                                          "\n\n");
 994                if (res != KEY_ESC)
 995                        res = 0;
 996        }
 997
 998        return res;
 999}
1000
1001static void sig_handler(int signo)
1002{
1003        exit(handle_exit());
1004}
1005
1006int main(int ac, char **av)
1007{
1008        char *mode;
1009        int res;
1010
1011        signal(SIGINT, sig_handler);
1012
1013        if (ac > 1 && strcmp(av[1], "-s") == 0) {
1014                silent = 1;
1015                /* Silence conf_read() until the real callback is set up */
1016                conf_set_message_callback(NULL);
1017                av++;
1018        }
1019        conf_parse(av[1]);
1020        conf_read(NULL);
1021
1022        mode = getenv("MENUCONFIG_MODE");
1023        if (mode) {
1024                if (!strcasecmp(mode, "single_menu"))
1025                        single_menu_mode = 1;
1026        }
1027
1028        if (init_dialog(NULL)) {
1029                fprintf(stderr, "Your display is too small to run Menuconfig!\n");
1030                fprintf(stderr, "It must be at least 19 lines by 80 columns.\n");
1031                return 1;
1032        }
1033
1034        set_config_filename(conf_get_configname());
1035        conf_set_message_callback(conf_message_callback);
1036        do {
1037                conf(&rootmenu, NULL);
1038                res = handle_exit();
1039        } while (res == KEY_ESC);
1040
1041        return res;
1042}
1043