1 package org.codehaus.plexus.util.cli;
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 import java.io.IOException;
20 import java.io.InputStream;
21 import java.util.Locale;
22 import java.util.Map;
23 import java.util.Properties;
24 import java.util.StringTokenizer;
25 import java.util.Vector;
26
27 import org.codehaus.plexus.util.Os;
28 import org.codehaus.plexus.util.StringUtils;
29
30
31
32
33
34 public abstract class CommandLineUtils
35 {
36
37
38
39
40
41
42 public static class StringStreamConsumer
43 implements StreamConsumer
44 {
45
46 private StringBuffer string = new StringBuffer();
47
48 private String ls = System.getProperty( "line.separator" );
49
50 @Override
51 public void consumeLine( String line )
52 {
53 string.append( line ).append( ls );
54 }
55
56 public String getOutput()
57 {
58 return string.toString();
59 }
60
61 }
62
63
64
65
66 private static final long MILLIS_PER_SECOND = 1000L;
67
68
69
70
71 private static final long NANOS_PER_SECOND = 1000000000L;
72
73 public static int executeCommandLine( Commandline cl, StreamConsumer systemOut, StreamConsumer systemErr )
74 throws CommandLineException
75 {
76 return executeCommandLine( cl, null, systemOut, systemErr, 0 );
77 }
78
79 public static int executeCommandLine( Commandline cl, StreamConsumer systemOut, StreamConsumer systemErr,
80 int timeoutInSeconds )
81 throws CommandLineException
82 {
83 return executeCommandLine( cl, null, systemOut, systemErr, timeoutInSeconds );
84 }
85
86 public static int executeCommandLine( Commandline cl, InputStream systemIn, StreamConsumer systemOut,
87 StreamConsumer systemErr )
88 throws CommandLineException
89 {
90 return executeCommandLine( cl, systemIn, systemOut, systemErr, 0 );
91 }
92
93
94
95
96
97
98
99
100
101
102 public static int executeCommandLine( Commandline cl, InputStream systemIn, StreamConsumer systemOut,
103 StreamConsumer systemErr, int timeoutInSeconds )
104 throws CommandLineException
105 {
106 final CommandLineCallable future =
107 executeCommandLineAsCallable( cl, systemIn, systemOut, systemErr, timeoutInSeconds );
108 return future.call();
109 }
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124 public static CommandLineCallable executeCommandLineAsCallable( final Commandline cl, final InputStream systemIn,
125 final StreamConsumer systemOut,
126 final StreamConsumer systemErr,
127 final int timeoutInSeconds )
128 throws CommandLineException
129 {
130 if ( cl == null )
131 {
132 throw new IllegalArgumentException( "cl cannot be null." );
133 }
134
135 final Process p = cl.execute();
136
137 final Thread processHook = new Thread()
138 {
139
140 {
141 this.setName( "CommandLineUtils process shutdown hook" );
142 this.setContextClassLoader( null );
143 }
144
145 @Override
146 public void run()
147 {
148 p.destroy();
149 }
150
151 };
152
153 ShutdownHookUtils.addShutDownHook( processHook );
154
155 return new CommandLineCallable()
156 {
157
158 @Override
159 public Integer call()
160 throws CommandLineException
161 {
162 StreamFeeder inputFeeder = null;
163 StreamPumper outputPumper = null;
164 StreamPumper errorPumper = null;
165 boolean success = false;
166 try
167 {
168 if ( systemIn != null )
169 {
170 inputFeeder = new StreamFeeder( systemIn, p.getOutputStream() );
171 inputFeeder.start();
172 }
173
174 outputPumper = new StreamPumper( p.getInputStream(), systemOut );
175 outputPumper.start();
176
177 errorPumper = new StreamPumper( p.getErrorStream(), systemErr );
178 errorPumper.start();
179
180 int returnValue;
181 if ( timeoutInSeconds <= 0 )
182 {
183 returnValue = p.waitFor();
184 }
185 else
186 {
187 final long now = System.nanoTime();
188 final long timeout = now + NANOS_PER_SECOND * timeoutInSeconds;
189
190 while ( isAlive( p ) && ( System.nanoTime() < timeout ) )
191 {
192
193
194 Thread.sleep( MILLIS_PER_SECOND - 1L );
195 }
196
197 if ( isAlive( p ) )
198 {
199 throw new InterruptedException( String.format( "Process timed out after %d seconds.",
200 timeoutInSeconds ) );
201 }
202
203 returnValue = p.exitValue();
204 }
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227 if ( inputFeeder != null )
228 {
229 inputFeeder.waitUntilDone();
230 }
231
232 outputPumper.waitUntilDone();
233 errorPumper.waitUntilDone();
234
235 if ( inputFeeder != null )
236 {
237 inputFeeder.close();
238 handleException( inputFeeder, "stdin" );
239 }
240
241 outputPumper.close();
242 handleException( outputPumper, "stdout" );
243
244 errorPumper.close();
245 handleException( errorPumper, "stderr" );
246
247 success = true;
248 return returnValue;
249 }
250 catch ( InterruptedException ex )
251 {
252 throw new CommandLineTimeOutException( "Error while executing external command, process killed.",
253 ex );
254
255 }
256 finally
257 {
258 if ( inputFeeder != null )
259 {
260 inputFeeder.disable();
261 }
262 if ( outputPumper != null )
263 {
264 outputPumper.disable();
265 }
266 if ( errorPumper != null )
267 {
268 errorPumper.disable();
269 }
270
271 try
272 {
273 ShutdownHookUtils.removeShutdownHook( processHook );
274 processHook.run();
275 }
276 finally
277 {
278 try
279 {
280 if ( inputFeeder != null )
281 {
282 inputFeeder.close();
283
284 if ( success )
285 {
286 success = false;
287 handleException( inputFeeder, "stdin" );
288 success = true;
289 }
290 }
291 }
292 finally
293 {
294 try
295 {
296 if ( outputPumper != null )
297 {
298 outputPumper.close();
299
300 if ( success )
301 {
302 success = false;
303 handleException( outputPumper, "stdout" );
304 success = true;
305 }
306 }
307 }
308 finally
309 {
310 if ( errorPumper != null )
311 {
312 errorPumper.close();
313
314 if ( success )
315 {
316 handleException( errorPumper, "stderr" );
317 }
318 }
319 }
320 }
321 }
322 }
323 }
324
325 };
326 }
327
328 private static void handleException( final StreamPumper streamPumper, final String streamName )
329 throws CommandLineException
330 {
331 if ( streamPumper.getException() != null )
332 {
333 throw new CommandLineException( String.format( "Failure processing %s.", streamName ),
334 streamPumper.getException() );
335
336 }
337 }
338
339 private static void handleException( final StreamFeeder streamFeeder, final String streamName )
340 throws CommandLineException
341 {
342 if ( streamFeeder.getException() != null )
343 {
344 throw new CommandLineException( String.format( "Failure processing %s.", streamName ),
345 streamFeeder.getException() );
346
347 }
348 }
349
350
351
352
353
354
355
356
357
358
359
360 public static Properties getSystemEnvVars()
361 {
362 return getSystemEnvVars( !Os.isFamily( Os.FAMILY_WINDOWS ) );
363 }
364
365
366
367
368
369
370
371
372
373
374 public static Properties getSystemEnvVars( boolean caseSensitive )
375 {
376 Properties envVars = new Properties();
377 Map<String, String> envs = System.getenv();
378 for ( String key : envs.keySet() )
379 {
380 String value = envs.get( key );
381 if ( !caseSensitive )
382 {
383 key = key.toUpperCase( Locale.ENGLISH );
384 }
385 envVars.put( key, value );
386 }
387 return envVars;
388 }
389
390 public static boolean isAlive( Process p )
391 {
392 if ( p == null )
393 {
394 return false;
395 }
396
397 try
398 {
399 p.exitValue();
400 return false;
401 }
402 catch ( IllegalThreadStateException e )
403 {
404 return true;
405 }
406 }
407
408 public static String[] translateCommandline( String toProcess )
409 throws Exception
410 {
411 if ( ( toProcess == null ) || ( toProcess.length() == 0 ) )
412 {
413 return new String[0];
414 }
415
416
417
418 final int normal = 0;
419 final int inQuote = 1;
420 final int inDoubleQuote = 2;
421 int state = normal;
422 StringTokenizer tok = new StringTokenizer( toProcess, "\"\' ", true );
423 Vector<String> v = new Vector<String>();
424 StringBuilder current = new StringBuilder();
425
426 while ( tok.hasMoreTokens() )
427 {
428 String nextTok = tok.nextToken();
429 switch ( state )
430 {
431 case inQuote:
432 if ( "\'".equals( nextTok ) )
433 {
434 state = normal;
435 }
436 else
437 {
438 current.append( nextTok );
439 }
440 break;
441 case inDoubleQuote:
442 if ( "\"".equals( nextTok ) )
443 {
444 state = normal;
445 }
446 else
447 {
448 current.append( nextTok );
449 }
450 break;
451 default:
452 if ( "\'".equals( nextTok ) )
453 {
454 state = inQuote;
455 }
456 else if ( "\"".equals( nextTok ) )
457 {
458 state = inDoubleQuote;
459 }
460 else if ( " ".equals( nextTok ) )
461 {
462 if ( current.length() != 0 )
463 {
464 v.addElement( current.toString() );
465 current.setLength( 0 );
466 }
467 }
468 else
469 {
470 current.append( nextTok );
471 }
472 break;
473 }
474 }
475
476 if ( current.length() != 0 )
477 {
478 v.addElement( current.toString() );
479 }
480
481 if ( ( state == inQuote ) || ( state == inDoubleQuote ) )
482 {
483 throw new CommandLineException( "unbalanced quotes in " + toProcess );
484 }
485
486 String[] args = new String[v.size()];
487 v.copyInto( args );
488 return args;
489 }
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506 @Deprecated
507 @SuppressWarnings( { "JavaDoc", "deprecation" } )
508 public static String quote( String argument )
509 throws CommandLineException
510 {
511 return quote( argument, false, false, true );
512 }
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530 @Deprecated
531 @SuppressWarnings( { "JavaDoc", "UnusedDeclaration", "deprecation" } )
532 public static String quote( String argument, boolean wrapExistingQuotes )
533 throws CommandLineException
534 {
535 return quote( argument, false, false, wrapExistingQuotes );
536 }
537
538
539
540
541
542
543
544
545
546
547
548
549 @Deprecated
550 @SuppressWarnings( { "JavaDoc" } )
551 public static String quote( String argument, boolean escapeSingleQuotes, boolean escapeDoubleQuotes,
552 boolean wrapExistingQuotes )
553 throws CommandLineException
554 {
555 if ( argument.contains( "\"" ) )
556 {
557 if ( argument.contains( "\'" ) )
558 {
559 throw new CommandLineException( "Can't handle single and double quotes in same argument" );
560 }
561 else
562 {
563 if ( escapeSingleQuotes )
564 {
565 return "\\\'" + argument + "\\\'";
566 }
567 else if ( wrapExistingQuotes )
568 {
569 return '\'' + argument + '\'';
570 }
571 }
572 }
573 else if ( argument.contains( "\'" ) )
574 {
575 if ( escapeDoubleQuotes )
576 {
577 return "\\\"" + argument + "\\\"";
578 }
579 else if ( wrapExistingQuotes )
580 {
581 return '\"' + argument + '\"';
582 }
583 }
584 else if ( argument.contains( " " ) )
585 {
586 if ( escapeDoubleQuotes )
587 {
588 return "\\\"" + argument + "\\\"";
589 }
590 else
591 {
592 return '\"' + argument + '\"';
593 }
594 }
595
596 return argument;
597 }
598
599 public static String toString( String[] line )
600 {
601
602 if ( ( line == null ) || ( line.length == 0 ) )
603 {
604 return "";
605 }
606
607
608 final StringBuilder result = new StringBuilder();
609 for ( int i = 0; i < line.length; i++ )
610 {
611 if ( i > 0 )
612 {
613 result.append( ' ' );
614 }
615 try
616 {
617 result.append( StringUtils.quoteAndEscape( line[i], '\"' ) );
618 }
619 catch ( Exception e )
620 {
621 System.err.println( "Error quoting argument: " + e.getMessage() );
622 }
623 }
624 return result.toString();
625 }
626
627 }