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