Changeset 12607


Ignore:
Timestamp:
04/15/10 14:27:09 (12 years ago)
Author:
Mark Evenson
Message:

URL pathnames working for OPEN for built-in schemas.

Still need to decide with URI escaping issues, as we currently rely on
the URL Stream handlers to do the right thing. And we still need to
retrofit jar pathname's use of a string to represent a URL.

Updates for URL and jar pathname design documents.

Implemented URL-PATHNAME and JAR-PATHNAME as subtypes of PATHNAME.

Adjusted ABCL-TEST-LISP to use functions provided in
"pathname-test.lisp" in "jar-file.lisp". Added one test for url
pathnames.

Constructor in Java added for a Cons by copying references from the
orignal Cons.

Location:
trunk/abcl
Files:
14 edited

Legend:

Unmodified
Added
Removed
  • trunk/abcl/abcl.asd

    r12509 r12607  
    3636                      (:file "mop-tests" :depends-on ("mop-tests-setup"))
    3737                      (:file "file-system-tests")
    38                       (:file "jar-file")
     38                      (:file "jar-file" :depend-on ("pathname-test"))
    3939                      (:file "math-tests")
    4040                      (:file "misc-tests")
  • trunk/abcl/doc/design/pathnames/jar-pathnames.markdown

    r12560 r12607  
    44    Mark Evenson
    55    Created:  09 JAN 2010
    6     Modified: 16 MAR 2010
    7 
    8 Notes towards sketching an implementation of "jar:" references to be
    9 contained in Common Lisp `PATHNAMEs` within ABCL. 
     6    Modified: 25 MAR 2010
     7
     8Notes towards an implementation of "jar:" references to be contained
     9in Common Lisp `PATHNAME`s within ABCL.
    1010
    1111Goals
     
    5252    resolve works.
    5353
    54 7.  Make jar pathnames work as a valid argument for OPEN.
     547.  Make jar pathnames work as a valid argument for OPEN with
     55:DIRECTION :INPUT.
    5556
    56578.  Enable the loading of ASDF systems packaged within jar files.
     58
     599.  Enable the matching of jar pathnames with PATHNAME-MATCH-P
     60
     61        (pathname-match-p
     62          "jar:file:/a/b/some.jar!/a/system/def.asd"
     63          "jar:file:/**/*.jar!/**/*.asd")     
     64        ==> t
    5765
    5866Status
    5967------
    6068
    61 As of svn r12501, all the above goals have been implemented and tested
    62 *except* for:
    63 
    64 7.  Make jar pathnames work as a valid argument for OPEN.
     69As of svn r125??, all the above goals have been implemented and
     70tested.
    6571
    6672
     
    6874--------------
    6975
    70 Using PATHNAMES
    71 
    72 *   A PATHNAME refering to a file within a JAR is known as a JAR
    73     PATHNAME.  It can either refer to the entire JAR file or an entry
    74     within the JAR file.
    75 
    76 *   A JAR PATHNAME always has a DEVICE which is a proper list.  This
    77     distinguishes it from other uses of Pathname. 
    78 
    79 *   The DEVICE of a JAR PATHNAME will be a list with either one or two
    80     elements.  The first element of the JAR PATHNAME can be either a
    81     PATHNAME representing a JAR on the filesystem, or a SimpleString
    82     representing a URL.
    83 
    84 *   a PATHNAME occuring in the list in the DEVICE of a JAR PATHNAME is
    85     known as a DEVICE PATHNAME.
    86 
    87 *   If the DEVICE is a String it must be a String that successfully
    88     references a URL via the java.net.URL(String) constructor
    89 
    90 *   Only the first entry in the the DEVICE list may be a String.
    91 
    92 *   Otherwise the the DEVICE PATHAME denotes the PATHNAME of the JAR file
    93 
    94 *   The DEVICE PATHNAME list of enclosing JARs runs from outermost to
    95     innermost.
     76A PATHNAME refering to a file within a JAR is known as a JAR PATHNAME.
     77It can either refer to the entire JAR file or an entry within the JAR
     78file.
     79
     80A JAR PATHNAME always has a DEVICE which is a proper list.  This
     81distinguishes it from other uses of Pathname.
     82
     83The DEVICE of a JAR PATHNAME will be a list with either one or two
     84elements.  The first element of the JAR PATHNAME can be either a
     85PATHNAME representing a JAR on the filesystem, or a SimpleString
     86representing a URL.
     87
     88A PATHNAME occuring in the list in the DEVICE of a JAR PATHNAME is
     89known as a DEVICE PATHNAME.
     90
     91If the DEVICE is a String it must be a String that successfully
     92references a URL via the java.net.URL(String) constructor
     93
     94Only the first entry in the the DEVICE list may be a String.
     95
     96Otherwise the the DEVICE PATHAME denotes the PATHNAME of the JAR file.
     97
     98The DEVICE PATHNAME list of enclosing JARs runs from outermost to
     99innermost.
    96100   
    97 *   The DIRECTORY component of a JAR PATHNAME should be a list starting
    98     with the :ABSOLUTE keyword.  Even though hierarchial entries in
    99     jar files are stored in the form "foo/bar/a.lisp" not
    100     "/foo/bar/a.lisp", the meaning of DIRECTORY component better
    101     represented as an absolute path.
     101The DIRECTORY component of a JAR PATHNAME should be a list starting
     102with the :ABSOLUTE keyword.  Even though hierarchial entries in jar
     103files are stored in the form "foo/bar/a.lisp" not "/foo/bar/a.lisp",
     104the meaning of DIRECTORY component better represented as an absolute
     105path.
     106
     107A jar Pathname has type JAR-PATHNAME, derived from PATHNAME.
    102108
    103109BNF
  • trunk/abcl/doc/design/pathnames/url-pathnames.markdown

    r12573 r12607  
    111111conventions of a URL.
    112112
     113A URL Pathname has type URL-PATHNAME, derived from PATHNAME.
    113114
    114115Status
  • trunk/abcl/src/org/armedbear/lisp/BuiltInClass.java

    r12575 r12607  
    114114  public static final BuiltInClass PACKAGE              = addClass(Symbol.PACKAGE);
    115115  public static final BuiltInClass PATHNAME             = addClass(Symbol.PATHNAME);
     116  public static final BuiltInClass JAR_PATHNAME         = addClass(Symbol.JAR_PATHNAME);
     117  public static final BuiltInClass URL_PATHNAME         = addClass(Symbol.URL_PATHNAME);
    116118  public static final BuiltInClass RANDOM_STATE         = addClass(Symbol.RANDOM_STATE);
    117119  public static final BuiltInClass RATIO                = addClass(Symbol.RATIO);
     
    179181    addClass(Symbol.FILE_STREAM,
    180182             new StructureClass(Symbol.FILE_STREAM, list(SYSTEM_STREAM)));
     183  public static final LispClass JAR_STREAM =
     184    addClass(Symbol.JAR_STREAM,
     185             new StructureClass(Symbol.JAR_STREAM, list(SYSTEM_STREAM)));
     186  public static final LispClass URL_STREAM =
     187    addClass(Symbol.URL_STREAM,
     188             new StructureClass(Symbol.URL_STREAM, list(SYSTEM_STREAM)));
    181189  public static final LispClass CONCATENATED_STREAM =
    182190    addClass(Symbol.CONCATENATED_STREAM,
     
    231239    FILE_STREAM.setCPL(FILE_STREAM, SYSTEM_STREAM, STREAM,
    232240                       STRUCTURE_OBJECT, CLASS_T);
     241    JAR_STREAM.setCPL(JAR_STREAM, SYSTEM_STREAM, STREAM,
     242                      STRUCTURE_OBJECT, CLASS_T);
     243    URL_STREAM.setCPL(URL_STREAM, SYSTEM_STREAM, STREAM,
     244                      STRUCTURE_OBJECT, CLASS_T);
    233245    FLOAT.setDirectSuperclass(REAL);
    234246    FLOAT.setCPL(FLOAT, REAL, NUMBER, CLASS_T);
     
    261273    PATHNAME.setDirectSuperclass(CLASS_T);
    262274    PATHNAME.setCPL(PATHNAME, CLASS_T);
     275    JAR_PATHNAME.setDirectSuperclass(PATHNAME);
     276    JAR_PATHNAME.setCPL(JAR_PATHNAME, PATHNAME, CLASS_T);
     277    URL_PATHNAME.setDirectSuperclass(PATHNAME);
     278    URL_PATHNAME.setCPL(URL_PATHNAME, PATHNAME, CLASS_T);
    263279    RANDOM_STATE.setDirectSuperclass(CLASS_T);
    264280    RANDOM_STATE.setCPL(RANDOM_STATE, CLASS_T);
  • trunk/abcl/src/org/armedbear/lisp/Cons.java

    r12598 r12607  
    5959    this.car = new SimpleString(name);
    6060    this.cdr = value != null ? value : NULL_VALUE;
     61    ++count;
     62  }
     63
     64  public Cons(Cons original)
     65  {
     66    Cons rest = original;
     67    LispObject result = NIL;
     68    while (rest.car() != NIL) {
     69      result = result.push(rest.car());
     70      if (rest.cdr() == NIL) {
     71        result = result.push(NIL);
     72        break;
     73      }
     74      rest = (Cons) rest.cdr();
     75    }
     76    result = result.nreverse();
     77    this.car = result.car();
     78    this.cdr = result.cdr();
    6179    ++count;
    6280  }
  • trunk/abcl/src/org/armedbear/lisp/FileStream.java

    r12422 r12607  
    287287                return type_error(first, Symbol.PATHNAME);
    288288            }
    289             if (pathname.isJar()) {
    290                 error(new FileError("Direct stream input/output on entries in JAR files no currently supported.",
    291                                     pathname));
    292             }
    293 
    294289            final LispObject namestring = checkString(second);
    295290            LispObject elementType = third;
     
    301296                direction != Keyword.IO)
    302297                error(new LispError("Direction must be :INPUT, :OUTPUT, or :IO."));
    303             try {
    304                 return new FileStream(pathname, namestring.getStringValue(),
    305                                       elementType, direction, ifExists,
    306                                       externalFormat);
    307             }
    308             catch (FileNotFoundException e) {
    309                 return NIL;
    310             }
    311             catch (IOException e) {
    312                 return error(new StreamError(null, e));
     298
     299            if (pathname.isJar())  {
     300                if (direction != Keyword.INPUT) {
     301                    error(new FileError("Only direction :INPUT is supported for jar files.", pathname));
     302                }
     303                try {
     304                    return new JarStream(pathname, namestring.getStringValue(),
     305                                         elementType, direction, ifExists,
     306                                         externalFormat);
     307                } catch (IOException e) {
     308                    return error(new StreamError(null, e));
     309                }
     310            } else if (pathname.isURL()) {
     311                if (direction != Keyword.INPUT) {
     312                    error(new FileError("Only direction :INPUT is supported for URLs.", pathname));
     313                }
     314                try {
     315                    return new URLStream(pathname, namestring.getStringValue(),
     316                                         elementType, direction, ifExists,
     317                                         externalFormat);
     318                } catch (IOException e) {
     319                    return error(new StreamError(null, e));
     320                }
     321            } else {
     322                try {
     323                    return new FileStream(pathname, namestring.getStringValue(),
     324                                          elementType, direction, ifExists,
     325                                          externalFormat);
     326                }
     327                catch (FileNotFoundException e) {
     328                    return NIL;
     329                }
     330                catch (IOException e) {
     331                    return error(new StreamError(null, e));
     332                }
    313333            }
    314334        }
  • trunk/abcl/src/org/armedbear/lisp/Lisp.java

    r12599 r12607  
    17421742    if (arg instanceof FileStream)
    17431743      return ((FileStream)arg).getPathname();
     1744    if (arg instanceof JarStream)
     1745      return ((JarStream)arg).getPathname();
     1746    if (arg instanceof URLStream)
     1747      return ((URLStream)arg).getPathname();
    17441748    type_error(arg, list(Symbol.OR, Symbol.PATHNAME,
    1745                                Symbol.STRING, Symbol.FILE_STREAM));
     1749                         Symbol.STRING, Symbol.FILE_STREAM,
     1750                         Symbol.JAR_STREAM, Symbol.URL_STREAM));
    17461751    // Not reached.
    17471752    return null;
  • trunk/abcl/src/org/armedbear/lisp/Load.java

    r12597 r12607  
    463463                if (type.equals(COMPILE_FILE_TYPE)
    464464                    || type.equals(COMPILE_FILE_INIT_FASL_TYPE.toString())) {
    465                     thread.bindSpecial(Symbol.LOAD_TRUENAME_FASL, truePathname);
     465                    Pathname truenameFasl = new Pathname(truePathname);
     466                    thread.bindSpecial(Symbol.LOAD_TRUENAME_FASL, truenameFasl);
    466467                }
    467468                if (truePathname.type.getStringValue()
     
    469470                    && truePathname.isJar()) {
    470471                    if (truePathname.device.cdr() != NIL ) {
    471                         // set truename to the enclosing JAR
     472                        // We set *LOAD-TRUENAME* to the argument that
     473                        // a user would pass to LOAD.
     474                        Pathname enclosingJar = (Pathname)truePathname.device.cdr().car();
     475                        truePathname.device = new Cons(truePathname.device.car(), NIL);
    472476                        truePathname.host = NIL;
    473                         truePathname.directory = NIL;
    474                         truePathname.name = NIL;
    475                         truePathname.type = NIL;
     477                        truePathname.directory = enclosingJar.directory;
     478                        if (truePathname.directory.car().equals(Keyword.RELATIVE)) {
     479                            truePathname.directory.setCar(Keyword.ABSOLUTE);
     480                        }
     481                        truePathname.name = enclosingJar.name;
     482                        truePathname.type = enclosingJar.type;
    476483                        truePathname.invalidateNamestring();
    477484                    } else {
  • trunk/abcl/src/org/armedbear/lisp/Pathname.java

    r12551 r12607  
    4040import java.io.FileInputStream;
    4141import java.net.MalformedURLException;
     42import java.net.URI;
     43import java.net.URISyntaxException;
    4244import java.net.URL;
    4345import java.net.URLDecoder;
     46import java.net.URLConnection;
    4447import java.util.Enumeration;
    4548import java.util.StringTokenizer;
     
    6568     *  We could do this with setter/getters, but that choose not to in order to avoid the
    6669     *  performance indirection penalty.
     70     *
     71     *  Although, given the number of bugs that crop up when this
     72     *  protocol is not adhered to, maybe we should consider it.
    6773     */
    6874    public void invalidateNamestring() {
     
    7985            } else  if (p.host instanceof Symbol) {
    8086                host = p.host;
     87            } else if (p.host instanceof Cons) {
     88                host = new Cons((Cons)p.host);
    8189            } else {
    8290                Debug.assertTrue(false);
     
    153161
    154162    public static boolean isSupportedProtocol(String protocol) {
    155         return "jar".equals(protocol) || "file".equals(protocol);
     163        // There is no programmatic way to know what protocols will
     164        // sucessfully construct a URL, so we check for well known ones...
     165        if ("jar".equals(protocol)
     166            || "file".equals(protocol))
     167            //            || "http".equals(protocol))  XXX remove this as an optimization
     168            {
     169                return true;
     170            }
     171        // ... and try the entire constructor with some hopefully
     172        // reasonable parameters for everything else.
     173        try {
     174            new URL(protocol, "example.org", "foo");
     175            return true;
     176        }  catch (MalformedURLException e) {
     177            return false;
     178        }
    156179    }
    157180
    158181    public Pathname(URL url) {
    159         String protocol = url.getProtocol();
    160         if (!isSupportedProtocol(protocol)) {
    161             error(new LispError("Unsupported URL: '" + url.toString() + "'"));
    162         }
    163 
    164         if ("jar".equals(protocol)) {
    165             init(url.toString());
    166             return;
    167         } else if ("file".equals(protocol)) {
     182        if ("file".equals(url.getProtocol())) {
    168183            String s;
    169184            try {
     
    189204                return;
    190205            }
     206        } else {
     207            init(url.toString());
     208            return;
    191209        }
    192210        error(new LispError("Failed to construct Pathname from URL: "
    193211                            + "'" + url.toString() + "'"));
    194212    }
     213
     214    static final Symbol SCHEME = internKeyword("SCHEME");
     215    static final Symbol AUTHORITY = internKeyword("AUTHORITY");
    195216
    196217    static final private String jarSeparator = "!/";
     
    231252            }
    232253        }
    233 
     254       
    234255        // A JAR file
    235256        if (s.startsWith("jar:") && s.endsWith(jarSeparator)) {
     
    303324            type = p.type;
    304325            version = p.version;
     326            return;
     327        }
     328
     329        // A URL
     330        if (isValidURL(s)) {
     331            URL url = null;
     332            try {
     333                url = new URL(s);
     334            } catch (MalformedURLException e) {
     335                Debug.assertTrue(false);
     336            }
     337            String scheme = url.getProtocol();
     338            Debug.assertTrue(scheme != null);
     339            String authority = url.getAuthority();
     340            Debug.assertTrue(authority != null);
     341
     342            host = NIL;
     343            host = host.push(SCHEME);
     344            host = host.push(new SimpleString(scheme));
     345            host = host.push(AUTHORITY);
     346            host = host.push(new SimpleString(authority));
     347            host = host.nreverse();
     348
     349            device = NIL;
     350           
     351            // URI encode necessary characters
     352            URI uri = null;
     353            try {
     354                uri = url.toURI().normalize();
     355            } catch (URISyntaxException e) {
     356                error(new LispError("Could not URI escape characters in "
     357                                    + "'" + url + "'"
     358                                    + " because: " + e));
     359            }
     360
     361            String path = uri.getRawPath();
     362            if (path == null) {
     363                path = "";
     364            }
     365            String query = uri.getRawQuery();
     366            if (query != null) {
     367                path += "?" + query;
     368            }
     369            String fragment = uri.getRawFragment();
     370            if (fragment != null) {
     371                path += "#" + fragment;
     372            }
     373            Pathname p = new Pathname(path != null ? path : "");
     374
     375            directory = p.directory;
     376            name = p.name;
     377            type = p.type;
     378           
    305379            return;
    306380        }
     
    447521    @Override
    448522    public LispObject typeOf() {
     523        if (isURL()) {
     524            return Symbol.URL_PATHNAME;
     525        }
     526        if (isJar()) {
     527            return Symbol.JAR_PATHNAME;
     528        }
    449529        return Symbol.PATHNAME;
    450530    }
     
    452532    @Override
    453533    public LispObject classOf() {
     534        if (isURL()) {
     535            return BuiltInClass.URL_PATHNAME;
     536        }
     537        if (isJar()) {
     538            return BuiltInClass.JAR_PATHNAME;
     539        }
    454540        return BuiltInClass.PATHNAME;
    455541    }
     
    460546            return T;
    461547        }
     548        if (type == Symbol.JAR_PATHNAME && isJar()) {
     549            return T;
     550        }
     551        if (type == Symbol.URL_PATHNAME && isURL()) {
     552            return T;
     553        }
    462554        if (type == BuiltInClass.PATHNAME) {
     555            return T;
     556        }
     557        if (type == BuiltInClass.JAR_PATHNAME && isJar()) {
     558            return T;
     559        }
     560        if (type == BuiltInClass.URL_PATHNAME && isURL()) {
    463561            return T;
    464562        }
     
    487585        // the namestring." 19.2.2.2.3.1
    488586        if (host != NIL) {
    489             Debug.assertTrue(host instanceof AbstractString);
    490             if (!(this instanceof LogicalPathname)) {
    491                 sb.append("\\\\"); //UNC file support; if there's a host, it's a UNC path.
    492             }
    493             sb.append(host.getStringValue());
    494             if (this instanceof LogicalPathname) {
    495                 sb.append(':');
    496             } else {
    497                 sb.append(File.separatorChar);
     587            Debug.assertTrue(host instanceof AbstractString
     588                             || host instanceof Cons);
     589            if (host instanceof Cons) {
     590                LispObject scheme = Symbol.GETF.execute(host, SCHEME, NIL);
     591                LispObject authority = Symbol.GETF.execute(host, AUTHORITY, NIL);
     592                Debug.assertTrue(scheme != NIL);
     593                sb.append(scheme.getStringValue());
     594                sb.append(":");
     595                if (authority != NIL) {
     596                    sb.append("//");
     597                    sb.append(authority.getStringValue());
     598                }
     599            } else {
     600                if (!(this instanceof LogicalPathname)) {
     601                    sb.append("\\\\"); //UNC file support; if there's a host, it's a UNC path.
     602                }
     603                sb.append(host.getStringValue());
     604                if (this instanceof LogicalPathname) {
     605                    sb.append(':');
     606                } else {
     607                    sb.append(File.separatorChar);
     608                }
    498609            }
    499610        }
     
    583694            }
    584695        }
    585         return namestring = sb.toString();
     696        namestring = sb.toString();
     697        if (isURL()) {
     698            namestring = Utilities.uriEncode(namestring);
     699        }
     700        return namestring;
    586701    }
    587702
     
    644759        p.name = name;
    645760        p.type = type;
     761        p.invalidateNamestring();
    646762        String path = p.getNamestring();
    647763        StringBuilder result = new StringBuilder();
     
    746862                // We have a namestring. Check for pathname components that
    747863                // can't be read from the namestring.
    748                 if (host != NIL || version != NIL) {
     864                if ((host != NIL && !isURL())
     865                    || version != NIL)
     866                {
    749867                    useNamestring = false;
    750868                } else if (name instanceof AbstractString) {
     
    829947    }
    830948
     949    public static boolean isValidURL(String s) {
     950        try {
     951            URL url = new URL(s);
     952        } catch (MalformedURLException e) {
     953            return false;
     954        }
     955        return true;
     956    }
     957
     958    public static URL toURL(Pathname p) {
     959        URL url = null;
     960        if (!(p.host instanceof Cons)) {
     961            Debug.assertTrue(false); // XXX
     962        }
     963        try {
     964            url = new URL(p.getNamestring());
     965        } catch (MalformedURLException e) {
     966            Debug.assertTrue(false); // XXX
     967        }
     968        return url;
     969    }
     970
     971    URLConnection getURLConnection() {
     972        Debug.assertTrue(isURL());
     973        URL url = Pathname.toURL(this);
     974        URLConnection result = null;
     975        try {
     976            result = url.openConnection();
     977        } catch (IOException e) {
     978            error(new FileError("Failed to open URL connection.",
     979                                this));
     980        }
     981        return result;
     982    }
     983
    831984    public static Pathname parseNamestring(AbstractString namestring) {
    832985        // Check for a logical pathname host.
    833986        String s = namestring.getStringValue();
    834         String h = getHostString(s);
    835         if (h != null && LOGICAL_PATHNAME_TRANSLATIONS.get(new SimpleString(h)) != null) {
    836             // A defined logical pathname host.
    837             return new LogicalPathname(h, s.substring(s.indexOf(':') + 1));
     987        if (!isValidURL(s)) {
     988            String h = getHostString(s);
     989            if (h != null && LOGICAL_PATHNAME_TRANSLATIONS.get(new SimpleString(h)) != null) {
     990                // A defined logical pathname host.
     991                return new LogicalPathname(h, s.substring(s.indexOf(':') + 1));
     992            }
    838993        }
    839994        return new Pathname(s);
    840995    }
    841996
    842     public static Pathname parseNamestring(AbstractString namestring,
    843       AbstractString host) {
    844         // Look for a logical pathname host in the namestring.
     997    // XXX was @return Pathname
     998    public static LogicalPathname parseNamestring(AbstractString namestring,
     999                                                  AbstractString host)
     1000    {
    8451001        String s = namestring.getStringValue();
     1002
     1003        // Look for a logical pathname host in the namestring.       
    8461004        String h = getHostString(s);
    8471005        if (h != null) {
     
    12631421            }
    12641422            case 1:
    1265                 return NIL; // ??? huh? -- ME 20100206
     1423                return NIL;
    12661424            default:
    12671425                return error(new WrongNumberOfArgumentsException(this));
     
    13271485                }
    13281486                return result;
     1487            }
     1488
     1489            if (pathname.isURL()) {
     1490                return error(new LispError("Unimplemented.")); // XXX
    13291491            }
    13301492
     
    14421604
    14431605    public boolean isJar() {
    1444         if (device instanceof Cons) {
    1445             return true;
    1446         }
    1447         return false;
     1606        return (device instanceof Cons);
     1607    }
     1608
     1609    // ### PATHNAME-URL-P
     1610    private static final Primitive PATHNAME_URL_P = new pf_pathname_url_p();
     1611    private static class pf_pathname_url_p extends Primitive {
     1612        pf_pathname_url_p() {
     1613            super("pathname-url-p", PACKAGE_SYS, true, "pathname",
     1614                  "Predicate for whether PATHNAME references a URL.");
     1615        }
     1616        @Override
     1617        public LispObject execute(LispObject arg) {
     1618            Pathname p = coerceToPathname(arg);
     1619            return p.isURL() ? T : NIL;
     1620        }
     1621    }
     1622
     1623    public boolean isURL() {
     1624        return (host instanceof Cons);
    14481625    }
    14491626
     
    16071784            result.directory = mergeDirectories(p.directory, d.directory);
    16081785        }
    1609 
    1610         // A JAR always has absolute directories
    1611         // if (result.isJar()
    1612         //     && result.directory instanceof Cons
    1613         //     && result.directory.car().equals(Keyword.ABSOLUTE)) {
    1614         //     if (result.directory.cdr().equals(NIL)) {
    1615         //         result.directory = NIL;
    1616         //     } else {
    1617         //         ((Cons)result.directory).car = Keyword.RELATIVE;
    1618         //     }
    1619         // }
    16201786
    16211787        if (pathname.name != NIL) {
     
    17281894                                       pathname));
    17291895        }
    1730         if (!(pathname.device instanceof Cons)) {
     1896        if (!(pathname.isJar() || pathname.isURL())) {
    17311897            pathname
    17321898                = mergePathnames(pathname,
     
    17501916                    return error(new FileError(e.getMessage(), pathname));
    17511917                }
     1918            }
     1919        } else if (pathname.isURL()) {
     1920            if (pathname.getInputStream() != null) {
     1921                return pathname;
    17521922            }
    17531923        } else
     
    18862056                }
    18872057            }
     2058        } else if (isURL()) {
     2059            URL url = toURL(this);
     2060            try {
     2061                result = url.openStream();
     2062            } catch (IOException e) {
     2063                error(new FileError("Failed to get InputStream from "
     2064                                    + "'" + Utilities.escapeFormat(getNamestring()) + "'"
     2065                                    + ": " + e,
     2066                                    this));
     2067            }
    18882068        } else {
    18892069            File file = Utilities.getFile(this);
     
    18912071                result = new FileInputStream(file);
    18922072            } catch (IOException e) {
    1893                 Debug.trace("Failed to get InputStream for read from "
    1894                                 + "'" + getNamestring() + "'"
    1895                                 + ": " + e);
     2073                error(new FileError("Failed to get InputStream from "
     2074                                    + "'" + getNamestring() + "'"
     2075                                    + ": " + e, this));
    18962076            }
    18972077        }
     
    19032083     */
    19042084    public long getLastModified() {
    1905         if (!(device instanceof Cons)) {
     2085        if (!(isJar() || isURL())) {
    19062086            File f = Utilities.getFile(this);
    19072087            return f.lastModified();
    19082088        }
    1909         // JAR cases
    1910         // 0.  JAR from URL
    1911         // 1.  JAR
    1912         // 2.  JAR in JAR
    1913         // 3.  Entry in JAR
    1914         // 4.  Entry in JAR in JAR
    1915         String entryPath = asEntryPath();
    1916         Cons d = (Cons)device;
    1917         if (d.cdr().equals(NIL)) {
    1918             if (entryPath.length() == 0) {
    1919                 LispObject o = d.car();
    1920                 if (o instanceof SimpleString) {
    1921                     // 0. JAR from URL
    1922                     // URL u = makeJarURL(o.getStringValue());
    1923                     // XXX unimplemented
    1924                     Debug.assertTrue(false);
    1925                     // URLConnection c = null;
    1926                     // try {
    1927                     //   c = u.openConnection();
    1928                     // } catch(IOException e) {
    1929                     //   Debug.trace("Failed to open Connection for URL "
    1930                     //               + "'" + u + "'");
    1931                     //   return 0;
    1932                     // }
    1933                     // c.getLastModified();
    1934                 } else  { 
    1935                     // 1. JAR
    1936                     return ((Pathname)o).getLastModified();
    1937                 }
    1938             } else {
    1939                 // 3. Entry in JAR
    1940                 final ZipEntry entry
    1941                     = ZipCache.get(device.car()).getEntry(entryPath);
    1942                 if (entry == null) {
    1943                     return 0;
    1944                 }
    1945                 final long time = entry.getTime();
    1946                 if (time == -1) {
    1947                     return 0;
    1948                 }
    1949                 return time;
    1950             }
    1951         } else {
    1952             ZipFile outerJar = ZipCache.get(d.car());
    1953             if (entryPath.length() == 0) {
    1954                 // 4.  JAR in JAR
    1955                 String jarPath = ((Pathname)d.cdr()).asEntryPath();
    1956                 final ZipEntry entry = outerJar.getEntry(jarPath);
    1957                 final long time = entry.getTime();
    1958                 if (time == -1) {
    1959                     return 0;
    1960                 }
    1961                 return time;
    1962             } else {
    1963                 // 5. Entry in JAR in JAR
    1964                 String innerJarPath = ((Pathname)d.cdr()).asEntryPath();
    1965                 ZipEntry entry = outerJar.getEntry(entryPath);
    1966                 ZipInputStream innerJarInputStream
    1967                     = Utilities.getZipInputStream(outerJar, innerJarPath);
    1968                 ZipEntry innerEntry = Utilities.getEntry(innerJarInputStream,
    1969                                                          entryPath);
    1970                 long time = innerEntry.getTime();
    1971                 if (time == -1) {
    1972                     return 0;
    1973                 }
    1974                 return time;
    1975             }
     2089
     2090        if (isJar()) {
     2091            // JAR cases
     2092            // 0.  JAR from URL
     2093            // 1.  JAR
     2094            // 2.  JAR in JAR
     2095            // 3.  Entry in JAR
     2096            // 4.  Entry in JAR in JAR
     2097            String entryPath = asEntryPath();
     2098            Cons d = (Cons)device;
     2099            if (d.cdr().equals(NIL)) {
     2100                if (entryPath.length() == 0) {
     2101                    LispObject o = d.car();
     2102                    if (o instanceof SimpleString) {
     2103                        // 0. JAR from URL
     2104                        // URL u = makeJarURL(o.getStringValue());
     2105                        // XXX unimplemented
     2106                        Debug.assertTrue(false);
     2107                        // URLConnection c = null;
     2108                        // try {
     2109                        //   c = u.openConnection();
     2110                        // } catch(IOException e) {
     2111                        //   Debug.trace("Failed to open Connection for URL "
     2112                        //               + "'" + u + "'");
     2113                        //   return 0;
     2114                        // }
     2115                        // c.getLastModified();
     2116                    } else  { 
     2117                        // 1. JAR
     2118                        return ((Pathname)o).getLastModified();
     2119                    }
     2120                } else {
     2121                    // 3. Entry in JAR
     2122                    final ZipEntry entry
     2123                        = ZipCache.get(device.car()).getEntry(entryPath);
     2124                    if (entry == null) {
     2125                        return 0;
     2126                    }
     2127                    final long time = entry.getTime();
     2128                    if (time == -1) {
     2129                        return 0;
     2130                    }
     2131                    return time;
     2132                }
     2133            } else {
     2134                ZipFile outerJar = ZipCache.get(d.car());
     2135                if (entryPath.length() == 0) {
     2136                    // 4.  JAR in JAR
     2137                    String jarPath = ((Pathname)d.cdr()).asEntryPath();
     2138                    final ZipEntry entry = outerJar.getEntry(jarPath);
     2139                    final long time = entry.getTime();
     2140                    if (time == -1) {
     2141                        return 0;
     2142                    }
     2143                    return time;
     2144                } else {
     2145                    // 5. Entry in JAR in JAR
     2146                    String innerJarPath = ((Pathname)d.cdr()).asEntryPath();
     2147                    ZipEntry entry = outerJar.getEntry(entryPath);
     2148                    ZipInputStream innerJarInputStream
     2149                        = Utilities.getZipInputStream(outerJar, innerJarPath);
     2150                    ZipEntry innerEntry = Utilities.getEntry(innerJarInputStream,
     2151                                                             entryPath);
     2152                    long time = innerEntry.getTime();
     2153                    if (time == -1) {
     2154                        return 0;
     2155                    }
     2156                    return time;
     2157                }
     2158            }
     2159        }
     2160        if (isURL()) {
     2161            return getURLConnection().getLastModified();
    19762162        }
    19772163        return 0;
     
    19952181                               coerceToPathname(Symbol.DEFAULT_PATHNAME_DEFAULTS.symbolValue()),
    19962182                               NIL);
     2183            if (defaultedPathname.isURL() || defaultedPathname.isJar()) {
     2184                return new FileError("Cannot mkdir with a "
     2185                                     + (defaultedPathname.isURL() ? "URL" : "jar")
     2186                                     + " Pathname.",
     2187                                     defaultedPathname);
     2188            }
     2189                   
    19972190            File file = Utilities.getFile(defaultedPathname);
    19982191            return file.mkdir() ? T : NIL;
     
    20892282        Symbol.DEFAULT_PATHNAME_DEFAULTS.setSymbolValue(coerceToPathname(obj));
    20902283    }
     2284
    20912285}
    20922286
  • trunk/abcl/src/org/armedbear/lisp/Symbol.java

    r12598 r12607  
    30053005  public static final Symbol SET_SCHAR =
    30063006    PACKAGE_SYS.addExternalSymbol("SET-SCHAR");
     3007  public static final Symbol JAR_STREAM =
     3008    PACKAGE_SYS.addExternalSymbol("JAR-STREAM");
     3009  public static final Symbol URL_STREAM =
     3010    PACKAGE_SYS.addExternalSymbol("URL-STREAM");
     3011
    30073012
    30083013  // Internal symbols in SYSTEM package.
     
    30613066  public static final Symbol JAVA_STACK_FRAME =
    30623067    PACKAGE_SYS.addInternalSymbol("JAVA-STACK-FRAME");
     3068  public static final Symbol JAR_PATHNAME =
     3069    PACKAGE_SYS.addExternalSymbol("JAR-PATHNAME");
     3070  public static final Symbol URL_PATHNAME =
     3071    PACKAGE_SYS.addExternalSymbol("URL-PATHNAME");
    30633072
    30643073  // CDR6
  • trunk/abcl/src/org/armedbear/lisp/Utilities.java

    r12441 r12607  
    4141import java.io.IOException;
    4242import java.io.InputStream;
     43import java.net.URI;
     44import java.net.URISyntaxException;
    4345import java.util.jar.JarFile;
    4446import java.util.zip.ZipEntry;
     
    254256
    255257
    256 
     258    static String uriEncode(String s) {
     259        try {
     260            URI uri = new URI("?" + s);
     261            return uri.getQuery();
     262        } catch (URISyntaxException e) {}
     263        return null;
     264    }
     265
     266    static String uriDecode(String s) {
     267        try {
     268            URI uri = new URI(null, null, null, s, null);
     269            return uri.toASCIIString().substring(1);
     270        } catch (URISyntaxException e) {}
     271        return null;  // Error
     272    }
     273   
     274    static String escapeFormat(String s) {
     275        return s.replace("~", "~~");
     276    }
    257277}
  • trunk/abcl/src/org/armedbear/lisp/ZipCache.java

    r12531 r12607  
    112112                    return new ZipFile(f);
    113113                } catch (ZipException e) {
    114                     Debug.trace(e); // XXX
    115                     return null;
     114                    error(new FileError("Failed to construct ZipFile"
     115                                        + " because " + e,
     116                                        Pathname.makePathname(f)));
    116117                } catch (IOException e) {
    117                     Debug.trace(e); // XXX
    118                     return null;
     118                    error(new FileError("Failed to contruct ZipFile"
     119                                        + " because " + e,
     120                                        Pathname.makePathname(f)));
    119121                }
    120122            } else {
     
    186188                    entry.file = new ZipFile(f);
    187189                } catch (ZipException e) {
    188                     Debug.trace(e); // XXX
    189                     return null;
     190                    error(new FileError("Failed to get cached ZipFile"
     191                                        + " because " + e,
     192                                        Pathname.makePathname(f)));
    190193                } catch (IOException e) {
    191                     Debug.trace(e); // XXX
    192                     return null;
     194                    error(new FileError("Failed to get cached ZipFile"
     195                                        + " because " + e,
     196                                        Pathname.makePathname(f)));
    193197                }
    194198            } else {
     
    206210            jarURL = new URL("jar:" + url + "!/");
    207211        } catch (MalformedURLException e) {
    208             Debug.trace(e);
    209             Debug.assertTrue(false); // XXX
    210         }
    211         URLConnection connection;
     212            error(new LispError("Failed to form a jar: URL from "
     213                                + "'" + url + "'"
     214                                + " because " + e));
     215        }
     216        URLConnection connection = null;
    212217        try {
    213218            connection = jarURL.openConnection();
    214         } catch (IOException ex) {
    215             Debug.trace("Failed to open "
    216                         + "'" + jarURL + "'");
    217             return null;
     219        } catch (IOException e) {
     220            error(new LispError("Failed to open "
     221                                + "'" + jarURL + "'"
     222                                + " with exception "
     223                                + e));
    218224        }
    219225        if (!(connection instanceof JarURLConnection)) {
    220             // XXX
    221             Debug.trace("Could not get a URLConnection from " + jarURL);
    222             return null;
     226            error(new LispError("Could not get a URLConnection from "
     227                                + "'" + jarURL + "'"));
    223228        }
    224229        JarURLConnection jarURLConnection = (JarURLConnection) connection;
     
    227232            result.file = jarURLConnection.getJarFile();
    228233        } catch (IOException e) {
    229             Debug.trace(e);
    230             Debug.assertTrue(false); // XXX
     234            error(new LispError("Failed to fetch URL "
     235                                 + "'" + jarURLConnection + "'"
     236                                + " because " + e));
    231237        }
    232238        result.lastModified = jarURLConnection.getLastModified();
  • trunk/abcl/src/org/armedbear/lisp/pathnames.lisp

    r12552 r12607  
    135135  (unless (component-match-p (pathname-host pathname) (pathname-host wildcard) nil)
    136136    (return-from pathname-match-p nil))
     137  (when (and (pathname-jar-p pathname)
     138             (pathname-jar-p wildcard))
     139    (unless
     140        (every (lambda (value) (not (null value)))
     141               (mapcar #'pathname-match-p
     142                       (pathname-device pathname) 
     143                       (pathname-device wildcard)))
     144      (return-from pathname-match-p nil)))
     145  (when (or (and (pathname-jar-p pathname)
     146                 (not (pathname-jar-p wildcard)))
     147            (and (not (pathname-jar-p pathname))
     148                 (pathname-jar-p wildcard)))
     149    (return-from pathname-match-p nil))
    137150  (let* ((windows-p (featurep :windows))
    138151         (ignore-case (or windows-p (typep pathname 'logical-pathname))))
    139152    (cond ((and windows-p
     153                (not (pathname-jar-p pathname))
     154                (not (pathname-jar-p wildcard))
    140155                (not (component-match-p (pathname-device pathname)
    141156                                        (pathname-device wildcard)
     
    196211         (error "Unsupported wildcard pattern: ~S" to))))
    197212
     213(defun translate-jar-device (source from to &optional case)
     214  (declare (ignore case)) ; FIXME
     215  (unless to
     216    (return-from translate-jar-device nil))
     217  (when (not (= (length source)
     218                (length from)
     219                (length to)))
     220    (error "Unsupported pathname translation for unequal jar ~
     221  references: ~S != ~S != ~S" source from to))
     222  (mapcar #'translate-pathname source from to))
    198223
    199224(defun translate-directory-components-aux (src from to case)
     
    269294         (device (if (typep 'to 'logical-pathname)
    270295                     :unspecific
    271                      (translate-component (pathname-device source)
    272                                           (pathname-device from)
    273                                           (pathname-device to))))
     296                     (if (pathname-jar-p source)
     297                         (translate-jar-device (pathname-device source)
     298                                               (pathname-device from)
     299                                               (pathname-device to))
     300                         (translate-component (pathname-device source)
     301                                              (pathname-device from)
     302                                              (pathname-device to)))))
    274303         (case   (and (typep source 'logical-pathname)
    275304                      (or (featurep :unix) (featurep :windows))
     
    389418  (cond ((eq host :unspecific)
    390419         (setf host nil))
     420        ((consp host)) ;; A URL
    391421        (host
    392422         (setf host (canonicalize-logical-host host))))
  • trunk/abcl/test/lisp/abcl/jar-file.lisp

    r12531 r12607  
    321321  (:absolute "c" "d") "foo" "lisp")
    322322
     323(deftest jar-file.pathname-match-p.1
     324    (pathname-match-p "jar:file:/a/b/some.jar!/a/system/def.asd"
     325                      "jar:file:/**/*.jar!/**/*.asd")
     326  t)
     327
     328(deftest jar-file.pathname-match-p.2
     329    (pathname-match-p "/a/system/def.asd"
     330                      "jar:file:/**/*.jar!/**/*.asd")
     331  nil)
     332
     333(deftest jar-file.pathname-match-p.3
     334    (pathname-match-p "jar:file:/a/b/some.jar!/a/system/def.asd"
     335                      "/**/*.asd")
     336  nil)
     337
     338(deftest jar-file.translate-pathname.1
     339    (namestring
     340     (translate-pathname "jar:file:/a/b/c.jar!/d/e/f.lisp"
     341                         "jar:file:/**/*.jar!/**/*.*"
     342                         "/foo/**/*.*"))
     343  "/foo/d/e/f.lisp")
     344
     345;; URL Pathname tests
     346(deftest pathname-url.1
     347    (let* ((p #p"http://example.org/a/b/foo.lisp")
     348           (host (pathname-host p)))
     349      (values
     350       (check-physical-pathname p '(:absolute "a" "b") "foo" "lisp")
     351       (and (consp host)
     352            (equal (getf host :scheme)
     353                   "http")
     354            (equal (getf host :authority)
     355                   "example.org"))))
     356  (t t))
     357
    323358     
    324      
    325              
    326 
    327        
     359
    328360       
    329361
Note: See TracChangeset for help on using the changeset viewer.