View Javadoc
1   package org.codehaus.plexus.util.cli.shell;
2   
3   /*
4    * Copyright The Codehaus Foundation.
5    *
6    * Licensed under the Apache License, Version 2.0 (the "License");
7    * you may not use this file except in compliance with the License.
8    * You may obtain a copy of the License at
9    *
10   *     http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing, software
13   * distributed under the License is distributed on an "AS IS" BASIS,
14   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15   * See the License for the specific language governing permissions and
16   * limitations under the License.
17   */
18  
19  import org.codehaus.plexus.util.StringUtils;
20  
21  import java.io.File;
22  import java.util.ArrayList;
23  import java.util.Arrays;
24  import java.util.List;
25  
26  /**
27   * <p>
28   * Class that abstracts the Shell functionality, with subclasses for shells that behave particularly, like
29   * <ul>
30   * <li><code>command.com</code></li>
31   * <li><code>cmd.exe</code></li>
32   * </ul>
33   * </p>
34   *
35   * @author <a href="mailto:carlos@apache.org">Carlos Sanchez</a>
36   * @since 1.2
37   * @version $Id$
38   */
39  public class Shell
40      implements Cloneable
41  {
42      private static final char[] DEFAULT_QUOTING_TRIGGER_CHARS = { ' ' };
43  
44      private String shellCommand;
45  
46      private List<String> shellArgs = new ArrayList<String>();
47  
48      private boolean quotedArgumentsEnabled = true;
49  
50      private boolean unconditionallyQuote = false;
51  
52      private String executable;
53  
54      private String workingDir;
55  
56      private boolean quotedExecutableEnabled = true;
57  
58      private boolean doubleQuotedArgumentEscaped = false;
59  
60      private boolean singleQuotedArgumentEscaped = false;
61  
62      private boolean doubleQuotedExecutableEscaped = false;
63  
64      private boolean singleQuotedExecutableEscaped = false;
65  
66      private char argQuoteDelimiter = '\"';
67  
68      private char exeQuoteDelimiter = '\"';
69  
70      private String argumentEscapePattern = "\\%s";
71  
72      /**
73       * Toggle unconditional quoting
74       *
75       * @param unconditionallyQuote
76       */
77      public void setUnconditionalQuoting( boolean unconditionallyQuote )
78      {
79          this.unconditionallyQuote = unconditionallyQuote;
80      }
81  
82      /**
83       * Set the command to execute the shell (eg. COMMAND.COM, /bin/bash,...)
84       *
85       * @param shellCommand
86       */
87      public void setShellCommand( String shellCommand )
88      {
89          this.shellCommand = shellCommand;
90      }
91  
92      /**
93       * Get the command to execute the shell
94       *
95       * @return
96       */
97      public String getShellCommand()
98      {
99          return shellCommand;
100     }
101 
102     /**
103      * Set the shell arguments when calling a command line (not the executable arguments) (eg. /X /C for CMD.EXE)
104      *
105      * @param shellArgs
106      */
107     public void setShellArgs( String[] shellArgs )
108     {
109         this.shellArgs.clear();
110         this.shellArgs.addAll( Arrays.asList( shellArgs ) );
111     }
112 
113     /**
114      * Get the shell arguments
115      *
116      * @return
117      */
118     public String[] getShellArgs()
119     {
120         if ( ( shellArgs == null ) || shellArgs.isEmpty() )
121         {
122             return null;
123         }
124         else
125         {
126             return (String[]) shellArgs.toArray( new String[shellArgs.size()] );
127         }
128     }
129 
130     /**
131      * Get the command line for the provided executable and arguments in this shell
132      *
133      * @param executable executable that the shell has to call
134      * @param arguments arguments for the executable, not the shell
135      * @return List with one String object with executable and arguments quoted as needed
136      */
137     public List<String> getCommandLine( String executable, String[] arguments )
138     {
139         return getRawCommandLine( executable, arguments );
140     }
141 
142     protected String quoteOneItem( String inputString, boolean isExecutable )
143     {
144         char[] escapeChars = getEscapeChars( isSingleQuotedExecutableEscaped(), isDoubleQuotedExecutableEscaped() );
145         return StringUtils.quoteAndEscape( inputString,
146                                            isExecutable ? getExecutableQuoteDelimiter() : getArgumentQuoteDelimiter(),
147                                            escapeChars, getQuotingTriggerChars(), '\\', unconditionallyQuote );
148     }
149 
150     protected List<String> getRawCommandLine( String executable, String[] arguments )
151     {
152         List<String> commandLine = new ArrayList<String>();
153         StringBuilder sb = new StringBuilder();
154 
155         if ( executable != null )
156         {
157             String preamble = getExecutionPreamble();
158             if ( preamble != null )
159             {
160                 sb.append( preamble );
161             }
162 
163             if ( isQuotedExecutableEnabled() )
164             {
165                 sb.append( quoteOneItem( getOriginalExecutable(), true ) );
166             }
167             else
168             {
169                 sb.append( getExecutable() );
170             }
171         }
172         for ( String argument : arguments )
173         {
174             if ( sb.length() > 0 )
175             {
176                 sb.append( " " );
177             }
178 
179             if ( isQuotedArgumentsEnabled() )
180             {
181                 sb.append( quoteOneItem( argument, false ) );
182             }
183             else
184             {
185                 sb.append( argument );
186             }
187         }
188 
189         commandLine.add( sb.toString() );
190 
191         return commandLine;
192     }
193 
194     protected char[] getQuotingTriggerChars()
195     {
196         return DEFAULT_QUOTING_TRIGGER_CHARS;
197     }
198 
199     protected String getExecutionPreamble()
200     {
201         return null;
202     }
203 
204     protected char[] getEscapeChars( boolean includeSingleQuote, boolean includeDoubleQuote )
205     {
206         StringBuilder buf = new StringBuilder( 2 );
207         if ( includeSingleQuote )
208         {
209             buf.append( '\'' );
210         }
211 
212         if ( includeDoubleQuote )
213         {
214             buf.append( '\"' );
215         }
216 
217         char[] result = new char[buf.length()];
218         buf.getChars( 0, buf.length(), result, 0 );
219 
220         return result;
221     }
222 
223     protected boolean isDoubleQuotedArgumentEscaped()
224     {
225         return doubleQuotedArgumentEscaped;
226     }
227 
228     protected boolean isSingleQuotedArgumentEscaped()
229     {
230         return singleQuotedArgumentEscaped;
231     }
232 
233     protected boolean isDoubleQuotedExecutableEscaped()
234     {
235         return doubleQuotedExecutableEscaped;
236     }
237 
238     protected boolean isSingleQuotedExecutableEscaped()
239     {
240         return singleQuotedExecutableEscaped;
241     }
242 
243     protected void setArgumentQuoteDelimiter( char argQuoteDelimiter )
244     {
245         this.argQuoteDelimiter = argQuoteDelimiter;
246     }
247 
248     protected char getArgumentQuoteDelimiter()
249     {
250         return argQuoteDelimiter;
251     }
252 
253     protected void setExecutableQuoteDelimiter( char exeQuoteDelimiter )
254     {
255         this.exeQuoteDelimiter = exeQuoteDelimiter;
256     }
257 
258     protected char getExecutableQuoteDelimiter()
259     {
260         return exeQuoteDelimiter;
261     }
262 
263     protected void setArgumentEscapePattern( String argumentEscapePattern )
264     {
265         this.argumentEscapePattern = argumentEscapePattern;
266     }
267 
268     protected String getArgumentEscapePattern()
269     {
270         return argumentEscapePattern;
271     }
272 
273     /**
274      * Get the full command line to execute, including shell command, shell arguments, executable and executable
275      * arguments
276      *
277      * @param arguments arguments for the executable, not the shell
278      * @return List of String objects, whose array version is suitable to be used as argument of
279      *         Runtime.getRuntime().exec()
280      */
281     public List<String> getShellCommandLine( String[] arguments )
282     {
283 
284         List<String> commandLine = new ArrayList<String>();
285 
286         if ( getShellCommand() != null )
287         {
288             commandLine.add( getShellCommand() );
289         }
290 
291         if ( getShellArgs() != null )
292         {
293             commandLine.addAll( getShellArgsList() );
294         }
295 
296         commandLine.addAll( getCommandLine( getOriginalExecutable(), arguments ) );
297 
298         return commandLine;
299 
300     }
301 
302     public List<String> getShellArgsList()
303     {
304         return shellArgs;
305     }
306 
307     public void addShellArg( String arg )
308     {
309         shellArgs.add( arg );
310     }
311 
312     public void setQuotedArgumentsEnabled( boolean quotedArgumentsEnabled )
313     {
314         this.quotedArgumentsEnabled = quotedArgumentsEnabled;
315     }
316 
317     public boolean isQuotedArgumentsEnabled()
318     {
319         return quotedArgumentsEnabled;
320     }
321 
322     public void setQuotedExecutableEnabled( boolean quotedExecutableEnabled )
323     {
324         this.quotedExecutableEnabled = quotedExecutableEnabled;
325     }
326 
327     public boolean isQuotedExecutableEnabled()
328     {
329         return quotedExecutableEnabled;
330     }
331 
332     /**
333      * Sets the executable to run.
334      */
335     public void setExecutable( String executable )
336     {
337         if ( ( executable == null ) || ( executable.length() == 0 ) )
338         {
339             return;
340         }
341         this.executable = executable.replace( '/', File.separatorChar ).replace( '\\', File.separatorChar );
342     }
343 
344     public String getExecutable()
345     {
346         return executable;
347     }
348 
349     /**
350      * Sets execution directory.
351      */
352     public void setWorkingDirectory( String path )
353     {
354         if ( path != null )
355         {
356             workingDir = path;
357         }
358     }
359 
360     /**
361      * Sets execution directory.
362      */
363     public void setWorkingDirectory( File workingDir )
364     {
365         if ( workingDir != null )
366         {
367             this.workingDir = workingDir.getAbsolutePath();
368         }
369     }
370 
371     public File getWorkingDirectory()
372     {
373         return workingDir == null ? null : new File( workingDir );
374     }
375 
376     public String getWorkingDirectoryAsString()
377     {
378         return workingDir;
379     }
380 
381     public void clearArguments()
382     {
383         shellArgs.clear();
384     }
385 
386     public Object clone()
387     {
388         Shell shell = new Shell();
389         shell.setExecutable( getExecutable() );
390         shell.setWorkingDirectory( getWorkingDirectory() );
391         shell.setShellArgs( getShellArgs() );
392         return shell;
393     }
394 
395     public String getOriginalExecutable()
396     {
397         return executable;
398     }
399 
400     public List<String> getOriginalCommandLine( String executable, String[] arguments )
401     {
402         return getRawCommandLine( executable, arguments );
403     }
404 
405     protected void setDoubleQuotedArgumentEscaped( boolean doubleQuotedArgumentEscaped )
406     {
407         this.doubleQuotedArgumentEscaped = doubleQuotedArgumentEscaped;
408     }
409 
410     protected void setDoubleQuotedExecutableEscaped( boolean doubleQuotedExecutableEscaped )
411     {
412         this.doubleQuotedExecutableEscaped = doubleQuotedExecutableEscaped;
413     }
414 
415     protected void setSingleQuotedArgumentEscaped( boolean singleQuotedArgumentEscaped )
416     {
417         this.singleQuotedArgumentEscaped = singleQuotedArgumentEscaped;
418     }
419 
420     protected void setSingleQuotedExecutableEscaped( boolean singleQuotedExecutableEscaped )
421     {
422         this.singleQuotedExecutableEscaped = singleQuotedExecutableEscaped;
423     }
424 }