2
0
mirror of https://github.com/boostorg/process.git synced 2026-01-20 04:42:24 +00:00

Compare commits

...

117 Commits

Author SHA1 Message Date
Klemens Morgenstern
c1d0f1be76 Merge pull request #227 from x-santiaga-x/patch-1
Closes klemens-morgenstern/boost-process#218
2021-10-29 18:59:23 +08:00
Klemens Morgenstern
10c93d88a1 Merge pull request #228 from x-santiaga-x/patch-2
Fix missing headers
2021-10-29 18:55:03 +08:00
silent
5f80e72e9c Closes klemens-morgenstern/boost-process#218 2021-10-29 13:24:19 +03:00
silent
d26ef52519 fix missing headers 2021-10-29 12:18:20 +03:00
Klemens Morgenstern
f4d2c260d4 Merge pull request #231 from klemens-morgenstern/develop
Master update
2021-10-27 17:36:17 +08:00
Klemens Morgenstern
e32651a260 Removed overly constraint tests. 2021-10-27 12:23:18 +08:00
Klemens Morgenstern
71aa7d9c00 Merge commit 'ed3b066' into develop 2021-10-27 11:45:43 +08:00
Klemens Morgenstern
ed3b066da1 Multiple windows test fixes 2021-10-27 11:43:33 +08:00
Klemens
83380dad79 Increased log level on windows. 2021-10-26 23:53:02 +08:00
Klemens
5ad5e82577 Trying to catch windows early complete. 2021-10-22 19:42:53 +08:00
Klemens
3acc1a3fa8 Added missing work guard on windows. 2021-10-16 15:46:11 +08:00
Klemens
9bb088ed5d Closes boostorg/process#191. 2021-10-15 10:57:01 +08:00
Klemens
cd4ef692e1 Closes boostorg/process#189. 2021-10-15 10:54:38 +08:00
Klemens
268795f3c0 Multiple fixes. 2021-10-14 17:41:27 +08:00
Klemens
f8f9c2323c Changed child(pid_t) signature. 2021-10-14 15:36:07 +08:00
Klemens
0c3ded6636 Closes boostorg/process#197. 2021-10-14 15:36:07 +08:00
Klemens
20b328dbf1 Attempting to fix wchar_t build error on circle. 2021-10-14 14:48:23 +08:00
Klemens
a60203dac3 Closes boostorg/process#121 2021-10-14 14:48:23 +08:00
Klemens
6d08cb369e Closes boostorg/process#190 2021-10-14 14:28:07 +08:00
Klemens
8dc5ee22f5 Merge remote-tracking branch 'boostorg/develop' into develop 2021-10-14 14:20:27 +08:00
Klemens
a13a60d428 Removed unneeded WNOHANG. 2021-10-14 14:19:32 +08:00
Klemens Morgenstern
fa2a522ef2 Merge pull request #220 from odhinnsrunes/file_descriptor_move_assign_fix
Fixed file_descriptor move assignment operator returns a reference to this.
2021-10-14 14:14:23 +08:00
Klemens Morgenstern
2b39b56efb Merge pull request #208 from jorj1988/extend_hpp_include_memory
Add include to extend.hpp for memory.
2021-10-09 16:23:38 +08:00
Klemens Morgenstern
5a283e5200 Merge pull request #218 from Val-test/develop
Fix child construction from pid_t (it takes reference to pid_t for no reason).
2021-10-09 16:23:26 +08:00
Klemens Morgenstern
3d5f449052 Merge pull request #223 from apolukhin/patch-1
Remove trailing whitespace
2021-10-09 16:22:13 +08:00
Antony Polukhin
aefb990a7a Remove triling whitespace 2021-10-03 10:30:10 +03:00
Valentyn Pavliuchenko
3d092498b2 Fix child construction from pid_t (it takes reference to pid_t for no reason). 2021-08-19 19:54:42 +03:00
Klemens Morgenstern
7d7476343a Merge pull request #215 from Eddie-cz/develop
Missing include added
2021-08-08 20:09:20 +08:00
Eddie-cz
e08374ed95 Update wchar_t.hpp 2021-08-02 10:49:42 +02:00
Klemens Morgenstern
155ebdcf1f Merge pull request #181 from orgads/patch-1
Fix cross-compile linux->mingw
2021-07-05 14:48:01 +08:00
Orgad Shaneh
a9925a5d6d Fix cross-compile linux->mingw
Headers are lower case.
2021-07-05 09:17:16 +03:00
Peter Dimov
741d3f4a07 Add CMakeLists.txt 2021-05-28 02:42:50 +03:00
Peter Dimov
f195243a81 Merge branch 'master' into develop 2021-05-28 02:40:12 +03:00
George Pimm
8c5ab02192 Add include to extend.hpp for memory.
extend.hpp uses std::shared_ptr as a member but does not include it
2021-04-14 13:52:46 +01:00
Klemens Morgenstern
8222a57744 Merge pull request #192 from eldiener/develop
Add "cxxstd" json field. The "cxxstd" json field is being added to ea…
2020-12-17 17:35:23 +08:00
Edward Diener
aa3ae06ab7 Add "cxxstd" json field. The "cxxstd" json field is being added to each Boost library's meta json information for libraries whose minumum C++ standard compilation level is C++11 on up. The value of this field matches one of the values for 'cxxstd' in Boost.Build. The purpose of doing this is to provide information for the Boost website documentation for each library which will specify the minimum C++ standard compilation that an end-user must employ in order to use the particular library. This will aid end-users who want to know if they can successfully use a Boost library based on their C++ compiler's compilation level, without having to search the library's documentation to find this out. 2020-12-16 10:32:08 -05:00
Klemens Morgenstern
1b4d67170f Merge pull request #177 from giomasce-throwaway/develop
Fix typo in copyright headers.
2020-10-29 21:17:53 +04:00
Klemens Morgenstern
e3594034eb Merge pull request #179 from nre-ableton/nre/develop/posix-default-locale
Return default locale on POSIX systems
2020-10-29 21:17:34 +04:00
Klemens Morgenstern
ee983eba64 Merge pull request #171 from Shauren/fix-windows-h
Fix windows builds that don't include Windows.h
2020-10-29 21:17:05 +04:00
nre
0eb4685558 Return default locale on POSIX systems
The comment here is incorrect; an empty locale is not the correct
behavior here and will cause exceptions to be thrown in other code.
Instead a locale object with the default constructor should be
returned, not one created with an empty string. This is a known issue
which was originally reported here:

https://svn.boost.org/trac10/ticket/4688

