<commit_before>#include "shell_manager.hh"

#include "context.hh"
#include "debug.hh"
#include "event_manager.hh"
#include "file.hh"

#include <cstring>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>

namespace Kakoune
{

static const Regex env_var_regex(R"(\bkak_(\w+)\b)");

ShellManager::ShellManager()
{
    const char* path = getenv("PATH");
    String new_path;
    if (path)
        new_path = path + ":"_str;

    String kak_path = get_kak_binary_path();
    StringView kak_dir = kak_path.substr(0_byte, (int)kak_path.find_last_of('/'));
    new_path += kak_dir;
    setenv("PATH", new_path.c_str(), 1);
}

String ShellManager::eval(StringView cmdline, const Context& context,
                          memoryview<String> params,
                          const EnvVarMap& env_vars,
                          int* exit_status)
{
    return pipe("", cmdline, context, params, env_vars, exit_status);
}

String ShellManager::pipe(StringView input,
                          StringView cmdline, const Context& context,
                          memoryview<String> params,
                          const EnvVarMap& env_vars,
                          int* exit_status)
{
    int write_pipe[2]; // child stdin
    int read_pipe[2];  // child stdout
    int error_pipe[2]; // child stderr

    ::pipe(write_pipe);
    ::pipe(read_pipe);
    ::pipe(error_pipe);

    String output;
    if (pid_t pid = fork())
    {
        close(write_pipe[0]);
        close(read_pipe[1]);
        close(error_pipe[1]);

        write(write_pipe[1], input.data(), (int)input.length());
        close(write_pipe[1]);

        String error;
        {
            auto pipe_reader = [](String& output) {
                return [&output](FDWatcher& watcher, EventMode) {
                    char buffer[1024];
                    size_t size = read(watcher.fd(), buffer, 1024);
                    if (size <= 0)
                        watcher.close_fd();
                    output += String(buffer, buffer+size);
                };
            };

            FDWatcher stdout_watcher{read_pipe[0], pipe_reader(output)};
            FDWatcher stderr_watcher{error_pipe[0], pipe_reader(error)};

            while (not stdout_watcher.closed() and not stderr_watcher.closed())
                EventManager::instance().handle_next_events(EventMode::Urgent);
        }

        if (not error.empty())
            write_debug("shell stderr: <<<\n" + error + ">>>");

        waitpid(pid, exit_status, 0);
        if (exit_status)
        {
            if (WIFEXITED(*exit_status))
                *exit_status = WEXITSTATUS(*exit_status);
            else
                *exit_status = -1;
        }<commit_msg>Fix logic error in shell manager<commit_after>#include "shell_manager.hh"

#include "context.hh"
#include "debug.hh"
#include "event_manager.hh"
#include "file.hh"

#include <cstring>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>

namespace Kakoune
{

static const Regex env_var_regex(R"(\bkak_(\w+)\b)");

ShellManager::ShellManager()
{
    const char* path = getenv("PATH");
    String new_path;
    if (path)
        new_path = path + ":"_str;

    String kak_path = get_kak_binary_path();
    StringView kak_dir = kak_path.substr(0_byte, (int)kak_path.find_last_of('/'));
    new_path += kak_dir;
    setenv("PATH", new_path.c_str(), 1);
}

String ShellManager::eval(StringView cmdline, const Context& context,
                          memoryview<String> params,
                          const EnvVarMap& env_vars,
                          int* exit_status)
{
    return pipe("", cmdline, context, params, env_vars, exit_status);
}

String ShellManager::pipe(StringView input,
                          StringView cmdline, const Context& context,
                          memoryview<String> params,
                          const EnvVarMap& env_vars,
                          int* exit_status)
{
    int write_pipe[2]; // child stdin
    int read_pipe[2];  // child stdout
    int error_pipe[2]; // child stderr

    ::pipe(write_pipe);
    ::pipe(read_pipe);
    ::pipe(error_pipe);

    String output;
    if (pid_t pid = fork())
    {
        close(write_pipe[0]);
        close(read_pipe[1]);
        close(error_pipe[1]);

        write(write_pipe[1], input.data(), (int)input.length());
        close(write_pipe[1]);

        String error;
        {
            auto pipe_reader = [](String& output) {
                return [&output](FDWatcher& watcher, EventMode) {
                    char buffer[1024];
                    size_t size = read(watcher.fd(), buffer, 1024);
                    if (size <= 0)
                        watcher.close_fd();
                    output += String(buffer, buffer+size);
                };
            };

            FDWatcher stdout_watcher{read_pipe[0], pipe_reader(output)};
            FDWatcher stderr_watcher{error_pipe[0], pipe_reader(error)};

            while (not stdout_watcher.closed() or not stderr_watcher.closed())
                EventManager::instance().handle_next_events(EventMode::Urgent);
        }

        if (not error.empty())
            write_debug("shell stderr: <<<\n" + error + ">>>");

        waitpid(pid, exit_status, 0);
        if (exit_status)
        {
            if (WIFEXITED(*exit_status))
                *exit_status = WEXITSTATUS(*exit_status);
            else
                *exit_status = -1;
        }
{'fraction_non_alphanumeric': 0.12794117647058822, 'fraction_numerical': 0.008455882352941176, 'mean_word_length': 2.0396648044692736, 'pattern_counts': {'":': 2, '<': 23, '<?xml version=': 0, '>': 21, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '6052298', 'n_tokens_mistral': 1716, 'n_tokens_neox': 1652, 'n_words': 339}