aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBoris Kolpackov <boris@codesynthesis.com>2018-06-14 18:13:04 +0200
committerKaren Arutyunov <karen@codesynthesis.com>2018-06-15 14:21:58 +0300
commit481f9ba1aee62fea092184f2243d210a8686781f (patch)
treea54fc536d835c5277038e145fabc1255c42ff8ac
parent5d424ea127333859a32addaf9e28eae07a4dc9f6 (diff)
Add portable environment variable manipulation functions
-rw-r--r--libbutl/path.cxx33
-rw-r--r--libbutl/process.cxx41
-rw-r--r--libbutl/utility.cxx32
-rw-r--r--libbutl/utility.ixx13
-rw-r--r--libbutl/utility.mxx22
-rw-r--r--tests/process/driver.cxx35
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")));