summaryrefslogtreecommitdiff
path: root/libjava/classpath/java/util/zip
diff options
context:
space:
mode:
authorupstream source tree <ports@midipix.org>2015-03-15 20:14:05 -0400
committerupstream source tree <ports@midipix.org>2015-03-15 20:14:05 -0400
commit554fd8c5195424bdbcabf5de30fdc183aba391bd (patch)
tree976dc5ab7fddf506dadce60ae936f43f58787092 /libjava/classpath/java/util/zip
downloadcbb-gcc-4.6.4-554fd8c5195424bdbcabf5de30fdc183aba391bd.tar.bz2
cbb-gcc-4.6.4-554fd8c5195424bdbcabf5de30fdc183aba391bd.tar.xz
obtained gcc-4.6.4.tar.bz2 from upstream website;upstream
verified gcc-4.6.4.tar.bz2.sig; imported gcc-4.6.4 source tree from verified upstream tarball. downloading a git-generated archive based on the 'upstream' tag should provide you with a source tree that is binary identical to the one extracted from the above tarball. if you have obtained the source via the command 'git clone', however, do note that line-endings of files in your working directory might differ from line-endings of the respective files in the upstream repository.
Diffstat (limited to 'libjava/classpath/java/util/zip')
-rw-r--r--libjava/classpath/java/util/zip/Adler32.java205
-rw-r--r--libjava/classpath/java/util/zip/CRC32.java132
-rw-r--r--libjava/classpath/java/util/zip/CheckedInputStream.java135
-rw-r--r--libjava/classpath/java/util/zip/CheckedOutputStream.java100
-rw-r--r--libjava/classpath/java/util/zip/Checksum.java86
-rw-r--r--libjava/classpath/java/util/zip/DataFormatException.java71
-rw-r--r--libjava/classpath/java/util/zip/Deflater.java537
-rw-r--r--libjava/classpath/java/util/zip/DeflaterConstants.java78
-rw-r--r--libjava/classpath/java/util/zip/DeflaterEngine.java698
-rw-r--r--libjava/classpath/java/util/zip/DeflaterHuffman.java776
-rw-r--r--libjava/classpath/java/util/zip/DeflaterOutputStream.java198
-rw-r--r--libjava/classpath/java/util/zip/DeflaterPending.java53
-rw-r--r--libjava/classpath/java/util/zip/GZIPInputStream.java355
-rw-r--r--libjava/classpath/java/util/zip/GZIPOutputStream.java151
-rw-r--r--libjava/classpath/java/util/zip/Inflater.java726
-rw-r--r--libjava/classpath/java/util/zip/InflaterDynHeader.java203
-rw-r--r--libjava/classpath/java/util/zip/InflaterHuffmanTree.java217
-rw-r--r--libjava/classpath/java/util/zip/InflaterInputStream.java265
-rw-r--r--libjava/classpath/java/util/zip/OutputWindow.java175
-rw-r--r--libjava/classpath/java/util/zip/PendingBuffer.java199
-rw-r--r--libjava/classpath/java/util/zip/StreamManipulator.java215
-rw-r--r--libjava/classpath/java/util/zip/ZipConstants.java93
-rw-r--r--libjava/classpath/java/util/zip/ZipEntry.java457
-rw-r--r--libjava/classpath/java/util/zip/ZipException.java72
-rw-r--r--libjava/classpath/java/util/zip/ZipFile.java782
-rw-r--r--libjava/classpath/java/util/zip/ZipInputStream.java381
-rw-r--r--libjava/classpath/java/util/zip/ZipOutputStream.java440
-rw-r--r--libjava/classpath/java/util/zip/package.html47
28 files changed, 7847 insertions, 0 deletions
diff --git a/libjava/classpath/java/util/zip/Adler32.java b/libjava/classpath/java/util/zip/Adler32.java
new file mode 100644
index 000000000..cc27da927
--- /dev/null
+++ b/libjava/classpath/java/util/zip/Adler32.java
@@ -0,0 +1,205 @@
+/* Adler32.java - Computes Adler32 data checksum of a data stream
+ Copyright (C) 1999, 2000, 2001 Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath 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 for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module. An independent module is a module which is not derived from
+or based on this library. If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so. If you do not wish to do so, delete this
+exception statement from your version. */
+
+package java.util.zip;
+
+/*
+ * Written using on-line Java Platform 1.2 API Specification, as well
+ * as "The Java Class Libraries", 2nd edition (Addison-Wesley, 1998).
+ * The actual Adler32 algorithm is taken from RFC 1950.
+ * Status: Believed complete and correct.
+ */
+
+/**
+ * Computes Adler32 checksum for a stream of data. An Adler32
+ * checksum is not as reliable as a CRC32 checksum, but a lot faster to
+ * compute.
+ *<p>
+ * The specification for Adler32 may be found in RFC 1950.
+ * (ZLIB Compressed Data Format Specification version 3.3)
+ *<p>
+ *<p>
+ * From that document:
+ *<p>
+ * "ADLER32 (Adler-32 checksum)
+ * This contains a checksum value of the uncompressed data
+ * (excluding any dictionary data) computed according to Adler-32
+ * algorithm. This algorithm is a 32-bit extension and improvement
+ * of the Fletcher algorithm, used in the ITU-T X.224 / ISO 8073
+ * standard.
+ *<p>
+ * Adler-32 is composed of two sums accumulated per byte: s1 is
+ * the sum of all bytes, s2 is the sum of all s1 values. Both sums
+ * are done modulo 65521. s1 is initialized to 1, s2 to zero. The
+ * Adler-32 checksum is stored as s2*65536 + s1 in most-
+ * significant-byte first (network) order."
+ *<p>
+ * "8.2. The Adler-32 algorithm
+ *<p>
+ * The Adler-32 algorithm is much faster than the CRC32 algorithm yet
+ * still provides an extremely low probability of undetected errors.
+ *<p>
+ * The modulo on unsigned long accumulators can be delayed for 5552
+ * bytes, so the modulo operation time is negligible. If the bytes
+ * are a, b, c, the second sum is 3a + 2b + c + 3, and so is position
+ * and order sensitive, unlike the first sum, which is just a
+ * checksum. That 65521 is prime is important to avoid a possible
+ * large class of two-byte errors that leave the check unchanged.
+ * (The Fletcher checksum uses 255, which is not prime and which also
+ * makes the Fletcher check insensitive to single byte changes 0 <->
+ * 255.)
+ *<p>
+ * The sum s1 is initialized to 1 instead of zero to make the length
+ * of the sequence part of s2, so that the length does not have to be
+ * checked separately. (Any sequence of zeroes has a Fletcher
+ * checksum of zero.)"
+ *
+ * @author John Leuner, Per Bothner
+ * @since JDK 1.1
+ *
+ * @see InflaterInputStream
+ * @see DeflaterOutputStream
+ */
+public class Adler32 implements Checksum
+{
+
+ /** largest prime smaller than 65536 */
+ private static final int BASE = 65521;
+
+ private int checksum; //we do all in int.
+
+ //Note that java doesn't have unsigned integers,
+ //so we have to be careful with what arithmetic
+ //we do. We return the checksum as a long to
+ //avoid sign confusion.
+
+ /**
+ * Creates a new instance of the <code>Adler32</code> class.
+ * The checksum starts off with a value of 1.
+ */
+ public Adler32 ()
+ {
+ reset();
+ }
+
+ /**
+ * Resets the Adler32 checksum to the initial value.
+ */
+ public void reset ()
+ {
+ checksum = 1; //Initialize to 1
+ }
+
+ /**
+ * Updates the checksum with the byte b.
+ *
+ * @param bval the data value to add. The high byte of the int is ignored.
+ */
+ public void update (int bval)
+ {
+ //We could make a length 1 byte array and call update again, but I
+ //would rather not have that overhead
+ int s1 = checksum & 0xffff;
+ int s2 = checksum >>> 16;
+
+ s1 = (s1 + (bval & 0xFF)) % BASE;
+ s2 = (s1 + s2) % BASE;
+
+ checksum = (s2 << 16) + s1;
+ }
+
+ /**
+ * Updates the checksum with the bytes taken from the array.
+ *
+ * @param buffer an array of bytes
+ */
+ public void update (byte[] buffer)
+ {
+ update(buffer, 0, buffer.length);
+ }
+
+ /**
+ * Updates the checksum with the bytes taken from the array.
+ *
+ * @param buf an array of bytes
+ * @param off the start of the data used for this update
+ * @param len the number of bytes to use for this update
+ */
+ public void update (byte[] buf, int off, int len)
+ {
+ //(By Per Bothner)
+ 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;
+ }
+ 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;
+ }
+
+ /**
+ * Returns the Adler32 data checksum computed so far.
+ */
+ public long getValue()
+ {
+ return (long) checksum & 0xffffffffL;
+ }
+}
diff --git a/libjava/classpath/java/util/zip/CRC32.java b/libjava/classpath/java/util/zip/CRC32.java
new file mode 100644
index 000000000..de2e7083d
--- /dev/null
+++ b/libjava/classpath/java/util/zip/CRC32.java
@@ -0,0 +1,132 @@
+/* CRC32.java - Computes CRC32 data checksum of a data stream
+ Copyright (C) 1999. 2000, 2001 Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath 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 for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module. An independent module is a module which is not derived from
+or based on this library. If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so. If you do not wish to do so, delete this
+exception statement from your version. */
+
+package java.util.zip;
+
+/*
+ * Written using on-line Java Platform 1.2 API Specification, as well
+ * as "The Java Class Libraries", 2nd edition (Addison-Wesley, 1998).
+ * The actual CRC32 algorithm is taken from RFC 1952.
+ * Status: Believed complete and correct.
+ */
+
+/**
+ * Computes CRC32 data checksum of a data stream.
+ * The actual CRC32 algorithm is described in RFC 1952
+ * (GZIP file format specification version 4.3).
+ * Can be used to get the CRC32 over a stream if used with checked input/output
+ * streams.
+ *
+ * @see InflaterInputStream
+ * @see DeflaterOutputStream
+ *
+ * @author Per Bothner
+ * @date April 1, 1999.
+ */
+public class CRC32 implements Checksum
+{
+ /** The crc data checksum so far. */
+ private int crc = 0;
+
+ /** The fast CRC table. Computed once when the CRC32 class is loaded. */
+ private static int[] crc_table = make_crc_table();
+
+ /** Make the table for a fast CRC. */
+ private static int[] make_crc_table ()
+ {
+ int[] crc_table = new int[256];
+ for (int n = 0; n < 256; n++)
+ {
+ int c = n;
+ for (int k = 8; --k >= 0; )
+ {
+ if ((c & 1) != 0)
+ c = 0xedb88320 ^ (c >>> 1);
+ else
+ c = c >>> 1;
+ }
+ crc_table[n] = c;
+ }
+ return crc_table;
+ }
+
+ /**
+ * Returns the CRC32 data checksum computed so far.
+ */
+ public long getValue ()
+ {
+ return (long) crc & 0xffffffffL;
+ }
+
+ /**
+ * Resets the CRC32 data checksum as if no update was ever called.
+ */
+ public void reset () { crc = 0; }
+
+ /**
+ * Updates the checksum with the int bval.
+ *
+ * @param bval (the byte is taken as the lower 8 bits of bval)
+ */
+
+ public void update (int bval)
+ {
+ int c = ~crc;
+ c = crc_table[(c ^ bval) & 0xff] ^ (c >>> 8);
+ crc = ~c;
+ }
+
+ /**
+ * Adds the byte array to the data checksum.
+ *
+ * @param buf the buffer which contains the data
+ * @param off the offset in the buffer where the data starts
+ * @param len the length of the data
+ */
+ public void update (byte[] buf, int off, int len)
+ {
+ int c = ~crc;
+ while (--len >= 0)
+ c = crc_table[(c ^ buf[off++]) & 0xff] ^ (c >>> 8);
+ crc = ~c;
+ }
+
+ /**
+ * Adds the complete byte array to the data checksum.
+ */
+ public void update (byte[] buf) { update(buf, 0, buf.length); }
+}
diff --git a/libjava/classpath/java/util/zip/CheckedInputStream.java b/libjava/classpath/java/util/zip/CheckedInputStream.java
new file mode 100644
index 000000000..163a4c4aa
--- /dev/null
+++ b/libjava/classpath/java/util/zip/CheckedInputStream.java
@@ -0,0 +1,135 @@
+/* CheckedInputStream.java - Compute checksum of data being read
+ Copyright (C) 1999, 2000, 2004 Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath 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 for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module. An independent module is a module which is not derived from
+or based on this library. If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so. If you do not wish to do so, delete this
+exception statement from your version. */
+
+
+package java.util.zip;
+
+import java.io.FilterInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+
+/* Written using on-line Java Platform 1.2 API Specification
+ * and JCL book.
+ * Believed complete and correct.
+ */
+
+/**
+ * InputStream that computes a checksum of the data being read using a
+ * supplied Checksum object.
+ *
+ * @see Checksum
+ *
+ * @author Tom Tromey
+ * @date May 17, 1999
+ */
+public class CheckedInputStream extends FilterInputStream
+{
+ /**
+ * Creates a new CheckInputStream on top of the supplied OutputStream
+ * using the supplied Checksum.
+ */
+ public CheckedInputStream (InputStream in, Checksum sum)
+ {
+ super (in);
+ this.sum = sum;
+ }
+
+ /**
+ * Returns the Checksum object used. To get the data checksum computed so
+ * far call <code>getChecksum.getValue()</code>.
+ */
+ public Checksum getChecksum ()
+ {
+ return sum;
+ }
+
+ /**
+ * Reads one byte, updates the checksum and returns the read byte
+ * (or -1 when the end of file was reached).
+ */
+ public int read () throws IOException
+ {
+ int x = in.read();
+ if (x != -1)
+ sum.update(x);
+ return x;
+ }
+
+ /**
+ * Reads at most len bytes in the supplied buffer and updates the checksum
+ * with it. Returns the number of bytes actually read or -1 when the end
+ * of file was reached.
+ */
+ public int read (byte[] buf, int off, int len) throws IOException
+ {
+ int r = in.read(buf, off, len);
+ if (r != -1)
+ sum.update(buf, off, r);
+ return r;
+ }
+
+ /**
+ * Skips n bytes by reading them in a temporary buffer and updating the
+ * the checksum with that buffer. Returns the actual number of bytes skiped
+ * which can be less then requested when the end of file is reached.
+ */
+ public long skip (long n) throws IOException
+ {
+ if (n == 0)
+ return 0;
+
+ int min = (int) Math.min(n, 1024);
+ byte[] buf = new byte[min];
+
+ long s = 0;
+ while (n > 0)
+ {
+ int r = in.read(buf, 0, min);
+ if (r == -1)
+ break;
+ n -= r;
+ s += r;
+ min = (int) Math.min(n, 1024);
+ sum.update(buf, 0, r);
+ }
+
+ return s;
+ }
+
+ /** The checksum object. */
+ private Checksum sum;
+}
diff --git a/libjava/classpath/java/util/zip/CheckedOutputStream.java b/libjava/classpath/java/util/zip/CheckedOutputStream.java
new file mode 100644
index 000000000..e0222258a
--- /dev/null
+++ b/libjava/classpath/java/util/zip/CheckedOutputStream.java
@@ -0,0 +1,100 @@
+/* CheckedOutputStream.java - Compute checksum of data being written.
+ Copyright (C) 1999, 2000 Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath 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 for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module. An independent module is a module which is not derived from
+or based on this library. If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so. If you do not wish to do so, delete this
+exception statement from your version. */
+
+
+package java.util.zip;
+
+import java.io.FilterOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+
+/* Written using on-line Java Platform 1.2 API Specification
+ * and JCL book.
+ * Believed complete and correct.
+ */
+
+/**
+ * OutputStream that computes a checksum of data being written using a
+ * supplied Checksum object.
+ *
+ * @see Checksum
+ *
+ * @author Tom Tromey
+ * @date May 17, 1999
+ */
+public class CheckedOutputStream extends FilterOutputStream
+{
+ /**
+ * Creates a new CheckInputStream on top of the supplied OutputStream
+ * using the supplied Checksum.
+ */
+ public CheckedOutputStream (OutputStream out, Checksum cksum)
+ {
+ super (out);
+ this.sum = cksum;
+ }
+
+ /**
+ * Returns the Checksum object used. To get the data checksum computed so
+ * far call <code>getChecksum.getValue()</code>.
+ */
+ public Checksum getChecksum ()
+ {
+ return sum;
+ }
+
+ /**
+ * Writes one byte to the OutputStream and updates the Checksum.
+ */
+ public void write (int bval) throws IOException
+ {
+ out.write(bval);
+ sum.update(bval);
+ }
+
+ /**
+ * Writes the byte array to the OutputStream and updates the Checksum.
+ */
+ public void write (byte[] buf, int off, int len) throws IOException
+ {
+ out.write(buf, off, len);
+ sum.update(buf, off, len);
+ }
+
+ /** The checksum object. */
+ private Checksum sum;
+}
diff --git a/libjava/classpath/java/util/zip/Checksum.java b/libjava/classpath/java/util/zip/Checksum.java
new file mode 100644
index 000000000..3342ba3a6
--- /dev/null
+++ b/libjava/classpath/java/util/zip/Checksum.java
@@ -0,0 +1,86 @@
+/* Checksum.java - Interface to compute a data checksum
+ Copyright (C) 1999, 2000, 2001 Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath 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 for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module. An independent module is a module which is not derived from
+or based on this library. If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so. If you do not wish to do so, delete this
+exception statement from your version. */
+
+package java.util.zip;
+
+/*
+ * Written using on-line Java Platform 1.2 API Specification, as well
+ * as "The Java Class Libraries", 2nd edition (Addison-Wesley, 1998).
+ * Status: Believed complete and correct.
+ */
+
+/**
+ * Interface to compute a data checksum used by checked input/output streams.
+ * A data checksum can be updated by one byte or with a byte array. After each
+ * update the value of the current checksum can be returned by calling
+ * <code>getValue</code>. The complete checksum object can also be reset
+ * so it can be used again with new data.
+ *
+ * @see CheckedInputStream
+ * @see CheckedOutputStream
+ *
+ * @author Per Bothner
+ * @author Jochen Hoenicke
+ */
+public interface Checksum
+{
+ /**
+ * Returns the data checksum computed so far.
+ */
+ long getValue();
+
+ /**
+ * Resets the data checksum as if no update was ever called.
+ */
+ void reset();
+
+ /**
+ * Adds one byte to the data checksum.
+ *
+ * @param bval the data value to add. The high byte of the int is ignored.
+ */
+ void update (int bval);
+
+ /**
+ * Adds the byte array to the data checksum.
+ *
+ * @param buf the buffer which contains the data
+ * @param off the offset in the buffer where the data starts
+ * @param len the length of the data
+ */
+ void update (byte[] buf, int off, int len);
+}
diff --git a/libjava/classpath/java/util/zip/DataFormatException.java b/libjava/classpath/java/util/zip/DataFormatException.java
new file mode 100644
index 000000000..dc5b10dec
--- /dev/null
+++ b/libjava/classpath/java/util/zip/DataFormatException.java
@@ -0,0 +1,71 @@
+/* DataformatException.java -- thrown when compressed data is corrupt
+ Copyright (C) 1999, 2000, 2001, 2002 Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath 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 for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module. An independent module is a module which is not derived from
+or based on this library. If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so. If you do not wish to do so, delete this
+exception statement from your version. */
+
+package java.util.zip;
+
+/**
+ * Exception thrown when compressed data is corrupt.
+ *
+ * @author Tom Tromey
+ * @author John Leuner
+ * @since 1.1
+ * @status updated to 1.4
+ */
+public class DataFormatException extends Exception
+{
+ /**
+ * Compatible with JDK 1.1+.
+ */
+ private static final long serialVersionUID = 2219632870893641452L;
+
+ /**
+ * Create an exception without a message.
+ */
+ public DataFormatException()
+ {
+ }
+
+ /**
+ * Create an exception with a message.
+ *
+ * @param msg the message
+ */
+ public DataFormatException(String msg)
+ {
+ super(msg);
+ }
+}
diff --git a/libjava/classpath/java/util/zip/Deflater.java b/libjava/classpath/java/util/zip/Deflater.java
new file mode 100644
index 000000000..dd81fe748
--- /dev/null
+++ b/libjava/classpath/java/util/zip/Deflater.java
@@ -0,0 +1,537 @@
+/* Deflater.java - Compress a data stream
+ Copyright (C) 1999, 2000, 2001, 2004, 2005 Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath 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 for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module. An independent module is a module which is not derived from
+or based on this library. If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so. If you do not wish to do so, delete this
+exception statement from your version. */
+
+package java.util.zip;
+
+/**
+ * This is the Deflater class. The deflater class compresses input
+ * with the deflate algorithm described in RFC 1951. It has several
+ * compression levels and three different strategies described below.
+ *
+ * This class is <i>not</i> thread safe. This is inherent in the API, due
+ * to the split of deflate and setInput.
+ *
+ * @author Jochen Hoenicke
+ * @author Tom Tromey
+ */
+public class Deflater
+{
+ /**
+ * 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:
+ *
+ * (1) -> INIT_STATE ----> INIT_FINISHING_STATE ---.
+ * / | (2) (5) |
+ * / v (5) |
+ * (3)| SETDICT_STATE ---> SETDICT_FINISHING_STATE |(3)
+ * \ | (3) | ,-------'
+ * | | | (3) /
+ * v v (5) v v
+ * (1) -> BUSY_STATE ----> FINISHING_STATE
+ * | (6)
+ * v
+ * FINISHED_STATE
+ * \_____________________________________/
+ * | (7)
+ * v
+ * CLOSED_STATE
+ *
+ * (1) If we should produce a header we start in INIT_STATE, otherwise
+ * we start in BUSY_STATE.
+ * (2) A dictionary may be set only when we are in INIT_STATE, then
+ * we change the state as indicated.
+ * (3) Whether a dictionary is set or not, on the first call of deflate
+ * we change to BUSY_STATE.
+ * (4) -- intentionally left blank -- :)
+ * (5) FINISHING_STATE is entered, when flush() is called to indicate that
+ * there is no more INPUT. There are also states indicating, that
+ * the header wasn't written yet.
+ * (6) FINISHED_STATE is entered, when everything has been flushed to the
+ * internal pending output buffer.
+ * (7) At any time (7)
+ *
+ */
+
+ private static final int IS_SETDICT = 0x01;
+ private static final int IS_FLUSHING = 0x04;
+ private static final int IS_FINISHING = 0x08;
+
+ private static final int INIT_STATE = 0x00;
+ private static final int SETDICT_STATE = 0x01;
+ private static final int INIT_FINISHING_STATE = 0x08;
+ private static final int SETDICT_FINISHING_STATE = 0x09;
+ private static final int BUSY_STATE = 0x10;
+ private static final int FLUSHING_STATE = 0x14;
+ private static final int FINISHING_STATE = 0x1c;
+ private static final int FINISHED_STATE = 0x1e;
+ private static final int CLOSED_STATE = 0x7f;
+
+ /** Compression level. */
+ private int level;
+
+ /** should we include a header. */
+ private boolean noHeader;
+
+ /** The current state. */
+ private int state;
+
+ /** The total bytes of output written. */
+ private long totalOut;
+
+ /** The pending output. */
+ private DeflaterPending pending;
+
+ /** The deflater engine. */
+ private DeflaterEngine engine;
+
+ /**
+ * Creates a new deflater with default compression level.
+ */
+ public Deflater()
+ {
+ this(DEFAULT_COMPRESSION, false);
+ }
+
+ /**
+ * Creates a new deflater with given compression level.
+ * @param lvl the compression level, a value between NO_COMPRESSION
+ * and BEST_COMPRESSION, or DEFAULT_COMPRESSION.
+ * @exception IllegalArgumentException if lvl is out of range.
+ */
+ public Deflater(int lvl)
+ {
+ this(lvl, false);
+ }
+
+ /**
+ * Creates a new deflater with given compression level.
+ * @param lvl the compression level, a value between NO_COMPRESSION
+ * and BEST_COMPRESSION.
+ * @param nowrap true, iff we should suppress the deflate header at the
+ * beginning and the adler checksum at the end of the output. This is
+ * useful for the GZIP format.
+ * @exception IllegalArgumentException if lvl is out of range.
+ */
+ public Deflater(int lvl, boolean nowrap)
+ {
+ if (lvl == DEFAULT_COMPRESSION)
+ lvl = 6;
+ else if (lvl < NO_COMPRESSION || lvl > BEST_COMPRESSION)
+ throw new IllegalArgumentException();
+
+ pending = new DeflaterPending();
+ engine = new DeflaterEngine(pending);
+ this.noHeader = nowrap;
+ setStrategy(DEFAULT_STRATEGY);
+ setLevel(lvl);
+ reset();
+ }
+
+ /**
+ * Resets the deflater. The deflater acts afterwards as if it was
+ * just created with the same compression level and strategy as it
+ * had before.
+ */
+ public void reset()
+ {
+ state = (noHeader ? BUSY_STATE : INIT_STATE);
+ totalOut = 0;
+ pending.reset();
+ engine.reset();
+ }
+
+ /**
+ * Frees all objects allocated by the compressor. There's no
+ * reason to call this, since you can just rely on garbage
+ * collection. Exists only for compatibility against Sun's JDK,
+ * where the compressor allocates native memory.
+ * If you call any method (even reset) afterwards the behaviour is
+ * <i>undefined</i>.
+ */
+ public void end()
+ {
+ engine = null;
+ pending = null;
+ state = CLOSED_STATE;
+ }
+
+ /**
+ * Gets the current adler checksum of the data that was processed so
+ * far.
+ */
+ public int getAdler()
+ {
+ return engine.getAdler();
+ }
+
+ /**
+ * Gets the number of input bytes processed so far.
+ */
+ public int getTotalIn()
+ {
+ return (int) engine.getTotalIn();
+ }
+
+ /**
+ * Gets the number of input bytes processed so far.
+ * @since 1.5
+ */
+ public long getBytesRead()
+ {
+ return engine.getTotalIn();
+ }
+
+ /**
+ * Gets the number of output bytes so far.
+ */
+ public int getTotalOut()
+ {
+ return (int) totalOut;
+ }
+
+ /**
+ * Gets the number of output bytes so far.
+ * @since 1.5
+ */
+ public long getBytesWritten()
+ {
+ return totalOut;
+ }
+
+ /**
+ * Finalizes this object.
+ */
+ protected void finalize()
+ {
+ /* Exists solely for compatibility. We don't have any native state. */
+ }
+
+ /**
+ * Flushes the current input block. Further calls to deflate() will
+ * produce enough output to inflate everything in the current input
+ * block. This is not part of Sun's JDK so I have made it package
+ * private. It is used by DeflaterOutputStream to implement
+ * flush().
+ */
+ void flush() {
+ state |= IS_FLUSHING;
+ }
+
+ /**
+ * Finishes the deflater with the current input block. It is an error
+ * to give more input after this method was called. This method must
+ * be called to force all bytes to be flushed.
+ */
+ public void finish() {
+ state |= IS_FLUSHING | IS_FINISHING;
+ }
+
+ /**
+ * Returns true iff the stream was finished and no more output bytes
+ * are available.
+ */
+ public boolean finished()
+ {
+ return state == FINISHED_STATE && pending.isFlushed();
+ }
+
+ /**
+ * Returns true, if the input buffer is empty.
+ * You should then call setInput(). <br>
+ *
+ * <em>NOTE</em>: This method can also return true when the stream
+ * was finished.
+ */
+ public boolean needsInput()
+ {
+ return engine.needsInput();
+ }
+
+ /**
+ * Sets the data which should be compressed next. This should be only
+ * called when needsInput indicates that more input is needed.
+ * If you call setInput when needsInput() returns false, the
+ * previous input that is still pending will be thrown away.
+ * The given byte array should not be changed, before needsInput() returns
+ * true again.
+ * This call is equivalent to <code>setInput(input, 0, input.length)</code>.
+ * @param input the buffer containing the input data.
+ * @exception IllegalStateException if the buffer was finished() or ended().
+ */
+ public void setInput(byte[] input)
+ {
+ setInput(input, 0, input.length);
+ }
+
+ /**
+ * Sets the data which should be compressed next. This should be
+ * only called when needsInput indicates that more input is needed.
+ * The given byte array should not be changed, before needsInput() returns
+ * true again.
+ * @param input the buffer containing the input data.
+ * @param off the start of the data.
+ * @param len the length of the data.
+ * @exception IllegalStateException if the buffer was finished() or ended()
+ * or if previous input is still pending.
+ */
+ public void setInput(byte[] input, int off, int len)
+ {
+ if ((state & IS_FINISHING) != 0)
+ throw new IllegalStateException("finish()/end() already called");
+ engine.setInput(input, off, len);
+ }
+
+ /**
+ * Sets the compression level. There is no guarantee of the exact
+ * position of the change, but if you call this when needsInput is
+ * true the change of compression level will occur somewhere near
+ * before the end of the so far given input.
+ * @param lvl the new compression level.
+ */
+ public void setLevel(int lvl)
+ {
+ if (lvl == DEFAULT_COMPRESSION)
+ lvl = 6;
+ else if (lvl < NO_COMPRESSION || lvl > BEST_COMPRESSION)
+ throw new IllegalArgumentException();
+
+
+ if (level != lvl)
+ {
+ level = lvl;
+ engine.setLevel(lvl);
+ }
+ }
+
+ /**
+ * Sets the compression strategy. Strategy is one of
+ * DEFAULT_STRATEGY, HUFFMAN_ONLY and FILTERED. For the exact
+ * position where the strategy is changed, the same as for
+ * setLevel() applies.
+ * @param stgy the new compression strategy.
+ */
+ public void setStrategy(int stgy)
+ {
+ if (stgy != DEFAULT_STRATEGY && stgy != FILTERED
+ && stgy != HUFFMAN_ONLY)
+ throw new IllegalArgumentException();
+ engine.setStrategy(stgy);
+ }
+
+ /**
+ * Deflates the current input block to the given array. It returns
+ * the number of bytes compressed, or 0 if either
+ * needsInput() or finished() returns true or length is zero.
+ * @param output the buffer where to write the compressed data.
+ */
+ public int deflate(byte[] output)
+ {
+ return deflate(output, 0, output.length);
+ }
+
+ /**
+ * Deflates the current input block to the given array. It returns
+ * the number of bytes compressed, or 0 if either
+ * needsInput() or finished() returns true or length is zero.
+ * @param output the buffer where to write the compressed data.
+ * @param offset the offset into the output array.
+ * @param length the maximum number of bytes that may be written.
+ * @exception IllegalStateException if end() was called.
+ * @exception IndexOutOfBoundsException if offset and/or length
+ * don't match the array length.
+ */
+ public int deflate(byte[] output, int offset, int length)
+ {
+ int origLength = length;
+
+ if (state == CLOSED_STATE)
+ throw new IllegalStateException("Deflater closed");
+
+ if (state < BUSY_STATE)
+ {
+ /* output header */
+ int header = (DEFLATED +
+ ((DeflaterConstants.MAX_WBITS - 8) << 4)) << 8;
+ int level_flags = (level - 1) >> 1;
+ if (level_flags < 0 || level_flags > 3)
+ level_flags = 3;
+ header |= level_flags << 6;
+ if ((state & IS_SETDICT) != 0)
+ /* Dictionary was set */
+ header |= DeflaterConstants.PRESET_DICT;
+ header += 31 - (header % 31);
+
+ pending.writeShortMSB(header);
+ if ((state & IS_SETDICT) != 0)
+ {
+ int chksum = engine.getAdler();
+ engine.resetAdler();
+ pending.writeShortMSB(chksum >> 16);
+ pending.writeShortMSB(chksum & 0xffff);
+ }
+
+ state = BUSY_STATE | (state & (IS_FLUSHING | IS_FINISHING));
+ }
+
+ for (;;)
+ {
+ int count = pending.flush(output, offset, length);
+ offset += count;
+ totalOut += count;
+ length -= count;
+ if (length == 0 || state == FINISHED_STATE)
+ break;
+
+ if (!engine.deflate((state & IS_FLUSHING) != 0,
+ (state & IS_FINISHING) != 0))
+ {
+ if (state == BUSY_STATE)
+ /* We need more input now */
+ return origLength - length;
+ else if (state == FLUSHING_STATE)
+ {
+ if (level != NO_COMPRESSION)
+ {
+ /* We have to supply some lookahead. 8 bit lookahead
+ * are needed by the zlib inflater, and we must fill
+ * the next byte, so that all bits are flushed.
+ */
+ int neededbits = 8 + ((-pending.getBitCount()) & 7);
+ while (neededbits > 0)
+ {
+ /* write a static tree block consisting solely of
+ * an EOF:
+ */
+ pending.writeBits(2, 10);
+ neededbits -= 10;
+ }
+ }
+ state = BUSY_STATE;
+ }
+ else if (state == FINISHING_STATE)
+ {
+ pending.alignToByte();
+ /* We have completed the stream */
+ if (!noHeader)
+ {
+ int adler = engine.getAdler();
+ pending.writeShortMSB(adler >> 16);
+ pending.writeShortMSB(adler & 0xffff);
+ }
+ state = FINISHED_STATE;
+ }
+ }
+ }
+
+ return origLength - length;
+ }
+
+ /**
+ * Sets the dictionary which should be used in the deflate process.
+ * This call is equivalent to <code>setDictionary(dict, 0,
+ * dict.length)</code>.
+ * @param dict the dictionary.
+ * @exception IllegalStateException if setInput () or deflate ()
+ * were already called or another dictionary was already set.
+ */
+ public void setDictionary(byte[] dict)
+ {
+ setDictionary(dict, 0, dict.length);
+ }
+
+ /**
+ * Sets the dictionary which should be used in the deflate process.
+ * The dictionary should be a byte array containing strings that are
+ * likely to occur in the data which should be compressed. The
+ * dictionary is not stored in the compressed output, only a
+ * checksum. To decompress the output you need to supply the same
+ * dictionary again.
+ * @param dict the dictionary.
+ * @param offset an offset into the dictionary.
+ * @param length the length of the dictionary.
+ * @exception IllegalStateException if setInput () or deflate () were
+ * already called or another dictionary was already set.
+ */
+ public void setDictionary(byte[] dict, int offset, int length)
+ {
+ if (state != INIT_STATE)
+ throw new IllegalStateException();
+
+ state = SETDICT_STATE;
+ engine.setDictionary(dict, offset, length);
+ }
+}
diff --git a/libjava/classpath/java/util/zip/DeflaterConstants.java b/libjava/classpath/java/util/zip/DeflaterConstants.java
new file mode 100644
index 000000000..bfef86344
--- /dev/null
+++ b/libjava/classpath/java/util/zip/DeflaterConstants.java
@@ -0,0 +1,78 @@
+/* java.util.zip.DeflaterConstants
+ Copyright (C) 2001 Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath 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 for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module. An independent module is a module which is not derived from
+or based on this library. If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so. If you do not wish to do so, delete this
+exception statement from your version. */
+
+package java.util.zip;
+
+interface DeflaterConstants
+{
+ boolean DEBUGGING = false;
+
+ int STORED_BLOCK = 0;
+ int STATIC_TREES = 1;
+ int DYN_TREES = 2;
+ int PRESET_DICT = 0x20;
+
+ int DEFAULT_MEM_LEVEL = 8;
+
+ int MAX_MATCH = 258;
+ int MIN_MATCH = 3;
+
+ int MAX_WBITS = 15;
+ int WSIZE = 1 << MAX_WBITS;
+ int WMASK = WSIZE - 1;
+
+ int HASH_BITS = DEFAULT_MEM_LEVEL + 7;
+ int HASH_SIZE = 1 << HASH_BITS;
+ int HASH_MASK = HASH_SIZE - 1;
+ int HASH_SHIFT = (HASH_BITS + MIN_MATCH - 1) / MIN_MATCH;
+
+ int MIN_LOOKAHEAD = MAX_MATCH + MIN_MATCH + 1;
+ int MAX_DIST = WSIZE - MIN_LOOKAHEAD;
+
+ int PENDING_BUF_SIZE = 1 << (DEFAULT_MEM_LEVEL + 8);
+ int MAX_BLOCK_SIZE = Math.min(65535, PENDING_BUF_SIZE-5);
+
+ int DEFLATE_STORED = 0;
+ int DEFLATE_FAST = 1;
+ int DEFLATE_SLOW = 2;
+
+ int GOOD_LENGTH[] = { 0,4, 4, 4, 4, 8, 8, 8, 32, 32 };
+ int MAX_LAZY[] = { 0,4, 5, 6, 4,16, 16, 32, 128, 258 };
+ int NICE_LENGTH[] = { 0,8,16,32,16,32,128,128, 258, 258 };
+ int MAX_CHAIN[] = { 0,4, 8,32,16,32,128,256,1024,4096 };
+ int COMPR_FUNC[] = { 0,1, 1, 1, 1, 2, 2, 2, 2, 2 };
+}
diff --git a/libjava/classpath/java/util/zip/DeflaterEngine.java b/libjava/classpath/java/util/zip/DeflaterEngine.java
new file mode 100644
index 000000000..287558e3e
--- /dev/null
+++ b/libjava/classpath/java/util/zip/DeflaterEngine.java
@@ -0,0 +1,698 @@
+/* DeflaterEngine.java --
+ Copyright (C) 2001, 2004, 2005 Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath 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 for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module. An independent module is a module which is not derived from
+or based on this library. If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so. If you do not wish to do so, delete this
+exception statement from your version. */
+
+package java.util.zip;
+
+class DeflaterEngine implements DeflaterConstants
+{
+ private static final int TOO_FAR = 4096;
+
+ private int ins_h;
+
+ /**
+ * Hashtable, hashing three characters to an index for window, so
+ * that window[index]..window[index+2] have this hash code.
+ * Note that the array should really be unsigned short, so you need
+ * to and the values with 0xffff.
+ */
+ private short[] head;
+
+ /**
+ * prev[index & WMASK] points to the previous index that has the
+ * same hash code as the string starting at index. This way
+ * entries with the same hash code are in a linked list.
+ * Note that the array should really be unsigned short, so you need
+ * to and the values with 0xffff.
+ */
+ private short[] prev;
+
+ private int matchStart, matchLen;
+ private boolean prevAvailable;
+ private int blockStart;
+
+ /**
+ * strstart points to the current character in window.
+ */
+ private int strstart;
+
+ /**
+ * lookahead is the number of characters starting at strstart in
+ * window that are valid.
+ * So window[strstart] until window[strstart+lookahead-1] are valid
+ * characters.
+ */
+ private int lookahead;
+
+ /**
+ * This array contains the part of the uncompressed stream that
+ * is of relevance. The current character is indexed by strstart.
+ */
+ private byte[] window;
+
+ private int strategy, max_chain, max_lazy, niceLength, goodLength;
+
+ /** The current compression function. */
+ private int comprFunc;
+
+ /** The input data for compression. */
+ private byte[] inputBuf;
+
+ /** The total bytes of input read. */
+ private long totalIn;
+
+ /** The offset into inputBuf, where input data starts. */
+ private int inputOff;
+
+ /** The end offset of the input data. */
+ private int inputEnd;
+
+ private DeflaterPending pending;
+ private DeflaterHuffman huffman;
+
+ /** The adler checksum */
+ private Adler32 adler;
+
+ /* DEFLATE ALGORITHM:
+ *
+ * The uncompressed stream is inserted into the window array. When
+ * the window array is full the first half is thrown away and the
+ * second half is copied to the beginning.
+ *
+ * The head array is a hash table. Three characters build a hash value
+ * and they the value points to the corresponding index in window of
+ * the last string with this hash. The prev array implements a
+ * linked list of matches with the same hash: prev[index & WMASK] points
+ * to the previous index with the same hash.
+ *
+ *
+ */
+
+
+ DeflaterEngine(DeflaterPending pending) {
+ this.pending = pending;
+ huffman = new DeflaterHuffman(pending);
+ adler = new Adler32();
+
+ window = new byte[2*WSIZE];
+ head = new short[HASH_SIZE];
+ prev = new short[WSIZE];
+
+ /* We start at index 1, to avoid a implementation deficiency, that
+ * we cannot build a repeat pattern at index 0.
+ */
+ blockStart = strstart = 1;
+ }
+
+ public void reset()
+ {
+ huffman.reset();
+ adler.reset();
+ blockStart = strstart = 1;
+ lookahead = 0;
+ totalIn = 0;
+ prevAvailable = false;
+ matchLen = MIN_MATCH - 1;
+ for (int i = 0; i < HASH_SIZE; i++)
+ head[i] = 0;
+ for (int i = 0; i < WSIZE; i++)
+ prev[i] = 0;
+ }
+
+ public final void resetAdler()
+ {
+ adler.reset();
+ }
+
+ public final int getAdler()
+ {
+ int chksum = (int) adler.getValue();
+ return chksum;
+ }
+
+ public final long getTotalIn()
+ {
+ return totalIn;
+ }
+
+ public final void setStrategy(int strat)
+ {
+ strategy = strat;
+ }
+
+ public void setLevel(int lvl)
+ {
+ goodLength = DeflaterConstants.GOOD_LENGTH[lvl];
+ max_lazy = DeflaterConstants.MAX_LAZY[lvl];
+ niceLength = DeflaterConstants.NICE_LENGTH[lvl];
+ max_chain = DeflaterConstants.MAX_CHAIN[lvl];
+
+ if (DeflaterConstants.COMPR_FUNC[lvl] != comprFunc)
+ {
+ if (DeflaterConstants.DEBUGGING)
+ System.err.println("Change from "+comprFunc +" to "
+ + DeflaterConstants.COMPR_FUNC[lvl]);
+ switch (comprFunc)
+ {
+ case DEFLATE_STORED:
+ if (strstart > blockStart)
+ {
+ huffman.flushStoredBlock(window, blockStart,
+ strstart - blockStart, false);
+ blockStart = strstart;
+ }
+ updateHash();
+ break;
+ case DEFLATE_FAST:
+ if (strstart > blockStart)
+ {
+ huffman.flushBlock(window, blockStart, strstart - blockStart,
+ false);
+ blockStart = strstart;
+ }
+ break;
+ case DEFLATE_SLOW:
+ if (prevAvailable)
+ huffman.tallyLit(window[strstart-1] & 0xff);
+ if (strstart > blockStart)
+ {
+ huffman.flushBlock(window, blockStart, strstart - blockStart,
+ false);
+ blockStart = strstart;
+ }
+ prevAvailable = false;
+ matchLen = MIN_MATCH - 1;
+ break;
+ }
+ comprFunc = COMPR_FUNC[lvl];
+ }
+ }
+
+ private void updateHash() {
+ if (DEBUGGING)
+ System.err.println("updateHash: "+strstart);
+ ins_h = (window[strstart] << HASH_SHIFT) ^ window[strstart + 1];
+ }
+
+ /**
+ * Inserts the current string in the head hash and returns the previous
+ * value for this hash.
+ */
+ private int insertString() {
+ short match;
+ int hash = ((ins_h << HASH_SHIFT) ^ window[strstart + (MIN_MATCH -1)])
+ & HASH_MASK;
+
+ if (DEBUGGING)
+ {
+ if (hash != (((window[strstart] << (2*HASH_SHIFT))
+ ^ (window[strstart + 1] << HASH_SHIFT)
+ ^ (window[strstart + 2])) & HASH_MASK))
+ throw new InternalError("hash inconsistent: "+hash+"/"
+ +window[strstart]+","
+ +window[strstart+1]+","
+ +window[strstart+2]+","+HASH_SHIFT);
+ }
+
+ prev[strstart & WMASK] = match = head[hash];
+ head[hash] = (short) strstart;
+ ins_h = hash;
+ return match & 0xffff;
+ }
+
+ private void slideWindow()
+ {
+ System.arraycopy(window, WSIZE, window, 0, WSIZE);
+ matchStart -= WSIZE;
+ strstart -= WSIZE;
+ blockStart -= WSIZE;
+
+ /* Slide the hash table (could be avoided with 32 bit values
+ * at the expense of memory usage).
+ */
+ for (int i = 0; i < HASH_SIZE; i++)
+ {
+ int m = head[i] & 0xffff;
+ head[i] = m >= WSIZE ? (short) (m - WSIZE) : 0;
+ }
+
+ /* Slide the prev table.
+ */
+ for (int i = 0; i < WSIZE; i++)
+ {
+ int m = prev[i] & 0xffff;
+ prev[i] = m >= WSIZE ? (short) (m - WSIZE) : 0;
+ }
+ }
+
+ /**
+ * Fill the window when the lookahead becomes insufficient.
+ * Updates strstart and lookahead.
+ *
+ * OUT assertions: strstart + lookahead <= 2*WSIZE
+ * lookahead >= MIN_LOOKAHEAD or inputOff == inputEnd
+ */
+ private void fillWindow()
+ {
+ /* If the window is almost full and there is insufficient lookahead,
+ * move the upper half to the lower one to make room in the upper half.
+ */
+ if (strstart >= WSIZE + MAX_DIST)
+ slideWindow();
+
+ /* If there is not enough lookahead, but still some input left,
+ * read in the input
+ */
+ while (lookahead < DeflaterConstants.MIN_LOOKAHEAD && inputOff < inputEnd)
+ {
+ int more = 2*WSIZE - lookahead - strstart;
+
+ if (more > inputEnd - inputOff)
+ more = inputEnd - inputOff;
+
+ System.arraycopy(inputBuf, inputOff,
+ window, strstart + lookahead, more);
+ adler.update(inputBuf, inputOff, more);
+ inputOff += more;
+ totalIn += more;
+ lookahead += more;
+ }
+
+ if (lookahead >= MIN_MATCH)
+ updateHash();
+ }
+
+ /**
+ * Find the best (longest) string in the window matching the
+ * string starting at strstart.
+ *
+ * Preconditions:
+ * strstart + MAX_MATCH <= window.length.
+ *
+ *
+ * @param curMatch
+ */
+ private boolean findLongestMatch(int curMatch) {
+ int chainLength = this.max_chain;
+ int niceLength = this.niceLength;
+ short[] prev = this.prev;
+ int scan = this.strstart;
+ int match;
+ int best_end = this.strstart + matchLen;
+ int best_len = Math.max(matchLen, MIN_MATCH - 1);
+
+ int limit = Math.max(strstart - MAX_DIST, 0);
+
+ int strend = scan + MAX_MATCH - 1;
+ byte scan_end1 = window[best_end - 1];
+ byte scan_end = window[best_end];
+
+ /* Do not waste too much time if we already have a good match: */
+ if (best_len >= this.goodLength)
+ chainLength >>= 2;
+
+ /* Do not look for matches beyond the end of the input. This is necessary
+ * to make deflate deterministic.
+ */
+ if (niceLength > lookahead)
+ niceLength = lookahead;
+
+ if (DeflaterConstants.DEBUGGING
+ && strstart > 2*WSIZE - MIN_LOOKAHEAD)
+ throw new InternalError("need lookahead");
+
+ do {
+ if (DeflaterConstants.DEBUGGING && curMatch >= strstart)
+ throw new InternalError("future match");
+ if (window[curMatch + best_len] != scan_end
+ || window[curMatch + best_len - 1] != scan_end1
+ || window[curMatch] != window[scan]
+ || window[curMatch+1] != window[scan + 1])
+ continue;
+
+ match = curMatch + 2;
+ scan += 2;
+
+ /* We check for insufficient lookahead only every 8th comparison;
+ * the 256th check will be made at strstart+258.
+ */
+ while (window[++scan] == window[++match]
+ && window[++scan] == window[++match]
+ && window[++scan] == window[++match]
+ && window[++scan] == window[++match]
+ && window[++scan] == window[++match]
+ && window[++scan] == window[++match]
+ && window[++scan] == window[++match]
+ && window[++scan] == window[++match]
+ && scan < strend)
+ ;
+
+ if (scan > best_end) {
+// if (DeflaterConstants.DEBUGGING && ins_h == 0)
+// System.err.println("Found match: "+curMatch+"-"+(scan-strstart));
+ matchStart = curMatch;
+ best_end = scan;
+ best_len = scan - strstart;
+ if (best_len >= niceLength)
+ break;
+
+ scan_end1 = window[best_end-1];
+ scan_end = window[best_end];
+ }
+ scan = strstart;
+ } while ((curMatch = (prev[curMatch & WMASK] & 0xffff)) > limit
+ && --chainLength != 0);
+
+ matchLen = Math.min(best_len, lookahead);
+ return matchLen >= MIN_MATCH;
+ }
+
+ void setDictionary(byte[] buffer, int offset, int length) {
+ if (DeflaterConstants.DEBUGGING && strstart != 1)
+ throw new IllegalStateException("strstart not 1");
+ adler.update(buffer, offset, length);
+ if (length < MIN_MATCH)
+ return;
+ if (length > MAX_DIST) {
+ offset += length - MAX_DIST;
+ length = MAX_DIST;
+ }
+
+ System.arraycopy(buffer, offset, window, strstart, length);
+
+ updateHash();
+ length--;
+ while (--length > 0)
+ {
+ insertString();
+ strstart++;
+ }
+ strstart += 2;
+ blockStart = strstart;
+ }
+
+ private boolean deflateStored(boolean flush, boolean finish)
+ {
+ if (!flush && lookahead == 0)
+ return false;
+
+ strstart += lookahead;
+ lookahead = 0;
+
+ int storedLen = strstart - blockStart;
+
+ if ((storedLen >= DeflaterConstants.MAX_BLOCK_SIZE)
+ /* Block is full */
+ || (blockStart < WSIZE && storedLen >= MAX_DIST)
+ /* Block may move out of window */
+ || flush)
+ {
+ boolean lastBlock = finish;
+ if (storedLen > DeflaterConstants.MAX_BLOCK_SIZE)
+ {
+ storedLen = DeflaterConstants.MAX_BLOCK_SIZE;
+ lastBlock = false;
+ }
+
+ if (DeflaterConstants.DEBUGGING)
+ System.err.println("storedBlock["+storedLen+","+lastBlock+"]");
+
+ huffman.flushStoredBlock(window, blockStart, storedLen, lastBlock);
+ blockStart += storedLen;
+ return !lastBlock;
+ }
+ return true;
+ }
+
+ private boolean deflateFast(boolean flush, boolean finish)
+ {
+ if (lookahead < MIN_LOOKAHEAD && !flush)
+ return false;
+
+ while (lookahead >= MIN_LOOKAHEAD || flush)
+ {
+ if (lookahead == 0)
+ {
+ /* We are flushing everything */
+ huffman.flushBlock(window, blockStart, strstart - blockStart,
+ finish);
+ blockStart = strstart;
+ return false;
+ }
+
+ if (strstart > 2 * WSIZE - MIN_LOOKAHEAD)
+ {
+ /* slide window, as findLongestMatch need this.
+ * This should only happen when flushing and the window
+ * is almost full.
+ */
+ slideWindow();
+ }
+
+ int hashHead;
+ if (lookahead >= MIN_MATCH
+ && (hashHead = insertString()) != 0
+ && strategy != Deflater.HUFFMAN_ONLY
+ && strstart - hashHead <= MAX_DIST
+ && findLongestMatch(hashHead))
+ {
+ /* longestMatch sets matchStart and matchLen */
+ if (DeflaterConstants.DEBUGGING)
+ {
+ for (int i = 0 ; i < matchLen; i++)
+ {
+ if (window[strstart+i] != window[matchStart + i])
+ throw new InternalError();
+ }
+ }
+ boolean full = huffman.tallyDist(strstart - matchStart, matchLen);
+
+ lookahead -= matchLen;
+ if (matchLen <= max_lazy && lookahead >= MIN_MATCH)
+ {
+ while (--matchLen > 0)
+ {
+ strstart++;
+ insertString();
+ }
+ strstart++;
+ }
+ else
+ {
+ strstart += matchLen;
+ if (lookahead >= MIN_MATCH - 1)
+ updateHash();
+ }
+ matchLen = MIN_MATCH - 1;
+ if (!full)
+ continue;
+ }
+ else
+ {
+ /* No match found */
+ huffman.tallyLit(window[strstart] & 0xff);
+ strstart++;
+ lookahead--;
+ }
+
+ if (huffman.isFull())
+ {
+ boolean lastBlock = finish && lookahead == 0;
+ huffman.flushBlock(window, blockStart, strstart - blockStart,
+ lastBlock);
+ blockStart = strstart;
+ return !lastBlock;
+ }
+ }
+ return true;
+ }
+
+ private boolean deflateSlow(boolean flush, boolean finish)
+ {
+ if (lookahead < MIN_LOOKAHEAD && !flush)
+ return false;
+
+ while (lookahead >= MIN_LOOKAHEAD || flush)
+ {
+ if (lookahead == 0)
+ {
+ if (prevAvailable)
+ huffman.tallyLit(window[strstart-1] & 0xff);
+ prevAvailable = false;
+
+ /* We are flushing everything */
+ if (DeflaterConstants.DEBUGGING && !flush)
+ throw new InternalError("Not flushing, but no lookahead");
+ huffman.flushBlock(window, blockStart, strstart - blockStart,
+ finish);
+ blockStart = strstart;
+ return false;
+ }
+
+ if (strstart >= 2 * WSIZE - MIN_LOOKAHEAD)
+ {
+ /* slide window, as findLongestMatch need this.
+ * This should only happen when flushing and the window
+ * is almost full.
+ */
+ slideWindow();
+ }
+
+ int prevMatch = matchStart;
+ int prevLen = matchLen;
+ if (lookahead >= MIN_MATCH)
+ {
+ int hashHead = insertString();
+ if (strategy != Deflater.HUFFMAN_ONLY
+ && hashHead != 0 && strstart - hashHead <= MAX_DIST
+ && findLongestMatch(hashHead))
+ {
+ /* longestMatch sets matchStart and matchLen */
+
+ /* Discard match if too small and too far away */
+ if (matchLen <= 5
+ && (strategy == Deflater.FILTERED
+ || (matchLen == MIN_MATCH
+ && strstart - matchStart > TOO_FAR))) {
+ matchLen = MIN_MATCH - 1;
+ }
+ }
+ }
+
+ /* previous match was better */
+ if (prevLen >= MIN_MATCH && matchLen <= prevLen)
+ {
+ if (DeflaterConstants.DEBUGGING)
+ {
+ for (int i = 0 ; i < matchLen; i++)
+ {
+ if (window[strstart-1+i] != window[prevMatch + i])
+ throw new InternalError();
+ }
+ }
+ huffman.tallyDist(strstart - 1 - prevMatch, prevLen);
+ prevLen -= 2;
+ do
+ {
+ strstart++;
+ lookahead--;
+ if (lookahead >= MIN_MATCH)
+ insertString();
+ }
+ while (--prevLen > 0);
+ strstart ++;
+ lookahead--;
+ prevAvailable = false;
+ matchLen = MIN_MATCH - 1;
+ }
+ else
+ {
+ if (prevAvailable)
+ huffman.tallyLit(window[strstart-1] & 0xff);
+ prevAvailable = true;
+ strstart++;
+ lookahead--;
+ }
+
+ if (huffman.isFull())
+ {
+ int len = strstart - blockStart;
+ if (prevAvailable)
+ len--;
+ boolean lastBlock = (finish && lookahead == 0 && !prevAvailable);
+ huffman.flushBlock(window, blockStart, len, lastBlock);
+ blockStart += len;
+ return !lastBlock;
+ }
+ }
+ return true;
+ }
+
+ public boolean deflate(boolean flush, boolean finish)
+ {
+ boolean progress;
+ do
+ {
+ fillWindow();
+ boolean canFlush = flush && inputOff == inputEnd;
+ if (DeflaterConstants.DEBUGGING)
+ System.err.println("window: ["+blockStart+","+strstart+","
+ +lookahead+"], "+comprFunc+","+canFlush);
+ switch (comprFunc)
+ {
+ case DEFLATE_STORED:
+ progress = deflateStored(canFlush, finish);
+ break;
+ case DEFLATE_FAST:
+ progress = deflateFast(canFlush, finish);
+ break;
+ case DEFLATE_SLOW:
+ progress = deflateSlow(canFlush, finish);
+ break;
+ default:
+ throw new InternalError();
+ }
+ }
+ while (pending.isFlushed() /* repeat while we have no pending output */
+ && progress); /* and progress was made */
+
+ return progress;
+ }
+
+ public void setInput(byte[] buf, int off, int len)
+ {
+ if (inputOff < inputEnd)
+ throw new IllegalStateException
+ ("Old input was not completely processed");
+
+ int end = off + len;
+
+ /* We want to throw an ArrayIndexOutOfBoundsException early. The
+ * check is very tricky: it also handles integer wrap around.
+ */
+ if (0 > off || off > end || end > buf.length)
+ throw new ArrayIndexOutOfBoundsException();
+
+ inputBuf = buf;
+ inputOff = off;
+ inputEnd = end;
+ }
+
+ public final boolean needsInput()
+ {
+ return inputEnd == inputOff;
+ }
+}
diff --git a/libjava/classpath/java/util/zip/DeflaterHuffman.java b/libjava/classpath/java/util/zip/DeflaterHuffman.java
new file mode 100644
index 000000000..8da987e0f
--- /dev/null
+++ b/libjava/classpath/java/util/zip/DeflaterHuffman.java
@@ -0,0 +1,776 @@
+/* DeflaterHuffman.java --
+ Copyright (C) 2001, 2004, 2005 Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath 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 for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module. An independent module is a module which is not derived from
+or based on this library. If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so. If you do not wish to do so, delete this
+exception statement from your version. */
+
+package java.util.zip;
+
+/**
+ * This is the DeflaterHuffman class.
+ *
+ * This class is <i>not</i> thread safe. This is inherent in the API, due
+ * to the split of deflate and setInput.
+ *
+ * @author Jochen Hoenicke
+ * @date Jan 6, 2000
+ */
+class DeflaterHuffman
+{
+ private static final int BUFSIZE = 1 << (DeflaterConstants.DEFAULT_MEM_LEVEL + 6);
+ private static final int LITERAL_NUM = 286;
+ private static final int DIST_NUM = 30;
+ private static final int BITLEN_NUM = 19;
+ private static final int REP_3_6 = 16;
+ private static final int REP_3_10 = 17;
+ private static final int REP_11_138 = 18;
+ private static final int EOF_SYMBOL = 256;
+ 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 final String bit4Reverse =
+ "\000\010\004\014\002\012\006\016\001\011\005\015\003\013\007\017";
+
+ class Tree {
+ short[] freqs;
+ short[] codes;
+ byte[] length;
+ int[] bl_counts;
+ int minNumCodes, numCodes;
+ int maxLength;
+
+ Tree(int elems, int minCodes, int maxLength) {
+ this.minNumCodes = minCodes;
+ this.maxLength = maxLength;
+ freqs = new short[elems];
+ bl_counts = new int[maxLength];
+ }
+
+ void reset() {
+ for (int i = 0; i < freqs.length; i++)
+ freqs[i] = 0;
+ codes = null;
+ length = null;
+ }
+
+ final void writeSymbol(int code)
+ {
+ if (DeflaterConstants.DEBUGGING)
+ {
+ freqs[code]--;
+// System.err.print("writeSymbol("+freqs.length+","+code+"): ");
+ }
+ pending.writeBits(codes[code] & 0xffff, length[code]);
+ }
+
+ final void checkEmpty()
+ {
+ boolean empty = true;
+ for (int i = 0; i < freqs.length; i++)
+ if (freqs[i] != 0)
+ {
+ System.err.println("freqs["+i+"] == "+freqs[i]);
+ empty = false;
+ }
+ if (!empty)
+ throw new InternalError();
+ System.err.println("checkEmpty suceeded!");
+ }
+
+ void setStaticCodes(short[] stCodes, byte[] stLength)
+ {
+ codes = stCodes;
+ length = stLength;
+ }
+
+ public void buildCodes() {
+ int[] nextCode = new int[maxLength];
+ int code = 0;
+ codes = new short[freqs.length];
+
+ if (DeflaterConstants.DEBUGGING)
+ System.err.println("buildCodes: "+freqs.length);
+ for (int bits = 0; bits < maxLength; bits++)
+ {
+ nextCode[bits] = code;
+ code += bl_counts[bits] << (15 - bits);
+ if (DeflaterConstants.DEBUGGING)
+ System.err.println("bits: "+(bits+1)+" count: "+bl_counts[bits]
+ +" nextCode: "+Integer.toHexString(code));
+ }
+ if (DeflaterConstants.DEBUGGING && code != 65536)
+ throw new RuntimeException("Inconsistent bl_counts!");
+
+ for (int i=0; i < numCodes; i++)
+ {
+ int bits = length[i];
+ if (bits > 0)
+ {
+ if (DeflaterConstants.DEBUGGING)
+ System.err.println("codes["+i+"] = rev("
+ +Integer.toHexString(nextCode[bits-1])+"),"
+ +bits);
+ codes[i] = bitReverse(nextCode[bits-1]);
+ nextCode[bits-1] += 1 << (16 - bits);
+ }
+ }
+ }
+
+ private void buildLength(int childs[])
+ {
+ this.length = new byte [freqs.length];
+ int numNodes = childs.length / 2;
+ int numLeafs = (numNodes + 1) / 2;
+ int overflow = 0;
+
+ for (int i = 0; i < maxLength; i++)
+ bl_counts[i] = 0;
+
+ /* First calculate optimal bit lengths */
+ int lengths[] = new int[numNodes];
+ lengths[numNodes-1] = 0;
+ for (int i = numNodes - 1; i >= 0; i--)
+ {
+ if (childs[2*i+1] != -1)
+ {
+ int bitLength = lengths[i] + 1;
+ if (bitLength > maxLength)
+ {
+ bitLength = maxLength;
+ overflow++;
+ }
+ lengths[childs[2*i]] = lengths[childs[2*i+1]] = bitLength;
+ }
+ else
+ {
+ /* A leaf node */
+ int bitLength = lengths[i];
+ bl_counts[bitLength - 1]++;
+ this.length[childs[2*i]] = (byte) lengths[i];
+ }
+ }
+
+ if (DeflaterConstants.DEBUGGING)
+ {
+ System.err.println("Tree "+freqs.length+" lengths:");
+ for (int i=0; i < numLeafs; i++)
+ System.err.println("Node "+childs[2*i]+" freq: "+freqs[childs[2*i]]
+ + " len: "+length[childs[2*i]]);
+ }
+
+ if (overflow == 0)
+ return;
+
+ int incrBitLen = maxLength - 1;
+ do
+ {
+ /* Find the first bit length which could increase: */
+ while (bl_counts[--incrBitLen] == 0)
+ ;
+
+ /* Move this node one down and remove a corresponding
+ * amount of overflow nodes.
+ */
+ do
+ {
+ bl_counts[incrBitLen]--;
+ bl_counts[++incrBitLen]++;
+ overflow -= 1 << (maxLength - 1 - incrBitLen);
+ }
+ while (overflow > 0 && incrBitLen < maxLength - 1);
+ }
+ while (overflow > 0);
+
+ /* We may have overshot above. Move some nodes from maxLength to
+ * maxLength-1 in that case.
+ */
+ bl_counts[maxLength-1] += overflow;
+ bl_counts[maxLength-2] -= overflow;
+
+ /* Now recompute all bit lengths, scanning in increasing
+ * frequency. It is simpler to reconstruct all lengths instead of
+ * fixing only the wrong ones. This idea is taken from 'ar'
+ * written by Haruhiko Okumura.
+ *
+ * The nodes were inserted with decreasing frequency into the childs
+ * array.
+ */
+ int nodePtr = 2 * numLeafs;
+ for (int bits = maxLength; bits != 0; bits--)
+ {
+ int n = bl_counts[bits-1];
+ while (n > 0)
+ {
+ int childPtr = 2*childs[nodePtr++];
+ if (childs[childPtr + 1] == -1)
+ {
+ /* We found another leaf */
+ length[childs[childPtr]] = (byte) bits;
+ n--;
+ }
+ }
+ }
+ if (DeflaterConstants.DEBUGGING)
+ {
+ System.err.println("*** After overflow elimination. ***");
+ for (int i=0; i < numLeafs; i++)
+ System.err.println("Node "+childs[2*i]+" freq: "+freqs[childs[2*i]]
+ + " len: "+length[childs[2*i]]);
+ }
+ }
+
+ void buildTree()
+ {
+ int numSymbols = freqs.length;
+
+ /* heap is a priority queue, sorted by frequency, least frequent
+ * nodes first. The heap is a binary tree, with the property, that
+ * the parent node is smaller than both child nodes. This assures
+ * that the smallest node is the first parent.
+ *
+ * The binary tree is encoded in an array: 0 is root node and
+ * the nodes 2*n+1, 2*n+2 are the child nodes of node n.
+ */
+ int[] heap = new int[numSymbols];
+ int heapLen = 0;
+ int maxCode = 0;
+ for (int n = 0; n < numSymbols; n++)
+ {
+ int freq = freqs[n];
+ if (freq != 0)
+ {
+ /* Insert n into heap */
+ int pos = heapLen++;
+ int ppos;
+ while (pos > 0 &&
+ freqs[heap[ppos = (pos - 1) / 2]] > freq) {
+ heap[pos] = heap[ppos];
+ pos = ppos;
+ }
+ heap[pos] = n;
+ maxCode = n;
+ }
+ }
+
+ /* We could encode a single literal with 0 bits but then we
+ * don't see the literals. Therefore we force at least two
+ * literals to avoid this case. We don't care about order in
+ * this case, both literals get a 1 bit code.
+ */
+ while (heapLen < 2)
+ {
+ int node = maxCode < 2 ? ++maxCode : 0;
+ heap[heapLen++] = node;
+ }
+
+ numCodes = Math.max(maxCode + 1, minNumCodes);
+
+ int numLeafs = heapLen;
+ int[] childs = new int[4*heapLen - 2];
+ int[] values = new int[2*heapLen - 1];
+ int numNodes = numLeafs;
+ for (int i = 0; i < heapLen; i++)
+ {
+ int node = heap[i];
+ childs[2*i] = node;
+ childs[2*i+1] = -1;
+ values[i] = freqs[node] << 8;
+ heap[i] = i;
+ }
+
+ /* Construct the Huffman tree by repeatedly combining the least two
+ * frequent nodes.
+ */
+ do
+ {
+ int first = heap[0];
+ int last = heap[--heapLen];
+
+ /* Propagate the hole to the leafs of the heap */
+ int ppos = 0;
+ int path = 1;
+ while (path < heapLen)
+ {
+ if (path + 1 < heapLen
+ && values[heap[path]] > values[heap[path+1]])
+ path++;
+
+ heap[ppos] = heap[path];
+ ppos = path;
+ path = path * 2 + 1;
+ }
+
+ /* Now propagate the last element down along path. Normally
+ * it shouldn't go too deep.
+ */
+ int lastVal = values[last];
+ while ((path = ppos) > 0
+ && values[heap[ppos = (path - 1)/2]] > lastVal)
+ heap[path] = heap[ppos];
+ heap[path] = last;
+
+
+ int second = heap[0];
+
+ /* Create a new node father of first and second */
+ last = numNodes++;
+ childs[2*last] = first;
+ childs[2*last+1] = second;
+ int mindepth = Math.min(values[first] & 0xff, values[second] & 0xff);
+ values[last] = lastVal = values[first] + values[second] - mindepth + 1;
+
+ /* Again, propagate the hole to the leafs */
+ ppos = 0;
+ path = 1;
+ while (path < heapLen)
+ {
+ if (path + 1 < heapLen
+ && values[heap[path]] > values[heap[path+1]])
+ path++;
+
+ heap[ppos] = heap[path];
+ ppos = path;
+ path = ppos * 2 + 1;
+ }
+
+ /* Now propagate the new element down along path */
+ while ((path = ppos) > 0
+ && values[heap[ppos = (path - 1)/2]] > lastVal)
+ heap[path] = heap[ppos];
+ heap[path] = last;
+ }
+ while (heapLen > 1);
+
+ if (heap[0] != childs.length / 2 - 1)
+ throw new RuntimeException("Weird!");
+
+ buildLength(childs);
+ }
+
+ int getEncodedLength()
+ {
+ int len = 0;
+ for (int i = 0; i < freqs.length; i++)
+ len += freqs[i] * length[i];
+ return len;
+ }
+
+ void calcBLFreq(Tree blTree) {
+ int max_count; /* max repeat count */
+ int min_count; /* min repeat count */
+ int count; /* repeat count of the current code */
+ int curlen = -1; /* length of current code */
+
+ int i = 0;
+ while (i < numCodes)
+ {
+ count = 1;
+ int nextlen = length[i];
+ if (nextlen == 0)
+ {
+ max_count = 138;
+ min_count = 3;
+ }
+ else
+ {
+ max_count = 6;
+ min_count = 3;
+ if (curlen != nextlen)
+ {
+ blTree.freqs[nextlen]++;
+ count = 0;
+ }
+ }
+ curlen = nextlen;
+ i++;
+
+ while (i < numCodes && curlen == length[i])
+ {
+ i++;
+ if (++count >= max_count)
+ break;
+ }
+
+ if (count < min_count)
+ blTree.freqs[curlen] += count;
+ else if (curlen != 0)
+ blTree.freqs[REP_3_6]++;
+ else if (count <= 10)
+ blTree.freqs[REP_3_10]++;
+ else
+ blTree.freqs[REP_11_138]++;
+ }
+ }
+
+ void writeTree(Tree blTree)
+ {
+ int max_count; /* max repeat count */
+ int min_count; /* min repeat count */
+ int count; /* repeat count of the current code */
+ int curlen = -1; /* length of current code */
+
+ int i = 0;
+ while (i < numCodes)
+ {
+ count = 1;
+ int nextlen = length[i];
+ if (nextlen == 0)
+ {
+ max_count = 138;
+ min_count = 3;
+ }
+ else
+ {
+ max_count = 6;
+ min_count = 3;
+ if (curlen != nextlen)
+ {
+ blTree.writeSymbol(nextlen);
+ count = 0;
+ }
+ }
+ curlen = nextlen;
+ i++;
+
+ while (i < numCodes && curlen == length[i])
+ {
+ i++;
+ if (++count >= max_count)
+ break;
+ }
+
+ if (count < min_count)
+ {
+ while (count-- > 0)
+ blTree.writeSymbol(curlen);
+ }
+ else if (curlen != 0)
+ {
+ blTree.writeSymbol(REP_3_6);
+ pending.writeBits(count - 3, 2);
+ }
+ else if (count <= 10)
+ {
+ blTree.writeSymbol(REP_3_10);
+ pending.writeBits(count - 3, 3);
+ }
+ else
+ {
+ blTree.writeSymbol(REP_11_138);
+ pending.writeBits(count - 11, 7);
+ }
+ }
+ }
+ }
+
+
+
+ DeflaterPending pending;
+ private Tree literalTree, distTree, blTree;
+
+ private short d_buf[];
+ private byte l_buf[];
+ private int last_lit;
+ private int extra_bits;
+
+ private static short staticLCodes[];
+ private static byte staticLLength[];
+ private static short staticDCodes[];
+ private static byte staticDLength[];
+
+ /**
+ * Reverse the bits of a 16 bit value.
+ */
+ static short bitReverse(int value) {
+ return (short) (bit4Reverse.charAt(value & 0xf) << 12
+ | bit4Reverse.charAt((value >> 4) & 0xf) << 8
+ | bit4Reverse.charAt((value >> 8) & 0xf) << 4
+ | bit4Reverse.charAt(value >> 12));
+ }
+
+ static {
+ /* See RFC 1951 3.2.6 */
+ /* Literal codes */
+ staticLCodes = new short[LITERAL_NUM];
+ staticLLength = new byte[LITERAL_NUM];
+ int i = 0;
+ while (i < 144) {
+ staticLCodes[i] = bitReverse((0x030 + i) << 8);
+ staticLLength[i++] = 8;
+ }
+ while (i < 256) {
+ staticLCodes[i] = bitReverse((0x190 - 144 + i) << 7);
+ staticLLength[i++] = 9;
+ }
+ while (i < 280) {
+ staticLCodes[i] = bitReverse((0x000 - 256 + i) << 9);
+ staticLLength[i++] = 7;
+ }
+ while (i < LITERAL_NUM) {
+ staticLCodes[i] = bitReverse((0x0c0 - 280 + i) << 8);
+ staticLLength[i++] = 8;
+ }
+
+ /* Distant codes */
+ staticDCodes = new short[DIST_NUM];
+ staticDLength = new byte[DIST_NUM];
+ for (i = 0; i < DIST_NUM; i++) {
+ staticDCodes[i] = bitReverse(i << 11);
+ staticDLength[i] = 5;
+ }
+ }
+
+ public DeflaterHuffman(DeflaterPending pending)
+ {
+ this.pending = pending;
+
+ literalTree = new Tree(LITERAL_NUM, 257, 15);
+ distTree = new Tree(DIST_NUM, 1, 15);
+ blTree = new Tree(BITLEN_NUM, 4, 7);
+
+ d_buf = new short[BUFSIZE];
+ l_buf = new byte [BUFSIZE];
+ }
+
+ public final void reset() {
+ last_lit = 0;
+ extra_bits = 0;
+ literalTree.reset();
+ distTree.reset();
+ blTree.reset();
+ }
+
+ private int l_code(int len) {
+ if (len == 255)
+ return 285;
+
+ int code = 257;
+ while (len >= 8)
+ {
+ code += 4;
+ len >>= 1;
+ }
+ return code + len;
+ }
+
+ private int d_code(int distance) {
+ int code = 0;
+ while (distance >= 4)
+ {
+ code += 2;
+ distance >>= 1;
+ }
+ return code + distance;
+ }
+
+ public void sendAllTrees(int blTreeCodes) {
+ blTree.buildCodes();
+ literalTree.buildCodes();
+ distTree.buildCodes();
+ pending.writeBits(literalTree.numCodes - 257, 5);
+ pending.writeBits(distTree.numCodes - 1, 5);
+ pending.writeBits(blTreeCodes - 4, 4);
+ for (int rank = 0; rank < blTreeCodes; rank++)
+ pending.writeBits(blTree.length[BL_ORDER[rank]], 3);
+ literalTree.writeTree(blTree);
+ distTree.writeTree(blTree);
+ if (DeflaterConstants.DEBUGGING)
+ blTree.checkEmpty();
+ }
+
+ public void compressBlock() {
+ for (int i = 0; i < last_lit; i++)
+ {
+ int litlen = l_buf[i] & 0xff;
+ int dist = d_buf[i];
+ if (dist-- != 0)
+ {
+ if (DeflaterConstants.DEBUGGING)
+ System.err.print("["+(dist+1)+","+(litlen+3)+"]: ");
+
+ int lc = l_code(litlen);
+ literalTree.writeSymbol(lc);
+
+ int bits = (lc - 261) / 4;
+ if (bits > 0 && bits <= 5)
+ pending.writeBits(litlen & ((1 << bits) - 1), bits);
+
+ int dc = d_code(dist);
+ distTree.writeSymbol(dc);
+
+ bits = dc / 2 - 1;
+ if (bits > 0)
+ pending.writeBits(dist & ((1 << bits) - 1), bits);
+ }
+ else
+ {
+ if (DeflaterConstants.DEBUGGING)
+ {
+ if (litlen > 32 && litlen < 127)
+ System.err.print("("+(char)litlen+"): ");
+ else
+ System.err.print("{"+litlen+"}: ");
+ }
+ literalTree.writeSymbol(litlen);
+ }
+ }
+ if (DeflaterConstants.DEBUGGING)
+ System.err.print("EOF: ");
+ literalTree.writeSymbol(EOF_SYMBOL);
+ if (DeflaterConstants.DEBUGGING)
+ {
+ literalTree.checkEmpty();
+ distTree.checkEmpty();
+ }
+ }
+
+ public void flushStoredBlock(byte[] stored,
+ int stored_offset, int stored_len,
+ boolean lastBlock) {
+ if (DeflaterConstants.DEBUGGING)
+ System.err.println("Flushing stored block "+ stored_len);
+ pending.writeBits((DeflaterConstants.STORED_BLOCK << 1)
+ + (lastBlock ? 1 : 0), 3);
+ pending.alignToByte();
+ pending.writeShort(stored_len);
+ pending.writeShort(~stored_len);
+ pending.writeBlock(stored, stored_offset, stored_len);
+ reset();
+ }
+
+ public void flushBlock(byte[] stored, int stored_offset, int stored_len,
+ boolean lastBlock) {
+ literalTree.freqs[EOF_SYMBOL]++;
+
+ /* Build trees */
+ literalTree.buildTree();
+ distTree.buildTree();
+
+ /* Calculate bitlen frequency */
+ literalTree.calcBLFreq(blTree);
+ distTree.calcBLFreq(blTree);
+
+ /* Build bitlen tree */
+ blTree.buildTree();
+
+ int blTreeCodes = 4;
+ for (int i = 18; i > blTreeCodes; i--)
+ {
+ if (blTree.length[BL_ORDER[i]] > 0)
+ blTreeCodes = i+1;
+ }
+ int opt_len = 14 + blTreeCodes * 3 + blTree.getEncodedLength()
+ + literalTree.getEncodedLength() + distTree.getEncodedLength()
+ + extra_bits;
+
+ int static_len = extra_bits;
+ for (int i = 0; i < LITERAL_NUM; i++)
+ static_len += literalTree.freqs[i] * staticLLength[i];
+ for (int i = 0; i < DIST_NUM; i++)
+ static_len += distTree.freqs[i] * staticDLength[i];
+ if (opt_len >= static_len)
+ {
+ /* Force static trees */
+ opt_len = static_len;
+ }
+
+ if (stored_offset >= 0 && stored_len+4 < opt_len >> 3)
+ {
+ /* Store Block */
+ if (DeflaterConstants.DEBUGGING)
+ System.err.println("Storing, since " + stored_len + " < " + opt_len
+ + " <= " + static_len);
+ flushStoredBlock(stored, stored_offset, stored_len, lastBlock);
+ }
+ else if (opt_len == static_len)
+ {
+ /* Encode with static tree */
+ pending.writeBits((DeflaterConstants.STATIC_TREES << 1)
+ + (lastBlock ? 1 : 0), 3);
+ literalTree.setStaticCodes(staticLCodes, staticLLength);
+ distTree.setStaticCodes(staticDCodes, staticDLength);
+ compressBlock();
+ reset();
+ }
+ else
+ {
+ /* Encode with dynamic tree */
+ pending.writeBits((DeflaterConstants.DYN_TREES << 1)
+ + (lastBlock ? 1 : 0), 3);
+ sendAllTrees(blTreeCodes);
+ compressBlock();
+ reset();
+ }
+ }
+
+ public final boolean isFull()
+ {
+ return last_lit == BUFSIZE;
+ }
+
+ public final boolean tallyLit(int lit)
+ {
+ if (DeflaterConstants.DEBUGGING)
+ {
+ if (lit > 32 && lit < 127)
+ System.err.println("("+(char)lit+")");
+ else
+ System.err.println("{"+lit+"}");
+ }
+ d_buf[last_lit] = 0;
+ l_buf[last_lit++] = (byte) lit;
+ literalTree.freqs[lit]++;
+ return last_lit == BUFSIZE;
+ }
+
+ public final boolean tallyDist(int dist, int len)
+ {
+ if (DeflaterConstants.DEBUGGING)
+ System.err.println("["+dist+","+len+"]");
+
+ d_buf[last_lit] = (short) dist;
+ l_buf[last_lit++] = (byte) (len - 3);
+
+ int lc = l_code(len-3);
+ literalTree.freqs[lc]++;
+ if (lc >= 265 && lc < 285)
+ extra_bits += (lc - 261) / 4;
+
+ int dc = d_code(dist-1);
+ distTree.freqs[dc]++;
+ if (dc >= 4)
+ extra_bits += dc / 2 - 1;
+ return last_lit == BUFSIZE;
+ }
+}
diff --git a/libjava/classpath/java/util/zip/DeflaterOutputStream.java b/libjava/classpath/java/util/zip/DeflaterOutputStream.java
new file mode 100644
index 000000000..6fd1c5cfb
--- /dev/null
+++ b/libjava/classpath/java/util/zip/DeflaterOutputStream.java
@@ -0,0 +1,198 @@
+/* DeflaterOutputStream.java - Output filter for compressing.
+ Copyright (C) 1999, 2000, 2001, 2004, 2005 Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath 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 for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module. An independent module is a module which is not derived from
+or based on this library. If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so. If you do not wish to do so, delete this
+exception statement from your version. */
+
+
+package java.util.zip;
+
+import java.io.FilterOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+
+/* Written using on-line Java Platform 1.2 API Specification
+ * and JCL book.
+ * Believed complete and correct.
+ */
+
+/**
+ * This is a special FilterOutputStream deflating the bytes that are
+ * written through it. It uses the Deflater for deflating.
+ *
+ * A special thing to be noted is that flush() doesn't flush
+ * everything in Sun's JDK, but it does so in jazzlib. This is because
+ * Sun's Deflater doesn't have a way to flush() everything, without
+ * finishing the stream.
+ *
+ * @author Tom Tromey, Jochen Hoenicke
+ * @date Jan 11, 2001
+ */
+public class DeflaterOutputStream extends FilterOutputStream
+{
+ /**
+ * This buffer is used temporarily to retrieve the bytes from the
+ * deflater and write them to the underlying output stream.
+ */
+ protected byte[] buf;
+
+ /**
+ * The deflater which is used to deflate the stream.
+ */
+ protected Deflater def;
+
+ /**
+ * Deflates everything in the def's input buffers. This will call
+ * <code>def.deflate()</code> until all bytes from the input buffers
+ * are processed.
+ */
+ protected void deflate() throws IOException
+ {
+ while (! def.needsInput())
+ {
+ int len = def.deflate(buf, 0, buf.length);
+
+ // System.err.println("DOS deflated " + len + " out of " + buf.length);
+ if (len <= 0)
+ break;
+ out.write(buf, 0, len);
+ }
+
+ if (! def.needsInput())
+ throw new InternalError("Can't deflate all input?");
+ }
+
+ /**
+ * Creates a new DeflaterOutputStream with a default Deflater and
+ * default buffer size.
+ * @param out the output stream where deflated output should be written.
+ */
+ public DeflaterOutputStream(OutputStream out)
+ {
+ this(out, new Deflater(), 4096);
+ }
+
+ /**
+ * Creates a new DeflaterOutputStream with the given Deflater and
+ * default buffer size.
+ * @param out the output stream where deflated output should be written.
+ * @param defl the underlying deflater.
+ */
+ public DeflaterOutputStream(OutputStream out, Deflater defl)
+ {
+ this(out, defl, 4096);
+ }
+
+ /**
+ * Creates a new DeflaterOutputStream with the given Deflater and
+ * buffer size.
+ * @param out the output stream where deflated output should be written.
+ * @param defl the underlying deflater.
+ * @param bufsize the buffer size.
+ * @exception IllegalArgumentException if bufsize isn't positive.
+ */
+ public DeflaterOutputStream(OutputStream out, Deflater defl, int bufsize)
+ {
+ super(out);
+ if (bufsize <= 0)
+ throw new IllegalArgumentException("bufsize <= 0");
+ buf = new byte[bufsize];
+ def = defl;
+ }
+
+ /**
+ * Flushes the stream by calling flush() on the deflater and then
+ * on the underlying stream. This ensures that all bytes are
+ * flushed. This function doesn't work in Sun's JDK, but only in
+ * jazzlib.
+ */
+ public void flush() throws IOException
+ {
+ def.flush();
+ deflate();
+ out.flush();
+ }
+
+ /**
+ * Finishes the stream by calling finish() on the deflater. This
+ * was the only way to ensure that all bytes are flushed in Sun's
+ * JDK.
+ */
+ public void finish() throws IOException
+ {
+ def.finish();
+ while (! def.finished())
+ {
+ int len = def.deflate(buf, 0, buf.length);
+ if (len <= 0)
+ break;
+ out.write(buf, 0, len);
+ }
+ if (! def.finished())
+ throw new InternalError("Can't deflate all input?");
+ out.flush();
+ }
+
+ /**
+ * Calls finish() and closes the stream.
+ */
+ public void close() throws IOException
+ {
+ finish();
+ out.close();
+ }
+
+ /**
+ * Writes a single byte to the compressed output stream.
+ * @param bval the byte value.
+ */
+ public void write(int bval) throws IOException
+ {
+ byte[] b = new byte[1];
+ b[0] = (byte) bval;
+ write(b, 0, 1);
+ }
+
+ /**
+ * Writes a len bytes from an array to the compressed stream.
+ * @param buf the byte array.
+ * @param off the offset into the byte array where to start.
+ * @param len the number of bytes to write.
+ */
+ public void write(byte[] buf, int off, int len) throws IOException
+ {
+ def.setInput(buf, off, len);
+ deflate();
+ }
+}
diff --git a/libjava/classpath/java/util/zip/DeflaterPending.java b/libjava/classpath/java/util/zip/DeflaterPending.java
new file mode 100644
index 000000000..fabc2264e
--- /dev/null
+++ b/libjava/classpath/java/util/zip/DeflaterPending.java
@@ -0,0 +1,53 @@
+/* java.util.zip.DeflaterPending
+ Copyright (C) 2001 Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath 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 for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module. An independent module is a module which is not derived from
+or based on this library. If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so. If you do not wish to do so, delete this
+exception statement from your version. */
+
+package java.util.zip;
+
+/**
+ * This class stores the pending output of the Deflater.
+ *
+ * @author Jochen Hoenicke
+ * @date Jan 5, 2000
+ */
+
+class DeflaterPending extends PendingBuffer
+{
+ public DeflaterPending()
+ {
+ super(DeflaterConstants.PENDING_BUF_SIZE);
+ }
+}
diff --git a/libjava/classpath/java/util/zip/GZIPInputStream.java b/libjava/classpath/java/util/zip/GZIPInputStream.java
new file mode 100644
index 000000000..ed99ee92c
--- /dev/null
+++ b/libjava/classpath/java/util/zip/GZIPInputStream.java
@@ -0,0 +1,355 @@
+/* GZIPInputStream.java - Input filter for reading gzip file
+ Copyright (C) 1999, 2000, 2001, 2002, 2004 Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath 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 for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module. An independent module is a module which is not derived from
+or based on this library. If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so. If you do not wish to do so, delete this
+exception statement from your version. */
+
+
+package java.util.zip;
+
+import java.io.EOFException;
+import java.io.IOException;
+import java.io.InputStream;
+
+/**
+ * This filter stream is used to decompress a "GZIP" format stream.
+ * The "GZIP" format is described in RFC 1952.
+ *
+ * @author John Leuner
+ * @author Tom Tromey
+ * @since JDK 1.1
+ */
+public class GZIPInputStream
+ extends InflaterInputStream
+{
+ /**
+ * The magic number found at the start of a GZIP stream.
+ */
+ public static final int GZIP_MAGIC = 0x8b1f;
+
+ /**
+ * The mask for bit 0 of the flag byte.
+ */
+ static final int FTEXT = 0x1;
+
+ /**
+ * The mask for bit 1 of the flag byte.
+ */
+ static final int FHCRC = 0x2;
+
+ /**
+ * The mask for bit 2 of the flag byte.
+ */
+ static final int FEXTRA = 0x4;
+
+ /**
+ * The mask for bit 3 of the flag byte.
+ */
+ static final int FNAME = 0x8;
+
+ /**
+ * The mask for bit 4 of the flag byte.
+ */
+ static final int FCOMMENT = 0x10;
+
+ /**
+ * The CRC-32 checksum value for uncompressed data.
+ */
+ protected CRC32 crc;
+
+ /**
+ * Indicates whether or not the end of the stream has been reached.
+ */
+ protected boolean eos;
+
+ /**
+ * Indicates whether or not the GZIP header has been read in.
+ */
+ private boolean readGZIPHeader;
+
+ /**
+ * Creates a GZIPInputStream with the default buffer size.
+ *
+ * @param in The stream to read compressed data from
+ * (in GZIP format).
+ *
+ * @throws IOException if an error occurs during an I/O operation.
+ */
+ public GZIPInputStream(InputStream in)
+ throws IOException
+ {
+ this(in, 4096);
+ }
+
+ /**
+ * Creates a GZIPInputStream with the specified buffer size.
+ *
+ * @param in The stream to read compressed data from
+ * (in GZIP format).
+ * @param size The size of the buffer to use.
+ *
+ * @throws IOException if an error occurs during an I/O operation.
+ * @throws IllegalArgumentException if <code>size</code>
+ * is less than or equal to 0.
+ */
+ public GZIPInputStream(InputStream in, int size)
+ throws IOException
+ {
+ super(in, new Inflater(true), size);
+ crc = new CRC32();
+ readHeader();
+ }
+
+ /**
+ * Closes the input stream.
+ *
+ * @throws IOException if an error occurs during an I/O operation.
+ */
+ public void close()
+ throws IOException
+ {
+ // Nothing to do here.
+ super.close();
+ }
+
+ /**
+ * Reads in GZIP-compressed data and stores it in uncompressed form
+ * into an array of bytes. The method will block until either
+ * enough input data becomes available or the compressed stream
+ * reaches its end.
+ *
+ * @param buf the buffer into which the uncompressed data will
+ * be stored.
+ * @param offset the offset indicating where in <code>buf</code>
+ * the uncompressed data should be placed.
+ * @param len the number of uncompressed bytes to be read.
+ */
+ public int read(byte[] buf, int offset, int len) throws IOException
+ {
+ // We first have to slurp in the GZIP header, then we feed all the
+ // rest of the data to the superclass.
+ //
+ // As we do that we continually update the CRC32. Once the data is
+ // finished, we check the CRC32.
+ //
+ // This means we don't need our own buffer, as everything is done
+ // in the superclass.
+ if (!readGZIPHeader)
+ readHeader();
+
+ if (eos)
+ return -1;
+
+ // System.err.println("GZIPIS.read(byte[], off, len ... " + offset + " and len " + len);
+
+ /* We don't have to read the header,
+ * so we just grab data from the superclass.
+ */
+ int numRead = super.read(buf, offset, len);
+ if (numRead > 0)
+ crc.update(buf, offset, numRead);
+
+ if (inf.finished())
+ readFooter();
+ return numRead;
+ }
+
+
+ /**
+ * Reads in the GZIP header.
+ */
+ private void readHeader() throws IOException
+ {
+ /* 1. Check the two magic bytes */
+ CRC32 headCRC = new CRC32();
+ int magic = in.read();
+ if (magic < 0)
+ {
+ eos = true;
+ return;
+ }
+ int magic2 = in.read();
+ if ((magic + (magic2 << 8)) != GZIP_MAGIC)
+ throw new IOException("Error in GZIP header, bad magic code");
+ headCRC.update(magic);
+ headCRC.update(magic2);
+
+ /* 2. Check the compression type (must be 8) */
+ int CM = in.read();
+ if (CM != Deflater.DEFLATED)
+ throw new IOException("Error in GZIP header, data not in deflate format");
+ headCRC.update(CM);
+
+ /* 3. Check the flags */
+ int flags = in.read();
+ if (flags < 0)
+ throw new EOFException("Early EOF in GZIP header");
+ headCRC.update(flags);
+
+ /* This flag byte is divided into individual bits as follows:
+
+ bit 0 FTEXT
+ bit 1 FHCRC
+ bit 2 FEXTRA
+ bit 3 FNAME
+ bit 4 FCOMMENT
+ bit 5 reserved
+ bit 6 reserved
+ bit 7 reserved
+ */
+
+ /* 3.1 Check the reserved bits are zero */
+ if ((flags & 0xd0) != 0)
+ throw new IOException("Reserved flag bits in GZIP header != 0");
+
+ /* 4.-6. Skip the modification time, extra flags, and OS type */
+ for (int i=0; i< 6; i++)
+ {
+ int readByte = in.read();
+ if (readByte < 0)
+ throw new EOFException("Early EOF in GZIP header");
+ headCRC.update(readByte);
+ }
+
+ /* 7. Read extra field */
+ if ((flags & FEXTRA) != 0)
+ {
+ /* Skip subfield id */
+ for (int i=0; i< 2; i++)
+ {
+ int readByte = in.read();
+ if (readByte < 0)
+ throw new EOFException("Early EOF in GZIP header");
+ headCRC.update(readByte);
+ }
+ if (in.read() < 0 || in.read() < 0)
+ throw new EOFException("Early EOF in GZIP header");
+
+ int len1, len2, extraLen;
+ len1 = in.read();
+ len2 = in.read();
+ if ((len1 < 0) || (len2 < 0))
+ throw new EOFException("Early EOF in GZIP header");
+ headCRC.update(len1);
+ headCRC.update(len2);
+
+ extraLen = (len1 << 8) | len2;
+ for (int i = 0; i < extraLen;i++)
+ {
+ int readByte = in.read();
+ if (readByte < 0)
+ throw new EOFException("Early EOF in GZIP header");
+ headCRC.update(readByte);
+ }
+ }
+
+ /* 8. Read file name */
+ if ((flags & FNAME) != 0)
+ {
+ int readByte;
+ while ( (readByte = in.read()) > 0)
+ headCRC.update(readByte);
+ if (readByte < 0)
+ throw new EOFException("Early EOF in GZIP file name");
+ headCRC.update(readByte);
+ }
+
+ /* 9. Read comment */
+ if ((flags & FCOMMENT) != 0)
+ {
+ int readByte;
+ while ( (readByte = in.read()) > 0)
+ headCRC.update(readByte);
+
+ if (readByte < 0)
+ throw new EOFException("Early EOF in GZIP comment");
+ headCRC.update(readByte);
+ }
+
+ /* 10. Read header CRC */
+ if ((flags & FHCRC) != 0)
+ {
+ int tempByte;
+ int crcval = in.read();
+ if (crcval < 0)
+ throw new EOFException("Early EOF in GZIP header");
+
+ tempByte = in.read();
+ if (tempByte < 0)
+ throw new EOFException("Early EOF in GZIP header");
+
+ crcval = (crcval << 8) | tempByte;
+ if (crcval != ((int) headCRC.getValue() & 0xffff))
+ throw new IOException("Header CRC value mismatch");
+ }
+
+ readGZIPHeader = true;
+ //System.err.println("Read GZIP header");
+ }
+
+ private void readFooter() throws IOException
+ {
+ byte[] footer = new byte[8];
+ int avail = inf.getRemaining();
+ if (avail > 8)
+ avail = 8;
+ System.arraycopy(buf, len - inf.getRemaining(), footer, 0, avail);
+ int needed = 8 - avail;
+ while (needed > 0)
+ {
+ int count = in.read(footer, 8-needed, needed);
+ if (count <= 0)
+ throw new EOFException("Early EOF in GZIP footer");
+ needed -= count; //Jewel Jan 16
+ }
+
+ int crcval = (footer[0] & 0xff) | ((footer[1] & 0xff) << 8)
+ | ((footer[2] & 0xff) << 16) | (footer[3] << 24);
+ if (crcval != (int) crc.getValue())
+ throw new IOException("GZIP crc sum mismatch, theirs \""
+ + Integer.toHexString(crcval)
+ + "\" and ours \""
+ + Integer.toHexString( (int) crc.getValue()));
+
+ int total = (footer[4] & 0xff) | ((footer[5] & 0xff) << 8)
+ | ((footer[6] & 0xff) << 16) | (footer[7] << 24);
+ if (total != inf.getTotalOut())
+ throw new IOException("Number of bytes mismatch");
+
+ /* FIXME" XXX Should we support multiple members.
+ * Difficult, since there may be some bytes still in buf
+ */
+ eos = true;
+ }
+}
diff --git a/libjava/classpath/java/util/zip/GZIPOutputStream.java b/libjava/classpath/java/util/zip/GZIPOutputStream.java
new file mode 100644
index 000000000..0080ab645
--- /dev/null
+++ b/libjava/classpath/java/util/zip/GZIPOutputStream.java
@@ -0,0 +1,151 @@
+/* GZIPOutputStream.java - Create a file in gzip format
+ Copyright (C) 1999, 2000, 2001 Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath 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 for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module. An independent module is a module which is not derived from
+or based on this library. If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so. If you do not wish to do so, delete this
+exception statement from your version. */
+
+package java.util.zip;
+
+import java.io.IOException;
+import java.io.OutputStream;
+
+/**
+ * This filter stream is used to compress a stream into a "GZIP" stream.
+ * The "GZIP" format is described in RFC 1952.
+ *
+ * @author John Leuner
+ * @author Tom Tromey
+ * @since JDK 1.1
+ */
+
+/* Written using on-line Java Platform 1.2 API Specification
+ * and JCL book.
+ * Believed complete and correct.
+ */
+
+public class GZIPOutputStream extends DeflaterOutputStream
+{
+ /**
+ * CRC-32 value for uncompressed data
+ */
+ protected CRC32 crc;
+
+ /**
+ * Creates a GZIPOutputStream with the default buffer size
+ *
+ * @param out The stream to read data (to be compressed) from
+ *
+ */
+ public GZIPOutputStream(OutputStream out) throws IOException
+ {
+ this(out, 4096);
+ }
+
+ /**
+ * Creates a GZIPOutputStream with the specified buffer size
+ *
+ * @param out The stream to read compressed data from
+ * @param size Size of the buffer to use
+ */
+ public GZIPOutputStream(OutputStream out, int size) throws IOException
+ {
+ super(out, new Deflater(Deflater.DEFAULT_COMPRESSION, true), size);
+ crc = new CRC32();
+ int mod_time = (int) (System.currentTimeMillis() / 1000L);
+ byte[] gzipHeader =
+ {
+ /* The two magic bytes */
+ (byte) GZIPInputStream.GZIP_MAGIC,
+ (byte) (GZIPInputStream.GZIP_MAGIC >> 8),
+
+ /* The compression type */
+ (byte) Deflater.DEFLATED,
+
+ /* The flags (not set) */
+ 0,
+
+ /* The modification time */
+ (byte) mod_time, (byte) (mod_time >> 8),
+ (byte) (mod_time >> 16), (byte) (mod_time >> 24),
+
+ /* The extra flags */
+ 0,
+
+ /* The OS type (unknown) */
+ (byte) 255
+ };
+
+ out.write(gzipHeader);
+ // System.err.println("wrote GZIP header (" + gzipHeader.length + " bytes )");
+ }
+
+ public synchronized void write(byte[] buf, int off, int len)
+ throws IOException
+ {
+ super.write(buf, off, len);
+ crc.update(buf, off, len);
+ }
+
+ /**
+ * Writes remaining compressed output data to the output stream
+ * and closes it.
+ */
+ public void close() throws IOException
+ {
+ finish();
+ out.close();
+ }
+
+ public void finish() throws IOException
+ {
+ super.finish();
+
+ int totalin = def.getTotalIn();
+ int crcval = (int) (crc.getValue() & 0xffffffff);
+
+ // System.err.println("CRC val is " + Integer.toHexString( crcval ) + " and length " + Integer.toHexString(totalin));
+
+ byte[] gzipFooter =
+ {
+ (byte) crcval, (byte) (crcval >> 8),
+ (byte) (crcval >> 16), (byte) (crcval >> 24),
+
+ (byte) totalin, (byte) (totalin >> 8),
+ (byte) (totalin >> 16), (byte) (totalin >> 24)
+ };
+
+ out.write(gzipFooter);
+ // System.err.println("wrote GZIP trailer (" + gzipFooter.length + " bytes )");
+ }
+}
diff --git a/libjava/classpath/java/util/zip/Inflater.java b/libjava/classpath/java/util/zip/Inflater.java
new file mode 100644
index 000000000..0f094d667
--- /dev/null
+++ b/libjava/classpath/java/util/zip/Inflater.java
@@ -0,0 +1,726 @@
+/* Inflater.java - Decompress a data stream
+ Copyright (C) 1999, 2000, 2001, 2003, 2005 Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath 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 for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module. An independent module is a module which is not derived from
+or based on this library. If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so. If you do not wish to do so, delete this
+exception statement from your version. */
+
+package java.util.zip;
+
+/* Written using on-line Java Platform 1.2 API Specification
+ * and JCL book.
+ * Believed complete and correct.
+ */
+
+/**
+ * Inflater is used to decompress data that has been compressed according
+ * to the "deflate" standard described in rfc1950.
+ *
+ * The usage is as following. First you have to set some input with
+ * <code>setInput()</code>, then inflate() it. If inflate doesn't
+ * inflate any bytes there may be three reasons:
+ * <ul>
+ * <li>needsInput() returns true because the input buffer is empty.
+ * You have to provide more input with <code>setInput()</code>.
+ * NOTE: needsInput() also returns true when, the stream is finished.
+ * </li>
+ * <li>needsDictionary() returns true, you have to provide a preset
+ * dictionary with <code>setDictionary()</code>.</li>
+ * <li>finished() returns true, the inflater has finished.</li>
+ * </ul>
+ * Once the first output byte is produced, a dictionary will not be
+ * needed at a later stage.
+ *
+ * @author John Leuner, Jochen Hoenicke
+ * @author Tom Tromey
+ * @date May 17, 1999
+ * @since JDK 1.1
+ */
+public class Inflater
+{
+ /* Copy lengths for literal codes 257..285 */
+ private static final int CPLENS[] =
+ {
+ 3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, 31,
+ 35, 43, 51, 59, 67, 83, 99, 115, 131, 163, 195, 227, 258
+ };
+
+ /* Extra bits for literal codes 257..285 */
+ private static final int CPLEXT[] =
+ {
+ 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2,
+ 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0
+ };
+
+ /* Copy offsets for distance codes 0..29 */
+ private static final int CPDIST[] = {
+ 1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193,
+ 257, 385, 513, 769, 1025, 1537, 2049, 3073, 4097, 6145,
+ 8193, 12289, 16385, 24577
+ };
+
+ /* Extra bits for distance codes */
+ private static final int CPDEXT[] = {
+ 0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6,
+ 7, 7, 8, 8, 9, 9, 10, 10, 11, 11,
+ 12, 12, 13, 13
+ };
+
+ /* This are the state in which the inflater can be. */
+ private static final int DECODE_HEADER = 0;
+ private static final int DECODE_DICT = 1;
+ private static final int DECODE_BLOCKS = 2;
+ private static final int DECODE_STORED_LEN1 = 3;
+ private static final int DECODE_STORED_LEN2 = 4;
+ private static final int DECODE_STORED = 5;
+ private static final int DECODE_DYN_HEADER = 6;
+ private static final int DECODE_HUFFMAN = 7;
+ private static final int DECODE_HUFFMAN_LENBITS = 8;
+ private static final int DECODE_HUFFMAN_DIST = 9;
+ private static final int DECODE_HUFFMAN_DISTBITS = 10;
+ private static final int DECODE_CHKSUM = 11;
+ private static final int FINISHED = 12;
+
+ /** This variable contains the current state. */
+ private int mode;
+
+ /**
+ * The adler checksum of the dictionary or of the decompressed
+ * stream, as it is written in the header resp. footer of the
+ * compressed stream. <br>
+ *
+ * Only valid if mode is DECODE_DICT or DECODE_CHKSUM.
+ */
+ private int readAdler;
+ /**
+ * The number of bits needed to complete the current state. This
+ * is valid, if mode is DECODE_DICT, DECODE_CHKSUM,
+ * DECODE_HUFFMAN_LENBITS or DECODE_HUFFMAN_DISTBITS.
+ */
+ private int neededBits;
+ private int repLength, repDist;
+ private int uncomprLen;
+ /**
+ * True, if the last block flag was set in the last block of the
+ * inflated stream. This means that the stream ends after the
+ * current block.
+ */
+ private boolean isLastBlock;
+
+ /**
+ * The total number of inflated bytes.
+ */
+ private long totalOut;
+ /**
+ * The total number of bytes set with setInput(). This is not the
+ * value returned by getTotalIn(), since this also includes the
+ * unprocessed input.
+ */
+ private long totalIn;
+ /**
+ * This variable stores the nowrap flag that was given to the constructor.
+ * True means, that the inflated stream doesn't contain a header nor the
+ * checksum in the footer.
+ */
+ private boolean nowrap;
+
+ private StreamManipulator input;
+ private OutputWindow outputWindow;
+ private InflaterDynHeader dynHeader;
+ private InflaterHuffmanTree litlenTree, distTree;
+ private Adler32 adler;
+
+ /**
+ * Creates a new inflater.
+ */
+ public Inflater ()
+ {
+ this (false);
+ }
+
+ /**
+ * Creates a new inflater.
+ * @param nowrap true if no header and checksum field appears in the
+ * stream. This is used for GZIPed input. For compatibility with
+ * Sun JDK you should provide one byte of input more than needed in
+ * this case.
+ */
+ public Inflater (boolean nowrap)
+ {
+ this.nowrap = nowrap;
+ this.adler = new Adler32();
+ input = new StreamManipulator();
+ outputWindow = new OutputWindow();
+ mode = nowrap ? DECODE_BLOCKS : DECODE_HEADER;
+ }
+
+ /**
+ * Finalizes this object.
+ */
+ protected void finalize ()
+ {
+ /* Exists only for compatibility */
+ }
+
+ /**
+ * Frees all objects allocated by the inflater. There's no reason
+ * to call this, since you can just rely on garbage collection (even
+ * for the Sun implementation). Exists only for compatibility
+ * with Sun's JDK, where the compressor allocates native memory.
+ * If you call any method (even reset) afterwards the behaviour is
+ * <i>undefined</i>.
+ */
+ public void end ()
+ {
+ outputWindow = null;
+ input = null;
+ dynHeader = null;
+ litlenTree = null;
+ distTree = null;
+ adler = null;
+ }
+
+ /**
+ * Returns true, if the inflater has finished. This means, that no
+ * input is needed and no output can be produced.
+ */
+ public boolean finished()
+ {
+ return mode == FINISHED && outputWindow.getAvailable() == 0;
+ }
+
+ /**
+ * Gets the adler checksum. This is either the checksum of all
+ * uncompressed bytes returned by inflate(), or if needsDictionary()
+ * returns true (and thus no output was yet produced) this is the
+ * adler checksum of the expected dictionary.
+ * @returns the adler checksum.
+ */
+ public int getAdler()
+ {
+ return needsDictionary() ? readAdler : (int) adler.getValue();
+ }
+
+ /**
+ * Gets the number of unprocessed input. Useful, if the end of the
+ * stream is reached and you want to further process the bytes after
+ * the deflate stream.
+ * @return the number of bytes of the input which were not processed.
+ */
+ public int getRemaining()
+ {
+ return input.getAvailableBytes();
+ }
+
+ /**
+ * Gets the total number of processed compressed input bytes.
+ * @return the total number of bytes of processed input bytes.
+ */
+ public int getTotalIn()
+ {
+ return (int) (totalIn - getRemaining());
+ }
+
+ /**
+ * Gets the total number of processed compressed input bytes.
+ * @return the total number of bytes of processed input bytes.
+ * @since 1.5
+ */
+ public long getBytesRead()
+ {
+ return totalIn - getRemaining();
+ }
+
+ /**
+ * Gets the total number of output bytes returned by inflate().
+ * @return the total number of output bytes.
+ */
+ public int getTotalOut()
+ {
+ return (int) totalOut;
+ }
+
+ /**
+ * Gets the total number of output bytes returned by inflate().
+ * @return the total number of output bytes.
+ * @since 1.5
+ */
+ public long getBytesWritten()
+ {
+ return totalOut;
+ }
+
+ /**
+ * Inflates the compressed stream to the output buffer. If this
+ * returns 0, you should check, whether needsDictionary(),
+ * needsInput() or finished() returns true, to determine why no
+ * further output is produced.
+ * @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.
+ * @exception IllegalArgumentException if buf has length 0.
+ */
+ public int inflate (byte[] buf) throws DataFormatException
+ {
+ return inflate (buf, 0, buf.length);
+ }
+
+ /**
+ * Inflates the compressed stream to the output buffer. If this
+ * returns 0, you should check, whether needsDictionary(),
+ * needsInput() or finished() returns true, to determine why no
+ * further output is produced.
+ * @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
+ * output can be produced.
+ * @exception DataFormatException if deflated stream is invalid.
+ * @exception IndexOutOfBoundsException if the off and/or len are wrong.
+ */
+ public int inflate (byte[] buf, int off, int len) throws DataFormatException
+ {
+ /* Check for correct buff, off, len triple */
+ if (0 > off || off > off + len || off + len > buf.length)
+ throw new ArrayIndexOutOfBoundsException();
+ int count = 0;
+ for (;;)
+ {
+ if (outputWindow.getAvailable() == 0)
+ {
+ if (!decode())
+ break;
+ }
+ else if (len > 0)
+ {
+ int more = outputWindow.copyOutput(buf, off, len);
+ adler.update(buf, off, more);
+ off += more;
+ count += more;
+ totalOut += more;
+ len -= more;
+ }
+ else
+ break;
+ }
+ return count;
+ }
+
+ /**
+ * Returns true, if a preset dictionary is needed to inflate the input.
+ */
+ public boolean needsDictionary ()
+ {
+ return mode == DECODE_DICT && neededBits == 0;
+ }
+
+ /**
+ * Returns true, if the input buffer is empty.
+ * You should then call setInput(). <br>
+ *
+ * <em>NOTE</em>: This method also returns true when the stream is finished.
+ */
+ public boolean needsInput ()
+ {
+ return input.needsInput ();
+ }
+
+ /**
+ * Resets the inflater so that a new stream can be decompressed. All
+ * pending input and output will be discarded.
+ */
+ public void reset ()
+ {
+ mode = nowrap ? DECODE_BLOCKS : DECODE_HEADER;
+ totalIn = totalOut = 0;
+ input.reset();
+ outputWindow.reset();
+ dynHeader = null;
+ litlenTree = null;
+ distTree = null;
+ isLastBlock = false;
+ adler.reset();
+ }
+
+ /**
+ * Sets the preset dictionary. This should only be called, if
+ * needsDictionary() returns true and it should set the same
+ * dictionary, that was used for deflating. The getAdler()
+ * function returns the checksum of the dictionary needed.
+ * @param buffer the dictionary.
+ * @exception IllegalStateException if no dictionary is needed.
+ * @exception IllegalArgumentException if the dictionary checksum is
+ * wrong.
+ */
+ public void setDictionary (byte[] buffer)
+ {
+ setDictionary(buffer, 0, buffer.length);
+ }
+
+ /**
+ * Sets the preset dictionary. This should only be called, if
+ * needsDictionary() returns true and it should set the same
+ * dictionary, that was used for deflating. The getAdler()
+ * function returns the checksum of the dictionary needed.
+ * @param buffer the dictionary.
+ * @param off the offset into buffer where the dictionary starts.
+ * @param len the length of the dictionary.
+ * @exception IllegalStateException if no dictionary is needed.
+ * @exception IllegalArgumentException if the dictionary checksum is
+ * wrong.
+ * @exception IndexOutOfBoundsException if the off and/or len are wrong.
+ */
+ public void setDictionary (byte[] buffer, int off, int len)
+ {
+ if (!needsDictionary())
+ throw new IllegalStateException();
+
+ adler.update(buffer, off, len);
+ if ((int) adler.getValue() != readAdler)
+ throw new IllegalArgumentException("Wrong adler checksum");
+ adler.reset();
+ outputWindow.copyDict(buffer, off, len);
+ mode = DECODE_BLOCKS;
+ }
+
+ /**
+ * Sets the input. This should only be called, if needsInput()
+ * returns true.
+ * @param buf the input.
+ * @exception IllegalStateException if no input is needed.
+ */
+ public void setInput (byte[] buf)
+ {
+ setInput (buf, 0, buf.length);
+ }
+
+ /**
+ * Sets the input. This should only be called, if needsInput()
+ * returns true.
+ * @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.
+ * @exception IndexOutOfBoundsException if the off and/or len are wrong.
+ */
+ public void setInput (byte[] buf, int off, int len)
+ {
+ input.setInput (buf, off, len);
+ totalIn += len;
+ }
+
+ /**
+ * Decodes the deflate header.
+ * @return false if more input is needed.
+ * @exception DataFormatException if header is invalid.
+ */
+ private boolean decodeHeader () throws DataFormatException
+ {
+ int header = input.peekBits(16);
+ if (header < 0)
+ return false;
+ input.dropBits(16);
+
+ /* The header is written in "wrong" byte order */
+ header = ((header << 8) | (header >> 8)) & 0xffff;
+ if (header % 31 != 0)
+ throw new DataFormatException("Header checksum illegal");
+
+ if ((header & 0x0f00) != (Deflater.DEFLATED << 8))
+ throw new DataFormatException("Compression Method unknown");
+
+ /* Maximum size of the backwards window in bits.
+ * We currently ignore this, but we could use it to make the
+ * inflater window more space efficient. On the other hand the
+ * full window (15 bits) is needed most times, anyway.
+ int max_wbits = ((header & 0x7000) >> 12) + 8;
+ */
+
+ if ((header & 0x0020) == 0) // Dictionary flag?
+ {
+ mode = DECODE_BLOCKS;
+ }
+ else
+ {
+ mode = DECODE_DICT;
+ neededBits = 32;
+ }
+ return true;
+ }
+
+ /**
+ * Decodes the dictionary checksum after the deflate header.
+ * @return false if more input is needed.
+ */
+ private boolean decodeDict ()
+ {
+ while (neededBits > 0)
+ {
+ int dictByte = input.peekBits(8);
+ if (dictByte < 0)
+ return false;
+ input.dropBits(8);
+ readAdler = (readAdler << 8) | dictByte;
+ neededBits -= 8;
+ }
+ return false;
+ }
+
+ /**
+ * Decodes the huffman encoded symbols in the input stream.
+ * @return false if more input is needed, true if output window is
+ * full or the current block ends.
+ * @exception DataFormatException if deflated stream is invalid.
+ */
+ private boolean decodeHuffman () throws DataFormatException
+ {
+ int free = outputWindow.getFreeSpace();
+ while (free >= 258)
+ {
+ int symbol;
+ switch (mode)
+ {
+ case DECODE_HUFFMAN:
+ /* This is the inner loop so it is optimized a bit */
+ while (((symbol = litlenTree.getSymbol(input)) & ~0xff) == 0)
+ {
+ outputWindow.write(symbol);
+ if (--free < 258)
+ return true;
+ }
+ if (symbol < 257)
+ {
+ if (symbol < 0)
+ return false;
+ else
+ {
+ /* symbol == 256: end of block */
+ distTree = null;
+ litlenTree = null;
+ mode = DECODE_BLOCKS;
+ return true;
+ }
+ }
+
+ try
+ {
+ repLength = CPLENS[symbol - 257];
+ neededBits = CPLEXT[symbol - 257];
+ }
+ catch (ArrayIndexOutOfBoundsException ex)
+ {
+ throw new DataFormatException("Illegal rep length code");
+ }
+ /* fall through */
+ case DECODE_HUFFMAN_LENBITS:
+ if (neededBits > 0)
+ {
+ mode = DECODE_HUFFMAN_LENBITS;
+ int i = input.peekBits(neededBits);
+ if (i < 0)
+ return false;
+ input.dropBits(neededBits);
+ repLength += i;
+ }
+ mode = DECODE_HUFFMAN_DIST;
+ /* fall through */
+ case DECODE_HUFFMAN_DIST:
+ symbol = distTree.getSymbol(input);
+ if (symbol < 0)
+ return false;
+ try
+ {
+ repDist = CPDIST[symbol];
+ neededBits = CPDEXT[symbol];
+ }
+ catch (ArrayIndexOutOfBoundsException ex)
+ {
+ throw new DataFormatException("Illegal rep dist code");
+ }
+ /* fall through */
+ case DECODE_HUFFMAN_DISTBITS:
+ if (neededBits > 0)
+ {
+ mode = DECODE_HUFFMAN_DISTBITS;
+ int i = input.peekBits(neededBits);
+ if (i < 0)
+ return false;
+ input.dropBits(neededBits);
+ repDist += i;
+ }
+ outputWindow.repeat(repLength, repDist);
+ free -= repLength;
+ mode = DECODE_HUFFMAN;
+ break;
+ default:
+ throw new IllegalStateException();
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Decodes the adler checksum after the deflate stream.
+ * @return false if more input is needed.
+ * @exception DataFormatException if checksum doesn't match.
+ */
+ private boolean decodeChksum () throws DataFormatException
+ {
+ while (neededBits > 0)
+ {
+ int chkByte = input.peekBits(8);
+ if (chkByte < 0)
+ return false;
+ input.dropBits(8);
+ readAdler = (readAdler << 8) | chkByte;
+ neededBits -= 8;
+ }
+ if ((int) adler.getValue() != readAdler)
+ throw new DataFormatException("Adler chksum doesn't match: "
+ +Integer.toHexString((int)adler.getValue())
+ +" vs. "+Integer.toHexString(readAdler));
+ mode = FINISHED;
+ return false;
+ }
+
+ /**
+ * Decodes the deflated stream.
+ * @return false if more input is needed, or if finished.
+ * @exception DataFormatException if deflated stream is invalid.
+ */
+ private boolean decode () throws DataFormatException
+ {
+ switch (mode)
+ {
+ case DECODE_HEADER:
+ return decodeHeader();
+ case DECODE_DICT:
+ return decodeDict();
+ case DECODE_CHKSUM:
+ return decodeChksum();
+
+ case DECODE_BLOCKS:
+ if (isLastBlock)
+ {
+ if (nowrap)
+ {
+ mode = FINISHED;
+ return false;
+ }
+ else
+ {
+ input.skipToByteBoundary();
+ neededBits = 32;
+ mode = DECODE_CHKSUM;
+ return true;
+ }
+ }
+
+ int type = input.peekBits(3);
+ if (type < 0)
+ return false;
+ input.dropBits(3);
+
+ if ((type & 1) != 0)
+ isLastBlock = true;
+ switch (type >> 1)
+ {
+ case DeflaterConstants.STORED_BLOCK:
+ input.skipToByteBoundary();
+ mode = DECODE_STORED_LEN1;
+ break;
+ case DeflaterConstants.STATIC_TREES:
+ litlenTree = InflaterHuffmanTree.defLitLenTree;
+ distTree = InflaterHuffmanTree.defDistTree;
+ mode = DECODE_HUFFMAN;
+ break;
+ case DeflaterConstants.DYN_TREES:
+ dynHeader = new InflaterDynHeader();
+ mode = DECODE_DYN_HEADER;
+ break;
+ default:
+ throw new DataFormatException("Unknown block type "+type);
+ }
+ return true;
+
+ case DECODE_STORED_LEN1:
+ {
+ if ((uncomprLen = input.peekBits(16)) < 0)
+ return false;
+ input.dropBits(16);
+ mode = DECODE_STORED_LEN2;
+ }
+ /* fall through */
+ case DECODE_STORED_LEN2:
+ {
+ int nlen = input.peekBits(16);
+ if (nlen < 0)
+ return false;
+ input.dropBits(16);
+ if (nlen != (uncomprLen ^ 0xffff))
+ throw new DataFormatException("broken uncompressed block");
+ mode = DECODE_STORED;
+ }
+ /* fall through */
+ case DECODE_STORED:
+ {
+ int more = outputWindow.copyStored(input, uncomprLen);
+ uncomprLen -= more;
+ if (uncomprLen == 0)
+ {
+ mode = DECODE_BLOCKS;
+ return true;
+ }
+ return !input.needsInput();
+ }
+
+ case DECODE_DYN_HEADER:
+ if (!dynHeader.decode(input))
+ return false;
+ litlenTree = dynHeader.buildLitLenTree();
+ distTree = dynHeader.buildDistTree();
+ mode = DECODE_HUFFMAN;
+ /* fall through */
+ case DECODE_HUFFMAN:
+ case DECODE_HUFFMAN_LENBITS:
+ case DECODE_HUFFMAN_DIST:
+ case DECODE_HUFFMAN_DISTBITS:
+ return decodeHuffman();
+ case FINISHED:
+ return false;
+ default:
+ throw new IllegalStateException();
+ }
+ }
+}
diff --git a/libjava/classpath/java/util/zip/InflaterDynHeader.java b/libjava/classpath/java/util/zip/InflaterDynHeader.java
new file mode 100644
index 000000000..64e08d6bb
--- /dev/null
+++ b/libjava/classpath/java/util/zip/InflaterDynHeader.java
@@ -0,0 +1,203 @@
+/* java.util.zip.InflaterDynHeader
+ Copyright (C) 2001 Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath 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 for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module. An independent module is a module which is not derived from
+or based on this library. If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so. If you do not wish to do so, delete this
+exception statement from your version. */
+
+package java.util.zip;
+
+class InflaterDynHeader
+{
+ private static final int LNUM = 0;
+ private static final int DNUM = 1;
+ private static final int BLNUM = 2;
+ private static final int BLLENS = 3;
+ private static final int LENS = 4;
+ private static final int REPS = 5;
+
+ private static final int repMin[] = { 3, 3, 11 };
+ private static final int repBits[] = { 2, 3, 7 };
+
+
+ private byte[] blLens;
+ private byte[] litdistLens;
+
+ private InflaterHuffmanTree blTree;
+
+ private int mode;
+ private int lnum, dnum, blnum, num;
+ private int repSymbol;
+ private byte lastLen;
+ private int ptr;
+
+ 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 };
+
+ public InflaterDynHeader()
+ {
+ }
+
+ public boolean decode(StreamManipulator input) throws DataFormatException
+ {
+ decode_loop:
+ for (;;)
+ {
+ switch (mode)
+ {
+ case LNUM:
+ lnum = input.peekBits(5);
+ if (lnum < 0)
+ return false;
+ lnum += 257;
+ input.dropBits(5);
+// System.err.println("LNUM: "+lnum);
+ mode = DNUM;
+ /* fall through */
+ case DNUM:
+ dnum = input.peekBits(5);
+ if (dnum < 0)
+ return false;
+ dnum++;
+ input.dropBits(5);
+// System.err.println("DNUM: "+dnum);
+ num = lnum+dnum;
+ litdistLens = new byte[num];
+ mode = BLNUM;
+ /* fall through */
+ case BLNUM:
+ blnum = input.peekBits(4);
+ if (blnum < 0)
+ return false;
+ blnum += 4;
+ input.dropBits(4);
+ blLens = new byte[19];
+ ptr = 0;
+// System.err.println("BLNUM: "+blnum);
+ mode = BLLENS;
+ /* fall through */
+ case BLLENS:
+ while (ptr < blnum)
+ {
+ int len = input.peekBits(3);
+ if (len < 0)
+ return false;
+ input.dropBits(3);
+// System.err.println("blLens["+BL_ORDER[ptr]+"]: "+len);
+ blLens[BL_ORDER[ptr]] = (byte) len;
+ ptr++;
+ }
+ blTree = new InflaterHuffmanTree(blLens);
+ blLens = null;
+ ptr = 0;
+ mode = LENS;
+ /* fall through */
+ case LENS:
+ {
+ int symbol;
+ while (((symbol = blTree.getSymbol(input)) & ~15) == 0)
+ {
+ /* Normal case: symbol in [0..15] */
+
+// System.err.println("litdistLens["+ptr+"]: "+symbol);
+ litdistLens[ptr++] = lastLen = (byte) symbol;
+
+ if (ptr == num)
+ {
+ /* Finished */
+ return true;
+ }
+ }
+
+ /* need more input ? */
+ if (symbol < 0)
+ return false;
+
+ /* otherwise repeat code */
+ if (symbol >= 17)
+ {
+ /* repeat zero */
+// System.err.println("repeating zero");
+ lastLen = 0;
+ }
+ else
+ {
+ if (ptr == 0)
+ throw new DataFormatException();
+ }
+ repSymbol = symbol-16;
+ mode = REPS;
+ }
+ /* fall through */
+
+ case REPS:
+ {
+ int bits = repBits[repSymbol];
+ int count = input.peekBits(bits);
+ if (count < 0)
+ return false;
+ input.dropBits(bits);
+ count += repMin[repSymbol];
+// System.err.println("litdistLens repeated: "+count);
+
+ if (ptr + count > num)
+ throw new DataFormatException();
+ while (count-- > 0)
+ litdistLens[ptr++] = lastLen;
+
+ if (ptr == num)
+ {
+ /* Finished */
+ return true;
+ }
+ }
+ mode = LENS;
+ continue decode_loop;
+ }
+ }
+ }
+
+ public InflaterHuffmanTree buildLitLenTree() throws DataFormatException
+ {
+ byte[] litlenLens = new byte[lnum];
+ System.arraycopy(litdistLens, 0, litlenLens, 0, lnum);
+ return new InflaterHuffmanTree(litlenLens);
+ }
+
+ public InflaterHuffmanTree buildDistTree() throws DataFormatException
+ {
+ byte[] distLens = new byte[dnum];
+ System.arraycopy(litdistLens, lnum, distLens, 0, dnum);
+ return new InflaterHuffmanTree(distLens);
+ }
+}
diff --git a/libjava/classpath/java/util/zip/InflaterHuffmanTree.java b/libjava/classpath/java/util/zip/InflaterHuffmanTree.java
new file mode 100644
index 000000000..c12c732e0
--- /dev/null
+++ b/libjava/classpath/java/util/zip/InflaterHuffmanTree.java
@@ -0,0 +1,217 @@
+/* InflaterHuffmanTree.java --
+ Copyright (C) 2001, 2004 Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath 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 for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module. An independent module is a module which is not derived from
+or based on this library. If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so. If you do not wish to do so, delete this
+exception statement from your version. */
+
+package java.util.zip;
+
+class InflaterHuffmanTree
+{
+ private static final int MAX_BITLEN = 15;
+
+ private short[] tree;
+
+ static InflaterHuffmanTree defLitLenTree, defDistTree;
+
+ static
+ {
+ try
+ {
+ byte[] codeLengths = new byte[288];
+ int i = 0;
+ while (i < 144)
+ codeLengths[i++] = 8;
+ while (i < 256)
+ codeLengths[i++] = 9;
+ while (i < 280)
+ codeLengths[i++] = 7;
+ while (i < 288)
+ codeLengths[i++] = 8;
+ defLitLenTree = new InflaterHuffmanTree(codeLengths);
+
+ codeLengths = new byte[32];
+ i = 0;
+ while (i < 32)
+ codeLengths[i++] = 5;
+ defDistTree = new InflaterHuffmanTree(codeLengths);
+ }
+ catch (DataFormatException ex)
+ {
+ throw new InternalError
+ ("InflaterHuffmanTree: static tree length illegal");
+ }
+ }
+
+ /**
+ * Constructs a Huffman tree from the array of code lengths.
+ *
+ * @param codeLengths the array of code lengths
+ */
+ InflaterHuffmanTree(byte[] codeLengths) throws DataFormatException
+ {
+ buildTree(codeLengths);
+ }
+
+ private void buildTree(byte[] codeLengths) throws DataFormatException
+ {
+ int[] blCount = new int[MAX_BITLEN+1];
+ int[] nextCode = new int[MAX_BITLEN+1];
+ for (int i = 0; i < codeLengths.length; i++)
+ {
+ int bits = codeLengths[i];
+ if (bits > 0)
+ blCount[bits]++;
+ }
+
+ int code = 0;
+ int treeSize = 512;
+ for (int bits = 1; bits <= MAX_BITLEN; bits++)
+ {
+ nextCode[bits] = code;
+ code += blCount[bits] << (16 - bits);
+ if (bits >= 10)
+ {
+ /* We need an extra table for bit lengths >= 10. */
+ int start = nextCode[bits] & 0x1ff80;
+ int end = code & 0x1ff80;
+ treeSize += (end - start) >> (16 - bits);
+ }
+ }
+ if (code != 65536)
+ throw new DataFormatException("Code lengths don't add up properly.");
+
+ /* Now create and fill the extra tables from longest to shortest
+ * bit len. This way the sub trees will be aligned.
+ */
+ tree = new short[treeSize];
+ int treePtr = 512;
+ for (int bits = MAX_BITLEN; bits >= 10; bits--)
+ {
+ int end = code & 0x1ff80;
+ code -= blCount[bits] << (16 - bits);
+ int start = code & 0x1ff80;
+ for (int i = start; i < end; i += 1 << 7)
+ {
+ tree[DeflaterHuffman.bitReverse(i)]
+ = (short) ((-treePtr << 4) | bits);
+ treePtr += 1 << (bits-9);
+ }
+ }
+
+ for (int i = 0; i < codeLengths.length; i++)
+ {
+ int bits = codeLengths[i];
+ if (bits == 0)
+ continue;
+ code = nextCode[bits];
+ int revcode = DeflaterHuffman.bitReverse(code);
+ if (bits <= 9)
+ {
+ do
+ {
+ tree[revcode] = (short) ((i << 4) | bits);
+ revcode += 1 << bits;
+ }
+ while (revcode < 512);
+ }
+ else
+ {
+ int subTree = tree[revcode & 511];
+ int treeLen = 1 << (subTree & 15);
+ subTree = -(subTree >> 4);
+ do
+ {
+ tree[subTree | (revcode >> 9)] = (short) ((i << 4) | bits);
+ revcode += 1 << bits;
+ }
+ while (revcode < treeLen);
+ }
+ nextCode[bits] = code + (1 << (16 - bits));
+ }
+ }
+
+ /**
+ * Reads the next symbol from input. The symbol is encoded using the
+ * huffman tree.
+ * @param input the input source.
+ * @return the next symbol, or -1 if not enough input is available.
+ */
+ int getSymbol(StreamManipulator input) throws DataFormatException
+ {
+ int lookahead, symbol;
+ if ((lookahead = input.peekBits(9)) >= 0)
+ {
+ if ((symbol = tree[lookahead]) >= 0)
+ {
+ input.dropBits(symbol & 15);
+ return symbol >> 4;
+ }
+ int subtree = -(symbol >> 4);
+ int bitlen = symbol & 15;
+ if ((lookahead = input.peekBits(bitlen)) >= 0)
+ {
+ symbol = tree[subtree | (lookahead >> 9)];
+ input.dropBits(symbol & 15);
+ return symbol >> 4;
+ }
+ else
+ {
+ int bits = input.getAvailableBits();
+ lookahead = input.peekBits(bits);
+ symbol = tree[subtree | (lookahead >> 9)];
+ if ((symbol & 15) <= bits)
+ {
+ input.dropBits(symbol & 15);
+ return symbol >> 4;
+ }
+ else
+ return -1;
+ }
+ }
+ else
+ {
+ int bits = input.getAvailableBits();
+ lookahead = input.peekBits(bits);
+ symbol = tree[lookahead];
+ if (symbol >= 0 && (symbol & 15) <= bits)
+ {
+ input.dropBits(symbol & 15);
+ return symbol >> 4;
+ }
+ else
+ return -1;
+ }
+ }
+}
diff --git a/libjava/classpath/java/util/zip/InflaterInputStream.java b/libjava/classpath/java/util/zip/InflaterInputStream.java
new file mode 100644
index 000000000..1c5e9f2dd
--- /dev/null
+++ b/libjava/classpath/java/util/zip/InflaterInputStream.java
@@ -0,0 +1,265 @@
+/* InflaterInputStream.java - Input stream filter for decompressing
+ Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004, 2006
+ Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath 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 for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module. An independent module is a module which is not derived from
+or based on this library. If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so. If you do not wish to do so, delete this
+exception statement from your version. */
+
+
+package java.util.zip;
+
+import java.io.FilterInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+
+/**
+ * This filter stream is used to decompress data compressed in the "deflate"
+ * format. The "deflate" format is described in RFC 1951.
+ *
+ * This stream may form the basis for other decompression filters, such
+ * as the <code>GZIPInputStream</code>.
+ *
+ * @author John Leuner
+ * @author Tom Tromey
+ * @since 1.1
+ */
+public class InflaterInputStream extends FilterInputStream
+{
+ /**
+ * Decompressor for this filter
+ */
+ protected Inflater inf;
+
+ /**
+ * Byte array used as a buffer
+ */
+ protected byte[] buf;
+
+ /**
+ * Size of buffer
+ */
+ protected int len;
+
+ // We just use this if we are decoding one byte at a time with the
+ // read() call.
+ private byte[] onebytebuffer = new byte[1];
+
+ /**
+ * Create an InflaterInputStream with the default decompresseor
+ * and a default buffer size.
+ *
+ * @param in the InputStream to read bytes from
+ */
+ public InflaterInputStream(InputStream in)
+ {
+ this(in, new Inflater(), 4096);
+ }
+
+ /**
+ * Create an InflaterInputStream with the specified decompresseor
+ * and a default buffer size.
+ *
+ * @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)
+ {
+ this(in, inf, 4096);
+ }
+
+ /**
+ * Create an InflaterInputStream with the specified decompresseor
+ * and a specified buffer size.
+ *
+ * @param in the InputStream to read bytes from
+ * @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)
+ {
+ super(in);
+
+ if (in == null)
+ throw new NullPointerException("in may not be null");
+ if (inf == null)
+ throw new NullPointerException("inf may not be null");
+ if (size < 0)
+ throw new IllegalArgumentException("size may not be negative");
+
+ this.inf = inf;
+ this.buf = new byte [size];
+ }
+
+ /**
+ * Returns 0 once the end of the stream (EOF) has been reached.
+ * Otherwise returns 1.
+ */
+ public int available() throws IOException
+ {
+ // According to the JDK 1.2 docs, this should only ever return 0
+ // or 1 and should not be relied upon by Java programs.
+ if (inf == null)
+ throw new IOException("stream closed");
+ return inf.finished() ? 0 : 1;
+ }
+
+ /**
+ * Closes the input stream
+ */
+ public synchronized void close() throws IOException
+ {
+ if (in != null)
+ in.close();
+ in = null;
+ }
+
+ /**
+ * Fills the buffer with more data to decompress.
+ */
+ protected void fill() throws IOException
+ {
+ if (in == null)
+ throw new ZipException ("InflaterInputStream is closed");
+
+ len = in.read(buf, 0, buf.length);
+
+ if (len < 0)
+ throw new ZipException("Deflated stream ends early.");
+
+ inf.setInput(buf, 0, len);
+ }
+
+ /**
+ * Reads one byte of decompressed data.
+ *
+ * The byte is in the lower 8 bits of the int.
+ */
+ public int read() throws IOException
+ {
+ int nread = read(onebytebuffer, 0, 1);
+ if (nread > 0)
+ return onebytebuffer[0] & 0xff;
+ return -1;
+ }
+
+ /**
+ * Decompresses data into the byte array
+ *
+ * @param b the array to read and decompress data into
+ * @param off the offset indicating where the data should be placed
+ * @param len the number of bytes to decompress
+ */
+ public int read(byte[] b, int off, int len) throws IOException
+ {
+ if (inf == null)
+ throw new IOException("stream closed");
+ if (len == 0)
+ return 0;
+ if (inf.finished())
+ return -1;
+
+ int count = 0;
+ while (count == 0)
+ {
+ if (inf.needsInput())
+ fill();
+
+ try
+ {
+ count = inf.inflate(b, off, len);
+ if (count == 0)
+ {
+ if (this.len == -1)
+ {
+ // Couldn't get any more data to feed to the Inflater
+ return -1;
+ }
+ if (inf.needsDictionary())
+ throw new ZipException("Inflater needs Dictionary");
+ }
+ }
+ catch (DataFormatException dfe)
+ {
+ throw new ZipException(dfe.getMessage());
+ }
+ }
+ return count;
+ }
+
+ /**
+ * Skip specified number of bytes of uncompressed data
+ *
+ * @param n number of bytes to skip
+ */
+ public long skip(long n) throws IOException
+ {
+ if (inf == null)
+ throw new IOException("stream closed");
+ if (n < 0)
+ throw new IllegalArgumentException();
+
+ if (n == 0)
+ return 0;
+
+ int buflen = (int) Math.min(n, 2048);
+ byte[] tmpbuf = new byte[buflen];
+
+ long skipped = 0L;
+ while (n > 0L)
+ {
+ int numread = read(tmpbuf, 0, buflen);
+ if (numread <= 0)
+ break;
+ n -= numread;
+ skipped += numread;
+ buflen = (int) Math.min(n, 2048);
+ }
+
+ return skipped;
+ }
+
+ public boolean markSupported()
+ {
+ return false;
+ }
+
+ public void mark(int readLimit)
+ {
+ }
+
+ public void reset() throws IOException
+ {
+ throw new IOException("reset not supported");
+ }
+}
diff --git a/libjava/classpath/java/util/zip/OutputWindow.java b/libjava/classpath/java/util/zip/OutputWindow.java
new file mode 100644
index 000000000..59dadb5f3
--- /dev/null
+++ b/libjava/classpath/java/util/zip/OutputWindow.java
@@ -0,0 +1,175 @@
+/* OutputWindow.java --
+ Copyright (C) 2001, 2004 Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath 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 for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module. An independent module is a module which is not derived from
+or based on this library. If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so. If you do not wish to do so, delete this
+exception statement from your version. */
+
+package java.util.zip;
+
+/**
+ * Contains the output from the Inflation process.
+ *
+ * We need to have a window so that we can refer backwards into the output stream
+ * to repeat stuff.
+ *
+ * @author John Leuner
+ * @since 1.1
+ */
+class OutputWindow
+{
+ private static final int WINDOW_SIZE = 1 << 15;
+ private static final int WINDOW_MASK = WINDOW_SIZE - 1;
+
+ private byte[] window = new byte[WINDOW_SIZE]; //The window is 2^15 bytes
+ private int window_end = 0;
+ private int window_filled = 0;
+
+ public void write(int abyte)
+ {
+ if (window_filled++ == WINDOW_SIZE)
+ throw new IllegalStateException("Window full");
+ window[window_end++] = (byte) abyte;
+ window_end &= WINDOW_MASK;
+ }
+
+ private void slowRepeat(int rep_start, int len, int dist)
+ {
+ while (len-- > 0)
+ {
+ window[window_end++] = window[rep_start++];
+ window_end &= WINDOW_MASK;
+ rep_start &= WINDOW_MASK;
+ }
+ }
+
+ public void repeat(int len, int dist)
+ {
+ if ((window_filled += len) > WINDOW_SIZE)
+ throw new IllegalStateException("Window full");
+
+ int rep_start = (window_end - dist) & WINDOW_MASK;
+ int border = WINDOW_SIZE - len;
+ if (rep_start <= border && window_end < border)
+ {
+ if (len <= dist)
+ {
+ System.arraycopy(window, rep_start, window, window_end, len);
+ window_end += len;
+ }
+ else
+ {
+ /* We have to copy manually, since the repeat pattern overlaps.
+ */
+ while (len-- > 0)
+ window[window_end++] = window[rep_start++];
+ }
+ }
+ else
+ slowRepeat(rep_start, len, dist);
+ }
+
+ public int copyStored(StreamManipulator input, int len)
+ {
+ len = Math.min(Math.min(len, WINDOW_SIZE - window_filled),
+ input.getAvailableBytes());
+ int copied;
+
+ int tailLen = WINDOW_SIZE - window_end;
+ if (len > tailLen)
+ {
+ copied = input.copyBytes(window, window_end, tailLen);
+ if (copied == tailLen)
+ copied += input.copyBytes(window, 0, len - tailLen);
+ }
+ else
+ copied = input.copyBytes(window, window_end, len);
+
+ window_end = (window_end + copied) & WINDOW_MASK;
+ window_filled += copied;
+ return copied;
+ }
+
+ public void copyDict(byte[] dict, int offset, int len)
+ {
+ if (window_filled > 0)
+ throw new IllegalStateException();
+
+ if (len > WINDOW_SIZE)
+ {
+ offset += len - WINDOW_SIZE;
+ len = WINDOW_SIZE;
+ }
+ System.arraycopy(dict, offset, window, 0, len);
+ window_end = len & WINDOW_MASK;
+ }
+
+ public int getFreeSpace()
+ {
+ return WINDOW_SIZE - window_filled;
+ }
+
+ public int getAvailable()
+ {
+ return window_filled;
+ }
+
+ public int copyOutput(byte[] output, int offset, int len)
+ {
+ int copy_end = window_end;
+ if (len > window_filled)
+ len = window_filled;
+ else
+ copy_end = (window_end - window_filled + len) & WINDOW_MASK;
+
+ int copied = len;
+ int tailLen = len - copy_end;
+
+ if (tailLen > 0)
+ {
+ System.arraycopy(window, WINDOW_SIZE - tailLen,
+ output, offset, tailLen);
+ offset += tailLen;
+ len = copy_end;
+ }
+ System.arraycopy(window, copy_end - len, output, offset, len);
+ window_filled -= copied;
+ if (window_filled < 0)
+ throw new IllegalStateException();
+ return copied;
+ }
+
+ public void reset() {
+ window_filled = window_end = 0;
+ }
+}
diff --git a/libjava/classpath/java/util/zip/PendingBuffer.java b/libjava/classpath/java/util/zip/PendingBuffer.java
new file mode 100644
index 000000000..50f561f49
--- /dev/null
+++ b/libjava/classpath/java/util/zip/PendingBuffer.java
@@ -0,0 +1,199 @@
+/* java.util.zip.PendingBuffer
+ Copyright (C) 2001 Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath 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 for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module. An independent module is a module which is not derived from
+or based on this library. If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so. If you do not wish to do so, delete this
+exception statement from your version. */
+
+package java.util.zip;
+
+/**
+ * This class is general purpose class for writing data to a buffer.
+ *
+ * It allows you to write bits as well as bytes
+ *
+ * Based on DeflaterPending.java
+ *
+ * @author Jochen Hoenicke
+ * @date Jan 5, 2000
+ */
+
+class PendingBuffer
+{
+ protected byte[] buf;
+ int start;
+ int end;
+
+ int bits;
+ int bitCount;
+
+ public PendingBuffer()
+ {
+ this( 4096 );
+ }
+
+ public PendingBuffer(int bufsize)
+ {
+ buf = new byte[bufsize];
+ }
+
+ public final void reset() {
+ start = end = bitCount = 0;
+ }
+
+ public final void writeByte(int b)
+ {
+ if (DeflaterConstants.DEBUGGING && start != 0)
+ throw new IllegalStateException();
+ buf[end++] = (byte) b;
+ }
+
+ public final void writeShort(int s)
+ {
+ if (DeflaterConstants.DEBUGGING && start != 0)
+ throw new IllegalStateException();
+ buf[end++] = (byte) s;
+ buf[end++] = (byte) (s >> 8);
+ }
+
+ public final void writeInt(int s)
+ {
+ if (DeflaterConstants.DEBUGGING && start != 0)
+ throw new IllegalStateException();
+ buf[end++] = (byte) s;
+ buf[end++] = (byte) (s >> 8);
+ buf[end++] = (byte) (s >> 16);
+ buf[end++] = (byte) (s >> 24);
+ }
+
+ public final void writeBlock(byte[] block, int offset, int len)
+ {
+ if (DeflaterConstants.DEBUGGING && start != 0)
+ throw new IllegalStateException();
+ System.arraycopy(block, offset, buf, end, len);
+ end += len;
+ }
+
+ public final int getBitCount() {
+ return bitCount;
+ }
+
+ public final void alignToByte() {
+ if (DeflaterConstants.DEBUGGING && start != 0)
+ throw new IllegalStateException();
+ if (bitCount > 0)
+ {
+ buf[end++] = (byte) bits;
+ if (bitCount > 8)
+ buf[end++] = (byte) (bits >>> 8);
+ }
+ bits = 0;
+ bitCount = 0;
+ }
+
+ public final void writeBits(int b, int count)
+ {
+ if (DeflaterConstants.DEBUGGING && start != 0)
+ throw new IllegalStateException();
+ if (DeflaterConstants.DEBUGGING)
+ System.err.println("writeBits("+Integer.toHexString(b)+","+count+")");
+ bits |= b << bitCount;
+ bitCount += count;
+ if (bitCount >= 16) {
+ buf[end++] = (byte) bits;
+ buf[end++] = (byte) (bits >>> 8);
+ bits >>>= 16;
+ bitCount -= 16;
+ }
+ }
+
+ public final void writeShortMSB(int s) {
+ if (DeflaterConstants.DEBUGGING && start != 0)
+ throw new IllegalStateException();
+ buf[end++] = (byte) (s >> 8);
+ buf[end++] = (byte) s;
+ }
+
+ public final boolean isFlushed() {
+ return end == 0;
+ }
+
+ /**
+ * Flushes the pending buffer into the given output array. If the
+ * output array is to small, only a partial flush is done.
+ *
+ * @param output the output array;
+ * @param offset the offset into output array;
+ * @param length the maximum number of bytes to store;
+ * @exception IndexOutOfBoundsException if offset or length are
+ * invalid.
+ */
+ public final int flush(byte[] output, int offset, int length) {
+ if (bitCount >= 8)
+ {
+ buf[end++] = (byte) bits;
+ bits >>>= 8;
+ bitCount -= 8;
+ }
+ if (length > end - start)
+ {
+ length = end - start;
+ System.arraycopy(buf, start, output, offset, length);
+ start = 0;
+ end = 0;
+ }
+ else
+ {
+ System.arraycopy(buf, start, output, offset, length);
+ start += length;
+ }
+ return length;
+ }
+
+ /**
+ * Flushes the pending buffer and returns that data in a new array
+ *
+ * @return the output stream
+ */
+
+ public final byte[] toByteArray()
+ {
+ byte[] ret = new byte[ end - start ];
+ System.arraycopy(buf, start, ret, 0, ret.length);
+ start = 0;
+ end = 0;
+ return ret;
+ }
+
+
+}
diff --git a/libjava/classpath/java/util/zip/StreamManipulator.java b/libjava/classpath/java/util/zip/StreamManipulator.java
new file mode 100644
index 000000000..105d807e4
--- /dev/null
+++ b/libjava/classpath/java/util/zip/StreamManipulator.java
@@ -0,0 +1,215 @@
+/* java.util.zip.StreamManipulator
+ Copyright (C) 2001 Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath 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 for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module. An independent module is a module which is not derived from
+or based on this library. If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so. If you do not wish to do so, delete this
+exception statement from your version. */
+
+package java.util.zip;
+
+/**
+ * This class allows us to retrieve a specified amount of bits from
+ * the input buffer, as well as copy big byte blocks.
+ *
+ * It uses an int buffer to store up to 31 bits for direct
+ * manipulation. This guarantees that we can get at least 16 bits,
+ * but we only need at most 15, so this is all safe.
+ *
+ * There are some optimizations in this class, for example, you must
+ * never peek more then 8 bits more than needed, and you must first
+ * peek bits before you may drop them. This is not a general purpose
+ * class but optimized for the behaviour of the Inflater.
+ *
+ * @author John Leuner, Jochen Hoenicke
+ */
+
+class StreamManipulator
+{
+ private byte[] window;
+ private int window_start = 0;
+ private int window_end = 0;
+
+ private int buffer = 0;
+ private int bits_in_buffer = 0;
+
+ /**
+ * Get the next n bits but don't increase input pointer. n must be
+ * less or equal 16 and if you if this call succeeds, you must drop
+ * at least n-8 bits in the next call.
+ *
+ * @return the value of the bits, or -1 if not enough bits available. */
+ public final int peekBits(int n)
+ {
+ if (bits_in_buffer < n)
+ {
+ if (window_start == window_end)
+ return -1;
+ buffer |= (window[window_start++] & 0xff
+ | (window[window_start++] & 0xff) << 8) << bits_in_buffer;
+ bits_in_buffer += 16;
+ }
+ return buffer & ((1 << n) - 1);
+ }
+
+ /* Drops the next n bits from the input. You should have called peekBits
+ * with a bigger or equal n before, to make sure that enough bits are in
+ * the bit buffer.
+ */
+ public final void dropBits(int n)
+ {
+ buffer >>>= n;
+ bits_in_buffer -= n;
+ }
+
+ /**
+ * Gets the next n bits and increases input pointer. This is equivalent
+ * to peekBits followed by dropBits, except for correct error handling.
+ * @return the value of the bits, or -1 if not enough bits available.
+ */
+ public final int getBits(int n)
+ {
+ int bits = peekBits(n);
+ if (bits >= 0)
+ dropBits(n);
+ return bits;
+ }
+ /**
+ * Gets the number of bits available in the bit buffer. This must be
+ * only called when a previous peekBits() returned -1.
+ * @return the number of bits available.
+ */
+ public final int getAvailableBits()
+ {
+ return bits_in_buffer;
+ }
+
+ /**
+ * Gets the number of bytes available.
+ * @return the number of bytes available.
+ */
+ public final int getAvailableBytes()
+ {
+ return window_end - window_start + (bits_in_buffer >> 3);
+ }
+
+ /**
+ * Skips to the next byte boundary.
+ */
+ public void skipToByteBoundary()
+ {
+ buffer >>= (bits_in_buffer & 7);
+ bits_in_buffer &= ~7;
+ }
+
+ public final boolean needsInput() {
+ return window_start == window_end;
+ }
+
+
+ /* Copies length bytes from input buffer to output buffer starting
+ * at output[offset]. You have to make sure, that the buffer is
+ * byte aligned. If not enough bytes are available, copies fewer
+ * bytes.
+ * @param length the length to copy, 0 is allowed.
+ * @return the number of bytes copied, 0 if no byte is available.
+ */
+ public int copyBytes(byte[] output, int offset, int length)
+ {
+ if (length < 0)
+ throw new IllegalArgumentException("length negative");
+ if ((bits_in_buffer & 7) != 0)
+ /* bits_in_buffer may only be 0 or 8 */
+ throw new IllegalStateException("Bit buffer is not aligned!");
+
+ int count = 0;
+ while (bits_in_buffer > 0 && length > 0)
+ {
+ output[offset++] = (byte) buffer;
+ buffer >>>= 8;
+ bits_in_buffer -= 8;
+ length--;
+ count++;
+ }
+ if (length == 0)
+ return count;
+
+ int avail = window_end - window_start;
+ if (length > avail)
+ length = avail;
+ System.arraycopy(window, window_start, output, offset, length);
+ window_start += length;
+
+ if (((window_start - window_end) & 1) != 0)
+ {
+ /* We always want an even number of bytes in input, see peekBits */
+ buffer = (window[window_start++] & 0xff);
+ bits_in_buffer = 8;
+ }
+ return count + length;
+ }
+
+ public StreamManipulator()
+ {
+ }
+
+ public void reset()
+ {
+ window_start = window_end = buffer = bits_in_buffer = 0;
+ }
+
+ public void setInput(byte[] buf, int off, int len)
+ {
+ if (window_start < window_end)
+ throw new IllegalStateException
+ ("Old input was not completely processed");
+
+ int end = off + len;
+
+ /* We want to throw an ArrayIndexOutOfBoundsException early. The
+ * check is very tricky: it also handles integer wrap around.
+ */
+ if (0 > off || off > end || end > buf.length)
+ throw new ArrayIndexOutOfBoundsException();
+
+ if ((len & 1) != 0)
+ {
+ /* We always want an even number of bytes in input, see peekBits */
+ buffer |= (buf[off++] & 0xff) << bits_in_buffer;
+ bits_in_buffer += 8;
+ }
+
+ window = buf;
+ window_start = off;
+ window_end = end;
+ }
+}
diff --git a/libjava/classpath/java/util/zip/ZipConstants.java b/libjava/classpath/java/util/zip/ZipConstants.java
new file mode 100644
index 000000000..69f589a31
--- /dev/null
+++ b/libjava/classpath/java/util/zip/ZipConstants.java
@@ -0,0 +1,93 @@
+/* java.util.zip.ZipConstants
+ Copyright (C) 2001, 2006 Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath 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 for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module. An independent module is a module which is not derived from
+or based on this library. If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so. If you do not wish to do so, delete this
+exception statement from your version. */
+
+package java.util.zip;
+
+interface ZipConstants
+{
+ /* The local file header */
+ int LOCHDR = 30;
+ long LOCSIG = 'P'|('K'<<8)|(3<<16)|(4<<24);
+
+ int LOCVER = 4;
+ int LOCFLG = 6;
+ int LOCHOW = 8;
+ int LOCTIM = 10;
+ int LOCCRC = 14;
+ int LOCSIZ = 18;
+ int LOCLEN = 22;
+ int LOCNAM = 26;
+ int LOCEXT = 28;
+
+ /* The Data descriptor */
+ long EXTSIG = 'P'|('K'<<8)|(7<<16)|(8<<24);
+ int EXTHDR = 16;
+
+ int EXTCRC = 4;
+ int EXTSIZ = 8;
+ int EXTLEN = 12;
+
+ /* The central directory file header */
+ long CENSIG = 'P'|('K'<<8)|(1<<16)|(2<<24);
+ int CENHDR = 46;
+
+ int CENVEM = 4;
+ int CENVER = 6;
+ int CENFLG = 8;
+ int CENHOW = 10;
+ int CENTIM = 12;
+ int CENCRC = 16;
+ int CENSIZ = 20;
+ int CENLEN = 24;
+ int CENNAM = 28;
+ int CENEXT = 30;
+ int CENCOM = 32;
+ int CENDSK = 34;
+ int CENATT = 36;
+ int CENATX = 38;
+ int CENOFF = 42;
+
+ /* The entries in the end of central directory */
+ long ENDSIG = 'P'|('K'<<8)|(5<<16)|(6<<24);
+ int ENDHDR = 22;
+
+ int ENDSUB = 8;
+ int ENDTOT = 10;
+ int ENDSIZ = 12;
+ int ENDOFF = 16;
+ int ENDCOM = 20;
+}
diff --git a/libjava/classpath/java/util/zip/ZipEntry.java b/libjava/classpath/java/util/zip/ZipEntry.java
new file mode 100644
index 000000000..73afc893b
--- /dev/null
+++ b/libjava/classpath/java/util/zip/ZipEntry.java
@@ -0,0 +1,457 @@
+/* ZipEntry.java --
+ Copyright (C) 2001, 2002, 2004, 2005 Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath 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 for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module. An independent module is a module which is not derived from
+or based on this library. If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so. If you do not wish to do so, delete this
+exception statement from your version. */
+
+
+package java.util.zip;
+
+import java.util.Calendar;
+
+/**
+ * This class represents a member of a zip archive. ZipFile and
+ * ZipInputStream will give you instances of this class as information
+ * about the members in an archive. On the other hand ZipOutputStream
+ * needs an instance of this class to create a new member.
+ *
+ * @author Jochen Hoenicke
+ */
+public class ZipEntry implements ZipConstants, Cloneable
+{
+ private static final byte KNOWN_SIZE = 1;
+ private static final byte KNOWN_CSIZE = 2;
+ private static final byte KNOWN_CRC = 4;
+ private static final byte KNOWN_TIME = 8;
+ private static final byte KNOWN_DOSTIME = 16;
+ private static final byte KNOWN_EXTRA = 32;
+
+ /** Immutable name of the entry */
+ private final String name;
+ /** Uncompressed size */
+ private int size;
+ /** Compressed size */
+ private long compressedSize = -1;
+ /** CRC of uncompressed data */
+ private int crc;
+ /** Comment or null if none */
+ private String comment = null;
+ /** The compression method. Either DEFLATED or STORED, by default -1. */
+ private byte method = -1;
+ /** Flags specifying what we know about this entry */
+ private byte known = 0;
+ /**
+ * The 32bit DOS encoded format for the time of this entry. Only valid if
+ * KNOWN_DOSTIME is set in known.
+ */
+ private int dostime;
+ /**
+ * The 64bit Java encoded millisecond time since the beginning of the epoch.
+ * Only valid if KNOWN_TIME is set in known.
+ */
+ private long time;
+ /** Extra data */
+ private byte[] extra = null;
+
+ int flags; /* used by ZipOutputStream */
+ int offset; /* used by ZipFile and ZipOutputStream */
+
+ /**
+ * Compression method. This method doesn't compress at all.
+ */
+ public static final int STORED = 0;
+ /**
+ * Compression method. This method uses the Deflater.
+ */
+ public static final int DEFLATED = 8;
+
+ /**
+ * Creates a zip entry with the given name.
+ * @param name the name. May include directory components separated
+ * by '/'.
+ *
+ * @exception NullPointerException when name is null.
+ * @exception IllegalArgumentException when name is bigger then 65535 chars.
+ */
+ public ZipEntry(String name)
+ {
+ int length = name.length();
+ if (length > 65535)
+ throw new IllegalArgumentException("name length is " + length);
+ this.name = name;
+ }
+
+ /**
+ * Creates a copy of the given zip entry.
+ * @param e the entry to copy.
+ */
+ public ZipEntry(ZipEntry e)
+ {
+ this(e, e.name);
+ }
+
+ ZipEntry(ZipEntry e, String name)
+ {
+ this.name = name;
+ known = e.known;
+ size = e.size;
+ compressedSize = e.compressedSize;
+ crc = e.crc;
+ dostime = e.dostime;
+ time = e.time;
+ method = e.method;
+ extra = e.extra;
+ comment = e.comment;
+ }
+
+ final void setDOSTime(int dostime)
+ {
+ this.dostime = dostime;
+ known |= KNOWN_DOSTIME;
+ known &= ~KNOWN_TIME;
+ }
+
+ final int getDOSTime()
+ {
+ if ((known & KNOWN_DOSTIME) != 0)
+ return dostime;
+ else if ((known & KNOWN_TIME) != 0)
+ {
+ Calendar cal = Calendar.getInstance();
+ cal.setTimeInMillis(time);
+ dostime = (cal.get(Calendar.YEAR) - 1980 & 0x7f) << 25
+ | (cal.get(Calendar.MONTH) + 1) << 21
+ | (cal.get(Calendar.DAY_OF_MONTH)) << 16
+ | (cal.get(Calendar.HOUR_OF_DAY)) << 11
+ | (cal.get(Calendar.MINUTE)) << 5
+ | (cal.get(Calendar.SECOND)) >> 1;
+ known |= KNOWN_DOSTIME;
+ return dostime;
+ }
+ else
+ return 0;
+ }
+
+ /**
+ * Creates a copy of this zip entry.
+ */
+ public Object clone()
+ {
+ // JCL defines this as being the same as the copy constructor above,
+ // except that value of the "extra" field is also copied. Take care
+ // that in the case of a subclass we use clone() rather than the copy
+ // constructor.
+ ZipEntry clone;
+ if (this.getClass() == ZipEntry.class)
+ clone = new ZipEntry(this);
+ else
+ {
+ try
+ {
+ clone = (ZipEntry) super.clone();
+ }
+ catch (CloneNotSupportedException e)
+ {
+ throw new InternalError();
+ }
+ }
+ if (extra != null)
+ {
+ clone.extra = new byte[extra.length];
+ System.arraycopy(extra, 0, clone.extra, 0, extra.length);
+ }
+ return clone;
+ }
+
+ /**
+ * Returns the entry name. The path components in the entry are
+ * always separated by slashes ('/').
+ */
+ public String getName()
+ {
+ return name;
+ }
+
+ /**
+ * Sets the time of last modification of the entry.
+ * @time the time of last modification of the entry.
+ */
+ public void setTime(long time)
+ {
+ this.time = time;
+ this.known |= KNOWN_TIME;
+ this.known &= ~KNOWN_DOSTIME;
+ }
+
+ /**
+ * Gets the time of last modification of the entry.
+ * @return the time of last modification of the entry, or -1 if unknown.
+ */
+ public long getTime()
+ {
+ // The extra bytes might contain the time (posix/unix extension)
+ parseExtra();
+
+ if ((known & KNOWN_TIME) != 0)
+ return time;
+ else if ((known & KNOWN_DOSTIME) != 0)
+ {
+ 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 */
+
+ try
+ {
+ Calendar cal = Calendar.getInstance();
+ cal.set(year, mon, day, hrs, min, sec);
+ time = cal.getTimeInMillis();
+ known |= KNOWN_TIME;
+ return time;
+ }
+ catch (RuntimeException ex)
+ {
+ /* Ignore illegal time stamp */
+ known &= ~KNOWN_TIME;
+ return -1;
+ }
+ }
+ else
+ return -1;
+ }
+
+ /**
+ * Sets the size of the uncompressed data.
+ * @exception IllegalArgumentException if size is not in 0..0xffffffffL
+ */
+ public void setSize(long size)
+ {
+ if ((size & 0xffffffff00000000L) != 0)
+ throw new IllegalArgumentException();
+ this.size = (int) size;
+ this.known |= KNOWN_SIZE;
+ }
+
+ /**
+ * Gets the size of the uncompressed data.
+ * @return the size or -1 if unknown.
+ */
+ public long getSize()
+ {
+ return (known & KNOWN_SIZE) != 0 ? size & 0xffffffffL : -1L;
+ }
+
+ /**
+ * Sets the size of the compressed data.
+ */
+ public void setCompressedSize(long csize)
+ {
+ this.compressedSize = csize;
+ }
+
+ /**
+ * Gets the size of the compressed data.
+ * @return the size or -1 if unknown.
+ */
+ public long getCompressedSize()
+ {
+ return compressedSize;
+ }
+
+ /**
+ * Sets the crc of the uncompressed data.
+ * @exception IllegalArgumentException if crc is not in 0..0xffffffffL
+ */
+ public void setCrc(long crc)
+ {
+ if ((crc & 0xffffffff00000000L) != 0)
+ throw new IllegalArgumentException();
+ this.crc = (int) crc;
+ this.known |= KNOWN_CRC;
+ }
+
+ /**
+ * Gets the crc of the uncompressed data.
+ * @return the crc or -1 if unknown.
+ */
+ public long getCrc()
+ {
+ return (known & KNOWN_CRC) != 0 ? crc & 0xffffffffL : -1L;
+ }
+
+ /**
+ * Sets the compression method. Only DEFLATED and STORED are
+ * supported.
+ * @exception IllegalArgumentException if method is not supported.
+ * @see ZipOutputStream#DEFLATED
+ * @see ZipOutputStream#STORED
+ */
+ public void setMethod(int method)
+ {
+ if (method != ZipOutputStream.STORED
+ && method != ZipOutputStream.DEFLATED)
+ throw new IllegalArgumentException();
+ this.method = (byte) method;
+ }
+
+ /**
+ * Gets the compression method.
+ * @return the compression method or -1 if unknown.
+ */
+ public int getMethod()
+ {
+ return method;
+ }
+
+ /**
+ * Sets the extra data.
+ * @exception IllegalArgumentException if extra is longer than 0xffff bytes.
+ */
+ public void setExtra(byte[] extra)
+ {
+ if (extra == null)
+ {
+ this.extra = null;
+ return;
+ }
+ if (extra.length > 0xffff)
+ throw new IllegalArgumentException();
+ this.extra = extra;
+ }
+
+ private void parseExtra()
+ {
+ // Already parsed?
+ if ((known & KNOWN_EXTRA) != 0)
+ return;
+
+ if (extra == null)
+ {
+ known |= KNOWN_EXTRA;
+ return;
+ }
+
+ try
+ {
+ int pos = 0;
+ while (pos < extra.length)
+ {
+ int sig = (extra[pos++] & 0xff)
+ | (extra[pos++] & 0xff) << 8;
+ int len = (extra[pos++] & 0xff)
+ | (extra[pos++] & 0xff) << 8;
+ if (sig == 0x5455)
+ {
+ /* extended time stamp */
+ int flags = extra[pos];
+ if ((flags & 1) != 0)
+ {
+ long time = ((extra[pos+1] & 0xff)
+ | (extra[pos+2] & 0xff) << 8
+ | (extra[pos+3] & 0xff) << 16
+ | (extra[pos+4] & 0xff) << 24);
+ setTime(time*1000);
+ }
+ }
+ pos += len;
+ }
+ }
+ catch (ArrayIndexOutOfBoundsException ex)
+ {
+ /* be lenient */
+ }
+
+ known |= KNOWN_EXTRA;
+ return;
+ }
+
+ /**
+ * Gets the extra data.
+ * @return the extra data or null if not set.
+ */
+ public byte[] getExtra()
+ {
+ return extra;
+ }
+
+ /**
+ * Sets the entry comment.
+ * @exception IllegalArgumentException if comment is longer than 0xffff.
+ */
+ public void setComment(String comment)
+ {
+ if (comment != null && comment.length() > 0xffff)
+ throw new IllegalArgumentException();
+ this.comment = comment;
+ }
+
+ /**
+ * Gets the comment.
+ * @return the comment or null if not set.
+ */
+ public String getComment()
+ {
+ return comment;
+ }
+
+ /**
+ * Gets true, if the entry is a directory. This is solely
+ * determined by the name, a trailing slash '/' marks a directory.
+ */
+ public boolean isDirectory()
+ {
+ int nlen = name.length();
+ return nlen > 0 && name.charAt(nlen - 1) == '/';
+ }
+
+ /**
+ * Gets the string representation of this ZipEntry. This is just
+ * the name as returned by getName().
+ */
+ public String toString()
+ {
+ return name;
+ }
+
+ /**
+ * Gets the hashCode of this ZipEntry. This is just the hashCode
+ * of the name. Note that the equals method isn't changed, though.
+ */
+ public int hashCode()
+ {
+ return name.hashCode();
+ }
+}
diff --git a/libjava/classpath/java/util/zip/ZipException.java b/libjava/classpath/java/util/zip/ZipException.java
new file mode 100644
index 000000000..c5bfc1e7c
--- /dev/null
+++ b/libjava/classpath/java/util/zip/ZipException.java
@@ -0,0 +1,72 @@
+/* ZipException.java - exception representing a zip related error
+ Copyright (C) 1998, 1999, 2000, 2001, 2002 Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath 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 for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module. An independent module is a module which is not derived from
+or based on this library. If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so. If you do not wish to do so, delete this
+exception statement from your version. */
+
+package java.util.zip;
+
+import java.io.IOException;
+
+/**
+ * Thrown during the creation or input of a zip file.
+ *
+ * @author Jochen Hoenicke
+ * @author Per Bothner
+ * @status updated to 1.4
+ */
+public class ZipException extends IOException
+{
+ /**
+ * Compatible with JDK 1.0+.
+ */
+ private static final long serialVersionUID = 8000196834066748623L;
+
+ /**
+ * Create an exception without a message.
+ */
+ public ZipException()
+ {
+ }
+
+ /**
+ * Create an exception with a message.
+ *
+ * @param msg the message
+ */
+ public ZipException (String msg)
+ {
+ super(msg);
+ }
+}
diff --git a/libjava/classpath/java/util/zip/ZipFile.java b/libjava/classpath/java/util/zip/ZipFile.java
new file mode 100644
index 000000000..3963bcb1e
--- /dev/null
+++ b/libjava/classpath/java/util/zip/ZipFile.java
@@ -0,0 +1,782 @@
+/* ZipFile.java --
+ Copyright (C) 2001, 2002, 2003, 2004, 2005, 2006
+ Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath 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 for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module. An independent module is a module which is not derived from
+or based on this library. If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so. If you do not wish to do so, delete this
+exception statement from your version. */
+
+
+package java.util.zip;
+
+import gnu.java.util.EmptyEnumeration;
+
+import java.io.EOFException;
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.RandomAccessFile;
+import java.io.UnsupportedEncodingException;
+import java.nio.ByteBuffer;
+import java.nio.charset.Charset;
+import java.nio.charset.CharsetDecoder;
+import java.util.Enumeration;
+import java.util.Iterator;
+import java.util.LinkedHashMap;
+
+/**
+ * This class represents a Zip archive. You can ask for the contained
+ * entries, or get an input stream for a file entry. The entry is
+ * automatically decompressed.
+ *
+ * This class is thread safe: You can open input streams for arbitrary
+ * entries in different threads.
+ *
+ * @author Jochen Hoenicke
+ * @author Artur Biesiadowski
+ */
+public class ZipFile implements ZipConstants
+{
+
+ /**
+ * Mode flag to open a zip file for reading.
+ */
+ public static final int OPEN_READ = 0x1;
+
+ /**
+ * Mode flag to delete a zip file after reading.
+ */
+ public static final int OPEN_DELETE = 0x4;
+
+ /**
+ * This field isn't defined in the JDK's ZipConstants, but should be.
+ */
+ static final int ENDNRD = 4;
+
+ // Name of this zip file.
+ private final String name;
+
+ // File from which zip entries are read.
+ private final RandomAccessFile raf;
+
+ // The entries of this zip file when initialized and not yet closed.
+ private LinkedHashMap<String, ZipEntry> entries;
+
+ private boolean closed = false;
+
+
+ /**
+ * Helper function to open RandomAccessFile and throw the proper
+ * ZipException in case opening the file fails.
+ *
+ * @param name the file name, or null if file is provided
+ *
+ * @param file the file, or null if name is provided
+ *
+ * @return the newly open RandomAccessFile, never null
+ */
+ private RandomAccessFile openFile(String name,
+ File file)
+ throws ZipException, IOException
+ {
+ try
+ {
+ return
+ (name != null)
+ ? new RandomAccessFile(name, "r")
+ : new RandomAccessFile(file, "r");
+ }
+ catch (FileNotFoundException f)
+ {
+ ZipException ze = new ZipException(f.getMessage());
+ ze.initCause(f);
+ throw ze;
+ }
+ }
+
+
+ /**
+ * 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.
+ */
+ public ZipFile(String name) throws ZipException, IOException
+ {
+ this.raf = openFile(name,null);
+ this.name = name;
+ checkZipFile();
+ }
+
+ /**
+ * 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.
+ */
+ public ZipFile(File file) throws ZipException, IOException
+ {
+ this.raf = openFile(null,file);
+ this.name = file.getPath();
+ checkZipFile();
+ }
+
+ /**
+ * 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.
+ *
+ * @since JDK1.3
+ * @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.
+ */
+ public ZipFile(File file, 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 = openFile(null,file);
+ this.name = file.getPath();
+ checkZipFile();
+ }
+
+ private void checkZipFile() throws ZipException
+ {
+ boolean valid = false;
+
+ try
+ {
+ byte[] buf = new byte[4];
+ raf.readFully(buf);
+ int sig = buf[0] & 0xFF
+ | ((buf[1] & 0xFF) << 8)
+ | ((buf[2] & 0xFF) << 16)
+ | ((buf[3] & 0xFF) << 24);
+ valid = sig == LOCSIG;
+ }
+ catch (IOException _)
+ {
+ }
+
+ if (!valid)
+ {
+ try
+ {
+ raf.close();
+ }
+ catch (IOException _)
+ {
+ }
+ throw new ZipException("Not a valid zip file");
+ }
+ }
+
+ /**
+ * Checks if file is closed and throws an exception.
+ */
+ private void checkClosed()
+ {
+ if (closed)
+ throw new IllegalStateException("ZipFile has closed: " + name);
+ }
+
+ /**
+ * 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 <code>raf</code>.
+ *
+ * @exception IOException if a i/o error occured.
+ * @exception ZipException if the central directory is malformed
+ */
+ private void readEntries() throws ZipException, IOException
+ {
+ /* Search for the End Of Central Directory. When a zip comment is
+ * present the directory may start earlier.
+ * Note that a comment has a maximum length of 64K, so that is the
+ * maximum we search backwards.
+ */
+ PartialInputStream inp = new PartialInputStream(raf, 4096);
+ long pos = raf.length() - ENDHDR;
+ long top = Math.max(0, pos - 65536);
+ do
+ {
+ if (pos < top)
+ throw new ZipException
+ ("central directory not found, probably not a zip file: " + name);
+ inp.seek(pos--);
+ }
+ while (inp.readLeInt() != ENDSIG);
+
+ if (inp.skip(ENDTOT - ENDNRD) != ENDTOT - ENDNRD)
+ throw new EOFException(name);
+ int count = inp.readLeShort();
+ if (inp.skip(ENDOFF - ENDSIZ) != ENDOFF - ENDSIZ)
+ throw new EOFException(name);
+ int centralOffset = inp.readLeInt();
+
+ entries = new LinkedHashMap<String, ZipEntry> (count+count/2);
+ inp.seek(centralOffset);
+
+ for (int i = 0; i < count; i++)
+ {
+ if (inp.readLeInt() != CENSIG)
+ throw new ZipException("Wrong Central Directory signature: " + name);
+
+ inp.skip(6);
+ int method = inp.readLeShort();
+ int dostime = inp.readLeInt();
+ int crc = inp.readLeInt();
+ int csize = inp.readLeInt();
+ int size = inp.readLeInt();
+ int nameLen = inp.readLeShort();
+ int extraLen = inp.readLeShort();
+ int commentLen = inp.readLeShort();
+ inp.skip(8);
+ int offset = inp.readLeInt();
+ String name = inp.readString(nameLen);
+
+ ZipEntry entry = new ZipEntry(name);
+ entry.setMethod(method);
+ entry.setCrc(crc & 0xffffffffL);
+ entry.setSize(size & 0xffffffffL);
+ entry.setCompressedSize(csize & 0xffffffffL);
+ entry.setDOSTime(dostime);
+ if (extraLen > 0)
+ {
+ byte[] extra = new byte[extraLen];
+ inp.readFully(extra);
+ entry.setExtra(extra);
+ }
+ if (commentLen > 0)
+ {
+ entry.setComment(inp.readString(commentLen));
+ }
+ entry.offset = offset;
+ entries.put(name, entry);
+ }
+ }
+
+ /**
+ * 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.
+ */
+ public void close() throws IOException
+ {
+ RandomAccessFile raf = this.raf;
+ if (raf == null)
+ return;
+
+ synchronized (raf)
+ {
+ closed = true;
+ entries = null;
+ raf.close();
+ }
+ }
+
+ /**
+ * Calls the <code>close()</code> method when this ZipFile has not yet
+ * been explicitly closed.
+ */
+ protected void finalize() throws IOException
+ {
+ if (!closed && raf != null) close();
+ }
+
+ /**
+ * Returns an enumeration of all Zip entries in this Zip file.
+ *
+ * @exception IllegalStateException when the ZipFile has already been closed
+ */
+ public Enumeration<? extends ZipEntry> entries()
+ {
+ checkClosed();
+
+ try
+ {
+ return new ZipEntryEnumeration(getEntries().values().iterator());
+ }
+ catch (IOException ioe)
+ {
+ return new EmptyEnumeration<ZipEntry>();
+ }
+ }
+
+ /**
+ * Checks that the ZipFile is still open and reads entries when necessary.
+ *
+ * @exception IllegalStateException when the ZipFile has already been closed.
+ * @exception IOException when the entries could not be read.
+ */
+ private LinkedHashMap<String, ZipEntry> getEntries() throws IOException
+ {
+ synchronized(raf)
+ {
+ checkClosed();
+
+ if (entries == null)
+ readEntries();
+
+ return entries;
+ }
+ }
+
+ /**
+ * Searches for a zip entry in this archive with the given name.
+ *
+ * @param name the 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
+ */
+ public ZipEntry getEntry(String name)
+ {
+ checkClosed();
+
+ try
+ {
+ LinkedHashMap<String, ZipEntry> entries = getEntries();
+ ZipEntry entry = entries.get(name);
+ // If we didn't find it, maybe it's a directory.
+ if (entry == null && !name.endsWith("/"))
+ entry = entries.get(name + '/');
+ return entry != null ? new ZipEntry(entry, name) : null;
+ }
+ catch (IOException ioe)
+ {
+ return null;
+ }
+ }
+
+ /**
+ * Creates an input stream reading the given zip entry as
+ * uncompressed data. Normally zip entry should be an entry
+ * returned by getEntry() or entries().
+ *
+ * This implementation returns null if the requested entry does not
+ * exist. This decision is not obviously correct, however, it does
+ * appear to mirror Sun's implementation, and it is consistant with
+ * their javadoc. On the other hand, the old JCL book, 2nd Edition,
+ * claims that this should return a "non-null ZIP entry". We have
+ * chosen for now ignore the old book, as modern versions of Ant (an
+ * important application) depend on this behaviour. See discussion
+ * in this thread:
+ * http://gcc.gnu.org/ml/java-patches/2004-q2/msg00602.html
+ *
+ * @param entry the entry to create an InputStream for.
+ * @return the input stream, or null if the requested entry does not exist.
+ *
+ * @exception IllegalStateException when the ZipFile has already been closed
+ * @exception IOException if a i/o error occured.
+ * @exception ZipException if the Zip archive is malformed.
+ */
+ public InputStream getInputStream(ZipEntry entry) throws IOException
+ {
+ checkClosed();
+
+ LinkedHashMap<String, ZipEntry> entries = getEntries();
+ String name = entry.getName();
+ ZipEntry zipEntry = entries.get(name);
+ if (zipEntry == null)
+ return null;
+
+ PartialInputStream inp = new PartialInputStream(raf, 1024);
+ inp.seek(zipEntry.offset);
+
+ if (inp.readLeInt() != LOCSIG)
+ throw new ZipException("Wrong Local header signature: " + name);
+
+ inp.skip(4);
+
+ if (zipEntry.getMethod() != inp.readLeShort())
+ throw new ZipException("Compression method mismatch: " + name);
+
+ inp.skip(16);
+
+ int nameLen = inp.readLeShort();
+ int extraLen = inp.readLeShort();
+ inp.skip(nameLen + extraLen);
+
+ inp.setLength(zipEntry.getCompressedSize());
+
+ int method = zipEntry.getMethod();
+ switch (method)
+ {
+ case ZipOutputStream.STORED:
+ return inp;
+ case ZipOutputStream.DEFLATED:
+ inp.addDummyByte();
+ final Inflater inf = new Inflater(true);
+ final int sz = (int) entry.getSize();
+ return new InflaterInputStream(inp, inf)
+ {
+ public int available() throws IOException
+ {
+ if (sz == -1)
+ return super.available();
+ if (super.available() != 0)
+ return sz - inf.getTotalOut();
+ return 0;
+ }
+ };
+ default:
+ throw new ZipException("Unknown compression method " + method);
+ }
+ }
+
+ /**
+ * Returns the (path) name of this zip file.
+ */
+ public String getName()
+ {
+ return name;
+ }
+
+ /**
+ * Returns the number of entries in this zip file.
+ *
+ * @exception IllegalStateException when the ZipFile has already been closed
+ */
+ public int size()
+ {
+ checkClosed();
+
+ try
+ {
+ return getEntries().size();
+ }
+ catch (IOException ioe)
+ {
+ return 0;
+ }
+ }
+
+ private static class ZipEntryEnumeration implements Enumeration<ZipEntry>
+ {
+ private final Iterator<ZipEntry> elements;
+
+ public ZipEntryEnumeration(Iterator<ZipEntry> elements)
+ {
+ this.elements = elements;
+ }
+
+ public boolean hasMoreElements()
+ {
+ return elements.hasNext();
+ }
+
+ public ZipEntry nextElement()
+ {
+ /* We return a clone, just to be safe that the user doesn't
+ * change the entry.
+ */
+ return (ZipEntry) (elements.next().clone());
+ }
+ }
+
+ private static final class PartialInputStream extends InputStream
+ {
+ /**
+ * The UTF-8 charset use for decoding the filenames.
+ */
+ private static final Charset UTF8CHARSET = Charset.forName("UTF-8");
+
+ /**
+ * The actual UTF-8 decoder. Created on demand.
+ */
+ private CharsetDecoder utf8Decoder;
+
+ private final RandomAccessFile raf;
+ private final byte[] buffer;
+ private long bufferOffset;
+ private int pos;
+ private long end;
+ // We may need to supply an extra dummy byte to our reader.
+ // See Inflater. We use a count here to simplify the logic
+ // elsewhere in this class. Note that we ignore the dummy
+ // byte in methods where we know it is not needed.
+ private int dummyByteCount;
+
+ public PartialInputStream(RandomAccessFile raf, int bufferSize)
+ throws IOException
+ {
+ this.raf = raf;
+ buffer = new byte[bufferSize];
+ bufferOffset = -buffer.length;
+ pos = buffer.length;
+ end = raf.length();
+ }
+
+ void setLength(long length)
+ {
+ end = bufferOffset + pos + length;
+ }
+
+ private void fillBuffer() throws IOException
+ {
+ synchronized (raf)
+ {
+ long len = end - bufferOffset;
+ if (len == 0 && dummyByteCount > 0)
+ {
+ buffer[0] = 0;
+ dummyByteCount = 0;
+ }
+ else
+ {
+ raf.seek(bufferOffset);
+ raf.readFully(buffer, 0, (int) Math.min(buffer.length, len));
+ }
+ }
+ }
+
+ public int available()
+ {
+ long amount = end - (bufferOffset + pos);
+ if (amount > Integer.MAX_VALUE)
+ return Integer.MAX_VALUE;
+ return (int) amount;
+ }
+
+ public int read() throws IOException
+ {
+ if (bufferOffset + pos >= end + dummyByteCount)
+ return -1;
+ if (pos == buffer.length)
+ {
+ bufferOffset += buffer.length;
+ pos = 0;
+ fillBuffer();
+ }
+
+ return buffer[pos++] & 0xFF;
+ }
+
+ public int read(byte[] b, int off, int len) throws IOException
+ {
+ if (len > end + dummyByteCount - (bufferOffset + pos))
+ {
+ len = (int) (end + dummyByteCount - (bufferOffset + pos));
+ if (len == 0)
+ return -1;
+ }
+
+ int totalBytesRead = Math.min(buffer.length - pos, len);
+ System.arraycopy(buffer, pos, b, off, totalBytesRead);
+ pos += totalBytesRead;
+ off += totalBytesRead;
+ len -= totalBytesRead;
+
+ while (len > 0)
+ {
+ bufferOffset += buffer.length;
+ pos = 0;
+ fillBuffer();
+ int remain = Math.min(buffer.length, len);
+ System.arraycopy(buffer, pos, b, off, remain);
+ pos += remain;
+ off += remain;
+ len -= remain;
+ totalBytesRead += remain;
+ }
+
+ return totalBytesRead;
+ }
+
+ public long skip(long amount) throws IOException
+ {
+ if (amount < 0)
+ return 0;
+ if (amount > end - (bufferOffset + pos))
+ amount = end - (bufferOffset + pos);
+ seek(bufferOffset + pos + amount);
+ return amount;
+ }
+
+ void seek(long newpos) throws IOException
+ {
+ long offset = newpos - bufferOffset;
+ if (offset >= 0 && offset <= buffer.length)
+ {
+ pos = (int) offset;
+ }
+ else
+ {
+ bufferOffset = newpos;
+ pos = 0;
+ fillBuffer();
+ }
+ }
+
+ void readFully(byte[] buf) throws IOException
+ {
+ if (read(buf, 0, buf.length) != buf.length)
+ throw new EOFException();
+ }
+
+ void readFully(byte[] buf, int off, int len) throws IOException
+ {
+ if (read(buf, off, len) != len)
+ throw new EOFException();
+ }
+
+ int readLeShort() throws IOException
+ {
+ int result;
+ if(pos + 1 < buffer.length)
+ {
+ result = ((buffer[pos + 0] & 0xff) | (buffer[pos + 1] & 0xff) << 8);
+ pos += 2;
+ }
+ else
+ {
+ int b0 = read();
+ int b1 = read();
+ if (b1 == -1)
+ throw new EOFException();
+ result = (b0 & 0xff) | (b1 & 0xff) << 8;
+ }
+ return result;
+ }
+
+ int readLeInt() throws IOException
+ {
+ int result;
+ if(pos + 3 < buffer.length)
+ {
+ result = (((buffer[pos + 0] & 0xff) | (buffer[pos + 1] & 0xff) << 8)
+ | ((buffer[pos + 2] & 0xff)
+ | (buffer[pos + 3] & 0xff) << 8) << 16);
+ pos += 4;
+ }
+ else
+ {
+ int b0 = read();
+ int b1 = read();
+ int b2 = read();
+ int b3 = read();
+ if (b3 == -1)
+ throw new EOFException();
+ result = (((b0 & 0xff) | (b1 & 0xff) << 8) | ((b2 & 0xff)
+ | (b3 & 0xff) << 8) << 16);
+ }
+ return result;
+ }
+
+ /**
+ * Decode chars from byte buffer using UTF8 encoding. This
+ * operation is performance-critical since a jar file contains a
+ * large number of strings for the name of each file in the
+ * archive. This routine therefore avoids using the expensive
+ * utf8Decoder when decoding is straightforward.
+ *
+ * @param buffer the buffer that contains the encoded character
+ * data
+ * @param pos the index in buffer of the first byte of the encoded
+ * data
+ * @param length the length of the encoded data in number of
+ * bytes.
+ *
+ * @return a String that contains the decoded characters.
+ */
+ private String decodeChars(byte[] buffer, int pos, int length)
+ throws IOException
+ {
+ String result;
+ int i=length - 1;
+ while ((i >= 0) && (buffer[i] <= 0x7f))
+ {
+ i--;
+ }
+ if (i < 0)
+ {
+ result = new String(buffer, 0, pos, length);
+ }
+ else
+ {
+ ByteBuffer bufferBuffer = ByteBuffer.wrap(buffer, pos, length);
+ if (utf8Decoder == null)
+ utf8Decoder = UTF8CHARSET.newDecoder();
+ utf8Decoder.reset();
+ char [] characters = utf8Decoder.decode(bufferBuffer).array();
+ result = String.valueOf(characters);
+ }
+ return result;
+ }
+
+ String readString(int length) throws IOException
+ {
+ if (length > end - (bufferOffset + pos))
+ throw new EOFException();
+
+ String result = null;
+ try
+ {
+ if (buffer.length - pos >= length)
+ {
+ result = decodeChars(buffer, pos, length);
+ pos += length;
+ }
+ else
+ {
+ byte[] b = new byte[length];
+ readFully(b);
+ result = decodeChars(b, 0, length);
+ }
+ }
+ catch (UnsupportedEncodingException uee)
+ {
+ throw new AssertionError(uee);
+ }
+ return result;
+ }
+
+ public void addDummyByte()
+ {
+ dummyByteCount = 1;
+ }
+ }
+}
diff --git a/libjava/classpath/java/util/zip/ZipInputStream.java b/libjava/classpath/java/util/zip/ZipInputStream.java
new file mode 100644
index 000000000..3eae026da
--- /dev/null
+++ b/libjava/classpath/java/util/zip/ZipInputStream.java
@@ -0,0 +1,381 @@
+/* ZipInputStream.java --
+ Copyright (C) 2001, 2002, 2003, 2004, 2005 Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath 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 for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module. An independent module is a module which is not derived from
+or based on this library. If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so. If you do not wish to do so, delete this
+exception statement from your version. */
+
+
+package java.util.zip;
+
+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
+ * one after another. It has a special method to get the zip entry of
+ * the next file. The zip entry contains information about the file name
+ * size, compressed size, CRC, etc.
+ *
+ * It includes support for STORED and DEFLATED entries.
+ *
+ * @author Jochen Hoenicke
+ */
+public class ZipInputStream extends InflaterInputStream implements ZipConstants
+{
+ private CRC32 crc = new CRC32();
+ private ZipEntry entry = null;
+
+ private int csize;
+ private int size;
+ private int method;
+ private int flags;
+ private int avail;
+ private boolean entryAtEOF;
+
+ /**
+ * Creates a new Zip input stream, reading a zip archive.
+ */
+ public ZipInputStream(InputStream in)
+ {
+ super(in, new Inflater(true));
+ }
+
+ private void fillBuf() throws IOException
+ {
+ avail = len = in.read(buf, 0, buf.length);
+ }
+
+ private int readBuf(byte[] out, int offset, int length) throws IOException
+ {
+ if (avail <= 0)
+ {
+ fillBuf();
+ if (avail <= 0)
+ return -1;
+ }
+ if (length > avail)
+ length = avail;
+ System.arraycopy(buf, len - avail, out, offset, length);
+ avail -= length;
+ return length;
+ }
+
+ private void readFully(byte[] out) throws IOException
+ {
+ int off = 0;
+ int len = out.length;
+ while (len > 0)
+ {
+ int count = readBuf(out, off, len);
+ if (count == -1)
+ throw new EOFException();
+ off += count;
+ len -= count;
+ }
+ }
+
+ private int readLeByte() throws IOException
+ {
+ if (avail <= 0)
+ {
+ fillBuf();
+ if (avail <= 0)
+ throw new ZipException("EOF in header");
+ }
+ return buf[len - avail--] & 0xff;
+ }
+
+ /**
+ * Read an unsigned short in little endian byte order.
+ */
+ private int readLeShort() throws IOException
+ {
+ return readLeByte() | (readLeByte() << 8);
+ }
+
+ /**
+ * Read an int in little endian byte order.
+ */
+ private int readLeInt() throws IOException
+ {
+ return readLeShort() | (readLeShort() << 16);
+ }
+
+ /**
+ * Open the next entry from the zip archive, and return its description.
+ * If the previous entry wasn't closed, this method will close it.
+ */
+ public ZipEntry getNextEntry() throws IOException
+ {
+ if (crc == null)
+ throw new IOException("Stream closed.");
+ if (entry != null)
+ closeEntry();
+
+ int header = readLeInt();
+ if (header == CENSIG)
+ {
+ /* Central Header reached. */
+ close();
+ return null;
+ }
+ if (header != LOCSIG)
+ throw new ZipException("Wrong Local header signature: "
+ + Integer.toHexString(header));
+ /* skip version */
+ readLeShort();
+ flags = readLeShort();
+ method = readLeShort();
+ int dostime = readLeInt();
+ int crc = readLeInt();
+ csize = readLeInt();
+ size = readLeInt();
+ int nameLen = readLeShort();
+ int extraLen = readLeShort();
+
+ if (method == ZipOutputStream.STORED && csize != size)
+ throw new ZipException("Stored, but compressed != uncompressed");
+
+
+ byte[] buffer = new byte[nameLen];
+ readFully(buffer);
+ String name;
+ try
+ {
+ name = new String(buffer, "UTF-8");
+ }
+ catch (UnsupportedEncodingException uee)
+ {
+ throw new AssertionError(uee);
+ }
+
+ 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.setDOSTime(dostime);
+ if (extraLen > 0)
+ {
+ byte[] extra = new byte[extraLen];
+ readFully(extra);
+ entry.setExtra(extra);
+ }
+
+ if (method == ZipOutputStream.DEFLATED && avail > 0)
+ {
+ System.arraycopy(buf, len - avail, buf, 0, avail);
+ len = avail;
+ avail = 0;
+ inf.setInput(buf, 0, len);
+ }
+ return entry;
+ }
+
+ private void readDataDescr() throws IOException
+ {
+ if (readLeInt() != EXTSIG)
+ throw new ZipException("Data descriptor signature not found");
+ entry.setCrc(readLeInt() & 0xffffffffL);
+ csize = readLeInt();
+ size = readLeInt();
+ entry.setSize(size & 0xffffffffL);
+ entry.setCompressedSize(csize & 0xffffffffL);
+ }
+
+ /**
+ * Closes the current zip entry and moves to the next one.
+ */
+ public void closeEntry() throws IOException
+ {
+ if (crc == null)
+ throw new IOException("Stream closed.");
+ if (entry == null)
+ return;
+
+ if (method == ZipOutputStream.DEFLATED)
+ {
+ if ((flags & 8) != 0)
+ {
+ /* We don't know how much we must skip, read until end. */
+ byte[] tmp = new byte[2048];
+ while (read(tmp) > 0)
+ ;
+
+ /* read will close this entry */
+ return;
+ }
+ csize -= inf.getTotalIn();
+ avail = inf.getRemaining();
+ }
+
+ if (avail > csize && csize >= 0)
+ avail -= csize;
+ else
+ {
+ csize -= avail;
+ avail = 0;
+ while (csize != 0)
+ {
+ long skipped = in.skip(csize & 0xffffffffL);
+ if (skipped <= 0)
+ throw new ZipException("zip archive ends early.");
+ csize -= skipped;
+ }
+ }
+
+ size = 0;
+ crc.reset();
+ if (method == ZipOutputStream.DEFLATED)
+ inf.reset();
+ entry = null;
+ entryAtEOF = true;
+ }
+
+ public int available() throws IOException
+ {
+ return entryAtEOF ? 0 : 1;
+ }
+
+ /**
+ * Reads a byte from the current zip entry.
+ * @return the byte or -1 on EOF.
+ * @exception IOException if a i/o error occured.
+ * @exception ZipException if the deflated stream is corrupted.
+ */
+ public int read() throws IOException
+ {
+ byte[] b = new byte[1];
+ if (read(b, 0, 1) <= 0)
+ return -1;
+ return b[0] & 0xff;
+ }
+
+ /**
+ * Reads a block of bytes from the current zip entry.
+ * @return the number of bytes read (may be smaller, even before
+ * EOF), or -1 on EOF.
+ * @exception IOException if a i/o error occured.
+ * @exception ZipException if the deflated stream is corrupted.
+ */
+ public int read(byte[] b, int off, int len) throws IOException
+ {
+ if (len == 0)
+ return 0;
+ if (crc == null)
+ throw new IOException("Stream closed.");
+ if (entry == null)
+ return -1;
+ boolean finished = false;
+ switch (method)
+ {
+ case ZipOutputStream.DEFLATED:
+ len = super.read(b, off, len);
+ if (len < 0)
+ {
+ if (!inf.finished())
+ throw new ZipException("Inflater not finished!?");
+ avail = inf.getRemaining();
+ if ((flags & 8) != 0)
+ readDataDescr();
+
+ if (inf.getTotalIn() != csize
+ || inf.getTotalOut() != size)
+ throw new ZipException("size mismatch: "+csize+";"+size+" <-> "+inf.getTotalIn()+";"+inf.getTotalOut());
+ inf.reset();
+ finished = true;
+ }
+ break;
+
+ case ZipOutputStream.STORED:
+
+ if (len > csize && csize >= 0)
+ len = csize;
+
+ len = readBuf(b, off, len);
+ if (len > 0)
+ {
+ csize -= len;
+ size -= len;
+ }
+
+ if (csize == 0)
+ finished = true;
+ else if (len < 0)
+ throw new ZipException("EOF in stored block");
+ break;
+ }
+
+ if (len > 0)
+ crc.update(b, off, len);
+
+ if (finished)
+ {
+ if ((crc.getValue() & 0xffffffffL) != entry.getCrc())
+ throw new ZipException("CRC mismatch");
+ crc.reset();
+ entry = null;
+ entryAtEOF = true;
+ }
+ return len;
+ }
+
+ /**
+ * Closes the zip file.
+ * @exception IOException if a i/o error occured.
+ */
+ public void close() throws IOException
+ {
+ super.close();
+ crc = null;
+ entry = null;
+ entryAtEOF = true;
+ }
+
+ /**
+ * Creates a new zip entry for the given name. This is equivalent
+ * to new ZipEntry(name).
+ * @param name the name of the zip entry.
+ */
+ protected ZipEntry createZipEntry(String name)
+ {
+ return new ZipEntry(name);
+ }
+}
diff --git a/libjava/classpath/java/util/zip/ZipOutputStream.java b/libjava/classpath/java/util/zip/ZipOutputStream.java
new file mode 100644
index 000000000..bc1c3e99c
--- /dev/null
+++ b/libjava/classpath/java/util/zip/ZipOutputStream.java
@@ -0,0 +1,440 @@
+/* ZipOutputStream.java --
+ Copyright (C) 2001, 2004, 2005, 2006 Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath 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 for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module. An independent module is a module which is not derived from
+or based on this library. If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so. If you do not wish to do so, delete this
+exception statement from your version. */
+
+
+package java.util.zip;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.UnsupportedEncodingException;
+import java.util.Enumeration;
+import java.util.Vector;
+
+/**
+ * This is a FilterOutputStream that writes the files into a zip
+ * archive one after another. It has a special method to start a new
+ * zip entry. The zip entries contains information about the file name
+ * size, compressed size, CRC, etc.
+ *
+ * It includes support for STORED and DEFLATED entries.
+ *
+ * This class is not thread safe.
+ *
+ * @author Jochen Hoenicke
+ */
+public class ZipOutputStream extends DeflaterOutputStream implements ZipConstants
+{
+ private Vector entries = new Vector();
+ private CRC32 crc = new CRC32();
+ private ZipEntry curEntry = null;
+
+ private int curMethod;
+ private int size;
+ private int offset = 0;
+
+ private byte[] zipComment = new byte[0];
+ private int defaultMethod = DEFLATED;
+
+ /**
+ * Our Zip version is hard coded to 1.0 resp. 2.0
+ */
+ private static final int ZIP_STORED_VERSION = 10;
+ private static final int ZIP_DEFLATED_VERSION = 20;
+
+ /**
+ * Compression method. This method doesn't compress at all.
+ */
+ public static final int STORED = 0;
+
+ /**
+ * Compression method. This method uses the Deflater.
+ */
+ public static final int DEFLATED = 8;
+
+ /**
+ * Creates a new Zip output stream, writing a zip archive.
+ * @param out the output stream to which the zip archive is written.
+ */
+ public ZipOutputStream(OutputStream out)
+ {
+ super(out, new Deflater(Deflater.DEFAULT_COMPRESSION, true));
+ }
+
+ /**
+ * Set the zip file comment.
+ * @param comment the comment.
+ * @exception IllegalArgumentException if encoding of comment is
+ * longer than 0xffff bytes.
+ */
+ public void setComment(String comment)
+ {
+ byte[] commentBytes;
+ try
+ {
+ commentBytes = comment.getBytes("UTF-8");
+ }
+ catch (UnsupportedEncodingException uee)
+ {
+ throw new AssertionError(uee);
+ }
+ if (commentBytes.length > 0xffff)
+ throw new IllegalArgumentException("Comment too long.");
+ zipComment = commentBytes;
+ }
+
+ /**
+ * Sets default compression method. If the Zip entry specifies
+ * another method its method takes precedence.
+ * @param method the method.
+ * @exception IllegalArgumentException if method is not supported.
+ * @see #STORED
+ * @see #DEFLATED
+ */
+ public void setMethod(int method)
+ {
+ if (method != STORED && method != DEFLATED)
+ throw new IllegalArgumentException("Method not supported.");
+ defaultMethod = method;
+ }
+
+ /**
+ * Sets default compression level. The new level will be activated
+ * immediately.
+ * @exception IllegalArgumentException if level is not supported.
+ * @see Deflater
+ */
+ public void setLevel(int level)
+ {
+ def.setLevel(level);
+ }
+
+ /**
+ * Write an unsigned short in little endian byte order.
+ */
+ private void writeLeShort(int value) throws IOException
+ {
+ out.write(value & 0xff);
+ out.write((value >> 8) & 0xff);
+ }
+
+ /**
+ * Write an int in little endian byte order.
+ */
+ private void writeLeInt(int value) throws IOException
+ {
+ writeLeShort(value);
+ writeLeShort(value >> 16);
+ }
+
+ /**
+ * Write a long value as an int. Some of the zip constants
+ * are declared as longs even though they fit perfectly well
+ * into integers.
+ */
+ private void writeLeInt(long value) throws IOException
+ {
+ writeLeInt((int) value);
+ }
+
+ /**
+ * Starts a new Zip entry. It automatically closes the previous
+ * entry if present. If the compression method is stored, the entry
+ * must have a valid size and crc, otherwise all elements (except
+ * name) are optional, but must be correct if present. If the time
+ * is not set in the entry, the current time is used.
+ * @param entry the entry.
+ * @exception IOException if an I/O error occured.
+ * @exception ZipException if stream was finished.
+ */
+ public void putNextEntry(ZipEntry entry) throws IOException
+ {
+ if (entries == null)
+ throw new ZipException("ZipOutputStream was finished");
+
+ int method = entry.getMethod();
+ int flags = 0;
+ if (method == -1)
+ method = defaultMethod;
+
+ if (method == STORED)
+ {
+ if (entry.getCompressedSize() >= 0)
+ {
+ if (entry.getSize() < 0)
+ entry.setSize(entry.getCompressedSize());
+ else if (entry.getSize() != entry.getCompressedSize())
+ throw new ZipException
+ ("Method STORED, but compressed size != size");
+ }
+ else
+ entry.setCompressedSize(entry.getSize());
+
+ if (entry.getSize() < 0)
+ throw new ZipException("Method STORED, but size not set");
+ if (entry.getCrc() < 0)
+ throw new ZipException("Method STORED, but crc not set");
+ }
+ else if (method == DEFLATED)
+ {
+ if (entry.getCompressedSize() < 0
+ || entry.getSize() < 0 || entry.getCrc() < 0)
+ flags |= 8;
+ }
+
+ if (curEntry != null)
+ closeEntry();
+
+ if (entry.getTime() < 0)
+ entry.setTime(System.currentTimeMillis());
+
+ entry.flags = flags;
+ entry.offset = offset;
+ entry.setMethod(method);
+ curMethod = method;
+ /* Write the local file header */
+ writeLeInt(LOCSIG);
+ writeLeShort(method == STORED
+ ? ZIP_STORED_VERSION : ZIP_DEFLATED_VERSION);
+ writeLeShort(flags);
+ writeLeShort(method);
+ writeLeInt(entry.getDOSTime());
+ if ((flags & 8) == 0)
+ {
+ writeLeInt((int)entry.getCrc());
+ writeLeInt((int)entry.getCompressedSize());
+ writeLeInt((int)entry.getSize());
+ }
+ else
+ {
+ writeLeInt(0);
+ writeLeInt(0);
+ writeLeInt(0);
+ }
+ byte[] name;
+ try
+ {
+ name = entry.getName().getBytes("UTF-8");
+ }
+ catch (UnsupportedEncodingException uee)
+ {
+ throw new AssertionError(uee);
+ }
+ if (name.length > 0xffff)
+ throw new ZipException("Name too long.");
+ byte[] extra = entry.getExtra();
+ if (extra == null)
+ extra = new byte[0];
+ writeLeShort(name.length);
+ writeLeShort(extra.length);
+ out.write(name);
+ out.write(extra);
+
+ offset += LOCHDR + name.length + extra.length;
+
+ /* Activate the entry. */
+
+ curEntry = entry;
+ crc.reset();
+ if (method == DEFLATED)
+ def.reset();
+ size = 0;
+ }
+
+ /**
+ * Closes the current entry.
+ * @exception IOException if an I/O error occured.
+ * @exception ZipException if no entry is active.
+ */
+ public void closeEntry() throws IOException
+ {
+ if (curEntry == null)
+ throw new ZipException("No open entry");
+
+ /* First finish the deflater, if appropriate */
+ if (curMethod == DEFLATED)
+ super.finish();
+
+ int csize = curMethod == DEFLATED ? def.getTotalOut() : size;
+
+ if (curEntry.getSize() < 0)
+ curEntry.setSize(size);
+ else if (curEntry.getSize() != size)
+ throw new ZipException("size was "+size
+ +", but I expected "+curEntry.getSize());
+
+ if (curEntry.getCompressedSize() < 0)
+ curEntry.setCompressedSize(csize);
+ else if (curEntry.getCompressedSize() != csize)
+ throw new ZipException("compressed size was "+csize
+ +", but I expected "+curEntry.getSize());
+
+ if (curEntry.getCrc() < 0)
+ curEntry.setCrc(crc.getValue());
+ else if (curEntry.getCrc() != crc.getValue())
+ throw new ZipException("crc was " + Long.toHexString(crc.getValue())
+ + ", but I expected "
+ + Long.toHexString(curEntry.getCrc()));
+
+ offset += csize;
+
+ /* Now write the data descriptor entry if needed. */
+ if (curMethod == DEFLATED && (curEntry.flags & 8) != 0)
+ {
+ writeLeInt(EXTSIG);
+ writeLeInt((int)curEntry.getCrc());
+ writeLeInt((int)curEntry.getCompressedSize());
+ writeLeInt((int)curEntry.getSize());
+ offset += EXTHDR;
+ }
+
+ entries.addElement(curEntry);
+ curEntry = null;
+ }
+
+ /**
+ * Writes the given buffer to the current entry.
+ * @exception IOException if an I/O error occured.
+ * @exception ZipException if no entry is active.
+ */
+ public void write(byte[] b, int off, int len) throws IOException
+ {
+ if (curEntry == null)
+ throw new ZipException("No open entry.");
+
+ switch (curMethod)
+ {
+ case DEFLATED:
+ super.write(b, off, len);
+ break;
+
+ case STORED:
+ out.write(b, off, len);
+ break;
+ }
+
+ crc.update(b, off, len);
+ size += len;
+ }
+
+ /**
+ * Finishes the stream. This will write the central directory at the
+ * end of the zip file and flush the stream.
+ * @exception IOException if an I/O error occured.
+ */
+ public void finish() throws IOException
+ {
+ if (entries == null)
+ return;
+ if (curEntry != null)
+ closeEntry();
+
+ int numEntries = 0;
+ int sizeEntries = 0;
+
+ Enumeration e = entries.elements();
+ while (e.hasMoreElements())
+ {
+ ZipEntry entry = (ZipEntry) e.nextElement();
+
+ int method = entry.getMethod();
+ writeLeInt(CENSIG);
+ writeLeShort(method == STORED
+ ? ZIP_STORED_VERSION : ZIP_DEFLATED_VERSION);
+ writeLeShort(method == STORED
+ ? ZIP_STORED_VERSION : ZIP_DEFLATED_VERSION);
+ writeLeShort(entry.flags);
+ writeLeShort(method);
+ writeLeInt(entry.getDOSTime());
+ writeLeInt((int)entry.getCrc());
+ writeLeInt((int)entry.getCompressedSize());
+ writeLeInt((int)entry.getSize());
+
+ byte[] name;
+ try
+ {
+ name = entry.getName().getBytes("UTF-8");
+ }
+ catch (UnsupportedEncodingException uee)
+ {
+ throw new AssertionError(uee);
+ }
+ if (name.length > 0xffff)
+ throw new ZipException("Name too long.");
+ byte[] extra = entry.getExtra();
+ if (extra == null)
+ extra = new byte[0];
+ String str = entry.getComment();
+ byte[] comment;
+ try
+ {
+ comment = str != null ? str.getBytes("UTF-8") : new byte[0];
+ }
+ catch (UnsupportedEncodingException uee)
+ {
+ throw new AssertionError(uee);
+ }
+ if (comment.length > 0xffff)
+ throw new ZipException("Comment too long.");
+
+ writeLeShort(name.length);
+ writeLeShort(extra.length);
+ writeLeShort(comment.length);
+ writeLeShort(0); /* disk number */
+ writeLeShort(0); /* internal file attr */
+ writeLeInt(0); /* external file attr */
+ writeLeInt(entry.offset);
+
+ out.write(name);
+ out.write(extra);
+ out.write(comment);
+ numEntries++;
+ sizeEntries += CENHDR + name.length + extra.length + comment.length;
+ }
+
+ writeLeInt(ENDSIG);
+ writeLeShort(0); /* disk number */
+ writeLeShort(0); /* disk with start of central dir */
+ writeLeShort(numEntries);
+ writeLeShort(numEntries);
+ writeLeInt(sizeEntries);
+ writeLeInt(offset);
+ writeLeShort(zipComment.length);
+ out.write(zipComment);
+ out.flush();
+ entries = null;
+ }
+}
diff --git a/libjava/classpath/java/util/zip/package.html b/libjava/classpath/java/util/zip/package.html
new file mode 100644
index 000000000..5f3ac4917
--- /dev/null
+++ b/libjava/classpath/java/util/zip/package.html
@@ -0,0 +1,47 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
+<!-- package.html - describes classes in java.util.zip package.
+ Copyright (C) 2002 Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath 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 for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module. An independent module is a module which is not derived from
+or based on this library. If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so. If you do not wish to do so, delete this
+exception statement from your version. -->
+
+<html>
+<head><title>GNU Classpath - java.util.zip</title></head>
+
+<body>
+<p>Utility classes to manipulate zip and gzip archives as files or streams,
+includes checksum and compression support.</p>
+
+</body>
+</html>