mirror of
https://github.com/vdemydiuk/mtapi.git
synced 2026-08-12 18:28:07 +00:00
Added thirdparty: boost library
This commit is contained in:
+180
@@ -0,0 +1,180 @@
|
||||
// Copyright (c) 2022 Klemens D. Morgenstern
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
#ifndef BOOST_PROCESS_V2_DETAIL_CONFIG_HPP
|
||||
#define BOOST_PROCESS_V2_DETAIL_CONFIG_HPP
|
||||
|
||||
#if defined(BOOST_PROCESS_V2_STANDALONE)
|
||||
|
||||
#define BOOST_PROCESS_V2_ASIO_NAMESPACE asio
|
||||
#define BOOST_PROCESS_V2_COMPLETION_TOKEN_FOR(Sig) ASIO_COMPLETION_TOKEN_FOR(Sig)
|
||||
#define BOOST_PROCESS_V2_DEFAULT_COMPLETION_TOKEN_TYPE(Executor) ASIO_DEFAULT_COMPLETION_TOKEN_TYPE(Executor)
|
||||
#define BOOST_PROCESS_V2_INITFN_AUTO_RESULT_TYPE(Token, Signature) ASIO_INITFN_AUTO_RESULT_TYPE(Token, Signature)
|
||||
#define BOOST_PROCESS_V2_DEFAULT_COMPLETION_TOKEN(Executor) ASIO_DEFAULT_COMPLETION_TOKEN(Executor)
|
||||
#define BOOST_PROCESS_V2_INITFN_DEDUCED_RESULT_TYPE(x,y,z) ASIO_INITFN_DEDUCED_RESULT_TYPE(x,y,z)
|
||||
|
||||
#include <asio/detail/config.hpp>
|
||||
#include <system_error>
|
||||
#include <filesystem>
|
||||
#include <string_view>
|
||||
#include <iomanip>
|
||||
#include <optional>
|
||||
|
||||
#if defined(ASIO_WINDOWS)
|
||||
#define BOOST_PROCESS_V2_WINDOWS 1
|
||||
|
||||
// Windows: suppress definition of "min" and "max" macros.
|
||||
#if !defined(NOMINMAX)
|
||||
# define NOMINMAX 1
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if defined(ASIO_HAS_UNISTD_H)
|
||||
#define BOOST_PROCESS_V2_POSIX 1
|
||||
#endif
|
||||
|
||||
#define BOOST_PROCESS_V2_BEGIN_NAMESPACE namespace process_v2 {
|
||||
#define BOOST_PROCESS_V2_END_NAMESPACE }
|
||||
#define BOOST_PROCESS_V2_NAMESPACE process_v2
|
||||
|
||||
#else
|
||||
|
||||
#define BOOST_PROCESS_V2_ASIO_NAMESPACE boost::asio
|
||||
#define BOOST_PROCESS_V2_COMPLETION_TOKEN_FOR(Sig) BOOST_ASIO_COMPLETION_TOKEN_FOR(Sig)
|
||||
#define BOOST_PROCESS_V2_DEFAULT_COMPLETION_TOKEN_TYPE(Executor) BOOST_ASIO_DEFAULT_COMPLETION_TOKEN_TYPE(Executor)
|
||||
#define BOOST_PROCESS_V2_INITFN_AUTO_RESULT_TYPE(Token, Signature) BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(Token, Signature)
|
||||
#define BOOST_PROCESS_V2_DEFAULT_COMPLETION_TOKEN(Executor) BOOST_ASIO_DEFAULT_COMPLETION_TOKEN(Executor)
|
||||
#define BOOST_PROCESS_V2_INITFN_DEDUCED_RESULT_TYPE(x,y,z) BOOST_ASIO_INITFN_DEDUCED_RESULT_TYPE(x,y,z)
|
||||
|
||||
#include <boost/config.hpp>
|
||||
#include <boost/io/quoted.hpp>
|
||||
#include <boost/system/error_code.hpp>
|
||||
#include <boost/system/system_category.hpp>
|
||||
#include <boost/system/system_error.hpp>
|
||||
#include <boost/optional.hpp>
|
||||
|
||||
#if defined(BOOST_WINDOWS_API)
|
||||
#define BOOST_PROCESS_V2_WINDOWS 1
|
||||
|
||||
// Windows: suppress definition of "min" and "max" macros.
|
||||
#if !defined(NOMINMAX)
|
||||
# define NOMINMAX 1
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
#if defined(BOOST_POSIX_API)
|
||||
#define BOOST_PROCESS_V2_POSIX 1
|
||||
#endif
|
||||
|
||||
#if !defined(BOOST_PROCESS_V2_WINDOWS) && !defined(BOOST_POSIX_API)
|
||||
#error Unsupported operating system
|
||||
#endif
|
||||
|
||||
#if defined(BOOST_PROCESS_USE_STD_FS)
|
||||
#include <filesystem>
|
||||
#else
|
||||
#include <boost/filesystem/path.hpp>
|
||||
#include <boost/filesystem/operations.hpp>
|
||||
#endif
|
||||
|
||||
#define BOOST_PROCESS_V2_BEGIN_NAMESPACE namespace boost { namespace process { namespace v2 {
|
||||
#define BOOST_PROCESS_V2_END_NAMESPACE } } }
|
||||
#define BOOST_PROCESS_V2_NAMESPACE boost::process::v2
|
||||
|
||||
#endif
|
||||
|
||||
BOOST_PROCESS_V2_BEGIN_NAMESPACE
|
||||
|
||||
#if defined(BOOST_PROCESS_STANDALONE)
|
||||
|
||||
using std::error_code ;
|
||||
using std::error_category ;
|
||||
using std::system_category ;
|
||||
using std::system_error ;
|
||||
namespace filesystem = std::filesystem;
|
||||
using std::quoted;
|
||||
using std::optional;
|
||||
|
||||
#define BOOST_PROCESS_V2_RETURN_EC(ev) \
|
||||
return ::BOOST_PROCESS_V2_NAMESPACE::error_code(ev, ::BOOST_PROCESS_V2_NAMESPACE::system_category()); \
|
||||
|
||||
#define BOOST_PROCESS_V2_ASSIGN_EC(ec, ...) ec.assign(__VA_ARGS__);
|
||||
#define BOOST_PROCESS_V2_ASSIGN_LAST_ERROR(ec) \
|
||||
ec.assign(::BOOST_PROCESS_V2_NAMESPACE::detail::get_last_error()); \
|
||||
|
||||
|
||||
#else
|
||||
|
||||
using boost::system::error_code ;
|
||||
using boost::system::error_category ;
|
||||
using boost::system::system_category ;
|
||||
using boost::system::system_error ;
|
||||
using boost::io::quoted;
|
||||
using boost::optional;
|
||||
|
||||
#ifdef BOOST_PROCESS_USE_STD_FS
|
||||
namespace filesystem = std::filesystem;
|
||||
#else
|
||||
namespace filesystem = boost::filesystem;
|
||||
#endif
|
||||
|
||||
#define BOOST_PROCESS_V2_RETURN_EC(ev) \
|
||||
{ \
|
||||
static constexpr auto loc##__LINE__((BOOST_CURRENT_LOCATION)); \
|
||||
return ::BOOST_PROCESS_V2_NAMESPACE::error_code(ev, ::BOOST_PROCESS_V2_NAMESPACE::system_category(), &loc##__LINE__); \
|
||||
}
|
||||
|
||||
#define BOOST_PROCESS_V2_ASSIGN_EC(ec, ...) \
|
||||
{ \
|
||||
static constexpr auto loc##__LINE__((BOOST_CURRENT_LOCATION)); \
|
||||
ec.assign(__VA_ARGS__, &loc##__LINE__); \
|
||||
}
|
||||
|
||||
#define BOOST_PROCESS_V2_ASSIGN_LAST_ERROR(ec) \
|
||||
{ \
|
||||
static constexpr auto loc##__LINE__((BOOST_CURRENT_LOCATION)); \
|
||||
ec.assign(::BOOST_PROCESS_V2_NAMESPACE::detail::get_last_error(), &loc##__LINE__); \
|
||||
}
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
BOOST_PROCESS_V2_END_NAMESPACE
|
||||
|
||||
#ifndef BOOST_PROCESS_V2_HEADER_ONLY
|
||||
# ifndef BOOST_PROCESS_V2_SEPARATE_COMPILATION
|
||||
# define BOOST_PROCESS_V2_HEADER_ONLY 1
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#if BOOST_PROCESS_V2_DOXYGEN
|
||||
# define BOOST_PROCESS_V2_DECL
|
||||
#elif defined(BOOST_PROCESS_V2_HEADER_ONLY)
|
||||
# define BOOST_PROCESS_V2_DECL inline
|
||||
#else
|
||||
# define BOOST_PROCESS_V2_DECL
|
||||
#endif
|
||||
|
||||
#if defined(BOOST_PROCESS_V2_POSIX)
|
||||
|
||||
#if defined(__linux__) && !defined(BOOST_PROCESS_V2_DISABLE_PIDFD_OPEN)
|
||||
|
||||
#include <sys/syscall.h>
|
||||
|
||||
#if defined(SYS_pidfd_open)
|
||||
#define BOOST_PROCESS_V2_PIDFD_OPEN 1
|
||||
#define BOOST_PROCESS_V2_HAS_PROCESS_HANDLE 1
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if defined(__FreeBSD__) && defined(BOOST_PROCESS_V2_ENABLE_PDFORK)
|
||||
#define BOOST_PROCESS_V2_PDFORK 1
|
||||
#define BOOST_PROCESS_V2_HAS_PROCESS_HANDLE 1
|
||||
#endif
|
||||
#else
|
||||
#define BOOST_PROCESS_V2_HAS_PROCESS_HANDLE 1
|
||||
#endif
|
||||
|
||||
#endif //BOOST_PROCESS_V2_DETAIL_CONFIG_HPP
|
||||
+80
@@ -0,0 +1,80 @@
|
||||
//
|
||||
// process/environment/detail/environment_posix.hpp
|
||||
// ~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
//
|
||||
// Copyright (c) 2021 Klemens D. Morgenstern (klemens dot morgenstern at gmx dot net)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#ifndef BOOST_PROCESS_V2_DETAIL_ENVIRONMENT_POSIX_HPP
|
||||
#define BOOST_PROCESS_V2_DETAIL_ENVIRONMENT_POSIX_HPP
|
||||
|
||||
#include <boost/process/v2/detail/config.hpp>
|
||||
#include <boost/process/v2/cstring_ref.hpp>
|
||||
|
||||
#if defined(__APPLE__) || defined(__MACH__) || defined(__FreeBSD__) || defined(__DragonFly__) || defined(__NetBSD__) || defined(__OpenBSD__) || defined(__sun)
|
||||
extern "C" { extern char **environ; }
|
||||
#endif
|
||||
|
||||
BOOST_PROCESS_V2_BEGIN_NAMESPACE
|
||||
|
||||
namespace environment
|
||||
{
|
||||
|
||||
using char_type = char;
|
||||
|
||||
template<typename Char>
|
||||
using key_char_traits = std::char_traits<Char>;
|
||||
|
||||
template<typename Char>
|
||||
using value_char_traits = std::char_traits<Char>;
|
||||
|
||||
|
||||
constexpr char_type equality_sign = '=';
|
||||
constexpr char_type delimiter = ':';
|
||||
|
||||
namespace detail
|
||||
{
|
||||
|
||||
BOOST_PROCESS_V2_DECL
|
||||
basic_cstring_ref<char_type, value_char_traits<char>>
|
||||
get(basic_cstring_ref<char_type, key_char_traits<char_type>> key, error_code & ec);
|
||||
|
||||
BOOST_PROCESS_V2_DECL
|
||||
void set(basic_cstring_ref<char_type, key_char_traits<char_type>> key,
|
||||
basic_cstring_ref<char_type, value_char_traits<char_type>> value,
|
||||
error_code & ec);
|
||||
|
||||
BOOST_PROCESS_V2_DECL
|
||||
void unset(basic_cstring_ref<char_type, key_char_traits<char_type>> key,
|
||||
error_code & ec);
|
||||
}
|
||||
|
||||
|
||||
using native_handle_type = const char * const *;
|
||||
using native_iterator = native_handle_type;
|
||||
|
||||
namespace detail
|
||||
{
|
||||
|
||||
BOOST_PROCESS_V2_DECL native_handle_type load_native_handle();
|
||||
struct native_handle_deleter
|
||||
{
|
||||
void operator()(native_handle_type) const {}
|
||||
};
|
||||
|
||||
BOOST_PROCESS_V2_DECL native_iterator next(native_handle_type nh);
|
||||
BOOST_PROCESS_V2_DECL native_iterator find_end(native_handle_type nh);
|
||||
inline const char_type * dereference(native_iterator iterator) {return *iterator;}
|
||||
|
||||
BOOST_PROCESS_V2_DECL bool is_executable(const filesystem::path & pth, error_code & ec);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
BOOST_PROCESS_V2_END_NAMESPACE
|
||||
|
||||
#endif
|
||||
+212
@@ -0,0 +1,212 @@
|
||||
//
|
||||
// process/environment/detail/environment_win.hpp
|
||||
// ~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
//
|
||||
// Copyright (c) 2021 Klemens D. Morgenstern (klemens dot morgenstern at gmx dot net)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#ifndef BOOST_PROCESS_V2_DETAIL_ENVIRONMENT_WIN_HPP
|
||||
#define BOOST_PROCESS_V2_DETAIL_ENVIRONMENT_WIN_HPP
|
||||
|
||||
#include <boost/process/v2/detail/config.hpp>
|
||||
#include <boost/process/v2/cstring_ref.hpp>
|
||||
|
||||
#include <cwctype>
|
||||
|
||||
BOOST_PROCESS_V2_BEGIN_NAMESPACE
|
||||
|
||||
namespace environment
|
||||
{
|
||||
|
||||
using char_type = wchar_t;
|
||||
|
||||
template<typename Char>
|
||||
struct key_char_traits
|
||||
{
|
||||
typedef Char char_type;
|
||||
typedef typename std::char_traits<char_type>::int_type int_type;
|
||||
typedef typename std::char_traits<char_type>::off_type off_type;
|
||||
typedef typename std::char_traits<char_type>::pos_type pos_type;
|
||||
typedef typename std::char_traits<char_type>::state_type state_type;
|
||||
|
||||
BOOST_CONSTEXPR static char to_lower(char c) {return std::tolower(to_int_type(c));}
|
||||
BOOST_CONSTEXPR static wchar_t to_lower(wchar_t c) {return std::towlower(to_int_type(c));}
|
||||
|
||||
BOOST_CONSTEXPR static int_type to_lower(int_type i, char ) {return std::tolower(i);}
|
||||
BOOST_CONSTEXPR static int_type to_lower(int_type i, wchar_t) {return std::towlower(i);}
|
||||
|
||||
|
||||
BOOST_CONSTEXPR static
|
||||
void assign(char_type& c1, const char_type& c2) BOOST_NOEXCEPT
|
||||
{
|
||||
c1 = c2;
|
||||
}
|
||||
|
||||
BOOST_CONSTEXPR static
|
||||
bool eq(char_type c1, char_type c2) BOOST_NOEXCEPT
|
||||
{
|
||||
return to_lower(c1) == to_lower(c2);
|
||||
}
|
||||
|
||||
BOOST_CONSTEXPR static
|
||||
bool lt(char_type c1, char_type c2) BOOST_NOEXCEPT
|
||||
{
|
||||
return to_lower(c1) < to_lower(c2);
|
||||
}
|
||||
|
||||
BOOST_CONSTEXPR static
|
||||
int compare(const char_type* s1, const char_type* s2, size_t n) BOOST_NOEXCEPT
|
||||
{
|
||||
auto itrs = std::mismatch(s1, s1 + n, s2, &eq);
|
||||
if (itrs.first == (s1 + n))
|
||||
return 0;
|
||||
auto c1 = to_lower(*itrs.first);
|
||||
auto c2 = to_lower(*itrs.second);
|
||||
|
||||
return (c1 < c2 ) ? -1 : 1;
|
||||
}
|
||||
|
||||
BOOST_CONSTEXPR static size_t length(const char* s) BOOST_NOEXCEPT { return std::strlen(s); }
|
||||
BOOST_CONSTEXPR static size_t length(const wchar_t* s) BOOST_NOEXCEPT { return std::wcslen(s); }
|
||||
|
||||
BOOST_CONSTEXPR static
|
||||
const char_type* find(const char_type* s, size_t n, const char_type& a) BOOST_NOEXCEPT
|
||||
{
|
||||
const char_type u = to_lower(a);
|
||||
return std::find_if(s, s + n, [u](char_type c){return to_lower(c) == u;});
|
||||
}
|
||||
|
||||
BOOST_CONSTEXPR static
|
||||
char_type* move(char_type* s1, const char_type* s2, size_t n) BOOST_NOEXCEPT
|
||||
{
|
||||
if (s1 < s2)
|
||||
return std::move(s2, s2 + n, s1);
|
||||
else
|
||||
return std::move_backward(s2, s2 + n, s1);
|
||||
}
|
||||
|
||||
BOOST_CONSTEXPR static
|
||||
char_type* copy(char_type* s1, const char_type* s2, size_t n) BOOST_NOEXCEPT
|
||||
{
|
||||
return std::copy(s2, s2 + n, s1);
|
||||
}
|
||||
|
||||
BOOST_CONSTEXPR static
|
||||
char_type* assign(char_type* s, size_t n, char_type a) BOOST_NOEXCEPT
|
||||
{
|
||||
std::fill(s, s + n, a);
|
||||
return s +n;
|
||||
}
|
||||
|
||||
BOOST_CONSTEXPR static
|
||||
int_type not_eof(int_type c) BOOST_NOEXCEPT
|
||||
{
|
||||
return eq_int_type(c, eof()) ? ~eof() : c;
|
||||
}
|
||||
|
||||
BOOST_CONSTEXPR static
|
||||
char_type to_char_type(int_type c) BOOST_NOEXCEPT
|
||||
{
|
||||
return char_type(c);
|
||||
}
|
||||
|
||||
BOOST_CONSTEXPR static
|
||||
int_type to_int_type(char c) BOOST_NOEXCEPT
|
||||
{
|
||||
return int_type((unsigned char)c);
|
||||
}
|
||||
|
||||
BOOST_CONSTEXPR static
|
||||
int_type to_int_type(wchar_t c) BOOST_NOEXCEPT
|
||||
{
|
||||
return int_type((wchar_t)c);
|
||||
}
|
||||
|
||||
BOOST_CONSTEXPR static
|
||||
bool eq_int_type(int_type c1, int_type c2) BOOST_NOEXCEPT
|
||||
{
|
||||
return to_lower(c1, char_type()) == to_lower(c2, char_type());
|
||||
}
|
||||
|
||||
BOOST_CONSTEXPR static inline int_type eof() BOOST_NOEXCEPT
|
||||
{
|
||||
return int_type(EOF);
|
||||
}
|
||||
};
|
||||
|
||||
namespace detail
|
||||
{
|
||||
|
||||
|
||||
template<typename Char>
|
||||
std::size_t hash_step(std::size_t prev, Char c, key_char_traits<Char>)
|
||||
{
|
||||
return prev ^ (key_char_traits<Char>::to_lower(c) << 1);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
template<typename Char>
|
||||
using value_char_traits = std::char_traits<Char>;
|
||||
|
||||
BOOST_CONSTEXPR static char_type equality_sign = L'=';
|
||||
BOOST_CONSTEXPR static char_type delimiter = L';';
|
||||
|
||||
using native_handle_type = wchar_t*;
|
||||
using native_iterator = const wchar_t*;
|
||||
|
||||
namespace detail
|
||||
{
|
||||
|
||||
BOOST_PROCESS_V2_DECL
|
||||
std::basic_string<wchar_t, value_char_traits<wchar_t>> get(
|
||||
basic_cstring_ref<wchar_t, key_char_traits<wchar_t>> key,
|
||||
error_code & ec);
|
||||
|
||||
BOOST_PROCESS_V2_DECL
|
||||
void set(basic_cstring_ref<wchar_t, key_char_traits<wchar_t>> key,
|
||||
basic_cstring_ref<wchar_t, value_char_traits<wchar_t>> value,
|
||||
error_code & ec);
|
||||
|
||||
BOOST_PROCESS_V2_DECL
|
||||
void unset(basic_cstring_ref<wchar_t, key_char_traits<wchar_t>> key,
|
||||
error_code & ec);
|
||||
|
||||
|
||||
BOOST_PROCESS_V2_DECL
|
||||
std::basic_string<char, value_char_traits<char>> get(
|
||||
basic_cstring_ref<char, key_char_traits<char>> key,
|
||||
error_code & ec);
|
||||
|
||||
BOOST_PROCESS_V2_DECL
|
||||
void set(basic_cstring_ref<char, key_char_traits<char>> key,
|
||||
basic_cstring_ref<char, value_char_traits<char>> value,
|
||||
error_code & ec);
|
||||
|
||||
BOOST_PROCESS_V2_DECL
|
||||
void unset(basic_cstring_ref<char, key_char_traits<char>> key,
|
||||
error_code & ec);
|
||||
|
||||
BOOST_PROCESS_V2_DECL native_handle_type load_native_handle();
|
||||
struct native_handle_deleter
|
||||
{
|
||||
native_handle_deleter() = default;
|
||||
native_handle_deleter(const native_handle_deleter& ) = default;
|
||||
BOOST_PROCESS_V2_DECL void operator()(native_handle_type nh) const;
|
||||
|
||||
};
|
||||
|
||||
inline const char_type * dereference(native_iterator iterator) {return iterator;}
|
||||
BOOST_PROCESS_V2_DECL native_iterator next(native_iterator nh);
|
||||
BOOST_PROCESS_V2_DECL native_iterator find_end(native_handle_type nh);
|
||||
BOOST_PROCESS_V2_DECL bool is_executable(const filesystem::path & pth, error_code & ec);
|
||||
}
|
||||
|
||||
}
|
||||
BOOST_PROCESS_V2_END_NAMESPACE
|
||||
|
||||
#endif //BOOST_PROCESS_V2_DETAIL_ENVIRONMENT_WIN_HPP
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
// Copyright (c) 2022 Klemens D. Morgenstern
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
#ifndef BOOST_PROCESS_V2_DETAIL_IMPL_ENVIRONMENT_IPP
|
||||
#define BOOST_PROCESS_V2_DETAIL_IMPL_ENVIRONMENT_IPP
|
||||
|
||||
#include <boost/process/v2/detail/config.hpp>
|
||||
|
||||
#if defined(BOOST_PROCESS_V2_WINDOWS)
|
||||
#include <boost/process/v2/detail/impl/environment_win.ipp>
|
||||
#elif defined(BOOST_PROCESS_V2_POSIX)
|
||||
#include <boost/process/v2/detail/impl/environment_posix.ipp>
|
||||
#else
|
||||
#error Operating System not supported.
|
||||
#endif
|
||||
|
||||
#endif //BOOST_PROCESS_V2_DETAIL_IMPL_ENVIRONMENT_IPP
|
||||
+81
@@ -0,0 +1,81 @@
|
||||
//
|
||||
// process/this_process/detail/environment_posix.hpp
|
||||
// ~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
//
|
||||
// Copyright (c) 2021 Klemens D. Morgenstern (klemens dot morgenstern at gmx dot net)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#ifndef BOOST_PROCESS_V2_DETAIL_ENVIRONMENT_WIN_HPP
|
||||
#define BOOST_PROCESS_V2_DETAIL_ENVIRONMENT_WIN_HPP
|
||||
|
||||
#include <boost/process/v2/detail/config.hpp>
|
||||
#include <boost/process/v2/detail/last_error.hpp>
|
||||
#include <boost/process/v2/environment.hpp>
|
||||
#include <boost/process/v2/cstring_ref.hpp>
|
||||
|
||||
#include <unistd.h>
|
||||
#include <cstring>
|
||||
|
||||
|
||||
BOOST_PROCESS_V2_BEGIN_NAMESPACE
|
||||
|
||||
namespace environment
|
||||
{
|
||||
namespace detail
|
||||
{
|
||||
|
||||
basic_cstring_ref<char_type, value_char_traits<char>> get(
|
||||
basic_cstring_ref<char_type, key_char_traits<char_type>> key,
|
||||
error_code & ec)
|
||||
{
|
||||
auto res = ::getenv(key.c_str());
|
||||
if (res == nullptr)
|
||||
{
|
||||
BOOST_PROCESS_V2_ASSIGN_EC(ec, ENOENT, system_category())
|
||||
return {};
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
void set(basic_cstring_ref<char_type, key_char_traits<char_type>> key,
|
||||
basic_cstring_ref<char_type, value_char_traits<char_type>> value,
|
||||
error_code & ec)
|
||||
{
|
||||
if (::setenv(key.c_str(), value.c_str(), true))
|
||||
BOOST_PROCESS_V2_ASSIGN_LAST_ERROR(ec)
|
||||
}
|
||||
|
||||
void unset(basic_cstring_ref<char_type, key_char_traits<char_type>> key, error_code & ec)
|
||||
{
|
||||
if (::unsetenv(key.c_str()))
|
||||
BOOST_PROCESS_V2_ASSIGN_LAST_ERROR(ec)
|
||||
}
|
||||
|
||||
|
||||
native_handle_type load_native_handle() { return ::environ; }
|
||||
|
||||
|
||||
native_iterator next(native_iterator nh)
|
||||
{
|
||||
return nh + 1;
|
||||
}
|
||||
|
||||
native_iterator find_end(native_handle_type nh)
|
||||
{
|
||||
while (*nh != nullptr)
|
||||
nh++;
|
||||
return nh;
|
||||
}
|
||||
bool is_executable(const filesystem::path & p, error_code & ec)
|
||||
{
|
||||
return filesystem::is_regular_file(p, ec) && (::access(p.c_str(), X_OK) == 0);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
BOOST_PROCESS_V2_END_NAMESPACE
|
||||
|
||||
#endif //BOOST_PROCESS_V2_DETAIL_ENVIRONMENT_WIN_HPP
|
||||
+142
@@ -0,0 +1,142 @@
|
||||
//
|
||||
// process/this_process/detail/environment_win.hpp
|
||||
// ~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
//
|
||||
// Copyright (c) 2021 Klemens D. Morgenstern (klemens dot morgenstern at gmx dot net)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#ifndef BOOST_PROCESS_V2_DETAIL_IMPL_ENVIRONMENT_WIN_IPP
|
||||
#define BOOST_PROCESS_V2_DETAIL_IMPL_ENVIRONMENT_WIN_IPP
|
||||
|
||||
|
||||
#include <boost/process/v2/detail/config.hpp>
|
||||
#include <boost/process/v2/detail/environment_win.hpp>
|
||||
#include <boost/process/v2/detail/impl/environment.ipp>
|
||||
#include <boost/process/v2/detail/last_error.hpp>
|
||||
|
||||
#include <algorithm>
|
||||
#include <cwctype>
|
||||
#include <cstring>
|
||||
|
||||
#include <shellapi.h>
|
||||
|
||||
#include <boost/process/v2/cstring_ref.hpp>
|
||||
#include <boost/process/v2/error.hpp>
|
||||
|
||||
BOOST_PROCESS_V2_BEGIN_NAMESPACE
|
||||
|
||||
namespace environment
|
||||
{
|
||||
namespace detail
|
||||
{
|
||||
|
||||
std::basic_string<char_type, value_char_traits<char_type>> get(
|
||||
basic_cstring_ref<char_type, key_char_traits<char_type>> key,
|
||||
error_code & ec)
|
||||
{
|
||||
std::basic_string<char_type, value_char_traits<char_type>> buf;
|
||||
|
||||
std::size_t size = 0u;
|
||||
do
|
||||
{
|
||||
buf.resize(buf.size() + 4096);
|
||||
size = ::GetEnvironmentVariableW(key.c_str(), &buf.front(), static_cast<DWORD>(buf.size()));
|
||||
}
|
||||
while (size == buf.size());
|
||||
|
||||
buf.resize(size);
|
||||
|
||||
if (buf.size() == 0)
|
||||
BOOST_PROCESS_V2_ASSIGN_LAST_ERROR(ec)
|
||||
|
||||
return buf;
|
||||
}
|
||||
|
||||
void set(basic_cstring_ref<char_type, key_char_traits<char_type>> key,
|
||||
basic_cstring_ref<char_type, value_char_traits<char_type>> value,
|
||||
error_code & ec)
|
||||
{
|
||||
if (!::SetEnvironmentVariableW(key.c_str(), value.c_str()))
|
||||
BOOST_PROCESS_V2_ASSIGN_LAST_ERROR(ec)
|
||||
}
|
||||
|
||||
void unset(basic_cstring_ref<char_type, key_char_traits<char_type>> key,
|
||||
error_code & ec)
|
||||
{
|
||||
if (!::SetEnvironmentVariableW(key.c_str(), nullptr))
|
||||
BOOST_PROCESS_V2_ASSIGN_LAST_ERROR(ec)
|
||||
}
|
||||
|
||||
|
||||
std::basic_string<char, value_char_traits<char>> get(
|
||||
basic_cstring_ref<char, key_char_traits<char>> key,
|
||||
error_code & ec)
|
||||
{
|
||||
std::basic_string<char, value_char_traits<char>> buf;
|
||||
|
||||
std::size_t size = 0u;
|
||||
do
|
||||
{
|
||||
buf.resize(buf.size() + 4096);
|
||||
size = ::GetEnvironmentVariableA(key.c_str(), &buf.front(), static_cast<DWORD>(buf.size()));
|
||||
}
|
||||
while (size == buf.size());
|
||||
|
||||
buf.resize(size);
|
||||
|
||||
if (buf.size() == 0)
|
||||
BOOST_PROCESS_V2_ASSIGN_LAST_ERROR(ec)
|
||||
|
||||
return buf;
|
||||
}
|
||||
|
||||
void set(basic_cstring_ref<char, key_char_traits<char>> key,
|
||||
basic_cstring_ref<char, value_char_traits<char>> value,
|
||||
error_code & ec)
|
||||
{
|
||||
if (!::SetEnvironmentVariableA(key.c_str(), value.c_str()))
|
||||
BOOST_PROCESS_V2_ASSIGN_LAST_ERROR(ec)
|
||||
}
|
||||
|
||||
void unset(basic_cstring_ref<char, key_char_traits<char>> key,
|
||||
error_code & ec)
|
||||
{
|
||||
if (!::SetEnvironmentVariableA(key.c_str(), nullptr))
|
||||
BOOST_PROCESS_V2_ASSIGN_LAST_ERROR(ec)
|
||||
}
|
||||
|
||||
|
||||
native_handle_type load_native_handle() { return ::GetEnvironmentStringsW(); }
|
||||
void native_handle_deleter::operator()(native_handle_type nh) const
|
||||
{
|
||||
::FreeEnvironmentStringsW(nh);
|
||||
}
|
||||
|
||||
native_iterator next(native_iterator nh)
|
||||
{
|
||||
while (*nh != L'\0')
|
||||
nh++;
|
||||
return ++nh;
|
||||
}
|
||||
|
||||
|
||||
native_iterator find_end(native_handle_type nh)
|
||||
{
|
||||
while ((*nh != L'\0') || (*std::next(nh) != L'\0'))
|
||||
nh++;
|
||||
return ++nh;
|
||||
}
|
||||
|
||||
bool is_executable(const filesystem::path & pth, error_code & ec)
|
||||
{
|
||||
return filesystem::is_regular_file(pth, ec) && SHGetFileInfoW(pth.native().c_str(), 0,0,0, SHGFI_EXETYPE);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
BOOST_PROCESS_V2_END_NAMESPACE
|
||||
|
||||
#endif //BOOST_PROCESS_V2_DETAIL_IMPL_ENVIRONMENT_WIN_IPP
|
||||
+34
@@ -0,0 +1,34 @@
|
||||
// Copyright (c) 2022 Klemens D. Morgenstern
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
#ifndef BOOST_PROCESS_V2_DETAIL_IMPL_LAST_ERROR_IPP
|
||||
#define BOOST_PROCESS_V2_DETAIL_IMPL_LAST_ERROR_IPP
|
||||
|
||||
#include <boost/process/v2/detail/config.hpp>
|
||||
#include <boost/process/v2/detail/last_error.hpp>
|
||||
|
||||
#if defined(BOOST_PROCESS_V2_WINDOWS)
|
||||
#include <windows.h>
|
||||
#else
|
||||
#include <cerrno>
|
||||
#endif
|
||||
|
||||
BOOST_PROCESS_V2_BEGIN_NAMESPACE
|
||||
namespace detail
|
||||
{
|
||||
|
||||
error_code get_last_error()
|
||||
{
|
||||
#if defined(BOOST_PROCESS_V2_WINDOWS)
|
||||
return error_code(::GetLastError(), system_category());
|
||||
#else
|
||||
return error_code(errno, system_category());
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
BOOST_PROCESS_V2_END_NAMESPACE
|
||||
|
||||
#endif //BOOST_PROCESS_V2_DETAIL_IMPL_LAST_ERROR_IPP
|
||||
+169
@@ -0,0 +1,169 @@
|
||||
// Copyright (c) 2022 Klemens D. Morgenstern
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
#ifndef BOOST_PROCESS_V2_DETAIL_IMPL_PROCESS_HANDLE_WINDOWS_IPP
|
||||
#define BOOST_PROCESS_V2_DETAIL_IMPL_PROCESS_HANDLE_WINDOWS_IPP
|
||||
|
||||
#include <boost/process/v2/detail/config.hpp>
|
||||
#include <boost/process/v2/detail/last_error.hpp>
|
||||
#include <boost/process/v2/detail/throw_error.hpp>
|
||||
#include <boost/process/v2/detail/process_handle_windows.hpp>
|
||||
#include <boost/process/v2/ext/detail/proc_info.hpp>
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
#if !defined(BOOST_PROCESS_V2_DISABLE_UNDOCUMENTED_API)
|
||||
extern "C"
|
||||
{
|
||||
|
||||
LONG WINAPI NtResumeProcess(HANDLE ProcessHandle);
|
||||
LONG WINAPI NtSuspendProcess(HANDLE ProcessHandle);
|
||||
|
||||
}
|
||||
#endif
|
||||
|
||||
BOOST_PROCESS_V2_BEGIN_NAMESPACE
|
||||
|
||||
namespace detail
|
||||
{
|
||||
|
||||
void get_exit_code_(
|
||||
HANDLE handle,
|
||||
native_exit_code_type & exit_code,
|
||||
error_code & ec)
|
||||
{
|
||||
if (!::GetExitCodeProcess(handle, &exit_code))
|
||||
BOOST_PROCESS_V2_ASSIGN_LAST_ERROR(ec)
|
||||
}
|
||||
|
||||
|
||||
HANDLE open_process_(DWORD pid)
|
||||
{
|
||||
auto proc = OpenProcess(PROCESS_TERMINATE | SYNCHRONIZE, FALSE, pid);
|
||||
if (proc == nullptr)
|
||||
{
|
||||
error_code ec;
|
||||
BOOST_PROCESS_V2_ASSIGN_LAST_ERROR(ec)
|
||||
throw system_error(ec, "open_process()");
|
||||
}
|
||||
|
||||
return proc;
|
||||
}
|
||||
|
||||
|
||||
void terminate_if_running_(HANDLE handle)
|
||||
{
|
||||
DWORD exit_code = 0u;
|
||||
if (handle == INVALID_HANDLE_VALUE)
|
||||
return ;
|
||||
if (::GetExitCodeProcess(handle, &exit_code))
|
||||
if (exit_code == STILL_ACTIVE)
|
||||
::TerminateProcess(handle, 260);
|
||||
}
|
||||
|
||||
bool check_handle_(HANDLE handle, error_code & ec)
|
||||
{
|
||||
if (handle == INVALID_HANDLE_VALUE)
|
||||
{
|
||||
BOOST_PROCESS_V2_ASSIGN_EC(ec, ERROR_INVALID_HANDLE_STATE, system_category())
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool check_pid_(pid_type pid_, error_code & ec)
|
||||
{
|
||||
if (pid_ == 0)
|
||||
{
|
||||
BOOST_PROCESS_V2_ASSIGN_EC(ec, ERROR_INVALID_HANDLE_STATE, system_category())
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
struct enum_windows_data_t
|
||||
{
|
||||
error_code &ec;
|
||||
pid_type pid;
|
||||
};
|
||||
|
||||
static BOOL CALLBACK enum_window(HWND hwnd, LPARAM param)
|
||||
{
|
||||
auto data = reinterpret_cast<enum_windows_data_t*>(param);
|
||||
DWORD pid{0u};
|
||||
GetWindowThreadProcessId(hwnd, &pid);
|
||||
if (pid != data->pid)
|
||||
return TRUE;
|
||||
|
||||
LRESULT res = ::SendMessageW(hwnd, WM_CLOSE, 0, 0);
|
||||
|
||||
if (res)
|
||||
BOOST_PROCESS_V2_ASSIGN_LAST_ERROR(data->ec)
|
||||
return res == 0;
|
||||
}
|
||||
|
||||
void request_exit_(pid_type pid_, error_code & ec)
|
||||
{
|
||||
enum_windows_data_t data{ec, pid_};
|
||||
|
||||
if (!::EnumWindows(enum_window, reinterpret_cast<LONG_PTR>(&data)))
|
||||
BOOST_PROCESS_V2_ASSIGN_LAST_ERROR(ec)
|
||||
}
|
||||
|
||||
void interrupt_(pid_type pid_, error_code & ec)
|
||||
{
|
||||
if (!::GenerateConsoleCtrlEvent(CTRL_C_EVENT, pid_))
|
||||
BOOST_PROCESS_V2_ASSIGN_LAST_ERROR(ec)
|
||||
}
|
||||
|
||||
void terminate_(HANDLE handle, error_code & ec, DWORD & exit_status)
|
||||
{
|
||||
if (!::TerminateProcess(handle, 260))
|
||||
BOOST_PROCESS_V2_ASSIGN_LAST_ERROR(ec)
|
||||
}
|
||||
|
||||
void check_running_(HANDLE handle, error_code & ec, DWORD & exit_status)
|
||||
{
|
||||
if (!::GetExitCodeProcess(handle, &exit_status))
|
||||
BOOST_PROCESS_V2_ASSIGN_LAST_ERROR(ec)
|
||||
}
|
||||
|
||||
#if !defined(BOOST_PROCESS_V2_DISABLE_UNDOCUMENTED_API)
|
||||
void suspend_(HANDLE handle, error_code & ec)
|
||||
{
|
||||
auto nt_err = NtSuspendProcess(handle);
|
||||
ULONG dos_err = RtlNtStatusToDosError(nt_err);
|
||||
if (dos_err)
|
||||
BOOST_PROCESS_V2_ASSIGN_LAST_ERROR(ec)
|
||||
}
|
||||
|
||||
void resume_(HANDLE handle, error_code & ec)
|
||||
{
|
||||
auto nt_err = NtResumeProcess(handle);
|
||||
ULONG dos_err = RtlNtStatusToDosError(nt_err);
|
||||
if (dos_err)
|
||||
BOOST_PROCESS_V2_ASSIGN_LAST_ERROR(ec)
|
||||
}
|
||||
#else
|
||||
void suspend_(HANDLE, error_code & ec)
|
||||
{
|
||||
BOOST_PROCESS_V2_ASSIGN_EC(ec, ERROR_CALL_NOT_IMPLEMENTED, system_category())
|
||||
}
|
||||
|
||||
void resume_(HANDLE handle, error_code & ec)
|
||||
{
|
||||
BOOST_PROCESS_V2_ASSIGN_EC(ec, ERROR_CALL_NOT_IMPLEMENTED, system_category())
|
||||
}
|
||||
#endif
|
||||
|
||||
#if !defined(BOOST_PROCESS_V2_HEADER_ONLY)
|
||||
template struct basic_process_handle_win<>;
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
|
||||
BOOST_PROCESS_V2_END_NAMESPACE
|
||||
|
||||
#endif //BOOST_PROCESS_V2_DETAIL_IMPL_PROCESS_HANDLE_WINDOWS_IPP
|
||||
+31
@@ -0,0 +1,31 @@
|
||||
// Copyright (c) 2022 Klemens D. Morgenstern
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
#ifndef BOOST_PROCESS_V2_DETAIL_IMPL_THROW_ERROR_IPP
|
||||
#define BOOST_PROCESS_V2_DETAIL_IMPL_THROW_ERROR_IPP
|
||||
|
||||
#include <boost/process/v2/detail/config.hpp>
|
||||
#include <boost/process/v2/detail/throw_error.hpp>
|
||||
#include <boost/process/v2/detail/throw_exception.hpp>
|
||||
|
||||
BOOST_PROCESS_V2_BEGIN_NAMESPACE
|
||||
namespace detail
|
||||
{
|
||||
|
||||
void do_throw_error(const error_code& err)
|
||||
{
|
||||
system_error e(err);
|
||||
throw_exception(e);
|
||||
}
|
||||
|
||||
void do_throw_error(const error_code& err, const char* location)
|
||||
{
|
||||
system_error e(err, location);
|
||||
throw_exception(e);
|
||||
}
|
||||
|
||||
}
|
||||
BOOST_PROCESS_V2_END_NAMESPACE
|
||||
|
||||
#endif //BOOST_PROCESS_V2_DETAIL_IMPL_THROW_ERROR_IPP
|
||||
+379
@@ -0,0 +1,379 @@
|
||||
// Copyright (c) 2022 Klemens D. Morgenstern
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
#ifndef BOOST_PROCESS_V2_DETAIL_IMPL_UTF8_HPP
|
||||
#define BOOST_PROCESS_V2_DETAIL_IMPL_UTF8_HPP
|
||||
|
||||
#include <boost/process/v2/detail/utf8.hpp>
|
||||
#include <boost/process/v2/detail/config.hpp>
|
||||
#include <boost/process/v2/detail/last_error.hpp>
|
||||
#include <boost/process/v2/error.hpp>
|
||||
|
||||
#if defined(BOOST_PROCESS_V2_WINDOWS)
|
||||
#include <Windows.h>
|
||||
#endif
|
||||
|
||||
BOOST_PROCESS_V2_BEGIN_NAMESPACE
|
||||
|
||||
namespace detail
|
||||
{
|
||||
|
||||
#if defined(BOOST_PROCESS_V2_WINDOWS)
|
||||
|
||||
inline void handle_error(error_code & ec)
|
||||
{
|
||||
const auto err = ::GetLastError();
|
||||
switch (err)
|
||||
{
|
||||
case ERROR_INSUFFICIENT_BUFFER:
|
||||
BOOST_PROCESS_V2_ASSIGN_EC(ec, error::insufficient_buffer, error::utf8_category)
|
||||
break;
|
||||
case ERROR_NO_UNICODE_TRANSLATION:
|
||||
BOOST_PROCESS_V2_ASSIGN_EC(ec, error::invalid_character, error::utf8_category)
|
||||
break;
|
||||
default:
|
||||
BOOST_PROCESS_V2_ASSIGN_EC(ec, err, system_category())
|
||||
}
|
||||
}
|
||||
|
||||
std::size_t size_as_utf8(const wchar_t * in, std::size_t size, error_code & ec)
|
||||
{
|
||||
auto res = WideCharToMultiByte(
|
||||
CP_UTF8, // CodePage,
|
||||
0, // dwFlags,
|
||||
in, // lpWideCharStr,
|
||||
static_cast<int>(size), // cchWideChar,
|
||||
nullptr, // lpMultiByteStr,
|
||||
0, // cbMultiByte,
|
||||
nullptr, // lpDefaultChar,
|
||||
FALSE); // lpUsedDefaultChar
|
||||
|
||||
if (res == 0u)
|
||||
handle_error(ec);
|
||||
return static_cast<std::size_t>(res);
|
||||
}
|
||||
|
||||
std::size_t size_as_wide(const char * in, std::size_t size, error_code & ec)
|
||||
{
|
||||
auto res = ::MultiByteToWideChar(
|
||||
CP_UTF8, // CodePage
|
||||
0, // dwFlags
|
||||
in, // lpMultiByteStr
|
||||
static_cast<int>(size), // cbMultiByte
|
||||
nullptr, // lpWideCharStr
|
||||
0); // cchWideChar
|
||||
if (res == 0u)
|
||||
handle_error(ec);
|
||||
|
||||
return static_cast<std::size_t>(res);
|
||||
}
|
||||
|
||||
std::size_t convert_to_utf8(const wchar_t *in, std::size_t size, char * out,
|
||||
std::size_t max_size, error_code & ec)
|
||||
{
|
||||
auto res = ::WideCharToMultiByte(
|
||||
CP_UTF8, // CodePage
|
||||
0, // dwFlags
|
||||
in, // lpWideCharStr
|
||||
static_cast<int>(size), // cchWideChar
|
||||
out, // lpMultiByteStr
|
||||
static_cast<int>(max_size), // cbMultiByte
|
||||
nullptr, // lpDefaultChar
|
||||
FALSE); // lpUsedDefaultChar
|
||||
if (res == 0u)
|
||||
handle_error(ec);
|
||||
|
||||
return static_cast<std::size_t>(res);
|
||||
}
|
||||
|
||||
std::size_t convert_to_wide(const char *in, std::size_t size, wchar_t * out,
|
||||
std::size_t max_size, error_code & ec)
|
||||
{
|
||||
auto res = ::MultiByteToWideChar(
|
||||
CP_UTF8, // CodePage
|
||||
0, // dwFlags
|
||||
in, // lpMultiByteStr
|
||||
static_cast<int>(size), // cbMultiByte
|
||||
out, // lpWideCharStr
|
||||
static_cast<int>(max_size)); // cchWideChar
|
||||
if (res == 0u)
|
||||
handle_error(ec);
|
||||
|
||||
return static_cast<std::size_t>(res);
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
|
||||
template<std::size_t s>
|
||||
inline int get_cont_octet_out_count_impl(wchar_t word) {
|
||||
if (word < 0x80) {
|
||||
return 0;
|
||||
}
|
||||
if (word < 0x800) {
|
||||
return 1;
|
||||
}
|
||||
return 2;
|
||||
}
|
||||
|
||||
template<>
|
||||
inline int get_cont_octet_out_count_impl<4>(wchar_t word) {
|
||||
if (word < 0x80) {
|
||||
return 0;
|
||||
}
|
||||
if (word < 0x800) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Note that the following code will generate warnings on some platforms
|
||||
// where wchar_t is defined as UCS2. The warnings are superfluous as the
|
||||
// specialization is never instantiated with such compilers, but this
|
||||
// can cause problems if warnings are being treated as errors, so we guard
|
||||
// against that. Including <boost/detail/utf8_codecvt_facet.hpp> as we do
|
||||
// should be enough to get WCHAR_MAX defined.
|
||||
#if !defined(WCHAR_MAX)
|
||||
# error WCHAR_MAX not defined!
|
||||
#endif
|
||||
// cope with VC++ 7.1 or earlier having invalid WCHAR_MAX
|
||||
#if defined(_MSC_VER) && _MSC_VER <= 1310 // 7.1 or earlier
|
||||
return 2;
|
||||
#elif WCHAR_MAX > 0x10000
|
||||
|
||||
if (word < 0x10000) {
|
||||
return 2;
|
||||
}
|
||||
if (word < 0x200000) {
|
||||
return 3;
|
||||
}
|
||||
if (word < 0x4000000) {
|
||||
return 4;
|
||||
}
|
||||
return 5;
|
||||
|
||||
#else
|
||||
return 2;
|
||||
#endif
|
||||
}
|
||||
|
||||
inline int get_cont_octet_out_count(wchar_t word)
|
||||
{
|
||||
return detail::get_cont_octet_out_count_impl<sizeof(wchar_t)>(word);
|
||||
}
|
||||
|
||||
// copied from boost/detail/utf8_codecvt_facet.ipp
|
||||
// Copyright (c) 2001 Ronald Garcia, Indiana University (garcia@osl.iu.edu)
|
||||
// Andrew Lumsdaine, Indiana University (lums@osl.iu.edu).
|
||||
|
||||
inline unsigned int get_octet_count(unsigned char lead_octet)
|
||||
{
|
||||
// if the 0-bit (MSB) is 0, then 1 character
|
||||
if (lead_octet <= 0x7f) return 1;
|
||||
|
||||
// Otherwise the count number of consecutive 1 bits starting at MSB
|
||||
// assert(0xc0 <= lead_octet && lead_octet <= 0xfd);
|
||||
|
||||
if (0xc0 <= lead_octet && lead_octet <= 0xdf) return 2;
|
||||
else if (0xe0 <= lead_octet && lead_octet <= 0xef) return 3;
|
||||
else if (0xf0 <= lead_octet && lead_octet <= 0xf7) return 4;
|
||||
else if (0xf8 <= lead_octet && lead_octet <= 0xfb) return 5;
|
||||
else return 6;
|
||||
}
|
||||
|
||||
inline bool invalid_continuing_octet(unsigned char octet_1) {
|
||||
return (octet_1 < 0x80|| 0xbf< octet_1);
|
||||
}
|
||||
|
||||
inline unsigned int get_cont_octet_count(unsigned char lead_octet)
|
||||
{
|
||||
return get_octet_count(lead_octet) - 1;
|
||||
}
|
||||
|
||||
inline const wchar_t * get_octet1_modifier_table() noexcept
|
||||
{
|
||||
static const wchar_t octet1_modifier_table[] = {
|
||||
0x00, 0xc0, 0xe0, 0xf0, 0xf8, 0xfc
|
||||
};
|
||||
return octet1_modifier_table;
|
||||
}
|
||||
|
||||
|
||||
std::size_t size_as_utf8(const wchar_t * in, std::size_t size, error_code & ec)
|
||||
{
|
||||
std::size_t res = 0u;
|
||||
const auto from_end = in + size;
|
||||
for (auto from = in; from != from_end; from++)
|
||||
res += get_cont_octet_out_count(*from) + 1;
|
||||
return res;
|
||||
}
|
||||
|
||||
std::size_t size_as_wide(const char * in, std::size_t size, error_code & ec)
|
||||
{
|
||||
const auto from = in;
|
||||
const auto from_end = from + size;
|
||||
const char * from_next = from;
|
||||
for (std::size_t char_count = 0u; from_next < from_end; ++char_count) {
|
||||
unsigned int octet_count = get_octet_count(*from_next);
|
||||
// The buffer may represent incomplete characters, so terminate early if one is found
|
||||
if (octet_count > static_cast<std::size_t>(from_end - from_next))
|
||||
break;
|
||||
from_next += octet_count;
|
||||
}
|
||||
|
||||
return from_next - from;
|
||||
}
|
||||
|
||||
std::size_t convert_to_utf8(const wchar_t * in, std::size_t size,
|
||||
char * out, std::size_t max_size, error_code & ec)
|
||||
{
|
||||
|
||||
const wchar_t * from = in;
|
||||
const wchar_t * from_end = from + size;
|
||||
const wchar_t * & from_next = from;
|
||||
char * to = out;
|
||||
char * to_end = out + max_size;
|
||||
char * & to_next = to;
|
||||
|
||||
const wchar_t * const octet1_modifier_table = get_octet1_modifier_table();
|
||||
wchar_t max_wchar = (std::numeric_limits<wchar_t>::max)();
|
||||
while (from != from_end && to != to_end) {
|
||||
|
||||
// Check for invalid UCS-4 character
|
||||
if (*from > max_wchar) {
|
||||
from_next = from;
|
||||
to_next = to;
|
||||
BOOST_PROCESS_V2_ASSIGN_EC(ec, error::invalid_character, error::get_utf8_category())
|
||||
return 0u;
|
||||
}
|
||||
|
||||
int cont_octet_count = get_cont_octet_out_count(*from);
|
||||
|
||||
// RG - comment this formula better
|
||||
int shift_exponent = cont_octet_count * 6;
|
||||
|
||||
// Process the first character
|
||||
*to++ = static_cast<char>(octet1_modifier_table[cont_octet_count] +
|
||||
(unsigned char)(*from / (1 << shift_exponent)));
|
||||
|
||||
// Process the continuation characters
|
||||
// Invariants: At the start of the loop:
|
||||
// 1) 'i' continuing octets have been generated
|
||||
// 2) '*to' points to the next location to place an octet
|
||||
// 3) shift_exponent is 6 more than needed for the next octet
|
||||
int i = 0;
|
||||
while (i != cont_octet_count && to != to_end) {
|
||||
shift_exponent -= 6;
|
||||
*to++ = static_cast<char>(0x80 + ((*from / (1 << shift_exponent)) % (1 << 6)));
|
||||
++i;
|
||||
}
|
||||
// If we filled up the out buffer before encoding the character
|
||||
if (to == to_end && i != cont_octet_count) {
|
||||
from_next = from;
|
||||
to_next = to - (i + 1);
|
||||
BOOST_PROCESS_V2_ASSIGN_EC(ec, error::insufficient_buffer, error::get_utf8_category())
|
||||
return 0u;
|
||||
}
|
||||
++from;
|
||||
}
|
||||
from_next = from;
|
||||
to_next = to;
|
||||
|
||||
// Were we done or did we run out of destination space
|
||||
if (from != from_end)
|
||||
BOOST_PROCESS_V2_ASSIGN_EC(ec, error::insufficient_buffer, error::get_utf8_category())
|
||||
|
||||
return to_next - out;
|
||||
}
|
||||
|
||||
inline bool invalid_leading_octet(unsigned char octet_1) {
|
||||
return (0x7f < octet_1 && octet_1 < 0xc0) ||
|
||||
(octet_1 > 0xfd);
|
||||
}
|
||||
|
||||
std::size_t convert_to_wide(const char * in, std::size_t size,
|
||||
wchar_t * out, std::size_t max_size, error_code & ec)
|
||||
{
|
||||
const char * from = in;
|
||||
const char * from_end = from + size;
|
||||
const char * & from_next = from;
|
||||
wchar_t * to = out;
|
||||
wchar_t * to_end = out + max_size;
|
||||
wchar_t * & to_next = to;
|
||||
|
||||
// Basic algorithm: The first octet determines how many
|
||||
// octets total make up the UCS-4 character. The remaining
|
||||
// "continuing octets" all begin with "10". To convert, subtract
|
||||
// the amount that specifies the number of octets from the first
|
||||
// octet. Subtract 0x80 (1000 0000) from each continuing octet,
|
||||
// then mash the whole lot together. Note that each continuing
|
||||
// octet only uses 6 bits as unique values, so only shift by
|
||||
// multiples of 6 to combine.
|
||||
const wchar_t * const octet1_modifier_table = detail::get_octet1_modifier_table();
|
||||
while (from != from_end && to != to_end) {
|
||||
|
||||
// Error checking on the first octet
|
||||
if (invalid_leading_octet(*from)) {
|
||||
from_next = from;
|
||||
to_next = to;
|
||||
BOOST_PROCESS_V2_ASSIGN_EC(ec, error::invalid_character, error::get_utf8_category())
|
||||
return 0u;
|
||||
}
|
||||
|
||||
// The first octet is adjusted by a value dependent upon
|
||||
// the number of "continuing octets" encoding the character
|
||||
const int cont_octet_count = get_cont_octet_count(*from);
|
||||
|
||||
// The unsigned char conversion is necessary in case char is
|
||||
// signed (I learned this the hard way)
|
||||
wchar_t ucs_result =
|
||||
(unsigned char)(*from++) - octet1_modifier_table[cont_octet_count];
|
||||
|
||||
// Invariants:
|
||||
// 1) At the start of the loop, 'i' continuing characters have been
|
||||
// processed
|
||||
// 2) *from points to the next continuing character to be processed.
|
||||
int i = 0;
|
||||
while (i != cont_octet_count && from != from_end) {
|
||||
|
||||
// Error checking on continuing characters
|
||||
if (invalid_continuing_octet(*from)) {
|
||||
from_next = from;
|
||||
to_next = to;
|
||||
BOOST_PROCESS_V2_ASSIGN_EC(ec, error::invalid_character, error::get_utf8_category())
|
||||
return 0u;
|
||||
}
|
||||
|
||||
ucs_result *= (1 << 6);
|
||||
|
||||
// each continuing character has an extra (10xxxxxx)b attached to
|
||||
// it that must be removed.
|
||||
ucs_result += (unsigned char)(*from++) - 0x80;
|
||||
++i;
|
||||
}
|
||||
|
||||
// If the buffer ends with an incomplete unicode character...
|
||||
if (from == from_end && i != cont_octet_count) {
|
||||
// rewind "from" to before the current character translation
|
||||
from_next = from - (i + 1);
|
||||
to_next = to;
|
||||
BOOST_PROCESS_V2_ASSIGN_EC(ec, error::insufficient_buffer, error::get_utf8_category())
|
||||
return 0u;
|
||||
}
|
||||
*to++ = ucs_result;
|
||||
}
|
||||
from_next = from;
|
||||
to_next = to;
|
||||
|
||||
if (from != from_end)
|
||||
BOOST_PROCESS_V2_ASSIGN_EC(ec, error::insufficient_buffer, error::get_utf8_category())
|
||||
|
||||
return to_next - out;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
BOOST_PROCESS_V2_END_NAMESPACE
|
||||
|
||||
#endif //BOOST_PROCESS_V2_DETAIL_IMPL_UTF8_HPP
|
||||
+28
@@ -0,0 +1,28 @@
|
||||
// Copyright (c) 2022 Klemens D. Morgenstern
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
#ifndef BOOST_PROCESS_V2_DETAIL_LAST_ERROR_HPP
|
||||
#define BOOST_PROCESS_V2_DETAIL_LAST_ERROR_HPP
|
||||
|
||||
#include <boost/process/v2/detail/config.hpp>
|
||||
|
||||
BOOST_PROCESS_V2_BEGIN_NAMESPACE
|
||||
|
||||
namespace detail
|
||||
{
|
||||
|
||||
BOOST_PROCESS_V2_DECL error_code get_last_error();
|
||||
|
||||
}
|
||||
|
||||
BOOST_PROCESS_V2_END_NAMESPACE
|
||||
|
||||
#if defined(BOOST_PROCESS_V2_HEADER_ONLY)
|
||||
|
||||
#include <boost/process/v2/detail/impl/last_error.ipp>
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
#endif //BOOST_PROCESS_V2_DETAIL_LAST_ERROR_HPP
|
||||
+355
@@ -0,0 +1,355 @@
|
||||
|
||||
// Copyright (c) 2022 Klemens D. Morgenstern
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
#ifndef BOOST_PROCESS_V2_DETAIL_PROCESS_HANDLE_FD_HPP
|
||||
#define BOOST_PROCESS_V2_DETAIL_PROCESS_HANDLE_FD_HPP
|
||||
|
||||
#include <boost/process/v2/detail/config.hpp>
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/wait.h>
|
||||
#include <sys/syscall.h>
|
||||
|
||||
#include <boost/process/v2/detail/last_error.hpp>
|
||||
#include <boost/process/v2/detail/throw_error.hpp>
|
||||
#include <boost/process/v2/exit_code.hpp>
|
||||
#include <boost/process/v2/pid.hpp>
|
||||
|
||||
#if defined(BOOST_PROCESS_V2_STANDALONE)
|
||||
#include <asio/any_io_executor.hpp>
|
||||
#include <asio/compose.hpp>
|
||||
#include <asio/posix/basic_stream_descriptor.hpp>
|
||||
#include <asio/post.hpp>
|
||||
#else
|
||||
#include <boost/asio/any_io_executor.hpp>
|
||||
#include <boost/asio/compose.hpp>
|
||||
#include <boost/asio/posix/basic_stream_descriptor.hpp>
|
||||
#include <boost/asio/post.hpp>
|
||||
#endif
|
||||
|
||||
BOOST_PROCESS_V2_BEGIN_NAMESPACE
|
||||
|
||||
namespace detail
|
||||
{
|
||||
|
||||
template<typename Executor = BOOST_PROCESS_V2_ASIO_NAMESPACE::any_io_executor>
|
||||
struct basic_process_handle_fd
|
||||
{
|
||||
using native_handle_type = int;
|
||||
|
||||
typedef Executor executor_type;
|
||||
|
||||
executor_type get_executor()
|
||||
{ return descriptor_.get_executor(); }
|
||||
|
||||
/// Rebinds the process_handle to another executor.
|
||||
template<typename Executor1>
|
||||
struct rebind_executor
|
||||
{
|
||||
/// The socket type when rebound to the specified executor.
|
||||
typedef basic_process_handle_fd<Executor1> other;
|
||||
};
|
||||
|
||||
template<typename ExecutionContext>
|
||||
basic_process_handle_fd(ExecutionContext &context,
|
||||
typename std::enable_if<
|
||||
std::is_convertible<ExecutionContext &,
|
||||
BOOST_PROCESS_V2_ASIO_NAMESPACE::execution_context &>::value>::type * = nullptr)
|
||||
: pid_(-1), descriptor_(context)
|
||||
{
|
||||
}
|
||||
|
||||
basic_process_handle_fd(executor_type executor)
|
||||
: pid_(-1), descriptor_(executor)
|
||||
{
|
||||
}
|
||||
|
||||
basic_process_handle_fd(executor_type executor, pid_type pid)
|
||||
: pid_(pid), descriptor_(executor, syscall(SYS_pidfd_open, pid, 0))
|
||||
{
|
||||
}
|
||||
|
||||
basic_process_handle_fd(executor_type executor, pid_type pid, native_handle_type process_handle)
|
||||
: pid_(pid), descriptor_(executor, process_handle)
|
||||
{
|
||||
}
|
||||
|
||||
basic_process_handle_fd(basic_process_handle_fd &&handle)
|
||||
: pid_(handle.pid_), descriptor_(std::move(handle.descriptor_))
|
||||
{
|
||||
handle.pid_ = -1;
|
||||
}
|
||||
|
||||
template<typename Executor1>
|
||||
basic_process_handle_fd(basic_process_handle_fd<Executor1> &&handle)
|
||||
: pid_(handle.pid_), descriptor_(std::move(handle.descriptor_))
|
||||
{
|
||||
handle.pid_ = -1;
|
||||
}
|
||||
|
||||
|
||||
basic_process_handle_fd& operator=(basic_process_handle_fd &&handle)
|
||||
{
|
||||
pid_ = handle.pid_;
|
||||
descriptor_ = std::move(handle.descriptor_);
|
||||
handle.pid_ = -1;
|
||||
return *this;
|
||||
}
|
||||
|
||||
pid_type id() const
|
||||
{ return pid_; }
|
||||
|
||||
native_handle_type native_handle() {return pid_;}
|
||||
|
||||
void terminate_if_running(error_code &)
|
||||
{
|
||||
if (pid_ <= 0)
|
||||
return;
|
||||
if (::waitpid(pid_, nullptr, WNOHANG) == 0)
|
||||
{
|
||||
::kill(pid_, SIGKILL);
|
||||
::waitpid(pid_, nullptr, 0);
|
||||
}
|
||||
}
|
||||
|
||||
void terminate_if_running()
|
||||
{
|
||||
if (pid_ <= 0)
|
||||
return;
|
||||
if (::waitpid(pid_, nullptr, WNOHANG) == 0)
|
||||
{
|
||||
::kill(pid_, SIGKILL);
|
||||
::waitpid(pid_, nullptr, 0);
|
||||
}
|
||||
}
|
||||
|
||||
void wait(native_exit_code_type &exit_status, error_code &ec)
|
||||
{
|
||||
if (pid_ <= 0)
|
||||
return;
|
||||
while (::waitpid(pid_, &exit_status, 0) < 0)
|
||||
{
|
||||
if (errno != EINTR)
|
||||
{
|
||||
ec = get_last_error();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void wait(native_exit_code_type &exit_status)
|
||||
{
|
||||
if (pid_ <= 0)
|
||||
return;
|
||||
error_code ec;
|
||||
wait(exit_status, ec);
|
||||
if (ec)
|
||||
detail::throw_error(ec, "wait(pid)");
|
||||
}
|
||||
|
||||
void interrupt(error_code &ec)
|
||||
{
|
||||
if (pid_ <= 0)
|
||||
return;
|
||||
if (::kill(pid_, SIGINT) == -1)
|
||||
ec = get_last_error();
|
||||
}
|
||||
|
||||
void interrupt()
|
||||
{
|
||||
if (pid_ <= 0)
|
||||
return;
|
||||
error_code ec;
|
||||
interrupt(ec);
|
||||
if (ec)
|
||||
detail::throw_error(ec, "interrupt");
|
||||
}
|
||||
|
||||
void request_exit(error_code &ec)
|
||||
{
|
||||
if (pid_ <= 0)
|
||||
return;
|
||||
if (::kill(pid_, SIGTERM) == -1)
|
||||
ec = get_last_error();
|
||||
}
|
||||
|
||||
void request_exit()
|
||||
{
|
||||
if (pid_ <= 0)
|
||||
return;
|
||||
error_code ec;
|
||||
request_exit(ec);
|
||||
if (ec)
|
||||
detail::throw_error(ec, "request_exit");
|
||||
}
|
||||
|
||||
void suspend()
|
||||
{
|
||||
if (pid_ <= 0)
|
||||
return ;
|
||||
error_code ec;
|
||||
suspend(ec);
|
||||
if (ec)
|
||||
detail::throw_error(ec, "suspend");
|
||||
}
|
||||
|
||||
void suspend(error_code &ec)
|
||||
{
|
||||
if (pid_ <= 0)
|
||||
return ;
|
||||
if (::kill(pid_, SIGSTOP) == -1)
|
||||
ec = get_last_error();
|
||||
}
|
||||
|
||||
void resume()
|
||||
{
|
||||
if (pid_ <= 0)
|
||||
return ;
|
||||
error_code ec;
|
||||
resume(ec);
|
||||
if (ec)
|
||||
detail::throw_error(ec, "resume");
|
||||
}
|
||||
|
||||
void resume(error_code &ec)
|
||||
{
|
||||
if (pid_ <= 0)
|
||||
return ;
|
||||
if (::kill(pid_, SIGCONT) == -1)
|
||||
ec = get_last_error();
|
||||
}
|
||||
void terminate(native_exit_code_type &exit_status, error_code &ec)
|
||||
{
|
||||
if (pid_ <= 0)
|
||||
return;
|
||||
if (::kill(pid_, SIGKILL) == -1)
|
||||
ec = get_last_error();
|
||||
}
|
||||
|
||||
void terminate(native_exit_code_type &exit_status)
|
||||
{
|
||||
if (pid_ <= 0)
|
||||
return;
|
||||
error_code ec;
|
||||
terminate(exit_status, ec);
|
||||
if (ec)
|
||||
detail::throw_error(ec, "terminate");
|
||||
}
|
||||
|
||||
bool running(native_exit_code_type &exit_code, error_code & ec)
|
||||
{
|
||||
if (pid_ <= 0)
|
||||
return false;
|
||||
int code = 0;
|
||||
int res = ::waitpid(pid_, &code, WNOHANG);
|
||||
if (res == -1)
|
||||
ec = get_last_error();
|
||||
else if (res == 0)
|
||||
return true;
|
||||
else
|
||||
ec.clear();
|
||||
|
||||
if (res == 0)
|
||||
return true;
|
||||
else
|
||||
{
|
||||
exit_code = code;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool running(native_exit_code_type &exit_code)
|
||||
{
|
||||
if (pid_ <= 0)
|
||||
return false;
|
||||
|
||||
error_code ec;
|
||||
bool res = running(exit_code, ec);
|
||||
if (ec)
|
||||
detail::throw_error(ec, "is_running");
|
||||
return res;
|
||||
}
|
||||
|
||||
bool is_open() const
|
||||
{
|
||||
return pid_ != -1;
|
||||
}
|
||||
|
||||
template<BOOST_PROCESS_V2_COMPLETION_TOKEN_FOR(void(error_code, native_exit_code_type))
|
||||
WaitHandler BOOST_PROCESS_V2_DEFAULT_COMPLETION_TOKEN_TYPE(executor_type)>
|
||||
BOOST_PROCESS_V2_INITFN_AUTO_RESULT_TYPE(WaitHandler, void (error_code, native_exit_code_type))
|
||||
async_wait(WaitHandler &&handler BOOST_ASIO_DEFAULT_COMPLETION_TOKEN(executor_type))
|
||||
{
|
||||
return BOOST_PROCESS_V2_ASIO_NAMESPACE::async_compose<WaitHandler, void(error_code, native_exit_code_type)>(
|
||||
async_wait_op_{descriptor_, pid_}, handler, descriptor_);
|
||||
}
|
||||
|
||||
private:
|
||||
template<typename>
|
||||
friend
|
||||
struct basic_process_handle_fd;
|
||||
pid_type pid_ = -1;
|
||||
BOOST_PROCESS_V2_ASIO_NAMESPACE::posix::basic_stream_descriptor<Executor> descriptor_;
|
||||
|
||||
struct async_wait_op_
|
||||
{
|
||||
BOOST_PROCESS_V2_ASIO_NAMESPACE::posix::basic_descriptor<Executor> &descriptor;
|
||||
pid_type pid_;
|
||||
|
||||
template<typename Self>
|
||||
void operator()(Self &&self)
|
||||
{
|
||||
error_code ec;
|
||||
native_exit_code_type exit_code{};
|
||||
int wait_res = -1;
|
||||
if (pid_ <= 0) // error, complete early
|
||||
ec = BOOST_PROCESS_V2_ASIO_NAMESPACE::error::bad_descriptor;
|
||||
else
|
||||
{
|
||||
wait_res = ::waitpid(pid_, &exit_code, WNOHANG);
|
||||
if (wait_res == -1)
|
||||
ec = get_last_error();
|
||||
}
|
||||
|
||||
|
||||
if (!ec && (wait_res == 0))
|
||||
{
|
||||
descriptor.async_wait(
|
||||
BOOST_PROCESS_V2_ASIO_NAMESPACE::posix::descriptor_base::wait_read, std::move(self));
|
||||
return;
|
||||
}
|
||||
|
||||
struct completer
|
||||
{
|
||||
error_code ec;
|
||||
native_exit_code_type code;
|
||||
typename std::decay<Self>::type self;
|
||||
|
||||
void operator()()
|
||||
{
|
||||
self.complete(ec, code);
|
||||
}
|
||||
};
|
||||
BOOST_PROCESS_V2_ASIO_NAMESPACE::post(descriptor.get_executor(),
|
||||
completer{ec, exit_code, std::move(self)});
|
||||
|
||||
}
|
||||
|
||||
template<typename Self>
|
||||
void operator()(Self &&self, error_code ec, int = 0)
|
||||
{
|
||||
native_exit_code_type exit_code{};
|
||||
if (!ec)
|
||||
if (::waitpid(pid_, &exit_code, 0) == -1)
|
||||
ec = get_last_error();
|
||||
std::move(self).complete(ec, exit_code);
|
||||
}
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
BOOST_PROCESS_V2_END_NAMESPACE
|
||||
|
||||
#endif //BOOST_PROCESS_HANDLE_FD_OR_SIGNAL_HPP
|
||||
+383
@@ -0,0 +1,383 @@
|
||||
|
||||
// Copyright (c) 2022 Klemens D. Morgenstern
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
#ifndef BOOST_PROCESS_V2_DETAIL_PROCESS_HANDLE_FD_OR_SIGNAL_HPP
|
||||
#define BOOST_PROCESS_V2_DETAIL_PROCESS_HANDLE_FD_OR_SIGNAL_HPP
|
||||
|
||||
#include <boost/process/v2/detail/config.hpp>
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/wait.h>
|
||||
|
||||
#include <boost/process/v2/detail/last_error.hpp>
|
||||
#include <boost/process/v2/detail/throw_error.hpp>
|
||||
#include <boost/process/v2/exit_code.hpp>
|
||||
#include <boost/process/v2/pid.hpp>
|
||||
|
||||
#if defined(BOOST_PROCESS_V2_STANDALONE)
|
||||
#include <asio/any_io_executor.hpp>
|
||||
#include <asio/compose.hpp>
|
||||
#include <asio/dispatch.hpp>
|
||||
#include <asio/posix/basic_stream_descriptor.hpp>
|
||||
#include <asio/post.hpp>
|
||||
#include <asio/windows/signal_set.hpp>
|
||||
#else
|
||||
#include <boost/asio/any_io_executor.hpp>
|
||||
#include <boost/asio/compose.hpp>
|
||||
#include <boost/asio/dispatch.hpp>
|
||||
#include <boost/asio/posix/basic_stream_descriptor.hpp>
|
||||
#include <boost/asio/post.hpp>
|
||||
#include <boost/asio/signal_set.hpp>
|
||||
#endif
|
||||
|
||||
BOOST_PROCESS_V2_BEGIN_NAMESPACE
|
||||
|
||||
namespace detail
|
||||
{
|
||||
|
||||
template<typename Executor = BOOST_PROCESS_V2_ASIO_NAMESPACE::any_io_executor>
|
||||
struct basic_process_handle_fd_or_signal
|
||||
{
|
||||
using native_handle_type = int;
|
||||
|
||||
typedef Executor executor_type;
|
||||
|
||||
executor_type get_executor()
|
||||
{ return signal_set_.get_executor(); }
|
||||
|
||||
/// Rebinds the process_handle to another executor.
|
||||
template<typename Executor1>
|
||||
struct rebind_executor
|
||||
{
|
||||
/// The socket type when rebound to the specified executor.
|
||||
typedef basic_process_handle_fd_or_signal<Executor1> other;
|
||||
};
|
||||
|
||||
template<typename ExecutionContext>
|
||||
basic_process_handle_fd_or_signal(ExecutionContext &context,
|
||||
typename std::enable_if<
|
||||
std::is_convertible<ExecutionContext &,
|
||||
BOOST_PROCESS_V2_ASIO_NAMESPACE::execution_context &>::value
|
||||
>::type * = nullptr)
|
||||
: pid_(-1), descriptor_(context)
|
||||
{
|
||||
}
|
||||
|
||||
template<typename ExecutionContext>
|
||||
basic_process_handle_fd_or_signal(ExecutionContext &context,
|
||||
pid_type pid,
|
||||
typename std::enable_if<
|
||||
std::is_convertible<ExecutionContext &,
|
||||
BOOST_PROCESS_V2_ASIO_NAMESPACE::execution_context &>::value
|
||||
>::type * = nullptr)
|
||||
: pid_(pid), descriptor_(context)
|
||||
{
|
||||
}
|
||||
|
||||
template<typename ExecutionContext>
|
||||
basic_process_handle_fd_or_signal(ExecutionContext &context,
|
||||
pid_type pid, native_handle_type process_handle,
|
||||
typename std::enable_if<
|
||||
std::is_convertible<ExecutionContext &,
|
||||
BOOST_PROCESS_V2_ASIO_NAMESPACE::execution_context &>::value
|
||||
>::type * = nullptr)
|
||||
: pid_(pid), descriptor_(context, process_handle)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
basic_process_handle_fd_or_signal(Executor executor)
|
||||
: pid_(-1), descriptor_(executor)
|
||||
{
|
||||
}
|
||||
|
||||
basic_process_handle_fd_or_signal(Executor executor, pid_type pid)
|
||||
: pid_(pid), descriptor_(executor)
|
||||
{
|
||||
}
|
||||
|
||||
basic_process_handle_fd_or_signal(Executor executor, pid_type pid, native_handle_type process_handle)
|
||||
: pid_(pid), descriptor_(executor, process_handle)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
basic_process_handle_fd_or_signal(basic_process_handle_fd_or_signal &&handle)
|
||||
: pid_(handle.pid_), descriptor_(std::move(handle.descriptor_))
|
||||
{
|
||||
handle.pid_ = -1;
|
||||
}
|
||||
// Warn: does not change the executor of the signal-set.
|
||||
basic_process_handle_fd_or_signal& operator=(basic_process_handle_fd_or_signal &&handle)
|
||||
{
|
||||
pid_ = handle.pid_;
|
||||
descriptor_ = std::move(handle.descriptor_);
|
||||
handle.pid_ = -1;
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
||||
template<typename Executor1>
|
||||
basic_process_handle_fd_or_signal(basic_process_handle_fd_or_signal<Executor1> &&handle)
|
||||
: pid_(handle.pid_), descriptor_(std::move(handle.descriptor_))
|
||||
{
|
||||
handle.pid_ = -1;
|
||||
}
|
||||
|
||||
pid_type id() const
|
||||
{ return pid_; }
|
||||
native_handle_type native_handle() {return pid_;}
|
||||
|
||||
void terminate_if_running(error_code &)
|
||||
{
|
||||
if (pid_ <= 0)
|
||||
return;
|
||||
if (::waitpid(pid_, nullptr, WNOHANG) == 0)
|
||||
{
|
||||
::kill(pid_, SIGKILL);
|
||||
::waitpid(pid_, nullptr, 0);
|
||||
}
|
||||
}
|
||||
|
||||
void terminate_if_running()
|
||||
{
|
||||
if (pid_ <= 0)
|
||||
return;
|
||||
if (::waitpid(pid_, nullptr, WNOHANG) == 0)
|
||||
{
|
||||
::kill(pid_, SIGKILL);
|
||||
::waitpid(pid_, nullptr, 0);
|
||||
}
|
||||
}
|
||||
|
||||
void wait(native_exit_code_type &exit_status, error_code &ec)
|
||||
{
|
||||
if (pid_ <= 0)
|
||||
return;
|
||||
while (::waitpid(pid_, &exit_status, 0) < 0)
|
||||
{
|
||||
if (errno != EINTR)
|
||||
{
|
||||
ec = get_last_error();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void wait(native_exit_code_type &exit_status)
|
||||
{
|
||||
if (pid_ <= 0)
|
||||
return;
|
||||
error_code ec;
|
||||
wait(exit_status, ec);
|
||||
if (ec)
|
||||
detail::throw_error(ec, "wait(pid)");
|
||||
}
|
||||
|
||||
void interrupt(error_code &ec)
|
||||
{
|
||||
if (pid_ <= 0)
|
||||
return;
|
||||
if (::kill(pid_, SIGINT) == -1)
|
||||
ec = get_last_error();
|
||||
}
|
||||
|
||||
void interrupt()
|
||||
{
|
||||
if (pid_ <= 0)
|
||||
return;
|
||||
error_code ec;
|
||||
interrupt(ec);
|
||||
if (ec)
|
||||
detail::throw_error(ec, "interrupt");
|
||||
}
|
||||
|
||||
void request_exit(error_code &ec)
|
||||
{
|
||||
if (pid_ <= 0)
|
||||
return;
|
||||
if (::kill(pid_, SIGTERM) == -1)
|
||||
ec = get_last_error();
|
||||
}
|
||||
|
||||
void request_exit()
|
||||
{
|
||||
if (pid_ <= 0)
|
||||
return;
|
||||
error_code ec;
|
||||
request_exit(ec);
|
||||
if (ec)
|
||||
detail::throw_error(ec, "request_exit");
|
||||
}
|
||||
|
||||
void suspend()
|
||||
{
|
||||
if (pid_ <= 0)
|
||||
return ;
|
||||
error_code ec;
|
||||
suspend(ec);
|
||||
if (ec)
|
||||
detail::throw_error(ec, "suspend");
|
||||
}
|
||||
|
||||
void suspend(error_code &ec)
|
||||
{
|
||||
if (pid_ <= 0)
|
||||
return ;
|
||||
if (::kill(pid_, SIGSTOP) == -1)
|
||||
ec = get_last_error();
|
||||
}
|
||||
|
||||
void resume()
|
||||
{
|
||||
if (pid_ <= 0)
|
||||
return ;
|
||||
error_code ec;
|
||||
resume(ec);
|
||||
if (ec)
|
||||
detail::throw_error(ec, "resume");
|
||||
}
|
||||
|
||||
void resume(error_code &ec)
|
||||
{
|
||||
if (pid_ <= 0)
|
||||
return ;
|
||||
if (::kill(pid_, SIGCONT) == -1)
|
||||
ec = get_last_error();
|
||||
}
|
||||
|
||||
void terminate(native_exit_code_type &exit_status, error_code &ec)
|
||||
{
|
||||
if (pid_ <= 0)
|
||||
return;
|
||||
if (::kill(pid_, SIGKILL) == -1)
|
||||
ec = get_last_error();
|
||||
}
|
||||
|
||||
void terminate(native_exit_code_type &exit_status)
|
||||
{
|
||||
if (pid_ <= 0)
|
||||
return;
|
||||
error_code ec;
|
||||
terminate(exit_status, ec);
|
||||
if (ec)
|
||||
detail::throw_error(ec, "terminate");
|
||||
}
|
||||
|
||||
bool running(native_exit_code_type &exit_code, error_code & ec)
|
||||
{
|
||||
if (pid_ <= 0)
|
||||
return false;
|
||||
int code = 0;
|
||||
int res = ::waitpid(pid_, &code, WNOHANG);
|
||||
if (res == -1)
|
||||
ec = get_last_error();
|
||||
else
|
||||
ec.clear();
|
||||
|
||||
if (process_is_running(res))
|
||||
return true;
|
||||
else
|
||||
{
|
||||
exit_code = code;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool running(native_exit_code_type &exit_code)
|
||||
{
|
||||
if (pid_ <= 0)
|
||||
return false;
|
||||
|
||||
error_code ec;
|
||||
bool res = running(exit_code, ec);
|
||||
if (ec)
|
||||
detail::throw_error(ec, "is_running");
|
||||
return res;
|
||||
}
|
||||
|
||||
bool is_open() const
|
||||
{
|
||||
return pid_ != -1;
|
||||
}
|
||||
|
||||
template<BOOST_PROCESS_V2_COMPLETION_TOKEN_FOR(void(error_code, int))
|
||||
WaitHandler BOOST_PROCESS_V2_DEFAULT_COMPLETION_TOKEN_TYPE(executor_type)>
|
||||
BOOST_PROCESS_V2_INITFN_AUTO_RESULT_TYPE(WaitHandler, void (error_code, native_exit_code_type))
|
||||
async_wait(WaitHandler &&handler BOOST_ASIO_DEFAULT_COMPLETION_TOKEN(executor_type))
|
||||
{
|
||||
return BOOST_PROCESS_V2_ASIO_NAMESPACE::async_compose<WaitHandler, void(error_code, native_exit_code_type)>(
|
||||
async_wait_op_{descriptor_, signal_set_, pid_}, handler, descriptor_);
|
||||
}
|
||||
|
||||
private:
|
||||
template<typename>
|
||||
friend
|
||||
struct basic_process_handle_fd_or_signal;
|
||||
pid_type pid_ = -1;
|
||||
BOOST_PROCESS_V2_ASIO_NAMESPACE::posix::basic_stream_descriptor<Executor> descriptor_;
|
||||
BOOST_PROCESS_V2_ASIO_NAMESPACE::basic_signal_set<Executor> signal_set_{descriptor_.get_executor(), SIGCHLD};
|
||||
|
||||
struct async_wait_op_
|
||||
{
|
||||
BOOST_PROCESS_V2_ASIO_NAMESPACE::posix::basic_descriptor<Executor> &descriptor;
|
||||
BOOST_PROCESS_V2_ASIO_NAMESPACE::basic_signal_set<Executor> &handle;
|
||||
pid_type pid_;
|
||||
bool needs_post = true;
|
||||
|
||||
template<typename Self>
|
||||
void operator()(Self &&self, error_code ec = {}, int = 0)
|
||||
{
|
||||
native_exit_code_type exit_code{};
|
||||
int wait_res = -1;
|
||||
if (pid_ <= 0) // error, complete early
|
||||
ec = BOOST_PROCESS_V2_ASIO_NAMESPACE::error::bad_descriptor;
|
||||
else
|
||||
{
|
||||
wait_res = ::waitpid(pid_, &exit_code, WNOHANG);
|
||||
if (wait_res == -1)
|
||||
ec = get_last_error();
|
||||
}
|
||||
|
||||
if (!ec && (wait_res == 0))
|
||||
{
|
||||
needs_post = false;
|
||||
if (descriptor.is_open())
|
||||
descriptor.async_wait(
|
||||
BOOST_PROCESS_V2_ASIO_NAMESPACE::posix::descriptor_base::wait_read,
|
||||
std::move(self));
|
||||
else
|
||||
handle.async_wait(std::move(self));
|
||||
return;
|
||||
}
|
||||
|
||||
struct completer
|
||||
{
|
||||
error_code ec;
|
||||
native_exit_code_type code;
|
||||
typename std::decay<Self>::type self;
|
||||
|
||||
void operator()()
|
||||
{
|
||||
self.complete(ec, code);
|
||||
}
|
||||
};
|
||||
|
||||
const auto exec = self.get_executor();
|
||||
completer cpl{ec, exit_code, std::move(self)};
|
||||
if (needs_post)
|
||||
BOOST_PROCESS_V2_ASIO_NAMESPACE::post(exec, std::move(cpl));
|
||||
else
|
||||
BOOST_PROCESS_V2_ASIO_NAMESPACE::dispatch(exec, std::move(cpl));
|
||||
|
||||
}
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
BOOST_PROCESS_V2_END_NAMESPACE
|
||||
|
||||
#endif //BOOST_PROCESS_HANDLE_FD_OR_SIGNAL_HPP
|
||||
+350
@@ -0,0 +1,350 @@
|
||||
// Copyright (c) 2022 Klemens D. Morgenstern
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
#ifndef BOOST_PROCESS_V2_DETAIL_PROCESS_HANDLE_SIGNAL_HPP
|
||||
#define BOOST_PROCESS_V2_DETAIL_PROCESS_HANDLE_SIGNAL_HPP
|
||||
|
||||
#include <boost/process/v2/detail/config.hpp>
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/wait.h>
|
||||
|
||||
#include <boost/process/v2/detail/last_error.hpp>
|
||||
#include <boost/process/v2/detail/throw_error.hpp>
|
||||
#include <boost/process/v2/exit_code.hpp>
|
||||
#include <boost/process/v2/pid.hpp>
|
||||
|
||||
#if defined(BOOST_PROCESS_V2_STANDALONE)
|
||||
#include <asio/any_io_executor.hpp>
|
||||
#include <asio/compose.hpp>
|
||||
#include <asio/dispatch.hpp>
|
||||
#include <asio/post.hpp>
|
||||
#include <asio/signal_set.hpp>
|
||||
#else
|
||||
#include <boost/asio/any_io_executor.hpp>
|
||||
#include <boost/asio/compose.hpp>
|
||||
#include <boost/asio/dispatch.hpp>
|
||||
#include <boost/asio/post.hpp>
|
||||
#include <boost/asio/signal_set.hpp>
|
||||
#endif
|
||||
|
||||
BOOST_PROCESS_V2_BEGIN_NAMESPACE
|
||||
|
||||
namespace detail
|
||||
{
|
||||
|
||||
template<typename Executor = BOOST_PROCESS_V2_ASIO_NAMESPACE::any_io_executor>
|
||||
struct basic_process_handle_signal
|
||||
{
|
||||
struct native_handle_type
|
||||
{
|
||||
native_handle_type() = delete;
|
||||
native_handle_type(const native_handle_type & ) = delete;
|
||||
~native_handle_type() = default;
|
||||
};
|
||||
|
||||
typedef Executor executor_type;
|
||||
|
||||
executor_type get_executor()
|
||||
{ return signal_set_.get_executor(); }
|
||||
|
||||
/// Rebinds the process_handle to another executor.
|
||||
template<typename Executor1>
|
||||
struct rebind_executor
|
||||
{
|
||||
/// The socket type when rebound to the specified executor.
|
||||
typedef basic_process_handle_signal<Executor1> other;
|
||||
};
|
||||
|
||||
template<typename ExecutionContext>
|
||||
basic_process_handle_signal(ExecutionContext &context,
|
||||
typename std::enable_if<
|
||||
std::is_convertible<ExecutionContext &,
|
||||
BOOST_PROCESS_V2_ASIO_NAMESPACE::execution_context &>::value
|
||||
>::type * = nullptr)
|
||||
: pid_(-1), signal_set_(context, SIGCHLD)
|
||||
{
|
||||
}
|
||||
|
||||
basic_process_handle_signal(Executor executor)
|
||||
: pid_(-1), signal_set_(executor, SIGCHLD)
|
||||
{
|
||||
}
|
||||
|
||||
basic_process_handle_signal(Executor executor, pid_type pid)
|
||||
: pid_(pid), signal_set_(executor, SIGCHLD)
|
||||
{
|
||||
}
|
||||
|
||||
basic_process_handle_signal(basic_process_handle_signal && handle)
|
||||
: pid_(handle.pid_), signal_set_(handle.signal_set_.get_executor(), SIGCHLD)
|
||||
{
|
||||
handle.pid_ = -1;
|
||||
}
|
||||
|
||||
basic_process_handle_signal& operator=(basic_process_handle_signal && handle)
|
||||
{
|
||||
pid_ = handle.id();
|
||||
signal_set_.~basic_signal_set();
|
||||
using ss = BOOST_PROCESS_V2_ASIO_NAMESPACE::basic_signal_set<Executor>;
|
||||
new (&signal_set_) ss(handle.get_executor(), SIGCHLD);
|
||||
handle.pid_ = -1;
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
||||
template<typename Executor1>
|
||||
basic_process_handle_signal(basic_process_handle_signal<Executor1> && handle)
|
||||
: pid_(handle.pid_), signal_set_(Executor1(handle.signal_set_.get_executor()), SIGCHLD)
|
||||
{
|
||||
handle.pid_ = -1;
|
||||
}
|
||||
|
||||
pid_type id() const { return pid_; }
|
||||
native_handle_type native_handle() {return {};}
|
||||
|
||||
void terminate_if_running(error_code &)
|
||||
{
|
||||
terminate_if_running();
|
||||
}
|
||||
|
||||
void terminate_if_running()
|
||||
{
|
||||
if (pid_ <= 0)
|
||||
return;
|
||||
if (::waitpid(pid_, nullptr, WNOHANG) == 0)
|
||||
{
|
||||
::kill(pid_, SIGKILL);
|
||||
::waitpid(pid_, nullptr, 0);
|
||||
}
|
||||
}
|
||||
|
||||
void wait(native_exit_code_type &exit_status, error_code &ec)
|
||||
{
|
||||
if (pid_ <= 0)
|
||||
return;
|
||||
while (::waitpid(pid_, &exit_status, 0) < 0)
|
||||
{
|
||||
if (errno != EINTR)
|
||||
{
|
||||
ec = get_last_error();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void wait(native_exit_code_type &exit_status)
|
||||
{
|
||||
if (pid_ <= 0)
|
||||
return;
|
||||
error_code ec;
|
||||
wait(exit_status, ec);
|
||||
if (ec)
|
||||
detail::throw_error(ec, "wait(pid)");
|
||||
}
|
||||
|
||||
void interrupt(error_code &ec)
|
||||
{
|
||||
if (pid_ <= 0)
|
||||
return;
|
||||
if (::kill(pid_, SIGTERM) == -1)
|
||||
ec = get_last_error();
|
||||
}
|
||||
|
||||
void interrupt()
|
||||
{
|
||||
if (pid_ <= 0)
|
||||
return;
|
||||
error_code ec;
|
||||
interrupt(ec);
|
||||
if (ec)
|
||||
detail::throw_error(ec, "interrupt");
|
||||
}
|
||||
|
||||
void request_exit(error_code &ec)
|
||||
{
|
||||
if (pid_ <= 0)
|
||||
return;
|
||||
if (::kill(pid_, SIGTERM) == -1)
|
||||
ec = get_last_error();
|
||||
}
|
||||
|
||||
void request_exit()
|
||||
{
|
||||
if (pid_ <= 0)
|
||||
return;
|
||||
error_code ec;
|
||||
request_exit(ec);
|
||||
if (ec)
|
||||
detail::throw_error(ec, "request_exit");
|
||||
}
|
||||
|
||||
void suspend()
|
||||
{
|
||||
if (pid_ <= 0)
|
||||
return;
|
||||
error_code ec;
|
||||
suspend(ec);
|
||||
if (ec)
|
||||
detail::throw_error(ec, "suspend");
|
||||
}
|
||||
|
||||
void suspend(error_code &ec)
|
||||
{
|
||||
if (pid_ <= 0)
|
||||
return;
|
||||
if (::kill(pid_, SIGCONT) == -1)
|
||||
ec = get_last_error();
|
||||
}
|
||||
|
||||
void resume()
|
||||
{
|
||||
if (pid_ <= 0)
|
||||
return;
|
||||
error_code ec;
|
||||
resume(ec);
|
||||
if (ec)
|
||||
detail::throw_error(ec, "resume");
|
||||
}
|
||||
|
||||
void resume(error_code &ec)
|
||||
{
|
||||
if (pid_ <= 0)
|
||||
return;
|
||||
if (::kill(pid_, SIGTERM) == -1)
|
||||
ec = get_last_error();
|
||||
}
|
||||
|
||||
void terminate(native_exit_code_type &exit_status, error_code &ec)
|
||||
{
|
||||
if (pid_ <= 0)
|
||||
return;
|
||||
if (::kill(pid_, SIGKILL) == -1)
|
||||
ec = get_last_error();
|
||||
}
|
||||
|
||||
void terminate(native_exit_code_type &exit_status)
|
||||
{
|
||||
if (pid_ <= 0)
|
||||
return;
|
||||
error_code ec;
|
||||
terminate(exit_status, ec);
|
||||
if (ec)
|
||||
detail::throw_error(ec, "terminate");
|
||||
}
|
||||
|
||||
bool running(native_exit_code_type &exit_code, error_code & ec)
|
||||
{
|
||||
if (pid_ <= 0)
|
||||
return false;
|
||||
int code = 0;
|
||||
int res = ::waitpid(pid_, &code, WNOHANG);
|
||||
if (res == -1)
|
||||
ec = get_last_error();
|
||||
|
||||
if (res == 0)
|
||||
return true;
|
||||
else
|
||||
{
|
||||
exit_code = code;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool running(native_exit_code_type &exit_code)
|
||||
{
|
||||
if (pid_ <= 0)
|
||||
return false;
|
||||
|
||||
error_code ec;
|
||||
bool res = running(exit_code, ec);
|
||||
if (ec)
|
||||
detail::throw_error(ec, "is_running");
|
||||
return res;
|
||||
}
|
||||
|
||||
bool is_open() const
|
||||
{
|
||||
return pid_ != -1;
|
||||
}
|
||||
|
||||
template<BOOST_PROCESS_V2_COMPLETION_TOKEN_FOR(void(error_code, int))
|
||||
WaitHandler BOOST_PROCESS_V2_DEFAULT_COMPLETION_TOKEN_TYPE(executor_type)>
|
||||
BOOST_PROCESS_V2_INITFN_AUTO_RESULT_TYPE(WaitHandler, void (error_code, native_exit_code_type))
|
||||
async_wait(WaitHandler &&handler BOOST_ASIO_DEFAULT_COMPLETION_TOKEN(executor_type))
|
||||
{
|
||||
return BOOST_PROCESS_V2_ASIO_NAMESPACE::async_compose<WaitHandler, void(error_code, native_exit_code_type)>(
|
||||
async_wait_op_{signal_set_, pid_}, handler, signal_set_);
|
||||
}
|
||||
|
||||
private:
|
||||
template<typename>
|
||||
friend struct basic_process_handle_signal;
|
||||
pid_type pid_ = -1;
|
||||
BOOST_PROCESS_V2_ASIO_NAMESPACE::basic_signal_set<Executor> signal_set_;
|
||||
|
||||
struct async_wait_op_
|
||||
{
|
||||
BOOST_PROCESS_V2_ASIO_NAMESPACE::basic_signal_set<Executor> &handle;
|
||||
pid_type pid_;
|
||||
|
||||
template<typename Self>
|
||||
void operator()(Self &&self)
|
||||
{
|
||||
handle.async_wait(std::move(self));
|
||||
handle.cancel();
|
||||
// we cancel so we end up on the signal-sets executor
|
||||
}
|
||||
|
||||
template<typename Self>
|
||||
void operator()(Self &&self, error_code ec, int sig)
|
||||
{
|
||||
if (ec == BOOST_PROCESS_V2_ASIO_NAMESPACE::error::operation_aborted &&
|
||||
self.get_cancellation_state().cancelled()
|
||||
== BOOST_PROCESS_V2_ASIO_NAMESPACE::cancellation_type::none)
|
||||
ec.clear();
|
||||
|
||||
native_exit_code_type exit_code = -1;
|
||||
int wait_res = -1;
|
||||
|
||||
if (pid_ <= 0) // error, complete early
|
||||
ec = BOOST_PROCESS_V2_ASIO_NAMESPACE::error::bad_descriptor;
|
||||
else if (!ec)
|
||||
{
|
||||
wait_res = ::waitpid(pid_, &exit_code, WNOHANG);
|
||||
if (wait_res == -1)
|
||||
ec = get_last_error();
|
||||
}
|
||||
|
||||
if (!ec && (wait_res == 0))
|
||||
{
|
||||
handle.async_wait(std::move(self));
|
||||
return;
|
||||
}
|
||||
|
||||
struct completer
|
||||
{
|
||||
error_code ec;
|
||||
native_exit_code_type code;
|
||||
typename std::decay<Self>::type self;
|
||||
|
||||
void operator()()
|
||||
{
|
||||
self.complete(ec, code);
|
||||
}
|
||||
};
|
||||
|
||||
const auto exec = self.get_executor();
|
||||
BOOST_PROCESS_V2_ASIO_NAMESPACE::dispatch(exec, completer{ec, exit_code, std::move(self)});
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
|
||||
BOOST_PROCESS_V2_END_NAMESPACE
|
||||
|
||||
|
||||
#endif //BOOST_PROCESS_V2_DETAIL_PROCESS_HANDLE_SIGNAL_HPP
|
||||
+305
@@ -0,0 +1,305 @@
|
||||
// Copyright (c) 2022 Klemens D. Morgenstern
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
#ifndef BOOST_PROCESS_V2_DETAIL_PROCESS_HANDLE_WINDOWS_HPP
|
||||
#define BOOST_PROCESS_V2_DETAIL_PROCESS_HANDLE_WINDOWS_HPP
|
||||
|
||||
#include <boost/process/v2/detail/config.hpp>
|
||||
|
||||
#include <boost/process/v2/exit_code.hpp>
|
||||
#include <boost/process/v2/pid.hpp>
|
||||
#include <boost/process/v2/detail/throw_error.hpp>
|
||||
|
||||
#if defined(BOOST_PROCESS_V2_STANDALONE)
|
||||
#include <asio/any_io_executor.hpp>
|
||||
#include <asio/compose.hpp>
|
||||
#include <asio/windows/basic_object_handle.hpp>
|
||||
#else
|
||||
#include <boost/asio/any_io_executor.hpp>
|
||||
#include <boost/asio/compose.hpp>
|
||||
#include <boost/asio/windows/basic_object_handle.hpp>
|
||||
#endif
|
||||
|
||||
BOOST_PROCESS_V2_BEGIN_NAMESPACE
|
||||
|
||||
namespace detail
|
||||
{
|
||||
|
||||
BOOST_PROCESS_V2_DECL void get_exit_code_( void * handle, native_exit_code_type & exit_code, error_code & ec);
|
||||
BOOST_PROCESS_V2_DECL void * open_process_(pid_type pid);
|
||||
BOOST_PROCESS_V2_DECL void terminate_if_running_(void * handle);
|
||||
BOOST_PROCESS_V2_DECL bool check_handle_(void* handle, error_code & ec);
|
||||
BOOST_PROCESS_V2_DECL bool check_pid_(pid_type pid_, error_code & ec);
|
||||
BOOST_PROCESS_V2_DECL void interrupt_(pid_type pid_, error_code & ec);
|
||||
BOOST_PROCESS_V2_DECL void suspend_(void * handle, error_code & ec);
|
||||
BOOST_PROCESS_V2_DECL void resume_(void * handle, error_code & ec);
|
||||
BOOST_PROCESS_V2_DECL void terminate_(void * handle, error_code & ec, native_exit_code_type & exit_code);
|
||||
BOOST_PROCESS_V2_DECL void request_exit_(pid_type pid_, error_code & ec);
|
||||
BOOST_PROCESS_V2_DECL void check_running_(void* handle, error_code & ec, native_exit_code_type & exit_status);
|
||||
|
||||
template<typename Executor = BOOST_PROCESS_V2_ASIO_NAMESPACE::any_io_executor>
|
||||
struct basic_process_handle_win
|
||||
{
|
||||
typedef BOOST_PROCESS_V2_ASIO_NAMESPACE::windows::basic_object_handle<Executor> handle_type;
|
||||
typedef typename handle_type::native_handle_type native_handle_type;
|
||||
|
||||
typedef Executor executor_type;
|
||||
|
||||
executor_type get_executor()
|
||||
{ return handle_.get_executor(); }
|
||||
|
||||
/// Rebinds the process_handle to another executor.
|
||||
template<typename Executor1>
|
||||
struct rebind_executor
|
||||
{
|
||||
/// The socket type when rebound to the specified executor.
|
||||
typedef basic_process_handle_win<Executor1> other;
|
||||
};
|
||||
|
||||
template<typename ExecutionContext>
|
||||
basic_process_handle_win(ExecutionContext &context,
|
||||
typename std::enable_if<
|
||||
std::is_convertible<ExecutionContext &,
|
||||
BOOST_PROCESS_V2_ASIO_NAMESPACE::execution_context &>::value
|
||||
>::type = 0)
|
||||
: pid_(0), handle_(context)
|
||||
{
|
||||
}
|
||||
|
||||
basic_process_handle_win(Executor executor)
|
||||
: pid_(0), handle_(executor)
|
||||
{
|
||||
}
|
||||
|
||||
basic_process_handle_win(Executor executor, pid_type pid)
|
||||
: pid_(pid), handle_(executor, detail::open_process_(pid))
|
||||
{
|
||||
}
|
||||
|
||||
basic_process_handle_win(Executor executor, pid_type pid, native_handle_type process_handle)
|
||||
: pid_(pid), handle_(executor, process_handle)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
template<typename Executor1>
|
||||
basic_process_handle_win(basic_process_handle_win<Executor1> && handle)
|
||||
: pid_(handle.pid_), handle_(std::move(handle.handle_))
|
||||
{
|
||||
}
|
||||
|
||||
basic_process_handle_win(basic_process_handle_win && handle)
|
||||
: pid_(handle.id()), handle_(std::move(handle.handle_))
|
||||
{
|
||||
handle.pid_ = static_cast<DWORD>(-1);
|
||||
}
|
||||
|
||||
basic_process_handle_win& operator=(basic_process_handle_win && handle)
|
||||
{
|
||||
pid_ = handle.pid_;
|
||||
handle_ = std::move(handle.handle_);
|
||||
handle.pid_ = static_cast<DWORD>(-1);
|
||||
return *this;
|
||||
}
|
||||
|
||||
~basic_process_handle_win()
|
||||
{
|
||||
if (handle_.is_open())
|
||||
{
|
||||
error_code ec;
|
||||
handle_.close(ec);
|
||||
}
|
||||
}
|
||||
|
||||
native_handle_type native_handle()
|
||||
{ return handle_.native_handle(); }
|
||||
|
||||
pid_type id() const
|
||||
{ return pid_; }
|
||||
|
||||
void terminate_if_running(error_code &)
|
||||
{
|
||||
detail::terminate_if_running_(handle_.native_handle());
|
||||
}
|
||||
|
||||
void terminate_if_running()
|
||||
{
|
||||
detail::terminate_if_running_(handle_.native_handle());
|
||||
}
|
||||
|
||||
void wait(native_exit_code_type &exit_status, error_code &ec)
|
||||
{
|
||||
if (!detail::check_handle_(handle_.native_handle(), ec))
|
||||
return;
|
||||
|
||||
handle_.wait(ec);
|
||||
if (!ec)
|
||||
detail::get_exit_code_(handle_.native_handle(), exit_status, ec);
|
||||
}
|
||||
|
||||
|
||||
void wait(native_exit_code_type &exit_status)
|
||||
{
|
||||
error_code ec;
|
||||
wait(exit_status, ec);
|
||||
if (ec)
|
||||
detail::throw_error(ec, "wait(pid)");
|
||||
}
|
||||
|
||||
void interrupt(error_code &ec)
|
||||
{
|
||||
if (!detail::check_pid_(pid_, ec))
|
||||
return;
|
||||
|
||||
detail::interrupt_(pid_, ec);
|
||||
}
|
||||
|
||||
void interrupt()
|
||||
{
|
||||
error_code ec;
|
||||
interrupt(ec);
|
||||
if (ec)
|
||||
detail::throw_error(ec, "interrupt");
|
||||
}
|
||||
|
||||
void request_exit(error_code &ec)
|
||||
{
|
||||
if (!detail::check_pid_(pid_, ec))
|
||||
return;
|
||||
detail::request_exit_(pid_, ec);
|
||||
}
|
||||
|
||||
void request_exit()
|
||||
{
|
||||
error_code ec;
|
||||
request_exit(ec);
|
||||
if (ec)
|
||||
detail::throw_error(ec, "request_exit");
|
||||
}
|
||||
|
||||
void suspend(error_code &ec)
|
||||
{
|
||||
detail::suspend_(handle_.native_handle(), ec);
|
||||
}
|
||||
|
||||
void suspend()
|
||||
{
|
||||
error_code ec;
|
||||
suspend(ec);
|
||||
if (ec)
|
||||
detail::throw_error(ec, "suspend");
|
||||
}
|
||||
|
||||
void resume(error_code &ec)
|
||||
{
|
||||
detail::resume_(handle_.native_handle(), ec);
|
||||
}
|
||||
|
||||
void resume()
|
||||
{
|
||||
error_code ec;
|
||||
suspend(ec);
|
||||
if (ec)
|
||||
detail::throw_error(ec, "resume");
|
||||
}
|
||||
|
||||
void terminate(native_exit_code_type &exit_status, error_code &ec)
|
||||
{
|
||||
if (!detail::check_handle_(handle_.native_handle(), ec))
|
||||
return;
|
||||
|
||||
detail::terminate_(handle_.native_handle(), ec, exit_status);
|
||||
if (!ec)
|
||||
wait(exit_status, ec);
|
||||
|
||||
}
|
||||
|
||||
void terminate(native_exit_code_type &exit_status)
|
||||
{
|
||||
error_code ec;
|
||||
terminate(exit_status, ec);
|
||||
if (ec)
|
||||
detail::throw_error(ec, "terminate");
|
||||
}
|
||||
|
||||
bool running(native_exit_code_type &exit_code, error_code & ec)
|
||||
{
|
||||
if (!detail::check_handle_(handle_.native_handle(), ec))
|
||||
return false;
|
||||
|
||||
native_exit_code_type code;
|
||||
//single value, not needed in the winapi.
|
||||
detail::check_running_(handle_.native_handle(), ec, code);
|
||||
if (ec)
|
||||
return false;
|
||||
|
||||
if (process_is_running(code))
|
||||
return true;
|
||||
else
|
||||
{
|
||||
exit_code = code;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool running(native_exit_code_type &exit_code)
|
||||
{
|
||||
error_code ec;
|
||||
bool res = running(exit_code, ec);
|
||||
if (ec)
|
||||
detail::throw_error(ec, "is_running");
|
||||
return res;
|
||||
}
|
||||
|
||||
bool is_open() const
|
||||
{
|
||||
return handle_.is_open();
|
||||
}
|
||||
|
||||
template<BOOST_PROCESS_V2_COMPLETION_TOKEN_FOR(void(error_code, native_exit_code_type))
|
||||
WaitHandler BOOST_PROCESS_V2_DEFAULT_COMPLETION_TOKEN_TYPE(executor_type)>
|
||||
BOOST_PROCESS_V2_INITFN_AUTO_RESULT_TYPE(WaitHandler, void (error_code, native_exit_code_type))
|
||||
async_wait(WaitHandler &&handler BOOST_ASIO_DEFAULT_COMPLETION_TOKEN(executor_type))
|
||||
{
|
||||
return BOOST_PROCESS_V2_ASIO_NAMESPACE::async_compose<WaitHandler, void(error_code, native_exit_code_type)>(
|
||||
async_wait_op_{handle_}, handler, handle_
|
||||
);
|
||||
}
|
||||
template<typename>
|
||||
friend struct basic_process_handle_win;
|
||||
private:
|
||||
pid_type pid_;
|
||||
handle_type handle_;
|
||||
|
||||
struct async_wait_op_
|
||||
{
|
||||
handle_type &handle;
|
||||
|
||||
template<typename Self>
|
||||
void operator()(Self &&self)
|
||||
{
|
||||
handle.async_wait(std::move(self));
|
||||
}
|
||||
|
||||
template<typename Self>
|
||||
void operator()(Self &&self, error_code ec)
|
||||
{
|
||||
native_exit_code_type exit_code{};
|
||||
if (!ec)
|
||||
detail::get_exit_code_(handle.native_handle(), exit_code, ec);
|
||||
std::move(self).complete(ec, exit_code);
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
#if !defined(BOOST_PROCESS_V2_HEADER_ONLY)
|
||||
extern template struct basic_process_handle_win<>;
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
BOOST_PROCESS_V2_END_NAMESPACE
|
||||
|
||||
|
||||
#endif //BOOST_PROCESS_V2_DETAIL_PROCESS_HANDLE_WINDOWS_HPP
|
||||
+38
@@ -0,0 +1,38 @@
|
||||
// Copyright (c) 2022 Klemens D. Morgenstern
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
#ifndef BOOST_PROCESS_V2_DETAIL_THROW_ERROR_HPP
|
||||
#define BOOST_PROCESS_V2_DETAIL_THROW_ERROR_HPP
|
||||
|
||||
#include <boost/process/v2/detail/config.hpp>
|
||||
|
||||
BOOST_PROCESS_V2_BEGIN_NAMESPACE
|
||||
namespace detail
|
||||
{
|
||||
|
||||
BOOST_PROCESS_V2_DECL void do_throw_error(const error_code& err);
|
||||
BOOST_PROCESS_V2_DECL void do_throw_error(const error_code& err, const char* location);
|
||||
|
||||
inline void throw_error(const error_code& err)
|
||||
{
|
||||
if (err)
|
||||
do_throw_error(err);
|
||||
}
|
||||
|
||||
inline void throw_error(const error_code& err, const char* location)
|
||||
{
|
||||
if (err)
|
||||
do_throw_error(err, location);
|
||||
}
|
||||
|
||||
}
|
||||
BOOST_PROCESS_V2_END_NAMESPACE
|
||||
|
||||
#if defined(BOOST_PROCESS_V2_HEADER_ONLY)
|
||||
|
||||
#include <boost/process/v2/detail/impl/throw_error.ipp>
|
||||
|
||||
#endif
|
||||
|
||||
#endif //BOOST_PROCESS_V2_DETAIL_THROW_ERROR_HPP
|
||||
+39
@@ -0,0 +1,39 @@
|
||||
// Copyright (c) 2022 Klemens D. Morgenstern
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
#ifndef BOOST_PROCESS_V2_DETAIL_THROW_EXCEPTION_HPP
|
||||
#define BOOST_PROCESS_V2_DETAIL_THROW_EXCEPTION_HPP
|
||||
|
||||
#include <boost/process/v2/detail/config.hpp>
|
||||
|
||||
#if !defined(BOOST_PROCESS_V2_STANDALONE)
|
||||
|
||||
#include <boost/throw_exception.hpp>
|
||||
|
||||
#endif
|
||||
|
||||
BOOST_PROCESS_V2_BEGIN_NAMESPACE
|
||||
|
||||
namespace detail
|
||||
{
|
||||
|
||||
#if defined(BOOST_PROCESS_V2_STANDALONE)
|
||||
|
||||
template <typename Exception>
|
||||
inline void throw_exception(const Exception& e)
|
||||
{
|
||||
throw e;
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
using boost::throw_exception;
|
||||
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
BOOST_PROCESS_V2_END_NAMESPACE
|
||||
|
||||
#endif //BOOST_PROCESS_V2_DETAIL_THROW_EXCEPTION_HPP
|
||||
+95
@@ -0,0 +1,95 @@
|
||||
// Copyright (c) 2022 Klemens D. Morgenstern
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
#ifndef BOOST_PROCESS_V2_DETAIL_UTF8_HPP
|
||||
#define BOOST_PROCESS_V2_DETAIL_UTF8_HPP
|
||||
|
||||
#include <boost/process/v2/detail/config.hpp>
|
||||
#include <boost/process/v2/detail/throw_error.hpp>
|
||||
|
||||
BOOST_PROCESS_V2_BEGIN_NAMESPACE
|
||||
|
||||
namespace detail
|
||||
{
|
||||
|
||||
BOOST_PROCESS_V2_DECL std::size_t size_as_utf8(const wchar_t * in, std::size_t size, error_code & ec);
|
||||
BOOST_PROCESS_V2_DECL std::size_t size_as_wide(const char * in, std::size_t size, error_code & ec);
|
||||
|
||||
BOOST_PROCESS_V2_DECL std::size_t convert_to_utf8(const wchar_t * in, std::size_t size,
|
||||
char * out, std::size_t max_size, error_code & ec);
|
||||
BOOST_PROCESS_V2_DECL std::size_t convert_to_wide(const char * in, std::size_t size,
|
||||
wchar_t * out, std::size_t max_size, error_code & ec);
|
||||
|
||||
template<typename CharOut, typename Traits = std::char_traits<CharOut>,
|
||||
typename Allocator = std::allocator<CharOut>, typename CharIn,
|
||||
typename = typename std::enable_if<std::is_same<CharOut, CharIn>::value>::type>
|
||||
BOOST_PROCESS_V2_DECL
|
||||
std::basic_string<CharOut, Traits, Allocator> conv_string(
|
||||
const CharIn * data, std::size_t size,
|
||||
const Allocator allocator = Allocator{})
|
||||
{
|
||||
return std::basic_string<CharOut, Traits, Allocator>(data, size, allocator);
|
||||
}
|
||||
|
||||
|
||||
template<typename CharOut, typename Traits = std::char_traits<CharOut>,
|
||||
typename Allocator = std::allocator<CharOut>,
|
||||
typename = typename std::enable_if<std::is_same<CharOut, char>::value>::type>
|
||||
BOOST_PROCESS_V2_DECL
|
||||
std::basic_string<CharOut, Traits, Allocator> conv_string(
|
||||
const wchar_t * data, std::size_t size,
|
||||
const Allocator allocator = Allocator{})
|
||||
{
|
||||
error_code ec;
|
||||
const auto req_size = size_as_utf8(data, size, ec);
|
||||
if (ec)
|
||||
detail::throw_error(ec, "size_as_utf8");
|
||||
|
||||
std::basic_string<CharOut, Traits, Allocator> res(allocator);
|
||||
res.resize(req_size);
|
||||
|
||||
auto res_size = convert_to_utf8(data, size, &res.front(), req_size, ec);
|
||||
if (ec)
|
||||
detail::throw_error(ec, "convert_to_utf8");
|
||||
|
||||
res.resize(res_size);
|
||||
return res;
|
||||
}
|
||||
|
||||
template<typename CharOut, typename Traits = std::char_traits<CharOut>,
|
||||
typename Allocator = std::allocator<CharOut>,
|
||||
typename = typename std::enable_if<std::is_same<CharOut, wchar_t>::value>::type>
|
||||
BOOST_PROCESS_V2_DECL
|
||||
std::basic_string<CharOut, Traits, Allocator> conv_string(
|
||||
const char * data, std::size_t size,
|
||||
const Allocator allocator = Allocator{})
|
||||
{
|
||||
error_code ec;
|
||||
const auto req_size = size_as_wide(data, size, ec);
|
||||
if (ec)
|
||||
detail::throw_error(ec, "size_as_wide");
|
||||
|
||||
std::basic_string<CharOut, Traits, Allocator> res(allocator);
|
||||
res.resize(req_size);
|
||||
|
||||
auto res_size = convert_to_wide(data, size, &res.front(), req_size, ec);
|
||||
if (ec)
|
||||
detail::throw_error(ec, "convert_to_wide");
|
||||
|
||||
res.resize(res_size);
|
||||
return res;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
BOOST_PROCESS_V2_END_NAMESPACE
|
||||
|
||||
#if defined(BOOST_PROCESS_V2_HEADER_ONLY)
|
||||
|
||||
#include <boost/process/v2/detail/impl/utf8.ipp>
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
#endif //BOOST_PROCESS_V2_DETAIL_UTF8_HPP
|
||||
Reference in New Issue
Block a user