/// <summary> AllocChannelBuffer -> ChannelBuffer /// /// Create a new ChannelBuffer object /// </summary> internal ChannelBuffer( int length ) { int n; n = length + BUFFER_PADDING + BUFFER_PADDING; buf = new byte[n]; nextAdded = BUFFER_PADDING; nextRemoved = BUFFER_PADDING; bufLength = length + BUFFER_PADDING; next = null; }
/// <summary> AllocChannelBuffer -> ChannelBuffer /// /// Create a new ChannelBuffer object /// </summary> internal ChannelBuffer(int length) { int n; n = length + BUFFER_PADDING + BUFFER_PADDING; buf = new byte[n]; nextAdded = BUFFER_PADDING; nextRemoved = BUFFER_PADDING; bufLength = length + BUFFER_PADDING; next = null; }
/// <summary> DiscardOutputQueued -> discardQueued /// /// Discards all output queued in the output queue of a channel. /// </summary> private void discardQueued() { ChannelBuffer buf; while ( outQueueHead != null ) { buf = outQueueHead; outQueueHead = buf.next; recycleBuffer( buf, false ); } outQueueHead = null; outQueueTail = null; }
/// <summary> RecycleBuffer -> recycleBuffer /// /// Helper function to recycle output buffers. Ensures that /// that curOut is set to a buffer. Only if these conditions /// are met is the buffer released so that it can be /// garbage collected. /// </summary> private void recycleBuffer( ChannelBuffer buf, bool mustDiscard ) { if ( mustDiscard ) return; // Only save buffers which are at least as big as the requested // buffersize for the channel. This is to honor dynamic changes // of the buffersize made by the user. if ( ( buf.bufLength - tcl.lang.ChannelBuffer.BUFFER_PADDING ) < bufSize ) { return; } if ( curOut == null ) { curOut = buf; buf.nextRemoved = tcl.lang.ChannelBuffer.BUFFER_PADDING; buf.nextAdded = tcl.lang.ChannelBuffer.BUFFER_PADDING; buf.next = null; } }
/// <summary> FlushChannel -> flushChannel /// /// This function flushes as much of the queued output as is possible /// now. If calledFromAsyncFlush is true, it is being called in an /// event handler to flush channel output asynchronously. /// /// Return 0 if successful, else the error code that was returned by the /// channel type operation. /// /// May produce output on a channel. May block indefinitely if the /// channel is synchronous. May schedule an async flush on the channel. /// May recycle memory for buffers in the output queue. /// /// </summary> /// <param name="interp"> Interp object. /// </param> /// <param name="calledFromAsyncFlush"> True if called from an asynchronous /// flush callback. /// </param> internal int flushChannel( Interp interp, bool calledFromAsyncFlush ) { //ChannelState *statePtr = chanPtr->state; ChannelBuffer buf; int toWrite; // Amount of output data in current // buffer available to be written. int written; // Amount of output data actually // written in current round. int errorCode = 0; // Stores POSIX error codes from // channel driver operations. bool wroteSome = false; // Set to true if any data was // written to the driver. // Prevent writing on a dead channel -- a channel that has been closed // but not yet deallocated. This can occur if the exit handler for the // channel deallocation runs before all channels are deregistered in // all interpreters. //if (CheckForDeadChannel(interp, statePtr)) return -1; // Loop over the queued buffers and attempt to flush as // much as possible of the queued output to the channel. while ( true ) { // If the queue is empty and there is a ready current buffer, OR if // the current buffer is full, then move the current buffer to the // queue. if ( ( ( curOut != null ) && ( curOut.nextAdded == curOut.bufLength ) ) || ( bufferReady && ( outQueueHead == null ) ) ) { bufferReady = false; curOut.next = null; if ( outQueueHead == null ) { outQueueHead = curOut; } else { outQueueTail.next = curOut; } outQueueTail = curOut; curOut = null; } buf = outQueueHead; // If we are not being called from an async flush and an async // flush is active, we just return without producing any output. if ( ( !calledFromAsyncFlush ) && bgFlushScheduled ) { return 0; } // If the output queue is still empty, break out of the while loop. if ( buf == null ) { break; // Out of the "while (1)". } // Produce the output on the channel. toWrite = buf.nextAdded - buf.nextRemoved; //written = (chanPtr->typePtr->outputProc) (chanPtr->instanceData, // bufPtr->buf + bufPtr->nextRemoved, toWrite, // &errorCode); try { output.Write( buf.buf, buf.nextRemoved, toWrite ); written = toWrite; } catch ( IOException ex ) { // FIXME: How can we recover and get posix errors? SupportClass.WriteStackTrace( ex, System.Console.Error ); errorCode = TclPosixException.EIO; // Generic I/O error ??? written = -1; } // If the write failed completely attempt to start the asynchronous // flush mechanism and break out of this loop - do not attempt to // write any more output at this time. if ( written < 0 ) { // If the last attempt to write was interrupted, simply retry. if ( errorCode == TclPosixException.EINTR ) { errorCode = 0; continue; } // If the channel is non-blocking and we would have blocked, // start a background flushing handler and break out of the loop. if ( ( errorCode == TclPosixException.EWOULDBLOCK ) || ( errorCode == TclPosixException.EAGAIN ) ) { // This used to check for CHANNEL_NONBLOCKING, and panic // if the channel was blocking. However, it appears // that setting stdin to -blocking 0 has some effect on // the stdout when it's a tty channel (dup'ed underneath) if ( !bgFlushScheduled ) { bgFlushScheduled = true; updateInterest(); } errorCode = 0; break; } // Decide whether to report the error upwards or defer it. if ( calledFromAsyncFlush ) { if ( unreportedError == 0 ) { unreportedError = errorCode; } } else { // FIXME: Need to figure out what to do here! //Tcl_SetErrno(errorCode); //if (interp != NULL) { // // Casting away CONST here is safe because the // // TCL_VOLATILE flag guarantees CONST treatment // // of the Posix error string. // Tcl_SetResult(interp, // (char *) Tcl_PosixError(interp), TCL_VOLATILE); } // When we get an error we throw away all the output // currently queued. discardQueued(); continue; } else { wroteSome = true; } buf.nextRemoved += written; // If this buffer is now empty, recycle it. if ( buf.nextRemoved == buf.nextAdded ) { outQueueHead = buf.next; if ( outQueueHead == null ) { outQueueTail = null; } recycleBuffer( buf, false ); } } // Closes "while (1)". // If we wrote some data while flushing in the background, we are done. // We can't finish the background flush until we run out of data and // the channel becomes writable again. This ensures that all of the // pending data has been flushed at the system level. if ( bgFlushScheduled ) { if ( wroteSome ) { return errorCode; } else if ( outQueueHead == null ) { bgFlushScheduled = false; // FIXME: What is this watchProc? //(chanPtr->typePtr->watchProc)(chanPtr->instanceData, // statePtr->interestMask); } } // If the channel is flagged as closed, delete it when the refCount // drops to zero, the output queue is empty and there is no output // in the current output buffer. if ( closed && ( refCount <= 0 ) && ( outQueueHead == null ) && ( ( curOut == null ) || ( curOut.nextAdded == curOut.nextRemoved ) ) ) { return closeChannel( interp, errorCode ); } return errorCode; }
/// <summary> CloseChannel -> closeChannel /// /// Utility procedure to close a channel and free associated resources. /// /// If the channel was stacked, then the it will copy the necessary /// elements of the NEXT channel into the TOP channel, in essence /// unstacking the channel. The NEXT channel will then be freed. /// /// If the channel was not stacked, then we will free all the bits /// for the TOP channel, including the data structure itself. /// /// Returns 1 if the channel was stacked, 0 otherwise. /// </summary> protected internal int closeChannel( Interp interp, int errorCode ) { int result = 0; //ChannelState *statePtr; // state of the channel stack. //ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey); //if (chanPtr == NULL) { // return result; //} //statePtr = chanPtr->state; // Discard a leftover buffer in the current output buffer field. if ( curOut != null ) { //ckfree((char *) statePtr->curOutPtr); curOut = null; } // The caller guarantees that there are no more buffers // queued for output. if ( outQueueHead != null ) { throw new TclRuntimeError( "TclFlush, closed channel: queued output left" ); } // If the EOF character is set in the channel, append that to the // output device. if ( eofChar != 0 ) { try { output.WriteByte( (byte)eofChar ); } catch ( IOException ex ) { // FIXME: How can we recover here?? SupportClass.WriteStackTrace( ex, Console.Error ); } } // Remove this channel from of the list of all channels. //Tcl_CutChannel((Tcl_Channel) chanPtr); // Close and free the channel driver state. //if (chanPtr->typePtr->closeProc != TCL_CLOSE2PROC) { // result = (chanPtr->typePtr->closeProc)(chanPtr->instanceData, interp); //} else { // result = (chanPtr->typePtr->close2Proc)(chanPtr->instanceData, interp, // 0); //} // Some resources can be cleared only if the bottom channel // in a stack is closed. All the other channels in the stack // are not allowed to remove. //if (chanPtr == statePtr->bottomChanPtr) { // if (statePtr->channelName != (char *) NULL) { // ckfree((char *) statePtr->channelName); // statePtr->channelName = NULL; // } // Tcl_FreeEncoding(statePtr->encoding); // if (statePtr->outputStage != NULL) { // ckfree((char *) statePtr->outputStage); // statePtr->outputStage = (char *) NULL; // } //} // If we are being called synchronously, report either // any latent error on the channel or the current error. if ( unreportedError != 0 ) { errorCode = unreportedError; } if ( errorCode == 0 ) { errorCode = result; if ( errorCode != 0 ) { // FIXME: How can we deal with this errno issue? //Tcl_SetErrno(errorCode); } } // Cancel any outstanding timer. //Tcl_DeleteTimerHandler(statePtr->timer); // Mark the channel as deleted by clearing the type structure. //if (chanPtr->downChanPtr != (Channel *) NULL) { // Channel *downChanPtr = chanPtr->downChanPtr; // statePtr->nextCSPtr = tsdPtr->firstCSPtr; // tsdPtr->firstCSPtr = statePtr; // statePtr->topChanPtr = downChanPtr; // downChanPtr->upChanPtr = (Channel *) NULL; // chanPtr->typePtr = NULL; // Tcl_EventuallyFree((ClientData) chanPtr, TCL_DYNAMIC); // return Tcl_Close(interp, (Tcl_Channel) downChanPtr); //} // There is only the TOP Channel, so we free the remaining // pointers we have and then ourselves. Since this is the // last of the channels in the stack, make sure to free the // ChannelState structure associated with it. We use // Tcl_EventuallyFree to allow for any last //chanPtr->typePtr = NULL; //Tcl_EventuallyFree((ClientData) statePtr, TCL_DYNAMIC); //Tcl_EventuallyFree((ClientData) chanPtr, TCL_DYNAMIC); return errorCode; }
/// <summary> WriteChars -> writeChars /// /// Convert chars to the channel's external encoding and /// write the produced bytes into an output buffer, may queue the /// buffer for output if it gets full, and also remembers whether the /// current buffer is ready e.g. if it contains a newline and we are in /// line buffering mode. /// /// The number of bytes written or -1 in case of error. If -1, /// Tcl_GetErrno will return the error code. /// /// May buffer up output and may cause output to be produced on the /// channel. /// /// </summary> /// <param name="src"> Chars to write. /// </param> /// <param name="srfOff"> First index in src array. /// </param> /// <param name="srfLen"> Number of chars to write. /// </param> internal int writeChars( char[] srcArray, int srcOff, int srcLen ) { //ChannelState *statePtr = chanPtr->state; // state info for channel ChannelBuffer buf; char[] stageArray; byte[] dstArray; int stage, src, dst; int saved, savedLF, sawLF, total, dstLen, stageMax; int endEncoding, result; bool consumedSomething; //Tcl_Encoding encoding; byte[] safe = new byte[ChannelBuffer.BUFFER_PADDING]; IntPtr stageLen = new IntPtr( this ); IntPtr toWrite = new IntPtr( this ); IntPtr stageRead = new IntPtr( this ); IntPtr dstWrote = new IntPtr( this ); total = 0; sawLF = 0; savedLF = 0; saved = 0; //encoding = statePtr->encoding; src = 0; // Write the terminated escape sequence even if srcLen is 0. endEncoding = ( encodingEnd ? 0 : 1 ); // Loop over all characters in src, storing them in staging buffer // with proper EOL translation. consumedSomething = true; while ( consumedSomething && ( srcLen + savedLF + endEncoding > 0 ) ) { consumedSomething = false; if ( outputStage == null ) { outputStage = new char[bufSize + 2]; } stageArray = outputStage; stage = 0; stageMax = bufSize; stageLen.i = stageMax; toWrite.i = stageLen.i; if ( toWrite.i > srcLen ) { toWrite.i = srcLen; } if ( savedLF != 0 ) { // 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). stageArray[stage++] = '\n'; stageLen.i--; sawLF++; } if ( translateEOL( stageArray, stage, srcArray, src, stageLen, toWrite ) ) { sawLF++; } stage -= savedLF; stageLen.i += savedLF; savedLF = 0; if ( stageLen.i > stageMax ) { savedLF = 1; stageLen.i = stageMax; } src += toWrite.i; srcLen -= toWrite.i; // Loop over all characters in staging buffer, converting them // to external encoding, storing them in output buffer. while ( stageLen.i + saved + endEncoding > 0 ) { buf = curOut; if ( buf == null ) { buf = new ChannelBuffer( bufSize ); curOut = buf; } // dst = buf.buf + buf.nextAdded; dstArray = buf.buf; dst = buf.nextAdded; dstLen = buf.bufLength - buf.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. Array.Copy( safe, 0, dstArray, dst, saved ); buf.nextAdded += saved; dst += saved; dstLen -= saved; saved = 0; } result = unicodeToExternal( stageArray, stage, stageLen.i, dstArray, dst, dstLen + ChannelBuffer.BUFFER_PADDING, stageRead, dstWrote, null ); // FIXME: Not clear how this condition is dealt with. // // Fix for SF #506297, reported by Martin Forssen // <*****@*****.**>. // // 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. encodingStart = false; // The following can never happen since we use unicode characters. // //if ((result != 0) && ((stageRead.i + dstWrote.i) == 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.i; // srcLen += stageLen.i; // stageLen.i = 0; // savedLF = 0; // break; //} buf.nextAdded += dstWrote.i; if ( buf.nextAdded > buf.bufLength ) { // When translating from unicode 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 = buf.nextAdded - buf.bufLength; // ATK Array.Copy(SupportClass.ToByteArray((System.Array) dstArray), dst + dstLen, SupportClass.ToByteArray(safe), 0, saved); Array.Copy( dstArray, dst + dstLen, safe, 0, saved ); buf.nextAdded = buf.bufLength; } if ( checkFlush( buf, ( sawLF != 0 ) ) != 0 ) { return -1; } total += dstWrote.i; stage += stageRead.i; stageLen.i -= stageRead.i; sawLF = 0; consumedSomething = true; // If all translated characters are written to the buffer, // endEncoding is set to 0 because the escape sequence may be // output. if ( ( stageLen.i + 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; }
/// <summary> CheckFlush -> checkFlush /// /// Helper function for writeBytes() and writeChars(). If the /// channel buffer is ready to be flushed, flush it. /// /// The return value is -1 if there was a problem flushing the /// channel buffer, or 0 otherwise. /// /// The buffer will be recycled if it is flushed. /// /// </summary> /// <param name="buf"> Channel buffer to possibly flush. /// </param> /// <param name="newlineFlag"> True if a the channel buffer /// contains a newline. /// </param> internal int checkFlush( ChannelBuffer buf, bool newlineFlag ) { // 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 ( !bufferReady ) { if ( buf.nextAdded == buf.bufLength ) { bufferReady = true; } else if ( buffering == TclIO.BUFF_LINE ) { if ( newlineFlag ) { bufferReady = true; } } else if ( buffering == TclIO.BUFF_NONE ) { bufferReady = true; } } if ( bufferReady ) { if ( flushChannel( null, false ) != 0 ) { return -1; } } return 0; }
/// <summary> WriteBytes -> writeBytes /// /// Write a sequence of bytes into an output buffer, may queue the /// buffer for output if it gets full, and also remembers whether the /// current buffer is ready e.g. if it contains a newline and we are in /// line buffering mode. /// /// The number of bytes written or -1 in case of error. If -1, /// Tcl_GetErrno will return the error code. /// /// May buffer up output and may cause output to be produced on the /// channel. /// /// </summary> /// <param name="src"> Bytes to write. /// </param> /// <param name="srfOff"> First index in src array. /// </param> /// <param name="srfLen"> Number of bytes to write. /// </param> internal int writeBytes( byte[] srcArray, int srcOff, int srcLen ) { ChannelBuffer buf; byte[] dstArray; int dst, src, dstMax, sawLF, total, savedLF; IntPtr dstLen = new IntPtr( this ); IntPtr toWrite = new IntPtr( this ); total = 0; sawLF = 0; savedLF = 0; src = srcOff; // Loop over all bytes in src, storing them in output buffer with // proper EOL translation. while ( srcLen + savedLF > 0 ) { buf = curOut; if ( buf == null ) { buf = new ChannelBuffer( bufSize ); curOut = buf; } //dst = bufPtr->buf + bufPtr->nextAdded; dstArray = buf.buf; dst = buf.nextAdded; dstMax = buf.bufLength - buf.nextAdded; dstLen.i = dstMax; toWrite.i = dstLen.i; if ( toWrite.i > srcLen ) { toWrite.i = srcLen; } if ( savedLF != 0 ) { // A '\n' was left over from last call to translateEOL() // and we need to store it in this buffer. If the channel is // line-based, we will need to flush it. dstArray[dst++] = (byte)SupportClass.Identity( '\n' ); dstLen.i--; sawLF++; } if ( translateEOL( dstArray, dst, srcArray, src, dstLen, toWrite ) ) { sawLF++; } dstLen.i += savedLF; savedLF = 0; if ( dstLen.i > dstMax ) { savedLF = 1; dstLen.i = dstMax; } buf.nextAdded += dstLen.i; if ( checkFlush( buf, ( sawLF != 0 ) ) != 0 ) { return -1; } total += dstLen.i; src += toWrite.i; srcLen -= toWrite.i; sawLF = 0; } return total; }
/// <summary> FilterInputBytes -> filterBytes /// /// Helper function for getsObj. Appends Unicode characters /// onto the TclObject associated with the GetsState after /// converting them from raw bytes encoded in 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. /// /// The return value is -1 if there was an error reading from the /// channel, 0 otherwise. /// /// FIXME: Doc modification of object's StringBuffer /// /// Status object keeps track of how much data from channel buffers /// has been consumed and where characters should be stored. /// </summary> internal int filterBytes( GetsState gs ) { ChannelBuffer buf; byte[] raw; int rawStart, rawEnd; char[] dst; int offset, toRead, spaceLeft, result, rawLen, length; TclObject obj; int 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 bytes of room. bool goto_read = false; // Set to true when jumping to the read // label, used to simulate a goto. obj = gs.obj; // Subtract the number of bytes that were removed from channel buffer // during last call. buf = gs.buf; if ( buf != null ) { buf.nextRemoved += gs.rawRead.i; if ( buf.nextRemoved >= buf.nextAdded ) { buf = buf.next; } } gs.totalChars += gs.charsWrote.i; while ( true ) { if ( goto_read || ( buf == null ) || ( buf.nextAdded == tcl.lang.ChannelBuffer.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 ( blocked ) { if ( !blocking ) { gs.charsWrote.i = 0; gs.rawRead.i = 0; return -1; } blocked = false; } if ( Input != 0 ) { gs.charsWrote.i = 0; gs.rawRead.i = 0; return -1; } buf = inQueueTail; gs.buf = buf; } // Convert some of the bytes from the channel buffer to characters. // Space in obj's string rep is used to hold the characters. rawStart = buf.nextRemoved; raw = buf.buf; rawEnd = buf.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; //} dst = new char[toRead]; gs.state = encodingState; result = externalToUnicode( raw, rawStart, rawLen, dst, 0, toRead, gs.rawRead, null, gs.charsWrote ); TclString.append( gs.obj, dst, 0, gs.charsWrote.i ); // Make sure that if we go through 'gets', that we reset the // TCL_ENCODING_START flag still. encodingStart = false; 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 next; int extra; next = buf.next; if ( buf.nextAdded < buf.bufLength ) { if ( gs.rawRead.i > 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 ( eofCond ) { // There was a partial character followed by EOF on the // device. Fall through, returning that nothing was found. buf.nextRemoved = buf.nextAdded; } else { // There are no more cached raw bytes left. See if we can // get some more. goto_read = true; goto read; //goto read; } } else { if ( next == null ) { next = new ChannelBuffer( bufSize ); buf.next = next; inQueueTail = next; } extra = rawLen - gs.rawRead.i; Array.Copy( raw, gs.rawRead.i, next.buf, tcl.lang.ChannelBuffer.BUFFER_PADDING - extra, extra ); next.nextRemoved -= extra; buf.nextAdded -= extra; } } goto read_brk; // End loop in the normal case read: ; } read_brk: ; gs.buf = buf; return 0; }
/// <summary> DiscardInputQueued -> discardQueued /// /// Discards any input read from the channel but not yet consumed /// by Tcl reading commands. /// </summary> private void discardQueued( bool discardSavedBuffers ) { ChannelBuffer buf, nxt; buf = inQueueHead; inQueueHead = null; inQueueTail = null; for ( ; buf != null; buf = nxt ) { nxt = buf.next; recycleBuffer( buf, discardSavedBuffers ); } // If discardSavedBuffers is true, must also discard any previously // saved buffer in the saveInBuf field. if ( discardSavedBuffers ) { if ( saveInBuf != null ) { saveInBuf = null; } } }
/// <summary> DoReadChars -> 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 characters using the channel's current /// encoding and stored in a Tcl string object. /// /// </summary> /// <param name="obj">Input data is stored in this object. /// </param> /// <param name="toRead">Maximum number of characters to store, /// or -1 to read all available data (up to EOF /// or when channel blocks). /// </param> internal int doReadChars( TclObject obj, int toRead ) { ChannelBuffer buf; int copied, copiedNow, result; IntPtr offset = new IntPtr( this ); if ( (System.Object)encoding == null ) { TclByteArray.setLength( null, obj, 0 ); } else { TclString.empty( obj ); } offset.i = 0; // if toRead is negative, read until EOF if ( toRead < 0 ) { toRead = System.Int32.MaxValue; } { for ( copied = 0; toRead > 0; ) { copiedNow = -1; if ( inQueueHead != null ) { if ( (System.Object)encoding == null ) { System.Diagnostics.Debug.WriteLine( "calling readBytes " + toRead ); copiedNow = readBytes( obj, toRead, offset ); } else { System.Diagnostics.Debug.WriteLine( "calling readChars " + toRead ); copiedNow = readChars( obj, toRead ); } // If the current buffer is empty recycle it. buf = inQueueHead; System.Diagnostics.Debug.WriteLine( "after read* buf.nextRemoved is " + buf.nextRemoved ); System.Diagnostics.Debug.WriteLine( "after read* buf.nextAdded is " + buf.nextAdded ); if ( buf.nextRemoved == buf.nextAdded ) { System.Diagnostics.Debug.WriteLine( "recycling empty buffer" ); ChannelBuffer next; next = buf.next; recycleBuffer( buf, false ); inQueueHead = next; if ( next == null ) { System.Diagnostics.Debug.WriteLine( "inQueueTail set to null" ); inQueueTail = null; } else { System.Diagnostics.Debug.WriteLine( "inQueueTail is not null" ); } } } if ( copiedNow < 0 ) { System.Diagnostics.Debug.WriteLine( "copiedNow < 0" ); if ( eofCond ) { System.Diagnostics.Debug.WriteLine( "eofCond" ); break; } if ( blocked ) { System.Diagnostics.Debug.WriteLine( "blocked" ); if ( !blocking ) { break; } blocked = false; } result = Input; if ( result != 0 ) { System.Diagnostics.Debug.WriteLine( "non-zero result" ); if ( result == TclPosixException.EAGAIN ) { break; } copied = -1; goto done_brk; //goto done } } else { copied += copiedNow; System.Diagnostics.Debug.WriteLine( "copied incremented to " + copied ); toRead -= copiedNow; System.Diagnostics.Debug.WriteLine( "toRead decremented to " + toRead ); } } blocked = false; if ( (System.Object)encoding == null ) { TclByteArray.setLength( null, obj, offset.i ); System.Diagnostics.Debug.WriteLine( "set byte array length to " + offset.i ); } } done_brk: ; // end done: block //done: updateInterest(); #if DEBUG System.Diagnostics.Debug.WriteLine("returning copied = " + copied); System.Diagnostics.Debug.WriteLine("returning string \"" + obj + "\""); obj.invalidateStringRep(); System.Diagnostics.Debug.WriteLine("returning string \"" + obj + "\""); #endif return copied; }
/// <summary> CommonGetsCleanup -> commonGetsCleanup /// /// Helper function used by getsObj to restore the channel after /// a "gets" operation. /// /// </summary> internal void commonGetsCleanup() { ChannelBuffer buf, next; buf = inQueueHead; for ( ; buf != null; buf = next ) { next = buf.next; if ( buf.nextRemoved < buf.nextAdded ) { break; } recycleBuffer( buf, false ); } inQueueHead = buf; if ( buf == null ) { 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 filterBytes(). 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. next = buf.next; for ( ; next != null; next = buf.next ) { int extra; extra = buf.bufLength - buf.nextAdded; if ( extra > 0 ) { Array.Copy( next.buf, tcl.lang.ChannelBuffer.BUFFER_PADDING - extra, buf.buf, buf.nextAdded, extra ); buf.nextAdded += extra; next.nextRemoved = tcl.lang.ChannelBuffer.BUFFER_PADDING; } buf = next; } } if ( (System.Object)encoding != null ) { //Tcl_FreeEncoding(encoding); } }