Skip to content

Commit 2130068

Browse files
committed
Avoid throwing NullPointerException when calling CommandLineParser will
null array elements
1 parent 8f57b20 commit 2130068

6 files changed

Lines changed: 133 additions & 119 deletions

File tree

src/changes/changes.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
<body>
2525
<release version="1.7.1" date="YYYY-MM-DD" description="This release contains new features and bug fixes and requires Java 8 or above.">
2626
<action type="fix" issue="CLI-331" dev="ggregory" due-to="Claude Warren, Gary Gregory">Handle reporting of deprecated options when parameters are not String type. #270.</action>
27+
<action type="fix" issue="CLI-331" dev="ggregory" due-to="Claude Warren, Gary Gregory">Avoid throwing NullPointerException when calling CommandLineParser will null array elements.</action>
2728
</release>
2829
<release version="1.7.0" date="2024-04-13" description="This release contains new features and bug fixes and requires Java 8 or above.">
2930
<!-- FIX -->

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

Lines changed: 18 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -541,22 +541,24 @@ private void handleShortAndLongOption(final String hyphenToken) throws ParseExce
541541
* @throws ParseException
542542
*/
543543
private void handleToken(final String token) throws ParseException {
544-
currentToken = token;
545-
if (skipParsing) {
546-
cmd.addArg(token);
547-
} else if ("--".equals(token)) {
548-
skipParsing = true;
549-
} else if (currentOption != null && currentOption.acceptsArg() && isArgument(token)) {
550-
currentOption.processValue(stripLeadingAndTrailingQuotesDefaultOn(token));
551-
} else if (token.startsWith("--")) {
552-
handleLongOption(token);
553-
} else if (token.startsWith("-") && !"-".equals(token)) {
554-
handleShortAndLongOption(token);
555-
} else {
556-
handleUnknownToken(token);
557-
}
558-
if (currentOption != null && !currentOption.acceptsArg()) {
559-
currentOption = null;
544+
if (token != null) {
545+
currentToken = token;
546+
if (skipParsing) {
547+
cmd.addArg(token);
548+
} else if ("--".equals(token)) {
549+
skipParsing = true;
550+
} else if (currentOption != null && currentOption.acceptsArg() && isArgument(token)) {
551+
currentOption.processValue(stripLeadingAndTrailingQuotesDefaultOn(token));
552+
} else if (token.startsWith("--")) {
553+
handleLongOption(token);
554+
} else if (token.startsWith("-") && !"-".equals(token)) {
555+
handleShortAndLongOption(token);
556+
} else {
557+
handleUnknownToken(token);
558+
}
559+
if (currentOption != null && !currentOption.acceptsArg()) {
560+
currentOption = null;
561+
}
560562
}
561563
}
562564

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

Lines changed: 29 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -48,37 +48,39 @@ protected String[] flatten(final Options options, final String[] arguments, fina
4848
boolean eatTheRest = false;
4949
for (int i = 0; i < arguments.length; i++) {
5050
final String arg = arguments[i];
51-
if ("--".equals(arg)) {
52-
eatTheRest = true;
53-
tokens.add("--");
54-
} else if ("-".equals(arg)) {
55-
tokens.add("-");
56-
} else if (arg.startsWith("-")) {
57-
final String opt = Util.stripLeadingHyphens(arg);
58-
if (options.hasOption(opt)) {
59-
tokens.add(arg);
60-
} else {
61-
final int equalPos = DefaultParser.indexOfEqual(opt);
62-
if (equalPos != -1 && options.hasOption(opt.substring(0, equalPos))) {
63-
// the format is --foo=value or -foo=value
64-
tokens.add(arg.substring(0, arg.indexOf(Char.EQUAL))); // --foo
65-
tokens.add(arg.substring(arg.indexOf(Char.EQUAL) + 1)); // value
66-
} else if (options.hasOption(arg.substring(0, 2))) {
67-
// the format is a special properties option (-Dproperty=value)
68-
tokens.add(arg.substring(0, 2)); // -D
69-
tokens.add(arg.substring(2)); // property=value
70-
} else {
71-
eatTheRest = stopAtNonOption;
51+
if (arg != null) {
52+
if ("--".equals(arg)) {
53+
eatTheRest = true;
54+
tokens.add("--");
55+
} else if ("-".equals(arg)) {
56+
tokens.add("-");
57+
} else if (arg.startsWith("-")) {
58+
final String opt = Util.stripLeadingHyphens(arg);
59+
if (options.hasOption(opt)) {
7260
tokens.add(arg);
61+
} else {
62+
final int equalPos = DefaultParser.indexOfEqual(opt);
63+
if (equalPos != -1 && options.hasOption(opt.substring(0, equalPos))) {
64+
// the format is --foo=value or -foo=value
65+
tokens.add(arg.substring(0, arg.indexOf(Char.EQUAL))); // --foo
66+
tokens.add(arg.substring(arg.indexOf(Char.EQUAL) + 1)); // value
67+
} else if (options.hasOption(arg.substring(0, 2))) {
68+
// the format is a special properties option (-Dproperty=value)
69+
tokens.add(arg.substring(0, 2)); // -D
70+
tokens.add(arg.substring(2)); // property=value
71+
} else {
72+
eatTheRest = stopAtNonOption;
73+
tokens.add(arg);
74+
}
7375
}
76+
} else {
77+
tokens.add(arg);
7478
}
75-
} else {
76-
tokens.add(arg);
77-
}
7879

79-
if (eatTheRest) {
80-
for (i++; i < arguments.length; i++) { // NOPMD
81-
tokens.add(arguments[i]);
80+
if (eatTheRest) {
81+
for (i++; i < arguments.length; i++) { // NOPMD
82+
tokens.add(arguments[i]);
83+
}
8284
}
8385
}
8486
}

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

Lines changed: 30 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -156,41 +156,40 @@ public CommandLine parse(final Options options, final String[] arguments, final
156156
// process each flattened token
157157
while (iterator.hasNext()) {
158158
final String token = iterator.next();
159-
// the value is the double-dash
160-
if ("--".equals(token)) {
161-
eatTheRest = true;
162-
}
163-
// the value is a single dash
164-
else if ("-".equals(token)) {
165-
if (stopAtNonOption) {
159+
if (token != null) {
160+
// the value is the double-dash
161+
if ("--".equals(token)) {
166162
eatTheRest = true;
163+
} else if ("-".equals(token)) {
164+
// the value is a single dash
165+
if (stopAtNonOption) {
166+
eatTheRest = true;
167+
} else {
168+
cmd.addArg(token);
169+
}
170+
} else if (token.startsWith("-")) {
171+
// the value is an option
172+
if (stopAtNonOption && !getOptions().hasOption(token)) {
173+
eatTheRest = true;
174+
cmd.addArg(token);
175+
} else {
176+
processOption(token, iterator);
177+
}
167178
} else {
179+
// the value is an argument
168180
cmd.addArg(token);
181+
if (stopAtNonOption) {
182+
eatTheRest = true;
183+
}
169184
}
170-
}
171-
// the value is an option
172-
else if (token.startsWith("-")) {
173-
if (stopAtNonOption && !getOptions().hasOption(token)) {
174-
eatTheRest = true;
175-
cmd.addArg(token);
176-
} else {
177-
processOption(token, iterator);
178-
}
179-
}
180-
// the value is an argument
181-
else {
182-
cmd.addArg(token);
183-
if (stopAtNonOption) {
184-
eatTheRest = true;
185-
}
186-
}
187-
// eat the remaining tokens
188-
if (eatTheRest) {
189-
while (iterator.hasNext()) {
190-
final String str = iterator.next();
191-
// ensure only one double-dash is added
192-
if (!"--".equals(str)) {
193-
cmd.addArg(str);
185+
// eat the remaining tokens
186+
if (eatTheRest) {
187+
while (iterator.hasNext()) {
188+
final String str = iterator.next();
189+
// ensure only one double-dash is added
190+
if (!"--".equals(str)) {
191+
cmd.addArg(str);
192+
}
194193
}
195194
}
196195
}

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

Lines changed: 38 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -119,63 +119,56 @@ protected void burstToken(final String token, final boolean stopAtNonOption) {
119119
protected String[] flatten(final Options options, final String[] arguments, final boolean stopAtNonOption) throws ParseException {
120120
init();
121121
this.options = options;
122-
123122
// an iterator for the command line tokens
124123
final Iterator<String> iter = Arrays.asList(arguments).iterator();
125-
126124
// process each command line token
127125
while (iter.hasNext()) {
128126
// get the next command line token
129127
final String token = iter.next();
130-
131-
// single or double hyphen
132-
if ("-".equals(token) || "--".equals(token)) {
133-
tokens.add(token);
134-
}
135-
136-
// handle long option --foo or --foo=bar
137-
else if (token.startsWith("--")) {
138-
final int pos = DefaultParser.indexOfEqual(token);
139-
final String opt = pos == -1 ? token : token.substring(0, pos); // --foo
140-
141-
final List<String> matchingOpts = options.getMatchingOptions(opt);
142-
143-
if (matchingOpts.isEmpty()) {
144-
processNonOptionToken(token, stopAtNonOption);
145-
} else if (matchingOpts.size() > 1) {
146-
throw new AmbiguousOptionException(opt, matchingOpts);
147-
} else {
148-
currentOption = options.getOption(matchingOpts.get(0));
149-
150-
tokens.add("--" + currentOption.getLongOpt());
151-
if (pos != -1) {
152-
tokens.add(token.substring(pos + 1));
128+
if (token != null) {
129+
// single or double hyphen
130+
if ("-".equals(token) || "--".equals(token)) {
131+
tokens.add(token);
132+
} else if (token.startsWith("--")) {
133+
// handle long option --foo or --foo=bar
134+
final int pos = DefaultParser.indexOfEqual(token);
135+
final String opt = pos == -1 ? token : token.substring(0, pos); // --foo
136+
137+
final List<String> matchingOpts = options.getMatchingOptions(opt);
138+
139+
if (matchingOpts.isEmpty()) {
140+
processNonOptionToken(token, stopAtNonOption);
141+
} else if (matchingOpts.size() > 1) {
142+
throw new AmbiguousOptionException(opt, matchingOpts);
143+
} else {
144+
currentOption = options.getOption(matchingOpts.get(0));
145+
146+
tokens.add("--" + currentOption.getLongOpt());
147+
if (pos != -1) {
148+
tokens.add(token.substring(pos + 1));
149+
}
153150
}
154-
}
155-
}
156-
157-
else if (token.startsWith("-")) {
158-
if (token.length() == 2 || options.hasOption(token)) {
159-
processOptionToken(token, stopAtNonOption);
160-
} else if (!options.getMatchingOptions(token).isEmpty()) {
161-
final List<String> matchingOpts = options.getMatchingOptions(token);
162-
if (matchingOpts.size() > 1) {
163-
throw new AmbiguousOptionException(token, matchingOpts);
151+
} else if (token.startsWith("-")) {
152+
if (token.length() == 2 || options.hasOption(token)) {
153+
processOptionToken(token, stopAtNonOption);
154+
} else if (!options.getMatchingOptions(token).isEmpty()) {
155+
final List<String> matchingOpts = options.getMatchingOptions(token);
156+
if (matchingOpts.size() > 1) {
157+
throw new AmbiguousOptionException(token, matchingOpts);
158+
}
159+
final Option opt = options.getOption(matchingOpts.get(0));
160+
processOptionToken("-" + opt.getLongOpt(), stopAtNonOption);
164161
}
165-
final Option opt = options.getOption(matchingOpts.get(0));
166-
processOptionToken("-" + opt.getLongOpt(), stopAtNonOption);
167-
}
168-
// requires bursting
169-
else {
170-
burstToken(token, stopAtNonOption);
162+
// requires bursting
163+
else {
164+
burstToken(token, stopAtNonOption);
165+
}
166+
} else {
167+
processNonOptionToken(token, stopAtNonOption);
171168
}
172-
} else {
173-
processNonOptionToken(token, stopAtNonOption);
174169
}
175-
176170
gobble(iter);
177171
}
178-
179172
return tokens.toArray(Util.EMPTY_STRING_ARRAY);
180173
}
181174

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

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -420,6 +420,23 @@ public void testMultiple() throws Exception {
420420
assertEquals("foobar", cl.getArgList().get(0), "Confirm value of extra arg: " + cl.getArgList().get(0));
421421
}
422422

423+
@Test
424+
public void testMultipleWithNull() throws Exception {
425+
final String[] args = { null, "-c", null, "foobar", null, "-b", null, "toast", null };
426+
427+
CommandLine cl = parser.parse(options, args, true);
428+
assertTrue(cl.hasOption("c"), "Confirm -c is set");
429+
assertEquals(3, cl.getArgList().size(), "Confirm 3 extra args: " + cl.getArgList().size());
430+
431+
cl = parser.parse(options, cl.getArgs());
432+
433+
assertFalse(cl.hasOption("c"), "Confirm -c is not set");
434+
assertTrue(cl.hasOption("b"), "Confirm -b is set");
435+
assertEquals("toast", cl.getOptionValue("b"), "Confirm arg of -b");
436+
assertEquals(1, cl.getArgList().size(), "Confirm 1 extra arg: " + cl.getArgList().size());
437+
assertEquals("foobar", cl.getArgList().get(0), "Confirm value of extra arg: " + cl.getArgList().get(0));
438+
}
439+
423440
@Test
424441
public void testMultipleWithLong() throws Exception {
425442
final String[] args = { "--copt", "foobar", "--bfile", "toast" };

0 commit comments

Comments
 (0)