The issue claims to have been fixed in 7bb19f9 (see also:
https://svn.boost.org/trac10/changeset/72855).

However, this only fixes the issue for FreeBSD and not other POSIX
platforms. This patch is based on the one originally submitted here:

https://svn.boost.org/trac10/attachment/ticket/4688/boost_filesystem.patch
2020-10-16 10:07:14 +02:00
Giovanni Mascellani
7cf1a3b8e6 Fix typo in copyright headers. 2020-10-09 15:49:45 +02:00
Klemens Morgenstern
c0a1a82361 Merge pull request #173 from alloutlife/issue_121
Adding bug fix to resolve issue #121
2020-09-22 01:46:03 +03:00
Alexey Kushnarev
532951902f Adding bug fix to resolve issue #121 2020-09-14 12:16:14 +03:00
Shauren
642c2d369f Use ::boost::winapi::DWORD_ instead of global DWORD 2020-09-03 13:21:36 +02:00
Shauren
d7a721ee0d Removed __kernel_entry usage from Nt* function typedefs, is is part of SAL annoations, not neccessary for proper signature 2020-09-03 13:21:02 +02:00
James Baker
baa8d3fe7c Returning *this instead of erroneous *this. Issue # 219 2020-08-28 12:43:16 -04:00
James Baker
42bdfb5545 Fixed file_descriptor move assignment operator to return a reference to 'this'. Issue # 219 2020-08-28 11:13:39 -04:00
Klemens Morgenstern
0f053c323e Merge pull request #167 from zaucy/move_assignment_return_value_fix
Added return *this in move assignment operator
2020-08-03 16:29:09 +07:00
Ezekiel Warren
036c9fc4d1 Added return *this in move assignment operator 2020-08-01 18:53:20 -07:00
Klemens Morgenstern
b2a96a3e13 Merge pull request #215 from klemens-morgenstern/develop
Develop merge
2020-06-28 23:25:58 +07:00
Klemens David Morgenstern
1dbb3626a9 Changed async system test to let the subprocess wait, to avoid a race condition 2020-06-28 22:06:07 +07:00
Klemens David Morgenstern
d79e1f2443 changed limit_fd test to use stdin 2020-06-28 16:29:07 +07:00
Klemens David Morgenstern
605dcd19d8 Removed faulty reinterpret cast from posix test 2020-06-28 15:09:58 +07:00
Klemens David Morgenstern
6b6a6fa61c Fixed limit_fd windows test the current broke if stdout and stderr were the same, as a CI system might do 2020-06-27 14:21:53 +07:00
Klemens David Morgenstern
295e2bdd9c Merge branch 'develop' of github.com:boostorg/process into develop 2020-06-27 12:55:04 +07:00
Klemens Morgenstern
dd1513846b Merge pull request #165 from chriskohlhoff/standard-executors-changes
Changes required to compile with new Asio executors model
2020-06-27 12:54:34 +07:00
Christopher Kohlhoff
7a94abfaf2 Use strand<> rather than legacy executor io_context::strand. 2020-06-24 19:52:52 +10:00
Christopher Kohlhoff
b55a09479c New name for polymorphic executor. Remove unused "old services" code. 2020-06-24 19:52:52 +10:00
Klemens David Morgenstern
5afb20760c Fixed exe-args style for windows 2020-05-21 18:48:04 +07:00
Klemens Morgenstern
eec87e1dd9 Update child_handle.hpp 2020-05-21 01:38:37 +07:00
Klemens Morgenstern
f250a33fb4 Switched to report.py script 2020-05-20 21:50:37 +07:00
Klemens David Morgenstern
570cf83a96 Merge branch 'develop' of github.com:boostorg/process into develop 2020-05-20 21:07:49 +07:00
Klemens David Morgenstern
d52d244f83 Closes boostorg/process#134 2020-05-20 21:00:50 +07:00
Klemens Morgenstern
04ab646f12 Merge pull request #136 from jonesmz/patch-10
Fix "comparison between signed and unsigned" warning
2020-05-20 20:51:31 +07:00
Klemens David Morgenstern
31c65b5442 Closes boostorg/process#127 2020-05-20 20:49:46 +07:00
Klemens Morgenstern
ebbb6d8b36 Merge pull request #126 from Martchus/fix-on_exit-example
Pass io_context in on_exit example
2020-05-20 20:44:42 +07:00
Klemens Morgenstern
b9c0140a26 Merge pull request #119 from licekto/double-closed-fd-fixed
Fix for double closed FD: https://github.com/boostorg/process/issues/105
2020-05-20 20:44:02 +07:00
Klemens Morgenstern
27f587a4be Merge pull request #137 from jonesmz/patch-11
Fix async_system_future.cpp:60:9: warning: variable ‘exit_code’ set but not used
2020-05-20 20:40:07 +07:00
Klemens Morgenstern
7f6061c956 Merge pull request #138 from jonesmz/patch-12
Fix "comparison between signed and unsigned" warning
2020-05-20 20:38:58 +07:00
Klemens David Morgenstern
2f32c95341 Closes boostorg/process#145 and boostorg/process#188 2020-05-20 20:33:55 +07:00
Klemens Morgenstern
a8029fc191 Merge pull request #140 from jonesmz/patch-14
Fix comparison between signed and unsigned integer expressions
2020-05-20 20:15:01 +07:00
Klemens Morgenstern
9f4bd9bce3 Merge pull request #139 from jonesmz/patch-13
Fix warning: unused variable ‘env’
2020-05-20 20:14:53 +07:00
Klemens Morgenstern
71f844c24f Merge pull request #149 from jonesmz/patch-22
file_in.hpp:29:17: warning: suggest braces around initialization of subobject
2020-05-20 20:09:30 +07:00
Klemens David Morgenstern
b0b6d67e6f Closes boostorg/process#144 2020-05-20 20:09:14 +07:00
Klemens David Morgenstern
3a2576a4d8 Merge branch 'develop' of github.com:boostorg/process into develop 2020-05-20 20:07:47 +07:00
Klemens Morgenstern
34eaa262dd Merge pull request #147 from jonesmz/patch-20
close_out.hpp:25:52: warning: suggest braces around initialization of subobject
2020-05-20 20:06:46 +07:00
Klemens Morgenstern
ce3b3d8f99 Merge pull request #148 from jonesmz/patch-21
null_in.hpp:28:17: warning: suggest braces around initialization of s…
2020-05-20 20:06:35 +07:00
Klemens Morgenstern
429f2ba95c Merge pull request #150 from jonesmz/patch-23
pipe_in.hpp:30:17: warning: suggest braces around initialization of subobject
2020-05-20 20:05:29 +07:00
Klemens Morgenstern
0c2e7387c8 Merge pull request #152 from Naios/msvcfix
[Build Fix] Workaround for a MSVC 14.1 and 14.2 compiler bug
2020-05-20 20:05:22 +07:00
Klemens Morgenstern
2a2ea4b92d Merge pull request #146 from jonesmz/patch-19
Fix example/intro.cpp includes
2020-05-20 20:01:47 +07:00
Klemens David Morgenstern
873ab2558d Closes boostorg/process#153 2020-05-20 19:58:23 +07:00
Klemens David Morgenstern
03571d4eaf Merge branch 'develop' of github.com:boostorg/process into develop 2020-05-20 19:44:42 +07:00
Klemens Morgenstern
46ab3ba9b8 Merge pull request #158 from acronce/develop
Add the lambda declarator needed to disambiguate the multiple return …
2020-05-20 19:39:24 +07:00
Allen Cronce
3aba1f6eb1 Add the lambda declarator needed to disambiguate the multiple return values from these lambda functions as a bool. Before this change the return of a "false" was interpreted as an int, but the second return is a bool. This caused the following compile error when accessing environment variables under Xcode 11.4.1: Return type 'bool' must match previous return type 'int' when lambda expression has unspecified explicit return type 2020-05-13 07:25:36 -07:00
Denis Blank
44771769fa Workaround for a MSVC 14.1 and 14.2 compiler bug
* boost\include\boost/process/child.hpp(35): error C2600:
    'boost::process::child::child': cannot define a compiler-generated
    special member function (must be declared in the class first)
* Introduced by 8541cae
* See boostorg/process#113
2020-03-23 22:35:59 +01:00
Michael Jones
8704416941 pipe_in.hpp:30:17: warning: suggest braces around initialization of subobject 2020-03-13 02:57:56 -05:00
Michael Jones
f48392399f file_in.hpp:29:17: warning: suggest braces around initialization of subobject 2020-03-13 02:57:11 -05:00
Michael Jones
80f81117aa null_in.hpp:28:17: warning: suggest braces around initialization of subobject 2020-03-13 02:56:28 -05:00
Michael Jones
9cff55215d close_out.hpp:25:52: warning: suggest braces around initialization of subobject 2020-03-13 02:55:35 -05:00
Michael Jones
2e4b3c2406 Fix example/intro.cpp includes 2020-03-13 02:26:10 -05:00
Michael Jones
b510b6a9d9 Fix comparison between signed and unsigned integer expressions 2020-03-13 01:22:01 -05:00
Michael Jones
046b96186f Fix warning: unused variable ‘env’ 2020-03-13 01:20:10 -05:00
Michael Jones
1df2e67bc4 Fix "comparison between signed and unsigned" warning 2020-03-13 01:18:11 -05:00
Michael Jones
6cf69e2797 Fix async_system_future.cpp:60:9: warning: variable ‘exit_code’ set but not used 2020-03-13 01:11:46 -05:00
Michael Jones
d3e4cbf3b3 Fix "comparison between signed and unsigned" warning 2020-03-13 01:03:31 -05:00
Martchus
e67e49c891 Pass io_context in on_exit example
The description already states that an io_context needs
to be passed. This change is adjusting the example accordingly.
2020-03-08 21:17:54 +01:00
Licek Tomáš
29a43b17e4 Fix for double closed FD: https://github.com/boostorg/process/issues/105 2020-02-04 10:22:11 +01:00
Klemens David Morgenstern
6d10c3a807 Merge branch 'develop' of github.com:klemens-morgenstern/boost-process into develop 2020-01-26 15:18:00 +07:00
Klemens Morgenstern
668579ed6f Merge pull request #110 from sorayuki/patch-1
Fix memory over-write in pipe.hpp
2020-01-26 15:17:27 +07:00
Klemens Morgenstern
8af828cd43 Merge pull request #73 from fkonvick/develop
Fix double quotes processing on Windows
2020-01-26 15:16:39 +07:00
Klemens Morgenstern
590cc10b42 Merge pull request #75 from brunom/patch-1
typo
2020-01-26 15:15:07 +07:00
Klemens Morgenstern
3bc11ce3ac Merge pull request #113 from jonesmz/patch-1
Use = default for default constructor
2020-01-26 15:14:52 +07:00
Klemens Morgenstern
603441ecc3 Merge pull request #114 from jonesmz/patch-2
Use static constexpr instead of const for compile-time constant variable
2020-01-26 15:14:32 +07:00
Klemens Morgenstern
612a953369 Merge pull request #115 from jonesmz/patch-3
Use move construction for struct start_dir
2020-01-26 15:13:15 +07:00
Klemens Morgenstern
1554773d39 Merge pull request #213 from dermojo/develop
Documentation fixes
2020-01-26 15:10:44 +07:00
Daniel Evers
dd003bf2b0 Correct some typos etc. 2020-01-05 17:33:59 +01:00
Michael Jones
1502de1001 Use move construction for struct start_dir 2019-12-05 17:48:17 -06:00
Michael Jones
8541cae396 Update child_decl.hpp 2019-12-05 12:59:09 -06:00
Michael Jones
38fa1fd040 Update cmd.hpp 2019-12-05 12:54:35 -06:00
Michael Jones
ba15f760ab Use static constexpr instead of const for compile-time constant variable 2019-12-05 12:53:06 -06:00
Michael Jones
5e3e8f977e Update error.hpp 2019-12-05 12:49:56 -06:00
Michael Jones
6182876d4f Update io.hpp 2019-12-05 12:44:14 -06:00
Michael Jones
5bfd2ee08c Use = default for default constructor 2019-12-05 12:41:31 -06:00
sorayuki
c91b227c47 Fix memory over-write in pipe.hpp 2019-11-25 15:31:48 +08:00
Bruno Martinez
192191ecfb typo 2019-03-05 12:30:06 -03:00
Filip Konvička
3aeb6601cd Merge pull request #1 from fkonvick/windows-shell-dquotes
Fix double quotes processing on Windows
2019-02-27 14:05:59 +01:00
Filip Konvička
8a7c37617e Fix double quotes processing on Windows
Fix double quotes processing on Windows:
- quotes should only be added when not already present
- quotes need to be used when an argument includes spaces or double quotes
- quotes inside an argument should be doubled (that's the way Windows does escaping)
2019-02-27 14:02:45 +01:00
53 changed files with 295 additions and 229 deletions

View File

@@ -133,5 +133,4 @@ after_success:
after_script:
- bash <(curl -s https://codecov.io/bash)
- cd $BOOST/libs/$PROJECT_TO_TEST/test
- curl -s https://report.ci/upload.py | python - --name="$BADGE test run" --root_dir $BOOST
- curl -s https://report.ci/report.py | python - --name="$BADGE test run"

37
CMakeLists.txt Normal file
View File

@@ -0,0 +1,37 @@
# Generated by `boostdep --cmake process`
# Copyright 2020 Peter Dimov
# Distributed under the Boost Software License, Version 1.0.
# https://www.boost.org/LICENSE_1_0.txt
cmake_minimum_required(VERSION 3.5...3.16)
project(boost_process VERSION "${BOOST_SUPERPROJECT_VERSION}" LANGUAGES CXX)
add_library(boost_process INTERFACE)
add_library(Boost::process ALIAS boost_process)
target_include_directories(boost_process INTERFACE include)
target_link_libraries(boost_process
INTERFACE
Boost::algorithm
Boost::asio
Boost::config
Boost::core
Boost::filesystem
Boost::fusion
Boost::iterator
Boost::move
Boost::optional
Boost::system
Boost::tokenizer
Boost::type_index
Boost::winapi
)
if(BUILD_TESTING AND EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/test/CMakeLists.txt")
add_subdirectory(test)
endif()

View File

@@ -16,11 +16,11 @@ In that it is different than other facilities (like sockets) and provides anothe
Pipes are typically used for interprocess communication. The main reason is, that pipes can be directly assigned to the process stdio, i.e. stderr, stdin and stdout.
Additionally, half of the pipe can be inherited to the child process and closed in the father process. This will cause the pipe to be broken when the child process exits.
Though please note, that if the the same thread reads and write to a pipe, it will only talk to itself.
Though please note, that if the same thread reads and writes to a pipe, it will only talk to itself.
[section:anonymous Anonymous Pipes]
The most common pipes are anonymous. Since the have no name,
The most common pipes are anonymous. Since they have no name,
a handle to them can only be obtained from duplicating either handle.
In this library the following functions are used for the creation of unnamed pipes:
@@ -57,12 +57,12 @@ Every process is identified by a unique number[footnote it is unique as long as
[section:exit_code Exit code]
A process will return an integer value indicating whether it was successful. On posix
there are more codes associated with that, but not so on windows. Therefore there is not such encoding currently in the library.
there are more codes associated with that, but not so on windows. Therefore there is no such encoding currently in the library.
However an exit code of zero means the process was successful, while one different than zero indicates an error.
[endsect]
[section:termination Termination]
Processes can also be forced to exit. There are two ways to do this, signal the process to so and wait, and just terminate the process without conditions.
Processes can also be forced to exit. There are two ways to do this, signal the process to do so and wait, and just terminate the process without conditions.
Usually the first approach is to signal an exit request, but windows - unlike posix - does not provide a consistent way to do this. Hence this is not part of the
library and only the hard terminate is.
@@ -79,4 +79,4 @@ The environment is a map of variables local to every process. The most significa
[endsect]
[endsect]
[endsect]

View File

@@ -1,12 +1,12 @@
[section:design Design Rationale]
[section Scope]
This library is meant to give an wrapper around the different OS-specific methods
This library is meant to give a wrapper around the different OS-specific methods
to launch processes. Its aim is to provide all functionality that is available on
those systems and allow the user to do all related things, which require using the OS APIs.
[*This library does not try to provide a full library for everything process related]
[*This library does not try to provide a full library for everything process related.]
In many discussions the proposal was made to build boost.process into a DSEL [footnote Domain Specific Embedded Language] of some sort.
This is not the goal, it rather provides the facilities to build such a DSEL-Library on top of it.
This is not the goal, it rather provides the facilities to build such a DSEL-library on top of it.
Therefore the library also does [*not] force any particular use (such as only asynchronous communication) on its user.
It rather could be integrated with such a library.
@@ -33,7 +33,7 @@ Both styles can also be mixed in some cases.
system("gcc", "-c", args+={"main.cpp"});
```
In the following section the avaible styles will be described. Note that the
In the following section the available styles will be described. Note that the
overload style is implemented via type traits, so the types will be listed.
[caution There is no guarantee in which order the arguments will be applied!
@@ -54,7 +54,7 @@ interpret each string as an argument.
]
When using the overloading variant, a single string will result in a cmd interpretation,
several strings will yield a exe-args interpretation. Both version can be set explicitly:
several strings will yield a exe-args interpretation. Both versions can be set explicitly:
```
system("grep -c false /etc/passwd"); //cmd style
@@ -65,7 +65,7 @@ system(exe="grep", args={"-c", "false", "/etc/passwd"}); //exe-/args-
```
[note If a '"' sign is used in the argument style, it will be passed as part of the argument.
If the same effect it wanted with the cmd syntax, it ought to be escaped, i.e. '\\\"'. ]
If the same effect is wanted with the cmd syntax, it ought to be escaped, i.e. '\\\"'. ]
[note The `PATH` variable will automatically be searched in the command style,
but the one of the launching process, not the one passed to the child process.]
[endsect]

View File

@@ -15,14 +15,14 @@ To extend the library, the header [headerref boost/process/extend.hpp extend] is
It only provides the explicit style for custom properties, but no implicit style.
What this means is, that a custom initializer can be implemented, a reference to which can be passed to one of the launching functions.
If a class inherits [classref boost::process::extend::handler] it will be regarded as a initializer and thus directly put into the sequence
What this means is, that a custom initializer can be implemented, a reference which can be passed to one of the launching functions.
If a class inherits [classref boost::process::extend::handler] it will be regarded as an initializer and thus directly put into the sequence
the executor gets passed.
[section:structure Structure]
The executor calls different handlers of the initializers during the process launch.
The basic structure is consists of three functions, as given below:
The basic structure consists of three functions, as given below:
* [globalref boost::process::extend::on_setup on_setup]
* [globalref boost::process::extend::on_error on_error]
@@ -53,7 +53,7 @@ namespace ex = bp::extend;
__child__ c("foo", __on_success__=[](auto & exec) {std::cout << "hello world" << std::endl;});
```
Considering that lambda can also capture values, data can easily be shared between handlers.
Considering that lambdas can also capture values, data can easily be shared between handlers.
To see which members the executor has, refer to [classref boost::process::extend::windows_executor windows_executor]
and [classref boost::process::extend::posix_executor posix_executor].
@@ -86,7 +86,7 @@ __child__ c("foo", hello_world());
[note The implementation is done via overloading, not overriding.]
Every handler not implemented dafaults to [classref boost::process::extend::handler handler], where an empty handler is defined for each event.
Every handler not implemented defaults to [classref boost::process::extend::handler handler], where an empty handler is defined for each event.
[endsect]
@@ -100,7 +100,7 @@ this functionality is also available for extensions. If the class needs the __io
```
struct async_foo : __handler__, __require_io_context__
{
tempalte<typename Executor>
template<typename Executor>
void on_setup(Executor & exec)
{
__io_context__ & ios = __get_io_context__(exec.seq); //gives us a reference and a compiler error if not present.
@@ -139,10 +139,10 @@ struct async_bar : __handler, __async_handler__
[section:error Error handling]
If an error occurs in the initializers it shall be told to the executor and not handles directly. This is because
the behaviour can be changed through arguments passed to the launching function. Hence the the executor
If an error occurs in the initializers it shall be told to the executor and not handled directly. This is because
the behaviour can be changed through arguments passed to the launching function. Hence the executor
has the function `set_error`, which takes an [@http://en.cppreference.com/w/cpp/error/error_code std::error_code] and a string.
Depending on the cofiguration of the executor, this may either throw, set an internal `error_code`, or do nothing.
Depending on the configuration of the executor, this may either throw, set an internal `error_code`, or do nothing.
So let's take a simple example, where we set a randomly chosen `error_code`.
@@ -202,8 +202,8 @@ struct hello_exe : __handler__
};
```
So given our example, the definitions with the non-native exectur are still a template so that they will not be evaluated if not used. Hence this provides a
way to implement systems-specific code without using the preprocessor.
So given our example, the definitions with the non-native executor are still a template so that they will not be evaluated if not used. Hence this provides a
way to implement system-specific code without using the preprocessor.
[note If you only write a partial implementation, e.g. only for __posix_executor__, the other variants will default to __handler__].

View File

@@ -47,7 +47,7 @@ while (is >> file)
This will also deadlock, because the pipe does not close when the subprocess exits.
So the `ipstream` will still look for data even though the process has ended.
[note It is not possible to use automatically pipe-closing in this library, because
[note It is not possible to use automatic pipe-closing in this library, because
a pipe might be a file-handle (as for async pipes on windows).]
But, since pipes are buffered, you might get incomplete data if you do this:
@@ -64,7 +64,7 @@ while (c.running())
}
```
It is therefore highly recommended that you use the asynchronous api if you are
It is therefore highly recommended that you use the asynchronous API if you are
not absolutely sure how the output will look.
[endsect]

View File

@@ -30,7 +30,7 @@
[def asio_async_read [@http://www.boost.org/doc/libs/release/doc/html/boost_asio/reference/async_read.html boost::asio::async_read]]
[def bp::environment [classref boost::process::basic_environment bp::environment]]
[def bp::native_environment [classref boost::process::basic_native_environment bp::native_environment]]
[def boost::this_process::environment [funcref boost::this_process::environment boost::this_process:deadlock :environment]]
[def boost::this_process::environment [funcref boost::this_process::environment boost::this_process::environment]]
[def std::chrono::seconds [@http://en.cppreference.com/w/cpp/chrono/duration std::chrono::seconds]]
[def std::vector [@http://en.cppreference.com/w/cpp/container/vector std::vector]]
@@ -68,12 +68,12 @@ namespace bp = boost::process; //we will assume this for all further examples
int result = bp::system("g++ main.cpp");
```
If a single string (or the explicit form bp::cmd), it will be interpreted as a command line.
If a single string is given (or the explicit form bp::cmd), it will be interpreted as a command line.
That will cause the execution function to search the `PATH` variable to find the executable.
The alternative is the `exe-args` style, where the first string will be interpreted as a filename (including the path),
and the rest as arguments passed to said function.
[note For more details on the `cmd`/`exe-args` style look [link boost_process.design.arg_cmd_style here]]
[note For more details on the `cmd`/`exe-args` style look [link boost_process.design.arg_cmd_style here].]
So as a first step, we'll use the `exe-args` style.
@@ -141,7 +141,7 @@ things while the process is running and afterwards get the exit code. The call
to child_wait is necessary, to obtain it and tell the operating system, that no
one is waiting for the process anymore.
[note You can also wait for a time span or a until a time point with __wait_for__ and __wait_until__]
[note You can also wait for a time span or until a time point with __wait_for__ and __wait_until__.]
[warning If you don't call wait on a child object, it will be terminated on destruction.
This can be avoided by calling __detach__ beforehand]
@@ -217,7 +217,7 @@ std::vector<std::string> read_outline(std::string & file)
What this does is redirect the `stdout` of the process into a pipe and we read this
synchronously.
[note You can do the same thing with [globalref boost::process::std_err std_err]]
[note You can do the same thing with [globalref boost::process::std_err std_err].]
Now we get the name from `nm` and we might want to demangle it, so we use input and output.
`nm` has a demangle option, but for the sake of the example, we'll use
@@ -246,7 +246,7 @@ std::vector<std::string> read_demangled_outline(const std::string & file)
std::vector<std::string> outline;
//we just use the same pipe, so the
//we just use the same pipe, so the output of nm is directly passed as input to c++filt
bp::child nm(bp::search_path("nm"), file, bp::std_out > p);
bp::child filt(bp::search_path("c++filt"), bp::std_in < p, bp::std_out > is);
@@ -270,7 +270,7 @@ If you are familiar with [@http://www.boost.org/doc/libs/release/libs/asio/ boos
you can use [classref boost::process::async_pipe async_pipe] which is implemented
as an I/O-Object and can be used like [classref boost::process::pipe pipe] as shown above.
Now we get back to our compiling example. `nm` we might analyze it line by line,
Now we get back to our compiling example. For `nm` we might analyze the output line by line,
but the compiler output will just be put into one large buffer.
With [@http://www.boost.org/doc/libs/release/libs/asio/ boost.asio] this is what it looks like.
@@ -290,7 +290,7 @@ ios.run();
int result = c.exit_code();
```
To make it easier, boost.process provides simpler interface for that, so that the buffer can be passed directly,
To make it easier, boost.process provides a simpler interface for that, so that the buffer can be passed directly,
provided we also pass a reference to an io_service.
```
@@ -304,7 +304,7 @@ int result = c.exit_code();
```
[note Passing an instance of io_service to the launching function automatically cause it to wait asynchronously for the exit, so no call of
[memberref boost::process::child::wait wait] is needed]
[memberref boost::process::child::wait wait] is needed.]
To make it even easier, you can use [@http://en.cppreference.com/w/cpp/thread/future std::future] for asynchronous operations
(you will still need to pass a reference to a io_service) to the launching function, unless you use bp::system or bp::async_system.
@@ -331,7 +331,7 @@ auto err = data.get();
[endsect]
[section:group Groups]
When launching several processes, processes can be grouped together.
When launching several processes, they can be grouped together.
This will also apply for a child process, that launches other processes,
if they do not modify the group membership. E.g. if you call `make` which
launches other processes and call terminate on it,
@@ -342,7 +342,7 @@ The two main reasons to use groups are:
# Being able to terminate child processes of the child process
# Grouping several processes into one, just so they can be terminated at once
If we have program like `make`, which does launch its own child processes,
If we have a program like `make`, which does launch its own child processes,
a call of child_terminate might not suffice. I.e. if we have a makefile launching `gcc`
and use the following code, the `gcc` process will still run afterwards:
@@ -412,14 +412,14 @@ bp::system("stuff", env_);
```
A more convenient way to modify the environment for the child is the
[globalref boost::process::env env] property, which the example as following:
[globalref boost::process::env env] property, which can be used in the example as following:
```
bp::system("stuff", bp::env["VALUE_1"]="foo", bp::env["VALUE_2"]+={"bar1", "bar2"});
```
Please see to the [headerref boost/process/environment.hpp reference] for more information.
Please see the [headerref boost/process/environment.hpp reference] for more information.
[endsect]
[endsect]

View File

@@ -10,6 +10,9 @@
//[intro
#include <boost/process.hpp>
#include <string>
#include <iostream>
using namespace boost::process;
int main()

View File

@@ -138,7 +138,7 @@ struct args_
arg_setter_<char, true> operator()(std::initializer_list<const char*> &&range) const
{
return arg_setter_<char>(range.begin(), range.end());
return arg_setter_<char, true>(range.begin(), range.end());
}
arg_setter_<char, true> operator+=(std::initializer_list<const char*> &&range) const
{
@@ -146,11 +146,11 @@ struct args_
}
arg_setter_<char, false> operator= (std::initializer_list<const char*> &&range) const
{
return arg_setter_<char, true>(range.begin(), range.end());
return arg_setter_<char, false>(range.begin(), range.end());
}
arg_setter_<char, true> operator()(std::initializer_list<std::string> &&range) const
{
return arg_setter_<char>(range.begin(), range.end());
return arg_setter_<char, true>(range.begin(), range.end());
}
arg_setter_<char, true> operator+=(std::initializer_list<std::string> &&range) const
{
@@ -158,12 +158,12 @@ struct args_
}
arg_setter_<char, false> operator= (std::initializer_list<std::string> &&range) const
{
return arg_setter_<char, true>(range.begin(), range.end());
return arg_setter_<char, false>(range.begin(), range.end());
}
arg_setter_<wchar_t, true> operator()(std::initializer_list<const wchar_t*> &&range) const
{
return arg_setter_<wchar_t>(range.begin(), range.end());
return arg_setter_<wchar_t, true>(range.begin(), range.end());
}
arg_setter_<wchar_t, true> operator+=(std::initializer_list<const wchar_t*> &&range) const
{
@@ -171,11 +171,11 @@ struct args_
}
arg_setter_<wchar_t, false> operator= (std::initializer_list<const wchar_t*> &&range) const
{
return arg_setter_<wchar_t, true>(range.begin(), range.end());
return arg_setter_<wchar_t, false>(range.begin(), range.end());
}
arg_setter_<wchar_t, true> operator()(std::initializer_list<std::wstring> &&range) const
{
return arg_setter_<wchar_t>(range.begin(), range.end());
return arg_setter_<wchar_t, true>(range.begin(), range.end());
}
arg_setter_<wchar_t, true> operator+=(std::initializer_list<std::wstring> &&range) const
{
@@ -183,7 +183,7 @@ struct args_
}
arg_setter_<wchar_t, false> operator= (std::initializer_list<std::wstring> &&range) const
{
return arg_setter_<wchar_t, true>(range.begin(), range.end());
return arg_setter_<wchar_t, false>(range.begin(), range.end());
}
};

View File

@@ -109,10 +109,10 @@ with `function` being a callable object with the signature `(int, const std::err
\code{.cpp}
io_context ios;
child c("ls", on_exit=[](int exit, const std::error_code& ec_in){});
child c("ls", ios, on_exit=[](int exit, const std::error_code& ec_in){});
std::future<int> exit_code;
chlid c2("ls", on_exit=exit_code);
chlid c2("ls", ios, on_exit=exit_code);
\endcode

View File

@@ -43,7 +43,7 @@ namespace boost { namespace process { namespace detail {
struct cmd_
{
constexpr cmd_() {}
constexpr cmd_() = default;
template<typename Char>
inline api::cmd_setter_<Char> operator()(const Char *s) const

View File

@@ -60,7 +60,7 @@ public:
explicit child(child_handle &&ch, const std::shared_ptr<std::atomic<int>> &ptr) : _child_handle(std::move(ch)), _exit_status(ptr) {}
explicit child(child_handle &&ch) : _child_handle(std::move(ch)) {}
explicit child(pid_t & pid) : _child_handle(pid), _attached(false) {};
explicit child(pid_t pid) : _child_handle(pid), _attached(false) {};
child(const child&) = delete;
child(child && lhs) noexcept
: _child_handle(std::move(lhs._child_handle)),
@@ -73,7 +73,7 @@ public:
template<typename ...Args>
explicit child(Args&&...args);
child() {}
child() { } // Must be kept non defaulted for MSVC 14.1 & 14.2 #113
child& operator=(const child&) = delete;
child& operator=(child && lhs)
{

View File

@@ -7,6 +7,7 @@
#define BOOST_PROCESS_DETAIL_POSIX_ASIO_FWD_HPP_
#include <memory>
#include <boost/asio/ts/netfwd.hpp>
namespace boost { namespace asio {
@@ -20,39 +21,19 @@ template<typename Allocator>
class basic_streambuf;
typedef basic_streambuf<std::allocator<char>> streambuf;
class io_context;
class executor;
#if defined(BOOST_ASIO_ENABLE_OLD_SERVICES)
class signal_set_service;
template <typename SignalSetService>
class basic_signal_set;
typedef basic_signal_set<signal_set_service> signal_set;
#else /* defined(BOOST_ASIO_ENABLE_OLD_SERVICES) */
template <typename Executor>
class basic_signal_set;
typedef basic_signal_set<executor> signal_set;
#endif /* defined(BOOST_ASIO_ENABLE_OLD_SERVICES) */
typedef basic_signal_set<any_io_executor> signal_set;
template <typename Handler>
class basic_yield_context;
namespace posix {
#if defined(BOOST_ASIO_ENABLE_OLD_SERVICES)
class stream_descriptor_service;
template <typename StreamDesscriptorService>
class basic_stream_descriptor;
typedef basic_stream_descriptor<stream_descriptor_service> stream_descriptor;
#else /* defined(BOOST_ASIO_ENABLE_OLD_SERVICES) */
template <typename Executor>
class basic_stream_descriptor;
typedef basic_stream_descriptor<executor> stream_descriptor;
#endif /* defined(BOOST_ASIO_ENABLE_OLD_SERVICES) */
typedef basic_stream_descriptor<any_io_executor> stream_descriptor;
} //posix
} //asio

View File

@@ -25,6 +25,11 @@ public:
typedef ::boost::asio::posix::stream_descriptor handle_type;
typedef typename handle_type::executor_type executor_type;
executor_type get_executor()
{
return _source.get_executor();
}
inline async_pipe(boost::asio::io_context & ios) : async_pipe(ios, ios) {}
inline async_pipe(boost::asio::io_context & ios_source,
@@ -45,8 +50,8 @@ public:
inline async_pipe(const async_pipe& lhs);
async_pipe(async_pipe&& lhs) : _source(std::move(lhs._source)), _sink(std::move(lhs._sink))
{
lhs._source.assign (-1);
lhs._sink .assign (-1);
lhs._source = ::boost::asio::posix::stream_descriptor{lhs._source.get_executor()};
lhs._sink = ::boost::asio::posix::stream_descriptor{lhs._sink. get_executor()};
}
template<class CharT, class Traits = std::char_traits<CharT>>
@@ -71,10 +76,8 @@ public:
~async_pipe()
{
if (_sink .native_handle() != -1)
::close(_sink.native_handle());
if (_source.native_handle() != -1)
::close(_source.native_handle());
boost::system::error_code ec;
close(ec);
}
template<class CharT, class Traits = std::char_traits<CharT>>

View File

@@ -22,7 +22,7 @@ struct close_out : handler_base_ext
template <class Executor>
inline void on_exec_setup(Executor &e) const;
std::array<int, 2> get_used_handles() {return {p1 != -1 ? p1 : p2, p2 != -1 ? p2 : p1};}
std::array<int, 2> get_used_handles() {return {{p1 != -1 ? p1 : p2, p2 != -1 ? p2 : p1}};}
};
template<>

View File

@@ -56,6 +56,7 @@ public:
{
_buffer = _load();
_impl = _load_var(_buffer);
_env_impl = _impl.data();
}
string_type get(const pointer_type id) { return get(string_type(id)); }
@@ -230,7 +231,7 @@ template<typename Char>
inline auto basic_environment_impl<Char>::get(const string_type &id) -> string_type
{
auto itr = std::find_if(_data.begin(), _data.end(),
[&](const string_type & st)
[&](const string_type & st) -> bool
{
if (st.size() <= id.size())
return false;
@@ -250,7 +251,7 @@ template<typename Char>
inline void basic_environment_impl<Char>::set(const string_type &id, const string_type &value)
{
auto itr = std::find_if(_data.begin(), _data.end(),
[&](const string_type & st)
[&](const string_type & st) -> bool
{
if (st.size() <= id.size())
return false;
@@ -270,7 +271,7 @@ template<typename Char>
inline void basic_environment_impl<Char>::reset(const string_type &id)
{
auto itr = std::find_if(_data.begin(), _data.end(),
[&](const string_type & st)
[&](const string_type & st) -> bool
{
if (st.size() <= id.size())
return false;

View File

@@ -152,11 +152,10 @@ class executor
void write_error(const std::error_code & ec, const char * msg)
{
//I am the child
int len = ec.value();
::write(_pipe_sink, &len, sizeof(int));
const auto len = std::strlen(msg);
int data[2] = {ec.value(), len + 1};
len = std::strlen(msg) + 1;
::write(_pipe_sink, &len, sizeof(int));
::write(_pipe_sink, &data[0], sizeof(int) * 2);
::write(_pipe_sink, msg, len);
}
@@ -273,14 +272,15 @@ class executor
prepare_cmd_style_fn = exe;
if ((prepare_cmd_style_fn.find('/') == std::string::npos) && ::access(prepare_cmd_style_fn.c_str(), X_OK))
{
auto e = ::environ;
while ((*e != nullptr) && !boost::starts_with(*e, "PATH="))
const auto * e = ::environ;
while ((e != nullptr) && (*e != nullptr) && !boost::starts_with(*e, "PATH="))
e++;
if (e != nullptr)
if ((e != nullptr) && (*e != nullptr))
{
std::vector<std::string> path;
boost::split(path, *e, boost::is_any_of(":"));
//the beginning of the string contains "PATH="
boost::split(path, (*e) + 5, boost::is_any_of(":"));
for (const std::string & pp : path)
{

View File

@@ -26,7 +26,7 @@ struct file_in : handler_base_ext, ::boost::process::detail::uses_handles
std::array<int, 2> get_used_handles()
{
return {STDIN_FILENO, handle};
return {{STDIN_FILENO, handle}};
}
template<typename T>

View File

@@ -42,7 +42,7 @@ struct group_handle
}
void add(handle_t proc)
{
{
if (::setpgid(proc, grp))
throw_last_error();
}

View File

@@ -1,4 +1,4 @@
// Copyright (c) 2106 Klemens D. Morgenstern
// Copyright (c) 2016 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)

View File

@@ -25,7 +25,7 @@ struct null_in : handler_base_ext, ::boost::process::detail::uses_handles
std::array<int, 2> get_used_handles()
{
return {STDIN_FILENO, source.handle()};
return {{STDIN_FILENO, source.handle()}};
}

View File

@@ -6,26 +6,39 @@
#ifndef BOOST_PROCESS_POSIX_ON_EXIT_HPP_
#define BOOST_PROCESS_POSIX_ON_EXIT_HPP_
#include <boost/asio/execution.hpp>
#include <boost/process/async.hpp>
#include <boost/process/detail/config.hpp>
#include <boost/process/detail/handler_base.hpp>
#include <boost/process/detail/posix/async_handler.hpp>
#include <system_error>
#include <functional>
namespace boost { namespace process { namespace detail { namespace posix {
namespace boost { namespace process { namespace detail {
template<typename Tuple>
inline asio::io_context& get_io_context(const Tuple & tup);
namespace posix {
struct on_exit_ : boost::process::detail::posix::async_handler
{
std::function<void(int, const std::error_code&)> handler;
on_exit_(const std::function<void(int, const std::error_code&)> & handler) : handler(handler)
{
}
template<typename Executor>
std::function<void(int, const std::error_code&)> on_exit_handler(Executor&)
std::function<void(int, const std::error_code&)> on_exit_handler(Executor& exec)
{
return handler;
auto v = boost::asio::prefer(boost::process::detail::get_io_context(exec.seq).get_executor(),
boost::asio::execution::outstanding_work.tracked);
auto handler_ = this->handler;
return
[handler_, v](int exit_code, const std::error_code & ec)
{
handler_(exit_code, ec);
};
}
};

View File

@@ -27,7 +27,7 @@ struct pipe_in : handler_base_ext, ::boost::process::detail::uses_handles
std::array<int, 3> get_used_handles()
{
return {STDIN_FILENO, source, sink};
return {{STDIN_FILENO, source, sink}};
}

View File

@@ -20,7 +20,7 @@ namespace boost { namespace process { namespace detail { namespace posix {
class sigchld_service : public boost::asio::detail::service_base<sigchld_service>
{
boost::asio::io_context::strand _strand{get_io_context()};
boost::asio::strand<boost::asio::io_context::executor_type> _strand{get_io_context().get_executor()};
boost::asio::signal_set _signal_set{get_io_context(), SIGCHLD};
std::vector<std::pair<::pid_t, std::function<void(int, std::error_code)>>> _receivers;
@@ -48,9 +48,22 @@ public:
int status;
auto pid_res = ::waitpid(pid, &status, WNOHANG);
if (pid_res < 0)
h(-1, get_last_error());
{
auto ec = get_last_error();
boost::asio::post(
_strand,
[pid_res, ec, h]
{
h(pid_res, ec);
});
}
else if ((pid_res == pid) && (WIFEXITED(status) || WIFSIGNALED(status)))
h(status, {}); //successfully exited already
boost::asio::post(
_strand,
[status, h]
{
h(status, {}); //successfully exited already
});
else //still running
{
if (_receivers.empty())

View File

@@ -21,7 +21,7 @@ struct start_dir_init : handler_base_ext
{
typedef Char value_type;
typedef std::basic_string<value_type> string_type;
start_dir_init(const string_type &s) : s_(s) {}
start_dir_init(string_type s) : s_(std::move(s)) {}
template <class PosixExecutor>
void on_exec_setup(PosixExecutor&) const

View File

@@ -27,7 +27,7 @@ inline void terminate(const child_handle &p, std::error_code &ec) noexcept
ec.clear();
int status;
::waitpid(p.pid, &status, WNOHANG); //just to clean it up
::waitpid(p.pid, &status, 0); //should not be WNOHANG, since that would allow zombies.
}
inline void terminate(const child_handle &p)

View File

@@ -158,7 +158,7 @@ inline bool wait_until(
{
int res;
::kill(pid, SIGKILL);
::waitpid(pid, &res, WNOHANG);
::waitpid(pid, &res, 0);
}
};
child_cleaner_t child_cleaner{timeout_pid};

View File

@@ -7,6 +7,8 @@
#ifndef BOOST_PROCESS_DETAIL_TRAITS_WCHAR_T_HPP_
#define BOOST_PROCESS_DETAIL_TRAITS_WCHAR_T_HPP_
#include <algorithm>
#include <boost/process/detail/traits/decl.hpp>
#include <boost/process/detail/traits/cmd_or_exe.hpp>
#include <boost/process/detail/traits/env.hpp>

View File

@@ -7,6 +7,7 @@
#define BOOST_PROCESS_DETAIL_WINDOWS_ASIO_FWD_HPP_
#include <memory>
#include <boost/asio/ts/netfwd.hpp>
namespace boost { namespace asio {
@@ -19,43 +20,19 @@ template<typename Allocator>
class basic_streambuf;
typedef basic_streambuf<std::allocator<char>> streambuf;
class io_context;
class executor;
template <typename Handler>
class basic_yield_context;
namespace windows {
#if defined(BOOST_ASIO_ENABLE_OLD_SERVICES)
class stream_handle_service;
template <typename StreamHandleService>
class basic_stream_handle;
typedef basic_stream_handle<stream_handle_service> stream_handle;
#else /* defined(BOOST_ASIO_ENABLE_OLD_SERVICES) */
template <typename Executor>
class basic_stream_handle;
typedef basic_stream_handle<executor> stream_handle;
typedef basic_stream_handle<any_io_executor> stream_handle;
#endif /* defined(BOOST_ASIO_ENABLE_OLD_SERVICES) */
#if defined(BOOST_ASIO_ENABLE_OLD_SERVICES)
class object_handle_service;
template <typename ObjectHandleService>
class basic_object_handle;
typedef basic_object_handle<object_handle_service> object_handle;
#else /* defined(BOOST_ASIO_ENABLE_OLD_SERVICES) */
template <typename Executor>
class basic_object_handle;
typedef basic_object_handle<executor> object_handle;
#endif /* defined(BOOST_ASIO_ENABLE_OLD_SERVICES) */
typedef basic_object_handle<any_io_executor> object_handle;
} //windows
} //asio

View File

@@ -30,14 +30,17 @@ inline std::string build_args(const std::string & exe, std::vector<std::string>
{
std::string st = exe;
//put in quotes if it has spaces
//put in quotes if it has spaces or double quotes
if(!exe.empty())
{
boost::replace_all(st, "\"", "\\\"");
auto it = st.find_first_of(" \"");
auto it = std::find(st.begin(), st.end(), ' ');
if (it != st.end())//contains spaces.
if(it != st.npos)//contains spaces.
{
// double existing quotes
boost::replace_all(st, "\"", "\"\"");
// surround with quotes
st.insert(st.begin(), '"');
st += '"';
}
@@ -45,15 +48,18 @@ inline std::string build_args(const std::string & exe, std::vector<std::string>
for (auto & arg : data)
{
boost::replace_all(arg, "\"", "\\\"");
auto it = std::find(arg.begin(), arg.end(), ' ');//contains space?
if (it != arg.end())//ok, contains spaces.
if(!arg.empty())
{
//the first one is put directly onto the output,
//because then I don't have to copy the whole string
arg.insert(arg.begin(), '"');
arg += '"'; //thats the post one.
auto it = arg.find_first_of(" \"");//contains space or double quotes?
if(it != arg.npos)//yes
{
// double existing quotes
boost::replace_all(arg, "\"", "\"\"");
// surround with quotes
arg.insert(arg.begin(), '"');
arg += '"';
}
}
if (!st.empty())//first one does not need a preceeding space
@@ -68,30 +74,36 @@ inline std::wstring build_args(const std::wstring & exe, std::vector<std::wstrin
{
std::wstring st = exe;
//put in quotes if it has spaces
//put in quotes if it has spaces or double quotes
if(!exe.empty())
{
boost::replace_all(st, L"\"", L"\\\"");
auto it = st.find_first_of(L" \"");
auto it = std::find(st.begin(), st.end(), L' ');
if (it != st.end())//contains spaces.
if(it != st.npos)//contains spaces or double quotes.
{
// double existing quotes
boost::replace_all(st, L"\"", L"\"\"");
// surround with quotes
st.insert(st.begin(), L'"');
st += L'"';
}
}
for (auto & arg : data)
for(auto & arg : data)
{
boost::replace_all(arg, L"\"", L"\\\"");
auto it = std::find(arg.begin(), arg.end(), L' ');//contains space?
if (it != arg.end())//ok, contains spaces.
if(!arg.empty())
{
//the first one is put directly onto the output,
//because then I don't have to copy the whole string
arg.insert(arg.begin(), L'"');
arg += L'"'; //thats the post one.
auto it = arg.find_first_of(L" \"");//contains space or double quotes?
if(it != arg.npos)//yes
{
// double existing quotes
boost::replace_all(arg, L"\"", L"\"\"");
// surround with quotes
arg.insert(arg.begin(), L'"');
arg += '"';
}
}
if (!st.empty())//first one does not need a preceeding space

View File

@@ -17,7 +17,7 @@
namespace boost { namespace process { namespace detail { namespace windows {
typedef int pid_t;
typedef ::boost::winapi::DWORD_ pid_t;
struct child_handle
{
@@ -66,7 +66,7 @@ struct child_handle
pid_t id() const
{
return static_cast<int>(proc_info.dwProcessId);
return static_cast<pid_t>(proc_info.dwProcessId);
}
typedef ::boost::winapi::HANDLE_ process_handle_t;

View File

@@ -102,6 +102,7 @@ struct file_descriptor
if (_handle != ::boost::winapi::INVALID_HANDLE_VALUE_)
::boost::winapi::CloseHandle(_handle);
_handle = boost::exchange(other._handle, ::boost::winapi::INVALID_HANDLE_VALUE_);
return *this;
}
~file_descriptor()

View File

@@ -12,7 +12,7 @@
//#define BOOST_USE_WINDOWS_H 1
#if defined( BOOST_USE_WINDOWS_H )
#include <Winternl.h>
#include <winternl.h>
#endif
@@ -198,20 +198,20 @@ typedef struct _OBJECT_TYPE_INFORMATION_ {
/*
__kernel_entry NTSTATUS NtQuerySystemInformation(
NTSTATUS NtQuerySystemInformation(
IN SYSTEM_INFORMATION_CLASS SystemInformationClass,
OUT PVOID SystemInformation,
IN ULONG SystemInformationLength,
OUT PULONG ReturnLength
);
*/
typedef ::boost::winapi::NTSTATUS_ (__kernel_entry *nt_system_query_information_p )(
typedef ::boost::winapi::NTSTATUS_ (*nt_system_query_information_p )(
SYSTEM_INFORMATION_CLASS_,
void *,
::boost::winapi::ULONG_,
::boost::winapi::PULONG_);
/*
__kernel_entry NTSYSCALLAPI NTSTATUS NtQueryObject(
NTSYSCALLAPI NTSTATUS NtQueryObject(
HANDLE Handle,
OBJECT_INFORMATION_CLASS ObjectInformationClass,
PVOID ObjectInformation,
@@ -220,7 +220,7 @@ __kernel_entry NTSYSCALLAPI NTSTATUS NtQueryObject(
);
*/
typedef ::boost::winapi::NTSTATUS_ (__kernel_entry *nt_query_object_p )(
typedef ::boost::winapi::NTSTATUS_ (*nt_query_object_p )(
::boost::winapi::HANDLE_,
OBJECT_INFORMATION_CLASS_,
void *,

View File

@@ -136,7 +136,7 @@ struct limit_handles_ : handler_base_ext
[&](::boost::winapi::HANDLE_ handle)
{
auto itr = std::find(all_handles.begin(), all_handles .end(), handle);
DWORD flags = 0u;
::boost::winapi::DWORD_ flags = 0u;
if (itr != all_handles.end())
*itr = ::boost::winapi::INVALID_HANDLE_VALUE_;
else if ((::boost::winapi::GetHandleInformation(*itr, &flags) != 0)

View File

@@ -8,7 +8,9 @@
#include <boost/process/detail/handler_base.hpp>
#include <boost/process/detail/windows/async_handler.hpp>
#include <boost/process/detail/windows/is_running.hpp>
#include <boost/asio/io_context.hpp>
#include <boost/asio/post.hpp>
#include <boost/asio/windows/object_handle.hpp>
#include <boost/winapi/process.hpp>
#include <boost/winapi/handles.hpp>
@@ -114,6 +116,15 @@ struct io_context_ref : boost::process::detail::handler_base
wait_handler wh(std::move(funcs), ios, process_handle, exec.exit_status);
::boost::winapi::DWORD_ code;
if(::boost::winapi::GetExitCodeProcess(process_handle, &code)
&& code != still_active)
{
::boost::asio::post(wh.handle->get_executor(), std::move(wh));
return;
}
auto handle_p = wh.handle.get();
handle_p->async_wait(std::move(wh));
}
@@ -130,12 +141,13 @@ struct io_context_ref : boost::process::detail::handler_base
boost::asio::io_context & ios, void * handle,
const std::shared_ptr<std::atomic<int>> &exit_status)
: funcs(std::move(funcs)),
handle(new boost::asio::windows::object_handle(ios.get_executor(), handle)),
handle(new boost::asio::windows::object_handle(
asio::prefer(ios.get_executor(), asio::execution::outstanding_work.tracked), handle)),
exit_status(exit_status)
{
}
void operator()(const boost::system::error_code & ec_in)
void operator()(const boost::system::error_code & ec_in = {})
{
std::error_code ec;
if (ec_in)

View File

@@ -1,4 +1,4 @@
// Copyright (c) 2106 Klemens D. Morgenstern
// Copyright (c) 2016 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)
@@ -7,6 +7,7 @@
#define BOOST_PROCESS_WINDOWS_IS_RUNNING_HPP
#include <boost/process/detail/config.hpp>
#include <boost/process/detail/windows/child_handle.hpp>
#include <system_error>
#include <cstdlib>
#include <boost/winapi/process.hpp>

View File

@@ -6,13 +6,20 @@
#ifndef BOOST_PROCESS_WINDOWS_ON_EXIT_HPP_
#define BOOST_PROCESS_WINDOWS_ON_EXIT_HPP_
#include <boost/process/async.hpp>
#include <boost/process/detail/config.hpp>
#include <boost/process/detail/handler_base.hpp>
#include <boost/process/detail/windows/async_handler.hpp>
#include <boost/asio/execution.hpp>
#include <system_error>
#include <functional>
namespace boost { namespace process { namespace detail { namespace windows {
namespace boost { namespace process { namespace detail {
template<typename Tuple>
inline asio::io_context& get_io_context(const Tuple & tup);
namespace windows {
struct on_exit_ : boost::process::detail::windows::async_handler
{
@@ -23,10 +30,12 @@ struct on_exit_ : boost::process::detail::windows::async_handler
}
template<typename Executor>
std::function<void(int, const std::error_code&)> on_exit_handler(Executor&)
std::function<void(int, const std::error_code&)> on_exit_handler(Executor& exec)
{
auto v = boost::asio::prefer(boost::process::detail::get_io_context(exec.seq).get_executor(),
boost::asio::execution::outstanding_work.tracked);
auto handler_ = this->handler;
return [handler_](int exit_code, const std::error_code & ec)
return [v, handler_](int exit_code, const std::error_code & ec)
{
handler_(static_cast<int>(exit_code), ec);
};

View File

@@ -677,11 +677,11 @@ inline std::vector<boost::filesystem::path> path()
#if defined(BOOST_WINDOWS_API)
const ::boost::process::wnative_environment ne{};
typedef typename ::boost::process::wnative_environment::const_entry_type value_type;
const auto id = L"PATH";
static constexpr auto id = L"PATH";
#else
const ::boost::process::native_environment ne{};
typedef typename ::boost::process::native_environment::const_entry_type value_type;
const auto id = "PATH";
static constexpr auto id = "PATH";
#endif
auto itr = std::find_if(ne.cbegin(), ne.cend(),

View File

@@ -59,7 +59,7 @@ namespace detail {
struct throw_on_error_ : ::boost::process::detail::api::handler_base_ext
{
constexpr throw_on_error_() {};
constexpr throw_on_error_() = default;
template <class Executor>
void on_error(Executor&, const std::error_code & ec) const
@@ -72,7 +72,7 @@ struct throw_on_error_ : ::boost::process::detail::api::handler_base_ext
struct ignore_error_ : ::boost::process::detail::api::handler_base_ext
{
constexpr ignore_error_() {};
constexpr ignore_error_() = default;
};
struct set_on_error : ::boost::process::detail::api::handler_base_ext
@@ -81,7 +81,7 @@ struct set_on_error : ::boost::process::detail::api::handler_base_ext
explicit set_on_error(std::error_code &ec) : ec_(ec) {}
template <class Executor>
void on_error(Executor&, const std::error_code & ec) const
void on_error(Executor&, const std::error_code & ec) const noexcept
{
ec_ = ec;
}
@@ -92,7 +92,7 @@ private:
struct error_
{
constexpr error_() {}
constexpr error_() = default;
set_on_error operator()(std::error_code &ec) const {return set_on_error(ec);}
set_on_error operator= (std::error_code &ec) const {return set_on_error(ec);}

View File

@@ -8,6 +8,7 @@
#include <boost/process/detail/handler.hpp>
#include <boost/process/detail/used_handles.hpp>
#include <memory>
#if defined(BOOST_WINDOWS_API)
#include <boost/process/detail/windows/executor.hpp>

View File

@@ -136,13 +136,13 @@ template<typename T> using is_mutable_buffer =
>;
struct null_t {constexpr null_t() {}};
struct null_t {constexpr null_t() = default;};
struct close_t;
template<class>
struct std_in_
{
constexpr std_in_() {}
constexpr std_in_() = default;
api::close_in close() const {return api::close_in(); }
api::close_in operator=(const close_t &) const {return api::close_in();}
@@ -199,7 +199,7 @@ struct std_in_
template<int p1, int p2 = -1>
struct std_out_
{
constexpr std_out_() {}
constexpr std_out_() = default;
api::close_out<p1,p2> close() const {return api::close_out<p1,p2>(); }
api::close_out<p1,p2> operator=(const close_t &) const {return api::close_out<p1,p2>();}
@@ -260,7 +260,7 @@ struct std_out_
struct close_t
{
constexpr close_t() {}
constexpr close_t() = default;
template<int T, int U>
api::close_out<T,U> operator()(std_out_<T,U>) {return api::close_out<T,U>();}
};

View File

@@ -28,7 +28,7 @@ namespace detail
class codecvt_category_t : public std::error_category
{
public:
codecvt_category_t(){}
codecvt_category_t() = default;
const char* name() const noexcept override {return "codecvt";}
std::string message(int ev) const override
{
@@ -76,9 +76,8 @@ inline std::locale default_locale()
std::locale global_loc = std::locale();
return std::locale(global_loc, new std::codecvt_utf8<wchar_t>);
# else // Other POSIX
// ISO C calls std::locale("") "the locale-specific native environment", and this
// locale is the default for many POSIX-based operating systems such as Linux.
return std::locale("");
// Return a default locale object.
return std::locale();
# endif
}

View File

@@ -123,8 +123,8 @@ struct basic_pipebuf : std::basic_streambuf<CharT, Traits>
///Destructor -> writes the frest of the data
~basic_pipebuf()
{
if (is_open())
overflow(Traits::eof());
if (basic_pipebuf::is_open())
basic_pipebuf::overflow(Traits::eof());
}
///Move construct from a pipe.
@@ -167,10 +167,12 @@ struct basic_pipebuf : std::basic_streambuf<CharT, Traits>
if (this->pptr() == this->epptr())
{
bool wr = this->_write_impl();
*this->pptr() = ch;
this->pbump(1);
if (wr)
{
*this->pptr() = ch;
this->pbump(1);
return ch;
}
}
else
{
@@ -277,7 +279,7 @@ private:
else if (wrt == 0) //broken pipe
return false;
this->pbump(-wrt);
this->pbump(static_cast<int>(-wrt));
return true;
}

View File

@@ -11,5 +11,6 @@
"description": "Library to create processes in a portable way.",
"category": [
"System"
]
],
"cxxstd": "11"
}

View File

@@ -13,6 +13,7 @@
init:
- set BRANCH_TO_TEST=%APPVEYOR_REPO_BRANCH%
- set BOOST_REMOVE=process
- set BOOST_TEST_LOG_LEVEL=success
os: Visual Studio 2015
configuration: Debug

View File

@@ -182,8 +182,8 @@ BOOST_AUTO_TEST_CASE(async_wait_different_contexts, *boost::unit_test::timeout(1
BOOST_REQUIRE(!ec);
// Regression test for #143: make sure each io_context handles its own children
std::thread thr1{[&]{io_context1.run();}};
std::thread thr2{[&]{io_context2.run();}};
std::thread thr1{[&]() noexcept {io_context1.run();}};
std::thread thr2{[&]() noexcept {io_context2.run();}};
thr1.join();
thr2.join();
@@ -214,7 +214,7 @@ BOOST_AUTO_TEST_CASE(async_wait_abort, *boost::unit_test::timeout(5))
int exit_code = 0;
bp::child c(
master_test_suite().argv[1],
"test", "--abort",
"test", "exit-code", "42",
ec,
io_context,
bp::on_exit([&](int exit, const std::error_code& ec_in)
@@ -232,7 +232,7 @@ BOOST_AUTO_TEST_CASE(async_wait_abort, *boost::unit_test::timeout(5))
io_context.run();
BOOST_CHECK(exit_called);
BOOST_CHECK_NE(exit_code, 0);
BOOST_CHECK_NE(exit_code, 42);
BOOST_CHECK_EQUAL(c.exit_code(), exit_code);
}
@@ -293,7 +293,7 @@ BOOST_AUTO_TEST_CASE(async_out_stream, *boost::unit_test::timeout(5))
std::string line;
std::getline(istr, line);
BOOST_REQUIRE_GE(line.size(), 3);
BOOST_REQUIRE_GE(line.size(), 3u);
BOOST_CHECK(boost::algorithm::starts_with(line, "abc"));
c.wait();
}
@@ -413,4 +413,5 @@ BOOST_AUTO_TEST_CASE(mixed_async, *boost::unit_test::timeout(5))
}*/
BOOST_AUTO_TEST_SUITE_END();
BOOST_AUTO_TEST_SUITE_END();

View File

@@ -57,8 +57,7 @@ BOOST_AUTO_TEST_CASE(future_error, *boost::unit_test::timeout(15))
ios.run();
int exit_code = 0;
BOOST_CHECK_THROW(exit_code = fut.get(), boost::system::system_error);
BOOST_CHECK_THROW(fut.get(), boost::system::system_error);
}
BOOST_AUTO_TEST_SUITE_END();
BOOST_AUTO_TEST_SUITE_END();

View File

@@ -41,9 +41,9 @@ BOOST_AUTO_TEST_CASE(stackful, *boost::unit_test::timeout(15))
bp::async_system(
ios, yield_,
master_test_suite().argv[1],
"test", "--exit-code", "123");
"test", "--wait", "1");
BOOST_CHECK_EQUAL(ret, 123);
BOOST_CHECK_EQUAL(ret, 0);
BOOST_CHECK(did_something_else);
};

View File

@@ -53,7 +53,7 @@ BOOST_AUTO_TEST_CASE(stackless, *boost::unit_test::timeout(15))
master_test_suite().argv[1],
"test", "--exit-code", "42");
BOOST_CHECK_EQUAL(exit_code, 42);
BOOST_CHECK_EQUAL(exit_code, 42u);
BOOST_CHECK(did_something_else);
}
}
@@ -68,4 +68,4 @@ BOOST_AUTO_TEST_CASE(stackless, *boost::unit_test::timeout(15))
}
BOOST_AUTO_TEST_SUITE_END();
BOOST_AUTO_TEST_SUITE_END();

View File

@@ -29,7 +29,6 @@ struct run_exe
{
e.exe = exe.c_str();
}
};
struct set_on_error
@@ -74,13 +73,11 @@ struct overload_handler : ex::handler
void on_setup(ex::windows_executor<Char, Sequence>& exec) const
{
st = "windows";
const char* env = exec.env;
}
template <class Sequence>
void on_setup(ex::posix_executor<Sequence>& exec) const
{
st = "posix";
char** env = exec.env;
}
};

View File

@@ -57,7 +57,7 @@ BOOST_AUTO_TEST_CASE(leak_test, *boost::unit_test::timeout(5))
BOOST_CHECK(bt::is_stream_handle(get_handle(stderr), ec)); BOOST_CHECK_MESSAGE(!ec, ec.message());
BOOST_CHECK_GE(fd_list.size(), 3);
BOOST_CHECK_GE(fd_list.size(), 3u);
BOOST_CHECK_GE(bt::get_handles(ec).size(), fd_list.size());
bp::pipe p;
@@ -66,14 +66,14 @@ BOOST_AUTO_TEST_CASE(leak_test, *boost::unit_test::timeout(5))
auto fd_list_new = bt::get_handles(ec);
BOOST_CHECK_MESSAGE(!ec, ec);
BOOST_CHECK_LE(fd_list.size() + 2, fd_list_new.size());
BOOST_CHECK_LE(fd_list.size() + 2u, fd_list_new.size());
fd_list = std::move(fd_list_new);
}
BOOST_CHECK_EQUAL(std::count(fd_list.begin(), fd_list.end(), p.native_source()), 1);
BOOST_CHECK_EQUAL(std::count(fd_list.begin(), fd_list.end(), p.native_sink()), 1);
BOOST_CHECK_EQUAL(std::count(fd_list.begin(), fd_list.end(), p.native_source()), 1u);
BOOST_CHECK_EQUAL(std::count(fd_list.begin(), fd_list.end(), p.native_sink()), 1u);
BOOST_CHECK(bt::is_stream_handle(p.native_source(), ec)); BOOST_CHECK_MESSAGE(!ec, ec.message());
BOOST_CHECK(bt::is_stream_handle(p.native_sink(), ec)); BOOST_CHECK_MESSAGE(!ec, ec.message());
@@ -82,8 +82,8 @@ BOOST_AUTO_TEST_CASE(leak_test, *boost::unit_test::timeout(5))
p.close();
fd_list = bt::get_handles(ec);
BOOST_CHECK_EQUAL(std::count(fd_list.begin(), fd_list.end(), p.native_source()), 0);
BOOST_CHECK_EQUAL(std::count(fd_list.begin(), fd_list.end(), p.native_sink()), 0);
BOOST_CHECK_EQUAL(std::count(fd_list.begin(), fd_list.end(), p.native_source()), 0u);
BOOST_CHECK_EQUAL(std::count(fd_list.begin(), fd_list.end(), p.native_sink()), 0u);
#if defined( BOOST_WINDOWS_API )
std::thread thr([]{});
@@ -128,7 +128,6 @@ struct on_setup_t
bp::extend::foreach_used_handle(e, [this](bt::native_handle_type handle)
{
res.push_back(handle);
std::cout << "Pushing " << handle << std::endl;
});
}
};
@@ -155,9 +154,9 @@ BOOST_AUTO_TEST_CASE(iterate_handles, *boost::unit_test::timeout(5))
BOOST_CHECK_MESSAGE(!ec, ec.message());
BOOST_CHECK_EQUAL(ret, 42);
BOOST_CHECK_EQUAL(std::count(res.begin(), res.end(), p_in. native_sink()), 0);
BOOST_CHECK_EQUAL(std::count(res.begin(), res.end(), p_out.native_source()), 0);
BOOST_CHECK_EQUAL(ret, 42u);
BOOST_CHECK_EQUAL(std::count(res.begin(), res.end(), p_in. native_sink()), 0u);
BOOST_CHECK_EQUAL(std::count(res.begin(), res.end(), p_out.native_source()), 0u);
}
BOOST_AUTO_TEST_CASE(limit_fd, *boost::unit_test::timeout(5))
@@ -168,12 +167,14 @@ BOOST_AUTO_TEST_CASE(limit_fd, *boost::unit_test::timeout(5))
const auto get_handle = [](FILE * f){return std::to_string(fileno(f));};
#endif
auto p = fopen("./test-file", "w");
using boost::unit_test::framework::master_test_suite;
BOOST_CHECK_EQUAL(bp::system(master_test_suite().argv[1], "--has-handle", get_handle(stdout), bp::std_err > stderr), EXIT_SUCCESS);
BOOST_CHECK_EQUAL(bp::system(master_test_suite().argv[1], "--has-handle", get_handle(stderr), bp::std_err > stderr), EXIT_SUCCESS);
BOOST_CHECK_EQUAL(bp::system(master_test_suite().argv[1], "--has-handle", bp::limit_handles, get_handle(p), bp::std_in < p), EXIT_SUCCESS);
BOOST_CHECK_EQUAL(bp::system(master_test_suite().argv[1], "--has-handle", bp::limit_handles, get_handle(p), bp::std_err > p), EXIT_SUCCESS);
BOOST_CHECK_EQUAL(bp::system(master_test_suite().argv[1], "--has-handle", bp::limit_handles, get_handle(p), bp::std_out > p), EXIT_SUCCESS);
BOOST_CHECK_EQUAL(bp::system(master_test_suite().argv[1], "--has-handle", bp::limit_handles, get_handle(p)), EXIT_FAILURE);
BOOST_CHECK_EQUAL(bp::system(master_test_suite().argv[1], "--has-handle", get_handle(stdout), bp::std_err > stderr, bp::limit_handles), EXIT_FAILURE);
BOOST_CHECK_EQUAL(bp::system(master_test_suite().argv[1], "--has-handle", get_handle(stderr), bp::std_err > stderr, bp::limit_handles), EXIT_SUCCESS);
}
fclose(p);
}

0
test/test-file Normal file
View File

View File

@@ -33,7 +33,7 @@ BOOST_AUTO_TEST_CASE(wargs, *boost::unit_test::timeout(2))
std::error_code ec;
bp::child c(
master_test_suite().argv[1],
L"test", "--echo-argv", L"hello thingy", "\"stuff\"", static_cast<const wchar_t*>(L" spa ce "),
L"test", "--echo-argv", L"hello thingy", "\"stuff\"", static_cast<const wchar_t*>(L" spa\" ce "),
bp::std_out>is,
ec
);
@@ -62,9 +62,9 @@ BOOST_AUTO_TEST_CASE(wargs, *boost::unit_test::timeout(2))
BOOST_CHECK_EQUAL(s, "\"stuff\"");
std::getline(is, s);
s.resize(10);
s.resize(11);
BOOST_CHECK_EQUAL(s, " spa ce ");
BOOST_CHECK_EQUAL(s, " spa\" ce ");
}