1 /*
2  * Copyright (C) 2008 Nir Tzachar <nir.tzachar@gmail.com?
3  * Released under the terms of the GNU GPL v2.0.
4  *
5  * Derived from menuconfig.
6  *
7  */
8 #ifndef _GNU_SOURCE
9 #define _GNU_SOURCE
10 #endif
11 #include <string.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 
print_function_line(void)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 */
handle_f1(int * key,struct menu * current_item)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 */
handle_f2(int * key,struct menu * current_item)396 static void handle_f2(int *key, struct menu *current_item)
397 {
398 	show_help(current_item);
399 	return;
400 }
401 
402 /* instructions */
handle_f3(int * key,struct menu * current_item)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 */
handle_f4(int * key,struct menu * current_item)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 */
handle_f5(int * key,struct menu * current_item)428 static void handle_f5(int *key, struct menu *current_item)
429 {
430 	*key = KEY_LEFT;
431 	return;
432 }
433 
434 /* save */
handle_f6(int * key,struct menu * current_item)435 static void handle_f6(int *key, struct menu *current_item)
436 {
437 	conf_save();
438 	return;
439 }
440 
441 /* load */
handle_f7(int * key,struct menu * current_item)442 static void handle_f7(int *key, struct menu *current_item)
443 {
444 	conf_load();
445 	return;
446 }
447 
448 /* search */
handle_f8(int * key,struct menu * current_item)449 static void handle_f8(int *key, struct menu *current_item)
450 {
451 	search_conf();
452 	return;
453 }
454 
455 /* exit */
handle_f9(int * key,struct menu * current_item)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 */
process_special_keys(int * key,struct menu * menu)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 
clean_items(void)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 */
get_mext_match(const char * match_str,match_f flag)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. */
item_make(struct menu * menu,char tag,const char * fmt,...)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 */
item_add_str(const char * fmt,...)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 */
item_tag(void)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 
curses_item_index(void)605 static int curses_item_index(void)
606 {
607 	return  item_index(current_item(curses_menu));
608 }
609 
item_data(void)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 
item_is_tag(char tag)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];
set_config_filename(const char * config_filename)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  */
do_exit(void)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 		break;
678 	default:
679 		btn_dialog(
680 			main_window,
681 			"Your configuration changes were NOT saved.",
682 			1,
683 			"<OK>");
684 		break;
685 	}
686 	global_exit = 1;
687 	return 0;
688 }
689 
search_conf(void)690 static void search_conf(void)
691 {
692 	struct symbol **sym_arr;
693 	struct gstr res;
694 	struct gstr title;
695 	char *dialog_input;
696 	int dres;
697 
698 	title = str_new();
699 	str_printf( &title, "Enter (sub)string or regexp to search for "
700 			      "(with or without \"%s\")", CONFIG_);
701 
702 again:
703 	dres = dialog_inputbox(main_window,
704 			"Search Configuration Parameter",
705 			str_get(&title),
706 			"", &dialog_input_result, &dialog_input_result_len);
707 	switch (dres) {
708 	case 0:
709 		break;
710 	case 1:
711 		show_scroll_win(main_window,
712 				"Search Configuration", search_help);
713 		goto again;
714 	default:
715 		str_free(&title);
716 		return;
717 	}
718 
719 	/* strip the prefix if necessary */
720 	dialog_input = dialog_input_result;
721 	if (strncasecmp(dialog_input_result, CONFIG_, strlen(CONFIG_)) == 0)
722 		dialog_input += strlen(CONFIG_);
723 
724 	sym_arr = sym_re_search(dialog_input);
725 	res = get_relations_str(sym_arr, NULL);
726 	free(sym_arr);
727 	show_scroll_win(main_window,
728 			"Search Results", str_get(&res));
729 	str_free(&res);
730 	str_free(&title);
731 }
732 
build_conf(struct menu * menu)733 static void build_conf(struct menu *menu)
734 {
735 	struct symbol *sym;
736 	struct property *prop;
737 	struct menu *child;
738 	int type, tmp, doint = 2;
739 	tristate val;
740 	char ch;
741 
742 	if (!menu || (!show_all_items && !menu_is_visible(menu)))
743 		return;
744 
745 	sym = menu->sym;
746 	prop = menu->prompt;
747 	if (!sym) {
748 		if (prop && menu != current_menu) {
749 			const char *prompt = menu_get_prompt(menu);
750 			enum prop_type ptype;
751 			ptype = menu->prompt ? menu->prompt->type : P_UNKNOWN;
752 			switch (ptype) {
753 			case P_MENU:
754 				child_count++;
755 				prompt = prompt;
756 				if (single_menu_mode) {
757 					item_make(menu, 'm',
758 						"%s%*c%s",
759 						menu->data ? "-->" : "++>",
760 						indent + 1, ' ', prompt);
761 				} else
762 					item_make(menu, 'm',
763 						  "   %*c%s  %s",
764 						  indent + 1, ' ', prompt,
765 						  menu_is_empty(menu) ? "----" : "--->");
766 
767 				if (single_menu_mode && menu->data)
768 					goto conf_childs;
769 				return;
770 			case P_COMMENT:
771 				if (prompt) {
772 					child_count++;
773 					item_make(menu, ':',
774 						"   %*c*** %s ***",
775 						indent + 1, ' ',
776 						prompt);
777 				}
778 				break;
779 			default:
780 				if (prompt) {
781 					child_count++;
782 					item_make(menu, ':', "---%*c%s",
783 						indent + 1, ' ',
784 						prompt);
785 				}
786 			}
787 		} else
788 			doint = 0;
789 		goto conf_childs;
790 	}
791 
792 	type = sym_get_type(sym);
793 	if (sym_is_choice(sym)) {
794 		struct symbol *def_sym = sym_get_choice_value(sym);
795 		struct menu *def_menu = NULL;
796 
797 		child_count++;
798 		for (child = menu->list; child; child = child->next) {
799 			if (menu_is_visible(child) && child->sym == def_sym)
800 				def_menu = child;
801 		}
802 
803 		val = sym_get_tristate_value(sym);
804 		if (sym_is_changable(sym)) {
805 			switch (type) {
806 			case S_BOOLEAN:
807 				item_make(menu, 't', "[%c]",
808 						val == no ? ' ' : '*');
809 				break;
810 			case S_TRISTATE:
811 				switch (val) {
812 				case yes:
813 					ch = '*';
814 					break;
815 				case mod:
816 					ch = 'M';
817 					break;
818 				default:
819 					ch = ' ';
820 					break;
821 				}
822 				item_make(menu, 't', "<%c>", ch);
823 				break;
824 			}
825 		} else {
826 			item_make(menu, def_menu ? 't' : ':', "   ");
827 		}
828 
829 		item_add_str("%*c%s", indent + 1,
830 				' ', menu_get_prompt(menu));
831 		if (val == yes) {
832 			if (def_menu) {
833 				item_add_str(" (%s)",
834 					menu_get_prompt(def_menu));
835 				item_add_str("  --->");
836 				if (def_menu->list) {
837 					indent += 2;
838 					build_conf(def_menu);
839 					indent -= 2;
840 				}
841 			}
842 			return;
843 		}
844 	} else {
845 		if (menu == current_menu) {
846 			item_make(menu, ':',
847 				"---%*c%s", indent + 1,
848 				' ', menu_get_prompt(menu));
849 			goto conf_childs;
850 		}
851 		child_count++;
852 		val = sym_get_tristate_value(sym);
853 		if (sym_is_choice_value(sym) && val == yes) {
854 			item_make(menu, ':', "   ");
855 		} else {
856 			switch (type) {
857 			case S_BOOLEAN:
858 				if (sym_is_changable(sym))
859 					item_make(menu, 't', "[%c]",
860 						val == no ? ' ' : '*');
861 				else
862 					item_make(menu, 't', "-%c-",
863 						val == no ? ' ' : '*');
864 				break;
865 			case S_TRISTATE:
866 				switch (val) {
867 				case yes:
868 					ch = '*';
869 					break;
870 				case mod:
871 					ch = 'M';
872 					break;
873 				default:
874 					ch = ' ';
875 					break;
876 				}
877 				if (sym_is_changable(sym)) {
878 					if (sym->rev_dep.tri == mod)
879 						item_make(menu,
880 							't', "{%c}", ch);
881 					else
882 						item_make(menu,
883 							't', "<%c>", ch);
884 				} else
885 					item_make(menu, 't', "-%c-", ch);
886 				break;
887 			default:
888 				tmp = 2 + strlen(sym_get_string_value(sym));
889 				item_make(menu, 's', "    (%s)",
890 						sym_get_string_value(sym));
891 				tmp = indent - tmp + 4;
892 				if (tmp < 0)
893 					tmp = 0;
894 				item_add_str("%*c%s%s", tmp, ' ',
895 						menu_get_prompt(menu),
896 						(sym_has_value(sym) ||
897 						 !sym_is_changable(sym)) ? "" :
898 						" (NEW)");
899 				goto conf_childs;
900 			}
901 		}
902 		item_add_str("%*c%s%s", indent + 1, ' ',
903 				menu_get_prompt(menu),
904 				(sym_has_value(sym) || !sym_is_changable(sym)) ?
905 				"" : " (NEW)");
906 		if (menu->prompt && menu->prompt->type == P_MENU) {
907 			item_add_str("  %s", menu_is_empty(menu) ? "----" : "--->");
908 			return;
909 		}
910 	}
911 
912 conf_childs:
913 	indent += doint;
914 	for (child = menu->list; child; child = child->next)
915 		build_conf(child);
916 	indent -= doint;
917 }
918 
reset_menu(void)919 static void reset_menu(void)
920 {
921 	unpost_menu(curses_menu);
922 	clean_items();
923 }
924 
925 /* adjust the menu to show this item.
926  * prefer not to scroll the menu if possible*/
center_item(int selected_index,int * last_top_row)927 static void center_item(int selected_index, int *last_top_row)
928 {
929 	int toprow;
930 
931 	set_top_row(curses_menu, *last_top_row);
932 	toprow = top_row(curses_menu);
933 	if (selected_index < toprow ||
934 	    selected_index >= toprow+mwin_max_lines) {
935 		toprow = max(selected_index-mwin_max_lines/2, 0);
936 		if (toprow >= item_count(curses_menu)-mwin_max_lines)
937 			toprow = item_count(curses_menu)-mwin_max_lines;
938 		set_top_row(curses_menu, toprow);
939 	}
940 	set_current_item(curses_menu,
941 			curses_menu_items[selected_index]);
942 	*last_top_row = toprow;
943 	post_menu(curses_menu);
944 	refresh_all_windows(main_window);
945 }
946 
947 /* this function assumes reset_menu has been called before */
show_menu(const char * prompt,const char * instructions,int selected_index,int * last_top_row)948 static void show_menu(const char *prompt, const char *instructions,
949 		int selected_index, int *last_top_row)
950 {
951 	int maxx, maxy;
952 	WINDOW *menu_window;
953 
954 	current_instructions = instructions;
955 
956 	clear();
957 	(void) wattrset(main_window, attributes[NORMAL]);
958 	print_in_middle(stdscr, 1, 0, getmaxx(stdscr),
959 			menu_backtitle,
960 			attributes[MAIN_HEADING]);
961 
962 	(void) wattrset(main_window, attributes[MAIN_MENU_BOX]);
963 	box(main_window, 0, 0);
964 	(void) wattrset(main_window, attributes[MAIN_MENU_HEADING]);
965 	mvwprintw(main_window, 0, 3, " %s ", prompt);
966 	(void) wattrset(main_window, attributes[NORMAL]);
967 
968 	set_menu_items(curses_menu, curses_menu_items);
969 
970 	/* position the menu at the middle of the screen */
971 	scale_menu(curses_menu, &maxy, &maxx);
972 	maxx = min(maxx, mwin_max_cols-2);
973 	maxy = mwin_max_lines;
974 	menu_window = derwin(main_window,
975 			maxy,
976 			maxx,
977 			2,
978 			(mwin_max_cols-maxx)/2);
979 	keypad(menu_window, TRUE);
980 	set_menu_win(curses_menu, menu_window);
981 	set_menu_sub(curses_menu, menu_window);
982 
983 	/* must reassert this after changing items, otherwise returns to a
984 	 * default of 16
985 	 */
986 	set_menu_format(curses_menu, maxy, 1);
987 	center_item(selected_index, last_top_row);
988 	set_menu_format(curses_menu, maxy, 1);
989 
990 	print_function_line();
991 
992 	/* Post the menu */
993 	post_menu(curses_menu);
994 	refresh_all_windows(main_window);
995 }
996 
adj_match_dir(match_f * match_direction)997 static void adj_match_dir(match_f *match_direction)
998 {
999 	if (*match_direction == FIND_NEXT_MATCH_DOWN)
1000 		*match_direction =
1001 			MATCH_TINKER_PATTERN_DOWN;
1002 	else if (*match_direction == FIND_NEXT_MATCH_UP)
1003 		*match_direction =
1004 			MATCH_TINKER_PATTERN_UP;
1005 	/* else, do no change.. */
1006 }
1007 
1008 struct match_state
1009 {
1010 	int in_search;
1011 	match_f match_direction;
1012 	char pattern[256];
1013 };
1014 
1015 /* Return 0 means I have handled the key. In such a case, ans should hold the
1016  * item to center, or -1 otherwise.
1017  * Else return -1 .
1018  */
do_match(int key,struct match_state * state,int * ans)1019 static int do_match(int key, struct match_state *state, int *ans)
1020 {
1021 	char c = (char) key;
1022 	int terminate_search = 0;
1023 	*ans = -1;
1024 	if (key == '/' || (state->in_search && key == 27)) {
1025 		move(0, 0);
1026 		refresh();
1027 		clrtoeol();
1028 		state->in_search = 1-state->in_search;
1029 		bzero(state->pattern, sizeof(state->pattern));
1030 		state->match_direction = MATCH_TINKER_PATTERN_DOWN;
1031 		return 0;
1032 	} else if (!state->in_search)
1033 		return 1;
1034 
1035 	if (isalnum(c) || isgraph(c) || c == ' ') {
1036 		state->pattern[strlen(state->pattern)] = c;
1037 		state->pattern[strlen(state->pattern)] = '\0';
1038 		adj_match_dir(&state->match_direction);
1039 		*ans = get_mext_match(state->pattern,
1040 				state->match_direction);
1041 	} else if (key == KEY_DOWN) {
1042 		state->match_direction = FIND_NEXT_MATCH_DOWN;
1043 		*ans = get_mext_match(state->pattern,
1044 				state->match_direction);
1045 	} else if (key == KEY_UP) {
1046 		state->match_direction = FIND_NEXT_MATCH_UP;
1047 		*ans = get_mext_match(state->pattern,
1048 				state->match_direction);
1049 	} else if (key == KEY_BACKSPACE || key == 127) {
1050 		state->pattern[strlen(state->pattern)-1] = '\0';
1051 		adj_match_dir(&state->match_direction);
1052 	} else
1053 		terminate_search = 1;
1054 
1055 	if (terminate_search) {
1056 		state->in_search = 0;
1057 		bzero(state->pattern, sizeof(state->pattern));
1058 		move(0, 0);
1059 		refresh();
1060 		clrtoeol();
1061 		return -1;
1062 	}
1063 	return 0;
1064 }
1065 
conf(struct menu * menu)1066 static void conf(struct menu *menu)
1067 {
1068 	struct menu *submenu = NULL;
1069 	const char *prompt = menu_get_prompt(menu);
1070 	struct symbol *sym;
1071 	int res;
1072 	int current_index = 0;
1073 	int last_top_row = 0;
1074 	struct match_state match_state = {
1075 		.in_search = 0,
1076 		.match_direction = MATCH_TINKER_PATTERN_DOWN,
1077 		.pattern = "",
1078 	};
1079 
1080 	while (!global_exit) {
1081 		reset_menu();
1082 		current_menu = menu;
1083 		build_conf(menu);
1084 		if (!child_count)
1085 			break;
1086 
1087 		show_menu(prompt ? prompt : "Main Menu",
1088 				menu_instructions,
1089 				current_index, &last_top_row);
1090 		keypad((menu_win(curses_menu)), TRUE);
1091 		while (!global_exit) {
1092 			if (match_state.in_search) {
1093 				mvprintw(0, 0,
1094 					"searching: %s", match_state.pattern);
1095 				clrtoeol();
1096 			}
1097 			refresh_all_windows(main_window);
1098 			res = wgetch(menu_win(curses_menu));
1099 			if (!res)
1100 				break;
1101 			if (do_match(res, &match_state, &current_index) == 0) {
1102 				if (current_index != -1)
1103 					center_item(current_index,
1104 						    &last_top_row);
1105 				continue;
1106 			}
1107 			if (process_special_keys(&res,
1108 						(struct menu *) item_data()))
1109 				break;
1110 			switch (res) {
1111 			case KEY_DOWN:
1112 				menu_driver(curses_menu, REQ_DOWN_ITEM);
1113 				break;
1114 			case KEY_UP:
1115 				menu_driver(curses_menu, REQ_UP_ITEM);
1116 				break;
1117 			case KEY_NPAGE:
1118 				menu_driver(curses_menu, REQ_SCR_DPAGE);
1119 				break;
1120 			case KEY_PPAGE:
1121 				menu_driver(curses_menu, REQ_SCR_UPAGE);
1122 				break;
1123 			case KEY_HOME:
1124 				menu_driver(curses_menu, REQ_FIRST_ITEM);
1125 				break;
1126 			case KEY_END:
1127 				menu_driver(curses_menu, REQ_LAST_ITEM);
1128 				break;
1129 			case 'h':
1130 			case '?':
1131 				show_help((struct menu *) item_data());
1132 				break;
1133 			}
1134 			if (res == 10 || res == 27 ||
1135 				res == 32 || res == 'n' || res == 'y' ||
1136 				res == KEY_LEFT || res == KEY_RIGHT ||
1137 				res == 'm')
1138 				break;
1139 			refresh_all_windows(main_window);
1140 		}
1141 
1142 		refresh_all_windows(main_window);
1143 		/* if ESC or left*/
1144 		if (res == 27 || (menu != &rootmenu && res == KEY_LEFT))
1145 			break;
1146 
1147 		/* remember location in the menu */
1148 		last_top_row = top_row(curses_menu);
1149 		current_index = curses_item_index();
1150 
1151 		if (!item_tag())
1152 			continue;
1153 
1154 		submenu = (struct menu *) item_data();
1155 		if (!submenu || !menu_is_visible(submenu))
1156 			continue;
1157 		sym = submenu->sym;
1158 
1159 		switch (res) {
1160 		case ' ':
1161 			if (item_is_tag('t'))
1162 				sym_toggle_tristate_value(sym);
1163 			else if (item_is_tag('m'))
1164 				conf(submenu);
1165 			break;
1166 		case KEY_RIGHT:
1167 		case 10: /* ENTER WAS PRESSED */
1168 			switch (item_tag()) {
1169 			case 'm':
1170 				if (single_menu_mode)
1171 					submenu->data =
1172 						(void *) (long) !submenu->data;
1173 				else
1174 					conf(submenu);
1175 				break;
1176 			case 't':
1177 				if (sym_is_choice(sym) &&
1178 				    sym_get_tristate_value(sym) == yes)
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 
conf_message_callback(const char * s)1211 static void conf_message_callback(const char *s)
1212 {
1213 	btn_dialog(main_window, s, 1, "<OK>");
1214 }
1215 
show_help(struct menu * menu)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 
conf_choice(struct menu * menu)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_get_choice_value(menu->sym);
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_get_choice_value(menu->sym))
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 				menu_driver(curses_menu, REQ_DOWN_ITEM);
1295 				break;
1296 			case KEY_UP:
1297 				menu_driver(curses_menu, REQ_UP_ITEM);
1298 				break;
1299 			case KEY_NPAGE:
1300 				menu_driver(curses_menu, REQ_SCR_DPAGE);
1301 				break;
1302 			case KEY_PPAGE:
1303 				menu_driver(curses_menu, REQ_SCR_UPAGE);
1304 				break;
1305 			case KEY_HOME:
1306 				menu_driver(curses_menu, REQ_FIRST_ITEM);
1307 				break;
1308 			case KEY_END:
1309 				menu_driver(curses_menu, REQ_LAST_ITEM);
1310 				break;
1311 			case 'h':
1312 			case '?':
1313 				show_help((struct menu *) item_data());
1314 				break;
1315 			}
1316 			if (res == 10 || res == 27 || res == ' ' ||
1317 					res == KEY_LEFT){
1318 				break;
1319 			}
1320 			refresh_all_windows(main_window);
1321 		}
1322 		/* if ESC or left */
1323 		if (res == 27 || res == KEY_LEFT)
1324 			break;
1325 
1326 		child = item_data();
1327 		if (!child || !menu_is_visible(child) || !child->sym)
1328 			continue;
1329 		switch (res) {
1330 		case ' ':
1331 		case  10:
1332 		case KEY_RIGHT:
1333 			sym_set_tristate_value(child->sym, yes);
1334 			return;
1335 		case 'h':
1336 		case '?':
1337 			show_help(child);
1338 			active = child->sym;
1339 			break;
1340 		case KEY_EXIT:
1341 			return;
1342 		}
1343 	}
1344 }
1345 
conf_string(struct menu * menu)1346 static void conf_string(struct menu *menu)
1347 {
1348 	const char *prompt = menu_get_prompt(menu);
1349 
1350 	while (1) {
1351 		int res;
1352 		const char *heading;
1353 
1354 		switch (sym_get_type(menu->sym)) {
1355 		case S_INT:
1356 			heading = inputbox_instructions_int;
1357 			break;
1358 		case S_HEX:
1359 			heading = inputbox_instructions_hex;
1360 			break;
1361 		case S_STRING:
1362 			heading = inputbox_instructions_string;
1363 			break;
1364 		default:
1365 			heading = "Internal nconf error!";
1366 		}
1367 		res = dialog_inputbox(main_window,
1368 				prompt ? prompt : "Main Menu",
1369 				heading,
1370 				sym_get_string_value(menu->sym),
1371 				&dialog_input_result,
1372 				&dialog_input_result_len);
1373 		switch (res) {
1374 		case 0:
1375 			if (sym_set_string_value(menu->sym,
1376 						dialog_input_result))
1377 				return;
1378 			btn_dialog(main_window,
1379 				"You have made an invalid entry.", 0);
1380 			break;
1381 		case 1:
1382 			show_help(menu);
1383 			break;
1384 		case KEY_EXIT:
1385 			return;
1386 		}
1387 	}
1388 }
1389 
conf_load(void)1390 static void conf_load(void)
1391 {
1392 	while (1) {
1393 		int res;
1394 		res = dialog_inputbox(main_window,
1395 				NULL, load_config_text,
1396 				filename,
1397 				&dialog_input_result,
1398 				&dialog_input_result_len);
1399 		switch (res) {
1400 		case 0:
1401 			if (!dialog_input_result[0])
1402 				return;
1403 			if (!conf_read(dialog_input_result)) {
1404 				set_config_filename(dialog_input_result);
1405 				sym_set_change_count(1);
1406 				return;
1407 			}
1408 			btn_dialog(main_window, "File does not exist!", 0);
1409 			break;
1410 		case 1:
1411 			show_scroll_win(main_window,
1412 					"Load Alternate Configuration",
1413 					load_config_help);
1414 			break;
1415 		case KEY_EXIT:
1416 			return;
1417 		}
1418 	}
1419 }
1420 
conf_save(void)1421 static void conf_save(void)
1422 {
1423 	while (1) {
1424 		int res;
1425 		res = dialog_inputbox(main_window,
1426 				NULL, save_config_text,
1427 				filename,
1428 				&dialog_input_result,
1429 				&dialog_input_result_len);
1430 		switch (res) {
1431 		case 0:
1432 			if (!dialog_input_result[0])
1433 				return;
1434 			res = conf_write(dialog_input_result);
1435 			if (!res) {
1436 				set_config_filename(dialog_input_result);
1437 				return;
1438 			}
1439 			btn_dialog(main_window, "Can't create file! "
1440 				"Probably a nonexistent directory.",
1441 				1, "<OK>");
1442 			break;
1443 		case 1:
1444 			show_scroll_win(main_window,
1445 				"Save Alternate Configuration",
1446 				save_config_help);
1447 			break;
1448 		case KEY_EXIT:
1449 			return;
1450 		}
1451 	}
1452 }
1453 
setup_windows(void)1454 static void setup_windows(void)
1455 {
1456 	int lines, columns;
1457 
1458 	getmaxyx(stdscr, lines, columns);
1459 
1460 	if (main_window != NULL)
1461 		delwin(main_window);
1462 
1463 	/* set up the menu and menu window */
1464 	main_window = newwin(lines-2, columns-2, 2, 1);
1465 	keypad(main_window, TRUE);
1466 	mwin_max_lines = lines-7;
1467 	mwin_max_cols = columns-6;
1468 
1469 	/* panels order is from bottom to top */
1470 	new_panel(main_window);
1471 }
1472 
main(int ac,char ** av)1473 int main(int ac, char **av)
1474 {
1475 	int lines, columns;
1476 	char *mode;
1477 
1478 	if (ac > 1 && strcmp(av[1], "-s") == 0) {
1479 		/* Silence conf_read() until the real callback is set up */
1480 		conf_set_message_callback(NULL);
1481 		av++;
1482 	}
1483 	conf_parse(av[1]);
1484 	conf_read(NULL);
1485 
1486 	mode = getenv("NCONFIG_MODE");
1487 	if (mode) {
1488 		if (!strcasecmp(mode, "single_menu"))
1489 			single_menu_mode = 1;
1490 	}
1491 
1492 	/* Initialize curses */
1493 	initscr();
1494 	/* set color theme */
1495 	set_colors();
1496 
1497 	cbreak();
1498 	noecho();
1499 	keypad(stdscr, TRUE);
1500 	curs_set(0);
1501 
1502 	getmaxyx(stdscr, lines, columns);
1503 	if (columns < 75 || lines < 20) {
1504 		endwin();
1505 		printf("Your terminal should have at "
1506 			"least 20 lines and 75 columns\n");
1507 		return 1;
1508 	}
1509 
1510 	notimeout(stdscr, FALSE);
1511 #if NCURSES_REENTRANT
1512 	set_escdelay(1);
1513 #else
1514 	ESCDELAY = 1;
1515 #endif
1516 
1517 	/* set btns menu */
1518 	curses_menu = new_menu(curses_menu_items);
1519 	menu_opts_off(curses_menu, O_SHOWDESC);
1520 	menu_opts_on(curses_menu, O_SHOWMATCH);
1521 	menu_opts_on(curses_menu, O_ONEVALUE);
1522 	menu_opts_on(curses_menu, O_NONCYCLIC);
1523 	menu_opts_on(curses_menu, O_IGNORECASE);
1524 	set_menu_mark(curses_menu, " ");
1525 	set_menu_fore(curses_menu, attributes[MAIN_MENU_FORE]);
1526 	set_menu_back(curses_menu, attributes[MAIN_MENU_BACK]);
1527 	set_menu_grey(curses_menu, attributes[MAIN_MENU_GREY]);
1528 
1529 	set_config_filename(conf_get_configname());
1530 	setup_windows();
1531 
1532 	/* check for KEY_FUNC(1) */
1533 	if (has_key(KEY_F(1)) == FALSE) {
1534 		show_scroll_win(main_window,
1535 				"Instructions",
1536 				menu_no_f_instructions);
1537 	}
1538 
1539 	conf_set_message_callback(conf_message_callback);
1540 	/* do the work */
1541 	while (!global_exit) {
1542 		conf(&rootmenu);
1543 		if (!global_exit && do_exit() == 0)
1544 			break;
1545 	}
1546 	/* ok, we are done */
1547 	unpost_menu(curses_menu);
1548 	free_menu(curses_menu);
1549 	delwin(main_window);
1550 	clear();
1551 	refresh();
1552 	endwin();
1553 	return 0;
1554 }
1555