357 lines
11 KiB
C++
357 lines
11 KiB
C++
// 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 (id.size() >= _data.size()) //ok, so it's impossible id is in there.
|
|
return string_type(_data.data());
|
|
|
|
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_ */
|