Linux Audio

Check our new training course

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