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