// 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_WINDOWS_ENV_STORAGE_HPP_ #define BOOST_PROCESS_DETAIL_WINDOWS_ENV_STORAGE_HPP_ #include #include #include #include #include #include #include #include #include #include namespace boost { namespace process { namespace detail { namespace windows { template class native_environment_impl { static void _deleter(Char* p) {boost::winapi::free_environment_strings(p);}; std::unique_ptr _buf{boost::winapi::get_environment_strings(), &native_environment_impl::_deleter}; static inline std::vector _load_var(Char* p); std::vector _env_arr{_load_var(_buf.get())}; public: using char_type = Char; using pointer_type = const char_type*; using string_type = std::basic_string; using native_handle_type = pointer_type; void reload() { _buf.reset(boost::winapi::get_environment_strings()); _env_arr = _load_var(_buf.get()); _env_impl = &*_env_arr.begin(); } 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 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()); } void reset(const string_type & id) {reset(id.c_str());} 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; Char ** _env_impl = &*_env_arr.begin(); native_handle_type native_handle() const {return _buf.get();} }; template inline auto native_environment_impl::get(const pointer_type id) -> string_type { Char buf[4096]; auto size = boost::winapi::get_environment_variable(id, buf, sizeof(buf)); if (size == 0) //failed { auto err = ::boost::winapi::GetLastError(); if (err == ::boost::winapi::ERROR_ENVVAR_NOT_FOUND_)//well, then we consider that an empty value return ""; else throw process_error(std::error_code(err, std::system_category()), "GetEnvironmentVariable() failed"); } 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::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); } template inline void native_environment_impl::set(const pointer_type id, const pointer_type value) { boost::winapi::set_environment_variable(id, value); } template inline void native_environment_impl::reset(const pointer_type id) { boost::winapi::set_environment_variable(id, nullptr); } template std::vector native_environment_impl::_load_var(Char* p) { std::vector ret; if (*p != null_char()) { ret.push_back(p); while ((*p != null_char()) || (*(p+1) != null_char())) { if (*p==null_char()) { p++; ret.push_back(p); } else p++; } } p++; ret.push_back(nullptr); return ret; } template struct basic_environment_impl { std::vector _data = {null_char()}; static std::vector _load_var(Char* p); std::vector _env_arr{_load_var(_data.data())}; public: using char_type = Char; 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()); _env_impl = _env_arr.data(); } 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), value);} void reset(const pointer_type id) {reset(string_type(id));} string_type get(const string_type & id); void set(const string_type & id, const string_type & value); void reset(const string_type & id); 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 && 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; } Char ** _env_impl = &*_env_arr.begin(); native_handle_type native_handle() const {return &*_data.begin();} }; template basic_environment_impl::basic_environment_impl(const native_environment_impl & nei) { auto beg = nei.native_handle(); auto p = beg; 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(); } template inline auto basic_environment_impl::get(const string_type &id) -> string_type { if (std::equal(id.begin(), id.end(), _data.begin()) && (_data[id.size()] == equal_sign())) return string_type(_data.data()); //null-char is handled by the string. std::vector seq = {'\0'}; //using a vector, because strings might cause problems with nullchars seq.insert(seq.end(), id.begin(), id.end()); seq.push_back('='); auto itr = std::search(_data.begin(), _data.end(), seq.begin(), seq.end()); if (itr == _data.end()) //not found return ""; itr += seq.size(); //advance to the value behind the '='; the std::string will take care of finding the null-char. return string_type(&*itr); } template inline void basic_environment_impl::set(const string_type &id, const string_type &value) { reset(id); std::vector insertion; insertion.insert(insertion.end(), id.begin(), id.end()); insertion.push_back('='); insertion.insert(insertion.end(), value.begin(), value.end()); insertion.push_back('\0'); _data.insert(_data.end() -1, insertion.begin(), insertion.end()); reload(); } 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()] == equal_sign())) { auto beg = _data.begin(); auto end = beg; while (*end != '\0') end++; end++; //to point behind the last null-char _data.erase(beg, end); //and remove the thingy } std::vector seq = {'\0'}; //using a vector, because strings might cause problems with nullchars seq.insert(seq.end(), id.begin(), id.end()); seq.push_back('='); auto itr = std::search(_data.begin(), _data.end(), seq.begin(), seq.end()); if (itr == _data.end()) return;//nothing to return if it's empty anyway... auto end = itr; while (*++end != '\0'); _data.erase(itr, end);//and remove it reload(); } template std::vector basic_environment_impl::_load_var(Char* p) { std::vector ret; if (*p != null_char()) { ret.push_back(p); while ((*p != null_char()) || (*(p+1) != null_char())) { if (*p==null_char()) { p++; ret.push_back(p); } else p++; } } p++; ret.push_back(nullptr); return ret; } template constexpr T env_seperator(); template<> constexpr char env_seperator() {return ';'; } template<> constexpr wchar_t env_seperator() {return L';'; } inline int get_id() {return boost::winapi::GetCurrentProcessId();} inline void* native_handle() {return boost::winapi::GetCurrentProcess(); } typedef void* native_handle_t; } } } } #endif /* BOOST_PROCESS_DETAIL_WINDOWS_ENV_STORAGE_HPP_ */