blob: 13aa587da86293e3807a9efe413756e8f8da2e81 [file] [log] [blame]
// 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_ */