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