diff -r 884fd6b1beec build.properties.in
--- a/build.properties.in Fri Feb 05 16:41:42 2010 +0100
+++ b/build.properties.in Fri Feb 05 18:31:59 2010 +0100
@@ -1,11 +1,14 @@
# build.properties
# $Id: build.properties,v 1.23 2007-03-03 19:19:11 piso Exp $
-# Contents show up in JAR Manifest in the Implementation-Source attribute
-#version.src=[abcl]
+# version.src contents show up in JAR Manifest in the Implementation-Source attribute
+#version.src=[abcl svn]
-# If set, ABCL attempts to perform incremental compilation
+# abcl.build.incremental attempts to perform incremental compilation
#abcl.build.incremental=true
-# Skip the compilation of Lisp sources (for debugging)
+# abcl.compile.lisp.skip skips the compilation of Lisp sources in Netbeans (for debugging)
#abcl.compile.lisp.skip=true
+
+# java.options sets the Java options in the abcl wrapper scripts
+#java.options=-Xmx1g
\ No newline at end of file
diff -r 884fd6b1beec build.xml
--- a/build.xml Fri Feb 05 16:41:42 2010 +0100
+++ b/build.xml Fri Feb 05 18:31:59 2010 +0100
@@ -252,6 +252,7 @@
classname="org.armedbear.lisp.Main">
+
@@ -722,7 +723,12 @@
Finished recording test output in ${abcl.test.log.file}.
-
+
+
+
+
diff -r 884fd6b1beec doc/pathnames/abcl-jar-url.text
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/doc/pathnames/abcl-jar-url.text Fri Feb 05 18:31:59 2010 +0100
@@ -0,0 +1,259 @@
+JARs and JAR entries in ABCL
+============================
+
+Mark Evenson
+Created: 09 JAN 2010
+Modified: 24 JAN 2010
+
+Notes towards sketching an implementation of "jar:" references to be
+contained in PATHNAMEs within ABCL
+
+
+Goals
+-----
+
+1. Use Common Lisp pathnames to refer to entries in a JAR file.
+
+
+2. Use 'jar:' schema as documented in java.net.JarURLConnection for
+ namestring representation.
+
+An entry in a JAR file:
+ #p"jar:file:baz.jar!/foo"
+
+A JAR file:
+ #p"jar:file:baz.jar!/"
+
+A JAR file accessible via URL
+ #p"jar:http://example.org/abcl.jar!/"
+
+An entry in a ABCL FASL in a URL accessible JAR file
+ #p"jar:jar:http://example.org/abcl.jar!/foo.abcl!/foo-1.cls"
+
+3. MERGE-PATHNAMES working for JAR entries
+
+ (merge-pathnames "foo-1.cls" "jar:jar:file:baz.jar!/foo.abcl!/foo._")
+ "jar:jar:file:baz.jar!/foo.abcl!/foo-1.cls"
+
+ (merge-pathnames "foo-1.cls" "jar:file:foo.abcl!/")
+ "jar:file:foo.abcl!/foo-1.cls"
+
+4. TRUENAME and PROBE-FILE working with "jar:"
+
+4.1 TRUENAME cannonicalizing the JAR reference.
+
+5. DIRECTORY working within JAR files (and within JAR in JAR).
+
+6. References "jar:" for all strings that java.net.URL can
+ resolve works.
+
+
+Implementation
+--------------
+
+Using PATHNAMES
+
+* A PATHNAME refering to a file within a JAR is known as a JAR
+ PATHNAME. It can either refer to the entire JAR file or an entry
+ within the JAR file.
+
+* A JAR PATHNAME always has a DEVICE which is a proper list. This
+ distinguishes it from other uses of Pathname.
+
+* The DEVICE of a JAR PATHNAME will be a list with either one or two
+ elements. The first element of the JAR PATHNAME can be either a
+ PATHNAME representing a JAR on the filesystem, or a SimpleString
+ representing a URL.
+
+* a PATHNAME occuring in the list in the DEVICE of a JAR PATHNAME is
+ known as a DEVICE PATHNAME.
+
+* If the DEVICE is a String it must be a String that successfully
+ constructs a URL via the java.net.URL(String) constructor
+
+* Only the first entry in the the DEVICE list may be a String.
+
+* Otherwise the the DEVICE PATHAME denotes the PATHNAME of the JAR file
+
+* The DEVICE PATHNAME list of enclosing JARs runs from outermost to
+ innermost.
+
+
+
+Use Cases
+---------
+
+// UC1 -- JAR
+pathname: {
+ namestring: "jar:file:foo/baz.jar!/"
+ device: (
+ pathname: {
+ device: "jar:file:"
+ directory: (:RELATIVE "foo")
+ name: "baz"
+ type: "jar"
+ }
+ )
+}
+
+
+// UC1 -- JAR entry
+pathname: {
+ namestring: "jar:file:baz.jar!/foo.abcl"
+ device: ( pathname: {
+ device: "jar:file:"
+ name: "baz"
+ type: "jar"
+ })
+ name: "foo"
+ type: "abcl"
+}
+
+
+// UC3 -- JAR file in a JAR entry
+pathname: {
+ namestring: "jar:jar:file:baz.jar!/foo.abcl!/"
+ device: (
+ pathname: {
+ name: "baz"
+ type: "jar"
+ }
+ pathname: {
+ name: "foo"
+ type: "abcl"
+ }
+ )
+}
+
+// UC4 -- JAR entry in a JAR entry with directories
+pathname: {
+ namestring: "jar:jar:file:a/baz.jar!/b/c/foo.abcl!/this/that/foo-20.cls"
+ device: (
+ pathname {
+ directory: (:RELATIVE "a")
+ name: "bar"
+ type: "jar"
+ }
+ pathname {
+ directory: (:RELATIVE "b")
+ name: "foo"
+ type: "abcl"
+ }
+ )
+ directory: (:RELATIVE "this" "that")
+ name: "foo-20"
+ type: "cls"
+}
+
+// UC5 -- JAR Entry in a JAR Entry
+pathname: {
+ namestring: "jar:jar:file:a/foo/baz.jar!/foo.abcl!/a/b/bar-1.cls"
+ device: (
+ pathname: {
+ device: "jar:file:"
+ name: "baz"
+ type: "jar"
+ }
+ pathname: {
+ name: "foo"
+ type: "abcl"
+ }
+ )
+ name: "bar-1"
+ type: "cls"
+}
+
+// UC6 -- JAR entry in a http: accessible JAR file
+pathname: {
+ namestring: "jar:http://example.org/abcl.jar!/org/armedbear/lisp/Version.class",
+ device: (
+ "http://example.org/abcl.jar"
+ pathname: {
+ directory: (:relative "org" "armedbear" "lisp")
+ name: "Version"
+ type: "class"
+ }
+}
+
+// UC7 -- JAR Entry in a JAR Entry in a URL accessible JAR FILE
+pathname: {
+ namestring "jar:jar:http://example.org/abcl.jar!/foo.abcl!/foo-1.cls"
+ device: (
+ "http://example.org/abcl.jar"
+ pathname: {
+ name: "foo"
+ type: "abcl"
+ }
+ )
+ name: "foo-1"
+ type: "cls"
+}
+
+// UC8 -- JAR in an absolute directory
+
+pathame: {
+ namestring: "jar:file:/a/b/foo.jar!/"
+ device: (
+ pathname: {
+ directory: (:ABSOLUTE "a" "b")
+ name: "foo"
+ type: "jar"
+ }
+ )
+}
+
+// UC9 -- JAR in an relative directory with entry
+pathname: {
+ namestring: "jar:file:a/b/foo.jar!/c/d/foo.lisp"
+ device: (
+ directory: (:RELATIVE "a" "b")
+ name: "foo"
+ type: "jar"
+ )
+ directory: (:RELATIVE "c" "d")
+ name: "foo"
+ type: "lisp
+}
+
+
+
+Problems
+--------
+
+1. DEVICE PATHNAMES require the context within the nested PATHNAME
+ structure to be interpreted correctly.
+
+Result: Be careful when manipulating PATHNAMEs that refer to JARs
+
+
+History
+-------
+
+In the use of PATHNAMEs linked by the DEVICE field, we found the problem
+that UNC path support uses the DEVICE field
+
+Result: JARs located on UNC mounts can't be referenced. via '\\'.
+
+ i.e. jar:jar:file:\\server\share\a\b\foo.jar!/this\that!/foo.java
+
+would not have
+
+Solution: Instead of having DEVICE point to a PATHNAME, have DEVICE
+be a list of PATHNAMES
+
+pathname: {
+ namestring: "jar:jar:file:\\server\share\foo.jar!/foo.abcl!/"
+ device: ( pathname: {
+ name: "foo"
+ type: "abcl"
+ }
+ pathname: {
+ host: "server"
+ device: "share"
+ name: "foo"
+ type: "jar"
+ }
+}
+
+Which order for the list? Outermost first or last? Outermost first.
+
diff -r 884fd6b1beec nbproject/build-impl.xml
--- a/src/org/armedbear/lisp/Autoload.java Fri Feb 05 16:41:42 2010 +0100
+++ b/src/org/armedbear/lisp/Autoload.java Fri Feb 05 18:31:59 2010 +0100
@@ -667,6 +667,7 @@
autoload(PACKAGE_SYS, "simple-list-remove-duplicates", "simple_list_remove_duplicates");
autoload(PACKAGE_SYS, "single-float-bits", "FloatFunctions", true);
autoload(PACKAGE_SYS, "std-allocate-instance", "StandardObjectFunctions", true);
+ autoload(PACKAGE_SYS, "unzip", "unzip", true);
autoload(PACKAGE_SYS, "zip", "zip", true);
autoload(PACKAGE_SYS, "proxy-preloaded-function",
diff -r 884fd6b1beec src/org/armedbear/lisp/AutoloadedFunctionProxy.java
--- a/src/org/armedbear/lisp/AutoloadedFunctionProxy.java Fri Feb 05 16:41:42 2010 +0100
+++ b/src/org/armedbear/lisp/AutoloadedFunctionProxy.java Fri Feb 05 18:31:59 2010 +0100
@@ -52,7 +52,7 @@
AUTOLOADING_CACHE, // allow loading local preloaded functions
Load._FASL_ANONYMOUS_PACKAGE_, // package for uninterned symbols
Symbol._PACKAGE_, // current package
- Symbol.LOAD_TRUENAME // LOAD-TIME-VALUE depends on this
+ Symbol.LOAD_TRUENAME // LOAD-TIME-VALUE depends on this
};
final private Symbol symbol;
@@ -304,7 +304,8 @@
Hashtable cache
= (Hashtable)AUTOLOADING_CACHE.symbolValue(thread).javaInstance();
- byte[] bytes = readFunctionBytes(namestring);
+ Pathname pathname = new Pathname(namestring);
+ byte[] bytes = readFunctionBytes(pathname);
cache.put(namestring, bytes);
return T;
diff -r 884fd6b1beec src/org/armedbear/lisp/FileStream.java
--- a/src/org/armedbear/lisp/FileStream.java Fri Feb 05 16:41:42 2010 +0100
+++ b/src/org/armedbear/lisp/FileStream.java Fri Feb 05 18:31:59 2010 +0100
@@ -280,12 +280,17 @@
{
final Pathname pathname;
- if(first instanceof Pathname) {
+ if (first instanceof Pathname) {
pathname = (Pathname) first;
}
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;
diff -r 884fd6b1beec src/org/armedbear/lisp/Interpreter.java
--- a/src/org/armedbear/lisp/Interpreter.java Fri Feb 05 16:41:42 2010 +0100
+++ b/src/org/armedbear/lisp/Interpreter.java Fri Feb 05 18:31:59 2010 +0100
@@ -285,8 +285,8 @@
if (i + 1 < args.length) {
if (arg.equals("--load"))
Load.load(new Pathname(args[i + 1]),
- args[i + 1],
false, false, true);
+
else
Load.loadSystemFile(args[i + 1]);
++i;
diff -r 884fd6b1beec src/org/armedbear/lisp/Lisp.java
--- a/src/org/armedbear/lisp/Lisp.java Fri Feb 05 16:41:42 2010 +0100
+++ b/src/org/armedbear/lisp/Lisp.java Fri Feb 05 18:31:59 2010 +0100
@@ -1201,134 +1201,63 @@
LispThread.currentThread());
}
+ @Deprecated
public static final LispObject loadCompiledFunction(final String namestring)
-
{
- byte[] bytes = readFunctionBytes(namestring);
+ Pathname name = new Pathname(namestring);
+ byte[] bytes = readFunctionBytes(name);
if (bytes != null)
return loadClassBytes(bytes);
return null;
}
- public static final byte[] readFunctionBytes(final String namestring)
- {
- final LispThread thread = LispThread.currentThread();
- final boolean absolute = Utilities.isFilenameAbsolute(namestring);
- LispObject device = NIL;
- final Pathname defaultPathname;
- if (absolute)
- {
- defaultPathname =
- coerceToPathname(Symbol.DEFAULT_PATHNAME_DEFAULTS.symbolValue(thread));
+ public static final byte[] readFunctionBytes(final Pathname name) {
+ final LispThread thread = LispThread.currentThread();
+ Pathname load = null;
+ LispObject truenameFasl = Symbol.LOAD_TRUENAME_FASL.symbolValue(thread);
+ LispObject truename = Symbol.LOAD_TRUENAME.symbolValue(thread);
+ Pathname fasl = null;
+ if (truenameFasl instanceof Pathname) {
+ load = Pathname.mergePathnames(name, (Pathname)truenameFasl, Keyword.NEWEST);
+ } else if (truename instanceof Pathname) {
+ load = Pathname.mergePathnames(name, (Pathname) truename, Keyword.NEWEST);
+ } else {
+ load = name;
}
- else
- {
- LispObject loadTruename = Symbol.LOAD_TRUENAME.symbolValue(thread);
- if (loadTruename instanceof Pathname)
- {
- defaultPathname = (Pathname) loadTruename;
- // We're loading a file.
- device = ((Pathname)loadTruename).getDevice();
+ InputStream input = load.getInputStream();
+ byte[] bytes = new byte[4096];
+ try {
+ if (input == null) {
+ Debug.trace("Pathname: " + name);
+ Debug.trace("LOAD_TRUENAME_FASL: " + truenameFasl);
+ Debug.trace("LOAD_TRUENAME: " + truename);
+ Debug.assertTrue(input != null);
}
- else
- {
- defaultPathname =
- coerceToPathname(Symbol.DEFAULT_PATHNAME_DEFAULTS.symbolValue(thread));
+
+ int n = 0;
+ java.io.ByteArrayOutputStream baos = new java.io.ByteArrayOutputStream();
+ try {
+ while (n >= 0) {
+ n = input.read(bytes, 0, 4096);
+ if (n >= 0) {
+ baos.write(bytes, 0, n);
+ }
+ }
+ } catch (IOException e) {
+ Debug.trace("Failed to read bytes from "
+ + "'" + name.getNamestring() + "'");
+ return null;
+ }
+ bytes = baos.toByteArray();
+ } finally {
+ try {
+ input.close();
+ } catch (IOException e) {
+ Debug.trace("Failed to close InputStream: " + e);
}
}
- if (device instanceof Pathname) { //Loading from a jar
- URL url = null;
- String jar = ((Pathname)device).getNamestring();
- if(jar.startsWith("jar:")) {
- try {
- url = new URL(jar + "!/" + namestring);
- } catch (MalformedURLException ex) {
- Debug.trace(ex);
- }
- } else {
- url = Lisp.class.getResource(namestring);
- }
- if (url != null) {
- try {
- InputStream input = null;
- java.io.ByteArrayOutputStream baos = new java.io.ByteArrayOutputStream();
- try {
- input = url.openStream();
- byte[] bytes = new byte[4096];
- int n = 0;
- while (n >= 0) {
- n = input.read(bytes, 0, 4096);
- if(n >= 0) {
- baos.write(bytes, 0, n);
- }
- }
- bytes = baos.toByteArray();
- return bytes;
- } finally {
- baos.close();
- if(input != null) {
- input.close();
- }
- }
- } catch (IOException e) {
- Debug.trace(e);
- }
- }
- error(new LispError("Unable to load " + namestring));
- return null; // not reached
- }
- Pathname pathname = new Pathname(namestring);
- final File file = Utilities.getFile(pathname, defaultPathname);
- if (file != null && file.isFile())
- {
- // The .cls file exists.
- try
- {
- byte[] bytes = readFunctionBytes(new FileInputStream(file),
- (int) file.length());
- // FIXME close stream!
- if (bytes != null)
- return bytes;
- }
- catch (FileNotFoundException fnf) {
- error(new LispError("Unable to load " + pathname.writeToString()
- + ": " + fnf.getMessage()));
- return null; // not reached
- }
- return null; // not reached
- }
- try
- {
- LispObject loadTruename = Symbol.LOAD_TRUENAME.symbolValue(thread);
- String zipFileName = ((Pathname)loadTruename).getNamestring();
- ZipFile zipFile = ZipCache.getZip(zipFileName);
- try
- {
- ZipEntry entry = zipFile.getEntry(namestring);
- if (entry != null)
- {
- byte[] bytes = readFunctionBytes(zipFile.getInputStream(entry),
- (int) entry.getSize());
- if (bytes != null)
- return bytes;
- Debug.trace("Unable to load " + namestring);
- error(new LispError("Unable to load " + namestring));
- return null; // not reached
- }
- }
- finally
- {
- ZipCache.removeZip(zipFile.getName());
- }
- }
- catch (IOException t)
- {
- Debug.trace(t);
- }
- error(new FileError("File not found: " + namestring,
- new Pathname(namestring)));
- return null; // not reached
+ return bytes;
}
public static final Function makeCompiledFunctionFromClass(Class> c) {
@@ -2395,6 +2324,7 @@
Symbol.LOAD_PRINT.initializeSpecial(NIL);
Symbol.LOAD_PATHNAME.initializeSpecial(NIL);
Symbol.LOAD_TRUENAME.initializeSpecial(NIL);
+ Symbol.LOAD_TRUENAME_FASL.initializeSpecial(NIL);
Symbol.COMPILE_VERBOSE.initializeSpecial(T);
Symbol.COMPILE_PRINT.initializeSpecial(T);
Symbol._COMPILE_FILE_PATHNAME_.initializeSpecial(NIL);
@@ -2784,3 +2714,6 @@
}
}
+// Local Variables:
+// c-basic-offset: 4
+// End:
diff -r 884fd6b1beec src/org/armedbear/lisp/Load.java
--- a/src/org/armedbear/lisp/Load.java Fri Feb 05 16:41:42 2010 +0100
+++ b/src/org/armedbear/lisp/Load.java Fri Feb 05 18:31:59 2010 +0100
@@ -61,209 +61,144 @@
* The FASL loader takes over and retrieves the file being loaded
* from the special variable and continues loading from there.
*
- * Note: In order to prevent re-opening the ZIP file again and again,
- * ABCL keeps a cache of opened zip files, which are retrieved to load
- * .cls (compiled-function files) from the ZIP while loading the main
- * ._ file with FASL loading instructions.
*/
public final class Load
{
public static final LispObject load(String filename)
-
{
final LispThread thread = LispThread.currentThread();
return load(new Pathname(filename),
- filename,
Symbol.LOAD_VERBOSE.symbolValue(thread) != NIL,
Symbol.LOAD_PRINT.symbolValue(thread) != NIL,
true);
}
-
- private static final File findLoadableFile(final String filename,
- final String dir)
- {
- File file = new File(dir, filename);
- if (!file.isFile()) {
- String extension = getExtension(filename);
- if (extension == null) {
- // No extension specified. Try appending ".lisp" or ".abcl".
- File lispFile = new File(dir, filename.concat(".lisp"));
- File abclFile = new File(dir, filename.concat(".abcl"));
- if (lispFile.isFile() && abclFile.isFile()) {
- if (abclFile.lastModified() > lispFile.lastModified()) {
- return abclFile;
- } else {
- return lispFile;
- }
- } else if (abclFile.isFile()) {
- return abclFile;
- } else if (lispFile.isFile()) {
- return lispFile;
+
+ /** @return Pathname of loadable file based on NAME, or null if
+ * none can be determined. */
+ private static final Pathname findLoadableFile(Pathname name) {
+ LispObject truename = Pathname.truename(name, false);
+ if (truename instanceof Pathname) {
+ Pathname t = (Pathname)truename;
+ if (t.name != NIL
+ && t.name != null) {
+ return t;
+ }
+ }
+ if (name.type == NIL
+ && (name.name != NIL || name.name != null)) {
+ Pathname lispPathname = new Pathname(name);
+ lispPathname.type = new SimpleString("lisp");
+ lispPathname.invalidateNamestring();
+ LispObject lisp = Pathname.truename(lispPathname, false);
+ Pathname abclPathname = new Pathname(name);
+ abclPathname.type = new SimpleString("abcl");
+ abclPathname.invalidateNamestring();
+ LispObject abcl = Pathname.truename(abclPathname, false);
+ if (lisp instanceof Pathname && abcl instanceof Pathname) {
+ lispPathname = (Pathname)lisp;
+ abclPathname = (Pathname)abcl;
+ long lispLastModified = lispPathname.getLastModified();
+ long abclLastModified = abclPathname.getLastModified();
+ if (abclLastModified > lispLastModified) {
+ return lispPathname;
+ } else {
+ return abclPathname;
+ }
+ } else if (abcl instanceof Pathname) {
+ return (Pathname) abcl;
+ } else if (lisp instanceof Pathname) {
+ return (Pathname) lisp;
+ }
+ }
+ if (name.isJar()) {
+ if (name.type.equals(NIL)) {
+ name.type = COMPILE_FILE_INIT_FASL_TYPE;
+ name.invalidateNamestring();
+ Pathname result = findLoadableFile(name);
+ if (result != null) {
+ return result;
+ }
+ name.type = new SimpleString(COMPILE_FILE_TYPE);
+ name.invalidateNamestring();
+ result = findLoadableFile(name);
+ if (result != null) {
+ return result;
}
}
- } else
- return file; // the file exists
- return null; // this is the error case: the file does not exist
- // no need to check again at the caller
+ }
+ return null;
}
public static final LispObject load(Pathname pathname,
- String filename,
boolean verbose,
boolean print,
boolean ifDoesNotExist)
{
- return load(pathname, filename, verbose, print, ifDoesNotExist, false);
+ return load(pathname, verbose, print, ifDoesNotExist, false);
}
- public static final LispObject load(Pathname pathname,
- String filename,
+ public static final LispObject load(final Pathname pathname,
boolean verbose,
boolean print,
boolean ifDoesNotExist,
boolean returnLastResult)
{
- String dir = null;
- if (!Utilities.isFilenameAbsolute(filename)) {
- dir = coerceToPathname(Symbol.DEFAULT_PATHNAME_DEFAULTS
- .symbolValue()).getNamestring();
+ Pathname mergedPathname = null;
+ if (!pathname.isAbsolute() && !pathname.isJar()) {
+ Pathname pathnameDefaults
+ = coerceToPathname(Symbol.DEFAULT_PATHNAME_DEFAULTS.symbolValue());
+ mergedPathname = Pathname.mergePathnames(pathname, pathnameDefaults);
}
- String zipFileName = null;
- String zipEntryName = null;
- if (filename.startsWith("jar:file:")) {
- String s = new String(filename);
- s = s.substring(9);
- int index = s.lastIndexOf('!');
- if (index >= 0) {
- zipFileName = s.substring(0, index);
- zipEntryName = s.substring(index + 1);
- if (zipEntryName.length() > 0 && zipEntryName.charAt(0) == '/')
- zipEntryName = zipEntryName.substring(1);
- if (Utilities.isPlatformWindows) {
- if (zipFileName.length() > 0 && zipFileName.charAt(0) == '/')
- zipFileName = zipFileName.substring(1);
- }
+ Pathname truename = findLoadableFile(mergedPathname != null ? mergedPathname : pathname);
+
+ if (truename == null || truename.equals(NIL)) {
+ if (ifDoesNotExist) {
+ return error(new FileError("File not found: " + pathname));
+ } else {
+ Debug.trace("Failed to load " + pathname.getNamestring());
+ return NIL;
}
}
- File file = findLoadableFile(filename, dir);
- if (null == file && null == zipFileName) {
- if (ifDoesNotExist)
- return error(new FileError("File not found: " + filename, pathname));
- else
- return NIL;
- }
-
- if (checkZipFile(file)) {
- // Either we are loading a packed FASL (i.e. ZIP with suffix ".abcl")
- // Or we are loading from a JAR archive
- if (".abcl".equals(getExtension(file.getPath()))) {
- // So we adjust the value passed to
- // loadFileFromStream() to get any further loading
- // within this invocation of LOAD to work properly.
- filename = file.getPath();
- }
- zipFileName = file.getPath();
- zipEntryName = file.getName();
- }
+ if (truename.type.getStringValue().equals(COMPILE_FILE_TYPE)
+ && Utilities.checkZipFile(truename))
+ {
+ String n = truename.getNamestring();
+ if (n.startsWith("jar:")) {
+ n = "jar:" + n + "!/" + truename.name.getStringValue() + "."
+ + COMPILE_FILE_INIT_FASL_TYPE;
+ } else {
+ n = "jar:file:" + n + "!/" + truename.name.getStringValue() + "."
+ + COMPILE_FILE_INIT_FASL_TYPE;
+ }
+ mergedPathname = new Pathname(n);
+ LispObject initTruename = Pathname.truename(mergedPathname);
+ if (initTruename == null || initTruename.equals(NIL)) {
+ String errorMessage
+ = "Loadable FASL not found for"
+ + "'" + pathname + "'"
+ + " in "
+ + "'" + mergedPathname + "'";
+ if (ifDoesNotExist) {
+ return error(new FileError(errorMessage, mergedPathname));
+ } else {
+ Debug.trace(errorMessage);
+ return NIL;
+ }
+ }
+ truename = (Pathname)initTruename;
+ }
- String truename = filename;
- ZipFile zipfile = null;
-
- boolean packedFASL = false;
-
- InputStream in = null;
- if (zipFileName != null) {
- try {
- zipfile = ZipCache.getZip(zipFileName);
- }
- catch (IOException e) {
- return error (new FileError("Zip file not found: " + filename, pathname));
- }
- ZipEntry entry = zipfile.getEntry(zipEntryName);
- if (null == entry) {
- // try appending "._" to base filename
- int index = zipEntryName.lastIndexOf('.');
- if (-1 == index) index = zipEntryName.length();
- zipEntryName = zipEntryName.substring(0, index).concat("._");
- entry = zipfile.getEntry(zipEntryName);
- }
- if (null == entry) {
- // try appending ".abcl" to base filename
- int index = zipEntryName.lastIndexOf('.');
- if (index == -1)
- index = zipEntryName.length();
- zipEntryName = zipEntryName.substring(0, index).concat(".abcl");
- entry = zipfile.getEntry(zipEntryName);
- if (entry != null)
- packedFASL = true;
- }
- if (null == entry) {
- // Try looking for ".lisp"
- int i = zipEntryName.lastIndexOf('.');
- if (i == -1) {
- i = zipEntryName.length();
- }
- zipEntryName = zipEntryName.substring(0, i).concat(".lisp");
- entry = zipfile.getEntry(zipEntryName);
- if (entry == null) {
- return error(new LispError("Failed to find " + zipEntryName + " in "
- + zipFileName + "."));
- }
- }
-
- if (null == entry) {
- return error(new FileError("Can't find zip file entry "
- + zipEntryName, pathname));
- }
- if (".abcl".equals(getExtension(zipEntryName))) {
- packedFASL = true;
- }
- if (packedFASL) {
- // If we are loading a packed FASL from the JAR we
- // have to decompress it first, and seek for the '._'
- // init FASL.
- int i = zipEntryName.lastIndexOf('.');
- int j = zipEntryName.lastIndexOf('/');
- if(j >= i) {
- return error(new LispError("Invalid zip entry name: " + zipEntryName));
- }
- String subZipEntryName = zipEntryName.substring(j + 1, i).concat("._");
- in = Utilities.getZippedZipEntryAsInputStream(zipfile,
- zipEntryName,
- subZipEntryName);
- } else {
- try {
- in = zipfile.getInputStream(entry);
- }
- catch (IOException e) {
- return error(new LispError(e.getMessage()));
- }
- }
- } else {
- try {
- in = new FileInputStream(file);
- truename = file.getCanonicalPath();
- }
- catch (FileNotFoundException e) {
- if (ifDoesNotExist)
- return error(new FileError("File not found: " + filename,
- pathname));
- else
- return NIL;
- }
- catch (IOException e) {
- return error(new LispError(e.getMessage()));
- }
- }
+ InputStream in = truename.getInputStream();
+ Debug.assertTrue(in != null);
+
try {
-
- return loadFileFromStream(null, truename,
- new Stream(Symbol.SYSTEM_STREAM, in, Symbol.CHARACTER),
- verbose, print, false, returnLastResult);
+ return loadFileFromStream(pathname, truename,
+ new Stream(Symbol.SYSTEM_STREAM, in, Symbol.CHARACTER),
+ verbose, print, false, returnLastResult);
}
catch (FaslVersionMismatch e) {
FastStringBuffer sb =
@@ -280,14 +215,6 @@
return error(new LispError(e.getMessage()));
}
}
- if (zipfile != null) {
- try {
- ZipCache.removeZip(zipfile.getName());
- }
- catch (IOException e) {
- return error(new LispError(e.getMessage()));
- }
- }
}
}
@@ -327,124 +254,86 @@
}
}
+ static final LispObject COMPILE_FILE_INIT_FASL_TYPE = new SimpleString("_");
+
public static final LispObject loadSystemFile(final String filename,
boolean verbose,
boolean print,
boolean auto)
{
- final int ARRAY_SIZE = 2;
- String[] candidates = new String[ARRAY_SIZE];
- final String extension = getExtension(filename);
- if (extension == null) {
- // No extension specified.
- candidates[0] = filename + '.' + COMPILE_FILE_TYPE;
- candidates[1] = filename.concat(".lisp");
- } else if (extension.equals(".abcl")) {
- candidates[0] = filename;
- candidates[1] =
- filename.substring(0, filename.length() - 5).concat(".lisp");
- } else
- candidates[0] = filename;
InputStream in = null;
Pathname pathname = null;
- String truename = null;
- for (int i = 0; i < ARRAY_SIZE; i++) {
- String s = candidates[i];
- if (s == null)
- break;
- ZipFile zipfile = null;
- final String dir = Site.getLispHome();
- try {
- if (dir != null) {
- File file = new File(dir, s);
- if (file.isFile()) {
- // File exists. For system files, we know the extension
- // will be .abcl if it is a compiled file.
- String ext = getExtension(s);
- if (ext.equalsIgnoreCase(".abcl")) {
- try {
- zipfile = ZipCache.getZip(file.getPath());
- String name = file.getName();
- int index = name.lastIndexOf('.');
- Debug.assertTrue(index >= 0);
- name = name.substring(0, index).concat("._");
- ZipEntry entry = zipfile.getEntry(name);
- if (entry != null) {
- in = zipfile.getInputStream(entry);
- truename = file.getCanonicalPath();
- }
- }
- catch (ZipException e) {
- // Fall through.
- }
- catch (IOException e) {
- // fall through
- }
- }
- if (in == null) {
- try {
- in = new FileInputStream(file);
- truename = file.getCanonicalPath();
- }
- catch (IOException e) {
- in = null;
- }
- }
- }
- } else {
- URL url = Lisp.class.getResource(s);
- if (url != null) {
- try {
- in = url.openStream();
- if ("jar".equals(url.getProtocol()) &&
- url.getPath().startsWith("file:"))
- pathname = new Pathname(url);
- truename = getPath(url);
- }
- catch (IOException e) {
- in = null;
- }
- }
- }
- if (in != null) {
- final LispThread thread = LispThread.currentThread();
- final SpecialBindingsMark mark = thread.markSpecialBindings();
- thread.bindSpecial(_WARN_ON_REDEFINITION_, NIL);
- try {
- return loadFileFromStream(pathname, truename,
- new Stream(Symbol.SYSTEM_STREAM, in, Symbol.CHARACTER),
- verbose, print, auto);
- }
- catch (FaslVersionMismatch e) {
- FastStringBuffer sb =
- new FastStringBuffer("; Incorrect fasl version: ");
- sb.append(truename);
- System.err.println(sb.toString());
- }
- finally {
- thread.resetSpecialBindings(mark);
- try {
- in.close();
- }
- catch (IOException e) {
- return error(new LispError(e.getMessage()));
- }
- }
+ Pathname truename = null;
+ pathname = new Pathname(filename);
+ Pathname mergedPathname = Pathname.mergePathnames(pathname, Site.getLispHome());
+ truename = findLoadableFile(mergedPathname);
+ if (truename == null || truename.equals(NIL)) {
+ // Make an attempt to use the boot classpath
+ String path = pathname.asEntryPath();
+ URL url = Lisp.class.getResource(path);
+ if (url == null || url.toString().endsWith("/")) {
+ url = Lisp.class.getResource(path + ".abcl");
+ if (url == null) {
+ url = Lisp.class.getResource(path + ".lisp");
}
}
- finally {
- if (zipfile != null) {
- try {
- ZipCache.removeZip(zipfile.getName());
- }
- catch (IOException e) {
- return error(new LispError(e.getMessage()));
- }
+ if (url == null) {
+ return error(new LispError("Failed to find loadable system file "
+ + "'" + path + "'"
+ + " in boot classpath."));
+ }
+ Pathname urlPathname = new Pathname(url);
+ truename = findLoadableFile(urlPathname);
+ if (truename == null) {
+ return error(new LispError("Failed to find loadable system file in boot classpath "
+ + "'" + url + "'"));
+ }
+ }
+
+ // Look for a init FASL inside a packed FASL
+ if (truename.type.writeToString().equals(COMPILE_FILE_TYPE) && Utilities.checkZipFile(truename)) {
+ Pathname init = new Pathname(truename.getNamestring());
+ init.type = COMPILE_FILE_INIT_FASL_TYPE;
+ LispObject t = Pathname.truename(init);
+ if (t instanceof Pathname) {
+ truename = (Pathname)t;
+ } else {
+ return error (new LispError("Failed to find loadable init FASL in "
+ + "'" + init.getNamestring() + "'"));
+ }
+ }
+
+ in = truename.getInputStream();
+
+ if (in != null) {
+ final LispThread thread = LispThread.currentThread();
+ final SpecialBindingsMark mark = thread.markSpecialBindings();
+ thread.bindSpecial(_WARN_ON_REDEFINITION_, NIL);
+ try {
+ Stream stream = new Stream(Symbol.SYSTEM_STREAM, in, Symbol.CHARACTER);
+ return loadFileFromStream(pathname, truename, stream,
+ verbose, print, auto);
+ } catch (FaslVersionMismatch e) {
+ FastStringBuffer sb =
+ new FastStringBuffer("; Incorrect fasl version: ");
+ sb.append(truename);
+ System.err.println(sb.toString());
+ } finally {
+ thread.resetSpecialBindings(mark);
+ try {
+ in.close();
+ }
+ catch (IOException e) {
+ return error(new LispError(e.getMessage()));
}
}
}
- return error(new LispError("File not found: " + filename));
+ return error(new FileError("Failed to load system file: "
+ + "'" + filename + "'"
+ + " resolved as "
+ + "'" + mergedPathname + "'" ,
+ truename));
}
// ### *fasl-version*
@@ -489,8 +378,8 @@
}
};
- private static final LispObject loadFileFromStream(LispObject pathname,
- String truename,
+ private static final LispObject loadFileFromStream(Pathname pathname,
+ Pathname truename,
Stream in,
boolean verbose,
boolean print,
@@ -499,8 +388,9 @@
return loadFileFromStream(pathname, truename, in, verbose, print, auto, false);
}
+ // A nil TRUENAME signals a load from stream which has no possible path
private static final LispObject loadFileFromStream(LispObject pathname,
- String truename,
+ LispObject truename,
Stream in,
boolean verbose,
boolean print,
@@ -525,12 +415,35 @@
thread.bindSpecialToCurrentValue(_EXPLAIN_);
final String prefix = getLoadVerbosePrefix(loadDepth);
try {
- if (pathname == null && truename != null)
- pathname = Pathname.parseNamestring(truename);
- thread.bindSpecial(Symbol.LOAD_PATHNAME,
- pathname != null ? pathname : NIL);
- thread.bindSpecial(Symbol.LOAD_TRUENAME,
- pathname != null ? pathname : NIL);
+ thread.bindSpecial(Symbol.LOAD_PATHNAME, pathname);
+ Pathname truePathname = new Pathname(((Pathname)truename).getNamestring());
+ 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);
+ }
+ if (truePathname.type.getStringValue().equals(COMPILE_FILE_INIT_FASL_TYPE.getStringValue())
+ && truePathname.isJar()) {
+ if (truePathname.device.cdr() != NIL ) {
+ // set truename to the enclosing JAR
+ truePathname.host = NIL;
+ truePathname.directory = NIL;
+ truePathname.name = NIL;
+ truePathname.type = NIL;
+ truePathname.invalidateNamestring();
+ } else {
+ // XXX There is something fishy in the asymmetry
+ // between the "jar:jar:http:" and "jar:jar:file:"
+ // cases but this currently passes the tests.
+ if (!(truePathname.device.car() instanceof AbstractString)) {
+ truePathname = (Pathname)truePathname.device.car();
+ truePathname.invalidateNamestring();
+ }
+ }
+ thread.bindSpecial(Symbol.LOAD_TRUENAME, truePathname);
+ } else {
+ thread.bindSpecial(Symbol.LOAD_TRUENAME, truename);
+ }
thread.bindSpecial(_SOURCE_,
pathname != null ? pathname : NIL);
if (verbose) {
@@ -538,7 +451,7 @@
out.freshLine();
out._writeString(prefix);
out._writeString(auto ? " Autoloading " : " Loading ");
- out._writeString(truename != null ? truename : "stream");
+ out._writeString(!truename.equals(NIL) ? truePathname.writeToString() : "stream");
out._writeLine(" ...");
out._finishOutput();
LispObject result = loadStream(in, print, thread, returnLastResult);
@@ -546,7 +459,7 @@
out.freshLine();
out._writeString(prefix);
out._writeString(auto ? " Autoloaded " : " Loaded ");
- out._writeString(truename != null ? truename : "stream");
+ out._writeString(!truename.equals(NIL) ? truePathname.writeToString() : "stream");
out._writeString(" (");
out._writeString(String.valueOf(((float)elapsed)/1000));
out._writeLine(" seconds)");
@@ -610,7 +523,6 @@
}
private static final LispObject faslLoadStream(LispThread thread)
-
{
Stream in = (Stream) _LOAD_STREAM_.symbolValue(thread);
final Environment env = new Environment();
@@ -638,65 +550,6 @@
//whether to return T or the last value.
}
- // Returns extension including leading '.'
- private static final String getExtension(String filename)
- {
- int index = filename.lastIndexOf('.');
- if (index < 0)
- return null;
- if (index < filename.lastIndexOf(File.separatorChar))
- return null; // Last dot was in path part of filename.
- return filename.substring(index);
- }
-
- private static final String getPath(URL url)
- {
- if (url != null) {
- String path;
- try {
- path = URLDecoder.decode(url.getPath(),"UTF-8");
- }
- catch (java.io.UnsupportedEncodingException uee) {
- // Can't happen: every Java is supposed to support
- // at least UTF-8 encoding
- path = null;
- }
- if (path != null) {
- if (Utilities.isPlatformWindows) {
- if (path.length() > 0 && path.charAt(0) == '/')
- path = path.substring(1);
- }
- return path;
- }
- }
- return null;
- }
-
- private static final boolean checkZipFile(File file)
- {
- InputStream in = null;
- try {
- in = new FileInputStream(file);
- byte[] bytes = new byte[4];
- int bytesRead = in.read(bytes);
- return (bytesRead == 4
- && bytes[0] == 0x50
- && bytes[1] == 0x4b
- && bytes[2] == 0x03
- && bytes[3] == 0x04);
- }
- catch (Throwable t) { // any error probably means 'no'
- return false;
- }
- finally {
- if (in != null) {
- try {
- in.close();
- }
- catch (IOException e) {} // ignore exceptions
- }
- }
- }
// ### %load filespec verbose print if-does-not-exist => generalized-boolean
private static final Primitive _LOAD =
@@ -737,11 +590,11 @@
pathname = ((FileStream)filespec).getPathname();
else
pathname = NIL;
- String truename;
+ LispObject truename;
if (pathname instanceof Pathname)
- truename = ((Pathname)pathname).getNamestring();
+ truename = pathname;
else
- truename = null;
+ truename = NIL;
return loadFileFromStream(pathname,
truename,
(Stream) filespec,
@@ -756,7 +609,6 @@
if (pathname instanceof LogicalPathname)
pathname = LogicalPathname.translateLogicalPathname((LogicalPathname)pathname);
return load(pathname,
- pathname.getNamestring(),
verbose != NIL,
print != NIL,
ifDoesNotExist != NIL,
diff -r 884fd6b1beec src/org/armedbear/lisp/LogicalPathname.java
--- a/src/org/armedbear/lisp/LogicalPathname.java Fri Feb 05 16:41:42 2010 +0100
+++ b/src/org/armedbear/lisp/LogicalPathname.java Fri Feb 05 18:31:59 2010 +0100
@@ -45,10 +45,14 @@
private static final HashMap map = new HashMap();
- public LogicalPathname()
+ protected LogicalPathname()
{
}
+ protected LogicalPathname(Pathname p) {
+ super(p);
+ }
+
public LogicalPathname(String host, String rest)
{
final int limit = rest.length();
diff -r 884fd6b1beec src/org/armedbear/lisp/Pathname.java
--- a/src/org/armedbear/lisp/Pathname.java Fri Feb 05 16:41:42 2010 +0100
+++ b/src/org/armedbear/lisp/Pathname.java Fri Feb 05 18:31:59 2010 +0100
@@ -30,93 +30,169 @@
* 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.IOException;
+import java.io.InputStream;
+import java.io.FileInputStream;
+import java.net.JarURLConnection;
+import java.net.MalformedURLException;
import java.net.URL;
+import java.net.URLConnection;
import java.net.URLDecoder;
import java.util.StringTokenizer;
+import java.util.jar.JarEntry;
+import java.util.jar.JarFile;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipInputStream;
-public class Pathname extends LispObject
-{
+public class Pathname extends LispObject {
+
protected LispObject host = NIL;
protected LispObject device = NIL;
protected LispObject directory = NIL;
protected LispObject name = NIL;
-
// A string, NIL, :WILD or :UNSPECIFIC.
protected LispObject type = NIL;
-
// A positive integer, or NIL, :WILD, :UNSPECIFIC, or :NEWEST.
protected LispObject version = NIL;
private String namestring;
- protected Pathname()
- {
+ /** The protocol for changing any instance field (i.e. 'host', 'type', etc.)
+ * is to call this method after changing the field to recompute the namestring.
+ * We could do this with setter/getters, but that choose not to in order to avoid the
+ * performance indirection penalty.
+ */
+ public void invalidateNamestring() {
+ namestring = null;
}
- public Pathname(String s)
- {
+ protected Pathname() {}
+
+ /** Copy constructor that shares no structure with the original. */
+ protected Pathname(Pathname p) {
+ if (p.host != NIL) {
+ if (p.host instanceof SimpleString) {
+ host = new SimpleString(((SimpleString)p.host).getStringValue());
+ } else if (p.host instanceof Symbol) {
+ host = p.host;
+ } else {
+ Debug.assertTrue(false);
+ }
+ }
+ if (p.device != NIL) {
+ if (p.device instanceof SimpleString) {
+ device = new SimpleString(((SimpleString)p.device).getStringValue());
+ } else if (p.device instanceof Cons) {
+ Cons jars = (Cons)p.device;
+ device = new Cons(NIL, NIL);
+ LispObject first = jars.car();
+ if (first instanceof SimpleString) {
+ ((Cons)device).car = new SimpleString(((SimpleString)first).getStringValue());
+ } else if (first instanceof Pathname) {
+ ((Cons)device).car = new Pathname((Pathname)first);
+ } else {
+ Debug.assertTrue(false);
+ }
+ if (!jars.cdr().equals(NIL)) {
+ if (jars.cdr() instanceof Cons) {
+ ((Cons)device).cdr = new Cons(new Pathname((Pathname)jars.cdr().car()), NIL);
+ } else {
+ Debug.assertTrue(false);
+ }
+ }
+ } else if (p.device instanceof Symbol) {
+ device = p.device;
+ } else {
+ Debug.assertTrue(false);
+ }
+ }
+ if (p.directory != NIL) {
+ if (p.directory instanceof Cons) {
+ directory = NIL;
+ for (LispObject list = p.directory; list != NIL; list = list.cdr()) {
+ LispObject o = list.car();
+ if (o instanceof Symbol) {
+ directory = directory.push(o);
+ } else if (o instanceof SimpleString) {
+ directory = directory.push(new SimpleString(((SimpleString)o).getStringValue()));
+ } else {
+ Debug.assertTrue(false);
+ }
+ }
+ directory.nreverse();
+ } else {
+ Debug.assertTrue(false);
+ }
+ }
+ if (p.name != NIL) {
+ if (p.name instanceof SimpleString) {
+ name = new SimpleString(((SimpleString)p.name).getStringValue());
+ } else if (p.name instanceof Symbol) {
+ name = p.name;
+ } else {
+ Debug.assertTrue(false);
+ }
+ }
+ if (p.type != NIL) {
+ if (p.type instanceof SimpleString) {
+ type = new SimpleString(((SimpleString)p.type).getStringValue());
+ } else if (p.type instanceof Symbol) {
+ type = p.type;
+ } else {
+ Debug.assertTrue(false);
+ }
+ }
+ }
+
+ public Pathname(String s) {
init(s);
}
- public Pathname(URL url)
- {
+ public Pathname(URL url) {
String protocol = url.getProtocol();
if ("jar".equals(protocol)) {
- String s;
- try {
- s = URLDecoder.decode(url.getPath(),"UTF-8");
- }
- catch (java.io.UnsupportedEncodingException uee) {
- // Can't happen: every Java is supposed to support
- // at least UTF-8 encoding
- s = null;
- }
- if (s.startsWith("file:")) {
- int index = s.indexOf("!/");
- String container = s.substring(5, index);
- if (Utilities.isPlatformWindows) {
- if (container.length() > 0 && container.charAt(0) == '/')
- container = container.substring(1);
- }
- device = new Pathname(container);
- s = s.substring(index + 1);
- Pathname p = new Pathname(s);
- directory = p.directory;
- name = p.name;
- type = p.type;
- return;
- }
+ init(url.toString());
+ return;
} else if ("file".equals(protocol)) {
String s;
try {
- s = URLDecoder.decode(url.getPath(),"UTF-8");
- }
- catch (java.io.UnsupportedEncodingException uee) {
+ s = URLDecoder.decode(url.getPath(), "UTF-8");
+ } catch (java.io.UnsupportedEncodingException uee) {
// Can't happen: every Java is supposed to support
// at least UTF-8 encoding
+ Debug.assertTrue(false);
s = null;
}
- if (s != null && s.startsWith("file:")) {
- init(s.substring(5));
+ if (s != null) {
+ if (Utilities.isPlatformWindows) {
+ // Workaround bug in Java's idea of URLs
+ // new (URL"file:///c:/a/b").getPath() --> "/c:/a/b"
+ // whereas we need "c" to be the DEVICE.
+ if (s.length() > 2
+ && s.charAt(0) == '/'
+ && s.charAt(2) == ':') {
+ s = s.substring(1);
+ }
+ }
+ init(s);
return;
}
}
- error(new LispError("Unsupported URL: \"" + url.toString() + '"'));
+ error(new LispError("Unsupported URL: '" + url.toString() + "'"));
}
- private final void init(String s)
- {
- if (s == null)
+ static final private String jarSeparator = "!/";
+ private final void init(String s) {
+ if (s == null) {
return;
- if (s.equals(".") || s.equals("./") ||
- (Utilities.isPlatformWindows && s.equals(".\\"))) {
+ }
+ if (s.equals(".") || s.equals("./")
+ || (Utilities.isPlatformWindows && s.equals(".\\"))) {
directory = new Cons(Keyword.RELATIVE);
return;
}
@@ -126,50 +202,130 @@
}
if (Utilities.isPlatformWindows) {
if (s.startsWith("\\\\")) {
- //UNC path support
- // match \\\\[directories-and-files]
+ //UNC path support
+ // match \\\\[directories-and-files]
- int shareIndex = s.indexOf('\\', 2);
- int dirIndex = s.indexOf('\\', shareIndex + 1);
+ int shareIndex = s.indexOf('\\', 2);
+ int dirIndex = s.indexOf('\\', shareIndex + 1);
- if (shareIndex == -1 || dirIndex == -1)
- error(new LispError("Unsupported UNC path format: \"" + s + '"'));
+ if (shareIndex == -1 || dirIndex == -1) {
+ error(new LispError("Unsupported UNC path format: \"" + s + '"'));
+ }
- host = new SimpleString(s.substring(2, shareIndex));
- device = new SimpleString(s.substring(shareIndex + 1, dirIndex));
+ host = new SimpleString(s.substring(2, shareIndex));
+ device = new SimpleString(s.substring(shareIndex + 1, dirIndex));
- Pathname p = new Pathname(s.substring(dirIndex));
- directory = p.directory;
- name = p.name;
- type = p.type;
- version = p.version;
- return;
+ Pathname p = new Pathname(s.substring(dirIndex));
+ directory = p.directory;
+ name = p.name;
+ type = p.type;
+ version = p.version;
+ return;
}
+ }
- s = s.replace('/', '\\');
- }
- // Jar file support.
- int bang = s.indexOf("!/");
- if (bang >= 0) {
- Pathname container = new Pathname(s.substring(0, bang));
- LispObject containerType = container.type;
- if (containerType instanceof AbstractString) {
- if (containerType.getStringValue().equalsIgnoreCase("jar")) {
- device = container;
- s = s.substring(bang + 1);
- Pathname p = new Pathname(s);
- directory = p.directory;
- name = p.name;
- type = p.type;
- return;
+ // A JAR file
+ if (s.startsWith("jar:") && s.endsWith(jarSeparator)) {
+ LispObject jars = NIL;
+ int i = s.lastIndexOf(jarSeparator, s.length() - jarSeparator.length() - 1);
+ String jar = null;
+ if (i == -1) {
+ jar = s;
+ } else {
+ // There can be no more than two jar references and the
+ // inner one must be a file reference within the outer.
+ jar = "jar:file:" + s.substring(i + jarSeparator.length());
+ s = s.substring("jar:".length(), i + jarSeparator.length());
+ Pathname p = new Pathname(s);
+ LispObject first = ((Cons) p.device).car();
+ if (first instanceof AbstractString) {
+ jars = jars.push(first);
+ } else {
+ jars = jars.push(p.device.car());
}
}
+ if (jar.startsWith("jar:file:")) {
+ String jarString = jar.substring("jar:".length(),
+ jar.length() - jarSeparator.length());
+ // Use URL constructor to normalize Windows' use of device
+ URL url = null;
+ try {
+ url = new URL(jarString);
+ } catch (MalformedURLException e) {
+ error(new LispError("Failed to parse '" + jarString + "'"
+ + " as URL:"
+ + e.getMessage()));
+ }
+ Pathname jarPathname = new Pathname(url);
+ if (jarString.endsWith(jarSeparator)) {
+ jars = jars.push(jarPathname.device);
+ } else {
+ jars = jars.push(jarPathname);
+ }
+ } else {
+ URL url = null;
+ try {
+ url = new URL(jar.substring("jar:".length(), jar.length() - 2));
+ jars = jars.push(new SimpleString(url.toString()));
+ } catch (MalformedURLException e) {
+ error(new LispError("Failed to parse url '" + url + "'"
+ + e.getMessage()));
+ }
+ }
+ jars = jars.nreverse();
+ device = jars;
+ return;
}
+
+ // An entry in a JAR file
+ final int separatorIndex = s.lastIndexOf(jarSeparator);
+ if (separatorIndex > 0 && s.startsWith("jar:")) {
+ final String jarURL = s.substring(0, separatorIndex + jarSeparator.length());
+ Pathname d = new Pathname(jarURL);
+ if (device instanceof Cons) {
+ LispObject[] jars = d.copyToArray();
+ // XXX Is this ever reached? If so, need to append lists
+ Debug.assertTrue(false);
+ } else {
+ device = d.device;
+ }
+ s = s.substring(separatorIndex + jarSeparator.length());
+ Pathname p = new Pathname(s);
+ directory = p.directory;
+ name = p.name;
+ type = p.type;
+ version = p.version;
+ return;
+ }
+
+ if (Utilities.isPlatformWindows) {
+ if (!s.contains(jarSeparator)) {
+ s = s.replace("/", "\\");
+ } else {
+ StringBuilder result = new StringBuilder();
+ for (int i = 0; i < s.length(); i++) {
+ char c = s.charAt(i);
+ if ( c != '/') {
+ result.append(c);
+ } else {
+ if (i != 0 && s.charAt(i-1) != '!') {
+ result.append("\\");
+ } else {
+ result.append(c);
+ }
+ }
+ }
+ s = result.toString();
+ }
+ }
+
+ // Expand user home directories
if (Utilities.isPlatformUnix) {
- if (s.equals("~"))
+ if (s.equals("~")) {
s = System.getProperty("user.home").concat("/");
- else if (s.startsWith("~/"))
+ } else if (s.startsWith("~/")) {
s = System.getProperty("user.home").concat(s.substring(1));
+ }
}
namestring = s;
if (Utilities.isPlatformWindows) {
@@ -215,56 +371,59 @@
if (index > 0) {
n = s.substring(0, index);
t = s.substring(index + 1);
- } else if (s.length() > 0)
+ } else if (s.length() > 0) {
n = s;
+ }
if (n != null) {
- if (n.equals("*"))
+ if (n.equals("*")) {
name = Keyword.WILD;
- else
+ } else {
name = new SimpleString(n);
+ }
}
if (t != null) {
- if (t.equals("*"))
+ if (t.equals("*")) {
type = Keyword.WILD;
- else
+ } else {
type = new SimpleString(t);
+ }
}
}
- private static final LispObject parseDirectory(String d)
-
- {
- if (d.equals("/") || (Utilities.isPlatformWindows && d.equals("\\")))
+ private static final LispObject parseDirectory(String d) {
+ if (d.equals("/") || (Utilities.isPlatformWindows && d.equals("\\"))) {
return new Cons(Keyword.ABSOLUTE);
+ }
LispObject result;
- if (d.startsWith("/") || (Utilities.isPlatformWindows && d.startsWith("\\")))
+ if (d.startsWith("/") || (Utilities.isPlatformWindows && d.startsWith("\\"))) {
result = new Cons(Keyword.ABSOLUTE);
- else
+ } else {
result = new Cons(Keyword.RELATIVE);
+ }
StringTokenizer st = new StringTokenizer(d, "/\\");
while (st.hasMoreTokens()) {
String token = st.nextToken();
LispObject obj;
- if (token.equals("*"))
+ if (token.equals("*")) {
obj = Keyword.WILD;
- else if (token.equals("**"))
+ } else if (token.equals("**")) {
obj = Keyword.WILD_INFERIORS;
- else if (token.equals("..")) {
+ } else if (token.equals("..")) {
if (result.car() instanceof AbstractString) {
result = result.cdr();
continue;
}
- obj= Keyword.UP;
- } else
+ obj = Keyword.UP;
+ } else {
obj = new SimpleString(token);
+ }
result = new Cons(obj, result);
}
return result.nreverse();
}
@Override
- public LispObject getParts()
- {
+ public LispObject getParts() {
LispObject parts = NIL;
parts = parts.push(new Cons("HOST", host));
parts = parts.push(new Cons("DEVICE", device));
@@ -276,42 +435,41 @@
}
@Override
- public LispObject typeOf()
- {
+ public LispObject typeOf() {
return Symbol.PATHNAME;
}
@Override
- public LispObject classOf()
- {
+ public LispObject classOf() {
return BuiltInClass.PATHNAME;
}
@Override
- public LispObject typep(LispObject type)
- {
- if (type == Symbol.PATHNAME)
+ public LispObject typep(LispObject type) {
+ if (type == Symbol.PATHNAME) {
return T;
- if (type == BuiltInClass.PATHNAME)
+ }
+ if (type == BuiltInClass.PATHNAME) {
return T;
+ }
return super.typep(type);
}
- public final LispObject getDevice()
- {
+ public final LispObject getDevice() {
return device;
}
- public String getNamestring()
- {
- if (namestring != null)
+ public String getNamestring() {
+ if (namestring != null) {
return namestring;
+ }
if (name == NIL && type != NIL) {
Debug.assertTrue(namestring == null);
return null;
}
- if (directory instanceof AbstractString)
+ if (directory instanceof AbstractString) {
Debug.assertTrue(false);
+ }
FastStringBuffer sb = new FastStringBuffer();
// "If a pathname is converted to a namestring, the symbols NIL and
// :UNSPECIFIC cause the field to be treated as if it were empty. That
@@ -319,26 +477,51 @@
// the namestring." 19.2.2.2.3.1
if (host != NIL) {
Debug.assertTrue(host instanceof AbstractString);
- if (! (this instanceof LogicalPathname))
- sb.append("\\\\"); //UNC file support; if there's a host, it's a UNC path.
+ if (!(this instanceof LogicalPathname)) {
+ sb.append("\\\\"); //UNC file support; if there's a host, it's a UNC path.
+ }
sb.append(host.getStringValue());
- if (this instanceof LogicalPathname)
- sb.append(':');
- else
- sb.append(File.separatorChar);
+ if (this instanceof LogicalPathname) {
+ sb.append(':');
+ } else {
+ sb.append(File.separatorChar);
+ }
}
if (device == NIL) {
} else if (device == Keyword.UNSPECIFIC) {
+ } else if (device instanceof Cons) {
+ LispObject[] jars = ((Cons) device).copyToArray();
+ int i = 0;
+ if (jars[0] instanceof AbstractString) {
+ sb.append("jar:");
+ sb.append(((AbstractString) jars[0]).getStringValue());
+ sb.append("!/");
+ i = 1;
+ }
+ FastStringBuffer prefix = new FastStringBuffer();
+ for (; i < jars.length; i++) {
+ prefix.append("jar:");
+ if (i == 0) {
+ sb.append("file:");
+ }
+ if (jars[i] instanceof Pathname) {
+ sb.append(((Pathname) jars[i]).getNamestring());
+ }
+ sb.append("!/");
+ }
+ sb = prefix.append(sb);
+ } else if (device instanceof AbstractString
+ && device.getStringValue().startsWith("jar:")) {
+ sb.append(device.getStringValue());
} else if (device instanceof AbstractString) {
sb.append(device.getStringValue());
if (this instanceof LogicalPathname
- || host == NIL)
- sb.append(':'); // non-UNC paths
- } else if (device instanceof Pathname) {
- sb.append(((Pathname)device).getNamestring());
- sb.append("!");
- } else
+ || host == NIL) {
+ sb.append(':'); // non-UNC paths
+ }
+ } else {
Debug.assertTrue(false);
+ }
sb.append(getDirectoryNamestring());
if (name instanceof AbstractString) {
String n = name.getStringValue();
@@ -347,8 +530,9 @@
return null;
}
sb.append(n);
- } else if (name == Keyword.WILD)
+ } else if (name == Keyword.WILD) {
sb.append('*');
+ }
if (type != NIL) {
sb.append('.');
if (type instanceof AbstractString) {
@@ -358,19 +542,21 @@
return null;
}
sb.append(t);
- } else if (type == Keyword.WILD)
+ } else if (type == Keyword.WILD) {
sb.append('*');
- else
+ } else {
Debug.assertTrue(false);
+ }
}
if (this instanceof LogicalPathname) {
if (version.integerp()) {
sb.append('.');
int base = Fixnum.getValue(Symbol.PRINT_BASE.symbolValue());
- if (version instanceof Fixnum)
- sb.append(Integer.toString(((Fixnum)version).value, base).toUpperCase());
- else if (version instanceof Bignum)
- sb.append(((Bignum)version).value.toString(base).toUpperCase());
+ if (version instanceof Fixnum) {
+ sb.append(Integer.toString(((Fixnum) version).value, base).toUpperCase());
+ } else if (version instanceof Bignum) {
+ sb.append(((Bignum) version).value.toString(base).toUpperCase());
+ }
} else if (version == Keyword.WILD) {
sb.append(".*");
} else if (version == Keyword.NEWEST) {
@@ -380,8 +566,7 @@
return namestring = sb.toString();
}
- protected String getDirectoryNamestring()
- {
+ protected String getDirectoryNamestring() {
validateDirectory(true);
FastStringBuffer sb = new FastStringBuffer();
// "If a pathname is converted to a namestring, the symbols NIL and
@@ -390,10 +575,11 @@
// the namestring." 19.2.2.2.3.1
if (directory != NIL) {
final char separatorChar;
- if (device instanceof Pathname)
+ if (device instanceof Cons) {
separatorChar = '/'; // Jar file.
- else
+ } else {
separatorChar = File.separatorChar;
+ }
LispObject temp = directory;
LispObject part = temp.car();
temp = temp.cdr();
@@ -407,23 +593,24 @@
}
// else: Nothing to do.
} else {
- error(new FileError("Unsupported directory component " +
- part.writeToString() + ".",
- this));
+ error(new FileError("Unsupported directory component "
+ + part.writeToString() + ".",
+ this));
}
while (temp != NIL) {
part = temp.car();
- if (part instanceof AbstractString)
+ if (part instanceof AbstractString) {
sb.append(part.getStringValue());
- else if (part == Keyword.WILD)
+ } else if (part == Keyword.WILD) {
sb.append('*');
- else if (part == Keyword.WILD_INFERIORS)
+ } else if (part == Keyword.WILD_INFERIORS) {
sb.append("**");
- else if (part == Keyword.UP)
+ } else if (part == Keyword.UP) {
sb.append("..");
- else
+ } else {
error(new FileError("Unsupported directory component " + part.writeToString() + ".",
- this));
+ this));
+ }
sb.append(separatorChar);
temp = temp.cdr();
}
@@ -431,39 +618,71 @@
return sb.toString();
}
+ /** @return The representation of this pathname suitable for referencing an entry in a Zip/JAR file */
+ protected String asEntryPath() {
+ Pathname p = new Pathname();
+ p.directory = directory;
+ p.name = name;
+ p.type = type;
+ String path = p.getNamestring();
+ if (Utilities.isPlatformWindows) {
+ StringBuilder result = new StringBuilder();
+ for (int i = 0; i < path.length(); i++) {
+ char c = path.charAt(i);
+ if (c == '\\') {
+ result.append('/');
+ } else {
+ result.append(c);
+ }
+ }
+ return result.toString();
+ }
+ return path;
+ }
+
@Override
- public boolean equal(LispObject obj)
- {
- if (this == obj)
+ public boolean equal(LispObject obj) {
+ if (this == obj) {
return true;
+ }
if (obj instanceof Pathname) {
Pathname p = (Pathname) obj;
if (Utilities.isPlatformWindows) {
- if (!host.equalp(p.host))
+ if (!host.equalp(p.host)) {
return false;
- if (!device.equalp(p.device))
+ }
+ if (!device.equalp(p.device)) {
return false;
- if (!directory.equalp(p.directory))
+ }
+ if (!directory.equalp(p.directory)) {
return false;
- if (!name.equalp(p.name))
+ }
+ if (!name.equalp(p.name)) {
return false;
- if (!type.equalp(p.type))
+ }
+ if (!type.equalp(p.type)) {
return false;
+ }
// Ignore version component.
//if (!version.equalp(p.version))
// return false;
} else {
// Unix.
- if (!host.equal(p.host))
+ if (!host.equal(p.host)) {
return false;
- if (!device.equal(p.device))
+ }
+ if (!device.equal(p.device)) {
return false;
- if (!directory.equal(p.directory))
+ }
+ if (!directory.equal(p.directory)) {
return false;
- if (!name.equal(p.name))
+ }
+ if (!name.equal(p.name)) {
return false;
- if (!type.equal(p.type))
+ }
+ if (!type.equal(p.type)) {
return false;
+ }
// Ignore version component.
//if (!version.equal(p.version))
// return false;
@@ -474,24 +693,21 @@
}
@Override
- public boolean equalp(LispObject obj)
- {
+ public boolean equalp(LispObject obj) {
return equal(obj);
}
@Override
- public int sxhash()
- {
- return ((host.sxhash() ^
- device.sxhash() ^
- directory.sxhash() ^
- name.sxhash() ^
- type.sxhash()) & 0x7fffffff);
+ public int sxhash() {
+ return ((host.sxhash()
+ ^ device.sxhash()
+ ^ directory.sxhash()
+ ^ name.sxhash()
+ ^ type.sxhash()) & 0x7fffffff);
}
@Override
- public String writeToString()
- {
+ public String writeToString() {
final LispThread thread = LispThread.currentThread();
boolean printReadably = (Symbol.PRINT_READABLY.symbolValue(thread) != NIL);
boolean printEscape = (Symbol.PRINT_ESCAPE.symbolValue(thread) != NIL);
@@ -507,47 +723,52 @@
useNamestring = false;
} else if (name instanceof AbstractString) {
String n = name.getStringValue();
- if (n.equals(".") || n.equals(".."))
+ if (n.equals(".") || n.equals("..")) {
useNamestring = false;
- else if (n.indexOf(File.separatorChar) >= 0)
+ } else if (n.indexOf(File.separatorChar) >= 0) {
useNamestring = false;
+ }
}
}
- } else
- useNamestring = false;
+ } else {
+ useNamestring = false;
+ }
FastStringBuffer sb = new FastStringBuffer();
if (useNamestring) {
- if (printReadably || printEscape)
+ if (printReadably || printEscape) {
sb.append("#P\"");
+ }
final int limit = s.length();
for (int i = 0; i < limit; i++) {
char c = s.charAt(i);
if (printReadably || printEscape) {
- if (c == '\"' || c == '\\')
+ if (c == '\"' || c == '\\') {
sb.append('\\');
+ }
}
sb.append(c);
}
- if (printReadably || printEscape)
+ if (printReadably || printEscape) {
sb.append('"');
+ }
} else {
sb.append("#P(");
if (host != NIL) {
sb.append(":HOST ");
sb.append(host.writeToString());
sb.append(' ');
- }
- if (device != NIL) {
- sb.append(":DEVICE ");
- sb.append(device.writeToString());
- sb.append(' ');
+ }
+ if (device != NIL) {
+ sb.append(":DEVICE ");
+ sb.append(device.writeToString());
+ sb.append(' ');
}
if (directory != NIL) {
sb.append(":DIRECTORY ");
sb.append(directory.writeToString());
sb.append(" ");
}
- if (name != NIL) {
+ if (name != NIL) {
sb.append(":NAME ");
sb.append(name.writeToString());
sb.append(' ');
@@ -562,31 +783,26 @@
sb.append(version.writeToString());
sb.append(' ');
}
- if (sb.charAt(sb.length() - 1) == ' ')
+ if (sb.charAt(sb.length() - 1) == ' ') {
sb.setLength(sb.length() - 1);
+ }
sb.append(')');
- }
- return sb.toString();
+ }
+ return sb.toString();
}
-
// A logical host is represented as the string that names it.
// (defvar *logical-pathname-translations* (make-hash-table :test 'equal))
public static EqualHashTable LOGICAL_PATHNAME_TRANSLATIONS =
- new EqualHashTable(64, NIL, NIL);
+ new EqualHashTable(64, NIL, NIL);
+ private static final Symbol _LOGICAL_PATHNAME_TRANSLATIONS_ =
+ exportSpecial("*LOGICAL-PATHNAME-TRANSLATIONS*", PACKAGE_SYS,
+ LOGICAL_PATHNAME_TRANSLATIONS);
- private static final Symbol _LOGICAL_PATHNAME_TRANSLATIONS_ =
- exportSpecial("*LOGICAL-PATHNAME-TRANSLATIONS*", PACKAGE_SYS,
- LOGICAL_PATHNAME_TRANSLATIONS);
-
- public static Pathname parseNamestring(String s)
-
- {
+ public static Pathname parseNamestring(String s) {
return new Pathname(s);
}
- public static Pathname parseNamestring(AbstractString namestring)
-
- {
+ public static Pathname parseNamestring(AbstractString namestring) {
// Check for a logical pathname host.
String s = namestring.getStringValue();
String h = getHostString(s);
@@ -598,17 +814,15 @@
}
public static Pathname parseNamestring(AbstractString namestring,
- AbstractString host)
-
- {
+ AbstractString host) {
// Look for a logical pathname host in the namestring.
String s = namestring.getStringValue();
String h = getHostString(s);
if (h != null) {
if (!h.equals(host.getStringValue())) {
- error(new LispError("Host in " + s +
- " does not match requested host " +
- host.getStringValue()));
+ error(new LispError("Host in " + s
+ + " does not match requested host "
+ + host.getStringValue()));
// Not reached.
return null;
}
@@ -625,197 +839,178 @@
}
// "one or more uppercase letters, digits, and hyphens"
- protected static String getHostString(String s)
- {
+ protected static String getHostString(String s) {
int colon = s.indexOf(':');
- if (colon >= 0)
+ if (colon >= 0) {
return s.substring(0, colon).toUpperCase();
- else
+ } else {
return null;
+ }
}
- private static final void checkCaseArgument(LispObject arg)
-
- {
- if (arg != Keyword.COMMON && arg != Keyword.LOCAL)
+ private static final void checkCaseArgument(LispObject arg) {
+ if (arg != Keyword.COMMON && arg != Keyword.LOCAL) {
type_error(arg, list(Symbol.MEMBER, Keyword.COMMON,
- Keyword.LOCAL));
+ Keyword.LOCAL));
+ }
}
-
// ### %pathname-host
private static final Primitive _PATHNAME_HOST =
- new Primitive("%pathname-host", PACKAGE_SYS, false)
- {
- @Override
- public LispObject execute(LispObject first, LispObject second)
+ new Primitive("%pathname-host", PACKAGE_SYS, false) {
- {
- checkCaseArgument(second);
- return coerceToPathname(first).host;
- }
- };
-
+ @Override
+ public LispObject execute(LispObject first, LispObject second) {
+ checkCaseArgument(second);
+ return coerceToPathname(first).host;
+ }
+ };
// ### %pathname-device
private static final Primitive _PATHNAME_DEVICE =
- new Primitive("%pathname-device", PACKAGE_SYS, false)
- {
- @Override
- public LispObject execute(LispObject first, LispObject second)
+ new Primitive("%pathname-device", PACKAGE_SYS, false) {
- {
- checkCaseArgument(second);
- return coerceToPathname(first).device;
- }
- };
-
+ @Override
+ public LispObject execute(LispObject first, LispObject second) {
+ checkCaseArgument(second);
+ return coerceToPathname(first).device;
+ }
+ };
// ### %pathname-directory
private static final Primitive _PATHNAME_DIRECTORY =
- new Primitive("%pathname-directory", PACKAGE_SYS, false)
- {
- @Override
- public LispObject execute(LispObject first, LispObject second)
+ new Primitive("%pathname-directory", PACKAGE_SYS, false) {
- {
- checkCaseArgument(second);
- return coerceToPathname(first).directory;
- }
- };
-
+ @Override
+ public LispObject execute(LispObject first, LispObject second) {
+ checkCaseArgument(second);
+ return coerceToPathname(first).directory;
+ }
+ };
// ### %pathname-name
private static final Primitive _PATHNAME_NAME =
- new Primitive("%pathname-name", PACKAGE_SYS, false)
- {
- @Override
- public LispObject execute(LispObject first, LispObject second)
+ new Primitive("%pathname-name", PACKAGE_SYS, false) {
- {
- checkCaseArgument(second);
- return coerceToPathname(first).name;
- }
- };
-
+ @Override
+ public LispObject execute(LispObject first, LispObject second) {
+ checkCaseArgument(second);
+ return coerceToPathname(first).name;
+ }
+ };
// ### %pathname-type
private static final Primitive _PATHNAME_TYPE =
- new Primitive("%pathname-type", PACKAGE_SYS, false)
- {
- @Override
- public LispObject execute(LispObject first, LispObject second)
+ new Primitive("%pathname-type", PACKAGE_SYS, false) {
- {
- checkCaseArgument(second);
- return coerceToPathname(first).type;
- }
- };
-
+ @Override
+ public LispObject execute(LispObject first, LispObject second) {
+ checkCaseArgument(second);
+ return coerceToPathname(first).type;
+ }
+ };
// ### pathname-version
private static final Primitive PATHNAME_VERSION =
- new Primitive("pathname-version", "pathname")
- {
- @Override
- public LispObject execute(LispObject arg)
- {
- return coerceToPathname(arg).version;
- }
- };
+ new Primitive("pathname-version", "pathname") {
+ @Override
+ public LispObject execute(LispObject arg) {
+ return coerceToPathname(arg).version;
+ }
+ };
// ### namestring
// namestring pathname => namestring
private static final Primitive NAMESTRING =
- new Primitive("namestring", "pathname")
- {
- @Override
- public LispObject execute(LispObject arg)
- {
- Pathname pathname = coerceToPathname(arg);
- String namestring = pathname.getNamestring();
- if (namestring == null)
- error(new SimpleError("Pathname has no namestring: " +
- pathname.writeToString()));
- return new SimpleString(namestring);
- }
- };
+ new Primitive("namestring", "pathname") {
+ @Override
+ public LispObject execute(LispObject arg) {
+ Pathname pathname = coerceToPathname(arg);
+ String namestring = pathname.getNamestring();
+ if (namestring == null) {
+ error(new SimpleError("Pathname has no namestring: "
+ + pathname.writeToString()));
+ }
+ return new SimpleString(namestring);
+ }
+ };
// ### directory-namestring
// directory-namestring pathname => namestring
private static final Primitive DIRECTORY_NAMESTRING =
- new Primitive("directory-namestring", "pathname")
- {
- @Override
- public LispObject execute(LispObject arg)
- {
- return new SimpleString(coerceToPathname(arg).getDirectoryNamestring());
- }
- };
+ new Primitive("directory-namestring", "pathname") {
+ @Override
+ public LispObject execute(LispObject arg) {
+ return new SimpleString(coerceToPathname(arg).getDirectoryNamestring());
+ }
+ };
// ### pathname pathspec => pathname
private static final Primitive PATHNAME =
- new Primitive("pathname", "pathspec")
- {
- @Override
- public LispObject execute(LispObject arg)
- {
- return coerceToPathname(arg);
- }
- };
+ new Primitive("pathname", "pathspec") {
+ @Override
+ public LispObject execute(LispObject arg) {
+ return coerceToPathname(arg);
+ }
+ };
// ### %parse-namestring string host default-pathname => pathname, position
private static final Primitive _PARSE_NAMESTRING =
- new Primitive("%parse-namestring", PACKAGE_SYS, false,
- "namestring host default-pathname")
- {
- @Override
- public LispObject execute(LispObject first, LispObject second,
- LispObject third)
+ new Primitive("%parse-namestring", PACKAGE_SYS, false,
+ "namestring host default-pathname") {
- {
- final LispThread thread = LispThread.currentThread();
- final AbstractString namestring = checkString(first);
- // The HOST parameter must be a string or NIL.
- if (second == NIL) {
- // "If HOST is NIL, DEFAULT-PATHNAME is a logical pathname, and
- // THING is a syntactically valid logical pathname namestring
- // without an explicit host, then it is parsed as a logical
- // pathname namestring on the host that is the host component
- // of DEFAULT-PATHNAME."
- third = coerceToPathname(third);
- if (third instanceof LogicalPathname)
- second = ((LogicalPathname)third).host;
- else
- return thread.setValues(parseNamestring(namestring),
- namestring.LENGTH());
- }
- Debug.assertTrue(second != NIL);
- final AbstractString host = checkString(second);
- return thread.setValues(parseNamestring(namestring, host),
- namestring.LENGTH());
- }
- };
-
+ @Override
+ public LispObject execute(LispObject first, LispObject second,
+ LispObject third) {
+ final LispThread thread = LispThread.currentThread();
+ final AbstractString namestring = checkString(first);
+ // The HOST parameter must be a string or NIL.
+ if (second == NIL) {
+ // "If HOST is NIL, DEFAULT-PATHNAME is a logical pathname, and
+ // THING is a syntactically valid logical pathname namestring
+ // without an explicit host, then it is parsed as a logical
+ // pathname namestring on the host that is the host component
+ // of DEFAULT-PATHNAME."
+ third = coerceToPathname(third);
+ if (third instanceof LogicalPathname) {
+ second = ((LogicalPathname) third).host;
+ } else {
+ return thread.setValues(parseNamestring(namestring),
+ namestring.LENGTH());
+ }
+ }
+ Debug.assertTrue(second != NIL);
+ final AbstractString host = checkString(second);
+ return thread.setValues(parseNamestring(namestring, host),
+ namestring.LENGTH());
+ }
+ };
// ### make-pathname
private static final Primitive MAKE_PATHNAME =
- new Primitive("make-pathname",
- "&key host device directory name type version defaults case")
- {
- @Override
- public LispObject execute(LispObject[] args)
+ new Primitive("make-pathname",
+ "&key host device directory name type version defaults case") {
- {
- return _makePathname(args);
- }
- };
+ @Override
+ public LispObject execute(LispObject[] args) {
+ return _makePathname(args);
+ }
+ };
// Used by the #p reader.
- public static final Pathname makePathname(LispObject args)
-
- {
+ public static final Pathname makePathname(LispObject args) {
return _makePathname(args.copyToArray());
}
- private static final Pathname _makePathname(LispObject[] args)
+ public static final Pathname makePathname(File file) {
+ String namestring = null;
+ try {
+ namestring = file.getCanonicalPath();
+ } catch (IOException e) {
+ Debug.trace("Failed to make a Pathname from "
+ + "." + file + "'");
+ return null;
+ }
+ return new Pathname(namestring);
+ }
- {
- if (args.length % 2 != 0)
+ private static final Pathname _makePathname(LispObject[] args) {
+ if (args.length % 2 != 0) {
error(new ProgramError("Odd number of keyword arguments."));
+ }
LispObject host = NIL;
LispObject device = NIL;
LispObject directory = NIL;
@@ -828,19 +1023,20 @@
boolean typeSupplied = false;
for (int i = 0; i < args.length; i += 2) {
LispObject key = args[i];
- LispObject value = args[i+1];
+ LispObject value = args[i + 1];
if (key == Keyword.HOST) {
host = value;
} else if (key == Keyword.DEVICE) {
device = value;
deviceSupplied = true;
} else if (key == Keyword.DIRECTORY) {
- if (value instanceof AbstractString)
+ if (value instanceof AbstractString) {
directory = list(Keyword.ABSOLUTE, value);
- else if (value == Keyword.WILD)
+ } else if (value == Keyword.WILD) {
directory = list(Keyword.ABSOLUTE, Keyword.WILD);
- else
+ } else {
directory = value;
+ }
} else if (key == Keyword.NAME) {
name = value;
nameSupplied = true;
@@ -852,25 +1048,30 @@
} else if (key == Keyword.DEFAULTS) {
defaults = coerceToPathname(value);
} else if (key == Keyword.CASE) {
- // Ignored.
+ // Ignored.
}
}
if (defaults != null) {
- if (host == NIL)
+ if (host == NIL) {
host = defaults.host;
+ }
directory = mergeDirectories(directory, defaults.directory);
- if (!deviceSupplied)
+ if (!deviceSupplied) {
device = defaults.device;
- if (!nameSupplied)
+ }
+ if (!nameSupplied) {
name = defaults.name;
- if (!typeSupplied)
+ }
+ if (!typeSupplied) {
type = defaults.type;
+ }
}
final Pathname p;
final boolean logical;
if (host != NIL) {
- if (host instanceof AbstractString)
- host = LogicalPathname.canonicalizeStringComponent((AbstractString)host);
+ if (host instanceof AbstractString) {
+ host = LogicalPathname.canonicalizeStringComponent((AbstractString) host);
+ }
if (LOGICAL_PATHNAME_TRANSLATIONS.get(host) == null) {
// Not a defined logical pathname host.
error(new LispError(host.writeToString() + " is not defined as a logical pathname host."));
@@ -886,10 +1087,12 @@
if (device != NIL) {
if (logical) {
// "The device component of a logical pathname is always :UNSPECIFIC."
- if (device != Keyword.UNSPECIFIC)
+ if (device != Keyword.UNSPECIFIC) {
error(new LispError("The device component of a logical pathname must be :UNSPECIFIC."));
- } else
+ }
+ } else {
p.device = device;
+ }
}
if (directory != NIL) {
if (logical) {
@@ -897,48 +1100,51 @@
LispObject d = NIL;
while (directory != NIL) {
LispObject component = directory.car();
- if (component instanceof AbstractString)
- d = d.push(LogicalPathname.canonicalizeStringComponent((AbstractString)component));
- else
+ if (component instanceof AbstractString) {
+ d = d.push(LogicalPathname.canonicalizeStringComponent((AbstractString) component));
+ } else {
d = d.push(component);
+ }
directory = directory.cdr();
}
p.directory = d.nreverse();
- } else if (directory == Keyword.WILD || directory == Keyword.WILD_INFERIORS)
+ } else if (directory == Keyword.WILD || directory == Keyword.WILD_INFERIORS) {
p.directory = directory;
- else
+ } else {
error(new LispError("Invalid directory component for logical pathname: " + directory.writeToString()));
- } else
+ }
+ } else {
p.directory = directory;
+ }
}
if (name != NIL) {
- if (logical && name instanceof AbstractString)
- p.name = LogicalPathname.canonicalizeStringComponent((AbstractString)name);
- else if (name instanceof AbstractString)
- p.name = validateStringComponent((AbstractString)name);
- else
+ if (logical && name instanceof AbstractString) {
+ p.name = LogicalPathname.canonicalizeStringComponent((AbstractString) name);
+ } else if (name instanceof AbstractString) {
+ p.name = validateStringComponent((AbstractString) name);
+ } else {
p.name = name;
+ }
}
if (type != NIL) {
- if (logical && type instanceof AbstractString)
- p.type = LogicalPathname.canonicalizeStringComponent((AbstractString)type);
- else
+ if (logical && type instanceof AbstractString) {
+ p.type = LogicalPathname.canonicalizeStringComponent((AbstractString) type);
+ } else {
p.type = type;
+ }
}
p.version = version;
return p;
}
- private static final AbstractString validateStringComponent(AbstractString s)
-
- {
+ private static final AbstractString validateStringComponent(AbstractString s) {
final int limit = s.length();
for (int i = 0; i < limit; i++) {
char c = s.charAt(i);
if (c == '/' || c == '\\' && Utilities.isPlatformWindows) {
- error(new LispError("Invalid character #\\" + c +
- " in pathname component \"" + s +
- '"'));
+ error(new LispError("Invalid character #\\" + c
+ + " in pathname component \"" + s
+ + '"'));
// Not reached.
return null;
}
@@ -946,9 +1152,7 @@
return s;
}
- private final boolean validateDirectory(boolean signalError)
-
- {
+ private final boolean validateDirectory(boolean signalError) {
LispObject temp = directory;
while (temp != NIL) {
LispObject first = temp.car();
@@ -970,259 +1174,321 @@
}
return true;
}
-
// ### pathnamep
private static final Primitive PATHNAMEP =
- new Primitive("pathnamep", "object")
- {
- @Override
- public LispObject execute(LispObject arg)
- {
- return arg instanceof Pathname ? T : NIL;
- }
- };
+ new Primitive("pathnamep", "object") {
+ @Override
+ public LispObject execute(LispObject arg) {
+ return arg instanceof Pathname ? T : NIL;
+ }
+ };
// ### logical-pathname-p
private static final Primitive LOGICAL_PATHNAME_P =
- new Primitive("logical-pathname-p", PACKAGE_SYS, true, "object")
- {
- @Override
- public LispObject execute(LispObject arg)
- {
- return arg instanceof LogicalPathname ? T : NIL;
- }
- };
+ new Primitive("logical-pathname-p", PACKAGE_SYS, true, "object") {
+ @Override
+ public LispObject execute(LispObject arg) {
+ return arg instanceof LogicalPathname ? T : NIL;
+ }
+ };
// ### user-homedir-pathname &optional host => pathname
private static final Primitive USER_HOMEDIR_PATHNAME =
- new Primitive("user-homedir-pathname", "&optional host")
- {
- @Override
- public LispObject execute(LispObject[] args)
- {
- switch (args.length) {
- case 0: {
- String s = System.getProperty("user.home");
- if (!s.endsWith(File.separator))
- s = s.concat(File.separator);
- return new Pathname(s);
+ new Primitive("user-homedir-pathname", "&optional host") {
+
+ @Override
+ public LispObject execute(LispObject[] args) {
+ switch (args.length) {
+ case 0: {
+ String s = System.getProperty("user.home");
+ if (!s.endsWith(File.separator)) {
+ s = s.concat(File.separator);
+ }
+ return new Pathname(s);
+ }
+ case 1:
+ return NIL;
+ default:
+ return error(new WrongNumberOfArgumentsException(this));
+ }
+ }
+ };
+ // ### list-directory
+ private static final Primitive LIST_DIRECTORY =
+ new Primitive("list-directory", PACKAGE_SYS, true) {
+
+ @Override
+ public LispObject execute(LispObject arg) {
+ Pathname pathname = coerceToPathname(arg);
+ if (pathname instanceof LogicalPathname) {
+ pathname = LogicalPathname.translateLogicalPathname((LogicalPathname) pathname);
+ }
+ LispObject result = NIL;
+ String s = pathname.getNamestring();
+ if (s != null) {
+ File f = new File(s);
+ if (f.isDirectory()) {
+ try {
+ File[] files = f.listFiles();
+ for (int i = files.length; i-- > 0;) {
+ File file = files[i];
+ Pathname p;
+ if (file.isDirectory()) {
+ p = Utilities.getDirectoryPathname(file);
+ } else {
+ p = new Pathname(file.getCanonicalPath());
+ }
+ result = new Cons(p, result);
+ }
+ } catch (IOException e) {
+ return error(new FileError("Unable to list directory " + pathname.writeToString() + ".",
+ pathname));
+ } catch (SecurityException e) {
+ } catch (NullPointerException e) {
+ }
+ }
+ }
+ return result;
+ }
+ };
+
+ public boolean isAbsolute() {
+ if (!directory.equals(NIL) || !(directory == null)) {
+ if (directory instanceof Cons) {
+ if (((Cons)directory).car().equals(Keyword.ABSOLUTE)) {
+ return true;
}
- case 1:
- return NIL;
- default:
- return error(new WrongNumberOfArgumentsException(this));
}
}
- };
-
- // ### list-directory
- private static final Primitive LIST_DIRECTORY =
- new Primitive("list-directory", PACKAGE_SYS, true)
- {
- @Override
- public LispObject execute(LispObject arg)
- {
- Pathname pathname = coerceToPathname(arg);
- if (pathname instanceof LogicalPathname)
- pathname = LogicalPathname.translateLogicalPathname((LogicalPathname)pathname);
- LispObject result = NIL;
- String s = pathname.getNamestring();
- if (s != null) {
- File f = new File(s);
- if (f.isDirectory()) {
- try {
- File[] files = f.listFiles();
- for (int i = files.length; i-- > 0;) {
- File file = files[i];
- Pathname p;
- if (file.isDirectory())
- p = Utilities.getDirectoryPathname(file);
- else
- p = new Pathname(file.getCanonicalPath());
- result = new Cons(p, result);
- }
- }
- catch (IOException e) {
- return error(new FileError("Unable to list directory " + pathname.writeToString() + ".",
- pathname));
- }
- catch (SecurityException e) {
- }
- catch (NullPointerException e) {
- }
- }
- }
- return result;
- }
- };
-
- public boolean isWild()
- {
- if (host == Keyword.WILD || host == Keyword.WILD_INFERIORS)
- return true;
- if (device == Keyword.WILD || device == Keyword.WILD_INFERIORS)
- return true;
- if (directory instanceof Cons) {
- if (memq(Keyword.WILD, directory))
- return true;
- if (memq(Keyword.WILD_INFERIORS, directory))
- return true;
- }
- if (name == Keyword.WILD || name == Keyword.WILD_INFERIORS)
- return true;
- if (type == Keyword.WILD || type == Keyword.WILD_INFERIORS)
- return true;
- if (version == Keyword.WILD || version == Keyword.WILD_INFERIORS)
- return true;
return false;
}
+ public boolean isJar() {
+ if (device instanceof Cons) {
+ return true;
+ }
+ return false;
+ }
+
+ public boolean isWild() {
+ if (host == Keyword.WILD || host == Keyword.WILD_INFERIORS) {
+ return true;
+ }
+ if (device == Keyword.WILD || device == Keyword.WILD_INFERIORS) {
+ return true;
+ }
+ if (directory instanceof Cons) {
+ if (memq(Keyword.WILD, directory)) {
+ return true;
+ }
+ if (memq(Keyword.WILD_INFERIORS, directory)) {
+ return true;
+ }
+ }
+ if (name == Keyword.WILD || name == Keyword.WILD_INFERIORS) {
+ return true;
+ }
+ if (type == Keyword.WILD || type == Keyword.WILD_INFERIORS) {
+ return true;
+ }
+ if (version == Keyword.WILD || version == Keyword.WILD_INFERIORS) {
+ return true;
+ }
+ return false;
+ }
// ### %wild-pathname-p
private static final Primitive _WILD_PATHNAME_P =
- new Primitive("%wild-pathname-p", PACKAGE_SYS, true)
- {
- @Override
- public LispObject execute(LispObject first, LispObject second)
+ new Primitive("%wild-pathname-p", PACKAGE_SYS, true) {
- {
- Pathname pathname = coerceToPathname(first);
- if (second == NIL)
- return pathname.isWild() ? T : NIL;
- if (second == Keyword.DIRECTORY) {
- if (pathname.directory instanceof Cons) {
- if (memq(Keyword.WILD, pathname.directory))
- return T;
- if (memq(Keyword.WILD_INFERIORS, pathname.directory))
- return T;
- }
- return NIL;
- }
- LispObject value;
- if (second == Keyword.HOST)
- value = pathname.host;
- else if (second == Keyword.DEVICE)
- value = pathname.device;
- else if (second == Keyword.NAME)
- value = pathname.name;
- else if (second == Keyword.TYPE)
- value = pathname.type;
- else if (second == Keyword.VERSION)
- value = pathname.version;
- else
- return error(new ProgramError("Unrecognized keyword " +
- second.writeToString() + "."));
- if (value == Keyword.WILD || value == Keyword.WILD_INFERIORS)
- return T;
- else
- return NIL;
- }
- };
-
+ @Override
+ public LispObject execute(LispObject first, LispObject second) {
+ Pathname pathname = coerceToPathname(first);
+ if (second == NIL) {
+ return pathname.isWild() ? T : NIL;
+ }
+ if (second == Keyword.DIRECTORY) {
+ if (pathname.directory instanceof Cons) {
+ if (memq(Keyword.WILD, pathname.directory)) {
+ return T;
+ }
+ if (memq(Keyword.WILD_INFERIORS, pathname.directory)) {
+ return T;
+ }
+ }
+ return NIL;
+ }
+ LispObject value;
+ if (second == Keyword.HOST) {
+ value = pathname.host;
+ } else if (second == Keyword.DEVICE) {
+ value = pathname.device;
+ } else if (second == Keyword.NAME) {
+ value = pathname.name;
+ } else if (second == Keyword.TYPE) {
+ value = pathname.type;
+ } else if (second == Keyword.VERSION) {
+ value = pathname.version;
+ } else {
+ return error(new ProgramError("Unrecognized keyword "
+ + second.writeToString() + "."));
+ }
+ if (value == Keyword.WILD || value == Keyword.WILD_INFERIORS) {
+ return T;
+ } else {
+ return NIL;
+ }
+ }
+ };
// ### merge-pathnames
private static final Primitive MERGE_PATHNAMES =
- new Primitive("merge-pathnames",
- "pathname &optional default-pathname default-version")
+ new Primitive("merge-pathnames",
+ "pathname &optional default-pathname default-version") {
+
+ @Override
+ public LispObject execute(LispObject arg) {
+ Pathname pathname = coerceToPathname(arg);
+ Pathname defaultPathname =
+ coerceToPathname(Symbol.DEFAULT_PATHNAME_DEFAULTS.symbolValue());
+ LispObject defaultVersion = Keyword.NEWEST;
+ return mergePathnames(pathname, defaultPathname, defaultVersion);
+ }
+
+ @Override
+ public LispObject execute(LispObject first, LispObject second) {
+ Pathname pathname = coerceToPathname(first);
+ Pathname defaultPathname =
+ coerceToPathname(second);
+ LispObject defaultVersion = Keyword.NEWEST;
+ return mergePathnames(pathname, defaultPathname, defaultVersion);
+ }
+
+ @Override
+ public LispObject execute(LispObject first, LispObject second,
+ LispObject third) {
+ Pathname pathname = coerceToPathname(first);
+ Pathname defaultPathname =
+ coerceToPathname(second);
+ LispObject defaultVersion = third;
+ return mergePathnames(pathname, defaultPathname, defaultVersion);
+ }
+ };
+
+ public static final Pathname mergePathnames(Pathname pathname, Pathname defaultPathname) {
+ return mergePathnames(pathname, defaultPathname, Keyword.NEWEST);
+ }
+
+ public static final Pathname mergePathnames(final Pathname pathname,
+ final Pathname defaultPathname,
+ final LispObject defaultVersion)
{
- @Override
- public LispObject execute(LispObject arg)
- {
- Pathname pathname = coerceToPathname(arg);
- Pathname defaultPathname =
- coerceToPathname(Symbol.DEFAULT_PATHNAME_DEFAULTS.symbolValue());
- LispObject defaultVersion = Keyword.NEWEST;
- return mergePathnames(pathname, defaultPathname, defaultVersion);
+ Pathname result;
+ Pathname p = new Pathname(pathname);
+ Pathname d;
+
+ if (pathname instanceof LogicalPathname) {
+ result = new LogicalPathname();
+ d = new Pathname(defaultPathname);
+ } else {
+ result = new Pathname();
+ if (defaultPathname instanceof LogicalPathname) {
+ d = LogicalPathname.translateLogicalPathname((LogicalPathname) defaultPathname);
+ } else {
+ d = new Pathname(defaultPathname);
+ }
}
- @Override
- public LispObject execute(LispObject first, LispObject second)
+ if (pathname.host != NIL) {
+ result.host = p.host;
+ } else {
+ result.host = d.host;
+ }
- {
- Pathname pathname = coerceToPathname(first);
- Pathname defaultPathname =
- coerceToPathname(second);
- LispObject defaultVersion = Keyword.NEWEST;
- return mergePathnames(pathname, defaultPathname, defaultVersion);
+ if (pathname.device != NIL) { // XXX if device represent JARs we want to merge
+ result.device = p.device;
+ } else {
+ result.device = d.device;
}
- @Override
- public LispObject execute(LispObject first, LispObject second,
- LispObject third)
- {
- Pathname pathname = coerceToPathname(first);
- Pathname defaultPathname =
- coerceToPathname(second);
- LispObject defaultVersion = third;
- return mergePathnames(pathname, defaultPathname, defaultVersion);
+ if (pathname.isJar()) {
+ Cons jars = (Cons)result.device;
+ LispObject jar = jars.car;
+ if (jar instanceof Pathname) {
+ Pathname defaults = new Pathname(d);
+ if (defaults.isJar()) {
+ defaults.device = NIL;
+ }
+ Pathname o = mergePathnames((Pathname)jar, defaults);
+ if (o.directory instanceof Cons
+ && ((Cons)o.directory).length() == 1) { // i.e. (:ABSOLUTE) or (:RELATIVE)
+ o.directory = NIL;
+ }
+ ((Cons)result.device).car = o;
+ }
+ } else {
+ result.directory = mergeDirectories(p.directory, d.directory);
}
- };
- public static final Pathname mergePathnames(Pathname pathname,
- Pathname defaultPathname,
- LispObject defaultVersion)
+ // A JAR always has relative directories
+ if (result.isJar()
+ && result.directory instanceof Cons
+ && result.directory.car().equals(Keyword.ABSOLUTE)) {
+ if (result.directory.cdr().equals(NIL)) {
+ result.directory = NIL;
+ } else {
+ ((Cons)result.directory).car = Keyword.RELATIVE;
+ }
+ }
- {
- Pathname p;
- if (pathname instanceof LogicalPathname)
- p = new LogicalPathname();
- else {
- p = new Pathname();
- if (defaultPathname instanceof LogicalPathname)
- defaultPathname = LogicalPathname.translateLogicalPathname((LogicalPathname)defaultPathname);
+ if (pathname.name != NIL) {
+ result.name = p.name;
+ } else {
+ result.name = d.name;
}
- if (pathname.host != NIL)
- p.host = pathname.host;
- else
- p.host = defaultPathname.host;
- if (pathname.device != NIL)
- p.device = pathname.device;
- else
- p.device = defaultPathname.device;
- p.directory =
- mergeDirectories(pathname.directory, defaultPathname.directory);
- if (pathname.name != NIL)
- p.name = pathname.name;
- else
- p.name = defaultPathname.name;
- if (pathname.type != NIL)
- p.type = pathname.type;
- else
- p.type = defaultPathname.type;
- if (pathname.version != NIL)
- p.version = pathname.version;
- else if (pathname.name instanceof AbstractString)
- p.version = defaultVersion;
- else if (defaultPathname.version != NIL)
- p.version = defaultPathname.version;
- else
- p.version = defaultVersion;
- if (p instanceof LogicalPathname) {
+ if (pathname.type != NIL) {
+ result.type = p.type;
+ } else {
+ result.type = d.type;
+ }
+ if (pathname.version != NIL) {
+ result.version = pathname.version;
+ } else if (pathname.name instanceof AbstractString) {
+ result.version = defaultVersion;
+ } else if (defaultPathname.version != NIL) {
+ result.version = defaultPathname.version;
+ } else {
+ result.version = defaultVersion;
+ }
+ if (pathname instanceof LogicalPathname) {
// When we're returning a logical
- p.device = Keyword.UNSPECIFIC;
- if (p.directory.listp()) {
- LispObject original = p.directory;
+ result.device = Keyword.UNSPECIFIC;
+ if (result.directory.listp()) {
+ LispObject original = result.directory;
LispObject canonical = NIL;
while (original != NIL) {
LispObject component = original.car();
- if (component instanceof AbstractString)
- component = LogicalPathname.canonicalizeStringComponent((AbstractString)component);
+ if (component instanceof AbstractString) {
+ component = LogicalPathname.canonicalizeStringComponent((AbstractString) component);
+ }
canonical = canonical.push(component);
original = original.cdr();
}
- p.directory = canonical.nreverse();
+ result.directory = canonical.nreverse();
}
- if (p.name instanceof AbstractString)
- p.name = LogicalPathname.canonicalizeStringComponent((AbstractString)p.name);
- if (p.type instanceof AbstractString)
- p.type = LogicalPathname.canonicalizeStringComponent((AbstractString)p.type);
+ if (result.name instanceof AbstractString) {
+ result.name = LogicalPathname.canonicalizeStringComponent((AbstractString) result.name);
+ }
+ if (result.type instanceof AbstractString) {
+ result.type = LogicalPathname.canonicalizeStringComponent((AbstractString) result.type);
+ }
}
- return p;
+ result.invalidateNamestring();
+ return result;
}
private static final LispObject mergeDirectories(LispObject dir,
- LispObject defaultDir)
-
- {
- if (dir == NIL)
+ LispObject defaultDir) {
+ if (dir == NIL) {
return defaultDir;
+ }
if (dir.car() == Keyword.RELATIVE && defaultDir != NIL) {
LispObject result = NIL;
while (defaultDir != NIL) {
@@ -1237,154 +1503,451 @@
LispObject[] array = result.copyToArray();
for (int i = 0; i < array.length - 1; i++) {
if (array[i] == Keyword.BACK) {
- if (array[i+1] instanceof AbstractString || array[i+1] == Keyword.WILD) {
+ if (array[i + 1] instanceof AbstractString || array[i + 1] == Keyword.WILD) {
array[i] = null;
- array[i+1] = null;
+ array[i + 1] = null;
}
}
}
result = NIL;
for (int i = 0; i < array.length; i++) {
- if (array[i] != null)
+ if (array[i] != null) {
result = new Cons(array[i], result);
+ }
}
return result;
}
return dir;
}
- public static final LispObject truename(LispObject arg,
- boolean errorIfDoesNotExist)
+ public static final LispObject truename(Pathname pathname) {
+ return truename(pathname, false);
+ }
+ public static final LispObject truename(LispObject arg) {
+ return truename(arg, false);
+ }
+
+ public static final LispObject truename(LispObject arg, boolean errorIfDoesNotExist) {
+ final Pathname pathname = coerceToPathname(arg);
+ return truename(pathname, errorIfDoesNotExist);
+ }
+
+ /** @return The canonical TRUENAME as a Pathname if the pathname
+ * exists, otherwise returns NIL or possibly a subtype of
+ * LispError if there are logical problems with the input.
+ */
+ public static final LispObject truename(Pathname pathname,
+ boolean errorIfDoesNotExist)
{
- Pathname pathname = coerceToPathname(arg);
- if (pathname instanceof LogicalPathname)
- pathname = LogicalPathname.translateLogicalPathname((LogicalPathname)pathname);
- if (pathname.isWild())
+ if (pathname instanceof LogicalPathname) {
+ pathname = LogicalPathname.translateLogicalPathname((LogicalPathname) pathname);
+ }
+ if (pathname.isWild()) {
return error(new FileError("Bad place for a wild pathname.",
- pathname));
- final Pathname defaultedPathname =
- mergePathnames(pathname,
- coerceToPathname(Symbol.DEFAULT_PATHNAME_DEFAULTS.symbolValue()),
- NIL);
- final String namestring = defaultedPathname.getNamestring();
- if (namestring == null)
- return error(new FileError("Pathname has no namestring: " + defaultedPathname.writeToString(),
- defaultedPathname));
- final File file = new File(namestring);
- if (file.isDirectory())
- return Utilities.getDirectoryPathname(file);
- if (file.exists()) {
- try {
- return new Pathname(file.getCanonicalPath());
+ pathname));
+ }
+ if (!(pathname.device instanceof Cons)) {
+ pathname
+ = mergePathnames(pathname,
+ coerceToPathname(Symbol.DEFAULT_PATHNAME_DEFAULTS.symbolValue()),
+ NIL);
+ final String namestring = pathname.getNamestring();
+ if (namestring == null) {
+ return error(new FileError("Pathname has no namestring: "
+ + pathname.writeToString(),
+ pathname));
}
- catch (IOException e) {
- return error(new LispError(e.getMessage()));
+
+ final File file = new File(namestring);
+ if (file.isDirectory()) {
+ return Utilities.getDirectoryPathname(file);
+ }
+ if (file.exists()) {
+ try {
+ return new Pathname(file.getCanonicalPath());
+ } catch (IOException e) {
+ return error(new FileError(e.getMessage(), pathname));
+ }
+ }
+ } else
+ jarfile: {
+ // Possibly canonicalize jar file directory
+ Cons jars = (Cons) pathname.device;
+ LispObject o = jars.car();
+ if (o instanceof Pathname) {
+ LispObject truename = Pathname.truename((Pathname)o, errorIfDoesNotExist);
+ if (truename != null
+ && truename instanceof Pathname) {
+ jars.car = (Pathname)truename;
+ } else {
+ break jarfile;
+ }
+ }
+
+ // Check for existence of a JAR file and/or JarEntry
+ //
+ // Cases:
+ // 1. JAR
+ // 2. JAR in JAR
+ // 3. JAR with Entry
+ // 4. JAR in JAR with Entry
+ JarFile jarFile = getJarFile(jars.car());
+ String entryPath = pathname.asEntryPath();
+ if (jarFile != null) {
+ if (jars.cdr() instanceof Cons) {
+ Pathname inner = (Pathname) jars.cdr().car();
+ InputStream inputStream = Utilities.getInputStream(jarFile, inner);
+ if (inputStream != null) {
+ if (entryPath.length() == 0) {
+ return pathname; // Case 2
+ } else {
+ ZipInputStream zipInputStream
+ = new ZipInputStream(inputStream);
+ ZipEntry entry = Utilities.getEntry(zipInputStream,
+ entryPath,
+ false);
+ if (entry != null) {
+ // XXX this could possibly be a directory?
+ return pathname; // Case 4
+ }
+ }
+ }
+ } else {
+ if (entryPath.length() == 0) {
+ return pathname; // Case 1
+ } else {
+ ZipEntry entry = jarFile.getEntry(entryPath);
+ if (entry != null) {
+ // ensure this isn't a directory
+ try {
+ InputStream input = jarFile.getInputStream(entry);
+ if (input != null) {
+ return pathname; // Case 3
+ }
+ } catch (IOException e) {
+ break jarfile;
+ }
+ }
+ }
+ }
}
}
+ error:
if (errorIfDoesNotExist) {
FastStringBuffer sb = new FastStringBuffer("The file ");
- sb.append(defaultedPathname.writeToString());
+ sb.append(pathname.writeToString());
sb.append(" does not exist.");
- return error(new FileError(sb.toString(), defaultedPathname));
+ return error(new FileError(sb.toString(), pathname));
}
return NIL;
}
+
+ /** Make a JarURL from a Pathname that references a file */
+ private static URL makeJarURL(Pathname p) {
+ String jarURL = "jar:file:" + p.getNamestring() + "!/";
+ URL result = null;
+ try {
+ result = new URL(jarURL);
+ } catch (MalformedURLException ex) {
+ // XXX
+ Debug.trace("Could not form URL from pathname "
+ + "'" + jarURL + "'"
+ + " because " + ex);
+ }
+ return result;
+ }
+
+ /** Make a JarURL from a generic URL reference. */
+ private static URL makeJarURL(String url) {
+ String jarURL = "jar:" + url + "!/";
+ URL result = null;
+ try {
+ result = new URL(jarURL);
+ } catch (MalformedURLException ex) {
+ // XXX
+ Debug.trace("Could not form jar URL from "
+ + "'" + jarURL + "'"
+ + " because " + ex);
+ }
+ return result;
+ }
+
+ private static JarFile getJarFile(LispObject device) {
+ URL url = null;
+ if (device instanceof SimpleString) {
+ url = makeJarURL(((SimpleString) device).getStringValue());
+ } else {
+ url = makeJarURL((Pathname) device);
+ }
+ if (url == null) {
+ return null;
+ }
+ URLConnection connection;
+ try {
+ connection = url.openConnection();
+ } catch (IOException ex) {
+ Debug.trace("Failed to open "
+ + "'" + url + "'");
+ return null;
+ }
+ if (!(connection instanceof JarURLConnection)) {
+ // XXX
+ Debug.trace("Could not get a URLConnection from " + url);
+ return null;
+ }
+ JarURLConnection jarURLConnection = (JarURLConnection) connection;
+
+ JarFile result;
+ try {
+ result = jarURLConnection.getJarFile();
+ } catch (IOException ex) {
+ Debug.trace("Could not get a JarURLConnection from "
+ + "'" + jarURLConnection + "'");
+ return null;
+ }
+ return result;
+ }
+
+ public InputStream getInputStream() {
+ InputStream result = null;
+ if (isJar()) {
+ String entryPath = asEntryPath();
+ // XXX We only return the bytes of an entry in a JAR
+ Debug.assertTrue(entryPath != null);
+ JarFile jarFile = Pathname.getJarFile(device.car());
+ Debug.assertTrue(jarFile != null);
+ // Is this a JAR within a JAR?
+ if (device.cdr() instanceof Cons) {
+ Pathname inner = (Pathname) device.cdr().car();
+ InputStream input = Utilities.getInputStream(jarFile, inner);
+ ZipInputStream zipInputStream = new ZipInputStream(input);
+ result = Utilities.getEntryAsInputStream(zipInputStream, entryPath);
+ } else {
+ ZipEntry entry = jarFile.getEntry(entryPath);
+ if (entry == null) {
+ Debug.trace("Failed to get InputStream for "
+ + "'" + getNamestring() + "'");
+
+ Debug.assertTrue(false);
+ }
+ try {
+ result = jarFile.getInputStream(entry);
+ } catch (IOException e) {
+ Debug.trace("Failed to get InputStream from "
+ + "'" + getNamestring() + "'"
+ + ": " + e);
+ }
+ }
+ } else {
+ File file = Utilities.getFile(this);
+ try {
+ result = new FileInputStream(file);
+ } catch (IOException e) {
+ Debug.trace("Failed to get InputStream for read from "
+ + "'" + getNamestring() + "'"
+ + ": " + e);
+ }
+ }
+ return result;
+ }
+
+ // ### last-modified pathname => time-in-milliseconds
+ public static final Primitive LAST_MODIFIED
+ = new Primitive("LAST-MODIFIED", PACKAGE_EXT, true, "pathname",
+ "If PATHNAME exists, returns the last modified time in miliseconds since the UNIX epoch.")
+ {
+ @Override
+ public LispObject execute(LispObject arg) {
+ final Pathname p = coerceToPathname(arg);
+ if (p.isWild()) {
+ error(new FileError("Bad place for a wild pathname.", p));
+ }
+ long time = p.getLastModified();
+ return LispInteger.getInstance(time);
+ }
+ };
+
+ /** @return Time in milliseconds since the UNIX epoch at which the
+ * resource was last modified, or 0 if the time is unknown.
+ */
+ public long getLastModified() {
+ if (!(device instanceof Cons)) {
+ File f = Utilities.getFile(this);
+ return f.lastModified();
+ }
+ // JAR cases
+ // 0. JAR from URL
+ // 1. JAR
+ // 2. JAR in JAR
+ // 3. Entry in JAR
+ // 4. Entry in JAR in JAR
+ String entryPath = asEntryPath();
+ Cons d = (Cons)device;
+ if (d.cdr().equals(NIL)) {
+ if (entryPath.length() == 0) {
+ LispObject o = d.car();
+ if (o instanceof SimpleString) {
+ // 0. JAR from URL
+ URL u = makeJarURL(o.getStringValue());
+ URLConnection c = null;
+ try {
+ c = u.openConnection();
+ } catch(IOException e) {
+ Debug.trace("Failed to open Connection for URL "
+ + "'" + u + "'");
+ return 0;
+ }
+ c.getLastModified();
+ } else {
+ // 1. JAR
+ return ((Pathname)o).getLastModified();
+ }
+ } else {
+ // 3. Entry in JAR
+ final JarEntry entry = getJarFile(device.car()).getJarEntry(entryPath);
+ if (entry == null) {
+ return 0;
+ }
+ final long time = entry.getTime();
+ if (time == -1) {
+ return 0;
+ }
+ return time;
+ }
+ } else {
+ JarFile outerJar = getJarFile(d.car());
+ if (entryPath.length() == 0) {
+ // 4. JAR in JAR
+ String jarPath = ((Pathname)d.cdr()).asEntryPath();
+ final JarEntry entry = outerJar.getJarEntry(jarPath);
+ final long time = entry.getTime();
+ if (time == -1) {
+ return 0;
+ }
+ return time;
+ } else {
+ // 5. Entry in JAR in JAR
+ String innerJarPath = ((Pathname)d.cdr()).asEntryPath();
+ ZipEntry entry = outerJar.getEntry(entryPath);
+ ZipInputStream innerJarInputStream
+ = Utilities.getZipInputStream(outerJar, innerJarPath);
+ ZipEntry innerEntry = Utilities.getEntry(innerJarInputStream,
+ entryPath);
+ long time = innerEntry.getTime();
+ if (time == -1) {
+ return 0;
+ }
+ return time;
+ }
+ }
+ return 0;
+ }
+
// ### mkdir
private static final Primitive MKDIR =
- new Primitive("mkdir", PACKAGE_SYS, false)
- {
- @Override
- public LispObject execute(LispObject arg)
- {
- final Pathname pathname = coerceToPathname(arg);
- if (pathname.isWild())
- error(new FileError("Bad place for a wild pathname.", pathname));
- Pathname defaultedPathname =
+ new Primitive("mkdir", PACKAGE_SYS, false) {
+
+ @Override
+ public LispObject execute(LispObject arg) {
+ final Pathname pathname = coerceToPathname(arg);
+ if (pathname.isWild()) {
+ error(new FileError("Bad place for a wild pathname.", pathname));
+ }
+ Pathname defaultedPathname =
mergePathnames(pathname,
- coerceToPathname(Symbol.DEFAULT_PATHNAME_DEFAULTS.symbolValue()),
- NIL);
- File file = Utilities.getFile(defaultedPathname);
- return file.mkdir() ? T : NIL;
- }
- };
-
+ coerceToPathname(Symbol.DEFAULT_PATHNAME_DEFAULTS.symbolValue()),
+ NIL);
+ File file = Utilities.getFile(defaultedPathname);
+ return file.mkdir() ? T : NIL;
+ }
+ };
// ### rename-file filespec new-name => defaulted-new-name, old-truename, new-truename
public static final Primitive RENAME_FILE =
- new Primitive("rename-file", "filespec new-name")
- {
- @Override
- public LispObject execute(LispObject first, LispObject second)
+ new Primitive("rename-file", "filespec new-name") {
- {
- final Pathname original = (Pathname) truename(first, true);
- final String originalNamestring = original.getNamestring();
- Pathname newName = coerceToPathname(second);
- if (newName.isWild())
- error(new FileError("Bad place for a wild pathname.", newName));
- newName = mergePathnames(newName, original, NIL);
- final String newNamestring;
- if (newName instanceof LogicalPathname)
- newNamestring = LogicalPathname.translateLogicalPathname((LogicalPathname)newName).getNamestring();
- else
- newNamestring = newName.getNamestring();
- if (originalNamestring != null && newNamestring != null) {
- final File source = new File(originalNamestring);
- final File destination = new File(newNamestring);
- if (Utilities.isPlatformWindows) {
- if (destination.isFile())
- destination.delete();
- }
- if (source.renameTo(destination))
- // Success!
- return LispThread.currentThread().setValues(newName, original,
- truename(newName, true));
- }
- return error(new FileError("Unable to rename " +
- original.writeToString() +
- " to " + newName.writeToString() +
- "."));
- }
- };
-
+ @Override
+ public LispObject execute(LispObject first, LispObject second) {
+ final Pathname original = (Pathname) truename(first, true);
+ final String originalNamestring = original.getNamestring();
+ Pathname newName = coerceToPathname(second);
+ if (newName.isWild()) {
+ error(new FileError("Bad place for a wild pathname.", newName));
+ }
+ newName = mergePathnames(newName, original, NIL);
+ final String newNamestring;
+ if (newName instanceof LogicalPathname) {
+ newNamestring = LogicalPathname.translateLogicalPathname((LogicalPathname) newName).getNamestring();
+ } else {
+ newNamestring = newName.getNamestring();
+ }
+ if (originalNamestring != null && newNamestring != null) {
+ final File source = new File(originalNamestring);
+ final File destination = new File(newNamestring);
+ if (Utilities.isPlatformWindows) {
+ if (destination.isFile()) {
+ destination.delete();
+ }
+ }
+ if (source.renameTo(destination)) // Success!
+ {
+ return LispThread.currentThread().setValues(newName, original,
+ truename(newName, true));
+ }
+ }
+ return error(new FileError("Unable to rename "
+ + original.writeToString()
+ + " to " + newName.writeToString()
+ + "."));
+ }
+ };
// ### file-namestring pathname => namestring
private static final Primitive FILE_NAMESTRING =
- new Primitive("file-namestring", "pathname")
- {
- @Override
- public LispObject execute(LispObject arg)
- {
- Pathname p = coerceToPathname(arg);
- FastStringBuffer sb = new FastStringBuffer();
- if (p.name instanceof AbstractString)
- sb.append(p.name.getStringValue());
- else if (p.name == Keyword.WILD)
- sb.append('*');
- else
- return NIL;
- if (p.type instanceof AbstractString) {
- sb.append('.');
- sb.append(p.type.getStringValue());
- } else if (p.type == Keyword.WILD)
- sb.append(".*");
- return new SimpleString(sb);
- }
- };
+ new Primitive("file-namestring", "pathname") {
+ @Override
+ public LispObject execute(LispObject arg) {
+ Pathname p = coerceToPathname(arg);
+ FastStringBuffer sb = new FastStringBuffer();
+ if (p.name instanceof AbstractString) {
+ sb.append(p.name.getStringValue());
+ } else if (p.name == Keyword.WILD) {
+ sb.append('*');
+ } else {
+ return NIL;
+ }
+ if (p.type instanceof AbstractString) {
+ sb.append('.');
+ sb.append(p.type.getStringValue());
+ } else if (p.type == Keyword.WILD) {
+ sb.append(".*");
+ }
+ return new SimpleString(sb);
+ }
+ };
// ### host-namestring pathname => namestring
private static final Primitive HOST_NAMESTRING =
- new Primitive("host-namestring", "pathname")
- {
- @Override
- public LispObject execute(LispObject arg)
- {
- return coerceToPathname(arg).host;
- }
- };
+ new Primitive("host-namestring", "pathname") {
+
+ @Override
+ public LispObject execute(LispObject arg) {
+ return coerceToPathname(arg).host;
+ }
+ };
+
+ public String toString() {
+ return getNamestring();
+ }
static {
LispObject obj = Symbol.DEFAULT_PATHNAME_DEFAULTS.getSymbolValue();
Symbol.DEFAULT_PATHNAME_DEFAULTS.setSymbolValue(coerceToPathname(obj));
}
+
+
}
+// Local Variables:
+// c-basic-offset: 4
+// End:
+
diff -r 884fd6b1beec src/org/armedbear/lisp/Site.java
--- a/src/org/armedbear/lisp/Site.java Fri Feb 05 16:41:42 2010 +0100
+++ b/src/org/armedbear/lisp/Site.java Fri Feb 05 18:31:59 2010 +0100
@@ -42,40 +42,36 @@
public final class Site
{
- private static final String LISP_HOME;
+ private static Pathname LISP_HOME;
- static {
- String lispHome = System.getProperty("abcl.home");
- if (lispHome == null) {
- URL url = Lisp.class.getResource("boot.lisp");
- if (url != null) {
- String protocol = url.getProtocol();
- if (protocol != null && protocol.equals("file")) {
- String path = url.getPath();
- try {
- path = URLDecoder.decode(path, "UTF-8");
- }
- catch (java.io.UnsupportedEncodingException uee) {
- // can't happen: Java implementations are required to
- // support UTF-8
- }
- int index = path.lastIndexOf('/');
- if (index >= 0) {
- lispHome = path.substring(0, index + 1);
- if (Utilities.isPlatformWindows) {
- if (lispHome.length() > 0 && lispHome.charAt(0) == '/')
- lispHome = lispHome.substring(1);
- }
- }
- }
+ private static void init() {
+ String s = System.getProperty("abcl.home");
+ if (s != null) {
+ String fileSeparator = System.getProperty("file.separator");
+ if (!s.endsWith(fileSeparator)) {
+ s += fileSeparator;;
}
+ LISP_HOME = new Pathname(s);
+ return;
}
- LISP_HOME = lispHome;
+ URL url = Lisp.class.getResource("boot.lisp");
+ if (url != null) {
+ LISP_HOME = new Pathname(url);
+ LISP_HOME.name = NIL;
+ LISP_HOME.type = NIL;
+ LISP_HOME.invalidateNamestring();
+ return;
+ }
+ Debug.trace("Unable to determine LISP_HOME.");
}
- public static final String getLispHome()
+
+ public static final Pathname getLispHome()
{
- return LISP_HOME;
+ if (LISP_HOME == null) {
+ init();
+ }
+ return LISP_HOME;
}
// ### *lisp-home*
@@ -83,8 +79,12 @@
exportSpecial("*LISP-HOME*", PACKAGE_EXT, NIL);
static {
- String s = Site.getLispHome();
- if (s != null)
- _LISP_HOME_.setSymbolValue(new Pathname(s));
+ Pathname p = Site.getLispHome();
+ if (p != null)
+ _LISP_HOME_.setSymbolValue(p);
}
}
+
+// Local Variables:
+// c-basic-offset: 4
+// End:
diff -r 884fd6b1beec src/org/armedbear/lisp/Stream.java
--- a/src/org/armedbear/lisp/Stream.java Fri Feb 05 16:41:42 2010 +0100
+++ b/src/org/armedbear/lisp/Stream.java Fri Feb 05 18:31:59 2010 +0100
@@ -394,7 +394,7 @@
{
LispObject result = readPreservingWhitespace(eofError, eofValue,
- recursive, thread);
+ recursive, thread);
if (result != eofValue && !recursive) {
try {
if (_charReady()) {
@@ -422,9 +422,9 @@
internSpecial("*SHARP-EQUAL-ALIST*", PACKAGE_SYS, NIL);
public LispObject readPreservingWhitespace(boolean eofError,
- LispObject eofValue,
- boolean recursive,
- LispThread thread)
+ LispObject eofValue,
+ boolean recursive,
+ LispThread thread)
{
if (recursive) {
@@ -434,6 +434,7 @@
try {
n = _readChar();
} catch (IOException e) {
+ Debug.trace(e);
error(new StreamError(this, e));
}
if (n < 0) {
@@ -2557,3 +2558,6 @@
}
};
}
+// Local Variables:
+// c-basic-offset: 4
+// End:
diff -r 884fd6b1beec src/org/armedbear/lisp/Symbol.java
--- a/src/org/armedbear/lisp/Symbol.java Fri Feb 05 16:41:42 2010 +0100
+++ b/src/org/armedbear/lisp/Symbol.java Fri Feb 05 18:31:59 2010 +0100
@@ -2909,6 +2909,8 @@
PACKAGE_EXT.addExternalSymbol("GETENV");
public static final Symbol MACROEXPAND_ALL =
PACKAGE_EXT.addExternalSymbol("MACROEXPAND-ALL");
+ public static final Symbol LOAD_TRUENAME_FASL =
+ PACKAGE_EXT.addExternalSymbol("*LOAD-TRUENAME-FASL*");
// MOP.
public static final Symbol STANDARD_READER_METHOD =
diff -r 884fd6b1beec src/org/armedbear/lisp/Utilities.java
--- a/src/org/armedbear/lisp/Utilities.java Fri Feb 05 16:41:42 2010 +0100
+++ b/src/org/armedbear/lisp/Utilities.java Fri Feb 05 18:31:59 2010 +0100
@@ -33,6 +33,7 @@
package org.armedbear.lisp;
+import java.util.jar.JarFile;
import static org.armedbear.lisp.Lisp.*;
import java.io.ByteArrayInputStream;
@@ -124,60 +125,137 @@
return null;
}
}
-
- public static byte[] getZippedZipEntryAsByteArray(ZipFile zipfile,
- String entryName,
- String subEntryName)
- {
- ZipEntry entry = zipfile.getEntry(entryName);
-
- ZipInputStream stream = null;
- try {
- stream = new ZipInputStream(zipfile.getInputStream(entry));
- }
- catch (IOException e) {
+ public static ZipInputStream getZipInputStream(ZipFile zipfile,
+ String entryName) {
+ return Utilities.getZipInputStream(zipfile, entryName, false);
+ }
+
+ public static ZipInputStream getZipInputStream(ZipFile zipfile,
+ String entryName,
+ boolean errorOnFailure) {
+ ZipEntry zipEntry = zipfile.getEntry(entryName);
+ ZipInputStream stream = null;
+ try {
+ stream = new ZipInputStream(zipfile.getInputStream(zipEntry));
+ } catch (IOException e) {
+ if (errorOnFailure) {
Lisp.error(new FileError("Failed to open '" + entryName + "' in zipfile '"
+ zipfile + "': " + e.getMessage()));
}
- // XXX Cache the zipEntries somehow
- do {
- try {
- entry = stream.getNextEntry();
- } catch (IOException e){
- Lisp.error(new FileError("Failed to seek for '" + subEntryName
- + "' in '"
- + zipfile.getName() + ":" + entryName + ".:"
- + e.getMessage()));
- }
- } while (!entry.getName().equals(subEntryName));
-
- ByteArrayOutputStream buffer = new ByteArrayOutputStream();
+ return null;
+ }
+ return stream;
+ }
+
+ public static InputStream getEntryAsInputStream(ZipInputStream zipInputStream,
+ String entryName)
+ {
+ ZipEntry entry = getEntry(zipInputStream, entryName);
+ ByteArrayOutputStream bytes = readEntry(zipInputStream);
+ return new ByteArrayInputStream(bytes.toByteArray());
+
+ }
+
+ public static ByteArrayOutputStream readEntry(ZipInputStream stream) {
+ ByteArrayOutputStream result = new ByteArrayOutputStream();
int count;
byte buf[] = new byte[1024];
try {
while ((count = stream.read(buf, 0, buf.length)) != -1) {
- buffer.write(buf, 0, count);
+ result.write(buf, 0, count);
}
} catch (java.io.IOException e) {
- Lisp.error(new FileError("Failed to read compressed '"
- + subEntryName
- + "' in '"
- + zipfile.getName() + ":" + entryName + ":"
- + e.getMessage()));
+ Debug.trace("Failed to read entry from "
+ + stream
+ + ": " + e);
+ return null;
}
- return buffer.toByteArray();
+ return result;
}
+
+ public static ZipEntry getEntry(ZipInputStream zipInputStream, String entryName) {
+ return Utilities.getEntry(zipInputStream, entryName, false);
+ }
+
+ public static ZipEntry getEntry(ZipInputStream zipInputStream,
+ String entryName,
+ boolean errorOnFailure)
+ {
+ ZipEntry entry = null;
+ do {
+ try {
+ entry = zipInputStream.getNextEntry();
+ } catch (IOException e) {
+ if (errorOnFailure) {
+ Lisp.error(new FileError("Failed to seek for "
+ + "'" + entryName + "'"
+ + " in " + zipInputStream.toString()));
+ }
+ return null;
+ }
+ } while (entry != null && !entry.getName().equals(entryName));
+ if (entry != null) {
+ return entry;
+ }
+ if (errorOnFailure) {
+ Lisp.error(new FileError("Failed to find "
+ + "'" + entryName + "'"
+ + " in " + zipInputStream.toString()));
+ }
+ return null;
+
+ }
- public static InputStream getZippedZipEntryAsInputStream(ZipFile zipfile,
- String entryName,
- String subEntryName)
+ public static final boolean checkZipFile(Pathname name) {
+ InputStream input = name.getInputStream();
+ try {
+ byte[] bytes = new byte[4];
+ int bytesRead = input.read(bytes);
+ return (bytesRead == 4
+ && bytes[0] == 0x50
+ && bytes[1] == 0x4b
+ && bytes[2] == 0x03
+ && bytes[3] == 0x04);
+ } catch (Throwable t) { // any error probably means 'no'
+ return false;
+ } finally {
+ if (input != null) {
+ try {
+ input.close();
+ }
+ catch (IOException e) {} // ignore exceptions
+ }
+ }
+ }
- {
- return
- new ByteArrayInputStream(Utilities
- .getZippedZipEntryAsByteArray(zipfile, entryName,
- subEntryName));
- }
+ static InputStream getInputStream(JarFile jarFile, Pathname inner) {
+ String entryPath = inner.asEntryPath();
+ ZipEntry entry = jarFile.getEntry(entryPath);
+ if (entry == null) {
+ Debug.trace("Failed to find entry "
+ + "'" + entryPath + "'"
+ + " in "
+ + "'" + jarFile.getName() + "'");
+ return null;
+ }
+ InputStream result = null;
+ try {
+ result = jarFile.getInputStream(entry);
+ } catch (IOException e) {
+ Debug.trace("Failed to open InputStream for "
+ + "'" + entryPath + "'"
+ + " in "
+ + "'" + jarFile.getName() + "'");
+ return null;
+ }
+ return result;
+ }
+
+
+
}
+// Local Variables:
+// c-basic-offset: 4
+// End:
diff -r 884fd6b1beec src/org/armedbear/lisp/asdf-abcl.lisp
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/org/armedbear/lisp/asdf-abcl.lisp Fri Feb 05 18:31:59 2010 +0100
@@ -0,0 +1,13 @@
+(in-package :asdf)
+
+;;; We don't compile if the output location would be within a JAR
+;;; file, which is unwritable.
+(defmethod operation-done-p :around ((o compile-op)
+ (c cl-source-file))
+ (let ((files (output-files o c)))
+ (if (every #'sys:pathname-jar-p files)
+ t
+ (call-next-method))))
+
+
+(provide 'asdf-abcl)
diff -r 884fd6b1beec src/org/armedbear/lisp/asdf.lisp
--- a/src/org/armedbear/lisp/asdf.lisp Fri Feb 05 16:41:42 2010 +0100
+++ b/src/org/armedbear/lisp/asdf.lisp Fri Feb 05 18:31:59 2010 +0100
@@ -1173,4 +1173,5 @@
(pushnew 'module-provide-asdf sb-ext:*module-provider-functions*)
(pushnew 'contrib-sysdef-search *system-definition-search-functions*))
+(require 'asdf-abcl)
(provide 'asdf)
diff -r 884fd6b1beec src/org/armedbear/lisp/autoloads.lisp
--- a/src/org/armedbear/lisp/autoloads.lisp Fri Feb 05 16:41:42 2010 +0100
+++ b/src/org/armedbear/lisp/autoloads.lisp Fri Feb 05 18:31:59 2010 +0100
@@ -184,7 +184,7 @@
pathname-type wild-pathname-p pathname-match-p translate-pathname
logical-pathname-translations translate-logical-pathname
load-logical-pathname-translations logical-pathname
- parse-namestring)
+ parse-namestring pathname-jar-p)
"pathnames")
(autoload 'make-string-output-stream)
(autoload 'find-all-symbols)
diff -r 884fd6b1beec src/org/armedbear/lisp/compile-system.lisp
--- a/src/org/armedbear/lisp/compile-system.lisp Fri Feb 05 16:41:42 2010 +0100
+++ b/src/org/armedbear/lisp/compile-system.lisp Fri Feb 05 18:31:59 2010 +0100
@@ -121,12 +121,14 @@
(load (do-compile "concatenate.lisp"))
(load (do-compile "ldb.lisp"))
(load (do-compile "destructuring-bind.lisp"))
+ (load (do-compile "asdf.lisp"))
+ (load (do-compile "pathnames.lisp"))
;; But not for these.
(mapc #'do-compile '("adjoin.lisp"
"and.lisp"
"apropos.lisp"
"arrays.lisp"
- "asdf.lisp"
+ "asdf-abcl.lisp"
"assert.lisp"
"assoc.lisp"
"autoloads.lisp"
@@ -211,7 +213,6 @@
"or.lisp"
"parse-integer.lisp"
"parse-lambda-list.lisp"
- "pathnames.lisp"
"package.lisp"
"print-object.lisp"
"print-unreadable-object.lisp"
diff -r 884fd6b1beec src/org/armedbear/lisp/file_write_date.java
--- a/src/org/armedbear/lisp/file_write_date.java Fri Feb 05 16:41:42 2010 +0100
+++ b/src/org/armedbear/lisp/file_write_date.java Fri Feb 05 18:31:59 2010 +0100
@@ -51,8 +51,7 @@
Pathname pathname = coerceToPathname(arg);
if (pathname.isWild())
error(new FileError("Bad place for a wild pathname.", pathname));
- File file = Utilities.getFile(pathname);
- long lastModified = file.lastModified();
+ long lastModified = pathname.getLastModified();
if (lastModified == 0)
return NIL;
return number(lastModified / 1000 + 2208988800L);
diff -r 884fd6b1beec src/org/armedbear/lisp/pathnames.lisp
--- a/src/org/armedbear/lisp/pathnames.lisp Fri Feb 05 16:41:42 2010 +0100
+++ b/src/org/armedbear/lisp/pathnames.lisp Fri Feb 05 18:31:59 2010 +0100
@@ -399,3 +399,12 @@
(error 'type-error
:format-control "~S cannot be converted to a pathname."
:format-arguments (list thing)))))
+
+(defun pathname-jar-p (p)
+ (let ((pathname (if (pathnamep p)
+ p
+ (pathname p))))
+ (if (equal (search "jar:" (namestring pathname)) 0)
+ t
+ nil)))
+(export 'pathname-jar-p 'system)
diff -r 884fd6b1beec src/org/armedbear/lisp/unzip.java
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/org/armedbear/lisp/unzip.java Fri Feb 05 18:31:59 2010 +0100
@@ -0,0 +1,121 @@
+/*
+ * unzip.java
+ *
+ * Copyright (C) 2005 Peter Graves
+ * $Id: unzip.java 12288 2009-11-29 22:00:12Z vvoutilainen $
+ *
+ * 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.FileOutputStream;
+import java.io.IOException;
+import java.util.Enumeration;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipFile;
+
+// ### unzip pathname directory => unzipped_pathnames
+public final class unzip
+ extends Primitive
+{
+ public unzip() {
+ super("unzip", PACKAGE_SYS, true, "pathname &optional directory => unzipped_pathnames");
+ }
+
+ @Override
+ public LispObject execute(LispObject first) {
+ Pathname zipFile = coerceToPathname(first);
+ Pathname directory = coerceToPathname(Symbol.DEFAULT_PATHNAME_DEFAULTS.symbolValue());
+ return unzipToDirectory(zipFile, directory);
+ }
+
+ @Override
+ public LispObject execute(LispObject first, LispObject second) {
+ Pathname zipFile = coerceToPathname(first);
+ Pathname directory = coerceToPathname(second);
+ directory.name = NIL;
+ directory.type = NIL;
+ directory.invalidateNamestring();
+ return unzipToDirectory(zipFile, directory);
+ }
+
+ private LispObject unzipToDirectory(Pathname zipPath, Pathname dirPath) {
+ if (!zipPath.isAbsolute()) {
+ zipPath = Pathname.mergePathnames(zipPath,
+ coerceToPathname(Symbol.DEFAULT_PATHNAME_DEFAULTS.symbolValue()));
+ }
+ LispObject o = Pathname.truename(zipPath, false);
+ if (!(o instanceof Pathname)) {
+ return error(new FileError("No file found: " + zipPath, zipPath));
+ }
+ String zip = ((Pathname)o).getNamestring();
+ if (zip == null) {
+ return error(new FileError("Pathname has no namestring: " + zip, zipPath));
+ }
+ String dir = dirPath.getNamestring();
+ if (dir == null) {
+ return error(new FileError("Could not parse diretory: " + dirPath, dirPath));
+ }
+ LispObject result = NIL;
+ try {
+ ZipFile zipfile = new ZipFile(zip);
+
+ byte[] buffer = new byte[4096];
+ for (Enumeration extends ZipEntry> entries = zipfile.entries();entries.hasMoreElements();) {
+ ZipEntry entry = entries.nextElement();
+ String name = entry.getName();
+ String filename = dir + name;
+ File file = new File(filename);
+ if (entry.isDirectory()) {
+ file.mkdirs();
+ continue;
+ }
+ FileOutputStream out = new FileOutputStream(file);
+ InputStream in = zipfile.getInputStream(entry);
+ int n;
+ while ((n = in.read(buffer)) > 0) {
+ out.write(buffer, 0, n);
+ }
+ out.close();
+ in.close();
+ result = result.push(new Pathname(filename));
+ }
+ } catch (IOException e) {
+ return error(new FileError("Failed to unzip "
+ + "'" + zipPath + "'"
+ + " into " + "'" + dirPath + "'"
+ + ": " + e, zipPath));
+ }
+ return result;
+ }
+
+ private static final Primitive unzip = new unzip();
+}
diff -r 884fd6b1beec test/lisp/abcl/jar-file.lisp
--- a/test/lisp/abcl/jar-file.lisp Fri Feb 05 16:41:42 2010 +0100
+++ b/test/lisp/abcl/jar-file.lisp Fri Feb 05 18:31:59 2010 +0100
@@ -128,6 +128,50 @@
(load "jar:file:baz.jar!/a/b/eek.lisp"))
t)
+;;; wrapped in PROGN for easy disabling without a network connection
+;;; XXX come up with a better abstraction
+(progn
+ (deftest jar-file.load.11
+ (load "jar:http://abcl-dynamic-install.googlecode.com/files/baz.jar!/foo")
+ t)
+
+ (deftest jar-file.load.12
+ (load "jar:http://abcl-dynamic-install.googlecode.com/files/baz.jar!/bar")
+ t)
+
+ (deftest jar-file.load.13
+ (load "jar:http://abcl-dynamic-install.googlecode.com/files/baz.jar!/bar.abcl")
+ t)
+
+ (deftest jar-file.load.14
+ (load "jar:http://abcl-dynamic-install.googlecode.com/files/baz.jar!/eek")
+ t)
+
+ (deftest jar-file.load.15
+ (load "jar:http://abcl-dynamic-install.googlecode.com/files/baz.jar!/eek.lisp")
+ t)
+
+ (deftest jar-file.load.16
+ (load "jar:http://abcl-dynamic-install.googlecode.com/files/baz.jar!/a/b/foo")
+ t)
+
+ (deftest jar-file.load.17
+ (load "jar:http://abcl-dynamic-install.googlecode.com/files/baz.jar!/a/b/bar")
+ t)
+
+ (deftest jar-file.load.18
+ (load "jar:http://abcl-dynamic-install.googlecode.com/files/baz.jar!/a/b/bar.abcl")
+ t)
+
+ (deftest jar-file.load.19
+ (load "jar:http://abcl-dynamic-install.googlecode.com/files/baz.jar!/a/b/eek")
+ t)
+
+ (deftest jar-file.load.20
+ (load "jar:http://abcl-dynamic-install.googlecode.com/files/baz.jar!/a/b/eek.lisp")
+ t))
+
+
(deftest jar-file.probe-file.1
(with-jar-file-init
(probe-file "jar:file:baz.jar!/eek.lisp"))
@@ -164,7 +208,7 @@
(deftest jar-file.merge-pathnames.2
(merge-pathnames
- "/bar.abcl" #p"jar:file:baz.jar!/foo/")
+ "bar.abcl" #p"jar:file:baz.jar!/foo/")
#p"jar:file:baz.jar!/foo/bar.abcl")
(deftest jar-file.merge-pathnames.3
@@ -172,6 +216,11 @@
"jar:file:baz.jar!/foo" "bar")
#p"jar:file:baz.jar!/foo")
+(deftest jar-file.merge-pathnames.4
+ (merge-pathnames
+ "jar:file:baz.jar!/foo" "/a/b/c")
+ #p"jar:file:/a/b/baz.jar!/foo")
+
(deftest jar-file.truename.1
(signals-error (truename "jar:file:baz.jar!/foo")
'file-error)
diff -r 884fd6b1beec test/src/org/armedbear/lisp/PathnameTest.java
--- a/test/src/org/armedbear/lisp/PathnameTest.java Fri Feb 05 16:41:42 2010 +0100
+++ b/test/src/org/armedbear/lisp/PathnameTest.java Fri Feb 05 18:31:59 2010 +0100
@@ -38,7 +38,7 @@
@Test
public void getInputStream() throws IOException {
- File file = File.createTempFile("foo", "lisp");
+ File file = File.createTempFile("foo", ".lisp");
FileWriter output = new FileWriter(file);
String contents = "(defun foo () 42)";
output.append(contents);
@@ -53,6 +53,52 @@
result.append(buffer, 0, i);
}
assertEquals(contents, result.toString());
+ input.close();
file.delete();
}
+
+ @Test
+ public void copyConstructor() {
+ Pathname orig = new Pathname("/a/b/c/d/e/foo.lisp");
+ Pathname copy = new Pathname(orig.getNamestring());
+ assertTrue(orig.getNamestring().equals(copy.getNamestring()));
+ }
+
+ @Test
+ public void mergePathnames1() {
+ Pathname p = new Pathname("a/b/c/d/foo.lisp");
+ Pathname d = new Pathname("/foo/bar/there");
+ Pathname r = Pathname.mergePathnames(p, d);
+ String s = r.getNamestring();
+ assertTrue(s.equals("/foo/bar/a/b/c/d/foo.lisp"));
+ }
+
+ @Test
+ public void mergePathnames2() {
+ Pathname p = new Pathname("/a/b/c/d/foo.lisp");
+ Pathname d = new Pathname("/foo/bar/there");
+ Pathname r = Pathname.mergePathnames(p, d);
+ assertTrue(r.getNamestring().equals("/a/b/c/d/foo.lisp"));
+ }
+
+ @Test
+ public void mergePathnames3() {
+ LispObject args = Lisp.NIL;
+ args = args.push(Keyword.TYPE);
+ args = args.push(new SimpleString("abcl-tmp"));
+ args = args.nreverse();
+ Pathname p = Pathname.makePathname(args);
+ Pathname d = new Pathname("/foo/bar.abcl");
+ Pathname r = Pathname.mergePathnames(p, d);
+ assertTrue(r.getNamestring().equals("/foo/bar.abcl-tmp"));
+ }
+
+ @Test
+ public void mergePathnames4() {
+ Pathname p = new Pathname("jar:file:foo.jar!/bar.abcl");
+ Pathname d = new Pathname("/a/b/c/");
+ Pathname r = Pathname.mergePathnames(p, d);
+ String s = r.getNamestring();
+ assertTrue(s.equals("jar:file:/a/b/c/foo.jar!/bar.abcl"));
+ }
}
diff -r 884fd6b1beec test/src/org/armedbear/lisp/StreamTest.java
--- a/test/src/org/armedbear/lisp/StreamTest.java Fri Feb 05 16:41:42 2010 +0100
+++ b/test/src/org/armedbear/lisp/StreamTest.java Fri Feb 05 18:31:59 2010 +0100
@@ -26,6 +26,7 @@
Stream in = new Stream(Symbol.SYSTEM_STREAM, pathname.getInputStream(), Symbol.CHARACTER);
LispObject o = in.read(false, Lisp.EOF, false, LispThread.currentThread());
assertFalse(o.equals(Lisp.NIL));
+ in._close();
file.delete();
}
}
\ No newline at end of file
diff -r 884fd6b1beec test/src/org/armedbear/lisp/UtilitiesTest.java
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/src/org/armedbear/lisp/UtilitiesTest.java Fri Feb 05 18:31:59 2010 +0100
@@ -0,0 +1,53 @@
+package org.armedbear.lisp;
+
+import java.io.FileNotFoundException;
+import static org.junit.Assert.*;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileWriter;
+import org.junit.Test;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.jar.JarFile;
+import java.util.jar.JarInputStream;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipInputStream;
+import org.junit.Before;
+
+public class UtilitiesTest
+{
+ File zipFile;
+
+
+ @Before
+ public void setup() {
+ // XXX currently created by the ABCL Lisp based tests
+ zipFile = new File("test/lisp/abcl/baz.jar");
+ assertTrue(zipFile.canRead());
+ }
+
+
+ @Test
+ public void getZipEntry() throws FileNotFoundException, IOException {
+ FileInputStream inputFile = new FileInputStream(zipFile);
+ ZipInputStream input = new ZipInputStream(inputFile);
+ ZipEntry entry = Utilities.getEntry(input, "a/b/bar.abcl");
+ assertNotNull(entry);
+ input.close();
+ inputFile.close();
+ }
+
+ @Test
+ public void getZipInputStreamZipEntry() throws FileNotFoundException, IOException {
+ JarFile jar = new JarFile(zipFile);
+ Pathname pathname = new Pathname("a/b/bar.abcl");
+ InputStream entryInputStream = Utilities.getInputStream(jar, pathname);
+ assertNotNull(entryInputStream);
+ ZipInputStream zip = new ZipInputStream(entryInputStream);
+ assertNotNull(zip);
+ ZipEntry entry = Utilities.getEntry(zip, "bar._");
+ assertNotNull(entry);
+ }
+
+}
\ No newline at end of file