Author: jamessan
Date: Mon Mar 16 04:11:36 2020
New Revision: 1875230

URL: http://svn.apache.org/viewvc?rev=1875230&view=rev
Log:
Followup to r1874093, add Windows-specific argument escaping

Rather than directly using apr_pescape_shell(), use apr_escape_shell() to give
an indication whether escaping is needed.  If APR reports no escaping is
needed, simply surround the argument in double-quotes to handle any embedded
whitespace.

When escaping is needed, on Unix we continue to use APR's escaping +
post-processing for whitespace.  On Windows, perform the escaping ourselves per
these rules:

  1. Surround the string with double-quotes
  2. Escape any double-quotes or backslashes preceding a double-quote
  3. Escape any metacharacters, including double-quotes, with ^

* subversion/libsvn_subr/cmdline.c
  (escape_path): Refactored as above

Modified:
    subversion/trunk/subversion/libsvn_subr/cmdline.c

Modified: subversion/trunk/subversion/libsvn_subr/cmdline.c
URL: 
http://svn.apache.org/viewvc/subversion/trunk/subversion/libsvn_subr/cmdline.c?rev=1875230&r1=1875229&r2=1875230&view=diff
==============================================================================
--- subversion/trunk/subversion/libsvn_subr/cmdline.c (original)
+++ subversion/trunk/subversion/libsvn_subr/cmdline.c Mon Mar 16 04:11:36 2020
@@ -1305,36 +1305,92 @@ static const char *
 escape_path(apr_pool_t *pool, const char *orig_path)
 {
   apr_size_t len, esc_len;
-  const char *path;
-  char *p, *esc_path;
+  apr_status_t status;
 
-  path = apr_pescape_shell(pool, orig_path);
+  len = strlen(orig_path);
+  esc_len = 0;
 
-  len = esc_len = 0;
+  status = apr_escape_shell(NULL, orig_path, len, &esc_len);
 
-  /* Now that apr has done its escaping, we can check whether there's any
-     whitespace that also needs to be escaped.  This must be done after the
-     fact, otherwise apr_pescape_shell() would escape the backslashes we're
-     inserting. */
-  for (p = (char *)path; *p; p++)
+  if (status == APR_NOTFOUND)
     {
-      len++;
-      if (*p == ' ' || *p == '\t')
-        esc_len++;
+      /* No special characters found by APR, so just surround it in double
+         quotes in case there is whitespace, which APR (as of 1.6.5) doesn't
+         consider special. */
+      return apr_psprintf(pool, "\"%s\"", orig_path);
     }
-
-  if (esc_len == 0)
-    return path;
-
-  p = esc_path = apr_pcalloc(pool, len + esc_len + 1);
-  while (*path)
+  else
     {
-      if (*path == ' ' || *path == '\t')
-        *p++ = '\\';
-      *p++ = *path++;
-    }
+#ifdef WIN32
+      const char *p;
+      /* Following the advice from
+         
https://docs.microsoft.com/en-us/archive/blogs/twistylittlepassagesallalike/everyone-quotes-command-line-arguments-the-wrong-way
+         1. Surround argument with double-quotes
+         2. Escape backslashes, if they're followed by a double-quote, and 
double-quotes
+         3. Escape any metacharacter, including double-quotes, with ^ */
+
+      /* Use APR's buffer size as an approximation for how large the escaped
+         string should be, plus 4 bytes for the leading/trailing ^" */
+      svn_stringbuf_t *buf = svn_stringbuf_create_ensure(esc_len + 4, pool);
+      svn_stringbuf_appendcstr(buf, "^\"");
+      for (p = orig_path; *p; p++)
+        {
+          int nr_backslash = 0;
+          while (*p && *p == '\\')
+            {
+              nr_backslash++;
+              p++;
+            }
+
+          if (!*p)
+            /* We've reached the end of the argument, so we need 2n backslash
+               characters.  That will be interpreted as n backslashes and the
+               final double-quote character will be interpreted as the final
+               string delimiter. */
+            svn_stringbuf_appendfill(buf, '\\', nr_backslash * 2);
+          else if (*p == '"')
+            {
+              /* Double-quote as part of the argument means we need to double
+                 any preceeding backslashes and then add one to escape the
+                 double-quote. */
+              svn_stringbuf_appendfill(buf, '\\', nr_backslash * 2 + 1);
+              svn_stringbuf_appendbyte(buf, '^');
+              svn_stringbuf_appendbyte(buf, *p);
+            }
+          else
+            {
+              /* Since there's no double-quote, we just insert any backslashes
+                 literally.  No escaping needed. */
+              svn_stringbuf_appendfill(buf, '\\', nr_backslash);
+              if (strchr("()%!^<>&|", *p))
+                svn_stringbuf_appendbyte(buf, '^');
+              svn_stringbuf_appendbyte(buf, *p);
+            }
+        }
+      svn_stringbuf_appendcstr(buf, "^\"");
+      return buf->data;
+#else
+      char *path, *p, *esc_path;
+
+      /* Account for whitespace, since APR doesn't */
+      for (p = (char *)orig_path; *p; p++)
+        if (strchr(" \t\n\r", *p))
+          esc_len++;
+
+      path = apr_pcalloc(pool, esc_len);
+      apr_escape_shell(path, orig_path, len, NULL);
+
+      p = esc_path = apr_pcalloc(pool, len + esc_len + 1);
+      while (*path)
+        {
+          if (strchr(" \t\n\r", *path))
+            *p++ = '\\';
+          *p++ = *path++;
+        }
 
-  return esc_path;
+      return esc_path;
+#endif
+    }
 }
 
 svn_error_t *


Reply via email to