summaryrefslogtreecommitdiffstats
path: root/win/tclWinPipe.c
diff options
context:
space:
mode:
Diffstat (limited to 'win/tclWinPipe.c')
-rw-r--r--win/tclWinPipe.c1607
1 files changed, 486 insertions, 1121 deletions
diff --git a/win/tclWinPipe.c b/win/tclWinPipe.c
index bb4983e..ee088a5 100644
--- a/win/tclWinPipe.c
+++ b/win/tclWinPipe.c
@@ -4,7 +4,7 @@
* This file implements the Windows-specific exec pipeline functions, the
* "pipe" channel driver, and the "pid" Tcl command.
*
- * Copyright © 1996-1997 Sun Microsystems, Inc.
+ * Copyright (c) 1996-1997 by Sun Microsystems, Inc.
*
* See the file "license.terms" for information on usage and redistribution of
* this file, and for a DISCLAIMER OF ALL WARRANTIES.
@@ -50,7 +50,7 @@ TCL_DECLARE_MUTEX(pipeMutex)
* used in a pipeline.
*/
-typedef struct {
+typedef struct WinFile {
int type; /* One of the file types defined above. */
HANDLE handle; /* Open file handle. */
} WinFile;
@@ -61,7 +61,7 @@ typedef struct {
typedef struct ProcInfo {
HANDLE hProcess;
- TCL_HASH_TYPE dwProcessId;
+ DWORD dwProcessId;
struct ProcInfo *nextPtr;
} ProcInfo;
@@ -82,12 +82,6 @@ static ProcInfo *procList;
#define PIPE_EXTRABYTE (1<<3) /* The reader thread has consumed one byte. */
/*
- * TODO: It appears the whole EXTRABYTE machinery is in place to support
- * outdated Win 95 systems. If this can be confirmed, much code can be
- * deleted.
- */
-
-/*
* This structure describes per-instance data for a pipe based channel.
*/
@@ -104,27 +98,35 @@ typedef struct PipeInfo {
TclFile readFile; /* Output from pipe. */
TclFile writeFile; /* Input from pipe. */
TclFile errorFile; /* Error output from pipe. */
- TCL_HASH_TYPE numPids; /* Number of processes attached to pipe. */
+ int numPids; /* Number of processes attached to pipe. */
Tcl_Pid *pidPtr; /* Pids of attached processes. */
Tcl_ThreadId threadId; /* Thread to which events should be reported.
* This value is used by the reader/writer
* threads. */
- TclPipeThreadInfo *writeTI; /* Thread info of writer and reader, this */
- TclPipeThreadInfo *readTI; /* structure owned by corresponding thread. */
HANDLE writeThread; /* Handle to writer thread. */
HANDLE readThread; /* Handle to reader thread. */
-
HANDLE writable; /* Manual-reset event to signal when the
* writer thread has finished waiting for the
* current buffer to be written. */
HANDLE readable; /* Manual-reset event to signal when the
* reader thread has finished waiting for
* input. */
+ HANDLE startWriter; /* Auto-reset event used by the main thread to
+ * signal when the writer thread should
+ * attempt to write to the pipe. */
+ HANDLE stopWriter; /* Manual-reset event used to alert the reader
+ * thread to fall-out and exit */
+ HANDLE startReader; /* Auto-reset event used by the main thread to
+ * signal when the reader thread should
+ * attempt to read from the pipe. */
+ HANDLE stopReader; /* Manual-reset event used to alert the reader
+ * thread to fall-out and exit */
DWORD writeError; /* An error caused by the last background
* write. Set to 0 if no error has been
* detected. This word is shared with the
* writer thread so access must be
- * synchronized with the writable object. */
+ * synchronized with the writable object.
+ */
char *writeBuf; /* Current background output buffer. Access is
* synchronized with the writable object. */
int writeBufLen; /* Size of write buffer. Access is
@@ -140,7 +142,7 @@ typedef struct PipeInfo {
* synchronized with the readable object. */
} PipeInfo;
-typedef struct {
+typedef struct ThreadSpecificData {
/*
* The following pointer refers to the head of the list of pipes that are
* being watched for file events.
@@ -156,7 +158,7 @@ static Tcl_ThreadDataKey dataKey;
* events are generated.
*/
-typedef struct {
+typedef struct PipeEvent {
Tcl_Event header; /* Information that is standard for all
* events. */
PipeInfo *infoPtr; /* Pointer to pipe info structure. Note that
@@ -171,28 +173,28 @@ typedef struct {
static int ApplicationType(Tcl_Interp *interp,
const char *fileName, char *fullName);
-static void BuildCommandLine(const char *executable, Tcl_Size argc,
+static void BuildCommandLine(const char *executable, int argc,
const char **argv, Tcl_DString *linePtr);
static BOOL HasConsole(void);
-static int PipeBlockModeProc(void *instanceData, int mode);
-static void PipeCheckProc(void *clientData, int flags);
-static int PipeClose2Proc(void *instanceData,
+static int PipeBlockModeProc(ClientData instanceData, int mode);
+static void PipeCheckProc(ClientData clientData, int flags);
+static int PipeClose2Proc(ClientData instanceData,
Tcl_Interp *interp, int flags);
static int PipeEventProc(Tcl_Event *evPtr, int flags);
-static int PipeGetHandleProc(void *instanceData,
- int direction, void **handlePtr);
+static int PipeGetHandleProc(ClientData instanceData,
+ int direction, ClientData *handlePtr);
static void PipeInit(void);
-static int PipeInputProc(void *instanceData, char *buf,
+static int PipeInputProc(ClientData instanceData, char *buf,
int toRead, int *errorCode);
-static int PipeOutputProc(void *instanceData,
+static int PipeOutputProc(ClientData instanceData,
const char *buf, int toWrite, int *errorCode);
static DWORD WINAPI PipeReaderThread(LPVOID arg);
-static void PipeSetupProc(void *clientData, int flags);
-static void PipeWatchProc(void *instanceData, int mask);
+static void PipeSetupProc(ClientData clientData, int flags);
+static void PipeWatchProc(ClientData instanceData, int mask);
static DWORD WINAPI PipeWriterThread(LPVOID arg);
static int TempFileName(WCHAR name[MAX_PATH]);
static int WaitForRead(PipeInfo *infoPtr, int blocking);
-static void PipeThreadActionProc(void *instanceData,
+static void PipeThreadActionProc(ClientData instanceData,
int action);
/*
@@ -200,7 +202,7 @@ static void PipeThreadActionProc(void *instanceData,
* I/O.
*/
-static const Tcl_ChannelType pipeChannelType = {
+static Tcl_ChannelType pipeChannelType = {
"pipe", /* Type name. */
TCL_CHANNEL_VERSION_5, /* v5 channel */
TCL_CLOSE2PROC, /* Close proc. */
@@ -217,7 +219,7 @@ static const Tcl_ChannelType pipeChannelType = {
NULL, /* handler proc. */
NULL, /* wide seek proc */
PipeThreadActionProc, /* thread action proc */
- NULL /* truncate */
+ NULL, /* truncate */
};
/*
@@ -310,7 +312,7 @@ TclpFinalizePipes(void)
void
PipeSetupProc(
- TCL_UNUSED(void *),
+ ClientData data, /* Not used. */
int flags) /* Event flags as passed to Tcl_DoOneEvent. */
{
PipeInfo *infoPtr;
@@ -363,7 +365,7 @@ PipeSetupProc(
static void
PipeCheckProc(
- TCL_UNUSED(void *),
+ ClientData data, /* Not used. */
int flags) /* Event flags as passed to Tcl_DoOneEvent. */
{
PipeInfo *infoPtr;
@@ -402,7 +404,7 @@ PipeCheckProc(
if (needEvent) {
infoPtr->flags |= PIPE_PENDING;
- evPtr = (PipeEvent *)ckalloc(sizeof(PipeEvent));
+ evPtr = (PipeEvent *) ckalloc(sizeof(PipeEvent));
evPtr->header.proc = PipeEventProc;
evPtr->infoPtr = infoPtr;
Tcl_QueueEvent((Tcl_Event *) evPtr, TCL_QUEUE_TAIL);
@@ -433,7 +435,7 @@ TclWinMakeFile(
{
WinFile *filePtr;
- filePtr = (WinFile *)ckalloc(sizeof(WinFile));
+ filePtr = (WinFile *) ckalloc(sizeof(WinFile));
filePtr->type = WIN_FILE;
filePtr->handle = handle;
@@ -465,15 +467,24 @@ TempFileName(
WCHAR name[MAX_PATH]) /* Buffer in which name for temporary file
* gets stored. */
{
- const WCHAR *prefix = L"TCL";
- if (GetTempPathW(MAX_PATH, name) != 0) {
- if (GetTempFileNameW(name, prefix, 0, name) != 0) {
+ TCHAR *prefix;
+
+ prefix = (tclWinProcs->useWide) ? (TCHAR *) L"TCL" : (TCHAR *) "TCL";
+ if ((*tclWinProcs->getTempPathProc)(MAX_PATH, name) != 0) {
+ if ((*tclWinProcs->getTempFileNameProc)((TCHAR *) name, prefix, 0,
+ name) != 0) {
return 1;
}
}
- name[0] = '.';
- name[1] = '\0';
- return GetTempFileNameW(name, prefix, 0, name);
+ if (tclWinProcs->useWide) {
+ ((WCHAR *) name)[0] = '.';
+ ((WCHAR *) name)[1] = '\0';
+ } else {
+ ((char *) name)[0] = '.';
+ ((char *) name)[1] = '\0';
+ }
+ return (*tclWinProcs->getTempFileNameProc)((TCHAR *) name, prefix, 0,
+ name);
}
/*
@@ -500,7 +511,7 @@ TclpMakeFile(
HANDLE handle;
if (Tcl_GetChannelHandle(channel, direction,
- (void **) &handle) == TCL_OK) {
+ (ClientData *) &handle) == TCL_OK) {
return TclWinMakeFile(handle);
} else {
return (TclFile) NULL;
@@ -532,7 +543,7 @@ TclpOpenFile(
HANDLE handle;
DWORD accessMode, createMode, shareMode, flags;
Tcl_DString ds;
- const WCHAR *nativePath;
+ const TCHAR *nativePath;
/*
* Map the access bits to the NT access mode.
@@ -549,7 +560,7 @@ TclpOpenFile(
accessMode = (GENERIC_READ | GENERIC_WRITE);
break;
default:
- Tcl_WinConvertError(ERROR_INVALID_FUNCTION);
+ TclWinConvertError(ERROR_INVALID_FUNCTION);
return NULL;
}
@@ -577,8 +588,7 @@ TclpOpenFile(
break;
}
- Tcl_DStringInit(&ds);
- nativePath = Tcl_UtfToWCharDString(path, TCL_INDEX_NONE, &ds);
+ nativePath = Tcl_WinUtfToTChar(path, -1, &ds);
/*
* If the file is not being created, use the existing file attributes.
@@ -586,7 +596,7 @@ TclpOpenFile(
flags = 0;
if (!(mode & O_CREAT)) {
- flags = GetFileAttributesW(nativePath);
+ flags = (*tclWinProcs->getFileAttributesProc)(nativePath);
if (flags == 0xFFFFFFFF) {
flags = 0;
}
@@ -602,18 +612,18 @@ TclpOpenFile(
* Now we get to create the file.
*/
- handle = CreateFileW(nativePath, accessMode, shareMode,
- NULL, createMode, flags, NULL);
+ handle = (*tclWinProcs->createFileProc)(nativePath, accessMode,
+ shareMode, NULL, createMode, flags, NULL);
Tcl_DStringFree(&ds);
if (handle == INVALID_HANDLE_VALUE) {
DWORD err;
err = GetLastError();
- if ((err & 0xFFFFL) == ERROR_OPEN_FAILED) {
+ if ((err & 0xffffL) == ERROR_OPEN_FAILED) {
err = (mode & O_CREAT) ? ERROR_FILE_EXISTS : ERROR_FILE_NOT_FOUND;
}
- Tcl_WinConvertError(err);
+ TclWinConvertError(err);
return NULL;
}
@@ -659,7 +669,7 @@ TclpCreateTempFile(
return NULL;
}
- handle = CreateFileW(name,
+ handle = (*tclWinProcs->createFileProc)((TCHAR *) name,
GENERIC_READ | GENERIC_WRITE, 0, NULL, CREATE_ALWAYS,
FILE_ATTRIBUTE_TEMPORARY|FILE_FLAG_DELETE_ON_CLOSE, NULL);
if (handle == INVALID_HANDLE_VALUE) {
@@ -679,7 +689,7 @@ TclpCreateTempFile(
* Convert the contents from UTF to native encoding
*/
- native = Tcl_UtfToExternalDString(NULL, contents, TCL_INDEX_NONE, &dstring);
+ native = Tcl_UtfToExternalDString(NULL, contents, -1, &dstring);
toCopy = Tcl_DStringLength(&dstring);
for (p = native; toCopy > 0; p++, toCopy--) {
@@ -719,9 +729,9 @@ TclpCreateTempFile(
Tcl_DStringFree(&dstring);
}
- Tcl_WinConvertError(GetLastError());
+ TclWinConvertError(GetLastError());
CloseHandle(handle);
- DeleteFileW(name);
+ (*tclWinProcs->deleteFileProc)((TCHAR *) name);
return NULL;
}
@@ -750,7 +760,7 @@ TclpTempFileName(void)
return NULL;
}
- return TclpNativeToNormalized(fileName);
+ return TclpNativeToNormalized((ClientData) fileName);
}
/*
@@ -784,7 +794,7 @@ TclpCreatePipe(
return 1;
}
- Tcl_WinConvertError(GetLastError());
+ TclWinConvertError(GetLastError());
return 0;
}
@@ -825,8 +835,8 @@ TclpCloseFile(
&& (GetStdHandle(STD_ERROR_HANDLE) != filePtr->handle))) {
if (filePtr->handle != NULL &&
CloseHandle(filePtr->handle) == FALSE) {
- Tcl_WinConvertError(GetLastError());
- ckfree(filePtr);
+ TclWinConvertError(GetLastError());
+ ckfree((char *) filePtr);
return -1;
}
}
@@ -836,7 +846,7 @@ TclpCloseFile(
Tcl_Panic("TclpCloseFile: unexpected file type");
}
- ckfree(filePtr);
+ ckfree((char *) filePtr);
return 0;
}
@@ -851,7 +861,7 @@ TclpCloseFile(
* Results:
* Returns the process id for the child process. If the pid was not known
* by Tcl, either because the pid was not created by Tcl or the child
- * process has already been reaped, TCL_INDEX_NONE is returned.
+ * process has already been reaped, -1 is returned.
*
* Side effects:
* None.
@@ -859,7 +869,7 @@ TclpCloseFile(
*--------------------------------------------------------------------------
*/
-Tcl_Size
+int
TclpGetPid(
Tcl_Pid pid) /* The HANDLE of the child process. */
{
@@ -869,13 +879,13 @@ TclpGetPid(
Tcl_MutexLock(&pipeMutex);
for (infoPtr = procList; infoPtr != NULL; infoPtr = infoPtr->nextPtr) {
- if (infoPtr->dwProcessId == PTR2UINT(pid)) {
+ if (infoPtr->hProcess == (HANDLE) pid) {
Tcl_MutexUnlock(&pipeMutex);
return infoPtr->dwProcessId;
}
}
Tcl_MutexUnlock(&pipeMutex);
- return TCL_INDEX_NONE;
+ return (unsigned long) -1;
}
/*
@@ -890,7 +900,7 @@ TclpGetPid(
*
* The complete Windows search path is searched to find the specified
* executable. If an executable by the given name is not found,
- * automatically tries appending standard extensions to the
+ * automatically tries appending ".com", ".exe", and ".bat" to the
* executable name.
*
* Results:
@@ -911,7 +921,7 @@ TclpCreateProcess(
* occurred when creating the child process.
* Error messages from the child process
* itself are sent to errorFile. */
- Tcl_Size argc, /* Number of arguments in following array. */
+ int argc, /* Number of arguments in following array. */
const char **argv, /* Array of argument strings. argv[0] contains
* the name of the executable converted to
* native format (using the
@@ -923,12 +933,12 @@ TclpCreateProcess(
* receive no standard input. */
TclFile outputFile, /* If non-NULL, gives the file that receives
* output from the child process. If
- * outputFile file is not writable or is
+ * outputFile file is not writeable or is
* NULL, output from the child will be
* discarded. */
TclFile errorFile, /* If non-NULL, gives the file that receives
* errors from the child process. If errorFile
- * file is not writable or is NULL, errors
+ * file is not writeable or is NULL, errors
* from the child will be discarded. errorFile
* may be the same as outputFile. */
Tcl_Pid *pidPtr) /* If this function is successful, pidPtr is
@@ -936,12 +946,12 @@ TclpCreateProcess(
* process. */
{
int result, applType, createFlags;
- Tcl_DString cmdLine; /* Complete command line (WCHAR). */
- STARTUPINFOW startInfo;
+ Tcl_DString cmdLine; /* Complete command line (TCHAR). */
+ STARTUPINFOA startInfo;
PROCESS_INFORMATION procInfo;
SECURITY_ATTRIBUTES secAtts;
HANDLE hProcess, h, inputHandle, outputHandle, errorHandle;
- char execPath[MAX_PATH * 3];
+ char execPath[MAX_PATH * TCL_UTF_MAX];
WinFile *filePtr;
PipeInit();
@@ -1026,10 +1036,9 @@ TclpCreateProcess(
0, TRUE, DUPLICATE_SAME_ACCESS);
}
if (startInfo.hStdInput == INVALID_HANDLE_VALUE) {
- Tcl_WinConvertError(GetLastError());
- Tcl_SetObjResult(interp, Tcl_ObjPrintf(
- "couldn't duplicate input handle: %s",
- Tcl_PosixError(interp)));
+ TclWinConvertError(GetLastError());
+ Tcl_AppendResult(interp, "couldn't duplicate input handle: ",
+ Tcl_PosixError(interp), (char *) NULL);
goto end;
}
@@ -1048,17 +1057,23 @@ TclpCreateProcess(
* sink.
*/
- startInfo.hStdOutput = CreateFileW(L"NUL:", GENERIC_WRITE, 0,
- &secAtts, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
+ if ((TclWinGetPlatformId() == VER_PLATFORM_WIN32_WINDOWS)
+ && (applType == APPL_DOS)) {
+ if (CreatePipe(&h, &startInfo.hStdOutput, &secAtts, 0) != FALSE) {
+ CloseHandle(h);
+ }
+ } else {
+ startInfo.hStdOutput = CreateFileA("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) {
- Tcl_WinConvertError(GetLastError());
- Tcl_SetObjResult(interp, Tcl_ObjPrintf(
- "couldn't duplicate output handle: %s",
- Tcl_PosixError(interp)));
+ TclWinConvertError(GetLastError());
+ Tcl_AppendResult(interp, "couldn't duplicate output handle: ",
+ Tcl_PosixError(interp), (char *) NULL);
goto end;
}
@@ -1068,17 +1083,16 @@ TclpCreateProcess(
* sink.
*/
- startInfo.hStdError = CreateFileW(L"NUL:", GENERIC_WRITE, 0,
+ startInfo.hStdError = CreateFileA("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) {
- Tcl_WinConvertError(GetLastError());
- Tcl_SetObjResult(interp, Tcl_ObjPrintf(
- "couldn't duplicate error handle: %s",
- Tcl_PosixError(interp)));
+ TclWinConvertError(GetLastError());
+ Tcl_AppendResult(interp, "couldn't duplicate error handle: ",
+ Tcl_PosixError(interp), (char *) NULL);
goto end;
}
@@ -1095,23 +1109,110 @@ TclpCreateProcess(
* detached processes. The GUI window will still pop up to the foreground.
*/
- if (HasConsole()) {
- createFlags = 0;
- } else if (applType == APPL_DOS) {
- /*
- * Under NT, 16-bit DOS applications will not run unless they can
- * be attached to a console. If we are running without a console,
- * run the 16-bit program as an normal process inside of a hidden
- * console application, and then run that hidden console as a
- * detached process.
- */
+ if (TclWinGetPlatformId() == VER_PLATFORM_WIN32_NT) {
+ if (HasConsole()) {
+ createFlags = 0;
+ } else if (applType == APPL_DOS) {
+ /*
+ * Under NT, 16-bit DOS applications will not run unless they can
+ * be attached to a console. If we are running without a console,
+ * run the 16-bit program as an normal process inside of a hidden
+ * console application, and then run that hidden console as a
+ * detached process.
+ */
- startInfo.wShowWindow = SW_HIDE;
- startInfo.dwFlags |= STARTF_USESHOWWINDOW;
- createFlags = CREATE_NEW_CONSOLE;
- TclDStringAppendLiteral(&cmdLine, "cmd.exe /c");
+ startInfo.wShowWindow = SW_HIDE;
+ startInfo.dwFlags |= STARTF_USESHOWWINDOW;
+ createFlags = CREATE_NEW_CONSOLE;
+ Tcl_DStringAppend(&cmdLine, "cmd.exe /c", -1);
+ } else {
+ createFlags = DETACHED_PROCESS;
+ }
} else {
- createFlags = DETACHED_PROCESS;
+ if (HasConsole()) {
+ createFlags = 0;
+ } else {
+ createFlags = DETACHED_PROCESS;
+ }
+
+ if (applType == APPL_DOS) {
+ /*
+ * Under Windows 95, 16-bit DOS applications do not work well with
+ * pipes:
+ *
+ * 1. EOF on a pipe between a detached 16-bit DOS application and
+ * another application is not seen at the other end of the pipe,
+ * so the listening process blocks forever on reads. This inablity
+ * to detect EOF happens when either a 16-bit app or the 32-bit
+ * app is the listener.
+ *
+ * 2. If a 16-bit DOS application (detached or not) blocks when
+ * writing to a pipe, it will never wake up again, and it
+ * eventually brings the whole system down around it.
+ *
+ * The 16-bit application is run as a normal process inside of a
+ * hidden helper console app, and this helper may be run as a
+ * detached process. If any of the stdio handles is a pipe, the
+ * helper application accumulates information into temp files and
+ * forwards it to or from the DOS application as appropriate.
+ * This means that DOS apps must receive EOF from a stdin pipe
+ * before they will actually begin, and must finish generating
+ * stdout or stderr before the data will be sent to the next stage
+ * of the pipe.
+ *
+ * The helper app should be located in the same directory as the
+ * tcl dll.
+ */
+ Tcl_Obj *tclExePtr, *pipeDllPtr;
+ char *start, *end;
+ int i, fileExists;
+ Tcl_DString pipeDll;
+
+ if (createFlags != 0) {
+ startInfo.wShowWindow = SW_HIDE;
+ startInfo.dwFlags |= STARTF_USESHOWWINDOW;
+ createFlags = CREATE_NEW_CONSOLE;
+ }
+
+ Tcl_DStringInit(&pipeDll);
+ Tcl_DStringAppend(&pipeDll, TCL_PIPE_DLL, -1);
+ tclExePtr = TclGetObjNameOfExecutable();
+ Tcl_IncrRefCount(tclExePtr);
+ start = Tcl_GetStringFromObj(tclExePtr, &i);
+ for (end = start + (i-1); end > start; end--) {
+ if (*end == '/') {
+ break;
+ }
+ }
+ if (*end != '/') {
+ Tcl_AppendResult(interp, "no / in executable path name \"",
+ start, "\"", (char *) NULL);
+ Tcl_DecrRefCount(tclExePtr);
+ Tcl_DStringFree(&pipeDll);
+ goto end;
+ }
+ i = (end - start) + 1;
+ pipeDllPtr = Tcl_NewStringObj(start, i);
+ Tcl_AppendToObj(pipeDllPtr, Tcl_DStringValue(&pipeDll), -1);
+ Tcl_IncrRefCount(pipeDllPtr);
+ if (Tcl_FSConvertToPathType(interp, pipeDllPtr) != TCL_OK) {
+ Tcl_Panic("Tcl_FSConvertToPathType failed");
+ }
+ fileExists = (Tcl_FSAccess(pipeDllPtr, F_OK) == 0);
+ if (!fileExists) {
+ Tcl_AppendResult(interp, "Tcl pipe dll \"",
+ Tcl_DStringValue(&pipeDll), "\" not found",
+ (char *) NULL);
+ Tcl_DecrRefCount(tclExePtr);
+ Tcl_DecrRefCount(pipeDllPtr);
+ Tcl_DStringFree(&pipeDll);
+ goto end;
+ }
+ Tcl_DStringAppend(&cmdLine, Tcl_DStringValue(&pipeDll), -1);
+ Tcl_DecrRefCount(tclExePtr);
+ Tcl_DecrRefCount(pipeDllPtr);
+ Tcl_DStringFree(&pipeDll);
+ }
}
/*
@@ -1134,12 +1235,12 @@ TclpCreateProcess(
BuildCommandLine(execPath, argc, argv, &cmdLine);
- if (CreateProcessW(NULL, (WCHAR *) Tcl_DStringValue(&cmdLine),
- NULL, NULL, TRUE, (DWORD) createFlags, NULL, NULL, &startInfo,
- &procInfo) == 0) {
- Tcl_WinConvertError(GetLastError());
- Tcl_SetObjResult(interp, Tcl_ObjPrintf("couldn't execute \"%s\": %s",
- argv[0], Tcl_PosixError(interp)));
+ if ((*tclWinProcs->createProcessProc)(NULL,
+ (TCHAR *) Tcl_DStringValue(&cmdLine), NULL, NULL, TRUE,
+ (DWORD) createFlags, NULL, NULL, &startInfo, &procInfo) == 0) {
+ TclWinConvertError(GetLastError());
+ Tcl_AppendResult(interp, "couldn't execute \"", argv[0],
+ "\": ", Tcl_PosixError(interp), (char *) NULL);
goto end;
}
@@ -1156,14 +1257,14 @@ TclpCreateProcess(
* will be created for each process but the previous instances may not be
* cleaned up. This results in a significant virtual memory loss each time
* the process is spawned. If there is a WaitForInputIdle() call between
- * CreateProcessW() and CloseHandle(), the problem does not occur." PSS ID
+ * CreateProcess() and CloseHandle(), the problem does not occur." PSS ID
* Number: Q124121
*/
WaitForInputIdle(procInfo.hProcess, 5000);
CloseHandle(procInfo.hThread);
- *pidPtr = (Tcl_Pid)INT2PTR(procInfo.dwProcessId);
+ *pidPtr = (Tcl_Pid) procInfo.hProcess;
if (*pidPtr != 0) {
TclWinAddProcess(procInfo.hProcess, procInfo.dwProcessId);
}
@@ -1205,7 +1306,7 @@ HasConsole(void)
{
HANDLE handle;
- handle = CreateFileW(L"CONOUT$", GENERIC_WRITE, FILE_SHARE_WRITE,
+ handle = CreateFileA("CONOUT$", GENERIC_WRITE, FILE_SHARE_WRITE,
NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
if (handle != INVALID_HANDLE_VALUE) {
@@ -1260,22 +1361,22 @@ ApplicationType(
{
int applType, i, nameLen, found;
HANDLE hFile;
- WCHAR *rest;
+ TCHAR *rest;
char *ext;
char buf[2];
DWORD attr, read;
IMAGE_DOS_HEADER header;
Tcl_DString nameBuf, ds;
- const WCHAR *nativeName;
+ const TCHAR *nativeName;
WCHAR nativeFullPath[MAX_PATH];
- static const char extensions[][5] = {"", ".com", ".exe", ".bat", ".cmd"};
+ static const char extensions[][5] = {"", ".com", ".exe", ".bat"};
/*
* Look for the program as an external program. First try the name as it
- * is, then try adding .com, .exe, .bat and .cmd, in that order, to the name,
+ * is, then try adding .com, .exe, and .bat, in that order, to the name,
* looking for an executable.
*
- * Using the raw SearchPathW() function doesn't do quite what is necessary.
+ * Using the raw SearchPath() function doesn't do quite what is necessary.
* If the name of the executable already contains a '.' character, it will
* not try appending the specified extension when searching (in other
* words, SearchPath will not find the program "a.b.exe" if the arguments
@@ -1285,17 +1386,16 @@ ApplicationType(
applType = APPL_NONE;
Tcl_DStringInit(&nameBuf);
- Tcl_DStringAppend(&nameBuf, originalName, TCL_INDEX_NONE);
+ Tcl_DStringAppend(&nameBuf, originalName, -1);
nameLen = Tcl_DStringLength(&nameBuf);
for (i = 0; i < (int) (sizeof(extensions) / sizeof(extensions[0])); i++) {
Tcl_DStringSetLength(&nameBuf, nameLen);
- Tcl_DStringAppend(&nameBuf, extensions[i], TCL_INDEX_NONE);
- Tcl_DStringInit(&ds);
- nativeName = Tcl_UtfToWCharDString(Tcl_DStringValue(&nameBuf),
+ Tcl_DStringAppend(&nameBuf, extensions[i], -1);
+ nativeName = Tcl_WinUtfToTChar(Tcl_DStringValue(&nameBuf),
Tcl_DStringLength(&nameBuf), &ds);
- found = SearchPathW(NULL, nativeName, NULL, MAX_PATH,
- nativeFullPath, &rest);
+ found = (*tclWinProcs->searchPathProc)(NULL, nativeName, NULL,
+ MAX_PATH, nativeFullPath, &rest);
Tcl_DStringFree(&ds);
if (found == 0) {
continue;
@@ -1306,22 +1406,20 @@ ApplicationType(
* known type.
*/
- attr = GetFileAttributesW(nativeFullPath);
- if ((attr == 0xFFFFFFFF) || (attr & FILE_ATTRIBUTE_DIRECTORY)) {
+ attr = (*tclWinProcs->getFileAttributesProc)((TCHAR *) nativeFullPath);
+ if ((attr == 0xffffffff) || (attr & FILE_ATTRIBUTE_DIRECTORY)) {
continue;
}
- Tcl_DStringInit(&ds);
- strcpy(fullName, Tcl_WCharToUtfDString(nativeFullPath, TCL_INDEX_NONE, &ds));
+ strcpy(fullName, Tcl_WinTCharToUtf((TCHAR *) nativeFullPath, -1, &ds));
Tcl_DStringFree(&ds);
ext = strrchr(fullName, '.');
- if ((ext != NULL) &&
- (strcasecmp(ext, ".cmd") == 0 || strcasecmp(ext, ".bat") == 0)) {
+ if ((ext != NULL) && (strcasecmp(ext, ".bat") == 0)) {
applType = APPL_DOS;
break;
}
- hFile = CreateFileW(nativeFullPath,
+ hFile = (*tclWinProcs->createFileProc)((TCHAR *) nativeFullPath,
GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL, NULL);
if (hFile == INVALID_HANDLE_VALUE) {
@@ -1387,13 +1485,13 @@ ApplicationType(
Tcl_DStringFree(&nameBuf);
if (applType == APPL_NONE) {
- Tcl_WinConvertError(GetLastError());
- Tcl_SetObjResult(interp, Tcl_ObjPrintf("couldn't execute \"%s\": %s",
- originalName, Tcl_PosixError(interp)));
+ TclWinConvertError(GetLastError());
+ Tcl_AppendResult(interp, "couldn't execute \"", originalName,
+ "\": ", Tcl_PosixError(interp), (char *) NULL);
return APPL_NONE;
}
- if (applType == APPL_WIN3X) {
+ if ((applType == APPL_DOS) || (applType == APPL_WIN3X)) {
/*
* Replace long path name of executable with short path name for
* 16-bit applications. Otherwise the application may not be able to
@@ -1401,9 +1499,9 @@ ApplicationType(
* application name from the arguments.
*/
- GetShortPathNameW(nativeFullPath, nativeFullPath, MAX_PATH);
- Tcl_DStringInit(&ds);
- strcpy(fullName, Tcl_WCharToUtfDString(nativeFullPath, TCL_INDEX_NONE, &ds));
+ (*tclWinProcs->getShortPathNameProc)((TCHAR *) nativeFullPath,
+ nativeFullPath, MAX_PATH);
+ strcpy(fullName, Tcl_WinTCharToUtf((TCHAR *) nativeFullPath, -1, &ds));
Tcl_DStringFree(&ds);
}
return applType;
@@ -1415,7 +1513,7 @@ ApplicationType(
* BuildCommandLine --
*
* The command line arguments are stored in linePtr separated by spaces,
- * in a form that CreateProcessW() understands. Special characters in
+ * in a form that CreateProcess() understands. Special characters in
* individual arguments from argv[] must be quoted when being stored in
* cmdLine.
*
@@ -1428,144 +1526,18 @@ ApplicationType(
*----------------------------------------------------------------------
*/
-static const char *
-BuildCmdLineBypassBS(
- const char *current,
- const char **bspos)
-{
- /*
- * Mark first backslash position.
- */
-
- if (!*bspos) {
- *bspos = current;
- }
- do {
- current++;
- } while (*current == '\\');
- return current;
-}
-
-static void
-QuoteCmdLineBackslash(
- Tcl_DString *dsPtr,
- const char *start,
- const char *current,
- const char *bspos)
-{
- if (!bspos) {
- if (current > start) { /* part before current (special) */
- Tcl_DStringAppend(dsPtr, start, (int) (current - start));
- }
- } else {
- if (bspos > start) { /* part before first backslash */
- Tcl_DStringAppend(dsPtr, start, (int) (bspos - start));
- }
- while (bspos++ < current) { /* each backslash twice */
- TclDStringAppendLiteral(dsPtr, "\\\\");
- }
- }
-}
-
-static const char *
-QuoteCmdLinePart(
- Tcl_DString *dsPtr,
- const char *start,
- const char *special,
- const char *specMetaChars,
- const char **bspos)
-{
- if (!*bspos) {
- /*
- * Rest before special (before quote).
- */
-
- QuoteCmdLineBackslash(dsPtr, start, special, NULL);
- start = special;
- } else {
- /*
- * Rest before first backslash and backslashes into new quoted block.
- */
-
- QuoteCmdLineBackslash(dsPtr, start, *bspos, NULL);
- start = *bspos;
- }
-
- /*
- * escape all special chars enclosed in quotes like `"..."`, note that
- * here we don't must escape `\` (with `\`), because it's outside of the
- * main quotes, so `\` remains `\`, but important - not at end of part,
- * because results as before the quote, so `%\%\` should be escaped as
- * `"%\%"\\`).
- */
-
- TclDStringAppendLiteral(dsPtr, "\""); /* opening escape quote-char */
- do {
- *bspos = NULL;
- special++;
- if (*special == '\\') {
- /*
- * Bypass backslashes (and mark first backslash position).
- */
-
- special = BuildCmdLineBypassBS(special, bspos);
- if (*special == '\0') {
- break;
- }
- }
- } while (*special && strchr(specMetaChars, *special));
- if (!*bspos) {
- /*
- * Unescaped rest before quote.
- */
-
- QuoteCmdLineBackslash(dsPtr, start, special, NULL);
- } else {
- /*
- * Unescaped rest before first backslash (rather belongs to the main
- * block).
- */
-
- QuoteCmdLineBackslash(dsPtr, start, *bspos, NULL);
- }
- TclDStringAppendLiteral(dsPtr, "\""); /* closing escape quote-char */
- return special;
-}
-
static void
BuildCommandLine(
const char *executable, /* Full path of executable (including
* extension). Replacement for argv[0]. */
- Tcl_Size argc, /* Number of arguments. */
+ int argc, /* Number of arguments. */
const char **argv, /* Argument strings in UTF. */
Tcl_DString *linePtr) /* Initialized Tcl_DString that receives the
- * command line (WCHAR). */
+ * command line (TCHAR). */
{
- const char *arg, *start, *special, *bspos;
- int quote = 0;
- Tcl_Size i;
+ const char *arg, *start, *special;
+ int quote, i;
Tcl_DString ds;
-#ifdef TCL_WIN_PIPE_FULLESC
- /* full escape inclusive %-subst avoidance */
- static const char specMetaChars[] = "&|^<>!()%";
- /* Characters to enclose in quotes if unpaired
- * quote flag set. */
- static const char specMetaChars2[] = "%";
- /* Character to enclose in quotes in any case
- * (regardless of unpaired-flag). */
-#else
- /* escape considering quotation only (no %-subst avoidance) */
- static const char specMetaChars[] = "&|^<>!()";
- /* Characters to enclose in quotes if unpaired
- * quote flag set. */
-#endif
- /*
- * Quote flags:
- * CL_ESCAPE - escape argument;
- * CL_QUOTE - enclose in quotes;
- * CL_UNPAIRED - previous arguments chain contains unpaired quote-char;
- */
- enum {CL_ESCAPE = 1, CL_QUOTE = 2, CL_UNPAIRED = 4};
Tcl_DStringInit(&ds);
@@ -1573,9 +1545,9 @@ BuildCommandLine(
* Prime the path. Add a space separator if we were primed with something.
*/
- TclDStringAppendDString(&ds, linePtr);
+ Tcl_DStringAppend(&ds, Tcl_DStringValue(linePtr), -1);
if (Tcl_DStringLength(linePtr) > 0) {
- TclDStringAppendLiteral(&ds, " ");
+ Tcl_DStringAppend(&ds, " ", 1);
}
for (i = 0; i < argc; i++) {
@@ -1583,166 +1555,68 @@ BuildCommandLine(
arg = executable;
} else {
arg = argv[i];
- TclDStringAppendLiteral(&ds, " ");
+ Tcl_DStringAppend(&ds, " ", 1);
}
- quote &= ~(CL_ESCAPE|CL_QUOTE); /* reset escape flags */
- bspos = NULL;
+ quote = 0;
if (arg[0] == '\0') {
- quote = CL_QUOTE;
+ quote = 1;
} else {
- for (start = arg;
- *start != '\0' &&
- (quote & (CL_ESCAPE|CL_QUOTE)) != (CL_ESCAPE|CL_QUOTE);
- start++) {
- if (*start & 0x80) {
- continue;
- }
- if (TclIsSpaceProc(*start)) {
- quote |= CL_QUOTE; /* quote only */
- if (bspos) { /* if backslash found, escape & quote */
- quote |= CL_ESCAPE;
- break;
- }
- continue;
- }
- if (strchr(specMetaChars, *start)) {
- quote |= (CL_ESCAPE|CL_QUOTE); /* escape & quote */
+ int count;
+ Tcl_UniChar ch;
+ for (start = arg; *start != '\0'; start += count) {
+ count = Tcl_UtfToUniChar(start, &ch);
+ if (Tcl_UniCharIsSpace(ch)) { /* INTL: ISO space. */
+ quote = 1;
break;
}
- if (*start == '"') {
- quote |= CL_ESCAPE; /* escape only */
- continue;
- }
- if (*start == '\\') {
- bspos = start;
- if (quote & CL_QUOTE) { /* if quote, escape & quote */
- quote |= CL_ESCAPE;
- break;
- }
- continue;
- }
}
- bspos = NULL;
- }
- if (quote & CL_QUOTE) {
- /*
- * Start of argument (main opening quote-char).
- */
-
- TclDStringAppendLiteral(&ds, "\"");
}
- if (!(quote & CL_ESCAPE)) {
- /*
- * Nothing to escape.
- */
-
- Tcl_DStringAppend(&ds, arg, TCL_INDEX_NONE);
- } else {
- start = arg;
- for (special = arg; *special != '\0'; ) {
- /*
- * Position of `\` is important before quote or at end (equal
- * `\"` because quoted).
- */
-
- if (*special == '\\') {
- /*
- * Bypass backslashes (and mark first backslash position)
- */
+ if (quote) {
+ Tcl_DStringAppend(&ds, "\"", 1);
+ }
+ start = arg;
+ for (special = arg; ; ) {
+ if ((*special == '\\') && (special[1] == '\\' ||
+ special[1] == '"' || (quote && special[1] == '\0'))) {
+ Tcl_DStringAppend(&ds, start, (int) (special - start));
+ start = special;
+ while (1) {
+ special++;
+ if (*special == '"' || (quote && *special == '\0')) {
+ /*
+ * N backslashes followed a quote -> insert N * 2 + 1
+ * backslashes then a quote.
+ */
- special = BuildCmdLineBypassBS(special, &bspos);
- if (*special == '\0') {
+ Tcl_DStringAppend(&ds, start,
+ (int) (special - start));
+ break;
+ }
+ if (*special != '\\') {
break;
}
}
- /* ["] */
- if (*special == '"') {
- /*
- * Invert the unpaired flag - observe unpaired quotes
- */
-
- quote ^= CL_UNPAIRED;
-
- /*
- * Add part before (and escape backslashes before quote).
- */
-
- QuoteCmdLineBackslash(&ds, start, special, bspos);
- bspos = NULL;
-
- /*
- * Escape using backslash
- */
-
- TclDStringAppendLiteral(&ds, "\\\"");
- start = ++special;
- continue;
- }
-
- /*
- * Unpaired (escaped) quote causes special handling on
- * meta-chars
- */
-
- if ((quote & CL_UNPAIRED) && strchr(specMetaChars, *special)) {
- special = QuoteCmdLinePart(&ds, start, special,
- specMetaChars, &bspos);
-
- /*
- * Start to current or first backslash
- */
-
- start = !bspos ? special : bspos;
- continue;
- }
-#ifdef TCL_WIN_PIPE_FULLESC
- /*
- * Special case for % - should be enclosed always (paired
- * also)
- */
-
- if (strchr(specMetaChars2, *special)) {
- special = QuoteCmdLinePart(&ds, start, special,
- specMetaChars2, &bspos);
-
- /*
- * Start to current or first backslash.
- */
-
- start = !bspos ? special : bspos;
- continue;
- }
-#endif
-
- /*
- * Other not special (and not meta) character
- */
-
- bspos = NULL; /* reset last backslash position (not
- * interesting) */
- special++;
+ Tcl_DStringAppend(&ds, start, (int) (special - start));
+ start = special;
}
-
- /*
- * Rest of argument (and escape backslashes before closing main
- * quote)
- */
-
- QuoteCmdLineBackslash(&ds, start, special,
- (quote & CL_QUOTE) ? bspos : NULL);
+ if (*special == '"') {
+ Tcl_DStringAppend(&ds, start, (int) (special - start));
+ Tcl_DStringAppend(&ds, "\\\"", 2);
+ start = special + 1;
+ }
+ if (*special == '\0') {
+ break;
+ }
+ special++;
}
- if (quote & CL_QUOTE) {
- /*
- * End of argument (main closing quote-char)
- */
-
- TclDStringAppendLiteral(&ds, "\"");
+ Tcl_DStringAppend(&ds, start, (int) (special - start));
+ if (quote) {
+ Tcl_DStringAppend(&ds, "\"", 1);
}
}
Tcl_DStringFree(linePtr);
- Tcl_DStringInit(linePtr);
- Tcl_UtfToWCharDString(Tcl_DStringValue(&ds), Tcl_DStringLength(&ds), linePtr);
+ Tcl_WinUtfToTChar(Tcl_DStringValue(&ds), Tcl_DStringLength(&ds), linePtr);
Tcl_DStringFree(&ds);
}
@@ -1769,11 +1643,12 @@ TclpCreateCommandChannel(
TclFile writeFile, /* If non-null, gives the file for writing. */
TclFile errorFile, /* If non-null, gives the file where errors
* can be read. */
- Tcl_Size numPids, /* The number of pids in the pid array. */
+ int numPids, /* The number of pids in the pid array. */
Tcl_Pid *pidPtr) /* An array of process identifiers. */
{
char channelName[16 + TCL_INTEGER_SPACE];
- PipeInfo *infoPtr = (PipeInfo *)ckalloc(sizeof(PipeInfo));
+ DWORD id;
+ PipeInfo *infoPtr = (PipeInfo *) ckalloc((unsigned) sizeof(PipeInfo));
PipeInit();
@@ -1788,7 +1663,7 @@ TclpCreateCommandChannel(
infoPtr->writeBuf = 0;
infoPtr->writeBufLen = 0;
infoPtr->writeError = 0;
- infoPtr->channel = NULL;
+ infoPtr->channel = (Tcl_Channel) NULL;
infoPtr->validMask = 0;
@@ -1799,14 +1674,14 @@ TclpCreateCommandChannel(
* Start the background reader thread.
*/
- infoPtr->readable = CreateEventW(NULL, TRUE, TRUE, NULL);
+ infoPtr->readable = CreateEvent(NULL, TRUE, TRUE, NULL);
+ infoPtr->startReader = CreateEvent(NULL, FALSE, FALSE, NULL);
+ infoPtr->stopReader = CreateEvent(NULL, TRUE, FALSE, NULL);
infoPtr->readThread = CreateThread(NULL, 256, PipeReaderThread,
- TclPipeThreadCreateTI(&infoPtr->readTI, infoPtr, infoPtr->readable),
- 0, NULL);
+ infoPtr, 0, &id);
SetThreadPriority(infoPtr->readThread, THREAD_PRIORITY_HIGHEST);
infoPtr->validMask |= TCL_READABLE;
} else {
- infoPtr->readTI = NULL;
infoPtr->readThread = 0;
}
if (writeFile != NULL) {
@@ -1814,15 +1689,13 @@ TclpCreateCommandChannel(
* Start the background writer thread.
*/
- infoPtr->writable = CreateEventW(NULL, TRUE, TRUE, NULL);
+ infoPtr->writable = CreateEvent(NULL, TRUE, TRUE, NULL);
+ infoPtr->startWriter = CreateEvent(NULL, FALSE, FALSE, NULL);
+ infoPtr->stopWriter = CreateEvent(NULL, TRUE, FALSE, NULL);
infoPtr->writeThread = CreateThread(NULL, 256, PipeWriterThread,
- TclPipeThreadCreateTI(&infoPtr->writeTI, infoPtr, infoPtr->writable),
- 0, NULL);
- SetThreadPriority(infoPtr->writeThread, THREAD_PRIORITY_HIGHEST);
+ infoPtr, 0, &id);
+ SetThreadPriority(infoPtr->readThread, THREAD_PRIORITY_HIGHEST);
infoPtr->validMask |= TCL_WRITABLE;
- } else {
- infoPtr->writeTI = NULL;
- infoPtr->writeThread = 0;
}
/*
@@ -1832,9 +1705,9 @@ TclpCreateCommandChannel(
* unique, in case channels share handles (stdin/stdout).
*/
- TclWinGenerateChannelName(channelName, "file", infoPtr);
+ sprintf(channelName, "file%" TCL_I_MODIFIER "x", (size_t)infoPtr);
infoPtr->channel = Tcl_CreateChannel(&pipeChannelType, channelName,
- infoPtr, infoPtr->validMask);
+ (ClientData) infoPtr, infoPtr->validMask);
/*
* Pipes have AUTO translation mode on Windows and ^Z eof char, which
@@ -1842,58 +1715,16 @@ TclpCreateCommandChannel(
* Windows programs that expect a ^Z at EOF.
*/
- Tcl_SetChannelOption(NULL, infoPtr->channel, "-translation", "auto");
- Tcl_SetChannelOption(NULL, infoPtr->channel, "-eofchar", "\x1A {}");
+ Tcl_SetChannelOption((Tcl_Interp *) NULL, infoPtr->channel,
+ "-translation", "auto");
+ Tcl_SetChannelOption((Tcl_Interp *) NULL, infoPtr->channel,
+ "-eofchar", "\032 {}");
return infoPtr->channel;
}
/*
*----------------------------------------------------------------------
*
- * Tcl_CreatePipe --
- *
- * System dependent interface to create a pipe for the [chan pipe]
- * command. Stolen from TclX.
- *
- * Results:
- * TCL_OK or TCL_ERROR.
- *
- *----------------------------------------------------------------------
- */
-
-int
-Tcl_CreatePipe(
- Tcl_Interp *interp, /* Errors returned in result.*/
- Tcl_Channel *rchan, /* Where to return the read side. */
- Tcl_Channel *wchan, /* Where to return the write side. */
- TCL_UNUSED(int) /*flags*/) /* Reserved for future use. */
-{
- HANDLE readHandle, writeHandle;
- SECURITY_ATTRIBUTES sec;
-
- sec.nLength = sizeof(SECURITY_ATTRIBUTES);
- sec.lpSecurityDescriptor = NULL;
- sec.bInheritHandle = FALSE;
-
- if (!CreatePipe(&readHandle, &writeHandle, &sec, 0)) {
- Tcl_WinConvertError(GetLastError());
- Tcl_SetObjResult(interp, Tcl_ObjPrintf(
- "pipe creation failed: %s", Tcl_PosixError(interp)));
- return TCL_ERROR;
- }
-
- *rchan = Tcl_MakeFileChannel((void *) readHandle, TCL_READABLE);
- Tcl_RegisterChannel(interp, *rchan);
-
- *wchan = Tcl_MakeFileChannel((void *) writeHandle, TCL_WRITABLE);
- Tcl_RegisterChannel(interp, *wchan);
-
- return TCL_OK;
-}
-
-/*
- *----------------------------------------------------------------------
- *
* TclGetAndDetachPids --
*
* Stores a list of the command PIDs for a command channel in the
@@ -1915,8 +1746,8 @@ TclGetAndDetachPids(
{
PipeInfo *pipePtr;
const Tcl_ChannelType *chanTypePtr;
- Tcl_Obj *pidsObj, *elemPtr;
- TCL_HASH_TYPE i;
+ int i;
+ char buf[TCL_INTEGER_SPACE];
/*
* Punt if the channel is not a command channel.
@@ -1927,16 +1758,14 @@ TclGetAndDetachPids(
return;
}
- pipePtr = (PipeInfo *)Tcl_GetChannelInstanceData(chan);
- TclNewObj(pidsObj);
+ pipePtr = (PipeInfo *) Tcl_GetChannelInstanceData(chan);
for (i = 0; i < pipePtr->numPids; i++) {
- TclNewIntObj(elemPtr, TclpGetPid(pipePtr->pidPtr[i]));
- Tcl_ListObjAppendElement(NULL, pidsObj, elemPtr);
- Tcl_DetachPids(1, &pipePtr->pidPtr[i]);
+ wsprintfA(buf, "%lu", TclpGetPid(pipePtr->pidPtr[i]));
+ Tcl_AppendElement(interp, buf);
+ Tcl_DetachPids(1, &(pipePtr->pidPtr[i]));
}
- Tcl_SetObjResult(interp, pidsObj);
if (pipePtr->numPids > 0) {
- ckfree(pipePtr->pidPtr);
+ ckfree((char *) pipePtr->pidPtr);
pipePtr->numPids = 0;
}
}
@@ -1959,7 +1788,7 @@ TclGetAndDetachPids(
static int
PipeBlockModeProc(
- void *instanceData, /* Instance data for channel. */
+ ClientData instanceData, /* Instance data for channel. */
int mode) /* TCL_MODE_BLOCKING or
* TCL_MODE_NONBLOCKING. */
{
@@ -1998,7 +1827,7 @@ PipeBlockModeProc(
static int
PipeClose2Proc(
- void *instanceData, /* Pointer to PipeInfo structure. */
+ ClientData instanceData, /* Pointer to PipeInfo structure. */
Tcl_Interp *interp, /* For error reporting. */
int flags) /* Flags that indicate which side to close. */
{
@@ -2007,12 +1836,12 @@ PipeClose2Proc(
int errorCode, result;
PipeInfo *infoPtr, **nextPtrPtr;
ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
- int inExit = (TclInExit() || TclInThreadExit());
+ DWORD exitCode;
errorCode = 0;
result = 0;
- if ((!flags || flags & TCL_CLOSE_READ) && (pipePtr->readFile != NULL)) {
+ if ((!flags || flags == TCL_CLOSE_READ) && (pipePtr->readFile != NULL)) {
/*
* Clean up the background thread if necessary. Note that this must be
* done before we can close the file, since the thread may be blocking
@@ -2020,10 +1849,55 @@ PipeClose2Proc(
*/
if (pipePtr->readThread) {
+ /*
+ * The thread may already have closed on its own. Check its exit
+ * code.
+ */
+
+ GetExitCodeThread(pipePtr->readThread, &exitCode);
+
+ if (exitCode == STILL_ACTIVE) {
+ /*
+ * Set the stop event so that if the reader thread is blocked
+ * in PipeReaderThread on WaitForMultipleEvents, it will exit
+ * cleanly.
+ */
+
+ SetEvent(pipePtr->stopReader);
+
+ /*
+ * Wait at most 20 milliseconds for the reader thread to
+ * close.
+ */
+
+ if (WaitForSingleObject(pipePtr->readThread,
+ 20) == WAIT_TIMEOUT) {
+ /*
+ * The thread must be blocked waiting for the pipe to
+ * become readable in ReadFile(). There isn't a clean way
+ * to exit the thread from this condition. We should
+ * terminate the child process instead to get the reader
+ * thread to fall out of ReadFile with a FALSE. (below) is
+ * not the correct way to do this, but will stay here
+ * until a better solution is found.
+ *
+ * Note that we need to guard against terminating the
+ * thread while it is in the middle of Tcl_ThreadAlert
+ * because it won't be able to release the notifier lock.
+ */
+
+ Tcl_MutexLock(&pipeMutex);
+
+ /* BUG: this leaks memory */
+ TerminateThread(pipePtr->readThread, 0);
+ Tcl_MutexUnlock(&pipeMutex);
+ }
+ }
- TclPipeThreadStop(&pipePtr->readTI, pipePtr->readThread);
CloseHandle(pipePtr->readThread);
CloseHandle(pipePtr->readable);
+ CloseHandle(pipePtr->startReader);
+ CloseHandle(pipePtr->stopReader);
pipePtr->readThread = NULL;
}
if (TclpCloseFile(pipePtr->readFile) != 0) {
@@ -2032,34 +1906,66 @@ PipeClose2Proc(
pipePtr->validMask &= ~TCL_READABLE;
pipePtr->readFile = NULL;
}
- if ((!flags || flags & TCL_CLOSE_WRITE) && (pipePtr->writeFile != NULL)) {
+ if ((!flags || flags & TCL_CLOSE_WRITE)
+ && (pipePtr->writeFile != NULL)) {
if (pipePtr->writeThread) {
+ /*
+ * Wait for the writer thread to finish the current buffer, then
+ * terminate the thread and close the handles. If the channel is
+ * nonblocking, there should be no pending write operations.
+ */
+
+ WaitForSingleObject(pipePtr->writable, INFINITE);
/*
- * Wait for the writer thread to finish the current buffer, then
- * terminate the thread and close the handles. If the channel is
- * nonblocking or may block during exit, bail out since the worker
- * thread is not interruptible and we want TIP#398-fast-exit.
+ * The thread may already have closed on it's own. Check its exit
+ * code.
*/
- if ((pipePtr->flags & PIPE_ASYNC) && inExit) {
- /* give it a chance to leave honorably */
- TclPipeThreadStopSignal(&pipePtr->writeTI, pipePtr->writable);
+ GetExitCodeThread(pipePtr->writeThread, &exitCode);
- if (WaitForSingleObject(pipePtr->writable, 20) == WAIT_TIMEOUT) {
- return EWOULDBLOCK;
- }
+ if (exitCode == STILL_ACTIVE) {
+ /*
+ * Set the stop event so that if the reader thread is blocked
+ * in PipeReaderThread on WaitForMultipleEvents, it will exit
+ * cleanly.
+ */
- } else {
+ SetEvent(pipePtr->stopWriter);
- WaitForSingleObject(pipePtr->writable, inExit ? 5000 : INFINITE);
+ /*
+ * Wait at most 20 milliseconds for the reader thread to
+ * close.
+ */
- }
+ if (WaitForSingleObject(pipePtr->writeThread,
+ 20) == WAIT_TIMEOUT) {
+ /*
+ * The thread must be blocked waiting for the pipe to
+ * consume input in WriteFile(). There isn't a clean way
+ * to exit the thread from this condition. We should
+ * terminate the child process instead to get the writer
+ * thread to fall out of WriteFile with a FALSE. (below)
+ * is not the correct way to do this, but will stay here
+ * until a better solution is found.
+ *
+ * Note that we need to guard against terminating the
+ * thread while it is in the middle of Tcl_ThreadAlert
+ * because it won't be able to release the notifier lock.
+ */
- TclPipeThreadStop(&pipePtr->writeTI, pipePtr->writeThread);
+ Tcl_MutexLock(&pipeMutex);
+
+ /* BUG: this leaks memory */
+ TerminateThread(pipePtr->writeThread, 0);
+ Tcl_MutexUnlock(&pipeMutex);
+ }
+ }
- CloseHandle(pipePtr->writable);
CloseHandle(pipePtr->writeThread);
+ CloseHandle(pipePtr->writable);
+ CloseHandle(pipePtr->startWriter);
+ CloseHandle(pipePtr->stopWriter);
pipePtr->writeThread = NULL;
}
if (TclpCloseFile(pipePtr->writeFile) != 0) {
@@ -2094,7 +2000,7 @@ PipeClose2Proc(
}
}
- if ((pipePtr->flags & PIPE_ASYNC) || inExit) {
+ if ((pipePtr->flags & PIPE_ASYNC) || TclInExit()) {
/*
* If the channel is non-blocking or Tcl is being cleaned up, just
* detach the children PIDs, reap them (important if we are in a
@@ -2119,11 +2025,12 @@ PipeClose2Proc(
*/
if (pipePtr->errorFile) {
- WinFile *filePtr = (WinFile *) pipePtr->errorFile;
+ WinFile *filePtr;
- errChan = Tcl_MakeFileChannel((void *)filePtr->handle,
+ filePtr = (WinFile*)pipePtr->errorFile;
+ errChan = Tcl_MakeFileChannel((ClientData) filePtr->handle,
TCL_READABLE);
- ckfree(filePtr);
+ ckfree((char *) filePtr);
} else {
errChan = NULL;
}
@@ -2133,14 +2040,14 @@ PipeClose2Proc(
}
if (pipePtr->numPids > 0) {
- ckfree(pipePtr->pidPtr);
+ ckfree((char *) pipePtr->pidPtr);
}
if (pipePtr->writeBuf != NULL) {
ckfree(pipePtr->writeBuf);
}
- ckfree(pipePtr);
+ ckfree((char*) pipePtr);
if (errorCode == 0) {
return result;
@@ -2168,7 +2075,7 @@ PipeClose2Proc(
static int
PipeInputProc(
- void *instanceData, /* Pipe state. */
+ ClientData instanceData, /* Pipe state. */
char *buf, /* Where to store data read. */
int bufSize, /* How much space is available in the
* buffer? */
@@ -2233,7 +2140,7 @@ PipeInputProc(
return bytesRead;
}
- Tcl_WinConvertError(GetLastError());
+ TclWinConvertError(GetLastError());
if (errno == EPIPE) {
infoPtr->readFlags |= PIPE_EOF;
return 0;
@@ -2262,7 +2169,7 @@ PipeInputProc(
static int
PipeOutputProc(
- void *instanceData, /* Pipe state. */
+ ClientData instanceData, /* Pipe state. */
const char *buf, /* The data buffer. */
int toWrite, /* How many bytes to write? */
int *errorCode) /* Where to store error code. */
@@ -2272,18 +2179,14 @@ PipeOutputProc(
DWORD bytesWritten, timeout;
*errorCode = 0;
-
- /* avoid blocking if pipe-thread exited */
- timeout = ((infoPtr->flags & PIPE_ASYNC)
- || !TclPipeThreadIsAlive(&infoPtr->writeTI)
- || TclInExit() || TclInThreadExit()) ? 0 : INFINITE;
+ timeout = (infoPtr->flags & PIPE_ASYNC) ? 0 : INFINITE;
if (WaitForSingleObject(infoPtr->writable, timeout) == WAIT_TIMEOUT) {
/*
* The writer thread is blocked waiting for a write to complete and
* the channel is in non-blocking mode.
*/
- errno = EWOULDBLOCK;
+ errno = EAGAIN;
goto error;
}
@@ -2292,7 +2195,7 @@ PipeOutputProc(
*/
if (infoPtr->writeError) {
- Tcl_WinConvertError(infoPtr->writeError);
+ TclWinConvertError(infoPtr->writeError);
infoPtr->writeError = 0;
goto error;
}
@@ -2312,12 +2215,12 @@ PipeOutputProc(
ckfree(infoPtr->writeBuf);
}
infoPtr->writeBufLen = toWrite;
- infoPtr->writeBuf = (char *)ckalloc(toWrite);
+ infoPtr->writeBuf = ckalloc((unsigned int) toWrite);
}
- memcpy(infoPtr->writeBuf, buf, toWrite);
+ memcpy(infoPtr->writeBuf, buf, (size_t) toWrite);
infoPtr->toWrite = toWrite;
ResetEvent(infoPtr->writable);
- TclPipeThreadSignal(&infoPtr->writeTI);
+ SetEvent(infoPtr->startWriter);
bytesWritten = toWrite;
} else {
/*
@@ -2327,7 +2230,7 @@ PipeOutputProc(
if (WriteFile(filePtr->handle, (LPVOID) buf, (DWORD) toWrite,
&bytesWritten, (LPOVERLAPPED) NULL) == FALSE) {
- Tcl_WinConvertError(GetLastError());
+ TclWinConvertError(GetLastError());
goto error;
}
}
@@ -2444,7 +2347,7 @@ PipeEventProc(
static void
PipeWatchProc(
- void *instanceData, /* Pipe state. */
+ ClientData instanceData, /* Pipe state. */
int mask) /* What events to watch for, OR-ed combination
* of TCL_READABLE, TCL_WRITABLE and
* TCL_EXCEPTION. */
@@ -2462,7 +2365,6 @@ PipeWatchProc(
infoPtr->watchMask = mask & infoPtr->validMask;
if (infoPtr->watchMask) {
Tcl_Time blockTime = { 0, 0 };
-
if (!oldMask) {
infoPtr->nextPtr = tsdPtr->firstPipePtr;
tsdPtr->firstPipePtr = infoPtr;
@@ -2506,21 +2408,21 @@ PipeWatchProc(
static int
PipeGetHandleProc(
- void *instanceData, /* The pipe state. */
+ ClientData instanceData, /* The pipe state. */
int direction, /* TCL_READABLE or TCL_WRITABLE */
- void **handlePtr) /* Where to store the handle. */
+ ClientData *handlePtr) /* Where to store the handle. */
{
PipeInfo *infoPtr = (PipeInfo *) instanceData;
WinFile *filePtr;
if (direction == TCL_READABLE && infoPtr->readFile) {
filePtr = (WinFile*) infoPtr->readFile;
- *handlePtr = (void *) filePtr->handle;
+ *handlePtr = (ClientData) filePtr->handle;
return TCL_OK;
}
if (direction == TCL_WRITABLE && infoPtr->writeFile) {
filePtr = (WinFile*) infoPtr->writeFile;
- *handlePtr = (void *) filePtr->handle;
+ *handlePtr = (ClientData) filePtr->handle;
return TCL_OK;
}
return TCL_ERROR;
@@ -2574,7 +2476,7 @@ Tcl_WaitPid(
prevPtrPtr = &procList;
for (infoPtr = procList; infoPtr != NULL;
prevPtrPtr = &infoPtr->nextPtr, infoPtr = infoPtr->nextPtr) {
- if (infoPtr->dwProcessId == PTR2UINT(pid)) {
+ if (infoPtr->hProcess == (HANDLE) pid) {
*prevPtrPtr = infoPtr->nextPtr;
break;
}
@@ -2692,7 +2594,7 @@ Tcl_WaitPid(
*/
CloseHandle(infoPtr->hProcess);
- ckfree(infoPtr);
+ ckfree((char*)infoPtr);
return result;
}
@@ -2718,9 +2620,9 @@ Tcl_WaitPid(
void
TclWinAddProcess(
void *hProcess, /* Handle to process */
- Tcl_Size id) /* Global process identifier */
+ unsigned long id) /* Global process identifier */
{
- ProcInfo *procPtr = (ProcInfo *)ckalloc(sizeof(ProcInfo));
+ ProcInfo *procPtr = (ProcInfo *) ckalloc(sizeof(ProcInfo));
PipeInit();
@@ -2749,9 +2651,10 @@ TclWinAddProcess(
*----------------------------------------------------------------------
*/
+ /* ARGSUSED */
int
Tcl_PidObjCmd(
- TCL_UNUSED(void *),
+ ClientData dummy, /* Not used. */
Tcl_Interp *interp, /* Current interpreter. */
int objc, /* Number of arguments. */
Tcl_Obj *const *objv) /* Argument strings. */
@@ -2759,18 +2662,19 @@ Tcl_PidObjCmd(
Tcl_Channel chan;
const Tcl_ChannelType *chanTypePtr;
PipeInfo *pipePtr;
- TCL_HASH_TYPE i;
- Tcl_Obj *resultPtr, *elemPtr;
+ int i;
+ Tcl_Obj *resultPtr;
+ char buf[TCL_INTEGER_SPACE];
if (objc > 2) {
Tcl_WrongNumArgs(interp, 1, objv, "?channelId?");
return TCL_ERROR;
}
if (objc == 1) {
- TclNewIntObj(elemPtr, getpid());
- Tcl_SetObjResult(interp, elemPtr);
+ wsprintfA(buf, "%lu", (unsigned long) getpid());
+ Tcl_SetObjResult(interp, Tcl_NewStringObj(buf, -1));
} else {
- chan = Tcl_GetChannel(interp, TclGetString(objv[1]),
+ chan = Tcl_GetChannel(interp, Tcl_GetStringFromObj(objv[1], NULL),
NULL);
if (chan == (Tcl_Channel) NULL) {
return TCL_ERROR;
@@ -2781,10 +2685,11 @@ Tcl_PidObjCmd(
}
pipePtr = (PipeInfo *) Tcl_GetChannelInstanceData(chan);
- TclNewObj(resultPtr);
+ resultPtr = Tcl_NewObj();
for (i = 0; i < pipePtr->numPids; i++) {
- TclNewIntObj(elemPtr, TclpGetPid(pipePtr->pidPtr[i]));
- Tcl_ListObjAppendElement(/*interp*/ NULL, resultPtr, elemPtr);
+ wsprintfA(buf, "%lu", TclpGetPid(pipePtr->pidPtr[i]));
+ Tcl_ListObjAppendElement(/*interp*/ NULL, resultPtr,
+ Tcl_NewStringObj(buf, -1));
}
Tcl_SetObjResult(interp, resultPtr);
}
@@ -2820,23 +2725,21 @@ WaitForRead(
* or not. */
{
DWORD timeout, count;
- HANDLE handle = ((WinFile *) infoPtr->readFile)->handle;
+ HANDLE *handle = ((WinFile *) infoPtr->readFile)->handle;
while (1) {
/*
* Synchronize with the reader thread.
*/
- /* avoid blocking if pipe-thread exited */
- timeout = (!blocking || !TclPipeThreadIsAlive(&infoPtr->readTI)
- || TclInExit() || TclInThreadExit()) ? 0 : INFINITE;
+ timeout = blocking ? INFINITE : 0;
if (WaitForSingleObject(infoPtr->readable, timeout) == WAIT_TIMEOUT) {
/*
* The reader thread is blocked waiting for data and the channel
* is in non-blocking mode.
*/
- errno = EWOULDBLOCK;
+ errno = EAGAIN;
return -1;
}
@@ -2859,7 +2762,7 @@ WaitForRead(
if (PeekNamedPipe(handle, (LPVOID) NULL, (DWORD) 0,
(LPDWORD) NULL, &count, (LPDWORD) NULL) != TRUE) {
- Tcl_WinConvertError(GetLastError());
+ TclWinConvertError(GetLastError());
/*
* Check to see if the peek failed because of EOF.
@@ -2903,7 +2806,7 @@ WaitForRead(
*/
ResetEvent(infoPtr->readable);
- TclPipeThreadSignal(&infoPtr->readTI);
+ SetEvent(infoPtr->startReader);
}
}
@@ -2931,11 +2834,15 @@ static DWORD WINAPI
PipeReaderThread(
LPVOID arg)
{
- TclPipeThreadInfo *pipeTI = (TclPipeThreadInfo *) arg;
- PipeInfo *infoPtr = NULL; /* access info only after success init/wait */
- HANDLE handle = NULL;
+ PipeInfo *infoPtr = (PipeInfo *)arg;
+ HANDLE *handle = ((WinFile *) infoPtr->readFile)->handle;
DWORD count, err;
int done = 0;
+ HANDLE wEvents[2];
+ DWORD waitResult;
+
+ wEvents[0] = infoPtr->stopReader;
+ wEvents[1] = infoPtr->startReader;
while (!done) {
/*
@@ -2943,14 +2850,15 @@ PipeReaderThread(
* pipe becoming readable.
*/
- if (!TclPipeThreadWaitForSignal(&pipeTI)) {
- /* exit */
- break;
- }
+ waitResult = WaitForMultipleObjects(2, wEvents, FALSE, INFINITE);
+
+ if (waitResult != (WAIT_OBJECT_0 + 1)) {
+ /*
+ * The start event was not signaled. It might be the stop event or
+ * an error, so exit.
+ */
- if (!infoPtr) {
- infoPtr = (PipeInfo *) pipeTI->clientData;
- handle = ((WinFile *) infoPtr->readFile)->handle;
+ break;
}
/*
@@ -2972,7 +2880,7 @@ PipeReaderThread(
infoPtr->readFlags |= PIPE_EOF;
done = 1;
} else if (err == ERROR_INVALID_HANDLE) {
- done = 1;
+ break;
}
} else if (count == 0) {
if (ReadFile(handle, &(infoPtr->extraByte), 1, &count, NULL)
@@ -2994,11 +2902,12 @@ PipeReaderThread(
infoPtr->readFlags |= PIPE_EOF;
done = 1;
} else if (err == ERROR_INVALID_HANDLE) {
- done = 1;
+ break;
}
}
}
+
/*
* Signal the main thread by signalling the readable event and then
* waking up the notifier thread.
@@ -3024,12 +2933,6 @@ PipeReaderThread(
Tcl_MutexUnlock(&pipeMutex);
}
- /*
- * If state of thread was set to stop, we can sane free info structure,
- * otherwise it is shared with main thread, so main thread will own it
- */
- TclPipeThreadExit(&pipeTI);
-
return 0;
}
@@ -3054,25 +2957,31 @@ static DWORD WINAPI
PipeWriterThread(
LPVOID arg)
{
- TclPipeThreadInfo *pipeTI = (TclPipeThreadInfo *)arg;
- PipeInfo *infoPtr = NULL; /* access info only after success init/wait */
- HANDLE handle = NULL;
+ PipeInfo *infoPtr = (PipeInfo *)arg;
+ HANDLE *handle = ((WinFile *) infoPtr->writeFile)->handle;
DWORD count, toWrite;
char *buf;
int done = 0;
+ HANDLE wEvents[2];
+ DWORD waitResult;
+
+ wEvents[0] = infoPtr->stopWriter;
+ wEvents[1] = infoPtr->startWriter;
while (!done) {
/*
* Wait for the main thread to signal before attempting to write.
*/
- if (!TclPipeThreadWaitForSignal(&pipeTI)) {
- /* exit */
- break;
- }
- if (!infoPtr) {
- infoPtr = (PipeInfo *)pipeTI->clientData;
- handle = ((WinFile *) infoPtr->writeFile)->handle;
+ waitResult = WaitForMultipleObjects(2, wEvents, FALSE, INFINITE);
+
+ if (waitResult != (WAIT_OBJECT_0 + 1)) {
+ /*
+ * The start event was not signaled. It might be the stop event or
+ * an error, so exit.
+ */
+
+ break;
}
buf = infoPtr->writeBuf;
@@ -3118,12 +3027,6 @@ PipeWriterThread(
Tcl_MutexUnlock(&pipeMutex);
}
- /*
- * If state of thread was set to stop, we can sane free info structure,
- * otherwise it is shared with main thread, so main thread will own it.
- */
- TclPipeThreadExit(&pipeTI);
-
return 0;
}
@@ -3145,7 +3048,7 @@ PipeWriterThread(
static void
PipeThreadActionProc(
- void *instanceData,
+ ClientData instanceData,
int action)
{
PipeInfo *infoPtr = (PipeInfo *) instanceData;
@@ -3153,7 +3056,7 @@ PipeThreadActionProc(
/*
* We do not access firstPipePtr in the thread structures. This is not for
* all pipes managed by the thread, but only those we are watching.
- * Removal of the fileevent handlers before transfer thus takes care of
+ * Removal of the filevent handlers before transfer thus takes care of
* this structure.
*/
@@ -3178,544 +3081,6 @@ PipeThreadActionProc(
}
/*
- *----------------------------------------------------------------------
- *
- * TclpOpenTemporaryFile --
- *
- * Creates a temporary file, possibly based on the supplied bits and
- * pieces of template supplied in the first three arguments. If the
- * fourth argument is non-NULL, it contains a Tcl_Obj to store the name
- * of the temporary file in (and it is caller's responsibility to clean
- * up). If the fourth argument is NULL, try to arrange for the temporary
- * file to go away once it is no longer needed.
- *
- * Results:
- * A read-write Tcl Channel open on the file.
- *
- *----------------------------------------------------------------------
- */
-
-Tcl_Channel
-TclpOpenTemporaryFile(
- TCL_UNUSED(Tcl_Obj *) /*dirObj*/,
- Tcl_Obj *basenameObj,
- TCL_UNUSED(Tcl_Obj *) /*extensionObj*/,
- Tcl_Obj *resultingNameObj)
-{
- WCHAR name[MAX_PATH];
- char *namePtr;
- HANDLE handle;
- DWORD flags = FILE_ATTRIBUTE_TEMPORARY;
- Tcl_Size length;
- int counter, counter2;
- Tcl_DString buf;
-
- if (!resultingNameObj) {
- flags |= FILE_FLAG_DELETE_ON_CLOSE;
- }
-
- namePtr = (char *) name;
- length = GetTempPathW(MAX_PATH, name);
- if (length == 0) {
- goto gotError;
- }
- namePtr += length * sizeof(WCHAR);
- if (basenameObj) {
- const char *string = TclGetStringFromObj(basenameObj, &length);
-
- Tcl_DStringInit(&buf);
- Tcl_UtfToWCharDString(string, length, &buf);
- memcpy(namePtr, Tcl_DStringValue(&buf), Tcl_DStringLength(&buf));
- namePtr += Tcl_DStringLength(&buf);
- Tcl_DStringFree(&buf);
- } else {
- const WCHAR *baseStr = L"TCL";
- length = 3 * sizeof(WCHAR);
-
- memcpy(namePtr, baseStr, length);
- namePtr += length;
- }
- counter = TclpGetClicks() % 65533;
- counter2 = 1024; /* Only try this many times! Prevents
- * an infinite loop. */
-
- do {
- char number[TCL_INTEGER_SPACE + 4];
-
- snprintf(number, sizeof(number), "%d.TMP", counter);
- counter = (unsigned short) (counter + 1);
- Tcl_DStringInit(&buf);
- Tcl_UtfToWCharDString(number, strlen(number), &buf);
- Tcl_DStringSetLength(&buf, Tcl_DStringLength(&buf) + 1);
- memcpy(namePtr, Tcl_DStringValue(&buf), Tcl_DStringLength(&buf) + 1);
- Tcl_DStringFree(&buf);
-
- handle = CreateFileW(name,
- GENERIC_READ|GENERIC_WRITE, 0, NULL, CREATE_NEW, flags, NULL);
- } while (handle == INVALID_HANDLE_VALUE
- && --counter2 > 0
- && GetLastError() == ERROR_FILE_EXISTS);
- if (handle == INVALID_HANDLE_VALUE) {
- goto gotError;
- }
-
- if (resultingNameObj) {
- Tcl_Obj *tmpObj = TclpNativeToNormalized(name);
-
- Tcl_AppendObjToObj(resultingNameObj, tmpObj);
- TclDecrRefCount(tmpObj);
- }
-
- return Tcl_MakeFileChannel((void *) handle,
- TCL_READABLE|TCL_WRITABLE);
-
- gotError:
- Tcl_WinConvertError(GetLastError());
- return NULL;
-}
-
-/*
- *----------------------------------------------------------------------
- *
- * TclPipeThreadCreateTI --
- *
- * Creates a thread info structure, can be owned by worker.
- *
- * Results:
- * Pointer to created TI structure.
- *
- *----------------------------------------------------------------------
- */
-
-TclPipeThreadInfo *
-TclPipeThreadCreateTI(
- TclPipeThreadInfo **pipeTIPtr,
- void *clientData,
- HANDLE wakeEvent)
-{
- TclPipeThreadInfo *pipeTI;
-#ifndef _PTI_USE_CKALLOC
- pipeTI = (TclPipeThreadInfo *)malloc(sizeof(TclPipeThreadInfo));
-#else
- pipeTI = (TclPipeThreadInfo *)ckalloc(sizeof(TclPipeThreadInfo));
-#endif /* !_PTI_USE_CKALLOC */
- pipeTI->evControl = CreateEventW(NULL, FALSE, FALSE, NULL);
- pipeTI->state = PTI_STATE_IDLE;
- pipeTI->clientData = clientData;
- pipeTI->evWakeUp = wakeEvent;
- return (*pipeTIPtr = pipeTI);
-}
-
-/*
- *----------------------------------------------------------------------
- *
- * TclPipeThreadWaitForSignal --
- *
- * Wait for work/stop signals inside pipe worker.
- *
- * Results:
- * 1 if signaled to work, 0 if signaled to stop.
- *
- * Side effects:
- * If this function returns 0, TI-structure pointer given via pipeTIPtr
- * may be NULL, so not accessible (can be owned by main thread).
- *
- *----------------------------------------------------------------------
- */
-
-int
-TclPipeThreadWaitForSignal(
- TclPipeThreadInfo **pipeTIPtr)
-{
- TclPipeThreadInfo *pipeTI = *pipeTIPtr;
- LONG state;
- DWORD waitResult;
- HANDLE wakeEvent;
-
- if (!pipeTI) {
- return 0;
- }
-
- wakeEvent = pipeTI->evWakeUp;
-
- /*
- * Wait for the main thread to signal before attempting to do the work.
- */
-
- /*
- * Reset work state of thread (idle/waiting)
- */
-
- state = InterlockedCompareExchange(&pipeTI->state, PTI_STATE_IDLE,
- PTI_STATE_WORK);
- if (state & (PTI_STATE_STOP|PTI_STATE_END)) {
- /*
- * End of work, check the owner of structure.
- */
-
- goto end;
- }
-
- /*
- * Entering wait
- */
-
- waitResult = WaitForSingleObject(pipeTI->evControl, INFINITE);
- if (waitResult != WAIT_OBJECT_0) {
- /*
- * The control event was not signaled, so end of work (unexpected
- * behaviour, main thread can be dead?).
- */
-
- goto end;
- }
-
- /*
- * Try to set work state of thread
- */
-
- state = InterlockedCompareExchange(&pipeTI->state, PTI_STATE_WORK,
- PTI_STATE_IDLE);
- if (state & (PTI_STATE_STOP|PTI_STATE_END)) {
- /*
- * End of work
- */
-
- goto end;
- }
-
- /*
- * Signaled to work.
- */
-
- return 1;
-
- end:
- /*
- * End of work, check the owner of the TI structure.
- */
-
- if (state != PTI_STATE_STOP) {
- *pipeTIPtr = NULL;
- } else {
- pipeTI->evWakeUp = NULL;
- }
- if (wakeEvent) {
- SetEvent(wakeEvent);
- }
- return 0;
-}
-
-/*
- *----------------------------------------------------------------------
- *
- * TclPipeThreadStopSignal --
- *
- * Send stop signal to the pipe worker (without waiting).
- *
- * After calling of this function, TI-structure pointer given via pipeTIPtr
- * may be NULL.
- *
- * Results:
- * 1 if signaled (or pipe-thread is down), 0 if pipe thread still working.
- *
- *----------------------------------------------------------------------
- */
-
-int
-TclPipeThreadStopSignal(
- TclPipeThreadInfo **pipeTIPtr,
- HANDLE wakeEvent)
-{
- TclPipeThreadInfo *pipeTI = *pipeTIPtr;
- HANDLE evControl;
- int state;
-
- if (!pipeTI) {
- return 1;
- }
- evControl = pipeTI->evControl;
- pipeTI->evWakeUp = wakeEvent;
- state = InterlockedCompareExchange(&pipeTI->state, PTI_STATE_STOP,
- PTI_STATE_IDLE);
- switch (state) {
- case PTI_STATE_IDLE:
- /*
- * Thread was idle/waiting, notify it goes teardown
- */
-
- SetEvent(evControl);
- *pipeTIPtr = NULL;
- /* FALLTHRU */
- case PTI_STATE_DOWN:
- return 1;
-
- default:
- /*
- * Thread works currently, we should try to end it, own the TI
- * structure (because of possible sharing the joint structures with
- * thread)
- */
-
- InterlockedExchange(&pipeTI->state, PTI_STATE_END);
- break;
- }
-
- return 0;
-}
-
-/*
- *----------------------------------------------------------------------
- *
- * TclPipeThreadStop --
- *
- * Send stop signal to the pipe worker and wait for thread completion.
- *
- * May be combined with TclPipeThreadStopSignal.
- *
- * After calling of this function, TI-structure pointer given via pipeTIPtr
- * is not accessible (owned by pipe worker or released here).
- *
- * Results:
- * None.
- *
- * Side effects:
- * Can terminate pipe worker (and / or stop its synchronous operations).
- *
- *----------------------------------------------------------------------
- */
-
-void
-TclPipeThreadStop(
- TclPipeThreadInfo **pipeTIPtr,
- HANDLE hThread)
-{
- TclPipeThreadInfo *pipeTI = *pipeTIPtr;
- HANDLE evControl;
- int state;
-
- if (!pipeTI) {
- return;
- }
- pipeTI = *pipeTIPtr;
- evControl = pipeTI->evControl;
- pipeTI->evWakeUp = NULL;
-
- /*
- * Try to sane stop the pipe worker, corresponding its current state
- */
-
- state = InterlockedCompareExchange(&pipeTI->state, PTI_STATE_STOP,
- PTI_STATE_IDLE);
- switch (state) {
- case PTI_STATE_IDLE:
- /*
- * Thread was idle/waiting, notify it goes teardown
- */
-
- SetEvent(evControl);
-
- /*
- * We don't need to wait for it at all, thread frees himself (owns the
- * TI structure)
- */
-
- pipeTI = NULL;
- break;
-
- case PTI_STATE_STOP:
- /*
- * Already stopped, thread frees himself (owns the TI structure)
- */
-
- pipeTI = NULL;
- break;
- case PTI_STATE_DOWN:
- /*
- * Thread already down (?), do nothing
- */
-
- /*
- * We don't need to wait for it, but we should free pipeTI
- */
- hThread = NULL;
- break;
-
- /* case PTI_STATE_WORK: */
- default:
- /*
- * Thread works currently, we should try to end it, own the TI
- * structure (because of possible sharing the joint structures with
- * thread)
- */
-
- state = InterlockedCompareExchange(&pipeTI->state, PTI_STATE_END,
- PTI_STATE_WORK);
- if (state == PTI_STATE_DOWN) {
- /*
- * We don't need to wait for it, but we should free pipeTI
- */
- hThread = NULL;
- }
- break;
- }
-
- if (pipeTI && hThread) {
- DWORD exitCode;
-
- /*
- * The thread may already have closed on its own. Check its exit
- * code.
- */
-
- GetExitCodeThread(hThread, &exitCode);
-
- if (exitCode == STILL_ACTIVE) {
- int inExit = (TclInExit() || TclInThreadExit());
-
- /*
- * Set the stop event so that if the pipe thread is blocked
- * somewhere, it may hereafter sane exit cleanly.
- */
-
- SetEvent(evControl);
-
- /*
- * Cancel all sync-IO of this thread (may be blocked there).
- */
-
- CancelSynchronousIo(hThread);
-
- /*
- * Wait at most 20 milliseconds for the reader thread to close
- * (regarding TIP#398-fast-exit).
- */
-
- /*
- * If we want TIP#398-fast-exit.
- */
-
- if (WaitForSingleObject(hThread, inExit ? 0 : 20) == WAIT_TIMEOUT) {
- /*
- * The thread must be blocked waiting for the pipe to become
- * readable in ReadFile(). There isn't a clean way to exit the
- * thread from this condition. We should terminate the child
- * process instead to get the reader thread to fall out of
- * ReadFile with a FALSE. (below) is not the correct way to do
- * this, but will stay here until a better solution is found.
- *
- * Note that we need to guard against terminating the thread
- * while it is in the middle of Tcl_ThreadAlert because it
- * won't be able to release the notifier lock.
- *
- * Also note that terminating threads during their
- * initialization or teardown phase may result in ntdll.dll's
- * LoaderLock to remain locked indefinitely. This causes
- * ntdll.dll's LdrpInitializeThread() to deadlock trying to
- * acquire LoaderLock. LdrpInitializeThread() is executed
- * within new threads to perform initialization and to execute
- * DllMain() of all loaded dlls. As a result, all new threads
- * are deadlocked in their initialization phase and never
- * execute, even though CreateThread() reports successful
- * thread creation. This results in a very weird process-wide
- * behavior, which is extremely hard to debug.
- *
- * THREADS SHOULD NEVER BE TERMINATED. Period.
- *
- * But for now, check if thread is exiting, and if so, let it
- * die peacefully.
- *
- * Also don't terminate if in exit (otherwise deadlocked in
- * ntdll.dll's).
- */
-
- if (pipeTI->state != PTI_STATE_DOWN
- && WaitForSingleObject(hThread,
- inExit ? 50 : 5000) != WAIT_OBJECT_0) {
- /* BUG: this leaks memory */
- if (inExit || !TerminateThread(hThread, 0)) {
- /*
- * in exit or terminate fails, just give thread a
- * chance to exit
- */
-
- if (InterlockedExchange(&pipeTI->state,
- PTI_STATE_STOP) != PTI_STATE_DOWN) {
- pipeTI = NULL;
- }
- }
- }
- }
- }
- }
-
- *pipeTIPtr = NULL;
- if (pipeTI) {
- if (pipeTI->evWakeUp) {
- SetEvent(pipeTI->evWakeUp);
- }
- CloseHandle(pipeTI->evControl);
-#ifndef _PTI_USE_CKALLOC
- free(pipeTI);
-#else
- ckfree(pipeTI);
-#endif /* !_PTI_USE_CKALLOC */
- }
-}
-
-/*
- *----------------------------------------------------------------------
- *
- * TclPipeThreadExit --
- *
- * Clean-up for the pipe thread (removes owned TI-structure in worker).
- *
- * Should be executed on worker exit, to inform the main thread or
- * free TI-structure (if owned).
- *
- * After calling of this function, TI-structure pointer given via pipeTIPtr
- * is not accessible (owned by main thread or released here).
- *
- * Results:
- * None.
- *
- *----------------------------------------------------------------------
- */
-
-void
-TclPipeThreadExit(
- TclPipeThreadInfo **pipeTIPtr)
-{
- LONG state;
- TclPipeThreadInfo *pipeTI = *pipeTIPtr;
-
- /*
- * If state of thread was set to stop (exactly), we can sane free its info
- * structure, otherwise it is shared with main thread, so main thread will
- * own it.
- */
-
- if (!pipeTI) {
- return;
- }
- *pipeTIPtr = NULL;
- state = InterlockedExchange(&pipeTI->state, PTI_STATE_DOWN);
- if (state == PTI_STATE_STOP) {
- CloseHandle(pipeTI->evControl);
- if (pipeTI->evWakeUp) {
- SetEvent(pipeTI->evWakeUp);
- }
-#ifndef _PTI_USE_CKALLOC
- free(pipeTI);
-#else
- ckfree(pipeTI);
- /* be sure all subsystems used are finalized */
- Tcl_FinalizeThread();
-#endif /* !_PTI_USE_CKALLOC */
- }
-}
-
-/*
* Local Variables:
* mode: c
* c-basic-offset: 4