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