Linux Audio

Check our new training course

Loading...
v4.17
   1/*
   2 * Copyright (C) 2008 Nir Tzachar <nir.tzachar@gmail.com?
   3 * Released under the terms of the GNU GPL v2.0.
   4 *
   5 * Derived from menuconfig.
   6 *
   7 */
   8#ifndef _GNU_SOURCE
   9#define _GNU_SOURCE
  10#endif
  11#include <string.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[] = N_(
  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[] = N_(
 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[] = N_(
 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[] = N_(
 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[] = N_(
 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[] = N_(
 177"Please enter a hexadecimal value.\n"
 178"Press <Enter> to apply, <Esc> to cancel."),
 179inputbox_instructions_string[] = N_(
 180"Please enter a string value.\n"
 181"Press <Enter> to apply, <Esc> to cancel."),
 182setmod_text[] = N_(
 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[] = N_(
 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[] = N_(
 
 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[] = N_(
 199"Enter a filename to which this configuration should be saved\n"
 200"as an alternate.  Leave empty to abort."),
 201save_config_help[] = N_(
 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[] = N_(
 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		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_changable(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_changable(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_changable(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_changable(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_changable(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 == 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 *fmt, va_list ap)
1214{
1215	char buf[1024];
1216
1217	vsnprintf(buf, sizeof(buf), fmt, ap);
1218	btn_dialog(main_window, buf, 1, "<OK>");
1219}
1220
1221static void show_help(struct menu *menu)
1222{
1223	struct gstr help;
1224
1225	if (!menu)
1226		return;
1227
1228	help = str_new();
1229	menu_get_ext_help(menu, &help);
1230	show_scroll_win(main_window, _(menu_get_prompt(menu)), str_get(&help));
1231	str_free(&help);
1232}
1233
1234static void conf_choice(struct menu *menu)
1235{
1236	const char *prompt = _(menu_get_prompt(menu));
1237	struct menu *child = NULL;
1238	struct symbol *active;
1239	int selected_index = 0;
1240	int last_top_row = 0;
1241	int res, i = 0;
1242	struct match_state match_state = {
1243		.in_search = 0,
1244		.match_direction = MATCH_TINKER_PATTERN_DOWN,
1245		.pattern = "",
1246	};
1247
1248	active = sym_get_choice_value(menu->sym);
1249	/* this is mostly duplicated from the conf() function. */
1250	while (!global_exit) {
1251		reset_menu();
1252
1253		for (i = 0, child = menu->list; child; child = child->next) {
1254			if (!show_all_items && !menu_is_visible(child))
1255				continue;
1256
1257			if (child->sym == sym_get_choice_value(menu->sym))
1258				item_make(child, ':', "<X> %s",
1259						_(menu_get_prompt(child)));
1260			else if (child->sym)
1261				item_make(child, ':', "    %s",
1262						_(menu_get_prompt(child)));
1263			else
1264				item_make(child, ':', "*** %s ***",
1265						_(menu_get_prompt(child)));
1266
1267			if (child->sym == active){
1268				last_top_row = top_row(curses_menu);
1269				selected_index = i;
1270			}
1271			i++;
1272		}
1273		show_menu(prompt ? _(prompt) : _("Choice Menu"),
1274				_(radiolist_instructions),
1275				selected_index,
1276				&last_top_row);
1277		while (!global_exit) {
1278			if (match_state.in_search) {
1279				mvprintw(0, 0, "searching: %s",
1280					 match_state.pattern);
1281				clrtoeol();
1282			}
1283			refresh_all_windows(main_window);
1284			res = wgetch(menu_win(curses_menu));
1285			if (!res)
1286				break;
1287			if (do_match(res, &match_state, &selected_index) == 0) {
1288				if (selected_index != -1)
1289					center_item(selected_index,
1290						    &last_top_row);
1291				continue;
1292			}
1293			if (process_special_keys(
1294						&res,
1295						(struct menu *) item_data()))
1296				break;
1297			switch (res) {
1298			case KEY_DOWN:
1299				menu_driver(curses_menu, REQ_DOWN_ITEM);
1300				break;
1301			case KEY_UP:
1302				menu_driver(curses_menu, REQ_UP_ITEM);
1303				break;
1304			case KEY_NPAGE:
1305				menu_driver(curses_menu, REQ_SCR_DPAGE);
1306				break;
1307			case KEY_PPAGE:
1308				menu_driver(curses_menu, REQ_SCR_UPAGE);
1309				break;
1310			case KEY_HOME:
1311				menu_driver(curses_menu, REQ_FIRST_ITEM);
1312				break;
1313			case KEY_END:
1314				menu_driver(curses_menu, REQ_LAST_ITEM);
1315				break;
1316			case 'h':
1317			case '?':
1318				show_help((struct menu *) item_data());
1319				break;
1320			}
1321			if (res == 10 || res == 27 || res == ' ' ||
1322					res == KEY_LEFT){
1323				break;
1324			}
1325			refresh_all_windows(main_window);
1326		}
1327		/* if ESC or left */
1328		if (res == 27 || res == KEY_LEFT)
1329			break;
1330
1331		child = item_data();
1332		if (!child || !menu_is_visible(child) || !child->sym)
1333			continue;
1334		switch (res) {
1335		case ' ':
1336		case  10:
1337		case KEY_RIGHT:
1338			sym_set_tristate_value(child->sym, yes);
1339			return;
1340		case 'h':
1341		case '?':
1342			show_help(child);
1343			active = child->sym;
1344			break;
1345		case KEY_EXIT:
1346			return;
1347		}
1348	}
1349}
1350
1351static void conf_string(struct menu *menu)
1352{
1353	const char *prompt = menu_get_prompt(menu);
 
1354
1355	while (1) {
1356		int res;
1357		const char *heading;
1358
1359		switch (sym_get_type(menu->sym)) {
1360		case S_INT:
1361			heading = _(inputbox_instructions_int);
1362			break;
1363		case S_HEX:
1364			heading = _(inputbox_instructions_hex);
1365			break;
1366		case S_STRING:
1367			heading = _(inputbox_instructions_string);
1368			break;
1369		default:
1370			heading = _("Internal nconf error!");
1371		}
1372		res = dialog_inputbox(main_window,
1373				prompt ? _(prompt) : _("Main Menu"),
1374				heading,
1375				sym_get_string_value(menu->sym),
1376				&dialog_input_result,
1377				&dialog_input_result_len);
1378		switch (res) {
1379		case 0:
1380			if (sym_set_string_value(menu->sym,
1381						dialog_input_result))
1382				return;
1383			btn_dialog(main_window,
1384				_("You have made an invalid entry."), 0);
1385			break;
1386		case 1:
1387			show_help(menu);
1388			break;
1389		case KEY_EXIT:
1390			return;
1391		}
1392	}
1393}
1394
1395static void conf_load(void)
1396{
 
1397	while (1) {
1398		int res;
1399		res = dialog_inputbox(main_window,
1400				NULL, load_config_text,
1401				filename,
1402				&dialog_input_result,
1403				&dialog_input_result_len);
1404		switch (res) {
1405		case 0:
1406			if (!dialog_input_result[0])
1407				return;
1408			if (!conf_read(dialog_input_result)) {
1409				set_config_filename(dialog_input_result);
1410				sym_set_change_count(1);
1411				return;
1412			}
1413			btn_dialog(main_window, _("File does not exist!"), 0);
1414			break;
1415		case 1:
1416			show_scroll_win(main_window,
1417					_("Load Alternate Configuration"),
1418					load_config_help);
1419			break;
1420		case KEY_EXIT:
1421			return;
1422		}
1423	}
1424}
1425
1426static void conf_save(void)
1427{
 
1428	while (1) {
1429		int res;
1430		res = dialog_inputbox(main_window,
1431				NULL, save_config_text,
1432				filename,
1433				&dialog_input_result,
1434				&dialog_input_result_len);
1435		switch (res) {
1436		case 0:
1437			if (!dialog_input_result[0])
1438				return;
1439			res = conf_write(dialog_input_result);
1440			if (!res) {
1441				set_config_filename(dialog_input_result);
1442				return;
1443			}
1444			btn_dialog(main_window, _("Can't create file! "
1445				"Probably a nonexistent directory."),
1446				1, "<OK>");
1447			break;
1448		case 1:
1449			show_scroll_win(main_window,
1450				_("Save Alternate Configuration"),
1451				save_config_help);
1452			break;
1453		case KEY_EXIT:
1454			return;
1455		}
1456	}
1457}
1458
1459static void setup_windows(void)
1460{
1461	int lines, columns;
1462
1463	getmaxyx(stdscr, lines, columns);
1464
1465	if (main_window != NULL)
1466		delwin(main_window);
1467
1468	/* set up the menu and menu window */
1469	main_window = newwin(lines-2, columns-2, 2, 1);
1470	keypad(main_window, TRUE);
1471	mwin_max_lines = lines-7;
1472	mwin_max_cols = columns-6;
1473
1474	/* panels order is from bottom to top */
1475	new_panel(main_window);
1476}
1477
1478int main(int ac, char **av)
1479{
1480	int lines, columns;
1481	char *mode;
1482
1483	setlocale(LC_ALL, "");
1484	bindtextdomain(PACKAGE, LOCALEDIR);
1485	textdomain(PACKAGE);
1486
1487	if (ac > 1 && strcmp(av[1], "-s") == 0) {
1488		/* Silence conf_read() until the real callback is set up */
1489		conf_set_message_callback(NULL);
1490		av++;
1491	}
1492	conf_parse(av[1]);
1493	conf_read(NULL);
1494
1495	mode = getenv("NCONFIG_MODE");
1496	if (mode) {
1497		if (!strcasecmp(mode, "single_menu"))
1498			single_menu_mode = 1;
1499	}
1500
1501	/* Initialize curses */
1502	initscr();
1503	/* set color theme */
1504	set_colors();
1505
1506	cbreak();
1507	noecho();
1508	keypad(stdscr, TRUE);
1509	curs_set(0);
1510
1511	getmaxyx(stdscr, lines, columns);
1512	if (columns < 75 || lines < 20) {
1513		endwin();
1514		printf("Your terminal should have at "
1515			"least 20 lines and 75 columns\n");
1516		return 1;
1517	}
1518
1519	notimeout(stdscr, FALSE);
1520#if NCURSES_REENTRANT
1521	set_escdelay(1);
1522#else
1523	ESCDELAY = 1;
1524#endif
1525
1526	/* set btns menu */
1527	curses_menu = new_menu(curses_menu_items);
1528	menu_opts_off(curses_menu, O_SHOWDESC);
1529	menu_opts_on(curses_menu, O_SHOWMATCH);
1530	menu_opts_on(curses_menu, O_ONEVALUE);
1531	menu_opts_on(curses_menu, O_NONCYCLIC);
1532	menu_opts_on(curses_menu, O_IGNORECASE);
1533	set_menu_mark(curses_menu, " ");
1534	set_menu_fore(curses_menu, attributes[MAIN_MENU_FORE]);
1535	set_menu_back(curses_menu, attributes[MAIN_MENU_BACK]);
1536	set_menu_grey(curses_menu, attributes[MAIN_MENU_GREY]);
1537
1538	set_config_filename(conf_get_configname());
1539	setup_windows();
1540
1541	/* check for KEY_FUNC(1) */
1542	if (has_key(KEY_F(1)) == FALSE) {
1543		show_scroll_win(main_window,
1544				_("Instructions"),
1545				_(menu_no_f_instructions));
1546	}
1547
1548	conf_set_message_callback(conf_message_callback);
1549	/* do the work */
1550	while (!global_exit) {
1551		conf(&rootmenu);
1552		if (!global_exit && do_exit() == 0)
1553			break;
1554	}
1555	/* ok, we are done */
1556	unpost_menu(curses_menu);
1557	free_menu(curses_menu);
1558	delwin(main_window);
1559	clear();
1560	refresh();
1561	endwin();
1562	return 0;
1563}
v3.1
   1/*
   2 * Copyright (C) 2008 Nir Tzachar <nir.tzachar@gmail.com?
   3 * Released under the terms of the GNU GPL v2.0.
   4 *
   5 * Derived from menuconfig.
   6 *
   7 */
 
   8#define _GNU_SOURCE
 
   9#include <string.h>
 
  10
  11#include "lkc.h"
  12#include "nconf.h"
  13#include <ctype.h>
  14
  15static const char nconf_readme[] = N_(
  16"Overview\n"
  17"--------\n"
  18"This interface let you select features and parameters for the build.\n"
  19"Features can either be built-in, modularized, or ignored. Parameters\n"
  20"must be entered in as decimal or hexadecimal numbers or text.\n"
  21"\n"
  22"Menu items beginning with following braces represent features that\n"
  23"  [ ] can be built in or removed\n"
  24"  < > can be built in, modularized or removed\n"
  25"  { } can be built in or modularized (selected by other feature)\n"
  26"  - - are selected by other feature,\n"
  27"  XXX cannot be selected. Use Symbol Info to find out why,\n"
  28"while *, M or whitespace inside braces means to build in, build as\n"
  29"a module or to exclude the feature respectively.\n"
  30"\n"
  31"To change any of these features, highlight it with the cursor\n"
  32"keys and press <Y> to build it in, <M> to make it a module or\n"
  33"<N> to removed it.  You may also press the <Space Bar> to cycle\n"
  34"through the available options (ie. Y->N->M->Y).\n"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
  35"\n"
  36"Some additional keyboard hints:\n"
  37"\n"
  38"Menus\n"
  39"----------\n"
  40"o  Use the Up/Down arrow keys (cursor keys) to highlight the item\n"
  41"   you wish to change use <Enter> or <Space>. Goto submenu by \n"
  42"   pressing <Enter> of <right-arrow>. Use <Esc> or <left-arrow> to go back.\n"
  43"   Submenus are designated by \"--->\".\n"
  44"\n"
  45"   Searching: pressing '/' triggers interactive search mode.\n"
  46"              nconfig performs a case insensitive search for the string\n"
  47"              in the menu prompts (no regex support).\n"
  48"              Pressing the up/down keys highlights the previous/next\n"
  49"              matching item. Backspace removes one character from the\n"
  50"              match string. Pressing either '/' again or ESC exits\n"
  51"              search mode. All other keys behave normally.\n"
  52"\n"
  53"   You may also use the <PAGE UP> and <PAGE DOWN> keys to scroll\n"
  54"   unseen options into view.\n"
 
  55"\n"
  56"o  To exit a menu use the just press <ESC> <F5> <F8> or <left-arrow>.\n"
  57"\n"
  58"o  To get help with an item, press <F1>\n"
  59"   Shortcut: Press <h> or <?>.\n"
  60"\n"
  61"\n"
  62"Radiolists  (Choice lists)\n"
  63"-----------\n"
  64"o  Use the cursor keys to select the option you wish to set and press\n"
  65"   <S> or the <SPACE BAR>.\n"
  66"\n"
  67"   Shortcut: Press the first letter of the option you wish to set then\n"
  68"             press <S> or <SPACE BAR>.\n"
  69"\n"
  70"o  To see available help for the item, press <F1>\n"
  71"   Shortcut: Press <H> or <?>.\n"
  72"\n"
  73"\n"
  74"Data Entry\n"
  75"-----------\n"
  76"o  Enter the requested information and press <ENTER>\n"
  77"   If you are entering hexadecimal values, it is not necessary to\n"
  78"   add the '0x' prefix to the entry.\n"
  79"\n"
  80"o  For help, press <F1>.\n"
  81"\n"
  82"\n"
  83"Text Box    (Help Window)\n"
  84"--------\n"
  85"o  Use the cursor keys to scroll up/down/left/right.  The VI editor\n"
  86"   keys h,j,k,l function here as do <SPACE BAR> for those\n"
  87"   who are familiar with less and lynx.\n"
  88"\n"
  89"o  Press <Enter>, <F1>, <F5>, <F7> or <Esc> to exit.\n"
  90"\n"
  91"\n"
  92"Alternate Configuration Files\n"
  93"-----------------------------\n"
  94"nconfig supports the use of alternate configuration files for\n"
  95"those who, for various reasons, find it necessary to switch\n"
  96"between different configurations.\n"
  97"\n"
  98"At the end of the main menu you will find two options.  One is\n"
  99"for saving the current configuration to a file of your choosing.\n"
 100"The other option is for loading a previously saved alternate\n"
 101"configuration.\n"
 102"\n"
 103"Even if you don't use alternate configuration files, but you\n"
 104"find during a nconfig session that you have completely messed\n"
 105"up your settings, you may use the \"Load Alternate...\" option to\n"
 106"restore your previously saved settings from \".config\" without\n"
 107"restarting nconfig.\n"
 108"\n"
 109"Other information\n"
 110"-----------------\n"
 111"If you use nconfig in an XTERM window make sure you have your\n"
 112"$TERM variable set to point to a xterm definition which supports color.\n"
 113"Otherwise, nconfig will look rather bad.  nconfig will not\n"
 114"display correctly in a RXVT window because rxvt displays only one\n"
 115"intensity of color, bright.\n"
 116"\n"
 117"nconfig will display larger menus on screens or xterms which are\n"
 118"set to display more than the standard 25 row by 80 column geometry.\n"
 119"In order for this to work, the \"stty size\" command must be able to\n"
 120"display the screen's current row and column geometry.  I STRONGLY\n"
 121"RECOMMEND that you make sure you do NOT have the shell variables\n"
 122"LINES and COLUMNS exported into your environment.  Some distributions\n"
 123"export those variables via /etc/profile.  Some ncurses programs can\n"
 124"become confused when those variables (LINES & COLUMNS) don't reflect\n"
 125"the true screen size.\n"
 126"\n"
 127"Optional personality available\n"
 128"------------------------------\n"
 129"If you prefer to have all of the options listed in a single menu, rather\n"
 130"than the default multimenu hierarchy, run the nconfig with NCONFIG_MODE\n"
 131"environment variable set to single_menu. Example:\n"
 132"\n"
 133"make NCONFIG_MODE=single_menu nconfig\n"
 134"\n"
 135"<Enter> will then unroll the appropriate category, or enfold it if it\n"
 136"is already unrolled.\n"
 
 137"\n"
 138"Note that this mode can eventually be a little more CPU expensive\n"
 139"(especially with a larger number of unrolled categories) than the\n"
 140"default mode.\n"
 141"\n"),
 142menu_no_f_instructions[] = N_(
 143" You do not have function keys support. Please follow the\n"
 144" following instructions:\n"
 145" Arrow keys navigate the menu.\n"
 146" <Enter> or <right-arrow> selects submenus --->.\n"
 147" Capital Letters are hotkeys.\n"
 148" Pressing <Y> includes, <N> excludes, <M> modularizes features.\n"
 149" Pressing SpaceBar toggles between the above options.\n"
 150" Press <Esc> or <left-arrow> to go back one menu,\n"
 151" <?> or <h> for Help, </> for Search.\n"
 152" <1> is interchangeable with <F1>, <2> with <F2>, etc.\n"
 153" Legend: [*] built-in  [ ] excluded  <M> module  < > module capable.\n"
 154" <Esc> always leaves the current window.\n"),
 
 
 
 
 155menu_instructions[] = N_(
 156" Arrow keys navigate the menu.\n"
 157" <Enter> or <right-arrow> selects submenus --->.\n"
 158" Capital Letters are hotkeys.\n"
 159" Pressing <Y> includes, <N> excludes, <M> modularizes features.\n"
 160" Pressing SpaceBar toggles between the above options\n"
 161" Press <Esc>, <F5> or <left-arrow> to go back one menu,\n"
 162" <?>, <F1> or <h> for Help, </> for Search.\n"
 163" <1> is interchangeable with <F1>, <2> with <F2>, etc.\n"
 164" Legend: [*] built-in  [ ] excluded  <M> module  < > module capable.\n"
 165" <Esc> always leaves the current window\n"),
 
 
 
 
 
 166radiolist_instructions[] = N_(
 167" Use the arrow keys to navigate this window or\n"
 168" press the hotkey of the item you wish to select\n"
 169" followed by the <SPACE BAR>.\n"
 170" Press <?>, <F1> or <h> for additional information about this option.\n"),
 171inputbox_instructions_int[] = N_(
 172"Please enter a decimal value.\n"
 173"Fractions will not be accepted.\n"
 174"Press <RETURN> to accept, <ESC> to cancel."),
 175inputbox_instructions_hex[] = N_(
 176"Please enter a hexadecimal value.\n"
 177"Press <RETURN> to accept, <ESC> to cancel."),
 178inputbox_instructions_string[] = N_(
 179"Please enter a string value.\n"
 180"Press <RETURN> to accept, <ESC> to cancel."),
 181setmod_text[] = N_(
 182"This feature depends on another which\n"
 183"has been configured as a module.\n"
 184"As a result, this feature will be built as a module."),
 185nohelp_text[] = N_(
 186"There is no help available for this option.\n"),
 187load_config_text[] = N_(
 188"Enter the name of the configuration file you wish to load.\n"
 189"Accept the name shown to restore the configuration you\n"
 190"last retrieved.  Leave blank to abort."),
 191load_config_help[] = N_(
 192"\n"
 193"For various reasons, one may wish to keep several different\n"
 194"configurations available on a single machine.\n"
 195"\n"
 196"If you have saved a previous configuration in a file other than the\n"
 197"default one, entering its name here will allow you to modify that\n"
 198"configuration.\n"
 199"\n"
 200"If you are uncertain, then you have probably never used alternate\n"
 201"configuration files.  You should therefor leave this blank to abort.\n"),
 202save_config_text[] = N_(
 203"Enter a filename to which this configuration should be saved\n"
 204"as an alternate.  Leave blank to abort."),
 205save_config_help[] = N_(
 206"\n"
 207"For various reasons, one may wish to keep different configurations\n"
 208"available on a single machine.\n"
 209"\n"
 210"Entering a file name here will allow you to later retrieve, modify\n"
 211"and use the current configuration as an alternate to whatever\n"
 212"configuration options you have selected at that time.\n"
 213"\n"
 214"If you are uncertain what all this means then you should probably\n"
 215"leave this blank.\n"),
 216search_help[] = N_(
 217"\n"
 218"Search for symbols and display their relations. Regular expressions\n"
 219"are allowed.\n"
 220"Example: search for \"^FOO\"\n"
 221"Result:\n"
 222"-----------------------------------------------------------------\n"
 223"Symbol: FOO [ = m]\n"
 224"Prompt: Foo bus is used to drive the bar HW\n"
 225"Defined at drivers/pci/Kconfig:47\n"
 226"Depends on: X86_LOCAL_APIC && X86_IO_APIC || IA64\n"
 227"Location:\n"
 228"  -> Bus options (PCI, PCMCIA, EISA, MCA, ISA)\n"
 229"    -> PCI support (PCI [ = y])\n"
 230"      -> PCI access mode (<choice> [ = y])\n"
 231"Selects: LIBCRC32\n"
 232"Selected by: BAR\n"
 233"-----------------------------------------------------------------\n"
 234"o The line 'Prompt:' shows the text used in the menu structure for\n"
 235"  this symbol\n"
 236"o The 'Defined at' line tell at what file / line number the symbol\n"
 237"  is defined\n"
 238"o The 'Depends on:' line tell what symbols needs to be defined for\n"
 239"  this symbol to be visible in the menu (selectable)\n"
 240"o The 'Location:' lines tell where in the menu structure this symbol\n"
 241"  is located\n"
 242"    A location followed by a [ = y] indicate that this is a selectable\n"
 243"    menu item - and current value is displayed inside brackets.\n"
 244"o The 'Selects:' line tell what symbol will be automatically\n"
 245"  selected if this symbol is selected (y or m)\n"
 246"o The 'Selected by' line tell what symbol has selected this symbol\n"
 247"\n"
 248"Only relevant lines are shown.\n"
 249"\n\n"
 250"Search examples:\n"
 251"Examples: USB  => find all symbols containing USB\n"
 252"          ^USB => find all symbols starting with USB\n"
 253"          USB$ => find all symbols ending with USB\n"
 254"\n");
 255
 256struct mitem {
 257	char str[256];
 258	char tag;
 259	void *usrptr;
 260	int is_visible;
 261};
 262
 263#define MAX_MENU_ITEMS 4096
 264static int show_all_items;
 265static int indent;
 266static struct menu *current_menu;
 267static int child_count;
 268static int single_menu_mode;
 269/* the window in which all information appears */
 270static WINDOW *main_window;
 271/* the largest size of the menu window */
 272static int mwin_max_lines;
 273static int mwin_max_cols;
 274/* the window in which we show option buttons */
 275static MENU *curses_menu;
 276static ITEM *curses_menu_items[MAX_MENU_ITEMS];
 277static struct mitem k_menu_items[MAX_MENU_ITEMS];
 278static int items_num;
 279static int global_exit;
 280/* the currently selected button */
 281const char *current_instructions = menu_instructions;
 
 
 
 282
 283static void conf(struct menu *menu);
 284static void conf_choice(struct menu *menu);
 285static void conf_string(struct menu *menu);
 286static void conf_load(void);
 287static void conf_save(void);
 288static void show_help(struct menu *menu);
 289static int do_exit(void);
 290static void setup_windows(void);
 291static void search_conf(void);
 292
 293typedef void (*function_key_handler_t)(int *key, struct menu *menu);
 294static void handle_f1(int *key, struct menu *current_item);
 295static void handle_f2(int *key, struct menu *current_item);
 296static void handle_f3(int *key, struct menu *current_item);
 297static void handle_f4(int *key, struct menu *current_item);
 298static void handle_f5(int *key, struct menu *current_item);
 299static void handle_f6(int *key, struct menu *current_item);
 300static void handle_f7(int *key, struct menu *current_item);
 301static void handle_f8(int *key, struct menu *current_item);
 302static void handle_f9(int *key, struct menu *current_item);
 303
 304struct function_keys {
 305	const char *key_str;
 306	const char *func;
 307	function_key key;
 308	function_key_handler_t handler;
 309};
 310
 311static const int function_keys_num = 9;
 312struct function_keys function_keys[] = {
 313	{
 314		.key_str = "F1",
 315		.func = "Help",
 316		.key = F_HELP,
 317		.handler = handle_f1,
 318	},
 319	{
 320		.key_str = "F2",
 321		.func = "Sym Info",
 322		.key = F_SYMBOL,
 323		.handler = handle_f2,
 324	},
 325	{
 326		.key_str = "F3",
 327		.func = "Insts",
 328		.key = F_INSTS,
 329		.handler = handle_f3,
 330	},
 331	{
 332		.key_str = "F4",
 333		.func = "Config",
 334		.key = F_CONF,
 335		.handler = handle_f4,
 336	},
 337	{
 338		.key_str = "F5",
 339		.func = "Back",
 340		.key = F_BACK,
 341		.handler = handle_f5,
 342	},
 343	{
 344		.key_str = "F6",
 345		.func = "Save",
 346		.key = F_SAVE,
 347		.handler = handle_f6,
 348	},
 349	{
 350		.key_str = "F7",
 351		.func = "Load",
 352		.key = F_LOAD,
 353		.handler = handle_f7,
 354	},
 355	{
 356		.key_str = "F8",
 357		.func = "Sym Search",
 358		.key = F_SEARCH,
 359		.handler = handle_f8,
 360	},
 361	{
 362		.key_str = "F9",
 363		.func = "Exit",
 364		.key = F_EXIT,
 365		.handler = handle_f9,
 366	},
 367};
 368
 369static void print_function_line(void)
 370{
 371	int i;
 372	int offset = 1;
 373	const int skip = 1;
 
 374
 375	for (i = 0; i < function_keys_num; i++) {
 376		(void) wattrset(main_window, attributes[FUNCTION_HIGHLIGHT]);
 377		mvwprintw(main_window, LINES-3, offset,
 378				"%s",
 379				function_keys[i].key_str);
 380		(void) wattrset(main_window, attributes[FUNCTION_TEXT]);
 381		offset += strlen(function_keys[i].key_str);
 382		mvwprintw(main_window, LINES-3,
 383				offset, "%s",
 384				function_keys[i].func);
 385		offset += strlen(function_keys[i].func) + skip;
 386	}
 387	(void) wattrset(main_window, attributes[NORMAL]);
 388}
 389
 390/* help */
 391static void handle_f1(int *key, struct menu *current_item)
 392{
 393	show_scroll_win(main_window,
 394			_("README"), _(nconf_readme));
 395	return;
 396}
 397
 398/* symbole help */
 399static void handle_f2(int *key, struct menu *current_item)
 400{
 401	show_help(current_item);
 402	return;
 403}
 404
 405/* instructions */
 406static void handle_f3(int *key, struct menu *current_item)
 407{
 408	show_scroll_win(main_window,
 409			_("Instructions"),
 410			_(current_instructions));
 411	return;
 412}
 413
 414/* config */
 415static void handle_f4(int *key, struct menu *current_item)
 416{
 417	int res = btn_dialog(main_window,
 418			_("Show all symbols?"),
 419			2,
 420			"   <Show All>   ",
 421			"<Don't show all>");
 422	if (res == 0)
 423		show_all_items = 1;
 424	else if (res == 1)
 425		show_all_items = 0;
 426
 427	return;
 428}
 429
 430/* back */
 431static void handle_f5(int *key, struct menu *current_item)
 432{
 433	*key = KEY_LEFT;
 434	return;
 435}
 436
 437/* save */
 438static void handle_f6(int *key, struct menu *current_item)
 439{
 440	conf_save();
 441	return;
 442}
 443
 444/* load */
 445static void handle_f7(int *key, struct menu *current_item)
 446{
 447	conf_load();
 448	return;
 449}
 450
 451/* search */
 452static void handle_f8(int *key, struct menu *current_item)
 453{
 454	search_conf();
 455	return;
 456}
 457
 458/* exit */
 459static void handle_f9(int *key, struct menu *current_item)
 460{
 461	do_exit();
 462	return;
 463}
 464
 465/* return != 0 to indicate the key was handles */
 466static int process_special_keys(int *key, struct menu *menu)
 467{
 468	int i;
 469
 470	if (*key == KEY_RESIZE) {
 471		setup_windows();
 472		return 1;
 473	}
 474
 475	for (i = 0; i < function_keys_num; i++) {
 476		if (*key == KEY_F(function_keys[i].key) ||
 477		    *key == '0' + function_keys[i].key){
 478			function_keys[i].handler(key, menu);
 479			return 1;
 480		}
 481	}
 482
 483	return 0;
 484}
 485
 486static void clean_items(void)
 487{
 488	int i;
 489	for (i = 0; curses_menu_items[i]; i++)
 490		free_item(curses_menu_items[i]);
 491	bzero(curses_menu_items, sizeof(curses_menu_items));
 492	bzero(k_menu_items, sizeof(k_menu_items));
 493	items_num = 0;
 494}
 495
 496typedef enum {MATCH_TINKER_PATTERN_UP, MATCH_TINKER_PATTERN_DOWN,
 497	FIND_NEXT_MATCH_DOWN, FIND_NEXT_MATCH_UP} match_f;
 498
 499/* return the index of the matched item, or -1 if no such item exists */
 500static int get_mext_match(const char *match_str, match_f flag)
 501{
 502	int match_start = item_index(current_item(curses_menu));
 503	int index;
 504
 505	if (flag == FIND_NEXT_MATCH_DOWN)
 506		++match_start;
 507	else if (flag == FIND_NEXT_MATCH_UP)
 508		--match_start;
 509
 510	index = match_start;
 511	index = (index + items_num) % items_num;
 512	while (true) {
 513		char *str = k_menu_items[index].str;
 514		if (strcasestr(str, match_str) != 0)
 515			return index;
 516		if (flag == FIND_NEXT_MATCH_UP ||
 517		    flag == MATCH_TINKER_PATTERN_UP)
 518			--index;
 519		else
 520			++index;
 521		index = (index + items_num) % items_num;
 522		if (index == match_start)
 523			return -1;
 524	}
 525}
 526
 527/* Make a new item. */
 528static void item_make(struct menu *menu, char tag, const char *fmt, ...)
 529{
 530	va_list ap;
 531
 532	if (items_num > MAX_MENU_ITEMS-1)
 533		return;
 534
 535	bzero(&k_menu_items[items_num], sizeof(k_menu_items[0]));
 536	k_menu_items[items_num].tag = tag;
 537	k_menu_items[items_num].usrptr = menu;
 538	if (menu != NULL)
 539		k_menu_items[items_num].is_visible =
 540			menu_is_visible(menu);
 541	else
 542		k_menu_items[items_num].is_visible = 1;
 543
 544	va_start(ap, fmt);
 545	vsnprintf(k_menu_items[items_num].str,
 546		  sizeof(k_menu_items[items_num].str),
 547		  fmt, ap);
 548	va_end(ap);
 549
 550	if (!k_menu_items[items_num].is_visible)
 551		memcpy(k_menu_items[items_num].str, "XXX", 3);
 552
 553	curses_menu_items[items_num] = new_item(
 554			k_menu_items[items_num].str,
 555			k_menu_items[items_num].str);
 556	set_item_userptr(curses_menu_items[items_num],
 557			&k_menu_items[items_num]);
 558	/*
 559	if (!k_menu_items[items_num].is_visible)
 560		item_opts_off(curses_menu_items[items_num], O_SELECTABLE);
 561	*/
 562
 563	items_num++;
 564	curses_menu_items[items_num] = NULL;
 565}
 566
 567/* very hackish. adds a string to the last item added */
 568static void item_add_str(const char *fmt, ...)
 569{
 570	va_list ap;
 571	int index = items_num-1;
 572	char new_str[256];
 573	char tmp_str[256];
 574
 575	if (index < 0)
 576		return;
 577
 578	va_start(ap, fmt);
 579	vsnprintf(new_str, sizeof(new_str), fmt, ap);
 580	va_end(ap);
 581	snprintf(tmp_str, sizeof(tmp_str), "%s%s",
 582			k_menu_items[index].str, new_str);
 583	strncpy(k_menu_items[index].str,
 584		tmp_str,
 585		sizeof(k_menu_items[index].str));
 586
 587	free_item(curses_menu_items[index]);
 588	curses_menu_items[index] = new_item(
 589			k_menu_items[index].str,
 590			k_menu_items[index].str);
 591	set_item_userptr(curses_menu_items[index],
 592			&k_menu_items[index]);
 593}
 594
 595/* get the tag of the currently selected item */
 596static char item_tag(void)
 597{
 598	ITEM *cur;
 599	struct mitem *mcur;
 600
 601	cur = current_item(curses_menu);
 602	if (cur == NULL)
 603		return 0;
 604	mcur = (struct mitem *) item_userptr(cur);
 605	return mcur->tag;
 606}
 607
 608static int curses_item_index(void)
 609{
 610	return  item_index(current_item(curses_menu));
 611}
 612
 613static void *item_data(void)
 614{
 615	ITEM *cur;
 616	struct mitem *mcur;
 617
 618	cur = current_item(curses_menu);
 619	if (!cur)
 620		return NULL;
 621	mcur = (struct mitem *) item_userptr(cur);
 622	return mcur->usrptr;
 623
 624}
 625
 626static int item_is_tag(char tag)
 627{
 628	return item_tag() == tag;
 629}
 630
 631static char filename[PATH_MAX+1];
 632static char menu_backtitle[PATH_MAX+128];
 633static const char *set_config_filename(const char *config_filename)
 634{
 635	int size;
 636
 637	size = snprintf(menu_backtitle, sizeof(menu_backtitle),
 638			"%s - %s", config_filename, rootmenu.prompt->text);
 639	if (size >= sizeof(menu_backtitle))
 640		menu_backtitle[sizeof(menu_backtitle)-1] = '\0';
 641
 642	size = snprintf(filename, sizeof(filename), "%s", config_filename);
 643	if (size >= sizeof(filename))
 644		filename[sizeof(filename)-1] = '\0';
 645	return menu_backtitle;
 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		break;
 681	default:
 682		btn_dialog(
 683			main_window,
 684			_("Your configuration changes were NOT saved."),
 685			1,
 686			"<OK>");
 687		break;
 688	}
 689	global_exit = 1;
 690	return 0;
 691}
 692
 693
 694static void search_conf(void)
 695{
 696	struct symbol **sym_arr;
 697	struct gstr res;
 698	char dialog_input_result[100];
 699	char *dialog_input;
 700	int dres;
 
 
 
 
 
 701again:
 702	dres = dialog_inputbox(main_window,
 703			_("Search Configuration Parameter"),
 704			_("Enter " CONFIG_ " (sub)string to search for "
 705				"(with or without \"" CONFIG_ "\")"),
 706			"", dialog_input_result, 99);
 707	switch (dres) {
 708	case 0:
 709		break;
 710	case 1:
 711		show_scroll_win(main_window,
 712				_("Search Configuration"), search_help);
 713		goto again;
 714	default:
 
 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);
 725	free(sym_arr);
 726	show_scroll_win(main_window,
 727			_("Search Results"), str_get(&res));
 728	str_free(&res);
 
 729}
 730
 731
 732static void build_conf(struct menu *menu)
 733{
 734	struct symbol *sym;
 735	struct property *prop;
 736	struct menu *child;
 737	int type, tmp, doint = 2;
 738	tristate val;
 739	char ch;
 740
 741	if (!menu || (!show_all_items && !menu_is_visible(menu)))
 742		return;
 743
 744	sym = menu->sym;
 745	prop = menu->prompt;
 746	if (!sym) {
 747		if (prop && menu != current_menu) {
 748			const char *prompt = menu_get_prompt(menu);
 749			enum prop_type ptype;
 750			ptype = menu->prompt ? menu->prompt->type : P_UNKNOWN;
 751			switch (ptype) {
 752			case P_MENU:
 753				child_count++;
 754				prompt = _(prompt);
 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  --->",
 763						indent + 1,
 764						' ', prompt);
 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_changable(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_changable(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_changable(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_changable(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_changable(sym)) ?
 904				"" : _(" (NEW)"));
 905		if (menu->prompt && menu->prompt->type == P_MENU) {
 906			item_add_str("  --->");
 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	(void) wattrset(main_window, attributes[NORMAL]);
 957	print_in_middle(stdscr, 1, 0, COLS,
 958			menu_backtitle,
 959			attributes[MAIN_HEADING]);
 960
 961	(void) wattrset(main_window, attributes[MAIN_MENU_BOX]);
 962	box(main_window, 0, 0);
 963	(void) wattrset(main_window, attributes[MAIN_MENU_HEADING]);
 964	mvwprintw(main_window, 0, 3, " %s ", prompt);
 965	(void) wattrset(main_window, attributes[NORMAL]);
 966
 967	set_menu_items(curses_menu, curses_menu_items);
 968
 969	/* position the menu at the middle of the screen */
 970	scale_menu(curses_menu, &maxy, &maxx);
 971	maxx = min(maxx, mwin_max_cols-2);
 972	maxy = mwin_max_lines;
 973	menu_window = derwin(main_window,
 974			maxy,
 975			maxx,
 976			2,
 977			(mwin_max_cols-maxx)/2);
 978	keypad(menu_window, TRUE);
 979	set_menu_win(curses_menu, menu_window);
 980	set_menu_sub(curses_menu, menu_window);
 981
 982	/* must reassert this after changing items, otherwise returns to a
 983	 * default of 16
 984	 */
 985	set_menu_format(curses_menu, maxy, 1);
 986	center_item(selected_index, last_top_row);
 987	set_menu_format(curses_menu, maxy, 1);
 988
 989	print_function_line();
 990
 991	/* Post the menu */
 992	post_menu(curses_menu);
 993	refresh_all_windows(main_window);
 994}
 995
 996static void adj_match_dir(match_f *match_direction)
 997{
 998	if (*match_direction == FIND_NEXT_MATCH_DOWN)
 999		*match_direction =
1000			MATCH_TINKER_PATTERN_DOWN;
1001	else if (*match_direction == FIND_NEXT_MATCH_UP)
1002		*match_direction =
1003			MATCH_TINKER_PATTERN_UP;
1004	/* else, do no change.. */
1005}
1006
1007struct match_state
1008{
1009	int in_search;
1010	match_f match_direction;
1011	char pattern[256];
1012};
1013
1014/* Return 0 means I have handled the key. In such a case, ans should hold the
1015 * item to center, or -1 otherwise.
1016 * Else return -1 .
1017 */
1018static int do_match(int key, struct match_state *state, int *ans)
1019{
1020	char c = (char) key;
1021	int terminate_search = 0;
1022	*ans = -1;
1023	if (key == '/' || (state->in_search && key == 27)) {
1024		move(0, 0);
1025		refresh();
1026		clrtoeol();
1027		state->in_search = 1-state->in_search;
1028		bzero(state->pattern, sizeof(state->pattern));
1029		state->match_direction = MATCH_TINKER_PATTERN_DOWN;
1030		return 0;
1031	} else if (!state->in_search)
1032		return 1;
1033
1034	if (isalnum(c) || isgraph(c) || c == ' ') {
1035		state->pattern[strlen(state->pattern)] = c;
1036		state->pattern[strlen(state->pattern)] = '\0';
1037		adj_match_dir(&state->match_direction);
1038		*ans = get_mext_match(state->pattern,
1039				state->match_direction);
1040	} else if (key == KEY_DOWN) {
1041		state->match_direction = FIND_NEXT_MATCH_DOWN;
1042		*ans = get_mext_match(state->pattern,
1043				state->match_direction);
1044	} else if (key == KEY_UP) {
1045		state->match_direction = FIND_NEXT_MATCH_UP;
1046		*ans = get_mext_match(state->pattern,
1047				state->match_direction);
1048	} else if (key == KEY_BACKSPACE || key == 127) {
1049		state->pattern[strlen(state->pattern)-1] = '\0';
1050		adj_match_dir(&state->match_direction);
1051	} else
1052		terminate_search = 1;
1053
1054	if (terminate_search) {
1055		state->in_search = 0;
1056		bzero(state->pattern, sizeof(state->pattern));
1057		move(0, 0);
1058		refresh();
1059		clrtoeol();
1060		return -1;
1061	}
1062	return 0;
1063}
1064
1065static void conf(struct menu *menu)
1066{
1067	struct menu *submenu = 0;
1068	const char *prompt = menu_get_prompt(menu);
1069	struct symbol *sym;
1070	int res;
1071	int current_index = 0;
1072	int last_top_row = 0;
1073	struct match_state match_state = {
1074		.in_search = 0,
1075		.match_direction = MATCH_TINKER_PATTERN_DOWN,
1076		.pattern = "",
1077	};
1078
1079	while (!global_exit) {
1080		reset_menu();
1081		current_menu = menu;
1082		build_conf(menu);
1083		if (!child_count)
1084			break;
1085
1086		show_menu(prompt ? _(prompt) : _("Main Menu"),
1087				_(menu_instructions),
1088				current_index, &last_top_row);
1089		keypad((menu_win(curses_menu)), TRUE);
1090		while (!global_exit) {
1091			if (match_state.in_search) {
1092				mvprintw(0, 0,
1093					"searching: %s", match_state.pattern);
1094				clrtoeol();
1095			}
1096			refresh_all_windows(main_window);
1097			res = wgetch(menu_win(curses_menu));
1098			if (!res)
1099				break;
1100			if (do_match(res, &match_state, &current_index) == 0) {
1101				if (current_index != -1)
1102					center_item(current_index,
1103						    &last_top_row);
1104				continue;
1105			}
1106			if (process_special_keys(&res,
1107						(struct menu *) item_data()))
1108				break;
1109			switch (res) {
1110			case KEY_DOWN:
1111				menu_driver(curses_menu, REQ_DOWN_ITEM);
1112				break;
1113			case KEY_UP:
1114				menu_driver(curses_menu, REQ_UP_ITEM);
1115				break;
1116			case KEY_NPAGE:
1117				menu_driver(curses_menu, REQ_SCR_DPAGE);
1118				break;
1119			case KEY_PPAGE:
1120				menu_driver(curses_menu, REQ_SCR_UPAGE);
1121				break;
1122			case KEY_HOME:
1123				menu_driver(curses_menu, REQ_FIRST_ITEM);
1124				break;
1125			case KEY_END:
1126				menu_driver(curses_menu, REQ_LAST_ITEM);
1127				break;
1128			case 'h':
1129			case '?':
1130				show_help((struct menu *) item_data());
1131				break;
1132			}
1133			if (res == 10 || res == 27 ||
1134				res == 32 || res == 'n' || res == 'y' ||
1135				res == KEY_LEFT || res == KEY_RIGHT ||
1136				res == 'm')
1137				break;
1138			refresh_all_windows(main_window);
1139		}
1140
1141		refresh_all_windows(main_window);
1142		/* if ESC or left*/
1143		if (res == 27 || (menu != &rootmenu && res == KEY_LEFT))
1144			break;
1145
1146		/* remember location in the menu */
1147		last_top_row = top_row(curses_menu);
1148		current_index = curses_item_index();
1149
1150		if (!item_tag())
1151			continue;
1152
1153		submenu = (struct menu *) item_data();
1154		if (!submenu || !menu_is_visible(submenu))
1155			continue;
1156		sym = submenu->sym;
1157
1158		switch (res) {
1159		case ' ':
1160			if (item_is_tag('t'))
1161				sym_toggle_tristate_value(sym);
1162			else if (item_is_tag('m'))
1163				conf(submenu);
1164			break;
1165		case KEY_RIGHT:
1166		case 10: /* ENTER WAS PRESSED */
1167			switch (item_tag()) {
1168			case 'm':
1169				if (single_menu_mode)
1170					submenu->data =
1171						(void *) (long) !submenu->data;
1172				else
1173					conf(submenu);
1174				break;
1175			case 't':
1176				if (sym_is_choice(sym) &&
1177				    sym_get_tristate_value(sym) == yes)
1178					conf_choice(submenu);
1179				else if (submenu->prompt &&
1180					 submenu->prompt->type == P_MENU)
1181					conf(submenu);
1182				else if (res == 10)
1183					sym_toggle_tristate_value(sym);
1184				break;
1185			case 's':
1186				conf_string(submenu);
1187				break;
1188			}
1189			break;
1190		case 'y':
1191			if (item_is_tag('t')) {
1192				if (sym_set_tristate_value(sym, yes))
1193					break;
1194				if (sym_set_tristate_value(sym, mod))
1195					btn_dialog(main_window, setmod_text, 0);
1196			}
1197			break;
1198		case 'n':
1199			if (item_is_tag('t'))
1200				sym_set_tristate_value(sym, no);
1201			break;
1202		case 'm':
1203			if (item_is_tag('t'))
1204				sym_set_tristate_value(sym, mod);
1205			break;
1206		}
1207	}
1208}
1209
1210static void conf_message_callback(const char *fmt, va_list ap)
1211{
1212	char buf[1024];
1213
1214	vsnprintf(buf, sizeof(buf), fmt, ap);
1215	btn_dialog(main_window, buf, 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 = 0;
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	char dialog_input_result[256];
1352
1353	while (1) {
1354		int res;
1355		const char *heading;
1356
1357		switch (sym_get_type(menu->sym)) {
1358		case S_INT:
1359			heading = _(inputbox_instructions_int);
1360			break;
1361		case S_HEX:
1362			heading = _(inputbox_instructions_hex);
1363			break;
1364		case S_STRING:
1365			heading = _(inputbox_instructions_string);
1366			break;
1367		default:
1368			heading = _("Internal nconf error!");
1369		}
1370		res = dialog_inputbox(main_window,
1371				prompt ? _(prompt) : _("Main Menu"),
1372				heading,
1373				sym_get_string_value(menu->sym),
1374				dialog_input_result,
1375				sizeof(dialog_input_result));
1376		switch (res) {
1377		case 0:
1378			if (sym_set_string_value(menu->sym,
1379						dialog_input_result))
1380				return;
1381			btn_dialog(main_window,
1382				_("You have made an invalid entry."), 0);
1383			break;
1384		case 1:
1385			show_help(menu);
1386			break;
1387		case KEY_EXIT:
1388			return;
1389		}
1390	}
1391}
1392
1393static void conf_load(void)
1394{
1395	char dialog_input_result[256];
1396	while (1) {
1397		int res;
1398		res = dialog_inputbox(main_window,
1399				NULL, load_config_text,
1400				filename,
1401				dialog_input_result,
1402				sizeof(dialog_input_result));
1403		switch (res) {
1404		case 0:
1405			if (!dialog_input_result[0])
1406				return;
1407			if (!conf_read(dialog_input_result)) {
1408				set_config_filename(dialog_input_result);
1409				sym_set_change_count(1);
1410				return;
1411			}
1412			btn_dialog(main_window, _("File does not exist!"), 0);
1413			break;
1414		case 1:
1415			show_scroll_win(main_window,
1416					_("Load Alternate Configuration"),
1417					load_config_help);
1418			break;
1419		case KEY_EXIT:
1420			return;
1421		}
1422	}
1423}
1424
1425static void conf_save(void)
1426{
1427	char dialog_input_result[256];
1428	while (1) {
1429		int res;
1430		res = dialog_inputbox(main_window,
1431				NULL, save_config_text,
1432				filename,
1433				dialog_input_result,
1434				sizeof(dialog_input_result));
1435		switch (res) {
1436		case 0:
1437			if (!dialog_input_result[0])
1438				return;
1439			res = conf_write(dialog_input_result);
1440			if (!res) {
1441				set_config_filename(dialog_input_result);
1442				return;
1443			}
1444			btn_dialog(main_window, _("Can't create file! "
1445				"Probably a nonexistent directory."),
1446				1, "<OK>");
1447			break;
1448		case 1:
1449			show_scroll_win(main_window,
1450				_("Save Alternate Configuration"),
1451				save_config_help);
1452			break;
1453		case KEY_EXIT:
1454			return;
1455		}
1456	}
1457}
1458
1459void setup_windows(void)
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, COLS-2, 2, 1);
1466	keypad(main_window, TRUE);
1467	mwin_max_lines = LINES-7;
1468	mwin_max_cols = COLS-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	char *mode;
1477
1478	setlocale(LC_ALL, "");
1479	bindtextdomain(PACKAGE, LOCALEDIR);
1480	textdomain(PACKAGE);
1481
 
 
 
 
 
1482	conf_parse(av[1]);
1483	conf_read(NULL);
1484
1485	mode = getenv("NCONFIG_MODE");
1486	if (mode) {
1487		if (!strcasecmp(mode, "single_menu"))
1488			single_menu_mode = 1;
1489	}
1490
1491	/* Initialize curses */
1492	initscr();
1493	/* set color theme */
1494	set_colors();
1495
1496	cbreak();
1497	noecho();
1498	keypad(stdscr, TRUE);
1499	curs_set(0);
1500
1501	if (COLS < 75 || LINES < 20) {
 
1502		endwin();
1503		printf("Your terminal should have at "
1504			"least 20 lines and 75 columns\n");
1505		return 1;
1506	}
1507
1508	notimeout(stdscr, FALSE);
 
 
 
1509	ESCDELAY = 1;
 
1510
1511	/* set btns menu */
1512	curses_menu = new_menu(curses_menu_items);
1513	menu_opts_off(curses_menu, O_SHOWDESC);
1514	menu_opts_on(curses_menu, O_SHOWMATCH);
1515	menu_opts_on(curses_menu, O_ONEVALUE);
1516	menu_opts_on(curses_menu, O_NONCYCLIC);
1517	menu_opts_on(curses_menu, O_IGNORECASE);
1518	set_menu_mark(curses_menu, " ");
1519	set_menu_fore(curses_menu, attributes[MAIN_MENU_FORE]);
1520	set_menu_back(curses_menu, attributes[MAIN_MENU_BACK]);
1521	set_menu_grey(curses_menu, attributes[MAIN_MENU_GREY]);
1522
1523	set_config_filename(conf_get_configname());
1524	setup_windows();
1525
1526	/* check for KEY_FUNC(1) */
1527	if (has_key(KEY_F(1)) == FALSE) {
1528		show_scroll_win(main_window,
1529				_("Instructions"),
1530				_(menu_no_f_instructions));
1531	}
1532
1533	conf_set_message_callback(conf_message_callback);
1534	/* do the work */
1535	while (!global_exit) {
1536		conf(&rootmenu);
1537		if (!global_exit && do_exit() == 0)
1538			break;
1539	}
1540	/* ok, we are done */
1541	unpost_menu(curses_menu);
1542	free_menu(curses_menu);
1543	delwin(main_window);
1544	clear();
1545	refresh();
1546	endwin();
1547	return 0;
1548}
1549