xref: /linux/scripts/kconfig/nconf.c (revision d524dac9279b6a41ffdf7ff7958c577f2e387db6)
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 
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 		wattrset(main_window, attributes[FUNCTION_HIGHLIGHT]);
377 		mvwprintw(main_window, LINES-3, offset,
378 				"%s",
379 				function_keys[i].key_str);
380 		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 	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 	wattrset(main_window, attributes[NORMAL]);
957 	print_in_middle(stdscr, 1, 0, COLS,
958 			menu_backtitle,
959 			attributes[MAIN_HEADING]);
960 
961 	wattrset(main_window, attributes[MAIN_MENU_BOX]);
962 	box(main_window, 0, 0);
963 	wattrset(main_window, attributes[MAIN_MENU_HEADING]);
964 	mvwprintw(main_window, 0, 3, " %s ", prompt);
965 	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 	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 
1215 static 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 
1223 static 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 
1243 static 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 
1360 static 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 
1405 static 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 
1437 static 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 
1471 void 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 
1486 int 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