| // 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 <string> |
| #include <vector> |
| #include <unordered_map> |
| #include <boost/winapi/error_codes.hpp> |
| #include <boost/winapi/environment.hpp> |
| #include <boost/winapi/get_current_process.hpp> |
| #include <boost/winapi/get_current_process_id.hpp> |
| #include <boost/process/detail/config.hpp> |
| #include <algorithm> |
| #include <boost/process/locale.hpp> |
| |
| namespace boost { namespace process { namespace detail { namespace windows { |
| |
| template<typename Char> |
| class native_environment_impl |
| { |
| static void _deleter(Char* p) {boost::winapi::free_environment_strings(p);}; |
| std::unique_ptr<Char[], void(*)(Char*)> _buf{boost::winapi::get_environment_strings<Char>(), &native_environment_impl::_deleter}; |
| static inline std::vector<Char*> _load_var(Char* p); |
| std::vector<Char*> _env_arr{_load_var(_buf.get())}; |
| public: |
| using char_type = Char; |
| using pointer_type = const char_type*; |
| using string_type = std::basic_string<char_type>; |
| using native_handle_type = pointer_type; |
| void reload() |
| { |
| _buf.reset(boost::winapi::get_environment_strings<Char>()); |
| _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<typename Char> |
| inline auto native_environment_impl<Char>::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<Char> 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<Char>( |
| buf.data(), buf.data()+ size + 1); |
| |
| } |
| |
| } |
| return std::basic_string<Char>(buf, buf+size+1); |
| } |
| |
| template<typename Char> |
| inline void native_environment_impl<Char>::set(const pointer_type id, const pointer_type value) |
| { |
| boost::winapi::set_environment_variable(id, value); |
| } |
| |
| template<typename Char> |
| inline void native_environment_impl<Char>::reset(const pointer_type id) |
| { |
| boost::winapi::set_environment_variable(id, nullptr); |
| } |
| |
| template<typename Char> |
| std::vector<Char*> native_environment_impl<Char>::_load_var(Char* p) |
| { |
| std::vector<Char*> ret; |
| if (*p != null_char<Char>()) |
| { |
| ret.push_back(p); |
| while ((*p != null_char<Char>()) || (*(p+1) != null_char<Char>())) |
| { |
| if (*p==null_char<Char>()) |
| { |
| p++; |
| ret.push_back(p); |
| } |
| else |
| p++; |
| } |
| } |
| p++; |
| ret.push_back(nullptr); |
| |
| return ret; |
| } |
| |
| |
| template<typename Char> |
| struct basic_environment_impl |
| { |
| std::vector<Char> _data = {null_char<Char>()}; |
| static std::vector<Char*> _load_var(Char* p); |
| std::vector<Char*> _env_arr{_load_var(_data.data())}; |
| public: |
| using char_type = Char; |
| using pointer_type = const char_type*; |
| using string_type = std::basic_string<char_type>; |
| 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<Char> & 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<typename CharR> |
| explicit inline basic_environment_impl( |
| const basic_environment_impl<CharR>& rhs, |
| const ::boost::process::codecvt_type & cv = ::boost::process::codecvt()) |
| : _data(::boost::process::detail::convert(rhs._data, cv)) |
| { |
| } |
| |
| template<typename CharR> |
| basic_environment_impl & operator=(const basic_environment_impl<CharR>& 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<typename Char> |
| basic_environment_impl<Char>::basic_environment_impl(const native_environment_impl<Char> & nei) |
| { |
| auto beg = nei.native_handle(); |
| auto p = beg; |
| while ((*p != null_char<Char>()) || (*(p+1) != null_char<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<typename Char> |
| inline auto basic_environment_impl<Char>::get(const string_type &id) -> string_type |
| { |
| |
| if (std::equal(id.begin(), id.end(), _data.begin()) && (_data[id.size()] == equal_sign<Char>())) |
| return string_type(_data.data()); //null-char is handled by the string. |
| |
| std::vector<Char> 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<typename Char> |
| inline void basic_environment_impl<Char>::set(const string_type &id, const string_type &value) |
| { |
| reset(id); |
| |
| std::vector<Char> 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<typename Char> |
| inline void basic_environment_impl<Char>::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<Char>())) |
| { |
| 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<Char> 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<typename Char> |
| std::vector<Char*> basic_environment_impl<Char>::_load_var(Char* p) |
| { |
| std::vector<Char*> ret; |
| if (*p != null_char<Char>()) |
| { |
| ret.push_back(p); |
| while ((*p != null_char<Char>()) || (*(p+1) != null_char<Char>())) |
| { |
| if (*p==null_char<Char>()) |
| { |
| p++; |
| ret.push_back(p); |
| } |
| else |
| p++; |
| } |
| } |
| p++; |
| ret.push_back(nullptr); |
| return ret; |
| } |
| |
| |
| template<typename T> 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_ */ |