Skip to content

Commit 14e7d40

Browse files
committed
CLI-254: "test" gets parsed as test, quotes die :-(
add a new constructor parameter to DefaultParser that disables quote processing on option arguments
1 parent e093df2 commit 14e7d40

2 files changed

Lines changed: 115 additions & 4 deletions

File tree

src/main/java/org/apache/commons/cli/DefaultParser.java

Lines changed: 57 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -57,8 +57,12 @@ public class DefaultParser implements CommandLineParser
5757
/** Flag indicating if partial matching of long options is supported. */
5858
private final boolean allowPartialMatching;
5959

60+
/** Flag indicating if balanced leading and trailing double quotes should be stripped from option arguments. */
61+
private final boolean stripLeadingAndTrailingQuotes;
62+
6063
/**
61-
* Creates a new DefaultParser instance with partial matching enabled.
64+
* Creates a new DefaultParser instance with partial matching and
65+
* stripping of balanced leading and trailing double quotes from option arguments enabled.
6266
*
6367
* By "partial matching" we mean that given the following code:
6468
* <pre>
@@ -73,13 +77,19 @@ public class DefaultParser implements CommandLineParser
7377
* <code>"debug"</code> option. However, with "partial matching" disabled,
7478
* <code>-de</code> would enable both <code>debug</code> as well as
7579
* <code>extract</code> options.
80+
* with "stripping of balanced leading and trailing double quotes from option arguments" turned
81+
* on, the outermost balanced double quotes of option arguments values will be removed.
82+
* ie.
83+
* for <code>-o '"x"'</code> getValue() will return <code>x</code>, instead of <code>"x"</code>
7684
*/
7785
public DefaultParser() {
7886
this.allowPartialMatching = true;
87+
this.stripLeadingAndTrailingQuotes = true;
7988
}
8089

8190
/**
82-
* Create a new DefaultParser instance with the specified partial matching policy.
91+
* Create a new DefaultParser instance with the specified partial matching policy and
92+
* stripping of balanced leading and trailing double quotes from option arguments enabled.
8393
*
8494
* By "partial matching" we mean that given the following code:
8595
* <pre>
@@ -94,13 +104,48 @@ public DefaultParser() {
94104
* <code>"debug"</code> option. However, with "partial matching" disabled,
95105
* <code>-de</code> would enable both <code>debug</code> as well as
96106
* <code>extract</code> options.
97-
*
107+
* with "stripping of balanced leading and trailing double quotes from option arguments" turned
108+
* on, the outermost balanced double quotes of option arguments values will be removed.
109+
* ie.
110+
* for <code>-o '"x"'</code> getValue() will return <code>x</code>, instead of <code>"x"</code>
98111
* @param allowPartialMatching if partial matching of long options shall be enabled
99112
*/
100113
public DefaultParser(final boolean allowPartialMatching) {
101114
this.allowPartialMatching = allowPartialMatching;
115+
this.stripLeadingAndTrailingQuotes = true;
116+
}
117+
118+
/**
119+
* Create a new DefaultParser instance with the specified partial matching and quote
120+
* stripping policy.
121+
*
122+
* By "partial matching" we mean that given the following code:
123+
* <pre>
124+
* {@code
125+
* final Options options = new Options();
126+
* options.addOption(new Option("d", "debug", false, "Turn on debug."));
127+
* options.addOption(new Option("e", "extract", false, "Turn on extract."));
128+
* options.addOption(new Option("o", "option", true, "Turn on option with argument."));
129+
* }
130+
* </pre>
131+
* with "partial matching" turned on, <code>-de</code> only matches the
132+
* <code>"debug"</code> option. However, with "partial matching" disabled,
133+
* <code>-de</code> would enable both <code>debug</code> as well as
134+
* <code>extract</code> options.
135+
* with "stripping of balanced leading and trailing double quotes from option arguments" turned
136+
* on, the outermost balanced double quotes of option arguments values will be removed.
137+
* ie.
138+
* for <code>-o '"x"'</code> getValue() will return <code>x</code>, instead of <code>"x"</code>
139+
* @param allowPartialMatching if partial matching of long options shall be enabled
140+
* @param stripLeadingAndTrailingQuotes if balanced outer double quoutes should be stripped
141+
*/
142+
public DefaultParser(final boolean allowPartialMatching,
143+
final boolean stripLeadingAndTrailingQuotes) {
144+
this.allowPartialMatching = allowPartialMatching;
145+
this.stripLeadingAndTrailingQuotes = stripLeadingAndTrailingQuotes;
102146
}
103147

148+
104149
@Override
105150
public CommandLine parse(final Options options, final String[] arguments) throws ParseException
106151
{
@@ -280,7 +325,7 @@ else if ("--".equals(token))
280325
}
281326
else if (currentOption != null && currentOption.acceptsArg() && isArgument(token))
282327
{
283-
currentOption.addValueForProcessing(Util.stripLeadingAndTrailingQuotes(token));
328+
currentOption.addValueForProcessing(conditionallyStripLeadingAndTrailingQuotes(token));
284329
}
285330
else if (token.startsWith("--"))
286331
{
@@ -777,4 +822,12 @@ protected void handleConcatenatedOptions(final String token) throws ParseExcepti
777822
}
778823
}
779824
}
825+
826+
protected String conditionallyStripLeadingAndTrailingQuotes(final String token) {
827+
if(stripLeadingAndTrailingQuotes) {
828+
return Util.stripLeadingAndTrailingQuotes(token);
829+
} else {
830+
return token;
831+
}
832+
}
780833
}

