This is an automated email from the ASF dual-hosted git repository.

gnodet pushed a commit to branch fix/maven-args-parsing
in repository https://gitbox.apache.org/repos/asf/maven.git

commit f3a7fb92113beed72312e6c5f15655ef778fddd6
Author: Guillaume Nodet <[email protected]>
AuthorDate: Mon Mar 23 13:06:47 2026 +0100

    [MNG-8571] Fix MAVEN_ARGS parsing to handle quoted strings with spaces
    
    Replace naive String.split(" ") with a proper argument parser that
    handles single and double quoted strings, preserving backslashes
    for Windows paths. This fixes MAVEN_ARGS values like:
      -f "C:\Program Files\project\pom.xml"
    
    Applied to both EmbeddedMavenExecutor and ForkedMavenExecutor.
    
    Co-Authored-By: Claude Opus 4.6 <[email protected]>
---
 .../executor/embedded/EmbeddedMavenExecutor.java   | 50 ++++++++++++++++++++--
 .../cling/executor/forked/ForkedMavenExecutor.java | 48 +++++++++++++++++++--
 .../embedded/EmbeddedMavenExecutorTest.java        | 35 +++++++++++++++
 .../executor/forked/ForkedMavenExecutorTest.java   | 34 +++++++++++++++
 4 files changed, 159 insertions(+), 8 deletions(-)

diff --git 
a/impl/maven-executor/src/main/java/org/apache/maven/cling/executor/embedded/EmbeddedMavenExecutor.java
 
b/impl/maven-executor/src/main/java/org/apache/maven/cling/executor/embedded/EmbeddedMavenExecutor.java
index e5eda275d5..8986bb371b 100644
--- 
a/impl/maven-executor/src/main/java/org/apache/maven/cling/executor/embedded/EmbeddedMavenExecutor.java
+++ 
b/impl/maven-executor/src/main/java/org/apache/maven/cling/executor/embedded/EmbeddedMavenExecutor.java
@@ -31,7 +31,6 @@
 import java.nio.file.Files;
 import java.nio.file.Path;
 import java.util.ArrayList;
-import java.util.Arrays;
 import java.util.Collection;
 import java.util.Collections;
 import java.util.HashMap;
@@ -225,9 +224,9 @@ protected Context doCreate(Path mavenHome, ExecutorRequest 
executorRequest) {
         ArrayList<String> mavenArgs = new ArrayList<>();
         String mavenArgsEnv = System.getenv("MAVEN_ARGS");
         if (useMavenArgsEnv && mavenArgsEnv != null && 
!mavenArgsEnv.isEmpty()) {
-            Arrays.stream(mavenArgsEnv.split(" "))
-                    .filter(s -> !s.trim().isEmpty())
-                    .forEach(s -> mavenArgs.add(0, s));
+            List<String> parsed = parseArguments(mavenArgsEnv);
+            Collections.reverse(parsed);
+            mavenArgs.addAll(parsed);
         }
 
         Properties properties = prepareProperties(executorRequest);
@@ -441,4 +440,47 @@ protected String getMavenVersion(Class<?> clazz) throws 
IOException {
             return UNKNOWN_VERSION;
         }
     }
+
+    /**
+     * Parses a string of arguments respecting quoted strings.
+     * Handles both single and double quotes, and preserves backslashes
+     * (important for Windows paths).
+     */
+    static List<String> parseArguments(String args) {
+        List<String> result = new ArrayList<>();
+        StringBuilder current = new StringBuilder();
+        boolean inDoubleQuotes = false;
+        boolean inSingleQuotes = false;
+        for (int i = 0; i < args.length(); i++) {
+            char c = args.charAt(i);
+            if (inDoubleQuotes) {
+                if (c == '"') {
+                    inDoubleQuotes = false;
+                } else {
+                    current.append(c);
+                }
+            } else if (inSingleQuotes) {
+                if (c == '\'') {
+                    inSingleQuotes = false;
+                } else {
+                    current.append(c);
+                }
+            } else if (c == '"') {
+                inDoubleQuotes = true;
+            } else if (c == '\'') {
+                inSingleQuotes = true;
+            } else if (Character.isWhitespace(c)) {
+                if (!current.isEmpty()) {
+                    result.add(current.toString());
+                    current.setLength(0);
+                }
+            } else {
+                current.append(c);
+            }
+        }
+        if (!current.isEmpty()) {
+            result.add(current.toString());
+        }
+        return result;
+    }
 }
