tclio.c
来自「tcl是工具命令语言」· C语言 代码 · 共 2,110 行 · 第 1/5 页
C
2,110 行
* *---------------------------------------------------------------------- */intTcl_GetChannelMode(chan) Tcl_Channel chan; /* The channel for which the mode is * being computed. */{ ChannelState *statePtr = ((Channel *) chan)->state; /* State of actual channel. */ return (statePtr->flags & (TCL_READABLE | TCL_WRITABLE));}/* *---------------------------------------------------------------------- * * Tcl_GetChannelName -- * * Returns the string identifying the channel name. * * Results: * The string containing the channel name. This memory is * owned by the generic layer and should not be modified by * the caller. * * Side effects: * None. * *---------------------------------------------------------------------- */CONST char *Tcl_GetChannelName(chan) Tcl_Channel chan; /* The channel for which to return the name. */{ ChannelState *statePtr; /* State of actual channel. */ statePtr = ((Channel *) chan)->state; return statePtr->channelName;}/* *---------------------------------------------------------------------- * * Tcl_GetChannelHandle -- * * Returns an OS handle associated with a channel. * * Results: * Returns TCL_OK and places the handle in handlePtr, or returns * TCL_ERROR on failure. * * Side effects: * None. * *---------------------------------------------------------------------- */intTcl_GetChannelHandle(chan, direction, handlePtr) Tcl_Channel chan; /* The channel to get file from. */ int direction; /* TCL_WRITABLE or TCL_READABLE. */ ClientData *handlePtr; /* Where to store handle */{ Channel *chanPtr; /* The actual channel. */ ClientData handle; int result; chanPtr = ((Channel *) chan)->state->bottomChanPtr; result = (chanPtr->typePtr->getHandleProc)(chanPtr->instanceData, direction, &handle); if (handlePtr) { *handlePtr = handle; } return result;}/* *--------------------------------------------------------------------------- * * AllocChannelBuffer -- * * A channel buffer has BUFFER_PADDING bytes extra at beginning to * hold any bytes of a native-encoding character that got split by * the end of the previous buffer and need to be moved to the * beginning of the next buffer to make a contiguous string so it * can be converted to UTF-8. * * A channel buffer has BUFFER_PADDING bytes extra at the end to * hold any bytes of a native-encoding character (generated from a * UTF-8 character) that overflow past the end of the buffer and * need to be moved to the next buffer. * * Results: * A newly allocated channel buffer. * * Side effects: * None. * *--------------------------------------------------------------------------- */static ChannelBuffer *AllocChannelBuffer(length) int length; /* Desired length of channel buffer. */{ ChannelBuffer *bufPtr; int n; n = length + CHANNELBUFFER_HEADER_SIZE + BUFFER_PADDING + BUFFER_PADDING; bufPtr = (ChannelBuffer *) ckalloc((unsigned) n); bufPtr->nextAdded = BUFFER_PADDING; bufPtr->nextRemoved = BUFFER_PADDING; bufPtr->bufLength = length + BUFFER_PADDING; bufPtr->nextPtr = (ChannelBuffer *) NULL; return bufPtr;}/* *---------------------------------------------------------------------- * * RecycleBuffer -- * * Helper function to recycle input and output buffers. Ensures * that two input buffers are saved (one in the input queue and * another in the saveInBufPtr field) and that curOutPtr is set * to a buffer. Only if these conditions are met is the buffer * freed to the OS. * * Results: * None. * * Side effects: * May free a buffer to the OS. * *---------------------------------------------------------------------- */static voidRecycleBuffer(statePtr, bufPtr, mustDiscard) ChannelState *statePtr; /* ChannelState in which to recycle buffers. */ ChannelBuffer *bufPtr; /* The buffer to recycle. */ int mustDiscard; /* If nonzero, free the buffer to the * OS, always. */{ /* * Do we have to free the buffer to the OS? */ if (mustDiscard) { ckfree((char *) bufPtr); return; } /* * Only save buffers which are at least as big as the requested * buffersize for the channel. This is to honor dynamic changes * of the buffersize made by the user. */ if ((bufPtr->bufLength - BUFFER_PADDING) < statePtr->bufSize) { ckfree((char *) bufPtr); return; } /* * Only save buffers for the input queue if the channel is readable. */ if (statePtr->flags & TCL_READABLE) { if (statePtr->inQueueHead == (ChannelBuffer *) NULL) { statePtr->inQueueHead = bufPtr; statePtr->inQueueTail = bufPtr; goto keepit; } if (statePtr->saveInBufPtr == (ChannelBuffer *) NULL) { statePtr->saveInBufPtr = bufPtr; goto keepit; } } /* * Only save buffers for the output queue if the channel is writable. */ if (statePtr->flags & TCL_WRITABLE) { if (statePtr->curOutPtr == (ChannelBuffer *) NULL) { statePtr->curOutPtr = bufPtr; goto keepit; } } /* * If we reached this code we return the buffer to the OS. */ ckfree((char *) bufPtr); return; keepit: bufPtr->nextRemoved = BUFFER_PADDING; bufPtr->nextAdded = BUFFER_PADDING; bufPtr->nextPtr = (ChannelBuffer *) NULL;}/* *---------------------------------------------------------------------- * * DiscardOutputQueued -- * * Discards all output queued in the output queue of a channel. * * Results: * None. * * Side effects: * Recycles buffers. * *---------------------------------------------------------------------- */static voidDiscardOutputQueued(statePtr) ChannelState *statePtr; /* ChannelState for which to discard output. */{ ChannelBuffer *bufPtr; while (statePtr->outQueueHead != (ChannelBuffer *) NULL) { bufPtr = statePtr->outQueueHead; statePtr->outQueueHead = bufPtr->nextPtr; RecycleBuffer(statePtr, bufPtr, 0); } statePtr->outQueueHead = (ChannelBuffer *) NULL; statePtr->outQueueTail = (ChannelBuffer *) NULL;}/* *---------------------------------------------------------------------- * * CheckForDeadChannel -- * * This function checks is a given channel is Dead. * (A channel that has been closed but not yet deallocated.) * * Results: * True (1) if channel is Dead, False (0) if channel is Ok * * Side effects: * None * *---------------------------------------------------------------------- */static intCheckForDeadChannel(interp, statePtr) Tcl_Interp *interp; /* For error reporting (can be NULL) */ ChannelState *statePtr; /* The channel state to check. */{ if (statePtr->flags & CHANNEL_DEAD) { Tcl_SetErrno(EINVAL); if (interp) { Tcl_AppendResult(interp, "unable to access channel: invalid channel", (char *) NULL); } return 1; } return 0;}/* *---------------------------------------------------------------------- * * FlushChannel -- * * This function flushes as much of the queued output as is possible * now. If calledFromAsyncFlush is nonzero, it is being called in an * event handler to flush channel output asynchronously. * * Results: * 0 if successful, else the error code that was returned by the * channel type operation. * * Side effects: * May produce output on a channel. May block indefinitely if the * channel is synchronous. May schedule an async flush on the channel. * May recycle memory for buffers in the output queue. * *---------------------------------------------------------------------- */static intFlushChannel(interp, chanPtr, calledFromAsyncFlush) Tcl_Interp *interp; /* For error reporting during close. */ Channel *chanPtr; /* The channel to flush on. */ int calledFromAsyncFlush; /* If nonzero then we are being * called from an asynchronous * flush callback. */{ ChannelState *statePtr = chanPtr->state; /* State of the channel stack. */ ChannelBuffer *bufPtr; /* Iterates over buffered output * queue. */ int toWrite; /* Amount of output data in current * buffer available to be written. */ int written; /* Amount of output data actually * written in current round. */ int errorCode = 0; /* Stores POSIX error codes from * channel driver operations. */ int wroteSome = 0; /* Set to one if any data was * written to the driver. */ /* * Prevent writing on a dead channel -- a channel that has been closed * but not yet deallocated. This can occur if the exit handler for the * channel deallocation runs before all channels are deregistered in * all interpreters. */ if (CheckForDeadChannel(interp, statePtr)) return -1; /* * Loop over the queued buffers and attempt to flush as * much as possible of the queued output to the channel. */ while (1) { /* * If the queue is empty and there is a ready current buffer, OR if * the current buffer is full, then move the current buffer to the * queue. */ if (((statePtr->curOutPtr != (ChannelBuffer *) NULL) && (statePtr->curOutPtr->nextAdded == statePtr->curOutPtr->bufLength)) || ((statePtr->flags & BUFFER_READY) && (statePtr->outQueueHead == (ChannelBuffer *) NULL))) { statePtr->flags &= (~(BUFFER_READY)); statePtr->curOutPtr->nextPtr = (ChannelBuffer *) NULL; if (statePtr->outQueueHead == (ChannelBuffer *) NULL) { statePtr->outQueueHead = statePtr->curOutPtr; } else { statePtr->outQueueTail->nextPtr = statePtr->curOutPtr; } statePtr->outQueueTail = statePtr->curOutPtr; statePtr->curOutPtr = (ChannelBuffer *) NULL; } bufPtr = statePtr->outQueueHead; /* * If we are not being called from an async flush and an async * flush is active, we just return without producing any output. */ if ((!calledFromAsyncFlush) && (statePtr->flags & BG_FLUSH_SCHEDULED)) { return 0; } /* * If the output queue is still empty, break out of the while loop. */ if (bufPtr == (ChannelBuffer *) NULL) { break; /* Out of the "while (1)". */ } /* * Produce the output on the channel. */ toWrite = bufPtr->nextAdded - bufPtr->nextRemoved; written = (chanPtr->typePtr->outputProc) (chanPtr->instanceData, bufPtr->buf + bufPtr->nextRemoved, toWrite, &errorCode); /* * If the write failed completely attempt to start the asynchronous * flush mechanism and break out of this loop - do not attempt to * write any more output at this time. */ if (written < 0) { /* * If the last attempt to write was interrupted, simply retry. */ if (errorCode == EINTR) { errorCode = 0; continue; } /* * If the channel is non-blocking and we would have blocked, * start a background flushing handler and break out of the loop. */ if ((errorCode == EWOULDBLOCK) || (errorCode == EAGAIN)) { /* * This used to check for CHANNEL_NONBLOCKING, and panic * if the channel was blocking. However, it appears * that setting stdin to -blocking 0 has some effect on * the stdout when it's a tty channel (dup'ed underneath) */ if (!(statePtr->flags & BG_FLUSH_SCHEDULED)) { statePtr->flags |= BG_FLUSH_SCHEDULED; UpdateInterest(chanPtr); } errorCode = 0; break; } /* * Decide whether to report the error upwards or defer it. */ if (calledFromAsyncFlush) { if (statePtr->unreportedError =
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?