xref: /linux/scripts/kconfig/nconf.c (revision 68e5c7d4cefb66de3953a874e670ec8f1ce86a24)
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 handles */
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 	free_item(curses_menu_items[index]);
597 	curses_menu_items[index] = new_item(
598 			k_menu_items[index].str,
599 			k_menu_items[index].str);
600 	set_item_userptr(curses_menu_items[index],
601 			&k_menu_items[index]);
602 }
603 
604 /* get the tag of the currently selected item */
605 static char item_tag(void)
606 {
607 	ITEM *cur;
608 	struct mitem *mcur;
609 
610 	cur = current_item(curses_menu);
611 	if (cur == NULL)
612 		return 0;
613 	mcur = (struct mitem *) item_userptr(cur);
614 	return mcur->tag;
615 }
616 
617 static int curses_item_index(void)
618 {
619 	return  item_index(current_item(curses_menu));
620 }
621 
622 static void *item_data(void)
623 {
624 	ITEM *cur;
625 	struct mitem *mcur;
626 
627 	cur = current_item(curses_menu);
628 	if (!cur)
629 		return NULL;
630 	mcur = (struct mitem *) item_userptr(cur);
631 	return mcur->usrptr;
632 
633 }
634 
635 static int item_is_tag(char tag)
636 {
637 	return item_tag() == tag;
638 }
639 
640 static char filename[PATH_MAX+1];
641 static char menu_backtitle[PATH_MAX+128];
642 static void set_config_filename(const char *config_filename)
643 {
644 	snprintf(menu_backtitle, sizeof(menu_backtitle), "%s - %s",
645 		 config_filename, rootmenu.prompt->text);
646 
647 	snprintf(filename, sizeof(filename), "%s", config_filename);
648 }
649 
650 /* return = 0 means we are successful.
651  * -1 means go on doing what you were doing
652  */
653 static int do_exit(void)
654 {
655 	int res;
656 	if (!conf_get_changed()) {
657 		global_exit = 1;
658 		return 0;
659 	}
660 	res = btn_dialog(main_window,
661 			"Do you wish to save your new configuration?\n"
662 				"<ESC> to cancel and resume nconfig.",
663 			2,
664 			"   <save>   ",
665 			"<don't save>");
666 	if (res == KEY_EXIT) {
667 		global_exit = 0;
668 		return -1;
669 	}
670 
671 	/* if we got here, the user really wants to exit */
672 	switch (res) {
673 	case 0:
674 		res = conf_write(filename);
675 		if (res)
676 			btn_dialog(
677 				main_window,
678 				"Error during writing of configuration.\n"
679 				  "Your configuration changes were NOT saved.",
680 				  1,
681 				  "<OK>");
682 		conf_write_autoconf(0);
683 		break;
684 	default:
685 		btn_dialog(
686 			main_window,
687 			"Your configuration changes were NOT saved.",
688 			1,
689 			"<OK>");
690 		break;
691 	}
692 	global_exit = 1;
693 	return 0;
694 }
695 
696 
697 static void search_conf(void)
698 {
699 	struct symbol **sym_arr;
700 	struct gstr res;
701 	struct gstr title;
702 	char *dialog_input;
703 	int dres, vscroll = 0, hscroll = 0;
704 	bool again;
705 
706 	title = str_new();
707 	str_printf( &title, "Enter (sub)string or regexp to search for "
708 			      "(with or without \"%s\")", CONFIG_);
709 
710 again:
711 	dres = dialog_inputbox(main_window,
712 			"Search Configuration Parameter",
713 			str_get(&title),
714 			"", &dialog_input_result, &dialog_input_result_len);
715 	switch (dres) {
716 	case 0:
717 		break;
718 	case 1:
719 		show_scroll_win(main_window,
720 				"Search Configuration", search_help);
721 		goto again;
722 	default:
723 		str_free(&title);
724 		return;
725 	}
726 
727 	/* strip the prefix if necessary */
728 	dialog_input = dialog_input_result;
729 	if (strncasecmp(dialog_input_result, CONFIG_, strlen(CONFIG_)) == 0)
730 		dialog_input += strlen(CONFIG_);
731 
732 	sym_arr = sym_re_search(dialog_input);
733 
734 	do {
735 		LIST_HEAD(head);
736 		struct search_data data = {
737 			.head = &head,
738 			.target = NULL,
739 		};
740 		jump_key_char = 0;
741 		res = get_relations_str(sym_arr, &head);
742 		dres = show_scroll_win_ext(main_window,
743 				"Search Results", str_get(&res),
744 				&vscroll, &hscroll,
745 				handle_search_keys, &data);
746 		again = false;
747 		if (dres >= '1' && dres <= '9') {
748 			assert(data.target != NULL);
749 			selected_conf(data.target->parent, data.target);
750 			again = true;
751 		}
752 		str_free(&res);
753 	} while (again);
754 	free(sym_arr);
755 	str_free(&title);
756 }
757 
758 
759 static void build_conf(struct menu *menu)
760 {
761 	struct symbol *sym;
762 	struct property *prop;
763 	struct menu *child;
764 	int type, tmp, doint = 2;
765 	tristate val;
766 	char ch;
767 
768 	if (!menu || (!show_all_items && !menu_is_visible(menu)))
769 		return;
770 
771 	sym = menu->sym;
772 	prop = menu->prompt;
773 	if (!sym) {
774 		if (prop && menu != current_menu) {
775 			const char *prompt = menu_get_prompt(menu);
776 			enum prop_type ptype;
777 			ptype = menu->prompt ? menu->prompt->type : P_UNKNOWN;
778 			switch (ptype) {
779 			case P_MENU:
780 				child_count++;
781 				if (single_menu_mode) {
782 					item_make(menu, 'm',
783 						"%s%*c%s",
784 						menu->data ? "-->" : "++>",
785 						indent + 1, ' ', prompt);
786 				} else
787 					item_make(menu, 'm',
788 						  "   %*c%s  %s",
789 						  indent + 1, ' ', prompt,
790 						  menu_is_empty(menu) ? "----" : "--->");
791 
792 				if (single_menu_mode && menu->data)
793 					goto conf_childs;
794 				return;
795 			case P_COMMENT:
796 				if (prompt) {
797 					child_count++;
798 					item_make(menu, ':',
799 						"   %*c*** %s ***",
800 						indent + 1, ' ',
801 						prompt);
802 				}
803 				break;
804 			default:
805 				if (prompt) {
806 					child_count++;
807 					item_make(menu, ':', "---%*c%s",
808 						indent + 1, ' ',
809 						prompt);
810 				}
811 			}
812 		} else
813 			doint = 0;
814 		goto conf_childs;
815 	}
816 
817 	type = sym_get_type(sym);
818 	if (sym_is_choice(sym)) {
819 		struct symbol *def_sym = sym_calc_choice(menu);
820 		struct menu *def_menu = NULL;
821 
822 		child_count++;
823 		for (child = menu->list; child; child = child->next) {
824 			if (menu_is_visible(child) && child->sym == def_sym)
825 				def_menu = child;
826 		}
827 
828 		val = sym_get_tristate_value(sym);
829 		item_make(menu, def_menu ? 't' : ':', "   ");
830 
831 		item_add_str("%*c%s", indent + 1,
832 				' ', menu_get_prompt(menu));
833 		if (def_menu)
834 			item_add_str(" (%s)  --->", menu_get_prompt(def_menu));
835 		return;
836 	} else {
837 		if (menu == current_menu) {
838 			item_make(menu, ':',
839 				"---%*c%s", indent + 1,
840 				' ', menu_get_prompt(menu));
841 			goto conf_childs;
842 		}
843 		child_count++;
844 		val = sym_get_tristate_value(sym);
845 		switch (type) {
846 		case S_BOOLEAN:
847 			if (sym_is_changeable(sym))
848 				item_make(menu, 't', "[%c]",
849 					  val == no ? ' ' : '*');
850 			else
851 				item_make(menu, 't', "-%c-",
852 					  val == no ? ' ' : '*');
853 			break;
854 		case S_TRISTATE:
855 			switch (val) {
856 			case yes:
857 				ch = '*';
858 				break;
859 			case mod:
860 				ch = 'M';
861 				break;
862 			default:
863 				ch = ' ';
864 				break;
865 			}
866 			if (sym_is_changeable(sym)) {
867 				if (sym->rev_dep.tri == mod)
868 					item_make(menu, 't', "{%c}", ch);
869 				else
870 					item_make(menu, 't', "<%c>", ch);
871 			} else
872 				item_make(menu, 't', "-%c-", ch);
873 			break;
874 		default:
875 			tmp = 2 + strlen(sym_get_string_value(sym));
876 			item_make(menu, 's', "    (%s)",
877 				  sym_get_string_value(sym));
878 			tmp = indent - tmp + 4;
879 			if (tmp < 0)
880 				tmp = 0;
881 			item_add_str("%*c%s%s", tmp, ' ', menu_get_prompt(menu),
882 				     (sym_has_value(sym) ||
883 				      !sym_is_changeable(sym)) ? "" : " (NEW)");
884 			goto conf_childs;
885 		}
886 		item_add_str("%*c%s%s", indent + 1, ' ',
887 				menu_get_prompt(menu),
888 				(sym_has_value(sym) || !sym_is_changeable(sym)) ?
889 				"" : " (NEW)");
890 		if (menu->prompt && menu->prompt->type == P_MENU) {
891 			item_add_str("  %s", menu_is_empty(menu) ? "----" : "--->");
892 			return;
893 		}
894 	}
895 
896 conf_childs:
897 	indent += doint;
898 	for (child = menu->list; child; child = child->next)
899 		build_conf(child);
900 	indent -= doint;
901 }
902 
903 static void reset_menu(void)
904 {
905 	unpost_menu(curses_menu);
906 	clean_items();
907 }
908 
909 /* adjust the menu to show this item.
910  * prefer not to scroll the menu if possible*/
911 static void center_item(int selected_index, int *last_top_row)
912 {
913 	int toprow;
914 
915 	set_top_row(curses_menu, *last_top_row);
916 	toprow = top_row(curses_menu);
917 	if (selected_index < toprow ||
918 	    selected_index >= toprow+mwin_max_lines) {
919 		toprow = max(selected_index-mwin_max_lines/2, 0);
920 		if (toprow >= item_count(curses_menu)-mwin_max_lines)
921 			toprow = item_count(curses_menu)-mwin_max_lines;
922 		set_top_row(curses_menu, toprow);
923 	}
924 	set_current_item(curses_menu,
925 			curses_menu_items[selected_index]);
926 	*last_top_row = toprow;
927 	post_menu(curses_menu);
928 	refresh_all_windows(main_window);
929 }
930 
931 /* this function assumes reset_menu has been called before */
932 static void show_menu(const char *prompt, const char *instructions,
933 		int selected_index, int *last_top_row)
934 {
935 	int maxx, maxy;
936 	WINDOW *menu_window;
937 
938 	current_instructions = instructions;
939 
940 	clear();
941 	print_in_middle(stdscr, 1, getmaxx(stdscr),
942 			menu_backtitle,
943 			attr_main_heading);
944 
945 	wattrset(main_window, attr_main_menu_box);
946 	box(main_window, 0, 0);
947 	wattrset(main_window, attr_main_menu_heading);
948 	mvwprintw(main_window, 0, 3, " %s ", prompt);
949 	wattrset(main_window, attr_normal);
950 
951 	set_menu_items(curses_menu, curses_menu_items);
952 
953 	/* position the menu at the middle of the screen */
954 	scale_menu(curses_menu, &maxy, &maxx);
955 	maxx = min(maxx, mwin_max_cols-2);
956 	maxy = mwin_max_lines;
957 	menu_window = derwin(main_window,
958 			maxy,
959 			maxx,
960 			2,
961 			(mwin_max_cols-maxx)/2);
962 	keypad(menu_window, TRUE);
963 	set_menu_win(curses_menu, menu_window);
964 	set_menu_sub(curses_menu, menu_window);
965 
966 	/* must reassert this after changing items, otherwise returns to a
967 	 * default of 16
968 	 */
969 	set_menu_format(curses_menu, maxy, 1);
970 	center_item(selected_index, last_top_row);
971 	set_menu_format(curses_menu, maxy, 1);
972 
973 	print_function_line();
974 
975 	/* Post the menu */
976 	post_menu(curses_menu);
977 	refresh_all_windows(main_window);
978 }
979 
980 static void adj_match_dir(match_f *match_direction)
981 {
982 	if (*match_direction == FIND_NEXT_MATCH_DOWN)
983 		*match_direction =
984 			MATCH_TINKER_PATTERN_DOWN;
985 	else if (*match_direction == FIND_NEXT_MATCH_UP)
986 		*match_direction =
987 			MATCH_TINKER_PATTERN_UP;
988 	/* else, do no change.. */
989 }
990 
991 struct match_state
992 {
993 	int in_search;
994 	match_f match_direction;
995 	char pattern[256];
996 };
997 
998 /* Return 0 means I have handled the key. In such a case, ans should hold the
999  * item to center, or -1 otherwise.
1000  * Else return -1 .
1001  */
1002 static int do_match(int key, struct match_state *state, int *ans)
1003 {
1004 	char c = (char) key;
1005 	int terminate_search = 0;
1006 	*ans = -1;
1007 	if (key == '/' || (state->in_search && key == 27)) {
1008 		move(0, 0);
1009 		refresh();
1010 		clrtoeol();
1011 		state->in_search = 1-state->in_search;
1012 		bzero(state->pattern, sizeof(state->pattern));
1013 		state->match_direction = MATCH_TINKER_PATTERN_DOWN;
1014 		return 0;
1015 	} else if (!state->in_search)
1016 		return 1;
1017 
1018 	if (isalnum(c) || isgraph(c) || c == ' ') {
1019 		state->pattern[strlen(state->pattern)] = c;
1020 		state->pattern[strlen(state->pattern)] = '\0';
1021 		adj_match_dir(&state->match_direction);
1022 		*ans = get_mext_match(state->pattern,
1023 				state->match_direction);
1024 	} else if (key == KEY_DOWN) {
1025 		state->match_direction = FIND_NEXT_MATCH_DOWN;
1026 		*ans = get_mext_match(state->pattern,
1027 				state->match_direction);
1028 	} else if (key == KEY_UP) {
1029 		state->match_direction = FIND_NEXT_MATCH_UP;
1030 		*ans = get_mext_match(state->pattern,
1031 				state->match_direction);
1032 	} else if (key == KEY_BACKSPACE || key == 8 || key == 127) {
1033 		state->pattern[strlen(state->pattern)-1] = '\0';
1034 		adj_match_dir(&state->match_direction);
1035 	} else
1036 		terminate_search = 1;
1037 
1038 	if (terminate_search) {
1039 		state->in_search = 0;
1040 		bzero(state->pattern, sizeof(state->pattern));
1041 		move(0, 0);
1042 		refresh();
1043 		clrtoeol();
1044 		return -1;
1045 	}
1046 	return 0;
1047 }
1048 
1049 static void conf(struct menu *menu)
1050 {
1051 	selected_conf(menu, NULL);
1052 }
1053 
1054 static void selected_conf(struct menu *menu, struct menu *active_menu)
1055 {
1056 	struct menu *submenu = NULL;
1057 	struct symbol *sym;
1058 	int i, res;
1059 	int current_index = 0;
1060 	int last_top_row = 0;
1061 	struct match_state match_state = {
1062 		.in_search = 0,
1063 		.match_direction = MATCH_TINKER_PATTERN_DOWN,
1064 		.pattern = "",
1065 	};
1066 
1067 	while (!global_exit) {
1068 		reset_menu();
1069 		current_menu = menu;
1070 		build_conf(menu);
1071 		if (!child_count)
1072 			break;
1073 
1074 		if (active_menu != NULL) {
1075 			for (i = 0; i < items_num; i++) {
1076 				struct mitem *mcur;
1077 
1078 				mcur = (struct mitem *) item_userptr(curses_menu_items[i]);
1079 				if ((struct menu *) mcur->usrptr == active_menu) {
1080 					current_index = i;
1081 					break;
1082 				}
1083 			}
1084 			active_menu = NULL;
1085 		}
1086 
1087 		show_menu(menu_get_prompt(menu), menu_instructions,
1088 			  current_index, &last_top_row);
1089 		keypad((menu_win(curses_menu)), TRUE);
1090 		while (!global_exit) {
1091 			if (match_state.in_search) {
1092 				mvprintw(0, 0,
1093 					"searching: %s", match_state.pattern);
1094 				clrtoeol();
1095 			}
1096 			refresh_all_windows(main_window);
1097 			res = wgetch(menu_win(curses_menu));
1098 			if (!res)
1099 				break;
1100 			if (do_match(res, &match_state, &current_index) == 0) {
1101 				if (current_index != -1)
1102 					center_item(current_index,
1103 						    &last_top_row);
1104 				continue;
1105 			}
1106 			if (process_special_keys(&res,
1107 						(struct menu *) item_data()))
1108 				break;
1109 			switch (res) {
1110 			case KEY_DOWN:
1111 			case 'j':
1112 				menu_driver(curses_menu, REQ_DOWN_ITEM);
1113 				break;
1114 			case KEY_UP:
1115 			case 'k':
1116 				menu_driver(curses_menu, REQ_UP_ITEM);
1117 				break;
1118 			case KEY_NPAGE:
1119 				menu_driver(curses_menu, REQ_SCR_DPAGE);
1120 				break;
1121 			case KEY_PPAGE:
1122 				menu_driver(curses_menu, REQ_SCR_UPAGE);
1123 				break;
1124 			case KEY_HOME:
1125 				menu_driver(curses_menu, REQ_FIRST_ITEM);
1126 				break;
1127 			case KEY_END:
1128 				menu_driver(curses_menu, REQ_LAST_ITEM);
1129 				break;
1130 			case 'h':
1131 			case '?':
1132 				show_help((struct menu *) item_data());
1133 				break;
1134 			}
1135 			if (res == 10 || res == 27 ||
1136 				res == 32 || res == 'n' || res == 'y' ||
1137 				res == KEY_LEFT || res == KEY_RIGHT ||
1138 				res == 'm')
1139 				break;
1140 			refresh_all_windows(main_window);
1141 		}
1142 
1143 		refresh_all_windows(main_window);
1144 		/* if ESC or left*/
1145 		if (res == 27 || (menu != &rootmenu && res == KEY_LEFT))
1146 			break;
1147 
1148 		/* remember location in the menu */
1149 		last_top_row = top_row(curses_menu);
1150 		current_index = curses_item_index();
1151 
1152 		if (!item_tag())
1153 			continue;
1154 
1155 		submenu = (struct menu *) item_data();
1156 		if (!submenu || !menu_is_visible(submenu))
1157 			continue;
1158 		sym = submenu->sym;
1159 
1160 		switch (res) {
1161 		case ' ':
1162 			if (item_is_tag('t'))
1163 				sym_toggle_tristate_value(sym);
1164 			else if (item_is_tag('m'))
1165 				conf(submenu);
1166 			break;
1167 		case KEY_RIGHT:
1168 		case 10: /* ENTER WAS PRESSED */
1169 			switch (item_tag()) {
1170 			case 'm':
1171 				if (single_menu_mode)
1172 					submenu->data =
1173 						(void *) (long) !submenu->data;
1174 				else
1175 					conf(submenu);
1176 				break;
1177 			case 't':
1178 				if (sym_is_choice(sym))
1179 					conf_choice(submenu);
1180 				else if (submenu->prompt &&
1181 					 submenu->prompt->type == P_MENU)
1182 					conf(submenu);
1183 				else if (res == 10)
1184 					sym_toggle_tristate_value(sym);
1185 				break;
1186 			case 's':
1187 				conf_string(submenu);
1188 				break;
1189 			}
1190 			break;
1191 		case 'y':
1192 			if (item_is_tag('t')) {
1193 				if (sym_set_tristate_value(sym, yes))
1194 					break;
1195 				if (sym_set_tristate_value(sym, mod))
1196 					btn_dialog(main_window, setmod_text, 0);
1197 			}
1198 			break;
1199 		case 'n':
1200 			if (item_is_tag('t'))
1201 				sym_set_tristate_value(sym, no);
1202 			break;
1203 		case 'm':
1204 			if (item_is_tag('t'))
1205 				sym_set_tristate_value(sym, mod);
1206 			break;
1207 		}
1208 	}
1209 }
1210 
1211 static void conf_message_callback(const char *s)
1212 {
1213 	btn_dialog(main_window, s, 1, "<OK>");
1214 }
1215 
1216 static void show_help(struct menu *menu)
1217 {
1218 	struct gstr help;
1219 
1220 	if (!menu)
1221 		return;
1222 
1223 	help = str_new();
1224 	menu_get_ext_help(menu, &help);
1225 	show_scroll_win(main_window, menu_get_prompt(menu), str_get(&help));
1226 	str_free(&help);
1227 }
1228 
1229 static void conf_choice(struct menu *menu)
1230 {
1231 	const char *prompt = menu_get_prompt(menu);
1232 	struct menu *child = NULL;
1233 	struct symbol *active;
1234 	int selected_index = 0;
1235 	int last_top_row = 0;
1236 	int res, i = 0;
1237 	struct match_state match_state = {
1238 		.in_search = 0,
1239 		.match_direction = MATCH_TINKER_PATTERN_DOWN,
1240 		.pattern = "",
1241 	};
1242 
1243 	active = sym_calc_choice(menu);
1244 	/* this is mostly duplicated from the conf() function. */
1245 	while (!global_exit) {
1246 		reset_menu();
1247 
1248 		for (i = 0, child = menu->list; child; child = child->next) {
1249 			if (!show_all_items && !menu_is_visible(child))
1250 				continue;
1251 
1252 			if (child->sym == sym_calc_choice(menu))
1253 				item_make(child, ':', "<X> %s",
1254 						menu_get_prompt(child));
1255 			else if (child->sym)
1256 				item_make(child, ':', "    %s",
1257 						menu_get_prompt(child));
1258 			else
1259 				item_make(child, ':', "*** %s ***",
1260 						menu_get_prompt(child));
1261 
1262 			if (child->sym == active){
1263 				last_top_row = top_row(curses_menu);
1264 				selected_index = i;
1265 			}
1266 			i++;
1267 		}
1268 		show_menu(prompt ? prompt : "Choice Menu",
1269 				radiolist_instructions,
1270 				selected_index,
1271 				&last_top_row);
1272 		while (!global_exit) {
1273 			if (match_state.in_search) {
1274 				mvprintw(0, 0, "searching: %s",
1275 					 match_state.pattern);
1276 				clrtoeol();
1277 			}
1278 			refresh_all_windows(main_window);
1279 			res = wgetch(menu_win(curses_menu));
1280 			if (!res)
1281 				break;
1282 			if (do_match(res, &match_state, &selected_index) == 0) {
1283 				if (selected_index != -1)
1284 					center_item(selected_index,
1285 						    &last_top_row);
1286 				continue;
1287 			}
1288 			if (process_special_keys(
1289 						&res,
1290 						(struct menu *) item_data()))
1291 				break;
1292 			switch (res) {
1293 			case KEY_DOWN:
1294 			case 'j':
1295 				menu_driver(curses_menu, REQ_DOWN_ITEM);
1296 				break;
1297 			case KEY_UP:
1298 			case 'k':
1299 				menu_driver(curses_menu, REQ_UP_ITEM);
1300 				break;
1301 			case KEY_NPAGE:
1302 				menu_driver(curses_menu, REQ_SCR_DPAGE);
1303 				break;
1304 			case KEY_PPAGE:
1305 				menu_driver(curses_menu, REQ_SCR_UPAGE);
1306 				break;
1307 			case KEY_HOME:
1308 				menu_driver(curses_menu, REQ_FIRST_ITEM);
1309 				break;
1310 			case KEY_END:
1311 				menu_driver(curses_menu, REQ_LAST_ITEM);
1312 				break;
1313 			case 'h':
1314 			case '?':
1315 				show_help((struct menu *) item_data());
1316 				break;
1317 			}
1318 			if (res == 10 || res == 27 || res == ' ' ||
1319 					res == KEY_LEFT){
1320 				break;
1321 			}
1322 			refresh_all_windows(main_window);
1323 		}
1324 		/* if ESC or left */
1325 		if (res == 27 || res == KEY_LEFT)
1326 			break;
1327 
1328 		child = item_data();
1329 		if (!child || !menu_is_visible(child) || !child->sym)
1330 			continue;
1331 		switch (res) {
1332 		case ' ':
1333 		case  10:
1334 		case KEY_RIGHT:
1335 			choice_set_value(menu, child->sym);
1336 			return;
1337 		case 'h':
1338 		case '?':
1339 			show_help(child);
1340 			active = child->sym;
1341 			break;
1342 		case KEY_EXIT:
1343 			return;
1344 		}
1345 	}
1346 }
1347 
1348 static void conf_string(struct menu *menu)
1349 {
1350 	const char *prompt = menu_get_prompt(menu);
1351 
1352 	while (1) {
1353 		int res;
1354 		const char *heading;
1355 
1356 		switch (sym_get_type(menu->sym)) {
1357 		case S_INT:
1358 			heading = inputbox_instructions_int;
1359 			break;
1360 		case S_HEX:
1361 			heading = inputbox_instructions_hex;
1362 			break;
1363 		case S_STRING:
1364 			heading = inputbox_instructions_string;
1365 			break;
1366 		default:
1367 			heading = "Internal nconf error!";
1368 		}
1369 		res = dialog_inputbox(main_window,
1370 				prompt ? prompt : "Main Menu",
1371 				heading,
1372 				sym_get_string_value(menu->sym),
1373 				&dialog_input_result,
1374 				&dialog_input_result_len);
1375 		switch (res) {
1376 		case 0:
1377 			if (sym_set_string_value(menu->sym,
1378 						dialog_input_result))
1379 				return;
1380 			btn_dialog(main_window,
1381 				"You have made an invalid entry.", 0);
1382 			break;
1383 		case 1:
1384 			show_help(menu);
1385 			break;
1386 		case KEY_EXIT:
1387 			return;
1388 		}
1389 	}
1390 }
1391 
1392 static void conf_load(void)
1393 {
1394 	while (1) {
1395 		int res;
1396 		res = dialog_inputbox(main_window,
1397 				NULL, load_config_text,
1398 				filename,
1399 				&dialog_input_result,
1400 				&dialog_input_result_len);
1401 		switch (res) {
1402 		case 0:
1403 			if (!dialog_input_result[0])
1404 				return;
1405 			if (!conf_read(dialog_input_result)) {
1406 				set_config_filename(dialog_input_result);
1407 				conf_set_changed(true);
1408 				return;
1409 			}
1410 			btn_dialog(main_window, "File does not exist!", 0);
1411 			break;
1412 		case 1:
1413 			show_scroll_win(main_window,
1414 					"Load Alternate Configuration",
1415 					load_config_help);
1416 			break;
1417 		case KEY_EXIT:
1418 			return;
1419 		}
1420 	}
1421 }
1422 
1423 static void conf_save(void)
1424 {
1425 	while (1) {
1426 		int res;
1427 		res = dialog_inputbox(main_window,
1428 				NULL, save_config_text,
1429 				filename,
1430 				&dialog_input_result,
1431 				&dialog_input_result_len);
1432 		switch (res) {
1433 		case 0:
1434 			if (!dialog_input_result[0])
1435 				return;
1436 			res = conf_write(dialog_input_result);
1437 			if (!res) {
1438 				set_config_filename(dialog_input_result);
1439 				return;
1440 			}
1441 			btn_dialog(main_window, "Can't create file!",
1442 				1, "<OK>");
1443 			break;
1444 		case 1:
1445 			show_scroll_win(main_window,
1446 				"Save Alternate Configuration",
1447 				save_config_help);
1448 			break;
1449 		case KEY_EXIT:
1450 			return;
1451 		}
1452 	}
1453 }
1454 
1455 static void setup_windows(void)
1456 {
1457 	int lines, columns;
1458 
1459 	getmaxyx(stdscr, lines, columns);
1460 
1461 	if (main_window != NULL)
1462 		delwin(main_window);
1463 
1464 	/* set up the menu and menu window */
1465 	main_window = newwin(lines-2, columns-2, 2, 1);
1466 	keypad(main_window, TRUE);
1467 	mwin_max_lines = lines-7;
1468 	mwin_max_cols = columns-6;
1469 
1470 	/* panels order is from bottom to top */
1471 	new_panel(main_window);
1472 }
1473 
1474 int main(int ac, char **av)
1475 {
1476 	int lines, columns;
1477 	char *mode;
1478 
1479 	if (ac > 1 && strcmp(av[1], "-s") == 0) {
1480 		/* Silence conf_read() until the real callback is set up */
1481 		conf_set_message_callback(NULL);
1482 		av++;
1483 	}
1484 	conf_parse(av[1]);
1485 	conf_read(NULL);
1486 
1487 	mode = getenv("NCONFIG_MODE");
1488 	if (mode) {
1489 		if (!strcasecmp(mode, "single_menu"))
1490 			single_menu_mode = 1;
1491 	}
1492 
1493 	/* Initialize curses */
1494 	initscr();
1495 	/* set color theme */
1496 	set_colors();
1497 
1498 	cbreak();
1499 	noecho();
1500 	keypad(stdscr, TRUE);
1501 	curs_set(0);
1502 
1503 	getmaxyx(stdscr, lines, columns);
1504 	if (columns < 75 || lines < 20) {
1505 		endwin();
1506 		printf("Your terminal should have at "
1507 			"least 20 lines and 75 columns\n");
1508 		return 1;
1509 	}
1510 
1511 	notimeout(stdscr, FALSE);
1512 #if NCURSES_REENTRANT
1513 	set_escdelay(1);
1514 #else
1515 	ESCDELAY = 1;
1516 #endif
1517 
1518 	/* set btns menu */
1519 	curses_menu = new_menu(curses_menu_items);
1520 	menu_opts_off(curses_menu, O_SHOWDESC);
1521 	menu_opts_on(curses_menu, O_SHOWMATCH);
1522 	menu_opts_on(curses_menu, O_ONEVALUE);
1523 	menu_opts_on(curses_menu, O_NONCYCLIC);
1524 	menu_opts_on(curses_menu, O_IGNORECASE);
1525 	set_menu_mark(curses_menu, " ");
1526 	set_menu_fore(curses_menu, attr_main_menu_fore);
1527 	set_menu_back(curses_menu, attr_main_menu_back);
1528 	set_menu_grey(curses_menu, attr_main_menu_grey);
1529 
1530 	set_config_filename(conf_get_configname());
1531 	setup_windows();
1532 
1533 	/* check for KEY_FUNC(1) */
1534 	if (has_key(KEY_F(1)) == FALSE) {
1535 		show_scroll_win(main_window,
1536 				"Instructions",
1537 				menu_no_f_instructions);
1538 	}
1539 
1540 	conf_set_message_callback(conf_message_callback);
1541 	/* do the work */
1542 	while (!global_exit) {
1543 		conf(&rootmenu);
1544 		if (!global_exit && do_exit() == 0)
1545 			break;
1546 	}
1547 	/* ok, we are done */
1548 	unpost_menu(curses_menu);
1549 	free_menu(curses_menu);
1550 	delwin(main_window);
1551 	clear();
1552 	refresh();
1553 	endwin();
1554 	return 0;
1555 }
1556