0001: /*
0002: * TclInputStream.java
0003: *
0004: * Copyright (c) 2003 Mo DeJong
0005: *
0006: * See the file "license.terms" for information on usage and
0007: * redistribution of this file, and for a DISCLAIMER OF ALL
0008: * WARRANTIES.
0009: *
0010: * RCS: @(#) $Id: TclInputStream.java,v 1.4 2006/07/11 19:40:52 mdejong Exp $
0011: */
0012:
0013: // A TclInputStream is a cross between a Java InputStream and
0014: // a Reader. The class supports reading raw bytes as well as
0015: // encoded characters. It manages buffering and supports
0016: // line oriented reading of data. It also supports a user
0017: // configurable EOF marker and line ending translations.
0018: package tcl.lang;
0019:
0020: import java.io.IOException;
0021: import java.io.EOFException;
0022: import java.io.InputStream;
0023: import java.io.UnsupportedEncodingException;
0024:
0025: import java.util.ArrayList;
0026:
0027: import java.nio.CharBuffer;
0028: import java.nio.ByteBuffer;
0029:
0030: import java.nio.charset.Charset;
0031: import java.nio.charset.CharsetDecoder;
0032: import java.nio.charset.CoderResult;
0033: import java.nio.charset.IllegalCharsetNameException;
0034: import java.nio.charset.UnsupportedCharsetException;
0035:
0036: class TclInputStream {
0037:
0038: /**
0039: * The Java byte stream object we pull data in from.
0040: */
0041:
0042: private InputStream input;
0043:
0044: /**
0045: * If nonzero, use this character as EOF marker.
0046: */
0047:
0048: private char eofChar;
0049:
0050: /**
0051: * Flag that is set on each read. If the read encountered EOF
0052: * or a custom eofChar is found, the it is set to true.
0053: */
0054:
0055: private boolean eofCond = false;
0056: private boolean stickyEofCond = false;
0057:
0058: /**
0059: * Translation mode for end-of-line character
0060: */
0061:
0062: protected int translation;
0063:
0064: /**
0065: * Name of Java encoding for this Channel.
0066: * A null value means use no encoding (binary).
0067: */
0068:
0069: protected String encoding;
0070:
0071: /**
0072: * Charset decoder object. A null value means
0073: * that no conversions have been done yet.
0074: */
0075:
0076: protected CharsetDecoder csd = null;
0077:
0078: /**
0079: * Buffering
0080: */
0081:
0082: protected int buffering;
0083:
0084: /**
0085: * Blocking
0086: */
0087:
0088: protected boolean blocking;
0089:
0090: /**
0091: * Blocked
0092: */
0093:
0094: protected boolean blocked = false;
0095:
0096: /**
0097: * Buffer size in bytes
0098: */
0099:
0100: protected int bufSize;
0101:
0102: /**
0103: * Used to track EOL state
0104: */
0105:
0106: protected boolean needNL = false;
0107: protected boolean sawCR = false;
0108:
0109: protected boolean needMoreData = false;
0110:
0111: /**
0112: * Flags used to track encoding states.
0113: * The encodingState member of called inputEncodingState
0114: * in the C ChannelState type. The encodingStart and encodingEnd
0115: * members combined are called inputEncodingFlags
0116: * and have the bit values TCL_ENCODING_END and TCL_ENCODING_START.
0117: */
0118:
0119: // Object encodingState = null;
0120: boolean encodingStart = true;
0121: boolean encodingEnd = false;
0122:
0123: /**
0124: * First and last buffers in the input queue.
0125: */
0126:
0127: ChannelBuffer inQueueHead = null;
0128: ChannelBuffer inQueueTail = null;
0129: ChannelBuffer saveInBuf = null;
0130:
0131: /**
0132: * Constructor for Tcl input stream class. We require
0133: * a byte stream source at init time, the stram can't
0134: * be changed after the TclInputStream is created.
0135: */
0136:
0137: TclInputStream(InputStream inInput) {
0138: input = inInput;
0139: }
0140:
0141: // Helper used by getsObj and filterBytes
0142:
0143: private class GetsState {
0144: TclObject obj;
0145: ArrayList charToBytes; // Saves number of bytes read for each char
0146: //int dst;
0147: String encoding;
0148: ChannelBuffer buf;
0149: // Java decoders don't provide any way to save decoder state.
0150: //Object state;
0151: IntPtr rawRead = new IntPtr();
0152: //IntPtr bytesWrote = new IntPtr();
0153: IntPtr charsWrote = new IntPtr();
0154: int totalChars;
0155: }
0156:
0157: // These static class members are used only when
0158: // adding elements to charToBytes.
0159:
0160: static Integer oneInteger = new Integer(1);
0161: static Integer twoInteger = new Integer(2);
0162: static Integer threeInteger = new Integer(3);
0163:
0164: // Return the number of bytes that a range of characters
0165: // was decoded from. This method operates on the charToBytes
0166: // result object passed to externalToUnicode().
0167:
0168: static int decodedNumBytes(ArrayList charToBytes, int start, int end) {
0169: int numBytes = 0;
0170: for (int i = start; i < end; i++) {
0171: Integer iobj = (Integer) charToBytes.get(i);
0172: numBytes += iobj.intValue();
0173: }
0174: return numBytes;
0175: }
0176:
0177: /**
0178: * Tcl_GetsObj -> getsObj
0179: *
0180: * Accumulate input from the input channel until end-of-line or
0181: * end-of-file has been seen. Bytes read from the input channel
0182: * are converted to Unicode using the encoding specified by the
0183: * channel.
0184: *
0185: * Returns the number of characters accumulated in the object
0186: * or -1 if error, blocked, or EOF. If -1, use Tcl_GetErrno()
0187: * to retrieve the POSIX error code for the error or condition
0188: * that occurred.
0189: *
0190: * Will consume input from the channel.
0191: * On reading EOF, leave channel at EOF char.
0192: * On reading EOL, leave channel after EOL, but don't
0193: * return EOL in dst buffer.
0194: */
0195:
0196: int getsObj(TclObject tobj) {
0197: GetsState gs;
0198: ChannelBuffer buf;
0199: boolean oldEncodingStart, oldEncodingEnd;
0200: int oldRemoved, skip, inEofChar;
0201: int copiedTotal, oldLength;
0202: boolean in_binary_encoding = false;
0203: int dst, dstEnd, eol, eof;
0204: final boolean debug = false;
0205:
0206: buf = inQueueHead;
0207:
0208: // Preserved so we can restore the channel's state in case we don't
0209: // find a newline in the available input.
0210:
0211: oldLength = 0;
0212: oldEncodingStart = encodingStart;
0213: oldEncodingEnd = encodingEnd;
0214: oldRemoved = buf.BUFFER_PADDING;
0215: if (buf != null) {
0216: oldRemoved = buf.nextRemoved;
0217: }
0218:
0219: // If there is no encoding, use "iso8859-1" -- readLine() doesn't
0220: // produce ByteArray objects.
0221:
0222: if (encoding == null) {
0223: in_binary_encoding = true;
0224: encoding = EncodingCmd.getJavaName("iso8859-1");
0225: }
0226:
0227: if (debug) {
0228: System.out.println("getsObj() : encoding is " + encoding);
0229: }
0230:
0231: // Object used by filterBytes to keep track of how much data has
0232: // been consumed from the channel buffers.
0233:
0234: gs = new GetsState();
0235: gs.obj = tobj;
0236: gs.charToBytes = new ArrayList(128);
0237: //gs.dst = &dst;
0238: gs.encoding = encoding;
0239: gs.buf = buf;
0240: gs.rawRead.i = 0;
0241: //gs.bytesWrote.i = 0;
0242: gs.charsWrote.i = 0;
0243: gs.totalChars = 0;
0244:
0245: // Ensure that tobj is an empty TclString object.
0246: // Cheat a bit and grab the StringBuffer out of
0247: // the TclString so we can query the data that
0248: // was just added to the buffer.
0249: TclString.empty(tobj);
0250: StringBuffer obj_sbuf = ((TclString) tobj.getInternalRep()).sbuf;
0251:
0252: dst = 0;
0253: dstEnd = dst;
0254:
0255: skip = 0;
0256: eof = -1;
0257: inEofChar = eofChar;
0258:
0259: // Used to implement goto like functionality for restore
0260: // and goteol loop terminaltion blocks.
0261:
0262: boolean restore = false;
0263: boolean goteol = false;
0264:
0265: // This is just here so that eol and copiedTotal are
0266: // definitely assigned before the try block.
0267: eol = -1;
0268: copiedTotal = -1;
0269:
0270: restore_or_goteol: {
0271: while (true) {
0272: if (dst >= dstEnd) {
0273: if (filterBytes(gs) != 0) {
0274: restore = true;
0275: break restore_or_goteol; //goto restore
0276: }
0277: if (debug) {
0278: System.out.println("advancing dstEnd by "
0279: + gs.charsWrote.i);
0280: }
0281: dstEnd += gs.charsWrote.i;
0282: }
0283:
0284: // Remember if EOF char is seen, then look for EOL anyhow, because
0285: // the EOL might be before the EOF char.
0286:
0287: if (inEofChar != '\0') {
0288: for (eol = dst; eol < dstEnd; eol++) {
0289: if (obj_sbuf.charAt(eol) == inEofChar) {
0290: if (debug) {
0291: System.out.println("found EOF char at "
0292: + eol);
0293: }
0294:
0295: dstEnd = eol;
0296: eof = eol;
0297: break;
0298: }
0299: }
0300: }
0301:
0302: // On EOL, leave current file position pointing after the EOL, but
0303: // don't store the EOL in the output string.
0304:
0305: switch (translation) {
0306: case TclIO.TRANS_LF: {
0307: for (eol = dst; eol < dstEnd; eol++) {
0308: if (obj_sbuf.charAt(eol) == '\n') {
0309: if (debug) {
0310: System.out
0311: .println("TRANS_LF: found EOL char at "
0312: + eol);
0313: }
0314:
0315: skip = 1;
0316: goteol = true;
0317: break restore_or_goteol; //goto goteol
0318: }
0319: }
0320: break;
0321: }
0322: case TclIO.TRANS_CR: {
0323: for (eol = dst; eol < dstEnd; eol++) {
0324: if (obj_sbuf.charAt(eol) == '\r') {
0325: if (debug) {
0326: System.out
0327: .println("TRANS_CR: found EOL char at "
0328: + eol);
0329: }
0330:
0331: skip = 1;
0332: goteol = true;
0333: break restore_or_goteol; //goto goteol
0334: }
0335: }
0336: break;
0337: }
0338: case TclIO.TRANS_CRLF: {
0339: for (eol = dst; eol < dstEnd; eol++) {
0340: if (obj_sbuf.charAt(eol) == '\r') {
0341: if (debug) {
0342: System.out
0343: .println("TRANS_CRLF: found EOL char at "
0344: + eol);
0345: }
0346:
0347: eol++;
0348:
0349: // If a CR is at the end of the buffer,
0350: // then check for a LF at the begining
0351: // of the next buffer, unless EOF char
0352: // was found already.
0353:
0354: if (eol >= dstEnd) {
0355: if (eol != eof) {
0356: if (debug) {
0357: System.out
0358: .println("TRANS_CRLF: filterBytes for \\n");
0359: }
0360:
0361: dst = dstEnd;
0362: if (filterBytes(gs) != 0) {
0363: restore = true;
0364: break restore_or_goteol; //goto restore
0365: }
0366: dstEnd += gs.charsWrote.i;
0367: }
0368: if (eol >= dstEnd) {
0369: skip = 0;
0370: goteol = true;
0371: break restore_or_goteol; //goto goteol
0372: }
0373: }
0374: if (obj_sbuf.charAt(eol) == '\n') {
0375: eol--;
0376: skip = 2;
0377: goteol = true;
0378: break restore_or_goteol; //goto goteol
0379: }
0380: }
0381: }
0382: break;
0383: }
0384: case TclIO.TRANS_AUTO: {
0385: eol = dst;
0386: skip = 1;
0387: if (sawCR) {
0388: sawCR = false;
0389: if ((eol < dstEnd)
0390: && (obj_sbuf.charAt(eol) == '\n')) {
0391: // Skip the raw bytes that make up the '\n'.
0392:
0393: if (debug) {
0394: System.out
0395: .println("TRANS_AUTO: found \\n at "
0396: + eol);
0397: }
0398:
0399: IntPtr rawRead = new IntPtr();
0400:
0401: buf = gs.buf;
0402: rawRead.i = decodedNumBytes(gs.charToBytes,
0403: eol, eol + 1);
0404: buf.nextRemoved += rawRead.i;
0405: gs.rawRead.i -= rawRead.i;
0406: //gs.bytesWrote.i--;
0407: gs.charsWrote.i--;
0408: obj_sbuf.deleteCharAt(eol);
0409: dstEnd--;
0410: }
0411: }
0412: for (eol = dst; eol < dstEnd; eol++) {
0413: if (obj_sbuf.charAt(eol) == '\r') {
0414: if (debug) {
0415: System.out
0416: .println("TRANS_AUTO: found \\r at "
0417: + eol);
0418: }
0419:
0420: eol++;
0421: if (eol == dstEnd) {
0422: // If buffer ended on \r, peek ahead to see if a
0423: // \n is available, unless EOF char was found already.
0424:
0425: if (eol != eof) {
0426: if (debug) {
0427: System.out
0428: .println("TRANS_AUTO: peeking for \\n");
0429: }
0430:
0431: dst = dstEnd;
0432: peekAhead(gs);
0433: dstEnd += gs.charsWrote.i;
0434: }
0435: if (eol >= dstEnd) {
0436: eol--;
0437: sawCR = true;
0438: goteol = true;
0439: break restore_or_goteol; //goto goteol
0440: }
0441: }
0442: if (obj_sbuf.charAt(eol) == '\n') {
0443: skip++;
0444: }
0445: eol--;
0446: goteol = true; //goto goteol
0447: break restore_or_goteol;
0448: } else if (obj_sbuf.charAt(eol) == '\n') {
0449: if (debug) {
0450: System.out
0451: .println("TRANS_AUTO: found \\n at "
0452: + eol);
0453: }
0454:
0455: goteol = true;
0456: break restore_or_goteol; //goto goteol
0457: }
0458: }
0459: }
0460: }
0461: if (eof != -1) {
0462: // EOF character was seen. On EOF, leave current file position
0463: // pointing at the EOF character, but don't store the EOF
0464: // character in the output string.
0465:
0466: dstEnd = eof;
0467: eofCond = true;
0468: stickyEofCond = true;
0469: encodingEnd = true;
0470: }
0471: if (eofCond) {
0472: skip = 0;
0473: eol = dstEnd;
0474: if (eol == oldLength) {
0475: // If we didn't append any bytes before encountering EOF,
0476: // caller needs to see -1.
0477:
0478: obj_sbuf.setLength(oldLength);
0479: commonGetsCleanup();
0480: copiedTotal = -1;
0481: break restore_or_goteol; //goto done
0482: }
0483: goteol = true;
0484: break restore_or_goteol; //goto goteol
0485: }
0486: dst = dstEnd;
0487: }
0488: } // end restore_or_goteol: block
0489:
0490: if (goteol) {
0491: // Found EOL or EOF, we need to find out how many raw bytes
0492: // correspond to the decoded characters (including EOL)
0493: // so that the channel buffer index can be updated.
0494: //
0495: // At this point, dst is the index of the character in
0496: // obj_sbuf that corresponds to the first byte in buf.
0497:
0498: int linelen = eol - dst + skip;
0499:
0500: if (debug) {
0501: System.out.println("goteol: linelen is " + linelen);
0502: System.out.println("eol is " + eol);
0503: System.out.println("dst is " + dst);
0504: System.out.println("skip is " + skip);
0505:
0506: System.out.println(gs.buf);
0507: }
0508:
0509: buf = gs.buf;
0510:
0511: // Determine the number of bytes that the given char
0512: // range was decoded from. Also reset gs.charsWrote.i
0513: // since it is used to calculate copiedTotal below.
0514:
0515: int numBytes = decodedNumBytes(gs.charToBytes, dst, dst
0516: + linelen);
0517: buf.nextRemoved += numBytes;
0518: gs.charsWrote.i = (dst + linelen) - dst;
0519:
0520: if (debug) {
0521: System.out.println("advanced buf.nextRemoved by "
0522: + numBytes);
0523: System.out.println(buf);
0524: System.out.println("gs.charsWrote.i set to "
0525: + gs.charsWrote.i);
0526: }
0527:
0528: // Recycle all the emptied buffers.
0529:
0530: obj_sbuf.setLength(eol);
0531:
0532: if (debug) {
0533: System.out.println("obj_sbuf.setLength(" + eol + ")");
0534: System.out.println("srep \"" + obj_sbuf.toString()
0535: + "\"");
0536: }
0537:
0538: commonGetsCleanup();
0539: blocked = false;
0540: copiedTotal = gs.totalChars + gs.charsWrote.i - skip;
0541:
0542: if (debug) {
0543: System.out.println("copiedTotal = " + copiedTotal);
0544: }
0545: }
0546: if (restore) {
0547: // Couldn't get a complete line. This only happens if we get a error
0548: // reading from the channel or we are non-blocking and there wasn't
0549: // an EOL or EOF in the data available.
0550:
0551: buf = inQueueHead;
0552: buf.nextRemoved = oldRemoved;
0553:
0554: for (buf = buf.next; buf != null; buf = buf.next) {
0555: buf.nextRemoved = buf.BUFFER_PADDING;
0556: }
0557: commonGetsCleanup();
0558:
0559: encodingStart = oldEncodingStart;
0560: encodingEnd = oldEncodingEnd;
0561: obj_sbuf.setLength(oldLength);
0562:
0563: // We didn't get a complete line so we need to indicate to UpdateInterest
0564: // that the gets blocked. It will wait for more data instead of firing
0565: // a timer, avoiding a busy wait. This is where we are assuming that the
0566: // next operation is a gets. No more file events will be delivered on
0567: // this channel until new data arrives or some operation is performed
0568: // on the channel (e.g. gets, read, fconfigure) that changes the blocking
0569: // state. Note that this means a file event will not be delivered even
0570: // though a read would be able to consume the buffered data.
0571:
0572: needMoreData = true;
0573: copiedTotal = -1;
0574: }
0575:
0576: // Update the notifier state so we don't block while there is still
0577: // data in the buffers.
0578:
0579: //done:
0580: // Reset original encoding in case it was set to binary
0581: if (in_binary_encoding) {
0582: encoding = null;
0583: }
0584:
0585: updateInterest();
0586:
0587: // Make sure tobj makes use of the string rep defined
0588: // by obj_sbuf. It is possible that TclObject.toString()
0589: // got called at some point during this method.
0590:
0591: tobj.invalidateStringRep();
0592: String srep = tobj.toString();
0593:
0594: if (copiedTotal > 0) {
0595: int len = srep.length();
0596: if (len != copiedTotal) {
0597: throw new TclRuntimeError("copiedTotal " + copiedTotal
0598: + " != " + len);
0599: }
0600: } else {
0601: if (!srep.equals("")) {
0602: throw new TclRuntimeError(
0603: "expected empty TclObject result"
0604: + " since copiedTotal is "
0605: + copiedTotal);
0606: }
0607: }
0608:
0609: return copiedTotal;
0610: }
0611:
0612: /**
0613: * FilterInputBytes -> filterBytes
0614: *
0615: * Helper function for getsObj. Appends Unicode characters
0616: * onto the TclObject associated with the GetsState after
0617: * converting them from raw bytes encoded in the Channel.
0618: * The StringBuffer inside a TclObject is modified directly.
0619: *
0620: * Consumes available bytes from channel buffers. When channel
0621: * buffers are exhausted, reads more bytes from channel device into
0622: * a new channel buffer. It is the caller's responsibility to
0623: * free the channel buffers that have been exhausted.
0624: *
0625: * The return value is -1 if there was an error reading from the
0626: * channel, 0 otherwise.
0627: *
0628: * Status object keeps track of how much data from channel buffers
0629: * has been consumed and where characters should be stored.
0630: */
0631:
0632: int filterBytes(GetsState gs) {
0633: ChannelBuffer buf;
0634: byte[] raw;
0635: int rawStart, rawEnd;
0636: char[] dst;
0637: int offset, toRead, /*dstNeeded,*/spaceLeft, result, rawLen, length;
0638: TclObject obj;
0639: final int ENCODING_LINESIZE = 20; // Lower bound on how many bytes
0640: // to convert at a time. Since we
0641: // don't know a priori how many
0642: // bytes of storage this many
0643: // source bytes will use, we
0644: // actually need at least
0645: // ENCODING_LINESIZE bytes of room.
0646:
0647: boolean goto_read = false; // Set to true when jumping to the read
0648: // label, used to simulate a goto.
0649:
0650: final boolean debug = false;
0651:
0652: obj = gs.obj;
0653:
0654: // Subtract the number of bytes that were removed from channel buffer
0655: // during last call.
0656:
0657: buf = gs.buf;
0658: if (buf != null) {
0659: buf.nextRemoved += gs.rawRead.i;
0660: if (buf.nextRemoved >= buf.nextAdded) {
0661: buf = buf.next;
0662: }
0663: }
0664: gs.totalChars += gs.charsWrote.i;
0665:
0666: read: while (true) {
0667: if (goto_read || (buf == null)
0668: || (buf.nextAdded == buf.BUFFER_PADDING)) {
0669: // All channel buffers were exhausted and the caller still hasn't
0670: // seen EOL. Need to read more bytes from the channel device.
0671: // Side effect is to allocate another channel buffer.
0672:
0673: //read:
0674: if (blocked) {
0675: if (!blocking) {
0676: gs.charsWrote.i = 0;
0677: gs.rawRead.i = 0;
0678: return -1;
0679: }
0680: blocked = false;
0681: }
0682: if (getInput() != 0) {
0683: gs.charsWrote.i = 0;
0684: gs.rawRead.i = 0;
0685: return -1;
0686: }
0687: buf = inQueueTail;
0688: gs.buf = buf;
0689: }
0690:
0691: // Convert some of the bytes from the channel buffer to characters.
0692: // Space in obj's string rep is used to hold the characters.
0693:
0694: rawStart = buf.nextRemoved;
0695: raw = buf.buf;
0696: rawEnd = buf.nextAdded;
0697: rawLen = rawEnd - rawStart;
0698:
0699: toRead = ENCODING_LINESIZE;
0700: if (toRead > rawLen) {
0701: toRead = rawLen;
0702: }
0703: dst = new char[toRead];
0704: result = externalToUnicode(raw, rawStart, rawLen, dst, 0,
0705: toRead, gs.rawRead, /*gs.bytesWrote*/null,
0706: gs.charsWrote, gs.charToBytes);
0707: TclString.append(gs.obj, dst, 0, gs.charsWrote.i);
0708:
0709: if (debug) {
0710: System.out.println("filterBytes chars");
0711:
0712: String srep = gs.obj.toString();
0713: int len = srep.length();
0714:
0715: for (int i = 0; i < len; i++) {
0716: char c = srep.charAt(i);
0717: String prep;
0718: if (c == '\r') {
0719: prep = "\\r";
0720: } else if (c == '\n') {
0721: prep = "\\n";
0722: } else {
0723: prep = "" + c;
0724: }
0725:
0726: System.out
0727: .println("filtered["
0728: + i
0729: + "] = '"
0730: + prep
0731: + "' (int) "
0732: + ((int) srep.charAt(i))
0733: + " 0x"
0734: + Integer.toHexString((int) srep
0735: .charAt(i)));
0736: }
0737: }
0738:
0739: // Make sure that if we go through 'gets', that we reset the
0740: // TCL_ENCODING_START flag still. [Bug #523988]
0741:
0742: encodingStart = false;
0743:
0744: if (result == TCL_CONVERT_MULTIBYTE) {
0745: // The last few bytes in this channel buffer were the start of a
0746: // multibyte sequence. If this buffer was full, then move them to
0747: // the next buffer so the bytes will be contiguous.
0748:
0749: if (debug) {
0750: System.out
0751: .println("TCL_CONVERT_MULTIBYTE decode result found");
0752: }
0753:
0754: ChannelBuffer next;
0755: int extra;
0756:
0757: next = buf.next;
0758: if (buf.nextAdded < buf.bufLength) {
0759: if (gs.rawRead.i > 0) {
0760: // Some raw bytes were converted to UTF-8. Fall through,
0761: // returning those UTF-8 characters because a EOL might be
0762: // present in them.
0763: } else if (eofCond) {
0764: // There was a partial character followed by EOF on the
0765: // device. Fall through, returning that nothing was found.
0766:
0767: buf.nextRemoved = buf.nextAdded;
0768: } else {
0769: // There are no more cached raw bytes left. See if we can
0770: // get some more.
0771:
0772: goto_read = true;
0773: continue read; //goto read;
0774: }
0775: } else {
0776: if (next == null) {
0777: next = new ChannelBuffer(bufSize);
0778: buf.next = next;
0779: inQueueTail = next;
0780: }
0781: extra = rawLen - gs.rawRead.i;
0782: System
0783: .arraycopy(raw, rawStart + gs.rawRead.i,
0784: next.buf, buf.BUFFER_PADDING
0785: - extra, extra);
0786: next.nextRemoved -= extra;
0787: buf.nextAdded -= extra;
0788:
0789: if (debug) {
0790: System.out.println("copied " + extra
0791: + " bytes to " + "next ChannelBuffer");
0792: }
0793: }
0794: }
0795:
0796: break read; // End loop in the normal case
0797: } // End read labeled while loop
0798:
0799: gs.buf = buf;
0800: return 0;
0801: }
0802:
0803: /**
0804: * PeekAhead -> peekAhead
0805: *
0806: * Helper function used by getsObj. Called when we've seen a
0807: * \r at the end of the string and want to look ahead one
0808: * character to see if it is a \n.
0809: *
0810: * Characters read from the channel are appended to gs.obj
0811: * via the filterBytes method.
0812: */
0813:
0814: void peekAhead(GetsState gs) {
0815: ChannelBuffer buf;
0816: //Tcl_DriverBlockModeProc *blockModeProc;
0817: int bytesLeft;
0818: boolean goto_cleanup = false; // Set to true when jumping to the
0819: // cleanup label, used to simulate a goto.
0820:
0821: buf = gs.buf;
0822:
0823: // If there's any more raw input that's still buffered, we'll peek into
0824: // that. Otherwise, only get more data from the channel driver if it
0825: // looks like there might actually be more data. The assumption is that
0826: // if the channel buffer is filled right up to the end, then there
0827: // might be more data to read.
0828:
0829: cleanup: {
0830: //blockModeProc = NULL;
0831: if (buf.next == null) {
0832: bytesLeft = buf.nextAdded
0833: - (buf.nextRemoved + gs.rawRead.i);
0834: if (bytesLeft == 0) {
0835: if (buf.nextAdded < buf.bufLength) {
0836: // Don't peek ahead if last read was short read.
0837: goto_cleanup = true;
0838: break cleanup;
0839: }
0840: // FIXME: This non-blocking check is currently disabled, non-blocking
0841: // is not currently supported and it is not clean why we would
0842: // need to depend on non-blocking IO when peeking anyway.
0843: if (blocking) {
0844: //blockModeProc = Tcl_ChannelBlockModeProc(chanPtr->typePtr);
0845: if (false /*blockModeProc == NULL*/) {
0846: // Don't peek ahead if cannot set non-blocking mode.
0847: goto_cleanup = true;
0848: break cleanup;
0849: }
0850: //StackSetBlockMode(chanPtr, TCL_MODE_NONBLOCKING);
0851: }
0852: }
0853: }
0854: filterBytes(gs);
0855: //if (blockModeProc != NULL) {
0856: // StackSetBlockMode(chanPtr, TCL_MODE_BLOCKING);
0857: //}
0858: }
0859:
0860: if (goto_cleanup) {
0861: buf.nextRemoved += gs.rawRead.i;
0862: gs.rawRead.i = 0;
0863: gs.totalChars += gs.charsWrote.i;
0864: //gs.bytesWrote.i = 0;
0865: gs.charsWrote.i = 0;
0866: }
0867: }
0868:
0869: /**
0870: * CommonGetsCleanup -> commonGetsCleanup
0871: *
0872: * Helper function used by getsObj to restore the channel after
0873: * a "gets" operation.
0874: *
0875: */
0876:
0877: void commonGetsCleanup() {
0878: ChannelBuffer buf, next;
0879:
0880: buf = inQueueHead;
0881: for (; buf != null; buf = next) {
0882: next = buf.next;
0883: if (buf.nextRemoved < buf.nextAdded) {
0884: break;
0885: }
0886: recycleBuffer(buf, false);
0887: }
0888: inQueueHead = buf;
0889: if (buf == null) {
0890: inQueueTail = null;
0891: } else {
0892: // If any multi-byte characters were split across channel buffer
0893: // boundaries, the split-up bytes were moved to the next channel
0894: // buffer by filterBytes(). Move the bytes back to their
0895: // original buffer because the caller could change the channel's
0896: // encoding which could change the interpretation of whether those
0897: // bytes really made up multi-byte characters after all.
0898:
0899: next = buf.next;
0900: for (; next != null; next = buf.next) {
0901: int extra;
0902:
0903: extra = buf.bufLength - buf.nextAdded;
0904: if (extra > 0) {
0905: System.arraycopy(next.buf, buf.BUFFER_PADDING
0906: - extra, buf.buf, buf.nextAdded, extra);
0907: buf.nextAdded += extra;
0908: next.nextRemoved = buf.BUFFER_PADDING;
0909: }
0910: buf = next;
0911: }
0912: }
0913: if (encoding != null) {
0914: //Tcl_FreeEncoding(encoding);
0915: }
0916: }
0917:
0918: // CloseChannel -> close
0919:
0920: void close() throws IOException {
0921: discardQueued(true);
0922: // FIXME: More close logic in CloseChannel
0923: }
0924:
0925: boolean eof() {
0926: return eofCond;
0927: }
0928:
0929: void setEncoding(String inEncoding) {
0930: encoding = inEncoding;
0931: }
0932:
0933: void setEofChar(char inEofChar) {
0934: eofChar = inEofChar;
0935: }
0936:
0937: void setTranslation(int inTranslation) {
0938: translation = inTranslation;
0939: }
0940:
0941: void setBuffering(int inBuffering) {
0942: buffering = inBuffering;
0943: }
0944:
0945: void setBufferSize(int inBufSize) {
0946: bufSize = inBufSize;
0947: }
0948:
0949: void setBlocking(boolean inBlocking) {
0950: blocking = inBlocking;
0951: }
0952:
0953: boolean isBlocked() {
0954: return blocked;
0955: }
0956:
0957: boolean sawCR() {
0958: return sawCR;
0959: }
0960:
0961: // Helper class to implement integer pass by reference
0962: // for methods like doReadChars, readBytes and so on.
0963:
0964: private class IntPtr {
0965: int i;
0966:
0967: IntPtr() {
0968: }
0969:
0970: IntPtr(int value) {
0971: i = value;
0972: }
0973: }
0974:
0975: /**
0976: * DoReadChars -> doReadChars
0977: *
0978: * Reads from the channel until the requested number of characters
0979: * have been seen, EOF is seen, or the channel would block. EOL
0980: * and EOF translation is done. If reading binary data, the raw
0981: * bytes are wrapped in a Tcl byte array object. Otherwise, the raw
0982: * bytes are converted to characters using the channel's current
0983: * encoding and stored in a Tcl string object.
0984: *
0985: * @param obj Input data is stored in this object.
0986: * @param toRead Maximum number of characters to store,
0987: * or -1 to read all available data (up to EOF
0988: * or when channel blocks).
0989: */
0990:
0991: int doReadChars(TclObject obj, int toRead) throws IOException {
0992: final boolean debug = false;
0993: ChannelBuffer buf;
0994: int copied, copiedNow, result;
0995: IntPtr offset = new IntPtr();
0996:
0997: if (encoding == null) {
0998: TclByteArray.setLength(null, obj, 0);
0999: } else {
1000: TclString.empty(obj);
1001: }
1002: offset.i = 0;
1003:
1004: // if toRead is negative, read until EOF
1005: if (toRead < 0) {
1006: toRead = Integer.MAX_VALUE;
1007: }
1008:
1009: done: {
1010: for (copied = 0; toRead > 0;) {
1011: copiedNow = -1;
1012: if (inQueueHead != null) {
1013: if (encoding == null) {
1014: if (debug) {
1015: System.out.println("calling readBytes "
1016: + toRead);
1017: }
1018: copiedNow = readBytes(obj, toRead, offset);
1019: } else {
1020: if (debug) {
1021: System.out.println("calling readChars "
1022: + toRead);
1023: }
1024: copiedNow = readChars(obj, toRead);
1025: }
1026:
1027: // If the current buffer is empty recycle it.
1028:
1029: buf = inQueueHead;
1030: if (debug) {
1031: System.out
1032: .println("after read* buf.nextRemoved is "
1033: + buf.nextRemoved);
1034: System.out
1035: .println("after read* buf.nextAdded is "
1036: + buf.nextAdded);
1037: }
1038: if (buf.nextRemoved == buf.nextAdded) {
1039: if (debug) {
1040: System.out
1041: .println("recycling empty buffer");
1042: }
1043: ChannelBuffer next;
1044:
1045: next = buf.next;
1046: recycleBuffer(buf, false);
1047: inQueueHead = next;
1048: if (next == null) {
1049: if (debug) {
1050: System.out
1051: .println("inQueueTail set to null");
1052: }
1053: inQueueTail = null;
1054: } else {
1055: if (debug) {
1056: System.out
1057: .println("inQueueTail is not null");
1058: }
1059: }
1060: }
1061: }
1062: if (copiedNow < 0) {
1063: if (debug) {
1064: System.out.println("copiedNow < 0");
1065: }
1066: if (eofCond) {
1067: if (debug) {
1068: System.out.println("eofCond");
1069: }
1070: break;
1071: }
1072: if (blocked) {
1073: if (debug) {
1074: System.out.println("blocked");
1075: }
1076: if (!blocking) {
1077: break;
1078: }
1079: blocked = false;
1080: }
1081: result = getInput();
1082: if (result != 0) {
1083: if (debug) {
1084: System.out.println("non-zero result");
1085: }
1086: if (result == TclPosixException.EAGAIN) {
1087: break;
1088: }
1089: copied = -1;
1090: break done; //goto done
1091: }
1092: } else {
1093: copied += copiedNow;
1094: if (debug) {
1095: System.out.println("copied incremented to "
1096: + copied);
1097: }
1098: toRead -= copiedNow;
1099: if (debug) {
1100: System.out.println("toRead decremented to "
1101: + toRead);
1102: }
1103: }
1104: }
1105:
1106: blocked = false;
1107:
1108: if (encoding == null) {
1109: TclByteArray.setLength(null, obj, offset.i);
1110: if (debug) {
1111: System.out.println("set byte array length to "
1112: + offset.i);
1113: }
1114: }
1115: } // end done: block
1116:
1117: //done:
1118: updateInterest();
1119:
1120: obj.invalidateStringRep();
1121: String srep = obj.toString();
1122:
1123: if (debug) {
1124: System.out.println("returning copied = " + copied);
1125: System.out.println("returning string \"" + srep + "\"");
1126: }
1127:
1128: if (copied > 0) {
1129: int len = srep.length();
1130: if (len != copied) {
1131: throw new TclRuntimeError("copied " + copied + " != "
1132: + len);
1133: }
1134: } else {
1135: if (!srep.equals("")) {
1136: throw new TclRuntimeError(
1137: "expected empty TclObject result"
1138: + " since copied is " + copied);
1139: }
1140: }
1141:
1142: return copied;
1143: }
1144:
1145: /**
1146: * ReadBytes -> readBytes
1147: *
1148: * Reads from the channel until the requested number of
1149: * bytes have been seen, EOF is seen, or the channel would
1150: * block. Bytes from the channel are stored in obj as a
1151: * ByteArray object. EOL and EOF translation are done.
1152: *
1153: * 'bytesToRead' can safely be a very large number because
1154: * space is only allocated to hold data read from the channel
1155: * as needed.
1156: *
1157: * The return value is the number of bytes appended to
1158: * the object.
1159: *
1160: * @param obj, the TclByteArrayObject we are operating on
1161: * @param bytesToRead, Maximum number of bytes to store.
1162: * Bytes are obtained from the first
1163: * buffer in the queue -- even if this number
1164: * is larger than the number of bytes only
1165: * the bytes from the first buffer are returned.
1166: * @param offsetPtr On input, contains how many bytes of
1167: * obj have been used to hold data. On
1168: * output, how many bytes are now being used.
1169: */
1170:
1171: int readBytes(TclObject obj, int bytesToRead, IntPtr offsetPtr) {
1172: final boolean debug = false;
1173: int toRead, srcOff, srcLen, offset, length;
1174: ChannelBuffer buf;
1175: IntPtr srcRead, dstWrote;
1176: byte[] src, dst;
1177:
1178: offset = offsetPtr.i;
1179:
1180: buf = inQueueHead;
1181: src = buf.buf;
1182: srcOff = buf.nextRemoved;
1183: srcLen = buf.nextAdded - buf.nextRemoved;
1184: if (debug) {
1185: System.out.println("readBytes() : src buffer len is "
1186: + buf.buf.length);
1187: System.out.println("readBytes() : buf.nextRemoved is "
1188: + buf.nextRemoved);
1189: System.out.println("readBytes() : buf.nextAdded is "
1190: + buf.nextAdded);
1191: }
1192:
1193: toRead = bytesToRead;
1194: if (toRead > srcLen) {
1195: toRead = srcLen;
1196: if (debug)
1197: System.out.println("readBytes() : toRead set to "
1198: + toRead);
1199: }
1200:
1201: length = TclByteArray.getLength(null, obj);
1202: dst = TclByteArray.getBytes(null, obj);
1203: if (debug) {
1204: System.out.println("readBytes() : toRead is " + toRead);
1205: System.out.println("readBytes() : length is " + length);
1206: System.out.println("readBytes() : array length is "
1207: + dst.length);
1208: }
1209:
1210: if (toRead > length - offset - 1) {
1211: if (debug) {
1212: System.out.println("readBytes() : TclObject too small");
1213: }
1214:
1215: // Double the existing size of the object or make enough room to
1216: // hold all the characters we may get from the source buffer,
1217: // whichever is larger.
1218:
1219: length = offset * 2;
1220: if (offset < toRead) {
1221: length = offset + toRead + 1;
1222: }
1223: dst = TclByteArray.setLength(null, obj, length);
1224: }
1225:
1226: if (needNL) {
1227: needNL = false;
1228: if ((srcLen == 0) || (src[srcOff] != '\n')) {
1229: dst[offset] = (byte) '\r';
1230: offsetPtr.i += 1;
1231: return 1;
1232: }
1233: dst[offset++] = (byte) '\n';
1234: srcOff++;
1235: srcLen--;
1236: toRead--;
1237: }
1238:
1239: srcRead = new IntPtr(srcLen);
1240: dstWrote = new IntPtr(toRead);
1241:
1242: if (translateEOL(dst, offset, src, srcOff, dstWrote, srcRead) != 0) {
1243: if (dstWrote.i == 0) {
1244: return -1;
1245: }
1246: }
1247:
1248: buf.nextRemoved += srcRead.i;
1249: offsetPtr.i += dstWrote.i;
1250: return dstWrote.i;
1251: }
1252:
1253: /**
1254: * ReadChars -> readChars
1255: *
1256: * Reads from the channel until the requested number of
1257: * characters have been seen, EOF is seen, or the channel would
1258: * block. Raw bytes from the channel are converted to characters
1259: * and stored in obj. EOL and EOF translation is done.
1260: *
1261: * 'charsToRead' can safely be a very large number because
1262: * space is only allocated to hold data read from the channel
1263: * as needed.
1264: *
1265: * The return value is the number of characters appended to
1266: * the object.
1267: *
1268: * @param obj, the TclByteArrayObject we are operating on
1269: * @param charsToRead, Maximum number of chars to store.
1270: * Chars are obtained from the first
1271: * buffer in the queue -- even if this number
1272: * is larger than the number of chars only
1273: * the chars from the first buffer are returned.
1274: */
1275:
1276: int readChars(TclObject tobj, int charsToRead) throws IOException {
1277: final boolean debug = false;
1278:
1279: int toRead, factor, spaceLeft, length, srcLen, dstNeeded;
1280: int srcOff, dstOff;
1281: IntPtr srcRead, numChars, dstRead, dstWrote;
1282: ChannelBuffer buf;
1283: byte[] src;
1284: char[] dst;
1285:
1286: if (debug) {
1287: System.out.println("readChars(tobj, " + charsToRead + ")");
1288: }
1289:
1290: srcRead = new IntPtr();
1291: numChars = new IntPtr();
1292: dstRead = new IntPtr();
1293: dstWrote = new IntPtr();
1294:
1295: buf = inQueueHead;
1296: src = buf.buf;
1297: srcOff = buf.nextRemoved;
1298: srcLen = buf.nextAdded - buf.nextRemoved;
1299:
1300: if (srcLen == 0) {
1301: if (debug) {
1302: System.out
1303: .println("srcLen is zero, checking for needNL");
1304: }
1305:
1306: if (needNL) {
1307: if (debug) {
1308: System.out.println("needNL was true");
1309: }
1310:
1311: needNL = false;
1312: TclString.append(tobj, "\r");
1313: return 1;
1314: }
1315: return -1;
1316: }
1317:
1318: // EOF char found during last invocation
1319:
1320: if (eofCond) {
1321: return -1;
1322: }
1323:
1324: toRead = charsToRead;
1325: if (toRead > srcLen) {
1326: // The requested read size in chars could be much larger than
1327: // the number of bytes in the buffer. Estimate a smaller
1328: // number of chars to read, assuming that a byte will
1329: // never be decoded to more than a single char.
1330:
1331: toRead = srcLen;
1332: }
1333:
1334: // FIXME : Do something to cache conversion buffer, or it might also
1335: // to pass the TclObject directly into the externalToUnicode method
1336: // so as to avoid the need for this extra buffer.
1337: dstNeeded = toRead;
1338: dst = new char[dstNeeded];
1339: dstOff = 0;
1340:
1341: if (debug) {
1342: System.out.println("using dst of size " + dstNeeded);
1343: }
1344:
1345: // FIXME: SF Tcl Bug 1462248
1346:
1347: if (needNL) {
1348: if (debug) {
1349: System.out.println("needNL held over");
1350: }
1351:
1352: // We want a '\n' because the last character we saw was '\r'.
1353:
1354: needNL = false;
1355:
1356: externalToUnicode(src, srcOff, srcLen, dst, dstOff, 1,
1357: srcRead, dstWrote, numChars, null);
1358: if ((numChars.i > 0) && (dst[dstOff] == '\n')) {
1359: // The next char was a '\n'. Consume it and produce a '\n'.
1360: buf.nextRemoved += srcRead.i;
1361: } else {
1362: // The next char was not a '\n'. Produce a '\r'.
1363: dst[dstOff] = '\r';
1364: }
1365: encodingStart = false;
1366: TclString.append(tobj, dst, dstOff, 1);
1367: return 1;
1368: }
1369:
1370: ArrayList charToBytes = new ArrayList(dstNeeded);
1371:
1372: externalToUnicode(src, srcOff, srcLen, dst, dstOff, dstNeeded,
1373: srcRead, dstWrote, numChars, charToBytes);
1374:
1375: if (srcRead.i == 0) {
1376: if (debug) {
1377: System.out
1378: .println("incomplete char from externalToUnicode");
1379: }
1380:
1381: // Not enough bytes in src buffer to make a complete char. Copy
1382: // the bytes to the next buffer to make a new contiguous string,
1383: // then tell the caller to fill the buffer with more bytes.
1384:
1385: ChannelBuffer next;
1386:
1387: next = buf.next;
1388: if (next == null) {
1389: if (srcLen > 0) {
1390: // There isn't enough data in the buffers to complete the next
1391: // character, so we need to wait for more data before the next
1392: // file event can be delivered.
1393: //
1394: // SF #478856.
1395: //
1396: // The exception to this is if the input buffer was
1397: // completely empty before we tried to convert its
1398: // contents. Nothing in, nothing out, and no incomplete
1399: // character data. The conversion before the current one
1400: // was complete.
1401:
1402: needMoreData = true;
1403: }
1404: return -1;
1405: }
1406:
1407: // Space is made at the beginning of the buffer to copy the
1408: // previous unused bytes there. Check first if the buffer we
1409: // are using actually has enough space at its beginning for
1410: // the data we are copying. Because if not we will write over the
1411: // buffer management information, especially the 'nextPtr'.
1412: //
1413: // Note that the BUFFER_PADDING (See AllocChannelBuffer) is
1414: // used to prevent exactly this situation. I.e. it should
1415: // never happen. Therefore it is ok to panic should it happen
1416: // despite the precautions.
1417:
1418: if ((next.nextRemoved - srcLen) < 0) {
1419: throw new TclRuntimeError(
1420: "Buffer Underflow, BUFFER_PADDING not enough");
1421: }
1422: next.nextRemoved -= srcLen;
1423: System.arraycopy(src, srcOff, next.buf, next.nextRemoved,
1424: srcLen);
1425: recycleBuffer(buf, false);
1426: inQueueHead = next;
1427: return readChars(tobj, charsToRead);
1428: }
1429:
1430: dstRead.i = dstWrote.i;
1431: if (translateEOL(dst, dstOff, dst, dstOff, dstWrote, dstRead) != 0) {
1432: // Hit EOF char. How many bytes of src correspond to where the
1433: // EOF was located in dst?
1434:
1435: if (debug) {
1436: System.out.println("found EOF char in translateEOL");
1437: }
1438:
1439: if (dstWrote.i == 0) {
1440: return -1;
1441: }
1442:
1443: if (debug) {
1444: System.out.println("read " + dstRead.i
1445: + " chars in translateEOL");
1446: System.out.println("wrote " + dstWrote.i
1447: + " chars in translateEOL");
1448: }
1449:
1450: int numBytes = decodedNumBytes(charToBytes, dstOff,
1451: dstRead.i);
1452: srcRead.i = numBytes;
1453: dstWrote.i = dstWrote.i;
1454: numChars.i = dstWrote.i;
1455:
1456: translateEOL(dst, dstOff, dst, dstOff, dstWrote, dstRead);
1457: }
1458:
1459: // The number of characters that we got may be less than the number
1460: // that we started with because "\r\n" sequences may have been
1461: // turned into just '\n' in dst.
1462:
1463: numChars.i -= (dstRead.i - dstWrote.i);
1464:
1465: if (debug) {
1466: System.out.println("reduced numChars.i by "
1467: + (dstRead.i - dstWrote.i));
1468: }
1469:
1470: if (numChars.i > toRead) {
1471: throw new TclRuntimeError("got more chars " + numChars.i
1472: + " than requested " + toRead);
1473: }
1474: encodingStart = false;
1475:
1476: buf.nextRemoved += srcRead.i;
1477:
1478: if (debug) {
1479: System.out.println("advanced buf.nextRemoved by "
1480: + srcRead.i);
1481: }
1482:
1483: TclString.append(tobj, dst, dstOff, numChars.i);
1484:
1485: if (debug) {
1486: System.out.println("appended " + numChars.i
1487: + " chars, TclObject is now \"" + tobj.toString()
1488: + "\"");
1489: }
1490:
1491: return numChars.i;
1492: }
1493:
1494: // FIXME: Only define the ones that we actually need/use.
1495:
1496: // The following definitions are the error codes returned by externalToUnicode
1497: //
1498: // TCL.OK: All characters were converted.
1499: //
1500: // TCL_CONVERT_NOSPACE: The output buffer would not have been large
1501: // enough for all of the converted data; as many
1502: // characters as could fit were converted though.
1503: //
1504: // TCL_CONVERT_MULTIBYTE: The last few bytes in the source string were
1505: // the beginning of a multibyte sequence, but
1506: // more bytes were needed to complete this
1507: // sequence. A subsequent call to the conversion
1508: // routine should pass the beginning of this
1509: // unconverted sequence plus additional bytes
1510: // from the source stream to properly convert
1511: // the formerly split-up multibyte sequence.
1512: //
1513: // TCL_CONVERT_SYNTAX: The source stream contained an invalid
1514: // character sequence. This may occur if the
1515: // input stream has been damaged or if the input
1516: // encoding method was misidentified. This error
1517: // is reported only if TCL_ENCODING_STOPONERROR
1518: // was specified.
1519: //
1520: // TCL_CONVERT_UNKNOWN: The source string contained a character
1521: // that could not be represented in the target
1522: // encoding. This error is reported only if
1523: // TCL_ENCODING_STOPONERROR was specified.
1524:
1525: private final int TCL_CONVERT_MULTIBYTE = -1;
1526: private final int TCL_CONVERT_SYNTAX = -2;
1527: private final int TCL_CONVERT_UNKNOWN = -3;
1528: private final int TCL_CONVERT_NOSPACE = -4;
1529:
1530: /**
1531: * Tcl_ExternalToUtf -> externalToUnicode
1532: *
1533: * Convert a source buffer from the specified encoding into Unicode.
1534: *
1535: * FIXME: Add doc for return values
1536: *
1537: * @param src, Source bytes in specified encoding.
1538: * @param srcOff, First index in src input array.
1539: * @param srcLen, Number of bytes in src buffer.
1540: * @param dst, Array to store unicode characters in.
1541: * @param dstOff, First available index in dst array.
1542: * @param dstLen, Length of dst array.
1543: * @param srcReadPtr, Filled with the number of bytes from
1544: * the source string that were converted.
1545: * This may be less than the original source
1546: * length if there was a problem converting
1547: * some source characters.
1548: * @param dstWrotePtr, Filled with the number of chars that were
1549: * stored in the output buffer as a result of
1550: * the conversion
1551: * @param dstCharsPtr, Filled with the number of characters that
1552: * correspond to the bytes stored in the
1553: * output buffer.
1554: * @param charToBytes, Contains int's that are used to map converted
1555: * bytes to chars. Can be null.
1556: */
1557:
1558: int externalToUnicode(byte[] src, final int srcOff,
1559: final int srcLen, char[] dst, final int dstOff,
1560: final int dstLen, IntPtr srcReadPtr, IntPtr dstWrotePtr,
1561: IntPtr dstCharsPtr, ArrayList charToBytes) {
1562: final boolean debug = false;
1563: final boolean debug_decode_loop = debug && false;
1564: int result = TCL.OK;
1565:
1566: if (encoding == null) {
1567: // This should never happen
1568: throw new TclRuntimeError(
1569: "externalToUnicode called with null encoding");
1570: }
1571:
1572: if (debug) {
1573: System.out.println("externalToUnicode( " + srcLen + " "
1574: + dstLen + " )");
1575: }
1576:
1577: // If decoder was flushed already then return 0.
1578:
1579: if ((srcLen == 0) && !encodingEnd) {
1580: if (debug) {
1581: System.out
1582: .println("srcLen is zero and encodingEnd is false");
1583: }
1584:
1585: srcReadPtr.i = 0;
1586: if (dstWrotePtr != null)
1587: dstWrotePtr.i = 0;
1588: if (dstCharsPtr != null)
1589: dstCharsPtr.i = 0;
1590: return 0;
1591: }
1592:
1593: // Convert bytes from src into unicode chars and store them in dst.
1594:
1595: if (debug) {
1596: System.out.println("now to decode byte array of length "
1597: + srcLen);
1598: System.out.println("srcOff is " + srcOff);
1599: for (int i = srcOff; i < (srcOff + srcLen); i++) {
1600: System.out.println("(byte) '" + ((char) src[i]) + "'"
1601: + " (int) " + ((int) src[i]) + " (hex) 0x"
1602: + Integer.toHexString(src[i]));
1603: }
1604: System.out.println("encoded as " + encoding);
1605: System.out.println("encodingStart is " + encodingStart);
1606: System.out.println("eofCond is " + eofCond);
1607: }
1608:
1609: // FIXME: In the cases where we know that we don't actually want
1610: // to copy the data, we could pass a flag so that we could
1611: // take advantage of encodings that had a one to one mapping
1612: // from bytes to chars (now need to copy then to find bytes used).
1613:
1614: if (csd == null) {
1615: // Note that UnsupportedCharsetException should never be raised
1616: // here since EncodingCmd.isSupported() should have already
1617: // returned true for this encoding.
1618:
1619: Charset chrset = Charset.forName(encoding);
1620: csd = chrset.newDecoder();
1621: }
1622: if (encodingStart) {
1623: csd.reset();
1624: }
1625:
1626: // Note that the default action on bad encoded
1627: // input is to stop and and report the error.
1628:
1629: // Implement byte -> char decoding loop. The
1630: // Java charset APIs provide no way to save
1631: // a decoder state or determine how many bytes
1632: // were consumed when a char is decoded. Since
1633: // we can't keep a copy of all data ever read,
1634: // this module has to make sure that only
1635: // one decode operation is done for input bytes
1636: // and that the number of bytes consumed for
1637: // each char is saved.
1638:
1639: int bytes_read = 0;
1640: int chars_written = 0;
1641: int dstOffI = dstOff;
1642:
1643: ByteBuffer srcb = ByteBuffer.wrap(src, srcOff, srcLen);
1644: CharBuffer oneChar = CharBuffer.allocate(1);
1645:
1646: while ((srcb.remaining() > 0) && (dstOffI < dstLen)) {
1647: if (debug_decode_loop) {
1648: System.out.println("char decode loop "
1649: + (chars_written + 1));
1650: System.out.println("srcb.position() is "
1651: + srcb.position());
1652: System.out.println("srcb.remaining() is "
1653: + srcb.remaining());
1654: byte b = srcb.get(srcb.position()); // Get byte without moving position
1655: System.out.println("current byte is '" + ((char) b)
1656: + "' " + "0x" + Integer.toHexString(b));
1657: }
1658:
1659: int srcbStartPos = srcb.position();
1660: oneChar.rewind();
1661:
1662: // srcb is never empty when this method is invoked
1663:
1664: CoderResult cresult = csd.decode(srcb, oneChar, false);
1665:
1666: if (cresult == CoderResult.UNDERFLOW) {
1667: // An UNDERFLOW could mean that srcb is now
1668: // empty. It could also mean that a valid char
1669: // char could not be decoded from srcb bytes.
1670:
1671: if (debug_decode_loop) {
1672: System.out.println("UNDERFLOW detected");
1673: }
1674:
1675: if ((oneChar.position() == 0) && (srcb.remaining() > 0)) {
1676: if (debug) {
1677: System.out.println("TCL_CONVERT_MULTIBYTE set");
1678: }
1679: result = TCL_CONVERT_MULTIBYTE;
1680: break;
1681: } else {
1682: // Some bytes were decoded into 1 char.
1683:
1684: if (debug_decode_loop) {
1685: System.out.println("some bytes decoded");
1686: }
1687: }
1688: } else if (cresult == CoderResult.OVERFLOW) {
1689: // Bytes from src have been decoded into 1 char.
1690:
1691: if (debug_decode_loop) {
1692: System.out.println("OVERFLOW detected");
1693: }
1694: } else {
1695: // Some unexpected result, like an invalid character
1696: result = TCL_CONVERT_MULTIBYTE;
1697: break;
1698: }
1699:
1700: int bytes_this _char = srcb.position() - srcbStartPos;
1701:
1702: if (oneChar.position() != 1) {
1703: throw new TclRuntimeError(
1704: "expected 1 char to be decoded, got "
1705: + oneChar.position());
1706: }
1707:
1708: char c = oneChar.get(0);
1709: dst[dstOffI++] = c;
1710:
1711: if (charToBytes != null) {
1712: Integer iobj;
1713: if (bytes_this _char == 1) {
1714: iobj = oneInteger;
1715: } else if (bytes_this _char == 2) {
1716: iobj = twoInteger;
1717: } else if (bytes_this _char == 3) {
1718: iobj = threeInteger;
1719: } else {
1720: iobj = new Integer(bytes_this _char);
1721: }
1722: charToBytes.add(iobj);
1723: }
1724:
1725: bytes_read += bytes_this _char;
1726: chars_written++;
1727:
1728: if (debug_decode_loop) {
1729: System.out.println("char '" + c + "' " + "0x"
1730: + Integer.toHexString(c) + " decoded from "
1731: + bytes_this _char + " bytes");
1732: }
1733: }
1734:
1735: if (debug) {
1736: System.out.println("decoded " + chars_written
1737: + " chars from " + bytes_read + " bytes");
1738:
1739: for (int i = 0; i < chars_written; i++) {
1740: int ind = dstOff + i;
1741: System.out.println("(char) '" + dst[ind] + "'"
1742: + " (int) " + ((int) dst[ind]) + " (hex) 0x"
1743: + Integer.toHexString(dst[ind]));
1744:
1745: if (charToBytes != null) {
1746: Integer iobj = (Integer) charToBytes.get(i);
1747: System.out.println("char was decoded from "
1748: + iobj.intValue() + " bytes");
1749: }
1750: }
1751: }
1752:
1753: if (!(dstOffI < dstLen) && (srcb.remaining() > 0)) {
1754: // The dst buffer is full but bytes remain
1755: // in srcb. This could happen when this
1756: // method is invoked with a small buffer
1757: // so that the number of consumed bytes
1758: // can be determined.
1759:
1760: if (debug) {
1761: System.out.println("TCL_CONVERT_NOSPACE detected");
1762: }
1763:
1764: result = TCL_CONVERT_NOSPACE;
1765: }
1766:
1767: boolean atEOF = (eofCond && encodingEnd);
1768:
1769: if (atEOF && !(dstOffI < dstLen)) {
1770: result = TCL_CONVERT_NOSPACE;
1771: }
1772:
1773: if (atEOF && (result == TCL.OK)) {
1774: // EOF flag must be passed to the decoder before
1775: // it can be flushed. This can only be done after
1776: // EOF was read from the input. If there was an
1777: // error, then don't pass EOF or flush the decoder.
1778:
1779: ByteBuffer emptyBytes = ByteBuffer.allocate(0);
1780: CharBuffer dstb = CharBuffer.wrap(dst, dstOffI, dstLen
1781: - dstOffI);
1782:
1783: int dstbStartPos = dstb.position();
1784:
1785: CoderResult cresult1 = csd.decode(emptyBytes, dstb, true);
1786: CoderResult cresult2 = csd.flush(dstb);
1787:
1788: if (cresult2 == CoderResult.OVERFLOW) {
1789: result = TCL_CONVERT_NOSPACE;
1790: } else {
1791: encodingEnd = false;
1792: }
1793:
1794: int chars_flushed = dstb.position() - chars_written;
1795: chars_written += chars_flushed;
1796:
1797: if (debug) {
1798: System.out.println("flushed " + chars_flushed
1799: + " chars at EOF");
1800: }
1801: }
1802:
1803: srcReadPtr.i = bytes_read;
1804: if (dstWrotePtr != null)
1805: dstWrotePtr.i = chars_written;
1806: if (dstCharsPtr != null)
1807: dstCharsPtr.i = chars_written;
1808:
1809: return result;
1810: }
1811:
1812: /**
1813: * GetInput -> getInput
1814: *
1815: * Reads input data from a device into a channel buffer.
1816: *
1817: * The return value is the Posix error code if an error occurred while
1818: * reading from the file, or 0 otherwise.
1819: */
1820:
1821: private int getInput() {
1822: final boolean debug = false;
1823: int toRead;
1824: int result;
1825: int nread;
1826:
1827: // if (checkForDeadChannel()) return EINVAL;
1828:
1829: // Skipped pushback processing code for stacked Channels
1830:
1831: // See if we can fill an existing buffer. If we can, read only
1832: // as much as will fit in it. Otherwise allocate a new buffer,
1833: // add it to the input queue and attempt to fill it to the max.
1834:
1835: ChannelBuffer buf = inQueueTail;
1836:
1837: if ((buf != null) && (buf.nextAdded < buf.bufLength)) {
1838: if (debug) {
1839: System.out.println("smaller than buffer");
1840: }
1841: toRead = buf.bufLength - buf.nextAdded;
1842: } else {
1843: if (debug) {
1844: System.out.println("fits in existing buffer");
1845: }
1846:
1847: buf = saveInBuf;
1848: saveInBuf = null;
1849:
1850: // Check the actual buffersize against the requested
1851: // buffersize. Buffers which are smaller than requested are
1852: // squashed. This is done to honor dynamic changes of the
1853: // buffersize made by the user.
1854:
1855: if ((buf != null)
1856: && ((buf.bufLength - buf.BUFFER_PADDING) < bufSize)) {
1857: buf = null;
1858: }
1859: if (buf == null) {
1860: if (debug) {
1861: System.out
1862: .println("allocated ChannelBuffer of size "
1863: + bufSize);
1864: }
1865: buf = new ChannelBuffer(bufSize);
1866: }
1867: buf.next = null;
1868:
1869: // Use the actual size of the buffer to determine
1870: // the number of bytes to read from the channel and not the
1871: // size for new buffers. They can be different if the
1872: // buffersize was changed between reads.
1873:
1874: toRead = buf.bufLength - buf.nextAdded;
1875: if (debug) {
1876: System.out.println("toRead set to " + toRead);
1877: }
1878:
1879: if (inQueueTail == null) {
1880: inQueueHead = buf;
1881: } else {
1882: inQueueTail.next = buf;
1883: }
1884:
1885: inQueueTail = buf;
1886: }
1887:
1888: // If EOF is set, we should avoid calling the driver because on some
1889: // platforms it is impossible to read from a device after EOF.
1890:
1891: if (eofCond) {
1892: if (debug) {
1893: System.out.println("eofCond was true, no error return");
1894: }
1895: return 0;
1896: }
1897:
1898: // FIXME: We do not handle non-blocking or this CHANNEL_TIMER_FEV flag yet
1899:
1900: if (/*CHANNEL_TIMER_FEV &&*/
1901: !blocking) {
1902: return TclPosixException.EWOULDBLOCK;
1903: } else {
1904: result = 0;
1905:
1906: // Can we even use this for a brain-dead nonblocking IO check?
1907: int numAvailable = /*input.available();*/0;
1908:
1909: if (!blocking && (numAvailable < toRead)) {
1910: result = TclPosixException.EWOULDBLOCK;
1911: nread = -1;
1912: } else {
1913: try {
1914: if (debug) {
1915: System.out.println("now to read " + toRead
1916: + " bytes");
1917: }
1918:
1919: nread = input.read(buf.buf, buf.nextAdded, toRead);
1920:
1921: // read() returns -1 on EOF
1922: if (nread == -1) {
1923: if (debug) {
1924: System.out
1925: .println("got EOF from read() call");
1926: }
1927: nread = 0;
1928: }
1929: } catch (IOException ex) {
1930: // FIXME: How do we recover from IO errors here?
1931: // I think we need to set result to a POSIX error
1932: ex.printStackTrace(System.err);
1933: nread = -1;
1934: }
1935: }
1936: }
1937:
1938: if (nread > 0) {
1939: if (debug) {
1940: System.out.println("nread is " + nread);
1941: }
1942: buf.nextAdded += nread;
1943:
1944: // should avoid calling the driver because on some platforms we
1945: // will block in the low level reading code even though the
1946: // channel is set into nonblocking mode.
1947:
1948: if (nread < toRead) {
1949: blocked = true;
1950: }
1951: } else if (nread == 0) {
1952: eofCond = true;
1953: encodingEnd = true;
1954: if (debug) {
1955: System.out
1956: .println("nread is zero, eofCond set, encodingEnd set");
1957: }
1958: } else if (nread < 0) {
1959: if (debug) {
1960: System.out.println("nread is " + nread);
1961: }
1962: if ((result == TclPosixException.EWOULDBLOCK)
1963: || (result == TclPosixException.EAGAIN)) {
1964: blocked = true;
1965: result = TclPosixException.EAGAIN;
1966: }
1967: // FIXME: Called needs to raise a TclException
1968: //Tcl_SetErrno(result);
1969: return result;
1970: }
1971: if (debug) {
1972: System.out.println("no error return");
1973: }
1974: return 0;
1975: }
1976:
1977: /**
1978: * RecycleBuffer -> recycleBuffer
1979: *
1980: * Helper function to recycle input buffers. Ensures that
1981: * two input buffers are saved (one in the input queue and
1982: * another in the saveInBuf field). Only if these conditions
1983: * are met is the buffer released so that it can be
1984: * garbage collected.
1985: */
1986:
1987: private void recycleBuffer(ChannelBuffer buf, boolean mustDiscard) {
1988:
1989: if (mustDiscard)
1990: return;
1991:
1992: // Only save buffers which are at least as big as the requested
1993: // buffersize for the channel. This is to honor dynamic changes
1994: // of the buffersize made by the user.
1995:
1996: if ((buf.bufLength - buf.BUFFER_PADDING) < bufSize) {
1997: return;
1998: }
1999:
2000: if (inQueueHead == null) {
2001: inQueueHead = buf;
2002: inQueueTail = buf;
2003:
2004: buf.nextRemoved = buf.BUFFER_PADDING;
2005: buf.nextAdded = buf.BUFFER_PADDING;
2006: buf.next = null;
2007: return;
2008: }
2009: if (saveInBuf == null) {
2010: saveInBuf = buf;
2011:
2012: buf.nextRemoved = buf.BUFFER_PADDING;
2013: buf.nextAdded = buf.BUFFER_PADDING;
2014: buf.next = null;
2015: return;
2016: }
2017: }
2018:
2019: /**
2020: * DiscardInputQueued -> discardQueued
2021: *
2022: * Discards any input read from the channel but not yet consumed
2023: * by Tcl reading commands.
2024: */
2025:
2026: private void discardQueued(boolean discardSavedBuffers) {
2027: ChannelBuffer buf, nxt;
2028:
2029: buf = inQueueHead;
2030: inQueueHead = null;
2031: inQueueTail = null;
2032: for (; buf != null; buf = nxt) {
2033: nxt = buf.next;
2034: recycleBuffer(buf, discardSavedBuffers);
2035: }
2036:
2037: // If discardSavedBuffers is true, must also discard any previously
2038: // saved buffer in the saveInBuf field.
2039:
2040: if (discardSavedBuffers) {
2041: if (saveInBuf != null) {
2042: saveInBuf = null;
2043: }
2044: }
2045: }
2046:
2047: /**
2048: * TranslateInputEOL -> translateEOL
2049: *
2050: * Perform input EOL and EOF translation on the source buffer,
2051: * leaving the translated result in the destination buffer.
2052: *
2053: * Results:
2054: * The return value is 1 if the EOF character was found when
2055: * copying bytes to the destination buffer, 0 otherwise.
2056: *
2057: * @param dstArray, Output buffer to fill with translated bytes or chars.
2058: * @param dstStart, First unused index in the dst output array.
2059: * @param srcArray, Input buffer that holds the bytes or chars to translate
2060: * @param srcStart, Index of first available byte in src array.
2061:
2062: * @param dstLenPtr, On entry, the maximum length of output
2063: * buffer in bytes or chars; must be <= srcLenPtr.i. On
2064: * exit, the number of bytes or chars actually used in
2065: * output buffer.
2066: * @param srcLenPtr, On entry, the length of source buffer.
2067: * On exit, the number of bytes or chars read from
2068: * the source buffer.
2069: */
2070:
2071: int translateEOL(Object dstArray, int dstStart, Object srcArray,
2072: int srcStart, IntPtr dstLenPtr, IntPtr srcLenPtr) {
2073: final boolean debug = false;
2074:
2075: // Figure out if the srcArray and dstArray buffers
2076: // are byte or char arrays.
2077: boolean isCharType;
2078: char[] srcArrayChar, dstArrayChar;
2079: byte[] srcArrayByte, dstArrayByte;
2080:
2081: if ((srcArray instanceof char[])
2082: && (dstArray instanceof char[])) {
2083: isCharType = true;
2084: srcArrayChar = (char[]) srcArray;
2085: dstArrayChar = (char[]) dstArray;
2086: srcArrayByte = null;
2087: dstArrayByte = null;
2088: } else if ((srcArray instanceof byte[])
2089: && (dstArray instanceof byte[])) {
2090: isCharType = false;
2091: srcArrayChar = null;
2092: dstArrayChar = null;
2093: srcArrayByte = (byte[]) srcArray;
2094: dstArrayByte = (byte[]) dstArray;
2095: } else {
2096: throw new TclRuntimeError("unknown array argument types");
2097: }
2098:
2099: int dstLen, srcLen, inEofChar, index;
2100: int eof;
2101:
2102: dstLen = dstLenPtr.i;
2103:
2104: eof = -1;
2105: inEofChar = eofChar;
2106: if (inEofChar != '\0') {
2107: // Find EOF in translated buffer then compress out the EOL. The
2108: // source buffer may be much longer than the destination buffer
2109: // we only want to return EOF if the EOF has been copied to the
2110: // destination buffer.
2111:
2112: int src, srcMax;
2113:
2114: srcMax = srcStart + srcLenPtr.i;
2115: for (src = srcStart; src < srcMax; src++) {
2116: if (isCharType) {
2117: index = srcArrayChar[src];
2118: } else {
2119: index = srcArrayByte[src];
2120: }
2121: if (index == inEofChar) {
2122: eof = src;
2123: srcLen = src - srcStart;
2124: if (srcLen < dstLen) {
2125: dstLen = srcLen;
2126: }
2127: srcLenPtr.i = srcLen;
2128: break;
2129: }
2130: }
2131: }
2132: switch (translation) {
2133: case TclIO.TRANS_LF: {
2134: if ((dstArray != srcArray)
2135: || ((dstArray == srcArray) && (dstStart != srcStart))) {
2136: System.arraycopy(srcArray, srcStart, dstArray,
2137: dstStart, dstLen);
2138: }
2139: srcLen = dstLen;
2140: break;
2141: }
2142: case TclIO.TRANS_CR: {
2143: int dst, dstEnd;
2144:
2145: if ((dstArray != srcArray)
2146: || ((dstArray == srcArray) && (dstStart != srcStart))) {
2147: System.arraycopy(srcArray, srcStart, dstArray,
2148: dstStart, dstLen);
2149: }
2150: dstEnd = dstStart + dstLen;
2151: if (isCharType) {
2152: for (dst = dstStart; dst < dstEnd; dst++) {
2153: if (dstArrayChar[dst] == '\r') {
2154: dstArrayChar[dst] = '\n';
2155: }
2156: }
2157: } else {
2158: for (dst = dstStart; dst < dstEnd; dst++) {
2159: if (dstArrayByte[dst] == '\r') {
2160: dstArrayByte[dst] = (byte) '\n';
2161: }
2162: }
2163: }
2164: srcLen = dstLen;
2165: break;
2166: }
2167: case TclIO.TRANS_CRLF: {
2168: int dst;
2169: int src, srcEnd, srcMax;
2170:
2171: dst = dstStart;
2172: src = srcStart;
2173: srcEnd = srcStart + dstLen;
2174: srcMax = srcStart + srcLenPtr.i;
2175:
2176: if (isCharType) {
2177: for (; src < srcEnd;) {
2178: if (srcArrayChar[src] == '\r') {
2179: src++;
2180: if (src >= srcMax) {
2181: needNL = true;
2182: } else if (srcArrayChar[src] == '\n') {
2183: dstArrayChar[dst++] = srcArrayChar[src++];
2184: } else {
2185: dstArrayChar[dst++] = '\r';
2186: }
2187: } else {
2188: dstArrayChar[dst++] = srcArrayChar[src++];
2189: }
2190: }
2191: } else {
2192: for (; src < srcEnd;) {
2193: if (srcArrayByte[src] == '\r') {
2194: src++;
2195: if (src >= srcMax) {
2196: needNL = true;
2197: } else if (srcArrayByte[src] == '\n') {
2198: dstArrayByte[dst++] = srcArrayByte[src++];
2199: } else {
2200: dstArrayByte[dst++] = (byte) '\r';
2201: }
2202: } else {
2203: dstArrayByte[dst++] = srcArrayByte[src++];
2204: }
2205: }
2206: }
2207:
2208: srcLen = src - srcStart;
2209: dstLen = dst - dstStart;
2210: break;
2211: }
2212: case TclIO.TRANS_AUTO: {
2213: int dst;
2214: int src, srcEnd, srcMax;
2215:
2216: dst = dstStart;
2217: src = srcStart;
2218: srcEnd = srcStart + dstLen;
2219: srcMax = srcStart + srcLenPtr.i;
2220:
2221: if (sawCR && (src < srcMax)) {
2222: if (isCharType) {
2223: index = srcArrayChar[src];
2224: } else {
2225: index = srcArrayByte[src];
2226: }
2227: if (index == '\n') {
2228: src++;
2229: }
2230: sawCR = false;
2231: }
2232: if (isCharType) {
2233: for (; src < srcEnd;) {
2234: if (srcArrayChar[src] == '\r') {
2235: src++;
2236: if (src >= srcMax) {
2237: sawCR = true;
2238: } else if (srcArrayChar[src] == '\n') {
2239: if (srcEnd < srcMax) {
2240: srcEnd++;
2241: }
2242: src++;
2243: }
2244: dstArrayChar[dst++] = '\n';
2245: } else {
2246: dstArrayChar[dst++] = srcArrayChar[src++];
2247: }
2248: }
2249: } else {
2250: for (; src < srcEnd;) {
2251: if (srcArrayByte[src] == '\r') {
2252: src++;
2253: if (src >= srcMax) {
2254: sawCR = true;
2255: } else if (srcArrayByte[src] == '\n') {
2256: if (srcEnd < srcMax) {
2257: srcEnd++;
2258: }
2259: src++;
2260: }
2261: dstArrayByte[dst++] = (byte) '\n';
2262: } else {
2263: dstArrayByte[dst++] = srcArrayByte[src++];
2264: }
2265: }
2266: }
2267: srcLen = src - srcStart;
2268: dstLen = dst - dstStart;
2269: break;
2270: }
2271: default: {
2272: throw new TclRuntimeError("invalid translation");
2273: }
2274: }
2275: dstLenPtr.i = dstLen;
2276:
2277: if ((eof != -1) && (srcStart + srcLen >= eof)) {
2278: // EOF character was seen in EOL translated range. Leave current
2279: // file position pointing at the EOF character, but don't store the
2280: // EOF character in the output string.
2281:
2282: eofCond = true;
2283: stickyEofCond = true;
2284: encodingEnd = true;
2285: sawCR = false;
2286: needNL = false;
2287: return 1;
2288: }
2289:
2290: srcLenPtr.i = srcLen;
2291: return 0;
2292: }
2293:
2294: /**
2295: * UpdateInterest -> updateInterest
2296: *
2297: * Arrange for the notifier to call us back at appropriate times
2298: * based on the current state of the channel.
2299: */
2300:
2301: void updateInterest() {
2302: // FIXME: Currently unimplemented
2303: }
2304:
2305: /**
2306: * Tcl_InputBuffered -> getNumBufferedBytes
2307: *
2308: * Return the number of bytes that are current buffered.
2309: */
2310:
2311: int getNumBufferedBytes() {
2312: ChannelBuffer buf;
2313: int IOQueued;
2314: for (IOQueued = 0, buf = inQueueHead; buf != null; buf = buf.next) {
2315: IOQueued += buf.nextAdded - buf.nextRemoved;
2316: }
2317: return IOQueued;
2318: }
2319:
2320: /**
2321: * seekReset
2322: *
2323: * Helper method used to reset state info when doing a seek.
2324: */
2325:
2326: void seekReset() {
2327: discardQueued(false);
2328: eofCond = false;
2329: stickyEofCond = false;
2330: blocked = false;
2331: sawCR = false;
2332: // FIXME: Change needed in Tcl
2333: //needNL = false;
2334: }
2335: }
|