? christiandiffs01 ? jazzlibDiffsSince20040511 ? jazzlib_christian_01.zip ? jazzlibpatch001 ? net-0.08.diff ? java/util/zip/CRC32If.java ? java/util/zip/DeflaterIf.java ? java/util/zip/InflaterIf.java ? java/util/zip/back ? java/util/zip/diffs08 ? lib/net ? lib/jazzlib/dist Index: java/util/zip/Adler32.java =================================================================== RCS file: /cvsroot/classpath/classpath/java/util/zip/Adler32.java,v retrieving revision 1.5 diff -u -r1.5 Adler32.java --- java/util/zip/Adler32.java 22 Jan 2002 22:27:02 -0000 1.5 +++ java/util/zip/Adler32.java 4 May 2005 02:16:28 -0000 @@ -152,6 +152,19 @@ update(buffer, 0, buffer.length); } + /** Calculated this value like this: + * + * + * static { + * int i=0, a=BASE, b=a; + * for(;b>0;i++) + * b += (a += 255); + * System.out.println("max defer = " + (i-1)); + } + */ + + private final static int DEFER = 3850; + /** * Updates the checksum with the bytes taken from the array. * @@ -161,37 +174,18 @@ */ public void update (byte[] buf, int off, int len) { - //(By Per Bothner) - int s1 = checksum & 0xffff; - int s2 = checksum >>> 16; + int s1 = checksum & 0xffff; + int s2 = checksum >>> 16; - while (len > 0) - { - // We can defer the modulo operation: - // s1 maximally grows from 65521 to 65521 + 255 * 3800 - // s2 maximally grows by 3800 * median(s1) = 2090079800 < 2^31 - int n = 3800; - if (n > len) - n = len; - len -= n; - while (--n >= 0) - { - s1 = s1 + (buf[off++] & 0xFF); - s2 = s2 + s1; + while (off < len) { + int max = Math.min(off + DEFER, len); + while (off < max) { + s1 = (buf[off++] & 0xff) + s1; + s2 += s1; } - s1 %= BASE; - s2 %= BASE; + s1 %= BASE; + s2 %= BASE; } - - /*Old implementation, borrowed from somewhere: - int n; - - while (len-- > 0) { - - s1 = (s1 + (bs[offset++] & 0xff)) % BASE; - s2 = (s2 + s1) % BASE; - }*/ - checksum = (s2 << 16) | s1; } Index: java/util/zip/CRC32.java =================================================================== RCS file: /cvsroot/classpath/classpath/java/util/zip/CRC32.java,v retrieving revision 1.4 diff -u -r1.4 CRC32.java --- java/util/zip/CRC32.java 22 Jan 2002 22:27:02 -0000 1.4 +++ java/util/zip/CRC32.java 4 May 2005 02:16:28 -0000 @@ -57,7 +57,7 @@ * @author Per Bothner * @date April 1, 1999. */ -public class CRC32 implements Checksum +public class CRC32 implements CRC32If { /** The crc data checksum so far. */ private int crc = 0; Index: java/util/zip/Deflater.java =================================================================== RCS file: /cvsroot/classpath/classpath/java/util/zip/Deflater.java,v retrieving revision 1.5 diff -u -r1.5 Deflater.java --- java/util/zip/Deflater.java 29 Apr 2004 15:47:26 -0000 1.5 +++ java/util/zip/Deflater.java 4 May 2005 02:16:29 -0000 @@ -48,48 +48,8 @@ * @author Jochen Hoenicke * @author Tom Tromey */ -public class Deflater +public class Deflater implements DeflaterIf { - /** - * The best and slowest compression level. This tries to find very - * long and distant string repetitions. - */ - public static final int BEST_COMPRESSION = 9; - /** - * The worst but fastest compression level. - */ - public static final int BEST_SPEED = 1; - /** - * The default compression level. - */ - public static final int DEFAULT_COMPRESSION = -1; - /** - * This level won't compress at all but output uncompressed blocks. - */ - public static final int NO_COMPRESSION = 0; - - /** - * The default strategy. - */ - public static final int DEFAULT_STRATEGY = 0; - /** - * This strategy will only allow longer string repetitions. It is - * useful for random data with a small character set. - */ - public static final int FILTERED = 1; - - /** - * This strategy will not look for string repetitions at all. It - * only encodes with Huffman trees (which means, that more common - * characters get a smaller encoding. - */ - public static final int HUFFMAN_ONLY = 2; - - /** - * The compression method. This is the only method supported so far. - * There is no need to use this constant at all. - */ - public static final int DEFLATED = 8; /* * The Deflater can do the following state transitions: Index: java/util/zip/DeflaterEngine.java =================================================================== RCS file: /cvsroot/classpath/classpath/java/util/zip/DeflaterEngine.java,v retrieving revision 1.7 diff -u -r1.7 DeflaterEngine.java --- java/util/zip/DeflaterEngine.java 22 Oct 2004 18:02:06 -0000 1.7 +++ java/util/zip/DeflaterEngine.java 4 May 2005 02:16:30 -0000 @@ -221,7 +221,7 @@ } } - private void updateHash() { + private final void updateHash() { if (DEBUGGING) System.err.println("updateHash: "+strstart); ins_h = (window[strstart] << HASH_SHIFT) ^ window[strstart + 1]; Index: java/util/zip/DeflaterHuffman.java =================================================================== RCS file: /cvsroot/classpath/classpath/java/util/zip/DeflaterHuffman.java,v retrieving revision 1.5 diff -u -r1.5 DeflaterHuffman.java --- java/util/zip/DeflaterHuffman.java 22 Oct 2004 18:02:06 -0000 1.5 +++ java/util/zip/DeflaterHuffman.java 4 May 2005 02:16:30 -0000 @@ -59,7 +59,7 @@ private static final int[] BL_ORDER = { 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15 }; - private static String bit4Reverse = + private final static String bit4Reverse = "\000\010\004\014\002\012\006\016\001\011\005\015\003\013\007\017"; class Tree { Index: java/util/zip/DeflaterOutputStream.java =================================================================== RCS file: /cvsroot/classpath/classpath/java/util/zip/DeflaterOutputStream.java,v retrieving revision 1.8 diff -u -r1.8 DeflaterOutputStream.java --- java/util/zip/DeflaterOutputStream.java 20 Oct 2004 08:08:52 -0000 1.8 +++ java/util/zip/DeflaterOutputStream.java 4 May 2005 02:16:31 -0000 @@ -1,5 +1,5 @@ /* DeflaterOutputStream.java - Output filter for compressing. - Copyright (C) 1999, 2000, 2001, 2004 Free Software Foundation, Inc. + Copyright (C) 1999, 2000, 2001, 2004, 2005 Free Software Foundation, Inc. This file is part of GNU Classpath. @@ -70,7 +70,7 @@ /** * The deflater which is used to deflate the stream. */ - protected Deflater def; + protected final DeflaterIf def; /** * Deflates everything in the def's input buffers. This will call @@ -100,7 +100,7 @@ */ public DeflaterOutputStream(OutputStream out) { - this(out, new Deflater(), 512); + this(out, new Deflater(), 4096); } /** @@ -109,9 +109,9 @@ * @param out the output stream where deflated output should be written. * @param defl the underlying deflater. */ - public DeflaterOutputStream(OutputStream out, Deflater defl) + public DeflaterOutputStream(OutputStream out, DeflaterIf def) { - this(out, defl, 512); + this(out, def, 4096); } /** @@ -122,13 +122,13 @@ * @param bufsize the buffer size. * @exception IllegalArgumentException if bufsize isn't positive. */ - public DeflaterOutputStream(OutputStream out, Deflater defl, int bufsize) + public DeflaterOutputStream(OutputStream out, DeflaterIf def, int bufsize) { super(out); if (bufsize <= 0) throw new IllegalArgumentException("bufsize <= 0"); buf = new byte[bufsize]; - def = defl; + this.def = def; } /** @@ -137,12 +137,12 @@ * flushed. This function doesn't work in Sun's JDK, but only in * jazzlib. */ - public void flush() throws IOException + /*public void flush() throws IOException { def.flush(); deflate(); out.flush(); - } + }*/ /** * Finishes the stream by calling finish() on the deflater. This Index: java/util/zip/Inflater.java =================================================================== RCS file: /cvsroot/classpath/classpath/java/util/zip/Inflater.java,v retrieving revision 1.6 diff -u -r1.6 Inflater.java --- java/util/zip/Inflater.java 22 May 2003 07:22:39 -0000 1.6 +++ java/util/zip/Inflater.java 4 May 2005 02:16:32 -0000 @@ -66,7 +66,7 @@ * @date May 17, 1999 * @since JDK 1.1 */ -public class Inflater +public class Inflater implements InflaterIf { /* Copy lengths for literal codes 257..285 */ private static final int CPLENS[] = @@ -266,7 +266,7 @@ * returns 0, you should check, whether needsDictionary(), * needsInput() or finished() returns true, to determine why no * further output is produced. - * @param buffer the output buffer. + * @param buf the output buffer. * @return the number of bytes written to the buffer, 0 if no further * output can be produced. * @exception DataFormatException if deflated stream is invalid. @@ -282,7 +282,7 @@ * returns 0, you should check, whether needsDictionary(), * needsInput() or finished() returns true, to determine why no * further output is produced. - * @param buffer the output buffer. + * @param buf the output buffer. * @param off the offset into buffer where the output should start. * @param len the maximum length of the output. * @return the number of bytes written to the buffer, 0 if no further @@ -406,7 +406,7 @@ /** * Sets the input. This should only be called, if needsInput() * returns true. - * @param buffer the input. + * @param buf the input. * @exception IllegalStateException if no input is needed. */ public void setInput (byte[] buf) @@ -417,7 +417,7 @@ /** * Sets the input. This should only be called, if needsInput() * returns true. - * @param buffer the input. + * @param buf the input. * @param off the offset into buffer where the input starts. * @param len the length of the input. * @exception IllegalStateException if no input is needed. Index: java/util/zip/InflaterInputStream.java =================================================================== RCS file: /cvsroot/classpath/classpath/java/util/zip/InflaterInputStream.java,v retrieving revision 1.16 diff -u -r1.16 InflaterInputStream.java --- java/util/zip/InflaterInputStream.java 23 Nov 2004 13:03:42 -0000 1.16 +++ java/util/zip/InflaterInputStream.java 4 May 2005 02:16:32 -0000 @@ -1,5 +1,5 @@ /* InflaterInputStream.java - Input stream filter for decompressing - Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004 + Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004, 2005 Free Software Foundation, Inc. This file is part of GNU Classpath. @@ -59,7 +59,7 @@ /** * Decompressor for this filter */ - protected Inflater inf; + protected final InflaterIf inf; /** * Byte array used as a buffer @@ -93,7 +93,7 @@ * @param in the InputStream to read bytes from * @param inf the decompressor used to decompress data read from in */ - public InflaterInputStream(InputStream in, Inflater inf) + public InflaterInputStream(InputStream in, InflaterIf inf) { this(in, inf, 4096); } @@ -106,7 +106,7 @@ * @param inf the decompressor used to decompress data read from in * @param size size of the buffer to use */ - public InflaterInputStream(InputStream in, Inflater inf, int size) + public InflaterInputStream(InputStream in, InflaterIf inf, int size) { super(in); @@ -115,8 +115,7 @@ if (inf == null) throw new NullPointerException("inf may not be null"); if (size < 0) - throw new IllegalArgumentException("size may not be negative"); - + throw new IllegalArgumentException("size may not be negative"); this.inf = inf; this.buf = new byte [size]; } @@ -195,7 +194,7 @@ { count = inf.inflate(b, off, len); } - catch (DataFormatException dfe) + catch (Exception dfe) // Hack for compatibility with InflaterIf { throw new ZipException(dfe.getMessage()); } Index: java/util/zip/OutputWindow.java =================================================================== RCS file: /cvsroot/classpath/classpath/java/util/zip/OutputWindow.java,v retrieving revision 1.5 diff -u -r1.5 OutputWindow.java --- java/util/zip/OutputWindow.java 22 Oct 2004 18:02:06 -0000 1.5 +++ java/util/zip/OutputWindow.java 4 May 2005 02:16:32 -0000 @@ -63,7 +63,7 @@ window_end &= WINDOW_MASK; } - private void slowRepeat(int rep_start, int len, int dist) + private final void slowRepeat(int rep_start, int len, int dist) { while (len-- > 0) { Index: java/util/zip/ZipConstants.java =================================================================== RCS file: /cvsroot/classpath/classpath/java/util/zip/ZipConstants.java,v retrieving revision 1.5 diff -u -r1.5 ZipConstants.java --- java/util/zip/ZipConstants.java 4 Nov 2003 10:04:16 -0000 1.5 +++ java/util/zip/ZipConstants.java 4 May 2005 02:16:32 -0000 @@ -39,9 +39,16 @@ interface ZipConstants { + /** + * The default character set used for entry names and comments in a ZIP + * or JAR file. This is UTF-8 for compatibility with Sun's java.util.zip + * package. + */ + String DEFAULT_CHARSET = "UTF-8"; + /* The local file header */ int LOCHDR = 30; - int LOCSIG = 'P'|('K'<<8)|(3<<16)|(4<<24); + long LOCSIG = 'P'|('K'<<8)|(3<<16)|(4<<24); int LOCVER = 4; int LOCFLG = 6; @@ -54,7 +61,7 @@ int LOCEXT = 28; /* The Data descriptor */ - int EXTSIG = 'P'|('K'<<8)|(7<<16)|(8<<24); + long EXTSIG = 'P'|('K'<<8)|(7<<16)|(8<<24); int EXTHDR = 16; int EXTCRC = 4; @@ -62,7 +69,7 @@ int EXTLEN = 12; /* The central directory file header */ - int CENSIG = 'P'|('K'<<8)|(1<<16)|(2<<24); + long CENSIG = 'P'|('K'<<8)|(1<<16)|(2<<24); int CENHDR = 46; int CENVEM = 4; @@ -82,7 +89,7 @@ int CENOFF = 42; /* The entries in the end of central directory */ - int ENDSIG = 'P'|('K'<<8)|(5<<16)|(6<<24); + long ENDSIG = 'P'|('K'<<8)|(5<<16)|(6<<24); int ENDHDR = 22; /* The following two fields are missing in SUN JDK */ Index: java/util/zip/ZipEntry.java =================================================================== RCS file: /cvsroot/classpath/classpath/java/util/zip/ZipEntry.java,v retrieving revision 1.17 diff -u -r1.17 ZipEntry.java --- java/util/zip/ZipEntry.java 5 Jan 2005 20:47:53 -0000 1.17 +++ java/util/zip/ZipEntry.java 4 May 2005 02:16:33 -0000 @@ -48,29 +48,29 @@ * needs an instance of this class to create a new member. * * @author Jochen Hoenicke + * @author Christian Schlichtherle Fixed some bugs: Noteably the 2 GB Bug, so + * that Jazzlib now supports 4 GB which is the maximum with the ZIP32 + * format. Second the date/time bug, which made it impossible to use + * getTime(). Minor performance enhancements are also implemented. */ + public class ZipEntry implements ZipConstants, Cloneable { - private static final int KNOWN_SIZE = 1; - private static final int KNOWN_CSIZE = 2; - private static final int KNOWN_CRC = 4; - private static final int KNOWN_TIME = 8; private static final int KNOWN_EXTRA = 16; private static Calendar cal; private String name; - private int size; - private long compressedSize = -1; - private int crc; - private int dostime; - private short known = 0; - private short method = -1; - private byte[] extra = null; - private String comment = null; - - int flags; /* used by ZipOutputStream */ - int offset; /* used by ZipFile and ZipOutputStream */ + private long size = -1; + private long csize = -1; + private long crc = -1; + private long dostime = -1; + private int method = -1; + private byte[] extra; + private String comment; + + int flags; + long offset; /** * Compression method. This method doesn't compress at all. @@ -92,7 +92,7 @@ public ZipEntry(String name) { int length = name.length(); - if (length > 65535) + if (length > 0xffff) throw new IllegalArgumentException("name length is " + length); this.name = name; } @@ -109,9 +109,8 @@ ZipEntry(ZipEntry e, String name) { this.name = name; - known = e.known; size = e.size; - compressedSize = e.compressedSize; + csize = e.csize; crc = e.crc; dostime = e.dostime; method = e.method; @@ -119,25 +118,8 @@ comment = e.comment; } - final void setDOSTime(int dostime) - { - this.dostime = dostime; - known |= KNOWN_TIME; - } - - final int getDOSTime() - { - if ((known & KNOWN_TIME) == 0) - return 0; - else - return dostime; - } - - /** - * Creates a copy of this zip entry. - */ /** - * Clones the entry. + * Creates a clone of this zip entry. */ public Object clone() { @@ -155,6 +137,18 @@ } } + final void setDOSTime(long dostime) + { + if(dostime > 0xffffffffL) + throw new IllegalArgumentException(); + this.dostime = dostime; + } + + final long getDOSTime() + { + return dostime; + } + /** * Returns the entry name. The path components in the entry are * always separated by slashes ('/'). @@ -173,7 +167,7 @@ Calendar cal = getCalendar(); synchronized (cal) { - cal.setTime(new Date(time)); + cal.setTimeInMillis(time); dostime = (cal.get(Calendar.YEAR) - 1980 & 0x7f) << 25 | (cal.get(Calendar.MONTH) + 1) << 21 | (cal.get(Calendar.DAY_OF_MONTH)) << 16 @@ -181,7 +175,6 @@ | (cal.get(Calendar.MINUTE)) << 5 | (cal.get(Calendar.SECOND)) >> 1; } - this.known |= KNOWN_TIME; } /** @@ -190,34 +183,25 @@ */ public long getTime() { - if ((known & KNOWN_TIME) == 0) - return -1; + if (dostime == -1) + return -1; // The extra bytes might contain the time (posix/unix extension) parseExtra (); - int sec = 2 * (dostime & 0x1f); - int min = (dostime >> 5) & 0x3f; - int hrs = (dostime >> 11) & 0x1f; - int day = (dostime >> 16) & 0x1f; - int mon = ((dostime >> 21) & 0xf) - 1; - int year = ((dostime >> 25) & 0x7f) + 1980; /* since 1900 */ + int sec = (int) (2 * (dostime & 0x1f)); + int min = (int) ((dostime >> 5) & 0x3f); + int hrs = (int) ((dostime >> 11) & 0x1f); + int day = (int) ((dostime >> 16) & 0x1f); + int mon = (int) (((dostime >> 21) & 0xf) - 1); + int year = (int) (((dostime >> 25) & 0x7f) + 1980); /* since 1900 */ - try - { - cal = getCalendar(); - synchronized (cal) + Calendar cal = getCalendar(); + synchronized (cal) { cal.set(year, mon, day, hrs, min, sec); - return cal.getTime().getTime(); + return cal.getTimeInMillis(); } - } - catch (RuntimeException ex) - { - /* Ignore illegal time stamp */ - known &= ~KNOWN_TIME; - return -1; - } } private static synchronized Calendar getCalendar() @@ -234,10 +218,9 @@ */ public void setSize(long size) { - if ((size & 0xffffffff00000000L) != 0) + if (size > 0xffffffffL) throw new IllegalArgumentException(); - this.size = (int) size; - this.known |= KNOWN_SIZE; + this.size = size; } /** @@ -246,7 +229,7 @@ */ public long getSize() { - return (known & KNOWN_SIZE) != 0 ? size & 0xffffffffL : -1L; + return size; } /** @@ -254,7 +237,9 @@ */ public void setCompressedSize(long csize) { - this.compressedSize = csize; + if(csize > 0xffffffffL) + throw new IllegalArgumentException(); + this.csize = csize; } /** @@ -263,7 +248,7 @@ */ public long getCompressedSize() { - return compressedSize; + return csize; } /** @@ -272,10 +257,9 @@ */ public void setCrc(long crc) { - if ((crc & 0xffffffff00000000L) != 0) + if (crc > 0xffffffffL) throw new IllegalArgumentException(); - this.crc = (int) crc; - this.known |= KNOWN_CRC; + this.crc = crc; } /** @@ -284,7 +268,7 @@ */ public long getCrc() { - return (known & KNOWN_CRC) != 0 ? crc & 0xffffffffL : -1L; + return crc; } /** @@ -299,7 +283,7 @@ if (method != ZipOutputStream.STORED && method != ZipOutputStream.DEFLATED) throw new IllegalArgumentException(); - this.method = (short) method; + this.method = method; } /** @@ -329,6 +313,7 @@ private void parseExtra() { + /* // Already parsed? if ((known & KNOWN_EXTRA) != 0) return; @@ -350,7 +335,6 @@ | (extra[pos++] & 0xff) << 8; if (sig == 0x5455) { - /* extended time stamp */ int flags = extra[pos]; if ((flags & 1) != 0) { @@ -360,17 +344,18 @@ | (extra[pos+4] & 0xff) << 24); setTime(time); } - } + } pos += len; } } catch (ArrayIndexOutOfBoundsException ex) { - /* be lenient */ + return; } known |= KNOWN_EXTRA; +*/ } /** Index: java/util/zip/ZipException.java =================================================================== RCS file: /cvsroot/classpath/classpath/java/util/zip/ZipException.java,v retrieving revision 1.8 diff -u -r1.8 ZipException.java --- java/util/zip/ZipException.java 24 Feb 2002 04:25:17 -0000 1.8 +++ java/util/zip/ZipException.java 4 May 2005 02:16:33 -0000 @@ -1,5 +1,5 @@ /* ZipException.java - exception representing a zip related error - Copyright (C) 1998, 1999, 2000, 2001, 2002 Free Software Foundation, Inc. + Copyright (C) 1998, 1999, 2000, 2001, 2002, 2005 Free Software Foundation, Inc. This file is part of GNU Classpath. Index: java/util/zip/ZipFile.java =================================================================== RCS file: /cvsroot/classpath/classpath/java/util/zip/ZipFile.java,v retrieving revision 1.22 diff -u -r1.22 ZipFile.java --- java/util/zip/ZipFile.java 10 Feb 2005 02:43:17 -0000 1.22 +++ java/util/zip/ZipFile.java 4 May 2005 02:16:35 -0000 @@ -39,8 +39,6 @@ package java.util.zip; -import gnu.java.util.EmptyEnumeration; - import java.io.BufferedInputStream; import java.io.DataInput; import java.io.EOFException; @@ -48,9 +46,11 @@ import java.io.IOException; import java.io.InputStream; import java.io.RandomAccessFile; +import java.io.UnsupportedEncodingException; import java.util.Enumeration; import java.util.HashMap; import java.util.Iterator; +import java.util.NoSuchElementException; /** * This class represents a Zip archive. You can ask for the contained @@ -62,6 +62,13 @@ * * @author Jochen Hoenicke * @author Artur Biesiadowski + * @author Christian Schlichtherle Added new constructors to support different + * character sets used to encode international entry names and comments. + * Added parameter to getInputStream to support different Inflater + * implementations. + * Fixed some bugs: Noteably the 2 GB Bug, so that Jazzlib now + * supports 4 GB which is the maximum with the ZIP32 format. + * Minor performance enhancements are also implemented. */ public class ZipFile implements ZipConstants { @@ -82,84 +89,142 @@ // File from which zip entries are read. private final RandomAccessFile raf; - // The entries of this zip file when initialized and not yet closed. - private HashMap entries; - + private final String charSet; + private final HashMap entries; private boolean closed = false; /** - * Opens a Zip file with the given name for reading. - * @exception IOException if a i/o error occured. - * @exception ZipException if the file doesn't contain a valid zip - * archive. + * Opens the given ZIP or JAR file. + * + * @exception IOException If a i/o error occured. + * @exception ZipException If the file doesn't contain a valid ZIP or JAR + * archive. */ - public ZipFile(String name) throws ZipException, IOException + public ZipFile(final String name) throws ZipException, IOException { - this.raf = new RandomAccessFile(name, "r"); - this.name = name; - checkZipFile(); + this(new File(name), OPEN_READ, DEFAULT_CHARSET); } /** - * Opens a Zip file reading the given File. - * @exception IOException if a i/o error occured. - * @exception ZipException if the file doesn't contain a valid zip - * archive. + * Opens the given ZIP or JAR file. + * + * @exception IOException If an i/o error occured. + * @exception ZipException If the file doesn't contain a valid ZIP or JAR + * archive. */ public ZipFile(File file) throws ZipException, IOException { - this.raf = new RandomAccessFile(file, "r"); - this.name = file.getPath(); - checkZipFile(); + this(file, OPEN_READ, DEFAULT_CHARSET); } /** - * Opens a Zip file reading the given File in the given mode. - * - * If the OPEN_DELETE mode is specified, the zip file will be deleted at - * some time moment after it is opened. It will be deleted before the zip - * file is closed or the Virtual Machine exits. - * - * The contents of the zip file will be accessible until it is closed. + * Opens the given ZIP or JAR file in the given mode. + *

+ * If the OPEN_DELETE mode is specified, the ZIP or JAR file will be deleted + * at some time after it has been opened and before it is closed or the + * Virtual Machine exits. + *

+ * Note:The OPEN_DELETE mode is currently unimplemented in this + * library. * * @since JDK1.3 - * @param mode Must be one of OPEN_READ or OPEN_READ | OPEN_DELETE + * @param mode Must be one of OPEN_READ or OPEN_READ | OPEN_DELETE. * - * @exception IOException if a i/o error occured. - * @exception ZipException if the file doesn't contain a valid zip - * archive. + * @exception IOException If an i/o error occured. + * @exception ZipException If the file doesn't contain a valid ZIP or JAR + * archive. */ - public ZipFile(File file, int mode) throws ZipException, IOException + public ZipFile(File file, final int mode) throws ZipException, IOException { - if (mode != OPEN_READ && mode != (OPEN_READ | OPEN_DELETE)) - throw new IllegalArgumentException("invalid mode"); - if ((mode & OPEN_DELETE) != 0) - file.deleteOnExit(); - this.raf = new RandomAccessFile(file, "r"); - this.name = file.getPath(); - checkZipFile(); + this(file, mode, DEFAULT_CHARSET); } - private void checkZipFile() throws IOException, ZipException + /** + * Opens a Zip file with the given name for reading. + *

+ * Note: This constructor is not part of the JDK API! + * + * @param charSet The character set to use for entry names and comments. + * + * @exception IOException If an i/o error occured. + * @exception ZipException If the file doesn't contain a valid ZIP or JAR + * archive. + */ + public ZipFile(final String name, final String charSet) + throws ZipException, IOException, UnsupportedEncodingException { + this(new File(name), OPEN_READ, charSet); + } + + /** + * Opens a Zip file reading the given File. + *

+ * Note: This constructor is not part of the JDK API! + * + * @param charSet The character set to use for entry names and comments. + * + * @exception IOException If an i/o error occured. + * @exception ZipException If the file doesn't contain a valid ZIP or JAR + * archive. + */ + public ZipFile(final File file, final String charSet) + throws ZipException, IOException, UnsupportedEncodingException { + this(file, OPEN_READ, charSet); + } + + /** + * Opens the given ZIP or JAR file in the given mode. + *

+ * If the OPEN_DELETE mode is specified, the ZIP or JAR file will be deleted + * at some time after it has been opened and before it is closed or the + * Virtual Machine exits. + *

+ * Note:The OPEN_DELETE mode is currently unimplemented in this + * library. + *

+ * Note: This constructor is not part of the JDK API! + * + * @param mode Must be one of OPEN_READ or OPEN_READ | OPEN_DELETE. + * @param charSet The character set to use for entry names and comments. + * + * @exception IOException If an i/o error occured. + * @exception ZipException If the file doesn't contain a valid ZIP or JAR + * archive. + */ + public ZipFile(final File file, final int mode, final String charSet) + throws ZipException, IOException, UnsupportedEncodingException { + if ((mode & OPEN_DELETE) != 0) + { + throw new IllegalArgumentException + + ("OPEN_DELETE mode not supported yet in ZipFile"); + } + this.raf = new RandomAccessFile(file, "r"); + this.name = file.getPath(); + this.charSet = charSet; + new String("").getBytes(charSet); // Fail-fast + try { + //checkZipFile(); // Does not help in many cases of corrupted ZIP files. + this.entries = readEntries(); + } + catch (IOException failure) { + // Don't forget to release our resources even on failure! + try { close(); } + catch (IOException ignore) { } + throw failure; + } + } + + /*private void checkZipFile() throws IOException, ZipException { + raf.seek(0); byte[] magicBuf = new byte[4]; raf.read(magicBuf); if (readLeInt(magicBuf, 0) != LOCSIG) { - raf.close(); - throw new ZipException("Not a valid zip file"); + throw new ZipException("Not a valid ZIP or JAR file: " + name); } - } - - /** - * Checks if file is closed and throws an exception. - */ - private void checkClosed() - { - if (closed) - throw new IllegalStateException("ZipFile has closed: " + name); - } + }*/ /** * Read an unsigned short in little endian byte order from the given @@ -172,7 +237,7 @@ * @exception IOException if a i/o error occured. * @exception EOFException if the file ends prematurely */ - private int readLeShort(DataInput di, byte[] b) throws IOException + private final int readLeShort(DataInput di, byte[] b) throws IOException { di.readFully(b, 0, 2); return (b[0] & 0xff) | (b[1] & 0xff) << 8; @@ -189,11 +254,11 @@ * @exception IOException if a i/o error occured. * @exception EOFException if the file ends prematurely */ - private int readLeInt(DataInput di, byte[] b) throws IOException + private final long readLeInt(DataInput di, byte[] b) throws IOException { di.readFully(b, 0, 4); - return ((b[0] & 0xff) | (b[1] & 0xff) << 8) - | ((b[2] & 0xff) | (b[3] & 0xff) << 8) << 16; + return ((b[0] & 0xffL) | (b[1] & 0xffL) << 8) + | ((b[2] & 0xffL) | (b[3] & 0xffL) << 8) << 16; } /** @@ -204,7 +269,7 @@ * @param off the offset to read from. * @return The value read. */ - private int readLeShort(byte[] b, int off) + private final int readLeShort(byte[] b, int off) { return (b[off] & 0xff) | (b[off+1] & 0xff) << 8; } @@ -217,37 +282,40 @@ * @param off the offset to read from. * @return The value read. */ - private int readLeInt(byte[] b, int off) + private final long readLeInt(byte[] b, int off) { - return ((b[off] & 0xff) | (b[off+1] & 0xff) << 8) - | ((b[off+2] & 0xff) | (b[off+3] & 0xff) << 8) << 16; + return ((b[off] & 0xffL) | (b[off+1] & 0xffL) << 8) + | ((b[off+2] & 0xffL) | (b[off+3] & 0xffL) << 8) << 16; } - /** - * Read the central directory of a zip file and fill the entries - * array. This is called exactly once when first needed. It is called - * while holding the lock on raf. + * Read the central directory of a zip file and return the entries. + * This is called exactly once from the constructor in order to implement + * fail-fast-behaviour. * - * @exception IOException if a i/o error occured. - * @exception ZipException if the central directory is malformed + * @return A HashMap containing all ZIP entries. + * It's keys are entry name strings and its values are ZipEntry + * objects. + * + * @exception IOException If an i/o error occured. + * @exception ZipException If the central directory is malformed. */ - private void readEntries() throws ZipException, IOException + private HashMap readEntries() throws ZipException, IOException { - /* Search for the End Of Central Directory. When a zip comment is + /* Search for the End Of Central Directory. When a zip comment is * present the directory may start earlier. - * FIXME: This searches the whole file in a very slow manner if the - * file isn't a zip file. */ long pos = raf.length() - ENDHDR; byte[] ebs = new byte[CENHDR]; + int centralDirOffset = 0; do { - if (pos < 0) + if (pos < 0 || centralDirOffset > 0xffff) throw new ZipException - ("central directory not found, probably not a zip file: " + name); + ("Central directory not found, probably not a zip file: " + name); raf.seek(pos--); + centralDirOffset++; } while (readLeInt(raf, ebs) != ENDSIG); @@ -256,9 +324,9 @@ int count = readLeShort(raf, ebs); if (raf.skipBytes(ENDOFF - ENDSIZ) != ENDOFF - ENDSIZ) throw new EOFException(name); - int centralOffset = readLeInt(raf, ebs); + long centralOffset = readLeInt(raf, ebs); - entries = new HashMap(count+count/2); + HashMap entries = new HashMap(count + count / 4); raf.seek(centralOffset); byte[] buffer = new byte[16]; @@ -269,28 +337,35 @@ throw new ZipException("Wrong Central Directory signature: " + name); int method = readLeShort(ebs, CENHOW); - int dostime = readLeInt(ebs, CENTIM); - int crc = readLeInt(ebs, CENCRC); - int csize = readLeInt(ebs, CENSIZ); - int size = readLeInt(ebs, CENLEN); + long dostime = readLeInt(ebs, CENTIM); + long crc = readLeInt(ebs, CENCRC); + long csize = readLeInt(ebs, CENSIZ); + long size = readLeInt(ebs, CENLEN); int nameLen = readLeShort(ebs, CENNAM); int extraLen = readLeShort(ebs, CENEXT); int commentLen = readLeShort(ebs, CENCOM); - int offset = readLeInt(ebs, CENOFF); + long offset = readLeInt(ebs, CENOFF); int needBuffer = Math.max(nameLen, commentLen); if (buffer.length < needBuffer) buffer = new byte[needBuffer]; raf.readFully(buffer, 0, nameLen); - String name = new String(buffer, 0, 0, nameLen); + + String name; + try { + name = new String(buffer, 0, nameLen, charSet); + } + catch (UnsupportedEncodingException cause) { + throw new AssertionError(cause); + } ZipEntry entry = new ZipEntry(name); entry.setMethod(method); - entry.setCrc(crc & 0xffffffffL); - entry.setSize(size & 0xffffffffL); - entry.setCompressedSize(csize & 0xffffffffL); + entry.setCrc(crc); + entry.setSize(size); + entry.setCompressedSize(csize); entry.setDOSTime(dostime); if (extraLen > 0) { @@ -301,106 +376,89 @@ if (commentLen > 0) { raf.readFully(buffer, 0, commentLen); - entry.setComment(new String(buffer, 0, commentLen)); + try { + entry.setComment(new String(buffer, 0, commentLen, charSet)); + } + catch (UnsupportedEncodingException cause) { + throw new AssertionError(cause); + } } entry.offset = offset; entries.put(name, entry); } + + return entries; } /** - * Closes the ZipFile. This also closes all input streams given by - * this class. After this is called, no further method should be + * Closes the ZipFile. This also closes all input streams given by + * this class. After this is called, no further method should be * called. * - * @exception IOException if a i/o error occured. + * @exception IOException If an i/o error occured. */ public void close() throws IOException { + if (closed) + return; synchronized (raf) { + if (raf != null) // Condition not required, but fail-safe + raf.close(); + if (entries != null) // Guard against call from constructor + entries.clear(); closed = true; - entries = null; - raf.close(); } } - /** - * Calls the close() method when this ZipFile has not yet - * been explicitly closed. + /*/ + * Ensures that the ZIP or JAR file is open. + * + * @exception IllegalStateException If the ZIP or JAR file has already been + * closed. */ - protected void finalize() throws IOException + private final void ensureOpen() { - if (!closed && raf != null) close(); + if (closed) + throw new IllegalStateException("ZIP or JAR file has already been closed: " + name); } - + /** - * Returns an enumeration of all Zip entries in this Zip file. - * - * @exception IllegalStateException when the ZipFile has already been closed + * Calls the close() method. */ - public Enumeration entries() + protected void finalize() throws IOException { - checkClosed(); - - try - { - return new ZipEntryEnumeration(getEntries().values().iterator()); - } - catch (IOException ioe) - { - return EmptyEnumeration.getInstance(); - } + close(); } /** - * Checks that the ZipFile is still open and reads entries when necessary. + * Returns an enumeration of all Zip entries in this ZIP or JAR file. * - * @exception IllegalStateException when the ZipFile has already been closed. - * @exception IOEexception when the entries could not be read. + * @exception IllegalStateException If the ZIP or JAR file has already been + * closed. */ - private HashMap getEntries() throws IOException - { - synchronized(raf) - { - checkClosed(); - - if (entries == null) - readEntries(); + public Enumeration entries() + { + ensureOpen(); + return new ZipEntryEnumeration(entries.values().iterator()); + } - return entries; - } - } /** * Searches for a zip entry in this archive with the given name. * - * @param the name. May contain directory components separated by - * slashes ('/'). - * @return the zip entry, or null if no entry with that name exists. + * @param name May contain directory components separated by slashes ('/'). + * @return The zip entry, or null if no entry with that name exists. * - * @exception IllegalStateException when the ZipFile has already been closed + * @exception IllegalStateException If the ZipFile has already been closed. */ public ZipEntry getEntry(String name) { - checkClosed(); - - try - { - HashMap entries = getEntries(); - ZipEntry entry = (ZipEntry) entries.get(name); - // If we didn't find it, maybe it's a directory. - if (entry == null && !name.endsWith("/")) - entry = (ZipEntry) entries.get(name + '/'); - return entry != null ? new ZipEntry(entry, name) : null; - } - catch (IOException ioe) - { - return null; - } + ensureOpen(); + ZipEntry entry = (ZipEntry) entries.get(name); + return entry != null ? (ZipEntry) entry.clone() : null; } - //access should be protected by synchronized(raf) private byte[] locBuf = new byte[LOCHDR]; @@ -429,7 +487,7 @@ throw new ZipException("Compression method mismatch: " + name); if (entry.getName().length() != readLeShort(locBuf, LOCNAM)) - throw new ZipException("file name length mismatch: " + name); + throw new ZipException("File name length mismatch: " + name); int extraLen = entry.getName().length() + readLeShort(locBuf, LOCEXT); return entry.offset + LOCHDR + extraLen; @@ -460,9 +518,31 @@ */ public InputStream getInputStream(ZipEntry entry) throws IOException { - checkClosed(); + return getInputStream(entry, new Inflater(true)); + } + + /** + * This is the actual implementation of address@hidden #getInputStream(ZipEntry)} + * - it allows you to specify a custom Inflater. + *

+ * Warning: This method is not part of the JDK API and + * should only be used if you absolutely know what you're doing! It is very + * easy to corrupt your input if you provide this method with the wrong + * parameters! + * + * @param entry The entry to create an InputStream for. + * @param inf An inflater with nowrap set to true. + * + * @return the input stream. + * + * @throws IOException If a i/o error occured. + * @throws ZipException If the Zip archive is malformed. + * @throws IllegalStateException If the ZIP file has been closed. + */ + protected final InputStream getInputStream(ZipEntry entry, InflaterIf inf) + throws IOException { + ensureOpen(); - HashMap entries = getEntries(); String name = entry.getName(); ZipEntry zipEntry = (ZipEntry) entries.get(name); if (zipEntry == null) @@ -477,7 +557,7 @@ case ZipOutputStream.STORED: return is; case ZipOutputStream.DEFLATED: - return new InflaterInputStream(is, new Inflater(true)); + return new PaddedInflaterInputStream(is, inf); default: throw new ZipException("Unknown compression method " + method); } @@ -492,22 +572,14 @@ } /** - * Returns the number of entries in this zip file. + * Returns the number of entries in this ZIP or JAR file. * - * @exception IllegalStateException when the ZipFile has already been closed + * @exception IllegalStateException If the ZipFile has already been closed. */ public int size() { - checkClosed(); - - try - { - return getEntries().size(); - } - catch (IOException ioe) - { - return 0; - } + ensureOpen(); + return entries.size(); } private static class ZipEntryEnumeration implements Enumeration @@ -592,4 +664,30 @@ return amount; } } + + /** + * Provides an extra "dummy" byte at the end of the input stream for + * address@hidden InflaterInputStream#fill()} + * as required by Sun's address@hidden Inflater#Inflater(boolean nowrap)}. + */ + private static class PaddedInflaterInputStream extends InflaterInputStream { + + private boolean eof; + + public PaddedInflaterInputStream(InputStream in, InflaterIf inf) { + super(in, inf); + } + + protected void fill() throws IOException { + if (eof) + throw new EOFException("Unexpected end of ZLIB input stream"); + if ((len = in.read(buf, 0, buf.length)) == -1) { + // Provide dummy byte + buf[0] = 0; + len = 1; + eof = true; + } + inf.setInput(buf, 0, len); + } + } } Index: java/util/zip/ZipInputStream.java =================================================================== RCS file: /cvsroot/classpath/classpath/java/util/zip/ZipInputStream.java,v retrieving revision 1.14 diff -u -r1.14 ZipInputStream.java --- java/util/zip/ZipInputStream.java 22 Oct 2004 18:02:06 -0000 1.14 +++ java/util/zip/ZipInputStream.java 4 May 2005 02:16:36 -0000 @@ -41,6 +41,7 @@ import java.io.EOFException; import java.io.IOException; import java.io.InputStream; +import java.io.UnsupportedEncodingException; /** * This is a FilterInputStream that reads the files in an zip archive @@ -51,25 +52,79 @@ * It includes support for STORED and DEFLATED entries. * * @author Jochen Hoenicke + * @author Christian Schlichtherle Added new constructors to support different + * character sets used to encode international entry names and comments. + * Added constructors to support different CRC32 and Inflater + * implementations. + * Fixed some bugs: Noteably the 2 GB Bug, so that Jazzlib now + * supports 4 GB which is the maximum with the ZIP32 format. + * Minor performance enhancements are also implemented. */ public class ZipInputStream extends InflaterInputStream implements ZipConstants { - private CRC32 crc = new CRC32(); + private final CRC32If crc; private ZipEntry entry = null; - private int csize; - private int size; + private long csize; + private long size; private int method; private int flags; private int avail; private boolean entryAtEOF; + private final String charSet; + /** * Creates a new Zip input stream, reading a zip archive. */ - public ZipInputStream(InputStream in) - { - super(in, new Inflater(true)); + public ZipInputStream(final InputStream in) { + this(in, new Inflater(true), new CRC32()); + } + + /** + * Creates a new Zip input stream, reading a zip archive. + * + * @param inf An inflater with nowrap set to true. + * @param crc The CRC23 to use. + */ + protected ZipInputStream(final InputStream in, + final InflaterIf inf, + final CRC32If crc) { + super(in, inf); + this.charSet = DEFAULT_CHARSET; + this.crc = crc; + } + + /** + * Creates a new Zip input stream, reading a zip archive. + *

+ * Warning: This constructor is not part of the JDK API! + * + * @param charSet The character set to use for entry names. + */ + public ZipInputStream(final InputStream in, final String charSet) + throws UnsupportedEncodingException { + this(in, charSet, new Inflater(true), new CRC32()); + } + + /** + * Creates a new Zip input stream, reading a zip archive. + *

+ * Warning: This constructor is not part of the JDK API! + * + * @param charSet The character set to use for entry names. + * @param inf An inflater with nowrap set to true. + * @param crc The CRC23 to use. + */ + protected ZipInputStream(final InputStream in, + final String charSet, + final InflaterIf inf, + final CRC32If crc) + throws UnsupportedEncodingException { + super(in, inf); + this.charSet = charSet; + new String("").getBytes(charSet); // Fail-fast + this.crc = crc; } private void fillBuf() throws IOException @@ -106,7 +161,7 @@ } } - private int readLeByte() throws IOException + private final int readLeByte() throws IOException { if (avail <= 0) { @@ -120,7 +175,7 @@ /** * Read an unsigned short in little endian byte order. */ - private int readLeShort() throws IOException + private final int readLeShort() throws IOException { return readLeByte() | (readLeByte() << 8); } @@ -128,9 +183,9 @@ /** * Read an int in little endian byte order. */ - private int readLeInt() throws IOException + private final long readLeInt() throws IOException { - return readLeShort() | (readLeShort() << 16); + return readLeShort() | ((long) readLeShort() << 16); } /** @@ -144,7 +199,7 @@ if (entry != null) closeEntry(); - int header = readLeInt(); + long header = readLeInt(); if (header == CENSIG) { /* Central Header reached. */ @@ -153,13 +208,13 @@ } if (header != LOCSIG) throw new ZipException("Wrong Local header signature: " - + Integer.toHexString(header)); + + Long.toHexString(header)); /* skip version */ readLeShort(); flags = readLeShort(); method = readLeShort(); - int dostime = readLeInt(); - int crc = readLeInt(); + long dostime = readLeInt(); + long crc = readLeInt(); csize = readLeInt(); size = readLeInt(); int nameLen = readLeShort(); @@ -171,16 +226,23 @@ byte[] buffer = new byte[nameLen]; readFully(buffer); - String name = new String(buffer); + //String name = new String(buffer); + String name; + try { + name = new String(buffer, 0, nameLen, charSet); + } + catch (UnsupportedEncodingException cause) { + throw new AssertionError(cause); + } entry = createZipEntry(name); entryAtEOF = false; entry.setMethod(method); if ((flags & 8) == 0) { - entry.setCrc(crc & 0xffffffffL); - entry.setSize(size & 0xffffffffL); - entry.setCompressedSize(csize & 0xffffffffL); + entry.setCrc(crc); + entry.setSize(size); + entry.setCompressedSize(csize); } entry.setDOSTime(dostime); if (extraLen > 0) @@ -204,11 +266,11 @@ { if (readLeInt() != EXTSIG) throw new ZipException("Data descriptor signature not found"); - entry.setCrc(readLeInt() & 0xffffffffL); + entry.setCrc(readLeInt()); csize = readLeInt(); size = readLeInt(); - entry.setSize(size & 0xffffffffL); - entry.setCompressedSize(csize & 0xffffffffL); + entry.setSize(size); + entry.setCompressedSize(csize); } /** @@ -226,7 +288,7 @@ if ((flags & 8) != 0) { /* We don't know how much we must skip, read until end. */ - byte[] tmp = new byte[2048]; + byte[] tmp = new byte[4096]; while (read(tmp) > 0) ; /* read will close this entry */ @@ -244,7 +306,7 @@ avail = 0; while (csize != 0) { - long skipped = in.skip(csize & 0xffffffffL); + long skipped = in.skip(csize); if (skipped <= 0) throw new ZipException("zip archive ends early."); csize -= skipped; @@ -316,8 +378,8 @@ case ZipOutputStream.STORED: - if (len > csize && csize >= 0) - len = csize; + if ((long) len > csize && csize >= 0) + len = (int) csize; len = readBuf(b, off, len); if (len > 0) @@ -338,7 +400,7 @@ if (finished) { - if ((crc.getValue() & 0xffffffffL) != entry.getCrc()) + if (crc.getValue() != entry.getCrc()) throw new ZipException("CRC mismatch"); crc.reset(); entry = null; @@ -354,7 +416,7 @@ public void close() throws IOException { super.close(); - crc = null; + crc.reset(); entry = null; entryAtEOF = true; } Index: java/util/zip/ZipOutputStream.java =================================================================== RCS file: /cvsroot/classpath/classpath/java/util/zip/ZipOutputStream.java,v retrieving revision 1.9 diff -u -r1.9 ZipOutputStream.java --- java/util/zip/ZipOutputStream.java 22 Oct 2004 18:02:07 -0000 1.9 +++ java/util/zip/ZipOutputStream.java 4 May 2005 02:16:37 -0000 @@ -42,6 +42,7 @@ import java.io.OutputStream; import java.util.Enumeration; import java.util.Vector; +import java.io.UnsupportedEncodingException; /** * This is a FilterOutputStream that writes the files into a zip @@ -54,11 +55,18 @@ * This class is not thread safe. * * @author Jochen Hoenicke + * @author Christian Schlichtherle Added new constructors to support different + * character sets used to encode international entry names and comments. + * Added constructors to support different CRC32 and Deflater + * implementations. + * Fixed some bugs: Noteably the 2 GB Bug, so that Jazzlib now + * supports 4 GB which is the maximum with the ZIP32 format. + * Minor performance enhancements are also implemented. */ public class ZipOutputStream extends DeflaterOutputStream implements ZipConstants { private Vector entries = new Vector(); - private CRC32 crc = new CRC32(); + private final CRC32If crc; private ZipEntry curEntry = null; private int curMethod; @@ -68,6 +76,8 @@ private byte[] zipComment = new byte[0]; private int defaultMethod = DEFLATED; + private final String charSet; + /** * Our Zip version is hard coded to 1.0 resp. 2.0 */ @@ -86,11 +96,69 @@ /** * Creates a new Zip output stream, writing a zip archive. - * @param out the output stream to which the zip archive is written. + * + * @param out The output stream to which the zip archive is written. */ - public ZipOutputStream(OutputStream out) - { - super(out, new Deflater(Deflater.DEFAULT_COMPRESSION, true)); + public ZipOutputStream(final OutputStream out) { + this(out, new Deflater(Deflater.DEFAULT_COMPRESSION, true), new CRC32()); + } + + /** + * Creates a new Zip output stream, writing a zip archive. + *

+ * Warning: This constructor is not part of the JDK API and + * should only be used if you absolutely know what you're doing! It is very + * easy to corrupt your ZIP files if you provide this method with the wrong + * parameters! + * + * @param out The output stream to which the zip archive is written. + * @param def A deflater with nowrap set to true. + * @param crc The CRC23 to use. + */ + protected ZipOutputStream(final OutputStream out, + final DeflaterIf def, + final CRC32If crc) { + super(out, def); + this.charSet = DEFAULT_CHARSET; + this.crc = crc; + } + + /** + * Creates a new Zip output stream, writing a zip archive. + *

+ * Warning: This constructor is not part of the JDK API! + * + * @param out The output stream to which the zip archive is written. + * @param charSet The character set to use for entry names and comments. + */ + public ZipOutputStream(final OutputStream out, final String charSet) + throws UnsupportedEncodingException { + this(out, charSet, + new Deflater(Deflater.DEFAULT_COMPRESSION, true), new CRC32()); + } + + /** + * Creates a new Zip output stream, writing a zip archive. + *

+ * Warning: This method is not part of the JDK API and + * should only be used if you absolutely know what you're doing! It is very + * easy to corrupt your ZIP files if you provide this method with the wrong + * parameters! + * + * @param out The output stream to which the zip archive is written. + * @param charSet The character set to use for entry names and comments. + * @param def A deflater with nowrap set to true. + * @param crc The CRC23 to use. + */ + protected ZipOutputStream(final OutputStream out, + final String charSet, + final DeflaterIf def, + final CRC32If crc) + throws UnsupportedEncodingException { + super(out, def); + this.charSet = charSet; + new String("").getBytes(charSet); // Fail-fast + this.crc = crc; } /** @@ -102,7 +170,12 @@ public void setComment(String comment) { byte[] commentBytes; - commentBytes = comment.getBytes(); + try { + commentBytes = comment.getBytes(charSet); + } + catch (UnsupportedEncodingException cause) { + throw new AssertionError(cause); + } if (commentBytes.length > 0xffff) throw new IllegalArgumentException("Comment too long."); zipComment = commentBytes; @@ -137,7 +210,7 @@ /** * Write an unsigned short in little endian byte order. */ - private void writeLeShort(int value) throws IOException + private final void writeLeShort(int value) throws IOException { out.write(value & 0xff); out.write((value >> 8) & 0xff); @@ -146,10 +219,10 @@ /** * Write an int in little endian byte order. */ - private void writeLeInt(int value) throws IOException + private final void writeLeInt(long value) throws IOException { - writeLeShort(value); - writeLeShort(value >> 16); + writeLeShort((int) (value & 0xffff)); + writeLeShort((int) ((value >> 16) & 0xffff)); } /** @@ -216,9 +289,9 @@ writeLeInt(entry.getDOSTime()); if ((flags & 8) == 0) { - writeLeInt((int)entry.getCrc()); - writeLeInt((int)entry.getCompressedSize()); - writeLeInt((int)entry.getSize()); + writeLeInt(entry.getCrc()); + writeLeInt(entry.getCompressedSize()); + writeLeInt(entry.getSize()); } else { @@ -226,7 +299,13 @@ writeLeInt(0); writeLeInt(0); } - byte[] name = entry.getName().getBytes(); + byte[] name; + try { + name = entry.getName().getBytes(charSet); + } + catch (UnsupportedEncodingException cause) { + throw new AssertionError(cause); + } if (name.length > 0xffff) throw new ZipException("Name too long."); byte[] extra = entry.getExtra(); @@ -289,9 +368,9 @@ if (curMethod == DEFLATED && (curEntry.flags & 8) != 0) { writeLeInt(EXTSIG); - writeLeInt((int)curEntry.getCrc()); - writeLeInt((int)curEntry.getCompressedSize()); - writeLeInt((int)curEntry.getSize()); + writeLeInt(curEntry.getCrc()); + writeLeInt(curEntry.getCompressedSize()); + writeLeInt(curEntry.getSize()); offset += EXTHDR; } @@ -339,10 +418,10 @@ int numEntries = 0; int sizeEntries = 0; - Enumeration e = entries.elements(); - while (e.hasMoreElements()) + Enumeration enumeration = entries.elements(); + while (enumeration.hasMoreElements()) { - ZipEntry entry = (ZipEntry) e.nextElement(); + ZipEntry entry = (ZipEntry) enumeration.nextElement(); int method = entry.getMethod(); writeLeInt(CENSIG); @@ -353,19 +432,31 @@ writeLeShort(entry.flags); writeLeShort(method); writeLeInt(entry.getDOSTime()); - writeLeInt((int)entry.getCrc()); - writeLeInt((int)entry.getCompressedSize()); - writeLeInt((int)entry.getSize()); - - byte[] name = entry.getName().getBytes(); + writeLeInt(entry.getCrc()); + writeLeInt(entry.getCompressedSize()); + writeLeInt(entry.getSize()); + + byte[] name; + try { + name = entry.getName().getBytes(charSet); + } + catch (UnsupportedEncodingException cause) { + throw new AssertionError(cause); + } if (name.length > 0xffff) throw new ZipException("Name too long."); byte[] extra = entry.getExtra(); if (extra == null) extra = new byte[0]; String strComment = entry.getComment(); - byte[] comment = strComment != null - ? strComment.getBytes() : new byte[0]; + byte[] comment; + try { + comment = strComment != null + ? strComment.getBytes(charSet) : new byte[0]; + } + catch (UnsupportedEncodingException cause) { + throw new AssertionError(cause); + } if (comment.length > 0xffff) throw new ZipException("Comment too long."); Index: lib/jazzlib/makeJazzlibDist.sh =================================================================== RCS file: /cvsroot/classpath/classpath/lib/jazzlib/makeJazzlibDist.sh,v retrieving revision 1.5 diff -u -r1.5 makeJazzlibDist.sh --- lib/jazzlib/makeJazzlibDist.sh 13 May 2004 12:50:14 -0000 1.5 +++ lib/jazzlib/makeJazzlibDist.sh 4 May 2005 02:16:57 -0000 @@ -12,7 +12,7 @@ echo "----- Do rm -rf dist when you're finished -----" echo "----- 30 May 2002 John Leuner -----" -RELEASE_NUMBER=07 +RELEASE_NUMBER=08 # $1 is the archive command, eg "tar czvf" or "zip" or "jar cf" # $2 is the archive suffix, eg ".zip" or ".tar.gz"