wasCSharpSQLite – Rev 1

Subversion Repositories:
Rev:
/*
* ExecCmd.java --
*
*       This file contains the Jacl implementation of the built-in Tcl "exec"
*       command. The exec command is not available on the Mac.
*
* Copyright (c) 1997 Sun Microsystems, Inc.
*
* See the file "license.terms" for information on usage and
* redistribution of this file, and for a DISCLAIMER OF ALL
* WARRANTIES.
* 
* Included in SQLite3 port to C# for use in testharness only;  2008 Noah B Hart
*
* RCS @(#) $Id: ExecCmd.java,v 1.8 2002/01/19 00:15:01 mdejong Exp $
*/
using System;
using System.Diagnostics;
using System.IO;
using System.Text;

namespace tcl.lang
{


  /*
  * This class implements the built-in "exec" command in Tcl.
  */

  class ExecCmd : Command
  {

    /// <summary> Reference to Runtime.exec, null when JDK < 1.3</summary>
    private static System.Reflection.MethodInfo execMethod;

    public TCL.CompletionCode cmdProc( Interp interp, TclObject[] argv )
    {
      int firstWord; /* Index to the first non-switch arg */
      int argLen = argv.Length; /* No of args to copy to argStrs     */
      int exit; /* denotes exit status of process    */
      int errorBytes = 0; /* number of bytes of process stderr */
      //bool background; /* Indicates a bg process            */
      //bool keepNewline; /* Retains newline in pipline output */
      System.Diagnostics.Process p; /* The exec-ed process               */
      string argStr; /* Conversion of argv to a string    */
      StringBuilder sbuf;

      /*
      * Check for a leading "-keepnewline" argument.
      */

      for ( firstWord = 1; firstWord < argLen; firstWord++ )
      {
        argStr = argv[firstWord].ToString();
        if ( ( argStr.Length > 0 ) && ( argStr[0] == '-' ) )
        {
          //if (argStr.Equals("-keepnewline"))
          //{
          //  keepNewline = true;
          //}
          //else 
          if ( argStr.Equals( "--" ) )
          {
            firstWord++;
            break;
          }
          else
          {
            throw new TclException( interp, "bad switch \"" + argStr + "\": must be -keepnewline or --" );
          }
        }
      }

      if ( argLen <= firstWord )
      {
        throw new TclNumArgsException( interp, 1, argv, "?switches? arg ?arg ...?" );
      }


      /*
      * See if the command is to be run in background.
      * Currently this does nothing, it is just for compatibility
      */


      //if (argv[argLen - 1].ToString().Equals("&"))
      //{
      //  argLen--;
      //  background = true;
      //}

      try
      {
        /*
        * It is necessary to perform system specific 
        * operations before calling exec.  For now Solaris
        * and Windows execs are somewhat supported, in all other cases
        * we simply call exec and give it our "best shot"
        */

        if ( execMethod != null )
        {
          p = execReflection( interp, argv, firstWord, argLen );
        }
        else if ( Util.Unix )
        {
          p = execUnix( interp, argv, firstWord, argLen );
        }
        else if ( Util.Windows )
        {
          p = execWin( interp, argv, firstWord, argLen );
        }
        else
        {
          p = execDefault( interp, argv, firstWord, argLen );
        }


        //note to self : buffer reading should be done in
        //a separate thread and not by calling waitFor()
        //because a process that is waited for can block


        //Wait for the process to finish running,
        try
        {
          p.Start();
          p.WaitForExit();
          exit = p.ExitCode;
        }
        catch ( Exception e )
        {
          throw new TclException( interp, "exception in exec process: " + e.Message );
        }


        //Make buffer for the results of the subprocess execution
        sbuf = new StringBuilder();

        //read data on stdout stream into  result buffer
        readStreamIntoBuffer( p.StandardOutput.BaseStream, sbuf );

        //if there is data on the stderr stream then append
        //this data onto the result StringBuffer
        //check for the special case where there is no error
        //data but the process returns an error result

        errorBytes = readStreamIntoBuffer( p.StandardError.BaseStream, sbuf );

        if ( ( errorBytes == 0 ) && ( exit != 0 ) )
        {
          sbuf.Append( "child process exited abnormally" );
        }

        //If the last character of the result buffer is a newline, then 
        //remove the newline character (the newline would just confuse 
        //things).  Finally, we set pass the result to the interpreter.



        // Tcl supports lots of child status conditions.
        // Unfortunately, we can only find the child's
        // exit status using the Java API

        if ( exit != 0 )
        {
          TclObject childstatus = TclList.newInstance();
          TclList.append( interp, childstatus, TclString.newInstance( "CHILDSTATUS" ) );

          // We don't know how to find the child's pid
          TclList.append( interp, childstatus, TclString.newInstance( "?PID?" ) );

          TclList.append( interp, childstatus, TclInteger.newInstance( exit ) );

          interp.setErrorCode( childstatus );
        }

        //when the subprocess writes to its stderr stream or returns
        //a non zero result we generate an error
        if ( ( exit != 0 ) || ( errorBytes != 0 ) )
        {
          throw new TclException( interp, sbuf.ToString() );
        }

        //otherwise things went well so set the result
        interp.setResult( sbuf.ToString() );
      }
      catch ( IOException e )
      {
        //if exec fails we end up catching the exception here


        throw new TclException( interp, "couldn't execute \"" + argv[firstWord].ToString() + "\": no such file or directory" );
      }
      catch ( System.Threading.ThreadInterruptedException e )
      {
        /*
        * Do Nothing...
        */
      }
      return TCL.CompletionCode.RETURN;
    }


