From 481f9ba1aee62fea092184f2243d210a8686781f Mon Sep 17 00:00:00 2001 From: Boris Kolpackov Date: Thu, 14 Jun 2018 18:13:04 +0200 Subject: Add portable environment variable manipulation functions --- libbutl/path.cxx | 33 ++++++++++++++++++++------------- libbutl/process.cxx | 41 +++++++++++++++++++++++------------------ libbutl/utility.cxx | 32 ++++++++++++++++++++++++++++++++ libbutl/utility.ixx | 13 +++++++++++++ libbutl/utility.mxx | 22 ++++++++++++++++++++++ 5 files changed, 110 insertions(+), 31 deletions(-) (limited to 'libbutl') 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 // struct passwd, getpwuid_r() # include // EINVAL -# include // realpath(), getenv() +# include // realpath() # include // PATH_MAX # include // getcwd(), chdir() # include // strlen(), strcpy() @@ -29,6 +29,7 @@ #ifndef __cpp_lib_modules #include +#include #include #include @@ -123,33 +124,39 @@ namespace butl } #ifndef _WIN32 - static const char* + static const vector tmp_vars ({"TMPDIR", "TMP", "TEMP", "TEMPDIR"}); + + static string temp_directory () { - const char* dir (nullptr); - const char* env[] = {"TMPDIR", "TMP", "TEMP", "TEMPDIR", nullptr}; + optional 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 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 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 #ifndef _WIN32 -# include // setenv(), unsetenv() # include // SIG* # include // execvp, fork, dup2, pipe, chdir, *_FILENO, getpid # include // waitpid @@ -60,7 +59,7 @@ #include #include // milli #include -#include // getenv(), __argv[] +#include // __argv[] #include // 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 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 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 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 (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 #endif +#include // setenv(), unsetenv(), _putenv() + #ifndef __cpp_lib_modules #include #include @@ -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 // getenv() +#endif + namespace butl { inline char @@ -199,4 +203,13 @@ namespace butl throw std::istream::failure (""); } + + inline optional + 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 #endif #include @@ -183,6 +186,25 @@ LIBBUTL_MODEXPORT namespace butl bool eof (std::istream&); + // Environment variables. + // + optional + 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 -- cgit v1.1