[armedbear-cvs] r11434 - in trunk/j: . src/org/armedbear/lisp src/org/armedbear/lisp/util

Erik Huelsmann ehuelsmann at common-lisp.net
Sun Dec 7 23:24:34 UTC 2008


Author: ehuelsmann
Date: Sun Dec  7 23:24:31 2008
New Revision: 11434

Log:
Merge open-external-format branch back to trunk.

Added:
   trunk/j/src/org/armedbear/lisp/util/
      - copied from r11432, /branches/open-external-format/src/org/armedbear/lisp/util/
Modified:
   trunk/j/build.xml
   trunk/j/src/org/armedbear/lisp/FileStream.java
   trunk/j/src/org/armedbear/lisp/Stream.java
   trunk/j/src/org/armedbear/lisp/StringInputStream.java
   trunk/j/src/org/armedbear/lisp/StringOutputStream.java
   trunk/j/src/org/armedbear/lisp/open.lisp
   trunk/j/src/org/armedbear/lisp/socket.lisp
   trunk/j/src/org/armedbear/lisp/socket_stream.java

Modified: trunk/j/build.xml
==============================================================================
--- trunk/j/build.xml	(original)
+++ trunk/j/build.xml	Sun Dec  7 23:24:31 2008
@@ -100,6 +100,7 @@
 
     <patternset id="abcl.source.java">
       <include name="org/armedbear/lisp/*.java"/>
+      <include name="org/armedbear/lisp/util/*.java"/>
       <include name="org/armedbear/Main.java"/>
     </patternset>
     
@@ -117,6 +118,7 @@
 
     <patternset id="abcl.objects">
       <include name="org/armedbear/lisp/*.class"/>
+      <include name="org/armedbear/lisp/util/*.class"/>
       <include name="org/armedbear/lisp/*.cls"/> 
       <include name="org/armedbear/lisp/*.abcl"/>
       <patternset refid="abcl.source.lisp.dist"/>

Modified: trunk/j/src/org/armedbear/lisp/FileStream.java
==============================================================================
--- trunk/j/src/org/armedbear/lisp/FileStream.java	(original)
+++ trunk/j/src/org/armedbear/lisp/FileStream.java	Sun Dec  7 23:24:31 2008
@@ -2,6 +2,7 @@
  * FileStream.java
  *
  * Copyright (C) 2004-2006 Peter Graves
+ * Copyright (C) 2008 Hideo at Yokohama
  * $Id$
  *
  * This program is free software; you can redistribute it and/or
@@ -34,32 +35,39 @@
 package org.armedbear.lisp;
 
 import java.io.File;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.Reader;
+import java.io.Writer;
 import java.io.FileNotFoundException;
 import java.io.IOException;
 import java.io.RandomAccessFile;
+import org.armedbear.lisp.util.RandomAccessCharacterFile;
 
 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 RandomAccessCharacterFile racf;
     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)
+                      LispObject ifExists, LispObject format)
         throws IOException
     {
+        /* externalFormat is a LispObject of which the first char is a
+         * name of a character encoding (such as :UTF-8 or :ISO-8859-1), used
+         * by ABCL as a string designator, unless the name is :CODE-PAGE.
+         * A real string is (thus) also allowed.
+         * 
+         * Then, a property list follows with 3 possible keys:
+         *   :ID (values: code page numbers supported by MS-DOS/IBM-DOS/MS-Windows
+         *   :EOL-STYLE (values: :CR / :LF / :CRLF [none means native])
+         *   :LITTLE-ENDIAN (values: NIL / T)
+         * 
+         * These definitions have been taken from FLEXI-STREAMS:
+         *    http://www.weitz.de/flexi-streams/#make-external-format
+         */
         final File file = new File(namestring);
         String mode = null;
         if (direction == Keyword.INPUT) {
@@ -73,10 +81,10 @@
             isInputStream = true;
             isOutputStream = true;
         }
+        
         Debug.assertTrue(mode != null);
-        raf = new RandomAccessFile(file, mode);
-        in = isInputStream ? raf : null;
-        out = isOutputStream ? raf : null;
+        RandomAccessFile raf = new RandomAccessFile(file, mode);
+	
         // ifExists is ignored unless we have an output stream.
         if (isOutputStream) {
             final long length = file.isFile() ? file.length() : 0;
@@ -89,11 +97,23 @@
                     raf.setLength(0);
             }
         }
+        setExternalFormat(format);
+        
+	// don't touch raf directly after passing it to racf.
+	// the state will become inconsistent if you do that.
+        racf = new RandomAccessCharacterFile(raf, encoding);
+
         this.pathname = pathname;
         this.elementType = elementType;
         if (elementType == Symbol.CHARACTER || elementType == Symbol.BASE_CHAR) {
             isCharacterStream = true;
             bytesPerUnit = 1;
+	    if (isInputStream) {
+		initAsCharacterInputStream(racf.getReader());
+	    }
+	    if (isOutputStream) {
+		initAsCharacterOutputStream(racf.getWriter());
+	    }
         } else {
             isBinaryStream = true;
             int width;
@@ -104,19 +124,13 @@
                 width = 8;
             }
             bytesPerUnit = width / 8;
