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