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