wasCSharpSQLite – Blame information for rev 1

Subversion Repositories:
Rev:
Rev Author Line No. Line
1 office 1 #undef DEBUG
2 /*
3 * TclOutputStream.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: TclOutputStream.java,v 1.1 2003/03/08 03:42:44 mdejong Exp $
14 */
15  
16 // A TclOutputStream is a cross between a Java OutputStream and
17 // a Writer. The class supports writing raw bytes as well as
18 // encoded characters.
19 using System;
20 using System.IO;
21 using System.Text;
22  
23 namespace tcl.lang
24 {
25  
26 public class TclOutputStream
27 {
28 internal System.Text.Encoding Encoding
29 {
30 set
31 {
32 encoding = value;
33 }
34  
35 }
36 internal char EofChar
37 {
38 set
39 {
40 eofChar = value;
41 }
42  
43 }
44 internal int Translation
45 {
46 set
47 {
48 translation = value;
49 }
50  
51 }
52 public int Buffering
53 {
54 set
55 {
56 buffering = value;
57 }
58  
59 }
60 public int BufferSize
61 {
62 set
63 {
64 bufSize = value;
65 outputStage = null;
66 }
67  
68 }
69 public bool Blocking
70 {
71 set
72 {
73 blocking = value;
74 }
75  
76 }
77 public bool Blocked
78 {
79 get
80 {
81 return blocked;
82 }
83  
84 }
85 /// <summary> Tcl_OutputBuffered -> getNumBufferedBytes
86 ///
87 /// Return the number of bytes that are current buffered.
88 /// </summary>
89 internal int NumBufferedBytes
90 {
91  
92  
93 get
94 {
95 ChannelBuffer buf;
96 int IOQueued = 0;
97 for ( buf = outQueueHead; buf != null; buf = buf.next )
98 {
99 IOQueued += buf.nextAdded - buf.nextRemoved;
100 }
101 if ( ( curOut != null ) && ( curOut.nextAdded > curOut.nextRemoved ) )
102 {
103 //bufferReady = true;
104 IOQueued += curOut.nextAdded - curOut.nextRemoved;
105 }
106 return IOQueued;
107 }
108  
109 }
110  
111 /// <summary> The Java byte stream object data will be written to.</summary>
112  
113 private Stream output;
114  
115 /// <summary> If nonzero, use this character as EOF marker.</summary>
116  
117 private char eofChar;
118  
119 /// <summary> Translation mode for end-of-line character</summary>
120  
121 protected internal int translation;
122  
123 /// <summary> Name of Java encoding for this Channel.
124 /// A null value means use no encoding (binary).
125 /// </summary>
126  
127 protected internal System.Text.Encoding encoding;
128  
129 /// <summary> Current converter object. A null value means
130 /// that no conversions have been done yet.
131 /// </summary>
132  
133 protected internal Encoder ctb = null;
134  
135 /// <summary> Buffering</summary>
136  
137 protected internal int buffering;
138  
139 /// <summary> Blocking</summary>
140  
141 protected internal bool blocking;
142  
143 /// <summary> Blocked</summary>
144  
145 protected internal bool blocked = false;
146  
147 /// <summary> Buffer size in bytes</summary>
148  
149 protected internal int bufSize;
150  
151 /// <summary> Staging area used to store chars before conversion into
152 /// buffered bytes.
153 /// </summary>
154  
155 protected internal char[] outputStage = null;
156  
157 /// <summary> Flags used to track encoding states.
158 /// The encodingState member of called outputEncodingState
159 /// in the C ChannelState type. The encodingStart and encodingEnd
160 /// members combined are called outputEncodingFlags
161 /// and have the bit values TCL_ENCODING_END and TCL_ENCODING_START.
162 /// </summary>
163  
164 internal Object encodingState = null;
165 internal bool encodingStart = true;
166 internal bool encodingEnd = false;
167  
168 /// <summary> First and last buffers in the output queue and
169 /// the current buffer being filled.
170 /// </summary>
171  
172 internal ChannelBuffer outQueueHead = null;
173 internal ChannelBuffer outQueueTail = null;
174 internal ChannelBuffer curOut = null;
175  
176  
177 /// <summary> Used to track buffer state, these are bit flags stored
178 /// in the flags filed in the C impl.
179 /// </summary>
180  
181 protected internal bool bufferReady = false;
182 protected internal bool bgFlushScheduled = false;
183 protected internal bool closed = false;
184  
185 /// <summary> Posix error code of deferred error.</summary>
186 protected internal int unreportedError = 0;
187  
188 /// <summary> FIXME: add desc</summary>
189  
190 protected internal int refCount = 0;
191  
192 /// <summary> Constructor for Tcl input stream class. We require
193 /// a byte stream source at init time, the stram can't
194 /// be changed after the TclInputStream is created.
195 /// </summary>
196  
197 internal TclOutputStream( Stream inOutput )
198 {
199 output = inOutput;
200 }
201  
202 /// <summary> Tcl_Close -> close
203 ///
204 /// Closes a channel.
205 ///
206 /// Closes the channel if this is the last reference.
207 ///
208 /// close removes the channel as far as the user is concerned.
209 /// However, it may continue to exist for a while longer if it has
210 /// a background flush scheduled. The device itself is eventually
211 /// closed and the channel record removed, in closeChannel.
212 /// </summary>
213  
214 internal void close()
215 {
216 //CloseCallback *cbPtr;
217 //Channel *chanPtr;
218 //ChannelState *statePtr;
219 int result;
220  
221 // Perform special handling for standard channels being closed. If the
222 // refCount is now 1 it means that the last reference to the standard
223 // channel is being explicitly closed, so bump the refCount down
224 // artificially to 0. This will ensure that the channel is actually
225 // closed, below. Also set the static pointer to NULL for the channel.
226  
227 //CheckForStdChannelsBeingClosed();
228  
229 // This operation should occur at the top of a channel stack.
230  
231 //chanPtr = (Channel *) chan;
232 //statePtr = chanPtr->state;
233 //chanPtr = statePtr->topChanPtr;
234  
235 if ( refCount > 0 )
236 {
237 throw new TclRuntimeError( "called Tcl_Close on channel with refCount > 0" );
238 }
239  
240 // When the channel has an escape sequence driven encoding such as
241 // iso2022, the terminated escape sequence must write to the buffer.
242  
243 if ( ( (System.Object)encoding != null ) && ( curOut != null ) )
244 {
245 encodingEnd = true;
246 // FIXME : Make sure this flushes the CharToByteConverter
247 char[] empty = new char[0];
248 writeChars( empty, 0, 0 );
249 }
250  
251 // FIXME: Impl channel close callbacks ???
252 //Tcl_ClearChannelHandlers(chan);
253  
254 // Invoke the registered close callbacks and delete their records.
255  
256 //while (statePtr->closeCbPtr != (CloseCallback *) NULL) {
257 // cbPtr = statePtr->closeCbPtr;
258 // statePtr->closeCbPtr = cbPtr->nextPtr;
259 // (cbPtr.proc) (cbPtr->clientData);
260 // ckfree((char *) cbPtr);
261 //}
262  
263 // Ensure that the last output buffer will be flushed.
264  
265 if ( ( curOut != null ) && ( curOut.nextAdded > curOut.nextRemoved ) )
266 {
267 bufferReady = true;
268 }
269  
270 // If this channel supports it, close the read side, since we don't need it
271 // anymore and this will help avoid deadlocks on some channel types.
272  
273 //if (chanPtr->typePtr->closeProc == TCL_CLOSE2PROC) {
274 // result = (chanPtr->typePtr->close2Proc)(chanPtr->instanceData, interp,
275 // TCL_CLOSE_READ);
276 //} else {
277 // result = 0;
278 //}
279 result = 0;
280  
281 // The call to flushChannel will flush any queued output and invoke
282 // the close function of the channel driver, or it will set up the
283 // channel to be flushed and closed asynchronously.
284  
285 closed = true;
286 if ( ( flushChannel( null, false ) != 0 ) || ( result != 0 ) )
287 {
288 // FIXME: We should raise a TclPosixException here instead
289 //return TCL.TCL_ERROR;
290 throw new IOException( "Exception in flushChannel" );
291 }
292 }
293  
294 /// <summary> CloseChannel -> closeChannel
295 ///
296 /// Utility procedure to close a channel and free associated resources.
297 ///
298 /// If the channel was stacked, then the it will copy the necessary
299 /// elements of the NEXT channel into the TOP channel, in essence
300 /// unstacking the channel. The NEXT channel will then be freed.
301 ///
302 /// If the channel was not stacked, then we will free all the bits
303 /// for the TOP channel, including the data structure itself.
304 ///
305 /// Returns 1 if the channel was stacked, 0 otherwise.
306 /// </summary>
307  
308 protected internal int closeChannel( Interp interp, int errorCode )
309 {
310 int result = 0;
311 //ChannelState *statePtr; // state of the channel stack.
312 //ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
313  
314 //if (chanPtr == NULL) {
315 // return result;
316 //}
317 //statePtr = chanPtr->state;
318  
319 // Discard a leftover buffer in the current output buffer field.
320  
321 if ( curOut != null )
322 {
323 //ckfree((char *) statePtr->curOutPtr);
324 curOut = null;
325 }
326  
327 // The caller guarantees that there are no more buffers
328 // queued for output.
329  
330 if ( outQueueHead != null )
331 {
332 throw new TclRuntimeError( "TclFlush, closed channel: queued output left" );
333 }
334  
335 // If the EOF character is set in the channel, append that to the
336 // output device.
337  
338 if ( eofChar != 0 )
339 {
340 try
341 {
342 output.WriteByte( (byte)eofChar );
343 }
344 catch ( IOException ex )
345 {
346 // FIXME: How can we recover here??
347 SupportClass.WriteStackTrace( ex, Console.Error );
348 }
349 }
350  
351 // Remove this channel from of the list of all channels.
352  
353 //Tcl_CutChannel((Tcl_Channel) chanPtr);
354  
355 // Close and free the channel driver state.
356  
357 //if (chanPtr->typePtr->closeProc != TCL_CLOSE2PROC) {
358 // result = (chanPtr->typePtr->closeProc)(chanPtr->instanceData, interp);
359 //} else {
360 // result = (chanPtr->typePtr->close2Proc)(chanPtr->instanceData, interp,
361 // 0);
362 //}
363  
364 // Some resources can be cleared only if the bottom channel
365 // in a stack is closed. All the other channels in the stack
366 // are not allowed to remove.
367  
368 //if (chanPtr == statePtr->bottomChanPtr) {
369 // if (statePtr->channelName != (char *) NULL) {
370 // ckfree((char *) statePtr->channelName);
371 // statePtr->channelName = NULL;
372 // }
373  
374 // Tcl_FreeEncoding(statePtr->encoding);
375 // if (statePtr->outputStage != NULL) {
376 // ckfree((char *) statePtr->outputStage);
377 // statePtr->outputStage = (char *) NULL;
378 // }
379 //}
380  
381 // If we are being called synchronously, report either
382 // any latent error on the channel or the current error.
383  
384 if ( unreportedError != 0 )
385 {
386 errorCode = unreportedError;
387 }
388 if ( errorCode == 0 )
389 {
390 errorCode = result;
391 if ( errorCode != 0 )
392 {
393 // FIXME: How can we deal with this errno issue?
394 //Tcl_SetErrno(errorCode);
395 }
396 }
397  
398 // Cancel any outstanding timer.
399  
400 //Tcl_DeleteTimerHandler(statePtr->timer);
401  
402 // Mark the channel as deleted by clearing the type structure.
403  
404 //if (chanPtr->downChanPtr != (Channel *) NULL) {
405 // Channel *downChanPtr = chanPtr->downChanPtr;
406  
407 // statePtr->nextCSPtr = tsdPtr->firstCSPtr;
408 // tsdPtr->firstCSPtr = statePtr;
409  
410 // statePtr->topChanPtr = downChanPtr;
411 // downChanPtr->upChanPtr = (Channel *) NULL;
412 // chanPtr->typePtr = NULL;
413  
414 // Tcl_EventuallyFree((ClientData) chanPtr, TCL_DYNAMIC);
415 // return Tcl_Close(interp, (Tcl_Channel) downChanPtr);
416 //}
417  
418 // There is only the TOP Channel, so we free the remaining
419 // pointers we have and then ourselves. Since this is the
420 // last of the channels in the stack, make sure to free the
421 // ChannelState structure associated with it. We use
422 // Tcl_EventuallyFree to allow for any last
423  
424 //chanPtr->typePtr = NULL;
425  
426 //Tcl_EventuallyFree((ClientData) statePtr, TCL_DYNAMIC);
427 //Tcl_EventuallyFree((ClientData) chanPtr, TCL_DYNAMIC);
428  
429 return errorCode;
430 }
431  
432 /// <summary> Tcl_Flush -> flush
433 ///
434 /// Flushes output data on a channel.
435 /// </summary>
436  
437 internal void flush()
438 {
439 // Force current output buffer to be output also.
440  
441 if ( ( curOut != null ) && ( curOut.nextAdded > curOut.nextRemoved ) )
442 {
443 bufferReady = true;
444 }
445  
446 int result = flushChannel( null, false );
447 if ( result != 0 )
448 {
449 // FIXME: Should we throw an exception here?
450 throw new IOException( "Exception during flushChannel" );
451 }
452 // ATK .NET has own buffer also we need to Flush the
453 // IO.Stream too
454 output.Flush();
455 }
456  
457 /// <summary> FlushChannel -> flushChannel
458 ///
459 /// This function flushes as much of the queued output as is possible
460 /// now. If calledFromAsyncFlush is true, it is being called in an
461 /// event handler to flush channel output asynchronously.
462 ///
463 /// Return 0 if successful, else the error code that was returned by the
464 /// channel type operation.
465 ///
466 /// May produce output on a channel. May block indefinitely if the
467 /// channel is synchronous. May schedule an async flush on the channel.
468 /// May recycle memory for buffers in the output queue.
469 ///
470 /// </summary>
471 /// <param name="interp"> Interp object.
472 /// </param>
473 /// <param name="calledFromAsyncFlush"> True if called from an asynchronous
474 /// flush callback.
475 /// </param>
476  
477 internal int flushChannel( Interp interp, bool calledFromAsyncFlush )
478 {
479 //ChannelState *statePtr = chanPtr->state;
480 ChannelBuffer buf;
481 int toWrite; // Amount of output data in current
482 // buffer available to be written.
483 int written; // Amount of output data actually
484 // written in current round.
485 int errorCode = 0; // Stores POSIX error codes from
486 // channel driver operations.
487 bool wroteSome = false; // Set to true if any data was
488 // written to the driver.
489  
490 // Prevent writing on a dead channel -- a channel that has been closed
491 // but not yet deallocated. This can occur if the exit handler for the
492 // channel deallocation runs before all channels are deregistered in
493 // all interpreters.
494  
495 //if (CheckForDeadChannel(interp, statePtr)) return -1;
496  
497 // Loop over the queued buffers and attempt to flush as
498 // much as possible of the queued output to the channel.
499  
500 while ( true )
501 {
502 // If the queue is empty and there is a ready current buffer, OR if
503 // the current buffer is full, then move the current buffer to the
504 // queue.
505  
506 if ( ( ( curOut != null ) && ( curOut.nextAdded == curOut.bufLength ) ) || ( bufferReady && ( outQueueHead == null ) ) )
507 {
508 bufferReady = false;
509 curOut.next = null;
510 if ( outQueueHead == null )
511 {
512 outQueueHead = curOut;
513 }
514 else
515 {
516 outQueueTail.next = curOut;
517 }
518 outQueueTail = curOut;
519 curOut = null;
520 }
521 buf = outQueueHead;
522  
523 // If we are not being called from an async flush and an async
524 // flush is active, we just return without producing any output.
525  
526 if ( ( !calledFromAsyncFlush ) && bgFlushScheduled )
527 {
528 return 0;
529 }
530  
531 // If the output queue is still empty, break out of the while loop.
532  
533 if ( buf == null )
534 {
535 break; // Out of the "while (1)".
536 }
537  
538 // Produce the output on the channel.
539  
540 toWrite = buf.nextAdded - buf.nextRemoved;
541 //written = (chanPtr->typePtr->outputProc) (chanPtr->instanceData,
542 // bufPtr->buf + bufPtr->nextRemoved, toWrite,
543 // &errorCode);
544 try
545 {
546 output.Write( buf.buf, buf.nextRemoved, toWrite );
547 written = toWrite;
548 }
549 catch ( IOException ex )
550 {
551 // FIXME: How can we recover and get posix errors?
552 SupportClass.WriteStackTrace( ex, System.Console.Error );
553 errorCode = TclPosixException.EIO; // Generic I/O error ???
554 written = -1;
555 }
556  
557 // If the write failed completely attempt to start the asynchronous
558 // flush mechanism and break out of this loop - do not attempt to
559 // write any more output at this time.
560  
561 if ( written < 0 )
562 {
563 // If the last attempt to write was interrupted, simply retry.
564  
565 if ( errorCode == TclPosixException.EINTR )
566 {
567 errorCode = 0;
568 continue;
569 }
570  
571 // If the channel is non-blocking and we would have blocked,
572 // start a background flushing handler and break out of the loop.
573  
574 if ( ( errorCode == TclPosixException.EWOULDBLOCK ) || ( errorCode == TclPosixException.EAGAIN ) )
575 {
576 // This used to check for CHANNEL_NONBLOCKING, and panic
577 // if the channel was blocking. However, it appears
578 // that setting stdin to -blocking 0 has some effect on
579 // the stdout when it's a tty channel (dup'ed underneath)
580  
581 if ( !bgFlushScheduled )
582 {
583 bgFlushScheduled = true;
584 updateInterest();
585 }
586 errorCode = 0;
587 break;
588 }
589  
590 // Decide whether to report the error upwards or defer it.
591  
592 if ( calledFromAsyncFlush )
593 {
594 if ( unreportedError == 0 )
595 {
596 unreportedError = errorCode;
597 }
598 }
599 else
600 {
601 // FIXME: Need to figure out what to do here!
602 //Tcl_SetErrno(errorCode);
603 //if (interp != NULL) {
604 // // Casting away CONST here is safe because the
605 // // TCL_VOLATILE flag guarantees CONST treatment
606 // // of the Posix error string.
607 // Tcl_SetResult(interp,
608 // (char *) Tcl_PosixError(interp), TCL_VOLATILE);
609 }
610  
611 // When we get an error we throw away all the output
612 // currently queued.
613  
614 discardQueued();
615 continue;
616 }
617 else
618 {
619 wroteSome = true;
620 }
621  
622 buf.nextRemoved += written;
623  
624 // If this buffer is now empty, recycle it.
625  
626 if ( buf.nextRemoved == buf.nextAdded )
627 {
628 outQueueHead = buf.next;
629 if ( outQueueHead == null )
630 {
631 outQueueTail = null;
632 }
633 recycleBuffer( buf, false );
634 }
635 } // Closes "while (1)".
636  
637 // If we wrote some data while flushing in the background, we are done.
638 // We can't finish the background flush until we run out of data and
639 // the channel becomes writable again. This ensures that all of the
640 // pending data has been flushed at the system level.
641  
642 if ( bgFlushScheduled )
643 {
644 if ( wroteSome )
645 {
646 return errorCode;
647 }
648 else if ( outQueueHead == null )
649 {
650 bgFlushScheduled = false;
651 // FIXME: What is this watchProc?
652 //(chanPtr->typePtr->watchProc)(chanPtr->instanceData,
653 // statePtr->interestMask);
654 }
655 }
656  
657 // If the channel is flagged as closed, delete it when the refCount
658 // drops to zero, the output queue is empty and there is no output
659 // in the current output buffer.
660  
661 if ( closed && ( refCount <= 0 ) && ( outQueueHead == null ) && ( ( curOut == null ) || ( curOut.nextAdded == curOut.nextRemoved ) ) )
662 {
663 return closeChannel( interp, errorCode );
664 }
665 return errorCode;
666 }
667  
668 // Helper class to implement integer pass by reference.
669  
670 public class IntPtr
671 {
672 private void InitBlock( TclOutputStream enclosingInstance )
673 {
674 this.enclosingInstance = enclosingInstance;
675 }
676 private TclOutputStream enclosingInstance;
677 public TclOutputStream Enclosing_Instance
678 {
679 get
680 {
681 return enclosingInstance;
682 }
683  
684 }
685 internal int i;
686  
687 internal IntPtr( TclOutputStream enclosingInstance )
688 {
689 InitBlock( enclosingInstance );
690 }
691  
692 internal IntPtr( TclOutputStream enclosingInstance, int value )
693 {
694 InitBlock( enclosingInstance );
695 i = value;
696 }
697 }
698  
699 /// <summary> RecycleBuffer -> recycleBuffer
700 ///
701 /// Helper function to recycle output buffers. Ensures that
702 /// that curOut is set to a buffer. Only if these conditions
703 /// are met is the buffer released so that it can be
704 /// garbage collected.
705 /// </summary>
706  
707 private void recycleBuffer( ChannelBuffer buf, bool mustDiscard )
708 {
709  
710 if ( mustDiscard )
711 return;
712  
713 // Only save buffers which are at least as big as the requested
714 // buffersize for the channel. This is to honor dynamic changes
715 // of the buffersize made by the user.
716  
717 if ( ( buf.bufLength - tcl.lang.ChannelBuffer.BUFFER_PADDING ) < bufSize )
718 {
719 return;
720 }
721  
722 if ( curOut == null )
723 {
724 curOut = buf;
725 buf.nextRemoved = tcl.lang.ChannelBuffer.BUFFER_PADDING;
726 buf.nextAdded = tcl.lang.ChannelBuffer.BUFFER_PADDING;
727 buf.next = null;
728 }
729 }
730  
731 /// <summary> DiscardOutputQueued -> discardQueued
732 ///
733 /// Discards all output queued in the output queue of a channel.
734 /// </summary>
735  
736 private void discardQueued()
737 {
738 ChannelBuffer buf;
739  
740 while ( outQueueHead != null )
741 {
742 buf = outQueueHead;
743 outQueueHead = buf.next;
744 recycleBuffer( buf, false );
745 }
746 outQueueHead = null;
747 outQueueTail = null;
748 }
749  
750 /// <summary> UpdateInterest -> updateInterest
751 ///
752 /// Arrange for the notifier to call us back at appropriate times
753 /// based on the current state of the channel.
754 /// </summary>
755  
756 internal void updateInterest()
757 {
758 // FIXME: Currently unimplemented
759 }
760  
761 /// <summary> seekCheckBuferReady
762 ///
763 /// This method is used by the seek command to check
764 /// the channel for buffered output and mark the
765 /// buffer as ready to flush if found.
766 /// </summary>
767  
768 internal void seekCheckBuferReady()
769 {
770 if ( ( curOut != null ) && ( curOut.nextAdded > curOut.nextRemoved ) )
771 {
772 bufferReady = true;
773 }
774 }
775  
776 /// <summary> TranslateOutputEOL -> translateEOL
777 ///
778 /// Helper function for writeBytes() and writeChars(). Converts the
779 /// '\n' characters in the source buffer into the appropriate EOL
780 /// form specified by the output translation mode.
781 ///
782 /// EOL translation stops either when the source buffer is empty
783 /// or the output buffer is full.
784 ///
785 /// When converting to CRLF mode and there is only 1 byte left in
786 /// the output buffer, this routine stores the '\r' in the last
787 /// byte and then stores the '\n' in the byte just past the end of the
788 /// buffer. The caller is responsible for passing in a buffer that
789 /// is large enough to hold the extra byte.
790 ///
791 /// Results:
792 ///
793 /// The return value is 1 if a '\n' was translated from the source
794 /// buffer, or 0 otherwise -- this can be used by the caller to
795 /// decide to flush a line-based channel even though the channel
796 /// buffer is not full.
797 ///
798 /// dstLenPtr.i is filled with how many bytes of the output buffer
799 /// were used. As mentioned above, this can be one more that
800 /// the output buffer's specified length if a CRLF was stored.
801 ///
802 /// srcLenPtr.i is filled with how many bytes of the source buffer
803 /// were consumed.
804 ///
805 /// It may be obvious, but bears mentioning that when converting
806 /// in CRLF mode (which requires two bytes of storage in the output
807 /// buffer), the number of bytes consumed from the source buffer
808 /// will be less than the number of bytes stored in the output buffer.
809 ///
810 /// </summary>
811 /// <param name="dstArray,">Output buffer to fill with translated bytes or chars.
812 /// </param>
813 /// <param name="dstStart,">First unused index in the dst output array.
814 /// </param>
815 /// <param name="srcArray,">Input buffer that holds the bytes or chars to translate
816 /// </param>
817 /// <param name="srcStart,">Index of first available byte in src array.
818 /// </param>
819 /// <param name="dstLenPtr,">On entry, the maximum length of output
820 /// buffer in bytes or chars. On exit, the number of
821 /// bytes or chars actually used in output buffer.
822 /// </param>
823 /// <param name="srcLenPtr,">On entry, the length of source buffer.
824 /// On exit, the number of bytes or chars read from
825 /// the source buffer.
826 /// </param>
827  
828 internal bool translateEOL( System.Object dstArray, int dstStart, Object srcArray, int srcStart, IntPtr dstLenPtr, IntPtr srcLenPtr )
829 {
830  
831 // Figure out if the srcArray and dstArray buffers
832 // are byte or char arrays.
833 bool isCharType;
834 char[] srcArrayChar, dstArrayChar;
835 byte[] srcArrayByte, dstArrayByte;
836  
837 if ( ( srcArray is char[] ) && ( dstArray is char[] ) )
838 {
839 isCharType = true;
840 srcArrayChar = (char[])srcArray;
841 dstArrayChar = (char[])dstArray;
842 srcArrayByte = null;
843 dstArrayByte = null;
844 }
845 else if ( ( srcArray is byte[] ) && ( dstArray is byte[] ) )
846 {
847 isCharType = false;
848 srcArrayChar = null;
849 dstArrayChar = null;
850 srcArrayByte = (byte[])srcArray;
851 dstArrayByte = (byte[])dstArray;
852 }
853 else
854 {
855 throw new TclRuntimeError( "unknown array argument types" );
856 }
857  
858 int src, dst, dstEnd, srcLen;
859 bool newlineFound;
860  
861 src = srcStart;
862 dst = dstStart;
863 newlineFound = false;
864 srcLen = srcLenPtr.i;
865  
866 switch ( translation )
867 {
868  
869 case TclIO.TRANS_LF:
870 {
871 if ( isCharType )
872 {
873 for ( dstEnd = dst + srcLen; dst < dstEnd; )
874 {
875 if ( srcArrayChar[src] == '\n' )
876 {
877 newlineFound = true;
878 }
879 dstArrayChar[dst++] = srcArrayChar[src++];
880 }
881 }
882 else
883 {
884 for ( dstEnd = dst + srcLen; dst < dstEnd; )
885 {
886 if ( srcArrayByte[src] == '\n' )
887 {
888 newlineFound = true;
889 }
890 dstArrayByte[dst++] = srcArrayByte[src++];
891 }
892 }
893 dstLenPtr.i = srcLen;
894 break;
895 }
896  
897 case TclIO.TRANS_CR:
898 {
899 if ( isCharType )
900 {
901 for ( dstEnd = dst + srcLen; dst < dstEnd; )
902 {
903 if ( srcArrayChar[src] == '\n' )
904 {
905 dstArrayChar[dst++] = '\r';
906 newlineFound = true;
907 src++;
908 }
909 else
910 {
911 dstArrayChar[dst++] = srcArrayChar[src++];
912 }
913 }
914 }
915 else
916 {
917 for ( dstEnd = dst + srcLen; dst < dstEnd; )
918 {
919 if ( srcArrayByte[src] == '\n' )
920 {
921 dstArrayByte[dst++] = (byte)SupportClass.Identity( '\r' );
922 newlineFound = true;
923 src++;
924 }
925 else
926 {
927 dstArrayByte[dst++] = srcArrayByte[src++];
928 }
929 }
930 }
931 dstLenPtr.i = srcLen;
932 break;
933 }
934  
935 case TclIO.TRANS_CRLF:
936 {
937 // Since this causes the number of bytes to grow, we
938 // start off trying to put 'srcLen' bytes into the
939 // output buffer, but allow it to store more bytes, as
940 // long as there's still source bytes and room in the
941 // output buffer.
942  
943 int dstMax;
944 //int dstStart, srcStart;
945  
946 //dstStart = dst;
947 dstMax = dst + dstLenPtr.i;
948  
949 //srcStart = src;
950  
951 if ( srcLen < dstLenPtr.i )
952 {
953 dstEnd = dst + srcLen;
954 }
955 else
956 {
957 dstEnd = dst + dstLenPtr.i;
958 }
959  
960 if ( isCharType )
961 {
962 while ( dst < dstEnd )
963 {
964 if ( srcArrayChar[src] == '\n' )
965 {
966 if ( dstEnd < dstMax )
967 {
968 dstEnd++;
969 }
970 dstArrayChar[dst++] = '\r';
971 newlineFound = true;
972 }
973 dstArrayChar[dst++] = srcArrayChar[src++];
974 }
975 }
976 else
977 {
978 while ( dst < dstEnd )
979 {
980 if ( srcArrayByte[src] == '\n' )
981 {
982 if ( dstEnd < dstMax )
983 {
984 dstEnd++;
985 }
986 dstArrayByte[dst++] = (byte)SupportClass.Identity( '\r' );
987 newlineFound = true;
988 }
989 dstArrayByte[dst++] = srcArrayByte[src++];
990 }
991 }
992  
993 srcLenPtr.i = src - srcStart;
994 dstLenPtr.i = dst - dstStart;
995 break;
996 }
997  
998 default:
999 {
1000 break;
1001 }
1002  
1003 }
1004 return newlineFound;
1005 }
1006  
1007 /// <summary> Tcl_UtfToExternal -> unicodeToExternal
1008 ///
1009 /// Convert a source buffer from unicode characters to a specified encoding.
1010 ///
1011 /// FIXME: Add doc for return values
1012 ///
1013 /// </summary>
1014 /// <param name="src,"> Source characters.
1015 /// </param>
1016 /// <param name="srcOff,"> First index in src input array.
1017 /// </param>
1018 /// <param name="srcLen,"> Number of characters in src buffer.
1019 /// </param>
1020 /// <param name="dst,"> Array to store encoded bytes in.
1021 /// </param>
1022 /// <param name="dstOff,"> First available index in dst array.
1023 /// </param>
1024 /// <param name="dstLen,"> Length of dst array.
1025 /// </param>
1026 /// <param name="srcReadPtr,"> Filled with the number of characters from
1027 /// the source string that were converted.
1028 /// This may be less than the original source
1029 /// length if there was a problem converting
1030 /// some source characters.
1031 /// </param>
1032 /// <param name="dstWrotePtr,">Filled with the number of bytes that were
1033 /// stored in the output buffer as a result of
1034 /// the conversion
1035 /// </param>
1036 /// <param name="dstCharsPtr,">Filled with the number of characters that
1037 /// correspond to the bytes stored in the
1038 /// output buffer.
1039 /// </param>
1040  
1041 internal int unicodeToExternal( char[] src, int srcOff, int srcLen, byte[] dst, int dstOff, int dstLen, IntPtr srcReadPtr, IntPtr dstWrotePtr, IntPtr dstCharsPtr )
1042 {
1043 bool debug;
1044 int result;
1045  
1046 if ( (System.Object)encoding == null )
1047 {
1048 throw new TclRuntimeError( "unicodeToExternal called with null encoding" );
1049 }
1050  
1051 if ( srcLen == 0 )
1052 {
1053 srcReadPtr.i = 0;
1054 if ( dstWrotePtr != null )
1055 dstWrotePtr.i = 0;
1056 if ( dstCharsPtr != null )
1057 dstCharsPtr.i = 0;
1058 return 0;
1059 }
1060  
1061 #if DEBUG
1062 System.Diagnostics.Debug.WriteLine("now to encode char array of length " + srcLen);
1063 System.Diagnostics.Debug.WriteLine("srcOff is " + srcOff);
1064 for (int i = srcOff; i < (srcOff + srcLen); i++)
1065 {
1066 System.Diagnostics.Debug.WriteLine("(char) '" + src[i] + "'");
1067 }
1068 System.Diagnostics.Debug.WriteLine("encoded as " + encoding);
1069 #endif
1070  
1071 if ( ctb == null )
1072 {
1073 try
1074 {
1075 ctb = this.encoding.GetEncoder();
1076 }
1077 catch ( IOException ex )
1078 {
1079 // Valid encodings should be checked already
1080 throw new TclRuntimeError( "unsupported encoding \"" + encoding + "\"" );
1081 }
1082 }
1083  
1084 int chars_read, bytes_written;
1085  
1086 int required_bytes = ctb.GetByteCount( src, srcOff, srcLen, false );
1087 // ATK do not allow buffer overflow by decresing read bytes count
1088 if ( required_bytes > dstLen )
1089 {
1090 srcLen = dstLen;
1091 }
1092 bytes_written = ctb.GetBytes( src, srcOff, srcLen, dst, dstOff, false );
1093 srcReadPtr.i = srcLen;
1094 if ( dstWrotePtr != null )
1095 dstWrotePtr.i = bytes_written;
1096 if ( dstCharsPtr != null )
1097 dstCharsPtr.i = srcLen;
1098  
1099 // FIXME: When do we return error codes?
1100 result = 0;
1101  
1102 return result;
1103 }
1104  
1105 /// <summary> WriteBytes -> writeBytes
1106 ///
1107 /// Write a sequence of bytes into an output buffer, may queue the
1108 /// buffer for output if it gets full, and also remembers whether the
1109 /// current buffer is ready e.g. if it contains a newline and we are in
1110 /// line buffering mode.
1111 ///
1112 /// The number of bytes written or -1 in case of error. If -1,
1113 /// Tcl_GetErrno will return the error code.
1114 ///
1115 /// May buffer up output and may cause output to be produced on the
1116 /// channel.
1117 ///
1118 /// </summary>
1119 /// <param name="src"> Bytes to write.
1120 /// </param>
1121 /// <param name="srfOff"> First index in src array.
1122 /// </param>
1123 /// <param name="srfLen"> Number of bytes to write.
1124 /// </param>
1125  
1126 internal int writeBytes( byte[] srcArray, int srcOff, int srcLen )
1127 {
1128 ChannelBuffer buf;
1129 byte[] dstArray;
1130 int dst, src, dstMax, sawLF, total, savedLF;
1131 IntPtr dstLen = new IntPtr( this );
1132 IntPtr toWrite = new IntPtr( this );
1133  
1134 total = 0;
1135 sawLF = 0;
1136 savedLF = 0;
1137 src = srcOff;
1138  
1139 // Loop over all bytes in src, storing them in output buffer with
1140 // proper EOL translation.
1141  
1142 while ( srcLen + savedLF > 0 )
1143 {
1144 buf = curOut;
1145 if ( buf == null )
1146 {
1147 buf = new ChannelBuffer( bufSize );
1148 curOut = buf;
1149 }
1150 //dst = bufPtr->buf + bufPtr->nextAdded;
1151 dstArray = buf.buf;
1152 dst = buf.nextAdded;
1153 dstMax = buf.bufLength - buf.nextAdded;
1154 dstLen.i = dstMax;
1155  
1156 toWrite.i = dstLen.i;
1157 if ( toWrite.i > srcLen )
1158 {
1159 toWrite.i = srcLen;
1160 }
1161  
1162 if ( savedLF != 0 )
1163 {
1164 // A '\n' was left over from last call to translateEOL()
1165 // and we need to store it in this buffer. If the channel is
1166 // line-based, we will need to flush it.
1167  
1168 dstArray[dst++] = (byte)SupportClass.Identity( '\n' );
1169 dstLen.i--;
1170 sawLF++;
1171 }
1172 if ( translateEOL( dstArray, dst, srcArray, src, dstLen, toWrite ) )
1173 {
1174 sawLF++;
1175 }
1176 dstLen.i += savedLF;
1177 savedLF = 0;
1178  
1179 if ( dstLen.i > dstMax )
1180 {
1181 savedLF = 1;
1182 dstLen.i = dstMax;
1183 }
1184 buf.nextAdded += dstLen.i;
1185 if ( checkFlush( buf, ( sawLF != 0 ) ) != 0 )
1186 {
1187 return -1;
1188 }
1189 total += dstLen.i;
1190 src += toWrite.i;
1191 srcLen -= toWrite.i;
1192 sawLF = 0;
1193 }
1194 return total;
1195 }
1196  
1197 /// <summary> CheckFlush -> checkFlush
1198 ///
1199 /// Helper function for writeBytes() and writeChars(). If the
1200 /// channel buffer is ready to be flushed, flush it.
1201 ///
1202 /// The return value is -1 if there was a problem flushing the
1203 /// channel buffer, or 0 otherwise.
1204 ///
1205 /// The buffer will be recycled if it is flushed.
1206 ///
1207 /// </summary>
1208 /// <param name="buf"> Channel buffer to possibly flush.
1209 /// </param>
1210 /// <param name="newlineFlag"> True if a the channel buffer
1211 /// contains a newline.
1212 /// </param>
1213  
1214 internal int checkFlush( ChannelBuffer buf, bool newlineFlag )
1215 {
1216 // The current buffer is ready for output:
1217 // 1. if it is full.
1218 // 2. if it contains a newline and this channel is line-buffered.
1219 // 3. if it contains any output and this channel is unbuffered.
1220  
1221 if ( !bufferReady )
1222 {
1223 if ( buf.nextAdded == buf.bufLength )
1224 {
1225 bufferReady = true;
1226 }
1227 else if ( buffering == TclIO.BUFF_LINE )
1228 {
1229 if ( newlineFlag )
1230 {
1231 bufferReady = true;
1232 }
1233 }
1234 else if ( buffering == TclIO.BUFF_NONE )
1235 {
1236 bufferReady = true;
1237 }
1238 }
1239 if ( bufferReady )
1240 {
1241 if ( flushChannel( null, false ) != 0 )
1242 {
1243 return -1;
1244 }
1245 }
1246 return 0;
1247 }
1248  
1249 /// <summary> WriteChars -> writeChars
1250 ///
1251 /// Convert chars to the channel's external encoding and
1252 /// write the produced bytes into an output buffer, may queue the
1253 /// buffer for output if it gets full, and also remembers whether the
1254 /// current buffer is ready e.g. if it contains a newline and we are in
1255 /// line buffering mode.
1256 ///
1257 /// The number of bytes written or -1 in case of error. If -1,
1258 /// Tcl_GetErrno will return the error code.
1259 ///
1260 /// May buffer up output and may cause output to be produced on the
1261 /// channel.
1262 ///
1263 /// </summary>
1264 /// <param name="src"> Chars to write.
1265 /// </param>
1266 /// <param name="srfOff"> First index in src array.
1267 /// </param>
1268 /// <param name="srfLen"> Number of chars to write.
1269 /// </param>
1270  
1271 internal int writeChars( char[] srcArray, int srcOff, int srcLen )
1272 {
1273 //ChannelState *statePtr = chanPtr->state; // state info for channel
1274 ChannelBuffer buf;
1275 char[] stageArray;
1276 byte[] dstArray;
1277 int stage, src, dst;
1278 int saved, savedLF, sawLF, total, dstLen, stageMax;
1279 int endEncoding, result;
1280 bool consumedSomething;
1281 //Tcl_Encoding encoding;
1282 byte[] safe = new byte[ChannelBuffer.BUFFER_PADDING];
1283 IntPtr stageLen = new IntPtr( this );
1284 IntPtr toWrite = new IntPtr( this );
1285 IntPtr stageRead = new IntPtr( this );
1286 IntPtr dstWrote = new IntPtr( this );
1287  
1288 total = 0;
1289 sawLF = 0;
1290 savedLF = 0;
1291 saved = 0;
1292 //encoding = statePtr->encoding;
1293 src = 0;
1294  
1295 // Write the terminated escape sequence even if srcLen is 0.
1296  
1297 endEncoding = ( encodingEnd ? 0 : 1 );
1298  
1299 // Loop over all characters in src, storing them in staging buffer
1300 // with proper EOL translation.
1301  
1302 consumedSomething = true;
1303 while ( consumedSomething && ( srcLen + savedLF + endEncoding > 0 ) )
1304 {
1305 consumedSomething = false;
1306 if ( outputStage == null )
1307 {
1308 outputStage = new char[bufSize + 2];
1309 }
1310 stageArray = outputStage;
1311 stage = 0;
1312 stageMax = bufSize;
1313 stageLen.i = stageMax;
1314  
1315 toWrite.i = stageLen.i;
1316 if ( toWrite.i > srcLen )
1317 {
1318 toWrite.i = srcLen;
1319 }
1320  
1321 if ( savedLF != 0 )
1322 {
1323 // A '\n' was left over from last call to TranslateOutputEOL()
1324 // and we need to store it in the staging buffer. If the
1325 // channel is line-based, we will need to flush the output
1326 // buffer (after translating the staging buffer).
1327  
1328 stageArray[stage++] = '\n';
1329 stageLen.i--;
1330 sawLF++;
1331 }
1332 if ( translateEOL( stageArray, stage, srcArray, src, stageLen, toWrite ) )
1333 {
1334 sawLF++;
1335 }
1336  
1337 stage -= savedLF;
1338 stageLen.i += savedLF;
1339 savedLF = 0;
1340  
1341 if ( stageLen.i > stageMax )
1342 {
1343 savedLF = 1;
1344 stageLen.i = stageMax;
1345 }
1346 src += toWrite.i;
1347 srcLen -= toWrite.i;
1348  
1349 // Loop over all characters in staging buffer, converting them
1350 // to external encoding, storing them in output buffer.
1351  
1352 while ( stageLen.i + saved + endEncoding > 0 )
1353 {
1354 buf = curOut;
1355 if ( buf == null )
1356 {
1357 buf = new ChannelBuffer( bufSize );
1358 curOut = buf;
1359 }
1360 // dst = buf.buf + buf.nextAdded;
1361 dstArray = buf.buf;
1362 dst = buf.nextAdded;
1363 dstLen = buf.bufLength - buf.nextAdded;
1364  
1365 if ( saved != 0 )
1366 {
1367 // Here's some translated bytes left over from the last
1368 // buffer that we need to stick at the beginning of this
1369 // buffer.
1370  
1371 Array.Copy( safe, 0, dstArray, dst, saved );
1372 buf.nextAdded += saved;
1373 dst += saved;
1374 dstLen -= saved;
1375 saved = 0;
1376 }
1377  
1378 result = unicodeToExternal( stageArray, stage, stageLen.i, dstArray, dst, dstLen + ChannelBuffer.BUFFER_PADDING, stageRead, dstWrote, null );
1379  
1380 // FIXME: Not clear how this condition is dealt with.
1381 //
1382 // Fix for SF #506297, reported by Martin Forssen
1383 // <ruric@users.sourceforge.net>.
1384 //
1385 // The encoding chosen in the script exposing the bug writes out
1386 // three intro characters when TCL_ENCODING_START is set, but does
1387 // not consume any input as TCL_ENCODING_END is cleared. As some
1388 // output was generated the enclosing loop calls UtfToExternal
1389 // again, again with START set. Three more characters in the out
1390 // and still no use of input ... To break this infinite loop we
1391 // remove TCL_ENCODING_START from the set of flags after the first
1392 // call (no condition is required, the later calls remove an unset
1393 // flag, which is a no-op). This causes the subsequent calls to
1394 // UtfToExternal to consume and convert the actual input.
1395  
1396 encodingStart = false;
1397  
1398 // The following can never happen since we use unicode characters.
1399 //
1400 //if ((result != 0) && ((stageRead.i + dstWrote.i) == 0)) {
1401 // // We have an incomplete UTF-8 character at the end of the
1402 // // staging buffer. It will get moved to the beginning of the
1403 // // staging buffer followed by more bytes from src.
1404 //
1405 // src -= stageLen.i;
1406 // srcLen += stageLen.i;
1407 // stageLen.i = 0;
1408 // savedLF = 0;
1409 // break;
1410 //}
1411 buf.nextAdded += dstWrote.i;
1412 if ( buf.nextAdded > buf.bufLength )
1413 {
1414 // When translating from unicode to external encoding, we
1415 // allowed the translation to produce a character that
1416 // crossed the end of the output buffer, so that we would
1417 // get a completely full buffer before flushing it. The
1418 // extra bytes will be moved to the beginning of the next
1419 // buffer.
1420  
1421 saved = buf.nextAdded - buf.bufLength;
1422 // ATK Array.Copy(SupportClass.ToByteArray((System.Array) dstArray), dst + dstLen, SupportClass.ToByteArray(safe), 0, saved);
1423 Array.Copy( dstArray, dst + dstLen, safe, 0, saved );
1424 buf.nextAdded = buf.bufLength;
1425 }
1426 if ( checkFlush( buf, ( sawLF != 0 ) ) != 0 )
1427 {
1428 return -1;
1429 }
1430  
1431 total += dstWrote.i;
1432 stage += stageRead.i;
1433 stageLen.i -= stageRead.i;
1434 sawLF = 0;
1435  
1436 consumedSomething = true;
1437  
1438 // If all translated characters are written to the buffer,
1439 // endEncoding is set to 0 because the escape sequence may be
1440 // output.
1441  
1442 if ( ( stageLen.i + saved == 0 ) && ( result == 0 ) )
1443 {
1444 endEncoding = 0;
1445 }
1446 }
1447 }
1448  
1449 // If nothing was written and it happened because there was no progress
1450 // in the UTF conversion, we throw an error.
1451  
1452 if ( !consumedSomething && ( total == 0 ) )
1453 {
1454 //Tcl_SetErrno (EINVAL);
1455 return -1;
1456 }
1457 return total;
1458 }
1459  
1460 /// <summary> DoWriteChars -> doWriteChars
1461 ///
1462 /// Takes a sequence of characters and converts them for output
1463 /// using the channel's current encoding, may queue the buffer for
1464 /// output if it gets full, and also remembers whether the current
1465 /// buffer is ready e.g. if it contains a newline and we are in
1466 /// line buffering mode. Compensates stacking, i.e. will redirect the
1467 /// data from the specified channel to the topmost channel in a stack.
1468 ///
1469 /// The number of bytes written or -1 in case of error. If -1,
1470 /// Tcl_GetErrno will return the error code.
1471 ///
1472 /// May buffer up output and may cause output to be produced on the
1473 /// channel.
1474 ///
1475 /// </summary>
1476 /// <param name="src"> Chars to write.
1477 /// </param>
1478 /// <param name="srfOff"> First index in src array.
1479 /// </param>
1480 /// <param name="srfLen"> Number of chars to write.
1481 /// </param>
1482  
1483 internal int doWriteChars( char[] src, int srcOff, int srcLen )
1484 {
1485 // HACK ATK Was soll das?
1486 return -1;
1487 }
1488  
1489 /// <summary> Tcl_WriteObj -> writeObj
1490 ///
1491 /// Takes the Tcl object and queues its contents for output. If the
1492 /// encoding of the channel is NULL, takes the byte-array representation
1493 /// of the object and queues those bytes for output. Otherwise, takes
1494 /// the characters in the UTF-8 (string) representation of the object
1495 /// and converts them for output using the channel's current encoding.
1496 /// May flush internal buffers to output if one becomes full or is ready
1497 /// for some other reason, e.g. if it contains a newline and the channel
1498 /// is in line buffering mode.
1499 ///
1500 /// The number of bytes written or -1 in case of error. If -1,
1501 /// Tcl_GetErrno will return the error code.
1502 ///
1503 /// May buffer up output and may cause output to be produced on the
1504 /// channel.
1505 ///
1506 /// </summary>
1507 /// <param name="obj"> The object to write.
1508 /// </param>
1509  
1510 internal int writeObj( TclObject obj )
1511 {
1512 // Always use the topmost channel of the stack
1513  
1514 //char *src;
1515 int srcLen;
1516  
1517 //statePtr = ((Channel *) chan)->state;
1518 //chanPtr = statePtr->topChanPtr;
1519  
1520 //if (CheckChannelErrors(statePtr, TCL_WRITABLE) != 0) {
1521 // return -1;
1522 //}
1523  
1524 if ( (System.Object)encoding == null )
1525 {
1526 srcLen = TclByteArray.getLength( null, obj );
1527 byte[] bytes = TclByteArray.getBytes( null, obj );
1528 return writeBytes( bytes, 0, srcLen );
1529 }
1530 else
1531 {
1532 char[] chars = obj.ToString().ToCharArray();
1533 return writeChars( chars, 0, chars.Length );
1534 }
1535 }
1536 }
1537 }