From c310600df56e7896cb0b996eb0dd9d24203d94fb Mon Sep 17 00:00:00 2001
From: Andy Cedilnik <andy.cedilnik@kitware.com>
Date: Fri, 27 Sep 2002 17:26:37 -0400
Subject: Add class for process execution on Windows

---
 Source/CMakeLists.txt              |   2 +
 Source/cmWin32ProcessExecution.cxx | 614 +++++++++++++++++++++++++++++++++++++
 Source/cmWin32ProcessExecution.h   |  85 +++++
 3 files changed, 701 insertions(+)
 create mode 100644 Source/cmWin32ProcessExecution.cxx
 create mode 100644 Source/cmWin32ProcessExecution.h

diff --git a/Source/CMakeLists.txt b/Source/CMakeLists.txt
index 6a108b9..a1bc408 100644
--- a/Source/CMakeLists.txt
+++ b/Source/CMakeLists.txt
@@ -72,6 +72,8 @@ IF (WIN32)
                     cmLocalVisualStudio6Generator.h
                     cmGlobalVisualStudio7Generator.h
                     cmLocalVisualStudio7Generator.h
+		    cmWin32ProcessExecution.cxx
+		    cmWin32ProcessExecution.h
                     )
     IF(  NOT BORLAND )
       LINK_LIBRARIES( rpcrt4.lib )
