Start line:  
End line:  

Snippet Preview

Snippet HTML Code

Stack Overflow Questions
BEGIN LICENSE BLOCK ***** Version: EPL 1.0/GPL 2.0/LGPL 2.1 The contents of this file are subject to the Eclipse Public License Version 1.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.eclipse.org/legal/epl-v10.html Software distributed under the License is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the specific language governing rights and limitations under the License. Copyright (C) 2007-2011 JRuby Team <team@jruby.org> Alternatively, the contents of this file may be used under the terms of either of the GNU General Public License Version 2 or later (the "GPL"), or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), in which case the provisions of the GPL or the LGPL are applicable instead of those above. If you wish to allow use of your version of this file only under the terms of either the GPL or the LGPL, and not to allow others to use your version of this file under the terms of the EPL, indicate your decision by deleting the provisions above and replace them with the notice and other provisions required by the GPL or the LGPL. If you do not delete the provisions above, a recipient may use your version of this file under the terms of any one of the EPL, the GPL or the LGPL. END LICENSE BLOCK ***
  
  
  package org.jruby.util;
  
  import static java.lang.System.out;
  
  import java.io.File;
  import java.util.Arrays;
  import java.util.HashMap;
  import java.util.Map;
  import java.util.Set;
  
  import org.jruby.Main;
  import org.jruby.Ruby;
  import org.jruby.RubyIO;
This mess of a class is what happens when all Java gives you is Runtime.getRuntime().exec(). Thanks dude, that really helped.

