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
--- a/jdk/make/java/zip/FILES_c.gmk Thu Apr 16 11:16:40 2009 +0800
+++ b/jdk/make/java/zip/FILES_c.gmk Thu Apr 16 21:00:42 2009 -0700
@@ -29,7 +29,6 @@
Deflater.c \
Inflater.c \
ZipFile.c \
- ZipEntry.c \
zadler32.c \
zcrc32.c \
deflate.c \
--- a/jdk/make/java/zip/mapfile-vers Thu Apr 16 11:16:40 2009 +0800
+++ b/jdk/make/java/zip/mapfile-vers Thu Apr 16 21:00:42 2009 -0700
@@ -50,15 +50,17 @@
Java_java_util_zip_Inflater_initIDs;
Java_java_util_zip_Inflater_reset;
Java_java_util_zip_Inflater_setDictionary;
- Java_java_util_zip_ZipEntry_initFields;
- Java_java_util_zip_ZipEntry_initIDs;
Java_java_util_zip_ZipFile_close;
Java_java_util_zip_ZipFile_freeEntry;
- Java_java_util_zip_ZipFile_getCSize;
Java_java_util_zip_ZipFile_getEntry;
- Java_java_util_zip_ZipFile_getMethod;
+ Java_java_util_zip_ZipFile_getEntryBytes;
+ Java_java_util_zip_ZipFile_getEntryCrc;
+ Java_java_util_zip_ZipFile_getEntryCSize;
+ Java_java_util_zip_ZipFile_getEntryFlag;
+ Java_java_util_zip_ZipFile_getEntryMethod;
+ Java_java_util_zip_ZipFile_getEntrySize;
+ Java_java_util_zip_ZipFile_getEntryTime;
Java_java_util_zip_ZipFile_getNextEntry;
- Java_java_util_zip_ZipFile_getSize;
Java_java_util_zip_ZipFile_getZipMessage;
Java_java_util_zip_ZipFile_getTotal;
Java_java_util_zip_ZipFile_initIDs;
--- a/jdk/make/java/zip/reorder-i586 Thu Apr 16 11:16:40 2009 +0800
+++ b/jdk/make/java/zip/reorder-i586 Thu Apr 16 21:00:42 2009 -0700
@@ -20,12 +20,14 @@
text: .text%Java_java_util_zip_ZipFile_open;
text: .text%Java_java_util_zip_ZipFile_getTotal;
text: .text%Java_java_util_zip_ZipFile_getEntry;
-text: .text%Java_java_util_zip_ZipEntry_initIDs;
-text: .text%Java_java_util_zip_ZipEntry_initFields;
text: .text%Java_java_util_zip_ZipFile_freeEntry;
-text: .text%Java_java_util_zip_ZipFile_getCSize;
-text: .text%Java_java_util_zip_ZipFile_getSize;
-text: .text%Java_java_util_zip_ZipFile_getMethod;
+text: .text%Java_java_util_zip_ZipFile_getEntryTime;
+text: .text%Java_java_util_zip_ZipFile_getEntryCrc;
+text: .text%Java_java_util_zip_ZipFile_getEntryCSize;
+text: .text%Java_java_util_zip_ZipFile_getEntrySize;
+text: .text%Java_java_util_zip_ZipFile_getEntryFlag;
+text: .text%Java_java_util_zip_ZipFile_getEntryMethod;
+text: .text%Java_java_util_zip_ZipFile_getEntryBytes;
text: .text%Java_java_util_zip_Inflater_initIDs;
text: .text%Java_java_util_zip_Inflater_init;
text: .text%inflateInit2_;
--- a/jdk/make/java/zip/reorder-sparc Thu Apr 16 11:16:40 2009 +0800
+++ b/jdk/make/java/zip/reorder-sparc Thu Apr 16 21:00:42 2009 -0700
@@ -19,12 +19,14 @@
text: .text%Java_java_util_zip_ZipFile_open;
text: .text%Java_java_util_zip_ZipFile_getTotal;
text: .text%Java_java_util_zip_ZipFile_getEntry;
-text: .text%Java_java_util_zip_ZipEntry_initIDs;
-text: .text%Java_java_util_zip_ZipEntry_initFields;
text: .text%Java_java_util_zip_ZipFile_freeEntry;
-text: .text%Java_java_util_zip_ZipFile_getCSize;
-text: .text%Java_java_util_zip_ZipFile_getSize;
-text: .text%Java_java_util_zip_ZipFile_getMethod;
+text: .text%Java_java_util_zip_ZipFile_getEntryTime;
+text: .text%Java_java_util_zip_ZipFile_getEntryCrc;
+text: .text%Java_java_util_zip_ZipFile_getEntryCSize;
+text: .text%Java_java_util_zip_ZipFile_getEntrySize;
+text: .text%Java_java_util_zip_ZipFile_getEntryFlag;
+text: .text%Java_java_util_zip_ZipFile_getEntryMethod;
+text: .text%Java_java_util_zip_ZipFile_getEntryBytes;
text: .text%Java_java_util_zip_Inflater_initIDs;
text: .text%Java_java_util_zip_Inflater_init;
text: .text%inflateInit2_;
--- a/jdk/make/java/zip/reorder-sparcv9 Thu Apr 16 11:16:40 2009 +0800
+++ b/jdk/make/java/zip/reorder-sparcv9 Thu Apr 16 21:00:42 2009 -0700
@@ -20,12 +20,14 @@
text: .text%Java_java_util_zip_ZipFile_open;
text: .text%Java_java_util_zip_ZipFile_getTotal;
text: .text%Java_java_util_zip_ZipFile_getEntry;
-text: .text%Java_java_util_zip_ZipEntry_initIDs;
-text: .text%Java_java_util_zip_ZipEntry_initFields;
text: .text%Java_java_util_zip_ZipFile_freeEntry;
-text: .text%Java_java_util_zip_ZipFile_getCSize;
-text: .text%Java_java_util_zip_ZipFile_getSize;
-text: .text%Java_java_util_zip_ZipFile_getMethod;
+text: .text%Java_java_util_zip_ZipFile_getEntryTime;
+text: .text%Java_java_util_zip_ZipFile_getEntryCrc;
+text: .text%Java_java_util_zip_ZipFile_getEntryCSize;
+text: .text%Java_java_util_zip_ZipFile_getEntrySize;
+text: .text%Java_java_util_zip_ZipFile_getEntryFlag;
+text: .text%Java_java_util_zip_ZipFile_getEntryMethod;
+text: .text%Java_java_util_zip_ZipFile_getEntryBytes;
text: .text%Java_java_util_zip_Inflater_initIDs;
text: .text%Java_java_util_zip_Inflater_init;
text: .text%inflateInit2_;
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/share/classes/java/util/zip/ZipCoder.java Thu Apr 16 21:00:42 2009 -0700
@@ -0,0 +1,139 @@
+/*
+ * 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.
+ */
+
+package java.util.zip;
+
+import java.nio.ByteBuffer;
+import java.nio.CharBuffer;
+import java.nio.charset.Charset;
+import java.nio.charset.CharsetDecoder;
+import java.nio.charset.CharsetEncoder;
+import java.nio.charset.CoderResult;
+import java.nio.charset.CodingErrorAction;
+import java.util.Arrays;
+
+/**
+ * Utility class for zipfile name and comment decoding and encoding
+ */
+
+final class ZipCoder {
+
+ String toString(byte[] ba, int length) {
+ CharsetDecoder cd = decoder().reset();
+ int len = (int)(length * cd.maxCharsPerByte());
+ char[] ca = new char[len];
+ if (len == 0)
+ return new String(ca);
+ ByteBuffer bb = ByteBuffer.wrap(ba, 0, length);
+ CharBuffer cb = CharBuffer.wrap(ca);
+ CoderResult cr = cd.decode(bb, cb, true);
+ if (!cr.isUnderflow())
+ throw new IllegalArgumentException(cr.toString());
+ cr = cd.flush(cb);
+ if (!cr.isUnderflow())
+ throw new IllegalArgumentException(cr.toString());
+ return new String(ca, 0, cb.position());
+ }
+
+ String toString(byte[] ba) {
+ return toString(ba, ba.length);
+ }
+
+ byte[] getBytes(String s) {
+ CharsetEncoder ce = encoder().reset();
+ char[] ca = s.toCharArray();
+ int len = (int)(ca.length * ce.maxBytesPerChar());
+ byte[] ba = new byte[len];
+ if (len == 0)
+ return ba;
+ ByteBuffer bb = ByteBuffer.wrap(ba);
+ CharBuffer cb = CharBuffer.wrap(ca);
+ CoderResult cr = ce.encode(cb, bb, true);
+ if (!cr.isUnderflow())
+ throw new IllegalArgumentException(cr.toString());
+ cr = ce.flush(bb);
+ if (!cr.isUnderflow())
+ throw new IllegalArgumentException(cr.toString());
+ if (bb.position() == ba.length) // defensive copy?
+ return ba;
+ else
+ return Arrays.copyOf(ba, bb.position());
+ }
+
+ // assume invoked only if "this" is not utf8
+ byte[] getBytesUTF8(String s) {
+ if (isutf8)
+ return getBytes(s);
+ if (utf8 == null)
+ utf8 = new ZipCoder(Charset.forName("UTF-8"));
+ return utf8.getBytes(s);
+ }
+
+
+ String toStringUTF8(byte[] ba, int len) {
+ if (isutf8)
+ return toString(ba, len);
+ if (utf8 == null)
+ utf8 = new ZipCoder(Charset.forName("UTF-8"));
+ return utf8.toString(ba, len);
+ }
+
+ boolean isUTF8() {
+ return isutf8;
+ }
+
+ private Charset cs;
+ private CharsetDecoder dec;
+ private CharsetEncoder enc;
+ private boolean isutf8;
+ private ZipCoder utf8;
+
+ private ZipCoder(Charset cs) {
+ this.cs = cs;
+ this.isutf8 = cs.name().equals("UTF-8");
+ }
+
+ static ZipCoder get(Charset charset) {
+ return new ZipCoder(charset);
+ }
+
+ private CharsetDecoder decoder() {
+ if (dec == null) {
+ dec = cs.newDecoder()
+ .onMalformedInput(CodingErrorAction.REPORT)
+ .onUnmappableCharacter(CodingErrorAction.REPORT);
+ }
+ return dec;
+ }
+
+ private CharsetEncoder encoder() {
+ if (enc == null) {
+ enc = cs.newEncoder()
+ .onMalformedInput(CodingErrorAction.REPORT)
+ .onUnmappableCharacter(CodingErrorAction.REPORT);
+ }
+ return enc;
+ }
+}
--- a/jdk/src/share/classes/java/util/zip/ZipConstants64.java Thu Apr 16 11:16:40 2009 +0800
+++ b/jdk/src/share/classes/java/util/zip/ZipConstants64.java Thu Apr 16 21:00:42 2009 -0700
@@ -73,5 +73,12 @@
static final int ZIP64_EXTSIZ = 8; // compressed size, 8-byte
static final int ZIP64_EXTLEN = 16; // uncompressed size, 8-byte
+ /*
+ * Language encoding flag EFS
+ */
+ static final int EFS = 0x800; // If this bit is set the filename and
+ // comment fields for this file must be
+ // encoded using UTF-8.
+
private ZipConstants64() {}
}
--- a/jdk/src/share/classes/java/util/zip/ZipEntry.java Thu Apr 16 11:16:40 2009 +0800
+++ b/jdk/src/share/classes/java/util/zip/ZipEntry.java Thu Apr 16 21:00:42 2009 -0700
@@ -40,6 +40,7 @@
long size = -1; // uncompressed size of entry data
long csize = -1; // compressed size of entry data
int method = -1; // compression method
+ int flag = 0; // general purpose flag
byte[] extra; // optional extra field data for entry
String comment; // optional comment string for entry
@@ -53,13 +54,6 @@
*/
public static final int DEFLATED = 8;
- static {
- /* Zip library is loaded from System.initializeSystemClass */
- initIDs();
- }
-
- private static native void initIDs();
-
/**
* Creates a new zip entry with the specified name.
*
@@ -90,28 +84,15 @@
size = e.size;
csize = e.csize;
method = e.method;
+ flag = e.flag;
extra = e.extra;
comment = e.comment;
}
/*
- * Creates a new zip entry for the given name with fields initialized
- * from the specified jzentry data.
+ * Creates a new un-initialized zip entry
*/
- ZipEntry(String name, long jzentry) {
- this.name = name;
- initFields(jzentry);
- }
-
- private native void initFields(long jzentry);
-
- /*
- * Creates a new zip entry with fields initialized from the specified
- * jzentry data.
- */
- ZipEntry(long jzentry) {
- initFields(jzentry);
- }
+ ZipEntry() {}
/**
* Returns the name of the entry.
@@ -258,16 +239,16 @@
/**
* Sets the optional comment string for the entry.
+ *
+ * <p>ZIP entry comments have maximum length of 0xffff. If the length of the
+ * specified comment string is greater than 0xFFFF bytes after encoding, only
+ * the first 0xFFFF bytes are output to the ZIP file entry.
+ *
* @param comment the comment string
- * @exception IllegalArgumentException if the length of the specified
- * comment string is greater than 0xFFFF bytes
+ *
* @see #getComment()
*/
public void setComment(String comment) {
- if (comment != null && comment.length() > 0xffff/3
- && ZipOutputStream.getUTF8Length(comment) > 0xffff) {
- throw new IllegalArgumentException("invalid entry comment length");
- }
this.comment = comment;
}
--- a/jdk/src/share/classes/java/util/zip/ZipFile.java Thu Apr 16 11:16:40 2009 +0800
+++ b/jdk/src/share/classes/java/util/zip/ZipFile.java Thu Apr 16 21:00:42 2009 -0700
@@ -29,9 +29,11 @@
import java.io.IOException;
import java.io.EOFException;
import java.io.File;
+import java.nio.charset.Charset;
import java.util.Vector;
import java.util.Enumeration;
import java.util.NoSuchElementException;
+import static java.util.zip.ZipConstants64.*;
/**
* This class is used to read entries from a zip file.
@@ -76,16 +78,19 @@
/**
* Opens a zip file for reading.
*
- * <p>First, if there is a security
- * manager, its <code>checkRead</code> method
- * is called with the <code>name</code> argument
- * as its argument to ensure the read is allowed.
+ * <p>First, if there is a security manager, its <code>checkRead</code>
+ * method is called with the <code>name</code> argument as its argument
+ * to ensure the read is allowed.
+ *
+ * <p>The UTF-8 {@link java.nio.charset.Charset charset} is used to
+ * decode the entry names and comments.
*
* @param name the name of the zip file
* @throws ZipException if a ZIP format error has occurred
* @throws IOException if an I/O error has occurred
* @throws SecurityException if a security manager exists and its
* <code>checkRead</code> method doesn't allow read access to the file.
+ *
* @see SecurityManager#checkRead(java.lang.String)
*/
public ZipFile(String name) throws IOException {
@@ -101,6 +106,9 @@
* method is called with the <code>name</code> argument as its argument to
* ensure the read is allowed.
*
+ * <p>The UTF-8 {@link java.nio.charset.Charset charset} is used to
+ * decode the entry names and comments
+ *
* @param file the ZIP file to be opened for reading
* @param mode the mode in which the file is to be opened
* @throws ZipException if a ZIP format error has occurred
@@ -115,6 +123,59 @@
* @since 1.3
*/
public ZipFile(File file, int mode) throws IOException {
+ this(file, mode, Charset.forName("UTF-8"));
+ }
+
+ /**
+ * Opens a ZIP file for reading given the specified File object.
+ *
+ * <p>The UTF-8 {@link java.nio.charset.Charset charset} is used to
+ * decode the entry names and comments.
+ *
+ * @param file the ZIP file to be opened for reading
+ * @throws ZipException if a ZIP format error has occurred
+ * @throws IOException if an I/O error has occurred
+ */
+ public ZipFile(File file) throws ZipException, IOException {
+ this(file, OPEN_READ);
+ }
+
+ private ZipCoder zc;
+
+ /**
+ * Opens a new <code>ZipFile</code> to read from the specified
+ * <code>File</code> object in the specified mode. The mode argument
+ * must be either <tt>OPEN_READ</tt> or <tt>OPEN_READ | OPEN_DELETE</tt>.
+ *
+ * <p>First, if there is a security manager, its <code>checkRead</code>
+ * method is called with the <code>name</code> argument as its argument to
+ * ensure the read is allowed.
+ *
+ * @param file the ZIP file to be opened for reading
+ * @param mode the mode in which the file is to be opened
+ * @param charset
+ * the {@link java.nio.charset.Charset {@code charset}} to
+ * be used to decode the ZIP entry name and comment that are not
+ * encoded by using UTF-8 encoding (indicated by entry's general
+ * purpose flag).
+ *
+ * @throws ZipException if a ZIP format error has occurred
+ * @throws IOException if an I/O error has occurred
+ *
+ * @throws SecurityException
+ * if a security manager exists and its <code>checkRead</code>
+ * method doesn't allow read access to the file,or its
+ * <code>checkDelete</code> method doesn't allow deleting the
+ * file when the <tt>OPEN_DELETE</tt> flag is set
+ *
+ * @throws IllegalArgumentException if the <tt>mode</tt> argument is invalid
+ *
+ * @see SecurityManager#checkRead(java.lang.String)
+ *
+ * @since 1.7
+ */
+ public ZipFile(File file, int mode, Charset charset) throws IOException
+ {
if (((mode & OPEN_READ) == 0) ||
((mode & ~(OPEN_READ | OPEN_DELETE)) != 0)) {
throw new IllegalArgumentException("Illegal mode: 0x"+
@@ -128,24 +189,61 @@
sm.checkDelete(name);
}
}
+ if (charset == null)
+ throw new NullPointerException("charset is null");
+ this.zc = ZipCoder.get(charset);
jzfile = open(name, mode, file.lastModified());
-
this.name = name;
this.total = getTotal(jzfile);
}
- private static native long open(String name, int mode, long lastModified);
- private static native int getTotal(long jzfile);
-
+ /**
+ * Opens a zip file for reading.
+ *
+ * <p>First, if there is a security manager, its <code>checkRead</code>
+ * method is called with the <code>name</code> argument as its argument
+ * to ensure the read is allowed.
+ *
+ * @param name the name of the zip file
+ * @param charset
+ * the {@link java.nio.charset.Charset {@code charset}} to
+ * be used to decode the ZIP entry name and comment that are not
+ * encoded by using UTF-8 encoding (indicated by entry's general
+ * purpose flag).
+ *
+ * @throws ZipException if a ZIP format error has occurred
+ * @throws IOException if an I/O error has occurred
+ * @throws SecurityException
+ * if a security manager exists and its <code>checkRead</code>
+ * method doesn't allow read access to the file
+ *
+ * @see SecurityManager#checkRead(java.lang.String)
+ *
+ * @since 1.7
+ */
+ public ZipFile(String name, Charset charset) throws IOException
+ {
+ this(new File(name), OPEN_READ, charset);
+ }
/**
* Opens a ZIP file for reading given the specified File object.
* @param file the ZIP file to be opened for reading
- * @throws ZipException if a ZIP error has occurred
+ * @param charset
+ * The {@link java.nio.charset.Charset {@code charset}} to be
+ * used to decode the ZIP entry name and comment (ignored if
+ * the <a href="package-summary.html#lang_encoding"> language
+ * encoding bit</a> of the ZIP entry's general purpose bit
+ * flag is set).
+ *
+ * @throws ZipException if a ZIP format error has occurred
* @throws IOException if an I/O error has occurred
+ *
+ * @since 1.7
*/
- public ZipFile(File file) throws ZipException, IOException {
- this(file, OPEN_READ);
+ public ZipFile(File file, Charset charset) throws IOException
+ {
+ this(file, OPEN_READ, charset);
}
/**
@@ -163,9 +261,9 @@
long jzentry = 0;
synchronized (this) {
ensureOpen();
- jzentry = getEntry(jzfile, name, true);
+ jzentry = getEntry(jzfile, zc.getBytes(name), true);
if (jzentry != 0) {
- ZipEntry ze = new ZipEntry(name, jzentry);
+ ZipEntry ze = getZipEntry(name, jzentry);
freeEntry(jzfile, jzentry);
return ze;
}
@@ -173,7 +271,7 @@
return null;
}
- private static native long getEntry(long jzfile, String name,
+ private static native long getEntry(long jzfile, byte[] name,
boolean addSlash);
// freeEntry releases the C jzentry struct.
@@ -194,36 +292,30 @@
* @throws IllegalStateException if the zip file has been closed
*/
public InputStream getInputStream(ZipEntry entry) throws IOException {
- return getInputStream(entry.name);
- }
-
- /**
- * Returns an input stream for reading the contents of the specified
- * entry, or null if the entry was not found.
- */
- private InputStream getInputStream(String name) throws IOException {
- if (name == null) {
- throw new NullPointerException("name");
+ if (entry == null) {
+ throw new NullPointerException("entry");
}
long jzentry = 0;
ZipFileInputStream in = null;
synchronized (this) {
ensureOpen();
- jzentry = getEntry(jzfile, name, false);
+ if (!zc.isUTF8() && (entry.flag & EFS) != 0) {
+ jzentry = getEntry(jzfile, zc.getBytesUTF8(entry.name), false);
+ } else {
+ jzentry = getEntry(jzfile, zc.getBytes(entry.name), false);
+ }
if (jzentry == 0) {
return null;
}
-
in = new ZipFileInputStream(jzentry);
-
}
final ZipFileInputStream zfin = in;
- switch (getMethod(jzentry)) {
+ switch (getEntryMethod(jzentry)) {
case STORED:
return zfin;
case DEFLATED:
// MORE: Compute good size for inflater stream:
- long size = getSize(jzentry) + 2; // Inflater likes a bit of slack
+ long size = getEntrySize(jzentry) + 2; // Inflater likes a bit of slack
if (size > 65536) size = 8192;
if (size <= 0) size = 4096;
return new InflaterInputStream(zfin, getInflater(), (int)size) {
@@ -267,8 +359,6 @@
}
}
- private static native int getMethod(long jzentry);
-
/*
* Gets an inflater from the list of available inflaters or allocates
* a new one.
@@ -343,7 +433,7 @@
",\n message = " + message
);
}
- ZipEntry ze = new ZipEntry(jzentry);
+ ZipEntry ze = getZipEntry(null, jzentry);
freeEntry(jzfile, jzentry);
return ze;
}
@@ -351,6 +441,38 @@
};
}
+ private ZipEntry getZipEntry(String name, long jzentry) {
+ ZipEntry e = new ZipEntry();
+ e.flag = getEntryFlag(jzentry); // get the flag first
+ if (name != null) {
+ e.name = name;
+ } else {
+ byte[] bname = getEntryBytes(jzentry, JZENTRY_NAME);
+ if (!zc.isUTF8() && (e.flag & EFS) != 0) {
+ e.name = zc.toStringUTF8(bname, bname.length);
+ } else {
+ e.name = zc.toString(bname, bname.length);
+ }
+ }
+ e.time = getEntryTime(jzentry);
+ e.crc = getEntryCrc(jzentry);
+ e.size = getEntrySize(jzentry);
+ e. csize = getEntryCSize(jzentry);
+ e.method = getEntryMethod(jzentry);
+ e.extra = getEntryBytes(jzentry, JZENTRY_EXTRA);
+ byte[] bcomm = getEntryBytes(jzentry, JZENTRY_COMMENT);
+ if (bcomm == null) {
+ e.comment = null;
+ } else {
+ if (!zc.isUTF8() && (e.flag & EFS) != 0) {
+ e.comment = zc.toStringUTF8(bcomm, bcomm.length);
+ } else {
+ e.comment = zc.toString(bcomm, bcomm.length);
+ }
+ }
+ return e;
+ }
+
private static native long getNextEntry(long jzfile, int i);
/**
@@ -443,8 +565,8 @@
ZipFileInputStream(long jzentry) {
pos = 0;
- rem = getCSize(jzentry);
- size = getSize(jzentry);
+ rem = getEntryCSize(jzentry);
+ size = getEntrySize(jzentry);
this.jzentry = jzentry;
}
@@ -514,13 +636,25 @@
}
+
+ private static native long open(String name, int mode, long lastModified)
+ throws IOException;
+ private static native int getTotal(long jzfile);
private static native int read(long jzfile, long jzentry,
long pos, byte[] b, int off, int len);
- private static native long getCSize(long jzentry);
+ // access to the native zentry object
+ private static native long getEntryTime(long jzentry);
+ private static native long getEntryCrc(long jzentry);
+ private static native long getEntryCSize(long jzentry);
+ private static native long getEntrySize(long jzentry);
+ private static native int getEntryMethod(long jzentry);
+ private static native int getEntryFlag(long jzentry);
- private static native long getSize(long jzentry);
+ private static final int JZENTRY_NAME = 0;
+ private static final int JZENTRY_EXTRA = 1;
+ private static final int JZENTRY_COMMENT = 2;
+ private static native byte[] getEntryBytes(long jzentry, int type);
- // Temporary add on for bug troubleshooting
private static native String getZipMessage(long jzfile);
}
--- a/jdk/src/share/classes/java/util/zip/ZipInputStream.java Thu Apr 16 11:16:40 2009 +0800
+++ b/jdk/src/share/classes/java/util/zip/ZipInputStream.java Thu Apr 16 21:00:42 2009 -0700
@@ -29,6 +29,7 @@
import java.io.IOException;
import java.io.EOFException;
import java.io.PushbackInputStream;
+import java.nio.charset.Charset;
import static java.util.zip.ZipConstants64.*;
/**
@@ -54,6 +55,8 @@
// one entry
private boolean entryEOF = false;
+ private ZipCoder zc;
+
/**
* Check to make sure that this stream has not been closed
*/
@@ -65,14 +68,39 @@
/**
* Creates a new ZIP input stream.
+ *
+ * <p>The UTF-8 {@link java.nio.charset.Charset charset} is used to
+ * decode the entry names.
+ *
* @param in the actual input stream
*/
public ZipInputStream(InputStream in) {
+ this(in, Charset.forName("UTF-8"));
+ }
+
+ /**
+ * Creates a new ZIP input stream.
+ *
+ * @param in the actual input stream
+ *
+ * @param charset
+ * The {@link java.nio.charset.Charset {@code charset}} to be
+ * used to decode the ZIP entry name (ignored if the
+ * <a href="package-summary.html#lang_encoding"> language
+ * encoding bit</a> of the ZIP entry's general purpose bit
+ * flag is set).
+ *
+ * @since 1.7
+ */
+ public ZipInputStream(InputStream in, Charset charset) {
super(new PushbackInputStream(in, 512), new Inflater(true), 512);
usesDefaultInflater = true;
if(in == null) {
throw new NullPointerException("in is null");
}
+ if (charset == null)
+ throw new NullPointerException("charset is null");
+ this.zc = ZipCoder.get(charset);
}
/**
@@ -141,8 +169,8 @@
* @param len the maximum number of bytes read
* @return the actual number of bytes read, or -1 if the end of the
* entry is reached
- * @exception NullPointerException If <code>b</code> is <code>null</code>.
- * @exception IndexOutOfBoundsException If <code>off</code> is negative,
+ * @exception NullPointerException if <code>b</code> is <code>null</code>.
+ * @exception IndexOutOfBoundsException if <code>off</code> is negative,
* <code>len</code> is negative, or <code>len</code> is greater than
* <code>b.length - off</code>
* @exception ZipException if a ZIP file error has occurred
@@ -252,6 +280,8 @@
if (get32(tmpbuf, 0) != LOCSIG) {
return null;
}
+ // get flag first, we need check EFS.
+ flag = get16(tmpbuf, LOCFLG);
// get the entry name and create the ZipEntry first
int len = get16(tmpbuf, LOCNAM);
int blen = b.length;
@@ -262,9 +292,11 @@
b = new byte[blen];
}
readFully(b, 0, len);
- ZipEntry e = createZipEntry(getUTF8String(b, 0, len));
+ // Force to use UTF-8 if the EFS bit is ON, even the cs is NOT UTF-8
+ ZipEntry e = createZipEntry(((flag & EFS) != 0)
+ ? zc.toStringUTF8(b, len)
+ : zc.toString(b, len));
// now get the remaining fields for the entry
- flag = get16(tmpbuf, LOCFLG);
if ((flag & 1) == 1) {
throw new ZipException("encrypted ZIP entry not supported");
}
@@ -313,71 +345,6 @@
return e;
}
- /*
- * Fetches a UTF8-encoded String from the specified byte array.
- */
- private static String getUTF8String(byte[] b, int off, int len) {
- // First, count the number of characters in the sequence
- int count = 0;
- int max = off + len;
- int i = off;
- while (i < max) {
- int c = b[i++] & 0xff;
- switch (c >> 4) {
- case 0: case 1: case 2: case 3: case 4: case 5: case 6: case 7:
- // 0xxxxxxx
- count++;
- break;
- case 12: case 13:
- // 110xxxxx 10xxxxxx
- if ((b[i++] & 0xc0) != 0x80) {
- throw new IllegalArgumentException();
- }
- count++;
- break;
- case 14:
- // 1110xxxx 10xxxxxx 10xxxxxx
- if (((b[i++] & 0xc0) != 0x80) ||
- ((b[i++] & 0xc0) != 0x80)) {
- throw new IllegalArgumentException();
- }
- count++;
- break;
- default:
- // 10xxxxxx, 1111xxxx
- throw new IllegalArgumentException();
- }
- }
- if (i != max) {
- throw new IllegalArgumentException();
- }
- // Now decode the characters...
- char[] cs = new char[count];
- i = 0;
- while (off < max) {
- int c = b[off++] & 0xff;
- switch (c >> 4) {
- case 0: case 1: case 2: case 3: case 4: case 5: case 6: case 7:
- // 0xxxxxxx
- cs[i++] = (char)c;
- break;
- case 12: case 13:
- // 110xxxxx 10xxxxxx
- cs[i++] = (char)(((c & 0x1f) << 6) | (b[off++] & 0x3f));
- break;
- case 14:
- // 1110xxxx 10xxxxxx 10xxxxxx
- int t = (b[off++] & 0x3f) << 6;
- cs[i++] = (char)(((c & 0x0f) << 12) | t | (b[off++] & 0x3f));
- break;
- default:
- // 10xxxxxx, 1111xxxx
- throw new IllegalArgumentException();
- }
- }
- return new String(cs, 0, count);
- }
-
/**
* Creates a new <code>ZipEntry</code> object for the specified
* entry name.
--- a/jdk/src/share/classes/java/util/zip/ZipOutputStream.java Thu Apr 16 11:16:40 2009 +0800
+++ b/jdk/src/share/classes/java/util/zip/ZipOutputStream.java Thu Apr 16 21:00:42 2009 -0700
@@ -27,6 +27,7 @@
import java.io.OutputStream;
import java.io.IOException;
+import java.nio.charset.Charset;
import java.util.Vector;
import java.util.HashSet;
import static java.util.zip.ZipConstants64.*;
@@ -44,19 +45,9 @@
private static class XEntry {
public final ZipEntry entry;
public final long offset;
- public final int flag;
public XEntry(ZipEntry entry, long offset) {
this.entry = entry;
this.offset = offset;
- this.flag = (entry.method == DEFLATED &&
- (entry.size == -1 ||
- entry.csize == -1 ||
- entry.crc == -1))
- // store size, compressed size, and crc-32 in data descriptor
- // immediately following the compressed entry data
- ? 8
- // store size, compressed size, and crc-32 in LOC header
- : 0;
}
}
@@ -66,12 +57,14 @@
private CRC32 crc = new CRC32();
private long written = 0;
private long locoff = 0;
- private String comment;
+ private byte[] comment;
private int method = DEFLATED;
private boolean finished;
private boolean closed = false;
+ private final ZipCoder zc;
+
private static int version(ZipEntry e) throws ZipException {
switch (e.method) {
case DEFLATED: return 20;
@@ -100,10 +93,31 @@
/**
* Creates a new ZIP output stream.
+ *
+ * <p>The UTF-8 {@link java.nio.charset.Charset charset} is used
+ * to encode the entry names and comments.
+ *
* @param out the actual output stream
*/
public ZipOutputStream(OutputStream out) {
+ this(out, Charset.forName("UTF-8"));
+ }
+
+ /**
+ * Creates a new ZIP output stream.
+ *
+ * @param out the actual output stream
+ *
+ * @param charset the {@link java.nio.charset.Charset </code>charset<code>}
+ * to be used to encode the entry names and comments
+ *
+ * @since 1.7
+ */
+ public ZipOutputStream(OutputStream out, Charset charset) {
super(out, new Deflater(Deflater.DEFAULT_COMPRESSION, true));
+ if (charset == null)
+ throw new NullPointerException("charset is null");
+ this.zc = ZipCoder.get(charset);
usesDefaultDeflater = true;
}
@@ -114,11 +128,11 @@
* ZIP file comment is greater than 0xFFFF bytes
*/
public void setComment(String comment) {
- if (comment != null && comment.length() > 0xffff/3
- && getUTF8Length(comment) > 0xffff) {
- throw new IllegalArgumentException("ZIP file comment too long.");
+ if (comment != null) {
+ this.comment = zc.getBytes(comment);
+ if (this.comment.length > 0xffff)
+ throw new IllegalArgumentException("ZIP file comment too long.");
}
- this.comment = comment;
}
/**
@@ -167,8 +181,15 @@
if (e.method == -1) {
e.method = method; // use default method
}
+ // store size, compressed size, and crc-32 in LOC header
+ e.flag = 0;
switch (e.method) {
case DEFLATED:
+ // store size, compressed size, and crc-32 in data descriptor
+ // immediately following the compressed entry data
+ if (e.size == -1 || e.csize == -1 || e.crc == -1)
+ e.flag = 8;
+
break;
case STORED:
// compressed size, uncompressed size, and crc-32 must all be
@@ -192,6 +213,8 @@
if (! names.add(e.name)) {
throw new ZipException("duplicate entry: " + e.name);
}
+ if (zc.isUTF8())
+ e.flag |= EFS;
current = new XEntry(e, written);
xentries.add(current);
writeLOC(current);
@@ -213,7 +236,7 @@
while (!def.finished()) {
deflate();
}
- if ((current.flag & 8) == 0) {
+ if ((e.flag & 8) == 0) {
// verify size, compressed size, and crc-32 settings
if (e.size != def.getBytesRead()) {
throw new ZipException(
@@ -343,11 +366,11 @@
*/
private void writeLOC(XEntry xentry) throws IOException {
ZipEntry e = xentry.entry;
- int flag = xentry.flag;
+ int flag = e.flag;
int elen = (e.extra != null) ? e.extra.length : 0;
boolean hasZip64 = false;
- writeInt(LOCSIG); // LOC header signature
+ writeInt(LOCSIG); // LOC header signature
if ((flag & 8) == 8) {
writeShort(version(e)); // version needed to extract
@@ -380,7 +403,7 @@
writeInt(e.size); // uncompressed size
}
}
- byte[] nameBytes = getUTF8Bytes(e.name);
+ byte[] nameBytes = zc.getBytes(e.name);
writeShort(nameBytes.length);
writeShort(elen);
writeBytes(nameBytes, 0, nameBytes.length);
@@ -417,7 +440,7 @@
*/
private void writeCEN(XEntry xentry) throws IOException {
ZipEntry e = xentry.entry;
- int flag = xentry.flag;
+ int flag = e.flag;
int version = version(e);
long csize = e.csize;
@@ -454,7 +477,7 @@
writeInt(e.crc); // crc-32
writeInt(csize); // compressed size
writeInt(size); // uncompressed size
- byte[] nameBytes = getUTF8Bytes(e.name);
+ byte[] nameBytes = zc.getBytes(e.name);
writeShort(nameBytes.length);
if (hasZip64) {
// + headid(2) + datasize(2)
@@ -464,8 +487,8 @@
}
byte[] commentBytes;
if (e.comment != null) {
- commentBytes = getUTF8Bytes(e.comment);
- writeShort(commentBytes.length);
+ commentBytes = zc.getBytes(e.comment);
+ writeShort(Math.min(commentBytes.length, 0xffff));
} else {
commentBytes = null;
writeShort(0);
@@ -489,7 +512,7 @@
writeBytes(e.extra, 0, e.extra.length);
}
if (commentBytes != null) {
- writeBytes(commentBytes, 0, commentBytes.length);
+ writeBytes(commentBytes, 0, Math.min(commentBytes.length, 0xffff));
}
}
@@ -541,9 +564,8 @@
writeInt(xlen); // length of central directory
writeInt(xoff); // offset of central directory
if (comment != null) { // zip file comment
- byte[] b = getUTF8Bytes(comment);
- writeShort(b.length);
- writeBytes(b, 0, b.length);
+ writeShort(comment.length);
+ writeBytes(comment, 0, comment.length);
} else {
writeShort(0);
}
@@ -594,60 +616,4 @@
super.out.write(b, off, len);
written += len;
}
-
- /*
- * Returns the length of String's UTF8 encoding.
- */
- static int getUTF8Length(String s) {
- int count = 0;
- for (int i = 0; i < s.length(); i++) {
- char ch = s.charAt(i);
- if (ch <= 0x7f) {
- count++;
- } else if (ch <= 0x7ff) {
- count += 2;
- } else {
- count += 3;
- }
- }
- return count;
- }
-
- /*
- * Returns an array of bytes representing the UTF8 encoding
- * of the specified String.
- */
- private static byte[] getUTF8Bytes(String s) {
- char[] c = s.toCharArray();
- int len = c.length;
- // Count the number of encoded bytes...
- int count = 0;
- for (int i = 0; i < len; i++) {
- int ch = c[i];
- if (ch <= 0x7f) {
- count++;
- } else if (ch <= 0x7ff) {
- count += 2;
- } else {
- count += 3;
- }
- }
- // Now return the encoded bytes...
- byte[] b = new byte[count];
- int off = 0;
- for (int i = 0; i < len; i++) {
- int ch = c[i];
- if (ch <= 0x7f) {
- b[off++] = (byte)ch;
- } else if (ch <= 0x7ff) {
- b[off++] = (byte)((ch >> 6) | 0xc0);
- b[off++] = (byte)((ch & 0x3f) | 0x80);
- } else {
- b[off++] = (byte)((ch >> 12) | 0xe0);
- b[off++] = (byte)(((ch >> 6) & 0x3f) | 0x80);
- b[off++] = (byte)((ch & 0x3f) | 0x80);
- }
- }
- return b;
- }
}
--- a/jdk/src/share/classes/java/util/zip/package.html Thu Apr 16 11:16:40 2009 +0800
+++ b/jdk/src/share/classes/java/util/zip/package.html Thu Apr 16 21:00:42 2009 -0700
@@ -53,6 +53,11 @@
PKWARE ZIP File Format Specification</a>. The ZIP64(tm) format extensions
are used to overcome the size limitations of the original ZIP format.
<p>
+ <a name="lang_encoding">
+ <li>APPENDIX D of <a href="http://www.pkware.com/documents/casestudies/APPNOTE.TXT">
+ PKWARE ZIP File Format Specification</a> - Language Encoding Flag (EFS) to
+ encode ZIP entry filename and comment fields using UTF-8.
+<p>
<li><a href="http://www.isi.edu/in-notes/rfc1950.txt">
ZLIB Compressed Data Format Specification version 3.3</a>
--- a/jdk/src/share/native/java/util/zip/ZipEntry.c Thu Apr 16 11:16:40 2009 +0800
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,105 +0,0 @@
-/*
- * Copyright 1998 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.
- */
-
-/*
- * Native method support for java.util.zip.ZipEntry
- */
-
-#include <stdio.h>
-#include <stdlib.h>
-#include "jlong.h"
-#include "jvm.h"
-#include "jni.h"
-#include "jni_util.h"
-#include "zip_util.h"
-
-#include "java_util_zip_ZipEntry.h"
-
-#define DEFLATED 8
-#define STORED 0
-
-static jfieldID nameID;
-static jfieldID timeID;
-static jfieldID crcID;
-static jfieldID sizeID;
-static jfieldID csizeID;
-static jfieldID methodID;
-static jfieldID extraID;
-static jfieldID commentID;
-
-JNIEXPORT void JNICALL
-Java_java_util_zip_ZipEntry_initIDs(JNIEnv *env, jclass cls)
-{
- nameID = (*env)->GetFieldID(env, cls, "name", "Ljava/lang/String;");
- timeID = (*env)->GetFieldID(env, cls, "time", "J");
- crcID = (*env)->GetFieldID(env, cls, "crc", "J");
- sizeID = (*env)->GetFieldID(env, cls, "size", "J");
- csizeID = (*env)->GetFieldID(env, cls, "csize", "J");
- methodID = (*env)->GetFieldID(env, cls, "method", "I");
- extraID = (*env)->GetFieldID(env, cls, "extra", "[B");
- commentID = (*env)->GetFieldID(env, cls, "comment", "Ljava/lang/String;");
-}
-
-JNIEXPORT void JNICALL
-Java_java_util_zip_ZipEntry_initFields(JNIEnv *env, jobject obj, jlong zentry)
-{
- jzentry *ze = jlong_to_ptr(zentry);
- jstring name = (*env)->GetObjectField(env, obj, nameID);
-
- if (name == 0) {
- name = (*env)->NewStringUTF(env, ze->name);
- if (name == 0) {
- return;
- }
- (*env)->SetObjectField(env, obj, nameID, name);
- }
- (*env)->SetLongField(env, obj, timeID, (jlong)ze->time & 0xffffffffUL);
- (*env)->SetLongField(env, obj, crcID, (jlong)ze->crc & 0xffffffffUL);
- (*env)->SetLongField(env, obj, sizeID, (jlong)ze->size);
- if (ze->csize == 0) {
- (*env)->SetLongField(env, obj, csizeID, (jlong)ze->size);
- (*env)->SetIntField(env, obj, methodID, STORED);
- } else {
- (*env)->SetLongField(env, obj, csizeID, (jlong)ze->csize);
- (*env)->SetIntField(env, obj, methodID, DEFLATED);
- }
- if (ze->extra != 0) {
- unsigned char *bp = (unsigned char *)&ze->extra[0];
- jsize len = (bp[0] | (bp[1] << 8));
- jbyteArray extra = (*env)->NewByteArray(env, len);
- if (extra == 0) {
- return;
- }
- (*env)->SetByteArrayRegion(env, extra, 0, len, &ze->extra[2]);
- (*env)->SetObjectField(env, obj, extraID, extra);
- }
- if (ze->comment != 0) {
- jstring comment = (*env)->NewStringUTF(env, ze->comment);
- if (comment == 0) {
- return;
- }
- (*env)->SetObjectField(env, obj, commentID, comment);
- }
-}
--- a/jdk/src/share/native/java/util/zip/ZipFile.c Thu Apr 16 11:16:40 2009 +0800
+++ b/jdk/src/share/native/java/util/zip/ZipFile.c Thu Apr 16 21:00:42 2009 -0700
@@ -141,12 +141,11 @@
JNIEXPORT jlong JNICALL
Java_java_util_zip_ZipFile_getEntry(JNIEnv *env, jclass cls, jlong zfile,
- jstring name, jboolean addSlash)
+ jbyteArray name, jboolean addSlash)
{
#define MAXNAME 1024
jzfile *zip = jlong_to_ptr(zfile);
- jsize slen = (*env)->GetStringLength(env, name);
- jsize ulen = (*env)->GetStringUTFLength(env, name);
+ jsize ulen = (*env)->GetArrayLength(env, name);
char buf[MAXNAME+2], *path;
jzentry *ze;
@@ -159,7 +158,7 @@
} else {
path = buf;
}
- (*env)->GetStringUTFRegion(env, name, 0, slen, path);
+ (*env)->GetByteArrayRegion(env, name, 0, ulen, (jbyte *)path);
path[ulen] = '\0';
if (addSlash == JNI_FALSE) {
ze = ZIP_GetEntry(zip, path, 0);
@@ -186,32 +185,85 @@
jint n)
{
jzentry *ze = ZIP_GetNextEntry(jlong_to_ptr(zfile), n);
-
return ptr_to_jlong(ze);
}
JNIEXPORT jint JNICALL
-Java_java_util_zip_ZipFile_getMethod(JNIEnv *env, jclass cls, jlong zentry)
+Java_java_util_zip_ZipFile_getEntryMethod(JNIEnv *env, jclass cls, jlong zentry)
{
jzentry *ze = jlong_to_ptr(zentry);
-
return ze->csize != 0 ? DEFLATED : STORED;
}
-JNIEXPORT jlong JNICALL
-Java_java_util_zip_ZipFile_getCSize(JNIEnv *env, jclass cls, jlong zentry)
+JNIEXPORT jint JNICALL
+Java_java_util_zip_ZipFile_getEntryFlag(JNIEnv *env, jclass cls, jlong zentry)
{
jzentry *ze = jlong_to_ptr(zentry);
+ return ze->flag;
+}
+JNIEXPORT jlong JNICALL
+Java_java_util_zip_ZipFile_getEntryCSize(JNIEnv *env, jclass cls, jlong zentry)
+{
+ jzentry *ze = jlong_to_ptr(zentry);
return ze->csize != 0 ? ze->csize : ze->size;
}
JNIEXPORT jlong JNICALL
-Java_java_util_zip_ZipFile_getSize(JNIEnv *env, jclass cls, jlong zentry)
+Java_java_util_zip_ZipFile_getEntrySize(JNIEnv *env, jclass cls, jlong zentry)
+{
+ jzentry *ze = jlong_to_ptr(zentry);
+ return ze->size;
+}
+
+JNIEXPORT jlong JNICALL
+Java_java_util_zip_ZipFile_getEntryTime(JNIEnv *env, jclass cls, jlong zentry)
+{
+ jzentry *ze = jlong_to_ptr(zentry);
+ return (jlong)ze->time & 0xffffffffUL;
+}
+
+JNIEXPORT jlong JNICALL
+Java_java_util_zip_ZipFile_getEntryCrc(JNIEnv *env, jclass cls, jlong zentry)
+{
+ jzentry *ze = jlong_to_ptr(zentry);
+ return (jlong)ze->crc & 0xffffffffUL;
+}
+
+JNIEXPORT jbyteArray JNICALL
+Java_java_util_zip_ZipFile_getEntryBytes(JNIEnv *env, jclass cls, jlong zentry, jint type)
{
jzentry *ze = jlong_to_ptr(zentry);
-
- return ze->size;
+ int len = 0;
+ jbyteArray jba = NULL;
+ switch (type) {
+ case java_util_zip_ZipFile_JZENTRY_NAME:
+ if (ze->name != 0) {
+ len = (int)strlen(ze->name);
+ if (len == 0 || (jba = (*env)->NewByteArray(env, len)) == NULL)
+ break;
+ (*env)->SetByteArrayRegion(env, jba, 0, len, (jbyte *)ze->name);
+ }
+ break;
+ case java_util_zip_ZipFile_JZENTRY_EXTRA:
+ if (ze->extra != 0) {
+ unsigned char *bp = (unsigned char *)&ze->extra[0];
+ len = (bp[0] | (bp[1] << 8));
+ if (len <= 0 || (jba = (*env)->NewByteArray(env, len)) == NULL)
+ break;
+ (*env)->SetByteArrayRegion(env, jba, 0, len, &ze->extra[2]);
+ }
+ break;
+ case java_util_zip_ZipFile_JZENTRY_COMMENT:
+ if (ze->comment != 0) {
+ len = (int)strlen(ze->comment);
+ if (len == 0 || (jba = (*env)->NewByteArray(env, len)) == NULL)
+ break;
+ (*env)->SetByteArrayRegion(env, jba, 0, len, (jbyte*)ze->comment);
+ }
+ break;
+ }
+ return jba;
}
JNIEXPORT jint JNICALL
--- a/jdk/src/share/native/java/util/zip/zip_util.c Thu Apr 16 11:16:40 2009 +0800
+++ b/jdk/src/share/native/java/util/zip/zip_util.c Thu Apr 16 21:00:42 2009 -0700
@@ -512,7 +512,6 @@
/* Clear previous zip error */
zip->msg = NULL;
-
/* Get position of END header */
if ((endpos = findEND(zip, endbuf)) == -1)
return -1; /* no END header or system error */
@@ -520,7 +519,6 @@
if (endpos == 0) return 0; /* only END header present */
freeCEN(zip);
-
/* Get position and length of central directory */
cenlen = ENDSIZ(endbuf);
cenoff = ENDOFF(endbuf);
@@ -935,6 +933,7 @@
ze->crc = CENCRC(cen);
locoff = CENOFF(cen);
ze->pos = -(zip->locpos + locoff);
+ ze->flag = CENFLG(cen);
if ((ze->name = malloc(nlen + 1)) == NULL) goto Catch;
memcpy(ze->name, cen + CENHDR, nlen);
--- a/jdk/src/share/native/java/util/zip/zip_util.h Thu Apr 16 11:16:40 2009 +0800
+++ b/jdk/src/share/native/java/util/zip/zip_util.h Thu Apr 16 21:00:42 2009 -0700
@@ -168,6 +168,7 @@
char *comment; /* optional zip file comment */
jbyte *extra; /* optional extra data */
jlong pos; /* position of LOC header or entry data */
+ jint flag; /* general purpose flag */
} jzentry;
/*
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/util/zip/ZipCoding.java Thu Apr 16 21:00:42 2009 -0700
@@ -0,0 +1,133 @@
+/*
+ * 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.
+ *
+ * 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.
+ */
+
+/**
+ * @test
+ * @bug 4244499 4532049 4700978 4820807 4980042
+ * @summary Test ZipInputStream, ZipOutputStream and ZipFile with non-UTF8 encoding
+ */
+
+import java.io.*;
+import java.nio.charset.*;
+import java.util.*;
+import java.util.zip.*;
+
+public class ZipCoding {
+
+ public static void main(String[] args) throws Exception {
+
+ test("MS932",
+ "\u4e00\u4e01", "\uff67\uff68\uff69\uff6a\uff6b\uff6c");
+
+ test("ibm437",
+ "\u00e4\u00fc", "German Umlaut \u00fc in comment");
+
+ test("utf-8",
+ "\u4e00\u4e01", "\uff67\uff68\uff69\uff6a\uff6b\uff6c");
+
+ test("utf-8",
+ "\u00e4\u00fc", "German Umlaut \u00fc in comment");
+
+ test("utf-8",
+ "Surrogate\ud801\udc01", "Surrogates \ud800\udc00 in comment");
+
+ }
+
+ static void testZipInputStream(InputStream is, Charset cs,
+ String name, String comment, byte[] bb)
+ throws Exception
+ {
+ ZipInputStream zis = new ZipInputStream(is, cs);
+ ZipEntry e = zis.getNextEntry();
+ if (e == null || ! name.equals(e.getName()))
+ throw new RuntimeException("ZipIS name doesn't match!");
+ byte[] bBuf = new byte[bb.length << 1];
+ int n = zis.read(bBuf, 0, bBuf.length);
+ if (n != bb.length ||
+ !Arrays.equals(bb, Arrays.copyOf(bBuf, n))) {
+ throw new RuntimeException("ZipIS content doesn't match!");
+ }
+ zis.close();
+ }
+
+ static void testZipFile(File f, Charset cs,
+ String name, String comment, byte[] bb)
+ throws Exception
+ {
+ ZipFile zf = new ZipFile(f, cs);
+ Enumeration<? extends ZipEntry> zes = zf.entries();
+ ZipEntry e = (ZipEntry)zes.nextElement();
+ if (! name.equals(e.getName()) ||
+ ! comment.equals(e.getComment()))
+ throw new RuntimeException("ZipFile: name/comment doesn't match!");
+ InputStream is = zf.getInputStream(e);
+ if (is == null)
+ throw new RuntimeException("ZipFile: getIS failed!");
+ byte[] bBuf = new byte[bb.length << 1];
+ int n = 0;
+ int nn =0;
+ while ((nn = is.read(bBuf, n, bBuf.length-n)) != -1) {
+ n += nn;
+ }
+ if (n != bb.length ||
+ !Arrays.equals(bb, Arrays.copyOf(bBuf, n))) {
+ throw new RuntimeException("ZipFile content doesn't match!");
+ }
+ zf.close();
+ }
+
+ static void test(String csn, String name, String comment)
+ throws Exception
+ {
+ byte[] bb = "This is the conent of the zipfile".getBytes("ISO-8859-1");
+ Charset cs = Charset.forName(csn);
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ ZipOutputStream zos = new ZipOutputStream(baos, cs);
+
+ ZipEntry e = new ZipEntry(name);
+ e.setComment(comment);
+ zos.putNextEntry(e);
+ zos.write(bb, 0, bb.length);
+ zos.closeEntry();
+ zos.close();
+ ByteArrayInputStream bis = new ByteArrayInputStream(baos.toByteArray());
+ testZipInputStream(bis, cs, name, comment, bb);
+
+ if ("utf-8".equals(csn)) {
+ // EFS should be set
+ bis.reset();
+ testZipInputStream(bis, Charset.forName("MS932"), name, comment, bb);
+ }
+
+ File f = new File(new File(System.getProperty("test.dir", ".")),
+ "zfcoding.zip");
+ FileOutputStream fos = new FileOutputStream(f);
+ baos.writeTo(fos);
+ fos.close();
+ testZipFile(f, cs, name, comment, bb);
+ if ("utf-8".equals(csn)) {
+ testZipFile(f, Charset.forName("MS932"), name, comment, bb);
+ }
+ f.delete();
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/util/zip/zip.java Thu Apr 16 21:00:42 2009 -0700
@@ -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);
+ }
+}
+