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