InflaterInputStream.java (read): Loop if data has been read but none output by inflater.

* java/util/zip/InflaterInputStream.java (read): Loop if data has
	been read but none output by inflater.
	* java/util/zip/natDeflater.cc (reset): Set is_finished.
	* java/util/zip/natInflater.cc (reset): Set dist_needed and
	is_finished.
	* java/util/zip/ZipOutputStream.java: Replaced with Classpath
	version.
	* java/util/zip/ZipFile.java: Replaced with Classpath version.
	* java/util/zip/ZipEntry.java: Replaced with Classpath version.
	* java/util/zip/ZipInputStream.java: Replaced with Classpath
	version.
	* java/util/zip/ZipConstants.java: Replaced with Classpath version.

From-SVN: r54653
This commit is contained in:
Tom Tromey 2002-06-15 18:31:13 +00:00 committed by Tom Tromey
parent 21505616a5
commit 5f51b048fb
9 changed files with 1415 additions and 855 deletions

View File

@ -1,3 +1,18 @@
2002-06-15 Tom Tromey <tromey@redhat.com>
* java/util/zip/InflaterInputStream.java (read): Loop if data has
been read but none output by inflater.
* java/util/zip/natDeflater.cc (reset): Set is_finished.
* java/util/zip/natInflater.cc (reset): Set dist_needed and
is_finished.
* java/util/zip/ZipOutputStream.java: Replaced with Classpath
version.
* java/util/zip/ZipFile.java: Replaced with Classpath version.
* java/util/zip/ZipEntry.java: Replaced with Classpath version.
* java/util/zip/ZipInputStream.java: Replaced with Classpath
version.
* java/util/zip/ZipConstants.java: Replaced with Classpath version.
2002-06-13 Tom Tromey <tromey@redhat.com> 2002-06-13 Tom Tromey <tromey@redhat.com>
* java/lang/natString.cc (init): Handle case where DONT_COPY is * java/lang/natString.cc (init): Handle case where DONT_COPY is

View File

@ -1,5 +1,5 @@
/* InflaterInputStream.java - Input stream filter for decompressing /* InflaterInputStream.java - Input stream filter for decompressing
Copyright (C) 1999, 2000 Free Software Foundation, Inc. Copyright (C) 1999, 2000, 2002 Free Software Foundation, Inc.
This file is part of GNU Classpath. This file is part of GNU Classpath.
@ -92,23 +92,30 @@ public class InflaterInputStream extends FilterInputStream
throw new IOException ("stream closed"); throw new IOException ("stream closed");
if (inf.finished()) if (inf.finished())
return -1; return -1;
if (inf.needsInput())
fill (); int count = 0;
int count; while (count == 0)
try
{ {
count = inf.inflate(buf, off, len); if (inf.needsInput())
if (count == 0) fill ();
try
{ {
if (this.len == -1) count = inf.inflate(buf, off, len);
return -1; // Couldn't get any more data to feed to the Inflater if (count == 0)
if (inf.needsDictionary()) {
throw new ZipException ("Inflater needs Dictionary"); if (this.len == -1)
} {
} // Couldn't get any more data to feed to the Inflater
catch (DataFormatException dfe) return -1;
{ }
throw new ZipException (dfe.getMessage()); if (inf.needsDictionary())
throw new ZipException ("Inflater needs Dictionary");
}
}
catch (DataFormatException dfe)
{
throw new ZipException (dfe.getMessage());
}
} }
return count; return count;
} }

View File

@ -1,5 +1,5 @@
/* ZipConstants.java - Some constants used in the zip package /* java.util.zip.ZipConstants
Copyright (C) 1999, 2000 Free Software Foundation, Inc. Copyright (C) 2001 Free Software Foundation, Inc.
This file is part of GNU Classpath. 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 it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2, or (at your option) the Free Software Foundation; either version 2, or (at your option)
any later version. any later version.
GNU Classpath is distributed in the hope that it will be useful, but GNU Classpath is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
@ -37,19 +37,61 @@ exception statement from your version. */
package java.util.zip; package java.util.zip;
/**
* Some constants used in the zip package.
* <p>
* Since this package local interface is completely undocumented no effort
* is made to make it compatible with other implementations.
* If someone is really interested you can probably come up with the right
* constants and documentation by studying the Info-ZIP zipfile.c constants.
*/
interface ZipConstants interface ZipConstants
{ {
// Size in bytes of local file header, including signature. /* The local file header */
public static final int LOCAL_FILE_HEADER_SIZE = 30; public final static int LOCHDR = 30;
public final static int LOCSIG = 'P'|('K'<<8)|(3<<16)|(4<<24);
// Size in bytes of the "end of central directory" record, with signature. public final static int LOCVER = 4;
public static final int END_CENTRAL_DIR_SIZE = 22; public final static int LOCFLG = 6;
public final static int LOCHOW = 8;
public final static int LOCTIM = 10;
public final static int LOCCRC = 14;
public final static int LOCSIZ = 18;
public final static int LOCLEN = 22;
public final static int LOCNAM = 26;
public final static int LOCEXT = 28;
/* The Data descriptor */
public final static int EXTSIG = 'P'|('K'<<8)|(7<<16)|(8<<24);
public final static int EXTHDR = 16;
public final static int EXTCRC = 4;
public final static int EXTSIZ = 8;
public final static int EXTLEN = 12;
/* The central directory file header */
public final static int CENSIG = 'P'|('K'<<8)|(1<<16)|(2<<24);
public final static int CENHDR = 46;
public final static int CENVEM = 4;
public final static int CENVER = 6;
public final static int CENFLG = 8;
public final static int CENHOW = 10;
public final static int CENTIM = 12;
public final static int CENCRC = 16;
public final static int CENSIZ = 20;
public final static int CENLEN = 24;
public final static int CENNAM = 28;
public final static int CENEXT = 30;
public final static int CENCOM = 32;
public final static int CENDSK = 34;
public final static int CENATT = 36;
public final static int CENATX = 38;
public final static int CENOFF = 42;
/* The entries in the end of central directory */
public final static int ENDSIG = 'P'|('K'<<8)|(5<<16)|(6<<24);
public final static int ENDHDR = 22;
/* The following two fields are missing in SUN JDK */
final static int ENDNRD = 4;
final static int ENDDCD = 6;
public final static int ENDSUB = 8;
public final static int ENDTOT = 10;
public final static int ENDSIZ = 12;
public final static int ENDOFF = 16;
public final static int ENDCOM = 20;
} }

