From 8eee5b63330e896ff2f2d615aa41de2aa6b3db0c Mon Sep 17 00:00:00 2001 From: Alexey Utkin Date: Mon, 1 Dec 2014 19:38:59 +0300 Subject: [PATCH] OC-11191 Correct escaping support for compiler arguments and defines --- .../configurations/CommandLineTokenizer.java | 16 +++++++++++++--- .../intellij/openapi/util/text/StringUtil.java | 14 +++++++++++--- .../configurations/CommandLineTokenizerTest.java | 7 +++++++ 3 files changed, 31 insertions(+), 6 deletions(-) diff --git a/platform/util/src/com/intellij/execution/configurations/CommandLineTokenizer.java b/platform/util/src/com/intellij/execution/configurations/CommandLineTokenizer.java index 3f351b9a573e..925bbb838d71 100644 --- a/platform/util/src/com/intellij/execution/configurations/CommandLineTokenizer.java +++ b/platform/util/src/com/intellij/execution/configurations/CommandLineTokenizer.java @@ -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; + } } diff --git a/platform/util/src/com/intellij/openapi/util/text/StringUtil.java b/platform/util/src/com/intellij/openapi/util/text/StringUtil.java index 543b3bc2d73c..1eddfa14c710 100644 --- a/platform/util/src/com/intellij/openapi/util/text/StringUtil.java +++ b/platform/util/src/com/intellij/openapi/util/text/StringUtil.java @@ -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); diff --git a/platform/util/testSrc/com/intellij/execution/configurations/CommandLineTokenizerTest.java b/platform/util/testSrc/com/intellij/execution/configurations/CommandLineTokenizerTest.java index 276ca549eb30..98d56bb87dd6 100644 --- a/platform/util/testSrc/com/intellij/execution/configurations/CommandLineTokenizerTest.java +++ b/platform/util/testSrc/com/intellij/execution/configurations/CommandLineTokenizerTest.java @@ -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) {