diff --git a/.gitignore b/.gitignore index 084c81f9..4354fdab 100644 --- a/.gitignore +++ b/.gitignore @@ -21,3 +21,14 @@ *.app /bin /*/bin +/doc/autodoc.xml +/doc/html +/sparring_log.txt +/.cproject +/.project +/Doxyfile + +/notes.cpp +/notes_p.txt +.settings + diff --git a/.travis.yml b/.travis.yml index 6ea74d90..67fe8e2e 100644 --- a/.travis.yml +++ b/.travis.yml @@ -65,14 +65,17 @@ before_install: - BOOST=$HOME/boost-local - git init $BOOST - cd $BOOST - - git remote add --no-tags -t $BRANCH_TO_TEST origin https://github.com/boostorg/boost.git + - if [ $(BRANCH_TO_TEST) = "master" ]; then + BOOST_BRANCH=master; + else BOOST_BRANCH=develop; fi + - git remote add --no-tags -t $BOOST_BRANCH origin https://github.com/boostorg/boost.git - git fetch --depth=1 - - git checkout $BRANCH_TO_TEST + - git checkout $BOOST_BRANCH - git submodule update --init --merge - - git remote set-branches --add origin $BRANCH_TO_TEST + - git remote set-branches --add origin $BOOST_BRANCH - git pull --recurse-submodules - git submodule update --init - - git checkout $BRANCH_TO_TEST + - git checkout $BOOST_BRANCH - git submodule foreach "git reset --quiet --hard; git clean -fxd" - git reset --hard; git clean -fxd - git status diff --git a/include/boost/process/args.hpp b/include/boost/process/args.hpp index c1b4a56d..7b75468c 100644 --- a/include/boost/process/args.hpp +++ b/include/boost/process/args.hpp @@ -22,8 +22,6 @@ namespace boost { namespace process { namespace detail { - - struct args_ { template @@ -31,111 +29,152 @@ struct args_ template using value_type = typename remove_reference_t::value_type; + template + using vvalue_type = value_type>; + template - arg_setter_, true> operator()(Range &&range) const + arg_setter_, true> operator()(Range &&range) const { - return arg_setter_, true>(std::forward(range)); + return arg_setter_, true>(std::forward(range)); } template - arg_setter_, true> operator+=(Range &&range) const + arg_setter_, true> operator+=(Range &&range) const { - return arg_setter_, true>(std::forward(range)); + return arg_setter_, true>(std::forward(range)); } template - arg_setter_, false> operator= (Range &&range) const + arg_setter_, false> operator= (Range &&range) const { - return arg_setter_, false>(std::forward(range)); + return arg_setter_, false>(std::forward(range)); } - arg_setter_ operator()(std::string && str) const + template + arg_setter_ operator()(std::basic_string && str) const { - return arg_setter_ (str); + return arg_setter_ (str); } - arg_setter_ operator+=(std::string && str) const + template + arg_setter_ operator+=(std::basic_string && str) const { - return arg_setter_ (str); + return arg_setter_ (str); } - arg_setter_ operator= (std::string && str) const + template + arg_setter_ operator= (std::basic_string && str) const { - return arg_setter_(str); + return arg_setter_(str); } - arg_setter_ operator()(const std::string & str) const + template + arg_setter_ operator()(const std::basic_string & str) const { - return arg_setter_ (str); + return arg_setter_ (str); } - arg_setter_ operator+=(const std::string & str) const + template + arg_setter_ operator+=(const std::basic_string & str) const { - return arg_setter_ (str); + return arg_setter_ (str); } - arg_setter_ operator= (const std::string & str) const + template + arg_setter_ operator= (const std::basic_string & str) const { - return arg_setter_(str); + return arg_setter_(str); } - arg_setter_ operator()(std::string & str) const + template + arg_setter_ operator()(std::basic_string & str) const { - return arg_setter_ (str); + return arg_setter_ (str); } - arg_setter_ operator+=(std::string & str) const + template + arg_setter_ operator+=(std::basic_string & str) const { - return arg_setter_ (str); + return arg_setter_ (str); } - arg_setter_ operator= (std::string & str) const + template + arg_setter_ operator= (std::basic_string & str) const { - return arg_setter_(str); + return arg_setter_(str); } - arg_setter_ operator()(const char* str) const + template + arg_setter_ operator()(const Char* str) const { - return arg_setter_ (str); + return arg_setter_ (str); } - arg_setter_ operator+=(const char* str) const + template + arg_setter_ operator+=(const Char* str) const { - return arg_setter_ (str); + return arg_setter_ (str); } - arg_setter_ operator= (const char* str) const + template + arg_setter_ operator= (const Char* str) const { - return arg_setter_(str); + return arg_setter_(str); } - template - arg_setter_ operator()(const char (&str) [Size]) const +// template +// arg_setter_ operator()(const Char (&str) [Size]) const +// { +// return arg_setter_ (str); +// } +// template +// arg_setter_ operator+=(const Char (&str) [Size]) const +// { +// return arg_setter_ (str); +// } +// template +// arg_setter_ operator= (const Char (&str) [Size]) const +// { +// return arg_setter_(str); +// } + + arg_setter_ operator()(std::initializer_list &&range) const { - return arg_setter_ (str); + return arg_setter_(range.begin(), range.end()); } - template - arg_setter_ operator+=(const char (&str) [Size]) const + arg_setter_ operator+=(std::initializer_list &&range) const { - return arg_setter_ (str); + return arg_setter_(range.begin(), range.end()); } - template - arg_setter_ operator= (const char (&str) [Size]) const + arg_setter_ operator= (std::initializer_list &&range) const { - return arg_setter_(str); + return arg_setter_(range.begin(), range.end()); + } + arg_setter_ operator()(std::initializer_list &&range) const + { + return arg_setter_(range.begin(), range.end()); + } + arg_setter_ operator+=(std::initializer_list &&range) const + { + return arg_setter_(range.begin(), range.end()); + } + arg_setter_ operator= (std::initializer_list &&range) const + { + return arg_setter_(range.begin(), range.end()); } - arg_setter_ operator()(std::initializer_list &&range) const + arg_setter_ operator()(std::initializer_list &&range) const { - return arg_setter_(range.begin(), range.end()); + return arg_setter_(range.begin(), range.end()); } - arg_setter_ operator+=(std::initializer_list &&range) const + arg_setter_ operator+=(std::initializer_list &&range) const { - return arg_setter_(range.begin(), range.end()); + return arg_setter_(range.begin(), range.end()); } - arg_setter_ operator= (std::initializer_list &&range) const + arg_setter_ operator= (std::initializer_list &&range) const { - return arg_setter_(range.begin(), range.end()); + return arg_setter_(range.begin(), range.end()); } - arg_setter_ operator()(std::initializer_list &&range) const + arg_setter_ operator()(std::initializer_list &&range) const { - return arg_setter_(range.begin(), range.end()); + return arg_setter_(range.begin(), range.end()); } - arg_setter_ operator+=(std::initializer_list &&range) const + arg_setter_ operator+=(std::initializer_list &&range) const { - return arg_setter_(range.begin(), range.end()); + return arg_setter_(range.begin(), range.end()); } - arg_setter_ operator= (std::initializer_list &&range) const + arg_setter_ operator= (std::initializer_list &&range) const { - return arg_setter_(range.begin(), range.end()); + return arg_setter_(range.begin(), range.end()); } }; + } constexpr boost::process::detail::args_ args{}; diff --git a/include/boost/process/cmd.hpp b/include/boost/process/cmd.hpp index d731929e..66686622 100644 --- a/include/boost/process/cmd.hpp +++ b/include/boost/process/cmd.hpp @@ -15,6 +15,7 @@ #include #include #include +#include #if defined(BOOST_POSIX_API) #include @@ -28,8 +29,6 @@ * */ - - namespace boost { namespace process { namespace detail { @@ -37,27 +36,57 @@ struct cmd_ { constexpr cmd_() {} - inline api::cmd_setter_ operator()(const char *s) const + template + inline api::cmd_setter_ operator()(const Char *s) const { - return api::cmd_setter_(s); + return api::cmd_setter_(s); } - inline api::cmd_setter_ operator= (const char *s) const + template + inline api::cmd_setter_ operator= (const Char *s) const { - return api::cmd_setter_(s); + return api::cmd_setter_(s); } - inline api::cmd_setter_ operator()(const std::string &s) const + template + inline api::cmd_setter_ operator()(const std::basic_string &s) const { - return api::cmd_setter_(s); + return api::cmd_setter_(s); } - inline api::cmd_setter_ operator= (const std::string &s) const + template + inline api::cmd_setter_ operator= (const std::basic_string &s) const { - return api::cmd_setter_(s); + return api::cmd_setter_(s); } }; +template<> struct is_wchar_t> : std::true_type {}; + + + +template<> +struct char_converter> +{ + static api::cmd_setter_ conv(const api::cmd_setter_ & in) + { + return { ::boost::process::detail::convert(in.str()) }; + } +}; + +template<> +struct char_converter> +{ + static api::cmd_setter_ conv(const api::cmd_setter_ & in) + { + return { ::boost::process::detail::convert(in.str()) }; + } +}; + + + constexpr static cmd_ cmd; + + } using boost::process::detail::cmd; diff --git a/include/boost/process/detail/basic_cmd.hpp b/include/boost/process/detail/basic_cmd.hpp index a1879568..1e84c648 100644 --- a/include/boost/process/detail/basic_cmd.hpp +++ b/include/boost/process/detail/basic_cmd.hpp @@ -11,6 +11,7 @@ #include #include +#include #if defined( BOOST_WINDOWS_API ) #include @@ -27,23 +28,49 @@ namespace boost { namespace process { namespace detail { +template struct exe_setter_ { - std::string exe_; - exe_setter_(std::string && str) : exe_(std::move(str)) {} - exe_setter_(const std::string & str) : exe_(str) {} + typedef Char value_type; + typedef std::basic_string string_type; + + string_type exe_; + exe_setter_(string_type && str) : exe_(std::move(str)) {} + exe_setter_(const string_type & str) : exe_(str) {} +}; + +template<> struct is_wchar_t> : std::true_type {}; +template<> +struct char_converter, exe_setter_> +{ + static exe_setter_ conv(const exe_setter_ & in) + { + return {::boost::process::detail::convert(in.exe_)}; + } +}; + +template<> +struct char_converter, exe_setter_> +{ + static exe_setter_ conv(const exe_setter_ & in) + { + return {::boost::process::detail::convert(in.exe_)}; + } }; -template + +template struct arg_setter_ { - std::vector _args; + using value_type = Char; + using string_type = std::basic_string; + std::vector _args; - typedef typename std::vector::iterator iterator; - typedef typename std::vector::const_iterator const_iterator; + typedef typename std::vector::iterator iterator; + typedef typename std::vector::const_iterator const_iterator; template arg_setter_(Iterator && begin, Iterator && end) : _args(begin, end) {} @@ -57,45 +84,107 @@ struct arg_setter_ iterator end() {return _args.end();} const_iterator begin() const {return _args.begin();} const_iterator end() const {return _args.end();} - arg_setter_(std::string & str) : _args{{str}} {} - arg_setter_(std::string && s) : _args({std::move(s)}) {} - arg_setter_(const std::string & s) : _args({s}) {} - arg_setter_(const char* s) : _args({std::move(s)}) {} + arg_setter_(string_type & str) : _args{{str}} {} + arg_setter_(string_type && s) : _args({std::move(s)}) {} + arg_setter_(const string_type & s) : _args({s}) {} + arg_setter_(const value_type* s) : _args({std::move(s)}) {} template - arg_setter_(const char (&s) [Size]) : _args({s}) {} + arg_setter_(const value_type (&s) [Size]) : _args({s}) {} +}; + +template<> struct is_wchar_t> : std::true_type {}; +template<> struct is_wchar_t> : std::true_type {}; + +template<> +struct char_converter> +{ + static arg_setter_ conv(const arg_setter_ & in) + { + std::vector vec(in._args.size()); + std::transform(in._args.begin(), in._args.end(), vec.begin(), + [](const std::wstring & ws) + { + return ::boost::process::detail::convert(ws); + }); + return {vec}; + } +}; + +template<> +struct char_converter> +{ + static arg_setter_ conv(const arg_setter_ & in) + { + std::vector vec(in._args.size()); + std::transform(in._args.begin(), in._args.end(), vec.begin(), + [](const std::string & ws) + { + return ::boost::process::detail::convert(ws); + }); + + return {vec}; + } +}; + +template<> +struct char_converter> +{ + static arg_setter_ conv(const arg_setter_ & in) + { + std::vector vec(in._args.size()); + std::transform(in._args.begin(), in._args.end(), vec.begin(), + [](const std::wstring & ws) + { + return ::boost::process::detail::convert(ws); + }); + return {vec}; } +}; + +template<> +struct char_converter> +{ + static arg_setter_ conv2(const arg_setter_ & in) + { + std::vector vec(in._args.size()); + std::transform(in._args.begin(), in._args.end(), vec.begin(), + [](const std::string & ws) + { + return ::boost::process::detail::convert(ws); + }); + return {vec}; + } }; using api::exe_cmd_init; +template struct exe_builder { //set by path, because that will not be interpreted as a cmd bool not_cmd = false; - bool shell = false; - std::string exe; - std::vector args; - - const char* get_exe () const { return exe.c_str();} - const char* get_cmd_line() const { return nullptr;} //TODO: Implement. + bool shell = false; + using string_type = std::basic_string; + string_type exe; + std::vector args; void operator()(const boost::filesystem::path & data) { not_cmd = true; if (exe.empty()) - exe = data.string(); + exe = data.native(); else - args.push_back(data.string()); + args.push_back(data.native()); } - void operator()(const std::string & data) + void operator()(const string_type & data) { if (exe.empty()) exe = data; else args.push_back(data); } - void operator()(const char* data) + void operator()(const Char* data) { if (exe.empty()) exe = data; @@ -103,7 +192,7 @@ struct exe_builder args.push_back(data); } void operator()(shell_) {shell = true;} - void operator()(std::vector && data) + void operator()(std::vector && data) { if (data.empty()) return; @@ -119,7 +208,7 @@ struct exe_builder args.insert(args.end(), itr, end); } - void operator()(const std::vector & data) + void operator()(const std::vector & data) { if (data.empty()) return; @@ -134,67 +223,67 @@ struct exe_builder } args.insert(args.end(), itr, end); } - void operator()(exe_setter_ && data) + void operator()(exe_setter_ && data) { not_cmd = true; exe = std::move(data.exe_); } - void operator()(const exe_setter_ & data) + void operator()(const exe_setter_ & data) { not_cmd = true; exe = data.exe_; } - template - void operator()(arg_setter_ && data) + void operator()(arg_setter_ && data) { args.assign( std::make_move_iterator(data._args.begin()), std::make_move_iterator(data._args.end())); } - template - void operator()(arg_setter_ && data) + void operator()(arg_setter_ && data) { args.insert(args.end(), std::make_move_iterator(data._args.begin()), std::make_move_iterator(data._args.end())); } - template - void operator()(const arg_setter_ & data) + void operator()(const arg_setter_ & data) { args.assign(data._args.begin(), data._args.end()); } - template - void operator()(const arg_setter_ & data) + void operator()(const arg_setter_ & data) { args.insert(args.end(), data._args.begin(), data._args.end()); } - api::exe_cmd_init get_initializer() + api::exe_cmd_init get_initializer() { if (not_cmd || !args.empty()) { if (shell) - return api::exe_cmd_init::exe_args_shell(std::move(exe), std::move(args)); + return api::exe_cmd_init::exe_args_shell(std::move(exe), std::move(args)); else - return api::exe_cmd_init::exe_args(std::move(exe), std::move(args)); + return api::exe_cmd_init::exe_args(std::move(exe), std::move(args)); } else if (shell) - return api::exe_cmd_init::cmd_shell(std::move(exe)); + return api::exe_cmd_init::cmd_shell(std::move(exe)); else - return api::exe_cmd_init::cmd(std::move(exe)); + return api::exe_cmd_init::cmd(std::move(exe)); } - typedef api::exe_cmd_init result_type; + typedef api::exe_cmd_init result_type; }; template<> -struct initializer_builder +struct initializer_builder> { - typedef exe_builder type; + typedef exe_builder type; }; - +template<> +struct initializer_builder> +{ + typedef exe_builder type; +}; }}} diff --git a/include/boost/process/detail/config.hpp b/include/boost/process/detail/config.hpp index c32f086a..ceb9478e 100644 --- a/include/boost/process/detail/config.hpp +++ b/include/boost/process/detail/config.hpp @@ -72,22 +72,23 @@ inline void throw_last_error() throw std::system_error(get_last_error()); } -} - - - template constexpr static Char null_char(); template<> constexpr char null_char (){return '\0';} template<> constexpr wchar_t null_char (){return L'\0';} -template<> constexpr char16_t null_char (){return u'\0';} -template<> constexpr char32_t null_char (){return U'\0';} template constexpr static Char equal_sign(); template<> constexpr char equal_sign () {return '='; } template<> constexpr wchar_t equal_sign () {return L'='; } -template<> constexpr char16_t equal_sign() {return u'='; } -template<> constexpr char32_t equal_sign() {return U'='; } -}} +template constexpr static Char quote_sign(); +template<> constexpr char quote_sign () {return '"'; } +template<> constexpr wchar_t quote_sign () {return L'"'; } + +template constexpr static Char space_sign(); +template<> constexpr char space_sign () {return ' '; } +template<> constexpr wchar_t space_sign () {return L' '; } + + +}}} #endif diff --git a/include/boost/process/detail/execute_impl.hpp b/include/boost/process/detail/execute_impl.hpp index 326725fd..88127ddc 100644 --- a/include/boost/process/detail/execute_impl.hpp +++ b/include/boost/process/detail/execute_impl.hpp @@ -37,7 +37,6 @@ #include #include - #include #include @@ -45,9 +44,70 @@ namespace boost { namespace process { class child; - namespace detail { + +template +struct has_wchar; + +template +struct has_wchar +{ + typedef has_wchar next; + typedef typename std::remove_cv< + typename std::remove_reference::type>::type res_type; + + constexpr static bool my_value = is_wchar_t::value; + constexpr static bool value = my_value || next::value; + + typedef std::integral_constant type; +}; + +template +struct has_wchar +{ + typedef typename std::remove_cv< + typename std::remove_reference::type>::type res_type; + + constexpr static bool value = is_wchar_t::value; + + typedef std::integral_constant type; +}; + + +#if defined(BOOST_WINDOWS_API) +//everything needs to be wchar_t +#if defined(BOOST_NO_ANSI_APIS) +template +struct required_char_type +{ + typedef wchar_t type; +}; +#else +template struct required_char_type; +template<> struct required_char_type +{ + typedef wchar_t type; +}; +template<> struct required_char_type +{ + typedef char type; +}; +#endif + +#elif defined(BOOST_POSIX_API) +template +struct required_char_type +{ + typedef char type; +}; +#endif + +template +using required_char_type_t = typename required_char_type< + has_wchar::value>::type; + + template struct make_builders_from_view { @@ -161,11 +221,8 @@ inline boost::fusion::tuple::type...> } - - - -template -inline child execute_impl(Args&& ... args) +template +inline child basic_execute_impl(Args && ... args) { //create a tuple from the argument list boost::fusion::tuple::type&...> tup(args...); @@ -201,16 +258,26 @@ inline child execute_impl(Args&& ... args) detail::builder_ref builder_ref(builders); boost::fusion::for_each(others, builder_ref); - auto other_inits = ::boost::process::detail::get_initializers(builders); boost::fusion::joint_view complete_inits(other_inits, inits); - auto exec = boost::process::detail::api::make_executor(complete_inits); + auto exec = boost::process::detail::api::make_executor(complete_inits); return exec(); } +template +inline child execute_impl(Args&& ... args) +{ + typedef required_char_type_t req_char_type; + + return basic_execute_impl( + boost::process::detail::char_converter_t::conv( + std::forward(args))... + ); +} + }}} diff --git a/include/boost/process/detail/posix/async_in.hpp b/include/boost/process/detail/posix/async_in.hpp index 8e5ec780..57660f2b 100644 --- a/include/boost/process/detail/posix/async_in.hpp +++ b/include/boost/process/detail/posix/async_in.hpp @@ -28,7 +28,7 @@ struct async_in_buffer : ::boost::process::detail::posix::async_handler Buffer & buf; std::shared_ptr> promise; - async_in_buffer operator<(std::future & fut) + async_in_buffer operator>(std::future & fut) { promise = std::make_shared>(); fut = promise->get_future(); return std::move(*this); diff --git a/include/boost/process/detail/posix/basic_cmd.hpp b/include/boost/process/detail/posix/basic_cmd.hpp index 87354cc7..9779e019 100644 --- a/include/boost/process/detail/posix/basic_cmd.hpp +++ b/include/boost/process/detail/posix/basic_cmd.hpp @@ -97,9 +97,11 @@ inline std::vector build_args(const std::string & data) return st; } +template +struct exe_cmd_init; - -struct exe_cmd_init : boost::process::detail::api::handler_base_ext +template<> +struct exe_cmd_init : boost::process::detail::api::handler_base_ext { exe_cmd_init(const exe_cmd_init & ) = delete; exe_cmd_init(exe_cmd_init && ) = default; @@ -152,7 +154,7 @@ private: std::vector cmd_impl; }; -std::vector exe_cmd_init::make_cmd() +std::vector exe_cmd_init::make_cmd() { std::vector vec; if (!exe.empty()) diff --git a/include/boost/process/detail/posix/basic_pipe.hpp b/include/boost/process/detail/posix/basic_pipe.hpp index 9c8dab29..c6ffb90a 100644 --- a/include/boost/process/detail/posix/basic_pipe.hpp +++ b/include/boost/process/detail/posix/basic_pipe.hpp @@ -169,6 +169,19 @@ basic_pipe::basic_pipe(const std::string & name) ::unlink(name.c_str()); } +template +inline bool operator==(const basic_pipe & lhs, const basic_pipe & rhs) +{ + return compare_handles(lhs.native_source(), rhs.native_source()) && + compare_handles(lhs.native_sink(), rhs.native_sink()); +} + +template +inline bool operator!=(const basic_pipe & lhs, const basic_pipe & rhs) +{ + return !compare_handles(lhs.native_source(), rhs.native_source()) || + !compare_handles(lhs.native_sink(), rhs.native_sink()); +} }}}} diff --git a/include/boost/process/detail/posix/cmd.hpp b/include/boost/process/detail/posix/cmd.hpp index 7b99dd16..0c662388 100644 --- a/include/boost/process/detail/posix/cmd.hpp +++ b/include/boost/process/detail/posix/cmd.hpp @@ -7,6 +7,7 @@ #ifndef BOOST_PROCESS_DETAIL_POSIX_CMD_HPP_ #define BOOST_PROCESS_DETAIL_POSIX_CMD_HPP_ +#include #include #include #include @@ -22,19 +23,19 @@ namespace posix -inline std::vector build_cmd(const std::string & value) +template +inline std::vector> build_cmd(const std::basic_string & value) { - std::vector ret; - + std::vector> ret; - bool in_quotes = false; - auto beg = value.begin(); + bool in_quotes = false; + auto beg = value.begin(); for (auto itr = value.begin(); itr != value.end(); itr++) { - if (*itr == '"') + if (*itr == quote_sign()) in_quotes = !in_quotes; - if (!in_quotes && (*itr == ' ')) + if (!in_quotes && (*itr == space_sign())) { if (itr != beg) { @@ -49,25 +50,45 @@ inline std::vector build_cmd(const std::string & value) return ret; } +template struct cmd_setter_ : handler_base_ext { - cmd_setter_(std::string && cmd_line) : _cmd_line(api::build_cmd(std::move(cmd_line))) {} - cmd_setter_(const std::string & cmd_line) : _cmd_line(api::build_cmd(cmd_line)) {} + typedef Char value_type; + typedef std::basic_string string_type; + + cmd_setter_(string_type && cmd_line) : _cmd_line(api::build_cmd(std::move(cmd_line))) {} + cmd_setter_(const string_type & cmd_line) : _cmd_line(api::build_cmd(cmd_line)) {} template void on_setup(Executor& exec) { exec.cmd_line = &_cmd_impl.front(); } + string_type str() const + { + string_type ret; + std::size_t size = 0; + for (auto & cmd : _cmd_line) + size += cmd.size() + 1; + ret.reserve(size -1); + + for (auto & cmd : _cmd_line) + { + if (!ret.empty()) + ret += equal_sign(); + ret += cmd; + } + return ret; + } private: - static inline std::vector make_cmd(std::vector & args); - std::vector _cmd_line; - std::vector _cmd_impl = make_cmd(_cmd_line); + static inline std::vector make_cmd(std::vector & args); + std::vector _cmd_line; + std::vector _cmd_impl = make_cmd(_cmd_line); }; - -std::vector cmd_setter_::make_cmd(std::vector & args) +template +std::vector cmd_setter_::make_cmd(std::vector> & args) { - std::vector vec; + std::vector vec; for (auto & v : args) vec.push_back(&v.front()); diff --git a/include/boost/process/detail/posix/compare_handles.hpp b/include/boost/process/detail/posix/compare_handles.hpp index aa484907..63475721 100644 --- a/include/boost/process/detail/posix/compare_handles.hpp +++ b/include/boost/process/detail/posix/compare_handles.hpp @@ -18,6 +18,12 @@ namespace boost { namespace process { namespace detail { namespace posix { inline bool compare_handles(int lhs, int rhs) { + if ((lhs == -1) || (rhs == -1)) + return false; + + if (lhs == rhs) + return true; + struct stat stat1, stat2; if(fstat(lhs, &stat1) < 0) ::boost::process::detail::throw_last_error("fstat() failed"); if(fstat(rhs, &stat2) < 0) ::boost::process::detail::throw_last_error("fstat() failed"); diff --git a/include/boost/process/detail/posix/env_init.hpp b/include/boost/process/detail/posix/env_init.hpp index d212b3ac..4d01cdad 100644 --- a/include/boost/process/detail/posix/env_init.hpp +++ b/include/boost/process/detail/posix/env_init.hpp @@ -14,7 +14,11 @@ namespace boost { namespace process { namespace detail { namespace posix { -struct env_init : handler_base_ext +template +struct env_init; + +template<> +struct env_init : handler_base_ext { boost::process::environment env; diff --git a/include/boost/process/detail/posix/environment.hpp b/include/boost/process/detail/posix/environment.hpp index 5a76ba3a..613821b9 100644 --- a/include/boost/process/detail/posix/environment.hpp +++ b/include/boost/process/detail/posix/environment.hpp @@ -12,25 +12,116 @@ #include #include #include +#include namespace boost { namespace process { namespace detail { namespace posix { template -struct native_environment_impl +class native_environment_impl { - + static std::vector> _load() + { + std::vector> val; + auto p = environ; + while (*p != nullptr) + { + std::string str = *p; + val.push_back(::boost::process::detail::convert(str)); + p++; + } + return val; + } + static std::vector _load_var(std::vector> & vec) + { + std::vector val; + val.resize(vec.size() + 1); + std::transform(vec.begin(), vec.end(), val.begin(), + [](std::basic_string & str) + { + return &str.front(); + }); + val.back() = nullptr; + return val; + } + std::vector> _buffer = _load(); + std::vector _impl = _load_var(_buffer); public: using char_type = Char; using pointer_type = const char_type*; using string_type = std::basic_string; - using native_handle_type = char **; + using native_handle_type = char_type **; + + void reload() + { + _buffer = _load(); + _impl = _load_var(_buffer); + } + + string_type get(const pointer_type id) { return get(string_type(id)); } + void set(const pointer_type id, const pointer_type value) + { + set(string_type(id), string_type(value)); + } + void reset(const pointer_type id) { reset(string_type(id)); } + + string_type get(const string_type & id) + { + std::string id_c = ::boost::process::detail::convert(id); + std::string g = ::getenv(id_c.c_str()); + return ::boost::process::detail::convert(g.c_str()); + } + void set(const string_type & id, const string_type & value) + { + std::string id_c = ::boost::process::detail::convert(id.c_str()); + std::string value_c = ::boost::process::detail::convert(value.c_str()); + auto res = ::setenv(id_c.c_str(), value_c.c_str(), true); + if (res != 0) + boost::process::detail::throw_last_error(); + } + void reset(const string_type & id) + { + std::string id_c = ::boost::process::detail::convert(id.c_str()); + auto res = ::unsetenv(id_c.c_str()); + if (res != 0) + ::boost::process::detail::throw_last_error(); + } + + native_environment_impl() = default; + native_environment_impl(const native_environment_impl& ) = delete; + native_environment_impl(native_environment_impl && ) = default; + native_environment_impl & operator=(const native_environment_impl& ) = delete; + native_environment_impl & operator=(native_environment_impl && ) = default; + native_handle_type _env_impl = _impl.data(); + + native_handle_type native_handle() const {return environ;} +}; + +template<> +class native_environment_impl +{ +public: + using char_type = char; + using pointer_type = const char_type*; + using string_type = std::basic_string; + using native_handle_type = char_type **; void reload() {} - string_type get(const pointer_type id); - void set(const pointer_type id, const pointer_type value); - void reset(const pointer_type id); + string_type get(const pointer_type id) { return getenv(id); } + void set(const pointer_type id, const pointer_type value) + { + auto val = std::string(id) + "=" + value; + auto res = ::setenv(id, value, true); + if (res != 0) + boost::process::detail::throw_last_error(); + } + void reset(const pointer_type id) + { + auto res = ::unsetenv(id); + if (res != 0) + boost::process::detail::throw_last_error(); + } string_type get(const string_type & id) {return get(id.c_str());} void set(const string_type & id, const string_type & value) {set(id.c_str(), value.c_str()); } @@ -46,29 +137,6 @@ public: native_handle_type native_handle() const {return environ;} }; -template -inline auto native_environment_impl::get(const pointer_type id) -> string_type -{ - return getenv(id); -} - -template -inline void native_environment_impl::set(const pointer_type id, const pointer_type value) -{ - auto val = std::string(id) + "=" + value; - auto res = setenv(id, value, true); - if (res != 0) - boost::process::detail::throw_last_error(); -} - -template -inline void native_environment_impl::reset(const pointer_type id) -{ - auto res = unsetenv(id); - if (res != 0) - boost::process::detail::throw_last_error(); -} - template @@ -113,6 +181,31 @@ public: } basic_environment_impl & operator=(basic_environment_impl && ) = default; + template + explicit inline basic_environment_impl( + const basic_environment_impl& rhs, + const ::boost::process::codecvt_type & cv = ::boost::process::codecvt()) + : _data(rhs._data.size()) + { + std::transform(rhs._data.begin(), rhs._data.end(), _data.begin(), + [&](const std::basic_string & st) + { + return ::boost::process::detail::convert(st, cv); + } + + ); + reload(); + } + + template + basic_environment_impl & operator=(const basic_environment_impl& rhs) + { + _data = ::boost::process::detail::convert(rhs._data); + _env_arr = _load_var(&*_data.begin()); + _env_impl = &*_env_arr.begin(); + return *this; + } + Char ** _env_impl = &*_env_arr.data(); native_handle_type native_handle() const {return &_data.front();} @@ -166,11 +259,11 @@ inline void basic_environment_impl::set(const string_type &id, const strin if (itr != _data.end()) { - *itr = id + '=' + value; + *itr = id + equal_sign() + value; } else { - _data.push_back(id + '=' + value); + _data.push_back(id + equal_sign() + value); } reload(); diff --git a/include/boost/process/detail/posix/executor.hpp b/include/boost/process/detail/posix/executor.hpp index 2ac03153..bfdf89b2 100644 --- a/include/boost/process/detail/posix/executor.hpp +++ b/include/boost/process/detail/posix/executor.hpp @@ -257,7 +257,7 @@ public: Sequence & seq; const char * exe = nullptr; char *const* cmd_line = nullptr; - char **env = nullptr; + char **env = ::environ; pid_t pid = -1; std::shared_ptr> exit_status = std::make_shared>(still_active); @@ -462,7 +462,7 @@ child executor::invoke(boost::mpl::false_, boost::mpl::true_) #endif -template +template inline executor make_executor(Tup & tup) { return executor(tup); diff --git a/include/boost/process/detail/posix/start_dir.hpp b/include/boost/process/detail/posix/start_dir.hpp index 8b2ee111..1b73172c 100644 --- a/include/boost/process/detail/posix/start_dir.hpp +++ b/include/boost/process/detail/posix/start_dir.hpp @@ -16,20 +16,21 @@ namespace boost { namespace process { namespace detail { namespace posix { - +template struct start_dir_init : handler_base_ext { -public: - explicit start_dir_init(const std::string &s) : s_(s) {} + typedef Char value_type; + typedef std::basic_string string_type; + start_dir_init(const string_type &s) : s_(s) {} template void on_exec_setup(PosixExecutor&) const { ::chdir(s_.c_str()); } - + const string_type & str() const {return s_;} private: - std::string s_; + string_type s_; }; }}}} diff --git a/include/boost/process/detail/traits.hpp b/include/boost/process/detail/traits.hpp index 70fd9091..7ed16107 100644 --- a/include/boost/process/detail/traits.hpp +++ b/include/boost/process/detail/traits.hpp @@ -12,5 +12,6 @@ #include #include #include +#include #endif /* BOOST_PROCESS_TRAITS_HPP_ */ diff --git a/include/boost/process/detail/traits/cmd_or_exe.hpp b/include/boost/process/detail/traits/cmd_or_exe.hpp index 2642b9ca..00217480 100644 --- a/include/boost/process/detail/traits/cmd_or_exe.hpp +++ b/include/boost/process/detail/traits/cmd_or_exe.hpp @@ -1,9 +1,8 @@ -/* - * string_traits.hpp - * - * Created on: 05.03.2016 - * Author: Klemens - */ +// 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) + #ifndef BOOST_PROCESS_DETAIL_TRAITS_CMD_OR_EXE_HPP_ #define BOOST_PROCESS_DETAIL_TRAITS_CMD_OR_EXE_HPP_ @@ -14,75 +13,66 @@ #include #include #include - namespace boost { namespace process { namespace detail { +template struct cmd_or_exe_tag {}; + struct shell_; -template<> struct initializer_tag { typedef cmd_or_exe_tag type;}; -template<> struct initializer_tag { typedef cmd_or_exe_tag type;}; -template<> struct initializer_tag { typedef cmd_or_exe_tag type;}; -template<> struct initializer_tag { typedef cmd_or_exe_tag type;}; -template<> struct initializer_tag { typedef cmd_or_exe_tag type;}; +template<> struct initializer_tag { typedef cmd_or_exe_tag type;}; +template<> struct initializer_tag { typedef cmd_or_exe_tag type;}; +template<> struct initializer_tag { typedef cmd_or_exe_tag type;}; +template<> struct initializer_tag { typedef cmd_or_exe_tag type;}; -template<> struct initializer_tag { typedef cmd_or_exe_tag type;}; -template<> struct initializer_tag { typedef cmd_or_exe_tag type;}; -template<> struct initializer_tag { typedef cmd_or_exe_tag type;}; -template<> struct initializer_tag { typedef cmd_or_exe_tag type;}; +template struct initializer_tag { typedef cmd_or_exe_tag type;}; +template struct initializer_tag { typedef cmd_or_exe_tag type;}; -template struct initializer_tag { typedef cmd_or_exe_tag type;}; -template struct initializer_tag { typedef cmd_or_exe_tag type;}; -template struct initializer_tag { typedef cmd_or_exe_tag type;}; -template struct initializer_tag { typedef cmd_or_exe_tag type;}; +template struct initializer_tag { typedef cmd_or_exe_tag type;}; +template struct initializer_tag { typedef cmd_or_exe_tag type;}; -template struct initializer_tag { typedef cmd_or_exe_tag type;}; -template struct initializer_tag { typedef cmd_or_exe_tag type;}; -template struct initializer_tag { typedef cmd_or_exe_tag type;}; -template struct initializer_tag { typedef cmd_or_exe_tag type;}; +template<> struct initializer_tag> { typedef cmd_or_exe_tag type;}; +template<> struct initializer_tag> { typedef cmd_or_exe_tag type;}; +template<> struct initializer_tag>> { typedef cmd_or_exe_tag type;}; +template<> struct initializer_tag>> { typedef cmd_or_exe_tag type;}; -template<> struct initializer_tag> { typedef cmd_or_exe_tag type;}; -template<> struct initializer_tag> { typedef cmd_or_exe_tag type;}; -template<> struct initializer_tag> { typedef cmd_or_exe_tag type;}; -template<> struct initializer_tag> { typedef cmd_or_exe_tag type;}; +template<> struct initializer_tag>> { typedef cmd_or_exe_tag type;}; +template<> struct initializer_tag>> { typedef cmd_or_exe_tag type;}; +template<> struct initializer_tag> { typedef cmd_or_exe_tag type;}; +template<> struct initializer_tag> { typedef cmd_or_exe_tag type;}; -template<> struct initializer_tag>> { typedef cmd_or_exe_tag type;}; -template<> struct initializer_tag>> { typedef cmd_or_exe_tag type;}; -template<> struct initializer_tag>> { typedef cmd_or_exe_tag type;}; -template<> struct initializer_tag>> { typedef cmd_or_exe_tag type;}; +template<> struct initializer_tag> { typedef cmd_or_exe_tag type;}; +template<> struct initializer_tag> { typedef cmd_or_exe_tag type;}; -template<> struct initializer_tag>> { typedef cmd_or_exe_tag type;}; -template<> struct initializer_tag>> { typedef cmd_or_exe_tag type;}; -template<> struct initializer_tag>> { typedef cmd_or_exe_tag type;}; -template<> struct initializer_tag>> { typedef cmd_or_exe_tag type;}; +template<> struct initializer_tag +{ + typedef cmd_or_exe_tag type; +}; +template<> struct initializer_tag +{ + typedef cmd_or_exe_tag type; +}; -template<> struct initializer_tag> { typedef cmd_or_exe_tag type;}; -template<> struct initializer_tag> { typedef cmd_or_exe_tag type;}; -template<> struct initializer_tag> { typedef cmd_or_exe_tag type;}; -template<> struct initializer_tag> { typedef cmd_or_exe_tag type;}; - -template<> struct initializer_tag> { typedef cmd_or_exe_tag type;}; -template<> struct initializer_tag> { typedef cmd_or_exe_tag type;}; -template<> struct initializer_tag> { typedef cmd_or_exe_tag type;}; -template<> struct initializer_tag> { typedef cmd_or_exe_tag type;}; - -template<> struct initializer_tag { typedef cmd_or_exe_tag type; }; - +template struct exe_setter_; -template +template struct arg_setter_; -template -struct initializer_tag> { typedef cmd_or_exe_tag type;}; -template<> struct initializer_tag { typedef cmd_or_exe_tag type;}; +template +struct initializer_tag> { typedef cmd_or_exe_tag type;}; + +template struct initializer_tag> { typedef cmd_or_exe_tag type;}; template<> -struct initializer_builder; +struct initializer_builder>; + +template<> +struct initializer_builder>; }}} diff --git a/include/boost/process/detail/traits/env.hpp b/include/boost/process/detail/traits/env.hpp index 584491f1..5418e9e8 100644 --- a/include/boost/process/detail/traits/env.hpp +++ b/include/boost/process/detail/traits/env.hpp @@ -12,51 +12,38 @@ namespace boost { namespace process { -template class Implementation> +template class basic_environment; +template +class basic_native_environment; + namespace detail { +template struct env_tag {}; -#if defined(BOOST_POSIX_API) -namespace posix -{ -template struct native_environment_impl; -template struct basic_environment_impl; -} - -#elif defined(BOOST_WINDOWS_API) -namespace windows -{ -template struct native_environment_impl; -template struct basic_environment_impl; -} -#endif - -template struct env_set; -template struct env_append; - -struct env_reset; -struct env_init; +template struct env_set; +template struct env_append; -template struct initializer_tag> { typedef env_tag type; }; -template struct initializer_tag> { typedef env_tag type; }; - -template<> struct initializer_tag { typedef env_tag type;}; -template<> struct initializer_tag { typedef env_tag type;}; - -template class Implementation> -struct initializer_tag> { typedef env_tag type; }; -template<> struct initializer_tag<::boost::process::basic_environment> { typedef env_tag type; }; -template<> struct initializer_tag<::boost::process::basic_environment> { typedef env_tag type; }; +template struct env_reset; +template struct env_init; -template<> -struct initializer_builder; +template struct initializer_tag> { typedef env_tag type; }; +template struct initializer_tag> { typedef env_tag type; }; + +template struct initializer_tag> { typedef env_tag type;}; +template struct initializer_tag> { typedef env_tag type;}; + +template struct initializer_tag<::boost::process::basic_environment> { typedef env_tag type; }; +template struct initializer_tag<::boost::process::basic_native_environment> { typedef env_tag type; }; + +template<> struct initializer_builder>; +template<> struct initializer_builder>; } diff --git a/include/boost/process/detail/traits/wchar_t.hpp b/include/boost/process/detail/traits/wchar_t.hpp new file mode 100644 index 00000000..817227fc --- /dev/null +++ b/include/boost/process/detail/traits/wchar_t.hpp @@ -0,0 +1,274 @@ +// 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) + + +#ifndef BOOST_PROCESS_DETAIL_TRAITS_WCHAR_T_HPP_ +#define BOOST_PROCESS_DETAIL_TRAITS_WCHAR_T_HPP_ + +#include +#include +#include +#include + +namespace boost { namespace process { namespace detail { + +//template + +template struct is_wchar_t : std::false_type {}; + +template<> struct is_wchar_t : std::is_same +{ +}; + +template<> struct is_wchar_t : std::true_type {}; + +template<> struct is_wchar_t { typedef std::true_type type;}; + +template struct is_wchar_t : std::true_type {}; +template struct is_wchar_t : std::true_type {}; + +template<> struct is_wchar_t : std::true_type {}; +template<> struct is_wchar_t> : std::true_type {}; +template<> struct is_wchar_t> : std::true_type {}; +template<> struct is_wchar_t> : std::true_type {}; +template<> struct is_wchar_t> : std::true_type {}; + + + +template +struct char_converter +{ + static T& conv(T & in) + { + return in; + } + static T&& conv(T&& in) + { + return std::move(in); + } + static const T& conv(const T & in) + { + return in; + } +}; + +template +using char_converter_t = char_converter::type>::type>; + + +template<> +struct char_converter +{ + static std::string conv(const wchar_t* in) + { + std::size_t size = 0; + while (in[size] != L'\0') size++; + return ::boost::process::detail::convert(in, in + size); + } +}; + +template<> +struct char_converter +{ + static std::string conv(wchar_t* in) + { + std::size_t size = 0; + while (in[size] != L'\0') size++; + return ::boost::process::detail::convert(in, in + size); + } +}; + +template +struct char_converter +{ + static std::string conv(const wchar_t(&in)[Size]) + { + return ::boost::process::detail::convert(in, in + Size -1); + } +}; + +template<> +struct char_converter +{ + static std::wstring conv(const char* in) + { + std::size_t size = 0; + while (in[size] != '\0') size++; + return ::boost::process::detail::convert(in, in + size); + } +}; + +template<> +struct char_converter +{ + static std::wstring conv(char* in) + { + std::size_t size = 0; + while (in[size] != '\0') size++; + return ::boost::process::detail::convert(in, in + size); + } +}; + + +template +struct char_converter +{ + static std::wstring conv(const char(&in)[Size]) + { + return ::boost::process::detail::convert(in, in + Size -1); + } +}; + +//all the containers. +template<> +struct char_converter +{ + static std::wstring conv(const std::string & in) + { + return ::boost::process::detail::convert(in); + } +}; + +template<> +struct char_converter +{ + static std::string conv(const std::wstring & in) + { + return ::boost::process::detail::convert(in); + } +}; + +template<> +struct char_converter> +{ + static std::vector conv(const std::vector & in) + { + std::vector ret(in.size()); + std::transform(in.begin(), in.end(), ret.begin(), + [](const std::string & st) + { + return convert(st); + }); + return ret; + } +}; + +template<> +struct char_converter> +{ + static std::vector conv(const std::initializer_list & in) + { + std::vector ret(in.size()); + std::transform(in.begin(), in.end(), ret.begin(), + [](const std::string & st) + { + return convert(st); + }); + return ret; + } +}; + +template<> +struct char_converter> +{ + static std::vector conv(const std::vector & in) + { + std::vector ret(in.size()); + std::transform(in.begin(), in.end(), ret.begin(), + [](const char* st) + { + std::size_t sz; + while (st[sz] != '\0') sz++; + return convert(st, st + sz); + }); + return ret; + } +}; + +template<> +struct char_converter> +{ + static std::vector conv(const std::initializer_list & in) + { + std::vector ret(in.size()); + std::transform(in.begin(), in.end(), ret.begin(), + [](const char* st) + { + std::size_t sz; + while (st[sz] != '\0') sz++; + return convert(st, st + sz); + }); + return ret; + } +}; + +template<> +struct char_converter> +{ + static std::vector conv(const std::vector & in) + { + std::vector ret(in.size()); + std::transform(in.begin(), in.end(), ret.begin(), + [](const std::wstring & st) + { + return convert(st); + }); + return ret; + } +}; + +template<> +struct char_converter> +{ + static std::vector conv(const std::initializer_list & in) + { + std::vector ret(in.size()); + std::transform(in.begin(), in.end(), ret.begin(), + [](const std::wstring & st) + { + return convert(st); + }); + return ret; + } +}; + +template<> +struct char_converter> +{ + static std::vector conv(const std::vector & in) + { + std::vector ret(in.size()); + std::transform(in.begin(), in.end(), ret.begin(), + [](const wchar_t* st) + { + std::size_t sz; + while (st[sz] != L'\0') sz++; + return convert(st, st + sz); + }); + return ret; + } +}; + +template<> +struct char_converter> +{ + static std::vector conv(const std::initializer_list & in) + { + std::vector ret(in.size()); + std::transform(in.begin(), in.end(), ret.begin(), + [](const wchar_t* st) + { + std::size_t sz; + while (st[sz] != L'\0') sz++; + return convert(st, st + sz); + }); + return ret; + } +}; + + +}}} +#endif /* BOOST_PROCESS_DETAIL_TRAITS_WCHAR_T_HPP_ */ diff --git a/include/boost/process/detail/windows/async_in.hpp b/include/boost/process/detail/windows/async_in.hpp index 10583df2..b9301773 100644 --- a/include/boost/process/detail/windows/async_in.hpp +++ b/include/boost/process/detail/windows/async_in.hpp @@ -33,7 +33,7 @@ struct async_in_buffer : ::boost::process::detail::windows::async_handler Buffer & buf; std::shared_ptr> promise; - async_in_buffer operator<(std::future & fut) + async_in_buffer operator>(std::future & fut) { promise = std::make_shared>(); fut = promise->get_future(); return std::move(*this); diff --git a/include/boost/process/detail/windows/basic_cmd.hpp b/include/boost/process/detail/windows/basic_cmd.hpp index 45cc9ff9..be2be982 100644 --- a/include/boost/process/detail/windows/basic_cmd.hpp +++ b/include/boost/process/detail/windows/basic_cmd.hpp @@ -26,12 +26,23 @@ namespace detail namespace windows { -typedef std::string native_args; - - inline std::string build_args(const std::string & exe, std::vector && data) { std::string st = exe; + + //put in quotes if it has spaces + { + boost::replace_all(st, "\"", "\\\""); + + auto it = std::find(st.begin(), st.end(), ' '); + + if (it != st.end())//contains spaces. + { + st.insert(st.begin(), '"'); + st += '"'; + } + } + for (auto & arg : data) { boost::replace_all(arg, "\"", "\\\""); @@ -53,15 +64,45 @@ inline std::string build_args(const std::string & exe, std::vector return st; } +inline std::wstring build_args(const std::wstring & exe, std::vector && data) +{ + std::wstring st = exe; + for (auto & arg : data) + { + boost::replace_all(arg, L"\"", L"\\\""); + auto it = std::find(arg.begin(), arg.end(), ' ');//contains space? + if (it != arg.end())//ok, contains spaces. + { + //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. + } + + if (!st.empty())//first one does not need a preceeding space + st += L' '; + + st += arg; + } + return st; +} + +template struct exe_cmd_init : handler_base_ext { - exe_cmd_init(const std::string & exe, bool cmd_only = false) + using value_type = Char; + using string_type = std::basic_string; + + static const char* c_arg(char) { return "/c";} + static const wchar_t* c_arg(wchar_t) { return L"/c";} + + exe_cmd_init(const string_type & exe, bool cmd_only = false) : exe(exe), args({}), cmd_only(cmd_only) {}; - exe_cmd_init(std::string && exe, bool cmd_only = false) + exe_cmd_init(string_type && exe, bool cmd_only = false) : exe(std::move(exe)), args({}), cmd_only(cmd_only) {}; - exe_cmd_init(std::string && exe, std::vector && args) + exe_cmd_init(string_type && exe, std::vector && args) : exe(std::move(exe)), args(build_args(this->exe, std::move(args))), cmd_only(false) {}; template void on_setup(Executor& exec) const @@ -75,33 +116,38 @@ struct exe_cmd_init : handler_base_ext exec.cmd_line = args.c_str(); } } - static exe_cmd_init exe_args(std::string&& exe, std::vector && args) + static exe_cmd_init exe_args(string_type && exe, std::vector && args) { - return exe_cmd_init(std::move(exe), std::move(args)); + return exe_cmd_init(std::move(exe), std::move(args)); } - static exe_cmd_init cmd(std::string&& cmd) + static exe_cmd_init cmd(string_type&& cmd) { - return exe_cmd_init(std::move(cmd), true); + return exe_cmd_init(std::move(cmd), true); } - static exe_cmd_init exe_args_shell(std::string&& exe, std::vector && args) + static exe_cmd_init exe_args_shell(string_type && exe, std::vector && args) { - std::vector args_ = {"/c", std::move(exe)}; + std::vector args_ = {c_arg(Char()), std::move(exe)}; args_.insert(args_.end(), std::make_move_iterator(args.begin()), std::make_move_iterator(args.end())); - std::string sh = shell().string(); - return exe_cmd_init(std::move(sh), std::move(args_)); - } - static exe_cmd_init cmd_shell(std::string&& cmd) - { - std::vector args = {"/c", std::move(cmd)}; - std::string sh = shell().string(); + string_type sh = get_shell(Char()); - return exe_cmd_init( + return exe_cmd_init(std::move(sh), std::move(args_)); + } + + static std:: string get_shell(char) {return shell(). string(codecvt()); } + static std::wstring get_shell(wchar_t) {return shell().wstring(codecvt());} + + static exe_cmd_init cmd_shell(string_type&& cmd) + { + std::vector args = {c_arg(Char()), std::move(cmd)}; + string_type sh = get_shell(Char()); + + return exe_cmd_init( std::move(sh), std::move(args)); } private: - std::string exe; - std::string args; + string_type exe; + string_type args; bool cmd_only; }; diff --git a/include/boost/process/detail/windows/basic_pipe.hpp b/include/boost/process/detail/windows/basic_pipe.hpp index 15ba101e..c7165924 100644 --- a/include/boost/process/detail/windows/basic_pipe.hpp +++ b/include/boost/process/detail/windows/basic_pipe.hpp @@ -208,15 +208,15 @@ basic_pipe& basic_pipe::operator=(basic_pipe && lhs) template inline bool operator==(const basic_pipe & lhs, const basic_pipe & rhs) { - return compare_handles(lhs.source(), rhs.source()) && - compare_handles(lhs.sink(), rhs.sink()); + return compare_handles(lhs.native_source(), rhs.native_source()) && + compare_handles(lhs.native_sink(), rhs.native_sink()); } template inline bool operator!=(const basic_pipe & lhs, const basic_pipe & rhs) { - return !compare_handles(lhs.source(), rhs.source()) || - !compare_handles(lhs.sink(), rhs.sink()); + return !compare_handles(lhs.native_source(), rhs.native_source()) || + !compare_handles(lhs.native_sink(), rhs.native_sink()); } }}}} diff --git a/include/boost/process/detail/windows/cmd.hpp b/include/boost/process/detail/windows/cmd.hpp index da3f6649..ed78ca73 100644 --- a/include/boost/process/detail/windows/cmd.hpp +++ b/include/boost/process/detail/windows/cmd.hpp @@ -18,18 +18,23 @@ namespace detail namespace windows { - +template struct cmd_setter_ : ::boost::process::detail::handler_base { - cmd_setter_(std::string && cmd_line) : _cmd_line(std::move(cmd_line)) {} - cmd_setter_(const std::string & cmd_line) : _cmd_line(cmd_line) {} + typedef CharType value_type; + typedef std::basic_string string_type; + + cmd_setter_(string_type && cmd_line) : _cmd_line(std::move(cmd_line)) {} + cmd_setter_(const string_type & cmd_line) : _cmd_line(cmd_line) {} template - void on_setup(Executor& exec) const + void on_setup(Executor& exec) { - exec.cmd_line = _cmd_line.c_str(); + exec.cmd_line = _cmd_line.c_str(); } -public: - std::string _cmd_line; + const string_type & str() const {return _cmd_line;} + +private: + string_type _cmd_line; }; } diff --git a/include/boost/process/detail/windows/compare_handles.hpp b/include/boost/process/detail/windows/compare_handles.hpp index 5f8806c7..29e8304f 100644 --- a/include/boost/process/detail/windows/compare_handles.hpp +++ b/include/boost/process/detail/windows/compare_handles.hpp @@ -7,82 +7,33 @@ #define BOOST_PROCESS_DETAIL_WINDOWS_COMPARE_HANDLES_HPP_ #include -#include +#include #include - namespace boost { namespace process { namespace detail { namespace windows { -#if BOOST_USE_WINAPI_VERSION >= BOOST_WINAPI_VERSION_WIN10 inline bool compare_handles(boost::detail::winapi::HANDLE_ lhs, boost::detail::winapi::HANDLE_ rhs) { - return ::boost::detail::winapi::CompareObjectHandles(lhs, rhs); -} - -#else //ugly workaround - -extern "C" -{ - -typedef enum _OBJECT_INFORMATION_CLASS_ -{ - ObjectBasicInformation, - ObjectNameInformation, - ObjectTypeInformation, - ObjectAllInformation, - ObjectDataInformation -} OBJECT_INFORMATION_CLASS_; - -typedef struct _LSA_UNICODE_STRING { - ::boost::detail::winapi::USHORT_ Length; - ::boost::detail::winapi::USHORT_ MaximumLength; - ::boost::detail::winapi::WCHAR_* Buffer; -} UNICODE_STRING_; - - -typedef struct _OBJECT_NAME_INFORMATION -{ - UNICODE_STRING_ Name; - ::boost::detail::winapi::WCHAR_ NameBuffer[0]; - -} OBJECT_NAME_INFORMATION, *POBJECT_NAME_INFORMATION; - -typedef ::boost::detail::winapi::DWORD_ (* nt_query_object_p)( - ::boost::detail::winapi::HANDLE_ Handle, - OBJECT_INFORMATION_CLASS_ ObjectInformationClass, - ::boost::detail::winapi::PVOID_ ObjectInformation, - ::boost::detail::winapi::ULONG_ ObjectInformationLength, - ::boost::detail::winapi::PULONG_ ReturnLength -); - -} - - -inline bool compare_handles(boost::detail::winapi::HANDLE_ lhs, boost::detail::winapi::HANDLE_ rhs) -{ - static ::boost::detail::winapi::HMODULE_ h = ::boost::detail::winapi::get_module_handle("ntdll.dll"); - static nt_query_object_p f = reinterpret_cast(::boost::detail::winapi::get_proc_address(h, "NtQueryObject")); - - _OBJECT_NAME_INFORMATION lhs_info; - _OBJECT_NAME_INFORMATION rhs_info; - - if ((*f)(lhs, ObjectNameInformation, &rhs_info, sizeof(_OBJECT_NAME_INFORMATION), nullptr)) - ::boost::process::detail::throw_last_error("NtQueryObject failed"); - - if ((*f)(rhs, ObjectNameInformation, &rhs_info, sizeof(_OBJECT_NAME_INFORMATION), nullptr)) - ::boost::process::detail::throw_last_error("NtQueryObject failed"); - - if (lhs_info.Name.Length != rhs_info.Name.Length) + if ( (lhs == ::boost::detail::winapi::INVALID_HANDLE_VALUE_) + || (rhs == ::boost::detail::winapi::INVALID_HANDLE_VALUE_)) return false; - return std::equal(lhs_info.Name.Buffer, lhs_info.Name.Buffer + lhs_info.Name.Length, - rhs_info.Name.Buffer); + + if (lhs == rhs) + return true; + + ::boost::detail::winapi::BY_HANDLE_FILE_INFORMATION_ lhs_info{0,0,0,0,0,0,0,0,0,0}; + ::boost::detail::winapi::BY_HANDLE_FILE_INFORMATION_ rhs_info{0,0,0,0,0,0,0,0,0,0}; + + if (!::boost::detail::winapi::GetFileInformationByHandle(lhs, &lhs_info)) + ::boost::process::detail::throw_last_error("GetFileInformationByHandle"); + + if (!::boost::detail::winapi::GetFileInformationByHandle(rhs, &rhs_info)) + ::boost::process::detail::throw_last_error("GetFileInformationByHandle"); + + return (lhs_info.nFileIndexHigh == rhs_info.nFileIndexHigh) + && (lhs_info.nFileIndexLow == rhs_info.nFileIndexLow); } - - - -#endif - }}}} diff --git a/include/boost/process/detail/windows/env_init.hpp b/include/boost/process/detail/windows/env_init.hpp index 571f21b8..a7d467a4 100644 --- a/include/boost/process/detail/windows/env_init.hpp +++ b/include/boost/process/detail/windows/env_init.hpp @@ -16,19 +16,19 @@ namespace boost { namespace process { namespace detail { namespace windows { +template struct env_init : public ::boost::process::detail::handler_base { - boost::process::environment env; + boost::process::basic_environment env; - env_init(boost::process::environment && env) : env(std::move(env)) {}; - env_init(const boost::process::environment & env) : env(env) {}; + env_init(boost::process::basic_environment && env) : env(std::move(env)) {}; + env_init(const boost::process::basic_environment & env) : env(env) {}; template void on_setup(WindowsExecutor &exec) const { auto e = env.native_handle(); - if (*e == null_char()) { exec.set_error(std::error_code(::boost::detail::winapi::ERROR_BAD_ENVIRONMENT_, std::system_category()), diff --git a/include/boost/process/detail/windows/environment.hpp b/include/boost/process/detail/windows/environment.hpp index 68b9ad61..bb90d6e8 100644 --- a/include/boost/process/detail/windows/environment.hpp +++ b/include/boost/process/detail/windows/environment.hpp @@ -15,7 +15,7 @@ #include #include #include - +#include namespace boost { namespace process { namespace detail { namespace windows { @@ -63,10 +63,37 @@ inline auto native_environment_impl::get(const pointer_type id) -> string_ auto size = boost::detail::winapi::get_environment_variable(id, buf, sizeof(buf)); if (size == 0) //failed { - auto err = boost::detail::winapi::GetLastError(); - if (err == boost::detail::winapi::ERROR_ENVVAR_NOT_FOUND_)//well, then we consider that an empty value + auto err = ::boost::detail::winapi::GetLastError(); + if (err == ::boost::detail::winapi::ERROR_ENVVAR_NOT_FOUND_)//well, then we consider that an empty value return ""; + else + throw std::system_error("GetEnvironmentVariable() failed", + std::error_code(err, std::system_category())); + } + + if (size == sizeof(buf)) //the return size gives the size without the null, so I know this went wrong + { + /*limit defined here https://msdn.microsoft.com/en-us/library/windows/desktop/ms683188(v=vs.85).aspx + * but I used 32768 so it is a multiple of 4096. + */ + constexpr static std::size_t max_size = 32768; //Handle variables longer then buf. + std::size_t buf_size = sizeof(buf); + while (buf_size <= max_size) + { + std::vector buf(buf_size); + auto size = boost::detail::winapi::get_environment_variable(id, buf.data(), buf.size()); + + if (size == buf_size) //buffer to small + buf_size *= 2; + else if (size == 0) + ::boost::process::detail::throw_last_error("GetEnvironmentVariable() failed"); + else + return std::basic_string( + buf.data(), buf.data()+ size + 1); + + } + } return std::basic_string(buf, buf+size+1); } @@ -119,6 +146,9 @@ public: using pointer_type = const char_type*; using string_type = std::basic_string; using native_handle_type = pointer_type; + + std::size_t size() const { return _data.size();} + void reload() { _env_arr = _load_var(_data.data()); @@ -133,22 +163,50 @@ public: void set(const string_type & id, const string_type & value); void reset(const string_type & id); - basic_environment_impl(const native_environment_impl & nei); + inline basic_environment_impl(const native_environment_impl & nei); basic_environment_impl() = default; basic_environment_impl(const basic_environment_impl& rhs) : _data(rhs._data) { - } - basic_environment_impl(basic_environment_impl && ) = default; + basic_environment_impl(basic_environment_impl && rhs) + : _data(std::move(rhs._data)), + _env_arr(std::move(rhs._env_arr)), + _env_impl(_env_arr.data()) + { + } + basic_environment_impl &operator=(basic_environment_impl && rhs) + { + _data = std::move(rhs._data); + //reload(); + _env_arr = std::move(rhs._env_arr); + _env_impl = _env_arr.data(); + + return *this; + } basic_environment_impl & operator=(const basic_environment_impl& rhs) { _data = rhs._data; + reload(); + return *this; + } + + template + explicit inline basic_environment_impl( + const basic_environment_impl& rhs, + const ::boost::process::codecvt_type & cv = ::boost::process::codecvt()) + : _data(::boost::process::detail::convert(rhs._data, cv)) + { + } + + template + basic_environment_impl & operator=(const basic_environment_impl& rhs) + { + _data = ::boost::process::detail::convert(rhs._data); _env_arr = _load_var(&*_data.begin()); _env_impl = &*_env_arr.begin(); return *this; } - basic_environment_impl & operator=(basic_environment_impl && ) = default; Char ** _env_impl = &*_env_arr.begin(); @@ -161,13 +219,13 @@ basic_environment_impl::basic_environment_impl(const native_environment_im { auto beg = nei.native_handle(); auto p = beg; - while ((*p != null_char()) || (*(p+1) != null_char())) + while ((*p != null_char()) || (*(p+1) != null_char())) p++; - p++; //pointing to the second nullchar p++; //to get the pointer behing the second nullchar, so it's end. this->_data.assign(beg, p); + this->reload(); } @@ -212,13 +270,18 @@ inline void basic_environment_impl::set(const string_type &id, const strin template inline void basic_environment_impl::reset(const string_type &id) { + //ok, we need to check the size of data first + if (id.size() >= _data.size()) //ok, so it's impossible id is in there. + return; + + //check if it's the first one, spares us the search. if (std::equal(id.begin(), id.end(), _data.begin()) && (_data[id.size()] == '=')) { - auto beg = _data.begin() + _data.size() + 1; + auto beg = _data.begin(); auto end = beg; while (*end != '\0') - end++; + end++; end++; //to point behind the last null-char diff --git a/include/boost/process/detail/windows/executor.hpp b/include/boost/process/detail/windows/executor.hpp index 9176f456..5f44b33a 100644 --- a/include/boost/process/detail/windows/executor.hpp +++ b/include/boost/process/detail/windows/executor.hpp @@ -108,8 +108,8 @@ struct startup_info_impl -template -class executor : public startup_info_impl +template +class executor : public startup_info_impl { void internal_error_handle(const std::error_code &ec, const char* msg, boost::mpl::false_, boost::mpl::true_) {} @@ -188,12 +188,12 @@ public: //NOTE: The non-cast cmd-line string can only be modified by the wchar_t variant which is currently disabled. int err_code = ::boost::detail::winapi::create_process( exe, // LPCSTR_ lpApplicationName, - const_cast(cmd_line), // LPSTR_ lpCommandLine, + const_cast(cmd_line), // LPSTR_ lpCommandLine, proc_attrs, // LPSECURITY_ATTRIBUTES_ lpProcessAttributes, thread_attrs, // LPSECURITY_ATTRIBUTES_ lpThreadAttributes, inherit_handles, // INT_ bInheritHandles, - creation_flags, // DWORD_ dwCreationFlags, - reinterpret_cast(const_cast(env)), // LPVOID_ lpEnvironment, + this->creation_flags, // DWORD_ dwCreationFlags, + reinterpret_cast(const_cast(env)), // LPVOID_ lpEnvironment, work_dir, // LPCSTR_ lpCurrentDirectory, &this->startup_info, // LPSTARTUPINFOA_ lpStartupInfo, &proc_info); // LPPROCESS_INFORMATION_ lpProcessInformation) @@ -235,10 +235,10 @@ public: ::boost::detail::winapi::LPSECURITY_ATTRIBUTES_ proc_attrs = nullptr; ::boost::detail::winapi::LPSECURITY_ATTRIBUTES_ thread_attrs = nullptr; ::boost::detail::winapi::BOOL_ inherit_handles = false; - const char * work_dir = nullptr; - const char * cmd_line = nullptr; - const char * exe = nullptr; - const char * env = nullptr; + const Char * work_dir = nullptr; + const Char * cmd_line = nullptr; + const Char * exe = nullptr; + const Char * env = nullptr; Sequence & seq; @@ -247,10 +247,10 @@ public: -template -executor make_executor(Tup & tup) +template +executor make_executor(Tup & tup) { - return executor(tup); + return executor(tup); } diff --git a/include/boost/process/detail/windows/locale.hpp b/include/boost/process/detail/windows/locale.hpp new file mode 100644 index 00000000..dc8b3455 --- /dev/null +++ b/include/boost/process/detail/windows/locale.hpp @@ -0,0 +1,100 @@ +// Copyright (c) 2016 Klemens D. Morgenstern +// Copyright (c) 2008 Beman Dawes +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +#ifndef BOOST_PROCESS_DETAIL_WINDOWS_LOCALE_HPP_ +#define BOOST_PROCESS_DETAIL_WINDOWS_LOCALE_HPP_ + +#include +#include +#include + +namespace boost +{ +namespace process +{ +namespace detail +{ +namespace windows +{ + +//copied from boost.filesystem +class windows_file_codecvt + : public std::codecvt< wchar_t, char, std::mbstate_t > + { + public: + explicit windows_file_codecvt(std::size_t refs = 0) + : std::codecvt(refs) {} + protected: + + bool do_always_noconv() const noexcept override { return false; } + + // seems safest to assume variable number of characters since we don't + // actually know what codepage is active + int do_encoding() const noexcept override { return 0; } + + std::codecvt_base::result do_in(std::mbstate_t& state, + const char* from, const char* from_end, const char*& from_next, + wchar_t* to, wchar_t* to_end, wchar_t*& to_next) const override + { + ::boost::detail::winapi::UINT_ codepage = AreFileApisANSI() ? + ::boost::detail::winapi::CP_ACP_ : + ::boost::detail::winapi::CP_OEMCP_; + + int count; + if ((count = ::boost::detail::winapi::MultiByteToWideChar(codepage, + ::boost::detail::winapi::MB_PRECOMPOSED_, from, + from_end - from, to, to_end - to)) == 0) + { + return error; // conversion failed + } + + from_next = from_end; + to_next = to + count; + *to_next = L'\0'; + return ok; + } + + std::codecvt_base::result do_out(std::mbstate_t & state, + const wchar_t* from, const wchar_t* from_end, const wchar_t*& from_next, + char* to, char* to_end, char*& to_next) const override + { + auto codepage = ::boost::detail::winapi::AreFileApisANSI() ? + ::boost::detail::winapi::CP_ACP_ : + ::boost::detail::winapi::CP_OEMCP_; + + int count; + if ((count = ::boost::detail::winapi::WideCharToMultiByte(codepage, + ::boost::detail::winapi::WC_NO_BEST_FIT_CHARS_, from, + from_end - from, to, to_end - to, 0, 0)) == 0) + { + return error; // conversion failed + } + + from_next = from_end; + to_next = to + count; + *to_next = '\0'; + return ok; + } + + std::codecvt_base::result do_unshift(std::mbstate_t&, + char* /*from*/, char* /*to*/, char* & /*next*/) const override { return ok; } + + int do_length(std::mbstate_t&, + const char* /*from*/, const char* /*from_end*/, std::size_t /*max*/) const override { return 0; } + + int do_max_length() const noexcept override { return 0; } + }; + + + +} +} +} +} + + + +#endif /* BOOST_PROCESS_LOCALE_HPP_ */ diff --git a/include/boost/process/detail/windows/start_dir.hpp b/include/boost/process/detail/windows/start_dir.hpp index a21a5ffc..fbcbca5c 100644 --- a/include/boost/process/detail/windows/start_dir.hpp +++ b/include/boost/process/detail/windows/start_dir.hpp @@ -15,9 +15,10 @@ namespace boost { namespace process { namespace detail { namespace windows { +template struct start_dir_init : handler_base_ext { - explicit start_dir_init(const std::string &s) : s_(s) {} + start_dir_init(const std::basic_string &s) : s_(s) {} template void on_setup(Executor& exec) const @@ -25,8 +26,9 @@ struct start_dir_init : handler_base_ext exec.work_dir = s_.c_str(); } + const std::basic_string &str() const {return s_;} private: - std::string s_; + std::basic_string s_; }; }}}} diff --git a/include/boost/process/env.hpp b/include/boost/process/env.hpp index fdfa66f8..157de2f0 100644 --- a/include/boost/process/env.hpp +++ b/include/boost/process/env.hpp @@ -9,7 +9,6 @@ #include #include - #if defined(BOOST_POSIX_API) #include #elif defined(BOOST_WINDOWS_API) @@ -26,61 +25,190 @@ namespace boost { namespace process { namespace detail { -template +template +std::size_t make_env_string_size(const std::basic_string & ch) +{ + return ch.size() + 1; +} + +template +std::size_t make_env_string_size(const Char * ch) +{ + std::size_t sz = 0; + while (ch[sz] != null_char()) + sz++; + + sz++; + return sz; +} + +template +inline std::basic_string make_env_string(const Container & value) +{ + std::size_t sz = 0; + for (auto & v : value) + sz += make_env_string_size(v); + + std::basic_string s; + s.reserve(sz); //+1 for ;, end doesn't have one. + + for (auto & val : value) + (s += val) += ';'; + + s.resize(s.size() -1); //remove last ';' + return s; +} + + +template struct env_set { - std::string key; - T value; + using string_type = std::basic_string; + string_type key; + string_type value; }; -template +template struct env_append { - std::string key; - T value; + using string_type = std::basic_string; + string_type key; + string_type value; }; + + +template struct env_reset { - std::string key; + using string_type = std::basic_string; + string_type key; }; + +template<> struct is_wchar_t> : std::true_type {}; +template<> struct is_wchar_t> : std::true_type {}; +template<> struct is_wchar_t> : std::true_type {}; +template<> struct is_wchar_t> : std::true_type {}; + + +template<> +struct char_converter, env_set> +{ + static env_set conv(const env_set & in) + { + return {::boost::process::detail::convert(in.key), + ::boost::process::detail::convert(in.value)}; + } +}; + +template<> +struct char_converter, env_set> +{ + static env_set conv(const env_set & in) + { + return {::boost::process::detail::convert(in.key), + ::boost::process::detail::convert(in.value)}; + } +}; + +template<> +struct char_converter, env_append> +{ + static env_append conv(const env_append & in) + { + return {::boost::process::detail::convert(in.key), + ::boost::process::detail::convert(in.value)}; + } +}; + +template<> +struct char_converter, env_append> +{ + static env_append conv(const env_append & in) + { + return {::boost::process::detail::convert(in.key), + ::boost::process::detail::convert(in.value)}; + } +}; + +template<> +struct char_converter, env_reset> +{ + static env_reset conv(const env_reset & in) + { + return {::boost::process::detail::convert(in.key)}; + } +}; + +template<> +struct char_converter, env_reset> +{ + static env_reset conv(const env_reset & in) + { + return {::boost::process::detail::convert(in.key)}; + } +}; + + +template struct env_init { - environment env; + basic_environment env; }; +template<> +struct char_converter, env_init> +{ + static env_init conv(const env_init & in) + { + return {basic_environment(in.env)}; + } +}; + +template<> +struct char_converter, env_init> +{ + static env_init conv(const env_init & in) + { + return {basic_environment(in.env)}; + } +}; + + +template struct env_proxy { - std::string key; + using string_type = std::basic_string; + string_type key; - env_set operator=(const std::string & value) + + env_set operator=(const string_type & value) { return {std::move(key), value}; } - env_set> operator=(const std::vector & value) + env_set operator=(const std::vector & value) { - return {std::move(key), value}; + return {std::move(key), make_env_string(value)}; } - env_set> operator=(const std::initializer_list & value) + env_set operator=(const std::initializer_list & value) { - return {std::move(key), value}; + return {std::move(key), make_env_string(value)}; } - env_append operator+=(const std::string & value) + env_append operator+=(const string_type & value) { return {std::move(key), value}; } - env_append> operator+=(const std::vector & value) + env_append operator+=(const std::vector & value) { - return {std::move(key), value}; + return {std::move(key), make_env_string(value)}; } - env_append> operator+=(const std::initializer_list & value) + env_append operator+=(const std::initializer_list & value) { - return {std::move(key), value}; + return {std::move(key), make_env_string(value)}; } - - env_reset operator=(boost::none_t) + env_reset operator=(boost::none_t) { return {std::move(key)}; } @@ -90,75 +218,95 @@ struct env_ { constexpr env_() {}; - env_set operator()(const std::string & key, const std::string & value) const + template + env_set operator()(const std::basic_string & key, + const std::basic_string & value) const { return {key, value}; } - env_set> operator()(const std::string & key, const std::vector & value) const + template + env_set operator()(const std::basic_string & key, + const std::vector> & value) const { - return {key, value}; + return {key, make_env_string(value)}; } - env_set> operator()(const std::string & key, const std::initializer_list & value) const + template + env_set operator()(const std::basic_string & key, + const std::initializer_list & value) const { - return {key, std::vector(value)}; + return {key, make_env_string(value)}; } - env_reset operator()(const std::string & key, boost::none_t) + template + env_reset operator()(const std::basic_string & key, boost::none_t) { return {key}; } - env_proxy operator[](const std::string & key) const + template + env_proxy operator[](const std::basic_string & key) const { return {key}; } - env_init operator()(const environment & env) const + template + env_proxy operator[](const Char* key) const + { + return {key}; + } + template + env_init operator()(const basic_environment & env) const { return {env}; } - env_init operator= (const environment & env) const + template + env_init operator= (const basic_environment & env) const { return {env}; } }; - +template struct env_builder { - environment env; - void operator()(const environment & e) + basic_environment env; + void operator()(const basic_environment & e) { env = e; } - void operator()(env_init & ei) + void operator()(env_init & ei) { env = std::move(ei.env); } - template - void operator()(env_set & es) + void operator()(env_set & es) { - //env.set(es.key, es.value_to_string()); + env[es.key] = es.value; } - void operator()(env_reset & es) + void operator()(env_reset & es) { env.erase(es.key); } template void operator()(env_append & es) { - //env.set(es.key, es.value_to_string()); + env[es.key] += es.value; } - typedef api::env_init result_type; - api::env_init get_initializer() + typedef api::env_init result_type; + api::env_init get_initializer() { - return api::env_init(std::move(env)); + return api::env_init(std::move(env)); } }; template<> -struct initializer_builder +struct initializer_builder> { - typedef env_builder type; + typedef env_builder type; +}; + +template<> +struct initializer_builder> +{ + typedef env_builder type; }; diff --git a/include/boost/process/environment.hpp b/include/boost/process/environment.hpp index fbe9709f..6d51c2a1 100644 --- a/include/boost/process/environment.hpp +++ b/include/boost/process/environment.hpp @@ -11,6 +11,7 @@ #include #include #include +#include #if defined(BOOST_POSIX_API) #include @@ -36,7 +37,7 @@ struct const_entry if (_data == nullptr) return std::vector(); std::vector data; - auto str = std::string(_data); + auto str = string_type(_data); struct splitter { bool operator()(wchar_t w) const {return w == L';';} @@ -113,16 +114,34 @@ struct entry : const_entry data += v; } this->_env->set(this->_name, data); - this->_env->reload(); + this->reload(); + + } + void assign(const std::initializer_list &value) + { + string_type data; + for (auto &v : value) + { + if (&v != &*value.begin()) + data += ';'; + data += v; + } + this->_env->set(this->_name, data); + this->reload(); + } void append(const string_type &value) { - string_type st = this->_data; - if (st.empty()) + if (this->_data == nullptr) this->_env->set(this->_name, value); else + { + string_type st = this->_data; this->_env->set(this->_name, st + ';' + value); + + } this->reload(); + } void clear() { @@ -140,6 +159,11 @@ struct entry : const_entry assign(value); return *this; } + entry &operator=(const std::initializer_list & value) + { + assign(value); + return *this; + } entry &operator+=(const string_type & value) { append(value); @@ -154,11 +178,11 @@ template struct make_entry { - make_entry(const make_entry&) = default; - make_entry& operator=(const make_entry&) = default; + make_entry(const make_entry&) = default; + make_entry& operator=(const make_entry&) = default; - Environment &env; - make_entry(Environment & env) : env(env) {}; + Environment *env; + make_entry(Environment & env) : env(&env) {}; entry operator()(const Char* data) const { auto p = data; @@ -167,7 +191,7 @@ struct make_entry auto name = std::basic_string(data, p); p++; //go behind equal sign - return entry(std::move(name), p, env); + return entry(std::move(name), p, *env); } }; @@ -175,11 +199,11 @@ template struct make_const_entry { - make_const_entry(const make_const_entry&) = default; - make_const_entry& operator=(const make_const_entry&) = default; + make_const_entry(const make_const_entry&) = default; + make_const_entry& operator=(const make_const_entry&) = default; - Environment &env; - make_const_entry(Environment & env) : env(env) {}; + Environment *env; + make_const_entry(Environment & env) : env(&env) {}; const_entry operator()(const Char* data) const { auto p = data; @@ -188,7 +212,7 @@ struct make_const_entry auto name = std::basic_string(data, p); p++; //go behind equal sign - return const_entry(std::move(name), p, env); + return const_entry(std::move(name), p, *env); } }; @@ -196,8 +220,8 @@ struct make_const_entry #if !defined (BOOST_PROCESS_DOXYGEN) -template class Implementation = detail::api::native_environment_impl> -class basic_environment : public Implementation +template class Implementation = detail::api::basic_environment_impl> +class basic_environment_impl : public Implementation { Char** _get_end() const { @@ -210,7 +234,7 @@ class basic_environment : public Implementation public: using string_type = std::basic_string; using implementation_type = Implementation; - using base_type = basic_environment; + using base_type = basic_environment_impl; using entry_maker = detail::make_entry; using entry_type = detail::entry ; using const_entry_type = detail::const_entry ; @@ -234,7 +258,7 @@ public: iterator find( const string_type& key ) { auto p = this->_env_impl; - auto st1 = key + equal_sign(); + auto st1 = key + ::boost::process::detail::equal_sign(); while (*p != nullptr) { if (std::equal(st1.begin(), st1.end(), *p)) @@ -246,7 +270,7 @@ public: const_iterator find( const string_type& key ) const { auto p = this->_env_impl; - auto st1 = key + equal_sign(); + auto st1 = key + ::boost::process::detail::equal_sign(); while (*p != nullptr) { if (std::equal(st1.begin(), st1.end(), *p)) @@ -259,7 +283,7 @@ public: std::size_t count(const string_type & st) const { auto p = this->_env_impl; - auto st1 = st + equal_sign(); + auto st1 = st + ::boost::process::detail::equal_sign(); while (*p != nullptr) { if (std::equal(st1.begin(), st1.end(), *p)) @@ -275,7 +299,7 @@ public: std::pair emplace(const string_type & id, const string_type & value) { auto p = this->_env_impl; - auto st1 = id + equal_sign(); + auto st1 = id + ::boost::process::detail::equal_sign(); auto f = find(id); if (f != end()) { @@ -293,7 +317,7 @@ public: //copy ctor if impl is copy-constructible bool empty() { - return this->_env_impl == nullptr; + return *this->_env_impl == nullptr; } std::size_t size() const { @@ -335,7 +359,7 @@ public: /**Template representation of environments. It takes a template * as template parameter to implement the environment */ -template class Implementation = detail::api::native_environment_impl> +template class basic_environment { @@ -363,12 +387,12 @@ public: ///Default constructor basic_environment(); - ///Copy constructor. @note Deleted for native_environment. + ///Copy constructor. basic_environment(const basic_environment & ); ///Move constructor. basic_environment(basic_environment && ); - ///Copy assignment. @note Deleted for native_environment. + ///Copy assignment. basic_environment& operator=(const basic_environment & ); ///Move assignment. basic_environment& operator=(basic_environment && ); @@ -379,7 +403,7 @@ public: bool empty(); ///Get the number of variables. std::size_t size() const; - ///Clear the environment. @attention Use with care, environment cannot be empty. + ///Clear the environment. @attention Use with care, passed environment cannot be empty. void clear(); ///Get the entry with the key. Throws if it does not exist. entry_type at( const string_type& key ); @@ -458,13 +482,158 @@ public: }; +/**Template representation of environments. It takes a template + * as template parameter to implement the environment + */ +template +class basic_native_environment +{ + +public: + typedef std::basic_string string_type; + typedef boost::transform_iterator< entry_maker, Char**> iterator ; + typedef boost::transform_iterator const_iterator ; + typedef std::size_t size_type ; + + iterator begin() ; /// emplace(const string_type & id, const string_type & value); + + ///Default constructor + basic_native_environment(); + ///Move constructor. + basic_native_environment(basic_native_environment && ); + ///Move assignment. + basic_native_environment& operator=(basic_native_environment && ); + + typedef typename detail::implementation_type::native_handle_type native_handle; + + ///Check if environment has entries. + bool empty(); + ///Get the number of variables. + std::size_t size() const; + ///Get the entry with the key. Throws if it does not exist. + entry_type at( const string_type& key ); + ///Get the entry with the key. Throws if it does not exist. + const_entry_type at( const string_type& key ) const; + ///Get the entry with the given key. It creates the entry if it doesn't exist. + entry_type operator[](const string_type & key); + + /**Proxy class used for read access to members by [] or .at() + * @attention Holds a reference to the environment it was created from. + */ + template + struct const_entry_type + { + typedef Char value_type; + typedef const value_type * pointer; + typedef std::basic_string string_type; + typedef boost::iterator_range range; + typedef Environment environment_t; + + ///Split the entry by ";" and return it as a vector. Used by PATH. + std::vector to_vector() const + ///Get the value as string. + string_type to_string() const + ///Get the name of this entry. + string_type get_name() const {return string_type(_name.begin(), _name.end());} + ///Copy Constructor + const_entry(const const_entry&) = default; + ///Move Constructor + const_entry& operator=(const const_entry&) = default; + ///Check if the entry is empty. + bool empty() const; + }; + + /**Proxy class used for read and write access to members by [] or .at() + * @attention Holds a reference to the environment it was created from. + */ + template + struct entry_type + { + + typedef Char value_type; + typedef const value_type * pointer; + typedef std::basic_string string_type; + typedef boost::iterator_range range; + typedef Environment environment_t; + + ///Split the entry by ";" and return it as a vector. Used by PATH. + std::vector to_vector() const + ///Get the value as string. + string_type to_string() const + ///Get the name of this entry. + string_type get_name() const {return string_type(_name.begin(), _name.end());} + ///Copy Constructor + entry(const entry&) = default; + ///Move Constructor + entry& operator=(const entry&) = default; + ///Check if the entry is empty. + bool empty() const; + + ///Assign a string to the value + void assign(const string_type &value); + ///Assign a set of strings to the entry; they will be seperated by ';'. + void assign(const std::vector &value); + ///Append a string to the end of the entry, it will seperated by ';'. + void append(const string_type &value); + ///Reset the value + void clear(); + ///Assign a string to the entry. + entry &operator=(const string_type & value); + ///Assign a set of strings to the entry; they will be seperated by ';'. + entry &operator=(const std::vector & value); + ///Append a string to the end of the entry, it will seperated by ';'. + entry &operator+=(const string_type & value); + }; + +}; + #endif ///Definition of the environment for the current process. -typedef basic_environment native_environment; -///Type definition to hold a seperate environment. -typedef basic_environment environment; +template +class basic_native_environment : public basic_environment_impl +{ +public: + using base_type = basic_environment_impl; + using base_type::base_type; + using base_type::operator=; + }; +///Type definition to hold a seperate environment. +template +class basic_environment : public basic_environment_impl +{ +public: + using base_type = basic_environment_impl; + using base_type::base_type; + using base_type::operator=; +}; + + + +///Definition of the environment for the current process. +typedef basic_native_environment native_environment; +///Definition of the environment for the current process. +typedef basic_native_environment wnative_environment; + +///Type definition to hold a seperate environment. +typedef basic_environment environment; +///Type definition to hold a seperate environment. +typedef basic_environment wenvironment; } @@ -477,22 +646,45 @@ typedef ::boost::process::detail::api::native_handle_t native_handle_t; ///Definition of the environment for this process. using ::boost::process::native_environment; +///Definition of the environment for this process. +using ::boost::process::wnative_environment; ///Get the process id of the current process. inline int get_id() { return ::boost::process::detail::api::get_id();} ///Get the native handle of the current process. inline native_handle_t native_handle() { return ::boost::process::detail::api::native_handle();} ///Get the enviroment of the current process. -inline native_environment environment() { return ::boost::process::native_environment(); } +inline native_environment environment() { return ::boost::process:: native_environment(); } +///Get the enviroment of the current process. +inline wnative_environment wenvironment() { return ::boost::process::wnative_environment(); } ///Get the path environment variable of the current process runs. -inline std::vector path() +inline std::vector path() { - const ::boost::process::native_environment ne; +#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"; +#else + const ::boost::process::native_environment ne; + typedef typename ::boost::process::native_environment::const_entry_type value_type; + const auto id = "path"; +#endif - for (const auto & e : ne) - if ("PATH" == ::boost::to_upper_copy(e.get_name())) - return e.to_vector(); - return {}; + auto itr = std::find_if(ne.cbegin(), ne.cend(), + [&](const value_type & e) + {return id == ::boost::to_upper_copy(e.get_name(), ::boost::process::detail::process_locale());}); + + if (itr == ne.cend()) + return {}; + + auto vec = itr->to_vector(); + + std::vector val; + val.resize(vec.size()); + + std::copy(vec.begin(), vec.end(), val.begin()); + + return {}; } } diff --git a/include/boost/process/exe.hpp b/include/boost/process/exe.hpp index 49b99fef..34c2a87f 100644 --- a/include/boost/process/exe.hpp +++ b/include/boost/process/exe.hpp @@ -22,22 +22,26 @@ namespace boost { namespace process { namespace detail { struct exe_ { - inline exe_setter_ operator()(const char *s) const + template + inline exe_setter_ operator()(const Char *s) const { - return exe_setter_(s); + return exe_setter_(s); } - inline exe_setter_ operator= (const char *s) const + template + inline exe_setter_ operator= (const Char *s) const { - return exe_setter_(s); + return exe_setter_(s); } - inline exe_setter_ operator()(const std::string &s) const + template + inline exe_setter_ operator()(const std::basic_string &s) const { - return exe_setter_(s); + return exe_setter_(s); } - inline exe_setter_ operator= (const std::string &s) const + template + inline exe_setter_ operator= (const std::basic_string &s) const { - return exe_setter_(s); + return exe_setter_(s); } }; diff --git a/include/boost/process/io.hpp b/include/boost/process/io.hpp index 341b2b20..9acfc404 100644 --- a/include/boost/process/io.hpp +++ b/include/boost/process/io.hpp @@ -82,12 +82,16 @@ struct std_in_ api::null_in operator<(const null_t &) const {return api::null_in();} api::file_in operator=(const boost::filesystem::path &p) const {return p;} - api::file_in operator=(const std::string &p) const {return p;} - api::file_in operator=(const char*p) const {return p;} + api::file_in operator=(const std::string & p) const {return p;} + api::file_in operator=(const std::wstring &p) const {return p;} + api::file_in operator=(const char * p) const {return p;} + api::file_in operator=(const wchar_t * p) const {return p;} api::file_in operator<(const boost::filesystem::path &p) const {return p;} api::file_in operator<(const std::string &p) const {return p;} + api::file_in operator<(const std::wstring &p) const {return p;} api::file_in operator<(const char*p) const {return p;} + api::file_in operator<(const wchar_t * p) const {return p;} api::file_in operator=(FILE * f) const {return f;} api::file_in operator<(FILE * f) const {return f;} @@ -137,11 +141,15 @@ struct std_out_ api::file_out operator=(const boost::filesystem::path &p) const {return api::file_out(p);} api::file_out operator=(const std::string &p) const {return api::file_out(p);} - api::file_out operator=(const char*p) const {return api::file_out(p);} + api::file_out operator=(const std::wstring &p) const {return api::file_out(p);} + api::file_out operator=(const char * p) const {return api::file_out(p);} + api::file_out operator=(const wchar_t * p) const {return api::file_out(p);} api::file_out operator>(const boost::filesystem::path &p) const {return api::file_out(p);} api::file_out operator>(const std::string &p) const {return api::file_out(p);} - api::file_out operator>(const char*p) const {return api::file_out(p);} + api::file_out operator>(const std::wstring &p) const {return api::file_out(p);} + api::file_out operator>(const char * p) const {return api::file_out(p);} + api::file_out operator>(const wchar_t * p) const {return api::file_out(p);} api::file_out operator=(FILE * f) const {return f;} api::file_out operator>(FILE * f) const {return f;} diff --git a/include/boost/process/locale.hpp b/include/boost/process/locale.hpp new file mode 100644 index 00000000..04792298 --- /dev/null +++ b/include/boost/process/locale.hpp @@ -0,0 +1,240 @@ +// Copyright (c) 2016 Klemens D. Morgenstern +// Copyright (c) 2008 Beman Dawes +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +#ifndef BOOST_PROCESS_LOCALE_HPP_ +#define BOOST_PROCESS_LOCALE_HPP_ + +#include +#include + +#if defined(BOOST_WINDOWS_API) +#include +#endif + +#include + +namespace boost +{ +namespace process +{ +namespace detail +{ + +class codecvt_category_t : public std::error_category +{ +public: + codecvt_category_t(){} + const char* name() const noexcept override {return "codecvt";} + std::string message(int ev) const override + { + std::string str; + switch (ev) + { + case std::codecvt_base::ok: + str = "ok"; + break; + case std::codecvt_base::partial: + str = "partial"; + break; + case std::codecvt_base::error: + str = "error"; + break; + case std::codecvt_base::noconv: + str = "noconv"; + break; + default: + str = "unknown error"; + } + return str; + } +}; + +} + +inline const std::error_category& codecvt_category() +{ + static const ::boost::process::detail::codecvt_category_t cat; + return cat; +} + +namespace detail +{ +//copied from boost.filesystem +inline std::locale default_locale() +{ +# if defined(BOOST_WINDOWS_API) + std::locale global_loc = std::locale(); + return std::locale(global_loc, new boost::process::detail::windows::windows_file_codecvt); +# elif defined(macintosh) || defined(__APPLE__) || defined(__APPLE_CC__) \ +|| defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__HAIKU__) + std::locale global_loc = std::locale(); + return std::locale(global_loc, new std::utf8_codecvt_facet); +# 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(""); +# endif +} + +inline std::locale& process_locale() +{ + static std::locale loc(default_locale()); + return loc; +} + +} + + +typedef std::codecvt codecvt_type; + +inline const codecvt_type& codecvt() +{ + return std::use_facet>( + detail::process_locale()); +} + +inline std::locale imbue(const std::locale& loc) +{ + std::locale temp(detail::process_locale()); + detail::process_locale() = loc; + return temp; +} + + +namespace detail +{ + +inline std::size_t convert(const char* from, + const char* from_end, + wchar_t* to, wchar_t* to_end, + const ::boost::process::codecvt_type & cvt = + ::boost::process::codecvt()) +{ + std::mbstate_t state = std::mbstate_t(); // perhaps unneeded, but cuts bug reports + const char* from_next; + wchar_t* to_next; + + auto res = cvt.in(state, from, from_end, from_next, + to, to_end, to_next); + + if (res != std::codecvt_base::ok) + throw std::system_error(res, ::boost::process::codecvt_category(), + "boost::process codecvt to wchar_t"); + + + return to_next - to; + +} + +inline std::size_t convert(const wchar_t* from, + const wchar_t* from_end, + char* to, char* to_end, + const ::boost::process::codecvt_type & cvt = + ::boost::process::codecvt()) +{ + std::mbstate_t state = std::mbstate_t(); // perhaps unneeded, but cuts bug reports + const wchar_t* from_next; + char* to_next; + + std::codecvt_base::result res; + + if ((res=cvt.out(state, from, from_end, from_next, + to, to_end, to_next)) != std::codecvt_base::ok) + throw std::system_error(res, ::boost::process::codecvt_category(), + "boost::process codecvt to char"); + + return to_next - to; +} + +inline std::wstring convert(const std::string & st, + const ::boost::process::codecvt_type & cvt = + ::boost::process::codecvt()) +{ + std::wstring out(st.size() + 10, ' '); //just to be sure + auto sz = convert(st.c_str(), st.c_str() + st.size(), + &out.front(), &out.back(), cvt); + + out.resize(sz); + return out; +} + +inline std::string convert(const std::wstring & st, + const ::boost::process::codecvt_type & cvt = + ::boost::process::codecvt()) +{ + std::string out(st.size() * 2, ' '); //just to be sure + auto sz = convert(st.c_str(), st.c_str() + st.size(), + &out.front(), &out.back(), cvt); + + out.resize(sz); + return out; +} + +inline std::vector convert(const std::vector & st, + const ::boost::process::codecvt_type & cvt = + ::boost::process::codecvt()) +{ + std::vector out(st.size() + 10); //just to be sure + auto sz = convert(st.data(), st.data() + st.size(), + &out.front(), &out.back(), cvt); + + out.resize(sz); + return out; +} + +inline std::vector convert(const std::vector & st, + const ::boost::process::codecvt_type & cvt = + ::boost::process::codecvt()) +{ + std::vector out(st.size() * 2); //just to be sure + auto sz = convert(st.data(), st.data() + st.size(), + &out.front(), &out.back(), cvt); + + out.resize(sz); + return out; +} + + +inline std::wstring convert(const char *begin, const char* end, + const ::boost::process::codecvt_type & cvt = + ::boost::process::codecvt()) +{ + auto size = end-begin; + std::wstring out(size + 10, ' '); //just to be sure + using namespace std; + auto sz = convert(begin, end, + &out.front(), &out.back(), cvt); + out.resize(sz); + return out; +} + +inline std::string convert(const wchar_t * begin, const wchar_t *end, + const ::boost::process::codecvt_type & cvt = + ::boost::process::codecvt()) +{ + auto size = end-begin; + + std::string out(size * 2, ' '); //just to be sure + auto sz = convert(begin, end , + &out.front(), &out.back(), cvt); + + out.resize(sz); + return out; +} + + + + +} + + + +} +} + + + + +#endif /* BOOST_PROCESS_LOCALE_HPP_ */ diff --git a/include/boost/process/pipe.hpp b/include/boost/process/pipe.hpp index 5ff00e02..6a2f804f 100644 --- a/include/boost/process/pipe.hpp +++ b/include/boost/process/pipe.hpp @@ -67,9 +67,9 @@ public: /** Get the native handle of the sink. */ native_handle native_sink () const; - ///Write date to the pipe. + ///Write data to the pipe. int_type write(const char_type * data, int_type count); - ///Read date from the pipe. + ///Read data from the pipe. int_type read(char_type * data, int_type count); ///Check if the pipe is open. bool is_open(); @@ -116,16 +116,16 @@ struct basic_pipebuf : std::basic_streambuf ///Move construct from a pipe. basic_pipebuf(pipe_type && p) : _pipe(std::move(p)), - _write(default_buffer_size), - _read(default_buffer_size) + _write(default_buffer_size), + _read(default_buffer_size) { this->setg(_read.data(), _read.data()+ 128, _read.data() + 128); this->setp(_write.data(), _write.data() + _write.size()); } ///Construct from a pipe. basic_pipebuf(const pipe_type & p) : _pipe(p), - _write(default_buffer_size), - _read(default_buffer_size) + _write(default_buffer_size), + _read(default_buffer_size) { this->setg(_read.data(), _read.data()+ 128, _read.data() + 128); this->setp(_write.data(), _write.data() + _write.size()); @@ -151,21 +151,21 @@ struct basic_pipebuf : std::basic_streambuf { if ((ch != traits_type::eof()) && _pipe.is_open()) { - if (this->pptr() == this->epptr()) - { - bool wr = this->_write_impl(); - *this->pptr() = ch; - this->pbump(1); - if (wr) - return ch; - } - else - { - *this->pptr() = ch; - this->pbump(1); - if (this->_write_impl()) - return ch; - } + if (this->pptr() == this->epptr()) + { + bool wr = this->_write_impl(); + *this->pptr() = ch; + this->pbump(1); + if (wr) + return ch; + } + else + { + *this->pptr() = ch; + this->pbump(1); + if (this->_write_impl()) + return ch; + } } return traits_type::eof(); } @@ -184,6 +184,8 @@ struct basic_pipebuf : std::basic_streambuf auto len = &_read.back() - this->egptr() ; auto res = _pipe.read(this->egptr(), len); + if (res == 0) + return traits_type::eof(); this->setg(this->eback(), this->gptr(), this->egptr() + res); auto val = *this->gptr(); @@ -251,9 +253,9 @@ public: ///Default constructor. basic_ipstream() : std::basic_istream(nullptr) - { - std::basic_istream::rdbuf(&_buf); - }; + { + std::basic_istream::rdbuf(&_buf); + }; ///Copy constructor. basic_ipstream(const basic_ipstream & ) = delete; ///Move constructor. @@ -261,15 +263,15 @@ public: ///Move construct from a pipe. basic_ipstream(pipe_type && p) : std::basic_istream(nullptr), _buf(std::move(p)) - { - std::basic_istream::rdbuf(&_buf); - } + { + std::basic_istream::rdbuf(&_buf); + } ///Copy construct from a pipe. basic_ipstream(const pipe_type & p) : std::basic_istream(nullptr), _buf(p) - { - std::basic_istream::rdbuf(&_buf); - } + { + std::basic_istream::rdbuf(&_buf); + } ///Copy assignment. basic_ipstream& operator=(const basic_ipstream & ) = delete; @@ -325,9 +327,9 @@ public: ///Default constructor. basic_opstream() : std::basic_ostream(nullptr) - { - std::basic_ostream::rdbuf(&_buf); - }; + { + std::basic_ostream::rdbuf(&_buf); + }; ///Copy constructor. basic_opstream(const basic_opstream & ) = delete; ///Move constructor. @@ -336,13 +338,13 @@ public: ///Move construct from a pipe. basic_opstream(pipe_type && p) : std::basic_ostream(nullptr), _buf(std::move(p)) { - std::basic_ostream::rdbuf(&_buf); - }; + std::basic_ostream::rdbuf(&_buf); + }; ///Copy construct from a pipe. basic_opstream(const pipe_type & p) : std::basic_ostream(nullptr), _buf(p) { - std::basic_ostream::rdbuf(&_buf); - }; + std::basic_ostream::rdbuf(&_buf); + }; ///Copy assignment. basic_opstream& operator=(const basic_opstream & ) = delete; ///Move assignment @@ -398,9 +400,9 @@ public: ///Default constructor. basic_pstream() : std::basic_iostream(nullptr) - { - std::basic_iostream::rdbuf(&_buf); - }; + { + std::basic_iostream::rdbuf(&_buf); + }; ///Copy constructor. basic_pstream(const basic_pstream & ) = delete; ///Move constructor. @@ -408,14 +410,14 @@ public: ///Move construct from a pipe. basic_pstream(pipe_type && p) : std::basic_iostream(nullptr), _buf(std::move(p)) - { - std::basic_iostream::rdbuf(&_buf); - }; + { + std::basic_iostream::rdbuf(&_buf); + }; ///Copy construct from a pipe. basic_pstream(const pipe_type & p) : std::basic_iostream(nullptr), _buf(p) - { - std::basic_iostream::rdbuf(&_buf); - }; + { + std::basic_iostream::rdbuf(&_buf); + }; ///Copy assignment. basic_pstream& operator=(const basic_pstream & ) = delete; ///Move assignment diff --git a/include/boost/process/shell.hpp b/include/boost/process/shell.hpp index fbc2c917..e0770d5e 100644 --- a/include/boost/process/shell.hpp +++ b/include/boost/process/shell.hpp @@ -18,6 +18,7 @@ #define BOOST_PROCESS_SHELL_PATH_HPP #include +#include #if defined(BOOST_POSIX_API) #include @@ -49,6 +50,11 @@ struct shell_ } }; +template<> +struct is_wchar_t : is_wchar_t +{ +}; + } constexpr static ::boost::process::detail::shell_ shell; diff --git a/include/boost/process/start_dir.hpp b/include/boost/process/start_dir.hpp index b20c66ba..e0cbba11 100644 --- a/include/boost/process/start_dir.hpp +++ b/include/boost/process/start_dir.hpp @@ -12,6 +12,7 @@ #include #include +#include #if defined (BOOST_POSIX_API) #include @@ -34,18 +35,46 @@ struct start_dir_ { constexpr start_dir_() {}; - api::start_dir_init operator()(const std::string & st) const {return api::start_dir_init(st); } - api::start_dir_init operator()(std::string && s) const {return api::start_dir_init(std::move(s)); } - api::start_dir_init operator()(const char* s) const {return api::start_dir_init(s); } - api::start_dir_init operator()(const boost::filesystem::path & st) const {return api::start_dir_init(st.string()); } + template + api::start_dir_init operator()(const std::basic_string & st) const {return {st}; } + template + api::start_dir_init operator()(std::basic_string && s) const {return {std::move(s)}; } + template + api::start_dir_init operator()(const Char* s) const {return {s}; } + api::start_dir_init + operator()(const boost::filesystem::path & st) const {return {st.native()}; } - api::start_dir_init operator=(const std::string & st) const {return api::start_dir_init(st); } - api::start_dir_init operator=(std::string && s) const {return api::start_dir_init(std::move(s)); } - api::start_dir_init operator=(const char* s) const {return api::start_dir_init(s); } - api::start_dir_init operator=(const boost::filesystem::path & st) const {return api::start_dir_init(st.string()); } + template + api::start_dir_init operator= (const std::basic_string & st) const {return {st}; } + template + api::start_dir_init operator= (std::basic_string && s) const {return {std::move(s)}; } + template + api::start_dir_init operator= (const Char* s) const {return {s}; } + api::start_dir_init + operator= (const boost::filesystem::path & st) const {return {st.native()}; } }; +template<> struct is_wchar_t> { typedef std::true_type type;}; + +template<> +struct char_converter, api::start_dir_init> +{ + static api::start_dir_init conv(const api::start_dir_init & in) + { + return api::start_dir_init{::boost::process::detail::convert(in.str())}; + } +}; + +template<> +struct char_converter, api::start_dir_init> +{ + static api::start_dir_init conv(const api::start_dir_init & in) + { + return api::start_dir_init{::boost::process::detail::convert(in.str())}; + } +}; + constexpr static start_dir_ start_dir; } diff --git a/include/boost/process/system.hpp b/include/boost/process/system.hpp index 849267ac..48b11428 100644 --- a/include/boost/process/system.hpp +++ b/include/boost/process/system.hpp @@ -106,7 +106,7 @@ inline int system_impl( #if defined(BOOST_POSIX_API) ,::boost::process::posix::sig.dfl() #endif - ); + ); if (!c.valid()) return -1; c.wait(); diff --git a/test/Jamfile.jam b/test/Jamfile.jam index 7e75f16f..be07a130 100644 --- a/test/Jamfile.jam +++ b/test/Jamfile.jam @@ -41,41 +41,45 @@ exe sparring_partner : sparring_partner.cpp program_options system filesystem io exe sub_launch : sub_launcher.cpp program_options iostreams system filesystem : off ; - -test-suite ts : - [ run pipe.cpp system filesystem ] - [ run async.cpp system thread filesystem : : sparring_partner ] - [ run async_fut.cpp system thread filesystem : : sparring_partner ] - [ run args_cmd.cpp system filesystem : : sparring_partner ] - [ run bind_stderr.cpp filesystem : : sparring_partner ] - [ run bind_stdin.cpp system filesystem : : sparring_partner ] - [ run bind_stdin_stdout.cpp system filesystem : : sparring_partner ] - [ run bind_stdout.cpp system filesystem : : sparring_partner ] - [ run bind_stdout_stderr.cpp system filesystem : : sparring_partner ] - [ run pipe_fwd.cpp system filesystem : : sparring_partner ] - [ run close_stderr.cpp system filesystem : : sparring_partner ] - [ run close_stdin.cpp system filesystem : : sparring_partner ] - [ run close_stdout.cpp system filesystem : : sparring_partner ] - [ run coroutine_test.cpp system filesystem coroutine : : sparring_partner : static ] - [ run error.cpp system filesystem : : sparring_partner ] - [ run exit_code.cpp program_options system filesystem : : sparring_partner ] - [ run extensions.cpp system filesystem : : sparring_partner ] - [ run env.cpp program_options system filesystem : : sparring_partner ] - [ run group.cpp system thread filesystem : : sub_launch ] - [ run posix_specific.cpp system filesystem : : sparring_partner : no linux:yes ] - [ run run_exe.cpp filesystem : : sparring_partner ] - [ run run_exe_path.cpp filesystem : : sparring_partner ] - [ run search_path.cpp filesystem system : : : windows:shell32 ] - [ run shell.cpp filesystem system : : sparring_partner ] - [ run shell_path.cpp filesystem system ] - [ run show_window.cpp filesystem system : : sparring_partner : no windows:yes ] - [ run system_test1.cpp filesystem system : : sparring_partner ] - [ run system_test2.cpp filesystem system : --log_level=all : sparring_partner ] - [ run spawn.cpp filesystem system : : sparring_partner ] - [ run start_dir.cpp filesystem system : : sparring_partner ] - [ run terminate.cpp system filesystem : : sparring_partner ] - [ run throw_on_error.cpp system filesystem : : sparring_partner ] -# [ run vfork.cpp system filesystem : : sparring_partner : no linux:yes ] - [ run wait.cpp system filesystem : : sparring_partner ] - [ compile-fail spawn_fail.cpp ] +test-suite bare : + [ run environment.cpp system filesystem ] + [ run pipe.cpp system filesystem ] ; + +test-suite execution : + [ run async.cpp system thread filesystem : : sparring_partner ] + [ run async_fut.cpp system thread filesystem : : sparring_partner ] + [ run args_cmd.cpp system filesystem : : sparring_partner ] + [ run wargs_cmd.cpp system filesystem : : sparring_partner ] + [ run bind_stderr.cpp filesystem : : sparring_partner ] + [ run bind_stdin.cpp system filesystem : : sparring_partner ] + [ run bind_stdin_stdout.cpp system filesystem : : sparring_partner ] + [ run bind_stdout.cpp system filesystem : : sparring_partner ] + [ run bind_stdout_stderr.cpp system filesystem : : sparring_partner ] + [ run pipe_fwd.cpp system filesystem : : sparring_partner ] + [ run close_stderr.cpp system filesystem : : sparring_partner ] + [ run close_stdin.cpp system filesystem : : sparring_partner ] + [ run close_stdout.cpp system filesystem : : sparring_partner ] + [ run coroutine_test.cpp system filesystem coroutine : : sparring_partner : static ] + [ run error.cpp system filesystem : : sparring_partner ] + [ run exit_code.cpp program_options system filesystem : : sparring_partner ] + [ run extensions.cpp system filesystem : : sparring_partner ] + [ run env.cpp program_options system filesystem : : sparring_partner ] + [ run group.cpp system thread filesystem : : sub_launch ] + [ run posix_specific.cpp system filesystem : : sparring_partner : no linux:yes ] + [ run run_exe.cpp filesystem : : sparring_partner ] + [ run run_exe_path.cpp filesystem : : sparring_partner ] + [ run search_path.cpp filesystem system : : : windows:shell32 ] + [ run shell.cpp filesystem system : : sparring_partner ] + [ run shell_path.cpp filesystem system ] + [ run show_window.cpp filesystem system : : sparring_partner : no windows:yes ] + [ run system_test1.cpp filesystem system : : sparring_partner ] + [ run system_test2.cpp filesystem system : --log_level=all : sparring_partner ] + [ run spawn.cpp filesystem system : : sparring_partner ] + [ run start_dir.cpp filesystem system : : sparring_partner ] + [ run terminate.cpp system filesystem : : sparring_partner ] + [ run throw_on_error.cpp system filesystem : : sparring_partner ] +# [ run vfork.cpp system filesystem : : sparring_partner : no linux:yes ] + [ run wait.cpp system filesystem : : sparring_partner ] + [ compile-fail spawn_fail.cpp ] + : bare ; diff --git a/test/appveyor.yml b/test/appveyor.yml index 10e94a59..e95e1615 100644 --- a/test/appveyor.yml +++ b/test/appveyor.yml @@ -36,17 +36,18 @@ before_build: - set PROJECT_TO_TEST=%APPVEYOR_PROJECT_NAME% - echo "Testing %PROJECT_TO_TEST%" # Cloning Boost libraries (fast nondeep cloning) + - if %BRANCH_TO_TEST%==master ( set BOOST_BRANCH=master ) else ( set BOOST_BRANCH=develop ) - set BOOST=C:/boost-local - git init %BOOST% - cd %BOOST% - - git remote add --no-tags -t %BRANCH_TO_TEST% origin https://github.com/boostorg/boost.git + - git remote add --no-tags -t %BOOST_BRANCH% origin https://github.com/boostorg/boost.git - git fetch --depth=1 - - git checkout %BRANCH_TO_TEST% + - git checkout %BOOST_BRANCH% - git submodule update --init --merge - - git remote set-branches --add origin %BRANCH_TO_TEST% + - git remote set-branches --add origin %BOOST_BRANCH% - git pull --recurse-submodules - git submodule update --init - - git checkout %BRANCH_TO_TEST% + - git checkout %BOOST_BRANCH% #- git submodule foreach "git reset --quiet --hard" #- git submodule foreach "git clean -fxd" - git reset --hard diff --git a/test/async_fut.cpp b/test/async_fut.cpp index bcb3e860..e9ea01fc 100644 --- a/test/async_fut.cpp +++ b/test/async_fut.cpp @@ -39,6 +39,7 @@ BOOST_AUTO_TEST_CASE(async_out_future, *boost::unit_test::timeout(2)) std::error_code ec; std::future fut; + std::future fut_in; boost::asio::streambuf in_buf; @@ -48,7 +49,7 @@ BOOST_AUTO_TEST_CASE(async_out_future, *boost::unit_test::timeout(2)) bp::child c( master_test_suite().argv[1], "test", "--prefix-once", "test", - bp::std_in < in_buf, + bp::std_in < in_buf > fut_in, bp::std_out > fut, io_service, ec @@ -59,6 +60,9 @@ BOOST_AUTO_TEST_CASE(async_out_future, *boost::unit_test::timeout(2)) io_service.run(); BOOST_REQUIRE(fut.valid()); + BOOST_REQUIRE(fut_in.valid()); + BOOST_CHECK_NO_THROW(fut_in.get()); + std::string line; BOOST_CHECK_NO_THROW(line = fut.get()); diff --git a/test/coroutine_test.cpp b/test/coroutine_test.cpp index 6fd4d58c..8458335e 100644 --- a/test/coroutine_test.cpp +++ b/test/coroutine_test.cpp @@ -157,13 +157,13 @@ BOOST_AUTO_TEST_CASE(stackless, *boost::unit_test::timeout(15)) { if (!ec) reenter (this) { - yield bp::child(master_test_suite().argv[1], + yield bp::child(master_test_suite().argv[1], "test", "--exit-code", "42", ios, bp::on_exit= [this](int ret, const std::error_code&) { - exit_code = ret; + exit_code = ret; (*this)(); }).detach(); diff --git a/test/env.cpp b/test/env.cpp index 33584578..5d709897 100644 --- a/test/env.cpp +++ b/test/env.cpp @@ -11,11 +11,12 @@ #define BOOST_TEST_IGNORE_SIGCHLD #include +#include +#include +#include #include #include #include -#include -#include #include @@ -24,6 +25,7 @@ #include #include #include +#include namespace bp = boost::process; @@ -49,8 +51,8 @@ BOOST_AUTO_TEST_CASE(inherit_env, *boost::unit_test::timeout(2)) auto path = boost::this_process::environment()["PATH"].to_string(); - std::cout << "Path: '" << path << "'" << std::endl; - std::cout << "Valu: '" << s << "'" << std::endl; + std::cout << "Path : '" << path << "'" << std::endl; + std::cout << "Value: '" << s << "'" << std::endl; if(!path.empty()) { @@ -61,6 +63,8 @@ BOOST_AUTO_TEST_CASE(inherit_env, *boost::unit_test::timeout(2)) path.begin(), path.begin() + size ); } + else + BOOST_CHECK(boost::starts_with(s, "************** empty environment **************")); c.wait(); } @@ -73,6 +77,16 @@ BOOST_AUTO_TEST_CASE(inherit_mod_env, *boost::unit_test::timeout(2)) std::string value = "TestString"; ie["BOOST_PROCESS_TEST_1"] = value; + { + auto ie2 = boost::this_process::environment(); + auto val = ie2["BOOST_PROCESS_TEST_1"]; + auto st = val.to_string(); + + BOOST_CHECK_EQUAL_COLLECTIONS( + st.begin(), st.end(), + value.begin(), value.end() + ); + } bp::ipstream st; std::error_code ec; @@ -127,3 +141,38 @@ BOOST_AUTO_TEST_CASE(modifided_env, *boost::unit_test::timeout(2)) c.wait(); } + +BOOST_AUTO_TEST_CASE(append, *boost::unit_test::timeout(5)) +{ + using boost::unit_test::framework::master_test_suite; + + bp::ipstream st; + BOOST_TEST_PASSPOINT(); + bp::environment e = boost::this_process::environment(); + + std::error_code ec; + BOOST_REQUIRE_GE(e.size(), 1u); + + std::list arg = {"test", "--query", "BOOST_PROCESS_TEST_3"}; + bp::child c( + master_test_suite().argv[1], + bp::env["BOOST_PROCESS_TEST_3"]="some_string", + bp::env=e, + bp::env["BOOST_PROCESS_TEST_3"]=boost::none, + bp::env["BOOST_PROCESS_TEST_3"]+="some_fictional_path_42", + bp::env["BOOST_PROCESS_TEST_3"]+={"other", "next"}, + bp::args=arg, + bp::std_out>st, + ec + ); + + BOOST_REQUIRE(!ec); + BOOST_WARN(c.running()); + std::string s; + + std::getline(st, s); + + BOOST_CHECK(boost::starts_with(s, "some_fictional_path_42;other;next")); + + c.wait(); +} diff --git a/test/environment.cpp b/test/environment.cpp new file mode 100644 index 00000000..77915e8f --- /dev/null +++ b/test/environment.cpp @@ -0,0 +1,132 @@ +// Copyright (c) 2006, 2007 Julio M. Merino Vidal +// Copyright (c) 2008 Ilya Sokolov, Boris Schaeling +// Copyright (c) 2009 Boris Schaeling +// Copyright (c) 2010 Felipe Tanus, Boris Schaeling +// Copyright (c) 2011, 2012 Jeff Flinn, Boris Schaeling +// +// 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) + +#define BOOST_TEST_MAIN +#define BOOST_TEST_IGNORE_SIGCHLD +#include + +#include + +namespace bp = boost::process; + + +namespace std +{ +std::ostream & operator<<(std::ostream & str, const std::wstring & ws) +{ + str << bp::detail::convert(ws); + return str; +} +} + +BOOST_AUTO_TEST_CASE(empty) +{ + bp::environment ev ; + BOOST_CHECK(ev.empty()); + BOOST_CHECK_EQUAL(ev.size(), 0u); + BOOST_CHECK_EQUAL(ev.end() - ev.begin(), 0); + ev["Thingy"] = "My value"; + + BOOST_CHECK(!ev.empty()); + BOOST_CHECK_EQUAL(ev.size(), 1u); + BOOST_CHECK_EQUAL(ev.end() - ev.begin(), 1); + + for (auto x : ev) + { + BOOST_CHECK_EQUAL(x.to_string(), "My value"); + BOOST_CHECK_EQUAL(x.get_name(), "Thingy"); + } + + ev["Thingy"].clear(); + BOOST_CHECK(ev.empty()); + BOOST_CHECK_EQUAL(ev.size(), 0u); + BOOST_CHECK_EQUAL(ev.end() - ev.begin(), 0); +} + +BOOST_AUTO_TEST_CASE(wempty) +{ + bp::wenvironment ev ; + BOOST_CHECK(ev.empty()); + BOOST_CHECK_EQUAL(ev.size(), 0u); + BOOST_CHECK_EQUAL(ev.end() - ev.begin(), 0); + ev[L"Thingy"] = L"My value"; + + BOOST_CHECK(!ev.empty()); + BOOST_CHECK_EQUAL(ev.size(), 1u); + BOOST_CHECK_EQUAL(ev.end() - ev.begin(), 1); + + for (auto x : ev) + { + BOOST_CHECK(x.to_string() == L"My value"); + BOOST_CHECK(x.get_name() == L"Thingy"); + } + + ev[L"Thingy"].clear(); + BOOST_CHECK(ev.empty()); + BOOST_CHECK_EQUAL(ev.size(), 0u); + BOOST_CHECK_EQUAL(ev.end() - ev.begin(), 0); +} + +BOOST_AUTO_TEST_CASE(compare) +{ + auto nat = boost::this_process::environment(); + bp::environment env = nat; + + { + BOOST_CHECK_EQUAL(nat.size(), env.size()); + auto ni = nat.begin(); + auto ei = env.begin(); + + while ((ni != nat.end()) &&(ei != env.end())) + { + BOOST_CHECK_EQUAL(ni->get_name(), ei->get_name()); + BOOST_CHECK_EQUAL(ni->to_string(), ei->to_string()); + ni++; ei++; + } + } + + //ok check if I can convert it. + bp::wenvironment wenv{env}; + auto wnat = boost::this_process::wenvironment(); + BOOST_CHECK_EQUAL(wenv.size(), env.size()); + BOOST_CHECK_EQUAL(wnat.size(), nat.size()); + { + BOOST_CHECK_EQUAL(wnat.size(), wenv.size()); + auto ni = wnat.begin(); + auto ei = wenv.begin(); + + while ((ni != wnat.end()) && (ei != wenv.end())) + { + BOOST_CHECK_EQUAL(ni->get_name() , ei->get_name()); + BOOST_CHECK_EQUAL(ni->to_string(), ei->to_string()); + ni++; ei++; + } + + BOOST_CHECK(ni == wnat.end()); + } +} + +BOOST_AUTO_TEST_CASE(insert_remove) +{ + bp::environment env(boost::this_process::environment()); + + auto sz = env.size(); + BOOST_REQUIRE_GE(sz, 1u); + BOOST_REQUIRE_EQUAL(env.count("BOOST_TEST_VAR"), 0u); + + env["BOOST_TEST_VAR"] = {"some string", "badabumm"}; + + BOOST_CHECK_EQUAL(env["BOOST_TEST_VAR"].to_string(), "some string;badabumm"); + BOOST_CHECK_EQUAL(sz +1, env.size()); + + env["BOOST_TEST_VAR"].clear(); + + BOOST_CHECK_EQUAL(env.size(), sz); + +} diff --git a/test/pipe.cpp b/test/pipe.cpp index 81a001a1..6a0bcc31 100644 --- a/test/pipe.cpp +++ b/test/pipe.cpp @@ -124,15 +124,18 @@ BOOST_AUTO_TEST_CASE(large_data, *boost::unit_test::timeout(20)) { bp::pipe pipe; - bp::ipstream is(pipe); - bp::opstream os(pipe); + bp::pipebuf is_buf(pipe); + bp::pipebuf os_buf(std::move(pipe)); + + std::istream is(&is_buf); + std::ostream os(&os_buf); std::string in(1000000, '0'); std::string out; int cnt = 0; for (auto & c: in) - c = (cnt++ % 26) + 'A'; + c = (cnt++ % 26) + 'A'; std::thread th([&]{os << in << std::endl;}); @@ -141,4 +144,89 @@ BOOST_AUTO_TEST_CASE(large_data, *boost::unit_test::timeout(20)) th.join(); } +BOOST_AUTO_TEST_CASE(closed, *boost::unit_test::timeout(2)) +{ + bp::opstream os; + bp::ipstream is; + + os.pipe().close(); + is.pipe().close(); + + int i; + + BOOST_CHECK(!(os << 42 << endl)); + BOOST_CHECK(!(is >> i)); +} + + +BOOST_AUTO_TEST_CASE(coverage, *boost::unit_test::timeout(5)) +{ + //more of a syntax check, since template. + { + bp::pipe p1; + bp::ipstream is1(p1); + bp::ipstream is2(std::move(p1)); + + is2.pipe(is1.pipe()); + + bp::pipe p2_; + bp::pipe p2 = p2_; + BOOST_REQUIRE_NO_THROW(p2_ == p2); + BOOST_CHECK(p2_ == p2); + + bp::opstream os1(p2); + bp::opstream os2(std::move(p2)); + + os2.pipe(os1.pipe()); + + bp::pipe p3; + is1 = p3; + is2 = std::move(p3); + + bp::pipe p4_; + bp::pipe p4 = std::move(p4_); + + bp::pipe p5; + BOOST_REQUIRE_NO_THROW(p4_ != p4); + BOOST_CHECK(p4_ != p4); + + BOOST_REQUIRE_NO_THROW(p5 != p4); + BOOST_CHECK(p4 != p5); + + is1 = p4; + is2 = std::move(p4); + } + { + bp::wpipe p; + bp::wpstream ws1(p); + bp::wpstream ws2(std::move(p)); + + ws2.pipe(std::move(ws1.pipe())); + + bp::wpipe p2; + + ws1 = p2; + ws2 = std::move(p2); + + const bp::wpstream & ws2c = ws2; + ws1.pipe(ws2c.pipe()); + } + + { + bp::wpipe p; + bp::wpipebuf ws1(p); + bp::wpipebuf ws2(std::move(p)); + + ws2.pipe(std::move(ws1.pipe())); + + bp::wpipe p2; + + ws1 = p2; + ws2 = std::move(p2); + + const bp::wpipebuf & ws2c = ws2; + ws1.pipe(ws2c.pipe()); + + } +} diff --git a/test/sparring_partner.cpp b/test/sparring_partner.cpp index 3de9562a..a7825d91 100644 --- a/test/sparring_partner.cpp +++ b/test/sparring_partner.cpp @@ -54,7 +54,7 @@ int main(int argc, char *argv[]) ("prefix", value()) ("prefix-once", value()) ("pwd", bool_switch()) - ("query-env", value()) + ("query", value()) ("stdin-to-stdout", bool_switch()) #if defined(BOOST_POSIX_API) ("posix-echo-one", value >()->multitoken()) @@ -165,11 +165,15 @@ int main(int argc, char *argv[]) { std::cout << boost::filesystem::current_path().string() << std::endl; } - else if (vm.count("query-env")) + else if (vm.count("query")) { - auto key = vm["query-env"].as(); - - std::cout << boost::this_process::environment()[key].to_string() << std::endl; + auto key = vm["query"].as(); + auto env = boost::this_process::environment(); + auto val = env[key]; + if (val.empty()) + std::cout << "************** empty environment **************" << std::endl; + else + std::cout << val.to_string() << std::endl; } else if (vm["stdin-to-stdout"].as()) { diff --git a/test/wargs_cmd.cpp b/test/wargs_cmd.cpp new file mode 100644 index 00000000..e8977512 --- /dev/null +++ b/test/wargs_cmd.cpp @@ -0,0 +1,115 @@ +// Copyright (c) 2006, 2007 Julio M. Merino Vidal +// Copyright (c) 2008 Ilya Sokolov, Boris Schaeling +// Copyright (c) 2009 Boris Schaeling +// Copyright (c) 2010 Felipe Tanus, Boris Schaeling +// Copyright (c) 2011, 2012 Jeff Flinn, Boris Schaeling +// +// 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) + +#define BOOST_TEST_MAIN +#define BOOST_TEST_IGNORE_SIGCHLD + +#include + +#include +#include +#include +#include +#include +#include + +#include + +namespace bp = boost::process; + + +BOOST_AUTO_TEST_CASE(args, *boost::unit_test::timeout(2)) +{ + using boost::unit_test::framework::master_test_suite; + + bp::ipstream is; + + std::error_code ec; + bp::child c( + master_test_suite().argv[1], + L"test", "--echo-argv", L"hello thingy", "\"stuff\"", L" spa ce ", + bp::std_out>is, + ec + ); + if (ec) + std::cout << "EC: " << ec.message() << std::endl; + BOOST_REQUIRE(!ec); + return ; + + std::string s; + + std::getline(is, s); + s.resize(4); + BOOST_CHECK_EQUAL(s, "test"); + + std::getline(is, s); + s.resize(11); + BOOST_CHECK_EQUAL(s, "--echo-argv"); + + std::getline(is, s); + s.resize(12); + + BOOST_CHECK_EQUAL(s, "hello thingy"); + + std::getline(is, s); + s.resize(7); + + BOOST_CHECK_EQUAL(s, "\"stuff\""); + + std::getline(is, s); + s.resize(10); + + BOOST_CHECK_EQUAL(s, " spa ce "); + +} + + +BOOST_AUTO_TEST_CASE(cmd, *boost::unit_test::timeout(2)) +{ + using boost::unit_test::framework::master_test_suite; + + bp::ipstream is; + + std::error_code ec; + + std::wstring cmd = + bp::detail::convert(master_test_suite().argv[1]); + cmd+= L" test --echo-argv \"hello thingy\" \\\"stuff\\\" \" spa ce \""; + + bp::child c(cmd, + bp::std_out>is, + ec + ); + BOOST_REQUIRE(!ec); + return ; + std::string s; + + std::getline(is, s); + s.resize(4); + BOOST_CHECK_EQUAL(s, "test"); + + std::getline(is, s); + s.resize(11); + BOOST_CHECK_EQUAL(s, "--echo-argv"); + + std::getline(is, s); + s.resize(12); + + BOOST_CHECK_EQUAL(s, "hello thingy"); + + std::getline(is, s); + s.resize(7); + + BOOST_CHECK_EQUAL(s, "\"stuff\""); + + std::getline(is, s); + s.resize(10); + + BOOST_CHECK_EQUAL(s, " spa ce "); +}