SHL-139: Handle escape sequences in option values

This commit is contained in:
Eric Bottard
2014-04-18 15:12:00 +02:00
committed by mpollack
parent fb082a5224
commit 714b143bda
2 changed files with 79 additions and 1 deletions

View File

@@ -152,6 +152,10 @@ public class Tokenizer {
lastValueDelimiter = endDelimiter;
lastValueStartOffset = pos;
while (pos < buffer.length && buffer[pos] != endDelimiter) {
if (buffer[pos] == ESCAPE_CHAR) {
sb.append(processCharacterEscapeCodes(endDelimiter));
continue;
}
if (lookAhead(ESCAPE_CHAR, endDelimiter)) {
sb.append(endDelimiter);
pos += 2;
@@ -193,6 +197,59 @@ public class Tokenizer {
return sb.toString();
}
/**
* When the escape character is encountered, consume and return the escaped sequence. Note that depending on which
* end delimiter is currently in use, not all combinations need to be escaped
* @param endDelimiter the current endDelimiter
*/
private char processCharacterEscapeCodes(char endDelimiter) {
pos++;
if (pos >= buffer.length) {
throw new IllegalArgumentException("Ran out of input in escape sequence");
}
switch (buffer[pos]) {
case ESCAPE_CHAR:
pos++; // consume the second escape char
return ESCAPE_CHAR;
case 't':
pos++;
return '\t';
case 'r':
pos++;
return '\r';
case 'n':
pos++;
return '\n';
case 'f':
pos++;
return '\f';
case 'u':
if (pos + 5 > buffer.length) {
throw new IllegalArgumentException("Ran out input in unicode escape sequence");
}
String hex = new String(buffer, pos + 1, 4);
try {
char code = (char) Integer.parseInt(hex, 16);
pos += 5;
return code;
}
catch (NumberFormatException e) {
throw new IllegalArgumentException("Illegal unicode escape sequence: " + ESCAPE_CHAR + "u" + hex);
}
default:
if (buffer[pos] == endDelimiter) {
pos++;
return endDelimiter;
}
else {
// Not an actual escape. Do not increment pos,
// and return the \ we consumed at the very beginning
return ESCAPE_CHAR;
}
}
}
/**
* Return the offset at which the last value seen started (NOT including any delimiter).
*/
@@ -229,7 +286,12 @@ public class Tokenizer {
* Apply delimiter escaping to the given string, using the actual delimiter that was used for the last value.
*/
public String escape(String value) {
return value.replace("" + lastValueDelimiter, "" + ESCAPE_CHAR + lastValueDelimiter);
String result = value.replace("" + lastValueDelimiter, "" + ESCAPE_CHAR + lastValueDelimiter);
result = result.replace("\r", "\\r");
result = result.replace("\n", "\\n");
result = result.replace("\t", "\\t");
result = result.replace("\f", "\\f");
return result;
}
/**

View File

@@ -24,6 +24,7 @@ import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
@@ -198,6 +199,21 @@ public class TokenizerTests {
}
@Test
public void testEscapeSequences() {
Map<String, String> result = tokenize("--foo \\t\\n\\r\\f");
assertEquals(Collections.singletonMap("foo", "\t\n\r\f"), result);
// Unicode and space-escape
result = tokenize("--foo \\u65e5\\ \\u672c");
assertEquals(Collections.singletonMap("foo", "\u65e5 \u672c"), result);
// backslash escape
result = tokenize("--foo \\\\u\\g");
assertEquals(Collections.singletonMap("foo", "\\u\\g"), result);
}
private Map<String, String> tokenize(String what) {
return new Tokenizer(what).getTokens();
}