* Compares two strings for equality, ignoring case. The second * argument must contain only upper-case ASCII characters. * We don't want case comparison to be locale-dependent (else we * have the notorious "turkish i bug"). */ private boolean equalsIgnoreCase(String s, String upper) { assert upper.toUpperCase(java.util.Locale.ENGLISH).equals(upper); int len; if ((len = s.length()) != upper.length()) return false; for (int i = 0; i < len; i++) { char c1 = s.charAt(i); char c2 = upper.charAt(i); if (c1 != c2 && toUpperCaseASCII(c1) != c2) return false; } return true; } /** * Updates an existing jar file. */ boolean update(InputStream in, OutputStream out, InputStream newManifest, JarIndex jarIndex) throws IOException { ZipInputStream zis = new ZipInputStream(in); ZipOutputStream zos = new JarOutputStream(out); ZipEntry e = null; boolean foundManifest = false; boolean updateOk = true; if (jarIndex != null) { addIndex(jarIndex, zos); } // put the old entries first, replace if necessary while ((e = zis.getNextEntry()) != null) { String name = e.getName(); boolean isManifestEntry = equalsIgnoreCase(name, MANIFEST_NAME); if ((jarIndex != null && equalsIgnoreCase(name, INDEX_NAME)) || (Mflag && isManifestEntry)) { continue; } else if (isManifestEntry && ((newManifest != null) || (ename != null))) { foundManifest = true; if (newManifest != null) { // Don't read from the newManifest InputStream, as we // might need it below, and we can't re-read the same data // twice. FileInputStream fis = new FileInputStream(mname); boolean ambiguous = isAmbiguousMainClass(new Manifest(fis)); fis.close(); if (ambiguous) { return false; } } // Update the manifest. Manifest old = new Manifest(zis); if (newManifest != null) { old.read(newManifest); } updateManifest(old, zos); } else { if (!entryMap.containsKey(name)) { // copy the old stuff // do our own compression ZipEntry e2 = new ZipEntry(name); e2.setMethod(e.getMethod()); e2.setTime(e.getTime()); e2.setComment(e.getComment()); e2.setExtra(e.getExtra()); if (e.getMethod() == ZipEntry.STORED) { e2.setSize(e.getSize()); e2.setCrc(e.getCrc()); } zos.putNextEntry(e2); copy(zis, zos); } else { // replace with the new files File f = entryMap.get(name); addFile(zos, f); entryMap.remove(name); entries.remove(f); } } } // add the remaining new files for (File f: entries) { addFile(zos, f); } if (!foundManifest) { if (newManifest != null) { Manifest m = new Manifest(newManifest); updateOk = !isAmbiguousMainClass(m); if (updateOk) { updateManifest(m, zos); } } else if (ename != null) { updateManifest(new Manifest(), zos); } } zis.close(); zos.close(); return updateOk; } private void addIndex(JarIndex index, ZipOutputStream zos) throws IOException { ZipEntry e = new ZipEntry(INDEX_NAME); e.setTime(System.currentTimeMillis()); if (flag0) { CRC32OutputStream os = new CRC32OutputStream(); index.write(os); os.updateEntry(e); } zos.putNextEntry(e); index.write(zos); zos.closeEntry(); } private void updateManifest(Manifest m, ZipOutputStream zos) throws IOException { addVersion(m); addCreatedBy(m); if (ename != null) { addMainClass(m, ename); } ZipEntry e = new ZipEntry(MANIFEST_NAME); e.setTime(System.currentTimeMillis()); if (flag0) { crc32Manifest(e, m); } zos.putNextEntry(e); m.write(zos); if (vflag) { output(getMsg("out.update.manifest")); } } private String entryName(String name) { name = name.replace(File.separatorChar, '/'); String matchPath = ""; for (String path : paths) { if (name.startsWith(path) && (path.length() > matchPath.length())) { matchPath = path; } } name = name.substring(matchPath.length()); if (name.startsWith("/")) { name = name.substring(1); } else if (name.startsWith("./")) { name = name.substring(2); } return name; } private void addVersion(Manifest m) { Attributes global = m.getMainAttributes(); if (global.getValue(Attributes.Name.MANIFEST_VERSION) == null) { global.put(Attributes.Name.MANIFEST_VERSION, VERSION); } } private void addCreatedBy(Manifest m) { Attributes global = m.getMainAttributes(); if (global.getValue(new Attributes.Name("Created-By")) == null) { String javaVendor = System.getProperty("java.vendor"); String jdkVersion = System.getProperty("java.version"); global.put(new Attributes.Name("Created-By"), jdkVersion + " (" + javaVendor + ")"); } } private void addMainClass(Manifest m, String mainApp) { Attributes global = m.getMainAttributes(); // overrides any existing Main-Class attribute global.put(Attributes.Name.MAIN_CLASS, mainApp); } private boolean isAmbiguousMainClass(Manifest m) { if (ename != null) { Attributes global = m.getMainAttributes(); if ((global.get(Attributes.Name.MAIN_CLASS) != null)) { error(getMsg("error.bad.eflag")); usageError(); return true; } } return false; } /** * Adds a new file entry to the ZIP output stream. */ void addFile(ZipOutputStream zos, File file) throws IOException { String name = file.getPath(); boolean isDir = file.isDirectory(); if (isDir) { name = name.endsWith(File.separator) ? name : (name + File.separator); } name = entryName(name); if (name.equals("") || name.equals(".") || name.equals(zname)) { return; } else if ((name.equals(MANIFEST_DIR) || name.equals(MANIFEST_NAME)) && !Mflag) { if (vflag) { output(formatMsg("out.ignore.entry", name)); } return; } long size = isDir ? 0 : file.length(); if (vflag) { out.print(formatMsg("out.adding", name)); } ZipEntry e = new ZipEntry(name); e.setTime(file.lastModified()); if (size == 0) { e.setMethod(ZipEntry.STORED); e.setSize(0); e.setCrc(0); } else if (flag0) { crc32File(e, file); } zos.putNextEntry(e); if (!isDir) { copy(file, zos); } zos.closeEntry(); /* report how much compression occurred. */ if (vflag) { size = e.getSize(); long csize = e.getCompressedSize(); out.print(formatMsg2("out.size", String.valueOf(size), String.valueOf(csize))); if (e.getMethod() == ZipEntry.DEFLATED) { long ratio = 0; if (size != 0) { ratio = ((size - csize) * 100) / size; } output(formatMsg("out.deflated", String.valueOf(ratio))); } else { output(getMsg("out.stored")); } } } /** * A buffer for use only by copy(InputStream, OutputStream). * Not as clean as allocating a new buffer as needed by copy, * but significantly more efficient. */ private byte[] copyBuf = new byte[8192]; /** * Copies all bytes from the input stream to the output stream. * Does not close or flush either stream. * * @param from the input stream to read from * @param to the output stream to write to * @throws IOException if an I/O error occurs */ private void copy(InputStream from, OutputStream to) throws IOException { int n; while ((n = from.read(copyBuf)) != -1) to.write(copyBuf, 0, n); } /** * Copies all bytes from the input file to the output stream. * Does not close or flush the output stream. * * @param from the input file to read from * @param to the output stream to write to * @throws IOException if an I/O error occurs */ private void copy(File from, OutputStream to) throws IOException { InputStream in = new FileInputStream(from); try { copy(in, to); } finally { in.close(); } } /** * Copies all bytes from the input stream to the output file. * Does not close the input stream. * * @param from the input stream to read from * @param to the output file to write to * @throws IOException if an I/O error occurs */ private void copy(InputStream from, File to) throws IOException { OutputStream out = new FileOutputStream(to); try { copy(from, out); } finally { out.close(); } } /** * Computes the crc32 of a Manifest. This is necessary when the * ZipOutputStream is in STORED mode. */ private void crc32Manifest(ZipEntry e, Manifest m) throws IOException { CRC32OutputStream os = new CRC32OutputStream(); m.write(os); os.updateEntry(e); } /** * Computes the crc32 of a File. This is necessary when the * ZipOutputStream is in STORED mode. */ private void crc32File(ZipEntry e, File f) throws IOException { CRC32OutputStream os = new CRC32OutputStream(); copy(f, os); if (os.n != f.length()) { throw new JarException(formatMsg( "error.incorrect.length", f.getPath())); } os.updateEntry(e); } void replaceFSC(String files[]) { if (files != null) { for (String file : files) { file = file.replace(File.separatorChar, '/'); } } } @SuppressWarnings("serial") Set<ZipEntry> newDirSet() { return new HashSet<ZipEntry>() { public boolean add(ZipEntry e) { return ((e == null || useExtractionTime) ? false : super.add(e)); }}; } void updateLastModifiedTime(Set<ZipEntry> zes) throws IOException { for (ZipEntry ze : zes) { long lastModified = ze.getTime(); if (lastModified != -1) { File f = new File(ze.getName().replace('/', File.separatorChar)); f.setLastModified(lastModified); } } } /** * Extracts specified entries from JAR file. */ void extract(InputStream in, String files[]) throws IOException { ZipInputStream zis = new ZipInputStream(in); ZipEntry e; // Set of all directory entries specified in archive. Disallows // null entries. Disallows all entries if using pre-6.0 behavior. Set<ZipEntry> dirs = newDirSet(); while ((e = zis.getNextEntry()) != null) { if (files == null) { dirs.add(extractFile(zis, e)); } else { String name = e.getName(); for (String file : files) { if (name.startsWith(file)) { dirs.add(extractFile(zis, e)); break; } } } } // Update timestamps of directories specified in archive with their // timestamps as given in the archive. We do this after extraction, // instead of during, because creating a file in a directory changes // that directory's timestamp. updateLastModifiedTime(dirs); } /** * Extracts specified entries from JAR file, via ZipFile. */ void extract(String fname, String files[]) throws IOException { ZipFile zf = new ZipFile(fname); Set<ZipEntry> dirs = newDirSet(); Enumeration<? extends ZipEntry> zes = zf.entries(); while (zes.hasMoreElements()) { ZipEntry e = zes.nextElement(); InputStream is; if (files == null) { dirs.add(extractFile(zf.getInputStream(e), e)); } else { String name = e.getName(); for (String file : files) { if (name.startsWith(file)) { dirs.add(extractFile(zf.getInputStream(e), e)); break; } } } } zf.close(); updateLastModifiedTime(dirs); } /** * Extracts next entry from JAR file, creating directories as needed. If * the entry is for a directory which doesn't exist prior to this * invocation, returns that entry, otherwise returns null. */ ZipEntry extractFile(InputStream is, ZipEntry e) throws IOException { ZipEntry rc = null; String name = e.getName(); File f = new File(e.getName().replace('/', File.separatorChar)); if (e.isDirectory()) { if (f.exists()) { if (!f.isDirectory()) { throw new IOException(formatMsg("error.create.dir", f.getPath())); } } else { if (!f.mkdirs()) { throw new IOException(formatMsg("error.create.dir", f.getPath())); } else { rc = e; } } if (vflag) { output(formatMsg("out.create", name)); } } else { if (f.getParent() != null) { File d = new File(f.getParent()); if (!d.exists() && !d.mkdirs() || !d.isDirectory()) { throw new IOException(formatMsg( "error.create.dir", d.getPath())); } } try { copy(is, f); } finally { if (is instanceof ZipInputStream) ((ZipInputStream)is).closeEntry(); else is.close(); } if (vflag) { if (e.getMethod() == ZipEntry.DEFLATED) { output(formatMsg("out.inflated", name)); } else { output(formatMsg("out.extracted", name)); } } } if (!useExtractionTime) { long lastModified = e.getTime(); if (lastModified != -1) { f.setLastModified(lastModified); } } return rc; } /** * Lists contents of JAR file. */ void list(InputStream in, String files[]) throws IOException { ZipInputStream zis = new ZipInputStream(in); ZipEntry e; while ((e = zis.getNextEntry()) != null) { /* * In the case of a compressed (deflated) entry, the entry size * is stored immediately following the entry data and cannot be * determined until the entry is fully read. Therefore, we close * the entry first before printing out its attributes. */ zis.closeEntry(); printEntry(e, files); } } /**Lines 501–1000 of 1211. Previous lines Load more
* Compares two strings for equality, ignoring case. The second * argument must contain only upper-case ASCII characters. * We don't want case comparison to be locale-dependent (else we * have the notorious "turkish i bug"). */ private boolean equalsIgnoreCase(String s, String upper) { assert upper.toUpperCase(java.util.Locale.ENGLISH).equals(upper); int len; if ((len = s.length()) != upper.length()) return false; for (int i = 0; i < len; i++) { char c1 = s.charAt(i); char c2 = upper.charAt(i); if (c1 != c2 && toUpperCaseASCII(c1) != c2) return false; } return true; } /** * Updates an existing jar file. */ boolean update(InputStream in, OutputStream out, InputStream newManifest, JarIndex jarIndex) throws IOException { ZipInputStream zis = new ZipInputStream(in); ZipOutputStream zos = new JarOutputStream(out); ZipEntry e = null; boolean foundManifest = false; boolean updateOk = true; if (jarIndex != null) { addIndex(jarIndex, zos); } // put the old entries first, replace if necessary while ((e = zis.getNextEntry()) != null) { String name = e.getName(); boolean isManifestEntry = equalsIgnoreCase(name, MANIFEST_NAME); if ((jarIndex != null && equalsIgnoreCase(name, INDEX_NAME)) || (Mflag && isManifestEntry)) { continue; } else if (isManifestEntry && ((newManifest != null) || (ename != null))) { foundManifest = true; if (newManifest != null) { // Don't read from the newManifest InputStream, as we // might need it below, and we can't re-read the same data // twice. FileInputStream fis = new FileInputStream(mname); boolean ambiguous = isAmbiguousMainClass(new Manifest(fis)); fis.close(); if (ambiguous) { return false; } } // Update the manifest. Manifest old = new Manifest(zis); if (newManifest != null) { old.read(newManifest); } updateManifest(old, zos); } else { if (!entryMap.containsKey(name)) { // copy the old stuff // do our own compression ZipEntry e2 = new ZipEntry(name); e2.setMethod(e.getMethod()); e2.setTime(e.getTime()); e2.setComment(e.getComment()); e2.setExtra(e.getExtra()); if (e.getMethod() == ZipEntry.STORED) { e2.setSize(e.getSize()); e2.setCrc(e.getCrc()); } zos.putNextEntry(e2); copy(zis, zos); } else { // replace with the new files File f = entryMap.get(name); addFile(zos, f); entryMap.remove(name); entries.remove(f); } } } // add the remaining new files for (File f: entries) { addFile(zos, f); } if (!foundManifest) { if (newManifest != null) { Manifest m = new Manifest(newManifest); updateOk = !isAmbiguousMainClass(m); if (updateOk) { updateManifest(m, zos); } } else if (ename != null) { updateManifest(new Manifest(), zos); } } zis.close(); zos.close(); return updateOk; } private void addIndex(JarIndex index, ZipOutputStream zos) throws IOException { ZipEntry e = new ZipEntry(INDEX_NAME); e.setTime(System.currentTimeMillis()); if (flag0) { CRC32OutputStream os = new CRC32OutputStream(); index.write(os); os.updateEntry(e); } zos.putNextEntry(e); index.write(zos); zos.closeEntry(); } private void updateManifest(Manifest m, ZipOutputStream zos) throws IOException { addVersion(m); addCreatedBy(m); if (ename != null) { addMainClass(m, ename); } ZipEntry e = new ZipEntry(MANIFEST_NAME); e.setTime(System.currentTimeMillis()); if (flag0) { crc32Manifest(e, m); } zos.putNextEntry(e); m.write(zos); if (vflag) { output(getMsg("out.update.manifest")); } } private String entryName(String name) { name = name.replace(File.separatorChar, '/'); String matchPath = ""; for (String path : paths) { if (name.startsWith(path) && (path.length() > matchPath.length())) { matchPath = path; } } name = name.substring(matchPath.length()); if (name.startsWith("/")) { name = name.substring(1); } else if (name.startsWith("./")) { name = name.substring(2); } return name; } private void addVersion(Manifest m) { Attributes global = m.getMainAttributes(); if (global.getValue(Attributes.Name.MANIFEST_VERSION) == null) { global.put(Attributes.Name.MANIFEST_VERSION, VERSION); } } private void addCreatedBy(Manifest m) { Attributes global = m.getMainAttributes(); if (global.getValue(new Attributes.Name("Created-By")) == null) { String javaVendor = System.getProperty("java.vendor"); String jdkVersion = System.getProperty("java.version"); global.put(new Attributes.Name("Created-By"), jdkVersion + " (" + javaVendor + ")"); } } private void addMainClass(Manifest m, String mainApp) { Attributes global = m.getMainAttributes(); // overrides any existing Main-Class attribute global.put(Attributes.Name.MAIN_CLASS, mainApp); } private boolean isAmbiguousMainClass(Manifest m) { if (ename != null) { Attributes global = m.getMainAttributes(); if ((global.get(Attributes.Name.MAIN_CLASS) != null)) { error(getMsg("error.bad.eflag")); usageError(); return true; } } return false; } /** * Adds a new file entry to the ZIP output stream. */ void addFile(ZipOutputStream zos, File file) throws IOException { String name = file.getPath(); boolean isDir = file.isDirectory(); if (isDir) { name = name.endsWith(File.separator) ? name : (name + File.separator); } name = entryName(name); if (name.equals("") || name.equals(".") || name.equals(zname)) { return; } else if ((name.equals(MANIFEST_DIR) || name.equals(MANIFEST_NAME)) && !Mflag) { if (vflag) { output(formatMsg("out.ignore.entry", name)); } return; } long size = isDir ? 0 : file.length(); if (vflag) { out.print(formatMsg("out.adding", name)); } ZipEntry e = new ZipEntry(name); e.setTime(file.lastModified()); if (size == 0) { e.setMethod(ZipEntry.STORED); e.setSize(0); e.setCrc(0); } else if (flag0) { crc32File(e, file); } zos.putNextEntry(e); if (!isDir) { copy(file, zos); } zos.closeEntry(); /* report how much compression occurred. */ if (vflag) { size = e.getSize(); long csize = e.getCompressedSize(); out.print(formatMsg2("out.size", String.valueOf(size), String.valueOf(csize))); if (e.getMethod() == ZipEntry.DEFLATED) { long ratio = 0; if (size != 0) { ratio = ((size - csize) * 100) / size; } output(formatMsg("out.deflated", String.valueOf(ratio))); } else { output(getMsg("out.stored")); } } } /** * A buffer for use only by copy(InputStream, OutputStream). * Not as clean as allocating a new buffer as needed by copy, * but significantly more efficient. */ private byte[] copyBuf = new byte[8192]; /** * Copies all bytes from the input stream to the output stream. * Does not close or flush either stream. * * @param from the input stream to read from * @param to the output stream to write to * @throws IOException if an I/O error occurs */ private void copy(InputStream from, OutputStream to) throws IOException { int n; while ((n = from.read(copyBuf)) != -1) to.write(copyBuf, 0, n); } /** * Copies all bytes from the input file to the output stream. * Does not close or flush the output stream. * * @param from the input file to read from * @param to the output stream to write to * @throws IOException if an I/O error occurs */ private void copy(File from, OutputStream to) throws IOException { InputStream in = new FileInputStream(from); try { copy(in, to); } finally { in.close(); } } /** * Copies all bytes from the input stream to the output file. * Does not close the input stream. * * @param from the input stream to read from * @param to the output file to write to * @throws IOException if an I/O error occurs */ private void copy(InputStream from, File to) throws IOException { OutputStream out = new FileOutputStream(to); try { copy(from, out); } finally { out.close(); } } /** * Computes the crc32 of a Manifest. This is necessary when the * ZipOutputStream is in STORED mode. */ private void crc32Manifest(ZipEntry e, Manifest m) throws IOException { CRC32OutputStream os = new CRC32OutputStream(); m.write(os); os.updateEntry(e); } /** * Computes the crc32 of a File. This is necessary when the * ZipOutputStream is in STORED mode. */ private void crc32File(ZipEntry e, File f) throws IOException { CRC32OutputStream os = new CRC32OutputStream(); copy(f, os); if (os.n != f.length()) { throw new JarException(formatMsg( "error.incorrect.length", f.getPath())); } os.updateEntry(e); } void replaceFSC(String files[]) { if (files != null) { for (String file : files) { file = file.replace(File.separatorChar, '/'); } } } @SuppressWarnings("serial") Set<ZipEntry> newDirSet() { return new HashSet<ZipEntry>() { public boolean add(ZipEntry e) { return ((e == null || useExtractionTime) ? false : super.add(e)); }}; } void updateLastModifiedTime(Set<ZipEntry> zes) throws IOException { for (ZipEntry ze : zes) { long lastModified = ze.getTime(); if (lastModified != -1) { File f = new File(ze.getName().replace('/', File.separatorChar)); f.setLastModified(lastModified); } } } /** * Extracts specified entries from JAR file. */ void extract(InputStream in, String files[]) throws IOException { ZipInputStream zis = new ZipInputStream(in); ZipEntry e; // Set of all directory entries specified in archive. Disallows // null entries. Disallows all entries if using pre-6.0 behavior. Set<ZipEntry> dirs = newDirSet(); while ((e = zis.getNextEntry()) != null) { if (files == null) { dirs.add(extractFile(zis, e)); } else { String name = e.getName(); for (String file : files) { if (name.startsWith(file)) { dirs.add(extractFile(zis, e)); break; } } } } // Update timestamps of directories specified in archive with their // timestamps as given in the archive. We do this after extraction, // instead of during, because creating a file in a directory changes // that directory's timestamp. updateLastModifiedTime(dirs); } /** * Extracts specified entries from JAR file, via ZipFile. */ void extract(String fname, String files[]) throws IOException { ZipFile zf = new ZipFile(fname); Set<ZipEntry> dirs = newDirSet(); Enumeration<? extends ZipEntry> zes = zf.entries(); while (zes.hasMoreElements()) { ZipEntry e = zes.nextElement(); InputStream is; if (files == null) { dirs.add(extractFile(zf.getInputStream(e), e)); } else { String name = e.getName(); for (String file : files) { if (name.startsWith(file)) { dirs.add(extractFile(zf.getInputStream(e), e)); break; } } } } zf.close(); updateLastModifiedTime(dirs); } /** * Extracts next entry from JAR file, creating directories as needed. If * the entry is for a directory which doesn't exist prior to this * invocation, returns that entry, otherwise returns null. */ ZipEntry extractFile(InputStream is, ZipEntry e) throws IOException { ZipEntry rc = null; String name = e.getName(); File f = new File(e.getName().replace('/', File.separatorChar)); if (e.isDirectory()) { if (f.exists()) { if (!f.isDirectory()) { throw new IOException(formatMsg("error.create.dir", f.getPath())); } } else { if (!f.mkdirs()) { throw new IOException(formatMsg("error.create.dir", f.getPath())); } else { rc = e; } } if (vflag) { output(formatMsg("out.create", name)); } } else { if (f.getParent() != null) { File d = new File(f.getParent()); if (!d.exists() && !d.mkdirs() || !d.isDirectory()) { throw new IOException(formatMsg( "error.create.dir", d.getPath())); } } try { copy(is, f); } finally { if (is instanceof ZipInputStream) ((ZipInputStream)is).closeEntry(); else is.close(); } if (vflag) { if (e.getMethod() == ZipEntry.DEFLATED) { output(formatMsg("out.inflated", name)); } else { output(formatMsg("out.extracted", name)); } } } if (!useExtractionTime) { long lastModified = e.getTime(); if (lastModified != -1) { f.setLastModified(lastModified); } } return rc; } /** * Lists contents of JAR file. */ void list(InputStream in, String files[]) throws IOException { ZipInputStream zis = new ZipInputStream(in); ZipEntry e; while ((e = zis.getNextEntry()) != null) { /* * In the case of a compressed (deflated) entry, the entry size * is stored immediately following the entry data and cannot be * determined until the entry is fully read. Therefore, we close * the entry first before printing out its attributes. */ zis.closeEntry(); printEntry(e, files); } } /**