Changeset 15395


Ignore:
Timestamp:
10/10/20 21:43:28 (2 years ago)
Author:
Mark Evenson
Message:

Make Pathname.init() static method

Running under Netbeans, but failing to enumerate jar pathnames in the
Java tests.

Moved (most) Pathname.truename() invocations to using symbols

Changed a lot of use of PROBE-FILE over TRUENAME.

Running under Netbeans, but can't probe-file on jar locations:

(probe-file "jar:file:/opt/local/share/java/abcl/abcl.jar!/org/armedbear/lisp/")
(probe-file "jar:file:/opt/local/share/java/abcl/abcl.jar!/org/armedbear/lisp/boot.lisp")

Loading from boot jar not working.

Reading all the jar entries into the entry cache at once is not a good
idea. For abcl.jar this is ~10k entries which takes a noticable
amount of time.

And PathnameJar? does not implement the right equals() contract, so
looking up the entry in the cache doesn't work either.

Running from abcl.jar!

Not loading in contribs…
Good progress: able to load from abcl.jar.

Failing to bootstrap contrib due to the following errors.

DIRECTORY partly working for jar archives

Use java.net.URI to encode namestrings.

INCOMPLETE Overhaul ZipCache? to give linear access to PathnameURL keys

TODO Failing to distinguish that org/armedbear/lisp/java *doesn't* name a directory

TODO: comprehensive multi-level cache design that will plausibly work

Location:
trunk/abcl
Files:
1 added
1 deleted
18 edited

Legend:

Unmodified
Added
Removed
  • trunk/abcl/src/org/armedbear/lisp/Autoload.java

    r14997 r15395  
    150150            thread.resetSpecialBindings(mark);
    151151        }
    152         if (debug) {
    153             if (symbol != null) {
    154                 if (symbol.getSymbolFunction() instanceof Autoload) {
    155                     Debug.trace("Unable to autoload " + symbol.princToString());
    156                     throw new IntegrityError();
    157                 }
    158             }
    159         }
     152        // if (debug) {
     153        if (symbol != null) {
     154          if (symbol.getSymbolFunction() instanceof Autoload) {
     155            simple_error("Unable to autoload " + symbol.princToString(), symbol, fileName, className);
     156          }
     157        } // end if (debug)
    160158    }
    161159
  • trunk/abcl/src/org/armedbear/lisp/FaslClassLoader.java

    r15393 r15395  
    111111     
    112112      if (truenameFasl instanceof Pathname) {
    113           return Pathname.mergePathnames(name, (Pathname)truenameFasl, Keyword.NEWEST)
     113          return ((Pathname)Pathname.mergePathnames(name, (Pathname)truenameFasl, Keyword.NEWEST))
    114114                    .getInputStream();
    115115      } else if (truename instanceof Pathname) {
    116           return Pathname.mergePathnames(name, (Pathname) truename, Keyword.NEWEST)
     116          return ((Pathname)Pathname.mergePathnames(name, (Pathname) truename, Keyword.NEWEST))
    117117                  .getInputStream();
    118       } else if (!Pathname.truename(name).equals(NIL)) {
    119               return name.getInputStream();
     118      } else if (!Symbol.PROBE_FILE.execute(name).equals(NIL)) {
     119        return name.getInputStream();
    120120      }
    121121
  • trunk/abcl/src/org/armedbear/lisp/Interpreter.java

    r15393 r15395  
    331331                    if (i + 1 < args.length) {
    332332                        if (arg.equals("--load"))
    333                           Load.load(Pathname.mergePathnames((Pathname)Pathname.create(args[i + 1]),
     333                          Load.load((Pathname)Pathname.mergePathnames((Pathname)Pathname.create(args[i + 1]),
    334334                                    checkPathname(Symbol.DEFAULT_PATHNAME_DEFAULTS.getSymbolValue())),
    335335                                      false, false, true);
  • trunk/abcl/src/org/armedbear/lisp/Lisp.java

    r15394 r15395  
    13731373      LispObject truename = Symbol.LOAD_TRUENAME.symbolValue(thread);
    13741374      if (truenameFasl instanceof Pathname) {
    1375           load = Pathname.mergePathnames(name, (Pathname)truenameFasl, Keyword.NEWEST);
     1375          load = (Pathname)Pathname.mergePathnames(name, (Pathname)truenameFasl, Keyword.NEWEST);
    13761376      } else if (truename instanceof Pathname) {
    1377           load = Pathname.mergePathnames(name, (Pathname) truename, Keyword.NEWEST);
     1377          load = (Pathname)Pathname.mergePathnames(name, (Pathname) truename, Keyword.NEWEST);
    13781378      } else {
    1379           if (!Pathname.truename(name).equals(NIL)) {
    1380               load = name;
    1381           } else {
    1382               load = null;
    1383           }
     1379        if (!Symbol.PROBE_FILE.execute(name).equals(NIL)) {
     1380          load = name;
     1381        } else {
     1382          load = null;
     1383        }
    13841384      }
    13851385      InputStream input = null;
     
    19141914      return (Pathname) arg;
    19151915    if (arg instanceof AbstractString)
    1916       return (Pathname)Pathname.parseNamestring((AbstractString)arg);
     1916      return (Pathname)Pathname.create(((AbstractString)arg).toString());
    19171917    if (arg instanceof FileStream)
    19181918      return ((FileStream)arg).getPathname();
     
    19211921    if (arg instanceof URLStream)
    19221922      return ((URLStream)arg).getPathname();
    1923     type_error(arg, list(Symbol.OR, Symbol.PATHNAME,
    1924                          Symbol.STRING, Symbol.FILE_STREAM,
    1925                          Symbol.JAR_STREAM, Symbol.URL_STREAM));
     1923    type_error(arg, list(Symbol.OR,
     1924                           Symbol.STRING,
     1925                           Symbol.PATHNAME, Symbol.JAR_PATHNAME, Symbol.URL_PATHNAME,
     1926                           Symbol.FILE_STREAM, Symbol.JAR_STREAM, Symbol.URL_STREAM));
    19261927    // Not reached.
    19271928    return null;
  • trunk/abcl/src/org/armedbear/lisp/Load.java

    r15393 r15395  
    6767     * none can be determined. */
    6868    private static final Pathname findLoadableFile(Pathname name) {
    69         LispObject truename  = Pathname.truename(name, false);
    70         if (truename instanceof Pathname) {
    71             Pathname t = (Pathname)truename;
    72             if (t.getName() != NIL
    73                 && t.getName() != null) {
    74                 return t;
    75             }
    76         }
    77         final String COMPILE_FILE_TYPE = Lisp._COMPILE_FILE_TYPE_.symbolValue().getStringValue();
    78         if (name.getType() == NIL
    79             && (name.getName() != NIL || name.getName() != null)) {
    80             Pathname lispPathname = Pathname.create(name);
    81             lispPathname.setType(new SimpleString("lisp"));
    82             lispPathname.invalidateNamestring();
    83             LispObject lisp = Pathname.truename(lispPathname, false);
    84             Pathname abclPathname = Pathname.create(name);
    85             abclPathname.setType(new SimpleString(COMPILE_FILE_TYPE));
    86             abclPathname.invalidateNamestring();
    87             LispObject abcl = Pathname.truename(abclPathname, false);
    88             if (lisp instanceof Pathname && abcl instanceof Pathname) {
    89               lispPathname = (Pathname)lisp;
    90               abclPathname = (Pathname)abcl;
    91               long lispLastModified = lispPathname.getLastModified();
    92               long abclLastModified = abclPathname.getLastModified();
    93               if (abclLastModified > lispLastModified) {
    94                   return abclPathname;  // fasl file is newer
    95               } else {
    96                   return lispPathname;
    97               }
    98             } else if (abcl instanceof Pathname) {
    99                 return (Pathname) abcl;
    100             } else if (lisp instanceof Pathname) {
    101                 return (Pathname) lisp;
    102             }
    103         }
    104         if (name.isJar()) {
    105             if (name.getType().equals(NIL)) {
    106                 name.setType(COMPILE_FILE_INIT_FASL_TYPE);
    107                 name.invalidateNamestring();
    108                 Pathname result = findLoadableFile(name);
    109                 if (result != null) {
    110                     return result;
    111                 }
    112                 name.setType(new SimpleString(COMPILE_FILE_TYPE));
    113                 name.invalidateNamestring();
    114                 result = findLoadableFile(name);
    115                 if (result != null) {
    116                     return result;
    117                 }
    118             }
    119         }
    120         return null;
     69      LispObject truename  = Symbol.PROBE_FILE.execute(name);
     70      if (truename instanceof Pathname) {
     71        Pathname t = (Pathname)truename;
     72        if (t.getName() != NIL
     73            && t.getName() != null) {
     74          return t;
     75        }
     76      }
     77      final String COMPILE_FILE_TYPE = Lisp._COMPILE_FILE_TYPE_.symbolValue().getStringValue();
     78      if (name.getType() == NIL
     79          && (name.getName() != NIL || name.getName() != null)) {
     80        Pathname lispPathname = Pathname.create(name);
     81        lispPathname.setType(new SimpleString("lisp"));
     82        LispObject lisp = Symbol.PROBE_FILE.execute(lispPathname);
     83        Pathname abclPathname = Pathname.create(name);
     84        abclPathname.setType(new SimpleString(COMPILE_FILE_TYPE));
     85        LispObject abcl = Symbol.PROBE_FILE.execute(abclPathname);
     86        if (lisp instanceof Pathname && abcl instanceof Pathname) {
     87          lispPathname = (Pathname)lisp;
     88          abclPathname = (Pathname)abcl;
     89          long lispLastModified = lispPathname.getLastModified();
     90          long abclLastModified = abclPathname.getLastModified();
     91          if (abclLastModified > lispLastModified) {
     92            return abclPathname;  // fasl file is newer
     93          } else {
     94            return lispPathname;
     95          }
     96        } else if (abcl instanceof Pathname) {
     97          return (Pathname) abcl;
     98        } else if (lisp instanceof Pathname) {
     99          return (Pathname) lisp;
     100        }
     101      }
     102      if (name.isJar()) {
     103        if (name.getType().equals(NIL)) {
     104          name.setType(COMPILE_FILE_INIT_FASL_TYPE);
     105          Pathname result = findLoadableFile(name);
     106          if (result != null) {
     107            return result;
     108          }
     109          name.setType(new SimpleString(COMPILE_FILE_TYPE));
     110          name.invalidateNamestring();
     111          result = findLoadableFile(name);
     112          if (result != null) {
     113            return result;
     114          }
     115        }
     116      }
     117      return null;
    121118    }
    122119 
     
    158155            Pathname pathnameDefaults
    159156                = coerceToPathname(Symbol.DEFAULT_PATHNAME_DEFAULTS.symbolValue());
    160             mergedPathname = Pathname.mergePathnames(pathname, pathnameDefaults);
     157            mergedPathname = (Pathname)Pathname.mergePathnames(pathname, pathnameDefaults);
    161158        }
    162159        Pathname loadableFile = findLoadableFile(mergedPathname != null ? mergedPathname : pathname);
    163         Pathname truename = coercePathnameOrNull(Pathname.truename(loadableFile));
    164 
    165         if (truename == null || truename.equals(NIL)) {
    166             if (ifDoesNotExist) {
    167                 return error(new FileError("File not found: " + pathname.princToString(), pathname));
    168             } else {
    169                 Debug.warn("Failed to load " + pathname.getNamestring());
    170                 return NIL;
    171             }
    172         }
    173 
    174         if (Utilities.checkZipFile(truename)) {
    175             String n = truename.getNamestring();
    176             String name = Pathname.uriEncode(truename.getName().getStringValue());
    177             if (n.startsWith("jar:")) {
    178                 n = "jar:" + n + "!/" + name + "."
    179                     + COMPILE_FILE_INIT_FASL_TYPE;
    180       } else if (n.startsWith("zip:")) {
    181                 n = "zip:" + n + "!/" + name + "."
    182                     + COMPILE_FILE_INIT_FASL_TYPE;
    183             } else {
    184                 n = "jar:file:" + Pathname.uriEncode(n) + "!/" + name + "."
    185                     + COMPILE_FILE_INIT_FASL_TYPE;
    186             }
    187             if (!((mergedPathname = (Pathname)Pathname.create(n)) instanceof Pathname)) {
    188               return error(new FileError((MessageFormat.format("Failed to address JAR-PATHNAME truename {0} for name {1}", truename.princToString(), name)), truename));
    189             }
    190 
    191             LispObject initTruename = coercePathnameOrNull(Pathname.truename(mergedPathname));
    192             if (initTruename == null || initTruename.equals(NIL)) {
     160        Pathname truename = (Pathname)Symbol.PROBE_FILE.execute(loadableFile);
     161
     162        if (truename.equals(NIL)) {
     163          if (ifDoesNotExist) {
     164            return error(new FileError("File not found: " + pathname.princToString(), pathname));
     165          } else {
     166            Debug.warn("Failed to load " + pathname.getNamestring());
     167            return NIL;
     168          }
     169        }
     170
     171        if (ZipCache.checkZipFile(truename)) {
     172            // String n = truename.getNamestring();
     173            // String name = Pathname.uriEncode(truename.getName().getStringValue());
     174            // if (n.startsWith("jar:")) {
     175            //     n = "jar:" + n + "!/" + name + "."
     176            //         + COMPILE_FILE_INIT_FASL_TYPE;
     177      // } else if (n.startsWith("zip:")) {
     178            //     n = "zip:" + n + "!/" + name + "."
     179            //         + COMPILE_FILE_INIT_FASL_TYPE;
     180            // } else {
     181            //     n = "jar:file:" + Pathname.uriEncode(n) + "!/" + name + "."
     182            //         + COMPILE_FILE_INIT_FASL_TYPE;
     183            // }
     184            // if (!((mergedPathname = (Pathname)Pathname.create(n)) instanceof Pathname)) {
     185            //   return error(new FileError((MessageFormat.format("Failed to address JAR-PATHNAME truename {0} for name {1}", truename.princToString(), name)), truename));
     186            // }
     187          truename = (Pathname) PathnameJar.createFromPathname(truename);
     188          Pathname loader = (Pathname)Pathname.create("__loader__._"); // FIXME use constants
     189          mergedPathname = (Pathname)Symbol.MERGE_PATHNAMES.execute(loader, truename);
     190
     191            LispObject initTruename = Symbol.PROBE_FILE.execute(mergedPathname);
     192            if (initTruename.equals(NIL)) {
    193193                // Maybe the enclosing JAR has been renamed?
    194194                Pathname p = Pathname.create(mergedPathname);
    195195                p.setName(Keyword.WILD);
    196                 p.invalidateNamestring();
    197                 LispObject result = Pathname.MATCH_WILD_JAR_PATHNAME.execute(p);
    198 
    199                 if      (result instanceof Cons
     196                LispObject result = Symbol.MATCH_WILD_JAR_PATHNAME.execute(p);
     197
     198                if (result instanceof Cons
    200199                    && ((Cons)result).length() == 1
    201200                    && ((Cons)result).car() instanceof Pathname) {
     
    298297        Pathname mergedPathname;
    299298        if (bootPath instanceof Pathname) {
    300             mergedPathname = Pathname.mergePathnames(pathname, (Pathname)bootPath);
     299          mergedPathname = (Pathname)Symbol.MERGE_PATHNAMES.execute(pathname, bootPath);
    301300        } else {
    302             mergedPathname = pathname;
     301          mergedPathname = pathname;
    303302        }
    304303        URL url = null;
    305304        Pathname loadableFile = findLoadableFile(mergedPathname);
    306         truename = coercePathnameOrNull(Pathname.truename(loadableFile));
     305        truename = (Pathname)Symbol.PROBE_FILE.execute(loadableFile);
    307306       
    308307        final String COMPILE_FILE_TYPE
     
    326325            if (!bootPath.equals(NIL)) {
    327326              Pathname urlPathname = (Pathname)Pathname.create(url);
    328                 loadableFile = findLoadableFile(urlPathname);
    329                 truename = (Pathname)Pathname.truename(loadableFile);
    330                 if (truename == null) {
    331                     return error(new LispError("Failed to find loadable system file in boot classpath "
    332                                                + "'" + url + "'"));
    333                 }
     327              loadableFile = findLoadableFile(urlPathname);
     328              truename = (Pathname)Symbol.PROBE_FILE.execute(loadableFile);
     329              if (truename.equals(NIL)) {
     330                return error(new LispError("Failed to find loadable system file in boot classpath "
     331                                           + "'" + url + "'"));
     332              }
    334333            } else {
    335334                truename = null; // We can't represent the FASL in a Pathname (q.v. OSGi)
     
    339338        // Look for a init FASL inside a packed FASL
    340339        if (truename != null
    341             && truename.getType().princToString().equals(COMPILE_FILE_TYPE) && Utilities.checkZipFile(truename))  {
     340            && truename.getType().princToString().equals(COMPILE_FILE_TYPE) && ZipCache.checkZipFile(truename))  {
    342341          Pathname init = (Pathname)Pathname.create(truename.getNamestring());
    343342            init.setType(COMPILE_FILE_INIT_FASL_TYPE);
    344343            init.setName(new SimpleString("__loader__"));
    345             LispObject t = Pathname.truename(init);
     344            LispObject t = Symbol.PROBE_FILE.execute(init);
    346345            if (t instanceof Pathname) {
    347346                truename = (Pathname)t;
  • trunk/abcl/src/org/armedbear/lisp/Pathname.java

    r15394 r15395  
    7878      }
    7979    }
    80     Pathname result = new Pathname();
    81     result.init(s);
     80    Pathname result = Pathname.init(s);
     81
    8282    return result;
    8383  }
     
    8888
    8989  public static LispObject create(URL url) {
    90     return new Pathname(url);
     90    return Pathname.create(url.toString());
    9191  }
    9292
    9393  public static LispObject create(URI uri) {
    94     return new Pathname(uri);
     94    return Pathname.create(uri.toString());
    9595  }
    9696  protected LispObject host = NIL;
     
    9898    return host;
    9999  }
    100   public LispObject setHost(LispObject host) {
     100  public Pathname setHost(LispObject host) {
    101101    this.host = host;
    102102    return this;
     
    107107    return device;
    108108  }
    109   public LispObject setDevice(LispObject device) {
     109  public Pathname setDevice(LispObject device) {
    110110    this.device = device;
    111111    return this;
     
    116116    return directory;
    117117  }
    118   public LispObject setDirectory(LispObject directory) {
     118  public Pathname setDirectory(LispObject directory) {
    119119    this.directory = directory;
    120120    return this;
     
    125125    return name;
    126126  }
    127   public LispObject setName(LispObject name) {
     127  public Pathname setName(LispObject name) {
    128128    this.name = name;
    129129    return this;
     
    135135    return type;
    136136  }
    137   public LispObject setType(LispObject type) {
     137  public Pathname setType(LispObject type) {
    138138    this.type = type;
    139139    return this;
     
    146146  }
    147147
    148   public LispObject setVersion(LispObject version) {
     148  public Pathname setVersion(LispObject version) {
    149149    this.version = version;
    150150    return this;
     
    217217        if (p.host != NIL) {
    218218            if (p.host instanceof SimpleString) {
    219                 host = new SimpleString(((SimpleString)p.getHost()).getStringValue());
     219              setHost(new SimpleString(((SimpleString)p.getHost()).getStringValue()));
    220220            } else  if (p.host instanceof Symbol) {
    221                 host = p.host;
     221              setHost(p.host);
    222222            } else if (p.host instanceof Cons) {
    223223                host = new Cons((Cons)p.getHost());
    224224            } else {
    225                 Debug.assertTrue(false);
     225              simple_error("Failed to copy host in pathname ~a", p);
    226226            }
    227227        }
     
    229229            if (p.device instanceof SimpleString) {
    230230                device = new SimpleString(((SimpleString)p.getDevice()).getStringValue());
    231             } else if (p.device instanceof Cons) {
    232                 Cons jars = (Cons)p.device;
    233                 device = new Cons(NIL, NIL);
    234                 LispObject first = jars.car();
    235                 if (first instanceof Pathname) {
    236                     ((Cons)device).car = Pathname.create((Pathname)first);
    237                 } else {
    238                     Debug.assertTrue(false);
    239                 }
    240                 if (!jars.cdr().equals(NIL)) {
    241                     if (jars.cdr() instanceof Cons) {
    242                         ((Cons)device).cdr = new Cons(Pathname.create((Pathname)jars.cdr().car()), NIL);
    243                     } else {
    244                         Debug.assertTrue(false);
    245                     }
    246                 }
    247             } else if (p.device instanceof Symbol) {
     231            } else if (p.getDevice() instanceof Cons) {
     232              LispObject jars = p.getDevice();
     233              jars = jars.nreverse();
     234              device = NIL;
     235              while (jars.car() != NIL) {
     236                Pathname jar = (Pathname) Pathname.create(((Pathname)jars.car()).getNamestring());
     237                device = device.push(jar);
     238                jars = jars.cdr();
     239              }
     240            } else if (p.device instanceof Symbol) { // When is this the case?
    248241                device = p.device;
    249242            } else {
    250                 Debug.assertTrue(false);
     243              simple_error("Failed to copy device in pathname ~a", p);
    251244            }               
    252245        }
     
    266259                directory.nreverse();
    267260            } else {
    268                 Debug.assertTrue(false);
     261              simple_error("Failed to copy directory in pathname ~a", p);
    269262            }
    270263        }
     
    275268                name = p.name;
    276269            } else {
    277                 Debug.assertTrue(false);
     270              simple_error("Failed to copy name in pathname ~a", p);
    278271            }
    279272        }
     
    284277                type = p.type;
    285278            } else {
    286                 Debug.assertTrue(false);
     279              simple_error("Failed to copy type in pathname ~a", p);
    287280            }
    288281        }
     
    293286        version = p.version;
    294287        } else {
    295         Debug.assertTrue(false);
    296         }
    297     }
    298     }
    299 
    300     private Pathname(String s) {
    301         init(s);
    302     }
     288          simple_error("Failed to copy version in pathname ~a", p);
     289        }
     290    }
     291  }
    303292
    304293    public static boolean isSupportedProtocol(String protocol) {
     
    321310    }
    322311
    323     private Pathname(URL url) {
    324          // URL handling is now buried in init(String), as the URI
    325          // escaping mechanism didn't interact well with '+' and other
    326          // characters.
    327         init(url.toString());
    328     }
    329    
    330     private Pathname(URI uri) {
    331         init(uri.toString());
    332     }
    333 
    334312    static final Symbol SCHEME = internKeyword("SCHEME");
    335313    static final Symbol AUTHORITY = internKeyword("AUTHORITY");
     
    338316
    339317    static final private String jarSeparator = "!/";
    340     private final void init(String s) {
     318
     319  private static final Pathname init(String s) {
     320      Pathname result = new Pathname();
    341321        if (s == null) {
    342             return;
     322          return (Pathname)simple_error("Refusing to create a PATHNAME for the null reference.");
    343323        }
    344324        if (s.equals(".") || s.equals("./")
    345325          || (Utilities.isPlatformWindows && s.equals(".\\"))) {
    346             setDirectory(new Cons(Keyword.RELATIVE));
    347             return;
    348         }
     326            result.setDirectory(new Cons(Keyword.RELATIVE));
     327            return result;
     328        } 
    349329        if (s.equals("..") || s.equals("../")) {
    350             setDirectory(list(Keyword.RELATIVE, Keyword.UP));
    351             return;
    352         }
     330            result.setDirectory(list(Keyword.RELATIVE, Keyword.UP));
     331            return result;
     332        }
     333        // UNC Windows shares
    353334        if (Utilities.isPlatformWindows) {
    354335          if (s.startsWith("\\\\") || s.startsWith("//")) {
     
    366347            }
    367348            if (shareIndex == -1 || dirIndex == -1) {
    368               error(new LispError("Unsupported UNC path format: \"" + s + '"'));
    369             }
    370 
    371             setHost(new SimpleString(s.substring(2, shareIndex)));
    372             setDevice(new SimpleString(s.substring(shareIndex + 1, dirIndex)));
     349              return Pathname_simple_error("Unsupported UNC path format: \"" + s + '"');
     350            }
     351
     352            result
     353              .setHost(new SimpleString(s.substring(2, shareIndex)))
     354              .setDevice(new SimpleString(s.substring(shareIndex + 1, dirIndex)));
    373355
    374356            Pathname p = (Pathname)Pathname.create(s.substring(dirIndex));
    375             setDirectory(p.getDirectory());
    376             setName(p.getName());
    377             setType(p.getType());
    378             setVersion(p.getVersion());
    379             invalidateNamestring();
    380             return;
     357            result
     358              .setDirectory(p.getDirectory())
     359              .setName(p.getName())
     360              .setType(p.getType())
     361              .setVersion(p.getVersion());
     362            return result;
    381363          }
    382364        }
     
    384366        // A JAR file
    385367        if (s.startsWith("jar:") && s.endsWith(jarSeparator)) {
    386             LispObject jars = NIL;
    387             int i = s.lastIndexOf(jarSeparator, s.length() - jarSeparator.length() - 1);
    388             String jar = null;
    389             if (i == -1) {
    390                 jar = s;
    391             } else {
    392                 // There can be no more than two jar references and the
    393                 // inner one must be a file reference within the outer.
    394                 jar = "jar:file:" + s.substring(i + jarSeparator.length());
    395                 s = s.substring("jar:".length(), i + jarSeparator.length());
    396                 Pathname p = (Pathname)Pathname.create(s);
    397                 jars = jars.push(p.getDevice().car());
    398             }
    399             if (jar.startsWith("jar:file:")) {
    400                 String file
    401                     = jar.substring("jar:file:".length(),
    402                                     jar.length() - jarSeparator.length());
    403                 Pathname jarPathname;
    404                 if (file.length() > 0) {
    405                     URL url = null;
    406                     URI uri = null;
    407                     try {
    408                         url = new URL("file:" + file);
    409                         uri = url.toURI();
    410                     } catch (MalformedURLException e1) {
    411                         error(new SimpleError("Failed to create URI from "
    412                                             + "'" + file + "'"
    413                                             + ": " + e1.getMessage()));
    414                     } catch (URISyntaxException e2) {
    415                         error(new SimpleError("Failed to create URI from "
    416                                             + "'" + file + "'"
    417                                             + ": " + e2.getMessage()));
    418                     }
    419                     String path = uri.getPath();
    420                     if (path == null) {
    421                         // We allow "jar:file:baz.jar!/" to construct a relative
    422                         // path for jar files, so MERGE-PATHNAMES means something.
    423                       jarPathname = (Pathname)Pathname.create(uri.getSchemeSpecificPart());
    424                     } else {
    425                       jarPathname = (Pathname)Pathname.create((new File(path)).getPath());
    426                     }
    427                 } else {
    428                   jarPathname = (Pathname)Pathname.create("");
    429                 }
    430                 jars = jars.push(jarPathname);
    431             } else {
    432                 URL url = null;
    433                 try {
    434                     url = new URL(jar.substring("jar:".length(), jar.length() - 2));
    435                     Pathname p = (Pathname)Pathname.create(url);
    436                     jars = jars.push(p);
    437                 } catch (MalformedURLException e) {
    438                     error(new LispError("Failed to parse URL "
    439                                         + "'" + url + "'"
    440                                         + e.getMessage()));
    441                 }
    442             }
    443             jars = jars.nreverse();
    444             setDevice(jars);
    445             invalidateNamestring();
    446             return;
     368          return (PathnameJar)PathnameJar.create(s);
    447369        }
    448370
     
    450372        final int separatorIndex = s.lastIndexOf(jarSeparator);
    451373        if (separatorIndex > 0 && s.startsWith("jar:")) {
    452             final String jarURL = s.substring(0, separatorIndex + jarSeparator.length());
    453             URL url = null;
    454             try {
    455                 url = new URL(jarURL);
    456             } catch (MalformedURLException ex) {
    457                 error(new LispError("Failed to parse URL "
    458                                     + "'" + jarURL + "'"
    459                                     + ex.getMessage()));
    460             }
    461             Pathname d = (Pathname)Pathname.create(url);
    462             if (getDevice() instanceof Cons) {
    463                 LispObject[] jars = d.copyToArray();
    464                 //  XXX Is this ever reached?  If so, need to append lists
    465                 Debug.assertTrue(false);
    466             } else {
    467                 setDevice(d.getDevice());
    468             }
    469             s = "/" + s.substring(separatorIndex + jarSeparator.length());
    470             Pathname p = (Pathname)Pathname.create("file:" + s); // Use URI escaping rules
    471             setDirectory(p.getDirectory());
    472             setName(p.getName());
    473             setType(p.getType());
    474             setVersion(p.getVersion());
    475             return;
    476         }
    477 
    478         // A URL
     374          return (PathnameJar)PathnameJar.create(s);
     375        }
     376
     377        // A URL (anything with a scheme that is not a logical
     378        // pathname, and not a JAR file or an entry in a JAR file)
    479379        if (isValidURL(s)) {
    480             URL url = null;
    481             try {
    482                 url = new URL(s);
    483             } catch (MalformedURLException e) {
    484                 Debug.assertTrue(false);
    485             }
    486             String scheme = url.getProtocol();
    487             if (scheme.equals("file")) {
    488                 URI uri = null;
    489                 try {
    490                     uri = new URI(s);
    491                 } catch (URISyntaxException ex) {
    492                     error(new SimpleError("Improper URI syntax for "
    493                                     + "'" + url.toString() + "'"
    494                                     + ": " + ex.toString()));
    495                 }
    496            
    497                 String uriPath = uri.getPath();
    498                 if (null == uriPath) {
    499                                   // Under Windows, deal with pathnames containing
    500                                   // devices expressed as "file:z:/foo/path"
    501                                   uriPath = uri.getSchemeSpecificPart();
    502                                   if (uriPath == null || uriPath.equals("")) {
    503                     error(new LispError("The URI has no path: " + uri));
    504                   }
    505                 }
    506                 final File file = new File(uriPath);
    507                 String path = file.getPath();
    508                 if (uri.toString().endsWith("/") && !path.endsWith("/")) {
    509                   path += "/";
    510                 }
    511                 final Pathname p = (Pathname)Pathname.create(path);
    512                 this.setHost(p.getHost());
    513                 this.setDevice(p.getDevice());
    514                 this.setDirectory(p.getDirectory());
    515                 this.setName(p.getName());
    516                 this.setType(p.getType());
    517                 this.setVersion(p.getVersion());
    518                 return;
    519             }
    520             Debug.assertTrue(scheme != null);
    521             URI uri = null;
    522             try {
    523                 uri = url.toURI().normalize();
    524             } catch (URISyntaxException e) {
    525                 error(new LispError("Couldn't form URI from "
    526                                     + "'" + url + "'"
    527                                     + " because: " + e));
    528             }
    529             String authority = uri.getAuthority();
    530         if (authority == null) {
    531           authority = url.getAuthority();
    532           if (authority == null) {
    533             Debug.trace(MessageFormat.format("{0} has a null authority.", url));
    534           }
    535         }
    536 
    537         setHost(NIL);
    538         setHost(getHost().push(SCHEME));
    539         setHost(getHost().push(new SimpleString(scheme)));
    540 
    541         if (authority != null) {
    542           setHost(getHost().push(AUTHORITY));
    543           setHost(getHost().push(new SimpleString(authority)));
    544         }
    545 
    546         setDevice(NIL);
    547            
    548         // URI encode necessary characters
    549         String path = uri.getRawPath();
    550         if (path == null) {
    551           path = "";
    552         }
    553         String query = uri.getRawQuery();
    554         if (query != null) {
    555           setHost(getHost().push(QUERY));
    556                 setHost(getHost().push(new SimpleString(query)));
    557             }
    558             String fragment = uri.getRawFragment();
    559             if (fragment != null) {
    560                 setHost(getHost().push(FRAGMENT));
    561                 setHost(getHost().push(new SimpleString(fragment)));
    562             }
    563             Pathname p = (Pathname)Pathname.create(path != null ? path : "");
    564 
    565             setDirectory(p.getDirectory());
    566             setName(p.getName());
    567             setType(p.getType());
    568            
    569             setHost(getHost().nreverse());
    570             invalidateNamestring();
    571             return;
     380          return (PathnameURL)PathnameURL.create(s);
    572381        }
    573382
     
    586395            }
    587396        }
    588         namestring = s;
     397        //        namestring = s;
    589398        if (Utilities.isPlatformWindows) {
    590399            if (s.length() >= 2 && s.charAt(1) == ':') {
    591                 setDevice(new SimpleString(s.charAt(0)));
     400                result.setDevice(new SimpleString(s.charAt(0)));
    592401                s = s.substring(2);
    593402            }
     
    607416                s = "";
    608417            }
    609             setDirectory(parseDirectory(d));
     418            result.setDirectory(parseDirectory(d));
    610419        }
    611420        if (s.startsWith(".")
     
    613422            && (s.indexOf(".", 1) == -1
    614423                || s.substring(s.length() -1).equals("."))) {
    615             setName(new SimpleString(s));
    616             return;
     424            result.setName(new SimpleString(s));
     425            return result;
    617426        }
    618427        int index = s.lastIndexOf('.');
     
    627436        if (n != null) {
    628437            if (n.equals("*")) {
    629                 setName(Keyword.WILD);
     438                result.setName(Keyword.WILD);
    630439            } else {
    631                 setName(new SimpleString(n));
     440                result.setName(new SimpleString(n));
    632441            }
    633442        }
    634443        if (t != null) {
    635444            if (t.equals("*")) {
    636                 setType(Keyword.WILD);
     445                result.setType(Keyword.WILD);
    637446            } else {
    638                 setType(new SimpleString(t));
    639             }
    640         }
    641     }
     447                result.setType(new SimpleString(t));
     448            }
     449        }
     450        return result;
     451  }
    642452
    643453    private static final LispObject parseDirectory(String d) {
     
    772582        if (getDevice() == NIL) {
    773583        } else if (getDevice() == Keyword.UNSPECIFIC) {
    774         } else if (isJar()) {
    775             LispObject[] jars = ((Cons) getDevice()).copyToArray();
    776             StringBuilder prefix = new StringBuilder();
    777             for (int i = 0; i < jars.length; i++) {
    778                 prefix.append("jar:");
    779                 LispObject component = jars[i];
    780                 if (!(component instanceof Pathname)) {
    781                   return null; // If DEVICE is a CONS, it should only contain Pathname
    782                 }
    783                 if (! ((Pathname)component).isURL() && i == 0) {
    784                   sb.append("file:");
    785                   uriEncoded = true;
    786                 }
    787                 Pathname jar = (Pathname) component;
    788                 String encodedNamestring;
    789                 if (uriEncoded) {
    790                   encodedNamestring = uriEncode(jar.getNamestring());
    791                 } else {
    792                   encodedNamestring = jar.getNamestring();
    793                 }
    794                 sb.append(encodedNamestring);
    795                 sb.append("!/");
    796             }
    797             sb = prefix.append(sb);
     584        // } else if (isJar()) {
     585        //     LispObject[] jars = ((Cons) getDevice()).copyToArray();
     586        //     StringBuilder prefix = new StringBuilder();
     587        //     for (int i = 0; i < jars.length; i++) {
     588        //         prefix.append("jar:");
     589        //         LispObject component = jars[i];
     590        //         if (!(component instanceof Pathname)) {
     591        //           return null; // If DEVICE is a CONS, it should only contain Pathname
     592        //         }
     593        //         if (! ((Pathname)component).isURL() && i == 0) {
     594        //           sb.append("file:");
     595        //           uriEncoded = true;
     596        //         }
     597        //         Pathname jar = (Pathname) component;
     598        //         String encodedNamestring;
     599        //         if (uriEncoded) {
     600        //           encodedNamestring = uriEncode(jar.getNamestring());
     601        //         } else {
     602        //           encodedNamestring = jar.getNamestring();
     603        //         }
     604        //         sb.append(encodedNamestring);
     605        //         sb.append("!/");
     606        //     }
     607        //     sb = prefix.append(sb);
    798608        } else if (getDevice() instanceof AbstractString) {
    799609            sb.append(getDevice().getStringValue());
     
    809619            directoryNamestring = uriEncode(directoryNamestring);
    810620        }
    811         if (isJar()) {
    812             if (directoryNamestring.startsWith("/")) {
    813                 sb.append(directoryNamestring.substring(1));
    814             } else {
    815                 sb.append(directoryNamestring);
    816             }
    817         } else {
     621        // if (isJar()) {
     622        //     if (directoryNamestring.startsWith("/")) {
     623        //         sb.append(directoryNamestring.substring(1));
     624        //     } else {
     625        //         sb.append(directoryNamestring);
     626        //     }
     627        // } else {
    818628            sb.append(directoryNamestring);
    819         }
     629            //        }
    820630        if (getName() instanceof AbstractString) {
    821631            String n = getName().getStringValue();
     
    855665        }
    856666       
    857         if (isURL()) {
    858             LispObject o = Symbol.GETF.execute(getHost(), QUERY, NIL);
    859             if (o != NIL) {
    860                 sb.append("?");
    861                 sb.append(o.getStringValue());
    862             }
    863             o = Symbol.GETF.execute(getHost(), FRAGMENT, NIL);
    864             if (o != NIL) {
    865                 sb.append("#");
    866                 sb.append(o.getStringValue());
    867             }
    868         }
     667        // if (isURL()) {
     668        //     LispObject o = Symbol.GETF.execute(getHost(), QUERY, NIL);
     669        //     if (o != NIL) {
     670        //         sb.append("?");
     671        //         sb.append(o.getStringValue());
     672        //     }
     673        //     o = Symbol.GETF.execute(getHost(), FRAGMENT, NIL);
     674        //     if (o != NIL) {
     675        //         sb.append("#");
     676        //         sb.append(o.getStringValue());
     677        //     }
     678        // }
    869679           
    870680        if (this instanceof LogicalPathname) {
     
    935745    }
    936746
    937     /** @return The representation of this pathname suitable for
    938      *  referencing an entry in a Zip/JAR file
    939      */
    940     protected String asEntryPath() {
    941         Pathname p = Pathname.create();
    942         p.setDirectory(getDirectory());
    943         p.setName(getName());
    944         p.setType(getType());
    945         p.invalidateNamestring();
    946         String path = p.getNamestring();
    947         StringBuilder result = new StringBuilder();
    948         result.append(path);
    949 
    950         // Entries in jar files are always relative, but Pathname
    951         // directories are :ABSOLUTE.
    952         if (result.length() > 1
    953           && result.substring(0, 1).equals("/")) {
    954             return result.substring(1);
    955         }
    956         return result.toString();
    957     }
    958747
    959748    @Override
     
    1012801    public boolean equalp(LispObject obj) {
    1013802        return equal(obj);
     803    }
     804
     805    public boolean equals(Object o) {
     806      if (!(this.getClass().isAssignableFrom(o.getClass()))) {
     807        return super.equals(o);
     808      }
     809      return equal((Pathname)o);
     810    }
     811
     812    public int hashCode() {
     813      return sxhash();
    1014814    }
    1015815
     
    16861486        @Override
    16871487        public LispObject execute(LispObject arg, LispObject resolveSymlinks) {
    1688             Pathname pathname = coerceToPathname(arg);
    1689             if (pathname instanceof LogicalPathname) {
    1690                 pathname = LogicalPathname.translateLogicalPathname((LogicalPathname) pathname);
    1691             }
    1692 
    1693             LispObject result = NIL;
    1694             if (pathname.isJar()) {
    1695                 String directory = pathname.asEntryPath();
    1696                 Debug.assertTrue(directory != null);  // We should only be listing directories
    1697 
    1698                 if (pathname.getDevice().cdr() instanceof Cons) {
    1699                     return error(new FileError("Unimplemented directory listing of JAR within JAR.", pathname));
    1700                 }
    1701 
    1702                 if (directory.length() == 0) {
    1703                     directory = "/*";
    1704                 } else {
    1705                     if (directory.endsWith("/")) {
    1706                         directory = "/" + directory + "*";
    1707                     } else {
    1708                         directory = "/" + directory + "/*";
    1709                     }
    1710                 }
    1711                 SimpleString wildcard = new SimpleString(directory);
    1712                 SimpleString wildcardDirectory = new SimpleString(directory + "/");
    1713 
    1714                 ZipFile jar = ZipCache.get((Pathname)pathname.getDevice().car());
    1715                 LispObject matches;
    1716                 for (Enumeration<? extends ZipEntry> entries = jar.entries();
    1717                      entries.hasMoreElements();) {
    1718                     ZipEntry entry = entries.nextElement();
    1719                     String entryName = "/" + entry.getName();
    1720 
    1721                     if (entryName.endsWith("/")) {
    1722                         matches = Symbol.PATHNAME_MATCH_P
    1723                             .execute(new SimpleString(entryName), wildcardDirectory);
    1724                     } else {
    1725                         matches = Symbol.PATHNAME_MATCH_P.
    1726                             execute(new SimpleString(entryName), wildcard);
    1727                     }
    1728                     if (!matches.equals(NIL)) {
    1729                         String namestring = new String(pathname.getNamestring());
    1730                         namestring = namestring.substring(0, namestring.lastIndexOf("!/") + 2)
    1731                                  + entry.getName();
    1732                         Pathname p = (Pathname)Pathname.create(namestring);
    1733                         result = new Cons(p, result);
    1734                     }
    1735                 }
    1736                 return result;
    1737             }
    1738 
    1739             if (pathname.isURL()) {
    1740                 return error(new LispError("Unimplemented.")); // XXX
    1741             }
    1742 
    1743             String s = pathname.getNamestring();
    1744             if (s != null) {
    1745                 File f = new File(s);
    1746                 if (f.isDirectory()) {
    1747                     try {
    1748                         File[] files = f.listFiles();
    1749                         if (files == null) {
    1750                             return error(new FileError("Unable to list directory "
    1751                                                        + pathname.princToString() + ".",
    1752                                                        pathname));
    1753                         }
    1754                         for (int i = files.length; i-- > 0;) {
    1755                             File file = files[i];
    1756                             Pathname p;
    1757                             String path;
    1758                             if (resolveSymlinks == NIL) {
    1759                               path = file.getAbsolutePath();
    1760                             } else {
    1761                               path = file.getCanonicalPath();
    1762                             }
    1763                             URI pathURI = (new File(path)).toURI();
    1764                             p = (Pathname)Pathname.create(pathURI);
    1765                             result = new Cons(p, result);
    1766                         }
    1767                     } catch (IOException e) {
    1768                         return error(new FileError("Unable to list directory "
    1769                                                    + pathname.princToString() + ".",
    1770                                                    pathname));
    1771                     } catch (SecurityException e) {
    1772                         Debug.trace(e);
    1773                     } catch (NullPointerException e) {
    1774                         Debug.trace(e);
    1775                     }
    1776                 }
    1777             }
    1778             return result;
    1779         }
    1780     }
    1781 
    1782     @DocString(name="match-wild-jar-pathname",
    1783                args="wild-jar-pathname",
    1784                returns="pathnames",
    1785     doc="Returns the pathnames matching WILD-JAR-PATHNAME which is both wild and a jar-pathname.")
    1786     static final Primitive MATCH_WILD_JAR_PATHNAME = new pf_match_wild_jar_pathname();
    1787     private static class pf_match_wild_jar_pathname extends Primitive {
    1788         pf_match_wild_jar_pathname() {
    1789             super("match-wild-jar-pathname", PACKAGE_SYS, false, "wild-jar-pathname");
    1790         }
    1791         @Override
    1792         public LispObject execute(LispObject arg) {
    1793             Pathname pathname = coerceToPathname(arg);
    1794             if (pathname instanceof LogicalPathname) {
    1795                 pathname = LogicalPathname.translateLogicalPathname((LogicalPathname) pathname);
    1796             }
    1797             if (!pathname.isJar()) {
    1798                 return new FileError("Not a jar pathname.", pathname);
    1799             }
    1800             if (!pathname.isWild()) {
    1801                 return new FileError("Not a wild pathname.", pathname);
    1802             }
    1803             Pathname jarPathname = Pathname.create(pathname);
    1804             jarPathname.setDirectory(NIL);
    1805             jarPathname.setName(NIL);
    1806             jarPathname.setType(NIL);
    1807             jarPathname.invalidateNamestring();
    1808             LispObject jarTruename = truename(jarPathname, false);
    1809            
    1810             // We can't match anything in a non-existent jar
    1811             if (jarTruename == NIL) {
    1812                 return NIL;
    1813             }
    1814            
    1815             LispObject result = NIL;
    1816             String wild = "/" + pathname.asEntryPath();
    1817             final SimpleString wildcard = new SimpleString(wild);
    1818 
    1819             if (pathname.getDevice().cdr() instanceof Cons) {
    1820                 ZipFile outerJar = ZipCache.get((Pathname)pathname.getDevice().car());
    1821                 String entryPath = ((Pathname)pathname.getDevice().cdr().car()).getNamestring(); //???
    1822                 if (entryPath.startsWith("/")) {
    1823                     entryPath = entryPath.substring(1);
    1824                 }
    1825                 ZipEntry entry = outerJar.getEntry(entryPath);
    1826                 InputStream inputStream = null;
    1827                 try {
    1828                     inputStream = outerJar.getInputStream(entry);
    1829                 } catch (IOException e) {
    1830                     return new FileError("Failed to read zip input stream inside zip.",
    1831                                          pathname);
    1832                 }
    1833                 ZipInputStream zipInputStream
    1834                     = new ZipInputStream(inputStream);
    1835 
    1836                 try {
    1837                     while ((entry = zipInputStream.getNextEntry()) != null) {
    1838                         String entryName = "/" + entry.getName();
    1839                         LispObject matches = Symbol.PATHNAME_MATCH_P
    1840                             .execute(new SimpleString(entryName), wildcard);
    1841                    
    1842                         if (!matches.equals(NIL)) {
    1843                             String namestring = new String(pathname.getNamestring());
    1844                             namestring = namestring.substring(0, namestring.lastIndexOf("!/") + 2)
    1845                                 + entry.getName();
    1846                             Pathname p = (Pathname)Pathname.create(namestring);
    1847                             result = new Cons(p, result);
    1848                         }
    1849                     }
    1850                 } catch (IOException e) {
    1851                     return new FileError("Failed to seek through zip inputstream inside zip.",
    1852                                          pathname);
    1853                 }
    1854             } else {
    1855                 ZipFile jar = ZipCache.get((Pathname)pathname.getDevice().car());
    1856                 for (Enumeration<? extends ZipEntry> entries = jar.entries();
    1857                      entries.hasMoreElements();)
    1858                     {
    1859                         ZipEntry entry = entries.nextElement();
    1860                         String entryName = "/" + entry.getName();
    1861                         LispObject matches = Symbol.PATHNAME_MATCH_P
    1862                             .execute(new SimpleString(entryName), wildcard);
    1863 
    1864                         if (!matches.equals(NIL)) {
    1865                             String namestring = new String(pathname.getNamestring());
    1866                             namestring = namestring.substring(0, namestring.lastIndexOf("!/") + 2)
    1867                                 + entry.getName();
    1868                             Pathname p = (Pathname)Pathname.create(namestring);
    1869                             result = new Cons(p, result);
    1870                         }
    1871                     }
    1872             }
    1873             return result;
    1874         }
    1875     }
     1488          Pathname pathname = coerceToPathname(arg);
     1489          if (pathname instanceof LogicalPathname) {
     1490            pathname = LogicalPathname.translateLogicalPathname((LogicalPathname) pathname);
     1491          }
     1492
     1493          LispObject result = NIL;
     1494          if (pathname.isJar()) {
     1495            return PathnameJar.listDirectory((PathnameJar)pathname);
     1496          }
     1497
     1498          // "All" pathnames are PathnameURLs now, so this condition needs to be tightened
     1499          // should be all remote URLs can't have directory listings?
     1500          // if (pathname.isURL()) {
     1501          //   return error(new LispError("Unimplemented.")); // XXX
     1502          // }
     1503
     1504          String s = pathname.getNamestring();
     1505          if (s != null) {
     1506            File f = new File(s);
     1507            if (f.isDirectory()) {
     1508              try {
     1509                File[] files = f.listFiles();
     1510                if (files == null) {
     1511                  return error(new FileError("Unable to list directory "
     1512                                             + pathname.princToString() + ".",
     1513                                             pathname));
     1514                }
     1515                for (int i = files.length; i-- > 0;) {
     1516                  File file = files[i];
     1517                  Pathname p;
     1518                  String path;
     1519                  if (resolveSymlinks == NIL) {
     1520                    path = file.getAbsolutePath();
     1521                  } else {
     1522                    path = file.getCanonicalPath();
     1523                  }
     1524                  URI pathURI = (new File(path)).toURI();
     1525                  p = (Pathname)Pathname.create(pathURI);
     1526                  result = new Cons(p, result);
     1527                }
     1528              } catch (IOException e) {
     1529                return error(new FileError("Unable to list directory "
     1530                                           + pathname.princToString() + ".",
     1531                                           pathname));
     1532              } catch (SecurityException e) {
     1533                Debug.trace(e);
     1534              } catch (NullPointerException e) {
     1535                Debug.trace(e);
     1536              }
     1537            }
     1538          }
     1539          return result;
     1540        }
     1541    };
    18761542
    18771543    public boolean isAbsolute()  {
     
    20281694        }
    20291695    }
    2030 
     1696 
    20311697    static final Primitive MERGE_PATHNAMES = new pf_merge_pathnames();
    20321698    @DocString(name="merge-pathnames",
     
    20371703    static final class pf_merge_pathnames extends Primitive {
    20381704        pf_merge_pathnames() {
    2039             super("merge-pathnames", "pathname &optional default-pathname default-version");
     1705            super(Symbol.MERGE_PATHNAMES, "pathname &optional default-pathname default-version");
    20401706        }
    20411707        @Override
    20421708        public LispObject execute(LispObject arg) {
    2043             Pathname pathname = coerceToPathname(arg);
    2044             Pathname defaultPathname =
    2045                 coerceToPathname(Symbol.DEFAULT_PATHNAME_DEFAULTS.symbolValue());
    2046             LispObject defaultVersion = Keyword.NEWEST;
    2047             return mergePathnames(pathname, defaultPathname, defaultVersion);
     1709          Pathname pathname = coerceToPathname(arg);
     1710          Pathname defaultPathname
     1711            = coerceToPathname(Symbol.DEFAULT_PATHNAME_DEFAULTS.symbolValue());
     1712          LispObject defaultVersion = Keyword.NEWEST;
     1713          return mergePathnames(pathname, defaultPathname, defaultVersion);
    20481714        }
    20491715        @Override
    20501716        public LispObject execute(LispObject first, LispObject second) {
    20511717            Pathname pathname = coerceToPathname(first);
    2052             Pathname defaultPathname =
    2053                 coerceToPathname(second);
     1718            Pathname defaultPathname = coerceToPathname(second);
    20541719            LispObject defaultVersion = Keyword.NEWEST;
    20551720            return mergePathnames(pathname, defaultPathname, defaultVersion);
     
    20591724                                  LispObject third) {
    20601725            Pathname pathname = coerceToPathname(first);
    2061             Pathname defaultPathname =
    2062                 coerceToPathname(second);
     1726            Pathname defaultPathname = coerceToPathname(second);
    20631727            LispObject defaultVersion = third;
    20641728            return mergePathnames(pathname, defaultPathname, defaultVersion);
     
    20661730    }
    20671731
    2068     public static final Pathname mergePathnames(Pathname pathname, Pathname defaultPathname) {
    2069         return mergePathnames(pathname, defaultPathname, Keyword.NEWEST);
    2070     }
     1732  public static final LispObject mergePathnames(Pathname pathname, Pathname defaultPathname) {
     1733    return mergePathnames(pathname, defaultPathname, Keyword.NEWEST);
     1734  }
    20711735   
    2072     public static final Pathname mergePathnames(final Pathname pathname,
    2073                                                 final Pathname defaultPathname,
    2074                                                 final LispObject defaultVersion)
    2075     {
    2076         Pathname result;
    2077         Pathname p = Pathname.create(pathname);
    2078         Pathname d;
    2079 
    2080         if (pathname instanceof LogicalPathname) {
    2081             result = LogicalPathname.create();
     1736  public static final LispObject mergePathnames(final Pathname pathname,
     1737                                              final Pathname defaultPathname,
     1738                                              final LispObject defaultVersion) {
     1739    Pathname result;
     1740    Pathname p = Pathname.create(pathname);
     1741    Pathname d;
     1742
     1743      if (pathname instanceof LogicalPathname) {
     1744        result = LogicalPathname.create();
     1745        d = Pathname.create(defaultPathname);
     1746      } else {
     1747        if ((pathname instanceof PathnameJar)
     1748            || (defaultPathname instanceof PathnameJar)) {
     1749          result = PathnameJar.create();
     1750        } else {
     1751          result = Pathname.create();
     1752        }
     1753             
     1754        if (defaultPathname instanceof LogicalPathname) {
     1755          d = LogicalPathname.translateLogicalPathname((LogicalPathname) defaultPathname);
     1756        } else {
     1757          if (defaultPathname instanceof PathnameJar) {
     1758            d = PathnameJar.create(defaultPathname);
     1759          } else {
    20821760            d = Pathname.create(defaultPathname);
     1761          }
     1762        }
     1763      }
     1764
     1765      if (pathname.getHost() != NIL) {
     1766        result.setHost(p.getHost());
     1767      } else {
     1768        result.setHost(d.getHost());
     1769      }
     1770
     1771      if (pathname.getDevice() != NIL) {
     1772        result.setDevice(p.getDevice());
     1773      } else {
     1774        if (d instanceof PathnameJar) {
     1775          // If the defaults contain a JAR-PATHNAME, and the pathname
     1776          // to be be merged does not have a specified DEVICE, a
     1777          // specified HOST, and doesn't contain a relative DIRECTORY,
     1778          // then on non-MSDOG, set its device to :UNSPECIFIC.
     1779          if (pathname.getHost() == NIL
     1780              && pathname.getDevice() == NIL
     1781              && d.isJar()
     1782              && !Utilities.isPlatformWindows) {
     1783            if (pathname.getDirectory() != NIL
     1784                && pathname.getDirectory().car() == Keyword.ABSOLUTE) {
     1785              result.setDevice(Keyword.UNSPECIFIC);
     1786            } else {
     1787              result.setDevice(d.getDevice());
     1788            }
     1789          } else {
     1790            result.setDevice(d.getDevice());
     1791          }
     1792        }
     1793      }
     1794
     1795      // This part I no longer understand
     1796      // if (pathname.isJar()) {
     1797      //   Cons jars = (Cons)result.getDevice();
     1798      //   LispObject jar = jars.car;
     1799      //   if (jar instanceof Pathname) {
     1800      //     Pathname defaults = Pathname.create(d);
     1801      //     if (defaults.isJar()) {
     1802      //       defaults.setDevice(NIL);
     1803      //     }
     1804      //     Pathname o = mergePathnames((Pathname)jar, defaults);
     1805      //     if (o.getDirectory() instanceof Cons
     1806      //         && ((Cons)o.getDirectory()).length() == 1) { // i.e. (:ABSOLUTE) or (:RELATIVE)
     1807      //       o.setDirectory(NIL);
     1808      //     }
     1809      //     ((Cons)result.device).car = o;
     1810      //   }
     1811      //   result.setDirectory(p.getDirectory());
     1812      // } else {
     1813      //   result.setDirectory(mergeDirectories(p.getDirectory(), d.getDirectory()));
     1814      // }
     1815      result.setDirectory(mergeDirectories(p.getDirectory(), d.getDirectory()));
     1816     
     1817      if (pathname.getName() != NIL) {
     1818        result.setName(p.getName());
     1819      } else {
     1820        result.setName(d.getName());
     1821      }
     1822      if (pathname.getType() != NIL) {
     1823        result.setType(p.getType());
     1824      } else {
     1825        result.setType(d.getType());
     1826      }
     1827      //  CLtLv2 MERGE-PATHNAMES
     1828   
     1829      // "[T]he missing components in the given pathname are filled
     1830      // in from the defaults pathname, except that if no version is
     1831      // specified the default version is used."
     1832
     1833      // "The merging rules for the version are more complicated and
     1834      // depend on whether the pathname specifies a name. If the
     1835      // pathname doesn't specify a name, then the version, if not
     1836      // provided, will come from the defaults, just like the other
     1837      // components. However, if the pathname does specify a name,
     1838      // then the version is not affected by the defaults. The
     1839      // reason is that the version ``belongs to'' some other file
     1840      // name and is unlikely to have anything to do with the new
     1841      // one. Finally, if this process leaves the
     1842      // version missing, the default version is used."
     1843      if (p.getVersion() != NIL) {
     1844        result.setVersion(p.getVersion());
     1845      } else if (p.getName() == NIL) {
     1846        if (defaultPathname.getVersion() == NIL) {
     1847          result.setVersion(defaultVersion);
    20831848        } else {
    2084             result = Pathname.create();
    2085             if (defaultPathname instanceof LogicalPathname) {
    2086                 d = LogicalPathname.translateLogicalPathname((LogicalPathname) defaultPathname);
    2087             } else {
    2088                 d = Pathname.create(defaultPathname);
    2089             }
    2090         }
    2091         if (pathname.getHost() != NIL) {
    2092           result.setHost(p.getHost());
    2093         } else {
    2094           result.setHost(d.getHost());
    2095         }
    2096 
    2097         if (pathname.getDevice() != NIL) { // XXX if device represent JARs we want to merge
    2098           result.setDevice(p.getDevice());
    2099         } else {
    2100             if (!p.isURL()) {
    2101                 // If the defaults contain a JAR-PATHNAME, and the
    2102                 // pathname to be be merged does not have a specified
    2103                 // DEVICE, a specified HOST, and doesn't contain a
    2104                 // relative DIRECTORY, then on non-MSDOG, set its
    2105                 // device to :UNSPECIFIC.
    2106                 if (pathname.getHost() == NIL
    2107                     && pathname.getDevice() == NIL
    2108                     && d.isJar()
    2109                     && !Utilities.isPlatformWindows) {
    2110                     if (pathname.getDirectory() != NIL
    2111                         && pathname.getDirectory().car() == Keyword.ABSOLUTE) {
    2112                       result.setDevice(Keyword.UNSPECIFIC);
    2113                     } else {
    2114                       result.setDevice(d.getDevice());
    2115                     }
    2116                 } else {
    2117                   result.setDevice(d.getDevice());
    2118                 }
    2119             }
    2120         }
    2121 
    2122         if (pathname.isJar()) {
    2123             Cons jars = (Cons)result.getDevice();
    2124             LispObject jar = jars.car;
    2125             if (jar instanceof Pathname) {
    2126                 Pathname defaults = Pathname.create(d);
    2127                 if (defaults.isJar()) {
    2128                   defaults.setDevice(NIL);
    2129                 }
    2130                 Pathname o = mergePathnames((Pathname)jar, defaults);
    2131                 if (o.getDirectory() instanceof Cons
    2132                     && ((Cons)o.getDirectory()).length() == 1) { // i.e. (:ABSOLUTE) or (:RELATIVE)
    2133                   o.setDirectory(NIL);
    2134                 }
    2135                 ((Cons)result.device).car = o;
    2136             }
    2137             result.setDirectory(p.getDirectory());
    2138         } else {
    2139           result.setDirectory(mergeDirectories(p.getDirectory(), d.getDirectory()));
    2140         }
    2141 
    2142         if (pathname.getName() != NIL) {
    2143           result.setName(p.getName());
    2144         } else {
    2145           result.setName(d.getName());
    2146         }
    2147         if (pathname.getType() != NIL) {
    2148           result.setType(p.getType());
    2149         } else {
    2150           result.setType(d.getType());
    2151         }
    2152         //  CLtLv2 MERGE-PATHNAMES
    2153    
    2154     // "[T]he missing components in the given pathname are filled
    2155     // in from the defaults pathname, except that if no version is
    2156     // specified the default version is used."
    2157 
    2158     // "The merging rules for the version are more complicated and
    2159     // depend on whether the pathname specifies a name. If the
    2160     // pathname doesn't specify a name, then the version, if not
    2161     // provided, will come from the defaults, just like the other
    2162     // components. However, if the pathname does specify a name,
    2163     // then the version is not affected by the defaults. The
    2164     // reason is that the version ``belongs to'' some other file
    2165     // name and is unlikely to have anything to do with the new
    2166     // one. Finally, if this process leaves the
    2167     // version missing, the default version is used."
    2168 
    2169         if (p.getVersion() != NIL) {
    2170           result.setVersion(p.getVersion());
    2171         } else if (p.getName() == NIL) {
    2172             if (defaultPathname.getVersion() == NIL) {
    2173               result.setVersion(defaultVersion);
    2174             } else {
    2175               result.setVersion(defaultPathname.getVersion());
    2176             }
    2177         } else if (defaultVersion == NIL) {
    2178           result.setVersion(p.getVersion());
    2179         }
    2180         if (result.getVersion() == NIL) {
    2181           result.setVersion(defaultVersion);
    2182         }
    2183 
    2184         if (pathname instanceof LogicalPathname) {
    2185             // When we're returning a logical
    2186             result.setDevice(Keyword.UNSPECIFIC);
    2187             if (result.getDirectory().listp()) {
    2188                 LispObject original = result.getDirectory();
    2189                 LispObject canonical = NIL;
    2190                 while (original != NIL) {
    2191                     LispObject component = original.car();
    2192                     if (component instanceof AbstractString) {
    2193                         component = LogicalPathname.canonicalizeStringComponent((AbstractString) component);
    2194                     }
    2195                     canonical = canonical.push(component);
    2196                     original = original.cdr();
    2197                 }
    2198                 result.setDirectory(canonical.nreverse());
    2199             }
    2200             if (result.getName() instanceof AbstractString) {
    2201               result.setName(LogicalPathname.canonicalizeStringComponent((AbstractString) result.getName()));
    2202             }
    2203             if (result.getType() instanceof AbstractString) {
    2204               result.setType(LogicalPathname.canonicalizeStringComponent((AbstractString) result.getType()));
    2205             }
    2206         }
    2207         result.invalidateNamestring();
    2208         return result;
     1849          result.setVersion(defaultPathname.getVersion());
     1850        }
     1851      } else if (defaultVersion == NIL) {
     1852        result.setVersion(p.getVersion());
     1853      }
     1854      if (result.getVersion() == NIL) {
     1855        result.setVersion(defaultVersion);
     1856      }
     1857
     1858      if (pathname instanceof LogicalPathname) {
     1859        // When we're returning a logical
     1860        result.setDevice(Keyword.UNSPECIFIC);
     1861        if (result.getDirectory().listp()) {
     1862          LispObject original = result.getDirectory();
     1863          LispObject canonical = NIL;
     1864          while (original != NIL) {
     1865            LispObject component = original.car();
     1866            if (component instanceof AbstractString) {
     1867              component = LogicalPathname.canonicalizeStringComponent((AbstractString) component);
     1868            }
     1869            canonical = canonical.push(component);
     1870            original = original.cdr();
     1871          }
     1872          result.setDirectory(canonical.nreverse());
     1873        }
     1874        if (result.getName() instanceof AbstractString) {
     1875          result.setName(LogicalPathname.canonicalizeStringComponent((AbstractString) result.getName()));
     1876        }
     1877        if (result.getType() instanceof AbstractString) {
     1878          result.setType(LogicalPathname.canonicalizeStringComponent((AbstractString) result.getType()));
     1879        }
     1880      }
     1881      return result;
    22091882    }
    22101883
     
    22451918    }
    22461919
    2247 
    22481920    public static final LispObject truename(Pathname pathname) {
    22491921        return truename(pathname, false);
     
    22641936     */
    22651937    public static final LispObject truename(Pathname pathname,
    2266                                             boolean errorIfDoesNotExist)
    2267     {
    2268         if (pathname == null || pathname.equals(NIL)) { 
    2269            return doTruenameExit(pathname, errorIfDoesNotExist);
    2270         }
    2271         if (pathname instanceof LogicalPathname) {
    2272             pathname = LogicalPathname.translateLogicalPathname((LogicalPathname) pathname);
    2273         }
    2274         if (pathname.isWild()) {
    2275             return error(new FileError("Bad place for a wild pathname.",
    2276                                        pathname));
    2277         }
    2278         if (pathname instanceof PathnameJar) {
    2279           return truename((PathnameJar)pathname, errorIfDoesNotExist);
    2280         }
    2281         if (pathname instanceof PathnameURL) {
    2282           return truename((PathnameURL)pathname, errorIfDoesNotExist);
    2283         }
    2284         if (!(pathname.isJar() || pathname.isURL())) {
    2285             Pathname result
    2286                 = mergePathnames(pathname,
    2287                                  coerceToPathname(Symbol.DEFAULT_PATHNAME_DEFAULTS.symbolValue()),
    2288                                  NIL);
    2289             final File file = result.getFile();
    2290             if (file.exists()) {
    2291                 if (file.isDirectory()) {
    2292                     result = Pathname.getDirectoryPathname(file);
    2293                 } else {
    2294                     try {
    2295                       result = (Pathname)Pathname.create(file.getCanonicalPath());
    2296                     } catch (IOException e) {
    2297                         return error(new FileError(e.getMessage(), pathname));
    2298                     }
    2299                 }
    2300                 if (Utilities.isPlatformUnix) {
    2301                   result.setDevice(Keyword.UNSPECIFIC);
    2302                 }
    2303                 return result;
    2304             }
    2305         } else if (pathname.isURL()) {
    2306             if (pathname.getInputStream() != null) {
    2307               // If there is no type, query or fragment, we check to
    2308               // see if there is URL available "underneath".
    2309               if (pathname.getName() != NIL
    2310                   && pathname.getType() == NIL
    2311                   && Symbol.GETF.execute(pathname.getHost(), QUERY, NIL) == NIL
    2312                   && Symbol.GETF.execute(pathname.getHost(), FRAGMENT, NIL) == NIL) {
    2313                 Pathname p = (Pathname)Pathname.create(pathname.getNamestring() + "/");
    2314                 if (p.getInputStream() != null) {
    2315                   return p;
    2316                 }
    2317               }
    2318               return pathname;
    2319             }
    2320         } else
    2321         jarfile: {
    2322             // Possibly canonicalize jar file directory
    2323             Cons jars = (Cons) pathname.getDevice();
    2324             LispObject o = jars.car();
    2325         if (!(o instanceof Pathname)) {
    2326            return doTruenameExit(pathname, errorIfDoesNotExist);
    2327         }
    2328             if (o instanceof Pathname
    2329                 && !(((Pathname)o).isURL())
    2330                 // XXX Silently fail to call truename() if the default
    2331                 // pathname defaults exist within a jar, as that will
    2332                 // (probably) not succeed.  The better solution would
    2333                 // probably be to parametize the value of
    2334                 // *DEFAULT-PATHNAME-DEFAULTS* on invocations of
    2335                 // truename().
    2336                 && !coerceToPathname(Symbol.DEFAULT_PATHNAME_DEFAULTS.symbolValue()).isJar())
    2337                 {
    2338                 LispObject truename = Pathname.truename((Pathname)o, errorIfDoesNotExist);
    2339                 if (truename != null && truename != NIL
    2340                     && truename instanceof Pathname) {
    2341                     Pathname truePathname = (Pathname)truename;
    2342                     // A jar that is a directory makes no sense, so exit
    2343                     if (truePathname.getNamestring().endsWith("/")) {
    2344                         break jarfile;
    2345                     }
    2346                     jars.car = truePathname;
    2347                 } else {
    2348                     break jarfile;
    2349                 }
    2350             }
    2351 
    2352             // Check for existence of a JAR file and/or JarEntry
    2353             //
    2354             // Cases:
    2355             // 1.  JAR
    2356             // 2.  JAR in JAR
    2357             // 3.  JAR with Entry
    2358             // 4.  JAR in JAR with Entry
    2359 
    2360             ZipFile jarFile = ZipCache.get((Pathname)jars.car());
    2361             String entryPath = pathname.asEntryPath();
    2362             if (jarFile != null) {
    2363                 if (jars.cdr() instanceof Cons) {
    2364                   Pathname inner = (Pathname) jars.cdr().car();
    2365                   InputStream inputStream = Utilities.getInputStream(jarFile, inner);
    2366                   if (inputStream != null) {
    2367                       if (entryPath.length() == 0) {
    2368                           return pathname; // Case 2
    2369                       } else {
    2370                           ZipInputStream zipInputStream
    2371                               = new ZipInputStream(inputStream);
    2372                           ZipEntry entry = Utilities.getEntry(zipInputStream,
    2373                                                               entryPath,
    2374                                                               false);
    2375                           if (entry != null) {
    2376                               // XXX this could possibly be a directory?
    2377                               return pathname; // Case 4
    2378                          }
    2379                       }
    2380                   }
    2381                 } else {
    2382                     if (entryPath.length() == 0) {
    2383                         return pathname; // Case 1
    2384                     } else {
    2385                         ZipEntry entry = jarFile.getEntry(entryPath);
    2386                         if (entry != null) {
    2387                             // ensure this isn't a directory
    2388                             if (entry.isDirectory()) {
    2389                                 break jarfile;
    2390                             }
    2391                             try {
    2392                                 InputStream input = jarFile.getInputStream(entry);
    2393                                 if (input != null) {
    2394                                     return pathname; // Case 3
    2395                                 }
    2396                             } catch (IOException e) {
    2397                                 break jarfile;
    2398                             }
    2399                         }
    2400                     }
    2401                 }
    2402             }
    2403         }
    2404         error:
     1938                                            boolean errorIfDoesNotExist) {
     1939      if (pathname == null || pathname.equals(NIL)) { 
     1940        return doTruenameExit(pathname, errorIfDoesNotExist);
     1941      }
     1942      if (pathname instanceof LogicalPathname) {
     1943        pathname = LogicalPathname.translateLogicalPathname((LogicalPathname) pathname);
     1944      }
     1945      if (pathname.isWild()) {
     1946        return error(new FileError("Fundamentally unable to find a truename for any wild pathname.",
     1947                                   pathname));
     1948      }
     1949      Pathname result
     1950        = (Pathname)mergePathnames(pathname,
     1951                                   coerceToPathname(Symbol.DEFAULT_PATHNAME_DEFAULTS.symbolValue()),
     1952                                   NIL);
     1953      final File file = result.getFile();
     1954      if (file.exists()) {
     1955        if (file.isDirectory()) {
     1956          result = Pathname.getDirectoryPathname(file);
     1957        } else {
     1958          try {
     1959            result = (Pathname)Pathname.create(file.getCanonicalPath());
     1960          } catch (IOException e) {
     1961            return error(new FileError(e.getMessage(), pathname));
     1962          }
     1963        }
     1964        if (Utilities.isPlatformUnix) {
     1965          result.setDevice(Keyword.UNSPECIFIC);
     1966        }
     1967        return result;
     1968      }
    24051969      return doTruenameExit(pathname, errorIfDoesNotExist);
    24061970    }
     
    24522016    }
    24532017  };
    2454 
    2455 
    2456     public InputStream getInputStream() {
    2457         InputStream result = null;
    2458         if (isJar()) {
    2459             String entryPath = asEntryPath();
    2460             // XXX We only return the bytes of an entry in a JAR
    2461             Debug.assertTrue(entryPath != null);
    2462             ZipFile jarFile = ZipCache.get((Pathname)getDevice().car());
    2463             Debug.assertTrue(jarFile != null);
    2464             // Is this a JAR within a JAR?
    2465             if (getDevice().cdr() instanceof Cons) {
    2466                 Pathname inner = (Pathname) getDevice().cdr().car();
    2467                 InputStream input = Utilities.getInputStream(jarFile, inner);
    2468                 ZipInputStream zipInputStream = new ZipInputStream(input);
    2469                 result =  Utilities.getEntryAsInputStream(zipInputStream, entryPath);
    2470             } else {
    2471                 ZipEntry entry = jarFile.getEntry(entryPath);
    2472                 if (entry == null) {
    2473                     Debug.trace("Failed to get InputStream for "   
    2474                                 + "'" + getNamestring() + "'");
    2475                     // XXX should this be fatal?
    2476                     Debug.assertTrue(false);
    2477                 }
    2478                 try {
    2479                     result = jarFile.getInputStream(entry);
    2480                 } catch (IOException e) {
    2481                     Debug.warn("Failed to get InputStream from "
    2482                                 + "'" + getNamestring() + "'"
    2483                                 + ": " + e);
    2484                 }
    2485             }
    2486         } else if (isURL()) {
    2487             URL url = this.toURL();
    2488             try {
    2489                 result = url.openStream();
    2490             } catch (IOException e) {
    2491                 Debug.warn("Failed to get InputStream from "
    2492                             + "'" + getNamestring() + "'"
    2493                             + ": " + e);
    2494             }
    2495         } else {
    2496             File file = getFile();
    2497             try {
    2498                 result = new FileInputStream(file);
    2499             } catch (IOException e) {
    2500                 Debug.warn("Failed to get InputStream from "
    2501                             + "'" + getNamestring() + "'"
    2502                             + ": " + e);
    2503             }
    2504         }
    2505         return result;
    2506     }
    2507 
    2508     /** @return Time in milliseconds since the UNIX epoch at which the
    2509      * resource was last modified, or 0 if the time is unknown.
    2510      */
    2511     public long getLastModified() {
    2512         if (!(isJar() || isURL())) {
    2513             File f = getFile();
    2514             return f.lastModified();
    2515         }
    2516 
    2517         if (isJar()) {
    2518             // JAR cases
    2519             // 0.  JAR from URL
    2520             // 1.  JAR
    2521             // 2.  JAR in JAR
    2522             // 3.  Entry in JAR
    2523             // 4.  Entry in JAR in JAR
    2524             String entryPath = asEntryPath();
    2525             Cons d = (Cons)getDevice();
    2526             if (d.cdr().equals(NIL)) {
    2527                 if (entryPath.length() == 0) {
    2528                     LispObject o = d.car();
    2529                         // 0. JAR from URL
    2530                         // 1. JAR
    2531                     return ((Pathname)o).getLastModified();
    2532                 } else {
    2533                     // 3. Entry in JAR
    2534                     final ZipEntry entry
    2535                         = ZipCache.get((Pathname)getDevice().car()).getEntry(entryPath);
    2536                     if (entry == null) {
    2537                         return 0;
    2538                     }
    2539                     final long time = entry.getTime();
    2540                     if (time == -1) {
    2541                         return 0;
    2542                     }
    2543                     return time;
    2544                 }
    2545             } else {
    2546                 ZipFile outerJar = ZipCache.get((Pathname)d.car());
    2547                 if (entryPath.length() == 0) {
    2548                     // 4.  JAR in JAR
    2549                     String jarPath = ((Pathname)d.cdr()).asEntryPath();
    2550                     final ZipEntry entry = outerJar.getEntry(jarPath);
    2551                     final long time = entry.getTime();
    2552                     if (time == -1) {
    2553                         return 0;
    2554                     }
    2555                     return time;
    2556                 } else {
    2557                     // 5. Entry in JAR in JAR
    2558                     String innerJarPath = ((Pathname)d.cdr()).asEntryPath();
    2559                     ZipEntry entry = outerJar.getEntry(entryPath);
    2560                     ZipInputStream innerJarInputStream
    2561                         = Utilities.getZipInputStream(outerJar, innerJarPath);
    2562                     ZipEntry innerEntry = Utilities.getEntry(innerJarInputStream,
    2563                                                              entryPath);
    2564                     long time = innerEntry.getTime();
    2565                     if (time == -1) {
    2566                         return 0;
    2567                     }
    2568                     return time;
    2569                 }
    2570             }
    2571         }
    2572         if (isURL()) {
    2573             return getURLConnection().getLastModified();
    2574         }
    2575         return 0;
    2576     }
    2577 
    2578     private static final Primitive MKDIR = new pf_mkdir();
    2579     @DocString(name="mkdir",
    2580                args="pathname",
    2581                returns="generalized-boolean",
    2582     doc="Attempts to create directory at PATHNAME returning the success or failure.")
    2583     private static class pf_mkdir extends Primitive {
    2584         pf_mkdir() {
    2585             super("mkdir", PACKAGE_SYS, false, "pathname");
    2586         }
    2587 
    2588         @Override
    2589         public LispObject execute(LispObject arg) {
    2590             final Pathname pathname = coerceToPathname(arg);
    2591             if (pathname.isWild()) {
    2592                 error(new FileError("Bad place for a wild pathname.", pathname));
    2593             }
    2594             Pathname defaultedPathname =
    2595                 mergePathnames(pathname,
    2596                                coerceToPathname(Symbol.DEFAULT_PATHNAME_DEFAULTS.symbolValue()),
    2597                                NIL);
    2598             if (defaultedPathname.isURL() || defaultedPathname.isJar()) {
    2599                 return new FileError("Cannot mkdir with a "
    2600                                      + (defaultedPathname.isURL() ? "URL" : "jar")
    2601                                      + " Pathname.",
    2602                                      defaultedPathname);
    2603             }
     2018 
     2019
     2020  public InputStream getInputStream() {
     2021    InputStream result = null;
     2022    File file = getFile();
     2023    try {
     2024      result = new FileInputStream(file);
     2025    } catch (IOException e) {
     2026      simple_error("Failed to get InputStream from ~a because ~a", this,  e);
     2027    }
     2028    return result;
     2029  }
     2030
     2031
     2032
     2033  /** @return Time in milliseconds since the UNIX epoch at which the
     2034   * resource was last modified, or 0 if the time is unknown.
     2035   */
     2036  public long getLastModified() {
     2037    File f = getFile();
     2038    return f.lastModified();
     2039  }
     2040
     2041
     2042  private static final Primitive MKDIR = new pf_mkdir();
     2043  @DocString(name="mkdir",
     2044             args="pathname",
     2045             returns="generalized-boolean",
     2046             doc="Attempts to create directory at PATHNAME returning the success or failure.")
     2047 private static class pf_mkdir extends Primitive {
     2048   pf_mkdir() {
     2049     super("mkdir", PACKAGE_SYS, false, "pathname");
     2050   }
     2051
     2052   @Override
     2053   public LispObject execute(LispObject arg) {
     2054     final Pathname pathname = coerceToPathname(arg);
     2055     if (pathname.isWild()) {
     2056       error(new FileError("Bad place for a wild pathname.", pathname));
     2057     }
     2058     Pathname defaultedPathname
     2059       = (Pathname)mergePathnames(pathname,
     2060                                  coerceToPathname(Symbol.DEFAULT_PATHNAME_DEFAULTS.symbolValue()),
     2061                                  NIL);
     2062     if (defaultedPathname.isURL() || defaultedPathname.isJar()) {
     2063       return new FileError("Cannot mkdir with a "
     2064                            + (defaultedPathname.isURL() ? "URL" : "jar")
     2065                            + " Pathname.",
     2066                            defaultedPathname);
     2067     }
    26042068                   
    2605             File file = defaultedPathname.getFile();
    2606             return file.mkdir() ? T : NIL;
    2607         }
    2608     }
    2609 
    2610     private static final Primitive RENAME_FILE = new pf_rename_file();
    2611     @DocString(name="rename-file",
     2069     File file = defaultedPathname.getFile();
     2070     return file.mkdir() ? T : NIL;
     2071   }
     2072 }
     2073
     2074  private static final Primitive RENAME_FILE = new pf_rename_file();
     2075  @DocString(name="rename-file",
    26122076               args="filespec new-name",
    26132077               returns="defaulted-new-name, old-truename, new-truename",
     
    26192083               + "value, OLD-TRUENAME, is the truename of the file before it was renamed. The tertiary \n"
    26202084               + "value, NEW-TRUENAME, is the truename of the file after it was renamed.\n")
    2621     private static class pf_rename_file extends Primitive {
    2622         pf_rename_file() {
    2623             super("rename-file", "filespec new-name");
    2624         }
    2625         @Override
    2626         public LispObject execute(LispObject first, LispObject second) {
    2627             Pathname oldPathname = coerceToPathname(first);
    2628             Pathname oldTruename = (Pathname) truename(oldPathname, true);
    2629             Pathname newName = coerceToPathname(second);
    2630             if (newName.isWild()) {
    2631                 error(new FileError("Bad place for a wild pathname.", newName));
    2632             }
    2633             if (oldTruename.isJar()) {
    2634                 error(new FileError("Bad place for a jar pathname.", oldTruename));
    2635             }
    2636             if (newName.isJar()) {
    2637                 error(new FileError("Bad place for a jar pathname.", newName));
    2638             }
    2639             if (oldTruename.isURL()) {
    2640                 error(new FileError("Bad place for a URL pathname.", oldTruename));
    2641             }
    2642             if (newName.isURL()) {
    2643                 error(new FileError("Bad place for a jar pathname.", newName));
    2644             }
    2645                
    2646             Pathname defaultedNewName = mergePathnames(newName, oldTruename, NIL);
    2647 
    2648             File source = oldTruename.getFile();
    2649             File destination = null;
    2650             if (defaultedNewName instanceof LogicalPathname) {
    2651                 destination = LogicalPathname.translateLogicalPathname((LogicalPathname)defaultedNewName)
    2652                     .getFile();
    2653             } else {
    2654                 destination = defaultedNewName.getFile();
    2655             }
    2656             // By default, MSDOG doesn't allow one to remove files that are open.
    2657             if (Utilities.isPlatformWindows) {
    2658               if (destination.isFile()) {
    2659                 ZipCache.remove(destination);
    2660                 destination.delete();
    2661               }
    2662             }
    2663             if (source.renameTo(destination)) { // Success!
    2664               Pathname newTruename = (Pathname)truename(defaultedNewName, true);
    2665               return LispThread.currentThread().setValues(defaultedNewName,
    2666                                                           oldTruename,
    2667                                                           newTruename);
    2668             }
    2669             return error(new FileError("Unable to rename "
    2670                                        + oldTruename.princToString()
    2671                                        + " to " + newName.princToString()
    2672                                        + ".",
    2673                                        oldTruename));
    2674         }
    2675     }
     2085  private static class pf_rename_file extends Primitive {
     2086    pf_rename_file() {
     2087      super("rename-file", "filespec new-name");
     2088    }
     2089    @Override
     2090    public LispObject execute(LispObject first, LispObject second) {
     2091      Pathname oldPathname = coerceToPathname(first);
     2092      Pathname oldTruename = (Pathname) Symbol.TRUENAME.execute(oldPathname);
     2093      Pathname newName = coerceToPathname(second);
     2094      if (newName.isWild()) {
     2095        error(new FileError("Bad place for a wild pathname.", newName));
     2096      }
     2097      if (oldTruename.isJar()) {
     2098        error(new FileError("Bad place for a jar pathname.", oldTruename));
     2099      }
     2100      if (newName.isJar()) {
     2101        error(new FileError("Bad place for a jar pathname.", newName));
     2102      }
     2103      if (oldTruename.isURL()) {
     2104        error(new FileError("Bad place for a URL pathname.", oldTruename));
     2105      }
     2106      if (newName.isURL()) {
     2107        error(new FileError("Bad place for a jar pathname.", newName));
     2108      }
     2109     
     2110      Pathname defaultedNewName = (Pathname)mergePathnames(newName, oldTruename, NIL);
     2111     
     2112      File source = oldTruename.getFile();
     2113      File destination = null;
     2114      if (defaultedNewName instanceof LogicalPathname) {
     2115        destination = LogicalPathname.translateLogicalPathname((LogicalPathname)defaultedNewName)
     2116          .getFile();
     2117      } else {
     2118        destination = defaultedNewName.getFile();
     2119      }
     2120      if (Utilities.isPlatformWindows) {
     2121        if (destination.isFile()) {
     2122          //if (destination.isJar()) {
     2123            // By default, MSDOG doesn't allow one to remove files that are open, so we need to close
     2124            // any open jar references
     2125            // FIXME
     2126            //            ZipCache.remove(destination);
     2127          //          }
     2128          destination.delete();
     2129        }
     2130      }
     2131      if (source.renameTo(destination)) { // Success!
     2132        Pathname newTruename = (Pathname)truename(defaultedNewName, true);
     2133        return LispThread.currentThread().setValues(defaultedNewName,
     2134                                                    oldTruename,
     2135                                                    newTruename);
     2136      }
     2137      return error(new FileError("Unable to rename "
     2138                                 + oldTruename.princToString()
     2139                                 + " to " + newName.princToString()
     2140                                 + ".",
     2141                                 oldTruename));
     2142    }
     2143  }
    26762144   
    2677     // TODO clarify uri encoding cases in implementation and document
    2678     private static final Primitive FILE_NAMESTRING = new pf_file_namestring();
    2679     @DocString(name="file-namestring",
    2680                args="pathname",
    2681                returns="namestring",
    2682     doc="Returns just the name, type, and version components of PATHNAME.")
    2683     private static class pf_file_namestring extends Primitive {
    2684         pf_file_namestring() {
    2685             super("file-namestring", "pathname");
    2686         }
    2687         @Override
    2688         public LispObject execute(LispObject arg) {
    2689             Pathname p = coerceToPathname(arg);
    2690             StringBuilder sb = new StringBuilder();
    2691             if (p.getName() instanceof AbstractString) {
    2692                 sb.append(p.getName().getStringValue());
    2693             } else if (p.getName() == Keyword.WILD) {
    2694                 sb.append('*');
    2695             } else {
    2696                 return NIL;
    2697             }
    2698             if (p.getType() instanceof AbstractString) {
    2699                 sb.append('.');
    2700                 sb.append(p.getType().getStringValue());
    2701             } else if (p.getType() == Keyword.WILD) {
    2702                 sb.append(".*");
    2703             }
    2704             return new SimpleString(sb);
    2705         }
    2706     }
    2707 
    2708     private static final Primitive HOST_NAMESTRING = new pf_host_namestring();
    2709     @DocString(name="host-namestring",
    2710                args="pathname",
    2711                returns="namestring",
    2712     doc="Returns the host name of PATHNAME.")
    2713     private static class pf_host_namestring extends Primitive {
    2714         pf_host_namestring() {
    2715             super("host-namestring", "pathname");
    2716         }
    2717         @Override
    2718         public LispObject execute(LispObject arg) {
    2719             return coerceToPathname(arg).getHost(); // XXX URL-PATHNAME
    2720         }
    2721     }
     2145  // TODO clarify uri encoding cases in implementation and document
     2146  private static final Primitive FILE_NAMESTRING = new pf_file_namestring();
     2147  @DocString(name="file-namestring",
     2148             args="pathname",
     2149             returns="namestring",
     2150             doc="Returns just the name, type, and version components of PATHNAME.")
     2151  private static class pf_file_namestring extends Primitive {
     2152    pf_file_namestring() {
     2153      super(Symbol.FILE_NAMESTRING, "pathname");
     2154    }
     2155    @Override
     2156    public LispObject execute(LispObject arg) {
     2157      Pathname p = coerceToPathname(arg);
     2158      StringBuilder sb = new StringBuilder();
     2159      if (p.getName() instanceof AbstractString) {
     2160        sb.append(p.getName().getStringValue());
     2161      } else if (p.getName() == Keyword.WILD) {
     2162        sb.append('*');
     2163      } else {
     2164        return NIL;
     2165      }
     2166      if (p.getType() instanceof AbstractString) {
     2167        sb.append('.');
     2168        sb.append(p.getType().getStringValue());
     2169      } else if (p.getType() == Keyword.WILD) {
     2170        sb.append(".*");
     2171      }
     2172      return new SimpleString(sb);
     2173    }
     2174  }
     2175
     2176  private static final Primitive HOST_NAMESTRING = new pf_host_namestring();
     2177  @DocString(name="host-namestring",
     2178             args="pathname",
     2179             returns="namestring",
     2180             doc="Returns the host name of PATHNAME.")
     2181  private static class pf_host_namestring extends Primitive {
     2182    pf_host_namestring() {
     2183      super("host-namestring", "pathname");
     2184    }
     2185    @Override
     2186    public LispObject execute(LispObject arg) {
     2187      return coerceToPathname(arg).getHost(); // XXX URL-PATHNAME
     2188    }
     2189  }
    27222190   
    2723     public URL toURL() {
    2724         try {
    2725             if (isURL()) {
    2726                 return new URL(getNamestring());
    2727             } else {
    2728                 return toFile().toURI().toURL();
    2729             }
    2730         } catch (MalformedURLException e) {
    2731             error(new LispError(getNamestring() + " is not a valid URL"));
    2732             return null; // not reached
    2733         }
    2734     }
    2735 
    2736     public File toFile() {
    2737         if(!isURL()) {
    2738             return new File(getNamestring());
    2739         } else {
    2740             throw new RuntimeException(this + " does not represent a file");
    2741         }
    2742     }
    2743 
    2744     static {
    2745         LispObject obj = Symbol.DEFAULT_PATHNAME_DEFAULTS.getSymbolValue();
    2746         Symbol.DEFAULT_PATHNAME_DEFAULTS.setSymbolValue(coerceToPathname(obj));
    2747     }
     2191  public URL toURL() {
     2192    try {
     2193      if (isURL()) {
     2194        return new URL(getNamestring());
     2195      } else {
     2196        return toFile().toURI().toURL();
     2197      }
     2198    } catch (MalformedURLException e) {
     2199      error(new LispError(getNamestring() + " is not a valid URL"));
     2200      return null; // not reached
     2201    }
     2202  }
     2203
     2204  public File toFile() {
     2205    if(!isURL()) {
     2206      return new File(getNamestring());
     2207    } else {
     2208      throw new RuntimeException(this + " does not represent a file");
     2209    }
     2210  }
     2211
     2212  static {
     2213    LispObject obj = Symbol.DEFAULT_PATHNAME_DEFAULTS.getSymbolValue();
     2214    Symbol.DEFAULT_PATHNAME_DEFAULTS.setSymbolValue(coerceToPathname(obj));
     2215  }
    27482216
    27492217   
    2750     @DocString(name="uri-decode",
    2751                args="string",
    2752                returns="string",
    2753     doc="Decode STRING percent escape sequences in the manner of URI encodings.")
    2754     private static final Primitive URI_DECODE = new pf_uri_decode();
    2755     private static final class pf_uri_decode extends Primitive {
    2756         pf_uri_decode() {
    2757             super("uri-decode", PACKAGE_EXT, true);
    2758         }
    2759         @Override
    2760         public LispObject execute(LispObject arg) {
    2761             if (!(arg instanceof AbstractString)) {
    2762                 return type_error(arg, Symbol.STRING);
    2763             }
    2764             String result = uriDecode(((AbstractString)arg).toString());
    2765             return new SimpleString(result);
    2766         }
    2767     };
    2768 
    2769     static String uriDecode(String s) {
    2770         try {
    2771             URI uri = new URI("file://foo?" + s);
    2772             return uri.getQuery();
    2773         } catch (URISyntaxException e) {}
    2774         return null;  // Error
    2775     }
     2218  @DocString(name="uri-decode",
     2219             args="string",
     2220             returns="string",
     2221  doc="Decode STRING percent escape sequences in the manner of URI encodings.")
     2222  private static final Primitive URI_DECODE = new pf_uri_decode();
     2223  private static final class pf_uri_decode extends Primitive {
     2224    pf_uri_decode() {
     2225      super("uri-decode", PACKAGE_EXT, true);
     2226    }
     2227    @Override
     2228    public LispObject execute(LispObject arg) {
     2229      if (!(arg instanceof AbstractString)) {
     2230        return type_error(arg, Symbol.STRING);
     2231      }
     2232      String result = uriDecode(((AbstractString)arg).toString());
     2233      return new SimpleString(result);
     2234    }
     2235  };
     2236
     2237  static String uriDecode(String s) {
     2238    try {
     2239      URI uri = new URI("file://foo?" + s);
     2240      return uri.getQuery();
     2241    } catch (URISyntaxException e) {}
     2242    return null;  // Error
     2243  }
    27762244   
    2777     @DocString(name="uri-encode",
    2778                args="string",
    2779                returns="string",
    2780     doc="Encode percent escape sequences in the manner of URI encodings.")
    2781     private static final Primitive URI_ENCODE = new pf_uri_encode();
    2782     private static final class pf_uri_encode extends Primitive {
    2783         pf_uri_encode() {
    2784             super("uri-encode", PACKAGE_EXT, true);
    2785         }
    2786         @Override
    2787         public LispObject execute(LispObject arg) {
    2788             if (!(arg instanceof AbstractString)) {
    2789                 return type_error(arg, Symbol.STRING);
    2790             }
    2791             String result = uriEncode(((AbstractString)arg).toString());
    2792             return new SimpleString(result);
    2793         }
    2794     };
    2795 
    2796     static String uriEncode(String s) {
    2797         // The constructor we use here only allows absolute paths, so
    2798         // we manipulate the input and output correspondingly.
    2799         String u;
    2800         if (!s.startsWith("/")) {
    2801             u = "/" + s;
    2802         } else {
    2803             u = new String(s);
    2804         }
    2805         try {
    2806             URI uri = new URI("file", "", u, "");
    2807             String result = uri.getRawPath();
    2808             if (!s.startsWith("/")) {
    2809                 return result.substring(1);
    2810             }
    2811             return result;
    2812         } catch (URISyntaxException e) {
    2813             Debug.assertTrue(false);
    2814         }
    2815         return null; // Error
    2816     }
    2817 
    2818 
    2819     File getFile() {
    2820         String namestring = getNamestring(); // XXX UNC pathnames currently have no namestring
    2821         if (namestring != null) {
    2822             return new File(namestring);
    2823         }
    2824         error(new FileError("Pathname has no namestring: " + princToString(),
     2245  @DocString(name="uri-encode",
     2246             args="string",
     2247             returns="string",
     2248  doc="Encode percent escape sequences in the manner of URI encodings.")
     2249  private static final Primitive URI_ENCODE = new pf_uri_encode();
     2250  private static final class pf_uri_encode extends Primitive {
     2251    pf_uri_encode() {
     2252      super("uri-encode", PACKAGE_EXT, true);
     2253    }
     2254    @Override
     2255    public LispObject execute(LispObject arg) {
     2256      if (!(arg instanceof AbstractString)) {
     2257        return type_error(arg, Symbol.STRING);
     2258      }
     2259      String result = uriEncode(((AbstractString)arg).toString());
     2260      return new SimpleString(result);
     2261    }
     2262  };
     2263
     2264  static String uriEncode(String s) {
     2265    // The constructor we use here only allows absolute paths, so
     2266    // we manipulate the input and output correspondingly.
     2267    String u;
     2268    if (!s.startsWith("/")) {
     2269      u = "/" + s;
     2270    } else {
     2271      u = new String(s);
     2272    }
     2273    try {
     2274      URI uri = new URI("file", "", u, "");
     2275      String result = uri.getRawPath();
     2276      if (!s.startsWith("/")) {
     2277        return result.substring(1);
     2278      }
     2279      return result;
     2280    } catch (URISyntaxException e) {
     2281      Debug.assertTrue(false);
     2282    }
     2283    return null; // Error
     2284  }
     2285
     2286
     2287  File getFile() {
     2288    String namestring = getNamestring(); // XXX UNC pathnames currently have no namestring
     2289    if (namestring != null) {
     2290      return new File(namestring);
     2291    }
     2292    error(new FileError("Pathname has no namestring: " + princToString(),
    28252293                        this));
    2826         // Not reached.
    2827         return null;
    2828     }
    2829     public static Pathname getDirectoryPathname(File file) {
     2294    return (File)UNREACHED;
     2295  }
     2296
     2297  public static Pathname getDirectoryPathname(File file) {
    28302298        try {
    28312299            String namestring = file.getCanonicalPath();
     
    28422310        }
    28432311    }
     2312
     2313  // Whether this pathname represents a file on the filesystem, not
     2314  // addressed as a JAR-PATHNAME
     2315  public boolean isLocalFile() {
     2316    if (getHost().equals(NIL)
     2317        || Symbol.GETF.execute(getHost(), PathnameURL.SCHEME, NIL).equals("file")) {
     2318      return true;
     2319    }
     2320    return false;
     2321  }
     2322
     2323  /** @return The representation of this pathname suitable for
     2324   *  referencing an entry in a Zip/JAR file
     2325   */
     2326  protected String asEntryPath() {
     2327    Pathname p = Pathname.create();
     2328    p.setDirectory(getDirectory())
     2329      .setName(getName())
     2330      .setType(getType());
     2331    String path = p.getNamestring();
     2332   
     2333    StringBuilder result = new StringBuilder();
     2334    result.append(path);
     2335
     2336    // Entries in jar files are always relative, but Pathname
     2337    // directories are :ABSOLUTE.
     2338    if (result.length() > 1
     2339        && result.substring(0, 1).equals("/")) {
     2340      return result.substring(1);
     2341    }
     2342    return result.toString();
     2343  }
     2344
    28442345}
  • trunk/abcl/src/org/armedbear/lisp/PathnameJar.java

    r15393 r15395  
    3535import static org.armedbear.lisp.Lisp.*;
    3636
     37import java.io.InputStream;
    3738import java.io.IOException;
    3839import java.io.File;
     
    4243import java.net.URISyntaxException;
    4344import java.util.ArrayList;
     45import java.util.Iterator;
    4446import java.util.List;
     47import java.util.Map;
     48import java.util.Set;
     49import java.util.zip.ZipEntry;
    4550import java.util.zip.ZipFile;
    4651
     
    4853  extends PathnameURL
    4954{
     55  static final public String JAR_URI_SUFFIX = "!/";
     56  static final public String JAR_URI_PREFIX = "jar:";
     57
    5058  protected PathnameJar() {}
    5159
     
    5462  }
    5563
    56   static final public String JAR_URI_SUFFIX = "!/";
    57   static final public String JAR_URI_PREFIX = "jar:";
    58 
     64  public static LispObject createFromPathname(Pathname p) {
     65    if (p instanceof PathnameURL) {
     66      return PathnameJar.create(JAR_URI_PREFIX + ((PathnameURL)p).getNamestringAsURI() + JAR_URI_SUFFIX);
     67    } else if (p instanceof Pathname) {
     68      // FIXME: not going to work with namestrings with characters that need URI escaping
     69      return PathnameJar.create(JAR_URI_PREFIX + "file://" + p.getNamestring() + JAR_URI_SUFFIX);
     70    } else {
     71      return p;
     72    }
     73  }
     74
     75  static public LispObject createFromFile(String s) {
     76    return PathnameJar.create("jar:file:" + s + JAR_URI_SUFFIX);
     77  }
     78
     79  static public LispObject createEntryFromFile(String jar, String entry) {
     80    return PathnameJar.create("jar:file:" + jar + JAR_URI_SUFFIX + entry);
     81  }
     82
     83  static public LispObject createEntryFromJar(PathnameJar jar, String entry) {
     84    if (jar.isArchiveEntry()) {
     85      return simple_error("Failed to create the entry ~a in ~a", entry, jar);
     86    }
     87    return PathnameJar.create(jar.getNamestring() + entry);
     88  }
    5989  /**
    6090   *  Enumerate the components of a jar namestring
     
    101131    }
    102132
    103     // the first value is not a jar Pathname,
     133    // The root, non-PathnameJar location of this reference
     134    // For files, possibly either a relative or absolute directory
    104135    result.add(parts[0]);
    105136
    106     // the references to jars, all suffixed with JAR_URI_SUFFIX
     137    // The references to the pathnames of archives located within the
     138    // root jar.
     139    // These will be relative directory paths suffixed with JAR_URI_SUFFIX
    107140    for (int j = 1; j < prefixCount; j++) {
    108141      String ns = parts[j] + JAR_URI_SUFFIX;
     
    110143    }
    111144
    112     // possibly return the path inside the last jar
     145    // possibly return the path inside the last jar as an absolute path
    113146    if (parts.length == (prefixCount + 1)) {
    114147      result.add("/" + parts[parts.length - 1]);
     
    164197  }
    165198
     199  static public Pathname create() {
     200    return new PathnameJar();
     201  }
    166202  static public LispObject create(String s) {
    167203    if (!s.startsWith(JAR_URI_PREFIX)) {
     
    174210      return parse_error("Couldn't parse PATHNAME-JAR from namestring: " + s);
    175211    }
     212   
    176213    PathnameJar result = new PathnameJar();
    177214
     
    268305  }
    269306
     307  LispObject getRootJar() {
     308    return getDevice().car();
     309  }
     310
     311  LispObject getJars() {
     312    return getDevice();
     313  }
    270314
    271315  public static final LispObject truename(PathnameJar pathname,
    272316                                          boolean errorIfDoesNotExist) {
    273     // PathnameJar result
    274     //   = (PathnameJar) mergePathnames(pathname,
    275     //                                  coerceToPathname(Symbol.DEFAULT_PATHNAME_DEFAULTS.symbolValue()),
    276     //                                  NIL);
    277 
    278317    PathnameJar result = pathname;
    279    
    280     LispObject enclosingJars = NIL;
    281     LispObject current = pathname.getDevice();
    282 
    283     while (current instanceof PathnameJar){
    284       enclosingJars = enclosingJars.push(current);
    285       current = ((PathnameJar)current).getDevice();
    286     }
    287 
    288     LispObject outerJar = Pathname.truename(current, errorIfDoesNotExist);
    289 
    290     String outerJarNamestring = ((Pathname)outerJar).getNamestring();
    291     try {
    292       ZipFile jarFile = new ZipFile(outerJarNamestring);
    293       while (enclosingJars.car() != NIL) {
    294         LispObject jar = enclosingJars.car();
    295         String ns = ((Pathname)jar).getNamestring();
    296         ZipFile f = new ZipFile(ns);
    297         enclosingJars = enclosingJars.cdr();
    298       }
    299     } catch (IOException e) {
    300       return Pathname.doTruenameExit(result, errorIfDoesNotExist);
    301     }
    302 
    303     return result;
     318
     319    LispObject jars = pathname.getJars();
     320    Pathname rootJar = (Pathname) pathname.getRootJar();
     321    LispObject enclosingJars = jars.cdr();
     322
     323    if (!rootJar.isLocalFile()) {
     324      // FIXME implement me
     325      simple_error("Unimplemented TRUENAME for non-file root jar.");
     326    } else {
     327      rootJar = (Pathname)Symbol.MERGE_PATHNAMES.execute(rootJar);
     328    }
     329
     330    rootJar = (Pathname)Symbol.TRUENAME.execute(rootJar);
     331
     332    PathnameJar p = new PathnameJar();
     333    p.copyFrom(pathname);
     334    p.setDevice(new Cons(rootJar, enclosingJars));
     335
     336    if (!p.isArchiveEntry()) {
     337      ZipCache.Archive archive = ZipCache.getArchive(p);
     338      if (archive == null) {
     339        if (errorIfDoesNotExist) {
     340          return simple_error("Accessible TRUENAME can't be determined for: ~a", pathname); //time !?
     341        } else {
     342          return NIL;
     343        }
     344      }
     345      return p;
     346    }
     347
     348    ZipEntry entry = ZipCache.getZipEntry(p);
     349    if (entry == null) {
     350      if (errorIfDoesNotExist) {
     351        return simple_error("Accessible TRUENAME can't be determined for: ~a", pathname); //time !?
     352      } else {
     353        return NIL;
     354      }
     355    }
     356    return p;
     357  }
     358
     359  public boolean isLocalFile() {
     360    Pathname p = (Pathname) getRootJar();
     361    if (p.isLocalFile()) {
     362      return true;
     363    }
     364    return super.isLocalFile();
     365  }
     366
     367  public boolean isArchiveEntry() {
     368    return !(getDirectory().equals(NIL)
     369             && getName().equals(NIL)
     370             && getType().equals(NIL));
     371  }
     372
     373  public PathnameJar getArchive() {
     374    if (!isArchiveEntry()) {
     375      return (PathnameJar)simple_error("Pathname already represents an archive.");
     376    }
     377    PathnameJar archive = new PathnameJar();
     378    archive.copyFrom(this);
     379    archive
     380      .setDirectory(NIL)
     381      .setName(NIL)
     382      .setType(NIL);
     383    return archive;
    304384  }
    305385
     
    313393  }
    314394
    315         //   jarfile: {
     395  public InputStream getInputStream() {
     396    String entryPath = asEntryPath();
     397    // XXX We only return the bytes of an entry in a JAR
     398    if (!isArchiveEntry()) {
     399      simple_error("Can only get input stream for an entry in a JAR-PATHNAME.", this);
     400    }
     401    InputStream result = ZipCache.getEntryAsInputStream(this);
     402    if (result == null) {
     403      error(new FileError("Failed to get InputStream", this));
     404    }
     405    return result;
     406  }
     407
     408  /** List the contents of the directory */
     409  static public LispObject listDirectory(PathnameJar pathname) {
     410    String directory = pathname.asEntryPath();
     411    // We should only be listing directories
     412    if (pathname.getDirectory() == NIL) {
     413      return simple_error("Not a directory in a jar ~a", pathname);
     414    }
     415
     416    // if (pathname.getDevice().cdr() instanceof Cons) {
     417    //   return error(new FileError("Unimplemented directory listing of JAR within JAR.", pathname));
     418    // }
     419   
     420    if (directory.length() == 0) {
     421      directory = "/*";
     422    } else {
     423      if (directory.endsWith("/")) {
     424        directory = "/" + directory + "*";
     425      } else {
     426        directory = "/" + directory + "/*";
     427      }
     428    }
     429
     430    Pathname wildcard = (Pathname)Pathname.create(directory);
     431    //    String wildcard = new SimpleString(directory);
     432    // directories in a a jar aren't marked with a suffixed slash
     433    //    SimpleString wildcardDirectory = new SimpleString(directory + "/");
     434
     435    LispObject result = NIL;
     436    //LispObject matches;
     437   
     438    Iterator<Map.Entry<PathnameJar,ZipEntry>> iterator = ZipCache.getEntriesIterator(pathname);
     439    while (iterator.hasNext()) {
     440      Map.Entry<PathnameJar,ZipEntry> e = iterator.next();
     441      PathnameJar entry = e.getKey();
     442      // PathnameJar jarPath = (PathnameJar)PathnameJar.create(e.getKey());
     443      // ZipEntry entry = e.getValue();
     444     
     445      // if (entryName.endsWith("/")) {
     446      //   matches = Symbol.PATHNAME_MATCH_P
     447      //     .execute(new SimpleString(entryName), wildcardDirectory);
     448      // } else {
     449      //   matches = Symbol.PATHNAME_MATCH_P.
     450      //     execute(new SimpleString(entryName), wildcard);
     451      // }
     452      if (!Symbol.PATHNAME_MATCH_P.execute(entry, wildcard).equals(NIL)) {
     453        result = result.push(entry);
     454      }
     455    }
     456    return result.nreverse();
     457  }
     458
     459  @DocString(name="match-wild-jar-pathname",
     460             args="wild-jar-pathname",
     461             returns="pathnames",
     462  doc="Returns the pathnames matching WILD-JAR-PATHNAME which must be both wild and a JAR-PATHNAME")
     463  static final Primitive MATCH_WILD_JAR_PATHNAME = new pf_match_wild_jar_pathname();
     464
     465  private static class pf_match_wild_jar_pathname extends Primitive {
     466    pf_match_wild_jar_pathname() {
     467      super(Symbol.MATCH_WILD_JAR_PATHNAME, "wild-jar-pathname");
     468    }
     469    @Override
     470    public LispObject execute(LispObject arg) {
     471      Pathname pathname = coerceToPathname(arg);
     472      if (pathname instanceof LogicalPathname) {
     473        pathname = LogicalPathname.translateLogicalPathname((LogicalPathname) pathname);
     474      }
     475      if (!pathname.isJar()) {
     476        return new FileError("Not a jar pathname.", pathname);
     477      }
     478      if (!pathname.isWild()) {
     479        return new FileError("Not a wild pathname.", pathname);
     480      }
     481      if (!((PathnameJar)pathname).getJars().cdr().equals(NIL)) {
     482        return simple_error("Unimplemented match on contents of inner jars"); // FIXME
     483      }
     484
     485      PathnameJar jarPathname = new PathnameJar();
     486      jarPathname.copyFrom(pathname);
     487      jarPathname
     488        .setDirectory(NIL)
     489        .setName(NIL)
     490        .setType(NIL);
     491      PathnameJar wildcard = (PathnameJar)Symbol.TRUENAME.execute(jarPathname);
     492      Iterator<Map.Entry<PathnameJar,ZipEntry>> iterator
     493        = ZipCache.getEntriesIterator(wildcard);
     494      wildcard
     495        .setDirectory(pathname.getDirectory())
     496        .setName(pathname.getName())
     497        .setType(pathname.getType());
     498           
     499      LispObject result = NIL;
     500      while (iterator.hasNext()) {
     501        Map.Entry<PathnameJar,ZipEntry> e = iterator.next();
     502        PathnameJar entry = e.getKey();
     503        LispObject matches
     504          = Symbol.PATHNAME_MATCH_P.execute(entry, wildcard);
     505         
     506        if (!matches.equals(NIL)) {
     507          result = new Cons(entry, result);
     508        }
     509      }
     510
     511      // FIXME implement recursive jars
     512      // if (pathname.getDevice().cdr() instanceof Cons) {
     513      //   ZipFile outerJar = ZipCache.get((Pathname)pathname.getDevice().car());
     514      //   String entryPath = ((Pathname)pathname.getDevice().cdr().car()).getNamestring(); //???
     515      //   if (entryPath.startsWith("/")) {
     516      //     entryPath = entryPath.substring(1);
     517      //   }
     518      //   ZipEntry entry = outerJar.getEntry(entryPath);
     519      //   InputStream inputStream = null;
     520      //   try {
     521      //     inputStream = outerJar.getInputStream(entry);
     522      //   } catch (IOException e) {
     523      //     return new FileError("Failed to read zip input stream inside zip.",
     524      //                          pathname);
     525      //   }
     526      //   ZipInputStream zipInputStream
     527      //     = new ZipInputStream(inputStream);
     528
     529      //   try {
     530      //     while ((entry = zipInputStream.getNextEntry()) != null) {
     531      //       String entryName = "/" + entry.getName();
     532      //       LispObject matches = Symbol.PATHNAME_MATCH_P
     533      //         .execute(new SimpleString(entryName), wildcard);
     534           
     535      //       if (!matches.equals(NIL)) {
     536      //         String namestring = new String(pathname.getNamestring());
     537      //         namestring = namestring.substring(0, namestring.lastIndexOf("!/") + 2)
     538      //           + entry.getName();
     539      //         Pathname p = (Pathname)Pathname.create(namestring);
     540      //         result = new Cons(p, result);
     541      //       }
     542      //     }
     543      //   } catch (IOException e) {
     544      //     return new FileError("Failed to seek through zip inputstream inside zip.",
     545      //                          pathname);
     546      //   }
     547      // } else {
     548      // }
     549      return result;
     550    }
     551  }
     552
     553  public long getLastModified() {
     554    if (!isArchiveEntry()) {
     555      ZipCache.Archive archive = ZipCache.getArchive(this);
     556      if (archive != null) {
     557        return archive.lastModified;
     558      }
     559    } else {
     560      ZipEntry entry = ZipCache.getZipEntry(this);
     561      if (entry != null) {
     562        return entry.getTime();
     563      }
     564    }
     565    return -1; // shouldn't be reached
     566  }
     567}
     568     
     569
     570    // String rootJarNamestring = ((Pathname)rootJar).getNamestring();
     571    // try {
     572    //   ZipFile jarFile = new ZipFile(rootJarNamestring);
     573    //   ZipFileInputStream inputStream = null;
     574    //   while (enclosingJars.car() != NIL) {
     575    //     LispObject jar = enclosingJars.car();
     576    //     String ns = ((Pathname)jar).getNamestring();
     577    //     ZipEntry entry = currentZip.getEntry(ns);
     578    //     if (entry == null) {
     579    //       return simple_error("Failed to find entry ~a in ~a", ns, pathname);
     580    //     }
     581    //     InputStream i = entry.getInputStream();
     582    //     ZipInputStream zi = new ZipInputStream(i);
     583    //     enclosingJars = enclosingJars.cdr();
     584    //   }
     585    // } catch (IOException e) {
     586    //   return Pathname.doTruenameExit(result, errorIfDoesNotExist);
     587    // }
     588
     589
     590
     591
     592
     593  // truename
     594  //   jarfile: {
    316595        //     // Possibly canonicalize jar file directory
    317596        //     LispObject o = pathname.getDevice();
     
    396675        // }
    397676
    398 }
    399 
    400                                  
     677
     678
     679
     680// Pathname.init()
     681          // A JAR file
     682
     683
     684            // LispObject jars = NIL;
     685            // int i = s.lastIndexOf(jarSeparator, s.length() - jarSeparator.length() - 1);
     686            // String jar = null;
     687            // if (i == -1) {
     688            //     jar = s;
     689            // } else {
     690            //     // There can be no more than two jar references and the
     691            //     // inner one must be a file reference within the outer.
     692            //     jar = "jar:file:" + s.substring(i + jarSeparator.length());
     693            //     s = s.substring("jar:".length(), i + jarSeparator.length());
     694            //     Pathname p = (Pathname) Pathname.create(s);
     695            //     jars = jars.push(p.getDevice().car());
     696            // }
     697            // if (jar.startsWith("jar:file:")) { // PathnameJar should handle this part

     698            //     String file
     699            //         = jar.substring("jar:file:".length(),
     700            //                         jar.length() - jarSeparator.length());
     701            //     Pathname jarPathname;
     702            //     if (file.length() > 0) {
     703            //         URL url = null;
     704            //         URI uri = null;
     705            //         try {
     706            //             url = new URL("file:" + file);
     707            //             uri = url.toURI();
     708            //         } catch (MalformedURLException e1) {
     709            //             error(new SimpleError("Failed to create URI from "
     710            //                                 + "'" + file + "'"
     711            //                                 + ": " + e1.getMessage()));
     712            //         } catch (URISyntaxException e2) {
     713            //             error(new SimpleError("Failed to create URI from "
     714            //                                 + "'" + file + "'"
     715            //                                 + ": " + e2.getMessage()));
     716            //         }
     717            //         String path = uri.getPath();
     718            //         if (path == null) {
     719            //             // We allow "jar:file:baz.jar!/" to construct a relative
     720            //             // path for jar files, so MERGE-PATHNAMES means something.
     721            //           jarPathname = (Pathname)Pathname.create(uri.getSchemeSpecificPart());
     722            //         } else {
     723            //           jarPathname = (Pathname)Pathname.create((new File(path)).getPath());
     724            //         }
     725            //     } else {
     726            //       jarPathname = (Pathname)Pathname.create("");
     727            //     }
     728            //     jars = jars.push(jarPathname);
     729            // } else {
     730            //     URL url = null;
     731            //     try {
     732            //         url = new URL(jar.substring("jar:".length(), jar.length() - 2));
     733            //         Pathname p = (Pathname)Pathname.create(url);
     734            //         jars = jars.push(p);
     735            //     } catch (MalformedURLException e) {
     736            //         error(new LispError("Failed to parse URL "
     737            //                             + "'" + url + "'"
     738            //                             + e.getMessage()));
     739            //     }
     740            // }
     741            // jars = jars.nreverse();
     742            // setDevice(jars);
     743            // invalidateNamestring();
     744            // return;
     745
     746
     747        // // An entry in a JAR file
     748        // final int separatorIndex = s.lastIndexOf(jarSeparator);
     749        // if (separatorIndex > 0 && s.startsWith("jar:")) {
     750        //   return PathnameJar.create(s);
     751        // }
     752        //     final String jarURL = s.substring(0, separatorIndex + jarSeparator.length());
     753        //     URL url = null;
     754        //     try {
     755        //         url = new URL(jarURL);
     756        //     } catch (MalformedURLException ex) {
     757        //         error(new LispError("Failed to parse URL "
     758        //                             + "'" + jarURL + "'"
     759        //                             + ex.getMessage()));
     760        //     }
     761        //     Pathname d = (Pathname)Pathname.create(url);
     762        //     if (getDevice() instanceof Cons) {
     763        //         LispObject[] jars = d.copyToArray();
     764        //         //  XXX Is this ever reached?  If so, need to append lists
     765        //         Debug.assertTrue(false);
     766        //     } else {
     767        //         setDevice(d.getDevice());
     768        //     }
     769        //     s = "/" + s.substring(separatorIndex + jarSeparator.length());
     770        //     Pathname p = (Pathname)Pathname.create("file:" + s); // Use URI escaping rules
     771        //     setDirectory(p.getDirectory());
     772        //     setName(p.getName());
     773        //     setType(p.getType());
     774        //     setVersion(p.getVersion());
     775        //     return;
     776        // }
     777
     778
     779
     780
  • trunk/abcl/src/org/armedbear/lisp/PathnameURL.java

    r15393 r15395  
    11/*
    2  * PathnameJar.java
     2 * PathnameURL.java
    33 *
    44 * Copyright (C) 2020 @easye
     
    3636
    3737import java.io.File;
     38import java.io.InputStream;
     39import java.io.IOException;
    3840import java.net.URL;
    3941import java.net.URI;
     
    4244import java.text.MessageFormat;
    4345
    44 public class PathnameURL extends Pathname {
     46public class PathnameURL
     47  extends Pathname
     48{
    4549  static final Symbol SCHEME = internKeyword("SCHEME");
    4650  static final Symbol AUTHORITY = internKeyword("AUTHORITY");
     
    147151
    148152    Pathname p = (Pathname)Pathname.create(path != null ? path : "");
    149     result.setDirectory(p.getDirectory());
    150     result.setName(p.getName());
    151     result.setType(p.getType());
     153    result
     154      .setDirectory(p.getDirectory())
     155      .setName(p.getName())
     156      .setType(p.getType());
    152157
    153158    return result;
     
    203208
    204209  public String getNamestringAsURI() {
    205     URL url = makeURL(this);
    206     return url.toString();
     210    LispObject schemeProperty = Symbol.GETF.execute(getHost(), SCHEME, NIL);
     211    LispObject authorityProperty = Symbol.GETF.execute(getHost(), AUTHORITY, NIL);
     212    LispObject queryProperty = Symbol.GETF.execute(getHost(), QUERY, NIL);
     213    LispObject fragmentProperty = Symbol.GETF.execute(getHost(), FRAGMENT, NIL);
     214
     215    String scheme;
     216    String authority;
     217    if (!schemeProperty.equals(NIL)) {
     218      scheme = schemeProperty.getStringValue();
     219      authority =  authorityProperty.getStringValue();
     220    } else {
     221      scheme = "file";
     222      authority = "";
     223    }
     224
     225    String directory = getDirectoryNamestring();
     226    String file = Symbol.FILE_NAMESTRING.execute(this).getStringValue();
     227    String path = "";
     228
     229    if (!directory.equals("")) {
     230      path = directory + file;
     231    } else {
     232      path = file;
     233    }
     234
     235    String query = null;
     236    if (!queryProperty.equals(NIL)) {
     237      query = queryProperty.getStringValue();
     238    }
     239
     240    String fragment = null;
     241    if (!fragmentProperty.equals(NIL)) {
     242      fragment = fragmentProperty.getStringValue();
     243    }
     244
     245    try {
     246      URI uri = new URI(scheme, authority, path, query, fragment);
     247      return uri.toString();
     248    } catch (URISyntaxException e) {
     249      simple_error("Failed to construct a URI: ~a", this);
     250      return (String)UNREACHED;
     251    }
    207252  }
    208253
     
    217262
    218263  public static LispObject truename(PathnameURL p, boolean errorIfDoesNotExist) {
     264    if (p.getHost().equals(NIL)
     265        || Symbol.GETF.execute(p.getHost(), PathnameURL.SCHEME, NIL).equals("file")) {
     266      return Pathname.truename((Pathname)p, errorIfDoesNotExist);
     267    }
     268       
    219269    if (p.getInputStream() != null) {
    220270      // If there is no type, query or fragment, we check to
     
    232282    return Pathname.doTruenameExit(p, errorIfDoesNotExist);
    233283  }
     284
     285  public InputStream getInputStream() {
     286    InputStream result = null;
     287
     288    URL url = this.toURL();
     289    try {
     290      result = url.openStream();
     291    } catch (IOException e) {
     292      Debug.warn("Failed to get InputStream from "
     293                 + "'" + getNamestring() + "'"
     294                 + ": " + e);
     295    }
     296    return result;
     297  }
     298
     299  public long getLastModified() {
     300    return getURLConnection().getLastModified();
     301  }
    234302}
    235303                                 
     304
     305
     306// From Pathname.init()
     307        // A URL
     308        // if (isValidURL(s)) {
     309        //     URL url = null;
     310        //     try {
     311        //         url = new URL(s);
     312        //     } catch (MalformedURLException e) {
     313        //         Debug.assertTrue(false);
     314        //     }
     315        //     String scheme = url.getProtocol();
     316        //     if (scheme.equals("file")) {
     317        //         URI uri = null;
     318        //         try {
     319        //             uri = new URI(s);
     320        //         } catch (URISyntaxException ex) {
     321        //             error(new SimpleError("Improper URI syntax for "
     322        //                             + "'" + url.toString() + "'"
     323        //                             + ": " + ex.toString()));
     324        //         }
     325           
     326        //         String uriPath = uri.getPath();
     327        //         if (null == uriPath) {
     328        //                           // Under Windows, deal with pathnames containing
     329        //                           // devices expressed as "file:z:/foo/path"
     330        //                           uriPath = uri.getSchemeSpecificPart();
     331        //                           if (uriPath == null || uriPath.equals("")) {
     332        //             error(new LispError("The URI has no path: " + uri));
     333        //           }
     334        //         }
     335        //         final File file = new File(uriPath);
     336        //         String path = file.getPath();
     337        //         if (uri.toString().endsWith("/") && !path.endsWith("/")) {
     338        //           path += "/";
     339        //         }
     340        //         final Pathname p = (Pathname)Pathname.create(path);
     341        //         this.setHost(p.getHost());
     342        //         this.setDevice(p.getDevice());
     343        //         this.setDirectory(p.getDirectory());
     344        //         this.setName(p.getName());
     345        //         this.setType(p.getType());
     346        //         this.setVersion(p.getVersion());
     347        //         return;
     348        //     }
     349        //     Debug.assertTrue(scheme != null);
     350        //     URI uri = null;
     351        //     try {
     352        //         uri = url.toURI().normalize();
     353        //     } catch (URISyntaxException e) {
     354        //         error(new LispError("Couldn't form URI from "
     355        //                             + "'" + url + "'"
     356        //                             + " because: " + e));
     357        //     }
     358        //     String authority = uri.getAuthority();
     359        // if (authority == null) {
     360        //   authority = url.getAuthority();
     361        //   if (authority == null) {
     362        //     Debug.trace(MessageFormat.format("{0} has a null authority.", url));
     363        //   }
     364        // }
  • trunk/abcl/src/org/armedbear/lisp/Site.java

    r15393 r15395  
    5353            return;
    5454        }
    55         URL url = Lisp.class.getResource("boot.lisp");
     55        URL url = Lisp.class.getResource("boot.lisp"); // what if this was "__loader__._"?!!
    5656        if (url != null) {
    5757            if (!Pathname.isSupportedProtocol(url.getProtocol())) {
     
    5959            } else {
    6060              Pathname p = (Pathname)Pathname.create(url);
    61                 p.setName(NIL);
    62                 p.setType(NIL);
    63                 p.invalidateNamestring();
    64                 LISP_HOME = p;
     61              p.setName(NIL).setType(NIL);
     62              LISP_HOME = p;
    6563            }
    6664            return;
    6765        }
    68         Debug.trace("Unable to determine LISP_HOME.");
     66        simple_error("Unable to determine LISP_HOME.");
    6967    }
    7068
  • trunk/abcl/src/org/armedbear/lisp/Symbol.java

    r15099 r15395  
    31113111  public static final Symbol LAYOUT =
    31123112    PACKAGE_SYS.addExternalSymbol("LAYOUT");
     3113  public static final Symbol MATCH_WILD_JAR_PATHNAME =
     3114    PACKAGE_SYS.addExternalSymbol("MATCH-WILD-JAR-PATHNAME");
    31133115  public static final Symbol NAME =
    31143116    PACKAGE_SYS.addExternalSymbol("NAME");
  • trunk/abcl/src/org/armedbear/lisp/Utilities.java

    r14176 r15395  
    9191    }
    9292
    93     public static ZipInputStream getZipInputStream(ZipFile zipfile,
    94                                                    String entryName) {
    95         return Utilities.getZipInputStream(zipfile, entryName, false);
    96     }
    97 
    98   public static ZipInputStream getZipInputStream(ZipFile zipfile,
    99                                                  String entryName,
    100                                                  boolean errorOnFailure) {
    101     ZipEntry zipEntry = zipfile.getEntry(entryName);
    102     ZipInputStream stream = null;
    103     try {
    104       stream = new ZipInputStream(zipfile.getInputStream(zipEntry));
    105     } catch (IOException e) {
    106       if (errorOnFailure) {
    107           Lisp.error(new FileError("Failed to open '" + entryName + "' in zipfile '"
    108                                    + zipfile + "': " + e.getMessage()));
    109       }
    110       return null;
    111     }
    112     return stream;
    113   }
    114 
    115   public static InputStream getEntryAsInputStream(ZipInputStream zipInputStream,
    116                                                   String entryName)
    117     {
    118         ZipEntry entry = getEntry(zipInputStream, entryName);
    119         ByteArrayOutputStream bytes = readEntry(zipInputStream);
    120         return new ByteArrayInputStream(bytes.toByteArray());
    121 
    122     }
    123 
    124     public static ByteArrayOutputStream readEntry(ZipInputStream stream) {
    125         ByteArrayOutputStream result = new ByteArrayOutputStream();
    126         int count;
    127         byte buf[] = new byte[1024];
    128         try {
    129             while ((count = stream.read(buf, 0, buf.length)) != -1) {
    130                 result.write(buf, 0, count);
    131             }
    132         } catch (java.io.IOException e) {
    133             Debug.trace("Failed to read entry from "
    134                         + stream
    135                         + ": " + e);
    136             return null;
    137         }
    138         return result;
    139     }
    140 
    141     public static ZipEntry getEntry(ZipInputStream zipInputStream, String entryName) {
    142         return Utilities.getEntry(zipInputStream, entryName, false);
    143     }
    144 
    145   public static ZipEntry getEntry(ZipInputStream zipInputStream,
    146                                   String entryName,
    147                                   boolean errorOnFailure)
    148   {
    149     ZipEntry entry = null;
    150     do {
    151       try {
    152         entry = zipInputStream.getNextEntry();
    153       } catch (IOException e) {
    154         if (errorOnFailure) {
    155           Lisp.error(new FileError("Failed to seek for "
    156             + "'" + entryName + "'"
    157             + " in " + zipInputStream.toString()));
    158         }
    159         return null;
    160       }
    161     } while (entry != null && !entry.getName().equals(entryName));
    162     if (entry != null) {
    163       return entry;
    164     }
    165     if (errorOnFailure) {
    166       Lisp.error(new FileError("Failed to find "
    167         + "'" + entryName + "'"
    168         + " in " + zipInputStream.toString()));
    169     }
    170     return null;
    171 
    172   }
    173    
    174     public static final boolean checkZipFile(Pathname name) {
    175         InputStream input = name.getInputStream();
    176         try {
    177             byte[] bytes = new byte[4];
    178             int bytesRead = input.read(bytes);
    179             return (bytesRead == 4
    180                     && bytes[0] == 0x50
    181                     && bytes[1] == 0x4b
    182                     && bytes[2] == 0x03
    183                     && bytes[3] == 0x04);
    184         } catch (Throwable t) { // any error probably means 'no'
    185             return false;
    186         } finally {
    187             if (input != null) {
    188                 try {
    189                     input.close();
    190                 }
    191                 catch (IOException e) {} // ignore exceptions
    192             }
    193         }
    194     }
    195 
    196     static InputStream getInputStream(ZipFile jarFile, Pathname inner) {
    197         String entryPath = inner.asEntryPath();
    198         ZipEntry entry = jarFile.getEntry(entryPath);
    199         if (entry == null) {
    200             Debug.trace("Failed to find entry "
    201                     + "'" + entryPath + "'"
    202                     + " in "
    203                     + "'" + jarFile.getName() + "'");
    204             return null;
    205         }
    206         InputStream result = null;
    207         try {
    208             result = jarFile.getInputStream(entry);
    209         } catch (IOException e) {
    210             Debug.trace("Failed to open InputStream for "
    211               + "'" + entryPath + "'"
    212               + " in "
    213               + "'" + jarFile.getName() + "'");
    214             return null;
    215         }
    216         return result;
    217     }
    21893
    21994    static String escapeFormat(String s) {
  • trunk/abcl/src/org/armedbear/lisp/ZipCache.java

    r14627 r15395  
    3333package org.armedbear.lisp;
    3434
     35import java.io.ByteArrayInputStream;
     36import java.io.ByteArrayOutputStream;
    3537import org.armedbear.lisp.util.HttpHead;
    3638import static org.armedbear.lisp.Lisp.*;
     
    3840import java.io.File;
    3941import java.io.IOException;
     42import java.io.InputStream;
    4043import java.net.JarURLConnection;
    4144import java.net.MalformedURLException;
     
    4548import java.text.SimpleDateFormat;
    4649import java.util.Date;
     50import java.util.Enumeration;
     51import java.util.Iterator;
    4752import java.util.HashMap;
    4853import java.util.Locale;
     54import java.util.LinkedHashMap;
     55import java.util.Map;
     56import java.util.Set;
    4957import java.util.zip.ZipException;
    5058import java.util.zip.ZipFile;
     59import java.util.zip.ZipEntry;
     60import java.util.zip.ZipInputStream;
    5161
    5262/**
    53  * A cache for all zip/jar file accesses by URL that uses the last
     63 * A cache for all zip/jar file access by JarPathname that uses the last
    5464 * modified time of the cached resource.
    55  *
    56  * This implementation is synchronized on accesses via get().
    57  * Usage without multiple threads recompiling code that is then
    58  * re-loaded should be fine.
    5965 *
    6066 * If you run into problems with caching, use
     
    6470 */
    6571public class ZipCache {
    66 
    67     // To make this thread safe, we should return a proxy for ZipFile
    68     // that keeps track of the number of outstanding references handed
    69     // out, not allowing ZipFile.close() to succeed until that count
    70     // has been reduced to 1 or the finalizer is executing.
    71     // Unfortunately the relatively simple strategy of extending
    72     // ZipFile via a CachedZipFile does not work because there is not
    73     // a null arg constructor for ZipFile.
    74     static class Entry {
    75         long lastModified;
    76         ZipFile file;
    77     }
    78 
    79     static boolean cacheEnabled = true;
    80 
    81     private final static Primitive DISABLE_ZIP_CACHE = new disable_zip_cache();
    82     final static class disable_zip_cache extends Primitive {
    83         disable_zip_cache() {
    84             super("disable-zip-cache", PACKAGE_SYS, true, "",
    85                   "Disable all caching of ABCL FASLs and ZIPs.");
     72  public static final boolean checkZipFile(Pathname name) {
     73    InputStream input = name.getInputStream();
     74    try {
     75      byte[] bytes = new byte[4];
     76      int bytesRead = input.read(bytes);
     77      return bytesRead == 4 && bytes[0] == 80 && bytes[1] == 75 && bytes[2] == 3 && bytes[3] == 4;
     78    } catch (Throwable t) {
     79      // any error probably means 'no'
     80      return false;
     81    } finally {
     82      if (input != null) {
     83        try {
     84          input.close();
     85        } catch (IOException e) {
     86        } // ignore exceptions
     87      }
     88    }
     89  }
     90  static InputStream getInputStream(ZipFile jarFile, String entryPath) {
     91    ZipEntry entry = jarFile.getEntry(entryPath);
     92    if (entry == null) {
     93      Debug.trace("Failed to find entry " + "'" + entryPath + "'" + " in " + "'" + jarFile.getName() + "'");
     94      return null;
     95    }
     96    InputStream result = null;
     97    try {
     98      result = jarFile.getInputStream(entry);
     99    } catch (IOException e) {
     100      Debug.trace("Failed to open InputStream for " + "'" + entryPath + "'" + " in " + "'" + jarFile.getName() + "'");
     101      return null;
     102    }
     103    return result;
     104  }
     105  public static ZipInputStream getZipInputStream(ZipFile zipfile, String entryName) {
     106    return ZipCache.getZipInputStream(zipfile, entryName, false);
     107  }
     108  public static ZipInputStream getZipInputStream(ZipFile zipfile, String entryName, boolean errorOnFailure) {
     109    ZipEntry zipEntry = zipfile.getEntry(entryName);
     110    ZipInputStream stream = null;
     111    try {
     112      stream = new ZipInputStream(zipfile.getInputStream(zipEntry));
     113    } catch (IOException e) {
     114      if (errorOnFailure) {
     115        simple_error("Failed to open '" + entryName + "' in zipfile '" + zipfile + "': " + e.getMessage());
     116      }
     117      return null;
     118    }
     119    return stream;
     120  }
     121  public static ByteArrayOutputStream readEntry(ZipInputStream stream) {
     122    ByteArrayOutputStream result = new ByteArrayOutputStream();
     123    int count;
     124    byte[] buf = new byte[1024]; // What's a decent buffer size?
     125    try {
     126      while ((count = stream.read(buf, 0, buf.length)) != -1) {
     127        result.write(buf, 0, count);
     128      }
     129    } catch (IOException e) {
     130      Debug.trace("Failed to read entry from " + stream + ": " + e);
     131      return null;
     132    }
     133    return result;
     134  }
     135  public static ZipEntry getEntry(ZipInputStream zipInputStream, String entryName) {
     136    return ZipCache.getEntry(zipInputStream, entryName, false);
     137  }
     138  public static ZipEntry getEntry(ZipInputStream zipInputStream, String entryName, boolean errorOnFailure) {
     139    ZipEntry entry = null;
     140    do {
     141      try {
     142        entry = zipInputStream.getNextEntry();
     143      } catch (IOException e) {
     144        if (errorOnFailure) {
     145          Lisp.error(new FileError("Failed to seek for " + "'" + entryName + "'" + " in " + zipInputStream.toString()));
    86146        }
    87         @Override
    88         public LispObject execute() {
    89             ZipCache.disable();
    90             return T;
     147        return null;
     148      }
     149    } while (entry != null && !entry.getName().equals(entryName));
     150    if (entry != null) {
     151      return entry;
     152    }
     153    if (errorOnFailure) {
     154      Lisp.error(new FileError("Failed to find " + "'" + entryName + "'" + " in " + zipInputStream.toString()));
     155    }
     156    return null;
     157  }
     158
     159  public static InputStream getEntryAsInputStream(ZipInputStream zipInputStream, String entryName) {
     160    ZipEntry entry = getEntry(zipInputStream, entryName);
     161    ByteArrayOutputStream bytes = readEntry(zipInputStream);
     162    return new ByteArrayInputStream(bytes.toByteArray());
     163  }
     164
     165  public static InputStream getEntryAsInputStream(PathnameJar archiveEntry) {
     166    InputStream result = null;
     167    if (archiveEntry.getDevice().length() > 1) {
     168      simple_error("Unimplemented retrieval of InputStream from a nested jar reference");
     169      return (InputStream)UNREACHED;
     170      // Pathname inner = (Pathname) getDevice().cdr().car();
     171      // InputStream input = ZipCache.getInputStream(jarFile, inner);
     172      // ZipInputStream zipInputStream = new ZipInputStream(input);
     173      // result =  ZipCache.getEntryAsInputStream(zipInputStream, entryPath);
     174    } else {
     175      Archive archive = ZipCache.getArchive(archiveEntry);
     176      ZipFile zipFile = archive.file;
     177      ZipEntry entry = archive.getEntry(archiveEntry);
     178     
     179      try {
     180        result = zipFile.getInputStream(entry);
     181      } catch (IOException e) {
     182        simple_error("Failed to get InputStream for ~a", archiveEntry);
     183      }
     184    }
     185    return result;
     186  }
     187
     188  // To make this thread safe, we should return a proxy for ZipFile
     189  // that keeps track of the number of outstanding references handed
     190  // out, not allowing ZipFile.close() to succeed until that count
     191  // has been reduced to 1 or the finalizer is executing.
     192  // Unfortunately the relatively simple strategy of extending
     193  // ZipFile via a CachedZipFile does not work because there is not
     194  // a null arg constructor for ZipFile.
     195  static HashMap<PathnameJar, Archive> cache = new HashMap<PathnameJar, Archive>();
     196
     197  static public class Archive {
     198    ZipFile file;
     199    ZipInputStream inputStream;   // Unused, speculative
     200    LinkedHashMap<PathnameJar, ZipEntry> entries
     201      = new LinkedHashMap<PathnameJar, ZipEntry>();
     202    long lastModified;
     203
     204    public ZipEntry getEntry(PathnameJar entryPathname) {
     205      ZipEntry result = entries.get(entryPathname);
     206      if (result != null) {
     207        return result;
     208      }
     209      String entryPath = entryPathname.asEntryPath();
     210      result = file.getEntry(entryPath);
     211
     212      if (result == null) {
     213        return null;
     214      }
     215
     216      // ZipFile.getEntry() will return directories when asked for
     217      // files.
     218      if (result.isDirectory()
     219          && (!entryPathname.getName().equals(NIL)
     220              || !entryPathname.getType().equals(NIL))) {
     221        return null;
     222      }
     223     
     224      entries.put(entryPathname, result);
     225      return result;
     226    }
     227
     228    void populateAllEntries(PathnameJar jar) {
     229      ZipFile f = file;
     230      if (f.size() == entries.size()) {
     231        return;
     232      }
     233
     234      Enumeration<? extends ZipEntry> e = f.entries();
     235      while (e.hasMoreElements()) {
     236        ZipEntry entry = e.nextElement();
     237        String name = entry.getName();
     238        PathnameJar entryPathname
     239          = (PathnameJar)PathnameJar.createEntryFromJar(jar, name);
     240        entries.put(entryPathname, entry);
     241      }
     242    }
     243  }
     244
     245  static boolean cacheEnabled = true;
     246  private final static Primitive DISABLE_ZIP_CACHE = new disable_zip_cache();
     247  final static class disable_zip_cache extends Primitive {
     248    disable_zip_cache() {
     249      super("disable-zip-cache", PACKAGE_SYS, true, "",
     250            "Disable all caching of ABCL FASLs and ZIPs.");
     251    }
     252    @Override
     253    public LispObject execute() {
     254      ZipCache.disable();
     255      return T;
     256    }
     257  }
     258  static public synchronized void disable() {
     259    cacheEnabled = false;
     260    cache.clear(); 
     261  }
     262
     263  synchronized public static LinkedHashMap<PathnameJar,ZipEntry> getEntries(PathnameJar jar) {
     264    Archive archive = getArchive(jar);
     265    archive.populateAllEntries(jar); // Very expensive for jars with large number of entries
     266    return archive.entries;
     267  }
     268
     269  synchronized public static Iterator<Map.Entry<PathnameJar,ZipEntry>> getEntriesIterator(PathnameJar jar) {
     270    LinkedHashMap<PathnameJar,ZipEntry> entries = getEntries(jar);
     271    Set<Map.Entry<PathnameJar,ZipEntry>> set = entries.entrySet();
     272    return set.iterator();
     273  }
     274
     275
     276  static ZipEntry getZipEntry(PathnameJar archiveEntry) {
     277    PathnameJar archiveJar = archiveEntry.getArchive();
     278    Archive zip = getArchive(archiveJar);
     279    ZipEntry entry = zip.getEntry(archiveEntry);
     280    return entry;
     281  }
     282
     283  synchronized public static Archive getArchive(PathnameJar jar) {
     284    Pathname rootJar = (Pathname) jar.getRootJar();
     285    LispObject innerJars = jar.getJars().cdr();
     286    if (innerJars != NIL) {
     287      // FIXME
     288      simple_error("Currently unimplemented recursive archive: ~a" , jar);
     289      return (Archive)UNREACHED;
     290    }
     291
     292    if (jar.isLocalFile()) {
     293      Archive cached = cache.get(jar);
     294      if (cached != null) {
     295        return cached;
     296      }
     297
     298      File f = rootJar.getFile();
     299     
     300      try {
     301        Archive result = new Archive();
     302        result.file = new ZipFile(f);
     303        result.lastModified = f.lastModified();
     304        cache.put(jar, result);
     305
     306        return result;
     307      } catch (ZipException e) {
     308        error(new FileError("Failed to construct ZipFile"
     309                            + " because " + e,
     310                            Pathname.makePathname(f)));
     311        return null;
     312      } catch (IOException e) {
     313        error(new FileError("Failed to contruct ZipFile"
     314                            + " because " + e,
     315                            Pathname.makePathname(f)));
     316        return (Archive)UNREACHED;
     317      }
     318    } else {
     319      simple_error("Unimplemented fetch of remote resource.");
     320      return (Archive)UNREACHED;
     321      // CachedItem e = fetchURL(jarOrEntry, false);
     322      // return e;
     323    }
     324  }
     325
     326  // Archive archive  = cache.getArchive(jar);
     327
     328  //   // Check that the cache entry still accesses a valid ZipFile
     329  //   if (entry != null) {
     330  //     // Simplest way to call private ZipFile.ensureOpen()
     331  //     try {
     332  //       int size = archive.file.size();
     333  //     } catch (IllegalStateException e) {
     334  //       cache.remove(jar);
     335  //       entry = null;
     336  //     }
     337  //   }
     338
     339  //   if (entry != null) {
     340  //     if (url.getProtocol().equals("file")) {
     341  //       File f = new File(url.getPath());
     342  //       long current = f.lastModified();
     343  //       if (current > entry.lastModified) {
     344  //         try {
     345  //           entry.file = new ZipFile(f);
     346  //           entry.lastModified = current;
     347  //         } catch (IOException e) {
     348  //           Debug.trace(e.toString()); // XXX
     349  //         }
     350  //       }
     351  //     } else if (url.getProtocol().equals("http")) {
     352  //       checkRemoteLastModified();
     353  //     } else {
     354  //       entry = fetchURL(url, false);
     355  //       zipCache.put(url, entry);
     356  //     }
     357  //   } else {
     358  //     if (url.getProtocol().equals("file")) {
     359  //       entry = new Entry();
     360  //       String path = url.getPath();
     361       
     362  //       if (Utilities.isPlatformWindows) {
     363  //         String authority = url.getAuthority();
     364  //         if (authority != null) {
     365  //           path = authority + path;
     366  //         }
     367  //       }
     368  //       File f = new File(path);
     369  //       entry.lastModified = f.lastModified();
     370  //       try {
     371  //         entry.file = new ZipFile(f);
     372  //       } catch (ZipException e) {
     373  //         error(new FileError("Failed to get cached ZipFile"
     374  //                             + " because " + e,
     375  //                             Pathname.makePathname(f)));
     376  //       } catch (IOException e) {
     377  //         error(new FileError("Failed to get cached ZipFile"
     378  //                             + " because " + e,
     379  //                             Pathname.makePathname(f)));
     380  //       }
     381  //     } else {
     382  //       entry = fetchURL(url, true);
     383  //     }
     384  //     zipCache.put(url, entry);
     385  //   }
     386  //   return entry.file;
     387  // }
     388
     389  static final SimpleDateFormat ASCTIME
     390    = new SimpleDateFormat("EEE MMM d HH:mm:ss yyyy", Locale.US);
     391  static final SimpleDateFormat RFC_1036
     392    = new SimpleDateFormat("EEEE, dd-MMM-yy HH:mm:ss zzz", Locale.US);
     393  static final SimpleDateFormat RFC_1123
     394    = new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss zzz", Locale.US);
     395
     396  static void checkRemoteLastModified (PathnameJar jarEntry) {
     397    // Unfortunately, the Apple JDK under OS X doesn't do
     398    // HTTP HEAD requests, instead refetching the entire
     399    // resource, and I assume this is the case in all
     400    // Sun-derived JVMs.  So, we use a custom HEAD
     401    // implementation only looking for Last-Modified
     402    // headers, which if we don't find, we give up and
     403    // refetch the resource.
     404
     405    String dateString = null;
     406    // try {
     407    //   dateString = HttpHead.get(url, "Last-Modified");
     408    // } catch (IOException ex) {
     409    //   Debug.trace(ex);
     410    // }
     411    // Date date = null;
     412    // ParsePosition pos = new ParsePosition(0);
     413   
     414    // if (dateString != null) {
     415    //   date = RFC_1123.parse(dateString, pos);
     416    //   if (date == null) {
     417    //     date = RFC_1036.parse(dateString, pos);
     418    //     if (date == null)
     419    //       date = ASCTIME.parse(dateString, pos);
     420    //   }
     421    // }
     422     
     423    // if (date == null || date.getTime() > entry.lastModified) {
     424    //   entry = fetchURL(url, false);
     425    //   cache.put(url, entry);
     426    //   }
     427    //   if (date == null) {
     428    //     if (dateString == null) {
     429    //       Debug.trace("Failed to retrieve request header: "
     430    //                   + url.toString());
     431    //     } else {
     432    //       Debug.trace("Failed to parse Last-Modified date: " +
     433    //                   dateString);
     434    //     }
     435    //   }
     436  }
     437
     438  // FIXME recode once local fileaccesses are working
     439  static private ZipFile fetchURL(PathnameJar url, boolean useCaches) {
     440    ZipFile result = null;
     441    URL jarURL = null;
     442    try {
     443      jarURL = new URL("jar:" + url.getRootJar() + "!/");
     444    } catch (MalformedURLException e) {
     445      error(new LispError("Failed to form a jar: URL from "
     446                          + "'" + url + "'"
     447                          + " because " + e));
     448    }
     449    URLConnection connection = null;
     450    try {
     451      connection = jarURL.openConnection();
     452    } catch (IOException e) {
     453      error(new LispError("Failed to open "
     454                          + "'" + jarURL + "'"
     455                          + " with exception "
     456                          + e));
     457    }
     458    if (!(connection instanceof JarURLConnection)) {
     459      error(new LispError("Could not get a URLConnection from "
     460                          + "'" + jarURL + "'"));
     461    }
     462    JarURLConnection jarURLConnection = (JarURLConnection) connection;
     463    jarURLConnection.setUseCaches(useCaches);
     464    try {
     465      result = jarURLConnection.getJarFile();
     466    } catch (IOException e) {
     467      error(new LispError("Failed to fetch URL "
     468                          + "'" + jarURLConnection + "'"
     469                          + " because " + e));
     470    }
     471    if (cacheEnabled) {
     472      Archive archive = new Archive();
     473      archive.file = result;
     474      archive.lastModified = jarURLConnection.getLastModified();
     475      cache.put(url, archive);
     476    }
     477    return result;
     478  }
     479
     480  // ## remove-zip-cache-entry pathname => boolean
     481  private static final Primitive REMOVE_ZIP_CACHE_ENTRY = new remove_zip_cache_entry();
     482  private static class remove_zip_cache_entry extends Primitive {
     483    remove_zip_cache_entry() {
     484      super("remove-zip-cache-entry", PACKAGE_SYS, true, "pathname");
     485    }
     486    @Override
     487    public LispObject execute(LispObject arg) {
     488      Pathname p = coerceToPathname(arg);
     489      if (!(p instanceof PathnameJar)) {
     490        type_error(arg, Symbol.JAR_PATHNAME);
     491      }
     492      boolean result = ZipCache.remove((PathnameJar)p);
     493      return result ? T : NIL;
     494    }
     495  }
     496     
     497  synchronized public static boolean remove(PathnameJar p) {
     498    Archive archive = cache.get(p.getNamestring());
     499    if (archive != null) {
     500      try {
     501        if (archive.file != null) {
     502          archive.file.close();
    91503        }
    92     }
    93 
    94     static public synchronized void disable() {
    95         cacheEnabled = false;
    96         zipCache.clear(); 
    97     }
    98 
    99     static HashMap<URL, Entry> zipCache = new HashMap<URL, Entry>();
    100 
    101     synchronized public static ZipFile get(Pathname p) {
    102         return get(Pathname.makeURL(p));
    103     }
    104 
    105     static final SimpleDateFormat ASCTIME
    106         = new SimpleDateFormat("EEE MMM d HH:mm:ss yyyy", Locale.US);
    107     static final SimpleDateFormat RFC_1036
    108         = new SimpleDateFormat("EEEE, dd-MMM-yy HH:mm:ss zzz", Locale.US);
    109     static final SimpleDateFormat RFC_1123
    110         = new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss zzz", Locale.US);
    111 
    112 
    113     synchronized public static ZipFile get(final URL url) {
    114         if (!cacheEnabled) {
    115             if (url.getProtocol().equals("file")) {
    116                 File f = new File(url.getPath());
    117                 try {
    118                     return new ZipFile(f);
    119                 } catch (ZipException e) {
    120                     error(new FileError("Failed to construct ZipFile"
    121                                         + " because " + e,
    122                                         Pathname.makePathname(f)));
    123                 } catch (IOException e) {
    124                     error(new FileError("Failed to contruct ZipFile"
    125                                         + " because " + e,
    126                                         Pathname.makePathname(f)));
    127                 }
    128             } else {
    129                 Entry e = fetchURL(url, false);
    130                 return e.file;
    131             }
    132         }               
    133 
    134         Entry entry = zipCache.get(url);
    135 
    136         // Check that the cache entry still accesses a valid ZipFile
    137         if (entry != null) {
    138             // Simplest way to call private ZipFile.ensureOpen()
    139             try {
    140                 int size = entry.file.size();
    141             } catch (IllegalStateException e) {
    142                 zipCache.remove(url);
    143                 entry = null;
    144             }
     504        if (archive.inputStream != null) {
     505          archive.inputStream.close();
    145506        }
    146 
    147         if (entry != null) {
    148             if (url.getProtocol().equals("file")) {
    149                 File f = new File(url.getPath());
    150                 long current = f.lastModified();
    151                 if (current > entry.lastModified) {
    152                     try {
    153                         entry.file = new ZipFile(f);
    154                         entry.lastModified = current;
    155                     } catch (IOException e) {
    156                         Debug.trace(e.toString()); // XXX
    157                     }
    158                 }
    159             } else if (url.getProtocol().equals("http")) {
    160                 // Unfortunately, the Apple JDK under OS X doesn't do
    161                 // HTTP HEAD requests, instead refetching the entire
    162                 // resource, and I assume this is the case in all
    163                 // Sun-derived JVMs.  So, we use a custom HEAD
    164                 // implementation only looking for Last-Modified
    165                 // headers, which if we don't find, we give up and
    166                 // refetch the resource.
    167                 String dateString = null;
    168                 try {
    169                   dateString = HttpHead.get(url, "Last-Modified");
    170                 } catch (IOException ex) {
    171                   Debug.trace(ex);
    172                 }
    173                 Date date = null;
    174                 ParsePosition pos = new ParsePosition(0);
    175 
    176                 if (dateString != null) {
    177                     date = RFC_1123.parse(dateString, pos);
    178                     if (date == null) {
    179                         date = RFC_1036.parse(dateString, pos);
    180                         if (date == null)
    181                             date = ASCTIME.parse(dateString, pos);
    182                     }
    183                 }
    184 
    185                 if (date == null || date.getTime() > entry.lastModified) {
    186                     entry = fetchURL(url, false);
    187                     zipCache.put(url, entry);
    188                 }
    189                 if (date == null) {
    190                     if (dateString == null)
    191                         Debug.trace("Failed to retrieve request header: "
    192                                     + url.toString());
    193                     else
    194                         Debug.trace("Failed to parse Last-Modified date: " +
    195                                     dateString);
    196                 }
    197 
    198            } else {
    199                 entry = fetchURL(url, false);
    200                 zipCache.put(url, entry);
    201             }
    202         } else {
    203             if (url.getProtocol().equals("file")) {
    204                 entry = new Entry();
    205                 String path = url.getPath();
    206 
    207                 if (Utilities.isPlatformWindows) {
    208                     String authority = url.getAuthority();
    209                     if (authority != null) {
    210                         path = authority + path;
    211                     }
    212                 }
    213                 File f = new File(path);
    214                 entry.lastModified = f.lastModified();
    215                 try {
    216                     entry.file = new ZipFile(f);
    217                 } catch (ZipException e) {
    218                     error(new FileError("Failed to get cached ZipFile"
    219                                         + " because " + e,
    220                                         Pathname.makePathname(f)));
    221                 } catch (IOException e) {
    222                     error(new FileError("Failed to get cached ZipFile"
    223                                         + " because " + e,
    224                                         Pathname.makePathname(f)));
    225                 }
    226             } else {
    227                 entry = fetchURL(url, true);
    228             }
    229             zipCache.put(url, entry);
    230         }
    231         return entry.file;
    232     }
    233      
    234     static private Entry fetchURL(URL url, boolean cached) {
    235         Entry result = new Entry();
    236         URL jarURL = null;
    237         try {
    238             jarURL = new URL("jar:" + url + "!/");
    239         } catch (MalformedURLException e) {
    240             error(new LispError("Failed to form a jar: URL from "
    241                                 + "'" + url + "'"
    242                                 + " because " + e));
    243         }
    244         URLConnection connection = null;
    245         try {
    246             connection = jarURL.openConnection();
    247         } catch (IOException e) {
    248             error(new LispError("Failed to open "
    249                                 + "'" + jarURL + "'"
    250                                 + " with exception "
    251                                 + e));
    252         }
    253         if (!(connection instanceof JarURLConnection)) {
    254             error(new LispError("Could not get a URLConnection from "
    255                                 + "'" + jarURL + "'"));
    256         }
    257         JarURLConnection jarURLConnection = (JarURLConnection) connection;
    258         jarURLConnection.setUseCaches(cached);
    259         try {
    260             result.file = jarURLConnection.getJarFile();
    261         } catch (IOException e) {
    262             error(new LispError("Failed to fetch URL "
    263                                  + "'" + jarURLConnection + "'"
    264                                 + " because " + e));
    265         }
    266         result.lastModified = jarURLConnection.getLastModified();
    267         return result;
    268     }
    269 
    270     // ## remove-zip-cache-entry pathname => boolean
    271     private static final Primitive REMOVE_ZIP_CACHE_ENTRY = new remove_zip_cache_entry();
    272     private static class remove_zip_cache_entry extends Primitive {
    273         remove_zip_cache_entry() {
    274             super("remove-zip-cache-entry", PACKAGE_SYS, true, "pathname");
    275         }
    276         @Override
    277         public LispObject execute(LispObject arg) {
    278             Pathname p = coerceToPathname(arg);
    279             boolean result = ZipCache.remove(p);
    280             return result ? T : NIL;
    281         }
    282     }
    283      
    284     synchronized public static boolean remove(URL url) {
    285         Entry entry = zipCache.get(url);
    286         if (entry != null) {
    287             try {
    288                 entry.file.close();
    289             } catch (IOException e) {}
    290             zipCache.remove(entry);
    291             return true;
    292         }
    293         return false;
    294     }
    295 
    296     synchronized public static boolean remove(Pathname p) {
    297         URL url = Pathname.makeURL(p);
    298         if (url == null) {
    299             return false;
    300         }
    301         return ZipCache.remove(url);
    302     }
    303 
    304     synchronized public static boolean remove(File f) {
    305         Pathname p = Pathname.makePathname(f);
    306         return ZipCache.remove(p);
    307     }
     507        cache.remove(p);
     508       
     509        return true;
     510      } catch (IOException e) {
     511        simple_error("failed to close zip cache references", e);
     512      }
     513    }
     514    return false;
     515  }
     516
     517  static public long getLastModified(PathnameJar p) {
     518    return 0; // FIXME
     519    // JAR cases
     520    // 0.  JAR from URL
     521    // 1.  JAR
     522    // 2.  JAR in JAR
     523    // 3.  Entry in JAR
     524    // 4.  Entry in JAR in JAR
     525    // String entryPath = p.asEntryPath();
     526
     527    // LispObject jars = p.getDevice();
     528    // if (jars.cdr().equals(NIL)) {
     529    //   if (entryPath.length() == 0) {
     530    //     LispObject o = jars.car();
     531    //     // 0. JAR from URL
     532    //     // 1. JAR
     533    //     return ((Pathname)o).getLastModified();
     534    //   } else {
     535    //     // 3. Entry in JAR
     536    //     final ZipEntry entry
     537    //       = ZipCache.get((Pathname)getDevice().car()).getEntry(entryPath);
     538    //     if (entry == null) {
     539    //       return 0;
     540    //     }
     541    //     final long time = entry.getTime();
     542    //     if (time == -1) {
     543    //       return 0;
     544    //     }
     545    //     return time;
     546    //   }
     547    // } else {
     548    //   ZipFile outerJar = ZipCache.get((Pathname)d.car());
     549    //   if (entryPath.length() == 0) {
     550    //     // 4.  JAR in JAR
     551    //     String jarPath = ((Pathname)d.cdr()).asEntryPath();
     552    //     final ZipEntry entry = outerJar.getEntry(jarPath);
     553    //     final long time = entry.getTime();
     554    //     if (time == -1) {
     555    //       return 0;
     556    //     }
     557    //     return time;
     558    //   } else {
     559    //     // 5. Entry in JAR in JAR
     560    //     String innerJarPath = ((Pathname)d.cdr()).asEntryPath();
     561    //     ZipEntry entry = outerJar.getEntry(entryPath);
     562    //     ZipInputStream innerJarInputStream
     563    //       = ZipCache.getZipInputStream(outerJar, innerJarPath);
     564    //     ZipEntry innerEntry = ZipCache.getEntry(innerJarInputStream,
     565    //                                             entryPath);
     566    //     long time = innerEntry.getTime();
     567    //     if (time == -1) {
     568    //       return 0;
     569    //     }
     570    //     return time;
     571    //   }
     572    // }
     573  }
     574
    308575}
  • trunk/abcl/src/org/armedbear/lisp/delete_file.java

    r15393 r15395  
    5050    public LispObject execute(LispObject arg)
    5151    {
    52         // Don't follow symlinks! We want to delete the symlink itself, not
    53         // the linked-to file.
    54         Pathname pathname = coerceToPathname(arg);
    55         if (arg instanceof Stream)
    56             ((Stream)arg)._close();
    57         if (pathname instanceof LogicalPathname)
    58             pathname = LogicalPathname.translateLogicalPathname((LogicalPathname)pathname);
    59         if (pathname.isWild())
    60             return error(new FileError("Bad place for a wild pathname.",
    61                                         pathname));
    62         final Pathname defaultedPathname =
    63             Pathname.mergePathnames(pathname,
    64                                     coerceToPathname(Symbol.DEFAULT_PATHNAME_DEFAULTS.symbolValue()),
    65                                     NIL);
    66         final String namestring = defaultedPathname.getNamestring();
    67         if (namestring == null)
    68             return error(new FileError("Pathname has no namestring: " + defaultedPathname.princToString(),
    69                                         defaultedPathname));
    70         final File file = new File(namestring);
    71   ZipCache.remove(file);
    72         if (file.exists()) {
    73             // File exists.
    74             for (int i = 0; i < 5; i++) {
    75                 if (file.delete())
    76                     return T;
    77                 System.gc();
    78                 Thread.yield();
    79             }
    80             Pathname truename = (Pathname)Pathname.create(file.getAbsolutePath());
    81             StringBuilder sb = new StringBuilder("Unable to delete ");
    82             sb.append(file.isDirectory() ? "directory " : "file ");
    83             sb.append(truename.princToString());
    84             sb.append('.');
    85             return error(new FileError(sb.toString(), truename));
    86         } else {
    87             // File does not exist.
     52      // Don't follow symlinks! We want to delete the symlink itself, not
     53      // the linked-to file.
     54      Pathname pathname = coerceToPathname(arg);
     55      if (arg instanceof Stream)
     56        ((Stream)arg)._close();
     57      if (pathname instanceof LogicalPathname)
     58        pathname = LogicalPathname.translateLogicalPathname((LogicalPathname)pathname);
     59      if (pathname.isWild())
     60        return error(new FileError("Bad place for a wild pathname.",
     61                                   pathname));
     62      final Pathname defaultedPathname
     63              = (Pathname)Pathname.mergePathnames(pathname,
     64                                coerceToPathname(Symbol.DEFAULT_PATHNAME_DEFAULTS.symbolValue()),
     65                                NIL);
     66      final String namestring = defaultedPathname.getNamestring();
     67      if (namestring == null)
     68        return error(new FileError("Pathname has no namestring: " + defaultedPathname.princToString(),
     69                                   defaultedPathname));
     70      final File file = new File(namestring);
     71
     72      if (file.exists()) {
     73        // File exists.
     74        for (int i = 0; i < 5; i++) {
     75          if (file.delete())
    8876            return T;
     77          System.gc();
     78          Thread.yield();
    8979        }
     80        Pathname truename = (Pathname)Pathname.create(file.getAbsolutePath());
     81        StringBuilder sb = new StringBuilder("Unable to delete ");
     82        sb.append(file.isDirectory() ? "directory " : "file ");
     83        sb.append(truename.princToString());
     84        sb.append('.');
     85        return error(new FileError(sb.toString(), truename));
     86      } else {
     87        // File does not exist.
     88        return T;
     89      }
    9090    }
    9191
  • trunk/abcl/src/org/armedbear/lisp/probe_file.java

    r14921 r15395  
    4747    private static final class pf_probe_file extends Primitive {
    4848        pf_probe_file() {
    49             super("probe-file", "pathspec");
     49            super(Symbol.PROBE_FILE, "pathspec");
    5050        }
    5151        @Override
    5252        public LispObject execute(LispObject arg)
    5353        {
    54             return Pathname.truename(arg, false);
     54          Pathname p = coerceToPathname(arg);
     55          // TODO: refactor Pathname{,Jar,URL}.truename() to be non-static?
     56          if (p instanceof PathnameJar) {
     57            return PathnameJar.truename((PathnameJar)p, false);
     58          } else if (p instanceof PathnameURL) {
     59            return PathnameURL.truename((PathnameURL)p, false);
     60          } else {
     61            return Pathname.truename(p, false);
     62          }
    5563        }
    5664    };
     
    6371    private static class pf_truename extends Primitive {
    6472        pf_truename() {
    65             super("truename", "filespec");
     73            super(Symbol.TRUENAME, "filespec");
    6674        }
    6775        @Override
    6876        public LispObject execute(LispObject arg)
    6977        {
    70             return Pathname.truename(arg, true);
     78          Pathname p = coerceToPathname(arg);
     79          // TODO: refactor Pathname{,Jar,URL}.truename() to be non-static?
     80          if (p instanceof PathnameJar) {
     81            return PathnameJar.truename((PathnameJar)p, true);
     82          } else if (p instanceof PathnameURL) {
     83            return PathnameURL.truename((PathnameURL)p, true);
     84          } else {
     85            return Pathname.truename(p, true);
     86          }
    7187        }
    7288    };
     
    85101        {
    86102            Pathname pathname = coerceToPathname(arg);
    87             if (pathname.isWild())
    88                 error(new FileError("Bad place for a wild pathname.", pathname));
     103            if (pathname.isWild()) {
     104                error(new FileError("Cannot probe a wild pathname as a directory.", pathname));
     105            }
    89106            Pathname defaultedPathname = (Pathname)Pathname.MERGE_PATHNAMES.execute(pathname);
    90107            File file = defaultedPathname.getFile();
     
    113130            Pathname pathname = coerceToPathname(arg);
    114131            if (pathname.isWild()) {
    115                 error(new FileError("Bad place for a wild pathname.", pathname));
     132                error(new FileError("Fundamentally unable to determine whether a wild pathname is a directory.",
     133                                    pathname));
    116134            }
    117135            return isDirectory(pathname);
     
    127145            if (wildErrorP != NIL) {
    128146                if (pathname.isWild()) {
    129                     error(new FileError("Bad place for a wild pathname.", pathname));
     147                    error(new FileError("Fundamentally to determine whether a wild pathname is a directory.",
     148                                        pathname));
    130149                }
    131150            }
  • trunk/abcl/src/org/armedbear/lisp/unzip.java

    r15391 r15395  
    7373    private LispObject unzipToDirectory(Pathname zipPath, Pathname dirPath) {
    7474        if (!zipPath.isAbsolute()) {
    75             zipPath = Pathname.mergePathnames(zipPath,
     75            zipPath = (Pathname)Pathname.mergePathnames(zipPath,
    7676                                              coerceToPathname(Symbol.DEFAULT_PATHNAME_DEFAULTS.symbolValue()));
    7777        }
    78         LispObject o = Pathname.truename(zipPath, false);
     78        LispObject o = Symbol.PROBE_FILE.execute(zipPath);
    7979        if (!(o instanceof Pathname)) {
    8080            return error(new FileError("No file found: " + zipPath, zipPath));
  • trunk/abcl/src/org/armedbear/lisp/zip.java

    r14176 r15395  
    113113            ZipOutputStream out =
    114114                new ZipOutputStream(new FileOutputStream(zipfileNamestring));
    115             Pathname root = (Pathname) Pathname.truename(coerceToPathname(third));
     115            Pathname root = (Pathname) Symbol.PROBE_FILE.execute(third);
    116116            String rootPath = root.getDirectoryNamestring();
    117117            int rootPathLength = rootPath.length();
     
    119119            LispObject list = second;
    120120            while (list != NIL) {
    121                 Pathname pathname = (Pathname) Pathname.truename(coerceToPathname(list.car()));
     121              Pathname pathname = (Pathname) Symbol.PROBE_FILE.execute(list.car());
    122122                String namestring = pathname.getNamestring();
    123123                if (namestring == null) {
  • trunk/abcl/test/src/org/armedbear/lisp/PathnameJarTest.java

    r15394 r15395  
    1010import static org.junit.Assert.*;
    1111
    12 public class PathnameJarTest {
    13  
    14   public PathnameJarTest() {
    15   }
    16  
    17   @BeforeClass
    18   public static void setUpClass() {
    19   }
    20  
    21   @AfterClass
    22   public static void tearDownClass() {
    23   }
    24  
    25   @Before
    26   public void setUp() {
    27   }
    28  
    29   @After
    30   public void tearDown() {
     12public class PathnameJarTest
     13{
     14  @Test
     15  public void enumerate1() {
     16    String s = "jar:jar:file:/a/foo.jar!/b/baz.abcl!/path/c.lisp";
     17    List<String> r = PathnameJar.enumerate(s);
     18    assertTrue("3 results", r.size() == 3);
     19    String parts[] = {
     20      "file:/a/foo.jar",
     21      "b/baz.abcl!/",
     22      "/path/c.lisp"
     23    };
     24    for (int i = 0; i < parts.length; i++) {
     25      assertTrue(parts[i], r.get(i).equals(parts[i]));
     26    }
    3127  }
    3228
    3329  @Test
    34   public void testParseJars1() {
    35     String s1 = "jar:jar:file:/a/foo.jar!/b/baz.abcl!/path/c.lisp";
    36     List<String> r1 = PathnameJar.enumerate(s1);
    37     assertTrue(r1.size() == 3);
    38     String s10 = "file:/a/foo.jar";
    39     assertTrue(s10.equals(r1.get(0)));
    40     String s11 = "/b/baz.abcl!/";
    41     assertTrue(s11.equals(r1.get(1)));
    42     String s12 = "/path/c.lisp";
    43     assertTrue(s12.equals(r1.get(2)));
     30  public void enumerate2() {
     31    String s = "jar:jar:file:/a/foo.jar!/b/baz.abcl!/";
     32    List<String> r = PathnameJar.enumerate(s);
     33    assertTrue("2 results", r.size() == 2);
     34    String parts[] = {
     35      "file:/a/foo.jar",
     36      "b/baz.abcl!/"
     37    };
     38    for (int i = 0; i < parts.length; i++) {
     39      assertTrue(parts[i], r.get(i).equals(parts[i]));
     40    }
    4441  }
    4542
    4643  @Test
    47   public void testParseJars10() {
    48     String s1 = "jar:jar:file:/a/foo.jar!/b/baz.abcl!/";
    49     List<String> r1 = PathnameJar.enumerate(s1);
    50     assertTrue(r1.size() == 2);
    51     String s10 = "file:/a/foo.jar";
    52     assertTrue(s10.equals(r1.get(0)));
    53     String s11 = "/b/baz.abcl!/";
    54     assertTrue(s11.equals(r1.get(1)));
     44  public void enumerate3() {
     45    String s = "jar:jar:https://example.com/a/foo.jar!/b/baz.abcl!/path/c.lisp";
     46    List<String> r = PathnameJar.enumerate(s);
     47    assertTrue("3 results", r.size() == 3);
     48    String parts[] = {
     49      "https://example.com/a/foo.jar",
     50      "b/baz.abcl!/",
     51      "/path/c.lisp"
     52    };
     53    for (int i = 0; i < parts.length; i++) {
     54      assertTrue(parts[i], r.get(i).equals(parts[i]));
     55    }
    5556  }
    5657
    5758  @Test
    58   public void testParseJars2() {
    59     String s1 = "jar:jar:https://example.com/a/foo.jar!/b/baz.abcl!/path/c.lisp";
    60     List<String> r1 = PathnameJar.enumerate(s1);
    61     assertTrue(r1.size() == 3);
    62     String s10 = "https://example.com/a/foo.jar";
    63     assertTrue(s10.equals(r1.get(0)));
    64     String s11 = "/b/baz.abcl!/";
    65     assertTrue(s11.equals(r1.get(1)));
    66     String s12 = "/path/c.lisp";
    67     assertTrue(s12.equals(r1.get(2)));
     59  public void enumerate4() {
     60    String s = "jar:jar:jar:file:/a/foo.jar!/b/baz.abcl!/log4j.jar!/MF/manifest.mf";
     61    List<String> r = PathnameJar.enumerate(s);
     62    assertTrue("4 results", r.size() == 4);
     63    String parts[] = {
     64      "file:/a/foo.jar",
     65      "b/baz.abcl!/",
     66      "log4j.jar!/",
     67      "/MF/manifest.mf"
     68    };
     69    for (int i = 0; i < parts.length; i++) {
     70      assertTrue(parts[i], r.get(i).equals(parts[i]));
     71    }
    6872  }
    6973
     
    7276    String namestrings[] = {
    7377      "jar:file:foo.jar!/",
     78      "jar:file:/foo.jar!/",
    7479      "jar:jar:file:foo.jar!/baz.abcl!/",
    75       "jar:jar:file:foo.jar!/baz.abcl!/__loader__._"
     80      "jar:jar:file:/foo.jar!/baz.abcl!/",
     81      "jar:jar:file:foo.jar!/baz.abcl!/__loader__._",
     82      "jar:jar:file:/foo.jar!/baz.abcl!/__loader__._",
     83      "jar:jar:jar:file:a/b/foo.jar!/c/baz.zip!/log4j.jar!/MF/manifest.mf",
     84      "jar:jar:jar:file:/a/b/foo.jar!/c/baz.zip!/log4j.jar!/MF/manifest.mf"
    7685    };
    7786
  • trunk/abcl/test/src/org/armedbear/lisp/PathnameTest.java

    r15393 r15395  
    123123    assertTrue(parsedNamestring.equals(namestring));
    124124  }
     125 
     126  @Test
     127  public void equality() {
     128    Pathname p1 = (Pathname)Pathname.create("file://tmp/");
     129    Pathname p2 = (Pathname)Pathname.create("file://tmp/");
     130    boolean result = p1.equals(p2);
     131    assertTrue("Java equals() for Pathname", result);
     132
     133    PathnameJar p3 = (PathnameJar)Pathname.create("jar:file:/abcl.jar!/tmp/");
     134    PathnameJar p4 = (PathnameJar)Pathname.create("jar:file:/abcl.jar!/tmp/");
     135    result = p3.equals(p4);
     136    assertTrue("Java equals() for PathnameJar", result);
     137  }
    125138}
Note: See TracChangeset for help on using the changeset viewer.