Source Code Cross Referenced for TclInputStream.java in  » Scripting » jacl » tcl » lang » Java Source Code / Java DocumentationJava Source Code and Java Documentation

Java Source Code / Java Documentation
1. 6.0 JDK Core
2. 6.0 JDK Modules
3. 6.0 JDK Modules com.sun
4. 6.0 JDK Modules com.sun.java
5. 6.0 JDK Modules sun
6. 6.0 JDK Platform
7. Ajax
8. Apache Harmony Java SE
9. Aspect oriented
10. Authentication Authorization
11. Blogger System
12. Build
13. Byte Code
14. Cache
15. Chart
16. Chat
17. Code Analyzer
18. Collaboration
19. Content Management System
20. Database Client
21. Database DBMS
22. Database JDBC Connection Pool
23. Database ORM
24. Development
25. EJB Server geronimo
26. EJB Server GlassFish
27. EJB Server JBoss 4.2.1
28. EJB Server resin 3.1.5
29. ERP CRM Financial
30. ESB
31. Forum
32. GIS
33. Graphic Library
34. Groupware
35. HTML Parser
36. IDE
37. IDE Eclipse
38. IDE Netbeans
39. Installer
40. Internationalization Localization
41. Inversion of Control
42. Issue Tracking
43. J2EE
44. JBoss
45. JMS
46. JMX
47. Library
48. Mail Clients
49. Net
50. Parser
51. PDF
52. Portal
53. Profiler
54. Project Management
55. Report
56. RSS RDF
57. Rule Engine
58. Science
59. Scripting
60. Search Engine
61. Security
62. Sevlet Container
63. Source Control
64. Swing Library
65. Template Engine
66. Test Coverage
67. Testing
68. UML
69. Web Crawler
70. Web Framework
71. Web Mail
72. Web Server
73. Web Services
74. Web Services apache cxf 2.0.1
75. Web Services AXIS2
76. Wiki Engine
77. Workflow Engines
78. XML
79. XML UI
Java
Java Tutorial
Java Open Source
Jar File Download
Java Articles
Java Products
Java by API
Photoshop Tutorials
Maya Tutorials
Flash Tutorials
3ds-Max Tutorials
Illustrator Tutorials
GIMP Tutorials
C# / C Sharp
C# / CSharp Tutorial
C# / CSharp Open Source
ASP.Net
ASP.NET Tutorial
JavaScript DHTML
JavaScript Tutorial
JavaScript Reference
HTML / CSS
HTML CSS Reference
C / ANSI-C
C Tutorial
C++
C++ Tutorial
Ruby
PHP
Python
Python Tutorial
Python Open Source
SQL Server / T-SQL
SQL Server / T-SQL Tutorial
Oracle PL / SQL
Oracle PL/SQL Tutorial
PostgreSQL
SQL / MySQL
MySQL Tutorial
VB.Net
VB.Net Tutorial
Flash / Flex / ActionScript
VBA / Excel / Access / Word
XML
XML Tutorial
Microsoft Office PowerPoint 2007 Tutorial
Microsoft Office Excel 2007 Tutorial
Microsoft Office Word 2007 Tutorial
Java Source Code / Java Documentation » Scripting » jacl » tcl.lang 
Source Cross Referenced  Class Diagram Java Document (Java Doc) 


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:        }
www.java2java.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.