// 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_ENVIRONMENT_HPP_ #define BOOST_PROCESS_ENVIRONMENT_HPP_ #include <boost/process/detail/config.hpp> #include <boost/algorithm/string/split.hpp> #include <boost/algorithm/string/case_conv.hpp> #include <boost/iterator/transform_iterator.hpp> #include <boost/filesystem/path.hpp> #if defined(BOOST_POSIX_API) #include <boost/process/detail/posix/environment.hpp> #elif defined(BOOST_WINDOWS_API) #include <boost/process/detail/windows/environment.hpp> #endif namespace boost { namespace process { namespace detail { template<typename Char, typename Environment> struct const_entry { using value_type = Char ; using pointer = const value_type * ; using string_type = std::basic_string<value_type> ; using range = boost::iterator_range<pointer> ; using environment_t = Environment ; std::vector<string_type> to_vector() const { if (_data == nullptr) return std::vector<string_type>(); std::vector<string_type> data; auto str = string_type(_data); struct splitter { bool operator()(wchar_t w) const {return w == api::env_seperator<wchar_t>();} bool operator()(char c) const {return c == api::env_seperator<char> ();} } s; boost::split(data, _data, s); return data; } string_type to_string() const { if (_data != nullptr) return string_type(_data); else return string_type(); } string_type get_name() const {return string_type(_name.begin(), _name.end());} explicit const_entry(string_type&& name, pointer data, environment_t & env_) : _name(std::move(name)), _data(data), _env(&env_) {} explicit const_entry(string_type &&name, environment_t & env) : _name(std::move(name)), _data(nullptr), _env(&env) {} const_entry(const const_entry&) = default; const_entry& operator=(const const_entry&) = default; void reload() { auto p = _env->find(_name); if (p == _env->end()) _data = nullptr; else _data = p->_data; this->_env->reload(); } bool empty() const { return _data == nullptr; } protected: string_type _name; pointer _data; environment_t * _env; }; template<typename Char, typename Environment> struct entry : const_entry<Char, Environment> { using father = const_entry<Char, Environment>; using value_type = typename father::value_type; using string_type = typename father::string_type; using pointer = typename father::pointer; using environment_t = typename father::environment_t; explicit entry(string_type&& name, pointer data, environment_t & env) : father(std::move(name), data, env) {} explicit entry(string_type &&name, environment_t & env_) : father(std::move(name), env_) {} entry(const entry&) = default; entry& operator=(const entry&) = default; void assign(const string_type &value) { this->_env->set(this->_name, value); this->reload(); } void assign(const std::vector<string_type> &value) { string_type data; for (auto &v : value) { if (&v != &value.front()) data += api::env_seperator<value_type>(); data += v; } this->_env->set(this->_name, data); this->reload(); } void assign(const std::initializer_list<string_type> &value) { string_type data; for (auto &v : value) { if (&v != &*value.begin()) data += api::env_seperator<value_type>(); data += v; } this->_env->set(this->_name, data); this->reload(); } void append(const string_type &value) { if (this->_data == nullptr) this->_env->set(this->_name, value); else { string_type st = this->_data; this->_env->set(this->_name, st + api::env_seperator<value_type>() + value); } this->reload(); } void clear() { this->_env->reset(this->_name); this->_env->reload(); this->_data = nullptr; } entry &operator=(const string_type & value) { assign(value); return *this; } entry &operator=(const std::vector<string_type> & value) { assign(value); return *this; } entry &operator=(const std::initializer_list<string_type> & value) { assign(value); return *this; } entry &operator+=(const string_type & value) { append(value); return *this; } }; template<typename Char, typename Environment> struct make_entry { make_entry(const make_entry&) = default; make_entry& operator=(const make_entry&) = default; Environment *env; make_entry(Environment & env) : env(&env) {}; entry<Char, Environment> operator()(const Char* data) const { auto p = data; while ((*p != equal_sign<Char>()) && (*p != null_char<Char>())) p++; auto name = std::basic_string<Char>(data, p); p++; //go behind equal sign return entry<Char, Environment>(std::move(name), p, *env); } }; template<typename Char, typename Environment> struct make_const_entry { 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) {}; const_entry<Char, Environment> operator()(const Char* data) const { auto p = data; while ((*p != equal_sign<Char>()) && (*p != null_char<Char>())) p++; auto name = std::basic_string<Char>(data, p); p++; //go behind equal sign return const_entry<Char, Environment>(std::move(name), p, *env); } }; } #if !defined (BOOST_PROCESS_DOXYGEN) template<typename Char, template <class> class Implementation = detail::api::basic_environment_impl> class basic_environment_impl : public Implementation<Char> { Char** _get_end() const { auto p = this->_env_impl; while (*p != nullptr) p++; return p; } public: using string_type = std::basic_string<Char>; using implementation_type = Implementation<Char>; using base_type = basic_environment_impl<Char, Implementation>; using entry_maker = detail::make_entry<Char, base_type>; using entry_type = detail::entry <Char, base_type>; using const_entry_type = detail::const_entry <Char, const base_type>; using const_entry_maker = detail::make_const_entry<Char, const base_type>; friend entry_type; friend const_entry_type; using iterator = boost::transform_iterator< entry_maker, Char**, entry_type, entry_type>; using const_iterator = boost::transform_iterator<const_entry_maker, Char**, const_entry_type, const_entry_type>; using size_type = std::size_t; iterator begin() {return iterator(this->_env_impl, entry_maker(*this));} const_iterator begin() const {return const_iterator(this->_env_impl, const_entry_maker(*this));} const_iterator cbegin() const {return const_iterator(this->_env_impl, const_entry_maker(*this));} iterator end() {return iterator(_get_end(), entry_maker(*this));} const_iterator end() const {return const_iterator(_get_end(), const_entry_maker(*this));} const_iterator cend() const {return const_iterator(_get_end(), const_entry_maker(*this));} iterator find( const string_type& key ) { auto p = this->_env_impl; auto st1 = key + ::boost::process::detail::equal_sign<Char>(); while (*p != nullptr) { if (std::equal(st1.begin(), st1.end(), *p)) break; p++; } return iterator(p, entry_maker(*this)); } const_iterator find( const string_type& key ) const { auto p = this->_env_impl; auto st1 = key + ::boost::process::detail::equal_sign<Char>(); while (*p != nullptr) { if (std::equal(st1.begin(), st1.end(), *p)) break; p++; } return const_iterator(p, const_entry_maker(*this)); } std::size_t count(const string_type & st) const { auto p = this->_env_impl; auto st1 = st + ::boost::process::detail::equal_sign<Char>(); while (*p != nullptr) { if (std::equal(st1.begin(), st1.end(), *p)) return 1u; p++; } return 0u; } void erase(const string_type & id) { implementation_type::reset(id); this->reload(); } std::pair<iterator,bool> emplace(const string_type & id, const string_type & value) { auto f = find(id); if (f == end()) { implementation_type::set(id, value); this->reload(); return std::pair<iterator, bool>(find(id), true); } else return std::pair<iterator, bool>(f, false); } using implementation_type::implementation_type; using implementation_type::operator=; using native_handle_type = typename implementation_type::native_handle_type; using implementation_type::native_handle; //copy ctor if impl is copy-constructible bool empty() { return *this->_env_impl == nullptr; } std::size_t size() const { return (_get_end() - this->_env_impl); } void clear() { std::vector<string_type> names; names.resize(size()); std::transform(cbegin(), cend(), names.begin(), [](const const_entry_type & cet){return cet.get_name();}); for (auto & nm : names) implementation_type::reset(nm); this->reload(); } entry_type at( const string_type& key ) { auto f = find(key); if (f== end()) throw std::out_of_range(key + " not found"); return *f; } const_entry_type at( const string_type& key ) const { auto f = find(key); if (f== end()) throw std::out_of_range(key + " not found"); return *f; } entry_type operator[](const string_type & key) { auto p = find(key); if (p != end()) return *p; return entry_type(string_type(key), *this); } }; #endif #if defined(BOOST_PROCESS_DOXYGEN) /**Template representation of environments. It takes a character type (`char` or `wchar_t`) * as template parameter to implement the environment */ template<typename Char> class basic_environment { public: typedef std::basic_string<Char> string_type; typedef boost::transform_iterator< entry_maker, Char**> iterator ; typedef boost::transform_iterator<const_entry_maker, Char**> const_iterator ; typedef std::size_t size_type ; iterator begin() ; ///<Returns an iterator to the beginning const_iterator begin() const ; ///<Returns an iterator to the beginning const_iterator cbegin() const ; ///<Returns an iterator to the beginning iterator end() ; ///<Returns an iterator to the end const_iterator end() const; ///<Returns an iterator to the end const_iterator cend() const; ///<Returns an iterator to the end iterator find( const string_type& key ); ///<Find a variable by its name const_iterator find( const string_type& key ) const; ///<Find a variable by its name std::size_t count(const string_type & st) const; ///<Number of variables void erase(const string_type & id); ///<Erase variable by id. ///Emplace an environment variable. std::pair<iterator,bool> emplace(const string_type & id, const string_type & value); ///Default constructor basic_environment(); ///Copy constructor. basic_environment(const basic_environment & ); ///Move constructor. basic_environment(basic_environment && ); ///Copy assignment. basic_environment& operator=(const basic_environment & ); ///Move assignment. basic_environment& operator=(basic_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; ///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 ); ///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<typename Char, typename Environment> struct const_entry_type { typedef Char value_type; typedef const value_type * pointer; typedef std::basic_string<value_type> string_type; typedef boost::iterator_range<pointer> range; typedef Environment environment_t; ///Split the entry by ";" or ":" and return it as a vector. Used by PATH. std::vector<string_type> 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<typename Char, typename Environment> struct entry_type { typedef Char value_type; typedef const value_type * pointer; typedef std::basic_string<value_type> string_type; typedef boost::iterator_range<pointer> range; typedef Environment environment_t; ///Split the entry by ";" or ":" and return it as a vector. Used by PATH. std::vector<string_type> 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 separated by ';' or ':'. void assign(const std::vector<string_type> &value); ///Append a string to the end of the entry, it will separated by ';' or ':'. 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 separated by ';' or ':'. entry &operator=(const std::vector<string_type> & value); ///Append a string to the end of the entry, it will separated by ';' or ':'. entry &operator+=(const string_type & value); }; }; /**Template representation of the environment of this process. It takes a template * as template parameter to implement the environment. All instances of this class * refer to the same environment, but might not get updated if another one makes changes. */ template<typename Char> class basic_native_environment { public: typedef std::basic_string<Char> string_type; typedef boost::transform_iterator< entry_maker, Char**> iterator ; typedef boost::transform_iterator<const_entry_maker, Char**> const_iterator ; typedef std::size_t size_type ; iterator begin() ; ///<Returns an iterator to the beginning const_iterator begin() const ; ///<Returns an iterator to the beginning const_iterator cbegin() const ; ///<Returns an iterator to the beginning iterator end() ; ///<Returns an iterator to the end const_iterator end() const; ///<Returns an iterator to the end const_iterator cend() const; ///<Returns an iterator to the end iterator find( const string_type& key ); ///<Find a variable by its name const_iterator find( const string_type& key ) const; ///<Find a variable by its name std::size_t count(const string_type & st) const; ///<Number of variables void erase(const string_type & id); ///<Erase variable by id. ///Emplace an environment variable. std::pair<iterator,bool> 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<typename Char, typename Environment> struct const_entry_type { typedef Char value_type; typedef const value_type * pointer; typedef std::basic_string<value_type> string_type; typedef boost::iterator_range<pointer> range; typedef Environment environment_t; ///Split the entry by ";" or ":" and return it as a vector. Used by PATH. std::vector<string_type> 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<typename Char, typename Environment> struct entry_type { typedef Char value_type; typedef const value_type * pointer; typedef std::basic_string<value_type> string_type; typedef boost::iterator_range<pointer> range; typedef Environment environment_t; ///Split the entry by ";" or ":" and return it as a vector. Used by PATH. std::vector<string_type> 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 separated by ';' or ':'. void assign(const std::vector<string_type> &value); ///Append a string to the end of the entry, it will separated by ';' or ':'. 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 separated by ';' or ':'. entry &operator=(const std::vector<string_type> & value); ///Append a string to the end of the entry, it will separated by ';' or ':'. entry &operator+=(const string_type & value); }; }; #endif ///Definition of the environment for the current process. template<typename Char> class basic_native_environment : public basic_environment_impl<Char, detail::api::native_environment_impl> { public: using base_type = basic_environment_impl<Char, detail::api::native_environment_impl>; using base_type::base_type; using base_type::operator=; }; ///Type definition to hold a seperate environment. template<typename Char> class basic_environment : public basic_environment_impl<Char, detail::api::basic_environment_impl> { public: using base_type = basic_environment_impl<Char, detail::api::basic_environment_impl>; using base_type::base_type; using base_type::operator=; }; #if !defined(BOOST_NO_ANSI_APIS) ///Definition of the environment for the current process. typedef basic_native_environment<char> native_environment; #endif ///Definition of the environment for the current process. typedef basic_native_environment<wchar_t> wnative_environment; #if !defined(BOOST_NO_ANSI_APIS) ///Type definition to hold a seperate environment. typedef basic_environment<char> environment; #endif ///Type definition to hold a seperate environment. typedef basic_environment<wchar_t> wenvironment; } ///Namespace containing information of the calling process. namespace this_process { ///Definition of the native handle type. typedef ::boost::process::detail::api::native_handle_t native_handle_type; #if !defined(BOOST_NO_ANSI_APIS) ///Definition of the environment for this process. using ::boost::process::native_environment; #endif ///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_type native_handle() { return ::boost::process::detail::api::native_handle();} #if !defined(BOOST_NO_ANSI_APIS) ///Get the enviroment of the current process. inline native_environment environment() { return ::boost::process:: native_environment(); } #endif ///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<boost::filesystem::path> path() { #if defined(BOOST_WINDOWS_API) const ::boost::process::wnative_environment ne{}; typedef typename ::boost::process::wnative_environment::const_entry_type value_type; static constexpr auto id = L"PATH"; #else const ::boost::process::native_environment ne{}; typedef typename ::boost::process::native_environment::const_entry_type value_type; static constexpr auto id = "PATH"; #endif 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<boost::filesystem::path> val; val.resize(vec.size()); std::copy(vec.begin(), vec.end(), val.begin()); return val; } } } #endif /* INCLUDE_BOOST_PROCESS_DETAIL_ENVIRONMENT_HPP_ */