From b4f53143b0e05fd3061cdf2e65e17a6a2904090b Mon Sep 17 00:00:00 2001 From: ridiculousfish Date: Fri, 24 Jul 2015 00:50:58 -0700 Subject: Migrate source files into src/ directory This change moves source files into a src/ directory, and puts object files into an obj/ directory. The Makefile and xcode project are updated accordingly. Fixes #1866 --- src/input.cpp | 1136 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 1136 insertions(+) create mode 100644 src/input.cpp (limited to 'src/input.cpp') diff --git a/src/input.cpp b/src/input.cpp new file mode 100644 index 00000000..4dfe80cd --- /dev/null +++ b/src/input.cpp @@ -0,0 +1,1136 @@ +/** \file input.c + + Functions for reading a character of input from stdin. + +*/ + +#include "config.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef HAVE_SYS_IOCTL_H +#include +#endif + +#include +#include + +#if HAVE_NCURSES_H +#include +#elif HAVE_NCURSES_CURSES_H +#include +#else +#include +#endif + +#if HAVE_TERM_H +#include +#elif HAVE_NCURSES_TERM_H +#include +#endif + +#include +#include +#include + + + +#include "fallback.h" +#include "util.h" + +#include "wutil.h" +#include "reader.h" +#include "proc.h" +#include "common.h" +#include "sanity.h" +#include "input_common.h" +#include "input.h" +#include "parser.h" +#include "env.h" +#include "expand.h" +#include "event.h" +#include "signal.h" + +#include "output.h" +#include "intern.h" +#include +#include + +#define DEFAULT_TERM L"ansi" +#define MAX_INPUT_FUNCTION_ARGS 20 + +/** + Struct representing a keybinding. Returned by input_get_mappings. + */ + +struct input_mapping_t +{ + wcstring seq; /**< Character sequence which generates this event */ + wcstring_list_t commands; /**< commands that should be evaluated by this mapping */ + + /* We wish to preserve the user-specified order. This is just an incrementing value. */ + unsigned int specification_order; + + wcstring mode; /**< mode in which this command should be evaluated */ + wcstring sets_mode; /** new mode that should be switched to after command evaluation */ + + input_mapping_t(const wcstring &s, const std::vector &c, + const wcstring &m = DEFAULT_BIND_MODE, + const wcstring &sm = DEFAULT_BIND_MODE) : seq(s), commands(c), mode(m), sets_mode(sm) + { + static unsigned int s_last_input_mapping_specification_order = 0; + specification_order = ++s_last_input_mapping_specification_order; + + } +}; + +/** + A struct representing the mapping from a terminfo key name to a terminfo character sequence + */ +struct terminfo_mapping_t +{ + const wchar_t *name; /**< Name of key */ + const char *seq; /**< Character sequence generated on keypress. Constant string. */ +}; + + +/** + Names of all the input functions supported +*/ +static const wchar_t * const name_arr[] = +{ + L"beginning-of-line", + L"end-of-line", + L"forward-char", + L"backward-char", + L"forward-word", + L"backward-word", + L"forward-bigword", + L"backward-bigword", + L"history-search-backward", + L"history-search-forward", + L"delete-char", + L"backward-delete-char", + L"kill-line", + L"yank", + L"yank-pop", + L"complete", + L"complete-and-search", + L"beginning-of-history", + L"end-of-history", + L"backward-kill-line", + L"kill-whole-line", + L"kill-word", + L"kill-bigword", + L"backward-kill-word", + L"backward-kill-path-component", + L"backward-kill-bigword", + L"history-token-search-backward", + L"history-token-search-forward", + L"self-insert", + L"transpose-chars", + L"transpose-words", + L"upcase-word", + L"downcase-word", + L"capitalize-word", + L"vi-arg-digit", + L"vi-delete-to", + L"execute", + L"beginning-of-buffer", + L"end-of-buffer", + L"repaint", + L"force-repaint", + L"up-line", + L"down-line", + L"suppress-autosuggestion", + L"accept-autosuggestion", + L"begin-selection", + L"end-selection", + L"kill-selection", + L"forward-jump", + L"backward-jump", + L"and", + L"cancel" +}; + +wcstring describe_char(wint_t c) +{ + wint_t initial_cmd_char = R_BEGINNING_OF_LINE; + size_t name_count = sizeof name_arr / sizeof *name_arr; + if (c >= initial_cmd_char && c < initial_cmd_char + name_count) + { + return format_string(L"%02x (%ls)", c, name_arr[c - initial_cmd_char]); + } + return format_string(L"%02x", c); +} + +/** + Description of each supported input function +*/ +/* +static const wchar_t *desc_arr[] = +{ + L"Move to beginning of line", + L"Move to end of line", + L"Move forward one character", + L"Move backward one character", + L"Move forward one word", + L"Move backward one word", + L"Search backward through list of previous commands", + L"Search forward through list of previous commands", + L"Delete one character forward", + L"Delete one character backward", + L"Move contents from cursor to end of line to killring", + L"Paste contents of killring", + L"Rotate to previous killring entry", + L"Guess the rest of the next input token", + L"Move to first item of history", + L"Move to last item of history", + L"Clear current line", + L"Move contents from beginning of line to cursor to killring", + L"Move entire line to killring", + L"Move next word to killring", + L"Move previous word to killring", + L"Write out key bindings", + L"Clear entire screen", + L"Quit the running program", + L"Search backward through list of previous commands for matching token", + L"Search forward through list of previous commands for matching token", + L"Insert the pressed key", + L"Do nothing", + L"End of file", + L"Repeat command" +} + ; +*/ + +/** + Internal code for each supported input function +*/ +static const wchar_t code_arr[] = +{ + R_BEGINNING_OF_LINE, + R_END_OF_LINE, + R_FORWARD_CHAR, + R_BACKWARD_CHAR, + R_FORWARD_WORD, + R_BACKWARD_WORD, + R_FORWARD_BIGWORD, + R_BACKWARD_BIGWORD, + R_HISTORY_SEARCH_BACKWARD, + R_HISTORY_SEARCH_FORWARD, + R_DELETE_CHAR, + R_BACKWARD_DELETE_CHAR, + R_KILL_LINE, + R_YANK, + R_YANK_POP, + R_COMPLETE, + R_COMPLETE_AND_SEARCH, + R_BEGINNING_OF_HISTORY, + R_END_OF_HISTORY, + R_BACKWARD_KILL_LINE, + R_KILL_WHOLE_LINE, + R_KILL_WORD, + R_KILL_BIGWORD, + R_BACKWARD_KILL_WORD, + R_BACKWARD_KILL_PATH_COMPONENT, + R_BACKWARD_KILL_BIGWORD, + R_HISTORY_TOKEN_SEARCH_BACKWARD, + R_HISTORY_TOKEN_SEARCH_FORWARD, + R_SELF_INSERT, + R_TRANSPOSE_CHARS, + R_TRANSPOSE_WORDS, + R_UPCASE_WORD, + R_DOWNCASE_WORD, + R_CAPITALIZE_WORD, + R_VI_ARG_DIGIT, + R_VI_DELETE_TO, + R_EXECUTE, + R_BEGINNING_OF_BUFFER, + R_END_OF_BUFFER, + R_REPAINT, + R_FORCE_REPAINT, + R_UP_LINE, + R_DOWN_LINE, + R_SUPPRESS_AUTOSUGGESTION, + R_ACCEPT_AUTOSUGGESTION, + R_BEGIN_SELECTION, + R_END_SELECTION, + R_KILL_SELECTION, + R_FORWARD_JUMP, + R_BACKWARD_JUMP, + R_AND, + R_CANCEL +}; + +/** Mappings for the current input mode */ +static std::vector mapping_list; + +/* Terminfo map list */ +static std::vector terminfo_mappings; + +#define TERMINFO_ADD(key) { (L ## #key) + 4, key } + + +/** + List of all terminfo mappings + */ +static std::vector mappings; + + +/** + Set to one when the input subsytem has been initialized. +*/ +static bool is_init = false; + +/** + Initialize terminfo. + */ +static void input_terminfo_init(); + +static wchar_t input_function_args[MAX_INPUT_FUNCTION_ARGS]; +static bool input_function_status; +static int input_function_args_index = 0; + +/** + Return the current bind mode +*/ +wcstring input_get_bind_mode() +{ + env_var_t mode = env_get_string(FISH_BIND_MODE_VAR); + return mode.missing() ? DEFAULT_BIND_MODE : mode; +} + +/** + Set the current bind mode +*/ +void input_set_bind_mode(const wcstring &bm) +{ + env_set(FISH_BIND_MODE_VAR, bm.c_str(), ENV_GLOBAL); +} + + +/** + Returns the arity of a given input function +*/ +int input_function_arity(int function) +{ + switch (function) + { + case R_FORWARD_JUMP: + case R_BACKWARD_JUMP: + return 1; + default: + return 0; + } +} + +/** + Sets the return status of the most recently executed input function +*/ +void input_function_set_status(bool status) +{ + input_function_status = status; +} + +/* Helper function to compare the lengths of sequences */ +static bool length_is_greater_than(const input_mapping_t &m1, const input_mapping_t &m2) +{ + return m1.seq.size() > m2.seq.size(); +} + +static bool specification_order_is_less_than(const input_mapping_t &m1, const input_mapping_t &m2) +{ + return m1.specification_order < m2.specification_order; +} + +/* Inserts an input mapping at the correct position. We sort them in descending order by length, so that we test longer sequences first. */ +static void input_mapping_insert_sorted(const input_mapping_t &new_mapping) +{ + std::vector::iterator loc = std::lower_bound(mapping_list.begin(), mapping_list.end(), new_mapping, length_is_greater_than); + mapping_list.insert(loc, new_mapping); +} + +/* Adds an input mapping */ +void input_mapping_add(const wchar_t *sequence, const wchar_t **commands, size_t commands_len, + const wchar_t *mode, const wchar_t *sets_mode) +{ + CHECK(sequence,); + CHECK(commands,); + CHECK(mode,); + CHECK(sets_mode,); + + // debug( 0, L"Add mapping from %ls to %ls in mode %ls", escape(sequence, ESCAPE_ALL).c_str(), escape(command, ESCAPE_ALL).c_str(), mode); + + // remove existing mappings with this sequence + const wcstring_list_t commands_vector(commands, commands + commands_len); + + for (size_t i=0; i(fish_term256); + } + else + { + env_var_t term = env_get_string(L"TERM"); + if (term.missing()) + { + support_term256 = false; + } + else if (term.find(L"256color") != wcstring::npos) + { + /* Explicitly supported */ + support_term256 = true; + } + else if (term.find(L"xterm") != wcstring::npos) + { + // assume that all xterms are 256, except for OS X SnowLeopard + env_var_t prog = env_get_string(L"TERM_PROGRAM"); + support_term256 = (prog != L"Apple_Terminal"); + } + else + { + // Don't know, default to false + support_term256 = false; + } + } + + env_var_t fish_term24bit = env_get_string(L"fish_term24bit"); + bool support_term24bit; + if (! fish_term24bit.missing_or_empty()) + { + support_term24bit = from_string(fish_term24bit); + } + else + { + /* We don't attempt to infer term24 bit support yet. */ + support_term24bit = false; + } + + color_support_t support = (support_term256 ? color_support_term256 : 0) | (support_term24bit ? color_support_term24bit : 0); + output_set_color_support(support); +} + +int input_init() +{ + if (is_init) + return 1; + + is_init = true; + + input_common_init(&interrupt_handler); + + const env_var_t term = env_get_string(L"TERM"); + int errret; + if (setupterm(0, STDOUT_FILENO, &errret) == ERR) + { + debug(0, _(L"Could not set up terminal")); + if (errret == 0) + { + debug(0, _(L"Check that your terminal type, '%ls', is supported on this system"), + term.c_str()); + debug(0, _(L"Attempting to use '%ls' instead"), DEFAULT_TERM); + env_set(L"TERM", DEFAULT_TERM, ENV_GLOBAL | ENV_EXPORT); + const std::string default_term = wcs2string(DEFAULT_TERM); + if (setupterm(const_cast(default_term.c_str()), STDOUT_FILENO, &errret) == ERR) + { + debug(0, _(L"Could not set up terminal")); + exit_without_destructors(1); + } + } + else + { + exit_without_destructors(1); + } + } + assert(! term.missing()); + output_set_term(term); + + input_terminfo_init(); + + update_fish_color_support(); + + /* If we have no keybindings, add a few simple defaults */ + if (mapping_list.empty()) + { + input_mapping_add(L"", L"self-insert"); + input_mapping_add(L"\n", L"execute"); + input_mapping_add(L"\t", L"complete"); + input_mapping_add(L"\x3", L"commandline \"\""); + input_mapping_add(L"\x4", L"exit"); + input_mapping_add(L"\x5", L"bind"); + } + + return 1; +} + +void input_destroy() +{ + if (!is_init) + return; + + + is_init = false; + + input_common_destroy(); + + if (del_curterm(cur_term) == ERR) + { + debug(0, _(L"Error while closing terminfo")); + } +} + +void input_function_push_arg(wchar_t arg) +{ + input_function_args[input_function_args_index++] = arg; +} + +wchar_t input_function_pop_arg() +{ + return input_function_args[--input_function_args_index]; +} + +void input_function_push_args(int code) +{ + int arity = input_function_arity(code); + for (int i = 0; i < arity; i++) + { + input_function_push_arg(input_common_readch(0)); + } +} + +/** + Perform the action of the specified binding + allow_commands controls whether fish commands should be executed, or should + be deferred until later. +*/ +static void input_mapping_execute(const input_mapping_t &m, bool allow_commands) +{ + /* has_functions: there are functions that need to be put on the input + queue + has_commands: there are shell commands that need to be evaluated */ + bool has_commands = false, has_functions = false; + + for (wcstring_list_t::const_iterator it = m.commands.begin(), end = m.commands.end(); it != end; it++) + { + if (input_function_get_code(*it) != -1) + has_functions = true; + else + has_commands = true; + } + + /* !has_functions && !has_commands: only set bind mode */ + if (!has_commands && !has_functions) + { + input_set_bind_mode(m.sets_mode); + return; + } + + if (!allow_commands) + { + /* We don't want to run commands yet. Put the characters back and return + R_NULL */ + for (wcstring::const_reverse_iterator it = m.seq.rbegin(), end = m.seq.rend(); it != end; ++it) + { + input_common_next_ch(*it); + } + input_common_next_ch(R_NULL); + return; /* skip the input_set_bind_mode */ + } + else if (has_functions && !has_commands) + { + /* functions are added at the head of the input queue */ + for (wcstring_list_t::const_reverse_iterator it = m.commands.rbegin(), end = m.commands.rend (); it != end; it++) + { + wchar_t code = input_function_get_code(*it); + input_function_push_args(code); + input_common_next_ch(code); + } + } + else if (has_commands && !has_functions) + { + /* Execute all commands. + + FIXME(snnw): if commands add stuff to input queue (e.g. commandline + -f execute), we won't see that until all other commands have also + been run. */ + int last_status = proc_get_last_status(); + for (wcstring_list_t::const_iterator it = m.commands.begin(), end = m.commands.end(); it != end; it++) + { + parser_t::principal_parser().eval(it->c_str(), io_chain_t(), TOP); + } + proc_set_last_status(last_status); + input_common_next_ch(R_NULL); + } + else + { + /* invalid binding, mixed commands and functions. We would need to + execute these one by one. */ + input_common_next_ch(R_NULL); + } + + input_set_bind_mode(m.sets_mode); +} + + + +/** + Try reading the specified function mapping +*/ +static bool input_mapping_is_match(const input_mapping_t &m) +{ + wint_t c = 0; + int j; + + //debug(0, L"trying mapping %ls\n", escape(m.seq.c_str(), ESCAPE_ALL).c_str()); + const wchar_t *str = m.seq.c_str(); + for (j=0; str[j] != L'\0'; j++) + { + bool timed = (j > 0 && iswcntrl(str[0])); + + c = input_common_readch(timed); + if (str[j] != c) + { + break; + } + } + + if (str[j] == L'\0') + { + //debug(0, L"matched mapping %ls (%ls)\n", escape(m.seq.c_str(), ESCAPE_ALL).c_str(), m.command.c_str()); + /* We matched the entire sequence */ + return true; + } + else + { + int k; + /* + Return the read characters + */ + input_common_next_ch(c); + for (k=j-1; k>=0; k--) + { + input_common_next_ch(m.seq[k]); + } + } + + return false; + +} + +void input_queue_ch(wint_t ch) +{ + input_common_queue_ch(ch); +} + +static void input_mapping_execute_matching_or_generic(bool allow_commands) +{ + const input_mapping_t *generic = NULL; + + const wcstring bind_mode = input_get_bind_mode(); + + for (int i = 0; i < mapping_list.size(); i++) + { + const input_mapping_t &m = mapping_list.at(i); + + //debug(0, L"trying mapping (%ls,%ls,%ls)\n", escape(m.seq.c_str(), ESCAPE_ALL).c_str(), + // m.mode.c_str(), m.sets_mode.c_str()); + + if (m.mode != bind_mode) + { + //debug(0, L"skipping mapping because mode %ls != %ls\n", m.mode.c_str(), input_get_bind_mode().c_str()); + continue; + } + + if (m.seq.length() == 0) + { + generic = &m; + } + else if (input_mapping_is_match(m)) + { + input_mapping_execute(m, allow_commands); + return; + } + } + + if (generic) + { + input_mapping_execute(*generic, allow_commands); + } + else + { + //debug(0, L"no generic found, ignoring..."); + wchar_t c = input_common_readch(0); + if (c == R_EOF) + input_common_next_ch(c); + } +} + +/* Helper function. Picks through the queue of incoming characters until we get to one that's not a readline function. */ +static wchar_t input_read_characters_only() +{ + std::vector functions_to_put_back; + wchar_t char_to_return; + for (;;) + { + char_to_return = input_common_readch(0); + bool is_readline_function = (char_to_return >= R_MIN && char_to_return <= R_MAX); + // R_NULL and R_EOF are more control characters than readline functions, so check specially for those + if (!is_readline_function || char_to_return == R_NULL || char_to_return == R_EOF) + { + break; + } + // This is a readline function; save it off for later re-enqueing and try again + functions_to_put_back.push_back(char_to_return); + } + // Restore any readline functions, in reverse to preserve their original order + size_t idx = functions_to_put_back.size(); + while (idx--) + { + input_common_next_ch(functions_to_put_back.at(idx)); + } + return char_to_return; +} + +wint_t input_readch(bool allow_commands) +{ + CHECK_BLOCK(R_NULL); + + /* + Clear the interrupted flag + */ + reader_reset_interrupted(); + + /* + Search for sequence in mapping tables + */ + + while (1) + { + wchar_t c = input_common_readch(0); + + if (c >= R_MIN && c <= R_MAX) + { + switch (c) + { + case R_EOF: /* If it's closed, then just return */ + { + return R_EOF; + } + case R_SELF_INSERT: + { + /* #1595: ensure we only insert characters, not readline functions. The common case is that this will be empty. */ + return input_read_characters_only(); + } + case R_AND: + { + if (input_function_status) + { + return input_readch(); + } + else + { + while ((c = input_common_readch(0)) && c >= R_MIN && c <= R_MAX); + input_common_next_ch(c); + return input_readch(); + } + } + default: + { + return c; + } + } + } + else + { + input_common_next_ch(c); + input_mapping_execute_matching_or_generic(allow_commands); + // regarding allow_commands, we're in a loop, but if a fish command + // is executed, R_NULL is unread, so the next pass through the loop + // we'll break out and return it. + } + } +} + +std::vector input_mapping_get_names() +{ + // Sort the mappings by the user specification order, so we can return them in the same order that the user specified them in + std::vector local_list = mapping_list; + std::sort(local_list.begin(), local_list.end(), specification_order_is_less_than); + std::vector result; + result.reserve(local_list.size()); + + for (size_t i=0; i::iterator it = mapping_list.begin(), end = mapping_list.end(); + it != end; + ++it) + { + if (sequence == it->seq && mode == it->mode) + { + mapping_list.erase(it); + result = true; + break; + } + } + return result; +} + +bool input_mapping_get(const wcstring &sequence, const wcstring &mode, wcstring_list_t *out_cmds, wcstring *out_sets_mode) +{ + bool result = false; + for (std::vector::const_iterator it = mapping_list.begin(), end = mapping_list.end(); + it != end; + ++it) + { + if (sequence == it->seq && mode == it->mode) + { + *out_cmds = it->commands; + *out_sets_mode = it->sets_mode; + result = true; + break; + } + } + return result; +} + +/** + Add all terminfo mappings + */ +static void input_terminfo_init() +{ + const terminfo_mapping_t tinfos[] = + { + TERMINFO_ADD(key_a1), + TERMINFO_ADD(key_a3), + TERMINFO_ADD(key_b2), + TERMINFO_ADD(key_backspace), + TERMINFO_ADD(key_beg), + TERMINFO_ADD(key_btab), + TERMINFO_ADD(key_c1), + TERMINFO_ADD(key_c3), + TERMINFO_ADD(key_cancel), + TERMINFO_ADD(key_catab), + TERMINFO_ADD(key_clear), + TERMINFO_ADD(key_close), + TERMINFO_ADD(key_command), + TERMINFO_ADD(key_copy), + TERMINFO_ADD(key_create), + TERMINFO_ADD(key_ctab), + TERMINFO_ADD(key_dc), + TERMINFO_ADD(key_dl), + TERMINFO_ADD(key_down), + TERMINFO_ADD(key_eic), + TERMINFO_ADD(key_end), + TERMINFO_ADD(key_enter), + TERMINFO_ADD(key_eol), + TERMINFO_ADD(key_eos), + TERMINFO_ADD(key_exit), + TERMINFO_ADD(key_f0), + TERMINFO_ADD(key_f1), + TERMINFO_ADD(key_f2), + TERMINFO_ADD(key_f3), + TERMINFO_ADD(key_f4), + TERMINFO_ADD(key_f5), + TERMINFO_ADD(key_f6), + TERMINFO_ADD(key_f7), + TERMINFO_ADD(key_f8), + TERMINFO_ADD(key_f9), + TERMINFO_ADD(key_f10), + TERMINFO_ADD(key_f11), + TERMINFO_ADD(key_f12), + TERMINFO_ADD(key_f13), + TERMINFO_ADD(key_f14), + TERMINFO_ADD(key_f15), + TERMINFO_ADD(key_f16), + TERMINFO_ADD(key_f17), + TERMINFO_ADD(key_f18), + TERMINFO_ADD(key_f19), + TERMINFO_ADD(key_f20), + /* + I know of no keyboard with more than 20 function keys, so + adding the rest here makes very little sense, since it will + take up a lot of room in any listings (like tab completions), + but with no benefit. + */ + /* + TERMINFO_ADD(key_f21), + TERMINFO_ADD(key_f22), + TERMINFO_ADD(key_f23), + TERMINFO_ADD(key_f24), + TERMINFO_ADD(key_f25), + TERMINFO_ADD(key_f26), + TERMINFO_ADD(key_f27), + TERMINFO_ADD(key_f28), + TERMINFO_ADD(key_f29), + TERMINFO_ADD(key_f30), + TERMINFO_ADD(key_f31), + TERMINFO_ADD(key_f32), + TERMINFO_ADD(key_f33), + TERMINFO_ADD(key_f34), + TERMINFO_ADD(key_f35), + TERMINFO_ADD(key_f36), + TERMINFO_ADD(key_f37), + TERMINFO_ADD(key_f38), + TERMINFO_ADD(key_f39), + TERMINFO_ADD(key_f40), + TERMINFO_ADD(key_f41), + TERMINFO_ADD(key_f42), + TERMINFO_ADD(key_f43), + TERMINFO_ADD(key_f44), + TERMINFO_ADD(key_f45), + TERMINFO_ADD(key_f46), + TERMINFO_ADD(key_f47), + TERMINFO_ADD(key_f48), + TERMINFO_ADD(key_f49), + TERMINFO_ADD(key_f50), + TERMINFO_ADD(key_f51), + TERMINFO_ADD(key_f52), + TERMINFO_ADD(key_f53), + TERMINFO_ADD(key_f54), + TERMINFO_ADD(key_f55), + TERMINFO_ADD(key_f56), + TERMINFO_ADD(key_f57), + TERMINFO_ADD(key_f58), + TERMINFO_ADD(key_f59), + TERMINFO_ADD(key_f60), + TERMINFO_ADD(key_f61), + TERMINFO_ADD(key_f62), + TERMINFO_ADD(key_f63),*/ + TERMINFO_ADD(key_find), + TERMINFO_ADD(key_help), + TERMINFO_ADD(key_home), + TERMINFO_ADD(key_ic), + TERMINFO_ADD(key_il), + TERMINFO_ADD(key_left), + TERMINFO_ADD(key_ll), + TERMINFO_ADD(key_mark), + TERMINFO_ADD(key_message), + TERMINFO_ADD(key_move), + TERMINFO_ADD(key_next), + TERMINFO_ADD(key_npage), + TERMINFO_ADD(key_open), + TERMINFO_ADD(key_options), + TERMINFO_ADD(key_ppage), + TERMINFO_ADD(key_previous), + TERMINFO_ADD(key_print), + TERMINFO_ADD(key_redo), + TERMINFO_ADD(key_reference), + TERMINFO_ADD(key_refresh), + TERMINFO_ADD(key_replace), + TERMINFO_ADD(key_restart), + TERMINFO_ADD(key_resume), + TERMINFO_ADD(key_right), + TERMINFO_ADD(key_save), + TERMINFO_ADD(key_sbeg), + TERMINFO_ADD(key_scancel), + TERMINFO_ADD(key_scommand), + TERMINFO_ADD(key_scopy), + TERMINFO_ADD(key_screate), + TERMINFO_ADD(key_sdc), + TERMINFO_ADD(key_sdl), + TERMINFO_ADD(key_select), + TERMINFO_ADD(key_send), + TERMINFO_ADD(key_seol), + TERMINFO_ADD(key_sexit), + TERMINFO_ADD(key_sf), + TERMINFO_ADD(key_sfind), + TERMINFO_ADD(key_shelp), + TERMINFO_ADD(key_shome), + TERMINFO_ADD(key_sic), + TERMINFO_ADD(key_sleft), + TERMINFO_ADD(key_smessage), + TERMINFO_ADD(key_smove), + TERMINFO_ADD(key_snext), + TERMINFO_ADD(key_soptions), + TERMINFO_ADD(key_sprevious), + TERMINFO_ADD(key_sprint), + TERMINFO_ADD(key_sr), + TERMINFO_ADD(key_sredo), + TERMINFO_ADD(key_sreplace), + TERMINFO_ADD(key_sright), + TERMINFO_ADD(key_srsume), + TERMINFO_ADD(key_ssave), + TERMINFO_ADD(key_ssuspend), + TERMINFO_ADD(key_stab), + TERMINFO_ADD(key_sundo), + TERMINFO_ADD(key_suspend), + TERMINFO_ADD(key_undo), + TERMINFO_ADD(key_up) + }; + const size_t count = sizeof tinfos / sizeof *tinfos; + terminfo_mappings.reserve(terminfo_mappings.size() + count); + terminfo_mappings.insert(terminfo_mappings.end(), tinfos, tinfos + count); +} + +bool input_terminfo_get_sequence(const wchar_t *name, wcstring *out_seq) +{ + ASSERT_IS_MAIN_THREAD(); + + const char *res = 0; + int err = ENOENT; + + CHECK(name, 0); + input_init(); + + for (size_t i=0; iassign(m.name); + return true; + } + } + + return false; +} + +wcstring_list_t input_terminfo_get_names(bool skip_null) +{ + wcstring_list_t result; + result.reserve(terminfo_mappings.size()); + + input_init(); + + for (size_t i=0; i