Here's a patch for Kernel#system that executes ruby scripts in the current jruby process rather than forking a new JVM and adds support for the multi-argument variant of Kernel#system. Its based in large part on a patch provided by Charles.

The new code still fails one of the rubicon tests due to some shell wildcard expansion differences that I haven't yet figured out.

Marc.

---
Marc Hadley <marc.hadley at sun.com>

Index: RubyKernel.java
*** /Users/mh124079/Development/Ruby/jruby/src/org/jruby/ RubyKernel.java Base (1.45) --- /Users/mh124079/Development/Ruby/jruby/src/org/jruby/ RubyKernel.java Locally Modified (Based On 1.45)
***************
*** 40,46 ****
--- 40,48 ----
  import java.io.IOException;
  import java.io.InputStreamReader;
  import java.util.Iterator;
+ import java.util.Stack;
  import java.util.StringTokenizer;
+ import java.util.Vector;
  import java.util.regex.Pattern;

  import org.jruby.ast.util.ArgsUtil;
***************
*** 686,692 ****
          StringBuffer output = new StringBuffer();
          IRuby runtime = recv.getRuntime();
          runtime.getGlobalVariables().set("$?", runtime.newFixnum(
!             runInShell(runtime, aString.toString(), output)));

          return recv.getRuntime().newString(output.toString());
      }
--- 688,694 ----
          StringBuffer output = new StringBuffer();
          IRuby runtime = recv.getRuntime();
          runtime.getGlobalVariables().set("$?", runtime.newFixnum(
! runInShell(runtime, new IRubyObject[] {aString}, output)));

          return recv.getRuntime().newString(output.toString());
      }
***************
*** 745,770 ****
return PATH_SEPARATORS.matcher(executable).replaceAll (replacement) + remainder;
      }

! private static int runInShell(IRuby runtime, String command, StringBuffer output) {
          try {
              String shell = System.getProperty("jruby.shell");
!             Process aProcess;
!             String shellSwitch = "-c";

!             command = repairDirSeps(command);

!             if (shell != null) {
!                 if (!shell.endsWith("sh")) {
!                     shellSwitch = "/c";
                  }
! aProcess = Runtime.getRuntime().exec(new String[] { shell, shellSwitch, command });
              } else {
!                 aProcess = Runtime.getRuntime().exec(command);
              }

final BufferedReader reader = new BufferedReader(new InputStreamReader(aProcess.getInputStream()));

--- 747,845 ----
return PATH_SEPARATORS.matcher(executable).replaceAll (replacement) + remainder;
      }

!     private static Vector parseCommandLine(IRubyObject[] rawArgs) {
! // first parse the first element of rawArgs since this may contain
!         // the whole command line
!         String command = rawArgs[0].toString();
!         Stack args = new Stack();
!         StringTokenizer st = new StringTokenizer(command, " ");
!         String quoteChar = null;
!
!         while (st.hasMoreTokens()) {
!             String token = st.nextToken();
!             if (quoteChar == null) {
!                 // not currently in the middle of a quoted token
!                 if (token.startsWith("'") || token.startsWith("\"")) {
! // note quote char and remove from beginning of token
!                     quoteChar = token.substring(0, 1);
!                     token = token.substring(1);
!                 }
!                 if (quoteChar!=null && token.endsWith(quoteChar)) {
! // quoted token self contained, remove from end of token
!                     token = token.substring(0, token.length()-1);
!                     quoteChar = null;
!                 }
!                 // add new token to list
!                 args.push(token);
!             } else {
!                 // in the middle of quoted token
!                 if (token.endsWith(quoteChar)) {
!                     // end of quoted token
!                     token = token.substring(0, token.length()-1);
!                     quoteChar = null;
!                 }
!                 // update token at end of list
!                 token = args.pop() + " " + token;
!                 args.push(token);
!             }
!         }
!
!         // now append the remaining raw args to the cooked arg list
!         for (int i=1;i<rawArgs.length;i++) {
!             args.push(rawArgs[i].toString());
!         }
!
!         return args;
!     }
!
! private static int runInShell(IRuby runtime, IRubyObject[] rawArgs, StringBuffer output) {
          try {
+ // startup scripts set jruby.shell to /bin/sh for Unix, cmd.exe for Windows
              String shell = System.getProperty("jruby.shell");
!             Process aProcess = null;

!             final Vector args = parseCommandLine(rawArgs);
!             String command = (String)args.get(0);

! // this will match ruby anywhere in the path to the command. ! // it might be better to restrict matches to the final token after
!             // tokenizing with '/'
!             if (command.contains("ruby") || command.endsWith(".rb")) {
!                 // use existing JRuby process to execute command
!                 Thread t = new Thread() {
!                     public void run() {
!                         try {
! String[] argArray = new String[args.size ()-1]; ! // snip off ruby or jruby command from list of arguments ! args.subList(1,args.size()).toArray (argArray);
!                             org.jruby.Main.main(argArray);
!                         } catch (Throwable t) {
                          }
!                     }
!                 };
!                 t.start();
!                 t.join();
!             } else if (shell != null) {
!                 // execute command with sh -c or cmd.exe /c
! String shellSwitch = shell.endsWith("sh") ? "-c" : "/c";
!                 String[] argArray = new String[3];
!                 argArray[0] = shell;
!                 argArray[1] = shellSwitch;
!                 StringBuffer buf = new StringBuffer();
!                 for (int i=0;i<args.size();i++) {
!                     buf.append(args.get(i));
!                     buf.append(' ');
!                 }
!                 argArray[2] = buf.toString();
!                 aProcess = Runtime.getRuntime().exec(argArray);
              } else {
!                 // execute command directly
!                 String[] argArray = new String[args.size()];
!                 args.toArray(argArray);
!                 aProcess = Runtime.getRuntime().exec(argArray);
              }

+             if (aProcess != null) {
final BufferedReader reader = new BufferedReader (new InputStreamReader(aProcess.getInputStream()));

// Fairly innefficient impl, but readLine is unable to tell
***************
*** 789,794 ****
--- 867,875 ----
                  reader.close();

                  return aProcess.waitFor();
+             } else {
+                 return 0;
+             }
          } catch (IOException e) {
              throw runtime.newIOErrorFromException(e);
          } catch (InterruptedException e) {
***************
*** 839,849 ****

public static RubyBoolean system(IRubyObject recv, IRubyObject [] args) {
          IRuby runtime = recv.getRuntime();
-         if (args.length > 1) {
- throw runtime.newArgumentError("more arguments not yet supported");
-         }
          StringBuffer output = new StringBuffer();
! int resultCode = runInShell(runtime, args[0].toString(), output); recv.getRuntime().getGlobalVariables().set("$?", runtime.newFixnum(resultCode));
          return runtime.newBoolean(resultCode == 0);
      }
--- 920,928 ----

public static RubyBoolean system(IRubyObject recv, IRubyObject [] args) {
          IRuby runtime = recv.getRuntime();
          StringBuffer output = new StringBuffer();
!         int resultCode = runInShell(runtime, args, output);
!         System.out.println(output.toString());
recv.getRuntime().getGlobalVariables().set("$?", runtime.newFixnum(resultCode));
          return runtime.newBoolean(resultCode == 0);
      }



-------------------------------------------------------
This SF.Net email is sponsored by xPML, a groundbreaking scripting language
that extends applications into web and mobile media. Attend the live webcast
and join the prime developer group breaking into this new coding territory!
http://sel.as-us.falkag.net/sel?cmd=lnk&kid=110944&bid=241720&dat=121642
_______________________________________________
Jruby-devel mailing list
Jruby-devel@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/jruby-devel

Reply via email to