xref: /linux/scripts/kconfig/nconf.c (revision 0d456bad36d42d16022be045c8a53ddbb59ee478)
1 /*
2  * Copyright (C) 2008 Nir Tzachar <nir.tzachar@gmail.com?
3  * Released under the terms of the GNU GPL v2.0.
4  *
5  * Derived from menuconfig.
6  *
7  */
8 #define _GNU_SOURCE
9 #include <string.h>
10 
11 #include "lkc.h"
12 #include "nconf.h"
13 #include <ctype.h>
14 
15 static 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 <u>, <d> and <SPACE BAR> for\n"
87 "   those who are familiar with less and lynx.\n"
88 "\n"
89 "o  Press <Enter>, <F1>, <F5>, <F9>, <q> 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"),
142 menu_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"),
155 menu_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"),
166 radiolist_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"),
171 inputbox_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."),
175 inputbox_instructions_hex[] = N_(
176 "Please enter a hexadecimal value.\n"
177 "Press <RETURN> to accept, <ESC> to cancel."),
178 inputbox_instructions_string[] = N_(
179 "Please enter a string value.\n"
180 "Press <RETURN> to accept, <ESC> to cancel."),
181 setmod_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."),
185 load_config_text[] = N_(
186 "Enter the name of the configuration file you wish to load.\n"
187 "Accept the name shown to restore the configuration you\n"
188 "last retrieved.  Leave blank to abort."),
189 load_config_help[] = N_(
190 "\n"
191 "For various reasons, one may wish to keep several different\n"
192 "configurations available on a single machine.\n"
193 "\n"
194 "If you have saved a previous configuration in a file other than the\n"
195 "default one, entering its name here will allow you to modify that\n"
196 "configuration.\n"
197 "\n"
198 "If you are uncertain, then you have probably never used alternate\n"
199 "configuration files.  You should therefor leave this blank to abort.\n"),
200 save_config_text[] = N_(
201 "Enter a filename to which this configuration should be saved\n"
202 "as an alternate.  Leave blank to abort."),
203 save_config_help[] = N_(
204 "\n"
205 "For various reasons, one may wish to keep different configurations\n"
206 "available on a single machine.\n"
207 "\n"
208 "Entering a file name here will allow you to later retrieve, modify\n"
209 "and use the current configuration as an alternate to whatever\n"
210 "configuration options you have selected at that time.\n"
211 "\n"
212 "If you are uncertain what all this means then you should probably\n"
213 "leave this blank.\n"),
214 search_help[] = N_(
215 "\n"
216 "Search for symbols and display their relations. Regular expressions\n"
217 "are allowed.\n"
218 "Example: search for \"^FOO\"\n"
219 "Result:\n"
220 "-----------------------------------------------------------------\n"
221 "Symbol: FOO [ = m]\n"
222 "Prompt: Foo bus is used to drive the bar HW\n"
223 "Defined at drivers/pci/Kconfig:47\n"
224 "Depends on: X86_LOCAL_APIC && X86_IO_APIC || IA64\n"
225 "Location:\n"
226 "  -> Bus options (PCI, PCMCIA, EISA, ISA)\n"
227 "    -> PCI support (PCI [ = y])\n"
228 "      -> PCI access mode (<choice> [ = y])\n"
229 "Selects: LIBCRC32\n"
230 "Selected by: BAR\n"
231 "-----------------------------------------------------------------\n"
232 "o The line 'Prompt:' shows the text used in the menu structure for\n"
233 "  this symbol\n"
234 "o The 'Defined at' line tell at what file / line number the symbol\n"
235 "  is defined\n"
236 "o The 'Depends on:' line tell what symbols needs to be defined for\n"
237 "  this symbol to be visible in the menu (selectable)\n"
238 "o The 'Location:' lines tell where in the menu structure this symbol\n"
239 "  is located\n"
240 "    A location followed by a [ = y] indicate that this is a selectable\n"
241 "    menu item - and current value is displayed inside brackets.\n"
242 "o The 'Selects:' line tell what symbol will be automatically\n"
243 "  selected if this symbol is selected (y or m)\n"
244 "o The 'Selected by' line tell what symbol has selected this symbol\n"
245 "\n"
246 "Only relevant lines are shown.\n"
247 "\n\n"
248 "Search examples:\n"
249 "Examples: USB  => find all symbols containing USB\n"
250 "          ^USB => find all symbols starting with USB\n"
251 "          USB$ => find all symbols ending with USB\n"
252 "\n");
253 
254 struct mitem {
255 	char str[256];
256 	char tag;
257 	void *usrptr;
258 	int is_visible;
259 };
260 
261 #define MAX_MENU_ITEMS 4096
262 static int show_all_items;
263 static int indent;
264 static struct menu *current_menu;
265 static int child_count;
266 static int single_menu_mode;
267 /* the window in which all information appears */
268 static WINDOW *main_window;
269 /* the largest size of the menu window */
270 static int mwin_max_lines;
271 static int mwin_max_cols;
272 /* the window in which we show option buttons */
273 static MENU *curses_menu;
274 static ITEM *curses_menu_items[MAX_MENU_ITEMS];
275 static struct mitem k_menu_items[MAX_MENU_ITEMS];
276 static int items_num;
277 static int global_exit;
278 /* the currently selected button */
279 const char *current_instructions = menu_instructions;
280 
281 static char *dialog_input_result;
282 static int dialog_input_result_len;
283 
284 static void conf(struct menu *menu);
285 static void conf_choice(struct menu *menu);
286 static void conf_string(struct menu *menu);
287 static void conf_load(void);
288 static void conf_save(void);
289 static void show_help(struct menu *menu);
290 static int do_exit(void);
291 static void setup_windows(void);
292 static void search_conf(void);
293 
294 typedef void (*function_key_handler_t)(int *key, struct menu *menu);
295 static void handle_f1(int *key, struct menu *current_item);
296 static void handle_f2(int *key, struct menu *current_item);
297 static void handle_f3(int *key, struct menu *current_item);
298 static void handle_f4(int *key, struct menu *current_item);
299 static void handle_f5(int *key, struct menu *current_item);
300 static void handle_f6(int *key, struct menu *current_item);
301 static void handle_f7(int *key, struct menu *current_item);
302 static void handle_f8(int *key, struct menu *current_item);
303 static void handle_f9(int *key, struct menu *current_item);
304 
305 struct function_keys {
306 	const char *key_str;
307 	const char *func;
308 	function_key key;
309 	function_key_handler_t handler;
310 };
311 
312 static const int function_keys_num = 9;
313 struct function_keys function_keys[] = {
314 	{
315 		.key_str = "F1",
316 		.func = "Help",
317 		.key = F_HELP,
318 		.handler = handle_f1,
319 	},
320 	{
321 		.key_str = "F2",
322 		.func = "Sym Info",
323 		.key = F_SYMBOL,
324 		.handler = handle_f2,
325 	},
326 	{
327 		.key_str = "F3",
328 		.func = "Insts",
329 		.key = F_INSTS,
330 		.handler = handle_f3,
331 	},
332 	{
333 		.key_str = "F4",
334 		.func = "Config",
335 		.key = F_CONF,
336 		.handler = handle_f4,
337 	},
338 	{
339 		.key_str = "F5",
340 		.func = "Back",
341 		.key = F_BACK,
342 		.handler = handle_f5,
343 	},
344 	{
345 		.key_str = "F6",
346 		.func = "Save",
347 		.key = F_SAVE,
348 		.handler = handle_f6,
349 	},
350 	{
351 		.key_str = "F7",
352 		.func = "Load",
353 		.key = F_LOAD,
354 		.handler = handle_f7,
355 	},
356 	{
357 		.key_str = "F8",
358 		.func = "Sym Search",
359 		.key = F_SEARCH,
360 		.handler = handle_f8,
361 	},
362 	{
363 		.key_str = "F9",
364 		.func = "Exit",
365 		.key = F_EXIT,
366 		.handler = handle_f9,
367 	},
368 };
369 
370 static void print_function_line(void)
371 {
372 	int i;
373 	int offset = 1;
374 	const int skip = 1;
375 
376 	for (i = 0; i < function_keys_num; i++) {
377 		(void) wattrset(main_window, attributes[FUNCTION_HIGHLIGHT]);
378 		mvwprintw(main_window, LINES-3, offset,
379 				"%s",
380 				function_keys[i].key_str);
381 		(void) wattrset(main_window, attributes[FUNCTION_TEXT]);
382 		offset += strlen(function_keys[i].key_str);
383 		mvwprintw(main_window, LINES-3,
384 				offset, "%s",
385 				function_keys[i].func);
386 		offset += strlen(function_keys[i].func) + skip;
387 	}
388 	(void) wattrset(main_window, attributes[NORMAL]);
389 }
390 
391 /* help */
392 static void handle_f1(int *key, struct menu *current_item)
393 {
394 	show_scroll_win(main_window,
395 			_("README"), _(nconf_readme));
396 	return;
397 }
398 
399 /* symbole help */
400 static void handle_f2(int *key, struct menu *current_item)
401 {
402 	show_help(current_item);
403 	return;
404 }
405 
406 /* instructions */
407 static void handle_f3(int *key, struct menu *current_item)
408 {
409 	show_scroll_win(main_window,
410 			_("Instructions"),
411 			_(current_instructions));
412 	return;
413 }
414 
415 /* config */
416 static void handle_f4(int *key, struct menu *current_item)
417 {
418 	int res = btn_dialog(main_window,
419 			_("Show all symbols?"),
420 			2,
421 			"   <Show All>   ",
422 			"<Don't show all>");
423 	if (res == 0)
424 		show_all_items = 1;
425 	else if (res == 1)
426 		show_all_items = 0;
427 
428 	return;
429 }
430 
431 /* back */
432 static void handle_f5(int *key, struct menu *current_item)
433 {
434 	*key = KEY_LEFT;
435 	return;
436 }
437 
438 /* save */
439 static void handle_f6(int *key, struct menu *current_item)
440 {
441 	conf_save();
442 	return;
443 }
444 
445 /* load */
446 static void handle_f7(int *key, struct menu *current_item)
447 {
448 	conf_load();
449 	return;
450 }
451 
452 /* search */
453 static void handle_f8(int *key, struct menu *current_item)
454 {
455 	search_conf();
456 	return;
457 }
458 
459 /* exit */
460 static void handle_f9(int *key, struct menu *current_item)
461 {
462 	do_exit();
463 	return;
464 }
465 
466 /* return != 0 to indicate the key was handles */
467 static int process_special_keys(int *key, struct menu *menu)
468 {
469 	int i;
470 
471 	if (*key == KEY_RESIZE) {
472 		setup_windows();
473 		return 1;
474 	}
475 
476 	for (i = 0; i < function_keys_num; i++) {
477 		if (*key == KEY_F(function_keys[i].key) ||
478 		    *key == '0' + function_keys[i].key){
479 			function_keys[i].handler(key, menu);
480 			return 1;
481 		}
482 	}
483 
484 	return 0;
485 }
486 
487 static void clean_items(void)
488 {
489 	int i;
490 	for (i = 0; curses_menu_items[i]; i++)
491 		free_item(curses_menu_items[i]);
492 	bzero(curses_menu_items, sizeof(curses_menu_items));
493 	bzero(k_menu_items, sizeof(k_menu_items));
494 	items_num = 0;
495 }
496 
497 typedef enum {MATCH_TINKER_PATTERN_UP, MATCH_TINKER_PATTERN_DOWN,
498 	FIND_NEXT_MATCH_DOWN, FIND_NEXT_MATCH_UP} match_f;
499 
500 /* return the index of the matched item, or -1 if no such item exists */
501 static int get_mext_match(const char *match_str, match_f flag)
502 {
503 	int match_start = item_index(current_item(curses_menu));
504 	int index;
505 
506 	if (flag == FIND_NEXT_MATCH_DOWN)
507 		++match_start;
508 	else if (flag == FIND_NEXT_MATCH_UP)
509 		--match_start;
510 
511 	index = match_start;
512 	index = (index + items_num) % items_num;
513 	while (true) {
514 		char *str = k_menu_items[index].str;
515 		if (strcasestr(str, match_str) != 0)
516 			return index;
517 		if (flag == FIND_NEXT_MATCH_UP ||
518 		    flag == MATCH_TINKER_PATTERN_UP)
519 			--index;
520 		else
521 			++index;
522 		index = (index + items_num) % items_num;
523 		if (index == match_start)
524 			return -1;
525 	}
526 }
527 
528 /* Make a new item. */
529 static void item_make(struct menu *menu, char tag, const char *fmt, ...)
530 {
531 	va_list ap;
532 
533 	if (items_num > MAX_MENU_ITEMS-1)
534 		return;
535 
536 	bzero(&k_menu_items[items_num], sizeof(k_menu_items[0]));
537 	k_menu_items[items_num].tag = tag;
538 	k_menu_items[items_num].usrptr = menu;
539 	if (menu != NULL)
540 		k_menu_items[items_num].is_visible =
541 			menu_is_visible(menu);
542 	else
543 		k_menu_items[items_num].is_visible = 1;
544 
545 	va_start(ap, fmt);
546 	vsnprintf(k_menu_items[items_num].str,
547 		  sizeof(k_menu_items[items_num].str),
548 		  fmt, ap);
549 	va_end(ap);
550 
551 	if (!k_menu_items[items_num].is_visible)
552 		memcpy(k_menu_items[items_num].str, "XXX", 3);
553 
554 	curses_menu_items[items_num] = new_item(
555 			k_menu_items[items_num].str,
556 			k_menu_items[items_num].str);
557 	set_item_userptr(curses_menu_items[items_num],
558 			&k_menu_items[items_num]);
559 	/*
560 	if (!k_menu_items[items_num].is_visible)
561 		item_opts_off(curses_menu_items[items_num], O_SELECTABLE);
562 	*/
563 
564 	items_num++;
565 	curses_menu_items[items_num] = NULL;
566 }
567 
568 /* very hackish. adds a string to the last item added */
569 static void item_add_str(const char *fmt, ...)
570 {
571 	va_list ap;
572 	int index = items_num-1;
573 	char new_str[256];
574 	char tmp_str[256];
575 
576 	if (index < 0)
577 		return;
578 
579 	va_start(ap, fmt);
580 	vsnprintf(new_str, sizeof(new_str), fmt, ap);
581 	va_end(ap);
582 	snprintf(tmp_str, sizeof(tmp_str), "%s%s",
583 			k_menu_items[index].str, new_str);
584 	strncpy(k_menu_items[index].str,
585 		tmp_str,
586 		sizeof(k_menu_items[index].str));
587 
588 	free_item(curses_menu_items[index]);
589 	curses_menu_items[index] = new_item(
590 			k_menu_items[index].str,
591 			k_menu_items[index].str);
592 	set_item_userptr(curses_menu_items[index],
593 			&k_menu_items[index]);
594 }
595 
596 /* get the tag of the currently selected item */
597 static char item_tag(void)
598 {
599 	ITEM *cur;
600 	struct mitem *mcur;
601 
602 	cur = current_item(curses_menu);
603 	if (cur == NULL)
604 		return 0;
605 	mcur = (struct mitem *) item_userptr(cur);
606 	return mcur->tag;
607 }
608 
609 static int curses_item_index(void)
610 {
611 	return  item_index(current_item(curses_menu));
612 }
613 
614 static void *item_data(void)
615 {
616 	ITEM *cur;
617 	struct mitem *mcur;
618 
619 	cur = current_item(curses_menu);
620 	if (!cur)
621 		return NULL;
622 	mcur = (struct mitem *) item_userptr(cur);
623 	return mcur->usrptr;
624 
625 }
626 
627 static int item_is_tag(char tag)
628 {
629 	return item_tag() == tag;
630 }
631 
632 static char filename[PATH_MAX+1];
633 static char menu_backtitle[PATH_MAX+128];
634 static const char *set_config_filename(const char *config_filename)
635 {
636 	int size;
637 
638 	size = snprintf(menu_backtitle, sizeof(menu_backtitle),
639 			"%s - %s", config_filename, rootmenu.prompt->text);
640 	if (size >= sizeof(menu_backtitle))
641 		menu_backtitle[sizeof(menu_backtitle)-1] = '\0';
642 
643 	size = snprintf(filename, sizeof(filename), "%s", config_filename);
644 	if (size >= sizeof(filename))
645 		filename[sizeof(filename)-1] = '\0';
646 	return menu_backtitle;
647 }
648 
649 /* return = 0 means we are successful.
650  * -1 means go on doing what you were doing
651  */
652 static int do_exit(void)
653 {
654 	int res;
655 	if (!conf_get_changed()) {
656 		global_exit = 1;
657 		return 0;
658 	}
659 	res = btn_dialog(main_window,
660 			_("Do you wish to save your new configuration?\n"
661 				"<ESC> to cancel and resume nconfig."),
662 			2,
663 			"   <save>   ",
664 			"<don't save>");
665 	if (res == KEY_EXIT) {
666 		global_exit = 0;
667 		return -1;
668 	}
669 
670 	/* if we got here, the user really wants to exit */
671 	switch (res) {
672 	case 0:
673 		res = conf_write(filename);
674 		if (res)
675 			btn_dialog(
676 				main_window,
677 				_("Error during writing of configuration.\n"
678 				  "Your configuration changes were NOT saved."),
679 				  1,
680 				  "<OK>");
681 		break;
682 	default:
683 		btn_dialog(
684 			main_window,
685 			_("Your configuration changes were NOT saved."),
686 			1,
687 			"<OK>");
688 		break;
689 	}
690 	global_exit = 1;
691 	return 0;
692 }
693 
694 
695 static void search_conf(void)
696 {
697 	struct symbol **sym_arr;
698 	struct gstr res;
699 	char *dialog_input;
700 	int dres;
701 again:
702 	dres = dialog_inputbox(main_window,
703 			_("Search Configuration Parameter"),
704 			_("Enter " CONFIG_ " (sub)string to search for "
705 				"(with or without \"" CONFIG_ "\")"),
706 			"", &dialog_input_result, &dialog_input_result_len);
707 	switch (dres) {
708 	case 0:
709 		break;
710 	case 1:
711 		show_scroll_win(main_window,
712 				_("Search Configuration"), search_help);
713 		goto again;
714 	default:
715 		return;
716 	}
717 
718 	/* strip the prefix if necessary */
719 	dialog_input = dialog_input_result;
720 	if (strncasecmp(dialog_input_result, CONFIG_, strlen(CONFIG_)) == 0)
721 		dialog_input += strlen(CONFIG_);
722 
723 	sym_arr = sym_re_search(dialog_input);
724 	res = get_relations_str(sym_arr, NULL);
725 	free(sym_arr);
726 	show_scroll_win(main_window,
727 			_("Search Results"), str_get(&res));
728 	str_free(&res);
729 }
730 
731 
732 static 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 
911 conf_childs:
912 	indent += doint;
913 	for (child = menu->list; child; child = child->next)
914 		build_conf(child);
915 	indent -= doint;
916 }
917 
918 static 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*/
926 static 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 */
947 static 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 
996 static 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 
1007 struct 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  */
1018 static 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 
1065 static void conf(struct menu *menu)
1066 {
1067 	struct menu *submenu = 0;
1068 	const char *prompt = menu_get_prompt(menu);
1069 	struct symbol *sym;
1070 	int res;
1071 	int current_index = 0;
1072 	int last_top_row = 0;
1073 	struct match_state match_state = {
1074 		.in_search = 0,
1075 		.match_direction = MATCH_TINKER_PATTERN_DOWN,
1076 		.pattern = "",
1077 	};
1078 
1079 	while (!global_exit) {
1080 		reset_menu();
1081 		current_menu = menu;
1082 		build_conf(menu);
1083 		if (!child_count)
1084 			break;
1085 
1086 		show_menu(prompt ? _(prompt) : _("Main Menu"),
1087 				_(menu_instructions),
1088 				current_index, &last_top_row);
1089 		keypad((menu_win(curses_menu)), TRUE);
1090 		while (!global_exit) {
1091 			if (match_state.in_search) {
1092 				mvprintw(0, 0,
1093 					"searching: %s", match_state.pattern);
1094 				clrtoeol();
1095 			}
1096 			refresh_all_windows(main_window);
1097 			res = wgetch(menu_win(curses_menu));
1098 			if (!res)
1099 				break;
1100 			if (do_match(res, &match_state, &current_index) == 0) {
1101 				if (current_index != -1)
1102 					center_item(current_index,
1103 						    &last_top_row);
1104 				continue;
1105 			}
1106 			if (process_special_keys(&res,
1107 						(struct menu *) item_data()))
1108 				break;
1109 			switch (res) {
1110 			case KEY_DOWN:
1111 				menu_driver(curses_menu, REQ_DOWN_ITEM);
1112 				break;
1113 			case KEY_UP:
1114 				menu_driver(curses_menu, REQ_UP_ITEM);
1115 				break;
1116 			case KEY_NPAGE:
1117 				menu_driver(curses_menu, REQ_SCR_DPAGE);
1118 				break;
1119 			case KEY_PPAGE:
1120 				menu_driver(curses_menu, REQ_SCR_UPAGE);
1121 				break;
1122 			case KEY_HOME:
1123 				menu_driver(curses_menu, REQ_FIRST_ITEM);
1124 				break;
1125 			case KEY_END:
1126 				menu_driver(curses_menu, REQ_LAST_ITEM);
1127 				break;
1128 			case 'h':
1129 			case '?':
1130 				show_help((struct menu *) item_data());
1131 				break;
1132 			}
1133 			if (res == 10 || res == 27 ||
1134 				res == 32 || res == 'n' || res == 'y' ||
1135 				res == KEY_LEFT || res == KEY_RIGHT ||
1136 				res == 'm')
1137 				break;
1138 			refresh_all_windows(main_window);
1139 		}
1140 
1141 		refresh_all_windows(main_window);
1142 		/* if ESC or left*/
1143 		if (res == 27 || (menu != &rootmenu && res == KEY_LEFT))
1144 			break;
1145 
1146 		/* remember location in the menu */
1147 		last_top_row = top_row(curses_menu);
1148 		current_index = curses_item_index();
1149 
1150 		if (!item_tag())
1151 			continue;
1152 
1153 		submenu = (struct menu *) item_data();
1154 		if (!submenu || !menu_is_visible(submenu))
1155 			continue;
1156 		sym = submenu->sym;
1157 
1158 		switch (res) {
1159 		case ' ':
1160 			if (item_is_tag('t'))
1161 				sym_toggle_tristate_value(sym);
1162 			else if (item_is_tag('m'))
1163 				conf(submenu);
1164 			break;
1165 		case KEY_RIGHT:
1166 		case 10: /* ENTER WAS PRESSED */
1167 			switch (item_tag()) {
1168 			case 'm':
1169 				if (single_menu_mode)
1170 					submenu->data =
1171 						(void *) (long) !submenu->data;
1172 				else
1173 					conf(submenu);
1174 				break;
1175 			case 't':
1176 				if (sym_is_choice(sym) &&
1177 				    sym_get_tristate_value(sym) == yes)
1178 					conf_choice(submenu);
1179 				else if (submenu->prompt &&
1180 					 submenu->prompt->type == P_MENU)
1181 					conf(submenu);
1182 				else if (res == 10)
1183 					sym_toggle_tristate_value(sym);
1184 				break;
1185 			case 's':
1186 				conf_string(submenu);
1187 				break;
1188 			}
1189 			break;
1190 		case 'y':
1191 			if (item_is_tag('t')) {
1192 				if (sym_set_tristate_value(sym, yes))
1193 					break;
1194 				if (sym_set_tristate_value(sym, mod))
1195 					btn_dialog(main_window, setmod_text, 0);
1196 			}
1197 			break;
1198 		case 'n':
1199 			if (item_is_tag('t'))
1200 				sym_set_tristate_value(sym, no);
1201 			break;
1202 		case 'm':
1203 			if (item_is_tag('t'))
1204 				sym_set_tristate_value(sym, mod);
1205 			break;
1206 		}
1207 	}
1208 }
1209 
1210 static void conf_message_callback(const char *fmt, va_list ap)
1211 {
1212 	char buf[1024];
1213 
1214 	vsnprintf(buf, sizeof(buf), fmt, ap);
1215 	btn_dialog(main_window, buf, 1, "<OK>");
1216 }
1217 
1218 static void show_help(struct menu *menu)
1219 {
1220 	struct gstr help;
1221 
1222 	if (!menu)
1223 		return;
1224 
1225 	help = str_new();
1226 	menu_get_ext_help(menu, &help);
1227 	show_scroll_win(main_window, _(menu_get_prompt(menu)), str_get(&help));
1228 	str_free(&help);
1229 }
1230 
1231 static void conf_choice(struct menu *menu)
1232 {
1233 	const char *prompt = _(menu_get_prompt(menu));
1234 	struct menu *child = 0;
1235 	struct symbol *active;
1236 	int selected_index = 0;
1237 	int last_top_row = 0;
1238 	int res, i = 0;
1239 	struct match_state match_state = {
1240 		.in_search = 0,
1241 		.match_direction = MATCH_TINKER_PATTERN_DOWN,
1242 		.pattern = "",
1243 	};
1244 
1245 	active = sym_get_choice_value(menu->sym);
1246 	/* this is mostly duplicated from the conf() function. */
1247 	while (!global_exit) {
1248 		reset_menu();
1249 
1250 		for (i = 0, child = menu->list; child; child = child->next) {
1251 			if (!show_all_items && !menu_is_visible(child))
1252 				continue;
1253 
1254 			if (child->sym == sym_get_choice_value(menu->sym))
1255 				item_make(child, ':', "<X> %s",
1256 						_(menu_get_prompt(child)));
1257 			else if (child->sym)
1258 				item_make(child, ':', "    %s",
1259 						_(menu_get_prompt(child)));
1260 			else
1261 				item_make(child, ':', "*** %s ***",
1262 						_(menu_get_prompt(child)));
1263 
1264 			if (child->sym == active){
1265 				last_top_row = top_row(curses_menu);
1266 				selected_index = i;
1267 			}
1268 			i++;
1269 		}
1270 		show_menu(prompt ? _(prompt) : _("Choice Menu"),
1271 				_(radiolist_instructions),
1272 				selected_index,
1273 				&last_top_row);
1274 		while (!global_exit) {
1275 			if (match_state.in_search) {
1276 				mvprintw(0, 0, "searching: %s",
1277 					 match_state.pattern);
1278 				clrtoeol();
1279 			}
1280 			refresh_all_windows(main_window);
1281 			res = wgetch(menu_win(curses_menu));
1282 			if (!res)
1283 				break;
1284 			if (do_match(res, &match_state, &selected_index) == 0) {
1285 				if (selected_index != -1)
1286 					center_item(selected_index,
1287 						    &last_top_row);
1288 				continue;
1289 			}
1290 			if (process_special_keys(
1291 						&res,
1292 						(struct menu *) item_data()))
1293 				break;
1294 			switch (res) {
1295 			case KEY_DOWN:
1296 				menu_driver(curses_menu, REQ_DOWN_ITEM);
1297 				break;
1298 			case KEY_UP:
1299 				menu_driver(curses_menu, REQ_UP_ITEM);
1300 				break;
1301 			case KEY_NPAGE:
1302 				menu_driver(curses_menu, REQ_SCR_DPAGE);
1303 				break;
1304 			case KEY_PPAGE:
1305 				menu_driver(curses_menu, REQ_SCR_UPAGE);
1306 				break;
1307 			case KEY_HOME:
1308 				menu_driver(curses_menu, REQ_FIRST_ITEM);
1309 				break;
1310 			case KEY_END:
1311 				menu_driver(curses_menu, REQ_LAST_ITEM);
1312 				break;
1313 			case 'h':
1314 			case '?':
1315 				show_help((struct menu *) item_data());
1316 				break;
1317 			}
1318 			if (res == 10 || res == 27 || res == ' ' ||
1319 					res == KEY_LEFT){
1320 				break;
1321 			}
1322 			refresh_all_windows(main_window);
1323 		}
1324 		/* if ESC or left */
1325 		if (res == 27 || res == KEY_LEFT)
1326 			break;
1327 
1328 		child = item_data();
1329 		if (!child || !menu_is_visible(child) || !child->sym)
1330 			continue;
1331 		switch (res) {
1332 		case ' ':
1333 		case  10:
1334 		case KEY_RIGHT:
1335 			sym_set_tristate_value(child->sym, yes);
1336 			return;
1337 		case 'h':
1338 		case '?':
1339 			show_help(child);
1340 			active = child->sym;
1341 			break;
1342 		case KEY_EXIT:
1343 			return;
1344 		}
1345 	}
1346 }
1347 
1348 static void conf_string(struct menu *menu)
1349 {
1350 	const char *prompt = menu_get_prompt(menu);
1351 
1352 	while (1) {
1353 		int res;
1354 		const char *heading;
1355 
1356 		switch (sym_get_type(menu->sym)) {
1357 		case S_INT:
1358 			heading = _(inputbox_instructions_int);
1359 			break;
1360 		case S_HEX:
1361 			heading = _(inputbox_instructions_hex);
1362 			break;
1363 		case S_STRING:
1364 			heading = _(inputbox_instructions_string);
1365 			break;
1366 		default:
1367 			heading = _("Internal nconf error!");
1368 		}
1369 		res = dialog_inputbox(main_window,
1370 				prompt ? _(prompt) : _("Main Menu"),
1371 				heading,
1372 				sym_get_string_value(menu->sym),
1373 				&dialog_input_result,
1374 				&dialog_input_result_len);
1375 		switch (res) {
1376 		case 0:
1377 			if (sym_set_string_value(menu->sym,
1378 						dialog_input_result))
1379 				return;
1380 			btn_dialog(main_window,
1381 				_("You have made an invalid entry."), 0);
1382 			break;
1383 		case 1:
1384 			show_help(menu);
1385 			break;
1386 		case KEY_EXIT:
1387 			return;
1388 		}
1389 	}
1390 }
1391 
1392 static void conf_load(void)
1393 {
1394 	while (1) {
1395 		int res;
1396 		res = dialog_inputbox(main_window,
1397 				NULL, load_config_text,
1398 				filename,
1399 				&dialog_input_result,
1400 				&dialog_input_result_len);
1401 		switch (res) {
1402 		case 0:
1403 			if (!dialog_input_result[0])
1404 				return;
1405 			if (!conf_read(dialog_input_result)) {
1406 				set_config_filename(dialog_input_result);
1407 				sym_set_change_count(1);
1408 				return;
1409 			}
1410 			btn_dialog(main_window, _("File does not exist!"), 0);
1411 			break;
1412 		case 1:
1413 			show_scroll_win(main_window,
1414 					_("Load Alternate Configuration"),
1415 					load_config_help);
1416 			break;
1417 		case KEY_EXIT:
1418 			return;
1419 		}
1420 	}
1421 }
1422 
1423 static void conf_save(void)
1424 {
1425 	while (1) {
1426 		int res;
1427 		res = dialog_inputbox(main_window,
1428 				NULL, save_config_text,
1429 				filename,
1430 				&dialog_input_result,
1431 				&dialog_input_result_len);
1432 		switch (res) {
1433 		case 0:
1434 			if (!dialog_input_result[0])
1435 				return;
1436 			res = conf_write(dialog_input_result);
1437 			if (!res) {
1438 				set_config_filename(dialog_input_result);
1439 				return;
1440 			}
1441 			btn_dialog(main_window, _("Can't create file! "
1442 				"Probably a nonexistent directory."),
1443 				1, "<OK>");
1444 			break;
1445 		case 1:
1446 			show_scroll_win(main_window,
1447 				_("Save Alternate Configuration"),
1448 				save_config_help);
1449 			break;
1450 		case KEY_EXIT:
1451 			return;
1452 		}
1453 	}
1454 }
1455 
1456 void setup_windows(void)
1457 {
1458 	if (main_window != NULL)
1459 		delwin(main_window);
1460 
1461 	/* set up the menu and menu window */
1462 	main_window = newwin(LINES-2, COLS-2, 2, 1);
1463 	keypad(main_window, TRUE);
1464 	mwin_max_lines = LINES-7;
1465 	mwin_max_cols = COLS-6;
1466 
1467 	/* panels order is from bottom to top */
1468 	new_panel(main_window);
1469 }
1470 
1471 int main(int ac, char **av)
1472 {
1473 	char *mode;
1474 
1475 	setlocale(LC_ALL, "");
1476 	bindtextdomain(PACKAGE, LOCALEDIR);
1477 	textdomain(PACKAGE);
1478 
1479 	conf_parse(av[1]);
1480 	conf_read(NULL);
1481 
1482 	mode = getenv("NCONFIG_MODE");
1483 	if (mode) {
1484 		if (!strcasecmp(mode, "single_menu"))
1485 			single_menu_mode = 1;
1486 	}
1487 
1488 	/* Initialize curses */
1489 	initscr();
1490 	/* set color theme */
1491 	set_colors();
1492 
1493 	cbreak();
1494 	noecho();
1495 	keypad(stdscr, TRUE);
1496 	curs_set(0);
1497 
1498 	if (COLS < 75 || LINES < 20) {
1499 		endwin();
1500 		printf("Your terminal should have at "
1501 			"least 20 lines and 75 columns\n");
1502 		return 1;
1503 	}
1504 
1505 	notimeout(stdscr, FALSE);
1506 #if NCURSES_REENTRANT
1507 	set_escdelay(1);
1508 #else
1509 	ESCDELAY = 1;
1510 #endif
1511 
1512 	/* set btns menu */
1513 	curses_menu = new_menu(curses_menu_items);
1514 	menu_opts_off(curses_menu, O_SHOWDESC);
1515 	menu_opts_on(curses_menu, O_SHOWMATCH);
1516 	menu_opts_on(curses_menu, O_ONEVALUE);
1517 	menu_opts_on(curses_menu, O_NONCYCLIC);
1518 	menu_opts_on(curses_menu, O_IGNORECASE);
1519 	set_menu_mark(curses_menu, " ");
1520 	set_menu_fore(curses_menu, attributes[MAIN_MENU_FORE]);
1521 	set_menu_back(curses_menu, attributes[MAIN_MENU_BACK]);
1522 	set_menu_grey(curses_menu, attributes[MAIN_MENU_GREY]);
1523 
1524 	set_config_filename(conf_get_configname());
1525 	setup_windows();
1526 
1527 	/* check for KEY_FUNC(1) */
1528 	if (has_key(KEY_F(1)) == FALSE) {
1529 		show_scroll_win(main_window,
1530 				_("Instructions"),
1531 				_(menu_no_f_instructions));
1532 	}
1533 
1534 	conf_set_message_callback(conf_message_callback);
1535 	/* do the work */
1536 	while (!global_exit) {
1537 		conf(&rootmenu);
1538 		if (!global_exit && do_exit() == 0)
1539 			break;
1540 	}
1541 	/* ok, we are done */
1542 	unpost_menu(curses_menu);
1543 	free_menu(curses_menu);
1544 	delwin(main_window);
1545 	clear();
1546 	refresh();
1547 	endwin();
1548 	return 0;
1549 }
1550 
1551