wasCSharpSQLite – Blame information for rev 4
?pathlinks?
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 | } |