From 7436262361a75c268d6c49dd4b142fc56186814d Mon Sep 17 00:00:00 2001 From: Md Tanvir Alam Date: Thu, 27 Aug 2026 12:50:23 +0000 Subject: [PATCH] [CLI-300] Parse -opt=value for multi-character short options DefaultParser.handleShortAndLongOption only treated the left-hand side of an equals token as a short option when it was a single character, so a multi-character short option such as Option.builder("foo").hasArg() failed on "-foo=bar" with UnrecognizedOptionException. Match short options with Options.hasShortOption instead, which already works for the non-equals path. --- .../org/apache/commons/cli/DefaultParser.java | 6 +++-- .../apache/commons/cli/DefaultParserTest.java | 24 +++++++++++++++++++ 2 files changed, 28 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/apache/commons/cli/DefaultParser.java b/src/main/java/org/apache/commons/cli/DefaultParser.java index 4dd7dfec2..21708f92c 100644 --- a/src/main/java/org/apache/commons/cli/DefaultParser.java +++ b/src/main/java/org/apache/commons/cli/DefaultParser.java @@ -568,8 +568,10 @@ private void handleShortAndLongOption(final String hyphenToken) throws ParseExce // equal sign found (-xxx=yyy) final String opt = token.substring(0, pos); final String value = token.substring(pos + 1); - if (opt.length() == 1) { - // -S=V + // Prefer an exact short-option match (including multi-character short opts; CLI-300) + // before falling through to Java-property or long-option handling. + if (options.hasShortOption(opt)) { + // -S=V or -short=V final Option option = options.getOption(opt); if (option != null && option.acceptsArg()) { handleOption(option); diff --git a/src/test/java/org/apache/commons/cli/DefaultParserTest.java b/src/test/java/org/apache/commons/cli/DefaultParserTest.java index c162323f0..3eca7d928 100644 --- a/src/test/java/org/apache/commons/cli/DefaultParserTest.java +++ b/src/test/java/org/apache/commons/cli/DefaultParserTest.java @@ -400,6 +400,30 @@ void testParseSkipNonHappyPath() throws ParseException { assertTrue(e.getMessage().contains("-d")); } + /** + * CLI-300: multi-character short options must accept {@code -opt=value} the same way + * single-character short options do. + */ + @Test + void testMultiCharShortOptionWithEqual() throws Exception { + final Options options = new Options(); + options.addOption(Option.builder("foo").hasArg().build()); + final CommandLine cmdLine = parser.parse(options, new String[] {"-foo=bar"}); + assertEquals("bar", cmdLine.getOptionValue("foo")); + } + + /** + * CLI-300: multi-character short option without an equals sign already worked via + * {@code hasShortOption}; keep a paired regression for the non-equals form. + */ + @Test + void testMultiCharShortOptionWithoutEqual() throws Exception { + final Options options = new Options(); + options.addOption(Option.builder("foo").hasArg().build()); + final CommandLine cmdLine = parser.parse(options, new String[] {"-foo", "bar"}); + assertEquals("bar", cmdLine.getOptionValue("foo")); + } + @Override @Test @Disabled("Test case handled in the parameterized tests as \"DEFAULT behavior\"")