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