+	    if (isInputStream) {
+		initAsBinaryInputStream(racf.getInputStream());
+	    }
+	    if (isOutputStream) {
+		initAsBinaryOutputStream(racf.getOutputStream());
+	    }
         }
-        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;
     }
 
     @Override
@@ -147,28 +161,12 @@
     }
 
     @Override
-    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;
-    }
-
-    @Override
     public LispObject fileLength() throws ConditionThrowable
     {
         final long length;
         if (isOpen()) {
             try {
-                length = raf.length();
+                length = racf.length();
             }
             catch (IOException e) {
                 error(new StreamError(this, e));
@@ -191,116 +189,10 @@
     }
 
     @Override
-    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.
-    @Override
-    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;
-    }
-
-    @Override
     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'.
-            if (inputBufferOffset > 0) {
-                if (inputBuffer[--inputBufferOffset] != '\r')
-                    ++inputBufferOffset;
-                return;
-            }
-            // We can't go back far enough in the buffered input. Reset and
-            // fall through...
-            ++inputBufferOffset;
-        }
         try {
-            long pos;
-            if (inputBuffer != null && inputBufferFilePosition >= 0)
-                pos = inputBufferFilePosition + inputBufferOffset;
-            else
-                pos = in.getFilePointer();
-            clearInputBuffer();
-            if (pos > 0)
-                in.seek(pos - 1);
-            if (Utilities.isPlatformWindows && n == '\n') {
-                // Check for preceding '\r'.
-                pos = in.getFilePointer();
-                if (pos > 0) {
-                    in.seek(pos - 1);
-                    n = in.read();
-                    if (n == '\r')
-                        in.seek(pos - 1);
-                }
-            }
-        }
-        catch (NullPointerException e) {
-            streamNotInputStream();
+            racf.unreadChar((char)n);
         }
         catch (IOException e) {
             error(new StreamError(this, e));
@@ -314,141 +206,14 @@
     }
 
     @Override
