diff options
Diffstat (limited to 'libjava/java/util/zip/ZipOutputStream.java')
-rw-r--r-- | libjava/java/util/zip/ZipOutputStream.java | 263 |
1 files changed, 233 insertions, 30 deletions
diff --git a/libjava/java/util/zip/ZipOutputStream.java b/libjava/java/util/zip/ZipOutputStream.java index 18f4d388652..f4ce7accc01 100644 --- a/libjava/java/util/zip/ZipOutputStream.java +++ b/libjava/java/util/zip/ZipOutputStream.java @@ -9,65 +9,268 @@ details. */ package java.util.zip; import java.io.*; -/** JUST AN INCOMPLETE STUB! */ +/* Written using on-line Java Platform 1.2 API Specification + * and JCL book. + * Believed complete and correct. + */ public class ZipOutputStream extends DeflaterOutputStream implements ZipConstants { - ZipEntry current; - int method = DEFLATED; - int level = 3; // FIXME - should be DEFAULT_COMPRESSION - String comment; - public static final int STORED = 0; public static final int DEFLATED = 8; - public ZipOutputStream (OutputStream out) + public void close () throws IOException { - super(out); + finish (); + out.close(); + } + + public void closeEntry () throws IOException + { + int uncompressed_size = def.getTotalIn(); + int compressed_size = def.getTotalOut(); + int crc = (int) (filter.getChecksum().getValue()); + + bytes_written += compressed_size; + + bytes_written += put4 (0x08074b50); + if (current.getCrc() == -1 || current.getCompressedSize() == -1 + || current.getSize() == -1) + { + current.setCrc(crc); + current.compressedSize = compressed_size; + current.setSize(uncompressed_size); + } + else + { + if (current.getCrc() != crc + || current.getCompressedSize() != compressed_size + || current.getSize() != uncompressed_size) + throw new ZipException ("zip entry field incorrect"); + } + bytes_written += put4 ((int) (current.getCrc())); + bytes_written += put4 ((int) (current.getCompressedSize())); + bytes_written += put4 ((int) (current.getSize())); + + current.next = chain; + chain = current; + current = null; + filter = null; + } + + public void finish () throws IOException + { + if (current != null) + closeEntry (); + + // Write the central directory. + long offset = bytes_written; + int count = 0; + int bytes = 0; + while (chain != null) + { + bytes += write_entry (chain, false); + ++count; + chain = chain.next; + } + + // Write the end of the central directory record. + put4 (0x06054b50); + // Disk number. + put2 (0); + // Another disk number. + put2 (0); + put2 (count); + put4 (bytes); + put4 ((int) offset); + + byte[] c = comment.getBytes("8859_1"); + put2 (c.length); + out.write(c); + out.write((byte) 0); } - public void setLevel (int level) { this.level = level; } - public void setMethod (int method) { this.method = method; } - public void setComment(String comment) { this.comment = comment; } + // Helper for finish and putNextEntry. + private int write_entry (ZipEntry entry, boolean is_local) + throws IOException + { + long offset = bytes_written; + + int bytes = put4 (is_local ? 0x04034b50 : 0x02014b50); + if (! is_local) + bytes += put_version (); + bytes += put_version (); + + boolean crc_after = false; + if (is_local + && (current.getCrc() == -1 || current.getCompressedSize() == -1 + || current.getSize() == -1)) + crc_after = true; + // For the bits field we always indicate `normal' compression, + // even if that isn't true. + bytes += put2 (crc_after ? (1 << 3) : 0); + bytes += put2 (entry.method); + + bytes += put2(0); // time - FIXME + bytes += put2(0); // date - FIXME + + if (crc_after) + { + // CRC, compressedSize, and Size are always 0 in this header. + // The actual values are given after the entry. + bytes += put4 (0); + bytes += put4 (0); + bytes += put4 (0); + } + else + { + bytes += put4 ((int) (entry.getCrc())); + bytes += put4 ((int) (entry.getCompressedSize())); + bytes += put4 ((int) (entry.getSize())); + } + + byte[] name = entry.name.getBytes("8859_1"); + bytes += put2 (name.length); + bytes += put2 (entry.extra == null ? 0 : entry.extra.length); + + byte[] comment = null; + if (! is_local) + { + if (entry.getComment() == null) + bytes += put2 (0); + else + { + comment = entry.getComment().getBytes("8859_1"); + bytes += put2 (comment.length); + } + + // Disk number start. + bytes += put2 (0); + // Internal file attributes. + bytes += put2 (0); + // External file attributes. + bytes += put2 (0); + // Relative offset of local header. + bytes += put2 ((int) offset); + } + + out.write (name); + out.write ((byte) 0); + bytes += name.length + 1; + if (entry.extra != null) + { + out.write(entry.extra); + out.write((byte) 0); + bytes += entry.extra.length + 1; + } + if (comment != null) + { + out.write(comment); + out.write((byte) 0); + bytes += comment.length + 1; + } + + bytes_written += bytes; + return bytes; + } public void putNextEntry (ZipEntry entry) throws IOException { - put4(0x04034b50); - put2(0); // version - FIXME - put2(0); // bits - FIXME + if (current != null) + closeEntry (); + if (entry.method < 0 ) entry.method = method; - put2(entry.method); - put2(0); // time - FIXME - put2(0); // date - FIXME - put4((int) entry.crc); - put4((int) entry.compressedSize); // FIXME - put4((int) entry.size); // FIXME - put2(entry.name.length()); - put2(entry.extra == null ? 0 : entry.extra.length); - byte[] name = entry.name.getBytes("8859_1"); - out.write(name); - if (entry.extra != null) - out.write(entry.extra); - throw new Error ("java.util.zip.ZipOutputStream.putNextEntry: not implemented"); + if (entry.method == STORED) + { + if (entry.getSize() == -1 || entry.getCrc() == -1) + throw new ZipException ("required entry not set"); + // Just in case. + entry.compressedSize = entry.getSize(); + } + write_entry (entry, true); + current = entry; + int compr = (method == STORED) ? Deflater.NO_COMPRESSION : level; + def.reset(); + def.setLevel(compr); + filter = new CheckedOutputStream (new DeflaterOutputStream (out, def), + new CRC32 ()); } - public void closeEntry () throws IOException + public void setLevel (int level) { + if (level != Deflater.DEFAULT_COMPRESSION + && (level < Deflater.NO_COMPRESSION + || level > Deflater.BEST_COMPRESSION)) + throw new IllegalArgumentException (); + this.level = level; } - private void put2 (int i) throws IOException + public void setMethod (int method) + { + if (method != DEFLATED && method != STORED) + throw new IllegalArgumentException (); + this.method = method; + } + + public void setComment (String comment) + { + if (comment.length() > 65535) + throw new IllegalArgumentException (); + this.comment = comment; + } + + public synchronized void write (byte[] buf, int off, int len) + throws IOException + { + if (filter == null) + throw new ZipException ("no open zip entry"); + filter.write(buf, off, len); + } + + public ZipOutputStream (OutputStream out) + { + super (out); + def = new Deflater (level, true); + } + + private int put2 (int i) throws IOException { out.write (i); out.write (i >> 8); + return 2; } - private void put4 (int i) throws IOException + private int put4 (int i) throws IOException { out.write (i); out.write (i >> 8); out.write (i >> 16); out.write (i >> 24); + return 4; } + + private int put_version () throws IOException + { + // FIXME: for now we assume Unix, and we ignore the version + // number. + return put2 (3 << 8); + } + + // The entry we are currently writing, or null if we've called + // closeEntry. + private ZipEntry current; + // The chain of entries which have been written to this file. + private ZipEntry chain; + // The output stream to which data should be sent. + private CheckedOutputStream filter; + + private int method = DEFLATED; + private int level = Deflater.DEFAULT_COMPRESSION; + private String comment = ""; + private long bytes_written; + + // The Deflater we use. + private Deflater def; } |