root/scripts/kconfig/nconf.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. print_function_line
  2. handle_f1
  3. handle_f2
  4. handle_f3
  5. handle_f4
  6. handle_f5
  7. handle_f6
  8. handle_f7
  9. handle_f8
  10. handle_f9
  11. process_special_keys
  12. clean_items
  13. get_mext_match
  14. item_make
  15. item_add_str
  16. item_tag
  17. curses_item_index
  18. item_data
  19. item_is_tag
  20. set_config_filename
  21. do_exit
  22. search_conf
  23. build_conf
  24. reset_menu
  25. center_item
  26. show_menu
  27. adj_match_dir
  28. do_match
  29. conf
  30. conf_message_callback
  31. show_help
  32. conf_choice
  33. conf_string
  34. conf_load
  35. conf_save
  36. setup_windows
  37. main

   1 // SPDX-License-Identifier: GPL-2.0
   2 /*
   3  * Copyright (C) 2008 Nir Tzachar <nir.tzachar@gmail.com>
   4  *
   5  * Derived from menuconfig.
   6  */
   7 #ifndef _GNU_SOURCE
   8 #define _GNU_SOURCE
   9 #endif
  10 #include <string.h>
  11 #include <stdlib.h>
  12 
  13 #include "lkc.h"
  14 #include "nconf.h"
  15 #include <ctype.h>
  16 
  17 static const char nconf_global_help[] =
  18 "Help windows\n"
  19 "------------\n"
  20 "o  Global help:  Unless in a data entry window, pressing <F1> will give \n"
  21 "   you the global help window, which you are just reading.\n"
  22 "\n"
  23 "o  A short version of the global help is available by pressing <F3>.\n"
  24 "\n"
  25 "o  Local help:  To get help related to the current menu entry, use any\n"
  26 "   of <?> <h>, or if in a data entry window then press <F1>.\n"
  27 "\n"
  28 "\n"
  29 "Menu entries\n"
  30 "------------\n"
  31 "This interface lets you select features and parameters for the kernel\n"
  32 "build.  Kernel features can either be built-in, modularized, or removed.\n"
  33 "Parameters must be entered as text or decimal or hexadecimal numbers.\n"
  34 "\n"
  35 "Menu entries beginning with following braces represent features that\n"
  36 "  [ ]  can be built in or removed\n"
  37 "  < >  can be built in, modularized or removed\n"
  38 "  { }  can be built in or modularized, are selected by another feature\n"
  39 "  - -  are selected by another feature\n"
  40 "  XXX  cannot be selected.  Symbol Info <F2> tells you why.\n"
  41 "*, M or whitespace inside braces means to build in, build as a module\n"
  42 "or to exclude the feature respectively.\n"
  43 "\n"
  44 "To change any of these features, highlight it with the movement keys\n"
  45 "listed below and press <y> to build it in, <m> to make it a module or\n"
  46 "<n> to remove it.  You may press the <Space> key to cycle through the\n"
  47 "available options.\n"
  48 "\n"
  49 "A trailing \"--->\" designates a submenu, a trailing \"----\" an\n"
  50 "empty submenu.\n"
  51 "\n"
  52 "Menu navigation keys\n"
  53 "----------------------------------------------------------------------\n"
  54 "Linewise up                 <Up>\n"
  55 "Linewise down               <Down>\n"
  56 "Pagewise up                 <Page Up>\n"
  57 "Pagewise down               <Page Down>\n"
  58 "First entry                 <Home>\n"
  59 "Last entry                  <End>\n"
  60 "Enter a submenu             <Right>  <Enter>\n"
  61 "Go back to parent menu      <Left>   <Esc>  <F5>\n"
  62 "Close a help window         <Enter>  <Esc>  <F5>\n"
  63 "Close entry window, apply   <Enter>\n"
  64 "Close entry window, forget  <Esc>  <F5>\n"
  65 "Start incremental, case-insensitive search for STRING in menu entries,\n"
  66 "    no regex support, STRING is displayed in upper left corner\n"
  67 "                            </>STRING\n"
  68 "    Remove last character   <Backspace>\n"
  69 "    Jump to next hit        <Down>\n"
  70 "    Jump to previous hit    <Up>\n"
  71 "Exit menu search mode       </>  <Esc>\n"
  72 "Search for configuration variables with or without leading CONFIG_\n"
  73 "                            <F8>RegExpr<Enter>\n"
  74 "Verbose search help         <F8><F1>\n"
  75 "----------------------------------------------------------------------\n"
  76 "\n"
  77 "Unless in a data entry window, key <1> may be used instead of <F1>,\n"
  78 "<2> instead of <F2>, etc.\n"
  79 "\n"
  80 "\n"
  81 "Radiolist (Choice list)\n"
  82 "-----------------------\n"
  83 "Use the movement keys listed above to select the option you wish to set\n"
  84 "and press <Space>.\n"
  85 "\n"
  86 "\n"
  87 "Data entry\n"
  88 "----------\n"
  89 "Enter the requested information and press <Enter>.  Hexadecimal values\n"
  90 "may be entered without the \"0x\" prefix.\n"
  91 "\n"
  92 "\n"
  93 "Text Box (Help Window)\n"
  94 "----------------------\n"
  95 "Use movement keys as listed in table above.\n"
  96 "\n"
  97 "Press any of <Enter> <Esc> <q> <F5> <F9> to exit.\n"
  98 "\n"
  99 "\n"
 100 "Alternate configuration files\n"
 101 "-----------------------------\n"
 102 "nconfig supports switching between different configurations.\n"
 103 "Press <F6> to save your current configuration.  Press <F7> and enter\n"
 104 "a file name to load a previously saved configuration.\n"
 105 "\n"
 106 "\n"
 107 "Terminal configuration\n"
 108 "----------------------\n"
 109 "If you use nconfig in a xterm window, make sure your TERM environment\n"
 110 "variable specifies a terminal configuration which supports at least\n"
 111 "16 colors.  Otherwise nconfig will look rather bad.\n"
 112 "\n"
 113 "If the \"stty size\" command reports the current terminalsize correctly,\n"
 114 "nconfig will adapt to sizes larger than the traditional 80x25 \"standard\"\n"
 115 "and display longer menus properly.\n"
 116 "\n"
 117 "\n"
 118 "Single menu mode\n"
 119 "----------------\n"
 120 "If you prefer to have all of the menu entries listed in a single menu,\n"
 121 "rather than the default multimenu hierarchy, run nconfig with\n"
 122 "NCONFIG_MODE environment variable set to single_menu.  Example:\n"
 123 "\n"
 124 "make NCONFIG_MODE=single_menu nconfig\n"
 125 "\n"
 126 "<Enter> will then unfold the appropriate category, or fold it if it\n"
 127 "is already unfolded.  Folded menu entries will be designated by a\n"
 128 "leading \"++>\" and unfolded entries by a leading \"-->\".\n"
 129 "\n"
 130 "Note that this mode can eventually be a little more CPU expensive than\n"
 131 "the default mode, especially with a larger number of unfolded submenus.\n"
 132 "\n",
 133 menu_no_f_instructions[] =
 134 "Legend:  [*] built-in  [ ] excluded  <M> module  < > module capable.\n"
 135 "Submenus are designated by a trailing \"--->\", empty ones by \"----\".\n"
 136 "\n"
 137 "Use the following keys to navigate the menus:\n"
 138 "Move up or down with <Up> and <Down>.\n"
 139 "Enter a submenu with <Enter> or <Right>.\n"
 140 "Exit a submenu to its parent menu with <Esc> or <Left>.\n"
 141 "Pressing <y> includes, <n> excludes, <m> modularizes features.\n"
 142 "Pressing <Space> cycles through the available options.\n"
 143 "To search for menu entries press </>.\n"
 144 "<Esc> always leaves the current window.\n"
 145 "\n"
 146 "You do not have function keys support.\n"
 147 "Press <1> instead of <F1>, <2> instead of <F2>, etc.\n"
 148 "For verbose global help use key <1>.\n"
 149 "For help related to the current menu entry press <?> or <h>.\n",
 150 menu_instructions[] =
 151 "Legend:  [*] built-in  [ ] excluded  <M> module  < > module capable.\n"
 152 "Submenus are designated by a trailing \"--->\", empty ones by \"----\".\n"
 153 "\n"
 154 "Use the following keys to navigate the menus:\n"
 155 "Move up or down with <Up> or <Down>.\n"
 156 "Enter a submenu with <Enter> or <Right>.\n"
 157 "Exit a submenu to its parent menu with <Esc> or <Left>.\n"
 158 "Pressing <y> includes, <n> excludes, <m> modularizes features.\n"
 159 "Pressing <Space> cycles through the available options.\n"
 160 "To search for menu entries press </>.\n"
 161 "<Esc> always leaves the current window.\n"
 162 "\n"
 163 "Pressing <1> may be used instead of <F1>, <2> instead of <F2>, etc.\n"
 164 "For verbose global help press <F1>.\n"
 165 "For help related to the current menu entry press <?> or <h>.\n",
 166 radiolist_instructions[] =
 167 "Press <Up>, <Down>, <Home> or <End> to navigate a radiolist, select\n"
 168 "with <Space>.\n"
 169 "For help related to the current entry press <?> or <h>.\n"
 170 "For global help press <F1>.\n",
 171 inputbox_instructions_int[] =
 172 "Please enter a decimal value.\n"
 173 "Fractions will not be accepted.\n"
 174 "Press <Enter> to apply, <Esc> to cancel.",
 175 inputbox_instructions_hex[] =
 176 "Please enter a hexadecimal value.\n"
 177 "Press <Enter> to apply, <Esc> to cancel.",
 178 inputbox_instructions_string[] =
 179 "Please enter a string value.\n"
 180 "Press <Enter> to apply, <Esc> to cancel.",
 181 setmod_text[] =
 182 "This feature depends on another feature which has been configured as a\n"
 183 "module.  As a result, the current feature will be built as a module too.",
 184 load_config_text[] =
 185 "Enter the name of the configuration file you wish to load.\n"
 186 "Accept the name shown to restore the configuration you last\n"
 187 "retrieved.  Leave empty to abort.",
 188 load_config_help[] =
 189 "For various reasons, one may wish to keep several different\n"
 190 "configurations available on a single machine.\n"
 191 "\n"
 192 "If you have saved a previous configuration in a file other than the\n"
 193 "default one, entering its name here will allow you to load and modify\n"
 194 "that configuration.\n"
 195 "\n"
 196 "Leave empty to abort.\n",
 197 save_config_text[] =
 198 "Enter a filename to which this configuration should be saved\n"
 199 "as an alternate.  Leave empty to abort.",
 200 save_config_help[] =
 201 "For various reasons, one may wish to keep several different\n"
 202 "configurations available on a single machine.\n"
 203 "\n"
 204 "Entering a file name here will allow you to later retrieve, modify\n"
 205 "and use the current configuration as an alternate to whatever\n"
 206 "configuration options you have selected at that time.\n"
 207 "\n"
 208 "Leave empty to abort.\n",
 209 search_help[] =
 210 "Search for symbols (configuration variable names CONFIG_*) and display\n"
 211 "their relations.  Regular expressions are supported.\n"
 212 "Example:  Search for \"^FOO\".\n"
 213 "Result:\n"
 214 "-----------------------------------------------------------------\n"
 215 "Symbol: FOO [ = m]\n"
 216 "Prompt: Foo bus is used to drive the bar HW\n"
 217 "Defined at drivers/pci/Kconfig:47\n"
 218 "Depends on: X86_LOCAL_APIC && X86_IO_APIC || IA64\n"
 219 "Location:\n"
 220 "  -> Bus options (PCI, PCMCIA, EISA, ISA)\n"
 221 "    -> PCI support (PCI [ = y])\n"
 222 "      -> PCI access mode (<choice> [ = y])\n"
 223 "Selects: LIBCRC32\n"
 224 "Selected by: BAR\n"
 225 "-----------------------------------------------------------------\n"
 226 "o  The line 'Prompt:' shows the text displayed for this symbol in\n"
 227 "   the menu hierarchy.\n"
 228 "o  The 'Defined at' line tells at what file / line number the symbol is\n"
 229 "   defined.\n"
 230 "o  The 'Depends on:' line lists symbols that need to be defined for\n"
 231 "   this symbol to be visible and selectable in the menu.\n"
 232 "o  The 'Location:' lines tell, where in the menu structure this symbol\n"
 233 "   is located.  A location followed by a [ = y] indicates that this is\n"
 234 "   a selectable menu item, and the current value is displayed inside\n"
 235 "   brackets.\n"
 236 "o  The 'Selects:' line tells, what symbol will be automatically selected\n"
 237 "   if this symbol is selected (y or m).\n"
 238 "o  The 'Selected by' line tells what symbol has selected this symbol.\n"
 239 "\n"
 240 "Only relevant lines are shown.\n"
 241 "\n\n"
 242 "Search examples:\n"
 243 "USB  => find all symbols containing USB\n"
 244 "^USB => find all symbols starting with USB\n"
 245 "USB$ => find all symbols ending with USB\n"
 246 "\n";
 247 
 248 struct mitem {
 249         char str[256];
 250         char tag;
 251         void *usrptr;
 252         int is_visible;
 253 };
 254 
 255 #define MAX_MENU_ITEMS 4096
 256 static int show_all_items;
 257 static int indent;
 258 static struct menu *current_menu;
 259 static int child_count;
 260 static int single_menu_mode;
 261 /* the window in which all information appears */
 262 static WINDOW *main_window;
 263 /* the largest size of the menu window */
 264 static int mwin_max_lines;
 265 static int mwin_max_cols;
 266 /* the window in which we show option buttons */
 267 static MENU *curses_menu;
 268 static ITEM *curses_menu_items[MAX_MENU_ITEMS];
 269 static struct mitem k_menu_items[MAX_MENU_ITEMS];
 270 static int items_num;
 271 static int global_exit;
 272 /* the currently selected button */
 273 static const char *current_instructions = menu_instructions;
 274 
 275 static char *dialog_input_result;
 276 static int dialog_input_result_len;
 277 
 278 static void conf(struct menu *menu);
 279 static void conf_choice(struct menu *menu);
 280 static void conf_string(struct menu *menu);
 281 static void conf_load(void);
 282 static void conf_save(void);
 283 static void show_help(struct menu *menu);
 284 static int do_exit(void);
 285 static void setup_windows(void);
 286 static void search_conf(void);
 287 
 288 typedef void (*function_key_handler_t)(int *key, struct menu *menu);
 289 static void handle_f1(int *key, struct menu *current_item);
 290 static void handle_f2(int *key, struct menu *current_item);
 291 static void handle_f3(int *key, struct menu *current_item);
 292 static void handle_f4(int *key, struct menu *current_item);
 293 static void handle_f5(int *key, struct menu *current_item);
 294 static void handle_f6(int *key, struct menu *current_item);
 295 static void handle_f7(int *key, struct menu *current_item);
 296 static void handle_f8(int *key, struct menu *current_item);
 297 static void handle_f9(int *key, struct menu *current_item);
 298 
 299 struct function_keys {
 300         const char *key_str;
 301         const char *func;
 302         function_key key;
 303         function_key_handler_t handler;
 304 };
 305 
 306 static const int function_keys_num = 9;
 307 static struct function_keys function_keys[] = {
 308         {
 309                 .key_str = "F1",
 310                 .func = "Help",
 311                 .key = F_HELP,
 312                 .handler = handle_f1,
 313         },
 314         {
 315                 .key_str = "F2",
 316                 .func = "SymInfo",
 317                 .key = F_SYMBOL,
 318                 .handler = handle_f2,
 319         },
 320         {
 321                 .key_str = "F3",
 322                 .func = "Help 2",
 323                 .key = F_INSTS,
 324                 .handler = handle_f3,
 325         },
 326         {
 327                 .key_str = "F4",
 328                 .func = "ShowAll",
 329                 .key = F_CONF,
 330                 .handler = handle_f4,
 331         },
 332         {
 333                 .key_str = "F5",
 334                 .func = "Back",
 335                 .key = F_BACK,
 336                 .handler = handle_f5,
 337         },
 338         {
 339                 .key_str = "F6",
 340                 .func = "Save",
 341                 .key = F_SAVE,
 342                 .handler = handle_f6,
 343         },
 344         {
 345                 .key_str = "F7",
 346                 .func = "Load",
 347                 .key = F_LOAD,
 348                 .handler = handle_f7,
 349         },
 350         {
 351                 .key_str = "F8",
 352                 .func = "SymSearch",
 353                 .key = F_SEARCH,
 354                 .handler = handle_f8,
 355         },
 356         {
 357                 .key_str = "F9",
 358                 .func = "Exit",
 359                 .key = F_EXIT,
 360                 .handler = handle_f9,
 361         },
 362 };
 363 
 364 static void print_function_line(void)
 365 {
 366         int i;
 367         int offset = 1;
 368         const int skip = 1;
 369         int lines = getmaxy(stdscr);
 370 
 371         for (i = 0; i < function_keys_num; i++) {
 372                 (void) wattrset(main_window, attributes[FUNCTION_HIGHLIGHT]);
 373                 mvwprintw(main_window, lines-3, offset,
 374                                 "%s",
 375                                 function_keys[i].key_str);
 376                 (void) wattrset(main_window, attributes[FUNCTION_TEXT]);
 377                 offset += strlen(function_keys[i].key_str);
 378                 mvwprintw(main_window, lines-3,
 379                                 offset, "%s",
 380                                 function_keys[i].func);
 381                 offset += strlen(function_keys[i].func) + skip;
 382         }
 383         (void) wattrset(main_window, attributes[NORMAL]);
 384 }
 385 
 386 /* help */
 387 static void handle_f1(int *key, struct menu *current_item)
 388 {
 389         show_scroll_win(main_window,
 390                         "Global help", nconf_global_help);
 391         return;
 392 }
 393 
 394 /* symbole help */
 395 static void handle_f2(int *key, struct menu *current_item)
 396 {
 397         show_help(current_item);
 398         return;
 399 }
 400 
 401 /* instructions */
 402 static void handle_f3(int *key, struct menu *current_item)
 403 {
 404         show_scroll_win(main_window,
 405                         "Short help",
 406                         current_instructions);
 407         return;
 408 }
 409 
 410 /* config */
 411 static void handle_f4(int *key, struct menu *current_item)
 412 {
 413         int res = btn_dialog(main_window,
 414                         "Show all symbols?",
 415                         2,
 416                         "   <Show All>   ",
 417                         "<Don't show all>");
 418         if (res == 0)
 419                 show_all_items = 1;
 420         else if (res == 1)
 421                 show_all_items = 0;
 422 
 423         return;
 424 }
 425 
 426 /* back */
 427 static void handle_f5(int *key, struct menu *current_item)
 428 {
 429         *key = KEY_LEFT;
 430         return;
 431 }
 432 
 433 /* save */
 434 static void handle_f6(int *key, struct menu *current_item)
 435 {
 436         conf_save();
 437         return;
 438 }
 439 
 440 /* load */
 441 static void handle_f7(int *key, struct menu *current_item)
 442 {
 443         conf_load();
 444         return;
 445 }
 446 
 447 /* search */
 448 static void handle_f8(int *key, struct menu *current_item)
 449 {
 450         search_conf();
 451         return;
 452 }
 453 
 454 /* exit */
 455 static void handle_f9(int *key, struct menu *current_item)
 456 {
 457         do_exit();
 458         return;
 459 }
 460 
 461 /* return != 0 to indicate the key was handles */
 462 static int process_special_keys(int *key, struct menu *menu)
 463 {
 464         int i;
 465 
 466         if (*key == KEY_RESIZE) {
 467                 setup_windows();
 468                 return 1;
 469         }
 470 
 471         for (i = 0; i < function_keys_num; i++) {
 472                 if (*key == KEY_F(function_keys[i].key) ||
 473                     *key == '0' + function_keys[i].key){
 474                         function_keys[i].handler(key, menu);
 475                         return 1;
 476                 }
 477         }
 478 
 479         return 0;
 480 }
 481 
 482 static void clean_items(void)
 483 {
 484         int i;
 485         for (i = 0; curses_menu_items[i]; i++)
 486                 free_item(curses_menu_items[i]);
 487         bzero(curses_menu_items, sizeof(curses_menu_items));
 488         bzero(k_menu_items, sizeof(k_menu_items));
 489         items_num = 0;
 490 }
 491 
 492 typedef enum {MATCH_TINKER_PATTERN_UP, MATCH_TINKER_PATTERN_DOWN,
 493         FIND_NEXT_MATCH_DOWN, FIND_NEXT_MATCH_UP} match_f;
 494 
 495 /* return the index of the matched item, or -1 if no such item exists */
 496 static int get_mext_match(const char *match_str, match_f flag)
 497 {
 498         int match_start = item_index(current_item(curses_menu));
 499         int index;
 500 
 501         if (flag == FIND_NEXT_MATCH_DOWN)
 502                 ++match_start;
 503         else if (flag == FIND_NEXT_MATCH_UP)
 504                 --match_start;
 505 
 506         index = match_start;
 507         index = (index + items_num) % items_num;
 508         while (true) {
 509                 char *str = k_menu_items[index].str;
 510                 if (strcasestr(str, match_str) != NULL)
 511                         return index;
 512                 if (flag == FIND_NEXT_MATCH_UP ||
 513                     flag == MATCH_TINKER_PATTERN_UP)
 514                         --index;
 515                 else
 516                         ++index;
 517                 index = (index + items_num) % items_num;
 518                 if (index == match_start)
 519                         return -1;
 520         }
 521 }
 522 
 523 /* Make a new item. */
 524 static void item_make(struct menu *menu, char tag, const char *fmt, ...)
 525 {
 526         va_list ap;
 527 
 528         if (items_num > MAX_MENU_ITEMS-1)
 529                 return;
 530 
 531         bzero(&k_menu_items[items_num], sizeof(k_menu_items[0]));
 532         k_menu_items[items_num].tag = tag;
 533         k_menu_items[items_num].usrptr = menu;
 534         if (menu != NULL)
 535                 k_menu_items[items_num].is_visible =
 536                         menu_is_visible(menu);
 537         else
 538                 k_menu_items[items_num].is_visible = 1;
 539 
 540         va_start(ap, fmt);
 541         vsnprintf(k_menu_items[items_num].str,
 542                   sizeof(k_menu_items[items_num].str),
 543                   fmt, ap);
 544         va_end(ap);
 545 
 546         if (!k_menu_items[items_num].is_visible)
 547                 memcpy(k_menu_items[items_num].str, "XXX", 3);
 548 
 549         curses_menu_items[items_num] = new_item(
 550                         k_menu_items[items_num].str,
 551                         k_menu_items[items_num].str);
 552         set_item_userptr(curses_menu_items[items_num],
 553                         &k_menu_items[items_num]);
 554         /*
 555         if (!k_menu_items[items_num].is_visible)
 556                 item_opts_off(curses_menu_items[items_num], O_SELECTABLE);
 557         */
 558 
 559         items_num++;
 560         curses_menu_items[items_num] = NULL;
 561 }
 562 
 563 /* very hackish. adds a string to the last item added */
 564 static void item_add_str(const char *fmt, ...)
 565 {
 566         va_list ap;
 567         int index = items_num-1;
 568         char new_str[256];
 569         char tmp_str[256];
 570 
 571         if (index < 0)
 572                 return;
 573 
 574         va_start(ap, fmt);
 575         vsnprintf(new_str, sizeof(new_str), fmt, ap);
 576         va_end(ap);
 577         snprintf(tmp_str, sizeof(tmp_str), "%s%s",
 578                         k_menu_items[index].str, new_str);
 579         strncpy(k_menu_items[index].str,
 580                 tmp_str,
 581                 sizeof(k_menu_items[index].str));
 582 
 583         free_item(curses_menu_items[index]);
 584         curses_menu_items[index] = new_item(
 585                         k_menu_items[index].str,
 586                         k_menu_items[index].str);
 587         set_item_userptr(curses_menu_items[index],
 588                         &k_menu_items[index]);
 589 }
 590 
 591 /* get the tag of the currently selected item */
 592 static char item_tag(void)
 593 {
 594         ITEM *cur;
 595         struct mitem *mcur;
 596 
 597         cur = current_item(curses_menu);
 598         if (cur == NULL)
 599                 return 0;
 600         mcur = (struct mitem *) item_userptr(cur);
 601         return mcur->tag;
 602 }
 603 
 604 static int curses_item_index(void)
 605 {
 606         return  item_index(current_item(curses_menu));
 607 }
 608 
 609 static void *item_data(void)
 610 {
 611         ITEM *cur;
 612         struct mitem *mcur;
 613 
 614         cur = current_item(curses_menu);
 615         if (!cur)
 616                 return NULL;
 617         mcur = (struct mitem *) item_userptr(cur);
 618         return mcur->usrptr;
 619 
 620 }
 621 
 622 static int item_is_tag(char tag)
 623 {
 624         return item_tag() == tag;
 625 }
 626 
 627 static char filename[PATH_MAX+1];
 628 static char menu_backtitle[PATH_MAX+128];
 629 static const char *set_config_filename(const char *config_filename)
 630 {
 631         int size;
 632 
 633         size = snprintf(menu_backtitle, sizeof(menu_backtitle),
 634                         "%s - %s", config_filename, rootmenu.prompt->text);
 635         if (size >= sizeof(menu_backtitle))
 636                 menu_backtitle[sizeof(menu_backtitle)-1] = '\0';
 637 
 638         size = snprintf(filename, sizeof(filename), "%s", config_filename);
 639         if (size >= sizeof(filename))
 640                 filename[sizeof(filename)-1] = '\0';
 641         return menu_backtitle;
 642 }
 643 
 644 /* return = 0 means we are successful.
 645  * -1 means go on doing what you were doing
 646  */
 647 static int do_exit(void)
 648 {
 649         int res;
 650         if (!conf_get_changed()) {
 651                 global_exit = 1;
 652                 return 0;
 653         }
 654         res = btn_dialog(main_window,
 655                         "Do you wish to save your new configuration?\n"
 656                                 "<ESC> to cancel and resume nconfig.",
 657                         2,
 658                         "   <save>   ",
 659                         "<don't save>");
 660         if (res == KEY_EXIT) {
 661                 global_exit = 0;
 662                 return -1;
 663         }
 664 
 665         /* if we got here, the user really wants to exit */
 666         switch (res) {
 667         case 0:
 668                 res = conf_write(filename);
 669                 if (res)
 670                         btn_dialog(
 671                                 main_window,
 672                                 "Error during writing of configuration.\n"
 673                                   "Your configuration changes were NOT saved.",
 674                                   1,
 675                                   "<OK>");
 676                 conf_write_autoconf(0);
 677                 break;
 678         default:
 679                 btn_dialog(
 680                         main_window,
 681                         "Your configuration changes were NOT saved.",
 682                         1,
 683                         "<OK>");
 684                 break;
 685         }
 686         global_exit = 1;
 687         return 0;
 688 }
 689 
 690 
 691 static void search_conf(void)
 692 {
 693         struct symbol **sym_arr;
 694         struct gstr res;
 695         struct gstr title;
 696         char *dialog_input;
 697         int dres;
 698 
 699         title = str_new();
 700         str_printf( &title, "Enter (sub)string or regexp to search for "
 701                               "(with or without \"%s\")", CONFIG_);
 702 
 703 again:
 704         dres = dialog_inputbox(main_window,
 705                         "Search Configuration Parameter",
 706                         str_get(&title),
 707                         "", &dialog_input_result, &dialog_input_result_len);
 708         switch (dres) {
 709         case 0:
 710                 break;
 711         case 1:
 712                 show_scroll_win(main_window,
 713                                 "Search Configuration", search_help);
 714                 goto again;
 715         default:
 716                 str_free(&title);
 717                 return;
 718         }
 719 
 720         /* strip the prefix if necessary */
 721         dialog_input = dialog_input_result;
 722         if (strncasecmp(dialog_input_result, CONFIG_, strlen(CONFIG_)) == 0)
 723                 dialog_input += strlen(CONFIG_);
 724 
 725         sym_arr = sym_re_search(dialog_input);
 726         res = get_relations_str(sym_arr, NULL);
 727         free(sym_arr);
 728         show_scroll_win(main_window,
 729                         "Search Results", str_get(&res));
 730         str_free(&res);
 731         str_free(&title);
 732 }
 733 
 734 
 735 static void build_conf(struct menu *menu)
 736 {
 737         struct symbol *sym;
 738         struct property *prop;
 739         struct menu *child;
 740         int type, tmp, doint = 2;
 741         tristate val;
 742         char ch;
 743 
 744         if (!menu || (!show_all_items && !menu_is_visible(menu)))
 745                 return;
 746 
 747         sym = menu->sym;
 748         prop = menu->prompt;
 749         if (!sym) {
 750                 if (prop && menu != current_menu) {
 751                         const char *prompt = menu_get_prompt(menu);
 752                         enum prop_type ptype;
 753                         ptype = menu->prompt ? menu->prompt->type : P_UNKNOWN;
 754                         switch (ptype) {
 755                         case P_MENU:
 756                                 child_count++;
 757                                 prompt = prompt;
 758                                 if (single_menu_mode) {
 759                                         item_make(menu, 'm',
 760                                                 "%s%*c%s",
 761                                                 menu->data ? "-->" : "++>",
 762                                                 indent + 1, ' ', prompt);
 763                                 } else
 764                                         item_make(menu, 'm',
 765                                                   "   %*c%s  %s",
 766                                                   indent + 1, ' ', prompt,
 767                                                   menu_is_empty(menu) ? "----" : "--->");
 768 
 769                                 if (single_menu_mode && menu->data)
 770                                         goto conf_childs;
 771                                 return;
 772                         case P_COMMENT:
 773                                 if (prompt) {
 774                                         child_count++;
 775                                         item_make(menu, ':',
 776                                                 "   %*c*** %s ***",
 777                                                 indent + 1, ' ',
 778                                                 prompt);
 779                                 }
 780                                 break;
 781                         default:
 782                                 if (prompt) {
 783                                         child_count++;
 784                                         item_make(menu, ':', "---%*c%s",
 785                                                 indent + 1, ' ',
 786                                                 prompt);
 787                                 }
 788                         }
 789                 } else
 790                         doint = 0;
 791                 goto conf_childs;
 792         }
 793 
 794         type = sym_get_type(sym);
 795         if (sym_is_choice(sym)) {
 796                 struct symbol *def_sym = sym_get_choice_value(sym);
 797                 struct menu *def_menu = NULL;
 798 
 799                 child_count++;
 800                 for (child = menu->list; child; child = child->next) {
 801                         if (menu_is_visible(child) && child->sym == def_sym)
 802                                 def_menu = child;
 803                 }
 804 
 805                 val = sym_get_tristate_value(sym);
 806                 if (sym_is_changeable(sym)) {
 807                         switch (type) {
 808                         case S_BOOLEAN:
 809                                 item_make(menu, 't', "[%c]",
 810                                                 val == no ? ' ' : '*');
 811                                 break;
 812                         case S_TRISTATE:
 813                                 switch (val) {
 814                                 case yes:
 815                                         ch = '*';
 816                                         break;
 817                                 case mod:
 818                                         ch = 'M';
 819                                         break;
 820                                 default:
 821                                         ch = ' ';
 822                                         break;
 823                                 }
 824                                 item_make(menu, 't', "<%c>", ch);
 825                                 break;
 826                         }
 827                 } else {
 828                         item_make(menu, def_menu ? 't' : ':', "   ");
 829                 }
 830 
 831                 item_add_str("%*c%s", indent + 1,
 832                                 ' ', menu_get_prompt(menu));
 833                 if (val == yes) {
 834                         if (def_menu) {
 835                                 item_add_str(" (%s)",
 836                                         menu_get_prompt(def_menu));
 837                                 item_add_str("  --->");
 838                                 if (def_menu->list) {
 839                                         indent += 2;
 840                                         build_conf(def_menu);
 841                                         indent -= 2;
 842                                 }
 843                         }
 844                         return;
 845                 }
 846         } else {
 847                 if (menu == current_menu) {
 848                         item_make(menu, ':',
 849                                 "---%*c%s", indent + 1,
 850                                 ' ', menu_get_prompt(menu));
 851                         goto conf_childs;
 852                 }
 853                 child_count++;
 854                 val = sym_get_tristate_value(sym);
 855                 if (sym_is_choice_value(sym) && val == yes) {
 856                         item_make(menu, ':', "   ");
 857                 } else {
 858                         switch (type) {
 859                         case S_BOOLEAN:
 860                                 if (sym_is_changeable(sym))
 861                                         item_make(menu, 't', "[%c]",
 862                                                 val == no ? ' ' : '*');
 863                                 else
 864                                         item_make(menu, 't', "-%c-",
 865                                                 val == no ? ' ' : '*');
 866                                 break;
 867                         case S_TRISTATE:
 868                                 switch (val) {
 869                                 case yes:
 870                                         ch = '*';
 871                                         break;
 872                                 case mod:
 873                                         ch = 'M';
 874                                         break;
 875                                 default:
 876                                         ch = ' ';
 877                                         break;
 878                                 }
 879                                 if (sym_is_changeable(sym)) {
 880                                         if (sym->rev_dep.tri == mod)
 881                                                 item_make(menu,
 882                                                         't', "{%c}", ch);
 883                                         else
 884                                                 item_make(menu,
 885                                                         't', "<%c>", ch);
 886                                 } else
 887                                         item_make(menu, 't', "-%c-", ch);
 888                                 break;
 889                         default:
 890                                 tmp = 2 + strlen(sym_get_string_value(sym));
 891                                 item_make(menu, 's', "    (%s)",
 892                                                 sym_get_string_value(sym));
 893                                 tmp = indent - tmp + 4;
 894                                 if (tmp < 0)
 895                                         tmp = 0;
 896                                 item_add_str("%*c%s%s", tmp, ' ',
 897                                                 menu_get_prompt(menu),
 898                                                 (sym_has_value(sym) ||
 899                                                  !sym_is_changeable(sym)) ? "" :
 900                                                 " (NEW)");
 901                                 goto conf_childs;
 902                         }
 903                 }
 904                 item_add_str("%*c%s%s", indent + 1, ' ',
 905                                 menu_get_prompt(menu),
 906                                 (sym_has_value(sym) || !sym_is_changeable(sym)) ?
 907                                 "" : " (NEW)");
 908                 if (menu->prompt && menu->prompt->type == P_MENU) {
 909                         item_add_str("  %s", menu_is_empty(menu) ? "----" : "--->");
 910                         return;
 911                 }
 912         }
 913 
 914 conf_childs:
 915         indent += doint;
 916         for (child = menu->list; child; child = child->next)
 917                 build_conf(child);
 918         indent -= doint;
 919 }
 920 
 921 static void reset_menu(void)
 922 {
 923         unpost_menu(curses_menu);
 924         clean_items();
 925 }
 926 
 927 /* adjust the menu to show this item.
 928  * prefer not to scroll the menu if possible*/
 929 static void center_item(int selected_index, int *last_top_row)
 930 {
 931         int toprow;
 932 
 933         set_top_row(curses_menu, *last_top_row);
 934         toprow = top_row(curses_menu);
 935         if (selected_index < toprow ||
 936             selected_index >= toprow+mwin_max_lines) {
 937                 toprow = max(selected_index-mwin_max_lines/2, 0);
 938                 if (toprow >= item_count(curses_menu)-mwin_max_lines)
 939                         toprow = item_count(curses_menu)-mwin_max_lines;
 940                 set_top_row(curses_menu, toprow);
 941         }
 942         set_current_item(curses_menu,
 943                         curses_menu_items[selected_index]);
 944         *last_top_row = toprow;
 945         post_menu(curses_menu);
 946         refresh_all_windows(main_window);
 947 }
 948 
 949 /* this function assumes reset_menu has been called before */
 950 static void show_menu(const char *prompt, const char *instructions,
 951                 int selected_index, int *last_top_row)
 952 {
 953         int maxx, maxy;
 954         WINDOW *menu_window;
 955 
 956         current_instructions = instructions;
 957 
 958         clear();
 959         (void) wattrset(main_window, attributes[NORMAL]);
 960         print_in_middle(stdscr, 1, 0, getmaxx(stdscr),
 961                         menu_backtitle,
 962                         attributes[MAIN_HEADING]);
 963 
 964         (void) wattrset(main_window, attributes[MAIN_MENU_BOX]);
 965         box(main_window, 0, 0);
 966         (void) wattrset(main_window, attributes[MAIN_MENU_HEADING]);
 967         mvwprintw(main_window, 0, 3, " %s ", prompt);
 968         (void) wattrset(main_window, attributes[NORMAL]);
 969 
 970         set_menu_items(curses_menu, curses_menu_items);
 971 
 972         /* position the menu at the middle of the screen */
 973         scale_menu(curses_menu, &maxy, &maxx);
 974         maxx = min(maxx, mwin_max_cols-2);
 975         maxy = mwin_max_lines;
 976         menu_window = derwin(main_window,
 977                         maxy,
 978                         maxx,
 979                         2,
 980                         (mwin_max_cols-maxx)/2);
 981         keypad(menu_window, TRUE);
 982         set_menu_win(curses_menu, menu_window);
 983         set_menu_sub(curses_menu, menu_window);
 984 
 985         /* must reassert this after changing items, otherwise returns to a
 986          * default of 16
 987          */
 988         set_menu_format(curses_menu, maxy, 1);
 989         center_item(selected_index, last_top_row);
 990         set_menu_format(curses_menu, maxy, 1);
 991 
 992         print_function_line();
 993 
 994         /* Post the menu */
 995         post_menu(curses_menu);
 996         refresh_all_windows(main_window);
 997 }
 998 
 999 static void adj_match_dir(match_f *match_direction)
