Root/scripts/kconfig/nconf.c

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

Archive Download this file



interactive