[armedbear-cvs] r13056 - trunk/abcl/src/org/armedbear/lisp

Mark Evenson mevenson at common-lisp.net
Sat Nov 27 11:03:35 UTC 2010


Author: mevenson
Date: Sat Nov 27 06:03:34 2010
New Revision: 13056

Log:
Fix problems with #\Space characters in JAR pathnames.

We now require that inputs to the PATHNAME routines that have the URI
scheme "jar:file" or "file" properly encode themselves as URIs
according to RFC2396.  Mainly this means that #\Space and #\?
characters in such strings should be percent encoded
(i.e. "jar:file:/path%20with%20/space/and%3fquestion-mark").  The
corresponding namestring routines have been adjusted to output such
URI encoded representations, although the underlying PATHNAME objects
contain unescaped values.  The routines for loading FASLs have been
adjusted to URI encode their inputs as well.

The #\+ character is no longer an escape for #\Space (this was a bug).

Modified:
   trunk/abcl/src/org/armedbear/lisp/Load.java
   trunk/abcl/src/org/armedbear/lisp/Pathname.java
   trunk/abcl/src/org/armedbear/lisp/Utilities.java

Modified: trunk/abcl/src/org/armedbear/lisp/Load.java
==============================================================================
--- trunk/abcl/src/org/armedbear/lisp/Load.java	(original)
+++ trunk/abcl/src/org/armedbear/lisp/Load.java	Sat Nov 27 06:03:34 2010
@@ -153,6 +153,7 @@
 
         if (Utilities.checkZipFile(truename)) {
             String n = truename.getNamestring();
+            n = Pathname.uriEncode(n);
             if (n.startsWith("jar:")) {
                 n = "jar:" + n + "!/" + truename.name.getStringValue() + "."
                     + COMPILE_FILE_INIT_FASL_TYPE;

Modified: trunk/abcl/src/org/armedbear/lisp/Pathname.java
==============================================================================
--- trunk/abcl/src/org/armedbear/lisp/Pathname.java	(original)
+++ trunk/abcl/src/org/armedbear/lisp/Pathname.java	Sat Nov 27 06:03:34 2010
@@ -38,12 +38,14 @@
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.FileInputStream;
+import java.io.UnsupportedEncodingException;
 import java.net.MalformedURLException;
 import java.net.URI;
 import java.net.URISyntaxException;
 import java.net.URL;
 import java.net.URLDecoder;
 import java.net.URLConnection;
+import java.net.URLEncoder;
 import java.util.Enumeration;
 import java.util.StringTokenizer;
 import java.util.zip.ZipEntry;
@@ -195,28 +197,10 @@
     }
 
     public Pathname(URL url) {
-        if ("file".equals(url.getProtocol())) {
-            String s = url.getPath();
-            if (s != null) {
-                if (Utilities.isPlatformWindows) {
-                    //  Workaround for 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;
-            }
-        } else {
-            init(url.toString());
-            return;
-        }
-        error(new LispError("Failed to construct Pathname from URL: "
-                            + "'" + url.toString() + "'"));
+         // URL handling is now buried in init(String), as the URI
+         // escaping mechanism didn't interact well with '+' and other
+         // characters. 
+        init(url.toString());
     }
 
     static final Symbol SCHEME = internKeyword("SCHEME");
@@ -279,19 +263,45 @@
                 jars = jars.push(p.device.car());
             }
             if (jar.startsWith("jar:file:")) {
-                String jarString 
-                    = jar.substring("jar:".length(),
+                String file
+                    = jar.substring("jar:file:".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;
+                if (file.length() > 0) {
+                    // Instead of "use URL constructor to normalize Windows' use of device"
+                    // attempt to shorten the URL to pass through the normal constructor.
+                    if (Utilities.isPlatformWindows
+                        && file.charAt(0) == '/'
+                        && file.charAt(2) == ':'
+                        && Character.isLetter(file.charAt(1)))
+                        {
+                            file = file.substring(1);
+                        }
+                    URL url = null;
+                    URI uri = null;
+                    try {
+                        url = new URL("file:" + file);
+                        uri = url.toURI();
+                    } catch (MalformedURLException e1) {
+                        error(new FileError("Failed to create URI from "
+                                            + "'" + file + "'"
+                                            + ": " + e1.getMessage()));
+                    } catch (URISyntaxException e2) {
+                        error(new FileError("Failed to create URI from "
+                                            + "'" + file + "'"
+                                            + ": " + e2.getMessage()));
+                    }
+                    String path = uri.getPath();
+                    if (path == null) {
+                        // We allow "jar:file:baz.jar!/" to construct a relative
+                        // path for jar files, so MERGE-PATHNAMES means something.
+                        jarPathname = new Pathname(uri.getSchemeSpecificPart());
+                    } else {
+                        jarPathname = new Pathname(path);
+                    }
+                } else {
+                    jarPathname = new Pathname("");
                 }
-                Pathname jarPathname = new Pathname(url);
                 jars = jars.push(jarPathname);
             } else {
                 URL url = null;
@@ -315,7 +325,15 @@
         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);
+            URL url = null;
+            try {
+                url = new URL(jarURL);
+            } catch (MalformedURLException ex) {
+                error(new FileError("Failed to parse URL "
+                                    + "'" + jarURL + "'"
+                                    + ex.getMessage()));
+            }
+            Pathname d = new Pathname(url);
             if (device instanceof Cons) {
                 LispObject[] jars = d.copyToArray();
                 //  XXX Is this ever reached?  If so, need to append lists
@@ -342,7 +360,15 @@
             }
             String scheme = url.getProtocol();
             if (scheme.equals("file")) {
-                Pathname p = new Pathname(url.getFile());
+                URI uri = null;
+                try {
+                    uri = url.toURI();
+                } catch (URISyntaxException ex) {
+                    error(new FileError("Improper URI syntax for "
+                                    + "'" + url.toString() + "'"
+                                    + ": " + ex.toString()));
+                }
+                Pathname p = new Pathname(uri.getPath());
                 this.host = p.host;
                 this.device = p.device;
                 this.directory = p.directory;
@@ -596,6 +622,7 @@
                 return null;
             }
         }
+        boolean uriEncoded = false;
         if (device == NIL) {
         } else if (device == Keyword.UNSPECIFIC) {
         } else if (isJar()) {
@@ -605,8 +632,16 @@
                 prefix.append("jar:");
                 if (!((Pathname)jars[i]).isURL() && i == 0) {
                     sb.append("file:");
+                    uriEncoded = true;
+                }
+                Pathname jar = (Pathname) jars[i];
+                String encodedNamestring;
+                if (uriEncoded) {
+                    encodedNamestring = uriEncode(jar.getNamestring());
+                } else { 
+                    encodedNamestring = jar.getNamestring();
                 }
-                sb.append(((Pathname) jars[i]).getNamestring());
+                sb.append(encodedNamestring);
                 sb.append("!/");
             }
             sb = prefix.append(sb);
@@ -620,6 +655,9 @@
             Debug.assertTrue(false);
         }
         String directoryNamestring = getDirectoryNamestring();
+        if (uriEncoded) {
+            directoryNamestring = uriEncode(directoryNamestring);
+        }
         if (isJar()) {
             if (directoryNamestring.startsWith("/")) {
                 sb.append(directoryNamestring.substring(1));
@@ -635,7 +673,11 @@
                 Debug.assertTrue(namestring == null);
                 return null;
             }
-            sb.append(n);
+            if (uriEncoded) {
+                sb.append(uriEncode(n));
+            } else {
+                sb.append(n);
+            }
         } else if (name == Keyword.WILD) {
             sb.append('*');
         }
@@ -650,7 +692,11 @@
                         return null;
                     }
                 }
