diff options
Diffstat (limited to 'libjava/java/util/zip/ZipInputStream.java')
-rw-r--r-- | libjava/java/util/zip/ZipInputStream.java | 462 |
1 files changed, 265 insertions, 197 deletions
diff --git a/libjava/java/util/zip/ZipInputStream.java b/libjava/java/util/zip/ZipInputStream.java index 8fb63ef2cac..63153b649c9 100644 --- a/libjava/java/util/zip/ZipInputStream.java +++ b/libjava/java/util/zip/ZipInputStream.java @@ -1,5 +1,5 @@ -/* ZipInputStream.java - Input filter for reading zip file - Copyright (C) 1999, 2000 Free Software Foundation, Inc. +/* java.util.zip.ZipInputStream + Copyright (C) 2001, 2002 Free Software Foundation, Inc. This file is part of GNU Classpath. @@ -7,7 +7,7 @@ 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 @@ -36,259 +36,327 @@ 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.*; +import java.io.EOFException; +import java.io.InputStream; +import java.io.IOException; +import java.util.Enumeration; /** - * @author Per Bothner - * @date May 1999. + * 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; -/* - * Written using on-line Java Platform 1.2 API Specification, as well - * as "The Java Class Libraries", 2nd edition (Addison-Wesley, 1998). - * Status: Quite incomplete, but can read uncompressed .zip archives. - */ + private int csize; + private int size; + private int method; + private int flags; + private int avail; -// We do not calculate the CRC and compare it with the specified value; -// we probably should. FIXME. - + /** + * Creates a new Zip input stream, reading a zip archive. + */ + public ZipInputStream(InputStream in) + { + super(in, new Inflater(true)); + } -public class ZipInputStream extends InflaterInputStream implements ZipConstants -{ - public ZipInputStream (InputStream in) + private void fillBuf() throws IOException { - super (in, new Inflater (true)); + avail = len = in.read(buf, 0, buf.length); } - public ZipEntry getNextEntry () throws IOException + private int readBuf(byte[] out, int offset, int length) throws IOException { - if (closed) - throw new IOException ("stream closed"); - if (current != null) - closeEntry(); - if (in.read() != 'P' - || in.read() != 'K') - return null; - int code = in.read(); - while (code == '\001') + if (avail <= 0) { - code = in.read(); - if (code != '\002') - return null; - in.skip(16); - int size = read4(); - in.skip(4); - int fname_length = readu2(); - int extra_length = readu2(); - int fcomment_length = readu2(); - // `12' is the number of bytes between the comment length - // field and the end of the fixed part of the header: - // 2 bytes for `disk number start' - // 2 bytes for `internal file attributes' - // 4 bytes for `external file attributes' - // 4 bytes for `relative offset of local header' - in.skip(12 + fname_length + extra_length + fcomment_length); - if (in.read() != 'P' || in.read() != 'K') - return null; - code = in.read(); + fillBuf(); + if (avail <= 0) + return -1; } - if (code == '\005') + 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) { - if (in.read() != '\006') - return null; - in.skip(16); - int comment_size = readu2(); - in.skip(comment_size); - if (in.read() != 'P' || in.read() != 'K') - return null; - code = in.read(); + int count = readBuf(out, off, len); + if (count == -1) + throw new EOFException(); + off += count; + len -= count; } - if (code != '\003' - || in.read() != '\004') - return null; - int ex_version = readu2(); - current_flags = readu2(); - int method = readu2(); - int modtime = readu2(); - int moddate = readu2(); - int crc = read4(); - int compressedSize = read4(); - int uncompressedSize = read4(); - int filenameLength = readu2(); - int extraLength = readu2(); - byte[] bname = new byte[filenameLength]; - readFully(bname); - ZipEntry entry = createZipEntry(new String(bname, "8859_1")); - if (extraLength > 0) + } + + private final int readLeByte() throws IOException + { + if (avail <= 0) { - byte[] bextra = new byte[extraLength]; - readFully(bextra); - entry.extra = bextra; + fillBuf(); + if (avail <= 0) + throw new ZipException("EOF in header"); } - entry.compressedSize = compressedSize; - entry.size = uncompressedSize; - entry.crc = (long) crc & 0xffffffffL; - entry.method = method; - entry.time = ZipEntry.timeFromDOS(moddate, modtime); - current = entry; - avail = uncompressedSize; - compressed_bytes = compressedSize; - return entry; + return buf[len - avail--] & 0xff; } - // We override fill to let us control how much data gets read from - // the underlying input stream. This lets us avoid having to push - // back data. - protected void fill () throws IOException + /** + * Read an unsigned short in little endian byte order. + */ + private final int readLeShort() throws IOException { - if (closed) - throw new IOException ("stream closed"); - int count = buf.length; - if (count > compressed_bytes) - count = compressed_bytes; - len = in.read(buf, 0, count); - if (len != -1) - { - compressed_bytes -= len; - inf.setInput(buf, 0, len); - } + return readLeByte() | (readLeByte() << 8); } /** - * Creates a new ZipEntry with the given name. - * Used by ZipInputStream when normally <code>new ZipEntry (name)</code> - * would be called. This gives subclasses such as JarInputStream a change - * to override this method and add aditional information to the ZipEntry - * (subclass). + * Read an int in little endian byte order. */ - protected ZipEntry createZipEntry (String name) + private final int readLeInt() throws IOException { - return new ZipEntry (name); + return readLeShort() | (readLeShort() << 16); } - public int read (byte[] b, int off, int len) throws IOException + /** + * 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 (closed) - throw new IOException ("stream closed"); - if (len > avail) - len = avail; - int count; - if (current.method == Deflater.DEFLATED) - count = super.read(b, off, len); - else - count = in.read(b, off, len); - if (count == -1 || avail == 0) + if (crc == null) + throw new IllegalStateException("Closed."); + if (entry != null) + closeEntry(); + + int header = readLeInt(); + if (header == CENSIG) { - inf.reset(); - count = -1; + /* Central Header reached. */ + close(); + return null; } - else - avail -= count; - return count; + 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); + 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; } - public long skip (long n) throws IOException + private void readDataDescr() throws IOException { - if (closed) - throw new IOException ("stream closed"); - if (n > avail) - n = avail; - long count; - if (current.method == Deflater.DEFLATED) - count = super.skip(n); - else - count = in.skip(n); - avail = avail - (int) count; - return count; + 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); } /** - * Returns 0 if the ZipInputStream is closed and 1 otherwise. - * - * @since 1.2 + * Closes the current zip entry and moves to the next one. */ - public int available() + public void closeEntry() throws IOException { - return closed ? 0 : 1; - } + if (crc == null) + throw new IllegalStateException("Closed."); + if (entry == null) + return; - private void readFully (byte[] b) throws IOException - { - int off = 0; - int len = b.length; - while (len > 0) + if (method == ZipOutputStream.DEFLATED) { - int count = in.read(b, off, len); - if (count <= 0) - throw new EOFException(".zip archive ended prematurely"); - off += count; - len -= count; + 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; } - private int readu2 () throws IOException + public int available() throws IOException { - int byte0 = in.read(); - int byte1 = in.read(); - if (byte0 < 0 || byte1 < 0) - throw new EOFException(".zip archive ended prematurely"); - return ((byte1 & 0xFF) << 8) | (byte0 & 0xFF); + return entry != null ? 1 : 0; } - private int read4 () throws IOException + /** + * 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 { - int byte0 = in.read(); - int byte1 = in.read(); - int byte2 = in.read(); - int byte3 = in.read(); - if (byte3 < 0) - throw new EOFException(".zip archive ended prematurely"); - return ((byte3 & 0xFF) << 24) + ((byte2 & 0xFF) << 16) - + ((byte1 & 0xFF) << 8) + (byte0 & 0xFF); + byte[] b = new byte[1]; + if (read(b, 0, 1) <= 0) + return -1; + return b[0] & 0xff; } - public void closeEntry () throws IOException + /** + * 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 (current != null) + if (crc == null) + throw new IllegalStateException("Closed."); + if (entry == null) + return -1; + boolean finished = false; + switch (method) { - if (avail > 0) - skip (avail); - if ((current_flags & 8) != 0) + case ZipOutputStream.DEFLATED: + len = super.read(b, off, len); + if (len < 0) { - int sig = read4(); - if (sig != 0x04034b50) - throw new ZipException("bad/missing magic number at end of .zip entry"); - int crc = read4(); - int compressedSize = read4(); - int uncompressedSize = read4(); - if (current.compressedSize != compressedSize - || current.size != uncompressedSize - || current.crc != crc) - throw new ZipException("bad data descriptor at end of .zip entry"); + 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; } - current = null; - avail = 0; + 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; } + return len; } /** - * Closes this InflaterInputStream. - * - * @since 1.2 + * Closes the zip file. + * @exception IOException if a i/o error occured. */ - public void close () throws IOException + public void close() throws IOException { - current = null; - closed = true; super.close(); + crc = null; + entry = null; } - private ZipEntry current; - private int current_flags; - // Number of uncompressed bytes to be read. - private int avail; - // Number of bytes we can read from underlying stream. - private int compressed_bytes; - // Is this ZipInputStream closed? Set by the close() method. - private boolean closed = false; + /** + * 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); + } } |