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_ENV_HPP_
7 #define BOOST_PROCESS_DETAIL_ENV_HPP_
8 
9 #include <boost/process/environment.hpp>
10 #include <boost/none.hpp>
11 
12 #if defined(BOOST_POSIX_API)
13 #include <boost/process/detail/posix/env_init.hpp>
14 #elif defined(BOOST_WINDOWS_API)
15 #include <boost/process/detail/windows/env_init.hpp>
16 #endif
17 
18 /** \file boost/process/env.hpp
19  *
20  *    This header which provides the `env` property. It allows the modification of the
21  *    environment the child process will run in, in a functional style.
22  *
23  *  \xmlonly
24 <programlisting>
25 namespace boost {
26   namespace process {
27     <emphasis>unspecified</emphasis> <globalname alt="boost::process::env">env</globalname>;
28   }
29 }
30 </programlisting>
31  *  \endxmlonly
32  *
33  *  For additional information see the platform documentations:
34  *
35  *   - [windows](https://msdn.microsoft.com/en-US/library/windows/desktop/ms682653.aspx)
36  *   - [posix](http://pubs.opengroup.org/onlinepubs/009695399/basedefs/xbd_chap08.html)
37  *
38  */
39 
40 
41 namespace boost {
42 
43 namespace process { namespace detail {
44 
45 
46 template<typename Char>
make_env_string_size(const std::basic_string<Char> & ch)47 std::size_t make_env_string_size(const std::basic_string<Char> & ch)
48 {
49     return ch.size() + 1;
50 }
51 
52 template<typename Char>
make_env_string_size(const Char * ch)53 std::size_t make_env_string_size(const Char * ch)
54 {
55     std::size_t sz = 0;
56     while (ch[sz] != null_char<Char>())
57         sz++;
58 
59     sz++;
60     return sz;
61 }
62 
63 template<typename Char, typename Container>
make_env_string(const Container & value)64 inline std::basic_string<Char> make_env_string(const Container & value)
65 {
66     std::size_t sz = 0;
67     for (auto & v : value)
68         sz += make_env_string_size(v);
69 
70     std::basic_string<Char> s;
71     s.reserve(sz); //+1 for ;, end doesn't have one.
72 
73     for (auto & val : value)
74         (s += val) += api::env_seperator<Char>();
75 
76     s.resize(s.size() -1); //remove last ';'
77     return s;
78 }
79 
80 
81 template<typename Char>
82 struct env_set
83 {
84     using string_type = std::basic_string<Char>;
85     string_type key;
86     string_type value;
87 };
88 
89 template<typename Char>
90 struct env_append
91 {
92     using string_type = std::basic_string<Char>;
93     string_type key;
94     string_type value;
95 };
96 
97 
98 
99 template<typename Char>
100 struct env_reset
101 {
102     using string_type = std::basic_string<Char>;
103     string_type key;
104 };
105 
106 
107 template<> struct is_wchar_t<env_set<wchar_t>>           : std::true_type {};
108 template<> struct is_wchar_t<env_append<wchar_t>>        : std::true_type {};
109 template<> struct is_wchar_t<env_reset<wchar_t>>         : std::true_type {};
110 template<> struct is_wchar_t<basic_environment<wchar_t>> : std::true_type {};
111 
112 
113 template<>
114 struct char_converter<char, env_set<wchar_t>>
115 {
convboost::process::detail::char_converter116     static env_set<char> conv(const env_set<wchar_t> & in)
117     {
118         return {::boost::process::detail::convert(in.key),
119                 ::boost::process::detail::convert(in.value)};
120     }
121 };
122 
123 template<>
124 struct char_converter<wchar_t, env_set<char>>
125 {
convboost::process::detail::char_converter126     static env_set<wchar_t> conv(const env_set<char> & in)
127     {
128         return {::boost::process::detail::convert(in.key),
129                 ::boost::process::detail::convert(in.value)};
130     }
131 };
132 
133 template<>
134 struct char_converter<char, env_append<wchar_t>>
135 {
convboost::process::detail::char_converter136     static env_append<char> conv(const env_append<wchar_t> & in)
137     {
138         return {::boost::process::detail::convert(in.key),
139                 ::boost::process::detail::convert(in.value)};
140     }
141 };
142 
143 template<>
144 struct char_converter<wchar_t, env_append<char>>
145 {
convboost::process::detail::char_converter146     static env_append<wchar_t> conv(const env_append<char> & in)
147     {
148         return {::boost::process::detail::convert(in.key),
149                 ::boost::process::detail::convert(in.value)};
150     }
151 };
152 
153 template<>
154 struct char_converter<char, env_reset<wchar_t>>
155 {
convboost::process::detail::char_converter156     static env_reset<char> conv(const env_reset<wchar_t> & in)
157     {
158         return {::boost::process::detail::convert(in.key)};
159     }
160 };
161 
162 template<>
163 struct char_converter<wchar_t, env_reset<char>>
164 {
convboost::process::detail::char_converter165     static env_reset<wchar_t> conv(const env_reset<char> & in)
166     {
167         return {::boost::process::detail::convert(in.key)};
168     }
169 };
170 
171 
172 template<typename Char>
173 struct env_init
174 {
175     basic_environment<Char> env;
176 };
177 
178 template<>
179 struct char_converter<char, env_init<wchar_t>>
180 {
convboost::process::detail::char_converter181     static env_init<char> conv(const env_init<wchar_t> & in)
182     {
183         return {basic_environment<char>(in.env)};
184     }
185 };
186 
187 template<>
188 struct char_converter<wchar_t, env_init<char>>
189 {
convboost::process::detail::char_converter190     static env_init<wchar_t> conv(const env_init<char> & in)
191     {
192         return {basic_environment<wchar_t>(in.env)};
193     }
194 };
195 
196 template<>
197 struct char_converter<char, basic_environment<wchar_t>>
198 {
convboost::process::detail::char_converter199     static basic_environment<char> conv(const basic_environment<wchar_t> & in)
200     {
201         return { basic_environment<char>(in) };
202     }
203 };
204 
205 template<>
206 struct char_converter<wchar_t, basic_environment<char>>
207 {
convboost::process::detail::char_converter208     static basic_environment<wchar_t> conv(const basic_environment<char> & in)
209     {
210         return { basic_environment<wchar_t>(in) };
211     }
212 };
213 
214 template<typename Char>
215 struct env_proxy
216 {
217     using string_type = std::basic_string<Char>;
218     string_type key;
219 
220 
operator =boost::process::detail::env_proxy221     env_set<Char> operator=(const string_type & value)
222     {
223         return {std::move(key), value};
224     }
operator =boost::process::detail::env_proxy225     env_set<Char> operator=(const std::vector<string_type> & value)
226     {
227         return {std::move(key), make_env_string<Char>(value)};
228     }
operator =boost::process::detail::env_proxy229     env_set<Char> operator=(const std::initializer_list<const Char*> & value)
230     {
231         return {std::move(key), make_env_string<Char>(value)};
232     }
233 
operator +=boost::process::detail::env_proxy234     env_append<Char> operator+=(const string_type & value)
235     {
236         return {std::move(key), value};
237     }
operator +=boost::process::detail::env_proxy238     env_append<Char> operator+=(const std::vector<string_type> & value)
239     {
240         return {std::move(key), make_env_string<Char>(value)};
241     }
operator +=boost::process::detail::env_proxy242     env_append<Char> operator+=(const std::initializer_list<const Char*> & value)
243     {
244         return {std::move(key), make_env_string<Char>(value)};
245     }
operator =boost::process::detail::env_proxy246     env_reset<Char> operator=(boost::none_t)
247     {
248         return {std::move(key)};
249     }
250 };
251 
252 struct env_
253 {
env_boost::process::detail::env_254     constexpr env_() {};
255 
256     template<typename Char>
operator ()boost::process::detail::env_257     env_set<Char> operator()(const std::basic_string<Char> & key,
258                              const std::basic_string<Char> & value) const
259     {
260         return {key, value};
261     }
262     template<typename Char>
operator ()boost::process::detail::env_263     env_set<Char> operator()(const std::basic_string<Char> & key,
264                                 const std::vector<std::basic_string<Char>> & value) const
265     {
266         return {key, make_env_string<Char>(value)};
267     }
268     template<typename Char>
operator ()boost::process::detail::env_269     env_set<Char> operator()(const std::basic_string<Char> & key,
270                              const std::initializer_list<Char*> & value) const
271     {
272         return {key, make_env_string<Char>(value)};
273     }
274     template<typename Char>
operator ()boost::process::detail::env_275     env_reset<Char> operator()(const std::basic_string<Char> & key, boost::none_t)
276     {
277         return {key};
278     }
279     template<typename Char>
operator []boost::process::detail::env_280     env_proxy<Char> operator[](const std::basic_string<Char> & key) const
281     {
282         return {key};
283     }
284     template<typename Char>
operator []boost::process::detail::env_285     env_proxy<Char> operator[](const Char* key) const
286     {
287         return {key};
288     }
289     template<typename Char>
operator ()boost::process::detail::env_290     env_init<Char> operator()(const basic_environment<Char> & env) const
291     {
292         return {env};
293     }
294     template<typename Char>
operator =boost::process::detail::env_295     env_init<Char> operator= (const basic_environment<Char> & env) const
296     {
297         return {env};
298     }
299 };
300 
301 template<typename Char>
302 struct env_builder
303 {
304     basic_environment<Char> env;
env_builderboost::process::detail::env_builder305     env_builder() : env{basic_native_environment<Char>()} {}
306 
operator ()boost::process::detail::env_builder307     void operator()(const basic_environment<Char> & e)
308     {
309         env = e;
310     }
311 
operator ()boost::process::detail::env_builder312     void operator()(env_init<Char> & ei)
313     {
314         env = std::move(ei.env);
315     }
operator ()boost::process::detail::env_builder316     void operator()(env_set<Char> & es)
317     {
318         env[es.key] = es.value;
319     }
operator ()boost::process::detail::env_builder320     void operator()(env_reset<Char> & es)
321     {
322         env.erase(es.key);
323     }
324     template<typename T>
operator ()boost::process::detail::env_builder325     void operator()(env_append<T> & es)
326     {
327         env[es.key] += es.value;
328     }
329 
330     typedef api::env_init<Char> result_type;
get_initializerboost::process::detail::env_builder331     api::env_init<Char> get_initializer()
332     {
333         return api::env_init<Char>(std::move(env));
334     }
335 };
336 
337 template<>
338 struct initializer_builder<env_tag<char>>
339 {
340     typedef env_builder<char> type;
341 };
342 
343 template<>
344 struct initializer_builder<env_tag<wchar_t>>
345 {
346     typedef env_builder<wchar_t> type;
347 };
348 
349 }
350 
351 /**
352 
353 The `env` property provides a functional way to modify the environment used by
354 the child process. If none is passed the environment is inherited from the father
355 process. Appending means that the environment will be interpreted as a ';' or ':'
356 separated list as used in `PATH`.
357 
358 On both `posix` and `windows` the environment variables can be lists of strings,
359 separated by ';'. This is typically used for the `PATH` variable.
360 
361 By default the environment will be inherited from the launching process,
362 which is also true if environment are modified with this initializer.
363 
364 \section env_details Details
365 
366 \subsection env_operations Operations
367 
368 \subsubsection env_set_var Setting variables
369 
370 To set a variable `id` the value `value` the following syntax can be used.
371 
372 \code{.cpp}
373 env[id] = value;
374 env(id, value);
375 \endcode
376 
377 `std::initializer_list` is among the allowed types, so the following syntax is also possible.
378 
379 \code{.cpp}
380 env[id] = {value1, value2};
381 env(id, {value1, value2});
382 \endcode
383 
384 \note Creates the variable if it does not exist.
385 
386 The following lists contain possible value types, with `char_type` being either `char` or `wchar_t`
387 for both `id` and `value`.
388 
389 \paragraph id id
390 
391  - `std::basic_string<char_type>`
392  - `const char_type *`
393 
394 \paragraph env_set_var_value value
395 
396  - `std::basic_string<char_type>`
397  - `const char_type * `
398  - `std::initializer_list<const char_type *>`
399  - `std::vector<std::basic_string<char_type>>`
400 
401 
402 \note Using `std::vector` or `std::initializer_list`
403 
404 \subsubsection env_append_var Append variables
405 
406 Appending means, that a variable will be interpreted as a
407 To append  a variable `id` the value `value` the following syntax can be used:
408 
409 \code{.cpp}
410 env[id] += value;
411 \endcode
412 
413 `std::initializer_list` is among the allowed types, so the following syntax is also possible.
414 
415 \code{.cpp}
416 env[id] += {value1, value2};
417 \endcode
418 
419 \note Creates the variable if it does not exist.
420 
421 The following lists contain possible value types, with `char_type` being either `char` or `wchar_t`
422 for both `id` and `value`.
423 
424 \paragraph env_append_var_id id
425 
426  - `std::basic_string<char_type>`
427  - `const char_type *`
428 
429 \paragraph env_append_var_value value
430 
431  - `std::basic_string<char_type>`
432  - `const char_type *`
433  - `std::initializer_list<const char_type *>`
434  - `std::vector<std::basic_string<char_type>>`
435 
436 
437 \subsubsection env_reset Reset variables
438 
439 Reseting signle variables can be done in the following way:
440 
441 \code{.cpp}
442 env[id] = boost::none;
443 env(id, boost::none);
444 \endcode
445 
446 \note This does not set the value empty, but removes it from the list.
447 
448 The following lists contain possible value types, with `char_type` being either `char` or `wchar_t`:
449 
450 \paragraph env_reset_var_id id
451 
452  - `std::basic_string<char_type>`
453  - `const char_type *`
454 
455 \subsubsection env_init Initialize the environment
456 
457 The whole environment can be initialized from an object of type
458 \xmlonly <classname>boost::process::environment</classname> \endxmlonly
459 
460 \code{.cpp}
461 env=env;
462 env(env);
463 \endcode
464 
465 \note The passed `environment` can also be default-constructed to get an empty environment.
466 
467 \paragraph env_init_var_id id
468 
469  - `std::basic_string<char_type>`
470  - `const char_type *`
471 
472 \paragraph env_init_var_value value
473 
474  - `boost::process::basic_environment<char_type>`
475 
476 \subsection env_example Example
477 
478 \code{.cpp}
479 spawn("b2", env["PATH"]+="F:/boost", env["SOME_VAR"]=boost::none, env["NEW_VAR"]="VALUE");
480 \endcode
481 
482 If the overload style should be done by passing an instance of
483 \xmlonly <classname>boost::process::environment</classname> \endxmlonly
484 the above example would look like this.
485 
486 \code{.cpp}
487 environment e = this_process::environment();
488 e["PATH"]   += "F:/boost";
489 e.erase("SOME_VAR");
490 e["NEW_VAR"] = "VALUE";
491 spawn("b2", e);
492 \endcode
493 
494 \warning Passing an empty environment will cause undefined behaviour.
495 
496  */
497 constexpr boost::process::detail::env_ env{};
498 
499 
500 }}
501 
502 #endif /* INCLUDE_BOOST_PROCESS_DETAIL_ENV_HPP_ */
503