diff options
author | Boris Kolpackov <boris@codesynthesis.com> | 2018-06-14 18:13:04 +0200 |
---|---|---|
committer | Karen Arutyunov <karen@codesynthesis.com> | 2018-06-15 14:21:58 +0300 |
commit | 481f9ba1aee62fea092184f2243d210a8686781f (patch) | |
tree | a54fc536d835c5277038e145fabc1255c42ff8ac | |
parent | 5d424ea127333859a32addaf9e28eae07a4dc9f6 (diff) |
Add portable environment variable manipulation functions
-rw-r--r-- | libbutl/path.cxx | 33 | ||||
-rw-r--r-- | libbutl/process.cxx | 41 | ||||
-rw-r--r-- | libbutl/utility.cxx | 32 | ||||
-rw-r--r-- | libbutl/utility.ixx | 13 | ||||
-rw-r--r-- | libbutl/utility.mxx | 22 | ||||
-rw-r--r-- | tests/process/driver.cxx | 35 |
6 files changed, 124 insertions, 52 deletions
diff --git a/libbutl/path.cxx b/libbutl/path.cxx index 11a34c9..47d691b 100644 --- a/libbutl/path.cxx +++ b/libbutl/path.cxx @@ -17,7 +17,7 @@ #else # include <pwd.h> // struct passwd, getpwuid_r() # include <errno.h> // EINVAL -# include <stdlib.h> // realpath(), getenv() +# include <stdlib.h> // realpath() # include <limits.h> // PATH_MAX # include <unistd.h> // getcwd(), chdir() # include <string.h> // strlen(), strcpy() @@ -29,6 +29,7 @@ #ifndef __cpp_lib_modules #include <string> +#include <vector> #include <cstddef> #include <utility> @@ -123,33 +124,39 @@ namespace butl } #ifndef _WIN32 - static const char* + static const vector<string> tmp_vars ({"TMPDIR", "TMP", "TEMP", "TEMPDIR"}); + + static string temp_directory () { - const char* dir (nullptr); - const char* env[] = {"TMPDIR", "TMP", "TEMP", "TEMPDIR", nullptr}; + optional<string> dir; - for (auto e (env); dir == nullptr && *e != nullptr; ++e) - dir = getenv (*e); + for (const string& v: tmp_vars) + { + dir = getenv (v); + + if (dir) + break; + } - if (dir == nullptr) + if (!dir) dir = "/tmp"; struct stat s; - if (stat (dir, &s) != 0) + if (stat (dir->c_str (), &s) != 0) throw_generic_error (errno); if (!S_ISDIR (s.st_mode)) throw_generic_error (ENOTDIR); - return dir; + return *dir; } static string home () { - if (const char* h = getenv ("HOME")) - return h; + if (optional<string> h = getenv ("HOME")) + return *h; // Struct passwd has 5 members that will use this buffer. Two are the // home directory and shell paths. The other three are the user login @@ -214,8 +221,8 @@ namespace butl #else // Could be set by, e.g., MSYS and Cygwin shells. // - if (const char* h = getenv ("HOME")) - return h; + if (optional<string> h = getenv ("HOME")) + return *h; char h[_MAX_PATH]; HRESULT r (SHGetFolderPathA (NULL, CSIDL_PROFILE, NULL, 0, h)); diff --git a/libbutl/process.cxx b/libbutl/process.cxx index 41e2c0d..384af29 100644 --- a/libbutl/process.cxx +++ b/libbutl/process.cxx @@ -9,7 +9,6 @@ #include <errno.h> #ifndef _WIN32 -# include <stdlib.h> // setenv(), unsetenv() # include <signal.h> // SIG* # include <unistd.h> // execvp, fork, dup2, pipe, chdir, *_FILENO, getpid # include <sys/wait.h> // waitpid @@ -60,7 +59,7 @@ #include <map> #include <ratio> // milli #include <chrono> -#include <cstdlib> // getenv(), __argv[] +#include <cstdlib> // __argv[] #include <algorithm> // find() #endif #endif @@ -83,7 +82,6 @@ import std.io; import std.threading; // Clang wants it in purview (see process-details.hxx). #endif import butl.path; -import butl.optional; import butl.fdstream; import butl.vector_view; import butl.small_vector; @@ -273,7 +271,8 @@ namespace butl // 2. We do not continue searching on EACCES from execve(). // 3. We do not execute via default shell on ENOEXEC from execve(). // - for (const char* b (getenv ("PATH")), *e; + optional<string> p (getenv ("PATH")); + for (const char* b (p ? p->c_str () : nullptr), *e; b != nullptr; b = (e != nullptr ? e + 1 : e)) { @@ -460,12 +459,17 @@ namespace butl { const char* v (strchr (ev, '=')); - int r (v != nullptr - ? setenv (string (ev, v - ev).c_str (), v + 1, 1) - : unsetenv (ev)); - - if (r == -1) - fail (true); + try + { + if (v != nullptr) + setenv (string (ev, v - ev), v + 1); + else + unsetenv (ev); + } + catch (const system_error& e) + { + throw process_child_error (e.code ().value ()); + } } } @@ -845,7 +849,8 @@ namespace butl // Now search in PATH. Recall is unchanged. // - for (const char* b (getenv ("PATH")), *e; + optional<string> p (getenv ("PATH")); + for (const char* b (p ? p->c_str () : nullptr), *e; b != nullptr; b = (e != nullptr ? e + 1 : e)) { @@ -1126,7 +1131,7 @@ namespace butl // cmd.exe and passing the batch file as an argument (see CreateProcess() // for deails). // - const char* batch (nullptr); + optional<string> batch; { const char* p (pp.effect_string ()); const char* e (path::traits::find_extension (p, strlen (p))); @@ -1135,7 +1140,7 @@ namespace butl { batch = getenv ("COMSPEC"); - if (batch == nullptr) + if (!batch) batch = "C:\\Windows\\System32\\cmd.exe"; } } @@ -1240,14 +1245,14 @@ namespace butl cmd_line += '"'; }; - if (batch != nullptr) + if (batch) { - append (batch); + append (*batch); append ("/c"); append (pp.effect_string ()); } - for (const char* const* p (args + (batch != nullptr ? 1 : 0)); + for (const char* const* p (args + (batch ? 1 : 0)); *p != 0; ++p) append (*p); @@ -1463,7 +1468,7 @@ namespace butl }; bool msys (false); - if (batch == nullptr) + if (!batch) { string p (pp.effect_string ()); auto i (detect_msys_cache_.find (p)); @@ -1482,7 +1487,7 @@ namespace butl for (system_clock::duration timeout (1h);;) // Try for about 1 hour. { if (!CreateProcess ( - batch != nullptr ? batch : pp.effect_string (), + batch ? batch->c_str () : pp.effect_string (), const_cast<char*> (cmd_line.c_str ()), 0, // Process security attributes. 0, // Primary thread security attributes. diff --git a/libbutl/utility.cxx b/libbutl/utility.cxx index 8e2422aa..502586c 100644 --- a/libbutl/utility.cxx +++ b/libbutl/utility.cxx @@ -10,6 +10,8 @@ #include <libbutl/win32-utility.hxx> #endif +#include <stdlib.h> // setenv(), unsetenv(), _putenv() + #ifndef __cpp_lib_modules #include <string> #include <cstddef> @@ -124,6 +126,36 @@ namespace butl return l; } + + void + setenv (const string& name, const string& value) + { +#ifndef _WIN32 + if (::setenv (name.c_str (), value.c_str (), 1 /* overwrite */) == -1) + throw_generic_error (errno); +#else + // The documentation doesn't say how to obtain the failure reason, so we + // will assume it to always be EINVAL (as the most probable one). + // + if (_putenv (string (name + '=' + value).c_str ()) == -1) + throw_generic_error (EINVAL); +#endif + } + + void + unsetenv (const string& name) + { +#ifndef _WIN32 + if (::unsetenv (name.c_str ()) == -1) + throw_generic_error (errno); +#else + // The documentation doesn't say how to obtain the failure reason, so we + // will assume it to always be EINVAL (as the most probable one). + // + if (_putenv (string (name + '=').c_str ()) == -1) + throw_generic_error (EINVAL); +#endif + } } namespace std diff --git a/libbutl/utility.ixx b/libbutl/utility.ixx index d4aaa65..565a694 100644 --- a/libbutl/utility.ixx +++ b/libbutl/utility.ixx @@ -2,6 +2,10 @@ // copyright : Copyright (c) 2014-2018 Code Synthesis Ltd // license : MIT; see accompanying LICENSE file +#ifndef __cpp_lib_modules +#include <cstdlib> // getenv() +#endif + namespace butl { inline char @@ -199,4 +203,13 @@ namespace butl throw std::istream::failure (""); } + + inline optional<std::string> + getenv (const std::string& name) + { + if (const char* r = std::getenv (name.c_str ())) + return std::string (r); + + return nullopt; + } } diff --git a/libbutl/utility.mxx b/libbutl/utility.mxx index 740b411..252a4b4 100644 --- a/libbutl/utility.mxx +++ b/libbutl/utility.mxx @@ -35,6 +35,9 @@ export module butl.utility; import std.core; import std.io; #endif +import butl.optional; +#else +#include <libbutl/optional.mxx> #endif #include <libbutl/export.hxx> @@ -183,6 +186,25 @@ LIBBUTL_MODEXPORT namespace butl bool eof (std::istream&); + // Environment variables. + // + optional<std::string> + getenv (const std::string&); + + // Throw system_error on failure. + // + // Note that on Windows setting an empty value usets the variable. + // + LIBBUTL_SYMEXPORT + void + setenv (const std::string& name, const std::string& value); + + // Throw system_error on failure. + // + LIBBUTL_SYMEXPORT + void + unsetenv (const std::string&); + // Key comparators (i.e., to be used in sets, maps, etc). // struct compare_c_string diff --git a/tests/process/driver.cxx b/tests/process/driver.cxx index 9bee6c5..6ca28a5 100644 --- a/tests/process/driver.cxx +++ b/tests/process/driver.cxx @@ -2,8 +2,6 @@ // copyright : Copyright (c) 2014-2018 Code Synthesis Ltd // license : MIT; see accompanying LICENSE file -#include <stdlib.h> // getenv(), setenv(), _putenv() - #include <cassert> #ifndef __cpp_lib_modules @@ -23,11 +21,13 @@ import std.core; import std.io; #endif import butl.path; +import butl.utility; // setenv(), getenv() import butl.process; import butl.optional; import butl.fdstream; #else #include <libbutl/path.mxx> +#include <libbutl/utility.mxx> #include <libbutl/process.mxx> #include <libbutl/optional.mxx> #include <libbutl/fdstream.mxx> @@ -68,13 +68,8 @@ exec (const path& p, // sure that the child process will not see the variable that is requested // to be unset, and will see the other one unaffected. // -#ifndef _WIN32 - assert (setenv ("DEF", "2", 1) == 0); - assert (setenv ("XYZ", "3", 1) == 0); -#else - assert (_putenv ("DEF=2") == 0); - assert (_putenv ("XYZ=3") == 0); -#endif + setenv ("DEF", "2"); + setenv ("XYZ", "3"); } if (cwd != nullptr) @@ -205,6 +200,9 @@ exec (const path& p, int main (int argc, const char* argv[]) { + using butl::getenv; + using butl::optional; + bool child (false); bool bin (false); dir_path wd; // Working directory. @@ -276,10 +274,9 @@ main (int argc, const char* argv[]) // Check that the ABC variable is set, the DEF is unset and the XYZ is // left unchanged. // - const char* v; - if ((v = getenv ("ABC")) == nullptr || string ("1") != v || - getenv ("DEF") != nullptr || - (v = getenv ("XYZ")) == nullptr || string ("3") != v) + if (getenv ("ABC") != optional<string> ("1") || + getenv ("DEF") || + getenv ("XYZ") != optional<string> ("3")) return 1; } @@ -383,14 +380,10 @@ main (int argc, const char* argv[]) // string paths (fp.directory ().string ()); - if (char const* s = getenv ("PATH")) - paths += string (1, path::traits::path_separator) + s; + if (optional<string> p = getenv ("PATH")) + paths += string (1, path::traits::path_separator) + *p; -#ifndef _WIN32 - assert (setenv ("PATH", paths.c_str (), 1) == 0); -#else - assert (_putenv (("PATH=" + paths).c_str ()) == 0); -#endif + setenv ("PATH", paths); dir_path::current_directory (fp.directory () / dir_path ("..")); @@ -419,7 +412,7 @@ main (int argc, const char* argv[]) assert (exec (owd / "test")); paths = owd.string () + path::traits::path_separator + paths; - assert (_putenv (("PATH=" + paths).c_str ()) == 0); + setenv ("PATH", paths); assert (exec (path ("test.bat"))); assert (exec (path ("test"))); |