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