// package net.sf.zipme; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.EOFException; import java.io.IOException; import java.io.InputStream; import java.io.UnsupportedEncodingException; import java.util.Enumeration; import java.util.Hashtable; import java.io.File; import java.io.FileInputStream; /** * 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 * @author Artur Biesiadowski */ public class ZipArchive implements ZipConstants { /** * This field isn't defined in the JDK's ZipConstants, but should be. */ static final int ENDNRD=4; private byte[] buf; private int off; private int len; private Hashtable entries; /** * Opens a Zip archive reading the given byte array. * @exception ZipException if the byte array doesn't contain a valid * zip archive. */ public ZipArchive( byte[] b) throws ZipException { this(b,0,b.length); } /** * Opens a Zip archive reading the given byte array. * @exception ZipException if the byte array doesn't contain a valid * zip archive. */ public ZipArchive( byte[] b, int off, int len) throws ZipException { if (off < 0 || len < 0 || off > b.length) throw new IllegalArgumentException(); buf=b; this.off=off; this.len=len; if (this.len > buf.length - this.off) { this.len=buf.length - this.off; } hook1(); } public void hook1() throws ZipException { } /** * Opens a Zip archive reading the given InputStream. * @exception IOException if a i/o error occured. * @exception ZipException if the stream doesn't contain a valid zip * archive. */ public ZipArchive( InputStream in) throws ZipException, IOException { ByteArrayOutputStream out=new ByteArrayOutputStream(); byte[] b=new byte[1024]; int l; while ((l=in.read(b)) > 0) { out.write(b,0,l); } buf=out.toByteArray(); off=0; len=buf.length; hook1(); } /** * Read the central directory of a zip archive and fill the entries * array. This is called exactly once when first needed. It is called * while holding the lock on <code>raf</code>. * @exception IOException if a i/o error occured. * @exception ZipException if the central directory is malformed */ private void readEntries() throws ZipException, IOException { ZipArchive_PartialInputStream inp=new ZipArchive_PartialInputStream(buf,off,len); int pos=len - ENDHDR; int top=Math.max(0,pos - 65536); do { if (pos < top) throw new ZipException("central directory not found, probably not a zip archive"); inp.seek(off + pos--); } while (inp.readLeInt() != ENDSIG); if (inp.skip(ENDTOT - ENDNRD) != ENDTOT - ENDNRD) throw new EOFException(); int count=inp.readLeShort(); if (inp.skip(ENDOFF - ENDSIZ) != ENDOFF - ENDSIZ) throw new EOFException(); int centralOffset=inp.readLeInt(); entries=new Hashtable(count + count / 2); inp.seek(off + centralOffset); for (int i=0; i < count; i++) { if (inp.readLeInt() != CENSIG) throw new ZipException("Wrong Central Directory signature"); inp.skip(6); int method=inp.readLeShort(); int dostime=inp.readLeInt(); int crc=inp.readLeInt(); int csize=inp.readLeInt(); int size=inp.readLeInt(); int nameLen=inp.readLeShort(); int extraLen=inp.readLeShort(); int commentLen=inp.readLeShort(); inp.skip(8); int offset=inp.readLeInt(); String name=inp.readString(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[] extra=new byte[extraLen]; inp.readFully(extra); entry.setExtra(extra); } if (commentLen > 0) { entry.setComment(inp.readString(commentLen)); } entry.offset=offset; entries.put(name,entry); } } /** * Returns an enumeration of all Zip entries in this Zip archive. */ public Enumeration entries(){ try { return getEntries().elements(); } catch ( IOException ioe) { return (new Hashtable()).elements(); } } /** * Reads entries when necessary. * @exception IOException when the entries could not be read. */ private Hashtable getEntries() throws IOException { if (entries == null) readEntries(); return entries; } /** * Searches for a zip entry in this archive with the given name. * @param name the name. May contain directory components separated by * slashes ('/'). * @return the zip entry, or null if no entry with that name exists. */ public ZipEntry getEntry( String name){ try { Hashtable entries=getEntries(); ZipEntry entry=(ZipEntry)entries.get(name); if (entry == null && !name.endsWith("/")) entry=(ZipEntry)entries.get(name + '/'); return entry != null ? new ZipEntry(entry,name) : null; } catch ( IOException ioe) { return null; } } /** * Returns the number of entries in this zip archive. */ public int size(){ try { return getEntries().size(); } catch ( IOException ioe) { return 0; } } }