home *** CD-ROM | disk | FTP | other *** search
/ Java 1.2 How-To / JavaHowTo.iso / 3rdParty / jbuilder / unsupported / JDK1.2beta3 / SOURCE / SRC.ZIP / java / util / zip / ZipFile.java < prev    next >
Encoding:
Java Source  |  1998-03-20  |  12.1 KB  |  434 lines

  1. /*
  2.  * @(#)ZipFile.java    1.22 98/03/18
  3.  *
  4.  * Copyright 1995-1997 by Sun Microsystems, Inc.,
  5.  * 901 San Antonio Road, Palo Alto, California, 94303, U.S.A.
  6.  * All rights reserved.
  7.  *
  8.  * This software is the confidential and proprietary information
  9.  * of Sun Microsystems, Inc. ("Confidential Information").  You
  10.  * shall not disclose such Confidential Information and shall use
  11.  * it only in accordance with the terms of the license agreement
  12.  * you entered into with Sun.
  13.  */
  14.  
  15. package java.util.zip;
  16.  
  17. import java.io.RandomAccessFile;
  18. import java.io.IOException;
  19. import java.io.File;
  20. import java.io.InputStream;
  21. import java.util.Hashtable;
  22. import java.util.Enumeration;
  23.  
  24. /**
  25.  * This class can be used to read the contents of a ZIP file. It uses
  26.  * RandomAccessFile for quick access to ZIP file entries, and supports
  27.  * both compressed and uncompressed entries.
  28.  
  29.  * @version    1.22, 03/18/98
  30.  * @author    David Connelly
  31.  */
  32. public
  33. class ZipFile implements ZipConstants {
  34.     RandomAccessFile raf;
  35.     private String name;
  36.     private Hashtable entries;
  37.     long cenpos;
  38.     private long endpos;
  39.     long pos;
  40.  
  41.     private static final int STORED = ZipEntry.STORED;
  42.     private static final int DEFLATED = ZipEntry.DEFLATED;
  43.  
  44.     /**
  45.      * Creates a ne w ZIP file with no name.
  46.      */
  47.     protected ZipFile() {
  48.     }
  49.  
  50.     /**
  51.      * Opens a ZIP file for reading given the specified file name.
  52.      * @param name the name of the zip file
  53.      * @exception ZipException if a ZIP format error has occurred
  54.      * @exception IOException if an I/O error has occurred
  55.      */
  56.     public ZipFile(String name) throws IOException {
  57.     raf = new RandomAccessFile(name, "r");
  58.     this.name = name;
  59.     readCEN();
  60.     }
  61.  
  62.     /**
  63.      * Opens a ZIP file for reading given the specified File object.
  64.      * @param file the ZIP file to be opened for reading
  65.      * @exception ZipException if a ZIP error has occurred
  66.      * @exception IOException if an I/O error has occurred
  67.      */
  68.     public ZipFile(File file) throws ZipException, IOException {
  69.     this(file.getPath());
  70.     }
  71.  
  72.     /**
  73.      * Returns the ZIP file entry for the given path name. Returns null if
  74.      * there is no entry corresponding to the given name.
  75.      * @param name the name of the entry
  76.      * @return the ZIP file entry
  77.      */
  78.     public ZipEntry getEntry(String name) {
  79.     return (ZipEntry)entries.get(name);
  80.     }
  81.  
  82.     /**
  83.      * Returns an input stream for reading the contents of the specified
  84.      * ZIP file entry.
  85.      * @param ze the zip file entry
  86.      * @exception ZipException if a ZIP format error has occurred
  87.      * @exception IOException if an I/O error has occurred
  88.      */
  89.     public InputStream getInputStream(ZipEntry ze) throws IOException {
  90.     InputStream in = new ZipFileInputStream(this, ze);
  91.     switch (ze.method) {
  92.     case STORED:
  93.         return in;
  94.     case DEFLATED:
  95.         return new InflaterInputStream(in, new Inflater(true));
  96.     default:
  97.         throw new ZipException("invalid compression method");
  98.     }
  99.     }
  100.  
  101.     /**
  102.      * Returns the path name of the ZIP file.
  103.      */
  104.     public String getName() {
  105.         return name;
  106.     }
  107.  
  108.     /**
  109.      * Returns an enumeration of the ZIP file entries.
  110.      */
  111.     public Enumeration entries() {
  112.     return entries.elements();
  113.     }
  114.  
  115.     /**
  116.      * Returns the number of entry in the ZIP file.
  117.      */
  118.     public int size() {
  119.     return entries.size();
  120.     }
  121.  
  122.     /**
  123.      * Closes the ZIP file.
  124.      */
  125.     public void close() throws IOException {
  126.     if (raf != null) {
  127.         raf.close();
  128.         raf = null;
  129.     }
  130.     }
  131.  
  132.     /*
  133.      * Reads data at specified file position into an array of bytes.
  134.      * This method will block until some input is available.
  135.      */
  136.     synchronized int read(long pos, byte b[], int off, int len)
  137.     throws IOException
  138.     {
  139.         if (pos != this.pos) {
  140.         raf.seek(pos);
  141.     }
  142.     int n = raf.read(b, off, len);
  143.     if (n > 0) {
  144.         this.pos = pos + n;
  145.     }
  146.     return n;
  147.     }
  148.  
  149.     /*
  150.      * Reads a byte of data at the specified file position. This method
  151.      * will block until some input is available.
  152.      */
  153.     synchronized int read(long pos) throws IOException {
  154.     if (pos != this.pos) {
  155.         raf.seek(pos);
  156.     }
  157.     int n = raf.read();
  158.     if (n > 0) {
  159.         this.pos = pos + 1;
  160.     }
  161.     return n;
  162.     }
  163.  
  164.     /*
  165.      * Read contents of central directory (CEN) and build hash table of
  166.      * ZIP file entries.
  167.      */
  168.     private void readCEN() throws IOException {
  169.     // Find and seek to beginning of END header
  170.     findEND();
  171.     // Read END header and check signature
  172.     byte[] endbuf = new byte[ENDHDR];
  173.     raf.readFully(endbuf);
  174.     if (get32(endbuf, 0) != ENDSIG) {
  175.         throw new ZipException("invalid END header signature"); 
  176.     }
  177.     // Get position and length of central directory
  178.     cenpos = get32(endbuf, ENDOFF);
  179.     int cenlen = (int)get32(endbuf, ENDSIZ);
  180.     if (cenpos + cenlen != endpos) {
  181.         throw new ZipException("invalid END header format");
  182.     }
  183.     // Get total number of entries
  184.     int nent = get16(endbuf, ENDTOT);
  185.     if (nent * CENHDR > cenlen) {
  186.         throw new ZipException("invalid END header format");
  187.     }
  188.     // Check number of drives
  189.     if (get16(endbuf, ENDSUB) != nent) {
  190.         throw new ZipException("cannot have more than one drive");
  191.     }
  192.     // Seek to first CEN record and read central directory
  193.     raf.seek(cenpos);
  194.     byte cenbuf[] = new byte[cenlen];
  195.     raf.readFully(cenbuf);
  196.     // Scan entries in central directory and build lookup table.
  197.     entries = new Hashtable(nent);
  198.     for (int off = 0; off < cenlen; ) {
  199.         int cenoff = off;
  200.         // Check CEN header signature
  201.         if (get32(cenbuf, cenoff) != CENSIG) {
  202.         throw new ZipException("invalid CEN header signature");
  203.         }
  204.         // Get path name of entry and create ZipEntry object
  205.         int len = get16(cenbuf, cenoff + CENNAM);
  206.         off += CENHDR;
  207.         if (len == 0 || off + len > cenlen) {
  208.         throw new ZipException("invalid CEN entry name");
  209.         }
  210.         String name = new String(cenbuf, 0, off, len);
  211.         off += len;
  212.         ZipEntry e = createZipEntry(name);
  213.         // Now get remaining fields for entry
  214.         e.version = get16(cenbuf, cenoff + CENVER);
  215.         e.flag    = get16(cenbuf, cenoff + CENFLG);
  216.         e.method  = get16(cenbuf, cenoff + CENHOW);
  217.         e.time    = get32(cenbuf, cenoff + CENTIM);
  218.         e.crc     = get32(cenbuf, cenoff + CENCRC);
  219.         e.size    = get32(cenbuf, cenoff + CENLEN);
  220.         e.csize   = get32(cenbuf, cenoff + CENSIZ);
  221.         e.offset  = get32(cenbuf, cenoff + CENOFF);
  222.         if (e.offset + e.csize > cenpos) {
  223.         throw new ZipException("invalid CEN entry size");
  224.         }
  225.         // Get extra field data
  226.         len = get16(cenbuf, cenoff + CENEXT);
  227.         if (len > 0) {
  228.         if (off + len > cenlen) {
  229.             throw new ZipException("invalid CEN entry extra data");
  230.         }
  231.         e.extra = new byte[len];
  232.         System.arraycopy(cenbuf, off, e.extra, 0, len);
  233.         off += len;
  234.         }
  235.         // Get entry comment
  236.         len = get16(cenbuf, cenoff + CENCOM);
  237.         if (len > 0) {
  238.         if (off + len > cenlen) {
  239.             throw new ZipException("invalid CEN entry comment");
  240.         }
  241.         e.comment = new String(cenbuf, 0, off, len);
  242.         off += len;
  243.         }
  244.         // Add entry to the hash table of entries
  245.         entries.put(e.name, e);
  246.     }
  247.     // Make sure we got the right number of entries
  248.     if (entries.size() != nent) {
  249.         throw new ZipException("invalid CEN header format");
  250.     }
  251.     }
  252.  
  253.     /**
  254.      * Creates a new <code>ZipEntry</code> object for the specified
  255.      * entry name.
  256.      *
  257.      * @param name the ZIP file entry name
  258.      */
  259.     protected ZipEntry createZipEntry(String name) {
  260.     return new ZipEntry(name);
  261.     }
  262.  
  263.     private static final int INBUFSIZ = 64;
  264.  
  265.     /*
  266.      * Find end of central directory (END) header.
  267.      */
  268.     private void findEND() throws IOException {
  269.     // Start searching backwards from end of file
  270.     long len = raf.length();
  271.     raf.seek(len);
  272.     // Set limit on how far back we need to search. The END header
  273.     // must be located within the last 64K bytes of the raf.
  274.     long markpos = Math.max(0, len - 0xffff);
  275.     // Search backwards INBUFSIZ bytes at a time from end of file
  276.     // stopping when the END header signature has been found. Since
  277.     // the signature may straddle a buffer boundary, we need to stash
  278.     // the first 4-1 bytes of the previous record at the end of
  279.     // the current record so that the search may overlap.
  280.     byte buf[] = new byte[INBUFSIZ + 4];
  281.     for (pos = len; pos > markpos; ) {
  282.         int n = Math.min((int)(pos - markpos), INBUFSIZ);
  283.         pos -= n;
  284.         raf.seek(pos);
  285.         raf.readFully(buf, 0, n);
  286.         while (--n > 0) {
  287.         if (get32(buf, n) == ENDSIG) {
  288.             // Could be END header, but we need to make sure that
  289.             // the record extends to the end of the raf.
  290.             endpos = pos + n;
  291.             if (len - endpos < ENDHDR) {
  292.             continue;
  293.             }
  294.             raf.seek(endpos);
  295.             byte endbuf[] = new byte[ENDHDR];
  296.             raf.readFully(endbuf);
  297.             int comlen = get16(endbuf, ENDCOM);
  298.             if (endpos + ENDHDR + comlen != len) {
  299.             continue;
  300.             }
  301.             // This is definitely the END record, so position
  302.             // the file pointer at the header and return.
  303.             raf.seek(endpos);
  304.             pos = endpos;
  305.             return;
  306.         }
  307.         }
  308.     }
  309.     throw new ZipException("not a ZIP file (END header not found)");
  310.     }
  311.  
  312.     /*
  313.      * Fetch unsigned 16-bit value from byte array at specified offset.
  314.      * The bytes are assumed to be in Intel (little-endian) byte order.
  315.      */
  316.     static final int get16(byte b[], int off) {
  317.     return (b[off] & 0xff) | ((b[off+1] & 0xff) << 8);
  318.     }
  319.  
  320.     /*
  321.      * Fetch unsigned 32-bit value from byte array at specified offset.
  322.      * The bytes are assumed to be in Intel (little-endian) byte order.
  323.      */
  324.     static final long get32(byte b[], int off) {
  325.     return get16(b, off) | ((long)get16(b, off+2) << 16);
  326.     }
  327. }
  328.  
  329. class ZipFileInputStream extends InputStream implements ZipConstants {
  330.     private ZipFile zf;
  331.     private ZipEntry ze;
  332.     private long pos;
  333.     private long count;
  334.  
  335.     /*
  336.      * Creates an input stream for reading the specified ZIP file entries
  337.      * raw data.
  338.      */
  339.     ZipFileInputStream(ZipFile zf, ZipEntry ze) throws IOException {
  340.     this.zf = zf;
  341.     this.ze = ze;
  342.     readLOC();
  343.     }
  344.  
  345.     /**
  346.      * Returns number of bytes available for reading.
  347.      */
  348.     public int available() {
  349.     return (int)Math.min(count, Integer.MAX_VALUE);
  350.     }
  351.  
  352.     /**
  353.      * Reads ZIP file entry into an array of bytes. This method will
  354.      * block until some input is available.
  355.      * @param b the buffer into which the data is read
  356.      * @param off the start offset of the data
  357.      * @param len the maximum number of bytes to read
  358.      * @return the actual number of bytes read, or -1 if the end of
  359.      *            the stream has been reached.
  360.      * @exception ZipException if a ZIP format error has occurred
  361.      * @exception IOException if an I/O error has occurred
  362.      */
  363.     public int read(byte b[], int off, int len) throws IOException {
  364.         if (count == 0) {
  365.         return -1;
  366.     }
  367.     if (len > count) {
  368.         len = (int)Math.min(count, Integer.MAX_VALUE);
  369.     }
  370.     len = zf.read(pos, b, off, len);
  371.     if (len == -1) {
  372.         throw new ZipException("premature EOF");
  373.     }
  374.     pos += len;
  375.     count -= len;
  376.     return len;
  377.     }
  378.  
  379.     /**
  380.      * Reads a byte of data. This method will block until some input
  381.      * is available.
  382.      * @return the byte read, or -1 if the end of the stream has been
  383.      *           reached.
  384.      * @exception ZipException if a ZIP format error has occurred
  385.      * @exception IOException if an I/O error has occurred
  386.      */
  387.     public int read() throws IOException {
  388.     if (count == 0) {
  389.         return -1;
  390.     }
  391.     int n = zf.read(pos);
  392.     if (n == -1) {
  393.         throw new ZipException("premature EOF");
  394.     }
  395.     pos += 1;
  396.     count -= 1;
  397.     return n;
  398.     }
  399.  
  400.     /**
  401.      * Skips n bytes of input.
  402.      * @param n    the number of bytes to skip
  403.      * @return the actual number of bytes skipped
  404.      */
  405.     public long skip(long n) {
  406.     if (n > count) {
  407.         n = count;
  408.     }
  409.     pos += n;
  410.     count -= n;
  411.     return n;
  412.     }
  413.  
  414.     /*
  415.      * Read and verify LOC header, and position input stream at beginning of
  416.      * entry data.
  417.      */
  418.     private void readLOC() throws IOException {
  419.     // Read LOC header and check signature
  420.     byte locbuf[] = new byte[LOCHDR];
  421.     zf.read(ze.offset, locbuf, 0, LOCHDR);
  422.     if (zf.get32(locbuf, 0) != LOCSIG) {
  423.         throw new ZipException("invalid LOC header signature");
  424.     }
  425.     // Get length and position of entry data
  426.     count = ze.csize;
  427.     pos = ze.offset + LOCHDR + ZipFile.get16(locbuf, LOCNAM) +
  428.                    ZipFile.get16(locbuf, LOCEXT);
  429.     if (pos + count > zf.cenpos) {
  430.         throw new ZipException("invalid LOC header format");
  431.     }
  432.     }
  433. }
  434.