File Index
Files and directories
Sign in
FILE

nconf.c

File preview with syntax highlighting.

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