-    public void _writeChar(char c) throws ConditionThrowable
-    {
-        if (c == '\n') {
-            if (Utilities.isPlatformWindows)
-                _writeByte((byte)'\r');
-            _writeByte((byte)c);
-            charPos = 0;
-        } else {
-            _writeByte((byte)c);
-            ++charPos;
-        }
-    }
-
-    @Override
-    public void _writeChars(char[] chars, int start, int end)
-        throws ConditionThrowable
-    {
-        if (Utilities.isPlatformWindows) {
-            for (int i = start; i < end; i++) {
-                char c = chars[i];
-                if (c == '\n') {
-                    _writeByte((byte)'\r');
-                    _writeByte((byte)c);
-                    charPos = 0;
-                } else {
-                    _writeByte((byte)c);
-                    ++charPos;
-                }
-            }
-        } else {
-            // We're not on Windows, so no newline conversion is necessary.
-            for (int i = start; i < end; i++) {
-                char c = chars[i];
-                _writeByte((byte)c);
-                if (c == '\n')
-                    charPos = 0;
-                else
-                    ++charPos;
-            }
-        }
-    }
-
-    @Override
-    public void _writeString(String s) throws ConditionThrowable
-    {
-        final int length = s.length();
-        if (Utilities.isPlatformWindows) {
-            for (int i = 0; i < length; i++) {
-                char c = s.charAt(i);
-                if (c == '\n') {
-                    _writeByte((byte)'\r');
-                    _writeByte((byte)c);
-                    charPos = 0;
-                } else {
-                    _writeByte((byte)c);
-                    ++charPos;
-                }
-            }
-        } else {
-            // We're not on Windows, so no newline conversion is necessary.
-            for (int i = 0; i < length; i++) {
-                char c = s.charAt(i);
-                _writeByte((byte)c);
-                if (c == '\n')
-                    charPos = 0;
-                else
-                    ++charPos;
-            }
-        }
-    }
-
-    @Override
-    public void _writeLine(String s) throws ConditionThrowable
-    {
-        _writeString(s);
-        if (Utilities.isPlatformWindows)
-            _writeByte((byte)'\r');
-        _writeByte((byte)'\n');
-        charPos = 0;
-    }
-
-    // Reads an 8-bit byte.
-    @Override
-    public int _readByte() throws ConditionThrowable
-    {
-        if (inputBuffer != null)
-            return readByteFromBuffer();
-        try {
-            return in.read(); // Reads an 8-bit byte.
-        }
-        catch (NullPointerException e) {
-            streamNotInputStream();
-        }
-        catch (IOException e) {
-            error(new StreamError(this, e));
-        }
-        // Not reached.
-        return -1;
-    }
-
-    // Writes an 8-bit byte.
-    @Override
-    public void _writeByte(int n) throws ConditionThrowable
-    {
-        if (outputBuffer != null) {
-            writeByteToBuffer((byte)n);
-        } else {
-            try {
-                out.write((byte)n); // Writes an 8-bit byte.
-            }
-            catch (NullPointerException e) {
-                streamNotOutputStream();
-            }
-            catch (IOException e) {
-                error(new StreamError(this, e));
-            }
-        }
-    }
-
-    @Override
-    public void _finishOutput() throws ConditionThrowable
-    {
-        if (outputBuffer != null)
-            flushOutputBuffer();
-    }
-
-    @Override
     public void _clearInput() throws ConditionThrowable
     {
         try {
-            in.seek(in.length());
-            clearInputBuffer();
-        }
-        catch (NullPointerException e) {
-            streamNotInputStream();
+	    if (isInputStream) {
+		racf.position(racf.length());
+	    } else {
+		streamNotInputStream();
+	    }
         }
         catch (IOException e) {
             error(new StreamError(this, e));
@@ -458,14 +223,8 @@
     @Override
     protected long _getFilePosition() throws ConditionThrowable
     {
-        if (inputBuffer != null) {
-            if (inputBufferFilePosition >= 0)
-                return inputBufferFilePosition + inputBufferOffset;
-        }
-        if (outputBuffer != null)
-            flushOutputBuffer();
         try {
-            long pos = raf.getFilePointer();
+            long pos = racf.position();
             return pos / bytesPerUnit;
         }
         catch (IOException e) {
@@ -478,21 +237,17 @@
     @Override
     protected boolean _setFilePosition(LispObject arg) throws ConditionThrowable
     {
-        if (outputBuffer != null)
-            flushOutputBuffer();
-        if (inputBuffer != null)
-            clearInputBuffer();
         try {
             long pos;
             if (arg == Keyword.START)
                 pos = 0;
             else if (arg == Keyword.END)
-                pos = raf.length();
+                pos = racf.length();
             else {
                 long n = Fixnum.getValue(arg); // FIXME arg might be a bignum
                 pos = n * bytesPerUnit;
             }
-            raf.seek(pos);
+            racf.position(pos);
         }
         catch (IOException e) {
             error(new StreamError(this, e));
@@ -503,10 +258,8 @@
     @Override
     public void _close() throws ConditionThrowable
     {
-        if (outputBuffer != null)
-            flushOutputBuffer();
         try {
-            raf.close();
+            racf.close();
             setOpen(false);
         }
         catch (IOException e) {
@@ -514,76 +267,21 @@
         }
     }
 
-    private int readByteFromBuffer() throws ConditionThrowable
-    {
-        if (inputBufferOffset >= inputBufferCount) {
-            fillInputBuffer();
-            if (inputBufferCount < 0)
-                return -1;
-        }
-        return inputBuffer[inputBufferOffset++] & 0xff;
-    }
-
-    private void fillInputBuffer() throws ConditionThrowable
-    {
-        try {
-            inputBufferFilePosition = in.getFilePointer();
-            inputBufferOffset = 0;
-            inputBufferCount = in.read(inputBuffer, 0, BUFSIZE);
-        }
-        catch (NullPointerException e) {
-            streamNotInputStream();
-        }
-        catch (IOException e) {
-            error(new StreamError(this, e));
-        }
-    }
-
-    private void clearInputBuffer()
-    {
-        inputBufferFilePosition = -1;
-        inputBufferOffset = 0;
-        inputBufferCount = 0;
-    }
-
-    private void writeByteToBuffer(byte b) throws ConditionThrowable
-    {
-        if (outputBufferOffset == BUFSIZE)
-            flushOutputBuffer();
-        outputBuffer[outputBufferOffset++] = b;
-    }
-
-    private void flushOutputBuffer() throws ConditionThrowable
-    {
-        if (outputBufferOffset > 0) {
-            try {
-                out.write(outputBuffer, 0, outputBufferOffset);
-                outputBufferOffset = 0;
-            }
-            catch (NullPointerException e) {
-                streamNotOutputStream();
-            }
-            catch (IOException e) {
-                error(new StreamError(this, e));
-            }
-        }
-    }
-
     @Override
     public String writeToString() throws ConditionThrowable
     {
         return unreadableString(Symbol.FILE_STREAM);
     }
 
-    // ### make-file-stream pathname namestring element-type direction if-exists => stream
+    // ### make-file-stream pathname namestring element-type direction if-exists external-format => stream
     private static final Primitive MAKE_FILE_STREAM =
         new Primitive("make-file-stream", PACKAGE_SYS, true,
-                      "pathname namestring element-type direction if-exists")
+                      "pathname namestring element-type direction if-exists external-format")
     {
         @Override
         public LispObject execute(LispObject first, LispObject second,
                                   LispObject third, LispObject fourth,
-                                  LispObject fifth)
+                                  LispObject fifth, LispObject sixth)
             throws ConditionThrowable
         {
             final Pathname pathname;
@@ -603,12 +301,15 @@
             LispObject elementType = third;
             LispObject direction = fourth;
             LispObject ifExists = fifth;
+            LispObject externalFormat = sixth;
+            
             if (direction != Keyword.INPUT && direction != Keyword.OUTPUT &&
                 direction != Keyword.IO)
                 error(new LispError("Direction must be :INPUT, :OUTPUT, or :IO."));
             try {
                 return new FileStream(pathname, namestring.getStringValue(),
-                                      elementType, direction, ifExists);
+                                      elementType, direction, ifExists,
+                                      externalFormat);
             }
             catch (FileNotFoundException e) {
                 return NIL;

Modified: trunk/j/src/org/armedbear/lisp/Stream.java
==============================================================================
--- trunk/j/src/org/armedbear/lisp/Stream.java	(original)
+++ trunk/j/src/org/armedbear/lisp/Stream.java	Sun Dec  7 23:24:31 2008
@@ -43,6 +43,7 @@
 import java.io.OutputStreamWriter;
 import java.io.PrintWriter;
 import java.io.PushbackReader;
+import java.io.Reader;
 import java.io.StringWriter;
 import java.io.Writer;
 import java.math.BigInteger;
@@ -62,11 +63,12 @@
   protected boolean isCharacterStream;
   protected boolean isBinaryStream;
 
+  private boolean pastEnd = false;
   private boolean interactive;
   private boolean open = true;
-
+  
   // Character input.
-  private PushbackReader reader;
+  protected PushbackReader reader;
   protected int offset;
   protected int lineNumber;
 
@@ -79,29 +81,63 @@
    * required when calling FRESH-LINE
    */
   protected int charPos;
-
+  
+  public enum EolStyle {
+    RAW,
+    CR,
+    CRLF,
+    LF
+  }
+
+  static final protected Symbol keywordDefault = Packages.internKeyword("DEFAULT");
+  
+  static final private Symbol keywordCodePage = Packages.internKeyword("CODE-PAGE");
+  static final private Symbol keywordID = Packages.internKeyword("ID");
+
+  static final private Symbol keywordEolStyle = Packages.internKeyword("EOL-STYLE");
+  static final private Symbol keywordCR = Packages.internKeyword("CR");
+  static final private Symbol keywordLF = Packages.internKeyword("LF");
+  static final private Symbol keywordCRLF = Packages.internKeyword("CRLF");
+  static final private Symbol keywordRAW = Packages.internKeyword("RAW");
+    
+  public final static EolStyle platformEolStyle = Utilities.isPlatformWindows ? EolStyle.CRLF : EolStyle.LF;
+    
+  protected EolStyle eolStyle = platformEolStyle;
+  protected char eolChar = (eolStyle == EolStyle.CR) ? '\r' : '\n';
+  protected LispObject externalFormat = LispObject.NIL;
+  protected String encoding = null;
+  protected char lastChar = 0;
+  
   // Binary input.
-  private BufferedInputStream in;
+  private InputStream in;
 
   // Binary output.
-  private BufferedOutputStream out;
+  private OutputStream out;
 
   protected Stream()
   {
   }
 
-  // Input stream constructors.
   public Stream(InputStream inputStream, LispObject elementType)
+    {
+      this(inputStream, elementType, keywordDefault);
+    }
+
+
+  // Input stream constructors.
+    public Stream(InputStream inputStream, LispObject elementType, LispObject format)
   {
     this.elementType = elementType;
+    setExternalFormat(format);
     if (elementType == Symbol.CHARACTER || elementType == Symbol.BASE_CHAR)
       {
-        isCharacterStream = true;
         InputStreamReader inputStreamReader;
         try
           {
             inputStreamReader =
-              new InputStreamReader(inputStream, "ISO-8859-1");
+                    (encoding == null) ?
+                        new InputStreamReader(inputStream)
+                        : new InputStreamReader(inputStream, encoding);
           }
         catch (java.io.UnsupportedEncodingException e)
           {
@@ -109,16 +145,14 @@
             inputStreamReader =
               new InputStreamReader(inputStream);
           }
-        reader = new PushbackReader(new BufferedReader(inputStreamReader),
-                                    2);
+        initAsCharacterInputStream(new BufferedReader(inputStreamReader));
       }
     else
       {
         isBinaryStream = true;
-        in = new BufferedInputStream(inputStream);
+        InputStream stream = new BufferedInputStream(inputStream);
+	initAsBinaryInputStream(stream);
       }
-    isInputStream = true;
-    isOutputStream = false;
   }
 
   public Stream(InputStream inputStream, LispObject elementType, boolean interactive)
@@ -127,30 +161,37 @@
     setInteractive(interactive);
   }
 
-  // Output stream constructors.
   public Stream(OutputStream outputStream, LispObject elementType)
+    {
+      this(outputStream, elementType, keywordDefault);
+    }
+    
+  // Output stream constructors.
+  public Stream(OutputStream outputStream, LispObject elementType, LispObject format)
   {
     this.elementType = elementType;
+    setExternalFormat(format);
     if (elementType == Symbol.CHARACTER || elementType == Symbol.BASE_CHAR)
       {
-        isCharacterStream = true;
+	Writer w;
         try
           {
-            writer = new OutputStreamWriter(outputStream, "ISO-8859-1");
+            w = (encoding == null) ?
+                new OutputStreamWriter(outputStream)
+                : new OutputStreamWriter(outputStream, encoding);
           }
         catch (java.io.UnsupportedEncodingException e)
           {
             Debug.trace(e);
-            writer = new OutputStreamWriter(outputStream);
+            w = new OutputStreamWriter(outputStream);
           }
+	initAsCharacterOutputStream(w);
       }
     else
       {
-        isBinaryStream = true;
-        out = new BufferedOutputStream(outputStream);
+        OutputStream stream = new BufferedOutputStream(outputStream);
+	initAsBinaryOutputStream(stream);
       }
-    isInputStream = false;
-    isOutputStream = true;
   }
 
   public Stream(OutputStream outputStream, LispObject elementType,
@@ -160,6 +201,35 @@
     setInteractive(interactive);
   }
 
+  protected void initAsCharacterInputStream(Reader reader)
+  {
+    if (! (reader instanceof PushbackReader))
+        this.reader = new PushbackReader(reader, 5);
+    else
+        this.reader = (PushbackReader)reader;
+    
+    isInputStream = true;
+    isCharacterStream = true;
+  }
+
+  protected void initAsBinaryInputStream(InputStream in) {
+    this.in = in;
+    isInputStream = true;
+    isBinaryStream = true;
+  }
+
+  protected void initAsCharacterOutputStream(Writer writer) {
+    this.writer = writer;
+    isOutputStream = true;
+    isCharacterStream = true;
+  }
+
+  protected void initAsBinaryOutputStream(OutputStream out) {
+    this.out = out;
+    isOutputStream = true;
+    isBinaryStream = true;
+  }
+
   public boolean isInputStream() throws ConditionThrowable
   {
     return isInputStream;
@@ -200,6 +270,76 @@
     interactive = b;
   }
 
+  public LispObject getExternalFormat() {
+      return externalFormat;
+  }
+  
+  public String getEncoding() {
+      return encoding;
+  }
+  
+  public void setExternalFormat(LispObject format) {
+    if (format == keywordDefault) {
+      encoding = null;
+      eolStyle = platformEolStyle;
+      eolChar = (eolStyle == EolStyle.CR) ? '\r' : '\n';
+      externalFormat = format;
+      return;
+    }
+      
+    try {
+      LispObject enc;
+      boolean encIsCp = false;
+      
+      if (format instanceof Cons) {
+          // meaning a non-empty list
+          enc = format.car();
+
+          if (enc == keywordCodePage) {
+              encIsCp = true;
+
+              enc = LispObject.getf(format.cdr(), keywordID, null);
+          }
+          
+          LispObject eol = LispObject.getf(format.cdr(), keywordEolStyle, keywordRAW);
+          if (eol == keywordCR)
+              eolStyle = EolStyle.CR;
+          else if (eol == keywordLF)
+              eolStyle = EolStyle.LF;
+          else if (eol == keywordCRLF)
+              eolStyle = EolStyle.CRLF;
+          else if (eol != keywordRAW)
+              //###FIXME: raise an error
+              ;
+          
+      } else
+        enc = format;
+      
+      if (enc.numberp())
+          encoding = enc.toString();
+      else if (enc instanceof AbstractString)
+          encoding = enc.getStringValue();
+      else if (enc == keywordDefault)
+          // This allows the user to use the encoding determined by
+          // Java to be the default for the current environment
+          // while still being able to set other stream options
+          // (e.g. :EOL-STYLE)
+          encoding = null;
+      else if (enc instanceof Symbol)
+          encoding = ((Symbol)enc).getName();
+      else
+          //###FIXME: raise an error!
+          ;
+      
+      if (encIsCp)
+          encoding = "Cp" + encoding;
+    }
+    catch (ConditionThrowable ct) { }
+    
+    eolChar = (eolStyle == EolStyle.CR) ? '\r' : '\n';
+    externalFormat = format;
+  }
+  
   public boolean isOpen()
   {
     return open;
@@ -1603,7 +1743,19 @@
 
   public LispObject listen() throws ConditionThrowable
   {
-    return _charReady() ? T : NIL;
+    if (pastEnd)
+      return NIL;
+    
+    if (! _charReady())
+      return NIL;
+    
+    int n = _readChar();
+    if (n < 0)
+      return NIL;
+
+    _unreadChar(n);
+    
+    return T;
   }
 
   public LispObject fileLength() throws ConditionThrowable
@@ -1651,17 +1803,32 @@
    */
   protected int _readChar() throws ConditionThrowable
   {
+    if (pastEnd)
+      return -1;
+    
     try
       {
         int n = reader.read();
+        
+        if (n < 0) {
+            pastEnd = true;
+            return -1;
+        }
+        
         ++offset;
-        if (n == '\r')
-          {
-            if (interactive && Utilities.isPlatformWindows)
-              return _readChar();
-          }
-        if (n == '\n')
+        if (eolStyle == EolStyle.CRLF && n == '\r') {
+            n = _readChar();
+            if (n != '\n') {
+                _unreadChar(n);
+                return '\r';
+            }
+        }
+
+        if (n == eolChar) {
           ++lineNumber;
+          return '\n';
+        }
+
         return n;
       }
     catch (NullPointerException e)
@@ -1688,7 +1855,8 @@
       {
         reader.unread(n);
         --offset;
-        if (n == '\n')
+        pastEnd = false;
+        if (n == eolChar)
           --lineNumber;
       }
     catch (NullPointerException e)
@@ -1736,14 +1904,19 @@
   {
     try
       {
-        writer.write(c);
-        if (c == '\n')
-          {
-            writer.flush();
-            charPos = 0;
-          }
-        else
+        if (c == '\n') {
+	  if (eolStyle == EolStyle.CRLF && lastChar != '\r')
+              writer.write('\r');
+
+          writer.write(eolChar);
+          lastChar = eolChar;
+          writer.flush();
+          charPos = 0;
+        } else {
+          writer.write(c);
+          lastChar = c;
           ++charPos;
+        }
       }
     catch (NullPointerException e)
       {
@@ -1769,7 +1942,18 @@
   {
     try
       {
+        if (eolStyle != EolStyle.RAW) {
+          for (int i = start; i < end; i++)
+            //###FIXME: the number of writes can be greatly reduced by
+            // writing the space between newlines as chunks.
+            _writeChar(chars[i]);
+          return;
+        }
+        
         writer.write(chars, start, end - start);
+        if (start < end)
+          lastChar = chars[end-1];
+        
         int index = -1;
         for (int i = end; i-- > start;)
           {
@@ -1777,19 +1961,19 @@
               {
                 index = i;
                 break;
-              }
-          }
+	  }
+	}
         if (index < 0)
           {
             // No newline.
             charPos += (end - start);
-          }
+	      }
         else
           {
             charPos = end - (index + 1);
-            writer.flush();
-          }
-      }
+              writer.flush();
+	    }
+	  }
     catch (NullPointerException e)
       {
         if (writer == null)
@@ -1813,15 +1997,7 @@
   {
     try
       {
-        writer.write(s);
-        int index = s.lastIndexOf('\n');
-        if (index < 0)
-          charPos += s.length();
-        else
-          {
-            charPos = s.length() - (index + 1);
-            writer.flush();
-          }
+	_writeChars(s.toCharArray(), 0, s.length());
       }
     catch (NullPointerException e)
       {
@@ -1830,10 +2006,6 @@
         else
           throw e;
       }
-    catch (IOException e)
-      {
-        error(new StreamError(this, e));
-      }
   }
 
   /** Writes a string to the underlying stream, appending
@@ -1846,20 +2018,14 @@
   {
     try
       {
-        writer.write(s);
-        writer.write('\n');
-        writer.flush();
-        charPos = 0;
+        _writeString(s);
+        _writeChar('\n');
       }
     catch (NullPointerException e)
       {
         // writer is null
         streamNotCharacterOutputStream();
       }
-    catch (IOException e)
-      {
-        error(new StreamError(this, e));
-      }
   }
 
   // Reads an 8-bit byte.
@@ -1872,7 +2038,11 @@
   {
     try
       {
-        return in.read(); // Reads an 8-bit byte.
+        int n = in.read();
+        if (n < 0)
+          pastEnd = true;
+        
+        return n; // Reads an 8-bit byte.
       }
     catch (IOException e)
       {
@@ -1933,15 +2103,20 @@
   {
     if (reader != null)
       {
-        while (_charReady())
-          _readChar();
+        int c = 0;
+        while (_charReady() && (c >= 0))
+          c = _readChar();
       }
     else if (in != null)
       {
         try
           {
+            int n = 0;
             while (in.available() > 0)
-              in.read();
+              n = in.read();
+            
+            if (n < 0)
+              pastEnd = true;
           }
         catch (IOException e)
           {
@@ -2006,6 +2181,7 @@
       {
         writer.write(sw.toString());
         writer.write('\n');
+        lastChar = '\n';
         writer.flush();
         charPos = 0;
       }

Modified: trunk/j/src/org/armedbear/lisp/StringInputStream.java
==============================================================================
--- trunk/j/src/org/armedbear/lisp/StringInputStream.java	(original)
+++ trunk/j/src/org/armedbear/lisp/StringInputStream.java	Sun Dec  7 23:24:31 2008
@@ -33,12 +33,13 @@
 
 package org.armedbear.lisp;
 
+import java.io.StringReader;
+
 public final class StringInputStream extends Stream
 {
-    final String s;
-    final int start;
-    final int end;
-
+    private final StringReader stringReader;
+    private final int start;
+    
     public StringInputStream(String s)
     {
         this(s, 0, s.length());
@@ -52,26 +53,28 @@
     public StringInputStream(String s, int start, int end)
     {
         elementType = Symbol.CHARACTER;
-        isInputStream = true;
-        isOutputStream = false;
-        isCharacterStream = true;
-        isBinaryStream = false;
-        this.s = s;
+        setExternalFormat(keywordDefault);
+        eolStyle = EolStyle.RAW;
+
         this.start = start;
-        this.end = end;
-        offset = start;
+        
+        stringReader = new StringReader(s.substring(start, end));
+        initAsCharacterInputStream(stringReader);
     }
 
+    @Override
     public LispObject typeOf()
     {
         return Symbol.STRING_INPUT_STREAM;
     }
 
+    @Override
     public LispObject classOf()
     {
         return BuiltInClass.STRING_INPUT_STREAM;
     }
 
+    @Override
     public LispObject typep(LispObject type) throws ConditionThrowable
     {
         if (type == Symbol.STRING_INPUT_STREAM)
@@ -85,57 +88,29 @@
         return super.typep(type);
     }
 
-    public LispObject close(LispObject abort) throws ConditionThrowable
-    {
-        setOpen(false);
-        return T;
-    }
-
-    public LispObject listen()
-    {
-        return offset < end ? T : NIL;
-    }
-
-    protected int _readChar()
-    {
-        if (offset >= end)
-            return -1;
-        int n = s.charAt(offset);
-        ++offset;
-        if (n == '\n')
-            ++lineNumber;
-        return n;
-    }
-
-    protected void _unreadChar(int n)
-    {
-        if (offset > start) {
-            --offset;
-            if (n == '\n')
-                --lineNumber;
-        }
-    }
-
-    protected boolean _charReady()
-    {
-        return true;
-    }
-
+    @Override
     public String toString()
     {
         return unreadableString("STRING-INPUT-STREAM");
     }
 
+    @Override
+    public int getOffset() {
+        return start + super.getOffset();
+    }
+    
     // ### make-string-input-stream
     // make-string-input-stream string &optional start end => string-stream
     private static final Primitive MAKE_STRING_INPUT_STREAM =
         new Primitive("make-string-input-stream", "string &optional start end")
     {
+        @Override
         public LispObject execute(LispObject arg) throws ConditionThrowable
         {
             return new StringInputStream(arg.getStringValue());
         }
 
+        @Override
         public LispObject execute(LispObject first, LispObject second)
             throws ConditionThrowable
         {
@@ -144,6 +119,7 @@
             return new StringInputStream(s, start);
         }
 
+        @Override
         public LispObject execute(LispObject first, LispObject second,
                                   LispObject third)
             throws ConditionThrowable
@@ -161,6 +137,7 @@
     private static final Primitive STRING_INPUT_STREAM_CURRENT =
         new Primitive("string-input-stream-current", PACKAGE_EXT, true, "stream")
     {
+        @Override
         public LispObject execute(LispObject arg) throws ConditionThrowable
         {
             if (arg instanceof StringInputStream)

Modified: trunk/j/src/org/armedbear/lisp/StringOutputStream.java
==============================================================================
--- trunk/j/src/org/armedbear/lisp/StringOutputStream.java	(original)
+++ trunk/j/src/org/armedbear/lisp/StringOutputStream.java	Sun Dec  7 23:24:31 2008
@@ -47,23 +47,23 @@
     private StringOutputStream(LispObject elementType)
     {
         this.elementType = elementType;
-        isInputStream = false;
-        isOutputStream = true;
-        isCharacterStream = true;
-        isBinaryStream = false;
-        setWriter(stringWriter = new StringWriter());
+        this.eolStyle = EolStyle.RAW;
+        initAsCharacterOutputStream(stringWriter = new StringWriter());
     }
 
+    @Override
     public LispObject typeOf()
     {
         return Symbol.STRING_OUTPUT_STREAM;
     }
 
+    @Override
     public LispObject classOf()
     {
         return BuiltInClass.STRING_OUTPUT_STREAM;
     }
 
+    @Override
     public LispObject typep(LispObject type) throws ConditionThrowable
     {
         if (type == Symbol.STRING_OUTPUT_STREAM)
@@ -77,45 +77,12 @@
         return super.typep(type);
     }
 
-    public void _writeChar(char c) throws ConditionThrowable
-    {
-        if (elementType == NIL)
-            writeError();
-        super._writeChar(c);
-    }
-
-    public void _writeChars(char[] chars, int start, int end)
-        throws ConditionThrowable
-    {
-        if (elementType == NIL)
-            writeError();
-        super._writeChars(chars, start, end);
-    }
-
-    public void _writeString(String s) throws ConditionThrowable
-    {
-        if (elementType == NIL)
-            writeError();
-        super._writeString(s);
-    }
-
-    public void _writeLine(String s) throws ConditionThrowable
-    {
-        if (elementType == NIL)
-            writeError();
-        super._writeLine(s);
-    }
-
-    private void writeError() throws ConditionThrowable
-    {
-        error(new TypeError("Attempt to write to a string output stream of element type NIL."));
-    }
-
+    @Override
     protected long _getFilePosition() throws ConditionThrowable
     {
         if (elementType == NIL)
             return 0;
-        return stringWriter.toString().length();
+        return stringWriter.getBuffer().length();
     }
 
     public LispObject getString() throws ConditionThrowable
@@ -128,6 +95,7 @@
         return s;
     }
 
+    @Override
     public String toString()
     {
         return unreadableString("STRING-OUTPUT-STREAM");
@@ -139,6 +107,7 @@
         new Primitive("%make-string-output-stream", PACKAGE_SYS, false,
                        "element-type")
     {
+        @Override
         public LispObject execute(LispObject arg) throws ConditionThrowable
         {
             return new StringOutputStream(arg);
@@ -150,6 +119,7 @@
     private static final Primitive GET_OUTPUT_STREAM_STRING =
         new Primitive("get-output-stream-string", "string-output-stream")
     {
+        @Override
         public LispObject execute(LispObject arg) throws ConditionThrowable
         {
             try {

Modified: trunk/j/src/org/armedbear/lisp/open.lisp
==============================================================================
--- trunk/j/src/org/armedbear/lisp/open.lisp	(original)
+++ trunk/j/src/org/armedbear/lisp/open.lisp	Sun Dec  7 23:24:31 2008
@@ -106,7 +106,7 @@
 	     (if-exists nil if-exists-given)
 	     (if-does-not-exist nil if-does-not-exist-given)
 	     (external-format :default))
-  (declare (ignore external-format)) ; FIXME
+;  (declare (ignore external-format)) ; FIXME
   (setf element-type (case element-type
                        ((character base-char)
                         'character)
@@ -143,7 +143,7 @@
                    :pathname pathname
                    :format-control "The file ~S does not exist."
                    :format-arguments (list namestring)))))
-       (make-file-stream pathname namestring element-type :input nil))
+       (make-file-stream pathname namestring element-type :input nil external-format))
       (:probe
        (case if-does-not-exist
          (:error
@@ -157,7 +157,8 @@
           ;; this abstract pathname if and only if a file with this name does
           ;; not yet exist." See java.io.File.createNewFile().
           (create-new-file namestring)))
-       (let ((stream (make-file-stream pathname namestring element-type :input nil)))
+       (let ((stream (make-file-stream pathname namestring element-type
+                                       :input nil external-format)))
          (when stream
            (close stream))
          stream))
@@ -204,7 +205,8 @@
           (error 'simple-error
                  :format-control "Option not supported: ~S."
                  :format-arguments (list if-exists))))
-       (let ((stream (make-file-stream pathname namestring element-type direction if-exists)))
+       (let ((stream (make-file-stream pathname namestring element-type
+                                       direction if-exists external-format)))
          (unless stream
            (error 'file-error
                   :pathname pathname

Modified: trunk/j/src/org/armedbear/lisp/socket.lisp
==============================================================================
--- trunk/j/src/org/armedbear/lisp/socket.lisp	(original)
+++ trunk/j/src/org/armedbear/lisp/socket.lisp	Sun Dec  7 23:24:31 2008
@@ -31,15 +31,16 @@
 
 (in-package "SYSTEM")
 
-(defun get-socket-stream (socket &key (element-type 'character))
-  ":ELEMENT-TYPE must be CHARACTER or (UNSIGNED-BYTE 8); the default is CHARACTER."
+(defun get-socket-stream (socket &key (element-type 'character) (external-format :default))
+  ":ELEMENT-TYPE must be CHARACTER or (UNSIGNED-BYTE 8); the default is CHARACTER.
+EXTERNAL-FORMAT must be of the same format as specified for OPEN."
   (cond ((eq element-type 'character))
         ((equal element-type '(unsigned-byte 8)))
         (t
          (error 'simple-type-error
                 :format-control
                 ":ELEMENT-TYPE must be CHARACTER or (UNSIGNED-BYTE 8).")))
-  (%socket-stream socket element-type))
+  (%socket-stream socket element-type external-format))
 
 (defun make-socket (host port)
   (%make-socket host port))

Modified: trunk/j/src/org/armedbear/lisp/socket_stream.java
==============================================================================
--- trunk/j/src/org/armedbear/lisp/socket_stream.java	(original)
+++ trunk/j/src/org/armedbear/lisp/socket_stream.java	Sun Dec  7 23:24:31 2008
@@ -40,19 +40,19 @@
 {
     private socket_stream()
     {
-        super("%socket-stream", PACKAGE_SYS, false, "socket element-type");
+        super("%socket-stream", PACKAGE_SYS, false, "socket element-type external-format");
     }
 
-    public LispObject execute(LispObject first, LispObject second)
+    public LispObject execute(LispObject first, LispObject second, LispObject third)
         throws ConditionThrowable
     {
         Socket socket = (Socket) ((JavaObject)first).getObject();
         LispObject elementType = second; // Checked by caller.
         try {
              Stream in =
-                 new Stream(socket.getInputStream(), elementType);
+                 new Stream(socket.getInputStream(), elementType, third);
              Stream out =
-                 new Stream(socket.getOutputStream(), elementType);
+                 new Stream(socket.getOutputStream(), elementType, third);
              return new SocketStream(socket, in, out);
         }
         catch (Exception e) {




More information about the armedbear-cvs mailing list