/* Copyright (C) 1999 Cygnus Solutions This file is part of libgcj. This software is copyrighted work licensed under the terms of the Libgcj License. Please consult the file "LIBGCJ_LICENSE" for details. */ package java.util.zip; import java.io.*; /** * @author Per Bothner * @date May 1999. */ /* * 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. */ // JDK1.2 has "protected ZipEntry createZipEntry(String)" but is very // vague about what the method does. FIXME. // We do not calculate the CRC and compare it with the specified value; // we probably should. FIXME. public class ZipInputStream extends InflaterInputStream implements ZipConstants { public ZipInputStream (InputStream in) { super (in, new Inflater (true)); } public ZipEntry getNextEntry () throws IOException { if (current != null) closeEntry(); if (in.read() != 'P' || in.read() != 'K') return null; int code = in.read(); while (code == '\001') { 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(); in.skip(12+fname_length+extra_length+fcomment_length+size); if (in.read() != 'P' || in.read() != 'K') return null; code = in.read(); } if (code == '\005') { 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(); } 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 = new ZipEntry(new String(bname, "8859_1")); if (extraLength > 0) { byte[] bextra = new byte[extraLength]; readFully(bextra); entry.extra = bextra; } 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; } // 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 { 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); } } public int read (byte[] b, int off, int len) throws IOException { 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) { inf.reset(); count = -1; } else avail -= count; return count; } public long skip (long n) throws IOException { 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; } private void readFully (byte[] b) throws IOException { int off = 0; int len = b.length; while (len > 0) { int count = in.read(b, off, len); if (count <= 0) throw new EOFException(".zip archive ended prematurely"); off += count; len -= count; } } private int readu2 () 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); } private int read4 () 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); } public void closeEntry () throws IOException { if (current != null) { if (avail > 0) skip (avail); if ((current_flags & 8) != 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"); } current = null; avail = 0; } } public void close () throws IOException { current = null; super.close(); } 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; }