View File

@ -1,5 +1,5 @@
/* ZipEntry.java - Represents entries in a zip file archive /* java.util.zip.ZipEntry
Copyright (C) 1999, 2000 Free Software Foundation, Inc. Copyright (C) 2001, 2002 Free Software Foundation, Inc.
This file is part of GNU Classpath. 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 it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2, or (at your option) the Free Software Foundation; either version 2, or (at your option)
any later version. any later version.
GNU Classpath is distributed in the hope that it will be useful, but GNU Classpath is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
@ -36,201 +36,361 @@ obligated to do so. If you do not wish to do so, delete this
exception statement from your version. */ exception statement from your version. */
package java.util.zip; package java.util.zip;
import java.util.Calendar;
import java.util.TimeZone;
import java.util.Date;
/** /**
* @author Per Bothner * This class represents a member of a zip archive. ZipFile and
* @date January 6, 1999. * 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.
/* *
* Written using on-line Java Platform 1.2 API Specification, as well * @author Jochen Hoenicke
* as "The Java Class Libraries", 2nd edition (Addison-Wesley, 1998).
* Status: Believed complete and correct.
*/
/**
* Represents entries in a zip file archive.
* An Entry cn be created by giving a name or by giving an already existing
* ZipEntries whose values should be copied. The name normally represents a
* file path name or directory name.
*/ */
public class ZipEntry implements ZipConstants, Cloneable public class ZipEntry implements ZipConstants, Cloneable
{ {
// These values were determined using a simple test program. private static int KNOWN_SIZE = 1;
public static final int STORED = 0; private static int KNOWN_CSIZE = 2;
public static final int DEFLATED = 8; private static int KNOWN_CRC = 4;
private static int KNOWN_TIME = 8;
String comment; private static Calendar cal = Calendar.getInstance();
long compressedSize = -1;
long crc = -1;
byte[] extra;
int method = -1;
String name;
long size = -1;
long time = -1;
long relativeOffset = -1;
ZipEntry next; private String name;
private int size;
private int compressedSize;
private int crc;
private int time;
private short known = 0;
private short method = -1;
private byte[] extra = null;
private String comment = null;
public ZipEntry (String name) int zipFileIndex = -1; /* used by ZipFile */
int flags; /* used by ZipOutputStream */
int offset; /* used by ZipFile and ZipOutputStream */
/**
* Compression method. This method doesn't compress at all.
*/
public final static int STORED = 0;
/**
* Compression method. This method uses the Deflater.
*/
public final static int DEFLATED = 8;
/**
* Creates a zip entry with the given name.
* @param name the name. May include directory components separated
* by '/'.
*/
public ZipEntry(String name)
{ {
if (name.length() > 65535) if (name == null)
throw new IllegalArgumentException (); throw new NullPointerException();
this.name = name; this.name = name;
} }
/** /**
* Creates a new ZipEntry using the fields of a given ZipEntry. * Creates a copy of the given zip entry.
* The comment, compressedSize, crc, extra, method, name, size, time and * @param e the entry to copy.
* relativeOffset fields are copied from the given entry.
* Note that the contents of the extra byte array field is not cloned,
* only the reference is copied.
* The clone() method does clone the contents of the extra byte array if
* needed.
* @since 1.2
*/ */
public ZipEntry (ZipEntry ent) public ZipEntry(ZipEntry e)
{ {
comment = ent.comment; name = e.name;
compressedSize = ent.compressedSize; known = e.known;
crc = ent.crc; size = e.size;
extra = ent.extra; compressedSize = e.compressedSize;
method = ent.method; crc = e.crc;
name = ent.name; time = e.time;
size = ent.size; method = e.method;
time = ent.time; extra = e.extra;
relativeOffset = ent.relativeOffset; comment = e.comment;
}
/**
* Creates a clone of this ZipEntry. Calls <code>new ZipEntry (this)</code>
* and creates a clone of the contents of the extra byte array field.
*
* @since 1.2
*/
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.
ZipEntry clone = new ZipEntry (this);
clone.extra = (byte[]) extra.clone ();
return clone;
} }
public String getComment () { return comment; } void setDOSTime(int dostime)
public long getCompressedSize () { return compressedSize; }
public long getCrc () { return crc; }
public byte[] getExtra() { return extra; }
public int getMethod () { return method; }
public String getName () { return name; }
public long getSize () { return size; }
public long getTime () { return time; }
public boolean isDirectory ()
{ {
if (name != null) 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 */
// Guard against invalid or missing date causing
// IndexOutOfBoundsException.
try
{ {
int nlen = name.length(); synchronized (cal)
if (nlen > 0 && name.charAt(nlen-1) == '/') {
return true; cal.set(year, mon, day, hrs, min, sec);
time = (int) (cal.getTime().getTime() / 1000L);
}
known |= KNOWN_TIME;
}
catch (RuntimeException ex)
{
/* Ignore illegal time stamp */
known &= ~KNOWN_TIME;
} }
return false;
} }
public void setComment (String comment) int getDOSTime()
{ {
if (comment != null && comment.length() > 65535) if ((known & KNOWN_TIME) == 0)
throw new IllegalArgumentException (); return 0;
synchronized (cal)
{
cal.setTime(new Date(time*1000L));
return (cal.get(cal.YEAR) - 1980 & 0x7f) << 25
| (cal.get(cal.MONTH) + 1) << 21
| (cal.get(cal.DAY_OF_MONTH)) << 16
| (cal.get(cal.HOUR_OF_DAY)) << 11
| (cal.get(cal.MINUTE)) << 5
| (cal.get(cal.SECOND)) >> 1;
}
}
/**
* Creates a copy of this zip entry.
*/
/**
* Clones the entry.
*/
public Object clone()
{
try
{
// The JCL says that the `extra' field is also copied.
ZipEntry clone = (ZipEntry) super.clone();
if (extra != null)
clone.extra = (byte[]) extra.clone();
return clone;
}
catch (CloneNotSupportedException ex)
{
throw new InternalError();
}
}
/**
* 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 = (int) (time / 1000L);
this.known |= KNOWN_TIME;
}
/**
* 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()
{
return (known & KNOWN_TIME) != 0 ? time * 1000L : -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.
* @exception IllegalArgumentException if size is not in 0..0xffffffffL
*/
public void setCompressedSize(long csize)
{
if ((csize & 0xffffffff00000000L) != 0)
throw new IllegalArgumentException();
this.compressedSize = (int) csize;
this.known |= KNOWN_CSIZE;
}
/**
* Gets the size of the compressed data.
* @return the size or -1 if unknown.
*/
public long getCompressedSize()
{
return (known & KNOWN_CSIZE) != 0 ? compressedSize & 0xffffffffL : -1L;
}
/**
* 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 = (short) 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;
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)
{
time = ((extra[pos+1] & 0xff)
| (extra[pos+2] & 0xff) << 8
| (extra[pos+3] & 0xff) << 16
| (extra[pos+4] & 0xff) << 24);
known |= KNOWN_TIME;
}
}
pos += len;
}
}
catch (ArrayIndexOutOfBoundsException ex)
{
/* be lenient */
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.length() > 0xffff)
throw new IllegalArgumentException();
this.comment = comment; this.comment = comment;
} }
/** /**
* Sets the compressedSize of this ZipEntry. * Gets the comment.
* The new size must be between 0 and 0xffffffffL. * @return the comment or null if not set.
* @since 1.2
*/ */
public void setCompressedSize (long compressedSize) public String getComment()
{ {
if (compressedSize < 0 || compressedSize > 0xffffffffL) return comment;
throw new IllegalArgumentException ();
this.compressedSize = compressedSize;
} }
public void setCrc (long crc)
{
if (crc < 0 || crc > 0xffffffffL)
throw new IllegalArgumentException ();
this.crc = crc;
}
public void setExtra (byte[] extra)
{
if (extra != null && extra.length > 65535)
throw new IllegalArgumentException ();
this.extra = extra;
}
public void setMethod (int method)
{
if (method != DEFLATED && method != STORED)
throw new IllegalArgumentException ();
this.method = method;
}
public void setSize (long size)
{
if (size < 0 || size > 0xffffffffL)
throw new IllegalArgumentException ();
this.size = size;
}
public void setTime (long time)
{
this.time = time;
}
private final static short[] daysToMonthStart = {
//Jan Feb Mar Apr May Jun Jul
0, 31, 31+28, 2*31+28, 2*31+28+30, 3*31+28+30, 3*31+28+2*30,
// Aug Sep Oct Nov Dec
4*31+28+2*30, 5*31+28+2*30, 5*31+28+3*30, 6*31+28+3*30, 6*31+28+4*30};
/** Convert a DOS-style type value to milliseconds since 1970. */
static long timeFromDOS (int date, int time)
{
int sec = 2 * (time & 0x1f);
int min = (time >> 5) & 0x3f;
int hrs = (time >> 11) & 0x1f;
int day = date & 0x1f;
int mon = ((date >> 5) & 0xf) - 1;
int year = ((date >> 9) & 0x7f) + 10; /* Since 1970. */
// Guard against invalid or missing date causing IndexOutOfBoundsException.
if (mon < 0 || mon > 11)
return -1;
long mtime = (((hrs * 60) + min) * 60 + sec) * 1000;
// Leap year calculations are rather trivial in this case ...
int days = 365 * year + ((year+1)>>2);
days += daysToMonthStart[mon];
if ((year & 3) == 0 && mon > 1)
days++;
days += day;
return (days * 24*60*60L + ((hrs * 60) + min) * 60 + sec) * 1000L;
}
public String toString () { return name; }
/** /**
* Returns the hashcode of the name of this ZipEntry. * Gets true, if the entry is a directory. This is solely
* determined by the name, a trailing slash '/' marks a directory.
*/ */
public int hashCode () { return name.hashCode (); } 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();
}
} }