-                sb.append(t);
+                if (uriEncoded) {
+                    sb.append(uriEncode(t));
+                } else {
+                    sb.append(t);
+                }
             } else if (type == Keyword.WILD) {
                 sb.append('*');
             } else {
@@ -1981,7 +2027,12 @@
                 LispObject truename = Pathname.truename((Pathname)o, errorIfDoesNotExist);
                 if (truename != null
                     && truename instanceof Pathname) {
-                    jars.car = (Pathname)truename;
+                    Pathname truePathname = (Pathname)truename;
+                    // A jar that is a directory makes no sense, so exit
+                    if (truePathname.getNamestring().endsWith("/")) {
+                        break jarfile;
+                    }
+                    jars.car = truePathname;
                 } else {
                     break jarfile;
                 }
@@ -1994,6 +2045,7 @@
             // 2.  JAR in JAR
             // 3.  JAR with Entry
             // 4.  JAR in JAR with Entry
+
             ZipFile jarFile = ZipCache.get((Pathname)jars.car());
             String entryPath = pathname.asEntryPath();
             if (jarFile != null) {
@@ -2350,5 +2402,34 @@
         Symbol.DEFAULT_PATHNAME_DEFAULTS.setSymbolValue(coerceToPathname(obj));
     }
 
+    static String uriDecode(String s) {
+        try {
+            URI uri = new URI(null, null, null, s, null);
+            return uri.toASCIIString().substring(1);
+        } catch (URISyntaxException e) {}
+        return null;  // Error
+    }
+
+    static String uriEncode(String s) {
+        // The constructor we use here only allows absolute paths, so
+        // we manipulate the input and output correspondingly.
+        String u;
+        if (!s.startsWith("/")) {
+            u = "/" + s;
+        } else {
+            u = new String(s);
+        }
+        try {
+            URI uri = new URI("file", "", u, "");
+            String result = uri.getRawPath();
+            if (!s.startsWith("/")) {
+                return result.substring(1);
+            } 
+            return result;
+        } catch (URISyntaxException e) {
+            Debug.assertTrue(false);
+        }
+        return null; // Error
+    }
 }
 

Modified: trunk/abcl/src/org/armedbear/lisp/Utilities.java
==============================================================================
--- trunk/abcl/src/org/armedbear/lisp/Utilities.java	(original)
+++ trunk/abcl/src/org/armedbear/lisp/Utilities.java	Sat Nov 27 06:03:34 2010
@@ -254,22 +254,6 @@
         return result;
     }
 
-    static String uriEncode(String s) {
-        try {
-            URI uri = new URI("?" + s);
-            return uri.getQuery();
-        } catch (URISyntaxException e) {}
-        return null;
-    }
-
-    static String uriDecode(String s) {
-        try {
-            URI uri = new URI(null, null, null, s, null);
-            return uri.toASCIIString().substring(1);
-        } catch (URISyntaxException e) {}
-        return null;  // Error
-    }
-    
     static String escapeFormat(String s) {
         return s.replace("~", "~~");
     }




More information about the armedbear-cvs mailing list