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