1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * Copyright (C) 2008 Nir Tzachar <nir.tzachar@gmail.com>
4  *
5  * Derived from menuconfig.
6  */
7 #ifndef _GNU_SOURCE
8 #define _GNU_SOURCE
9 #endif
10 #include <string.h>
11 #include <strings.h>
12 #include <stdlib.h>
13 
14 #include "lkc.h"
15 #include "nconf.h"
16 #include <ctype.h>
17 
18 static const char nconf_global_help[] =
19 "Help windows\n"
20 "------------\n"
21 "o  Global help:  Unless in a data entry window, pressing <F1> will give \n"
22 "   you the global help window, which you are just reading.\n"
23 "\n"
24 "o  A short version of the global help is available by pressing <F3>.\n"
25 "\n"
26 "o  Local help:  To get help related to the current menu entry, use any\n"
27 "   of <?> <h>, or if in a data entry window then press <F1>.\n"
28 "\n"
29 "\n"
30 "Menu entries\n"
31 "------------\n"
32 "This interface lets you select features and parameters for the kernel\n"
33 "build.  Kernel features can either be built-in, modularized, or removed.\n"
34 "Parameters must be entered as text or decimal or hexadecimal numbers.\n"
35 "\n"
36 "Menu entries beginning with following braces represent features that\n"
37 "  [ ]  can be built in or removed\n"
38 "  < >  can be built in, modularized or removed\n"
39 "  { }  can be built in or modularized, are selected by another feature\n"
40 "  - -  are selected by another feature\n"
41 "  XXX  cannot be selected.  Symbol Info <F2> tells you why.\n"
42 "*, M or whitespace inside braces means to build in, build as a module\n"
43 "or to exclude the feature respectively.\n"
44 "\n"
45 "To change any of these features, highlight it with the movement keys\n"
46 "listed below and press <y> to build it in, <m> to make it a module or\n"
47 "<n> to remove it.  You may press the <Space> key to cycle through the\n"
48 "available options.\n"
49 "\n"
50 "A trailing \"--->\" designates a submenu, a trailing \"----\" an\n"
51 "empty submenu.\n"
52 "\n"
53 "Menu navigation keys\n"
54 "----------------------------------------------------------------------\n"
55 "Linewise up                 <Up>    <k>\n"
56 "Linewise down               <Down>  <j>\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 unsigned 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 		wattrset(main_window, attr_function_highlight);
374 		mvwprintw(main_window, lines-3, offset,
375 				"%s",
376 				function_keys[i].key_str);
377 		wattrset(main_window, attr_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 	wattrset(main_window, attr_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, index;
500 
501 	/* Do not search if the menu is empty (i.e. items_num == 0) */
502 	match_start = item_index(current_item(curses_menu));
503 	if (match_start == ERR)
504 		return -1;
505 
506 	if (flag == FIND_NEXT_MATCH_DOWN)
507 		++match_start;
508 	else if (flag == FIND_NEXT_MATCH_UP)
509 		--match_start;
510 
511 	match_start = (match_start + items_num) % items_num;
512 	index = match_start;
513 	while (true) {
514 		char *str = k_menu_items[index].str;
515 		if (strcasestr(str, match_str) != NULL)
516 			return index;
517 		if (flag == FIND_NEXT_MATCH_UP ||
518 		    flag == MATCH_TINKER_PATTERN_UP)
519 			--index;
520 		else
521 			++index;
522 		index = (index + items_num) % items_num;
523 		if (index == match_start)
524 			return -1;
525 	}
526 }
527 
528 /* Make a new item. */
item_make(struct menu * menu,char tag,const char * fmt,...)529 static void item_make(struct menu *menu, char tag, const char *fmt, ...)
530 {
531 	va_list ap;
532 
533 	if (items_num > MAX_MENU_ITEMS-1)
534 		return;
535 
536 	bzero(&k_menu_items[items_num], sizeof(k_menu_items[0]));
537 	k_menu_items[items_num].tag = tag;
538 	k_menu_items[items_num].usrptr = menu;
539 	if (menu != NULL)
540 		k_menu_items[items_num].is_visible =
541 			menu_is_visible(menu);
542 	else
543 		k_menu_items[items_num].is_visible = 1;
544 
545 	va_start(ap, fmt);
546 	vsnprintf(k_menu_items[items_num].str,
547 		  sizeof(k_menu_items[items_num].str),
548 		  fmt, ap);
549 	va_end(ap);
550 
551 	if (!k_menu_items[items_num].is_visible)
552 		memcpy(k_menu_items[items_num].str, "XXX", 3);
553 
554 	curses_menu_items[items_num] = new_item(
555 			k_menu_items[items_num].str,
556 			k_menu_items[items_num].str);
557 	set_item_userptr(curses_menu_items[items_num],
558 			&k_menu_items[items_num]);
559 	/*
560 	if (!k_menu_items[items_num].is_visible)
561 		item_opts_off(curses_menu_items[items_num], O_SELECTABLE);
562 	*/
563 
564 	items_num++;
565 	curses_menu_items[items_num] = NULL;
566 }
567 
568 /* very hackish. adds a string to the last item added */
item_add_str(const char * fmt,...)569 static void item_add_str(const char *fmt, ...)
570 {
571 	va_list ap;
572 	int index = items_num-1;
573 	char new_str[256];
574 	char tmp_str[256];
575 
576 	if (index < 0)
577 		return;
578 
579 	va_start(ap, fmt);
580 	vsnprintf(new_str, sizeof(new_str), fmt, ap);
581 	va_end(ap);
582 	snprintf(tmp_str, sizeof(tmp_str), "%s%s",
583 			k_menu_items[index].str, new_str);
584 	strncpy(k_menu_items[index].str,
585 		tmp_str,
586 		sizeof(k_menu_items[index].str));
587 
588 	free_item(curses_menu_items[index]);
589 	curses_menu_items[index] = new_item(
590 			k_menu_items[index].str,
591 			k_menu_items[index].str);
592 	set_item_userptr(curses_menu_items[index],
593 			&k_menu_items[index]);
594 }
595 
596 /* get the tag of the currently selected item */
item_tag(void)597 static char item_tag(void)
598 {
599 	ITEM *cur;
600 	struct mitem *mcur;
601 
602 	cur = current_item(curses_menu);
603 	if (cur == NULL)
604 		return 0;
605 	mcur = (struct mitem *) item_userptr(cur);
606 	return mcur->tag;
607 }
608 
curses_item_index(void)609 static int curses_item_index(void)
610 {
611 	return  item_index(current_item(curses_menu));
612 }
613 
item_data(void)614 static void *item_data(void)
615 {
616 	ITEM *cur;
617 	struct mitem *mcur;
618 
619 	cur = current_item(curses_menu);
620 	if (!cur)
621 		return NULL;
622 	mcur = (struct mitem *) item_userptr(cur);
623 	return mcur->usrptr;
624 
625 }
626 
item_is_tag(char tag)627 static int item_is_tag(char tag)
628 {
629 	return item_tag() == tag;
630 }
631 
632 static char filename[PATH_MAX+1];
633 static char menu_backtitle[PATH_MAX+128];
set_config_filename(const char * config_filename)634 static void set_config_filename(const char *config_filename)
635 {
636 	snprintf(menu_backtitle, sizeof(menu_backtitle), "%s - %s",
637 		 config_filename, rootmenu.prompt->text);
638 
639 	snprintf(filename, sizeof(filename), "%s", config_filename);
640 }
641 
642 /* return = 0 means we are successful.
643  * -1 means go on doing what you were doing
644  */
do_exit(void)645 static int do_exit(void)
646 {
647 	int res;
648 	if (!conf_get_changed()) {
649 		global_exit = 1;
650 		return 0;
651 	}
652 	res = btn_dialog(main_window,
653 			"Do you wish to save your new configuration?\n"
654 				"<ESC> to cancel and resume nconfig.",
655 			2,
656 			"   <save>   ",
657 			"<don't save>");
658 	if (res == KEY_EXIT) {
659 		global_exit = 0;
660 		return -1;
661 	}
662 
663 	/* if we got here, the user really wants to exit */
664 	switch (res) {
665 	case 0:
666 		res = conf_write(filename);
667 		if (res)
668 			btn_dialog(
669 				main_window,
670 				"Error during writing of configuration.\n"
671 				  "Your configuration changes were NOT saved.",
672 				  1,
673 				  "<OK>");
674 		conf_write_autoconf(0);
675 		break;
676 	default:
677 		btn_dialog(
678 			main_window,
679 			"Your configuration changes were NOT saved.",
680 			1,
681 			"<OK>");
682 		break;
683 	}
684 	global_exit = 1;
685 	return 0;
686 }
687 
688 
search_conf(void)689 static void search_conf(void)
690 {
691 	struct symbol **sym_arr;
692 	struct gstr res;
693 	struct gstr title;
694 	char *dialog_input;
695 	int dres;
696 
697 	title = str_new();
698 	str_printf( &title, "Enter (sub)string or regexp to search for "
699 			      "(with or without \"%s\")", CONFIG_);
700 
701 again:
702 	dres = dialog_inputbox(main_window,
703 			"Search Configuration Parameter",
704 			str_get(&title),
705 			"", &dialog_input_result, &dialog_input_result_len);
706 	switch (dres) {
707 	case 0:
708 		break;
709 	case 1:
710 		show_scroll_win(main_window,
711 				"Search Configuration", search_help);
712 		goto again;
713 	default:
714 		str_free(&title);
715 		return;
716 	}
717 
718 	/* strip the prefix if necessary */
719 	dialog_input = dialog_input_result;
720 	if (strncasecmp(dialog_input_result, CONFIG_, strlen(CONFIG_)) == 0)
721 		dialog_input += strlen(CONFIG_);
722 
723 	sym_arr = sym_re_search(dialog_input);
724 	res = get_relations_str(sym_arr, NULL);
725 	free(sym_arr);
726 	show_scroll_win(main_window,
727 			"Search Results", str_get(&res));
728 	str_free(&res);
729 	str_free(&title);
730 }
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 				if (single_menu_mode) {
756 					item_make(menu, 'm',
757 						"%s%*c%s",
758 						menu->data ? "-->" : "++>",
759 						indent + 1, ' ', prompt);
760 				} else
761 					item_make(menu, 'm',
762 						  "   %*c%s  %s",
763 						  indent + 1, ' ', prompt,
764 						  menu_is_empty(menu) ? "----" : "--->");
765 
766 				if (single_menu_mode && menu->data)
767 					goto conf_childs;
768 				return;
769 			case P_COMMENT:
770 				if (prompt) {
771 					child_count++;
772 					item_make(menu, ':',
773 						"   %*c*** %s ***",
774 						indent + 1, ' ',
775 						prompt);
776 				}
777 				break;
778 			default:
779 				if (prompt) {
780 					child_count++;
781 					item_make(menu, ':', "---%*c%s",
782 						indent + 1, ' ',
783 						prompt);
784 				}
785 			}
786 		} else
787 			doint = 0;
788 		goto conf_childs;
789 	}
790 
791 	type = sym_get_type(sym);
792 	if (sym_is_choice(sym)) {
793 		struct symbol *def_sym = sym_get_choice_value(sym);
794 		struct menu *def_menu = NULL;
795 
796 		child_count++;
797 		for (child = menu->list; child; child = child->next) {
798 			if (menu_is_visible(child) && child->sym == def_sym)
799 				def_menu = child;
800 		}
801 
802 		val = sym_get_tristate_value(sym);
803 		if (sym_is_changeable(sym)) {
804 			switch (type) {
805 			case S_BOOLEAN:
806 				item_make(menu, 't', "[%c]",
807 						val == no ? ' ' : '*');
808 				break;
809 			case S_TRISTATE:
810 				switch (val) {
811 				case yes:
812 					ch = '*';
813 					break;
814 				case mod:
815 					ch = 'M';
816 					break;
817 				default:
818 					ch = ' ';
819 					break;
820 				}
821 				item_make(menu, 't', "<%c>", ch);
822 				break;
823 			}
824 		} else {
825 			item_make(menu, def_menu ? 't' : ':', "   ");
826 		}
827 
828 		item_add_str("%*c%s", indent + 1,
829 				' ', menu_get_prompt(menu));
830 		if (val == yes) {
831 			if (def_menu) {
832 				item_add_str(" (%s)",
833 					menu_get_prompt(def_menu));
834 				item_add_str("  --->");
835 				if (def_menu->list) {
836 					indent += 2;
837 					build_conf(def_menu);
838 					indent -= 2;
839 				}
840 			}
841 			return;
842 		}
843 	} else {
844 		if (menu == current_menu) {
845 			item_make(menu, ':',
846 				"---%*c%s", indent + 1,
847 				' ', menu_get_prompt(menu));
848 			goto conf_childs;
849 		}
850 		child_count++;
851 		val = sym_get_tristate_value(sym);
852 		if (sym_is_choice_value(sym) && val == yes) {
853 			item_make(menu, ':', "   ");
854 		} else {
855 			switch (type) {
856 			case S_BOOLEAN:
857 				if (sym_is_changeable(sym))
858 					item_make(menu, 't', "[%c]",
859 						val == no ? ' ' : '*');
860 				else
861 					item_make(menu, 't', "-%c-",
862 						val == no ? ' ' : '*');
863 				break;
864 			case S_TRISTATE:
865 				switch (val) {
866 				case yes:
867 					ch = '*';
868 					break;
869 				case mod:
870 					ch = 'M';
871 					break;
872 				default:
873 					ch = ' ';
874 					break;
875 				}
876 				if (sym_is_changeable(sym)) {
877 					if (sym->rev_dep.tri == mod)
878 						item_make(menu,
879 							't', "{%c}", ch);
880 					else
881 						item_make(menu,
882 							't', "<%c>", ch);
883 				} else
884 					item_make(menu, 't', "-%c-", ch);
885 				break;
886 			default:
887 				tmp = 2 + strlen(sym_get_string_value(sym));
888 				item_make(menu, 's', "    (%s)",
889 						sym_get_string_value(sym));
890 				tmp = indent - tmp + 4;
891 				if (tmp < 0)
892 					tmp = 0;
893 				item_add_str("%*c%s%s", tmp, ' ',
894 						menu_get_prompt(menu),
895 						(sym_has_value(sym) ||
896 						 !sym_is_changeable(sym)) ? "" :
897 						" (NEW)");
898 				goto conf_childs;
899 			}
900 		}
901 		item_add_str("%*c%s%s", indent + 1, ' ',
902 				menu_get_prompt(menu),
903 				(sym_has_value(sym) || !sym_is_changeable(sym)) ?
904 				"" : " (NEW)");
905 		if (menu->prompt && menu->prompt->type == P_MENU) {
906 			item_add_str("  %s", menu_is_empty(menu) ? "----" : "--->");
907 			return;
908 		}
909 	}
910 
911 conf_childs:
912 	indent += doint;
913 	for (child = menu->list; child; child = child->next)
914 		build_conf(child);
915 	indent -= doint;
916 }
917 
reset_menu(void)918 static void reset_menu(void)
919 {
920 	unpost_menu(curses_menu);
921 	clean_items();
922 }
923 
924 /* adjust the menu to show this item.
925  * prefer not to scroll the menu if possible*/
center_item(int selected_index,int * last_top_row)926 static void center_item(int selected_index, int *last_top_row)
927 {
928 	int toprow;
929 
930 	set_top_row(curses_menu, *last_top_row);
931 	toprow = top_row(curses_menu);
932 	if (selected_index < toprow ||
933 	    selected_index >= toprow+mwin_max_lines) {
934 		toprow = max(selected_index-mwin_max_lines/2, 0);
935 		if (toprow >= item_count(curses_menu)-mwin_max_lines)
936 			toprow = item_count(curses_menu)-mwin_max_lines;
937 		set_top_row(curses_menu, toprow);
938 	}
939 	set_current_item(curses_menu,
940 			curses_menu_items[selected_index]);
941 	*last_top_row = toprow;
942 	post_menu(curses_menu);
943 	refresh_all_windows(main_window);
944 }
945 
946 /* this function assumes reset_menu has been called before */
show_menu(const char * prompt,const char * instructions,int selected_index,int * last_top_row)947 static void show_menu(const char *prompt, const char *instructions,
948 		int selected_index, int *last_top_row)
949 {
950 	int maxx, maxy;
951 	WINDOW *menu_window;
952 
953 	current_instructions = instructions;
954 
955 	clear();
956 	print_in_middle(stdscr, 1, getmaxx(stdscr),
957 			menu_backtitle,
958 			attr_main_heading);
959 
960 	wattrset(main_window, attr_main_menu_box);
961 	box(main_window, 0, 0);
962 	wattrset(main_window, attr_main_menu_heading);
963 	mvwprintw(main_window, 0, 3, " %s ", prompt);
964 	wattrset(main_window, attr_normal);
965 
966 	set_menu_items(curses_menu, curses_menu_items);
967 
968 	/* position the menu at the middle of the screen */
969 	scale_menu(curses_menu, &maxy, &maxx);
970 	maxx = min(maxx, mwin_max_cols-2);
971 	maxy = mwin_max_lines;
972 	menu_window = derwin(main_window,
973 			maxy,
974 			maxx,
975 			2,
976 			(mwin_max_cols-maxx)/2);
977 	keypad(menu_window, TRUE);
978 	set_menu_win(curses_menu, menu_window);
979 	set_menu_sub(curses_menu, menu_window);
980 
981 	/* must reassert this after changing items, otherwise returns to a
982 	 * default of 16
983 	 */
984 	set_menu_format(curses_menu, maxy, 1);
985 	center_item(selected_index, last_top_row);
986 	set_menu_format(curses_menu, maxy, 1);
987 
988 	print_function_line();
989 
990 	/* Post the menu */
991 	post_menu(curses_menu);
992 	refresh_all_windows(main_window);
993 }
994 
adj_match_dir(match_f * match_direction)995 static void adj_match_dir(match_f *match_direction)
996 {
997 	if (*match_direction == FIND_NEXT_MATCH_DOWN)
998 		*match_direction =
999 			MATCH_TINKER_PATTERN_DOWN;
1000 	else if (*match_direction == FIND_NEXT_MATCH_UP)
1001 		*match_direction =
1002 			MATCH_TINKER_PATTERN_UP;
1003 	/* else, do no change.. */
1004 }
1005 
1006 struct match_state
1007 {
1008 	int in_search;
1009 	match_f match_direction;
1010 	char pattern[256];
1011 };
1012 
1013 /* Return 0 means I have handled the key. In such a case, ans should hold the
1014  * item to center, or -1 otherwise.
1015  * Else return -1 .
1016  */
do_match(int key,struct match_state * state,int * ans)1017 static int do_match(int key, struct match_state *state, int *ans)
1018 {
1019 	char c = (char) key;
1020 	int terminate_search = 0;
1021 	*ans = -1;
1022 	if (key == '/' || (state->in_search && key == 27)) {
1023 		move(0, 0);
1024 		refresh();
1025 		clrtoeol();
1026 		state->in_search = 1-state->in_search;
1027 		bzero(state->pattern, sizeof(state->pattern));
1028 		state->match_direction = MATCH_TINKER_PATTERN_DOWN;
1029 		return 0;
1030 	} else if (!state->in_search)
1031 		return 1;
1032 
1033 	if (isalnum(c) || isgraph(c) || c == ' ') {
1034 		state->pattern[strlen(state->pattern)] = c;
1035 		state->pattern[strlen(state->pattern)] = '\0';
1036 		adj_match_dir(&state->match_direction);
1037 		*ans = get_mext_match(state->pattern,
1038 				state->match_direction);
1039 	} else if (key == KEY_DOWN) {
1040 		state->match_direction = FIND_NEXT_MATCH_DOWN;
1041 		*ans = get_mext_match(state->pattern,
1042 				state->match_direction);
1043 	} else if (key == KEY_UP) {
1044 		state->match_direction = FIND_NEXT_MATCH_UP;
1045 		*ans = get_mext_match(state->pattern,
1046 				state->match_direction);
1047 	} else if (key == KEY_BACKSPACE || key == 8 || key == 127) {
1048 		state->pattern[strlen(state->pattern)-1] = '\0';
1049 		adj_match_dir(&state->match_direction);
1050 	} else
1051 		terminate_search = 1;
1052 
1053 	if (terminate_search) {
1054 		state->in_search = 0;
1055 		bzero(state->pattern, sizeof(state->pattern));
1056 		move(0, 0);
1057 		refresh();
1058 		clrtoeol();
1059 		return -1;
1060 	}
1061 	return 0;
1062 }
1063 
conf(struct menu * menu)1064 static void conf(struct menu *menu)
1065 {
1066 	struct menu *submenu = NULL;
1067 	struct symbol *sym;
1068 	int res;
1069 	int current_index = 0;
1070 	int last_top_row = 0;
1071 	struct match_state match_state = {
1072 		.in_search = 0,
1073 		.match_direction = MATCH_TINKER_PATTERN_DOWN,
1074 		.pattern = "",
1075 	};
1076 
1077 	while (!global_exit) {
1078 		reset_menu();
1079 		current_menu = menu;
1080 		build_conf(menu);
1081 		if (!child_count)
1082 			break;
1083 
1084 		show_menu(menu_get_prompt(menu), menu_instructions,
1085 			  current_index, &last_top_row);
1086 		keypad((menu_win(curses_menu)), TRUE);
1087 		while (!global_exit) {
1088 			if (match_state.in_search) {
1089 				mvprintw(0, 0,
1090 					"searching: %s", match_state.pattern);
1091 				clrtoeol();
1092 			}
1093 			refresh_all_windows(main_window);
1094 			res = wgetch(menu_win(curses_menu));
1095 			if (!res)
1096 				break;
1097 			if (do_match(res, &match_state, &current_index) == 0) {
1098 				if (current_index != -1)
1099 					center_item(current_index,
1100 						    &last_top_row);
1101 				continue;
1102 			}
1103 			if (process_special_keys(&res,
1104 						(struct menu *) item_data()))
1105 				break;
1106 			switch (res) {
1107 			case KEY_DOWN:
1108 			case 'j':
1109 				menu_driver(curses_menu, REQ_DOWN_ITEM);
1110 				break;
1111 			case KEY_UP:
1112 			case 'k':
1113 				menu_driver(curses_menu, REQ_UP_ITEM);
1114 				break;
1115 			case KEY_NPAGE:
1116 				menu_driver(curses_menu, REQ_SCR_DPAGE);
1117 				break;
1118 			case KEY_PPAGE:
1119 				menu_driver(curses_menu, REQ_SCR_UPAGE);
1120 				break;
1121 			case KEY_HOME:
1122 				menu_driver(curses_menu, REQ_FIRST_ITEM);
1123 				break;
1124 			case KEY_END:
1125 				menu_driver(curses_menu, REQ_LAST_ITEM);
1126 				break;
1127 			case 'h':
1128 			case '?':
1129 				show_help((struct menu *) item_data());
1130 				break;
1131 			}
1132 			if (res == 10 || res == 27 ||
1133 				res == 32 || res == 'n' || res == 'y' ||
1134 				res == KEY_LEFT || res == KEY_RIGHT ||
1135 				res == 'm')
1136 				break;
1137 			refresh_all_windows(main_window);
1138 		}
1139 
1140 		refresh_all_windows(main_window);
1141 		/* if ESC or left*/
1142 		if (res == 27 || (menu != &rootmenu && res == KEY_LEFT))
1143 			break;
1144 
1145 		/* remember location in the menu */
1146 		last_top_row = top_row(curses_menu);
1147 		current_index = curses_item_index();
1148 
1149 		if (!item_tag())
1150 			continue;
1151 
1152 		submenu = (struct menu *) item_data();
1153 		if (!submenu || !menu_is_visible(submenu))
1154 			continue;
1155 		sym = submenu->sym;
1156 
1157 		switch (res) {
1158 		case ' ':
1159 			if (item_is_tag('t'))
1160 				sym_toggle_tristate_value(sym);
1161 			else if (item_is_tag('m'))
1162 				conf(submenu);
1163 			break;
1164 		case KEY_RIGHT:
1165 		case 10: /* ENTER WAS PRESSED */
1166 			switch (item_tag()) {
1167 			case 'm':
1168 				if (single_menu_mode)
1169 					submenu->data =
1170 						(void *) (long) !submenu->data;
1171 				else
1172 					conf(submenu);
1173 				break;
1174 			case 't':
1175 				if (sym_is_choice(sym) &&
1176 				    sym_get_tristate_value(sym) == yes)
1177 					conf_choice(submenu);
1178 				else if (submenu->prompt &&
1179 					 submenu->prompt->type == P_MENU)
1180 					conf(submenu);
1181 				else if (res == 10)
1182 					sym_toggle_tristate_value(sym);
1183 				break;
1184 			case 's':
1185 				conf_string(submenu);
1186 				break;
1187 			}
1188 			break;
1189 		case 'y':
1190 			if (item_is_tag('t')) {
1191 				if (sym_set_tristate_value(sym, yes))
1192 					break;
1193 				if (sym_set_tristate_value(sym, mod))
1194 					btn_dialog(main_window, setmod_text, 0);
1195 			}
1196 			break;
1197 		case 'n':
1198 			if (item_is_tag('t'))
1199 				sym_set_tristate_value(sym, no);
1200 			break;
1201 		case 'm':
1202 			if (item_is_tag('t'))
1203 				sym_set_tristate_value(sym, mod);
1204 			break;
1205 		}
1206 	}
1207 }
1208 
conf_message_callback(const char * s)1209 static void conf_message_callback(const char *s)
1210 {
1211 	btn_dialog(main_window, s, 1, "<OK>");
1212 }
1213 
show_help(struct menu * menu)1214 static void show_help(struct menu *menu)
1215 {
1216 	struct gstr help;
1217 
1218 	if (!menu)
1219 		return;
1220 
1221 	help = str_new();
1222 	menu_get_ext_help(menu, &help);
1223 	show_scroll_win(main_window, menu_get_prompt(menu), str_get(&help));
1224 	str_free(&help);
1225 }
1226 
conf_choice(struct menu * menu)1227 static void conf_choice(struct menu *menu)
1228 {
1229 	const char *prompt = menu_get_prompt(menu);
1230 	struct menu *child = NULL;
1231 	struct symbol *active;
1232 	int selected_index = 0;
1233 	int last_top_row = 0;
1234 	int res, i = 0;
1235 	struct match_state match_state = {
1236 		.in_search = 0,
1237 		.match_direction = MATCH_TINKER_PATTERN_DOWN,
1238 		.pattern = "",
1239 	};
1240 
1241 	active = sym_get_choice_value(menu->sym);
1242 	/* this is mostly duplicated from the conf() function. */
1243 	while (!global_exit) {
1244 		reset_menu();
1245 
1246 		for (i = 0, child = menu->list; child; child = child->next) {
1247 			if (!show_all_items && !menu_is_visible(child))
1248 				continue;
1249 
1250 			if (child->sym == sym_get_choice_value(menu->sym))
1251 				item_make(child, ':', "<X> %s",
1252 						menu_get_prompt(child));
1253 			else if (child->sym)
1254 				item_make(child, ':', "    %s",
1255 						menu_get_prompt(child));
1256 			else
1257 				item_make(child, ':', "*** %s ***",
1258 						menu_get_prompt(child));
1259 
1260 			if (child->sym == active){
1261 				last_top_row = top_row(curses_menu);
1262 				selected_index = i;
1263 			}
1264 			i++;
1265 		}
1266 		show_menu(prompt ? prompt : "Choice Menu",
1267 				radiolist_instructions,
1268 				selected_index,
1269 				&last_top_row);
1270 		while (!global_exit) {
1271 			if (match_state.in_search) {
1272 				mvprintw(0, 0, "searching: %s",
1273 					 match_state.pattern);
1274 				clrtoeol();
1275 			}
1276 			refresh_all_windows(main_window);
1277 			res = wgetch(menu_win(curses_menu));
1278 			if (!res)
1279 				break;
1280 			if (do_match(res, &match_state, &selected_index) == 0) {
1281 				if (selected_index != -1)
1282 					center_item(selected_index,
1283 						    &last_top_row);
1284 				continue;
1285 			}
1286 			if (process_special_keys(
1287 						&res,
1288 						(struct menu *) item_data()))
1289 				break;
1290 			switch (res) {
1291 			case KEY_DOWN:
1292 			case 'j':
1293 				menu_driver(curses_menu, REQ_DOWN_ITEM);
1294 				break;
1295 			case KEY_UP:
1296 			case 'k':
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 				conf_set_changed(true);
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 				1, "<OK>");
1441 			break;
1442 		case 1:
1443 			show_scroll_win(main_window,
1444 				"Save Alternate Configuration",
1445 				save_config_help);
1446 			break;
1447 		case KEY_EXIT:
1448 			return;
1449 		}
1450 	}
1451 }
1452 
setup_windows(void)1453 static void setup_windows(void)
1454 {
1455 	int lines, columns;
1456 
1457 	getmaxyx(stdscr, lines, columns);
1458 
1459 	if (main_window != NULL)
1460 		delwin(main_window);
1461 
1462 	/* set up the menu and menu window */
1463 	main_window = newwin(lines-2, columns-2, 2, 1);
1464 	keypad(main_window, TRUE);
1465 	mwin_max_lines = lines-7;
1466 	mwin_max_cols = columns-6;
1467 
1468 	/* panels order is from bottom to top */
1469 	new_panel(main_window);
1470 }
1471 
main(int ac,char ** av)1472 int main(int ac, char **av)
1473 {
1474 	int lines, columns;
1475 	char *mode;
1476 
1477 	if (ac > 1 && strcmp(av[1], "-s") == 0) {
1478 		/* Silence conf_read() until the real callback is set up */
1479 		conf_set_message_callback(NULL);
1480 		av++;
1481 	}
1482 	conf_parse(av[1]);
1483 	conf_read(NULL);
1484 
1485 	mode = getenv("NCONFIG_MODE");
1486 	if (mode) {
1487 		if (!strcasecmp(mode, "single_menu"))
1488 			single_menu_mode = 1;
1489 	}
1490 
1491 	/* Initialize curses */
1492 	initscr();
1493 	/* set color theme */
1494 	set_colors();
1495 
1496 	cbreak();
1497 	noecho();
1498 	keypad(stdscr, TRUE);
1499 	curs_set(0);
1500 
1501 	getmaxyx(stdscr, lines, columns);
1502 	if (columns < 75 || lines < 20) {
1503 		endwin();
1504 		printf("Your terminal should have at "
1505 			"least 20 lines and 75 columns\n");
1506 		return 1;
1507 	}
1508 
1509 	notimeout(stdscr, FALSE);
1510 #if NCURSES_REENTRANT
1511 	set_escdelay(1);
1512 #else
1513 	ESCDELAY = 1;
1514 #endif
1515 
1516 	/* set btns menu */
1517 	curses_menu = new_menu(curses_menu_items);
1518 	menu_opts_off(curses_menu, O_SHOWDESC);
1519 	menu_opts_on(curses_menu, O_SHOWMATCH);
1520 	menu_opts_on(curses_menu, O_ONEVALUE);
1521 	menu_opts_on(curses_menu, O_NONCYCLIC);
1522 	menu_opts_on(curses_menu, O_IGNORECASE);
1523 	set_menu_mark(curses_menu, " ");
1524 	set_menu_fore(curses_menu, attr_main_menu_fore);
1525 	set_menu_back(curses_menu, attr_main_menu_back);
1526 	set_menu_grey(curses_menu, attr_main_menu_grey);
1527 
1528 	set_config_filename(conf_get_configname());
1529 	setup_windows();
1530 
1531 	/* check for KEY_FUNC(1) */
1532 	if (has_key(KEY_F(1)) == FALSE) {
1533 		show_scroll_win(main_window,
1534 				"Instructions",
1535 				menu_no_f_instructions);
1536 	}
1537 
1538 	conf_set_message_callback(conf_message_callback);
1539 	/* do the work */
1540 	while (!global_exit) {
1541 		conf(&rootmenu);
1542 		if (!global_exit && do_exit() == 0)
1543 			break;
1544 	}
1545 	/* ok, we are done */
1546 	unpost_menu(curses_menu);
1547 	free_menu(curses_menu);
1548 	delwin(main_window);
1549 	clear();
1550 	refresh();
1551 	endwin();
1552 	return 0;
1553 }
1554