Coverage Report - org.codehaus.plexus.util.cli.Commandline
 
Classes in this File Line Coverage Branch Coverage Complexity
Commandline
0%
0/169
0%
0/89
2,649
Commandline$Argument
0%
0/14
0%
0/4
2,649
Commandline$Marker
0%
0/10
0%
0/6
2,649
 
 1  
 package org.codehaus.plexus.util.cli;
 2  
 
 3  
 /*
 4  
  * Licensed to the Apache Software Foundation (ASF) under one
 5  
  * or more contributor license agreements.  See the NOTICE file
 6  
  * distributed with this work for additional information
 7  
  * regarding copyright ownership.  The ASF licenses this file
 8  
  * to you under the Apache License, Version 2.0 (the
 9  
  * "License"); you may not use this file except in compliance
 10  
  * with the License.  You may obtain a copy of the License at
 11  
  *
 12  
  *   http://www.apache.org/licenses/LICENSE-2.0
 13  
  *
 14  
  * Unless required by applicable law or agreed to in writing,
 15  
  * software distributed under the License is distributed on an
 16  
  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 17  
  * KIND, either express or implied.  See the License for the
 18  
  * specific language governing permissions and limitations
 19  
  * under the License.
 20  
  */
 21  
 
 22  
 import org.codehaus.plexus.util.cli.shell.Shell;
 23  
 import org.codehaus.plexus.util.cli.shell.CmdShell;
 24  
 import org.codehaus.plexus.util.cli.shell.CommandShell;
 25  
 
 26  
 import java.io.File;
 27  
 import java.io.IOException;
 28  
 import java.util.ArrayList;
 29  
 import java.util.Arrays;
 30  
 import java.util.Iterator;
 31  
 import java.util.List;
 32  
 import java.util.Properties;
 33  
 import java.util.StringTokenizer;
 34  
 import java.util.Vector;
 35  
 
 36  
 /**
 37  
  * <p/>
 38  
  * Commandline objects help handling command lines specifying processes to
 39  
  * execute.
 40  
  * </p>
 41  
  * <p/>
 42  
  * The class can be used to define a command line as nested elements or as a
 43  
  * helper to define a command line by an application.
 44  
  * </p>
 45  
  * <p/>
 46  
  * <code>
 47  
  * &lt;someelement&gt;<br>
 48  
  * &nbsp;&nbsp;&lt;acommandline executable="/executable/to/run"&gt;<br>
 49  
  * &nbsp;&nbsp;&nbsp;&nbsp;&lt;argument value="argument 1" /&gt;<br>
 50  
  * &nbsp;&nbsp;&nbsp;&nbsp;&lt;argument line="argument_1 argument_2 argument_3" /&gt;<br>
 51  
  * &nbsp;&nbsp;&nbsp;&nbsp;&lt;argument value="argument 4" /&gt;<br>
 52  
  * &nbsp;&nbsp;&lt;/acommandline&gt;<br>
 53  
  * &lt;/someelement&gt;<br>
 54  
  * </code>
 55  
  * </p>
 56  
  * <p/>
 57  
  * The element <code>someelement</code> must provide a method
 58  
  * <code>createAcommandline</code> which returns an instance of this class.
 59  
  * </p>
 60  
  *
 61  
  * @author thomas.haas@softwired-inc.com
 62  
  * @author <a href="mailto:stefan.bodewig@epost.de">Stefan Bodewig</a>
 63  
  */
 64  
 public class Commandline
 65  
     implements Cloneable
 66  
 {
 67  
     protected static final String OS_NAME = "os.name";
 68  
 
 69  
     protected static final String WINDOWS = "Windows";
 70  
 
 71  0
     protected String executable = null;
 72  
 
 73  0
     protected Vector arguments = new Vector();
 74  
 
 75  0
     protected Vector envVars = new Vector();
 76  
 
 77  0
     private File workingDir = null;
 78  
 
 79  0
     private long pid = -1;
 80  
 
 81  
     private Shell shell;
 82  
 
 83  
     /**
 84  
      * Create a new command line object.
 85  
      * Shell is autodetected from operating system
 86  
      *
 87  
      * @param toProcess
 88  
      */
 89  
     public Commandline( String toProcess )
 90  
     {
 91  0
         super();
 92  0
         setDefaultShell();
 93  0
         String[] tmp = new String[0];
 94  
         try
 95  
         {
 96  0
             tmp = translateCommandline( toProcess );
 97  
         }
 98  0
         catch ( Exception e )
 99  
         {
 100  0
             System.err.println( "Error translating Commandline." );
 101  0
         }
 102  0
         if ( tmp != null && tmp.length > 0 )
 103  
         {
 104  0
             setExecutable( tmp[0] );
 105  0
             for ( int i = 1; i < tmp.length; i++ )
 106  
             {
 107  0
                 createArgument().setValue( tmp[i] );
 108  
             }
 109  
         }
 110  0
     }
 111  
 
 112  
     /**
 113  
      * Create a new command line object.
 114  
      * Shell is autodetected from operating system
 115  
      */
 116  
     public Commandline()
 117  
     {
 118  0
         super();
 119  0
         setDefaultShell();
 120  0
     }
 121  
 
 122  
     public long getPid()
 123  
     {
 124  0
         if ( pid == -1 )
 125  
         {
 126  0
             pid = Long.parseLong( String.valueOf( System.currentTimeMillis() ) );
 127  
         }
 128  
 
 129  0
         return pid;
 130  
     }
 131  
 
 132  
     public void setPid( long pid )
 133  
     {
 134  0
         this.pid = pid;
 135  0
     }
 136  
 
 137  
     /**
 138  
      * Used for nested xml command line definitions.
 139  
      */
 140  0
     public static class Argument
 141  
     {
 142  
 
 143  
         private String[] parts;
 144  
 
 145  
         /**
 146  
          * Sets a single commandline argument.
 147  
          *
 148  
          * @param value a single commandline argument.
 149  
          */
 150  
         public void setValue( String value )
 151  
         {
 152  0
             if ( value != null )
 153  
             {
 154  0
                 parts = new String[]{value};
 155  
             }
 156  0
         }
 157  
 
 158  
         /**
 159  
          * Line to split into several commandline arguments.
 160  
          *
 161  
          * @param line line to split into several commandline arguments
 162  
          */
 163  
         public void setLine( String line )
 164  
         {
 165  0
             if ( line == null )
 166  
             {
 167  0
                 return;
 168  
             }
 169  
             try
 170  
             {
 171  0
                 parts = translateCommandline( line );
 172  
             }
 173  0
             catch ( Exception e )
 174  
             {
 175  0
                 System.err.println( "Error translating Commandline." );
 176  0
             }
 177  0
         }
 178  
 
 179  
         /**
 180  
          * Sets a single commandline argument to the absolute filename
 181  
          * of the given file.
 182  
          *
 183  
          * @param value a single commandline argument.
 184  
          */
 185  
         public void setFile( File value )
 186  
         {
 187  0
             parts = new String[]{value.getAbsolutePath()};
 188  0
         }
 189  
 
 190  
         /**
 191  
          * Returns the parts this Argument consists of.
 192  
          */
 193  
         public String[] getParts()
 194  
         {
 195  0
             return parts;
 196  
         }
 197  
     }
 198  
 
 199  
     /**
 200  
      * Class to keep track of the position of an Argument.
 201  
      */
 202  
     // <p>This class is there to support the srcfile and targetfile
 203  
     // elements of &lt;execon&gt; and &lt;transform&gt; - don't know
 204  
     // whether there might be additional use cases.</p> --SB
 205  
     public class Marker
 206  
     {
 207  
 
 208  
         private int position;
 209  
 
 210  0
         private int realPos = -1;
 211  
 
 212  
         Marker( int position )
 213  0
         {
 214  0
             this.position = position;
 215  0
         }
 216  
 
 217  
         /**
 218  
          * Return the number of arguments that preceeded this marker.
 219  
          * <p/>
 220  
          * <p>The name of the executable - if set - is counted as the
 221  
          * very first argument.</p>
 222  
          */
 223  
         public int getPosition()
 224  
         {
 225  0
             if ( realPos == -1 )
 226  
             {
 227  0
                 realPos = ( executable == null ? 0 : 1 );
 228  0
                 for ( int i = 0; i < position; i++ )
 229  
                 {
 230  0
                     Argument arg = (Argument) arguments.elementAt( i );
 231  0
                     realPos += arg.getParts().length;
 232  
                 }
 233  
             }
 234  0
             return realPos;
 235  
         }
 236  
     }
 237  
 
 238  
 
 239  
     /**
 240  
      * <p>Sets the shell or command-line interpretor for the detected operating system,
 241  
      * and the shell arguments.</p>
 242  
      */
 243  
     private void setDefaultShell()
 244  
     {
 245  0
         String os = System.getProperty( OS_NAME );
 246  
 
 247  
         //If this is windows set the shell to command.com or cmd.exe with correct arguments.
 248  0
         if ( os.indexOf( WINDOWS ) > -1 )
 249  
         {
 250  0
             if ( os.indexOf( "95" ) > -1 || os.indexOf( "98" ) > -1 || os.indexOf( "Me" ) > -1 )
 251  
             {
 252  0
                 setShell( new CommandShell() );
 253  
             }
 254  
             else
 255  
             {
 256  0
                 setShell( new CmdShell() );
 257  
             }
 258  
         }
 259  0
     }
 260  
 
 261  
     /**
 262  
      * Creates an argument object.
 263  
      * <p/>
 264  
      * <p>Each commandline object has at most one instance of the
 265  
      * argument class.  This method calls
 266  
      * <code>this.createArgument(false)</code>.</p>
 267  
      *
 268  
      * @return the argument object.
 269  
      * @see #createArgument(boolean)
 270  
      */
 271  
     public Argument createArgument()
 272  
     {
 273  0
         return this.createArgument( false );
 274  
     }
 275  
 
 276  
     /**
 277  
      * Creates an argument object and adds it to our list of args.
 278  
      * <p/>
 279  
      * <p>Each commandline object has at most one instance of the
 280  
      * argument class.</p>
 281  
      *
 282  
      * @param insertAtStart if true, the argument is inserted at the
 283  
      *                      beginning of the list of args, otherwise it is appended.
 284  
      */
 285  
     public Argument createArgument( boolean insertAtStart )
 286  
     {
 287  0
         Argument argument = new Argument();
 288  0
         if ( insertAtStart )
 289  
         {
 290  0
             arguments.insertElementAt( argument, 0 );
 291  
         }
 292  
         else
 293  
         {
 294  0
             arguments.addElement( argument );
 295  
         }
 296  0
         return argument;
 297  
     }
 298  
 
 299  
     /**
 300  
      * Sets the executable to run.
 301  
      */
 302  
     public void setExecutable( String executable )
 303  
     {
 304  0
         if ( executable == null || executable.length() == 0 )
 305  
         {
 306  0
             return;
 307  
         }
 308  0
         this.executable = executable.replace( '/', File.separatorChar ).replace( '\\', File.separatorChar );
 309  0
     }
 310  
 
 311  
     public String getExecutable()
 312  
     {
 313  0
         return executable;
 314  
     }
 315  
 
 316  
     public void addArguments( String[] line )
 317  
     {
 318  0
         for ( int i = 0; i < line.length; i++ )
 319  
         {
 320  0
             createArgument().setValue( line[i] );
 321  
         }
 322  0
     }
 323  
 
 324  
     /**
 325  
      * Add an environment variable
 326  
      */
 327  
     public void addEnvironment( String name,
 328  
                                 String value )
 329  
     {
 330  0
         envVars.add( name + "=" + value );
 331  0
     }
 332  
 
 333  
     /**
 334  
      * Add system environment variables
 335  
      */
 336  
     public void addSystemEnvironment()
 337  
         throws Exception
 338  
     {
 339  0
         Properties envVars = CommandLineUtils.getSystemEnvVars();
 340  
 
 341  0
         for ( Iterator i = envVars.keySet().iterator(); i.hasNext(); )
 342  
         {
 343  0
             String key = (String) i.next();
 344  
 
 345  0
             addEnvironment( key, envVars.getProperty( key ) );
 346  
         }
 347  0
     }
 348  
 
 349  
     /**
 350  
      * Return the list of environment variables
 351  
      */
 352  
     public String[] getEnvironmentVariables()
 353  
         throws CommandLineException
 354  
     {
 355  
         try
 356  
         {
 357  0
             addSystemEnvironment();
 358  
         }
 359  0
         catch ( Exception e )
 360  
         {
 361  0
             throw new CommandLineException( "Error setting up environmental variables", e );
 362  0
         }
 363  
 
 364  0
         return (String[]) envVars.toArray( new String[envVars.size()] );
 365  
     }
 366  
 
 367  
     /**
 368  
      * Returns the executable and all defined arguments.
 369  
      */
 370  
     public String[] getCommandline()
 371  
     {
 372  0
         final String[] args = getArguments();
 373  0
         if ( executable == null )
 374  
         {
 375  0
             return args;
 376  
         }
 377  0
         final String[] result = new String[args.length + 1];
 378  0
         result[0] = executable;
 379  0
         System.arraycopy( args, 0, result, 1, args.length );
 380  0
         return result;
 381  
     }
 382  
 
 383  
     /**
 384  
      * Returns the shell, executable and all defined arguments.
 385  
      */
 386  
     public String[] getShellCommandline()
 387  
     {
 388  
 
 389  0
         if ( getShell() == null )
 390  
         {
 391  0
             if ( executable != null )
 392  
             {
 393  0
                 List commandLine = new ArrayList();
 394  0
                 commandLine.add( executable );
 395  0
                 commandLine.addAll( Arrays.asList( getArguments() ) );
 396  0
                 return (String[]) commandLine.toArray( new String[0] );
 397  
             }
 398  
             else
 399  
             {
 400  0
                 return getArguments();
 401  
             }
 402  
 
 403  
         }
 404  
         else
 405  
         {
 406  0
             return (String[]) getShell().getShellCommandLine( executable, getArguments() ).toArray( new String[0] );
 407  
         }
 408  
     }
 409  
 
 410  
     /**
 411  
      * Returns all arguments defined by <code>addLine</code>,
 412  
      * <code>addValue</code> or the argument object.
 413  
      */
 414  
     public String[] getArguments()
 415  
     {
 416  0
         Vector result = new Vector( arguments.size() * 2 );
 417  0
         for ( int i = 0; i < arguments.size(); i++ )
 418  
         {
 419  0
             Argument arg = (Argument) arguments.elementAt( i );
 420  0
             String[] s = arg.getParts();
 421  0
             if ( s != null )
 422  
             {
 423  0
                 for ( int j = 0; j < s.length; j++ )
 424  
                 {
 425  0
                     result.addElement( s[j] );
 426  
                 }
 427  
             }
 428  
         }
 429  
 
 430  0
         String[] res = new String[result.size()];
 431  0
         result.copyInto( res );
 432  0
         return res;
 433  
     }
 434  
 
 435  
     public String toString()
 436  
     {
 437  0
         return toString( getCommandline() );
 438  
     }
 439  
 
 440  
     /**
 441  
      * <p>Put quotes around the given String if necessary.</p>
 442  
      * <p>If the argument doesn't include spaces or quotes, return it
 443  
      * as is. If it contains double quotes, use single quotes - else
 444  
      * surround the argument by double quotes.</p>
 445  
      *
 446  
      * @throws CommandLineException if the argument contains both, single
 447  
      *                              and double quotes.
 448  
      */
 449  
     public static String quoteArgument( String argument )
 450  
         throws CommandLineException
 451  
     {
 452  0
         if ( argument.indexOf( "\"" ) > -1 )
 453  
         {
 454  0
             if ( argument.indexOf( "\'" ) > -1 )
 455  
             {
 456  0
                 throw new CommandLineException( "Can't handle single and double quotes in same argument" );
 457  
             }
 458  
             else
 459  
             {
 460  0
                 return '\'' + argument + '\'';
 461  
             }
 462  
         }
 463  0
         else if ( argument.indexOf( "\'" ) > -1 || argument.indexOf( " " ) > -1 )
 464  
         {
 465  0
             return '\"' + argument + '\"';
 466  
         }
 467  
         else
 468  
         {
 469  0
             return argument;
 470  
         }
 471  
     }
 472  
 
 473  
     public static String toString( String[] line )
 474  
     {
 475  
         // empty path return empty string
 476  0
         if ( line == null || line.length == 0 )
 477  
         {
 478  0
             return "";
 479  
         }
 480  
 
 481  
         // path containing one or more elements
 482  0
         final StringBuffer result = new StringBuffer();
 483  0
         for ( int i = 0; i < line.length; i++ )
 484  
         {
 485  0
             if ( i > 0 )
 486  
             {
 487  0
                 result.append( ' ' );
 488  
             }
 489  
             try
 490  
             {
 491  0
                 result.append( quoteArgument( line[i] ) );
 492  
             }
 493  0
             catch ( Exception e )
 494  
             {
 495  0
                 System.err.println( "Error quoting argument." );
 496  0
             }
 497  
         }
 498  0
         return result.toString();
 499  
     }
 500  
 
 501  
     public static String[] translateCommandline( String toProcess )
 502  
         throws Exception
 503  
     {
 504  0
         if ( toProcess == null || toProcess.length() == 0 )
 505  
         {
 506  0
             return new String[0];
 507  
         }
 508  
 
 509  
         // parse with a simple finite state machine
 510  
 
 511  0
         final int normal = 0;
 512  0
         final int inQuote = 1;
 513  0
         final int inDoubleQuote = 2;
 514  0
         int state = normal;
 515  0
         StringTokenizer tok = new StringTokenizer( toProcess, "\"\' ", true );
 516  0
         Vector v = new Vector();
 517  0
         StringBuffer current = new StringBuffer();
 518  
 
 519  0
         while ( tok.hasMoreTokens() )
 520  
         {
 521  0
             String nextTok = tok.nextToken();
 522  0
             switch ( state )
 523  
             {
 524  
                 case inQuote:
 525  0
                     if ( "\'".equals( nextTok ) )
 526  
                     {
 527  0
                         state = normal;
 528  
                     }
 529  
                     else
 530  
                     {
 531  0
                         current.append( nextTok );
 532  
                     }
 533  0
                     break;
 534  
                 case inDoubleQuote:
 535  0
                     if ( "\"".equals( nextTok ) )
 536  
                     {
 537  0
                         state = normal;
 538  
                     }
 539  
                     else
 540  
                     {
 541  0
                         current.append( nextTok );
 542  
                     }
 543  0
                     break;
 544  
                 default:
 545  0
                     if ( "\'".equals( nextTok ) )
 546  
                     {
 547  0
                         state = inQuote;
 548  
                     }
 549  0
                     else if ( "\"".equals( nextTok ) )
 550  
                     {
 551  0
                         state = inDoubleQuote;
 552  
                     }
 553  0
                     else if ( " ".equals( nextTok ) )
 554  
                     {
 555  0
                         if ( current.length() != 0 )
 556  
                         {
 557  0
                             v.addElement( current.toString() );
 558  0
                             current.setLength( 0 );
 559  
                         }
 560  
                     }
 561  
                     else
 562  
                     {
 563  0
                         current.append( nextTok );
 564  
                     }
 565  0
                     break;
 566  
             }
 567  
         }
 568  
 
 569  0
         if ( current.length() != 0 )
 570  
         {
 571  0
             v.addElement( current.toString() );
 572  
         }
 573  
 
 574  0
         if ( state == inQuote || state == inDoubleQuote )
 575  
         {
 576  0
             throw new CommandLineException( "unbalanced quotes in " + toProcess );
 577  
         }
 578  
 
 579  0
         String[] args = new String[v.size()];
 580  0
         v.copyInto( args );
 581  0
         return args;
 582  
     }
 583  
 
 584  
     public int size()
 585  
     {
 586  0
         return getCommandline().length;
 587  
     }
 588  
 
 589  
     public Object clone()
 590  
     {
 591  0
         Commandline c = new Commandline();
 592  0
         c.setExecutable( executable );
 593  0
         c.addArguments( getArguments() );
 594  0
         return c;
 595  
     }
 596  
 
 597  
     /**
 598  
      * Clear out the whole command line.
 599  
      */
 600  
     public void clear()
 601  
     {
 602  0
         executable = null;
 603  0
         arguments.removeAllElements();
 604  0
     }
 605  
 
 606  
     /**
 607  
      * Clear out the arguments but leave the executable in place for another operation.
 608  
      */
 609  
     public void clearArgs()
 610  
     {
 611  0
         arguments.removeAllElements();
 612  0
     }
 613  
 
 614  
     /**
 615  
      * Return a marker.
 616  
      * <p/>
 617  
      * <p>This marker can be used to locate a position on the
 618  
      * commandline - to insert something for example - when all
 619  
      * parameters have been set.</p>
 620  
      */
 621  
     public Marker createMarker()
 622  
     {
 623  0
         return new Marker( arguments.size() );
 624  
     }
 625  
 
 626  
     /**
 627  
      * Sets execution directory.
 628  
      */
 629  
     public void setWorkingDirectory( String path )
 630  
     {
 631  0
         if ( path != null )
 632  
         {
 633  0
             workingDir = new File( path );
 634  
         }
 635  0
     }
 636  
 
 637  
     public File getWorkingDirectory()
 638  
     {
 639  0
         return workingDir;
 640  
     }
 641  
 
 642  
     /**
 643  
      * Executes the command.
 644  
      */
 645  
     public Process execute()
 646  
         throws CommandLineException
 647  
     {
 648  
         Process process;
 649  
 
 650  
         //addEnvironment( "MAVEN_TEST_ENVAR", "MAVEN_TEST_ENVAR_VALUE" );
 651  
 
 652  0
         String[] environment = getEnvironmentVariables();
 653  
 
 654  
         try
 655  
         {
 656  0
             if ( workingDir == null )
 657  
             {
 658  0
                 process = Runtime.getRuntime().exec( getShellCommandline(), environment );
 659  
             }
 660  
             else
 661  
             {
 662  0
                 if ( !workingDir.exists() )
 663  
                 {
 664  0
                     throw new CommandLineException(
 665  
                         "Working directory \"" + workingDir.getPath() + "\" does not exist!" );
 666  
                 }
 667  0
                 else if ( !workingDir.isDirectory() )
 668  
                 {
 669  0
                     throw new CommandLineException(
 670  
                         "Path \"" + workingDir.getPath() + "\" does not specify a directory." );
 671  
                 }
 672  
 
 673  0
                 process = Runtime.getRuntime().exec( getShellCommandline(), environment, workingDir );
 674  
             }
 675  
         }
 676  0
         catch ( IOException ex )
 677  
         {
 678  0
             throw new CommandLineException( "Error while executing process.", ex );
 679  0
         }
 680  
 
 681  0
         return process;
 682  
     }
 683  
 
 684  
     public Properties getSystemEnvVars()
 685  
         throws Exception
 686  
     {
 687  0
         return CommandLineUtils.getSystemEnvVars();
 688  
     }
 689  
 
 690  
     /**
 691  
      * Allows to set the shell to be used in this command line.
 692  
      *
 693  
      * @param shell
 694  
      * @since 1.2
 695  
      */
 696  
     public void setShell( Shell shell )
 697  
     {
 698  0
         this.shell = shell;
 699  0
     }
 700  
 
 701  
     /**
 702  
      * Get the shell to be used in this command line.
 703  
      *
 704  
      * @since 1.2
 705  
      */
 706  
     public Shell getShell()
 707  
     {
 708  0
         return shell;
 709  
     }
 710  
 }