You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@maven.apache.org by st...@apache.org on 2012/08/30 00:06:48 UTC

svn commit: r1378754 - in /maven/shared/trunk/maven-shared-utils/src: main/java/org/apache/maven/shared/utils/cli/ main/java/org/apache/maven/shared/utils/cli/shell/ test/java/org/apache/maven/shared/utils/cli/

Author: struberg
Date: Wed Aug 29 22:06:47 2012
New Revision: 1378754

URL: http://svn.apache.org/viewvc?rev=1378754&view=rev
Log:
MSHARED-236 apply changes from people with iCLA on file

This commit leaves out all changes which were done after someone changed
"Licensed to the Apache Software Foundation" to 
"The Codehaus Foundation" without changing a single line of code.

Added:
    maven/shared/trunk/maven-shared-utils/src/test/java/org/apache/maven/shared/utils/cli/
    maven/shared/trunk/maven-shared-utils/src/test/java/org/apache/maven/shared/utils/cli/CommandLineUtilsTest.java   (with props)
Modified:
    maven/shared/trunk/maven-shared-utils/src/main/java/org/apache/maven/shared/utils/cli/CommandLineUtils.java
    maven/shared/trunk/maven-shared-utils/src/main/java/org/apache/maven/shared/utils/cli/Commandline.java
    maven/shared/trunk/maven-shared-utils/src/main/java/org/apache/maven/shared/utils/cli/StreamConsumer.java
    maven/shared/trunk/maven-shared-utils/src/main/java/org/apache/maven/shared/utils/cli/StreamFeeder.java
    maven/shared/trunk/maven-shared-utils/src/main/java/org/apache/maven/shared/utils/cli/StreamPumper.java
    maven/shared/trunk/maven-shared-utils/src/main/java/org/apache/maven/shared/utils/cli/shell/BourneShell.java
    maven/shared/trunk/maven-shared-utils/src/main/java/org/apache/maven/shared/utils/cli/shell/CmdShell.java
    maven/shared/trunk/maven-shared-utils/src/main/java/org/apache/maven/shared/utils/cli/shell/Shell.java

Modified: maven/shared/trunk/maven-shared-utils/src/main/java/org/apache/maven/shared/utils/cli/CommandLineUtils.java
URL: http://svn.apache.org/viewvc/maven/shared/trunk/maven-shared-utils/src/main/java/org/apache/maven/shared/utils/cli/CommandLineUtils.java?rev=1378754&r1=1378753&r2=1378754&view=diff
==============================================================================
--- maven/shared/trunk/maven-shared-utils/src/main/java/org/apache/maven/shared/utils/cli/CommandLineUtils.java (original)
+++ maven/shared/trunk/maven-shared-utils/src/main/java/org/apache/maven/shared/utils/cli/CommandLineUtils.java Wed Aug 29 22:06:47 2012
@@ -24,16 +24,23 @@ package org.apache.maven.shared.utils.cl
  * SOFTWARE.
  */
 
+import org.apache.maven.shared.utils.Os;
+import org.apache.maven.shared.utils.StringUtils;
+import org.apache.maven.shared.utils.io.IOUtil;
+
 import java.io.BufferedReader;
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.InputStreamReader;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.Iterator;
+import java.io.Reader;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.List;
 import java.util.Locale;
 import java.util.Map;
 import java.util.Properties;
+import java.util.StringTokenizer;
 
 /**
  * @author <a href="mailto:trygvis@inamo.no">Trygve Laugst&oslash;l </a>
@@ -41,30 +48,13 @@ import java.util.Properties;
  */
 public abstract class CommandLineUtils
 {
-    private static Map processes = Collections.synchronizedMap( new HashMap() );
-
-    private static Properties envVars;
-
-    static
-    {
-        Runtime.getRuntime().addShutdownHook( new Thread( "CommandlineUtil shutdown" )
-        {
-            public void run()
-            {
-                if ( ( processes != null ) && ( processes.size() > 0 ) )
-                {
-                    System.err.println( "Destroying " + processes.size() + " processes" );
-                    for ( Iterator it = processes.values().iterator(); it.hasNext(); )
-                    {
-                        System.err.println( "Destroying process.." );
-                        ( (Process) it.next() ).destroy();
+    /**
+     * Sixteen-bit Unicode Transformation Format, little-endian byte order.
+     * Every implementation of the Java platform is required to support this character encoding.
+     * @see java.nio.charset.Charset
+     */
+    public static final String UTF_16LE = "UTF-16LE";
 
-                    }
-                    System.err.println( "Destroyed " + processes.size() + " processes" );
-                }
-            }
-        } );
-    }
 
     public static class StringStreamConsumer
         implements StreamConsumer
@@ -84,37 +74,94 @@ public abstract class CommandLineUtils
         }
     }
 
