<commit_before>#include "shell_manager.hh"

#include "buffer_utils.hh"
#include "clock.hh"
#include "context.hh"
#include "display_buffer.hh"
#include "event_manager.hh"
#include "face_registry.hh"
#include "file.hh"
#include "regex.hh"

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

extern char **environ;

namespace Kakoune
{

ShellManager::ShellManager()
{
    // Get a guaranteed to be POSIX shell binary
    {
        auto size = confstr(_CS_PATH, 0, 0);
        String path; path.resize(size, 0);
        confstr(_CS_PATH, path.data(), size);
        for (auto dir : StringView{path} | split<StringView>(':'))
        {
            String candidate = format("{}/sh", dir);
            struct stat st;
            if (stat(candidate.c_str(), &st))
                continue;

            bool executable = (st.st_mode & S_IXUSR)
                            | (st.st_mode & S_IXGRP)
                            | (st.st_mode & S_IXOTH);
            if (S_ISREG(st.st_mode) and executable)
            {
                m_shell = std::move(candidate);
                break;
            }
        }
        if (m_shell.empty())
            throw runtime_error{format("unable to find a posix shell in {}", path)};
    }

    // Add Kakoune binary location to the path to guarantee that %sh{ ... }
    // have access to the kak command regardless of if the user installed it
    {
        const char* path = getenv("PATH");
        auto new_path = format("{}:{}", path, split_path(get_kak_binary_path()).first);<commit_msg>Fix getting path confstr, the returned size includes the zero terminator<commit_after>#include "shell_manager.hh"

#include "buffer_utils.hh"
#include "clock.hh"
#include "context.hh"
#include "display_buffer.hh"
#include "event_manager.hh"
#include "face_registry.hh"
#include "file.hh"
#include "regex.hh"

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

extern char **environ;

namespace Kakoune
{

ShellManager::ShellManager()
{
    // Get a guaranteed to be POSIX shell binary
    {
        auto size = confstr(_CS_PATH, 0, 0);
        String path; path.resize(size-1, 0);
        confstr(_CS_PATH, path.data(), size);
        for (auto dir : StringView{path} | split<StringView>(':'))
        {
            String candidate = format("{}/sh", dir);
            struct stat st;
            if (stat(candidate.c_str(), &st))
                continue;

            bool executable = (st.st_mode & S_IXUSR)
                            | (st.st_mode & S_IXGRP)
                            | (st.st_mode & S_IXOTH);
            if (S_ISREG(st.st_mode) and executable)
            {
                m_shell = std::move(candidate);
                break;
            }
        }
        if (m_shell.empty())
            throw runtime_error{format("unable to find a posix shell in {}", path)};
    }

    // Add Kakoune binary location to the path to guarantee that %sh{ ... }
    // have access to the kak command regardless of if the user installed it
    {
        const char* path = getenv("PATH");
        auto new_path = format("{}:{}", path, split_path(get_kak_binary_path()).first);
{'fraction_non_alphanumeric': 0.13085271317829458, 'fraction_numerical': 0.0021705426356589145, 'mean_word_length': 2.2324649298597192, 'pattern_counts': {'":': 0, '<': 15, '<?xml version=': 0, '>': 15, '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': '2808521', 'n_tokens_mistral': 1038, 'n_tokens_neox': 1011, 'n_words': 267}