From a6227f6c3ac2e99f7328e9395e57fe04ca3b9c5b Mon Sep 17 00:00:00 2001 From: Kurtis Rader Date: Mon, 12 Jun 2017 17:19:13 -0700 Subject: [PATCH] split builtin bind into its own module As part of putting the `bind` command code into its own module refactor how it parses its flags. --- Makefile.in | 112 ++++++------ src/builtin.cpp | 345 +------------------------------------ src/builtin_bind.cpp | 398 +++++++++++++++++++++++++++++++++++++++++++ src/builtin_bind.h | 11 ++ src/env.cpp | 1 + src/input.h | 2 +- 6 files changed, 471 insertions(+), 398 deletions(-) create mode 100644 src/builtin_bind.cpp create mode 100644 src/builtin_bind.h diff --git a/Makefile.in b/Makefile.in index 3e5a7dd04..5889ff5d8 100644 --- a/Makefile.in +++ b/Makefile.in @@ -98,7 +98,7 @@ HAVE_DOXYGEN=@HAVE_DOXYGEN@ # # All objects that the system needs to build fish, except fish.o # -FISH_OBJS := obj/autoload.o obj/builtin.o obj/builtin_commandline.o \ +FISH_OBJS := obj/autoload.o obj/builtin.o obj/builtin_bind.o obj/builtin_commandline.o \ obj/builtin_complete.o obj/builtin_jobs.o obj/builtin_printf.o \ obj/builtin_set.o obj/builtin_set_color.o obj/builtin_string.o \ obj/builtin_test.o obj/builtin_ulimit.o obj/color.o obj/common.o \ @@ -943,10 +943,10 @@ v = $(V$(V)) obj/autoload.o: config.h src/autoload.h src/common.h src/fallback.h obj/autoload.o: src/signal.h src/lru.h src/env.h src/exec.h src/wutil.h -obj/builtin.o: config.h src/builtin.h src/common.h src/fallback.h -obj/builtin.o: src/signal.h src/builtin_commandline.h src/builtin_complete.h -obj/builtin.o: src/builtin_jobs.h src/builtin_printf.h src/builtin_set.h -obj/builtin.o: src/builtin_set_color.h src/builtin_string.h +obj/builtin.o: config.h src/signal.h src/builtin.h src/common.h +obj/builtin.o: src/fallback.h src/builtin_bind.h src/builtin_commandline.h +obj/builtin.o: src/builtin_complete.h src/builtin_jobs.h src/builtin_printf.h +obj/builtin.o: src/builtin_set.h src/builtin_set_color.h src/builtin_string.h obj/builtin.o: src/builtin_test.h src/builtin_ulimit.h src/complete.h obj/builtin.o: src/env.h src/event.h src/exec.h src/expand.h obj/builtin.o: src/parse_constants.h src/function.h src/highlight.h @@ -954,12 +954,17 @@ obj/builtin.o: src/color.h src/history.h src/wutil.h src/input.h src/intern.h obj/builtin.o: src/io.h src/parse_util.h src/tokenizer.h src/parser.h obj/builtin.o: src/parse_tree.h src/proc.h src/parser_keywords.h src/path.h obj/builtin.o: src/reader.h src/wcstringutil.h src/wgetopt.h +obj/builtin_bind.o: config.h src/builtin.h src/common.h src/fallback.h +obj/builtin_bind.o: src/signal.h src/builtin_bind.h src/env.h src/input.h +obj/builtin_bind.o: src/io.h src/tokenizer.h src/wcstringutil.h src/wgetopt.h +obj/builtin_bind.o: src/wutil.h obj/builtin_commandline.o: config.h src/builtin.h src/common.h src/fallback.h -obj/builtin_commandline.o: src/signal.h src/input.h src/env.h src/io.h -obj/builtin_commandline.o: src/parse_util.h src/parse_constants.h -obj/builtin_commandline.o: src/tokenizer.h src/proc.h src/parse_tree.h -obj/builtin_commandline.o: src/reader.h src/complete.h src/highlight.h -obj/builtin_commandline.o: src/color.h src/util.h src/wgetopt.h src/wutil.h +obj/builtin_commandline.o: src/signal.h src/input.h src/builtin_bind.h +obj/builtin_commandline.o: src/env.h src/io.h src/parse_util.h +obj/builtin_commandline.o: src/parse_constants.h src/tokenizer.h src/proc.h +obj/builtin_commandline.o: src/parse_tree.h src/reader.h src/complete.h +obj/builtin_commandline.o: src/highlight.h src/color.h src/util.h +obj/builtin_commandline.o: src/wgetopt.h src/wutil.h obj/builtin_complete.o: config.h src/builtin.h src/common.h src/fallback.h obj/builtin_complete.o: src/signal.h src/complete.h src/env.h src/io.h obj/builtin_complete.o: src/parse_constants.h src/parse_util.h @@ -971,44 +976,44 @@ obj/builtin_jobs.o: src/signal.h src/io.h src/proc.h src/parse_tree.h obj/builtin_jobs.o: src/parse_constants.h src/tokenizer.h src/wgetopt.h obj/builtin_jobs.o: src/wutil.h obj/builtin_printf.o: config.h src/builtin.h src/common.h src/fallback.h -obj/builtin_printf.o: src/signal.h src/io.h src/proc.h src/parse_tree.h -obj/builtin_printf.o: src/parse_constants.h src/tokenizer.h src/wutil.h +obj/builtin_printf.o: src/signal.h src/io.h src/wutil.h obj/builtin_set.o: config.h src/builtin.h src/common.h src/fallback.h obj/builtin_set.o: src/signal.h src/env.h src/expand.h src/parse_constants.h obj/builtin_set.o: src/io.h src/proc.h src/parse_tree.h src/tokenizer.h obj/builtin_set.o: src/wgetopt.h src/wutil.h -obj/builtin_set_color.o: config.h src/builtin.h src/common.h src/env.h src/fallback.h -obj/builtin_set_color.o: src/signal.h src/color.h src/io.h src/output.h -obj/builtin_set_color.o: src/proc.h src/parse_tree.h src/parse_constants.h -obj/builtin_set_color.o: src/tokenizer.h src/wgetopt.h src/wutil.h +obj/builtin_set_color.o: config.h src/builtin.h src/common.h src/fallback.h +obj/builtin_set_color.o: src/signal.h src/color.h src/env.h src/io.h +obj/builtin_set_color.o: src/output.h src/wgetopt.h src/wutil.h obj/builtin_string.o: config.h src/builtin.h src/common.h src/fallback.h obj/builtin_string.o: src/signal.h src/io.h src/parse_util.h obj/builtin_string.o: src/parse_constants.h src/tokenizer.h src/wgetopt.h obj/builtin_string.o: src/wildcard.h src/complete.h src/expand.h src/wutil.h obj/builtin_test.o: config.h src/builtin.h src/common.h src/fallback.h -obj/builtin_test.o: src/signal.h src/io.h src/proc.h src/parse_tree.h -obj/builtin_test.o: src/parse_constants.h src/tokenizer.h src/wutil.h +obj/builtin_test.o: src/signal.h src/io.h src/wutil.h obj/builtin_ulimit.o: config.h src/builtin.h src/common.h src/fallback.h obj/builtin_ulimit.o: src/signal.h src/io.h src/util.h src/wgetopt.h obj/builtin_ulimit.o: src/wutil.h obj/color.o: config.h src/color.h src/common.h src/fallback.h src/signal.h -obj/common.o: config.h src/signal.h src/common.h src/fallback.h src/expand.h -obj/common.o: src/parse_constants.h src/wildcard.h src/complete.h src/wutil.h +obj/common.o: config.h src/common.h src/fallback.h src/signal.h src/env.h +obj/common.o: src/expand.h src/parse_constants.h src/proc.h src/io.h +obj/common.o: src/parse_tree.h src/tokenizer.h src/wildcard.h src/complete.h +obj/common.o: src/wutil.h obj/complete.o: config.h src/autoload.h src/common.h src/fallback.h obj/complete.o: src/signal.h src/lru.h src/builtin.h src/complete.h src/env.h obj/complete.o: src/exec.h src/expand.h src/parse_constants.h src/function.h obj/complete.o: src/event.h src/iothread.h src/parse_tree.h src/tokenizer.h obj/complete.o: src/parse_util.h src/parser.h src/proc.h src/io.h src/path.h obj/complete.o: src/util.h src/wildcard.h src/wutil.h -obj/env.o: config.h src/common.h src/fallback.h src/signal.h src/env.h -obj/env.o: src/env_universal_common.h src/wutil.h src/event.h src/expand.h -obj/env.o: src/parse_constants.h src/fish_version.h src/history.h src/input.h -obj/env.o: src/input_common.h src/path.h src/proc.h src/io.h src/parse_tree.h +obj/env.o: config.h src/builtin_bind.h src/common.h src/fallback.h +obj/env.o: src/signal.h src/env.h src/env_universal_common.h src/wutil.h +obj/env.o: src/event.h src/expand.h src/parse_constants.h src/fish_version.h +obj/env.o: src/history.h src/input.h src/input_common.h src/output.h +obj/env.o: src/color.h src/path.h src/proc.h src/io.h src/parse_tree.h obj/env.o: src/tokenizer.h src/reader.h src/complete.h src/highlight.h -obj/env.o: src/color.h src/sanity.h +obj/env.o: src/sanity.h src/screen.h obj/env_universal_common.o: config.h src/common.h src/fallback.h src/signal.h obj/env_universal_common.o: src/env.h src/env_universal_common.h src/wutil.h -obj/env_universal_common.o: src/utf8.h src/util.h +obj/env_universal_common.o: src/path.h src/utf8.h src/util.h obj/event.o: config.h src/signal.h src/common.h src/fallback.h src/event.h obj/event.o: src/input_common.h src/io.h src/parser.h src/expand.h obj/event.o: src/parse_constants.h src/parse_tree.h src/tokenizer.h @@ -1027,28 +1032,29 @@ obj/fallback.o: config.h src/signal.h src/common.h src/fallback.h src/util.h obj/fish.o: config.h src/builtin.h src/common.h src/fallback.h src/signal.h obj/fish.o: src/env.h src/event.h src/expand.h src/parse_constants.h obj/fish.o: src/fish_version.h src/function.h src/history.h src/wutil.h -obj/fish.o: src/input.h src/io.h src/parser.h src/parse_tree.h -obj/fish.o: src/tokenizer.h src/proc.h src/path.h src/reader.h src/complete.h -obj/fish.o: src/highlight.h src/color.h +obj/fish.o: src/io.h src/parser.h src/parse_tree.h src/tokenizer.h src/proc.h +obj/fish.o: src/path.h src/reader.h src/complete.h src/highlight.h +obj/fish.o: src/color.h obj/fish_indent.o: config.h src/color.h src/common.h src/fallback.h obj/fish_indent.o: src/signal.h src/env.h src/fish_version.h src/highlight.h -obj/fish_indent.o: src/input.h src/output.h src/parse_constants.h -obj/fish_indent.o: src/parse_tree.h src/tokenizer.h src/print_help.h -obj/fish_indent.o: src/wutil.h +obj/fish_indent.o: src/output.h src/parse_constants.h src/parse_tree.h +obj/fish_indent.o: src/tokenizer.h src/print_help.h src/wutil.h obj/fish_key_reader.o: config.h src/signal.h src/common.h src/fallback.h -obj/fish_key_reader.o: src/env.h src/input.h src/input_common.h -obj/fish_key_reader.o: src/print_help.h src/proc.h src/io.h src/parse_tree.h -obj/fish_key_reader.o: src/parse_constants.h src/tokenizer.h src/reader.h -obj/fish_key_reader.o: src/complete.h src/highlight.h src/color.h src/wutil.h +obj/fish_key_reader.o: src/env.h src/input.h src/builtin_bind.h +obj/fish_key_reader.o: src/input_common.h src/print_help.h src/proc.h +obj/fish_key_reader.o: src/io.h src/parse_tree.h src/parse_constants.h +obj/fish_key_reader.o: src/tokenizer.h src/reader.h src/complete.h +obj/fish_key_reader.o: src/highlight.h src/color.h src/wutil.h obj/fish_tests.o: config.h src/signal.h src/builtin.h src/common.h obj/fish_tests.o: src/fallback.h src/color.h src/complete.h src/env.h obj/fish_tests.o: src/env_universal_common.h src/wutil.h src/event.h obj/fish_tests.o: src/expand.h src/parse_constants.h src/function.h obj/fish_tests.o: src/highlight.h src/history.h src/input.h -obj/fish_tests.o: src/input_common.h src/io.h src/iothread.h src/lru.h -obj/fish_tests.o: src/pager.h src/reader.h src/screen.h src/parse_tree.h -obj/fish_tests.o: src/tokenizer.h src/parse_util.h src/parser.h src/proc.h -obj/fish_tests.o: src/path.h src/utf8.h src/wcstringutil.h src/wildcard.h +obj/fish_tests.o: src/builtin_bind.h src/input_common.h src/io.h +obj/fish_tests.o: src/iothread.h src/lru.h src/pager.h src/reader.h +obj/fish_tests.o: src/screen.h src/parse_tree.h src/tokenizer.h +obj/fish_tests.o: src/parse_util.h src/parser.h src/proc.h src/path.h +obj/fish_tests.o: src/utf8.h src/util.h src/wcstringutil.h src/wildcard.h obj/fish_version.o: src/fish_version.h obj/function.o: config.h src/autoload.h src/common.h src/fallback.h obj/function.o: src/signal.h src/lru.h src/env.h src/event.h src/function.h @@ -1064,13 +1070,13 @@ obj/highlight.o: src/wildcard.h src/complete.h obj/history.o: config.h src/common.h src/fallback.h src/signal.h src/env.h obj/history.o: src/history.h src/wutil.h src/io.h src/iothread.h src/lru.h obj/history.o: src/parse_constants.h src/parse_tree.h src/tokenizer.h -obj/history.o: src/path.h src/reader.h src/complete.h src/highlight.h -obj/history.o: src/color.h +obj/history.o: src/parse_util.h src/path.h src/reader.h src/complete.h +obj/history.o: src/highlight.h src/color.h obj/input.o: config.h src/common.h src/fallback.h src/signal.h src/env.h -obj/input.o: src/event.h src/input.h src/input_common.h src/io.h src/output.h -obj/input.o: src/color.h src/parser.h src/expand.h src/parse_constants.h +obj/input.o: src/event.h src/input.h src/builtin_bind.h src/input_common.h +obj/input.o: src/io.h src/parser.h src/expand.h src/parse_constants.h obj/input.o: src/parse_tree.h src/tokenizer.h src/proc.h src/reader.h -obj/input.o: src/complete.h src/highlight.h src/wutil.h +obj/input.o: src/complete.h src/highlight.h src/color.h src/wutil.h obj/input_common.o: config.h src/common.h src/fallback.h src/signal.h obj/input_common.o: src/env.h src/env_universal_common.h src/wutil.h obj/input_common.o: src/input_common.h src/iothread.h src/util.h @@ -1078,7 +1084,7 @@ obj/intern.o: config.h src/common.h src/fallback.h src/signal.h src/intern.h obj/io.o: config.h src/common.h src/fallback.h src/signal.h src/exec.h obj/io.o: src/io.h src/wutil.h obj/iothread.o: config.h src/signal.h src/common.h src/fallback.h -obj/iothread.o: src/iothread.h +obj/iothread.o: src/iothread.h src/wutil.h obj/kill.o: config.h src/common.h src/fallback.h src/signal.h obj/output.o: config.h src/color.h src/common.h src/fallback.h src/signal.h obj/output.o: src/env.h src/output.h src/wutil.h @@ -1128,18 +1134,18 @@ obj/proc.o: src/sanity.h src/util.h src/wutil.h obj/reader.o: config.h src/signal.h src/color.h src/common.h src/fallback.h obj/reader.o: src/complete.h src/env.h src/event.h src/exec.h src/expand.h obj/reader.o: src/parse_constants.h src/function.h src/highlight.h -obj/reader.o: src/history.h src/wutil.h src/input.h src/input_common.h -obj/reader.o: src/intern.h src/io.h src/iothread.h src/kill.h src/output.h -obj/reader.o: src/pager.h src/reader.h src/screen.h src/parse_tree.h -obj/reader.o: src/tokenizer.h src/parse_util.h src/parser.h src/proc.h -obj/reader.o: src/sanity.h src/util.h +obj/reader.o: src/history.h src/wutil.h src/input.h src/builtin_bind.h +obj/reader.o: src/input_common.h src/intern.h src/io.h src/iothread.h +obj/reader.o: src/kill.h src/output.h src/pager.h src/reader.h src/screen.h +obj/reader.o: src/parse_tree.h src/tokenizer.h src/parse_util.h src/parser.h +obj/reader.o: src/proc.h src/sanity.h src/util.h obj/sanity.o: config.h src/common.h src/fallback.h src/signal.h src/history.h obj/sanity.o: src/wutil.h src/kill.h src/proc.h src/io.h src/parse_tree.h obj/sanity.o: src/parse_constants.h src/tokenizer.h src/reader.h obj/sanity.o: src/complete.h src/highlight.h src/color.h src/env.h obj/sanity.o: src/sanity.h -obj/screen.o: config.h src/common.h src/fallback.h src/signal.h -obj/screen.o: src/highlight.h src/color.h src/env.h src/output.h src/pager.h +obj/screen.o: config.h src/common.h src/fallback.h src/signal.h src/env.h +obj/screen.o: src/highlight.h src/color.h src/output.h src/pager.h obj/screen.o: src/complete.h src/reader.h src/parse_constants.h src/screen.h obj/screen.o: src/util.h obj/signal.o: config.h src/signal.h src/common.h src/fallback.h src/event.h diff --git a/src/builtin.cpp b/src/builtin.cpp index 622802401..350614e7b 100644 --- a/src/builtin.cpp +++ b/src/builtin.cpp @@ -38,6 +38,7 @@ #include #include "builtin.h" +#include "builtin_bind.h" #include "builtin_commandline.h" #include "builtin_complete.h" #include "builtin_jobs.h" @@ -57,7 +58,6 @@ #include "function.h" #include "highlight.h" #include "history.h" -#include "input.h" #include "intern.h" #include "io.h" #include "parse_constants.h" @@ -244,349 +244,6 @@ void builtin_missing_argument(parser_t &parser, io_streams_t &streams, const wch builtin_print_help(parser, streams, cmd, streams.err); } -// Here follows the definition of all builtin commands. The function names are all of the form -// builtin_NAME where NAME is the name of the builtin. so the function name for the builtin 'fg' is -// 'builtin_fg'. -// -// A few builtins, including 'while', 'command' and 'builtin' are not defined here as they are -// handled directly by the parser. (They are not parsed as commands, instead they only alter the -// parser state) -// -// The builtins 'break' and 'continue' are so closely related that they share the same -// implementation, namely 'builtin_break_continue. -// -// Several other builtins, including jobs, ulimit and set are so big that they have been given their -// own module. These files are all named 'builtin_NAME.cpp', where NAME is the name of the builtin. - -/// List a single key binding. -/// Returns false if no binding with that sequence and mode exists. -static bool builtin_bind_list_one(const wcstring &seq, const wcstring &bind_mode, - io_streams_t &streams) { - std::vector ecmds; - wcstring sets_mode; - - if (!input_mapping_get(seq, bind_mode, &ecmds, &sets_mode)) { - return false; - } - - streams.out.append(L"bind"); - - // Append the mode flags if applicable. - if (bind_mode != DEFAULT_BIND_MODE) { - const wcstring emode = escape_string(bind_mode, ESCAPE_ALL); - streams.out.append(L" -M "); - streams.out.append(emode); - } - if (!sets_mode.empty() && sets_mode != bind_mode) { - const wcstring esets_mode = escape_string(sets_mode, ESCAPE_ALL); - streams.out.append(L" -m "); - streams.out.append(esets_mode); - } - - // Append the name. - wcstring tname; - if (input_terminfo_get_name(seq, &tname)) { - // Note that we show -k here because we have an input key name. - streams.out.append_format(L" -k %ls", tname.c_str()); - } else { - // No key name, so no -k; we show the escape sequence directly. - const wcstring eseq = escape_string(seq, ESCAPE_ALL); - streams.out.append_format(L" %ls", eseq.c_str()); - } - - // Now show the list of commands. - for (size_t i = 0; i < ecmds.size(); i++) { - const wcstring &ecmd = ecmds.at(i); - const wcstring escaped_ecmd = escape_string(ecmd, ESCAPE_ALL); - streams.out.push_back(' '); - streams.out.append(escaped_ecmd); - } - streams.out.push_back(L'\n'); - - return true; -} - -/// List all current key bindings. -static void builtin_bind_list(const wchar_t *bind_mode, io_streams_t &streams) { - const std::vector lst = input_mapping_get_names(); - - for (const input_mapping_name_t &binding : lst) { - if (bind_mode != NULL && bind_mode != binding.mode) { - continue; - } - - builtin_bind_list_one(binding.seq, binding.mode, streams); - } -} - -/// Print terminfo key binding names to string buffer used for standard output. -/// -/// \param all if set, all terminfo key binding names will be printed. If not set, only ones that -/// are defined for this terminal are printed. -static void builtin_bind_key_names(int all, io_streams_t &streams) { - const wcstring_list_t names = input_terminfo_get_names(!all); - for (size_t i = 0; i < names.size(); i++) { - const wcstring &name = names.at(i); - - streams.out.append_format(L"%ls\n", name.c_str()); - } -} - -/// Print all the special key binding functions to string buffer used for standard output. -static void builtin_bind_function_names(io_streams_t &streams) { - wcstring_list_t names = input_function_get_names(); - - for (size_t i = 0; i < names.size(); i++) { - const wchar_t *seq = names.at(i).c_str(); - streams.out.append_format(L"%ls\n", seq); - } -} - -/// Wraps input_terminfo_get_sequence(), appending the correct error messages as needed. -static bool get_terminfo_sequence(const wchar_t *seq, wcstring *out_seq, io_streams_t &streams) { - if (input_terminfo_get_sequence(seq, out_seq)) { - return true; - } - - wcstring eseq = escape_string(seq, 0); - if (errno == ENOENT) { - streams.err.append_format(_(L"%ls: No key with name '%ls' found\n"), L"bind", eseq.c_str()); - } else if (errno == EILSEQ) { - streams.err.append_format(_(L"%ls: Key with name '%ls' does not have any mapping\n"), - L"bind", eseq.c_str()); - } else { - streams.err.append_format(_(L"%ls: Unknown error trying to bind to key named '%ls'\n"), - L"bind", eseq.c_str()); - } - return false; -} - -/// Add specified key binding. -static bool builtin_bind_add(const wchar_t *seq, const wchar_t *const *cmds, size_t cmds_len, - const wchar_t *mode, const wchar_t *sets_mode, int terminfo, - io_streams_t &streams) { - if (terminfo) { - wcstring seq2; - if (get_terminfo_sequence(seq, &seq2, streams)) { - input_mapping_add(seq2.c_str(), cmds, cmds_len, mode, sets_mode); - } else { - return true; - } - - } else { - input_mapping_add(seq, cmds, cmds_len, mode, sets_mode); - } - - return false; -} - -/// Erase specified key bindings -/// -/// @param seq -/// an array of all key bindings to erase -/// @param all -/// if specified, _all_ key bindings will be erased -/// @param mode -/// if specified, only bindings from that mode will be erased. If not given -/// and @c all is @c false, @c DEFAULT_BIND_MODE will be used. -/// @param use_terminfo -/// Whether to look use terminfo -k name -/// -static bool builtin_bind_erase(wchar_t **seq, int all, const wchar_t *mode, int use_terminfo, - io_streams_t &streams) { - if (all) { - const std::vector lst = input_mapping_get_names(); - for (std::vector::const_iterator it = lst.begin(), end = lst.end(); - it != end; ++it) { - if (mode == NULL || mode == it->mode) { - input_mapping_erase(it->seq, it->mode); - } - } - - return false; - } - - bool res = false; - if (mode == NULL) mode = DEFAULT_BIND_MODE; //!OCLINT(parameter reassignment) - - while (*seq) { - if (use_terminfo) { - wcstring seq2; - if (get_terminfo_sequence(*seq++, &seq2, streams)) { - input_mapping_erase(seq2, mode); - } else { - res = true; - } - } else { - input_mapping_erase(*seq++, mode); - } - } - - return res; -} - -/// List all current bind modes. -static void builtin_bind_list_modes(io_streams_t &streams) { - const std::vector lst = input_mapping_get_names(); - // A set accomplishes two things for us here: - // - It removes duplicates (no twenty "default" entries). - // - It sorts it, which makes it nicer on the user. - std::set modes; - - for (const input_mapping_name_t &binding : lst) { - modes.insert(binding.mode); - } - for (const auto &mode : modes) { - streams.out.append_format(L"%ls\n", mode.c_str()); - } -} - -/// The bind builtin, used for setting character sequences. -static int builtin_bind(parser_t &parser, io_streams_t &streams, wchar_t **argv) { - enum { BIND_INSERT, BIND_ERASE, BIND_KEY_NAMES, BIND_FUNCTION_NAMES }; - wchar_t *cmd = argv[0]; - int argc = builtin_count_args(argv); - int mode = BIND_INSERT; - int res = STATUS_CMD_OK; - bool all = false; - bool use_terminfo = false; - const wchar_t *bind_mode = DEFAULT_BIND_MODE; - bool bind_mode_given = false; - const wchar_t *sets_bind_mode = L""; - - static const wchar_t *short_options = L"aehkKfM:Lm:"; - static const struct woption long_options[] = {{L"all", no_argument, NULL, 'a'}, - {L"erase", no_argument, NULL, 'e'}, - {L"function-names", no_argument, NULL, 'f'}, - {L"help", no_argument, NULL, 'h'}, - {L"key", no_argument, NULL, 'k'}, - {L"key-names", no_argument, NULL, 'K'}, - {L"mode", required_argument, NULL, 'M'}, - {L"list-modes", no_argument, NULL, 'L'}, - {L"sets-mode", required_argument, NULL, 'm'}, - {NULL, 0, NULL, 0}}; - - int opt; - wgetopter_t w; - while ((opt = w.wgetopt_long(argc, argv, short_options, long_options, NULL)) != -1) { - switch (opt) { - case L'a': { - all = true; - break; - } - case L'e': { - mode = BIND_ERASE; - break; - } - case L'h': { - builtin_print_help(parser, streams, argv[0], streams.out); - return STATUS_CMD_OK; - } - case L'k': { - use_terminfo = true; - break; - } - case L'K': { - mode = BIND_KEY_NAMES; - break; - } - case L'f': { - mode = BIND_FUNCTION_NAMES; - break; - } - case L'M': { - if (!valid_var_name(w.woptarg)) { - streams.err.append_format(BUILTIN_ERR_BIND_MODE, cmd, w.woptarg); - return STATUS_INVALID_ARGS; - } - bind_mode = w.woptarg; - bind_mode_given = true; - break; - } - case L'm': { - if (!valid_var_name(w.woptarg)) { - streams.err.append_format(BUILTIN_ERR_BIND_MODE, cmd, w.woptarg); - return STATUS_INVALID_ARGS; - } - sets_bind_mode = w.woptarg; - break; - } - case L'L': { - builtin_bind_list_modes(streams); - return STATUS_CMD_OK; - } - case L'?': { - builtin_unknown_option(parser, streams, argv[0], argv[w.woptind - 1]); - return STATUS_INVALID_ARGS; - } - default: { - DIE("unexpected retval from wgetopt_long"); - break; - } - } - } - - switch (mode) { - case BIND_ERASE: { - if (builtin_bind_erase(&argv[w.woptind], all, bind_mode_given ? bind_mode : NULL, - use_terminfo, streams)) { - res = STATUS_CMD_ERROR; - } - break; - } - case BIND_INSERT: { - int arg_count = argc - w.woptind; - if (arg_count == 0) { - builtin_bind_list(bind_mode_given ? bind_mode : NULL, streams); - } else if (arg_count == 1) { - wcstring seq; - if (use_terminfo) { - if (!get_terminfo_sequence(argv[w.woptind], &seq, streams)) { - res = STATUS_CMD_ERROR; - // get_terminfo_sequence already printed the error. - break; - } - } else { - seq = argv[w.woptind]; - } - if (!builtin_bind_list_one(seq, bind_mode, streams)) { - res = STATUS_CMD_ERROR; - wcstring eseq = escape_string(argv[w.woptind], 0); - if (use_terminfo) { - streams.err.append_format(_(L"%ls: No binding found for key '%ls'\n"), - argv[0], eseq.c_str()); - } else { - streams.err.append_format(_(L"%ls: No binding found for sequence '%ls'\n"), - argv[0], eseq.c_str()); - } - } - } else { - if (builtin_bind_add(argv[w.woptind], argv + (w.woptind + 1), - argc - (w.woptind + 1), bind_mode, sets_bind_mode, - use_terminfo, streams)) { - res = STATUS_CMD_ERROR; - } - } - break; - } - case BIND_KEY_NAMES: { - builtin_bind_key_names(all, streams); - break; - } - case BIND_FUNCTION_NAMES: { - builtin_bind_function_names(streams); - break; - } - default: { - res = STATUS_CMD_ERROR; - streams.err.append_format(_(L"%ls: Invalid state\n"), argv[0]); - break; - } - } - - return res; -} - /// The block builtin, used for temporarily blocking events. static int builtin_block(parser_t &parser, io_streams_t &streams, wchar_t **argv) { enum { diff --git a/src/builtin_bind.cpp b/src/builtin_bind.cpp new file mode 100644 index 000000000..60c0d4dea --- /dev/null +++ b/src/builtin_bind.cpp @@ -0,0 +1,398 @@ +// Implementation of the bind builtin. +#include "config.h" // IWYU pragma: keep + +#include +#include + +#include +#include +#include +#include + +#include "builtin.h" +#include "builtin_bind.h" +#include "common.h" +#include "fallback.h" // IWYU pragma: keep +#include "input.h" // IWYU pragma: keep +#include "io.h" // IWYU pragma: keep +#include "wgetopt.h" +#include "wutil.h" // IWYU pragma: keep + +enum { BIND_INSERT, BIND_ERASE, BIND_KEY_NAMES, BIND_FUNCTION_NAMES }; +struct bind_opts { + int mode = BIND_INSERT; + int res = STATUS_CMD_OK; + bool all = false; + bool use_terminfo = false; + const wchar_t *bind_mode = DEFAULT_BIND_MODE; + bool bind_mode_given = false; + bool list_modes = false; + bool print_help = false; + const wchar_t *sets_bind_mode = L""; +}; + +// Here follows the definition of all builtin commands. The function names are all of the form +// builtin_NAME where NAME is the name of the builtin. so the function name for the builtin 'fg' is +// 'builtin_fg'. +// +// A few builtins, including 'while', 'command' and 'builtin' are not defined here as they are +// handled directly by the parser. (They are not parsed as commands, instead they only alter the +// parser state) +// +// The builtins 'break' and 'continue' are so closely related that they share the same +// implementation, namely 'builtin_break_continue. +// +// Several other builtins, including jobs, ulimit and set are so big that they have been given their +// own module. These files are all named 'builtin_NAME.cpp', where NAME is the name of the builtin. + +/// List a single key binding. +/// Returns false if no binding with that sequence and mode exists. +static bool builtin_bind_list_one(const wcstring &seq, const wcstring &bind_mode, + io_streams_t &streams) { + std::vector ecmds; + wcstring sets_mode; + + if (!input_mapping_get(seq, bind_mode, &ecmds, &sets_mode)) { + return false; + } + + streams.out.append(L"bind"); + + // Append the mode flags if applicable. + if (bind_mode != DEFAULT_BIND_MODE) { + const wcstring emode = escape_string(bind_mode, ESCAPE_ALL); + streams.out.append(L" -M "); + streams.out.append(emode); + } + if (!sets_mode.empty() && sets_mode != bind_mode) { + const wcstring esets_mode = escape_string(sets_mode, ESCAPE_ALL); + streams.out.append(L" -m "); + streams.out.append(esets_mode); + } + + // Append the name. + wcstring tname; + if (input_terminfo_get_name(seq, &tname)) { + // Note that we show -k here because we have an input key name. + streams.out.append_format(L" -k %ls", tname.c_str()); + } else { + // No key name, so no -k; we show the escape sequence directly. + const wcstring eseq = escape_string(seq, ESCAPE_ALL); + streams.out.append_format(L" %ls", eseq.c_str()); + } + + // Now show the list of commands. + for (size_t i = 0; i < ecmds.size(); i++) { + const wcstring &ecmd = ecmds.at(i); + const wcstring escaped_ecmd = escape_string(ecmd, ESCAPE_ALL); + streams.out.push_back(' '); + streams.out.append(escaped_ecmd); + } + streams.out.push_back(L'\n'); + + return true; +} + +/// List all current key bindings. +static void builtin_bind_list(const wchar_t *bind_mode, io_streams_t &streams) { + const std::vector lst = input_mapping_get_names(); + + for (const input_mapping_name_t &binding : lst) { + if (bind_mode && bind_mode != binding.mode) { + continue; + } + + builtin_bind_list_one(binding.seq, binding.mode, streams); + } +} + +/// Print terminfo key binding names to string buffer used for standard output. +/// +/// \param all if set, all terminfo key binding names will be printed. If not set, only ones that +/// are defined for this terminal are printed. +static void builtin_bind_key_names(int all, io_streams_t &streams) { + const wcstring_list_t names = input_terminfo_get_names(!all); + for (size_t i = 0; i < names.size(); i++) { + const wcstring &name = names.at(i); + + streams.out.append_format(L"%ls\n", name.c_str()); + } +} + +/// Print all the special key binding functions to string buffer used for standard output. +static void builtin_bind_function_names(io_streams_t &streams) { + wcstring_list_t names = input_function_get_names(); + + for (size_t i = 0; i < names.size(); i++) { + const wchar_t *seq = names.at(i).c_str(); + streams.out.append_format(L"%ls\n", seq); + } +} + +/// Wraps input_terminfo_get_sequence(), appending the correct error messages as needed. +static bool get_terminfo_sequence(const wchar_t *seq, wcstring *out_seq, io_streams_t &streams) { + if (input_terminfo_get_sequence(seq, out_seq)) { + return true; + } + + wcstring eseq = escape_string(seq, 0); + if (errno == ENOENT) { + streams.err.append_format(_(L"%ls: No key with name '%ls' found\n"), L"bind", eseq.c_str()); + } else if (errno == EILSEQ) { + streams.err.append_format(_(L"%ls: Key with name '%ls' does not have any mapping\n"), + L"bind", eseq.c_str()); + } else { + streams.err.append_format(_(L"%ls: Unknown error trying to bind to key named '%ls'\n"), + L"bind", eseq.c_str()); + } + return false; +} + +/// Add specified key binding. +static bool builtin_bind_add(const wchar_t *seq, const wchar_t *const *cmds, size_t cmds_len, + const wchar_t *mode, const wchar_t *sets_mode, int terminfo, + io_streams_t &streams) { + if (terminfo) { + wcstring seq2; + if (get_terminfo_sequence(seq, &seq2, streams)) { + input_mapping_add(seq2.c_str(), cmds, cmds_len, mode, sets_mode); + } else { + return true; + } + + } else { + input_mapping_add(seq, cmds, cmds_len, mode, sets_mode); + } + + return false; +} + +/// Erase specified key bindings +/// +/// @param seq +/// an array of all key bindings to erase +/// @param all +/// if specified, _all_ key bindings will be erased +/// @param mode +/// if specified, only bindings from that mode will be erased. If not given +/// and @c all is @c false, @c DEFAULT_BIND_MODE will be used. +/// @param use_terminfo +/// Whether to look use terminfo -k name +/// +static bool builtin_bind_erase(wchar_t **seq, int all, const wchar_t *mode, int use_terminfo, + io_streams_t &streams) { + if (all) { + const std::vector lst = input_mapping_get_names(); + for (std::vector::const_iterator it = lst.begin(), end = lst.end(); + it != end; ++it) { + if (mode == NULL || mode == it->mode) { + input_mapping_erase(it->seq, it->mode); + } + } + + return false; + } + + bool res = false; + if (mode == NULL) mode = DEFAULT_BIND_MODE; //!OCLINT(parameter reassignment) + + while (*seq) { + if (use_terminfo) { + wcstring seq2; + if (get_terminfo_sequence(*seq++, &seq2, streams)) { + input_mapping_erase(seq2, mode); + } else { + res = true; + } + } else { + input_mapping_erase(*seq++, mode); + } + } + + return res; +} + +static bool builtin_bind_insert(struct bind_opts *opts, int optind, int argc, wchar_t **argv, + io_streams_t &streams) { + wchar_t *cmd = argv[0]; + int arg_count = argc - optind; + + if (arg_count == 0) { + builtin_bind_list(opts->bind_mode_given ? opts->bind_mode : NULL, streams); + } else if (arg_count == 1) { + wcstring seq; + if (opts->use_terminfo) { + if (!get_terminfo_sequence(argv[optind], &seq, streams)) { + // get_terminfo_sequence already printed the error. + return true; + } + } else { + seq = argv[optind]; + } + + if (!builtin_bind_list_one(seq, opts->bind_mode, streams)) { + wcstring eseq = escape_string(argv[optind], 0); + if (opts->use_terminfo) { + streams.err.append_format(_(L"%ls: No binding found for key '%ls'\n"), cmd, + eseq.c_str()); + } else { + streams.err.append_format(_(L"%ls: No binding found for sequence '%ls'\n"), cmd, + eseq.c_str()); + } + return true; + } + } else { + if (builtin_bind_add(argv[optind], argv + (optind + 1), argc - (optind + 1), + opts->bind_mode, opts->sets_bind_mode, opts->use_terminfo, streams)) { + return true; + } + } + + return false; +} + +/// List all current bind modes. +static void builtin_bind_list_modes(io_streams_t &streams) { + const std::vector lst = input_mapping_get_names(); + // A set accomplishes two things for us here: + // - It removes duplicates (no twenty "default" entries). + // - It sorts it, which makes it nicer on the user. + std::set modes; + + for (const input_mapping_name_t &binding : lst) { + modes.insert(binding.mode); + } + for (const auto &mode : modes) { + streams.out.append_format(L"%ls\n", mode.c_str()); + } +} + +static int parse_bind_opts(struct bind_opts *opts, int *optind, //!OCLINT(high ncss method) + int argc, wchar_t **argv, parser_t &parser, io_streams_t &streams) { + wchar_t *cmd = argv[0]; + static const wchar_t *short_options = L"aehkKfM:Lm:"; + static const struct woption long_options[] = {{L"all", no_argument, NULL, 'a'}, + {L"erase", no_argument, NULL, 'e'}, + {L"function-names", no_argument, NULL, 'f'}, + {L"help", no_argument, NULL, 'h'}, + {L"key", no_argument, NULL, 'k'}, + {L"key-names", no_argument, NULL, 'K'}, + {L"mode", required_argument, NULL, 'M'}, + {L"list-modes", no_argument, NULL, 'L'}, + {L"sets-mode", required_argument, NULL, 'm'}, + {NULL, 0, NULL, 0}}; + + int opt; + wgetopter_t w; + while ((opt = w.wgetopt_long(argc, argv, short_options, long_options, NULL)) != -1) { + switch (opt) { + case L'a': { + opts->all = true; + break; + } + case L'e': { + opts->mode = BIND_ERASE; + break; + } + case L'h': { + opts->print_help = true; + return STATUS_CMD_OK; + } + case L'k': { + opts->use_terminfo = true; + break; + } + case L'K': { + opts->mode = BIND_KEY_NAMES; + break; + } + case L'f': { + opts->mode = BIND_FUNCTION_NAMES; + break; + } + case L'M': { + if (!valid_var_name(w.woptarg)) { + streams.err.append_format(BUILTIN_ERR_BIND_MODE, cmd, w.woptarg); + return STATUS_INVALID_ARGS; + } + opts->bind_mode = w.woptarg; + opts->bind_mode_given = true; + break; + } + case L'm': { + if (!valid_var_name(w.woptarg)) { + streams.err.append_format(BUILTIN_ERR_BIND_MODE, cmd, w.woptarg); + return STATUS_INVALID_ARGS; + } + opts->sets_bind_mode = w.woptarg; + break; + } + case L'L': { + opts->list_modes = true; + return STATUS_CMD_OK; + } + case L'?': { + builtin_unknown_option(parser, streams, argv[0], argv[w.woptind - 1]); + return STATUS_INVALID_ARGS; + } + default: { + DIE("unexpected retval from wgetopt_long"); + break; + } + } + } + + *optind = w.woptind; + return STATUS_CMD_OK; +} + +/// The bind builtin, used for setting character sequences. +int builtin_bind(parser_t &parser, io_streams_t &streams, wchar_t **argv) { + wchar_t *cmd = argv[0]; + int argc = builtin_count_args(argv); + struct bind_opts opts; + + int optind; + int retval = parse_bind_opts(&opts, &optind, argc, argv, parser, streams); + if (retval != STATUS_CMD_OK) return retval; + + if (opts.list_modes) { + builtin_bind_list_modes(streams); + return STATUS_CMD_OK; + } + if (opts.print_help) { + builtin_print_help(parser, streams, cmd, streams.out); + return STATUS_CMD_OK; + } + + switch (opts.mode) { + case BIND_ERASE: { + const wchar_t *bind_mode = opts.bind_mode_given ? opts.bind_mode : NULL; + if (builtin_bind_erase(&argv[optind], opts.all, bind_mode, opts.use_terminfo, + streams)) { + return STATUS_CMD_ERROR; + } + break; + } + case BIND_INSERT: { + if (builtin_bind_insert(&opts, optind, argc, argv, streams)) { + return STATUS_CMD_ERROR; + } + break; + } + case BIND_KEY_NAMES: { + builtin_bind_key_names(opts.all, streams); + break; + } + case BIND_FUNCTION_NAMES: { + builtin_bind_function_names(streams); + break; + } + default: { + streams.err.append_format(_(L"%ls: Invalid state\n"), cmd); + return STATUS_CMD_ERROR; + } + } + + return STATUS_CMD_OK; +} diff --git a/src/builtin_bind.h b/src/builtin_bind.h new file mode 100644 index 000000000..6a5f6f41a --- /dev/null +++ b/src/builtin_bind.h @@ -0,0 +1,11 @@ +// Prototypes for functions for executing builtin_bind functions. +#ifndef FISH_BUILTIN_BIND_H +#define FISH_BUILTIN_BIND_H + +#define DEFAULT_BIND_MODE L"default" + +class parser_t; +struct io_streams_t; + +int builtin_bind(parser_t &parser, io_streams_t &streams, wchar_t **argv); +#endif diff --git a/src/env.cpp b/src/env.cpp index 13490acd4..81f8e71e2 100644 --- a/src/env.cpp +++ b/src/env.cpp @@ -36,6 +36,7 @@ #include #include +#include "builtin_bind.h" #include "common.h" #include "env.h" #include "env_universal_common.h" diff --git a/src/input.h b/src/input.h index 99a78d80d..862b28bac 100644 --- a/src/input.h +++ b/src/input.h @@ -7,10 +7,10 @@ #include +#include "builtin_bind.h" #include "common.h" #include "env.h" -#define DEFAULT_BIND_MODE L"default" #define FISH_BIND_MODE_VAR L"fish_bind_mode" wcstring describe_char(wint_t c);