Merge tag 'for-6.6-rc2-tag' of git://git.kernel.org/pub/scm/linux/kernel/git/kdave...
[platform/kernel/linux-starfive.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 "nconf.h"
16 #include <ctype.h>
17
18 static const char nconf_global_help[] =
19 "Help windows\n"
20 "------------\n"
21 "o  Global help:  Unless in a data entry window, pressing <F1> will give \n"
22 "   you the global help window, which you are just reading.\n"
23 "\n"
24 "o  A short version of the global help is available by pressing <F3>.\n"
25 "\n"
26 "o  Local help:  To get help related to the current menu entry, use any\n"
27 "   of <?> <h>, or if in a data entry window then press <F1>.\n"
28 "\n"
29 "\n"
30 "Menu entries\n"
31 "------------\n"
32 "This interface lets you select features and parameters for the kernel\n"
33 "build.  Kernel features can either be built-in, modularized, or removed.\n"
34 "Parameters must be entered as text or decimal or hexadecimal numbers.\n"
35 "\n"
36 "Menu entries beginning with following braces represent features that\n"
37 "  [ ]  can be built in or removed\n"
38 "  < >  can be built in, modularized or removed\n"
39 "  { }  can be built in or modularized, are selected by another feature\n"
40 "  - -  are selected by another feature\n"
41 "  XXX  cannot be selected.  Symbol Info <F2> tells you why.\n"
42 "*, M or whitespace inside braces means to build in, build as a module\n"
43 "or to exclude the feature respectively.\n"
44 "\n"
45 "To change any of these features, highlight it with the movement keys\n"
46 "listed below and press <y> to build it in, <m> to make it a module or\n"
47 "<n> to remove it.  You may press the <Space> key to cycle through the\n"
48 "available options.\n"
49 "\n"
50 "A trailing \"--->\" designates a submenu, a trailing \"----\" an\n"
51 "empty submenu.\n"
52 "\n"
53 "Menu navigation keys\n"
54 "----------------------------------------------------------------------\n"
55 "Linewise up                 <Up>    <k>\n"
56 "Linewise down               <Down>  <j>\n"
57 "Pagewise up                 <Page Up>\n"
58 "Pagewise down               <Page Down>\n"
59 "First entry                 <Home>\n"
60 "Last entry                  <End>\n"
61 "Enter a submenu             <Right>  <Enter>\n"
62 "Go back to parent menu      <Left>   <Esc>  <F5>\n"
63 "Close a help window         <Enter>  <Esc>  <F5>\n"
64 "Close entry window, apply   <Enter>\n"
65 "Close entry window, forget  <Esc>  <F5>\n"
66 "Start incremental, case-insensitive search for STRING in menu entries,\n"
67 "    no regex support, STRING is displayed in upper left corner\n"
68 "                            </>STRING\n"
69 "    Remove last character   <Backspace>\n"
70 "    Jump to next hit        <Down>\n"
71 "    Jump to previous hit    <Up>\n"
72 "Exit menu search mode       </>  <Esc>\n"
73 "Search for configuration variables with or without leading CONFIG_\n"
74 "                            <F8>RegExpr<Enter>\n"
75 "Verbose search help         <F8><F1>\n"
76 "----------------------------------------------------------------------\n"
77 "\n"
78 "Unless in a data entry window, key <1> may be used instead of <F1>,\n"
79 "<2> instead of <F2>, etc.\n"
80 "\n"
81 "\n"
82 "Radiolist (Choice list)\n"
83 "-----------------------\n"
84 "Use the movement keys listed above to select the option you wish to set\n"
85 "and press <Space>.\n"
86 "\n"
87 "\n"
88 "Data entry\n"
89 "----------\n"
90 "Enter the requested information and press <Enter>.  Hexadecimal values\n"
91 "may be entered without the \"0x\" prefix.\n"
92 "\n"
93 "\n"
94 "Text Box (Help Window)\n"
95 "----------------------\n"
96 "Use movement keys as listed in table above.\n"
97 "\n"
98 "Press any of <Enter> <Esc> <q> <F5> <F9> to exit.\n"
99 "\n"
100 "\n"
101 "Alternate configuration files\n"
102 "-----------------------------\n"
103 "nconfig supports switching between different configurations.\n"
104 "Press <F6> to save your current configuration.  Press <F7> and enter\n"
105 "a file name to load a previously saved configuration.\n"
106 "\n"
107 "\n"
108 "Terminal configuration\n"
109 "----------------------\n"
110 "If you use nconfig in a xterm window, make sure your TERM environment\n"
111 "variable specifies a terminal configuration which supports at least\n"
112 "16 colors.  Otherwise nconfig will look rather bad.\n"
113 "\n"
114 "If the \"stty size\" command reports the current terminalsize correctly,\n"
115 "nconfig will adapt to sizes larger than the traditional 80x25 \"standard\"\n"
116 "and display longer menus properly.\n"
117 "\n"
118 "\n"
119 "Single menu mode\n"
120 "----------------\n"
121 "If you prefer to have all of the menu entries listed in a single menu,\n"
122 "rather than the default multimenu hierarchy, run nconfig with\n"
123 "NCONFIG_MODE environment variable set to single_menu.  Example:\n"
124 "\n"
125 "make NCONFIG_MODE=single_menu nconfig\n"
126 "\n"
127 "<Enter> will then unfold the appropriate category, or fold it if it\n"
128 "is already unfolded.  Folded menu entries will be designated by a\n"
129 "leading \"++>\" and unfolded entries by a leading \"-->\".\n"
130 "\n"
131 "Note that this mode can eventually be a little more CPU expensive than\n"
132 "the default mode, especially with a larger number of unfolded submenus.\n"
133 "\n",
134 menu_no_f_instructions[] =
135 "Legend:  [*] built-in  [ ] excluded  <M> module  < > module capable.\n"
136 "Submenus are designated by a trailing \"--->\", empty ones by \"----\".\n"
137 "\n"
138 "Use the following keys to navigate the menus:\n"
139 "Move up or down with <Up> and <Down>.\n"
140 "Enter a submenu with <Enter> or <Right>.\n"
141 "Exit a submenu to its parent menu with <Esc> or <Left>.\n"
142 "Pressing <y> includes, <n> excludes, <m> modularizes features.\n"
143 "Pressing <Space> cycles through the available options.\n"
144 "To search for menu entries press </>.\n"
145 "<Esc> always leaves the current window.\n"
146 "\n"
147 "You do not have function keys support.\n"
148 "Press <1> instead of <F1>, <2> instead of <F2>, etc.\n"
149 "For verbose global help use key <1>.\n"
150 "For help related to the current menu entry press <?> or <h>.\n",
151 menu_instructions[] =
152 "Legend:  [*] built-in  [ ] excluded  <M> module  < > module capable.\n"
153 "Submenus are designated by a trailing \"--->\", empty ones by \"----\".\n"
154 "\n"
155 "Use the following keys to navigate the menus:\n"
156 "Move up or down with <Up> or <Down>.\n"
157 "Enter a submenu with <Enter> or <Right>.\n"
158 "Exit a submenu to its parent menu with <Esc> or <Left>.\n"
159 "Pressing <y> includes, <n> excludes, <m> modularizes features.\n"
160 "Pressing <Space> cycles through the available options.\n"
161 "To search for menu entries press </>.\n"
162 "<Esc> always leaves the current window.\n"
163 "\n"
164 "Pressing <1> may be used instead of <F1>, <2> instead of <F2>, etc.\n"
165 "For verbose global help press <F1>.\n"
166 "For help related to the current menu entry press <?> or <h>.\n",
167 radiolist_instructions[] =
168 "Press <Up>, <Down>, <Home> or <End> to navigate a radiolist, select\n"
169 "with <Space>.\n"
170 "For help related to the current entry press <?> or <h>.\n"
171 "For global help press <F1>.\n",
172 inputbox_instructions_int[] =
173 "Please enter a decimal value.\n"
174 "Fractions will not be accepted.\n"
175 "Press <Enter> to apply, <Esc> to cancel.",
176 inputbox_instructions_hex[] =
177 "Please enter a hexadecimal value.\n"
178 "Press <Enter> to apply, <Esc> to cancel.",
179 inputbox_instructions_string[] =
180 "Please enter a string value.\n"
181 "Press <Enter> to apply, <Esc> to cancel.",
182 setmod_text[] =
183 "This feature depends on another feature which has been configured as a\n"
184 "module.  As a result, the current feature will be built as a module too.",
185 load_config_text[] =
186 "Enter the name of the configuration file you wish to load.\n"
187 "Accept the name shown to restore the configuration you last\n"
188 "retrieved.  Leave empty to abort.",
189 load_config_help[] =
190 "For various reasons, one may wish to keep several different\n"
191 "configurations available on a single machine.\n"
192 "\n"
193 "If you have saved a previous configuration in a file other than the\n"
194 "default one, entering its name here will allow you to load and modify\n"
195 "that configuration.\n"
196 "\n"
197 "Leave empty to abort.\n",
198 save_config_text[] =
199 "Enter a filename to which this configuration should be saved\n"
200 "as an alternate.  Leave empty to abort.",
201 save_config_help[] =
202 "For various reasons, one may wish to keep several different\n"
203 "configurations available on a single machine.\n"
204 "\n"
205 "Entering a file name here will allow you to later retrieve, modify\n"
206 "and use the current configuration as an alternate to whatever\n"
207 "configuration options you have selected at that time.\n"
208 "\n"
209 "Leave empty to abort.\n",
210 search_help[] =
211 "Search for symbols (configuration variable names CONFIG_*) and display\n"
212 "their relations.  Regular expressions are supported.\n"
213 "Example:  Search for \"^FOO\".\n"
214 "Result:\n"
215 "-----------------------------------------------------------------\n"
216 "Symbol: FOO [ = m]\n"
217 "Prompt: Foo bus is used to drive the bar HW\n"
218 "Defined at drivers/pci/Kconfig:47\n"
219 "Depends on: X86_LOCAL_APIC && X86_IO_APIC || IA64\n"
220 "Location:\n"
221 "  -> Bus options (PCI, PCMCIA, EISA, ISA)\n"
222 "    -> PCI support (PCI [ = y])\n"
223 "(1)   -> PCI access mode (<choice> [ = y])\n"
224 "Selects: LIBCRC32\n"
225 "Selected by: BAR\n"
226 "-----------------------------------------------------------------\n"
227 "o  The line 'Prompt:' shows the text displayed for this symbol in\n"
228 "   the menu hierarchy.\n"
229 "o  The 'Defined at' line tells at what file / line number the symbol is\n"
230 "   defined.\n"
231 "o  The 'Depends on:' line lists symbols that need to be defined for\n"
232 "   this symbol to be visible and selectable in the menu.\n"
233 "o  The 'Location:' lines tell, where in the menu structure this symbol\n"
234 "   is located.\n"
235 "     A location followed by a [ = y] indicates that this is\n"
236 "     a selectable menu item, and the current value is displayed inside\n"
237 "     brackets.\n"
238 "     Press the key in the (#) prefix to jump directly to that\n"
239 "     location. You will be returned to the current search results\n"
240 "     after exiting this new menu.\n"
241 "o  The 'Selects:' line tells, what symbol will be automatically selected\n"
242 "   if this symbol is selected (y or m).\n"
243 "o  The 'Selected by' line tells what symbol has selected this symbol.\n"
244 "\n"
245 "Only relevant lines are shown.\n"
246 "\n\n"
247 "Search examples:\n"
248 "USB  => find all symbols containing USB\n"
249 "^USB => find all symbols starting with USB\n"
250 "USB$ => find all symbols ending with USB\n"
251 "\n";
252
253 struct mitem {
254         char str[256];
255         char tag;
256         void *usrptr;
257         int is_visible;
258 };
259
260 #define MAX_MENU_ITEMS 4096
261 static int show_all_items;
262 static int indent;
263 static struct menu *current_menu;
264 static int child_count;
265 static int single_menu_mode;
266 /* the window in which all information appears */
267 static WINDOW *main_window;
268 /* the largest size of the menu window */
269 static int mwin_max_lines;
270 static int mwin_max_cols;
271 /* the window in which we show option buttons */
272 static MENU *curses_menu;
273 static ITEM *curses_menu_items[MAX_MENU_ITEMS];
274 static struct mitem k_menu_items[MAX_MENU_ITEMS];
275 static unsigned int items_num;
276 static int global_exit;
277 /* the currently selected button */
278 static const char *current_instructions = menu_instructions;
279
280 static char *dialog_input_result;
281 static int dialog_input_result_len;
282 static int jump_key_char;
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 struct search_data {
695         struct list_head *head;
696         struct menu *target;
697 };
698
699 static int next_jump_key(int key)
700 {
701         if (key < '1' || key > '9')
702                 return '1';
703
704         key++;
705
706         if (key > '9')
707                 key = '1';
708
709         return key;
710 }
711
712 static int handle_search_keys(int key, size_t start, size_t end, void *_data)
713 {
714         struct search_data *data = _data;
715         struct jump_key *pos;
716         int index = 0;
717
718         if (key < '1' || key > '9')
719                 return 0;
720
721         list_for_each_entry(pos, data->head, entries) {
722                 index = next_jump_key(index);
723
724                 if (pos->offset < start)
725                         continue;
726
727                 if (pos->offset >= end)
728                         break;
729
730                 if (key == index) {
731                         data->target = pos->target;
732                         return 1;
733                 }
734         }
735
736         return 0;
737 }
738
739 int get_jump_key_char(void)
740 {
741         jump_key_char = next_jump_key(jump_key_char);
742
743         return jump_key_char;
744 }
745
746 static void search_conf(void)
747 {
748         struct symbol **sym_arr;
749         struct gstr res;
750         struct gstr title;
751         char *dialog_input;
752         int dres, vscroll = 0, hscroll = 0;
753         bool again;
754
755         title = str_new();
756         str_printf( &title, "Enter (sub)string or regexp to search for "
757                               "(with or without \"%s\")", CONFIG_);
758
759 again:
760         dres = dialog_inputbox(main_window,
761                         "Search Configuration Parameter",
762                         str_get(&title),
763                         "", &dialog_input_result, &dialog_input_result_len);
764         switch (dres) {
765         case 0:
766                 break;
767         case 1:
768                 show_scroll_win(main_window,
769                                 "Search Configuration", search_help);
770                 goto again;
771         default:
772                 str_free(&title);
773                 return;
774         }
775
776         /* strip the prefix if necessary */
777         dialog_input = dialog_input_result;
778         if (strncasecmp(dialog_input_result, CONFIG_, strlen(CONFIG_)) == 0)
779                 dialog_input += strlen(CONFIG_);
780
781         sym_arr = sym_re_search(dialog_input);
782
783         do {
784                 LIST_HEAD(head);
785                 struct search_data data = {
786                         .head = &head,
787                         .target = NULL,
788                 };
789                 jump_key_char = 0;
790                 res = get_relations_str(sym_arr, &head);
791                 dres = show_scroll_win_ext(main_window,
792                                 "Search Results", str_get(&res),
793                                 &vscroll, &hscroll,
794                                 handle_search_keys, &data);
795                 again = false;
796                 if (dres >= '1' && dres <= '9') {
797                         assert(data.target != NULL);
798                         selected_conf(data.target->parent, data.target);
799                         again = true;
800                 }
801                 str_free(&res);
802         } while (again);
803         free(sym_arr);
804         str_free(&title);
805 }
806
807
808 static void build_conf(struct menu *menu)
809 {
810         struct symbol *sym;
811         struct property *prop;
812         struct menu *child;
813         int type, tmp, doint = 2;
814         tristate val;
815         char ch;
816
817         if (!menu || (!show_all_items && !menu_is_visible(menu)))
818                 return;
819
820         sym = menu->sym;
821         prop = menu->prompt;
822         if (!sym) {
823                 if (prop && menu != current_menu) {
824                         const char *prompt = menu_get_prompt(menu);
825                         enum prop_type ptype;
826                         ptype = menu->prompt ? menu->prompt->type : P_UNKNOWN;
827                         switch (ptype) {
828                         case P_MENU:
829                                 child_count++;
830                                 if (single_menu_mode) {
831                                         item_make(menu, 'm',
832                                                 "%s%*c%s",
833                                                 menu->data ? "-->" : "++>",
834                                                 indent + 1, ' ', prompt);
835                                 } else
836                                         item_make(menu, 'm',
837                                                   "   %*c%s  %s",
838                                                   indent + 1, ' ', prompt,
839                                                   menu_is_empty(menu) ? "----" : "--->");
840
841                                 if (single_menu_mode && menu->data)
842                                         goto conf_childs;
843                                 return;
844                         case P_COMMENT:
845                                 if (prompt) {
846                                         child_count++;
847                                         item_make(menu, ':',
848                                                 "   %*c*** %s ***",
849                                                 indent + 1, ' ',
850                                                 prompt);
851                                 }
852                                 break;
853                         default:
854                                 if (prompt) {
855                                         child_count++;
856                                         item_make(menu, ':', "---%*c%s",
857                                                 indent + 1, ' ',
858                                                 prompt);
859                                 }
860                         }
861                 } else
862                         doint = 0;
863                 goto conf_childs;
864         }
865
866         type = sym_get_type(sym);
867         if (sym_is_choice(sym)) {
868                 struct symbol *def_sym = sym_get_choice_value(sym);
869                 struct menu *def_menu = NULL;
870
871                 child_count++;
872                 for (child = menu->list; child; child = child->next) {
873                         if (menu_is_visible(child) && child->sym == def_sym)
874                                 def_menu = child;
875                 }
876
877                 val = sym_get_tristate_value(sym);
878                 if (sym_is_changeable(sym)) {
879                         switch (type) {
880                         case S_BOOLEAN:
881                                 item_make(menu, 't', "[%c]",
882                                                 val == no ? ' ' : '*');
883                                 break;
884                         case S_TRISTATE:
885                                 switch (val) {
886                                 case yes:
887                                         ch = '*';
888                                         break;
889                                 case mod:
890                                         ch = 'M';
891                                         break;
892                                 default:
893                                         ch = ' ';
894                                         break;
895                                 }
896                                 item_make(menu, 't', "<%c>", ch);
897                                 break;
898                         }
899                 } else {
900                         item_make(menu, def_menu ? 't' : ':', "   ");
901                 }
902
903                 item_add_str("%*c%s", indent + 1,
904                                 ' ', menu_get_prompt(menu));
905                 if (val == yes) {
906                         if (def_menu) {
907                                 item_add_str(" (%s)",
908                                         menu_get_prompt(def_menu));
909                                 item_add_str("  --->");
910                                 if (def_menu->list) {
911                                         indent += 2;
912                                         build_conf(def_menu);
913                                         indent -= 2;
914                                 }
915                         }
916                         return;
917                 }
918         } else {
919                 if (menu == current_menu) {
920                         item_make(menu, ':',
921                                 "---%*c%s", indent + 1,
922                                 ' ', menu_get_prompt(menu));
923                         goto conf_childs;
924                 }
925                 child_count++;
926                 val = sym_get_tristate_value(sym);
927                 if (sym_is_choice_value(sym) && val == yes) {
928                         item_make(menu, ':', "   ");
929                 } else {
930                         switch (type) {
931                         case S_BOOLEAN:
932                                 if (sym_is_changeable(sym))
933                                         item_make(menu, 't', "[%c]",
934                                                 val == no ? ' ' : '*');
935                                 else
936                                         item_make(menu, 't', "-%c-",
937                                                 val == no ? ' ' : '*');
938                                 break;
939                         case S_TRISTATE:
940                                 switch (val) {
941                                 case yes:
942                                         ch = '*';
943                                         break;
944                                 case mod:
945                                         ch = 'M';
946                                         break;
947                                 default:
948                                         ch = ' ';
949                                         break;
950                                 }
951                                 if (sym_is_changeable(sym)) {
952                                         if (sym->rev_dep.tri == mod)
953                                                 item_make(menu,
954                                                         't', "{%c}", ch);
955                                         else
956                                                 item_make(menu,
957                                                         't', "<%c>", ch);
958                                 } else
959                                         item_make(menu, 't', "-%c-", ch);
960                                 break;
961                         default:
962                                 tmp = 2 + strlen(sym_get_string_value(sym));
963                                 item_make(menu, 's', "    (%s)",
964                                                 sym_get_string_value(sym));
965                                 tmp = indent - tmp + 4;
966                                 if (tmp < 0)
967                                         tmp = 0;
968                                 item_add_str("%*c%s%s", tmp, ' ',
969                                                 menu_get_prompt(menu),
970                                                 (sym_has_value(sym) ||
971                                                  !sym_is_changeable(sym)) ? "" :
972                                                 " (NEW)");
973                                 goto conf_childs;
974                         }
975                 }
976                 item_add_str("%*c%s%s", indent + 1, ' ',
977                                 menu_get_prompt(menu),
978                                 (sym_has_value(sym) || !sym_is_changeable(sym)) ?
979                                 "" : " (NEW)");
980                 if (menu->prompt && menu->prompt->type == P_MENU) {
981                         item_add_str("  %s", menu_is_empty(menu) ? "----" : "--->");
982                         return;
983                 }
984         }
985
986 conf_childs:
987         indent += doint;
988         for (child = menu->list; child; child = child->next)
989                 build_conf(child);
990         indent -= doint;
991 }
992
993 static void reset_menu(void)
994 {
995         unpost_menu(curses_menu);
996         clean_items();
997 }
998
999 /* adjust the menu to show this item.
1000  * prefer not to scroll the menu if possible*/
1001 static void center_item(int selected_index, int *last_top_row)
1002 {
1003         int toprow;
1004
1005         set_top_row(curses_menu, *last_top_row);
1006         toprow = top_row(curses_menu);
1007         if (selected_index < toprow ||
1008             selected_index >= toprow+mwin_max_lines) {
1009                 toprow = max(selected_index-mwin_max_lines/2, 0);
1010                 if (toprow >= item_count(curses_menu)-mwin_max_lines)
1011                         toprow = item_count(curses_menu)-mwin_max_lines;
1012                 set_top_row(curses_menu, toprow);
1013         }
1014         set_current_item(curses_menu,
1015                         curses_menu_items[selected_index]);
1016         *last_top_row = toprow;
1017         post_menu(curses_menu);
1018         refresh_all_windows(main_window);
1019 }
1020
1021 /* this function assumes reset_menu has been called before */
1022 static void show_menu(const char *prompt, const char *instructions,
1023                 int selected_index, int *last_top_row)
1024 {
1025         int maxx, maxy;
1026         WINDOW *menu_window;
1027
1028         current_instructions = instructions;
1029
1030         clear();
1031         print_in_middle(stdscr, 1, getmaxx(stdscr),
1032                         menu_backtitle,
1033                         attr_main_heading);
1034
1035         wattrset(main_window, attr_main_menu_box);
1036         box(main_window, 0, 0);
1037         wattrset(main_window, attr_main_menu_heading);
1038         mvwprintw(main_window, 0, 3, " %s ", prompt);
1039         wattrset(main_window, attr_normal);
1040
1041         set_menu_items(curses_menu, curses_menu_items);
1042
1043         /* position the menu at the middle of the screen */
1044         scale_menu(curses_menu, &maxy, &maxx);
1045         maxx = min(maxx, mwin_max_cols-2);
1046         maxy = mwin_max_lines;
1047         menu_window = derwin(main_window,
1048                         maxy,
1049                         maxx,
1050                         2,
1051                         (mwin_max_cols-maxx)/2);
1052         keypad(menu_window, TRUE);
1053         set_menu_win(curses_menu, menu_window);
1054         set_menu_sub(curses_menu, menu_window);
1055
1056         /* must reassert this after changing items, otherwise returns to a
1057          * default of 16
1058          */
1059         set_menu_format(curses_menu, maxy, 1);
1060         center_item(selected_index, last_top_row);
1061         set_menu_format(curses_menu, maxy, 1);
1062
1063         print_function_line();
1064
1065         /* Post the menu */
1066         post_menu(curses_menu);
1067         refresh_all_windows(main_window);
1068 }
1069
1070 static void adj_match_dir(match_f *match_direction)
1071 {
1072         if (*match_direction == FIND_NEXT_MATCH_DOWN)
1073                 *match_direction =
1074                         MATCH_TINKER_PATTERN_DOWN;
1075         else if (*match_direction == FIND_NEXT_MATCH_UP)
1076                 *match_direction =
1077                         MATCH_TINKER_PATTERN_UP;
1078         /* else, do no change.. */
1079 }
1080
1081 struct match_state
1082 {
1083         int in_search;
1084         match_f match_direction;
1085         char pattern[256];
1086 };
1087
1088 /* Return 0 means I have handled the key. In such a case, ans should hold the
1089  * item to center, or -1 otherwise.
1090  * Else return -1 .
1091  */
1092 static int do_match(int key, struct match_state *state, int *ans)
1093 {
1094         char c = (char) key;
1095         int terminate_search = 0;
1096         *ans = -1;
1097         if (key == '/' || (state->in_search && key == 27)) {
1098                 move(0, 0);
1099                 refresh();
1100                 clrtoeol();
1101                 state->in_search = 1-state->in_search;
1102                 bzero(state->pattern, sizeof(state->pattern));
1103                 state->match_direction = MATCH_TINKER_PATTERN_DOWN;
1104                 return 0;
1105         } else if (!state->in_search)
1106                 return 1;
1107
1108         if (isalnum(c) || isgraph(c) || c == ' ') {
1109                 state->pattern[strlen(state->pattern)] = c;
1110                 state->pattern[strlen(state->pattern)] = '\0';
1111                 adj_match_dir(&state->match_direction);
1112                 *ans = get_mext_match(state->pattern,
1113                                 state->match_direction);
1114         } else if (key == KEY_DOWN) {
1115                 state->match_direction = FIND_NEXT_MATCH_DOWN;
1116                 *ans = get_mext_match(state->pattern,
1117                                 state->match_direction);
1118         } else if (key == KEY_UP) {
1119                 state->match_direction = FIND_NEXT_MATCH_UP;
1120                 *ans = get_mext_match(state->pattern,
1121                                 state->match_direction);
1122         } else if (key == KEY_BACKSPACE || key == 8 || key == 127) {
1123                 state->pattern[strlen(state->pattern)-1] = '\0';
1124                 adj_match_dir(&state->match_direction);
1125         } else
1126                 terminate_search = 1;
1127
1128         if (terminate_search) {
1129                 state->in_search = 0;
1130                 bzero(state->pattern, sizeof(state->pattern));
1131                 move(0, 0);
1132                 refresh();
1133                 clrtoeol();
1134                 return -1;
1135         }
1136         return 0;
1137 }
1138
1139 static void conf(struct menu *menu)
1140 {
1141         selected_conf(menu, NULL);
1142 }
1143
1144 static void selected_conf(struct menu *menu, struct menu *active_menu)
1145 {
1146         struct menu *submenu = NULL;
1147         struct symbol *sym;
1148         int i, res;
1149         int current_index = 0;
1150         int last_top_row = 0;
1151         struct match_state match_state = {
1152                 .in_search = 0,
1153                 .match_direction = MATCH_TINKER_PATTERN_DOWN,
1154                 .pattern = "",
1155         };
1156
1157         while (!global_exit) {
1158                 reset_menu();
1159                 current_menu = menu;
1160                 build_conf(menu);
1161                 if (!child_count)
1162                         break;
1163
1164                 if (active_menu != NULL) {
1165                         for (i = 0; i < items_num; i++) {
1166                                 struct mitem *mcur;
1167
1168                                 mcur = (struct mitem *) item_userptr(curses_menu_items[i]);
1169                                 if ((struct menu *) mcur->usrptr == active_menu) {
1170                                         current_index = i;
1171                                         break;
1172                                 }
1173                         }
1174                         active_menu = NULL;
1175                 }
1176
1177                 show_menu(menu_get_prompt(menu), menu_instructions,
1178                           current_index, &last_top_row);
1179                 keypad((menu_win(curses_menu)), TRUE);
1180                 while (!global_exit) {
1181                         if (match_state.in_search) {
1182                                 mvprintw(0, 0,
1183                                         "searching: %s", match_state.pattern);
1184                                 clrtoeol();
1185                         }
1186                         refresh_all_windows(main_window);
1187                         res = wgetch(menu_win(curses_menu));
1188                         if (!res)
1189                                 break;
1190                         if (do_match(res, &match_state, &current_index) == 0) {
1191                                 if (current_index != -1)
1192                                         center_item(current_index,
1193                                                     &last_top_row);
1194                                 continue;
1195                         }
1196                         if (process_special_keys(&res,
1197                                                 (struct menu *) item_data()))
1198                                 break;
1199                         switch (res) {
1200                         case KEY_DOWN:
1201                         case 'j':
1202                                 menu_driver(curses_menu, REQ_DOWN_ITEM);
1203                                 break;
1204                         case KEY_UP:
1205                         case 'k':
1206                                 menu_driver(curses_menu, REQ_UP_ITEM);
1207                                 break;
1208                         case KEY_NPAGE:
1209                                 menu_driver(curses_menu, REQ_SCR_DPAGE);
1210                                 break;
1211                         case KEY_PPAGE:
1212                                 menu_driver(curses_menu, REQ_SCR_UPAGE);
1213                                 break;
1214                         case KEY_HOME:
1215                                 menu_driver(curses_menu, REQ_FIRST_ITEM);
1216                                 break;
1217                         case KEY_END:
1218                                 menu_driver(curses_menu, REQ_LAST_ITEM);
1219                                 break;
1220                         case 'h':
1221                         case '?':
1222                                 show_help((struct menu *) item_data());
1223                                 break;
1224                         }
1225                         if (res == 10 || res == 27 ||
1226                                 res == 32 || res == 'n' || res == 'y' ||
1227                                 res == KEY_LEFT || res == KEY_RIGHT ||
1228                                 res == 'm')
1229                                 break;
1230                         refresh_all_windows(main_window);
1231                 }
1232
1233                 refresh_all_windows(main_window);
1234                 /* if ESC or left*/
1235                 if (res == 27 || (menu != &rootmenu && res == KEY_LEFT))
1236                         break;
1237
1238                 /* remember location in the menu */
1239                 last_top_row = top_row(curses_menu);
1240                 current_index = curses_item_index();
1241
1242                 if (!item_tag())
1243                         continue;
1244
1245                 submenu = (struct menu *) item_data();
1246                 if (!submenu || !menu_is_visible(submenu))
1247                         continue;
1248                 sym = submenu->sym;
1249
1250                 switch (res) {
1251                 case ' ':
1252                         if (item_is_tag('t'))
1253                                 sym_toggle_tristate_value(sym);
1254                         else if (item_is_tag('m'))
1255                                 conf(submenu);
1256                         break;
1257                 case KEY_RIGHT:
1258                 case 10: /* ENTER WAS PRESSED */
1259                         switch (item_tag()) {
1260                         case 'm':
1261                                 if (single_menu_mode)
1262                                         submenu->data =
1263                                                 (void *) (long) !submenu->data;
1264                                 else
1265                                         conf(submenu);
1266                                 break;
1267                         case 't':
1268                                 if (sym_is_choice(sym) &&
1269                                     sym_get_tristate_value(sym) == yes)
1270                                         conf_choice(submenu);
1271                                 else if (submenu->prompt &&
1272                                          submenu->prompt->type == P_MENU)
1273                                         conf(submenu);
1274                                 else if (res == 10)
1275                                         sym_toggle_tristate_value(sym);
1276                                 break;
1277                         case 's':
1278                                 conf_string(submenu);
1279                                 break;
1280                         }
1281                         break;
1282                 case 'y':
1283                         if (item_is_tag('t')) {
1284                                 if (sym_set_tristate_value(sym, yes))
1285                                         break;
1286                                 if (sym_set_tristate_value(sym, mod))
1287                                         btn_dialog(main_window, setmod_text, 0);
1288                         }
1289                         break;
1290                 case 'n':
1291                         if (item_is_tag('t'))
1292                                 sym_set_tristate_value(sym, no);
1293                         break;
1294                 case 'm':
1295                         if (item_is_tag('t'))
1296                                 sym_set_tristate_value(sym, mod);
1297                         break;
1298                 }
1299         }
1300 }
1301
1302 static void conf_message_callback(const char *s)
1303 {
1304         btn_dialog(main_window, s, 1, "<OK>");
1305 }
1306
1307 static void show_help(struct menu *menu)
1308 {
1309         struct gstr help;
1310
1311         if (!menu)
1312                 return;
1313
1314         help = str_new();
1315         menu_get_ext_help(menu, &help);
1316         show_scroll_win(main_window, menu_get_prompt(menu), str_get(&help));
1317         str_free(&help);
1318 }
1319
1320 static void conf_choice(struct menu *menu)
1321 {
1322         const char *prompt = menu_get_prompt(menu);
1323         struct menu *child = NULL;
1324         struct symbol *active;
1325         int selected_index = 0;
1326         int last_top_row = 0;
1327         int res, i = 0;
1328         struct match_state match_state = {
1329                 .in_search = 0,
1330                 .match_direction = MATCH_TINKER_PATTERN_DOWN,
1331                 .pattern = "",
1332         };
1333
1334         active = sym_get_choice_value(menu->sym);
1335         /* this is mostly duplicated from the conf() function. */
1336         while (!global_exit) {
1337                 reset_menu();
1338
1339                 for (i = 0, child = menu->list; child; child = child->next) {
1340                         if (!show_all_items && !menu_is_visible(child))
1341                                 continue;
1342
1343                         if (child->sym == sym_get_choice_value(menu->sym))
1344                                 item_make(child, ':', "<X> %s",
1345                                                 menu_get_prompt(child));
1346                         else if (child->sym)
1347                                 item_make(child, ':', "    %s",
1348                                                 menu_get_prompt(child));
1349                         else
1350                                 item_make(child, ':', "*** %s ***",
1351                                                 menu_get_prompt(child));
1352
1353                         if (child->sym == active){
1354                                 last_top_row = top_row(curses_menu);
1355                                 selected_index = i;
1356                         }
1357                         i++;
1358                 }
1359                 show_menu(prompt ? prompt : "Choice Menu",
1360                                 radiolist_instructions,
1361                                 selected_index,
1362                                 &last_top_row);
1363                 while (!global_exit) {
1364                         if (match_state.in_search) {
1365                                 mvprintw(0, 0, "searching: %s",
1366                                          match_state.pattern);
1367                                 clrtoeol();
1368                         }
1369                         refresh_all_windows(main_window);
1370                         res = wgetch(menu_win(curses_menu));
1371                         if (!res)
1372                                 break;
1373                         if (do_match(res, &match_state, &selected_index) == 0) {
1374                                 if (selected_index != -1)
1375                                         center_item(selected_index,
1376                                                     &last_top_row);
1377                                 continue;
1378                         }
1379                         if (process_special_keys(
1380                                                 &res,
1381                                                 (struct menu *) item_data()))
1382                                 break;
1383                         switch (res) {
1384                         case KEY_DOWN:
1385                         case 'j':
1386                                 menu_driver(curses_menu, REQ_DOWN_ITEM);
1387                                 break;
1388                         case KEY_UP:
1389                         case 'k':
1390                                 menu_driver(curses_menu, REQ_UP_ITEM);
1391                                 break;
1392                         case KEY_NPAGE:
1393                                 menu_driver(curses_menu, REQ_SCR_DPAGE);
1394                                 break;
1395                         case KEY_PPAGE:
1396                                 menu_driver(curses_menu, REQ_SCR_UPAGE);
1397                                 break;
1398                         case KEY_HOME:
1399                                 menu_driver(curses_menu, REQ_FIRST_ITEM);
1400                                 break;
1401                         case KEY_END:
1402                                 menu_driver(curses_menu, REQ_LAST_ITEM);
1403                                 break;
1404                         case 'h':
1405                         case '?':
1406                                 show_help((struct menu *) item_data());
1407                                 break;
1408                         }
1409                         if (res == 10 || res == 27 || res == ' ' ||
1410                                         res == KEY_LEFT){
1411                                 break;
1412                         }
1413                         refresh_all_windows(main_window);
1414                 }
1415                 /* if ESC or left */
1416                 if (res == 27 || res == KEY_LEFT)
1417                         break;
1418
1419                 child = item_data();
1420                 if (!child || !menu_is_visible(child) || !child->sym)
1421                         continue;
1422                 switch (res) {
1423                 case ' ':
1424                 case  10:
1425                 case KEY_RIGHT:
1426                         sym_set_tristate_value(child->sym, yes);
1427                         return;
1428                 case 'h':
1429                 case '?':
1430                         show_help(child);
1431                         active = child->sym;
1432                         break;
1433                 case KEY_EXIT:
1434                         return;
1435                 }
1436         }
1437 }
1438
1439 static void conf_string(struct menu *menu)
1440 {
1441         const char *prompt = menu_get_prompt(menu);
1442
1443         while (1) {
1444                 int res;
1445                 const char *heading;
1446
1447                 switch (sym_get_type(menu->sym)) {
1448                 case S_INT:
1449                         heading = inputbox_instructions_int;
1450                         break;
1451                 case S_HEX:
1452                         heading = inputbox_instructions_hex;
1453                         break;
1454                 case S_STRING:
1455                         heading = inputbox_instructions_string;
1456                         break;
1457                 default:
1458                         heading = "Internal nconf error!";
1459                 }
1460                 res = dialog_inputbox(main_window,
1461                                 prompt ? prompt : "Main Menu",
1462                                 heading,
1463                                 sym_get_string_value(menu->sym),
1464                                 &dialog_input_result,
1465                                 &dialog_input_result_len);
1466                 switch (res) {
1467                 case 0:
1468                         if (sym_set_string_value(menu->sym,
1469                                                 dialog_input_result))
1470                                 return;
1471                         btn_dialog(main_window,
1472                                 "You have made an invalid entry.", 0);
1473                         break;
1474                 case 1:
1475                         show_help(menu);
1476                         break;
1477                 case KEY_EXIT:
1478                         return;
1479                 }
1480         }
1481 }
1482
1483 static void conf_load(void)
1484 {
1485         while (1) {
1486                 int res;
1487                 res = dialog_inputbox(main_window,
1488                                 NULL, load_config_text,
1489                                 filename,
1490                                 &dialog_input_result,
1491                                 &dialog_input_result_len);
1492                 switch (res) {
1493                 case 0:
1494                         if (!dialog_input_result[0])
1495                                 return;
1496                         if (!conf_read(dialog_input_result)) {
1497                                 set_config_filename(dialog_input_result);
1498                                 conf_set_changed(true);
1499                                 return;
1500                         }
1501                         btn_dialog(main_window, "File does not exist!", 0);
1502                         break;
1503                 case 1:
1504                         show_scroll_win(main_window,
1505                                         "Load Alternate Configuration",
1506                                         load_config_help);
1507                         break;
1508                 case KEY_EXIT:
1509                         return;
1510                 }
1511         }
1512 }
1513
1514 static void conf_save(void)
1515 {
1516         while (1) {
1517                 int res;
1518                 res = dialog_inputbox(main_window,
1519                                 NULL, save_config_text,
1520                                 filename,
1521                                 &dialog_input_result,
1522                                 &dialog_input_result_len);
1523                 switch (res) {
1524                 case 0:
1525                         if (!dialog_input_result[0])
1526                                 return;
1527                         res = conf_write(dialog_input_result);
1528                         if (!res) {
1529                                 set_config_filename(dialog_input_result);
1530                                 return;
1531                         }
1532                         btn_dialog(main_window, "Can't create file!",
1533                                 1, "<OK>");
1534                         break;
1535                 case 1:
1536                         show_scroll_win(main_window,
1537                                 "Save Alternate Configuration",
1538                                 save_config_help);
1539                         break;
1540                 case KEY_EXIT:
1541                         return;
1542                 }
1543         }
1544 }
1545
1546 static void setup_windows(void)
1547 {
1548         int lines, columns;
1549
1550         getmaxyx(stdscr, lines, columns);
1551
1552         if (main_window != NULL)
1553                 delwin(main_window);
1554
1555         /* set up the menu and menu window */
1556         main_window = newwin(lines-2, columns-2, 2, 1);
1557         keypad(main_window, TRUE);
1558         mwin_max_lines = lines-7;
1559         mwin_max_cols = columns-6;
1560
1561         /* panels order is from bottom to top */
1562         new_panel(main_window);
1563 }
1564
1565 int main(int ac, char **av)
1566 {
1567         int lines, columns;
1568         char *mode;
1569
1570         if (ac > 1 && strcmp(av[1], "-s") == 0) {
1571                 /* Silence conf_read() until the real callback is set up */
1572                 conf_set_message_callback(NULL);
1573                 av++;
1574         }
1575         conf_parse(av[1]);
1576         conf_read(NULL);
1577
1578         mode = getenv("NCONFIG_MODE");
1579         if (mode) {
1580                 if (!strcasecmp(mode, "single_menu"))
1581                         single_menu_mode = 1;
1582         }
1583
1584         /* Initialize curses */
1585         initscr();
1586         /* set color theme */
1587         set_colors();
1588
1589         cbreak();
1590         noecho();
1591         keypad(stdscr, TRUE);
1592         curs_set(0);
1593
1594         getmaxyx(stdscr, lines, columns);
1595         if (columns < 75 || lines < 20) {
1596                 endwin();
1597                 printf("Your terminal should have at "
1598                         "least 20 lines and 75 columns\n");
1599                 return 1;
1600         }
1601
1602         notimeout(stdscr, FALSE);
1603 #if NCURSES_REENTRANT
1604         set_escdelay(1);
1605 #else
1606         ESCDELAY = 1;
1607 #endif
1608
1609         /* set btns menu */
1610         curses_menu = new_menu(curses_menu_items);
1611         menu_opts_off(curses_menu, O_SHOWDESC);
1612         menu_opts_on(curses_menu, O_SHOWMATCH);
1613         menu_opts_on(curses_menu, O_ONEVALUE);
1614         menu_opts_on(curses_menu, O_NONCYCLIC);
1615         menu_opts_on(curses_menu, O_IGNORECASE);
1616         set_menu_mark(curses_menu, " ");
1617         set_menu_fore(curses_menu, attr_main_menu_fore);
1618         set_menu_back(curses_menu, attr_main_menu_back);
1619         set_menu_grey(curses_menu, attr_main_menu_grey);
1620
1621         set_config_filename(conf_get_configname());
1622         setup_windows();
1623
1624         /* check for KEY_FUNC(1) */
1625         if (has_key(KEY_F(1)) == FALSE) {
1626                 show_scroll_win(main_window,
1627                                 "Instructions",
1628                                 menu_no_f_instructions);
1629         }
1630
1631         conf_set_message_callback(conf_message_callback);
1632         /* do the work */
1633         while (!global_exit) {
1634                 conf(&rootmenu);
1635                 if (!global_exit && do_exit() == 0)
1636                         break;
1637         }
1638         /* ok, we are done */
1639         unpost_menu(curses_menu);
1640         free_menu(curses_menu);
1641         delwin(main_window);
1642         clear();
1643         refresh();
1644         endwin();
1645         return 0;
1646 }