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