blob: f9944a64de6e268cd65ea65bc1dda9cca8ad8c36 [file] [log] [blame]
Patrick Georgid5208402014-04-11 20:24:06 +02001/*
2 * Copyright (C) 2008 Nir Tzachar <nir.tzachar@gmail.com?
3 * Released under the terms of the GNU GPL v2.0.
4 *
5 * Derived from menuconfig.
6 *
7 */
8#define _GNU_SOURCE
9#include <string.h>
10#include <stdlib.h>
11
12#include "lkc.h"
13#include "nconf.h"
14#include <ctype.h>
15
Patrick Georgi9595ed42015-09-30 12:01:18 +020016int kconfig_warnings = 0;
17
Patrick Georgid5208402014-04-11 20:24:06 +020018static const char nconf_global_help[] = N_(
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>\n"
56"Linewise down <Down>\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"),
134menu_no_f_instructions[] = N_(
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"),
151menu_instructions[] = N_(
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"),
167radiolist_instructions[] = N_(
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"),
172inputbox_instructions_int[] = N_(
173"Please enter a decimal value.\n"
174"Fractions will not be accepted.\n"
175"Press <Enter> to apply, <Esc> to cancel."),
176inputbox_instructions_hex[] = N_(
177"Please enter a hexadecimal value.\n"
178"Press <Enter> to apply, <Esc> to cancel."),
179inputbox_instructions_string[] = N_(
180"Please enter a string value.\n"
181"Press <Enter> to apply, <Esc> to cancel."),
182setmod_text[] = N_(
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."),
185load_config_text[] = N_(
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."),
189load_config_help[] = N_(
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"),
198save_config_text[] = N_(
199"Enter a filename to which this configuration should be saved\n"
200"as an alternate. Leave empty to abort."),
201save_config_help[] = N_(
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"),
210search_help[] = N_(
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" -> 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. A location followed by a [ = y] indicates that this is\n"
235" a selectable menu item, and the current value is displayed inside\n"
236" brackets.\n"
237"o The 'Selects:' line tells, what symbol will be automatically selected\n"
238" if this symbol is selected (y or m).\n"
239"o The 'Selected by' line tells what symbol has selected this symbol.\n"
240"\n"
241"Only relevant lines are shown.\n"
242"\n\n"
243"Search examples:\n"
244"USB => find all symbols containing USB\n"
245"^USB => find all symbols starting with USB\n"
246"USB$ => find all symbols ending with USB\n"
247"\n");
248
249struct mitem {
250 char str[256];
251 char tag;
252 void *usrptr;
253 int is_visible;
254};
255
256#define MAX_MENU_ITEMS 4096
257static int show_all_items;
258static int indent;
259static struct menu *current_menu;
260static int child_count;
261static int single_menu_mode;
262/* the window in which all information appears */
263static WINDOW *main_window;
264/* the largest size of the menu window */
265static int mwin_max_lines;
266static int mwin_max_cols;
267/* the window in which we show option buttons */
268static MENU *curses_menu;
269static ITEM *curses_menu_items[MAX_MENU_ITEMS];
270static struct mitem k_menu_items[MAX_MENU_ITEMS];
271static int items_num;
272static int global_exit;
273/* the currently selected button */
274const char *current_instructions = menu_instructions;
275
276static char *dialog_input_result;
277static int dialog_input_result_len;
278
279static void conf(struct menu *menu);
280static void conf_choice(struct menu *menu);
281static void conf_string(struct menu *menu);
282static void conf_load(void);
283static void conf_save(void);
284static void show_help(struct menu *menu);
285static int do_exit(void);
286static void setup_windows(void);
287static void search_conf(void);
288
289typedef void (*function_key_handler_t)(int *key, struct menu *menu);
290static void handle_f1(int *key, struct menu *current_item);
291static void handle_f2(int *key, struct menu *current_item);
292static void handle_f3(int *key, struct menu *current_item);
293static void handle_f4(int *key, struct menu *current_item);
294static void handle_f5(int *key, struct menu *current_item);
295static void handle_f6(int *key, struct menu *current_item);
296static void handle_f7(int *key, struct menu *current_item);
297static void handle_f8(int *key, struct menu *current_item);
298static void handle_f9(int *key, struct menu *current_item);
299
300struct function_keys {
301 const char *key_str;
302 const char *func;
303 function_key key;
304 function_key_handler_t handler;
305};
306
307static const int function_keys_num = 9;
308struct function_keys function_keys[] = {
309 {
310 .key_str = "F1",
311 .func = "Help",
312 .key = F_HELP,
313 .handler = handle_f1,
314 },
315 {
316 .key_str = "F2",
317 .func = "SymInfo",
318 .key = F_SYMBOL,
319 .handler = handle_f2,
320 },
321 {
322 .key_str = "F3",
323 .func = "Help 2",
324 .key = F_INSTS,
325 .handler = handle_f3,
326 },
327 {
328 .key_str = "F4",
329 .func = "ShowAll",
330 .key = F_CONF,
331 .handler = handle_f4,
332 },
333 {
334 .key_str = "F5",
335 .func = "Back",
336 .key = F_BACK,
337 .handler = handle_f5,
338 },
339 {
340 .key_str = "F6",
341 .func = "Save",
342 .key = F_SAVE,
343 .handler = handle_f6,
344 },
345 {
346 .key_str = "F7",
347 .func = "Load",
348 .key = F_LOAD,
349 .handler = handle_f7,
350 },
351 {
352 .key_str = "F8",
353 .func = "SymSearch",
354 .key = F_SEARCH,
355 .handler = handle_f8,
356 },
357 {
358 .key_str = "F9",
359 .func = "Exit",
360 .key = F_EXIT,
361 .handler = handle_f9,
362 },
363};
364
365static void print_function_line(void)
366{
367 int i;
368 int offset = 1;
369 const int skip = 1;
370 int lines = getmaxy(stdscr);
371
372 for (i = 0; i < function_keys_num; i++) {
373 (void) wattrset(main_window, attributes[FUNCTION_HIGHLIGHT]);
374 mvwprintw(main_window, lines-3, offset,
375 "%s",
376 function_keys[i].key_str);
377 (void) wattrset(main_window, attributes[FUNCTION_TEXT]);
378 offset += strlen(function_keys[i].key_str);
379 mvwprintw(main_window, lines-3,
380 offset, "%s",
381 function_keys[i].func);
382 offset += strlen(function_keys[i].func) + skip;
383 }
384 (void) wattrset(main_window, attributes[NORMAL]);
385}
386
387/* help */
388static void handle_f1(int *key, struct menu *current_item)
389{
390 show_scroll_win(main_window,
391 _("Global help"), _(nconf_global_help));
392 return;
393}
394
395/* symbole help */
396static void handle_f2(int *key, struct menu *current_item)
397{
398 show_help(current_item);
399 return;
400}
401
402/* instructions */
403static void handle_f3(int *key, struct menu *current_item)
404{
405 show_scroll_win(main_window,
406 _("Short help"),
407 _(current_instructions));
408 return;
409}
410
411/* config */
412static void handle_f4(int *key, struct menu *current_item)
413{
414 int res = btn_dialog(main_window,
415 _("Show all symbols?"),
416 2,
417 " <Show All> ",
418 "<Don't show all>");
419 if (res == 0)
420 show_all_items = 1;
421 else if (res == 1)
422 show_all_items = 0;
423
424 return;
425}
426
427/* back */
428static void handle_f5(int *key, struct menu *current_item)
429{
430 *key = KEY_LEFT;
431 return;
432}
433
434/* save */
435static void handle_f6(int *key, struct menu *current_item)
436{
437 conf_save();
438 return;
439}
440
441/* load */
442static void handle_f7(int *key, struct menu *current_item)
443{
444 conf_load();
445 return;
446}
447
448/* search */
449static void handle_f8(int *key, struct menu *current_item)
450{
451 search_conf();
452 return;
453}
454
455/* exit */
456static void handle_f9(int *key, struct menu *current_item)
457{
458 do_exit();
459 return;
460}
461
462/* return != 0 to indicate the key was handles */
463static int process_special_keys(int *key, struct menu *menu)
464{
465 int i;
466
467 if (*key == KEY_RESIZE) {
468 setup_windows();
469 return 1;
470 }
471
472 for (i = 0; i < function_keys_num; i++) {
473 if (*key == KEY_F(function_keys[i].key) ||
474 *key == '0' + function_keys[i].key){
475 function_keys[i].handler(key, menu);
476 return 1;
477 }
478 }
479
480 return 0;
481}
482
483static void clean_items(void)
484{
485 int i;
486 for (i = 0; curses_menu_items[i]; i++)
487 free_item(curses_menu_items[i]);
488 bzero(curses_menu_items, sizeof(curses_menu_items));
489 bzero(k_menu_items, sizeof(k_menu_items));
490 items_num = 0;
491}
492
493typedef enum {MATCH_TINKER_PATTERN_UP, MATCH_TINKER_PATTERN_DOWN,
494 FIND_NEXT_MATCH_DOWN, FIND_NEXT_MATCH_UP} match_f;
495
496/* return the index of the matched item, or -1 if no such item exists */
497static int get_mext_match(const char *match_str, match_f flag)
498{
499 int match_start = item_index(current_item(curses_menu));
500 int index;
501
502 if (flag == FIND_NEXT_MATCH_DOWN)
503 ++match_start;
504 else if (flag == FIND_NEXT_MATCH_UP)
505 --match_start;
506
507 index = match_start;
508 index = (index + items_num) % items_num;
509 while (true) {
510 char *str = k_menu_items[index].str;
511 if (strcasestr(str, match_str) != 0)
512 return index;
513 if (flag == FIND_NEXT_MATCH_UP ||
514 flag == MATCH_TINKER_PATTERN_UP)
515 --index;
516 else
517 ++index;
518 index = (index + items_num) % items_num;
519 if (index == match_start)
520 return -1;
521 }
522}
523
524/* Make a new item. */
525static void item_make(struct menu *menu, char tag, const char *fmt, ...)
526{
527 va_list ap;
528
529 if (items_num > MAX_MENU_ITEMS-1)
530 return;
531
532 bzero(&k_menu_items[items_num], sizeof(k_menu_items[0]));
533 k_menu_items[items_num].tag = tag;
534 k_menu_items[items_num].usrptr = menu;
535 if (menu != NULL)
536 k_menu_items[items_num].is_visible =
537 menu_is_visible(menu);
538 else
539 k_menu_items[items_num].is_visible = 1;
540
541 va_start(ap, fmt);
542 vsnprintf(k_menu_items[items_num].str,
543 sizeof(k_menu_items[items_num].str),
544 fmt, ap);
545 va_end(ap);
546
547 if (!k_menu_items[items_num].is_visible)
548 memcpy(k_menu_items[items_num].str, "XXX", 3);
549
550 curses_menu_items[items_num] = new_item(
551 k_menu_items[items_num].str,
552 k_menu_items[items_num].str);
553 set_item_userptr(curses_menu_items[items_num],
554 &k_menu_items[items_num]);
555 /*
556 if (!k_menu_items[items_num].is_visible)
557 item_opts_off(curses_menu_items[items_num], O_SELECTABLE);
558 */
559
560 items_num++;
561 curses_menu_items[items_num] = NULL;
562}
563
564/* very hackish. adds a string to the last item added */
565static void item_add_str(const char *fmt, ...)
566{
567 va_list ap;
568 int index = items_num-1;
569 char new_str[256];
570 char tmp_str[256];
571
572 if (index < 0)
573 return;
574
575 va_start(ap, fmt);
576 vsnprintf(new_str, sizeof(new_str), fmt, ap);
577 va_end(ap);
578 snprintf(tmp_str, sizeof(tmp_str), "%s%s",
579 k_menu_items[index].str, new_str);
580 strncpy(k_menu_items[index].str,
581 tmp_str,
582 sizeof(k_menu_items[index].str));
583
584 free_item(curses_menu_items[index]);
585 curses_menu_items[index] = new_item(
586 k_menu_items[index].str,
587 k_menu_items[index].str);
588 set_item_userptr(curses_menu_items[index],
589 &k_menu_items[index]);
590}
591
592/* get the tag of the currently selected item */
593static char item_tag(void)
594{
595 ITEM *cur;
596 struct mitem *mcur;
597
598 cur = current_item(curses_menu);
599 if (cur == NULL)
600 return 0;
601 mcur = (struct mitem *) item_userptr(cur);
602 return mcur->tag;
603}
604
605static int curses_item_index(void)
606{
607 return item_index(current_item(curses_menu));
608}
609
610static void *item_data(void)
611{
612 ITEM *cur;
613 struct mitem *mcur;
614
615 cur = current_item(curses_menu);
616 if (!cur)
617 return NULL;
618 mcur = (struct mitem *) item_userptr(cur);
619 return mcur->usrptr;
620
621}
622
623static int item_is_tag(char tag)
624{
625 return item_tag() == tag;
626}
627
628static char filename[PATH_MAX+1];
629static char menu_backtitle[PATH_MAX+128];
630static const char *set_config_filename(const char *config_filename)
631{
632 int size;
633
634 size = snprintf(menu_backtitle, sizeof(menu_backtitle),
635 "%s - %s", config_filename, rootmenu.prompt->text);
636 if (size >= sizeof(menu_backtitle))
637 menu_backtitle[sizeof(menu_backtitle)-1] = '\0';
638
639 size = snprintf(filename, sizeof(filename), "%s", config_filename);
640 if (size >= sizeof(filename))
641 filename[sizeof(filename)-1] = '\0';
642 return menu_backtitle;
643}
644
645/* return = 0 means we are successful.
646 * -1 means go on doing what you were doing
647 */
648static int do_exit(void)
649{
650 int res;
Stefan Reinauer57a31312015-08-20 11:19:34 -0700651 char *env;
652
Patrick Georgid5208402014-04-11 20:24:06 +0200653 if (!conf_get_changed()) {
654 global_exit = 1;
655 return 0;
656 }
657 res = btn_dialog(main_window,
658 _("Do you wish to save your new configuration?\n"
659 "<ESC> to cancel and resume nconfig."),
660 2,
661 " <save> ",
662 "<don't save>");
663 if (res == KEY_EXIT) {
664 global_exit = 0;
665 return -1;
666 }
667
Stefan Reinauer57a31312015-08-20 11:19:34 -0700668 env = getenv("KCONFIG_STRICT");
669 if (env && *env && kconfig_warnings) {
670 btn_dialog(main_window,
671 _("\nWarnings encountered, and warnings are errors.\n\n"),
672 1,
673 "<OK>");
674 res = 2;
675 }
676
Patrick Georgid5208402014-04-11 20:24:06 +0200677 /* if we got here, the user really wants to exit */
678 switch (res) {
679 case 0:
680 res = conf_write(filename);
681 if (res)
682 btn_dialog(
683 main_window,
684 _("Error during writing of configuration.\n"
685 "Your configuration changes were NOT saved."),
686 1,
687 "<OK>");
688 break;
689 default:
690 btn_dialog(
691 main_window,
692 _("Your configuration changes were NOT saved."),
693 1,
694 "<OK>");
695 break;
696 }
697 global_exit = 1;
698 return 0;
699}
700
701
702static void search_conf(void)
703{
704 struct symbol **sym_arr;
705 struct gstr res;
706 struct gstr title;
707 char *dialog_input;
708 int dres;
709
710 title = str_new();
711 str_printf( &title, _("Enter (sub)string or regexp to search for "
712 "(with or without \"%s\")"), CONFIG_);
713
714again:
715 dres = dialog_inputbox(main_window,
716 _("Search Configuration Parameter"),
717 str_get(&title),
718 "", &dialog_input_result, &dialog_input_result_len);
719 switch (dres) {
720 case 0:
721 break;
722 case 1:
723 show_scroll_win(main_window,
724 _("Search Configuration"), search_help);
725 goto again;
726 default:
727 str_free(&title);
728 return;
729 }
730
731 /* strip the prefix if necessary */
732 dialog_input = dialog_input_result;
733 if (strncasecmp(dialog_input_result, CONFIG_, strlen(CONFIG_)) == 0)
734 dialog_input += strlen(CONFIG_);
735
736 sym_arr = sym_re_search(dialog_input);
737 res = get_relations_str(sym_arr, NULL);
738 free(sym_arr);
739 show_scroll_win(main_window,
740 _("Search Results"), str_get(&res));
741 str_free(&res);
742 str_free(&title);
743}
744
745
746static void build_conf(struct menu *menu)
747{
748 struct symbol *sym;
749 struct property *prop;
750 struct menu *child;
751 int type, tmp, doint = 2;
752 tristate val;
753 char ch;
754
755 if (!menu || (!show_all_items && !menu_is_visible(menu)))
756 return;
757
758 sym = menu->sym;
759 prop = menu->prompt;
760 if (!sym) {
761 if (prop && menu != current_menu) {
762 const char *prompt = menu_get_prompt(menu);
763 enum prop_type ptype;
764 ptype = menu->prompt ? menu->prompt->type : P_UNKNOWN;
765 switch (ptype) {
766 case P_MENU:
767 child_count++;
768 prompt = _(prompt);
769 if (single_menu_mode) {
770 item_make(menu, 'm',
771 "%s%*c%s",
772 menu->data ? "-->" : "++>",
773 indent + 1, ' ', prompt);
774 } else
775 item_make(menu, 'm',
776 " %*c%s %s",
777 indent + 1, ' ', prompt,
778 menu_is_empty(menu) ? "----" : "--->");
779
780 if (single_menu_mode && menu->data)
781 goto conf_childs;
782 return;
783 case P_COMMENT:
784 if (prompt) {
785 child_count++;
786 item_make(menu, ':',
787 " %*c*** %s ***",
788 indent + 1, ' ',
789 _(prompt));
790 }
791 break;
792 default:
793 if (prompt) {
794 child_count++;
795 item_make(menu, ':', "---%*c%s",
796 indent + 1, ' ',
797 _(prompt));
798 }
799 }
800 } else
801 doint = 0;
802 goto conf_childs;
803 }
804
805 type = sym_get_type(sym);
806 if (sym_is_choice(sym)) {
807 struct symbol *def_sym = sym_get_choice_value(sym);
808 struct menu *def_menu = NULL;
809
810 child_count++;
811 for (child = menu->list; child; child = child->next) {
812 if (menu_is_visible(child) && child->sym == def_sym)
813 def_menu = child;
814 }
815
816 val = sym_get_tristate_value(sym);
817 if (sym_is_changable(sym)) {
818 switch (type) {
819 case S_BOOLEAN:
820 item_make(menu, 't', "[%c]",
821 val == no ? ' ' : '*');
822 break;
823 case S_TRISTATE:
824 switch (val) {
825 case yes:
826 ch = '*';
827 break;
828 case mod:
829 ch = 'M';
830 break;
831 default:
832 ch = ' ';
833 break;
834 }
835 item_make(menu, 't', "<%c>", ch);
836 break;
837 }
838 } else {
839 item_make(menu, def_menu ? 't' : ':', " ");
840 }
841
842 item_add_str("%*c%s", indent + 1,
843 ' ', _(menu_get_prompt(menu)));
844 if (val == yes) {
845 if (def_menu) {
846 item_add_str(" (%s)",
847 _(menu_get_prompt(def_menu)));
848 item_add_str(" --->");
849 if (def_menu->list) {
850 indent += 2;
851 build_conf(def_menu);
852 indent -= 2;
853 }
854 }
855 return;
856 }
857 } else {
858 if (menu == current_menu) {
859 item_make(menu, ':',
860 "---%*c%s", indent + 1,
861 ' ', _(menu_get_prompt(menu)));
862 goto conf_childs;
863 }
864 child_count++;
865 val = sym_get_tristate_value(sym);
866 if (sym_is_choice_value(sym) && val == yes) {
867 item_make(menu, ':', " ");
868 } else {
869 switch (type) {
870 case S_BOOLEAN:
871 if (sym_is_changable(sym))
872 item_make(menu, 't', "[%c]",
873 val == no ? ' ' : '*');
874 else
875 item_make(menu, 't', "-%c-",
876 val == no ? ' ' : '*');
877 break;
878 case S_TRISTATE:
879 switch (val) {
880 case yes:
881 ch = '*';
882 break;
883 case mod:
884 ch = 'M';
885 break;
886 default:
887 ch = ' ';
888 break;
889 }
890 if (sym_is_changable(sym)) {
891 if (sym->rev_dep.tri == mod)
892 item_make(menu,
893 't', "{%c}", ch);
894 else
895 item_make(menu,
896 't', "<%c>", ch);
897 } else
898 item_make(menu, 't', "-%c-", ch);
899 break;
900 default:
901 tmp = 2 + strlen(sym_get_string_value(sym));
902 item_make(menu, 's', " (%s)",
903 sym_get_string_value(sym));
904 tmp = indent - tmp + 4;
905 if (tmp < 0)
906 tmp = 0;
907 item_add_str("%*c%s%s", tmp, ' ',
908 _(menu_get_prompt(menu)),
909 (sym_has_value(sym) ||
910 !sym_is_changable(sym)) ? "" :
911 _(" (NEW)"));
912 goto conf_childs;
913 }
914 }
915 item_add_str("%*c%s%s", indent + 1, ' ',
916 _(menu_get_prompt(menu)),
917 (sym_has_value(sym) || !sym_is_changable(sym)) ?
918 "" : _(" (NEW)"));
919 if (menu->prompt && menu->prompt->type == P_MENU) {
920 item_add_str(" %s", menu_is_empty(menu) ? "----" : "--->");
921 return;
922 }
923 }
924
925conf_childs:
926 indent += doint;
927 for (child = menu->list; child; child = child->next)
928 build_conf(child);
929 indent -= doint;
930}
931
932static void reset_menu(void)
933{
934 unpost_menu(curses_menu);
935 clean_items();
936}
937
938/* adjust the menu to show this item.
939 * prefer not to scroll the menu if possible*/
940static void center_item(int selected_index, int *last_top_row)
941{
942 int toprow;
943
944 set_top_row(curses_menu, *last_top_row);
945 toprow = top_row(curses_menu);
946 if (selected_index < toprow ||
947 selected_index >= toprow+mwin_max_lines) {
948 toprow = max(selected_index-mwin_max_lines/2, 0);
949 if (toprow >= item_count(curses_menu)-mwin_max_lines)
950 toprow = item_count(curses_menu)-mwin_max_lines;
951 set_top_row(curses_menu, toprow);
952 }
953 set_current_item(curses_menu,
954 curses_menu_items[selected_index]);
955 *last_top_row = toprow;
956 post_menu(curses_menu);
957 refresh_all_windows(main_window);
958}
959
960/* this function assumes reset_menu has been called before */
961static void show_menu(const char *prompt, const char *instructions,
962 int selected_index, int *last_top_row)
963{
964 int maxx, maxy;
965 WINDOW *menu_window;
966
967 current_instructions = instructions;
968
969 clear();
970 (void) wattrset(main_window, attributes[NORMAL]);
971 print_in_middle(stdscr, 1, 0, getmaxx(stdscr),
972 menu_backtitle,
973 attributes[MAIN_HEADING]);
974
975 (void) wattrset(main_window, attributes[MAIN_MENU_BOX]);
976 box(main_window, 0, 0);
977 (void) wattrset(main_window, attributes[MAIN_MENU_HEADING]);
978 mvwprintw(main_window, 0, 3, " %s ", prompt);
979 (void) wattrset(main_window, attributes[NORMAL]);
980
981 set_menu_items(curses_menu, curses_menu_items);
982
983 /* position the menu at the middle of the screen */
984 scale_menu(curses_menu, &maxy, &maxx);
985 maxx = min(maxx, mwin_max_cols-2);
986 maxy = mwin_max_lines;
987 menu_window = derwin(main_window,
988 maxy,
989 maxx,
990 2,
991 (mwin_max_cols-maxx)/2);
992 keypad(menu_window, TRUE);
993 set_menu_win(curses_menu, menu_window);
994 set_menu_sub(curses_menu, menu_window);
995
996 /* must reassert this after changing items, otherwise returns to a
997 * default of 16
998 */
999 set_menu_format(curses_menu, maxy, 1);
1000 center_item(selected_index, last_top_row);
1001 set_menu_format(curses_menu, maxy, 1);
1002
1003 print_function_line();
1004
1005 /* Post the menu */
1006 post_menu(curses_menu);
1007 refresh_all_windows(main_window);
1008}
1009
1010static void adj_match_dir(match_f *match_direction)
1011{
1012 if (*match_direction == FIND_NEXT_MATCH_DOWN)
1013 *match_direction =
1014 MATCH_TINKER_PATTERN_DOWN;
1015 else if (*match_direction == FIND_NEXT_MATCH_UP)
1016 *match_direction =
1017 MATCH_TINKER_PATTERN_UP;
1018 /* else, do no change.. */
1019}
1020
1021struct match_state
1022{
1023 int in_search;
1024 match_f match_direction;
1025 char pattern[256];
1026};
1027
1028/* Return 0 means I have handled the key. In such a case, ans should hold the
1029 * item to center, or -1 otherwise.
1030 * Else return -1 .
1031 */
1032static int do_match(int key, struct match_state *state, int *ans)
1033{
1034 char c = (char) key;
1035 int terminate_search = 0;
1036 *ans = -1;
1037 if (key == '/' || (state->in_search && key == 27)) {
1038 move(0, 0);
1039 refresh();
1040 clrtoeol();
1041 state->in_search = 1-state->in_search;
1042 bzero(state->pattern, sizeof(state->pattern));
1043 state->match_direction = MATCH_TINKER_PATTERN_DOWN;
1044 return 0;
1045 } else if (!state->in_search)
1046 return 1;
1047
1048 if (isalnum(c) || isgraph(c) || c == ' ') {
1049 state->pattern[strlen(state->pattern)] = c;
1050 state->pattern[strlen(state->pattern)] = '\0';
1051 adj_match_dir(&state->match_direction);
1052 *ans = get_mext_match(state->pattern,
1053 state->match_direction);
1054 } else if (key == KEY_DOWN) {
1055 state->match_direction = FIND_NEXT_MATCH_DOWN;
1056 *ans = get_mext_match(state->pattern,
1057 state->match_direction);
1058 } else if (key == KEY_UP) {
1059 state->match_direction = FIND_NEXT_MATCH_UP;
1060 *ans = get_mext_match(state->pattern,
1061 state->match_direction);
1062 } else if (key == KEY_BACKSPACE || key == 127) {
1063 state->pattern[strlen(state->pattern)-1] = '\0';
1064 adj_match_dir(&state->match_direction);
1065 } else
1066 terminate_search = 1;
1067
1068 if (terminate_search) {
1069 state->in_search = 0;
1070 bzero(state->pattern, sizeof(state->pattern));
1071 move(0, 0);
1072 refresh();
1073 clrtoeol();
1074 return -1;
1075 }
1076 return 0;
1077}
1078
1079static void conf(struct menu *menu)
1080{
1081 struct menu *submenu = 0;
1082 const char *prompt = menu_get_prompt(menu);
1083 struct symbol *sym;
1084 int res;
1085 int current_index = 0;
1086 int last_top_row = 0;
1087 struct match_state match_state = {
1088 .in_search = 0,
1089 .match_direction = MATCH_TINKER_PATTERN_DOWN,
1090 .pattern = "",
1091 };
1092
1093 while (!global_exit) {
1094 reset_menu();
1095 current_menu = menu;
1096 build_conf(menu);
1097 if (!child_count)
1098 break;
1099
1100 show_menu(prompt ? _(prompt) : _("Main Menu"),
1101 _(menu_instructions),
1102 current_index, &last_top_row);
1103 keypad((menu_win(curses_menu)), TRUE);
1104 while (!global_exit) {
1105 if (match_state.in_search) {
1106 mvprintw(0, 0,
1107 "searching: %s", match_state.pattern);
1108 clrtoeol();
1109 }
1110 refresh_all_windows(main_window);
1111 res = wgetch(menu_win(curses_menu));
1112 if (!res)
1113 break;
1114 if (do_match(res, &match_state, &current_index) == 0) {
1115 if (current_index != -1)
1116 center_item(current_index,
1117 &last_top_row);
1118 continue;
1119 }
1120 if (process_special_keys(&res,
1121 (struct menu *) item_data()))
1122 break;
1123 switch (res) {
1124 case KEY_DOWN:
1125 menu_driver(curses_menu, REQ_DOWN_ITEM);
1126 break;
1127 case KEY_UP:
1128 menu_driver(curses_menu, REQ_UP_ITEM);
1129 break;
1130 case KEY_NPAGE:
1131 menu_driver(curses_menu, REQ_SCR_DPAGE);
1132 break;
1133 case KEY_PPAGE:
1134 menu_driver(curses_menu, REQ_SCR_UPAGE);
1135 break;
1136 case KEY_HOME:
1137 menu_driver(curses_menu, REQ_FIRST_ITEM);
1138 break;
1139 case KEY_END:
1140 menu_driver(curses_menu, REQ_LAST_ITEM);
1141 break;
1142 case 'h':
1143 case '?':
1144 show_help((struct menu *) item_data());
1145 break;
1146 }
1147 if (res == 10 || res == 27 ||
1148 res == 32 || res == 'n' || res == 'y' ||
1149 res == KEY_LEFT || res == KEY_RIGHT ||
1150 res == 'm')
1151 break;
1152 refresh_all_windows(main_window);
1153 }
1154
1155 refresh_all_windows(main_window);
1156 /* if ESC or left*/
1157 if (res == 27 || (menu != &rootmenu && res == KEY_LEFT))
1158 break;
1159
1160 /* remember location in the menu */
1161 last_top_row = top_row(curses_menu);
1162 current_index = curses_item_index();
1163
1164 if (!item_tag())
1165 continue;
1166
1167 submenu = (struct menu *) item_data();
1168 if (!submenu || !menu_is_visible(submenu))
1169 continue;
1170 sym = submenu->sym;
1171
1172 switch (res) {
1173 case ' ':
1174 if (item_is_tag('t'))
1175 sym_toggle_tristate_value(sym);
1176 else if (item_is_tag('m'))
1177 conf(submenu);
1178 break;
1179 case KEY_RIGHT:
1180 case 10: /* ENTER WAS PRESSED */
1181 switch (item_tag()) {
1182 case 'm':
1183 if (single_menu_mode)
1184 submenu->data =
1185 (void *) (long) !submenu->data;
1186 else
1187 conf(submenu);
1188 break;
1189 case 't':
1190 if (sym_is_choice(sym) &&
1191 sym_get_tristate_value(sym) == yes)
1192 conf_choice(submenu);
1193 else if (submenu->prompt &&
1194 submenu->prompt->type == P_MENU)
1195 conf(submenu);
1196 else if (res == 10)
1197 sym_toggle_tristate_value(sym);
1198 break;
1199 case 's':
1200 conf_string(submenu);
1201 break;
1202 }
1203 break;
1204 case 'y':
1205 if (item_is_tag('t')) {
1206 if (sym_set_tristate_value(sym, yes))
1207 break;
1208 if (sym_set_tristate_value(sym, mod))
1209 btn_dialog(main_window, setmod_text, 0);
1210 }
1211 break;
1212 case 'n':
1213 if (item_is_tag('t'))
1214 sym_set_tristate_value(sym, no);
1215 break;
1216 case 'm':
1217 if (item_is_tag('t'))
1218 sym_set_tristate_value(sym, mod);
1219 break;
1220 }
1221 }
1222}
1223
1224static void conf_message_callback(const char *fmt, va_list ap)
1225{
1226 char buf[1024];
1227
1228 vsnprintf(buf, sizeof(buf), fmt, ap);
1229 btn_dialog(main_window, buf, 1, "<OK>");
1230}
1231
1232static void show_help(struct menu *menu)
1233{
1234 struct gstr help;
1235
1236 if (!menu)
1237 return;
1238
1239 help = str_new();
1240 menu_get_ext_help(menu, &help);
1241 show_scroll_win(main_window, _(menu_get_prompt(menu)), str_get(&help));
1242 str_free(&help);
1243}
1244
1245static void conf_choice(struct menu *menu)
1246{
1247 const char *prompt = _(menu_get_prompt(menu));
1248 struct menu *child = 0;
1249 struct symbol *active;
1250 int selected_index = 0;
1251 int last_top_row = 0;
1252 int res, i = 0;
1253 struct match_state match_state = {
1254 .in_search = 0,
1255 .match_direction = MATCH_TINKER_PATTERN_DOWN,
1256 .pattern = "",
1257 };
1258
1259 active = sym_get_choice_value(menu->sym);
1260 /* this is mostly duplicated from the conf() function. */
1261 while (!global_exit) {
1262 reset_menu();
1263
1264 for (i = 0, child = menu->list; child; child = child->next) {
1265 if (!show_all_items && !menu_is_visible(child))
1266 continue;
1267
1268 if (child->sym == sym_get_choice_value(menu->sym))
1269 item_make(child, ':', "<X> %s",
1270 _(menu_get_prompt(child)));
1271 else if (child->sym)
1272 item_make(child, ':', " %s",
1273 _(menu_get_prompt(child)));
1274 else
1275 item_make(child, ':', "*** %s ***",
1276 _(menu_get_prompt(child)));
1277
1278 if (child->sym == active){
1279 last_top_row = top_row(curses_menu);
1280 selected_index = i;
1281 }
1282 i++;
1283 }
1284 show_menu(prompt ? _(prompt) : _("Choice Menu"),
1285 _(radiolist_instructions),
1286 selected_index,
1287 &last_top_row);
1288 while (!global_exit) {
1289 if (match_state.in_search) {
1290 mvprintw(0, 0, "searching: %s",
1291 match_state.pattern);
1292 clrtoeol();
1293 }
1294 refresh_all_windows(main_window);
1295 res = wgetch(menu_win(curses_menu));
1296 if (!res)
1297 break;
1298 if (do_match(res, &match_state, &selected_index) == 0) {
1299 if (selected_index != -1)
1300 center_item(selected_index,
1301 &last_top_row);
1302 continue;
1303 }
1304 if (process_special_keys(
1305 &res,
1306 (struct menu *) item_data()))
1307 break;
1308 switch (res) {
1309 case KEY_DOWN:
1310 menu_driver(curses_menu, REQ_DOWN_ITEM);
1311 break;
1312 case KEY_UP:
1313 menu_driver(curses_menu, REQ_UP_ITEM);
1314 break;
1315 case KEY_NPAGE:
1316 menu_driver(curses_menu, REQ_SCR_DPAGE);
1317 break;
1318 case KEY_PPAGE:
1319 menu_driver(curses_menu, REQ_SCR_UPAGE);
1320 break;
1321 case KEY_HOME:
1322 menu_driver(curses_menu, REQ_FIRST_ITEM);
1323 break;
1324 case KEY_END:
1325 menu_driver(curses_menu, REQ_LAST_ITEM);
1326 break;
1327 case 'h':
1328 case '?':
1329 show_help((struct menu *) item_data());
1330 break;
1331 }
1332 if (res == 10 || res == 27 || res == ' ' ||
1333 res == KEY_LEFT){
1334 break;
1335 }
1336 refresh_all_windows(main_window);
1337 }
1338 /* if ESC or left */
1339 if (res == 27 || res == KEY_LEFT)
1340 break;
1341
1342 child = item_data();
1343 if (!child || !menu_is_visible(child) || !child->sym)
1344 continue;
1345 switch (res) {
1346 case ' ':
1347 case 10:
1348 case KEY_RIGHT:
1349 sym_set_tristate_value(child->sym, yes);
1350 return;
1351 case 'h':
1352 case '?':
1353 show_help(child);
1354 active = child->sym;
1355 break;
1356 case KEY_EXIT:
1357 return;
1358 }
1359 }
1360}
1361
1362static void conf_string(struct menu *menu)
1363{
1364 const char *prompt = menu_get_prompt(menu);
1365
1366 while (1) {
1367 int res;
1368 const char *heading;
1369
1370 switch (sym_get_type(menu->sym)) {
1371 case S_INT:
1372 heading = _(inputbox_instructions_int);
1373 break;
1374 case S_HEX:
1375 heading = _(inputbox_instructions_hex);
1376 break;
1377 case S_STRING:
1378 heading = _(inputbox_instructions_string);
1379 break;
1380 default:
1381 heading = _("Internal nconf error!");
1382 }
1383 res = dialog_inputbox(main_window,
1384 prompt ? _(prompt) : _("Main Menu"),
1385 heading,
1386 sym_get_string_value(menu->sym),
1387 &dialog_input_result,
1388 &dialog_input_result_len);
1389 switch (res) {
1390 case 0:
1391 if (sym_set_string_value(menu->sym,
1392 dialog_input_result))
1393 return;
1394 btn_dialog(main_window,
1395 _("You have made an invalid entry."), 0);
1396 break;
1397 case 1:
1398 show_help(menu);
1399 break;
1400 case KEY_EXIT:
1401 return;
1402 }
1403 }
1404}
1405
1406static void conf_load(void)
1407{
1408 while (1) {
1409 int res;
1410 res = dialog_inputbox(main_window,
1411 NULL, load_config_text,
1412 filename,
1413 &dialog_input_result,
1414 &dialog_input_result_len);
1415 switch (res) {
1416 case 0:
1417 if (!dialog_input_result[0])
1418 return;
1419 if (!conf_read(dialog_input_result)) {
1420 set_config_filename(dialog_input_result);
1421 sym_set_change_count(1);
1422 return;
1423 }
1424 btn_dialog(main_window, _("File does not exist!"), 0);
1425 break;
1426 case 1:
1427 show_scroll_win(main_window,
1428 _("Load Alternate Configuration"),
1429 load_config_help);
1430 break;
1431 case KEY_EXIT:
1432 return;
1433 }
1434 }
1435}
1436
1437static void conf_save(void)
1438{
1439 while (1) {
1440 int res;
1441 res = dialog_inputbox(main_window,
1442 NULL, save_config_text,
1443 filename,
1444 &dialog_input_result,
1445 &dialog_input_result_len);
1446 switch (res) {
1447 case 0:
1448 if (!dialog_input_result[0])
1449 return;
1450 res = conf_write(dialog_input_result);
1451 if (!res) {
1452 set_config_filename(dialog_input_result);
1453 return;
1454 }
1455 btn_dialog(main_window, _("Can't create file! "
1456 "Probably a nonexistent directory."),
1457 1, "<OK>");
1458 break;
1459 case 1:
1460 show_scroll_win(main_window,
1461 _("Save Alternate Configuration"),
1462 save_config_help);
1463 break;
1464 case KEY_EXIT:
1465 return;
1466 }
1467 }
1468}
1469
1470void setup_windows(void)
1471{
1472 int lines, columns;
1473
1474 getmaxyx(stdscr, lines, columns);
1475
1476 if (main_window != NULL)
1477 delwin(main_window);
1478
1479 /* set up the menu and menu window */
1480 main_window = newwin(lines-2, columns-2, 2, 1);
1481 keypad(main_window, TRUE);
1482 mwin_max_lines = lines-7;
1483 mwin_max_cols = columns-6;
1484
1485 /* panels order is from bottom to top */
1486 new_panel(main_window);
1487}
1488
1489int main(int ac, char **av)
1490{
1491 int lines, columns;
1492 char *mode;
1493
1494 setlocale(LC_ALL, "");
1495 bindtextdomain(PACKAGE, LOCALEDIR);
1496 textdomain(PACKAGE);
1497
1498 conf_parse(av[1]);
1499 conf_read(NULL);
1500
1501 mode = getenv("NCONFIG_MODE");
1502 if (mode) {
1503 if (!strcasecmp(mode, "single_menu"))
1504 single_menu_mode = 1;
1505 }
1506
1507 /* Initialize curses */
1508 initscr();
1509 /* set color theme */
1510 set_colors();
1511
1512 cbreak();
1513 noecho();
1514 keypad(stdscr, TRUE);
1515 curs_set(0);
1516
1517 getmaxyx(stdscr, lines, columns);
1518 if (columns < 75 || lines < 20) {
1519 endwin();
1520 printf("Your terminal should have at "
1521 "least 20 lines and 75 columns\n");
1522 return 1;
1523 }
1524
1525 notimeout(stdscr, FALSE);
1526#if NCURSES_REENTRANT
1527 set_escdelay(1);
1528#else
1529 ESCDELAY = 1;
1530#endif
1531
1532 /* set btns menu */
1533 curses_menu = new_menu(curses_menu_items);
1534 menu_opts_off(curses_menu, O_SHOWDESC);
1535 menu_opts_on(curses_menu, O_SHOWMATCH);
1536 menu_opts_on(curses_menu, O_ONEVALUE);
1537 menu_opts_on(curses_menu, O_NONCYCLIC);
1538 menu_opts_on(curses_menu, O_IGNORECASE);
1539 set_menu_mark(curses_menu, " ");
1540 set_menu_fore(curses_menu, attributes[MAIN_MENU_FORE]);
1541 set_menu_back(curses_menu, attributes[MAIN_MENU_BACK]);
1542 set_menu_grey(curses_menu, attributes[MAIN_MENU_GREY]);
1543
1544 set_config_filename(conf_get_configname());
1545 setup_windows();
1546
1547 /* check for KEY_FUNC(1) */
1548 if (has_key(KEY_F(1)) == FALSE) {
1549 show_scroll_win(main_window,
1550 _("Instructions"),
1551 _(menu_no_f_instructions));
1552 }
1553
1554 conf_set_message_callback(conf_message_callback);
1555 /* do the work */
1556 while (!global_exit) {
1557 conf(&rootmenu);
1558 if (!global_exit && do_exit() == 0)
1559 break;
1560 }
1561 /* ok, we are done */
1562 unpost_menu(curses_menu);
1563 free_menu(curses_menu);
1564 delwin(main_window);
1565 clear();
1566 refresh();
1567 endwin();
1568 return 0;
1569}