linux/scripts/kconfig/mconf.c
<<
>>
Prefs
   1/*
   2 * Copyright (C) 2002 Roman Zippel <zippel@linux-m68k.org>
   3 * Released under the terms of the GNU GPL v2.0.
   4 *
   5 * Introduced single menu mode (show all sub-menus in one large tree).
   6 * 2002-11-06 Petr Baudis <pasky@ucw.cz>
   7 *
   8 * i18n, 2005, Arnaldo Carvalho de Melo <acme@conectiva.com.br>
   9 */
  10
  11#include <ctype.h>
  12#include <errno.h>
  13#include <fcntl.h>
  14#include <limits.h>
  15#include <stdarg.h>
  16#include <stdlib.h>
  17#include <string.h>
  18#include <signal.h>
  19#include <unistd.h>
  20#include <locale.h>
  21
  22#include "lkc.h"
  23#include "lxdialog/dialog.h"
  24
  25static const char mconf_readme[] = N_(
  26"Overview\n"
  27"--------\n"
  28"This interface lets you select features and parameters for the build.\n"
  29"Features can either be built-in, modularized, or ignored. Parameters\n"
  30"must be entered in as decimal or hexadecimal numbers or text.\n"
  31"\n"
  32"Menu items beginning with following braces represent features that\n"
  33"  [ ] can be built in or removed\n"
  34"  < > can be built in, modularized or removed\n"
  35"  { } can be built in or modularized (selected by other feature)\n"
  36"  - - are selected by other feature,\n"
  37"while *, M or whitespace inside braces means to build in, build as\n"
  38"a module or to exclude the feature respectively.\n"
  39"\n"
  40"To change any of these features, highlight it with the cursor\n"
  41"keys and press <Y> to build it in, <M> to make it a module or\n"
  42"<N> to remove it.  You may also press the <Space Bar> to cycle\n"
  43"through the available options (i.e. Y->N->M->Y).\n"
  44"\n"
  45"Some additional keyboard hints:\n"
  46"\n"
  47"Menus\n"
  48"----------\n"
  49"o  Use the Up/Down arrow keys (cursor keys) to highlight the item you\n"
  50"   wish to change or the submenu you wish to select and press <Enter>.\n"
  51"   Submenus are designated by \"--->\", empty ones by \"----\".\n"
  52"\n"
  53"   Shortcut: Press the option's highlighted letter (hotkey).\n"
  54"             Pressing a hotkey more than once will sequence\n"
  55"             through all visible items which use that hotkey.\n"
  56"\n"
  57"   You may also use the <PAGE UP> and <PAGE DOWN> keys to scroll\n"
  58"   unseen options into view.\n"
  59"\n"
  60"o  To exit a menu use the cursor keys to highlight the <Exit> button\n"
  61"   and press <ENTER>.\n"
  62"\n"
  63"   Shortcut: Press <ESC><ESC> or <E> or <X> if there is no hotkey\n"
  64"             using those letters.  You may press a single <ESC>, but\n"
  65"             there is a delayed response which you may find annoying.\n"
  66"\n"
  67"   Also, the <TAB> and cursor keys will cycle between <Select>,\n"
  68"   <Exit>, <Help>, <Save>, and <Load>.\n"
  69"\n"
  70"o  To get help with an item, use the cursor keys to highlight <Help>\n"
  71"   and press <ENTER>.\n"
  72"\n"
  73"   Shortcut: Press <H> or <?>.\n"
  74"\n"
  75"o  To toggle the display of hidden options, press <Z>.\n"
  76"\n"
  77"\n"
  78"Radiolists  (Choice lists)\n"
  79"-----------\n"
  80"o  Use the cursor keys to select the option you wish to set and press\n"
  81"   <S> or the <SPACE BAR>.\n"
  82"\n"
  83"   Shortcut: Press the first letter of the option you wish to set then\n"
  84"             press <S> or <SPACE BAR>.\n"
  85"\n"
  86"o  To see available help for the item, use the cursor keys to highlight\n"
  87"   <Help> and Press <ENTER>.\n"
  88"\n"
  89"   Shortcut: Press <H> or <?>.\n"
  90"\n"
  91"   Also, the <TAB> and cursor keys will cycle between <Select> and\n"
  92"   <Help>\n"
  93"\n"
  94"\n"
  95"Data Entry\n"
  96"-----------\n"
  97"o  Enter the requested information and press <ENTER>\n"
  98"   If you are entering hexadecimal values, it is not necessary to\n"
  99"   add the '0x' prefix to the entry.\n"
 100"\n"
 101"o  For help, use the <TAB> or cursor keys to highlight the help option\n"
 102"   and press <ENTER>.  You can try <TAB><H> as well.\n"
 103"\n"
 104"\n"
 105"Text Box    (Help Window)\n"
 106"--------\n"
 107"o  Use the cursor keys to scroll up/down/left/right.  The VI editor\n"
 108"   keys h,j,k,l function here as do <u>, <d>, <SPACE BAR> and <B> for\n"
 109"   those who are familiar with less and lynx.\n"
 110"\n"
 111"o  Press <E>, <X>, <q>, <Enter> or <Esc><Esc> to exit.\n"
 112"\n"
 113"\n"
 114"Alternate Configuration Files\n"
 115"-----------------------------\n"
 116"Menuconfig supports the use of alternate configuration files for\n"
 117"those who, for various reasons, find it necessary to switch\n"
 118"between different configurations.\n"
 119"\n"
 120"The <Save> button will let you save the current configuration to\n"
 121"a file of your choosing.  Use the <Load> button to load a previously\n"
 122"saved alternate configuration.\n"
 123"\n"
 124"Even if you don't use alternate configuration files, but you find\n"
 125"during a Menuconfig session that you have completely messed up your\n"
 126"settings, you may use the <Load> button to restore your previously\n"
 127"saved settings from \".config\" without restarting Menuconfig.\n"
 128"\n"
 129"Other information\n"
 130"-----------------\n"
 131"If you use Menuconfig in an XTERM window, make sure you have your\n"
 132"$TERM variable set to point to an xterm definition which supports\n"
 133"color.  Otherwise, Menuconfig will look rather bad.  Menuconfig will\n"
 134"not display correctly in an RXVT window because rxvt displays only one\n"
 135"intensity of color, bright.\n"
 136"\n"
 137"Menuconfig will display larger menus on screens or xterms which are\n"
 138"set to display more than the standard 25 row by 80 column geometry.\n"
 139"In order for this to work, the \"stty size\" command must be able to\n"
 140"display the screen's current row and column geometry.  I STRONGLY\n"
 141"RECOMMEND that you make sure you do NOT have the shell variables\n"
 142"LINES and COLUMNS exported into your environment.  Some distributions\n"
 143"export those variables via /etc/profile.  Some ncurses programs can\n"
 144"become confused when those variables (LINES & COLUMNS) don't reflect\n"
 145"the true screen size.\n"
 146"\n"
 147"Optional personality available\n"
 148"------------------------------\n"
 149"If you prefer to have all of the options listed in a single menu,\n"
 150"rather than the default multimenu hierarchy, run the menuconfig with\n"
 151"MENUCONFIG_MODE environment variable set to single_menu. Example:\n"
 152"\n"
 153"make MENUCONFIG_MODE=single_menu menuconfig\n"
 154"\n"
 155"<Enter> will then unroll the appropriate category, or enfold it if it\n"
 156"is already unrolled.\n"
 157"\n"
 158"Note that this mode can eventually be a little more CPU expensive\n"
 159"(especially with a larger number of unrolled categories) than the\n"
 160"default mode.\n"
 161"\n"
 162"Different color themes available\n"
 163"--------------------------------\n"
 164"It is possible to select different color themes using the variable\n"
 165"MENUCONFIG_COLOR. To select a theme use:\n"
 166"\n"
 167"make MENUCONFIG_COLOR=<theme> menuconfig\n"
 168"\n"
 169"Available themes are\n"
 170" mono       => selects colors suitable for monochrome displays\n"
 171" blackbg    => selects a color scheme with black background\n"
 172" classic    => theme with blue background. The classic look\n"
 173" bluetitle  => an LCD friendly version of classic. (default)\n"
 174"\n"),
 175menu_instructions[] = N_(
 176        "Arrow keys navigate the menu.  "
 177        "<Enter> selects submenus ---> (or empty submenus ----).  "
 178        "Highlighted letters are hotkeys.  "
 179        "Pressing <Y> includes, <N> excludes, <M> modularizes features.  "
 180        "Press <Esc><Esc> to exit, <?> for Help, </> for Search.  "
 181        "Legend: [*] built-in  [ ] excluded  <M> module  < > module capable"),
 182radiolist_instructions[] = N_(
 183        "Use the arrow keys to navigate this window or "
 184        "press the hotkey of the item you wish to select "
 185        "followed by the <SPACE BAR>. "
 186        "Press <?> for additional information about this option."),
 187inputbox_instructions_int[] = N_(
 188        "Please enter a decimal value. "
 189        "Fractions will not be accepted.  "
 190        "Use the <TAB> key to move from the input field to the buttons below it."),
 191inputbox_instructions_hex[] = N_(
 192        "Please enter a hexadecimal value. "
 193        "Use the <TAB> key to move from the input field to the buttons below it."),
 194inputbox_instructions_string[] = N_(
 195        "Please enter a string value. "
 196        "Use the <TAB> key to move from the input field to the buttons below it."),
 197setmod_text[] = N_(
 198        "This feature depends on another which has been configured as a module.\n"
 199        "As a result, this feature will be built as a module."),
 200load_config_text[] = N_(
 201        "Enter the name of the configuration file you wish to load.  "
 202        "Accept the name shown to restore the configuration you "
 203        "last retrieved.  Leave blank to abort."),
 204load_config_help[] = N_(
 205        "\n"
 206        "For various reasons, one may wish to keep several different\n"
 207        "configurations available on a single machine.\n"
 208        "\n"
 209        "If you have saved a previous configuration in a file other than the\n"
 210        "default one, entering its name here will allow you to modify that\n"
 211        "configuration.\n"
 212        "\n"
 213        "If you are uncertain, then you have probably never used alternate\n"
 214        "configuration files. You should therefore leave this blank to abort.\n"),
 215save_config_text[] = N_(
 216        "Enter a filename to which this configuration should be saved "
 217        "as an alternate.  Leave blank to abort."),
 218save_config_help[] = N_(
 219        "\n"
 220        "For various reasons, one may wish to keep different configurations\n"
 221        "available on a single machine.\n"
 222        "\n"
 223        "Entering a file name here will allow you to later retrieve, modify\n"
 224        "and use the current configuration as an alternate to whatever\n"
 225        "configuration options you have selected at that time.\n"
 226        "\n"
 227        "If you are uncertain what all this means then you should probably\n"
 228        "leave this blank.\n"),
 229search_help[] = N_(
 230        "\n"
 231        "Search for symbols and display their relations.\n"
 232        "Regular expressions are allowed.\n"
 233        "Example: search for \"^FOO\"\n"
 234        "Result:\n"
 235        "-----------------------------------------------------------------\n"
 236        "Symbol: FOO [=m]\n"
 237        "Type  : tristate\n"
 238        "Prompt: Foo bus is used to drive the bar HW\n"
 239        "  Location:\n"
 240        "    -> Bus options (PCI, PCMCIA, EISA, ISA)\n"
 241        "      -> PCI support (PCI [=y])\n"
 242        "(1)     -> PCI access mode (<choice> [=y])\n"
 243        "  Defined at drivers/pci/Kconfig:47\n"
 244        "  Depends on: X86_LOCAL_APIC && X86_IO_APIC || IA64\n"
 245        "  Selects: LIBCRC32\n"
 246        "  Selected by: BAR [=n]\n"
 247        "-----------------------------------------------------------------\n"
 248        "o The line 'Type:' shows the type of the configuration option for\n"
 249        "  this symbol (boolean, tristate, string, ...)\n"
 250        "o The line 'Prompt:' shows the text used in the menu structure for\n"
 251        "  this symbol\n"
 252        "o The 'Defined at' line tells at what file / line number the symbol\n"
 253        "  is defined\n"
 254        "o The 'Depends on:' line tells what symbols need to be defined for\n"
 255        "  this symbol to be visible in the menu (selectable)\n"
 256        "o The 'Location:' lines tells where in the menu structure this symbol\n"
 257        "  is located\n"
 258        "    A location followed by a [=y] indicates that this is a\n"
 259        "    selectable menu item - and the current value is displayed inside\n"
 260        "    brackets.\n"
 261        "    Press the key in the (#) prefix to jump directly to that\n"
 262        "    location. You will be returned to the current search results\n"
 263        "    after exiting this new menu.\n"
 264        "o The 'Selects:' line tells what symbols will be automatically\n"
 265        "  selected if this symbol is selected (y or m)\n"
 266        "o The 'Selected by' line tells what symbol has selected this symbol\n"
 267        "\n"
 268        "Only relevant lines are shown.\n"
 269        "\n\n"
 270        "Search examples:\n"
 271        "Examples: USB  => find all symbols containing USB\n"
 272        "          ^USB => find all symbols starting with USB\n"
 273        "          USB$ => find all symbols ending with USB\n"
 274        "\n");
 275
 276static int indent;
 277static struct menu *current_menu;
 278static int child_count;
 279static int single_menu_mode;
 280static int show_all_options;
 281static int save_and_exit;
 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(sizeof(*pos), 1);
 334                                pos = pos->next;
 335                        } else {
 336                                subtitles = pos = xcalloc(sizeof(*pos), 1);
 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                printf("%s", buf);
 782        else
 783                show_textbox(NULL, buf, 6, 60);
 784}
 785
 786static void show_help(struct menu *menu)
 787{
 788        struct gstr help = str_new();
 789
 790        help.max_width = getmaxx(stdscr) - 10;
 791        menu_get_ext_help(menu, &help);
 792
 793        show_helptext(_(menu_get_prompt(menu)), str_get(&help));
 794        str_free(&help);
 795}
 796
 797static void conf_choice(struct menu *menu)
 798{
 799        const char *prompt = _(menu_get_prompt(menu));
 800        struct menu *child;
 801        struct symbol *active;
 802
 803        active = sym_get_choice_value(menu->sym);
 804        while (1) {
 805                int res;
 806                int selected;
 807                item_reset();
 808
 809                current_menu = menu;
 810                for (child = menu->list; child; child = child->next) {
 811                        if (!menu_is_visible(child))
 812                                continue;
 813                        if (child->sym)
 814                                item_make("%s", _(menu_get_prompt(child)));
 815                        else {
 816                                item_make("*** %s ***", _(menu_get_prompt(child)));
 817                                item_set_tag(':');
 818                        }
 819                        item_set_data(child);
 820                        if (child->sym == active)
 821                                item_set_selected(1);
 822                        if (child->sym == sym_get_choice_value(menu->sym))
 823                                item_set_tag('X');
 824                }
 825                dialog_clear();
 826                res = dialog_checklist(prompt ? _(prompt) : _("Main Menu"),
 827                                        _(radiolist_instructions),
 828                                        MENUBOX_HEIGTH_MIN,
 829                                        MENUBOX_WIDTH_MIN,
 830                                        CHECKLIST_HEIGTH_MIN);
 831                selected = item_activate_selected();
 832                switch (res) {
 833                case 0:
 834                        if (selected) {
 835                                child = item_data();
 836                                if (!child->sym)
 837                                        break;
 838
 839                                sym_set_tristate_value(child->sym, yes);
 840                        }
 841                        return;
 842                case 1:
 843                        if (selected) {
 844                                child = item_data();
 845                                show_help(child);
 846                                active = child->sym;
 847                        } else
 848                                show_help(menu);
 849                        break;
 850                case KEY_ESC:
 851                        return;
 852                case -ERRDISPLAYTOOSMALL:
 853                        return;
 854                }
 855        }
 856}
 857
 858static void conf_string(struct menu *menu)
 859{
 860        const char *prompt = menu_get_prompt(menu);
 861
 862        while (1) {
 863                int res;
 864                const char *heading;
 865
 866                switch (sym_get_type(menu->sym)) {
 867                case S_INT:
 868                        heading = _(inputbox_instructions_int);
 869                        break;
 870                case S_HEX:
 871                        heading = _(inputbox_instructions_hex);
 872                        break;
 873                case S_STRING:
 874                        heading = _(inputbox_instructions_string);
 875                        break;
 876                default:
 877                        heading = _("Internal mconf error!");
 878                }
 879                dialog_clear();
 880                res = dialog_inputbox(prompt ? _(prompt) : _("Main Menu"),
 881                                      heading, 10, 75,
 882                                      sym_get_string_value(menu->sym));
 883                switch (res) {
 884                case 0:
 885                        if (sym_set_string_value(menu->sym, dialog_input_result))
 886                                return;
 887                        show_textbox(NULL, _("You have made an invalid entry."), 5, 43);
 888                        break;
 889                case 1:
 890                        show_help(menu);
 891                        break;
 892                case KEY_ESC:
 893                        return;
 894                }
 895        }
 896}
 897
 898static void conf_load(void)
 899{
 900
 901        while (1) {
 902                int res;
 903                dialog_clear();
 904                res = dialog_inputbox(NULL, load_config_text,
 905                                      11, 55, filename);
 906                switch(res) {
 907                case 0:
 908                        if (!dialog_input_result[0])
 909                                return;
 910                        if (!conf_read(dialog_input_result)) {
 911                                set_config_filename(dialog_input_result);
 912                                sym_set_change_count(1);
 913                                return;
 914                        }
 915                        show_textbox(NULL, _("File does not exist!"), 5, 38);
 916                        break;
 917                case 1:
 918                        show_helptext(_("Load Alternate Configuration"), load_config_help);
 919                        break;
 920                case KEY_ESC:
 921                        return;
 922                }
 923        }
 924}
 925
 926static void conf_save(void)
 927{
 928        while (1) {
 929                int res;
 930                dialog_clear();
 931                res = dialog_inputbox(NULL, save_config_text,
 932                                      11, 55, filename);
 933                switch(res) {
 934                case 0:
 935                        if (!dialog_input_result[0])
 936                                return;
 937                        if (!conf_write(dialog_input_result)) {
 938                                set_config_filename(dialog_input_result);
 939                                return;
 940                        }
 941                        show_textbox(NULL, _("Can't create file!  Probably a nonexistent directory."), 5, 60);
 942                        break;
 943                case 1:
 944                        show_helptext(_("Save Alternate Configuration"), save_config_help);
 945                        break;
 946                case KEY_ESC:
 947                        return;
 948                }
 949        }
 950}
 951
 952static int handle_exit(void)
 953{
 954        int res;
 955
 956        save_and_exit = 1;
 957        reset_subtitle();
 958        dialog_clear();
 959        if (conf_get_changed())
 960                res = dialog_yesno(NULL,
 961                                   _("Do you wish to save your new configuration?\n"
 962                                     "(Press <ESC><ESC> to continue kernel configuration.)"),
 963                                   6, 60);
 964        else
 965                res = -1;
 966
 967        end_dialog(saved_x, saved_y);
 968
 969        switch (res) {
 970        case 0:
 971                if (conf_write(filename)) {
 972                        fprintf(stderr, _("\n\n"
 973                                          "Error while writing of the configuration.\n"
 974                                          "Your configuration changes were NOT saved."
 975                                          "\n\n"));
 976                        return 1;
 977                }
 978                /* fall through */
 979        case -1:
 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                fprintf(stderr, _("\n\n"
 988                                  "Your configuration changes were NOT saved."
 989                                  "\n\n"));
 990                if (res != KEY_ESC)
 991                        res = 0;
 992        }
 993
 994        return res;
 995}
 996
 997static void sig_handler(int signo)
 998{
 999        exit(handle_exit());
1000}
1001
1002int main(int ac, char **av)
1003{
1004        char *mode;
1005        int res;
1006
1007        setlocale(LC_ALL, "");
1008        bindtextdomain(PACKAGE, LOCALEDIR);
1009        textdomain(PACKAGE);
1010
1011        signal(SIGINT, sig_handler);
1012
1013        conf_parse(av[1]);
1014        conf_read(NULL);
1015
1016        mode = getenv("MENUCONFIG_MODE");
1017        if (mode) {
1018                if (!strcasecmp(mode, "single_menu"))
1019                        single_menu_mode = 1;
1020        }
1021
1022        if (init_dialog(NULL)) {
1023                fprintf(stderr, N_("Your display is too small to run Menuconfig!\n"));
1024                fprintf(stderr, N_("It must be at least 19 lines by 80 columns.\n"));
1025                return 1;
1026        }
1027
1028        set_config_filename(conf_get_configname());
1029        conf_set_message_callback(conf_message_callback);
1030        do {
1031                conf(&rootmenu, NULL);
1032                res = handle_exit();
1033        } while (res == KEY_ESC);
1034
1035        return res;
1036}
1037