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.
This commit is contained in:
Maxime Coste 2015-06-08 13:34:08 +01:00
parent 7acf3da3ca
commit 942fc224af
5 changed files with 41 additions and 11 deletions

View File

@ -271,6 +271,7 @@ String expand_token(const Token& token, const Context& context,
{ {
case Token::Type::ShellExpand: case Token::Type::ShellExpand:
return ShellManager::instance().eval(content, context, {}, return ShellManager::instance().eval(content, context, {},
ShellManager::Flags::ReadOutput,
shell_params, env_vars).first; shell_params, env_vars).first;
case Token::Type::RegisterExpand: case Token::Type::RegisterExpand:
return context.main_sel_register_value(content).str(); return context.main_sel_register_value(content).str();

View File

@ -786,8 +786,9 @@ void define_command(const ParametersParser& parser, Context& context)
{ "token_to_complete", to_string(token_to_complete) }, { "token_to_complete", to_string(token_to_complete) },
{ "pos_in_token", to_string(pos_in_token) } { "pos_in_token", to_string(pos_in_token) }
}; };
String output = ShellManager::instance().eval(shell_cmd, context, String output = ShellManager::instance().eval(shell_cmd, context, {},
{}, params, vars).first; ShellManager::Flags::ReadOutput,
params, vars).first;
return Completions{ 0_byte, pos_in_token, split(output, '\n', 0) }; return Completions{ 0_byte, pos_in_token, split(output, '\n', 0) };
}; };
} }

View File

@ -394,8 +394,11 @@ void pipe(Context& context, NormalParams)
bool insert_eol = str.back() != '\n'; bool insert_eol = str.back() != '\n';
if (insert_eol) if (insert_eol)
str += '\n'; str += '\n';
str = ShellManager::instance().eval(real_cmd, context, str, str = ShellManager::instance().eval(
{}, EnvVarMap{}).first; real_cmd, context, str,
ShellManager::Flags::ReadOutput,
{}, EnvVarMap{}).first;
if ((insert_eol or sel.max() == buffer.back_coord()) and if ((insert_eol or sel.max() == buffer.back_coord()) and
str.back() == '\n') str.back() == '\n')
str = str.substr(0, str.length()-1).str(); str = str.substr(0, str.length()-1).str();
@ -409,7 +412,8 @@ void pipe(Context& context, NormalParams)
for (auto& sel : selections) for (auto& sel : selections)
ShellManager::instance().eval(real_cmd, context, ShellManager::instance().eval(real_cmd, context,
content(buffer, sel), content(buffer, sel),
{}, EnvVarMap{}).first; ShellManager::Flags::None,
{}, EnvVarMap{});
} }
}); });
} }
@ -436,8 +440,9 @@ void insert_output(Context& context, NormalParams)
if (real_cmd.empty()) if (real_cmd.empty())
return; return;
auto str = ShellManager::instance().eval(real_cmd, context, {}, {}, auto str = ShellManager::instance().eval(real_cmd, context, {},
EnvVarMap{}).first; ShellManager::Flags::ReadOutput,
{}, EnvVarMap{}).first;
ScopedEdition edition(context); ScopedEdition edition(context);
context.selections().insert(str, mode); context.selections().insert(str, mode);
}); });
@ -788,6 +793,7 @@ void keep_pipe(Context& context, NormalParams)
for (auto& sel : context.selections()) for (auto& sel : context.selections())
{ {
if (shell_manager.eval(cmdline, context, content(buffer, sel), if (shell_manager.eval(cmdline, context, content(buffer, sel),
ShellManager::Flags::None,
{}, EnvVarMap{}).second == 0) {}, EnvVarMap{}).second == 0)
keep.push_back(sel); keep.push_back(sel);
} }

View File

@ -24,7 +24,7 @@ ShellManager::ShellManager()
std::pair<String, int> ShellManager::eval( std::pair<String, int> ShellManager::eval(
StringView cmdline, const Context& context, StringView input, StringView cmdline, const Context& context, StringView input,
ConstArrayView<String> params, const EnvVarMap& env_vars) Flags flags, ConstArrayView<String> params, const EnvVarMap& env_vars)
{ {
int write_pipe[2]; // child stdin int write_pipe[2]; // child stdin
int read_pipe[2]; // child stdout int read_pipe[2]; // child stdout
@ -44,6 +44,8 @@ std::pair<String, int> ShellManager::eval(
close(write_pipe[1]); close(write_pipe[1]);
String child_stdout, child_stderr; String child_stdout, child_stderr;
int status = 0;
bool terminated = false;
{ {
auto pipe_reader = [](String& output) { auto pipe_reader = [](String& output) {
return [&output](FDWatcher& watcher, EventMode) { return [&output](FDWatcher& watcher, EventMode) {
@ -58,15 +60,25 @@ std::pair<String, int> ShellManager::eval(
FDWatcher stdout_watcher{read_pipe[0], pipe_reader(child_stdout)}; FDWatcher stdout_watcher{read_pipe[0], pipe_reader(child_stdout)};
FDWatcher stderr_watcher{error_pipe[0], pipe_reader(child_stderr)}; 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); EventManager::instance().handle_next_events(EventMode::Urgent);
if (not terminated)
terminated = waitpid(pid, &status, WNOHANG);
}
} }
if (not child_stderr.empty()) if (not child_stderr.empty())
write_to_debug_buffer(format("shell stderr: <<<\n{}>>>", child_stderr)); 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 }; return { child_stdout, WIFEXITED(status) ? WEXITSTATUS(status) : - 1 };
} }
else try else try

View File

@ -5,6 +5,7 @@
#include "regex.hh" #include "regex.hh"
#include "utils.hh" #include "utils.hh"
#include "env_vars.hh" #include "env_vars.hh"
#include "flags.hh"
namespace Kakoune namespace Kakoune
{ {
@ -20,8 +21,15 @@ class ShellManager : public Singleton<ShellManager>
public: public:
ShellManager(); ShellManager();
enum class Flags
{
None = 0,
ReadOutput = 1
};
std::pair<String, int> eval(StringView cmdline, const Context& context, std::pair<String, int> eval(StringView cmdline, const Context& context,
StringView input = {}, StringView input = {},
Flags flags = Flags::ReadOutput,
ConstArrayView<String> params = {}, ConstArrayView<String> params = {},
const EnvVarMap& env_vars = EnvVarMap{}); const EnvVarMap& env_vars = EnvVarMap{});
@ -32,6 +40,8 @@ private:
Vector<std::pair<Regex, EnvVarRetriever>> m_env_vars; Vector<std::pair<Regex, EnvVarRetriever>> m_env_vars;
}; };
template<> struct WithBitOps<ShellManager::Flags> : std::true_type {};
} }
#endif // shell_manager_hh_INCLUDED #endif // shell_manager_hh_INCLUDED