Commit 5f51b048 by Tom Tromey Committed by Tom Tromey

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
parent 21505616
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
......
/* 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 ...@@ -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;
} }
......
/* 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 ...@@ -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. */ ...@@ -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);
public final static int LOCVER = 4;
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;
// Size in bytes of the "end of central directory" record, with signature. /* The central directory file header */
public static final int END_CENTRAL_DIR_SIZE = 22; 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;
} }
/* 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 ...@@ -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 ...@@ -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;
long compressedSize = -1; private static Calendar cal = Calendar.getInstance();
long crc = -1;
byte[] extra; private String name;
int method = -1; private int size;
String name; private int compressedSize;
long size = -1; private int crc;
long time = -1; private int time;
long relativeOffset = -1; private short known = 0;
private short method = -1;
ZipEntry next; private byte[] extra = null;
private String comment = null;
public ZipEntry (String name)
{ int zipFileIndex = -1; /* used by ZipFile */
if (name.length() > 65535) int flags; /* used by ZipOutputStream */
throw new IllegalArgumentException (); int offset; /* used by ZipFile and ZipOutputStream */
this.name = name;
}
/**
* 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 ZipEntry using the fields of a given ZipEntry. * Creates a zip entry with the given name.
* The comment, compressedSize, crc, extra, method, name, size, time and * @param name the name. May include directory components separated
* relativeOffset fields are copied from the given entry. * by '/'.
* 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(String name)
{ {
comment = ent.comment; if (name == null)
compressedSize = ent.compressedSize; throw new NullPointerException();
crc = ent.crc; this.name = name;
extra = ent.extra;
method = ent.method;
name = ent.name;
size = ent.size;
time = ent.time;
relativeOffset = ent.relativeOffset;
} }
/** /**
* Creates a clone of this ZipEntry. Calls <code>new ZipEntry (this)</code> * Creates a copy of the given zip entry.
* and creates a clone of the contents of the extra byte array field. * @param e the entry to copy.
*
* @since 1.2
*/ */
public Object clone () public ZipEntry(ZipEntry e)
{ {
// JCL defines this as being the same as the copy constructor above, name = e.name;
// except that value of the "extra" field is also copied. known = e.known;
ZipEntry clone = new ZipEntry (this); size = e.size;
clone.extra = (byte[]) extra.clone (); compressedSize = e.compressedSize;
return clone; crc = e.crc;
time = e.time;
method = e.method;
extra = e.extra;
comment = e.comment;
} }
public String getComment () { return comment; } void setDOSTime(int dostime)
{
public long getCompressedSize () { return compressedSize; } 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
{
synchronized (cal)
{
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;
}
}
public long getCrc () { return crc; } int getDOSTime()
{
if ((known & KNOWN_TIME) == 0)
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;
}
}
public byte[] getExtra() { return extra; } /**
* 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();
}
}
public int getMethod () { return method; } /**
* Returns the entry name. The path components in the entry are
* always separated by slashes ('/').
*/
public String getName()
{
return name;
}
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;
}
public long getSize () { return size; } /**
* 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;
}
public long getTime () { return time; } /**
* 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;
}
public boolean isDirectory () /**
* Gets the size of the uncompressed data.
* @return the size or -1 if unknown.
*/
public long getSize()
{ {
if (name != null) return (known & KNOWN_SIZE) != 0 ? size & 0xffffffffL : -1L;
{
int nlen = name.length();
if (nlen > 0 && name.charAt(nlen-1) == '/')
return true;
}
return false;
} }
public void setComment (String comment) /**
* Sets the size of the compressed data.
* @exception IllegalArgumentException if size is not in 0..0xffffffffL
*/
public void setCompressedSize(long csize)
{ {
if (comment != null && comment.length() > 65535) if ((csize & 0xffffffff00000000L) != 0)
throw new IllegalArgumentException (); throw new IllegalArgumentException();
this.comment = comment; this.compressedSize = (int) csize;
this.known |= KNOWN_CSIZE;
} }
/** /**
* Sets the compressedSize of this ZipEntry. * Gets the size of the compressed data.
* The new size must be between 0 and 0xffffffffL. * @return the size or -1 if unknown.
* @since 1.2
*/ */
public void setCompressedSize (long compressedSize) public long getCompressedSize()
{ {
if (compressedSize < 0 || compressedSize > 0xffffffffL) return (known & KNOWN_CSIZE) != 0 ? compressedSize & 0xffffffffL : -1L;
throw new IllegalArgumentException ();
this.compressedSize = compressedSize;
} }
public void setCrc (long crc) /**
* Sets the crc of the uncompressed data.
* @exception IllegalArgumentException if crc is not in 0..0xffffffffL
*/
public void setCrc(long crc)
{ {
if (crc < 0 || crc > 0xffffffffL) if ((crc & 0xffffffff00000000L) != 0)
throw new IllegalArgumentException (); throw new IllegalArgumentException();
this.crc = crc; this.crc = (int) crc;
this.known |= KNOWN_CRC;
} }
public void setExtra (byte[] extra) /**
* Gets the crc of the uncompressed data.
* @return the crc or -1 if unknown.
*/
public long getCrc()
{ {
if (extra != null && extra.length > 65535) return (known & KNOWN_CRC) != 0 ? crc & 0xffffffffL : -1L;
throw new IllegalArgumentException ();
this.extra = extra;
} }
public void setMethod (int method) /**
* 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 != DEFLATED && method != STORED) if (method != ZipOutputStream.STORED
throw new IllegalArgumentException (); && method != ZipOutputStream.DEFLATED)
this.method = method; throw new IllegalArgumentException();
this.method = (short) method;
} }
public void setSize (long size) /**
* Gets the compression method.
* @return the compression method or -1 if unknown.
*/
public int getMethod()
{ {
if (size < 0 || size > 0xffffffffL) return method;
throw new IllegalArgumentException ();
this.size = size;
} }
public void setTime (long time) /**
* Sets the extra data.
* @exception IllegalArgumentException if extra is longer than 0xffff bytes.
*/
public void setExtra(byte[] extra)
{ {
this.time = time; 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;
}
} }
private final static short[] daysToMonthStart = { /**
//Jan Feb Mar Apr May Jun Jul * Gets the extra data.
0, 31, 31+28, 2*31+28, 2*31+28+30, 3*31+28+30, 3*31+28+2*30, * @return the extra data or null if not set.
// 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}; public byte[] getExtra()
{
return extra;
}
/** Convert a DOS-style type value to milliseconds since 1970. */ /**
static long timeFromDOS (int date, int time) * Sets the entry comment.
* @exception IllegalArgumentException if comment is longer than 0xffff.
*/
public void setComment(String comment)
{ {
int sec = 2 * (time & 0x1f); if (comment.length() > 0xffff)
int min = (time >> 5) & 0x3f; throw new IllegalArgumentException();
int hrs = (time >> 11) & 0x1f; this.comment = comment;
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) * Gets the comment.
return -1; * @return the comment or null if not set.
*/
public String getComment()
{
return comment;
}
long mtime = (((hrs * 60) + min) * 60 + sec) * 1000; /**
* Gets true, if the entry is a directory. This is solely
* determined by the name, a trailing slash '/' marks a directory.
*/
public boolean isDirectory()
{
int nlen = name.length();
return nlen > 0 && name.charAt(nlen - 1) == '/';
}
// Leap year calculations are rather trivial in this case ... /**
int days = 365 * year + ((year+1)>>2); * Gets the string representation of this ZipEntry. This is just
days += daysToMonthStart[mon]; * the name as returned by getName().
if ((year & 3) == 0 && mon > 1) */
days++; public String toString()
days += day; {
return (days * 24*60*60L + ((hrs * 60) + min) * 60 + sec) * 1000L; return name;
} }
public String toString () { return name; }
/** /**
* Returns the hashcode of the name of this ZipEntry. * 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 (); } public int hashCode()
{
return name.hashCode();
}
} }
/* 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 ...@@ -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 ...@@ -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.*; import java.io.InputStream;
import java.io.IOException;
/* Written using on-line Java Platform 1.2 API Specification import java.io.EOFException;
* and JCL book. import java.io.RandomAccessFile;
* Believed complete and correct. import java.util.Enumeration;
import java.util.NoSuchElementException;
/**
* This class represents a Zip archive. You can ask for the contained
* entries, or get an input stream for a file entry. The entry is
* automatically decompressed.
*
* 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
{
delete_on_close = null;
} }
this.raf = new RandomAccessFile(file, "r");
this.name = file.getName();
readEntries();
}
file = new RandomAccessFile(f, "r"); /**
name = f.getName(); * Read an unsigned short in little endian byte order.
readDirectory (); * @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;
} }
void readDirectory () throws IOException /**
* 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' if (pos < 0)
|| file.read() != '\005' throw new ZipException
|| file.read() != '\006') ("central directory not found, probably not a zip file");
throw new ZipException("not a valid zipfile"); raf.seek(pos--);
file.skipBytes(6); }
numEntries = readu2(); while (readLeInt() != ENDSIG);
int dir_size = read4 (); // Read "size of the central directory". if (raf.skipBytes(ENDTOT - ENDNRD) != ENDTOT - ENDNRD)
file.seek(size - (dir_size + ZipConstants.END_CENTRAL_DIR_SIZE)); throw new EOFException();
int count = readLeShort();
ZipEntry last = null; if (raf.skipBytes(ENDOFF - ENDSIZ) != ENDOFF - ENDSIZ)
for (int i = 0; i < numEntries; i++) throw new EOFException();
int centralOffset = readLeInt();
entries = new ZipEntry[count];
raf.seek(centralOffset);
for (int i = 0; i < count; i++)
{ {
file.skipBytes(10); if (readLeInt() != CENSIG)
int method = readu2(); throw new ZipException("Wrong Central Directory signature");
int modtime = readu2(); if (raf.skipBytes(CENHOW - CENVEM) != CENHOW - CENVEM)
int moddate = readu2(); throw new EOFException();
int crc = read4(); int method = readLeShort();
int compressedSize = read4(); int dostime = readLeInt();
int uncompressedSize = read4(); int crc = readLeInt();
int filenameLength = readu2(); int csize = readLeInt();
int extraLength = readu2(); int size = readLeInt();
int commentLength = readu2(); int nameLen = readLeShort();
int diskNumberStart = readu2(); int extraLen = readLeShort();
int intAttributes = readu2(); int commentLen = readLeShort();
int extAttributes = read4(); if (raf.skipBytes(CENOFF - CENDSK) != CENOFF - CENDSK)
int relativeOffset = read4(); throw new EOFException();
byte[] bname = new byte[filenameLength]; int offset = readLeInt();
file.readFully(bname);
ZipEntry entry = new ZipEntry(new String(bname, "8859_1")); byte[] buffer = new byte[Math.max(nameLen, commentLen)];
if (extraLength > 0)
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();
}
public ZipEntry getEntry(String name)
{
for (ZipEntry entry = entries; entry != null; entry = entry.next)
{ {
if (name.equals(entry.getName())) raf.close();
return entry;
} }
return null;
} }
public InputStream getInputStream(ZipEntry ze) throws IOException /**
* Returns an enumeration of all Zip entries in this Zip file.
*/
public Enumeration entries()
{ {
byte[] buffer = new byte[(int) ze.getCompressedSize()]; if (entries == null)
throw new IllegalStateException("ZipFile has closed");
/* Read the size of the extra field, and skip to the start of the return new ZipEntryEnumeration(entries);
data. */
file.seek (ze.relativeOffset + ZipConstants.LOCAL_FILE_HEADER_SIZE - 2);
int extraFieldLength = readu2();
file.skipBytes (ze.getName().length() + extraFieldLength);
file.readFully(buffer);
InputStream is = new ByteArrayInputStream (buffer);
if (ze.getMethod() == ZipEntry.DEFLATED)
// Data in zipfile entries does not have a zlib header, so construct
// an Inflater with the `nowrapper' option.
is = new InflaterInputStream (is, new Inflater (true), 512);
return is;
} }
public String getName () private int getEntryIndex(String name)
{ {
return name; for (int i = 0; i < entries.length; i++)
if (name.equals(entries[i].getName()))
return i;
return -1;
} }
/** /**
* Returns the number of entries in this ZipFile. * Searches for a zip entry in this archive with the given name.
* @exception IllegalStateException if the ZipFile has been closed. * @param the name. May contain directory components separated by
* * slashes ('/').
* @since 1.2 * @return the zip entry, or null if no entry with that name exists.
*/ * @see #entries */
public int size () public ZipEntry getEntry(String name)
{ {
if (entries == null) if (entries == null)
throw new IllegalStateException("ZipFile already closed"); throw new IllegalStateException("ZipFile has closed");
else int index = getEntryIndex(name);
return numEntries; return index >= 0 ? (ZipEntry) entries[index].clone() : null;
} }
protected void finalize () 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
{ {
close(); synchronized (raf)
{
raf.seek(entry.offset);
if (readLeInt() != LOCSIG)
throw new ZipException("Wrong Local header signature");
/* skip version and flags */
if (raf.skipBytes(LOCHOW - LOCVER) != LOCHOW - LOCVER)
throw new EOFException();
if (entry.getMethod() != readLeShort())
throw new ZipException("Compression method mismatch");
/* Skip time, crc, size and csize */
if (raf.skipBytes(LOCNAM - LOCTIM) != LOCNAM - LOCTIM)
throw new EOFException();
if (entry.getName().length() != readLeShort())
throw new ZipException("file name length mismatch");
int extraLen = entry.getName().length() + readLeShort();
return entry.offset + LOCHDR + extraLen;
}
} }
private int readu2 () throws IOException /**
* 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
{ {
int byte0 = file.read(); if (entries == null)
int byte1 = file.read(); throw new IllegalStateException("ZipFile has closed");
if (byte0 < 0 || byte1 < 0) int index = entry.zipFileIndex;
throw new ZipException (".zip archive ended prematurely"); if (index < 0 || index >= entries.length
return ((byte1 & 0xFF) << 8) | (byte0 & 0xFF); || entries[index].getName() != entry.getName())
} {
index = getEntryIndex(entry.getName());
if (index < 0)
throw new NoSuchElementException();
}
private int read4 () throws IOException 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()
{ {
int byte0 = file.read(); return name;
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; * Returns the number of entries in this zip file.
RandomAccessFile file; */
String name; public int size()
/** File to delete on close or null. */
File delete_on_close;
}
final class ZipEnumeration implements java.util.Enumeration
{
ZipEntry entry;
ZipEnumeration (ZipFile zfile)
{ {
entry = zfile.entries; try
{
return entries.length;
}
catch (NullPointerException ex)
{
throw new IllegalStateException("ZipFile has closed");
}
} }
public boolean hasMoreElements () private static class ZipEntryEnumeration implements Enumeration
{ {
return entry != null; 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();
}
}
} }
public Object nextElement () private static class PartialInputStream extends InputStream
{ {
ZipEntry cur = entry; RandomAccessFile raf;
if (cur == null) long filepos, end;
throw new java.util.NoSuchElementException();
entry = cur.next; public PartialInputStream(RandomAccessFile raf, long start, long len)
return cur; {
this.raf = raf;
filepos = start;
end = start + len;
}
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();
}
}
public int read(byte[] b, int off, int len) throws IOException
{
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;
}
}
public long skip(long amount)
{
if (amount < 0)
throw new IllegalArgumentException();
if (amount > end - filepos)
amount = end - filepos;
filepos += amount;
return amount;
}
} }
} }
/* 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 ...@@ -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 ...@@ -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
*/ */
public class ZipInputStream extends InflaterInputStream implements ZipConstants
{
private CRC32 crc = new CRC32();
private ZipEntry entry = null;
/* private int csize;
* Written using on-line Java Platform 1.2 API Specification, as well private int size;
* as "The Java Class Libraries", 2nd edition (Addison-Wesley, 1998). private int method;
* Status: Quite incomplete, but can read uncompressed .zip archives. 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 private void fillBuf() throws IOException
{
public ZipInputStream (InputStream in)
{ {
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) if (avail <= 0)
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')
{ {
code = in.read(); fillBuf();
if (code != '\002') if (avail <= 0)
return null; return -1;
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 (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') int count = readBuf(out, off, len);
return null; if (count == -1)
in.skip(16); throw new EOFException();
int comment_size = readu2(); off += count;
in.skip(comment_size); len -= count;
if (in.read() != 'P' || in.read() != 'K')
return null;
code = in.read();
} }
if (code != '\003' }
|| in.read() != '\004')
return null; private final int readLeByte() throws IOException
int ex_version = readu2(); {
current_flags = readu2(); if (avail <= 0)
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]; fillBuf();
readFully(bextra); if (avail <= 0)
entry.extra = bextra; throw new ZipException("EOF in header");
} }
entry.compressedSize = compressedSize; return buf[len - avail--] & 0xff;
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 * Read an unsigned short in little endian byte order.
// back data. */
protected void fill () throws IOException private final int readLeShort() throws IOException
{ {
if (closed) return readLeByte() | (readLeByte() << 8);
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. * Read an int in little endian byte order.
* 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) 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) if (crc == null)
throw new IOException ("stream closed"); throw new IllegalStateException("Closed.");
if (len > avail) if (entry != null)
len = avail; closeEntry();
int count;
if (current.method == Deflater.DEFLATED) int header = readLeInt();
count = super.read(b, off, len); if (header == CENSIG)
else
count = in.read(b, off, len);
if (count == -1 || avail == 0)
{ {
inf.reset(); /* Central Header reached. */
count = -1; close();
return null;
} }
else if (header != LOCSIG)
avail -= count; throw new ZipException("Wrong Local header signature" + Integer.toHexString(header));
return count; /* 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) if (readLeInt() != EXTSIG)
throw new IOException ("stream closed"); throw new ZipException("Data descriptor signature not found");
if (n > avail) entry.setCrc(readLeInt() & 0xffffffffL);
n = avail; csize = readLeInt();
long count; size = readLeInt();
if (current.method == Deflater.DEFLATED) entry.setSize(size & 0xffffffffL);
count = super.skip(n); entry.setCompressedSize(csize & 0xffffffffL);
else
count = in.skip(n);
avail = avail - (int) count;
return count;
} }
/** /**
* Returns 0 if the ZipInputStream is closed and 1 otherwise. * Closes the current zip entry and moves to the next one.
*
* @since 1.2
*/ */
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 if (method == ZipOutputStream.DEFLATED)
{
int off = 0;
int len = b.length;
while (len > 0)
{ {
int count = in.read(b, off, len); if ((flags & 8) != 0)
if (count <= 0) {
throw new EOFException(".zip archive ended prematurely"); /* We don't know how much we must skip, read until end. */
off += count; byte[] tmp = new byte[2048];
len -= count; 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(); return entry != null ? 1 : 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 /**
* 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(); byte[] b = new byte[1];
int byte1 = in.read(); if (read(b, 0, 1) <= 0)
int byte2 = in.read(); return -1;
int byte3 = in.read(); return b[0] & 0xff;
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 /**
* 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) case ZipOutputStream.DEFLATED:
skip (avail); len = super.read(b, off, len);
if ((current_flags & 8) != 0) if (len < 0)
{ {
int sig = read4(); if (!inf.finished())
if (sig != 0x04034b50) throw new ZipException("Inflater not finished!?");
throw new ZipException("bad/missing magic number at end of .zip entry"); avail = inf.getRemaining();
int crc = read4(); if ((flags & 8) != 0)
int compressedSize = read4(); readDataDescr();
int uncompressedSize = read4();
if (current.compressedSize != compressedSize if (inf.getTotalIn() != csize
|| current.size != uncompressedSize || inf.getTotalOut() != size)
|| current.crc != crc) throw new ZipException("size mismatch: "+csize+";"+size+" <-> "+inf.getTotalIn()+";"+inf.getTotalOut());
throw new ZipException("bad data descriptor at end of .zip entry"); inf.reset();
finished = true;
} }
current = null; break;
avail = 0;
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. * Closes the zip file.
* * @exception IOException if a i/o error occured.
* @since 1.2
*/ */
public void close () throws IOException public void close() throws IOException
{ {
current = null;
closed = true;
super.close(); super.close();
crc = null;
entry = null;
} }
private ZipEntry current; /**
private int current_flags; * Creates a new zip entry for the given name. This is equivalent
// Number of uncompressed bytes to be read. * to new ZipEntry(name).
private int avail; * @param name the name of the zip entry.
// Number of bytes we can read from underlying stream. */
private int compressed_bytes; protected ZipEntry createZipEntry(String name)
// Is this ZipInputStream closed? Set by the close() method. {
private boolean closed = false; return new ZipEntry(name);
}
} }
/* 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 ...@@ -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 ...@@ -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.*; import java.io.IOException;
import java.io.UnsupportedEncodingException;
/* Written using on-line Java Platform 1.2 API Specification import java.util.Vector;
* and JCL book. import java.util.Enumeration;
* Believed complete and correct.
/**
* This is a FilterOutputStream that writes the files into a zip
* archive one after another. It has a special method to start a new
* zip entry. The zip entries contains information about the file name
* 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();
}
long crc = sum.getValue();
bytes_written += compressed_size;
if (current.getCrc() == -1 || current.getCompressedSize() == -1
|| current.getSize() == -1)
{
current.setCrc(crc);
current.compressedSize = compressed_size;
current.setSize(uncompressed_size);
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 method. If the Zip entry specifies
* another method its method takes precedence.
* @param method the method.
* @exception IllegalArgumentException if method is not supported.
* @see #STORED
* @see #DEFLATED
*/
public void setMethod(int method)
{ {
if (current.method == STORED) if (method != STORED && method != DEFLATED)
{ throw new IllegalArgumentException("Method not supported.");
out.write(bval); defaultMethod = method;
}
else
super.write(bval);
sum.update(bval);
uncompressed_size += 1;
} }
public void write (byte[] buf, int off, int len) 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(buf, off, len);
else
super.write(buf, off, len);
sum.update(buf, off, len);
uncompressed_size += len;
} }
public void finish () throws IOException /**
* Write an unsigned short in little endian byte order.
*/
private final void writeLeShort(int value) throws IOException
{ {
if (current != null) out.write(value & 0xff);
closeEntry (); out.write((value >> 8) & 0xff);
}
// Write the central directory.
long offset = bytes_written;
int count = 0;
int bytes = 0;
while (chain != null)
{
bytes += write_entry (chain, false);
++count;
chain = chain.next;
}
// Write the end of the central directory record. /**
put4 (0x06054b50); * Write an int in little endian byte order.
// Disk number. */
put2 (0); private final void writeLeInt(int value) throws IOException
// Another disk number. {
put2 (0); writeLeShort(value);
put2 (count); writeLeShort(value >> 16);
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) * Starts a new Zip entry. It automatically closes the previous
throws IOException * 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
{ {
int bytes = put4 (is_local ? 0x04034b50 : 0x02014b50); if (entries == null)
if (! is_local) throw new IllegalStateException("ZipOutputStream was finished");
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"); int method = entry.getMethod();
bytes += put2 (name.length); int flags = 0;
bytes += put2 (entry.extra == null ? 0 : entry.extra.length); if (method == -1)
method = defaultMethod;
byte[] comment = null; if (method == STORED)
if (! is_local)
{ {
if (entry.getComment() == null) if (entry.getCompressedSize() >= 0)
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
{ {
out.write(comment); writeLeInt(0);
bytes += comment.length; writeLeInt(0);
writeLeInt(0);
} }
byte[] name = entry.getName().getBytes();
bytes_written += bytes; if (name.length > 0xffff)
return bytes; 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;
} }
public void putNextEntry (ZipEntry entry) throws IOException /**
* 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 (current != null) if (curEntry == null)
closeEntry (); throw new IllegalStateException("No open entry");
if (entry.method < 0 ) /* First finish the deflater, if appropriate */
entry.method = method; if (curMethod == DEFLATED)
if (entry.method == STORED) 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)
{ {
if (entry.getSize() == -1 || entry.getCrc() == -1) writeLeInt(EXTSIG);
throw new ZipException ("required entry not set"); writeLeInt((int)curEntry.getCrc());
// Just in case. writeLeInt((int)curEntry.getCompressedSize());
entry.compressedSize = entry.getSize(); writeLeInt((int)curEntry.getSize());
offset += EXTHDR;
} }
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) entries.addElement(curEntry);
{ curEntry = null;
if (level != Deflater.DEFAULT_COMPRESSION
&& (level < Deflater.NO_COMPRESSION
|| level > Deflater.BEST_COMPRESSION))
throw new IllegalArgumentException ();
this.level = level;
} }
public void setMethod (int method) /**
* 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 (method != DEFLATED && method != STORED) if (curEntry == null)
throw new IllegalArgumentException (); throw new IllegalStateException("No open entry.");
this.method = method;
}
public void setComment (String comment)
{
if (comment.length() > 65535)
throw new IllegalArgumentException ();
this.comment = comment;
}
public ZipOutputStream (OutputStream out) switch (curMethod)
{ {
super (out, new Deflater (Deflater.DEFAULT_COMPRESSION, true), 8192); case DEFLATED:
sum = new CRC32 (); super.write(b, off, len);
} break;
case STORED:
out.write(b, off, len);
break;
}
private int put2 (int i) throws IOException crc.update(b, off, len);
{ size += len;
out.write (i);
out.write (i >> 8);
return 2;
} }
private int put4 (int i) throws IOException /**
* 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
{ {
out.write (i); if (entries == null)
out.write (i >> 8); return;
out.write (i >> 16); if (curEntry != null)
out.write (i >> 24); closeEntry();
return 4;
} 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);
numEntries++;
sizeEntries += CENHDR + name.length + extra.length + comment.length;
}
private int put_version () throws IOException writeLeInt(ENDSIG);
{ writeLeShort(0); /* disk number */
// FIXME: for now we assume Unix, and we ignore the version writeLeShort(0); /* disk with start of central dir */
// number. writeLeShort(numEntries);
return put2 (3 << 8); writeLeShort(numEntries);
writeLeInt(sizeEntries);
writeLeInt(offset);
writeLeShort(zipComment.length);
out.write(zipComment);
out.flush();
entries = null;
} }
// 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;
} }
// 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 () ...@@ -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
......
// 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 () ...@@ -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
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment