diff options
| author | Maxime Coste <frrrwww@gmail.com> | 2012-02-13 21:38:07 +0000 |
|---|---|---|
| committer | Maxime Coste <frrrwww@gmail.com> | 2012-02-13 21:38:07 +0000 |
| commit | 2c8a6ca56a7cf331229acbc20472781bda97639e (patch) | |
| tree | a272a37c27e83cec7b8cd3c4b7d33f479eedbb91 /src | |
| parent | b49279503b47d10ad829984b2e0019eb19796a37 (diff) | |
Support `shell commands` expansion in CommandManager
Diffstat (limited to 'src')
| -rw-r--r-- | src/command_manager.cc | 99 | ||||
| -rw-r--r-- | src/command_manager.hh | 1 | ||||
| -rw-r--r-- | src/main.cc | 2 |
3 files changed, 90 insertions, 12 deletions
diff --git a/src/command_manager.cc b/src/command_manager.cc index b8d032ac..577e0273 100644 --- a/src/command_manager.cc +++ b/src/command_manager.cc @@ -2,8 +2,12 @@ #include "utils.hh" #include "assert.hh" +#include "context.hh" #include <algorithm> +#include <cstring> +#include <sys/types.h> +#include <sys/wait.h> namespace Kakoune { @@ -23,6 +27,11 @@ void CommandManager::register_commands(const memoryview<std::string>& command_na register_command(command_name, command, flags, completer); } +static bool is_blank(char c) +{ + return c == ' ' or c == '\t' or c == '\n'; +} + typedef std::vector<std::pair<size_t, size_t>> TokenList; static TokenList split(const std::string& line) { @@ -31,23 +40,31 @@ static TokenList split(const std::string& line) size_t pos = 0; while (pos < line.length()) { - while(line[pos] == ' ' and pos != line.length()) + while(is_blank(line[pos]) and pos != line.length()) ++pos; - char delimiter = ' '; - if (line[pos] == '"' or line[pos] == '\'') + size_t token_start = pos; + + if (line[pos] == '"' or line[pos] == '\'' or line[pos] == '`') { - delimiter = line[pos]; + char delimiter = line[pos]; ++pos; - } + token_start = delimiter == '`' ? pos - 1 : pos; - size_t token_start = pos; + while ((line[pos] != delimiter or line[pos-1] == '\\') and + pos != line.length()) + ++pos; - while (((line[pos] != delimiter and line[pos] != ';') or - line[pos-1] == '\\') and pos != line.length()) - ++pos; + if (delimiter == '`' and line[pos] == '`') + ++pos; + } + else + while (not is_blank(line[pos]) and pos != line.length() and + (line[pos] != ';' or line[pos-1] == '\\')) + ++pos; - result.push_back(std::make_pair(token_start, pos)); + if (token_start != pos) + result.push_back(std::make_pair(token_start, pos)); if (line[pos] == ';') result.push_back(std::make_pair(pos, pos+1)); @@ -80,6 +97,50 @@ void CommandManager::execute(const std::string& command_line, execute(params, context); } +static void shell_eval(std::vector<std::string>& params, + const std::string& cmdline, + const Context& context) +{ + int write_pipe[2]; + int read_pipe[2]; + + pipe(write_pipe); + pipe(read_pipe); + + if (pid_t pid = fork()) + { + close(write_pipe[0]); + close(read_pipe[1]); + close(write_pipe[1]); + + std::string output; + char buffer[1024]; + while (size_t size = read(read_pipe[0], buffer, 1024)) + output += std::string(buffer, buffer+size); + close(read_pipe[0]); + waitpid(pid, NULL, 0); + + TokenList tokens = split(output); + + for (auto it = tokens.begin(); it != tokens.end(); ++it) + { + params.push_back(output.substr(it->first, + it->second - it->first)); + } + } + else + { + close(write_pipe[1]); + close(read_pipe[0]); + + dup2(read_pipe[1], 1); + dup2(write_pipe[0], 0); + + setenv("kak_bufname", context.buffer().name().c_str(), 1); + execlp("sh", "sh", "-c", cmdline.c_str(), NULL); + } +} + void CommandManager::execute(const CommandParameters& params, const Context& context) { @@ -102,7 +163,23 @@ void CommandManager::execute(const CommandParameters& params, if (command_it->second.flags & IgnoreSemiColons) end = params.end(); - command_it->second.command(CommandParameters(begin + 1, end), context); + if (command_it->second.flags & DeferredShellEval) + command_it->second.command(CommandParameters(begin + 1, end), context); + else + { + std::vector<std::string> expanded_params; + for (auto param = begin+1; param != end; ++param) + { + if (param->front() == '`' and param->back() == '`') + shell_eval(expanded_params, + param->substr(1, param->length() - 2), + context); + else + expanded_params.push_back(*param); + } + command_it->second.command(expanded_params, context); + } + } if (end == params.end()) diff --git a/src/command_manager.hh b/src/command_manager.hh index aa9e6818..5e08d364 100644 --- a/src/command_manager.hh +++ b/src/command_manager.hh @@ -51,6 +51,7 @@ public: { None = 0, IgnoreSemiColons = 1, + DeferredShellEval = 2, }; void execute(const std::string& command_line, const Context& context); diff --git a/src/main.cc b/src/main.cc index c40124ab..40c9a764 100644 --- a/src/main.cc +++ b/src/main.cc @@ -1165,7 +1165,7 @@ int main(int argc, char* argv[]) [&](const std::string& prefix, size_t cursor_pos) { return main_context.window().complete_filterid(prefix, cursor_pos); } })); - command_manager.register_command("hook", add_hook, CommandManager::IgnoreSemiColons); + command_manager.register_command("hook", add_hook, CommandManager::IgnoreSemiColons | CommandManager::DeferredShellEval); command_manager.register_command("source", exec_commands_in_file, CommandManager::None, |