diff --git 
a/impl/maven-executor/src/main/java/org/apache/maven/cling/executor/forked/ForkedMavenExecutor.java
 
b/impl/maven-executor/src/main/java/org/apache/maven/cling/executor/forked/ForkedMavenExecutor.java
index a559a24baf..4bea89c720 100644
--- 
a/impl/maven-executor/src/main/java/org/apache/maven/cling/executor/forked/ForkedMavenExecutor.java
+++ 
b/impl/maven-executor/src/main/java/org/apache/maven/cling/executor/forked/ForkedMavenExecutor.java
@@ -27,7 +27,6 @@
 import java.nio.file.Path;
 import java.nio.file.Paths;
 import java.util.ArrayList;
-import java.util.Arrays;
 import java.util.HashMap;
 import java.util.List;
 import java.util.concurrent.CountDownLatch;
@@ -116,9 +115,7 @@ protected int doExecute(ExecutorRequest executorRequest) 
throws ExecutorExceptio
 
         String mavenArgsEnv = System.getenv("MAVEN_ARGS");
         if (useMavenArgsEnv && mavenArgsEnv != null && 
!mavenArgsEnv.isEmpty()) {
-            Arrays.stream(mavenArgsEnv.split(" "))
-                    .filter(s -> !s.trim().isEmpty())
-                    .forEach(cmdAndArguments::add);
+            cmdAndArguments.addAll(parseArguments(mavenArgsEnv));
         }
 
         cmdAndArguments.addAll(executorRequest.arguments());
@@ -223,4 +220,47 @@ public void close() throws ExecutorException {
             // nothing yet
         }
     }
+
+    /**
+     * Parses a string of arguments respecting quoted strings.
+     * Handles both single and double quotes, and preserves backslashes
+     * (important for Windows paths).
+     */
+    static List<String> parseArguments(String args) {
+        List<String> result = new ArrayList<>();
+        StringBuilder current = new StringBuilder();
+        boolean inDoubleQuotes = false;
+        boolean inSingleQuotes = false;
+        for (int i = 0; i < args.length(); i++) {
+            char c = args.charAt(i);
+            if (inDoubleQuotes) {
+                if (c == '"') {
+                    inDoubleQuotes = false;
+                } else {
+                    current.append(c);
+                }
+            } else if (inSingleQuotes) {
+                if (c == '\'') {
+                    inSingleQuotes = false;
+                } else {
+                    current.append(c);
+                }
+            } else if (c == '"') {
+                inDoubleQuotes = true;
+            } else if (c == '\'') {
+                inSingleQuotes = true;
+            } else if (Character.isWhitespace(c)) {
+                if (!current.isEmpty()) {
+                    result.add(current.toString());
+                    current.setLength(0);
+                }
+            } else {
+                current.append(c);
+            }
+        }
+        if (!current.isEmpty()) {
+            result.add(current.toString());
+        }
+        return result;
+    }
 }
diff --git 
a/impl/maven-executor/src/test/java/org/apache/maven/cling/executor/embedded/EmbeddedMavenExecutorTest.java
 
b/impl/maven-executor/src/test/java/org/apache/maven/cling/executor/embedded/EmbeddedMavenExecutorTest.java
index c214fc6ffe..45beda77a9 100644
--- 
a/impl/maven-executor/src/test/java/org/apache/maven/cling/executor/embedded/EmbeddedMavenExecutorTest.java
+++ 
b/impl/maven-executor/src/test/java/org/apache/maven/cling/executor/embedded/EmbeddedMavenExecutorTest.java
@@ -18,8 +18,13 @@
  */
 package org.apache.maven.cling.executor.embedded;
 
