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
6 #ifndef BOOST_PROCESS_DETAIL_WINDOWS_ENV_STORAGE_HPP_
7 #define BOOST_PROCESS_DETAIL_WINDOWS_ENV_STORAGE_HPP_
8
9 #include <string>
10 #include <vector>
11 #include <unordered_map>
12 #include <boost/winapi/error_codes.hpp>
13 #include <boost/winapi/environment.hpp>
14 #include <boost/winapi/get_current_process.hpp>
15 #include <boost/winapi/get_current_process_id.hpp>
16 #include <boost/process/detail/config.hpp>
17 #include <algorithm>
18 #include <boost/process/locale.hpp>
19
20 namespace boost { namespace process { namespace detail { namespace windows {
21
22 template<typename Char>
23 class native_environment_impl
24 {
_deleter(Char * p)25 static void _deleter(Char* p) {boost::winapi::free_environment_strings(p);};
26 std::unique_ptr<Char[], void(*)(Char*)> _buf{boost::winapi::get_environment_strings<Char>(), &native_environment_impl::_deleter};
27 static inline std::vector<Char*> _load_var(Char* p);
28 std::vector<Char*> _env_arr{_load_var(_buf.get())};
29 public:
30 using char_type = Char;
31 using pointer_type = const char_type*;
32 using string_type = std::basic_string<char_type>;
33 using native_handle_type = pointer_type;
reload()34 void reload()
35 {
36 _buf.reset(boost::winapi::get_environment_strings<Char>());
37 _env_arr = _load_var(_buf.get());
38 _env_impl = &*_env_arr.begin();
39 }
40
41 string_type get(const pointer_type id);
42 void set(const pointer_type id, const pointer_type value);
43 void reset(const pointer_type id);
44
get(const string_type & id)45 string_type get(const string_type & id) {return get(id.c_str());}
set(const string_type & id,const string_type & value)46 void set(const string_type & id, const string_type & value) {set(id.c_str(), value.c_str()); }
reset(const string_type & id)47 void reset(const string_type & id) {reset(id.c_str());}
48
49 native_environment_impl() = default;
50 native_environment_impl(const native_environment_impl& ) = delete;
51 native_environment_impl(native_environment_impl && ) = default;
52 native_environment_impl & operator=(const native_environment_impl& ) = delete;
53 native_environment_impl & operator=(native_environment_impl && ) = default;
54 Char ** _env_impl = &*_env_arr.begin();
55
native_handle() const56 native_handle_type native_handle() const {return _buf.get();}
57 };
58
59 template<typename Char>
get(const pointer_type id)60 inline auto native_environment_impl<Char>::get(const pointer_type id) -> string_type
61 {
62 Char buf[4096];
63 auto size = boost::winapi::get_environment_variable(id, buf, sizeof(buf));
64 if (size == 0) //failed
65 {
66 auto err = ::boost::winapi::GetLastError();
67 if (err == ::boost::winapi::ERROR_ENVVAR_NOT_FOUND_)//well, then we consider that an empty value
68 return "";
69 else
70 throw process_error(std::error_code(err, std::system_category()),
71 "GetEnvironmentVariable() failed");
72 }
73
74 if (size == sizeof(buf)) //the return size gives the size without the null, so I know this went wrong
75 {
76 /*limit defined here https://msdn.microsoft.com/en-us/library/windows/desktop/ms683188(v=vs.85).aspx
77 * but I used 32768 so it is a multiple of 4096.
78 */
79 constexpr static std::size_t max_size = 32768;
80 //Handle variables longer then buf.
81 std::size_t buf_size = sizeof(buf);
82 while (buf_size <= max_size)
83 {
84 std::vector<Char> buf(buf_size);
85 auto size = boost::winapi::get_environment_variable(id, buf.data(), buf.size());
86
87 if (size == buf_size) //buffer to small
88 buf_size *= 2;
89 else if (size == 0)
90 ::boost::process::detail::throw_last_error("GetEnvironmentVariable() failed");
91 else
92 return std::basic_string<Char>(
93 buf.data(), buf.data()+ size + 1);
94
95 }
96
97 }
98 return std::basic_string<Char>(buf, buf+size+1);
99 }
100
101 template<typename Char>
set(const pointer_type id,const pointer_type value)102 inline void native_environment_impl<Char>::set(const pointer_type id, const pointer_type value)
103 {
104 boost::winapi::set_environment_variable(id, value);
105 }
106
107 template<typename Char>
reset(const pointer_type id)108 inline void native_environment_impl<Char>::reset(const pointer_type id)
109 {
110 boost::winapi::set_environment_variable(id, nullptr);
111 }
112
113 template<typename Char>
_load_var(Char * p)114 std::vector<Char*> native_environment_impl<Char>::_load_var(Char* p)
115 {
116 std::vector<Char*> ret;
117 if (*p != null_char<Char>())
118 {
119 ret.push_back(p);
120 while ((*p != null_char<Char>()) || (*(p+1) != null_char<Char>()))
121 {
122 if (*p==null_char<Char>())
123 {
124 p++;
125 ret.push_back(p);
126 }
127 else
128 p++;
129 }
130 }
131 p++;
132 ret.push_back(nullptr);
133
134 return ret;
135 }
136
137
138 template<typename Char>
139 struct basic_environment_impl
140 {
141 std::vector<Char> _data = {null_char<Char>()};
142 static std::vector<Char*> _load_var(Char* p);
143 std::vector<Char*> _env_arr{_load_var(_data.data())};
144 public:
145 using char_type = Char;
146 using pointer_type = const char_type*;
147 using string_type = std::basic_string<char_type>;
148 using native_handle_type = pointer_type;
149
sizeboost::process::detail::windows::basic_environment_impl150 std::size_t size() const { return _data.size();}
151
reloadboost::process::detail::windows::basic_environment_impl152 void reload()
153 {
154 _env_arr = _load_var(_data.data());
155 _env_impl = _env_arr.data();
156 }
157
getboost::process::detail::windows::basic_environment_impl158 string_type get(const pointer_type id) {return get(string_type(id));}
setboost::process::detail::windows::basic_environment_impl159 void set(const pointer_type id, const pointer_type value) {set(string_type(id), value);}
resetboost::process::detail::windows::basic_environment_impl160 void reset(const pointer_type id) {reset(string_type(id));}
161
162 string_type get(const string_type & id);
163 void set(const string_type & id, const string_type & value);
164 void reset(const string_type & id);
165
166 inline basic_environment_impl(const native_environment_impl<Char> & nei);
167 basic_environment_impl() = default;
basic_environment_implboost::process::detail::windows::basic_environment_impl168 basic_environment_impl(const basic_environment_impl& rhs)
169 : _data(rhs._data)
170 {
171 }
basic_environment_implboost::process::detail::windows::basic_environment_impl172 basic_environment_impl(basic_environment_impl && rhs)
173 : _data(std::move(rhs._data)),
174 _env_arr(std::move(rhs._env_arr)),
175 _env_impl(_env_arr.data())
176 {
177 }
operator =boost::process::detail::windows::basic_environment_impl178 basic_environment_impl &operator=(basic_environment_impl && rhs)
179 {
180 _data = std::move(rhs._data);
181 //reload();
182 _env_arr = std::move(rhs._env_arr);
183 _env_impl = _env_arr.data();
184
185 return *this;
186 }
operator =boost::process::detail::windows::basic_environment_impl187 basic_environment_impl & operator=(const basic_environment_impl& rhs)
188 {
189 _data = rhs._data;
190 reload();
191 return *this;
192 }
193
194 template<typename CharR>
basic_environment_implboost::process::detail::windows::basic_environment_impl195 explicit inline basic_environment_impl(
196 const basic_environment_impl<CharR>& rhs,
197 const ::boost::process::codecvt_type & cv = ::boost::process::codecvt())
198 : _data(::boost::process::detail::convert(rhs._data, cv))
199 {
200 }
201
202 template<typename CharR>
operator =boost::process::detail::windows::basic_environment_impl203 basic_environment_impl & operator=(const basic_environment_impl<CharR>& rhs)
204 {
205 _data = ::boost::process::detail::convert(rhs._data);
206 _env_arr = _load_var(&*_data.begin());
207 _env_impl = &*_env_arr.begin();
208 return *this;
209 }
210
211 Char ** _env_impl = &*_env_arr.begin();
212
native_handleboost::process::detail::windows::basic_environment_impl213 native_handle_type native_handle() const {return &*_data.begin();}
214 };
215
216
217 template<typename Char>
basic_environment_impl(const native_environment_impl<Char> & nei)218 basic_environment_impl<Char>::basic_environment_impl(const native_environment_impl<Char> & nei)
219 {
220 auto beg = nei.native_handle();
221 auto p = beg;
222 while ((*p != null_char<Char>()) || (*(p+1) != null_char<Char>()))
223 p++;
224 p++; //pointing to the second nullchar
225 p++; //to get the pointer behing the second nullchar, so it's end.
226
227 this->_data.assign(beg, p);
228 this->reload();
229 }
230
231
232 template<typename Char>
get(const string_type & id)233 inline auto basic_environment_impl<Char>::get(const string_type &id) -> string_type
234 {
235
236 if (std::equal(id.begin(), id.end(), _data.begin()) && (_data[id.size()] == equal_sign<Char>()))
237 return string_type(_data.data()); //null-char is handled by the string.
238
239 std::vector<Char> seq = {'\0'}; //using a vector, because strings might cause problems with nullchars
240 seq.insert(seq.end(), id.begin(), id.end());
241 seq.push_back('=');
242
243 auto itr = std::search(_data.begin(), _data.end(), seq.begin(), seq.end());
244
245 if (itr == _data.end()) //not found
246 return "";
247
248 itr += seq.size(); //advance to the value behind the '='; the std::string will take care of finding the null-char.
249
250 return string_type(&*itr);
251 }
252
253 template<typename Char>
set(const string_type & id,const string_type & value)254 inline void basic_environment_impl<Char>::set(const string_type &id, const string_type &value)
255 {
256 reset(id);
257
258 std::vector<Char> insertion;
259
260 insertion.insert(insertion.end(), id.begin(), id.end());
261 insertion.push_back('=');
262 insertion.insert(insertion.end(), value.begin(), value.end());
263 insertion.push_back('\0');
264
265 _data.insert(_data.end() -1, insertion.begin(), insertion.end());
266
267 reload();
268 }
269
270 template<typename Char>
reset(const string_type & id)271 inline void basic_environment_impl<Char>::reset(const string_type &id)
272 {
273 //ok, we need to check the size of data first
274 if (id.size() >= _data.size()) //ok, so it's impossible id is in there.
275 return;
276
277 //check if it's the first one, spares us the search.
278 if (std::equal(id.begin(), id.end(), _data.begin()) && (_data[id.size()] == equal_sign<Char>()))
279 {
280 auto beg = _data.begin();
281 auto end = beg;
282
283 while (*end != '\0')
284 end++;
285
286 end++; //to point behind the last null-char
287
288 _data.erase(beg, end); //and remove the thingy
289
290 }
291
292 std::vector<Char> seq = {'\0'}; //using a vector, because strings might cause problems with nullchars
293 seq.insert(seq.end(), id.begin(), id.end());
294 seq.push_back('=');
295
296 auto itr = std::search(_data.begin(), _data.end(), seq.begin(), seq.end());
297
298 if (itr == _data.end())
299 return;//nothing to return if it's empty anyway...
300
301 auto end = itr;
302
303 while (*++end != '\0');
304
305
306 _data.erase(itr, end);//and remove it
307 reload();
308
309
310 }
311
312 template<typename Char>
_load_var(Char * p)313 std::vector<Char*> basic_environment_impl<Char>::_load_var(Char* p)
314 {
315 std::vector<Char*> ret;
316 if (*p != null_char<Char>())
317 {
318 ret.push_back(p);
319 while ((*p != null_char<Char>()) || (*(p+1) != null_char<Char>()))
320 {
321 if (*p==null_char<Char>())
322 {
323 p++;
324 ret.push_back(p);
325 }
326 else
327 p++;
328 }
329 }
330 p++;
331 ret.push_back(nullptr);
332 return ret;
333 }
334
335
336 template<typename T> constexpr T env_seperator();
env_seperator()337 template<> constexpr char env_seperator() {return ';'; }
env_seperator()338 template<> constexpr wchar_t env_seperator() {return L';'; }
339
get_id()340 inline int get_id() {return boost::winapi::GetCurrentProcessId();}
native_handle()341 inline void* native_handle() {return boost::winapi::GetCurrentProcess(); }
342
343 typedef void* native_handle_t;
344
345 }
346
347 }
348 }
349 }
350
351
352
353
354 #endif /* BOOST_PROCESS_DETAIL_WINDOWS_ENV_STORAGE_HPP_ */
355