2
0
mirror of https://github.com/boostorg/process.git synced 2026-01-20 16:52:14 +00:00

Compare commits

..

1 Commits

Author SHA1 Message Date
Klemens Morgenstern
dd60cc1464 added test for special args to tests 2025-07-21 21:08:46 +08:00
25 changed files with 80 additions and 256 deletions

View File

@@ -3,7 +3,7 @@
# Distributed under the Boost Software License, Version 1.0. # Distributed under the Boost Software License, Version 1.0.
# https://www.boost.org/LICENSE_1_0.txt # https://www.boost.org/LICENSE_1_0.txt
cmake_minimum_required(VERSION 3.8...3.31) cmake_minimum_required(VERSION 3.5...3.16)
project(boost_process VERSION "${BOOST_SUPERPROJECT_VERSION}" LANGUAGES CXX) project(boost_process VERSION "${BOOST_SUPERPROJECT_VERSION}" LANGUAGES CXX)

View File

@@ -6,8 +6,8 @@ Boost.process is a library for comfortable management of processes, released wit
| Branches | Linux / Windows | Code coverage | Matrix | | Branches | Linux / Windows | Code coverage | Matrix |
|----------|----------------------------------------------------------------------------------------------------------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------| |----------|----------------------------------------------------------------------------------------------------------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------|
| Develop: | [![Build Status](https://drone.cpp.al/api/badges/boostorg/process/status.svg)](https://drone.cpp.al/boostorg/process) | [![codecov](https://codecov.io/gh/boostorg/process/branch/develop/graph/badge.svg?token=AhunMqTSpA)](https://codecov.io/gh/boostorg/process) | [![Matrix](https://img.shields.io/badge/matrix-develop-lightgray.svg)](https://regression.boost.io/develop/developer/process.html) | | Develop: | [![Build Status](https://drone.cpp.al/api/badges/boostorg/process/status.svg)](https://drone.cpp.al/boostorg/process) | [![codecov](https://codecov.io/gh/boostorg/process/branch/develop/graph/badge.svg?token=AhunMqTSpA)](https://codecov.io/gh/boostorg/process) | [![Matrix](https://img.shields.io/badge/matrix-develop-lightgray.svg)](http://www.boost.org/development/tests/develop/developer/process.html) |
| Master: | [![Build Status](https://drone.cpp.al/api/badges/boostorg/process/status.svg?ref=refs/heads/develop)](https://drone.cpp.al/boostorg/process) | [![codecov](https://codecov.io/gh/boostorg/process/branch/master/graph/badge.svg?token=AhunMqTSpA)](https://codecov.io/gh/boostorg/process) | [![Matrix](https://img.shields.io/badge/matrix-master-lightgray.svg)](https://regression.boost.io/master/developer/process.html) | | Master: | [![Build Status](https://drone.cpp.al/api/badges/boostorg/process/status.svg?ref=refs/heads/develop)](https://drone.cpp.al/boostorg/process) | [![codecov](https://codecov.io/gh/boostorg/process/branch/master/graph/badge.svg?token=AhunMqTSpA)](https://codecov.io/gh/boostorg/process) | [![Matrix](https://img.shields.io/badge/matrix-master-lightgray.svg)](http://www.boost.org/development/tests/master/developer/process.html) |

View File

@@ -25,7 +25,7 @@ include::../example/env.cpp[tag=current_env]
The subprocess environment assignment follows the same constraints: The subprocess environment assignment follows the same constraints:
.example/env.cpp:34-42 .example/env.cpp:34-42
[source,cpp,indent=0] [source,cpp,ident=0]
---- ----
include::../example/env.cpp[tag=subprocess_env] include::../example/env.cpp[tag=subprocess_env]
---- ----
@@ -36,7 +36,7 @@ The current environment can be obtained by calling `environment::current` which
a forward range of `environment::key_value_pair_view`. a forward range of `environment::key_value_pair_view`.
.example/env.cpp:48-54 .example/env.cpp:48-54
[source,cpp,indent=0] [source,cpp,ident=0]
---- ----
include::../example/env.cpp[tag=vector_env] include::../example/env.cpp[tag=vector_env]
---- ----
@@ -44,7 +44,7 @@ include::../example/env.cpp[tag=vector_env]
Alternatively you can use a map container for the environment. Alternatively you can use a map container for the environment.
.example/env.cpp:61-68 .example/env.cpp:61-68
[source,cpp,indent=0] [source,cpp,ident=0]
---- ----
include::../example/env.cpp[tag=map_env] include::../example/env.cpp[tag=map_env]
---- ----

View File

@@ -118,7 +118,7 @@ struct custom_initializer
}; };
---- ----
NOTE: All the additional launchers for windows inherit `default_launcher`. NTOE: All the additional launchers for windows inherit `default_launcher`.
The call sequence is as follows: The call sequence is as follows:

View File

@@ -1,6 +1,6 @@
== `bind_launcher.hpp` == `bind_launcher.hpp`
The `bind_launcher` utilities allow on the fly construction of a launcher with bound initializers. The `bind_launcher` utlitities allow on the fly construction of a launcher with bound initializers.
[source,cpp] [source,cpp]
---- ----

View File

@@ -8,7 +8,7 @@ The `default_launcher` is the standard way of creating a process.
asio::io_context ctx; asio::io_context ctx;
process proc(ctx.get_executor(), "test", {}); process proc(ctx.get_executor(), "test", {});
// equivalent to // equivalent to
process proc = default_launcher()(ctx.get_executor(), "test", {}); process prod = default_launcher()(ctx.get_executor(), "test", {});
---- ----
It has four overloads: It has four overloads:

View File

@@ -1,7 +1,7 @@
== `popen.hpp` == `popen.hpp`
[#popen] [#popen]
`popen` is a class that launches a process and connect stdin & stdout to pipes. `popen` is a class that launches a process and connect stdin & stderr to pipes.
[source,cpp] [source,cpp]
---- ----

View File

@@ -41,7 +41,7 @@ struct bind_fd
process p{"test", {}, posix::bind_fd(42, 24)}; process p{"test", {}, posix::bind_fd(42, 24)};
*/ */
bind_fd(int target, int fd); bind_fd(int target, int fd):
// Inherit a null device as a set descriptor. // Inherit a null device as a set descriptor.
/* This will a null device as 42 to the child process: /* This will a null device as 42 to the child process:

View File

@@ -78,7 +78,7 @@ struct basic_process
template<typename ExecutionContext, typename Args, typename ... Inits> template<typename ExecutionContext, typename Args, typename ... Inits>
explicit basic_process( explicit basic_process(
ExecutionContext & context, ExecutionContext & context,
const filesystem::path& exe, const filesystem::path&>::type exe,
Args&& args, Inits&&... inits); Args&& args, Inits&&... inits);
// Attach to an existing process // Attach to an existing process
@@ -142,7 +142,7 @@ struct basic_process
native_handle_type native_handle() {return process_handle_.native_handle(); } native_handle_type native_handle() {return process_handle_.native_handle(); }
// Return the evaluated exit_code. // Return the evaluated exit_code.
int exit_code() const; int exit_code() cons;
// Get the id of the process; // Get the id of the process;
pid_type id() const; pid_type id() const;

View File

@@ -35,57 +35,12 @@ Valid initializers for any stdio are:
- `std::nullptr_t` assigning a null-device - `std::nullptr_t` assigning a null-device
- `FILE*` any open file, including `stdin`, `stdout` and `stderr` - `FILE*` any open file, including `stdin`, `stdout` and `stderr`
- `native_handle` any native file handle (`HANDLE` on windows) or file descriptor (`int` on posix)
- any io-object with a `.native_handle()` function that is compatible with the above. E.g. a `asio::ip::tcp::socket`, or a pipe object.
- a filesystem::path, which will open a readable or writable depending on the direction of the stream - a filesystem::path, which will open a readable or writable depending on the direction of the stream
- an `asio::basic_writeable_pipe` for stdin or `asio::basic_readable_pipe` for stderr/stdout. - `native_handle` any native file handle (`HANDLE` on windows) or file descriptor (`int` on posix)
- any io-object with a .native_handle() function that is compatible with the above. E.g. a asio::ip::tcp::socket
- an asio::basic_writeable_pipe for stdin or asio::basic_readable_pipe for stderr/stdout.
When passing a `FILE*`, a `native_handle` or an io-object with a `native_handle`,
the initializer will assign the handle as is to the child process.
That is the file descriptor/handle gets cloned into the subprocess and used without modification.
When passing a filesystem::path, the initializer will attempt to open the file and then pass the handle
to the subprocess.
When passing a `readable_pipe` to stdout/stderr or a `writable_pipe` to stdin by reference,
the initializer to create the other side of the pipe (`writable_pipe` for stdout/stderr, `readable_pipe` for `stdin`),
connect the pair and pass the native_handle to the child process.
That is, these two are equivalent:
.Implicit construction of the readable pipe.
[source,cpp]
----
asio::io_context ctx;
asio::writable_pipe wp{ctx};
// create a readable pipe internally and connect it to wp
process proc{ctx, "/bin/bash", {}, process_stdio{.in=wp}};
// create it explicitly
{
// the pipe the child process reads from
asio::readable_pipe rp{ctx};
asio::connect_pipe(rp, wp);
// `rp.native_handle()` will be assigned to the child processes stdin
process proc{ctx, "/bin/bash", {}, process_stdio{.in=rp}};
rp.close(); // close it so the pipe closes when the `proc exits.
}
----
The explicit version allows you to assign the same `writable_pipe` to `stdout` and `stderr`:
[source,cpp]
----
// the pipe the parent process reads from and both
// stderr & stdout of the child process write to
asio::readable_pipe rp{ctx};
asio::writable_pipe wp{ctx};
asio::connect_pipe(rp, wp);
process proc{ctx, "/bin/bash", {}, process_stdio{.out=wp, .err=wp}};
wp.close(); // close it so the pipe closes when the `proc exits.
----
NOTE: If the child writes to a pipe, the parent reads from it et vice versa.
[source,cpp] [source,cpp]
@@ -97,4 +52,4 @@ struct process_stdio
__implementation_defined__ out; __implementation_defined__ out;
__implementation_defined__ err; __implementation_defined__ err;
}; };
---- ----

View File

@@ -162,9 +162,7 @@ struct exe_cmd_init : handler_base_ext
} }
static exe_cmd_init<Char> exe_args_shell(string_type && exe, std::vector<string_type> && args) static exe_cmd_init<Char> exe_args_shell(string_type && exe, std::vector<string_type> && args)
{ {
std::vector<string_type> args_ = {c_arg(Char())}; std::vector<string_type> args_ = {c_arg(Char()), std::move(exe)};
if (!exe.empty())
args_.emplace_back(std::move(exe));
args_.insert(args_.end(), std::make_move_iterator(args.begin()), std::make_move_iterator(args.end())); args_.insert(args_.end(), std::make_move_iterator(args.begin()), std::make_move_iterator(args.end()));
string_type sh = get_shell(Char()); string_type sh = get_shell(Char());

View File

@@ -69,8 +69,6 @@ struct basic_process_handle_fd
basic_process_handle_fd(executor_type executor, pid_type pid) basic_process_handle_fd(executor_type executor, pid_type pid)
: pid_(pid), descriptor_(executor, syscall(SYS_pidfd_open, pid, 0)) : pid_(pid), descriptor_(executor, syscall(SYS_pidfd_open, pid, 0))
{ {
if (descriptor_.native_handle() == -1)
detail::throw_error(detail::get_last_error(), "wait(pid)");
} }
basic_process_handle_fd(executor_type executor, pid_type pid, native_handle_type process_handle) basic_process_handle_fd(executor_type executor, pid_type pid, native_handle_type process_handle)

View File

@@ -44,14 +44,9 @@ std::basic_string<CharOut, Traits, Allocator> conv_string(
if (ec) if (ec)
detail::throw_error(ec, "size_as_utf8"); detail::throw_error(ec, "size_as_utf8");
std::basic_string<CharOut, Traits, Allocator> res(allocator); std::basic_string<CharOut, Traits, Allocator> res(allocator);
res.resize(req_size); res.resize(req_size);
if (req_size == 0)
return res;
auto res_size = convert_to_utf8(data, size, &res.front(), req_size, ec); auto res_size = convert_to_utf8(data, size, &res.front(), req_size, ec);
if (ec) if (ec)
detail::throw_error(ec, "convert_to_utf8"); detail::throw_error(ec, "convert_to_utf8");
@@ -75,9 +70,6 @@ std::basic_string<CharOut, Traits, Allocator> conv_string(
std::basic_string<CharOut, Traits, Allocator> res(allocator); std::basic_string<CharOut, Traits, Allocator> res(allocator);
res.resize(req_size); res.resize(req_size);
if (req_size == 0)
return res;
auto res_size = convert_to_wide(data, size, &res.front(), req_size, ec); auto res_size = convert_to_wide(data, size, &res.front(), req_size, ec);
if (ec) if (ec)
detail::throw_error(ec, "convert_to_wide"); detail::throw_error(ec, "convert_to_wide");

View File

@@ -1762,12 +1762,9 @@ struct process_environment
std::vector<environment::key_value_pair> env_buffer; std::vector<environment::key_value_pair> env_buffer;
std::vector<wchar_t> unicode_env; std::vector<wchar_t> unicode_env;
BOOST_PROCESS_V2_DECL
error_code on_setup(windows::default_launcher & launcher, error_code on_setup(windows::default_launcher & launcher,
const filesystem::path &, const std::wstring &) const filesystem::path &, const std::wstring &);
{
return do_setup(launcher);
}
BOOST_PROCESS_V2_DECL error_code do_setup(windows::default_launcher & launcher);
#else #else
@@ -1816,13 +1813,7 @@ struct process_environment
BOOST_PROCESS_V2_DECL BOOST_PROCESS_V2_DECL
error_code on_setup(posix::default_launcher & launcher, error_code on_setup(posix::default_launcher & launcher,
const filesystem::path &, const char * const *) const filesystem::path &, const char * const *);
{
return do_setup(launcher);
}
BOOST_PROCESS_V2_DECL error_code do_setup(posix::default_launcher & launcher);
std::vector<environment::key_value_pair> env_buffer; std::vector<environment::key_value_pair> env_buffer;
std::vector<const char *> env; std::vector<const char *> env;

View File

@@ -352,9 +352,9 @@ struct default_launcher
} }
fd_whitelist.push_back(pg.p[1]); fd_whitelist.push_back(pg.p[1]);
#if !defined(BOOST_PROCESS_V2_DISABLE_NOTIFY_FORK)
auto & ctx = net::query( auto & ctx = net::query(
exec, net::execution::context); exec, net::execution::context);
#if !defined(BOOST_PROCESS_V2_DISABLE_NOTIFY_FORK)
ctx.notify_fork(net::execution_context::fork_prepare); ctx.notify_fork(net::execution_context::fork_prepare);
#endif #endif
pid = ::fork(); pid = ::fork();
@@ -386,7 +386,7 @@ struct default_launcher
ignore_unused(::write(pg.p[1], &errno, sizeof(int))); ignore_unused(::write(pg.p[1], &errno, sizeof(int)));
BOOST_PROCESS_V2_ASSIGN_EC(ec, errno, system_category()); BOOST_PROCESS_V2_ASSIGN_EC(ec, errno, system_category());
detail::on_exec_error(*this, executable, argv, ec, inits...); detail::on_exec_error(*this, executable, argv, ec, inits...);
::_exit(EXIT_FAILURE); ::exit(EXIT_FAILURE);
return basic_process<Executor>{exec}; return basic_process<Executor>{exec};
} }
#if !defined(BOOST_PROCESS_V2_DISABLE_NOTIFY_FORK) #if !defined(BOOST_PROCESS_V2_DISABLE_NOTIFY_FORK)

View File

@@ -115,7 +115,7 @@ struct fork_and_forget_launcher : default_launcher
BOOST_PROCESS_V2_ASSIGN_EC(ec, errno, system_category()); BOOST_PROCESS_V2_ASSIGN_EC(ec, errno, system_category());
detail::on_exec_error(*this, executable, argv, ec, inits...); detail::on_exec_error(*this, executable, argv, ec, inits...);
::_exit(EXIT_FAILURE); ::exit(EXIT_FAILURE);
return basic_process<Executor>{exec}; return basic_process<Executor>{exec};
} }
#if !defined(BOOST_PROCESS_V2_DISABLE_NOTIFY_FORK) #if !defined(BOOST_PROCESS_V2_DISABLE_NOTIFY_FORK)

View File

@@ -136,7 +136,7 @@ struct pdfork_launcher : default_launcher
default_launcher::ignore_unused(::write(pg.p[1], &errno, sizeof(int))); default_launcher::ignore_unused(::write(pg.p[1], &errno, sizeof(int)));
BOOST_PROCESS_V2_ASSIGN_EC(ec, errno, system_category()); BOOST_PROCESS_V2_ASSIGN_EC(ec, errno, system_category());
detail::on_exec_error(*this, executable, argv, ec, inits...); detail::on_exec_error(*this, executable, argv, ec, inits...);
::_exit(EXIT_FAILURE); ::exit(EXIT_FAILURE);
return basic_process<Executor>{exec}; return basic_process<Executor>{exec};
} }
#if !defined(BOOST_PROCESS_V2_DISABLE_NOTIFY_FORK) #if !defined(BOOST_PROCESS_V2_DISABLE_NOTIFY_FORK)

View File

@@ -140,7 +140,7 @@ struct pipe_fork_launcher : default_launcher
default_launcher::ignore_unused(::write(pg.p[1], &errno, sizeof(int))); default_launcher::ignore_unused(::write(pg.p[1], &errno, sizeof(int)));
BOOST_PROCESS_V2_ASSIGN_EC(ec, errno, system_category()); BOOST_PROCESS_V2_ASSIGN_EC(ec, errno, system_category());
detail::on_exec_error(*this, executable, argv, ec, inits...); detail::on_exec_error(*this, executable, argv, ec, inits...);
::_exit(EXIT_FAILURE); ::exit(EXIT_FAILURE);
return basic_process<Executor>{exec}; return basic_process<Executor>{exec};
} }
ctx.notify_fork(net::execution_context::fork_parent); ctx.notify_fork(net::execution_context::fork_parent);

View File

@@ -184,7 +184,7 @@ struct process_io_binding
process_io_binding & operator=(const process_io_binding &) = delete; process_io_binding & operator=(const process_io_binding &) = delete;
process_io_binding(process_io_binding && other) noexcept process_io_binding(process_io_binding && other) noexcept
: fd(other.fd), fd_needs_closing(other.fd_needs_closing), ec(other.ec) : fd(other.fd), fd_needs_closing(other.fd), ec(other.ec)
{ {
other.fd = target; other.fd = target;
other.fd_needs_closing = false; other.fd_needs_closing = false;

View File

@@ -225,9 +225,6 @@ struct default_launcher
INVALID_HANDLE_VALUE, INVALID_HANDLE_VALUE,
INVALID_HANDLE_VALUE}, INVALID_HANDLE_VALUE},
nullptr}; nullptr};
/// Allow batch files to be executed, which might pose a security threat.
bool allow_batch_files = false;
/// The process_information that gets assigned after a call to CreateProcess /// The process_information that gets assigned after a call to CreateProcess
PROCESS_INFORMATION process_information{nullptr, nullptr, 0,0}; PROCESS_INFORMATION process_information{nullptr, nullptr, 0,0};
@@ -296,12 +293,6 @@ struct default_launcher
Args && args, Args && args,
Inits && ... inits ) -> enable_init<Executor, Inits...> Inits && ... inits ) -> enable_init<Executor, Inits...>
{ {
if (!allow_batch_files && ((executable.extension() == ".bat") || (executable.extension() == ".cmd")))
{
BOOST_PROCESS_V2_ASSIGN_EC(ec, ERROR_ACCESS_DENIED, system_category());
return basic_process<Executor>(exec);
}
auto command_line = this->build_command_line(executable, std::forward<Args>(args)); auto command_line = this->build_command_line(executable, std::forward<Args>(args));
ec = detail::on_setup(*this, executable, command_line, inits...); ec = detail::on_setup(*this, executable, command_line, inits...);
@@ -361,6 +352,7 @@ struct default_launcher
BOOST_PROCESS_V2_DECL static BOOST_PROCESS_V2_DECL static
std::size_t escape_argv_string(wchar_t * itr, std::size_t max_size, std::size_t escape_argv_string(wchar_t * itr, std::size_t max_size,
basic_string_view<wchar_t> ws); basic_string_view<wchar_t> ws);
@@ -403,7 +395,6 @@ struct default_launcher
{ {
return detail::conv_string<wchar_t>(arg.data(), arg.size()); return detail::conv_string<wchar_t>(arg.data(), arg.size());
}); });
return build_command_line_impl(pt, argw, L""); return build_command_line_impl(pt, argw, L"");
} }
@@ -415,11 +406,10 @@ struct default_launcher
{ {
std::wstring buffer; std::wstring buffer;
buffer.resize(escaped_argv_length(pt.native())); buffer.resize(escaped_argv_length(pt.native()));
escape_argv_string(&buffer.front(), buffer.size(), pt.native());
if (!buffer.empty())
escape_argv_string(&buffer.front(), buffer.size(), pt.native());
return buffer; return buffer;
} }
return build_command_line_impl(pt, args, *std::begin(args)); return build_command_line_impl(pt, args, *std::begin(args));
} }
@@ -448,4 +438,4 @@ BOOST_PROCESS_V2_END_NAMESPACE
#endif //BOOST_PROCESS_V2_WINDOWS_DEFAULT_LAUNCHER_HPP #endif //BOOST_PROCESS_V2_WINDOWS_DEFAULT_LAUNCHER_HPP

View File

@@ -17,7 +17,7 @@ BOOST_PROCESS_V2_BEGIN_NAMESPACE
#if defined(BOOST_PROCESS_V2_WINDOWS) #if defined(BOOST_PROCESS_V2_WINDOWS)
error_code process_environment::do_setup(windows::default_launcher & launcher) error_code process_environment::on_setup(windows::default_launcher & launcher, const filesystem::path &, const std::wstring &)
{ {
if (!unicode_env.empty() && !ec) if (!unicode_env.empty() && !ec)
{ {
@@ -30,7 +30,7 @@ error_code process_environment::do_setup(windows::default_launcher & launcher)
#else #else
error_code process_environment::do_setup(posix::default_launcher & launcher) error_code process_environment::on_setup(posix::default_launcher & launcher, const filesystem::path &, const char * const *)
{ {
launcher.env = env.data(); launcher.env = env.data();
return error_code{}; return error_code{};

View File

@@ -24,72 +24,54 @@ namespace windows
if (ws.empty()) if (ws.empty())
return 2u; // just quotes return 2u; // just quotes
constexpr static auto space = L' ';
constexpr static auto quote = L'"'; constexpr static auto quote = L'"';
const auto needs_quotes = ws.find_first_of(L" \t") != basic_string_view<wchar_t>::npos;
std::size_t needed_escapes = 0u; const auto has_space = ws.find(space) != basic_string_view<wchar_t>::npos;
for (auto itr = ws.begin(); itr != ws.end(); itr ++) const auto quoted = (ws.front() == quote) && (ws.back() == quote);
{ const auto needs_escape = has_space && !quoted ;
if (*itr == quote)
needed_escapes++; if (!needs_escape)
else if (*itr == L'\\') return ws.size();
{ else
auto nx = std::next(itr); return ws.size() + std::count(ws.begin(), ws.end(), quote) + 2u;
if (nx != ws.end() && *nx == L'"')
needed_escapes ++;
else if (nx == ws.end())
needed_escapes ++;
}
}
return ws.size() + needed_escapes + (needs_quotes ? 2u : 0u);
} }
std::size_t default_launcher::escape_argv_string(wchar_t * itr, std::size_t max_size, std::size_t default_launcher::escape_argv_string(wchar_t * itr, std::size_t max_size,
basic_string_view<wchar_t> ws) basic_string_view<wchar_t> ws)
{ {
constexpr static auto quote = L'"'; const auto sz = escaped_argv_length(ws);
const auto needs_quotes = ws.find_first_of(L" \t") != basic_string_view<wchar_t>::npos;
const auto needed_escapes = std::count(ws.begin(), ws.end(), quote);
const auto sz = ws.size() + needed_escapes + (needs_quotes ? 2u : 0u);
if (sz > max_size) if (sz > max_size)
return 0u; return 0u;
if (ws.empty()) if (ws.empty())
{ {
itr[0] = quote; itr[0] = L'"';
itr[1] = quote; itr[1] = L'"';
return 2u; return 2u;
} }
const auto has_space = ws.find(L' ') != basic_string_view<wchar_t>::npos;
const auto quoted = (ws.front() == L'"') && (ws.back() == L'"');
const auto needs_escape = has_space && !quoted;
if (!needs_escape)
return std::copy(ws.begin(), ws.end(), itr) - itr;
if (sz < (2u + ws.size()))
return 0u;
const auto end = itr + sz; const auto end = itr + sz;
const auto begin = itr; const auto begin = itr;
if (needs_quotes) *(itr ++) = L'"';
*(itr++) = quote; for (auto wc : ws)
for (auto it = ws.begin(); it != ws.end(); it ++)
{ {
if (*it == quote) // makes it \" if (wc == L'"')
*(itr++) = L'\\'; *(itr++) = L'\\';
*(itr++) = wc;
if (*it == L'\\') // \" needs to become \\\"
{
auto nx = std::next(it);
if (nx != ws.end() && *nx == L'"')
*(itr++) = L'\\';
else if (nx == ws.end())
*(itr++) = L'\\';
}
*(itr++) = *it;
} }
if (needs_quotes) *(itr ++) = L'"';
*(itr++) = quote;
return itr - begin; return itr - begin;
} }
@@ -126,18 +108,13 @@ namespace windows
auto tl = get_thread_attribute_list(ec); auto tl = get_thread_attribute_list(ec);
if (ec) if (ec)
return; return;
auto itr = std::unique(inherited_handles.begin(), inherited_handles.end());
auto size = std::distance(inherited_handles.begin(), itr);
if (!::UpdateProcThreadAttribute( if (!::UpdateProcThreadAttribute(
tl, 0, PROC_THREAD_ATTRIBUTE_HANDLE_LIST, tl, 0, PROC_THREAD_ATTRIBUTE_HANDLE_LIST,
inherited_handles.data(), size * sizeof(HANDLE), nullptr, nullptr)) inherited_handles.data(), inherited_handles.size() * sizeof(HANDLE), nullptr, nullptr))
BOOST_PROCESS_V2_ASSIGN_LAST_ERROR(ec); BOOST_PROCESS_V2_ASSIGN_LAST_ERROR(ec);
} }
} }
BOOST_PROCESS_V2_END_NAMESPACE BOOST_PROCESS_V2_END_NAMESPACE
#endif #endif

View File

@@ -1,12 +1,18 @@
enable_testing() enable_testing()
add_library(boost_process_v2_test_impl test_impl.cpp)
target_compile_definitions(boost_process_v2_test_impl PUBLIC -DBOOST_PROCESS_V2_SEPARATE_COMPILATION=1)
if (WIN32)
target_compile_definitions(boost_process_v2_test_impl PUBLIC WIN32_LEAN_AND_MEAN=1)
target_link_libraries(boost_process_v2_test_impl Boost::process Boost::unit_test_framework Boost::process Ntdll)
else()
target_link_libraries(boost_process_v2_test_impl Boost::process Boost::unit_test_framework Boost::process)
endif()
function(boost_process_v2_standalone_test name) function(boost_process_v2_standalone_test name)
add_executable(boost_process_v2_${name} ${name}.cpp test_impl.cpp) add_executable(boost_process_v2_${name} ${name}.cpp)
target_link_libraries(boost_process_v2_${name} PUBLIC Boost::process Boost::system Boost::filesystem Boost::unit_test_framework ) target_link_libraries(boost_process_v2_${name} Boost::process Boost::system Boost::filesystem boost_process_v2_test_impl)
if (WIN32)
target_compile_definitions(boost_process_v2_${name} PUBLIC WIN32_LEAN_AND_MEAN=1)
target_link_libraries(boost_process_v2_${name} PUBLIC Ntdll)
endif()
add_test(NAME boost_process_v2_${name} COMMAND $<TARGET_FILE:boost_process_v2_${name}> ) add_test(NAME boost_process_v2_${name} COMMAND $<TARGET_FILE:boost_process_v2_${name}> )
endfunction() endfunction()
@@ -21,16 +27,13 @@ target_link_libraries(boost_process_v2_test_target PUBLIC Boost::process Boost::
function(boost_process_v2_test_with_target name) function(boost_process_v2_test_with_target name)
add_executable(boost_process_v2_${name} ${name}.cpp) add_executable(boost_process_v2_${name} ${name}.cpp)
target_link_libraries(boost_process_v2_${name} PUBLIC Boost::process Boost::system Boost::filesystem boost_process_v2_test_impl) target_link_libraries(boost_process_v2_${name} Boost::process Boost::system Boost::filesystem boost_process_v2_test_impl)
if (WIN32)
target_compile_definitions(boost_process_v2_${name} PUBLIC WIN32_LEAN_AND_MEAN=1)
target_link_libraries(boost_process_v2_${name} PUBLIC Ntdll)
endif()
add_dependencies(boost_process_v2_${name} boost_process_v2_test_target) add_dependencies(boost_process_v2_${name} boost_process_v2_test_target)
add_test(NAME boost_process_v2_${name} COMMAND $<TARGET_FILE:boost_process_v2_${name}> add_test(NAME boost_process_v2_${name} COMMAND $<TARGET_FILE:boost_process_v2_${name}>
-- $<TARGET_FILE:boost_process_v2_test_target>) -- $<TARGET_FILE:boost_process_v2_test_target>)
endfunction() endfunction()
boost_process_v2_test_with_target(process) boost_process_v2_test_with_target(process)
boost_process_v2_test_with_target(ext) boost_process_v2_test_with_target(ext)

View File

@@ -33,6 +33,7 @@ project : requirements
<os>NT,<toolset>cw:<library>ws2_32 <os>NT,<toolset>cw:<library>ws2_32
<os>NT,<toolset>gcc:<library>ws2_32 <os>NT,<toolset>gcc:<library>ws2_32
<os>NT,<toolset>gcc:<library>Bcrypt <os>NT,<toolset>gcc:<library>Bcrypt
<define>BOOST_PROCESS_V2_SEPARATE_COMPILATION=1
<library>/boost/test//included <library>/boost/test//included
; ;

View File

@@ -256,11 +256,8 @@ BOOST_AUTO_TEST_CASE(print_args_spec_out)
asio::writable_pipe wp{ctx}; asio::writable_pipe wp{ctx};
asio::connect_pipe(rp, wp); asio::connect_pipe(rp, wp);
fprintf(stderr, "print_args_spec_out\n");
bpv::process proc(ctx, pth, {"print-args", "&foo", "&", "|bar", "\"", "#foobar"}, bpv::process_stdio{/*in*/{},/*out*/wp, /*err*/ nullptr});
bpv::process proc(ctx, pth, {"print-args", "&foo", "&", "", "\"\"", "\\\"", "|bar", "\"", "#foobar"},
bpv::process_stdio{/*in*/{},/*out*/wp, /*err*/ nullptr});
BOOST_CHECK(proc.running());
wp.close(); wp.close();
asio::streambuf st; asio::streambuf st;
@@ -291,18 +288,6 @@ BOOST_AUTO_TEST_CASE(print_args_spec_out)
trim_end(line); trim_end(line);
BOOST_CHECK_EQUAL("&", line); BOOST_CHECK_EQUAL("&", line);
BOOST_CHECK(std::getline(is, line));
trim_end(line);
BOOST_CHECK_EQUAL("", line);
BOOST_CHECK(std::getline(is, line));
trim_end(line);
BOOST_CHECK_EQUAL("\"\"", line);
BOOST_CHECK(std::getline(is, line));
trim_end(line);
BOOST_CHECK_EQUAL("\\\"", line);
BOOST_CHECK(std::getline(is, line)); BOOST_CHECK(std::getline(is, line));
trim_end(line); trim_end(line);
BOOST_CHECK_EQUAL("|bar", line); BOOST_CHECK_EQUAL("|bar", line);
@@ -311,7 +296,6 @@ BOOST_AUTO_TEST_CASE(print_args_spec_out)
trim_end(line); trim_end(line);
BOOST_CHECK_EQUAL("\"", line); BOOST_CHECK_EQUAL("\"", line);
BOOST_CHECK(std::getline(is, line)); BOOST_CHECK(std::getline(is, line));
trim_end(line); trim_end(line);
BOOST_CHECK_EQUAL("#foobar", line); BOOST_CHECK_EQUAL("#foobar", line);
@@ -868,70 +852,5 @@ BOOST_AUTO_TEST_CASE(async_terminate_code)
#endif #endif
BOOST_AUTO_TEST_CASE(print_args_combined)
{
using boost::unit_test::framework::master_test_suite;
const auto pth = master_test_suite().argv[1];
asio::io_context ctx;
asio::readable_pipe rp{ctx};
asio::writable_pipe wp{ctx};
asio::connect_pipe(rp, wp);
bpv::process proc(ctx, pth, {"print-args", "bar", "foo"}, bpv::process_stdio{/*in*/{}, /*.out= */ wp, /* .err=*/ wp});
wp.close();
asio::streambuf st;
std::istream is{&st};
bpv::error_code ec;
auto sz = asio::read(rp, st, ec);
while (ec == asio::error::interrupted)
sz += asio::read(rp, st, ec);
BOOST_CHECK_NE(sz , 0u);
BOOST_CHECK_MESSAGE((ec == asio::error::broken_pipe) || (ec == asio::error::eof), ec.message());
std::string line;
BOOST_CHECK(std::getline(is, line));
trim_end(line);
BOOST_CHECK_EQUAL(pth, line );
BOOST_CHECK(std::getline(is, line));
trim_end(line);
BOOST_CHECK_EQUAL(pth, line );
BOOST_CHECK(std::getline(is, line));
trim_end(line);
BOOST_CHECK_EQUAL("print-args", line);
BOOST_CHECK(std::getline(is, line));
trim_end(line);
BOOST_CHECK_EQUAL("print-args", line);
BOOST_CHECK(std::getline(is, line));
trim_end(line);
BOOST_CHECK_EQUAL("bar", line);
BOOST_CHECK(std::getline(is, line));
trim_end(line);
BOOST_CHECK_EQUAL("bar", line);
BOOST_CHECK(std::getline(is, line));
trim_end(line);
BOOST_CHECK_EQUAL("foo", line);
BOOST_CHECK(std::getline(is, line));
trim_end(line);
BOOST_CHECK_EQUAL("foo", line);
proc.wait();
BOOST_CHECK_EQUAL(proc.exit_code(), 0);
}
BOOST_AUTO_TEST_SUITE_END(); BOOST_AUTO_TEST_SUITE_END();