xref: /linux/scripts/kconfig/nconf.c (revision 60e13231561b3a4c5269bfa1ef6c0569ad6f28ec)
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 <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"),
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 nohelp_text[] = N_(
186 "There is no help available for this option.\n"),
187 load_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."),
191 load_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"),
202 save_config_text[] = N_(
203 "Enter a filename to which this configuration should be saved\n"
204 "as an alternate.  Leave blank to abort."),
205 save_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"),
216 search_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 
256 struct mitem {
257 	char str[256];
258 	char tag;
259 	void *usrptr;
260 	int is_visible;
261 };
262 
263 #define MAX_MENU_ITEMS 4096
264 static int show_all_items;
265 static int indent;
266 static struct menu *current_menu;
267 static int child_count;
268 static int single_menu_mode;
269 /* the window in which all information appears */
270 static WINDOW *main_window;
271 /* the largest size of the menu window */
272 static int mwin_max_lines;
273 static int mwin_max_cols;
274 /* the window in which we show option buttons */
275 static MENU *curses_menu;
276 static ITEM *curses_menu_items[MAX_MENU_ITEMS];
277 static struct mitem k_menu_items[MAX_MENU_ITEMS];
278 static int items_num;
279 static int global_exit;
280 /* the currently selected button */
281 const char *current_instructions = menu_instructions;
282 
283 static void conf(struct menu *menu);
284 static void conf_choice(struct menu *menu);
285 static void conf_string(struct menu *menu);
286 static void conf_load(void);
287 static void conf_save(void);
288 static void show_help(struct menu *menu);
289 static int do_exit(void);
290 static void setup_windows(void);
291 static void search_conf(void);
292 
293 typedef void (*function_key_handler_t)(int *key, struct menu *menu);
294 static void handle_f1(int *key, struct menu *current_item);
295 static void handle_f2(int *key, struct menu *current_item);
296 static void handle_f3(int *key, struct menu *current_item);
297 static void handle_f4(int *key, struct menu *current_item);
298 static void handle_f5(int *key, struct menu *current_item);
299 static void handle_f6(int *key, struct menu *current_item);
300 static void handle_f7(int *key, struct menu *current_item);
301 static void handle_f8(int *key, struct menu *current_item);
302 static void handle_f9(int *key, struct menu *current_item);
303 
304 struct function_keys {
305 	const char *key_str;
306 	const char *func;
307 	function_key key;
308 	function_key_handler_t handler;
309 };
310 
311 static const int function_keys_num = 9;
312 struct 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 
369 static 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 */
391 static 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 */
399 static void handle_f2(int *key, struct menu *current_item)
400 {
401 	show_help(current_item);
402 	return;
403 }
404 
405 /* instructions */
406 static 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 */
415 static 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 */
431 static void handle_f5(int *key, struct menu *current_item)
432 {
433 	*key = KEY_LEFT;
434 	return;
435 }
436 
437 /* save */
438 static void handle_f6(int *key, struct menu *current_item)
439 {
440 	conf_save();
441 	return;
442 }
443 
444 /* load */
445 static void handle_f7(int *key, struct menu *current_item)
446 {
447 	conf_load();
448 	return;
449 }
450 
451 /* search */
452 static void handle_f8(int *key, struct menu *current_item)
453 {
454 	search_conf();
455 	return;
456 }
457 
458 /* exit */
459 static 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 */
466 static 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 
486 static 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 
496 typedef 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 */
500 static 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. */
528 static 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 */
568 static 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 */
596 static 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 
608 static int curses_item_index(void)
609 {
610 	return  item_index(current_item(curses_menu));
611 }
612 
613 static 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 
626 static int item_is_tag(char tag)
627 {
628 	return item_tag() == tag;
629 }
630 
631 static char filename[PATH_MAX+1];
632 static char menu_backtitle[PATH_MAX+128];
633 static 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  */
651 static 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 
694 static 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;
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, 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 
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 	char dialog_input_result[256];
1352 
1353 	while (1) {
1354 		int res;
1355 		const char *heading;
1356 
1357 		switch (sym_get_type(menu->sym)) {
1358 		case S_INT:
1359 			heading = _(inputbox_instructions_int);
1360 			break;
1361 		case S_HEX:
1362 			heading = _(inputbox_instructions_hex);
1363 			break;
1364 		case S_STRING:
1365 			heading = _(inputbox_instructions_string);
1366 			break;
1367 		default:
1368 			heading = _("Internal nconf error!");
1369 		}
1370 		res = dialog_inputbox(main_window,
1371 				prompt ? _(prompt) : _("Main Menu"),
1372 				heading,
1373 				sym_get_string_value(menu->sym),
1374 				dialog_input_result,
1375 				sizeof(dialog_input_result));
1376 		switch (res) {
1377 		case 0:
1378 			if (sym_set_string_value(menu->sym,
1379 						dialog_input_result))
1380 				return;
1381 			btn_dialog(main_window,
1382 				_("You have made an invalid entry."), 0);
1383 			break;
1384 		case 1:
1385 			show_help(menu);
1386 			break;
1387 		case KEY_EXIT:
1388 			return;
1389 		}
1390 	}
1391 }
1392 
1393 static void conf_load(void)
1394 {
1395 	char dialog_input_result[256];
1396 	while (1) {
1397 		int res;
1398 		res = dialog_inputbox(main_window,
1399 				NULL, load_config_text,
1400 				filename,
1401 				dialog_input_result,
1402 				sizeof(dialog_input_result));
1403 		switch (res) {
1404 		case 0:
1405 			if (!dialog_input_result[0])
1406 				return;
1407 			if (!conf_read(dialog_input_result)) {
1408 				set_config_filename(dialog_input_result);
1409 				sym_set_change_count(1);
1410 				return;
1411 			}
1412 			btn_dialog(main_window, _("File does not exist!"), 0);
1413 			break;
1414 		case 1:
1415 			show_scroll_win(main_window,
1416 					_("Load Alternate Configuration"),
1417 					load_config_help);
1418 			break;
1419 		case KEY_EXIT:
1420 			return;
1421 		}
1422 	}
1423 }
1424 
1425 static void conf_save(void)
1426 {
1427 	char dialog_input_result[256];
1428 	while (1) {
1429 		int res;
1430 		res = dialog_inputbox(main_window,
1431 				NULL, save_config_text,
1432 				filename,
1433 				dialog_input_result,
1434 				sizeof(dialog_input_result));
1435 		switch (res) {
1436 		case 0:
1437 			if (!dialog_input_result[0])
1438 				return;
1439 			res = conf_write(dialog_input_result);
1440 			if (!res) {
1441 				set_config_filename(dialog_input_result);
1442 				return;
1443 			}
1444 			btn_dialog(main_window, _("Can't create file! "
1445 				"Probably a nonexistent directory."),
1446 				1, "<OK>");
1447 			break;
1448 		case 1:
1449 			show_scroll_win(main_window,
1450 				_("Save Alternate Configuration"),
1451 				save_config_help);
1452 			break;
1453 		case KEY_EXIT:
1454 			return;
1455 		}
1456 	}
1457 }
1458 
1459 void setup_windows(void)
1460 {
1461 	if (main_window != NULL)
1462 		delwin(main_window);
1463 
1464 	/* set up the menu and menu window */
1465 	main_window = newwin(LINES-2, COLS-2, 2, 1);
1466 	keypad(main_window, TRUE);
1467 	mwin_max_lines = LINES-7;
1468 	mwin_max_cols = COLS-6;
1469 
1470 	/* panels order is from bottom to top */
1471 	new_panel(main_window);
1472 }
1473 
1474 int main(int ac, char **av)
1475 {
1476 	char *mode;
1477 
1478 	setlocale(LC_ALL, "");
1479 	bindtextdomain(PACKAGE, LOCALEDIR);
1480 	textdomain(PACKAGE);
1481 
1482 	conf_parse(av[1]);
1483 	conf_read(NULL);
1484 
1485 	mode = getenv("NCONFIG_MODE");
1486 	if (mode) {
1487 		if (!strcasecmp(mode, "single_menu"))
1488 			single_menu_mode = 1;
1489 	}
1490 
1491 	/* Initialize curses */
1492 	initscr();
1493 	/* set color theme */
1494 	set_colors();
1495 
1496 	cbreak();
1497 	noecho();
1498 	keypad(stdscr, TRUE);
1499 	curs_set(0);
1500 
1501 	if (COLS < 75 || LINES < 20) {
1502 		endwin();
1503 		printf("Your terminal should have at "
1504 			"least 20 lines and 75 columns\n");
1505 		return 1;
1506 	}
1507 
1508 	notimeout(stdscr, FALSE);
1509 	ESCDELAY = 1;
1510 
1511 	/* set btns menu */
1512 	curses_menu = new_menu(curses_menu_items);
1513 	menu_opts_off(curses_menu, O_SHOWDESC);
1514 	menu_opts_on(curses_menu, O_SHOWMATCH);
1515 	menu_opts_on(curses_menu, O_ONEVALUE);
1516 	menu_opts_on(curses_menu, O_NONCYCLIC);
1517 	menu_opts_on(curses_menu, O_IGNORECASE);
1518 	set_menu_mark(curses_menu, " ");
1519 	set_menu_fore(curses_menu, attributes[MAIN_MENU_FORE]);
1520 	set_menu_back(curses_menu, attributes[MAIN_MENU_BACK]);
1521 	set_menu_grey(curses_menu, attributes[MAIN_MENU_GREY]);
1522 
1523 	set_config_filename(conf_get_configname());
1524 	setup_windows();
1525 
1526 	/* check for KEY_FUNC(1) */
1527 	if (has_key(KEY_F(1)) == FALSE) {
1528 		show_scroll_win(main_window,
1529 				_("Instructions"),
1530 				_(menu_no_f_instructions));
1531 	}
1532 
1533 	conf_set_message_callback(conf_message_callback);
1534 	/* do the work */
1535 	while (!global_exit) {
1536 		conf(&rootmenu);
1537 		if (!global_exit && do_exit() == 0)
1538 			break;
1539 	}
1540 	/* ok, we are done */
1541 	unpost_menu(curses_menu);
1542 	free_menu(curses_menu);
1543 	delwin(main_window);
1544 	clear();
1545 	refresh();
1546 	endwin();
1547 	return 0;
1548 }
1549 
1550