From 720019b5080e2d14a6c11ef8fbd13b10f5813a9d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexander=20S=C3=B6derberg?= Date: Sat, 17 Oct 2020 23:05:28 +0200 Subject: [PATCH] :bug: Fixed quoted parsing in StringArgument --- CHANGELOG.md | 3 + .../arguments/standard/StringArgument.java | 62 ++++++++++++------- .../standard/StringArgumentTest.java | 9 +++ 3 files changed, 51 insertions(+), 23 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d71fe7b2..9556d057 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Fixes - Fixes chained optionals not allowing the command to be executed when more than one optional is omitted +### Fixed + - Fixed quoted parsing in StringArgument + ## [1.0.1] - 2020-10-14 ### Changes diff --git a/cloud-core/src/main/java/cloud/commandframework/arguments/standard/StringArgument.java b/cloud-core/src/main/java/cloud/commandframework/arguments/standard/StringArgument.java index c98b07e8..34d8c4e1 100644 --- a/cloud-core/src/main/java/cloud/commandframework/arguments/standard/StringArgument.java +++ b/cloud-core/src/main/java/cloud/commandframework/arguments/standard/StringArgument.java @@ -37,10 +37,15 @@ import java.util.List; import java.util.Queue; import java.util.StringJoiner; import java.util.function.BiFunction; +import java.util.regex.Matcher; +import java.util.regex.Pattern; @SuppressWarnings("unused") public final class StringArgument extends CommandArgument { + private static final Pattern QUOTED_DOUBLE = Pattern.compile("\"(?(?:[^\"\\\\]|\\\\.)*)\""); + private static final Pattern QUOTED_SINGLE = Pattern.compile("'(?(?:[^'\\\\]|\\\\.)*)'"); + private final StringMode stringMode; private StringArgument( @@ -305,6 +310,40 @@ public final class StringArgument extends CommandArgument { } inputQueue.remove(); return ArgumentParseResult.success(input); + } else if (this.stringMode == StringMode.QUOTED) { + final StringJoiner sj = new StringJoiner(" "); + for (final String string : inputQueue) { + sj.add(string); + } + final String string = sj.toString(); + + Matcher matcher = QUOTED_DOUBLE.matcher(string); + String inner = null; + if (matcher.find()) { + inner = matcher.group("inner"); + } else { + matcher = QUOTED_SINGLE.matcher(string); + if (matcher.find()) { + inner = matcher.group("inner"); + } + } + + if (inner != null) { + final int numSpaces = (int) inner.chars().filter(c -> c == ' ').count(); + for (int i = 0; i <= numSpaces; i++) { + inputQueue.remove(); + } + } else { + inner = inputQueue.remove(); + if (inner.startsWith("\"") || inner.startsWith("'")) { + return ArgumentParseResult.failure(new StringParseException(sj.toString(), + StringMode.QUOTED, commandContext)); + } + } + + inner = inner.replace("\\\"", "\"").replace("\\\'", "\""); + + return ArgumentParseResult.success(inner); } final StringJoiner sj = new StringJoiner(" "); @@ -321,33 +360,10 @@ public final class StringArgument extends CommandArgument { break; } - if (this.stringMode == StringMode.QUOTED) { - if (!started) { - if (string.startsWith("\"") || string.startsWith("'")) { - start = string.charAt(0); - string = string.substring(1); - started = true; - } else { - /* Just read a single string instead */ - inputQueue.remove(); - return ArgumentParseResult.success(string); - } - } else if (string.endsWith(Character.toString(start))) { - sj.add(string.substring(0, string.length() - 1)); - inputQueue.remove(); - finished = true; - break; - } - } - sj.add(string); inputQueue.remove(); } - if (this.stringMode == StringMode.QUOTED && !finished) { - return ArgumentParseResult.failure(new StringParseException(sj.toString(), StringMode.QUOTED, commandContext)); - } - return ArgumentParseResult.success(sj.toString()); } diff --git a/cloud-core/src/test/java/cloud/commandframework/arguments/standard/StringArgumentTest.java b/cloud-core/src/test/java/cloud/commandframework/arguments/standard/StringArgumentTest.java index 3c5afd93..1195555a 100644 --- a/cloud-core/src/test/java/cloud/commandframework/arguments/standard/StringArgumentTest.java +++ b/cloud-core/src/test/java/cloud/commandframework/arguments/standard/StringArgumentTest.java @@ -30,6 +30,8 @@ import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; +import java.util.concurrent.CompletionException; + class StringArgumentTest { private static final String[] storage = new String[2]; @@ -85,6 +87,13 @@ class StringArgumentTest { manager.executeCommand(new TestCommandSender(), "quoted quoted unquoted"); Assertions.assertEquals("quoted", storage[0]); Assertions.assertEquals("unquoted", storage[1]); + clear(); + manager.executeCommand(new TestCommandSender(), "quoted \"quoted \\\" string\" unquoted").join(); + Assertions.assertEquals("quoted \" string", storage[0]); + Assertions.assertEquals("unquoted", storage[1]); + clear(); + Assertions.assertThrows(CompletionException.class, () -> manager.executeCommand(new TestCommandSender(), + "'quoted quoted unquoted").join()); } @Test