1 /*
   2  * Copyright (c) 1995, 2012, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 package java.io;
  27 
  28 import java.util.Arrays;
  29 
  30 /**
  31  * The {@code StreamTokenizer} class takes an input stream and
  32  * parses it into "tokens", allowing the tokens to be
  33  * read one at a time. The parsing process is controlled by a table
  34  * and a number of flags that can be set to various states. The
  35  * stream tokenizer can recognize identifiers, numbers, quoted
  36  * strings, and various comment styles.
  37  * <p>
  38  * Each byte read from the input stream is regarded as a character
  39  * in the range {@code '\u005Cu0000'} through {@code '\u005Cu00FF'}.
  40  * The character value is used to look up five possible attributes of
  41  * the character: <i>white space</i>, <i>alphabetic</i>,
  42  * <i>numeric</i>, <i>string quote</i>, and <i>comment character</i>.
  43  * Each character can have zero or more of these attributes.
  44  * <p>
  45  * In addition, an instance has four flags. These flags indicate:
  46  * <ul>
  47  * <li>Whether line terminators are to be returned as tokens or treated
  48  *     as white space that merely separates tokens.
  49  * <li>Whether C-style comments are to be recognized and skipped.
  50  * <li>Whether C++-style comments are to be recognized and skipped.
  51  * <li>Whether the characters of identifiers are converted to lowercase.
  52  * </ul>
  53  * <p>
  54  * A typical application first constructs an instance of this class,
  55  * sets up the syntax tables, and then repeatedly loops calling the
  56  * {@code nextToken} method in each iteration of the loop until
  57  * it returns the value {@code TT_EOF}.
  58  *
  59  * @author  James Gosling
  60  * @see     java.io.StreamTokenizer#nextToken()
  61  * @see     java.io.StreamTokenizer#TT_EOF
  62  * @since   1.0
  63  */
  64 
  65 public class StreamTokenizer {
  66 
  67     /* Only one of these will be non-null */
  68     private Reader reader = null;
  69     private InputStream input = null;
  70 
  71     private char buf[] = new char[20];
  72 
  73     /**
  74      * The next character to be considered by the nextToken method.  May also
  75      * be NEED_CHAR to indicate that a new character should be read, or SKIP_LF
  76      * to indicate that a new character should be read and, if it is a '\n'
  77      * character, it should be discarded and a second new character should be
  78      * read.
  79      */
  80     private int peekc = NEED_CHAR;
  81 
  82     private static final int NEED_CHAR = Integer.MAX_VALUE;
  83     private static final int SKIP_LF = Integer.MAX_VALUE - 1;
  84 
  85     private boolean pushedBack;
  86     private boolean forceLower;
  87     /** The line number of the last token read */
  88     private int LINENO = 1;
  89 
  90     private boolean eolIsSignificantP = false;
  91     private boolean slashSlashCommentsP = false;
  92     private boolean slashStarCommentsP = false;
  93 
  94     private byte ctype[] = new byte[256];
  95     private static final byte CT_WHITESPACE = 1;
  96     private static final byte CT_DIGIT = 2;
  97     private static final byte CT_ALPHA = 4;
  98     private static final byte CT_QUOTE = 8;
  99     private static final byte CT_COMMENT = 16;
 100 
 101     /**
 102      * After a call to the {@code nextToken} method, this field
 103      * contains the type of the token just read. For a single character
 104      * token, its value is the single character, converted to an integer.
 105      * For a quoted string token, its value is the quote character.
 106      * Otherwise, its value is one of the following:
 107      * <ul>
 108      * <li>{@code TT_WORD} indicates that the token is a word.
 109      * <li>{@code TT_NUMBER} indicates that the token is a number.
 110      * <li>{@code TT_EOL} indicates that the end of line has been read.
 111      *     The field can only have this value if the
 112      *     {@code eolIsSignificant} method has been called with the
 113      *     argument {@code true}.
 114      * <li>{@code TT_EOF} indicates that the end of the input stream
 115      *     has been reached.
 116      * </ul>
 117      * <p>
 118      * The initial value of this field is -4.
 119      *
 120      * @see     java.io.StreamTokenizer#eolIsSignificant(boolean)
 121      * @see     java.io.StreamTokenizer#nextToken()
 122      * @see     java.io.StreamTokenizer#quoteChar(int)
 123      * @see     java.io.StreamTokenizer#TT_EOF
 124      * @see     java.io.StreamTokenizer#TT_EOL
 125      * @see     java.io.StreamTokenizer#TT_NUMBER
 126      * @see     java.io.StreamTokenizer#TT_WORD
 127      */
 128     public int ttype = TT_NOTHING;
 129 
 130     /**
 131      * A constant indicating that the end of the stream has been read.
 132      */
 133     public static final int TT_EOF = -1;
 134 
 135     /**
 136      * A constant indicating that the end of the line has been read.
 137      */
 138     public static final int TT_EOL = '\n';
 139 
 140     /**
 141      * A constant indicating that a number token has been read.
 142      */
 143     public static final int TT_NUMBER = -2;
 144 
 145     /**
 146      * A constant indicating that a word token has been read.
 147      */
 148     public static final int TT_WORD = -3;
 149 
 150     /* A constant indicating that no token has been read, used for
 151      * initializing ttype.  FIXME This could be made public and
 152      * made available as the part of the API in a future release.
 153      */
 154     private static final int TT_NOTHING = -4;
 155 
 156     /**
 157      * If the current token is a word token, this field contains a
 158      * string giving the characters of the word token. When the current
 159      * token is a quoted string token, this field contains the body of
 160      * the string.
 161      * <p>
 162      * The current token is a word when the value of the
 163      * {@code ttype} field is {@code TT_WORD}. The current token is
 164      * a quoted string token when the value of the {@code ttype} field is
 165      * a quote character.
 166      * <p>
 167      * The initial value of this field is null.
 168      *
 169      * @see     java.io.StreamTokenizer#quoteChar(int)
 170      * @see     java.io.StreamTokenizer#TT_WORD
 171      * @see     java.io.StreamTokenizer#ttype
 172      */
 173     public String sval;
 174 
 175     /**
 176      * If the current token is a number, this field contains the value
 177      * of that number. The current token is a number when the value of
 178      * the {@code ttype} field is {@code TT_NUMBER}.
 179      * <p>
 180      * The initial value of this field is 0.0.
 181      *
 182      * @see     java.io.StreamTokenizer#TT_NUMBER
 183      * @see     java.io.StreamTokenizer#ttype
 184      */
 185     public double nval;
 186 
 187     /** Private constructor that initializes everything except the streams. */
 188     private StreamTokenizer() {
 189         wordChars('a', 'z');
 190         wordChars('A', 'Z');
 191         wordChars(128 + 32, 255);
 192         whitespaceChars(0, ' ');
 193         commentChar('/');
 194         quoteChar('"');
 195         quoteChar('\'');
 196         parseNumbers();
 197     }
 198 
 199     /**
 200      * Creates a stream tokenizer that parses the specified input
 201      * stream. The stream tokenizer is initialized to the following
 202      * default state:
 203      * <ul>
 204      * <li>All byte values {@code 'A'} through {@code 'Z'},
 205      *     {@code 'a'} through {@code 'z'}, and
 206      *     {@code '\u005Cu00A0'} through {@code '\u005Cu00FF'} are
 207      *     considered to be alphabetic.
 208      * <li>All byte values {@code '\u005Cu0000'} through
 209      *     {@code '\u005Cu0020'} are considered to be white space.
 210      * <li>{@code '/'} is a comment character.
 211      * <li>Single quote {@code '\u005C''} and double quote {@code '"'}
 212      *     are string quote characters.
 213      * <li>Numbers are parsed.
 214      * <li>Ends of lines are treated as white space, not as separate tokens.
 215      * <li>C-style and C++-style comments are not recognized.
 216      * </ul>
 217      *
 218      * @deprecated As of JDK version 1.1, the preferred way to tokenize an
 219      * input stream is to convert it into a character stream, for example:
 220      * <blockquote><pre>
 221      *   Reader r = new BufferedReader(new InputStreamReader(is));
 222      *   StreamTokenizer st = new StreamTokenizer(r);
 223      * </pre></blockquote>
 224      *
 225      * @param      is        an input stream.
 226      * @see        java.io.BufferedReader
 227      * @see        java.io.InputStreamReader
 228      * @see        java.io.StreamTokenizer#StreamTokenizer(java.io.Reader)
 229      */
 230     @Deprecated
 231     public StreamTokenizer(InputStream is) {
 232         this();
 233         if (is == null) {
 234             throw new NullPointerException();
 235         }
 236         input = is;
 237     }
 238 
 239     /**
 240      * Create a tokenizer that parses the given character stream.
 241      *
 242      * @param r  a Reader object providing the input stream.
 243      * @since   1.1
 244      */
 245     public StreamTokenizer(Reader r) {
 246         this();
 247         if (r == null) {
 248             throw new NullPointerException();
 249         }
 250         reader = r;
 251     }
 252 
 253     /**
 254      * Resets this tokenizer's syntax table so that all characters are
 255      * "ordinary." See the {@code ordinaryChar} method
 256      * for more information on a character being ordinary.
 257      *
 258      * @see     java.io.StreamTokenizer#ordinaryChar(int)
 259      */
 260     public void resetSyntax() {
 261         for (int i = ctype.length; --i >= 0;)
 262             ctype[i] = 0;
 263     }
 264 
 265     /**
 266      * Specifies that all characters <i>c</i> in the range
 267      * <code>low&nbsp;&lt;=&nbsp;<i>c</i>&nbsp;&lt;=&nbsp;high</code>
 268      * are word constituents. A word token consists of a word constituent
 269      * followed by zero or more word constituents or number constituents.
 270      *
 271      * @param   low   the low end of the range.
 272      * @param   hi    the high end of the range.
 273      */
 274     public void wordChars(int low, int hi) {
 275         if (low < 0)
 276             low = 0;
 277         if (hi >= ctype.length)
 278             hi = ctype.length - 1;
 279         while (low <= hi)
 280             ctype[low++] |= CT_ALPHA;
 281     }
 282 
 283     /**
 284      * Specifies that all characters <i>c</i> in the range
 285      * <code>low&nbsp;&lt;=&nbsp;<i>c</i>&nbsp;&lt;=&nbsp;high</code>
 286      * are white space characters. White space characters serve only to
 287      * separate tokens in the input stream.
 288      *
 289      * <p>Any other attribute settings for the characters in the specified
 290      * range are cleared.
 291      *
 292      * @param   low   the low end of the range.
 293      * @param   hi    the high end of the range.
 294      */
 295     public void whitespaceChars(int low, int hi) {
 296         if (low < 0)
 297             low = 0;
 298         if (hi >= ctype.length)
 299             hi = ctype.length - 1;
 300         while (low <= hi)
 301             ctype[low++] = CT_WHITESPACE;
 302     }
 303 
 304     /**
 305      * Specifies that all characters <i>c</i> in the range
 306      * <code>low&nbsp;&lt;=&nbsp;<i>c</i>&nbsp;&lt;=&nbsp;high</code>
 307      * are "ordinary" in this tokenizer. See the
 308      * {@code ordinaryChar} method for more information on a
 309      * character being ordinary.
 310      *
 311      * @param   low   the low end of the range.
 312      * @param   hi    the high end of the range.
 313      * @see     java.io.StreamTokenizer#ordinaryChar(int)
 314      */
 315     public void ordinaryChars(int low, int hi) {
 316         if (low < 0)
 317             low = 0;
 318         if (hi >= ctype.length)
 319             hi = ctype.length - 1;
 320         while (low <= hi)
 321             ctype[low++] = 0;
 322     }
 323 
 324     /**
 325      * Specifies that the character argument is "ordinary"
 326      * in this tokenizer. It removes any special significance the
 327      * character has as a comment character, word component, string
 328      * delimiter, white space, or number character. When such a character
 329      * is encountered by the parser, the parser treats it as a
 330      * single-character token and sets {@code ttype} field to the
 331      * character value.
 332      *
 333      * <p>Making a line terminator character "ordinary" may interfere
 334      * with the ability of a {@code StreamTokenizer} to count
 335      * lines. The {@code lineno} method may no longer reflect
 336      * the presence of such terminator characters in its line count.
 337      *
 338      * @param   ch   the character.
 339      * @see     java.io.StreamTokenizer#ttype
 340      */
 341     public void ordinaryChar(int ch) {
 342         if (ch >= 0 && ch < ctype.length)
 343             ctype[ch] = 0;
 344     }
 345 
 346     /**
 347      * Specified that the character argument starts a single-line
 348      * comment. All characters from the comment character to the end of
 349      * the line are ignored by this stream tokenizer.
 350      *
 351      * <p>Any other attribute settings for the specified character are cleared.
 352      *
 353      * @param   ch   the character.
 354      */
 355     public void commentChar(int ch) {
 356         if (ch >= 0 && ch < ctype.length)
 357             ctype[ch] = CT_COMMENT;
 358     }
 359 
 360     /**
 361      * Specifies that matching pairs of this character delimit string
 362      * constants in this tokenizer.
 363      * <p>
 364      * When the {@code nextToken} method encounters a string
 365      * constant, the {@code ttype} field is set to the string
 366      * delimiter and the {@code sval} field is set to the body of
 367      * the string.
 368      * <p>
 369      * If a string quote character is encountered, then a string is
 370      * recognized, consisting of all characters after (but not including)
 371      * the string quote character, up to (but not including) the next
 372      * occurrence of that same string quote character, or a line
 373      * terminator, or end of file. The usual escape sequences such as
 374      * {@code "\u005Cn"} and {@code "\u005Ct"} are recognized and
 375      * converted to single characters as the string is parsed.
 376      *
 377      * <p>Any other attribute settings for the specified character are cleared.
 378      *
 379      * @param   ch   the character.
 380      * @see     java.io.StreamTokenizer#nextToken()
 381      * @see     java.io.StreamTokenizer#sval
 382      * @see     java.io.StreamTokenizer#ttype
 383      */
 384     public void quoteChar(int ch) {
 385         if (ch >= 0 && ch < ctype.length)
 386             ctype[ch] = CT_QUOTE;
 387     }
 388 
 389     /**
 390      * Specifies that numbers should be parsed by this tokenizer. The
 391      * syntax table of this tokenizer is modified so that each of the twelve
 392      * characters:
 393      * <blockquote><pre>
 394      *      0 1 2 3 4 5 6 7 8 9 . -
 395      * </pre></blockquote>
 396      * <p>
 397      * has the "numeric" attribute.
 398      * <p>
 399      * When the parser encounters a word token that has the format of a
 400      * double precision floating-point number, it treats the token as a
 401      * number rather than a word, by setting the {@code ttype}
 402      * field to the value {@code TT_NUMBER} and putting the numeric
 403      * value of the token into the {@code nval} field.
 404      *
 405      * @see     java.io.StreamTokenizer#nval
 406      * @see     java.io.StreamTokenizer#TT_NUMBER
 407      * @see     java.io.StreamTokenizer#ttype
 408      */
 409     public void parseNumbers() {
 410         for (int i = '0'; i <= '9'; i++)
 411             ctype[i] |= CT_DIGIT;
 412         ctype['.'] |= CT_DIGIT;
 413         ctype['-'] |= CT_DIGIT;
 414     }
 415 
 416     /**
 417      * Determines whether or not ends of line are treated as tokens.
 418      * If the flag argument is true, this tokenizer treats end of lines
 419      * as tokens; the {@code nextToken} method returns
 420      * {@code TT_EOL} and also sets the {@code ttype} field to
 421      * this value when an end of line is read.
 422      * <p>
 423      * A line is a sequence of characters ending with either a
 424      * carriage-return character ({@code '\u005Cr'}) or a newline
 425      * character ({@code '\u005Cn'}). In addition, a carriage-return
 426      * character followed immediately by a newline character is treated
 427      * as a single end-of-line token.
 428      * <p>
 429      * If the {@code flag} is false, end-of-line characters are
 430      * treated as white space and serve only to separate tokens.
 431      *
 432      * @param   flag   {@code true} indicates that end-of-line characters
 433      *                 are separate tokens; {@code false} indicates that
 434      *                 end-of-line characters are white space.
 435      * @see     java.io.StreamTokenizer#nextToken()
 436      * @see     java.io.StreamTokenizer#ttype
 437      * @see     java.io.StreamTokenizer#TT_EOL
 438      */
 439     public void eolIsSignificant(boolean flag) {
 440         eolIsSignificantP = flag;
 441     }
 442 
 443     /**
 444      * Determines whether or not the tokenizer recognizes C-style comments.
 445      * If the flag argument is {@code true}, this stream tokenizer
 446      * recognizes C-style comments. All text between successive
 447      * occurrences of {@code /*} and <code>*/</code> are discarded.
 448      * <p>
 449      * If the flag argument is {@code false}, then C-style comments
 450      * are not treated specially.
 451      *
 452      * @param   flag   {@code true} indicates to recognize and ignore
 453      *                 C-style comments.
 454      */
 455     public void slashStarComments(boolean flag) {
 456         slashStarCommentsP = flag;
 457     }
 458 
 459     /**
 460      * Determines whether or not the tokenizer recognizes C++-style comments.
 461      * If the flag argument is {@code true}, this stream tokenizer
 462      * recognizes C++-style comments. Any occurrence of two consecutive
 463      * slash characters ({@code '/'}) is treated as the beginning of
 464      * a comment that extends to the end of the line.
 465      * <p>
 466      * If the flag argument is {@code false}, then C++-style
 467      * comments are not treated specially.
 468      *
 469      * @param   flag   {@code true} indicates to recognize and ignore
 470      *                 C++-style comments.
 471      */
 472     public void slashSlashComments(boolean flag) {
 473         slashSlashCommentsP = flag;
 474     }
 475 
 476     /**
 477      * Determines whether or not word token are automatically lowercased.
 478      * If the flag argument is {@code true}, then the value in the
 479      * {@code sval} field is lowercased whenever a word token is
 480      * returned (the {@code ttype} field has the
 481      * value {@code TT_WORD} by the {@code nextToken} method
 482      * of this tokenizer.
 483      * <p>
 484      * If the flag argument is {@code false}, then the
 485      * {@code sval} field is not modified.
 486      *
 487      * @param   fl   {@code true} indicates that all word tokens should
 488      *               be lowercased.
 489      * @see     java.io.StreamTokenizer#nextToken()
 490      * @see     java.io.StreamTokenizer#ttype
 491      * @see     java.io.StreamTokenizer#TT_WORD
 492      */
 493     public void lowerCaseMode(boolean fl) {
 494         forceLower = fl;
 495     }
 496 
 497     /** Read the next character */
 498     private int read() throws IOException {
 499         if (reader != null)
 500             return reader.read();
 501         else if (input != null)
 502             return input.read();
 503         else
 504             throw new IllegalStateException();
 505     }
 506 
 507     /**
 508      * Parses the next token from the input stream of this tokenizer.
 509      * The type of the next token is returned in the {@code ttype}
 510      * field. Additional information about the token may be in the
 511      * {@code nval} field or the {@code sval} field of this
 512      * tokenizer.
 513      * <p>
 514      * Typical clients of this
 515      * class first set up the syntax tables and then sit in a loop
 516      * calling nextToken to parse successive tokens until TT_EOF
 517      * is returned.
 518      *
 519      * @return     the value of the {@code ttype} field.
 520      * @exception  IOException  if an I/O error occurs.
 521      * @see        java.io.StreamTokenizer#nval
 522      * @see        java.io.StreamTokenizer#sval
 523      * @see        java.io.StreamTokenizer#ttype
 524      */
 525     public int nextToken() throws IOException {
 526         if (pushedBack) {
 527             pushedBack = false;
 528             return ttype;
 529         }
 530         byte ct[] = ctype;
 531         sval = null;
 532 
 533         int c = peekc;
 534         if (c < 0)
 535             c = NEED_CHAR;
 536         if (c == SKIP_LF) {
 537             c = read();
 538             if (c < 0)
 539                 return ttype = TT_EOF;
 540             if (c == '\n')
 541                 c = NEED_CHAR;
 542         }
 543         if (c == NEED_CHAR) {
 544             c = read();
 545             if (c < 0)
 546                 return ttype = TT_EOF;
 547         }
 548         ttype = c;              /* Just to be safe */
 549 
 550         /* Set peekc so that the next invocation of nextToken will read
 551          * another character unless peekc is reset in this invocation
 552          */
 553         peekc = NEED_CHAR;
 554 
 555         int ctype = c < 256 ? ct[c] : CT_ALPHA;
 556         while ((ctype & CT_WHITESPACE) != 0) {
 557             if (c == '\r') {
 558                 LINENO++;
 559                 if (eolIsSignificantP) {
 560                     peekc = SKIP_LF;
 561                     return ttype = TT_EOL;
 562                 }
 563                 c = read();
 564                 if (c == '\n')
 565                     c = read();
 566             } else {
 567                 if (c == '\n') {
 568                     LINENO++;
 569                     if (eolIsSignificantP) {
 570                         return ttype = TT_EOL;
 571                     }
 572                 }
 573                 c = read();
 574             }
 575             if (c < 0)
 576                 return ttype = TT_EOF;
 577             ctype = c < 256 ? ct[c] : CT_ALPHA;
 578         }
 579 
 580         if ((ctype & CT_DIGIT) != 0) {
 581             boolean neg = false;
 582             if (c == '-') {
 583                 c = read();
 584                 if (c != '.' && (c < '0' || c > '9')) {
 585                     peekc = c;
 586                     return ttype = '-';
 587                 }
 588                 neg = true;
 589             }
 590             double v = 0;
 591             int decexp = 0;
 592             int seendot = 0;
 593             while (true) {
 594                 if (c == '.' && seendot == 0)
 595                     seendot = 1;
 596                 else if ('0' <= c && c <= '9') {
 597                     v = v * 10 + (c - '0');
 598                     decexp += seendot;
 599                 } else
 600                     break;
 601                 c = read();
 602             }
 603             peekc = c;
 604             if (decexp != 0) {
 605                 double denom = 10;
 606                 decexp--;
 607                 while (decexp > 0) {
 608                     denom *= 10;
 609                     decexp--;
 610                 }
 611                 /* Do one division of a likely-to-be-more-accurate number */
 612                 v = v / denom;
 613             }
 614             nval = neg ? -v : v;
 615             return ttype = TT_NUMBER;
 616         }
 617 
 618         if ((ctype & CT_ALPHA) != 0) {
 619             int i = 0;
 620             do {
 621                 if (i >= buf.length) {
 622                     buf = Arrays.copyOf(buf, buf.length * 2);
 623                 }
 624                 buf[i++] = (char) c;
 625                 c = read();
 626                 ctype = c < 0 ? CT_WHITESPACE : c < 256 ? ct[c] : CT_ALPHA;
 627             } while ((ctype & (CT_ALPHA | CT_DIGIT)) != 0);
 628             peekc = c;
 629             sval = String.copyValueOf(buf, 0, i);
 630             if (forceLower)
 631                 sval = sval.toLowerCase();
 632             return ttype = TT_WORD;
 633         }
 634 
 635         if ((ctype & CT_QUOTE) != 0) {
 636             ttype = c;
 637             int i = 0;
 638             /* Invariants (because \Octal needs a lookahead):
 639              *   (i)  c contains char value
 640              *   (ii) d contains the lookahead
 641              */
 642             int d = read();
 643             while (d >= 0 && d != ttype && d != '\n' && d != '\r') {
 644                 if (d == '\\') {
 645                     c = read();
 646                     int first = c;   /* To allow \377, but not \477 */
 647                     if (c >= '0' && c <= '7') {
 648                         c = c - '0';
 649                         int c2 = read();
 650                         if ('0' <= c2 && c2 <= '7') {
 651                             c = (c << 3) + (c2 - '0');
 652                             c2 = read();
 653                             if ('0' <= c2 && c2 <= '7' && first <= '3') {
 654                                 c = (c << 3) + (c2 - '0');
 655                                 d = read();
 656                             } else
 657                                 d = c2;
 658                         } else
 659                           d = c2;
 660                     } else {
 661                         switch (c) {
 662                         case 'a':
 663                             c = 0x7;
 664                             break;
 665                         case 'b':
 666                             c = '\b';
 667                             break;
 668                         case 'f':
 669                             c = 0xC;
 670                             break;
 671                         case 'n':
 672                             c = '\n';
 673                             break;
 674                         case 'r':
 675                             c = '\r';
 676                             break;
 677                         case 't':
 678                             c = '\t';
 679                             break;
 680                         case 'v':
 681                             c = 0xB;
 682                             break;
 683                         }
 684                         d = read();
 685                     }
 686                 } else {
 687                     c = d;
 688                     d = read();
 689                 }
 690                 if (i >= buf.length) {
 691                     buf = Arrays.copyOf(buf, buf.length * 2);
 692                 }
 693                 buf[i++] = (char)c;
 694             }
 695 
 696             /* If we broke out of the loop because we found a matching quote
 697              * character then arrange to read a new character next time
 698              * around; otherwise, save the character.
 699              */
 700             peekc = (d == ttype) ? NEED_CHAR : d;
 701 
 702             sval = String.copyValueOf(buf, 0, i);
 703             return ttype;
 704         }
 705 
 706         if (c == '/' && (slashSlashCommentsP || slashStarCommentsP)) {
 707             c = read();
 708             if (c == '*' && slashStarCommentsP) {
 709                 int prevc = 0;
 710                 while ((c = read()) != '/' || prevc != '*') {
 711                     if (c == '\r') {
 712                         LINENO++;
 713                         c = read();
 714                         if (c == '\n') {
 715                             c = read();
 716                         }
 717                     } else {
 718                         if (c == '\n') {
 719                             LINENO++;
 720                             c = read();
 721                         }
 722                     }
 723                     if (c < 0)
 724                         return ttype = TT_EOF;
 725                     prevc = c;
 726                 }
 727                 return nextToken();
 728             } else if (c == '/' && slashSlashCommentsP) {
 729                 while ((c = read()) != '\n' && c != '\r' && c >= 0);
 730                 peekc = c;
 731                 return nextToken();
 732             } else {
 733                 /* Now see if it is still a single line comment */
 734                 if ((ct['/'] & CT_COMMENT) != 0) {
 735                     while ((c = read()) != '\n' && c != '\r' && c >= 0);
 736                     peekc = c;
 737                     return nextToken();
 738                 } else {
 739                     peekc = c;
 740                     return ttype = '/';
 741                 }
 742             }
 743         }
 744 
 745         if ((ctype & CT_COMMENT) != 0) {
 746             while ((c = read()) != '\n' && c != '\r' && c >= 0);
 747             peekc = c;
 748             return nextToken();
 749         }
 750 
 751         return ttype = c;
 752     }
 753 
 754     /**
 755      * Causes the next call to the {@code nextToken} method of this
 756      * tokenizer to return the current value in the {@code ttype}
 757      * field, and not to modify the value in the {@code nval} or
 758      * {@code sval} field.
 759      *
 760      * @see     java.io.StreamTokenizer#nextToken()
 761      * @see     java.io.StreamTokenizer#nval
 762      * @see     java.io.StreamTokenizer#sval
 763      * @see     java.io.StreamTokenizer#ttype
 764      */
 765     public void pushBack() {
 766         if (ttype != TT_NOTHING)   /* No-op if nextToken() not called */
 767             pushedBack = true;
 768     }
 769 
 770     /**
 771      * Return the current line number.
 772      *
 773      * @return  the current line number of this stream tokenizer.
 774      */
 775     public int lineno() {
 776         return LINENO;
 777     }
 778 
 779     /**
 780      * Returns the string representation of the current stream token and
 781      * the line number it occurs on.
 782      *
 783      * <p>The precise string returned is unspecified, although the following
 784      * example can be considered typical:
 785      *
 786      * <blockquote><pre>Token['a'], line 10</pre></blockquote>
 787      *
 788      * @return  a string representation of the token
 789      * @see     java.io.StreamTokenizer#nval
 790      * @see     java.io.StreamTokenizer#sval
 791      * @see     java.io.StreamTokenizer#ttype
 792      */
 793     public String toString() {
 794         String ret;
 795         switch (ttype) {
 796           case TT_EOF:
 797             ret = "EOF";
 798             break;
 799           case TT_EOL:
 800             ret = "EOL";
 801             break;
 802           case TT_WORD:
 803             ret = sval;
 804             break;
 805           case TT_NUMBER:
 806             ret = "n=" + nval;
 807             break;
 808           case TT_NOTHING:
 809             ret = "NOTHING";
 810             break;
 811           default: {
 812                 /*
 813                  * ttype is the first character of either a quoted string or
 814                  * is an ordinary character. ttype can definitely not be less
 815                  * than 0, since those are reserved values used in the previous
 816                  * case statements
 817                  */
 818                 if (ttype < 256 &&
 819                     ((ctype[ttype] & CT_QUOTE) != 0)) {
 820                     ret = sval;
 821                     break;
 822                 }
 823 
 824                 char s[] = new char[3];
 825                 s[0] = s[2] = '\'';
 826                 s[1] = (char) ttype;
 827                 ret = new String(s);
 828                 break;
 829             }
 830         }
 831         return "Token[" + ret + "], line " + LINENO;
 832     }
 833 
 834 }