Linux Audio

Check our new training course

Linux BSP upgrade and security maintenance

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