ZipInputStream.java 9.21 KB
Newer Older
1
/* java.util.zip.ZipInputStream
John Leuner committed
2
   Copyright (C) 2001, 2002, 2003 Free Software Foundation, Inc.
Per Bothner committed
3

4
This file is part of GNU Classpath.
Per Bothner committed
5

6 7 8 9
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.
10

11 12 13 14 15 16 17 18 19 20
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., 59 Temple Place, Suite 330, Boston, MA
02111-1307 USA.

21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36
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. */
Per Bothner committed
37 38

package java.util.zip;
39 40 41 42
import java.io.EOFException;
import java.io.InputStream;
import java.io.IOException;
import java.util.Enumeration;
Per Bothner committed
43 44

/**
45 46 47 48 49 50 51 52
 * 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
Per Bothner committed
53
 */
54 55 56 57
public class ZipInputStream extends InflaterInputStream implements ZipConstants
{
  private CRC32 crc = new CRC32();
  private ZipEntry entry = null;
Per Bothner committed
58

59 60 61 62 63
  private int csize;
  private int size;
  private int method;
  private int flags;
  private int avail;
64
  private boolean entryAtEOF;
Per Bothner committed
65

66 67 68 69 70 71 72
  /**
   * Creates a new Zip input stream, reading a zip archive.
   */
  public ZipInputStream(InputStream in)
  {
    super(in, new Inflater(true));
  }
Per Bothner committed
73

74
  private void fillBuf() throws IOException
Per Bothner committed
75
  {
76
    avail = len = in.read(buf, 0, buf.length);
Per Bothner committed
77 78
  }

79
  private int readBuf(byte[] out, int offset, int length) throws IOException
Per Bothner committed
80
  {
81
    if (avail <= 0)
Per Bothner committed
82
      {
83 84 85
	fillBuf();
	if (avail <= 0)
	  return -1;
Per Bothner committed
86
      }
87 88 89 90 91 92 93 94 95 96 97 98
    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)
Per Bothner committed
99
      {
100 101 102 103 104
	int count = readBuf(out, off, len);
	if (count == -1)
	  throw new EOFException();
	off += count;
	len -= count;
Per Bothner committed
105
      }
106 107 108 109 110
  }
  
  private final int readLeByte() throws IOException
  {
    if (avail <= 0)
Per Bothner committed
111
      {
112 113 114
	fillBuf();
	if (avail <= 0)
	  throw new ZipException("EOF in header");
Per Bothner committed
115
      }
116
    return buf[len - avail--] & 0xff;
Per Bothner committed
117 118
  }

119 120 121 122
  /**
   * Read an unsigned short in little endian byte order.
   */
  private final int readLeShort() throws IOException 
123
  {
124
    return readLeByte() | (readLeByte() << 8);
125 126
  }

127
  /**
128
   * Read an int in little endian byte order.
129
   */
130
  private final int readLeInt() throws IOException 
131
  {
132
    return readLeShort() | (readLeShort() << 16);
133
  }
134

135 136 137 138 139
  /**
   * 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
Per Bothner committed
140
  {
141
    if (crc == null)
Mark Wielaard committed
142
      throw new IOException("Stream closed.");
143 144 145 146 147
    if (entry != null)
      closeEntry();

    int header = readLeInt();
    if (header == CENSIG)
148
      {
149 150 151
	/* Central Header reached. */
	close();
	return null;
152
      }
153
    if (header != LOCSIG)
154
      throw new ZipException("Wrong Local header signature: "
155
			     + Integer.toHexString(header));
156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175
    /* 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);
176
    entryAtEOF = false;
177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199
    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;
Per Bothner committed
200 201
  }

202
  private void readDataDescr() throws IOException
Per Bothner committed
203
  {
204 205 206 207 208 209 210
    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);
Per Bothner committed
211 212
  }

213
  /**
214
   * Closes the current zip entry and moves to the next one.
215
   */
216
  public void closeEntry() throws IOException
217
  {
218
    if (crc == null)
Mark Wielaard committed
219
      throw new IOException("Stream closed.");
220 221
    if (entry == null)
      return;
222

223
    if (method == ZipOutputStream.DEFLATED)
Per Bothner committed
224
      {
225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250
	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;
	  }
Per Bothner committed
251
      }
252 253 254 255 256 257

    size = 0;
    crc.reset();
    if (method == ZipOutputStream.DEFLATED)
      inf.reset();
    entry = null;
258
    entryAtEOF = true;
Per Bothner committed
259 260
  }

261
  public int available() throws IOException
Per Bothner committed
262
  {
263
    return entryAtEOF ? 0 : 1;
Per Bothner committed
264 265
  }

266 267 268 269 270 271 272
  /**
   * 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
Per Bothner committed
273
  {
274 275 276 277
    byte[] b = new byte[1];
    if (read(b, 0, 1) <= 0)
      return -1;
    return b[0] & 0xff;
Per Bothner committed
278 279
  }

280 281 282 283 284 285 286 287
  /**
   * 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
Per Bothner committed
288
  {
John Leuner committed
289 290
    if (len == 0)
      return 0;
291
    if (crc == null)
Mark Wielaard committed
292
      throw new IOException("Stream closed.");
293 294 295 296
    if (entry == null)
      return -1;
    boolean finished = false;
    switch (method)
Per Bothner committed
297
      {
298 299 300
      case ZipOutputStream.DEFLATED:
	len = super.read(b, off, len);
	if (len < 0)
Per Bothner committed
301
	  {
302 303 304 305 306 307 308 309 310 311 312
	    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;
Per Bothner committed
313
	  }
314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343
	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;
344
	entryAtEOF = true;
Per Bothner committed
345
      }
346
    return len;
Per Bothner committed
347 348
  }

349
  /**
350 351
   * Closes the zip file.
   * @exception IOException if a i/o error occured.
352
   */
353
  public void close() throws IOException
Per Bothner committed
354 355
  {
    super.close();
356 357
    crc = null;
    entry = null;
358
    entryAtEOF = true;
Per Bothner committed
359
  }
360

361 362 363 364 365 366 367 368 369
  /**
   * 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);
  }
Per Bothner committed
370
}