gt:             if (inputBufferOffset > 0) {
context: /*
   * FileStream.java
   *
   * Copyright (C) 2004-2006 Peter Graves
   * $Id: FileStream.java,v 1.31 2007/02/23 21:17:33 piso Exp $
   *
   * This program is free software; you can redistribute it and/or
   * modify it under the terms of the GNU General Public License
   * as published by the Free Software Foundation; either version 2
   * of the License, or (at your option) any later version.
   *
   * This program is distributed in the hope that it will be useful,
   * but WITHOUT ANY WARRANTY; without even the implied warranty of
   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   * GNU General Public License for more details.
   *
   * You should have received a copy of the GNU General Public License
   * along with this program; if not, write to the Free Software
   * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
   */
  
  package org.armedbear.lisp;
  
  import java.io.File;
  import java.io.FileNotFoundException;
  import java.io.IOException;
  import java.io.RandomAccessFile;
  
  public final class FileStream extends Stream
  {
      private static final int BUFSIZE = 4096;
  
      private final RandomAccessFile raf;
      private final RandomAccessFile in;
      private final RandomAccessFile out;
      private final Pathname pathname;
      private final int bytesPerUnit;
      private final byte[] inputBuffer;
      private final byte[] outputBuffer;
  
      private long inputBufferFilePosition;
      private int inputBufferOffset;
      private int inputBufferCount;
      private int outputBufferOffset;
  
      public FileStream(Pathname pathname, String namestring,
                        LispObject elementType, LispObject direction,
                        LispObject ifExists)
          throws IOException
      {
          final File file = new File(namestring);
          String mode = null;
          if (direction == Keyword.INPUT) {
              mode = "r";
              isInputStream = true;
          } else if (direction == Keyword.OUTPUT) {
              mode = "rw";
              isOutputStream = true;
          } else if (direction == Keyword.IO) {
              mode = "rw";
              isInputStream = true;
              isOutputStream = true;
          }
          Debug.assertTrue(mode != null);
          raf = new RandomAccessFile(file, mode);
          in = isInputStream ? raf : null;
          out = isOutputStream ? raf : null;
          // ifExists is ignored unless we have an output stream.
          if (isOutputStream) {
              final long length = file.isFile() ? file.length() : 0;
              if (length > 0) {
                  if (ifExists == Keyword.OVERWRITE)
                      raf.seek(0);
                  else if (ifExists == Keyword.APPEND)
                      raf.seek(raf.length());
                  else
                      raf.setLength(0);
              }
          }
          this.pathname = pathname;
          this.elementType = elementType;
          if (elementType == Symbol.CHARACTER || elementType == Symbol.BASE_CHAR) {
              isCharacterStream = true;
              bytesPerUnit = 1;
          } else {
              isBinaryStream = true;
              int width;
              try {
                  width = Fixnum.getValue(elementType.cadr());
              }
              catch (ConditionThrowable t) {
                  width = 8;
              }
              bytesPerUnit = width / 8;
          }
          if (isBinaryStream && isInputStream && !isOutputStream && bytesPerUnit == 1)
              inputBuffer = new byte[BUFSIZE];
          else if (isCharacterStream && isInputStream && !isOutputStream)
              inputBuffer = new byte[BUFSIZE];
          else
              inputBuffer = null;
          if (isBinaryStream && isOutputStream && !isInputStream && bytesPerUnit == 1)
              outputBuffer = new byte[BUFSIZE];
          else if (isCharacterStream && isOutputStream && !isInputStream)
              outputBuffer = new byte[BUFSIZE];
          else
              outputBuffer = null;
      }
  
      public LispObject typeOf()
      {
          return Symbol.FILE_STREAM;
      }
  
      public LispObject classOf()
      {
          return BuiltInClass.FILE_STREAM;
      }
  
      public LispObject typep(LispObject typeSpecifier) throws ConditionThrowable
      {
          if (typeSpecifier == Symbol.FILE_STREAM)
              return T;
          if (typeSpecifier == BuiltInClass.FILE_STREAM)
              return T;
          return super.typep(typeSpecifier);
      }
  
      public Pathname getPathname()
      {
          return pathname;
      }
  
      public LispObject listen() throws ConditionThrowable
      {
          try {
              return in.getFilePointer() < in.length() ? T : NIL;
          }
          catch (NullPointerException e) {
              streamNotInputStream();
          }
          catch (IOException e) {
              error(new StreamError(this, e));
          }
          // Not reached.
          return NIL;
      }
  
      public LispObject fileLength() throws ConditionThrowable
      {
          final long length;
          if (isOpen()) {
              try {
                  length = raf.length();
              }
              catch (IOException e) {
                  error(new StreamError(this, e));
                  // Not reached.
                  return NIL;
              }
          } else {
              String namestring = pathname.getNamestring();
              if (namestring == null)
                  return error(new SimpleError("Pathname has no namestring: " +
                                                pathname.writeToString()));
              File file = new File(namestring);
              length = file.length(); // in 8-bit bytes
          }
          if (isCharacterStream)
              return number(length);
          // "For a binary file, the length is measured in units of the
          // element type of the stream."
          return number(length / bytesPerUnit);
      }
  
      public LispObject readLine(boolean eofError, LispObject eofValue)
          throws ConditionThrowable
      {
          if (inputBuffer != null) {
              final LispThread thread = LispThread.currentThread();
              final FastStringBuffer sb = new FastStringBuffer();
              while (true) {
                  int n = _readChar();
                  if (n < 0) {
                      // End of file.
                      if (sb.length() == 0) {
                          if (eofError)
                              return error(new EndOfFile(this));
                          return thread.setValues(eofValue, T);
                      }
                      return thread.setValues(new SimpleString(sb), T);
                  }
                  char c = (char) n;
                  if (c == '\n')
                      return thread.setValues(new SimpleString(sb), NIL);
                  else
                      sb.append(c);
              }
          } else
              return super.readLine(eofError, eofValue);
      }
  
      // Returns -1 at end of file.
      protected int _readChar() throws ConditionThrowable
      {
          try {
              int c = _readByte();
              if (Utilities.isPlatformWindows) {
                  if (c == '\r') {
                      int c2 = _readByte();
                      if (c2 == '\n') {
                          ++lineNumber;
                          return c2;
                      }
                      // '\r' was not followed by '\n'
                      if (inputBuffer != null && inputBufferOffset > 0) {
                          --inputBufferOffset;
                      } else {
                          clearInputBuffer();
                          long pos = in.getFilePointer();
                          if (pos > 0)
                              in.seek(pos - 1);
                      }
                  }
                  return c;
              }
              if (c == '\n') {
                  ++lineNumber;
                  return c;
              }
              return c;
          }
          catch (NullPointerException e) {
              streamNotInputStream();
          }
          catch (IOException e) {
              error(new StreamError(this, e));
          }
          // Not reached.
          return -1;
      }
  
      protected void _unreadChar(int n) throws ConditionThrowable
      {
          if (inputBuffer != null && inputBufferOffset > 0) {
              --inputBufferOffset;
              if (n != '\n')
                  return;
              --lineNumber;
              if (!Utilities.isPlatformWindows)
                  return;
              // Check for preceding '\r'.
  