File tree Expand file tree Collapse file tree
clikt/src/commonMain/kotlin/com/github/ajalt/clikt/parsers
test/src/commonTest/kotlin/com/github/ajalt/clikt/parsers Expand file tree Collapse file tree Original file line number Diff line number Diff line change @@ -11,6 +11,7 @@ internal fun shlex(
1111): List <String > {
1212 val toks = mutableListOf<String >()
1313 var inQuote: Char? = null
14+ var inToken = false // Track if we're building a token (for empty quoted strings)
1415 val sb = StringBuilder ()
1516 var i = 0
1617 fun err (msg : String ): Nothing {
@@ -31,14 +32,15 @@ internal fun shlex(
3132 i + = 1
3233 } while (i <= text.lastIndex && text[i].isWhitespace())
3334 } else {
35+ inToken = true
3436 sb.append(text[i + 1 ])
3537 i + = 2
3638 }
3739 }
3840
3941 c == inQuote -> {
40- toks + = sb.toString()
41- sb.clear()
42+ // Don't emit here - just close the quote. Adjacent quoted/unquoted
43+ // strings should concatenate into a single token (POSIX behavior).
4244 inQuote = null
4345 i + = 1
4446 }
@@ -49,19 +51,22 @@ internal fun shlex(
4951 }
5052
5153 c in " \" '" && inQuote == null -> {
54+ inToken = true
5255 inQuote = c
5356 i + = 1
5457 }
5558
5659 c.isWhitespace() && inQuote == null -> {
57- if (sb.isNotEmpty() ) {
60+ if (inToken ) {
5861 toks + = sb.toString()
5962 sb.clear()
63+ inToken = false
6064 }
6165 i + = 1
6266 }
6367
6468 else -> {
69+ inToken = true
6570 sb.append(c)
6671 i + = 1
6772 }
@@ -72,7 +77,7 @@ internal fun shlex(
7277 err(localization.unclosedQuote())
7378 }
7479
75- if (sb.isNotEmpty() ) {
80+ if (inToken ) {
7681 toks + = sb.toString()
7782 }
7883
Original file line number Diff line number Diff line change @@ -180,6 +180,23 @@ class AtFileTest {
180180 C ().parse(" @file" )
181181 }
182182
183+ @[Test JsName (" adjacent_quoted_strings_concatenate" )]
184+ fun `adjacent quoted strings concatenate` () {
185+ // Verify that adjacent quoted/unquoted strings produce a single token (POSIX behavior)
186+ // e.g., "'"c"'" should produce 'c' (single-quote, c, single-quote)
187+ class C : TestCommand () {
188+ val arg by argument().multiple()
189+
190+ override fun run_ () {
191+ arg shouldBe listOf (" a" , " b" , " 'c'" )
192+ }
193+ }
194+
195+ C ().withAtFiles(
196+ " foo" to """ a b "'"c"'""""
197+ ).parse(" @foo" )
198+ }
199+
183200 @[Test JsName (" parsing_atfile_with_alias" )]
184201 fun `parsing atfile with alias` () {
185202 class C : TestCommand () {
You can’t perform that action at this time.
0 commit comments