View File

@ -1,5 +1,5 @@
/* ZipFile.java - Read contents of a ZIP file /* java.util.zip.ZipFile
Copyright (C) 1999, 2000 Free Software Foundation, Inc. Copyright (C) 2001 Free Software Foundation, Inc.
This file is part of GNU Classpath. 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 it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2, or (at your option) the Free Software Foundation; either version 2, or (at your option)
any later version. any later version.
GNU Classpath is distributed in the hope that it will be useful, but GNU Classpath is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
@ -36,233 +36,422 @@ obligated to do so. If you do not wish to do so, delete this
exception statement from your version. */ exception statement from your version. */
package java.util.zip; package java.util.zip;
import java.io.File;
import java.io.InputStream;
import java.io.IOException;
import java.io.EOFException;
import java.io.RandomAccessFile;
import java.util.Enumeration;
import java.util.NoSuchElementException;
import java.io.*; /**
* This class represents a Zip archive. You can ask for the contained
/* Written using on-line Java Platform 1.2 API Specification * entries, or get an input stream for a file entry. The entry is
* and JCL book. * automatically decompressed.
* Believed complete and correct. *
* This class is thread safe: You can open input streams for arbitrary
* entries in different threads.
*
* @author Jochen Hoenicke
*/ */
public class ZipFile implements ZipConstants public class ZipFile implements ZipConstants
{ {
public static final int OPEN_READ = 1;
public static final int OPEN_DELETE = 4;
public ZipFile (String fname) throws IOException /** 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;
private String name;
RandomAccessFile raf;
ZipEntry[] entries;
/**
* 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(new File(fname)); this.raf = new RandomAccessFile(name, "r");
this.name = name;
readEntries();
} }
public ZipFile (File f) throws IOException /**
* 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(f, OPEN_READ); this.raf = new RandomAccessFile(file, "r");
this.name = file.getName();
readEntries();
} }
public ZipFile (File f, int mode) throws IOException /**
* 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.
*
* The OPEN_DELETE mode is currently unimplemented in this library
*
* @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
("mode can only be OPEN_READ or OPEN_READ | OPEN_DELETE");
if ((mode & OPEN_DELETE) != 0) if ((mode & OPEN_DELETE) != 0)
{ {
delete_on_close = f; throw new IllegalArgumentException("OPEN_DELETE mode not supported yet in java.util.zip.ZipFile");
f.deleteOnExit();
} }
else this.raf = new RandomAccessFile(file, "r");
{ this.name = file.getName();
delete_on_close = null; readEntries();
}
file = new RandomAccessFile(f, "r");
name = f.getName();
readDirectory ();
} }
void readDirectory () throws IOException /**
* Read an unsigned short in little endian byte order.
* @exception IOException if a i/o error occured.
* @exception EOFException if the file ends prematurely
*/
private final int readLeShort() throws IOException {
return raf.readUnsignedByte() | raf.readUnsignedByte() << 8;
}
/**
* Read an int in little endian byte order.
* @exception IOException if a i/o error occured.
* @exception EOFException if the file ends prematurely
*/
private final int readLeInt() throws IOException {
return readLeShort() | readLeShort() << 16;
}
/**
* Read the central directory of a zip file and fill the entries
* array. This is called exactly once by the constructors.
* @exception IOException if a i/o error occured.
* @exception ZipException if the central directory is malformed
*/
private void readEntries() throws ZipException, IOException
{ {
long size = file.length (); /* Search for the End Of Central Directory. When a zip comment is
if (size < ZipConstants.END_CENTRAL_DIR_SIZE) * present the directory may start earlier.
throw new ZipException ("zipfile too short"); * FIXME: This searches the whole file in a very slow manner if the
// We do not handle a "zipfile comment", which the appnote says can * file isn't a zip file.
// be at the end of a .zip file. We could handle this by seeking */
// to the beginning and reading forwards. long pos = raf.length() - ENDHDR;
file.seek(size - ZipConstants.END_CENTRAL_DIR_SIZE); do
if (file.read() != 'P'
|| file.read() != 'K'
|| file.read() != '\005'
|| file.read() != '\006')
throw new ZipException("not a valid zipfile");
file.skipBytes(6);
numEntries = readu2();
int dir_size = read4 (); // Read "size of the central directory".
file.seek(size - (dir_size + ZipConstants.END_CENTRAL_DIR_SIZE));
ZipEntry last = null;
for (int i = 0; i < numEntries; i++)
{ {
file.skipBytes(10); if (pos < 0)
int method = readu2(); throw new ZipException
int modtime = readu2(); ("central directory not found, probably not a zip file");
int moddate = readu2(); raf.seek(pos--);
int crc = read4(); }
int compressedSize = read4(); while (readLeInt() != ENDSIG);
int uncompressedSize = read4(); if (raf.skipBytes(ENDTOT - ENDNRD) != ENDTOT - ENDNRD)
int filenameLength = readu2(); throw new EOFException();
int extraLength = readu2(); int count = readLeShort();
int commentLength = readu2(); if (raf.skipBytes(ENDOFF - ENDSIZ) != ENDOFF - ENDSIZ)
int diskNumberStart = readu2(); throw new EOFException();
int intAttributes = readu2(); int centralOffset = readLeInt();
int extAttributes = read4();
int relativeOffset = read4(); entries = new ZipEntry[count];
byte[] bname = new byte[filenameLength]; raf.seek(centralOffset);
file.readFully(bname); for (int i = 0; i < count; i++)
ZipEntry entry = new ZipEntry(new String(bname, "8859_1")); {
if (extraLength > 0) if (readLeInt() != CENSIG)
throw new ZipException("Wrong Central Directory signature");
if (raf.skipBytes(CENHOW - CENVEM) != CENHOW - CENVEM)
throw new EOFException();
int method = readLeShort();
int dostime = readLeInt();
int crc = readLeInt();
int csize = readLeInt();
int size = readLeInt();
int nameLen = readLeShort();
int extraLen = readLeShort();
int commentLen = readLeShort();
if (raf.skipBytes(CENOFF - CENDSK) != CENOFF - CENDSK)
throw new EOFException();
int offset = readLeInt();
byte[] buffer = new byte[Math.max(nameLen, commentLen)];
raf.readFully(buffer, 0, nameLen);
String name = new String(buffer, 0, 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[] bextra = new byte[extraLength]; byte[] extra = new byte[extraLen];
file.readFully(bextra); raf.readFully(extra);
entry.extra = bextra; entry.setExtra(extra);
} }
if (commentLength > 0) if (commentLen > 0)
{ {
byte[] bcomment = new byte[commentLength]; raf.readFully(buffer, 0, commentLen);
file.readFully(bcomment); entry.setComment(new String(buffer, 0, commentLen));
entry.comment = new String(bcomment, "8859_1");
} }
entry.compressedSize = compressedSize; entry.zipFileIndex = i;
entry.size = uncompressedSize; entry.offset = offset;
entry.crc = (long) crc & 0xffffffffL; entries[i] = entry;
entry.method = method;
entry.relativeOffset = relativeOffset;
entry.time = ZipEntry.timeFromDOS(moddate, modtime);
if (last == null)
entries = entry;
else
last.next = entry;
last = entry;
} }
} }
public java.util.Enumeration entries() /**
{ * Closes the ZipFile. This also closes all input streams given by
return new ZipEnumeration(this); * 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 public void close() throws IOException
{ {
file.close();
entries = null; entries = null;
numEntries = 0; synchronized (raf)
if (delete_on_close != null) {
delete_on_close.delete(); raf.close();
}
} }
/**
* Returns an enumeration of all Zip entries in this Zip file.
*/
public Enumeration entries()
{
if (entries == null)
throw new IllegalStateException("ZipFile has closed");
return new ZipEntryEnumeration(entries);
}
private int getEntryIndex(String name)
{
for (int i = 0; i < entries.length; i++)
if (name.equals(entries[i].getName()))
return i;
return -1;
}
/**
* Searches for a zip entry in this archive with the given name.
* @param the name. May contain directory components separated by
* slashes ('/').
* @return the zip entry, or null if no entry with that name exists.
* @see #entries */
public ZipEntry getEntry(String name) public ZipEntry getEntry(String name)
{ {
for (ZipEntry entry = entries; entry != null; entry = entry.next) if (entries == null)
{ throw new IllegalStateException("ZipFile has closed");
if (name.equals(entry.getName())) int index = getEntryIndex(name);
return entry; return index >= 0 ? (ZipEntry) entries[index].clone() : null;
}
return null;
} }
public InputStream getInputStream(ZipEntry ze) throws IOException /**
* Checks, if the local header of the entry at index i matches the
* central directory, and returns the offset to the data.
* @return the start offset of the (compressed) data.
* @exception IOException if a i/o error occured.
* @exception ZipException if the local header doesn't match the
* central directory header
*/
private long checkLocalHeader(ZipEntry entry) throws IOException
{ {
byte[] buffer = new byte[(int) ze.getCompressedSize()]; synchronized (raf)
{
raf.seek(entry.offset);
if (readLeInt() != LOCSIG)
throw new ZipException("Wrong Local header signature");
/* Read the size of the extra field, and skip to the start of the /* skip version and flags */
data. */ if (raf.skipBytes(LOCHOW - LOCVER) != LOCHOW - LOCVER)
file.seek (ze.relativeOffset + ZipConstants.LOCAL_FILE_HEADER_SIZE - 2); throw new EOFException();
int extraFieldLength = readu2();
file.skipBytes (ze.getName().length() + extraFieldLength);
file.readFully(buffer); if (entry.getMethod() != readLeShort())
throw new ZipException("Compression method mismatch");
InputStream is = new ByteArrayInputStream (buffer); /* Skip time, crc, size and csize */
if (ze.getMethod() == ZipEntry.DEFLATED) if (raf.skipBytes(LOCNAM - LOCTIM) != LOCNAM - LOCTIM)
// Data in zipfile entries does not have a zlib header, so construct throw new EOFException();
// an Inflater with the `nowrapper' option.
is = new InflaterInputStream (is, new Inflater (true), 512); if (entry.getName().length() != readLeShort())
return is; throw new ZipException("file name length mismatch");
int extraLen = entry.getName().length() + readLeShort();
return entry.offset + LOCHDR + extraLen;
}
} }
public String getName () /**
* Creates an input stream reading the given zip entry as
* uncompressed data. Normally zip entry should be an entry
* returned by getEntry() or entries().
* @return the input stream.
* @exception IOException if a i/o error occured.
* @exception ZipException if the Zip archive is malformed.
*/
public InputStream getInputStream(ZipEntry entry) throws IOException
{
if (entries == null)
throw new IllegalStateException("ZipFile has closed");
int index = entry.zipFileIndex;
if (index < 0 || index >= entries.length
|| entries[index].getName() != entry.getName())
{
index = getEntryIndex(entry.getName());
if (index < 0)
throw new NoSuchElementException();
}
long start = checkLocalHeader(entries[index]);
int method = entries[index].getMethod();
InputStream is = new PartialInputStream
(raf, start, entries[index].getCompressedSize());
switch (method)
{
case ZipOutputStream.STORED:
return is;
case ZipOutputStream.DEFLATED:
return new InflaterInputStream(is, new Inflater(true));
default:
throw new ZipException("Unknown compression method " + method);
}
}
/**
* Returns the name of this zip file.
*/
public String getName()
{ {
return name; return name;
} }
/** /**
* Returns the number of entries in this ZipFile. * Returns the number of entries in this zip file.
* @exception IllegalStateException if the ZipFile has been closed.
*
* @since 1.2
*/ */
public int size () public int size()
{ {
if (entries == null) try
throw new IllegalStateException("ZipFile already closed"); {
else return entries.length;
return numEntries; }
catch (NullPointerException ex)
{
throw new IllegalStateException("ZipFile has closed");
}
}
private static class ZipEntryEnumeration implements Enumeration
{
ZipEntry[] array;
int ptr = 0;
public ZipEntryEnumeration(ZipEntry[] arr)
{
array = arr;
}
public boolean hasMoreElements()
{
return ptr < array.length;
}
public Object nextElement()
{
try
{
/* We return a clone, just to be safe that the user doesn't
* change the entry.
*/
return array[ptr++].clone();
}
catch (ArrayIndexOutOfBoundsException ex)
{
throw new NoSuchElementException();
}
}
} }
protected void finalize () throws IOException private static class PartialInputStream extends InputStream
{ {
close(); RandomAccessFile raf;
} long filepos, end;
private int readu2 () throws IOException public PartialInputStream(RandomAccessFile raf, long start, long len)
{ {
int byte0 = file.read(); this.raf = raf;
int byte1 = file.read(); filepos = start;
if (byte0 < 0 || byte1 < 0) end = start + len;
throw new ZipException (".zip archive ended prematurely"); }
return ((byte1 & 0xFF) << 8) | (byte0 & 0xFF);
}
private int read4 () throws IOException
{
int byte0 = file.read();
int byte1 = file.read();
int byte2 = file.read();
int byte3 = file.read();
if (byte3 < 0)
throw new ZipException (".zip archive ended prematurely");
return ((byte3 & 0xFF) << 24) + ((byte2 & 0xFF) << 16)
+ ((byte1 & 0xFF) << 8) + (byte0 & 0xFF);
}
ZipEntry entries;
int numEntries;
RandomAccessFile file;
String name;
/** File to delete on close or null. */
File delete_on_close;
} public int available()
{
long amount = end - filepos;
if (amount > Integer.MAX_VALUE)
return Integer.MAX_VALUE;
return (int) amount;
}
public int read() throws IOException
{
if (filepos == end)
return -1;
synchronized (raf)
{
raf.seek(filepos++);
return raf.read();
}
}
final class ZipEnumeration implements java.util.Enumeration public int read(byte[] b, int off, int len) throws IOException
{ {
ZipEntry entry; if (len > end - filepos)
{
len = (int) (end - filepos);
if (len == 0)
return -1;
}
synchronized (raf)
{
raf.seek(filepos);
int count = raf.read(b, off, len);
if (count > 0)
filepos += len;
return count;
}
}
ZipEnumeration (ZipFile zfile) public long skip(long amount)
{ {
entry = zfile.entries; if (amount < 0)
} throw new IllegalArgumentException();
if (amount > end - filepos)
public boolean hasMoreElements () amount = end - filepos;
{ filepos += amount;
return entry != null; return amount;
} }
public Object nextElement ()
{
ZipEntry cur = entry;
if (cur == null)
throw new java.util.NoSuchElementException();
entry = cur.next;
return cur;
} }
} }

