wasCSharpSQLite – Blame information for rev 4

Subversion Repositories:
Rev:
Rev Author Line No. Line
1 office 1 #undef DEBUG
2 /*
3 * TclInputStream.java
4 *
5 * Copyright (c) 2003 Mo DeJong
6 *
7 * See the file "license.terms" for information on usage and
8 * redistribution of this file, and for a DISCLAIMER OF ALL
9 * WARRANTIES.
10 *
11 * Included in SQLite3 port to C# for use in testharness only; 2008 Noah B Hart
12 *
13 * RCS @(#) $Id: TclInputStream.java,v 1.1 2003/03/08 03:42:44 mdejong Exp $
14 */
15  
16 // A TclInputStream is a cross between a Java InputStream and
17 // a Reader. The class supports reading raw bytes as well as
18 // encoded characters. It manages buffering and supports
19 // line oriented reading of data. It also supports a user
20 // configurable EOF marker and line ending translations.
21 using System;
22 using System.Text;
23 using System.IO;
24  
25 namespace tcl.lang
26 {
27  
28 public class TclInputStream
29 {
30 internal System.Text.Encoding Encoding
31 {
32 set
33 {
34 encoding = value;
35 }
36  
37 }
38 internal char EofChar
39 {
40 set
41 {
42 eofChar = value;
43 }
44  
45 }
46 internal int Translation
47 {
48 set
49 {
50 translation = value;
51 }
52  
53 }
54 internal int Buffering
55 {
56 set
57 {
58 buffering = value;
59 }
60  
61 }
62 internal int BufferSize
63 {
64 set
65 {
66 bufSize = value;
67 }
68  
69 }
70 internal bool Blocking
71 {
72 set
73 {
74 blocking = value;
75 }
76  
77 }
78 internal bool Blocked
79 {
80 get
81 {
82 return blocked;
83 }
84  
85 }
86 /// <summary> GetInput -> getInput
87 ///
88 /// Reads input data from a device into a channel buffer.
89 ///
90 /// The return value is the Posix error code if an error occurred while
91 /// reading from the file, or 0 otherwise.
92 /// </summary>
93 private int Input
94 {
95  
96  
97 get
98 {
99 int toRead;
100 int result;
101 int nread;
102  
103 // if (checkForDeadChannel()) return EINVAL;
104  
105 // Skipped pushback processing code for stacked Channels
106  
107  
108 // See if we can fill an existing buffer. If we can, read only
109 // as much as will fit in it. Otherwise allocate a new buffer,
110 // add it to the input queue and attempt to fill it to the max.
111  
112 ChannelBuffer buf = inQueueTail;
113  
114 if ( ( buf != null ) && ( buf.nextAdded < buf.bufLength ) )
115 {
116 System.Diagnostics.Debug.WriteLine( "smaller than buffer" );
117 toRead = buf.bufLength - buf.nextAdded;
118 }
119 else
120 {
121 System.Diagnostics.Debug.WriteLine( "fits in existing buffer" );
122  
123 buf = saveInBuf;
124 saveInBuf = null;
125  
126 // Check the actual buffersize against the requested
127 // buffersize. Buffers which are smaller than requested are
128 // squashed. This is done to honor dynamic changes of the
129 // buffersize made by the user.
130  
131 if ( ( buf != null ) && ( ( buf.bufLength - tcl.lang.ChannelBuffer.BUFFER_PADDING ) < bufSize ) )
132 {
133 buf = null;
134 }
135 if ( buf == null )
136 {
137 System.Diagnostics.Debug.WriteLine( "allocated ChannelBuffer of size " + bufSize );
138 buf = new ChannelBuffer( bufSize );
139 }
140 buf.next = null;
141  
142 // Use the actual size of the buffer to determine
143 // the number of bytes to read from the channel and not the
144 // size for new buffers. They can be different if the
145 // buffersize was changed between reads.
146  
147 toRead = buf.bufLength - buf.nextAdded;
148 System.Diagnostics.Debug.WriteLine( "toRead set to " + toRead );
149  
150 if ( inQueueTail == null )
151 inQueueHead = buf;
152 else
153 inQueueTail.next = buf;
154  
155 inQueueTail = buf;
156 }
157  
158 // If EOF is set, we should avoid calling the driver because on some
159 // platforms it is impossible to read from a device after EOF.
160  
161 if ( eofCond )
162 {
163 System.Diagnostics.Debug.WriteLine( "eofCond was true, no error return" );
164 return 0;
165 }
166  
167 // FIXME: We do not handle non-blocking or this CHANNEL_TIMER_FEV flag yet
168  
169 if ( !blocking )
170 {
171 return TclPosixException.EWOULDBLOCK;
172 }
173 else
174 {
175 result = 0;
176  
177 // Can we even use this for a brain-dead nonblocking IO check?
178 int numAvailable = 0;
179  
180 if ( !blocking && ( numAvailable < toRead ) )
181 {
182 result = TclPosixException.EWOULDBLOCK;
183 nread = -1;
184 }
185 else
186 {
187 try
188 {
189 System.Diagnostics.Debug.WriteLine( "now to read " + toRead + " bytes" );
190 if ( input == null )
191 input = System.Console.OpenStandardInput();
192 nread = SupportClass.ReadInput( input, ref buf.buf, buf.nextAdded, toRead );
193  
194 // read() returns -1 on EOF
195 if ( nread == -1 )
196 {
197 System.Diagnostics.Debug.WriteLine( "got EOF from read() call" );
198 nread = 0;
199 }
200 }
201 catch ( IOException ex )
202 {
203 // FIXME: How do we recover from IO errors here?
204 // I think we need to set result to a POSIX error
205 SupportClass.WriteStackTrace( ex, System.Console.Error );
206 nread = -1;
207 }
208 }
209 }
210  
211 if ( nread > 0 )
212 {
213 System.Diagnostics.Debug.WriteLine( "nread is " + nread );
214 buf.nextAdded += nread;
215  
216 // should avoid calling the driver because on some platforms we
217 // will block in the low level reading code even though the
218 // channel is set into nonblocking mode.
219  
220 if ( nread < toRead )
221 {
222 blocked = true;
223 }
224 }
225 else if ( nread == 0 )
226 {
227 System.Diagnostics.Debug.WriteLine( "nread is zero" );
228 eofCond = true;
229 encodingEnd = true;
230 }
231 else if ( nread < 0 )
232 {
233 System.Diagnostics.Debug.WriteLine( "nread is " + nread );
234 if ( ( result == TclPosixException.EWOULDBLOCK ) || ( result == TclPosixException.EAGAIN ) )
235 {
236 blocked = true;
237 result = TclPosixException.EAGAIN;
238 }
239 // FIXME: Called needs to raise a TclException
240 //Tcl_SetErrno(result);
241 return result;
242 }
243 System.Diagnostics.Debug.WriteLine( "no error return" );
244 return 0;
245 }
246  
247 }
248 /// <summary> Tcl_InputBuffered -> getNumBufferedBytes
249 ///
250 /// Return the number of bytes that are current buffered.
251 /// </summary>
252 internal int NumBufferedBytes
253 {
254  
255  
256 get
257 {
258 ChannelBuffer buf;
259 int IOQueued;
260 for ( IOQueued = 0, buf = inQueueHead; buf != null; buf = buf.next )
261 {
262 IOQueued += buf.nextAdded - buf.nextRemoved;
263 }
264 return IOQueued;
265 }
266  
267 }
268  
269 /// <summary> The Java byte stream object we pull data in from.</summary>
270  
271 private Stream input;
272  
273 /// <summary> If nonzero, use this character as EOF marker.</summary>
274  
275 private char eofChar;
276  
277 /// <summary> Flag that is set on each read. If the read encountered EOF
278 /// or a custom eofChar is found, the it is set to true.
279 /// </summary>
280  
281 private bool eofCond = false;
282 private bool stickyEofCond = false;
283  
284 /// <summary> Translation mode for end-of-line character</summary>
285  
286 protected internal int translation;
287  
288 /// <summary> Name of Java encoding for this Channel.
289 /// A null value means use no encoding (binary).
290 /// </summary>
291  
292 protected internal System.Text.Encoding encoding;
293  
294 /// <summary> Current converter object. A null value means
295 /// that no conversions have been done yet.
296 /// </summary>
297  
298 protected internal Decoder btc = null;
299  
300 /// <summary> Buffering</summary>
301  
302 protected internal int buffering;
303  
304 /// <summary> Blocking</summary>
305  
306 protected internal bool blocking;
307  
308 /// <summary> Blocked</summary>
309  
310 protected internal bool blocked = false;
311  
312 /// <summary> Buffer size in bytes</summary>
313  
314 protected internal int bufSize;
315  
316 /// <summary> Used to track EOL state</summary>
317  
318 protected internal bool needNL = false;
319 protected internal bool sawCR_Renamed_Field = false;
320  
321 protected internal bool needMoreData = false;
322  
323 /// <summary> Flags used to track encoding states.
324 /// The encodingState member of called inputEncodingState
325 /// in the C ChannelState type. The encodingStart and encodingEnd
326 /// members combined are called inputEncodingFlags
327 /// and have the bit values TCL_ENCODING_END and TCL_ENCODING_START.
328 /// </summary>
329  
330 internal Object encodingState = null;
331 internal bool encodingStart = true;
332 internal bool encodingEnd = false;
333  
334 /// <summary> First and last buffers in the input queue.</summary>
335  
336 internal ChannelBuffer inQueueHead = null;
337 internal ChannelBuffer inQueueTail = null;
338 internal ChannelBuffer saveInBuf = null;
339  
340 /// <summary> Constructor for Tcl input stream class. We require
341 /// a byte stream source at init time, the stram can't
342 /// be changed after the TclInputStream is created.
343 /// </summary>
344  
345 internal TclInputStream( Stream inInput )
346 {
347 input = inInput;
348 }
349  
350 // Helper used by getsObj and filterBytes
351  
352 internal class GetsState
353 {
354 public GetsState( TclInputStream enclosingInstance )
355 {
356 InitBlock( enclosingInstance );
357 }
358 private void InitBlock( TclInputStream enclosingInstance )
359 {
360 this.enclosingInstance = enclosingInstance;
361 rawRead = new IntPtr();
362 charsWrote = new IntPtr();
363 }
364 private TclInputStream enclosingInstance;
365 public TclInputStream Enclosing_Instance
366 {
367 get
368 {
369 return enclosingInstance;
370 }
371  
372 }
373 internal TclObject obj;
374 //int dst;
375 internal System.Text.Encoding encoding;
376 internal ChannelBuffer buf;
377 internal Object state;
378 internal IntPtr rawRead;
379 IntPtr bytesWrote = new IntPtr();
380 internal IntPtr charsWrote;
381 internal int totalChars;
382 }
383  
384 /// <summary> Tcl_GetsObj -> getsObj
385 ///
386 /// Accumulate input from the input channel until end-of-line or
387 /// end-of-file has been seen. Bytes read from the input channel
388 /// are converted to Unicode using the encoding specified by the
389 /// channel.
390 ///
391 /// Returns the number of characters accumulated in the object
392 /// or -1 if error, blocked, or EOF. If -1, use Tcl_GetErrno()
393 /// to retrieve the POSIX error code for the error or condition
394 /// that occurred.
395 ///
396 /// FIXME: Above setting of error code is not fully implemented.
397 ///
398 /// Will consume input from the channel.
399 /// On reading EOF, leave channel at EOF char.
400 /// On reading EOL, leave channel after EOL, but don't
401 /// return EOL in dst buffer.
402 /// </summary>
403  
404 internal int getsObj( TclObject obj )
405 {
406 GetsState gs;
407 ChannelBuffer buf;
408 bool oldEncodingStart, oldEncodingEnd;
409 int oldRemoved, skip, inEofChar;
410 int copiedTotal, oldLength;
411 bool in_binary_encoding = false;
412 int dst, dstEnd, eol, eof;
413 Object oldState;
414  
415 buf = inQueueHead;
416 //encoding = this.encoding;
417  
418 // Preserved so we can restore the channel's state in case we don't
419 // find a newline in the available input.
420  
421 oldLength = 0;
422 oldEncodingStart = encodingStart;
423 oldEncodingEnd = encodingEnd;
424 oldState = encodingState;
425 oldRemoved = tcl.lang.ChannelBuffer.BUFFER_PADDING;
426 if ( buf != null )
427 {
428 oldRemoved = buf.nextRemoved;
429 }
430  
431 // If there is no encoding, use "iso8859-1" -- readLine() doesn't
432 // produce ByteArray objects.
433  
434 if ( (System.Object)encoding == null )
435 {
436 in_binary_encoding = true;
437 encoding = EncodingCmd.getJavaName( "utf-8" );
438 }
439  
440 System.Diagnostics.Debug.WriteLine( "getsObj encoding is " + encoding );
441  
442 // Object used by filterBytes to keep track of how much data has
443 // been consumed from the channel buffers.
444  
445 gs = new GetsState( this );
446 gs.obj = obj;
447 //gs.dst = &dst;
448 gs.encoding = encoding;
449 gs.buf = buf;
450 gs.state = oldState;
451 gs.rawRead.i = 0;
452 //gs.bytesWrote.i = 0;
453 gs.charsWrote.i = 0;
454 gs.totalChars = 0;
455  
456 // Ensure that tobj is an empty TclString object.
457 // Cheat a bit and grab the StringBuffer out of
458 // the TclString so we can query the data that
459 // was just added to the buffer.
460 TclString.empty( obj );
461 StringBuilder obj_sbuf = ( (TclString)obj.InternalRep ).sbuf;
462  
463 dst = 0;
464 dstEnd = dst;
465  
466 skip = 0;
467 eof = -1;
468 inEofChar = eofChar;
469  
470 // Used to implement goto like functionality for restore
471 // and goteol loop terminaltion blocks.
472  
473 bool restore = false;
474 bool goteol = false;
475  
476 // This is just here so that eol and copiedTotal are
477 // definitely assigned before the try block.
478 eol = -1;
479 copiedTotal = -1;
480  
481 {
482 while ( true )
483 {
484 if ( dst >= dstEnd )
485 {
486 if ( filterBytes( gs ) != 0 )
487 {
488 restore = true;
489 goto restore_or_goteol_brk; //goto restore
490 }
491 dstEnd += gs.charsWrote.i; // dstEnd = dst + gs.bytesWrote;
492 }
493  
494 // Remember if EOF char is seen, then look for EOL anyhow, because
495 // the EOL might be before the EOF char.
496  
497 if ( inEofChar != '\x0000' )
498 {
499 for ( eol = dst; eol < dstEnd; eol++ )
500 {
501 if ( obj_sbuf[eol] == inEofChar )
502 {
503 dstEnd = eol;
504 eof = eol;
505 break;
506 }
507 }
508 }
509  
510 // On EOL, leave current file position pointing after the EOL, but
511 // don't store the EOL in the output string.
512  
513 switch ( translation )
514 {
515  
516 case TclIO.TRANS_LF:
517 {
518 for ( eol = dst; eol < dstEnd; eol++ )
519 {
520 if ( obj_sbuf[eol] == '\n' )
521 {
522 skip = 1;
523 goteol = true;
524 goto restore_or_goteol_brk; //goto goteol
525 }
526 }
527 break;
528 }
529  
530 case TclIO.TRANS_CR:
531 {
532 for ( eol = dst; eol < dstEnd; eol++ )
533 {
534 if ( obj_sbuf[eol] == '\r' )
535 {
536 skip = 1;
537 goteol = true;
538 goto restore_or_goteol_brk; //goto goteol
539 }
540 }
541 break;
542 }
543  
544 case TclIO.TRANS_CRLF:
545 {
546 for ( eol = dst; eol < dstEnd; eol++ )
547 {
548 if ( obj_sbuf[eol] == '\r' )
549 {
550 eol++;
551  
552 // If a CR is at the end of the buffer,
553 // then check for a LF at the begining
554 // of the next buffer.
555  
556 if ( eol >= dstEnd )
557 {
558 //int offset;
559  
560 //offset = eol - objPtr->bytes;
561 dst = dstEnd;
562 if ( filterBytes( gs ) != 0 )
563 {
564 restore = true;
565 goto restore_or_goteol_brk; //goto restore
566 }
567 dstEnd += gs.charsWrote.i; // dstEnd = dst + gs.bytesWrote
568 //eol = objPtr->bytes + offset;
569 if ( eol >= dstEnd )
570 {
571 skip = 0;
572 goteol = true;
573 goto restore_or_goteol_brk; //goto goteol
574 }
575 }
576 if ( obj_sbuf[eol] == '\n' )
577 {
578 eol--;
579 skip = 2;
580 goteol = true;
581 goto restore_or_goteol_brk; //goto goteol
582 }
583 }
584 }
585 break;
586 }
587  
588 case TclIO.TRANS_AUTO:
589 {
590 eol = dst;
591 skip = 1;
592 if ( sawCR_Renamed_Field )
593 {
594 sawCR_Renamed_Field = false;
595 if ( ( eol < dstEnd ) && ( obj_sbuf[eol] == '\n' ) )
596 {
597 // Skip the raw bytes that make up the '\n'.
598  
599 char[] tmp = new char[1];
600 IntPtr rawRead = new IntPtr( this );
601  
602 buf = gs.buf;
603 // FIXME: We don't actually pass gs.state here, should we?
604 //if (btc != null) btc.reset();
605 externalToUnicode( buf.buf, buf.nextRemoved, gs.rawRead.i, tmp, 0, 1, rawRead, null, null );
606 buf.nextRemoved += rawRead.i;
607 gs.rawRead.i -= rawRead.i;
608 //gs.bytesWrote.i--;
609 gs.charsWrote.i--;
610 obj_sbuf.Remove( dst, 1 );
611 dstEnd--;
612 }
613 }
614 for ( eol = dst; eol < dstEnd; eol++ )
615 {
616 if ( obj_sbuf[eol] == '\r' )
617 {
618 eol++;
619 if ( eol == dstEnd )
620 {
621 // If buffer ended on \r, peek ahead to see if a
622 // \n is available.
623  
624 //int offset;
625 IntPtr dstEndPtr = new IntPtr();
626  
627 //offset = eol /* - objPtr->bytes*/;
628 dst = dstEnd;
629  
630 // FIXME: Why does this peek in AUTO mode
631 // but filter in CRLF mode?
632 peekAhead( gs );
633 //dstEnd = dstEndPtr.i;
634 dstEnd += gs.charsWrote.i;
635 //eol = /*objPtr->bytes + */ offset;
636 if ( eol >= dstEnd )
637 {
638 eol--;
639 sawCR_Renamed_Field = true;
640 goteol = true;
641 goto restore_or_goteol_brk; //goto goteol
642 }
643 }
644 if ( obj_sbuf[eol] == '\n' )
645 {
646 skip++;
647 }
648 eol--;
649 goteol = true; //goto goteol
650 goto restore_or_goteol_brk;
651 }
652 else if ( obj_sbuf[eol] == '\n' )
653 {
654 goteol = true;
655 goto restore_or_goteol_brk; //goto goteol
656 }
657 }
658 }
659 break;
660 }
661 if ( eof != -1 )
662 {
663 // EOF character was seen. On EOF, leave current file position
664 // pointing at the EOF character, but don't store the EOF
665 // character in the output string.
666  
667 dstEnd = eof;
668 eofCond = true;
669 stickyEofCond = true;
670 encodingEnd = true;
671 }
672 if ( eofCond )
673 {
674 skip = 0;
675 eol = dstEnd;
676 if ( eol == oldLength )
677 {
678 // If we didn't append any bytes before encountering EOF,
679 // caller needs to see -1.
680  
681 obj_sbuf.Length = oldLength;
682 commonGetsCleanup();
683 copiedTotal = -1;
684 goto restore_or_goteol_brk; //goto done
685 }
686 goteol = true;
687 goto restore_or_goteol_brk; //goto goteol
688 }
689 dst = dstEnd;
690 }
691 }
692  
693 restore_or_goteol_brk:
694 ;
695 // end restore_or_goteol: block
696  
697 if ( goteol )
698 {
699 // Found EOL or EOF, but the output buffer may now contain too many
700 // characters. We need to know how many raw bytes correspond to
701 // the number of characters we want, plus how many raw bytes
702 // correspond to the character(s) making up EOL (if any), so we can
703 // remove the correct number of bytes from the channel buffer.
704  
705 int linelen = eol - dst + skip;
706 char[] tmp = new char[linelen];
707  
708 buf = gs.buf;
709 encodingState = gs.state;
710 if ( btc != null )
711 {
712 btc = this.encoding.GetDecoder();
713 }
714 externalToUnicode( buf.buf, buf.nextRemoved, gs.rawRead.i, tmp, 0, linelen, gs.rawRead, null, gs.charsWrote );
715 buf.nextRemoved += gs.rawRead.i;
716  
717 // Recycle all the emptied buffers.
718  
719 obj_sbuf.Length = eol;
720 commonGetsCleanup();
721 blocked = false;
722 copiedTotal = gs.totalChars + gs.charsWrote.i - skip;
723 }
724 if ( restore )
725 {
726 // Couldn't get a complete line. This only happens if we get a error
727 // reading from the channel or we are non-blocking and there wasn't
728 // an EOL or EOF in the data available.
729  
730 buf = inQueueHead;
731 buf.nextRemoved = oldRemoved;
732  
733 for ( buf = buf.next; buf != null; buf = buf.next )
734 {
735 buf.nextRemoved = tcl.lang.ChannelBuffer.BUFFER_PADDING;
736 }
737 commonGetsCleanup();
738  
739 encodingState = oldState;
740 //if (btc != null) btc.reset(); // Not sure we want to reset encoder state here
741 encodingStart = oldEncodingStart;
742 encodingEnd = oldEncodingEnd;
743 obj_sbuf.Length = oldLength;
744  
745 // We didn't get a complete line so we need to indicate to UpdateInterest
746 // that the gets blocked. It will wait for more data instead of firing
747 // a timer, avoiding a busy wait. This is where we are assuming that the
748 // next operation is a gets. No more file events will be delivered on
749 // this channel until new data arrives or some operation is performed
750 // on the channel (e.g. gets, read, fconfigure) that changes the blocking
751 // state. Note that this means a file event will not be delivered even
752 // though a read would be able to consume the buffered data.
753  
754 needMoreData = true;
755 copiedTotal = -1;
756 }
757  
758 // Update the notifier state so we don't block while there is still
759 // data in the buffers.
760  
761 //done:
762 // Reset original encoding in case it was set to binary
763 if ( in_binary_encoding )
764 encoding = null;
765  
766 updateInterest();
767  
768 // FIXME: copiedTotal seems to be returning incorrect values
769 // for some tests, need to make caller code use the return
770 // value instead of the length of the returned object before
771 // these errors can be detected by the test suite.
772 return copiedTotal;
773 }
774  
775 /// <summary> FilterInputBytes -> filterBytes
776 ///
777 /// Helper function for getsObj. Appends Unicode characters
778 /// onto the TclObject associated with the GetsState after
779 /// converting them from raw bytes encoded in the Channel.
780 ///
781 /// Consumes available bytes from channel buffers. When channel
782 /// buffers are exhausted, reads more bytes from channel device into
783 /// a new channel buffer. It is the caller's responsibility to
784 /// free the channel buffers that have been exhausted.
785 ///
786 /// The return value is -1 if there was an error reading from the
787 /// channel, 0 otherwise.
788 ///
789 /// FIXME: Doc modification of object's StringBuffer
790 ///
791 /// Status object keeps track of how much data from channel buffers
792 /// has been consumed and where characters should be stored.
793 /// </summary>
794  
795 internal int filterBytes( GetsState gs )
796 {
797 ChannelBuffer buf;
798 byte[] raw;
799 int rawStart, rawEnd;
800 char[] dst;
801 int offset, toRead, spaceLeft, result, rawLen, length;
802 TclObject obj;
803 int ENCODING_LINESIZE = 20; // Lower bound on how many bytes
804 // to convert at a time. Since we
805 // don't know a priori how many
806 // bytes of storage this many
807 // source bytes will use, we
808 // actually need at least
809 // ENCODING_LINESIZE bytes of room.
810  
811 bool goto_read = false; // Set to true when jumping to the read
812 // label, used to simulate a goto.
813  
814 obj = gs.obj;
815  
816 // Subtract the number of bytes that were removed from channel buffer
817 // during last call.
818  
819 buf = gs.buf;
820 if ( buf != null )
821 {
822 buf.nextRemoved += gs.rawRead.i;
823 if ( buf.nextRemoved >= buf.nextAdded )
824 {
825 buf = buf.next;
826 }
827 }
828 gs.totalChars += gs.charsWrote.i;
829  
830 while ( true )
831 {
832 if ( goto_read || ( buf == null ) || ( buf.nextAdded == tcl.lang.ChannelBuffer.BUFFER_PADDING ) )
833 {
834 // All channel buffers were exhausted and the caller still hasn't
835 // seen EOL. Need to read more bytes from the channel device.
836 // Side effect is to allocate another channel buffer.
837  
838 //read:
839 if ( blocked )
840 {
841 if ( !blocking )
842 {
843 gs.charsWrote.i = 0;
844 gs.rawRead.i = 0;
845 return -1;
846 }
847 blocked = false;
848 }
849 if ( Input != 0 )
850 {
851 gs.charsWrote.i = 0;
852 gs.rawRead.i = 0;
853 return -1;
854 }
855 buf = inQueueTail;
856 gs.buf = buf;
857 }
858  
859 // Convert some of the bytes from the channel buffer to characters.
860 // Space in obj's string rep is used to hold the characters.
861  
862 rawStart = buf.nextRemoved;
863 raw = buf.buf;
864 rawEnd = buf.nextAdded;
865 rawLen = rawEnd - rawStart;
866  
867 //dst = *gsPtr->dstPtr;
868 //offset = dst - objPtr->bytes;
869 toRead = ENCODING_LINESIZE;
870 if ( toRead > rawLen )
871 {
872 toRead = rawLen;
873 }
874 //dstNeeded = toRead * TCL_UTF_MAX + 1;
875 //spaceLeft = objPtr->length - offset - TCL_UTF_MAX - 1;
876 //if (dstNeeded > spaceLeft) {
877 // length = offset * 2;
878 // if (offset < dstNeeded) {
879 // length = offset + dstNeeded;
880 // }
881 // length += TCL_UTF_MAX + 1;
882 // Tcl_SetObjLength(objPtr, length);
883 // spaceLeft = length - offset;
884 // dst = objPtr->bytes + offset;
885 // *gsPtr->dstPtr = dst;
886 //}
887 dst = new char[toRead];
888 gs.state = encodingState;
889 result = externalToUnicode( raw, rawStart, rawLen, dst, 0, toRead, gs.rawRead, null, gs.charsWrote );
890 TclString.append( gs.obj, dst, 0, gs.charsWrote.i );
891  
892 // Make sure that if we go through 'gets', that we reset the
893 // TCL_ENCODING_START flag still.
894  
895 encodingStart = false;
896  
897 if ( result == TCL_CONVERT_MULTIBYTE )
898 {
899 // The last few bytes in this channel buffer were the start of a
900 // multibyte sequence. If this buffer was full, then move them to
901 // the next buffer so the bytes will be contiguous.
902  
903 ChannelBuffer next;
904 int extra;
905  
906 next = buf.next;
907 if ( buf.nextAdded < buf.bufLength )
908 {
909 if ( gs.rawRead.i > 0 )
910 {
911 // Some raw bytes were converted to UTF-8. Fall through,
912 // returning those UTF-8 characters because a EOL might be
913 // present in them.
914 }
915 else if ( eofCond )
916 {
917 // There was a partial character followed by EOF on the
918 // device. Fall through, returning that nothing was found.
919  
920 buf.nextRemoved = buf.nextAdded;
921 }
922 else
923 {
924 // There are no more cached raw bytes left. See if we can
925 // get some more.
926  
927 goto_read = true;
928 goto read; //goto read;
929 }
930 }
931 else
932 {
933 if ( next == null )
934 {
935 next = new ChannelBuffer( bufSize );
936 buf.next = next;
937 inQueueTail = next;
938 }
939 extra = rawLen - gs.rawRead.i;
940 Array.Copy( raw, gs.rawRead.i, next.buf, tcl.lang.ChannelBuffer.BUFFER_PADDING - extra, extra );
941 next.nextRemoved -= extra;
942 buf.nextAdded -= extra;
943 }
944 }
945  
946 goto read_brk; // End loop in the normal case
947  
948 read:
949 ;
950 }
951  
952 read_brk:
953 ;
954  
955  
956 gs.buf = buf;
957 return 0;
958 }
959  
960 /// <summary> PeekAhead -> peekAhead
961 ///
962 /// Helper function used by getsObj. Called when we've seen a
963 /// \r at the end of the string and want to look ahead one
964 /// character to see if it is a \n.
965 ///
966 /// Characters read from the channel are appended to gs.obj
967 /// via the filterBytes method.
968 /// </summary>
969  
970 internal void peekAhead( GetsState gs )
971 {
972 ChannelBuffer buf;
973 //Tcl_DriverBlockModeProc *blockModeProc;
974 int bytesLeft;
975 bool goto_cleanup = false; // Set to true when jumping to the
976 // cleanup label, used to simulate a goto.
977  
978 buf = gs.buf;
979  
980 // If there's any more raw input that's still buffered, we'll peek into
981 // that. Otherwise, only get more data from the channel driver if it
982 // looks like there might actually be more data. The assumption is that
983 // if the channel buffer is filled right up to the end, then there
984 // might be more data to read.
985  
986 {
987 //blockModeProc = NULL;
988 if ( buf.next == null )
989 {
990 bytesLeft = buf.nextAdded - ( buf.nextRemoved + gs.rawRead.i );
991 if ( bytesLeft == 0 )
992 {
993 if ( buf.nextAdded < buf.bufLength )
994 {
995 // Don't peek ahead if last read was short read.
996 goto_cleanup = true;
997 goto cleanup_brk;
998 }
999 // FIXME: This non-blocking check is currently disabled, non-blocking
1000 // is not currently supported and it is not clean why we would
1001 // need to depend on non-blocking IO when peeking anyway.
1002 if ( blocking )
1003 {
1004 //blockModeProc = Tcl_ChannelBlockModeProc(chanPtr->typePtr);
1005 //if (false)
1006 //{
1007 // // Don't peek ahead if cannot set non-blocking mode.
1008 // goto_cleanup = true;
1009 // goto cleanup_brk;
1010 //}
1011 //StackSetBlockMode(chanPtr, TCL_MODE_NONBLOCKING);
1012 }
1013 }
1014 }
1015 //if (filterBytes(gs) == 0) {
1016 // dstEndPtr.i = gs.charsWrote.i; *gsPtr->dstPtr + gs.bytesWrote.i
1017 //}
1018 filterBytes( gs );
1019 //if (blockModeProc != NULL) {
1020 // StackSetBlockMode(chanPtr, TCL_MODE_BLOCKING);
1021 //}
1022 }
1023  
1024 cleanup_brk:
1025 ;
1026  
1027  
1028 if ( goto_cleanup )
1029 {
1030 buf.nextRemoved += gs.rawRead.i;
1031 gs.rawRead.i = 0;
1032 gs.totalChars += gs.charsWrote.i;
1033 //gs.bytesWrote.i = 0;
1034 gs.charsWrote.i = 0;
1035 }
1036 }
1037  
1038 /// <summary> CommonGetsCleanup -> commonGetsCleanup
1039 ///
1040 /// Helper function used by getsObj to restore the channel after
1041 /// a "gets" operation.
1042 ///
1043 /// </summary>
1044  
1045 internal void commonGetsCleanup()
1046 {
1047 ChannelBuffer buf, next;
1048  
1049 buf = inQueueHead;
1050 for ( ; buf != null; buf = next )
1051 {
1052 next = buf.next;
1053 if ( buf.nextRemoved < buf.nextAdded )
1054 {
1055 break;
1056 }
1057 recycleBuffer( buf, false );
1058 }
1059 inQueueHead = buf;
1060 if ( buf == null )
1061 {
1062 inQueueTail = null;
1063 }
1064 else
1065 {
1066 // If any multi-byte characters were split across channel buffer
1067 // boundaries, the split-up bytes were moved to the next channel
1068 // buffer by filterBytes(). Move the bytes back to their
1069 // original buffer because the caller could change the channel's
1070 // encoding which could change the interpretation of whether those
1071 // bytes really made up multi-byte characters after all.
1072  
1073 next = buf.next;
1074 for ( ; next != null; next = buf.next )
1075 {
1076 int extra;
1077  
1078 extra = buf.bufLength - buf.nextAdded;
1079 if ( extra > 0 )
1080 {
1081 Array.Copy( next.buf, tcl.lang.ChannelBuffer.BUFFER_PADDING - extra, buf.buf, buf.nextAdded, extra );
1082 buf.nextAdded += extra;
1083 next.nextRemoved = tcl.lang.ChannelBuffer.BUFFER_PADDING;
1084 }
1085 buf = next;
1086 }
1087 }
1088 if ( (System.Object)encoding != null )
1089 {
1090 //Tcl_FreeEncoding(encoding);
1091 }
1092 }
1093  
1094 // CloseChannel -> close
1095  
1096 internal void close()
1097 {
1098 discardQueued( true );
1099 // FIXME: More close logic in CloseChannel
1100 }
1101  
1102 internal bool eof()
1103 {
1104 return eofCond;
1105 }
1106  
1107 internal bool sawCR()
1108 {
1109 return sawCR_Renamed_Field;
1110 }
1111  
1112 // Helper class to implement integer pass by reference
1113 // for methods like doReadChars, readBytes and so on.
1114  
1115 internal class IntPtr
1116 {
1117 private void InitBlock( TclInputStream enclosingInstance )
1118 {
1119 this.enclosingInstance = enclosingInstance;
1120 }
1121 private TclInputStream enclosingInstance;
1122 public TclInputStream Enclosing_Instance
1123 {
1124 get
1125 {
1126 return enclosingInstance;
1127 }
1128  
1129 }
1130 internal int i;
1131  
1132 internal IntPtr()
1133 {
1134 }
1135 internal IntPtr( TclInputStream enclosingInstance )
1136 {
1137 InitBlock( enclosingInstance );
1138 }
1139  
1140 internal IntPtr( TclInputStream enclosingInstance, int value )
1141 {
1142 InitBlock( enclosingInstance );
1143 i = value;
1144 }
1145 }
1146  
1147 /// <summary> DoReadChars -> doReadChars
1148 ///
1149 /// Reads from the channel until the requested number of characters
1150 /// have been seen, EOF is seen, or the channel would block. EOL
1151 /// and EOF translation is done. If reading binary data, the raw
1152 /// bytes are wrapped in a Tcl byte array object. Otherwise, the raw
1153 /// bytes are converted to characters using the channel's current
1154 /// encoding and stored in a Tcl string object.
1155 ///
1156 /// </summary>
1157 /// <param name="obj">Input data is stored in this object.
1158 /// </param>
1159 /// <param name="toRead">Maximum number of characters to store,
1160 /// or -1 to read all available data (up to EOF
1161 /// or when channel blocks).
1162 /// </param>
1163  
1164 internal int doReadChars( TclObject obj, int toRead )
1165 {
1166 ChannelBuffer buf;
1167 int copied, copiedNow, result;
1168 IntPtr offset = new IntPtr( this );
1169  
1170 if ( (System.Object)encoding == null )
1171 {
1172 TclByteArray.setLength( null, obj, 0 );
1173 }
1174 else
1175 {
1176 TclString.empty( obj );
1177 }
1178 offset.i = 0;
1179  
1180 // if toRead is negative, read until EOF
1181 if ( toRead < 0 )
1182 {
1183 toRead = System.Int32.MaxValue;
1184 }
1185  
1186 {
1187 for ( copied = 0; toRead > 0; )
1188 {
1189 copiedNow = -1;
1190 if ( inQueueHead != null )
1191 {
1192 if ( (System.Object)encoding == null )
1193 {
1194 System.Diagnostics.Debug.WriteLine( "calling readBytes " + toRead );
1195 copiedNow = readBytes( obj, toRead, offset );
1196 }
1197 else
1198 {
1199 System.Diagnostics.Debug.WriteLine( "calling readChars " + toRead );
1200 copiedNow = readChars( obj, toRead );
1201 }
1202  
1203 // If the current buffer is empty recycle it.
1204  
1205 buf = inQueueHead;
1206 System.Diagnostics.Debug.WriteLine( "after read* buf.nextRemoved is " + buf.nextRemoved );
1207 System.Diagnostics.Debug.WriteLine( "after read* buf.nextAdded is " + buf.nextAdded );
1208  
1209 if ( buf.nextRemoved == buf.nextAdded )
1210 {
1211 System.Diagnostics.Debug.WriteLine( "recycling empty buffer" );
1212 ChannelBuffer next;
1213  
1214 next = buf.next;
1215 recycleBuffer( buf, false );
1216 inQueueHead = next;
1217 if ( next == null )
1218 {
1219 System.Diagnostics.Debug.WriteLine( "inQueueTail set to null" );
1220 inQueueTail = null;
1221 }
1222 else
1223 {
1224 System.Diagnostics.Debug.WriteLine( "inQueueTail is not null" );
1225 }
1226 }
1227 }
1228 if ( copiedNow < 0 )
1229 {
1230 System.Diagnostics.Debug.WriteLine( "copiedNow < 0" );
1231 if ( eofCond )
1232 {
1233 System.Diagnostics.Debug.WriteLine( "eofCond" );
1234 break;
1235 }
1236 if ( blocked )
1237 {
1238 System.Diagnostics.Debug.WriteLine( "blocked" );
1239 if ( !blocking )
1240 {
1241 break;
1242 }
1243 blocked = false;
1244 }
1245 result = Input;
1246 if ( result != 0 )
1247 {
1248 System.Diagnostics.Debug.WriteLine( "non-zero result" );
1249 if ( result == TclPosixException.EAGAIN )
1250 {
1251 break;
1252 }
1253 copied = -1;
1254 goto done_brk; //goto done
1255 }
1256 }
1257 else
1258 {
1259 copied += copiedNow;
1260 System.Diagnostics.Debug.WriteLine( "copied incremented to " + copied );
1261 toRead -= copiedNow;
1262 System.Diagnostics.Debug.WriteLine( "toRead decremented to " + toRead );
1263 }
1264 }
1265  
1266 blocked = false;
1267  
1268 if ( (System.Object)encoding == null )
1269 {
1270 TclByteArray.setLength( null, obj, offset.i );
1271 System.Diagnostics.Debug.WriteLine( "set byte array length to " + offset.i );
1272 }
1273 }
1274  
1275 done_brk:
1276 ;
1277 // end done: block
1278  
1279 //done:
1280 updateInterest();
1281  
1282 #if DEBUG
1283 System.Diagnostics.Debug.WriteLine("returning copied = " + copied);
1284  
1285 System.Diagnostics.Debug.WriteLine("returning string \"" + obj + "\"");
1286 obj.invalidateStringRep();
1287  
1288 System.Diagnostics.Debug.WriteLine("returning string \"" + obj + "\"");
1289 #endif
1290  
1291 return copied;
1292 }
1293  
1294 /// <summary> ReadBytes -> readBytes
1295 ///
1296 /// Reads from the channel until the requested number of
1297 /// bytes have been seen, EOF is seen, or the channel would
1298 /// block. Bytes from the channel are stored in obj as a
1299 /// ByteArray object. EOL and EOF translation are done.
1300 ///
1301 /// 'bytesToRead' can safely be a very large number because
1302 /// space is only allocated to hold data read from the channel
1303 /// as needed.
1304 ///
1305 /// The return value is the number of bytes appended to
1306 /// the object.
1307 ///
1308 /// </summary>
1309 /// <param name="obj,">the TclByteArrayObject we are operating on
1310 /// </param>
1311 /// <param name="bytesToRead,">Maximum number of bytes to store.
1312 /// Bytes are obtained from the first
1313 /// buffer in the queue -- even if this number
1314 /// is larger than the number of bytes only
1315 /// the bytes from the first buffer are returned.
1316 /// </param>
1317 /// <param name="offsetPtr"> On input, contains how many bytes of
1318 /// obj have been used to hold data. On
1319 /// output, how many bytes are now being used.
1320 /// </param>
1321  
1322 internal int readBytes( TclObject obj, int bytesToRead, IntPtr offsetPtr )
1323 {
1324 int toRead, srcOff, srcLen, offset, length;
1325 ChannelBuffer buf;
1326 IntPtr srcRead, dstWrote;
1327 byte[] src, dst;
1328  
1329 offset = offsetPtr.i;
1330  
1331 buf = inQueueHead;
1332 src = buf.buf;
1333 srcOff = buf.nextRemoved;
1334 srcLen = buf.nextAdded - buf.nextRemoved;
1335  
1336 System.Diagnostics.Debug.WriteLine( "readBytes() : src buffer len is " + buf.buf.Length );
1337 System.Diagnostics.Debug.WriteLine( "readBytes() : buf.nextRemoved is " + buf.nextRemoved );
1338 System.Diagnostics.Debug.WriteLine( "readBytes() : buf.nextAdded is " + buf.nextAdded );
1339  
1340 toRead = bytesToRead;
1341 if ( toRead > srcLen )
1342 {
1343 toRead = srcLen;
1344 System.Diagnostics.Debug.WriteLine( "readBytes() : toRead set to " + toRead );
1345 }
1346  
1347 length = TclByteArray.getLength( null, obj );
1348 dst = TclByteArray.getBytes( null, obj );
1349 System.Diagnostics.Debug.WriteLine( "readBytes() : toRead is " + toRead );
1350 System.Diagnostics.Debug.WriteLine( "readBytes() : length is " + length );
1351 System.Diagnostics.Debug.WriteLine( "readBytes() : array length is " + dst.Length );
1352  
1353 if ( toRead > length - offset - 1 )
1354 {
1355 System.Diagnostics.Debug.WriteLine( "readBytes() : TclObject too small" );
1356  
1357 // Double the existing size of the object or make enough room to
1358 // hold all the characters we may get from the source buffer,
1359 // whichever is larger.
1360  
1361 length = offset * 2;
1362 if ( offset < toRead )
1363 {
1364 length = offset + toRead + 1;
1365 }
1366 dst = TclByteArray.setLength( null, obj, length );
1367 }
1368  
1369 if ( needNL )
1370 {
1371 needNL = false;
1372 if ( ( srcLen == 0 ) || ( src[srcOff] != '\n' ) )
1373 {
1374 dst[offset] = (byte)SupportClass.Identity( '\r' );
1375 offsetPtr.i += 1;
1376 return 1;
1377 }
1378 dst[offset++] = (byte)SupportClass.Identity( '\n' );
1379 srcOff++;
1380 srcLen--;
1381 toRead--;
1382 }
1383  
1384 srcRead = new IntPtr( this, srcLen );
1385 dstWrote = new IntPtr( this, toRead );
1386  
1387 if ( translateEOL( dst, offset, src, srcOff, dstWrote, srcRead ) != 0 )
1388 {
1389 if ( dstWrote.i == 0 )
1390 {
1391 return -1;
1392 }
1393 }
1394  
1395 buf.nextRemoved += srcRead.i;
1396 offsetPtr.i += dstWrote.i;
1397 return dstWrote.i;
1398 }
1399  
1400 /// <summary> ReadChars -> readChars
1401 ///
1402 /// Reads from the channel until the requested number of
1403 /// characters have been seen, EOF is seen, or the channel would
1404 /// block. Raw bytes from the channel are converted to characters
1405 /// and stored in obj. EOL and EOF translation is done.
1406 ///
1407 /// 'charsToRead' can safely be a very large number because
1408 /// space is only allocated to hold data read from the channel
1409 /// as needed.
1410 ///
1411 /// The return value is the number of characters appended to
1412 /// the object.
1413 ///
1414 /// </summary>
1415 /// <param name="obj,">the TclByteArrayObject we are operating on
1416 /// </param>
1417 /// <param name="charsToRead,">Maximum number of chars to store.
1418 /// Chars are obtained from the first
1419 /// buffer in the queue -- even if this number
1420 /// is larger than the number of chars only
1421 /// the chars from the first buffer are returned.
1422 /// </param>
1423  
1424 internal int readChars( TclObject obj, int charsToRead )
1425 {
1426 int toRead, factor, spaceLeft, length, srcLen, dstNeeded;
1427 int srcOff, dstOff;
1428 IntPtr srcRead, numChars, dstRead, dstWrote;
1429 ChannelBuffer buf;
1430 byte[] src;
1431 char[] dst;
1432  
1433 Object oldState;
1434  
1435 srcRead = new IntPtr( this );
1436 numChars = new IntPtr( this );
1437 dstRead = new IntPtr( this );
1438 dstWrote = new IntPtr( this );
1439  
1440 buf = inQueueHead;
1441 src = buf.buf;
1442 srcOff = buf.nextRemoved;
1443 srcLen = buf.nextAdded - buf.nextRemoved;
1444  
1445 /* FIXME: Include final Tcl patch for srcLen == 0 case */
1446  
1447 if ( srcLen == 0 )
1448 {
1449 if ( needNL )
1450 {
1451 TclString.append( obj, "\r" );
1452 return 1;
1453 }
1454 return -1;
1455 }
1456  
1457 toRead = charsToRead;
1458 if ( toRead > srcLen )
1459 {
1460 toRead = srcLen;
1461 }
1462  
1463 // FIXME : Do something to cache conversion buffer, or it might also
1464 // to pass the TclObject directly into the externalToUnicode method
1465 // so as to avoid the need for this extra buffer.
1466 dstNeeded = toRead;
1467 dst = new char[dstNeeded];
1468 dstOff = 0;
1469  
1470 oldState = encodingState;
1471 if ( needNL )
1472 {
1473 // We want a '\n' because the last character we saw was '\r'.
1474 needNL = false;
1475  
1476 externalToUnicode( src, srcOff, srcLen, dst, dstOff, 1, srcRead, dstWrote, numChars );
1477 if ( ( numChars.i > 0 ) && ( dst[dstOff] == '\n' ) )
1478 {
1479 // The next char was a '\n'. Consume it and produce a '\n'.
1480 buf.nextRemoved += srcRead.i;
1481 }
1482 else
1483 {
1484 // The next char was not a '\n'. Produce a '\r'.
1485 dst[dstOff] = '\r';
1486 }
1487 encodingStart = false;
1488 TclString.append( obj, dst, dstOff, 1 );
1489 return 1;
1490 }
1491  
1492 externalToUnicode( src, srcOff, srcLen, dst, dstOff, dstNeeded, srcRead, dstWrote, numChars );
1493  
1494 // This block is disabled since the char converter does
1495 // not inform us about partial chars, instead it silently
1496 // stores the partial character internally.
1497  
1498 //if (false && srcRead.i == 0)
1499 //{
1500 // // Not enough bytes in src buffer to make a complete char. Copy
1501 // // the bytes to the next buffer to make a new contiguous string,
1502 // // then tell the caller to fill the buffer with more bytes.
1503  
1504 // ChannelBuffer next;
1505  
1506 // next = buf.next;
1507 // if (next == null)
1508 // {
1509 // if (srcLen > 0)
1510 // {
1511 // // There isn't enough data in the buffers to complete the next
1512 // // character, so we need to wait for more data before the next
1513 // // file event can be delivered.
1514 // //
1515 // // The exception to this is if the input buffer was
1516 // // completely empty before we tried to convert its
1517 // // contents. Nothing in, nothing out, and no incomplete
1518 // // character data. The conversion before the current one
1519 // // was complete.
1520  
1521 // needMoreData = true;
1522 // }
1523 // return - 1;
1524 // }
1525 // next.nextRemoved -= srcLen;
1526 // Array.Copy(src, srcOff, next.buf, next.nextRemoved, srcLen);
1527 // recycleBuffer(buf, false);
1528 // inQueueHead = next;
1529 // return readChars(obj, charsToRead);
1530 //}
1531  
1532 dstRead.i = dstWrote.i;
1533 if ( translateEOL( dst, dstOff, dst, dstOff, dstWrote, dstRead ) != 0 )
1534 {
1535 // Hit EOF char. How many bytes of src correspond to where the
1536 // EOF was located in dst? Run the conversion again with an
1537 // output buffer just big enough to hold the data so we can
1538 // get the correct value for srcRead.
1539  
1540 if ( dstWrote.i == 0 )
1541 {
1542 return -1;
1543 }
1544 encodingState = oldState;
1545 if ( btc != null )
1546 {
1547 btc = this.encoding.GetDecoder();
1548 }
1549 externalToUnicode( src, srcOff, srcLen, dst, dstOff, dstRead.i, srcRead, dstWrote, numChars );
1550 translateEOL( dst, dstOff, dst, dstOff, dstWrote, dstRead );
1551 }
1552  
1553 // The number of characters that we got may be less than the number
1554 // that we started with because "\r\n" sequences may have been
1555 // turned into just '\n' in dst.
1556  
1557 numChars.i -= ( dstRead.i - dstWrote.i );
1558  
1559 if ( numChars.i > toRead )
1560 {
1561 // Got too many chars.
1562  
1563 int eof;
1564 eof = toRead;
1565 encodingState = oldState;
1566 if ( btc != null )
1567 {
1568 btc = this.encoding.GetDecoder();
1569 }
1570 externalToUnicode( src, srcOff, srcLen, dst, dstOff, ( eof - dstOff ), srcRead, dstWrote, numChars );
1571 dstRead.i = dstWrote.i;
1572 translateEOL( dst, dstOff, dst, dstOff, dstWrote, dstRead );
1573 numChars.i -= ( dstRead.i - dstWrote.i );
1574 }
1575 encodingStart = false;
1576  
1577 buf.nextRemoved += srcRead.i;
1578  
1579 TclString.append( obj, dst, dstOff, numChars.i );
1580  
1581 return numChars.i;
1582 }
1583  
1584 // FIXME: Only define the ones that we actually need/use.
1585  
1586 // The following definitions are the error codes returned by externalToUnicode
1587 //
1588 // TCL_OK: All characters were converted.
1589 //
1590 // TCL_CONVERT_NOSPACE: The output buffer would not have been large
1591 // enough for all of the converted data; as many
1592 // characters as could fit were converted though.
1593 //
1594 // TCL_CONVERT_MULTIBYTE: The last few bytes in the source string were
1595 // the beginning of a multibyte sequence, but
1596 // more bytes were needed to complete this
1597 // sequence. A subsequent call to the conversion
1598 // routine should pass the beginning of this
1599 // unconverted sequence plus additional bytes
1600 // from the source stream to properly convert
1601 // the formerly split-up multibyte sequence.
1602 //
1603 // TCL_CONVERT_SYNTAX: The source stream contained an invalid
1604 // character sequence. This may occur if the
1605 // input stream has been damaged or if the input
1606 // encoding method was misidentified. This error
1607 // is reported only if TCL_ENCODING_STOPONERROR
1608 // was specified.
1609 //
1610 // TCL_CONVERT_UNKNOWN: The source string contained a character
1611 // that could not be represented in the target
1612 // encoding. This error is reported only if
1613 // TCL_ENCODING_STOPONERROR was specified.
1614  
1615 private int TCL_CONVERT_MULTIBYTE = -1;
1616 private int TCL_CONVERT_SYNTAX = -2;
1617 private int TCL_CONVERT_UNKNOWN = -3;
1618 private int TCL_CONVERT_NOSPACE = -4;
1619  
1620 /// <summary> Tcl_ExternalToUtf -> externalToUnicode
1621 ///
1622 /// Convert a source buffer from the specified encoding into Unicode.
1623 ///
1624 /// FIXME: Add doc for return values
1625 ///
1626 /// </summary>
1627 /// <param name="src,"> Source bytes in specified encoding.
1628 /// </param>
1629 /// <param name="srcOff,"> First index in src input array.
1630 /// </param>
1631 /// <param name="srcLen,"> Number of bytes in src buffer.
1632 /// </param>
1633 /// <param name="dst,"> Array to store unicode characters in.
1634 /// </param>
1635 /// <param name="dstOff,"> First available index in dst array.
1636 /// </param>
1637 /// <param name="dstLen,"> Length of dst array.
1638 /// </param>
1639 /// <param name="srcReadPtr,"> Filled with the number of bytes from
1640 /// the source string that were converted.
1641 /// This may be less than the original source
1642 /// length if there was a problem converting
1643 /// some source characters.
1644 /// </param>
1645 /// <param name="dstWrotePtr,">Filled with the number of chars that were
1646 /// stored in the output buffer as a result of
1647 /// the conversion
1648 /// </param>
1649 /// <param name="dstCharsPtr,">Filled with the number of characters that
1650 /// correspond to the bytes stored in the
1651 /// output buffer.
1652 /// </param>
1653  
1654 internal int externalToUnicode( byte[] src, int srcOff, int srcLen, char[] dst, int dstOff, int dstLen, IntPtr srcReadPtr, IntPtr dstWrotePtr, IntPtr dstCharsPtr )
1655 {
1656 System.Text.Encoding encoding = this.encoding;
1657 int result;
1658 //Object state;
1659 //String encoded_string;
1660  
1661 if ( (System.Object)encoding == null )
1662 {
1663 // This should never happen
1664 //encoding = Encoding.getJavaName("identity");
1665 throw new TclRuntimeError( "externalToUnicode called with null encoding" );
1666 }
1667  
1668 // FIXME: This may no longer be needed after Tcl srcLen == 0 patch
1669  
1670 if ( srcLen == 0 )
1671 {
1672 srcReadPtr.i = 0;
1673 if ( dstWrotePtr != null )
1674 dstWrotePtr.i = 0;
1675 if ( dstCharsPtr != null )
1676 dstCharsPtr.i = 0;
1677 return 0;
1678 }
1679  
1680 // Convert bytes from src into unicode chars and store them in dst.
1681  
1682 // FIXME: This allocated a buffer for the String and then copies the
1683 // encoded data into a second buffer. Need to decode the data directly
1684 // into the dst array since this is performance critical.
1685  
1686 #if DEBUG
1687 System.Diagnostics.Debug.WriteLine("now to decode byte array of length " + srcLen);
1688 System.Diagnostics.Debug.WriteLine("srcOff is " + srcOff);
1689 for (int i = srcOff; i < (srcOff + srcLen); i++)
1690 {
1691 System.Diagnostics.Debug.WriteLine("(byte) '" + ((char) src[i]) + "'");
1692 }
1693 System.Diagnostics.Debug.WriteLine("encoded as " + encoding);
1694 #endif
1695  
1696 // FIXME: In the cases where we know that we don't actually want
1697 // to copy the data, we could pass a flag so that we could
1698 // take advantage of encodings that had a one to one mapping
1699 // from bytes to chars (now need to copy then to find bytes used).
1700  
1701 if ( btc == null )
1702 {
1703 try
1704 {
1705 btc = this.encoding.GetDecoder();
1706 }
1707 catch ( IOException ex )
1708 {
1709 // Valid encodings should be checked already
1710 throw new TclRuntimeError( "unsupported encoding \"" + encoding + "\"" );
1711 }
1712 }
1713  
1714 int bytes_read, chars_written;
1715  
1716 int required_chars = btc.GetCharCount( src, srcOff, srcLen );
1717 if ( required_chars > dstLen )
1718 {
1719 srcLen = dstLen;
1720 }
1721 chars_written = btc.GetChars( src, srcOff, srcLen, dst, dstOff );
1722 bytes_read = srcLen;
1723  
1724 srcReadPtr.i = bytes_read;
1725 if ( dstWrotePtr != null )
1726 dstWrotePtr.i = chars_written;
1727 if ( dstCharsPtr != null )
1728 dstCharsPtr.i = chars_written;
1729  
1730 // FIXME: When do we return error codes?
1731 result = 0;
1732  
1733 return result;
1734 }
1735  
1736 /// <summary> RecycleBuffer -> recycleBuffer
1737 ///
1738 /// Helper function to recycle input buffers. Ensures that
1739 /// two input buffers are saved (one in the input queue and
1740 /// another in the saveInBuf field). Only if these conditions
1741 /// are met is the buffer released so that it can be
1742 /// garbage collected.
1743 /// </summary>
1744  
1745 private void recycleBuffer( ChannelBuffer buf, bool mustDiscard )
1746 {
1747  
1748 if ( mustDiscard )
1749 return;
1750  
1751 // Only save buffers which are at least as big as the requested
1752 // buffersize for the channel. This is to honor dynamic changes
1753 // of the buffersize made by the user.
1754  
1755 if ( ( buf.bufLength - tcl.lang.ChannelBuffer.BUFFER_PADDING ) < bufSize )
1756 {
1757 return;
1758 }
1759  
1760 if ( inQueueHead == null )
1761 {
1762 inQueueHead = buf;
1763 inQueueTail = buf;
1764  
1765 buf.nextRemoved = tcl.lang.ChannelBuffer.BUFFER_PADDING;
1766 buf.nextAdded = tcl.lang.ChannelBuffer.BUFFER_PADDING;
1767 buf.next = null;
1768 return;
1769 }
1770 if ( saveInBuf == null )
1771 {
1772 saveInBuf = buf;
1773  
1774 buf.nextRemoved = tcl.lang.ChannelBuffer.BUFFER_PADDING;
1775 buf.nextAdded = tcl.lang.ChannelBuffer.BUFFER_PADDING;
1776 buf.next = null;
1777 return;
1778 }
1779 }
1780  
1781 /// <summary> DiscardInputQueued -> discardQueued
1782 ///
1783 /// Discards any input read from the channel but not yet consumed
1784 /// by Tcl reading commands.
1785 /// </summary>
1786  
1787 private void discardQueued( bool discardSavedBuffers )
1788 {
1789 ChannelBuffer buf, nxt;
1790  
1791 buf = inQueueHead;
1792 inQueueHead = null;
1793 inQueueTail = null;
1794 for ( ; buf != null; buf = nxt )
1795 {
1796 nxt = buf.next;
1797 recycleBuffer( buf, discardSavedBuffers );
1798 }
1799  
1800 // If discardSavedBuffers is true, must also discard any previously
1801 // saved buffer in the saveInBuf field.
1802  
1803 if ( discardSavedBuffers )
1804 {
1805 if ( saveInBuf != null )
1806 {
1807 saveInBuf = null;
1808 }
1809 }
1810 }
1811  
1812 /// <summary> TranslateInputEOL -> translateEOL
1813 ///
1814 /// Perform input EOL and EOF translation on the source buffer,
1815 /// leaving the translated result in the destination buffer.
1816 ///
1817 /// Results:
1818 /// The return value is 1 if the EOF character was found when
1819 /// copying bytes to the destination buffer, 0 otherwise.
1820 ///
1821 /// </summary>
1822 /// <param name="dstArray,">Output buffer to fill with translated bytes or chars.
1823 /// </param>
1824 /// <param name="dstStart,">First unused index in the dst output array.
1825 /// </param>
1826 /// <param name="srcArray,">Input buffer that holds the bytes or chars to translate
1827 /// </param>
1828 /// <param name="srcStart,">Index of first available byte in src array.
1829 /// </param>
1830 /// <param name="dstLenPtr,">On entry, the maximum length of output
1831 /// buffer in bytes or chars; must be <= srcLenPtr.i. On
1832 /// exit, the number of bytes or chars actually used in
1833 /// output buffer.
1834 /// </param>
1835 /// <param name="srcLenPtr,">On entry, the length of source buffer.
1836 /// On exit, the number of bytes or chars read from
1837 /// the source buffer.
1838 /// </param>
1839  
1840 internal int translateEOL( System.Object dstArray, int dstStart, Object srcArray, int srcStart, IntPtr dstLenPtr, IntPtr srcLenPtr )
1841 {
1842  
1843 // Figure out if the srcArray and dstArray buffers
1844 // are byte or char arrays.
1845 bool isCharType;
1846 char[] srcArrayChar, dstArrayChar;
1847 byte[] srcArrayByte, dstArrayByte;
1848  
1849 if ( ( srcArray is char[] ) && ( dstArray is char[] ) )
1850 {
1851 isCharType = true;
1852 srcArrayChar = (char[])srcArray;
1853 dstArrayChar = (char[])dstArray;
1854 srcArrayByte = null;
1855 dstArrayByte = null;
1856 }
1857 else if ( ( srcArray is byte[] ) && ( dstArray is byte[] ) )
1858 {
1859 isCharType = false;
1860 srcArrayChar = null;
1861 dstArrayChar = null;
1862 srcArrayByte = (byte[])srcArray;
1863 dstArrayByte = (byte[])dstArray;
1864 }
1865 else
1866 {
1867 throw new TclRuntimeError( "unknown array argument types" );
1868 }
1869  
1870 int dstLen, srcLen, inEofChar, index;
1871 int eof;
1872  
1873 dstLen = dstLenPtr.i;
1874  
1875 eof = -1;
1876 inEofChar = eofChar;
1877 if ( inEofChar != '\x0000' )
1878 {
1879 // Find EOF in translated buffer then compress out the EOL. The
1880 // source buffer may be much longer than the destination buffer --
1881 // we only want to return EOF if the EOF has been copied to the
1882 // destination buffer.
1883  
1884 int src, srcMax;
1885  
1886 srcMax = srcStart + srcLenPtr.i;
1887 for ( src = srcStart; src < srcMax; src++ )
1888 {
1889 if ( isCharType )
1890 {
1891 index = srcArrayChar[src];
1892 }
1893 else
1894 {
1895 index = srcArrayByte[src];
1896 }
1897 if ( index == inEofChar )
1898 {
1899 eof = src;
1900 srcLen = src - srcStart;
1901 if ( srcLen < dstLen )
1902 {
1903 dstLen = srcLen;
1904 }
1905 srcLenPtr.i = srcLen;
1906 break;
1907 }
1908 }
1909 }
1910 switch ( translation )
1911 {
1912  
1913 case TclIO.TRANS_LF:
1914 {
1915 if ( ( dstArray != srcArray ) || ( ( dstArray == srcArray ) && ( dstStart != srcStart ) ) )
1916 {
1917 Array.Copy( (System.Array)srcArray, srcStart, (System.Array)dstArray, dstStart, dstLen );
1918 }
1919 srcLen = dstLen;
1920 break;
1921 }
1922  
1923 case TclIO.TRANS_CR:
1924 {
1925 int dst, dstEnd;
1926  
1927 if ( ( dstArray != srcArray ) || ( ( dstArray == srcArray ) && ( dstStart != srcStart ) ) )
1928 {
1929 Array.Copy( (System.Array)srcArray, srcStart, (System.Array)dstArray, dstStart, dstLen );
1930 }
1931 dstEnd = dstStart + dstLen;
1932 if ( isCharType )
1933 {
1934 for ( dst = dstStart; dst < dstEnd; dst++ )
1935 {
1936 if ( dstArrayChar[dst] == '\r' )
1937 {
1938 dstArrayChar[dst] = '\n';
1939 }
1940 }
1941 }
1942 else
1943 {
1944 for ( dst = dstStart; dst < dstEnd; dst++ )
1945 {
1946 if ( dstArrayByte[dst] == '\r' )
1947 {
1948 dstArrayByte[dst] = (byte)SupportClass.Identity( '\n' );
1949 }
1950 }
1951 }
1952 srcLen = dstLen;
1953 break;
1954 }
1955  
1956 case TclIO.TRANS_CRLF:
1957 {
1958 int dst;
1959 int src, srcEnd, srcMax;
1960  
1961 dst = dstStart;
1962 src = srcStart;
1963 srcEnd = srcStart + dstLen;
1964 srcMax = srcStart + srcLenPtr.i;
1965  
1966 if ( isCharType )
1967 {
1968 for ( ; src < srcEnd; )
1969 {
1970 if ( srcArrayChar[src] == '\r' )
1971 {
1972 src++;
1973 if ( src >= srcMax )
1974 {
1975 needNL = true;
1976 }
1977 else if ( srcArrayChar[src] == '\n' )
1978 {
1979 dstArrayChar[dst++] = srcArrayChar[src++];
1980 }
1981 else
1982 {
1983 dstArrayChar[dst++] = '\r';
1984 }
1985 }
1986 else
1987 {
1988 dstArrayChar[dst++] = srcArrayChar[src++];
1989 }
1990 }
1991 }
1992 else
1993 {
1994 for ( ; src < srcEnd; )
1995 {
1996 if ( srcArrayByte[src] == '\r' )
1997 {
1998 src++;
1999 if ( src >= srcMax )
2000 {
2001 needNL = true;
2002 }
2003 else if ( srcArrayByte[src] == '\n' )
2004 {
2005 dstArrayByte[dst++] = srcArrayByte[src++];
2006 }
2007 else
2008 {
2009 dstArrayByte[dst++] = (byte)SupportClass.Identity( '\r' );
2010 }
2011 }
2012 else
2013 {
2014 dstArrayByte[dst++] = srcArrayByte[src++];
2015 }
2016 }
2017 }
2018  
2019 srcLen = src - srcStart;
2020 dstLen = dst - dstStart;
2021 break;
2022 }
2023  
2024 case TclIO.TRANS_AUTO:
2025 {
2026 int dst;
2027 int src, srcEnd, srcMax;
2028  
2029 dst = dstStart;
2030 src = srcStart;
2031 srcEnd = srcStart + dstLen;
2032 srcMax = srcStart + srcLenPtr.i;
2033  
2034 if ( sawCR_Renamed_Field && ( src < srcMax ) )
2035 {
2036 if ( isCharType )
2037 {
2038 index = srcArrayChar[src];
2039 }
2040 else
2041 {
2042 index = srcArrayByte[src];
2043 }
2044 if ( index == '\n' )
2045 {
2046 src++;
2047 }
2048 sawCR_Renamed_Field = false;
2049 }
2050 if ( isCharType )
2051 {
2052 for ( ; src < srcEnd; )
2053 {
2054 if ( srcArrayChar[src] == '\r' )
2055 {
2056 src++;
2057 if ( src >= srcMax )
2058 {
2059 sawCR_Renamed_Field = true;
2060 }
2061 else if ( srcArrayChar[src] == '\n' )
2062 {
2063 if ( srcEnd < srcMax )
2064 {
2065 srcEnd++;
2066 }
2067 src++;
2068 }
2069 dstArrayChar[dst++] = '\n';
2070 }
2071 else
2072 {
2073 dstArrayChar[dst++] = srcArrayChar[src++];
2074 }
2075 }
2076 }
2077 else
2078 {
2079 for ( ; src < srcEnd; )
2080 {
2081 if ( srcArrayByte[src] == '\r' )
2082 {
2083 src++;
2084 if ( src >= srcMax )
2085 {
2086 sawCR_Renamed_Field = true;
2087 }
2088 else if ( srcArrayByte[src] == '\n' )
2089 {
2090 if ( srcEnd < srcMax )
2091 {
2092 srcEnd++;
2093 }
2094 src++;
2095 }
2096 dstArrayByte[dst++] = (byte)SupportClass.Identity( '\n' );
2097 }
2098 else
2099 {
2100 dstArrayByte[dst++] = srcArrayByte[src++];
2101 }
2102 }
2103 }
2104 srcLen = src - srcStart;
2105 dstLen = dst - dstStart;
2106 break;
2107 }
2108  
2109 default:
2110 {
2111 throw new TclRuntimeError( "invalid translation" );
2112 }
2113  
2114 }
2115 dstLenPtr.i = dstLen;
2116  
2117 if ( ( eof != -1 ) && ( srcStart + srcLen >= eof ) )
2118 {
2119 // EOF character was seen in EOL translated range. Leave current
2120 // file position pointing at the EOF character, but don't store the
2121 // EOF character in the output string.
2122  
2123 eofCond = true;
2124 stickyEofCond = true;
2125 encodingEnd = true;
2126 sawCR_Renamed_Field = false;
2127 needNL = false;
2128 return 1;
2129 }
2130  
2131 srcLenPtr.i = srcLen;
2132 return 0;
2133 }
2134  
2135 /// <summary> UpdateInterest -> updateInterest
2136 ///
2137 /// Arrange for the notifier to call us back at appropriate times
2138 /// based on the current state of the channel.
2139 /// </summary>
2140  
2141 internal void updateInterest()
2142 {
2143 // FIXME: Currently unimplemented
2144 }
2145  
2146 /// <summary> seekReset
2147 ///
2148 /// Helper method used to reset state info when doing a seek.
2149 /// </summary>
2150  
2151 internal void seekReset()
2152 {
2153 discardQueued( false );
2154 eofCond = false;
2155 stickyEofCond = false;
2156 blocked = false;
2157 sawCR_Renamed_Field = false;
2158 // FIXME: Change needed in Tcl
2159 //needNL = false;
2160 }
2161 }
2162 }