aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKaren Arutyunov <karen@codesynthesis.com>2019-08-27 13:32:24 +0300
committerKaren Arutyunov <karen@codesynthesis.com>2019-08-27 16:02:20 +0300
commit69ed8e0c82e4965c9cdd96f6847e163f7a032842 (patch)
treed79eeecf03af8558026ce93a69a3cbddac232863
parentdd681cba8b18eba62d3fce475ddcd5f1825b75bf (diff)
Fix backslash escaping in windows process arguments
-rw-r--r--libbutl/process.cxx28
-rw-r--r--tests/process/driver.cxx72
2 files changed, 89 insertions, 11 deletions
diff --git a/libbutl/process.cxx b/libbutl/process.cxx
index 3b18355..8b558ff 100644
--- a/libbutl/process.cxx
+++ b/libbutl/process.cxx
@@ -1301,16 +1301,38 @@ namespace butl
if (q)
s += '"';
+ // Note that backslashes don't need escaping, unless they immediately
+ // precede the double quote (see `Parsing C Command-Line Arguments` MSDN
+ // article for more details). For example:
+ //
+ // -DPATH="C:\\foo\\" -> -DPATH=\"C:\\foo\\\\\"
+ // -DPATH=C:\foo bar\ -> "-DPATH=C:\foo bar\\"
+ //
+ size_t nbs (0); // Number of consecutive backslashes.
for (size_t i (0), n (strlen (a)); i != n; ++i)
{
- if (a[i] != '"')
- s += a[i];
+ char c (a[i]);
+
+ if (c != '"')
+ s += c;
else
- s += "\\\"";
+ {
+ if (nbs != 0)
+ s.append (nbs, '\\'); // Escape backslashes.
+
+ s += "\\\""; // Escape quote.
+ }
+
+ nbs = c == '\\' ? nbs + 1 : 0;
}
if (q)
+ {
+ if (nbs != 0)
+ s.append (nbs, '\\'); // Escape backslashes.
+
s += '"';
+ }
return s.c_str ();
}
diff --git a/tests/process/driver.cxx b/tests/process/driver.cxx
index f2dd337..a235f05 100644
--- a/tests/process/driver.cxx
+++ b/tests/process/driver.cxx
@@ -38,6 +38,36 @@ using namespace butl;
static const char* envvars[] = {"ABC=1", "DEF", nullptr};
+using cstrings = vector<const char*>;
+
+bool
+roundtrip_arg (const path& p, const string& a)
+{
+ string r;
+
+ try
+ {
+ cstrings args {p.string ().c_str (), "-a", a.c_str (), nullptr};
+ process pr (args.data (), 0 /* in */, -1 /* out */, 2 /* err */);
+
+ ifdstream is (move (pr.in_ofd));
+
+ assert (getline (is, r));
+ assert (pr.wait ());
+ }
+ catch (const process_error& e)
+ {
+ //cerr << args[0] << ": " << e << endl;
+
+ if (e.child)
+ exit (1);
+
+ assert (false);
+ }
+
+ return r == a;
+}
+
static bool
exec (const path& p,
vector<char> in = vector<char> (),
@@ -48,7 +78,6 @@ exec (const path& p,
dir_path wd = dir_path (), // Set the working dir for the child process.
bool env = false) // Set environment variables for the child.
{
- using cstrings = vector<const char*>;
using butl::optional;
assert (!in.empty () || (!out && !err)); // Nothing to output if no input.
@@ -197,6 +226,26 @@ exec (const path& p,
p, vector<char> (i.begin (), i.end ()), o, e, pipeline, false, wd, env);
}
+// Usages:
+//
+// argv[0]
+// argv[0] -a <args>
+// argv[0] -c [-b] [-e] [<cwd>]
+//
+// In the first form run some basic process execution/communication tests.
+//
+// In the second form print the arguments to STDOUT one per line.
+//
+// In the third form read the data from STDIN and print it to STDOUT and
+// STDERR. Also check if the working directory argument matches the current
+// directory, if specified.
+//
+// -b
+// Set binary mode for the standard streams.
+//
+// -e
+// Check some environment variable values.
+//
int
main (int argc, const char* argv[])
{
@@ -210,6 +259,14 @@ main (int argc, const char* argv[])
assert (argc > 0);
+ if (argc > 1 && string (argv[1]) == "-a")
+ {
+ for (int i (2); i != argc; ++i)
+ cout << argv[i] << endl;
+
+ return 0;
+ }
+
int i (1);
for (; i != argc; ++i)
{
@@ -237,13 +294,7 @@ main (int argc, const char* argv[])
}
}
- if (i != argc)
- {
- if (!child)
- cerr << "usage: " << argv[0] << " [-c] [-b] [<dir>]" << endl;
-
- return 1;
- }
+ assert (i == argc);
path p;
@@ -328,6 +379,11 @@ main (int argc, const char* argv[])
assert (!p.wait ()); // "Exited" with an error.
}
+ assert (roundtrip_arg (p, "-DPATH=\"C:\\\\foo\\\\\"")); // -DPATH="C:\\foo\\"
+ assert (roundtrip_arg (p, "C:\\\\f oo\\\\"));
+ assert (roundtrip_arg (p, "C:\\\"f oo\\\\"));
+ assert (roundtrip_arg (p, "C:\\f oo\\"));
+
const char* s ("ABC\nXYZ");
assert (exec (p));