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('"');
buffer.append(nextToken);
}
- if ((isEscapedWhitespace || quotationMarks % 2 == 1) && super.hasMoreTokens()) {
+ if ((isEscapedWhitespace || (quotationMarks & 1) == 1) && super.hasMoreTokens()) {
nextToken = super.nextToken();
} else {
nextToken = null;
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;
+ }
}
@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++) {
if (ch == '\\' && i != last) {
i++;
ch = str.charAt(i);
- if (ch != '/') buf.append('\\');
+ if (ch != unescapeChar) buf.append('\\');
}
buf.append(ch);
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 {
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) {