mirror of git://gcc.gnu.org/git/gcc.git
				
				
				
			
		
			
				
	
	
		
			786 lines
		
	
	
		
			22 KiB
		
	
	
	
		
			Java
		
	
	
	
			
		
		
	
	
			786 lines
		
	
	
		
			22 KiB
		
	
	
	
		
			Java
		
	
	
	
/* ZipFile.java --
 | 
						|
   Copyright (C) 2001, 2002, 2003, 2004, 2005, 2006
 | 
						|
   Free Software Foundation, Inc.
 | 
						|
 | 
						|
This file is part of GNU Classpath.
 | 
						|
 | 
						|
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
 | 
						|
the Free Software Foundation; either version 2, or (at your option)
 | 
						|
any later version.
 | 
						|
 | 
						|
GNU Classpath is distributed in the hope that it will be useful, but
 | 
						|
WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
						|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 | 
						|
General Public License for more details.
 | 
						|
 | 
						|
You should have received a copy of the GNU General Public License
 | 
						|
along with GNU Classpath; see the file COPYING.  If not, write to the
 | 
						|
Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
 | 
						|
02110-1301 USA.
 | 
						|
 | 
						|
Linking this library statically or dynamically with other modules is
 | 
						|
making a combined work based on this library.  Thus, the terms and
 | 
						|
conditions of the GNU General Public License cover the whole
 | 
						|
combination.
 | 
						|
 | 
						|
As a special exception, the copyright holders of this library give you
 | 
						|
permission to link this library with independent modules to produce an
 | 
						|
executable, regardless of the license terms of these independent
 | 
						|
modules, and to copy and distribute the resulting executable under
 | 
						|
terms of your choice, provided that you also meet, for each linked
 | 
						|
independent module, the terms and conditions of the license of that
 | 
						|
module.  An independent module is a module which is not derived from
 | 
						|
or based on this library.  If you modify this library, you may extend
 | 
						|
this exception to your version of the library, but you are not
 | 
						|
obligated to do so.  If you do not wish to do so, delete this
 | 
						|
exception statement from your version. */
 | 
						|
 | 
						|
 | 
						|
package java.util.zip;
 | 
						|
 | 
						|
import gnu.java.util.EmptyEnumeration;
 | 
						|
 | 
						|
import java.io.EOFException;
 | 
						|
import java.io.File;
 | 
						|
import java.io.FileNotFoundException;
 | 
						|
import java.io.IOException;
 | 
						|
import java.io.InputStream;
 | 
						|
import java.io.RandomAccessFile;
 | 
						|
import java.io.UnsupportedEncodingException;
 | 
						|
import java.nio.ByteBuffer;
 | 
						|
import java.nio.charset.Charset;
 | 
						|
import java.nio.charset.CharsetDecoder;
 | 
						|
import java.util.Enumeration;
 | 
						|
import java.util.Iterator;
 | 
						|
import java.util.LinkedHashMap;
 | 
						|
 | 
						|
