diff options
| author | Maxime Coste <mawww@kakoune.org> | 2021-06-15 21:38:17 +1000 |
|---|---|---|
| committer | Maxime Coste <mawww@kakoune.org> | 2021-07-07 19:25:06 +1000 |
| commit | 3d7fb8ddbce37a078cbc2235dd5faed968c69024 (patch) | |
| tree | 92b5d41a03d2f5eb26b55124747ceabb3db3a4e9 /src/shell_manager.cc | |
| parent | 6215bff76414a36575421f646c8504e5a93f54f6 (diff) | |
Add support for $kak_command_fifo and $kak_response_fifo
Those fifos are accessible during %sh{...} blocks, the command fifo
executes commands written to it once the write end side is closed
(multiple open/write/close sequences are supported), the response
fifo is a simple helper fifo available to write response back to
the shell process
An example use of this feature is to request some list options
content from without being limited by the environment size:
```
%sh{
echo "echo -to-file $kak_response_fifo -quoting shell -- %opt{some_list}" > $kak_command_fifo
eval "set -- $(cat $kak_response_fifo)"
}
```
Diffstat (limited to 'src/shell_manager.cc')
| -rw-r--r-- | src/shell_manager.cc | 63 |
1 files changed, 55 insertions, 8 deletions
diff --git a/src/shell_manager.cc b/src/shell_manager.cc index 8e1cefc5..298412b7 100644 --- a/src/shell_manager.cc +++ b/src/shell_manager.cc @@ -4,6 +4,7 @@ #include "client.hh" #include "clock.hh" #include "context.hh" +#include "command_manager.hh" #include "display_buffer.hh" #include "event_manager.hh" #include "face_registry.hh" @@ -163,18 +164,19 @@ Vector<String> generate_env(StringView cmdline, const Context& context, GetValue return env; } -FDWatcher make_pipe_reader(Pipe& pipe, String& contents) +template<typename OnClose> +FDWatcher make_reader(int fd, String& contents, OnClose&& on_close) { - return {pipe.read_fd(), FdEvents::Read, EventMode::Urgent, - [&contents, &pipe](FDWatcher& watcher, FdEvents, EventMode) { + return {fd, FdEvents::Read, EventMode::Urgent, + [fd, &contents, on_close](FDWatcher& watcher, FdEvents, EventMode) { char buffer[1024]; - while (fd_readable(pipe.read_fd())) + while (fd_readable(fd)) { - size_t size = ::read(pipe.read_fd(), buffer, sizeof(buffer)); + size_t size = ::read(fd, buffer, sizeof(buffer)); if (size <= 0) { - pipe.close_read_fd(); watcher.disable(); + on_close(); return; } contents += StringView{buffer, buffer+size}; @@ -206,6 +208,41 @@ FDWatcher make_pipe_writer(Pipe& pipe, StringView contents) }}; } +struct CommandFifos +{ + String base_dir; + String command; + FDWatcher command_watcher; + + CommandFifos(Context& context, const ShellContext& shell_context) + : base_dir(format("{}/kak-fifo.XXXXXX", tmpdir())), + command_watcher([&] { + mkdtemp(base_dir.data()), + mkfifo(command_fifo_path().c_str(), 0600); + mkfifo(response_fifo_path().c_str(), 0600); + int fd = open(command_fifo_path().c_str(), O_RDONLY | O_NONBLOCK); + return make_reader(fd, command, [&, fd] { + close(fd); + CommandManager::instance().execute(command, context, shell_context); + command.clear(); + command_watcher.reset_fd(open(command_fifo_path().c_str(), O_RDONLY | O_NONBLOCK)); + }); + }()) + { + } + + ~CommandFifos() + { + command_watcher.close_fd(); + unlink(command_fifo_path().c_str()); + unlink(response_fifo_path().c_str()); + rmdir(base_dir.c_str()); + } + + String command_fifo_path() const { return format("{}/command-fifo", base_dir); } + String response_fifo_path() const { return format("{}/response-fifo", base_dir); } +}; + } std::pair<String, int> ShellManager::eval( @@ -219,7 +256,17 @@ std::pair<String, int> ShellManager::eval( auto start_time = profile ? Clock::now() : Clock::time_point{}; + Optional<CommandFifos> command_fifos; + auto kak_env = generate_env(cmdline, context, [&](StringView name, Quoting quoting) { + if (name == "command_fifo" or name == "response_fifo") + { + if (not command_fifos) + command_fifos.emplace(const_cast<Context&>(context), shell_context); + return name == "command_fifo" ? + command_fifos->command_fifo_path() : command_fifos->response_fifo_path(); + } + if (auto it = shell_context.env_vars.find(name); it != shell_context.env_vars.end()) return it->value; return join(get_val(name, context) | transform(quoter(quoting)), ' ', false); @@ -259,8 +306,8 @@ std::pair<String, int> ShellManager::eval( auto wait_time = Clock::now(); String stdout_contents, stderr_contents; - auto stdout_reader = make_pipe_reader(child_stdout, stdout_contents); - auto stderr_reader = make_pipe_reader(child_stderr, stderr_contents); + auto stdout_reader = make_reader(child_stdout.read_fd(), stdout_contents, [&]{ child_stdout.close_read_fd(); }); + auto stderr_reader = make_reader(child_stderr.read_fd(), stderr_contents, [&]{ child_stderr.close_read_fd(); }); auto stdin_writer = make_pipe_writer(child_stdin, input); // block SIGCHLD to make sure we wont receive it before |
