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;
 | |
|   }
 | |
| }
 |