environment.hpp 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354
  1. // Copyright (c) 2016 Klemens D. Morgenstern
  2. //
  3. // Distributed under the Boost Software License, Version 1.0. (See accompanying
  4. // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
  5. #ifndef BOOST_PROCESS_DETAIL_WINDOWS_ENV_STORAGE_HPP_
  6. #define BOOST_PROCESS_DETAIL_WINDOWS_ENV_STORAGE_HPP_
  7. #include <string>
  8. #include <vector>
  9. #include <unordered_map>
  10. #include <boost/winapi/error_codes.hpp>
  11. #include <boost/winapi/environment.hpp>
  12. #include <boost/winapi/get_current_process.hpp>
  13. #include <boost/winapi/get_current_process_id.hpp>
  14. #include <boost/process/detail/config.hpp>
  15. #include <algorithm>
  16. #include <boost/process/locale.hpp>
  17. namespace boost { namespace process { namespace detail { namespace windows {
  18. template<typename Char>
  19. class native_environment_impl
  20. {
  21. static void _deleter(Char* p) {boost::winapi::free_environment_strings(p);};
  22. std::unique_ptr<Char[], void(*)(Char*)> _buf{boost::winapi::get_environment_strings<Char>(), &native_environment_impl::_deleter};
  23. static inline std::vector<Char*> _load_var(Char* p);
  24. std::vector<Char*> _env_arr{_load_var(_buf.get())};
  25. public:
  26. using char_type = Char;
  27. using pointer_type = const char_type*;
  28. using string_type = std::basic_string<char_type>;
  29. using native_handle_type = pointer_type;
  30. void reload()
  31. {
  32. _buf.reset(boost::winapi::get_environment_strings<Char>());
  33. _env_arr = _load_var(_buf.get());
  34. _env_impl = &*_env_arr.begin();
  35. }
  36. string_type get(const pointer_type id);
  37. void set(const pointer_type id, const pointer_type value);
  38. void reset(const pointer_type id);
  39. string_type get(const string_type & id) {return get(id.c_str());}
  40. void set(const string_type & id, const string_type & value) {set(id.c_str(), value.c_str()); }
  41. void reset(const string_type & id) {reset(id.c_str());}
  42. native_environment_impl() = default;
  43. native_environment_impl(const native_environment_impl& ) = delete;
  44. native_environment_impl(native_environment_impl && ) = default;
  45. native_environment_impl & operator=(const native_environment_impl& ) = delete;
  46. native_environment_impl & operator=(native_environment_impl && ) = default;
  47. Char ** _env_impl = &*_env_arr.begin();
  48. native_handle_type native_handle() const {return _buf.get();}
  49. };
  50. template<typename Char>
  51. inline auto native_environment_impl<Char>::get(const pointer_type id) -> string_type
  52. {
  53. Char buf[4096];
  54. auto size = boost::winapi::get_environment_variable(id, buf, sizeof(buf));
  55. if (size == 0) //failed
  56. {
  57. auto err = ::boost::winapi::GetLastError();
  58. if (err == ::boost::winapi::ERROR_ENVVAR_NOT_FOUND_)//well, then we consider that an empty value
  59. return "";
  60. else
  61. throw process_error(std::error_code(err, std::system_category()),
  62. "GetEnvironmentVariable() failed");
  63. }
  64. if (size == sizeof(buf)) //the return size gives the size without the null, so I know this went wrong
  65. {
  66. /*limit defined here https://msdn.microsoft.com/en-us/library/windows/desktop/ms683188(v=vs.85).aspx
  67. * but I used 32768 so it is a multiple of 4096.
  68. */
  69. constexpr static std::size_t max_size = 32768;
  70. //Handle variables longer then buf.
  71. std::size_t buf_size = sizeof(buf);
  72. while (buf_size <= max_size)
  73. {
  74. std::vector<Char> buf(buf_size);
  75. auto size = boost::winapi::get_environment_variable(id, buf.data(), buf.size());
  76. if (size == buf_size) //buffer to small
  77. buf_size *= 2;
  78. else if (size == 0)
  79. ::boost::process::detail::throw_last_error("GetEnvironmentVariable() failed");
  80. else
  81. return std::basic_string<Char>(
  82. buf.data(), buf.data()+ size + 1);
  83. }
  84. }
  85. return std::basic_string<Char>(buf, buf+size+1);
  86. }
  87. template<typename Char>
  88. inline void native_environment_impl<Char>::set(const pointer_type id, const pointer_type value)
  89. {
  90. boost::winapi::set_environment_variable(id, value);
  91. }
  92. template<typename Char>
  93. inline void native_environment_impl<Char>::reset(const pointer_type id)
  94. {
  95. boost::winapi::set_environment_variable(id, nullptr);
  96. }
  97. template<typename Char>
  98. std::vector<Char*> native_environment_impl<Char>::_load_var(Char* p)
  99. {
  100. std::vector<Char*> ret;
  101. if (*p != null_char<Char>())
  102. {
  103. ret.push_back(p);
  104. while ((*p != null_char<Char>()) || (*(p+1) != null_char<Char>()))
  105. {
  106. if (*p==null_char<Char>())
  107. {
  108. p++;
  109. ret.push_back(p);
  110. }
  111. else
  112. p++;
  113. }
  114. }
  115. p++;
  116. ret.push_back(nullptr);
  117. return ret;
  118. }
  119. template<typename Char>
  120. struct basic_environment_impl
  121. {
  122. std::vector<Char> _data = {null_char<Char>()};
  123. static std::vector<Char*> _load_var(Char* p);
  124. std::vector<Char*> _env_arr{_load_var(_data.data())};
  125. public:
  126. using char_type = Char;
  127. using pointer_type = const char_type*;
  128. using string_type = std::basic_string<char_type>;
  129. using native_handle_type = pointer_type;
  130. std::size_t size() const { return _data.size();}
  131. void reload()
  132. {
  133. _env_arr = _load_var(_data.data());
  134. _env_impl = _env_arr.data();
  135. }
  136. string_type get(const pointer_type id) {return get(string_type(id));}
  137. void set(const pointer_type id, const pointer_type value) {set(string_type(id), value);}
  138. void reset(const pointer_type id) {reset(string_type(id));}
  139. string_type get(const string_type & id);
  140. void set(const string_type & id, const string_type & value);
  141. void reset(const string_type & id);
  142. inline basic_environment_impl(const native_environment_impl<Char> & nei);
  143. basic_environment_impl() = default;
  144. basic_environment_impl(const basic_environment_impl& rhs)
  145. : _data(rhs._data)
  146. {
  147. }
  148. basic_environment_impl(basic_environment_impl && rhs)
  149. : _data(std::move(rhs._data)),
  150. _env_arr(std::move(rhs._env_arr)),
  151. _env_impl(_env_arr.data())
  152. {
  153. }
  154. basic_environment_impl &operator=(basic_environment_impl && rhs)
  155. {
  156. _data = std::move(rhs._data);
  157. //reload();
  158. _env_arr = std::move(rhs._env_arr);
  159. _env_impl = _env_arr.data();
  160. return *this;
  161. }
  162. basic_environment_impl & operator=(const basic_environment_impl& rhs)
  163. {
  164. _data = rhs._data;
  165. reload();
  166. return *this;
  167. }
  168. template<typename CharR>
  169. explicit inline basic_environment_impl(
  170. const basic_environment_impl<CharR>& rhs,
  171. const ::boost::process::codecvt_type & cv = ::boost::process::codecvt())
  172. : _data(::boost::process::detail::convert(rhs._data, cv))
  173. {
  174. }
  175. template<typename CharR>
  176. basic_environment_impl & operator=(const basic_environment_impl<CharR>& rhs)
  177. {
  178. _data = ::boost::process::detail::convert(rhs._data);
  179. _env_arr = _load_var(&*_data.begin());
  180. _env_impl = &*_env_arr.begin();
  181. return *this;
  182. }
  183. Char ** _env_impl = &*_env_arr.begin();
  184. native_handle_type native_handle() const {return &*_data.begin();}
  185. };
  186. template<typename Char>
  187. basic_environment_impl<Char>::basic_environment_impl(const native_environment_impl<Char> & nei)
  188. {
  189. auto beg = nei.native_handle();
  190. auto p = beg;
  191. while ((*p != null_char<Char>()) || (*(p+1) != null_char<Char>()))
  192. p++;
  193. p++; //pointing to the second nullchar
  194. p++; //to get the pointer behing the second nullchar, so it's end.
  195. this->_data.assign(beg, p);
  196. this->reload();
  197. }
  198. template<typename Char>
  199. inline auto basic_environment_impl<Char>::get(const string_type &id) -> string_type
  200. {
  201. if (std::equal(id.begin(), id.end(), _data.begin()) && (_data[id.size()] == equal_sign<Char>()))
  202. return string_type(_data.data()); //null-char is handled by the string.
  203. std::vector<Char> seq = {'\0'}; //using a vector, because strings might cause problems with nullchars
  204. seq.insert(seq.end(), id.begin(), id.end());
  205. seq.push_back('=');
  206. auto itr = std::search(_data.begin(), _data.end(), seq.begin(), seq.end());
  207. if (itr == _data.end()) //not found
  208. return "";
  209. itr += seq.size(); //advance to the value behind the '='; the std::string will take care of finding the null-char.
  210. return string_type(&*itr);
  211. }
  212. template<typename Char>
  213. inline void basic_environment_impl<Char>::set(const string_type &id, const string_type &value)
  214. {
  215. reset(id);
  216. std::vector<Char> insertion;
  217. insertion.insert(insertion.end(), id.begin(), id.end());
  218. insertion.push_back('=');
  219. insertion.insert(insertion.end(), value.begin(), value.end());
  220. insertion.push_back('\0');
  221. _data.insert(_data.end() -1, insertion.begin(), insertion.end());
  222. reload();
  223. }
  224. template<typename Char>
  225. inline void basic_environment_impl<Char>::reset(const string_type &id)
  226. {
  227. //ok, we need to check the size of data first
  228. if (id.size() >= _data.size()) //ok, so it's impossible id is in there.
  229. return;
  230. //check if it's the first one, spares us the search.
  231. if (std::equal(id.begin(), id.end(), _data.begin()) && (_data[id.size()] == equal_sign<Char>()))
  232. {
  233. auto beg = _data.begin();
  234. auto end = beg;
  235. while (*end != '\0')
  236. end++;
  237. end++; //to point behind the last null-char
  238. _data.erase(beg, end); //and remove the thingy
  239. }
  240. std::vector<Char> seq = {'\0'}; //using a vector, because strings might cause problems with nullchars
  241. seq.insert(seq.end(), id.begin(), id.end());
  242. seq.push_back('=');
  243. auto itr = std::search(_data.begin(), _data.end(), seq.begin(), seq.end());
  244. if (itr == _data.end())
  245. return;//nothing to return if it's empty anyway...
  246. auto end = itr;
  247. while (*++end != '\0');
  248. _data.erase(itr, end);//and remove it
  249. reload();
  250. }
  251. template<typename Char>
  252. std::vector<Char*> basic_environment_impl<Char>::_load_var(Char* p)
  253. {
  254. std::vector<Char*> ret;
  255. if (*p != null_char<Char>())
  256. {
  257. ret.push_back(p);
  258. while ((*p != null_char<Char>()) || (*(p+1) != null_char<Char>()))
  259. {
  260. if (*p==null_char<Char>())
  261. {
  262. p++;
  263. ret.push_back(p);
  264. }
  265. else
  266. p++;
  267. }
  268. }
  269. p++;
  270. ret.push_back(nullptr);
  271. return ret;
  272. }
  273. template<typename T> constexpr T env_seperator();
  274. template<> constexpr char env_seperator() {return ';'; }
  275. template<> constexpr wchar_t env_seperator() {return L';'; }
  276. inline int get_id() {return boost::winapi::GetCurrentProcessId();}
  277. inline void* native_handle() {return boost::winapi::GetCurrentProcess(); }
  278. typedef void* native_handle_t;
  279. }
  280. }
  281. }
  282. }
  283. #endif /* BOOST_PROCESS_DETAIL_WINDOWS_ENV_STORAGE_HPP_ */