+import java.util.List;
+
 import org.apache.maven.api.cli.Executor;
 import org.apache.maven.cling.executor.MavenExecutorTestSupport;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
 
 /**
  * Embedded executor UT
@@ -30,4 +35,34 @@ public class EmbeddedMavenExecutorTest extends 
MavenExecutorTestSupport {
     protected Executor doSelectExecutor() {
         return new EmbeddedMavenExecutor();
     }
+
+    @Test
+    void testParseArgumentsSimple() {
+        assertEquals(
+                List.of("-T", "4", "clean", "install"), 
EmbeddedMavenExecutor.parseArguments("-T 4 clean install"));
+    }
+
+    @Test
+    void testParseArgumentsDoubleQuoted() {
+        assertEquals(
+                List.of("-f", "C:\\Program Files\\project\\pom.xml"),
+                EmbeddedMavenExecutor.parseArguments("-f \"C:\\Program 
Files\\project\\pom.xml\""));
+    }
+
+    @Test
+    void testParseArgumentsSingleQuoted() {
+        assertEquals(
+                List.of("-f", "/path with spaces/pom.xml"),
+                EmbeddedMavenExecutor.parseArguments("-f '/path with 
spaces/pom.xml'"));
+    }
+
+    @Test
+    void testParseArgumentsEmpty() {
+        assertEquals(List.of(), EmbeddedMavenExecutor.parseArguments(""));
+    }
+
+    @Test
+    void testParseArgumentsExtraWhitespace() {
+        assertEquals(List.of("clean", "install"), 
EmbeddedMavenExecutor.parseArguments("  clean   install  "));
+    }
 }
diff --git 
a/impl/maven-executor/src/test/java/org/apache/maven/cling/executor/forked/ForkedMavenExecutorTest.java
 
b/impl/maven-executor/src/test/java/org/apache/maven/cling/executor/forked/ForkedMavenExecutorTest.java
index 5555e0ba34..8e6ee7cad8 100644
--- 
a/impl/maven-executor/src/test/java/org/apache/maven/cling/executor/forked/ForkedMavenExecutorTest.java
+++ 
b/impl/maven-executor/src/test/java/org/apache/maven/cling/executor/forked/ForkedMavenExecutorTest.java
@@ -18,8 +18,13 @@
  */
 package org.apache.maven.cling.executor.forked;
 
+import java.util.List;
+
 import org.apache.maven.api.cli.Executor;
 import org.apache.maven.cling.executor.MavenExecutorTestSupport;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
 
 /**
  * Forked executor UT
@@ -30,4 +35,33 @@ public class ForkedMavenExecutorTest extends 
MavenExecutorTestSupport {
     protected Executor doSelectExecutor() {
         return new ForkedMavenExecutor();
     }
+
+    @Test
+    void testParseArgumentsSimple() {
+        assertEquals(List.of("-T", "4", "clean", "install"), 
ForkedMavenExecutor.parseArguments("-T 4 clean install"));
+    }
+
+    @Test
+    void testParseArgumentsDoubleQuoted() {
+        assertEquals(
+                List.of("-f", "C:\\Program Files\\project\\pom.xml"),
+                ForkedMavenExecutor.parseArguments("-f \"C:\\Program 
Files\\project\\pom.xml\""));
+    }
+
+    @Test
+    void testParseArgumentsSingleQuoted() {
+        assertEquals(
+                List.of("-f", "/path with spaces/pom.xml"),
+                ForkedMavenExecutor.parseArguments("-f '/path with 
spaces/pom.xml'"));
+    }
+
+    @Test
+    void testParseArgumentsEmpty() {
+        assertEquals(List.of(), ForkedMavenExecutor.parseArguments(""));
+    }
+
+    @Test
+    void testParseArgumentsExtraWhitespace() {
+        assertEquals(List.of("clean", "install"), 
ForkedMavenExecutor.parseArguments("  clean   install  "));
+    }
 }

Reply via email to