    internal static int readStreamIntoBuffer( Stream in_Renamed, StringBuilder sbuf )
    {
      int numRead = 0;
      StreamReader br = new StreamReader( new StreamReader( in_Renamed ).BaseStream, System.Text.Encoding.UTF7 );

      try
      {
        string line = br.ReadLine();

        while ( (System.Object)line != null )
        {
          sbuf.Append( line );
          numRead += line.Length;
          sbuf.Append( '\n' );
          numRead++;
          line = br.ReadLine();
        }
      }
      catch ( IOException e )
      {
        //do nothing just return numRead
      }
      finally
      {
        try
        {
          br.Close();
        }
        catch ( IOException e )
        {
        } //ignore IO error
      }

      return numRead;
    }


    internal static string escapeWinString( string str )
    {
      if ( str.IndexOf( (System.Char)'%' ) == -1 )
        return str;

      char[] arr = str.ToCharArray();
      StringBuilder sb = new StringBuilder( 50 );

      for ( int i = 0; i < arr.Length; i++ )
      {
        if ( arr[i] == '%' )
        {
          sb.Append( '%' );
        }
        sb.Append( arr[i] );
      }

      return sb.ToString();
    }


    private System.Diagnostics.Process execUnix( Interp interp, TclObject[] argv, int first, int last )
    {
      return execWin( interp, argv, first, last );
    }

    private System.Diagnostics.Process execWin( Interp interp, TclObject[] argv, int first, int last )
    {
      StringBuilder sb = new StringBuilder();
      for ( int i = ( first + 1 ); i < last; i++ )
      {
        sb.Append( '"' );
        sb.Append( escapeWinString( argv[i].ToString() ) );
        sb.Append( '"' );
        sb.Append( ' ' );
      }

      Process proc = new Process();
      proc.StartInfo.UseShellExecute = false;
      proc.StartInfo.RedirectStandardOutput = true;
      proc.StartInfo.RedirectStandardError = true;
      proc.StartInfo.RedirectStandardInput = true;
      proc.StartInfo.WorkingDirectory = interp.getWorkingDir().FullName;
      proc.StartInfo.FileName = argv[first].ToString();
      proc.StartInfo.Arguments = sb.ToString();
      return proc;
    }

    private System.Diagnostics.Process execDefault( Interp interp, TclObject[] argv, int first, int last )
    {
      return execWin( interp, argv, first, last );
    }

    private System.Diagnostics.Process execReflection( Interp interp, TclObject[] argv, int first, int last )
    {

      string[] strv = new string[last - first];

      for ( int i = first, j = 0; i < last; j++, i++ )
      {

        strv[j] = argv[i].ToString();
      }

      Object[] methodArgs = new Object[3];
      methodArgs[0] = strv; // exec command arguments
      methodArgs[1] = null; // inherit all environment variables
      methodArgs[2] = interp.getWorkingDir();

      try
      {
        return (System.Diagnostics.Process)execMethod.Invoke( System.Diagnostics.Process.GetCurrentProcess(), (System.Object[])methodArgs );
      }
      catch ( System.UnauthorizedAccessException ex )
      {
        throw new TclRuntimeError( "IllegalAccessException in execReflection" );
      }
      catch ( System.ArgumentException ex )
      {
        throw new TclRuntimeError( "IllegalArgumentException in execReflection" );
      }
      catch ( System.Reflection.TargetInvocationException ex )
      {
        System.Exception t = ex.GetBaseException();

        if ( t is System.ApplicationException )
        {
          throw (System.ApplicationException)t;
        }
        else if ( t is IOException )
        {
          throw (IOException)t;
        }
        else
        {
          throw new TclRuntimeError( "unexected exception in execReflection" );
        }
      }
    }
    static ExecCmd()
    {
      {
        // Runtime.exec(String[] cmdArr, String[] envArr, File currDir)
        Type[] parameterTypes = new Type[] { typeof( string[] ), typeof( string[] ), typeof( FileInfo ) };
        try
        {
          execMethod = System.Diagnostics.Process.GetCurrentProcess().GetType().GetMethod( "exec", (System.Type[])parameterTypes );
        }
        catch ( System.MethodAccessException e )
        {
          execMethod = null;
        }
      }
    }
  } // end ExecCmd
}