static inline void ResultAdd( ResultBuffer *r, /* The buffer to extend. */ unsigned char *buf, /* The buffer to read from. */ size_t toWrite) /* The number of bytes in 'buf'. */ { if (r->used + toWrite > r->allocated) { /* * Extension of the internal buffer is required. */ if (r->allocated == 0) { r->allocated = toWrite + INCREMENT; r->buf = UCHARP(ckalloc(r->allocated)); } else { r->allocated += toWrite + INCREMENT; r->buf = UCHARP(ckrealloc((char *) r->buf, r->allocated)); } } /* * Now we may copy the data. */ memcpy(r->buf + r->used, buf, toWrite); r->used += toWrite; }
static int TransformOutputProc( ClientData instanceData, const char *buf, int toWrite, int *errorCodePtr) { TransformChannelData *dataPtr = instanceData; /* * Should assert(dataPtr->mode & TCL_WRITABLE); */ if (toWrite == 0) { /* * Catch a no-op. */ return 0; } PreserveData(dataPtr); if (ExecuteCallback(dataPtr, NULL, A_WRITE, UCHARP(buf), toWrite, TRANSMIT_DOWN, P_NO_PRESERVE) != TCL_OK) { *errorCodePtr = EINVAL; toWrite = -1; } ReleaseData(dataPtr); return toWrite; }
static int TransformInputProc( ClientData instanceData, char *buf, int toRead, int *errorCodePtr) { TransformChannelData *dataPtr = instanceData; int gotBytes, read, copied; Tcl_Channel downChan; /* * Should assert(dataPtr->mode & TCL_READABLE); */ if (toRead == 0 || dataPtr->self == NULL) { /* * Catch a no-op. TODO: Is this a panic()? */ return 0; } gotBytes = 0; downChan = Tcl_GetStackedChannel(dataPtr->self); PreserveData(dataPtr); while (toRead > 0) { /* * Loop until the request is satisfied (or no data is available from * below, possibly EOF). */ copied = ResultCopy(&dataPtr->result, UCHARP(buf), toRead); toRead -= copied; buf += copied; gotBytes += copied; if (toRead == 0) { /* * The request was completely satisfied from our buffers. We can * break out of the loop and return to the caller. */ break; } /* * Length (dataPtr->result) == 0, toRead > 0 here. Use the incoming * 'buf'! as target to store the intermediary information read from * the underlying channel. * * Ask the tcl level how much data it allows us to read from the * underlying channel. This feature allows the transform to signal EOF * upstream although there is none downstream. Useful to control an * unbounded 'fcopy', either through counting bytes, or by pattern * matching. */ ExecuteCallback(dataPtr, NULL, A_QUERY_MAXREAD, NULL, 0, TRANSMIT_NUM /* -> maxRead */, P_PRESERVE); if (dataPtr->maxRead >= 0) { if (dataPtr->maxRead < toRead) { toRead = dataPtr->maxRead; } } /* else: 'maxRead < 0' == Accept the current value of toRead. */ if (toRead <= 0) { break; } if (dataPtr->eofPending) { /* * Already saw EOF from downChan; don't ask again. * NOTE: Could move this up to avoid the last maxRead * execution. Believe this would still be correct behavior, * but the test suite tests the whole command callback * sequence, so leave it unchanged for now. */ break; } /* * Get bytes from the underlying channel. */ read = Tcl_ReadRaw(downChan, buf, toRead); if (read < 0) { if (Tcl_InputBlocked(downChan) && (gotBytes > 0)) { /* * Zero bytes available from downChan because blocked. * But nonzero bytes already copied, so total is a * valid blocked short read. Return to caller. */ break; } /* * Either downChan is not blocked (there's a real error). * or it is and there are no bytes copied yet. In either * case we want to pass the "error" along to the caller, * either to report an error, or to signal to the caller * that zero bytes are available because blocked. */ *errorCodePtr = Tcl_GetErrno(); gotBytes = -1; break; } else if (read == 0) { /* * Zero returned from Tcl_ReadRaw() always indicates EOF * on the down channel. */ dataPtr->eofPending = 1; dataPtr->readIsFlushed = 1; ExecuteCallback(dataPtr, NULL, A_FLUSH_READ, NULL, 0, TRANSMIT_IBUF, P_PRESERVE); if (ResultEmpty(&dataPtr->result)) { /* * We had nothing to flush. */ break; } continue; /* at: while (toRead > 0) */ } /* read == 0 */ /* * Transform the read chunk and add the result to our read buffer * (dataPtr->result). */ if (ExecuteCallback(dataPtr, NULL, A_READ, UCHARP(buf), read, TRANSMIT_IBUF, P_PRESERVE) != TCL_OK) { *errorCodePtr = EINVAL; gotBytes = -1; break; } } /* while toRead > 0 */ if (gotBytes == 0) { dataPtr->eofPending = 0; } ReleaseData(dataPtr); return gotBytes; }
static int TransformInputProc( ClientData instanceData, char *buf, int toRead, int *errorCodePtr) { TransformChannelData *dataPtr = instanceData; int gotBytes, read, copied; Tcl_Channel downChan; /* * Should assert(dataPtr->mode & TCL_READABLE); */ if (toRead == 0) { /* * Catch a no-op. */ return 0; } gotBytes = 0; downChan = Tcl_GetStackedChannel(dataPtr->self); while (toRead > 0) { /* * Loop until the request is satisfied (or no data is available from * below, possibly EOF). */ copied = ResultCopy(&dataPtr->result, UCHARP(buf), toRead); toRead -= copied; buf += copied; gotBytes += copied; if (toRead == 0) { /* * The request was completely satisfied from our buffers. We can * break out of the loop and return to the caller. */ return gotBytes; } /* * Length (dataPtr->result) == 0, toRead > 0 here. Use the incoming * 'buf'! as target to store the intermediary information read from * the underlying channel. * * Ask the tcl level how much data it allows us to read from the * underlying channel. This feature allows the transform to signal EOF * upstream although there is none downstream. Useful to control an * unbounded 'fcopy', either through counting bytes, or by pattern * matching. */ ExecuteCallback(dataPtr, NULL, A_QUERY_MAXREAD, NULL, 0, TRANSMIT_NUM /* -> maxRead */, P_PRESERVE); if (dataPtr->maxRead >= 0) { if (dataPtr->maxRead < toRead) { toRead = dataPtr->maxRead; } } /* else: 'maxRead < 0' == Accept the current value of toRead. */ if (toRead <= 0) { return gotBytes; } /* * Get bytes from the underlying channel. */ read = Tcl_ReadRaw(downChan, buf, toRead); if (read < 0) { /* * Report errors to caller. EAGAIN is a special situation. If we * had some data before we report that instead of the request to * re-try. */ if ((Tcl_GetErrno() == EAGAIN) && (gotBytes > 0)) { return gotBytes; } *errorCodePtr = Tcl_GetErrno(); return -1; } else if (read == 0) { /* * Check wether we hit on EOF in the underlying channel or not. If * not differentiate between blocking and non-blocking modes. In * non-blocking mode we ran temporarily out of data. Signal this * to the caller via EWOULDBLOCK and error return (-1). In the * other cases we simply return what we got and let the caller * wait for more. On the other hand, if we got an EOF we have to * convert and flush all waiting partial data. */ if (!Tcl_Eof(downChan)) { if ((gotBytes == 0) && (dataPtr->flags & CHANNEL_ASYNC)) { *errorCodePtr = EWOULDBLOCK; return -1; } return gotBytes; } if (dataPtr->readIsFlushed) { /* * Already flushed, nothing to do anymore. */ return gotBytes; } dataPtr->readIsFlushed = 1; ExecuteCallback(dataPtr, NULL, A_FLUSH_READ, NULL, 0, TRANSMIT_IBUF, P_PRESERVE); if (ResultEmpty(&dataPtr->result)) { /* * We had nothing to flush. */ return gotBytes; } continue; /* at: while (toRead > 0) */ } /* read == 0 */ /* * Transform the read chunk and add the result to our read buffer * (dataPtr->result). */ if (ExecuteCallback(dataPtr, NULL, A_READ, UCHARP(buf), read, TRANSMIT_IBUF, P_PRESERVE) != TCL_OK) { *errorCodePtr = EINVAL; return -1; } } /* while toRead > 0 */ return gotBytes; }