From df27d2ac52bba33c541119caa3a1dc9d83690079 Mon Sep 17 00:00:00 2001 From: Boris Kolpackov Date: Fri, 9 Apr 2021 18:42:08 +0200 Subject: Escape additional characters (=,;) if running batch file --- libbutl/process.cxx | 28 ++++++++++++++++++++-------- libbutl/process.mxx | 10 ++++++---- 2 files changed, 26 insertions(+), 12 deletions(-) diff --git a/libbutl/process.cxx b/libbutl/process.cxx index 570c857..73cc8a6 100644 --- a/libbutl/process.cxx +++ b/libbutl/process.cxx @@ -98,7 +98,7 @@ #include #include // ios_base::failure -#include // strlen(), strchr(), strncmp() +#include // strlen(), strchr(), strpbrk(), strncmp() #include // move() #include @@ -1309,13 +1309,25 @@ namespace butl }; const char* process:: - quote_argument (const char* a, string& s) + quote_argument (const char* a, string& s, bool bat) { - // On Windows we need to protect values with spaces using quotes. - // Since there could be actual quotes in the value, we need to - // escape them. + // On Windows we need to protect values with spaces using quotes. Since + // there could be actual quotes in the value, we need to escape them. // - bool q (*a == '\0' || strchr (a, ' ') != nullptr); + // For batch files we also protect equal (`=`), comma (`,`) and semicolon + // (`;`) since otherwise an argument containing any of these will be split + // into several as if they were spaces (that is, the parts will appear in + // %1 %2, etc., instead of all in %1). This of course could break some + // batch files that rely on this semantics (for example, to automatically + // handle --foo=bar as --foo bar) but overall seeing a single argument + // (albeit quoted) is closer to the behavior of real executables. So we do + // this by default and if it becomes a problem we can invent a flag + // (probably in process_env) to disable this quoting (and while at it we + // may add a flag to disable all quoting since the user may need to quote + // some arguments but not others). + // + bool q (*a == '\0' || + (bat ? strpbrk (a, " =,;") : strchr (a, ' ')) != nullptr); if (!q && strchr (a, '"') == nullptr) return a; @@ -1548,12 +1560,12 @@ namespace butl // string cmd_line; { - auto append = [&cmd_line, buf = string ()] (const char* a) mutable + auto append = [&batch, &cmd_line, buf = string ()] (const char* a) mutable { if (!cmd_line.empty ()) cmd_line += ' '; - cmd_line += quote_argument (a, buf); + cmd_line += quote_argument (a, buf, batch.has_value ()); }; if (batch) diff --git a/libbutl/process.mxx b/libbutl/process.mxx index 9106549..ab5e84f 100644 --- a/libbutl/process.mxx +++ b/libbutl/process.mxx @@ -503,13 +503,15 @@ LIBBUTL_MODEXPORT namespace butl static void print (std::ostream&, const char* const args[], size_t n = 0); - // Quote and escape the specified command line argument. Return the - // original string if neither is necessary and a pointer to the provided - // buffer string containing the escaped version otherwise. + // Quote and escape the specified command line argument. If batch is true + // then also quote the equal (`=`), comma (`,`) and semicolon (`;`) + // characters which are treated as argument separators in batch file. + // Return the original string if neither is necessary and a pointer to the + // provided buffer string containing the escaped version otherwise. // #ifdef _WIN32 static const char* - quote_argument (const char*, std::string& buffer); + quote_argument (const char*, std::string& buffer, bool batch); #endif public: -- cgit v1.1