home *** CD-ROM | disk | FTP | other *** search
/ DOS/V Power Report 1998 February / VPR9802A.ISO / APP_DEMO / VC / MAIN.BIN / ZipFile.java < prev    next >
Text File  |  1997-10-27  |  12KB  |  418 lines

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