diff -r a9813719dfba src/org/armedbear/lisp/BuiltInClass.java --- a/src/org/armedbear/lisp/BuiltInClass.java Tue Mar 23 13:59:08 2010 +0100 +++ b/src/org/armedbear/lisp/BuiltInClass.java Thu Mar 25 06:20:00 2010 +0100 @@ -181,6 +181,9 @@ public static final LispClass FILE_STREAM = addClass(Symbol.FILE_STREAM, new StructureClass(Symbol.FILE_STREAM, list(SYSTEM_STREAM))); + public static final LispClass JAR_STREAM = + addClass(Symbol.JAR_STREAM, + new StructureClass(Symbol.JAR_STREAM, list(SYSTEM_STREAM))); public static final LispClass CONCATENATED_STREAM = addClass(Symbol.CONCATENATED_STREAM, new StructureClass(Symbol.CONCATENATED_STREAM, list(SYSTEM_STREAM))); @@ -233,6 +236,8 @@ FIXNUM.setCPL(FIXNUM, INTEGER, RATIONAL, REAL, NUMBER, CLASS_T); FILE_STREAM.setCPL(FILE_STREAM, SYSTEM_STREAM, STREAM, STRUCTURE_OBJECT, CLASS_T); + JAR_STREAM.setCPL(JAR_STREAM, SYSTEM_STREAM, STREAM, + STRUCTURE_OBJECT, CLASS_T); FLOAT.setDirectSuperclass(REAL); FLOAT.setCPL(FLOAT, REAL, NUMBER, CLASS_T); FUNCTION.setDirectSuperclass(CLASS_T); diff -r a9813719dfba src/org/armedbear/lisp/FileStream.java --- a/src/org/armedbear/lisp/FileStream.java Tue Mar 23 13:59:08 2010 +0100 +++ b/src/org/armedbear/lisp/FileStream.java Thu Mar 25 06:20:00 2010 +0100 @@ -286,11 +286,6 @@ else { return type_error(first, Symbol.PATHNAME); } - if (pathname.isJar()) { - error(new FileError("Direct stream input/output on entries in JAR files no currently supported.", - pathname)); - } - final LispObject namestring = checkString(second); LispObject elementType = third; LispObject direction = fourth; @@ -300,16 +295,30 @@ 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, - externalFormat); - } - catch (FileNotFoundException e) { - return NIL; - } - catch (IOException e) { - return error(new StreamError(null, e)); + + if (pathname.isJar()) { + if (direction != Keyword.INPUT) { + error(new FileError("Only direction :INPUT is supported for jar files.", pathname)); + } + try { + return new JarStream(pathname, namestring.getStringValue(), + elementType, direction, ifExists, + externalFormat); + } catch (IOException e) { + return error(new StreamError(null, e)); + } + } else { + try { + return new FileStream(pathname, namestring.getStringValue(), + elementType, direction, ifExists, + externalFormat); + } + catch (FileNotFoundException e) { + return NIL; + } + catch (IOException e) { + return error(new StreamError(null, e)); + } } } }; diff -r a9813719dfba src/org/armedbear/lisp/JarStream.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/org/armedbear/lisp/JarStream.java Thu Mar 25 06:20:00 2010 +0100 @@ -0,0 +1,150 @@ +/* + * JarStream.java + * + * Copyright (C) 2010 Mark Evenson + * $Id: FileStream.java 12422 2010-02-06 10:52:32Z mevenson $ + * + * 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. + * + * As a special exception, the copyright holders of this library give you + * permission to link this library with independent modules to produce an + * executable, regardless of the license terms of these independent + * modules, and to copy and distribute the resulting executable under + * terms of your choice, provided that you also meet, for each linked + * independent module, the terms and conditions of the license of that + * module. An independent module is a module which is not derived from + * or based on this library. If you modify this library, you may extend + * this exception to your version of the library, but you are not + * obligated to do so. If you do not wish to do so, delete this + * exception statement from your version. + */ + +package org.armedbear.lisp; + +import static org.armedbear.lisp.Lisp.*; + +import java.io.File; +import java.io.InputStream; +import java.io.Reader; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.BufferedReader; + +/** + * Stream interface for an entry in a jar pathname. + * + * This only supports reading from the stream. + */ +public final class JarStream extends Stream +{ + private final Pathname pathname; + private final InputStream input; + private final Reader reader; + private final int bytesPerUnit; + + public JarStream(Pathname pathname, String namestring, + LispObject elementType, LispObject direction, + LispObject ifExists, LispObject format) + throws IOException + { + super(Symbol.JAR_STREAM); + Debug.assertTrue(direction == Keyword.INPUT); + Debug.assertTrue(pathname.name != NIL); + isInputStream = true; + + super.setExternalFormat(format); + + this.pathname = pathname; + this.elementType = elementType; + + this.input = pathname.getInputStream(); + if (elementType == Symbol.CHARACTER || elementType == Symbol.BASE_CHAR) { + isCharacterStream = true; + bytesPerUnit = 1; + InputStreamReader isr = new InputStreamReader(input); + this.reader = (Reader) new BufferedReader(isr); + initAsCharacterInputStream(this.reader); + } else { + isBinaryStream = true; + int width = Fixnum.getValue(elementType.cadr()); + bytesPerUnit = width / 8; + this.reader = null; + initAsBinaryInputStream(this.input); + } + } + + @Override + public LispObject typeOf() + { + return Symbol.JAR_STREAM; + } + + @Override + public LispObject classOf() + { + return BuiltInClass.JAR_STREAM; + } + + @Override + public LispObject typep(LispObject typeSpecifier) + { + if (typeSpecifier == Symbol.JAR_STREAM) + return T; + if (typeSpecifier == BuiltInClass.JAR_STREAM) + return T; + return super.typep(typeSpecifier); + } + + @Override + public void setExternalFormat(LispObject format) { + super.setExternalFormat(format); + } + + public Pathname getPathname() + { + return pathname; + } + + @Override + public void _close() + { + try { + if (input != null) { + input.close(); + } + if (reader != null) { + reader.close(); + } + setOpen(false); + } + catch (IOException e) { + error(new StreamError(this, e)); + } + } + + @Override + public String writeToString() + { + StringBuffer sb = new StringBuffer(); + sb.append(Symbol.JAR_STREAM.writeToString()); + String namestring = pathname.getNamestring(); + if (namestring != null) { + sb.append(" "); + sb.append(namestring); + } + return unreadableString(sb.toString()); + } +} diff -r a9813719dfba src/org/armedbear/lisp/Load.java --- a/src/org/armedbear/lisp/Load.java Tue Mar 23 13:59:08 2010 +0100 +++ b/src/org/armedbear/lisp/Load.java Thu Mar 25 06:20:00 2010 +0100 @@ -462,17 +462,24 @@ String type = truePathname.type.getStringValue(); if (type.equals(COMPILE_FILE_TYPE) || type.equals(COMPILE_FILE_INIT_FASL_TYPE.toString())) { - thread.bindSpecial(Symbol.LOAD_TRUENAME_FASL, truePathname); + Pathname truenameFasl = new Pathname(truePathname); + thread.bindSpecial(Symbol.LOAD_TRUENAME_FASL, truenameFasl); } if (truePathname.type.getStringValue() .equals(COMPILE_FILE_INIT_FASL_TYPE.getStringValue()) && truePathname.isJar()) { if (truePathname.device.cdr() != NIL ) { - // set truename to the enclosing JAR + // We set *LOAD-TRUENAME* to the argument that + // a user would pass to LOAD. + Pathname enclosingJar = (Pathname)truePathname.device.cdr().car(); + truePathname.device = new Cons(truePathname.device.car(), NIL); truePathname.host = NIL; - truePathname.directory = NIL; - truePathname.name = NIL; - truePathname.type = NIL; + truePathname.directory = enclosingJar.directory; + if (truePathname.directory.car().equals(Keyword.RELATIVE)) { + truePathname.directory.setCar(Keyword.ABSOLUTE); + } + truePathname.name = enclosingJar.name; + truePathname.type = enclosingJar.type; truePathname.invalidateNamestring(); } else { // XXX There is something fishy in the asymmetry diff -r a9813719dfba src/org/armedbear/lisp/Pathname.java --- a/src/org/armedbear/lisp/Pathname.java Tue Mar 23 13:59:08 2010 +0100 +++ b/src/org/armedbear/lisp/Pathname.java Thu Mar 25 06:20:00 2010 +0100 @@ -643,6 +643,7 @@ p.directory = directory; p.name = name; p.type = type; + p.invalidateNamestring(); String path = p.getNamestring(); StringBuilder result = new StringBuilder(); if (Utilities.isPlatformWindows) { diff -r a9813719dfba src/org/armedbear/lisp/Symbol.java --- a/src/org/armedbear/lisp/Symbol.java Tue Mar 23 13:59:08 2010 +0100 +++ b/src/org/armedbear/lisp/Symbol.java Thu Mar 25 06:20:00 2010 +0100 @@ -2999,6 +2999,9 @@ PACKAGE_SYS.addExternalSymbol("SET-CHAR"); public static final Symbol SET_SCHAR = PACKAGE_SYS.addExternalSymbol("SET-SCHAR"); + public static final Symbol JAR_STREAM = + PACKAGE_SYS.addExternalSymbol("JAR-STREAM"); + // Internal symbols in SYSTEM package. public static final Symbol BACKQUOTE_MACRO = diff -r a9813719dfba src/org/armedbear/lisp/ZipCache.java --- a/src/org/armedbear/lisp/ZipCache.java Tue Mar 23 13:59:08 2010 +0100 +++ b/src/org/armedbear/lisp/ZipCache.java Thu Mar 25 06:20:00 2010 +0100 @@ -111,11 +111,13 @@ try { return new ZipFile(f); } catch (ZipException e) { - Debug.trace(e); // XXX - return null; + error(new FileError("Failed to construct ZipFile" + + " because " + e, + Pathname.makePathname(f))); } catch (IOException e) { - Debug.trace(e); // XXX - return null; + error(new FileError("Failed to contruct ZipFile" + + " because " + e, + Pathname.makePathname(f))); } } else { Entry e = fetchURL(url, false); @@ -185,11 +187,13 @@ try { entry.file = new ZipFile(f); } catch (ZipException e) { - Debug.trace(e); // XXX - return null; + error(new FileError("Failed to get cached ZipFile" + + " because " + e, + Pathname.makePathname(f))); } catch (IOException e) { - Debug.trace(e); // XXX - return null; + error(new FileError("Failed to get cached ZipFile" + + " because " + e, + Pathname.makePathname(f))); } } else { entry = fetchURL(url, true); @@ -205,29 +209,31 @@ try { jarURL = new URL("jar:" + url + "!/"); } catch (MalformedURLException e) { - Debug.trace(e); - Debug.assertTrue(false); // XXX + error(new LispError("Failed to form a jar: URL from " + + "'" + url + "'" + + " because " + e)); } - URLConnection connection; + URLConnection connection = null; try { connection = jarURL.openConnection(); - } catch (IOException ex) { - Debug.trace("Failed to open " - + "'" + jarURL + "'"); - return null; + } catch (IOException e) { + error(new LispError("Failed to open " + + "'" + jarURL + "'" + + " with exception " + + e)); } if (!(connection instanceof JarURLConnection)) { - // XXX - Debug.trace("Could not get a URLConnection from " + jarURL); - return null; + error(new LispError("Could not get a URLConnection from " + + "'" + jarURL + "'")); } JarURLConnection jarURLConnection = (JarURLConnection) connection; jarURLConnection.setUseCaches(cached); try { result.file = jarURLConnection.getJarFile(); } catch (IOException e) { - Debug.trace(e); - Debug.assertTrue(false); // XXX + error(new LispError("Failed to fetch URL " + + "'" + jarURLConnection + "'" + + " because " + e)); } result.lastModified = jarURLConnection.getLastModified(); return result;