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