tclIO.c
上传用户:rrhhcc
上传日期:2015-12-11
资源大小:54129k
文件大小:273k
- endEncoding = ((statePtr->outputEncodingFlags & TCL_ENCODING_END) != 0);
- /*
- * Loop over all UTF-8 characters in src, storing them in staging buffer
- * with proper EOL translation.
- */
- consumedSomething = 1;
- while (consumedSomething && (srcLen + savedLF + endEncoding > 0)) {
- consumedSomething = 0;
- stage = statePtr->outputStage;
- stageMax = statePtr->bufSize;
- stageLen = stageMax;
- toWrite = stageLen;
- if (toWrite > srcLen) {
- toWrite = srcLen;
- }
- if (savedLF) {
- /*
- * A 'n' was left over from last call to TranslateOutputEOL()
- * and we need to store it in the staging buffer. If the
- * channel is line-based, we will need to flush the output
- * buffer (after translating the staging buffer).
- */
-
- *stage++ = 'n';
- stageLen--;
- sawLF++;
- }
- sawLF += TranslateOutputEOL(statePtr, stage, src, &stageLen, &toWrite);
- stage -= savedLF;
- stageLen += savedLF;
- savedLF = 0;
- if (stageLen > stageMax) {
- savedLF = 1;
- stageLen = stageMax;
- }
- src += toWrite;
- srcLen -= toWrite;
- /*
- * Loop over all UTF-8 characters in staging buffer, converting them
- * to external encoding, storing them in output buffer.
- */
- while (stageLen + saved + endEncoding > 0) {
- bufPtr = statePtr->curOutPtr;
- if (bufPtr == NULL) {
- bufPtr = AllocChannelBuffer(statePtr->bufSize);
- statePtr->curOutPtr = bufPtr;
- }
- dst = bufPtr->buf + bufPtr->nextAdded;
- dstLen = bufPtr->bufLength - bufPtr->nextAdded;
- if (saved != 0) {
- /*
- * Here's some translated bytes left over from the last
- * buffer that we need to stick at the beginning of this
- * buffer.
- */
-
- memcpy((VOID *) dst, (VOID *) safe, (size_t) saved);
- bufPtr->nextAdded += saved;
- dst += saved;
- dstLen -= saved;
- saved = 0;
- }
- result = Tcl_UtfToExternal(NULL, encoding, stage, stageLen,
- statePtr->outputEncodingFlags,
- &statePtr->outputEncodingState, dst,
- dstLen + BUFFER_PADDING, &stageRead, &dstWrote, NULL);
- /* Fix for SF #506297, reported by Martin Forssen
- * <ruric@users.sourceforge.net>.
- *
- * The encoding chosen in the script exposing the bug writes out
- * three intro characters when TCL_ENCODING_START is set, but does
- * not consume any input as TCL_ENCODING_END is cleared. As some
- * output was generated the enclosing loop calls UtfToExternal
- * again, again with START set. Three more characters in the out
- * and still no use of input ... To break this infinite loop we
- * remove TCL_ENCODING_START from the set of flags after the first
- * call (no condition is required, the later calls remove an unset
- * flag, which is a no-op). This causes the subsequent calls to
- * UtfToExternal to consume and convert the actual input.
- */
- statePtr->outputEncodingFlags &= ~TCL_ENCODING_START;
- /*
- * The following code must be executed only when result is not 0.
- */
- if (result && ((stageRead + dstWrote) == 0)) {
- /*
- * We have an incomplete UTF-8 character at the end of the
- * staging buffer. It will get moved to the beginning of the
- * staging buffer followed by more bytes from src.
- */
- src -= stageLen;
- srcLen += stageLen;
- stageLen = 0;
- savedLF = 0;
- break;
- }
- bufPtr->nextAdded += dstWrote;
- if (bufPtr->nextAdded > bufPtr->bufLength) {
- /*
- * When translating from UTF-8 to external encoding, we
- * allowed the translation to produce a character that
- * crossed the end of the output buffer, so that we would
- * get a completely full buffer before flushing it. The
- * extra bytes will be moved to the beginning of the next
- * buffer.
- */
- saved = bufPtr->nextAdded - bufPtr->bufLength;
- memcpy((VOID *) safe, (VOID *) (dst + dstLen), (size_t) saved);
- bufPtr->nextAdded = bufPtr->bufLength;
- }
- if (CheckFlush(chanPtr, bufPtr, sawLF) != 0) {
- return -1;
- }
- total += dstWrote;
- stage += stageRead;
- stageLen -= stageRead;
- sawLF = 0;
- consumedSomething = 1;
- /*
- * If all translated characters are written to the buffer,
- * endEncoding is set to 0 because the escape sequence may be
- * output.
- */
- if ((stageLen + saved == 0) && (result == 0)) {
- endEncoding = 0;
- }
- }
- }
- /* If nothing was written and it happened because there was no progress
- * in the UTF conversion, we throw an error.
- */
- if (!consumedSomething && (total == 0)) {
- Tcl_SetErrno (EINVAL);
- return -1;
- }
- return total;
- }
- /*
- *---------------------------------------------------------------------------
- *
- * TranslateOutputEOL --
- *
- * Helper function for WriteBytes() and WriteChars(). Converts the
- * 'n' characters in the source buffer into the appropriate EOL
- * form specified by the output translation mode.
- *
- * EOL translation stops either when the source buffer is empty
- * or the output buffer is full.
- *
- * When converting to CRLF mode and there is only 1 byte left in
- * the output buffer, this routine stores the 'r' in the last
- * byte and then stores the 'n' in the byte just past the end of the
- * buffer. The caller is responsible for passing in a buffer that
- * is large enough to hold the extra byte.
- *
- * Results:
- * The return value is 1 if a 'n' was translated from the source
- * buffer, or 0 otherwise -- this can be used by the caller to
- * decide to flush a line-based channel even though the channel
- * buffer is not full.
- *
- * *dstLenPtr is filled with how many bytes of the output buffer
- * were used. As mentioned above, this can be one more that
- * the output buffer's specified length if a CRLF was stored.
- *
- * *srcLenPtr is filled with how many bytes of the source buffer
- * were consumed.
- *
- * Side effects:
- * It may be obvious, but bears mentioning that when converting
- * in CRLF mode (which requires two bytes of storage in the output
- * buffer), the number of bytes consumed from the source buffer
- * will be less than the number of bytes stored in the output buffer.
- *
- *---------------------------------------------------------------------------
- */
- static int
- TranslateOutputEOL(statePtr, dst, src, dstLenPtr, srcLenPtr)
- ChannelState *statePtr; /* Channel being read, for translation and
- * buffering modes. */
- char *dst; /* Output buffer filled with UTF-8 chars by
- * applying appropriate EOL translation to
- * source characters. */
- CONST char *src; /* Source UTF-8 characters. */
- int *dstLenPtr; /* On entry, the maximum length of output
- * buffer in bytes. On exit, the number of
- * bytes actually used in output buffer. */
- int *srcLenPtr; /* On entry, the length of source buffer.
- * On exit, the number of bytes read from
- * the source buffer. */
- {
- char *dstEnd;
- int srcLen, newlineFound;
-
- newlineFound = 0;
- srcLen = *srcLenPtr;
- switch (statePtr->outputTranslation) {
- case TCL_TRANSLATE_LF: {
- for (dstEnd = dst + srcLen; dst < dstEnd; ) {
- if (*src == 'n') {
- newlineFound = 1;
- }
- *dst++ = *src++;
- }
- *dstLenPtr = srcLen;
- break;
- }
- case TCL_TRANSLATE_CR: {
- for (dstEnd = dst + srcLen; dst < dstEnd;) {
- if (*src == 'n') {
- *dst++ = 'r';
- newlineFound = 1;
- src++;
- } else {
- *dst++ = *src++;
- }
- }
- *dstLenPtr = srcLen;
- break;
- }
- case TCL_TRANSLATE_CRLF: {
- /*
- * Since this causes the number of bytes to grow, we
- * start off trying to put 'srcLen' bytes into the
- * output buffer, but allow it to store more bytes, as
- * long as there's still source bytes and room in the
- * output buffer.
- */
- char *dstStart, *dstMax;
- CONST char *srcStart;
-
- dstStart = dst;
- dstMax = dst + *dstLenPtr;
- srcStart = src;
-
- if (srcLen < *dstLenPtr) {
- dstEnd = dst + srcLen;
- } else {
- dstEnd = dst + *dstLenPtr;
- }
- while (dst < dstEnd) {
- if (*src == 'n') {
- if (dstEnd < dstMax) {
- dstEnd++;
- }
- *dst++ = 'r';
- newlineFound = 1;
- }
- *dst++ = *src++;
- }
- *srcLenPtr = src - srcStart;
- *dstLenPtr = dst - dstStart;
- break;
- }
- default: {
- break;
- }
- }
- return newlineFound;
- }
- /*
- *---------------------------------------------------------------------------
- *
- * CheckFlush --
- *
- * Helper function for WriteBytes() and WriteChars(). If the
- * channel buffer is ready to be flushed, flush it.
- *
- * Results:
- * The return value is -1 if there was a problem flushing the
- * channel buffer, or 0 otherwise.
- *
- * Side effects:
- * The buffer will be recycled if it is flushed.
- *
- *---------------------------------------------------------------------------
- */
- static int
- CheckFlush(chanPtr, bufPtr, newlineFlag)
- Channel *chanPtr; /* Channel being read, for buffering mode. */
- ChannelBuffer *bufPtr; /* Channel buffer to possibly flush. */
- int newlineFlag; /* Non-zero if a the channel buffer
- * contains a newline. */
- {
- ChannelState *statePtr = chanPtr->state; /* state info for channel */
- /*
- * The current buffer is ready for output:
- * 1. if it is full.
- * 2. if it contains a newline and this channel is line-buffered.
- * 3. if it contains any output and this channel is unbuffered.
- */
- if ((statePtr->flags & BUFFER_READY) == 0) {
- if (bufPtr->nextAdded == bufPtr->bufLength) {
- statePtr->flags |= BUFFER_READY;
- } else if (statePtr->flags & CHANNEL_LINEBUFFERED) {
- if (newlineFlag != 0) {
- statePtr->flags |= BUFFER_READY;
- }
- } else if (statePtr->flags & CHANNEL_UNBUFFERED) {
- statePtr->flags |= BUFFER_READY;
- }
- }
- if (statePtr->flags & BUFFER_READY) {
- if (FlushChannel(NULL, chanPtr, 0) != 0) {
- return -1;
- }
- }
- return 0;
- }
- /*
- *---------------------------------------------------------------------------
- *
- * Tcl_Gets --
- *
- * Reads a complete line of input from the channel into a Tcl_DString.
- *
- * Results:
- * Length of line read (in characters) or -1 if error, EOF, or blocked.
- * If -1, use Tcl_GetErrno() to retrieve the POSIX error code for the
- * error or condition that occurred.
- *
- * Side effects:
- * May flush output on the channel. May cause input to be consumed
- * from the channel.
- *
- *---------------------------------------------------------------------------
- */
- int
- Tcl_Gets(chan, lineRead)
- Tcl_Channel chan; /* Channel from which to read. */
- Tcl_DString *lineRead; /* The line read will be appended to this
- * DString as UTF-8 characters. The caller
- * must have initialized it and is responsible
- * for managing the storage. */
- {
- Tcl_Obj *objPtr;
- int charsStored, length;
- char *string;
- objPtr = Tcl_NewObj();
- charsStored = Tcl_GetsObj(chan, objPtr);
- if (charsStored > 0) {
- string = Tcl_GetStringFromObj(objPtr, &length);
- Tcl_DStringAppend(lineRead, string, length);
- }
- Tcl_DecrRefCount(objPtr);
- return charsStored;
- }
- /*
- *---------------------------------------------------------------------------
- *
- * Tcl_GetsObj --
- *
- * Accumulate input from the input channel until end-of-line or
- * end-of-file has been seen. Bytes read from the input channel
- * are converted to UTF-8 using the encoding specified by the
- * channel.
- *
- * Results:
- * Number of characters accumulated in the object or -1 if error,
- * blocked, or EOF. If -1, use Tcl_GetErrno() to retrieve the
- * POSIX error code for the error or condition that occurred.
- *
- * Side effects:
- * Consumes input from the channel.
- *
- * On reading EOF, leave channel pointing at EOF char.
- * On reading EOL, leave channel pointing after EOL, but don't
- * return EOL in dst buffer.
- *
- *---------------------------------------------------------------------------
- */
- int
- Tcl_GetsObj(chan, objPtr)
- Tcl_Channel chan; /* Channel from which to read. */
- Tcl_Obj *objPtr; /* The line read will be appended to this
- * object as UTF-8 characters. */
- {
- GetsState gs;
- Channel *chanPtr = (Channel *) chan;
- ChannelState *statePtr = chanPtr->state; /* state info for channel */
- ChannelBuffer *bufPtr;
- int inEofChar, skip, copiedTotal, oldLength, oldFlags, oldRemoved;
- Tcl_Encoding encoding;
- char *dst, *dstEnd, *eol, *eof;
- Tcl_EncodingState oldState;
- /*
- * This operation should occur at the top of a channel stack.
- */
- chanPtr = statePtr->topChanPtr;
- if (CheckChannelErrors(statePtr, TCL_READABLE) != 0) {
- copiedTotal = -1;
- goto done;
- }
- bufPtr = statePtr->inQueueHead;
- encoding = statePtr->encoding;
- /*
- * Preserved so we can restore the channel's state in case we don't
- * find a newline in the available input.
- */
- Tcl_GetStringFromObj(objPtr, &oldLength);
- oldFlags = statePtr->inputEncodingFlags;
- oldState = statePtr->inputEncodingState;
- oldRemoved = BUFFER_PADDING;
- if (bufPtr != NULL) {
- oldRemoved = bufPtr->nextRemoved;
- }
- /*
- * If there is no encoding, use "iso8859-1" -- Tcl_GetsObj() doesn't
- * produce ByteArray objects. To avoid circularity problems,
- * "iso8859-1" is builtin to Tcl.
- */
- if (encoding == NULL) {
- encoding = Tcl_GetEncoding(NULL, "iso8859-1");
- }
- /*
- * Object used by FilterInputBytes to keep track of how much data has
- * been consumed from the channel buffers.
- */
- gs.objPtr = objPtr;
- gs.dstPtr = &dst;
- gs.encoding = encoding;
- gs.bufPtr = bufPtr;
- gs.state = oldState;
- gs.rawRead = 0;
- gs.bytesWrote = 0;
- gs.charsWrote = 0;
- gs.totalChars = 0;
- dst = objPtr->bytes + oldLength;
- dstEnd = dst;
- skip = 0;
- eof = NULL;
- inEofChar = statePtr->inEofChar;
- while (1) {
- if (dst >= dstEnd) {
- if (FilterInputBytes(chanPtr, &gs) != 0) {
- goto restore;
- }
- dstEnd = dst + gs.bytesWrote;
- }
-
- /*
- * Remember if EOF char is seen, then look for EOL anyhow, because
- * the EOL might be before the EOF char.
- */
- if (inEofChar != ' ') {
- for (eol = dst; eol < dstEnd; eol++) {
- if (*eol == inEofChar) {
- dstEnd = eol;
- eof = eol;
- break;
- }
- }
- }
- /*
- * On EOL, leave current file position pointing after the EOL, but
- * don't store the EOL in the output string.
- */
- switch (statePtr->inputTranslation) {
- case TCL_TRANSLATE_LF: {
- for (eol = dst; eol < dstEnd; eol++) {
- if (*eol == 'n') {
- skip = 1;
- goto goteol;
- }
- }
- break;
- }
- case TCL_TRANSLATE_CR: {
- for (eol = dst; eol < dstEnd; eol++) {
- if (*eol == 'r') {
- skip = 1;
- goto goteol;
- }
- }
- break;
- }
- case TCL_TRANSLATE_CRLF: {
- for (eol = dst; eol < dstEnd; eol++) {
- if (*eol == 'r') {
- eol++;
- if (eol >= dstEnd) {
- int offset;
-
- offset = eol - objPtr->bytes;
- dst = dstEnd;
- if (FilterInputBytes(chanPtr, &gs) != 0) {
- goto restore;
- }
- dstEnd = dst + gs.bytesWrote;
- eol = objPtr->bytes + offset;
- if (eol >= dstEnd) {
- skip = 0;
- goto goteol;
- }
- }
- if (*eol == 'n') {
- eol--;
- skip = 2;
- goto goteol;
- }
- }
- }
- break;
- }
- case TCL_TRANSLATE_AUTO: {
- eol = dst;
- skip = 1;
- if (statePtr->flags & INPUT_SAW_CR) {
- statePtr->flags &= ~INPUT_SAW_CR;
- if (*eol == 'n') {
- /*
- * Skip the raw bytes that make up the 'n'.
- */
- char tmp[1 + TCL_UTF_MAX];
- int rawRead;
- bufPtr = gs.bufPtr;
- Tcl_ExternalToUtf(NULL, gs.encoding,
- bufPtr->buf + bufPtr->nextRemoved,
- gs.rawRead, statePtr->inputEncodingFlags,
- &gs.state, tmp, 1 + TCL_UTF_MAX, &rawRead,
- NULL, NULL);
- bufPtr->nextRemoved += rawRead;
- gs.rawRead -= rawRead;
- gs.bytesWrote--;
- gs.charsWrote--;
- memmove(dst, dst + 1, (size_t) (dstEnd - dst));
- dstEnd--;
- }
- }
- for (eol = dst; eol < dstEnd; eol++) {
- if (*eol == 'r') {
- eol++;
- if (eol == dstEnd) {
- /*
- * If buffer ended on r, peek ahead to see if a
- * n is available.
- */
- int offset;
-
- offset = eol - objPtr->bytes;
- dst = dstEnd;
- PeekAhead(chanPtr, &dstEnd, &gs);
- eol = objPtr->bytes + offset;
- if (eol >= dstEnd) {
- eol--;
- statePtr->flags |= INPUT_SAW_CR;
- goto goteol;
- }
- }
- if (*eol == 'n') {
- skip++;
- }
- eol--;
- goto goteol;
- } else if (*eol == 'n') {
- goto goteol;
- }
- }
- }
- }
- if (eof != NULL) {
- /*
- * EOF character was seen. On EOF, leave current file position
- * pointing at the EOF character, but don't store the EOF
- * character in the output string.
- */
- dstEnd = eof;
- statePtr->flags |= (CHANNEL_EOF | CHANNEL_STICKY_EOF);
- statePtr->inputEncodingFlags |= TCL_ENCODING_END;
- }
- if (statePtr->flags & CHANNEL_EOF) {
- skip = 0;
- eol = dstEnd;
- if (eol == objPtr->bytes + oldLength) {
- /*
- * If we didn't append any bytes before encountering EOF,
- * caller needs to see -1.
- */
- Tcl_SetObjLength(objPtr, oldLength);
- CommonGetsCleanup(chanPtr, encoding);
- copiedTotal = -1;
- goto done;
- }
- goto goteol;
- }
- dst = dstEnd;
- }
- /*
- * Found EOL or EOF, but the output buffer may now contain too many
- * UTF-8 characters. We need to know how many raw bytes correspond to
- * the number of UTF-8 characters we want, plus how many raw bytes
- * correspond to the character(s) making up EOL (if any), so we can
- * remove the correct number of bytes from the channel buffer.
- */
-
- goteol:
- bufPtr = gs.bufPtr;
- statePtr->inputEncodingState = gs.state;
- Tcl_ExternalToUtf(NULL, gs.encoding, bufPtr->buf + bufPtr->nextRemoved,
- gs.rawRead, statePtr->inputEncodingFlags,
- &statePtr->inputEncodingState, dst,
- eol - dst + skip + TCL_UTF_MAX, &gs.rawRead, NULL,
- &gs.charsWrote);
- bufPtr->nextRemoved += gs.rawRead;
- /*
- * Recycle all the emptied buffers.
- */
- Tcl_SetObjLength(objPtr, eol - objPtr->bytes);
- CommonGetsCleanup(chanPtr, encoding);
- statePtr->flags &= ~CHANNEL_BLOCKED;
- copiedTotal = gs.totalChars + gs.charsWrote - skip;
- goto done;
- /*
- * Couldn't get a complete line. This only happens if we get a error
- * reading from the channel or we are non-blocking and there wasn't
- * an EOL or EOF in the data available.
- */
- restore:
- bufPtr = statePtr->inQueueHead;
- bufPtr->nextRemoved = oldRemoved;
- for (bufPtr = bufPtr->nextPtr; bufPtr != NULL; bufPtr = bufPtr->nextPtr) {
- bufPtr->nextRemoved = BUFFER_PADDING;
- }
- CommonGetsCleanup(chanPtr, encoding);
- statePtr->inputEncodingState = oldState;
- statePtr->inputEncodingFlags = oldFlags;
- Tcl_SetObjLength(objPtr, oldLength);
- /*
- * We didn't get a complete line so we need to indicate to UpdateInterest
- * that the gets blocked. It will wait for more data instead of firing
- * a timer, avoiding a busy wait. This is where we are assuming that the
- * next operation is a gets. No more file events will be delivered on
- * this channel until new data arrives or some operation is performed
- * on the channel (e.g. gets, read, fconfigure) that changes the blocking
- * state. Note that this means a file event will not be delivered even
- * though a read would be able to consume the buffered data.
- */
- statePtr->flags |= CHANNEL_NEED_MORE_DATA;
- copiedTotal = -1;
- done:
- /*
- * Update the notifier state so we don't block while there is still
- * data in the buffers.
- */
- UpdateInterest(chanPtr);
- return copiedTotal;
- }
- /*
- *---------------------------------------------------------------------------
- *
- * FilterInputBytes --
- *
- * Helper function for Tcl_GetsObj. Produces UTF-8 characters from
- * raw bytes read from the channel.
- *
- * Consumes available bytes from channel buffers. When channel
- * buffers are exhausted, reads more bytes from channel device into
- * a new channel buffer. It is the caller's responsibility to
- * free the channel buffers that have been exhausted.
- *
- * Results:
- * The return value is -1 if there was an error reading from the
- * channel, 0 otherwise.
- *
- * Side effects:
- * Status object keeps track of how much data from channel buffers
- * has been consumed and where UTF-8 bytes should be stored.
- *
- *---------------------------------------------------------------------------
- */
-
- static int
- FilterInputBytes(chanPtr, gsPtr)
- Channel *chanPtr; /* Channel to read. */
- GetsState *gsPtr; /* Current state of gets operation. */
- {
- ChannelState *statePtr = chanPtr->state; /* state info for channel */
- ChannelBuffer *bufPtr;
- char *raw, *rawStart, *rawEnd;
- char *dst;
- int offset, toRead, dstNeeded, spaceLeft, result, rawLen, length;
- Tcl_Obj *objPtr;
- #define ENCODING_LINESIZE 20 /* Lower bound on how many bytes to convert
- * at a time. Since we don't know a priori
- * how many bytes of storage this many source
- * bytes will use, we actually need at least
- * ENCODING_LINESIZE * TCL_MAX_UTF bytes of
- * room. */
- objPtr = gsPtr->objPtr;
- /*
- * Subtract the number of bytes that were removed from channel buffer
- * during last call.
- */
- bufPtr = gsPtr->bufPtr;
- if (bufPtr != NULL) {
- bufPtr->nextRemoved += gsPtr->rawRead;
- if (bufPtr->nextRemoved >= bufPtr->nextAdded) {
- bufPtr = bufPtr->nextPtr;
- }
- }
- gsPtr->totalChars += gsPtr->charsWrote;
- if ((bufPtr == NULL) || (bufPtr->nextAdded == BUFFER_PADDING)) {
- /*
- * All channel buffers were exhausted and the caller still hasn't
- * seen EOL. Need to read more bytes from the channel device.
- * Side effect is to allocate another channel buffer.
- */
- read:
- if (statePtr->flags & CHANNEL_BLOCKED) {
- if (statePtr->flags & CHANNEL_NONBLOCKING) {
- gsPtr->charsWrote = 0;
- gsPtr->rawRead = 0;
- return -1;
- }
- statePtr->flags &= ~CHANNEL_BLOCKED;
- }
- if (GetInput(chanPtr) != 0) {
- gsPtr->charsWrote = 0;
- gsPtr->rawRead = 0;
- return -1;
- }
- bufPtr = statePtr->inQueueTail;
- gsPtr->bufPtr = bufPtr;
- }
- /*
- * Convert some of the bytes from the channel buffer to UTF-8. Space in
- * objPtr's string rep is used to hold the UTF-8 characters. Grow the
- * string rep if we need more space.
- */
- rawStart = bufPtr->buf + bufPtr->nextRemoved;
- raw = rawStart;
- rawEnd = bufPtr->buf + bufPtr->nextAdded;
- rawLen = rawEnd - rawStart;
- dst = *gsPtr->dstPtr;
- offset = dst - objPtr->bytes;
- toRead = ENCODING_LINESIZE;
- if (toRead > rawLen) {
- toRead = rawLen;
- }
- dstNeeded = toRead * TCL_UTF_MAX + 1;
- spaceLeft = objPtr->length - offset - TCL_UTF_MAX - 1;
- if (dstNeeded > spaceLeft) {
- length = offset * 2;
- if (offset < dstNeeded) {
- length = offset + dstNeeded;
- }
- length += TCL_UTF_MAX + 1;
- Tcl_SetObjLength(objPtr, length);
- spaceLeft = length - offset;
- dst = objPtr->bytes + offset;
- *gsPtr->dstPtr = dst;
- }
- gsPtr->state = statePtr->inputEncodingState;
- result = Tcl_ExternalToUtf(NULL, gsPtr->encoding, raw, rawLen,
- statePtr->inputEncodingFlags, &statePtr->inputEncodingState,
- dst, spaceLeft, &gsPtr->rawRead, &gsPtr->bytesWrote,
- &gsPtr->charsWrote);
- /*
- * Make sure that if we go through 'gets', that we reset the
- * TCL_ENCODING_START flag still. [Bug #523988]
- */
- statePtr->inputEncodingFlags &= ~TCL_ENCODING_START;
- if (result == TCL_CONVERT_MULTIBYTE) {
- /*
- * The last few bytes in this channel buffer were the start of a
- * multibyte sequence. If this buffer was full, then move them to
- * the next buffer so the bytes will be contiguous.
- */
- ChannelBuffer *nextPtr;
- int extra;
-
- nextPtr = bufPtr->nextPtr;
- if (bufPtr->nextAdded < bufPtr->bufLength) {
- if (gsPtr->rawRead > 0) {
- /*
- * Some raw bytes were converted to UTF-8. Fall through,
- * returning those UTF-8 characters because a EOL might be
- * present in them.
- */
- } else if (statePtr->flags & CHANNEL_EOF) {
- /*
- * There was a partial character followed by EOF on the
- * device. Fall through, returning that nothing was found.
- */
- bufPtr->nextRemoved = bufPtr->nextAdded;
- } else {
- /*
- * There are no more cached raw bytes left. See if we can
- * get some more.
- */
- goto read;
- }
- } else {
- if (nextPtr == NULL) {
- nextPtr = AllocChannelBuffer(statePtr->bufSize);
- bufPtr->nextPtr = nextPtr;
- statePtr->inQueueTail = nextPtr;
- }
- extra = rawLen - gsPtr->rawRead;
- memcpy((VOID *) (nextPtr->buf + BUFFER_PADDING - extra),
- (VOID *) (raw + gsPtr->rawRead), (size_t) extra);
- nextPtr->nextRemoved -= extra;
- bufPtr->nextAdded -= extra;
- }
- }
- gsPtr->bufPtr = bufPtr;
- return 0;
- }
- /*
- *---------------------------------------------------------------------------
- *
- * PeekAhead --
- *
- * Helper function used by Tcl_GetsObj(). Called when we've seen a
- * r at the end of the UTF-8 string and want to look ahead one
- * character to see if it is a n.
- *
- * Results:
- * *gsPtr->dstPtr is filled with a pointer to the start of the range of
- * UTF-8 characters that were found by peeking and *dstEndPtr is filled
- * with a pointer to the bytes just after the end of the range.
- *
- * Side effects:
- * If no more raw bytes were available in one of the channel buffers,
- * tries to perform a non-blocking read to get more bytes from the
- * channel device.
- *
- *---------------------------------------------------------------------------
- */
- static void
- PeekAhead(chanPtr, dstEndPtr, gsPtr)
- Channel *chanPtr; /* The channel to read. */
- char **dstEndPtr; /* Filled with pointer to end of new range
- * of UTF-8 characters. */
- GetsState *gsPtr; /* Current state of gets operation. */
- {
- ChannelState *statePtr = chanPtr->state; /* state info for channel */
- ChannelBuffer *bufPtr;
- Tcl_DriverBlockModeProc *blockModeProc;
- int bytesLeft;
- bufPtr = gsPtr->bufPtr;
- /*
- * If there's any more raw input that's still buffered, we'll peek into
- * that. Otherwise, only get more data from the channel driver if it
- * looks like there might actually be more data. The assumption is that
- * if the channel buffer is filled right up to the end, then there
- * might be more data to read.
- */
- blockModeProc = NULL;
- if (bufPtr->nextPtr == NULL) {
- bytesLeft = bufPtr->nextAdded - (bufPtr->nextRemoved + gsPtr->rawRead);
- if (bytesLeft == 0) {
- if (bufPtr->nextAdded < bufPtr->bufLength) {
- /*
- * Don't peek ahead if last read was short read.
- */
-
- goto cleanup;
- }
- if ((statePtr->flags & CHANNEL_NONBLOCKING) == 0) {
- blockModeProc = Tcl_ChannelBlockModeProc(chanPtr->typePtr);
- if (blockModeProc == NULL) {
- /*
- * Don't peek ahead if cannot set non-blocking mode.
- */
- goto cleanup;
- }
- StackSetBlockMode(chanPtr, TCL_MODE_NONBLOCKING);
- }
- }
- }
- if (FilterInputBytes(chanPtr, gsPtr) == 0) {
- *dstEndPtr = *gsPtr->dstPtr + gsPtr->bytesWrote;
- }
- if (blockModeProc != NULL) {
- StackSetBlockMode(chanPtr, TCL_MODE_BLOCKING);
- }
- return;
- cleanup:
- bufPtr->nextRemoved += gsPtr->rawRead;
- gsPtr->rawRead = 0;
- gsPtr->totalChars += gsPtr->charsWrote;
- gsPtr->bytesWrote = 0;
- gsPtr->charsWrote = 0;
- }
- /*
- *---------------------------------------------------------------------------
- *
- * CommonGetsCleanup --
- *
- * Helper function for Tcl_GetsObj() to restore the channel after
- * a "gets" operation.
- *
- * Results:
- * None.
- *
- * Side effects:
- * Encoding may be freed.
- *
- *---------------------------------------------------------------------------
- */
-
- static void
- CommonGetsCleanup(chanPtr, encoding)
- Channel *chanPtr;
- Tcl_Encoding encoding;
- {
- ChannelState *statePtr = chanPtr->state; /* state info for channel */
- ChannelBuffer *bufPtr, *nextPtr;
-
- bufPtr = statePtr->inQueueHead;
- for ( ; bufPtr != NULL; bufPtr = nextPtr) {
- nextPtr = bufPtr->nextPtr;
- if (bufPtr->nextRemoved < bufPtr->nextAdded) {
- break;
- }
- RecycleBuffer(statePtr, bufPtr, 0);
- }
- statePtr->inQueueHead = bufPtr;
- if (bufPtr == NULL) {
- statePtr->inQueueTail = NULL;
- } else {
- /*
- * If any multi-byte characters were split across channel buffer
- * boundaries, the split-up bytes were moved to the next channel
- * buffer by FilterInputBytes(). Move the bytes back to their
- * original buffer because the caller could change the channel's
- * encoding which could change the interpretation of whether those
- * bytes really made up multi-byte characters after all.
- */
-
- nextPtr = bufPtr->nextPtr;
- for ( ; nextPtr != NULL; nextPtr = bufPtr->nextPtr) {
- int extra;
- extra = bufPtr->bufLength - bufPtr->nextAdded;
- if (extra > 0) {
- memcpy((VOID *) (bufPtr->buf + bufPtr->nextAdded),
- (VOID *) (nextPtr->buf + BUFFER_PADDING - extra),
- (size_t) extra);
- bufPtr->nextAdded += extra;
- nextPtr->nextRemoved = BUFFER_PADDING;
- }
- bufPtr = nextPtr;
- }
- }
- if (statePtr->encoding == NULL) {
- Tcl_FreeEncoding(encoding);
- }
- }
- /*
- *----------------------------------------------------------------------
- *
- * Tcl_Read --
- *
- * Reads a given number of bytes from a channel. EOL and EOF
- * translation is done on the bytes being read, so the the number
- * of bytes consumed from the channel may not be equal to the
- * number of bytes stored in the destination buffer.
- *
- * No encoding conversions are applied to the bytes being read.
- *
- * Results:
- * The number of bytes read, or -1 on error. Use Tcl_GetErrno()
- * to retrieve the error code for the error that occurred.
- *
- * Side effects:
- * May cause input to be buffered.
- *
- *----------------------------------------------------------------------
- */
- int
- Tcl_Read(chan, dst, bytesToRead)
- Tcl_Channel chan; /* The channel from which to read. */
- char *dst; /* Where to store input read. */
- int bytesToRead; /* Maximum number of bytes to read. */
- {
- Channel *chanPtr = (Channel *) chan;
- ChannelState *statePtr = chanPtr->state; /* state info for channel */
- /*
- * This operation should occur at the top of a channel stack.
- */
- chanPtr = statePtr->topChanPtr;
- if (CheckChannelErrors(statePtr, TCL_READABLE) != 0) {
- return -1;
- }
- return DoRead(chanPtr, dst, bytesToRead);
- }
- /*
- *----------------------------------------------------------------------
- *
- * Tcl_ReadRaw --
- *
- * Reads a given number of bytes from a channel. EOL and EOF
- * translation is done on the bytes being read, so the the number
- * of bytes consumed from the channel may not be equal to the
- * number of bytes stored in the destination buffer.
- *
- * No encoding conversions are applied to the bytes being read.
- *
- * Results:
- * The number of bytes read, or -1 on error. Use Tcl_GetErrno()
- * to retrieve the error code for the error that occurred.
- *
- * Side effects:
- * May cause input to be buffered.
- *
- *----------------------------------------------------------------------
- */
- int
- Tcl_ReadRaw(chan, bufPtr, bytesToRead)
- Tcl_Channel chan; /* The channel from which to read. */
- char *bufPtr; /* Where to store input read. */
- int bytesToRead; /* Maximum number of bytes to read. */
- {
- Channel *chanPtr = (Channel *) chan;
- ChannelState *statePtr = chanPtr->state; /* state info for channel */
- int nread, result;
- int copied, copiedNow;
- /*
- * The check below does too much because it will reject a call to this
- * function with a channel which is part of an 'fcopy'. But we have to
- * allow this here or else the chaining in the transformation drivers
- * will fail with 'file busy' error instead of retrieving and
- * transforming the data to copy.
- *
- * We let the check procedure now believe that there is no fcopy in
- * progress. A better solution than this might be an additional flag
- * argument to switch off specific checks.
- */
- if (CheckChannelErrors(statePtr, TCL_READABLE | CHANNEL_RAW_MODE) != 0) {
- return -1;
- }
- /*
- * Check for information in the push-back buffers. If there is
- * some, use it. Go to the driver only if there is none (anymore)
- * and the caller requests more bytes.
- */
- for (copied = 0; copied < bytesToRead; copied += copiedNow) {
- copiedNow = CopyBuffer(chanPtr, bufPtr + copied,
- bytesToRead - copied);
- if (copiedNow == 0) {
- if (statePtr->flags & CHANNEL_EOF) {
- goto done;
- }
- if (statePtr->flags & CHANNEL_BLOCKED) {
- if (statePtr->flags & CHANNEL_NONBLOCKING) {
- goto done;
- }
- statePtr->flags &= (~(CHANNEL_BLOCKED));
- }
- #ifdef TCL_IO_TRACK_OS_FOR_DRIVER_WITH_BAD_BLOCKING
- /* [SF Tcl Bug 943274]. Better emulation of non-blocking
- * channels for channels without BlockModeProc, by keeping
- * track of true fileevents generated by the OS == Data
- * waiting and reading if and only if we are sure to have
- * data.
- */
- if ((statePtr->flags & CHANNEL_NONBLOCKING) &&
- (Tcl_ChannelBlockModeProc(chanPtr->typePtr) == NULL) &&
- !(statePtr->flags & CHANNEL_HAS_MORE_DATA)) {
- /* We bypass the driver, it would block, as no data is available */
- nread = -1;
- result = EWOULDBLOCK;
- } else {
- #endif /* TCL_IO_TRACK_OS_FOR_DRIVER_WITH_BAD_BLOCKING */
- /*
- * Now go to the driver to get as much as is possible to
- * fill the remaining request. Do all the error handling
- * by ourselves. The code was stolen from 'GetInput' and
- * slightly adapted (different return value here).
- *
- * The case of 'bytesToRead == 0' at this point cannot happen.
- */
- nread = (chanPtr->typePtr->inputProc)(chanPtr->instanceData,
- bufPtr + copied, bytesToRead - copied, &result);
- #ifdef TCL_IO_TRACK_OS_FOR_DRIVER_WITH_BAD_BLOCKING
- }
- #endif /* TCL_IO_TRACK_OS_FOR_DRIVER_WITH_BAD_BLOCKING */
- if (nread > 0) {
- /*
- * If we get a short read, signal up that we may be
- * BLOCKED. We should avoid calling the driver because
- * on some platforms we will block in the low level
- * reading code even though the channel is set into
- * nonblocking mode.
- */
-
- if (nread < (bytesToRead - copied)) {
- statePtr->flags |= CHANNEL_BLOCKED;
- }
- #ifdef TCL_IO_TRACK_OS_FOR_DRIVER_WITH_BAD_BLOCKING
- if (nread <= (bytesToRead - copied)) {
- /* [SF Tcl Bug 943274] We have read the available
- * data, clear flag */
- statePtr->flags &= ~CHANNEL_HAS_MORE_DATA;
- }
- #endif /* TCL_IO_TRACK_OS_FOR_DRIVER_WITH_BAD_BLOCKING */
- } else if (nread == 0) {
- statePtr->flags |= CHANNEL_EOF;
- statePtr->inputEncodingFlags |= TCL_ENCODING_END;
- } else if (nread < 0) {
- if ((result == EWOULDBLOCK) || (result == EAGAIN)) {
- if (copied > 0) {
- /*
- * Information that was copied earlier has precedence
- * over EAGAIN/WOULDBLOCK handling.
- */
- return copied;
- }
- statePtr->flags |= CHANNEL_BLOCKED;
- result = EAGAIN;
- }
- Tcl_SetErrno(result);
- return -1;
- }
- return copied + nread;
- }
- }
- done:
- return copied;
- }
- /*
- *---------------------------------------------------------------------------
- *
- * Tcl_ReadChars --
- *
- * Reads from the channel until the requested number of characters
- * have been seen, EOF is seen, or the channel would block. EOL
- * and EOF translation is done. If reading binary data, the raw
- * bytes are wrapped in a Tcl byte array object. Otherwise, the raw
- * bytes are converted to UTF-8 using the channel's current encoding
- * and stored in a Tcl string object.
- *
- * Results:
- * The number of characters read, or -1 on error. Use Tcl_GetErrno()
- * to retrieve the error code for the error that occurred.
- *
- * Side effects:
- * May cause input to be buffered.
- *
- *---------------------------------------------------------------------------
- */
-
- int
- Tcl_ReadChars(chan, objPtr, toRead, appendFlag)
- Tcl_Channel chan; /* The channel to read. */
- Tcl_Obj *objPtr; /* Input data is stored in this object. */
- int toRead; /* Maximum number of characters to store,
- * or -1 to read all available data (up to EOF
- * or when channel blocks). */
- int appendFlag; /* If non-zero, data read from the channel
- * will be appended to the object. Otherwise,
- * the data will replace the existing contents
- * of the object. */
- {
- Channel* chanPtr = (Channel *) chan;
- ChannelState* statePtr = chanPtr->state; /* state info for channel */
-
- /*
- * This operation should occur at the top of a channel stack.
- */
- chanPtr = statePtr->topChanPtr;
- if (CheckChannelErrors(statePtr, TCL_READABLE) != 0) {
- /*
- * Update the notifier state so we don't block while there is still
- * data in the buffers.
- */
- UpdateInterest(chanPtr);
- return -1;
- }
- return DoReadChars (chanPtr, objPtr, toRead, appendFlag);
- }
- /*
- *---------------------------------------------------------------------------
- *
- * DoReadChars --
- *
- * Reads from the channel until the requested number of characters
- * have been seen, EOF is seen, or the channel would block. EOL
- * and EOF translation is done. If reading binary data, the raw
- * bytes are wrapped in a Tcl byte array object. Otherwise, the raw
- * bytes are converted to UTF-8 using the channel's current encoding
- * and stored in a Tcl string object.
- *
- * Results:
- * The number of characters read, or -1 on error. Use Tcl_GetErrno()
- * to retrieve the error code for the error that occurred.
- *
- * Side effects:
- * May cause input to be buffered.
- *
- *---------------------------------------------------------------------------
- */
-
- static int
- DoReadChars(chanPtr, objPtr, toRead, appendFlag)
- Channel* chanPtr; /* The channel to read. */
- Tcl_Obj *objPtr; /* Input data is stored in this object. */
- int toRead; /* Maximum number of characters to store,
- * or -1 to read all available data (up to EOF
- * or when channel blocks). */
- int appendFlag; /* If non-zero, data read from the channel
- * will be appended to the object. Otherwise,
- * the data will replace the existing contents
- * of the object. */
- {
- ChannelState *statePtr = chanPtr->state; /* state info for channel */
- ChannelBuffer *bufPtr;
- int offset, factor, copied, copiedNow, result;
- Tcl_Encoding encoding;
- #define UTF_EXPANSION_FACTOR 1024
- /*
- * This operation should occur at the top of a channel stack.
- */
- chanPtr = statePtr->topChanPtr;
- encoding = statePtr->encoding;
- factor = UTF_EXPANSION_FACTOR;
- if (appendFlag == 0) {
- if (encoding == NULL) {
- Tcl_SetByteArrayLength(objPtr, 0);
- } else {
- Tcl_SetObjLength(objPtr, 0);
- /*
- * We're going to access objPtr->bytes directly, so
- * we must ensure that this is actually a string
- * object (otherwise it might have been pure Unicode).
- */
- Tcl_GetString(objPtr);
- }
- offset = 0;
- } else {
- if (encoding == NULL) {
- Tcl_GetByteArrayFromObj(objPtr, &offset);
- } else {
- Tcl_GetStringFromObj(objPtr, &offset);
- }
- }
- for (copied = 0; (unsigned) toRead > 0; ) {
- copiedNow = -1;
- if (statePtr->inQueueHead != NULL) {
- if (encoding == NULL) {
- copiedNow = ReadBytes(statePtr, objPtr, toRead, &offset);
- } else {
- copiedNow = ReadChars(statePtr, objPtr, toRead, &offset,
- &factor);
- }
- /*
- * If the current buffer is empty recycle it.
- */
- bufPtr = statePtr->inQueueHead;
- if (bufPtr->nextRemoved == bufPtr->nextAdded) {
- ChannelBuffer *nextPtr;
- nextPtr = bufPtr->nextPtr;
- RecycleBuffer(statePtr, bufPtr, 0);
- statePtr->inQueueHead = nextPtr;
- if (nextPtr == NULL) {
- statePtr->inQueueTail = NULL;
- }
- }
- }
- if (copiedNow < 0) {
- if (statePtr->flags & CHANNEL_EOF) {
- break;
- }
- if (statePtr->flags & CHANNEL_BLOCKED) {
- if (statePtr->flags & CHANNEL_NONBLOCKING) {
- break;
- }
- statePtr->flags &= ~CHANNEL_BLOCKED;
- }
- result = GetInput(chanPtr);
- if (result != 0) {
- if (result == EAGAIN) {
- break;
- }
- copied = -1;
- goto done;
- }
- } else {
- copied += copiedNow;
- toRead -= copiedNow;
- }
- }
- statePtr->flags &= ~CHANNEL_BLOCKED;
- if (encoding == NULL) {
- Tcl_SetByteArrayLength(objPtr, offset);
- } else {
- Tcl_SetObjLength(objPtr, offset);
- }
- done:
- /*
- * Update the notifier state so we don't block while there is still
- * data in the buffers.
- */
- UpdateInterest(chanPtr);
- return copied;
- }
- /*
- *---------------------------------------------------------------------------
- *
- * ReadBytes --
- *
- * Reads from the channel until the requested number of bytes have
- * been seen, EOF is seen, or the channel would block. Bytes from
- * the channel are stored in objPtr as a ByteArray object. EOL
- * and EOF translation are done.
- *
- * 'bytesToRead' can safely be a very large number because
- * space is only allocated to hold data read from the channel
- * as needed.
- *
- * Results:
- * The return value is the number of bytes appended to the object
- * and *offsetPtr is filled with the total number of bytes in the
- * object (greater than the return value if there were already bytes
- * in the object).
- *
- * Side effects:
- * None.
- *
- *---------------------------------------------------------------------------
- */
- static int
- ReadBytes(statePtr, objPtr, bytesToRead, offsetPtr)
- ChannelState *statePtr; /* State of the channel to read. */
- Tcl_Obj *objPtr; /* Input data is appended to this ByteArray
- * object. Its length is how much space
- * has been allocated to hold data, not how
- * many bytes of data have been stored in the
- * object. */
- int bytesToRead; /* Maximum number of bytes to store,
- * or < 0 to get all available bytes.
- * Bytes are obtained from the first
- * buffer in the queue -- even if this number
- * is larger than the number of bytes
- * available in the first buffer, only the
- * bytes from the first buffer are
- * returned. */
- int *offsetPtr; /* On input, contains how many bytes of
- * objPtr have been used to hold data. On
- * output, filled with how many bytes are now
- * being used. */
- {
- int toRead, srcLen, offset, length, srcRead, dstWrote;
- ChannelBuffer *bufPtr;
- char *src, *dst;
- offset = *offsetPtr;
- bufPtr = statePtr->inQueueHead;
- src = bufPtr->buf + bufPtr->nextRemoved;
- srcLen = bufPtr->nextAdded - bufPtr->nextRemoved;
- toRead = bytesToRead;
- if ((unsigned) toRead > (unsigned) srcLen) {
- toRead = srcLen;
- }
- dst = (char *) Tcl_GetByteArrayFromObj(objPtr, &length);
- if (toRead > length - offset - 1) {
- /*
- * Double the existing size of the object or make enough room to
- * hold all the characters we may get from the source buffer,
- * whichever is larger.
- */
- length = offset * 2;
- if (offset < toRead) {
- length = offset + toRead + 1;
- }
- dst = (char *) Tcl_SetByteArrayLength(objPtr, length);
- }
- dst += offset;
- if (statePtr->flags & INPUT_NEED_NL) {
- statePtr->flags &= ~INPUT_NEED_NL;
- if ((srcLen == 0) || (*src != 'n')) {
- *dst = 'r';
- *offsetPtr += 1;
- return 1;
- }
- *dst++ = 'n';
- src++;
- srcLen--;
- toRead--;
- }
- srcRead = srcLen;
- dstWrote = toRead;
- if (TranslateInputEOL(statePtr, dst, src, &dstWrote, &srcRead) != 0) {
- if (dstWrote == 0) {
- return -1;
- }
- }
- bufPtr->nextRemoved += srcRead;
- *offsetPtr += dstWrote;
- return dstWrote;
- }
- /*
- *---------------------------------------------------------------------------
- *
- * ReadChars --
- *
- * Reads from the channel until the requested number of UTF-8
- * characters have been seen, EOF is seen, or the channel would
- * block. Raw bytes from the channel are converted to UTF-8
- * and stored in objPtr. EOL and EOF translation is done.
- *
- * 'charsToRead' can safely be a very large number because
- * space is only allocated to hold data read from the channel
- * as needed.
- *
- * Results:
- * The return value is the number of characters appended to
- * the object, *offsetPtr is filled with the number of bytes that
- * were appended, and *factorPtr is filled with the expansion
- * factor used to guess how many bytes of UTF-8 to allocate to
- * hold N source bytes.
- *
- * Side effects:
- * None.
- *
- *---------------------------------------------------------------------------
- */
- static int
- ReadChars(statePtr, objPtr, charsToRead, offsetPtr, factorPtr)
- ChannelState *statePtr; /* State of channel to read. */
- Tcl_Obj *objPtr; /* Input data is appended to this object.
- * objPtr->length is how much space has been
- * allocated to hold data, not how many bytes
- * of data have been stored in the object. */
- int charsToRead; /* Maximum number of characters to store,
- * or -1 to get all available characters.
- * Characters are obtained from the first
- * buffer in the queue -- even if this number
- * is larger than the number of characters
- * available in the first buffer, only the
- * characters from the first buffer are
- * returned. */
- int *offsetPtr; /* On input, contains how many bytes of
- * objPtr have been used to hold data. On
- * output, filled with how many bytes are now
- * being used. */
- int *factorPtr; /* On input, contains a guess of how many
- * bytes need to be allocated to hold the
- * result of converting N source bytes to
- * UTF-8. On output, contains another guess
- * based on the data seen so far. */
- {
- int toRead, factor, offset, spaceLeft, length, srcLen, dstNeeded;
- int srcRead, dstWrote, numChars, dstRead;
- ChannelBuffer *bufPtr;
- char *src, *dst;
- Tcl_EncodingState oldState;
- int encEndFlagSuppressed = 0;
- factor = *factorPtr;
- offset = *offsetPtr;
- bufPtr = statePtr->inQueueHead;
- src = bufPtr->buf + bufPtr->nextRemoved;
- srcLen = bufPtr->nextAdded - bufPtr->nextRemoved;
- toRead = charsToRead;
- if ((unsigned)toRead > (unsigned)srcLen) {
- toRead = srcLen;
- }
- /*
- * 'factor' is how much we guess that the bytes in the source buffer
- * will expand when converted to UTF-8 chars. This guess comes from
- * analyzing how many characters were produced by the previous
- * pass.
- */
- dstNeeded = toRead * factor / UTF_EXPANSION_FACTOR;
- spaceLeft = objPtr->length - offset - TCL_UTF_MAX - 1;
- if (dstNeeded > spaceLeft) {
- /*
- * Double the existing size of the object or make enough room to
- * hold all the characters we want from the source buffer,
- * whichever is larger.
- */
- length = offset * 2;
- if (offset < dstNeeded) {
- length = offset + dstNeeded;
- }
- spaceLeft = length - offset;
- length += TCL_UTF_MAX + 1;
- Tcl_SetObjLength(objPtr, length);
- }
- if (toRead == srcLen) {
- /*
- * Want to convert the whole buffer in one pass. If we have
- * enough space, convert it using all available space in object
- * rather than using the factor.
- */
- dstNeeded = spaceLeft;
- }
- dst = objPtr->bytes + offset;
- /*
- * SF Tcl Bug 1462248
- * The cause of the crash reported in the referenced bug is this:
- *
- * - ReadChars, called with a single buffer, with a incomplete
- * multi-byte character at the end (only the first byte of it).
- * - Encoding translation fails, asks for more data
- * - Data is read, and eof is reached, TCL_ENCODING_END (TEE) is set.
- * - ReadChar is called again, converts the first buffer, but due
- * to TEE it does not check for incomplete multi-byte data, and the
- * character just after the end of the first buffer is a valid
- * completion of the multi-byte header in the actual buffer. The
- * conversion reads more characters from the buffer then present.
- * This causes nextRemoved to overshoot nextAdded and the next
- * reads compute a negative srcLen, cause further translations to
- * fail, causing copying of data into the next buffer using bad
- * arguments, causing the mecpy for to eventually fail.
- *
- * In the end it is a memory access bug spiraling out of control
- * if the conditions are _just so_. And ultimate cause is that TEE
- * is given to a conversion where it should not. TEE signals that
- * this is the last buffer. Except in our case it is not.
- *
- * My solution is to suppress TEE if the first buffer is not the
- * last. We will eventually need it given that EOF has been
- * reached, but not right now. This is what the new flag
- * "endEncSuppressFlag" is for.
- *
- * The bug in 'Tcl_Utf2UtfProc' where it read from memory behind
- * the actual buffer has been fixed as well, and fixes the problem
- * with the crash too, but this would still allow the generic
- * layer to accidentially break a multi-byte sequence if the
- * conditions are just right, because again the ExternalToUtf
- * would be successful where it should not.
- */
- if ((statePtr->inputEncodingFlags & TCL_ENCODING_END) &&
- (bufPtr->nextPtr != NULL)) {
- /* TEE is set for a buffer which is not the last. Squash it
- * for now, and restore it later, before yielding control to
- * our caller.
- */
- statePtr->inputEncodingFlags &= ~TCL_ENCODING_END;
- encEndFlagSuppressed = 1;
- }
- oldState = statePtr->inputEncodingState;
- if (statePtr->flags & INPUT_NEED_NL) {
- /*
- * We want a 'n' because the last character we saw was 'r'.
- */
- statePtr->flags &= ~INPUT_NEED_NL;
- Tcl_ExternalToUtf(NULL, statePtr->encoding, src, srcLen,
- statePtr->inputEncodingFlags, &statePtr->inputEncodingState,
- dst, TCL_UTF_MAX + 1, &srcRead, &dstWrote, &numChars);
- if ((dstWrote > 0) && (*dst == 'n')) {
- /*
- * The next char was a 'n'. Consume it and produce a 'n'.
- */
- bufPtr->nextRemoved += srcRead;
- } else {
- /*
- * The next char was not a 'n'. Produce a 'r'.
- */
- *dst = 'r';
- }
- statePtr->inputEncodingFlags &= ~TCL_ENCODING_START;
- *offsetPtr += 1;
- if (encEndFlagSuppressed) {
- statePtr->inputEncodingFlags |= TCL_ENCODING_END;
- }
- return 1;
- }
- Tcl_ExternalToUtf(NULL, statePtr->encoding, src, srcLen,
- statePtr->inputEncodingFlags, &statePtr->inputEncodingState, dst,
- dstNeeded + TCL_UTF_MAX, &srcRead, &dstWrote, &numChars);
- if (encEndFlagSuppressed) {
- statePtr->inputEncodingFlags |= TCL_ENCODING_END;
- }
- if (srcRead == 0) {
- /*
- * Not enough bytes in src buffer to make a complete char. Copy
- * the bytes to the next buffer to make a new contiguous string,
- * then tell the caller to fill the buffer with more bytes.
- */
- ChannelBuffer *nextPtr;
-
- nextPtr = bufPtr->nextPtr;
- if (nextPtr == NULL) {
- if (srcLen > 0) {
- /*
- * There isn't enough data in the buffers to complete the next
- * character, so we need to wait for more data before the next
- * file event can be delivered.
- *
- * SF #478856.
- *
- * The exception to this is if the input buffer was
- * completely empty before we tried to convert its
- * contents. Nothing in, nothing out, and no incomplete
- * character data. The conversion before the current one
- * was complete.
- */
- statePtr->flags |= CHANNEL_NEED_MORE_DATA;
- }
- return -1;
- }
- /* Space is made at the beginning of the buffer to copy the
- * previous unused bytes there. Check first if the buffer we
- * are using actually has enough space at its beginning for
- * the data we are copying. Because if not we will write over the
- * buffer management information, especially the 'nextPtr'.
- *
- * Note that the BUFFER_PADDING (See AllocChannelBuffer) is
- * used to prevent exactly this situation. I.e. it should
- * never happen. Therefore it is ok to panic should it happen
- * despite the precautions.
- */
- if (nextPtr->nextRemoved - srcLen < 0) {
- Tcl_Panic ("Buffer Underflow, BUFFER_PADDING not enough");
- }
- nextPtr->nextRemoved -= srcLen;
- memcpy((VOID *) (nextPtr->buf + nextPtr->nextRemoved), (VOID *) src,
- (size_t) srcLen);
- RecycleBuffer(statePtr, bufPtr, 0);
- statePtr->inQueueHead = nextPtr;
- return ReadChars(statePtr, objPtr, charsToRead, offsetPtr, factorPtr);
- }
- dstRead = dstWrote;
- if (TranslateInputEOL(statePtr, dst, dst, &dstWrote, &dstRead) != 0) {
- /*
- * Hit EOF char. How many bytes of src correspond to where the
- * EOF was located in dst? Run the conversion again with an
- * output buffer just big enough to hold the data so we can
- * get the correct value for srcRead.
- */
-
- if (dstWrote == 0) {
- return -1;
- }
- statePtr->inputEncodingState = oldState;
- Tcl_ExternalToUtf(NULL, statePtr->encoding, src, srcLen,
- statePtr->inputEncodingFlags, &statePtr->inputEncodingState,
- dst, dstRead + TCL_UTF_MAX, &srcRead, &dstWrote, &numChars);
- TranslateInputEOL(statePtr, dst, dst, &dstWrote, &dstRead);
- }
- /*
- * The number of characters that we got may be less than the number
- * that we started with because "rn" sequences may have been
- * turned into just 'n' in dst.
- */
- numChars -= (dstRead - dstWrote);
- if ((unsigned) numChars > (unsigned) toRead) {
- /*
- * Got too many chars.
- */
- CONST char *eof;
- eof = Tcl_UtfAtIndex(dst, toRead);
- statePtr->inputEncodingState = oldState;
- Tcl_ExternalToUtf(NULL, statePtr->encoding, src, srcLen,
- statePtr->inputEncodingFlags, &statePtr->inputEncodingState,
- dst, eof - dst + TCL_UTF_MAX, &srcRead, &dstWrote, &numChars);
- dstRead = dstWrote;
- TranslateInputEOL(statePtr, dst, dst, &dstWrote, &dstRead);
- numChars -= (dstRead - dstWrote);
- }
- statePtr->inputEncodingFlags &= ~TCL_ENCODING_START;
- bufPtr->nextRemoved += srcRead;
- if (dstWrote > srcRead + 1) {
- *factorPtr = dstWrote * UTF_EXPANSION_FACTOR / srcRead;
- }
- *offsetPtr += dstWrote;
- return numChars;
- }
- /*
- *---------------------------------------------------------------------------
- *
- * TranslateInputEOL --
- *
- * Perform input EOL and EOF translation on the source buffer,
- * leaving the translated result in the destination buffer.
- *
- * Results:
- * The return value is 1 if the EOF character was found when copying
- * bytes to the destination buffer, 0 otherwise.
- *
- * Side effects:
- * None.
- *
- *---------------------------------------------------------------------------
- */
- static int
- TranslateInputEOL(statePtr, dstStart, srcStart, dstLenPtr, srcLenPtr)
- ChannelState *statePtr; /* Channel being read, for EOL translation
- * and EOF character. */
- char *dstStart; /* Output buffer filled with chars by
- * applying appropriate EOL translation to
- * source characters. */
- CONST char *srcStart; /* Source characters. */
- int *dstLenPtr; /* On entry, the maximum length of output
- * buffer in bytes; must be <= *srcLenPtr. On
- * exit, the number of bytes actually used in
- * output buffer. */
- int *srcLenPtr; /* On entry, the length of source buffer.
- * On exit, the number of bytes read from
- * the source buffer. */
- {
- int dstLen, srcLen, inEofChar;
- CONST char *eof;
- dstLen = *dstLenPtr;
- eof = NULL;
- inEofChar = statePtr->inEofChar;
- if (inEofChar != ' ') {
- /*
- * Find EOF in translated buffer then compress out the EOL. The
- * source buffer may be much longer than the destination buffer --
- * we only want to return EOF if the EOF has been copied to the
- * destination buffer.
- */
- CONST char *src, *srcMax;
- srcMax = srcStart + *srcLenPtr;
- for (src = srcStart; src < srcMax; src++) {
- if (*src == inEofChar) {
- eof = src;
- srcLen = src - srcStart;
- if (srcLen < dstLen) {
- dstLen = srcLen;
- }
- *srcLenPtr = srcLen;
- break;
- }
- }
- }
- switch (statePtr->inputTranslation) {
- case TCL_TRANSLATE_LF: {
- if (dstStart != srcStart) {
- memcpy((VOID *) dstStart, (VOID *) srcStart, (size_t) dstLen);
- }
- srcLen = dstLen;
- break;
- }
- case TCL_TRANSLATE_CR: {
- char *dst, *dstEnd;
-
- if (dstStart != srcStart) {
- memcpy((VOID *) dstStart, (VOID *) srcStart, (size_t) dstLen);
- }
- dstEnd = dstStart + dstLen;
- for (dst = dstStart; dst < dstEnd; dst++) {
- if (*dst == 'r') {
- *dst = 'n';
- }
- }
- srcLen = dstLen;
- break;
- }
- case TCL_TRANSLATE_CRLF: {
- char *dst;
- CONST char *src, *srcEnd, *srcMax;
-
- dst = dstStart;
- src = srcStart;
- srcEnd = srcStart + dstLen;
- srcMax = srcStart + *srcLenPtr;
- for ( ; src < srcEnd; ) {
- if (*src == 'r') {
- src++;
- if (src >= srcMax) {
- statePtr->flags |= INPUT_NEED_NL;
- } else if (*src == 'n') {
- *dst++ = *src++;
- } else {
- *dst++ = 'r';
- }
- } else {
- *dst++ = *src++;
- }
- }
- srcLen = src - srcStart;
- dstLen = dst - dstStart;
- break;
- }
- case TCL_TRANSLATE_AUTO: {
- char *dst;
- CONST char *src, *srcEnd, *srcMax;
- dst = dstStart;
- src = srcStart;
- srcEnd = srcStart + dstLen;
- srcMax = srcStart + *srcLenPtr;
- if ((statePtr->flags & INPUT_SAW_CR) && (src < srcMax)) {
- if (*src == 'n') {
- src++;
- }
- statePtr->flags &= ~INPUT_SAW_CR;
- }
- for ( ; src < srcEnd; ) {
- if (*src == 'r') {
- src++;
- if (src >= srcMax) {
- statePtr->flags |= INPUT_SAW_CR;
- } else if (*src == 'n') {
- if (srcEnd < srcMax) {
- srcEnd++;
- }
- src++;
- }
- *dst++ = 'n';
- } else {
- *dst++ = *src++;
- }
- }
- srcLen = src - srcStart;
- dstLen = dst - dstStart;
- break;
- }
- default: { /* lint. */
- return 0;
- }
- }
- *dstLenPtr = dstLen;
- if ((eof != NULL) && (srcStart + srcLen >= eof)) {
- /*
- * EOF character was seen in EOL translated range. Leave current
- * file position pointing at the EOF character, but don't store the
- * EOF character in the output string.
- */
- statePtr->flags |= (CHANNEL_EOF | CHANNEL_STICKY_EOF);
- statePtr->inputEncodingFlags |= TCL_ENCODING_END;
- statePtr->flags &= ~(INPUT_SAW_CR | INPUT_NEED_NL);
- return 1;
- }
- *srcLenPtr = srcLen;
- return 0;
- }
- /*
- *----------------------------------------------------------------------
- *
- * Tcl_Ungets --
- *
- * Causes the supplied string to be added to the input queue of
- * the channel, at either the head or tail of the queue.
- *
- * Results:
- * The number of bytes stored in the channel, or -1 on error.
- *
- * Side effects:
- * Adds input to the input queue of a channel.
- *
- *----------------------------------------------------------------------
- */
- int
- Tcl_Ungets(chan, str, len, atEnd)
- Tcl_Channel chan; /* The channel for which to add the input. */
- CONST char *str; /* The input itself. */
- int len; /* The length of the input. */
- int atEnd; /* If non-zero, add at end of queue; otherwise
- * add at head of queue. */
- {
- Channel *chanPtr; /* The real IO channel. */
- ChannelState *statePtr; /* State of actual channel. */
- ChannelBuffer *bufPtr; /* Buffer to contain the data. */
- int i, flags;
- chanPtr = (Channel *) chan;
- statePtr = chanPtr->state;
- /*
- * This operation should occur at the top of a channel stack.
- */
- chanPtr = statePtr->topChanPtr;
- /*
- * CheckChannelErrors clears too many flag bits in this one case.
- */
-
- flags = statePtr->flags;
- if (CheckChannelErrors(statePtr, TCL_READABLE) != 0) {
- len = -1;
- goto done;
- }
- statePtr->flags = flags;
- /*
- * If we have encountered a sticky EOF, just punt without storing.
- * (sticky EOF is set if we have seen the input eofChar, to prevent
- * reading beyond the eofChar). Otherwise, clear the EOF flags, and
- * clear the BLOCKED bit. We want to discover these conditions anew
- * in each operation.
- */
- if (statePtr->flags & CHANNEL_STICKY_EOF) {
- goto done;
- }
- statePtr->flags &= (~(CHANNEL_BLOCKED | CHANNEL_EOF));
- bufPtr = AllocChannelBuffer(len);
- for (i = 0; i < len; i++) {
- bufPtr->buf[bufPtr->nextAdded++] = str[i];
- }
- if (statePtr->inQueueHead == (ChannelBuffer *) NULL) {
- bufPtr->nextPtr = (ChannelBuffer *) NULL;
- statePtr->inQueueHead = bufPtr;
- statePtr->inQueueTail = bufPtr;
- } else if (atEnd) {
- bufPtr->nextPtr = (ChannelBuffer *) NULL;
- statePtr->inQueueTail->nextPtr = bufPtr;
- statePtr->inQueueTail = bufPtr;
- } else {
- bufPtr->nextPtr = statePtr->inQueueHead;
- statePtr->inQueueHead = bufPtr;
- }
- done:
- /*
- * Update the notifier state so we don't block while there is still
- * data in the buffers.
- */
- UpdateInterest(chanPtr);
- return len;
- }
- /*
- *----------------------------------------------------------------------
- *
- * Tcl_Flush --
- *
- * Flushes output data on a channel.
- *
- * Results:
- * A standard Tcl result.
- *
- * Side effects:
- * May flush output queued on this channel.
- *
- *----------------------------------------------------------------------
- */
- int
- Tcl_Flush(chan)
- Tcl_Channel chan; /* The Channel to flush. */
- {
- int result; /* Of calling FlushChannel. */
- Channel *chanPtr = (Channel *) chan; /* The actual channel. */
- ChannelState *statePtr = chanPtr->state; /* State of actual channel. */
- /*
- * This operation should occur at the top of a channel stack.
- */
- chanPtr = statePtr->topChanPtr;
- if (CheckChannelErrors(statePtr, TCL_WRITABLE) != 0) {
- return -1;
- }
- /*
- * Force current output buffer to be output also.
- */
- if ((statePtr->curOutPtr != NULL)
- && (statePtr->curOutPtr->nextAdded > 0)) {
- statePtr->flags |= BUFFER_READY;
- }
-
- result = FlushChannel(NULL, chanPtr, 0);
- if (result != 0) {
- return TCL_ERROR;
- }
- return TCL_OK;
- }
- /*
- *----------------------------------------------------------------------
- *
- * DiscardInputQueued --
- *
- * Discards any input read from the channel but not yet consumed
- * by Tcl reading commands.
- *
- * Results:
- * None.
- *
- * Side effects:
- * May discard input from the channel. If discardLastBuffer is zero,
- * leaves one buffer in place for back-filling.
- *
- *----------------------------------------------------------------------
- */
- static void
- DiscardInputQueued(statePtr, discardSavedBuffers)
- ChannelState *statePtr; /* Channel on which to discard
- * the queued input. */
- int discardSavedBuffers; /* If non-zero, discard all buffers including
- * last one. */
- {
- ChannelBuffer *bufPtr, *nxtPtr; /* Loop variables. */
- bufPtr = statePtr->inQueueHead;
- statePtr->inQueueHead = (ChannelBuffer *) NULL;
- statePtr->inQueueTail = (ChannelBuffer *) NULL;
- for (; bufPtr != (ChannelBuffer *) NULL; bufPtr = nxtPtr) {
- nxtPtr = bufPtr->nextPtr;
- RecycleBuffer(statePtr, bufPtr, discardSavedBuffers);
- }
- /*
- * If discardSavedBuffers is nonzero, must also discard any previously
- * saved buffer in the saveInBufPtr field.
- */
-
- if (discardSavedBuffers) {
- if (statePtr->saveInBufPtr != (ChannelBuffer *) NULL) {
- ckfree((char *) statePtr->saveInBufPtr);
- statePtr->saveInBufPtr = (ChannelBuffer *) NULL;
- }
- }
- }
- /*
- *---------------------------------------------------------------------------
- *
- * GetInput --
- *
- * Reads input data from a device into a channel buffer.
- *
- * Results:
- * The return value is the Posix error code if an error occurred while
- * reading from the file, or 0 otherwise.
- *
- * Side effects:
- * Reads from the underlying device.
- *
- *---------------------------------------------------------------------------
- */
- static int
- GetInput(chanPtr)
- Channel *chanPtr; /* Channel to read input from. */
- {
- int toRead; /* How much to read? */
- int result; /* Of calling driver. */
- int nread; /* How much was read from channel? */
- ChannelBuffer *bufPtr; /* New buffer to add to input queue. */
- ChannelState *statePtr = chanPtr->state; /* state info for channel */
- /*
- * Prevent reading from a dead channel -- a channel that has been closed
- * but not yet deallocated, which can happen if the exit handler for
- * channel cleanup has run but the channel is still registered in some
- * interpreter.
- */
-
- if (CheckForDeadChannel(NULL, statePtr)) {
- return EINVAL;
- }
- /*
- * First check for more buffers in the pushback area of the
- * topmost channel in the stack and use them. They can be the
- * result of a transformation which went away without reading all
- * the information placed in the area when it was stacked.
- *
- * Two possibilities for the state: No buffers in it, or a single
- * empty buffer. In the latter case we can recycle it now.
- */
- if (chanPtr->inQueueHead != (ChannelBuffer*) NULL) {
- if (statePtr->inQueueHead != (ChannelBuffer*) NULL) {
- RecycleBuffer(statePtr, statePtr->inQueueHead, 0);
- statePtr->inQueueHead = (ChannelBuffer*) NULL;
- }
- statePtr->inQueueHead = chanPtr->inQueueHead;
- statePtr->inQueueTail = chanPtr->inQueueTail;
- chanPtr->inQueueHead = (ChannelBuffer*) NULL;
- chanPtr->inQueueTail = (ChannelBuffer*) NULL;
- return 0;
- }
- /*
- * Nothing in the pushback area, fall back to the usual handling
- * (driver, etc.)
- */
- /*
- * See if we can fill an existing buffer. If we can, read only
- * as much as will fit in it. Otherwise allocate a new buffer,
- * add it to the input queue and attempt to fill it to the max.
- */
- bufPtr = statePtr->inQueueTail;
- if ((bufPtr != NULL) && (bufPtr->nextAdded < bufPtr->bufLength)) {
- toRead = bufPtr->bufLength - bufPtr->nextAdded;
- } else {
- bufPtr = statePtr->saveInBufPtr;
- statePtr->saveInBufPtr = NULL;
- /*
- * Check the actual buffersize against the requested
- * buffersize. Buffers which are smaller than requested are
- * squashed. This is done to honor dynamic changes of the
- * buffersize made by the user.
- */
- if ((bufPtr != NULL) && ((bufPtr->bufLength - BUFFER_PADDING) < statePtr->bufSize)) {
- ckfree((char *) bufPtr);
- bufPtr = NULL;
- }
- if (bufPtr == NULL) {
- bufPtr = AllocChannelBuffer(statePtr->bufSize);
- }
- bufPtr->nextPtr = (ChannelBuffer *) NULL;
- /* SF #427196: Use the actual size of the buffer to determine
- * the number of bytes to read from the channel and not the
- * size for new buffers. They can be different if the
- * buffersize was changed between reads.
- *
- * Note: This affects performance negatively if the buffersize
- * was extended but this small buffer is reused for all
- * subsequent reads. The system never uses buffers with the
- * requested bigger size in that case. An adjunct patch could
- * try and delete all unused buffers it encounters and which
- * are smaller than the formally requested buffersize.
- */
- toRead = bufPtr->bufLength - bufPtr->nextAdded;
- if (statePtr->inQueueTail == NULL) {
- statePtr->inQueueHead = bufPtr;
- } else {
- statePtr->inQueueTail->nextPtr = bufPtr;
- }
- statePtr->inQueueTail = bufPtr;
- }
- /*
- * If EOF is set, we should avoid calling the driver because on some
- * platforms it is impossible to read from a device after EOF.
- */
- if (statePtr->flags & CHANNEL_EOF) {
- return 0;
- }
- #ifdef TCL_IO_TRACK_OS_FOR_DRIVER_WITH_BAD_BLOCKING
- /* [SF Tcl Bug 943274]. Better emulation of non-blocking channels
- * for channels without BlockModeProc, by keeping track of true
- * fileevents generated by the OS == Data waiting and reading if
- * and only if we are sure to have data.
- */
- if ((statePtr->flags & CHANNEL_NONBLOCKING) &&
- (Tcl_ChannelBlockModeProc(chanPtr->typePtr) == NULL) &&
- !(statePtr->flags & CHANNEL_HAS_MORE_DATA)) {
- /* Bypass the driver, it would block, as no data is available */
- nread = -1;
- result = EWOULDBLOCK;
- } else {
- #endif /* TCL_IO_TRACK_OS_FOR_DRIVER_WITH_BAD_BLOCKING */
- nread = (chanPtr->typePtr->inputProc)(chanPtr->instanceData,
- bufPtr->buf + bufPtr->nextAdded, toRead, &result);
- #ifdef TCL_IO_TRACK_OS_FOR_DRIVER_WITH_BAD_BLOCKING
- }
- #endif /* TCL_IO_TRACK_OS_FOR_DRIVER_WITH_BAD_BLOCKING */
- if (nread > 0) {
- bufPtr->nextAdded += nread;
- /*
- * If we get a short read, signal up that we may be BLOCKED. We
- * should avoid calling the driver because on some platforms we
- * will block in the low level reading code even though the
- * channel is set into nonblocking mode.
- */
-
- if (nread < toRead) {
- statePtr->flags |= CHANNEL_BLOCKED;
- }
- #ifdef TCL_IO_TRACK_OS_FOR_DRIVER_WITH_BAD_BLOCKING
- if (nread <= toRead) {
- /* [SF Tcl Bug 943274] We have read the available data,
- * clear flag */
- statePtr->flags &= ~CHANNEL_HAS_MORE_DATA;
- }
- #endif /* TCL_IO_TRACK_OS_FOR_DRIVER_WITH_BAD_BLOCKING */
- } else if (nread == 0) {
- statePtr->flags |= CHANNEL_EOF;
- statePtr->inputEncodingFlags |= TCL_ENCODING_END;
- } else if (nread < 0) {
- if ((result == EWOULDBLOCK) || (result == EAGAIN)) {
- statePtr->flags |= CHANNEL_BLOCKED;
- result = EAGAIN;
- }
- Tcl_SetErrno(result);
- return result;
- }
- return 0;
- }
- /*
- *----------------------------------------------------------------------
- *
- * Tcl_Seek --
- *
- * Implements seeking on Tcl Channels. This is a public function
- * so that other C facilities may be implemented on top of it.
- *
- * Results:
- * The new access point or -1 on error. If error, use Tcl_GetErrno()
- * to retrieve the POSIX error code for the error that occurred.
- *
- * Side effects:
- * May flush output on the channel. May discard queued input.
- *
- *----------------------------------------------------------------------
- */
- Tcl_WideInt
- Tcl_Seek(chan, offset, mode)
- Tcl_Channel chan; /* The channel on which to seek. */
- Tcl_WideInt offset; /* Offset to seek to. */
- int mode; /* Relative to which location to seek? */
- {
- Channel *chanPtr = (Channel *) chan; /* The real IO channel. */
- ChannelState *statePtr = chanPtr->state; /* state info for channel */
- int inputBuffered, outputBuffered;
- /* # bytes held in buffers. */
- int result; /* Of device driver operations. */
- Tcl_WideInt curPos; /* Position on the device. */
- int wasAsync; /* Was the channel nonblocking before the
- * seek operation? If so, must restore to
- * nonblocking mode after the seek. */
- if (CheckChannelErrors(statePtr, TCL_WRITABLE | TCL_READABLE) != 0) {
- return Tcl_LongAsWide(-1);
- }
- /*
- * Disallow seek on dead channels -- channels that have been closed but
- * not yet been deallocated. Such channels can be found if the exit
- * handler for channel cleanup has run but the channel is still
- * registered in an interpreter.
- */
- if (CheckForDeadChannel(NULL, statePtr)) {
- return Tcl_LongAsWide(-1);
- }
- /*
- * This operation should occur at the top of a channel stack.
- */
- chanPtr = statePtr->topChanPtr;
- /*
- * Disallow seek on channels whose type does not have a seek procedure
- * defined. This means that the channel does not support seeking.
- */
- if (chanPtr->typePtr->seekProc == (Tcl_DriverSeekProc *) NULL) {
- Tcl_SetErrno(EINVAL);
- return Tcl_LongAsWide(-1);
- }
- /*
- * Compute how much input and output is buffered. If both input and
- * output is buffered, cannot compute the current position.
- */
- inputBuffered = Tcl_InputBuffered(chan);
- outputBuffered = Tcl_OutputBuffered(chan);
- if ((inputBuffered != 0) && (outputBuffered != 0)) {
- Tcl_SetErrno(EFAULT);
- return Tcl_LongAsWide(-1);
- }
- /*
- * If we are seeking relative to the current position, compute the
- * corrected offset taking into account the amount of unread input.
- */
- if (mode == SEEK_CUR) {
- offset -= inputBuffered;
- }
- /*
- * Discard any queued input - this input should not be read after
- * the seek.
- */
- DiscardInputQueued(statePtr, 0);
- /*
- * Reset EOF and BLOCKED flags. We invalidate them by moving the
- * access point. Also clear CR related flags.
- */
- statePtr->flags &=
- (~(CHANNEL_EOF | CHANNEL_STICKY_EOF | CHANNEL_BLOCKED | INPUT_SAW_CR));
-
- /*
- * If the channel is in asynchronous output mode, switch it back
- * to synchronous mode and cancel any async flush that may be
- * scheduled. After the flush, the channel will be put back into
- * asynchronous output mode.
- */
- wasAsync = 0;
- if (statePtr->flags & CHANNEL_NONBLOCKING) {
- wasAsync = 1;
- result = StackSetBlockMode(chanPtr, TCL_MODE_BLOCKING);
- if (result != 0) {
- return Tcl_LongAsWide(-1);
- }
- statePtr->flags &= (~(CHANNEL_NONBLOCKING));
- if (statePtr->flags & BG_FLUSH_SCHEDULED) {
- statePtr->flags &= (~(BG_FLUSH_SCHEDULED));
- }
- }
-
- /*
- * If the flush fails we cannot recover the original position. In
- * that case the seek is not attempted because we do not know where
- * the access position is - instead we return the error. FlushChannel
- * has already called Tcl_SetErrno() to report the error upwards.
- * If the flush succeeds we do the seek also.
- */
-
- if (FlushChannel(NULL, chanPtr, 0) != 0) {
- curPos = -1;
- } else {
- /*
- * Now seek to the new position in the channel as requested by the
- * caller. Note that we prefer the wideSeekProc if that is
- * available and non-NULL...
- */
- if (HaveVersion(chanPtr->typePtr, TCL_CHANNEL_VERSION_3) &&
- chanPtr->typePtr->wideSeekProc != NULL) {
- curPos = (chanPtr->typePtr->wideSeekProc) (chanPtr->instanceData,
- offset, mode, &result);
- } else if (offset < Tcl_LongAsWide(LONG_MIN) ||
- offset > Tcl_LongAsWide(LONG_MAX)) {
- result = EOVERFLOW;
- curPos = Tcl_LongAsWide(-1);
- } else {
- curPos = Tcl_LongAsWide((chanPtr->typePtr->seekProc) (
- chanPtr->instanceData, Tcl_WideAsLong(offset), mode,
- &result));
- }
- if (curPos == Tcl_LongAsWide(-1)) {
- Tcl_SetErrno(result);
- }
- }
-
- /*
- * Restore to nonblocking mode if that was the previous behavior.
- *
- * NOTE: Even if there was an async flush active we do not restore
- * it now because we already flushed all the queued output, above.
- */
-
- if (wasAsync) {
- statePtr->flags |= CHANNEL_NONBLOCKING;
- result = StackSetBlockMode(chanPtr, TCL_MODE_NONBLOCKING);
- if (result != 0) {
- return Tcl_LongAsWide(-1);
- }
- }
- return curPos;
- }
- /*
- *----------------------------------------------------------------------
- *
- * Tcl_Tell --
- *
- * Returns the position of the next character to be read/written on
- * this channel.
- *
- * Results:
- * A nonnegative integer on success, -1 on failure. If failed,
- * use Tcl_GetErrno() to retrieve the POSIX error code for the
- * error that occurred.
- *
- * Side effects:
- * None.
- *
- *----------------------------------------------------------------------
- */
- Tcl_WideInt
- Tcl_Tell(chan)
- Tcl_Channel chan; /* The channel to return pos for. */
- {
- Channel *chanPtr = (Channel *) chan; /* The real IO channel. */
- ChannelState *statePtr = chanPtr->state; /* state info for channel */
- int inputBuffered, outputBuffered; /* # bytes held in buffers. */
- int result; /* Of calling device driver. */
- Tcl_WideInt curPos; /* Position on device. */
- if (CheckChannelErrors(statePtr, TCL_WRITABLE | TCL_READABLE) != 0) {
- return Tcl_LongAsWide(-1);
- }
- /*
- * Disallow tell on dead channels -- channels that have been closed but
- * not yet been deallocated. Such channels can be found if the exit
- * handler for channel cleanup has run but the channel is still
- * registered in an interpreter.
- */
- if (CheckForDeadChannel(NULL, statePtr)) {
- return Tcl_LongAsWide(-1);
- }
- /*
- * This operation should occur at the top of a channel stack.
- */
- chanPtr = statePtr->topChanPtr;
- /*
- * Disallow tell on channels whose type does not have a seek procedure
- * defined. This means that the channel does not support seeking.
- */
- if (chanPtr->typePtr->seekProc == (Tcl_DriverSeekProc *) NULL) {
- Tcl_SetErrno(EINVAL);
- return Tcl_LongAsWide(-1);
- }
- /*
- * Compute how much input and output is buffered. If both input and
- * output is buffered, cannot compute the current position.
- */
- inputBuffered = Tcl_InputBuffered(chan);
- outputBuffered = Tcl_OutputBuffered(chan);
- if ((inputBuffered != 0) && (outputBuffered != 0)) {
- Tcl_SetErrno(EFAULT);
- return Tcl_LongAsWide(-1);
- }
- /*
- * Get the current position in the device and compute the position
- * where the next character will be read or written. Note that we
- * prefer the wideSeekProc if that is available and non-NULL...
- */
- if (HaveVersion(chanPtr->typePtr, TCL_CHANNEL_VERSION_3) &&
- chanPtr->typePtr->wideSeekProc != NULL) {
- curPos = (chanPtr->typePtr->wideSeekProc) (chanPtr->instanceData,
- Tcl_LongAsWide(0), SEEK_CUR, &result);
- } else {
- curPos = Tcl_LongAsWide((chanPtr->typePtr->seekProc) (
- chanPtr->instanceData, 0, SEEK_CUR, &result));
- }
- if (curPos == Tcl_LongAsWide(-1)) {
- Tcl_SetErrno(result);
- return Tcl_LongAsWide(-1);
- }
- if (inputBuffered != 0) {
- return curPos - inputBuffered;
- }
- return curPos + outputBuffered;
- }
- /*
- *---------------------------------------------------------------------------
- *
- * Tcl_SeekOld, Tcl_TellOld --
- *
- * Backward-compatability versions of the seek/tell interface that
- * do not support 64-bit offsets. This interface is not documented
- * or expected to be supported indefinitely.
- *
- * Results:
- * As for Tcl_Seek and Tcl_Tell respectively, except truncated to
- * whatever value will fit in an 'int'.
- *
- * Side effects:
- * As for Tcl_Seek and Tcl_Tell respectively.
- *
- *---------------------------------------------------------------------------
- */
- int
- Tcl_SeekOld(chan, offset, mode)
- Tcl_Channel chan; /* The channel on which to seek. */
- int offset; /* Offset to seek to. */
- int mode; /* Relative to which location to seek? */
- {
- Tcl_WideInt wOffset, wResult;
- wOffset = Tcl_LongAsWide((long)offset);
- wResult = Tcl_Seek(chan, wOffset, mode);
- return (int)Tcl_WideAsLong(wResult);
- }
- int
- Tcl_TellOld(chan)
- Tcl_Channel chan; /* The channel to return pos for. */
- {
- Tcl_WideInt wResult;
- wResult = Tcl_Tell(chan);
- return (int)Tcl_WideAsLong(wResult);
- }
- /*
- *---------------------------------------------------------------------------
- *
- * CheckChannelErrors --
- *
- * See if the channel is in an ready state and can perform the
- * desired operation.
- *
- * Results:
- * The return value is 0 if the channel is OK, otherwise the
- * return value is -1 and errno is set to indicate the error.
- *
- * Side effects:
- * May clear the EOF and/or BLOCKED bits if reading from channel.
- *
- *---------------------------------------------------------------------------
- */
-
- static int
- CheckChannelErrors(statePtr, flags)
- ChannelState *statePtr; /* Channel to check. */
- int flags; /* Test if channel supports desired operation:
- * TCL_READABLE, TCL_WRITABLE. Also indicates
- * Raw read or write for special close
- * processing*/
- {
- int direction = flags & (TCL_READABLE|TCL_WRITABLE);
- /*
- * Check for unreported error.
- */
- if (statePtr->unreportedError != 0) {
- Tcl_SetErrno(statePtr->unreportedError);
- statePtr->unreportedError = 0;
- return -1;
- }
- /*
- * Only the raw read and write operations are allowed during close
- * in order to drain data from stacked channels.
- */
- if ((statePtr->flags & CHANNEL_CLOSED) &&
- ((flags & CHANNEL_RAW_MODE) == 0)) {
- Tcl_SetErrno(EACCES);
- return -1;
- }
- /*
- * Fail if the channel is not opened for desired operation.
- */
- if ((statePtr->flags & direction) == 0) {
- Tcl_SetErrno(EACCES);
- return -1;
- }
- /*
- * Fail if the channel is in the middle of a background copy.
- *
- * Don't do this tests for raw channels here or else the chaining in the
- * transformation drivers will fail with 'file busy' error instead of
- * retrieving and transforming the data to copy.
- */
- if ((statePtr->csPtr != NULL) && ((flags & CHANNEL_RAW_MODE) == 0)) {
- Tcl_SetErrno(EBUSY);
- return -1;
- }
- if (direction == TCL_READABLE) {
- /*
- * If we have not encountered a sticky EOF, clear the EOF bit
- * (sticky EOF is set if we have seen the input eofChar, to prevent
- * reading beyond the eofChar). Also, always clear the BLOCKED bit.
- * We want to discover these conditions anew in each operation.
- */
- if ((statePtr->flags & CHANNEL_STICKY_EOF) == 0) {
- statePtr->flags &= ~CHANNEL_EOF;
- }
- statePtr->flags &= ~(CHANNEL_BLOCKED | CHANNEL_NEED_MORE_DATA);
- }
- return 0;
- }
- /*
- *----------------------------------------------------------------------
- *
- * Tcl_Eof --
- *
- * Returns 1 if the channel is at EOF, 0 otherwise.
- *
- * Results:
- * 1 or 0, always.
- *
- * Side effects:
- * None.
- *
- *----------------------------------------------------------------------
- */
- int
- Tcl_Eof(chan)
- Tcl_Channel chan; /* Does this channel have EOF? */
- {
- ChannelState *statePtr = ((Channel *) chan)->state;
- /* State of real channel structure. */
- return ((statePtr->flags & CHANNEL_STICKY_EOF) ||
- ((statePtr->flags & CHANNEL_EOF) &&
- (Tcl_InputBuffered(chan) == 0))) ? 1 : 0;
- }
- /*
- *----------------------------------------------------------------------
- *
- * Tcl_InputBlocked --
- *
- * Returns 1 if input is blocked on this channel, 0 otherwise.
- *
- * Results:
- * 0 or 1, always.
- *
- * Side effects:
- * None.
- *
- *----------------------------------------------------------------------
- */
- int
- Tcl_InputBlocked(chan)
- Tcl_Channel chan; /* Is this channel blocked? */
- {
- ChannelState *statePtr = ((Channel *) chan)->state;
- /* State of real channel structure. */
- return (statePtr->flags & CHANNEL_BLOCKED) ? 1 : 0;
- }
- /*
- *----------------------------------------------------------------------
- *
- * Tcl_InputBuffered --
- *
- * Returns the number of bytes of input currently buffered in the
- * common internal buffer of a channel.
- *
- * Results:
- * The number of input bytes buffered, or zero if the channel is not
- * open for reading.
- *
- * Side effects:
- * None.
- *
- *----------------------------------------------------------------------
- */
- int
- Tcl_InputBuffered(chan)
- Tcl_Channel chan; /* The channel to query. */
- {
- ChannelState *statePtr = ((Channel *) chan)->state;
- /* State of real channel structure. */
- ChannelBuffer *bufPtr;
- int bytesBuffered;
- for (bytesBuffered = 0, bufPtr = statePtr->inQueueHead;
- bufPtr != (ChannelBuffer *) NULL;
- bufPtr = bufPtr->nextPtr) {
- bytesBuffered += (bufPtr->nextAdded - bufPtr->nextRemoved);
- }
- /*
- * Don't forget the bytes in the topmost pushback area.
- */
- for (bufPtr = statePtr->topChanPtr->inQueueHead;
- bufPtr != (ChannelBuffer *) NULL;
- bufPtr = bufPtr->nextPtr) {
- bytesBuffered += (bufPtr->nextAdded - bufPtr->nextRemoved);
- }
- return bytesBuffered;
- }
- /*
- *----------------------------------------------------------------------
- *
- * Tcl_OutputBuffered --
- *
- * Returns the number of bytes of output currently buffered in the
- * common internal buffer of a channel.
- *
- * Results:
- * The number of output bytes buffered, or zero if the channel is not
- * open for writing.
- *
- * Side effects:
- * None.
- *
- *----------------------------------------------------------------------
- */
- int
- Tcl_OutputBuffered(chan)
- Tcl_Channel chan; /* The channel to query. */
- {
- ChannelState *statePtr = ((Channel *) chan)->state;
- /* State of real channel structure. */
- ChannelBuffer *bufPtr;
- int bytesBuffered;
- for (bytesBuffered = 0, bufPtr = statePtr->outQueueHead;
- bufPtr != (ChannelBuffer *) NULL;
- bufPtr = bufPtr->nextPtr) {
- bytesBuffered += (bufPtr->nextAdded - bufPtr->nextRemoved);
- }
- if ((statePtr->curOutPtr != (ChannelBuffer *) NULL) &&
- (statePtr->curOutPtr->nextAdded > statePtr->curOutPtr->nextRemoved)) {
- statePtr->flags |= BUFFER_READY;
- bytesBuffered +=
- (statePtr->curOutPtr->nextAdded - statePtr->curOutPtr->nextRemoved);
- }
- return bytesBuffered;
- }
- /*
- *----------------------------------------------------------------------
- *
- * Tcl_ChannelBuffered --
- *
- * Returns the number of bytes of input currently buffered in the
- * internal buffer (push back area) of a channel.
- *
- * Results:
- * The number of input bytes buffered, or zero if the channel is not
- * open for reading.
- *
- * Side effects:
- * None.
- *
- *----------------------------------------------------------------------
- */
- int
- Tcl_ChannelBuffered(chan)
- Tcl_Channel chan; /* The channel to query. */
- {
- Channel *chanPtr = (Channel *) chan;
- /* real channel structure. */
- ChannelBuffer *bufPtr;
- int bytesBuffered;
- for (bytesBuffered = 0, bufPtr = chanPtr->inQueueHead;
- bufPtr != (ChannelBuffer *) NULL;
- bufPtr = bufPtr->nextPtr) {
- bytesBuffered += (bufPtr->nextAdded - bufPtr->nextRemoved);
- }
- return bytesBuffered;
- }
- /*
- *----------------------------------------------------------------------
- *
- * Tcl_SetChannelBufferSize --
- *
- * Sets the size of buffers to allocate to store input or output
- * in the channel. The size must be between 1 byte and 1 MByte.
- *
- * Results:
- * None.
- *
- * Side effects:
- * Sets the size of buffers subsequently allocated for this channel.
- *
- *----------------------------------------------------------------------
- */
- void
- Tcl_SetChannelBufferSize(chan, sz)
- Tcl_Channel chan; /* The channel whose buffer size
- * to set. */
- int sz; /* The size to set. */
- {
- ChannelState *statePtr; /* State of real channel structure. */
-
- /*
- * If the buffer size is smaller than 1 byte or larger than one MByte,
- * do not accept the requested size and leave the current buffer size.
- */
-
- if (sz < 1) {
- return;
- }
- if (sz > (1024 * 1024)) {
- return;
- }
- statePtr = ((Channel *) chan)->state;
- statePtr->bufSize = sz;
- if (statePtr->outputStage != NULL) {
- ckfree((char *) statePtr->outputStage);
- statePtr->outputStage = NULL;
- }
- if ((statePtr->encoding != NULL) && (statePtr->flags & TCL_WRITABLE)) {
- statePtr->outputStage = (char *)
- ckalloc((unsigned) (statePtr->bufSize + 2));
- }
- }
- /*
- *----------------------------------------------------------------------
- *
- * Tcl_GetChannelBufferSize --
- *
- * Retrieves the size of buffers to allocate for this channel.
- *
- * Results:
- * The size.
- *
- * Side effects:
- * None.
- *
- *----------------------------------------------------------------------
- */
- int
- Tcl_GetChannelBufferSize(chan)
- Tcl_Channel chan; /* The channel for which to find the
- * buffer size. */
- {
- ChannelState *statePtr = ((Channel *) chan)->state;
- /* State of real channel structure. */
- return statePtr->bufSize;
- }
- /*
- *----------------------------------------------------------------------
- *
- * Tcl_BadChannelOption --
- *
- * This procedure generates a "bad option" error message in an
- * (optional) interpreter. It is used by channel drivers when
- * a invalid Set/Get option is requested. Its purpose is to concatenate
- * the generic options list to the specific ones and factorize
- * the generic options error message string.
- *
- * Results:
- * TCL_ERROR.
- *
- * Side effects:
- * An error message is generated in interp's result object to
- * indicate that a command was invoked with the a bad option
- * The message has the form
- * bad option "blah": should be one of
- * <...generic options...>+<...specific options...>
- * "blah" is the optionName argument and "<specific options>"
- * is a space separated list of specific option words.
- * The function takes good care of inserting minus signs before
- * each option, commas after, and an "or" before the last option.
- *
- *----------------------------------------------------------------------
- */
- int
- Tcl_BadChannelOption(interp, optionName, optionList)
- Tcl_Interp *interp; /* Current interpreter. (can be NULL)*/
- CONST char *optionName; /* 'bad option' name */
- CONST char *optionList; /* Specific options list to append
- * to the standard generic options.
- * can be NULL for generic options
- * only.
- */
- {
- if (interp) {
- CONST char *genericopt =
- "blocking buffering buffersize encoding eofchar translation";
- CONST char **argv;
- int argc, i;
- Tcl_DString ds;
- Tcl_DStringInit(&ds);
- Tcl_DStringAppend(&ds, genericopt, -1);
- if (optionList && (*optionList)) {
- Tcl_DStringAppend(&ds, " ", 1);
- Tcl_DStringAppend(&ds, optionList, -1);
- }
- if (Tcl_SplitList(interp, Tcl_DStringValue(&ds),
- &argc, &argv) != TCL_OK) {
- panic("malformed option list in channel driver");
- }
- Tcl_ResetResult(interp);
- Tcl_AppendResult(interp, "bad option "", optionName,
- "": should be one of ", (char *) NULL);
- argc--;
- for (i = 0; i < argc; i++) {
- Tcl_AppendResult(interp, "-", argv[i], ", ", (char *) NULL);
- }
- Tcl_AppendResult(interp, "or -", argv[i], (char *) NULL);
- Tcl_DStringFree(&ds);
- ckfree((char *) argv);
- }
- Tcl_SetErrno(EINVAL);
- return TCL_ERROR;
- }
- /*
- *----------------------------------------------------------------------
- *
- * Tcl_GetChannelOption --
- *
- * Gets a mode associated with an IO channel. If the optionName arg
- * is non NULL, retrieves the value of that option. If the optionName
- * arg is NULL, retrieves a list of alternating option names and
- * values for the given channel.
- *
- * Results:
- * A standard Tcl result. Also sets the supplied DString to the
- * string value of the option(s) returned.
- *
- * Side effects:
- * None.
- *
- *----------------------------------------------------------------------
- */
- int
- Tcl_GetChannelOption(interp, chan, optionName, dsPtr)
- Tcl_Interp *interp; /* For error reporting - can be NULL. */
- Tcl_Channel chan; /* Channel on which to get option. */
- CONST char *optionName; /* Option to get. */
- Tcl_DString *dsPtr; /* Where to store value(s). */
- {
- size_t len; /* Length of optionName string. */
- char optionVal[128]; /* Buffer for sprintf. */
- Channel *chanPtr = (Channel *) chan;
- ChannelState *statePtr = chanPtr->state; /* state info for channel */
- int flags;
- /*
- * Disallow options on dead channels -- channels that have been closed but
- * not yet been deallocated. Such channels can be found if the exit
- * handler for channel cleanup has run but the channel is still
- * registered in an interpreter.
- */
- if (CheckForDeadChannel(interp, statePtr)) {
- return TCL_ERROR;
- }
- /*
- * This operation should occur at the top of a channel stack.
- */
- chanPtr = statePtr->topChanPtr;