Author(s):
nicksieger
  
  @SuppressWarnings("deprecation")
  public class ShellLauncher {
      private static final boolean DEBUG = false;
  
      private static final String PATH_ENV = "PATH";
  
      // from MRI -- note the unixy file separators
      private static final String[] DEFAULT_PATH =
          { "/usr/local/bin""/usr/ucb""/usr/bin""/bin" };
  
      private static final String[] WINDOWS_EXE_SUFFIXES =
          { ".exe"".com"".bat"".cmd" }; // the order is important
  
      private static final String[] WINDOWS_INTERNAL_CMDS = {
          "assoc""break""call""cd""chcp",
          "chdir""cls""color""copy""ctty""date""del""dir""echo""endlocal",
          "erase""exit""for""ftype""goto""if""lfnfor""lh""lock""md""mkdir",
          "move""path""pause""popd""prompt""pushd""rd""rem""ren""rename",
          "rmdir""set""setlocal""shift""start""time""title""truename""type",
          "unlock""ver""verify""vol", };
  
      // TODO: better check is needed, with quoting/escaping
     private static final Pattern SHELL_METACHARACTER_PATTERN =
         Pattern.compile("[*?{}\\[\\]<>()~&|$;'`\\\\\"\\n]");
 
     private static final Pattern WIN_ENVVAR_PATTERN = Pattern.compile("%\\w+%");
 
     private static class ScriptThreadProcess extends Process implements Runnable {
         private final String[] argArray;
         private final String[] env;
         private final File pwd;
         private final boolean pipedStreams;
         private final PipedInputStream processOutput;
         private final PipedInputStream processError;
         private final PipedOutputStream processInput;
 
         private RubyInstanceConfig config;
         private Thread processThread;
         private int result;
         private Ruby parentRuntime;
 
         public ScriptThreadProcess(Ruby parentRuntimefinal String[] argArrayfinal String[] envfinal File dir) {
             this(parentRuntimeargArrayenvdirtrue);
         }
 
         public ScriptThreadProcess(Ruby parentRuntimefinal String[] argArrayfinal String[] envfinal File dirfinal boolean pipedStreams) {
             this. = parentRuntime;
             this. = argArray;
             this. = env;
             this. = dir;
             this. = pipedStreams;
             if (pipedStreams) {
                  = new PipedInputStream();
                  = new PipedInputStream();
                  = new PipedOutputStream();
             } else {
                  =  = null;
                  = null;
             }
         }
         public void run() {
             try {
                 this. = (new Main().run()).getStatus();
             } catch (Throwable throwable) {
                 throwable.printStackTrace(this..getError());
                 this. = -1;
             } finally {
                 this..getOutput().close();
                 this..getError().close();
                 try {this..getInput().close();} catch (IOException ioe) {}
             }
         }
 
         private Map<StringStringenvironmentMap(String[] env) {
             Map<StringStringm = new HashMap<StringString>();
             for (int i = 0; i < env.lengthi++) {
                 String[] kv = env[i].split("=", 2);
                 m.put(kv[0], kv[1]);
             }
             return m;
         }
 
         public void start() throws IOException {
              = new RubyInstanceConfig(.getInstanceConfig());
             
             .setEnvironment(environmentMap());
             .setCurrentDirectory(.toString());
             
             if () {
                 .setInput(new PipedInputStream());
                 .setOutput(new PrintStream(new PipedOutputStream()));
                 .setError(new PrintStream(new PipedOutputStream()));
             }
             String procName = "piped";
             if (. > 0) {
                 procName = [0];
             }
              = new Thread(this"ScriptThreadProcess: " + procName);
             .setDaemon(true);
             .start();
         }
 
         public OutputStream getOutputStream() {
             return ;
         }
 
         public InputStream getInputStream() {
             return ;
         }
 
         public InputStream getErrorStream() {
             return ;
         }
 
         public int waitFor() throws InterruptedException {
             .join();
             return ;
         }
 
         public int exitValue() {
             return ;
         }
 
         public void destroy() {
             if () {
                 closeStreams();
             }
             .interrupt();
         }
 
         private void closeStreams() {
             try { .close(); } catch (IOException io) {}
             try { .close(); } catch (IOException io) {}
             try { .close(); } catch (IOException io) {}
         }
     }
 
     public static String[] getCurrentEnv(Ruby runtime) {
         return getCurrentEnv(runtimenull);
     }
 
     private static String[] getCurrentEnv(Ruby runtimeMap mergeEnv) {
         ThreadContext context = runtime.getCurrentContext();
 
         // disable tracing for the dup call below
         boolean traceEnabled = context.isEventHooksEnabled();
         context.setEventHooksEnabled(false);
 
         try {
             // dup for JRUBY-6603 (avoid concurrent modification while we walk it)
             RubyHash hash = (RubyHash)runtime.getObject().getConstant("ENV").dup();
             String[] retary;
 
             if (mergeEnv != null && !mergeEnv.isEmpty()) {
                 ret = new String[hash.size() + mergeEnv.size()];
             } else {
                 ret = new String[hash.size()];
             }
 
             int i=0;
             for(Map.Entry e : (Set<Map.Entry>)hash.directEntrySet()) {
                 // if the key is nil, raise TypeError
                 if (e.getKey() == null) {
                     throw runtime.newTypeError(runtime.getNil(), runtime.getStructClass());
                 }
                 // ignore if the value is nil
                 if (e.getValue() == null) {
                     continue;
                 }
                 ret[i] = e.getKey().toString() + "=" + e.getValue().toString();
                 i++;
             }
             if (mergeEnv != null) {
                 for (Map.Entry e : (Set<Map.Entry>) mergeEnv.entrySet()) {
                     // if the key is nil, raise TypeError
                     if (e.getKey() == null) {
                         throw runtime.newTypeError(runtime.getNil(), runtime.getStructClass());
                     }
                     // ignore if the value is nil
                     if (e.getValue() == null) {
                         continue;
                     }
                     ret[i] = e.getKey().toString() + "=" + e.getValue().toString();
                     i++;
                 }
             }
             
             ary = new String[i];
             System.arraycopy(ret, 0, ary, 0, i);
             return ary;
 
         } finally {
             context.setEventHooksEnabled(traceEnabled);
         }
     }
 
     private static boolean filenameIsPathSearchable(String fnameboolean forExec) {
         boolean isSearchable = true;
         if (fname.startsWith("/")   ||
             fname.startsWith("./")  ||
             fname.startsWith("../") ||
             (forExec && (fname.indexOf("/") != -1))) {
             isSearchable = false;
         }
         if (.) {
             if (fname.startsWith("\\")  ||
                 fname.startsWith(".\\") ||
                 fname.startsWith("..\\") ||
                 ((fname.length() > 2) && fname.startsWith(":",1)) ||
                 (forExec && (fname.indexOf("\\") != -1))) {
                 isSearchable = false;
             }
         }
         return isSearchable;
     }
 
     private static File tryFile(Ruby runtimeString fdirString fname) {
         File pathFile;
         if (fdir == null) {
             pathFile = new File(fname);
         } else {
             pathFile = new File(fdirfname);
         }
 
         if (!pathFile.isAbsolute()) {
             pathFile = new File(runtime.getCurrentDirectory(), pathFile.getPath());
         }
 
         log(runtime"Trying file " + pathFile);
         if (pathFile.exists()) {
             return pathFile;
         } else {
             return null;
         }
     }
 
     private static boolean withExeSuffix(String fname) {
         String lowerCaseFname = fname.toLowerCase();
         for (String suffix : ) {
             if (lowerCaseFname.endsWith(suffix)) {
                 return true;
             }
         }
         return false;
     }
 
     private static File isValidFile(Ruby runtimeString fdirString fnameboolean isExec) {
         File validFile = null;
         if (isExec && .) {
             if (withExeSuffix(fname)) {
                 validFile = tryFile(runtimefdirfname);
             } else {
                 for (String suffix) {
                     validFile = tryFile(runtimefdirfname + suffix);
                     if (validFile != null) {
                         // found a valid file, no need to search further
                         break;
                     }
                 }
             }
         } else {
             validFile = tryFile(runtimefdirfname);
             if (validFile != null) {
                 if (validFile.isDirectory()) {
                     return null;
                 }
                 if (isExec && !runtime.getPosix().stat(validFile.getAbsolutePath()).isExecutable()) {
                     throw runtime.newErrnoEACCESError(validFile.getAbsolutePath());
                 }
             }
         }
         return validFile;
     }
 
     private static File isValidFile(Ruby runtimeString fnameboolean isExec) {
         String fdir = null;
         return isValidFile(runtimefdirfnameisExec);
     }
 
     private static File findPathFile(Ruby runtimeString fnameString[] pathboolean isExec) {
         File pathFile = null;
         boolean doPathSearch = filenameIsPathSearchable(fnameisExec);
         if (doPathSearch) {
             for (String fdirpath) {
                 // NOTE: Jruby's handling of tildes is more complete than
                 //       MRI's, which can't handle user names after the tilde
                 //       when searching the executable path
                 pathFile = isValidFile(runtimefdirfnameisExec);
                 if (pathFile != null) {
                     break;
                 }
             }
         } else {
             pathFile = isValidFile(runtimefnameisExec);
         }
         return pathFile;
     }
 
     private static File findPathExecutable(Ruby runtimeString fname) {
         RubyHash env = (RubyHashruntime.getObject().getConstant("ENV");
         IRubyObject pathObject = env.op_aref(runtime.getCurrentContext(), RubyString.newString(runtime));
         String[] pathNodes = null;
         if (pathObject == null) {
             pathNodes = // ASSUME: not modified by callee
         }
         else {
             String pathSeparator = System.getProperty("path.separator");
             String path = pathObject.toString();
             if (.) {
                 // Windows-specific behavior
                 path = "." + pathSeparator + path;
             }
             pathNodes = path.split(pathSeparator);
         }
         return findPathFile(runtimefnamepathNodestrue);
     }
 
     public static int runAndWait(Ruby runtimeIRubyObject[] rawArgs) {
         return runAndWait(runtimerawArgsruntime.getOutputStream());
     }
 
     public static long[] runAndWaitPid(Ruby runtimeIRubyObject[] rawArgs) {
         return runAndWaitPid(runtimerawArgsruntime.getOutputStream(), true);
     }
 
     public static long runWithoutWait(Ruby runtimeIRubyObject[] rawArgs) {
         return runWithoutWait(runtimerawArgsruntime.getOutputStream());
     }
 
     public static int runExternalAndWait(Ruby runtimeIRubyObject[] rawArgsMap mergeEnv) {
         OutputStream output = runtime.getOutputStream();
         OutputStream error = runtime.getErrorStream();
         InputStream input = runtime.getInputStream();
         Process aProcess = null;
         File pwd = new File(runtime.getCurrentDirectory());
         LaunchConfig cfg = new LaunchConfig(runtimerawArgstrue);
 
         try {
             try {
                 if (cfg.shouldRunInShell()) {
                     log(runtime"Launching with shell");
                     // execute command with sh -c
                     // this does shell expansion of wildcards
                     cfg.verifyExecutableForShell();
                     aProcess = buildProcess(runtimecfg.getExecArgs(), getCurrentEnv(runtimemergeEnv), pwd);
                 } else {
                     log(runtime"Launching directly (no shell)");
                     cfg.verifyExecutableForDirect();
                     aProcess = buildProcess(runtimecfg.getExecArgs(), getCurrentEnv(runtimemergeEnv), pwd);
                 }
             } catch (SecurityException se) {
                 throw runtime.newSecurityError(se.getLocalizedMessage());
             }
             handleStreams(runtimeaProcessinputoutputerror);
             return aProcess.waitFor();
         } catch (IOException e) {
             throw runtime.newIOErrorFromException(e);
         } catch (InterruptedException e) {
             throw runtime.newThreadError("unexpected interrupt");
         }
     }
 
     public static long runExternalWithoutWait(Ruby runtimeIRubyObject envIRubyObject progIRubyObject optionsIRubyObject args) {
         return runExternal(runtimeenvprogoptionsargsfalse);
     }
 
     public static long runExternal(Ruby runtimeIRubyObject envIRubyObject progIRubyObject optionsIRubyObject argsboolean wait) {
         if (env.isNil() || !(env instanceof Map)) {
             env = null;
         }
         
         IRubyObject[] rawArgs = args.convertToArray().toJavaArray();
         
         OutputStream output = runtime.getOutputStream();
         OutputStream error = runtime.getErrorStream();
         InputStream input = runtime.getInputStream();
         
         try {
             Process aProcess = null;
             File pwd = new File(runtime.getCurrentDirectory());
             LaunchConfig cfg = new LaunchConfig(runtimerawArgstrue);
 
             try {
                 if (cfg.shouldRunInShell()) {
                     log(runtime"Launching with shell");
                     // execute command with sh -c
                     // this does shell expansion of wildcards
                     cfg.verifyExecutableForShell();
                     aProcess = buildProcess(runtimecfg.getExecArgs(), getCurrentEnv(runtime, (Map)env), pwd);
                 } else {
                     log(runtime"Launching directly (no shell)");
                     cfg.verifyExecutableForDirect();
                     aProcess = buildProcess(runtimecfg.getExecArgs(), getCurrentEnv(runtime, (Map)env), pwd);
                 }
             } catch (SecurityException se) {
                 throw runtime.newSecurityError(se.getLocalizedMessage());
             }
             
             if (wait) {
                 handleStreams(runtimeaProcessinputoutputerror);
                 try {
                     return aProcess.waitFor();
                 } catch (InterruptedException e) {
                     throw runtime.newThreadError("unexpected interrupt");
                 }
             } else {
                 handleStreamsNonblocking(runtimeaProcessruntime.getOutputStream(), error);
                 return getPidFromProcess(aProcess);
             }
         } catch (IOException e) {
             throw runtime.newIOErrorFromException(e);
         }
     }
 
     public static Process buildProcess(Ruby runtimeString[] argsString[] envFile pwdthrows IOException {
         return runtime.getPosix().newProcessMaker(args)
                 .environment(env)
                 .directory(pwd)
                 .start();
     }
 
     public static long runExternalWithoutWait(Ruby runtimeIRubyObject[] rawArgs) {
         return runWithoutWait(runtimerawArgsruntime.getOutputStream());
     }
 
     public static int execAndWait(Ruby runtimeIRubyObject[] rawArgs) {
         return execAndWait(runtimerawArgs.);
     }
 
     public static int execAndWait(Ruby runtimeIRubyObject[] rawArgsMap mergeEnv) {
         File pwd = new File(runtime.getCurrentDirectory());
         LaunchConfig cfg = new LaunchConfig(runtimerawArgstrue);
 
         if (cfg.shouldRunInProcess()) {
             log(runtime"ExecAndWait in-process");
             try {
                 // exec needs to behave differently in-process, because it's technically
                 // supposed to replace the calling process. So if we're supposed to run
                 // in-process, we allow it to use the default streams and not use
                 // pumpers at all. See JRUBY-2156 and JRUBY-2154.
                 ScriptThreadProcess ipScript = new ScriptThreadProcess(
                         runtimecfg.getExecArgs(), getCurrentEnv(runtimemergeEnv), pwdfalse);
                 ipScript.start();
                 return ipScript.waitFor();
             } catch (IOException e) {
                 throw runtime.newIOErrorFromException(e);
             } catch (InterruptedException e) {
                 throw runtime.newThreadError("unexpected interrupt");
             }
         } else {
             return runExternalAndWait(runtimerawArgsmergeEnv);
         }
     }
 
     public static int runAndWait(Ruby runtimeIRubyObject[] rawArgsOutputStream output) {
         return runAndWait(runtimerawArgsoutputtrue);
     }
 
     public static int runAndWait(Ruby runtimeIRubyObject[] rawArgsOutputStream outputboolean doExecutableSearch) {
         return (int)runAndWaitPid(runtimerawArgsoutputdoExecutableSearch)[0];
     }
 
     public static long[] runAndWaitPid(Ruby runtimeIRubyObject[] rawArgsOutputStream outputboolean doExecutableSearch) {
         OutputStream error = runtime.getErrorStream();
         InputStream input = runtime.getInputStream();
         try {
             Process aProcess = run(runtimerawArgsdoExecutableSearch);
             handleStreams(runtimeaProcessinputoutputerror);
             return new long[] {aProcess.waitFor(), getPidFromProcess(aProcess)};
         } catch (IOException e) {
             throw runtime.newIOErrorFromException(e);
         } catch (InterruptedException e) {
             throw runtime.newThreadError("unexpected interrupt");
         }
     }
 
     private static long runWithoutWait(Ruby runtimeIRubyObject[] rawArgsOutputStream output) {
         OutputStream error = runtime.getErrorStream();
         try {
             Process aProcess = run(runtimerawArgstrue);
             handleStreamsNonblocking(runtimeaProcessoutputerror);
             return getPidFromProcess(aProcess);
         } catch (IOException e) {
             throw runtime.newIOErrorFromException(e);
         }
     }
 
     private static long runExternalWithoutWait(Ruby runtimeIRubyObject[] rawArgsOutputStream output) {
         OutputStream error = runtime.getErrorStream();
         try {
             Process aProcess = run(runtimerawArgstruetrue);
             handleStreamsNonblocking(runtimeaProcessoutputerror);
             return getPidFromProcess(aProcess);
         } catch (IOException e) {
             throw runtime.newIOErrorFromException(e);
         }
     }
 
     public static long getPidFromProcess(Process process) {
         if (process instanceof ScriptThreadProcess) {
             return process.hashCode();
         } else if (process instanceof POpenProcess) {
             return reflectPidFromProcess(((POpenProcess)process).getChild());
         } else {
             return reflectPidFromProcess(process);
         }
     }
     
     private static final Class UNIXProcess;
     private static final Field UNIXProcess_pid;
     private static final Class ProcessImpl;
     private static final Field ProcessImpl_handle;
     private interface PidGetter { public long getPid(Process process); }
     private static final PidGetter PID_GETTER;
     
     static {
         // default PidGetter
         PidGetter pg = new PidGetter() {
             public long getPid(Process process) {
                 return process.hashCode();
             }
         };
         
         Class up = null;
         Field pid = null;
         try {
             up = Class.forName("java.lang.UNIXProcess");
             pid = up.getDeclaredField("pid");
             pid.setAccessible(true);
         } catch (Exception e) {
             // ignore and try windows version
         }
          = up;
          = pid;
 
         Class pi = null;
         Field handle = null;
         try {
             pi = Class.forName("java.lang.ProcessImpl");
             handle = pi.getDeclaredField("handle");
             handle.setAccessible(true);
         } catch (Exception e) {
             // ignore and use hashcode
         }
          = pi;
          = handle;
 
         if ( != null) {
             if ( != null) {
                 // try both
                 pg = new PidGetter() {
                     public long getPid(Process process) {
                         try {
                             if (.isInstance(process)) {
                                 return (Integer).get(process);
                             } else if (.isInstance(process)) {
                                 Long hproc = (Long.get(process);
                                 return WindowsFFI.getKernel32().GetProcessId(hproc);
                             }
                         } catch (Exception e) {
                             // ignore and use hashcode
                         }
                         return process.hashCode();
                     }
                 };
             } else {
                 // just unix
                 pg = new PidGetter() {
                     public long getPid(Process process) {
                         try {
                             if (.isInstance(process)) {
                                 return (Integer).get(process);
                             }
                         } catch (Exception e) {
                             // ignore and use hashcode
                         }
                         return process.hashCode();
                     }
                 };
             }
         } else if ( != null) {
             // just windows
             pg = new PidGetter() {
                 public long getPid(Process process) {
                     try {
                         if (.isInstance(process)) {
                             Long hproc = (Long.get(process);
                             return WindowsFFI.getKernel32().GetProcessId(hproc);
                         }
 
                     } catch (Exception e) {
                         // ignore and use hashcode
                     }
                     return process.hashCode();
                 }
             };
         } else {
             // neither
             pg = new PidGetter() {
                 public long getPid(Process process) {
                     return process.hashCode();
                 }
             };
         }
          = pg;
     }
 
     public static long reflectPidFromProcess(Process process) {
         return .getPid(process);
     }
 
     public static Process run(Ruby runtimeIRubyObject stringthrows IOException {
         return run(runtimenew IRubyObject[] {string}, false);
     }
 
     public static POpenProcess popen(Ruby runtimeIRubyObject stringModeFlags modesthrows IOException {
         return new POpenProcess(popenShared(runtimenew IRubyObject[] {string}, nulltrue), runtimemodes);
     }
 
     public static POpenProcess popen(Ruby runtimeIRubyObject[] stringsMap envModeFlags modesthrows IOException {
         return new POpenProcess(popenShared(runtimestringsenv), runtimemodes);
     }
     
     @Deprecated
     public static POpenProcess popen(Ruby runtimeIRubyObject stringIOOptions modesthrows IOException {
         return new POpenProcess(popenShared(runtimenew IRubyObject[] {string}, nulltrue), runtimemodes);
     }
 
     @Deprecated
     public static POpenProcess popen(Ruby runtimeIRubyObject[] stringsMap envIOOptions modesthrows IOException {
         return new POpenProcess(popenShared(runtimestringsenv), runtimemodes);
     }
 
     public static POpenProcess popen3(Ruby runtimeIRubyObject[] stringsthrows IOException {
         return new POpenProcess(popenShared(runtimestrings));
     }
 
     public static POpenProcess popen3(Ruby runtimeIRubyObject[] stringsboolean addShellthrows IOException {
         return new POpenProcess(popenShared(runtimestringsnulladdShell));
     }
 
     private static Process popenShared(Ruby runtimeIRubyObject[] stringsthrows IOException {
         return popenShared(runtimestringsnull);
     }
 
     private static Process popenShared(Ruby runtimeIRubyObject[] stringsMap envthrows IOException {
         return popenShared(runtimestringsenvfalse);
     }
 
     private static Process popenShared(Ruby runtimeIRubyObject[] stringsMap envboolean addShellthrows IOException {
         String shell = getShell(runtime);
         Process childProcess = null;
         File pwd = new File(runtime.getCurrentDirectory());
 
         try {
             String[] args = parseCommandLine(runtime.getCurrentContext(), runtimestrings);
             boolean useShell = false;
             if (addShellfor (String arg : argsuseShell |= shouldUseShell(arg);
             
             // CON: popen is a case where I think we should just always shell out.
             if (strings.length == 1) {
                 if (useShell) {
                     // single string command, pass to sh to expand wildcards
                     String[] argArray = new String[3];
                     argArray[0] = shell;
                     argArray[1] = shell.endsWith("sh") ? "-c" : "/c";
                     argArray[2] = strings[0].asJavaString();
                     childProcess = buildProcess(runtimeargArraygetCurrentEnv(runtimeenv), pwd);
                 } else {
                     childProcess = buildProcess(runtimeargsgetCurrentEnv(runtimeenv), pwd);
                 }
             } else {
                 if (useShell) {
                     String[] argArray = new String[args.length + 2];
                     argArray[0] = shell;
                     argArray[1] = shell.endsWith("sh") ? "-c" : "/c";
                     System.arraycopy(args, 0, argArray, 2, args.length);
                     childProcess = buildProcess(runtimeargArraygetCurrentEnv(runtimeenv), pwd);
                 } else {
                     // direct invocation of the command
                     childProcess = buildProcess(runtimeargsgetCurrentEnv(runtimeenv), pwd);
                 }
             }
         } catch (SecurityException se) {
             throw runtime.newSecurityError(se.getLocalizedMessage());
         }
 
         return childProcess;
     }

    
Unwrap all filtering streams between the given stream and its actual unfiltered stream. This is primarily to unwrap streams that have buffers that would interfere with interactivity.

Parameters:
filteredStream The stream to unwrap
Returns:
An unwrapped stream, presumably unbuffered
 
     public static OutputStream unwrapBufferedStream(OutputStream filteredStream) {
         if (.return filteredStream;
         while (filteredStream instanceof FilterOutputStream) {
             try {
                 filteredStream = (OutputStream)
                     FieldAccess.getProtectedFieldValue(FilterOutputStream.class,
                         "out"filteredStream);
             } catch (Exception e) {
                 break// break out if we've dug as deep as we can
             }
         }
         return filteredStream;
     }

    
Unwrap all filtering streams between the given stream and its actual unfiltered stream. This is primarily to unwrap streams that have buffers that would interfere with interactivity.

Parameters:
filteredStream The stream to unwrap
Returns:
An unwrapped stream, presumably unbuffered
 
     public static InputStream unwrapBufferedStream(InputStream filteredStream) {
         if (.return filteredStream;
         
         // Java 7+ uses a stream that drains the child on exit, which when
         // unwrapped breaks because the channel gets drained prematurely.
 //        System.out.println("class is :" + filteredStream.getClass().getName());
         if (filteredStream.getClass().getName().indexOf("ProcessPipeInputStream") != 1) {
             return filteredStream;
         }
         
         while (filteredStream instanceof FilterInputStream) {
             try {
                 filteredStream = (InputStream)
                     FieldAccess.getProtectedFieldValue(FilterInputStream.class,
                         "in"filteredStream);
             } catch (Exception e) {
                 break// break out if we've dug as deep as we can
             }
         }
         return filteredStream;
     }
 
     public static class POpenProcess extends Process {
         private final Process child;
         private final boolean waitForChild;
 
         // real stream references, to keep them from being GCed prematurely
         private InputStream realInput;
         private OutputStream realOutput;
         private InputStream realInerr;
 
         private InputStream input;
         private OutputStream output;
         private InputStream inerr;
         private FileChannel inputChannel;
         private FileChannel outputChannel;
         private FileChannel inerrChannel;
         private Pumper inputPumper;
         private Pumper inerrPumper;
 
         @Deprecated
         public POpenProcess(Process childRuby runtimeIOOptions modes) {
             this(childruntimemodes.getModeFlags());
         }
         
         public POpenProcess(Process childRuby runtimeModeFlags modes) {
             this. = child;
 
             if (modes.isWritable()) {
                 this. = true;
                 prepareOutput(child);
             } else {
                 this. = false;
                 // close process output
                 // See JRUBY-3405; hooking up to parent process stdin caused
                 // problems for IRB etc using stdin.
                 try {child.getOutputStream().close();} catch (IOException ioe) {}
             }
 
             if (modes.isReadable()) {
                 prepareInput(child);
             } else {
                 pumpInput(childruntime);
             }
 
             pumpInerr(childruntime);            
         }
 
         public POpenProcess(Process child) {
             this. = child;
             this. = false;
 
             prepareOutput(child);
             prepareInput(child);
             prepareInerr(child);
         }
 
         @Override
         public OutputStream getOutputStream() {
             return ;
         }
 
         @Override
         public InputStream getInputStream() {
             return ;
         }
 
         @Override
         public InputStream getErrorStream() {
             return ;
         }
 
         public FileChannel getInput() {
             return ;
         }
 
         public FileChannel getOutput() {
             return ;
         }
 
         public FileChannel getError() {
             return ;
         }
 
         public boolean hasOutput() {
             return  != null ||  != null;
         }
 
         public Process getChild() {
             return ;
         }
 
         @Override
         public int waitFor() throws InterruptedException {
             return .waitFor();
         }
 
         @Override
         public int exitValue() {
             return .exitValue();
         }
 
         @Override
         public void destroy() {
             try {
                 // We try to safely close all streams and channels to the greatest
                 // extent possible.
                 try {if ( != null.close();} catch (Exception e) {}
                 try {if ( != null.close();} catch (Exception e) {}
                 try {if ( != null.close();} catch (Exception e) {}
                 try {if ( != null.close();} catch (Exception e) {}
                 try {if ( != null.close();} catch (Exception e) {}
                 try {if ( != null.close();} catch (Exception e) {}
 
                 // processes seem to have some peculiar locking sequences, so we
                 // need to ensure nobody is trying to close/destroy while we are
                 synchronized (this) {
                     if ( != nullsynchronized() {.quit();}
                     if ( != nullsynchronized() {.quit();}
                     if () {
                         waitFor();
                     } else {
                         RubyIO.obliterateProcess();
                     }
                 }
             } catch (InterruptedException ie) {
                 Thread.currentThread().interrupt();
             }
         }
 
         private void prepareInput(Process child) {
             // popen callers wants to be able to read, provide subprocess in directly
              = child.getInputStream();
              = unwrapBufferedStream();
             if ( instanceof FileInputStream) {
 //                inputChannel = ((FileInputStream) input).getChannel();
             } else {
                  = null;
             }
              = null;
         }
 
         private void prepareInerr(Process child) {
             // popen callers wants to be able to read, provide subprocess in directly
              = child.getErrorStream();
              = unwrapBufferedStream();
             if ( instanceof FileInputStream) {
                  = ((FileInputStream).getChannel();
             } else {
                  = null;
             }
              = null;
         }
 
         private void prepareOutput(Process child) {
             // popen caller wants to be able to write, provide subprocess out directly
              = child.getOutputStream();
              = unwrapBufferedStream();
             if ( instanceof FileOutputStream) {
                  = ((FileOutputStream).getChannel();
             } else {
                  = null;
             }
         }
 
         private void pumpInput(Process childRuby runtime) {
             // no read requested, hook up read to parents output
             InputStream childIn = unwrapBufferedStream(child.getInputStream());
             FileChannel childInChannel = null;
             if (childIn instanceof FileInputStream) {
                 childInChannel = ((FileInputStreamchildIn).getChannel();
             }
             OutputStream parentOut = unwrapBufferedStream(runtime.getOut());
             FileChannel parentOutChannel = null;
             if (parentOut instanceof FileOutputStream) {
                 parentOutChannel = ((FileOutputStreamparentOut).getChannel();
             }
             if (childInChannel != null && parentOutChannel != null) {
                  = new ChannelPumper(runtimechildInChannelparentOutChannel..this);
             } else {
                  = new StreamPumper(runtimechildInparentOutfalse..this);
             }
            .start();
             = null;
             = null;
        }
        private void pumpInerr(Process childRuby runtime) {
            // no read requested, hook up read to parents output
            InputStream childIn = unwrapBufferedStream(child.getErrorStream());
            FileChannel childInChannel = null;
            if (childIn instanceof FileInputStream) {