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_POSIX_ENVIRONMENT_HPP_
7 #define BOOST_PROCESS_DETAIL_POSIX_ENVIRONMENT_HPP_
8 
9 #include <string>
10 #include <vector>
11 #include <unordered_map>
12 #include <boost/process/detail/config.hpp>
13 #include <algorithm>
14 #include <cstdlib>
15 #include <boost/process/locale.hpp>
16 
17 
18 namespace boost { namespace process { namespace detail { namespace posix {
19 
20 template<typename Char>
21 class native_environment_impl
22 {
_load()23     static std::vector<std::basic_string<Char>>  _load()
24     {
25         std::vector<std::basic_string<Char>> val;
26         auto p = environ;
27         while (*p != nullptr)
28         {
29             std::string str = *p;
30             val.push_back(::boost::process::detail::convert(str));
31             p++;
32         }
33         return val;
34     }
_load_var(std::vector<std::basic_string<Char>> & vec)35     static std::vector<Char*> _load_var(std::vector<std::basic_string<Char>> & vec)
36     {
37         std::vector<Char*> val;
38         val.resize(vec.size() + 1);
39         std::transform(vec.begin(), vec.end(), val.begin(),
40                 [](std::basic_string<Char> & str)
41                 {
42                     return &str.front();
43                 });
44         val.back() = nullptr;
45         return val;
46     }
47     std::vector<std::basic_string<Char>> _buffer = _load();
48     std::vector<Char*> _impl = _load_var(_buffer);
49 public:
50     using char_type = Char;
51     using pointer_type = const char_type*;
52     using string_type = std::basic_string<char_type>;
53     using native_handle_type = char_type **;
54 
reload()55     void reload()
56     {
57         _buffer = _load();
58         _impl = _load_var(_buffer);
59     }
60 
get(const pointer_type id)61     string_type get(const pointer_type id) { return get(string_type(id)); }
set(const pointer_type id,const pointer_type value)62     void        set(const pointer_type id, const pointer_type value)
63     {
64         set(string_type(id), string_type(value));
65     }
reset(const pointer_type id)66     void      reset(const pointer_type id) { reset(string_type(id)); }
67 
get(const string_type & id)68     string_type get(const string_type & id)
69     {
70         std::string id_c = ::boost::process::detail::convert(id);
71         std::string g = ::getenv(id_c.c_str());
72         return ::boost::process::detail::convert(g.c_str());
73     }
set(const string_type & id,const string_type & value)74     void        set(const string_type & id, const string_type & value)
75     {
76         std::string id_c    = ::boost::process::detail::convert(id.c_str());
77         std::string value_c = ::boost::process::detail::convert(value.c_str());
78         auto res = ::setenv(id_c.c_str(), value_c.c_str(), true);
79         if (res != 0)
80             boost::process::detail::throw_last_error();
81     }
reset(const string_type & id)82     void      reset(const string_type & id)
83     {
84         std::string id_c = ::boost::process::detail::convert(id.c_str());
85         auto res = ::unsetenv(id_c.c_str());
86         if (res != 0)
87             ::boost::process::detail::throw_last_error();
88     }
89 
90     native_environment_impl() = default;
91     native_environment_impl(const native_environment_impl& ) = delete;
92     native_environment_impl(native_environment_impl && ) = default;
93     native_environment_impl & operator=(const native_environment_impl& ) = delete;
94     native_environment_impl & operator=(native_environment_impl && ) = default;
95     native_handle_type _env_impl = _impl.data();
96 
native_handle() const97     native_handle_type native_handle() const {return environ;}
98 };
99 
100 template<>
101 class native_environment_impl<char>
102 {
103 public:
104     using char_type = char;
105     using pointer_type = const char_type*;
106     using string_type = std::basic_string<char_type>;
107     using native_handle_type = char_type **;
108 
reload()109     void reload() {this->_env_impl = ::environ;}
110 
get(const pointer_type id)111     string_type get(const pointer_type id) { return getenv(id); }
set(const pointer_type id,const pointer_type value)112     void        set(const pointer_type id, const pointer_type value)
113     {
114         auto res = ::setenv(id, value, 1);
115         if (res != 0)
116             boost::process::detail::throw_last_error();
117         reload();
118     }
reset(const pointer_type id)119     void      reset(const pointer_type id)
120     {
121         auto res = ::unsetenv(id);
122         if (res != 0)
123             boost::process::detail::throw_last_error();
124         reload();
125     }
126 
get(const string_type & id)127     string_type get(const string_type & id) {return get(id.c_str());}
set(const string_type & id,const string_type & value)128     void        set(const string_type & id, const string_type & value) {set(id.c_str(), value.c_str()); }
reset(const string_type & id)129     void      reset(const string_type & id) {reset(id.c_str());}
130 
131     native_environment_impl() = default;
132     native_environment_impl(const native_environment_impl& ) = delete;
133     native_environment_impl(native_environment_impl && ) = default;
134     native_environment_impl & operator=(const native_environment_impl& ) = delete;
135     native_environment_impl & operator=(native_environment_impl && ) = default;
136     native_handle_type _env_impl = environ;
137 
native_handle() const138     native_handle_type native_handle() const {return ::environ;}
139 };
140 
141 
142 
143 template<typename Char>
144 struct basic_environment_impl
145 {
146     std::vector<std::basic_string<Char>> _data {};
147     static std::vector<Char*> _load_var(std::vector<std::basic_string<Char>> & data);
148     std::vector<Char*> _env_arr{_load_var(_data)};
149 public:
150     using char_type = Char;
151     using pointer_type = const char_type*;
152     using string_type = std::basic_string<char_type>;
153     using native_handle_type = Char**;
reloadboost::process::detail::posix::basic_environment_impl154     void reload()
155     {
156         _env_arr = _load_var(_data);
157         _env_impl = _env_arr.data();
158     }
159 
getboost::process::detail::posix::basic_environment_impl160     string_type get(const pointer_type id) {return get(string_type(id));}
setboost::process::detail::posix::basic_environment_impl161     void        set(const pointer_type id, const pointer_type value) {set(string_type(id), value);}
resetboost::process::detail::posix::basic_environment_impl162     void      reset(const pointer_type id)  {reset(string_type(id));}
163 
164     string_type get(const string_type & id);
165     void        set(const string_type & id, const string_type & value);
166     void      reset(const string_type & id);
167 
168     basic_environment_impl(const native_environment_impl<Char> & nei);
169     basic_environment_impl() = default;
basic_environment_implboost::process::detail::posix::basic_environment_impl170     basic_environment_impl(const basic_environment_impl& rhs)
171         : _data(rhs._data)
172     {
173 
174     }
175     basic_environment_impl(basic_environment_impl && ) = default;
operator =boost::process::detail::posix::basic_environment_impl176     basic_environment_impl & operator=(const basic_environment_impl& rhs)
177     {
178         _data = rhs._data;
179         _env_arr = _load_var(_data);
180         _env_impl = &*_env_arr.begin();
181         return *this;
182     }
183     basic_environment_impl & operator=(basic_environment_impl && ) = default;
184 
185     template<typename CharR>
basic_environment_implboost::process::detail::posix::basic_environment_impl186     explicit inline  basic_environment_impl(
187                 const basic_environment_impl<CharR>& rhs,
188                 const ::boost::process::codecvt_type & cv = ::boost::process::codecvt())
189         : _data(rhs._data.size())
190     {
191         std::transform(rhs._data.begin(), rhs._data.end(), _data.begin(),
192                 [&](const std::basic_string<CharR> & st)
193                 {
194                     return ::boost::process::detail::convert(st, cv);
195                 }
196 
197             );
198         reload();
199     }
200 
201     template<typename CharR>
operator =boost::process::detail::posix::basic_environment_impl202     basic_environment_impl & operator=(const basic_environment_impl<CharR>& rhs)
203     {
204         _data = ::boost::process::detail::convert(rhs._data);
205         _env_arr = _load_var(&*_data.begin());
206         _env_impl = &*_env_arr.begin();
207         return *this;
208     }
209 
210     Char ** _env_impl = &*_env_arr.data();
211 
native_handleboost::process::detail::posix::basic_environment_impl212     native_handle_type native_handle() const {return &_data.front();}
213 };
214 
215 
216 template<typename Char>
basic_environment_impl(const native_environment_impl<Char> & nei)217 basic_environment_impl<Char>::basic_environment_impl(const native_environment_impl<Char> & nei)
218 {
219     auto beg = nei.native_handle();
220 
221     auto end = beg;
222     while (*end != nullptr)
223         end++;
224     this->_data.assign(beg, end);
225     reload();
226 }
227 
228 
229 template<typename Char>
get(const string_type & id)230 inline auto basic_environment_impl<Char>::get(const string_type &id) -> string_type
231 {
232     auto itr = std::find_if(_data.begin(), _data.end(),
233             [&](const string_type & st)
234             {
235                 if (st.size() <= id.size())
236                     return false;
237                 return std::equal(id.begin(), id.end(), st.begin()) && (st[id.size()] == equal_sign<Char>());
238             }
239         );
240 
241     if (itr == _data.end())
242     {
243         return "";
244     }
245     else return
246         itr->data() + id.size(); //id=Thingy -> +2 points to T
247 }
248 
249 template<typename Char>
set(const string_type & id,const string_type & value)250 inline void basic_environment_impl<Char>::set(const string_type &id, const string_type &value)
251 {
252     auto itr = std::find_if(_data.begin(), _data.end(),
253         [&](const string_type & st)
254         {
255             if (st.size() <= id.size())
256                 return false;
257             return std::equal(id.begin(), id.end(), st.begin()) && (st[id.size()] == equal_sign<Char>());
258         }
259     );
260 
261     if (itr != _data.end())
262         *itr = id + equal_sign<Char>() + value;
263     else
264         _data.push_back(id + equal_sign<Char>() + value);
265 
266     reload();
267 }
268 
269 template<typename Char>
reset(const string_type & id)270 inline void  basic_environment_impl<Char>::reset(const string_type &id)
271 {
272     auto itr = std::find_if(_data.begin(), _data.end(),
273         [&](const string_type & st)
274         {
275             if (st.size() <= id.size())
276                 return false;
277             return std::equal(id.begin(), id.end(), st.begin()) && (st[id.size()] == equal_sign<Char>());
278         }
279     );
280     if (itr != _data.end())
281     {
282         _data.erase(itr);//and remove it
283     }
284 
285     reload();
286 
287 
288 }
289 
290 template<typename Char>
_load_var(std::vector<std::basic_string<Char>> & data)291 std::vector<Char*> basic_environment_impl<Char>::_load_var(std::vector<std::basic_string<Char>> & data)
292 {
293     std::vector<Char*> ret;
294     ret.reserve(data.size() +1);
295 
296     for (auto & val : data)
297         ret.push_back(&val.front());
298 
299     ret.push_back(nullptr);
300     return ret;
301 }
302 
303 template<typename T> constexpr T env_seperator();
env_seperator()304 template<> constexpr   char   env_seperator() {return  ':'; }
env_seperator()305 template<> constexpr  wchar_t env_seperator() {return L':'; }
306 
307 
308 typedef int native_handle_t;
309 
get_id()310 inline int  get_id()        {return getpid(); }
native_handle()311 inline int native_handle()  {return getpid(); }
312 
313 }
314 
315 }
316 }
317 }
318 
319 
320 
321 
322 #endif /* BOOST_PROCESS_DETAIL_WINDOWS_ENV_STORAGE_HPP_ */
323