source: trunk/abcl/src/org/armedbear/lisp/FileStream.java

Last change on this file was 15771, checked in by Mark Evenson, 4 months ago

Add override for getPathname

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
File size: 11.1 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 15771 2023-12-27 10:30:31Z mevenson $
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 static org.armedbear.lisp.Lisp.*;
38
39import java.io.File;
40import java.io.InputStream;
41import java.io.OutputStream;
42import java.io.Reader;
43import java.io.Writer;
44import java.io.FileNotFoundException;
45import java.io.IOException;
46import java.io.RandomAccessFile;
47import org.armedbear.lisp.util.RandomAccessCharacterFile;
48
49public final class FileStream extends Stream
50{
51    private final RandomAccessCharacterFile racf;
52    private final Pathname pathname;
53    private final int bytesPerUnit;
54
55    public FileStream(Pathname pathname, 
56                      LispObject elementType, LispObject direction,
57                      LispObject ifExists, LispObject format)
58        throws IOException
59    {
60        /* externalFormat is a LispObject of which the first char is a
61         * name of a character encoding (such as :UTF-8 or :ISO-8859-1), used
62         * by ABCL as a string designator, unless the name is :CODE-PAGE.
63         * A real string is (thus) also allowed.
64         *
65         * Then, a property list follows with 3 possible keys:
66         *   :ID (values: code page numbers supported by MS-DOS/IBM-DOS/MS-Windows
67         *   :EOL-STYLE (values: :CR / :LF / :CRLF [none means native])
68         *   :LITTLE-ENDIAN (values: NIL / T)
69         *
70         * These definitions have been taken from FLEXI-STREAMS:
71         *    http://www.weitz.de/flexi-streams/#make-external-format
72         */
73        super(Symbol.FILE_STREAM);
74        final File file = pathname.getFile();
75        String mode = null;
76        if (direction == Keyword.INPUT) {
77            mode = "r";
78            isInputStream = true;
79        } else if (direction == Keyword.OUTPUT) {
80            mode = "rw";
81            isOutputStream = true;
82        } else if (direction == Keyword.IO) {
83            mode = "rw";
84            isInputStream = true;
85            isOutputStream = true;
86        }
87       
88        Debug.assertTrue(mode != null);
89        RandomAccessFile raf = new RandomAccessFile(file, mode);
90       
91        // ifExists is ignored unless we have an output stream.
92        if (isOutputStream) {
93            final long length = file.isFile() ? file.length() : 0;
94            if (length > 0) {
95                if (ifExists == Keyword.OVERWRITE)
96                    raf.seek(0);
97                else if (ifExists == Keyword.APPEND)
98                    raf.seek(raf.length());
99                else
100                    raf.setLength(0);
101            }
102        }
103        setExternalFormat(format);
104       
105        // don't touch raf directly after passing it to racf.
106        // the state will become inconsistent if you do that.
107        racf = new RandomAccessCharacterFile(raf, encoding);
108
109        this.pathname = pathname;
110        this.elementType = elementType;
111        if (elementType == Symbol.CHARACTER || elementType == Symbol.BASE_CHAR) {
112            isCharacterStream = true;
113            bytesPerUnit = 1;
114            if (isInputStream) {
115                initAsCharacterInputStream(racf.getReader());
116            }
117            if (isOutputStream) {
118                initAsCharacterOutputStream(racf.getWriter());
119            }
120        } else {
121            isBinaryStream = true;
122            int width = Fixnum.getValue(elementType.cadr());
123            bytesPerUnit = width / 8;
124            if (isInputStream) {
125                initAsBinaryInputStream(racf.getInputStream());
126            }
127            if (isOutputStream) {
128                initAsBinaryOutputStream(racf.getOutputStream());
129            }
130        }
131    }
132
133    @Override
134    public LispObject typeOf()
135    {
136        return Symbol.FILE_STREAM;
137    }
138
139    @Override
140    public LispObject classOf()
141    {
142        return BuiltInClass.FILE_STREAM;
143    }
144
145    @Override
146    public LispObject typep(LispObject typeSpecifier)
147    {
148        if (typeSpecifier == Symbol.FILE_STREAM)
149            return T;
150        if (typeSpecifier == BuiltInClass.FILE_STREAM)
151            return T;
152        return super.typep(typeSpecifier);
153    }
154
155    @Override
156    public void setExternalFormat(LispObject format) {
157        super.setExternalFormat(format);
158
159        if (racf != null)
160            // setExternalFormat also called before 'racf' is set up
161            racf.setEncoding(encoding);
162    }
163
164    @Override
165    public Pathname getPathname()
166    {
167        return pathname;
168    }
169
170    @Override
171    public LispObject fileLength()
172    {
173        final long length;
174        if (isOpen()) {
175            try {
176                length = racf.length();
177            }
178            catch (IOException e) {
179                error(new StreamError(this, e));
180                // Not reached.
181                return NIL;
182            }
183        } else {
184            String namestring = pathname.getNamestring();
185            if (namestring == null)
186                return error(new SimpleError("Pathname has no namestring: " +
187                                              pathname.princToString()));
188            File file = new File(namestring);
189            length = file.length(); // in 8-bit bytes
190        }
191        if (isCharacterStream)
192            return number(length);
193        // "For a binary file, the length is measured in units of the
194        // element type of the stream."
195        return number(length / bytesPerUnit);
196    }
197
198    @Override
199    protected boolean _charReady()
200    {
201        return true;
202    }
203
204    @Override
205    public void _clearInput()
206    {
207        try {
208            if (isInputStream) {
209                racf.position(racf.length());
210            } else {
211                streamNotInputStream();
212            }
213        }
214        catch (IOException e) {
215            error(new StreamError(this, e));
216        }
217    }
218
219    @Override
220    protected long _getFilePosition()
221    {
222        try {
223            long pos = racf.position();
224            return pos / bytesPerUnit;
225        }
226        catch (IOException e) {
227            error(new StreamError(this, e));
228            // Not reached.
229            return -1;
230        }
231    }
232
233    @Override
234    protected boolean _setFilePosition(LispObject arg)
235    {
236        try {
237            long pos = 0;
238            if (arg == Keyword.START)
239                pos = 0;
240            else if (arg == Keyword.END)
241                pos = racf.length();
242            else if (arg instanceof Fixnum)
243                pos = ((Fixnum) arg).value * bytesPerUnit;
244            else if (arg instanceof Bignum)
245                pos = ((Bignum) arg).longValue() * bytesPerUnit;
246            else
247                type_error(arg, Symbol.INTEGER);
248            racf.position(pos);
249        }
250        catch (IOException e) {
251            error(new StreamError(this, e));
252        }
253        return true;
254    }
255
256    @Override
257    public void _close()
258    {
259        try {
260            racf.close();
261            setOpen(false);
262        }
263        catch (IOException e) {
264            error(new StreamError(this, e));
265        }
266    }
267
268    @Override
269    public String printObject()
270    {
271        return unreadableString("FILE-STREAM");
272    }
273
274    // ### make-file-stream pathname element-type direction if-exists external-format => stream
275    private static final Primitive MAKE_FILE_STREAM =
276        new Primitive("make-file-stream", PACKAGE_SYS, true,
277                      "pathname element-type direction if-exists external-format")
278    {
279        @Override
280        public LispObject execute(LispObject first, LispObject second,
281                                  LispObject third, LispObject fourth,
282                                  LispObject fifth)
283
284        {
285            final Pathname pathname;
286            if (first instanceof Pathname) {
287                pathname = (Pathname) first;
288            }
289            else {
290                return type_error(first, Symbol.PATHNAME);
291            }
292            LispObject elementType = second;
293            LispObject direction = third;
294            LispObject ifExists = fourth;
295            LispObject externalFormat = fifth;
296           
297            if (direction != Keyword.INPUT && direction != Keyword.OUTPUT &&
298                direction != Keyword.IO)
299                error(new LispError("Direction must be :INPUT, :OUTPUT, or :IO."));
300
301            if (pathname.isJar())  {
302                if (direction != Keyword.INPUT) {
303                    error(new FileError("Only direction :INPUT is supported for jar files.", pathname));
304                }
305                try { 
306                    return new JarStream(pathname, 
307                                         elementType, direction, ifExists,
308                                         externalFormat);
309                } catch (IOException e) {
310                    return error(new StreamError(null, e));
311                }
312            } else if (pathname instanceof URLPathname
313                       && !(URLPathname.isFile(pathname))) {
314                if (direction != Keyword.INPUT) {
315                    error(new FileError("Only direction :INPUT is supported for URLs.", pathname));
316                }
317                try { 
318                    return new URLStream(pathname,
319                                         elementType, direction, ifExists,
320                                         externalFormat);
321                } catch (IOException e) {
322                    return error(new StreamError(null, e));
323                }
324            } else {
325                try {
326                    return new FileStream(pathname, 
327                                          elementType, direction, ifExists,
328                                          externalFormat);
329                }
330                catch (FileNotFoundException e) {
331                    return NIL;
332                }
333                catch (IOException e) {
334                    return error(new StreamError(null, e));
335                }
336            }
337        }
338    };
339}
Note: See TracBrowser for help on using the repository browser.