M7350/kernel/scripts/kconfig/nconf.c

1557 lines
38 KiB
C
Raw Permalink Normal View History

2024-09-09 08:52:07 +00:00
/*
* Copyright (C) 2008 Nir Tzachar <nir.tzachar@gmail.com?
* Released under the terms of the GNU GPL v2.0.
*
* Derived from menuconfig.
*
*/
#define _GNU_SOURCE
#include <string.h>
2024-09-09 08:57:42 +00:00
#include <stdlib.h>
2024-09-09 08:52:07 +00:00
#include "lkc.h"
#include "nconf.h"
#include <ctype.h>
2024-09-09 08:57:42 +00:00
static const char nconf_global_help[] = N_(
"Help windows\n"
"------------\n"
"o Global help: Unless in a data entry window, pressing <F1> will give \n"
" you the global help window, which you are just reading.\n"
2024-09-09 08:52:07 +00:00
"\n"
2024-09-09 08:57:42 +00:00
"o A short version of the global help is available by pressing <F3>.\n"
2024-09-09 08:52:07 +00:00
"\n"
2024-09-09 08:57:42 +00:00
"o Local help: To get help related to the current menu entry, use any\n"
" of <?> <h>, or if in a data entry window then press <F1>.\n"
2024-09-09 08:52:07 +00:00
"\n"
"\n"
2024-09-09 08:57:42 +00:00
"Menu entries\n"
"------------\n"
"This interface lets you select features and parameters for the kernel\n"
"build. Kernel features can either be built-in, modularized, or removed.\n"
"Parameters must be entered as text or decimal or hexadecimal numbers.\n"
2024-09-09 08:52:07 +00:00
"\n"
2024-09-09 08:57:42 +00:00
"Menu entries beginning with following braces represent features that\n"
" [ ] can be built in or removed\n"
" < > can be built in, modularized or removed\n"
" { } can be built in or modularized, are selected by another feature\n"
" - - are selected by another feature\n"
" XXX cannot be selected. Symbol Info <F2> tells you why.\n"
"*, M or whitespace inside braces means to build in, build as a module\n"
"or to exclude the feature respectively.\n"
2024-09-09 08:52:07 +00:00
"\n"
2024-09-09 08:57:42 +00:00
"To change any of these features, highlight it with the movement keys\n"
"listed below and press <y> to build it in, <m> to make it a module or\n"
"<n> to remove it. You may press the <Space> key to cycle through the\n"
"available options.\n"
2024-09-09 08:52:07 +00:00
"\n"
2024-09-09 08:57:42 +00:00
"A trailing \"--->\" designates a submenu, a trailing \"----\" an\n"
"empty submenu.\n"
2024-09-09 08:52:07 +00:00
"\n"
2024-09-09 08:57:42 +00:00
"Menu navigation keys\n"
"----------------------------------------------------------------------\n"
"Linewise up <Up>\n"
"Linewise down <Down>\n"
"Pagewise up <Page Up>\n"
"Pagewise down <Page Down>\n"
"First entry <Home>\n"
"Last entry <End>\n"
"Enter a submenu <Right> <Enter>\n"
"Go back to parent menu <Left> <Esc> <F5>\n"
"Close a help window <Enter> <Esc> <F5>\n"
"Close entry window, apply <Enter>\n"
"Close entry window, forget <Esc> <F5>\n"
"Start incremental, case-insensitive search for STRING in menu entries,\n"
" no regex support, STRING is displayed in upper left corner\n"
" </>STRING\n"
" Remove last character <Backspace>\n"
" Jump to next hit <Down>\n"
" Jump to previous hit <Up>\n"
"Exit menu search mode </> <Esc>\n"
"Search for configuration variables with or without leading CONFIG_\n"
" <F8>RegExpr<Enter>\n"
"Verbose search help <F8><F1>\n"
"----------------------------------------------------------------------\n"
2024-09-09 08:52:07 +00:00
"\n"
2024-09-09 08:57:42 +00:00
"Unless in a data entry window, key <1> may be used instead of <F1>,\n"
"<2> instead of <F2>, etc.\n"
2024-09-09 08:52:07 +00:00
"\n"
"\n"
2024-09-09 08:57:42 +00:00
"Radiolist (Choice list)\n"
"-----------------------\n"
"Use the movement keys listed above to select the option you wish to set\n"
"and press <Space>.\n"
2024-09-09 08:52:07 +00:00
"\n"
"\n"
2024-09-09 08:57:42 +00:00
"Data entry\n"
"----------\n"
"Enter the requested information and press <Enter>. Hexadecimal values\n"
"may be entered without the \"0x\" prefix.\n"
2024-09-09 08:52:07 +00:00
"\n"
"\n"
2024-09-09 08:57:42 +00:00
"Text Box (Help Window)\n"
"----------------------\n"
"Use movement keys as listed in table above.\n"
2024-09-09 08:52:07 +00:00
"\n"
2024-09-09 08:57:42 +00:00
"Press any of <Enter> <Esc> <q> <F5> <F9> to exit.\n"
2024-09-09 08:52:07 +00:00
"\n"
"\n"
2024-09-09 08:57:42 +00:00
"Alternate configuration files\n"
2024-09-09 08:52:07 +00:00
"-----------------------------\n"
2024-09-09 08:57:42 +00:00
"nconfig supports switching between different configurations.\n"
"Press <F6> to save your current configuration. Press <F7> and enter\n"
"a file name to load a previously saved configuration.\n"
2024-09-09 08:52:07 +00:00
"\n"
"\n"
2024-09-09 08:57:42 +00:00
"Terminal configuration\n"
"----------------------\n"
"If you use nconfig in a xterm window, make sure your TERM environment\n"
"variable specifies a terminal configuration which supports at least\n"
"16 colors. Otherwise nconfig will look rather bad.\n"
2024-09-09 08:52:07 +00:00
"\n"
2024-09-09 08:57:42 +00:00
"If the \"stty size\" command reports the current terminalsize correctly,\n"
"nconfig will adapt to sizes larger than the traditional 80x25 \"standard\"\n"
"and display longer menus properly.\n"
2024-09-09 08:52:07 +00:00
"\n"
"\n"
2024-09-09 08:57:42 +00:00
"Single menu mode\n"
"----------------\n"
"If you prefer to have all of the menu entries listed in a single menu,\n"
"rather than the default multimenu hierarchy, run nconfig with\n"
"NCONFIG_MODE environment variable set to single_menu. Example:\n"
2024-09-09 08:52:07 +00:00
"\n"
"make NCONFIG_MODE=single_menu nconfig\n"
"\n"
2024-09-09 08:57:42 +00:00
"<Enter> will then unfold the appropriate category, or fold it if it\n"
"is already unfolded. Folded menu entries will be designated by a\n"
"leading \"++>\" and unfolded entries by a leading \"-->\".\n"
2024-09-09 08:52:07 +00:00
"\n"
2024-09-09 08:57:42 +00:00
"Note that this mode can eventually be a little more CPU expensive than\n"
"the default mode, especially with a larger number of unfolded submenus.\n"
2024-09-09 08:52:07 +00:00
"\n"),
menu_no_f_instructions[] = N_(
2024-09-09 08:57:42 +00:00
"Legend: [*] built-in [ ] excluded <M> module < > module capable.\n"
"Submenus are designated by a trailing \"--->\", empty ones by \"----\".\n"
"\n"
"Use the following keys to navigate the menus:\n"
"Move up or down with <Up> and <Down>.\n"
"Enter a submenu with <Enter> or <Right>.\n"
"Exit a submenu to its parent menu with <Esc> or <Left>.\n"
"Pressing <y> includes, <n> excludes, <m> modularizes features.\n"
"Pressing <Space> cycles through the available options.\n"
"To search for menu entries press </>.\n"
"<Esc> always leaves the current window.\n"
"\n"
"You do not have function keys support.\n"
"Press <1> instead of <F1>, <2> instead of <F2>, etc.\n"
"For verbose global help use key <1>.\n"
"For help related to the current menu entry press <?> or <h>.\n"),
2024-09-09 08:52:07 +00:00
menu_instructions[] = N_(
2024-09-09 08:57:42 +00:00
"Legend: [*] built-in [ ] excluded <M> module < > module capable.\n"
"Submenus are designated by a trailing \"--->\", empty ones by \"----\".\n"
"\n"
"Use the following keys to navigate the menus:\n"
"Move up or down with <Up> or <Down>.\n"
"Enter a submenu with <Enter> or <Right>.\n"
"Exit a submenu to its parent menu with <Esc> or <Left>.\n"
"Pressing <y> includes, <n> excludes, <m> modularizes features.\n"
"Pressing <Space> cycles through the available options.\n"
"To search for menu entries press </>.\n"
"<Esc> always leaves the current window.\n"
"\n"
"Pressing <1> may be used instead of <F1>, <2> instead of <F2>, etc.\n"
"For verbose global help press <F1>.\n"
"For help related to the current menu entry press <?> or <h>.\n"),
2024-09-09 08:52:07 +00:00
radiolist_instructions[] = N_(
2024-09-09 08:57:42 +00:00
"Press <Up>, <Down>, <Home> or <End> to navigate a radiolist, select\n"
"with <Space>.\n"
"For help related to the current entry press <?> or <h>.\n"
"For global help press <F1>.\n"),
2024-09-09 08:52:07 +00:00
inputbox_instructions_int[] = N_(
"Please enter a decimal value.\n"
"Fractions will not be accepted.\n"
2024-09-09 08:57:42 +00:00
"Press <Enter> to apply, <Esc> to cancel."),
2024-09-09 08:52:07 +00:00
inputbox_instructions_hex[] = N_(
"Please enter a hexadecimal value.\n"
2024-09-09 08:57:42 +00:00
"Press <Enter> to apply, <Esc> to cancel."),
2024-09-09 08:52:07 +00:00
inputbox_instructions_string[] = N_(
"Please enter a string value.\n"
2024-09-09 08:57:42 +00:00
"Press <Enter> to apply, <Esc> to cancel."),
2024-09-09 08:52:07 +00:00
setmod_text[] = N_(
2024-09-09 08:57:42 +00:00
"This feature depends on another feature which has been configured as a\n"
"module. As a result, the current feature will be built as a module too."),
2024-09-09 08:52:07 +00:00
load_config_text[] = N_(
"Enter the name of the configuration file you wish to load.\n"
2024-09-09 08:57:42 +00:00
"Accept the name shown to restore the configuration you last\n"
"retrieved. Leave empty to abort."),
2024-09-09 08:52:07 +00:00
load_config_help[] = N_(
"For various reasons, one may wish to keep several different\n"
"configurations available on a single machine.\n"
"\n"
"If you have saved a previous configuration in a file other than the\n"
2024-09-09 08:57:42 +00:00
"default one, entering its name here will allow you to load and modify\n"
"that configuration.\n"
2024-09-09 08:52:07 +00:00
"\n"
2024-09-09 08:57:42 +00:00
"Leave empty to abort.\n"),
2024-09-09 08:52:07 +00:00
save_config_text[] = N_(
"Enter a filename to which this configuration should be saved\n"
2024-09-09 08:57:42 +00:00
"as an alternate. Leave empty to abort."),
2024-09-09 08:52:07 +00:00
save_config_help[] = N_(
2024-09-09 08:57:42 +00:00
"For various reasons, one may wish to keep several different\n"
"configurations available on a single machine.\n"
2024-09-09 08:52:07 +00:00
"\n"
"Entering a file name here will allow you to later retrieve, modify\n"
"and use the current configuration as an alternate to whatever\n"
"configuration options you have selected at that time.\n"
"\n"
2024-09-09 08:57:42 +00:00
"Leave empty to abort.\n"),
2024-09-09 08:52:07 +00:00
search_help[] = N_(
2024-09-09 08:57:42 +00:00
"Search for symbols (configuration variable names CONFIG_*) and display\n"
"their relations. Regular expressions are supported.\n"
"Example: Search for \"^FOO\".\n"
2024-09-09 08:52:07 +00:00
"Result:\n"
"-----------------------------------------------------------------\n"
"Symbol: FOO [ = m]\n"
"Prompt: Foo bus is used to drive the bar HW\n"
"Defined at drivers/pci/Kconfig:47\n"
"Depends on: X86_LOCAL_APIC && X86_IO_APIC || IA64\n"
"Location:\n"
2024-09-09 08:57:42 +00:00
" -> Bus options (PCI, PCMCIA, EISA, ISA)\n"
2024-09-09 08:52:07 +00:00
" -> PCI support (PCI [ = y])\n"
" -> PCI access mode (<choice> [ = y])\n"
"Selects: LIBCRC32\n"
"Selected by: BAR\n"
"-----------------------------------------------------------------\n"
2024-09-09 08:57:42 +00:00
"o The line 'Prompt:' shows the text displayed for this symbol in\n"
" the menu hierarchy.\n"
"o The 'Defined at' line tells at what file / line number the symbol is\n"
" defined.\n"
"o The 'Depends on:' line lists symbols that need to be defined for\n"
" this symbol to be visible and selectable in the menu.\n"
"o The 'Location:' lines tell, where in the menu structure this symbol\n"
" is located. A location followed by a [ = y] indicates that this is\n"
" a selectable menu item, and the current value is displayed inside\n"
" brackets.\n"
"o The 'Selects:' line tells, what symbol will be automatically selected\n"
" if this symbol is selected (y or m).\n"
"o The 'Selected by' line tells what symbol has selected this symbol.\n"
2024-09-09 08:52:07 +00:00
"\n"
"Only relevant lines are shown.\n"
"\n\n"
"Search examples:\n"
2024-09-09 08:57:42 +00:00
"USB => find all symbols containing USB\n"
"^USB => find all symbols starting with USB\n"
"USB$ => find all symbols ending with USB\n"
2024-09-09 08:52:07 +00:00
"\n");
struct mitem {
char str[256];
char tag;
void *usrptr;
int is_visible;
};
#define MAX_MENU_ITEMS 4096
static int show_all_items;
static int indent;
static struct menu *current_menu;
static int child_count;
static int single_menu_mode;
/* the window in which all information appears */
static WINDOW *main_window;
/* the largest size of the menu window */
static int mwin_max_lines;
static int mwin_max_cols;
/* the window in which we show option buttons */
static MENU *curses_menu;
static ITEM *curses_menu_items[MAX_MENU_ITEMS];
static struct mitem k_menu_items[MAX_MENU_ITEMS];
static int items_num;
static int global_exit;
/* the currently selected button */
const char *current_instructions = menu_instructions;
static char *dialog_input_result;
static int dialog_input_result_len;
static void conf(struct menu *menu);
static void conf_choice(struct menu *menu);
static void conf_string(struct menu *menu);
static void conf_load(void);
static void conf_save(void);
static void show_help(struct menu *menu);
static int do_exit(void);
static void setup_windows(void);
static void search_conf(void);
typedef void (*function_key_handler_t)(int *key, struct menu *menu);
static void handle_f1(int *key, struct menu *current_item);
static void handle_f2(int *key, struct menu *current_item);
static void handle_f3(int *key, struct menu *current_item);
static void handle_f4(int *key, struct menu *current_item);
static void handle_f5(int *key, struct menu *current_item);
static void handle_f6(int *key, struct menu *current_item);
static void handle_f7(int *key, struct menu *current_item);
static void handle_f8(int *key, struct menu *current_item);
static void handle_f9(int *key, struct menu *current_item);
struct function_keys {
const char *key_str;
const char *func;
function_key key;
function_key_handler_t handler;
};
static const int function_keys_num = 9;
struct function_keys function_keys[] = {
{
.key_str = "F1",
.func = "Help",
.key = F_HELP,
.handler = handle_f1,
},
{
.key_str = "F2",
2024-09-09 08:57:42 +00:00
.func = "SymInfo",
2024-09-09 08:52:07 +00:00
.key = F_SYMBOL,
.handler = handle_f2,
},
{
.key_str = "F3",
2024-09-09 08:57:42 +00:00
.func = "Help 2",
2024-09-09 08:52:07 +00:00
.key = F_INSTS,
.handler = handle_f3,
},
{
.key_str = "F4",
2024-09-09 08:57:42 +00:00
.func = "ShowAll",
2024-09-09 08:52:07 +00:00
.key = F_CONF,
.handler = handle_f4,
},
{
.key_str = "F5",
.func = "Back",
.key = F_BACK,
.handler = handle_f5,
},
{
.key_str = "F6",
.func = "Save",
.key = F_SAVE,
.handler = handle_f6,
},
{
.key_str = "F7",
.func = "Load",
.key = F_LOAD,
.handler = handle_f7,
},
{
.key_str = "F8",
2024-09-09 08:57:42 +00:00
.func = "SymSearch",
2024-09-09 08:52:07 +00:00
.key = F_SEARCH,
.handler = handle_f8,
},
{
.key_str = "F9",
.func = "Exit",
.key = F_EXIT,
.handler = handle_f9,
},
};
static void print_function_line(void)
{
int i;
int offset = 1;
const int skip = 1;
2024-09-09 08:57:42 +00:00
int lines = getmaxy(stdscr);
2024-09-09 08:52:07 +00:00
for (i = 0; i < function_keys_num; i++) {
(void) wattrset(main_window, attributes[FUNCTION_HIGHLIGHT]);
2024-09-09 08:57:42 +00:00
mvwprintw(main_window, lines-3, offset,
2024-09-09 08:52:07 +00:00
"%s",
function_keys[i].key_str);
(void) wattrset(main_window, attributes[FUNCTION_TEXT]);
offset += strlen(function_keys[i].key_str);
2024-09-09 08:57:42 +00:00
mvwprintw(main_window, lines-3,
2024-09-09 08:52:07 +00:00
offset, "%s",
function_keys[i].func);
offset += strlen(function_keys[i].func) + skip;
}
(void) wattrset(main_window, attributes[NORMAL]);
}
/* help */
static void handle_f1(int *key, struct menu *current_item)
{
show_scroll_win(main_window,
2024-09-09 08:57:42 +00:00
_("Global help"), _(nconf_global_help));
2024-09-09 08:52:07 +00:00
return;
}
/* symbole help */
static void handle_f2(int *key, struct menu *current_item)
{
show_help(current_item);
return;
}
/* instructions */
static void handle_f3(int *key, struct menu *current_item)
{
show_scroll_win(main_window,
2024-09-09 08:57:42 +00:00
_("Short help"),
2024-09-09 08:52:07 +00:00
_(current_instructions));
return;
}
/* config */
static void handle_f4(int *key, struct menu *current_item)
{
int res = btn_dialog(main_window,
_("Show all symbols?"),
2,
" <Show All> ",
"<Don't show all>");
if (res == 0)
show_all_items = 1;
else if (res == 1)
show_all_items = 0;
return;
}
/* back */
static void handle_f5(int *key, struct menu *current_item)
{
*key = KEY_LEFT;
return;
}
/* save */
static void handle_f6(int *key, struct menu *current_item)
{
conf_save();
return;
}
/* load */
static void handle_f7(int *key, struct menu *current_item)
{
conf_load();
return;
}
/* search */
static void handle_f8(int *key, struct menu *current_item)
{
search_conf();
return;
}
/* exit */
static void handle_f9(int *key, struct menu *current_item)
{
do_exit();
return;
}
/* return != 0 to indicate the key was handles */
static int process_special_keys(int *key, struct menu *menu)
{
int i;
if (*key == KEY_RESIZE) {
setup_windows();
return 1;
}
for (i = 0; i < function_keys_num; i++) {
if (*key == KEY_F(function_keys[i].key) ||
*key == '0' + function_keys[i].key){
function_keys[i].handler(key, menu);
return 1;
}
}
return 0;
}
static void clean_items(void)
{
int i;
for (i = 0; curses_menu_items[i]; i++)
free_item(curses_menu_items[i]);
bzero(curses_menu_items, sizeof(curses_menu_items));
bzero(k_menu_items, sizeof(k_menu_items));
items_num = 0;
}
typedef enum {MATCH_TINKER_PATTERN_UP, MATCH_TINKER_PATTERN_DOWN,
FIND_NEXT_MATCH_DOWN, FIND_NEXT_MATCH_UP} match_f;
/* return the index of the matched item, or -1 if no such item exists */
static int get_mext_match(const char *match_str, match_f flag)
{
int match_start = item_index(current_item(curses_menu));
int index;
if (flag == FIND_NEXT_MATCH_DOWN)
++match_start;
else if (flag == FIND_NEXT_MATCH_UP)
--match_start;
index = match_start;
index = (index + items_num) % items_num;
while (true) {
char *str = k_menu_items[index].str;
if (strcasestr(str, match_str) != 0)
return index;
if (flag == FIND_NEXT_MATCH_UP ||
flag == MATCH_TINKER_PATTERN_UP)
--index;
else
++index;
index = (index + items_num) % items_num;
if (index == match_start)
return -1;
}
}
/* Make a new item. */
static void item_make(struct menu *menu, char tag, const char *fmt, ...)
{
va_list ap;
if (items_num > MAX_MENU_ITEMS-1)
return;
bzero(&k_menu_items[items_num], sizeof(k_menu_items[0]));
k_menu_items[items_num].tag = tag;
k_menu_items[items_num].usrptr = menu;
if (menu != NULL)
k_menu_items[items_num].is_visible =
menu_is_visible(menu);
else
k_menu_items[items_num].is_visible = 1;
va_start(ap, fmt);
vsnprintf(k_menu_items[items_num].str,
sizeof(k_menu_items[items_num].str),
fmt, ap);
va_end(ap);
if (!k_menu_items[items_num].is_visible)
memcpy(k_menu_items[items_num].str, "XXX", 3);
curses_menu_items[items_num] = new_item(
k_menu_items[items_num].str,
k_menu_items[items_num].str);
set_item_userptr(curses_menu_items[items_num],
&k_menu_items[items_num]);
/*
if (!k_menu_items[items_num].is_visible)
item_opts_off(curses_menu_items[items_num], O_SELECTABLE);
*/
items_num++;
curses_menu_items[items_num] = NULL;
}
/* very hackish. adds a string to the last item added */
static void item_add_str(const char *fmt, ...)
{
va_list ap;
int index = items_num-1;
char new_str[256];
char tmp_str[256];
if (index < 0)
return;
va_start(ap, fmt);
vsnprintf(new_str, sizeof(new_str), fmt, ap);
va_end(ap);
snprintf(tmp_str, sizeof(tmp_str), "%s%s",
k_menu_items[index].str, new_str);
strncpy(k_menu_items[index].str,
tmp_str,
sizeof(k_menu_items[index].str));
free_item(curses_menu_items[index]);
curses_menu_items[index] = new_item(
k_menu_items[index].str,
k_menu_items[index].str);
set_item_userptr(curses_menu_items[index],
&k_menu_items[index]);
}
/* get the tag of the currently selected item */
static char item_tag(void)
{
ITEM *cur;
struct mitem *mcur;
cur = current_item(curses_menu);
if (cur == NULL)
return 0;
mcur = (struct mitem *) item_userptr(cur);
return mcur->tag;
}
static int curses_item_index(void)
{
return item_index(current_item(curses_menu));
}
static void *item_data(void)
{
ITEM *cur;
struct mitem *mcur;
cur = current_item(curses_menu);
if (!cur)
return NULL;
mcur = (struct mitem *) item_userptr(cur);
return mcur->usrptr;
}
static int item_is_tag(char tag)
{
return item_tag() == tag;
}
static char filename[PATH_MAX+1];
static char menu_backtitle[PATH_MAX+128];
static const char *set_config_filename(const char *config_filename)
{
int size;
size = snprintf(menu_backtitle, sizeof(menu_backtitle),
"%s - %s", config_filename, rootmenu.prompt->text);
if (size >= sizeof(menu_backtitle))
menu_backtitle[sizeof(menu_backtitle)-1] = '\0';
size = snprintf(filename, sizeof(filename), "%s", config_filename);
if (size >= sizeof(filename))
filename[sizeof(filename)-1] = '\0';
return menu_backtitle;
}
/* return = 0 means we are successful.
* -1 means go on doing what you were doing
*/
static int do_exit(void)
{
int res;
if (!conf_get_changed()) {
global_exit = 1;
return 0;
}
res = btn_dialog(main_window,
_("Do you wish to save your new configuration?\n"
"<ESC> to cancel and resume nconfig."),
2,
" <save> ",
"<don't save>");
if (res == KEY_EXIT) {
global_exit = 0;
return -1;
}
/* if we got here, the user really wants to exit */
switch (res) {
case 0:
res = conf_write(filename);
if (res)
btn_dialog(
main_window,
_("Error during writing of configuration.\n"
"Your configuration changes were NOT saved."),
1,
"<OK>");
break;
default:
btn_dialog(
main_window,
_("Your configuration changes were NOT saved."),
1,
"<OK>");
break;
}
global_exit = 1;
return 0;
}
static void search_conf(void)
{
struct symbol **sym_arr;
struct gstr res;
2024-09-09 08:57:42 +00:00
struct gstr title;
2024-09-09 08:52:07 +00:00
char *dialog_input;
int dres;
2024-09-09 08:57:42 +00:00
title = str_new();
str_printf( &title, _("Enter (sub)string or regexp to search for "
"(with or without \"%s\")"), CONFIG_);
2024-09-09 08:52:07 +00:00
again:
dres = dialog_inputbox(main_window,
_("Search Configuration Parameter"),
2024-09-09 08:57:42 +00:00
str_get(&title),
2024-09-09 08:52:07 +00:00
"", &dialog_input_result, &dialog_input_result_len);
switch (dres) {
case 0:
break;
case 1:
show_scroll_win(main_window,
_("Search Configuration"), search_help);
goto again;
default:
2024-09-09 08:57:42 +00:00
str_free(&title);
2024-09-09 08:52:07 +00:00
return;
}
/* strip the prefix if necessary */
dialog_input = dialog_input_result;
if (strncasecmp(dialog_input_result, CONFIG_, strlen(CONFIG_)) == 0)
dialog_input += strlen(CONFIG_);
sym_arr = sym_re_search(dialog_input);
2024-09-09 08:57:42 +00:00
res = get_relations_str(sym_arr, NULL);
2024-09-09 08:52:07 +00:00
free(sym_arr);
show_scroll_win(main_window,
_("Search Results"), str_get(&res));
str_free(&res);
2024-09-09 08:57:42 +00:00
str_free(&title);
2024-09-09 08:52:07 +00:00
}
static void build_conf(struct menu *menu)
{
struct symbol *sym;
struct property *prop;
struct menu *child;
int type, tmp, doint = 2;
tristate val;
char ch;
if (!menu || (!show_all_items && !menu_is_visible(menu)))
return;
sym = menu->sym;
prop = menu->prompt;
if (!sym) {
if (prop && menu != current_menu) {
const char *prompt = menu_get_prompt(menu);
enum prop_type ptype;
ptype = menu->prompt ? menu->prompt->type : P_UNKNOWN;
switch (ptype) {
case P_MENU:
child_count++;
prompt = _(prompt);
if (single_menu_mode) {
item_make(menu, 'm',
"%s%*c%s",
menu->data ? "-->" : "++>",
indent + 1, ' ', prompt);
} else
item_make(menu, 'm',
2024-09-09 08:57:42 +00:00
" %*c%s %s",
indent + 1, ' ', prompt,
menu_is_empty(menu) ? "----" : "--->");
2024-09-09 08:52:07 +00:00
if (single_menu_mode && menu->data)
goto conf_childs;
return;
case P_COMMENT:
if (prompt) {
child_count++;
item_make(menu, ':',
" %*c*** %s ***",
indent + 1, ' ',
_(prompt));
}
break;
default:
if (prompt) {
child_count++;
item_make(menu, ':', "---%*c%s",
indent + 1, ' ',
_(prompt));
}
}
} else
doint = 0;
goto conf_childs;
}
type = sym_get_type(sym);
if (sym_is_choice(sym)) {
struct symbol *def_sym = sym_get_choice_value(sym);
struct menu *def_menu = NULL;
child_count++;
for (child = menu->list; child; child = child->next) {
if (menu_is_visible(child) && child->sym == def_sym)
def_menu = child;
}
val = sym_get_tristate_value(sym);
if (sym_is_changable(sym)) {
switch (type) {
case S_BOOLEAN:
item_make(menu, 't', "[%c]",
val == no ? ' ' : '*');
break;
case S_TRISTATE:
switch (val) {
case yes:
ch = '*';
break;
case mod:
ch = 'M';
break;
default:
ch = ' ';
break;
}
item_make(menu, 't', "<%c>", ch);
break;
}
} else {
item_make(menu, def_menu ? 't' : ':', " ");
}
item_add_str("%*c%s", indent + 1,
' ', _(menu_get_prompt(menu)));
if (val == yes) {
if (def_menu) {
item_add_str(" (%s)",
_(menu_get_prompt(def_menu)));
item_add_str(" --->");
if (def_menu->list) {
indent += 2;
build_conf(def_menu);
indent -= 2;
}
}
return;
}
} else {
if (menu == current_menu) {
item_make(menu, ':',
"---%*c%s", indent + 1,
' ', _(menu_get_prompt(menu)));
goto conf_childs;
}
child_count++;
val = sym_get_tristate_value(sym);
if (sym_is_choice_value(sym) && val == yes) {
item_make(menu, ':', " ");
} else {
switch (type) {
case S_BOOLEAN:
if (sym_is_changable(sym))
item_make(menu, 't', "[%c]",
val == no ? ' ' : '*');
else
item_make(menu, 't', "-%c-",
val == no ? ' ' : '*');
break;
case S_TRISTATE:
switch (val) {
case yes:
ch = '*';
break;
case mod:
ch = 'M';
break;
default:
ch = ' ';
break;
}
if (sym_is_changable(sym)) {
if (sym->rev_dep.tri == mod)
item_make(menu,
't', "{%c}", ch);
else
item_make(menu,
't', "<%c>", ch);
} else
item_make(menu, 't', "-%c-", ch);
break;
default:
tmp = 2 + strlen(sym_get_string_value(sym));
item_make(menu, 's', " (%s)",
sym_get_string_value(sym));
tmp = indent - tmp + 4;
if (tmp < 0)
tmp = 0;
item_add_str("%*c%s%s", tmp, ' ',
_(menu_get_prompt(menu)),
(sym_has_value(sym) ||
!sym_is_changable(sym)) ? "" :
_(" (NEW)"));
goto conf_childs;
}
}
item_add_str("%*c%s%s", indent + 1, ' ',
_(menu_get_prompt(menu)),
(sym_has_value(sym) || !sym_is_changable(sym)) ?
"" : _(" (NEW)"));
if (menu->prompt && menu->prompt->type == P_MENU) {
2024-09-09 08:57:42 +00:00
item_add_str(" %s", menu_is_empty(menu) ? "----" : "--->");
2024-09-09 08:52:07 +00:00
return;
}
}
conf_childs:
indent += doint;
for (child = menu->list; child; child = child->next)
build_conf(child);
indent -= doint;
}
static void reset_menu(void)
{
unpost_menu(curses_menu);
clean_items();
}
/* adjust the menu to show this item.
* prefer not to scroll the menu if possible*/
static void center_item(int selected_index, int *last_top_row)
{
int toprow;
set_top_row(curses_menu, *last_top_row);
toprow = top_row(curses_menu);
if (selected_index < toprow ||
selected_index >= toprow+mwin_max_lines) {
toprow = max(selected_index-mwin_max_lines/2, 0);
if (toprow >= item_count(curses_menu)-mwin_max_lines)
toprow = item_count(curses_menu)-mwin_max_lines;
set_top_row(curses_menu, toprow);
}
set_current_item(curses_menu,
curses_menu_items[selected_index]);
*last_top_row = toprow;
post_menu(curses_menu);
refresh_all_windows(main_window);
}
/* this function assumes reset_menu has been called before */
static void show_menu(const char *prompt, const char *instructions,
int selected_index, int *last_top_row)
{
int maxx, maxy;
WINDOW *menu_window;
current_instructions = instructions;
clear();
(void) wattrset(main_window, attributes[NORMAL]);
2024-09-09 08:57:42 +00:00
print_in_middle(stdscr, 1, 0, getmaxx(stdscr),
2024-09-09 08:52:07 +00:00
menu_backtitle,
attributes[MAIN_HEADING]);
(void) wattrset(main_window, attributes[MAIN_MENU_BOX]);
box(main_window, 0, 0);
(void) wattrset(main_window, attributes[MAIN_MENU_HEADING]);
mvwprintw(main_window, 0, 3, " %s ", prompt);
(void) wattrset(main_window, attributes[NORMAL]);
set_menu_items(curses_menu, curses_menu_items);
/* position the menu at the middle of the screen */
scale_menu(curses_menu, &maxy, &maxx);
maxx = min(maxx, mwin_max_cols-2);
maxy = mwin_max_lines;
menu_window = derwin(main_window,
maxy,
maxx,
2,
(mwin_max_cols-maxx)/2);
keypad(menu_window, TRUE);
set_menu_win(curses_menu, menu_window);
set_menu_sub(curses_menu, menu_window);
/* must reassert this after changing items, otherwise returns to a
* default of 16
*/
set_menu_format(curses_menu, maxy, 1);
center_item(selected_index, last_top_row);
set_menu_format(curses_menu, maxy, 1);
print_function_line();
/* Post the menu */
post_menu(curses_menu);
refresh_all_windows(main_window);
}
static void adj_match_dir(match_f *match_direction)
{
if (*match_direction == FIND_NEXT_MATCH_DOWN)
*match_direction =
MATCH_TINKER_PATTERN_DOWN;
else if (*match_direction == FIND_NEXT_MATCH_UP)
*match_direction =
MATCH_TINKER_PATTERN_UP;
/* else, do no change.. */
}
struct match_state
{
int in_search;
match_f match_direction;
char pattern[256];
};
/* Return 0 means I have handled the key. In such a case, ans should hold the
* item to center, or -1 otherwise.
* Else return -1 .
*/
static int do_match(int key, struct match_state *state, int *ans)
{
char c = (char) key;
int terminate_search = 0;
*ans = -1;
if (key == '/' || (state->in_search && key == 27)) {
move(0, 0);
refresh();
clrtoeol();
state->in_search = 1-state->in_search;
bzero(state->pattern, sizeof(state->pattern));
state->match_direction = MATCH_TINKER_PATTERN_DOWN;
return 0;
} else if (!state->in_search)
return 1;
if (isalnum(c) || isgraph(c) || c == ' ') {
state->pattern[strlen(state->pattern)] = c;
state->pattern[strlen(state->pattern)] = '\0';
adj_match_dir(&state->match_direction);
*ans = get_mext_match(state->pattern,
state->match_direction);
} else if (key == KEY_DOWN) {
state->match_direction = FIND_NEXT_MATCH_DOWN;
*ans = get_mext_match(state->pattern,
state->match_direction);
} else if (key == KEY_UP) {
state->match_direction = FIND_NEXT_MATCH_UP;
*ans = get_mext_match(state->pattern,
state->match_direction);
} else if (key == KEY_BACKSPACE || key == 127) {
state->pattern[strlen(state->pattern)-1] = '\0';
adj_match_dir(&state->match_direction);
} else
terminate_search = 1;
if (terminate_search) {
state->in_search = 0;
bzero(state->pattern, sizeof(state->pattern));
move(0, 0);
refresh();
clrtoeol();
return -1;
}
return 0;
}
static void conf(struct menu *menu)
{
struct menu *submenu = 0;
const char *prompt = menu_get_prompt(menu);
struct symbol *sym;
int res;
int current_index = 0;
int last_top_row = 0;
struct match_state match_state = {
.in_search = 0,
.match_direction = MATCH_TINKER_PATTERN_DOWN,
.pattern = "",
};
while (!global_exit) {
reset_menu();
current_menu = menu;
build_conf(menu);
if (!child_count)
break;
show_menu(prompt ? _(prompt) : _("Main Menu"),
_(menu_instructions),
current_index, &last_top_row);
keypad((menu_win(curses_menu)), TRUE);
while (!global_exit) {
if (match_state.in_search) {
mvprintw(0, 0,
"searching: %s", match_state.pattern);
clrtoeol();
}
refresh_all_windows(main_window);
res = wgetch(menu_win(curses_menu));
if (!res)
break;
if (do_match(res, &match_state, &current_index) == 0) {
if (current_index != -1)
center_item(current_index,
&last_top_row);
continue;
}
if (process_special_keys(&res,
(struct menu *) item_data()))
break;
switch (res) {
case KEY_DOWN:
menu_driver(curses_menu, REQ_DOWN_ITEM);
break;
case KEY_UP:
menu_driver(curses_menu, REQ_UP_ITEM);
break;
case KEY_NPAGE:
menu_driver(curses_menu, REQ_SCR_DPAGE);
break;
case KEY_PPAGE:
menu_driver(curses_menu, REQ_SCR_UPAGE);
break;
case KEY_HOME:
menu_driver(curses_menu, REQ_FIRST_ITEM);
break;
case KEY_END:
menu_driver(curses_menu, REQ_LAST_ITEM);
break;
case 'h':
case '?':
show_help((struct menu *) item_data());
break;
}
if (res == 10 || res == 27 ||
res == 32 || res == 'n' || res == 'y' ||
res == KEY_LEFT || res == KEY_RIGHT ||
res == 'm')
break;
refresh_all_windows(main_window);
}
refresh_all_windows(main_window);
/* if ESC or left*/
if (res == 27 || (menu != &rootmenu && res == KEY_LEFT))
break;
/* remember location in the menu */
last_top_row = top_row(curses_menu);
current_index = curses_item_index();
if (!item_tag())
continue;
submenu = (struct menu *) item_data();
if (!submenu || !menu_is_visible(submenu))
continue;
sym = submenu->sym;
switch (res) {
case ' ':
if (item_is_tag('t'))
sym_toggle_tristate_value(sym);
else if (item_is_tag('m'))
conf(submenu);
break;
case KEY_RIGHT:
case 10: /* ENTER WAS PRESSED */
switch (item_tag()) {
case 'm':
if (single_menu_mode)
submenu->data =
(void *) (long) !submenu->data;
else
conf(submenu);
break;
case 't':
if (sym_is_choice(sym) &&
sym_get_tristate_value(sym) == yes)
conf_choice(submenu);
else if (submenu->prompt &&
submenu->prompt->type == P_MENU)
conf(submenu);
else if (res == 10)
sym_toggle_tristate_value(sym);
break;
case 's':
conf_string(submenu);
break;
}
break;
case 'y':
if (item_is_tag('t')) {
if (sym_set_tristate_value(sym, yes))
break;
if (sym_set_tristate_value(sym, mod))
btn_dialog(main_window, setmod_text, 0);
}
break;
case 'n':
if (item_is_tag('t'))
sym_set_tristate_value(sym, no);
break;
case 'm':
if (item_is_tag('t'))
sym_set_tristate_value(sym, mod);
break;
}
}
}
static void conf_message_callback(const char *fmt, va_list ap)
{
char buf[1024];
vsnprintf(buf, sizeof(buf), fmt, ap);
btn_dialog(main_window, buf, 1, "<OK>");
}
static void show_help(struct menu *menu)
{
struct gstr help;
if (!menu)
return;
help = str_new();
menu_get_ext_help(menu, &help);
show_scroll_win(main_window, _(menu_get_prompt(menu)), str_get(&help));
str_free(&help);
}
static void conf_choice(struct menu *menu)
{
const char *prompt = _(menu_get_prompt(menu));
struct menu *child = 0;
struct symbol *active;
int selected_index = 0;
int last_top_row = 0;
int res, i = 0;
struct match_state match_state = {
.in_search = 0,
.match_direction = MATCH_TINKER_PATTERN_DOWN,
.pattern = "",
};
active = sym_get_choice_value(menu->sym);
/* this is mostly duplicated from the conf() function. */
while (!global_exit) {
reset_menu();
for (i = 0, child = menu->list; child; child = child->next) {
if (!show_all_items && !menu_is_visible(child))
continue;
if (child->sym == sym_get_choice_value(menu->sym))
item_make(child, ':', "<X> %s",
_(menu_get_prompt(child)));
else if (child->sym)
item_make(child, ':', " %s",
_(menu_get_prompt(child)));
else
item_make(child, ':', "*** %s ***",
_(menu_get_prompt(child)));
if (child->sym == active){
last_top_row = top_row(curses_menu);
selected_index = i;
}
i++;
}
show_menu(prompt ? _(prompt) : _("Choice Menu"),
_(radiolist_instructions),
selected_index,
&last_top_row);
while (!global_exit) {
if (match_state.in_search) {
mvprintw(0, 0, "searching: %s",
match_state.pattern);
clrtoeol();
}
refresh_all_windows(main_window);
res = wgetch(menu_win(curses_menu));
if (!res)
break;
if (do_match(res, &match_state, &selected_index) == 0) {
if (selected_index != -1)
center_item(selected_index,
&last_top_row);
continue;
}
if (process_special_keys(
&res,
(struct menu *) item_data()))
break;
switch (res) {
case KEY_DOWN:
menu_driver(curses_menu, REQ_DOWN_ITEM);
break;
case KEY_UP:
menu_driver(curses_menu, REQ_UP_ITEM);
break;
case KEY_NPAGE:
menu_driver(curses_menu, REQ_SCR_DPAGE);
break;
case KEY_PPAGE:
menu_driver(curses_menu, REQ_SCR_UPAGE);
break;
case KEY_HOME:
menu_driver(curses_menu, REQ_FIRST_ITEM);
break;
case KEY_END:
menu_driver(curses_menu, REQ_LAST_ITEM);
break;
case 'h':
case '?':
show_help((struct menu *) item_data());
break;
}
if (res == 10 || res == 27 || res == ' ' ||
res == KEY_LEFT){
break;
}
refresh_all_windows(main_window);
}
/* if ESC or left */
if (res == 27 || res == KEY_LEFT)
break;
child = item_data();
if (!child || !menu_is_visible(child) || !child->sym)
continue;
switch (res) {
case ' ':
case 10:
case KEY_RIGHT:
sym_set_tristate_value(child->sym, yes);
return;
case 'h':
case '?':
show_help(child);
active = child->sym;
break;
case KEY_EXIT:
return;
}
}
}
static void conf_string(struct menu *menu)
{
const char *prompt = menu_get_prompt(menu);
while (1) {
int res;
const char *heading;
switch (sym_get_type(menu->sym)) {
case S_INT:
heading = _(inputbox_instructions_int);
break;
case S_HEX:
heading = _(inputbox_instructions_hex);
break;
case S_STRING:
heading = _(inputbox_instructions_string);
break;
default:
heading = _("Internal nconf error!");
}
res = dialog_inputbox(main_window,
prompt ? _(prompt) : _("Main Menu"),
heading,
sym_get_string_value(menu->sym),
&dialog_input_result,
&dialog_input_result_len);
switch (res) {
case 0:
if (sym_set_string_value(menu->sym,
dialog_input_result))
return;
btn_dialog(main_window,
_("You have made an invalid entry."), 0);
break;
case 1:
show_help(menu);
break;
case KEY_EXIT:
return;
}
}
}
static void conf_load(void)
{
while (1) {
int res;
res = dialog_inputbox(main_window,
NULL, load_config_text,
filename,
&dialog_input_result,
&dialog_input_result_len);
switch (res) {
case 0:
if (!dialog_input_result[0])
return;
if (!conf_read(dialog_input_result)) {
set_config_filename(dialog_input_result);
sym_set_change_count(1);
return;
}
btn_dialog(main_window, _("File does not exist!"), 0);
break;
case 1:
show_scroll_win(main_window,
_("Load Alternate Configuration"),
load_config_help);
break;
case KEY_EXIT:
return;
}
}
}
static void conf_save(void)
{
while (1) {
int res;
res = dialog_inputbox(main_window,
NULL, save_config_text,
filename,
&dialog_input_result,
&dialog_input_result_len);
switch (res) {
case 0:
if (!dialog_input_result[0])
return;
res = conf_write(dialog_input_result);
if (!res) {
set_config_filename(dialog_input_result);
return;
}
btn_dialog(main_window, _("Can't create file! "
"Probably a nonexistent directory."),
1, "<OK>");
break;
case 1:
show_scroll_win(main_window,
_("Save Alternate Configuration"),
save_config_help);
break;
case KEY_EXIT:
return;
}
}
}
void setup_windows(void)
{
2024-09-09 08:57:42 +00:00
int lines, columns;
getmaxyx(stdscr, lines, columns);
2024-09-09 08:52:07 +00:00
if (main_window != NULL)
delwin(main_window);
/* set up the menu and menu window */
2024-09-09 08:57:42 +00:00
main_window = newwin(lines-2, columns-2, 2, 1);
2024-09-09 08:52:07 +00:00
keypad(main_window, TRUE);
2024-09-09 08:57:42 +00:00
mwin_max_lines = lines-7;
mwin_max_cols = columns-6;
2024-09-09 08:52:07 +00:00
/* panels order is from bottom to top */
new_panel(main_window);
}
int main(int ac, char **av)
{
2024-09-09 08:57:42 +00:00
int lines, columns;
2024-09-09 08:52:07 +00:00
char *mode;
setlocale(LC_ALL, "");
bindtextdomain(PACKAGE, LOCALEDIR);
textdomain(PACKAGE);
conf_parse(av[1]);
conf_read(NULL);
mode = getenv("NCONFIG_MODE");
if (mode) {
if (!strcasecmp(mode, "single_menu"))
single_menu_mode = 1;
}
/* Initialize curses */
initscr();
/* set color theme */
set_colors();
cbreak();
noecho();
keypad(stdscr, TRUE);
curs_set(0);
2024-09-09 08:57:42 +00:00
getmaxyx(stdscr, lines, columns);
if (columns < 75 || lines < 20) {
2024-09-09 08:52:07 +00:00
endwin();
printf("Your terminal should have at "
"least 20 lines and 75 columns\n");
return 1;
}
notimeout(stdscr, FALSE);
2024-09-09 08:57:42 +00:00
#if NCURSES_REENTRANT
set_escdelay(1);
#else
2024-09-09 08:52:07 +00:00
ESCDELAY = 1;
2024-09-09 08:57:42 +00:00
#endif
2024-09-09 08:52:07 +00:00
/* set btns menu */
curses_menu = new_menu(curses_menu_items);
menu_opts_off(curses_menu, O_SHOWDESC);
menu_opts_on(curses_menu, O_SHOWMATCH);
menu_opts_on(curses_menu, O_ONEVALUE);
menu_opts_on(curses_menu, O_NONCYCLIC);
menu_opts_on(curses_menu, O_IGNORECASE);
set_menu_mark(curses_menu, " ");
set_menu_fore(curses_menu, attributes[MAIN_MENU_FORE]);
set_menu_back(curses_menu, attributes[MAIN_MENU_BACK]);
set_menu_grey(curses_menu, attributes[MAIN_MENU_GREY]);
set_config_filename(conf_get_configname());
setup_windows();
/* check for KEY_FUNC(1) */
if (has_key(KEY_F(1)) == FALSE) {
show_scroll_win(main_window,
_("Instructions"),
_(menu_no_f_instructions));
}
conf_set_message_callback(conf_message_callback);
/* do the work */
while (!global_exit) {
conf(&rootmenu);
if (!global_exit && do_exit() == 0)
break;
}
/* ok, we are done */
unpost_menu(curses_menu);
free_menu(curses_menu);
delwin(main_window);
clear();
refresh();
endwin();
return 0;
}