aboutsummaryrefslogtreecommitdiff
path: root/test/java/util/zip/zip.java
diff options
context:
space:
mode:
authorsherman <none@none>2009-04-16 21:00:42 -0700
committersherman <none@none>2009-04-16 21:00:42 -0700
commit52c91937e008186676c76c5f8aedb25cf6c8d760 (patch)
tree1f7daacd03089784c3734486775f255c57786a5e /test/java/util/zip/zip.java
parent5ecf45cc8cdb3ee5151f06cde25d7157a7de2081 (diff)
downloadjdk8u_jdk-52c91937e008186676c76c5f8aedb25cf6c8d760.tar.gz
4244499: ZipEntry() does not convert filenames from Unicode to platform
4532049: IllegalArgumentException in ZipInputStream while reading unicode file 5030283: Incorrect implementation of UTF-8 in zip package 4700978: ZipFile can't treat Japanese name in a zipfile properly 4980042: Cannot use Surrogates in zip file metadata like filenames 4820807: java.util.zip.ZipInputStream cannot extract files with Chinese chars in name Summary: Add new constructors for zip classes to support non-UTF-8 encoded names/comments in ZIP file Reviewed-by: alanb, martin
Diffstat (limited to 'test/java/util/zip/zip.java')
-rw-r--r--test/java/util/zip/zip.java743
1 files changed, 743 insertions, 0 deletions
diff --git a/test/java/util/zip/zip.java b/test/java/util/zip/zip.java
new file mode 100644
index 0000000000..33eccf9496
--- /dev/null
+++ b/test/java/util/zip/zip.java
@@ -0,0 +1,743 @@
+/*
+ * Copyright 2009 Sun Microsystems, Inc. All Rights Reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+import java.io.*;
+import java.nio.charset.Charset;
+import java.util.*;
+import java.util.zip.*;
+import java.text.MessageFormat;
+
+/**
+ * A stripped-down version of Jar tool with a "-encoding" option to
+ * support non-UTF8 encoidng for entry name and comment.
+ */
+public class zip {
+ String program;
+ PrintStream out, err;
+ String fname;
+ String zname = "";
+ String[] files;
+ Charset cs = Charset.forName("UTF-8");
+
+ Map<String, File> entryMap = new HashMap<String, File>();
+ Set<File> entries = new LinkedHashSet<File>();
+ List<String> paths = new ArrayList<String>();
+
+ CRC32 crc32 = new CRC32();
+ /*
+ * cflag: create
+ * uflag: update
+ * xflag: xtract
+ * tflag: table
+ * vflag: verbose
+ * flag0: no zip compression (store only)
+ */
+ boolean cflag, uflag, xflag, tflag, vflag, flag0;
+
+ private static ResourceBundle rsrc;
+ static {
+ try {
+ // just use the jar message
+ rsrc = ResourceBundle.getBundle("sun.tools.jar.resources.jar");
+ } catch (MissingResourceException e) {
+ throw new Error("Fatal: Resource for jar is missing");
+ }
+ }
+
+ public zip(PrintStream out, PrintStream err, String program) {
+ this.out = out;
+ this.err = err;
+ this.program = program;
+ }
+
+ private boolean ok;
+
+ public synchronized boolean run(String args[]) {
+ ok = true;
+ if (!parseArgs(args)) {
+ return false;
+ }
+ try {
+ if (cflag || uflag) {
+ if (fname != null) {
+ zname = fname.replace(File.separatorChar, '/');
+ if (zname.startsWith("./")) {
+ zname = zname.substring(2);
+ }
+ }
+ }
+ if (cflag) {
+ OutputStream out;
+ if (fname != null) {
+ out = new FileOutputStream(fname);
+ } else {
+ out = new FileOutputStream(FileDescriptor.out);
+ if (vflag) {
+ vflag = false;
+ }
+ }
+ expand(null, files, false);
+ create(new BufferedOutputStream(out, 4096));
+ out.close();
+ } else if (uflag) {
+ File inputFile = null, tmpFile = null;
+ FileInputStream in;
+ FileOutputStream out;
+ if (fname != null) {
+ inputFile = new File(fname);
+ String path = inputFile.getParent();
+ tmpFile = File.createTempFile("tmp", null,
+ new File((path == null) ? "." : path));
+ in = new FileInputStream(inputFile);
+ out = new FileOutputStream(tmpFile);
+ } else {
+ in = new FileInputStream(FileDescriptor.in);
+ out = new FileOutputStream(FileDescriptor.out);
+ vflag = false;
+ }
+ expand(null, files, true);
+ boolean updateOk = update(in, new BufferedOutputStream(out));
+ if (ok) {
+ ok = updateOk;
+ }
+ in.close();
+ out.close();
+ if (fname != null) {
+ inputFile.delete();
+ if (!tmpFile.renameTo(inputFile)) {
+ tmpFile.delete();
+ throw new IOException(getMsg("error.write.file"));
+ }
+ tmpFile.delete();
+ }
+ } else if (tflag) {
+ replaceFSC(files);
+ if (fname != null) {
+ list(fname, files);
+ } else {
+ InputStream in = new FileInputStream(FileDescriptor.in);
+ try{
+ list(new BufferedInputStream(in), files);
+ } finally {
+ in.close();
+ }
+ }
+ } else if (xflag) {
+ replaceFSC(files);
+ if (fname != null && files != null) {
+ extract(fname, files);
+ } else {
+ InputStream in = (fname == null)
+ ? new FileInputStream(FileDescriptor.in)
+ : new FileInputStream(fname);
+ try {
+ extract(new BufferedInputStream(in), files);
+ } finally {
+ in.close();
+ }
+ }
+ }
+ } catch (IOException e) {
+ fatalError(e);
+ ok = false;
+ } catch (Error ee) {
+ ee.printStackTrace();
+ ok = false;
+ } catch (Throwable t) {
+ t.printStackTrace();
+ ok = false;
+ }
+ out.flush();
+ err.flush();
+ return ok;
+ }
+
+
+ boolean parseArgs(String args[]) {
+ try {
+ args = parse(args);
+ } catch (FileNotFoundException e) {
+ fatalError(formatMsg("error.cant.open", e.getMessage()));
+ return false;
+ } catch (IOException e) {
+ fatalError(e);
+ return false;
+ }
+ int count = 1;
+ try {
+ String flags = args[0];
+ if (flags.startsWith("-")) {
+ flags = flags.substring(1);
+ }
+ for (int i = 0; i < flags.length(); i++) {
+ switch (flags.charAt(i)) {
+ case 'c':
+ if (xflag || tflag || uflag) {
+ usageError();
+ return false;
+ }
+ cflag = true;
+ break;
+ case 'u':
+ if (cflag || xflag || tflag) {
+ usageError();
+ return false;
+ }
+ uflag = true;
+ break;
+ case 'x':
+ if (cflag || uflag || tflag) {
+ usageError();
+ return false;
+ }
+ xflag = true;
+ break;
+ case 't':
+ if (cflag || uflag || xflag) {
+ usageError();
+ return false;
+ }
+ tflag = true;
+ break;
+ case 'v':
+ vflag = true;
+ break;
+ case 'f':
+ fname = args[count++];
+ break;
+ case '0':
+ flag0 = true;
+ break;
+ default:
+ error(formatMsg("error.illegal.option",
+ String.valueOf(flags.charAt(i))));
+ usageError();
+ return false;
+ }
+ }
+ } catch (ArrayIndexOutOfBoundsException e) {
+ usageError();
+ return false;
+ }
+ if (!cflag && !tflag && !xflag && !uflag) {
+ error(getMsg("error.bad.option"));
+ usageError();
+ return false;
+ }
+ /* parse file arguments */
+ int n = args.length - count;
+ if (n > 0) {
+ int k = 0;
+ String[] nameBuf = new String[n];
+ try {
+ for (int i = count; i < args.length; i++) {
+ if (args[i].equals("-encoding")) {
+ cs = Charset.forName(args[++i]);
+ } else if (args[i].equals("-C")) {
+ /* change the directory */
+ String dir = args[++i];
+ dir = (dir.endsWith(File.separator) ?
+ dir : (dir + File.separator));
+ dir = dir.replace(File.separatorChar, '/');
+ while (dir.indexOf("//") > -1) {
+ dir = dir.replace("//", "/");
+ }
+ paths.add(dir.replace(File.separatorChar, '/'));
+ nameBuf[k++] = dir + args[++i];
+ } else {
+ nameBuf[k++] = args[i];
+ }
+ }
+ } catch (ArrayIndexOutOfBoundsException e) {
+ e.printStackTrace();
+ usageError();
+ return false;
+ }
+ if (k != 0) {
+ files = new String[k];
+ System.arraycopy(nameBuf, 0, files, 0, k);
+ }
+ } else if (cflag || uflag) {
+ error(getMsg("error.bad.uflag"));
+ usageError();
+ return false;
+ }
+ return true;
+ }
+
+ void expand(File dir, String[] files, boolean isUpdate) {
+ if (files == null) {
+ return;
+ }
+ for (int i = 0; i < files.length; i++) {
+ File f;
+ if (dir == null) {
+ f = new File(files[i]);
+ } else {
+ f = new File(dir, files[i]);
+ }
+ if (f.isFile()) {
+ if (entries.add(f)) {
+ if (isUpdate)
+ entryMap.put(entryName(f.getPath()), f);
+ }
+ } else if (f.isDirectory()) {
+ if (entries.add(f)) {
+ if (isUpdate) {
+ String dirPath = f.getPath();
+ dirPath = (dirPath.endsWith(File.separator)) ? dirPath :
+ (dirPath + File.separator);
+ entryMap.put(entryName(dirPath), f);
+ }
+ expand(f, f.list(), isUpdate);
+ }
+ } else {
+ error(formatMsg("error.nosuch.fileordir", String.valueOf(f)));
+ ok = false;
+ }
+ }
+ }
+
+ void create(OutputStream out) throws IOException
+ {
+ ZipOutputStream zos = new ZipOutputStream(out, cs);
+ if (flag0) {
+ zos.setMethod(ZipOutputStream.STORED);
+ }
+ for (File file: entries) {
+ addFile(zos, file);
+ }
+ zos.close();
+ }
+
+ boolean update(InputStream in, OutputStream out) throws IOException
+ {
+ ZipInputStream zis = new ZipInputStream(in, cs);
+ ZipOutputStream zos = new ZipOutputStream(out, cs);
+ ZipEntry e = null;
+ byte[] buf = new byte[1024];
+ int n = 0;
+ boolean updateOk = true;
+
+ // put the old entries first, replace if necessary
+ while ((e = zis.getNextEntry()) != null) {
+ String name = e.getName();
+ 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);
+ while ((n = zis.read(buf, 0, buf.length)) != -1) {
+ zos.write(buf, 0, n);
+ }
+ } 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);
+ }
+ zis.close();
+ zos.close();
+ return updateOk;
+ }
+
+ 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;
+ }
+
+ 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;
+ }
+
+ 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) {
+ e.setSize(size);
+ e.setMethod(ZipEntry.STORED);
+ crc32File(e, file);
+ }
+ zos.putNextEntry(e);
+ if (!isDir) {
+ byte[] buf = new byte[8192];
+ int len;
+ InputStream is = new BufferedInputStream(new FileInputStream(file));
+ while ((len = is.read(buf, 0, buf.length)) != -1) {
+ zos.write(buf, 0, len);
+ }
+ is.close();
+ }
+ 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"));
+ }
+ }
+ }
+
+ private void crc32File(ZipEntry e, File f) throws IOException {
+ InputStream is = new BufferedInputStream(new FileInputStream(f));
+ byte[] buf = new byte[8192];
+ crc32.reset();
+ int r = 0;
+ int nread = 0;
+ long len = f.length();
+ while ((r = is.read(buf)) != -1) {
+ nread += r;
+ crc32.update(buf, 0, r);
+ }
+ is.close();
+ if (nread != (int) len) {
+ throw new ZipException(formatMsg(
+ "error.incorrect.length", f.getPath()));
+ }
+ e.setCrc(crc32.getValue());
+ }
+
+ void replaceFSC(String files[]) {
+ if (files != null) {
+ for (String file : files) {
+ file = file.replace(File.separatorChar, '/');
+ }
+ }
+ }
+
+ Set<ZipEntry> newDirSet() {
+ return new HashSet<ZipEntry>() {
+ public boolean add(ZipEntry e) {
+ return (e == null || 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);
+ }
+ }
+ }
+
+ void extract(InputStream in, String files[]) throws IOException {
+ ZipInputStream zis = new ZipInputStream(in, cs);
+ ZipEntry e;
+ 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;
+ }
+ }
+ }
+ }
+ updateLastModifiedTime(dirs);
+ }
+
+ void extract(String fname, String files[]) throws IOException {
+ ZipFile zf = new ZipFile(fname, cs);
+ 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);
+ }
+
+ 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()));
+ }
+ }
+ OutputStream os = new FileOutputStream(f);
+ byte[] b = new byte[8192];
+ int len;
+ try {
+ while ((len = is.read(b, 0, b.length)) != -1) {
+ os.write(b, 0, len);
+ }
+ } finally {
+ if (is instanceof ZipInputStream)
+ ((ZipInputStream)is).closeEntry();
+ else
+ is.close();
+ os.close();
+ }
+ if (vflag) {
+ if (e.getMethod() == ZipEntry.DEFLATED) {
+ output(formatMsg("out.inflated", name));
+ } else {
+ output(formatMsg("out.extracted", name));
+ }
+ }
+ }
+ long lastModified = e.getTime();
+ if (lastModified != -1) {
+ f.setLastModified(lastModified);
+ }
+ return rc;
+ }
+
+ void list(InputStream in, String files[]) throws IOException {
+ ZipInputStream zis = new ZipInputStream(in, cs);
+ ZipEntry e;
+ while ((e = zis.getNextEntry()) != null) {
+ zis.closeEntry();
+ printEntry(e, files);
+ }
+ }
+
+ void list(String fname, String files[]) throws IOException {
+ ZipFile zf = new ZipFile(fname, cs);
+ Enumeration<? extends ZipEntry> zes = zf.entries();
+ while (zes.hasMoreElements()) {
+ printEntry(zes.nextElement(), files);
+ }
+ zf.close();
+ }
+
+ void printEntry(ZipEntry e, String[] files) throws IOException {
+ if (files == null) {
+ printEntry(e);
+ } else {
+ String name = e.getName();
+ for (String file : files) {
+ if (name.startsWith(file)) {
+ printEntry(e);
+ return;
+ }
+ }
+ }
+ }
+
+ void printEntry(ZipEntry e) throws IOException {
+ if (vflag) {
+ StringBuilder sb = new StringBuilder();
+ String s = Long.toString(e.getSize());
+ for (int i = 6 - s.length(); i > 0; --i) {
+ sb.append(' ');
+ }
+ sb.append(s).append(' ').append(new Date(e.getTime()).toString());
+ sb.append(' ').append(e.getName());
+ output(sb.toString());
+ } else {
+ output(e.getName());
+ }
+ }
+
+ void usageError() {
+ error(
+ "Usage: zip {ctxu}[vf0] [zip-file] [-encoding encname][-C dir] files ...\n" +
+ "Options:\n" +
+ " -c create new archive\n" +
+ " -t list table of contents for archive\n" +
+ " -x extract named (or all) files from archive\n" +
+ " -u update existing archive\n" +
+ " -v generate verbose output on standard output\n" +
+ " -f specify archive file name\n" +
+ " -0 store only; use no ZIP compression\n" +
+ " -C change to the specified directory and include the following file\n" +
+ "If any file is a directory then it is processed recursively.\n");
+ }
+
+ void fatalError(Exception e) {
+ e.printStackTrace();
+ }
+
+
+ void fatalError(String s) {
+ error(program + ": " + s);
+ }
+
+
+ protected void output(String s) {
+ out.println(s);
+ }
+
+ protected void error(String s) {
+ err.println(s);
+ }
+
+ private String getMsg(String key) {
+ try {
+ return (rsrc.getString(key));
+ } catch (MissingResourceException e) {
+ throw new Error("Error in message file");
+ }
+ }
+
+ private String formatMsg(String key, String arg) {
+ String msg = getMsg(key);
+ String[] args = new String[1];
+ args[0] = arg;
+ return MessageFormat.format(msg, (Object[]) args);
+ }
+
+ private String formatMsg2(String key, String arg, String arg1) {
+ String msg = getMsg(key);
+ String[] args = new String[2];
+ args[0] = arg;
+ args[1] = arg1;
+ return MessageFormat.format(msg, (Object[]) args);
+ }
+
+ public static String[] parse(String[] args) throws IOException
+ {
+ ArrayList<String> newArgs = new ArrayList<String>(args.length);
+ for (int i = 0; i < args.length; i++) {
+ String arg = args[i];
+ if (arg.length() > 1 && arg.charAt(0) == '@') {
+ arg = arg.substring(1);
+ if (arg.charAt(0) == '@') {
+ newArgs.add(arg);
+ } else {
+ loadCmdFile(arg, newArgs);
+ }
+ } else {
+ newArgs.add(arg);
+ }
+ }
+ return newArgs.toArray(new String[newArgs.size()]);
+ }
+
+ private static void loadCmdFile(String name, List<String> args) throws IOException
+ {
+ Reader r = new BufferedReader(new FileReader(name));
+ StreamTokenizer st = new StreamTokenizer(r);
+ st.resetSyntax();
+ st.wordChars(' ', 255);
+ st.whitespaceChars(0, ' ');
+ st.commentChar('#');
+ st.quoteChar('"');
+ st.quoteChar('\'');
+ while (st.nextToken() != st.TT_EOF) {
+ args.add(st.sval);
+ }
+ r.close();
+ }
+
+ public static void main(String args[]) {
+ zip z = new zip(System.out, System.err, "zip");
+ System.exit(z.run(args) ? 0 : 1);
+ }
+}
+