365 lines
8.5 KiB
C
Executable File
365 lines
8.5 KiB
C
Executable File
/* 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);
|
|
FILE *pt_popen(const char *cmd, const char *mode);
|
|
int pt_pclose(FILE *fd);
|
|
|
|
|
|
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( void )
|
|
{ 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( void )
|
|
{ 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((intptr_t)pc->out[0],_O_BINARY),"r");
|
|
else
|
|
fptr = _fdopen(_open_osfhandle((intptr_t)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;
|
|
}
|
|
|
|
|