/**
 | 
						|
 * 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 ZipFile implements ZipConstants
 | 
						|
{
 | 
						|
 | 
						|
  /**
 | 
						|
   * 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;
 | 
						|
 | 
						|
  /**
 | 
						|
   * This field isn't defined in the JDK's ZipConstants, but should be.
 | 
						|
   */
 | 
						|
  static final int ENDNRD =  4;
 | 
						|
 | 
						|
  // Name of this zip file.
 | 
						|
  private final String name;
 | 
						|
 | 
						|
  // File from which zip entries are read.
 | 
						|
  private final RandomAccessFile raf;
 | 
						|
 | 
						|
  // The entries of this zip file when initialized and not yet closed.
 | 
						|
  private LinkedHashMap<String, ZipEntry> entries;
 | 
						|
 | 
						|
  private boolean closed = false;
 | 
						|
 | 
						|
 | 
						|
  /**
 | 
						|
   * Helper function to open RandomAccessFile and throw the proper
 | 
						|
   * ZipException in case opening the file fails.
 | 
						|
   *
 | 
						|
   * @param name the file name, or null if file is provided
 | 
						|
   *
 | 
						|
   * @param file the file, or null if name is provided
 | 
						|
   *
 | 
						|
   * @return the newly open RandomAccessFile, never null
 | 
						|
   */
 | 
						|
  private RandomAccessFile openFile(String name,
 | 
						|
                                    File file)
 | 
						|
    throws ZipException, IOException
 | 
						|
  {
 | 
						|
    try
 | 
						|
      {
 | 
						|
        return
 | 
						|
          (name != null)
 | 
						|
          ? new RandomAccessFile(name, "r")
 | 
						|
          : new RandomAccessFile(file, "r");
 | 
						|
      }
 | 
						|
    catch (FileNotFoundException f)
 | 
						|
      {
 | 
						|
        ZipException ze = new ZipException(f.getMessage());
 | 
						|
        ze.initCause(f);
 | 
						|
        throw ze;
 | 
						|
      }
 | 
						|
  }
 | 
						|
 | 
						|
 | 
						|
  /**
 | 
						|
   * 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.raf = openFile(name,null);
 | 
						|
    this.name = name;
 | 
						|
    checkZipFile();
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * 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.raf = openFile(null,file);
 | 
						|
    this.name = file.getPath();
 | 
						|
    checkZipFile();
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * 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.
 | 
						|
   *
 | 
						|
   * @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("invalid mode");
 | 
						|
    if ((mode & OPEN_DELETE) != 0)
 | 
						|
      file.deleteOnExit();
 | 
						|
    this.raf = openFile(null,file);
 | 
						|
    this.name = file.getPath();
 | 
						|
    checkZipFile();
 | 
						|
  }
 | 
						|
 | 
						|
  private void checkZipFile() throws ZipException
 | 
						|
  {
 | 
						|
    boolean valid = false;
 | 
						|
 | 
						|
    try
 | 
						|
      {
 | 
						|
        byte[] buf = new byte[4];
 | 
						|
        raf.readFully(buf);
 | 
						|
        int sig = buf[0] & 0xFF
 | 
						|
                | ((buf[1] & 0xFF) << 8)
 | 
						|
                | ((buf[2] & 0xFF) << 16)
 | 
						|
                | ((buf[3] & 0xFF) << 24);
 | 
						|
        valid = sig == LOCSIG;
 | 
						|
      }
 | 
						|
    catch (IOException _)
 | 
						|
      {
 | 
						|
      }
 | 
						|
 | 
						|
    if (!valid)
 | 
						|
      {
 | 
						|
        try
 | 
						|
          {
 | 
						|
            raf.close();
 | 
						|
          }
 | 
						|
        catch (IOException _)
 | 
						|
          {
 | 
						|
          }
 | 
						|
        throw new ZipException("Not a valid zip file");
 | 
						|
      }
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * Checks if file is closed and throws an exception.
 | 
						|
   */
 | 
						|
  private void checkClosed()
 | 
						|
  {
 | 
						|
    if (closed)
 | 
						|
      throw new IllegalStateException("ZipFile has closed: " + name);
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * Read the central directory of a zip file 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
 | 
						|
  {
 | 
						|
    /* Search for the End Of Central Directory.  When a zip comment is
 | 
						|
     * present the directory may start earlier.
 | 
						|
     * Note that a comment has a maximum length of 64K, so that is the
 | 
						|
     * maximum we search backwards.
 | 
						|
     */
 | 
						|
    PartialInputStream inp = new PartialInputStream(raf, 4096);
 | 
						|
    long pos = raf.length() - ENDHDR;
 | 
						|
    long top = Math.max(0, pos - 65536);
 | 
						|
    do
 | 
						|
      {
 | 
						|
        if (pos < top)
 | 
						|
          throw new ZipException
 | 
						|
            ("central directory not found, probably not a zip file: " + name);
 | 
						|
        inp.seek(pos--);
 | 
						|
      }
 | 
						|
    while (inp.readLeInt() != ENDSIG);
 | 
						|
 | 
						|
    if (inp.skip(ENDTOT - ENDNRD) != ENDTOT - ENDNRD)
 | 
						|
      throw new EOFException(name);
 | 
						|
    int count = inp.readLeShort();
 | 
						|
    if (inp.skip(ENDOFF - ENDSIZ) != ENDOFF - ENDSIZ)
 | 
						|
      throw new EOFException(name);
 | 
						|
    int centralOffset = inp.readLeInt();
 | 
						|
 | 
						|
    entries = new LinkedHashMap<String, ZipEntry> (count+count/2);
 | 
						|
    inp.seek(centralOffset);
 | 
						|
 | 
						|
    for (int i = 0; i < count; i++)
 | 
						|
      {
 | 
						|
        if (inp.readLeInt() != CENSIG)
 | 
						|
          throw new ZipException("Wrong Central Directory signature: " + name);
 | 
						|
 | 
						|
        inp.skip(4);
 | 
						|
        int flags = inp.readLeShort();
 | 
						|
        if ((flags & 1) != 0)
 | 
						|
          throw new ZipException("invalid CEN header (encrypted entry)");
 | 
						|
        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);
 | 
						|
      }
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * Closes the ZipFile.  This also closes all input streams given by
 | 
						|
   * 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
 | 
						|
  {
 | 
						|
    RandomAccessFile raf = this.raf;
 | 
						|
    if (raf == null)
 | 
						|
      return;
 | 
						|
 | 
						|
    synchronized (raf)
 | 
						|
      {
 | 
						|
        closed = true;
 | 
						|
        entries = null;
 | 
						|
        raf.close();
 | 
						|
      }
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * Calls the <code>close()</code> method when this ZipFile has not yet
 | 
						|
   * been explicitly closed.
 | 
						|
   */
 | 
						|
  protected void finalize() throws IOException
 | 
						|
  {
 | 
						|
    if (!closed && raf != null) close();
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * Returns an enumeration of all Zip entries in this Zip file.
 | 
						|
   *
 | 
						|
   * @exception IllegalStateException when the ZipFile has already been closed
 | 
						|
   */
 | 
						|
  public Enumeration<? extends ZipEntry> entries()
 | 
						|
  {
 | 
						|
    checkClosed();
 | 
						|
 | 
						|
    try
 | 
						|
      {
 | 
						|
        return new ZipEntryEnumeration(getEntries().values().iterator());
 | 
						|
      }
 | 
						|
    catch (IOException ioe)
 | 
						|
      {
 | 
						|
        return new EmptyEnumeration<ZipEntry>();
 | 
						|
      }
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * Checks that the ZipFile is still open and reads entries when necessary.
 | 
						|
   *
 | 
						|
   * @exception IllegalStateException when the ZipFile has already been closed.
 | 
						|
   * @exception IOException when the entries could not be read.
 | 
						|
   */
 | 
						|
  private LinkedHashMap<String, ZipEntry> getEntries() throws IOException
 | 
						|
  {
 | 
						|
    synchronized(raf)
 | 
						|
      {
 | 
						|
        checkClosed();
 | 
						|
 | 
						|
        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.
 | 
						|
   *
 | 
						|
   * @exception IllegalStateException when the ZipFile has already been closed
 | 
						|
   */
 | 
						|
  public ZipEntry getEntry(String name)
 | 
						|
  {
 | 
						|
    checkClosed();
 | 
						|
 | 
						|
    try
 | 
						|
      {
 | 
						|
        LinkedHashMap<String, ZipEntry> entries = getEntries();
 | 
						|
        ZipEntry entry = entries.get(name);
 | 
						|
        // If we didn't find it, maybe it's a directory.
 | 
						|
        if (entry == null && !name.endsWith("/"))
 | 
						|
          entry = entries.get(name + '/');
 | 
						|
        return entry != null ? new ZipEntry(entry, name) : null;
 | 
						|
      }
 | 
						|
    catch (IOException ioe)
 | 
						|
      {
 | 
						|
        return null;
 | 
						|
      }
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * Creates an input stream reading the given zip entry as
 | 
						|
   * uncompressed data.  Normally zip entry should be an entry
 | 
						|
   * returned by getEntry() or entries().
 | 
						|
   *
 | 
						|
   * This implementation returns null if the requested entry does not
 | 
						|
   * exist.  This decision is not obviously correct, however, it does
 | 
						|
   * appear to mirror Sun's implementation, and it is consistant with
 | 
						|
   * their javadoc.  On the other hand, the old JCL book, 2nd Edition,
 | 
						|
   * claims that this should return a "non-null ZIP entry".  We have
 | 
						|
   * chosen for now ignore the old book, as modern versions of Ant (an
 | 
						|
   * important application) depend on this behaviour.  See discussion
 | 
						|
   * in this thread:
 | 
						|
   * http://gcc.gnu.org/ml/java-patches/2004-q2/msg00602.html
 | 
						|
   *
 | 
						|
   * @param entry the entry to create an InputStream for.
 | 
						|
   * @return the input stream, or null if the requested entry does not exist.
 | 
						|
   *
 | 
						|
   * @exception IllegalStateException when the ZipFile has already been closed
 | 
						|
   * @exception IOException if a i/o error occured.
 | 
						|
   * @exception ZipException if the Zip archive is malformed.
 | 
						|
   */
 | 
						|
  public InputStream getInputStream(ZipEntry entry) throws IOException
 | 
						|
  {
 | 
						|
    checkClosed();
 | 
						|
 | 
						|
    LinkedHashMap<String, ZipEntry> entries = getEntries();
 | 
						|
    String name = entry.getName();
 | 
						|
    ZipEntry zipEntry = entries.get(name);
 | 
						|
    if (zipEntry == null)
 | 
						|
      return null;
 | 
						|
 | 
						|
    PartialInputStream inp = new PartialInputStream(raf, 1024);
 | 
						|
    inp.seek(zipEntry.offset);
 | 
						|
 | 
						|
    if (inp.readLeInt() != LOCSIG)
 | 
						|
      throw new ZipException("Wrong Local header signature: " + name);
 | 
						|
 | 
						|
    inp.skip(4);
 | 
						|
 | 
						|
    if (zipEntry.getMethod() != inp.readLeShort())
 | 
						|
      throw new ZipException("Compression method mismatch: " + name);
 | 
						|
 | 
						|
    inp.skip(16);
 | 
						|
 | 
						|
    int nameLen = inp.readLeShort();
 | 
						|
    int extraLen = inp.readLeShort();
 | 
						|
    inp.skip(nameLen + extraLen);
 | 
						|
 | 
						|
    inp.setLength(zipEntry.getCompressedSize());
 | 
						|
 | 
						|
    int method = zipEntry.getMethod();
 | 
						|
    switch (method)
 | 
						|
      {
 | 
						|
      case ZipOutputStream.STORED:
 | 
						|
        return inp;
 | 
						|
      case ZipOutputStream.DEFLATED:
 | 
						|
        inp.addDummyByte();
 | 
						|
        final Inflater inf = new Inflater(true);
 | 
						|
        final int sz = (int) entry.getSize();
 | 
						|
        return new InflaterInputStream(inp, inf)
 | 
						|
        {
 | 
						|
          public int available() throws IOException
 | 
						|
          {
 | 
						|
            if (sz == -1)
 | 
						|
              return super.available();
 | 
						|
            if (super.available() != 0)
 | 
						|
              return sz - inf.getTotalOut();
 | 
						|
            return 0;
 | 
						|
          }
 | 
						|
        };
 | 
						|
      default:
 | 
						|
        throw new ZipException("Unknown compression method " + method);
 | 
						|
      }
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * Returns the (path) name of this zip file.
 | 
						|
   */
 | 
						|
  public String getName()
 | 
						|
  {
 | 
						|
    return name;
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * Returns the number of entries in this zip file.
 | 
						|
   *
 | 
						|
   * @exception IllegalStateException when the ZipFile has already been closed
 | 
						|
   */
 | 
						|
  public int size()
 | 
						|
  {
 | 
						|
    checkClosed();
 | 
						|
 | 
						|
    try
 | 
						|
      {
 | 
						|
        return getEntries().size();
 | 
						|
      }
 | 
						|
    catch (IOException ioe)
 | 
						|
      {
 | 
						|
        return 0;
 | 
						|
      }
 | 
						|
  }
 | 
						|
 | 
						|
  private static class ZipEntryEnumeration implements Enumeration<ZipEntry>
 | 
						|
  {
 | 
						|
    private final Iterator<ZipEntry> elements;
 | 
						|
 | 
						|
    public ZipEntryEnumeration(Iterator<ZipEntry> elements)
 | 
						|
    {
 | 
						|
      this.elements = elements;
 | 
						|
    }
 | 
						|
 | 
						|
    public boolean hasMoreElements()
 | 
						|
    {
 | 
						|
      return elements.hasNext();
 | 
						|
    }
 | 
						|
 | 
						|
    public ZipEntry nextElement()
 | 
						|
    {
 | 
						|
      /* We return a clone, just to be safe that the user doesn't
 | 
						|
       * change the entry.
 | 
						|
       */
 | 
						|
      return (ZipEntry) (elements.next().clone());
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  private static final class PartialInputStream extends InputStream
 | 
						|
  {
 | 
						|
    /**
 | 
						|
     * The UTF-8 charset use for decoding the filenames.
 | 
						|
     */
 | 
						|
    private static final Charset UTF8CHARSET = Charset.forName("UTF-8");
 | 
						|
 | 
						|
    /**
 | 
						|
     * The actual UTF-8 decoder. Created on demand.
 | 
						|
     */
 | 
						|
    private CharsetDecoder utf8Decoder;
 | 
						|
 | 
						|
    private final RandomAccessFile raf;
 | 
						|
    private final byte[] buffer;
 | 
						|
    private long bufferOffset;
 | 
						|
    private int pos;
 | 
						|
    private long end;
 | 
						|
    // We may need to supply an extra dummy byte to our reader.
 | 
						|
    // See Inflater.  We use a count here to simplify the logic
 | 
						|
    // elsewhere in this class.  Note that we ignore the dummy
 | 
						|
    // byte in methods where we know it is not needed.
 | 
						|
    private int dummyByteCount;
 | 
						|
 | 
						|
    public PartialInputStream(RandomAccessFile raf, int bufferSize)
 | 
						|
      throws IOException
 | 
						|
    {
 | 
						|
      this.raf = raf;
 | 
						|
      buffer = new byte[bufferSize];
 | 
						|
      bufferOffset = -buffer.length;
 | 
						|
      pos = buffer.length;
 | 
						|
      end = raf.length();
 | 
						|
    }
 | 
						|
 | 
						|
    void setLength(long length)
 | 
						|
    {
 | 
						|
      end = bufferOffset + pos + length;
 | 
						|
    }
 | 
						|
 | 
						|
    private void fillBuffer() throws IOException
 | 
						|
    {
 | 
						|
      synchronized (raf)
 | 
						|
        {
 | 
						|
          long len = end - bufferOffset;
 | 
						|
          if (len == 0 && dummyByteCount > 0)
 | 
						|
            {
 | 
						|
              buffer[0] = 0;
 | 
						|
              dummyByteCount = 0;
 | 
						|
            }
 | 
						|
          else
 | 
						|
            {
 | 
						|
              raf.seek(bufferOffset);
 | 
						|
              raf.readFully(buffer, 0, (int) Math.min(buffer.length, len));
 | 
						|
            }
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    public int available()
 | 
						|
    {
 | 
						|
      long amount = end - (bufferOffset + pos);
 | 
						|
      if (amount > Integer.MAX_VALUE)
 | 
						|
        return Integer.MAX_VALUE;
 | 
						|
      return (int) amount;
 | 
						|
    }
 | 
						|
 | 
						|
    public int read() throws IOException
 | 
						|
    {
 | 
						|
      if (bufferOffset + pos >= end + dummyByteCount)
 | 
						|
        return -1;
 | 
						|
      if (pos == buffer.length)
 | 
						|
        {
 | 
						|
          bufferOffset += buffer.length;
 | 
						|
          pos = 0;
 | 
						|
          fillBuffer();
 | 
						|
        }
 | 
						|
 | 
						|
      return buffer[pos++] & 0xFF;
 | 
						|
    }
 | 
						|
 | 
						|
    public int read(byte[] b, int off, int len) throws IOException
 | 
						|
    {
 | 
						|
      if (len > end + dummyByteCount - (bufferOffset + pos))
 | 
						|
        {
 | 
						|
          len = (int) (end + dummyByteCount - (bufferOffset + pos));
 | 
						|
          if (len == 0)
 | 
						|
            return -1;
 | 
						|
        }
 | 
						|
 | 
						|
      int totalBytesRead = Math.min(buffer.length - pos, len);
 | 
						|
      System.arraycopy(buffer, pos, b, off, totalBytesRead);
 | 
						|
      pos += totalBytesRead;
 | 
						|
      off += totalBytesRead;
 | 
						|
      len -= totalBytesRead;
 | 
						|
 | 
						|
      while (len > 0)
 | 
						|
        {
 | 
						|
          bufferOffset += buffer.length;
 | 
						|
          pos = 0;
 | 
						|
          fillBuffer();
 | 
						|
          int remain = Math.min(buffer.length, len);
 | 
						|
          System.arraycopy(buffer, pos, b, off, remain);
 | 
						|
          pos += remain;
 | 
						|
          off += remain;
 | 
						|
          len -= remain;
 | 
						|
          totalBytesRead += remain;
 | 
						|
        }
 | 
						|
 | 
						|
      return totalBytesRead;
 | 
						|
    }
 | 
						|
 | 
						|
    public long skip(long amount) throws IOException
 | 
						|
    {
 | 
						|
      if (amount < 0)
 | 
						|
        return 0;
 | 
						|
      if (amount > end - (bufferOffset + pos))
 | 
						|
        amount = end - (bufferOffset + pos);
 | 
						|
      seek(bufferOffset + pos + amount);
 | 
						|
      return amount;
 | 
						|
    }
 | 
						|
 | 
						|
    void seek(long newpos) throws IOException
 | 
						|
    {
 | 
						|
      long offset = newpos - bufferOffset;
 | 
						|
      if (offset >= 0 && offset <= buffer.length)
 | 
						|
        {
 | 
						|
          pos = (int) offset;
 | 
						|
        }
 | 
						|
      else
 | 
						|
        {
 | 
						|
          bufferOffset = newpos;
 | 
						|
          pos = 0;
 | 
						|
          fillBuffer();
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    void readFully(byte[] buf) throws IOException
 | 
						|
    {
 | 
						|
      if (read(buf, 0, buf.length) != buf.length)
 | 
						|
        throw new EOFException();
 | 
						|
    }
 | 
						|
 | 
						|
    void readFully(byte[] buf, int off, int len) throws IOException
 | 
						|
    {
 | 
						|
      if (read(buf, off, len) != len)
 | 
						|
        throw new EOFException();
 | 
						|
    }
 | 
						|
 | 
						|
    int readLeShort() throws IOException
 | 
						|
    {
 | 
						|
      int result;
 | 
						|
      if(pos + 1 < buffer.length)
 | 
						|
        {
 | 
						|
          result = ((buffer[pos + 0] & 0xff) | (buffer[pos + 1] & 0xff) << 8);
 | 
						|
          pos += 2;
 | 
						|
        }
 | 
						|
      else
 | 
						|
        {
 | 
						|
          int b0 = read();
 | 
						|
          int b1 = read();
 | 
						|
          if (b1 == -1)
 | 
						|
            throw new EOFException();
 | 
						|
          result = (b0 & 0xff) | (b1 & 0xff) << 8;
 | 
						|
        }
 | 
						|
      return result;
 | 
						|
    }
 | 
						|
 | 
						|
    int readLeInt() throws IOException
 | 
						|
    {
 | 
						|
      int result;
 | 
						|
      if(pos + 3 < buffer.length)
 | 
						|
        {
 | 
						|
          result = (((buffer[pos + 0] & 0xff) | (buffer[pos + 1] & 0xff) << 8)
 | 
						|
                   | ((buffer[pos + 2] & 0xff)
 | 
						|
                       | (buffer[pos + 3] & 0xff) << 8) << 16);
 | 
						|
          pos += 4;
 | 
						|
        }
 | 
						|
      else
 | 
						|
        {
 | 
						|
          int b0 = read();
 | 
						|
          int b1 = read();
 | 
						|
          int b2 = read();
 | 
						|
          int b3 = read();
 | 
						|
          if (b3 == -1)
 | 
						|
            throw new EOFException();
 | 
						|
          result =  (((b0 & 0xff) | (b1 & 0xff) << 8) | ((b2 & 0xff)
 | 
						|
                    | (b3 & 0xff) << 8) << 16);
 | 
						|
        }
 | 
						|
      return result;
 | 
						|
    }
 | 
						|
 | 
						|
    /**
 | 
						|
     * Decode chars from byte buffer using UTF8 encoding.  This
 | 
						|
     * operation is performance-critical since a jar file contains a
 | 
						|
     * large number of strings for the name of each file in the
 | 
						|
     * archive.  This routine therefore avoids using the expensive
 | 
						|
     * utf8Decoder when decoding is straightforward.
 | 
						|
     *
 | 
						|
     * @param buffer the buffer that contains the encoded character
 | 
						|
     *        data
 | 
						|
     * @param pos the index in buffer of the first byte of the encoded
 | 
						|
     *        data
 | 
						|
     * @param length the length of the encoded data in number of
 | 
						|
     *        bytes.
 | 
						|
     *
 | 
						|
     * @return a String that contains the decoded characters.
 | 
						|
     */
 | 
						|
    private String decodeChars(byte[] buffer, int pos, int length)
 | 
						|
      throws IOException
 | 
						|
    {
 | 
						|
      String result;
 | 
						|
      int i=length - 1;
 | 
						|
      while ((i >= 0) && (buffer[i] <= 0x7f))
 | 
						|
        {
 | 
						|
          i--;
 | 
						|
        }
 | 
						|
      if (i < 0)
 | 
						|
        {
 | 
						|
          result = new String(buffer, 0, pos, length);
 | 
						|
        }
 | 
						|
      else
 | 
						|
        {
 | 
						|
          ByteBuffer bufferBuffer = ByteBuffer.wrap(buffer, pos, length);
 | 
						|
          if (utf8Decoder == null)
 | 
						|
            utf8Decoder = UTF8CHARSET.newDecoder();
 | 
						|
          utf8Decoder.reset();
 | 
						|
          char [] characters = utf8Decoder.decode(bufferBuffer).array();
 | 
						|
          result = String.valueOf(characters);
 | 
						|
        }
 | 
						|
      return result;
 | 
						|
    }
 | 
						|
 | 
						|
    String readString(int length) throws IOException
 | 
						|
    {
 | 
						|
      if (length > end - (bufferOffset + pos))
 | 
						|
        throw new EOFException();
 | 
						|
 | 
						|
      String result = null;
 | 
						|
      try
 | 
						|
        {
 | 
						|
          if (buffer.length - pos >= length)
 | 
						|
            {
 | 
						|
              result = decodeChars(buffer, pos, length);
 | 
						|
              pos += length;
 | 
						|
            }
 | 
						|
          else
 | 
						|
            {
 | 
						|
              byte[] b = new byte[length];
 | 
						|
              readFully(b);
 | 
						|
              result = decodeChars(b, 0, length);
 | 
						|
            }
 | 
						|
        }
 | 
						|
      catch (UnsupportedEncodingException uee)
 | 
						|
        {
 | 
						|
          throw new AssertionError(uee);
 | 
						|
        }
 | 
						|
      return result;
 | 
						|
    }
 | 
						|
 | 
						|
    public void addDummyByte()
 | 
						|
    {
 | 
						|
      dummyByteCount = 1;
 | 
						|
    }
 | 
						|
  }
 | 
						|
}
 |