📄 tclwinpipe.c
字号:
int mode; /* TCL_MODE_BLOCKING or * TCL_MODE_NONBLOCKING. */{ PipeInfo *infoPtr = (PipeInfo *) instanceData; /* * Pipes on Windows can not be switched between blocking and nonblocking, * hence we have to emulate the behavior. This is done in the input * function by checking against a bit in the state. We set or unset the * bit here to cause the input function to emulate the correct behavior. */ if (mode == TCL_MODE_NONBLOCKING) { infoPtr->flags |= PIPE_ASYNC; } else { infoPtr->flags &= ~(PIPE_ASYNC); } return 0;}/* *---------------------------------------------------------------------- * * PipeCloseProc -- * * Closes a pipe based IO channel. * * Results: * 0 on success, errno otherwise. * * Side effects: * Closes the physical channel. * *---------------------------------------------------------------------- */static intPipeCloseProc(instanceData, interp) ClientData instanceData; /* Pointer to PipeInfo structure. */ Tcl_Interp *interp; /* For error reporting. */{ PipeInfo *pipePtr = (PipeInfo *) instanceData; Tcl_Channel errChan; int errorCode, result; PipeInfo *infoPtr, **nextPtrPtr; /* * Remove the file from the list of watched files. */ 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; } } } /* * Wrap the error file into a channel and give it to the cleanup * routine. If we are running in Win32s, just delete the error file * immediately, because it was never used. */ 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); } ckfree((char*) pipePtr); if (errorCode == 0) { return result; } return errorCode;}/* *---------------------------------------------------------------------- * * PipeInputProc -- * * Reads input from the IO channel into the buffer given. Returns * count of how many bytes were actually read, and an error indication. * * Results: * A count of how many bytes were read is returned and an error * indication is returned in an output argument. * * Side effects: * Reads input from the actual channel. * *---------------------------------------------------------------------- */static intPipeInputProc(instanceData, buf, bufSize, errorCode) ClientData instanceData; /* Pipe state. */ char *buf; /* Where to store data read. */ int bufSize; /* How much space is available * in the buffer? */ int *errorCode; /* Where to store error code. */{ PipeInfo *infoPtr = (PipeInfo *) instanceData; WinFile *filePtr = (WinFile*) infoPtr->readFile; DWORD count; DWORD bytesRead; *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 { /* * Pipes will block until the requested number of bytes has been * read. To avoid blocking unnecessarily, we look ahead and only * read as much as is available. */ if (PeekNamedPipe(filePtr->handle, (LPVOID) NULL, (DWORD) 0, (LPDWORD) NULL, &count, (LPDWORD) NULL) == TRUE) { if ((count != 0) && ((DWORD) bufSize > count)) { bufSize = (int) count; /* * This code is commented out because on Win95 we don't get * notifier of eof on a pipe unless we try to read it. * The correct solution is to move to threads. *//* } else if ((count == 0) && (infoPtr->flags & PIPE_ASYNC)) { *//* errno = *errorCode = EAGAIN; *//* return -1; */ } else if ((count == 0) && !(infoPtr->flags & PIPE_ASYNC)) { bufSize = 1; } } else { goto error; } } /* * Note that we will block on reads from a console buffer until a * full line has been entered. The only way I know of to get * around this is to write a console driver. We should probably * do this at some point, but for now, we just block. */ if (ReadFile(filePtr->handle, (LPVOID) buf, (DWORD) bufSize, &bytesRead, (LPOVERLAPPED) NULL) == FALSE) { goto error; } return bytesRead; error: TclWinConvertError(GetLastError()); if (errno == EPIPE) { return 0; } *errorCode = errno; return -1;}/* *---------------------------------------------------------------------- * * PipeOutputProc -- * * Writes the given output on the IO channel. Returns count of how * many characters were actually written, and an error indication. * * Results: * A count of how many characters were written is returned and an * error indication is returned in an output argument. * * Side effects: * Writes output on the actual channel. * *---------------------------------------------------------------------- */static intPipeOutputProc(instanceData, buf, toWrite, errorCode) ClientData instanceData; /* Pipe state. */ char *buf; /* The data buffer. */ int toWrite; /* How many bytes to write? */ int *errorCode; /* Where to store error code. */{ 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;}/* *---------------------------------------------------------------------- * * PipeEventProc -- * * This function is invoked by Tcl_ServiceEvent when a file event * reaches the front of the event queue. This procedure invokes * Tcl_NotifyChannel on the pipe. * * Results: * Returns 1 if the event was handled, meaning it should be removed * from the queue. Returns 0 if the event was not handled, meaning * it should stay on the queue. The only time the event isn't * handled is if the TCL_FILE_EVENTS flag bit isn't set. * * Side effects: * Whatever the notifier callback does. * *---------------------------------------------------------------------- */static intPipeEventProc(evPtr, flags) Tcl_Event *evPtr; /* Event to service. */ int flags; /* Flags that indicate what events to * handle, such as TCL_FILE_EVENTS. */{ PipeEvent *pipeEvPtr = (PipeEvent *)evPtr; PipeInfo *infoPtr; WinFile *filePtr; int mask;/* DWORD count;*/ if (!(flags & TCL_FILE_EVENTS)) { return 0; } /* * Search through the list of watched pipes for the one whose handle * matches the event. We do this rather than simply dereferencing * the handle in the event so that pipes can be deleted while the * event is in the queue. */ for (infoPtr = firstPipePtr; infoPtr != NULL; infoPtr = infoPtr->nextPtr) { if (pipeEvPtr->infoPtr == infoPtr) { infoPtr->flags &= ~(PIPE_PENDING); break; } } /* * Remove stale events. */ if (!infoPtr) { return 1; } /* * If we aren't on Win32s, check to see if the pipe is readable. Note * that we can't tell if a pipe is writable, so we always report it * as being writable. */ filePtr = (WinFile*) ((PipeInfo*)infoPtr)->readFile; if (filePtr->type != WIN32S_PIPE) { /* * On windows 95, PeekNamedPipe returns 0 on eof so we can't * distinguish underflow from eof. The correct solution is to * switch to the threaded implementation. */ mask = TCL_WRITABLE|TCL_READABLE;/* if (PeekNamedPipe(filePtr->handle, (LPVOID) NULL, (DWORD) 0, *//* (LPDWORD) NULL, &count, (LPDWORD) NULL) == TRUE) { *//* if (count != 0) { *//* mask |= TCL_READABLE; *//* } *//* } else { */ /* * If the pipe has been closed by the other side, then * mark the pipe as readable, but not writable. *//* if (GetLastError() == ERROR_BROKEN_PIPE) { *//* mask = TCL_READABLE; *//* } *//* } */ } else { mask = TCL_READABLE | TCL_WRITABLE; } /* * Inform the channel of the events. */ Tcl_NotifyChannel(infoPtr->channel, infoPtr->watchMask & mask); return 1;}/* *---------------------------------------------------------------------- * * PipeWatchProc -- * * Called by the notifier to set up to watch for events on this * channel. * * Results: * None. * * Side effects: * None. * *---------------------------------------------------------------------- */static voidPipeWatchProc(instanceData, mask) ClientData instanceData; /* Pipe state. */ int mask; /* What events to watch for; OR-ed * combination of TCL_READABLE, * TCL_WRITABLE and TCL_EXCEPTION. */{ PipeInfo **nextPtrPtr, *ptr; PipeInfo *infoPtr = (PipeInfo *) instanceData; int oldMask = infoPtr->watchMask; /* * For now, we just send a message to ourselves so we can poll the * channel for readable events. */ infoPtr->watchMask = mask & infoPtr->validMask; if (infoPtr->watchMask) { Tcl_Time blockTime = { 0, 0 }; if (!oldMask) { infoPtr->nextPtr = firstPipePtr; firstPipePtr = infoPtr; } Tcl_SetMaxBlockTime(&blockTime); } else { if (oldMask) { /* * Remove the pipe from the list of watched pipes. */ for (nextPtrPtr = &firstPipePtr, ptr = *nextPtrPtr; ptr != NULL; nextPtrPtr = &ptr->nextPtr, ptr = *nextPtrPtr) { if (infoPtr == ptr) { *nextPtrPtr = ptr->nextPtr; break; } } } }}/* *---------------------------------------------------------------------- * * PipeGetHandleProc -- * * Called from Tcl_GetChannelHandle to retrieve OS handles from * inside a command pipeline based channel. * * Results: * Returns TCL_OK with the fd in handlePtr, or TCL_ERROR if * there is no handle for the specified direction. * * Side effects: * None. * *---------------------------------------------------------------------- */static intPipeGetHandleProc(instanceData, direction, handlePtr) ClientData instanceData; /* The pipe state. */ int direction; /* TCL_READABLE or TCL_WRITABLE */ ClientData *handlePtr; /* Where to store the handle. */{ 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_WaitPid -- * * Emulates the waitpid system call. * * Results: * Returns 0 if the process is still alive, -1 on an error, or * the pid on a clean close. * * Side effects: * Unless WNOHANG is set and the wait times out, the process * in
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -