mirror of git://gcc.gnu.org/git/gcc.git
				
				
				
			
		
			
				
	
	
		
			441 lines
		
	
	
		
			13 KiB
		
	
	
	
		
			Java
		
	
	
	
			
		
		
	
	
			441 lines
		
	
	
		
			13 KiB
		
	
	
	
		
			Java
		
	
	
	
/* ZipOutputStream.java --
 | 
						|
   Copyright (C) 2001, 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 java.io.IOException;
 | 
						|
import java.io.OutputStream;
 | 
						|
import java.io.UnsupportedEncodingException;
 | 
						|
import java.util.Enumeration;
 | 
						|
import java.util.Vector;
 | 
						|
 | 
						|
/**
 | 
						|
 * 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
 | 
						|
{
 | 
						|
  private Vector entries = new Vector();
 | 
						|
  private CRC32 crc = new CRC32();
 | 
						|
  private ZipEntry curEntry = null;
 | 
						|
 | 
						|
  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 static final int ZIP_STORED_VERSION = 10;
 | 
						|
  private static final int ZIP_DEFLATED_VERSION = 20;
 | 
						|
 | 
						|
  /**
 | 
						|
   * Compression method.  This method doesn't compress at all.
 | 
						|
   */
 | 
						|
  public static final int STORED = 0;
 | 
						|
 | 
						|
  /**
 | 
						|
   * Compression method.  This method uses the Deflater.
 | 
						|
   */
 | 
						|
  public static final 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)
 | 
						|
  {
 | 
						|
    super(out, new Deflater(Deflater.DEFAULT_COMPRESSION, true));
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * 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)
 | 
						|
  {
 | 
						|
    byte[] commentBytes;
 | 
						|
    try
 | 
						|
      {
 | 
						|
        commentBytes = comment.getBytes("UTF-8");
 | 
						|
      }
 | 
						|
    catch (UnsupportedEncodingException uee)
 | 
						|
      {
 | 
						|
        throw new AssertionError(uee);
 | 
						|
      }
 | 
						|
    if (commentBytes.length > 0xffff)
 | 
						|
      throw new IllegalArgumentException("Comment too long.");
 | 
						|
    zipComment = commentBytes;
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * 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 (method != STORED && method != DEFLATED)
 | 
						|
      throw new IllegalArgumentException("Method not supported.");
 | 
						|
    defaultMethod = method;
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * 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)
 | 
						|
  {
 | 
						|
    def.setLevel(level);
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * Write an unsigned short in little endian byte order.
 | 
						|
   */
 | 
						|
  private void writeLeShort(int value) throws IOException
 | 
						|
  {
 | 
						|
    out.write(value & 0xff);
 | 
						|
    out.write((value >> 8) & 0xff);
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * Write an int in little endian byte order.
 | 
						|
   */
 | 
						|
  private void writeLeInt(int value) throws IOException
 | 
						|
  {
 | 
						|
    writeLeShort(value);
 | 
						|
    writeLeShort(value >> 16);
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * Write a long value as an int.  Some of the zip constants
 | 
						|
   * are declared as longs even though they fit perfectly well
 | 
						|
   * into integers.
 | 
						|
   */
 | 
						|
  private void writeLeInt(long value) throws IOException
 | 
						|
  {
 | 
						|
    writeLeInt((int) value);
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * Starts a new Zip entry. It automatically closes the previous
 | 
						|
   * 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 ZipException if stream was finished.
 | 
						|
   */
 | 
						|
  public void putNextEntry(ZipEntry entry) throws IOException
 | 
						|
  {
 | 
						|
    if (entries == null)
 | 
						|
      throw new ZipException("ZipOutputStream was finished");
 | 
						|
 | 
						|
    int method = entry.getMethod();
 | 
						|
    int flags = 0;
 | 
						|
    if (method == -1)
 | 
						|
      method = defaultMethod;
 | 
						|
 | 
						|
    if (method == STORED)
 | 
						|
      {
 | 
						|
        if (entry.getCompressedSize() >= 0)
 | 
						|
          {
 | 
						|
            if (entry.getSize() < 0)
 | 
						|
              entry.setSize(entry.getCompressedSize());
 | 
						|
            else if (entry.getSize() != entry.getCompressedSize())
 | 
						|
              throw new ZipException
 | 
						|
                ("Method STORED, but compressed size != size");
 | 
						|
          }
 | 
						|
        else
 | 
						|
          entry.setCompressedSize(entry.getSize());
 | 
						|
 | 
						|
        if (entry.getSize() < 0)
 | 
						|
          throw new ZipException("Method STORED, but size not set");
 | 
						|
        if (entry.getCrc() < 0)
 | 
						|
          throw new ZipException("Method STORED, but crc not set");
 | 
						|
      }
 | 
						|
    else if (method == DEFLATED)
 | 
						|
      {
 | 
						|
        if (entry.getCompressedSize() < 0
 | 
						|
            || entry.getSize() < 0 || entry.getCrc() < 0)
 | 
						|
          flags |= 8;
 | 
						|
      }
 | 
						|
 | 
						|
    if (curEntry != null)
 | 
						|
      closeEntry();
 | 
						|
 | 
						|
    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)
 | 
						|
      {
 | 
						|
        writeLeInt((int)entry.getCrc());
 | 
						|
        writeLeInt((int)entry.getCompressedSize());
 | 
						|
        writeLeInt((int)entry.getSize());
 | 
						|
      }
 | 
						|
    else
 | 
						|
      {
 | 
						|
        writeLeInt(0);
 | 
						|
        writeLeInt(0);
 | 
						|
        writeLeInt(0);
 | 
						|
      }
 | 
						|
    byte[] name;
 | 
						|
    try
 | 
						|
      {
 | 
						|
        name = entry.getName().getBytes("UTF-8");
 | 
						|
      }
 | 
						|
    catch (UnsupportedEncodingException uee)
 | 
						|
      {
 | 
						|
        throw new AssertionError(uee);
 | 
						|
      }
 | 
						|
    if (name.length > 0xffff)
 | 
						|
      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;
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * Closes the current entry.
 | 
						|
   * @exception IOException if an I/O error occured.
 | 
						|
   * @exception ZipException if no entry is active.
 | 
						|
   */
 | 
						|
  public void closeEntry() throws IOException
 | 
						|
  {
 | 
						|
    if (curEntry == null)
 | 
						|
      throw new ZipException("No open entry");
 | 
						|
 | 
						|
    /* First finish the deflater, if appropriate */
 | 
						|
    if (curMethod == DEFLATED)
 | 
						|
      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)
 | 
						|
      {
 | 
						|
        writeLeInt(EXTSIG);
 | 
						|
        writeLeInt((int)curEntry.getCrc());
 | 
						|
        writeLeInt((int)curEntry.getCompressedSize());
 | 
						|
        writeLeInt((int)curEntry.getSize());
 | 
						|
        offset += EXTHDR;
 | 
						|
      }
 | 
						|
 | 
						|
    entries.addElement(curEntry);
 | 
						|
    curEntry = null;
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * Writes the given buffer to the current entry.
 | 
						|
   * @exception IOException if an I/O error occured.
 | 
						|
   * @exception ZipException if no entry is active.
 | 
						|
   */
 | 
						|
  public void write(byte[] b, int off, int len) throws IOException
 | 
						|
  {
 | 
						|
    if (curEntry == null)
 | 
						|
      throw new ZipException("No open entry.");
 | 
						|
 | 
						|
    switch (curMethod)
 | 
						|
      {
 | 
						|
      case DEFLATED:
 | 
						|
        super.write(b, off, len);
 | 
						|
        break;
 | 
						|
 | 
						|
      case STORED:
 | 
						|
        out.write(b, off, len);
 | 
						|
        break;
 | 
						|
      }
 | 
						|
 | 
						|
    crc.update(b, off, len);
 | 
						|
    size += len;
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * 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
 | 
						|
  {
 | 
						|
    if (entries == null)
 | 
						|
      return;
 | 
						|
    if (curEntry != null)
 | 
						|
      closeEntry();
 | 
						|
 | 
						|
    int numEntries = 0;
 | 
						|
    int sizeEntries = 0;
 | 
						|
 | 
						|
    Enumeration e = entries.elements();
 | 
						|
    while (e.hasMoreElements())
 | 
						|
      {
 | 
						|
        ZipEntry entry = (ZipEntry) e.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;
 | 
						|
        try
 | 
						|
          {
 | 
						|
            name = entry.getName().getBytes("UTF-8");
 | 
						|
          }
 | 
						|
        catch (UnsupportedEncodingException uee)
 | 
						|
          {
 | 
						|
            throw new AssertionError(uee);
 | 
						|
          }
 | 
						|
        if (name.length > 0xffff)
 | 
						|
          throw new ZipException("Name too long.");
 | 
						|
        byte[] extra = entry.getExtra();
 | 
						|
        if (extra == null)
 | 
						|
          extra = new byte[0];
 | 
						|
        String str = entry.getComment();
 | 
						|
        byte[] comment;
 | 
						|
        try
 | 
						|
          {
 | 
						|
            comment = str != null ? str.getBytes("UTF-8") : new byte[0];
 | 
						|
          }
 | 
						|
        catch (UnsupportedEncodingException uee)
 | 
						|
          {
 | 
						|
            throw new AssertionError(uee);
 | 
						|
          }
 | 
						|
        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;
 | 
						|
      }
 | 
						|
 | 
						|
    writeLeInt(ENDSIG);
 | 
						|
    writeLeShort(0); /* disk number */
 | 
						|
    writeLeShort(0); /* disk with start of central dir */
 | 
						|
    writeLeShort(numEntries);
 | 
						|
    writeLeShort(numEntries);
 | 
						|
    writeLeInt(sizeEntries);
 | 
						|
    writeLeInt(offset);
 | 
						|
    writeLeShort(zipComment.length);
 | 
						|
    out.write(zipComment);
 | 
						|
    out.flush();
 | 
						|
    entries = null;
 | 
						|
  }
 | 
						|
}
 |