Linux Audio

Check our new training course

Loading...
v6.8
   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 "mnconf-common.h"
  16#include "nconf.h"
  17#include <ctype.h>
  18
  19static 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",
 135menu_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",
 152menu_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",
 168radiolist_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",
 173inputbox_instructions_int[] =
 174"Please enter a decimal value.\n"
 175"Fractions will not be accepted.\n"
 176"Press <Enter> to apply, <Esc> to cancel.",
 177inputbox_instructions_hex[] =
 178"Please enter a hexadecimal value.\n"
 179"Press <Enter> to apply, <Esc> to cancel.",
 180inputbox_instructions_string[] =
 181"Please enter a string value.\n"
 182"Press <Enter> to apply, <Esc> to cancel.",
 183setmod_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.",
 186load_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.",
 190load_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",
 199save_config_text[] =
 200"Enter a filename to which this configuration should be saved\n"
 201"as an alternate.  Leave empty to abort.",
 202save_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",
 211search_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
 254struct mitem {
 255	char str[256];
 256	char tag;
 257	void *usrptr;
 258	int is_visible;
 259};
 260
 261#define MAX_MENU_ITEMS 4096
 262static int show_all_items;
 263static int indent;
 264static struct menu *current_menu;
 265static int child_count;
 266static int single_menu_mode;
 267/* the window in which all information appears */
 268static WINDOW *main_window;
 269/* the largest size of the menu window */
 270static int mwin_max_lines;
 271static int mwin_max_cols;
 272/* the window in which we show option buttons */
 273static MENU *curses_menu;
 274static ITEM *curses_menu_items[MAX_MENU_ITEMS];
 275static struct mitem k_menu_items[MAX_MENU_ITEMS];
 276static unsigned int items_num;
 277static int global_exit;
 278/* the currently selected button */
 279static const char *current_instructions = menu_instructions;
 280
 281static char *dialog_input_result;
 282static int dialog_input_result_len;
 283
 284static void selected_conf(struct menu *menu, struct menu *active_menu);
 285static void conf(struct menu *menu);
 286static void conf_choice(struct menu *menu);
 287static void conf_string(struct menu *menu);
 288static void conf_load(void);
 289static void conf_save(void);
 290static void show_help(struct menu *menu);
 291static int do_exit(void);
 292static void setup_windows(void);
 293static void search_conf(void);
 294
 295typedef void (*function_key_handler_t)(int *key, struct menu *menu);
 296static void handle_f1(int *key, struct menu *current_item);
 297static void handle_f2(int *key, struct menu *current_item);
 298static void handle_f3(int *key, struct menu *current_item);
 299static void handle_f4(int *key, struct menu *current_item);
 300static void handle_f5(int *key, struct menu *current_item);
 301static void handle_f6(int *key, struct menu *current_item);
 302static void handle_f7(int *key, struct menu *current_item);
 303static void handle_f8(int *key, struct menu *current_item);
 304static void handle_f9(int *key, struct menu *current_item);
 305
 306struct function_keys {
 307	const char *key_str;
 308	const char *func;
 309	function_key key;
 310	function_key_handler_t handler;
 311};
 312
 313static const int function_keys_num = 9;
 314static 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
 371static 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 */
 394static 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 */
 402static void handle_f2(int *key, struct menu *current_item)
 403{
 404	show_help(current_item);
 405	return;
 406}
 407
 408/* instructions */
 409static 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 */
 418static 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 */
 434static void handle_f5(int *key, struct menu *current_item)
 435{
 436	*key = KEY_LEFT;
 437	return;
 438}
 439
 440/* save */
 441static void handle_f6(int *key, struct menu *current_item)
 442{
 443	conf_save();
 444	return;
 445}
 446
 447/* load */
 448static void handle_f7(int *key, struct menu *current_item)
 449{
 450	conf_load();
 451	return;
 452}
 453
 454/* search */
 455static void handle_f8(int *key, struct menu *current_item)
 456{
 457	search_conf();
 458	return;
 459}
 460
 461/* exit */
 462static 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 */
 469static 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
 489static 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
 499typedef 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 */
 503static 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. */
 535static 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 */
 575static 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 */
 603static 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
 615static int curses_item_index(void)
 616{
 617	return  item_index(current_item(curses_menu));
 618}
 619
 620static 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
 633static int item_is_tag(char tag)
 634{
 635	return item_tag() == tag;
 636}
 637
 638static char filename[PATH_MAX+1];
 639static char menu_backtitle[PATH_MAX+128];
 640static 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 */
 651static 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
 695static 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
 708again:
 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
 757static 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
 935conf_childs:
 936	indent += doint;
 937	for (child = menu->list; child; child = child->next)
 938		build_conf(child);
 939	indent -= doint;
 940}
 941
 942static 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*/
 950static 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 */
 971static 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
