source: branches/0.17.x/abcl/src/org/armedbear/lisp/FileStream.java

Last change on this file was 12255, checked in by ehuelsmann, 16 years ago

Rename ConditionThrowable? to ControlTransfer? and remove

try/catch blocks which don't have anything to do with
non-local transfer of control.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
File size: 9.7 KB
Line 
1/*
2 * FileStream.java
3 *
4 * Copyright (C) 2004-2006 Peter Graves
5 * Copyright (C) 2008 Hideo at Yokohama
6 * $Id: FileStream.java 12255 2009-11-06 22:36:32Z ehuelsmann $
7 *
8 * This program is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU General Public License
10 * as published by the Free Software Foundation; either version 2
11 * of the License, or (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
21 *
22 * As a special exception, the copyright holders of this library give you
23 * permission to link this library with independent modules to produce an
24 * executable, regardless of the license terms of these independent
25 * modules, and to copy and distribute the resulting executable under
26 * terms of your choice, provided that you also meet, for each linked
27 * independent module, the terms and conditions of the license of that
28 * module.  An independent module is a module which is not derived from
29 * or based on this library.  If you modify this library, you may extend
30 * this exception to your version of the library, but you are not
31 * obligated to do so.  If you do not wish to do so, delete this
32 * exception statement from your version.
33 */
34
35package org.armedbear.lisp;
36
37import java.io.File;
38import java.io.InputStream;
39import java.io.OutputStream;
40import java.io.Reader;
41import java.io.Writer;
42import java.io.FileNotFoundException;
43import java.io.IOException;
44import java.io.RandomAccessFile;
45import org.armedbear.lisp.util.RandomAccessCharacterFile;
46
47public final class FileStream extends Stream
48{
49    private final RandomAccessCharacterFile racf;
50    private final Pathname pathname;
51    private final int bytesPerUnit;
52
53    public FileStream(Pathname pathname, String namestring,
54                      LispObject elementType, LispObject direction,
55                      LispObject ifExists, LispObject format)
56        throws IOException
57    {
58        /* externalFormat is a LispObject of which the first char is a
59         * name of a character encoding (such as :UTF-8 or :ISO-8859-1), used
60         * by ABCL as a string designator, unless the name is :CODE-PAGE.
61         * A real string is (thus) also allowed.
62         *
63         * Then, a property list follows with 3 possible keys:
64         *   :ID (values: code page numbers supported by MS-DOS/IBM-DOS/MS-Windows
65         *   :EOL-STYLE (values: :CR / :LF / :CRLF [none means native])
66         *   :LITTLE-ENDIAN (values: NIL / T)
67         *
68         * These definitions have been taken from FLEXI-STREAMS:
69         *    http://www.weitz.de/flexi-streams/#make-external-format
70         */
71        final File file = new File(namestring);
72        String mode = null;
73        if (direction == Keyword.INPUT) {
74            mode = "r";
75            isInputStream = true;
76        } else if (direction == Keyword.OUTPUT) {
77            mode = "rw";
78            isOutputStream = true;
79        } else if (direction == Keyword.IO) {
80            mode = "rw";
81            isInputStream = true;
82            isOutputStream = true;
83        }
84       
85        Debug.assertTrue(mode != null);
86        RandomAccessFile raf = new RandomAccessFile(file, mode);
87 
88        // ifExists is ignored unless we have an output stream.
89        if (isOutputStream) {
90            final long length = file.isFile() ? file.length() : 0;
91            if (length > 0) {
92                if (ifExists == Keyword.OVERWRITE)
93                    raf.seek(0);
94                else if (ifExists == Keyword.APPEND)
95                    raf.seek(raf.length());
96                else
97                    raf.setLength(0);
98            }
99        }
100        setExternalFormat(format);
101       
102  // don't touch raf directly after passing it to racf.
103  // the state will become inconsistent if you do that.
104        racf = new RandomAccessCharacterFile(raf, encoding);
105
106        this.pathname = pathname;
107        this.elementType = elementType;
108        if (elementType == Symbol.CHARACTER || elementType == Symbol.BASE_CHAR) {
109            isCharacterStream = true;
110            bytesPerUnit = 1;
111      if (isInputStream) {
112    initAsCharacterInputStream(racf.getReader());
113      }
114      if (isOutputStream) {
115    initAsCharacterOutputStream(racf.getWriter());
116      }
117        } else {
118            isBinaryStream = true;
119            int width = Fixnum.getValue(elementType.cadr());
120            bytesPerUnit = width / 8;
121      if (isInputStream) {
122    initAsBinaryInputStream(racf.getInputStream());
123      }
124      if (isOutputStream) {
125    initAsBinaryOutputStream(racf.getOutputStream());
126      }
127        }
128    }
129
130    @Override
131    public LispObject typeOf()
132    {
133        return Symbol.FILE_STREAM;
134    }
135
136    @Override
137    public LispObject classOf()
138    {
139        return BuiltInClass.FILE_STREAM;
140    }
141
142    @Override
143    public LispObject typep(LispObject typeSpecifier)
144    {
145        if (typeSpecifier == Symbol.FILE_STREAM)
146            return T;
147        if (typeSpecifier == BuiltInClass.FILE_STREAM)
148            return T;
149        return super.typep(typeSpecifier);
150    }
151
152    public Pathname getPathname()
153    {
154        return pathname;
155    }
156
157    @Override
158    public LispObject fileLength()
159    {
160        final long length;
161        if (isOpen()) {
162            try {
163                length = racf.length();
164            }
165            catch (IOException e) {
166                error(new StreamError(this, e));
167                // Not reached.
168                return NIL;
169            }
170        } else {
171            String namestring = pathname.getNamestring();
172            if (namestring == null)
173                return error(new SimpleError("Pathname has no namestring: " +
174                                              pathname.writeToString()));
175            File file = new File(namestring);
176            length = file.length(); // in 8-bit bytes
177        }
178        if (isCharacterStream)
179            return number(length);
180        // "For a binary file, the length is measured in units of the
181        // element type of the stream."
182        return number(length / bytesPerUnit);
183    }
184
185    @Override
186    protected void _unreadChar(int n)
187    {
188        try {
189            racf.unreadChar((char)n);
190        }
191        catch (IOException e) {
192            error(new StreamError(this, e));
193        }
194    }
195
196    @Override
197    protected boolean _charReady()
198    {
199        return true;
200    }
201
202    @Override
203    public void _clearInput()
204    {
205        try {
206      if (isInputStream) {
207    racf.position(racf.length());
208      } else {
209    streamNotInputStream();
210      }
211        }
212        catch (IOException e) {
213            error(new StreamError(this, e));
214        }
215    }
216
217    @Override
218    protected long _getFilePosition()
219    {
220        try {
221            long pos = racf.position();
222            return pos / bytesPerUnit;
223        }
224        catch (IOException e) {
225            error(new StreamError(this, e));
226            // Not reached.
227            return -1;
228        }
229    }
230
231    @Override
232    protected boolean _setFilePosition(LispObject arg)
233    {
234        try {
235            long pos;
236            if (arg == Keyword.START)
237                pos = 0;
238            else if (arg == Keyword.END)
239                pos = racf.length();
240            else {
241                long n = Fixnum.getValue(arg); // FIXME arg might be a bignum
242                pos = n * bytesPerUnit;
243            }
244            racf.position(pos);
245        }
246        catch (IOException e) {
247            error(new StreamError(this, e));
248        }
249        return true;
250    }
251
252    @Override
253    public void _close()
254    {
255        try {
256            racf.close();
257            setOpen(false);
258        }
259        catch (IOException e) {
260            error(new StreamError(this, e));
261        }
262    }
263
264    @Override
265    public String writeToString()
266    {
267        return unreadableString(Symbol.FILE_STREAM);
268    }
269
270    // ### make-file-stream pathname namestring element-type direction if-exists external-format => stream
271    private static final Primitive MAKE_FILE_STREAM =
272        new Primitive("make-file-stream", PACKAGE_SYS, true,
273                      "pathname namestring element-type direction if-exists external-format")
274    {
275        @Override
276        public LispObject execute(LispObject first, LispObject second,
277                                  LispObject third, LispObject fourth,
278                                  LispObject fifth, LispObject sixth)
279
280        {
281            final Pathname pathname;
282            if(first instanceof Pathname) {
283                pathname = (Pathname) first;
284            }
285            else {
286                return type_error(first, Symbol.PATHNAME);
287            }
288            final LispObject namestring = checkString(second);
289            LispObject elementType = third;
290            LispObject direction = fourth;
291            LispObject ifExists = fifth;
292            LispObject externalFormat = sixth;
293           
294            if (direction != Keyword.INPUT && direction != Keyword.OUTPUT &&
295                direction != Keyword.IO)
296                error(new LispError("Direction must be :INPUT, :OUTPUT, or :IO."));
297            try {
298                return new FileStream(pathname, namestring.getStringValue(),
299                                      elementType, direction, ifExists,
300                                      externalFormat);
301            }
302            catch (FileNotFoundException e) {
303                return NIL;
304            }
305            catch (IOException e) {
306                return error(new StreamError(null, e));
307            }
308        }
309    };
310}
Note: See TracBrowser for help on using the repository browser.