From 942fc224af403de0a73511a4e6a5dfe4bfa53b91 Mon Sep 17 00:00:00 2001 From: Maxime Coste Date: Mon, 8 Jun 2015 13:34:08 +0100 Subject: [PATCH] Specify if ShellManager should read output or not using a flag Some program (xclip), will fork a daemon keeping stdout/stderr open, so waiting for them to be closed make kakoune hang. Commands discarding stdout can then just not wait on it. --- src/command_manager.cc | 1 + src/commands.cc | 5 +++-- src/normal.cc | 16 +++++++++++----- src/shell_manager.cc | 20 ++++++++++++++++---- src/shell_manager.hh | 10 ++++++++++ 5 files changed, 41 insertions(+), 11 deletions(-) diff --git a/src/command_manager.cc b/src/command_manager.cc index 074b736b..e57ab196 100644 --- a/src/command_manager.cc +++ b/src/command_manager.cc @@ -271,6 +271,7 @@ String expand_token(const Token& token, const Context& context, { case Token::Type::ShellExpand: return ShellManager::instance().eval(content, context, {}, + ShellManager::Flags::ReadOutput, shell_params, env_vars).first; case Token::Type::RegisterExpand: return context.main_sel_register_value(content).str(); diff --git a/src/commands.cc b/src/commands.cc index 821a42e9..6d6a55fc 100644 --- a/src/commands.cc +++ b/src/commands.cc @@ -786,8 +786,9 @@ void define_command(const ParametersParser& parser, Context& context) { "token_to_complete", to_string(token_to_complete) }, { "pos_in_token", to_string(pos_in_token) } }; - String output = ShellManager::instance().eval(shell_cmd, context, - {}, params, vars).first; + String output = ShellManager::instance().eval(shell_cmd, context, {}, + ShellManager::Flags::ReadOutput, + params, vars).first; return Completions{ 0_byte, pos_in_token, split(output, '\n', 0) }; }; } diff --git a/src/normal.cc b/src/normal.cc index 20c2ce5a..c584baac 100644 --- a/src/normal.cc +++ b/src/normal.cc @@ -394,8 +394,11 @@ void pipe(Context& context, NormalParams) bool insert_eol = str.back() != '\n'; if (insert_eol) str += '\n'; - str = ShellManager::instance().eval(real_cmd, context, str, - {}, EnvVarMap{}).first; + str = ShellManager::instance().eval( + real_cmd, context, str, + ShellManager::Flags::ReadOutput, + {}, EnvVarMap{}).first; + if ((insert_eol or sel.max() == buffer.back_coord()) and str.back() == '\n') str = str.substr(0, str.length()-1).str(); @@ -409,7 +412,8 @@ void pipe(Context& context, NormalParams) for (auto& sel : selections) ShellManager::instance().eval(real_cmd, context, content(buffer, sel), - {}, EnvVarMap{}).first; + ShellManager::Flags::None, + {}, EnvVarMap{}); } }); } @@ -436,8 +440,9 @@ void insert_output(Context& context, NormalParams) if (real_cmd.empty()) return; - auto str = ShellManager::instance().eval(real_cmd, context, {}, {}, - EnvVarMap{}).first; + auto str = ShellManager::instance().eval(real_cmd, context, {}, + ShellManager::Flags::ReadOutput, + {}, EnvVarMap{}).first; ScopedEdition edition(context); context.selections().insert(str, mode); }); @@ -788,6 +793,7 @@ void keep_pipe(Context& context, NormalParams) for (auto& sel : context.selections()) { if (shell_manager.eval(cmdline, context, content(buffer, sel), + ShellManager::Flags::None, {}, EnvVarMap{}).second == 0) keep.push_back(sel); } diff --git a/src/shell_manager.cc b/src/shell_manager.cc index ce926ecb..ff483d89 100644 --- a/src/shell_manager.cc +++ b/src/shell_manager.cc @@ -24,7 +24,7 @@ ShellManager::ShellManager() std::pair ShellManager::eval( StringView cmdline, const Context& context, StringView input, - ConstArrayView params, const EnvVarMap& env_vars) + Flags flags, ConstArrayView params, const EnvVarMap& env_vars) { int write_pipe[2]; // child stdin int read_pipe[2]; // child stdout @@ -44,6 +44,8 @@ std::pair ShellManager::eval( close(write_pipe[1]); String child_stdout, child_stderr; + int status = 0; + bool terminated = false; { auto pipe_reader = [](String& output) { return [&output](FDWatcher& watcher, EventMode) { @@ -58,15 +60,25 @@ std::pair ShellManager::eval( FDWatcher stdout_watcher{read_pipe[0], pipe_reader(child_stdout)}; FDWatcher stderr_watcher{error_pipe[0], pipe_reader(child_stderr)}; - while (not stdout_watcher.closed() or not stderr_watcher.closed()) + if (not (flags & Flags::ReadOutput)) + { + stdout_watcher.close_fd(); + stderr_watcher.close_fd(); + } + + while (not stdout_watcher.closed() or + not stderr_watcher.closed() or + not terminated) + { EventManager::instance().handle_next_events(EventMode::Urgent); + if (not terminated) + terminated = waitpid(pid, &status, WNOHANG); + } } if (not child_stderr.empty()) write_to_debug_buffer(format("shell stderr: <<<\n{}>>>", child_stderr)); - int status = 0; - waitpid(pid, &status, 0); return { child_stdout, WIFEXITED(status) ? WEXITSTATUS(status) : - 1 }; } else try diff --git a/src/shell_manager.hh b/src/shell_manager.hh index fde507b6..36c74a3a 100644 --- a/src/shell_manager.hh +++ b/src/shell_manager.hh @@ -5,6 +5,7 @@ #include "regex.hh" #include "utils.hh" #include "env_vars.hh" +#include "flags.hh" namespace Kakoune { @@ -20,8 +21,15 @@ class ShellManager : public Singleton public: ShellManager(); + enum class Flags + { + None = 0, + ReadOutput = 1 + }; + std::pair eval(StringView cmdline, const Context& context, StringView input = {}, + Flags flags = Flags::ReadOutput, ConstArrayView params = {}, const EnvVarMap& env_vars = EnvVarMap{}); @@ -32,6 +40,8 @@ private: Vector> m_env_vars; }; +template<> struct WithBitOps : std::true_type {}; + } #endif // shell_manager_hh_INCLUDED