+    private static class ProcessHook extends Thread {
+        private final Process process;
+
+        private ProcessHook( Process process )
+        {
+            super("CommandlineUtils process shutdown hook");
+            this.process = process;
+            this.setContextClassLoader(  null );
+        }
+
+        public void run()
+        {
+            process.destroy();
+        }
+    }
+
+
     public static int executeCommandLine( Commandline cl, StreamConsumer systemOut, StreamConsumer systemErr )
         throws CommandLineException
     {
-        return executeCommandLine( cl, null, systemOut, systemErr );
+        return executeCommandLine( cl, null, systemOut, systemErr, 0 );
+    }
+
+    public static int executeCommandLine( Commandline cl, StreamConsumer systemOut, StreamConsumer systemErr,
+                                          int timeoutInSeconds )
+        throws CommandLineException
+    {
+        return executeCommandLine( cl, null, systemOut, systemErr, timeoutInSeconds );
     }
 
     public static int executeCommandLine( Commandline cl, InputStream systemIn, StreamConsumer systemOut,
                                           StreamConsumer systemErr )
         throws CommandLineException
     {
+        return executeCommandLine( cl, systemIn, systemOut, systemErr, 0 );
+    }
+
+    /**
+     * @param cl               The command line to execute
+     * @param systemIn         The input to read from, must be thread safe
+     * @param systemOut        A consumer that receives output, must be thread safe
+     * @param systemErr        A consumer that receives system error stream output, must be thread safe
+     * @param timeoutInSeconds Positive integer to specify timeout, zero and negative integers for no timeout.
+     * @return A return value, see {@link Process#exitValue()}
+     * @throws CommandLineException or CommandLineTimeOutException if time out occurs
+     * @noinspection ThrowableResultOfMethodCallIgnored
+     */
+    public static int executeCommandLine( Commandline cl, InputStream systemIn, StreamConsumer systemOut,
+                                          StreamConsumer systemErr, int timeoutInSeconds )
+        throws CommandLineException
+    {
+        final CommandLineCallable future =
+            executeCommandLineAsCallable( cl, systemIn, systemOut, systemErr, timeoutInSeconds );
+        return future.call();
+    }
+
+    /**
+     * Immediately forks a process, returns a callable that will block until process is complete.
+     * @param cl               The command line to execute
+     * @param systemIn         The input to read from, must be thread safe
+     * @param systemOut        A consumer that receives output, must be thread safe
+     * @param systemErr        A consumer that receives system error stream output, must be thread safe
+     * @param timeoutInSeconds Positive integer to specify timeout, zero and negative integers for no timeout.
+     * @return A CommandLineCallable that provides the process return value, see {@link Process#exitValue()}. "call" must be called on
+     *         this to be sure the forked process has terminated, no guarantees is made about
+     *         any internal state before after the completion of the call statements
+     * @throws CommandLineException or CommandLineTimeOutException if time out occurs
+     * @noinspection ThrowableResultOfMethodCallIgnored
+     */
+    public static CommandLineCallable executeCommandLineAsCallable( final Commandline cl, final InputStream systemIn,
+                                                                  final StreamConsumer systemOut,
+                                                                  final StreamConsumer systemErr,
+                                                                  final int timeoutInSeconds )
+        throws CommandLineException
+    {
         if ( cl == null )
         {
             throw new IllegalArgumentException( "cl cannot be null." );
         }
 
-        Process p;
-
-        p = cl.execute();
+        final Process p = cl.execute();
 
-        processes.put( new Long( cl.getPid() ), p );
+        final StreamFeeder inputFeeder = systemIn != null ?
+             new StreamFeeder( systemIn, p.getOutputStream() ) : null;
 
-        StreamFeeder inputFeeder = null;
+        final StreamPumper outputPumper = new StreamPumper( p.getInputStream(), systemOut );
 
-        if ( systemIn != null )
-        {
-            inputFeeder = new StreamFeeder( systemIn, p.getOutputStream() );
-        }
-
-        StreamPumper outputPumper = new StreamPumper( p.getInputStream(), systemOut );
-
-        StreamPumper errorPumper = new StreamPumper( p.getErrorStream(), systemErr );
+        final StreamPumper errorPumper = new StreamPumper( p.getErrorStream(), systemErr );
 
         if ( inputFeeder != null )
         {
@@ -125,48 +172,68 @@ public abstract class CommandLineUtils
 
         errorPumper.start();
 
+        final ProcessHook processHook = new ProcessHook( p );
+
+        ShutdownHookUtils.addShutDownHook( processHook );
+
+        return new CommandLineCallable()
+        {
+            public Integer call()
+                throws CommandLineException
+            {
         try
         {
-            int returnValue = p.waitFor();
-
-            if ( inputFeeder != null )
+                    int returnValue;
+                    if ( timeoutInSeconds <= 0 )
             {
-                synchronized ( inputFeeder )
+                        returnValue = p.waitFor();
+                    }
+                    else
                 {
-                    if ( !inputFeeder.isDone() )
+                        long now = System.currentTimeMillis();
+                        long timeoutInMillis = 1000L * timeoutInSeconds;
+                        long finish = now + timeoutInMillis;
+                        while ( isAlive( p ) && ( System.currentTimeMillis() < finish ) )
                     {
-                        inputFeeder.wait();
+                            Thread.sleep( 10 );
                     }
+                        if ( isAlive( p ) )
+                        {
+                            throw new InterruptedException( "Process timeout out after " + timeoutInSeconds + " seconds" );
                 }
+                        returnValue = p.exitValue();
             }
 
-            synchronized ( outputPumper )
+                    waitForAllPumpers( inputFeeder, outputPumper, errorPumper );
+
+                    if ( outputPumper.getException() != null )
             {
-                if ( !outputPumper.isDone() )
-                {
-                    outputPumper.wait();
-                }
+                throw new CommandLineException( "Error inside systemOut parser", outputPumper.getException() );
             }
 
-            synchronized ( errorPumper )
+                    if ( errorPumper.getException() != null )
             {
-                if ( !errorPumper.isDone() )
-                {
-                    errorPumper.wait();
-                }
+                        throw new CommandLineException( "Error inside systemErr parser", errorPumper.getException() );
             }
 
-            processes.remove( new Long( cl.getPid() ) );
-
             return returnValue;
         }
         catch ( InterruptedException ex )
         {
-            killProcess( cl.getPid() );
-            throw new CommandLineException( "Error while executing external command, process killed.", ex );
+                    if ( inputFeeder != null )
+                    {
+                        inputFeeder.disable();
         }
+                    outputPumper.disable();
+                    errorPumper.disable();
+                    throw new CommandLineTimeOutException( "Error while executing external command, process killed.", ex );
+                }
         finally
         {
+                    ShutdownHookUtils.removeShutdownHook( processHook );
+
+                    processHook.run();
+
             if ( inputFeeder != null )
             {
                 inputFeeder.close();
@@ -177,18 +244,37 @@ public abstract class CommandLineUtils
             errorPumper.close();
         }
     }
+        };
+    }
 
-    public static Properties getSystemEnvVars()
-        throws IOException
+    private static void waitForAllPumpers( StreamFeeder inputFeeder, StreamPumper outputPumper,
+                                           StreamPumper errorPumper )
+        throws InterruptedException
     {
-        if ( envVars == null )
+        if ( inputFeeder != null )
         {
-            envVars = getSystemEnvVars( true );
+            inputFeeder.waitUntilDone();
         }
 
-        Properties props = new Properties();
-        props.putAll( envVars );
-        return props;
+        outputPumper.waitUntilDone();
+        errorPumper.waitUntilDone();
+    }
+
+    /**
+     * Gets the shell environment variables for this process. Note that the returned mapping from variable names to
+     * values will always be case-sensitive regardless of the platform, i.e. <code>getSystemEnvVars().get("path")</code>
+     * and <code>getSystemEnvVars().get("PATH")</code> will in general return different values. However, on platforms
+     * with case-insensitive environment variables like Windows, all variable names will be normalized to upper case.
+     *
+     * @return The shell environment variables, can be empty but never <code>null</code>.
+     * @throws IOException If the environment variables could not be queried from the shell.
+     * @see System#getenv() System.getenv() API, new in JDK 5.0, to get the same result
+     *      <b>since 2.0.2 System#getenv() will be used if available in the current running jvm.</b>
+     */
+    public static Properties getSystemEnvVars()
+        throws IOException
+    {
+        return getSystemEnvVars( !Os.isFamily( Os.FAMILY_WINDOWS ) );
     }
 
     /**
@@ -197,29 +283,59 @@ public abstract class CommandLineUtils
      *
      * @param caseSensitive Whether environment variable keys should be treated case-sensitively.
      * @return Properties object of (possibly modified) envar keys mapped to their values.
-     * @throws IOException
+     * @throws IOException .
+     * @see System#getenv() System.getenv() API, new in JDK 5.0, to get the same result
+     *      <b>since 2.0.2 System#getenv() will be used if available in the current running jvm.</b>
      */
     public static Properties getSystemEnvVars( boolean caseSensitive )
         throws IOException
     {
+
+        // check if it's 1.5+ run
+
+        Method getenvMethod = getEnvMethod();
+        if ( getenvMethod != null )
+        {
+            try
+            {
+                return getEnvFromSystem( getenvMethod, caseSensitive );
+            }
+            catch ( IllegalAccessException e )
+            {
+                throw new IOException( e.getMessage() );
+            }
+            catch ( IllegalArgumentException e )
+            {
+                throw new IOException( e.getMessage() );
+            }
+            catch ( InvocationTargetException e )
+            {
+                throw new IOException( e.getMessage() );
+            }
+        }
+
         Process p = null;
 
+        try
+        {
         Properties envVars = new Properties();
 
         Runtime r = Runtime.getRuntime();
 
-        String os = System.getProperty( "os.name" ).toLowerCase( Locale.ENGLISH );
-
-        //If this is windows set the shell to command.com or cmd.exe with correct arguments.
-        if ( os.indexOf( "windows" ) != -1 )
+         //If this is windows set the shell to command.com or cmd.exe with correct arguments.
+            boolean overriddenEncoding = false;
+            if ( Os.isFamily( Os.FAMILY_WINDOWS ) )
         {
-            if ( os.indexOf( "95" ) != -1 || os.indexOf( "98" ) != -1 || os.indexOf( "Me" ) != -1 )
+                if ( Os.isFamily( Os.FAMILY_WIN9X ) )
             {
                 p = r.exec( "command.com /c set" );
             }
             else
             {
-                p = r.exec( "cmd.exe /c set" );
+                    overriddenEncoding = true;
+                    // /U = change stdout encoding to UTF-16LE to avoid encoding inconsistency
+                    // between command-line/DOS and GUI/Windows, see PLXUTILS-124
+                    p = r.exec( "cmd.exe /U /c set" );
             }
         }
         else
@@ -227,7 +343,10 @@ public abstract class CommandLineUtils
             p = r.exec( "env" );
         }
 
-        BufferedReader br = new BufferedReader( new InputStreamReader( p.getInputStream() ) );
+            Reader reader = overriddenEncoding
+                ? new InputStreamReader( p.getInputStream(), UTF_16LE )
+                : new InputStreamReader( p.getInputStream() );
+            BufferedReader br = new BufferedReader( reader );
 
         String line;
 
@@ -238,13 +357,13 @@ public abstract class CommandLineUtils
         {
             int idx = line.indexOf( '=' );
 
-            if ( idx > 1 )
+                if ( idx > 0 )
             {
                 lastKey = line.substring( 0, idx );
 
                 if ( !caseSensitive )
                 {
-                    lastKey = lastKey.toUpperCase();
+                        lastKey = lastKey.toUpperCase( Locale.ENGLISH );
                 }
 
                 lastVal = line.substring( idx + 1 );
@@ -261,32 +380,270 @@ public abstract class CommandLineUtils
 
         return envVars;
     }
+        finally
+        {
+            if ( p != null )
+            {
+                IOUtil.close( p.getOutputStream() );
+                IOUtil.close( p.getErrorStream() );
+                IOUtil.close( p.getInputStream() );
+
+                p.destroy();
+            }
+        }
+    }
+
+    public static boolean isAlive( Process p )
+    {
+        if ( p == null )
+        {
+            return false;
+        }
+
+        try
+        {
+            p.exitValue();
+            return false;
+        }
+        catch ( IllegalThreadStateException e )
+        {
+            return true;
+        }
+    }
+
+    public static String[] translateCommandline( String toProcess )
+        throws Exception
+    {
+        if ( ( toProcess == null ) || ( toProcess.length() == 0 ) )
+        {
+            return new String[0];
+        }
+
+        // parse with a simple finite state machine
+
+        final int normal = 0;
+        final int inQuote = 1;
+        final int inDoubleQuote = 2;
+        int state = normal;
+        StringTokenizer tok = new StringTokenizer( toProcess, "\"\' ", true );
+        List<String> tokens = new ArrayList<String>();
+        StringBuilder current = new StringBuilder();
+
+        while ( tok.hasMoreTokens() )
+        {
+            String nextTok = tok.nextToken();
+            switch ( state )
+            {
+                case inQuote:
+                    if ( "\'".equals( nextTok ) )
+                    {
+                        state = normal;
+                    }
+                    else
+                    {
+                        current.append( nextTok );
+                    }
+                    break;
+                case inDoubleQuote:
+                    if ( "\"".equals( nextTok ) )
+                    {
+                        state = normal;
+                    }
+                    else
+                    {
+                        current.append( nextTok );
+                    }
+                    break;
+                default:
+                    if ( "\'".equals( nextTok ) )
+                    {
+                        state = inQuote;
+                    }
+                    else if ( "\"".equals( nextTok ) )
+                    {
+                        state = inDoubleQuote;
+                    }
+                    else if ( " ".equals( nextTok ) )
+                    {
+                        if ( current.length() != 0 )
+                        {
+                            tokens.add( current.toString() );
+                            current.setLength( 0 );
+                        }
+                    }
+                    else
+                    {
+                        current.append( nextTok );
+                    }
+                    break;
+            }
+        }
+
+        if ( current.length() != 0 )
+        {
+            tokens.add( current.toString() );
+        }
+
+        if ( ( state == inQuote ) || ( state == inDoubleQuote ) )
+        {
+            throw new CommandLineException( "unbalanced quotes in " + toProcess );
+        }
+
+        String[] args = tokens.toArray( new String[tokens.size()] );
+
+        return args;
+    }
 
     /**
-     * Kill a process launched by executeCommandLine methods
-     * Doesn't work correctly on windows, only the cmd process will be destroy but not the sub process (<a href="http://bugs.sun.com/bugdatabase/view_bug.do;:YfiG?bug_id=4770092">Bug ID 4770092</a>)
+     * <p>Put quotes around the given String if necessary.</p>
+     * <p>If the argument doesn't include spaces or quotes, return it
+     * as is. If it contains double quotes, use single quotes - else
+     * surround the argument by double quotes.</p>
      *
-     * @param pid The pid of command return by Commandline.getPid()
+     * @throws CommandLineException if the argument contains both, single
+     *                              and double quotes.
+     * @deprecated Use {@link StringUtils#quoteAndEscape(String, char, char[], char[], char, boolean)},
+     *             {@link StringUtils#quoteAndEscape(String, char, char[], char, boolean)}, or
+     *             {@link StringUtils#quoteAndEscape(String, char)} instead.
      */
-    public static void killProcess( long pid )
+    @SuppressWarnings( { "JavaDoc", "deprecation" } )
+    public static String quote( String argument )
+        throws CommandLineException
     {
-        Process p = (Process) processes.get( new Long( pid ) );
+        return quote( argument, false, false, true );
+    }
 
-        if ( p != null )
+    /**
+     * <p>Put quotes around the given String if necessary.</p>
+     * <p>If the argument doesn't include spaces or quotes, return it
+     * as is. If it contains double quotes, use single quotes - else
+     * surround the argument by double quotes.</p>
+     *
+     * @throws CommandLineException if the argument contains both, single
+     *                              and double quotes.
+     * @deprecated Use {@link StringUtils#quoteAndEscape(String, char, char[], char[], char, boolean)},
+     *             {@link StringUtils#quoteAndEscape(String, char, char[], char, boolean)}, or
+     *             {@link StringUtils#quoteAndEscape(String, char)} instead.
+     */
+    @SuppressWarnings( { "JavaDoc", "UnusedDeclaration", "deprecation" } )
+    public static String quote( String argument, boolean wrapExistingQuotes )
+        throws CommandLineException
         {
-            p.destroy();
-            System.out.println( "killed." );
-            processes.remove( new Long( pid ) );
+        return quote( argument, false, false, wrapExistingQuotes );
         }
+
+    /**
+     * @deprecated Use {@link StringUtils#quoteAndEscape(String, char, char[], char[], char, boolean)},
+     *             {@link StringUtils#quoteAndEscape(String, char, char[], char, boolean)}, or
+     *             {@link StringUtils#quoteAndEscape(String, char)} instead.
+     */
+    @SuppressWarnings( { "JavaDoc" } )
+    public static String quote( String argument, boolean escapeSingleQuotes, boolean escapeDoubleQuotes,
+                                boolean wrapExistingQuotes )
+        throws CommandLineException
+    {
+        if ( argument.contains( "\"" ) )
+        {
+            if ( argument.contains( "\'" ) )
+            {
+                throw new CommandLineException( "Can't handle single and double quotes in same argument" );
+            }
         else
         {
-            System.out.println( "don't exist." );
+                if ( escapeSingleQuotes )
+                {
+                    return "\\\'" + argument + "\\\'";
+        }
+                else if ( wrapExistingQuotes )
+                {
+                    return '\'' + argument + '\'';
+    }
+            }
+        }
+        else if ( argument.contains( "\'" ) )
+        {
+            if ( escapeDoubleQuotes )
+            {
+                return "\\\"" + argument + "\\\"";
+            }
+            else if ( wrapExistingQuotes )
+            {
+                return '\"' + argument + '\"';
+            }
+        }
+        else if ( argument.contains( " " ) )
+        {
+            if ( escapeDoubleQuotes )
+            {
+                return "\\\"" + argument + "\\\"";
+            }
+            else
+            {
+                return '\"' + argument + '\"';
+            }
         }
+
+        return argument;
     }
 
-    public static boolean isAlive( long pid )
+    public static String toString( String[] line )
     {
-        return ( processes.get( new Long( pid ) ) != null );
+        // empty path return empty string
+        if ( ( line == null ) || ( line.length == 0 ) )
+        {
+            return "";
     }
 
+        // path containing one or more elements
+        final StringBuilder result = new StringBuilder();
+        for ( int i = 0; i < line.length; i++ )
+        {
+            if ( i > 0 )
+            {
+                result.append( ' ' );
+            }
+            try
+            {
+                result.append( StringUtils.quoteAndEscape( line[i], '\"' ) );
+            }
+            catch ( Exception e )
+            {
+                System.err.println( "Error quoting argument: " + e.getMessage() );
+            }
+        }
+        return result.toString();
+    }
+
+    private static Method getEnvMethod()
+    {
+        try
+        {
+            return System.class.getMethod( "getenv");
+        }
+        catch ( NoSuchMethodException e )
+        {
+            return null;
+        }
+        catch ( SecurityException e )
+        {
+            return null;
+        }
+    }
+
+    private static Properties getEnvFromSystem( Method method, boolean caseSensitive )
+        throws IllegalAccessException, IllegalArgumentException, InvocationTargetException
+    {
+        Properties envVars = new Properties();
+        @SuppressWarnings( { "unchecked" } ) Map<String, String> envs = (Map<String, String>) method.invoke( null );
+        for ( String key : envs.keySet() )
+        {
+            String value = envs.get( key );
+            if ( !caseSensitive )
+            {
+                key = key.toUpperCase( Locale.ENGLISH );
+            }
+            envVars.put( key, value );
+        }
+        return envVars;
+    }
 }

Modified: maven/shared/trunk/maven-shared-utils/src/main/java/org/apache/maven/shared/utils/cli/Commandline.java
URL: http://svn.apache.org/viewvc/maven/shared/trunk/maven-shared-utils/src/main/java/org/apache/maven/shared/utils/cli/Commandline.java?rev=1378754&r1=1378753&r2=1378754&view=diff
==============================================================================
--- maven/shared/trunk/maven-shared-utils/src/main/java/org/apache/maven/shared/utils/cli/Commandline.java (original)
+++ maven/shared/trunk/maven-shared-utils/src/main/java/org/apache/maven/shared/utils/cli/Commandline.java Wed Aug 29 22:06:47 2012
@@ -78,20 +78,21 @@ package org.apache.maven.shared.utils.cl
  * ====================================================================
  */
 
+import org.apache.maven.shared.utils.Os;
+import org.apache.maven.shared.utils.StringUtils;
+import org.apache.maven.shared.utils.cli.shell.BourneShell;
 import org.apache.maven.shared.utils.cli.shell.CommandShell;
 import org.apache.maven.shared.utils.cli.shell.CmdShell;
 import org.apache.maven.shared.utils.cli.shell.Shell;
 
 import java.io.File;
 import java.io.IOException;
-import java.util.ArrayList;
-import java.util.Arrays;
+import java.util.Collections;
 import java.util.Iterator;
-import java.util.List;
+import java.util.LinkedHashMap;
+import java.util.Map;
 import java.util.Properties;
-import java.util.StringTokenizer;
 import java.util.Vector;
-import java.util.Hashtable;
 
 /**
  * <p/>
@@ -122,23 +123,66 @@ import java.util.Hashtable;
  * @author <a href="mailto:stefan.bodewig@epost.de">Stefan Bodewig</a>
  */
 public class Commandline
-    implements Cloneable
+        implements Cloneable
 {
-    protected static final String OS_NAME = "os.name";
+    protected Vector arguments = new Vector();
 
-    protected static final String WINDOWS = "Windows";
+    //protected Vector envVars = new Vector();
+    // synchronized added to preserve synchronize of Vector class
+    protected Map envVars = Collections.synchronizedMap( new LinkedHashMap() );
 
-    protected String executable = null;
+    private long pid = -1;
 
-    protected Vector arguments = new Vector();
+    private Shell shell;
 
-    protected Hashtable envVars = new Hashtable();
+    /**
+     * @deprecated Use {@link Commandline#setExecutable(String)} instead.
+     */
+    protected String executable;
 
-    private File workingDir = null;
+    /**
+     * @deprecated Use {@link Commandline#setWorkingDirectory(File)} or
+     * {@link Commandline#setWorkingDirectory(String)} instead.
+     */
+    private File workingDir;
 
-    private long pid = -1;
+    /**
+     * Create a new command line object.
+     * Shell is autodetected from operating system
+     *
+     * @param toProcess
+     */
+    public Commandline( String toProcess, Shell shell )
+    {
+        this.shell = shell;
 
-    private Shell shell;
+        String[] tmp = new String[0];
+        try
+        {
+            tmp = CommandLineUtils.translateCommandline( toProcess );
+        }
+        catch ( Exception e )
+        {
+            System.err.println( "Error translating Commandline." );
+        }
+        if ( ( tmp != null ) && ( tmp.length > 0 ) )
+        {
+            setExecutable( tmp[0] );
+            for ( int i = 1; i < tmp.length; i++ )
+            {
+                createArgument().setValue( tmp[i] );
+            }
+        }
+    }
+
+    /**
+     * Create a new command line object.
+     * Shell is autodetected from operating system
+     */
+    public Commandline( Shell shell )
+    {
+        this.shell = shell;
+    }
 
     /**
      * Create a new command line object.
@@ -148,18 +192,17 @@ public class Commandline
      */
     public Commandline( String toProcess )
     {
-        super();
         setDefaultShell();
         String[] tmp = new String[0];
         try
         {
-            tmp = translateCommandline( toProcess );
+            tmp = CommandLineUtils.translateCommandline( toProcess );
         }
         catch ( Exception e )
         {
             System.err.println( "Error translating Commandline." );
         }
-        if ( tmp != null && tmp.length > 0 )
+        if ( ( tmp != null ) && ( tmp.length > 0 ) )
         {
             setExecutable( tmp[0] );
             for ( int i = 1; i < tmp.length; i++ )
@@ -175,7 +218,6 @@ public class Commandline
      */
     public Commandline()
     {
-        super();
         setDefaultShell();
     }
 
@@ -195,68 +237,6 @@ public class Commandline
     }
 
     /**
-     * Used for nested xml command line definitions.
-     */
-    public static class Argument
-    {
-
-        private String[] parts;
-
-        /**
-         * Sets a single commandline argument.
-         *
-         * @param value a single commandline argument.
-         */
-        public void setValue( String value )
-        {
-            if ( value != null )
-            {
-                parts = new String[]{value};
-            }
-        }
-
-        /**
-         * Line to split into several commandline arguments.
-         *
-         * @param line line to split into several commandline arguments
-         */
-        public void setLine( String line )
-        {
-            if ( line == null )
-            {
-                return;
-            }
-            try
-            {
-                parts = translateCommandline( line );
-            }
-            catch ( Exception e )
-            {
-                System.err.println( "Error translating Commandline." );
-            }
-        }
-
-        /**
-         * Sets a single commandline argument to the absolute filename
-         * of the given file.
-         *
-         * @param value a single commandline argument.
-         */
-        public void setFile( File value )
-        {
-            parts = new String[]{value.getAbsolutePath()};
-        }
-
-        /**
-         * Returns the parts this Argument consists of.
-         */
-        public String[] getParts()
-        {
-            return parts;
-        }
-    }
-
-    /**
      * Class to keep track of the position of an Argument.
      */
     // <p>This class is there to support the srcfile and targetfile
@@ -284,10 +264,10 @@ public class Commandline
         {
             if ( realPos == -1 )
             {
-                realPos = ( executable == null ? 0 : 1 );
+                realPos = ( getExecutable() == null ? 0 : 1 );
                 for ( int i = 0; i < position; i++ )
                 {
-                    Argument arg = (Argument) arguments.elementAt( i );
+                    Arg arg = (Arg) arguments.elementAt( i );
                     realPos += arg.getParts().length;
                 }
             }
@@ -295,19 +275,16 @@ public class Commandline
         }
     }
 
-
     /**
      * <p>Sets the shell or command-line interpretor for the detected operating system,
      * and the shell arguments.</p>
      */
     private void setDefaultShell()
     {
-        String os = System.getProperty( OS_NAME );
-
         //If this is windows set the shell to command.com or cmd.exe with correct arguments.
-        if ( os.indexOf( WINDOWS ) > -1 )
+        if ( Os.isFamily( Os.FAMILY_WINDOWS ) )
         {
-            if ( os.indexOf( "95" ) > -1 || os.indexOf( "98" ) > -1 || os.indexOf( "Me" ) > -1 )
+            if ( Os.isFamily( Os.FAMILY_WIN9X ) )
             {
                 setShell( new CommandShell() );
             }
@@ -316,6 +293,10 @@ public class Commandline
                 setShell( new CmdShell() );
             }
         }
+        else
+        {
+            setShell( new BourneShell() );
+        }
     }
 
     /**
@@ -327,6 +308,7 @@ public class Commandline
      *
      * @return the argument object.
      * @see #createArgument(boolean)
+     * @deprecated Use {@link Commandline#createArg()} instead
      */
     public Argument createArgument()
     {
@@ -341,6 +323,7 @@ public class Commandline
      *
      * @param insertAtStart if true, the argument is inserted at the
      *                      beginning of the list of args, otherwise it is appended.
+     * @deprecated Use {@link Commandline#createArg(boolean)} instead
      */
     public Argument createArgument( boolean insertAtStart )
     {
@@ -357,20 +340,91 @@ public class Commandline
     }
 
     /**
-     * Sets the executable to run.
+     * Creates an argument object.
+     * <p/>
+     * <p>Each commandline object has at most one instance of the
+     * argument class.  This method calls
+     * <code>this.createArgument(false)</code>.</p>
+     *
+     * @return the argument object.
+     * @see #createArgument(boolean)
      */
-    public void setExecutable( String executable )
+    public Arg createArg()
+    {
+        return this.createArg( false );
+    }
+
+    /**
+     * Creates an argument object and adds it to our list of args.
+     * <p/>
+     * <p>Each commandline object has at most one instance of the
+     * argument class.</p>
+     *
+     * @param insertAtStart if true, the argument is inserted at the
+     *                      beginning of the list of args, otherwise it is appended.
+     */
+    public Arg createArg( boolean insertAtStart )
+    {
+        Arg argument = new Argument();
+        if ( insertAtStart )
+        {
+            arguments.insertElementAt( argument, 0 );
+        }
+        else
+        {
+            arguments.addElement( argument );
+        }
+        return argument;
+    }
+
+    /**
+     * Adds an argument object to our list of args.
+     *
+     * @return the argument object.
+     * @see #addArg(Arg,boolean)
+     */
+    public void addArg( Arg argument )
+    {
+        this.addArg( argument, false );
+    }
+
+    /**
+     * Adds an argument object to our list of args.
+     *
+     * @param insertAtStart if true, the argument is inserted at the
+     *                      beginning of the list of args, otherwise it is appended.
+     */
+    public void addArg( Arg argument, boolean insertAtStart )
     {
-        if ( executable == null || executable.length() == 0 )
+        if ( insertAtStart )
         {
-            return;
+            arguments.insertElementAt( argument, 0 );
         }
-        this.executable = executable.replace( '/', File.separatorChar ).replace( '\\', File.separatorChar );
+        else
+        {
+            arguments.addElement( argument );
+        }
+    }
+
+    /**
+     * Sets the executable to run.
+     */
+    public void setExecutable( String executable )
+    {
+        shell.setExecutable( executable );
+        this.executable = executable;
     }
 
     public String getExecutable()
     {
-        return executable;
+        String exec = shell.getExecutable();
+
+        if ( exec == null )
+        {
+            exec = executable;
+        }
+
+        return exec;
     }
 
     public void addArguments( String[] line )
@@ -384,27 +438,26 @@ public class Commandline
     /**
      * Add an environment variable
      */
-    public void addEnvironment( String name,
-                                String value )
+    public void addEnvironment( String name, String value )
     {
-        envVars.put( name, name + "=" + value );
+        //envVars.add( name + "=" + value );
+        envVars.put( name, value );
     }
 
     /**
      * Add system environment variables
      */
     public void addSystemEnvironment()
-        throws Exception
+            throws Exception
     {
-        Properties envVars = CommandLineUtils.getSystemEnvVars();
+        Properties systemEnvVars = CommandLineUtils.getSystemEnvVars();
 
-        for ( Iterator i = envVars.keySet().iterator(); i.hasNext(); )
+        for ( Iterator i = systemEnvVars.keySet().iterator(); i.hasNext(); )
         {
             String key = (String) i.next();
-
-            if ( !this.envVars.containsKey( key ) )
+            if ( !envVars.containsKey( key ) )
             {
-                this.envVars.put( key, key + "=" + envVars.getProperty( key ) );
+                addEnvironment( key, systemEnvVars.getProperty( key ) );
             }
         }
     }
@@ -413,7 +466,7 @@ public class Commandline
      * Return the list of environment variables
      */
     public String[] getEnvironmentVariables()
-        throws CommandLineException
+            throws CommandLineException
     {
         try
         {
@@ -423,8 +476,16 @@ public class Commandline
         {
             throw new CommandLineException( "Error setting up environmental variables", e );
         }
-
-        return (String[]) envVars.values().toArray( new String[envVars.size()] );
+        String[] environmentVars = new String[envVars.size()];
+        int i = 0;
+        for ( Iterator iterator = envVars.keySet().iterator(); iterator.hasNext(); )
+        {
+            String name = (String) iterator.next();
+            String value = (String) envVars.get( name );
+            environmentVars[i] = name + "=" + value;
+            i++;
+        }
+        return environmentVars;
     }
 
     /**
@@ -433,6 +494,8 @@ public class Commandline
     public String[] getCommandline()
     {
         final String[] args = getArguments();
+        String executable = getExecutable();
+
         if ( executable == null )
         {
             return args;
@@ -448,26 +511,10 @@ public class Commandline
      */
     public String[] getShellCommandline()
     {
+        // TODO: Provided only for backward compat. with <= 1.4
+        verifyShellState();
 
-        if ( getShell() == null )
-        {
-            if ( executable != null )
-            {
-                List commandLine = new ArrayList();
-                commandLine.add( executable );
-                commandLine.addAll( Arrays.asList( getArguments() ) );
-                return (String[]) commandLine.toArray( new String[0] );
-            }
-            else
-            {
-                return getArguments();
-            }
-
-        }
-        else
-        {
-            return (String[]) getShell().getShellCommandLine( executable, getArguments() ).toArray( new String[0] );
-        }
+        return (String[]) getShell().getShellCommandLine( getArguments() ).toArray( new String[0] );
     }
 
     /**
@@ -497,163 +544,7 @@ public class Commandline
 
     public String toString()
     {
-        return toString( getCommandline() );
-    }
-
-    /**
-     * <p>Put quotes around the given String if necessary.</p>
-     * <p>If the argument doesn't include spaces or quotes, return it
-     * as is. If it contains double quotes, use single quotes - else
-     * surround the argument by double quotes.</p>
-     *
-     * @throws CommandLineException if the argument contains both, single
-     *                              and double quotes.
-     */
-    public static String quoteArgument( String argument )
-        throws CommandLineException
-    {
-        if ( argument.indexOf( '\"' ) > -1 )
-        {
-            if ( argument.indexOf( '\'' ) > -1 )
-            {
-                throw new CommandLineException( "Can't handle single and double quotes in same argument" );
-            }
-            else
-            {
-                return '\'' + argument + '\'';
-            }
-        }
-        else if ( containsAny( argument, "'<>&|*? " ) )
-        {
-            return '\"' + argument + '\"';
-        }
-        else
-        {
-            return argument;
-        }
-    }
-
-    private static boolean containsAny( String argument, String chars )
-    {
-        for ( int i = chars.length() - 1; i >= 0; i-- )
-        {
-            if ( argument.indexOf( chars.charAt( i ) ) >= 0 )
-            {
-                return true;
-            }
-        }
-        return false;
-    }
-
-    public static String toString( String[] line )
-    {
-        // empty path return empty string
-        if ( line == null || line.length == 0 )
-        {
-            return "";
-        }
-
-        // path containing one or more elements
-        final StringBuffer result = new StringBuffer();
-        for ( int i = 0; i < line.length; i++ )
-        {
-            if ( i > 0 )
-            {
-                result.append( ' ' );
-            }
-            try
-            {
-                result.append( quoteArgument( line[i] ) );
-            }
-            catch ( Exception e )
-            {
-                System.err.println( "Error quoting argument." );
-            }
-        }
-        return result.toString();
-    }
-
-    public static String[] translateCommandline( String toProcess )
-        throws Exception
-    {
-        if ( toProcess == null || toProcess.length() == 0 )
-        {
-            return new String[0];
-        }
-
-        // parse with a simple finite state machine
-
-        final int normal = 0;
-        final int inQuote = 1;
-        final int inDoubleQuote = 2;
-        int state = normal;
-        StringTokenizer tok = new StringTokenizer( toProcess, "\"\' ", true );
-        Vector v = new Vector();
-        StringBuffer current = new StringBuffer();
-
-        while ( tok.hasMoreTokens() )
-        {
-            String nextTok = tok.nextToken();
-            switch ( state )
-            {
-                case inQuote:
-                    if ( "\'".equals( nextTok ) )
-                    {
-                        state = normal;
-                    }
-                    else
-                    {
-                        current.append( nextTok );
-                    }
-                    break;
-                case inDoubleQuote:
-                    if ( "\"".equals( nextTok ) )
-                    {
-                        state = normal;
-                    }
-                    else
-                    {
-                        current.append( nextTok );
-                    }
-                    break;
-                default:
-                    if ( "\'".equals( nextTok ) )
-                    {
-                        state = inQuote;
-                    }
-                    else if ( "\"".equals( nextTok ) )
-                    {
-                        state = inDoubleQuote;
-                    }
-                    else if ( " ".equals( nextTok ) )
-                    {
-                        if ( current.length() != 0 )
-                        {
-                            v.addElement( current.toString() );
-                            current.setLength( 0 );
-                        }
-                    }
-                    else
-                    {
-                        current.append( nextTok );
-                    }
-                    break;
-            }
-        }
-
-        if ( current.length() != 0 )
-        {
-            v.addElement( current.toString() );
-        }
-
-        if ( state == inQuote || state == inDoubleQuote )
-        {
-            throw new CommandLineException( "unbalanced quotes in " + toProcess );
-        }
-
-        String[] args = new String[v.size()];
-        v.copyInto( args );
-        return args;
+        return StringUtils.join( getShellCommandline(), " " );
     }
 
     public int size()
@@ -663,8 +554,9 @@ public class Commandline
 
     public Object clone()
     {
-        Commandline c = new Commandline();
-        c.setExecutable( executable );
+        Commandline c = new Commandline( (Shell) shell.clone() );
+        c.executable = executable;
+        c.workingDir = workingDir;
         c.addArguments( getArguments() );
         return c;
     }
@@ -675,6 +567,9 @@ public class Commandline
     public void clear()
     {
         executable = null;
+        workingDir = null;
+        shell.setExecutable( null );
+        shell.clearArguments();
         arguments.removeAllElements();
     }
 
@@ -703,29 +598,48 @@ public class Commandline
      */
     public void setWorkingDirectory( String path )
     {
-        if ( path != null )
-        {
-            workingDir = new File( path );
-        }
+        shell.setWorkingDirectory( path );
+        workingDir = new File( path );
+    }
+
+    /**
+     * Sets execution directory.
+     */
+    public void setWorkingDirectory( File workingDirectory )
+    {
+        shell.setWorkingDirectory( workingDirectory );
+        workingDir = workingDirectory;
     }
 
     public File getWorkingDirectory()
     {
-        return workingDir;
+        File workDir = shell.getWorkingDirectory();
+
+        if ( workDir == null )
+        {
+            workDir = workingDir;
+        }
+
+        return workDir;
     }
 
     /**
      * Executes the command.
      */
     public Process execute()
-        throws CommandLineException
+            throws CommandLineException
     {
+        // TODO: Provided only for backward compat. with <= 1.4
+        verifyShellState();
+
         Process process;
 
         //addEnvironment( "MAVEN_TEST_ENVAR", "MAVEN_TEST_ENVAR_VALUE" );
 
         String[] environment = getEnvironmentVariables();
 
+        File workingDir = shell.getWorkingDirectory();
+
         try
         {
             if ( workingDir == null )
@@ -736,13 +650,13 @@ public class Commandline
             {
                 if ( !workingDir.exists() )
                 {
-                    throw new CommandLineException(
-                        "Working directory \"" + workingDir.getPath() + "\" does not exist!" );
+                    throw new CommandLineException( "Working directory \"" + workingDir.getPath()
+                            + "\" does not exist!" );
                 }
                 else if ( !workingDir.isDirectory() )
                 {
-                    throw new CommandLineException(
-                        "Path \"" + workingDir.getPath() + "\" does not specify a directory." );
+                    throw new CommandLineException( "Path \"" + workingDir.getPath()
+                            + "\" does not specify a directory." );
                 }
 
                 process = Runtime.getRuntime().exec( getShellCommandline(), environment, workingDir );
@@ -756,8 +670,24 @@ public class Commandline
         return process;
     }
 
+    /**
+     * @deprecated Remove once backward compat with plexus-utils <= 1.4 is no longer a consideration
+     */
+    private void verifyShellState()
+    {
+        if ( shell.getWorkingDirectory() == null )
+        {
+            shell.setWorkingDirectory( workingDir );
+        }
+
+        if ( shell.getExecutable() == null )
+        {
+            shell.setExecutable( executable );
+        }
+    }
+
     public Properties getSystemEnvVars()
-        throws Exception
+            throws Exception
     {
         return CommandLineUtils.getSystemEnvVars();
     }
@@ -782,4 +712,82 @@ public class Commandline
     {
         return shell;
     }
+
+    /**
+     * @deprecated Use {@link CommandLineUtils#translateCommandline(String)} instead.
+     */
+    public static String[] translateCommandline( String toProcess )
+            throws Exception
+    {
+        return CommandLineUtils.translateCommandline( toProcess );
+    }
+
+    /**
+     * @deprecated Use {@link CommandLineUtils#quote(String)} instead.
+     */
+    public static String quoteArgument( String argument )
+            throws CommandLineException
+    {
+        return CommandLineUtils.quote( argument );
+    }
+
+    /**
+     * @deprecated Use {@link CommandLineUtils#toString(String[])} instead.
+     */
+    public static String toString( String[] line )
+    {
+        return CommandLineUtils.toString( line );
+    }
+
+    public static class Argument
+            implements Arg
+    {
+        private String[] parts;
+
+        /* (non-Javadoc)
+         * @see org.codehaus.plexus.util.cli.Argumnt#setValue(java.lang.String)
+         */
+        public void setValue( String value )
+        {
+            if ( value != null )
+            {
+                parts = new String[] { value };
+            }
+        }
+
+        /* (non-Javadoc)
+         * @see org.codehaus.plexus.util.cli.Argumnt#setLine(java.lang.String)
+         */
+        public void setLine( String line )
+        {
+            if ( line == null )
+            {
+                return;
+            }
+            try
+            {
+                parts = CommandLineUtils.translateCommandline( line );
+            }
+            catch ( Exception e )
+            {
+                System.err.println( "Error translating Commandline." );
+            }
+        }
+
+        /* (non-Javadoc)
+         * @see org.codehaus.plexus.util.cli.Argumnt#setFile(java.io.File)
+         */
+        public void setFile( File value )
+        {
+            parts = new String[] { value.getAbsolutePath() };
+        }
+
+        /* (non-Javadoc)
+         * @see org.codehaus.plexus.util.cli.Argumnt#getParts()
+         */
+        public String[] getParts()
+        {
+            return parts;
+        }
+    }
 }

Modified: maven/shared/trunk/maven-shared-utils/src/main/java/org/apache/maven/shared/utils/cli/StreamConsumer.java
URL: http://svn.apache.org/viewvc/maven/shared/trunk/maven-shared-utils/src/main/java/org/apache/maven/shared/utils/cli/StreamConsumer.java?rev=1378754&r1=1378753&r2=1378754&view=diff
==============================================================================
--- maven/shared/trunk/maven-shared-utils/src/main/java/org/apache/maven/shared/utils/cli/StreamConsumer.java (original)
+++ maven/shared/trunk/maven-shared-utils/src/main/java/org/apache/maven/shared/utils/cli/StreamConsumer.java Wed Aug 29 22:06:47 2012
@@ -66,6 +66,9 @@ package org.apache.maven.shared.utils.cl
  * allow implementations to gain access to the lines being
  * "Pumped".
  *
+ * Please note that implementations of this interface can be expected to be
+ * called from arbitrary threads and must therefore be threadsafe.
+ *
  * @author <a href="mailto:fvancea@maxiq.com">Florin Vancea</a>
  * @author <a href="mailto:pj@thoughtworks.com">Paul Julius</a>
  */

Modified: maven/shared/trunk/maven-shared-utils/src/main/java/org/apache/maven/shared/utils/cli/StreamFeeder.java
URL: http://svn.apache.org/viewvc/maven/shared/trunk/maven-shared-utils/src/main/java/org/apache/maven/shared/utils/cli/StreamFeeder.java?rev=1378754&r1=1378753&r2=1378754&view=diff
==============================================================================
--- maven/shared/trunk/maven-shared-utils/src/main/java/org/apache/maven/shared/utils/cli/StreamFeeder.java (original)
+++ maven/shared/trunk/maven-shared-utils/src/main/java/org/apache/maven/shared/utils/cli/StreamFeeder.java Wed Aug 29 22:06:47 2012
@@ -34,14 +34,11 @@ import java.io.OutputStream;
  * @author <a href="mailto:trygvis@inamo.no">Trygve Laugst&oslash;l</a>
  * @version $Id$
  */
-public class StreamFeeder
-    extends Thread
-{
+public class StreamFeeder extends AbstractStreamHandler {
     private InputStream input;
 
     private OutputStream output;
 
-    private boolean done;
 
     /**
      * Create a new StreamFeeder
@@ -74,10 +71,10 @@ public class StreamFeeder
         {
             close();
 
-            done = true;
-
             synchronized ( this )
             {
+                setDone();
+
                 this.notifyAll();
             }
         }
@@ -124,28 +121,27 @@ public class StreamFeeder
         }
     }
 
-    public boolean isDone()
-    {
-        return done;
-    }
-
     // ----------------------------------------------------------------------
     //
     // ----------------------------------------------------------------------
 
     private void feed()
-        throws IOException
+            throws IOException
     {
         int data = input.read();
 
-        while ( !done && data != -1 )
+        while ( !isDone() && data != -1 )
         {
             synchronized ( output )
             {
-                output.write( data );
+                if ( !isDisabled())
+                {
+                    output.write( data );
+                }
 
                 data = input.read();
             }
         }
     }
+
 }

Modified: maven/shared/trunk/maven-shared-utils/src/main/java/org/apache/maven/shared/utils/cli/StreamPumper.java
URL: http://svn.apache.org/viewvc/maven/shared/trunk/maven-shared-utils/src/main/java/org/apache/maven/shared/utils/cli/StreamPumper.java?rev=1378754&r1=1378753&r2=1378754&view=diff
==============================================================================
--- maven/shared/trunk/maven-shared-utils/src/main/java/org/apache/maven/shared/utils/cli/StreamPumper.java (original)
+++ maven/shared/trunk/maven-shared-utils/src/main/java/org/apache/maven/shared/utils/cli/StreamPumper.java Wed Aug 29 22:06:47 2012
@@ -81,6 +81,7 @@ package org.apache.maven.shared.utils.cl
 import org.apache.maven.shared.utils.io.IOUtil;
 
 import java.io.BufferedReader;
+import java.io.IOException;
 import java.io.InputStream;
 import java.io.InputStreamReader;
 import java.io.PrintWriter;
@@ -94,7 +95,7 @@ import java.io.PrintWriter;
  * @since June 11, 2001
  */
 public class StreamPumper
-    extends Thread
+    extends AbstractStreamHandler
 {
     private BufferedReader in;
 
@@ -102,32 +103,28 @@ public class StreamPumper
 
     private PrintWriter out = null;
 
-    private static final int SIZE = 1024;
+    private volatile Exception exception = null;
 
-    boolean done;
+    private static final int SIZE = 1024;
 
     public StreamPumper( InputStream in )
     {
-        this.in = new BufferedReader( new InputStreamReader( in ), SIZE );
+        this( in, (StreamConsumer) null );
     }
 
     public StreamPumper( InputStream in, StreamConsumer consumer )
     {
-        this( in );
-
-        this.consumer = consumer;
+        this( in, null, consumer );
     }
 
     public StreamPumper( InputStream in, PrintWriter writer )
     {
-        this( in );
-
-        out = writer;
+        this( in, writer, null );
     }
 
     public StreamPumper( InputStream in, PrintWriter writer, StreamConsumer consumer )
     {
-        this( in );
+        this.in = new BufferedReader( new InputStreamReader( in ), SIZE );
         this.out = writer;
         this.consumer = consumer;
     }
@@ -136,25 +133,32 @@ public class StreamPumper
     {
         try
         {
-            String s = in.readLine();
-
-            while ( s != null )
+            for ( String line = in.readLine(); line != null; line = in.readLine() )
             {
-                consumeLine( s );
+                try
+                {
+                    if ( exception == null )
+                    {
+                        consumeLine( line );
+                    }
+                }
+                catch ( Exception t )
+                {
+                    exception = t;
+                }
 
                 if ( out != null )
                 {
-                    out.println( s );
+                    out.println( line );
 
                     out.flush();
                 }
 
-                s = in.readLine();
             }
         }
-        catch ( Throwable e )
+        catch ( IOException e )
         {
-            // Catched everything so the streams will be closed and flagged as done.
+            exception = e;
         }
         finally
         {
@@ -162,7 +166,7 @@ public class StreamPumper
 
             synchronized ( this )
             {
-                done = true;
+                setDone();
 
                 this.notifyAll();
             }
@@ -182,14 +186,14 @@ public class StreamPumper
         IOUtil.close( out );
     }
 
-    public boolean isDone()
+    public Exception getException()
     {
-        return done;
+        return exception;
     }
 
     private void consumeLine( String line )
     {
-        if ( consumer != null )
+        if ( consumer != null && !isDisabled() )
         {
             consumer.consumeLine( line );
         }

Modified: maven/shared/trunk/maven-shared-utils/src/main/java/org/apache/maven/shared/utils/cli/shell/BourneShell.java
URL: http://svn.apache.org/viewvc/maven/shared/trunk/maven-shared-utils/src/main/java/org/apache/maven/shared/utils/cli/shell/BourneShell.java?rev=1378754&r1=1378753&r2=1378754&view=diff
==============================================================================
--- maven/shared/trunk/maven-shared-utils/src/main/java/org/apache/maven/shared/utils/cli/shell/BourneShell.java (original)
+++ maven/shared/trunk/maven-shared-utils/src/main/java/org/apache/maven/shared/utils/cli/shell/BourneShell.java Wed Aug 29 22:06:47 2012
@@ -20,15 +20,154 @@ package org.apache.maven.shared.utils.cl
  */
 
 
+import org.apache.maven.shared.utils.Os;
+import org.apache.maven.shared.utils.StringUtils;
+
+import java.util.ArrayList;
+import java.util.List;
+
 /**
  * @author Jason van Zyl
  */
 public class BourneShell
     extends Shell
 {
+    private static final char[] BASH_QUOTING_TRIGGER_CHARS = {
+        ' ',
+        '$',
+        ';',
+        '&',
+        '|',
+        '<',
+        '>',
+        '*',
+        '?',
+        '(',
+        ')',
+        '[',
+        ']',
+        '{',
+        '}',
+        '`' };
+
     public BourneShell()
     {
+        this( false );
+    }
+
+    public BourneShell( boolean isLoginShell )
+    {
         setShellCommand( "/bin/sh" );
-        setShellArgs( new String[]{"-c"} );
+        setArgumentQuoteDelimiter( '\'' );
+        setExecutableQuoteDelimiter( '\"' );
+        setSingleQuotedArgumentEscaped( true );
+        setSingleQuotedExecutableEscaped( false );
+        setQuotedExecutableEnabled( true );
+
+        if ( isLoginShell )
+        {
+            addShellArg( "-l" );
+        }
+    }
+
+    /** {@inheritDoc} */
+    public String getExecutable()
+    {
+        if ( Os.isFamily( Os.FAMILY_WINDOWS ) )
+        {
+            return super.getExecutable();
+        }
+
+        return unifyQuotes( super.getExecutable());
+    }
+
+    public List<String> getShellArgsList()
+    {
+        List<String> shellArgs = new ArrayList<String>();
+        List<String> existingShellArgs = super.getShellArgsList();
+
+        if ( ( existingShellArgs != null ) && !existingShellArgs.isEmpty() )
+        {
+            shellArgs.addAll( existingShellArgs );
+        }
+
+        shellArgs.add( "-c" );
+
+        return shellArgs;
+    }
+
+    public String[] getShellArgs()
+    {
+        String[] shellArgs = super.getShellArgs();
+        if ( shellArgs == null )
+        {
+            shellArgs = new String[0];
+        }
+
+        if ( ( shellArgs.length > 0 ) && !shellArgs[shellArgs.length - 1].equals( "-c" ) )
+        {
+            String[] newArgs = new String[shellArgs.length + 1];
+
+            System.arraycopy( shellArgs, 0, newArgs, 0, shellArgs.length );
+            newArgs[shellArgs.length] = "-c";
+
+            shellArgs = newArgs;
+        }
+
+        return shellArgs;
+    }
+
+    protected String getExecutionPreamble()
+    {
+        if ( getWorkingDirectoryAsString() == null )
+        {
+            return null;
+        }
+
+        String dir = getWorkingDirectoryAsString();
+        StringBuffer sb = new StringBuffer();
+        sb.append( "cd " );
+
+        sb.append( unifyQuotes( dir ) );
+        sb.append( " && " );
+
+        return sb.toString();
+    }
+
+    protected char[] getQuotingTriggerChars()
+    {
+        return BASH_QUOTING_TRIGGER_CHARS;
+    }
+
+    /**
+     * <p>Unify quotes in a path for the Bourne Shell.</p>
+     *
+     * <pre>
+     * BourneShell.unifyQuotes(null)                       = null
+     * BourneShell.unifyQuotes("")                         = (empty)
+     * BourneShell.unifyQuotes("/test/quotedpath'abc")     = /test/quotedpath\'abc
+     * BourneShell.unifyQuotes("/test/quoted path'abc")    = "/test/quoted path'abc"
+     * BourneShell.unifyQuotes("/test/quotedpath\"abc")    = "/test/quotedpath\"abc"
+     * BourneShell.unifyQuotes("/test/quoted path\"abc")   = "/test/quoted path\"abc"
+     * BourneShell.unifyQuotes("/test/quotedpath\"'abc")   = "/test/quotedpath\"'abc"
+     * BourneShell.unifyQuotes("/test/quoted path\"'abc")  = "/test/quoted path\"'abc"
+     * </pre>
+     *
+     * @param path not null path.
+     * @return the path unified correctly for the Bourne shell.
+     */
+    protected static String unifyQuotes( String path )
+    {
+        if ( path == null )
+        {
+            return null;
+        }
+
+        if ( path.indexOf( " " ) == -1 && path.indexOf( "'" ) != -1 && path.indexOf( "\"" ) == -1 )
+        {
+            return StringUtils.escape( path );
+        }
+
+        return StringUtils.quoteAndEscape( path, '\"', BASH_QUOTING_TRIGGER_CHARS );
     }
 }

Modified: maven/shared/trunk/maven-shared-utils/src/main/java/org/apache/maven/shared/utils/cli/shell/CmdShell.java
URL: http://svn.apache.org/viewvc/maven/shared/trunk/maven-shared-utils/src/main/java/org/apache/maven/shared/utils/cli/shell/CmdShell.java?rev=1378754&r1=1378753&r2=1378754&view=diff
==============================================================================
--- maven/shared/trunk/maven-shared-utils/src/main/java/org/apache/maven/shared/utils/cli/shell/CmdShell.java (original)
+++ maven/shared/trunk/maven-shared-utils/src/main/java/org/apache/maven/shared/utils/cli/shell/CmdShell.java Wed Aug 29 22:06:47 2012
@@ -38,13 +38,50 @@ public class CmdShell
     public CmdShell()
     {
         setShellCommand( "cmd.exe" );
+        setQuotedExecutableEnabled( true );
         setShellArgs( new String[]{"/X", "/C"} );
     }
 
     /**
-     * Specific implementation that quotes the all the command line
+     * <p>
+     * Specific implementation that quotes all the command line.
+     * </p>
+     * <p>
+     * Workaround for http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6468220
+     * </p>
+     * <p>
+     * From cmd.exe /? output:
+     * </p>
+     *
+     * <pre>
+     *      If /C or /K is specified, then the remainder of the command line after
+     *      the switch is processed as a command line, where the following logic is
+     *      used to process quote (&quot;) characters:
+     *
+     *      1.  If all of the following conditions are met, then quote characters
+     *      on the command line are preserved:
+     *
+     *      - no /S switch
+     *      - exactly two quote characters
+     *      - no special characters between the two quote characters,
+     *      where special is one of: &amp;&lt;&gt;()@&circ;|
+     *      - there are one or more whitespace characters between the
+     *      the two quote characters
+     *      - the string between the two quote characters is the name
+     *      of an executable file.
+     *
+     *      2.  Otherwise, old behavior is to see if the first character is
+     *      a quote character and if so, strip the leading character and
+     *      remove the last quote character on the command line, preserving
+     *      any text after the last quote character.
+     * </pre>
+     *
+     *<p>
+     * Always quoting the entire command line, regardless of these conditions
+     * appears to make Windows processes invoke successfully.
+     * </p>
      */
-    public List getCommandLine( String executable, String[] arguments )
+    public List<String> getCommandLine( String executable, String[] arguments )
     {
         StringBuffer sb = new StringBuffer();
         sb.append( "\"" );

Modified: maven/shared/trunk/maven-shared-utils/src/main/java/org/apache/maven/shared/utils/cli/shell/Shell.java
URL: http://svn.apache.org/viewvc/maven/shared/trunk/maven-shared-utils/src/main/java/org/apache/maven/shared/utils/cli/shell/Shell.java?rev=1378754&r1=1378753&r2=1378754&view=diff
==============================================================================
--- maven/shared/trunk/maven-shared-utils/src/main/java/org/apache/maven/shared/utils/cli/shell/Shell.java (original)
+++ maven/shared/trunk/maven-shared-utils/src/main/java/org/apache/maven/shared/utils/cli/shell/Shell.java Wed Aug 29 22:06:47 2012
@@ -20,9 +20,10 @@ package org.apache.maven.shared.utils.cl
  */
 
 
-import org.apache.maven.shared.utils.cli.CommandLineException;
-import org.apache.maven.shared.utils.cli.Commandline;
 
+import org.apache.maven.shared.utils.StringUtils;
+
+import java.io.File;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.List;
@@ -41,10 +42,33 @@ import java.util.List;
  * @since 1.2
  */
 public class Shell
+    implements Cloneable
 {
+    private static final char[] DEFAULT_QUOTING_TRIGGER_CHARS = { ' ' };
+
     private String shellCommand;
 
-    private String[] shellArgs;
+    private List<String> shellArgs = new ArrayList<String>();
+
+    private boolean quotedArgumentsEnabled = true;
+
+    private String executable;
+
+    private String workingDir;
+
+    private boolean quotedExecutableEnabled = true;
+
+    private boolean doubleQuotedArgumentEscaped = false;
+
+    private boolean singleQuotedArgumentEscaped = false;
+
+    private boolean doubleQuotedExecutableEscaped = false;
+
+    private boolean singleQuotedExecutableEscaped = false;
+
+    private char argQuoteDelimiter = '\"';
+
+    private char exeQuoteDelimiter = '\"';
 
     /**
      * Set the command to execute the shell (eg. COMMAND.COM, /bin/bash,...)
@@ -74,7 +98,8 @@ public class Shell
      */
     public void setShellArgs( String[] shellArgs )
     {
-        this.shellArgs = shellArgs;
+        this.shellArgs.clear();
+        this.shellArgs.addAll( Arrays.asList( shellArgs ) );
     }
 
     /**
@@ -84,7 +109,14 @@ public class Shell
      */
     public String[] getShellArgs()
     {
-        return shellArgs;
+        if ( ( shellArgs == null ) || shellArgs.isEmpty() )
+        {
+            return null;
+    }
+        else
+        {
+            return (String[]) shellArgs.toArray( new String[shellArgs.size()] );
+        }
     }
 
     /**
@@ -94,47 +126,140 @@ public class Shell
      * @param arguments  arguments for the executable, not the shell
      * @return List with one String object with executable and arguments quoted as needed
      */
-    public List getCommandLine( String executable, String[] arguments )
+    public List<String> getCommandLine( String executable, String[] arguments )
     {
+        return getRawCommandLine( executable, arguments );
+    }
 
-        List commandLine = new ArrayList();
-        try
+    protected List<String> getRawCommandLine( String executable, String[] arguments )
         {
+        List<String> commandLine = new ArrayList<String>();
             StringBuffer sb = new StringBuffer();
 
             if ( executable != null )
             {
-                sb.append( Commandline.quoteArgument( executable ) );
+            String preamble = getExecutionPreamble();
+            if ( preamble != null )
+            {
+                sb.append( preamble );
             }
+
+            if ( isQuotedExecutableEnabled() )
+            {
+                char[] escapeChars = getEscapeChars( isSingleQuotedExecutableEscaped(), isDoubleQuotedExecutableEscaped() );
+
+                sb.append( StringUtils.quoteAndEscape( getExecutable(), getExecutableQuoteDelimiter(), escapeChars, getQuotingTriggerChars(), '\\', false ) );
+            }
+            else
+            {
+                sb.append( getExecutable() );
+            }
+        }
             for ( int i = 0; i < arguments.length; i++ )
             {
+            if ( sb.length() > 0 )
+            {
                 sb.append( " " );
-                sb.append( Commandline.quoteArgument( arguments[i] ) );
             }
 
-            commandLine.add( sb.toString() );
+            if ( isQuotedArgumentsEnabled() )
+            {
+                char[] escapeChars = getEscapeChars( isSingleQuotedExecutableEscaped(), isDoubleQuotedExecutableEscaped() );
+
+                sb.append( StringUtils.quoteAndEscape( arguments[i], getArgumentQuoteDelimiter(), escapeChars, getQuotingTriggerChars(), '\\', false ) );
         }
-        catch ( CommandLineException e )
+            else
         {
-            throw new RuntimeException( e );
+                sb.append( arguments[i] );
+        }
         }
 
+        commandLine.add( sb.toString() );
+
         return commandLine;
     }
 
+    protected char[] getQuotingTriggerChars()
+    {
+        return DEFAULT_QUOTING_TRIGGER_CHARS;
+    }
+
+    protected String getExecutionPreamble()
+    {
+        return null;
+    }
+
+    protected char[] getEscapeChars( boolean includeSingleQuote, boolean includeDoubleQuote )
+    {
+        StringBuffer buf = new StringBuffer( 2 );
+        if ( includeSingleQuote )
+        {
+            buf.append( '\'' );
+        }
+
+        if ( includeDoubleQuote )
+        {
+            buf.append( '\"' );
+        }
+
+        char[] result = new char[buf.length()];
+        buf.getChars( 0, buf.length(), result, 0 );
+
+        return result;
+    }
+
+    protected boolean isDoubleQuotedArgumentEscaped()
+    {
+        return doubleQuotedArgumentEscaped;
+    }
+
+    protected boolean isSingleQuotedArgumentEscaped()
+    {
+        return singleQuotedArgumentEscaped;
+    }
+
+    protected boolean isDoubleQuotedExecutableEscaped()
+    {
+        return doubleQuotedExecutableEscaped;
+    }
+
+    protected boolean isSingleQuotedExecutableEscaped()
+    {
+        return singleQuotedExecutableEscaped;
+    }
+
+    protected void setArgumentQuoteDelimiter( char argQuoteDelimiter )
+    {
+        this.argQuoteDelimiter = argQuoteDelimiter;
+    }
+
+    protected char getArgumentQuoteDelimiter()
+    {
+        return argQuoteDelimiter;
+    }
+
+    protected void setExecutableQuoteDelimiter( char exeQuoteDelimiter )
+    {
+        this.exeQuoteDelimiter = exeQuoteDelimiter;
+    }
+
+    protected char getExecutableQuoteDelimiter()
+    {
+        return exeQuoteDelimiter;
+    }
+
     /**
      * Get the full command line to execute, including shell command, shell arguments,
      * executable and executable arguments
      *
-     * @param executable executable that the shell has to call
      * @param arguments  arguments for the executable, not the shell
      * @return List of String objects, whose array version is suitable to be used as argument
      *         of Runtime.getRuntime().exec()
      */
-    public List getShellCommandLine( String executable, String[] arguments )
+    public List<String> getShellCommandLine( String[] arguments )
     {
 
-        List commandLine = new ArrayList();
+        List<String> commandLine = new ArrayList<String>();
 
         if ( getShellCommand() != null )
         {
@@ -143,13 +268,136 @@ public class Shell
 
         if ( getShellArgs() != null )
         {
-            commandLine.addAll( Arrays.asList( getShellArgs() ) );
+            commandLine.addAll( getShellArgsList() );
         }
 
-        commandLine.addAll( getCommandLine( executable, arguments ) );
+        commandLine.addAll( getCommandLine( getExecutable(), arguments ) );
 
         return commandLine;
 
     }
 
+    public List<String> getShellArgsList()
+    {
+        return shellArgs;
+    }
+
+    public void addShellArg( String arg )
+    {
+        shellArgs.add( arg );
+    }
+
+    public void setQuotedArgumentsEnabled( boolean quotedArgumentsEnabled )
+    {
+        this.quotedArgumentsEnabled = quotedArgumentsEnabled;
+    }
+
+    public boolean isQuotedArgumentsEnabled()
+    {
+        return quotedArgumentsEnabled;
+    }
+
+    public void setQuotedExecutableEnabled( boolean quotedExecutableEnabled )
+    {
+        this.quotedExecutableEnabled = quotedExecutableEnabled;
+    }
+
+    public boolean isQuotedExecutableEnabled()
+    {
+        return quotedExecutableEnabled;
+    }
+
+    /**
+     * Sets the executable to run.
+     */
+    public void setExecutable( String executable )
+    {
+        if ( ( executable == null ) || ( executable.length() == 0 ) )
+        {
+            return;
+        }
+        this.executable = executable.replace( '/', File.separatorChar ).replace( '\\', File.separatorChar );
+    }
+
+    public String getExecutable()
+    {
+        return executable;
+    }
+
+    /**
+     * Sets execution directory.
+     */
+    public void setWorkingDirectory( String path )
+    {
+        if ( path != null )
+        {
+            workingDir = path;
+        }
+    }
+
+    /**
+     * Sets execution directory.
+     */
+    public void setWorkingDirectory( File workingDir )
+    {
+        if ( workingDir != null )
+        {
+            this.workingDir = workingDir.getAbsolutePath();
+        }
+    }
+
+    public File getWorkingDirectory()
+    {
+        return workingDir == null ? null : new File( workingDir );
+    }
+
+    public String getWorkingDirectoryAsString()
+    {
+        return workingDir;
+    }
+
+    public void clearArguments()
+    {
+        shellArgs.clear();
+    }
+
+    public Object clone()
+    {
+        Shell shell = new Shell();
+        shell.setExecutable( getExecutable() );
+        shell.setWorkingDirectory( getWorkingDirectory() );
+        shell.setShellArgs( getShellArgs() );
+        return shell;
+    }
+
+    public String getOriginalExecutable()
+    {
+        return executable;
+    }
+
+    public List<String> getOriginalCommandLine( String executable, String[] arguments )
+    {
+        return getRawCommandLine( executable, arguments );
+    }
+
+    protected void setDoubleQuotedArgumentEscaped( boolean doubleQuotedArgumentEscaped )
+    {
+        this.doubleQuotedArgumentEscaped = doubleQuotedArgumentEscaped;
+    }
+
+    protected void setDoubleQuotedExecutableEscaped( boolean doubleQuotedExecutableEscaped )
+    {
+        this.doubleQuotedExecutableEscaped = doubleQuotedExecutableEscaped;
+    }
+
+    protected void setSingleQuotedArgumentEscaped( boolean singleQuotedArgumentEscaped )
+    {
+        this.singleQuotedArgumentEscaped = singleQuotedArgumentEscaped;
+    }
+
+    protected void setSingleQuotedExecutableEscaped( boolean singleQuotedExecutableEscaped )
+    {
+        this.singleQuotedExecutableEscaped = singleQuotedExecutableEscaped;
+    }
+
 }

Added: maven/shared/trunk/maven-shared-utils/src/test/java/org/apache/maven/shared/utils/cli/CommandLineUtilsTest.java
URL: http://svn.apache.org/viewvc/maven/shared/trunk/maven-shared-utils/src/test/java/org/apache/maven/shared/utils/cli/CommandLineUtilsTest.java?rev=1378754&view=auto
==============================================================================
--- maven/shared/trunk/maven-shared-utils/src/test/java/org/apache/maven/shared/utils/cli/CommandLineUtilsTest.java (added)
+++ maven/shared/trunk/maven-shared-utils/src/test/java/org/apache/maven/shared/utils/cli/CommandLineUtilsTest.java Wed Aug 29 22:06:47 2012
@@ -0,0 +1,123 @@
+package org.apache.maven.shared.utils.cli;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+
+import junit.framework.TestCase;
+import org.apache.maven.shared.utils.Os;
+
+import java.util.Arrays;
+import java.util.Locale;
+import java.util.Properties;
+
+@SuppressWarnings( { "JavaDoc", "deprecation" } )
+public class CommandLineUtilsTest
+    extends TestCase
+{
+
+    public void testQuoteArguments()
+    {
+        try
+        {
+            String result = CommandLineUtils.quote( "Hello" );
+            System.out.println( result );
+            assertEquals( "Hello", result );
+            result = CommandLineUtils.quote( "Hello World" );
+            System.out.println( result );
+            assertEquals( "\"Hello World\"", result );
+            result = CommandLineUtils.quote( "\"Hello World\"" );
+            System.out.println( result );
+            assertEquals( "\'\"Hello World\"\'", result );
+        }
+        catch ( Exception e )
+        {
+            fail( e.getMessage() );
+        }
+        try
+        {
+            CommandLineUtils.quote( "\"Hello \'World\'\'" );
+            fail();
+        }
+        catch ( Exception e )
+        {
+        }
+    }
+
+    /**
+     * Tests that case-insensitive environment variables are normalized to upper case.
+     */
+    public void testGetSystemEnvVarsCaseInsensitive()
+        throws Exception
+    {
+        Properties vars = CommandLineUtils.getSystemEnvVars( false );
+        for ( Object o : vars.keySet() )
+        {
+            String variable = (String) o;
+            assertEquals( variable.toUpperCase( Locale.ENGLISH ), variable );
+        }
+    }
+
+    /**
+     * Tests that environment variables on Windows are normalized to upper case. Does nothing on Unix platforms.
+     */
+    public void testGetSystemEnvVarsWindows()
+        throws Exception
+    {
+        if ( !Os.isFamily( Os.FAMILY_WINDOWS ) )
+        {
+            return;
+        }
+        Properties vars = CommandLineUtils.getSystemEnvVars();
+        for ( Object o : vars.keySet() )
+        {
+            String variable = (String) o;
+            assertEquals( variable.toUpperCase( Locale.ENGLISH ), variable );
+        }
+    }
+
+    /**
+     * Tests the splitting of a command line into distinct arguments.
+     */
+    public void testTranslateCommandline()
+        throws Exception
+    {
+        assertCmdLineArgs( new String[] {}, null );
+        assertCmdLineArgs( new String[] {}, "" );
+
+        assertCmdLineArgs( new String[] { "foo", "bar" }, "foo bar" );
+        assertCmdLineArgs( new String[] { "foo", "bar" }, "   foo   bar   " );
+
+        assertCmdLineArgs( new String[] { "foo", " double quotes ", "bar" }, "foo \" double quotes \" bar" );
+        assertCmdLineArgs( new String[] { "foo", " single quotes ", "bar" }, "foo ' single quotes ' bar" );
+
+        assertCmdLineArgs( new String[] { "foo", " \" ", "bar" }, "foo ' \" ' bar" );
+        assertCmdLineArgs( new String[] { "foo", " ' ", "bar" }, "foo \" ' \" bar" );
+    }
+
+    private void assertCmdLineArgs( String[] expected, String cmdLine )
+        throws Exception
+    {
+        String[] actual = CommandLineUtils.translateCommandline( cmdLine );
+        assertNotNull( actual );
+        assertEquals( expected.length, actual.length );
+        assertEquals( Arrays.asList( expected ), Arrays.asList( actual ) );
+    }
+
+}

Propchange: maven/shared/trunk/maven-shared-utils/src/test/java/org/apache/maven/shared/utils/cli/CommandLineUtilsTest.java
------------------------------------------------------------------------------
    svn:eol-style = native