text: /* popen.c
    RunSilent() is by Steven Szelei,
    and pt_popen()/pt_pclose() is by Kurt Keller
    Modified and comments translated by Steve Donovan
  
    Please note an extension; if your commmand contains '2>&1'
    then any error output will be redirected as well to the pipe.
  
    Put this file in scite\lua\src\lib and add to your project
  
    to modify liolib.c in the same dir,
    replace conditional at line 47 with:
  
    #ifndef USE_POPEN
    #ifdef __WINDOWS__
    #define USE_POPEN 1
    FILE* pt_popen(const char *cmd, const char*mode);
    int pt_pclose(FILE *file);
    uintptr_t RunSilent(const char* strCommand);
    #define popen pt_popen
    #define pclose pt_pclose
    #define system RunSilent
    #endif
    #else
    #define USE_POPEN	0
    #endif
  
  */
  
  /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  SWI-Prolog note:
  
  This        file        is         copied          verbatim         from
  http://lua-users.org/wiki/PipesOnWindows, where it is   contributed  for
  using pipes with the LUA programming  language. LUA is distributed under
  the MIT licence which is claimed to be compatible (but less restrictive)
  with the LGPL license. We  therefore  assume   we  can  use this file in
  SWI-Prolog without introducing new license problems.
  
  This version is heavily modified:
  
  	* Support Unicode commands (commands are specified in UTF-8)
  	* make popen()/pclose() thread-safe.
  	* Fix leak process-handles
  
  If you find this file and know better, please contact info@swi-prolog.org.
  - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
  
  #include <windows.h>
  #include <stdio.h>
  #include <fcntl.h>
  #include <string.h>
  #include <io.h>
  #include "pl-utf8.h"
  
  DWORD RunSilent(const char* strCommand)
  {
  	STARTUPINFO StartupInfo;
  	PROCESS_INFORMATION ProcessInfo;
  	char Args[4096];
  	char *pEnvCMD = NULL;
  	char *pDefaultCMD = "CMD.EXE";
  	ULONG rc;
  
  	memset(&StartupInfo, 0, sizeof(StartupInfo));
  	StartupInfo.cb = sizeof(STARTUPINFO);
  	StartupInfo.dwFlags = STARTF_USESHOWWINDOW;
  	StartupInfo.wShowWindow = SW_HIDE;
  
  	Args[0] = 0;
  
  	pEnvCMD = getenv("COMSPEC");
  
  	if(pEnvCMD){
  		strcpy(Args, pEnvCMD);
  	} else{
  		strcpy(Args, pDefaultCMD);
  	}
  
  	/* "/c" option - Do the command then terminate the command window */
  	strcat(Args, " /c ");
  	/*the application you would like to run from the command window */
  	strcat(Args, strCommand);
  
  	if (!CreateProcess( NULL, Args, NULL, NULL, FALSE,
  		CREATE_NEW_CONSOLE,
  		NULL,
  		NULL,
  		&StartupInfo,
  		&ProcessInfo))
  	{
  		return GetLastError();
  	}
  
  	WaitForSingleObject(ProcessInfo.hProcess, INFINITE);
  	if(!GetExitCodeProcess(ProcessInfo.hProcess, &rc))
  		rc = 0;
  
  	CloseHandle(ProcessInfo.hThread);
  	CloseHandle(ProcessInfo.hProcess);
  
  	return rc;
  
  }
  
  /*------------------------------------------------------------------------------
    Globals for the Routines pt_popen() / pt_pclose()
  ------------------------------------------------------------------------------*/
  
  CRITICAL_SECTION lock;
  #define LOCK()   EnterCriticalSection(&lock);
  #define UNLOCK() LeaveCriticalSection(&lock);
  
  static void
  pt_init()
  { InitializeCriticalSection(&lock);
  }
  
  
  typedef struct pipe_context
  { struct pipe_context *next;
    FILE   *fd;
    HANDLE in[2];
    HANDLE out[2];
    HANDLE err[2];
    char   mode;				/* 'r' or 'w' */
  } pipe_context;
  
  
  static pipe_context *pipes = NULL;
  
  static pipe_context *
  allocPipeContext()
  { pipe_context *pc = malloc(sizeof(*pc));
  
    if ( !pc )
      return NULL;
  
    pc->in[0]   = INVALID_HANDLE_VALUE;
    pc->in[1]   = INVALID_HANDLE_VALUE;
    pc->out[0]  = INVALID_HANDLE_VALUE;
    pc->out[1]  = INVALID_HANDLE_VALUE;
    pc->err[0]  = INVALID_HANDLE_VALUE;
    pc->err[1]  = INVALID_HANDLE_VALUE;
  
    return pc;
  }
  
  
  static void
  discardPipeContext(pipe_context *pc)
  { if (pc->in[0]  != INVALID_HANDLE_VALUE)
      CloseHandle(pc->in[0]);
    if (pc->in[1]  != INVALID_HANDLE_VALUE)
      CloseHandle(pc->in[1]);
    if (pc->out[0] != INVALID_HANDLE_VALUE)
      CloseHandle(pc->out[0]);
    if (pc->out[1] != INVALID_HANDLE_VALUE)
      CloseHandle(pc->out[1]);
    if (pc->err[0] != INVALID_HANDLE_VALUE)
      CloseHandle(pc->err[0]);
    if (pc->err[1] != INVALID_HANDLE_VALUE)
      CloseHandle(pc->err[1]);
  
    free(pc);
  }
  
  
  
  static void
  linkPipeContext(pipe_context *pc)
  { LOCK();
    pc->next = pipes;
    pipes = pc;
    UNLOCK();
  }
  
  
  static int
  my_pipe(HANDLE *readwrite)
  {
    SECURITY_ATTRIBUTES sa;
  
    sa.nLength = sizeof(sa);          /* Length in bytes */
    sa.bInheritHandle = 1;            /* the child must inherit these handles */
    sa.lpSecurityDescriptor = NULL;
  
    if (! CreatePipe (&readwrite[0],&readwrite[1],&sa,1 << 13))
    {
      errno = -1; /* EMFILE; que? */
      return -1;
    }
  
    return 0;
  }
  
  /*------------------------------------------------------------------------------
    Replacement for 'popen()' under Windows.
  
    cmd is taken to be encoded in UTF-8 for compatibility with the Unix
    version.
  
    NOTE: if cmd contains '2>&1', we connect the standard error file handle
      to the standard output file handle.
  ------------------------------------------------------------------------------*/
  
  static void
  utf8towcs(wchar_t *o, const char *src)
  { for( ; *src; )
    { int wc;
  
      src = utf8_get_char(src, &wc);
      *o++ = wc;
    }
    *o = 0;
  }
  
  
  FILE *
  pt_popen(const char *cmd, const char *mode)
  { FILE *fptr = NULL;
    PROCESS_INFORMATION piProcInfo;
    STARTUPINFOW siStartInfo;
    int success, redirect_error = 0;
    wchar_t *wcmd = NULL;
    wchar_t *err2out;
    pipe_context *pc;
  
    size_t utf8len = utf8_strlen(cmd, strlen(cmd));
    if ( !(wcmd = malloc((utf8len+1)*sizeof(wchar_t))) )
    { return NULL;
    }
    utf8towcs(wcmd, cmd);
  
    if ( !(pc=allocPipeContext()) )
      goto finito;
    if ( !mode || !*mode )
      goto finito;
    pc->mode = *mode;
    if ( pc->mode != 'r' && pc->mode != 'w' )
      goto finito;
  
    /*
     * Shall we redirect stderr to stdout ? */
    if ( (err2out=wcsstr(wcmd, L"2>&1")) != NULL)
    { /* this option doesn't apply to win32 shells, so we clear it out! */
       wcsncpy(err2out, L"    ", 4);
       redirect_error = 1;
    }
  
    /*
     * Create the Pipes... */
    if (my_pipe(pc->in)  == -1 ||
        my_pipe(pc->out) == -1)
      goto finito;
    if ( !redirect_error )
    { if ( my_pipe(pc->err) == -1)
        goto finito;
    }
  
    /*
     * Now create the child process */
    ZeroMemory(&siStartInfo, sizeof(STARTUPINFO));
    siStartInfo.cb           = sizeof(STARTUPINFO);
    siStartInfo.hStdInput    = pc->in[0];
    siStartInfo.hStdOutput   = pc->out[1];
    if ( redirect_error )
      siStartInfo.hStdError  = pc->out[1];
    else
      siStartInfo.hStdError  = pc->err[1];
    siStartInfo.dwFlags      = STARTF_USESTDHANDLES;
  
    success = CreateProcessW(NULL,
  			   wcmd,	// command line
  			   NULL,	// process security attributes
  			   NULL,	// primary thread security attributes
  			   TRUE,	// handles are inherited
  			   CREATE_NO_WINDOW,  // creation flags: without window (?)
  			   NULL,	// use parent's environment
  			   NULL,	// use parent's current directory
  			   &siStartInfo, // STARTUPINFO pointer
  			   &piProcInfo); // receives PROCESS_INFORMATION
  
    if ( !success )
      goto finito;
  
    CloseHandle(piProcInfo.hThread);
    CloseHandle(piProcInfo.hProcess);
  
    /*
     * These handles listen to the Child process */
    CloseHandle(pc->in[0]);  pc->in[0]  = INVALID_HANDLE_VALUE;
    CloseHandle(pc->out[1]); pc->out[1] = INVALID_HANDLE_VALUE;
    if ( pc->err[1] != INVALID_HANDLE_VALUE )
    { CloseHandle(pc->err[1]);
      pc->err[1] = INVALID_HANDLE_VALUE;
    }
  
    if ( pc->mode == 'r' )
      fptr = _fdopen(_open_osfhandle((long)pc->out[0],_O_BINARY),"r");
    else
      fptr = _fdopen(_open_osfhandle((long)pc->in[1],_O_BINARY),"w");
  
  finito:
    if ( fptr )
    { pc->fd = fptr;
      linkPipeContext(pc);
    } else
    { if ( pc )
        discardPipeContext(pc);
    }
    if ( wcmd )
      free(wcmd);
  
    return fptr;
  }
  
  /*------------------------------------------------------------------------------
    Replacement for 'pclose()' under Win32
  ------------------------------------------------------------------------------*/
  int
  pt_pclose(FILE *fd)
  { pipe_context **ppc;
    int rc;
  
    if ( !fd )
    { errno = EINVAL;
      return -1;
    }
  
    rc = fclose(fd);
    LOCK();
    for(ppc = &pipes; *ppc; ppc=&(*ppc)->next)
    { pipe_context *pc = *ppc;
  
      if ( pc->fd == fd )
      { *ppc = pc->next;
  
        UNLOCK();
        if ( pc->err[0] != INVALID_HANDLE_VALUE )
  	CloseHandle(pc->err[0]);
        if ( pc->mode == 'r' )
        { CloseHandle(pc->in[1]);
        } else
        { CloseHandle(pc->out[0]);
        }
  
        free(pc);
  
        return rc;
      }
    }
  
    UNLOCK();
    errno = EINVAL;
    return -1;
  }
  
  
  