OC-11191 Correct escaping support for compiler arguments and defines
authorAlexey Utkin <alexey.utkin@jetbrains.com>
Mon, 1 Dec 2014 16:38:59 +0000 (19:38 +0300)
committerAlexey Utkin <alexey.utkin@jetbrains.com>
Mon, 1 Dec 2014 16:53:53 +0000 (19:53 +0300)
platform/util/src/com/intellij/execution/configurations/CommandLineTokenizer.java
platform/util/src/com/intellij/openapi/util/text/StringUtil.java
platform/util/testSrc/com/intellij/execution/configurations/CommandLineTokenizerTest.java

index 3f351b9a573e452db6f0c4ce5b08a72bef8d967e..925bbb838d71d94a44f5731f1bd515556a0cd402 100644 (file)
@@ -99,11 +99,11 @@ public class CommandLineTokenizer extends StringTokenizer {
 
         int i;
         int quotationMarks = 0;
-        final StringBuffer buffer = new StringBuffer();
+        final StringBuilder buffer = new StringBuilder();
 
         do {
             while ((i = nextToken.indexOf('"')) >= 0) {
-                boolean isEscapedQuote = i > 0 && nextToken.charAt(i - 1) == '\\';
+                boolean isEscapedQuote = isEscapedAtPos(nextToken, i);
                 if (!isEscapedQuote) quotationMarks++;
                 buffer.append(nextToken.substring(0, isEscapedQuote ? i - 1 : i));
                 if (isEscapedQuote) buffer.append('"');
@@ -120,7 +120,7 @@ public class CommandLineTokenizer extends StringTokenizer {
               buffer.append(nextToken);
             }
 
-            if ((isEscapedWhitespace || quotationMarks % 2 == 1) && super.hasMoreTokens()) {
+            if ((isEscapedWhitespace || (quotationMarks & 1) == 1) && super.hasMoreTokens()) {
                 nextToken = super.nextToken();
             } else {
                 nextToken = null;
@@ -129,4 +129,14 @@ public class CommandLineTokenizer extends StringTokenizer {
 
         return buffer.toString();
     }
+
+    private static boolean isEscapedAtPos(String token, int pos) {
+        int escapeCount = 0;
+        --pos;
+        while (pos >= 0 && token.charAt(pos) == '\\') {
+            ++escapeCount;
+            --pos;
+        }
+        return (escapeCount & 1) == 1;
+    }
 }
index 543b3bc2d73c696a9a1dfb42a40b232b476dcb98..1eddfa14c7107fa3945e799503f65b207ac78f69 100644 (file)
@@ -1977,11 +1977,19 @@ public class StringUtil extends StringUtilRt {
   @Contract(pure = true)
   public static String unescapeSlashes(@NotNull final String str) {
     final StringBuilder buf = new StringBuilder(str.length());
-    unescapeSlashes(buf, str);
+    unescapeChar(buf, str, '/');
     return buf.toString();
   }
 
-  private static void unescapeSlashes(@NotNull StringBuilder buf, @NotNull String str) {
+  @NotNull
+  @Contract(pure = true)
+  public static String unescapeBackSlashes(@NotNull final String str) {
+    final StringBuilder buf = new StringBuilder(str.length());
+    unescapeChar(buf, str, '\\');
+    return buf.toString();
+  }
+
+  private static void unescapeChar(@NotNull StringBuilder buf, @NotNull String str, char unescapeChar) {
     final int length = str.length();
     final int last = length - 1;
     for (int i = 0; i < length; i++) {
@@ -1989,7 +1997,7 @@ public class StringUtil extends StringUtilRt {
       if (ch == '\\' && i != last) {
         i++;
         ch = str.charAt(i);
-        if (ch != '/') buf.append('\\');
+        if (ch != unescapeChar) buf.append('\\');
       }
 
       buf.append(ch);
index 276ca549eb30ed95e96dd1f83735be5f17429e2d..98d56bb87dd61bd6a8aa1529a8c7726b52c2233f 100644 (file)
@@ -24,8 +24,10 @@ public class CommandLineTokenizerTest extends TestCase {
     assertTokens("a\" b\"", "a b");
     assertTokens("\"a b", "a b");
     assertTokens("a b\"", "a", "b");
+    assertTokens("a b\\\"", "a", "b\"");
     assertTokens("a b\" c", "a", "b c");
     assertTokens("\"a b\" c \"d e\"", "a b", "c", "d e");
+    assertTokens("\"-Dquote=\\\\\"", "-Dquote=\\\\");
   }
 
   public void testEscape() throws Exception {
@@ -43,6 +45,11 @@ public class CommandLineTokenizerTest extends TestCase {
     assertTokens("\\\"a b\\\"", "\"a", "b\"");
     assertTokens("\\\"a\\ b\\\"", true, "\"a b\"");
     assertTokens("\\\"a\\ b\\\"", false, "\"a\\", "b\"");
+
+    // Check tail \" as Java does (most compatible way to go: doubling the tail backslash), see comment in
+    // http://cr.openjdk.java.net/~uta/openjdk-webrevs/JDK-8016046/webrev.01/src/windows/classes/java/lang/ProcessImpl.java.frames.html
+    // line L188/R199
+    assertTokens("\"-lib=c:\\My Lib\\\\\" \"-include=c:\\My Include\\\\\"", true, "-lib=c:\\My Lib\\\\", "-include=c:\\My Include\\\\");
   }
 
   private static void assertTokens(String cmd, String... tokens) {