Changeset 12607
- Timestamp:
- 04/15/10 14:27:09 (14 years ago)
- Location:
- trunk/abcl
- Files:
-
- 14 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/abcl/abcl.asd
r12509 r12607 36 36 (:file "mop-tests" :depends-on ("mop-tests-setup")) 37 37 (:file "file-system-tests") 38 (:file "jar-file" )38 (:file "jar-file" :depend-on ("pathname-test")) 39 39 (:file "math-tests") 40 40 (:file "misc-tests") -
trunk/abcl/doc/design/pathnames/jar-pathnames.markdown
r12560 r12607 4 4 Mark Evenson 5 5 Created: 09 JAN 2010 6 Modified: 16MAR 20107 8 Notes towards sketching an implementation of "jar:" references to be9 contained in Common Lisp `PATHNAMEs` within ABCL. 6 Modified: 25 MAR 2010 7 8 Notes towards an implementation of "jar:" references to be contained 9 in Common Lisp `PATHNAME`s within ABCL. 10 10 11 11 Goals … … 52 52 resolve works. 53 53 54 7. Make jar pathnames work as a valid argument for OPEN. 54 7. Make jar pathnames work as a valid argument for OPEN with 55 :DIRECTION :INPUT. 55 56 56 57 8. Enable the loading of ASDF systems packaged within jar files. 58 59 9. Enable the matching of jar pathnames with PATHNAME-MATCH-P 60 61 (pathname-match-p 62 "jar:file:/a/b/some.jar!/a/system/def.asd" 63 "jar:file:/**/*.jar!/**/*.asd") 64 ==> t 57 65 58 66 Status 59 67 ------ 60 68 61 As of svn r12501, all the above goals have been implemented and tested 62 *except* for: 63 64 7. Make jar pathnames work as a valid argument for OPEN. 69 As of svn r125??, all the above goals have been implemented and 70 tested. 65 71 66 72 … … 68 74 -------------- 69 75 70 Using PATHNAMES 71 72 * A PATHNAME refering to a file within a JAR is known as a JAR 73 PATHNAME. It can either refer to the entire JAR file or an entry 74 within the JAR file. 75 76 * A JAR PATHNAME always has a DEVICE which is a proper list. This 77 distinguishes it from other uses of Pathname. 78 79 * The DEVICE of a JAR PATHNAME will be a list with either one or two 80 elements. The first element of the JAR PATHNAME can be either a 81 PATHNAME representing a JAR on the filesystem, or a SimpleString 82 representing a URL. 83 84 * a PATHNAME occuring in the list in the DEVICE of a JAR PATHNAME is 85 known as a DEVICE PATHNAME. 86 87 * If the DEVICE is a String it must be a String that successfully 88 references a URL via the java.net.URL(String) constructor 89 90 * Only the first entry in the the DEVICE list may be a String. 91 92 * Otherwise the the DEVICE PATHAME denotes the PATHNAME of the JAR file 93 94 * The DEVICE PATHNAME list of enclosing JARs runs from outermost to 95 innermost. 76 A PATHNAME refering to a file within a JAR is known as a JAR PATHNAME. 77 It can either refer to the entire JAR file or an entry within the JAR 78 file. 79 80 A JAR PATHNAME always has a DEVICE which is a proper list. This 81 distinguishes it from other uses of Pathname. 82 83 The DEVICE of a JAR PATHNAME will be a list with either one or two 84 elements. The first element of the JAR PATHNAME can be either a 85 PATHNAME representing a JAR on the filesystem, or a SimpleString 86 representing a URL. 87 88 A PATHNAME occuring in the list in the DEVICE of a JAR PATHNAME is 89 known as a DEVICE PATHNAME. 90 91 If the DEVICE is a String it must be a String that successfully 92 references a URL via the java.net.URL(String) constructor 93 94 Only the first entry in the the DEVICE list may be a String. 95 96 Otherwise the the DEVICE PATHAME denotes the PATHNAME of the JAR file. 97 98 The DEVICE PATHNAME list of enclosing JARs runs from outermost to 99 innermost. 96 100 97 * The DIRECTORY component of a JAR PATHNAME should be a list starting 98 with the :ABSOLUTE keyword. Even though hierarchial entries in 99 jar files are stored in the form "foo/bar/a.lisp" not 100 "/foo/bar/a.lisp", the meaning of DIRECTORY component better 101 represented as an absolute path. 101 The DIRECTORY component of a JAR PATHNAME should be a list starting 102 with the :ABSOLUTE keyword. Even though hierarchial entries in jar 103 files are stored in the form "foo/bar/a.lisp" not "/foo/bar/a.lisp", 104 the meaning of DIRECTORY component better represented as an absolute 105 path. 106 107 A jar Pathname has type JAR-PATHNAME, derived from PATHNAME. 102 108 103 109 BNF -
trunk/abcl/doc/design/pathnames/url-pathnames.markdown
r12573 r12607 111 111 conventions of a URL. 112 112 113 A URL Pathname has type URL-PATHNAME, derived from PATHNAME. 113 114 114 115 Status -
trunk/abcl/src/org/armedbear/lisp/BuiltInClass.java
r12575 r12607 114 114 public static final BuiltInClass PACKAGE = addClass(Symbol.PACKAGE); 115 115 public static final BuiltInClass PATHNAME = addClass(Symbol.PATHNAME); 116 public static final BuiltInClass JAR_PATHNAME = addClass(Symbol.JAR_PATHNAME); 117 public static final BuiltInClass URL_PATHNAME = addClass(Symbol.URL_PATHNAME); 116 118 public static final BuiltInClass RANDOM_STATE = addClass(Symbol.RANDOM_STATE); 117 119 public static final BuiltInClass RATIO = addClass(Symbol.RATIO); … … 179 181 addClass(Symbol.FILE_STREAM, 180 182 new StructureClass(Symbol.FILE_STREAM, list(SYSTEM_STREAM))); 183 public static final LispClass JAR_STREAM = 184 addClass(Symbol.JAR_STREAM, 185 new StructureClass(Symbol.JAR_STREAM, list(SYSTEM_STREAM))); 186 public static final LispClass URL_STREAM = 187 addClass(Symbol.URL_STREAM, 188 new StructureClass(Symbol.URL_STREAM, list(SYSTEM_STREAM))); 181 189 public static final LispClass CONCATENATED_STREAM = 182 190 addClass(Symbol.CONCATENATED_STREAM, … … 231 239 FILE_STREAM.setCPL(FILE_STREAM, SYSTEM_STREAM, STREAM, 232 240 STRUCTURE_OBJECT, CLASS_T); 241 JAR_STREAM.setCPL(JAR_STREAM, SYSTEM_STREAM, STREAM, 242 STRUCTURE_OBJECT, CLASS_T); 243 URL_STREAM.setCPL(URL_STREAM, SYSTEM_STREAM, STREAM, 244 STRUCTURE_OBJECT, CLASS_T); 233 245 FLOAT.setDirectSuperclass(REAL); 234 246 FLOAT.setCPL(FLOAT, REAL, NUMBER, CLASS_T); … … 261 273 PATHNAME.setDirectSuperclass(CLASS_T); 262 274 PATHNAME.setCPL(PATHNAME, CLASS_T); 275 JAR_PATHNAME.setDirectSuperclass(PATHNAME); 276 JAR_PATHNAME.setCPL(JAR_PATHNAME, PATHNAME, CLASS_T); 277 URL_PATHNAME.setDirectSuperclass(PATHNAME); 278 URL_PATHNAME.setCPL(URL_PATHNAME, PATHNAME, CLASS_T); 263 279 RANDOM_STATE.setDirectSuperclass(CLASS_T); 264 280 RANDOM_STATE.setCPL(RANDOM_STATE, CLASS_T); -
trunk/abcl/src/org/armedbear/lisp/Cons.java
r12598 r12607 59 59 this.car = new SimpleString(name); 60 60 this.cdr = value != null ? value : NULL_VALUE; 61 ++count; 62 } 63 64 public Cons(Cons original) 65 { 66 Cons rest = original; 67 LispObject result = NIL; 68 while (rest.car() != NIL) { 69 result = result.push(rest.car()); 70 if (rest.cdr() == NIL) { 71 result = result.push(NIL); 72 break; 73 } 74 rest = (Cons) rest.cdr(); 75 } 76 result = result.nreverse(); 77 this.car = result.car(); 78 this.cdr = result.cdr(); 61 79 ++count; 62 80 } -
trunk/abcl/src/org/armedbear/lisp/FileStream.java
r12422 r12607 287 287 return type_error(first, Symbol.PATHNAME); 288 288 } 289 if (pathname.isJar()) {290 error(new FileError("Direct stream input/output on entries in JAR files no currently supported.",291 pathname));292 }293 294 289 final LispObject namestring = checkString(second); 295 290 LispObject elementType = third; … … 301 296 direction != Keyword.IO) 302 297 error(new LispError("Direction must be :INPUT, :OUTPUT, or :IO.")); 303 try { 304 return new FileStream(pathname, namestring.getStringValue(), 305 elementType, direction, ifExists, 306 externalFormat); 307 } 308 catch (FileNotFoundException e) { 309 return NIL; 310 } 311 catch (IOException e) { 312 return error(new StreamError(null, e)); 298 299 if (pathname.isJar()) { 300 if (direction != Keyword.INPUT) { 301 error(new FileError("Only direction :INPUT is supported for jar files.", pathname)); 302 } 303 try { 304 return new JarStream(pathname, namestring.getStringValue(), 305 elementType, direction, ifExists, 306 externalFormat); 307 } catch (IOException e) { 308 return error(new StreamError(null, e)); 309 } 310 } else if (pathname.isURL()) { 311 if (direction != Keyword.INPUT) { 312 error(new FileError("Only direction :INPUT is supported for URLs.", pathname)); 313 } 314 try { 315 return new URLStream(pathname, namestring.getStringValue(), 316 elementType, direction, ifExists, 317 externalFormat); 318 } catch (IOException e) { 319 return error(new StreamError(null, e)); 320 } 321 } else { 322 try { 323 return new FileStream(pathname, namestring.getStringValue(), 324 elementType, direction, ifExists, 325 externalFormat); 326 } 327 catch (FileNotFoundException e) { 328 return NIL; 329 } 330 catch (IOException e) { 331 return error(new StreamError(null, e)); 332 } 313 333 } 314 334 } -
trunk/abcl/src/org/armedbear/lisp/Lisp.java
r12599 r12607 1742 1742 if (arg instanceof FileStream) 1743 1743 return ((FileStream)arg).getPathname(); 1744 if (arg instanceof JarStream) 1745 return ((JarStream)arg).getPathname(); 1746 if (arg instanceof URLStream) 1747 return ((URLStream)arg).getPathname(); 1744 1748 type_error(arg, list(Symbol.OR, Symbol.PATHNAME, 1745 Symbol.STRING, Symbol.FILE_STREAM)); 1749 Symbol.STRING, Symbol.FILE_STREAM, 1750 Symbol.JAR_STREAM, Symbol.URL_STREAM)); 1746 1751 // Not reached. 1747 1752 return null; -
trunk/abcl/src/org/armedbear/lisp/Load.java
r12597 r12607 463 463 if (type.equals(COMPILE_FILE_TYPE) 464 464 || type.equals(COMPILE_FILE_INIT_FASL_TYPE.toString())) { 465 thread.bindSpecial(Symbol.LOAD_TRUENAME_FASL, truePathname); 465 Pathname truenameFasl = new Pathname(truePathname); 466 thread.bindSpecial(Symbol.LOAD_TRUENAME_FASL, truenameFasl); 466 467 } 467 468 if (truePathname.type.getStringValue() … … 469 470 && truePathname.isJar()) { 470 471 if (truePathname.device.cdr() != NIL ) { 471 // set truename to the enclosing JAR 472 // We set *LOAD-TRUENAME* to the argument that 473 // a user would pass to LOAD. 474 Pathname enclosingJar = (Pathname)truePathname.device.cdr().car(); 475 truePathname.device = new Cons(truePathname.device.car(), NIL); 472 476 truePathname.host = NIL; 473 truePathname.directory = NIL; 474 truePathname.name = NIL; 475 truePathname.type = NIL; 477 truePathname.directory = enclosingJar.directory; 478 if (truePathname.directory.car().equals(Keyword.RELATIVE)) { 479 truePathname.directory.setCar(Keyword.ABSOLUTE); 480 } 481 truePathname.name = enclosingJar.name; 482 truePathname.type = enclosingJar.type; 476 483 truePathname.invalidateNamestring(); 477 484 } else { -
trunk/abcl/src/org/armedbear/lisp/Pathname.java
r12551 r12607 40 40 import java.io.FileInputStream; 41 41 import java.net.MalformedURLException; 42 import java.net.URI; 43 import java.net.URISyntaxException; 42 44 import java.net.URL; 43 45 import java.net.URLDecoder; 46 import java.net.URLConnection; 44 47 import java.util.Enumeration; 45 48 import java.util.StringTokenizer; … … 65 68 * We could do this with setter/getters, but that choose not to in order to avoid the 66 69 * performance indirection penalty. 70 * 71 * Although, given the number of bugs that crop up when this 72 * protocol is not adhered to, maybe we should consider it. 67 73 */ 68 74 public void invalidateNamestring() { … … 79 85 } else if (p.host instanceof Symbol) { 80 86 host = p.host; 87 } else if (p.host instanceof Cons) { 88 host = new Cons((Cons)p.host); 81 89 } else { 82 90 Debug.assertTrue(false); … … 153 161 154 162 public static boolean isSupportedProtocol(String protocol) { 155 return "jar".equals(protocol) || "file".equals(protocol); 163 // There is no programmatic way to know what protocols will 164 // sucessfully construct a URL, so we check for well known ones... 165 if ("jar".equals(protocol) 166 || "file".equals(protocol)) 167 // || "http".equals(protocol)) XXX remove this as an optimization 168 { 169 return true; 170 } 171 // ... and try the entire constructor with some hopefully 172 // reasonable parameters for everything else. 173 try { 174 new URL(protocol, "example.org", "foo"); 175 return true; 176 } catch (MalformedURLException e) { 177 return false; 178 } 156 179 } 157 180 158 181 public Pathname(URL url) { 159 String protocol = url.getProtocol(); 160 if (!isSupportedProtocol(protocol)) { 161 error(new LispError("Unsupported URL: '" + url.toString() + "'")); 162 } 163 164 if ("jar".equals(protocol)) { 165 init(url.toString()); 166 return; 167 } else if ("file".equals(protocol)) { 182 if ("file".equals(url.getProtocol())) { 168 183 String s; 169 184 try { … … 189 204 return; 190 205 } 206 } else { 207 init(url.toString()); 208 return; 191 209 } 192 210 error(new LispError("Failed to construct Pathname from URL: " 193 211 + "'" + url.toString() + "'")); 194 212 } 213 214 static final Symbol SCHEME = internKeyword("SCHEME"); 215 static final Symbol AUTHORITY = internKeyword("AUTHORITY"); 195 216 196 217 static final private String jarSeparator = "!/"; … … 231 252 } 232 253 } 233 254 234 255 // A JAR file 235 256 if (s.startsWith("jar:") && s.endsWith(jarSeparator)) { … … 303 324 type = p.type; 304 325 version = p.version; 326 return; 327 } 328 329 // A URL 330 if (isValidURL(s)) { 331 URL url = null; 332 try { 333 url = new URL(s); 334 } catch (MalformedURLException e) { 335 Debug.assertTrue(false); 336 } 337 String scheme = url.getProtocol(); 338 Debug.assertTrue(scheme != null); 339 String authority = url.getAuthority(); 340 Debug.assertTrue(authority != null); 341 342 host = NIL; 343 host = host.push(SCHEME); 344 host = host.push(new SimpleString(scheme)); 345 host = host.push(AUTHORITY); 346 host = host.push(new SimpleString(authority)); 347 host = host.nreverse(); 348 349 device = NIL; 350 351 // URI encode necessary characters 352 URI uri = null; 353 try { 354 uri = url.toURI().normalize(); 355 } catch (URISyntaxException e) { 356 error(new LispError("Could not URI escape characters in " 357 + "'" + url + "'" 358 + " because: " + e)); 359 } 360 361 String path = uri.getRawPath(); 362 if (path == null) { 363 path = ""; 364 } 365 String query = uri.getRawQuery(); 366 if (query != null) { 367 path += "?" + query; 368 } 369 String fragment = uri.getRawFragment(); 370 if (fragment != null) { 371 path += "#" + fragment; 372 } 373 Pathname p = new Pathname(path != null ? path : ""); 374 375 directory = p.directory; 376 name = p.name; 377 type = p.type; 378 305 379 return; 306 380 } … … 447 521 @Override 448 522 public LispObject typeOf() { 523 if (isURL()) { 524 return Symbol.URL_PATHNAME; 525 } 526 if (isJar()) { 527 return Symbol.JAR_PATHNAME; 528 } 449 529 return Symbol.PATHNAME; 450 530 } … … 452 532 @Override 453 533 public LispObject classOf() { 534 if (isURL()) { 535 return BuiltInClass.URL_PATHNAME; 536 } 537 if (isJar()) { 538 return BuiltInClass.JAR_PATHNAME; 539 } 454 540 return BuiltInClass.PATHNAME; 455 541 } … … 460 546 return T; 461 547 } 548 if (type == Symbol.JAR_PATHNAME && isJar()) { 549 return T; 550 } 551 if (type == Symbol.URL_PATHNAME && isURL()) { 552 return T; 553 } 462 554 if (type == BuiltInClass.PATHNAME) { 555 return T; 556 } 557 if (type == BuiltInClass.JAR_PATHNAME && isJar()) { 558 return T; 559 } 560 if (type == BuiltInClass.URL_PATHNAME && isURL()) { 463 561 return T; 464 562 } … … 487 585 // the namestring." 19.2.2.2.3.1 488 586 if (host != NIL) { 489 Debug.assertTrue(host instanceof AbstractString); 490 if (!(this instanceof LogicalPathname)) { 491 sb.append("\\\\"); //UNC file support; if there's a host, it's a UNC path. 492 } 493 sb.append(host.getStringValue()); 494 if (this instanceof LogicalPathname) { 495 sb.append(':'); 496 } else { 497 sb.append(File.separatorChar); 587 Debug.assertTrue(host instanceof AbstractString 588 || host instanceof Cons); 589 if (host instanceof Cons) { 590 LispObject scheme = Symbol.GETF.execute(host, SCHEME, NIL); 591 LispObject authority = Symbol.GETF.execute(host, AUTHORITY, NIL); 592 Debug.assertTrue(scheme != NIL); 593 sb.append(scheme.getStringValue()); 594 sb.append(":"); 595 if (authority != NIL) { 596 sb.append("//"); 597 sb.append(authority.getStringValue()); 598 } 599 } else { 600 if (!(this instanceof LogicalPathname)) { 601 sb.append("\\\\"); //UNC file support; if there's a host, it's a UNC path. 602 } 603 sb.append(host.getStringValue()); 604 if (this instanceof LogicalPathname) { 605 sb.append(':'); 606 } else { 607 sb.append(File.separatorChar); 608 } 498 609 } 499 610 } … … 583 694 } 584 695 } 585 return namestring = sb.toString(); 696 namestring = sb.toString(); 697 if (isURL()) { 698 namestring = Utilities.uriEncode(namestring); 699 } 700 return namestring; 586 701 } 587 702 … … 644 759 p.name = name; 645 760 p.type = type; 761 p.invalidateNamestring(); 646 762 String path = p.getNamestring(); 647 763 StringBuilder result = new StringBuilder(); … … 746 862 // We have a namestring. Check for pathname components that 747 863 // can't be read from the namestring. 748 if (host != NIL || version != NIL) { 864 if ((host != NIL && !isURL()) 865 || version != NIL) 866 { 749 867 useNamestring = false; 750 868 } else if (name instanceof AbstractString) { … … 829 947 } 830 948 949 public static boolean isValidURL(String s) { 950 try { 951 URL url = new URL(s); 952 } catch (MalformedURLException e) { 953 return false; 954 } 955 return true; 956 } 957 958 public static URL toURL(Pathname p) { 959 URL url = null; 960 if (!(p.host instanceof Cons)) { 961 Debug.assertTrue(false); // XXX 962 } 963 try { 964 url = new URL(p.getNamestring()); 965 } catch (MalformedURLException e) { 966 Debug.assertTrue(false); // XXX 967 } 968 return url; 969 } 970 971 URLConnection getURLConnection() { 972 Debug.assertTrue(isURL()); 973 URL url = Pathname.toURL(this); 974 URLConnection result = null; 975 try { 976 result = url.openConnection(); 977 } catch (IOException e) { 978 error(new FileError("Failed to open URL connection.", 979 this)); 980 } 981 return result; 982 } 983 831 984 public static Pathname parseNamestring(AbstractString namestring) { 832 985 // Check for a logical pathname host. 833 986 String s = namestring.getStringValue(); 834 String h = getHostString(s); 835 if (h != null && LOGICAL_PATHNAME_TRANSLATIONS.get(new SimpleString(h)) != null) { 836 // A defined logical pathname host. 837 return new LogicalPathname(h, s.substring(s.indexOf(':') + 1)); 987 if (!isValidURL(s)) { 988 String h = getHostString(s); 989 if (h != null && LOGICAL_PATHNAME_TRANSLATIONS.get(new SimpleString(h)) != null) { 990 // A defined logical pathname host. 991 return new LogicalPathname(h, s.substring(s.indexOf(':') + 1)); 992 } 838 993 } 839 994 return new Pathname(s); 840 995 } 841 996 842 public static Pathname parseNamestring(AbstractString namestring, 843 AbstractString host) { 844 // Look for a logical pathname host in the namestring. 997 // XXX was @return Pathname 998 public static LogicalPathname parseNamestring(AbstractString namestring, 999 AbstractString host) 1000 { 845 1001 String s = namestring.getStringValue(); 1002 1003 // Look for a logical pathname host in the namestring. 846 1004 String h = getHostString(s); 847 1005 if (h != null) { … … 1263 1421 } 1264 1422 case 1: 1265 return NIL; // ??? huh? -- ME 201002061423 return NIL; 1266 1424 default: 1267 1425 return error(new WrongNumberOfArgumentsException(this)); … … 1327 1485 } 1328 1486 return result; 1487 } 1488 1489 if (pathname.isURL()) { 1490 return error(new LispError("Unimplemented.")); // XXX 1329 1491 } 1330 1492 … … 1442 1604 1443 1605 public boolean isJar() { 1444 if (device instanceof Cons) { 1445 return true; 1446 } 1447 return false; 1606 return (device instanceof Cons); 1607 } 1608 1609 // ### PATHNAME-URL-P 1610 private static final Primitive PATHNAME_URL_P = new pf_pathname_url_p(); 1611 private static class pf_pathname_url_p extends Primitive { 1612 pf_pathname_url_p() { 1613 super("pathname-url-p", PACKAGE_SYS, true, "pathname", 1614 "Predicate for whether PATHNAME references a URL."); 1615 } 1616 @Override 1617 public LispObject execute(LispObject arg) { 1618 Pathname p = coerceToPathname(arg); 1619 return p.isURL() ? T : NIL; 1620 } 1621 } 1622 1623 public boolean isURL() { 1624 return (host instanceof Cons); 1448 1625 } 1449 1626 … … 1607 1784 result.directory = mergeDirectories(p.directory, d.directory); 1608 1785 } 1609 1610 // A JAR always has absolute directories1611 // if (result.isJar()1612 // && result.directory instanceof Cons1613 // && result.directory.car().equals(Keyword.ABSOLUTE)) {1614 // if (result.directory.cdr().equals(NIL)) {1615 // result.directory = NIL;1616 // } else {1617 // ((Cons)result.directory).car = Keyword.RELATIVE;1618 // }1619 // }1620 1786 1621 1787 if (pathname.name != NIL) { … … 1728 1894 pathname)); 1729 1895 } 1730 if (!(pathname. device instanceof Cons)) {1896 if (!(pathname.isJar() || pathname.isURL())) { 1731 1897 pathname 1732 1898 = mergePathnames(pathname, … … 1750 1916 return error(new FileError(e.getMessage(), pathname)); 1751 1917 } 1918 } 1919 } else if (pathname.isURL()) { 1920 if (pathname.getInputStream() != null) { 1921 return pathname; 1752 1922 } 1753 1923 } else … … 1886 2056 } 1887 2057 } 2058 } else if (isURL()) { 2059 URL url = toURL(this); 2060 try { 2061 result = url.openStream(); 2062 } catch (IOException e) { 2063 error(new FileError("Failed to get InputStream from " 2064 + "'" + Utilities.escapeFormat(getNamestring()) + "'" 2065 + ": " + e, 2066 this)); 2067 } 1888 2068 } else { 1889 2069 File file = Utilities.getFile(this); … … 1891 2071 result = new FileInputStream(file); 1892 2072 } catch (IOException e) { 1893 Debug.trace("Failed to get InputStream for readfrom "1894 + "'" + getNamestring() + "'"1895 + ": " + e);2073 error(new FileError("Failed to get InputStream from " 2074 + "'" + getNamestring() + "'" 2075 + ": " + e, this)); 1896 2076 } 1897 2077 } … … 1903 2083 */ 1904 2084 public long getLastModified() { 1905 if (!( device instanceof Cons)) {2085 if (!(isJar() || isURL())) { 1906 2086 File f = Utilities.getFile(this); 1907 2087 return f.lastModified(); 1908 2088 } 1909 // JAR cases 1910 // 0. JAR from URL 1911 // 1. JAR 1912 // 2. JAR in JAR 1913 // 3. Entry in JAR 1914 // 4. Entry in JAR in JAR 1915 String entryPath = asEntryPath(); 1916 Cons d = (Cons)device; 1917 if (d.cdr().equals(NIL)) { 1918 if (entryPath.length() == 0) { 1919 LispObject o = d.car(); 1920 if (o instanceof SimpleString) { 1921 // 0. JAR from URL 1922 // URL u = makeJarURL(o.getStringValue()); 1923 // XXX unimplemented 1924 Debug.assertTrue(false); 1925 // URLConnection c = null; 1926 // try { 1927 // c = u.openConnection(); 1928 // } catch(IOException e) { 1929 // Debug.trace("Failed to open Connection for URL " 1930 // + "'" + u + "'"); 1931 // return 0; 1932 // } 1933 // c.getLastModified(); 1934 } else { 1935 // 1. JAR 1936 return ((Pathname)o).getLastModified(); 1937 } 1938 } else { 1939 // 3. Entry in JAR 1940 final ZipEntry entry 1941 = ZipCache.get(device.car()).getEntry(entryPath); 1942 if (entry == null) { 1943 return 0; 1944 } 1945 final long time = entry.getTime(); 1946 if (time == -1) { 1947 return 0; 1948 } 1949 return time; 1950 } 1951 } else { 1952 ZipFile outerJar = ZipCache.get(d.car()); 1953 if (entryPath.length() == 0) { 1954 // 4. JAR in JAR 1955 String jarPath = ((Pathname)d.cdr()).asEntryPath(); 1956 final ZipEntry entry = outerJar.getEntry(jarPath); 1957 final long time = entry.getTime(); 1958 if (time == -1) { 1959 return 0; 1960 } 1961 return time; 1962 } else { 1963 // 5. Entry in JAR in JAR 1964 String innerJarPath = ((Pathname)d.cdr()).asEntryPath(); 1965 ZipEntry entry = outerJar.getEntry(entryPath); 1966 ZipInputStream innerJarInputStream 1967 = Utilities.getZipInputStream(outerJar, innerJarPath); 1968 ZipEntry innerEntry = Utilities.getEntry(innerJarInputStream, 1969 entryPath); 1970 long time = innerEntry.getTime(); 1971 if (time == -1) { 1972 return 0; 1973 } 1974 return time; 1975 } 2089 2090 if (isJar()) { 2091 // JAR cases 2092 // 0. JAR from URL 2093 // 1. JAR 2094 // 2. JAR in JAR 2095 // 3. Entry in JAR 2096 // 4. Entry in JAR in JAR 2097 String entryPath = asEntryPath(); 2098 Cons d = (Cons)device; 2099 if (d.cdr().equals(NIL)) { 2100 if (entryPath.length() == 0) { 2101 LispObject o = d.car(); 2102 if (o instanceof SimpleString) { 2103 // 0. JAR from URL 2104 // URL u = makeJarURL(o.getStringValue()); 2105 // XXX unimplemented 2106 Debug.assertTrue(false); 2107 // URLConnection c = null; 2108 // try { 2109 // c = u.openConnection(); 2110 // } catch(IOException e) { 2111 // Debug.trace("Failed to open Connection for URL " 2112 // + "'" + u + "'"); 2113 // return 0; 2114 // } 2115 // c.getLastModified(); 2116 } else { 2117 // 1. JAR 2118 return ((Pathname)o).getLastModified(); 2119 } 2120 } else { 2121 // 3. Entry in JAR 2122 final ZipEntry entry 2123 = ZipCache.get(device.car()).getEntry(entryPath); 2124 if (entry == null) { 2125 return 0; 2126 } 2127 final long time = entry.getTime(); 2128 if (time == -1) { 2129 return 0; 2130 } 2131 return time; 2132 } 2133 } else { 2134 ZipFile outerJar = ZipCache.get(d.car()); 2135 if (entryPath.length() == 0) { 2136 // 4. JAR in JAR 2137 String jarPath = ((Pathname)d.cdr()).asEntryPath(); 2138 final ZipEntry entry = outerJar.getEntry(jarPath); 2139 final long time = entry.getTime(); 2140 if (time == -1) { 2141 return 0; 2142 } 2143 return time; 2144 } else { 2145 // 5. Entry in JAR in JAR 2146 String innerJarPath = ((Pathname)d.cdr()).asEntryPath(); 2147 ZipEntry entry = outerJar.getEntry(entryPath); 2148 ZipInputStream innerJarInputStream 2149 = Utilities.getZipInputStream(outerJar, innerJarPath); 2150 ZipEntry innerEntry = Utilities.getEntry(innerJarInputStream, 2151 entryPath); 2152 long time = innerEntry.getTime(); 2153 if (time == -1) { 2154 return 0; 2155 } 2156 return time; 2157 } 2158 } 2159 } 2160 if (isURL()) { 2161 return getURLConnection().getLastModified(); 1976 2162 } 1977 2163 return 0; … … 1995 2181 coerceToPathname(Symbol.DEFAULT_PATHNAME_DEFAULTS.symbolValue()), 1996 2182 NIL); 2183 if (defaultedPathname.isURL() || defaultedPathname.isJar()) { 2184 return new FileError("Cannot mkdir with a " 2185 + (defaultedPathname.isURL() ? "URL" : "jar") 2186 + " Pathname.", 2187 defaultedPathname); 2188 } 2189 1997 2190 File file = Utilities.getFile(defaultedPathname); 1998 2191 return file.mkdir() ? T : NIL; … … 2089 2282 Symbol.DEFAULT_PATHNAME_DEFAULTS.setSymbolValue(coerceToPathname(obj)); 2090 2283 } 2284 2091 2285 } 2092 2286 -
trunk/abcl/src/org/armedbear/lisp/Symbol.java
r12598 r12607 3005 3005 public static final Symbol SET_SCHAR = 3006 3006 PACKAGE_SYS.addExternalSymbol("SET-SCHAR"); 3007 public static final Symbol JAR_STREAM = 3008 PACKAGE_SYS.addExternalSymbol("JAR-STREAM"); 3009 public static final Symbol URL_STREAM = 3010 PACKAGE_SYS.addExternalSymbol("URL-STREAM"); 3011 3007 3012 3008 3013 // Internal symbols in SYSTEM package. … … 3061 3066 public static final Symbol JAVA_STACK_FRAME = 3062 3067 PACKAGE_SYS.addInternalSymbol("JAVA-STACK-FRAME"); 3068 public static final Symbol JAR_PATHNAME = 3069 PACKAGE_SYS.addExternalSymbol("JAR-PATHNAME"); 3070 public static final Symbol URL_PATHNAME = 3071 PACKAGE_SYS.addExternalSymbol("URL-PATHNAME"); 3063 3072 3064 3073 // CDR6 -
trunk/abcl/src/org/armedbear/lisp/Utilities.java
r12441 r12607 41 41 import java.io.IOException; 42 42 import java.io.InputStream; 43 import java.net.URI; 44 import java.net.URISyntaxException; 43 45 import java.util.jar.JarFile; 44 46 import java.util.zip.ZipEntry; … … 254 256 255 257 256 258 static String uriEncode(String s) { 259 try { 260 URI uri = new URI("?" + s); 261 return uri.getQuery(); 262 } catch (URISyntaxException e) {} 263 return null; 264 } 265 266 static String uriDecode(String s) { 267 try { 268 URI uri = new URI(null, null, null, s, null); 269 return uri.toASCIIString().substring(1); 270 } catch (URISyntaxException e) {} 271 return null; // Error 272 } 273 274 static String escapeFormat(String s) { 275 return s.replace("~", "~~"); 276 } 257 277 } -
trunk/abcl/src/org/armedbear/lisp/ZipCache.java
r12531 r12607 112 112 return new ZipFile(f); 113 113 } catch (ZipException e) { 114 Debug.trace(e); // XXX 115 return null; 114 error(new FileError("Failed to construct ZipFile" 115 + " because " + e, 116 Pathname.makePathname(f))); 116 117 } catch (IOException e) { 117 Debug.trace(e); // XXX 118 return null; 118 error(new FileError("Failed to contruct ZipFile" 119 + " because " + e, 120 Pathname.makePathname(f))); 119 121 } 120 122 } else { … … 186 188 entry.file = new ZipFile(f); 187 189 } catch (ZipException e) { 188 Debug.trace(e); // XXX 189 return null; 190 error(new FileError("Failed to get cached ZipFile" 191 + " because " + e, 192 Pathname.makePathname(f))); 190 193 } catch (IOException e) { 191 Debug.trace(e); // XXX 192 return null; 194 error(new FileError("Failed to get cached ZipFile" 195 + " because " + e, 196 Pathname.makePathname(f))); 193 197 } 194 198 } else { … … 206 210 jarURL = new URL("jar:" + url + "!/"); 207 211 } catch (MalformedURLException e) { 208 Debug.trace(e); 209 Debug.assertTrue(false); // XXX 210 } 211 URLConnection connection; 212 error(new LispError("Failed to form a jar: URL from " 213 + "'" + url + "'" 214 + " because " + e)); 215 } 216 URLConnection connection = null; 212 217 try { 213 218 connection = jarURL.openConnection(); 214 } catch (IOException ex) { 215 Debug.trace("Failed to open " 216 + "'" + jarURL + "'"); 217 return null; 219 } catch (IOException e) { 220 error(new LispError("Failed to open " 221 + "'" + jarURL + "'" 222 + " with exception " 223 + e)); 218 224 } 219 225 if (!(connection instanceof JarURLConnection)) { 220 // XXX 221 Debug.trace("Could not get a URLConnection from " + jarURL); 222 return null; 226 error(new LispError("Could not get a URLConnection from " 227 + "'" + jarURL + "'")); 223 228 } 224 229 JarURLConnection jarURLConnection = (JarURLConnection) connection; … … 227 232 result.file = jarURLConnection.getJarFile(); 228 233 } catch (IOException e) { 229 Debug.trace(e); 230 Debug.assertTrue(false); // XXX 234 error(new LispError("Failed to fetch URL " 235 + "'" + jarURLConnection + "'" 236 + " because " + e)); 231 237 } 232 238 result.lastModified = jarURLConnection.getLastModified(); -
trunk/abcl/src/org/armedbear/lisp/pathnames.lisp
r12552 r12607 135 135 (unless (component-match-p (pathname-host pathname) (pathname-host wildcard) nil) 136 136 (return-from pathname-match-p nil)) 137 (when (and (pathname-jar-p pathname) 138 (pathname-jar-p wildcard)) 139 (unless 140 (every (lambda (value) (not (null value))) 141 (mapcar #'pathname-match-p 142 (pathname-device pathname) 143 (pathname-device wildcard))) 144 (return-from pathname-match-p nil))) 145 (when (or (and (pathname-jar-p pathname) 146 (not (pathname-jar-p wildcard))) 147 (and (not (pathname-jar-p pathname)) 148 (pathname-jar-p wildcard))) 149 (return-from pathname-match-p nil)) 137 150 (let* ((windows-p (featurep :windows)) 138 151 (ignore-case (or windows-p (typep pathname 'logical-pathname)))) 139 152 (cond ((and windows-p 153 (not (pathname-jar-p pathname)) 154 (not (pathname-jar-p wildcard)) 140 155 (not (component-match-p (pathname-device pathname) 141 156 (pathname-device wildcard) … … 196 211 (error "Unsupported wildcard pattern: ~S" to)))) 197 212 213 (defun translate-jar-device (source from to &optional case) 214 (declare (ignore case)) ; FIXME 215 (unless to 216 (return-from translate-jar-device nil)) 217 (when (not (= (length source) 218 (length from) 219 (length to))) 220 (error "Unsupported pathname translation for unequal jar ~ 221 references: ~S != ~S != ~S" source from to)) 222 (mapcar #'translate-pathname source from to)) 198 223 199 224 (defun translate-directory-components-aux (src from to case) … … 269 294 (device (if (typep 'to 'logical-pathname) 270 295 :unspecific 271 (translate-component (pathname-device source) 272 (pathname-device from) 273 (pathname-device to)))) 296 (if (pathname-jar-p source) 297 (translate-jar-device (pathname-device source) 298 (pathname-device from) 299 (pathname-device to)) 300 (translate-component (pathname-device source) 301 (pathname-device from) 302 (pathname-device to))))) 274 303 (case (and (typep source 'logical-pathname) 275 304 (or (featurep :unix) (featurep :windows)) … … 389 418 (cond ((eq host :unspecific) 390 419 (setf host nil)) 420 ((consp host)) ;; A URL 391 421 (host 392 422 (setf host (canonicalize-logical-host host)))) -
trunk/abcl/test/lisp/abcl/jar-file.lisp
r12531 r12607 321 321 (:absolute "c" "d") "foo" "lisp") 322 322 323 (deftest jar-file.pathname-match-p.1 324 (pathname-match-p "jar:file:/a/b/some.jar!/a/system/def.asd" 325 "jar:file:/**/*.jar!/**/*.asd") 326 t) 327 328 (deftest jar-file.pathname-match-p.2 329 (pathname-match-p "/a/system/def.asd" 330 "jar:file:/**/*.jar!/**/*.asd") 331 nil) 332 333 (deftest jar-file.pathname-match-p.3 334 (pathname-match-p "jar:file:/a/b/some.jar!/a/system/def.asd" 335 "/**/*.asd") 336 nil) 337 338 (deftest jar-file.translate-pathname.1 339 (namestring 340 (translate-pathname "jar:file:/a/b/c.jar!/d/e/f.lisp" 341 "jar:file:/**/*.jar!/**/*.*" 342 "/foo/**/*.*")) 343 "/foo/d/e/f.lisp") 344 345 ;; URL Pathname tests 346 (deftest pathname-url.1 347 (let* ((p #p"http://example.org/a/b/foo.lisp") 348 (host (pathname-host p))) 349 (values 350 (check-physical-pathname p '(:absolute "a" "b") "foo" "lisp") 351 (and (consp host) 352 (equal (getf host :scheme) 353 "http") 354 (equal (getf host :authority) 355 "example.org")))) 356 (t t)) 357 323 358 324 325 326 327 359 328 360 329 361
Note: See TracChangeset
for help on using the changeset viewer.