content: /*******************************************************************************
  * Copyright (c) 2012 Luaj.org. All rights reserved.
  *
  * Permission is hereby granted, free of charge, to any person obtaining a copy
  * of this software and associated documentation files (the "Software"), to deal
  * in the Software without restriction, including without limitation the rights
  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  * copies of the Software, and to permit persons to whom the Software is
  * furnished to do so, subject to the following conditions:
  *
  * The above copyright notice and this permission notice shall be included in
  * all copies or substantial portions of the Software.
  *
  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  * THE SOFTWARE.
  ******************************************************************************/
  package org.luaj.vm2.lib.jse;
  
  import java.io.IOException;
  import java.io.InputStream;
  import java.io.OutputStream;
  
  /** Analog of Process that pipes input and output to client-specified streams.
   */
  public class JseProcess {
  
  	final Process process;
  	final Thread input,output,error;
  
  	/** Construct a process around a command, with specified streams to redirect input and output to.
  	 *
  	 * @param cmd The command to execute, including arguments, if any
  	 * @param stdin Optional InputStream to read from as process input, or null if input is not needed.
  	 * @param stdout Optional OutputStream to copy process output to, or null if output is ignored.
  	 * @param stderr Optinoal OutputStream to copy process stderr output to, or null if output is ignored.
  	 * @throws IOException If the system process could not be created.
  	 * @see Process
  	 */
  	public JseProcess(String[] cmd, InputStream stdin, OutputStream stdout, OutputStream stderr) throws IOException {
  		this(Runtime.getRuntime().exec(cmd), stdin, stdout, stderr);
  	}
  
  	/** Construct a process around a command, with specified streams to redirect input and output to.
  	 *
  	 * @param cmd The command to execute, including arguments, if any
  	 * @param stdin Optional InputStream to read from as process input, or null if input is not needed.
  	 * @param stdout Optional OutputStream to copy process output to, or null if output is ignored.
  	 * @param stderr Optinoal OutputStream to copy process stderr output to, or null if output is ignored.
  	 * @throws IOException If the system process could not be created.
  	 * @see Process
  	 */
  	public JseProcess(String cmd, InputStream stdin, OutputStream stdout, OutputStream stderr) throws IOException {
  		this(Runtime.getRuntime().exec(cmd), stdin, stdout, stderr);
  	}
  
  	private JseProcess(Process process, InputStream stdin, OutputStream stdout, OutputStream stderr) {
  		this.process = process;
  		input = stdin == null? null: copyBytes(stdin, process.getOutputStream(), null, process.getOutputStream());
  		output = stdout == null? null: copyBytes(process.getInputStream(), stdout, process.getInputStream(), null);
  		error = stderr == null? null: copyBytes(process.getErrorStream(), stderr, process.getErrorStream(), null);
  	}
  
  	/** Get the exit value of the process. */
  	public int exitValue() {
  		return process.exitValue();
  	}
  
  	/** Wait for the process to complete, and all pending output to finish.
  	 * @return The exit status.
  	 * @throws InterruptedException
  	 */
  	public int waitFor() throws InterruptedException {
  		int r = process.waitFor();
  		if (input != null)
  			input.join();
  		if (output != null)
  			output.join();
  		if (error != null)
  			error.join();
  		process.destroy();
  		return r;
  	}
  
  	/** Create a thread to copy bytes from input to output. */
  	private Thread copyBytes(final InputStream input,
  			final OutputStream output, final InputStream ownedInput,
  			final OutputStream ownedOutput) {
  		Thread t = (new Thread() {
  			public void run() {
  				try {
  					byte[] buf = new byte[1024];
  					int r;
  					try {
  						while ((r = input.read(buf)) >= 0) {
  							output.write(buf, 0, r);
  						}
  					} finally {
  						if (ownedInput != null)
  							ownedInput.close();
  						if (ownedOutput != null)
  							ownedOutput.close();
  					}
  				} catch (IOException e) {
  					e.printStackTrace();
  				}
  			}
  		});
  		t.start();
  		return t;
  	}
  }
  