This repository has been archived on 2023-08-20. You can view files and clone it, but cannot push or open issues or pull requests.
yap-6.3/os/windows/popen.c

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;
}