#include "tclWinInt.h"
#ifndef __CYGWIN32__
#include <dos.h>
#endif
#include <fcntl.h>
#include <io.h>
#include <sys/stat.h>
static int initialized = 0;
#define APPL_NONE 0
#define APPL_DOS 1
#define APPL_WIN3X 2
#define APPL_WIN32 3
#define WIN32S_PIPE 1
#define WIN32S_TMPFILE 2
#define WIN_FILE 3
typedef struct WinFile {
int type;
HANDLE handle;
} WinFile;
typedef struct TmpFile {
WinFile file;
char name[MAX_PATH];
} TmpFile;
typedef struct WinPipe {
WinFile file;
struct WinPipe *otherPtr;
char *fileName;
} WinPipe;
typedef struct ProcInfo {
HANDLE hProcess;
DWORD dwProcessId;
struct ProcInfo *nextPtr;
} ProcInfo;
static ProcInfo *procList;
#define PIPE_PENDING (1<<0)
#define PIPE_ASYNC (1<<1)
#define PIPE_READABLE (1<<2)
#define PIPE_CLOSED (1<<3)
#define PIPE_HAS_THREAD (1<<4)
#define PIPE_READAHEAD (1<<5)
typedef struct PipeInfo {
Tcl_Channel channel;
int validMask;
int watchMask;
int flags;
TclFile readFile;
TclFile writeFile;
TclFile errorFile;
int numPids;
Tcl_Pid *pidPtr;
struct PipeInfo *nextPtr;
HANDLE flagsMutex;
HANDLE mutex;
HANDLE tryReadEvent;
char readAhead;
} PipeInfo;
static PipeInfo *firstPipePtr;
typedef struct PipeEvent {
Tcl_Event header;
PipeInfo *infoPtr;
} PipeEvent;
static int ApplicationType(Tcl_Interp *interp, const char *fileName,
char *fullName);
static void BuildCommandLine(int argc, char **argv, Tcl_DString *linePtr);
static void CopyChannel(HANDLE dst, HANDLE src);
static BOOL HasConsole(void);
static TclFile MakeFile(HANDLE handle);
static char * MakeTempFile(Tcl_DString *namePtr);
static int PipeBlockModeProc(ClientData instanceData, int mode);
static void PipeCheckProc _ANSI_ARGS_((ClientData clientData,
int flags));
static int PipeCloseProc(ClientData instanceData, Tcl_Interp *interp);
static int PipeEventProc(Tcl_Event *evPtr, int flags);
static void PipeExitHandler(ClientData clientData);
static int PipeGetHandleProc(ClientData instanceData, int direction,
ClientData *handlePtr);
static void PipeInit(void);
static int PipeInputProc(ClientData instanceData, char *buf, int toRead,
int *errorCode);
static int PipeOutputProc(ClientData instanceData, char *buf, int toWrite,
int *errorCode);
static void PipeWatchProc(ClientData instanceData, int mask);
static void PipeSetupProc _ANSI_ARGS_((ClientData clientData,
int flags));
static int TempFileName(char name[MAX_PATH]);
static int PipeGetFlags _ANSI_ARGS_((PipeInfo *));
static void PipeSetFlag _ANSI_ARGS_((PipeInfo *, int));
static void PipeResetFlag _ANSI_ARGS_((PipeInfo *, int));
static DWORD PipeThread _ANSI_ARGS_((LPVOID arg));
static LRESULT CALLBACK PipeProc _ANSI_ARGS_((HWND hwnd, UINT message,
WPARAM wParam, LPARAM lParam));
static Tcl_ChannelType pipeChannelType = {
"pipe",
PipeBlockModeProc,
PipeCloseProc,
PipeInputProc,
PipeOutputProc,
NULL,
NULL,
NULL,
PipeWatchProc,
PipeGetHandleProc,
};
static HWND pipeHwnd;
#define PIPE_MESSAGE (WM_USER + 1)
static int
PipeGetFlags(pipe)
PipeInfo *pipe;
{
int flags;
WaitForSingleObject(pipe->flagsMutex, INFINITE);
flags = pipe->flags;
ReleaseMutex(pipe->flagsMutex);
return flags;
}
static void
PipeSetFlag(pipe, flag)
PipeInfo *pipe;
int flag;
{
WaitForSingleObject(pipe->flagsMutex, INFINITE);
pipe->flags |= flag;
ReleaseMutex(pipe->flagsMutex);
}
static void
PipeResetFlag(pipe, flag)
PipeInfo *pipe;
int flag;
{
WaitForSingleObject(pipe->flagsMutex, INFINITE);
pipe->flags &= ~ (flag);
ReleaseMutex(pipe->flagsMutex);
}
static DWORD
PipeThread(arg)
LPVOID arg;
{
PipeInfo *pipe = (PipeInfo *) arg;
WinFile *file = (WinFile*) pipe->readFile;
HANDLE handle = file->handle;
while (1) {
char b;
DWORD got;
WaitForSingleObject(pipe->tryReadEvent, INFINITE);
if (PipeGetFlags(pipe) & PIPE_CLOSED) {
break;
}
WaitForSingleObject(pipe->mutex, INFINITE);
if ((PipeGetFlags(pipe) & PIPE_READAHEAD) == 0) {
if (ReadFile(handle, &b, 1, &got, NULL) && got == 1) {
pipe->readAhead = b;
PipeSetFlag(pipe, PIPE_READAHEAD);
}
}
PipeSetFlag(pipe, PIPE_READABLE);
ResetEvent(pipe->tryReadEvent);
ReleaseMutex(pipe->mutex);
if (PipeGetFlags(pipe) & PIPE_CLOSED) {
break;
}
PostMessage(pipeHwnd, PIPE_MESSAGE, 0, (LPARAM) pipe);
}
CloseHandle(pipe->flagsMutex);
CloseHandle(pipe->tryReadEvent);
CloseHandle(pipe->mutex);
ckfree((char *)pipe);
return 0;
}
static LRESULT CALLBACK
PipeProc(hwnd, message, wParam, lParam)
HWND hwnd;
UINT message;
WPARAM wParam;
LPARAM lParam;
{
if (message != PIPE_MESSAGE) {
return DefWindowProc(hwnd, message, wParam, lParam);
}
return 0;
}
static void
PipeInit()
{
WNDCLASS class;
initialized = 1;
firstPipePtr = NULL;
procList = NULL;
Tcl_CreateEventSource(PipeSetupProc, PipeCheckProc, NULL);
Tcl_CreateExitHandler(PipeExitHandler, NULL);
class.style = 0;
class.cbClsExtra = 0;
class.cbWndExtra = 0;
class.hInstance = TclWinGetTclInstance();
class.hbrBackground = NULL;
class.lpszMenuName = NULL;
class.lpszClassName = "TclPipe";
class.lpfnWndProc = PipeProc;
class.hIcon = NULL;
class.hCursor = NULL;
if (RegisterClass(&class)) {
pipeHwnd = CreateWindow("TclPipe", "TclPipe", WS_TILED, 0, 0,
0, 0, NULL, NULL, class.hInstance, NULL);
} else {
pipeHwnd = NULL;
TclWinConvertError(GetLastError());
}
}
static void
PipeExitHandler(clientData)
ClientData clientData;
{
Tcl_DeleteEventSource(PipeSetupProc, PipeCheckProc, NULL);
initialized = 0;
UnregisterClass("TclPipe", TclWinGetTclInstance());
if (pipeHwnd != NULL) {
DestroyWindow(pipeHwnd);
pipeHwnd = NULL;
}
}
void
PipeSetupProc(data, flags)
ClientData data;
int flags;
{
PipeInfo *infoPtr;
Tcl_Time blockTime = { 0, 0 };
if (!(flags & TCL_FILE_EVENTS)) {
return;
}
for (infoPtr = firstPipePtr; infoPtr != NULL; infoPtr = infoPtr->nextPtr) {
if ((infoPtr->watchMask &~ TCL_READABLE)
|| ((infoPtr->watchMask & TCL_READABLE)
&& ((PipeGetFlags(infoPtr) & PIPE_HAS_THREAD) == 0
|| (PipeGetFlags(infoPtr) & PIPE_READABLE)))) {
Tcl_SetMaxBlockTime(&blockTime);
break;
} else if (infoPtr->watchMask & TCL_READABLE) {
SetEvent(infoPtr->tryReadEvent);
}
}
}
static void
PipeCheckProc(data, flags)
ClientData data;
int flags;
{
PipeInfo *infoPtr;
PipeEvent *evPtr;
if (!(flags & TCL_FILE_EVENTS)) {
return;
}
for (infoPtr = firstPipePtr; infoPtr != NULL; infoPtr = infoPtr->nextPtr) {
if (((infoPtr->watchMask &~ TCL_READABLE)
|| ((infoPtr->watchMask & TCL_READABLE)
&& ((PipeGetFlags(infoPtr) & PIPE_HAS_THREAD) == 0
|| (PipeGetFlags(infoPtr) & PIPE_READABLE))))
&& !(PipeGetFlags(infoPtr) & PIPE_PENDING)) {
PipeSetFlag(infoPtr, PIPE_PENDING);
evPtr = (PipeEvent *) ckalloc(sizeof(PipeEvent));
evPtr->header.proc = PipeEventProc;
evPtr->infoPtr = infoPtr;
Tcl_QueueEvent((Tcl_Event *) evPtr, TCL_QUEUE_TAIL);
}
}
}
static TclFile
MakeFile(handle)
HANDLE handle;
{
WinFile *filePtr;
filePtr = (WinFile *) ckalloc(sizeof(WinFile));
filePtr->type = WIN_FILE;
filePtr->handle = handle;
return (TclFile)filePtr;
}
TclFile
TclpMakeFile(channel, direction)
Tcl_Channel channel;
int direction;
{
HANDLE handle;
if (Tcl_GetChannelHandle(channel, direction,
(ClientData *) &handle) == TCL_OK) {
return MakeFile(handle);
} else {
return (TclFile) NULL;
}
}
static int
TempFileName(name)
char name[MAX_PATH];
{
if ((GetTempPath(MAX_PATH, name) == 0) ||
(GetTempFileName(name, "TCL", 0, name) == 0)) {
name[0] = '.';
name[1] = '\0';
if (GetTempFileName(name, "TCL", 0, name) == 0) {
return 0;
}
}
return 1;
}
TclFile
TclpCreateTempFile(contents, namePtr)
char *contents;
Tcl_DString *namePtr;
{
char name[MAX_PATH];
HANDLE handle;
if (TempFileName(name) == 0) {
return NULL;
}
handle = CreateFile(name, GENERIC_READ | GENERIC_WRITE, 0, NULL,
CREATE_ALWAYS, FILE_ATTRIBUTE_TEMPORARY|FILE_FLAG_DELETE_ON_CLOSE,
NULL);
if (handle == INVALID_HANDLE_VALUE) {
goto error;
}
if (contents != NULL) {
DWORD result, length;
char *p;
for (p = contents; *p != '\0'; p++) {
if (*p == '\n') {
length = p - contents;
if (length > 0) {
if (!WriteFile(handle, contents, length, &result, NULL)) {
goto error;
}
}
if (!WriteFile(handle, "\r\n", 2, &result, NULL)) {
goto error;
}
contents = p+1;
}
}
length = p - contents;
if (length > 0) {
if (!WriteFile(handle, contents, length, &result, NULL)) {
goto error;
}
}
}
if (SetFilePointer(handle, 0, NULL, FILE_BEGIN) == 0xFFFFFFFF) {
goto error;
}
if (namePtr != NULL) {
Tcl_DStringAppend(namePtr, name, -1);
}
if (TclWinGetPlatformId() == VER_PLATFORM_WIN32s) {
TmpFile *tmpFilePtr = (TmpFile *) ckalloc(sizeof(TmpFile));
tmpFilePtr->file.type = WIN32S_TMPFILE;
tmpFilePtr->file.handle = handle;
strcpy(tmpFilePtr->name, name);
return (TclFile)tmpFilePtr;
} else {
return MakeFile(handle);
}
error:
TclWinConvertError(GetLastError());
CloseHandle(handle);
DeleteFile(name);
return NULL;
}
TclFile
TclpOpenFile(path, mode)
char *path;
int mode;
{
HANDLE handle;
DWORD accessMode, createMode, shareMode, flags;
SECURITY_ATTRIBUTES sec;
switch (mode & (O_RDONLY | O_WRONLY | O_RDWR)) {
case O_RDONLY:
accessMode = GENERIC_READ;
break;
case O_WRONLY:
accessMode = GENERIC_WRITE;
break;
case O_RDWR:
accessMode = (GENERIC_READ | GENERIC_WRITE);
break;
default:
TclWinConvertError(ERROR_INVALID_FUNCTION);
return NULL;
}
switch (mode & (O_CREAT | O_EXCL | O_TRUNC)) {
case (O_CREAT | O_EXCL):
case (O_CREAT | O_EXCL | O_TRUNC):
createMode = CREATE_NEW;
break;
case (O_CREAT | O_TRUNC):
createMode = CREATE_ALWAYS;
break;
case O_CREAT:
createMode = OPEN_ALWAYS;
break;
case O_TRUNC:
case (O_TRUNC | O_EXCL):
createMode = TRUNCATE_EXISTING;
break;
default:
createMode = OPEN_EXISTING;
break;
}
flags = 0;
if (!(mode & O_CREAT)) {
flags = GetFileAttributes(path);
if (flags == 0xFFFFFFFF) {
flags = 0;
}
}
sec.nLength = sizeof(sec);
sec.lpSecurityDescriptor = NULL;
sec.bInheritHandle = 0;
shareMode = FILE_SHARE_READ | FILE_SHARE_WRITE;
handle = CreateFile(path, accessMode, shareMode, &sec, createMode, flags,
(HANDLE) NULL);
if (handle == INVALID_HANDLE_VALUE) {
DWORD err = GetLastError();
if ((err & 0xffffL) == ERROR_OPEN_FAILED) {
err = (mode & O_CREAT) ? ERROR_FILE_EXISTS : ERROR_FILE_NOT_FOUND;
}
TclWinConvertError(err);
return NULL;
}
if (mode & O_WRONLY) {
SetFilePointer(handle, 0, NULL, FILE_END);
}
return MakeFile(handle);
}
int
TclpCreatePipe(readPipe, writePipe)
TclFile *readPipe;
TclFile *writePipe;
{
HANDLE readHandle, writeHandle;
if (CreatePipe(&readHandle, &writeHandle, NULL, 0) != 0) {
*readPipe = MakeFile(readHandle);
*writePipe = MakeFile(writeHandle);
return 1;
}
if (TclWinGetPlatformId() == VER_PLATFORM_WIN32s) {
WinPipe *readPipePtr, *writePipePtr;
char buf[MAX_PATH];
if (TempFileName(buf) != 0) {
readPipePtr = (WinPipe *) ckalloc(sizeof(WinPipe));
writePipePtr = (WinPipe *) ckalloc(sizeof(WinPipe));
readPipePtr->file.type = WIN32S_PIPE;
readPipePtr->otherPtr = writePipePtr;
readPipePtr->fileName = strcpy(ckalloc(strlen(buf) + 1), buf);
readPipePtr->file.handle = INVALID_HANDLE_VALUE;
writePipePtr->file.type = WIN32S_PIPE;
writePipePtr->otherPtr = readPipePtr;
writePipePtr->fileName = readPipePtr->fileName;
writePipePtr->file.handle = INVALID_HANDLE_VALUE;
*readPipe = (TclFile)readPipePtr;
*writePipe = (TclFile)writePipePtr;
return 1;
}
}
TclWinConvertError(GetLastError());
return 0;
}
int
TclpCloseFile(file)
TclFile file;
{
WinFile *filePtr = (WinFile *) file;
WinPipe *pipePtr;
switch (filePtr->type) {
case WIN_FILE:
case WIN32S_TMPFILE:
if (CloseHandle(filePtr->handle) == FALSE) {
TclWinConvertError(GetLastError());
ckfree((char *) filePtr);
return -1;
}
if (filePtr->type == WIN32S_TMPFILE) {
DeleteFile(((TmpFile*)filePtr)->name);
}
break;
case WIN32S_PIPE:
pipePtr = (WinPipe *) file;
if (pipePtr->otherPtr != NULL) {
pipePtr->otherPtr->otherPtr = NULL;
} else {
if (pipePtr->file.handle != INVALID_HANDLE_VALUE) {
CloseHandle(pipePtr->file.handle);
}
DeleteFile(pipePtr->fileName);
ckfree((char *) pipePtr->fileName);
}
break;
default:
panic("Tcl_CloseFile: unexpected file type");
}
ckfree((char *) filePtr);
return 0;
}
unsigned long
TclpGetPid(pid)
Tcl_Pid pid;
{
ProcInfo *infoPtr;
for (infoPtr = procList; infoPtr != NULL; infoPtr = infoPtr->nextPtr) {
if (infoPtr->hProcess == (HANDLE) pid) {
return infoPtr->dwProcessId;
}
}
return (unsigned long) -1;
}
int
TclpCreateProcess(interp, argc, argv, inputFile, outputFile, errorFile,
pidPtr)
Tcl_Interp *interp;
int argc;
char **argv;
TclFile inputFile;
TclFile outputFile;
TclFile errorFile;
Tcl_Pid *pidPtr;
{
int result, applType, createFlags;
Tcl_DString cmdLine;
STARTUPINFO startInfo;
PROCESS_INFORMATION procInfo;
SECURITY_ATTRIBUTES secAtts;
HANDLE hProcess, h, inputHandle, outputHandle, errorHandle;
char execPath[MAX_PATH];
char *originalName;
WinFile *filePtr;
if (!initialized) {
PipeInit();
}
applType = ApplicationType(interp, argv[0], execPath);
if (applType == APPL_NONE) {
return TCL_ERROR;
}
originalName = argv[0];
argv[0] = execPath;
result = TCL_ERROR;
Tcl_DStringInit(&cmdLine);
if (TclWinGetPlatformId() == VER_PLATFORM_WIN32s) {
MSG msg;
DWORD status;
DWORD args[4];
void *trans[5];
char *inputFileName, *outputFileName;
Tcl_DString inputTempFile, outputTempFile;
BuildCommandLine(argc, argv, &cmdLine);
ZeroMemory(&startInfo, sizeof(startInfo));
startInfo.cb = sizeof(startInfo);
Tcl_DStringInit(&inputTempFile);
Tcl_DStringInit(&outputTempFile);
outputHandle = INVALID_HANDLE_VALUE;
inputFileName = NULL;
outputFileName = NULL;
if (inputFile != NULL) {
filePtr = (WinFile *) inputFile;
switch (filePtr->type) {
case WIN_FILE:
case WIN32S_TMPFILE: {
h = INVALID_HANDLE_VALUE;
inputFileName = MakeTempFile(&inputTempFile);
if (inputFileName != NULL) {
h = CreateFile(inputFileName, GENERIC_WRITE, 0,
NULL, CREATE_ALWAYS, 0, NULL);
}
if (h == INVALID_HANDLE_VALUE) {
Tcl_AppendResult(interp, "couldn't duplicate input handle: ",
Tcl_PosixError(interp), (char *) NULL);
goto end32s;
}
CopyChannel(h, filePtr->handle);
CloseHandle(h);
break;
}
case WIN32S_PIPE: {
inputFileName = ((WinPipe*)inputFile)->fileName;
break;
}
}
}
if (inputFileName == NULL) {
inputFileName = "nul";
}
if (outputFile != NULL) {
filePtr = (WinFile *)outputFile;
if (filePtr->type == WIN_FILE) {
outputFileName = MakeTempFile(&outputTempFile);
if (outputFileName == NULL) {
Tcl_AppendResult(interp, "couldn't duplicate output handle: ",
Tcl_PosixError(interp), (char *) NULL);
goto end32s;
}
outputHandle = filePtr->handle;
} else if (filePtr->type == WIN32S_PIPE) {
outputFileName = ((WinPipe*)outputFile)->fileName;
}
}
if (outputFileName == NULL) {
outputFileName = "nul";
}
if (applType == APPL_DOS) {
args[0] = (DWORD) Tcl_DStringValue(&cmdLine);
args[1] = (DWORD) inputFileName;
args[2] = (DWORD) outputFileName;
trans[0] = &args[0];
trans[1] = &args[1];
trans[2] = &args[2];
trans[3] = NULL;
if (TclWinSynchSpawn(args, 0, trans, pidPtr) != 0) {
result = TCL_OK;
}
} else if (applType == APPL_WIN3X) {
args[0] = (DWORD) Tcl_DStringValue(&cmdLine);
trans[0] = &args[0];
trans[1] = NULL;
if (TclWinSynchSpawn(args, 1, trans, pidPtr) != 0) {
result = TCL_OK;
}
} else {
if (CreateProcess(NULL, Tcl_DStringValue(&cmdLine), NULL, NULL,
FALSE, DETACHED_PROCESS, NULL, NULL, &startInfo,
&procInfo) != 0) {
CloseHandle(procInfo.hThread);
while (1) {
if (GetExitCodeProcess(procInfo.hProcess, &status) == FALSE) {
break;
}
if (status != STILL_ACTIVE) {
break;
}
if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE) == TRUE) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
*pidPtr = (Tcl_Pid) procInfo.hProcess;
if (*pidPtr != 0) {
ProcInfo *procPtr = (ProcInfo *) ckalloc(sizeof(ProcInfo));
procPtr->hProcess = procInfo.hProcess;
procPtr->dwProcessId = procInfo.dwProcessId;
procPtr->nextPtr = procList;
procList = procPtr;
}
result = TCL_OK;
}
}
if (result != TCL_OK) {
TclWinConvertError(GetLastError());
Tcl_AppendResult(interp, "couldn't execute \"", originalName,
"\": ", Tcl_PosixError(interp), (char *) NULL);
}
end32s:
if (outputHandle != INVALID_HANDLE_VALUE) {
h = CreateFile(outputFileName, GENERIC_READ, 0, NULL, OPEN_ALWAYS,
0, NULL);
if (h != INVALID_HANDLE_VALUE) {
CopyChannel(outputHandle, h);
}
CloseHandle(h);
}
if (inputFileName == Tcl_DStringValue(&inputTempFile)) {
DeleteFile(inputFileName);
}
if (outputFileName == Tcl_DStringValue(&outputTempFile)) {
DeleteFile(outputFileName);
}
Tcl_DStringFree(&inputTempFile);
Tcl_DStringFree(&outputTempFile);
Tcl_DStringFree(&cmdLine);
return result;
}
hProcess = GetCurrentProcess();
ZeroMemory(&startInfo, sizeof(startInfo));
startInfo.cb = sizeof(startInfo);
startInfo.dwFlags = STARTF_USESTDHANDLES;
startInfo.hStdInput = INVALID_HANDLE_VALUE;
startInfo.hStdOutput= INVALID_HANDLE_VALUE;
startInfo.hStdError = INVALID_HANDLE_VALUE;
secAtts.nLength = sizeof(SECURITY_ATTRIBUTES);
secAtts.lpSecurityDescriptor = NULL;
secAtts.bInheritHandle = TRUE;
inputHandle = INVALID_HANDLE_VALUE;
if (inputFile != NULL) {
filePtr = (WinFile *)inputFile;
if (filePtr->type == WIN_FILE) {
inputHandle = filePtr->handle;
}
}
outputHandle = INVALID_HANDLE_VALUE;
if (outputFile != NULL) {
filePtr = (WinFile *)outputFile;
if (filePtr->type == WIN_FILE) {
outputHandle = filePtr->handle;
}
}
errorHandle = INVALID_HANDLE_VALUE;
if (errorFile != NULL) {
filePtr = (WinFile *)errorFile;
if (filePtr->type == WIN_FILE) {
errorHandle = filePtr->handle;
}
}
if (inputHandle == INVALID_HANDLE_VALUE) {
if (CreatePipe(&startInfo.hStdInput, &h, &secAtts, 0) != FALSE) {
CloseHandle(h);
}
} else {
DuplicateHandle(hProcess, inputHandle, hProcess, &startInfo.hStdInput,
0, TRUE, DUPLICATE_SAME_ACCESS);
}
if (startInfo.hStdInput == INVALID_HANDLE_VALUE) {
TclWinConvertError(GetLastError());
Tcl_AppendResult(interp, "couldn't duplicate input handle: ",
Tcl_PosixError(interp), (char *) NULL);
goto end;
}
if (outputHandle == INVALID_HANDLE_VALUE) {
if ((TclWinGetPlatformId() == VER_PLATFORM_WIN32_WINDOWS)
&& (applType == APPL_DOS)) {
if (CreatePipe(&h, &startInfo.hStdOutput, &secAtts, 0) != FALSE) {
CloseHandle(h);
}
} else {
startInfo.hStdOutput = CreateFile("NUL:", GENERIC_WRITE, 0,
&secAtts, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
}
} else {
DuplicateHandle(hProcess, outputHandle, hProcess, &startInfo.hStdOutput,
0, TRUE, DUPLICATE_SAME_ACCESS);
}
if (startInfo.hStdOutput == INVALID_HANDLE_VALUE) {
TclWinConvertError(GetLastError());
Tcl_AppendResult(interp, "couldn't duplicate output handle: ",
Tcl_PosixError(interp), (char *) NULL);
goto end;
}
if (errorHandle == INVALID_HANDLE_VALUE) {
startInfo.hStdError = CreateFile("NUL:", GENERIC_WRITE, 0,
&secAtts, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
} else {
DuplicateHandle(hProcess, errorHandle, hProcess, &startInfo.hStdError,
0, TRUE, DUPLICATE_SAME_ACCESS);
}
if (startInfo.hStdError == INVALID_HANDLE_VALUE) {
TclWinConvertError(GetLastError());
Tcl_AppendResult(interp, "couldn't duplicate error handle: ",
Tcl_PosixError(interp), (char *) NULL);
goto end;
}
if (TclWinGetPlatformId() == VER_PLATFORM_WIN32_NT) {
if (HasConsole()) {
createFlags = 0;
} else if (applType == APPL_DOS) {
startInfo.wShowWindow = SW_HIDE;
startInfo.dwFlags |= STARTF_USESHOWWINDOW;
createFlags = CREATE_NEW_CONSOLE;
Tcl_DStringAppend(&cmdLine, "cmd.exe /c ", -1);
} else {
createFlags = DETACHED_PROCESS;
}
} else {
if (HasConsole()) {
createFlags = 0;
} else {
createFlags = DETACHED_PROCESS;
}
if (applType == APPL_DOS) {
if (createFlags != 0) {
startInfo.wShowWindow = SW_HIDE;
startInfo.dwFlags |= STARTF_USESHOWWINDOW;
createFlags = CREATE_NEW_CONSOLE;
}
Tcl_DStringAppend(&cmdLine, "cygtclpip" STRINGIFY(TCL_MAJOR_VERSION)
STRINGIFY(TCL_MINOR_VERSION) ".dll ", -1);
}
}
BuildCommandLine(argc, argv, &cmdLine);
if (!CreateProcess(NULL, Tcl_DStringValue(&cmdLine), NULL, NULL, TRUE,
createFlags, NULL, NULL, &startInfo, &procInfo)) {
TclWinConvertError(GetLastError());
Tcl_AppendResult(interp, "couldn't execute \"", originalName,
"\": ", Tcl_PosixError(interp), (char *) NULL);
goto end;
}
if (applType == APPL_DOS) {
WaitForSingleObject(hProcess, 50);
}
WaitForInputIdle(procInfo.hProcess, 5000);
CloseHandle(procInfo.hThread);
*pidPtr = (Tcl_Pid) procInfo.hProcess;
if (*pidPtr != 0) {
ProcInfo *procPtr = (ProcInfo *) ckalloc(sizeof(ProcInfo));
procPtr->hProcess = procInfo.hProcess;
procPtr->dwProcessId = procInfo.dwProcessId;
procPtr->nextPtr = procList;
procList = procPtr;
}
result = TCL_OK;
end:
Tcl_DStringFree(&cmdLine);
if (startInfo.hStdInput != INVALID_HANDLE_VALUE) {
CloseHandle(startInfo.hStdInput);
}
if (startInfo.hStdOutput != INVALID_HANDLE_VALUE) {
CloseHandle(startInfo.hStdOutput);
}
if (startInfo.hStdError != INVALID_HANDLE_VALUE) {
CloseHandle(startInfo.hStdError);
}
return result;
}
static BOOL
HasConsole()
{
HANDLE handle = CreateFile("CONOUT$", GENERIC_WRITE, FILE_SHARE_WRITE,
NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
if (handle != INVALID_HANDLE_VALUE) {
CloseHandle(handle);
return TRUE;
} else {
return FALSE;
}
}
static int
ApplicationType(interp, originalName, fullPath)
Tcl_Interp *interp;
const char *originalName;
char fullPath[MAX_PATH];
{
int applType, i;
HANDLE hFile;
char *ext, *rest;
char buf[2];
DWORD read;
IMAGE_DOS_HEADER header;
static char extensions[][5] = {"", ".com", ".exe", ".bat"};
applType = APPL_NONE;
for (i = 0; i < (int) (sizeof(extensions) / sizeof(extensions[0])); i++) {
lstrcpyn(fullPath, originalName, MAX_PATH - 5);
lstrcat(fullPath, extensions[i]);
SearchPath(NULL, fullPath, NULL, MAX_PATH, fullPath, &rest);
if (GetFileAttributes(fullPath) & FILE_ATTRIBUTE_DIRECTORY) {
continue;
}
ext = strrchr(fullPath, '.');
if ((ext != NULL) && (strcmpi(ext, ".bat") == 0)) {
applType = APPL_DOS;
break;
}
hFile = CreateFile(fullPath, GENERIC_READ, FILE_SHARE_READ, NULL,
OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
if (hFile == INVALID_HANDLE_VALUE) {
continue;
}
header.e_magic = 0;
ReadFile(hFile, (void *) &header, sizeof(header), &read, NULL);
if (header.e_magic != IMAGE_DOS_SIGNATURE) {
CloseHandle(hFile);
if ((ext != NULL) && (strcmpi(ext, ".com") == 0)) {
applType = APPL_DOS;
break;
}
continue;
}
if (header.e_lfarlc != sizeof(header)) {
CloseHandle(hFile);
applType = APPL_DOS;
break;
}
buf[0] = '\0';
SetFilePointer(hFile, header.e_lfanew, NULL, FILE_BEGIN);
ReadFile(hFile, (void *) buf, 2, &read, NULL);
CloseHandle(hFile);
if ((buf[0] == 'N') && (buf[1] == 'E')) {
applType = APPL_WIN3X;
} else if ((buf[0] == 'P') && (buf[1] == 'E')) {
applType = APPL_WIN32;
} else {
applType = APPL_DOS;
}
break;
}
if (applType == APPL_NONE) {
TclWinConvertError(GetLastError());
Tcl_AppendResult(interp, "couldn't execute \"", originalName,
"\": ", Tcl_PosixError(interp), (char *) NULL);
return APPL_NONE;
}
if ((applType == APPL_DOS) || (applType == APPL_WIN3X)) {
GetShortPathName(fullPath, fullPath, MAX_PATH);
}
return applType;
}
static void
BuildCommandLine(argc, argv, linePtr)
int argc;
char **argv;
Tcl_DString *linePtr;
{
char *start, *special;
int quote, i;
for (i = 0; i < argc; i++) {
if (i > 0) {
Tcl_DStringAppend(linePtr, " ", 1);
}
quote = 0;
if (argv[i][0] == '\0') {
quote = 1;
} else {
for (start = argv[i]; *start != '\0'; start++) {
if (isspace(*start)) {
quote = 1;
break;
}
}
}
if (quote) {
Tcl_DStringAppend(linePtr, "\"", 1);
}
start = argv[i];
for (special = argv[i]; ; ) {
if ((*special == '\\') &&
(special[1] == '\\' || special[1] == '"')) {
Tcl_DStringAppend(linePtr, start, special - start);
start = special;
while (1) {
special++;
if (*special == '"') {
Tcl_DStringAppend(linePtr, start, special - start);
break;
}
if (*special != '\\') {
break;
}
}
Tcl_DStringAppend(linePtr, start, special - start);
start = special;
}
if (*special == '"') {
Tcl_DStringAppend(linePtr, start, special - start);
Tcl_DStringAppend(linePtr, "\\\"", 2);
start = special + 1;
}
if (*special == '\0') {
break;
}
special++;
}
Tcl_DStringAppend(linePtr, start, special - start);
if (quote) {
Tcl_DStringAppend(linePtr, "\"", 1);
}
}
}
static char *
MakeTempFile(namePtr)
Tcl_DString *namePtr;
{
char name[MAX_PATH];
if (TempFileName(name) == 0) {
return NULL;
}
Tcl_DStringAppend(namePtr, name, -1);
return Tcl_DStringValue(namePtr);
}
static void
CopyChannel(dst, src)
HANDLE dst;
HANDLE src;
{
char buf[8192];
DWORD dwRead, dwWrite;
while (ReadFile(src, buf, sizeof(buf), &dwRead, NULL) != FALSE) {
if (dwRead == 0) {
break;
}
if (WriteFile(dst, buf, dwRead, &dwWrite, NULL) == FALSE) {
break;
}
}
}
Tcl_Channel
TclpCreateCommandChannel(readFile, writeFile, errorFile, numPids, pidPtr)
TclFile readFile;
TclFile writeFile;
TclFile errorFile;
int numPids;
Tcl_Pid *pidPtr;
{
char channelName[20];
int channelId;
PipeInfo *infoPtr = (PipeInfo *) ckalloc((unsigned) sizeof(PipeInfo));
if (!initialized) {
PipeInit();
}
infoPtr->watchMask = 0;
infoPtr->flags = 0;
infoPtr->readFile = readFile;
infoPtr->writeFile = writeFile;
infoPtr->errorFile = errorFile;
infoPtr->numPids = numPids;
infoPtr->pidPtr = pidPtr;
infoPtr->flagsMutex = CreateMutex(NULL, FALSE, NULL);
if (readFile) {
WinPipe *pipePtr = (WinPipe *) readFile;
if (pipePtr->file.type == WIN32S_PIPE
&& pipePtr->file.handle == INVALID_HANDLE_VALUE) {
pipePtr->file.handle = CreateFile(pipePtr->fileName, GENERIC_READ,
0, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
}
channelId = (int) pipePtr->file.handle;
} else if (writeFile) {
channelId = (int) ((WinFile*)writeFile)->handle;
} else if (errorFile) {
channelId = (int) ((WinFile*)errorFile)->handle;
} else {
channelId = 0;
}
infoPtr->validMask = 0;
if (readFile != NULL) {
infoPtr->validMask |= TCL_READABLE;
}
if (writeFile != NULL) {
infoPtr->validMask |= TCL_WRITABLE;
}
sprintf(channelName, "file%d", channelId);
infoPtr->channel = Tcl_CreateChannel(&pipeChannelType, channelName,
(ClientData) infoPtr, infoPtr->validMask);
Tcl_SetChannelOption((Tcl_Interp *) NULL, infoPtr->channel,
"-translation", "auto");
Tcl_SetChannelOption((Tcl_Interp *) NULL, infoPtr->channel,
"-eofchar", "\032 {}");
return infoPtr->channel;
}
void
TclGetAndDetachPids(interp, chan)
Tcl_Interp *interp;
Tcl_Channel chan;
{
PipeInfo *pipePtr;
Tcl_ChannelType *chanTypePtr;
int i;
char buf[20];
chanTypePtr = Tcl_GetChannelType(chan);
if (chanTypePtr != &pipeChannelType) {
return;
}
pipePtr = (PipeInfo *) Tcl_GetChannelInstanceData(chan);
for (i = 0; i < pipePtr->numPids; i++) {
sprintf(buf, "%lu", TclpGetPid(pipePtr->pidPtr[i]));
Tcl_AppendElement(interp, buf);
Tcl_DetachPids(1, &(pipePtr->pidPtr[i]));
}
if (pipePtr->numPids > 0) {
ckfree((char *) pipePtr->pidPtr);
pipePtr->numPids = 0;
}
}
static int
PipeBlockModeProc(instanceData, mode)
ClientData instanceData;
int mode;
{
PipeInfo *infoPtr = (PipeInfo *) instanceData;
if (mode == TCL_MODE_NONBLOCKING) {
PipeSetFlag(infoPtr, PIPE_ASYNC);
} else {
PipeResetFlag(infoPtr, PIPE_ASYNC);
}
return 0;
}
static int
PipeCloseProc(instanceData, interp)
ClientData instanceData;
Tcl_Interp *interp;
{
PipeInfo *pipePtr = (PipeInfo *) instanceData;
Tcl_Channel errChan;
int errorCode, result;
PipeInfo *infoPtr, **nextPtrPtr;
for (nextPtrPtr = &firstPipePtr, infoPtr = *nextPtrPtr; infoPtr != NULL;
nextPtrPtr = &infoPtr->nextPtr, infoPtr = *nextPtrPtr) {
if (infoPtr == (PipeInfo *)pipePtr) {
*nextPtrPtr = infoPtr->nextPtr;
break;
}
}
errorCode = 0;
if (pipePtr->readFile != NULL) {
if (TclpCloseFile(pipePtr->readFile) != 0) {
errorCode = errno;
}
}
if (pipePtr->writeFile != NULL) {
if (TclpCloseFile(pipePtr->writeFile) != 0) {
if (errorCode == 0) {
errorCode = errno;
}
}
}
if (pipePtr->errorFile) {
WinFile *filePtr;
OSVERSIONINFO os;
os.dwOSVersionInfoSize = sizeof(os);
GetVersionEx(&os);
if (os.dwPlatformId == VER_PLATFORM_WIN32s) {
TclpCloseFile(pipePtr->errorFile);
errChan = NULL;
} else {
filePtr = (WinFile*)pipePtr->errorFile;
errChan = Tcl_MakeFileChannel((ClientData) filePtr->handle,
TCL_READABLE);
}
} else {
errChan = NULL;
}
result = TclCleanupChildren(interp, pipePtr->numPids, pipePtr->pidPtr,
errChan);
if (pipePtr->numPids > 0) {
ckfree((char *) pipePtr->pidPtr);
}
if (PipeGetFlags(pipePtr) & PIPE_HAS_THREAD) {
WaitForSingleObject(pipePtr->flagsMutex, INFINITE);
pipePtr->flags |= PIPE_CLOSED;
SetEvent(pipePtr->tryReadEvent);
ReleaseMutex(pipePtr->flagsMutex);
} else {
CloseHandle(pipePtr->flagsMutex);
ckfree((char*) pipePtr);
}
if (errorCode == 0) {
return result;
}
return errorCode;
}
static int
PipeInputProc(instanceData, buf, bufSize, errorCode)
ClientData instanceData;
char *buf;
int bufSize;
int *errorCode;
{
PipeInfo *infoPtr = (PipeInfo *) instanceData;
WinFile *filePtr = (WinFile*) infoPtr->readFile;
DWORD count;
DWORD bytesRead;
int gotReadAhead = 0;
int origBufSize = bufSize;
if (PipeGetFlags(infoPtr) & PIPE_HAS_THREAD) {
WaitForSingleObject(infoPtr->mutex, INFINITE);
}
*errorCode = 0;
if (filePtr->type == WIN32S_PIPE) {
if (((WinPipe *)filePtr)->otherPtr != NULL) {
panic("PipeInputProc: child process isn't finished writing");
}
if (filePtr->handle == INVALID_HANDLE_VALUE) {
filePtr->handle = CreateFile(((WinPipe *)filePtr)->fileName,
GENERIC_READ, 0, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL,
NULL);
}
if (filePtr->handle == INVALID_HANDLE_VALUE) {
goto error;
}
} else {
if (PeekNamedPipe(filePtr->handle, (LPVOID) NULL, (DWORD) 0,
(LPDWORD) NULL, &count, (LPDWORD) NULL) == TRUE) {
if ((count != 0) && ((DWORD) bufSize > count)) {
bufSize = (int) count;
} else if ((count == 0) && !(PipeGetFlags(infoPtr) & PIPE_ASYNC)) {
bufSize = 1;
}
} else {
goto error;
}
}
if (PipeGetFlags(infoPtr) & PIPE_READAHEAD) {
*buf++ = infoPtr->readAhead;
PipeResetFlag(infoPtr, PIPE_READAHEAD);
if (bufSize <= 1) {
PipeResetFlag(infoPtr, PIPE_READABLE);
ReleaseMutex(infoPtr->mutex);
return 1;
}
gotReadAhead = 1;
if (bufSize == origBufSize) {
--bufSize;
}
}
if (ReadFile(filePtr->handle, (LPVOID) buf, (DWORD) bufSize, &bytesRead,
(LPOVERLAPPED) NULL) == FALSE) {
goto error;
}
if (PipeGetFlags(infoPtr) & PIPE_HAS_THREAD) {
PipeResetFlag(infoPtr, PIPE_READABLE);
ReleaseMutex(infoPtr->mutex);
}
return bytesRead + gotReadAhead;
error:
TclWinConvertError(GetLastError());
if (PipeGetFlags(infoPtr) & PIPE_HAS_THREAD) {
ReleaseMutex(infoPtr->mutex);
}
if (errno == EPIPE) {
return 0;
}
*errorCode = errno;
return -1;
}
static int
PipeOutputProc(instanceData, buf, toWrite, errorCode)
ClientData instanceData;
char *buf;
int toWrite;
int *errorCode;
{
PipeInfo *infoPtr = (PipeInfo *) instanceData;
WinFile *filePtr = (WinFile*) infoPtr->writeFile;
DWORD bytesWritten;
*errorCode = 0;
if (WriteFile(filePtr->handle, (LPVOID) buf, (DWORD) toWrite,
&bytesWritten, (LPOVERLAPPED) NULL) == FALSE) {
TclWinConvertError(GetLastError());
if (errno == EPIPE) {
return 0;
}
*errorCode = errno;
return -1;
}
return bytesWritten;
}
static int
PipeEventProc(evPtr, flags)
Tcl_Event *evPtr;
int flags;
{
PipeEvent *pipeEvPtr = (PipeEvent *)evPtr;
PipeInfo *infoPtr;
WinFile *filePtr;
int mask;
if (!(flags & TCL_FILE_EVENTS)) {
return 0;
}
for (infoPtr = firstPipePtr; infoPtr != NULL; infoPtr = infoPtr->nextPtr) {
if (pipeEvPtr->infoPtr == infoPtr) {
PipeResetFlag(infoPtr, PIPE_PENDING);
break;
}
}
if (!infoPtr) {
return 1;
}
filePtr = (WinFile*) ((PipeInfo*)infoPtr)->readFile;
if (filePtr->type != WIN32S_PIPE) {
if (PipeGetFlags(infoPtr) & PIPE_HAS_THREAD) {
mask = TCL_WRITABLE;
if (PipeGetFlags(infoPtr) & PIPE_READABLE) {
mask |= TCL_READABLE;
}
} else {
mask = TCL_WRITABLE|TCL_READABLE;
}
} else {
mask = TCL_READABLE | TCL_WRITABLE;
}
Tcl_NotifyChannel(infoPtr->channel, infoPtr->watchMask & mask);
return 1;
}
static void
PipeWatchProc(instanceData, mask)
ClientData instanceData;
int mask;
{
PipeInfo **nextPtrPtr, *ptr;
PipeInfo *infoPtr = (PipeInfo *) instanceData;
int oldMask = infoPtr->watchMask;
infoPtr->watchMask = mask & infoPtr->validMask;
if (infoPtr->watchMask) {
Tcl_Time blockTime = { 0, 0 };
if ((infoPtr->watchMask & TCL_READABLE) != 0
&& (PipeGetFlags(infoPtr) & PIPE_HAS_THREAD) == 0) {
HANDLE thread;
DWORD tid;
infoPtr->tryReadEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
infoPtr->mutex = CreateMutex(NULL, FALSE, NULL);
PipeSetFlag(infoPtr, PIPE_HAS_THREAD);
thread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) PipeThread, infoPtr, 0, &tid);
CloseHandle(thread);
}
if (!oldMask) {
infoPtr->nextPtr = firstPipePtr;
firstPipePtr = infoPtr;
}
Tcl_SetMaxBlockTime(&blockTime);
} else {
if (oldMask) {
for (nextPtrPtr = &firstPipePtr, ptr = *nextPtrPtr;
ptr != NULL;
nextPtrPtr = &ptr->nextPtr, ptr = *nextPtrPtr) {
if (infoPtr == ptr) {
*nextPtrPtr = ptr->nextPtr;
break;
}
}
}
}
}
static int
PipeGetHandleProc(instanceData, direction, handlePtr)
ClientData instanceData;
int direction;
ClientData *handlePtr;
{
PipeInfo *infoPtr = (PipeInfo *) instanceData;
WinFile *filePtr;
if (direction == TCL_READABLE && infoPtr->readFile) {
filePtr = (WinFile*) infoPtr->readFile;
if (filePtr->type == WIN32S_PIPE) {
if (filePtr->handle == INVALID_HANDLE_VALUE) {
filePtr->handle = CreateFile(((WinPipe *)filePtr)->fileName,
GENERIC_READ, 0, NULL, OPEN_ALWAYS,
FILE_ATTRIBUTE_NORMAL, NULL);
}
if (filePtr->handle == INVALID_HANDLE_VALUE) {
return TCL_ERROR;
}
}
*handlePtr = (ClientData) filePtr->handle;
return TCL_OK;
}
if (direction == TCL_WRITABLE && infoPtr->writeFile) {
filePtr = (WinFile*) infoPtr->writeFile;
*handlePtr = (ClientData) filePtr->handle;
return TCL_OK;
}
return TCL_ERROR;
}
Tcl_Pid
Tcl_WaitPid(pid, statPtr, options)
Tcl_Pid pid;
int *statPtr;
int options;
{
ProcInfo *infoPtr, **prevPtrPtr;
int flags;
Tcl_Pid result;
DWORD ret;
if (!initialized) {
PipeInit();
}
if (pid == 0) {
*statPtr = 0;
return 0;
}
prevPtrPtr = &procList;
for (infoPtr = procList; infoPtr != NULL;
prevPtrPtr = &infoPtr->nextPtr, infoPtr = infoPtr->nextPtr) {
if (infoPtr->hProcess == (HANDLE) pid) {
break;
}
}
if (infoPtr == NULL) {
*statPtr = 0;
return 0;
}
if (options & WNOHANG) {
flags = 0;
} else {
flags = INFINITE;
}
ret = WaitForSingleObject(infoPtr->hProcess, flags);
if (ret == WAIT_TIMEOUT) {
*statPtr = 0;
if (options & WNOHANG) {
return 0;
} else {
result = 0;
}
} else if (ret != WAIT_FAILED) {
GetExitCodeProcess(infoPtr->hProcess, (DWORD*)statPtr);
#ifdef __CYGWIN32__
if ((*statPtr & 0x10000) != 0
&& (*statPtr & 0xff00) != 0
&& (*statPtr & ~ 0x1ff00) == 0) {
*statPtr = (*statPtr >> 8) & 0xff;
} else
#endif
*statPtr = ((*statPtr << 8) & 0xff00);
result = pid;
} else {
errno = ECHILD;
*statPtr = ECHILD;
result = (Tcl_Pid) -1;
}
CloseHandle(infoPtr->hProcess);
*prevPtrPtr = infoPtr->nextPtr;
ckfree((char*)infoPtr);
return result;
}
int
Tcl_PidObjCmd(dummy, interp, objc, objv)
ClientData dummy;
Tcl_Interp *interp;
int objc;
Tcl_Obj *CONST *objv;
{
Tcl_Channel chan;
Tcl_ChannelType *chanTypePtr;
PipeInfo *pipePtr;
int i;
Tcl_Obj *resultPtr;
char buf[20];
if (objc > 2) {
Tcl_WrongNumArgs(interp, 1, objv, "?channelId?");
return TCL_ERROR;
}
if (objc == 1) {
resultPtr = Tcl_GetObjResult(interp);
sprintf(buf, "%lu", (unsigned long) getpid());
Tcl_SetStringObj(resultPtr, buf, -1);
} else {
chan = Tcl_GetChannel(interp, Tcl_GetStringFromObj(objv[1], NULL),
NULL);
if (chan == (Tcl_Channel) NULL) {
return TCL_ERROR;
}
chanTypePtr = Tcl_GetChannelType(chan);
if (chanTypePtr != &pipeChannelType) {
return TCL_OK;
}
pipePtr = (PipeInfo *) Tcl_GetChannelInstanceData(chan);
resultPtr = Tcl_GetObjResult(interp);
for (i = 0; i < pipePtr->numPids; i++) {
sprintf(buf, "%lu", TclpGetPid(pipePtr->pidPtr[i]));
Tcl_ListObjAppendElement( NULL, resultPtr,
Tcl_NewStringObj(buf, -1));
}
}
return TCL_OK;
}