diff --git a/Source/cmWin32ProcessExecution.cxx b/Source/cmWin32ProcessExecution.cxx
new file mode 100644
index 0000000..916693a
--- /dev/null
+++ b/Source/cmWin32ProcessExecution.cxx
@@ -0,0 +1,614 @@
+/*=========================================================================
+
+  Program:   Insight Segmentation & Registration Toolkit
+  Module:    $RCSfile$
+  Language:  C++
+  Date:      $Date$
+  Version:   $Revision$
+
+  Copyright (c) 2002 Insight Consortium. All rights reserved.
+  See ITKCopyright.txt or http://www.itk.org/HTML/Copyright.htm for details.
+
+     This software is distributed WITHOUT ANY WARRANTY; without even 
+     the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR 
+     PURPOSE.  See the above copyright notices for more information.
+
+=========================================================================*/
+#include "cmWin32ProcessExecution.h"
+
+#include "cmSystemTools.h"   
+
+#include <malloc.h>
+#include <io.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <sys/stat.h>
+#include <windows.h>
+
+#define POPEN_1 1
+#define POPEN_2 2
+#define POPEN_3 3
+#define POPEN_4 4
+
+#define cmMAX(x,y) (((x)<(y))?(y):(x))
+
+#define win32_error(x,y) std::cout << "Win32_Error(" << x << ", " << y << ")" << std::endl, false
+
+bool cmWin32ProcessExecution::StartProcess(
+  const char* cmd, const char* path, bool verbose)
+{
+  this->Initialize();
+  this->m_Verbose = verbose;
+  return this->PrivateOpen(cmd, path, _O_RDONLY | _O_TEXT, POPEN_3);
+}
+
+bool cmWin32ProcessExecution::Wait(int timeout)
+{
+  return this->PrivateClose(timeout);
+}
+
+/*
+ * Internal dictionary mapping popen* file pointers to process handles,
+ * for use when retrieving the process exit code.  See _PyPclose() below
+ * for more information on this dictionary's use.
+ */
+static void *_PyPopenProcs = NULL;
+
+static BOOL RealPopenCreateProcess(const char *cmdstring,
+				   const char *path, 
+				   const char *szConsoleSpawn,
+				   HANDLE hStdin,
+				   HANDLE hStdout,
+				   HANDLE hStderr,
+				   HANDLE *hProcess)
+{
+  PROCESS_INFORMATION piProcInfo;
+  STARTUPINFO siStartInfo;
+  char *s1,*s2, *s3 = " /c ";
+  int i;
+  int x;
+
+  if (i = GetEnvironmentVariable("COMSPEC",NULL,0)) 
+    {
+    char *comshell;
+
+    s1 = (char *)_alloca(i);
+    if (!(x = GetEnvironmentVariable("COMSPEC", s1, i)))
+      return x;
+
+    /* Explicitly check if we are using COMMAND.COM.  If we are
+     * then use the w9xpopen hack.
+     */
+    comshell = s1 + x;
+    while (comshell >= s1 && *comshell != '\\')
+      --comshell;
+    ++comshell;
+
+    if (GetVersion() < 0x80000000 &&
+	_stricmp(comshell, "command.com") != 0) 
+      {
+      /* NT/2000 and not using command.com. */
+      x = i + strlen(s3) + strlen(cmdstring) + 1;
+      s2 = (char *)_alloca(x);
+      ZeroMemory(s2, x);
+      sprintf(s2, "%s%s%s", s1, s3, cmdstring);
+      //std::cout << "s1: " << s1 << " s2: " << s2 << " s3: " << s3 
+      //		<< std::endl;
+      }
+    else 
+      {
+      /*
+       * Oh gag, we're on Win9x or using COMMAND.COM. Use
+       * the workaround listed in KB: Q150956
+       */
+      char modulepath[_MAX_PATH];
+      struct stat statinfo;
+      GetModuleFileName(NULL, modulepath, sizeof(modulepath));
+      for (i = x = 0; modulepath[i]; i++)
+	if (modulepath[i] == '\\')
+	  x = i+1;
+      modulepath[x] = '\0';
+      /* Create the full-name to w9xpopen, so we can test it exists */
+      strncat(modulepath, 
+	      szConsoleSpawn, 
+	      (sizeof(modulepath)/sizeof(modulepath[0]))
+	      -strlen(modulepath));
+      if (stat(modulepath, &statinfo) != 0) 
+	{
+				/* Eeek - file-not-found - possibly an embedding 
+				   situation - see if we can locate it in sys.prefix 
+				*/
+	strncpy(modulepath, 
+		".", 
+		sizeof(modulepath)/sizeof(modulepath[0]));
+	if (modulepath[strlen(modulepath)-1] != '\\')
+	  strcat(modulepath, "\\");
+	strncat(modulepath, 
+		szConsoleSpawn, 
+		(sizeof(modulepath)/sizeof(modulepath[0]))
+		-strlen(modulepath));
+	/* No where else to look - raise an easily identifiable
+	   error, rather than leaving Windows to report
+	   "file not found" - as the user is probably blissfully
+	   unaware this shim EXE is used, and it will confuse them.
+	   (well, it confused me for a while ;-)
+	*/
+	if (stat(modulepath, &statinfo) != 0) 
+	  {
+	  std::cout 
+	    << "Can not locate '" << szConsoleSpawn 
+	    << "' which is needed "
+	    "for popen to work with your shell "
+	    "or platform." << std::endl;
+	  return FALSE;
+	  }
+	}
+      x = i + strlen(s3) + strlen(cmdstring) + 1 +
+	strlen(modulepath) + 
+	strlen(szConsoleSpawn) + 1;
+
+      s2 = (char *)_alloca(x);
+      ZeroMemory(s2, x);
+      sprintf(
+	s2,
+	"%s \"%s%s%s\"",
+	modulepath,
+	s1,
+	s3,
+	cmdstring);
+      }
+    }
+
+  /* Could be an else here to try cmd.exe / command.com in the path
+     Now we'll just error out.. */
+  else 
+    {
+    std::cout << "Cannot locate a COMSPEC environment variable to "
+	      << "use as the shell" << std::endl;
+    return FALSE;
+    }
+  
+  ZeroMemory(&siStartInfo, sizeof(STARTUPINFO));
+  siStartInfo.cb = sizeof(STARTUPINFO);
+  siStartInfo.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW;
+  siStartInfo.hStdInput = hStdin;
+  siStartInfo.hStdOutput = hStdout;
+  siStartInfo.hStdError = hStderr;
+  siStartInfo.wShowWindow = SW_HIDE;
+
+  if (CreateProcess(NULL,
+		    s2,
+		    NULL,
+		    NULL,
+		    TRUE,
+		    CREATE_NEW_CONSOLE,
+		    NULL,
+		    path,
+		    &siStartInfo,
+		    &piProcInfo) ) 
+    {
+    /* Close the handles now so anyone waiting is woken. */
+    CloseHandle(piProcInfo.hThread);
+
+    /* Return process handle */
+    *hProcess = piProcInfo.hProcess;
+    return TRUE;
+    }
+  win32_error("CreateProcess", s2);
+  return FALSE;
+}
+
+/* The following code is based off of KB: Q190351 */
+
+bool cmWin32ProcessExecution::PrivateOpen(const char *cmdstring, 
+					  const char* path,
+					  int mode, 
+					  int n)
+{
+  HANDLE hChildStdinRd, hChildStdinWr, hChildStdoutRd, hChildStdoutWr,
+    hChildStderrRd, hChildStderrWr, hChildStdinWrDup, hChildStdoutRdDup,
+    hChildStderrRdDup, hProcess; /* hChildStdoutWrDup; */
+
+  SECURITY_ATTRIBUTES saAttr;
+  BOOL fSuccess;
+  int fd1, fd2, fd3;
+  FILE *f1, *f2, *f3;
+  long file_count;
+
+  saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
+  saAttr.bInheritHandle = TRUE;
+  saAttr.lpSecurityDescriptor = NULL;
+
+  if (!CreatePipe(&hChildStdinRd, &hChildStdinWr, &saAttr, 0))
+    return win32_error("CreatePipe", NULL);
+
+  /* Create new output read handle and the input write handle. Set
+   * the inheritance properties to FALSE. Otherwise, the child inherits
+   * the these handles; resulting in non-closeable handles to the pipes
+   * being created. */
+  fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdinWr,
+			     GetCurrentProcess(), &hChildStdinWrDup, 0,
+			     FALSE,
+			     DUPLICATE_SAME_ACCESS);
+  if (!fSuccess)
+    return win32_error("DuplicateHandle", NULL);
+
+  /* Close the inheritable version of ChildStdin
+     that we're using. */
+  CloseHandle(hChildStdinWr);
+
+  if (!CreatePipe(&hChildStdoutRd, &hChildStdoutWr, &saAttr, 0))
+    return win32_error("CreatePipe", NULL);
+
+  fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdoutRd,
+			     GetCurrentProcess(), &hChildStdoutRdDup, 0,
+			     FALSE, DUPLICATE_SAME_ACCESS);
+  if (!fSuccess)
+    return win32_error("DuplicateHandle", NULL);
+
+  /* Close the inheritable version of ChildStdout
+     that we're using. */
+  CloseHandle(hChildStdoutRd);
+
+  if (n != POPEN_4) 
+    {
+    if (!CreatePipe(&hChildStderrRd, &hChildStderrWr, &saAttr, 0))
+      return win32_error("CreatePipe", NULL);
+    fSuccess = DuplicateHandle(GetCurrentProcess(),
+			       hChildStderrRd,
+			       GetCurrentProcess(),
+			       &hChildStderrRdDup, 0,
+			       FALSE, DUPLICATE_SAME_ACCESS);
+    if (!fSuccess)
+      return win32_error("DuplicateHandle", NULL);
+    /* Close the inheritable version of ChildStdErr that we're using. */
+    CloseHandle(hChildStderrRd);
+    }
+	  
+  switch (n) 
+    {
+    case POPEN_1:
+      switch (mode & (_O_RDONLY | _O_TEXT | _O_BINARY | _O_WRONLY)) 
+	{
+	case _O_WRONLY | _O_TEXT:
+	  /* Case for writing to child Stdin in text mode. */
+	  fd1 = _open_osfhandle((long)hChildStdinWrDup, mode);
+	  f1 = _fdopen(fd1, "w");
+	  /* We don't care about these pipes anymore,
+	     so close them. */
+	  CloseHandle(hChildStdoutRdDup);
+	  CloseHandle(hChildStderrRdDup);
+	  break;
+
+	case _O_RDONLY | _O_TEXT:
+	  /* Case for reading from child Stdout in text mode. */
+	  fd1 = _open_osfhandle((long)hChildStdoutRdDup, mode);
+	  f1 = _fdopen(fd1, "r");
+	  /* We don't care about these pipes anymore,
+	     so close them. */
+	  CloseHandle(hChildStdinWrDup);
+	  CloseHandle(hChildStderrRdDup);
+	  break;
+
+	case _O_RDONLY | _O_BINARY:
+	  /* Case for readinig from child Stdout in
+	     binary mode. */
+	  fd1 = _open_osfhandle((long)hChildStdoutRdDup, mode);
+	  f1 = _fdopen(fd1, "rb");
+	  /* We don't care about these pipes anymore,
+	     so close them. */
+	  CloseHandle(hChildStdinWrDup);
+	  CloseHandle(hChildStderrRdDup);
+	  break;
+
+	case _O_WRONLY | _O_BINARY:
+	  /* Case for writing to child Stdin in binary mode. */
+	  fd1 = _open_osfhandle((long)hChildStdinWrDup, mode);
+	  f1 = _fdopen(fd1, "wb");
+	  /* We don't care about these pipes anymore,
+	     so close them. */
+	  CloseHandle(hChildStdoutRdDup);
+	  CloseHandle(hChildStderrRdDup);
+	  break;
+	}
+      file_count = 1;
+      break;
+	
+    case POPEN_2:
+    case POPEN_4: 
+      if ( 1 ) 
+	{
+	char *m1,  *m2;
+	
+	if (mode && _O_TEXT) 
+	  {
+	  m1 = "r";
+	  m2 = "w";
+	  } 
+	else 
+	  {
+	  m1 = "rb";
+	  m2 = "wb";
+	  }
+
+	fd1 = _open_osfhandle((long)hChildStdinWrDup, mode);
+	f1 = _fdopen(fd1, m2);
+	fd2 = _open_osfhandle((long)hChildStdoutRdDup, mode);
+	f2 = _fdopen(fd2, m1);
+
+	if (n != 4)
+	  CloseHandle(hChildStderrRdDup);
+
+	file_count = 2;
+	break;
+	}
+	
+    case POPEN_3:
+      if ( 1) 
+	{
+	char *m1, *m2;
+      
+	if (mode && _O_TEXT) 
+	  {
+	  m1 = "r";
+	  m2 = "w";
+	  } 
+	else 
+	  {
+	  m1 = "rb";
+	  m2 = "wb";
+	  }
+
+	fd1 = _open_osfhandle((long)hChildStdinWrDup, mode);
+	f1 = _fdopen(fd1, m2);
+	fd2 = _open_osfhandle((long)hChildStdoutRdDup, mode);
+	f2 = _fdopen(fd2, m1);
+	fd3 = _open_osfhandle((long)hChildStderrRdDup, mode);
+	f3 = _fdopen(fd3, m1);	
+
+	file_count = 3;
+	break;
+	}
+    }
+
+  if (n == POPEN_4) 
+    {
+    if (!RealPopenCreateProcess(cmdstring,
+				path,
+				this->m_ConsoleSpawn.c_str(),
+				hChildStdinRd,
+				hChildStdoutWr,
+				hChildStdoutWr,
+				&hProcess))
+      return NULL;
+    }
+  else 
+    {
+    if (!RealPopenCreateProcess(cmdstring,
+				path,
+				this->m_ConsoleSpawn.c_str(),
+				hChildStdinRd,
+				hChildStdoutWr,
+				hChildStderrWr,
+				&hProcess))
+      return NULL;
+    }
+
+  /*
+   * Insert the files we've created into the process dictionary
+   * all referencing the list with the process handle and the
+   * initial number of files (see description below in _PyPclose).
+   * Since if _PyPclose later tried to wait on a process when all
+   * handles weren't closed, it could create a deadlock with the
+   * child, we spend some energy here to try to ensure that we
+   * either insert all file handles into the dictionary or none
+   * at all.  It's a little clumsy with the various popen modes
+   * and variable number of files involved.
+   */
+
+  /* Child is launched. Close the parents copy of those pipe
+   * handles that only the child should have open.  You need to
+   * make sure that no handles to the write end of the output pipe
+   * are maintained in this process or else the pipe will not close
+   * when the child process exits and the ReadFile will hang. */
+
+  if (!CloseHandle(hChildStdinRd))
+    return win32_error("CloseHandle", NULL);
+	  
+  if (!CloseHandle(hChildStdoutWr))
+    return win32_error("CloseHandle", NULL);
+	  
+  if ((n != 4) && (!CloseHandle(hChildStderrWr)))
+    return win32_error("CloseHandle", NULL);
+  
+  this->m_ProcessHandle = hProcess;
+  if ( f1 )
+    {
+    this->m_StdIn = f1;
+    this->m_pStdIn = fd1;
+    }
+  if ( f2 )
+    {
+    this->m_StdOut = f2;
+    this->m_pStdOut = fd2;
+    }
+  if ( f3 )
+    {
+    this->m_StdErr = f3;
+    this->m_pStdErr = fd3;
+    }
+
+  return true;
+}
+
+/*
+ * Wrapper for fclose() to use for popen* files, so we can retrieve the
+ * exit code for the child process and return as a result of the close.
+ *
+ * This function uses the _PyPopenProcs dictionary in order to map the
+ * input file pointer to information about the process that was
+ * originally created by the popen* call that created the file pointer.
+ * The dictionary uses the file pointer as a key (with one entry
+ * inserted for each file returned by the original popen* call) and a
+ * single list object as the value for all files from a single call.
+ * The list object contains the Win32 process handle at [0], and a file
+ * count at [1], which is initialized to the total number of file
+ * handles using that list.
+ *
+ * This function closes whichever handle it is passed, and decrements
+ * the file count in the dictionary for the process handle pointed to
+ * by this file.  On the last close (when the file count reaches zero),
+ * this function will wait for the child process and then return its
+ * exit code as the result of the close() operation.  This permits the
+ * files to be closed in any order - it is always the close() of the
+ * final handle that will return the exit code.
+ */
+
+ /* RED_FLAG 31-Aug-2000 Tim
+  * This is always called (today!) between a pair of
+  * Py_BEGIN_ALLOW_THREADS/ Py_END_ALLOW_THREADS
+  * macros.  So the thread running this has no valid thread state, as
+  * far as Python is concerned.  However, this calls some Python API
+  * functions that cannot be called safely without a valid thread
+  * state, in particular PyDict_GetItem.
+  * As a temporary hack (although it may last for years ...), we
+  * *rely* on not having a valid thread state in this function, in
+  * order to create our own "from scratch".
+  * This will deadlock if _PyPclose is ever called by a thread
+  * holding the global lock.
+  */
+
+bool cmWin32ProcessExecution::PrivateClose(int timeout)
+{
+  HANDLE hProcess = this->m_ProcessHandle;
+
+  int result = -1;
+  DWORD exit_code;
+
+  std::string output = "";
+  bool done = false;
+  int stdoutloc = 0;
+  int stderrloc = 0;
+  while(!done)
+    {
+    Sleep(10);
+    struct _stat fsout;
+    struct _stat fserr;
+    int rout = _fstat(this->m_pStdOut, &fsout);
+    int rerr = _fstat(this->m_pStdErr, &fserr);
+    if ( rout && rerr )
+      {
+      break;
+      }
+    if (fserr.st_size > 0)
+      {
+      int dist = fserr.st_size;
+      char buffer[1023];
+      int len = read(this->m_pStdErr, buffer, 1023);
+      buffer[len] = 0;
+      if ( this->m_Verbose )
+	{
+	std::cout << buffer << std::flush;
+	}
+      output += buffer;
+      }
+    if (fsout.st_size > 0)
+      {
+      int dist = fsout.st_size;
+      char buffer[1023];
+      int len = read(this->m_pStdOut, buffer, 1023);
+      buffer[len] = 0;
+      if ( this->m_Verbose )
+	{
+	std::cout << buffer << std::flush;
+	}
+      output += buffer;
+      }
+    unsigned long exitCode;
+    GetExitCodeProcess(hProcess,&exitCode);
+    if (exitCode != STILL_ACTIVE)
+      {
+      break;
+      }
+    }  
+  
+  if (WaitForSingleObject(hProcess, INFINITE) != WAIT_FAILED &&
+      GetExitCodeProcess(hProcess, &exit_code)) 
+    {
+    result = exit_code;
+    } 
+  else 
+    {
+    /* Indicate failure - this will cause the file object
+     * to raise an I/O error and translate the last Win32
+     * error code from errno.  We do have a problem with
+     * last errors that overlap the normal errno table,
+     * but that's a consistent problem with the file object.
+     */
+    if (result != EOF) 
+      {
+      /* If the error wasn't from the fclose(), then
+       * set errno for the file object error handling.
+       */
+      errno = GetLastError();
+      }
+    result = -1;
+    }
+	
+  /* Free up the native handle at this point */
+  CloseHandle(hProcess);
+  this->m_ExitValue = result;
+  this->m_Output = output;
+  if ( result < 0 )
+    {
+    return false;
+    }
+  return true;
+}
+
+int cmWin32ProcessExecution::Windows9xHack(const char* command)
+{
+  BOOL bRet;
+  STARTUPINFO si;
+  PROCESS_INFORMATION pi;
+  DWORD exit_code=0;
+
+  if (!command) 
+    {
+    cmSystemTools::Error("Windows9xHack: Command not specified");
+    return 1;
+  }
+
+  /* Make child process use this app's standard files. */
+  ZeroMemory(&si, sizeof si);
+  si.cb = sizeof si;
+  si.dwFlags = STARTF_USESTDHANDLES;
+  si.hStdInput = GetStdHandle(STD_INPUT_HANDLE);
+  si.hStdOutput = GetStdHandle(STD_OUTPUT_HANDLE);
+  si.hStdError = GetStdHandle(STD_ERROR_HANDLE);
+
+  char* cmd = new char[ strlen(command) + 1 ];
+  strcpy(cmd, command);
+
+  bRet = CreateProcess(
+    NULL, cmd,
+    NULL, NULL,
+    TRUE, 0,
+    NULL, NULL,
+    &si, &pi
+    );
+  delete [] cmd;
+
+  if (bRet) 
+    {
+    if (WaitForSingleObject(pi.hProcess, INFINITE) != WAIT_FAILED) 
+      {
+      GetExitCodeProcess(pi.hProcess, &exit_code);
+      }
+    CloseHandle(pi.hProcess);
+    CloseHandle(pi.hThread);
+    return exit_code;
+    }
+
+  return 1;
+}
diff --git a/Source/cmWin32ProcessExecution.h b/Source/cmWin32ProcessExecution.h
new file mode 100644
index 0000000..acc214b
--- /dev/null
+++ b/Source/cmWin32ProcessExecution.h
@@ -0,0 +1,85 @@
+/*=========================================================================
+
+  Program:   Insight Segmentation & Registration Toolkit
+  Module:    $RCSfile$
+  Language:  C++
+  Date:      $Date$
+  Version:   $Revision$
+
+  Copyright (c) 2002 Insight Consortium. All rights reserved.
+  See ITKCopyright.txt or http://www.itk.org/HTML/Copyright.htm for details.
+
+     This software is distributed WITHOUT ANY WARRANTY; without even 
+     the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR 
+     PURPOSE.  See the above copyright notices for more information.
+
+=========================================================================*/
+#ifndef cmWin32ProcessExecution_h
+#define cmWin32ProcessExecution_h
+
+/*
+ * Portable 'popen' replacement for Win32.
+ *
+ * Written by Bill Tutt <billtut@microsoft.com>.  Minor tweaks
+ * and 2.0 integration by Fredrik Lundh <fredrik@pythonware.com>
+ * Return code handling by David Bolen <db3l@fitlinxx.com>.
+ */
+
+#include "cmStandardIncludes.h"
+#include "windows.h"
+
+class cmMakefile;
+
+/** \class cmWin32ProcessExecution
+ * \brief A process executor for windows
+ *
+ * cmWin32ProcessExecution is a class that provides a "clean" way of
+ * executing processes on Windows.
+ */
+class cmWin32ProcessExecution
+{
+public:
+  cmWin32ProcessExecution()
+    {
+    this->SetConsoleSpawn("w9xpopen.exe");
+    this->Initialize();
+    }
+  void Initialize() 
+    {
+    this->m_ProcessHandle = 0;
+    this->m_ExitValue    = -1;
+    this->m_StdIn  =  0;
+    this->m_StdOut =  0;
+    this->m_StdErr =  0;
+    }
+  bool StartProcess(const char*, const char* path, bool verbose);
+  bool Wait(int timeout);
+  const std::string GetOutput() const { return this->m_Output; }
+  int GetExitValue() const { return this->m_ExitValue; }
+
+  void SetConsoleSpawn(const char* prog) { this->m_ConsoleSpawn = prog; }
+
+  static int Windows9xHack(const char* command);
+
+private:
+  bool PrivateOpen(const char*, const char*, int, int);
+  bool PrivateClose(int timeout);
+
+  HANDLE m_ProcessHandle;
+  FILE* m_StdIn;
+  FILE* m_StdOut;
+  FILE* m_StdErr;
+  
+  int m_pStdIn;
+  int m_pStdOut;
+  int m_pStdErr;
+
+  int m_ExitValue;
+
+  std::string m_Output;
+  std::string m_ConsoleSpawn;
+  bool m_Verbose;
+};
+
+
+#endif
-- 
cgit v0.12