aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--libbutl/process-io.cxx106
-rw-r--r--libbutl/process-io.mxx24
-rw-r--r--libbutl/process.mxx11
-rw-r--r--tests/process/driver.cxx36
4 files changed, 176 insertions, 1 deletions
diff --git a/libbutl/process-io.cxx b/libbutl/process-io.cxx
new file mode 100644
index 0000000..c29bbc0
--- /dev/null
+++ b/libbutl/process-io.cxx
@@ -0,0 +1,106 @@
+// file : libbutl/process-io.cxx -*- C++ -*-
+// license : MIT; see accompanying LICENSE file
+
+#ifndef __cpp_modules_ts
+#include <libbutl/process-io.mxx>
+#endif
+
+// C includes.
+
+#ifndef __cpp_lib_modules_ts
+#include <ostream>
+
+#include <cstring> // strchr()
+#endif
+
+// Other includes.
+
+#ifdef __cpp_modules_ts
+module butl.process_io;
+
+// Only imports additional to interface.
+#ifdef __clang__
+#ifdef __cpp_lib_modules_ts
+import std.core;
+import std.io;
+#endif
+import butl.process;
+#endif
+
+import butl.path-io;
+#else
+#include <libbutl/path-io.mxx>
+#endif
+
+using namespace std;
+
+namespace butl
+{
+ // process_env
+ //
+ ostream&
+ operator<< (ostream& o, const process_env& env)
+ {
+ bool first (true);
+ const dir_path* cwd (env.cwd);
+
+ if (cwd != nullptr && !cwd->empty ())
+ {
+ if (cwd->string ().find (' ') != string::npos)
+ o << "PWD=\"" << *cwd << '"';
+ else
+ o << "PWD=" << *cwd;
+
+ first = false;
+ }
+
+ if (env.vars != nullptr)
+ {
+ for (const char* const* ev (env.vars); *ev != nullptr; ++ev)
+ {
+ if (first)
+ first = false;
+ else
+ o << ' ';
+
+ const char* v (*ev);
+
+ // If there is no `=` in the string, then this is just a name
+ // (variable unset) and we print it as the empty string assignment.
+ //
+ const char* eq (strchr (v, '='));
+
+ // If there is the space character in the string, then we quote the
+ // variable value, unless this is the variable name that contains the
+ // space character in which case we quote the whole (potentially
+ // broken) assignment.
+ //
+ const char* sp (strchr (v, ' '));
+
+ if (eq != nullptr) // Variable assignment?
+ {
+ if (sp == nullptr) // No space?
+ {
+ o << v;
+ }
+ else if (eq < sp) // Space in the value?
+ {
+ o.write (v, eq - v + 1); // Name and '='.
+ o << '"' << eq + 1 << '"'; // Quoted value.
+ }
+ else // Space in the name.
+ o << '"' << v << '"';
+ }
+ else // Variable unset.
+ {
+ if (sp == nullptr) // No space?
+ o << v << '=';
+ else // Space in the name.
+ o << '"' << v << "=\"";
+ }
+ }
+ }
+
+ return o;
+ }
+}
diff --git a/libbutl/process-io.mxx b/libbutl/process-io.mxx
index 5471846..d07a212 100644
--- a/libbutl/process-io.mxx
+++ b/libbutl/process-io.mxx
@@ -40,4 +40,28 @@ LIBBUTL_MODEXPORT namespace butl
process::print (o, a.argv, a.argc);
return o;
}
+
+ // Print the environment variables and the current working directory (if
+ // specified) in a POSIX shell command line notation. The process path
+ // itself is not printed. For example:
+ //
+ // LC_ALL=C
+ //
+ // If an environment variable is in the `name` rather than in the
+ // `name=value` form, then it is considered unset. Since there is no POSIX
+ // way to unset a variable on the command line, this information is printed
+ // as `name=` (ambiguous with assigning an empty value but the two cases are
+ // normally handled in the same way). For example:
+ //
+ // PATH= LC_ALL=C
+ //
+ // Note that since there is no POSIX way to change the current working
+ // directory of a command to be executed, this information is printed in a
+ // pseudo-notation by assigning to PWD (which, according POSIX, would result
+ // in the undefined behavior of the cwd utility). For example:
+ //
+ // PWD=/tmp LC_ALL=C
+ //
+ LIBBUTL_SYMEXPORT std::ostream&
+ operator<< (std::ostream&, const process_env&);
}
diff --git a/libbutl/process.mxx b/libbutl/process.mxx
index 1f378c4..b8c6054 100644
--- a/libbutl/process.mxx
+++ b/libbutl/process.mxx
@@ -494,7 +494,6 @@ LIBBUTL_MODEXPORT namespace butl
quote_argument (const char*, std::string& buffer);
#endif
-
public:
id_type
id () const;
@@ -548,6 +547,16 @@ LIBBUTL_MODEXPORT namespace butl
const dir_path* cwd = nullptr;
const char* const* vars = nullptr;
+ // Return true if there is an "environment", that is, either the current
+ // working directory or environment variables.
+ //
+ bool
+ env () const
+ {
+ return (cwd != nullptr && !cwd->empty ()) ||
+ (vars != nullptr && *vars != nullptr);
+ }
+
process_env (const process_path& p,
const dir_path& c = dir_path (),
const char* const* v = nullptr)
diff --git a/tests/process/driver.cxx b/tests/process/driver.cxx
index 7a0f38b..3be4154 100644
--- a/tests/process/driver.cxx
+++ b/tests/process/driver.cxx
@@ -7,6 +7,7 @@
#include <ios>
#include <string>
#include <vector>
+#include <sstream>
#include <iterator> // istreambuf_iterator, ostream_iterator
#include <algorithm> // copy()
#include <iostream>
@@ -28,6 +29,7 @@ import butl.fdstream;
#include <libbutl/path.mxx>
#include <libbutl/utility.mxx>
#include <libbutl/process.mxx>
+#include <libbutl/process-io.mxx>
#include <libbutl/optional.mxx>
#include <libbutl/fdstream.mxx>
#endif
@@ -474,4 +476,38 @@ main (int argc, const char* argv[])
assert (!exec (path ("testX.bat")));
}
#endif
+
+ // Test printing process_env to stream.
+ //
+ {
+ auto str = [] (const process_env& env)
+ {
+ ostringstream os;
+ os << env;
+ return os.str ();
+ };
+
+ process_path p;
+
+ assert (str (process_env (p)) == "");
+
+ {
+ dir_path d ("dir");
+ dir_path ds ("d ir");
+ assert (str (process_env (p, d)) == "PWD=dir");
+ assert (str (process_env (p, ds)) == "PWD=\"d ir\"");
+ }
+
+ {
+ dir_path ed; // Empty.
+ const char* vars[] = {nullptr};
+ assert (str (process_env (p, ed, vars)) == "");
+ }
+
+ {
+ const char* vars[] = {"A=B", "A=B C", "A B=C", "A", "A B", nullptr};
+ assert (str (process_env (p, vars)) ==
+ "A=B A=\"B C\" \"A B=C\" A= \"A B=\"");
+ }
+ }
}