View File

@ -1,5 +1,5 @@
/* ZipInputStream.java - Input filter for reading zip file /* java.util.zip.ZipInputStream
Copyright (C) 1999, 2000 Free Software Foundation, Inc. Copyright (C) 2001, 2002 Free Software Foundation, Inc.
This file is part of GNU Classpath. 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 it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2, or (at your option) the Free Software Foundation; either version 2, or (at your option)
any later version. any later version.
GNU Classpath is distributed in the hope that it will be useful, but GNU Classpath is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 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. */ exception statement from your version. */
package java.util.zip; 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 * This is a FilterInputStream that reads the files in an zip archive
* @date May 1999. * 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
*/ */
/*
* 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.
*/
// 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 class ZipInputStream extends InflaterInputStream implements ZipConstants
{ {
public ZipInputStream (InputStream in) private CRC32 crc = new CRC32();
{ private ZipEntry entry = null;
super (in, new Inflater (true));
}
public ZipEntry getNextEntry () throws IOException private int csize;
{ private int size;
if (closed) private int method;
throw new IOException ("stream closed"); private int flags;
if (current != null) private int avail;
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();
// `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();
}
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 = createZipEntry(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
{
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);
}
}
/** /**
* Creates a new ZipEntry with the given name. * Creates a new Zip input stream, reading a zip archive.
* 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).
*/ */
protected ZipEntry createZipEntry (String name) public ZipInputStream(InputStream in)
{ {
return new ZipEntry (name); super(in, new Inflater(true));
} }
public int read (byte[] b, int off, int len) throws IOException private void fillBuf() throws IOException
{ {
if (closed) avail = len = in.read(buf, 0, buf.length);
throw new IOException ("stream closed"); }
if (len > avail)
len = avail; private int readBuf(byte[] out, int offset, int length) throws IOException
int count; {
if (current.method == Deflater.DEFLATED) if (avail <= 0)
count = super.read(b, off, len);
else
count = in.read(b, off, len);
if (count == -1 || avail == 0)
{ {
inf.reset(); fillBuf();
count = -1; if (avail <= 0)
return -1;
} }
else if (length > avail)
avail -= count; length = avail;
return count; System.arraycopy(buf, len - avail, out, offset, length);
avail -= length;
return length;
} }
public long skip (long n) throws IOException private void readFully(byte[] out) 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;
}
/**
* Returns 0 if the ZipInputStream is closed and 1 otherwise.
*
* @since 1.2
*/
public int available()
{
return closed ? 0 : 1;
}
private void readFully (byte[] b) throws IOException
{ {
int off = 0; int off = 0;
int len = b.length; int len = out.length;
while (len > 0) while (len > 0)
{ {
int count = in.read(b, off, len); int count = readBuf(out, off, len);
if (count <= 0) if (count == -1)
throw new EOFException(".zip archive ended prematurely"); throw new EOFException();
off += count; off += count;
len -= count; len -= count;
} }
} }
private int readu2 () throws IOException private final int readLeByte() throws IOException
{ {
int byte0 = in.read(); if (avail <= 0)
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) fillBuf();
skip (avail); if (avail <= 0)
if ((current_flags & 8) != 0) throw new ZipException("EOF in header");
{
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;
} }
return buf[len - avail--] & 0xff;
} }
/** /**
* Closes this InflaterInputStream. * Read an unsigned short in little endian byte order.
*
* @since 1.2
*/ */
public void close () throws IOException private final int readLeShort() throws IOException
{ {
current = null; return readLeByte() | (readLeByte() << 8);
closed = true;
super.close();
} }
private ZipEntry current; /**
private int current_flags; * Read an int in little endian byte order.
// Number of uncompressed bytes to be read. */
private int avail; private final int readLeInt() throws IOException
// Number of bytes we can read from underlying stream. {
private int compressed_bytes; return readLeShort() | (readLeShort() << 16);
// Is this ZipInputStream closed? Set by the close() method. }
private boolean closed = false;
/**
* 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 IllegalStateException("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);
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 IllegalStateException("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;
}
public int available() throws IOException
{
return entry != null ? 1 : 0;
}
/**
* 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 (crc == null)
throw new IllegalStateException("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;
}
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;
}
/**
* 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);
}
} }

View File

@ -1,5 +1,5 @@
/* ZipOutputStream.java - Create a file in zip format /* java.util.zip.ZipOutputStream
Copyright (C) 1999, 2000 Free Software Foundation, Inc. Copyright (C) 2001 Free Software Foundation, Inc.
This file is part of GNU Classpath. 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 it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2, or (at your option) the Free Software Foundation; either version 2, or (at your option)
any later version. any later version.
GNU Classpath is distributed in the hope that it will be useful, but GNU Classpath is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
@ -36,286 +36,362 @@ obligated to do so. If you do not wish to do so, delete this
exception statement from your version. */ exception statement from your version. */
package java.util.zip; package java.util.zip;
import java.io.OutputStream;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.util.Vector;
import java.util.Enumeration;
import java.io.*; /**
* This is a FilterOutputStream that writes the files into a zip
/* Written using on-line Java Platform 1.2 API Specification * archive one after another. It has a special method to start a new
* and JCL book. * zip entry. The zip entries contains information about the file name
* Believed complete and correct. * 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
public class ZipOutputStream extends DeflaterOutputStream
implements ZipConstants
{ {
public static final int STORED = 0; private Vector entries = new Vector();
public static final int DEFLATED = 8; private CRC32 crc = new CRC32();
private ZipEntry curEntry = null;
public void close () throws IOException 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 final static int ZIP_STORED_VERSION = 10;
private final static int ZIP_DEFLATED_VERSION = 20;
/**
* Compression method. This method doesn't compress at all.
*/
public final static int STORED = 0;
/**
* Compression method. This method uses the Deflater.
*/
public final static 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)
{ {
finish (); super(out, new Deflater(Deflater.DEFAULT_COMPRESSION, true));
out.close();
} }
public void closeEntry () throws IOException /**
* 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)
{ {
int compressed_size; byte[] commentBytes;
if (current.method == STORED) commentBytes = comment.getBytes();
{ if (commentBytes.length > 0xffff)
compressed_size = uncompressed_size; throw new IllegalArgumentException("Comment too long.");
} zipComment = commentBytes;
else }
{
super.finish(); /**
compressed_size = def.getTotalOut(); * Sets default compression method. If the Zip entry specifies
} * another method its method takes precedence.
long crc = sum.getValue(); * @param method the method.
* @exception IllegalArgumentException if method is not supported.
bytes_written += compressed_size; * @see #STORED
* @see #DEFLATED
if (current.getCrc() == -1 || current.getCompressedSize() == -1 */
|| current.getSize() == -1) public void setMethod(int method)
{ {
current.setCrc(crc); if (method != STORED && method != DEFLATED)
current.compressedSize = compressed_size; throw new IllegalArgumentException("Method not supported.");
current.setSize(uncompressed_size); defaultMethod = method;
put4 (0x08074b50);
put4 ((int) (current.getCrc()));
put4 ((int) (current.getCompressedSize()));
put4 ((int) (current.getSize()));
bytes_written += 16;
}
else if (current.getCrc() != crc
|| current.getCompressedSize() != compressed_size
|| current.getSize() != uncompressed_size)
throw new ZipException ("zip entry field incorrect");
current.next = chain;
chain = current;
current = null;
} }
public void write (int bval) throws IOException /**
* 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)
{ {
if (current.method == STORED) def.setLevel(level);
{ }
out.write(bval);
} /**
else * Write an unsigned short in little endian byte order.
super.write(bval); */
sum.update(bval); private final void writeLeShort(int value) throws IOException
uncompressed_size += 1; {
out.write(value & 0xff);
out.write((value >> 8) & 0xff);
} }
public void write (byte[] buf, int off, int len) throws IOException /**
* Write an int in little endian byte order.
*/
private final void writeLeInt(int value) throws IOException
{ {
if (current.method == STORED) writeLeShort(value);
out.write(buf, off, len); writeLeShort(value >> 16);
else
super.write(buf, off, len);
sum.update(buf, off, len);
uncompressed_size += len;
} }
public void finish () throws IOException /**
* 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 IllegalStateException if stream was finished
*/
public void putNextEntry(ZipEntry entry) throws IOException
{ {
if (current != null) if (entries == null)
closeEntry (); throw new IllegalStateException("ZipOutputStream was finished");
// Write the central directory. int method = entry.getMethod();
long offset = bytes_written; int flags = 0;
int count = 0; if (method == -1)
int bytes = 0; method = defaultMethod;
while (chain != null)
if (method == STORED)
{ {
bytes += write_entry (chain, false); if (entry.getCompressedSize() >= 0)
++count;
chain = chain.next;
}
// Write the end of the central directory record.
put4 (0x06054b50);
// Disk number.
put2 (0);
// Another disk number.
put2 (0);
put2 (count);
put2 (count);
put4 (bytes);
put4 ((int) offset);
byte[] c = comment.getBytes("8859_1");
put2 (c.length);
out.write(c);
}
// Helper for finish and putNextEntry.
private int write_entry (ZipEntry entry, boolean is_local)
throws IOException
{
int bytes = put4 (is_local ? 0x04034b50 : 0x02014b50);
if (! is_local)
bytes += put_version ();
bytes += put_version ();
boolean crc_after = false;
if (is_local
&& (entry.getCrc() == -1 || entry.getCompressedSize() == -1
|| entry.getSize() == -1))
crc_after = true;
// For the bits field we always indicate `normal' compression,
// even if that isn't true.
bytes += put2 (crc_after ? (1 << 3) : 0);
bytes += put2 (entry.method);
bytes += put2(0); // time - FIXME
bytes += put2(0); // date - FIXME
if (crc_after)
{
// CRC, compressedSize, and Size are always 0 in this header.
// The actual values are given after the entry.
bytes += put4 (0);
bytes += put4 (0);
bytes += put4 (0);
}
else
{
bytes += put4 ((int) (entry.getCrc()));
bytes += put4 ((int) (entry.getCompressedSize()));
bytes += put4 ((int) (entry.getSize()));
}
byte[] name = entry.name.getBytes("8859_1");
bytes += put2 (name.length);
bytes += put2 (entry.extra == null ? 0 : entry.extra.length);
byte[] comment = null;
if (! is_local)
{
if (entry.getComment() == null)
bytes += put2 (0);
else
{ {
comment = entry.getComment().getBytes("8859_1"); if (entry.getSize() < 0)
bytes += put2 (comment.length); entry.setSize(entry.getCompressedSize());
else if (entry.getSize() != entry.getCompressedSize())
throw new ZipException
("Method STORED, but compressed size != size");
} }
else
entry.setCompressedSize(entry.getSize());
// Disk number start. if (entry.getSize() < 0)
bytes += put2 (0); throw new ZipException("Method STORED, but size not set");
// Internal file attributes. if (entry.getCrc() < 0)
bytes += put2 (0); throw new ZipException("Method STORED, but crc not set");
// External file attributes. }
bytes += put4 (0); else if (method == DEFLATED)
// Relative offset of local header. {
bytes += put4 ((int) entry.relativeOffset); if (entry.getCompressedSize() < 0
|| entry.getSize() < 0 || entry.getCrc() < 0)
flags |= 8;
} }
out.write (name); if (curEntry != null)
bytes += name.length; closeEntry();
if (entry.extra != null)
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)
{ {
out.write(entry.extra); writeLeInt((int)entry.getCrc());
bytes += entry.extra.length; writeLeInt((int)entry.getCompressedSize());
writeLeInt((int)entry.getSize());
} }
if (comment != null) else
{ {
writeLeInt(0);
writeLeInt(0);
writeLeInt(0);
}
byte[] name = entry.getName().getBytes();
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 IllegalStateException if no entry is active.
*/
public void closeEntry() throws IOException
{
if (curEntry == null)
throw new IllegalStateException("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 IllegalStateException if no entry is active.
*/
public void write(byte[] b, int off, int len) throws IOException
{
if (curEntry == null)
throw new IllegalStateException("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 enum = entries.elements();
while (enum.hasMoreElements())
{
ZipEntry entry = (ZipEntry) enum.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 = entry.getName().getBytes();
if (name.length > 0xffff)
throw new ZipException("Name too long.");
byte[] extra = entry.getExtra();
if (extra == null)
extra = new byte[0];
String strComment = entry.getComment();
byte[] comment = strComment != null
? strComment.getBytes() : new byte[0];
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); out.write(comment);
bytes += comment.length; numEntries++;
sizeEntries += CENHDR + name.length + extra.length + comment.length;
} }
bytes_written += bytes; writeLeInt(ENDSIG);
return bytes; 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;
} }
public void putNextEntry (ZipEntry entry) throws IOException
{
if (current != null)
closeEntry ();
if (entry.method < 0 )
entry.method = method;
if (entry.method == STORED)
{
if (entry.getSize() == -1 || entry.getCrc() == -1)
throw new ZipException ("required entry not set");
// Just in case.
entry.compressedSize = entry.getSize();
}
entry.relativeOffset = bytes_written;
write_entry (entry, true);
current = entry;
int compr = (method == STORED) ? Deflater.NO_COMPRESSION : level;
def.reset();
def.setLevel(compr);
sum.reset();
uncompressed_size = 0;
}
public void setLevel (int level)
{
if (level != Deflater.DEFAULT_COMPRESSION
&& (level < Deflater.NO_COMPRESSION
|| level > Deflater.BEST_COMPRESSION))
throw new IllegalArgumentException ();
this.level = level;
}
public void setMethod (int method)
{
if (method != DEFLATED && method != STORED)
throw new IllegalArgumentException ();
this.method = method;
}
public void setComment (String comment)
{
if (comment.length() > 65535)
throw new IllegalArgumentException ();
this.comment = comment;
}
public ZipOutputStream (OutputStream out)
{
super (out, new Deflater (Deflater.DEFAULT_COMPRESSION, true), 8192);
sum = new CRC32 ();
}
private int put2 (int i) throws IOException
{
out.write (i);
out.write (i >> 8);
return 2;
}
private int put4 (int i) throws IOException
{
out.write (i);
out.write (i >> 8);
out.write (i >> 16);
out.write (i >> 24);
return 4;
}
private int put_version () throws IOException
{
// FIXME: for now we assume Unix, and we ignore the version
// number.
return put2 (3 << 8);
}
// The entry we are currently writing, or null if we've called
// closeEntry.
private ZipEntry current;
// The chain of entries which have been written to this file.
private ZipEntry chain;
private int method = DEFLATED;
private int level = Deflater.DEFAULT_COMPRESSION;
private String comment = "";
private long bytes_written;
private int uncompressed_size;
/** The checksum object. */
private Checksum sum;
} }

View File

@ -1,6 +1,6 @@
// natDeflater.cc - Implementation of Deflater native methods. // natDeflater.cc - Implementation of Deflater native methods.
/* Copyright (C) 1999 Free Software Foundation /* Copyright (C) 1999, 2002 Free Software Foundation
This file is part of libgcj. This file is part of libgcj.
@ -125,6 +125,7 @@ java::util::zip::Deflater::reset ()
// Just ignore errors. // Just ignore errors.
deflateReset (s); deflateReset (s);
flush_flag = 0; flush_flag = 0;
is_finished = false;
} }
void void

View File

@ -1,6 +1,6 @@
// natInflater.cc - Implementation of Inflater native methods. // natInflater.cc - Implementation of Inflater native methods.
/* Copyright (C) 1999 Free Software Foundation /* Copyright (C) 1999, 2002 Free Software Foundation
This file is part of libgcj. This file is part of libgcj.
@ -149,6 +149,8 @@ java::util::zip::Inflater::reset ()
z_streamp s = (z_streamp) zstream; z_streamp s = (z_streamp) zstream;
// Just ignore errors. // Just ignore errors.
inflateReset (s); inflateReset (s);
is_finished = false;
dict_needed = false;
} }
void void