1 | /* |
---|
2 | * JarPathname.java |
---|
3 | * |
---|
4 | * Copyright (C) 2020 @easye |
---|
5 | * |
---|
6 | * This program is free software; you can redistribute it and/or |
---|
7 | * modify it under the terms of the GNU General Public License |
---|
8 | * as published by the Free Software Foundation; either version 2 |
---|
9 | * of the License, or (at your option) any later version. |
---|
10 | * |
---|
11 | * This program is distributed in the hope that it will be useful, |
---|
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
---|
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
---|
14 | * GNU General Public License for more details. |
---|
15 | * |
---|
16 | * You should have received a copy of the GNU General Public License |
---|
17 | * along with this program; if not, write to the Free Software |
---|
18 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
---|
19 | * |
---|
20 | * As a special exception, the copyright holders of this library give you |
---|
21 | * permission to link this library with independent modules to produce an |
---|
22 | * executable, regardless of the license terms of these independent |
---|
23 | * modules, and to copy and distribute the resulting executable under |
---|
24 | * terms of your choice, provided that you also meet, for each linked |
---|
25 | * independent module, the terms and conditions of the license of that |
---|
26 | * module. An independent module is a module which is not derived from |
---|
27 | * or based on this library. If you modify this library, you may extend |
---|
28 | * this exception to your version of the library, but you are not |
---|
29 | * obligated to do so. If you do not wish to do so, delete this |
---|
30 | * exception statement from your version. |
---|
31 | */ |
---|
32 | |
---|
33 | package org.armedbear.lisp; |
---|
34 | |
---|
35 | import static org.armedbear.lisp.Lisp.*; |
---|
36 | |
---|
37 | import java.io.InputStream; |
---|
38 | import java.io.IOException; |
---|
39 | import java.io.File; |
---|
40 | import java.net.URL; |
---|
41 | import java.net.URI; |
---|
42 | import java.net.MalformedURLException; |
---|
43 | import java.net.URISyntaxException; |
---|
44 | import java.nio.file.FileSystem; |
---|
45 | import java.nio.file.FileSystemAlreadyExistsException; |
---|
46 | import java.nio.file.FileSystems; |
---|
47 | import java.nio.file.Path; |
---|
48 | import java.util.ArrayList; |
---|
49 | import java.util.Iterator; |
---|
50 | import java.util.List; |
---|
51 | import java.util.HashMap; |
---|
52 | import java.util.Map; |
---|
53 | import java.util.Set; |
---|
54 | import java.util.zip.ZipEntry; |
---|
55 | import java.util.zip.ZipFile; |
---|
56 | |
---|
57 | public class JarPathname |
---|
58 | extends URLPathname |
---|
59 | { |
---|
60 | static final public String JAR_URI_SUFFIX = "!/"; |
---|
61 | static final public String JAR_URI_PREFIX = "jar:"; |
---|
62 | |
---|
63 | protected JarPathname() {} |
---|
64 | |
---|
65 | public static JarPathname create() { |
---|
66 | return new JarPathname(); |
---|
67 | } |
---|
68 | |
---|
69 | public static JarPathname create(JarPathname p) { |
---|
70 | JarPathname result = new JarPathname(); |
---|
71 | result.copyFrom(p); |
---|
72 | return result; |
---|
73 | } |
---|
74 | |
---|
75 | public static JarPathname createFromPathname(Pathname p) { |
---|
76 | JarPathname result = new JarPathname(); |
---|
77 | URLPathname rootDevice = new URLPathname(); |
---|
78 | |
---|
79 | if (p instanceof URLPathname) { |
---|
80 | rootDevice.copyFrom(p); |
---|
81 | } else if (p instanceof Pathname) { |
---|
82 | rootDevice = URLPathname.create(p); |
---|
83 | } else { |
---|
84 | simple_error("Argument is already a JAR-PATHNAME: ~a", p); |
---|
85 | } |
---|
86 | |
---|
87 | result.setDevice(new Cons(rootDevice, NIL)); |
---|
88 | |
---|
89 | return result; |
---|
90 | } |
---|
91 | |
---|
92 | /** Transform a reference to a nested Jar to an entry */ |
---|
93 | public static JarPathname archiveAsEntry(JarPathname p) { |
---|
94 | JarPathname result = new JarPathname(); |
---|
95 | result = (JarPathname)result.copyFrom(p); |
---|
96 | |
---|
97 | LispObject jars = result.getJars(); |
---|
98 | jars = jars.nreverse(); |
---|
99 | Pathname entry = (Pathname)jars.car(); |
---|
100 | jars = jars.cdr().nreverse(); |
---|
101 | |
---|
102 | result |
---|
103 | .setDevice(jars) |
---|
104 | .setDirectory(entry.getDirectory()) |
---|
105 | .setName(entry.getName()) |
---|
106 | .setType(entry.getType()); |
---|
107 | |
---|
108 | return result; |
---|
109 | } |
---|
110 | |
---|
111 | |
---|
112 | /** Transform an entry in a jar to a reference as a jar */ |
---|
113 | public static JarPathname createFromEntry(JarPathname p) { |
---|
114 | JarPathname result = new JarPathname(); |
---|
115 | result |
---|
116 | .copyFrom(p) |
---|
117 | .setDirectory(NIL) |
---|
118 | .setName(NIL) |
---|
119 | .setType(NIL) |
---|
120 | .setVersion(Keyword.NEWEST); |
---|
121 | Pathname entryPath = p.getEntryPath(); |
---|
122 | LispObject device = result.getDevice(); |
---|
123 | device = device.reverse().push(entryPath).reverse(); |
---|
124 | result.setDevice(device); |
---|
125 | return result; |
---|
126 | } |
---|
127 | |
---|
128 | @DocString(name="as-jar-pathname-archive", |
---|
129 | args="pathname", |
---|
130 | returns="jar-pathname", |
---|
131 | doc="Returns PATHNAME as a reference to a JAR-PATHNAME archive" |
---|
132 | + "\n" |
---|
133 | + "If PATHNAME names an ordinary file, the resulting JAR-PATHNAME addresses the" |
---|
134 | + "file as an archive. If PATHNAME names an entry in an archive, the resulting" |
---|
135 | + "JAR-PATHNAME addresses that entry as a zip archive within that archive.") |
---|
136 | private static final Primitive AS_JAR_PATHNAME_ARCHIVE = new pf_as_jar_pathname_archive(); |
---|
137 | private static class pf_as_jar_pathname_archive extends Primitive { |
---|
138 | pf_as_jar_pathname_archive() { |
---|
139 | super("as-jar-pathname-archive", PACKAGE_EXT, true); |
---|
140 | } |
---|
141 | @Override |
---|
142 | public LispObject execute(LispObject arg) { |
---|
143 | if (arg instanceof AbstractString) { |
---|
144 | arg = coerceToPathname(arg); |
---|
145 | } |
---|
146 | if (arg instanceof JarPathname) { |
---|
147 | return createFromEntry((JarPathname)arg); |
---|
148 | } if (arg instanceof Pathname) { |
---|
149 | return createFromPathname((Pathname)arg); |
---|
150 | } |
---|
151 | type_error(arg, |
---|
152 | list(Symbol.OR, |
---|
153 | Symbol.PATHNAME, Symbol.URL_PATHNAME, |
---|
154 | Symbol.JAR_PATHNAME)); |
---|
155 | return (LispObject)UNREACHED; |
---|
156 | } |
---|
157 | }; |
---|
158 | |
---|
159 | static public JarPathname createFromFile(String s) { |
---|
160 | JarPathname result |
---|
161 | = JarPathname.create(JAR_URI_PREFIX + "file:" + s + JAR_URI_SUFFIX); |
---|
162 | result.setVersion(Keyword.NEWEST); |
---|
163 | return result; |
---|
164 | } |
---|
165 | |
---|
166 | static public JarPathname createEntryFromFile(String jar, String entry) { |
---|
167 | JarPathname result |
---|
168 | = JarPathname.create(JAR_URI_PREFIX + "file:" + jar + JAR_URI_SUFFIX + entry); |
---|
169 | result.setVersion(Keyword.NEWEST); |
---|
170 | return result; |
---|
171 | } |
---|
172 | |
---|
173 | static public JarPathname createEntryFromJar(JarPathname jar, String entry) { |
---|
174 | if (jar.isArchiveEntry()) { |
---|
175 | simple_error("Failed to create the entry ~a in ~a", entry, jar); |
---|
176 | return (JarPathname)UNREACHED; |
---|
177 | } |
---|
178 | JarPathname result = new JarPathname(); |
---|
179 | result.copyFrom(jar); |
---|
180 | String path = new String(entry); |
---|
181 | if (!path.startsWith("/")) { |
---|
182 | path = "/" + path; |
---|
183 | } |
---|
184 | Pathname p = Pathname.create(path); |
---|
185 | result |
---|
186 | .setDirectory(p.getDirectory()) |
---|
187 | .setName(p.getName()) |
---|
188 | .setType(p.getType()) |
---|
189 | .setVersion(Keyword.NEWEST); |
---|
190 | |
---|
191 | return result; |
---|
192 | } |
---|
193 | /** |
---|
194 | * Enumerate the components of a jar namestring |
---|
195 | */ |
---|
196 | static List<String> enumerate(String s) { |
---|
197 | ArrayList<String> result = new ArrayList<String>(); |
---|
198 | |
---|
199 | int i = s.lastIndexOf(JAR_URI_PREFIX); |
---|
200 | if (i == -1) { |
---|
201 | parse_error("Failed to find any occurence of '" + JAR_URI_PREFIX + "' prefixes:" + s); |
---|
202 | return null; // not reached |
---|
203 | } |
---|
204 | i += JAR_URI_PREFIX.length(); // advance index to end of "jar:jar:jar:..." |
---|
205 | if ((i % JAR_URI_PREFIX.length()) != 0) { |
---|
206 | parse_error("Failed to parse 'jar:' prefixes:" + s); |
---|
207 | return null; |
---|
208 | } |
---|
209 | int prefixCount = i / JAR_URI_PREFIX.length(); |
---|
210 | String withoutPrefixes = s.substring(i); |
---|
211 | |
---|
212 | String parts[] = withoutPrefixes.split(JAR_URI_SUFFIX); |
---|
213 | |
---|
214 | // Do we have as many prefixes as suffixes? |
---|
215 | String notEndingInSuffix = withoutPrefixes + "nonce"; |
---|
216 | String suffixParts[] = notEndingInSuffix.split(JAR_URI_SUFFIX); |
---|
217 | int suffixCount = suffixParts.length - 1; |
---|
218 | if (suffixCount != prefixCount) { |
---|
219 | parse_error("Mismatched 'jar:' prefix and '/!' suffixes in jar: " + s); |
---|
220 | return null; |
---|
221 | } |
---|
222 | |
---|
223 | if (parts.length == 1) { |
---|
224 | if (!s.endsWith(JAR_URI_SUFFIX)) { |
---|
225 | error(new SimpleError("No trailing jar uri suffix: " + s)); |
---|
226 | return null; |
---|
227 | } |
---|
228 | if (!isValidURL(parts[0])) { |
---|
229 | error(new SimpleError("Not a valid URI: " + parts[0])); |
---|
230 | return null; |
---|
231 | } |
---|
232 | |
---|
233 | result.add(parts[0]); |
---|
234 | return result; |
---|
235 | } |
---|
236 | |
---|
237 | // The root, non-JarPathname location of this reference |
---|
238 | // For files, possibly either a relative or absolute directory |
---|
239 | result.add(parts[0]); |
---|
240 | |
---|
241 | // The references to the pathnames of archives located within the |
---|
242 | // root jar. |
---|
243 | // These will be relative directory paths suffixed with JAR_URI_SUFFIX |
---|
244 | for (int j = 1; j < prefixCount; j++) { |
---|
245 | String ns = parts[j] + JAR_URI_SUFFIX; |
---|
246 | result.add(ns); |
---|
247 | } |
---|
248 | |
---|
249 | // possibly return the path inside the last jar as an absolute path |
---|
250 | if (parts.length == (prefixCount + 1)) { |
---|
251 | result.add("/" + parts[parts.length - 1]); |
---|
252 | } |
---|
253 | |
---|
254 | return result; |
---|
255 | } |
---|
256 | |
---|
257 | static public JarPathname create(String s) { |
---|
258 | if (!s.startsWith(JAR_URI_PREFIX)) { |
---|
259 | parse_error("Cannot create a PATHNAME-JAR from namestring: " + s); |
---|
260 | return (JarPathname)UNREACHED; |
---|
261 | } |
---|
262 | |
---|
263 | List<String> contents = JarPathname.enumerate(s); |
---|
264 | |
---|
265 | if (contents == null) { |
---|
266 | parse_error("Couldn't parse PATHNAME-JAR from namestring: " + s); |
---|
267 | return (JarPathname)UNREACHED; |
---|
268 | } |
---|
269 | |
---|
270 | JarPathname result = new JarPathname(); |
---|
271 | |
---|
272 | // Normalize the root jar to be a URL |
---|
273 | URLPathname rootPathname; |
---|
274 | String rootNamestring = contents.get(0); |
---|
275 | if (!isValidURL(rootNamestring)) { |
---|
276 | Pathname root = Pathname.create(rootNamestring); |
---|
277 | rootPathname = URLPathname.createFromFile(root); |
---|
278 | } else { |
---|
279 | rootPathname = URLPathname.create(rootNamestring); |
---|
280 | } |
---|
281 | |
---|
282 | LispObject jars = NIL; |
---|
283 | jars = jars.push(rootPathname); |
---|
284 | |
---|
285 | if (contents.size() == 1) { |
---|
286 | result.setDevice(jars); |
---|
287 | return result; |
---|
288 | } |
---|
289 | |
---|
290 | for (int i = 1; i < contents.size(); i++) { |
---|
291 | String ns = contents.get(i); |
---|
292 | if (ns.endsWith(JAR_URI_SUFFIX)) { |
---|
293 | String nsWithoutSuffix = ns.substring(0, ns.length() - JAR_URI_SUFFIX.length()); |
---|
294 | Pathname pathname = (Pathname)Pathname.create(nsWithoutSuffix); |
---|
295 | Pathname jar = new Pathname(); |
---|
296 | jar.copyFrom(pathname); |
---|
297 | jars = jars.push(jar); |
---|
298 | } else { |
---|
299 | Pathname p = (Pathname)Pathname.create(contents.get(i)); |
---|
300 | result.copyFrom(p); |
---|
301 | } |
---|
302 | } |
---|
303 | jars = jars.nreverse(); |
---|
304 | result.setDevice(jars); |
---|
305 | result.validateComponents(); |
---|
306 | return result; |
---|
307 | } |
---|
308 | |
---|
309 | public LispObject validateComponents() { |
---|
310 | if (!(getDevice() instanceof Cons)) { |
---|
311 | return type_error("Invalid DEVICE for JAR-PATHNAME", getDevice(), Symbol.CONS); |
---|
312 | } |
---|
313 | |
---|
314 | LispObject jars = getDevice(); |
---|
315 | |
---|
316 | LispObject rootJar = getRootJar(); |
---|
317 | if (!(rootJar instanceof URLPathname)) { |
---|
318 | return type_error("The first element in the DEVICE component of a JAR-PATHNAME is not of expected type", |
---|
319 | rootJar, |
---|
320 | Symbol.URL_PATHNAME); |
---|
321 | } |
---|
322 | |
---|
323 | jars = jars.cdr(); |
---|
324 | |
---|
325 | while (!jars.car().equals(NIL)) { |
---|
326 | LispObject jar = jars.car(); |
---|
327 | if (!((jar instanceof Pathname) |
---|
328 | || (jar instanceof URLPathname))) { |
---|
329 | return type_error("The value in DEVICE component of a JAR-PATHNAME is not of expected type", |
---|
330 | jar, |
---|
331 | list(Symbol.OR, |
---|
332 | Symbol.PATHNAME, Symbol.URL_PATHNAME)); |
---|
333 | } |
---|
334 | jars = jars.cdr(); |
---|
335 | } |
---|
336 | |
---|
337 | return T; |
---|
338 | } |
---|
339 | |
---|
340 | public String getNamestring() { |
---|
341 | StringBuffer sb = new StringBuffer(); |
---|
342 | |
---|
343 | LispObject jars = getJars(); |
---|
344 | |
---|
345 | if (jars.equals(NIL) || jars.equals(Keyword.UNSPECIFIC)) { |
---|
346 | // type_error("JAR-PATHNAME has bad DEVICE", |
---|
347 | // jars, |
---|
348 | // list(Symbol.NOT, |
---|
349 | // list(Symbol.OR, |
---|
350 | // list(Symbol.EQL, NIL), |
---|
351 | // list(Symbol.EQL, Keyword.UNSPECIFIC)))); |
---|
352 | return null; |
---|
353 | } |
---|
354 | |
---|
355 | for (int i = 0; i < jars.length() - 1; i++) { |
---|
356 | sb.append(JAR_URI_PREFIX); |
---|
357 | } |
---|
358 | |
---|
359 | LispObject root = getRootJar(); |
---|
360 | |
---|
361 | if (root instanceof URLPathname) { |
---|
362 | String ns = ((URLPathname)root).getNamestringAsURL(); |
---|
363 | sb.append(JAR_URI_PREFIX) |
---|
364 | .append(ns) |
---|
365 | .append(JAR_URI_SUFFIX); |
---|
366 | } else if (root instanceof Pathname) { // For transitional compatibility? |
---|
367 | String ns = ((Pathname)root).getNamestring(); |
---|
368 | sb.append(JAR_URI_PREFIX) |
---|
369 | .append("file:") |
---|
370 | .append(ns) |
---|
371 | .append(JAR_URI_SUFFIX); |
---|
372 | } else { |
---|
373 | simple_error("Unable to generate namestring for jar with root pathname ~a", root); |
---|
374 | } |
---|
375 | |
---|
376 | LispObject innerJars = jars.cdr(); |
---|
377 | while (innerJars.car() != NIL) { |
---|
378 | Pathname jar = (Pathname)innerJars.car(); |
---|
379 | Pathname p = new Pathname(); |
---|
380 | p.copyFrom(jar) |
---|
381 | .setDevice(NIL); |
---|
382 | String ns = p.getNamestring(); |
---|
383 | sb.append(ns) |
---|
384 | .append(JAR_URI_SUFFIX); |
---|
385 | innerJars = innerJars.cdr(); |
---|
386 | } |
---|
387 | |
---|
388 | if (getDirectory() != NIL |
---|
389 | || getName() != NIL |
---|
390 | || getType() != NIL) { |
---|
391 | |
---|
392 | Pathname withoutDevice = new Pathname(); |
---|
393 | withoutDevice |
---|
394 | .copyFrom(this) |
---|
395 | .setDevice(NIL); |
---|
396 | |
---|
397 | String withoutDeviceNamestring = withoutDevice.getNamestring(); // need to URI encode? |
---|
398 | if (withoutDeviceNamestring.startsWith("/")) { |
---|
399 | sb.append(withoutDeviceNamestring.substring(1)); |
---|
400 | } else { |
---|
401 | sb.append(withoutDeviceNamestring); |
---|
402 | } |
---|
403 | } |
---|
404 | |
---|
405 | return sb.toString(); |
---|
406 | } |
---|
407 | |
---|
408 | LispObject getRootJar() { |
---|
409 | LispObject jars = getJars(); |
---|
410 | if (!(jars instanceof Cons)) { |
---|
411 | type_error("JAR-PATHNAME device is not a cons", |
---|
412 | jars, Symbol.CONS); |
---|
413 | return (LispObject)UNREACHED; |
---|
414 | } |
---|
415 | |
---|
416 | return jars.car(); |
---|
417 | } |
---|
418 | |
---|
419 | String getRootJarAsURLString() { |
---|
420 | return |
---|
421 | JarPathname.JAR_URI_PREFIX |
---|
422 | + ((URLPathname)getRootJar()).getNamestring() |
---|
423 | + JarPathname.JAR_URI_SUFFIX; |
---|
424 | } |
---|
425 | |
---|
426 | |
---|
427 | LispObject getJars() { |
---|
428 | return getDevice(); |
---|
429 | } |
---|
430 | |
---|
431 | public static LispObject truename(Pathname pathname, |
---|
432 | boolean errorIfDoesNotExist) { |
---|
433 | if (!(pathname instanceof JarPathname)) { |
---|
434 | return URLPathname.truename(pathname, errorIfDoesNotExist); |
---|
435 | } |
---|
436 | JarPathname p = new JarPathname(); |
---|
437 | p.copyFrom(pathname); |
---|
438 | |
---|
439 | // Run truename resolution on the path of local jar archives |
---|
440 | if (p.isLocalFile()) { |
---|
441 | Pathname rootJar; |
---|
442 | if (URLPathname.hasExplicitFile((Pathname)p.getRootJar())) { |
---|
443 | rootJar = new URLPathname(); |
---|
444 | } else { |
---|
445 | rootJar = new Pathname(); |
---|
446 | } |
---|
447 | rootJar.copyFrom((Pathname)p.getRootJar()); |
---|
448 | |
---|
449 | // Ensure that we don't return a JarPathname if the current |
---|
450 | // default is one when we resolve its TRUENAME. Under Windows, |
---|
451 | // the device will get filled in with the DOS drive letter if |
---|
452 | // applicable. |
---|
453 | if (rootJar.getDevice().equals(NIL) |
---|
454 | && !Utilities.isPlatformWindows) { |
---|
455 | rootJar.setDevice(Keyword.UNSPECIFIC); |
---|
456 | } |
---|
457 | LispObject rootJarTruename = Pathname.truename(rootJar, errorIfDoesNotExist); |
---|
458 | if (rootJarTruename.equals(NIL)) { |
---|
459 | return Pathname.doTruenameExit(rootJar, errorIfDoesNotExist); |
---|
460 | } |
---|
461 | LispObject otherJars = p.getJars().cdr(); |
---|
462 | URLPathname newRootJar; |
---|
463 | if (rootJarTruename instanceof Pathname) { |
---|
464 | newRootJar = URLPathname.createFromFile((Pathname)rootJarTruename); |
---|
465 | } else { |
---|
466 | newRootJar = (URLPathname) rootJarTruename; |
---|
467 | } |
---|
468 | |
---|
469 | p.setDevice(new Cons(newRootJar, otherJars)); |
---|
470 | } |
---|
471 | |
---|
472 | if (!p.isArchiveEntry()) { |
---|
473 | ZipCache.Archive archive = ZipCache.getArchive(p); |
---|
474 | if (archive == null) { |
---|
475 | return Pathname.doTruenameExit(pathname, errorIfDoesNotExist); |
---|
476 | } |
---|
477 | return p; |
---|
478 | } |
---|
479 | |
---|
480 | ZipEntry entry = ZipCache.getZipEntry(p); |
---|
481 | if (entry == null) { |
---|
482 | return Pathname.doTruenameExit(pathname, errorIfDoesNotExist); |
---|
483 | } |
---|
484 | return p; |
---|
485 | } |
---|
486 | |
---|
487 | public boolean isLocalFile() { |
---|
488 | Pathname p = (Pathname) getRootJar(); |
---|
489 | if (p != null) { |
---|
490 | return p.isLocalFile(); |
---|
491 | } |
---|
492 | return false; |
---|
493 | } |
---|
494 | |
---|
495 | public boolean isArchiveEntry() { |
---|
496 | return !(getDirectory().equals(NIL) |
---|
497 | && getName().equals(NIL) |
---|
498 | && getType().equals(NIL)); |
---|
499 | } |
---|
500 | |
---|
501 | public JarPathname getArchive() { |
---|
502 | if (!isArchiveEntry()) { |
---|
503 | return (JarPathname)simple_error("Pathname already represents an archive."); |
---|
504 | } |
---|
505 | JarPathname archive = new JarPathname(); |
---|
506 | archive.copyFrom(this); |
---|
507 | archive |
---|
508 | .setDirectory(NIL) |
---|
509 | .setName(NIL) |
---|
510 | .setType(NIL); |
---|
511 | return archive; |
---|
512 | } |
---|
513 | |
---|
514 | public LispObject classOf() { |
---|
515 | return BuiltInClass.JAR_PATHNAME; |
---|
516 | } |
---|
517 | |
---|
518 | @Override |
---|
519 | public LispObject typeOf() { |
---|
520 | return Symbol.JAR_PATHNAME; |
---|
521 | } |
---|
522 | |
---|
523 | public InputStream getInputStream() { |
---|
524 | // XXX We only return the bytes of an entry in a JAR |
---|
525 | if (!isArchiveEntry()) { |
---|
526 | simple_error("Can only get input stream for an entry in a JAR-PATHNAME.", this); |
---|
527 | } |
---|
528 | InputStream result = ZipCache.getEntryAsInputStream(this); |
---|
529 | if (result == null) { |
---|
530 | error(new FileError("Failed to get InputStream", this)); |
---|
531 | } |
---|
532 | return result; |
---|
533 | } |
---|
534 | |
---|
535 | /** List the contents of a directory within a JAR archive */ |
---|
536 | static public LispObject listDirectory(JarPathname pathname) { |
---|
537 | String directory = pathname.asEntryPath(); |
---|
538 | // We should only be listing directories |
---|
539 | if (pathname.getDirectory() == NIL) { |
---|
540 | return simple_error("Not a directory in a jar ~a", pathname); |
---|
541 | } |
---|
542 | |
---|
543 | if (directory.length() == 0) { |
---|
544 | directory = "/*"; |
---|
545 | } else { |
---|
546 | if (directory.endsWith("/")) { |
---|
547 | directory = "/" + directory + "*"; |
---|
548 | } else { |
---|
549 | directory = "/" + directory + "/*"; |
---|
550 | } |
---|
551 | } |
---|
552 | |
---|
553 | Pathname wildcard = (Pathname)Pathname.create(directory); |
---|
554 | |
---|
555 | LispObject result = NIL; |
---|
556 | |
---|
557 | Iterator<Map.Entry<JarPathname,ZipEntry>> iterator = ZipCache.getEntriesIterator(pathname); |
---|
558 | while (iterator.hasNext()) { |
---|
559 | Map.Entry<JarPathname,ZipEntry> e = iterator.next(); |
---|
560 | JarPathname entry = e.getKey(); |
---|
561 | if (!Symbol.PATHNAME_MATCH_P.execute(entry, wildcard).equals(NIL)) { |
---|
562 | result = result.push(entry); |
---|
563 | } |
---|
564 | } |
---|
565 | return result.nreverse(); |
---|
566 | } |
---|
567 | |
---|
568 | @DocString(name="match-wild-jar-pathname", |
---|
569 | args="wild-jar-pathname", |
---|
570 | returns="pathnames", |
---|
571 | doc="Returns the pathnames matching WILD-JAR-PATHNAME which must be both wild and a JAR-PATHNAME") |
---|
572 | static final Primitive MATCH_WILD_JAR_PATHNAME = new pf_match_wild_jar_pathname(); |
---|
573 | |
---|
574 | private static class pf_match_wild_jar_pathname extends Primitive { |
---|
575 | pf_match_wild_jar_pathname() { |
---|
576 | super(Symbol.MATCH_WILD_JAR_PATHNAME, "wild-jar-pathname"); |
---|
577 | } |
---|
578 | @Override |
---|
579 | public LispObject execute(LispObject arg) { |
---|
580 | Pathname pathname = coerceToPathname(arg); |
---|
581 | if (pathname instanceof LogicalPathname) { |
---|
582 | pathname = LogicalPathname.translateLogicalPathname((LogicalPathname) pathname); |
---|
583 | } |
---|
584 | if (!pathname.isJar()) { |
---|
585 | return new FileError("Not a jar pathname.", pathname); |
---|
586 | } |
---|
587 | if (!pathname.isWild()) { |
---|
588 | return new FileError("Not a wild pathname.", pathname); |
---|
589 | } |
---|
590 | |
---|
591 | JarPathname jarPathname = new JarPathname(); |
---|
592 | jarPathname |
---|
593 | .copyFrom(pathname) |
---|
594 | .setDirectory(NIL) |
---|
595 | .setName(NIL) |
---|
596 | .setType(NIL); |
---|
597 | JarPathname wildcard = (JarPathname)Symbol.TRUENAME.execute(jarPathname); |
---|
598 | Iterator<Map.Entry<JarPathname,ZipEntry>> iterator |
---|
599 | = ZipCache.getEntriesIterator(wildcard); |
---|
600 | wildcard |
---|
601 | .setDirectory(pathname.getDirectory()) |
---|
602 | .setName(pathname.getName()) |
---|
603 | .setType(pathname.getType()); |
---|
604 | |
---|
605 | LispObject result = NIL; |
---|
606 | while (iterator.hasNext()) { |
---|
607 | Map.Entry<JarPathname,ZipEntry> e = iterator.next(); |
---|
608 | JarPathname entry = e.getKey(); |
---|
609 | LispObject matches |
---|
610 | = Symbol.PATHNAME_MATCH_P.execute(entry, wildcard); |
---|
611 | |
---|
612 | if (!matches.equals(NIL)) { |
---|
613 | result = new Cons(entry, result); |
---|
614 | } |
---|
615 | } |
---|
616 | |
---|
617 | return result; |
---|
618 | } |
---|
619 | } |
---|
620 | |
---|
621 | public long getLastModified() { |
---|
622 | if (!isArchiveEntry()) { |
---|
623 | ZipCache.Archive archive = ZipCache.getArchive(this); |
---|
624 | if (archive != null) { |
---|
625 | return archive.lastModified; |
---|
626 | } |
---|
627 | } else { |
---|
628 | ZipEntry entry = ZipCache.getZipEntry(this); |
---|
629 | if (entry != null) { |
---|
630 | return entry.getTime(); |
---|
631 | } |
---|
632 | } |
---|
633 | return 0; |
---|
634 | } |
---|
635 | |
---|
636 | static JarPathname joinEntry(JarPathname root, Pathname entry) { |
---|
637 | JarPathname result = new JarPathname(); |
---|
638 | result |
---|
639 | .copyFrom(root) |
---|
640 | .setDirectory(entry.getDirectory()) |
---|
641 | .setName(entry.getName()) |
---|
642 | .setType(entry.getType()); // ??? VERSION |
---|
643 | return result; |
---|
644 | } |
---|
645 | |
---|
646 | static final Map<String, ?> emptyEnvironment = new HashMap(); |
---|
647 | |
---|
648 | public File getFile() { |
---|
649 | String jarFile = ((Pathname)getRootJar()).getNamestring(); |
---|
650 | URI jarURI = URI.create(JAR_URI_PREFIX + jarFile); |
---|
651 | // Path jarPath = Path.of(jarURI); |
---|
652 | // Map<String, String> env = Map.of("create", "true"); |
---|
653 | FileSystem zipfs = null; |
---|
654 | try { |
---|
655 | zipfs = FileSystems.newFileSystem(jarURI, emptyEnvironment); |
---|
656 | } catch (FileSystemAlreadyExistsException e0) { |
---|
657 | zipfs = FileSystems.getFileSystem(jarURI); |
---|
658 | } catch (IOException e1) { |
---|
659 | error(new JavaException(e1)); |
---|
660 | } |
---|
661 | String entryPath = getEntryPath().getNamestring(); |
---|
662 | String absoluteEntryPath = "/" + entryPath; |
---|
663 | Path path = zipfs.getPath(absoluteEntryPath); |
---|
664 | return path.toFile(); |
---|
665 | } |
---|
666 | } |
---|