1000 {
1001         if (*match_direction == FIND_NEXT_MATCH_DOWN)
1002                 *match_direction =
1003                         MATCH_TINKER_PATTERN_DOWN;
1004         else if (*match_direction == FIND_NEXT_MATCH_UP)
1005                 *match_direction =
1006                         MATCH_TINKER_PATTERN_UP;
1007         /* else, do no change.. */
1008 }
1009 
1010 struct match_state
1011 {
1012         int in_search;
1013         match_f match_direction;
1014         char pattern[256];
1015 };
1016 
1017 /* Return 0 means I have handled the key. In such a case, ans should hold the
1018  * item to center, or -1 otherwise.
1019  * Else return -1 .
1020  */
1021 static int do_match(int key, struct match_state *state, int *ans)
1022 {
1023         char c = (char) key;
1024         int terminate_search = 0;
1025         *ans = -1;
1026         if (key == '/' || (state->in_search && key == 27)) {
1027                 move(0, 0);
1028                 refresh();
1029                 clrtoeol();
1030                 state->in_search = 1-state->in_search;
1031                 bzero(state->pattern, sizeof(state->pattern));
1032                 state->match_direction = MATCH_TINKER_PATTERN_DOWN;
1033                 return 0;
1034         } else if (!state->in_search)
1035                 return 1;
1036 
1037         if (isalnum(c) || isgraph(c) || c == ' ') {
1038                 state->pattern[strlen(state->pattern)] = c;
1039                 state->pattern[strlen(state->pattern)] = '\0';
1040                 adj_match_dir(&state->match_direction);
1041                 *ans = get_mext_match(state->pattern,
1042                                 state->match_direction);
1043         } else if (key == KEY_DOWN) {
1044                 state->match_direction = FIND_NEXT_MATCH_DOWN;
1045                 *ans = get_mext_match(state->pattern,
1046                                 state->match_direction);
1047         } else if (key == KEY_UP) {
1048                 state->match_direction = FIND_NEXT_MATCH_UP;
1049                 *ans = get_mext_match(state->pattern,
1050                                 state->match_direction);
1051         } else if (key == KEY_BACKSPACE || key == 8 || key == 127) {
1052                 state->pattern[strlen(state->pattern)-1] = '\0';
1053                 adj_match_dir(&state->match_direction);
1054         } else
1055                 terminate_search = 1;
1056 
1057         if (terminate_search) {
1058                 state->in_search = 0;
1059                 bzero(state->pattern, sizeof(state->pattern));
1060                 move(0, 0);
1061                 refresh();
1062                 clrtoeol();
1063                 return -1;
1064         }
1065         return 0;
1066 }
1067 
1068 static void conf(struct menu *menu)
1069 {
1070         struct menu *submenu = NULL;
1071         const char *prompt = menu_get_prompt(menu);
1072         struct symbol *sym;
1073         int res;
1074         int current_index = 0;
1075         int last_top_row = 0;
1076         struct match_state match_state = {
1077                 .in_search = 0,
1078                 .match_direction = MATCH_TINKER_PATTERN_DOWN,
1079                 .pattern = "",
1080         };
1081 
1082         while (!global_exit) {
1083                 reset_menu();
1084                 current_menu = menu;
1085                 build_conf(menu);
1086                 if (!child_count)
1087                         break;
1088 
1089                 show_menu(prompt ? prompt : "Main Menu",
1090                                 menu_instructions,
1091                                 current_index, &last_top_row);
1092                 keypad((menu_win(curses_menu)), TRUE);
1093                 while (!global_exit) {
1094                         if (match_state.in_search) {
1095                                 mvprintw(0, 0,
1096                                         "searching: %s", match_state.pattern);
1097                                 clrtoeol();
1098                         }
1099                         refresh_all_windows(main_window);
1100                         res = wgetch(menu_win(curses_menu));
1101                         if (!res)
1102                                 break;
1103                         if (do_match(res, &match_state, &current_index) == 0) {
1104                                 if (current_index != -1)
1105                                         center_item(current_index,
1106                                                     &last_top_row);
1107                                 continue;
1108                         }
1109                         if (process_special_keys(&res,
1110                                                 (struct menu *) item_data()))
1111                                 break;
1112                         switch (res) {
1113                         case KEY_DOWN:
1114                                 menu_driver(curses_menu, REQ_DOWN_ITEM);
1115                                 break;
1116                         case KEY_UP:
1117                                 menu_driver(curses_menu, REQ_UP_ITEM);
1118                                 break;
1119                         case KEY_NPAGE:
1120                                 menu_driver(curses_menu, REQ_SCR_DPAGE);
1121                                 break;
1122                         case KEY_PPAGE:
1123                                 menu_driver(curses_menu, REQ_SCR_UPAGE);
1124                                 break;
1125                         case KEY_HOME:
1126                                 menu_driver(curses_menu, REQ_FIRST_ITEM);
1127                                 break;
1128                         case KEY_END:
1129                                 menu_driver(curses_menu, REQ_LAST_ITEM);
1130                                 break;
1131                         case 'h':
1132                         case '?':
1133                                 show_help((struct menu *) item_data());
1134                                 break;
1135                         }
1136                         if (res == 10 || res == 27 ||
1137                                 res == 32 || res == 'n' || res == 'y' ||
1138                                 res == KEY_LEFT || res == KEY_RIGHT ||
1139                                 res == 'm')
1140                                 break;
1141                         refresh_all_windows(main_window);
1142                 }
1143 
1144                 refresh_all_windows(main_window);
1145                 /* if ESC or left*/
1146                 if (res == 27 || (menu != &rootmenu && res == KEY_LEFT))
1147                         break;
1148 
1149                 /* remember location in the menu */
1150                 last_top_row = top_row(curses_menu);
1151                 current_index = curses_item_index();
1152 
1153                 if (!item_tag())
1154                         continue;
1155 
1156                 submenu = (struct menu *) item_data();
1157                 if (!submenu || !menu_is_visible(submenu))
1158                         continue;
1159                 sym = submenu->sym;
1160 
1161                 switch (res) {
1162                 case ' ':
1163                         if (item_is_tag('t'))
1164                                 sym_toggle_tristate_value(sym);
1165                         else if (item_is_tag('m'))
1166                                 conf(submenu);
1167                         break;
1168                 case KEY_RIGHT:
1169                 case 10: /* ENTER WAS PRESSED */
1170                         switch (item_tag()) {
1171                         case 'm':
1172                                 if (single_menu_mode)
1173                                         submenu->data =
1174                                                 (void *) (long) !submenu->data;
1175                                 else
1176                                         conf(submenu);
1177                                 break;
1178                         case 't':
1179                                 if (sym_is_choice(sym) &&
1180                                     sym_get_tristate_value(sym) == yes)
1181                                         conf_choice(submenu);
1182                                 else if (submenu->prompt &&
1183                                          submenu->prompt->type == P_MENU)
1184                                         conf(submenu);
1185                                 else if (res == 10)
1186                                         sym_toggle_tristate_value(sym);
1187                                 break;
1188                         case 's':
1189                                 conf_string(submenu);
1190                                 break;
1191                         }
1192                         break;
1193                 case 'y':
1194                         if (item_is_tag('t')) {
1195                                 if (sym_set_tristate_value(sym, yes))
1196                                         break;
1197                                 if (sym_set_tristate_value(sym, mod))
1198                                         btn_dialog(main_window, setmod_text, 0);
1199                         }
1200                         break;
1201                 case 'n':
1202                         if (item_is_tag('t'))
1203                                 sym_set_tristate_value(sym, no);
1204                         break;
1205                 case 'm':
1206                         if (item_is_tag('t'))
1207                                 sym_set_tristate_value(sym, mod);
1208                         break;
1209                 }
1210         }
1211 }
1212 
1213 static void conf_message_callback(const char *s)
1214 {
1215         btn_dialog(main_window, s, 1, "<OK>");
1216 }
1217 
1218 static void show_help(struct menu *menu)
1219 {
1220         struct gstr help;
1221 
1222         if (!menu)
1223                 return;
1224 
1225         help = str_new();
1226         menu_get_ext_help(menu, &help);
1227         show_scroll_win(main_window, menu_get_prompt(menu), str_get(&help));
1228         str_free(&help);
1229 }
1230 
1231 static void conf_choice(struct menu *menu)
1232 {
1233         const char *prompt = menu_get_prompt(menu);
1234         struct menu *child = NULL;
1235         struct symbol *active;
1236         int selected_index = 0;
1237         int last_top_row = 0;
1238         int res, i = 0;
1239         struct match_state match_state = {
1240                 .in_search = 0,
1241                 .match_direction = MATCH_TINKER_PATTERN_DOWN,
1242                 .pattern = "",
1243         };
1244 
1245         active = sym_get_choice_value(menu->sym);
1246         /* this is mostly duplicated from the conf() function. */
1247         while (!global_exit) {
1248                 reset_menu();
1249 
1250                 for (i = 0, child = menu->list; child; child = child->next) {
1251                         if (!show_all_items && !menu_is_visible(child))
1252                                 continue;
1253 
1254                         if (child->sym == sym_get_choice_value(menu->sym))
1255                                 item_make(child, ':', "<X> %s",
1256                                                 menu_get_prompt(child));
1257                         else if (child->sym)
1258                                 item_make(child, ':', "    %s",
1259                                                 menu_get_prompt(child));
1260                         else
1261                                 item_make(child, ':', "*** %s ***",
1262                                                 menu_get_prompt(child));
1263 
1264                         if (child->sym == active){
1265                                 last_top_row = top_row(curses_menu);
1266                                 selected_index = i;
1267                         }
1268                         i++;
1269                 }
1270                 show_menu(prompt ? prompt : "Choice Menu",
1271                                 radiolist_instructions,
1272                                 selected_index,
1273                                 &last_top_row);
1274                 while (!global_exit) {
1275                         if (match_state.in_search) {
1276                                 mvprintw(0, 0, "searching: %s",
1277                                          match_state.pattern);
1278                                 clrtoeol();
1279                         }
1280                         refresh_all_windows(main_window);
1281                         res = wgetch(menu_win(curses_menu));
1282                         if (!res)
1283                                 break;
1284                         if (do_match(res, &match_state, &selected_index) == 0) {
1285                                 if (selected_index != -1)
1286                                         center_item(selected_index,
1287                                                     &last_top_row);
1288                                 continue;
1289                         }
1290                         if (process_special_keys(
1291                                                 &res,
1292                                                 (struct menu *) item_data()))
1293                                 break;
1294                         switch (res) {
1295                         case KEY_DOWN:
1296                                 menu_driver(curses_menu, REQ_DOWN_ITEM);
1297                                 break;
1298                         case KEY_UP:
1299                                 menu_driver(curses_menu, REQ_UP_ITEM);
1300                                 break;
1301                         case KEY_NPAGE:
1302                                 menu_driver(curses_menu, REQ_SCR_DPAGE);
1303                                 break;
1304                         case KEY_PPAGE:
1305                                 menu_driver(curses_menu, REQ_SCR_UPAGE);
1306                                 break;
1307                         case KEY_HOME:
1308                                 menu_driver(curses_menu, REQ_FIRST_ITEM);
1309                                 break;
1310                         case KEY_END:
1311                                 menu_driver(curses_menu, REQ_LAST_ITEM);
1312                                 break;
1313                         case 'h':
1314                         case '?':
1315                                 show_help((struct menu *) item_data());
1316                                 break;
1317                         }
1318                         if (res == 10 || res == 27 || res == ' ' ||
1319                                         res == KEY_LEFT){
1320                                 break;
1321                         }
1322                         refresh_all_windows(main_window);
1323                 }
1324                 /* if ESC or left */
1325                 if (res == 27 || res == KEY_LEFT)
1326                         break;
1327 
1328                 child = item_data();
1329                 if (!child || !menu_is_visible(child) || !child->sym)
1330                         continue;
1331                 switch (res) {
1332                 case ' ':
1333                 case  10:
1334                 case KEY_RIGHT:
1335                         sym_set_tristate_value(child->sym, yes);
1336                         return;
1337                 case 'h':
1338                 case '?':
1339                         show_help(child);
1340                         active = child->sym;
1341                         break;
1342                 case KEY_EXIT:
1343                         return;
1344                 }
1345         }
1346 }
1347 
1348 static void conf_string(struct menu *menu)
1349 {
1350         const char *prompt = menu_get_prompt(menu);
1351 
1352         while (1) {
1353                 int res;
1354                 const char *heading;
1355 
1356                 switch (sym_get_type(menu->sym)) {
1357                 case S_INT:
1358                         heading = inputbox_instructions_int;
1359                         break;
1360                 case S_HEX:
1361                         heading = inputbox_instructions_hex;
1362                         break;
1363                 case S_STRING:
1364                         heading = inputbox_instructions_string;
1365                         break;
1366                 default:
1367                         heading = "Internal nconf error!";
1368                 }
1369                 res = dialog_inputbox(main_window,
1370                                 prompt ? prompt : "Main Menu",
1371                                 heading,
1372                                 sym_get_string_value(menu->sym),
1373                                 &dialog_input_result,
1374                                 &dialog_input_result_len);
1375                 switch (res) {
1376                 case 0:
1377                         if (sym_set_string_value(menu->sym,
1378                                                 dialog_input_result))
1379                                 return;
1380                         btn_dialog(main_window,
1381                                 "You have made an invalid entry.", 0);
1382                         break;
1383                 case 1:
1384                         show_help(menu);
1385                         break;
1386                 case KEY_EXIT:
1387                         return;
1388                 }
1389         }
1390 }
1391 
1392 static void conf_load(void)
1393 {
1394         while (1) {
1395                 int res;
1396                 res = dialog_inputbox(main_window,
1397                                 NULL, load_config_text,
1398                                 filename,
1399                                 &dialog_input_result,
1400                                 &dialog_input_result_len);
1401                 switch (res) {
1402                 case 0:
1403                         if (!dialog_input_result[0])
1404                                 return;
1405                         if (!conf_read(dialog_input_result)) {
1406                                 set_config_filename(dialog_input_result);
1407                                 sym_set_change_count(1);
1408                                 return;
1409                         }
1410                         btn_dialog(main_window, "File does not exist!", 0);
1411                         break;
1412                 case 1:
1413                         show_scroll_win(main_window,
1414                                         "Load Alternate Configuration",
1415                                         load_config_help);
1416                         break;
1417                 case KEY_EXIT:
1418                         return;
1419                 }
1420         }
1421 }
1422 
1423 static void conf_save(void)
1424 {
1425         while (1) {
1426                 int res;
1427                 res = dialog_inputbox(main_window,
1428                                 NULL, save_config_text,
1429                                 filename,
1430                                 &dialog_input_result,
1431                                 &dialog_input_result_len);
1432                 switch (res) {
1433                 case 0:
1434                         if (!dialog_input_result[0])
1435                                 return;
1436                         res = conf_write(dialog_input_result);
1437                         if (!res) {
1438                                 set_config_filename(dialog_input_result);
1439                                 return;
1440                         }
1441                         btn_dialog(main_window, "Can't create file!",
1442                                 1, "<OK>");
1443                         break;
1444                 case 1:
1445                         show_scroll_win(main_window,
1446                                 "Save Alternate Configuration",
1447                                 save_config_help);
1448                         break;
1449                 case KEY_EXIT:
1450                         return;
1451                 }
1452         }
1453 }
1454 
1455 static void setup_windows(void)
1456 {
1457         int lines, columns;
1458 
1459         getmaxyx(stdscr, lines, columns);
1460 
1461         if (main_window != NULL)
1462                 delwin(main_window);
1463 
1464         /* set up the menu and menu window */
1465         main_window = newwin(lines-2, columns-2, 2, 1);
1466         keypad(main_window, TRUE);
1467         mwin_max_lines = lines-7;
1468         mwin_max_cols = columns-6;
1469 
1470         /* panels order is from bottom to top */
1471         new_panel(main_window);
1472 }
1473 
1474 int main(int ac, char **av)
1475 {
1476         int lines, columns;
1477         char *mode;
1478 
1479         if (ac > 1 && strcmp(av[1], "-s") == 0) {
1480                 /* Silence conf_read() until the real callback is set up */
1481                 conf_set_message_callback(NULL);
1482                 av++;
1483         }
1484         conf_parse(av[1]);
1485         conf_read(NULL);
1486 
1487         mode = getenv("NCONFIG_MODE");
1488         if (mode) {
1489                 if (!strcasecmp(mode, "single_menu"))
1490                         single_menu_mode = 1;
1491         }
1492 
1493         /* Initialize curses */
1494         initscr();
1495         /* set color theme */
1496         set_colors();
1497 
1498         cbreak();
1499         noecho();
1500         keypad(stdscr, TRUE);
1501         curs_set(0);
1502 
1503         getmaxyx(stdscr, lines, columns);
1504         if (columns < 75 || lines < 20) {
1505                 endwin();
1506                 printf("Your terminal should have at "
1507                         "least 20 lines and 75 columns\n");
1508                 return 1;
1509         }
1510 
1511         notimeout(stdscr, FALSE);
1512 #if NCURSES_REENTRANT
1513         set_escdelay(1);
1514 #else
1515         ESCDELAY = 1;
1516 #endif
1517 
1518         /* set btns menu */
1519         curses_menu = new_menu(curses_menu_items);
1520         menu_opts_off(curses_menu, O_SHOWDESC);
1521         menu_opts_on(curses_menu, O_SHOWMATCH);
1522         menu_opts_on(curses_menu, O_ONEVALUE);
1523         menu_opts_on(curses_menu, O_NONCYCLIC);
1524         menu_opts_on(curses_menu, O_IGNORECASE);
1525         set_menu_mark(curses_menu, " ");
1526         set_menu_fore(curses_menu, attributes[MAIN_MENU_FORE]);
1527         set_menu_back(curses_menu, attributes[MAIN_MENU_BACK]);
1528         set_menu_grey(curses_menu, attributes[MAIN_MENU_GREY]);
1529 
1530         set_config_filename(conf_get_configname());
1531         setup_windows();
1532 
1533         /* check for KEY_FUNC(1) */
1534         if (has_key(KEY_F(1)) == FALSE) {
1535                 show_scroll_win(main_window,
1536                                 "Instructions",
1537                                 menu_no_f_instructions);
1538         }
1539 
1540         conf_set_message_callback(conf_message_callback);
1541         /* do the work */
1542         while (!global_exit) {
1543                 conf(&rootmenu);
1544                 if (!global_exit && do_exit() == 0)
1545                         break;
1546         }
1547         /* ok, we are done */
1548         unpost_menu(curses_menu);
1549         free_menu(curses_menu);
1550         delwin(main_window);
1551         clear();
1552         refresh();
1553         endwin();
1554         return 0;
1555 }

/* [<][>][^][v][top][bottom][index][help] */