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