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 let 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 removed it.  You may also press the <Space Bar> to cycle\n"
  43"through the available options (ie. 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\n"
  50"   you wish to change or submenu 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> and <Help>.\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"At the end of the main menu you will find two options.  One is\n"
 121"for saving the current configuration to a file of your choosing.\n"
 122"The other option is for loading a previously saved alternate\n"
 123"configuration.\n"
 124"\n"
 125"Even if you don't use alternate configuration files, but you\n"
 126"find during a Menuconfig session that you have completely messed\n"
 127"up your settings, you may use the \"Load Alternate...\" option to\n"
 128"restore your previously saved settings from \".config\" without\n"
 129"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 a xterm definition which supports color.\n"
 135"Otherwise, Menuconfig will look rather bad.  Menuconfig will not\n"
 136"display correctly in a 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, rather\n"
 152"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  => a LCD friendly version of classic. (default)\n"
 176"\n"),
 177menu_instructions[] = N_(
 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[] = N_(
 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[] = N_(
 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[] = N_(
 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[] = N_(
 197        "Please enter a string value. "
 198        "Use the <TAB> key to move from the input field to the buttons below it."),
 199setmod_text[] = N_(
 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[] = N_(
 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[] = N_(
 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[] = N_(
 218        "Enter a filename to which this configuration should be saved "
 219        "as an alternate.  Leave blank to abort."),
 220save_config_help[] = N_(
 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[] = N_(
 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        "  Defined at drivers/pci/Kconfig:47\n"
 242        "  Depends on: X86_LOCAL_APIC && X86_IO_APIC || IA64\n"
 243        "  Location:\n"
 244        "    -> Bus options (PCI, PCMCIA, EISA, ISA)\n"
 245        "      -> PCI support (PCI [=y])\n"
 246        "(1)     -> PCI access mode (<choice> [=y])\n"
 247        "  Selects: LIBCRC32\n"
 248        "  Selected by: BAR\n"
 249        "-----------------------------------------------------------------\n"
 250        "o The line 'Type:' shows the type of the configuration option for\n"
 251        "  this symbol (boolean, 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 tell at what file / line number the symbol\n"
 255        "  is defined\n"
 256        "o The 'Depends on:' line tell what symbols needs to be defined for\n"
 257        "  this symbol to be visible in the menu (selectable)\n"
 258        "o The 'Location:' lines tell 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 tell what symbol will be automatically\n"
 267        "  selected if this symbol is selected (y or m)\n"
 268        "o The 'Selected by' line tell 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;
 284
 285static void conf(struct menu *menu, struct menu *active_menu);
 286static void conf_choice(struct menu *menu);
 287static void conf_string(struct menu *menu);
 288static void conf_load(void);
 289static void conf_save(void);
 290static int show_textbox_ext(const char *title, char *text, int r, int c,
 291                            int *keys, int *vscroll, int *hscroll,
 292                            update_text_fn update_text, void *data);
 293static void show_textbox(const char *title, const char *text, int r, int c);
 294static void show_helptext(const char *title, const char *text);
 295static void show_help(struct menu *menu);
 296
 297static char filename[PATH_MAX+1];
 298static void set_config_filename(const char *config_filename)
 299{
 300        static char menu_backtitle[PATH_MAX+128];
 301        int size;
 302
 303        size = snprintf(menu_backtitle, sizeof(menu_backtitle),
 304                        "%s - %s", config_filename, rootmenu.prompt->text);
 305        if (size >= sizeof(menu_backtitle))
 306                menu_backtitle[sizeof(menu_backtitle)-1] = '\0';
 307        set_dialog_backtitle(menu_backtitle);
 308
 309        size = snprintf(filename, sizeof(filename), "%s", config_filename);
 310        if (size >= sizeof(filename))
 311                filename[sizeof(filename)-1] = '\0';
 312}
 313
 314struct subtitle_part {
 315        struct list_head entries;
 316        const char *text;
 317};
 318static LIST_HEAD(trail);
 319
 320static struct subtitle_list *subtitles;
 321static void set_subtitle(void)
 322{
 323        struct subtitle_part *sp;
 324        struct subtitle_list *pos, *tmp;
 325
 326        for (pos = subtitles; pos != NULL; pos = tmp) {
 327                tmp = pos->next;
 328                free(pos);
 329        }
 330
 331        subtitles = NULL;
 332        list_for_each_entry(sp, &trail, entries) {
 333                if (sp->text) {
 334                        if (pos) {
 335                                pos->next = xcalloc(sizeof(*pos), 1);
 336                                pos = pos->next;
 337                        } else {
 338                                subtitles = pos = xcalloc(sizeof(*pos), 1);
 339                        }
 340                        pos->text = sp->text;
 341                }
 342        }
 343
 344        set_dialog_subtitles(subtitles);
 345}
 346
 347static void reset_subtitle(void)
 348{
 349        struct subtitle_list *pos, *tmp;
 350
 351        for (pos = subtitles; pos != NULL; pos = tmp) {
 352                tmp = pos->next;
 353                free(pos);
 354        }
 355        subtitles = NULL;
 356        set_dialog_subtitles(subtitles);
 357}
 358
 359struct search_data {
 360        struct list_head *head;
 361        struct menu **targets;
 362        int *keys;
 363};
 364
 365static void update_text(char *buf, size_t start, size_t end, void *_data)
 366{
 367        struct search_data *data = _data;
 368        struct jump_key *pos;
 369        int k = 0;
 370
 371        list_for_each_entry(pos, data->head, entries) {
 372                if (pos->offset >= start && pos->offset < end) {
 373                        char header[4];
 374
 375                        if (k < JUMP_NB) {
 376                                int key = '0' + (pos->index % JUMP_NB) + 1;
 377
 378                                sprintf(header, "(%c)", key);
 379                                data->keys[k] = key;
 380                                data->targets[k] = pos->target;
 381                                k++;
 382                        } else {
 383                                sprintf(header, "   ");
 384                        }
 385
 386                        memcpy(buf + pos->offset, header, sizeof(header) - 1);
 387                }
 388        }
 389        data->keys[k] = 0;
 390}
 391
 392static void search_conf(void)
 393{
 394        struct symbol **sym_arr;
 395        struct gstr res;
 396        struct gstr title;
 397        char *dialog_input;
 398        int dres, vscroll = 0, hscroll = 0;
 399        bool again;
 400        struct gstr sttext;
 401        struct subtitle_part stpart;
 402
 403        title = str_new();
 404        str_printf( &title, _("Enter (sub)string or regexp to search for "
 405                              "(with or without \"%s\")"), CONFIG_);
 406
 407again:
 408        dialog_clear();
 409        dres = dialog_inputbox(_("Search Configuration Parameter"),
 410                              str_get(&title),
 411                              10, 75, "");
 412        switch (dres) {
 413        case 0:
 414                break;
 415        case 1:
 416                show_helptext(_("Search Configuration"), search_help);
 417                goto again;
 418        default:
 419                str_free(&title);
 420                return;
 421        }
 422
 423        /* strip the prefix if necessary */
 424        dialog_input = dialog_input_result;
 425        if (strncasecmp(dialog_input_result, CONFIG_, strlen(CONFIG_)) == 0)
 426                dialog_input += strlen(CONFIG_);
 427
 428        sttext = str_new();
 429        str_printf(&sttext, "Search (%s)", dialog_input_result);
 430        stpart.text = str_get(&sttext);
 431        list_add_tail(&stpart.entries, &trail);
 432
 433        sym_arr = sym_re_search(dialog_input);
 434        do {
 435                LIST_HEAD(head);
 436                struct menu *targets[JUMP_NB];
 437                int keys[JUMP_NB + 1], i;
 438                struct search_data data = {
 439                        .head = &head,
 440                        .targets = targets,
 441                        .keys = keys,
 442                };
 443                struct jump_key *pos, *tmp;
 444
 445                res = get_relations_str(sym_arr, &head);
 446                set_subtitle();
 447                dres = show_textbox_ext(_("Search Results"), (char *)
 448                                        str_get(&res), 0, 0, keys, &vscroll,
 449                                        &hscroll, &update_text, (void *)
 450                                        &data);
 451                again = false;
 452                for (i = 0; i < JUMP_NB && keys[i]; i++)
 453                        if (dres == keys[i]) {
 454                                conf(targets[i]->parent, targets[i]);
 455                                again = true;
 456                        }
 457                str_free(&res);
 458                list_for_each_entry_safe(pos, tmp, &head, entries)
 459                        free(pos);
 460        } while (again);
 461        free(sym_arr);
 462        str_free(&title);
 463        list_del(trail.prev);
 464        str_free(&sttext);
 465}
 466
 467static void build_conf(struct menu *menu)
 468{
 469        struct symbol *sym;
 470        struct property *prop;
 471        struct menu *child;
 472        int type, tmp, doint = 2;
 473        tristate val;
 474        char ch;
 475        bool visible;
 476
 477        /*
 478         * note: menu_is_visible() has side effect that it will
 479         * recalc the value of the symbol.
 480         */
 481        visible = menu_is_visible(menu);
 482        if (show_all_options && !menu_has_prompt(menu))
 483                return;
 484        else if (!show_all_options && !visible)
 485                return;
 486
 487        sym = menu->sym;
 488        prop = menu->prompt;
 489        if (!sym) {
 490                if (prop && menu != current_menu) {
 491                        const char *prompt = menu_get_prompt(menu);
 492                        switch (prop->type) {
 493                        case P_MENU:
 494                                child_count++;
 495                                prompt = _(prompt);
 496                                if (single_menu_mode) {
 497                                        item_make("%s%*c%s",
 498                                                  menu->data ? "-->" : "++>",
 499                                                  indent + 1, ' ', prompt);
 500                                } else
 501                                        item_make("   %*c%s  %s",
 502                                                  indent + 1, ' ', prompt,
 503                                                  menu_is_empty(menu) ? "----" : "--->");
 504                                item_set_tag('m');
 505                                item_set_data(menu);
 506                                if (single_menu_mode && menu->data)
 507                                        goto conf_childs;
 508                                return;
 509                        case P_COMMENT:
 510                                if (prompt) {
 511                                        child_count++;
 512                                        item_make("   %*c*** %s ***", indent + 1, ' ', _(prompt));
 513                                        item_set_tag(':');
 514                                        item_set_data(menu);
 515                                }
 516                                break;
 517                        default:
 518                                if (prompt) {
 519                                        child_count++;
 520                                        item_make("---%*c%s", indent + 1, ' ', _(prompt));
 521                                        item_set_tag(':');
 522                                        item_set_data(menu);
 523                                }
 524                        }
 525                } else
 526                        doint = 0;
 527                goto conf_childs;
 528        }
 529
 530        type = sym_get_type(sym);
 531        if (sym_is_choice(sym)) {
 532                struct symbol *def_sym = sym_get_choice_value(sym);
 533                struct menu *def_menu = NULL;
 534
 535                child_count++;
 536                for (child = menu->list; child; child = child->next) {
 537                        if (menu_is_visible(child) && child->sym == def_sym)
 538                                def_menu = child;
 539                }
 540
 541                val = sym_get_tristate_value(sym);
 542                if (sym_is_changable(sym)) {
 543                        switch (type) {
 544                        case S_BOOLEAN:
 545                                item_make("[%c]", val == no ? ' ' : '*');
 546                                break;
 547                        case S_TRISTATE:
 548                                switch (val) {
 549                                case yes: ch = '*'; break;
 550                                case mod: ch = 'M'; break;
 551                                default:  ch = ' '; break;
 552                                }
 553                                item_make("<%c>", ch);
 554                                break;
 555                        }
 556                        item_set_tag('t');
 557                        item_set_data(menu);
 558                } else {
 559                        item_make("   ");
 560                        item_set_tag(def_menu ? 't' : ':');
 561                        item_set_data(menu);
 562                }
 563
 564                item_add_str("%*c%s", indent + 1, ' ', _(menu_get_prompt(menu)));
 565                if (val == yes) {
 566                        if (def_menu) {
 567                                item_add_str(" (%s)", _(menu_get_prompt(def_menu)));
 568                                item_add_str("  --->");
 569                                if (def_menu->list) {
 570                                        indent += 2;
 571                                        build_conf(def_menu);
 572                                        indent -= 2;
 573                                }
 574                        }
 575                        return;
 576                }
 577        } else {
 578                if (menu == current_menu) {
 579                        item_make("---%*c%s", indent + 1, ' ', _(menu_get_prompt(menu)));
 580                        item_set_tag(':');
 581                        item_set_data(menu);
 582                        goto conf_childs;
 583                }
 584                child_count++;
 585                val = sym_get_tristate_value(sym);
 586                if (sym_is_choice_value(sym) && val == yes) {
 587                        item_make("   ");
 588                        item_set_tag(':');
 589                        item_set_data(menu);
 590                } else {
 591                        switch (type) {
 592                        case S_BOOLEAN:
 593                                if (sym_is_changable(sym))
 594                                        item_make("[%c]", val == no ? ' ' : '*');
 595                                else
 596                                        item_make("-%c-", val == no ? ' ' : '*');
 597                                item_set_tag('t');
 598                                item_set_data(menu);
 599                                break;
 600                        case S_TRISTATE:
 601                                switch (val) {
 602                                case yes: ch = '*'; break;
 603                                case mod: ch = 'M'; break;
 604                                default:  ch = ' '; break;
 605                                }
 606                                if (sym_is_changable(sym)) {
 607                                        if (sym->rev_dep.tri == mod)
 608                                                item_make("{%c}", ch);
 609                                        else
 610                                                item_make("<%c>", ch);
 611                                } else
 612                                        item_make("-%c-", ch);
 613                                item_set_tag('t');
 614                                item_set_data(menu);
 615                                break;
 616                        default:
 617                                tmp = 2 + strlen(sym_get_string_value(sym)); /* () = 2 */
 618                                item_make("(%s)", sym_get_string_value(sym));
 619                                tmp = indent - tmp + 4;
 620                                if (tmp < 0)
 621                                        tmp = 0;
 622                                item_add_str("%*c%s%s", tmp, ' ', _(menu_get_prompt(menu)),
 623                                             (sym_has_value(sym) || !sym_is_changable(sym)) ?
 624                                             "" : _(" (NEW)"));
 625                                item_set_tag('s');
 626                                item_set_data(menu);
 627                                goto conf_childs;
 628                        }
 629                }
 630                item_add_str("%*c%s%s", indent + 1, ' ', _(menu_get_prompt(menu)),
 631                          (sym_has_value(sym) || !sym_is_changable(sym)) ?
 632                          "" : _(" (NEW)"));
 633                if (menu->prompt->type == P_MENU) {
 634                        item_add_str("  %s", menu_is_empty(menu) ? "----" : "--->");
 635                        return;
 636                }
 637        }
 638
 639conf_childs:
 640        indent += doint;
 641        for (child = menu->list; child; child = child->next)
 642                build_conf(child);
 643        indent -= doint;
 644}
 645
 646static void conf(struct menu *menu, struct menu *active_menu)
 647{
 648        struct menu *submenu;
 649        const char *prompt = menu_get_prompt(menu);
 650        struct subtitle_part stpart;
 651        struct symbol *sym;
 652        int res;
 653        int s_scroll = 0;
 654
 655        if (menu != &rootmenu)
 656                stpart.text = menu_get_prompt(menu);
 657        else
 658                stpart.text = NULL;
 659        list_add_tail(&stpart.entries, &trail);
 660
 661        while (1) {
 662                item_reset();
 663                current_menu = menu;
 664                build_conf(menu);
 665                if (!child_count)
 666                        break;
 667                set_subtitle();
 668                dialog_clear();
 669                res = dialog_menu(prompt ? _(prompt) : _("Main Menu"),
 670                                  _(menu_instructions),
 671                                  active_menu, &s_scroll);
 672                if (res == 1 || res == KEY_ESC || res == -ERRDISPLAYTOOSMALL)
 673                        break;
 674                if (item_count() != 0) {
 675                        if (!item_activate_selected())
 676                                continue;
 677                        if (!item_tag())
 678                                continue;
 679                }
 680                submenu = item_data();
 681                active_menu = item_data();
 682                if (submenu)
 683                        sym = submenu->sym;
 684                else
 685                        sym = NULL;
 686
 687                switch (res) {
 688                case 0:
 689                        switch (item_tag()) {
 690                        case 'm':
 691                                if (single_menu_mode)
 692                                        submenu->data = (void *) (long) !submenu->data;
 693                                else
 694                                        conf(submenu, NULL);
 695                                break;
 696                        case 't':
 697                                if (sym_is_choice(sym) && sym_get_tristate_value(sym) == yes)
 698                                        conf_choice(submenu);
 699                                else if (submenu->prompt->type == P_MENU)
 700                                        conf(submenu, NULL);
 701                                break;
 702                        case 's':
 703                                conf_string(submenu);
 704                                break;
 705                        }
 706                        break;
 707                case 2:
 708                        if (sym)
 709                                show_help(submenu);
 710                        else {
 711                                reset_subtitle();
 712                                show_helptext(_("README"), _(mconf_readme));
 713                        }
 714                        break;
 715                case 3:
 716                        reset_subtitle();
 717                        conf_save();
 718                        break;
 719                case 4:
 720                        reset_subtitle();
 721                        conf_load();
 722                        break;
 723                case 5:
 724                        if (item_is_tag('t')) {
 725                                if (sym_set_tristate_value(sym, yes))
 726                                        break;
 727                                if (sym_set_tristate_value(sym, mod))
 728                                        show_textbox(NULL, setmod_text, 6, 74);
 729                        }
 730                        break;
 731                case 6:
 732                        if (item_is_tag('t'))
 733                                sym_set_tristate_value(sym, no);
 734                        break;
 735                case 7:
 736                        if (item_is_tag('t'))
 737                                sym_set_tristate_value(sym, mod);
 738                        break;
 739                case 8:
 740                        if (item_is_tag('t'))
 741                                sym_toggle_tristate_value(sym);
 742                        else if (item_is_tag('m'))
 743                                conf(submenu, NULL);
 744                        break;
 745                case 9:
 746                        search_conf();
 747                        break;
 748                case 10:
 749                        show_all_options = !show_all_options;
 750                        break;
 751                }
 752        }
 753
 754        list_del(trail.prev);
 755}
 756
 757static int show_textbox_ext(const char *title, char *text, int r, int c, int
 758                            *keys, int *vscroll, int *hscroll, update_text_fn
 759                            update_text, void *data)
 760{
 761        dialog_clear();
 762        return dialog_textbox(title, text, r, c, keys, vscroll, hscroll,
 763                              update_text, data);
 764}
 765
 766static void show_textbox(const char *title, const char *text, int r, int c)
 767{
 768        show_textbox_ext(title, (char *) text, r, c, (int []) {0}, NULL, NULL,
 769                         NULL, NULL);
 770}
 771
 772static void show_helptext(const char *title, const char *text)
 773{
 774        show_textbox(title, text, 0, 0);
 775}
 776
 777static void conf_message_callback(const char *fmt, va_list ap)
 778{
 779        char buf[PATH_MAX+1];
 780
 781        vsnprintf(buf, sizeof(buf), fmt, ap);
 782        if (save_and_exit)
 783                printf("%s", buf);
 784        else
 785                show_textbox(NULL, buf, 6, 60);
 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                printf(_("\n\n"
 983                         "*** End of the configuration.\n"
 984                         "*** Execute 'make' to start the build or try 'make help'."
 985                         "\n\n"));
 986                res = 0;
 987                break;
 988        default:
 989                fprintf(stderr, _("\n\n"
 990                                  "Your configuration changes were NOT saved."
 991                                  "\n\n"));
 992                if (res != KEY_ESC)
 993                        res = 0;
 994        }
 995
 996        return res;
 997}
 998
 999static void sig_handler(int signo)
1000{
1001        exit(handle_exit());
1002}
1003
1004int main(int ac, char **av)
1005{
1006        char *mode;
1007        int res;
1008
1009        setlocale(LC_ALL, "");
1010        bindtextdomain(PACKAGE, LOCALEDIR);
1011        textdomain(PACKAGE);
1012
1013        signal(SIGINT, sig_handler);
1014
1015        conf_parse(av[1]);
1016        conf_read(NULL);
1017
1018        mode = getenv("MENUCONFIG_MODE");
1019        if (mode) {
1020                if (!strcasecmp(mode, "single_menu"))
1021                        single_menu_mode = 1;
1022        }
1023
1024        if (init_dialog(NULL)) {
1025                fprintf(stderr, N_("Your display is too small to run Menuconfig!\n"));
1026                fprintf(stderr, N_("It must be at least 19 lines by 80 columns.\n"));
1027                return 1;
1028        }
1029
1030        set_config_filename(conf_get_configname());
1031        conf_set_message_callback(conf_message_callback);
1032        do {
1033                conf(&rootmenu, NULL);
1034                res = handle_exit();
1035        } while (res == KEY_ESC);
1036
1037        return res;
1038}
1039
1040