1019static 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
1030struct 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 */
1041static 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
1088static void conf(struct menu *menu)
1089{
1090	selected_conf(menu, NULL);
1091}
1092
1093static 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
1251static void conf_message_callback(const char *s)
1252{
1253	btn_dialog(main_window, s, 1, "<OK>");
1254}
1255
1256static 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
1269static 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
1388static 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
1432static 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
1463static 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
1495static 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
1514int 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}
v5.9
   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 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		(void) wattrset(main_window, attributes[FUNCTION_HIGHLIGHT]);
 374		mvwprintw(main_window, lines-3, offset,
 375				"%s",
 376				function_keys[i].key_str);
 377		(void) wattrset(main_window, attributes[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	(void) wattrset(main_window, attributes[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 = item_index(current_item(curses_menu));
 500	int index;
 
 
 
 
 501
 502	if (flag == FIND_NEXT_MATCH_DOWN)
 503		++match_start;
 504	else if (flag == FIND_NEXT_MATCH_UP)
 505		--match_start;
 506
 
 507	index = match_start;
 508	index = (index + items_num) % items_num;
 509	while (true) {
 510		char *str = k_menu_items[index].str;
 511		if (strcasestr(str, match_str) != NULL)
 512			return index;
 513		if (flag == FIND_NEXT_MATCH_UP ||
 514		    flag == MATCH_TINKER_PATTERN_UP)
 515			--index;
 516		else
 517			++index;
 518		index = (index + items_num) % items_num;
 519		if (index == match_start)
 520			return -1;
 521	}
 522}
 523
 524/* Make a new item. */
 525static void item_make(struct menu *menu, char tag, const char *fmt, ...)
 526{
 527	va_list ap;
 528
 529	if (items_num > MAX_MENU_ITEMS-1)
 530		return;
 531
 532	bzero(&k_menu_items[items_num], sizeof(k_menu_items[0]));
 533	k_menu_items[items_num].tag = tag;
 534	k_menu_items[items_num].usrptr = menu;
 535	if (menu != NULL)
 536		k_menu_items[items_num].is_visible =
 537			menu_is_visible(menu);
 538	else
 539		k_menu_items[items_num].is_visible = 1;
 540
 541	va_start(ap, fmt);
 542	vsnprintf(k_menu_items[items_num].str,
 543		  sizeof(k_menu_items[items_num].str),
 544		  fmt, ap);
 545	va_end(ap);
 546
 547	if (!k_menu_items[items_num].is_visible)
 548		memcpy(k_menu_items[items_num].str, "XXX", 3);
 549
 550	curses_menu_items[items_num] = new_item(
 551			k_menu_items[items_num].str,
 552			k_menu_items[items_num].str);
 553	set_item_userptr(curses_menu_items[items_num],
 554			&k_menu_items[items_num]);
 555	/*
 556	if (!k_menu_items[items_num].is_visible)
 557		item_opts_off(curses_menu_items[items_num], O_SELECTABLE);
 558	*/
 559
 560	items_num++;
 561	curses_menu_items[items_num] = NULL;
 562}
 563
 564/* very hackish. adds a string to the last item added */
 565static void item_add_str(const char *fmt, ...)
 566{
 567	va_list ap;
 568	int index = items_num-1;
 569	char new_str[256];
 570	char tmp_str[256];
 571
 572	if (index < 0)
 573		return;
 574
 575	va_start(ap, fmt);
 576	vsnprintf(new_str, sizeof(new_str), fmt, ap);
 577	va_end(ap);
 578	snprintf(tmp_str, sizeof(tmp_str), "%s%s",
 579			k_menu_items[index].str, new_str);
 580	strncpy(k_menu_items[index].str,
 581		tmp_str,
 582		sizeof(k_menu_items[index].str));
 583
 584	free_item(curses_menu_items[index]);
 585	curses_menu_items[index] = new_item(
 586			k_menu_items[index].str,
 587			k_menu_items[index].str);
 588	set_item_userptr(curses_menu_items[index],
 589			&k_menu_items[index]);
 590}
 591
 592/* get the tag of the currently selected item */
 593static char item_tag(void)
 594{
 595	ITEM *cur;
 596	struct mitem *mcur;
 597
 598	cur = current_item(curses_menu);
 599	if (cur == NULL)
 600		return 0;
 601	mcur = (struct mitem *) item_userptr(cur);
 602	return mcur->tag;
 603}
 604
 605static int curses_item_index(void)
 606{
 607	return  item_index(current_item(curses_menu));
 608}
 609
 610static void *item_data(void)
 611{
 612	ITEM *cur;
 613	struct mitem *mcur;
 614
 615	cur = current_item(curses_menu);
 616	if (!cur)
 617		return NULL;
 618	mcur = (struct mitem *) item_userptr(cur);
 619	return mcur->usrptr;
 620
 621}
 622
 623static int item_is_tag(char tag)
 624{
 625	return item_tag() == tag;
 626}
 627
 628static char filename[PATH_MAX+1];
 629static char menu_backtitle[PATH_MAX+128];
 630static const char *set_config_filename(const char *config_filename)
 631{
 632	int size;
 
 633
 634	size = snprintf(menu_backtitle, sizeof(menu_backtitle),
 635			"%s - %s", config_filename, rootmenu.prompt->text);
 636	if (size >= sizeof(menu_backtitle))
 637		menu_backtitle[sizeof(menu_backtitle)-1] = '\0';
 638
 639	size = snprintf(filename, sizeof(filename), "%s", config_filename);
 640	if (size >= sizeof(filename))
 641		filename[sizeof(filename)-1] = '\0';
 642	return menu_backtitle;
 643}
 644
 645/* return = 0 means we are successful.
 646 * -1 means go on doing what you were doing
 647 */
 648static int do_exit(void)
 649{
 650	int res;
 651	if (!conf_get_changed()) {
 652		global_exit = 1;
 653		return 0;
 654	}
 655	res = btn_dialog(main_window,
 656			"Do you wish to save your new configuration?\n"
 657				"<ESC> to cancel and resume nconfig.",
 658			2,
 659			"   <save>   ",
 660			"<don't save>");
 661	if (res == KEY_EXIT) {
 662		global_exit = 0;
 663		return -1;
 664	}
 665
 666	/* if we got here, the user really wants to exit */
 667	switch (res) {
 668	case 0:
 669		res = conf_write(filename);
 670		if (res)
 671			btn_dialog(
 672				main_window,
 673				"Error during writing of configuration.\n"
 674				  "Your configuration changes were NOT saved.",
 675				  1,
 676				  "<OK>");
 677		conf_write_autoconf(0);
 678		break;
 679	default:
 680		btn_dialog(
 681			main_window,
 682			"Your configuration changes were NOT saved.",
 683			1,
 684			"<OK>");
 685		break;
 686	}
 687	global_exit = 1;
 688	return 0;
 689}
 690
 691
 692static void search_conf(void)
 693{
 694	struct symbol **sym_arr;
 695	struct gstr res;
 696	struct gstr title;
 697	char *dialog_input;
 698	int dres;
 
 699
 700	title = str_new();
 701	str_printf( &title, "Enter (sub)string or regexp to search for "
 702			      "(with or without \"%s\")", CONFIG_);
 703
 704again:
 705	dres = dialog_inputbox(main_window,
 706			"Search Configuration Parameter",
 707			str_get(&title),
 708			"", &dialog_input_result, &dialog_input_result_len);
 709	switch (dres) {
 710	case 0:
 711		break;
 712	case 1:
 713		show_scroll_win(main_window,
 714				"Search Configuration", search_help);
 715		goto again;
 716	default:
 717		str_free(&title);
 718		return;
 719	}
 720
 721	/* strip the prefix if necessary */
 722	dialog_input = dialog_input_result;
 723	if (strncasecmp(dialog_input_result, CONFIG_, strlen(CONFIG_)) == 0)
 724		dialog_input += strlen(CONFIG_);
 725
 726	sym_arr = sym_re_search(dialog_input);
 727	res = get_relations_str(sym_arr, NULL);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 728	free(sym_arr);
 729	show_scroll_win(main_window,
 730			"Search Results", str_get(&res));
 731	str_free(&res);
 732	str_free(&title);
 733}
 734
 735
 736static void build_conf(struct menu *menu)
 737{
 738	struct symbol *sym;
 739	struct property *prop;
 740	struct menu *child;
 741	int type, tmp, doint = 2;
 742	tristate val;
 743	char ch;
 744
 745	if (!menu || (!show_all_items && !menu_is_visible(menu)))
 746		return;
 747
 748	sym = menu->sym;
 749	prop = menu->prompt;
 750	if (!sym) {
 751		if (prop && menu != current_menu) {
 752			const char *prompt = menu_get_prompt(menu);
 753			enum prop_type ptype;
 754			ptype = menu->prompt ? menu->prompt->type : P_UNKNOWN;
 755			switch (ptype) {
 756			case P_MENU:
 757				child_count++;
 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}