summaryrefslogtreecommitdiff
path: root/libjava/classpath/java/util/zip/ZipInputStream.java
diff options
context:
space:
mode:
Diffstat (limited to 'libjava/classpath/java/util/zip/ZipInputStream.java')
-rw-r--r--libjava/classpath/java/util/zip/ZipInputStream.java371
1 files changed, 371 insertions, 0 deletions
diff --git a/libjava/classpath/java/util/zip/ZipInputStream.java b/libjava/classpath/java/util/zip/ZipInputStream.java
new file mode 100644
index 00000000000..5732523238e
--- /dev/null
+++ b/libjava/classpath/java/util/zip/ZipInputStream.java
@@ -0,0 +1,371 @@
+/* ZipInputStream.java --
+ Copyright (C) 2001, 2002, 2003, 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 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 = new String(buffer);
+
+ 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);
+ }
+}