src/test/java/org/apache/commons/cli/ParserTestCase.java

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ Licensed to the Apache Software Foundation (ASF) under one or more
2222
import static org.junit.Assert.assertNotNull;
2323
import static org.junit.Assert.assertTrue;
2424
import static org.junit.Assert.fail;
25+
import static org.junit.Assume.assumeTrue;
2526

2627
import java.util.Arrays;
2728
import java.util.List;
@@ -1105,4 +1106,61 @@ public void testPropertyOptionGroup() throws Exception
11051106
assertTrue(cmd.hasOption("x"));
11061107
assertFalse(cmd.hasOption("y"));
11071108
}
1109+
1110+
@Test
1111+
public void testLongOptionWithEqualsQuoteHandling() throws Exception
1112+
{
1113+
assumeTrue(parser instanceof DefaultParser );
1114+
final String[] args = new String[] { "--bfile=\"quoted string\""};
1115+
1116+
final CommandLine cl = parser.parse(options, args);
1117+
1118+
assertEquals("Confirm --bfile=arg keeps quotes", "\"quoted string\"", cl.getOptionValue("bfile"));
1119+
}
1120+
1121+
@Test
1122+
public void testShortOptionQuoteHandlingWithDefault() throws Exception
1123+
{
1124+
assumeTrue(parser instanceof DefaultParser );
1125+
final String[] args = new String[] { "-b", "\"quoted string\""};
1126+
1127+
final CommandLine cl = parser.parse(options, args);
1128+
1129+
assertEquals("Confirm -b strips quotes", "quoted string", cl.getOptionValue("b"));
1130+
}
1131+
1132+
@Test
1133+
public void testShortOptionQuoteHandlingWithNoStrip() throws Exception
1134+
{
1135+
assumeTrue(parser instanceof DefaultParser );
1136+
parser = new DefaultParser(true, false);
1137+
final String[] args = new String[] { "-b", "\"quoted string\""};
1138+
1139+
final CommandLine cl = parser.parse(options, args);
1140+
1141+
assertEquals("Confirm -b keeps quotes", "\"quoted string\"", cl.getOptionValue("b"));
1142+
}
1143+
1144+
@Test
1145+
public void testLongOptionQuoteHandlingWithDefault() throws Exception
1146+
{
1147+
assumeTrue(parser instanceof DefaultParser );
1148+
final String[] args = new String[] { "--bfile", "\"quoted string\""};
1149+
1150+
final CommandLine cl = parser.parse(options, args);
1151+
1152+
assertEquals("Confirm --bfile arg strips quotes", "quoted string", cl.getOptionValue("b"));
1153+
}
1154+
1155+
@Test
1156+
public void testLongOptionQuoteHandlingWithNoStrip() throws Exception
1157+
{
1158+
assumeTrue(parser instanceof DefaultParser );
1159+
parser = new DefaultParser(true, false);
1160+
final String[] args = new String[] { "--bfile", "\"quoted string\""};
1161+
1162+
final CommandLine cl = parser.parse(options, args);
1163+
1164+
assertEquals("Confirm --bfile arg keeps quotes", "\"quoted string\"", cl.getOptionValue("b"));
1165+
}
11081166
}

0 commit comments

Comments
 (0)