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