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