From a22c8c8f2f21cd32910c1ea79ea8bf60b100e19f Mon Sep 17 00:00:00 2001 From: Ilya Muradyan Date: Fri, 22 Oct 2021 23:50:23 +0300 Subject: [PATCH] [scripting] Fix script resolver options parsing #KT-49400 Fixed --- ...ternalDependenciesResolverOptionsParser.kt | 20 ++++++++++++++++--- .../experimental/test/ResolverOptionsTest.kt | 16 +++++++++++++++ 2 files changed, 33 insertions(+), 3 deletions(-) diff --git a/libraries/scripting/dependencies/src/kotlin/script/experimental/dependencies/impl/SimpleExternalDependenciesResolverOptionsParser.kt b/libraries/scripting/dependencies/src/kotlin/script/experimental/dependencies/impl/SimpleExternalDependenciesResolverOptionsParser.kt index 994638e809c..2856f97c7b4 100644 --- a/libraries/scripting/dependencies/src/kotlin/script/experimental/dependencies/impl/SimpleExternalDependenciesResolverOptionsParser.kt +++ b/libraries/scripting/dependencies/src/kotlin/script/experimental/dependencies/impl/SimpleExternalDependenciesResolverOptionsParser.kt @@ -8,9 +8,23 @@ package kotlin.script.experimental.dependencies.impl import kotlin.script.experimental.api.* import kotlin.script.experimental.dependencies.ExternalDependenciesResolver -private val nameRegex = Regex("^[^\\S\\r\\n]*([a-zA-Z][a-zA-Z0-9-_]*)\\b") -private val valueRegex = Regex("^[^\\S\\r\\n]*([a-zA-Z0-9-_,]+)\\b") +private val nameRegex = Regex("^[^\\S\\r\\n]*([a-zA-Z][a-zA-Z0-9-_]*)[^\\S\\r\\n]?") +private val valueRegex = Regex("^[^\\S\\r\\n]*((([a-zA-Z0-9-_,/$.:])|(\\\\[\\\\ nt]))+)[^\\S\\r\\n]?") private val equalsRegex = Regex("^[^\\S\\r\\n]*=") +private val escapeRegex = Regex("\\\\(.)") + +private fun String.unescape(): String { + return replace(escapeRegex) { match -> + when (val c = match.groups[1]!!.value.single()) { + '\\' -> "\\" + ' ' -> " " + 'n' -> "\n" + 't' -> "\t" + // Impossible situation: all possible values are mentioned in the regex + else -> error("Unknown escaped symbol: $c") + } + } +} /** * Simple lightweight options parser for external dependency resolvers. @@ -42,7 +56,7 @@ object SimpleExternalDependenciesResolverOptionsParser { } fun takeName(): Token.Name? = take(nameRegex)?.let { Token.Name(it.groups[1]!!.value) } - fun takeValue(): Token.Value? = take(valueRegex)?.let { Token.Value(it.groups[1]!!.value) } + fun takeValue(): Token.Value? = take(valueRegex)?.let { Token.Value(it.groups[1]!!.value.unescape()) } fun takeEquals(): Token.Equals? = take(equalsRegex)?.let { Token.Equals } fun hasFinished(): Boolean = remaining.isBlank() diff --git a/libraries/scripting/dependencies/test/kotlin/script/experimental/test/ResolverOptionsTest.kt b/libraries/scripting/dependencies/test/kotlin/script/experimental/test/ResolverOptionsTest.kt index 9dd3ec3a953..a3d8e4c6570 100644 --- a/libraries/scripting/dependencies/test/kotlin/script/experimental/test/ResolverOptionsTest.kt +++ b/libraries/scripting/dependencies/test/kotlin/script/experimental/test/ResolverOptionsTest.kt @@ -65,6 +65,22 @@ class ResolverOptionsTest : TestCase() { assertEquals(options.flag("option3"), true) } + fun testParserAcceptsSpecialSymbols() { + val parser = SimpleExternalDependenciesResolverOptionsParser + val options = parser("option1=/User/path/file.kt option2=C:\\\\User\\\\file.pem option3=\$MY_ENV").valueOrThrow() + assertEquals(options.value("option1"), "/User/path/file.kt") + assertEquals(options.value("option2"), "C:\\User\\file.pem") + assertEquals(options.value("option3"), "\$MY_ENV") + } + + fun testParserAcceptsValuesWithSpaces() { + val parser = SimpleExternalDependenciesResolverOptionsParser + val options = parser("option1= spaced\\ \\ value\\ option2=\\ x option3=line1\\nline2").valueOrThrow() + assertEquals(options.value("option1"), "spaced value ") + assertEquals(options.value("option2"), " x") + assertEquals(options.value("option3"), "line1\nline2") + } + fun testParserReturnsMixOfValuesAndFlags() { val parser = SimpleExternalDependenciesResolverOptionsParser val options = parser("option1 = hello option2 option3=world option4 option5 = false").valueOrThrow()